]>
Commit | Line | Data |
---|---|---|
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 */ |
109 | assert_cc(EAGAIN == EWOULDBLOCK); | |
110 | ||
9a0e6896 LP |
111 | int saved_argc = 0; |
112 | char **saved_argv = NULL; | |
9086e840 | 113 | |
37f85e66 | 114 | size_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 | 128 | bool 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 |
151 | void 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 | ||
919ce0b7 | 182 | noreturn void freeze(void) { |
720ce21d LP |
183 | |
184 | /* Make sure nobody waits for us on a socket anymore */ | |
185 | close_all_fds(NULL, 0); | |
186 | ||
c29597a1 LP |
187 | sync(); |
188 | ||
3c14d26c LP |
189 | for (;;) |
190 | pause(); | |
191 | } | |
192 | ||
e801700e | 193 | static int do_execute(char **directories, usec_t timeout, char *argv[]) { |
49681057 | 194 | _cleanup_hashmap_free_free_ Hashmap *pids = NULL; |
e801700e ZJS |
195 | _cleanup_set_free_free_ Set *seen = NULL; |
196 | char **directory; | |
83cc030f | 197 | |
49681057 ZJS |
198 | /* We fork this all off from a child process so that we can |
199 | * somewhat cleanly make use of SIGALRM to set a time limit */ | |
83cc030f | 200 | |
ce30c8dc LP |
201 | (void) reset_all_signal_handlers(); |
202 | (void) reset_signal_mask(); | |
83cc030f | 203 | |
49681057 | 204 | assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0); |
83cc030f | 205 | |
49681057 ZJS |
206 | pids = hashmap_new(NULL); |
207 | if (!pids) | |
208 | return log_oom(); | |
83cc030f | 209 | |
e801700e ZJS |
210 | seen = set_new(&string_hash_ops); |
211 | if (!seen) | |
212 | return log_oom(); | |
83cc030f | 213 | |
e801700e ZJS |
214 | STRV_FOREACH(directory, directories) { |
215 | _cleanup_closedir_ DIR *d; | |
216 | struct dirent *de; | |
83cc030f | 217 | |
e801700e ZJS |
218 | d = opendir(*directory); |
219 | if (!d) { | |
220 | if (errno == ENOENT) | |
221 | continue; | |
83cc030f | 222 | |
e801700e ZJS |
223 | return log_error_errno(errno, "Failed to open directory %s: %m", *directory); |
224 | } | |
83cc030f | 225 | |
e801700e ZJS |
226 | FOREACH_DIRENT(de, d, break) { |
227 | _cleanup_free_ char *path = NULL; | |
228 | pid_t pid; | |
229 | int r; | |
230 | ||
231 | if (!dirent_is_file(de)) | |
232 | continue; | |
83cc030f | 233 | |
e801700e ZJS |
234 | if (set_contains(seen, de->d_name)) { |
235 | log_debug("%1$s/%2$s skipped (%2$s was already seen).", *directory, de->d_name); | |
236 | continue; | |
237 | } | |
238 | ||
239 | r = set_put_strdup(seen, de->d_name); | |
240 | if (r < 0) | |
241 | return log_oom(); | |
242 | ||
243 | path = strjoin(*directory, "/", de->d_name, NULL); | |
244 | if (!path) | |
245 | return log_oom(); | |
246 | ||
247 | if (null_or_empty_path(path)) { | |
248 | log_debug("%s is empty (a mask).", path); | |
249 | continue; | |
7034e9db | 250 | } |
83cc030f | 251 | |
e801700e ZJS |
252 | pid = fork(); |
253 | if (pid < 0) { | |
254 | log_error_errno(errno, "Failed to fork: %m"); | |
255 | continue; | |
256 | } else if (pid == 0) { | |
257 | char *_argv[2]; | |
83cc030f | 258 | |
e801700e | 259 | assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0); |
83cc030f | 260 | |
e801700e ZJS |
261 | if (!argv) { |
262 | _argv[0] = path; | |
263 | _argv[1] = NULL; | |
264 | argv = _argv; | |
265 | } else | |
266 | argv[0] = path; | |
267 | ||
268 | execv(path, argv); | |
269 | return log_error_errno(errno, "Failed to execute %s: %m", path); | |
270 | } | |
271 | ||
272 | log_debug("Spawned %s as " PID_FMT ".", path, pid); | |
83cc030f | 273 | |
e801700e ZJS |
274 | r = hashmap_put(pids, UINT_TO_PTR(pid), path); |
275 | if (r < 0) | |
276 | return log_oom(); | |
277 | path = NULL; | |
278 | } | |
49681057 | 279 | } |
83cc030f | 280 | |
49681057 ZJS |
281 | /* Abort execution of this process after the timout. We simply |
282 | * rely on SIGALRM as default action terminating the process, | |
283 | * and turn on alarm(). */ | |
83cc030f | 284 | |
49681057 ZJS |
285 | if (timeout != USEC_INFINITY) |
286 | alarm((timeout + USEC_PER_SEC - 1) / USEC_PER_SEC); | |
83cc030f | 287 | |
49681057 ZJS |
288 | while (!hashmap_isempty(pids)) { |
289 | _cleanup_free_ char *path = NULL; | |
290 | pid_t pid; | |
aa62a893 | 291 | |
49681057 ZJS |
292 | pid = PTR_TO_UINT(hashmap_first_key(pids)); |
293 | assert(pid > 0); | |
83cc030f | 294 | |
49681057 ZJS |
295 | path = hashmap_remove(pids, UINT_TO_PTR(pid)); |
296 | assert(path); | |
aa62a893 | 297 | |
49681057 ZJS |
298 | wait_for_terminate_and_warn(path, pid, true); |
299 | } | |
aa62a893 | 300 | |
49681057 ZJS |
301 | return 0; |
302 | } | |
aa62a893 | 303 | |
e801700e | 304 | void execute_directories(const char* const* directories, usec_t timeout, char *argv[]) { |
49681057 ZJS |
305 | pid_t executor_pid; |
306 | int r; | |
e801700e ZJS |
307 | char *name; |
308 | char **dirs = (char**) directories; | |
309 | ||
310 | assert(!strv_isempty(dirs)); | |
83cc030f | 311 | |
e801700e ZJS |
312 | name = basename(dirs[0]); |
313 | assert(!isempty(name)); | |
aa62a893 | 314 | |
e801700e ZJS |
315 | /* Executes all binaries in the directories in parallel and waits |
316 | * for them to finish. Optionally a timeout is applied. If a file | |
317 | * with the same name exists in more than one directory, the | |
318 | * earliest one wins. */ | |
83cc030f | 319 | |
49681057 ZJS |
320 | executor_pid = fork(); |
321 | if (executor_pid < 0) { | |
322 | log_error_errno(errno, "Failed to fork: %m"); | |
323 | return; | |
324 | ||
325 | } else if (executor_pid == 0) { | |
e801700e | 326 | r = do_execute(dirs, timeout, argv); |
49681057 | 327 | _exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS); |
aa62a893 | 328 | } |
83cc030f | 329 | |
e801700e | 330 | wait_for_terminate_and_warn(name, executor_pid, true); |
83cc030f LP |
331 | } |
332 | ||
a88c8750 TG |
333 | bool plymouth_running(void) { |
334 | return access("/run/plymouth/pid", F_OK) >= 0; | |
335 | } | |
336 | ||
4d6d6518 LP |
337 | bool display_is_local(const char *display) { |
338 | assert(display); | |
339 | ||
340 | return | |
341 | display[0] == ':' && | |
342 | display[1] >= '0' && | |
343 | display[1] <= '9'; | |
344 | } | |
345 | ||
346 | int socket_from_display(const char *display, char **path) { | |
347 | size_t k; | |
348 | char *f, *c; | |
349 | ||
350 | assert(display); | |
351 | assert(path); | |
352 | ||
353 | if (!display_is_local(display)) | |
354 | return -EINVAL; | |
355 | ||
356 | k = strspn(display+1, "0123456789"); | |
357 | ||
f8294e41 | 358 | f = new(char, strlen("/tmp/.X11-unix/X") + k + 1); |
4d6d6518 LP |
359 | if (!f) |
360 | return -ENOMEM; | |
361 | ||
362 | c = stpcpy(f, "/tmp/.X11-unix/X"); | |
363 | memcpy(c, display+1, k); | |
364 | c[k] = 0; | |
365 | ||
366 | *path = f; | |
367 | ||
368 | return 0; | |
369 | } | |
370 | ||
8092a428 | 371 | int glob_exists(const char *path) { |
7fd1b19b | 372 | _cleanup_globfree_ glob_t g = {}; |
8d98da3f | 373 | int k; |
8092a428 LP |
374 | |
375 | assert(path); | |
376 | ||
8092a428 LP |
377 | errno = 0; |
378 | k = glob(path, GLOB_NOSORT|GLOB_BRACE, NULL, &g); | |
379 | ||
380 | if (k == GLOB_NOMATCH) | |
8d98da3f | 381 | return 0; |
8092a428 | 382 | else if (k == GLOB_NOSPACE) |
8d98da3f | 383 | return -ENOMEM; |
8092a428 | 384 | else if (k == 0) |
8d98da3f | 385 | return !strv_isempty(g.gl_pathv); |
8092a428 | 386 | else |
8d98da3f ZJS |
387 | return errno ? -errno : -EIO; |
388 | } | |
8092a428 | 389 | |
8d98da3f ZJS |
390 | int glob_extend(char ***strv, const char *path) { |
391 | _cleanup_globfree_ glob_t g = {}; | |
392 | int k; | |
393 | char **p; | |
394 | ||
395 | errno = 0; | |
a8ccacf5 | 396 | k = glob(path, GLOB_NOSORT|GLOB_BRACE, NULL, &g); |
8d98da3f ZJS |
397 | |
398 | if (k == GLOB_NOMATCH) | |
399 | return -ENOENT; | |
400 | else if (k == GLOB_NOSPACE) | |
401 | return -ENOMEM; | |
402 | else if (k != 0 || strv_isempty(g.gl_pathv)) | |
403 | return errno ? -errno : -EIO; | |
404 | ||
405 | STRV_FOREACH(p, g.gl_pathv) { | |
406 | k = strv_extend(strv, *p); | |
407 | if (k < 0) | |
408 | break; | |
409 | } | |
410 | ||
411 | return k; | |
8092a428 LP |
412 | } |
413 | ||
94959f0f LP |
414 | int block_get_whole_disk(dev_t d, dev_t *ret) { |
415 | char *p, *s; | |
416 | int r; | |
417 | unsigned n, m; | |
418 | ||
419 | assert(ret); | |
420 | ||
421 | /* If it has a queue this is good enough for us */ | |
422 | if (asprintf(&p, "/sys/dev/block/%u:%u/queue", major(d), minor(d)) < 0) | |
423 | return -ENOMEM; | |
424 | ||
425 | r = access(p, F_OK); | |
426 | free(p); | |
427 | ||
428 | if (r >= 0) { | |
429 | *ret = d; | |
430 | return 0; | |
431 | } | |
432 | ||
433 | /* If it is a partition find the originating device */ | |
434 | if (asprintf(&p, "/sys/dev/block/%u:%u/partition", major(d), minor(d)) < 0) | |
435 | return -ENOMEM; | |
436 | ||
437 | r = access(p, F_OK); | |
438 | free(p); | |
439 | ||
440 | if (r < 0) | |
441 | return -ENOENT; | |
442 | ||
443 | /* Get parent dev_t */ | |
444 | if (asprintf(&p, "/sys/dev/block/%u:%u/../dev", major(d), minor(d)) < 0) | |
445 | return -ENOMEM; | |
446 | ||
447 | r = read_one_line_file(p, &s); | |
448 | free(p); | |
449 | ||
450 | if (r < 0) | |
451 | return r; | |
452 | ||
453 | r = sscanf(s, "%u:%u", &m, &n); | |
454 | free(s); | |
455 | ||
456 | if (r != 2) | |
457 | return -EINVAL; | |
458 | ||
459 | /* Only return this if it is really good enough for us. */ | |
460 | if (asprintf(&p, "/sys/dev/block/%u:%u/queue", m, n) < 0) | |
461 | return -ENOMEM; | |
462 | ||
463 | r = access(p, F_OK); | |
464 | free(p); | |
465 | ||
466 | if (r >= 0) { | |
467 | *ret = makedev(m, n); | |
468 | return 0; | |
469 | } | |
470 | ||
471 | return -ENOENT; | |
472 | } | |
473 | ||
f41607a6 LP |
474 | static const char *const ioprio_class_table[] = { |
475 | [IOPRIO_CLASS_NONE] = "none", | |
476 | [IOPRIO_CLASS_RT] = "realtime", | |
477 | [IOPRIO_CLASS_BE] = "best-effort", | |
478 | [IOPRIO_CLASS_IDLE] = "idle" | |
479 | }; | |
480 | ||
f8b69d1d | 481 | DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(ioprio_class, int, INT_MAX); |
f41607a6 LP |
482 | |
483 | static const char *const sigchld_code_table[] = { | |
484 | [CLD_EXITED] = "exited", | |
485 | [CLD_KILLED] = "killed", | |
486 | [CLD_DUMPED] = "dumped", | |
487 | [CLD_TRAPPED] = "trapped", | |
488 | [CLD_STOPPED] = "stopped", | |
489 | [CLD_CONTINUED] = "continued", | |
490 | }; | |
491 | ||
492 | DEFINE_STRING_TABLE_LOOKUP(sigchld_code, int); | |
493 | ||
f41607a6 LP |
494 | static const char* const sched_policy_table[] = { |
495 | [SCHED_OTHER] = "other", | |
496 | [SCHED_BATCH] = "batch", | |
497 | [SCHED_IDLE] = "idle", | |
498 | [SCHED_FIFO] = "fifo", | |
499 | [SCHED_RR] = "rr" | |
500 | }; | |
501 | ||
f8b69d1d | 502 | DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(sched_policy, int, INT_MAX); |
f41607a6 | 503 | |
65457142 FC |
504 | bool kexec_loaded(void) { |
505 | bool loaded = false; | |
506 | char *s; | |
507 | ||
508 | if (read_one_line_file("/sys/kernel/kexec_loaded", &s) >= 0) { | |
509 | if (s[0] == '1') | |
510 | loaded = true; | |
511 | free(s); | |
512 | } | |
513 | return loaded; | |
514 | } | |
fb9de93d | 515 | |
87d2c1ff LP |
516 | int prot_from_flags(int flags) { |
517 | ||
518 | switch (flags & O_ACCMODE) { | |
519 | ||
520 | case O_RDONLY: | |
521 | return PROT_READ; | |
522 | ||
523 | case O_WRONLY: | |
524 | return PROT_WRITE; | |
525 | ||
526 | case O_RDWR: | |
527 | return PROT_READ|PROT_WRITE; | |
528 | ||
529 | default: | |
530 | return -EINVAL; | |
531 | } | |
7c99e0c1 | 532 | } |
689b9a22 | 533 | |
55d7bfc1 LP |
534 | void* memdup(const void *p, size_t l) { |
535 | void *r; | |
536 | ||
537 | assert(p); | |
538 | ||
539 | r = malloc(l); | |
540 | if (!r) | |
541 | return NULL; | |
542 | ||
543 | memcpy(r, p, l); | |
544 | return r; | |
545 | } | |
bb99a35a | 546 | |
9bdc770c | 547 | int fork_agent(pid_t *pid, const int except[], unsigned n_except, const char *path, ...) { |
6bb92a16 | 548 | bool stdout_is_tty, stderr_is_tty; |
8a7c93d8 LP |
549 | pid_t parent_pid, agent_pid; |
550 | sigset_t ss, saved_ss; | |
6bb92a16 LP |
551 | unsigned n, i; |
552 | va_list ap; | |
553 | char **l; | |
554 | ||
555 | assert(pid); | |
556 | assert(path); | |
557 | ||
6bb92a16 LP |
558 | /* Spawns a temporary TTY agent, making sure it goes away when |
559 | * we go away */ | |
560 | ||
8a7c93d8 LP |
561 | parent_pid = getpid(); |
562 | ||
563 | /* First we temporarily block all signals, so that the new | |
564 | * child has them blocked initially. This way, we can be sure | |
565 | * that SIGTERMs are not lost we might send to the agent. */ | |
566 | assert_se(sigfillset(&ss) >= 0); | |
567 | assert_se(sigprocmask(SIG_SETMASK, &ss, &saved_ss) >= 0); | |
568 | ||
6bb92a16 | 569 | agent_pid = fork(); |
8a7c93d8 LP |
570 | if (agent_pid < 0) { |
571 | assert_se(sigprocmask(SIG_SETMASK, &saved_ss, NULL) >= 0); | |
6bb92a16 | 572 | return -errno; |
8a7c93d8 | 573 | } |
6bb92a16 LP |
574 | |
575 | if (agent_pid != 0) { | |
8a7c93d8 | 576 | assert_se(sigprocmask(SIG_SETMASK, &saved_ss, NULL) >= 0); |
6bb92a16 LP |
577 | *pid = agent_pid; |
578 | return 0; | |
579 | } | |
580 | ||
581 | /* In the child: | |
582 | * | |
583 | * Make sure the agent goes away when the parent dies */ | |
584 | if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0) | |
585 | _exit(EXIT_FAILURE); | |
586 | ||
8a7c93d8 LP |
587 | /* Make sure we actually can kill the agent, if we need to, in |
588 | * case somebody invoked us from a shell script that trapped | |
589 | * SIGTERM or so... */ | |
ce30c8dc LP |
590 | (void) reset_all_signal_handlers(); |
591 | (void) reset_signal_mask(); | |
8a7c93d8 | 592 | |
6bb92a16 | 593 | /* Check whether our parent died before we were able |
8a7c93d8 | 594 | * to set the death signal and unblock the signals */ |
6bb92a16 LP |
595 | if (getppid() != parent_pid) |
596 | _exit(EXIT_SUCCESS); | |
597 | ||
598 | /* Don't leak fds to the agent */ | |
9bdc770c | 599 | close_all_fds(except, n_except); |
6bb92a16 LP |
600 | |
601 | stdout_is_tty = isatty(STDOUT_FILENO); | |
602 | stderr_is_tty = isatty(STDERR_FILENO); | |
603 | ||
604 | if (!stdout_is_tty || !stderr_is_tty) { | |
8a7c93d8 LP |
605 | int fd; |
606 | ||
6bb92a16 LP |
607 | /* Detach from stdout/stderr. and reopen |
608 | * /dev/tty for them. This is important to | |
609 | * ensure that when systemctl is started via | |
610 | * popen() or a similar call that expects to | |
611 | * read EOF we actually do generate EOF and | |
612 | * not delay this indefinitely by because we | |
613 | * keep an unused copy of stdin around. */ | |
614 | fd = open("/dev/tty", O_WRONLY); | |
615 | if (fd < 0) { | |
56f64d95 | 616 | log_error_errno(errno, "Failed to open /dev/tty: %m"); |
6bb92a16 LP |
617 | _exit(EXIT_FAILURE); |
618 | } | |
619 | ||
620 | if (!stdout_is_tty) | |
621 | dup2(fd, STDOUT_FILENO); | |
622 | ||
623 | if (!stderr_is_tty) | |
624 | dup2(fd, STDERR_FILENO); | |
625 | ||
626 | if (fd > 2) | |
627 | close(fd); | |
628 | } | |
629 | ||
630 | /* Count arguments */ | |
631 | va_start(ap, path); | |
632 | for (n = 0; va_arg(ap, char*); n++) | |
633 | ; | |
634 | va_end(ap); | |
635 | ||
636 | /* Allocate strv */ | |
637 | l = alloca(sizeof(char *) * (n + 1)); | |
638 | ||
639 | /* Fill in arguments */ | |
640 | va_start(ap, path); | |
641 | for (i = 0; i <= n; i++) | |
642 | l[i] = va_arg(ap, char*); | |
643 | va_end(ap); | |
644 | ||
645 | execv(path, l); | |
646 | _exit(EXIT_FAILURE); | |
647 | } | |
68faf98c | 648 | |
3d7415f4 LP |
649 | bool http_etag_is_valid(const char *etag) { |
650 | if (isempty(etag)) | |
651 | return false; | |
652 | ||
653 | if (!endswith(etag, "\"")) | |
654 | return false; | |
655 | ||
656 | if (!startswith(etag, "\"") && !startswith(etag, "W/\"")) | |
657 | return false; | |
658 | ||
659 | return true; | |
660 | } | |
661 | ||
a2e03378 LP |
662 | bool http_url_is_valid(const char *url) { |
663 | const char *p; | |
49dbfa7b | 664 | |
a2e03378 LP |
665 | if (isempty(url)) |
666 | return false; | |
49dbfa7b | 667 | |
a2e03378 LP |
668 | p = startswith(url, "http://"); |
669 | if (!p) | |
670 | p = startswith(url, "https://"); | |
671 | if (!p) | |
672 | return false; | |
49dbfa7b | 673 | |
a2e03378 LP |
674 | if (isempty(p)) |
675 | return false; | |
49dbfa7b | 676 | |
a2e03378 LP |
677 | return ascii_is_valid(p); |
678 | } | |
49dbfa7b | 679 | |
a2e03378 LP |
680 | bool documentation_url_is_valid(const char *url) { |
681 | const char *p; | |
682 | ||
683 | if (isempty(url)) | |
684 | return false; | |
685 | ||
686 | if (http_url_is_valid(url)) | |
49dbfa7b LP |
687 | return true; |
688 | ||
a2e03378 LP |
689 | p = startswith(url, "file:/"); |
690 | if (!p) | |
691 | p = startswith(url, "info:"); | |
692 | if (!p) | |
693 | p = startswith(url, "man:"); | |
694 | ||
695 | if (isempty(p)) | |
696 | return false; | |
697 | ||
698 | return ascii_is_valid(p); | |
49dbfa7b | 699 | } |
9be346c9 HH |
700 | |
701 | bool in_initrd(void) { | |
73020ab2 | 702 | static int saved = -1; |
825c6fe5 | 703 | struct statfs s; |
8f33b5b8 | 704 | |
825c6fe5 LP |
705 | if (saved >= 0) |
706 | return saved; | |
707 | ||
708 | /* We make two checks here: | |
709 | * | |
710 | * 1. the flag file /etc/initrd-release must exist | |
711 | * 2. the root file system must be a memory file system | |
712 | * | |
713 | * The second check is extra paranoia, since misdetecting an | |
714 | * initrd can have bad bad consequences due the initrd | |
715 | * emptying when transititioning to the main systemd. | |
716 | */ | |
717 | ||
718 | saved = access("/etc/initrd-release", F_OK) >= 0 && | |
719 | statfs("/", &s) >= 0 && | |
943aad8c | 720 | is_temporary_fs(&s); |
9be346c9 | 721 | |
8f33b5b8 | 722 | return saved; |
9be346c9 | 723 | } |
069cfc85 | 724 | |
a9e12476 KS |
725 | /* hey glibc, APIs with callbacks without a user pointer are so useless */ |
726 | void *xbsearch_r(const void *key, const void *base, size_t nmemb, size_t size, | |
1c574591 | 727 | int (*compar) (const void *, const void *, void *), void *arg) { |
a9e12476 KS |
728 | size_t l, u, idx; |
729 | const void *p; | |
730 | int comparison; | |
731 | ||
732 | l = 0; | |
733 | u = nmemb; | |
734 | while (l < u) { | |
735 | idx = (l + u) / 2; | |
736 | p = (void *)(((const char *) base) + (idx * size)); | |
737 | comparison = compar(key, p, arg); | |
738 | if (comparison < 0) | |
739 | u = idx; | |
740 | else if (comparison > 0) | |
741 | l = idx + 1; | |
742 | else | |
743 | return (void *)p; | |
744 | } | |
745 | return NULL; | |
746 | } | |
09017585 | 747 | |
240dbaa4 LP |
748 | int on_ac_power(void) { |
749 | bool found_offline = false, found_online = false; | |
750 | _cleanup_closedir_ DIR *d = NULL; | |
751 | ||
752 | d = opendir("/sys/class/power_supply"); | |
753 | if (!d) | |
6d890034 | 754 | return errno == ENOENT ? true : -errno; |
240dbaa4 LP |
755 | |
756 | for (;;) { | |
757 | struct dirent *de; | |
240dbaa4 LP |
758 | _cleanup_close_ int fd = -1, device = -1; |
759 | char contents[6]; | |
760 | ssize_t n; | |
240dbaa4 | 761 | |
3fd11280 FW |
762 | errno = 0; |
763 | de = readdir(d); | |
764 | if (!de && errno != 0) | |
765 | return -errno; | |
240dbaa4 LP |
766 | |
767 | if (!de) | |
768 | break; | |
769 | ||
a34bf9db | 770 | if (hidden_file(de->d_name)) |
240dbaa4 LP |
771 | continue; |
772 | ||
773 | device = openat(dirfd(d), de->d_name, O_DIRECTORY|O_RDONLY|O_CLOEXEC|O_NOCTTY); | |
774 | if (device < 0) { | |
775 | if (errno == ENOENT || errno == ENOTDIR) | |
776 | continue; | |
777 | ||
778 | return -errno; | |
779 | } | |
780 | ||
781 | fd = openat(device, "type", O_RDONLY|O_CLOEXEC|O_NOCTTY); | |
782 | if (fd < 0) { | |
783 | if (errno == ENOENT) | |
784 | continue; | |
785 | ||
786 | return -errno; | |
787 | } | |
788 | ||
789 | n = read(fd, contents, sizeof(contents)); | |
790 | if (n < 0) | |
791 | return -errno; | |
792 | ||
793 | if (n != 6 || memcmp(contents, "Mains\n", 6)) | |
794 | continue; | |
795 | ||
03e334a1 | 796 | safe_close(fd); |
240dbaa4 LP |
797 | fd = openat(device, "online", O_RDONLY|O_CLOEXEC|O_NOCTTY); |
798 | if (fd < 0) { | |
799 | if (errno == ENOENT) | |
800 | continue; | |
801 | ||
802 | return -errno; | |
803 | } | |
804 | ||
805 | n = read(fd, contents, sizeof(contents)); | |
806 | if (n < 0) | |
807 | return -errno; | |
808 | ||
809 | if (n != 2 || contents[1] != '\n') | |
810 | return -EIO; | |
811 | ||
812 | if (contents[0] == '1') { | |
813 | found_online = true; | |
814 | break; | |
815 | } else if (contents[0] == '0') | |
816 | found_offline = true; | |
817 | else | |
818 | return -EIO; | |
819 | } | |
820 | ||
821 | return found_online || !found_offline; | |
822 | } | |
fabe5c0e | 823 | |
ca2d3784 ZJS |
824 | void* greedy_realloc(void **p, size_t *allocated, size_t need, size_t size) { |
825 | size_t a, newalloc; | |
392d5b37 LP |
826 | void *q; |
827 | ||
98088803 | 828 | assert(p); |
e93c33d4 SL |
829 | assert(allocated); |
830 | ||
392d5b37 LP |
831 | if (*allocated >= need) |
832 | return *p; | |
833 | ||
ca2d3784 ZJS |
834 | newalloc = MAX(need * 2, 64u / size); |
835 | a = newalloc * size; | |
98088803 LP |
836 | |
837 | /* check for overflows */ | |
ca2d3784 | 838 | if (a < size * need) |
98088803 LP |
839 | return NULL; |
840 | ||
392d5b37 LP |
841 | q = realloc(*p, a); |
842 | if (!q) | |
843 | return NULL; | |
844 | ||
845 | *p = q; | |
ca2d3784 | 846 | *allocated = newalloc; |
392d5b37 LP |
847 | return q; |
848 | } | |
aa96c6cb | 849 | |
ca2d3784 | 850 | void* greedy_realloc0(void **p, size_t *allocated, size_t need, size_t size) { |
98088803 | 851 | size_t prev; |
4545a231 DH |
852 | uint8_t *q; |
853 | ||
98088803 LP |
854 | assert(p); |
855 | assert(allocated); | |
856 | ||
857 | prev = *allocated; | |
858 | ||
ca2d3784 | 859 | q = greedy_realloc(p, allocated, need, size); |
4545a231 DH |
860 | if (!q) |
861 | return NULL; | |
862 | ||
863 | if (*allocated > prev) | |
ca2d3784 | 864 | memzero(q + prev * size, (*allocated - prev) * size); |
4545a231 DH |
865 | |
866 | return q; | |
867 | } | |
868 | ||
aa96c6cb LP |
869 | bool id128_is_valid(const char *s) { |
870 | size_t i, l; | |
871 | ||
872 | l = strlen(s); | |
873 | if (l == 32) { | |
874 | ||
875 | /* Simple formatted 128bit hex string */ | |
876 | ||
877 | for (i = 0; i < l; i++) { | |
878 | char c = s[i]; | |
879 | ||
880 | if (!(c >= '0' && c <= '9') && | |
881 | !(c >= 'a' && c <= 'z') && | |
882 | !(c >= 'A' && c <= 'Z')) | |
883 | return false; | |
884 | } | |
885 | ||
886 | } else if (l == 36) { | |
887 | ||
888 | /* Formatted UUID */ | |
889 | ||
890 | for (i = 0; i < l; i++) { | |
891 | char c = s[i]; | |
892 | ||
893 | if ((i == 8 || i == 13 || i == 18 || i == 23)) { | |
894 | if (c != '-') | |
895 | return false; | |
896 | } else { | |
897 | if (!(c >= '0' && c <= '9') && | |
898 | !(c >= 'a' && c <= 'z') && | |
899 | !(c >= 'A' && c <= 'Z')) | |
900 | return false; | |
901 | } | |
902 | } | |
903 | ||
904 | } else | |
905 | return false; | |
906 | ||
907 | return true; | |
908 | } | |
7085053a | 909 | |
bc9fd78c LP |
910 | int container_get_leader(const char *machine, pid_t *pid) { |
911 | _cleanup_free_ char *s = NULL, *class = NULL; | |
912 | const char *p; | |
913 | pid_t leader; | |
914 | int r; | |
915 | ||
916 | assert(machine); | |
917 | assert(pid); | |
918 | ||
b9a8d250 LP |
919 | if (!machine_name_is_valid(machine)) |
920 | return -EINVAL; | |
921 | ||
63c372cb | 922 | p = strjoina("/run/systemd/machines/", machine); |
bc9fd78c LP |
923 | r = parse_env_file(p, NEWLINE, "LEADER", &s, "CLASS", &class, NULL); |
924 | if (r == -ENOENT) | |
925 | return -EHOSTDOWN; | |
926 | if (r < 0) | |
927 | return r; | |
928 | if (!s) | |
929 | return -EIO; | |
930 | ||
931 | if (!streq_ptr(class, "container")) | |
932 | return -EIO; | |
933 | ||
934 | r = parse_pid(s, &leader); | |
935 | if (r < 0) | |
936 | return r; | |
937 | if (leader <= 1) | |
938 | return -EIO; | |
939 | ||
940 | *pid = leader; | |
941 | return 0; | |
942 | } | |
943 | ||
671c3419 RM |
944 | int namespace_open(pid_t pid, int *pidns_fd, int *mntns_fd, int *netns_fd, int *userns_fd, int *root_fd) { |
945 | _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, netnsfd = -1, usernsfd = -1; | |
359a06aa | 946 | int rfd = -1; |
bc9fd78c LP |
947 | |
948 | assert(pid >= 0); | |
bc9fd78c | 949 | |
878cd7e9 LP |
950 | if (mntns_fd) { |
951 | const char *mntns; | |
a4475f57 | 952 | |
878cd7e9 LP |
953 | mntns = procfs_file_alloca(pid, "ns/mnt"); |
954 | mntnsfd = open(mntns, O_RDONLY|O_NOCTTY|O_CLOEXEC); | |
955 | if (mntnsfd < 0) | |
956 | return -errno; | |
957 | } | |
bc9fd78c | 958 | |
878cd7e9 LP |
959 | if (pidns_fd) { |
960 | const char *pidns; | |
961 | ||
962 | pidns = procfs_file_alloca(pid, "ns/pid"); | |
963 | pidnsfd = open(pidns, O_RDONLY|O_NOCTTY|O_CLOEXEC); | |
964 | if (pidnsfd < 0) | |
965 | return -errno; | |
966 | } | |
967 | ||
968 | if (netns_fd) { | |
969 | const char *netns; | |
970 | ||
971 | netns = procfs_file_alloca(pid, "ns/net"); | |
972 | netnsfd = open(netns, O_RDONLY|O_NOCTTY|O_CLOEXEC); | |
973 | if (netnsfd < 0) | |
974 | return -errno; | |
975 | } | |
976 | ||
671c3419 RM |
977 | if (userns_fd) { |
978 | const char *userns; | |
979 | ||
980 | userns = procfs_file_alloca(pid, "ns/user"); | |
981 | usernsfd = open(userns, O_RDONLY|O_NOCTTY|O_CLOEXEC); | |
982 | if (usernsfd < 0 && errno != ENOENT) | |
983 | return -errno; | |
984 | } | |
985 | ||
878cd7e9 LP |
986 | if (root_fd) { |
987 | const char *root; | |
988 | ||
989 | root = procfs_file_alloca(pid, "root"); | |
990 | rfd = open(root, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY); | |
991 | if (rfd < 0) | |
992 | return -errno; | |
993 | } | |
994 | ||
995 | if (pidns_fd) | |
996 | *pidns_fd = pidnsfd; | |
bc9fd78c | 997 | |
878cd7e9 LP |
998 | if (mntns_fd) |
999 | *mntns_fd = mntnsfd; | |
1000 | ||
1001 | if (netns_fd) | |
1002 | *netns_fd = netnsfd; | |
1003 | ||
671c3419 RM |
1004 | if (userns_fd) |
1005 | *userns_fd = usernsfd; | |
1006 | ||
878cd7e9 LP |
1007 | if (root_fd) |
1008 | *root_fd = rfd; | |
1009 | ||
671c3419 | 1010 | pidnsfd = mntnsfd = netnsfd = usernsfd = -1; |
bc9fd78c LP |
1011 | |
1012 | return 0; | |
1013 | } | |
1014 | ||
671c3419 RM |
1015 | int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int root_fd) { |
1016 | if (userns_fd >= 0) { | |
1017 | /* Can't setns to your own userns, since then you could | |
1018 | * escalate from non-root to root in your own namespace, so | |
1019 | * check if namespaces equal before attempting to enter. */ | |
1020 | _cleanup_free_ char *userns_fd_path = NULL; | |
1021 | int r; | |
1022 | if (asprintf(&userns_fd_path, "/proc/self/fd/%d", userns_fd) < 0) | |
1023 | return -ENOMEM; | |
1024 | ||
1025 | r = files_same(userns_fd_path, "/proc/self/ns/user"); | |
1026 | if (r < 0) | |
1027 | return r; | |
1028 | if (r) | |
1029 | userns_fd = -1; | |
1030 | } | |
bc9fd78c | 1031 | |
878cd7e9 LP |
1032 | if (pidns_fd >= 0) |
1033 | if (setns(pidns_fd, CLONE_NEWPID) < 0) | |
1034 | return -errno; | |
a4475f57 | 1035 | |
878cd7e9 LP |
1036 | if (mntns_fd >= 0) |
1037 | if (setns(mntns_fd, CLONE_NEWNS) < 0) | |
1038 | return -errno; | |
bc9fd78c | 1039 | |
878cd7e9 LP |
1040 | if (netns_fd >= 0) |
1041 | if (setns(netns_fd, CLONE_NEWNET) < 0) | |
1042 | return -errno; | |
bc9fd78c | 1043 | |
671c3419 RM |
1044 | if (userns_fd >= 0) |
1045 | if (setns(userns_fd, CLONE_NEWUSER) < 0) | |
1046 | return -errno; | |
1047 | ||
878cd7e9 LP |
1048 | if (root_fd >= 0) { |
1049 | if (fchdir(root_fd) < 0) | |
1050 | return -errno; | |
1051 | ||
1052 | if (chroot(".") < 0) | |
1053 | return -errno; | |
1054 | } | |
bc9fd78c | 1055 | |
b4da6d6b | 1056 | return reset_uid_gid(); |
bc9fd78c | 1057 | } |
bf108e55 | 1058 | |
ac45f971 | 1059 | unsigned long personality_from_string(const char *p) { |
6afc95b7 LP |
1060 | |
1061 | /* Parse a personality specifier. We introduce our own | |
1062 | * identifiers that indicate specific ABIs, rather than just | |
1063 | * hints regarding the register size, since we want to keep | |
1064 | * things open for multiple locally supported ABIs for the | |
1065 | * same register size. We try to reuse the ABI identifiers | |
1066 | * used by libseccomp. */ | |
1067 | ||
1068 | #if defined(__x86_64__) | |
1069 | ||
1070 | if (streq(p, "x86")) | |
1071 | return PER_LINUX32; | |
1072 | ||
1073 | if (streq(p, "x86-64")) | |
1074 | return PER_LINUX; | |
1075 | ||
1076 | #elif defined(__i386__) | |
1077 | ||
1078 | if (streq(p, "x86")) | |
1079 | return PER_LINUX; | |
7517f51e HB |
1080 | |
1081 | #elif defined(__s390x__) | |
1082 | ||
1083 | if (streq(p, "s390")) | |
1084 | return PER_LINUX32; | |
1085 | ||
1086 | if (streq(p, "s390x")) | |
1087 | return PER_LINUX; | |
1088 | ||
1089 | #elif defined(__s390__) | |
1090 | ||
1091 | if (streq(p, "s390")) | |
1092 | return PER_LINUX; | |
6afc95b7 LP |
1093 | #endif |
1094 | ||
050f7277 | 1095 | return PERSONALITY_INVALID; |
6afc95b7 | 1096 | } |
ac45f971 LP |
1097 | |
1098 | const char* personality_to_string(unsigned long p) { | |
1099 | ||
1100 | #if defined(__x86_64__) | |
1101 | ||
1102 | if (p == PER_LINUX32) | |
1103 | return "x86"; | |
1104 | ||
1105 | if (p == PER_LINUX) | |
1106 | return "x86-64"; | |
1107 | ||
1108 | #elif defined(__i386__) | |
1109 | ||
1110 | if (p == PER_LINUX) | |
1111 | return "x86"; | |
7517f51e HB |
1112 | |
1113 | #elif defined(__s390x__) | |
1114 | ||
1115 | if (p == PER_LINUX) | |
1116 | return "s390x"; | |
1117 | ||
1118 | if (p == PER_LINUX32) | |
1119 | return "s390"; | |
1120 | ||
1121 | #elif defined(__s390__) | |
1122 | ||
1123 | if (p == PER_LINUX) | |
1124 | return "s390"; | |
1125 | ||
ac45f971 LP |
1126 | #endif |
1127 | ||
1128 | return NULL; | |
1129 | } | |
1c231f56 LP |
1130 | |
1131 | uint64_t physical_memory(void) { | |
1132 | long mem; | |
1133 | ||
1134 | /* We return this as uint64_t in case we are running as 32bit | |
1135 | * process on a 64bit kernel with huge amounts of memory */ | |
1136 | ||
1137 | mem = sysconf(_SC_PHYS_PAGES); | |
1138 | assert(mem > 0); | |
1139 | ||
1140 | return (uint64_t) mem * (uint64_t) page_size(); | |
1141 | } | |
6db615c1 | 1142 | |
966bff26 | 1143 | int update_reboot_param_file(const char *param) { |
c5220a94 MO |
1144 | int r = 0; |
1145 | ||
1146 | if (param) { | |
4c1fc3e4 | 1147 | r = write_string_file(REBOOT_PARAM_FILE, param, WRITE_STRING_FILE_CREATE); |
c5220a94 | 1148 | if (r < 0) |
e53fc357 | 1149 | return log_error_errno(r, "Failed to write reboot param to "REBOOT_PARAM_FILE": %m"); |
c5220a94 | 1150 | } else |
e53fc357 | 1151 | (void) unlink(REBOOT_PARAM_FILE); |
c5220a94 | 1152 | |
e53fc357 | 1153 | return 0; |
c5220a94 | 1154 | } |
6d313367 | 1155 | |
3f6fd1ba LP |
1156 | int version(void) { |
1157 | puts(PACKAGE_STRING "\n" | |
1158 | SYSTEMD_FEATURES); | |
1159 | return 0; | |
1160 | } | |
8dd4c05b LP |
1161 | |
1162 | bool fdname_is_valid(const char *s) { | |
1163 | const char *p; | |
1164 | ||
0a3bb96e | 1165 | /* Validates a name for $LISTEN_FDNAMES. We basically allow |
8dd4c05b LP |
1166 | * everything ASCII that's not a control character. Also, as |
1167 | * special exception the ":" character is not allowed, as we | |
0a3bb96e | 1168 | * use that as field separator in $LISTEN_FDNAMES. |
8dd4c05b | 1169 | * |
0a3bb96e LP |
1170 | * Note that the empty string is explicitly allowed |
1171 | * here. However, we limit the length of the names to 255 | |
1172 | * characters. */ | |
8dd4c05b LP |
1173 | |
1174 | if (!s) | |
1175 | return false; | |
1176 | ||
1177 | for (p = s; *p; p++) { | |
1178 | if (*p < ' ') | |
1179 | return false; | |
1180 | if (*p >= 127) | |
1181 | return false; | |
1182 | if (*p == ':') | |
1183 | return false; | |
1184 | } | |
1185 | ||
1186 | return p - s < 256; | |
1187 | } | |
257b0719 EV |
1188 | |
1189 | bool oom_score_adjust_is_valid(int oa) { | |
1190 | return oa >= OOM_SCORE_ADJ_MIN && oa <= OOM_SCORE_ADJ_MAX; | |
1191 | } |