chiark / gitweb /
Prep v228: With most functions split out, clean up the enormous includes list in...
[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 #if 0 /// UNNEEDED by elogind
270 bool plymouth_running(void) {
271         return access("/run/plymouth/pid", F_OK) >= 0;
272 }
273 #endif // 0
274
275 bool display_is_local(const char *display) {
276         assert(display);
277
278         return
279                 display[0] == ':' &&
280                 display[1] >= '0' &&
281                 display[1] <= '9';
282 }
283
284 int socket_from_display(const char *display, char **path) {
285         size_t k;
286         char *f, *c;
287
288         assert(display);
289         assert(path);
290
291         if (!display_is_local(display))
292                 return -EINVAL;
293
294         k = strspn(display+1, "0123456789");
295
296         f = new(char, strlen("/tmp/.X11-unix/X") + k + 1);
297         if (!f)
298                 return -ENOMEM;
299
300         c = stpcpy(f, "/tmp/.X11-unix/X");
301         memcpy(c, display+1, k);
302         c[k] = 0;
303
304         *path = f;
305
306         return 0;
307 }
308
309 #if 0 /// UNNEEDED by elogind
310 int block_get_whole_disk(dev_t d, dev_t *ret) {
311         char *p, *s;
312         int r;
313         unsigned n, m;
314
315         assert(ret);
316
317         /* If it has a queue this is good enough for us */
318         if (asprintf(&p, "/sys/dev/block/%u:%u/queue", major(d), minor(d)) < 0)
319                 return -ENOMEM;
320
321         r = access(p, F_OK);
322         free(p);
323
324         if (r >= 0) {
325                 *ret = d;
326                 return 0;
327         }
328
329         /* If it is a partition find the originating device */
330         if (asprintf(&p, "/sys/dev/block/%u:%u/partition", major(d), minor(d)) < 0)
331                 return -ENOMEM;
332
333         r = access(p, F_OK);
334         free(p);
335
336         if (r < 0)
337                 return -ENOENT;
338
339         /* Get parent dev_t */
340         if (asprintf(&p, "/sys/dev/block/%u:%u/../dev", major(d), minor(d)) < 0)
341                 return -ENOMEM;
342
343         r = read_one_line_file(p, &s);
344         free(p);
345
346         if (r < 0)
347                 return r;
348
349         r = sscanf(s, "%u:%u", &m, &n);
350         free(s);
351
352         if (r != 2)
353                 return -EINVAL;
354
355         /* Only return this if it is really good enough for us. */
356         if (asprintf(&p, "/sys/dev/block/%u:%u/queue", m, n) < 0)
357                 return -ENOMEM;
358
359         r = access(p, F_OK);
360         free(p);
361
362         if (r >= 0) {
363                 *ret = makedev(m, n);
364                 return 0;
365         }
366
367         return -ENOENT;
368 }
369
370 bool kexec_loaded(void) {
371        bool loaded = false;
372        char *s;
373
374        if (read_one_line_file("/sys/kernel/kexec_loaded", &s) >= 0) {
375                if (s[0] == '1')
376                        loaded = true;
377                free(s);
378        }
379        return loaded;
380 }
381
382 int prot_from_flags(int flags) {
383
384         switch (flags & O_ACCMODE) {
385
386         case O_RDONLY:
387                 return PROT_READ;
388
389         case O_WRONLY:
390                 return PROT_WRITE;
391
392         case O_RDWR:
393                 return PROT_READ|PROT_WRITE;
394
395         default:
396                 return -EINVAL;
397         }
398 }
399 #endif // 0
400
401 int fork_agent(pid_t *pid, const int except[], unsigned n_except, const char *path, ...) {
402         bool stdout_is_tty, stderr_is_tty;
403         pid_t parent_pid, agent_pid;
404         sigset_t ss, saved_ss;
405         unsigned n, i;
406         va_list ap;
407         char **l;
408
409         assert(pid);
410         assert(path);
411
412         /* Spawns a temporary TTY agent, making sure it goes away when
413          * we go away */
414
415         parent_pid = getpid();
416
417         /* First we temporarily block all signals, so that the new
418          * child has them blocked initially. This way, we can be sure
419          * that SIGTERMs are not lost we might send to the agent. */
420         assert_se(sigfillset(&ss) >= 0);
421         assert_se(sigprocmask(SIG_SETMASK, &ss, &saved_ss) >= 0);
422
423         agent_pid = fork();
424         if (agent_pid < 0) {
425                 assert_se(sigprocmask(SIG_SETMASK, &saved_ss, NULL) >= 0);
426                 return -errno;
427         }
428
429         if (agent_pid != 0) {
430                 assert_se(sigprocmask(SIG_SETMASK, &saved_ss, NULL) >= 0);
431                 *pid = agent_pid;
432                 return 0;
433         }
434
435         /* In the child:
436          *
437          * Make sure the agent goes away when the parent dies */
438         if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0)
439                 _exit(EXIT_FAILURE);
440
441         /* Make sure we actually can kill the agent, if we need to, in
442          * case somebody invoked us from a shell script that trapped
443          * SIGTERM or so... */
444         (void) reset_all_signal_handlers();
445         (void) reset_signal_mask();
446
447         /* Check whether our parent died before we were able
448          * to set the death signal and unblock the signals */
449         if (getppid() != parent_pid)
450                 _exit(EXIT_SUCCESS);
451
452         /* Don't leak fds to the agent */
453         close_all_fds(except, n_except);
454
455         stdout_is_tty = isatty(STDOUT_FILENO);
456         stderr_is_tty = isatty(STDERR_FILENO);
457
458         if (!stdout_is_tty || !stderr_is_tty) {
459                 int fd;
460
461                 /* Detach from stdout/stderr. and reopen
462                  * /dev/tty for them. This is important to
463                  * ensure that when systemctl is started via
464                  * popen() or a similar call that expects to
465                  * read EOF we actually do generate EOF and
466                  * not delay this indefinitely by because we
467                  * keep an unused copy of stdin around. */
468                 fd = open("/dev/tty", O_WRONLY);
469                 if (fd < 0) {
470                         log_error_errno(errno, "Failed to open /dev/tty: %m");
471                         _exit(EXIT_FAILURE);
472                 }
473
474                 if (!stdout_is_tty)
475                         dup2(fd, STDOUT_FILENO);
476
477                 if (!stderr_is_tty)
478                         dup2(fd, STDERR_FILENO);
479
480                 if (fd > 2)
481                         close(fd);
482         }
483
484         /* Count arguments */
485         va_start(ap, path);
486         for (n = 0; va_arg(ap, char*); n++)
487                 ;
488         va_end(ap);
489
490         /* Allocate strv */
491         l = alloca(sizeof(char *) * (n + 1));
492
493         /* Fill in arguments */
494         va_start(ap, path);
495         for (i = 0; i <= n; i++)
496                 l[i] = va_arg(ap, char*);
497         va_end(ap);
498
499         execv(path, l);
500         _exit(EXIT_FAILURE);
501 }
502
503 bool in_initrd(void) {
504         static int saved = -1;
505         struct statfs s;
506
507         if (saved >= 0)
508                 return saved;
509
510         /* We make two checks here:
511          *
512          * 1. the flag file /etc/initrd-release must exist
513          * 2. the root file system must be a memory file system
514          *
515          * The second check is extra paranoia, since misdetecting an
516          * initrd can have bad bad consequences due the initrd
517          * emptying when transititioning to the main systemd.
518          */
519
520         saved = access("/etc/initrd-release", F_OK) >= 0 &&
521                 statfs("/", &s) >= 0 &&
522                 is_temporary_fs(&s);
523
524         return saved;
525 }
526
527 #if 0 /// UNNEEDED by elogind
528 /* hey glibc, APIs with callbacks without a user pointer are so useless */
529 void *xbsearch_r(const void *key, const void *base, size_t nmemb, size_t size,
530                  int (*compar) (const void *, const void *, void *), void *arg) {
531         size_t l, u, idx;
532         const void *p;
533         int comparison;
534
535         l = 0;
536         u = nmemb;
537         while (l < u) {
538                 idx = (l + u) / 2;
539                 p = (void *)(((const char *) base) + (idx * size));
540                 comparison = compar(key, p, arg);
541                 if (comparison < 0)
542                         u = idx;
543                 else if (comparison > 0)
544                         l = idx + 1;
545                 else
546                         return (void *)p;
547         }
548         return NULL;
549 }
550
551 int on_ac_power(void) {
552         bool found_offline = false, found_online = false;
553         _cleanup_closedir_ DIR *d = NULL;
554
555         d = opendir("/sys/class/power_supply");
556         if (!d)
557                 return errno == ENOENT ? true : -errno;
558
559         for (;;) {
560                 struct dirent *de;
561                 _cleanup_close_ int fd = -1, device = -1;
562                 char contents[6];
563                 ssize_t n;
564
565                 errno = 0;
566                 de = readdir(d);
567                 if (!de && errno != 0)
568                         return -errno;
569
570                 if (!de)
571                         break;
572
573                 if (hidden_file(de->d_name))
574                         continue;
575
576                 device = openat(dirfd(d), de->d_name, O_DIRECTORY|O_RDONLY|O_CLOEXEC|O_NOCTTY);
577                 if (device < 0) {
578                         if (errno == ENOENT || errno == ENOTDIR)
579                                 continue;
580
581                         return -errno;
582                 }
583
584                 fd = openat(device, "type", O_RDONLY|O_CLOEXEC|O_NOCTTY);
585                 if (fd < 0) {
586                         if (errno == ENOENT)
587                                 continue;
588
589                         return -errno;
590                 }
591
592                 n = read(fd, contents, sizeof(contents));
593                 if (n < 0)
594                         return -errno;
595
596                 if (n != 6 || memcmp(contents, "Mains\n", 6))
597                         continue;
598
599                 safe_close(fd);
600                 fd = openat(device, "online", O_RDONLY|O_CLOEXEC|O_NOCTTY);
601                 if (fd < 0) {
602                         if (errno == ENOENT)
603                                 continue;
604
605                         return -errno;
606                 }
607
608                 n = read(fd, contents, sizeof(contents));
609                 if (n < 0)
610                         return -errno;
611
612                 if (n != 2 || contents[1] != '\n')
613                         return -EIO;
614
615                 if (contents[0] == '1') {
616                         found_online = true;
617                         break;
618                 } else if (contents[0] == '0')
619                         found_offline = true;
620                 else
621                         return -EIO;
622         }
623
624         return found_online || !found_offline;
625 }
626
627 bool id128_is_valid(const char *s) {
628         size_t i, l;
629
630         l = strlen(s);
631         if (l == 32) {
632
633                 /* Simple formatted 128bit hex string */
634
635                 for (i = 0; i < l; i++) {
636                         char c = s[i];
637
638                         if (!(c >= '0' && c <= '9') &&
639                             !(c >= 'a' && c <= 'z') &&
640                             !(c >= 'A' && c <= 'Z'))
641                                 return false;
642                 }
643
644         } else if (l == 36) {
645
646                 /* Formatted UUID */
647
648                 for (i = 0; i < l; i++) {
649                         char c = s[i];
650
651                         if ((i == 8 || i == 13 || i == 18 || i == 23)) {
652                                 if (c != '-')
653                                         return false;
654                         } else {
655                                 if (!(c >= '0' && c <= '9') &&
656                                     !(c >= 'a' && c <= 'z') &&
657                                     !(c >= 'A' && c <= 'Z'))
658                                         return false;
659                         }
660                 }
661
662         } else
663                 return false;
664
665         return true;
666 }
667 #endif // 0
668
669 int container_get_leader(const char *machine, pid_t *pid) {
670         _cleanup_free_ char *s = NULL, *class = NULL;
671         const char *p;
672         pid_t leader;
673         int r;
674
675         assert(machine);
676         assert(pid);
677
678         if (!machine_name_is_valid(machine))
679                 return -EINVAL;
680
681         p = strjoina("/run/systemd/machines/", machine);
682         r = parse_env_file(p, NEWLINE, "LEADER", &s, "CLASS", &class, NULL);
683         if (r == -ENOENT)
684                 return -EHOSTDOWN;
685         if (r < 0)
686                 return r;
687         if (!s)
688                 return -EIO;
689
690         if (!streq_ptr(class, "container"))
691                 return -EIO;
692
693         r = parse_pid(s, &leader);
694         if (r < 0)
695                 return r;
696         if (leader <= 1)
697                 return -EIO;
698
699         *pid = leader;
700         return 0;
701 }
702
703 int namespace_open(pid_t pid, int *pidns_fd, int *mntns_fd, int *netns_fd, int *userns_fd, int *root_fd) {
704         _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, netnsfd = -1, usernsfd = -1;
705         int rfd = -1;
706
707         assert(pid >= 0);
708
709         if (mntns_fd) {
710                 const char *mntns;
711
712                 mntns = procfs_file_alloca(pid, "ns/mnt");
713                 mntnsfd = open(mntns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
714                 if (mntnsfd < 0)
715                         return -errno;
716         }
717
718         if (pidns_fd) {
719                 const char *pidns;
720
721                 pidns = procfs_file_alloca(pid, "ns/pid");
722                 pidnsfd = open(pidns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
723                 if (pidnsfd < 0)
724                         return -errno;
725         }
726
727         if (netns_fd) {
728                 const char *netns;
729
730                 netns = procfs_file_alloca(pid, "ns/net");
731                 netnsfd = open(netns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
732                 if (netnsfd < 0)
733                         return -errno;
734         }
735
736         if (userns_fd) {
737                 const char *userns;
738
739                 userns = procfs_file_alloca(pid, "ns/user");
740                 usernsfd = open(userns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
741                 if (usernsfd < 0 && errno != ENOENT)
742                         return -errno;
743         }
744
745         if (root_fd) {
746                 const char *root;
747
748                 root = procfs_file_alloca(pid, "root");
749                 rfd = open(root, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
750                 if (rfd < 0)
751                         return -errno;
752         }
753
754         if (pidns_fd)
755                 *pidns_fd = pidnsfd;
756
757         if (mntns_fd)
758                 *mntns_fd = mntnsfd;
759
760         if (netns_fd)
761                 *netns_fd = netnsfd;
762
763         if (userns_fd)
764                 *userns_fd = usernsfd;
765
766         if (root_fd)
767                 *root_fd = rfd;
768
769         pidnsfd = mntnsfd = netnsfd = usernsfd = -1;
770
771         return 0;
772 }
773
774 int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int root_fd) {
775         if (userns_fd >= 0) {
776                 /* Can't setns to your own userns, since then you could
777                  * escalate from non-root to root in your own namespace, so
778                  * check if namespaces equal before attempting to enter. */
779                 _cleanup_free_ char *userns_fd_path = NULL;
780                 int r;
781                 if (asprintf(&userns_fd_path, "/proc/self/fd/%d", userns_fd) < 0)
782                         return -ENOMEM;
783
784                 r = files_same(userns_fd_path, "/proc/self/ns/user");
785                 if (r < 0)
786                         return r;
787                 if (r)
788                         userns_fd = -1;
789         }
790
791         if (pidns_fd >= 0)
792                 if (setns(pidns_fd, CLONE_NEWPID) < 0)
793                         return -errno;
794
795         if (mntns_fd >= 0)
796                 if (setns(mntns_fd, CLONE_NEWNS) < 0)
797                         return -errno;
798
799         if (netns_fd >= 0)
800                 if (setns(netns_fd, CLONE_NEWNET) < 0)
801                         return -errno;
802
803         if (userns_fd >= 0)
804                 if (setns(userns_fd, CLONE_NEWUSER) < 0)
805                         return -errno;
806
807         if (root_fd >= 0) {
808                 if (fchdir(root_fd) < 0)
809                         return -errno;
810
811                 if (chroot(".") < 0)
812                         return -errno;
813         }
814
815         return reset_uid_gid();
816 }
817
818 uint64_t physical_memory(void) {
819         long mem;
820
821         /* We return this as uint64_t in case we are running as 32bit
822          * process on a 64bit kernel with huge amounts of memory */
823
824         mem = sysconf(_SC_PHYS_PAGES);
825         assert(mem > 0);
826
827         return (uint64_t) mem * (uint64_t) page_size();
828 }
829
830 #if 0 /// UNNEEDED by elogind
831 int update_reboot_param_file(const char *param) {
832         int r = 0;
833
834         if (param) {
835                 r = write_string_file(REBOOT_PARAM_FILE, param, WRITE_STRING_FILE_CREATE);
836                 if (r < 0)
837                         return log_error_errno(r, "Failed to write reboot param to "REBOOT_PARAM_FILE": %m");
838         } else
839                 (void) unlink(REBOOT_PARAM_FILE);
840
841         return 0;
842 }
843 #endif // 0
844
845 int version(void) {
846         puts(PACKAGE_STRING "\n"
847              SYSTEMD_FEATURES);
848         return 0;
849 }