diff options
author | Tollef Fog Heen <tfheen@err.no> | 2011-03-01 17:05:35 +0100 |
---|---|---|
committer | Tollef Fog Heen <tfheen@err.no> | 2011-03-01 17:05:35 +0100 |
commit | 8a796f3efa44f101c361d9c26252722a14e417f5 (patch) | |
tree | 87277f4c01fd46d81625be6c89d44fd609d906bf | |
parent | f6f2f49345fd75fb27af159d96f17fa10e0181bb (diff) | |
parent | 020501d9524c2cba58873198c59909c9d591c292 (diff) | |
download | systemd-8a796f3efa44f101c361d9c26252722a14e417f5.tar.gz systemd-8a796f3efa44f101c361d9c26252722a14e417f5.tar.bz2 systemd-8a796f3efa44f101c361d9c26252722a14e417f5.zip |
Merge commit 'v19'
77 files changed, 1254 insertions, 356 deletions
diff --git a/.gitignore b/.gitignore index 1bb184f875..adee97f6ce 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +systemd-detect-virt systemd-sysctl test-strv systemd-ac-power diff --git a/CODING_STYLE b/CODING_STYLE index 93f54f6edc..9341b48d41 100644 --- a/CODING_STYLE +++ b/CODING_STYLE @@ -20,7 +20,7 @@ we those lookups involve synchronously talking to services that we would need to start up. -- Do not acccess any directories outside of /etc/, /dev, /lib from the +- Do not access any directories outside of /etc/, /dev, /lib from the init daemon to avoid deadlocks with the automounter. - Don't synchronously talk to any other service, due to risk of diff --git a/Makefile.am b/Makefile.am index f508163d5b..1841ad5c5a 100644 --- a/Makefile.am +++ b/Makefile.am @@ -129,6 +129,7 @@ rootlibexec_PROGRAMS = \ systemd-quotacheck \ systemd-timestamp \ systemd-ac-power \ + systemd-detect-virt \ systemd-sysctl if HAVE_LIBCRYPTSETUP @@ -242,7 +243,6 @@ dist_systemunit_DATA = \ units/systemd-tmpfiles-clean.timer \ units/quotaon.service \ units/systemd-ask-password-wall.path \ - units/systemd-ask-password-plymouth.path \ units/systemd-ask-password-console.path nodist_systemunit_DATA = \ @@ -269,7 +269,6 @@ nodist_systemunit_DATA = \ units/systemd-tmpfiles-clean.service \ units/systemd-user-sessions.service \ units/systemd-ask-password-wall.service \ - units/systemd-ask-password-plymouth.service \ units/systemd-ask-password-console.service \ units/systemd-sysctl.service \ units/syslog.target \ @@ -315,7 +314,6 @@ EXTRA_DIST = \ units/systemd-tmpfiles-clean.service.in \ units/systemd-user-sessions.service.in \ units/systemd-ask-password-wall.service.in \ - units/systemd-ask-password-plymouth.service.in \ units/systemd-ask-password-console.service.in \ units/systemd-sysctl.service.in \ units/syslog.target.in \ @@ -342,10 +340,18 @@ dist_systemunit_DATA += \ units/plymouth-start.service \ units/plymouth-read-write.service \ units/plymouth-quit.service \ + units/plymouth-quit-wait.service \ units/plymouth-reboot.service \ units/plymouth-kexec.service \ units/plymouth-poweroff.service \ - units/plymouth-halt.service + units/plymouth-halt.service \ + units/systemd-ask-password-plymouth.path + +nodist_systemunit_DATA += \ + units/systemd-ask-password-plymouth.service + +EXTRA_DIST += \ + units/systemd-ask-password-plymouth.service.in endif dist_doc_DATA = \ @@ -783,6 +789,15 @@ systemd_ac_power_LDADD = \ libsystemd-basic.la \ $(UDEV_LIBS) +systemd_detect_virt_SOURCES = \ + src/detect-virt.c + +systemd_detect_virt_CFLAGS = \ + $(AM_CFLAGS) + +systemd_detect_virt_LDADD = \ + libsystemd-basic.la + systemd_cryptsetup_SOURCES = \ src/cryptsetup.c \ src/ask-password-api.c @@ -1292,8 +1307,9 @@ if HAVE_PLYMOUTH $(LN_S) ../plymouth-start.service plymouth-start.service && \ $(LN_S) ../plymouth-read-write.service plymouth-read-write.service ) ( cd $(DESTDIR)$(systemunitdir)/multi-user.target.wants && \ - rm -f plymouth-quit.service && \ - $(LN_S) ../plymouth-quit.service plymouth-quit.service ) + rm -f plymouth-quit.service plymouth-quit-wait.service && \ + $(LN_S) ../plymouth-quit.service plymouth-quit.service && \ + $(LN_S) ../plymouth-quit-wait.service plymouth-quit-wait.service ) ( cd $(DESTDIR)$(systemunitdir)/reboot.target.wants && \ rm -f plymouth-reboot.service && \ $(LN_S) ../plymouth-reboot.service plymouth-reboot.service ) @@ -27,7 +27,7 @@ AUTHOR: Lennart Poettering with major support from Kay Sievers REQUIREMENTS: - Linux kernel >= 2.6.30 (with autofs4, devtmpfs, cgroups) + Linux kernel >= 2.6.30 (with devtmpfs, cgroups; optional but strongly recommended: autofs4, ipv6) libudev >= 163 dbus >= 1.4.0 libcap @@ -47,10 +47,27 @@ REQUIREMENTS: automake autoconf libtool + make, gcc, and similar tools During runtime you need the following dependencies: util-linux > v2.18 (requires fsck -l, agetty -s) - sulogin (from sysvinit-tools) + sulogin (from sysvinit-tools, optional but recommended) plymouth (optional) dracut (optional) + +WARNINGS: + systemd will warn you during boot if /etc/mtab is not a + symlink to /proc/mounts. Please ensure that /etc/mtab is a + proper symlink. + + systemd will warn you during boot if /usr is on a different + file system than /. While in systemd itself very little will + break if /usr is on a seperate partition many of its + dependencies very likely will break sooner or later in one + form or another. For example udev rules tend to refer to + binaries in /usr, binaries that link to libraries in /usr or + binaries that refer to data files in /usr. Since these + breakages are not always directly visible systemd will warn + about this, since this kind of file system setup is not really + supported anymore by the basic set of Linux OS components. @@ -1,18 +1,44 @@ -Bugs: +F15: + +* swap units that are activated by one name but shown in the kernel under another are semi-broken + +* dep cycle basic → udev-retry → auditd → iptables → basic * isolate multi-user.target doesn't start a getty@tty1 if we run it from graphical.target -* when plymouth is disabled the console password entry stuff seems to be borked - https://bugzilla.redhat.com/show_bug.cgi?id=655538 +* finish syslog socket stuff + +* load EnvironmentFile= when starting services, not when reloading configuration + https://bugzilla.redhat.com/show_bug.cgi?id=661282 + +* drop IN_ATTRIB from inotify watches for .path units where possible to avoid + lots of wakeups due to /dev changing when we watch a subdir of /dev. + +* NFS, networkmanager ordering issue + +* add fstab fields to add wait timeouts, change Wants to Requires by local-fs.target + +* hook emergency.target into local-fs.target in some way as OnFailure with isolate -* exclude java hsp files by default - https://bugzilla.redhat.com/show_bug.cgi?id=527425 +* convince Karel to give us our own mount option prefix Features: -* Make use of UnknownInterface, UnknownObject +* maybe implement "systemctl mask" and "systemctl unmask", but not + document it? When doing that add switch to make this temporary by + placing mask links in /dev. + +* introduce simple way to do mandatory conditions + +* detect LXC environment + +* invoke vhangup() before and after invoking getty -* increase password timeout +* skip readahead on physically r/o media + +* support "auto" and "comment=systemd.automount" at the same time for an fstab entry + +* Make use of UnknownInterface, UnknownObject * look up crypto partition mount points via fstab to show to the user when prompting for a password @@ -31,19 +57,8 @@ Features: * support remote/ssh systemctl/systemadm, and local privileged access -* finish syslog socket stuff - * configurable jitter for timer events -* support caching password questions in plymouth and on the console - https://bugzilla.redhat.com/show_bug.cgi?id=655538 - -* load EnvironmentFile= when starting services, not when reloading configuration - https://bugzilla.redhat.com/show_bug.cgi?id=661282 - -* drop IN_ATTRIB from inotify watches for .path units where possible to avoid - lots of wakeups due to /dev changing when we watch a subdir of /dev. - * Support ProcessNeededForShutdown=true to allow stuff like mdmon to be killed very late after the rootfs is read only (?) @@ -112,20 +127,12 @@ Features: * allow runtime changing of log level and target -Fedora: - -* chkconfig → systemd enable/daemon-reload glue - -* /var/lock, /var/run → what happens if rpm stuff is no %ghosted? - External: * udisks should not use udisks-part-id, instead use blkid. also not probe /dev/loopxxx * snd-seq should go, https://bugzilla.redhat.com/show_bug.cgi?id=676095 -* mount.tmpfs should be optimized, https://bugzilla.redhat.com/show_bug.cgi?id=676100 - * gnome-shell python script/glxinfo/is-accelerated wech * make cryptsetup lower --iter-time diff --git a/configure.ac b/configure.ac index ce5772d559..2cf31e4e84 100644 --- a/configure.ac +++ b/configure.ac @@ -17,7 +17,7 @@ AC_PREREQ(2.63) -AC_INIT([systemd],[18],[systemd-devel@lists.freedesktop.org]) +AC_INIT([systemd],[19],[systemd-devel@lists.freedesktop.org]) AC_CONFIG_SRCDIR([src/main.c]) AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_HEADERS([config.h]) @@ -368,8 +368,10 @@ case $with_distro in altlinux) SYSTEM_SYSVINIT_PATH=/etc/rc.d/init.d SYSTEM_SYSVRCND_PATH=/etc/rc.d + SPECIAL_SYSLOG_SERVICE=syslogd.service AC_DEFINE(TARGET_ALTLINUX, [], [Target is ALTLinux]) M4_DISTRO_FLAG=-DTARGET_ALTLINUX=1 + have_plymouth=true ;; other) AS_IF([test "x$with_syslog_service" = "x"], diff --git a/man/daemon.xml b/man/daemon.xml index 7e7039e07a..ea0e6d27f5 100644 --- a/man/daemon.xml +++ b/man/daemon.xml @@ -841,7 +841,7 @@ fi %preun if [ $1 -eq 0 ]; then # On uninstall (not upgrade), disable and stop the units - /bin/systemctl disable foobar.service foobar.socket >/dev/null 2>&1 || : + /bin/systemctl --no-reload disable foobar.service foobar.socket >/dev/null 2>&1 || : /bin/systemctl stop foobar.service foobar.socket >/dev/null 2>&1 || : fi @@ -870,7 +870,7 @@ fi</programlisting> <programlisting>%triggerun -- foobar < 0.47.11-1 if /sbin/chkconfig foobar ; then - /bin/systemctl enable foobar.service foobar.socket >/dev/null 2>&1 || : + /bin/systemctl --no-reload enable foobar.service foobar.socket >/dev/null 2>&1 || : fi</programlisting> <para>Where 0.47.11-1 is the first package diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml index e9576e1e72..1f79e1e19b 100644 --- a/man/systemd.exec.xml +++ b/man/systemd.exec.xml @@ -412,7 +412,7 @@ <filename>/dev/console</filename>.</para></listitem> </varlistentry> <varlistentry> - <term><varname>SyslogIdentifer=</varname></term> + <term><varname>SyslogIdentifier=</varname></term> <listitem><para>Sets the process name to prefix log lines sent to syslog or the kernel log buffer with. If not set diff --git a/man/systemd.unit.xml b/man/systemd.unit.xml index 14ec4561b2..fa8821afe5 100644 --- a/man/systemd.unit.xml +++ b/man/systemd.unit.xml @@ -575,6 +575,7 @@ <term><varname>ConditionPathExists=</varname></term> <term><varname>ConditionDirectoryNotEmpty=</varname></term> <term><varname>ConditionKernelCommandLine=</varname></term> + <term><varname>ConditionVirtualization=</varname></term> <term><varname>ConditionNull=</varname></term> <listitem><para>Before starting a unit @@ -615,7 +616,24 @@ assignment. In the latter case the exact assignment is looked for with right and left hand side - matching. Finally, + matching. <varname>ConditionVirtualization=</varname> + may be used to check whether the + system is executed in a virtualized + environment and optionally test + whether it is a specific + implementation. Takes either boolean + value to check if being executed in any + virtual environment or one of the + <varname>qemu</varname>, + <varname>kvm</varname>, + <varname>vmware</varname>, + <varname>microsoft</varname>, + <varname>oracle</varname>, + <varname>xen</varname>, + <varname>openvz</varname> to test + against a specific implementation. The + test may be negated by prepending an + exclamation mark. Finally, <varname>ConditionNull=</varname> may be used to add a constant condition check value to the unit. It takes a @@ -623,9 +641,9 @@ <varname>false</varname> the condition will always fail, otherwise succeed. If multiple conditions are - specified the unit will be executed - if at least one of them applies - (i.e. a logical OR is + specified the unit will be executed if + at least one of them applies (i.e. a + logical OR is applied).</para></listitem> </varlistentry> </variablelist> diff --git a/src/99-systemd.rules b/src/99-systemd.rules index 0ac6d0023f..4fba6d4d0b 100644 --- a/src/99-systemd.rules +++ b/src/99-systemd.rules @@ -7,12 +7,17 @@ ACTION!="add|change", GOTO="systemd_end" -KERNEL=="tty[0-9]|tty1[0-2]", TAG+="systemd" -KERNEL=="ttyS*", TAG+="systemd" +SUBSYSTEM=="tty", KERNEL=="tty[0-9]|tty1[0-2]", TAG+="systemd" +SUBSYSTEM=="tty", KERNEL=="ttyS*", TAG+="systemd" +SUBSYSTEM=="tty", KERNEL=="hvc*", TAG+="systemd" +SUBSYSTEM=="tty", KERNEL=="ttyUSB*", TAG+="systemd" SUBSYSTEM=="block", KERNEL!="ram*|loop*", TAG+="systemd" SUBSYSTEM=="block", KERNEL!="ram*|loop*", ENV{DM_UDEV_DISABLE_OTHER_RULES_FLAG}=="1", ENV{SYSTEMD_READY}="0" -SUBSYSTEM=="block", KERNEL!="ram*|loop*", ENV{ID_PART_TABLE_TYPE}=="", ENV{ID_FS_USAGE}=="", ENV{SYSTEMD_READY}="0" + +# Ignore encrypted devices with no identified superblock on it, since +# we are probably still calling mke2fs or mkswap on it. +SUBSYSTEM=="block", KERNEL!="ram*|loop*", ENV{DM_UUID}=="CRYPT-*", ENV{ID_PART_TABLE_TYPE}=="", ENV{ID_FS_USAGE}=="", ENV{SYSTEMD_READY}="0" # We need a hardware independent way to identify network devices. We # use the /sys/subsystem path for this. Current vanilla kernels don't diff --git a/src/ask-password-api.c b/src/ask-password-api.c index 9f7023e328..af1b611f27 100644 --- a/src/ask-password-api.c +++ b/src/ask-password-api.c @@ -32,6 +32,7 @@ #include <sys/signalfd.h> #include "util.h" +#include "strv.h" #include "ask-password-api.h" @@ -110,7 +111,7 @@ int ask_password_tty( y = now(CLOCK_MONOTONIC); if (y > until) { - r = -ETIMEDOUT; + r = -ETIME; goto finish; } @@ -131,7 +132,7 @@ int ask_password_tty( r = -errno; goto finish; } else if (k == 0) { - r = -ETIMEDOUT; + r = -ETIME; goto finish; } @@ -178,9 +179,6 @@ int ask_password_tty( } } - if (ttyfd >= 0) - loop_write(ttyfd, "\n", 1, false); - passphrase[p] = 0; if (!(*_passphrase = strdup(passphrase))) { @@ -195,8 +193,11 @@ finish: close_nointr_nofail(notify); if (ttyfd >= 0) { - if (reset_tty) + + if (reset_tty) { + loop_write(ttyfd, "\n", 1, false); tcsetattr(ttyfd, TCSADRAIN, &old_termios); + } close_nointr_nofail(ttyfd); } @@ -251,12 +252,12 @@ fail: return r; } - int ask_password_agent( const char *message, const char *icon, usec_t until, - char **_passphrase) { + bool accept_cached, + char ***_passphrases) { enum { FD_SOCKET, @@ -273,6 +274,8 @@ int ask_password_agent( sigset_t mask; struct pollfd pollfd[_FD_MAX]; + assert(_passphrases); + mkdir_p("/dev/.systemd/ask-password", 0755); if ((fd = mkostemp(temp, O_CLOEXEC|O_CREAT|O_WRONLY)) < 0) { @@ -310,9 +313,11 @@ int ask_password_agent( "[Ask]\n" "PID=%lu\n" "Socket=%s\n" + "AcceptCached=%i\n" "NotAfter=%llu\n", (unsigned long) getpid(), socket_name, + accept_cached ? 1 : 0, (unsigned long long) until); if (message) @@ -368,7 +373,7 @@ int ask_password_agent( goto finish; } - if ((k = poll(pollfd, _FD_MAX, until-t/USEC_PER_MSEC)) < 0) { + if ((k = poll(pollfd, _FD_MAX, (until-t)/USEC_PER_MSEC)) < 0) { if (errno == EINTR) continue; @@ -384,8 +389,10 @@ int ask_password_agent( goto finish; } - if (pollfd[FD_SIGNAL].revents & POLLIN) - break; + if (pollfd[FD_SIGNAL].revents & POLLIN) { + r = -EINTR; + goto finish; + } if (pollfd[FD_SOCKET].revents != POLLIN) { log_error("Unexpected poll() event."); @@ -395,7 +402,7 @@ int ask_password_agent( zero(iovec); iovec.iov_base = passphrase; - iovec.iov_len = sizeof(passphrase)-1; + iovec.iov_len = sizeof(passphrase); zero(control); zero(msghdr); @@ -435,13 +442,21 @@ int ask_password_agent( } if (passphrase[0] == '+') { - passphrase[n] = 0; + char **l; - if (!(*_passphrase = strdup(passphrase+1))) { + if (!(l = strv_parse_nulstr(passphrase+1, n-1))) { r = -ENOMEM; goto finish; } + if (strv_length(l) <= 0) { + strv_free(l); + log_error("Invalid packet"); + continue; + } + + *_passphrases = l; + } else if (passphrase[0] == '-') { r = -ECANCELED; goto finish; @@ -481,12 +496,26 @@ finish: return r; } -int ask_password_auto(const char *message, const char *icon, usec_t until, char **_passphrase) { +int ask_password_auto(const char *message, const char *icon, usec_t until, bool accept_cached, char ***_passphrases) { assert(message); - assert(_passphrase); + assert(_passphrases); + + if (isatty(STDIN_FILENO)) { + int r; + char *s = NULL, **l = NULL; + + if ((r = ask_password_tty(message, until, NULL, &s)) < 0) + return r; + + l = strv_new(s, NULL); + free(s); + + if (!l) + return -ENOMEM; + + *_passphrases = l; + return r; - if (isatty(STDIN_FILENO)) - return ask_password_tty(message, until, NULL, _passphrase); - else - return ask_password_agent(message, icon, until, _passphrase); + } else + return ask_password_agent(message, icon, until, accept_cached, _passphrases); } diff --git a/src/ask-password-api.h b/src/ask-password-api.h index ec858bac78..fec8625a0f 100644 --- a/src/ask-password-api.h +++ b/src/ask-password-api.h @@ -26,8 +26,8 @@ int ask_password_tty(const char *message, usec_t until, const char *flag_file, char **_passphrase); -int ask_password_agent(const char *message, const char *icon, usec_t until, char **_passphrase); +int ask_password_agent(const char *message, const char *icon, usec_t until, bool accept_cached, char ***_passphrases); -int ask_password_auto(const char *message, const char *icon, usec_t until, char **_passphrase); +int ask_password_auto(const char *message, const char *icon, usec_t until, bool accept_cached, char ***_passphrases); #endif diff --git a/src/ask-password.c b/src/ask-password.c index 596c8e08f0..c77376482e 100644 --- a/src/ask-password.c +++ b/src/ask-password.c @@ -38,21 +38,26 @@ #include "log.h" #include "macro.h" #include "util.h" +#include "strv.h" #include "ask-password-api.h" static const char *arg_icon = NULL; static const char *arg_message = NULL; static bool arg_use_tty = true; static usec_t arg_timeout = 60 * USEC_PER_SEC; +static bool arg_accept_cached = false; +static bool arg_multiple = false; static int help(void) { printf("%s [OPTIONS...] MESSAGE\n\n" "Query the user for a system passphrase, via the TTY or an UI agent.\n\n" - " -h --help Show this help\n" - " --icon=NAME Icon name\n" - " --timeout=SEC Timeout in sec\n" - " --no-tty Ask question via agent even on TTY\n", + " -h --help Show this help\n" + " --icon=NAME Icon name\n" + " --timeout=SEC Timeout in sec\n" + " --no-tty Ask question via agent even on TTY\n" + " --accept-cached Accept cached passwords\n" + " --multiple List multiple passwords if available\n", program_invocation_short_name); return 0; @@ -63,15 +68,19 @@ static int parse_argv(int argc, char *argv[]) { enum { ARG_ICON = 0x100, ARG_TIMEOUT, - ARG_NO_TTY + ARG_NO_TTY, + ARG_ACCEPT_CACHED, + ARG_MULTIPLE }; static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "icon", required_argument, NULL, ARG_ICON }, - { "timeout", required_argument, NULL, ARG_TIMEOUT }, - { "no-tty", no_argument, NULL, ARG_NO_TTY }, - { NULL, 0, NULL, 0 } + { "help", no_argument, NULL, 'h' }, + { "icon", required_argument, NULL, ARG_ICON }, + { "timeout", required_argument, NULL, ARG_TIMEOUT }, + { "no-tty", no_argument, NULL, ARG_NO_TTY }, + { "accept-cached", no_argument, NULL, ARG_ACCEPT_CACHED }, + { "multiple", no_argument, NULL, ARG_MULTIPLE }, + { NULL, 0, NULL, 0 } }; int c; @@ -102,6 +111,14 @@ static int parse_argv(int argc, char *argv[]) { arg_use_tty = false; break; + case ARG_ACCEPT_CACHED: + arg_accept_cached = true; + break; + + case ARG_MULTIPLE: + arg_multiple = true; + break; + case '?': return -EINVAL; @@ -122,7 +139,6 @@ static int parse_argv(int argc, char *argv[]) { int main(int argc, char *argv[]) { int r; - char *password = NULL; log_parse_environment(); log_open(); @@ -130,18 +146,32 @@ int main(int argc, char *argv[]) { if ((r = parse_argv(argc, argv)) <= 0) goto finish; - if (arg_use_tty && isatty(STDIN_FILENO)) - r = ask_password_tty(arg_message, now(CLOCK_MONOTONIC) + arg_timeout, NULL, &password); - else - r = ask_password_agent(arg_message, arg_icon, now(CLOCK_MONOTONIC) + arg_timeout, &password); + if (arg_use_tty && isatty(STDIN_FILENO)) { + char *password = NULL; + + if ((r = ask_password_tty(arg_message, now(CLOCK_MONOTONIC) + arg_timeout, NULL, &password)) >= 0) { + puts(password); + free(password); + } + + } else { + char **l; + + if ((r = ask_password_agent(arg_message, arg_icon, now(CLOCK_MONOTONIC) + arg_timeout, arg_accept_cached, &l)) >= 0) { + char **p; + + STRV_FOREACH(p, l) { + puts(*p); - if (r >= 0) { - fputs(password, stdout); - fflush(stdout); + if (!arg_multiple) + break; + } + + strv_free(l); + } } finish: - free(password); return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; } diff --git a/src/automount.c b/src/automount.c index 9447c0d8fc..f6f83d43b6 100644 --- a/src/automount.c +++ b/src/automount.c @@ -305,7 +305,7 @@ static int open_dev_autofs(Manager *m) { if (m->dev_autofs_fd >= 0) return m->dev_autofs_fd; - label_fix("/dev/autofs"); + label_fix("/dev/autofs", false); if ((m->dev_autofs_fd = open("/dev/autofs", O_CLOEXEC|O_RDONLY)) < 0) { log_error("Failed to open /dev/autofs: %s", strerror(errno)); @@ -583,7 +583,7 @@ static void automount_enter_runnning(Automount *a) { /* Before we do anything, let's see if somebody is playing games with us? */ if (lstat(a->where, &st) < 0) { - log_warning("%s failed stat automount point: %m", a->meta.id); + log_warning("%s failed to stat automount point: %m", a->meta.id); goto fail; } diff --git a/src/cgroups-agent.c b/src/cgroups-agent.c index 3fd0de698f..7b4fca245d 100644 --- a/src/cgroups-agent.c +++ b/src/cgroups-agent.c @@ -46,7 +46,7 @@ int main(int argc, char *argv[]) { /* We send this event to the private D-Bus socket and then the * system instance will forward this to the system bus. We do - * this to avoid an actviation loop when we start dbus when we + * this to avoid an activation loop when we start dbus when we * are called when the dbus service is shut down. */ if (!(bus = dbus_connection_open_private("unix:abstract=/org/freedesktop/systemd1/private", &error))) { diff --git a/src/condition.c b/src/condition.c index 21da2eb9e9..630350ed36 100644 --- a/src/condition.c +++ b/src/condition.c @@ -64,6 +64,8 @@ static bool test_kernel_command_line(const char *parameter) { size_t l, pl; bool found = false; + assert(parameter); + if ((r = read_one_line_file("/proc/cmdline", &line)) < 0) { log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r)); return false; @@ -98,6 +100,28 @@ static bool test_kernel_command_line(const char *parameter) { return found; } +static bool test_virtualization(const char *parameter) { + int r, b; + const char *id; + + assert(parameter); + + if ((r = detect_virtualization(&id)) < 0) { + log_warning("Failed to detect virtualization, ignoring: %s", strerror(-r)); + return false; + } + + b = parse_boolean(parameter); + + if (r > 0 && b > 0) + return true; + + if (r == 0 && b == 0) + return true; + + return streq(parameter, id); +} + bool condition_test(Condition *c) { assert(c); @@ -114,7 +138,10 @@ bool condition_test(Condition *c) { } case CONDITION_KERNEL_COMMAND_LINE: - return !!test_kernel_command_line(c->parameter) == !c->negate; + return test_kernel_command_line(c->parameter) == !c->negate; + + case CONDITION_VIRTUALIZATION: + return test_virtualization(c->parameter) == !c->negate; case CONDITION_NULL: return !c->negate; diff --git a/src/condition.h b/src/condition.h index 2f2689cc6d..f4903d76d9 100644 --- a/src/condition.h +++ b/src/condition.h @@ -30,6 +30,7 @@ typedef enum ConditionType { CONDITION_PATH_EXISTS, CONDITION_DIRECTORY_NOT_EMPTY, CONDITION_KERNEL_COMMAND_LINE, + CONDITION_VIRTUALIZATION, CONDITION_NULL, _CONDITION_TYPE_MAX, _CONDITION_TYPE_INVALID = -1 diff --git a/src/cryptsetup.c b/src/cryptsetup.c index c80572aed9..989734be1b 100644 --- a/src/cryptsetup.c +++ b/src/cryptsetup.c @@ -22,12 +22,14 @@ #include <string.h> #include <errno.h> #include <sys/mman.h> +#include <mntent.h> #include <libcryptsetup.h> #include <libudev.h> #include "log.h" #include "util.h" +#include "strv.h" #include "ask-password-api.h" static const char *opt_type = NULL; /* LUKS1 or PLAIN */ @@ -163,7 +165,8 @@ static char *disk_description(const char *path) { goto finish; if ((model = udev_device_get_property_value(device, "ID_MODEL_FROM_DATABASE")) || - (model = udev_device_get_property_value(device, "ID_MODEL"))) + (model = udev_device_get_property_value(device, "ID_MODEL")) || + (model = udev_device_get_property_value(device, "DM_NAME"))) description = strdup(model); finish: @@ -176,12 +179,56 @@ finish: return description; } +static char *disk_mount_point(const char *label) { + char *mp = NULL, *device = NULL; + FILE *f = NULL; + struct mntent *m; + + /* Yeah, we don't support native systemd unit files here for now */ + + if (asprintf(&device, "/dev/mapper/%s", label) < 0) + goto finish; + + if (!(f = setmntent("/etc/fstab", "r"))) + goto finish; + + while ((m = getmntent(f))) + if (path_equal(m->mnt_fsname, device)) { + mp = strdup(m->mnt_dir); + break; + } + +finish: + if (f) + endmntent(f); + + free(device); + + return mp; +} + +static int help(void) { + + printf("%s attach VOLUME SOURCEDEVICE [PASSWORD] [OPTIONS]\n" + "%s detach VOLUME\n\n" + "Attaches or detaches an encrypted block device.\n", + program_invocation_short_name, + program_invocation_short_name); + + return 0; +} + int main(int argc, char *argv[]) { int r = EXIT_FAILURE; struct crypt_device *cd = NULL; - char *password = NULL, *truncated_cipher = NULL; + char **passwords = NULL, *truncated_cipher = NULL; const char *cipher = NULL, *cipher_mode = NULL, *hash = NULL, *name = NULL; - char *description = NULL; + char *description = NULL, *name_buffer = NULL, *mount_point = NULL; + + if (argc <= 1) { + help(); + return EXIT_SUCCESS; + } if (argc < 3) { log_error("This program requires at least two arguments."); @@ -200,6 +247,8 @@ int main(int argc, char *argv[]) { usec_t until; crypt_status_info status; + /* Arguments: systemd-cryptsetup attach VOLUME SOURCE-DEVICE [PASSWORD] [OPTIONS] */ + if (argc < 4) { log_error("attach requires at least two arguments."); goto finish; @@ -223,7 +272,24 @@ int main(int argc, char *argv[]) { mlockall(MCL_FUTURE); description = disk_description(argv[3]); - name = description ? description : argv[2]; + mount_point = disk_mount_point(argv[2]); + + if (description && streq(argv[2], description)) { + /* If the description string is simply the + * volume name, then let's not show this + * twice */ + free(description); + description = NULL; + } + + if (mount_point && description) + asprintf(&name_buffer, "%s (%s) on %s", description, argv[2], mount_point); + else if (mount_point) + asprintf(&name_buffer, "%s on %s", argv[2], mount_point); + else if (description) + asprintf(&name_buffer, "%s (%s)", description, argv[2]); + + name = name_buffer ? name_buffer : argv[2]; if ((k = crypt_init(&cd, argv[3]))) { log_error("crypt_init() failed: %s", strerror(-k)); @@ -268,18 +334,19 @@ int main(int argc, char *argv[]) { for (try = 0; try < opt_tries; try++) { bool pass_volume_key = false; - free(password); - password = NULL; + strv_free(passwords); + passwords = NULL; if (!key_file) { char *text; + char **p; if (asprintf(&text, "Please enter passphrase for disk %s!", name) < 0) { log_error("Out of memory"); goto finish; } - k = ask_password_auto(text, "drive-harddisk", until, &password); + k = ask_password_auto(text, "drive-harddisk", until, try == 0 && !opt_verify, &passwords); free(text); if (k < 0) { @@ -288,14 +355,16 @@ int main(int argc, char *argv[]) { } if (opt_verify) { - char *password2 = NULL; + char **passwords2 = NULL; + + assert(strv_length(passwords) == 1); if (asprintf(&text, "Please enter passphrase for disk %s! (verification)", name) < 0) { log_error("Out of memory"); goto finish; } - k = ask_password_auto(text, "drive-harddisk", until, &password2); + k = ask_password_auto(text, "drive-harddisk", until, false, &passwords2); free(text); if (k < 0) { @@ -303,28 +372,34 @@ int main(int argc, char *argv[]) { goto finish; } - if (!streq(password, password2)) { + assert(strv_length(passwords2) == 1); + + if (!streq(passwords[0], passwords2[0])) { log_warning("Passwords did not match, retrying."); - free(password2); + strv_free(passwords2); continue; } - free(password2); + strv_free(passwords2); } - if (strlen(password)+1 < opt_key_size) { + strv_uniq(passwords); + + STRV_FOREACH(p, passwords) { char *c; - /* Pad password if necessary */ + if (strlen(*p)+1 >= opt_key_size) + continue; + /* Pad password if necessary */ if (!(c = new(char, opt_key_size))) { log_error("Out of memory."); goto finish; } - strncpy(c, password, opt_key_size); - free(password); - password = c; + strncpy(c, *p, opt_key_size); + free(*p); + *p = c; } } @@ -367,10 +442,20 @@ int main(int argc, char *argv[]) { if (key_file) k = crypt_activate_by_keyfile(cd, argv[2], CRYPT_ANY_SLOT, key_file, opt_key_size, flags); - else if (pass_volume_key) - k = crypt_activate_by_volume_key(cd, argv[2], password, opt_key_size, flags); - else - k = crypt_activate_by_passphrase(cd, argv[2], CRYPT_ANY_SLOT, password, strlen(password), flags); + else { + char **p; + + STRV_FOREACH(p, passwords) { + + if (pass_volume_key) + k = crypt_activate_by_volume_key(cd, argv[2], *p, opt_key_size, flags); + else + k = crypt_activate_by_passphrase(cd, argv[2], CRYPT_ANY_SLOT, *p, strlen(*p), flags); + + if (k >= 0) + break; + } + } if (k >= 0) break; @@ -421,9 +506,11 @@ finish: free(truncated_cipher); - free(password); + strv_free(passwords); free(description); + free(mount_point); + free(name_buffer); return r; } diff --git a/src/dbus-job.c b/src/dbus-job.c index 18da72d67a..2a33039bdb 100644 --- a/src/dbus-job.c +++ b/src/dbus-job.c @@ -97,7 +97,7 @@ static DBusHandlerResult bus_job_message_dispatch(Job *j, DBusConnection *connec if (!(reply = dbus_message_new_method_return(message))) goto oom; - job_free(j); + job_finish_and_invalidate(j, JOB_CANCELED); } else return bus_default_message_handler(j->manager, connection, message, INTROSPECTION, properties); @@ -295,10 +295,10 @@ oom: log_error("Failed to allocate job change signal."); } -void bus_job_send_removed_signal(Job *j, bool success) { +void bus_job_send_removed_signal(Job *j) { char *p = NULL; DBusMessage *m = NULL; - dbus_bool_t b = success; + const char *r; assert(j); @@ -314,10 +314,12 @@ void bus_job_send_removed_signal(Job *j, bool success) { if (!(m = dbus_message_new_signal("/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "JobRemoved"))) goto oom; + r = job_result_to_string(j->result); + if (!dbus_message_append_args(m, DBUS_TYPE_UINT32, &j->id, DBUS_TYPE_OBJECT_PATH, &p, - DBUS_TYPE_BOOLEAN, &b, + DBUS_TYPE_STRING, &r, DBUS_TYPE_INVALID)) goto oom; diff --git a/src/dbus-job.h b/src/dbus-job.h index 801e3e6349..103c2ff3ec 100644 --- a/src/dbus-job.h +++ b/src/dbus-job.h @@ -24,8 +24,10 @@ #include <dbus/dbus.h> +#include "job.h" + void bus_job_send_change_signal(Job *j); -void bus_job_send_removed_signal(Job *j, bool success); +void bus_job_send_removed_signal(Job *j); extern const DBusObjectPathVTable bus_job_vtable; diff --git a/src/dbus-manager.c b/src/dbus-manager.c index 6f98aa7308..1b3bddc348 100644 --- a/src/dbus-manager.c +++ b/src/dbus-manager.c @@ -143,7 +143,7 @@ " <signal name=\"JobRemoved\">\n" \ " <arg name=\"id\" type=\"u\"/>\n" \ " <arg name=\"job\" type=\"o\"/>\n" \ - " <arg name=\"success\" type=\"b\"/>\n" \ + " <arg name=\"result\" type=\"s\"/>\n" \ " </signal>" diff --git a/src/dbus-path.c b/src/dbus-path.c index 9692d6a247..6155792d0d 100644 --- a/src/dbus-path.c +++ b/src/dbus-path.c @@ -72,10 +72,24 @@ static int bus_path_append_paths(Manager *m, DBusMessageIter *i, const char *pro return 0; } +static int bus_path_append_unit(Manager *m, DBusMessageIter *i, const char *property, void *data) { + Unit *u = data; + const char *t; + + assert(m); + assert(i); + assert(property); + assert(u); + + t = u->path.unit ? u->path.unit->meta.id : ""; + + return dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &t) ? 0 : -ENOMEM; +} + DBusHandlerResult bus_path_message_handler(Unit *u, DBusConnection *c, DBusMessage *message) { const BusProperty properties[] = { BUS_UNIT_PROPERTIES, - { "org.freedesktop.systemd1.Path", "Unit", bus_property_append_string, "s", u->path.unit->meta.id }, + { "org.freedesktop.systemd1.Path", "Unit", bus_path_append_unit, "s", u }, { "org.freedesktop.systemd1.Path", "Paths", bus_path_append_paths, "a(ss)", u }, { NULL, NULL, NULL, NULL, NULL } }; diff --git a/src/dbus-timer.c b/src/dbus-timer.c index 56044661e7..f4c23e0ea7 100644 --- a/src/dbus-timer.c +++ b/src/dbus-timer.c @@ -96,10 +96,24 @@ static int bus_timer_append_timers(Manager *m, DBusMessageIter *i, const char *p return 0; } +static int bus_timer_append_unit(Manager *m, DBusMessageIter *i, const char *property, void *data) { + Unit *u = data; + const char *t; + + assert(m); + assert(i); + assert(property); + assert(u); + + t = u->timer.unit ? u->timer.unit->meta.id : ""; + + return dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &t) ? 0 : -ENOMEM; +} + DBusHandlerResult bus_timer_message_handler(Unit *u, DBusConnection *c, DBusMessage *message) { const BusProperty properties[] = { BUS_UNIT_PROPERTIES, - { "org.freedesktop.systemd1.Timer", "Unit", bus_property_append_string, "s", u->timer.unit->meta.id }, + { "org.freedesktop.systemd1.Timer", "Unit", bus_timer_append_unit, "s", u }, { "org.freedesktop.systemd1.Timer", "Timers", bus_timer_append_timers, "a(stt)", u }, { "org.freedesktop.systemd1.Timer", "NextElapseUSec", bus_property_append_usec, "t", &u->timer.next_elapse }, { NULL, NULL, NULL, NULL, NULL } diff --git a/src/dbus.c b/src/dbus.c index d7b80ba01d..965a3e2f21 100644 --- a/src/dbus.c +++ b/src/dbus.c @@ -418,7 +418,7 @@ static DBusHandlerResult api_bus_message_filter(DBusConnection *connection, DBus goto oom; } - /* On success we don't do anything, the service will be spwaned now */ + /* On success we don't do anything, the service will be spawned now */ } } @@ -1361,6 +1361,7 @@ static const char *error_to_dbus(int error) { return DBUS_ERROR_FILE_EXISTS; case -ETIMEDOUT: + case -ETIME: return DBUS_ERROR_TIMEOUT; case -EIO: diff --git a/src/detect-virt.c b/src/detect-virt.c new file mode 100644 index 0000000000..57f0176668 --- /dev/null +++ b/src/detect-virt.c @@ -0,0 +1,46 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <stdlib.h> +#include <stdbool.h> +#include <errno.h> +#include <string.h> + +#include "util.h" + +int main(int argc, char *argv[]) { + int r; + const char *id; + + /* This is mostly intended to be used for scripts which want + * to detect whether we are being run in a virtualized + * environment or not */ + + if ((r = detect_virtualization(&id)) < 0) { + log_error("Failed to check for virtualization: %s", strerror(-r)); + return EXIT_FAILURE; + } + + if (r > 0) + puts(id); + + return r == 0; +} diff --git a/src/device.c b/src/device.c index b9d8a2b9cf..ccf2935a91 100644 --- a/src/device.c +++ b/src/device.c @@ -65,7 +65,7 @@ static void device_init(Unit *u) { /* In contrast to all other unit types we timeout jobs waiting * for devices by default. This is because they otherwise wait - * indefinetely for plugged in devices, something which cannot + * indefinitely for plugged in devices, something which cannot * happen for the other units since their operations time out * anyway. */ d->meta.job_timeout = DEFAULT_TIMEOUT_USEC; @@ -542,7 +542,7 @@ void device_fd_event(Manager *m, int events) { if (!(dev = udev_monitor_receive_device(m->udev_monitor))) { /* * libudev might filter-out devices which pass the bloom filter, - * so getting NULL here is not neccessarily an error + * so getting NULL here is not necessarily an error */ return; } diff --git a/src/device.h b/src/device.h index ee50cfafc6..9a56a5205b 100644 --- a/src/device.h +++ b/src/device.h @@ -40,7 +40,7 @@ struct Device { char *sysfs; - /* In order to be able to distuingish dependencies on + /* In order to be able to distinguish dependencies on different device nodes we might end up creating multiple devices for the same sysfs path. We chain them up here. */ diff --git a/src/execute.c b/src/execute.c index e01cbde974..d6f09e26fe 100644 --- a/src/execute.c +++ b/src/execute.c @@ -592,7 +592,7 @@ static int get_user_creds(const char **username, uid_t *uid, gid_t *gid, const c /* If there are multiple users with the same id, make * sure to leave $USER to the configured value instead - * of the first occurence in the database. However if + * of the first occurrence in the database. However if * the uid was configured by a numeric uid, then let's * pick the real username from /etc/passwd. */ if (*username && p) @@ -617,7 +617,7 @@ static int enforce_groups(const ExecContext *context, const char *username, gid_ assert(context); - /* Lookup and ser GID and supplementary group list. Here too + /* Lookup and set GID and supplementary group list. Here too * we avoid NSS lookups for gid=0. */ if (context->group || username) { @@ -700,7 +700,7 @@ static int enforce_user(const ExecContext *context, uid_t uid) { /* First step: If we need to keep capabilities but * drop privileges we need to make sure we keep our - * caps, whiel we drop priviliges. */ + * caps, whiel we drop privileges. */ if (uid != 0) { int sb = context->secure_bits|SECURE_KEEP_CAPS; @@ -709,7 +709,7 @@ static int enforce_user(const ExecContext *context, uid_t uid) { return -errno; } - /* Second step: set the capabilites. This will reduce + /* Second step: set the capabilities. This will reduce * the capabilities to the minimum we need. */ if (!(d = cap_dup(context->capabilities))) @@ -780,7 +780,7 @@ static int setup_pam( assert(pam_env); /* We set up PAM in the parent process, then fork. The child - * will then stay around untill killed via PR_GET_PDEATHSIG or + * will then stay around until killed via PR_GET_PDEATHSIG or * systemd via the cgroup logic. It will then remove the PAM * session again. The parent process will exec() the actual * daemon. We do things this way to ensure that the main PID @@ -841,7 +841,7 @@ static int setup_pam( /* Wait until our parent died. This will most likely * not work since the kernel does not allow - * unpriviliged paretns kill their priviliged children + * unprivileged parents kill their privileged children * this way. We rely on the control groups kill logic * to do the rest for us. */ if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0) @@ -1242,7 +1242,7 @@ int exec_spawn(ExecCommand *command, goto fail; } - /* PR_GET_SECUREBITS is not priviliged, while + /* PR_GET_SECUREBITS is not privileged, while * PR_SET_SECUREBITS is. So to suppress * potential EPERMs we'll try not to call * PR_SET_SECUREBITS unless necessary. */ @@ -1779,7 +1779,7 @@ void exec_command_append_list(ExecCommand **l, ExecCommand *e) { assert(e); if (*l) { - /* It's kinda important that we keep the order here */ + /* It's kind of important, that we keep the order here */ LIST_FIND_TAIL(ExecCommand, command, *l, end); LIST_INSERT_AFTER(ExecCommand, command, *l, end, e); } else @@ -60,7 +60,7 @@ void job_free(Job *j) { /* Detach from next 'bigger' objects */ if (j->installed) { - bus_job_send_removed_signal(j, !j->failed); + bus_job_send_removed_signal(j); if (j->unit->meta.job == j) { j->unit->meta.job = NULL; @@ -101,7 +101,7 @@ JobDependency* job_dependency_new(Job *subject, Job *object, bool matters, bool /* Adds a new job link, which encodes that the 'subject' job * needs the 'object' job in some way. If 'subject' is NULL * this means the 'anchor' job (i.e. the one the user - * explcitily asked for) is the requester. */ + * explicitly asked for) is the requester. */ if (!(l = new0(JobDependency, 1))) return NULL; @@ -394,6 +394,7 @@ int job_run_and_invalidate(Job *j) { * wait */ if (r == -EBADR) r = 0; + break; case JOB_VERIFY_ACTIVE: { @@ -455,17 +456,17 @@ int job_run_and_invalidate(Job *j) { if ((j = manager_get_job(m, id))) { if (r == -EALREADY) - r = job_finish_and_invalidate(j, true); + r = job_finish_and_invalidate(j, JOB_DONE); else if (r == -EAGAIN) j->state = JOB_WAITING; else if (r < 0) - r = job_finish_and_invalidate(j, false); + r = job_finish_and_invalidate(j, JOB_FAILED); } return r; } -int job_finish_and_invalidate(Job *j, bool success) { +int job_finish_and_invalidate(Job *j, JobResult result) { Unit *u; Unit *other; JobType t; @@ -477,7 +478,7 @@ int job_finish_and_invalidate(Job *j, bool success) { job_add_to_dbus_queue(j); /* Patch restart jobs so that they become normal start jobs */ - if (success && (j->type == JOB_RESTART || j->type == JOB_TRY_RESTART)) { + if (result == JOB_DONE && (j->type == JOB_RESTART || j->type == JOB_TRY_RESTART)) { log_debug("Converting job %s/%s -> %s/%s", j->unit->meta.id, job_type_to_string(j->type), @@ -490,22 +491,26 @@ int job_finish_and_invalidate(Job *j, bool success) { return 0; } - j->failed = !success; + j->result = result; - log_debug("Job %s/%s finished, success=%s", j->unit->meta.id, job_type_to_string(j->type), yes_no(success)); + log_debug("Job %s/%s finished, result=%s", j->unit->meta.id, job_type_to_string(j->type), job_result_to_string(result)); - if (j->failed) + if (result == JOB_FAILED) j->manager->n_failed_jobs ++; u = j->unit; t = j->type; job_free(j); - if (!success && j->type == JOB_START) + if (result == JOB_FAILED && t == JOB_START) unit_status_printf(u, "Starting %s " ANSI_HIGHLIGHT_ON "failed" ANSI_HIGHLIGHT_OFF ", see 'systemctl status %s' for details.\n", unit_description(u), u->meta.id); + else if (result == JOB_TIMEOUT && t == JOB_START) + unit_status_printf(u, "Starting %s " ANSI_HIGHLIGHT_ON "timed out" ANSI_HIGHLIGHT_OFF ".\n", unit_description(u), u->meta.id); + else if (result == JOB_TIMEOUT && t == JOB_STOP) + unit_status_printf(u, "Stopping %s " ANSI_HIGHLIGHT_ON "timed out" ANSI_HIGHLIGHT_OFF ".\n", unit_description(u), u->meta.id); /* Fail depending jobs on failure */ - if (!success) { + if (result != JOB_DONE) { if (t == JOB_START || t == JOB_VERIFY_ACTIVE || @@ -516,14 +521,14 @@ int job_finish_and_invalidate(Job *j, bool success) { (other->meta.job->type == JOB_START || other->meta.job->type == JOB_VERIFY_ACTIVE || other->meta.job->type == JOB_RELOAD_OR_START)) - job_finish_and_invalidate(other->meta.job, false); + job_finish_and_invalidate(other->meta.job, JOB_DEPENDENCY); SET_FOREACH(other, u->meta.dependencies[UNIT_BOUND_BY], i) if (other->meta.job && (other->meta.job->type == JOB_START || other->meta.job->type == JOB_VERIFY_ACTIVE || other->meta.job->type == JOB_RELOAD_OR_START)) - job_finish_and_invalidate(other->meta.job, false); + job_finish_and_invalidate(other->meta.job, JOB_DEPENDENCY); SET_FOREACH(other, u->meta.dependencies[UNIT_REQUIRED_BY_OVERRIDABLE], i) if (other->meta.job && @@ -531,7 +536,7 @@ int job_finish_and_invalidate(Job *j, bool success) { (other->meta.job->type == JOB_START || other->meta.job->type == JOB_VERIFY_ACTIVE || other->meta.job->type == JOB_RELOAD_OR_START)) - job_finish_and_invalidate(other->meta.job, false); + job_finish_and_invalidate(other->meta.job, JOB_DEPENDENCY); } else if (t == JOB_STOP) { @@ -540,10 +545,17 @@ int job_finish_and_invalidate(Job *j, bool success) { (other->meta.job->type == JOB_START || other->meta.job->type == JOB_VERIFY_ACTIVE || other->meta.job->type == JOB_RELOAD_OR_START)) - job_finish_and_invalidate(other->meta.job, false); + job_finish_and_invalidate(other->meta.job, JOB_DEPENDENCY); } } + /* Trigger OnFailure dependencies that are not generated by + * the unit itself. We don't tread JOB_CANCELED as failure in + * this context. And JOB_FAILURE is already handled by the + * unit itself. */ + if (result == JOB_TIMEOUT || result == JOB_DEPENDENCY) + unit_trigger_on_failure(u); + /* Try to start the next jobs that can be started */ SET_FOREACH(other, u->meta.dependencies[UNIT_AFTER], i) if (other->meta.job) @@ -646,7 +658,7 @@ void job_timer_event(Job *j, uint64_t n_elapsed, Watch *w) { assert(w == &j->timer_watch); log_warning("Job %s/%s timed out.", j->unit->meta.id, job_type_to_string(j->type)); - job_finish_and_invalidate(j, false); + job_finish_and_invalidate(j, JOB_TIMEOUT); } static const char* const job_state_table[_JOB_STATE_MAX] = { @@ -676,3 +688,13 @@ static const char* const job_mode_table[_JOB_MODE_MAX] = { }; DEFINE_STRING_TABLE_LOOKUP(job_mode, JobMode); + +static const char* const job_result_table[_JOB_RESULT_MAX] = { + [JOB_DONE] = "done", + [JOB_CANCELED] = "canceled", + [JOB_TIMEOUT] = "timeout", + [JOB_FAILED] = "failed", + [JOB_DEPENDENCY] = "dependency" +}; + +DEFINE_STRING_TABLE_LOOKUP(job_result, JobResult); @@ -30,6 +30,7 @@ typedef struct JobDependency JobDependency; typedef enum JobType JobType; typedef enum JobState JobState; typedef enum JobMode JobMode; +typedef enum JobResult JobResult; #include "manager.h" #include "unit.h" @@ -71,6 +72,16 @@ enum JobMode { _JOB_MODE_INVALID = -1 }; +enum JobResult { + JOB_DONE, + JOB_CANCELED, + JOB_TIMEOUT, + JOB_FAILED, + JOB_DEPENDENCY, + _JOB_RESULT_MAX, + _JOB_RESULT_INVALID = -1 +}; + struct JobDependency { /* Encodes that the 'subject' job needs the 'object' job in * some way. This structure is used only while building a transaction. */ @@ -110,13 +121,14 @@ struct Job { DBusConnection *bus; char *bus_client; + JobResult result; + bool installed:1; bool in_run_queue:1; bool matters_to_anchor:1; bool override:1; bool in_dbus_queue:1; bool sent_dbus_new_signal:1; - bool failed:1; bool ignore_deps:1; }; @@ -146,7 +158,7 @@ int job_start_timer(Job *j); void job_timer_event(Job *j, uint64_t n_elapsed, Watch *w); int job_run_and_invalidate(Job *j); -int job_finish_and_invalidate(Job *j, bool success); +int job_finish_and_invalidate(Job *j, JobResult result); char *job_dbus_path(Job *j); @@ -159,4 +171,7 @@ JobState job_state_from_string(const char *s); const char* job_mode_to_string(JobMode t); JobMode job_mode_from_string(const char *s); +const char* job_result_to_string(JobResult t); +JobResult job_result_from_string(const char *s); + #endif diff --git a/src/kmsg-syslogd.c b/src/kmsg-syslogd.c index 2bdbb4b9f3..e23c9d3862 100644 --- a/src/kmsg-syslogd.c +++ b/src/kmsg-syslogd.c @@ -39,7 +39,7 @@ #include "fdset.h" #define SERVER_FD_MAX 16 -#define TIMEOUT ((int) (10*MSEC_PER_SEC)) +#define TIMEOUT ((int) (5*60*MSEC_PER_SEC)) typedef struct Stream Stream; diff --git a/src/label.c b/src/label.c index 218d0dfa06..09ded642fb 100644 --- a/src/label.c +++ b/src/label.c @@ -65,7 +65,7 @@ int label_init(void) { return r; } -int label_fix(const char *path) { +int label_fix(const char *path, bool ignore_enoent) { int r = 0; #ifdef HAVE_SELINUX @@ -90,6 +90,10 @@ int label_fix(const char *path) { /* If the FS doesn't support labels, then exit without warning */ if (r < 0 && errno == ENOTSUP) return 0; + + /* Ignore ENOENT in some cases */ + if (r < 0 && ignore_enoent && errno == ENOENT) + return 0; } } diff --git a/src/label.h b/src/label.h index f1bf5d6d5e..7ea11cdc55 100644 --- a/src/label.h +++ b/src/label.h @@ -23,11 +23,12 @@ ***/ #include <sys/types.h> +#include <stdbool.h> int label_init(void); void label_finish(void); -int label_fix(const char *path); +int label_fix(const char *path, bool ignore_enoent); int label_socket_set(const char *label); void label_socket_clear(void); diff --git a/src/load-fragment.c b/src/load-fragment.c index eaeaadaea4..2e67eccc00 100644 --- a/src/load-fragment.c +++ b/src/load-fragment.c @@ -1503,6 +1503,34 @@ static int config_parse_condition_kernel( return 0; } +static int config_parse_condition_virt( + const char *filename, + unsigned line, + const char *section, + const char *lvalue, + const char *rvalue, + void *data, + void *userdata) { + + Unit *u = data; + bool negate; + Condition *c; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + if ((negate = rvalue[0] == '!')) + rvalue++; + + if (!(c = condition_new(CONDITION_VIRTUALIZATION, rvalue, negate))) + return -ENOMEM; + + LIST_PREPEND(Condition, conditions, u->meta.conditions, c); + return 0; +} + static int config_parse_condition_null( const char *filename, unsigned line, @@ -1714,6 +1742,7 @@ static void dump_items(FILE *f, const ConfigItem *items) { { config_parse_condition_path, "CONDITION" }, { config_parse_condition_kernel, "CONDITION" }, { config_parse_condition_null, "CONDITION" }, + { config_parse_condition_virt, "CONDITION" }, }; assert(f); @@ -1838,6 +1867,7 @@ static int load_from_path(Unit *u, const char *path) { { "ConditionPathExists", config_parse_condition_path, u, "Unit" }, { "ConditionDirectoryNotEmpty", config_parse_condition_path, u, "Unit" }, { "ConditionKernelCommandLine", config_parse_condition_kernel, u, "Unit" }, + { "ConditionVirtualization",config_parse_condition_virt, u, "Unit" }, { "ConditionNull", config_parse_condition_null, u, "Unit" }, { "PIDFile", config_parse_path, &u->service.pid_file, "Service" }, @@ -45,7 +45,7 @@ static bool syslog_is_stream = false; static bool show_color = false; static bool show_location = false; -/* Akin to glibc's __abort_msg; which is private and we hance cannot +/* Akin to glibc's __abort_msg; which is private and we hence cannot * use here. */ static char *log_abort_msg = NULL; @@ -74,7 +74,7 @@ static int log_open_console(void) { return console_fd; } - log_debug("Succesfully opened /dev/console for logging."); + log_debug("Successfully opened /dev/console for logging."); } else console_fd = STDERR_FILENO; @@ -100,7 +100,7 @@ static int log_open_kmsg(void) { return -errno; } - log_debug("Succesfully opened /dev/kmsg for logging."); + log_debug("Successfully opened /dev/kmsg for logging."); return 0; } @@ -171,7 +171,7 @@ static int log_open_syslog(void) { } else syslog_is_stream = false; - log_debug("Succesfully opened syslog for logging."); + log_debug("Successfully opened syslog for logging."); return 0; diff --git a/src/logger.c b/src/logger.c index 342c307899..104d7c385b 100644 --- a/src/logger.c +++ b/src/logger.c @@ -38,9 +38,9 @@ #include "sd-daemon.h" #include "tcpwrap.h" -#define STREAMS_MAX 256 +#define STREAMS_MAX 4096 #define SERVER_FD_MAX 16 -#define TIMEOUT ((int) (10*MSEC_PER_SEC)) +#define TIMEOUT ((int) (5*60*MSEC_PER_SEC)) typedef struct Stream Stream; @@ -520,7 +520,7 @@ static int server_init(Server *s, unsigned n_sockets) { /* We use ev.data.ptr instead of ev.data.fd here, * since on 64bit archs fd is 32bit while a pointer is - * 64bit. To make sure we can easily distuingish fd + * 64bit. To make sure we can easily distinguish fd * values and pointer values we want to make sure to * write the full field unconditionally. */ diff --git a/src/main.c b/src/main.c index 96a282a34f..2c02217d89 100644 --- a/src/main.c +++ b/src/main.c @@ -858,7 +858,7 @@ static int prepare_reexecute(Manager *m, FILE **_f, FDSet **_fds) { assert(_fds); if ((r = manager_open_serialization(m, &f)) < 0) { - log_error("Failed to create serialization faile: %s", strerror(-r)); + log_error("Failed to create serialization file: %s", strerror(-r)); goto fail; } @@ -923,6 +923,8 @@ static struct dual_timestamp* parse_initrd_timestamp(struct dual_timestamp *t) { static void test_mtab(void) { char *p; + /* Check that /etc/mtab is a symlink */ + if (readlink_malloc("/etc/mtab", &p) >= 0) { bool b; @@ -933,9 +935,33 @@ static void test_mtab(void) { return; } - log_error("/etc/mtab is not a symlink or not pointing to /proc/self/mounts. " - "This is not supported anymore. " - "Please make sure to replace this file by a symlink to avoid incorrect or misleading mount(8) output."); + log_warning("/etc/mtab is not a symlink or not pointing to /proc/self/mounts. " + "This is not supported anymore. " + "Please make sure to replace this file by a symlink to avoid incorrect or misleading mount(8) output."); +} + +static void test_usr(void) { + struct stat a, b; + bool seperate = false; + + /* Check that /usr is not a seperate fs */ + + if (lstat("/", &a) >= 0 && lstat("/usr", &b) >= 0) + if (a.st_dev != b.st_dev) + seperate = true; + + /* This check won't work usually during boot, since /usr is + * probably not mounted yet, hence let's add a second + * check. We just check whether /usr is an empty directory. */ + + if (dir_is_empty("/usr") > 0) + seperate = true; + + if (!seperate) + return; + + log_warning("/usr appears to be on a different file system than /. This is not supported anymore. " + "Some things will probably break (sometimes even silently) in mysterious ways."); } int main(int argc, char *argv[]) { @@ -948,7 +974,7 @@ int main(int argc, char *argv[]) { char systemd[] = "systemd"; if (getpid() != 1 && strstr(program_invocation_short_name, "init")) { - /* This is compatbility support for SysV, where + /* This is compatibility support for SysV, where * calling init as a user is identical to telinit. */ errno = -ENOENT; @@ -1012,6 +1038,11 @@ int main(int argc, char *argv[]) { if (parse_argv(argc, argv) < 0) goto finish; + if (arg_action == ACTION_TEST && geteuid() == 0) { + log_error("Don't run test mode as root."); + goto finish; + } + /* If Plymouth is being run make sure we show the status, so * that there's something nice to see when people press Esc */ if (access("/dev/.systemd/plymouth", F_OK) >= 0) @@ -1103,6 +1134,7 @@ int main(int argc, char *argv[]) { mkdir_p("/dev/.systemd/ask-password/", 0755); test_mtab(); + test_usr(); } if ((r = manager_new(arg_running_as, &m)) < 0) { diff --git a/src/manager.c b/src/manager.c index f266aaa01c..194ad66a02 100644 --- a/src/manager.c +++ b/src/manager.c @@ -294,7 +294,7 @@ static unsigned manager_dispatch_cleanup_queue(Manager *m) { } enum { - GC_OFFSET_IN_PATH, /* This one is on the path we were travelling */ + GC_OFFSET_IN_PATH, /* This one is on the path we were traveling */ GC_OFFSET_UNSURE, /* No clue */ GC_OFFSET_GOOD, /* We still need this unit */ GC_OFFSET_BAD, /* We don't need this unit anymore */ @@ -768,7 +768,7 @@ static int delete_one_unmergeable_job(Manager *m, Job *j) { /* Tries to delete one item in the linked list * j->transaction_next->transaction_next->... that conflicts - * whith another one, in an attempt to make an inconsistent + * with another one, in an attempt to make an inconsistent * transaction work. */ /* We rely here on the fact that if a merged with b does not @@ -1286,7 +1286,7 @@ static int transaction_activate(Manager *m, JobMode mode, DBusError *e) { break; if (r != -EAGAIN) { - log_warning("Requested transaction contains unmergable jobs: %s", bus_error(e, r)); + log_warning("Requested transaction contains unmergeable jobs: %s", bus_error(e, r)); goto rollback; } @@ -1330,7 +1330,7 @@ static Job* transaction_add_one_job(Manager *m, JobType type, Unit *unit, bool o assert(m); assert(unit); - /* Looks for an axisting prospective job and returns that. If + /* Looks for an existing prospective job and returns that. If * it doesn't exist it is created and added to the prospective * jobs list. */ @@ -1823,7 +1823,7 @@ void manager_clear_jobs(Manager *m) { transaction_abort(m); while ((j = hashmap_first(m->jobs))) - job_free(j); + job_finish_and_invalidate(j, JOB_CANCELED); } unsigned manager_dispatch_run_queue(Manager *m) { diff --git a/src/manager.h b/src/manager.h index 19679686b1..efca4ff7f6 100644 --- a/src/manager.h +++ b/src/manager.h @@ -93,7 +93,7 @@ struct Watch { struct Manager { /* Note that the set of units we know of is allowed to be - * incosistent. However the subset of it that is loaded may + * inconsistent. However the subset of it that is loaded may * not, and the list of jobs may neither. */ /* Active jobs and units */ @@ -194,7 +194,7 @@ struct Manager { int gc_marker; unsigned n_in_gc_queue; - /* Make sure the user cannot accidentaly unmount our cgroup + /* Make sure the user cannot accidentally unmount our cgroup * file system */ int pin_cgroupfs_fd; diff --git a/src/mount-setup.c b/src/mount-setup.c index 64fb4765f6..5cbaee6be7 100644 --- a/src/mount-setup.c +++ b/src/mount-setup.c @@ -121,7 +121,7 @@ static int mount_one(const MountPoint *p) { return p->fatal ? -errno : 0; } - label_fix(p->where); + label_fix(p->where, false); return 0; } @@ -216,7 +216,7 @@ static int nftw_cb( if (ftwbuf->level == 0) return 0; - label_fix(fpath); + label_fix(fpath, true); return 0; }; diff --git a/src/mount.c b/src/mount.c index 102d88b6e0..88b54bbb29 100644 --- a/src/mount.c +++ b/src/mount.c @@ -306,7 +306,7 @@ static int mount_add_target_links(Mount *m) { return r; if (after) - if ((r = unit_add_dependency_by_name(tu, UNIT_AFTER, after, NULL, true)) < 0) + if ((r = unit_add_dependency_by_name(UNIT(m), UNIT_AFTER, after, NULL, true)) < 0) return r; if (automount && m->meta.manager->running_as == MANAGER_SYSTEM) { diff --git a/src/namespace.c b/src/namespace.c index 1d8f35ba57..54b22f494e 100644 --- a/src/namespace.c +++ b/src/namespace.c @@ -161,11 +161,11 @@ static int apply_mount(Path *p, const char *root_dir, const char *inaccessible_d /* The bind mount will always inherit the original * flags. If we want to set any flag we need - * to do so in a second indepdant step. */ + * to do so in a second independent step. */ if (flags) r = mount(NULL, where, NULL, MS_REMOUNT|MS_BIND|MS_REC|flags, NULL); - /* Avoid expontial growth of trees */ + /* Avoid exponential growth of trees */ if (r >= 0 && path_equal(p->path, "/")) r = mount(NULL, where, NULL, MS_REMOUNT|MS_BIND|MS_UNBINDABLE|flags, NULL); diff --git a/src/ratelimit.c b/src/ratelimit.c index 5adf1ae10d..1ddc83187f 100644 --- a/src/ratelimit.c +++ b/src/ratelimit.c @@ -38,25 +38,19 @@ bool ratelimit_test(RateLimit *r) { if (r->begin <= 0 || r->begin + r->interval < ts) { - - if (r->n_missed > 0) - log_warning("%u events suppressed", r->n_missed); - r->begin = ts; - /* Reset counters */ - r->n_printed = 0; - r->n_missed = 0; + /* Reset counter */ + r->num = 0; goto good; } - if (r->n_printed <= r->burst) + if (r->num <= r->burst) goto good; - r->n_missed++; return false; good: - r->n_printed++; + r->num++; return true; } diff --git a/src/ratelimit.h b/src/ratelimit.h index 2c77787d3c..a44ef70db4 100644 --- a/src/ratelimit.h +++ b/src/ratelimit.h @@ -28,15 +28,14 @@ typedef struct RateLimit { usec_t interval; usec_t begin; unsigned burst; - unsigned n_printed, n_missed; + unsigned num; } RateLimit; #define RATELIMIT_DEFINE(_name, _interval, _burst) \ RateLimit _name = { \ .interval = (_interval), \ .burst = (_burst), \ - .n_printed = 0, \ - .n_missed = 0, \ + .num = 0, \ .begin = 0 \ } @@ -45,8 +44,7 @@ typedef struct RateLimit { RateLimit *_r = &(v); \ _r->interval = (_interval); \ _r->burst = (_burst); \ - _r->n_printed = 0; \ - _r->n_missed = 0; \ + _r->num = 0; \ _r->begin = 0; \ } while (false); diff --git a/src/readahead-collect.c b/src/readahead-collect.c index 4ca6d74726..330d10776f 100644 --- a/src/readahead-collect.c +++ b/src/readahead-collect.c @@ -96,6 +96,9 @@ static int pack_file(FILE *pack, const char *fn, bool on_btrfs) { if (errno == ENOENT) return 0; + if (errno == EPERM || errno == EACCES) + return 0; + log_warning("open(%s) failed: %m", fn); r = -errno; goto finish; @@ -649,8 +652,8 @@ int main(int argc, char *argv[]) { return 0; } - if (running_in_vm()) { - log_info("Disabling readahead collector due to execution in virtual machine."); + if (detect_virtualization(NULL) > 0) { + log_info("Disabling readahead collector due to execution in virtualized environment."); return 0; } diff --git a/src/readahead-replay.c b/src/readahead-replay.c index e9c573a593..cd89654f98 100644 --- a/src/readahead-replay.c +++ b/src/readahead-replay.c @@ -62,7 +62,7 @@ static int unpack_file(FILE *pack) { if ((fd = open(fn, O_RDONLY|O_CLOEXEC|O_NOATIME|O_NOCTTY|O_NOFOLLOW)) < 0) { - if (errno != ENOENT) + if (errno != ENOENT && errno != EPERM && errno != EACCES) log_warning("open(%s) failed: %m", fn); } else if (file_verify(fd, fn, arg_file_size_max, &st) <= 0) { @@ -346,8 +346,8 @@ int main(int argc, char*argv[]) { return 0; } - if (running_in_vm()) { - log_info("Disabling readahead replay due to execution in virtual machine."); + if (detect_virtualization(NULL) > 0) { + log_info("Disabling readahead replay due to execution in virtualized environment."); return 0; } diff --git a/src/sd-daemon.h b/src/sd-daemon.h index d0a0a9459a..4b853a15be 100644 --- a/src/sd-daemon.h +++ b/src/sd-daemon.h @@ -185,7 +185,7 @@ int sd_is_socket_unix(int fd, int type, int listening, const char *path, size_t READY=1 Tells systemd that daemon startup is finished (only relevant for services of Type=notify). The passed argument is a boolean "1" or "0". Since there is - little value in signalling non-readiness the only + little value in signaling non-readiness the only value daemons should send is "READY=1". STATUS=... Passes a single-line status string back to systemd @@ -206,7 +206,7 @@ int sd_is_socket_unix(int fd, int type, int listening, const char *path, size_t fork off the process itself. Example: "MAINPID=4711" Daemons can choose to send additional variables. However, it is - recommened to prefix variable names not listed above with X_. + recommended to prefix variable names not listed above with X_. Returns a negative errno-style error code on failure. Returns > 0 if systemd could be notified, 0 if it couldn't possibly because diff --git a/src/securebits.h b/src/securebits.h index a5b99a3dbf..ba0bba5353 100644 --- a/src/securebits.h +++ b/src/securebits.h @@ -16,7 +16,7 @@ #define SECURE_NOROOT_LOCKED 1 /* make bit-0 immutable */ /* When set, setuid to/from uid 0 does not trigger capability-"fixup". - When unset, to provide compatiblility with old programs relying on + When unset, to provide compatibility with old programs relying on set*uid to gain/lose privilege, transitions to/from uid 0 cause capabilities to be gained/lost. */ #define SECURE_NO_SETUID_FIXUP 2 diff --git a/src/service.c b/src/service.c index e928d1a5e0..39a46d6cf5 100644 --- a/src/service.c +++ b/src/service.c @@ -335,7 +335,7 @@ static int sysv_translate_facility(const char *name, const char *filename, char /* Facilities starting with $ are most likely targets */ r = unit_name_build(n, NULL, ".target"); } else if (filename && streq(name, filename)) - /* Names equalling the file name of the services are redundant */ + /* Names equaling the file name of the services are redundant */ return 0; else /* Everything else we assume to be normal service names */ @@ -2496,16 +2496,26 @@ static const char *service_sub_state_to_string(Unit *u) { return service_state_to_string(SERVICE(u)->state); } -#ifdef HAVE_SYSV_COMPAT static bool service_check_gc(Unit *u) { Service *s = SERVICE(u); assert(s); - return !!s->sysv_path; -} + /* Never clean up services that still have a process around, + * even if the service is formally dead. */ + if (cgroup_good(s) > 0 || + main_pid_good(s) > 0 || + control_pid_good(s) > 0) + return true; + +#ifdef HAVE_SYSV_COMPAT + if (s->sysv_path) + return true; #endif + return false; +} + static bool service_check_snapshot(Unit *u) { Service *s = SERVICE(u); @@ -2767,7 +2777,7 @@ static void service_timer_event(Unit *u, uint64_t elapsed, Watch* w) { break; case SERVICE_STOP_SIGKILL: - /* Uh, wie sent a SIGKILL and it is still not gone? + /* Uh, we sent a SIGKILL and it is still not gone? * Must be something we cannot kill, so let's just be * weirded out and continue */ @@ -3042,7 +3052,7 @@ static int service_enumerate(Manager *m) { /* We honour K links only for halt/reboot. For the normal * runlevels we assume the stop jobs will be implicitly added - * by the core logic. Also, we don't really distuingish here + * by the core logic. Also, we don't really distinguish here * between the runlevels 0 and 6 and just add them to the * special shutdown target. On SUSE the boot.d/ runlevel is * also used for shutdown, so we add links for that too to the @@ -3317,9 +3327,7 @@ const UnitVTable service_vtable = { .active_state = service_active_state, .sub_state_to_string = service_sub_state_to_string, -#ifdef HAVE_SYSV_COMPAT .check_gc = service_check_gc, -#endif .check_snapshot = service_check_snapshot, .sigchld_event = service_sigchld_event, diff --git a/src/shutdown.c b/src/shutdown.c index 23b9f1b545..5c082cf4a5 100644 --- a/src/shutdown.c +++ b/src/shutdown.c @@ -334,7 +334,7 @@ int main(int argc, char *argv[]) { } if (retries >= FINALIZE_ATTEMPTS) - log_error("Too many interations, giving up."); + log_error("Too many iterations, giving up."); execute_directory(SYSTEM_SHUTDOWN_PATH, NULL, NULL); diff --git a/src/socket.c b/src/socket.c index c41130ce72..96c649b0ab 100644 --- a/src/socket.c +++ b/src/socket.c @@ -1363,7 +1363,7 @@ static int socket_start(Unit *u) { if (s->service->meta.load_state != UNIT_LOADED) return -ENOENT; - /* If the service is alredy active we cannot start the + /* If the service is already active we cannot start the * socket */ if (s->service->state != SERVICE_DEAD && s->service->state != SERVICE_FAILED && @@ -1798,7 +1798,7 @@ void socket_connection_unref(Socket *s) { /* The service is dead. Yay! * - * This is strictly for one-onstance-per-connection + * This is strictly for one-instance-per-connection * services. */ assert(s->n_connections > 0); diff --git a/src/strv.c b/src/strv.c index d1c7b2c32d..c8ff5745e8 100644 --- a/src/strv.c +++ b/src/strv.c @@ -355,7 +355,7 @@ char **strv_remove(char **l, const char *s) { if (!l) return NULL; - /* Drops every occurence of s in the string list */ + /* Drops every occurrence of s in the string list */ for (f = t = l; *f; f++) { @@ -379,7 +379,7 @@ static int env_append(char **r, char ***k, char **a) { return 0; /* Add the entries of a to *k unless they already exist in *r - * in which case they are overriden instead. This assumes + * in which case they are overridden instead. This assumes * there is enough space in the r array. */ for (; *a; a++) { @@ -474,7 +474,7 @@ char **strv_env_delete(char **x, unsigned n_lists, ...) { char **l, **k, **r, **j; va_list ap; - /* Deletes every entry fromx that is mentioned in the other + /* Deletes every entry from x that is mentioned in the other * string lists */ n = strv_length(x); @@ -577,3 +577,45 @@ char **strv_env_clean(char **l) { return ret; } + +char **strv_parse_nulstr(const char *s, size_t l) { + const char *p; + unsigned c = 0, i = 0; + char **v; + + assert(s || l <= 0); + + if (l <= 0) + return strv_new(NULL, NULL); + + for (p = s; p < s + l; p++) + if (*p == 0) + c++; + + if (s[l-1] != 0) + c++; + + if (!(v = new0(char*, c+1))) + return NULL; + + p = s; + while (p < s + l) { + const char *e; + + e = memchr(p, 0, s + l - p); + + if (!(v[i++] = strndup(p, e ? e - p : s + l - p))) { + strv_free(v); + return NULL; + } + + if (!e) + break; + + p = e + 1; + } + + assert(i == c); + + return v; +} diff --git a/src/strv.h b/src/strv.h index 5af84ee41f..064576ce1e 100644 --- a/src/strv.h +++ b/src/strv.h @@ -65,6 +65,8 @@ char *strv_env_get(char **x, const char *n); char **strv_env_clean(char **l); +char **strv_parse_nulstr(const char *s, size_t l); + #define STRV_FOREACH(s, l) \ for ((s) = (l); (s) && *(s); (s)++) diff --git a/src/swap.h b/src/swap.h index c39caa6658..0d5c9a23f0 100644 --- a/src/swap.h +++ b/src/swap.h @@ -89,7 +89,7 @@ struct Swap { Watch timer_watch; - /* In order to be able to distuingish dependencies on + /* In order to be able to distinguish dependencies on different device nodes we might end up creating multiple devices for the same swap. We chain them up here. */ diff --git a/src/systemadm.vala b/src/systemadm.vala index 33777b6b8b..c262794cb7 100644 --- a/src/systemadm.vala +++ b/src/systemadm.vala @@ -762,7 +762,7 @@ public class MainWindow : Window { } while (unit_model.iter_next(ref iter)); } - public void on_job_removed(uint32 id, ObjectPath path, bool success) { + public void on_job_removed(uint32 id, ObjectPath path, string res) { TreeIter iter; if (!(job_model.get_iter_first(out iter))) return; diff --git a/src/systemctl.c b/src/systemctl.c index 4a8b9a196f..c08e78c26d 100644 --- a/src/systemctl.c +++ b/src/systemctl.c @@ -112,6 +112,7 @@ static enum dot { static bool private_bus = false; static pid_t pager_pid = 0; +static pid_t agent_pid = 0; static int daemon_reload(DBusConnection *bus, char **args, unsigned n); static void pager_open(void); @@ -132,7 +133,10 @@ static bool on_tty(void) { } static void spawn_ask_password_agent(void) { - pid_t parent, child; + pid_t parent; + + if (agent_pid > 0) + return; /* We check STDIN here, not STDOUT, since this is about input, * not output */ @@ -150,16 +154,11 @@ static void spawn_ask_password_agent(void) { /* Spawns a temporary TTY agent, making sure it goes away when * we go away */ - if ((child = fork()) < 0) + if ((agent_pid = fork()) < 0) return; - if (child == 0) { + if (agent_pid == 0) { /* In the child */ - const char * const args[] = { - SYSTEMD_TTY_ASK_PASSWORD_AGENT_BINARY_PATH, - "--watch", - NULL - }; int fd; bool stdout_is_tty, stderr_is_tty; @@ -202,7 +201,9 @@ static void spawn_ask_password_agent(void) { close(fd); } - execv(args[0], (char **) args); + execl(SYSTEMD_TTY_ASK_PASSWORD_AGENT_BINARY_PATH, SYSTEMD_TTY_ASK_PASSWORD_AGENT_BINARY_PATH, "--watch", NULL); + + log_error("Unable to execute agent: %m"); _exit(EXIT_FAILURE); } } @@ -1120,7 +1121,7 @@ finish: typedef struct WaitData { Set *set; - bool failed; + char *result; } WaitData; static DBusHandlerResult wait_filter(DBusConnection *connection, DBusMessage *message, void *data) { @@ -1144,26 +1145,52 @@ static DBusHandlerResult wait_filter(DBusConnection *connection, DBusMessage *me } else if (dbus_message_is_signal(message, "org.freedesktop.systemd1.Manager", "JobRemoved")) { uint32_t id; - const char *path; + const char *path, *result; dbus_bool_t success = true; - if (!dbus_message_get_args(message, &error, - DBUS_TYPE_UINT32, &id, - DBUS_TYPE_OBJECT_PATH, &path, - DBUS_TYPE_BOOLEAN, &success, - DBUS_TYPE_INVALID)) - log_error("Failed to parse message: %s", bus_error_message(&error)); - else { + if (dbus_message_get_args(message, &error, + DBUS_TYPE_UINT32, &id, + DBUS_TYPE_OBJECT_PATH, &path, + DBUS_TYPE_STRING, &result, + DBUS_TYPE_INVALID)) { + char *p; + + if ((p = set_remove(d->set, (char*) path))) + free(p); + + if (*result) + d->result = strdup(result); + + goto finish; + } +#ifndef LEGACY + dbus_error_free(&error); + + if (dbus_message_get_args(message, &error, + DBUS_TYPE_UINT32, &id, + DBUS_TYPE_OBJECT_PATH, &path, + DBUS_TYPE_BOOLEAN, &success, + DBUS_TYPE_INVALID)) { char *p; + /* Compatibility with older systemd versions < + * 19 during upgrades. This should be dropped + * one day */ + if ((p = set_remove(d->set, (char*) path))) free(p); if (!success) - d->failed = true; + d->result = strdup("failed"); + + goto finish; } +#endif + + log_error("Failed to parse message: %s", bus_error_message(&error)); } +finish: dbus_error_free(&error); return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } @@ -1204,7 +1231,6 @@ static int wait_for_jobs(DBusConnection *bus, Set *s) { zero(d); d.set = s; - d.failed = false; if (!dbus_connection_add_filter(bus, wait_filter, &d, NULL)) { log_error("Failed to add filter."); @@ -1216,10 +1242,27 @@ static int wait_for_jobs(DBusConnection *bus, Set *s) { dbus_connection_read_write_dispatch(bus, -1)) ; - if (!arg_quiet && d.failed) - log_error("Job failed. See system logs and 'systemctl status' for details."); + if (!arg_quiet && d.result) { + if (streq(d.result, "timeout")) + log_error("Job timed out."); + else if (streq(d.result, "canceled")) + log_error("Job canceled."); + else if (streq(d.result, "dependency")) + log_error("A dependency job failed. See system logs for details."); + else if (!streq(d.result, "done")) + log_error("Job failed. See system logs and 'systemctl status' for details."); + } + + if (streq_ptr(d.result, "timeout")) + r = -ETIME; + else if (streq_ptr(d.result, "canceled")) + r = -ECANCELED; + else if (!streq_ptr(d.result, "done")) + r = -EIO; + else + r = 0; - r = d.failed ? -EIO : 0; + free(d.result); finish: /* This is slightly dirty, since we don't undo the filter registration. */ @@ -2750,11 +2793,12 @@ static DBusHandlerResult monitor_filter(DBusConnection *connection, DBusMessage } else if (dbus_message_is_signal(message, "org.freedesktop.systemd1.Manager", "JobNew") || dbus_message_is_signal(message, "org.freedesktop.systemd1.Manager", "JobRemoved")) { uint32_t id; - const char *path; + const char *path, *result; if (!dbus_message_get_args(message, &error, DBUS_TYPE_UINT32, &id, DBUS_TYPE_OBJECT_PATH, &path, + DBUS_TYPE_STRING, &result, DBUS_TYPE_INVALID)) log_error("Failed to parse message: %s", bus_error_message(&error)); else if (streq(dbus_message_get_member(message), "JobNew")) @@ -4668,6 +4712,18 @@ static int parse_time_spec(const char *t, usec_t *_u) { return 0; } +static 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; +} + static int shutdown_parse_argv(int argc, char *argv[]) { enum { @@ -4705,7 +4761,10 @@ static int shutdown_parse_argv(int argc, char *argv[]) { break; case 'r': - arg_action = ACTION_REBOOT; + if (kexec_loaded()) + arg_action = ACTION_KEXEC; + else + arg_action = ACTION_REBOOT; break; case 'h': @@ -4898,7 +4957,10 @@ static int parse_argv(int argc, char *argv[]) { arg_action = ACTION_POWEROFF; return halt_parse_argv(argc, argv); } else if (strstr(program_invocation_short_name, "reboot")) { - arg_action = ACTION_REBOOT; + if (kexec_loaded()) + arg_action = ACTION_KEXEC; + else + arg_action = ACTION_REBOOT; return halt_parse_argv(argc, argv); } else if (strstr(program_invocation_short_name, "shutdown")) { arg_action = ACTION_POWEROFF; @@ -5386,6 +5448,7 @@ static int runlevel_main(void) { static void pager_open(void) { int fd[2]; const char *pager; + pid_t parent_pid; if (pager_pid > 0) return; @@ -5406,6 +5469,8 @@ static void pager_open(void) { return; } + parent_pid = getpid(); + pager_pid = fork(); if (pager_pid < 0) { log_error("Failed to fork pager: %m"); @@ -5421,7 +5486,14 @@ static void pager_open(void) { setenv("LESS", "FRSX", 0); - prctl(PR_SET_PDEATHSIG, SIGTERM); + /* Make sure the pager goes away when the parent dies */ + if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0) + _exit(EXIT_FAILURE); + + /* Check whether our parent died before we were able + * to set the death signal */ + if (getppid() != parent_pid) + _exit(EXIT_SUCCESS); if (pager) { execlp(pager, pager, NULL); @@ -5462,6 +5534,18 @@ static void pager_close(void) { pager_pid = 0; } +static void agent_close(void) { + siginfo_t dummy; + + if (agent_pid <= 0) + return; + + /* Inform agent that we are done */ + kill(agent_pid, SIGTERM); + wait_for_terminate(agent_pid, &dummy); + agent_pid = 0; +} + int main(int argc, char*argv[]) { int r, retval = EXIT_FAILURE; DBusConnection *bus = NULL; @@ -5498,6 +5582,7 @@ int main(int argc, char*argv[]) { case ACTION_HALT: case ACTION_POWEROFF: case ACTION_REBOOT: + case ACTION_KEXEC: r = halt_main(bus); break; @@ -5543,6 +5628,7 @@ finish: strv_free(arg_property); pager_close(); + agent_close(); return retval; } diff --git a/src/systemd-interfaces.vala b/src/systemd-interfaces.vala index e3b7941288..a380f7970b 100644 --- a/src/systemd-interfaces.vala +++ b/src/systemd-interfaces.vala @@ -85,7 +85,7 @@ public interface Manager : DBusProxy { public abstract signal void unit_new(string id, ObjectPath path); public abstract signal void unit_removed(string id, ObjectPath path); public abstract signal void job_new(uint32 id, ObjectPath path); - public abstract signal void job_removed(uint32 id, ObjectPath path, bool success); + public abstract signal void job_removed(uint32 id, ObjectPath path, string res); } [DBus (name = "org.freedesktop.systemd1.Unit")] diff --git a/src/test-engine.c b/src/test-engine.c index 157b7f9fda..60619b9d36 100644 --- a/src/test-engine.c +++ b/src/test-engine.c @@ -74,14 +74,14 @@ int main(int argc, char *argv[]) { assert_se(manager_add_job(m, JOB_START, g, JOB_REPLACE, false, NULL, &j) == 0); manager_dump_jobs(m, stdout, "\t"); - printf("Test7: (Unmeargable job type, fail)\n"); + printf("Test7: (Unmergeable job type, fail)\n"); assert_se(manager_add_job(m, JOB_STOP, g, JOB_FAIL, false, NULL, &j) == -EEXIST); printf("Test8: (Mergeable job type, fail)\n"); assert_se(manager_add_job(m, JOB_RESTART, g, JOB_FAIL, false, NULL, &j) == 0); manager_dump_jobs(m, stdout, "\t"); - printf("Test9: (Unmeargable job type, replace)\n"); + printf("Test9: (Unmergeable job type, replace)\n"); assert_se(manager_add_job(m, JOB_STOP, g, JOB_REPLACE, false, NULL, &j) == 0); manager_dump_jobs(m, stdout, "\t"); @@ -89,7 +89,7 @@ int main(int argc, char *argv[]) { assert_se(manager_load_unit(m, "h.service", NULL, NULL, &h) >= 0); manager_dump_units(m, stdout, "\t"); - printf("Test10: (Unmeargable job type of auxiliary job, fail)\n"); + printf("Test10: (Unmergeable job type of auxiliary job, fail)\n"); assert_se(manager_add_job(m, JOB_START, h, JOB_FAIL, false, NULL, &j) == 0); manager_dump_jobs(m, stdout, "\t"); diff --git a/src/test-env-replace.c b/src/test-env-replace.c index 4188c67dde..05dbacd7df 100644 --- a/src/test-env-replace.c +++ b/src/test-env-replace.c @@ -48,6 +48,14 @@ int main(int argc, char *argv[]) { }; char **i, **r, *t, **a, **b; + const char nulstr[] = "fuck\0fuck2\0fuck3\0\0fuck5\0\0xxx"; + + a = strv_parse_nulstr(nulstr, sizeof(nulstr)-1); + + STRV_FOREACH(i, a) + printf("nulstr--%s\n", *i); + + strv_free(a); r = replace_env_argv((char**) line, (char**) env); diff --git a/src/test-job-type.c b/src/test-job-type.c index 9e2fc35f4e..9de21e1823 100644 --- a/src/test-job-type.c +++ b/src/test-job-type.c @@ -55,7 +55,7 @@ int main(int argc, char*argv[]) { assert(!job_type_is_mergeable(b, c) || job_type_is_mergeable(d, c)); /* Verify that if a merged - * with b is not mergable with + * with b is not mergeable with * c then either a or b is not * mergeable with c either. */ assert(job_type_is_mergeable(d, c) || !job_type_is_mergeable(a, c) || !job_type_is_mergeable(b, c)); diff --git a/src/tmpfiles.c b/src/tmpfiles.c index 01668da87e..0c3b88d35b 100644 --- a/src/tmpfiles.c +++ b/src/tmpfiles.c @@ -147,6 +147,8 @@ static void load_unix_sockets(void) { if (!(s = strdup(p))) goto fail; + path_kill_slashes(s); + if ((k = set_put(unix_sockets, s)) < 0) { free(s); @@ -301,6 +303,10 @@ static int dir_cleanup( if (S_ISSOCK(s.st_mode) && unix_socket_alive(sub_path)) continue; + /* Ignore device nodes */ + if (S_ISCHR(s.st_mode) || S_ISBLK(s.st_mode)) + continue; + age = MAX3(timespec_load(&s.st_mtim), timespec_load(&s.st_atim), timespec_load(&s.st_ctim)); @@ -501,7 +507,7 @@ static int create_item(Item *i) { break; } - if ((r = label_fix(i->path)) < 0) + if ((r = label_fix(i->path, false)) < 0) goto finish; log_debug("%s created successfully.", i->path); @@ -846,7 +852,7 @@ static int parse_argv(int argc, char *argv[]) { } if (!arg_clean && !arg_create && !arg_remove) { - log_error("You need to specify at leat one of --clean, --create or --remove."); + log_error("You need to specify at least one of --clean, --create or --remove."); return -EINVAL; } diff --git a/src/tty-ask-password-agent.c b/src/tty-ask-password-agent.c index 00496e7119..a9d06ac001 100644 --- a/src/tty-ask-password-agent.c +++ b/src/tty-ask-password-agent.c @@ -37,6 +37,7 @@ #include "utmp-wtmp.h" #include "socket-util.h" #include "ask-password-api.h" +#include "strv.h" static enum { ACTION_LIST, @@ -48,7 +49,13 @@ static enum { static bool arg_plymouth = false; static bool arg_console = false; -static int ask_password_plymouth(const char *message, usec_t until, const char *flag_file, char **_passphrase) { +static int ask_password_plymouth( + const char *message, + usec_t until, + const char *flag_file, + bool accept_cached, + char ***_passphrases) { + int fd = -1, notify = -1; union sockaddr_union sa; char *packet = NULL; @@ -62,6 +69,8 @@ static int ask_password_plymouth(const char *message, usec_t until, const char * POLL_INOTIFY }; + assert(_passphrases); + if (flag_file) { if ((notify = inotify_init1(IN_CLOEXEC|IN_NONBLOCK)) < 0) { r = -errno; @@ -83,12 +92,18 @@ static int ask_password_plymouth(const char *message, usec_t until, const char * sa.sa.sa_family = AF_UNIX; strncpy(sa.un.sun_path+1, "/org/freedesktop/plymouthd", sizeof(sa.un.sun_path)-1); if (connect(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + 1 + strlen(sa.un.sun_path+1)) < 0) { - log_error("FAILED TO CONNECT: %m"); + log_error("Failed to connect to Plymouth: %m"); r = -errno; goto finish; } - if (asprintf(&packet, "*\002%c%s%n", (int) (strlen(message) + 1), message, &n) < 0) { + if (accept_cached) { + packet = strdup("c"); + n = 1; + } else + asprintf(&packet, "*\002%c%s%n", (int) (strlen(message) + 1), message, &n); + + if (!packet) { r = -ENOMEM; goto finish; } @@ -113,7 +128,7 @@ static int ask_password_plymouth(const char *message, usec_t until, const char * y = now(CLOCK_MONOTONIC); if (y > until) { - r = -ETIMEDOUT; + r = -ETIME; goto finish; } @@ -134,7 +149,7 @@ static int ask_password_plymouth(const char *message, usec_t until, const char * r = -errno; goto finish; } else if (j == 0) { - r = -ETIMEDOUT; + r = -ETIME; goto finish; } @@ -155,15 +170,38 @@ static int ask_password_plymouth(const char *message, usec_t until, const char * continue; if (buffer[0] == 5) { + + if (accept_cached) { + /* Hmm, first try with cached + * passwords failed, so let's retry + * with a normal password request */ + free(packet); + packet = NULL; + + if (asprintf(&packet, "*\002%c%s%n", (int) (strlen(message) + 1), message, &n) < 0) { + r = -ENOMEM; + goto finish; + } + + if ((k = loop_write(fd, packet, n+1, true)) != n+1) { + r = k < 0 ? (int) k : -EIO; + goto finish; + } + + accept_cached = false; + p = 0; + continue; + } + /* No password, because UI not shown */ r = -ENOENT; goto finish; - } else if (buffer[0] == 2) { + } else if (buffer[0] == 2 || buffer[0] == 9) { uint32_t size; - char *s; + char **l; - /* One answer */ + /* One ore more answers */ if (p < 5) continue; @@ -176,13 +214,14 @@ static int ask_password_plymouth(const char *message, usec_t until, const char * if (p-5 < size) continue; - if (!(s = strndup(buffer + 5, size))) { + if (!(l = strv_parse_nulstr(buffer + 5, size))) { r = -ENOMEM; goto finish; } - *_passphrase = s; + *_passphrases = l; break; + } else { /* Unknown packet */ r = -EIO; @@ -209,12 +248,14 @@ static int parse_password(const char *filename, char **wall) { uint64_t not_after = 0; unsigned pid = 0; int socket_fd = -1; + bool accept_cached = false; const ConfigItem items[] = { - { "Socket", config_parse_string, &socket_name, "Ask" }, - { "NotAfter", config_parse_uint64, ¬_after, "Ask" }, - { "Message", config_parse_string, &message, "Ask" }, - { "PID", config_parse_unsigned, &pid, "Ask" }, + { "Socket", config_parse_string, &socket_name, "Ask" }, + { "NotAfter", config_parse_uint64, ¬_after, "Ask" }, + { "Message", config_parse_string, &message, "Ask" }, + { "PID", config_parse_unsigned, &pid, "Ask" }, + { "AcceptCached", config_parse_bool, &accept_cached, "Ask" }, { NULL, NULL, NULL, NULL } }; @@ -274,7 +315,7 @@ static int parse_password(const char *filename, char **wall) { struct sockaddr sa; struct sockaddr_un un; } sa; - char *password; + size_t packet_length = 0; assert(arg_action == ACTION_QUERY || arg_action == ACTION_WATCH); @@ -288,10 +329,32 @@ static int parse_password(const char *filename, char **wall) { goto finish; } - if (arg_plymouth) - r = ask_password_plymouth(message, not_after, filename, &password); - else { + if (arg_plymouth) { + char **passwords = NULL; + + if ((r = ask_password_plymouth(message, not_after, filename, accept_cached, &passwords)) >= 0) { + char **p; + + packet_length = 1; + STRV_FOREACH(p, passwords) + packet_length += strlen(*p) + 1; + + if (!(packet = new(char, packet_length))) + r = -ENOMEM; + else { + char *d; + + packet[0] = '+'; + d = packet+1; + + STRV_FOREACH(p, passwords) + d = stpcpy(d, *p) + 1; + } + } + + } else { int tty_fd = -1; + char *password; if (arg_console) if ((tty_fd = acquire_terminal("/dev/console", false, false, false)) < 0) { @@ -305,16 +368,25 @@ static int parse_password(const char *filename, char **wall) { close_nointr_nofail(tty_fd); release_terminal(); } + + asprintf(&packet, "+%s", password); + free(password); + + packet_length = strlen(packet); + } + + if (r == -ETIME || r == -ENOENT) { + /* If the query went away, that's OK */ + r = 0; + goto finish; } if (r < 0) { + log_error("Failed to query password: %s", strerror(-r)); goto finish; } - asprintf(&packet, "+%s", password); - free(password); - if (!packet) { log_error("Out of memory"); r = -ENOMEM; @@ -331,7 +403,7 @@ static int parse_password(const char *filename, char **wall) { sa.un.sun_family = AF_UNIX; strncpy(sa.un.sun_path, socket_name, sizeof(sa.un.sun_path)); - if (sendto(socket_fd, packet, strlen(packet), MSG_NOSIGNAL, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(socket_name)) < 0) { + if (sendto(socket_fd, packet, packet_length, MSG_NOSIGNAL, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(socket_name)) < 0) { log_error("Failed to send: %m"); r = -errno; goto finish; @@ -353,13 +425,13 @@ finish: static int wall_tty_block(void) { char *p; - const char *t; - int fd; + int fd, r; + dev_t devnr; - if (!(t = ttyname(STDIN_FILENO))) - return -errno; + if ((r = get_ctty_devnr(&devnr)) < 0) + return -r; - if (asprintf(&p, "/dev/.systemd/ask-password-block/%s", file_name_from_path(t)) < 0) + if (asprintf(&p, "/dev/.systemd/ask-password-block/%u:%u", major(devnr), minor(devnr)) < 0) return -ENOMEM; mkdir_parents(p, 0700); @@ -375,8 +447,25 @@ static int wall_tty_block(void) { } static bool wall_tty_match(const char *path) { - int fd; + int fd, k; char *p; + struct stat st; + + if (path_is_absolute(path)) + k = lstat(path, &st); + else { + if (asprintf(&p, "/dev/%s", path) < 0) + return true; + + k = lstat(p, &st); + free(p); + } + + if (k < 0) + return true; + + if (!S_ISCHR(st.st_mode)) + return true; /* We use named pipes to ensure that wall messages suggesting * password entry are not printed over password prompts @@ -386,7 +475,7 @@ static bool wall_tty_match(const char *path) { * advantage that the block will automatically go away if the * process dies. */ - if (asprintf(&p, "/dev/.systemd/ask-password-block/%s", file_name_from_path(path)) < 0) + if (asprintf(&p, "/dev/.systemd/ask-password-block/%u:%u", major(st.st_rdev), minor(st.st_rdev)) < 0) return true; fd = open(p, O_WRONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY); @@ -536,8 +625,8 @@ static int help(void) { " -h --help Show this help\n" " --list Show pending password requests\n" " --query Process pending password requests\n" - " --watch Continously process password requests\n" - " --wall Continously forward password requests to wall\n" + " --watch Continuously process password requests\n" + " --wall Continuously forward password requests to wall\n" " --plymouth Ask question with Plymouth instead of on TTY\n" " --console Ask question on /dev/console instead of current TTY\n", program_invocation_short_name); diff --git a/src/unit-name.c b/src/unit-name.c index debf2b2653..be4e73edcc 100644 --- a/src/unit-name.c +++ b/src/unit-name.c @@ -213,7 +213,7 @@ char *unit_name_build_escape(const char *prefix, const char *instance, const cha * suffix and makes a nice string suitable as unit name of it, * escaping all weird chars on the way. * - * / becomes ., and all chars not alloweed in a unit name get + * / becomes ., and all chars not allowed in a unit name get * escaped as \xFF, including \ and ., of course. This * escaping is hence reversible. * diff --git a/src/unit.c b/src/unit.c index 0d5312376c..359cb2d084 100644 --- a/src/unit.c +++ b/src/unit.c @@ -736,7 +736,7 @@ int unit_add_default_target_dependency(Unit *u, Unit *target) { if (target->meta.type != UNIT_TARGET) return 0; - /* Only add the dependency if boths units are loaded, so that + /* Only add the dependency if both units are loaded, so that * that loop check below is reliable */ if (u->meta.load_state != UNIT_LOADED || target->meta.load_state != UNIT_LOADED) @@ -1071,6 +1071,16 @@ static void retroactively_stop_dependencies(Unit *u) { unit_check_unneeded(other); } +void unit_trigger_on_failure(Unit *u) { + Unit *other; + Iterator i; + + assert(u); + + SET_FOREACH(other, u->meta.dependencies[UNIT_ON_FAILURE], i) + manager_add_job(u->meta.manager, JOB_START, other, JOB_REPLACE, true, NULL, NULL); +} + void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_success) { dual_timestamp ts; bool unexpected; @@ -1114,7 +1124,7 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su job_add_to_run_queue(u->meta.job); /* Let's check whether this state change constitutes a - * finished job, or maybe cotradicts a running job and + * finished job, or maybe contradicts a running job and * hence needs to invalidate jobs. */ switch (u->meta.job->type) { @@ -1123,12 +1133,12 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su case JOB_VERIFY_ACTIVE: if (UNIT_IS_ACTIVE_OR_RELOADING(ns)) - job_finish_and_invalidate(u->meta.job, true); + job_finish_and_invalidate(u->meta.job, JOB_DONE); else if (u->meta.job->state == JOB_RUNNING && ns != UNIT_ACTIVATING) { unexpected = true; if (UNIT_IS_INACTIVE_OR_FAILED(ns)) - job_finish_and_invalidate(u->meta.job, ns != UNIT_FAILED); + job_finish_and_invalidate(u->meta.job, ns == UNIT_FAILED ? JOB_FAILED : JOB_DONE); } break; @@ -1138,12 +1148,12 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su if (u->meta.job->state == JOB_RUNNING) { if (ns == UNIT_ACTIVE) - job_finish_and_invalidate(u->meta.job, reload_success); + job_finish_and_invalidate(u->meta.job, reload_success ? JOB_DONE : JOB_FAILED); else if (ns != UNIT_ACTIVATING && ns != UNIT_RELOADING) { unexpected = true; if (UNIT_IS_INACTIVE_OR_FAILED(ns)) - job_finish_and_invalidate(u->meta.job, ns != UNIT_FAILED); + job_finish_and_invalidate(u->meta.job, ns == UNIT_FAILED ? JOB_FAILED : JOB_DONE); } } @@ -1154,10 +1164,10 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su case JOB_TRY_RESTART: if (UNIT_IS_INACTIVE_OR_FAILED(ns)) - job_finish_and_invalidate(u->meta.job, true); + job_finish_and_invalidate(u->meta.job, JOB_DONE); else if (u->meta.job->state == JOB_RUNNING && ns != UNIT_DEACTIVATING) { unexpected = true; - job_finish_and_invalidate(u->meta.job, false); + job_finish_and_invalidate(u->meta.job, JOB_FAILED); } break; @@ -1183,13 +1193,8 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su } if (ns != os && ns == UNIT_FAILED) { - Iterator i; - Unit *other; - - SET_FOREACH(other, u->meta.dependencies[UNIT_ON_FAILURE], i) - manager_add_job(u->meta.manager, JOB_START, other, JOB_REPLACE, true, NULL, NULL); - log_notice("Unit %s entered failed state.", u->meta.id); + unit_trigger_on_failure(u); } /* Some names are special */ diff --git a/src/unit.h b/src/unit.h index b30f0cf9cb..5f55a89da8 100644 --- a/src/unit.h +++ b/src/unit.h @@ -40,7 +40,7 @@ typedef enum UnitDependency UnitDependency; #include "execute.h" #include "condition.h" -#define DEFAULT_TIMEOUT_USEC (60*USEC_PER_SEC) +#define DEFAULT_TIMEOUT_USEC (3*USEC_PER_MINUTE) #define DEFAULT_RESTART_USEC (100*USEC_PER_MSEC) enum UnitType { @@ -196,7 +196,7 @@ struct Meta { /* Garbage collect us we nobody wants or requires us anymore */ bool stop_when_unneeded; - /* Create default depedencies */ + /* Create default dependencies */ bool default_dependencies; /* Refuse manual starting, allow starting only indirectly via dependency. */ @@ -290,7 +290,7 @@ struct UnitVTable { /* Returns the substate specific to this unit type as * string. This is purely information so that we can give the - * user a more finegrained explanation in which actual state a + * user a more fine grained explanation in which actual state a * unit is in. */ const char* (*sub_state_to_string)(Unit *u); @@ -511,6 +511,8 @@ int unit_following_set(Unit *u, Set **s); UnitType unit_name_to_type(const char *n); bool unit_name_is_valid(const char *n, bool template_ok); +void unit_trigger_on_failure(Unit *u); + const char *unit_load_state_to_string(UnitLoadState i); UnitLoadState unit_load_state_from_string(const char *s); diff --git a/src/util.c b/src/util.c index 16e4ab289a..e2859fafc1 100644 --- a/src/util.c +++ b/src/util.c @@ -2134,8 +2134,32 @@ finish: int open_terminal(const char *name, int mode) { int fd, r; + unsigned c = 0; - if ((fd = open(name, mode)) < 0) + /* + * If a TTY is in the process of being closed opening it might + * cause EIO. This is horribly awful, but unlikely to be + * changed in the kernel. Hence we work around this problem by + * retrying a couple of times. + * + * https://bugs.launchpad.net/ubuntu/+source/linux/+bug/554172/comments/245 + */ + + for (;;) { + if ((fd = open(name, mode)) >= 0) + break; + + if (errno != EIO) + return -errno; + + if (c >= 20) + return -errno; + + usleep(50 * USEC_PER_MSEC); + c++; + } + + if (fd < 0) return -errno; if ((r = isatty(fd)) < 0) { @@ -2749,28 +2773,121 @@ char* getlogname_malloc(void) { return name; } -int getttyname_malloc(char **r) { - char path[PATH_MAX], *p, *c; +int getttyname_malloc(int fd, char **r) { + char path[PATH_MAX], *c; int k; assert(r); - if ((k = ttyname_r(STDIN_FILENO, path, sizeof(path))) != 0) + if ((k = ttyname_r(fd, path, sizeof(path))) != 0) return -k; char_array_0(path); - p = path; - if (startswith(path, "/dev/")) - p += 5; - - if (!(c = strdup(p))) + if (!(c = strdup(startswith(path, "/dev/") ? path + 5 : path))) return -ENOMEM; *r = c; return 0; } +int getttyname_harder(int fd, char **r) { + int k; + char *s; + + if ((k = getttyname_malloc(fd, &s)) < 0) + return k; + + if (streq(s, "tty")) { + free(s); + return get_ctty(r); + } + + *r = s; + return 0; +} + +int get_ctty_devnr(dev_t *d) { + int k; + char line[256], *p; + unsigned long ttynr; + FILE *f; + + if (!(f = fopen("/proc/self/stat", "r"))) + return -errno; + + if (!(fgets(line, sizeof(line), f))) { + k = -errno; + fclose(f); + return k; + } + + fclose(f); + + if (!(p = strrchr(line, ')'))) + return -EIO; + + p++; + + if (sscanf(p, " " + "%*c " /* state */ + "%*d " /* ppid */ + "%*d " /* pgrp */ + "%*d " /* session */ + "%lu ", /* ttynr */ + &ttynr) != 1) + return -EIO; + + *d = (dev_t) ttynr; + return 0; +} + +int get_ctty(char **r) { + int k; + char fn[128], *s, *b, *p; + dev_t devnr; + + assert(r); + + if ((k = get_ctty_devnr(&devnr)) < 0) + return k; + + snprintf(fn, sizeof(fn), "/dev/char/%u:%u", major(devnr), minor(devnr)); + char_array_0(fn); + + if ((k = readlink_malloc(fn, &s)) < 0) { + + if (k != -ENOENT) + return k; + + /* Probably something like the ptys which have no + * symlink in /dev/char. Let's return something + * vaguely useful. */ + + if (!(b = strdup(fn + 5))) + return -ENOMEM; + + *r = b; + return 0; + } + + if (startswith(s, "/dev/")) + p = s + 5; + else if (startswith(s, "../")) + p = s + 3; + else + p = s; + + b = strdup(p); + free(s); + + if (!b) + return -ENOMEM; + + *r = b; + return 0; +} + static int rm_rf_children(int fd, bool only_dirs) { DIR *d; int ret = 0; @@ -3589,51 +3706,51 @@ const char *default_term_for_tty(const char *tty) { return term; } -bool running_in_vm(void) { +/* Returns a short identifier for the various VM implementations */ +int detect_vm(const char **id) { #if defined(__i386__) || defined(__x86_64__) /* Both CPUID and DMI are x86 specific interfaces... */ - const char *const dmi_vendors[] = { + static const char *const dmi_vendors[] = { "/sys/class/dmi/id/sys_vendor", "/sys/class/dmi/id/board_vendor", "/sys/class/dmi/id/bios_vendor" }; - uint32_t eax = 0x40000000; + static const char dmi_vendor_table[] = + "QEMU\0" "qemu\0" + /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */ + "VMware\0" "vmware\0" + "VMW\0" "vmware\0" + "Microsoft Corporation\0" "microsoft\0" + "innotek GmbH\0" "oracle\0" + "Xen\0" "xen\0" + "Bochs\0" "bochs\0" + "\0"; + + static const char cpuid_vendor_table[] = + "XenVMMXenVMM\0" "xen\0" + "KVMKVMKVM\0" "kvm\0" + /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */ + "VMwareVMware\0" "vmware\0" + /* http://msdn.microsoft.com/en-us/library/ff542428.aspx */ + "Microsoft Hv\0" "microsoft\0" + "\0"; + + uint32_t eax, ecx; union { uint32_t sig32[3]; char text[13]; } sig; - unsigned i; - - for (i = 0; i < ELEMENTSOF(dmi_vendors); i++) { - char *s; - bool b; - - if (read_one_line_file(dmi_vendors[i], &s) < 0) - continue; - - b = startswith(s, "QEMU") || - /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */ - startswith(s, "VMware") || - startswith(s, "VMW") || - startswith(s, "Microsoft Corporation") || - startswith(s, "innotek GmbH") || - startswith(s, "Xen"); - - free(s); - - if (b) - return true; - } + const char *j, *k; + bool hypervisor; /* http://lwn.net/Articles/301888/ */ zero(sig); - #if defined (__i386__) #define REG_a "eax" #define REG_b "ebx" @@ -3642,27 +3759,109 @@ bool running_in_vm(void) { #define REG_b "rbx" #endif + /* First detect whether there is a hypervisor */ + eax = 1; __asm__ __volatile__ ( /* ebx/rbx is being used for PIC! */ " push %%"REG_b" \n\t" " cpuid \n\t" - " mov %%ebx, %1 \n\t" " pop %%"REG_b" \n\t" - : "=a" (eax), "=r" (sig.sig32[0]), "=c" (sig.sig32[1]), "=d" (sig.sig32[2]) + : "=a" (eax), "=c" (ecx) : "0" (eax) ); - if (streq(sig.text, "XenVMMXenVMM") || - streq(sig.text, "KVMKVMKVM") || - /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */ - streq(sig.text, "VMwareVMware") || - /* http://msdn.microsoft.com/en-us/library/bb969719.aspx */ - streq(sig.text, "Microsoft Hv")) - return true; + hypervisor = !!(ecx & ecx & 0x80000000U); + + if (hypervisor) { + + /* There is a hypervisor, see what it is */ + eax = 0x40000000U; + __asm__ __volatile__ ( + /* ebx/rbx is being used for PIC! */ + " push %%"REG_b" \n\t" + " cpuid \n\t" + " mov %%ebx, %1 \n\t" + " pop %%"REG_b" \n\t" + + : "=a" (eax), "=r" (sig.sig32[0]), "=c" (sig.sig32[1]), "=d" (sig.sig32[2]) + : "0" (eax) + ); + + NULSTR_FOREACH_PAIR(j, k, cpuid_vendor_table) + if (streq(sig.text, j)) { + + if (id) + *id = k; + + return 1; + } + } + + for (i = 0; i < ELEMENTSOF(dmi_vendors); i++) { + char *s; + int r; + const char *found = NULL; + + if ((r = read_one_line_file(dmi_vendors[i], &s)) < 0) { + if (r != -ENOENT) + return r; + + continue; + } + + NULSTR_FOREACH_PAIR(j, k, dmi_vendor_table) + if (startswith(s, j)) + found = k; + free(s); + + if (found) { + if (id) + *id = found; + + return 1; + } + } + + if (hypervisor) { + if (id) + *id = "other"; + + return 1; + } + #endif + return 0; +} - return false; +/* Returns a short identifier for the various VM/container implementations */ +int detect_virtualization(const char **id) { + int r; + + /* Unfortunately most of these operations require root access + * in one way or another */ + if (geteuid() != 0) + return -EPERM; + + if ((r = running_in_chroot()) > 0) { + if (id) + *id = "chroot"; + + return r; + } + + /* /proc/vz exists in container and outside of the container, + * /proc/bc only outside of the container. */ + if (access("/proc/vz", F_OK) >= 0 && + access("/proc/bc", F_OK) < 0) { + + if (id) + *id = "openvz"; + + return 1; + } + + return detect_vm(id); } void execute_directory(const char *directory, DIR *d, char *argv[]) { diff --git a/src/util.h b/src/util.h index 3898b89ff1..7f2cc080a7 100644 --- a/src/util.h +++ b/src/util.h @@ -331,7 +331,12 @@ void sigset_add_many(sigset_t *ss, ...); char* gethostname_malloc(void); char* getlogname_malloc(void); -int getttyname_malloc(char **r); + +int getttyname_malloc(int fd, char **r); +int getttyname_harder(int fd, char **r); + +int get_ctty_devnr(dev_t *d); +int get_ctty(char **r); int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid); @@ -373,7 +378,8 @@ void filter_environ(const char *prefix); bool tty_is_vc(const char *tty); const char *default_term_for_tty(const char *tty); -bool running_in_vm(void); +int detect_vm(const char **id); +int detect_virtualization(const char **id); void execute_directory(const char *directory, DIR *_d, char *argv[]); diff --git a/src/utmp-wtmp.c b/src/utmp-wtmp.c index 83da640bf3..b03a3e70af 100644 --- a/src/utmp-wtmp.c +++ b/src/utmp-wtmp.c @@ -370,7 +370,7 @@ int utmp_wall(const char *message, bool (*match_tty)(const char *tty)) { goto finish; } - getttyname_malloc(&tty); + getttyname_harder(STDIN_FILENO, &tty); if (asprintf(&text, "\a\r\n" diff --git a/units/emergency.service b/units/emergency.service index aa3d9856c0..cb28a78385 100644 --- a/units/emergency.service +++ b/units/emergency.service @@ -16,10 +16,10 @@ Before=shutdown.target [Service] Environment=HOME=/root WorkingDirectory=/root -ExecStartPre=-/bin/plymouth --hide-splash +ExecStartPre=-/bin/plymouth quit ExecStartPre=-/bin/echo 'Welcome to emergency mode. Use "systemctl default" or ^D to activate default mode.' ExecStart=-/sbin/sulogin -ExecStopPost=/bin/systemctl default +ExecStopPost=/bin/systemctl --fail default StandardInput=tty-force KillMode=process-group diff --git a/units/fsck-root.service.in b/units/fsck-root.service.in index 3df2cd3809..290c8453cc 100644 --- a/units/fsck-root.service.in +++ b/units/fsck-root.service.in @@ -20,3 +20,4 @@ RemainAfterExit=no ExecStart=@rootlibexecdir@/systemd-fsck StandardOutput=syslog+console FsckPassNo=1 +TimeoutSec=0 diff --git a/units/fsck@.service.in b/units/fsck@.service.in index 4751974312..ad9ec3bcc9 100644 --- a/units/fsck@.service.in +++ b/units/fsck@.service.in @@ -10,10 +10,11 @@ Description=File System Check on %f DefaultDependencies=no BindTo=%i.device After=systemd-readahead-collect.service systemd-readahead-replay.service %i.device -Before=local-fs.target shutdown.target +Before=basic.target shutdown.target [Service] Type=oneshot RemainAfterExit=no ExecStart=@rootlibexecdir@/systemd-fsck %f StandardOutput=syslog+console +TimeoutSec=0 diff --git a/units/getty@.service.m4 b/units/getty@.service.m4 index 74ec1f30d5..d282912274 100644 --- a/units/getty@.service.m4 +++ b/units/getty@.service.m4 @@ -8,7 +8,7 @@ [Unit] Description=Getty on %I BindTo=dev-%i.device -After=dev-%i.device systemd-user-sessions.service +After=dev-%i.device systemd-user-sessions.service plymouth-quit-wait.service m4_ifdef(`TARGET_FEDORA', After=rc-local.service )m4_dnl @@ -18,6 +18,9 @@ After=rc-local.service m4_ifdef(`TARGET_FRUGALWARE', After=local.service )m4_dnl +m4_ifdef(`TARGET_ALTLINUX', +After=rc-local.service +)m4_dnl # If additional gettys are spawned during boot then we should make # sure that this is synchronized before getty.target, even though diff --git a/units/plymouth-quit-wait.service b/units/plymouth-quit-wait.service new file mode 100644 index 0000000000..45c67bdabd --- /dev/null +++ b/units/plymouth-quit-wait.service @@ -0,0 +1,15 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. + +[Unit] +Description=Wait for Plymouth Boot Screen to Quit +After=rc-local.service plymouth-start.service + +[Service] +ExecStart=-/bin/plymouth --wait +Type=oneshot +TimeoutSec=20 diff --git a/units/plymouth-quit.service b/units/plymouth-quit.service index 6310eff495..164499a2a6 100644 --- a/units/plymouth-quit.service +++ b/units/plymouth-quit.service @@ -7,9 +7,9 @@ [Unit] Description=Terminate Plymouth Boot Screen -Before=getty@tty1.service -After=dev-tty1.device rc-local.service plymouth-start.service +After=rc-local.service plymouth-start.service [Service] ExecStart=-/bin/plymouth quit Type=oneshot +TimeoutSec=20 diff --git a/units/quotacheck.service.in b/units/quotacheck.service.in index 59a0c773eb..d46a335649 100644 --- a/units/quotacheck.service.in +++ b/units/quotacheck.service.in @@ -17,6 +17,7 @@ Type=oneshot RemainAfterExit=yes ExecStart=@rootlibexecdir@/systemd-quotacheck StandardOutput=syslog +TimeoutSec=0 [Install] WantedBy=local-fs.target diff --git a/units/rescue.service.m4 b/units/rescue.service.m4 index ba29f6e6e6..2a0d3280ec 100644 --- a/units/rescue.service.m4 +++ b/units/rescue.service.m4 @@ -17,7 +17,7 @@ Before=shutdown.target [Service] Environment=HOME=/root WorkingDirectory=/root -ExecStartPre=-/bin/plymouth --hide-splash +ExecStartPre=-/bin/plymouth quit ExecStartPre=-/bin/echo 'Welcome to rescue mode. Use "systemctl default" or ^D to activate default mode.' m4_ifdef(`TARGET_FEDORA', `EnvironmentFile=/etc/sysconfig/init diff --git a/units/serial-getty@.service.m4 b/units/serial-getty@.service.m4 index c7a11b1fa0..2b3c8edd57 100644 --- a/units/serial-getty@.service.m4 +++ b/units/serial-getty@.service.m4 @@ -8,13 +8,19 @@ [Unit] Description=Serial Getty on %I BindTo=dev-%i.device -After=dev-%i.device systemd-user-sessions.service +After=dev-%i.device systemd-user-sessions.service plymouth-quit-wait.service m4_ifdef(`TARGET_FEDORA', After=rc-local.service )m4_dnl m4_ifdef(`TARGET_ARCH', After=rc-local.service )m4_dnl +m4_ifdef(`TARGET_FRUGALWARE', +After=local.service +)m4_dnl +m4_ifdef(`TARGET_ALTLINUX', +After=rc-local.service +)m4_dnl # If additional gettys are spawned during boot then we should make # sure that this is synchronized before getty.target, even though |