]>
Commit | Line | Data |
---|---|---|
026b81e9 HH |
1 | /*** |
2 | This file is part of systemd. | |
3 | ||
4 | Copyright 2010 Lennart Poettering | |
5 | ||
6 | systemd is free software; you can redistribute it and/or modify it | |
7 | under the terms of the GNU Lesser General Public License as published by | |
8 | the Free Software Foundation; either version 2.1 of the License, or | |
9 | (at your option) any later version. | |
10 | ||
11 | systemd is distributed in the hope that it will be useful, but | |
12 | WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 | Lesser General Public License for more details. | |
15 | ||
16 | You should have received a copy of the GNU Lesser General Public License | |
17 | along with systemd; If not, see <http://www.gnu.org/licenses/>. | |
18 | ***/ | |
19 | ||
20 | #include <string.h> | |
21 | #include <unistd.h> | |
22 | #include <errno.h> | |
23 | #include <fcntl.h> | |
24 | #include <sys/types.h> | |
25 | #include <sys/syscall.h> | |
26 | ||
27 | #include "util.h" | |
28 | ||
29 | static inline pid_t gettid(void) { | |
30 | return (pid_t) syscall(SYS_gettid); | |
31 | } | |
32 | ||
33 | size_t page_size(void) { | |
34 | static __thread size_t pgsz = 0; | |
35 | long r; | |
36 | ||
37 | if (_likely_(pgsz > 0)) | |
38 | return pgsz; | |
39 | ||
40 | assert_se((r = sysconf(_SC_PAGESIZE)) > 0); | |
41 | ||
42 | pgsz = (size_t) r; | |
43 | ||
44 | return pgsz; | |
45 | } | |
46 | ||
47 | bool endswith(const char *s, const char *postfix) { | |
48 | size_t sl, pl; | |
49 | ||
50 | assert(s); | |
51 | assert(postfix); | |
52 | ||
53 | sl = strlen(s); | |
54 | pl = strlen(postfix); | |
55 | ||
56 | if (pl == 0) | |
57 | return true; | |
58 | ||
59 | if (sl < pl) | |
60 | return false; | |
61 | ||
62 | return memcmp(s + sl - pl, postfix, pl) == 0; | |
63 | } | |
64 | int close_nointr(int fd) { | |
65 | assert(fd >= 0); | |
66 | ||
67 | for (;;) { | |
68 | int r; | |
69 | ||
70 | r = close(fd); | |
71 | if (r >= 0) | |
72 | return r; | |
73 | ||
74 | if (errno != EINTR) | |
75 | return -errno; | |
76 | } | |
77 | } | |
78 | ||
79 | void close_nointr_nofail(int fd) { | |
80 | int saved_errno = errno; | |
81 | ||
82 | /* like close_nointr() but cannot fail, and guarantees errno | |
83 | * is unchanged */ | |
84 | ||
85 | assert_se(close_nointr(fd) == 0); | |
86 | ||
87 | errno = saved_errno; | |
88 | } | |
89 | ||
90 | int open_terminal(const char *name, int mode) { | |
91 | int fd, r; | |
92 | unsigned c = 0; | |
93 | ||
94 | /* | |
95 | * If a TTY is in the process of being closed opening it might | |
96 | * cause EIO. This is horribly awful, but unlikely to be | |
97 | * changed in the kernel. Hence we work around this problem by | |
98 | * retrying a couple of times. | |
99 | * | |
100 | * https://bugs.launchpad.net/ubuntu/+source/linux/+bug/554172/comments/245 | |
101 | */ | |
102 | ||
103 | for (;;) { | |
104 | if ((fd = open(name, mode)) >= 0) | |
105 | break; | |
106 | ||
107 | if (errno != EIO) | |
108 | return -errno; | |
109 | ||
110 | if (c >= 20) | |
111 | return -errno; | |
112 | ||
113 | usleep(50 * USEC_PER_MSEC); | |
114 | c++; | |
115 | } | |
116 | ||
117 | if (fd < 0) | |
118 | return -errno; | |
119 | ||
120 | if ((r = isatty(fd)) < 0) { | |
121 | close_nointr_nofail(fd); | |
122 | return -errno; | |
123 | } | |
124 | ||
125 | if (!r) { | |
126 | close_nointr_nofail(fd); | |
127 | return -ENOTTY; | |
128 | } | |
129 | ||
130 | return fd; | |
131 | } | |
132 | ||
133 | bool streq_ptr(const char *a, const char *b) { | |
134 | ||
135 | /* Like streq(), but tries to make sense of NULL pointers */ | |
136 | ||
137 | if (a && b) | |
138 | return streq(a, b); | |
139 | ||
140 | if (!a && !b) | |
141 | return true; | |
142 | ||
143 | return false; | |
144 | } | |
145 | bool is_main_thread(void) { | |
146 | static __thread int cached = 0; | |
147 | ||
148 | if (_unlikely_(cached == 0)) | |
149 | cached = getpid() == gettid() ? 1 : -1; | |
150 | ||
151 | return cached > 0; | |
152 | } | |
153 | ||
154 | int safe_atou(const char *s, unsigned *ret_u) { | |
155 | char *x = NULL; | |
156 | unsigned long l; | |
157 | ||
158 | assert(s); | |
159 | assert(ret_u); | |
160 | ||
161 | errno = 0; | |
162 | l = strtoul(s, &x, 0); | |
163 | ||
164 | if (!x || *x || errno) | |
165 | return errno ? -errno : -EINVAL; | |
166 | ||
167 | if ((unsigned long) (unsigned) l != l) | |
168 | return -ERANGE; | |
169 | ||
170 | *ret_u = (unsigned) l; | |
171 | return 0; | |
172 | } | |
173 | ||
174 | static const char *const log_level_table[] = { | |
175 | [LOG_EMERG] = "emerg", | |
176 | [LOG_ALERT] = "alert", | |
177 | [LOG_CRIT] = "crit", | |
178 | [LOG_ERR] = "err", | |
179 | [LOG_WARNING] = "warning", | |
180 | [LOG_NOTICE] = "notice", | |
181 | [LOG_INFO] = "info", | |
182 | [LOG_DEBUG] = "debug" | |
183 | }; | |
184 | ||
185 | DEFINE_STRING_TABLE_LOOKUP(log_level, int); | |
791532b0 HH |
186 | |
187 | char *strnappend(const char *s, const char *suffix, size_t b) { | |
188 | size_t a; | |
189 | char *r; | |
190 | ||
191 | if (!s && !suffix) | |
192 | return strdup(""); | |
193 | ||
194 | if (!s) | |
195 | return strndup(suffix, b); | |
196 | ||
197 | if (!suffix) | |
198 | return strdup(s); | |
199 | ||
200 | assert(s); | |
201 | assert(suffix); | |
202 | ||
203 | a = strlen(s); | |
204 | if (b > ((size_t) -1) - a) | |
205 | return NULL; | |
206 | ||
207 | r = new(char, a+b+1); | |
208 | if (!r) | |
209 | return NULL; | |
210 | ||
211 | memcpy(r, s, a); | |
212 | memcpy(r+a, suffix, b); | |
213 | r[a+b] = 0; | |
214 | ||
215 | return r; | |
216 | } | |
217 | ||
218 | char *strappend(const char *s, const char *suffix) { | |
219 | return strnappend(s, suffix, suffix ? strlen(suffix) : 0); | |
220 | } | |
221 | ||
222 | char *strjoin(const char *x, ...) { | |
223 | va_list ap; | |
224 | size_t l; | |
e7ba1392 | 225 | char *r; |
791532b0 HH |
226 | |
227 | va_start(ap, x); | |
228 | ||
229 | if (x) { | |
230 | l = strlen(x); | |
231 | ||
232 | for (;;) { | |
233 | const char *t; | |
234 | size_t n; | |
235 | ||
236 | t = va_arg(ap, const char *); | |
237 | if (!t) | |
238 | break; | |
239 | ||
240 | n = strlen(t); | |
241 | if (n > ((size_t) -1) - l) { | |
242 | va_end(ap); | |
243 | return NULL; | |
244 | } | |
245 | ||
246 | l += n; | |
247 | } | |
248 | } else | |
249 | l = 0; | |
250 | ||
251 | va_end(ap); | |
252 | ||
253 | r = new(char, l+1); | |
254 | if (!r) | |
255 | return NULL; | |
256 | ||
257 | if (x) { | |
e7ba1392 HH |
258 | char *p; |
259 | ||
791532b0 HH |
260 | p = stpcpy(r, x); |
261 | ||
262 | va_start(ap, x); | |
263 | ||
264 | for (;;) { | |
265 | const char *t; | |
266 | ||
267 | t = va_arg(ap, const char *); | |
268 | if (!t) | |
269 | break; | |
270 | ||
271 | p = stpcpy(p, t); | |
272 | } | |
273 | ||
274 | va_end(ap); | |
275 | } else | |
276 | r[0] = 0; | |
277 | ||
278 | return r; | |
279 | } | |
2f461da2 HH |
280 | |
281 | char *cunescape_length_with_prefix(const char *s, size_t length, const char *prefix) { | |
282 | char *r, *t; | |
283 | const char *f; | |
284 | size_t pl; | |
285 | ||
286 | assert(s); | |
287 | ||
288 | /* Undoes C style string escaping, and optionally prefixes it. */ | |
289 | ||
290 | pl = prefix ? strlen(prefix) : 0; | |
291 | ||
292 | r = new(char, pl+length+1); | |
293 | if (!r) | |
294 | return r; | |
295 | ||
296 | if (prefix) | |
297 | memcpy(r, prefix, pl); | |
298 | ||
299 | for (f = s, t = r + pl; f < s + length; f++) { | |
300 | ||
301 | if (*f != '\\') { | |
302 | *(t++) = *f; | |
303 | continue; | |
304 | } | |
305 | ||
306 | f++; | |
307 | ||
308 | switch (*f) { | |
309 | ||
310 | case 'a': | |
311 | *(t++) = '\a'; | |
312 | break; | |
313 | case 'b': | |
314 | *(t++) = '\b'; | |
315 | break; | |
316 | case 'f': | |
317 | *(t++) = '\f'; | |
318 | break; | |
319 | case 'n': | |
320 | *(t++) = '\n'; | |
321 | break; | |
322 | case 'r': | |
323 | *(t++) = '\r'; | |
324 | break; | |
325 | case 't': | |
326 | *(t++) = '\t'; | |
327 | break; | |
328 | case 'v': | |
329 | *(t++) = '\v'; | |
330 | break; | |
331 | case '\\': | |
332 | *(t++) = '\\'; | |
333 | break; | |
334 | case '"': | |
335 | *(t++) = '"'; | |
336 | break; | |
337 | case '\'': | |
338 | *(t++) = '\''; | |
339 | break; | |
340 | ||
341 | case 's': | |
342 | /* This is an extension of the XDG syntax files */ | |
343 | *(t++) = ' '; | |
344 | break; | |
345 | ||
346 | case 'x': { | |
347 | /* hexadecimal encoding */ | |
348 | int a, b; | |
349 | ||
350 | a = unhexchar(f[1]); | |
351 | b = unhexchar(f[2]); | |
352 | ||
353 | if (a < 0 || b < 0) { | |
354 | /* Invalid escape code, let's take it literal then */ | |
355 | *(t++) = '\\'; | |
356 | *(t++) = 'x'; | |
357 | } else { | |
358 | *(t++) = (char) ((a << 4) | b); | |
359 | f += 2; | |
360 | } | |
361 | ||
362 | break; | |
363 | } | |
364 | ||
365 | case '0': | |
366 | case '1': | |
367 | case '2': | |
368 | case '3': | |
369 | case '4': | |
370 | case '5': | |
371 | case '6': | |
372 | case '7': { | |
373 | /* octal encoding */ | |
374 | int a, b, c; | |
375 | ||
376 | a = unoctchar(f[0]); | |
377 | b = unoctchar(f[1]); | |
378 | c = unoctchar(f[2]); | |
379 | ||
380 | if (a < 0 || b < 0 || c < 0) { | |
381 | /* Invalid escape code, let's take it literal then */ | |
382 | *(t++) = '\\'; | |
383 | *(t++) = f[0]; | |
384 | } else { | |
385 | *(t++) = (char) ((a << 6) | (b << 3) | c); | |
386 | f += 2; | |
387 | } | |
388 | ||
389 | break; | |
390 | } | |
391 | ||
392 | case 0: | |
393 | /* premature end of string.*/ | |
394 | *(t++) = '\\'; | |
395 | goto finish; | |
396 | ||
397 | default: | |
398 | /* Invalid escape code, let's take it literal then */ | |
399 | *(t++) = '\\'; | |
400 | *(t++) = *f; | |
401 | break; | |
402 | } | |
403 | } | |
404 | ||
405 | finish: | |
406 | *t = 0; | |
407 | return r; | |
408 | } | |
409 | ||
410 | char *cunescape_length(const char *s, size_t length) { | |
411 | return cunescape_length_with_prefix(s, length, NULL); | |
412 | } | |
413 | ||
414 | ||
415 | /* Split a string into words, but consider strings enclosed in '' and | |
416 | * "" as words even if they include spaces. */ | |
417 | char *split_quoted(const char *c, size_t *l, char **state) { | |
418 | const char *current, *e; | |
419 | bool escaped = false; | |
420 | ||
421 | assert(c); | |
422 | assert(l); | |
423 | assert(state); | |
424 | ||
425 | current = *state ? *state : c; | |
426 | ||
427 | current += strspn(current, WHITESPACE); | |
428 | ||
429 | if (*current == 0) | |
430 | return NULL; | |
431 | ||
432 | else if (*current == '\'') { | |
433 | current ++; | |
434 | ||
435 | for (e = current; *e; e++) { | |
436 | if (escaped) | |
437 | escaped = false; | |
438 | else if (*e == '\\') | |
439 | escaped = true; | |
440 | else if (*e == '\'') | |
441 | break; | |
442 | } | |
443 | ||
444 | *l = e-current; | |
445 | *state = (char*) (*e == 0 ? e : e+1); | |
446 | ||
447 | } else if (*current == '\"') { | |
448 | current ++; | |
449 | ||
450 | for (e = current; *e; e++) { | |
451 | if (escaped) | |
452 | escaped = false; | |
453 | else if (*e == '\\') | |
454 | escaped = true; | |
455 | else if (*e == '\"') | |
456 | break; | |
457 | } | |
458 | ||
459 | *l = e-current; | |
460 | *state = (char*) (*e == 0 ? e : e+1); | |
461 | ||
462 | } else { | |
463 | for (e = current; *e; e++) { | |
464 | if (escaped) | |
465 | escaped = false; | |
466 | else if (*e == '\\') | |
467 | escaped = true; | |
468 | else if (strchr(WHITESPACE, *e)) | |
469 | break; | |
470 | } | |
471 | *l = e-current; | |
472 | *state = (char*) e; | |
473 | } | |
474 | ||
475 | return (char*) current; | |
476 | } | |
477 | ||
478 | /* Split a string into words. */ | |
479 | char *split(const char *c, size_t *l, const char *separator, char **state) { | |
480 | char *current; | |
481 | ||
482 | current = *state ? *state : (char*) c; | |
483 | ||
484 | if (!*current || *c == 0) | |
485 | return NULL; | |
486 | ||
487 | current += strspn(current, separator); | |
488 | *l = strcspn(current, separator); | |
489 | *state = current+*l; | |
490 | ||
491 | return (char*) current; | |
492 | } | |
493 | ||
494 | int unhexchar(char c) { | |
495 | ||
496 | if (c >= '0' && c <= '9') | |
497 | return c - '0'; | |
498 | ||
499 | if (c >= 'a' && c <= 'f') | |
500 | return c - 'a' + 10; | |
501 | ||
502 | if (c >= 'A' && c <= 'F') | |
503 | return c - 'A' + 10; | |
504 | ||
505 | return -1; | |
506 | } | |
507 | ||
508 | int unoctchar(char c) { | |
509 | ||
510 | if (c >= '0' && c <= '7') | |
511 | return c - '0'; | |
512 | ||
513 | return -1; | |
514 | } |