]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/basic/util.c
util-lib: move string table stuff into its own string-table.[ch]
[thirdparty/systemd.git] / src / basic / util.c
CommitLineData
d6c9574f 1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
60918275 2
a7334b09
LP
3/***
4 This file is part of systemd.
5
6 Copyright 2010 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
5430f7f2
LP
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
a7334b09
LP
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
5430f7f2 16 Lesser General Public License for more details.
a7334b09 17
5430f7f2 18 You should have received a copy of the GNU Lesser General Public License
a7334b09
LP
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
f6c2284a
LP
22#include <ctype.h>
23#include <dirent.h>
60918275 24#include <errno.h>
f6c2284a
LP
25#include <fcntl.h>
26#include <glob.h>
27#include <grp.h>
28#include <langinfo.h>
20f56fdd 29#include <libintl.h>
f6c2284a
LP
30#include <limits.h>
31#include <linux/magic.h>
257b0719 32#include <linux/oom.h>
ef886c6a 33#include <linux/sched.h>
f6c2284a 34#include <locale.h>
0a6f50c0 35#include <poll.h>
ef2f1067 36#include <pwd.h>
f6c2284a
LP
37#include <sched.h>
38#include <signal.h>
39#include <stdarg.h>
40#include <stdio.h>
41#include <stdlib.h>
42#include <string.h>
43#include <sys/file.h>
44#include <sys/ioctl.h>
87d2c1ff 45#include <sys/mman.h>
6d313367 46#include <sys/mount.h>
6afc95b7 47#include <sys/personality.h>
f6c2284a 48#include <sys/prctl.h>
f6c2284a 49#include <sys/stat.h>
abe4aa14 50#include <sys/statvfs.h>
f6c2284a
LP
51#include <sys/time.h>
52#include <sys/types.h>
53#include <sys/utsname.h>
54#include <sys/vfs.h>
55#include <sys/wait.h>
f6c2284a
LP
56#include <syslog.h>
57#include <unistd.h>
eef46c37
LP
58
59/* When we include libgen.h because we need dirname() we immediately
f6c2284a
LP
60 * undefine basename() since libgen.h defines it as a macro to the
61 * POSIX version which is really broken. We prefer GNU basename(). */
eef46c37 62#include <libgen.h>
2b6bf07d 63#undef basename
60918275 64
9bf3b535
LP
65#ifdef HAVE_SYS_AUXV_H
66#include <sys/auxv.h>
67#endif
68
f6c2284a
LP
69/* We include linux/fs.h as last of the system headers, as it
70 * otherwise conflicts with sys/mount.h. Yay, Linux is great! */
71#include <linux/fs.h>
72
3f6fd1ba 73#include "build.h"
f6c2284a
LP
74#include "def.h"
75#include "device-nodes.h"
76#include "env-util.h"
4f5dd394 77#include "escape.h"
f6c2284a 78#include "exit-status.h"
3ffd4af2 79#include "fd-util.h"
f6c2284a
LP
80#include "fileio.h"
81#include "formats-util.h"
82#include "gunicode.h"
83#include "hashmap.h"
84#include "hostname-util.h"
1dccbe19 85#include "ioprio.h"
a9f5d454 86#include "log.h"
f6c2284a
LP
87#include "macro.h"
88#include "missing.h"
c38dfac9 89#include "mkdir.h"
e4e73a63 90#include "hexdecoct.h"
6bedfcbb 91#include "parse-util.h"
9eb977db 92#include "path-util.h"
0b452006 93#include "process-util.h"
3df3e884 94#include "random-util.h"
24882e06 95#include "signal-util.h"
f6c2284a 96#include "sparse-endian.h"
8b43440b 97#include "string-table.h"
07630cea 98#include "string-util.h"
f6c2284a
LP
99#include "strv.h"
100#include "terminal-util.h"
b1d4f8e1 101#include "user-util.h"
f6c2284a 102#include "utf8.h"
4f5dd394 103#include "util.h"
3ffd4af2 104#include "virt.h"
a0956174 105#include "dirent-util.h"
8fcde012 106#include "stat-util.h"
56cf987f 107
012d7b42
ZJS
108/* Put this test here for a lack of better place */
109assert_cc(EAGAIN == EWOULDBLOCK);
110
9a0e6896
LP
111int saved_argc = 0;
112char **saved_argv = NULL;
9086e840 113
37f85e66 114size_t page_size(void) {
ec202eae 115 static thread_local size_t pgsz = 0;
37f85e66 116 long r;
117
87d2c1ff 118 if (_likely_(pgsz > 0))
37f85e66 119 return pgsz;
120
e67f47e5
LP
121 r = sysconf(_SC_PAGESIZE);
122 assert(r > 0);
37f85e66 123
124 pgsz = (size_t) r;
37f85e66 125 return pgsz;
126}
127
42856c10 128bool fstype_is_network(const char *fstype) {
a05f97b3 129 static const char table[] =
ba89821c 130 "afs\0"
a05f97b3
LP
131 "cifs\0"
132 "smbfs\0"
da92ca5e 133 "sshfs\0"
a05f97b3 134 "ncpfs\0"
dac70dc7 135 "ncp\0"
a05f97b3
LP
136 "nfs\0"
137 "nfs4\0"
138 "gfs\0"
67608cad
LP
139 "gfs2\0"
140 "glusterfs\0";
141
142 const char *x;
143
144 x = startswith(fstype, "fuse.");
145 if (x)
146 fstype = x;
42856c10 147
a05f97b3 148 return nulstr_contains(table, fstype);
42856c10
LP
149}
150
5b6319dc
LP
151void rename_process(const char name[8]) {
152 assert(name);
153
5d6b1584
LP
154 /* This is a like a poor man's setproctitle(). It changes the
155 * comm field, argv[0], and also the glibc's internally used
156 * name of the process. For the first one a limit of 16 chars
157 * applies, to the second one usually one of 10 (i.e. length
158 * of "/sbin/init"), to the third one one of 7 (i.e. length of
159 * "systemd"). If you pass a longer string it will be
160 * truncated */
5b6319dc 161
5d6b1584 162 prctl(PR_SET_NAME, name);
5b6319dc
LP
163
164 if (program_invocation_name)
165 strncpy(program_invocation_name, name, strlen(program_invocation_name));
9a0e6896
LP
166
167 if (saved_argc > 0) {
168 int i;
169
170 if (saved_argv[0])
171 strncpy(saved_argv[0], name, strlen(saved_argv[0]));
172
173 for (i = 1; i < saved_argc; i++) {
174 if (!saved_argv[i])
175 break;
176
29804cc1 177 memzero(saved_argv[i], strlen(saved_argv[i]));
9a0e6896
LP
178 }
179 }
5b6319dc
LP
180}
181
9d9951a4
HH
182int running_in_chroot(void) {
183 int ret;
184
185 ret = files_same("/proc/1/root", "/");
186 if (ret < 0)
187 return ret;
188
189 return ret == 0;
b4f10a5e
LP
190}
191
919ce0b7 192noreturn void freeze(void) {
720ce21d
LP
193
194 /* Make sure nobody waits for us on a socket anymore */
195 close_all_fds(NULL, 0);
196
c29597a1
LP
197 sync();
198
3c14d26c
LP
199 for (;;)
200 pause();
201}
202
e801700e 203static int do_execute(char **directories, usec_t timeout, char *argv[]) {
49681057 204 _cleanup_hashmap_free_free_ Hashmap *pids = NULL;
e801700e
ZJS
205 _cleanup_set_free_free_ Set *seen = NULL;
206 char **directory;
83cc030f 207
49681057
ZJS
208 /* We fork this all off from a child process so that we can
209 * somewhat cleanly make use of SIGALRM to set a time limit */
83cc030f 210
ce30c8dc
LP
211 (void) reset_all_signal_handlers();
212 (void) reset_signal_mask();
83cc030f 213
49681057 214 assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
83cc030f 215
49681057
ZJS
216 pids = hashmap_new(NULL);
217 if (!pids)
218 return log_oom();
83cc030f 219
e801700e
ZJS
220 seen = set_new(&string_hash_ops);
221 if (!seen)
222 return log_oom();
83cc030f 223
e801700e
ZJS
224 STRV_FOREACH(directory, directories) {
225 _cleanup_closedir_ DIR *d;
226 struct dirent *de;
83cc030f 227
e801700e
ZJS
228 d = opendir(*directory);
229 if (!d) {
230 if (errno == ENOENT)
231 continue;
83cc030f 232
e801700e
ZJS
233 return log_error_errno(errno, "Failed to open directory %s: %m", *directory);
234 }
83cc030f 235
e801700e
ZJS
236 FOREACH_DIRENT(de, d, break) {
237 _cleanup_free_ char *path = NULL;
238 pid_t pid;
239 int r;
240
241 if (!dirent_is_file(de))
242 continue;
83cc030f 243
e801700e
ZJS
244 if (set_contains(seen, de->d_name)) {
245 log_debug("%1$s/%2$s skipped (%2$s was already seen).", *directory, de->d_name);
246 continue;
247 }
248
249 r = set_put_strdup(seen, de->d_name);
250 if (r < 0)
251 return log_oom();
252
253 path = strjoin(*directory, "/", de->d_name, NULL);
254 if (!path)
255 return log_oom();
256
257 if (null_or_empty_path(path)) {
258 log_debug("%s is empty (a mask).", path);
259 continue;
7034e9db 260 }
83cc030f 261
e801700e
ZJS
262 pid = fork();
263 if (pid < 0) {
264 log_error_errno(errno, "Failed to fork: %m");
265 continue;
266 } else if (pid == 0) {
267 char *_argv[2];
83cc030f 268
e801700e 269 assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
83cc030f 270
e801700e
ZJS
271 if (!argv) {
272 _argv[0] = path;
273 _argv[1] = NULL;
274 argv = _argv;
275 } else
276 argv[0] = path;
277
278 execv(path, argv);
279 return log_error_errno(errno, "Failed to execute %s: %m", path);
280 }
281
282 log_debug("Spawned %s as " PID_FMT ".", path, pid);
83cc030f 283
e801700e
ZJS
284 r = hashmap_put(pids, UINT_TO_PTR(pid), path);
285 if (r < 0)
286 return log_oom();
287 path = NULL;
288 }
49681057 289 }
83cc030f 290
49681057
ZJS
291 /* Abort execution of this process after the timout. We simply
292 * rely on SIGALRM as default action terminating the process,
293 * and turn on alarm(). */
83cc030f 294
49681057
ZJS
295 if (timeout != USEC_INFINITY)
296 alarm((timeout + USEC_PER_SEC - 1) / USEC_PER_SEC);
83cc030f 297
49681057
ZJS
298 while (!hashmap_isempty(pids)) {
299 _cleanup_free_ char *path = NULL;
300 pid_t pid;
aa62a893 301
49681057
ZJS
302 pid = PTR_TO_UINT(hashmap_first_key(pids));
303 assert(pid > 0);
83cc030f 304
49681057
ZJS
305 path = hashmap_remove(pids, UINT_TO_PTR(pid));
306 assert(path);
aa62a893 307
49681057
ZJS
308 wait_for_terminate_and_warn(path, pid, true);
309 }
aa62a893 310
49681057
ZJS
311 return 0;
312}
aa62a893 313
e801700e 314void execute_directories(const char* const* directories, usec_t timeout, char *argv[]) {
49681057
ZJS
315 pid_t executor_pid;
316 int r;
e801700e
ZJS
317 char *name;
318 char **dirs = (char**) directories;
319
320 assert(!strv_isempty(dirs));
83cc030f 321
e801700e
ZJS
322 name = basename(dirs[0]);
323 assert(!isempty(name));
aa62a893 324
e801700e
ZJS
325 /* Executes all binaries in the directories in parallel and waits
326 * for them to finish. Optionally a timeout is applied. If a file
327 * with the same name exists in more than one directory, the
328 * earliest one wins. */
83cc030f 329
49681057
ZJS
330 executor_pid = fork();
331 if (executor_pid < 0) {
332 log_error_errno(errno, "Failed to fork: %m");
333 return;
334
335 } else if (executor_pid == 0) {
e801700e 336 r = do_execute(dirs, timeout, argv);
49681057 337 _exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS);
aa62a893 338 }
83cc030f 339
e801700e 340 wait_for_terminate_and_warn(name, executor_pid, true);
83cc030f
LP
341}
342
a88c8750
TG
343bool plymouth_running(void) {
344 return access("/run/plymouth/pid", F_OK) >= 0;
345}
346
4d6d6518
LP
347bool display_is_local(const char *display) {
348 assert(display);
349
350 return
351 display[0] == ':' &&
352 display[1] >= '0' &&
353 display[1] <= '9';
354}
355
356int socket_from_display(const char *display, char **path) {
357 size_t k;
358 char *f, *c;
359
360 assert(display);
361 assert(path);
362
363 if (!display_is_local(display))
364 return -EINVAL;
365
366 k = strspn(display+1, "0123456789");
367
f8294e41 368 f = new(char, strlen("/tmp/.X11-unix/X") + k + 1);
4d6d6518
LP
369 if (!f)
370 return -ENOMEM;
371
372 c = stpcpy(f, "/tmp/.X11-unix/X");
373 memcpy(c, display+1, k);
374 c[k] = 0;
375
376 *path = f;
377
378 return 0;
379}
380
8092a428 381int glob_exists(const char *path) {
7fd1b19b 382 _cleanup_globfree_ glob_t g = {};
8d98da3f 383 int k;
8092a428
LP
384
385 assert(path);
386
8092a428
LP
387 errno = 0;
388 k = glob(path, GLOB_NOSORT|GLOB_BRACE, NULL, &g);
389
390 if (k == GLOB_NOMATCH)
8d98da3f 391 return 0;
8092a428 392 else if (k == GLOB_NOSPACE)
8d98da3f 393 return -ENOMEM;
8092a428 394 else if (k == 0)
8d98da3f 395 return !strv_isempty(g.gl_pathv);
8092a428 396 else
8d98da3f
ZJS
397 return errno ? -errno : -EIO;
398}
8092a428 399
8d98da3f
ZJS
400int glob_extend(char ***strv, const char *path) {
401 _cleanup_globfree_ glob_t g = {};
402 int k;
403 char **p;
404
405 errno = 0;
a8ccacf5 406 k = glob(path, GLOB_NOSORT|GLOB_BRACE, NULL, &g);
8d98da3f
ZJS
407
408 if (k == GLOB_NOMATCH)
409 return -ENOENT;
410 else if (k == GLOB_NOSPACE)
411 return -ENOMEM;
412 else if (k != 0 || strv_isempty(g.gl_pathv))
413 return errno ? -errno : -EIO;
414
415 STRV_FOREACH(p, g.gl_pathv) {
416 k = strv_extend(strv, *p);
417 if (k < 0)
418 break;
419 }
420
421 return k;
8092a428
LP
422}
423
b636465b 424bool is_main_thread(void) {
ec202eae 425 static thread_local int cached = 0;
b636465b
LP
426
427 if (_unlikely_(cached == 0))
428 cached = getpid() == gettid() ? 1 : -1;
429
430 return cached > 0;
431}
432
94959f0f
LP
433int block_get_whole_disk(dev_t d, dev_t *ret) {
434 char *p, *s;
435 int r;
436 unsigned n, m;
437
438 assert(ret);
439
440 /* If it has a queue this is good enough for us */
441 if (asprintf(&p, "/sys/dev/block/%u:%u/queue", major(d), minor(d)) < 0)
442 return -ENOMEM;
443
444 r = access(p, F_OK);
445 free(p);
446
447 if (r >= 0) {
448 *ret = d;
449 return 0;
450 }
451
452 /* If it is a partition find the originating device */
453 if (asprintf(&p, "/sys/dev/block/%u:%u/partition", major(d), minor(d)) < 0)
454 return -ENOMEM;
455
456 r = access(p, F_OK);
457 free(p);
458
459 if (r < 0)
460 return -ENOENT;
461
462 /* Get parent dev_t */
463 if (asprintf(&p, "/sys/dev/block/%u:%u/../dev", major(d), minor(d)) < 0)
464 return -ENOMEM;
465
466 r = read_one_line_file(p, &s);
467 free(p);
468
469 if (r < 0)
470 return r;
471
472 r = sscanf(s, "%u:%u", &m, &n);
473 free(s);
474
475 if (r != 2)
476 return -EINVAL;
477
478 /* Only return this if it is really good enough for us. */
479 if (asprintf(&p, "/sys/dev/block/%u:%u/queue", m, n) < 0)
480 return -ENOMEM;
481
482 r = access(p, F_OK);
483 free(p);
484
485 if (r >= 0) {
486 *ret = makedev(m, n);
487 return 0;
488 }
489
490 return -ENOENT;
491}
492
f41607a6
LP
493static const char *const ioprio_class_table[] = {
494 [IOPRIO_CLASS_NONE] = "none",
495 [IOPRIO_CLASS_RT] = "realtime",
496 [IOPRIO_CLASS_BE] = "best-effort",
497 [IOPRIO_CLASS_IDLE] = "idle"
498};
499
f8b69d1d 500DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(ioprio_class, int, INT_MAX);
f41607a6
LP
501
502static const char *const sigchld_code_table[] = {
503 [CLD_EXITED] = "exited",
504 [CLD_KILLED] = "killed",
505 [CLD_DUMPED] = "dumped",
506 [CLD_TRAPPED] = "trapped",
507 [CLD_STOPPED] = "stopped",
508 [CLD_CONTINUED] = "continued",
509};
510
511DEFINE_STRING_TABLE_LOOKUP(sigchld_code, int);
512
513static const char *const log_facility_unshifted_table[LOG_NFACILITIES] = {
514 [LOG_FAC(LOG_KERN)] = "kern",
515 [LOG_FAC(LOG_USER)] = "user",
516 [LOG_FAC(LOG_MAIL)] = "mail",
517 [LOG_FAC(LOG_DAEMON)] = "daemon",
518 [LOG_FAC(LOG_AUTH)] = "auth",
519 [LOG_FAC(LOG_SYSLOG)] = "syslog",
520 [LOG_FAC(LOG_LPR)] = "lpr",
521 [LOG_FAC(LOG_NEWS)] = "news",
522 [LOG_FAC(LOG_UUCP)] = "uucp",
523 [LOG_FAC(LOG_CRON)] = "cron",
524 [LOG_FAC(LOG_AUTHPRIV)] = "authpriv",
525 [LOG_FAC(LOG_FTP)] = "ftp",
526 [LOG_FAC(LOG_LOCAL0)] = "local0",
527 [LOG_FAC(LOG_LOCAL1)] = "local1",
528 [LOG_FAC(LOG_LOCAL2)] = "local2",
529 [LOG_FAC(LOG_LOCAL3)] = "local3",
530 [LOG_FAC(LOG_LOCAL4)] = "local4",
531 [LOG_FAC(LOG_LOCAL5)] = "local5",
532 [LOG_FAC(LOG_LOCAL6)] = "local6",
533 [LOG_FAC(LOG_LOCAL7)] = "local7"
534};
535
f8b69d1d 536DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(log_facility_unshifted, int, LOG_FAC(~0));
f41607a6 537
adb8ec96
EV
538bool log_facility_unshifted_is_valid(int facility) {
539 return facility >= 0 && facility <= LOG_FAC(~0);
540}
541
f41607a6
LP
542static const char *const log_level_table[] = {
543 [LOG_EMERG] = "emerg",
544 [LOG_ALERT] = "alert",
545 [LOG_CRIT] = "crit",
546 [LOG_ERR] = "err",
547 [LOG_WARNING] = "warning",
548 [LOG_NOTICE] = "notice",
549 [LOG_INFO] = "info",
550 [LOG_DEBUG] = "debug"
551};
552
f8b69d1d 553DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(log_level, int, LOG_DEBUG);
f41607a6 554
adb8ec96
EV
555bool log_level_is_valid(int level) {
556 return level >= 0 && level <= LOG_DEBUG;
557}
558
f41607a6
LP
559static const char* const sched_policy_table[] = {
560 [SCHED_OTHER] = "other",
561 [SCHED_BATCH] = "batch",
562 [SCHED_IDLE] = "idle",
563 [SCHED_FIFO] = "fifo",
564 [SCHED_RR] = "rr"
565};
566
f8b69d1d 567DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(sched_policy, int, INT_MAX);
f41607a6 568
65457142
FC
569bool kexec_loaded(void) {
570 bool loaded = false;
571 char *s;
572
573 if (read_one_line_file("/sys/kernel/kexec_loaded", &s) >= 0) {
574 if (s[0] == '1')
575 loaded = true;
576 free(s);
577 }
578 return loaded;
579}
fb9de93d 580
87d2c1ff
LP
581int prot_from_flags(int flags) {
582
583 switch (flags & O_ACCMODE) {
584
585 case O_RDONLY:
586 return PROT_READ;
587
588 case O_WRONLY:
589 return PROT_WRITE;
590
591 case O_RDWR:
592 return PROT_READ|PROT_WRITE;
593
594 default:
595 return -EINVAL;
596 }
7c99e0c1 597}
689b9a22 598
55d7bfc1
LP
599void* memdup(const void *p, size_t l) {
600 void *r;
601
602 assert(p);
603
604 r = malloc(l);
605 if (!r)
606 return NULL;
607
608 memcpy(r, p, l);
609 return r;
610}
bb99a35a 611
9bdc770c 612int fork_agent(pid_t *pid, const int except[], unsigned n_except, const char *path, ...) {
6bb92a16 613 bool stdout_is_tty, stderr_is_tty;
8a7c93d8
LP
614 pid_t parent_pid, agent_pid;
615 sigset_t ss, saved_ss;
6bb92a16
LP
616 unsigned n, i;
617 va_list ap;
618 char **l;
619
620 assert(pid);
621 assert(path);
622
6bb92a16
LP
623 /* Spawns a temporary TTY agent, making sure it goes away when
624 * we go away */
625
8a7c93d8
LP
626 parent_pid = getpid();
627
628 /* First we temporarily block all signals, so that the new
629 * child has them blocked initially. This way, we can be sure
630 * that SIGTERMs are not lost we might send to the agent. */
631 assert_se(sigfillset(&ss) >= 0);
632 assert_se(sigprocmask(SIG_SETMASK, &ss, &saved_ss) >= 0);
633
6bb92a16 634 agent_pid = fork();
8a7c93d8
LP
635 if (agent_pid < 0) {
636 assert_se(sigprocmask(SIG_SETMASK, &saved_ss, NULL) >= 0);
6bb92a16 637 return -errno;
8a7c93d8 638 }
6bb92a16
LP
639
640 if (agent_pid != 0) {
8a7c93d8 641 assert_se(sigprocmask(SIG_SETMASK, &saved_ss, NULL) >= 0);
6bb92a16
LP
642 *pid = agent_pid;
643 return 0;
644 }
645
646 /* In the child:
647 *
648 * Make sure the agent goes away when the parent dies */
649 if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0)
650 _exit(EXIT_FAILURE);
651
8a7c93d8
LP
652 /* Make sure we actually can kill the agent, if we need to, in
653 * case somebody invoked us from a shell script that trapped
654 * SIGTERM or so... */
ce30c8dc
LP
655 (void) reset_all_signal_handlers();
656 (void) reset_signal_mask();
8a7c93d8 657
6bb92a16 658 /* Check whether our parent died before we were able
8a7c93d8 659 * to set the death signal and unblock the signals */
6bb92a16
LP
660 if (getppid() != parent_pid)
661 _exit(EXIT_SUCCESS);
662
663 /* Don't leak fds to the agent */
9bdc770c 664 close_all_fds(except, n_except);
6bb92a16
LP
665
666 stdout_is_tty = isatty(STDOUT_FILENO);
667 stderr_is_tty = isatty(STDERR_FILENO);
668
669 if (!stdout_is_tty || !stderr_is_tty) {
8a7c93d8
LP
670 int fd;
671
6bb92a16
LP
672 /* Detach from stdout/stderr. and reopen
673 * /dev/tty for them. This is important to
674 * ensure that when systemctl is started via
675 * popen() or a similar call that expects to
676 * read EOF we actually do generate EOF and
677 * not delay this indefinitely by because we
678 * keep an unused copy of stdin around. */
679 fd = open("/dev/tty", O_WRONLY);
680 if (fd < 0) {
56f64d95 681 log_error_errno(errno, "Failed to open /dev/tty: %m");
6bb92a16
LP
682 _exit(EXIT_FAILURE);
683 }
684
685 if (!stdout_is_tty)
686 dup2(fd, STDOUT_FILENO);
687
688 if (!stderr_is_tty)
689 dup2(fd, STDERR_FILENO);
690
691 if (fd > 2)
692 close(fd);
693 }
694
695 /* Count arguments */
696 va_start(ap, path);
697 for (n = 0; va_arg(ap, char*); n++)
698 ;
699 va_end(ap);
700
701 /* Allocate strv */
702 l = alloca(sizeof(char *) * (n + 1));
703
704 /* Fill in arguments */
705 va_start(ap, path);
706 for (i = 0; i <= n; i++)
707 l[i] = va_arg(ap, char*);
708 va_end(ap);
709
710 execv(path, l);
711 _exit(EXIT_FAILURE);
712}
68faf98c 713
3d7415f4
LP
714bool http_etag_is_valid(const char *etag) {
715 if (isempty(etag))
716 return false;
717
718 if (!endswith(etag, "\""))
719 return false;
720
721 if (!startswith(etag, "\"") && !startswith(etag, "W/\""))
722 return false;
723
724 return true;
725}
726
a2e03378
LP
727bool http_url_is_valid(const char *url) {
728 const char *p;
49dbfa7b 729
a2e03378
LP
730 if (isempty(url))
731 return false;
49dbfa7b 732
a2e03378
LP
733 p = startswith(url, "http://");
734 if (!p)
735 p = startswith(url, "https://");
736 if (!p)
737 return false;
49dbfa7b 738
a2e03378
LP
739 if (isempty(p))
740 return false;
49dbfa7b 741
a2e03378
LP
742 return ascii_is_valid(p);
743}
49dbfa7b 744
a2e03378
LP
745bool documentation_url_is_valid(const char *url) {
746 const char *p;
747
748 if (isempty(url))
749 return false;
750
751 if (http_url_is_valid(url))
49dbfa7b
LP
752 return true;
753
a2e03378
LP
754 p = startswith(url, "file:/");
755 if (!p)
756 p = startswith(url, "info:");
757 if (!p)
758 p = startswith(url, "man:");
759
760 if (isempty(p))
761 return false;
762
763 return ascii_is_valid(p);
49dbfa7b 764}
9be346c9
HH
765
766bool in_initrd(void) {
73020ab2 767 static int saved = -1;
825c6fe5 768 struct statfs s;
8f33b5b8 769
825c6fe5
LP
770 if (saved >= 0)
771 return saved;
772
773 /* We make two checks here:
774 *
775 * 1. the flag file /etc/initrd-release must exist
776 * 2. the root file system must be a memory file system
777 *
778 * The second check is extra paranoia, since misdetecting an
779 * initrd can have bad bad consequences due the initrd
780 * emptying when transititioning to the main systemd.
781 */
782
783 saved = access("/etc/initrd-release", F_OK) >= 0 &&
784 statfs("/", &s) >= 0 &&
943aad8c 785 is_temporary_fs(&s);
9be346c9 786
8f33b5b8 787 return saved;
9be346c9 788}
069cfc85 789
a9e12476
KS
790/* hey glibc, APIs with callbacks without a user pointer are so useless */
791void *xbsearch_r(const void *key, const void *base, size_t nmemb, size_t size,
1c574591 792 int (*compar) (const void *, const void *, void *), void *arg) {
a9e12476
KS
793 size_t l, u, idx;
794 const void *p;
795 int comparison;
796
797 l = 0;
798 u = nmemb;
799 while (l < u) {
800 idx = (l + u) / 2;
801 p = (void *)(((const char *) base) + (idx * size));
802 comparison = compar(key, p, arg);
803 if (comparison < 0)
804 u = idx;
805 else if (comparison > 0)
806 l = idx + 1;
807 else
808 return (void *)p;
809 }
810 return NULL;
811}
09017585 812
20f56fdd
DR
813void init_gettext(void) {
814 setlocale(LC_ALL, "");
815 textdomain(GETTEXT_PACKAGE);
816}
817
09017585
MS
818bool is_locale_utf8(void) {
819 const char *set;
820 static int cached_answer = -1;
821
822 if (cached_answer >= 0)
823 goto out;
824
825 if (!setlocale(LC_ALL, "")) {
826 cached_answer = true;
827 goto out;
828 }
829
830 set = nl_langinfo(CODESET);
831 if (!set) {
832 cached_answer = true;
833 goto out;
834 }
835
f168c273 836 if (streq(set, "UTF-8")) {
fee79e01
HH
837 cached_answer = true;
838 goto out;
839 }
840
6cf2f1d9
HH
841 /* For LC_CTYPE=="C" return true, because CTYPE is effectly
842 * unset and everything can do to UTF-8 nowadays. */
fee79e01
HH
843 set = setlocale(LC_CTYPE, NULL);
844 if (!set) {
845 cached_answer = true;
846 goto out;
847 }
848
6cf2f1d9
HH
849 /* Check result, but ignore the result if C was set
850 * explicitly. */
851 cached_answer =
9797f89b 852 STR_IN_SET(set, "C", "POSIX") &&
6cf2f1d9
HH
853 !getenv("LC_ALL") &&
854 !getenv("LC_CTYPE") &&
855 !getenv("LANG");
fee79e01 856
09017585 857out:
6cf2f1d9 858 return (bool) cached_answer;
09017585 859}
c339d977
MS
860
861const char *draw_special_char(DrawSpecialChar ch) {
862 static const char *draw_table[2][_DRAW_SPECIAL_CHAR_MAX] = {
6b01f1d3 863
c339d977 864 /* UTF-8 */ {
6b01f1d3 865 [DRAW_TREE_VERTICAL] = "\342\224\202 ", /* │ */
45a5ff0d
MS
866 [DRAW_TREE_BRANCH] = "\342\224\234\342\224\200", /* ├─ */
867 [DRAW_TREE_RIGHT] = "\342\224\224\342\224\200", /* └─ */
55c0b89c 868 [DRAW_TREE_SPACE] = " ", /* */
6b01f1d3
LP
869 [DRAW_TRIANGULAR_BULLET] = "\342\200\243", /* ‣ */
870 [DRAW_BLACK_CIRCLE] = "\342\227\217", /* ● */
871 [DRAW_ARROW] = "\342\206\222", /* → */
13f8b8cb 872 [DRAW_DASH] = "\342\200\223", /* – */
c339d977 873 },
6b01f1d3 874
c339d977 875 /* ASCII fallback */ {
6b01f1d3 876 [DRAW_TREE_VERTICAL] = "| ",
45a5ff0d
MS
877 [DRAW_TREE_BRANCH] = "|-",
878 [DRAW_TREE_RIGHT] = "`-",
55c0b89c 879 [DRAW_TREE_SPACE] = " ",
6b01f1d3
LP
880 [DRAW_TRIANGULAR_BULLET] = ">",
881 [DRAW_BLACK_CIRCLE] = "*",
882 [DRAW_ARROW] = "->",
13f8b8cb 883 [DRAW_DASH] = "-",
c339d977
MS
884 }
885 };
886
887 return draw_table[!is_locale_utf8()][ch];
888}
409bc9c3 889
240dbaa4
LP
890int on_ac_power(void) {
891 bool found_offline = false, found_online = false;
892 _cleanup_closedir_ DIR *d = NULL;
893
894 d = opendir("/sys/class/power_supply");
895 if (!d)
6d890034 896 return errno == ENOENT ? true : -errno;
240dbaa4
LP
897
898 for (;;) {
899 struct dirent *de;
240dbaa4
LP
900 _cleanup_close_ int fd = -1, device = -1;
901 char contents[6];
902 ssize_t n;
240dbaa4 903
3fd11280
FW
904 errno = 0;
905 de = readdir(d);
906 if (!de && errno != 0)
907 return -errno;
240dbaa4
LP
908
909 if (!de)
910 break;
911
a34bf9db 912 if (hidden_file(de->d_name))
240dbaa4
LP
913 continue;
914
915 device = openat(dirfd(d), de->d_name, O_DIRECTORY|O_RDONLY|O_CLOEXEC|O_NOCTTY);
916 if (device < 0) {
917 if (errno == ENOENT || errno == ENOTDIR)
918 continue;
919
920 return -errno;
921 }
922
923 fd = openat(device, "type", O_RDONLY|O_CLOEXEC|O_NOCTTY);
924 if (fd < 0) {
925 if (errno == ENOENT)
926 continue;
927
928 return -errno;
929 }
930
931 n = read(fd, contents, sizeof(contents));
932 if (n < 0)
933 return -errno;
934
935 if (n != 6 || memcmp(contents, "Mains\n", 6))
936 continue;
937
03e334a1 938 safe_close(fd);
240dbaa4
LP
939 fd = openat(device, "online", O_RDONLY|O_CLOEXEC|O_NOCTTY);
940 if (fd < 0) {
941 if (errno == ENOENT)
942 continue;
943
944 return -errno;
945 }
946
947 n = read(fd, contents, sizeof(contents));
948 if (n < 0)
949 return -errno;
950
951 if (n != 2 || contents[1] != '\n')
952 return -EIO;
953
954 if (contents[0] == '1') {
955 found_online = true;
956 break;
957 } else if (contents[0] == '0')
958 found_offline = true;
959 else
960 return -EIO;
961 }
962
963 return found_online || !found_offline;
964}
fabe5c0e 965
ca2d3784
ZJS
966void* greedy_realloc(void **p, size_t *allocated, size_t need, size_t size) {
967 size_t a, newalloc;
392d5b37
LP
968 void *q;
969
98088803 970 assert(p);
e93c33d4
SL
971 assert(allocated);
972
392d5b37
LP
973 if (*allocated >= need)
974 return *p;
975
ca2d3784
ZJS
976 newalloc = MAX(need * 2, 64u / size);
977 a = newalloc * size;
98088803
LP
978
979 /* check for overflows */
ca2d3784 980 if (a < size * need)
98088803
LP
981 return NULL;
982
392d5b37
LP
983 q = realloc(*p, a);
984 if (!q)
985 return NULL;
986
987 *p = q;
ca2d3784 988 *allocated = newalloc;
392d5b37
LP
989 return q;
990}
aa96c6cb 991
ca2d3784 992void* greedy_realloc0(void **p, size_t *allocated, size_t need, size_t size) {
98088803 993 size_t prev;
4545a231
DH
994 uint8_t *q;
995
98088803
LP
996 assert(p);
997 assert(allocated);
998
999 prev = *allocated;
1000
ca2d3784 1001 q = greedy_realloc(p, allocated, need, size);
4545a231
DH
1002 if (!q)
1003 return NULL;
1004
1005 if (*allocated > prev)
ca2d3784 1006 memzero(q + prev * size, (*allocated - prev) * size);
4545a231
DH
1007
1008 return q;
1009}
1010
aa96c6cb
LP
1011bool id128_is_valid(const char *s) {
1012 size_t i, l;
1013
1014 l = strlen(s);
1015 if (l == 32) {
1016
1017 /* Simple formatted 128bit hex string */
1018
1019 for (i = 0; i < l; i++) {
1020 char c = s[i];
1021
1022 if (!(c >= '0' && c <= '9') &&
1023 !(c >= 'a' && c <= 'z') &&
1024 !(c >= 'A' && c <= 'Z'))
1025 return false;
1026 }
1027
1028 } else if (l == 36) {
1029
1030 /* Formatted UUID */
1031
1032 for (i = 0; i < l; i++) {
1033 char c = s[i];
1034
1035 if ((i == 8 || i == 13 || i == 18 || i == 23)) {
1036 if (c != '-')
1037 return false;
1038 } else {
1039 if (!(c >= '0' && c <= '9') &&
1040 !(c >= 'a' && c <= 'z') &&
1041 !(c >= 'A' && c <= 'Z'))
1042 return false;
1043 }
1044 }
1045
1046 } else
1047 return false;
1048
1049 return true;
1050}
7085053a 1051
74df0fca 1052int shall_restore_state(void) {
1a299299 1053 _cleanup_free_ char *value = NULL;
74df0fca 1054 int r;
295edddf 1055
1a299299 1056 r = get_proc_cmdline_key("systemd.restore_state=", &value);
74df0fca
LP
1057 if (r < 0)
1058 return r;
1a299299
LP
1059 if (r == 0)
1060 return true;
295edddf 1061
1a299299 1062 return parse_boolean(value) != 0;
74df0fca
LP
1063}
1064
1065int proc_cmdline(char **ret) {
b5884878 1066 assert(ret);
295edddf 1067
75f86906 1068 if (detect_container() > 0)
b5884878
LP
1069 return get_process_cmdline(1, 0, false, ret);
1070 else
1071 return read_one_line_file("/proc/cmdline", ret);
295edddf 1072}
bc9fd78c 1073
059cb385 1074int parse_proc_cmdline(int (*parse_item)(const char *key, const char *value)) {
141a79f4 1075 _cleanup_free_ char *line = NULL;
f32d2db1 1076 const char *p;
141a79f4
ZJS
1077 int r;
1078
059cb385
LP
1079 assert(parse_item);
1080
141a79f4
ZJS
1081 r = proc_cmdline(&line);
1082 if (r < 0)
b5884878 1083 return r;
141a79f4 1084
f32d2db1
LP
1085 p = line;
1086 for (;;) {
1087 _cleanup_free_ char *word = NULL;
1088 char *value = NULL;
141a79f4 1089
12ba2c44 1090 r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES|EXTRACT_RELAX);
f32d2db1
LP
1091 if (r < 0)
1092 return r;
1093 if (r == 0)
1094 break;
141a79f4 1095
059cb385
LP
1096 /* Filter out arguments that are intended only for the
1097 * initrd */
1098 if (!in_initrd() && startswith(word, "rd."))
1099 continue;
1100
1101 value = strchr(word, '=');
1102 if (value)
1103 *(value++) = 0;
1104
1105 r = parse_item(word, value);
1106 if (r < 0)
141a79f4 1107 return r;
141a79f4
ZJS
1108 }
1109
1110 return 0;
1111}
1112
1a299299
LP
1113int get_proc_cmdline_key(const char *key, char **value) {
1114 _cleanup_free_ char *line = NULL, *ret = NULL;
1115 bool found = false;
1116 const char *p;
1117 int r;
1118
1119 assert(key);
1120
1121 r = proc_cmdline(&line);
1122 if (r < 0)
1123 return r;
1124
1125 p = line;
1126 for (;;) {
1127 _cleanup_free_ char *word = NULL;
1128 const char *e;
1129
12ba2c44 1130 r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES|EXTRACT_RELAX);
1a299299
LP
1131 if (r < 0)
1132 return r;
1133 if (r == 0)
1134 break;
1135
1136 /* Filter out arguments that are intended only for the
1137 * initrd */
1138 if (!in_initrd() && startswith(word, "rd."))
1139 continue;
1140
1141 if (value) {
1142 e = startswith(word, key);
1143 if (!e)
1144 continue;
1145
1146 r = free_and_strdup(&ret, e);
1147 if (r < 0)
1148 return r;
1149
1150 found = true;
1151 } else {
1152 if (streq(word, key))
1153 found = true;
1154 }
1155 }
1156
1157 if (value) {
1158 *value = ret;
1159 ret = NULL;
1160 }
1161
1162 return found;
1163
1164}
1165
bc9fd78c
LP
1166int container_get_leader(const char *machine, pid_t *pid) {
1167 _cleanup_free_ char *s = NULL, *class = NULL;
1168 const char *p;
1169 pid_t leader;
1170 int r;
1171
1172 assert(machine);
1173 assert(pid);
1174
b9a8d250
LP
1175 if (!machine_name_is_valid(machine))
1176 return -EINVAL;
1177
63c372cb 1178 p = strjoina("/run/systemd/machines/", machine);
bc9fd78c
LP
1179 r = parse_env_file(p, NEWLINE, "LEADER", &s, "CLASS", &class, NULL);
1180 if (r == -ENOENT)
1181 return -EHOSTDOWN;
1182 if (r < 0)
1183 return r;
1184 if (!s)
1185 return -EIO;
1186
1187 if (!streq_ptr(class, "container"))
1188 return -EIO;
1189
1190 r = parse_pid(s, &leader);
1191 if (r < 0)
1192 return r;
1193 if (leader <= 1)
1194 return -EIO;
1195
1196 *pid = leader;
1197 return 0;
1198}
1199
671c3419
RM
1200int namespace_open(pid_t pid, int *pidns_fd, int *mntns_fd, int *netns_fd, int *userns_fd, int *root_fd) {
1201 _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, netnsfd = -1, usernsfd = -1;
359a06aa 1202 int rfd = -1;
bc9fd78c
LP
1203
1204 assert(pid >= 0);
bc9fd78c 1205
878cd7e9
LP
1206 if (mntns_fd) {
1207 const char *mntns;
a4475f57 1208
878cd7e9
LP
1209 mntns = procfs_file_alloca(pid, "ns/mnt");
1210 mntnsfd = open(mntns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
1211 if (mntnsfd < 0)
1212 return -errno;
1213 }
bc9fd78c 1214
878cd7e9
LP
1215 if (pidns_fd) {
1216 const char *pidns;
1217
1218 pidns = procfs_file_alloca(pid, "ns/pid");
1219 pidnsfd = open(pidns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
1220 if (pidnsfd < 0)
1221 return -errno;
1222 }
1223
1224 if (netns_fd) {
1225 const char *netns;
1226
1227 netns = procfs_file_alloca(pid, "ns/net");
1228 netnsfd = open(netns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
1229 if (netnsfd < 0)
1230 return -errno;
1231 }
1232
671c3419
RM
1233 if (userns_fd) {
1234 const char *userns;
1235
1236 userns = procfs_file_alloca(pid, "ns/user");
1237 usernsfd = open(userns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
1238 if (usernsfd < 0 && errno != ENOENT)
1239 return -errno;
1240 }
1241
878cd7e9
LP
1242 if (root_fd) {
1243 const char *root;
1244
1245 root = procfs_file_alloca(pid, "root");
1246 rfd = open(root, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
1247 if (rfd < 0)
1248 return -errno;
1249 }
1250
1251 if (pidns_fd)
1252 *pidns_fd = pidnsfd;
bc9fd78c 1253
878cd7e9
LP
1254 if (mntns_fd)
1255 *mntns_fd = mntnsfd;
1256
1257 if (netns_fd)
1258 *netns_fd = netnsfd;
1259
671c3419
RM
1260 if (userns_fd)
1261 *userns_fd = usernsfd;
1262
878cd7e9
LP
1263 if (root_fd)
1264 *root_fd = rfd;
1265
671c3419 1266 pidnsfd = mntnsfd = netnsfd = usernsfd = -1;
bc9fd78c
LP
1267
1268 return 0;
1269}
1270
671c3419
RM
1271int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int root_fd) {
1272 if (userns_fd >= 0) {
1273 /* Can't setns to your own userns, since then you could
1274 * escalate from non-root to root in your own namespace, so
1275 * check if namespaces equal before attempting to enter. */
1276 _cleanup_free_ char *userns_fd_path = NULL;
1277 int r;
1278 if (asprintf(&userns_fd_path, "/proc/self/fd/%d", userns_fd) < 0)
1279 return -ENOMEM;
1280
1281 r = files_same(userns_fd_path, "/proc/self/ns/user");
1282 if (r < 0)
1283 return r;
1284 if (r)
1285 userns_fd = -1;
1286 }
bc9fd78c 1287
878cd7e9
LP
1288 if (pidns_fd >= 0)
1289 if (setns(pidns_fd, CLONE_NEWPID) < 0)
1290 return -errno;
a4475f57 1291
878cd7e9
LP
1292 if (mntns_fd >= 0)
1293 if (setns(mntns_fd, CLONE_NEWNS) < 0)
1294 return -errno;
bc9fd78c 1295
878cd7e9
LP
1296 if (netns_fd >= 0)
1297 if (setns(netns_fd, CLONE_NEWNET) < 0)
1298 return -errno;
bc9fd78c 1299
671c3419
RM
1300 if (userns_fd >= 0)
1301 if (setns(userns_fd, CLONE_NEWUSER) < 0)
1302 return -errno;
1303
878cd7e9
LP
1304 if (root_fd >= 0) {
1305 if (fchdir(root_fd) < 0)
1306 return -errno;
1307
1308 if (chroot(".") < 0)
1309 return -errno;
1310 }
bc9fd78c 1311
b4da6d6b 1312 return reset_uid_gid();
bc9fd78c 1313}
bf108e55 1314
ac45f971 1315unsigned long personality_from_string(const char *p) {
6afc95b7
LP
1316
1317 /* Parse a personality specifier. We introduce our own
1318 * identifiers that indicate specific ABIs, rather than just
1319 * hints regarding the register size, since we want to keep
1320 * things open for multiple locally supported ABIs for the
1321 * same register size. We try to reuse the ABI identifiers
1322 * used by libseccomp. */
1323
1324#if defined(__x86_64__)
1325
1326 if (streq(p, "x86"))
1327 return PER_LINUX32;
1328
1329 if (streq(p, "x86-64"))
1330 return PER_LINUX;
1331
1332#elif defined(__i386__)
1333
1334 if (streq(p, "x86"))
1335 return PER_LINUX;
7517f51e
HB
1336
1337#elif defined(__s390x__)
1338
1339 if (streq(p, "s390"))
1340 return PER_LINUX32;
1341
1342 if (streq(p, "s390x"))
1343 return PER_LINUX;
1344
1345#elif defined(__s390__)
1346
1347 if (streq(p, "s390"))
1348 return PER_LINUX;
6afc95b7
LP
1349#endif
1350
050f7277 1351 return PERSONALITY_INVALID;
6afc95b7 1352}
ac45f971
LP
1353
1354const char* personality_to_string(unsigned long p) {
1355
1356#if defined(__x86_64__)
1357
1358 if (p == PER_LINUX32)
1359 return "x86";
1360
1361 if (p == PER_LINUX)
1362 return "x86-64";
1363
1364#elif defined(__i386__)
1365
1366 if (p == PER_LINUX)
1367 return "x86";
7517f51e
HB
1368
1369#elif defined(__s390x__)
1370
1371 if (p == PER_LINUX)
1372 return "s390x";
1373
1374 if (p == PER_LINUX32)
1375 return "s390";
1376
1377#elif defined(__s390__)
1378
1379 if (p == PER_LINUX)
1380 return "s390";
1381
ac45f971
LP
1382#endif
1383
1384 return NULL;
1385}
1c231f56
LP
1386
1387uint64_t physical_memory(void) {
1388 long mem;
1389
1390 /* We return this as uint64_t in case we are running as 32bit
1391 * process on a 64bit kernel with huge amounts of memory */
1392
1393 mem = sysconf(_SC_PHYS_PAGES);
1394 assert(mem > 0);
1395
1396 return (uint64_t) mem * (uint64_t) page_size();
1397}
6db615c1 1398
966bff26 1399int update_reboot_param_file(const char *param) {
c5220a94
MO
1400 int r = 0;
1401
1402 if (param) {
4c1fc3e4 1403 r = write_string_file(REBOOT_PARAM_FILE, param, WRITE_STRING_FILE_CREATE);
c5220a94 1404 if (r < 0)
e53fc357 1405 return log_error_errno(r, "Failed to write reboot param to "REBOOT_PARAM_FILE": %m");
c5220a94 1406 } else
e53fc357 1407 (void) unlink(REBOOT_PARAM_FILE);
c5220a94 1408
e53fc357 1409 return 0;
c5220a94 1410}
6d313367 1411
3d7415f4
LP
1412int syslog_parse_priority(const char **p, int *priority, bool with_facility) {
1413 int a = 0, b = 0, c = 0;
1414 int k;
1415
1416 assert(p);
1417 assert(*p);
1418 assert(priority);
1419
1420 if ((*p)[0] != '<')
1421 return 0;
1422
1423 if (!strchr(*p, '>'))
1424 return 0;
1425
1426 if ((*p)[2] == '>') {
1427 c = undecchar((*p)[1]);
1428 k = 3;
1429 } else if ((*p)[3] == '>') {
1430 b = undecchar((*p)[1]);
1431 c = undecchar((*p)[2]);
1432 k = 4;
1433 } else if ((*p)[4] == '>') {
1434 a = undecchar((*p)[1]);
1435 b = undecchar((*p)[2]);
1436 c = undecchar((*p)[3]);
1437 k = 5;
1438 } else
1439 return 0;
1440
1441 if (a < 0 || b < 0 || c < 0 ||
1442 (!with_facility && (a || b || c > 7)))
1443 return 0;
1444
1445 if (with_facility)
1446 *priority = a*100 + b*10 + c;
1447 else
1448 *priority = (*priority & LOG_FACMASK) | c;
1449
1450 *p += k;
1451 return 1;
1452}
9cad100e 1453
3f6fd1ba
LP
1454int version(void) {
1455 puts(PACKAGE_STRING "\n"
1456 SYSTEMD_FEATURES);
1457 return 0;
1458}
8dd4c05b
LP
1459
1460bool fdname_is_valid(const char *s) {
1461 const char *p;
1462
0a3bb96e 1463 /* Validates a name for $LISTEN_FDNAMES. We basically allow
8dd4c05b
LP
1464 * everything ASCII that's not a control character. Also, as
1465 * special exception the ":" character is not allowed, as we
0a3bb96e 1466 * use that as field separator in $LISTEN_FDNAMES.
8dd4c05b 1467 *
0a3bb96e
LP
1468 * Note that the empty string is explicitly allowed
1469 * here. However, we limit the length of the names to 255
1470 * characters. */
8dd4c05b
LP
1471
1472 if (!s)
1473 return false;
1474
1475 for (p = s; *p; p++) {
1476 if (*p < ' ')
1477 return false;
1478 if (*p >= 127)
1479 return false;
1480 if (*p == ':')
1481 return false;
1482 }
1483
1484 return p - s < 256;
1485}
257b0719
EV
1486
1487bool oom_score_adjust_is_valid(int oa) {
1488 return oa >= OOM_SCORE_ADJ_MIN && oa <= OOM_SCORE_ADJ_MAX;
1489}