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