chiark / gitweb /
Prep v228: Substitute declaration masks (1/4)
[elogind.git] / src / basic / util.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
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
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
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
16   Lesser General Public License for more details.
17
18   You should have received a copy of the GNU Lesser General Public License
19   along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <ctype.h>
23 #include <dirent.h>
24 #include <errno.h>
25 #include <fcntl.h>
26 #include <grp.h>
27 #include <langinfo.h>
28 #include <libintl.h>
29 #include <limits.h>
30 #include <linux/magic.h>
31 #include <linux/oom.h>
32 #include <linux/sched.h>
33 #include <locale.h>
34 #include <poll.h>
35 #include <pwd.h>
36 #include <sched.h>
37 #include <signal.h>
38 #include <stdarg.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <sys/file.h>
43 #include <sys/ioctl.h>
44 #include <sys/mman.h>
45 #include <sys/mount.h>
46 #include <sys/personality.h>
47 #include <sys/prctl.h>
48 #include <sys/stat.h>
49 #include <sys/statvfs.h>
50 #include <sys/time.h>
51 #include <sys/types.h>
52 #include <sys/utsname.h>
53 #include <sys/vfs.h>
54 #include <sys/wait.h>
55 #include <syslog.h>
56 #include <unistd.h>
57
58 /* When we include libgen.h because we need dirname() we immediately
59  * undefine basename() since libgen.h defines it as a macro to the
60  * POSIX version which is really broken. We prefer GNU basename(). */
61 #include <libgen.h>
62 #undef basename
63
64 #ifdef HAVE_SYS_AUXV_H
65 #include <sys/auxv.h>
66 #endif
67
68 /* We include linux/fs.h as last of the system headers, as it
69  * otherwise conflicts with sys/mount.h. Yay, Linux is great! */
70 #include <linux/fs.h>
71
72 #include "alloc-util.h"
73 #include "build.h"
74 #include "def.h"
75 //#include "device-nodes.h"
76 #include "dirent-util.h"
77 //#include "env-util.h"
78 #include "escape.h"
79 //#include "exit-status.h"
80 #include "fd-util.h"
81 #include "fileio.h"
82 #include "formats-util.h"
83 #include "gunicode.h"
84 #include "hashmap.h"
85 #include "hexdecoct.h"
86 #include "hostname-util.h"
87 //#include "ioprio.h"
88 #include "log.h"
89 #include "macro.h"
90 #include "missing.h"
91 #include "mkdir.h"
92 #include "parse-util.h"
93 #include "path-util.h"
94 #include "process-util.h"
95 #include "random-util.h"
96 #include "signal-util.h"
97 #include "set.h"
98 #include "sparse-endian.h"
99 #include "stat-util.h"
100 #include "string-table.h"
101 #include "string-util.h"
102 #include "strv.h"
103 #include "terminal-util.h"
104 #include "user-util.h"
105 #include "utf8.h"
106 #include "util.h"
107 #include "virt.h"
108
109 /* Put this test here for a lack of better place */
110 assert_cc(EAGAIN == EWOULDBLOCK);
111
112 int saved_argc = 0;
113 char **saved_argv = NULL;
114
115 size_t page_size(void) {
116         static thread_local size_t pgsz = 0;
117         long r;
118
119         if (_likely_(pgsz > 0))
120                 return pgsz;
121
122         r = sysconf(_SC_PAGESIZE);
123         assert(r > 0);
124
125         pgsz = (size_t) r;
126         return pgsz;
127 }
128
129 static int do_execute(char **directories, usec_t timeout, char *argv[]) {
130         _cleanup_hashmap_free_free_ Hashmap *pids = NULL;
131         _cleanup_set_free_free_ Set *seen = NULL;
132         char **directory;
133
134         /* We fork this all off from a child process so that we can
135          * somewhat cleanly make use of SIGALRM to set a time limit */
136
137         (void) reset_all_signal_handlers();
138         (void) reset_signal_mask();
139
140         assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
141
142         pids = hashmap_new(NULL);
143         if (!pids)
144                 return log_oom();
145
146         seen = set_new(&string_hash_ops);
147         if (!seen)
148                 return log_oom();
149
150         STRV_FOREACH(directory, directories) {
151                 _cleanup_closedir_ DIR *d;
152                 struct dirent *de;
153
154                 d = opendir(*directory);
155                 if (!d) {
156                         if (errno == ENOENT)
157                                 continue;
158
159                         return log_error_errno(errno, "Failed to open directory %s: %m", *directory);
160                 }
161
162                 FOREACH_DIRENT(de, d, break) {
163                         _cleanup_free_ char *path = NULL;
164                         pid_t pid;
165                         int r;
166
167                         if (!dirent_is_file(de))
168                                 continue;
169
170                         if (set_contains(seen, de->d_name)) {
171                                 log_debug("%1$s/%2$s skipped (%2$s was already seen).", *directory, de->d_name);
172                                 continue;
173                         }
174
175                         r = set_put_strdup(seen, de->d_name);
176                         if (r < 0)
177                                 return log_oom();
178
179                         path = strjoin(*directory, "/", de->d_name, NULL);
180                         if (!path)
181                                 return log_oom();
182
183                         if (null_or_empty_path(path)) {
184                                 log_debug("%s is empty (a mask).", path);
185                                 continue;
186                         }
187
188                         pid = fork();
189                         if (pid < 0) {
190                                 log_error_errno(errno, "Failed to fork: %m");
191                                 continue;
192                         } else if (pid == 0) {
193                                 char *_argv[2];
194
195                                 assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
196
197                                 if (!argv) {
198                                         _argv[0] = path;
199                                         _argv[1] = NULL;
200                                         argv = _argv;
201                                 } else
202                                         argv[0] = path;
203
204                                 execv(path, argv);
205                                 return log_error_errno(errno, "Failed to execute %s: %m", path);
206                         }
207
208                         log_debug("Spawned %s as " PID_FMT ".", path, pid);
209
210                         r = hashmap_put(pids, PID_TO_PTR(pid), path);
211                         if (r < 0)
212                                 return log_oom();
213                         path = NULL;
214                 }
215         }
216
217         /* Abort execution of this process after the timout. We simply
218          * rely on SIGALRM as default action terminating the process,
219          * and turn on alarm(). */
220
221         if (timeout != USEC_INFINITY)
222                 alarm((timeout + USEC_PER_SEC - 1) / USEC_PER_SEC);
223
224         while (!hashmap_isempty(pids)) {
225                 _cleanup_free_ char *path = NULL;
226                 pid_t pid;
227
228                 pid = PTR_TO_PID(hashmap_first_key(pids));
229                 assert(pid > 0);
230
231                 path = hashmap_remove(pids, PID_TO_PTR(pid));
232                 assert(path);
233
234                 wait_for_terminate_and_warn(path, pid, true);
235         }
236
237         return 0;
238 }
239
240 void execute_directories(const char* const* directories, usec_t timeout, char *argv[]) {
241         pid_t executor_pid;
242         int r;
243         char *name;
244         char **dirs = (char**) directories;
245
246         assert(!strv_isempty(dirs));
247
248         name = basename(dirs[0]);
249         assert(!isempty(name));
250
251         /* Executes all binaries in the directories in parallel and waits
252          * for them to finish. Optionally a timeout is applied. If a file
253          * with the same name exists in more than one directory, the
254          * earliest one wins. */
255
256         executor_pid = fork();
257         if (executor_pid < 0) {
258                 log_error_errno(errno, "Failed to fork: %m");
259                 return;
260
261         } else if (executor_pid == 0) {
262                 r = do_execute(dirs, timeout, argv);
263                 _exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS);
264         }
265
266         wait_for_terminate_and_warn(name, executor_pid, true);
267 }
268
269 bool plymouth_running(void) {
270         return access("/run/plymouth/pid", F_OK) >= 0;
271 }
272
273 bool display_is_local(const char *display) {
274         assert(display);
275
276         return
277                 display[0] == ':' &&
278                 display[1] >= '0' &&
279                 display[1] <= '9';
280 }
281
282 int socket_from_display(const char *display, char **path) {
283         size_t k;
284         char *f, *c;
285
286         assert(display);
287         assert(path);
288
289         if (!display_is_local(display))
290                 return -EINVAL;
291
292         k = strspn(display+1, "0123456789");
293
294         f = new(char, strlen("/tmp/.X11-unix/X") + k + 1);
295         if (!f)
296                 return -ENOMEM;
297
298         c = stpcpy(f, "/tmp/.X11-unix/X");
299         memcpy(c, display+1, k);
300         c[k] = 0;
301
302         *path = f;
303
304         return 0;
305 }
306
307 int block_get_whole_disk(dev_t d, dev_t *ret) {
308         char *p, *s;
309         int r;
310         unsigned n, m;
311
312         assert(ret);
313
314         /* If it has a queue this is good enough for us */
315         if (asprintf(&p, "/sys/dev/block/%u:%u/queue", major(d), minor(d)) < 0)
316                 return -ENOMEM;
317
318         r = access(p, F_OK);
319         free(p);
320
321         if (r >= 0) {
322                 *ret = d;
323                 return 0;
324         }
325
326         /* If it is a partition find the originating device */
327         if (asprintf(&p, "/sys/dev/block/%u:%u/partition", major(d), minor(d)) < 0)
328                 return -ENOMEM;
329
330         r = access(p, F_OK);
331         free(p);
332
333         if (r < 0)
334                 return -ENOENT;
335
336         /* Get parent dev_t */
337         if (asprintf(&p, "/sys/dev/block/%u:%u/../dev", major(d), minor(d)) < 0)
338                 return -ENOMEM;
339
340         r = read_one_line_file(p, &s);
341         free(p);
342
343         if (r < 0)
344                 return r;
345
346         r = sscanf(s, "%u:%u", &m, &n);
347         free(s);
348
349         if (r != 2)
350                 return -EINVAL;
351
352         /* Only return this if it is really good enough for us. */
353         if (asprintf(&p, "/sys/dev/block/%u:%u/queue", m, n) < 0)
354                 return -ENOMEM;
355
356         r = access(p, F_OK);
357         free(p);
358
359         if (r >= 0) {
360                 *ret = makedev(m, n);
361                 return 0;
362         }
363
364         return -ENOENT;
365 }
366
367 bool kexec_loaded(void) {
368        bool loaded = false;
369        char *s;
370
371        if (read_one_line_file("/sys/kernel/kexec_loaded", &s) >= 0) {
372                if (s[0] == '1')
373                        loaded = true;
374                free(s);
375        }
376        return loaded;
377 }
378
379 int prot_from_flags(int flags) {
380
381         switch (flags & O_ACCMODE) {
382
383         case O_RDONLY:
384                 return PROT_READ;
385
386         case O_WRONLY:
387                 return PROT_WRITE;
388
389         case O_RDWR:
390                 return PROT_READ|PROT_WRITE;
391
392         default:
393                 return -EINVAL;
394         }
395 }
396
397 int fork_agent(pid_t *pid, const int except[], unsigned n_except, const char *path, ...) {
398         bool stdout_is_tty, stderr_is_tty;
399         pid_t parent_pid, agent_pid;
400         sigset_t ss, saved_ss;
401         unsigned n, i;
402         va_list ap;
403         char **l;
404
405         assert(pid);
406         assert(path);
407
408         /* Spawns a temporary TTY agent, making sure it goes away when
409          * we go away */
410
411         parent_pid = getpid();
412
413         /* First we temporarily block all signals, so that the new
414          * child has them blocked initially. This way, we can be sure
415          * that SIGTERMs are not lost we might send to the agent. */
416         assert_se(sigfillset(&ss) >= 0);
417         assert_se(sigprocmask(SIG_SETMASK, &ss, &saved_ss) >= 0);
418
419         agent_pid = fork();
420         if (agent_pid < 0) {
421                 assert_se(sigprocmask(SIG_SETMASK, &saved_ss, NULL) >= 0);
422                 return -errno;
423         }
424
425         if (agent_pid != 0) {
426                 assert_se(sigprocmask(SIG_SETMASK, &saved_ss, NULL) >= 0);
427                 *pid = agent_pid;
428                 return 0;
429         }
430
431         /* In the child:
432          *
433          * Make sure the agent goes away when the parent dies */
434         if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0)
435                 _exit(EXIT_FAILURE);
436
437         /* Make sure we actually can kill the agent, if we need to, in
438          * case somebody invoked us from a shell script that trapped
439          * SIGTERM or so... */
440         (void) reset_all_signal_handlers();
441         (void) reset_signal_mask();
442
443         /* Check whether our parent died before we were able
444          * to set the death signal and unblock the signals */
445         if (getppid() != parent_pid)
446                 _exit(EXIT_SUCCESS);
447
448         /* Don't leak fds to the agent */
449         close_all_fds(except, n_except);
450
451         stdout_is_tty = isatty(STDOUT_FILENO);
452         stderr_is_tty = isatty(STDERR_FILENO);
453
454         if (!stdout_is_tty || !stderr_is_tty) {
455                 int fd;
456
457                 /* Detach from stdout/stderr. and reopen
458                  * /dev/tty for them. This is important to
459                  * ensure that when systemctl is started via
460                  * popen() or a similar call that expects to
461                  * read EOF we actually do generate EOF and
462                  * not delay this indefinitely by because we
463                  * keep an unused copy of stdin around. */
464                 fd = open("/dev/tty", O_WRONLY);
465                 if (fd < 0) {
466                         log_error_errno(errno, "Failed to open /dev/tty: %m");
467                         _exit(EXIT_FAILURE);
468                 }
469
470                 if (!stdout_is_tty)
471                         dup2(fd, STDOUT_FILENO);
472
473                 if (!stderr_is_tty)
474                         dup2(fd, STDERR_FILENO);
475
476                 if (fd > 2)
477                         close(fd);
478         }
479
480         /* Count arguments */
481         va_start(ap, path);
482         for (n = 0; va_arg(ap, char*); n++)
483                 ;
484         va_end(ap);
485
486         /* Allocate strv */
487         l = alloca(sizeof(char *) * (n + 1));
488
489         /* Fill in arguments */
490         va_start(ap, path);
491         for (i = 0; i <= n; i++)
492                 l[i] = va_arg(ap, char*);
493         va_end(ap);
494
495         execv(path, l);
496         _exit(EXIT_FAILURE);
497 }
498
499 bool in_initrd(void) {
500         static int saved = -1;
501         struct statfs s;
502
503         if (saved >= 0)
504                 return saved;
505
506         /* We make two checks here:
507          *
508          * 1. the flag file /etc/initrd-release must exist
509          * 2. the root file system must be a memory file system
510          *
511          * The second check is extra paranoia, since misdetecting an
512          * initrd can have bad bad consequences due the initrd
513          * emptying when transititioning to the main systemd.
514          */
515
516         saved = access("/etc/initrd-release", F_OK) >= 0 &&
517                 statfs("/", &s) >= 0 &&
518                 is_temporary_fs(&s);
519
520         return saved;
521 }
522
523 /* hey glibc, APIs with callbacks without a user pointer are so useless */
524 void *xbsearch_r(const void *key, const void *base, size_t nmemb, size_t size,
525                  int (*compar) (const void *, const void *, void *), void *arg) {
526         size_t l, u, idx;
527         const void *p;
528         int comparison;
529
530         l = 0;
531         u = nmemb;
532         while (l < u) {
533                 idx = (l + u) / 2;
534                 p = (void *)(((const char *) base) + (idx * size));
535                 comparison = compar(key, p, arg);
536                 if (comparison < 0)
537                         u = idx;
538                 else if (comparison > 0)
539                         l = idx + 1;
540                 else
541                         return (void *)p;
542         }
543         return NULL;
544 }
545
546 int on_ac_power(void) {
547         bool found_offline = false, found_online = false;
548         _cleanup_closedir_ DIR *d = NULL;
549
550         d = opendir("/sys/class/power_supply");
551         if (!d)
552                 return errno == ENOENT ? true : -errno;
553
554         for (;;) {
555                 struct dirent *de;
556                 _cleanup_close_ int fd = -1, device = -1;
557                 char contents[6];
558                 ssize_t n;
559
560                 errno = 0;
561                 de = readdir(d);
562                 if (!de && errno != 0)
563                         return -errno;
564
565                 if (!de)
566                         break;
567
568                 if (hidden_file(de->d_name))
569                         continue;
570
571                 device = openat(dirfd(d), de->d_name, O_DIRECTORY|O_RDONLY|O_CLOEXEC|O_NOCTTY);
572                 if (device < 0) {
573                         if (errno == ENOENT || errno == ENOTDIR)
574                                 continue;
575
576                         return -errno;
577                 }
578
579                 fd = openat(device, "type", O_RDONLY|O_CLOEXEC|O_NOCTTY);
580                 if (fd < 0) {
581                         if (errno == ENOENT)
582                                 continue;
583
584                         return -errno;
585                 }
586
587                 n = read(fd, contents, sizeof(contents));
588                 if (n < 0)
589                         return -errno;
590
591                 if (n != 6 || memcmp(contents, "Mains\n", 6))
592                         continue;
593
594                 safe_close(fd);
595                 fd = openat(device, "online", O_RDONLY|O_CLOEXEC|O_NOCTTY);
596                 if (fd < 0) {
597                         if (errno == ENOENT)
598                                 continue;
599
600                         return -errno;
601                 }
602
603                 n = read(fd, contents, sizeof(contents));
604                 if (n < 0)
605                         return -errno;
606
607                 if (n != 2 || contents[1] != '\n')
608                         return -EIO;
609
610                 if (contents[0] == '1') {
611                         found_online = true;
612                         break;
613                 } else if (contents[0] == '0')
614                         found_offline = true;
615                 else
616                         return -EIO;
617         }
618
619         return found_online || !found_offline;
620 }
621
622 bool id128_is_valid(const char *s) {
623         size_t i, l;
624
625         l = strlen(s);
626         if (l == 32) {
627
628                 /* Simple formatted 128bit hex string */
629
630                 for (i = 0; i < l; i++) {
631                         char c = s[i];
632
633                         if (!(c >= '0' && c <= '9') &&
634                             !(c >= 'a' && c <= 'z') &&
635                             !(c >= 'A' && c <= 'Z'))
636                                 return false;
637                 }
638
639         } else if (l == 36) {
640
641                 /* Formatted UUID */
642
643                 for (i = 0; i < l; i++) {
644                         char c = s[i];
645
646                         if ((i == 8 || i == 13 || i == 18 || i == 23)) {
647                                 if (c != '-')
648                                         return false;
649                         } else {
650                                 if (!(c >= '0' && c <= '9') &&
651                                     !(c >= 'a' && c <= 'z') &&
652                                     !(c >= 'A' && c <= 'Z'))
653                                         return false;
654                         }
655                 }
656
657         } else
658                 return false;
659
660         return true;
661 }
662
663 int container_get_leader(const char *machine, pid_t *pid) {
664         _cleanup_free_ char *s = NULL, *class = NULL;
665         const char *p;
666         pid_t leader;
667         int r;
668
669         assert(machine);
670         assert(pid);
671
672         if (!machine_name_is_valid(machine))
673                 return -EINVAL;
674
675         p = strjoina("/run/systemd/machines/", machine);
676         r = parse_env_file(p, NEWLINE, "LEADER", &s, "CLASS", &class, NULL);
677         if (r == -ENOENT)
678                 return -EHOSTDOWN;
679         if (r < 0)
680                 return r;
681         if (!s)
682                 return -EIO;
683
684         if (!streq_ptr(class, "container"))
685                 return -EIO;
686
687         r = parse_pid(s, &leader);
688         if (r < 0)
689                 return r;
690         if (leader <= 1)
691                 return -EIO;
692
693         *pid = leader;
694         return 0;
695 }
696
697 int namespace_open(pid_t pid, int *pidns_fd, int *mntns_fd, int *netns_fd, int *userns_fd, int *root_fd) {
698         _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, netnsfd = -1, usernsfd = -1;
699         int rfd = -1;
700
701         assert(pid >= 0);
702
703         if (mntns_fd) {
704                 const char *mntns;
705
706                 mntns = procfs_file_alloca(pid, "ns/mnt");
707                 mntnsfd = open(mntns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
708                 if (mntnsfd < 0)
709                         return -errno;
710         }
711
712         if (pidns_fd) {
713                 const char *pidns;
714
715                 pidns = procfs_file_alloca(pid, "ns/pid");
716                 pidnsfd = open(pidns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
717                 if (pidnsfd < 0)
718                         return -errno;
719         }
720
721         if (netns_fd) {
722                 const char *netns;
723
724                 netns = procfs_file_alloca(pid, "ns/net");
725                 netnsfd = open(netns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
726                 if (netnsfd < 0)
727                         return -errno;
728         }
729
730         if (userns_fd) {
731                 const char *userns;
732
733                 userns = procfs_file_alloca(pid, "ns/user");
734                 usernsfd = open(userns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
735                 if (usernsfd < 0 && errno != ENOENT)
736                         return -errno;
737         }
738
739         if (root_fd) {
740                 const char *root;
741
742                 root = procfs_file_alloca(pid, "root");
743                 rfd = open(root, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
744                 if (rfd < 0)
745                         return -errno;
746         }
747
748         if (pidns_fd)
749                 *pidns_fd = pidnsfd;
750
751         if (mntns_fd)
752                 *mntns_fd = mntnsfd;
753
754         if (netns_fd)
755                 *netns_fd = netnsfd;
756
757         if (userns_fd)
758                 *userns_fd = usernsfd;
759
760         if (root_fd)
761                 *root_fd = rfd;
762
763         pidnsfd = mntnsfd = netnsfd = usernsfd = -1;
764
765         return 0;
766 }
767
768 int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int root_fd) {
769         if (userns_fd >= 0) {
770                 /* Can't setns to your own userns, since then you could
771                  * escalate from non-root to root in your own namespace, so
772                  * check if namespaces equal before attempting to enter. */
773                 _cleanup_free_ char *userns_fd_path = NULL;
774                 int r;
775                 if (asprintf(&userns_fd_path, "/proc/self/fd/%d", userns_fd) < 0)
776                         return -ENOMEM;
777
778                 r = files_same(userns_fd_path, "/proc/self/ns/user");
779                 if (r < 0)
780                         return r;
781                 if (r)
782                         userns_fd = -1;
783         }
784
785         if (pidns_fd >= 0)
786                 if (setns(pidns_fd, CLONE_NEWPID) < 0)
787                         return -errno;
788
789         if (mntns_fd >= 0)
790                 if (setns(mntns_fd, CLONE_NEWNS) < 0)
791                         return -errno;
792
793         if (netns_fd >= 0)
794                 if (setns(netns_fd, CLONE_NEWNET) < 0)
795                         return -errno;
796
797         if (userns_fd >= 0)
798                 if (setns(userns_fd, CLONE_NEWUSER) < 0)
799                         return -errno;
800
801         if (root_fd >= 0) {
802                 if (fchdir(root_fd) < 0)
803                         return -errno;
804
805                 if (chroot(".") < 0)
806                         return -errno;
807         }
808
809         return reset_uid_gid();
810 }
811
812 uint64_t physical_memory(void) {
813         long mem;
814
815         /* We return this as uint64_t in case we are running as 32bit
816          * process on a 64bit kernel with huge amounts of memory */
817
818         mem = sysconf(_SC_PHYS_PAGES);
819         assert(mem > 0);
820
821         return (uint64_t) mem * (uint64_t) page_size();
822 }
823
824 int update_reboot_param_file(const char *param) {
825         int r = 0;
826
827         if (param) {
828                 r = write_string_file(REBOOT_PARAM_FILE, param, WRITE_STRING_FILE_CREATE);
829                 if (r < 0)
830                         return log_error_errno(r, "Failed to write reboot param to "REBOOT_PARAM_FILE": %m");
831         } else
832                 (void) unlink(REBOOT_PARAM_FILE);
833
834         return 0;
835 }
836
837 int version(void) {
838         puts(PACKAGE_STRING "\n"
839              SYSTEMD_FEATURES);
840         return 0;
841 }