2010-08-14 19:59:25 +02:00
|
|
|
|
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
|
2009-11-18 00:42:52 +01:00
|
|
|
|
|
2010-02-03 13:03:47 +01:00
|
|
|
|
/***
|
|
|
|
|
This file is part of systemd.
|
|
|
|
|
|
|
|
|
|
Copyright 2010 Lennart Poettering
|
|
|
|
|
|
|
|
|
|
systemd is free software; you can redistribute it and/or modify it
|
2012-04-12 00:20:58 +02:00
|
|
|
|
under the terms of the GNU Lesser General Public License as published by
|
|
|
|
|
the Free Software Foundation; either version 2.1 of the License, or
|
2010-02-03 13:03:47 +01:00
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
|
|
systemd is distributed in the hope that it will be useful, but
|
|
|
|
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
2012-04-12 00:20:58 +02:00
|
|
|
|
Lesser General Public License for more details.
|
2010-02-03 13:03:47 +01:00
|
|
|
|
|
2012-04-12 00:20:58 +02:00
|
|
|
|
You should have received a copy of the GNU Lesser General Public License
|
2010-02-03 13:03:47 +01:00
|
|
|
|
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
***/
|
|
|
|
|
|
2015-09-19 00:53:58 +02:00
|
|
|
|
#include <ctype.h>
|
|
|
|
|
#include <dirent.h>
|
2009-11-18 00:42:52 +01:00
|
|
|
|
#include <errno.h>
|
2015-09-19 00:53:58 +02:00
|
|
|
|
#include <fcntl.h>
|
|
|
|
|
#include <glob.h>
|
|
|
|
|
#include <grp.h>
|
|
|
|
|
#include <langinfo.h>
|
2015-01-29 16:12:58 +01:00
|
|
|
|
#include <libintl.h>
|
2015-09-19 00:53:58 +02:00
|
|
|
|
#include <limits.h>
|
|
|
|
|
#include <linux/magic.h>
|
2015-10-17 22:01:41 +02:00
|
|
|
|
#include <linux/oom.h>
|
2010-02-02 10:30:04 +01:00
|
|
|
|
#include <linux/sched.h>
|
2015-09-19 00:53:58 +02:00
|
|
|
|
#include <locale.h>
|
2015-02-12 14:06:32 +01:00
|
|
|
|
#include <poll.h>
|
2010-06-18 02:28:35 +02:00
|
|
|
|
#include <pwd.h>
|
2015-09-19 00:53:58 +02:00
|
|
|
|
#include <sched.h>
|
|
|
|
|
#include <signal.h>
|
|
|
|
|
#include <stdarg.h>
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
#include <sys/file.h>
|
|
|
|
|
#include <sys/ioctl.h>
|
2011-10-07 21:06:39 +02:00
|
|
|
|
#include <sys/mman.h>
|
2014-06-05 21:35:35 +02:00
|
|
|
|
#include <sys/mount.h>
|
2014-02-18 23:35:19 +01:00
|
|
|
|
#include <sys/personality.h>
|
2015-09-19 00:53:58 +02:00
|
|
|
|
#include <sys/prctl.h>
|
|
|
|
|
#include <sys/stat.h>
|
2015-01-04 19:51:17 +01:00
|
|
|
|
#include <sys/statvfs.h>
|
2015-09-19 00:53:58 +02:00
|
|
|
|
#include <sys/time.h>
|
|
|
|
|
#include <sys/types.h>
|
|
|
|
|
#include <sys/utsname.h>
|
|
|
|
|
#include <sys/vfs.h>
|
|
|
|
|
#include <sys/wait.h>
|
|
|
|
|
#include <syslog.h>
|
|
|
|
|
#include <unistd.h>
|
2015-02-11 18:50:38 +01:00
|
|
|
|
|
|
|
|
|
/* When we include libgen.h because we need dirname() we immediately
|
2015-09-19 00:53:58 +02:00
|
|
|
|
* undefine basename() since libgen.h defines it as a macro to the
|
|
|
|
|
* POSIX version which is really broken. We prefer GNU basename(). */
|
2015-02-11 18:50:38 +01:00
|
|
|
|
#include <libgen.h>
|
2013-12-07 03:29:55 +01:00
|
|
|
|
#undef basename
|
2009-11-18 00:42:52 +01:00
|
|
|
|
|
2013-12-22 19:59:12 +01:00
|
|
|
|
#ifdef HAVE_SYS_AUXV_H
|
|
|
|
|
#include <sys/auxv.h>
|
|
|
|
|
#endif
|
|
|
|
|
|
2015-09-19 00:53:58 +02:00
|
|
|
|
/* We include linux/fs.h as last of the system headers, as it
|
|
|
|
|
* otherwise conflicts with sys/mount.h. Yay, Linux is great! */
|
|
|
|
|
#include <linux/fs.h>
|
|
|
|
|
|
2015-09-23 03:01:06 +02:00
|
|
|
|
#include "build.h"
|
2015-09-19 00:53:58 +02:00
|
|
|
|
#include "def.h"
|
|
|
|
|
#include "device-nodes.h"
|
|
|
|
|
#include "env-util.h"
|
2015-10-23 18:52:53 +02:00
|
|
|
|
#include "escape.h"
|
2015-09-19 00:53:58 +02:00
|
|
|
|
#include "exit-status.h"
|
2015-10-25 13:14:12 +01:00
|
|
|
|
#include "fd-util.h"
|
2015-09-19 00:53:58 +02:00
|
|
|
|
#include "fileio.h"
|
|
|
|
|
#include "formats-util.h"
|
|
|
|
|
#include "gunicode.h"
|
|
|
|
|
#include "hashmap.h"
|
|
|
|
|
#include "hostname-util.h"
|
2010-01-30 01:52:32 +01:00
|
|
|
|
#include "ioprio.h"
|
2010-02-12 02:01:14 +01:00
|
|
|
|
#include "log.h"
|
2015-09-19 00:53:58 +02:00
|
|
|
|
#include "macro.h"
|
|
|
|
|
#include "missing.h"
|
2014-05-22 14:10:50 +02:00
|
|
|
|
#include "mkdir.h"
|
2015-10-26 16:41:43 +01:00
|
|
|
|
#include "hexdecoct.h"
|
2015-10-26 16:18:16 +01:00
|
|
|
|
#include "parse-util.h"
|
2012-05-07 21:36:12 +02:00
|
|
|
|
#include "path-util.h"
|
2015-04-10 19:10:00 +02:00
|
|
|
|
#include "process-util.h"
|
2015-04-10 22:27:10 +02:00
|
|
|
|
#include "random-util.h"
|
2015-05-29 20:14:11 +02:00
|
|
|
|
#include "signal-util.h"
|
2015-09-19 00:53:58 +02:00
|
|
|
|
#include "sparse-endian.h"
|
2015-10-24 22:58:24 +02:00
|
|
|
|
#include "string-util.h"
|
2015-09-19 00:53:58 +02:00
|
|
|
|
#include "strv.h"
|
|
|
|
|
#include "terminal-util.h"
|
2015-10-25 22:32:30 +01:00
|
|
|
|
#include "user-util.h"
|
2015-09-19 00:53:58 +02:00
|
|
|
|
#include "utf8.h"
|
2015-10-23 18:52:53 +02:00
|
|
|
|
#include "util.h"
|
2015-10-25 13:14:12 +01:00
|
|
|
|
#include "virt.h"
|
2015-10-26 20:07:55 +01:00
|
|
|
|
#include "dirent-util.h"
|
Systemd is causing mislabeled devices to be created and then attempting to read them.
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
On 07/28/2010 05:57 AM, Kay Sievers wrote:
> On Wed, Jul 28, 2010 at 11:43, Lennart Poettering
> <lennart@poettering.net> wrote:
>> On Mon, 26.07.10 16:42, Daniel J Walsh (dwalsh@redhat.com) wrote:
>>> tcontext=system_u:object_r:device_t:s0 tclass=chr_file
>>> type=1400 audit(1280174589.476:7): avc: denied { read } for pid=1
>>> comm="systemd" name="autofs" dev=devtmpfs ino=9482
>>> scontext=system_u:system_r:init_t:s0
>>> tcontext=system_u:object_r:device_t:s0 tclass=chr_file
>>> type=1400 audit(1280174589.476:8): avc: denied { read } for pid=1
>>> comm="systemd" name="autofs" dev=devtmpfs ino=9482
>>> scontext=system_u:system_r:init_t:s0
>>> tcontext=system_u:object_r:device_t:s0 tclass=chr_file
>>>
>>> Lennart, we talked about this earlier. I think this is caused by the
>>> modprobe calls to create /dev/autofs. Since udev is not created at the
>>> point that init loads the kernel modules, the devices get created with
>>> the wrong label. Once udev starts the labels get fixed.
>>>
>>> I can allow init_t to read device_t chr_files.
>>
>> Hmm, I think a cleaner fix would be to make systemd relabel this device
>> properly before accessing it? Given that this is only one device this
>> should not be a problem for us to maintain, I think? How would the
>> fixing of the label work? Would we have to spawn restorecon for this, or
>> can we actually do this in C without too much work?
>
> I guess we can just do what udev is doing, and call setfilecon(), with
> a context of an earlier matchpathcon().
>
> Kay
> _______________________________________________
> systemd-devel mailing list
> systemd-devel@lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/systemd-devel
Here is the updated patch with a fix for the labeling of /dev/autofs
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2.0.14 (GNU/Linux)
Comment: Using GnuPG with Fedora - http://enigmail.mozdev.org/
iEYEARECAAYFAkxQMyoACgkQrlYvE4MpobNviACfWgxsjW2xzz1qznFex8RVAQHf
gIEAmwRmRcLvGqYtwQaZ3WKIg8wmrwNk
=pC2e
2010-07-28 15:39:54 +02:00
|
|
|
|
|
2015-03-14 03:10:19 +01:00
|
|
|
|
/* Put this test here for a lack of better place */
|
|
|
|
|
assert_cc(EAGAIN == EWOULDBLOCK);
|
|
|
|
|
|
2011-06-30 04:16:10 +02:00
|
|
|
|
int saved_argc = 0;
|
|
|
|
|
char **saved_argv = NULL;
|
2012-09-24 14:43:07 +02:00
|
|
|
|
|
2011-03-18 03:03:41 +01:00
|
|
|
|
size_t page_size(void) {
|
2013-12-16 01:24:14 +01:00
|
|
|
|
static thread_local size_t pgsz = 0;
|
2011-03-18 03:03:41 +01:00
|
|
|
|
long r;
|
|
|
|
|
|
2011-10-07 21:06:39 +02:00
|
|
|
|
if (_likely_(pgsz > 0))
|
2011-03-18 03:03:41 +01:00
|
|
|
|
return pgsz;
|
|
|
|
|
|
2012-09-14 10:06:42 +02:00
|
|
|
|
r = sysconf(_SC_PAGESIZE);
|
|
|
|
|
assert(r > 0);
|
2011-03-18 03:03:41 +01:00
|
|
|
|
|
|
|
|
|
pgsz = (size_t) r;
|
|
|
|
|
return pgsz;
|
|
|
|
|
}
|
|
|
|
|
|
2010-04-10 17:41:34 +02:00
|
|
|
|
bool fstype_is_network(const char *fstype) {
|
2012-09-14 10:24:27 +02:00
|
|
|
|
static const char table[] =
|
2015-02-20 19:35:11 +01:00
|
|
|
|
"afs\0"
|
2012-09-14 10:24:27 +02:00
|
|
|
|
"cifs\0"
|
|
|
|
|
"smbfs\0"
|
2014-06-21 04:43:49 +02:00
|
|
|
|
"sshfs\0"
|
2012-09-14 10:24:27 +02:00
|
|
|
|
"ncpfs\0"
|
2013-07-15 18:33:57 +02:00
|
|
|
|
"ncp\0"
|
2012-09-14 10:24:27 +02:00
|
|
|
|
"nfs\0"
|
|
|
|
|
"nfs4\0"
|
|
|
|
|
"gfs\0"
|
2014-03-25 01:46:24 +01:00
|
|
|
|
"gfs2\0"
|
|
|
|
|
"glusterfs\0";
|
|
|
|
|
|
|
|
|
|
const char *x;
|
|
|
|
|
|
|
|
|
|
x = startswith(fstype, "fuse.");
|
|
|
|
|
if (x)
|
|
|
|
|
fstype = x;
|
2010-04-10 17:41:34 +02:00
|
|
|
|
|
2012-09-14 10:24:27 +02:00
|
|
|
|
return nulstr_contains(table, fstype);
|
2010-04-10 17:41:34 +02:00
|
|
|
|
}
|
|
|
|
|
|
2010-05-24 05:25:33 +02:00
|
|
|
|
int dir_is_empty(const char *path) {
|
2012-09-14 10:24:27 +02:00
|
|
|
|
_cleanup_closedir_ DIR *d;
|
2015-10-19 23:58:17 +02:00
|
|
|
|
struct dirent *de;
|
2010-05-24 05:25:33 +02:00
|
|
|
|
|
2012-09-14 10:24:27 +02:00
|
|
|
|
d = opendir(path);
|
|
|
|
|
if (!d)
|
2010-05-24 05:25:33 +02:00
|
|
|
|
return -errno;
|
|
|
|
|
|
2015-10-19 23:58:17 +02:00
|
|
|
|
FOREACH_DIRENT(de, d, return -errno)
|
|
|
|
|
return 0;
|
2010-05-24 05:25:33 +02:00
|
|
|
|
|
2015-10-19 23:58:17 +02:00
|
|
|
|
return 1;
|
2010-05-24 05:25:33 +02:00
|
|
|
|
}
|
|
|
|
|
|
2010-06-16 21:54:17 +02:00
|
|
|
|
void rename_process(const char name[8]) {
|
|
|
|
|
assert(name);
|
|
|
|
|
|
2012-02-01 22:33:15 +01:00
|
|
|
|
/* This is a like a poor man's setproctitle(). It changes the
|
|
|
|
|
* comm field, argv[0], and also the glibc's internally used
|
|
|
|
|
* name of the process. For the first one a limit of 16 chars
|
|
|
|
|
* applies, to the second one usually one of 10 (i.e. length
|
|
|
|
|
* of "/sbin/init"), to the third one one of 7 (i.e. length of
|
|
|
|
|
* "systemd"). If you pass a longer string it will be
|
|
|
|
|
* truncated */
|
2010-06-16 21:54:17 +02:00
|
|
|
|
|
2012-02-01 22:33:15 +01:00
|
|
|
|
prctl(PR_SET_NAME, name);
|
2010-06-16 21:54:17 +02:00
|
|
|
|
|
|
|
|
|
if (program_invocation_name)
|
|
|
|
|
strncpy(program_invocation_name, name, strlen(program_invocation_name));
|
2011-06-30 04:16:10 +02:00
|
|
|
|
|
|
|
|
|
if (saved_argc > 0) {
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
if (saved_argv[0])
|
|
|
|
|
strncpy(saved_argv[0], name, strlen(saved_argv[0]));
|
|
|
|
|
|
|
|
|
|
for (i = 1; i < saved_argc; i++) {
|
|
|
|
|
if (!saved_argv[i])
|
|
|
|
|
break;
|
|
|
|
|
|
2014-01-31 06:51:32 +01:00
|
|
|
|
memzero(saved_argv[i], strlen(saved_argv[i]));
|
2011-06-30 04:16:10 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
2010-06-16 21:54:17 +02:00
|
|
|
|
}
|
|
|
|
|
|
2015-10-15 12:13:13 +02:00
|
|
|
|
bool is_fs_type(const struct statfs *s, statfs_f_type_t magic_value) {
|
2012-11-16 17:17:21 +01:00
|
|
|
|
assert(s);
|
2015-10-15 12:13:13 +02:00
|
|
|
|
assert_cc(sizeof(statfs_f_type_t) >= sizeof(s->f_type));
|
|
|
|
|
|
|
|
|
|
return F_TYPE_EQUAL(s->f_type, magic_value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int fd_check_fstype(int fd, statfs_f_type_t magic_value) {
|
|
|
|
|
struct statfs s;
|
|
|
|
|
|
|
|
|
|
if (fstatfs(fd, &s) < 0)
|
|
|
|
|
return -errno;
|
2013-12-16 01:56:21 +01:00
|
|
|
|
|
2015-10-15 12:13:13 +02:00
|
|
|
|
return is_fs_type(&s, magic_value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int path_check_fstype(const char *path, statfs_f_type_t magic_value) {
|
|
|
|
|
_cleanup_close_ int fd = -1;
|
|
|
|
|
|
|
|
|
|
fd = open(path, O_RDONLY);
|
|
|
|
|
if (fd < 0)
|
|
|
|
|
return -errno;
|
|
|
|
|
|
|
|
|
|
return fd_check_fstype(fd, magic_value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool is_temporary_fs(const struct statfs *s) {
|
|
|
|
|
return is_fs_type(s, TMPFS_MAGIC) ||
|
|
|
|
|
is_fs_type(s, RAMFS_MAGIC);
|
2012-11-16 17:17:21 +01:00
|
|
|
|
}
|
|
|
|
|
|
2015-04-04 11:52:57 +02:00
|
|
|
|
int fd_is_temporary_fs(int fd) {
|
2014-11-24 09:43:29 +01:00
|
|
|
|
struct statfs s;
|
|
|
|
|
|
|
|
|
|
if (fstatfs(fd, &s) < 0)
|
|
|
|
|
return -errno;
|
|
|
|
|
|
|
|
|
|
return is_temporary_fs(&s);
|
|
|
|
|
}
|
|
|
|
|
|
2014-03-06 09:12:57 +01:00
|
|
|
|
int files_same(const char *filea, const char *fileb) {
|
|
|
|
|
struct stat a, b;
|
2010-07-08 21:34:51 +02:00
|
|
|
|
|
2014-03-06 09:12:57 +01:00
|
|
|
|
if (stat(filea, &a) < 0)
|
2010-07-08 21:34:51 +02:00
|
|
|
|
return -errno;
|
|
|
|
|
|
2014-03-06 09:12:57 +01:00
|
|
|
|
if (stat(fileb, &b) < 0)
|
2010-07-08 21:34:51 +02:00
|
|
|
|
return -errno;
|
|
|
|
|
|
2014-03-06 09:12:57 +01:00
|
|
|
|
return a.st_dev == b.st_dev &&
|
|
|
|
|
a.st_ino == b.st_ino;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int running_in_chroot(void) {
|
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
|
|
ret = files_same("/proc/1/root", "/");
|
|
|
|
|
if (ret < 0)
|
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
|
|
return ret == 0;
|
2010-07-08 21:34:51 +02:00
|
|
|
|
}
|
|
|
|
|
|
2013-12-16 17:53:53 +01:00
|
|
|
|
noreturn void freeze(void) {
|
2011-03-11 00:52:13 +01:00
|
|
|
|
|
|
|
|
|
/* Make sure nobody waits for us on a socket anymore */
|
|
|
|
|
close_all_fds(NULL, 0);
|
|
|
|
|
|
2011-01-01 19:50:32 +01:00
|
|
|
|
sync();
|
|
|
|
|
|
2010-10-07 19:34:56 +02:00
|
|
|
|
for (;;)
|
|
|
|
|
pause();
|
|
|
|
|
}
|
|
|
|
|
|
2010-10-08 02:31:36 +02:00
|
|
|
|
bool null_or_empty(struct stat *st) {
|
|
|
|
|
assert(st);
|
|
|
|
|
|
|
|
|
|
if (S_ISREG(st->st_mode) && st->st_size <= 0)
|
|
|
|
|
return true;
|
|
|
|
|
|
2010-10-08 18:22:28 +02:00
|
|
|
|
if (S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode))
|
2010-10-08 02:31:36 +02:00
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2011-07-22 04:21:18 +02:00
|
|
|
|
int null_or_empty_path(const char *fn) {
|
|
|
|
|
struct stat st;
|
|
|
|
|
|
|
|
|
|
assert(fn);
|
|
|
|
|
|
|
|
|
|
if (stat(fn, &st) < 0)
|
|
|
|
|
return -errno;
|
|
|
|
|
|
|
|
|
|
return null_or_empty(&st);
|
|
|
|
|
}
|
|
|
|
|
|
2014-07-17 00:59:49 +02:00
|
|
|
|
int null_or_empty_fd(int fd) {
|
|
|
|
|
struct stat st;
|
|
|
|
|
|
|
|
|
|
assert(fd >= 0);
|
|
|
|
|
|
|
|
|
|
if (fstat(fd, &st) < 0)
|
|
|
|
|
return -errno;
|
|
|
|
|
|
|
|
|
|
return null_or_empty(&st);
|
|
|
|
|
}
|
|
|
|
|
|
Implement masking and overriding of generators
Sometimes it is necessary to stop a generator from running. Either
because of a bug, or for testing, or some other reason. The only way
to do that would be to rename or chmod the generator binary, which is
inconvenient and does not survive upgrades. Allow masking and
overriding generators similarly to units and other configuration
files.
For the systemd instance, masking would be more common, rather than
overriding generators. For the user instances, it may also be useful
for users to have generators in $XDG_CONFIG_HOME to augment or
override system-wide generators.
Directories are searched according to the usual scheme (/usr/lib,
/usr/local/lib, /run, /etc), and files with the same name in higher
priority directories override files with the same name in lower
priority directories. Empty files and links to /dev/null mask a given
name.
https://bugs.freedesktop.org/show_bug.cgi?id=87230
2015-01-09 02:47:25 +01:00
|
|
|
|
static int do_execute(char **directories, usec_t timeout, char *argv[]) {
|
2015-01-08 23:30:07 +01:00
|
|
|
|
_cleanup_hashmap_free_free_ Hashmap *pids = NULL;
|
Implement masking and overriding of generators
Sometimes it is necessary to stop a generator from running. Either
because of a bug, or for testing, or some other reason. The only way
to do that would be to rename or chmod the generator binary, which is
inconvenient and does not survive upgrades. Allow masking and
overriding generators similarly to units and other configuration
files.
For the systemd instance, masking would be more common, rather than
overriding generators. For the user instances, it may also be useful
for users to have generators in $XDG_CONFIG_HOME to augment or
override system-wide generators.
Directories are searched according to the usual scheme (/usr/lib,
/usr/local/lib, /run, /etc), and files with the same name in higher
priority directories override files with the same name in lower
priority directories. Empty files and links to /dev/null mask a given
name.
https://bugs.freedesktop.org/show_bug.cgi?id=87230
2015-01-09 02:47:25 +01:00
|
|
|
|
_cleanup_set_free_free_ Set *seen = NULL;
|
|
|
|
|
char **directory;
|
2011-02-15 00:30:11 +01:00
|
|
|
|
|
2015-01-08 23:30:07 +01:00
|
|
|
|
/* We fork this all off from a child process so that we can
|
|
|
|
|
* somewhat cleanly make use of SIGALRM to set a time limit */
|
2011-02-15 00:30:11 +01:00
|
|
|
|
|
2015-05-31 23:55:55 +02:00
|
|
|
|
(void) reset_all_signal_handlers();
|
|
|
|
|
(void) reset_signal_mask();
|
2011-02-15 00:30:11 +01:00
|
|
|
|
|
2015-01-08 23:30:07 +01:00
|
|
|
|
assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
|
2011-02-15 00:30:11 +01:00
|
|
|
|
|
2015-01-08 23:30:07 +01:00
|
|
|
|
pids = hashmap_new(NULL);
|
|
|
|
|
if (!pids)
|
|
|
|
|
return log_oom();
|
2011-02-15 00:30:11 +01:00
|
|
|
|
|
Implement masking and overriding of generators
Sometimes it is necessary to stop a generator from running. Either
because of a bug, or for testing, or some other reason. The only way
to do that would be to rename or chmod the generator binary, which is
inconvenient and does not survive upgrades. Allow masking and
overriding generators similarly to units and other configuration
files.
For the systemd instance, masking would be more common, rather than
overriding generators. For the user instances, it may also be useful
for users to have generators in $XDG_CONFIG_HOME to augment or
override system-wide generators.
Directories are searched according to the usual scheme (/usr/lib,
/usr/local/lib, /run, /etc), and files with the same name in higher
priority directories override files with the same name in lower
priority directories. Empty files and links to /dev/null mask a given
name.
https://bugs.freedesktop.org/show_bug.cgi?id=87230
2015-01-09 02:47:25 +01:00
|
|
|
|
seen = set_new(&string_hash_ops);
|
|
|
|
|
if (!seen)
|
|
|
|
|
return log_oom();
|
2011-02-15 00:30:11 +01:00
|
|
|
|
|
Implement masking and overriding of generators
Sometimes it is necessary to stop a generator from running. Either
because of a bug, or for testing, or some other reason. The only way
to do that would be to rename or chmod the generator binary, which is
inconvenient and does not survive upgrades. Allow masking and
overriding generators similarly to units and other configuration
files.
For the systemd instance, masking would be more common, rather than
overriding generators. For the user instances, it may also be useful
for users to have generators in $XDG_CONFIG_HOME to augment or
override system-wide generators.
Directories are searched according to the usual scheme (/usr/lib,
/usr/local/lib, /run, /etc), and files with the same name in higher
priority directories override files with the same name in lower
priority directories. Empty files and links to /dev/null mask a given
name.
https://bugs.freedesktop.org/show_bug.cgi?id=87230
2015-01-09 02:47:25 +01:00
|
|
|
|
STRV_FOREACH(directory, directories) {
|
|
|
|
|
_cleanup_closedir_ DIR *d;
|
|
|
|
|
struct dirent *de;
|
2011-02-15 00:30:11 +01:00
|
|
|
|
|
Implement masking and overriding of generators
Sometimes it is necessary to stop a generator from running. Either
because of a bug, or for testing, or some other reason. The only way
to do that would be to rename or chmod the generator binary, which is
inconvenient and does not survive upgrades. Allow masking and
overriding generators similarly to units and other configuration
files.
For the systemd instance, masking would be more common, rather than
overriding generators. For the user instances, it may also be useful
for users to have generators in $XDG_CONFIG_HOME to augment or
override system-wide generators.
Directories are searched according to the usual scheme (/usr/lib,
/usr/local/lib, /run, /etc), and files with the same name in higher
priority directories override files with the same name in lower
priority directories. Empty files and links to /dev/null mask a given
name.
https://bugs.freedesktop.org/show_bug.cgi?id=87230
2015-01-09 02:47:25 +01:00
|
|
|
|
d = opendir(*directory);
|
|
|
|
|
if (!d) {
|
|
|
|
|
if (errno == ENOENT)
|
|
|
|
|
continue;
|
2011-02-15 00:30:11 +01:00
|
|
|
|
|
Implement masking and overriding of generators
Sometimes it is necessary to stop a generator from running. Either
because of a bug, or for testing, or some other reason. The only way
to do that would be to rename or chmod the generator binary, which is
inconvenient and does not survive upgrades. Allow masking and
overriding generators similarly to units and other configuration
files.
For the systemd instance, masking would be more common, rather than
overriding generators. For the user instances, it may also be useful
for users to have generators in $XDG_CONFIG_HOME to augment or
override system-wide generators.
Directories are searched according to the usual scheme (/usr/lib,
/usr/local/lib, /run, /etc), and files with the same name in higher
priority directories override files with the same name in lower
priority directories. Empty files and links to /dev/null mask a given
name.
https://bugs.freedesktop.org/show_bug.cgi?id=87230
2015-01-09 02:47:25 +01:00
|
|
|
|
return log_error_errno(errno, "Failed to open directory %s: %m", *directory);
|
|
|
|
|
}
|
2011-02-15 00:30:11 +01:00
|
|
|
|
|
Implement masking and overriding of generators
Sometimes it is necessary to stop a generator from running. Either
because of a bug, or for testing, or some other reason. The only way
to do that would be to rename or chmod the generator binary, which is
inconvenient and does not survive upgrades. Allow masking and
overriding generators similarly to units and other configuration
files.
For the systemd instance, masking would be more common, rather than
overriding generators. For the user instances, it may also be useful
for users to have generators in $XDG_CONFIG_HOME to augment or
override system-wide generators.
Directories are searched according to the usual scheme (/usr/lib,
/usr/local/lib, /run, /etc), and files with the same name in higher
priority directories override files with the same name in lower
priority directories. Empty files and links to /dev/null mask a given
name.
https://bugs.freedesktop.org/show_bug.cgi?id=87230
2015-01-09 02:47:25 +01:00
|
|
|
|
FOREACH_DIRENT(de, d, break) {
|
|
|
|
|
_cleanup_free_ char *path = NULL;
|
|
|
|
|
pid_t pid;
|
|
|
|
|
int r;
|
|
|
|
|
|
|
|
|
|
if (!dirent_is_file(de))
|
|
|
|
|
continue;
|
2011-02-15 00:30:11 +01:00
|
|
|
|
|
Implement masking and overriding of generators
Sometimes it is necessary to stop a generator from running. Either
because of a bug, or for testing, or some other reason. The only way
to do that would be to rename or chmod the generator binary, which is
inconvenient and does not survive upgrades. Allow masking and
overriding generators similarly to units and other configuration
files.
For the systemd instance, masking would be more common, rather than
overriding generators. For the user instances, it may also be useful
for users to have generators in $XDG_CONFIG_HOME to augment or
override system-wide generators.
Directories are searched according to the usual scheme (/usr/lib,
/usr/local/lib, /run, /etc), and files with the same name in higher
priority directories override files with the same name in lower
priority directories. Empty files and links to /dev/null mask a given
name.
https://bugs.freedesktop.org/show_bug.cgi?id=87230
2015-01-09 02:47:25 +01:00
|
|
|
|
if (set_contains(seen, de->d_name)) {
|
|
|
|
|
log_debug("%1$s/%2$s skipped (%2$s was already seen).", *directory, de->d_name);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
r = set_put_strdup(seen, de->d_name);
|
|
|
|
|
if (r < 0)
|
|
|
|
|
return log_oom();
|
|
|
|
|
|
|
|
|
|
path = strjoin(*directory, "/", de->d_name, NULL);
|
|
|
|
|
if (!path)
|
|
|
|
|
return log_oom();
|
|
|
|
|
|
|
|
|
|
if (null_or_empty_path(path)) {
|
|
|
|
|
log_debug("%s is empty (a mask).", path);
|
|
|
|
|
continue;
|
2015-03-15 01:14:39 +01:00
|
|
|
|
}
|
2011-02-15 00:30:11 +01:00
|
|
|
|
|
Implement masking and overriding of generators
Sometimes it is necessary to stop a generator from running. Either
because of a bug, or for testing, or some other reason. The only way
to do that would be to rename or chmod the generator binary, which is
inconvenient and does not survive upgrades. Allow masking and
overriding generators similarly to units and other configuration
files.
For the systemd instance, masking would be more common, rather than
overriding generators. For the user instances, it may also be useful
for users to have generators in $XDG_CONFIG_HOME to augment or
override system-wide generators.
Directories are searched according to the usual scheme (/usr/lib,
/usr/local/lib, /run, /etc), and files with the same name in higher
priority directories override files with the same name in lower
priority directories. Empty files and links to /dev/null mask a given
name.
https://bugs.freedesktop.org/show_bug.cgi?id=87230
2015-01-09 02:47:25 +01:00
|
|
|
|
pid = fork();
|
|
|
|
|
if (pid < 0) {
|
|
|
|
|
log_error_errno(errno, "Failed to fork: %m");
|
|
|
|
|
continue;
|
|
|
|
|
} else if (pid == 0) {
|
|
|
|
|
char *_argv[2];
|
2011-02-15 00:30:11 +01:00
|
|
|
|
|
Implement masking and overriding of generators
Sometimes it is necessary to stop a generator from running. Either
because of a bug, or for testing, or some other reason. The only way
to do that would be to rename or chmod the generator binary, which is
inconvenient and does not survive upgrades. Allow masking and
overriding generators similarly to units and other configuration
files.
For the systemd instance, masking would be more common, rather than
overriding generators. For the user instances, it may also be useful
for users to have generators in $XDG_CONFIG_HOME to augment or
override system-wide generators.
Directories are searched according to the usual scheme (/usr/lib,
/usr/local/lib, /run, /etc), and files with the same name in higher
priority directories override files with the same name in lower
priority directories. Empty files and links to /dev/null mask a given
name.
https://bugs.freedesktop.org/show_bug.cgi?id=87230
2015-01-09 02:47:25 +01:00
|
|
|
|
assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
|
2011-02-15 00:30:11 +01:00
|
|
|
|
|
Implement masking and overriding of generators
Sometimes it is necessary to stop a generator from running. Either
because of a bug, or for testing, or some other reason. The only way
to do that would be to rename or chmod the generator binary, which is
inconvenient and does not survive upgrades. Allow masking and
overriding generators similarly to units and other configuration
files.
For the systemd instance, masking would be more common, rather than
overriding generators. For the user instances, it may also be useful
for users to have generators in $XDG_CONFIG_HOME to augment or
override system-wide generators.
Directories are searched according to the usual scheme (/usr/lib,
/usr/local/lib, /run, /etc), and files with the same name in higher
priority directories override files with the same name in lower
priority directories. Empty files and links to /dev/null mask a given
name.
https://bugs.freedesktop.org/show_bug.cgi?id=87230
2015-01-09 02:47:25 +01:00
|
|
|
|
if (!argv) {
|
|
|
|
|
_argv[0] = path;
|
|
|
|
|
_argv[1] = NULL;
|
|
|
|
|
argv = _argv;
|
|
|
|
|
} else
|
|
|
|
|
argv[0] = path;
|
|
|
|
|
|
|
|
|
|
execv(path, argv);
|
|
|
|
|
return log_error_errno(errno, "Failed to execute %s: %m", path);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
log_debug("Spawned %s as " PID_FMT ".", path, pid);
|
2011-02-15 00:30:11 +01:00
|
|
|
|
|
Implement masking and overriding of generators
Sometimes it is necessary to stop a generator from running. Either
because of a bug, or for testing, or some other reason. The only way
to do that would be to rename or chmod the generator binary, which is
inconvenient and does not survive upgrades. Allow masking and
overriding generators similarly to units and other configuration
files.
For the systemd instance, masking would be more common, rather than
overriding generators. For the user instances, it may also be useful
for users to have generators in $XDG_CONFIG_HOME to augment or
override system-wide generators.
Directories are searched according to the usual scheme (/usr/lib,
/usr/local/lib, /run, /etc), and files with the same name in higher
priority directories override files with the same name in lower
priority directories. Empty files and links to /dev/null mask a given
name.
https://bugs.freedesktop.org/show_bug.cgi?id=87230
2015-01-09 02:47:25 +01:00
|
|
|
|
r = hashmap_put(pids, UINT_TO_PTR(pid), path);
|
|
|
|
|
if (r < 0)
|
|
|
|
|
return log_oom();
|
|
|
|
|
path = NULL;
|
|
|
|
|
}
|
2015-01-08 23:30:07 +01:00
|
|
|
|
}
|
2011-02-15 00:30:11 +01:00
|
|
|
|
|
2015-01-08 23:30:07 +01:00
|
|
|
|
/* Abort execution of this process after the timout. We simply
|
|
|
|
|
* rely on SIGALRM as default action terminating the process,
|
|
|
|
|
* and turn on alarm(). */
|
2011-02-15 00:30:11 +01:00
|
|
|
|
|
2015-01-08 23:30:07 +01:00
|
|
|
|
if (timeout != USEC_INFINITY)
|
|
|
|
|
alarm((timeout + USEC_PER_SEC - 1) / USEC_PER_SEC);
|
2011-02-15 00:30:11 +01:00
|
|
|
|
|
2015-01-08 23:30:07 +01:00
|
|
|
|
while (!hashmap_isempty(pids)) {
|
|
|
|
|
_cleanup_free_ char *path = NULL;
|
|
|
|
|
pid_t pid;
|
2014-03-06 02:19:06 +01:00
|
|
|
|
|
2015-01-08 23:30:07 +01:00
|
|
|
|
pid = PTR_TO_UINT(hashmap_first_key(pids));
|
|
|
|
|
assert(pid > 0);
|
2011-02-15 00:30:11 +01:00
|
|
|
|
|
2015-01-08 23:30:07 +01:00
|
|
|
|
path = hashmap_remove(pids, UINT_TO_PTR(pid));
|
|
|
|
|
assert(path);
|
2014-03-06 02:19:06 +01:00
|
|
|
|
|
2015-01-08 23:30:07 +01:00
|
|
|
|
wait_for_terminate_and_warn(path, pid, true);
|
|
|
|
|
}
|
2014-03-06 02:19:06 +01:00
|
|
|
|
|
2015-01-08 23:30:07 +01:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
2014-03-06 02:19:06 +01:00
|
|
|
|
|
Implement masking and overriding of generators
Sometimes it is necessary to stop a generator from running. Either
because of a bug, or for testing, or some other reason. The only way
to do that would be to rename or chmod the generator binary, which is
inconvenient and does not survive upgrades. Allow masking and
overriding generators similarly to units and other configuration
files.
For the systemd instance, masking would be more common, rather than
overriding generators. For the user instances, it may also be useful
for users to have generators in $XDG_CONFIG_HOME to augment or
override system-wide generators.
Directories are searched according to the usual scheme (/usr/lib,
/usr/local/lib, /run, /etc), and files with the same name in higher
priority directories override files with the same name in lower
priority directories. Empty files and links to /dev/null mask a given
name.
https://bugs.freedesktop.org/show_bug.cgi?id=87230
2015-01-09 02:47:25 +01:00
|
|
|
|
void execute_directories(const char* const* directories, usec_t timeout, char *argv[]) {
|
2015-01-08 23:30:07 +01:00
|
|
|
|
pid_t executor_pid;
|
|
|
|
|
int r;
|
Implement masking and overriding of generators
Sometimes it is necessary to stop a generator from running. Either
because of a bug, or for testing, or some other reason. The only way
to do that would be to rename or chmod the generator binary, which is
inconvenient and does not survive upgrades. Allow masking and
overriding generators similarly to units and other configuration
files.
For the systemd instance, masking would be more common, rather than
overriding generators. For the user instances, it may also be useful
for users to have generators in $XDG_CONFIG_HOME to augment or
override system-wide generators.
Directories are searched according to the usual scheme (/usr/lib,
/usr/local/lib, /run, /etc), and files with the same name in higher
priority directories override files with the same name in lower
priority directories. Empty files and links to /dev/null mask a given
name.
https://bugs.freedesktop.org/show_bug.cgi?id=87230
2015-01-09 02:47:25 +01:00
|
|
|
|
char *name;
|
|
|
|
|
char **dirs = (char**) directories;
|
|
|
|
|
|
|
|
|
|
assert(!strv_isempty(dirs));
|
2011-02-15 00:30:11 +01:00
|
|
|
|
|
Implement masking and overriding of generators
Sometimes it is necessary to stop a generator from running. Either
because of a bug, or for testing, or some other reason. The only way
to do that would be to rename or chmod the generator binary, which is
inconvenient and does not survive upgrades. Allow masking and
overriding generators similarly to units and other configuration
files.
For the systemd instance, masking would be more common, rather than
overriding generators. For the user instances, it may also be useful
for users to have generators in $XDG_CONFIG_HOME to augment or
override system-wide generators.
Directories are searched according to the usual scheme (/usr/lib,
/usr/local/lib, /run, /etc), and files with the same name in higher
priority directories override files with the same name in lower
priority directories. Empty files and links to /dev/null mask a given
name.
https://bugs.freedesktop.org/show_bug.cgi?id=87230
2015-01-09 02:47:25 +01:00
|
|
|
|
name = basename(dirs[0]);
|
|
|
|
|
assert(!isempty(name));
|
2014-03-06 02:19:06 +01:00
|
|
|
|
|
Implement masking and overriding of generators
Sometimes it is necessary to stop a generator from running. Either
because of a bug, or for testing, or some other reason. The only way
to do that would be to rename or chmod the generator binary, which is
inconvenient and does not survive upgrades. Allow masking and
overriding generators similarly to units and other configuration
files.
For the systemd instance, masking would be more common, rather than
overriding generators. For the user instances, it may also be useful
for users to have generators in $XDG_CONFIG_HOME to augment or
override system-wide generators.
Directories are searched according to the usual scheme (/usr/lib,
/usr/local/lib, /run, /etc), and files with the same name in higher
priority directories override files with the same name in lower
priority directories. Empty files and links to /dev/null mask a given
name.
https://bugs.freedesktop.org/show_bug.cgi?id=87230
2015-01-09 02:47:25 +01:00
|
|
|
|
/* Executes all binaries in the directories in parallel and waits
|
|
|
|
|
* for them to finish. Optionally a timeout is applied. If a file
|
|
|
|
|
* with the same name exists in more than one directory, the
|
|
|
|
|
* earliest one wins. */
|
2011-02-15 00:30:11 +01:00
|
|
|
|
|
2015-01-08 23:30:07 +01:00
|
|
|
|
executor_pid = fork();
|
|
|
|
|
if (executor_pid < 0) {
|
|
|
|
|
log_error_errno(errno, "Failed to fork: %m");
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
} else if (executor_pid == 0) {
|
Implement masking and overriding of generators
Sometimes it is necessary to stop a generator from running. Either
because of a bug, or for testing, or some other reason. The only way
to do that would be to rename or chmod the generator binary, which is
inconvenient and does not survive upgrades. Allow masking and
overriding generators similarly to units and other configuration
files.
For the systemd instance, masking would be more common, rather than
overriding generators. For the user instances, it may also be useful
for users to have generators in $XDG_CONFIG_HOME to augment or
override system-wide generators.
Directories are searched according to the usual scheme (/usr/lib,
/usr/local/lib, /run, /etc), and files with the same name in higher
priority directories override files with the same name in lower
priority directories. Empty files and links to /dev/null mask a given
name.
https://bugs.freedesktop.org/show_bug.cgi?id=87230
2015-01-09 02:47:25 +01:00
|
|
|
|
r = do_execute(dirs, timeout, argv);
|
2015-01-08 23:30:07 +01:00
|
|
|
|
_exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS);
|
2014-03-06 02:19:06 +01:00
|
|
|
|
}
|
2011-02-15 00:30:11 +01:00
|
|
|
|
|
Implement masking and overriding of generators
Sometimes it is necessary to stop a generator from running. Either
because of a bug, or for testing, or some other reason. The only way
to do that would be to rename or chmod the generator binary, which is
inconvenient and does not survive upgrades. Allow masking and
overriding generators similarly to units and other configuration
files.
For the systemd instance, masking would be more common, rather than
overriding generators. For the user instances, it may also be useful
for users to have generators in $XDG_CONFIG_HOME to augment or
override system-wide generators.
Directories are searched according to the usual scheme (/usr/lib,
/usr/local/lib, /run, /etc), and files with the same name in higher
priority directories override files with the same name in lower
priority directories. Empty files and links to /dev/null mask a given
name.
https://bugs.freedesktop.org/show_bug.cgi?id=87230
2015-01-09 02:47:25 +01:00
|
|
|
|
wait_for_terminate_and_warn(name, executor_pid, true);
|
2011-02-15 00:30:11 +01:00
|
|
|
|
}
|
|
|
|
|
|
2015-03-16 18:29:26 +01:00
|
|
|
|
bool plymouth_running(void) {
|
|
|
|
|
return access("/run/plymouth/pid", F_OK) >= 0;
|
|
|
|
|
}
|
|
|
|
|
|
2011-06-27 22:44:12 +02:00
|
|
|
|
bool display_is_local(const char *display) {
|
|
|
|
|
assert(display);
|
|
|
|
|
|
|
|
|
|
return
|
|
|
|
|
display[0] == ':' &&
|
|
|
|
|
display[1] >= '0' &&
|
|
|
|
|
display[1] <= '9';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int socket_from_display(const char *display, char **path) {
|
|
|
|
|
size_t k;
|
|
|
|
|
char *f, *c;
|
|
|
|
|
|
|
|
|
|
assert(display);
|
|
|
|
|
assert(path);
|
|
|
|
|
|
|
|
|
|
if (!display_is_local(display))
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
|
|
k = strspn(display+1, "0123456789");
|
|
|
|
|
|
2014-03-15 19:40:07 +01:00
|
|
|
|
f = new(char, strlen("/tmp/.X11-unix/X") + k + 1);
|
2011-06-27 22:44:12 +02:00
|
|
|
|
if (!f)
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
|
|
c = stpcpy(f, "/tmp/.X11-unix/X");
|
|
|
|
|
memcpy(c, display+1, k);
|
|
|
|
|
c[k] = 0;
|
|
|
|
|
|
|
|
|
|
*path = f;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2011-07-07 02:07:39 +02:00
|
|
|
|
int glob_exists(const char *path) {
|
2013-04-18 09:11:22 +02:00
|
|
|
|
_cleanup_globfree_ glob_t g = {};
|
2013-06-06 01:30:17 +02:00
|
|
|
|
int k;
|
2011-07-07 02:07:39 +02:00
|
|
|
|
|
|
|
|
|
assert(path);
|
|
|
|
|
|
|
|
|
|
errno = 0;
|
|
|
|
|
k = glob(path, GLOB_NOSORT|GLOB_BRACE, NULL, &g);
|
|
|
|
|
|
|
|
|
|
if (k == GLOB_NOMATCH)
|
2013-06-06 01:30:17 +02:00
|
|
|
|
return 0;
|
2011-07-07 02:07:39 +02:00
|
|
|
|
else if (k == GLOB_NOSPACE)
|
2013-06-06 01:30:17 +02:00
|
|
|
|
return -ENOMEM;
|
2011-07-07 02:07:39 +02:00
|
|
|
|
else if (k == 0)
|
2013-06-06 01:30:17 +02:00
|
|
|
|
return !strv_isempty(g.gl_pathv);
|
2011-07-07 02:07:39 +02:00
|
|
|
|
else
|
2013-06-06 01:30:17 +02:00
|
|
|
|
return errno ? -errno : -EIO;
|
|
|
|
|
}
|
2011-07-07 02:07:39 +02:00
|
|
|
|
|
2013-06-06 01:30:17 +02:00
|
|
|
|
int glob_extend(char ***strv, const char *path) {
|
|
|
|
|
_cleanup_globfree_ glob_t g = {};
|
|
|
|
|
int k;
|
|
|
|
|
char **p;
|
|
|
|
|
|
|
|
|
|
errno = 0;
|
2013-10-14 08:15:51 +02:00
|
|
|
|
k = glob(path, GLOB_NOSORT|GLOB_BRACE, NULL, &g);
|
2013-06-06 01:30:17 +02:00
|
|
|
|
|
|
|
|
|
if (k == GLOB_NOMATCH)
|
|
|
|
|
return -ENOENT;
|
|
|
|
|
else if (k == GLOB_NOSPACE)
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
else if (k != 0 || strv_isempty(g.gl_pathv))
|
|
|
|
|
return errno ? -errno : -EIO;
|
|
|
|
|
|
|
|
|
|
STRV_FOREACH(p, g.gl_pathv) {
|
|
|
|
|
k = strv_extend(strv, *p);
|
|
|
|
|
if (k < 0)
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return k;
|
2011-07-07 02:07:39 +02:00
|
|
|
|
}
|
|
|
|
|
|
2011-08-01 05:05:12 +02:00
|
|
|
|
bool is_main_thread(void) {
|
2013-12-16 01:24:14 +01:00
|
|
|
|
static thread_local int cached = 0;
|
2011-08-01 05:05:12 +02:00
|
|
|
|
|
|
|
|
|
if (_unlikely_(cached == 0))
|
|
|
|
|
cached = getpid() == gettid() ? 1 : -1;
|
|
|
|
|
|
|
|
|
|
return cached > 0;
|
|
|
|
|
}
|
|
|
|
|
|
2011-08-21 00:28:30 +02:00
|
|
|
|
int block_get_whole_disk(dev_t d, dev_t *ret) {
|
|
|
|
|
char *p, *s;
|
|
|
|
|
int r;
|
|
|
|
|
unsigned n, m;
|
|
|
|
|
|
|
|
|
|
assert(ret);
|
|
|
|
|
|
|
|
|
|
/* If it has a queue this is good enough for us */
|
|
|
|
|
if (asprintf(&p, "/sys/dev/block/%u:%u/queue", major(d), minor(d)) < 0)
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
|
|
r = access(p, F_OK);
|
|
|
|
|
free(p);
|
|
|
|
|
|
|
|
|
|
if (r >= 0) {
|
|
|
|
|
*ret = d;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* If it is a partition find the originating device */
|
|
|
|
|
if (asprintf(&p, "/sys/dev/block/%u:%u/partition", major(d), minor(d)) < 0)
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
|
|
r = access(p, F_OK);
|
|
|
|
|
free(p);
|
|
|
|
|
|
|
|
|
|
if (r < 0)
|
|
|
|
|
return -ENOENT;
|
|
|
|
|
|
|
|
|
|
/* Get parent dev_t */
|
|
|
|
|
if (asprintf(&p, "/sys/dev/block/%u:%u/../dev", major(d), minor(d)) < 0)
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
|
|
r = read_one_line_file(p, &s);
|
|
|
|
|
free(p);
|
|
|
|
|
|
|
|
|
|
if (r < 0)
|
|
|
|
|
return r;
|
|
|
|
|
|
|
|
|
|
r = sscanf(s, "%u:%u", &m, &n);
|
|
|
|
|
free(s);
|
|
|
|
|
|
|
|
|
|
if (r != 2)
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
|
|
/* Only return this if it is really good enough for us. */
|
|
|
|
|
if (asprintf(&p, "/sys/dev/block/%u:%u/queue", m, n) < 0)
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
|
|
r = access(p, F_OK);
|
|
|
|
|
free(p);
|
|
|
|
|
|
|
|
|
|
if (r >= 0) {
|
|
|
|
|
*ret = makedev(m, n);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return -ENOENT;
|
|
|
|
|
}
|
|
|
|
|
|
2011-05-23 23:54:22 +02:00
|
|
|
|
static const char *const ioprio_class_table[] = {
|
|
|
|
|
[IOPRIO_CLASS_NONE] = "none",
|
|
|
|
|
[IOPRIO_CLASS_RT] = "realtime",
|
|
|
|
|
[IOPRIO_CLASS_BE] = "best-effort",
|
|
|
|
|
[IOPRIO_CLASS_IDLE] = "idle"
|
|
|
|
|
};
|
|
|
|
|
|
2012-10-30 14:29:38 +01:00
|
|
|
|
DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(ioprio_class, int, INT_MAX);
|
2011-05-23 23:54:22 +02:00
|
|
|
|
|
|
|
|
|
static const char *const sigchld_code_table[] = {
|
|
|
|
|
[CLD_EXITED] = "exited",
|
|
|
|
|
[CLD_KILLED] = "killed",
|
|
|
|
|
[CLD_DUMPED] = "dumped",
|
|
|
|
|
[CLD_TRAPPED] = "trapped",
|
|
|
|
|
[CLD_STOPPED] = "stopped",
|
|
|
|
|
[CLD_CONTINUED] = "continued",
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
DEFINE_STRING_TABLE_LOOKUP(sigchld_code, int);
|
|
|
|
|
|
|
|
|
|
static const char *const log_facility_unshifted_table[LOG_NFACILITIES] = {
|
|
|
|
|
[LOG_FAC(LOG_KERN)] = "kern",
|
|
|
|
|
[LOG_FAC(LOG_USER)] = "user",
|
|
|
|
|
[LOG_FAC(LOG_MAIL)] = "mail",
|
|
|
|
|
[LOG_FAC(LOG_DAEMON)] = "daemon",
|
|
|
|
|
[LOG_FAC(LOG_AUTH)] = "auth",
|
|
|
|
|
[LOG_FAC(LOG_SYSLOG)] = "syslog",
|
|
|
|
|
[LOG_FAC(LOG_LPR)] = "lpr",
|
|
|
|
|
[LOG_FAC(LOG_NEWS)] = "news",
|
|
|
|
|
[LOG_FAC(LOG_UUCP)] = "uucp",
|
|
|
|
|
[LOG_FAC(LOG_CRON)] = "cron",
|
|
|
|
|
[LOG_FAC(LOG_AUTHPRIV)] = "authpriv",
|
|
|
|
|
[LOG_FAC(LOG_FTP)] = "ftp",
|
|
|
|
|
[LOG_FAC(LOG_LOCAL0)] = "local0",
|
|
|
|
|
[LOG_FAC(LOG_LOCAL1)] = "local1",
|
|
|
|
|
[LOG_FAC(LOG_LOCAL2)] = "local2",
|
|
|
|
|
[LOG_FAC(LOG_LOCAL3)] = "local3",
|
|
|
|
|
[LOG_FAC(LOG_LOCAL4)] = "local4",
|
|
|
|
|
[LOG_FAC(LOG_LOCAL5)] = "local5",
|
|
|
|
|
[LOG_FAC(LOG_LOCAL6)] = "local6",
|
|
|
|
|
[LOG_FAC(LOG_LOCAL7)] = "local7"
|
|
|
|
|
};
|
|
|
|
|
|
2012-10-30 14:29:38 +01:00
|
|
|
|
DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(log_facility_unshifted, int, LOG_FAC(~0));
|
2011-05-23 23:54:22 +02:00
|
|
|
|
|
2015-10-14 18:28:40 +02:00
|
|
|
|
bool log_facility_unshifted_is_valid(int facility) {
|
|
|
|
|
return facility >= 0 && facility <= LOG_FAC(~0);
|
|
|
|
|
}
|
|
|
|
|
|
2011-05-23 23:54:22 +02:00
|
|
|
|
static const char *const log_level_table[] = {
|
|
|
|
|
[LOG_EMERG] = "emerg",
|
|
|
|
|
[LOG_ALERT] = "alert",
|
|
|
|
|
[LOG_CRIT] = "crit",
|
|
|
|
|
[LOG_ERR] = "err",
|
|
|
|
|
[LOG_WARNING] = "warning",
|
|
|
|
|
[LOG_NOTICE] = "notice",
|
|
|
|
|
[LOG_INFO] = "info",
|
|
|
|
|
[LOG_DEBUG] = "debug"
|
|
|
|
|
};
|
|
|
|
|
|
2012-10-30 14:29:38 +01:00
|
|
|
|
DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(log_level, int, LOG_DEBUG);
|
2011-05-23 23:54:22 +02:00
|
|
|
|
|
2015-10-14 18:28:40 +02:00
|
|
|
|
bool log_level_is_valid(int level) {
|
|
|
|
|
return level >= 0 && level <= LOG_DEBUG;
|
|
|
|
|
}
|
|
|
|
|
|
2011-05-23 23:54:22 +02:00
|
|
|
|
static const char* const sched_policy_table[] = {
|
|
|
|
|
[SCHED_OTHER] = "other",
|
|
|
|
|
[SCHED_BATCH] = "batch",
|
|
|
|
|
[SCHED_IDLE] = "idle",
|
|
|
|
|
[SCHED_FIFO] = "fifo",
|
|
|
|
|
[SCHED_RR] = "rr"
|
|
|
|
|
};
|
|
|
|
|
|
2012-10-30 14:29:38 +01:00
|
|
|
|
DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(sched_policy, int, INT_MAX);
|
2011-05-23 23:54:22 +02:00
|
|
|
|
|
2011-08-22 14:58:50 +02:00
|
|
|
|
bool kexec_loaded(void) {
|
|
|
|
|
bool loaded = false;
|
|
|
|
|
char *s;
|
|
|
|
|
|
|
|
|
|
if (read_one_line_file("/sys/kernel/kexec_loaded", &s) >= 0) {
|
|
|
|
|
if (s[0] == '1')
|
|
|
|
|
loaded = true;
|
|
|
|
|
free(s);
|
|
|
|
|
}
|
|
|
|
|
return loaded;
|
|
|
|
|
}
|
2011-09-28 04:25:13 +02:00
|
|
|
|
|
2011-10-07 21:06:39 +02:00
|
|
|
|
int prot_from_flags(int flags) {
|
|
|
|
|
|
|
|
|
|
switch (flags & O_ACCMODE) {
|
|
|
|
|
|
|
|
|
|
case O_RDONLY:
|
|
|
|
|
return PROT_READ;
|
|
|
|
|
|
|
|
|
|
case O_WRONLY:
|
|
|
|
|
return PROT_WRITE;
|
|
|
|
|
|
|
|
|
|
case O_RDWR:
|
|
|
|
|
return PROT_READ|PROT_WRITE;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
2011-10-12 04:42:38 +02:00
|
|
|
|
}
|
2011-10-12 04:29:11 +02:00
|
|
|
|
|
2012-01-05 20:11:47 +01:00
|
|
|
|
void* memdup(const void *p, size_t l) {
|
|
|
|
|
void *r;
|
|
|
|
|
|
|
|
|
|
assert(p);
|
|
|
|
|
|
|
|
|
|
r = malloc(l);
|
|
|
|
|
if (!r)
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
memcpy(r, p, l);
|
|
|
|
|
return r;
|
|
|
|
|
}
|
2012-01-27 18:57:37 +01:00
|
|
|
|
|
2012-04-11 22:37:13 +02:00
|
|
|
|
int fork_agent(pid_t *pid, const int except[], unsigned n_except, const char *path, ...) {
|
2012-04-11 18:50:16 +02:00
|
|
|
|
bool stdout_is_tty, stderr_is_tty;
|
2014-08-27 21:42:20 +02:00
|
|
|
|
pid_t parent_pid, agent_pid;
|
|
|
|
|
sigset_t ss, saved_ss;
|
2012-04-11 18:50:16 +02:00
|
|
|
|
unsigned n, i;
|
|
|
|
|
va_list ap;
|
|
|
|
|
char **l;
|
|
|
|
|
|
|
|
|
|
assert(pid);
|
|
|
|
|
assert(path);
|
|
|
|
|
|
|
|
|
|
/* Spawns a temporary TTY agent, making sure it goes away when
|
|
|
|
|
* we go away */
|
|
|
|
|
|
2014-08-27 21:42:20 +02:00
|
|
|
|
parent_pid = getpid();
|
|
|
|
|
|
|
|
|
|
/* First we temporarily block all signals, so that the new
|
|
|
|
|
* child has them blocked initially. This way, we can be sure
|
|
|
|
|
* that SIGTERMs are not lost we might send to the agent. */
|
|
|
|
|
assert_se(sigfillset(&ss) >= 0);
|
|
|
|
|
assert_se(sigprocmask(SIG_SETMASK, &ss, &saved_ss) >= 0);
|
|
|
|
|
|
2012-04-11 18:50:16 +02:00
|
|
|
|
agent_pid = fork();
|
2014-08-27 21:42:20 +02:00
|
|
|
|
if (agent_pid < 0) {
|
|
|
|
|
assert_se(sigprocmask(SIG_SETMASK, &saved_ss, NULL) >= 0);
|
2012-04-11 18:50:16 +02:00
|
|
|
|
return -errno;
|
2014-08-27 21:42:20 +02:00
|
|
|
|
}
|
2012-04-11 18:50:16 +02:00
|
|
|
|
|
|
|
|
|
if (agent_pid != 0) {
|
2014-08-27 21:42:20 +02:00
|
|
|
|
assert_se(sigprocmask(SIG_SETMASK, &saved_ss, NULL) >= 0);
|
2012-04-11 18:50:16 +02:00
|
|
|
|
*pid = agent_pid;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* In the child:
|
|
|
|
|
*
|
|
|
|
|
* Make sure the agent goes away when the parent dies */
|
|
|
|
|
if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0)
|
|
|
|
|
_exit(EXIT_FAILURE);
|
|
|
|
|
|
2014-08-27 21:42:20 +02:00
|
|
|
|
/* Make sure we actually can kill the agent, if we need to, in
|
|
|
|
|
* case somebody invoked us from a shell script that trapped
|
|
|
|
|
* SIGTERM or so... */
|
2015-05-31 23:55:55 +02:00
|
|
|
|
(void) reset_all_signal_handlers();
|
|
|
|
|
(void) reset_signal_mask();
|
2014-08-27 21:42:20 +02:00
|
|
|
|
|
2012-04-11 18:50:16 +02:00
|
|
|
|
/* Check whether our parent died before we were able
|
2014-08-27 21:42:20 +02:00
|
|
|
|
* to set the death signal and unblock the signals */
|
2012-04-11 18:50:16 +02:00
|
|
|
|
if (getppid() != parent_pid)
|
|
|
|
|
_exit(EXIT_SUCCESS);
|
|
|
|
|
|
|
|
|
|
/* Don't leak fds to the agent */
|
2012-04-11 22:37:13 +02:00
|
|
|
|
close_all_fds(except, n_except);
|
2012-04-11 18:50:16 +02:00
|
|
|
|
|
|
|
|
|
stdout_is_tty = isatty(STDOUT_FILENO);
|
|
|
|
|
stderr_is_tty = isatty(STDERR_FILENO);
|
|
|
|
|
|
|
|
|
|
if (!stdout_is_tty || !stderr_is_tty) {
|
2014-08-27 21:42:20 +02:00
|
|
|
|
int fd;
|
|
|
|
|
|
2012-04-11 18:50:16 +02:00
|
|
|
|
/* Detach from stdout/stderr. and reopen
|
|
|
|
|
* /dev/tty for them. This is important to
|
|
|
|
|
* ensure that when systemctl is started via
|
|
|
|
|
* popen() or a similar call that expects to
|
|
|
|
|
* read EOF we actually do generate EOF and
|
|
|
|
|
* not delay this indefinitely by because we
|
|
|
|
|
* keep an unused copy of stdin around. */
|
|
|
|
|
fd = open("/dev/tty", O_WRONLY);
|
|
|
|
|
if (fd < 0) {
|
2014-11-28 19:29:59 +01:00
|
|
|
|
log_error_errno(errno, "Failed to open /dev/tty: %m");
|
2012-04-11 18:50:16 +02:00
|
|
|
|
_exit(EXIT_FAILURE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!stdout_is_tty)
|
|
|
|
|
dup2(fd, STDOUT_FILENO);
|
|
|
|
|
|
|
|
|
|
if (!stderr_is_tty)
|
|
|
|
|
dup2(fd, STDERR_FILENO);
|
|
|
|
|
|
|
|
|
|
if (fd > 2)
|
|
|
|
|
close(fd);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Count arguments */
|
|
|
|
|
va_start(ap, path);
|
|
|
|
|
for (n = 0; va_arg(ap, char*); n++)
|
|
|
|
|
;
|
|
|
|
|
va_end(ap);
|
|
|
|
|
|
|
|
|
|
/* Allocate strv */
|
|
|
|
|
l = alloca(sizeof(char *) * (n + 1));
|
|
|
|
|
|
|
|
|
|
/* Fill in arguments */
|
|
|
|
|
va_start(ap, path);
|
|
|
|
|
for (i = 0; i <= n; i++)
|
|
|
|
|
l[i] = va_arg(ap, char*);
|
|
|
|
|
va_end(ap);
|
|
|
|
|
|
|
|
|
|
execv(path, l);
|
|
|
|
|
_exit(EXIT_FAILURE);
|
|
|
|
|
}
|
2012-04-12 03:38:52 +02:00
|
|
|
|
|
2015-01-22 03:57:15 +01:00
|
|
|
|
bool http_etag_is_valid(const char *etag) {
|
|
|
|
|
if (isempty(etag))
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
if (!endswith(etag, "\""))
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
if (!startswith(etag, "\"") && !startswith(etag, "W/\""))
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2015-01-19 20:45:27 +01:00
|
|
|
|
bool http_url_is_valid(const char *url) {
|
|
|
|
|
const char *p;
|
2012-05-21 15:12:18 +02:00
|
|
|
|
|
2015-01-19 20:45:27 +01:00
|
|
|
|
if (isempty(url))
|
|
|
|
|
return false;
|
2012-05-21 15:12:18 +02:00
|
|
|
|
|
2015-01-19 20:45:27 +01:00
|
|
|
|
p = startswith(url, "http://");
|
|
|
|
|
if (!p)
|
|
|
|
|
p = startswith(url, "https://");
|
|
|
|
|
if (!p)
|
|
|
|
|
return false;
|
2012-05-21 15:12:18 +02:00
|
|
|
|
|
2015-01-19 20:45:27 +01:00
|
|
|
|
if (isempty(p))
|
|
|
|
|
return false;
|
2012-05-21 15:12:18 +02:00
|
|
|
|
|
2015-01-19 20:45:27 +01:00
|
|
|
|
return ascii_is_valid(p);
|
|
|
|
|
}
|
2012-05-21 15:12:18 +02:00
|
|
|
|
|
2015-01-19 20:45:27 +01:00
|
|
|
|
bool documentation_url_is_valid(const char *url) {
|
|
|
|
|
const char *p;
|
|
|
|
|
|
|
|
|
|
if (isempty(url))
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
if (http_url_is_valid(url))
|
2012-05-21 15:12:18 +02:00
|
|
|
|
return true;
|
|
|
|
|
|
2015-01-19 20:45:27 +01:00
|
|
|
|
p = startswith(url, "file:/");
|
|
|
|
|
if (!p)
|
|
|
|
|
p = startswith(url, "info:");
|
|
|
|
|
if (!p)
|
|
|
|
|
p = startswith(url, "man:");
|
|
|
|
|
|
|
|
|
|
if (isempty(p))
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
return ascii_is_valid(p);
|
2012-05-21 15:12:18 +02:00
|
|
|
|
}
|
2012-05-16 14:22:40 +02:00
|
|
|
|
|
|
|
|
|
bool in_initrd(void) {
|
2013-12-16 01:56:21 +01:00
|
|
|
|
static int saved = -1;
|
2012-07-10 18:46:26 +02:00
|
|
|
|
struct statfs s;
|
2012-05-21 20:00:58 +02:00
|
|
|
|
|
2012-07-10 18:46:26 +02:00
|
|
|
|
if (saved >= 0)
|
|
|
|
|
return saved;
|
|
|
|
|
|
|
|
|
|
/* We make two checks here:
|
|
|
|
|
*
|
|
|
|
|
* 1. the flag file /etc/initrd-release must exist
|
|
|
|
|
* 2. the root file system must be a memory file system
|
|
|
|
|
*
|
|
|
|
|
* The second check is extra paranoia, since misdetecting an
|
|
|
|
|
* initrd can have bad bad consequences due the initrd
|
|
|
|
|
* emptying when transititioning to the main systemd.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
saved = access("/etc/initrd-release", F_OK) >= 0 &&
|
|
|
|
|
statfs("/", &s) >= 0 &&
|
2012-11-16 17:17:21 +01:00
|
|
|
|
is_temporary_fs(&s);
|
2012-05-16 14:22:40 +02:00
|
|
|
|
|
2012-05-21 20:00:58 +02:00
|
|
|
|
return saved;
|
2012-05-16 14:22:40 +02:00
|
|
|
|
}
|
2012-05-30 15:01:51 +02:00
|
|
|
|
|
2012-10-03 19:29:20 +02:00
|
|
|
|
bool string_is_safe(const char *p) {
|
|
|
|
|
const char *t;
|
|
|
|
|
|
2014-07-07 12:04:55 +02:00
|
|
|
|
if (!p)
|
|
|
|
|
return false;
|
2012-10-03 19:29:20 +02:00
|
|
|
|
|
|
|
|
|
for (t = p; *t; t++) {
|
2012-10-17 21:24:14 +02:00
|
|
|
|
if (*t > 0 && *t < ' ')
|
2012-10-03 19:29:20 +02:00
|
|
|
|
return false;
|
|
|
|
|
|
2015-06-28 19:23:00 +02:00
|
|
|
|
if (strchr("\\\"\'\x7f", *t))
|
2012-10-03 19:29:20 +02:00
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2012-10-11 16:42:46 +02:00
|
|
|
|
|
2012-10-22 14:31:46 +02:00
|
|
|
|
/* hey glibc, APIs with callbacks without a user pointer are so useless */
|
|
|
|
|
void *xbsearch_r(const void *key, const void *base, size_t nmemb, size_t size,
|
2012-10-25 21:40:01 +02:00
|
|
|
|
int (*compar) (const void *, const void *, void *), void *arg) {
|
2012-10-22 14:31:46 +02:00
|
|
|
|
size_t l, u, idx;
|
|
|
|
|
const void *p;
|
|
|
|
|
int comparison;
|
|
|
|
|
|
|
|
|
|
l = 0;
|
|
|
|
|
u = nmemb;
|
|
|
|
|
while (l < u) {
|
|
|
|
|
idx = (l + u) / 2;
|
|
|
|
|
p = (void *)(((const char *) base) + (idx * size));
|
|
|
|
|
comparison = compar(key, p, arg);
|
|
|
|
|
if (comparison < 0)
|
|
|
|
|
u = idx;
|
|
|
|
|
else if (comparison > 0)
|
|
|
|
|
l = idx + 1;
|
|
|
|
|
else
|
|
|
|
|
return (void *)p;
|
|
|
|
|
}
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
2012-11-02 17:27:15 +01:00
|
|
|
|
|
2015-01-29 16:12:58 +01:00
|
|
|
|
void init_gettext(void) {
|
|
|
|
|
setlocale(LC_ALL, "");
|
|
|
|
|
textdomain(GETTEXT_PACKAGE);
|
|
|
|
|
}
|
|
|
|
|
|
2012-11-02 17:27:15 +01:00
|
|
|
|
bool is_locale_utf8(void) {
|
|
|
|
|
const char *set;
|
|
|
|
|
static int cached_answer = -1;
|
|
|
|
|
|
|
|
|
|
if (cached_answer >= 0)
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
if (!setlocale(LC_ALL, "")) {
|
|
|
|
|
cached_answer = true;
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
set = nl_langinfo(CODESET);
|
|
|
|
|
if (!set) {
|
|
|
|
|
cached_answer = true;
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
2013-12-03 22:27:45 +01:00
|
|
|
|
if (streq(set, "UTF-8")) {
|
2013-04-15 18:34:53 +02:00
|
|
|
|
cached_answer = true;
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
2013-06-27 11:26:36 +02:00
|
|
|
|
/* For LC_CTYPE=="C" return true, because CTYPE is effectly
|
|
|
|
|
* unset and everything can do to UTF-8 nowadays. */
|
2013-04-15 18:34:53 +02:00
|
|
|
|
set = setlocale(LC_CTYPE, NULL);
|
|
|
|
|
if (!set) {
|
|
|
|
|
cached_answer = true;
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
2013-06-27 11:26:36 +02:00
|
|
|
|
/* Check result, but ignore the result if C was set
|
|
|
|
|
* explicitly. */
|
|
|
|
|
cached_answer =
|
2015-08-28 18:22:14 +02:00
|
|
|
|
STR_IN_SET(set, "C", "POSIX") &&
|
2013-06-27 11:26:36 +02:00
|
|
|
|
!getenv("LC_ALL") &&
|
|
|
|
|
!getenv("LC_CTYPE") &&
|
|
|
|
|
!getenv("LANG");
|
2013-04-15 18:34:53 +02:00
|
|
|
|
|
2012-11-02 17:27:15 +01:00
|
|
|
|
out:
|
2013-06-27 11:26:36 +02:00
|
|
|
|
return (bool) cached_answer;
|
2012-11-02 17:27:15 +01:00
|
|
|
|
}
|
2012-11-02 17:35:30 +01:00
|
|
|
|
|
|
|
|
|
const char *draw_special_char(DrawSpecialChar ch) {
|
|
|
|
|
static const char *draw_table[2][_DRAW_SPECIAL_CHAR_MAX] = {
|
2014-04-23 19:05:46 +02:00
|
|
|
|
|
2012-11-02 17:35:30 +01:00
|
|
|
|
/* UTF-8 */ {
|
2014-04-23 19:05:46 +02:00
|
|
|
|
[DRAW_TREE_VERTICAL] = "\342\224\202 ", /* │ */
|
2012-11-12 22:27:48 +01:00
|
|
|
|
[DRAW_TREE_BRANCH] = "\342\224\234\342\224\200", /* ├─ */
|
|
|
|
|
[DRAW_TREE_RIGHT] = "\342\224\224\342\224\200", /* └─ */
|
2013-01-17 21:34:11 +01:00
|
|
|
|
[DRAW_TREE_SPACE] = " ", /* */
|
2014-04-23 19:05:46 +02:00
|
|
|
|
[DRAW_TRIANGULAR_BULLET] = "\342\200\243", /* ‣ */
|
|
|
|
|
[DRAW_BLACK_CIRCLE] = "\342\227\217", /* ● */
|
|
|
|
|
[DRAW_ARROW] = "\342\206\222", /* → */
|
2014-06-10 17:56:17 +02:00
|
|
|
|
[DRAW_DASH] = "\342\200\223", /* – */
|
2012-11-02 17:35:30 +01:00
|
|
|
|
},
|
2014-04-23 19:05:46 +02:00
|
|
|
|
|
2012-11-02 17:35:30 +01:00
|
|
|
|
/* ASCII fallback */ {
|
2014-04-23 19:05:46 +02:00
|
|
|
|
[DRAW_TREE_VERTICAL] = "| ",
|
2012-11-12 22:27:48 +01:00
|
|
|
|
[DRAW_TREE_BRANCH] = "|-",
|
|
|
|
|
[DRAW_TREE_RIGHT] = "`-",
|
2013-01-17 21:34:11 +01:00
|
|
|
|
[DRAW_TREE_SPACE] = " ",
|
2014-04-23 19:05:46 +02:00
|
|
|
|
[DRAW_TRIANGULAR_BULLET] = ">",
|
|
|
|
|
[DRAW_BLACK_CIRCLE] = "*",
|
|
|
|
|
[DRAW_ARROW] = "->",
|
2014-06-10 17:56:17 +02:00
|
|
|
|
[DRAW_DASH] = "-",
|
2012-11-02 17:35:30 +01:00
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return draw_table[!is_locale_utf8()][ch];
|
|
|
|
|
}
|
2012-11-14 22:16:23 +01:00
|
|
|
|
|
2012-12-25 16:29:51 +01:00
|
|
|
|
int on_ac_power(void) {
|
|
|
|
|
bool found_offline = false, found_online = false;
|
|
|
|
|
_cleanup_closedir_ DIR *d = NULL;
|
|
|
|
|
|
|
|
|
|
d = opendir("/sys/class/power_supply");
|
|
|
|
|
if (!d)
|
2015-03-04 01:07:28 +01:00
|
|
|
|
return errno == ENOENT ? true : -errno;
|
2012-12-25 16:29:51 +01:00
|
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
|
struct dirent *de;
|
|
|
|
|
_cleanup_close_ int fd = -1, device = -1;
|
|
|
|
|
char contents[6];
|
|
|
|
|
ssize_t n;
|
|
|
|
|
|
2013-12-19 12:05:41 +01:00
|
|
|
|
errno = 0;
|
|
|
|
|
de = readdir(d);
|
|
|
|
|
if (!de && errno != 0)
|
|
|
|
|
return -errno;
|
2012-12-25 16:29:51 +01:00
|
|
|
|
|
|
|
|
|
if (!de)
|
|
|
|
|
break;
|
|
|
|
|
|
2014-12-19 20:03:36 +01:00
|
|
|
|
if (hidden_file(de->d_name))
|
2012-12-25 16:29:51 +01:00
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
device = openat(dirfd(d), de->d_name, O_DIRECTORY|O_RDONLY|O_CLOEXEC|O_NOCTTY);
|
|
|
|
|
if (device < 0) {
|
|
|
|
|
if (errno == ENOENT || errno == ENOTDIR)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
return -errno;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fd = openat(device, "type", O_RDONLY|O_CLOEXEC|O_NOCTTY);
|
|
|
|
|
if (fd < 0) {
|
|
|
|
|
if (errno == ENOENT)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
return -errno;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
n = read(fd, contents, sizeof(contents));
|
|
|
|
|
if (n < 0)
|
|
|
|
|
return -errno;
|
|
|
|
|
|
|
|
|
|
if (n != 6 || memcmp(contents, "Mains\n", 6))
|
|
|
|
|
continue;
|
|
|
|
|
|
2014-03-18 19:22:43 +01:00
|
|
|
|
safe_close(fd);
|
2012-12-25 16:29:51 +01:00
|
|
|
|
fd = openat(device, "online", O_RDONLY|O_CLOEXEC|O_NOCTTY);
|
|
|
|
|
if (fd < 0) {
|
|
|
|
|
if (errno == ENOENT)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
return -errno;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
n = read(fd, contents, sizeof(contents));
|
|
|
|
|
if (n < 0)
|
|
|
|
|
return -errno;
|
|
|
|
|
|
|
|
|
|
if (n != 2 || contents[1] != '\n')
|
|
|
|
|
return -EIO;
|
|
|
|
|
|
|
|
|
|
if (contents[0] == '1') {
|
|
|
|
|
found_online = true;
|
|
|
|
|
break;
|
|
|
|
|
} else if (contents[0] == '0')
|
|
|
|
|
found_offline = true;
|
|
|
|
|
else
|
|
|
|
|
return -EIO;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return found_online || !found_offline;
|
|
|
|
|
}
|
2013-02-11 23:48:36 +01:00
|
|
|
|
|
2014-04-10 15:48:59 +02:00
|
|
|
|
void* greedy_realloc(void **p, size_t *allocated, size_t need, size_t size) {
|
|
|
|
|
size_t a, newalloc;
|
2013-03-31 16:16:37 +02:00
|
|
|
|
void *q;
|
|
|
|
|
|
2013-12-10 19:53:03 +01:00
|
|
|
|
assert(p);
|
2013-12-01 01:09:26 +01:00
|
|
|
|
assert(allocated);
|
|
|
|
|
|
2013-03-31 16:16:37 +02:00
|
|
|
|
if (*allocated >= need)
|
|
|
|
|
return *p;
|
|
|
|
|
|
2014-04-10 15:48:59 +02:00
|
|
|
|
newalloc = MAX(need * 2, 64u / size);
|
|
|
|
|
a = newalloc * size;
|
2013-12-10 19:53:03 +01:00
|
|
|
|
|
|
|
|
|
/* check for overflows */
|
2014-04-10 15:48:59 +02:00
|
|
|
|
if (a < size * need)
|
2013-12-10 19:53:03 +01:00
|
|
|
|
return NULL;
|
|
|
|
|
|
2013-03-31 16:16:37 +02:00
|
|
|
|
q = realloc(*p, a);
|
|
|
|
|
if (!q)
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
*p = q;
|
2014-04-10 15:48:59 +02:00
|
|
|
|
*allocated = newalloc;
|
2013-03-31 16:16:37 +02:00
|
|
|
|
return q;
|
|
|
|
|
}
|
2013-04-29 23:39:12 +02:00
|
|
|
|
|
2014-04-10 15:48:59 +02:00
|
|
|
|
void* greedy_realloc0(void **p, size_t *allocated, size_t need, size_t size) {
|
2013-12-10 19:53:03 +01:00
|
|
|
|
size_t prev;
|
2013-11-30 11:31:59 +01:00
|
|
|
|
uint8_t *q;
|
|
|
|
|
|
2013-12-10 19:53:03 +01:00
|
|
|
|
assert(p);
|
|
|
|
|
assert(allocated);
|
|
|
|
|
|
|
|
|
|
prev = *allocated;
|
|
|
|
|
|
2014-04-10 15:48:59 +02:00
|
|
|
|
q = greedy_realloc(p, allocated, need, size);
|
2013-11-30 11:31:59 +01:00
|
|
|
|
if (!q)
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
if (*allocated > prev)
|
2014-04-10 15:48:59 +02:00
|
|
|
|
memzero(q + prev * size, (*allocated - prev) * size);
|
2013-11-30 11:31:59 +01:00
|
|
|
|
|
|
|
|
|
return q;
|
|
|
|
|
}
|
|
|
|
|
|
2013-04-29 23:39:12 +02:00
|
|
|
|
bool id128_is_valid(const char *s) {
|
|
|
|
|
size_t i, l;
|
|
|
|
|
|
|
|
|
|
l = strlen(s);
|
|
|
|
|
if (l == 32) {
|
|
|
|
|
|
|
|
|
|
/* Simple formatted 128bit hex string */
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < l; i++) {
|
|
|
|
|
char c = s[i];
|
|
|
|
|
|
|
|
|
|
if (!(c >= '0' && c <= '9') &&
|
|
|
|
|
!(c >= 'a' && c <= 'z') &&
|
|
|
|
|
!(c >= 'A' && c <= 'Z'))
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} else if (l == 36) {
|
|
|
|
|
|
|
|
|
|
/* Formatted UUID */
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < l; i++) {
|
|
|
|
|
char c = s[i];
|
|
|
|
|
|
|
|
|
|
if ((i == 8 || i == 13 || i == 18 || i == 23)) {
|
|
|
|
|
if (c != '-')
|
|
|
|
|
return false;
|
|
|
|
|
} else {
|
|
|
|
|
if (!(c >= '0' && c <= '9') &&
|
|
|
|
|
!(c >= 'a' && c <= 'z') &&
|
|
|
|
|
!(c >= 'A' && c <= 'Z'))
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} else
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2013-06-09 22:54:39 +02:00
|
|
|
|
|
2013-11-06 03:15:16 +01:00
|
|
|
|
int shall_restore_state(void) {
|
2014-11-27 21:28:13 +01:00
|
|
|
|
_cleanup_free_ char *value = NULL;
|
2013-11-06 03:15:16 +01:00
|
|
|
|
int r;
|
2013-10-19 00:46:07 +02:00
|
|
|
|
|
2014-11-27 21:28:13 +01:00
|
|
|
|
r = get_proc_cmdline_key("systemd.restore_state=", &value);
|
2013-11-06 03:15:16 +01:00
|
|
|
|
if (r < 0)
|
|
|
|
|
return r;
|
2014-11-27 21:28:13 +01:00
|
|
|
|
if (r == 0)
|
|
|
|
|
return true;
|
2013-10-19 00:46:07 +02:00
|
|
|
|
|
2014-11-27 21:28:13 +01:00
|
|
|
|
return parse_boolean(value) != 0;
|
2013-11-06 03:15:16 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int proc_cmdline(char **ret) {
|
2014-11-06 21:53:34 +01:00
|
|
|
|
assert(ret);
|
2013-10-19 00:46:07 +02:00
|
|
|
|
|
2015-09-07 13:42:47 +02:00
|
|
|
|
if (detect_container() > 0)
|
2014-11-06 21:53:34 +01:00
|
|
|
|
return get_process_cmdline(1, 0, false, ret);
|
|
|
|
|
else
|
|
|
|
|
return read_one_line_file("/proc/cmdline", ret);
|
2013-10-19 00:46:07 +02:00
|
|
|
|
}
|
2013-12-13 22:02:47 +01:00
|
|
|
|
|
2014-03-06 17:05:55 +01:00
|
|
|
|
int parse_proc_cmdline(int (*parse_item)(const char *key, const char *value)) {
|
2014-02-16 00:08:59 +01:00
|
|
|
|
_cleanup_free_ char *line = NULL;
|
2014-11-07 00:10:24 +01:00
|
|
|
|
const char *p;
|
2014-02-16 00:08:59 +01:00
|
|
|
|
int r;
|
|
|
|
|
|
2014-03-06 17:05:55 +01:00
|
|
|
|
assert(parse_item);
|
|
|
|
|
|
2014-02-16 00:08:59 +01:00
|
|
|
|
r = proc_cmdline(&line);
|
|
|
|
|
if (r < 0)
|
2014-11-06 21:53:34 +01:00
|
|
|
|
return r;
|
2014-02-16 00:08:59 +01:00
|
|
|
|
|
2014-11-07 00:10:24 +01:00
|
|
|
|
p = line;
|
|
|
|
|
for (;;) {
|
|
|
|
|
_cleanup_free_ char *word = NULL;
|
|
|
|
|
char *value = NULL;
|
2014-02-16 00:08:59 +01:00
|
|
|
|
|
2015-06-23 18:26:49 +02:00
|
|
|
|
r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES|EXTRACT_RELAX);
|
2014-11-07 00:10:24 +01:00
|
|
|
|
if (r < 0)
|
|
|
|
|
return r;
|
|
|
|
|
if (r == 0)
|
|
|
|
|
break;
|
2014-02-16 00:08:59 +01:00
|
|
|
|
|
2014-03-06 17:05:55 +01:00
|
|
|
|
/* Filter out arguments that are intended only for the
|
|
|
|
|
* initrd */
|
|
|
|
|
if (!in_initrd() && startswith(word, "rd."))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
value = strchr(word, '=');
|
|
|
|
|
if (value)
|
|
|
|
|
*(value++) = 0;
|
|
|
|
|
|
|
|
|
|
r = parse_item(word, value);
|
|
|
|
|
if (r < 0)
|
2014-02-16 00:08:59 +01:00
|
|
|
|
return r;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2014-11-27 21:28:13 +01:00
|
|
|
|
int get_proc_cmdline_key(const char *key, char **value) {
|
|
|
|
|
_cleanup_free_ char *line = NULL, *ret = NULL;
|
|
|
|
|
bool found = false;
|
|
|
|
|
const char *p;
|
|
|
|
|
int r;
|
|
|
|
|
|
|
|
|
|
assert(key);
|
|
|
|
|
|
|
|
|
|
r = proc_cmdline(&line);
|
|
|
|
|
if (r < 0)
|
|
|
|
|
return r;
|
|
|
|
|
|
|
|
|
|
p = line;
|
|
|
|
|
for (;;) {
|
|
|
|
|
_cleanup_free_ char *word = NULL;
|
|
|
|
|
const char *e;
|
|
|
|
|
|
2015-06-23 18:26:49 +02:00
|
|
|
|
r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES|EXTRACT_RELAX);
|
2014-11-27 21:28:13 +01:00
|
|
|
|
if (r < 0)
|
|
|
|
|
return r;
|
|
|
|
|
if (r == 0)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
/* Filter out arguments that are intended only for the
|
|
|
|
|
* initrd */
|
|
|
|
|
if (!in_initrd() && startswith(word, "rd."))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if (value) {
|
|
|
|
|
e = startswith(word, key);
|
|
|
|
|
if (!e)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
r = free_and_strdup(&ret, e);
|
|
|
|
|
if (r < 0)
|
|
|
|
|
return r;
|
|
|
|
|
|
|
|
|
|
found = true;
|
|
|
|
|
} else {
|
|
|
|
|
if (streq(word, key))
|
|
|
|
|
found = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (value) {
|
|
|
|
|
*value = ret;
|
|
|
|
|
ret = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return found;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2013-12-13 22:02:47 +01:00
|
|
|
|
int container_get_leader(const char *machine, pid_t *pid) {
|
|
|
|
|
_cleanup_free_ char *s = NULL, *class = NULL;
|
|
|
|
|
const char *p;
|
|
|
|
|
pid_t leader;
|
|
|
|
|
int r;
|
|
|
|
|
|
|
|
|
|
assert(machine);
|
|
|
|
|
assert(pid);
|
|
|
|
|
|
2015-08-23 14:33:50 +02:00
|
|
|
|
if (!machine_name_is_valid(machine))
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
2015-02-03 02:05:59 +01:00
|
|
|
|
p = strjoina("/run/systemd/machines/", machine);
|
2013-12-13 22:02:47 +01:00
|
|
|
|
r = parse_env_file(p, NEWLINE, "LEADER", &s, "CLASS", &class, NULL);
|
|
|
|
|
if (r == -ENOENT)
|
|
|
|
|
return -EHOSTDOWN;
|
|
|
|
|
if (r < 0)
|
|
|
|
|
return r;
|
|
|
|
|
if (!s)
|
|
|
|
|
return -EIO;
|
|
|
|
|
|
|
|
|
|
if (!streq_ptr(class, "container"))
|
|
|
|
|
return -EIO;
|
|
|
|
|
|
|
|
|
|
r = parse_pid(s, &leader);
|
|
|
|
|
if (r < 0)
|
|
|
|
|
return r;
|
|
|
|
|
if (leader <= 1)
|
|
|
|
|
return -EIO;
|
|
|
|
|
|
|
|
|
|
*pid = leader;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
namespace helpers: Allow entering a UID namespace
To be able to use `systemd-run` or `machinectl login` on a container
that is in a private user namespace, the sub-process must have entered
the user namespace before connecting to the container's D-Bus, otherwise
the UID and GID in the peer credentials are garbage.
So we extend namespace_open and namespace_enter to support UID namespaces,
and we enter the UID namespace in bus_container_connect_{socket,kernel}.
namespace_open will degrade to a no-op if user namespaces are not enabled
in the kernel.
Special handling is required for the setns call in namespace_enter with
a user namespace, since transitioning to your own namespace is forbidden,
as it would result in re-entering your user namespace as root.
Arguably it may be valid to check this at the call site, rather than
inside namespace_enter, but it is less code to do it inside, and if the
intention of calling namespace_enter is to *be* in the target namespace,
rather than to transition to the target namespace, it is a reasonable
approach.
The check for whether the user namespace is the same must happen before
entering namespaces, as we may not be able to access /proc during the
intermediate transition stage.
We can't instead attempt to enter the user namespace and then ignore
the failure from it being the same namespace, since the error code is
not distinct, and we can't compare namespaces while mid-transition.
2015-08-17 10:52:13 +02:00
|
|
|
|
int namespace_open(pid_t pid, int *pidns_fd, int *mntns_fd, int *netns_fd, int *userns_fd, int *root_fd) {
|
|
|
|
|
_cleanup_close_ int pidnsfd = -1, mntnsfd = -1, netnsfd = -1, usernsfd = -1;
|
2014-05-21 10:44:45 +02:00
|
|
|
|
int rfd = -1;
|
2013-12-13 22:02:47 +01:00
|
|
|
|
|
|
|
|
|
assert(pid >= 0);
|
|
|
|
|
|
2014-05-18 13:48:53 +02:00
|
|
|
|
if (mntns_fd) {
|
|
|
|
|
const char *mntns;
|
2013-12-17 01:03:09 +01:00
|
|
|
|
|
2014-05-18 13:48:53 +02:00
|
|
|
|
mntns = procfs_file_alloca(pid, "ns/mnt");
|
|
|
|
|
mntnsfd = open(mntns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
|
|
|
|
|
if (mntnsfd < 0)
|
|
|
|
|
return -errno;
|
|
|
|
|
}
|
2013-12-13 22:02:47 +01:00
|
|
|
|
|
2014-05-18 13:48:53 +02:00
|
|
|
|
if (pidns_fd) {
|
|
|
|
|
const char *pidns;
|
|
|
|
|
|
|
|
|
|
pidns = procfs_file_alloca(pid, "ns/pid");
|
|
|
|
|
pidnsfd = open(pidns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
|
|
|
|
|
if (pidnsfd < 0)
|
|
|
|
|
return -errno;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (netns_fd) {
|
|
|
|
|
const char *netns;
|
|
|
|
|
|
|
|
|
|
netns = procfs_file_alloca(pid, "ns/net");
|
|
|
|
|
netnsfd = open(netns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
|
|
|
|
|
if (netnsfd < 0)
|
|
|
|
|
return -errno;
|
|
|
|
|
}
|
|
|
|
|
|
namespace helpers: Allow entering a UID namespace
To be able to use `systemd-run` or `machinectl login` on a container
that is in a private user namespace, the sub-process must have entered
the user namespace before connecting to the container's D-Bus, otherwise
the UID and GID in the peer credentials are garbage.
So we extend namespace_open and namespace_enter to support UID namespaces,
and we enter the UID namespace in bus_container_connect_{socket,kernel}.
namespace_open will degrade to a no-op if user namespaces are not enabled
in the kernel.
Special handling is required for the setns call in namespace_enter with
a user namespace, since transitioning to your own namespace is forbidden,
as it would result in re-entering your user namespace as root.
Arguably it may be valid to check this at the call site, rather than
inside namespace_enter, but it is less code to do it inside, and if the
intention of calling namespace_enter is to *be* in the target namespace,
rather than to transition to the target namespace, it is a reasonable
approach.
The check for whether the user namespace is the same must happen before
entering namespaces, as we may not be able to access /proc during the
intermediate transition stage.
We can't instead attempt to enter the user namespace and then ignore
the failure from it being the same namespace, since the error code is
not distinct, and we can't compare namespaces while mid-transition.
2015-08-17 10:52:13 +02:00
|
|
|
|
if (userns_fd) {
|
|
|
|
|
const char *userns;
|
|
|
|
|
|
|
|
|
|
userns = procfs_file_alloca(pid, "ns/user");
|
|
|
|
|
usernsfd = open(userns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
|
|
|
|
|
if (usernsfd < 0 && errno != ENOENT)
|
|
|
|
|
return -errno;
|
|
|
|
|
}
|
|
|
|
|
|
2014-05-18 13:48:53 +02:00
|
|
|
|
if (root_fd) {
|
|
|
|
|
const char *root;
|
|
|
|
|
|
|
|
|
|
root = procfs_file_alloca(pid, "root");
|
|
|
|
|
rfd = open(root, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
|
|
|
|
|
if (rfd < 0)
|
|
|
|
|
return -errno;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (pidns_fd)
|
|
|
|
|
*pidns_fd = pidnsfd;
|
2013-12-13 22:02:47 +01:00
|
|
|
|
|
2014-05-18 13:48:53 +02:00
|
|
|
|
if (mntns_fd)
|
|
|
|
|
*mntns_fd = mntnsfd;
|
|
|
|
|
|
|
|
|
|
if (netns_fd)
|
|
|
|
|
*netns_fd = netnsfd;
|
|
|
|
|
|
namespace helpers: Allow entering a UID namespace
To be able to use `systemd-run` or `machinectl login` on a container
that is in a private user namespace, the sub-process must have entered
the user namespace before connecting to the container's D-Bus, otherwise
the UID and GID in the peer credentials are garbage.
So we extend namespace_open and namespace_enter to support UID namespaces,
and we enter the UID namespace in bus_container_connect_{socket,kernel}.
namespace_open will degrade to a no-op if user namespaces are not enabled
in the kernel.
Special handling is required for the setns call in namespace_enter with
a user namespace, since transitioning to your own namespace is forbidden,
as it would result in re-entering your user namespace as root.
Arguably it may be valid to check this at the call site, rather than
inside namespace_enter, but it is less code to do it inside, and if the
intention of calling namespace_enter is to *be* in the target namespace,
rather than to transition to the target namespace, it is a reasonable
approach.
The check for whether the user namespace is the same must happen before
entering namespaces, as we may not be able to access /proc during the
intermediate transition stage.
We can't instead attempt to enter the user namespace and then ignore
the failure from it being the same namespace, since the error code is
not distinct, and we can't compare namespaces while mid-transition.
2015-08-17 10:52:13 +02:00
|
|
|
|
if (userns_fd)
|
|
|
|
|
*userns_fd = usernsfd;
|
|
|
|
|
|
2014-05-18 13:48:53 +02:00
|
|
|
|
if (root_fd)
|
|
|
|
|
*root_fd = rfd;
|
|
|
|
|
|
namespace helpers: Allow entering a UID namespace
To be able to use `systemd-run` or `machinectl login` on a container
that is in a private user namespace, the sub-process must have entered
the user namespace before connecting to the container's D-Bus, otherwise
the UID and GID in the peer credentials are garbage.
So we extend namespace_open and namespace_enter to support UID namespaces,
and we enter the UID namespace in bus_container_connect_{socket,kernel}.
namespace_open will degrade to a no-op if user namespaces are not enabled
in the kernel.
Special handling is required for the setns call in namespace_enter with
a user namespace, since transitioning to your own namespace is forbidden,
as it would result in re-entering your user namespace as root.
Arguably it may be valid to check this at the call site, rather than
inside namespace_enter, but it is less code to do it inside, and if the
intention of calling namespace_enter is to *be* in the target namespace,
rather than to transition to the target namespace, it is a reasonable
approach.
The check for whether the user namespace is the same must happen before
entering namespaces, as we may not be able to access /proc during the
intermediate transition stage.
We can't instead attempt to enter the user namespace and then ignore
the failure from it being the same namespace, since the error code is
not distinct, and we can't compare namespaces while mid-transition.
2015-08-17 10:52:13 +02:00
|
|
|
|
pidnsfd = mntnsfd = netnsfd = usernsfd = -1;
|
2013-12-13 22:02:47 +01:00
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
namespace helpers: Allow entering a UID namespace
To be able to use `systemd-run` or `machinectl login` on a container
that is in a private user namespace, the sub-process must have entered
the user namespace before connecting to the container's D-Bus, otherwise
the UID and GID in the peer credentials are garbage.
So we extend namespace_open and namespace_enter to support UID namespaces,
and we enter the UID namespace in bus_container_connect_{socket,kernel}.
namespace_open will degrade to a no-op if user namespaces are not enabled
in the kernel.
Special handling is required for the setns call in namespace_enter with
a user namespace, since transitioning to your own namespace is forbidden,
as it would result in re-entering your user namespace as root.
Arguably it may be valid to check this at the call site, rather than
inside namespace_enter, but it is less code to do it inside, and if the
intention of calling namespace_enter is to *be* in the target namespace,
rather than to transition to the target namespace, it is a reasonable
approach.
The check for whether the user namespace is the same must happen before
entering namespaces, as we may not be able to access /proc during the
intermediate transition stage.
We can't instead attempt to enter the user namespace and then ignore
the failure from it being the same namespace, since the error code is
not distinct, and we can't compare namespaces while mid-transition.
2015-08-17 10:52:13 +02:00
|
|
|
|
int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int root_fd) {
|
|
|
|
|
if (userns_fd >= 0) {
|
|
|
|
|
/* Can't setns to your own userns, since then you could
|
|
|
|
|
* escalate from non-root to root in your own namespace, so
|
|
|
|
|
* check if namespaces equal before attempting to enter. */
|
|
|
|
|
_cleanup_free_ char *userns_fd_path = NULL;
|
|
|
|
|
int r;
|
|
|
|
|
if (asprintf(&userns_fd_path, "/proc/self/fd/%d", userns_fd) < 0)
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
|
|
r = files_same(userns_fd_path, "/proc/self/ns/user");
|
|
|
|
|
if (r < 0)
|
|
|
|
|
return r;
|
|
|
|
|
if (r)
|
|
|
|
|
userns_fd = -1;
|
|
|
|
|
}
|
2013-12-13 22:02:47 +01:00
|
|
|
|
|
2014-05-18 13:48:53 +02:00
|
|
|
|
if (pidns_fd >= 0)
|
|
|
|
|
if (setns(pidns_fd, CLONE_NEWPID) < 0)
|
|
|
|
|
return -errno;
|
2013-12-17 01:03:09 +01:00
|
|
|
|
|
2014-05-18 13:48:53 +02:00
|
|
|
|
if (mntns_fd >= 0)
|
|
|
|
|
if (setns(mntns_fd, CLONE_NEWNS) < 0)
|
|
|
|
|
return -errno;
|
2013-12-13 22:02:47 +01:00
|
|
|
|
|
2014-05-18 13:48:53 +02:00
|
|
|
|
if (netns_fd >= 0)
|
|
|
|
|
if (setns(netns_fd, CLONE_NEWNET) < 0)
|
|
|
|
|
return -errno;
|
2013-12-13 22:02:47 +01:00
|
|
|
|
|
namespace helpers: Allow entering a UID namespace
To be able to use `systemd-run` or `machinectl login` on a container
that is in a private user namespace, the sub-process must have entered
the user namespace before connecting to the container's D-Bus, otherwise
the UID and GID in the peer credentials are garbage.
So we extend namespace_open and namespace_enter to support UID namespaces,
and we enter the UID namespace in bus_container_connect_{socket,kernel}.
namespace_open will degrade to a no-op if user namespaces are not enabled
in the kernel.
Special handling is required for the setns call in namespace_enter with
a user namespace, since transitioning to your own namespace is forbidden,
as it would result in re-entering your user namespace as root.
Arguably it may be valid to check this at the call site, rather than
inside namespace_enter, but it is less code to do it inside, and if the
intention of calling namespace_enter is to *be* in the target namespace,
rather than to transition to the target namespace, it is a reasonable
approach.
The check for whether the user namespace is the same must happen before
entering namespaces, as we may not be able to access /proc during the
intermediate transition stage.
We can't instead attempt to enter the user namespace and then ignore
the failure from it being the same namespace, since the error code is
not distinct, and we can't compare namespaces while mid-transition.
2015-08-17 10:52:13 +02:00
|
|
|
|
if (userns_fd >= 0)
|
|
|
|
|
if (setns(userns_fd, CLONE_NEWUSER) < 0)
|
|
|
|
|
return -errno;
|
|
|
|
|
|
2014-05-18 13:48:53 +02:00
|
|
|
|
if (root_fd >= 0) {
|
|
|
|
|
if (fchdir(root_fd) < 0)
|
|
|
|
|
return -errno;
|
|
|
|
|
|
|
|
|
|
if (chroot(".") < 0)
|
|
|
|
|
return -errno;
|
|
|
|
|
}
|
2013-12-13 22:02:47 +01:00
|
|
|
|
|
2015-05-20 14:41:39 +02:00
|
|
|
|
return reset_uid_gid();
|
2013-12-13 22:02:47 +01:00
|
|
|
|
}
|
2013-12-18 04:19:20 +01:00
|
|
|
|
|
2014-02-19 02:15:24 +01:00
|
|
|
|
unsigned long personality_from_string(const char *p) {
|
2014-02-18 23:35:19 +01:00
|
|
|
|
|
|
|
|
|
/* Parse a personality specifier. We introduce our own
|
|
|
|
|
* identifiers that indicate specific ABIs, rather than just
|
|
|
|
|
* hints regarding the register size, since we want to keep
|
|
|
|
|
* things open for multiple locally supported ABIs for the
|
|
|
|
|
* same register size. We try to reuse the ABI identifiers
|
|
|
|
|
* used by libseccomp. */
|
|
|
|
|
|
|
|
|
|
#if defined(__x86_64__)
|
|
|
|
|
|
|
|
|
|
if (streq(p, "x86"))
|
|
|
|
|
return PER_LINUX32;
|
|
|
|
|
|
|
|
|
|
if (streq(p, "x86-64"))
|
|
|
|
|
return PER_LINUX;
|
|
|
|
|
|
|
|
|
|
#elif defined(__i386__)
|
|
|
|
|
|
|
|
|
|
if (streq(p, "x86"))
|
|
|
|
|
return PER_LINUX;
|
2015-09-24 12:47:22 +02:00
|
|
|
|
|
|
|
|
|
#elif defined(__s390x__)
|
|
|
|
|
|
|
|
|
|
if (streq(p, "s390"))
|
|
|
|
|
return PER_LINUX32;
|
|
|
|
|
|
|
|
|
|
if (streq(p, "s390x"))
|
|
|
|
|
return PER_LINUX;
|
|
|
|
|
|
|
|
|
|
#elif defined(__s390__)
|
|
|
|
|
|
|
|
|
|
if (streq(p, "s390"))
|
|
|
|
|
return PER_LINUX;
|
2014-02-18 23:35:19 +01:00
|
|
|
|
#endif
|
|
|
|
|
|
2015-05-21 19:48:49 +02:00
|
|
|
|
return PERSONALITY_INVALID;
|
2014-02-18 23:35:19 +01:00
|
|
|
|
}
|
2014-02-19 02:15:24 +01:00
|
|
|
|
|
|
|
|
|
const char* personality_to_string(unsigned long p) {
|
|
|
|
|
|
|
|
|
|
#if defined(__x86_64__)
|
|
|
|
|
|
|
|
|
|
if (p == PER_LINUX32)
|
|
|
|
|
return "x86";
|
|
|
|
|
|
|
|
|
|
if (p == PER_LINUX)
|
|
|
|
|
return "x86-64";
|
|
|
|
|
|
|
|
|
|
#elif defined(__i386__)
|
|
|
|
|
|
|
|
|
|
if (p == PER_LINUX)
|
|
|
|
|
return "x86";
|
2015-09-24 12:47:22 +02:00
|
|
|
|
|
|
|
|
|
#elif defined(__s390x__)
|
|
|
|
|
|
|
|
|
|
if (p == PER_LINUX)
|
|
|
|
|
return "s390x";
|
|
|
|
|
|
|
|
|
|
if (p == PER_LINUX32)
|
|
|
|
|
return "s390";
|
|
|
|
|
|
|
|
|
|
#elif defined(__s390__)
|
|
|
|
|
|
|
|
|
|
if (p == PER_LINUX)
|
|
|
|
|
return "s390";
|
|
|
|
|
|
2014-02-19 02:15:24 +01:00
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
2014-03-04 19:20:21 +01:00
|
|
|
|
|
|
|
|
|
uint64_t physical_memory(void) {
|
|
|
|
|
long mem;
|
|
|
|
|
|
|
|
|
|
/* We return this as uint64_t in case we are running as 32bit
|
|
|
|
|
* process on a 64bit kernel with huge amounts of memory */
|
|
|
|
|
|
|
|
|
|
mem = sysconf(_SC_PHYS_PAGES);
|
|
|
|
|
assert(mem > 0);
|
|
|
|
|
|
|
|
|
|
return (uint64_t) mem * (uint64_t) page_size();
|
|
|
|
|
}
|
2014-03-06 21:14:26 +01:00
|
|
|
|
|
2014-06-01 08:49:33 +02:00
|
|
|
|
int update_reboot_param_file(const char *param) {
|
2014-03-25 14:15:44 +01:00
|
|
|
|
int r = 0;
|
|
|
|
|
|
|
|
|
|
if (param) {
|
2015-07-07 01:19:25 +02:00
|
|
|
|
r = write_string_file(REBOOT_PARAM_FILE, param, WRITE_STRING_FILE_CREATE);
|
2014-03-25 14:15:44 +01:00
|
|
|
|
if (r < 0)
|
2015-09-30 22:16:17 +02:00
|
|
|
|
return log_error_errno(r, "Failed to write reboot param to "REBOOT_PARAM_FILE": %m");
|
2014-03-25 14:15:44 +01:00
|
|
|
|
} else
|
2015-09-30 22:16:17 +02:00
|
|
|
|
(void) unlink(REBOOT_PARAM_FILE);
|
2014-03-25 14:15:44 +01:00
|
|
|
|
|
2015-09-30 22:16:17 +02:00
|
|
|
|
return 0;
|
2014-03-25 14:15:44 +01:00
|
|
|
|
}
|
2014-06-05 21:35:35 +02:00
|
|
|
|
|
2014-08-15 01:05:47 +02:00
|
|
|
|
int is_symlink(const char *path) {
|
|
|
|
|
struct stat info;
|
|
|
|
|
|
|
|
|
|
if (lstat(path, &info) < 0)
|
|
|
|
|
return -errno;
|
|
|
|
|
|
2014-10-23 21:36:38 +02:00
|
|
|
|
return !!S_ISLNK(info.st_mode);
|
|
|
|
|
}
|
2014-08-15 01:05:47 +02:00
|
|
|
|
|
2014-10-23 21:36:38 +02:00
|
|
|
|
int is_dir(const char* path, bool follow) {
|
|
|
|
|
struct stat st;
|
2014-11-06 01:40:37 +01:00
|
|
|
|
int r;
|
2014-10-23 21:36:38 +02:00
|
|
|
|
|
2014-11-06 01:40:37 +01:00
|
|
|
|
if (follow)
|
|
|
|
|
r = stat(path, &st);
|
|
|
|
|
else
|
|
|
|
|
r = lstat(path, &st);
|
|
|
|
|
if (r < 0)
|
|
|
|
|
return -errno;
|
2014-10-23 21:36:38 +02:00
|
|
|
|
|
|
|
|
|
return !!S_ISDIR(st.st_mode);
|
2014-08-15 16:04:46 +02:00
|
|
|
|
}
|
2014-08-19 16:34:06 +02:00
|
|
|
|
|
2015-05-14 22:51:05 +02:00
|
|
|
|
int is_device_node(const char *path) {
|
|
|
|
|
struct stat info;
|
|
|
|
|
|
|
|
|
|
if (lstat(path, &info) < 0)
|
|
|
|
|
return -errno;
|
|
|
|
|
|
|
|
|
|
return !!(S_ISBLK(info.st_mode) || S_ISCHR(info.st_mode));
|
|
|
|
|
}
|
|
|
|
|
|
2015-01-22 03:57:15 +01:00
|
|
|
|
int syslog_parse_priority(const char **p, int *priority, bool with_facility) {
|
|
|
|
|
int a = 0, b = 0, c = 0;
|
|
|
|
|
int k;
|
|
|
|
|
|
|
|
|
|
assert(p);
|
|
|
|
|
assert(*p);
|
|
|
|
|
assert(priority);
|
|
|
|
|
|
|
|
|
|
if ((*p)[0] != '<')
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
if (!strchr(*p, '>'))
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
if ((*p)[2] == '>') {
|
|
|
|
|
c = undecchar((*p)[1]);
|
|
|
|
|
k = 3;
|
|
|
|
|
} else if ((*p)[3] == '>') {
|
|
|
|
|
b = undecchar((*p)[1]);
|
|
|
|
|
c = undecchar((*p)[2]);
|
|
|
|
|
k = 4;
|
|
|
|
|
} else if ((*p)[4] == '>') {
|
|
|
|
|
a = undecchar((*p)[1]);
|
|
|
|
|
b = undecchar((*p)[2]);
|
|
|
|
|
c = undecchar((*p)[3]);
|
|
|
|
|
k = 5;
|
|
|
|
|
} else
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
if (a < 0 || b < 0 || c < 0 ||
|
|
|
|
|
(!with_facility && (a || b || c > 7)))
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
if (with_facility)
|
|
|
|
|
*priority = a*100 + b*10 + c;
|
|
|
|
|
else
|
|
|
|
|
*priority = (*priority & LOG_FACMASK) | c;
|
|
|
|
|
|
|
|
|
|
*p += k;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
2015-02-13 21:40:50 +01:00
|
|
|
|
|
|
|
|
|
ssize_t string_table_lookup(const char * const *table, size_t len, const char *key) {
|
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
|
|
if (!key)
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < len; ++i)
|
|
|
|
|
if (streq_ptr(table[i], key))
|
2015-09-19 00:50:34 +02:00
|
|
|
|
return (ssize_t) i;
|
2015-02-13 21:40:50 +01:00
|
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
2015-02-18 19:20:47 +01:00
|
|
|
|
|
2015-09-23 03:01:06 +02:00
|
|
|
|
int version(void) {
|
|
|
|
|
puts(PACKAGE_STRING "\n"
|
|
|
|
|
SYSTEMD_FEATURES);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2015-10-04 17:36:19 +02:00
|
|
|
|
|
|
|
|
|
bool fdname_is_valid(const char *s) {
|
|
|
|
|
const char *p;
|
|
|
|
|
|
2015-10-06 12:33:14 +02:00
|
|
|
|
/* Validates a name for $LISTEN_FDNAMES. We basically allow
|
2015-10-04 17:36:19 +02:00
|
|
|
|
* everything ASCII that's not a control character. Also, as
|
|
|
|
|
* special exception the ":" character is not allowed, as we
|
2015-10-06 12:33:14 +02:00
|
|
|
|
* use that as field separator in $LISTEN_FDNAMES.
|
2015-10-04 17:36:19 +02:00
|
|
|
|
*
|
2015-10-06 12:33:14 +02:00
|
|
|
|
* Note that the empty string is explicitly allowed
|
|
|
|
|
* here. However, we limit the length of the names to 255
|
|
|
|
|
* characters. */
|
2015-10-04 17:36:19 +02:00
|
|
|
|
|
|
|
|
|
if (!s)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
for (p = s; *p; p++) {
|
|
|
|
|
if (*p < ' ')
|
|
|
|
|
return false;
|
|
|
|
|
if (*p >= 127)
|
|
|
|
|
return false;
|
|
|
|
|
if (*p == ':')
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return p - s < 256;
|
|
|
|
|
}
|
2015-10-17 22:01:41 +02:00
|
|
|
|
|
|
|
|
|
bool oom_score_adjust_is_valid(int oa) {
|
|
|
|
|
return oa >= OOM_SCORE_ADJ_MIN && oa <= OOM_SCORE_ADJ_MAX;
|
|
|
|
|
}
|