diff options
author | Tollef Fog Heen <tfheen@err.no> | 2012-02-20 20:00:20 +0100 |
---|---|---|
committer | Tollef Fog Heen <tfheen@err.no> | 2012-02-20 20:00:20 +0100 |
commit | 14466291560ece602232c1ae960033025f3bbb1c (patch) | |
tree | 735095b834ee1c726072bda7f969aa540da7ec94 | |
parent | dbe879eae939311429293dcd433c5ad99c53f54f (diff) | |
parent | 437b7dee328738b7aca89a9c7527f228ff8f2d34 (diff) | |
download | systemd-14466291560ece602232c1ae960033025f3bbb1c.tar.gz |
Merge tag 'v43' into debian-units
systemd 43
Conflicts:
units/remount-rootfs.service
381 files changed, 30534 insertions, 8721 deletions
diff --git a/.gitignore b/.gitignore index 6cfb3ed265..3da7e66518 100644 --- a/.gitignore +++ b/.gitignore @@ -1,15 +1,25 @@ +/systemd-multi-seat-x +/systemd-cgtop +/systemd-coredump +/systemd-cat +/systemd-rc-local-generator +/libsystemd-id128.pc +systemd-journalctl +systemd-journald +test-id128 +test-journal test-install org.freedesktop.hostname1.xml org.freedesktop.locale1.xml -org.freedesktop.timedate1.xml libsystemd-daemon.pc libsystemd-login.pc test-login systemd-loginctl systemd-localed systemd-timedated -systemd-uaccess +org.freedesktop.timedate1.xml systemd-logind +systemd-uaccess systemd-hostnamed systemd-binfmt systemd-getty-generator @@ -55,10 +65,10 @@ test-ns test-loopback systemd-cgroups-agent systemd-initctl -systemd +/systemd test-engine test-job-type -systemd-logger +systemd-stdout-syslog-bridge systemctl systemadm .dirstamp @@ -69,7 +79,6 @@ systemadm *.8 *.html *~ -*.tar.gz *.o *.lo *.a @@ -91,10 +100,7 @@ install-sh missing stamp-* *.stamp -Makefile +/Makefile ltmain.sh -*.tar.bz2 -*.tar.gz *.tar.xz libtool -.vimrc diff --git a/.vimrc b/.vimrc new file mode 100644 index 0000000000..366fbdca4b --- /dev/null +++ b/.vimrc @@ -0,0 +1,4 @@ +" 'set exrc' in ~/.vimrc will read .vimrc from the current directory +set tabstop=8 +set shiftwidth=8 +set expandtab diff --git a/Makefile.am b/Makefile.am index 25ce3f4704..9762da14ef 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,6 +1,7 @@ # This file is part of systemd. # -# Copyright 2010 Lennart Poettering +# Copyright 2011 Lennart Poettering +# Copyright 2011 Kay Sievers # # systemd is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by @@ -19,14 +20,22 @@ ACLOCAL_AMFLAGS = -I m4 SUBDIRS = po -LIBSYSTEMD_LOGIN_CURRENT=0 +LIBSYSTEMD_LOGIN_CURRENT=2 LIBSYSTEMD_LOGIN_REVISION=0 -LIBSYSTEMD_LOGIN_AGE=0 +LIBSYSTEMD_LOGIN_AGE=2 LIBSYSTEMD_DAEMON_CURRENT=0 -LIBSYSTEMD_DAEMON_REVISION=0 +LIBSYSTEMD_DAEMON_REVISION=1 LIBSYSTEMD_DAEMON_AGE=0 +LIBSYSTEMD_ID128_CURRENT=0 +LIBSYSTEMD_ID128_REVISION=2 +LIBSYSTEMD_ID128_AGE=0 + +LIBSYSTEMD_JOURNAL_CURRENT=0 +LIBSYSTEMD_JOURNAL_REVISION=2 +LIBSYSTEMD_JOURNAL_AGE=0 + # Dirs of external packages dbuspolicydir=@dbuspolicydir@ dbussessionservicedir=@dbussessionservicedir@ @@ -43,16 +52,28 @@ bashcompletiondir=$(sysconfdir)/bash_completion.d pkgsysconfdir=$(sysconfdir)/systemd userunitdir=$(prefix)/lib/systemd/user tmpfilesdir=$(prefix)/lib/tmpfiles.d +sysctldir=$(prefix)/lib/sysctl.d usergeneratordir=$(pkglibexecdir)/user-generators pkgincludedir=$(includedir)/systemd # And these are the special ones for / -rootdir=@rootdir@ -rootbindir=$(rootdir)/bin -rootlibexecdir=$(rootdir)/lib/systemd +rootprefix=@rootprefix@ +rootbindir=$(rootprefix)/bin +rootlibexecdir=$(rootprefix)/lib/systemd systemgeneratordir=$(rootlibexecdir)/system-generators systemshutdowndir=$(rootlibexecdir)/system-shutdown -systemunitdir=$(rootdir)/lib/systemd/system +systemunitdir=$(rootprefix)/lib/systemd/system + +CLEANFILES = +EXTRA_DIST = +INSTALL_EXEC_HOOKS = +UNINSTALL_EXEC_HOOKS = +INSTALL_DATA_HOOKS = +pkginclude_HEADERS = +lib_LTLIBRARIES = +pkgconfiglib_DATA = +polkitpolicy_in_files = +dist_udevrules_DATA = AM_CPPFLAGS = \ -include $(top_builddir)/config.h \ @@ -65,18 +86,25 @@ AM_CPPFLAGS = \ -DUSER_CONFIG_UNIT_PATH=\"$(pkgsysconfdir)/user\" \ -DUSER_DATA_UNIT_PATH=\"$(userunitdir)\" \ -DSYSTEMD_CGROUP_AGENT_PATH=\"$(rootlibexecdir)/systemd-cgroups-agent\" \ - -DSYSTEMD_BINARY_PATH=\"$(rootbindir)/systemd\" \ + -DSYSTEMD_BINARY_PATH=\"$(rootlibexecdir)/systemd\" \ -DSYSTEMD_SHUTDOWN_BINARY_PATH=\"$(rootlibexecdir)/systemd-shutdown\" \ -DSYSTEMCTL_BINARY_PATH=\"$(rootbindir)/systemctl\" \ -DSYSTEMD_TTY_ASK_PASSWORD_AGENT_BINARY_PATH=\"$(rootbindir)/systemd-tty-ask-password-agent\" \ -DSYSTEMD_STDIO_BRIDGE_BINARY_PATH=\"$(bindir)/systemd-stdio-bridge\" \ + -DROOTPREFIX=\"$(rootprefix)\" \ -DRUNTIME_DIR=\"/run\" \ -DRANDOM_SEED=\"$(localstatedir)/lib/random-seed\" \ -DSYSTEMD_CRYPTSETUP_PATH=\"$(rootlibexecdir)/systemd-cryptsetup\" \ -DSYSTEM_GENERATOR_PATH=\"$(systemgeneratordir)\" \ -DUSER_GENERATOR_PATH=\"$(usergeneratordir)\" \ -DSYSTEM_SHUTDOWN_PATH=\"$(systemshutdowndir)\" \ - -I $(top_srcdir)/src + -DSYSTEMD_KBD_MODEL_MAP=\"$(pkgdatadir)/kbd-model-map\" \ + -DX_SERVER=\"$(bindir)/X\" \ + -I $(top_srcdir)/src \ + -I $(top_srcdir)/src/readahead \ + -I $(top_srcdir)/src/login \ + -I $(top_srcdir)/src/journal \ + -I $(top_srcdir)/src/systemd if TARGET_GENTOO AM_CPPFLAGS += \ @@ -114,6 +142,12 @@ AM_CPPFLAGS += \ -DKBD_SETFONT=\"/usr/bin/setfont\" \ -DDEFAULT_FONT=\"LatArCyrHeb-16\" else +if TARGET_MAGEIA +AM_CPPFLAGS += \ + -DKBD_LOADKEYS=\"/bin/loadkeys\" \ + -DKBD_SETFONT=\"/bin/setfont\" \ + -DDEFAULT_FONT=\"LatArCyrHeb-16\" +else AM_CPPFLAGS += \ -DKBD_LOADKEYS=\"/bin/loadkeys\" \ -DKBD_SETFONT=\"/bin/setfont\" \ @@ -124,11 +158,10 @@ endif endif endif endif +endif rootbin_PROGRAMS = \ - systemd \ systemctl \ - systemd-loginctl \ systemd-notify \ systemd-ask-password \ systemd-tty-ask-password-agent \ @@ -137,6 +170,7 @@ rootbin_PROGRAMS = \ bin_PROGRAMS = \ systemd-cgls \ + systemd-cgtop \ systemd-stdio-bridge \ systemd-nspawn @@ -150,57 +184,24 @@ bin_PROGRAMS += \ endif rootlibexec_PROGRAMS = \ - systemd-logger \ + systemd \ systemd-cgroups-agent \ systemd-initctl \ systemd-update-utmp \ - systemd-random-seed \ systemd-shutdownd \ systemd-shutdown \ systemd-modules-load \ systemd-remount-api-vfs \ - systemd-kmsg-syslogd \ - systemd-vconsole-setup \ systemd-reply-password \ - systemd-readahead-collect \ - systemd-readahead-replay \ - systemd-user-sessions \ systemd-fsck \ - systemd-quotacheck \ systemd-timestamp \ systemd-ac-power \ systemd-detect-virt \ - systemd-sysctl \ - systemd-hostnamed \ - systemd-localed \ - systemd-timedated \ - systemd-logind \ - systemd-uaccess - -if ENABLE_BINFMT -rootlibexec_PROGRAMS += \ - systemd-binfmt -endif + systemd-sysctl systemgenerator_PROGRAMS = \ systemd-getty-generator -if HAVE_LIBCRYPTSETUP -rootlibexec_PROGRAMS += \ - systemd-cryptsetup - -systemgenerator_PROGRAMS += \ - systemd-cryptsetup-generator -endif - -lib_LTLIBRARIES = \ - libsystemd-daemon.la \ - libsystemd-login.la - -pkginclude_HEADERS = \ - src/sd-daemon.h \ - src/sd-login.h - noinst_PROGRAMS = \ test-engine \ test-job-type \ @@ -211,40 +212,20 @@ noinst_PROGRAMS = \ test-cgroup \ test-env-replace \ test-strv \ - test-login \ - test-install - -if HAVE_PAM -pamlib_LTLIBRARIES = \ - pam_systemd.la -endif + test-install dist_pkgsysconf_DATA = \ src/system.conf \ - src/user.conf \ - src/systemd-logind.conf + src/user.conf dist_dbuspolicy_DATA = \ - src/org.freedesktop.systemd1.conf \ - src/org.freedesktop.hostname1.conf \ - src/org.freedesktop.locale1.conf \ - src/org.freedesktop.timedate1.conf \ - src/org.freedesktop.login1.conf + src/org.freedesktop.systemd1.conf dist_dbussystemservice_DATA = \ - src/org.freedesktop.systemd1.service \ - src/org.freedesktop.hostname1.service \ - src/org.freedesktop.locale1.service \ - src/org.freedesktop.timedate1.service \ - src/org.freedesktop.login1.service - -dist_udevrules_DATA = \ - src/70-uaccess.rules \ - src/71-seat.rules + src/org.freedesktop.systemd1.service nodist_udevrules_DATA = \ - src/73-seat-late.rules \ - src/99-systemd.rules + src/99-systemd.rules dbusinterface_DATA = \ org.freedesktop.systemd1.Manager.xml \ @@ -259,16 +240,14 @@ dbusinterface_DATA = \ org.freedesktop.systemd1.Automount.xml \ org.freedesktop.systemd1.Snapshot.xml \ org.freedesktop.systemd1.Swap.xml \ - org.freedesktop.systemd1.Path.xml \ - org.freedesktop.hostname1.xml \ - org.freedesktop.locale1.xml \ - org.freedesktop.timedate1.xml + org.freedesktop.systemd1.Path.xml dist_bashcompletion_DATA = \ - src/systemctl-bash-completion.sh + src/systemd-bash-completion.sh dist_tmpfiles_DATA = \ tmpfiles.d/systemd.conf \ + tmpfiles.d/tmp.conf \ tmpfiles.d/x11.conf if HAVE_SYSV_COMPAT @@ -287,8 +266,9 @@ dist_systemunit_DATA = \ units/halt.target \ units/kexec.target \ units/local-fs.target \ + units/local-fs-pre.target \ units/remote-fs.target \ - units/cryptsetup.target \ + units/remote-fs-pre.target \ units/network.target \ units/nss-lookup.target \ units/mail-transfer-agent.target \ @@ -305,17 +285,14 @@ dist_systemunit_DATA = \ units/sockets.target \ units/swap.target \ units/systemd-initctl.socket \ - units/systemd-logger.socket \ units/systemd-shutdownd.socket \ units/syslog.socket \ - units/dev-hugepages.automount \ units/dev-hugepages.mount \ - units/dev-mqueue.automount \ units/dev-mqueue.mount \ - units/sys-kernel-debug.automount \ + units/sys-kernel-config.mount \ units/sys-kernel-debug.mount \ - units/sys-kernel-security.automount \ units/sys-kernel-security.mount \ + units/sys-fs-fuse-connections.mount \ units/var-run.mount \ units/media.mount \ units/remount-rootfs.service \ @@ -323,7 +300,6 @@ dist_systemunit_DATA = \ units/sound.target \ units/bluetooth.target \ units/smartcard.target \ - units/systemd-readahead-done.timer \ units/systemd-tmpfiles-clean.timer \ units/quotaon.service \ units/systemd-ask-password-wall.path \ @@ -335,37 +311,18 @@ dist_systemunit_DATA += \ units/var-lock.mount endif -if ENABLE_BINFMT -dist_systemunit_DATA += \ - units/proc-sys-fs-binfmt_misc.automount \ - units/proc-sys-fs-binfmt_misc.mount -endif - nodist_systemunit_DATA = \ units/getty@.service \ units/serial-getty@.service \ units/console-shell.service \ units/systemd-initctl.service \ - units/systemd-logger.service \ units/systemd-shutdownd.service \ - units/systemd-hostnamed.service \ - units/systemd-localed.service \ - units/systemd-timedated.service \ - units/systemd-logind.service \ - units/systemd-kmsg-syslogd.service \ units/systemd-modules-load.service \ - units/systemd-vconsole-setup.service \ units/systemd-remount-api-vfs.service \ units/systemd-update-utmp-runlevel.service \ units/systemd-update-utmp-shutdown.service \ - units/systemd-random-seed-save.service \ - units/systemd-random-seed-load.service \ - units/systemd-readahead-collect.service \ - units/systemd-readahead-replay.service \ - units/systemd-readahead-done.service \ units/systemd-tmpfiles-setup.service \ units/systemd-tmpfiles-clean.service \ - units/systemd-user-sessions.service \ units/systemd-ask-password-wall.service \ units/systemd-ask-password-console.service \ units/systemd-sysctl.service \ @@ -375,14 +332,8 @@ nodist_systemunit_DATA = \ units/kexec.service \ units/fsck@.service \ units/fsck-root.service \ - units/quotacheck.service \ units/rescue.service \ - units/user@.service - -if ENABLE_BINFMT -nodist_systemunit_DATA += \ - units/systemd-binfmt.service -endif + units/user@.service dist_userunit_DATA = \ units/user/default.target \ @@ -391,32 +342,19 @@ dist_userunit_DATA = \ nodist_userunit_DATA = \ units/user/exit.service -EXTRA_DIST = \ +EXTRA_DIST += \ units/getty@.service.m4 \ units/serial-getty@.service.m4 \ units/console-shell.service.m4 \ units/rescue.service.m4 \ units/systemd-initctl.service.in \ - units/systemd-logger.service.in \ units/systemd-shutdownd.service.in \ - units/systemd-hostnamed.service.in \ - units/systemd-localed.service.in \ - units/systemd-timedated.service.in \ - units/systemd-logind.service.in \ - units/systemd-kmsg-syslogd.service.in \ units/systemd-modules-load.service.in \ - units/systemd-vconsole-setup.service.in \ units/systemd-remount-api-vfs.service.in \ units/systemd-update-utmp-runlevel.service.in \ units/systemd-update-utmp-shutdown.service.in \ - units/systemd-random-seed-save.service.in \ - units/systemd-random-seed-load.service.in \ - units/systemd-readahead-collect.service.in \ - units/systemd-readahead-replay.service.in \ - units/systemd-readahead-done.service.in \ units/systemd-tmpfiles-setup.service.in \ units/systemd-tmpfiles-clean.service.in \ - units/systemd-user-sessions.service.in \ units/systemd-ask-password-wall.service.in \ units/systemd-ask-password-console.service.in \ units/systemd-sysctl.service.in \ @@ -427,27 +365,19 @@ EXTRA_DIST = \ units/user/exit.service.in \ units/fsck@.service.in \ units/fsck-root.service.in \ - units/quotacheck.service.in \ units/user@.service.in \ - systemd.pc.in \ - libsystemd-daemon.pc.in \ - libsystemd-login.pc.in \ - src/libsystemd-daemon.sym \ - src/libsystemd-login.sym \ + src/systemd.pc.in \ introspect.awk \ - src/73-seat-late.rules.in \ - src/99-systemd.rules.in - -if ENABLE_BINFMT -EXTRA_DIST += \ - units/systemd-binfmt.service.in -endif + src/99-systemd.rules.in \ + man/custom-html.xsl if TARGET_FEDORA dist_systemunit_DATA += \ units/fedora/prefdm.service \ units/fedora/rc-local.service \ units/fedora/halt-local.service +systemgenerator_PROGRAMS += \ + systemd-rc-local-generator endif if TARGET_MANDRIVA @@ -455,6 +385,8 @@ dist_systemunit_DATA += \ units/mandriva/prefdm.service \ units/fedora/rc-local.service \ units/fedora/halt-local.service +systemgenerator_PROGRAMS += \ + systemd-rc-local-generator endif if TARGET_FRUGALWARE @@ -466,6 +398,17 @@ if TARGET_SUSE dist_systemunit_DATA += \ units/suse/rc-local.service \ units/suse/halt-local.service +systemgenerator_PROGRAMS += \ + systemd-rc-local-generator +endif + +if TARGET_MAGEIA +dist_systemunit_DATA += \ + units/mageia/prefdm.service \ + units/fedora/rc-local.service \ + units/fedora/halt-local.service +systemgenerator_PROGRAMS += \ + systemd-rc-local-generator endif if HAVE_PLYMOUTH @@ -489,38 +432,24 @@ endif dist_doc_DATA = \ README \ + NEWS \ LICENSE \ - DISTRO_PORTING \ - src/sd-daemon.h \ - src/sd-daemon.c \ - src/sd-readahead.h \ - src/sd-readahead.c + DISTRO_PORTING pkgconfigdata_DATA = \ - systemd.pc - -pkgconfiglib_DATA = \ - libsystemd-daemon.pc \ - libsystemd-login.pc - -# Passed through intltool only -polkitpolicy_in_files = \ - src/org.freedesktop.hostname1.policy.in \ - src/org.freedesktop.locale1.policy.in \ - src/org.freedesktop.timedate1.policy.in \ - src/org.freedesktop.login1.policy.in + src/systemd.pc # First passed through sed, followed by intltool polkitpolicy_in_in_files = \ src/org.freedesktop.systemd1.policy.in.in nodist_polkitpolicy_DATA = \ - $(polkitpolicy_in_files:.policy.in=.policy) \ - $(polkitpolicy_in_in_files:.policy.in.in=.policy) + $(polkitpolicy_in_files:.policy.in=.policy) \ + $(polkitpolicy_in_in_files:.policy.in.in=.policy) EXTRA_DIST += \ - $(polkitpolicy_in_files) \ - $(polkitpolicy_in_in_files) + $(polkitpolicy_in_files) \ + $(polkitpolicy_in_in_files) @INTLTOOL_POLICY_RULE@ @@ -530,6 +459,7 @@ noinst_LTLIBRARIES = \ libsystemd_basic_la_SOURCES = \ src/util.c \ + src/virt.c \ src/label.c \ src/hashmap.c \ src/set.c \ @@ -545,7 +475,8 @@ libsystemd_basic_la_CFLAGS = \ $(SELINUX_CFLAGS) libsystemd_basic_la_LIBADD = \ - $(SELINUX_LIBS) + $(SELINUX_LIBS) \ + $(CAP_LIBS) libsystemd_core_la_SOURCES = \ src/unit.c \ @@ -596,9 +527,18 @@ libsystemd_core_la_SOURCES = \ src/tcpwrap.c \ src/cgroup-util.c \ src/condition.c \ - src/dbus-common.c \ - src/sd-daemon.c \ - src/install.c + src/dbus-common.c \ + src/sd-daemon.c \ + src/install.c \ + src/cgroup-attr.c \ + src/sd-id128.c + +nodist_libsystemd_core_la_SOURCES = \ + src/load-fragment-gperf.c \ + src/load-fragment-gperf-nulstr.c + +EXTRA_DIST += \ + src/load-fragment-gperf.gperf.m4 libsystemd_core_la_CFLAGS = \ $(AM_CFLAGS) \ @@ -606,7 +546,8 @@ libsystemd_core_la_CFLAGS = \ $(UDEV_CFLAGS) \ $(LIBWRAP_CFLAGS) \ $(PAM_CFLAGS) \ - $(AUDIT_CFLAGS) + $(AUDIT_CFLAGS) \ + $(KMOD_CFLAGS) libsystemd_core_la_LIBADD = \ libsystemd-basic.la \ @@ -614,16 +555,77 @@ libsystemd_core_la_LIBADD = \ $(UDEV_LIBS) \ $(LIBWRAP_LIBS) \ $(PAM_LIBS) \ - $(AUDIT_LIBS) + $(AUDIT_LIBS) \ + $(CAP_LIBS) \ + $(KMOD_LIBS) # This is needed because automake is buggy in how it generates the -# rules for C programs, but not Vala programs. We therefore can't +# rules for C programs, but not Vala programs. We therefore can't # list the .h files as dependencies if we want make dist to work. EXTRA_DIST += \ - ${libsystemd_basic_la_SOURCES:.c=.h} \ - ${libsystemd_core_la_SOURCES:.c=.h} \ - ${libsystemd_daemon_la_SOURCES:.c=.h} \ + src/util.h \ + src/virt.h \ + src/label.h \ + src/hashmap.h \ + src/set.h \ + src/strv.h \ + src/conf-parser.h \ + src/socket-util.h \ + src/log.h \ + src/ratelimit.h \ + src/exit-status.h \ + src/unit.h \ + src/job.h \ + src/manager.h \ + src/path-lookup.h \ + src/load-fragment.h \ + src/service.h \ + src/automount.h \ + src/mount.h \ + src/swap.h \ + src/device.h \ + src/target.h \ + src/snapshot.h \ + src/socket.h \ + src/timer.h \ + src/path.h \ + src/load-dropin.h \ + src/execute.h \ + src/utmp-wtmp.h \ + src/dbus.h \ + src/dbus-manager.h \ + src/dbus-unit.h \ + src/dbus-job.h \ + src/dbus-service.h \ + src/dbus-socket.h \ + src/dbus-timer.h \ + src/dbus-target.h \ + src/dbus-mount.h \ + src/dbus-automount.h \ + src/dbus-swap.h \ + src/dbus-snapshot.h \ + src/dbus-device.h \ + src/dbus-execute.h \ + src/dbus-path.h \ + src/cgroup.h \ + src/mount-setup.h \ + src/hostname-setup.h \ + src/selinux-setup.h \ + src/loopback-setup.h \ + src/kmod-setup.h \ + src/locale-setup.h \ + src/machine-id-setup.h \ + src/specifier.h \ + src/unit-name.h \ + src/fdset.h \ + src/namespace.h \ + src/tcpwrap.h \ + src/cgroup-util.h \ + src/condition.h \ + src/dbus-common.h \ + src/install.h \ + src/cgroup-attr.h \ src/macro.h \ src/def.h \ src/ioprio.h \ @@ -633,7 +635,6 @@ EXTRA_DIST += \ src/linux/auto_dev-ioctl.h \ src/linux/fanotify.h \ src/initreq.h \ - src/sd-readahead.h \ src/special.h \ src/dbus-common.h \ src/bus-errors.h \ @@ -641,33 +642,24 @@ EXTRA_DIST += \ src/build.h \ src/shutdownd.h \ src/umount.h \ - src/readahead-common.h \ src/ask-password-api.h \ - src/pager.h \ - src/sysfs-show.h \ - src/polkit.h \ - src/logind.h \ - src/logind-device.h \ - src/logind-seat.h \ - src/logind-session.h \ - src/logind-user.h \ - src/logind-acl.h \ - src/dbus-loop.h \ - src/spawn-agent.h + src/pager.h \ + src/sysfs-show.h \ + src/polkit.h \ + src/dbus-loop.h \ + src/spawn-agent.h \ + src/acl-util.h \ + src/logs-show.h MANPAGES = \ man/systemd.1 \ man/systemctl.1 \ man/systemadm.1 \ man/systemd-cgls.1 \ + man/systemd-cgtop.1 \ man/systemd-nspawn.1 \ man/systemd-tmpfiles.8 \ man/systemd-notify.1 \ - man/sd_notify.3 \ - man/sd_readahead.3 \ - man/sd_booted.3 \ - man/sd_listen_fds.3 \ - man/sd_is_fifo.3 \ man/systemd.unit.5 \ man/systemd.service.5 \ man/systemd.socket.5 \ @@ -681,49 +673,33 @@ MANPAGES = \ man/systemd.snapshot.5 \ man/systemd.exec.5 \ man/daemon.7 \ - man/sd-daemon.7 \ - man/sd-readahead.7 \ man/runlevel.8 \ man/telinit.8 \ man/halt.8 \ man/shutdown.8 \ man/pam_systemd.8 \ man/systemd.conf.5 \ - man/systemd-logind.conf.5 \ man/tmpfiles.d.5 \ man/hostname.5 \ + man/timezone.5 \ man/machine-id.5 \ - man/vconsole.conf.5 \ man/locale.conf.5 \ man/os-release.5 \ - man/machine-info.5 \ + man/machine-info.5 \ man/modules-load.d.5 \ man/sysctl.d.5 \ - man/systemd-ask-password.1 \ - man/systemd-loginctl.1 - -if ENABLE_BINFMT -MANPAGES += \ - man/binfmt.d.5 -endif + man/systemd-ask-password.1 MANPAGES_ALIAS = \ man/reboot.8 \ man/poweroff.8 \ - man/sd_is_socket.3 \ - man/sd_is_socket_unix.3 \ - man/sd_is_socket_inet.3 \ - man/sd_notifyf.3 \ man/init.1 man/reboot.8: man/halt.8 man/poweroff.8: man/halt.8 -man/sd_is_socket.3: man/sd_is_fifo.3 -man/sd_is_socket_unix.3: man/sd_is_fifo.3 -man/sd_is_socket_inet.3: man/sd_is_fifo.3 -man/sd_notifyf.3: man/sd_notify.3 man/init.1: man/systemd.1 +if ENABLE_MANPAGES dist_man_MANS = \ $(MANPAGES) \ $(MANPAGES_ALIAS) @@ -748,6 +724,7 @@ EXTRA_DIST += \ $(XML_IN_FILES) \ ${nodist_man_MANS:=.in} \ ${XML_IN_FILES:.xml.in=.html.in} +endif systemd_SOURCES = \ src/main.c @@ -803,18 +780,12 @@ test_cgroup_SOURCES = \ src/test-cgroup.c \ src/cgroup-util.c -test_cgroup_CFLAGS = \ - $(AM_CFLAGS) - test_cgroup_LDADD = \ libsystemd-basic.la test_env_replace_SOURCES = \ src/test-env-replace.c -test_env_replace_CFLAGS = \ - $(AM_CFLAGS) - test_env_replace_LDADD = \ libsystemd-basic.la @@ -822,44 +793,22 @@ test_strv_SOURCES = \ src/test-strv.c \ src/specifier.c -test_strv_CFLAGS = \ - $(AM_CFLAGS) - test_strv_LDADD = \ libsystemd-basic.la -test_login_SOURCES = \ - src/test-login.c - -test_login_CFLAGS = \ - $(AM_CFLAGS) - -test_login_LDADD = \ - libsystemd-basic.la \ - libsystemd-login.la - test_install_SOURCES = \ src/test-install.c \ - src/install.c \ - src/path-lookup.c \ - src/unit-name.c + src/install.c \ + src/path-lookup.c \ + src/unit-name.c test_install_CFLAGS = \ $(AM_CFLAGS) \ - $(DBUS_CFLAGS) + $(DBUS_CFLAGS) test_install_LDADD = \ libsystemd-basic.la -systemd_logger_SOURCES = \ - src/logger.c \ - src/tcpwrap.c - -systemd_logger_LDADD = \ - libsystemd-basic.la \ - libsystemd-daemon.la \ - $(LIBWRAP_LIBS) - systemd_initctl_SOURCES = \ src/initctl.c \ src/dbus-common.c @@ -888,15 +837,6 @@ systemd_update_utmp_LDADD = \ $(DBUS_LIBS) \ $(AUDIT_LIBS) -systemd_random_seed_SOURCES = \ - src/random-seed.c - -systemd_random_seed_CFLAGS = \ - $(AM_CFLAGS) - -systemd_random_seed_LDADD = \ - libsystemd-basic.la - systemd_shutdownd_SOURCES = \ src/utmp-wtmp.c \ src/shutdownd.c @@ -908,99 +848,6 @@ systemd_shutdownd_LDADD = \ libsystemd-basic.la \ libsystemd-daemon.la -systemd_hostnamed_SOURCES = \ - src/hostnamed.c \ - src/dbus-common.c \ - src/polkit.c - -systemd_hostnamed_CFLAGS = \ - $(AM_CFLAGS) \ - $(DBUS_CFLAGS) - -systemd_hostnamed_LDADD = \ - libsystemd-basic.la \ - libsystemd-daemon.la \ - $(DBUS_LIBS) - -systemd_localed_SOURCES = \ - src/localed.c \ - src/dbus-common.c \ - src/polkit.c - -systemd_localed_CFLAGS = \ - $(AM_CFLAGS) \ - $(DBUS_CFLAGS) - -systemd_localed_LDADD = \ - libsystemd-basic.la \ - libsystemd-daemon.la \ - $(DBUS_LIBS) - -systemd_timedated_SOURCES = \ - src/timedated.c \ - src/dbus-common.c \ - src/polkit.c - -systemd_timedated_CFLAGS = \ - $(AM_CFLAGS) \ - $(DBUS_CFLAGS) - -systemd_timedated_LDADD = \ - libsystemd-basic.la \ - libsystemd-daemon.la \ - $(DBUS_LIBS) - -systemd_logind_SOURCES = \ - src/logind.c \ - src/logind-dbus.c \ - src/logind-device.c \ - src/logind-seat.c \ - src/logind-seat-dbus.c \ - src/logind-session.c \ - src/logind-session-dbus.c \ - src/logind-user.c \ - src/logind-user-dbus.c \ - src/dbus-common.c \ - src/dbus-loop.c \ - src/cgroup-util.c \ - src/polkit.c - -systemd_logind_CFLAGS = \ - $(AM_CFLAGS) \ - $(DBUS_CFLAGS) \ - $(UDEV_CFLAGS) \ - $(ACL_CFLAGS) - -systemd_logind_LDADD = \ - libsystemd-basic.la \ - libsystemd-daemon.la \ - $(DBUS_LIBS) \ - $(UDEV_LIBS) \ - $(ACL_LIBS) - -systemd_uaccess_SOURCES = \ - src/uaccess.c - -if HAVE_ACL -systemd_logind_SOURCES += \ - src/logind-acl.c - -systemd_uaccess_SOURCES += \ - src/logind-acl.c -endif - -systemd_uaccess_CFLAGS = \ - $(AM_CFLAGS) \ - $(UDEV_CFLAGS) \ - $(ACL_CFLAGS) - -systemd_uaccess_LDADD = \ - libsystemd-basic.la \ - libsystemd-daemon.la \ - libsystemd-login.la \ - $(UDEV_LIBS) \ - $(ACL_LIBS) - systemd_shutdown_SOURCES = \ src/mount-setup.c \ src/umount.c \ @@ -1018,26 +865,22 @@ systemd_modules_load_SOURCES = \ src/modules-load.c systemd_modules_load_CFLAGS = \ - $(AM_CFLAGS) + $(KMOD_CFLAGS) systemd_modules_load_LDADD = \ - libsystemd-basic.la + libsystemd-basic.la \ + $(KMOD_LIBS) systemd_tmpfiles_SOURCES = \ src/tmpfiles.c -systemd_tmpfiles_CFLAGS = \ - $(AM_CFLAGS) - systemd_tmpfiles_LDADD = \ libsystemd-basic.la systemd_machine_id_setup_SOURCES = \ src/machine-id-setup.c \ - src/machine-id-main.c - -systemd_machine_id_setup_CFLAGS = \ - $(AM_CFLAGS) + src/machine-id-main.c \ + src/sd-id128.c systemd_machine_id_setup_LDADD = \ libsystemd-basic.la @@ -1045,21 +888,9 @@ systemd_machine_id_setup_LDADD = \ systemd_sysctl_SOURCES = \ src/sysctl.c -systemd_sysctl_CFLAGS = \ - $(AM_CFLAGS) - systemd_sysctl_LDADD = \ libsystemd-basic.la -systemd_binfmt_SOURCES = \ - src/binfmt.c - -systemd_binfmt_CFLAGS = \ - $(AM_CFLAGS) - -systemd_binfmt_LDADD = \ - libsystemd-basic.la - systemd_fsck_SOURCES = \ src/fsck.c \ src/dbus-common.c @@ -1074,21 +905,9 @@ systemd_fsck_LDADD = \ $(UDEV_LIBS) \ $(DBUS_LIBS) -systemd_quotacheck_SOURCES = \ - src/quotacheck.c - -systemd_quotacheck_CFLAGS = \ - $(AM_CFLAGS) - -systemd_quotacheck_LDADD = \ - libsystemd-basic.la - systemd_timestamp_SOURCES = \ src/timestamp.c -systemd_timestamp_CFLAGS = \ - $(AM_CFLAGS) - systemd_timestamp_LDADD = \ libsystemd-basic.la @@ -1106,63 +925,20 @@ systemd_ac_power_LDADD = \ 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 - -systemd_cryptsetup_CFLAGS = \ - $(LIBCRYPTSETUP_CFLAGS) \ - $(UDEV_CFLAGS) \ - $(AM_CFLAGS) - -systemd_cryptsetup_LDADD = \ - $(LIBCRYPTSETUP_LIBS) \ - $(UDEV_LIBS) \ - libsystemd-basic.la - -systemd_cryptsetup_generator_SOURCES = \ - src/cryptsetup-generator.c \ - src/unit-name.c - -systemd_cryptsetup_generator_CFLAGS = \ - $(AM_CFLAGS) - -systemd_cryptsetup_generator_LDADD = \ - libsystemd-basic.la - systemd_getty_generator_SOURCES = \ src/getty-generator.c \ src/unit-name.c -systemd_getty_generator_CFLAGS = \ - $(AM_CFLAGS) - systemd_getty_generator_LDADD = \ libsystemd-basic.la -systemd_user_sessions_SOURCES = \ - src/user-sessions.c \ - src/cgroup-util.c +systemd_rc_local_generator_SOURCES = \ + src/rc-local-generator.c -systemd_user_sessions_CFLAGS = \ - $(AM_CFLAGS) - -systemd_user_sessions_LDADD = \ - libsystemd-basic.la - -systemd_vconsole_setup_SOURCES = \ - src/vconsole-setup.c - -systemd_vconsole_setup_CFLAGS = \ - $(AM_CFLAGS) - -systemd_vconsole_setup_LDADD = \ +systemd_rc_local_generator_LDADD = \ libsystemd-basic.la systemd_remount_api_vfs_SOURCES = \ @@ -1170,9 +946,6 @@ systemd_remount_api_vfs_SOURCES = \ src/mount-setup.c \ src/exit-status.c -systemd_remount_api_vfs_CFLAGS = \ - $(AM_CFLAGS) - systemd_remount_api_vfs_LDADD = \ libsystemd-basic.la @@ -1188,17 +961,6 @@ systemd_cgroups_agent_LDADD = \ libsystemd-basic.la \ $(DBUS_LIBS) -systemd_kmsg_syslogd_SOURCES = \ - src/kmsg-syslogd.c \ - src/fdset.c - -systemd_kmsg_syslogd_CFLAGS = \ - $(AM_CFLAGS) - -systemd_kmsg_syslogd_LDADD = \ - libsystemd-basic.la \ - libsystemd-daemon.la - systemctl_SOURCES = \ src/systemctl.c \ src/utmp-wtmp.c \ @@ -1208,9 +970,10 @@ systemctl_SOURCES = \ src/cgroup-util.c \ src/exit-status.c \ src/unit-name.c \ - src/pager.c \ - src/install.c \ - src/spawn-agent.c + src/pager.c \ + src/install.c \ + src/spawn-agent.c \ + src/logs-show.c systemctl_CFLAGS = \ $(AM_CFLAGS) \ @@ -1219,29 +982,13 @@ systemctl_CFLAGS = \ systemctl_LDADD = \ libsystemd-basic.la \ libsystemd-daemon.la \ + libsystemd-journal.la \ + libsystemd-id128.la \ $(DBUS_LIBS) -systemd_loginctl_SOURCES = \ - src/loginctl.c \ - src/dbus-common.c \ - src/cgroup-show.c \ - src/cgroup-util.c \ - src/pager.c \ - src/sysfs-show.c - -systemd_loginctl_CFLAGS = \ - $(AM_CFLAGS) \ - $(DBUS_CFLAGS) \ - $(UDEV_CFLAGS) - -systemd_loginctl_LDADD = \ - libsystemd-basic.la \ - $(DBUS_LIBS) \ - $(UDEV_LIBS) - systemd_notify_SOURCES = \ src/notify.c \ - src/sd-readahead.c + src/readahead/sd-readahead.c systemd_notify_LDADD = \ libsystemd-basic.la \ @@ -1260,48 +1007,26 @@ systemd_reply_password_SOURCES = \ systemd_reply_password_LDADD = \ libsystemd-basic.la -systemd_readahead_collect_SOURCES = \ - src/readahead-collect.c \ - src/readahead-common.c - -systemd_readahead_collect_CFLAGS = \ - $(UDEV_CFLAGS) - -systemd_readahead_collect_LDADD = \ - libsystemd-basic.la \ - libsystemd-daemon.la \ - $(UDEV_LIBS) - -systemd_readahead_replay_SOURCES = \ - src/readahead-replay.c \ - src/readahead-common.c - -systemd_readahead_replay_CFLAGS = \ - $(UDEV_CFLAGS) - -systemd_readahead_replay_LDADD = \ - libsystemd-basic.la \ - libsystemd-daemon.la \ - $(UDEV_LIBS) - systemd_cgls_SOURCES = \ src/cgls.c \ src/cgroup-show.c \ src/cgroup-util.c \ - src/pager.c - -systemd_cgls_CFLAGS = \ - $(AM_CFLAGS) + src/pager.c systemd_cgls_LDADD = \ libsystemd-basic.la +systemd_cgtop_SOURCES = \ + src/cgtop.c \ + src/cgroup-util.c + +systemd_cgtop_LDADD = \ + libsystemd-basic.la + systemd_nspawn_SOURCES = \ src/nspawn.c \ - src/cgroup-util.c - -systemd_nspawn_CFLAGS = \ - $(AM_CFLAGS) + src/cgroup-util.c \ + src/loopback-setup.c systemd_nspawn_LDADD = \ libsystemd-basic.la \ @@ -1315,7 +1040,8 @@ systemd_stdio_bridge_LDADD = \ systemadm_SOURCES = \ src/systemadm.vala \ - src/systemd-interfaces.vala + src/systemd-interfaces.vala \ + src/wraplabel.vala systemadm_CFLAGS = \ $(AM_CFLAGS) \ @@ -1328,6 +1054,7 @@ systemadm_CFLAGS = \ systemadm_VALAFLAGS = \ --pkg=posix \ --pkg=gtk+-2.0 \ + --pkg=gee-1.0 \ -g systemadm_LDADD = \ @@ -1350,12 +1077,7 @@ systemd_gnome_ask_password_agent_VALAFLAGS = \ --pkg=gtk+-2.0 \ --pkg=linux \ --pkg=gio-unix-2.0 \ - --pkg=libnotify -if LIBNOTIFY07 -systemd_gnome_ask_password_agent_VALAFLAGS += \ - -D LIBNOTIFY07 -endif -systemd_gnome_ask_password_agent_VALAFLAGS += \ + --pkg=libnotify \ -g systemd_gnome_ask_password_agent_LDADD = \ @@ -1370,41 +1092,22 @@ systemd_tty_ask_password_agent_SOURCES = \ systemd_tty_ask_password_agent_LDADD = \ libsystemd-basic.la -pam_systemd_la_SOURCES = \ - src/pam-module.c \ - src/dbus-common.c - -pam_systemd_la_CFLAGS = \ - $(AM_CFLAGS) \ - $(PAM_CFLAGS) \ - $(DBUS_CFLAGS) \ - -fvisibility=hidden - -pam_systemd_la_LDFLAGS = \ - -module \ - -export-dynamic \ - -avoid-version \ - -shared \ - -export-symbols-regex '^pam_sm_.*' - -pam_systemd_la_LIBADD = \ - libsystemd-basic.la \ - libsystemd-daemon.la \ - $(PAM_LIBS) \ - $(DBUS_LIBS) - +# ------------------------------------------------------------------------------ libsystemd_daemon_la_SOURCES = \ src/sd-daemon.c libsystemd_daemon_la_CFLAGS = \ - $(AM_CFLAGS) \ + $(AM_CFLAGS) \ -fvisibility=hidden \ - -DSD_EXPORT_SYMBOLS + -DSD_EXPORT_SYMBOLS libsystemd_daemon_la_LDFLAGS = \ - -shared \ - -version-info $(LIBSYSTEMD_DAEMON_CURRENT):$(LIBSYSTEMD_DAEMON_REVISION):$(LIBSYSTEMD_DAEMON_AGE) \ - -Wl,--version-script=$(top_srcdir)/src/libsystemd-daemon.sym + -shared \ + -version-info $(LIBSYSTEMD_DAEMON_CURRENT):$(LIBSYSTEMD_DAEMON_REVISION):$(LIBSYSTEMD_DAEMON_AGE) \ + -Wl,--version-script=$(top_srcdir)/src/libsystemd-daemon.sym + +pkginclude_HEADERS += \ + src/systemd/sd-daemon.h # move lib from $(libdir) to $(rootlibdir) and update devel link, if needed libsystemd-daemon-install-hook: @@ -1416,25 +1119,828 @@ libsystemd-daemon-install-hook: mv $(DESTDIR)$(libdir)/libsystemd-daemon.so.* $(DESTDIR)$(rootlibdir); \ fi +INSTALL_EXEC_HOOKS += \ + libsystemd-daemon-install-hook + libsystemd-daemon-uninstall-hook: rm -f $(DESTDIR)$(rootlibdir)/libsystemd-daemon.so* +UNINSTALL_EXEC_HOOKS += \ + libsystemd-daemon-uninstall-hook + +lib_LTLIBRARIES += \ + libsystemd-daemon.la + +pkgconfiglib_DATA += \ + src/libsystemd-daemon.pc + +MANPAGES += \ + man/sd-daemon.7 \ + man/sd_notify.3 \ + man/sd_listen_fds.3 \ + man/sd_is_fifo.3 \ + man/sd_booted.3 + +MANPAGES_ALIAS += \ + man/sd_is_socket.3 \ + man/sd_is_socket_unix.3 \ + man/sd_is_socket_inet.3 \ + man/sd_is_mq.3 \ + man/sd_notifyf.3 + +man/sd_is_socket.3: man/sd_is_fifo.3 +man/sd_is_socket_unix.3: man/sd_is_fifo.3 +man/sd_is_socket_inet.3: man/sd_is_fifo.3 +man/sd_is_mq.3: man/sd_is_fifo.3 +man/sd_notifyf.3: man/sd_notify.3 + +EXTRA_DIST += \ + src/libsystemd-daemon.pc.in \ + src/libsystemd-daemon.sym + +# ------------------------------------------------------------------------------ +libsystemd_id128_la_SOURCES = \ + src/sd-id128.c + +libsystemd_id128_la_CFLAGS = \ + $(AM_CFLAGS) \ + -fvisibility=hidden + +libsystemd_id128_la_LDFLAGS = \ + -shared \ + -version-info $(LIBSYSTEMD_ID128_CURRENT):$(LIBSYSTEMD_ID128_REVISION):$(LIBSYSTEMD_ID128_AGE) \ + -Wl,--version-script=$(top_srcdir)/src/libsystemd-id128.sym + +libsystemd_id128_la_LIBADD = \ + libsystemd-basic.la + +test_id128_SOURCES = \ + src/test-id128.c \ + src/sd-id128.c + +test_id128_LDADD = \ + libsystemd-basic.la + +noinst_PROGRAMS += \ + test-id128 + +pkginclude_HEADERS += \ + src/systemd/sd-id128.h + +lib_LTLIBRARIES += \ + libsystemd-id128.la + +pkgconfiglib_DATA += \ + src/libsystemd-id128.pc + +# move lib from $(libdir) to $(rootlibdir) and update devel link, if needed +libsystemd-id128-install-hook: + if test "$(libdir)" != "$(rootlibdir)"; then \ + mkdir -p $(DESTDIR)$(rootlibdir) && \ + so_img_name=$$(readlink $(DESTDIR)$(libdir)/libsystemd-id128.so) && \ + so_img_rel_target_prefix=$$(echo $(libdir) | sed 's,\(^/\|\)[^/][^/]*,..,g') && \ + ln -sf $$so_img_rel_target_prefix$(rootlibdir)/$$so_img_name $(DESTDIR)$(libdir)/libsystemd-id128.so && \ + mv $(DESTDIR)$(libdir)/libsystemd-id128.so.* $(DESTDIR)$(rootlibdir); \ + fi + +INSTALL_EXEC_HOOKS += \ + libsystemd-id128-install-hook + +libsystemd-id128-uninstall-hook: + rm -f $(DESTDIR)$(rootlibdir)/libsystemd-id128.so* + +UNINSTALL_EXEC_HOOKS += \ + libsystemd-id128-uninstall-hook + +EXTRA_DIST += \ + src/libsystemd-id128.pc.in \ + src/libsystemd-id128.sym + +# ------------------------------------------------------------------------------ +systemd_journald_SOURCES = \ + src/journal/journald.c \ + src/journal/sd-journal.c \ + src/journal/journal-file.c \ + src/journal/lookup3.c \ + src/journal/journal-rate-limit.c \ + src/sd-id128.c \ + src/cgroup-util.c + +if HAVE_ACL +systemd_journald_SOURCES += \ + src/acl-util.c +endif + +nodist_systemd_journald_SOURCES = \ + src/journal/journald-gperf.c + +systemd_journald_CFLAGS = \ + $(AM_CFLAGS) \ + $(ACL_CFLAGS) + +systemd_journald_LDADD = \ + libsystemd-basic.la \ + libsystemd-daemon.la \ + libsystemd-login.la \ + $(ACL_LIBS) + +if HAVE_XZ +systemd_journald_SOURCES += \ + src/journal/compress.c +systemd_journald_CFLAGS += \ + $(XZ_CFLAGS) +systemd_journald_LDADD += \ + $(XZ_LIBS) +endif + +systemd_cat_SOURCES = \ + src/journal/cat.c + +systemd_cat_LDADD = \ + libsystemd-basic.la \ + libsystemd-journal.la + +systemd_journalctl_SOURCES = \ + src/journal/journalctl.c \ + src/pager.c \ + src/logs-show.c + +systemd_journalctl_LDADD = \ + libsystemd-basic.la \ + libsystemd-journal.la \ + libsystemd-id128.la + +if HAVE_XZ +systemd_journalctl_SOURCES += \ + src/journal/compress.c +systemd_journalctl_CFLAGS = \ + $(AM_CFLAGS) \ + $(XZ_CFLAGS) +systemd_journalctl_LDADD += \ + $(XZ_LIBS) +endif + +test_journal_SOURCES = \ + src/journal/test-journal.c \ + src/journal/sd-journal.c \ + src/journal/journal-file.c \ + src/journal/lookup3.c \ + src/journal/journal-send.c \ + src/sd-id128.c + +test_journal_LDADD = \ + libsystemd-basic.la + +if HAVE_XZ +test_journal_SOURCES += \ + src/journal/compress.c + +test_journal_CFLAGS = \ + $(AM_CFLAGS) \ + $(XZ_CFLAGS) + +test_journal_LDADD += \ + $(XZ_LIBS) +endif + +libsystemd_journal_la_SOURCES = \ + src/journal/sd-journal.c \ + src/journal/journal-file.c \ + src/journal/lookup3.c \ + src/journal/journal-send.c + +libsystemd_journal_la_CFLAGS = \ + $(AM_CFLAGS) \ + -fvisibility=hidden + +libsystemd_journal_la_LDFLAGS = \ + -shared \ + -version-info $(LIBSYSTEMD_JOURNAL_CURRENT):$(LIBSYSTEMD_JOURNAL_REVISION):$(LIBSYSTEMD_JOURNAL_AGE) \ + -Wl,--version-script=$(top_srcdir)/src/journal/libsystemd-journal.sym + +libsystemd_journal_la_LIBADD = \ + libsystemd-basic.la \ + libsystemd-id128.la + +if HAVE_XZ +libsystemd_journal_la_SOURCES += \ + src/journal/compress.c + +libsystemd_journal_la_CFLAGS += \ + $(XZ_CFLAGS) + +libsystemd_journal_la_LIBADD += \ + $(XZ_LIBS) +endif + +# move lib from $(libdir) to $(rootlibdir) and update devel link, if needed +libsystemd-journal-install-hook: + if test "$(libdir)" != "$(rootlibdir)"; then \ + mkdir -p $(DESTDIR)$(rootlibdir) && \ + so_img_name=$$(readlink $(DESTDIR)$(libdir)/libsystemd-journal.so) && \ + so_img_rel_target_prefix=$$(echo $(libdir) | sed 's,\(^/\|\)[^/][^/]*,..,g') && \ + ln -sf $$so_img_rel_target_prefix$(rootlibdir)/$$so_img_name $(DESTDIR)$(libdir)/libsystemd-journal.so && \ + mv $(DESTDIR)$(libdir)/libsystemd-journal.so.* $(DESTDIR)$(rootlibdir); \ + fi + +INSTALL_EXEC_HOOKS += \ + libsystemd-journal-install-hook + +libsystemd-journal-uninstall-hook: + rm -f $(DESTDIR)$(rootlibdir)/libsystemd-journal.so* + +UNINSTALL_EXEC_HOOKS += \ + libsystemd-journal-uninstall-hook + +noinst_PROGRAMS += \ + test-journal + +pkginclude_HEADERS += \ + src/systemd/sd-journal.h \ + src/systemd/sd-messages.h + +lib_LTLIBRARIES += \ + libsystemd-journal.la + +rootlibexec_PROGRAMS += \ + systemd-journald + +rootbin_PROGRAMS += \ + systemd-journalctl + +bin_PROGRAMS += \ + systemd-cat + +dist_systemunit_DATA += \ + units/systemd-journald.socket + +nodist_systemunit_DATA += \ + units/systemd-journald.service + +dist_pkgsysconf_DATA += \ + src/journal/systemd-journald.conf + +pkgconfiglib_DATA += \ + src/journal/libsystemd-journal.pc + +journal-install-data-hook: + $(MKDIR_P) -m 0755 \ + $(DESTDIR)$(systemunitdir)/sockets.target.wants \ + $(DESTDIR)$(systemunitdir)/sysinit.target.wants + ( cd $(DESTDIR)$(systemunitdir)/sockets.target.wants && \ + rm -f systemd-journald.socket && \ + $(LN_S) ../systemd-journald.socket ) + ( cd $(DESTDIR)$(systemunitdir)/sysinit.target.wants && \ + rm -f systemd-journald.service && \ + $(LN_S) ../systemd-journald.service ) + +INSTALL_DATA_HOOKS += \ + journal-install-data-hook + +EXTRA_DIST += \ + src/journal/journald.h \ + src/journal/journal-def.h \ + src/journal/journal-internal.h \ + src/journal/journal-file.h \ + src/journal/lookup3.h \ + src/journal/compress.h \ + src/journal/journal-rate-limit.h \ + src/journal/libsystemd-journal.pc.in \ + src/journal/libsystemd-journal.sym \ + units/systemd-journald.service.in \ + src/journal/journald-gperf.gperf + +CLEANFILES += \ + src/journal/journald-gperf.c + +# ------------------------------------------------------------------------------ +if ENABLE_COREDUMP +systemd_coredump_SOURCES = \ + src/journal/coredump.c + +systemd_coredump_LDADD = \ + libsystemd-basic.la \ + libsystemd-journal.la \ + libsystemd-login.la + +rootlibexec_PROGRAMS += \ + systemd-coredump + +sysctl_DATA = \ + sysctl.d/coredump.conf + +EXTRA_DIST += \ + sysctl.d/coredump.conf.in + +CLEANFILES += \ + sysctl.d/coredump.conf +endif + +# ------------------------------------------------------------------------------ +if ENABLE_BINFMT +systemd_binfmt_SOURCES = \ + src/binfmt/binfmt.c + +systemd_binfmt_LDADD = \ + libsystemd-basic.la + +rootlibexec_PROGRAMS += \ + systemd-binfmt + +dist_systemunit_DATA += \ + units/proc-sys-fs-binfmt_misc.automount \ + units/proc-sys-fs-binfmt_misc.mount + +nodist_systemunit_DATA += \ + units/systemd-binfmt.service + +binfmt-install-data-hook: + $(MKDIR_P) -m 0755 \ + $(DESTDIR)$(prefix)/lib/binfmt.d \ + $(DESTDIR)$(sysconfdir)/binfmt.d \ + $(DESTDIR)$(systemunitdir)/sysinit.target.wants + ( cd $(DESTDIR)$(systemunitdir)/sysinit.target.wants && \ + rm -f systemd-binfmt.service \ + proc-sys-fs-binfmt_misc.automount && \ + $(LN_S) ../systemd-binfmt.service systemd-binfmt.service && \ + $(LN_S) ../proc-sys-fs-binfmt_misc.automount proc-sys-fs-binfmt_misc.automount ) + +INSTALL_DATA_HOOKS += \ + binfmt-install-data-hook + +MANPAGES += \ + man/binfmt.d.5 + +EXTRA_DIST += \ + units/systemd-binfmt.service.in +endif + +# ------------------------------------------------------------------------------ +if ENABLE_VCONSOLE +systemd_vconsole_setup_SOURCES = \ + src/vconsole/vconsole-setup.c + +systemd_vconsole_setup_LDADD = \ + libsystemd-basic.la + +rootlibexec_PROGRAMS += \ + systemd-vconsole-setup + +nodist_systemunit_DATA += \ + units/systemd-vconsole-setup.service + +vconsole-install-data-hook: + $(MKDIR_P) -m 0755 \ + $(DESTDIR)$(systemunitdir)/sysinit.target.wants + ( cd $(DESTDIR)$(systemunitdir)/sysinit.target.wants && \ + rm -f systemd-vconsole-setup.service && \ + $(LN_S) ../systemd-vconsole-setup.service systemd-vconsole-setup.service ) + +INSTALL_DATA_HOOKS += \ + vconsole-install-data-hook + +MANPAGES += \ + man/vconsole.conf.5 + +EXTRA_DIST += \ + units/systemd-vconsole-setup.service.in +endif + +# ------------------------------------------------------------------------------ +if ENABLE_READAHEAD +systemd_readahead_collect_SOURCES = \ + src/readahead/readahead-collect.c \ + src/readahead/readahead-common.c + +systemd_readahead_collect_LDADD = \ + libsystemd-basic.la \ + libsystemd-daemon.la \ + $(UDEV_LIBS) + +systemd_readahead_collect_CFLAGS = \ + $(AM_CFLAGS) \ + $(UDEV_CFLAGS) + +systemd_readahead_replay_SOURCES = \ + src/readahead/readahead-replay.c \ + src/readahead/readahead-common.c + +systemd_readahead_replay_CFLAGS = \ + $(AM_CFLAGS) \ + $(UDEV_CFLAGS) + +systemd_readahead_replay_LDADD = \ + libsystemd-basic.la \ + libsystemd-daemon.la \ + $(UDEV_LIBS) + +rootlibexec_PROGRAMS += \ + systemd-readahead-collect \ + systemd-readahead-replay + +dist_systemunit_DATA += \ + units/systemd-readahead-done.timer + +nodist_systemunit_DATA += \ + units/systemd-readahead-collect.service \ + units/systemd-readahead-replay.service \ + units/systemd-readahead-done.service + +EXTRA_DIST += \ + src/systemd/sd-readahead.h \ + src/readahead/readahead-common.h \ + units/systemd-readahead-collect.service.in \ + units/systemd-readahead-replay.service.in \ + units/systemd-readahead-done.service.in + +MANPAGES += \ + man/sd_readahead.3 \ + man/sd-readahead.7 +endif + +# ------------------------------------------------------------------------------ +if ENABLE_QUOTACHECK +rootlibexec_PROGRAMS += \ + systemd-quotacheck + +nodist_systemunit_DATA += \ + units/quotacheck.service + +EXTRA_DIST += \ + units/quotacheck.service.in + +systemd_quotacheck_SOURCES = \ + src/quotacheck.c + +systemd_quotacheck_LDADD = \ + libsystemd-basic.la +endif + +# ------------------------------------------------------------------------------ +if ENABLE_RANDOMSEED +rootlibexec_PROGRAMS += \ + systemd-random-seed + +nodist_systemunit_DATA += \ + units/systemd-random-seed-save.service \ + units/systemd-random-seed-load.service + +EXTRA_DIST += \ + units/systemd-random-seed-save.service.in \ + units/systemd-random-seed-load.service.in + +systemd_random_seed_SOURCES = \ + src/random-seed.c + +systemd_random_seed_LDADD = \ + libsystemd-basic.la + +randomseed-install-data-hook: + $(MKDIR_P) -m 0755 \ + $(DESTDIR)$(systemunitdir)/shutdown.target.wants \ + $(DESTDIR)$(systemunitdir)/sysinit.target.wants + ( cd $(DESTDIR)$(systemunitdir)/shutdown.target.wants && \ + rm -f systemd-random-seed-save.service && \ + $(LN_S) ../systemd-random-seed-save.service systemd-random-seed-save.service ) + ( cd $(DESTDIR)$(systemunitdir)/sysinit.target.wants && \ + rm -f systemd-random-seed-load.service && \ + $(LN_S) ../systemd-random-seed-load.service systemd-random-seed-load.service ) + +INSTALL_DATA_HOOKS += \ + randomseed-install-data-hook +endif + +# ------------------------------------------------------------------------------ +if HAVE_LIBCRYPTSETUP +rootlibexec_PROGRAMS += \ + systemd-cryptsetup + +systemgenerator_PROGRAMS += \ + systemd-cryptsetup-generator + +dist_systemunit_DATA += \ + units/cryptsetup.target + +systemd_cryptsetup_SOURCES = \ + src/cryptsetup/cryptsetup.c \ + src/ask-password-api.c + +systemd_cryptsetup_CFLAGS = \ + $(AM_CFLAGS) \ + $(LIBCRYPTSETUP_CFLAGS) \ + $(UDEV_CFLAGS) + +systemd_cryptsetup_LDADD = \ + $(LIBCRYPTSETUP_LIBS) \ + $(UDEV_LIBS) \ + libsystemd-basic.la + +systemd_cryptsetup_generator_SOURCES = \ + src/cryptsetup/cryptsetup-generator.c \ + src/unit-name.c + +systemd_cryptsetup_generator_LDADD = \ + libsystemd-basic.la + +cryptsetup-install-data-hook: + $(MKDIR_P) -m 0755 \ + $(DESTDIR)$(systemunitdir)/sysinit.target.wants + ( cd $(DESTDIR)$(systemunitdir)/sysinit.target.wants && \ + rm -f cryptsetup.target && \ + $(LN_S) ../cryptsetup.target cryptsetup.target ) + +INSTALL_DATA_HOOKS += \ + cryptsetup-install-data-hook +endif + +# ------------------------------------------------------------------------------ +if ENABLE_HOSTNAMED +systemd_hostnamed_SOURCES = \ + src/hostname/hostnamed.c \ + src/dbus-common.c \ + src/polkit.c + +systemd_hostnamed_CFLAGS = \ + $(AM_CFLAGS) \ + $(DBUS_CFLAGS) + +systemd_hostnamed_LDADD = \ + libsystemd-basic.la \ + libsystemd-daemon.la \ + $(DBUS_LIBS) + +rootlibexec_PROGRAMS += \ + systemd-hostnamed + +nodist_systemunit_DATA += \ + units/systemd-hostnamed.service + +dist_dbuspolicy_DATA += \ + src/hostname/org.freedesktop.hostname1.conf + +dist_dbussystemservice_DATA += \ + src/hostname/org.freedesktop.hostname1.service + +polkitpolicy_in_files += \ + src/hostname/org.freedesktop.hostname1.policy.in + +dbusinterface_DATA += \ + org.freedesktop.hostname1.xml + +org.freedesktop.hostname1.xml: systemd-hostnamed + $(AM_V_GEN)$(LIBTOOL) --mode=execute $(OBJCOPY) -O binary -j introspect.hostname1 $< $@.tmp && \ + $(STRINGS) $@.tmp | $(AWK) -f $(srcdir)/introspect.awk | \ + $(DBUS_PREPROCESS) -o $@ - && rm $@.tmp + +hostnamed-install-data-hook: + ( cd $(DESTDIR)$(systemunitdir) && \ + rm -f dbus-org.freedesktop.hostname1.service && \ + $(LN_S) systemd-hostnamed.service dbus-org.freedesktop.hostname1.service ) + +INSTALL_DATA_HOOKS += \ + hostnamed-install-data-hook + +EXTRA_DIST += \ + units/systemd-hostnamed.service.in +endif + +# ------------------------------------------------------------------------------ +if ENABLE_LOCALED +systemd_localed_SOURCES = \ + src/locale/localed.c \ + src/dbus-common.c \ + src/polkit.c + +systemd_localed_CFLAGS = \ + $(AM_CFLAGS) \ + $(DBUS_CFLAGS) + +systemd_localed_LDADD = \ + libsystemd-basic.la \ + libsystemd-daemon.la \ + $(DBUS_LIBS) + +nodist_systemunit_DATA += \ + units/systemd-localed.service + +rootlibexec_PROGRAMS += \ + systemd-localed + +dist_dbuspolicy_DATA += \ + src/locale/org.freedesktop.locale1.conf + +dist_dbussystemservice_DATA += \ + src/locale/org.freedesktop.locale1.service + +polkitpolicy_in_files += \ + src/locale/org.freedesktop.locale1.policy.in + +dbusinterface_DATA += \ + org.freedesktop.locale1.xml + +org.freedesktop.locale1.xml: systemd-localed + $(AM_V_GEN)$(LIBTOOL) --mode=execute $(OBJCOPY) -O binary -j introspect.locale1 $< $@.tmp && \ + $(STRINGS) $@.tmp | $(AWK) -f $(srcdir)/introspect.awk | \ + $(DBUS_PREPROCESS) -o $@ - && rm $@.tmp + +localed-install-data-hook: + ( cd $(DESTDIR)$(systemunitdir) && \ + rm -f dbus-org.freedesktop.locale1.service && \ + $(LN_S) systemd-localed.service dbus-org.freedesktop.locale1.service ) + +INSTALL_DATA_HOOKS += \ + localed-install-data-hook + +EXTRA_DIST += \ + units/systemd-localed.service.in + +dist_pkgdata_DATA = \ + src/locale/kbd-model-map + +dist_noinst_SCRIPT = \ + src/locale/generate-kbd-model-map + +update-kbd-model-map: + src/locale/generate-kbd-model-map > src/locale/kbd-model-map + +endif + +# ------------------------------------------------------------------------------ +if ENABLE_TIMEDATED +systemd_timedated_SOURCES = \ + src/timedate/timedated.c \ + src/dbus-common.c \ + src/polkit.c + +systemd_timedated_CFLAGS = \ + $(AM_CFLAGS) \ + $(DBUS_CFLAGS) + +systemd_timedated_LDADD = \ + libsystemd-basic.la \ + libsystemd-daemon.la \ + $(DBUS_LIBS) + +rootlibexec_PROGRAMS += \ + systemd-timedated + +dist_dbussystemservice_DATA += \ + src/timedate/org.freedesktop.timedate1.service + +dist_dbuspolicy_DATA += \ + src/timedate/org.freedesktop.timedate1.conf + +nodist_systemunit_DATA += \ + units/systemd-timedated.service + +polkitpolicy_in_files += \ + src/timedate/org.freedesktop.timedate1.policy.in + +org.freedesktop.timedate1.xml: systemd-timedated + $(AM_V_GEN)$(LIBTOOL) --mode=execute $(OBJCOPY) -O binary -j introspect.timedate1 $< $@.tmp && \ + $(STRINGS) $@.tmp | $(AWK) -f $(srcdir)/introspect.awk | \ + $(DBUS_PREPROCESS) -o $@ - && rm $@.tmp + +dbusinterface_DATA += \ + org.freedesktop.timedate1.xml + +timedated-install-data-hook: + ( cd $(DESTDIR)$(systemunitdir) && \ + rm -f dbus-org.freedesktop.timedate1.service && \ + $(LN_S) systemd-timedated.service dbus-org.freedesktop.timedate1.service ) + +INSTALL_DATA_HOOKS += \ + timedated-install-data-hook + +EXTRA_DIST += \ + units/systemd-timedated.service.in +endif + +# ------------------------------------------------------------------------------ +if ENABLE_LOGIND +systemd_logind_SOURCES = \ + src/login/logind.c \ + src/login/logind-dbus.c \ + src/login/logind-device.c \ + src/login/logind-seat.c \ + src/login/logind-seat-dbus.c \ + src/login/logind-session.c \ + src/login/logind-session-dbus.c \ + src/login/logind-user.c \ + src/login/logind-user-dbus.c \ + src/dbus-common.c \ + src/dbus-loop.c \ + src/cgroup-util.c \ + src/polkit.c + +nodist_systemd_logind_SOURCES = \ + src/login/logind-gperf.c + +if HAVE_ACL +systemd_logind_SOURCES += \ + src/login/logind-acl.c \ + src/acl-util.c +endif + +systemd_logind_CFLAGS = \ + $(AM_CFLAGS) \ + $(DBUS_CFLAGS) \ + $(UDEV_CFLAGS) \ + $(ACL_CFLAGS) + +systemd_logind_LDADD = \ + libsystemd-basic.la \ + libsystemd-daemon.la \ + $(DBUS_LIBS) \ + $(UDEV_LIBS) \ + $(ACL_LIBS) + +systemd_user_sessions_SOURCES = \ + src/login/user-sessions.c \ + src/cgroup-util.c + +systemd_user_sessions_LDADD = \ + libsystemd-basic.la + +rootlibexec_PROGRAMS += \ + systemd-logind \ + systemd-user-sessions + +systemd_loginctl_SOURCES = \ + src/login/loginctl.c \ + src/login/sysfs-show.c \ + src/dbus-common.c \ + src/cgroup-show.c \ + src/cgroup-util.c \ + src/pager.c + +systemd_loginctl_CFLAGS = \ + $(AM_CFLAGS) \ + $(DBUS_CFLAGS) \ + $(UDEV_CFLAGS) + +systemd_loginctl_LDADD = \ + libsystemd-basic.la \ + $(DBUS_LIBS) \ + $(UDEV_LIBS) + +rootbin_PROGRAMS += \ + systemd-loginctl + +test_login_SOURCES = \ + src/login/test-login.c + +test_login_LDADD = \ + libsystemd-basic.la \ + libsystemd-login.la + +noinst_PROGRAMS += \ + test-login + libsystemd_login_la_SOURCES = \ - src/sd-login.c \ - src/cgroup-util.c + src/login/sd-login.c \ + src/cgroup-util.c libsystemd_login_la_CFLAGS = \ - $(AM_CFLAGS) \ + $(AM_CFLAGS) \ -fvisibility=hidden libsystemd_login_la_LDFLAGS = \ - -shared \ - -version-info $(LIBSYSTEMD_LOGIN_CURRENT):$(LIBSYSTEMD_LOGIN_REVISION):$(LIBSYSTEMD_LOGIN_AGE) \ - -Wl,--version-script=$(top_srcdir)/src/libsystemd-login.sym + -shared \ + -version-info $(LIBSYSTEMD_LOGIN_CURRENT):$(LIBSYSTEMD_LOGIN_REVISION):$(LIBSYSTEMD_LOGIN_AGE) \ + -Wl,--version-script=$(top_srcdir)/src/login/libsystemd-login.sym libsystemd_login_la_LIBADD = \ libsystemd-basic.la +if HAVE_PAM +pam_systemd_la_SOURCES = \ + src/login/pam-module.c \ + src/dbus-common.c + +pam_systemd_la_CFLAGS = \ + $(AM_CFLAGS) \ + $(PAM_CFLAGS) \ + $(DBUS_CFLAGS) \ + -fvisibility=hidden + +pam_systemd_la_LDFLAGS = \ + -module \ + -export-dynamic \ + -avoid-version \ + -shared \ + -export-symbols-regex '^pam_sm_.*' + +pam_systemd_la_LIBADD = \ + libsystemd-basic.la \ + libsystemd-daemon.la \ + $(PAM_LIBS) \ + $(DBUS_LIBS) + +pamlib_LTLIBRARIES = \ + pam_systemd.la +endif + # move lib from $(libdir) to $(rootlibdir) and update devel link, if needed libsystemd-login-install-hook: if test "$(libdir)" != "$(rootlibdir)"; then \ @@ -1445,12 +1951,167 @@ libsystemd-login-install-hook: mv $(DESTDIR)$(libdir)/libsystemd-login.so.* $(DESTDIR)$(rootlibdir); \ fi +INSTALL_EXEC_HOOKS += \ + libsystemd-login-install-hook + libsystemd-login-uninstall-hook: rm -f $(DESTDIR)$(rootlibdir)/libsystemd-login.so* +UNINSTALL_EXEC_HOOKS += \ + libsystemd-login-uninstall-hook + +nodist_systemunit_DATA += \ + units/systemd-logind.service \ + units/systemd-user-sessions.service + +dist_dbussystemservice_DATA += \ + src/login/org.freedesktop.login1.service + +dist_dbuspolicy_DATA += \ + src/login/org.freedesktop.login1.conf + +dist_pkgsysconf_DATA += \ + src/login/systemd-logind.conf + +pkginclude_HEADERS += \ + src/systemd/sd-login.h + +lib_LTLIBRARIES += \ + libsystemd-login.la + +pkgconfiglib_DATA += \ + src/login/libsystemd-login.pc + +polkitpolicy_in_files += \ + src/login/org.freedesktop.login1.policy.in + +logind-install-data-hook: + $(MKDIR_P) -m 0755 \ + $(DESTDIR)$(systemunitdir)/multi-user.target.wants \ + $(DESTDIR)$(localstatedir)/lib/systemd + ( cd $(DESTDIR)$(systemunitdir) && \ + rm -f dbus-org.freedesktop.login1.service && \ + $(LN_S) systemd-logind.service dbus-org.freedesktop.login1.service) + ( cd $(DESTDIR)$(systemunitdir)/multi-user.target.wants && \ + rm -f systemd-logind.service systemd-user-sessions.service && \ + $(LN_S) ../systemd-logind.service systemd-logind.service && \ + $(LN_S) ../systemd-user-sessions.service systemd-user-sessions.service ) + +INSTALL_DATA_HOOKS += \ + logind-install-data-hook + +systemd_multi_seat_x_SOURCES = \ + src/login/multi-seat-x.c + +systemd_multi_seat_x_CFLAGS = \ + $(AM_CFLAGS) \ + $(UDEV_CFLAGS) + +systemd_multi_seat_x_LDADD = \ + libsystemd-basic.la \ + $(UDEV_LIBS) + +rootlibexec_PROGRAMS += \ + systemd-multi-seat-x + +systemd_uaccess_SOURCES = \ + src/login/uaccess.c + +if HAVE_ACL +systemd_uaccess_SOURCES += \ + src/login/logind-acl.c \ + src/acl-util.c +endif + +systemd_uaccess_CFLAGS = \ + $(AM_CFLAGS) \ + $(UDEV_CFLAGS) \ + $(ACL_CFLAGS) + +systemd_uaccess_LDADD = \ + libsystemd-basic.la \ + libsystemd-daemon.la \ + libsystemd-login.la \ + $(UDEV_LIBS) \ + $(ACL_LIBS) + +rootlibexec_PROGRAMS += \ + systemd-uaccess + +dist_udevrules_DATA += \ + src/login/70-uaccess.rules + +dist_udevrules_DATA += \ + src/login/71-seat.rules + +nodist_udevrules_DATA += \ + src/login/73-seat-late.rules + +MANPAGES += \ + man/systemd-logind.conf.5 \ + man/sd-login.7 \ + man/systemd-loginctl.1 \ + man/sd_login_monitor_new.3 \ + man/sd_pid_get_session.3 \ + man/sd_uid_get_state.3 \ + man/sd_session_is_active.3 \ + man/sd_seat_get_active.3 \ + man/sd_get_seats.3 + +MANPAGES_ALIAS += \ + man/sd_login_monitor_unref.3 \ + man/sd_login_monitor_flush.3 \ + man/sd_login_monitor_get_fd.3 \ + man/sd_session_get_uid.3 \ + man/sd_session_get_seat.3 \ + man/sd_pid_get_owner_uid.3 \ + man/sd_pid_get_unit.3 \ + man/sd_uid_is_on_seat.3 \ + man/sd_uid_get_sessions.3 \ + man/sd_uid_get_seats.3 \ + man/sd_seat_get_sessions.3 \ + man/sd_seat_can_multi_session.3 \ + man/sd_get_sessions.3 \ + man/sd_get_uids.3 + +man/sd_login_monitor_unref.3: man/sd_login_monitor_new.3 +man/sd_login_monitor_flush.3: man/sd_login_monitor_new.3 +man/sd_login_monitor_get_fd.3: man/sd_login_monitor_new.3 +man/sd_session_get_uid.3: man/sd_session_is_active.3 +man/sd_session_get_seat.3: man/sd_session_is_active.3 +man/sd_pid_get_owner_uid.3: man/sd_pid_get_session.3 +man/sd_pid_get_unit.3: man/sd_pid_get_session.3 +man/sd_uid_is_on_seat.3: man/sd_uid_get_state.3 +man/sd_uid_get_sessions.3: man/sd_uid_get_state.3 +man/sd_uid_get_seats.3: man/sd_uid_get_state.3 +man/sd_seat_get_sessions.3: man/sd_seat_get_active.3 +man/sd_seat_can_multi_session.3: man/sd_seat_get_active.3 +man/sd_get_sessions.3: man/sd_get_seats.3 +man/sd_get_uids.3: man/sd_get_seats.3 + +EXTRA_DIST += \ + src/login/logind-gperf.gperf \ + src/login/libsystemd-login.pc.in \ + src/login/libsystemd-login.sym \ + src/login/logind.h \ + src/login/logind-device.h \ + src/login/logind-seat.h \ + src/login/logind-session.h \ + src/login/logind-user.h \ + src/login/logind-acl.h \ + src/login/73-seat-late.rules.in \ + units/systemd-logind.service.in \ + units/systemd-user-sessions.service.in + +CLEANFILES += \ + src/login/logind-gperf.c \ + src/login/73-seat-late.rules +endif +# ------------------------------------------------------------------------------ + SED_PROCESS = \ $(AM_V_GEN)$(MKDIR_P) $(dir $@) && \ - $(SED) -e 's,@rootlibexecdir\@,$(rootlibexecdir),g' \ + $(SED) -e 's,@rootlibexecdir\@,$(rootlibexecdir),g' \ -e 's,@rootbindir\@,$(rootbindir),g' \ -e 's,@bindir\@,$(bindir),g' \ -e 's,@SYSTEMCTL\@,$(rootbindir)/systemctl,g' \ @@ -1475,6 +2136,9 @@ units/%: units/%.in Makefile man/%: man/%.in Makefile $(SED_PROCESS) +sysctl.d/%: sysctl.d/%.in Makefile + $(SED_PROCESS) + %.pc: %.pc.in Makefile $(SED_PROCESS) @@ -1484,13 +2148,25 @@ src/%.policy.in: src/%.policy.in.in Makefile src/%.rules: src/%.rules.in Makefile $(SED_PROCESS) +src/%.c: src/%.gperf + $(AM_V_GEN)$(MKDIR_P) $(dir $@) && \ + $(GPERF) < $< > $@ + +src/%: src/%.m4 + $(AM_V_GEN)$(MKDIR_P) $(dir $@) && \ + $(M4) -P $(M4_DEFINES) < $< > $@ || rm $@ + +src/load-fragment-gperf-nulstr.c: src/load-fragment-gperf.gperf + $(AM_V_GEN)$(MKDIR_P) $(dir $@) && \ + $(AWK) 'BEGIN{ keywords=0 ; FS="," ; print "extern const char load_fragment_gperf_nulstr[];" ; print "const char load_fragment_gperf_nulstr[] ="} ; keyword==1 { print "\"" $$1 "\\0\"" } ; /%%/ { keyword=1} ; END { print ";" }' < $< > $@ || rm $@ + M4_PROCESS_SYSTEM = \ $(AM_V_GEN)$(MKDIR_P) $(dir $@) && \ - $(M4) -P $(M4_DISTRO_FLAG) -DFOR_SYSTEM=1 < $< > $@ || rm $@ + $(M4) -P $(M4_DEFINES) -DFOR_SYSTEM=1 < $< > $@ || rm $@ M4_PROCESS_USER = \ $(AM_V_GEN)$(MKDIR_P) $(dir $@) && \ - $(M4) -P $(M4_DISTRO_FLAG) -DFOR_USER=1 < $< > $@ || rm $@ + $(M4) -P $(M4_DEFINES) -DFOR_USER=1 < $< > $@ || rm $@ units/%: units/%.m4 Makefile $(M4_PROCESS_SYSTEM) @@ -1498,7 +2174,7 @@ units/%: units/%.m4 Makefile units/user/%: units/%.m4 Makefile $(M4_PROCESS_USER) -CLEANFILES = \ +CLEANFILES += \ $(nodist_systemunit_DATA) \ $(nodist_userunit_DATA) \ $(nodist_man_MANS) \ @@ -1506,8 +2182,10 @@ CLEANFILES = \ $(pkgconfigdata_DATA) \ $(pkgconfiglib_DATA) \ $(nodist_polkitpolicy_DATA) \ - src/73-seat-late.rules \ - src/99-systemd.rules + src/load-fragment-gperf.gperf \ + src/load-fragment-gperf.c \ + src/load-fragment-gperf-nulstr.c \ + src/99-systemd.rules if HAVE_VALAC CLEANFILES += \ @@ -1517,7 +2195,7 @@ endif if HAVE_XSLTPROC XSLTPROC_FLAGS = \ --nonet \ - --param funcsynopsis.style "'ansi'" + --stringparam funcsynopsis.style ansi XSLTPROC_PROCESS_MAN = \ $(AM_V_GEN)$(MKDIR_P) $(dir $@) && \ @@ -1530,11 +2208,11 @@ XSLTPROC_PROCESS_MAN_IN = \ XSLTPROC_PROCESS_HTML = \ $(AM_V_GEN)$(MKDIR_P) $(dir $@) && \ - $(XSLTPROC) -o $@ $(XSLTPROC_FLAGS) http://docbook.sourceforge.net/release/xsl/current/xhtml-1_1/docbook.xsl $< + $(XSLTPROC) -o $@ $(XSLTPROC_FLAGS) $(srcdir)/man/custom-html.xsl $< XSLTPROC_PROCESS_HTML_IN = \ $(AM_V_GEN)$(MKDIR_P) $(dir $@) && \ - $(XSLTPROC) -o ${@:.in=} $(XSLTPROC_FLAGS) http://docbook.sourceforge.net/release/xsl/current/xhtml-1_1/docbook.xsl $< && \ + $(XSLTPROC) -o ${@:.in=} $(XSLTPROC_FLAGS) $(srcdir)/man/custom-html.xsl $< && \ mv ${@:.in=} $@ man/%.1: man/%.xml @@ -1587,25 +2265,10 @@ org.freedesktop.systemd1.%.xml: systemd $(STRINGS) $@.tmp | $(AWK) -f $(srcdir)/introspect.awk | \ $(DBUS_PREPROCESS) -o $@ - && rm $@.tmp -org.freedesktop.hostname1.xml: systemd-hostnamed - $(AM_V_GEN)$(LIBTOOL) --mode=execute $(OBJCOPY) -O binary -j introspect.hostname1 $< $@.tmp && \ - $(STRINGS) $@.tmp | $(AWK) -f $(srcdir)/introspect.awk | \ - $(DBUS_PREPROCESS) -o $@ - && rm $@.tmp - -org.freedesktop.locale1.xml: systemd-localed - $(AM_V_GEN)$(LIBTOOL) --mode=execute $(OBJCOPY) -O binary -j introspect.locale1 $< $@.tmp && \ - $(STRINGS) $@.tmp | $(AWK) -f $(srcdir)/introspect.awk | \ - $(DBUS_PREPROCESS) -o $@ - && rm $@.tmp - -org.freedesktop.timedate1.xml: systemd-timedated - $(AM_V_GEN)$(LIBTOOL) --mode=execute $(OBJCOPY) -O binary -j introspect.timedate1 $< $@.tmp && \ - $(STRINGS) $@.tmp | $(AWK) -f $(srcdir)/introspect.awk | \ - $(DBUS_PREPROCESS) -o $@ - && rm $@.tmp - CLEANFILES += \ $(dbusinterface_DATA) -install-data-hook: +systemd-install-data-hook: $(MKDIR_P) -m 0755 \ $(DESTDIR)$(tmpfilesdir) \ $(DESTDIR)$(sysconfdir)/tmpfiles.d \ @@ -1616,11 +2279,6 @@ install-data-hook: $(DESTDIR)$(systemshutdowndir) \ $(DESTDIR)$(systemgeneratordir) \ $(DESTDIR)$(usergeneratordir) -if ENABLE_BINFMT - $(MKDIR_P) -m 0755 \ - $(DESTDIR)$(prefix)/lib/binfmt.d \ - $(DESTDIR)$(sysconfdir)/binfmt.d -endif $(MKDIR_P) -m 0755 \ $(DESTDIR)$(systemunitdir) \ $(DESTDIR)$(userunitdir) \ @@ -1648,11 +2306,9 @@ endif rm -f user && \ $(LN_S) $(pkgsysconfdir)/user user ) ( cd $(DESTDIR)$(systemunitdir)/sockets.target.wants && \ - rm -f systemd-initctl.socket systemd-logger.socket systemd-shutdownd.socket syslog.socket && \ - $(LN_S) ../systemd-logger.socket systemd-logger.socket && \ + rm -f systemd-initctl.socket systemd-shutdownd.socket && \ $(LN_S) ../systemd-initctl.socket systemd-initctl.socket && \ - $(LN_S) ../systemd-shutdownd.socket systemd-shutdownd.socket && \ - $(LN_S) ../syslog.socket syslog.socket ) + $(LN_S) ../systemd-shutdownd.socket systemd-shutdownd.socket ) ( cd $(DESTDIR)$(systemunitdir)/runlevel1.target.wants && \ rm -f systemd-update-utmp-runlevel.service && \ $(LN_S) ../systemd-update-utmp-runlevel.service systemd-update-utmp-runlevel.service ) @@ -1669,10 +2325,8 @@ endif rm -f systemd-update-utmp-runlevel.service && \ $(LN_S) ../systemd-update-utmp-runlevel.service systemd-update-utmp-runlevel.service ) ( cd $(DESTDIR)$(systemunitdir)/shutdown.target.wants && \ - rm -f systemd-update-utmp-shutdown.service \ - systemd-random-seed-save.service && \ - $(LN_S) ../systemd-update-utmp-shutdown.service systemd-update-utmp-shutdown.service && \ - $(LN_S) ../systemd-random-seed-save.service systemd-random-seed-save.service ) + rm -f systemd-update-utmp-shutdown.service && \ + $(LN_S) ../systemd-update-utmp-shutdown.service systemd-update-utmp-shutdown.service ) ( cd $(DESTDIR)$(systemunitdir)/local-fs.target.wants && \ rm -f systemd-remount-api-vfs.service \ fsck-root.service \ @@ -1701,20 +2355,14 @@ endif $(LN_S) graphical.target runlevel5.target && \ $(LN_S) reboot.target runlevel6.target ) ( cd $(DESTDIR)$(systemunitdir) && \ - rm -f default.target ctrl-alt-del.target dbus-org.freedesktop.hostname1.service dbus-org.freedesktop.locale1.service dbus-org.freedesktop.timedate1.service dbus-org.freedesktop.login1.service autovt@.service && \ + rm -f default.target ctrl-alt-del.target autovt@.service && \ $(LN_S) graphical.target default.target && \ $(LN_S) reboot.target ctrl-alt-del.target && \ - $(LN_S) systemd-hostnamed.service dbus-org.freedesktop.hostname1.service && \ - $(LN_S) systemd-localed.service dbus-org.freedesktop.locale1.service && \ - $(LN_S) systemd-timedated.service dbus-org.freedesktop.timedate1.service && \ - $(LN_S) systemd-logind.service dbus-org.freedesktop.login1.service && \ - $(LN_S) getty@.service autovt@.service ) + $(LN_S) getty@.service autovt@.service ) ( cd $(DESTDIR)$(systemunitdir)/multi-user.target.wants && \ - rm -f getty.target systemd-user-sessions.service systemd-ask-password-wall.path systemd-logind.service && \ + rm -f getty.target systemd-ask-password-wall.path && \ $(LN_S) ../getty.target getty.target && \ - $(LN_S) ../systemd-user-sessions.service systemd-user-sessions.service && \ - $(LN_S) ../systemd-ask-password-wall.path systemd-ask-password-wall.path && \ - $(LN_S) ../systemd-logind.service systemd-logind.service ) + $(LN_S) ../systemd-ask-password-wall.path systemd-ask-password-wall.path) ( cd $(DESTDIR)$(pkgsysconfdir)/system/getty.target.wants && \ rm -f getty@tty1.service && \ $(LN_S) $(systemunitdir)/getty@.service getty@tty1.service ) @@ -1722,37 +2370,26 @@ endif rm -f remote-fs.target && \ $(LN_S) $(systemunitdir)/remote-fs.target remote-fs.target ) ( cd $(DESTDIR)$(systemunitdir)/sysinit.target.wants && \ - rm -f dev-hugepages.automount \ - dev-mqueue.automount \ - sys-kernel-debug.automount \ - sys-kernel-security.automount \ - systemd-vconsole-setup.service \ + rm -f dev-hugepages.mount \ + dev-mqueue.mount \ + sys-kernel-config.mount \ + sys-kernel-debug.mount \ + sys-kernel-security.mount \ + sys-fs-fuse-connections.mount \ systemd-modules-load.service \ - systemd-random-seed-load.service \ systemd-tmpfiles-setup.service \ systemd-sysctl.service \ - systemd-ask-password-console.path \ - systemd-kmsg-syslogd.service \ - cryptsetup.target && \ - $(LN_S) ../dev-hugepages.automount dev-hugepages.automount && \ - $(LN_S) ../dev-mqueue.automount dev-mqueue.automount && \ - $(LN_S) ../sys-kernel-debug.automount sys-kernel-debug.automount && \ - $(LN_S) ../sys-kernel-security.automount sys-kernel-security.automount && \ - $(LN_S) ../systemd-vconsole-setup.service systemd-vconsole-setup.service && \ + systemd-ask-password-console.path && \ + $(LN_S) ../dev-hugepages.mount dev-hugepages.mount && \ + $(LN_S) ../dev-mqueue.mount dev-mqueue.mount && \ + $(LN_S) ../sys-kernel-config.mount sys-kernel-config.mount && \ + $(LN_S) ../sys-kernel-debug.mount sys-kernel-debug.mount && \ + $(LN_S) ../sys-kernel-security.mount sys-kernel-security.mount && \ + $(LN_S) ../sys-fs-fuse-connections.mount sys-fs-fuse-connections.mount && \ $(LN_S) ../systemd-modules-load.service systemd-modules-load.service && \ - $(LN_S) ../systemd-random-seed-load.service systemd-random-seed-load.service && \ $(LN_S) ../systemd-tmpfiles-setup.service systemd-tmpfiles-setup.service && \ $(LN_S) ../systemd-sysctl.service systemd-sysctl.service && \ - $(LN_S) ../systemd-ask-password-console.path systemd-ask-password-console.path && \ - $(LN_S) ../systemd-kmsg-syslogd.service && \ - $(LN_S) ../cryptsetup.target cryptsetup.target ) -if ENABLE_BINFMT - ( cd $(DESTDIR)$(systemunitdir)/sysinit.target.wants && \ - rm -f systemd-binfmt.service \ - proc-sys-fs-binfmt_misc.automount && \ - $(LN_S) ../systemd-binfmt.service systemd-binfmt.service && \ - $(LN_S) ../proc-sys-fs-binfmt_misc.automount proc-sys-fs-binfmt_misc.automount ) -endif + $(LN_S) ../systemd-ask-password-console.path systemd-ask-password-console.path ) ( cd $(DESTDIR)$(systemunitdir)/basic.target.wants && \ rm -f systemd-tmpfiles-clean.timer && \ $(LN_S) ../systemd-tmpfiles-clean.timer systemd-tmpfiles-clean.timer ) @@ -1804,9 +2441,6 @@ endif if TARGET_FEDORA $(MKDIR_P) -m 0755 $(DESTDIR)$(systemunitdir)/final.target.wants - ( cd $(DESTDIR)$(systemunitdir)/multi-user.target.wants && \ - rm -f rc-local.service && \ - $(LN_S) $(systemunitdir)/rc-local.service rc-local.service ) ( cd $(DESTDIR)$(systemunitdir)/final.target.wants && \ rm -f halt-local.service && \ $(LN_S) $(systemunitdir)/halt-local.service halt-local.service ) @@ -1821,9 +2455,6 @@ endif if TARGET_MANDRIVA $(MKDIR_P) -m 0755 $(DESTDIR)$(systemunitdir)/final.target.wants - ( cd $(DESTDIR)$(systemunitdir)/multi-user.target.wants && \ - rm -f rc-local.service && \ - $(LN_S) $(systemunitdir)/rc-local.service rc-local.service ) ( cd $(DESTDIR)$(systemunitdir)/final.target.wants && \ rm -f halt-local.service && \ $(LN_S) $(systemunitdir)/halt-local.service halt-local.service ) @@ -1845,9 +2476,6 @@ endif if TARGET_SUSE $(MKDIR_P) -m 0755 $(DESTDIR)$(systemunitdir)/final.target.wants - ( cd $(DESTDIR)$(systemunitdir)/multi-user.target.wants && \ - rm -f rc-local.service && \ - $(LN_S) $(systemunitdir)/rc-local.service rc-local.service ) ( cd $(DESTDIR)$(systemunitdir) && \ rm -f local.service && \ $(LN_S) rc-local.service local.service ) @@ -1856,15 +2484,31 @@ if TARGET_SUSE $(LN_S) $(systemunitdir)/halt-local.service halt-local.service ) endif +if TARGET_MAGEIA + $(MKDIR_P) -m 0755 $(DESTDIR)$(systemunitdir)/final.target.wants + ( cd $(DESTDIR)$(systemunitdir)/final.target.wants && \ + rm -f halt-local.service && \ + $(LN_S) $(systemunitdir)/halt-local.service halt-local.service ) + ( cd $(DESTDIR)$(systemunitdir) && \ + rm -f display-manager.service && \ + $(LN_S) prefdm.service display-manager.service && \ + $(LN_S) prefdm.service dm.service ) + ( cd $(DESTDIR)$(systemunitdir)/graphical.target.wants && \ + rm -f display-manager.service && \ + $(LN_S) $(systemunitdir)/display-manager.service display-manager.service ) +endif + if HAVE_SYSV_COMPAT ( cd $(DESTDIR)$(systemunitdir)/local-fs.target.wants && \ rm -f var-lock.mount && \ $(LN_S) ../var-lock.mount var-lock.mount ) endif -install-exec-hook: libsystemd-daemon-install-hook libsystemd-login-install-hook +install-exec-hook: $(INSTALL_EXEC_HOOKS) + +uninstall-hook: $(UNINSTALL_EXEC_HOOKS) -uninstall-hook: libsystemd-daemon-uninstall-hook libsystemd-login-uninstall-hook +install-data-hook: systemd-install-data-hook $(INSTALL_DATA_HOOKS) DISTCHECK_CONFIGURE_FLAGS = \ --with-dbuspolicydir=$$dc_install_base/$(dbuspolicydir) \ @@ -1873,11 +2517,13 @@ DISTCHECK_CONFIGURE_FLAGS = \ --with-dbusinterfacedir=$$dc_install_base/$(dbusinterfacedir) \ --with-udevrulesdir=$$dc_install_base/$(udevrulesdir) \ --with-pamlibdir=$$dc_install_base/$(pamlibdir) \ - --with-rootdir=$$dc_install_base/$(rootdir) + --with-rootprefix=$$dc_install_base/$(prefix) + upload: all distcheck - cp -v systemd-$(VERSION).tar.bz2 /home/lennart/git.fedora/systemd/ - scp systemd-$(VERSION).tar.bz2 fdo:/srv/www.freedesktop.org/www/software/systemd/ + cp -v systemd-$(VERSION).tar.xz /home/lennart/git.fedora/systemd/ + scp systemd-$(VERSION).tar.xz fdo:/srv/www.freedesktop.org/www/software/systemd/ + scp man/*.html fdo:/srv/www.freedesktop.org/www/software/systemd/man/ scp man/*.html tango:public/systemd-man/ git-tag: @@ -0,0 +1,225 @@ +systemd System and Service Manager + +CHANGES WITH 43: + * This is mostly a bugfix release + + * systems lacking /etc/os-release are no longer supported. + + * Various functionality updates to libsystemd-login.so + + * Track class of PAM logins to distuingish greeters from + normal user logins. + + Contributions from: Kay Sievers, Lennart Poettering, Michael + Biebl + +CHANGES WITH 42: + * This is an important bugfix release for v41. + + * Building man pages is now optional which should be useful + for those building systemd from git but unwilling to install + xsltproc. + + * Watchdog support for supervising services is now usable. In + a future release support for hardware watchdogs + (i.e. /dev/watchdog) will be added building on this. + + * Service start rate limiting is now configurable and can be + turned off per service. When a start rate limit is hit a + reboot can automatically be triggered. + + * New CanReboot(), CanPowerOff() bus calls in systemd-logind. + + Contributions from: Benjamin Franzke, Bill Nottingham, + Frederic Crozat, Lennart Poettering, Michael Olbrich, Michal + Schmidt, Michał Górny, Piotr Drąg + +CHANGES WITH 41: + * The systemd binary is installed /usr/lib/systemd/systemd now; + An existing /sbin/init symlink needs to be adapted with the + package update. + + * The code that loads kernel modules has been ported to invoke + libkmod directly, instead of modprobe. This means we do not + support systems with module-init-tools anymore. + + * Watchdog support is now already useful, but still not + complete. + + * A new kernel command line option systemd.setenv= is + understood to set system wide environment variables + dynamically at boot. + + * We now limit the set of capabilities of systemd-journald. + + * We now set SIGPIPE to ignore by default, since it only is + useful in shell pipelines, and has little use in general + code. This can be disabled with IgnoreSIPIPE=no in unit + files. + + Contributions from: Benjamin Franzke, Kay Sievers, Lennart + Poettering, Michael Olbrich, Michal Schmidt, Tom Gundersen, + William Douglas + +CHANGES WITH 40: + * This is mostly a bugfix release + + * We now expose the reason why a service failed in the + "Result" D-Bus property. + + * Rudimentary service watchdog support (will be completed over + the next few releases.) + + * When systemd forks off in order execute some service we will + now immediately changes its argv[0] to reflect which process + it will execute. This is useful to minimize the time window + with a generic argv[0], which makes bootcharts more useful + + Contributions from: Alvaro Soliverez, Chris Paulson-Ellis, Kay + Sievers, Lennart Poettering, Michael Olbrich, Michal Schmidt, + Mike Kazantsev, Ray Strode + +CHANGES WITH 39: + * This is mostly a test release, but incorporates many + bugfixes. + + * New systemd-cgtop tool to show control groups by their + resource usage. + + * Linking against libacl for ACLs is optional again. If + disabled, support tracking device access for active logins + goes becomes unavailable, and so does access to the user + journals by the respective users. + + * If a group "adm" exists, journal files are automatically + owned by them, thus allow members of this group full access + to the system journal as well as all user journals. + + * The journal now stores the SELinux context of the logging + client for all entries. + + * Add C++ inclusion guards to all public headers + + * New output mode "cat" in the journal to print only text + messages, without any meta data like date or time. + + * Include tiny X server wrapper as a temporary stop-gap to + teach XOrg udev display enumeration. This is used by display + managers such as gdm, and will go away as soon as XOrg + learned native udev hotplugging for display devices. + + * Add new systemd-cat tool for executing arbitrary programs + with STDERR/STDOUT connected to the journal. Can also act as + BSD logger replacement, and does so by default. + + * Optionally store all locally generated coredumps in the + journal along with meta data. + + * systemd-tmpfiles learnt four new commands: n, L, c, b, for + writing short strings to files (for usage for /sys), and for + creating symlinks, character and block device nodes. + + * New unit file option ControlGroupPersistent= to make cgroups + persistent, following the mechanisms outlined in + http://www.freedesktop.org/wiki/Software/systemd/PaxControlGroups + + * Support multiple local RTCs in a sane way + + * No longer monopolize IO when replaying readahead data on + rotating disks, since we might starve non-file-system IO to + death, since fanotify() will not see accesses done by blkid, + or fsck. + + * Don't show kernel threads in systemd-cgls anymore, unless + requested with new -k switch. + + Contributions from: Dan Horák, Kay Sievers, Lennart + Poettering, Michal Schmidt + +CHANGES WITH 38: + * This is mostly a test release, but incorporates many + bugfixes. + + * The git repository moved to: + git://anongit.freedesktop.org/systemd/systemd + ssh://git.freedesktop.org/git/systemd/systemd + + * First release with the journal + http://0pointer.de/blog/projects/the-journal.html + + * The journal replaces both systemd-kmsg-syslogd and + systemd-stdout-bridge. + + * New sd_pid_get_unit() API call in libsystemd-logind + + * Many systemadm clean-ups + + * Introduce remote-fs-pre.target which is ordered before all + remote mounts and may be used to start services before all + remote mounts. + + * Added Mageia support + + * Add bash completion for systemd-loginctl + + * Actively monitor PID file creation for daemons which exit in + the parent process before having finished writing the PID + file in the daemon process. Daemons which do this need to be + fixed (i.e. PID file creation must have finished before the + parent exits), but we now react a bit more gracefully to them. + + * Add colourful boot output, mimicking the well-known output + of existing distributions. + + * New option PassCredentials= for socket units, for + compatibility with a recent kernel ABI breakage. + + * /etc/rc.local is now hooked in via a generator binary, and + thus will no longer act as synchronization point during + boot. + + * systemctl list-unit-files now supports --root=. + + * systemd-tmpfiles now understands two new commands: z, Z for + relabelling files according to the SELinux database. This is + useful to apply SELinux labels to specific files in /sys, + among other things. + + * Output of SysV services is now forwarded to both the console + and the journal by default, not only just the console. + + * New man pages for all APIs from libsystemd-login. + + * The build tree got reorganized and a the build system is a + lot more modular allowing embedded setups to specifically + select the components of systemd they are interested in. + + * Support for Linux systems lacking the kernel VT subsystem is + restored. + + * configure's --with-rootdir= got renamed to + --with-rootprefix= to follow the naming used by udev and + kmod + + * Unless specified otherwise we'll now install to /usr instead + of /usr/local by default. + + * Processes with '@' in argv[0][0] are now excluded from the + final shut-down killing spree, following the logic explained + in: + http://www.freedesktop.org/wiki/Software/systemd/RootStorageDaemons + + * All processes remaining in a service cgroup when we enter + the START or START_PRE states are now killed with + SIGKILL. That means it is no longer possible to spawn + background processes from ExecStart= lines (which was never + supported anyway, and bad style). + + * New PropagateReloadTo=/PropagateReloadFrom= options to bind + reloading of units together. + + Contributions from: Bill Nottingham, Daniel Walsh, Dave + Reisner, Dexter Morgan, Gregs Gregs, Jonathan Nieder, Kay + Sievers, Lennart Poettering, Michael Biebl, Michal Schmidt, + Michał Górny, Ran Benita, Thomas Jarosch, Tim Waugh, Tollef + Fog Heen, Tom Gundersen, Zbigniew Jędrzejewski-Szmek @@ -1,4 +1,4 @@ -systemd System and Session Manager +systemd System and Service Manager DETAILS: http://0pointer.de/blog/projects/systemd.html @@ -7,11 +7,11 @@ WEB SITE: http://www.freedesktop.org/wiki/Software/systemd GIT: - git://anongit.freedesktop.org/systemd - ssh://git.freedesktop.org/git/systemd + git://anongit.freedesktop.org/systemd/systemd + ssh://git.freedesktop.org/git/systemd/systemd GITWEB: - http://cgit.freedesktop.org/systemd/ + http://cgit.freedesktop.org/systemd/systemd MAILING LIST: http://lists.freedesktop.org/mailman/listinfo/systemd-devel @@ -27,10 +27,14 @@ AUTHOR: Lennart Poettering with major support from Kay Sievers LICENSE: - GPLv2+ for all code, except sd-daemon.[ch] which is MIT + GPLv2+ for all code, except sd-daemon.[ch] and + sd-readahead.[ch] which are MIT REQUIREMENTS: - Linux kernel >= 2.6.30 (with devtmpfs, cgroups; optional but strongly recommended: autofs4, ipv6) + Linux kernel >= 2.6.39 + with devtmpfs + with cgroups (but it's OK to disable all controllers) + optional but strongly recommended: autofs4, ipv6 libudev >= 172 dbus >= 1.4.0 libcap @@ -50,6 +54,7 @@ REQUIREMENTS: automake autoconf libtool + gperf make, gcc, and similar tools During runtime you need the following dependencies: @@ -67,6 +72,11 @@ REQUIREMENTS: add a dependency on nss-myhostname to the package that includes systemd-hostnamed. + Note that D-Bus can link against libsystemd-login.so, which + results in a cyclic build dependency. To accomodate for this + please build D-Bus without systemd first, then build systemd, + then rebuild D-Bus with systemd support. + WARNINGS: systemd will warn you during boot if /etc/mtab is not a symlink to /proc/mounts. Please ensure that /etc/mtab is a @@ -1,67 +1,165 @@ -* udev-kernel.socket + udev.control.socket seems not - to work, udevd is started but no fd is passed - -F15: +Bugfixes: * swap units that are activated by one name but shown in the kernel under another are semi-broken -F15 External: - -* NFS, networkmanager ordering issue (PENDING) - * NM should pull in network.target (PENDING) https://bugzilla.redhat.com/show_bug.cgi?id=692008 -* bluetooth should be possible to disable (PENDING) - * make anaconda write timeout=0 for encrypted devices -* fix broken Sockets=syslog-ng.socket packaging +* service: pid file reading after reload doesn't work, since we don't reset the pid variable + +* make sure timeouts are applied to Type=oneshot services. + +* Dangling symlinks of .automount unit files in .wants/ directories, set up + automount points even when the original .automount file did not exist + anymore. Only the .mount unit was still around. + +* make polkit checks async + +* properly handle .mount unit state tracking when two mount points are stacked one on top of another on the exact same mount point. Features: -* add profiling to unit loading code +* systemctl reboot -ff should trigger an immediate reboot -* add gperf support for unit file parsing table +* support units generated by a generator and placed in /run/systemd/system/; the directory is + currently ignored because it is empty before the generatores are executed -* fix CUPS .path unit for globbing +* let 'systemctl reboot' called as non-root talk to logind instead of systemd, to get polkit + system policy in the loop of privilege checking, so normal users can possibly use /sbin/reboot -* move PAM code into its own binary +* Possibly, detect whether SysV init scripts can do reloading by looking for "echo Usage:" lines -* logind: ensure ACLs are updated on login and logout +* figure out whether we should leave dbus around during shutdown -* warn if the user stops a service but not its associated socket +* support closing all fds via RLIMIT_NOFILE instead of /proc, in order to make chroot stuff work. -* ensure we always set the facility when logging to kmsg +* add interface to allow immediate rotation of the journal, and even flushing. -* service: pid file reading after reload doesn't work, since we don't reset the pid variable +* don't log coredumps of PID 1 into the journal -* logind: spawn user@..service on login +* if a journal file is corrupt, rotate it and create a new one -* logind: non-local X11 server handling +* dbus: in fedora, make the machine a symlink to /etc/machine-id -* logind: use sysfs path in device hash table instead of sysname, as soon as fb driver is fixed +* journald: reuse XZ context -* implement Register= switch in .socket units to enable registration - in Avahi, RPC and other socket registration services. +* logind: add equivalent to sd_pid_get_owner_uid() to the D-Bus API -* make sure people don't leave processes around after ExecStartPre= +* write RPM spec macros for presets -* make sure systemd-ask-password-wall does not shutdown systemd-ask-password-console too early +* write man pages for systemd-cat -* kernel: add /proc/sys file exposing CAP_LAST_CAP? +* journal: write man pages for API -* kernel: add device_type = "fb", "fbcon" to class "graphics" +* journal: OR matches are borked + +* journal: extend hash tables as we go + +* journal: API for looking for retrieving "all values of this field" + +* journal: deal nicely with byte-by-byte copied files, especially regards header + +* journal: local deserializer of export mode, http server + +* journal: message catalog + +* journal: forward-secure signatures + +* document the exit codes when services fail before they are exec()ed + +* rework namespace support, don't use pivot_root, and mount things after creating the namespace, not before + +* systemctl journal command + +* journalctl: --cursor support, priority filtering + +* systemctl status: show coredumps + +* systemctl status: show whether journal was rotated since service started + +* save coredump in Windows/Mozilla minidump format + +* support crash reporting operation modes (https://live.gnome.org/GnomeOS/Design/Whiteboards/ProblemReporting) + +* allow per-entry control on /var vs. /run (think incognito browser mode) + +* clean up session cgroups that remain after logout (think sshd), but eventually run empty + +* support "systemctl stop foobar@.service" to stop all units matching a certain template + +* move to LGPL2+ + +* logind: allow showing logout dialog from system + +* document that %% can be used to write % in a string that is specifier extended + +* check utf8 everywhere + +* when an instanced service exits, remove its parent cgroup too if possible. + +* Make libselinux, libattr, libcap, libdl dependencies only of the tools which actually need them. + +* as Tom Gundersen pointed out there's a always a dep loop if people use crypto file systems with random keys + +* unset container= in PID1? + +* automatically escape unit names passed on the service (i.e. think "systemctl start serial-getty.service@serial/by-path/jshdfjsdfhkjh" being automatically escaped as necessary. + +* if we can not get user quota for tmpfs, mount a separate tmpfs instance + for every user in /run/user/$USER with a configured maximum size + +* default to actual 32bit PIDs, via /proc/sys/kernel/pid_max + +* add an option to make mounts private/shareable and so on, enable this for root by default + +* internal restart counter for units (focus on auto-respawn) + +* finer-grained auto-respawn settings (rate-limit) + +* be able to specify a forced restart of service A where service B depends on, in case B + needs to be auto-respawned? + +* Something is wrong with symlink handling of "autovt@.service" in "systemctl list-unit-files" + +* when a bus name of a service disappears from the bus make sure to queue further activation requests + +* something like ConditionExec= or ExecStartPre= without failure state + +* service restart retry configuration + +* tmpfiles: apply "x" on "D" too (see patch from William Douglas) + +* don't set $HOME in services unless requested + +* hide PAM/TCPWrap options in fragment parser when compile time disabled + +* when we automatically restart a service, ensure we retsart its rdeps, too. -* understand https://bugzilla.redhat.com/show_bug.cgi?id=672194 +* allow Type=simple with PIDFile= + https://bugzilla.redhat.com/show_bug.cgi?id=723942 + +* move PAM code into its own binary + +* warn if the user stops a service but not its associated socket + +* logind: spawn user@..service on login + +* logind: non-local X11 server handling + +* implement Register= switch in .socket units to enable registration + in Avahi, RPC and other socket registration services. + +* make sure systemd-ask-password-wall does not shutdown systemd-ask-password-console too early * readahead: use BTRFS_IOC_DEFRAG_RANGE instead of BTRFS_IOC_DEFRAG ioctl, with START_IO * readahead: check whether a btrfs volume includes ssd by checking mount flag "ssd" -* support sd_notify() style notification when reload is finished (RELOADED=1) +* support sd_notify() style notification when reload begins (RELOADING=1), reload is finished (READY=1), and add ReloadSignal= then to use in combination -* support sf_notify() style notification when shutting down, to make auto-exit bus services work +* support sd_notify() style notification when shutting down, to make auto-exit bus services work (STOPPING=1) * verify that the AF_UNIX sockets of a service in the fs still exist when we start a service in order to avoid confusion when a user @@ -72,34 +170,16 @@ Features: * move nss-myhostname into systemd -* figure out a standard place to configure timezone name, inform myllynen@redhat.com - -* add dbus call to convert snapshot into target, and a dbus call to generate target from current state - -* detect LXC with $container=lxc +* and a dbus call to generate target from current state * drop /.readahead on bigger upgrades with yum -* add inode stat() check to readahead to suppress preloading changed files - -* allow list of paths in config_parse_condition_path() - -* show enablement status in systemctl status +* add inode nr check to readahead to suppress preloading changed files * add support for /bin/mount -s * GC unreferenced jobs (such as .device jobs) -* add JoinControllers= to system.conf to mount certain cgroup - controllers together in order to guarantee atomic creation/addition - of cgroups - -* avoid DefaultStandardOutput=syslog to have any effect on StandardInput=socket services - -* cgroup_notify_empty(): recursively check groups up the tree, too - -* fix alsa mixer restore to not print error when no config is stored - * when failing to start a service due to ratelimiting, try again later, if restart=always is set * write blog stories about: @@ -107,105 +187,48 @@ Features: - status update - how to make changes to sysctl and sysfs attributes - remote access - - cgroup best pratices to avoid stepping on each others toes - how to pass throw-away units to systemd, or dynamically change properties of existing units - how to integrate cgconfig and suchlike with systemd + - resource control in systemd * allow port=0 in .socket units -* rename systemd-logger to systemd-stdio-syslog-bridge - -* take BSD file lock on tty devices when using them? - -* avoid any flag files, or readahead files in /, we need to support r/o / - or / on tmpfs like Android setups. - * move readahead files into /var, look for them with .path units * teach dbus to activate all services it finds in /etc/systemd/services/org-*.service -* get process transport into dbus for systemctl -P/-H - -* document default dependencies - -* support systemd.whitelist=/systemd.blacklist= on the kernel command - line. - -* Find a way to replace /var/run, /var/lock directories with - symlinks during an RPM package upgrade (filesystem.rpm or systemd.rpm). - (lua code to create symlinks right away for new installations is in filesytem.rpm now) - We soon want to get rid of var-run.mount var-lock.mount units: - if mountpoint /run ; then - umount /var/run || : - else - mount --move /var/run /run || mount --bind /var/run /run - fi - mv /var/run /var/.run.save - ln -s /run /var/run - echo "R /var/.run.save" > /etc/tmpfiles.d/remove-run-save.conf +* support systemd.mask= on the kernel command line. * when key file cannot be found, read it from kbd in cryptsetup -* add switch to systemctl to show enabled but not running services. Or - another switch that shows service that have been running since - booting but aren't running anymore. - * reuse mkdtemp namespace dirs in /tmp? * recreate systemd's D-Bus private socket file on SIGUSR2 -* be more specific what failed: - ... - Unmounting file systems. - Not all file systems unmounted, 1 left. - Disabling swaps. - Detaching loop devices. - Detaching DM devices. - Cannot finalize remaining file systems and devices, trying to kill remaining processes. - Unmounting file systems. - Not all file systems unmounted, 1 left. - Cannot finalize remaining file systems and devices, giving up. - ... - -* check for compiled-in, but not active selinux, and don't print any warnings - about policy loading. Probably check for available selinux in /proc/filesystems, - and check for active selinux with getcon_raw() == "kernel" - * Support --test based on current system state -* show failure error string in "systemctl status" - -* make sure timeouts are applied to Type=oneshot services. - * investigate whether the gnome pty helper should be moved into systemd, to provide cgroup support. -* need a way to apply mount options of api vfs from systemd unit files - (or some other modern source?) instead of fstab? - * maybe introduce ExecRestartPre= -* figure out what happened to bluez patch - -* Patch systemd-fsck to use -C and pass console fd to it - * configurable jitter for timer events -* Support ProcessNeededForShutdown=true to allow stuff like mdmon to - be killed very late after the rootfs is read only? If implement pass - this to shutdown binary via command line argument. +* timer events with system resume + +* timer events on calendar time * dot output for --test showing the 'initial transaction' * calendar time support in timer, iCalendar semantics for the timer stuff (RFC2445) http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commit;h=99ee5315dac6211e972fa3f23bcc9a0343ff58c4 -* systemd --user - - get PR_SET_ANCHOR merged: http://lkml.org/lkml/2010/2/2/165 - * implicitly import "defaults" settings file into all types +* exec settings override +* writable cgroups dbus properties for live changes + +* read config fragments for all units from /lib/systemd/system/foobar.service.d/ to override/extend specific settings * port over to LISTEN_FDS/LISTEN_PID: - - uuidd HAVEPATCH - rpcbind (/var/run/rpcbind.sock!) HAVEPATCH - cups HAVEPATCH - postfix, saslauthd @@ -214,9 +237,9 @@ Features: - bluetoothd (/var/run/sdp! @/org/bluez/audio!) - distccd -* fingerprint.target, wireless.target, gps.target, netdevice.target +* auditd service files -* set_put(), hashmap_put() return values check. i.e. == 0 doesn't free()! +* fingerprint.target, wireless.target, gps.target, netdevice.target * io priority during initialization @@ -224,20 +247,27 @@ Features: * systemctl list-jobs - show dependencies -* auditd service files - * add systemctl switch to dump transaction without executing it * suspend, resume support? -* readahead: btrfs/LVM SSD detection - -* allow runtime changing of log level and target - * drop cap bounding set in readahead and other services External: +* dbus: + - get process transport into dbus for systemctl -P/-H (PENDING) + - dbus --user + - natively watch for dbus-*.service symlinks (PENDING) + - allow specification of socket mode/umask when allocating DBusServer + - allow disabling of fd passing when connecting a AF_UNIX connection + - allow disabling of UID passing for AUTH EXTERNAL + +* systemd --user + PR_SET_CHILD_REAPER patch: https://lkml.org/lkml/2011/7/28/426 + +* fix alsa mixer restore to not print error when no config is stored + * 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 @@ -248,30 +278,19 @@ External: * patch kernel for xattr support in /dev, /proc/, /sys and /sys/fs/cgroup? +* NTP: the kernel's 11-minutes-mode syncs the system time to the RTC, but only + in an ~30 minutes window. It does not adjust larger differences. Find a way + to tell the kernel, to always do a full time sync when the RTC is in UTC and + we are in 11-minutes-mode. When we trust the system time to NTP we also want + the RTC to sync up. + * patch kernel for cpu feature modalias for autoloading aes/kvm/... - http://git.kernel.org/?p=linux/kernel/git/ak/linux-misc-2.6.git;a=shortlog;h=refs/heads/cpuid-match - (Rafael J. Wysocki's sysdev rework is on the way. After that CPUs can be exported a proper bus.) - -* procps, psmisc, sysvinit-tools, hostname → util-linux-ng - -https://bugzilla.redhat.com/show_bug.cgi?id=614245 -- plymouth -https://bugzilla.redhat.com/show_bug.cgi?id=612789 -- umount /cgroup on halt -https://bugzilla.redhat.com/show_bug.cgi?id=612728 -- /etc/rc.d/init.d/functions -https://bugzilla.redhat.com/show_bug.cgi?id=612712 -- pam_systemd -https://bugs.freedesktop.org/show_bug.cgi?id=29193 -- accountsservice -https://bugs.freedesktop.org/show_bug.cgi?id=29194 -- ConsoleKit -https://bugs.freedesktop.org/show_bug.cgi?id=29205 -- udisks -http://article.gmane.org/gmane.linux.bluez.kernel/6479 -- bluez -http://www.spinics.net/lists/linux-nfs/msg14371.html -- rpcbind -https://bugzilla.redhat.com/show_bug.cgi?id=617328 -- ntp -https://bugzilla.redhat.com/show_bug.cgi?id=617320 -- at -https://bugzilla.redhat.com/show_bug.cgi?id=617326 -- fprintd -https://bugzilla.redhat.com/show_bug.cgi?id=617333 -- yum -https://bugzilla.redhat.com/show_bug.cgi?id=617317 -- acpid -https://bugzilla.redhat.com/show_bug.cgi?id=617327 -- gpm -https://bugzilla.redhat.com/show_bug.cgi?id=617330 -- pcsc-lite -https://bugzilla.redhat.com/show_bug.cgi?id=617321 -- audit -https://bugzilla.redhat.com/show_bug.cgi?id=617316 -- abrt + (patches in linux-next, on the way to the next kernel) + +* kernel: add /proc/sys file exposing CAP_LAST_CAP? sysconf? + merged: http://git.kernel.org/?p=linux/kernel/git/torvalds/linux.git;a=commit;h=73efc0394e148d0e15583e13712637831f926720 + +* kernel: add device_type = "fb", "fbcon" to class "graphics" Regularly: @@ -285,4 +304,4 @@ Regularly: * pahole -* CFLAGS="-Wl,--gc-sections -Wl,--print-gc-sections -ffunction-sections -fdata-sections" +* set_put(), hashmap_put() return values check. i.e. == 0 doesn't free()! diff --git a/autogen.sh b/autogen.sh index b2b680a85c..9ca53772a4 100755 --- a/autogen.sh +++ b/autogen.sh @@ -2,8 +2,6 @@ # 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 @@ -17,67 +15,42 @@ # You should have received a copy of the GNU General Public License # along with systemd; If not, see <http://www.gnu.org/licenses/>. -AM_VERSION=1.11 -AC_VERSION=2.63 - -run_versioned() { - local P - local V - - V=$(echo "$2" | sed -e 's,\.,,g') - - if [ -e "`which $1$V 2> /dev/null`" ] ; then - P="$1$V" - else - if [ -e "`which $1-$2 2> /dev/null`" ] ; then - P="$1-$2" - else - P="$1" - fi - fi - - shift 2 - "$P" "$@" -} - -set -ex - if [ -f .git/hooks/pre-commit.sample -a ! -f .git/hooks/pre-commit ] ; then cp -p .git/hooks/pre-commit.sample .git/hooks/pre-commit && \ chmod +x .git/hooks/pre-commit && \ echo "Activated pre-commit hook." fi -if type -p colorgcc > /dev/null ; then - export CC=colorgcc -fi +intltoolize --force --automake +autoreconf --force --install --symlink libdir() { echo $(cd $1/$(gcc -print-multi-os-directory); pwd) } -if [ "x$1" = "xam" ] ; then - run_versioned automake "$AM_VERSION" -a -c --foreign - ./config.status -else - rm -rf autom4te.cache - rm -f config.cache - - libtoolize -c --force - intltoolize -c -f - run_versioned aclocal "$AM_VERSION" -I m4 - run_versioned autoconf "$AC_VERSION" -Wall - run_versioned autoheader "$AC_VERSION" - run_versioned automake "$AM_VERSION" --copy --foreign --add-missing +args="\ +--sysconfdir=/etc \ +--localstatedir=/var \ +--libdir=$(libdir /usr/lib) \ +--libexecdir=/usr/lib" + +if [ ! -L /bin ]; then +args="$args \ +--with-rootprefix= \ +--with-rootlibdir=$(libdir /lib) \ +" +fi - if [ "x$1" != "xac" ]; then - CFLAGS="$CFLAGS -g -O0" ./configure \ - --sysconfdir=/etc \ - --localstatedir=/var \ - --libexecdir=/usr/lib \ - --libdir=$(libdir /usr/local/lib) \ - --with-rootdir= \ - "$@" - make clean - fi +if [ "x$1" != "xc" ]; then + echo + echo "----------------------------------------------------------------" + echo "Initialized build system. For a common configuration please run:" + echo "----------------------------------------------------------------" + echo + echo "./configure CFLAGS='-g -O0' $args" + echo +else + echo ./configure CFLAGS='-g -O0' $args + ./configure CFLAGS='-g -O0' $args + make clean fi diff --git a/configure.ac b/configure.ac index 82ed85c3f8..62e8cdf582 100644 --- a/configure.ac +++ b/configure.ac @@ -17,14 +17,14 @@ AC_PREREQ(2.63) -AC_INIT([systemd],[31],[systemd-devel@lists.freedesktop.org]) +AC_INIT([systemd],[43],[systemd-devel@lists.freedesktop.org]) AC_CONFIG_SRCDIR([src/main.c]) AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_HEADERS([config.h]) AC_USE_SYSTEM_EXTENSIONS AC_SYS_LARGEFILE - -AM_INIT_AUTOMAKE([foreign 1.11 -Wall -Wno-portability silent-rules tar-pax subdir-objects dist-bzip2]) +AC_PREFIX_DEFAULT([/usr]) +AM_INIT_AUTOMAKE([foreign 1.11 -Wall -Wno-portability silent-rules tar-pax no-dist-gzip dist-xz subdir-objects check-news]) AC_SUBST(PACKAGE_URL, [http://www.freedesktop.org/wiki/Software/systemd]) @@ -36,13 +36,6 @@ AS_IF([test "x$host_cpu" = "xmips" || test "x$host_cpu" = "xmipsel" || AM_SILENT_RULES([yes]) -AC_CHECK_PROG([STOW], [stow], [yes], [no]) - -AS_IF([test "x$STOW" = "xyes" && test -d /usr/local/stow], [ - AC_MSG_NOTICE([*** Found /usr/local/stow: default install prefix set to /usr/local/stow/${PACKAGE_NAME}-${PACKAGE_VERSION} ***]) - ac_default_prefix="/usr/local/stow/${PACKAGE_NAME}-${PACKAGE_VERSION}" -]) - # i18n stuff for the PolicyKit policy files IT_PROG_INTLTOOL([0.40.0]) @@ -52,6 +45,8 @@ AC_SUBST(GETTEXT_PACKAGE) AC_PROG_MKDIR_P AC_PROG_LN_S AC_PROG_SED +AC_PROG_GREP +AC_PROG_AWK AC_PROG_CC AC_PROG_CC_C99 @@ -60,6 +55,10 @@ AC_PROG_GCC_TRADITIONAL AC_CHECK_TOOL(OBJCOPY, objcopy) AC_CHECK_TOOL(STRINGS, strings) +AC_CHECK_TOOL(GPERF, gperf) +if test -z "$GPERF" ; then + AC_MSG_ERROR([*** gperf not found]) +fi CC_CHECK_CFLAGS_APPEND([ \ -pipe \ @@ -95,6 +94,7 @@ CC_CHECK_CFLAGS_APPEND([ \ -Wno-unused-parameter \ -Wno-missing-field-initializers \ -Wno-unused-result \ + -Werror=overflow \ -Wp,-D_FORTIFY_SOURCE=2 \ -ffast-math \ -fno-common \ @@ -111,33 +111,44 @@ LT_INIT AC_SEARCH_LIBS([clock_gettime], [rt], [], [AC_MSG_ERROR([*** POSIX RT library not found])]) AC_SEARCH_LIBS([dlsym], [dl], [], [AC_MSG_ERROR([*** Dynamic linking loader library not found])]) + +save_LIBS="$LIBS" +LIBS= AC_SEARCH_LIBS([cap_init], [cap], [], [AC_MSG_ERROR([*** POSIX caps library not found])]) AC_CHECK_HEADERS([sys/capability.h], [], [AC_MSG_ERROR([*** POSIX caps headers not found])]) +CAP_LIBS="$LIBS" +LIBS="$save_LIBS" +AC_SUBST(CAP_LIBS) # This makes sure pkg.m4 is available. m4_pattern_forbid([^_?PKG_[A-Z_]+$],[*** pkg.m4 missing, please install pkg-config]) PKG_CHECK_MODULES(UDEV, [ libudev >= 172 ]) -AC_SUBST(UDEV_CFLAGS) -AC_SUBST(UDEV_LIBS) - PKG_CHECK_MODULES(DBUS, [ dbus-1 >= 1.3.2 ]) -AC_SUBST(DBUS_CFLAGS) -AC_SUBST(DBUS_LIBS) +PKG_CHECK_MODULES(KMOD, [ libkmod >= 5 ]) have_selinux=no AC_ARG_ENABLE(selinux, AS_HELP_STRING([--disable-selinux], [Disable optional SELINUX support])) if test "x$enable_selinux" != "xno"; then PKG_CHECK_MODULES(SELINUX, [ libselinux ], [AC_DEFINE(HAVE_SELINUX, 1, [Define if SELinux is available]) have_selinux=yes], have_selinux=no) - AC_SUBST(SELINUX_CFLAGS) - AC_SUBST(SELINUX_LIBS) if test "x$have_selinux" = xno -a "x$enable_selinux" = xyes; then AC_MSG_ERROR([*** SELinux support requested but libraries not found]) fi fi AM_CONDITIONAL(HAVE_SELINUX, [test "$have_selinux" = "yes"]) +have_xz=no +AC_ARG_ENABLE(xz, AS_HELP_STRING([--disable-xz], [Disable optional XZ support])) +if test "x$enable_xz" != "xno"; then + PKG_CHECK_MODULES(XZ, [ liblzma ], + [AC_DEFINE(HAVE_XZ, 1, [Define if XZ is available]) have_xz=yes], have_xz=no) + if test "x$have_xz" = xno -a "x$enable_xz" = xyes; then + AC_MSG_ERROR([*** Xz support requested but libraries not found]) + fi +fi +AM_CONDITIONAL(HAVE_XZ, [test "$have_xz" = "yes"]) + AC_ARG_ENABLE([tcpwrap], AS_HELP_STRING([--disable-tcpwrap],[Disable optional TCP wrappers support]), [case "${enableval}" in @@ -277,8 +288,6 @@ AC_ARG_ENABLE(libcryptsetup, AS_HELP_STRING([--disable-libcryptsetup], [disable if test "x$enable_libcryptsetup" != "xno"; then PKG_CHECK_MODULES(LIBCRYPTSETUP, [ libcryptsetup ], [AC_DEFINE(HAVE_LIBCRYPTSETUP, 1, [Define if libcryptsetup is available]) have_libcryptsetup=yes], have_libcryptsetup=no) - AC_SUBST(LIBCRYPTSETUP_CFLAGS) - AC_SUBST(LIBCRYPTSETUP_LIBS) if test "x$have_libcryptsetup" = xno -a "x$enable_libcryptsetup" = xyes; then AC_MSG_ERROR([*** libcryptsetup support requested but libraries not found]) fi @@ -288,17 +297,85 @@ AM_CONDITIONAL(HAVE_LIBCRYPTSETUP, [test "$have_libcryptsetup" = "yes"]) have_binfmt=no AC_ARG_ENABLE(binfmt, AS_HELP_STRING([--disable-binfmt], [disable binfmt tool])) if test "x$enable_binfmt" != "xno"; then - have_binfmt=yes + have_binfmt=yes fi AM_CONDITIONAL(ENABLE_BINFMT, [test "$have_binfmt" = "yes"]) +have_vconsole=no +AC_ARG_ENABLE(vconsole, AS_HELP_STRING([--disable-vconsole], [disable vconsole tool])) +if test "x$enable_vconsole" != "xno"; then + have_vconsole=yes +fi +AM_CONDITIONAL(ENABLE_VCONSOLE, [test "$have_vconsole" = "yes"]) + +have_readahead=no +AC_ARG_ENABLE(readahead, AS_HELP_STRING([--disable-readahead], [disable readahead tools])) +if test "x$enable_readahead" != "xno"; then + have_readahead=yes +fi +AM_CONDITIONAL(ENABLE_READAHEAD, [test "$have_readahead" = "yes"]) + +have_quotacheck=no +AC_ARG_ENABLE(quotacheck, AS_HELP_STRING([--disable-quotacheck], [disable quotacheck tools])) +if test "x$enable_quotacheck" != "xno"; then + have_quotacheck=yes +fi +AM_CONDITIONAL(ENABLE_QUOTACHECK, [test "$have_quotacheck" = "yes"]) + +have_randomseed=no +AC_ARG_ENABLE(randomseed, AS_HELP_STRING([--disable-randomseed], [disable randomseed tools])) +if test "x$enable_randomseed" != "xno"; then + have_randomseed=yes +fi +AM_CONDITIONAL(ENABLE_RANDOMSEED, [test "$have_randomseed" = "yes"]) + +have_logind=no +AC_ARG_ENABLE(logind, AS_HELP_STRING([--disable-logind], [disable login daemon])) +if test "x$enable_logind" != "xno"; then + have_logind=yes +fi +AM_CONDITIONAL(ENABLE_LOGIND, [test "$have_logind" = "yes"]) + +have_hostnamed=no +AC_ARG_ENABLE(hostnamed, AS_HELP_STRING([--disable-hostnamed], [disable hostname daemon])) +if test "x$enable_hostnamed" != "xno"; then + have_hostnamed=yes +fi +AM_CONDITIONAL(ENABLE_HOSTNAMED, [test "$have_hostnamed" = "yes"]) + +have_timedated=no +AC_ARG_ENABLE(timedated, AS_HELP_STRING([--disable-timedated], [disable timedate daemon])) +if test "x$enable_timedated" != "xno"; then + have_timedated=yes +fi +AM_CONDITIONAL(ENABLE_TIMEDATED, [test "$have_timedated" = "yes"]) + +have_localed=no +AC_ARG_ENABLE(localed, AS_HELP_STRING([--disable-localed], [disable locale daemon])) +if test "x$enable_localed" != "xno"; then + have_localed=yes +fi +AM_CONDITIONAL(ENABLE_LOCALED, [test "$have_localed" = "yes"]) + +have_coredump=no +AC_ARG_ENABLE(coredump, AS_HELP_STRING([--disable-coredump], [disable coredump hook])) +if test "x$enable_coredump" != "xno"; then + have_coredump=yes +fi +AM_CONDITIONAL(ENABLE_COREDUMP, [test "$have_coredump" = "yes"]) + +have_manpages=no +AC_ARG_ENABLE(manpages, AS_HELP_STRING([--disable-manpages], [disable manpages])) +if test "x$enable_manpages" != "xno"; then + have_manpages=yes +fi +AM_CONDITIONAL(ENABLE_MANPAGES, [test "$have_manpages" = "yes"]) + have_gtk=no AC_ARG_ENABLE(gtk, AS_HELP_STRING([--disable-gtk], [disable GTK tools])) if test "x$enable_gtk" != "xno"; then - PKG_CHECK_MODULES(GTK, [ gtk+-2.0 glib-2.0 > 2.26 gio-unix-2.0 ], + PKG_CHECK_MODULES(GTK, [ gtk+-2.0 glib-2.0 > 2.26 gio-unix-2.0 gee-1.0], [AC_DEFINE(HAVE_GTK, 1, [Define if GTK is available]) have_gtk=yes], have_gtk=no) - AC_SUBST(GTK_CFLAGS) - AC_SUBST(GTK_LIBS) if test "x$have_gtk" = xno -a "x$enable_gtk" = xyes; then AC_MSG_ERROR([*** gtk support requested but libraries not found]) fi @@ -307,12 +384,7 @@ AM_CONDITIONAL(HAVE_GTK, [test "$have_gtk" = "yes"]) if test "$have_gtk" = "yes"; then PKG_CHECK_MODULES(LIBNOTIFY, [ libnotify ]) - PKG_CHECK_EXISTS([ libnotify >= 0.7.0 ], [ libnotify07=yes ]) - - AC_SUBST(LIBNOTIFY_CFLAGS) - AC_SUBST(LIBNOTIFY_LIBS) fi -AM_CONDITIONAL(LIBNOTIFY07, [ test "$libnotify07" = "yes" ]) AM_PROG_VALAC([0.10]) AC_SUBST(VAPIDIR) @@ -323,28 +395,15 @@ AM_CONDITIONAL(HAVE_XSLTPROC, test x"$XSLTPROC" != x) AC_PATH_PROG([M4], [m4]) -AC_ARG_WITH(distro, AS_HELP_STRING([--with-distro=DISTRO],[Specify the distribution to target: One of fedora, suse, debian, ubuntu, arch, gentoo, slackware, altlinux or other])) +AC_ARG_WITH(distro, AS_HELP_STRING([--with-distro=DISTRO],[Specify the distribution to target: One of fedora, suse, debian, ubuntu, arch, gentoo, slackware, altlinuxi, mandriva, meego, mageia, angstrom or other])) if test "z$with_distro" = "z"; then if test "$cross_compiling" = yes; then AC_MSG_WARN([Target distribution cannot be reliably detected when cross-compiling. You should specify it with --with-distro (see $0 --help for recognized distros)]) else - test -f "/etc/redhat-release" && with_distro="fedora" - test -f "/etc/SuSE-release" && with_distro="suse" - test -f "/etc/debian_version" && with_distro="debian" - test -f "/etc/arch-release" && with_distro="arch" - test -f "/etc/gentoo-release" && with_distro="gentoo" - test -f "/etc/slackware-version" && with_distro="slackware" - test -f "/etc/frugalware-release" && with_distro="frugalware" - test -f "/etc/altlinux-release" && with_distro="altlinux" - test -f "/etc/mandriva-release" && with_distro="mandriva" - test -f "/etc/meego-release" && with_distro="meego" - test -f "/etc/angstrom-version" && with_distro="angstrom" - if test "x`lsb_release -is 2>/dev/null`" = "xUbuntu"; then - with_distro="ubuntu" - fi + with_distro=$($GREP '^ID=' /etc/os-release | $SED 's/ID=//'); fi if test "z$with_distro" = "z"; then - with_distro=`uname -s` + with_distro=other fi fi with_distro=`echo ${with_distro} | tr '[[:upper:]]' '[[:lower:]]' ` @@ -354,77 +413,83 @@ AC_DEFINE_UNQUOTED(DISTRIBUTION, ["${with_distro}"], [Target Distribution]) SYSTEM_SYSVINIT_PATH=/etc/init.d SYSTEM_SYSVRCND_PATH=/etc/rc.d -M4_DISTRO_FLAG= +M4_DEFINES= have_plymouth=no case $with_distro in fedora) SYSTEM_SYSVINIT_PATH=/etc/rc.d/init.d AC_DEFINE(TARGET_FEDORA, [], [Target is Fedora/RHEL]) - M4_DISTRO_FLAG=-DTARGET_FEDORA=1 + M4_DEFINES=-DTARGET_FEDORA=1 have_plymouth=yes ;; - suse) + opensuse|suse) SYSTEM_SYSVRCND_PATH=/etc/init.d AC_DEFINE(TARGET_SUSE, [], [Target is openSUSE/SLE]) - M4_DISTRO_FLAG=-DTARGET_SUSE=1 + M4_DEFINES=-DTARGET_SUSE=1 have_plymouth=yes ;; debian) SYSTEM_SYSVRCND_PATH=/etc AC_DEFINE(TARGET_DEBIAN, [], [Target is Debian]) - M4_DISTRO_FLAG=-DTARGET_DEBIAN=1 + M4_DEFINES=-DTARGET_DEBIAN=1 ;; ubuntu) SYSTEM_SYSVRCND_PATH=/etc AC_DEFINE(TARGET_UBUNTU, [], [Target is Ubuntu]) - M4_DISTRO_FLAG=-DTARGET_UBUNTU=1 + M4_DEFINES=-DTARGET_UBUNTU=1 ;; arch) SYSTEM_SYSVINIT_PATH=/etc/rc.d SYSTEM_SYSVRCND_PATH=/etc AC_DEFINE(TARGET_ARCH, [], [Target is ArchLinux]) - M4_DISTRO_FLAG=-DTARGET_ARCH=1 + M4_DEFINES=-DTARGET_ARCH=1 ;; gentoo) SYSTEM_SYSVINIT_PATH= SYSTEM_SYSVRCND_PATH= AC_DEFINE(TARGET_GENTOO, [], [Target is Gentoo]) - M4_DISTRO_FLAG=-DTARGET_GENTOO=1 + M4_DEFINES=-DTARGET_GENTOO=1 ;; slackware) SYSTEM_SYSVINIT_PATH=/etc/rc.d/init.d AC_DEFINE(TARGET_SLACKWARE, [], [Target is Slackware]) - M4_DISTRO_FLAG=-DTARGET_SLACKWARE=1 + M4_DEFINES=-DTARGET_SLACKWARE=1 ;; frugalware) SYSTEM_SYSVINIT_PATH=/etc/rc.d AC_DEFINE(TARGET_FRUGALWARE, [], [Target is Frugalware]) - M4_DISTRO_FLAG=-DTARGET_FRUGALWARE=1 + M4_DEFINES=-DTARGET_FRUGALWARE=1 have_plymouth=yes ;; altlinux) SYSTEM_SYSVINIT_PATH=/etc/rc.d/init.d AC_DEFINE(TARGET_ALTLINUX, [], [Target is ALTLinux]) - M4_DISTRO_FLAG=-DTARGET_ALTLINUX=1 + M4_DEFINES=-DTARGET_ALTLINUX=1 have_plymouth=yes ;; mandriva) SYSTEM_SYSVINIT_PATH=/etc/rc.d/init.d AC_DEFINE(TARGET_MANDRIVA, [], [Target is Mandriva]) - M4_DISTRO_FLAG=-DTARGET_MANDRIVA=1 + M4_DEFINES=-DTARGET_MANDRIVA=1 have_plymouth=yes ;; meego) SYSTEM_SYSVINIT_PATH= SYSTEM_SYSVRCND_PATH= AC_DEFINE(TARGET_MEEGO, [], [Target is MeeGo]) - M4_DISTRO_FLAG=-DTARGET_MEEGO=1 - ;; + M4_DEFINES=-DTARGET_MEEGO=1 + ;; angstrom) SYSTEM_SYSVRCND_PATH=/etc AC_DEFINE(TARGET_ANGSTROM, [], [Target is Ångström]) - M4_DISTRO_FLAG=-DTARGET_ANGSTROM=1 + M4_DEFINES=-DTARGET_ANGSTROM=1 + ;; + mageia) + SYSTEM_SYSVINIT_PATH=/etc/rc.d/init.d + AC_DEFINE(TARGET_MAGEIA, [], [Target is Mageia]) + M4_DISTRO_FLAG=-DTARGET_MAGEIA=1 + have_plymouth=yes ;; other) ;; @@ -434,10 +499,10 @@ case $with_distro in esac AC_ARG_WITH([sysvinit-path], - [AS_HELP_STRING([--with-sysvinit-path=PATH], - [Specify the path to where the SysV init scripts are located @<:@default=based on distro@:>@])], - [SYSTEM_SYSVINIT_PATH="$withval"], - []) + [AS_HELP_STRING([--with-sysvinit-path=PATH], + [Specify the path to where the SysV init scripts are located @<:@default=based on distro@:>@])], + [SYSTEM_SYSVINIT_PATH="$withval"], + []) AC_ARG_WITH([sysvrcd-path], [AS_HELP_STRING([--with-sysvrcd-path=PATH], @@ -447,11 +512,12 @@ AC_ARG_WITH([sysvrcd-path], AC_SUBST(SYSTEM_SYSVINIT_PATH) AC_SUBST(SYSTEM_SYSVRCND_PATH) -AC_SUBST(M4_DISTRO_FLAG) +AC_SUBST(M4_DEFINES) if test "x${SYSTEM_SYSVINIT_PATH}" != "x" -a "x${SYSTEM_SYSVRCND_PATH}" != "x"; then AC_DEFINE(HAVE_SYSV_COMPAT, [], [SysV init scripts and rcN.d links are supported.]) SYSTEM_SYSV_COMPAT="yes" + M4_DEFINES="$M4_DEFINES -DHAVE_SYSV_COMPAT" elif test "x${SYSTEM_SYSVINIT_PATH}" != "x" -o "x${SYSTEM_SYSVRCND_PATH}" != "x"; then AC_MSG_ERROR([*** You need both --with-sysvinit-path=PATH and --with-sysvrcd-path=PATH to enable SysV compatibility support, or both empty to disable it.]) else @@ -482,6 +548,7 @@ AM_CONDITIONAL(TARGET_ALTLINUX, test x"$with_distro" = xaltlinux) AM_CONDITIONAL(TARGET_MANDRIVA, test x"$with_distro" = xmandriva) AM_CONDITIONAL(TARGET_MEEGO, test x"$with_distro" = xmeego) AM_CONDITIONAL(TARGET_ANGSTROM, test x"$with_distro" = xangstrom) +AM_CONDITIONAL(TARGET_MAGEIA, test x"$with_distro" = xmageia) AM_CONDITIONAL(HAVE_PLYMOUTH, test "$have_plymouth" = "yes") AM_CONDITIONAL(HAVE_SYSV_COMPAT, test "$SYSTEM_SYSV_COMPAT" = "yes") @@ -507,32 +574,44 @@ AC_ARG_WITH([dbusinterfacedir], [with_dbusinterfacedir=`pkg-config --variable=session_bus_services_dir dbus-1`/../interfaces]) AC_ARG_WITH([udevrulesdir], - AS_HELP_STRING([--with-udevrulesdir=DIR], [Diectory for udev rules]), + AS_HELP_STRING([--with-udevrulesdir=DIR], [Directory for udev rules]), [], [with_udevrulesdir=`pkg-config --variable=udevdir udev`/rules.d]) -AC_ARG_WITH([pamlibdir], - AS_HELP_STRING([--with-pamlibdir=DIR], [Diectory for PAM modules]), - [], - [with_pamlibdir=/lib/`$CC -print-multi-os-directory`/security]) - -AC_ARG_WITH([rootdir], - AS_HELP_STRING([--with-rootdir=DIR], [Root directory for files necessary for boot]), - [], - [with_rootdir=${ac_default_prefix}]) +AC_ARG_WITH([rootprefix], + AS_HELP_STRING([--with-rootprefix=DIR], [rootfs directory prefix for config files and kernel modules]), + [], [with_rootprefix=${ac_default_prefix}]) AC_ARG_WITH([rootlibdir], AS_HELP_STRING([--with-rootlibdir=DIR], [Root directory for libraries necessary for boot]), [], [with_rootlibdir=${libdir}]) +AC_ARG_WITH([pamlibdir], + AS_HELP_STRING([--with-pamlibdir=DIR], [Directory for PAM modules]), + [], + [with_pamlibdir=${with_rootlibdir}/security]) + +AC_ARG_ENABLE([split-usr], + AS_HELP_STRING([--enable-split-usr], [Assume that /bin, /sbin aren\'t symlinks into /usr]), + [], + [AS_IF([test "x${ac_default_prefix}" != "x${with_rootprefix}"], [ + enable_split_usr=yes + ], [ + enable_split_usr=no + ])]) + +AS_IF([test "x${enable_split_usr}" = "xyes"], [ + AC_DEFINE(HAVE_SPLIT_USR, 1, [Define if /bin, /sbin aren't symlinks into /usr]) +]) + AC_SUBST([dbuspolicydir], [$with_dbuspolicydir]) AC_SUBST([dbussessionservicedir], [$with_dbussessionservicedir]) AC_SUBST([dbussystemservicedir], [$with_dbussystemservicedir]) AC_SUBST([dbusinterfacedir], [$with_dbusinterfacedir]) AC_SUBST([udevrulesdir], [$with_udevrulesdir]) AC_SUBST([pamlibdir], [$with_pamlibdir]) -AC_SUBST([rootdir], [$with_rootdir]) +AC_SUBST([rootprefix], [$with_rootprefix]) AC_SUBST([rootlibdir], [$with_rootlibdir]) AC_CONFIG_FILES([Makefile po/Makefile.in]) @@ -550,17 +629,30 @@ AC_MSG_RESULT([ PAM: ${have_pam} AUDIT: ${have_audit} SELinux: ${have_selinux} + XZ: ${have_xz} ACL: ${have_acl} binfmt: ${have_binfmt} + vconsole: ${have_vconsole} + readahead: ${have_readahead} + quotacheck: ${have_quotacheck} + randomseed: ${have_randomseed} + logind: ${have_logind} + hostnamed: ${have_hostnamed} + timedated: ${have_timedated} + localed: ${have_localed} + coredump: ${have_coredump} plymouth: ${have_plymouth} prefix: ${prefix} - root dir: ${with_rootdir} + rootprefix: ${with_rootprefix} + libexec dir: ${libexecdir} lib dir: ${libdir} rootlib dir: ${with_rootlibdir} - pam modules dir: ${with_pamlibdir} + PAM modules dir: ${with_pamlibdir} udev rules dir: ${with_udevrulesdir} - dbus policy dir: ${with_dbuspolicydir} - dbus session dir: ${with_dbussessionservicedir} - dbus system dir: ${with_dbussystemservicedir} - dbus interfaces dir: ${with_dbusinterfacedir} + D-Bus policy dir: ${with_dbuspolicydir} + D-Bus session dir: ${with_dbussessionservicedir} + D-Bus system dir: ${with_dbussystemservicedir} + D-Bus interfaces dir: ${with_dbusinterfacedir} + Split /usr: ${enable_split_usr} + Build man pages: ${have_manpages} ]) diff --git a/man/custom-html.xsl b/man/custom-html.xsl new file mode 100644 index 0000000000..2d2f458793 --- /dev/null +++ b/man/custom-html.xsl @@ -0,0 +1,29 @@ +<?xml version='1.0'?> <!--*-nxml-*--> + +<!-- + This file is part of systemd. + + Copyright 2011 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/>. +--> + +<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> + +<xsl:import href="http://docbook.sourceforge.net/release/xsl/current/html/docbook.xsl"/> + +<!-- Switch things to UTF-8, ISO-8859-1 is soo yesteryear --> +<xsl:output method="html" encoding="UTF-8" indent="no"/> + +</xsl:stylesheet> diff --git a/man/daemon.xml b/man/daemon.xml index d5a8491850..997ee5b253 100644 --- a/man/daemon.xml +++ b/man/daemon.xml @@ -323,7 +323,7 @@ <listitem><para>Instead of using the <function>syslog()</function> call to log directly to the - system logger, a new-style daemon may + system syslog service, a new-style daemon may choose to simply log to STDERR via <function>fprintf()</function>, which is then forwarded to syslog by the init system. If log @@ -705,24 +705,6 @@ operating system-independent.</para></listitem> - <listitem><para>Since not all syslog - implementations are socket-activatable - yet, it is recommended to place an - <varname>After=syslog.target</varname> - dependency in service files for - daemons that can log to - syslog. <filename>syslog.target</filename> - then either pulls in the syslog daemon - itself or simply the activation - socket. A <varname>Wants=</varname> or - even <varname>Requires=</varname> - dependency should generally not be - added, since it should be up to the - administrator whether he wants to - enable logging or not, and most syslog - clients work fine if no log daemon is - running.</para></listitem> - <listitem><para>Make sure to include an <literal>[Install]</literal> section including installation diff --git a/man/hostname.xml b/man/hostname.xml index b8b05c8d35..1acda1af5d 100644 --- a/man/hostname.xml +++ b/man/hostname.xml @@ -86,7 +86,7 @@ <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>, <citerefentry><refentrytitle>sethostname</refentrytitle><manvolnum>2</manvolnum></citerefentry>, <citerefentry><refentrytitle>hostname</refentrytitle><manvolnum>1</manvolnum></citerefentry>, - <citerefentry><refentrytitle>hostname</refentrytitle><manvolnum>5</manvolnum></citerefentry>, + <citerefentry><refentrytitle>hostname</refentrytitle><manvolnum>7</manvolnum></citerefentry>, <citerefentry><refentrytitle>machine-id</refentrytitle><manvolnum>5</manvolnum></citerefentry>, <citerefentry><refentrytitle>machine-info</refentrytitle><manvolnum>5</manvolnum></citerefentry> </para> diff --git a/man/locale.conf.xml b/man/locale.conf.xml index 742c5ebb2d..37239974b6 100644 --- a/man/locale.conf.xml +++ b/man/locale.conf.xml @@ -69,6 +69,7 @@ <para>Note that the kernel command line options <varname>locale.LANG=</varname>, + <varname>locale.LANGUAGE=</varname>, <varname>locale.LC_CTYPE=</varname>, <varname>locale.LC_NUMERIC=</varname>, <varname>locale.LC_TIME=</varname>, @@ -101,6 +102,7 @@ <para>The following locale settings may be set using <filename>/etc/locale.conf</filename>: <varname>LANG=</varname>, + <varname>LANGUAGE=</varname>, <varname>LC_CTYPE=</varname>, <varname>LC_NUMERIC=</varname>, <varname>LC_TIME=</varname>, diff --git a/man/os-release.xml b/man/os-release.xml index f85119d26a..7d799721de 100644 --- a/man/os-release.xml +++ b/man/os-release.xml @@ -58,23 +58,37 @@ contains operating system identification data.</para> <para>The basic file format of - <filename>os-release</filename> is a - newline-separated list of environment-like - shell-compatible variable assignments. It is possible - to source the configuration from shell scripts, - however, beyond mere variable assignments no shell - features are supported, allowing applications to read - the file without implementing a shell compatible - execution engine.</para> + <filename>os-release</filename> is a newline-separated + list of environment-like shell-compatible variable + assignments. It is possible to source the + configuration from shell scripts, however, beyond mere + variable assignments no shell features are supported + (this means variable expansion is explicitly not + supported), allowing applications to read the file + without implementing a shell compatible execution + engine. Variable assignment values should be enclosed + in double or single quotes if they include spaces, + semicolons or other special characters outside of A-Z, + a-z, 0-9. All strings should be in UTF-8 format, and + non-printable characters should not be used. If double + or single quotes or backslashes are to be used within + variable assignments they should be escaped with + backslashes, following shell style. It is not + supported to concatenate multiple individually quoted + strings. Lines beginning with "#" shall be ignored as + comments.</para> <para><filename>/etc/os-release</filename> contains data that is defined by the operating system vendor and should not be changed by the administrator.</para> - <para>Depending on the operating system other - configuration files might be checked for OS - identification as well, however only as - fallback.</para> + <para>As this file only encodes names and identifiers + it should not be localized.</para> + + <para>For a longer rationale for + <filename>/etc/os-release</filename> please refer to + the <ulink + url="http://0pointer.de/blog/projects/os-release">Announcement of <filename>/etc/os-release</filename></ulink>.</para> </refsect1> <refsect1> @@ -90,10 +104,10 @@ <listitem><para>A string identifying the operating system, without a - version string, and not necessarily - suitable for presentation to the - user. If not set defaults to - <literal>Linux</literal>. Example: + version component, and suitable for + presentation to the user. If not set + defaults to + <literal>NAME=Linux</literal>. Example: <literal>NAME=Fedora</literal> or <literal>NAME="Debian GNU/Linux"</literal>.</para></listitem> @@ -104,37 +118,46 @@ <listitem><para>A string identifying the operating system version, - excluding any name information and - suitable for presentation to the - user. Example: - <literal>VERSION=15</literal> or - <literal>VERSION="15 - (Rawhide)"</literal>.</para></listitem> + excluding any OS name information, + possibly including a release code + name, and suitable for presentation to + the user. This field is + optional. Example: + <literal>VERSION=17</literal> or + <literal>VERSION="17 (Beefy + Miracle)"</literal>.</para></listitem> </varlistentry> <varlistentry> <term><varname>ID=</varname></term> <listitem><para>A lower-case string - (no spaces) identifying the operating - system, excluding any version - information and suitable for - processing by scripts or usage in - generated file names. If not set - defaults to - <literal>linux</literal>. Example: - <literal>ID=fedora</literal>.</para></listitem> + (no spaces or other characters outside + of 0-9, a-z, ".", "_" and "-") + identifying the operating system, + excluding any version information and + suitable for processing by scripts or + usage in generated file names. If not + set defaults to + <literal>ID=linux</literal>. Example: + <literal>ID=fedora</literal> or + <literal>ID=debian</literal>.</para></listitem> </varlistentry> <varlistentry> <term><varname>VERSION_ID=</varname></term> <listitem><para>A lower-case string - (mostly numeric, no spaces) identifying the - operating system version, excluding - any name information and suitable for - processing by scripts or usage in generated file names. Example: - <literal>VERSION_ID=15</literal>.</para></listitem> + (mostly numeric, no spaces or other + characters outside of 0-9, a-z, ".", + "_" and "-") identifying the operating + system version, excluding any OS name + information or release code name, and + suitable for processing by scripts or + usage in generated file names. This + field is optional. Example: + <literal>VERSION_ID=17</literal> or + <literal>VERSION_ID=11.04</literal>.</para></listitem> </varlistentry> <varlistentry> @@ -143,11 +166,12 @@ <listitem><para>A pretty operating system name in a format suitable for presentation to the user. May or may - not contain an OS version of some - kind, as suitable. If not set defaults - to <literal>Linux</literal>. Example: - <literal>PRETTY_NAME=Fedora 15 - (Rawhide)</literal>.</para></listitem> + not contain a release code name or OS + version of some kind, as suitable. If + not set defaults to + <literal>PRETTY_NAME="Linux"</literal>. Example: + <literal>PRETTY_NAME="Fedora 17 (Beefy + Miracle)"</literal>.</para></listitem> </varlistentry> <varlistentry> @@ -159,15 +183,30 @@ should be specified as string suitable for inclusion in the ESC [ m ANSI/ECMA-48 escape code for setting - graphical rendition. Example: - <literal>ANSI_COLOR=0;31</literal> for - red, or - <literal>ANSI_COLOR=1;34</literal> for - light blue.</para></listitem> + graphical rendition. This field is + optional. Example: + <literal>ANSI_COLOR="0;31"</literal> + for red, or + <literal>ANSI_COLOR="1;34"</literal> + for light blue.</para></listitem> + </varlistentry> + + <varlistentry> + <term><varname>CPE_NAME=</varname></term> + + <listitem><para>A CPE name for the + operating system, following the <ulink + url="http://cpe.mitre.org/specification/">Common + Platform Enumeration + Specification</ulink> as proposed by + the MITRE Corporation. This field + is optional. Example: + <literal>CPE_NAME="cpe:/o:fedoraproject:fedora:17"</literal> + </para></listitem> </varlistentry> </variablelist> - <para>If you are reading this file from code or a + <para>If you are reading this file from C code or a shell script to determine the OS or a specific version of it, use the ID and VERSION_ID fields. When looking for an OS identification string for presentation to @@ -184,11 +223,12 @@ <title>Example</title> <programlisting>NAME=Fedora -VERSION="15 (Rawhide)" +VERSION="17 (Beefy Miracle)" ID=fedora -VERSION_ID=15 -PRETTY_NAME="Fedora 15 (Rawhide)" -ANSI_COLOR=0;34</programlisting> +VERSION_ID=17 +PRETTY_NAME="Fedora 17 (Beefy Miracle)" +ANSI_COLOR="0;34" +CPE_NAME="cpe:/o:fedoraproject:fedora:17"</programlisting> </refsect1> <refsect1> diff --git a/man/sd-daemon.xml b/man/sd-daemon.xml index cbfe28f842..4ea88e43d7 100644 --- a/man/sd-daemon.xml +++ b/man/sd-daemon.xml @@ -50,8 +50,13 @@ <refsynopsisdiv> <funcsynopsis> - <funcsynopsisinfo>#include "sd-daemon.h"</funcsynopsisinfo> + <funcsynopsisinfo>#include <systemd/sd-daemon.h></funcsynopsisinfo> </funcsynopsis> + + <cmdsynopsis> + <command>pkg-config --cflags --libs libsystemd-daemon</command> + </cmdsynopsis> + </refsynopsisdiv> <refsect1> @@ -125,21 +130,24 @@ <para>In addition, for details about the algorithms check the liberally licensed reference implementation sources: - <ulink url="http://cgit.freedesktop.org/systemd/tree/src/sd-daemon.c"/> - resp. <ulink url="http://cgit.freedesktop.org/systemd/tree/src/sd-daemon.h"/></para> + <ulink url="http://cgit.freedesktop.org/systemd/systemd/plain/src/sd-daemon.c"/> + resp. <ulink url="http://cgit.freedesktop.org/systemd/systemd/plain/src/systemd/sd-daemon.h"/></para> <para>These APIs are implemented in the reference - implementation's drop-in - <filename>sd-daemon.c</filename> and - <filename>sd-daemon.h</filename> files. It is - recommended that applications consuming these APIs copy - the implementation into their source tree, either - verbatim or in excerpts. These interfaces are - currently not available in a dynamic library.</para> + implementation's <filename>sd-daemon.c</filename> and + <filename>sd-daemon.h</filename> files. These + interfaces are available as shared library, which can + be compiled and linked to with the + <literal>libsystemd-daemon</literal> + <citerefentry><refentrytitle>pkg-config</refentrytitle><manvolnum>1</manvolnum></citerefentry> + file. Alternatively, applications consuming these APIs + may copy the implementation into their source tree, + either verbatim or in excerpts.</para> <para>The functions directly related to new-style daemons become NOPs when -DDISABLE_SYSTEMD is set - during compilation. In addition, if + during compilation and the reference implementation is + used as drop-in files. In addition, if <filename>sd-daemon.c</filename> is compiled on non-Linux systems they become NOPs.</para> </refsect1> @@ -156,7 +164,8 @@ <citerefentry><refentrytitle>systemd.service</refentrytitle><manvolnum>5</manvolnum></citerefentry>, <citerefentry><refentrytitle>systemd.socket</refentrytitle><manvolnum>5</manvolnum></citerefentry>, <citerefentry><refentrytitle>fprintf</refentrytitle><manvolnum>3</manvolnum></citerefentry>, - <citerefentry><refentrytitle>sd-readahead</refentrytitle><manvolnum>7</manvolnum></citerefentry> + <citerefentry><refentrytitle>sd-readahead</refentrytitle><manvolnum>7</manvolnum></citerefentry>, + <citerefentry><refentrytitle>pkg-config</refentrytitle><manvolnum>1</manvolnum></citerefentry> </para> </refsect1> diff --git a/man/sd-login.xml b/man/sd-login.xml new file mode 100644 index 0000000000..3fc0e16f69 --- /dev/null +++ b/man/sd-login.xml @@ -0,0 +1,146 @@ +<?xml version='1.0'?> <!--*-nxml-*--> +<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" + "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"> + +<!-- + 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/>. +--> + +<refentry id="sd-login"> + + <refentryinfo> + <title>sd-login</title> + <productname>systemd</productname> + + <authorgroup> + <author> + <contrib>Developer</contrib> + <firstname>Lennart</firstname> + <surname>Poettering</surname> + <email>lennart@poettering.net</email> + </author> + </authorgroup> + </refentryinfo> + + <refmeta> + <refentrytitle>sd-login</refentrytitle> + <manvolnum>7</manvolnum> + </refmeta> + + <refnamediv> + <refname>sd-login</refname> + <refpurpose>APIs for + tracking logins</refpurpose> + </refnamediv> + + <refsynopsisdiv> + <funcsynopsis> + <funcsynopsisinfo>#include <systemd/sd-login.h></funcsynopsisinfo> + </funcsynopsis> + + <cmdsynopsis> + <command>pkg-config --cflags --libs libsystemd-login</command> + </cmdsynopsis> + </refsynopsisdiv> + + <refsect1> + <title>Description</title> + + <para><filename>sd-login.h</filename> provides APIs to + introspect and monitor seat, login session and user + status information on the local system. </para> + + <para>See <ulink + url="http://www.freedesktop.org/wiki/Software/systemd/multiseat">Multi-Seat + on Linux</ulink> for an introduction into multi-seat + support on Linux, the background for this set of APIs.</para> + + <para>Note that these APIs only allow purely passive access + and monitoring of seats, sessions and users. To + actively make changes to the seat configuration, + terminate login sessions, or switch session on a seat + you need to utilize the D-Bus API of + systemd-logind, instead.</para> + + <para>These functions synchronously access data in + <filename>/proc</filename>, + <filename>/sys/fs/cgroup</filename> and + <filename>/run</filename>. All of these are virtual + file systems, hence the runtime cost of the accesses + is relatively cheap.</para> + + <para>It is possible (and often a very good choice) to + mix calls to the synchronous interface of + <filename>sd-login.h</filename> with the asynchronous + D-Bus interface of systemd-logind. However, if this is + done you need to think a bit about possible races + since the stream of events from D-Bus and from + <filename>sd-login.h</filename> interfaces such as the + login monitor are asynchronous and not ordered against + each other.</para> + + <para>If the functions return string arrays, these are + generally NULL terminated and need to be freed by the + caller with the libc + <citerefentry><refentrytitle>free</refentrytitle><manvolnum>3</manvolnum></citerefentry> + call after use, including the strings referenced + therein. Similar, individual strings returned need to + be freed, as well.</para> + + <para>As a special exception, instead of an empty + string array NULL may be returned, which should be + treated equivalent to an empty string array.</para> + + <para>See + <citerefentry><refentrytitle>sd_pid_get_session</refentrytitle><manvolnum>3</manvolnum></citerefentry>, + <citerefentry><refentrytitle>sd_uid_get_state</refentrytitle><manvolnum>3</manvolnum></citerefentry>, + <citerefentry><refentrytitle>sd_session_is_active</refentrytitle><manvolnum>3</manvolnum></citerefentry>, + <citerefentry><refentrytitle>sd_seat_get_active</refentrytitle><manvolnum>3</manvolnum></citerefentry>, + <citerefentry><refentrytitle>sd_get_seats</refentrytitle><manvolnum>3</manvolnum></citerefentry>, + <citerefentry><refentrytitle>sd_login_monitor_new</refentrytitle><manvolnum>3</manvolnum></citerefentry> + for more information about the functions + implemented.</para> + </refsect1> + + <refsect1> + <title>Notes</title> + + <para>These APIs are implemented as shared library, + which can be compiled and linked to with the + <literal>libsystemd-login</literal> + <citerefentry><refentrytitle>pkg-config</refentrytitle><manvolnum>1</manvolnum></citerefentry> + file.</para> + </refsect1> + + <refsect1> + <title>See Also</title> + <para> + <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>, + <citerefentry><refentrytitle>sd_pid_get_session</refentrytitle><manvolnum>3</manvolnum></citerefentry>, + <citerefentry><refentrytitle>sd_uid_get_state</refentrytitle><manvolnum>3</manvolnum></citerefentry>, + <citerefentry><refentrytitle>sd_session_is_active</refentrytitle><manvolnum>3</manvolnum></citerefentry>, + <citerefentry><refentrytitle>sd_seat_get_active</refentrytitle><manvolnum>3</manvolnum></citerefentry>, + <citerefentry><refentrytitle>sd_get_seats</refentrytitle><manvolnum>3</manvolnum></citerefentry>, + <citerefentry><refentrytitle>sd_login_monitor_new</refentrytitle><manvolnum>3</manvolnum></citerefentry>, + <citerefentry><refentrytitle>sd-daemon</refentrytitle><manvolnum>7</manvolnum></citerefentry>, + <citerefentry><refentrytitle>sd-readahead</refentrytitle><manvolnum>7</manvolnum></citerefentry>, + <citerefentry><refentrytitle>pkg-config</refentrytitle><manvolnum>1</manvolnum></citerefentry> + </para> + </refsect1> + +</refentry> diff --git a/man/sd-readahead.xml b/man/sd-readahead.xml index 819691e4de..7fb8634120 100644 --- a/man/sd-readahead.xml +++ b/man/sd-readahead.xml @@ -86,8 +86,8 @@ <para>In addition, for details about the algorithms check the liberally licensed reference implementation sources: - <ulink url="http://cgit.freedesktop.org/systemd/tree/src/sd-readahead.c"/> - resp. <ulink url="http://cgit.freedesktop.org/systemd/tree/src/sd-readahead.h"/></para> + <ulink url="http://cgit.freedesktop.org/systemd/systemd/plain/src/readahead/sd-readahead.c"/> + resp. <ulink url="http://cgit.freedesktop.org/systemd/systemd/plain/src/systemd/sd-readahead.h"/></para> <para>These APIs are implemented in the reference implementation's drop-in diff --git a/man/sd_booted.xml b/man/sd_booted.xml index 841ee1d479..141625d9ad 100644 --- a/man/sd_booted.xml +++ b/man/sd_booted.xml @@ -49,7 +49,7 @@ <refsynopsisdiv> <funcsynopsis> - <funcsynopsisinfo>#include "sd-daemon.h"</funcsynopsisinfo> + <funcsynopsisinfo>#include <systemd/sd-daemon.h></funcsynopsisinfo> <funcprototype> <funcdef>int <function>sd_booted</function></funcdef> @@ -93,22 +93,28 @@ <para>For details about the algorithm check the liberally licensed reference implementation sources: - <ulink url="http://cgit.freedesktop.org/systemd/tree/src/sd-daemon.c"/> + <ulink url="http://cgit.freedesktop.org/systemd/systemd/plain/src/sd-daemon.c"/> resp. <ulink - url="http://cgit.freedesktop.org/systemd/tree/src/sd-daemon.h"/></para> + url="http://cgit.freedesktop.org/systemd/systemd/plain/src/systemd/sd-daemon.h"/></para> <para><function>sd_booted()</function> is implemented - in the reference implementation's drop-in + in the reference implementation's <filename>sd-daemon.c</filename> and - <filename>sd-daemon.h</filename> files. It is - recommended that applications consuming these APIs - copy the implementation into their source tree. For - more details about the reference implementation see - <citerefentry><refentrytitle>sd_daemon</refentrytitle><manvolnum>7</manvolnum></citerefentry></para> - - <para>If -DDISABLE_SYSTEMD is set during compilation - this function will always return 0 and otherwise - become a NOP.</para> + <filename>sd-daemon.h</filename> files. These + interfaces are available as shared library, which can + be compiled and linked to with the + <literal>libsystemd-daemon</literal> + <citerefentry><refentrytitle>pkg-config</refentrytitle><manvolnum>1</manvolnum></citerefentry> + file. Alternatively, applications consuming these APIs + may copy the implementation into their source + tree. For more details about the reference + implementation see + <citerefentry><refentrytitle>sd_daemon</refentrytitle><manvolnum>7</manvolnum></citerefentry>.</para> + + <para>If the reference implementation is used as + drop-in files and -DDISABLE_SYSTEMD is set during + compilation this function will always return 0 and + otherwise become a NOP.</para> </refsect1> <refsect1> diff --git a/man/sd_get_seats.xml b/man/sd_get_seats.xml new file mode 100644 index 0000000000..2ac76500ec --- /dev/null +++ b/man/sd_get_seats.xml @@ -0,0 +1,127 @@ +<?xml version='1.0'?> <!--*-nxml-*--> +<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" + "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"> + +<!-- + 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/>. +--> + +<refentry id="sd_get_seats"> + + <refentryinfo> + <title>sd_get_seats</title> + <productname>systemd</productname> + + <authorgroup> + <author> + <contrib>Developer</contrib> + <firstname>Lennart</firstname> + <surname>Poettering</surname> + <email>lennart@poettering.net</email> + </author> + </authorgroup> + </refentryinfo> + + <refmeta> + <refentrytitle>sd_get_seats</refentrytitle> + <manvolnum>3</manvolnum> + </refmeta> + + <refnamediv> + <refname>sd_get_seats</refname> + <refname>sd_get_sessions</refname> + <refname>sd_get_uids</refname> + <refpurpose>Determine available seats, sessions and logged in users</refpurpose> + </refnamediv> + + <refsynopsisdiv> + <funcsynopsis> + <funcsynopsisinfo>#include <systemd/sd-login.h></funcsynopsisinfo> + + <funcprototype> + <funcdef>int <function>sd_get_seats</function></funcdef> + <paramdef>char*** <parameter>seats</parameter></paramdef> + </funcprototype> + + <funcprototype> + <funcdef>int <function>sd_get_sessions</function></funcdef> + <paramdef>char*** <parameter>sessions</parameter></paramdef> + </funcprototype> + + <funcprototype> + <funcdef>int <function>sd_get_uids</function></funcdef> + <paramdef>char*** <parameter>sessions</parameter></paramdef> + </funcprototype> + + </funcsynopsis> + </refsynopsisdiv> + + <refsect1> + <title>Description</title> + + <para><function>sd_get_seats()</function> may be used + to determine all currently available local + seats. Returns a NULL terminated array of seat + identifiers. The returned array and all strings it + references need to be freed with the libc + <citerefentry><refentrytitle>free</refentrytitle><manvolnum>3</manvolnum></citerefentry> + call after use. Note that instead of an empty array + NULL may be returned and should be considered + equivalent to an empty array.</para> + + <para>Similar, <function>sd_get_sessions()</function> may + be used to determine all current login sessions.</para> + + <para>Similar, <function>sd_get_uids()</function> may + be used to determine all Unix users who currently have login sessions.</para> + </refsect1> + + <refsect1> + <title>Return Value</title> + + <para>On success <function>sd_get_seats()</function>, + <function>sd_get_sessions()</function> and + <function>sd_get_uids()</function> return the number + of entries in the arrays. On failure, these calls + return a negative errno-style error code.</para> + </refsect1> + + <refsect1> + <title>Notes</title> + + <para>The <function>sd_get_seats()</function>, + <function>sd_get_sessions()</function> and + <function>sd_get_uids()</function> interfaces + are available as shared library, which can be compiled + and linked to with the + <literal>libsystemd-login</literal> + <citerefentry><refentrytitle>pkg-config</refentrytitle><manvolnum>1</manvolnum></citerefentry> + file.</para> + </refsect1> + + <refsect1> + <title>See Also</title> + + <para> + <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>, + <citerefentry><refentrytitle>sd-login</refentrytitle><manvolnum>7</manvolnum></citerefentry>, + <citerefentry><refentrytitle>sd_session_get_seat</refentrytitle><manvolnum>3</manvolnum></citerefentry> + </para> + </refsect1> + +</refentry> diff --git a/man/sd_is_fifo.xml b/man/sd_is_fifo.xml index 251f45ce0f..4db512012c 100644 --- a/man/sd_is_fifo.xml +++ b/man/sd_is_fifo.xml @@ -53,7 +53,7 @@ <refsynopsisdiv> <funcsynopsis> - <funcsynopsisinfo>#include "sd-daemon.h"</funcsynopsisinfo> + <funcsynopsisinfo>#include <systemd/sd-daemon.h></funcsynopsisinfo> <funcprototype> <funcdef>int <function>sd_is_fifo</function></funcdef> @@ -178,21 +178,25 @@ <filename>getsockname()</filename> to check the file descriptor type and where it is bound to.</para> - <para>For details about the algorithm check the + <para>For details about the algorithms check the liberally licensed reference implementation sources: - <ulink url="http://cgit.freedesktop.org/systemd/tree/src/sd-daemon.c"/> + <ulink url="http://cgit.freedesktop.org/systemd/systemd/plain/src/sd-daemon.c"/> resp. <ulink - url="http://cgit.freedesktop.org/systemd/tree/src/sd-daemon.h"/></para> + url="http://cgit.freedesktop.org/systemd/systemd/plain/src/systemd/sd-daemon.h"/></para> <para><function>sd_is_fifo()</function> and the related functions are implemented in the reference - implementation's drop-in - <filename>sd-daemon.c</filename> and - <filename>sd-daemon.h</filename> files. It is - recommended that applications consuming these APIs - copy the implementation into their source tree. For - more details about the reference implementation see - <citerefentry><refentrytitle>sd_daemon</refentrytitle><manvolnum>7</manvolnum></citerefentry></para> + implementation's <filename>sd-daemon.c</filename> and + <filename>sd-daemon.h</filename> files. These + interfaces are available as shared library, which can + be compiled and linked to with the + <literal>libsystemd-daemon</literal> + <citerefentry><refentrytitle>pkg-config</refentrytitle><manvolnum>1</manvolnum></citerefentry> + file. Alternatively, applications consuming these APIs + may copy the implementation into their source + tree. For more details about the reference + implementation see + <citerefentry><refentrytitle>sd_daemon</refentrytitle><manvolnum>7</manvolnum></citerefentry>.</para> <para>These functions continue to work as described, even if -DDISABLE_SYSTEMD is set during diff --git a/man/sd_listen_fds.xml b/man/sd_listen_fds.xml index 128d859b97..c3c70a0df0 100644 --- a/man/sd_listen_fds.xml +++ b/man/sd_listen_fds.xml @@ -49,7 +49,7 @@ <refsynopsisdiv> <funcsynopsis> - <funcsynopsisinfo>#include "sd-daemon.h"</funcsynopsisinfo> + <funcsynopsisinfo>#include <systemd/sd-daemon.h></funcsynopsisinfo> <funcsynopsisinfo>#define SD_LISTEN_FDS_START 3</funcsynopsisinfo> @@ -140,22 +140,28 @@ <para>For details about the algorithm check the liberally licensed reference implementation sources: - <ulink url="http://cgit.freedesktop.org/systemd/tree/src/sd-daemon.c"/> + <ulink url="http://cgit.freedesktop.org/systemd/systemd/plain/src/sd-daemon.c"/> resp. <ulink - url="http://cgit.freedesktop.org/systemd/tree/src/sd-daemon.h"/></para> + url="http://cgit.freedesktop.org/systemd/systemd/plain/src/systemd/sd-daemon.h"/></para> <para><function>sd_listen_fds()</function> is - implemented in the reference implementation's drop-in + implemented in the reference implementation's <filename>sd-daemon.c</filename> and - <filename>sd-daemon.h</filename> files. It is - recommended that applications consuming these APIs - copy the implementation into their source tree. For - more details about the reference implementation see - <citerefentry><refentrytitle>sd-daemon</refentrytitle><manvolnum>7</manvolnum></citerefentry></para> - - <para>If -DDISABLE_SYSTEMD is set during compilation - this function will always return 0 and otherwise - become a NOP.</para> + <filename>sd-daemon.h</filename> files. These + interfaces are available as shared library, which can + be compiled and linked to with the + <literal>libsystemd-daemon</literal> + <citerefentry><refentrytitle>pkg-config</refentrytitle><manvolnum>1</manvolnum></citerefentry> + file. Alternatively, applications consuming these APIs + may copy the implementation into their source + tree. For more details about the reference + implementation see + <citerefentry><refentrytitle>sd-daemon</refentrytitle><manvolnum>7</manvolnum></citerefentry>.</para> + + <para>If the reference implementation is used as + drop-in files and -DDISABLE_SYSTEMD is set during + compilation this function will always return 0 and + otherwise become a NOP.</para> </refsect1> <refsect1> diff --git a/man/sd_login_monitor_new.xml b/man/sd_login_monitor_new.xml new file mode 100644 index 0000000000..de484329a9 --- /dev/null +++ b/man/sd_login_monitor_new.xml @@ -0,0 +1,172 @@ +<?xml version='1.0'?> <!--*-nxml-*--> +<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" + "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"> + +<!-- + 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/>. +--> + +<refentry id="sd_login_monitor_new"> + + <refentryinfo> + <title>sd_login_monitor_new</title> + <productname>systemd</productname> + + <authorgroup> + <author> + <contrib>Developer</contrib> + <firstname>Lennart</firstname> + <surname>Poettering</surname> + <email>lennart@poettering.net</email> + </author> + </authorgroup> + </refentryinfo> + + <refmeta> + <refentrytitle>sd_login_monitor_new</refentrytitle> + <manvolnum>3</manvolnum> + </refmeta> + + <refnamediv> + <refname>sd_login_monitor_new</refname> + <refname>sd_login_monitor_unref</refname> + <refname>sd_login_monitor_flush</refname> + <refname>sd_login_monitor_get_fd</refname> + <refpurpose>Monitor login sessions, seats and users</refpurpose> + </refnamediv> + + <refsynopsisdiv> + <funcsynopsis> + <funcsynopsisinfo>#include <systemd/sd-login.h></funcsynopsisinfo> + + <funcprototype> + <funcdef>int <function>sd_login_monitor_new</function></funcdef> + <paramdef>const char* <parameter>category</parameter></paramdef> + <paramdef>sd_login_monitor** <parameter>ret</parameter></paramdef> + </funcprototype> + + <funcprototype> + <funcdef>sd_login_monitor* <function>sd_login_monitor_unref</function></funcdef> + <paramdef>sd_login_monitor* <parameter>m</parameter></paramdef> + </funcprototype> + + <funcprototype> + <funcdef>int <function>sd_login_monitor_flush</function></funcdef> + <paramdef>sd_login_monitor* <parameter>m</parameter></paramdef> + </funcprototype> + + <funcprototype> + <funcdef>int <function>sd_login_monitor_get_fd</function></funcdef> + <paramdef>sd_login_monitor* <parameter>m</parameter></paramdef> + </funcprototype> + + </funcsynopsis> + </refsynopsisdiv> + + <refsect1> + <title>Description</title> + + <para><function>sd_login_monitor_new()</function> may + be used to monitor login session, users and seats. Via + a monitor object a file descriptor can be integrated + into an application defined event loop which is woken + up each time a user logs in, logs out or a seat is + added or removed, or a session, user, or seat changes + state otherwise. The first parameter takes a string + which can be either <literal>seat</literal> (to get + only notifications about seats being added, removed or + changed), <literal>session</literal> (to get only + notifications about sessions being created or removed + or changed) or <literal>uid</literal> (to get only + notifications when a user changes state in respect to + logins). If notifications shall be generated in all + these conditions, NULL may be passed. Note that in + future additional categories may be defined. The + second parameter returns a monitor object and needs to + be freed with the + <function>sd_login_monitor_unref()</function> call + after use.</para> + + <para><function>sd_login_monitor_unref()</function> + may be used to destroy a monitor object. Note that + this will invalidate any file descriptor returned by + <function>sd_login_monitor_get_fd()</function>.</para> + + <para><function>sd_login_monitor_flush()</function> + may be used to reset the wakeup state of the monitor + object. Whenever an event causes the monitor to wake + up the event loop via the file descriptor this + function needs to be called to reset the wake-up + state. If this call is not invoked the file descriptor + will immediately wake up the event loop again.</para> + + <para><function>sd_login_monitor_get_fd()</function> + may be used to retrieve the file descriptor of the + monitor object that may be integrated in an + application defined event loop, based around + <citerefentry><refentrytitle>poll</refentrytitle><manvolnum>2</manvolnum></citerefentry> + or a similar interface. The application should include + the returned file descriptor as wake up source for + POLLIN events. Whenever a wake-up is triggered the + file descriptor needs to be reset via + <function>sd_login_monitor_flush()</function>. An + application needs to reread the login state with a + function like + <citerefentry><refentrytitle>sd_get_seats</refentrytitle><manvolnum>3</manvolnum></citerefentry> + or similar to determine what changed.</para> + </refsect1> + + <refsect1> + <title>Return Value</title> + + <para>On success + <function>sd_login_monitor_new()</function> and + <function>sd_login_monitor_flush()</function> return 0 + or a positive integer. On success + <function>sd_login_monitor_get_fd()</function> returns + a Unix file descriptor. On failure, these calls return + a negative errno-style error code.</para> + + <para><function>sd_login_monitor_unref()</function> + always returns NULL.</para> + </refsect1> + + <refsect1> + <title>Notes</title> + + <para>The <function>sd_login_monitor_new()</function>, + <function>sd_login_monitor_unref()</function>, <function>sd_login_monitor_flush()</function> and + <function>sd_login_monitor_get_fd()</function> interfaces + are available as shared library, which can be compiled + and linked to with the + <literal>libsystemd-login</literal> + <citerefentry><refentrytitle>pkg-config</refentrytitle><manvolnum>1</manvolnum></citerefentry> + file.</para> + </refsect1> + + <refsect1> + <title>See Also</title> + + <para> + <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>, + <citerefentry><refentrytitle>sd-login</refentrytitle><manvolnum>7</manvolnum></citerefentry>, + <citerefentry><refentrytitle>sd_get_seats</refentrytitle><manvolnum>3</manvolnum></citerefentry> + </para> + </refsect1> + +</refentry> diff --git a/man/sd_notify.xml b/man/sd_notify.xml index c060bbad27..9d9ea4132f 100644 --- a/man/sd_notify.xml +++ b/man/sd_notify.xml @@ -50,7 +50,7 @@ <refsynopsisdiv> <funcsynopsis> - <funcsynopsisinfo>#include "sd-daemon.h"</funcsynopsisinfo> + <funcsynopsisinfo>#include <systemd/sd-daemon.h></funcsynopsisinfo> <funcprototype> <funcdef>int <function>sd_notify</function></funcdef> @@ -151,6 +151,18 @@ itself. Example: "MAINPID=4711"</para></listitem> </varlistentry> + + <varlistentry> + <term>WATCHDOG=1</term> + + <listitem><para>Tells systemd to + update the watchdog timestamp. + Services using this feature should do + this in regular intervals. A watchdog + framework can use the timestamps to + detect failed + services.</para></listitem> + </varlistentry> </variablelist> <para>It is recommended to prefix variable names that @@ -166,7 +178,7 @@ for details.</para> <para><function>sd_notifyf()</function> is similar to - <function>sd_notifyf()</function> but takes a + <function>sd_notify()</function> but takes a <function>printf()</function>-like format string plus arguments.</para> </refsect1> @@ -206,25 +218,30 @@ datagram is accompanied by the process credentials of the sending daemon, using SCM_CREDENTIALS.</para> - <para>For details about the algorithm check the + <para>For details about the algorithms check the liberally licensed reference implementation sources: - <ulink url="http://cgit.freedesktop.org/systemd/tree/src/sd-daemon.c"/> + <ulink url="http://cgit.freedesktop.org/systemd/systemd/plain/src/sd-daemon.c"/> resp. <ulink - url="http://cgit.freedesktop.org/systemd/tree/src/sd-daemon.h"/></para> + url="http://cgit.freedesktop.org/systemd/systemd/plain/src/systemd/sd-daemon.h"/></para> <para><function>sd_notify()</function> and <function>sd_notifyf()</function> are implemented in - the reference implementation's drop-in + the reference implementation's <filename>sd-daemon.c</filename> and - <filename>sd-daemon.h</filename> files. It is - recommended that applications consuming these APIs - copy the implementation into their source tree. For + <filename>sd-daemon.h</filename> files. These + interfaces are available as shared library, which can + be compiled and linked to with the + <literal>libsystemd-daemon</literal> + <citerefentry><refentrytitle>pkg-config</refentrytitle><manvolnum>1</manvolnum></citerefentry> + file. Alternatively, applications consuming these APIs + may copy the implementation into their source tree. For more details about the reference implementation see - <citerefentry><refentrytitle>sd_daemon</refentrytitle><manvolnum>7</manvolnum></citerefentry></para> + <citerefentry><refentrytitle>sd_daemon</refentrytitle><manvolnum>7</manvolnum></citerefentry>.</para> - <para>If -DDISABLE_SYSTEMD is set during compilation - this function will always return 0 and otherwise - become a NOP.</para> + <para>If the reference implementation is used as + drop-in files and -DDISABLE_SYSTEMD is set during + compilation these functions will always return 0 and + otherwise become a NOP.</para> </refsect1> <refsect1> diff --git a/man/sd_pid_get_session.xml b/man/sd_pid_get_session.xml new file mode 100644 index 0000000000..94f5330222 --- /dev/null +++ b/man/sd_pid_get_session.xml @@ -0,0 +1,160 @@ +<?xml version='1.0'?> <!--*-nxml-*--> +<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" + "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"> + +<!-- + 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/>. +--> + +<refentry id="sd_pid_get_session"> + + <refentryinfo> + <title>sd_pid_get_session</title> + <productname>systemd</productname> + + <authorgroup> + <author> + <contrib>Developer</contrib> + <firstname>Lennart</firstname> + <surname>Poettering</surname> + <email>lennart@poettering.net</email> + </author> + </authorgroup> + </refentryinfo> + + <refmeta> + <refentrytitle>sd_pid_get_session</refentrytitle> + <manvolnum>3</manvolnum> + </refmeta> + + <refnamediv> + <refname>sd_pid_get_session</refname> + <refname>sd_pid_get_unit</refname> + <refname>sd_pid_get_owner_uid</refname> + <refpurpose>Determine session, service or owner of a session of a specific PID</refpurpose> + </refnamediv> + + <refsynopsisdiv> + <funcsynopsis> + <funcsynopsisinfo>#include <systemd/sd-login.h></funcsynopsisinfo> + + <funcprototype> + <funcdef>int <function>sd_pid_get_session</function></funcdef> + <paramdef>pid_t <parameter>pid</parameter></paramdef> + <paramdef>char** <parameter>session</parameter></paramdef> + </funcprototype> + + <funcprototype> + <funcdef>int <function>sd_pid_get_unit</function></funcdef> + <paramdef>pid_t <parameter>pid</parameter></paramdef> + <paramdef>char** <parameter>unit</parameter></paramdef> + </funcprototype> + + <funcprototype> + <funcdef>int <function>sd_pid_get_owner_uid</function></funcdef> + <paramdef>pid_t <parameter>pid</parameter></paramdef> + <paramdef>uid_t* <parameter>uid</parameter></paramdef> + </funcprototype> + </funcsynopsis> + </refsynopsisdiv> + + <refsect1> + <title>Description</title> + + <para><function>sd_pid_get_session()</function> may be + used to determine the login session identifier of a + process identified by the specified process + identifier. The session identifier is a short string, + suitable for usage in file system paths. Note that not + all processes are part of a login session (e.g. system + service processes, user processes that are shared + between multiple sessions of the same user, or kernel + threads). For processes not being part of a login + session this function will fail. The returned string + needs to be freed with the libc + <citerefentry><refentrytitle>free</refentrytitle><manvolnum>3</manvolnum></citerefentry> + call after use.</para> + + <para><function>sd_pid_get_unit()</function> may be + used to determine the systemd unit (i.e. system + service) identifier of a process identified by the + specified process identifier. The unit name is a short + string, suitable for usage in file system paths. Note + that not all processes are part of a unit/service + (e.g. user processes, or kernel threads). For + processes not being part of a systemd unit/system + service this function will fail. The returned string + needs to be freed with the libc + <citerefentry><refentrytitle>free</refentrytitle><manvolnum>3</manvolnum></citerefentry> + call after use.</para> + + <para><function>sd_pid_get_owner_uid()</function> may + be used to determine the Unix user identifier of the + owner of the session of a process identified the + specified PID. Note that this function will succeed + for user processes which are shared between multiple + login sessions of the same user, where + <function>sd_pid_get_session()</function> will + fail. For processes not being part of a login session + and not being a shared process of a user this function + will fail.</para> + + <para>If the <literal>pid</literal> paramater of any + of these functions is passed as 0 the operation is + executed for the calling process.</para> + </refsect1> + + <refsect1> + <title>Return Value</title> + + <para>On success these calls return 0 or a positive + integer. On failure, these calls return a negative + errno-style error code.</para> + </refsect1> + + <refsect1> + <title>Notes</title> + + <para>The <function>sd_pid_get_session()</function>, + <function>sd_pid_get_pid()</function>, and + <function>sd_pid_get_owner_uid()</function> interfaces + are available as shared library, which can be compiled + and linked to with the + <literal>libsystemd-login</literal> + <citerefentry><refentrytitle>pkg-config</refentrytitle><manvolnum>1</manvolnum></citerefentry> + file.</para> + + <para>Note that the login session identifier as + returned by <function>sd_pid_get_session()</function> + is completely unrelated to the process session + identifier as returned by + <citerefentry><refentrytitle>getsid</refentrytitle><manvolnum>2</manvolnum></citerefentry>.</para> + </refsect1> + + <refsect1> + <title>See Also</title> + + <para> + <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>, + <citerefentry><refentrytitle>sd-login</refentrytitle><manvolnum>7</manvolnum></citerefentry>, + <citerefentry><refentrytitle>sd_session_is_active</refentrytitle><manvolnum>3</manvolnum></citerefentry>, + <citerefentry><refentrytitle>getsid</refentrytitle><manvolnum>2</manvolnum></citerefentry> + </para> + </refsect1> + +</refentry> diff --git a/man/sd_readahead.xml b/man/sd_readahead.xml index 004608dfba..2e7e09c5ec 100644 --- a/man/sd_readahead.xml +++ b/man/sd_readahead.xml @@ -49,7 +49,7 @@ <refsynopsisdiv> <funcsynopsis> - <funcsynopsisinfo>#include "sd-daemon.h"</funcsynopsisinfo> + <funcsynopsisinfo>#include "sd-readahead.h"</funcsynopsisinfo> <funcprototype> <funcdef>int <function>sd_readahead</function></funcdef> @@ -129,9 +129,9 @@ <para>For details about the algorithm check the liberally licensed reference implementation sources: - <ulink url="http://cgit.freedesktop.org/systemd/tree/src/sd-readahead.c"/> + <ulink url="http://cgit.freedesktop.org/systemd/systemd/plain/src/readahead/sd-readahead.c"/> resp. <ulink - url="http://cgit.freedesktop.org/systemd/tree/src/sd-readahead.h"/></para> + url="http://cgit.freedesktop.org/systemd/systemd/plain/src/systemd/sd-readahead.h"/></para> <para><function>sd_readahead()</function> is implemented in the reference implementation's drop-in diff --git a/man/sd_seat_get_active.xml b/man/sd_seat_get_active.xml new file mode 100644 index 0000000000..acc6ee4ea7 --- /dev/null +++ b/man/sd_seat_get_active.xml @@ -0,0 +1,157 @@ +<?xml version='1.0'?> <!--*-nxml-*--> +<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" + "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"> + +<!-- + 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/>. +--> + +<refentry id="sd_seat_get_active"> + + <refentryinfo> + <title>sd_seat_get_active</title> + <productname>systemd</productname> + + <authorgroup> + <author> + <contrib>Developer</contrib> + <firstname>Lennart</firstname> + <surname>Poettering</surname> + <email>lennart@poettering.net</email> + </author> + </authorgroup> + </refentryinfo> + + <refmeta> + <refentrytitle>sd_seat_get_active</refentrytitle> + <manvolnum>3</manvolnum> + </refmeta> + + <refnamediv> + <refname>sd_seat_get_active</refname> + <refname>sd_seat_get_sessions</refname> + <refname>sd_seat_can_multi_session</refname> + <refpurpose>Determine state of a specific seat</refpurpose> + </refnamediv> + + <refsynopsisdiv> + <funcsynopsis> + <funcsynopsisinfo>#include <systemd/sd-login.h></funcsynopsisinfo> + + <funcprototype> + <funcdef>int <function>sd_seat_get_active</function></funcdef> + <paramdef>const char* <parameter>seat</parameter></paramdef> + <paramdef>char** <parameter>session</parameter></paramdef> + <paramdef>uid_t* <parameter>uid</parameter></paramdef> + </funcprototype> + + <funcprototype> + <funcdef>int <function>sd_seat_get_sessions</function></funcdef> + <paramdef>const char* <parameter>seat</parameter></paramdef> + <paramdef>char*** <parameter>sessions</parameter></paramdef> + <paramdef>uid_t** <parameter>uid</parameter></paramdef> + <paramdef>unsigned* <parameter>n_uids</parameter></paramdef> + </funcprototype> + + <funcprototype> + <funcdef>int <function>sd_seat_can_multi_session</function></funcdef> + <paramdef>const char* <parameter>seat</parameter></paramdef> + </funcprototype> + </funcsynopsis> + </refsynopsisdiv> + + <refsect1> + <title>Description</title> + + <para><function>sd_seat_get_active()</function> may be + used to determine which session is currently active on + a seat, if there is any. Returns the session + identifier and the user identifier of the Unix user + the session is belonging to. Either the session or the + user identifier parameter can be be passed NULL, in + case only one of the parameters shall be queried. The + returned string needs to be freed with the libc + <citerefentry><refentrytitle>free</refentrytitle><manvolnum>3</manvolnum></citerefentry> + call after use.</para> + + <para><function>sd_seat_get_sessions()</function> may + be used to determine all sessions on the specified + seat. Returns two arrays, one (NULL terminated) with + the session identifiers of the sessions and one with + the user identifiers of the Unix users the sessions + belong to. An additional parameter may be used to + return the number of entries in the latter array. The + two arrays and the latter parameter may be passed as + NULL in case these values need not to be + determined. The arrays and the strings referenced by + them need to be freed with the libc + <citerefentry><refentrytitle>free</refentrytitle><manvolnum>3</manvolnum></citerefentry> + call after use. Note that instead of an empty array + NULL may be returned and should be considered + equivalent to an empty array.</para> + + <para><function>sd_seat_can_multi_session()</function> + may be used to determine whether a specific seat is + capable of multi-session, i.e. allows multiple login + sessions in parallel (whith only one being active at a + time).</para> + + <para>If the <literal>seat</literal> parameter of any + of these functions is passed as NULL the operation is + executed for the seat of the session of the calling + process, if there is any.</para> + </refsect1> + + <refsect1> + <title>Return Value</title> + + <para> On success + <function>sd_seat_get_active()</function> return + return 0 or a positive integer. On success + <function>sd_seat_get_sessions()</function> returns + the number of entries in the session identifier + array. If the test succeeds + <function>sd_seat_can_multi_session</function> returns + a positive integer, if it fails 0. On failure, these + calls return a negative errno-style error code.</para> + </refsect1> + + <refsect1> + <title>Notes</title> + + <para>The <function>sd_seat_get_active()</function>, + <function>sd_seat_get_sessions()</function>, and + <function>sd_seat_can_multi_session()</function> interfaces + are available as shared library, which can be compiled + and linked to with the + <literal>libsystemd-login</literal> + <citerefentry><refentrytitle>pkg-config</refentrytitle><manvolnum>1</manvolnum></citerefentry> + file.</para> + </refsect1> + + <refsect1> + <title>See Also</title> + + <para> + <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>, + <citerefentry><refentrytitle>sd-login</refentrytitle><manvolnum>7</manvolnum></citerefentry>, + <citerefentry><refentrytitle>sd_session_get_seat</refentrytitle><manvolnum>3</manvolnum></citerefentry> + </para> + </refsect1> + +</refentry> diff --git a/man/sd_session_is_active.xml b/man/sd_session_is_active.xml new file mode 100644 index 0000000000..5db305d467 --- /dev/null +++ b/man/sd_session_is_active.xml @@ -0,0 +1,157 @@ +<?xml version='1.0'?> <!--*-nxml-*--> +<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" + "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"> + +<!-- + 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/>. +--> + +<refentry id="sd_session_is_active"> + + <refentryinfo> + <title>sd_session_is_active</title> + <productname>systemd</productname> + + <authorgroup> + <author> + <contrib>Developer</contrib> + <firstname>Lennart</firstname> + <surname>Poettering</surname> + <email>lennart@poettering.net</email> + </author> + </authorgroup> + </refentryinfo> + + <refmeta> + <refentrytitle>sd_session_is_active</refentrytitle> + <manvolnum>3</manvolnum> + </refmeta> + + <refnamediv> + <refname>sd_session_is_active</refname> + <refname>sd_session_get_uid</refname> + <refname>sd_session_get_seat</refname> + <refname>sd_session_get_service</refname> + <refpurpose>Determine state of a specific session</refpurpose> + </refnamediv> + + <refsynopsisdiv> + <funcsynopsis> + <funcsynopsisinfo>#include <systemd/sd-login.h></funcsynopsisinfo> + + <funcprototype> + <funcdef>int <function>sd_session_is_active</function></funcdef> + <paramdef>const char* <parameter>session</parameter></paramdef> + </funcprototype> + + <funcprototype> + <funcdef>int <function>sd_session_get_uid</function></funcdef> + <paramdef>const char* <parameter>session</parameter></paramdef> + <paramdef>uid_t* <parameter>uid</parameter></paramdef> + </funcprototype> + + <funcprototype> + <funcdef>int <function>sd_session_get_seat</function></funcdef> + <paramdef>const char* <parameter>session</parameter></paramdef> + <paramdef>char** <parameter>seat</parameter></paramdef> + </funcprototype> + + <funcprototype> + <funcdef>int <function>sd_session_get_service</function></funcdef> + <paramdef>const char* <parameter>session</parameter></paramdef> + <paramdef>char** <parameter>service</parameter></paramdef> + </funcprototype> + </funcsynopsis> + </refsynopsisdiv> + + <refsect1> + <title>Description</title> + + <para><function>sd_session_is_active()</function> may + be used to determine whether the session identified by + the specified session identifier is currently active + (i.e. currently in the foreground and available for + user input) or not.</para> + + <para><function>sd_session_get_uid()</function> may be + used to determine the user identifier of the Unix user the session + identified by the specified session identifier belongs + to.</para> + + <para><function>sd_session_get_seat()</function> may + be used to determine the seat identifier of the seat + the session identified by the specified session + identifier belongs to. Note that not all sessions are + attached to a seat, this call will fail for them. The + returned string needs to be freed with the libc + <citerefentry><refentrytitle>free</refentrytitle><manvolnum>3</manvolnum></citerefentry> + call after use.</para> + + <para><function>sd_session_get_service()</function> + may be used to determine the name of the service (as + passed during PAM session setup) that registered the + session identified by the specified session + identifier. The returned string needs to be freed with + the libc + <citerefentry><refentrytitle>free</refentrytitle><manvolnum>3</manvolnum></citerefentry> + call after use.</para> + + <para>If the <literal>session</literal> parameter of + any of these functions is passed as NULL the operation + is executed for the session the calling process is a + member of, if there is any.</para> + </refsect1> + + <refsect1> + <title>Return Value</title> + + <para>If the test succeeds + <function>sd_session_is_active()</function> returns a + positive integer, if it fails 0. On success + <function>sd_session_get_uid()</function>, + <function>sd_session_get_service()</function> and + <function>sd_session_get_seat()</function> return 0 or + a positive integer. On failure, these calls return a + negative errno-style error code.</para> + </refsect1> + + <refsect1> + <title>Notes</title> + + <para>The <function>sd_session_is_active()</function>, + <function>sd_session_get_uid()</function>, + <function>sd_session_get_service()</function> and + <function>sd_session_get_seat()</function> interfaces + are available as shared library, which can be compiled + and linked to with the + <literal>libsystemd-login</literal> + <citerefentry><refentrytitle>pkg-config</refentrytitle><manvolnum>1</manvolnum></citerefentry> + file.</para> + </refsect1> + + <refsect1> + <title>See Also</title> + + <para> + <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>, + <citerefentry><refentrytitle>sd-login</refentrytitle><manvolnum>7</manvolnum></citerefentry>, + <citerefentry><refentrytitle>sd_pid_get_session</refentrytitle><manvolnum>3</manvolnum></citerefentry> + </para> + </refsect1> + +</refentry> diff --git a/man/sd_uid_get_state.xml b/man/sd_uid_get_state.xml new file mode 100644 index 0000000000..67776257db --- /dev/null +++ b/man/sd_uid_get_state.xml @@ -0,0 +1,185 @@ +<?xml version='1.0'?> <!--*-nxml-*--> +<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" + "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"> + +<!-- + 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/>. +--> + +<refentry id="sd_uid_get_state"> + + <refentryinfo> + <title>sd_uid_get_state</title> + <productname>systemd</productname> + + <authorgroup> + <author> + <contrib>Developer</contrib> + <firstname>Lennart</firstname> + <surname>Poettering</surname> + <email>lennart@poettering.net</email> + </author> + </authorgroup> + </refentryinfo> + + <refmeta> + <refentrytitle>sd_uid_get_state</refentrytitle> + <manvolnum>3</manvolnum> + </refmeta> + + <refnamediv> + <refname>sd_uid_get_state</refname> + <refname>sd_uid_is_on_seat</refname> + <refname>sd_uid_get_sessions</refname> + <refname>sd_uid_get_seats</refname> + <refpurpose>Determine login state of a specific Unix user ID</refpurpose> + </refnamediv> + + <refsynopsisdiv> + <funcsynopsis> + <funcsynopsisinfo>#include <systemd/sd-login.h></funcsynopsisinfo> + + <funcprototype> + <funcdef>int <function>sd_uid_get_state</function></funcdef> + <paramdef>uid_t <parameter>pid</parameter></paramdef> + <paramdef>char** <parameter>state</parameter></paramdef> + </funcprototype> + + <funcprototype> + <funcdef>int <function>sd_uid_is_on_seat</function></funcdef> + <paramdef>uid_t <parameter>pid</parameter></paramdef> + <paramdef>int <parameter>require_active</parameter></paramdef> + <paramdef>const char* <parameter>seat</parameter></paramdef> + </funcprototype> + + <funcprototype> + <funcdef>int <function>sd_uid_get_sessions</function></funcdef> + <paramdef>uid_t <parameter>pid</parameter></paramdef> + <paramdef>int <parameter>require_active</parameter></paramdef> + <paramdef>char*** <parameter>sessions</parameter></paramdef> + </funcprototype> + + <funcprototype> + <funcdef>int <function>sd_uid_get_seats</function></funcdef> + <paramdef>uid_t <parameter>pid</parameter></paramdef> + <paramdef>int <parameter>require_active</parameter></paramdef> + <paramdef>char*** <parameter>seats</parameter></paramdef> + </funcprototype> + </funcsynopsis> + </refsynopsisdiv> + + <refsect1> + <title>Description</title> + + <para><function>sd_uid_get_state()</function> may be + used to determine the login state of a specific Unix + user identifier. The following states are currently + known: <literal>offline</literal> (user not logged in + at all), <literal>lingering</literal> (user not logged + in, but some user services running), + <literal>online</literal> (user logged in, but not + active), <literal>active</literal> (user logged in on + an active seat). In the future additional states might + be defined, client code should be written to be robust + in regards to additional state strings being + returned. The returned string needs to be freed with + the libc + <citerefentry><refentrytitle>free</refentrytitle><manvolnum>3</manvolnum></citerefentry> + call after use.</para> + + <para><function>sd_uid_is_on_seat()</function> may be + used to determine whether a specific user is logged in + or active on a specific seat. Accepts a Unix user + identifier and a seat identifier string as + parameters. The <parameter>require_active</parameter> + parameter is a boolean. If non-zero (true) this + function will test if the user is active (i.e. has a + session that is in the foreground and accepting user + input) on the specified seat, otherwise (false) only + if the user is logged in (and possibly inactive) on + the specified seat.</para> + + <para><function>sd_uid_get_sessions()</function> may + be used to determine the current sessions of the + specified user. Acceptes a Unix user identifier as + parameter. The <parameter>require_active</parameter> + boolean parameter controls whether the returned list + shall consist of only those sessions where the user is + currently active (true) or where the user is currently + logged in at all, possibly inactive (false). The call + returns a NULL terminated string array of session + identifiers in <parameter>sessions</parameter> which + needs to be freed by the caller with the libc + <citerefentry><refentrytitle>free</refentrytitle><manvolnum>3</manvolnum></citerefentry> + call after use, including all the strings + referenced. If the string array parameter is passed as + NULL the array will not be filled in, but the return + code still indicates the number of current + sessions. Note that instead of an empty array NULL may + be returned and should be considered equivalent to an + empty array.</para> + + <para>Similar, <function>sd_uid_get_seats()</function> + may be used to determine the list of seats on which + the user currently has sessions. Similar semantics + apply, however note that the user may have + multiple sessions on the same seat as well as sessions + with no attached seat and hence the number of entries + in the returned array may differ from the one returned + by <function>sd_uid_get_sessions()</function>.</para> + </refsect1> + + <refsect1> + <title>Return Value</title> + + <para>On success + <function>sd_uid_get_state()</function> returns 0 or a + positive integer. If the test succeeds + <function>sd_uid_is_on_seat()</function> returns a + positive integer, if it fails + 0. <function>sd_uid_get_sessions()</function> and + <function>sd_uid_get_seats()</function> return the + number of entries in the returned arrays. On failure, + these calls return a negative errno-style error + code.</para> + </refsect1> + + <refsect1> + <title>Notes</title> + + <para>The <function>sd_uid_get_state()</function>, + <function>sd_uid_is_on_seat()</function>, + <function>sd_uid_get_sessions()</function>, and + <function>sd_uid_get_seats()</function> interfaces are + available as shared library, which can be compiled and + linked to with the <literal>libsystemd-login</literal> + <citerefentry><refentrytitle>pkg-config</refentrytitle><manvolnum>1</manvolnum></citerefentry> + file.</para> + </refsect1> + + <refsect1> + <title>See Also</title> + + <para> + <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>, + <citerefentry><refentrytitle>sd-login</refentrytitle><manvolnum>7</manvolnum></citerefentry>, + <citerefentry><refentrytitle>sd_pid_get_owner_uid</refentrytitle><manvolnum>3</manvolnum></citerefentry> + </para> + </refsect1> + +</refentry> diff --git a/man/systemctl.xml b/man/systemctl.xml index 468141c2bd..5adee45163 100644 --- a/man/systemctl.xml +++ b/man/systemctl.xml @@ -198,6 +198,14 @@ </varlistentry> <varlistentry> + <term><option>--no-legend</option></term> + + <listitem><para>Do not print a legend, i.e. + the column headers and the footer with hints. + </para></listitem> + </varlistentry> + + <varlistentry> <term><option>--no-pager</option></term> <listitem><para>Do not pipe output into a @@ -595,6 +603,13 @@ </varlistentry> <varlistentry> + <term><command>list-unit-files</command></term> + + <listitem><para>List installed unit files. + </para></listitem> + </varlistentry> + + <varlistentry> <term><command>enable [NAME...]</command></term> <listitem><para>Enable one or more diff --git a/man/systemd-cgls.xml b/man/systemd-cgls.xml index 8546e39433..1e53147e1b 100644 --- a/man/systemd-cgls.xml +++ b/man/systemd-cgls.xml @@ -78,12 +78,27 @@ <variablelist> <varlistentry> + <term><option>-h</option></term> <term><option>--help</option></term> <listitem><para>Prints a short help text and exits.</para></listitem> </varlistentry> + <varlistentry> + <term><option>--no-pager</option></term> + + <listitem><para>Do not pipe output into a + pager.</para></listitem> + </varlistentry> + + <varlistentry> + <term><option>-k</option></term> + + <listitem><para>Include kernel + threads in output.</para></listitem> + </varlistentry> + </variablelist> </refsect1> @@ -99,7 +114,9 @@ <title>See Also</title> <para> <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>, - <citerefentry><refentrytitle>systemctl</refentrytitle><manvolnum>1</manvolnum></citerefentry> + <citerefentry><refentrytitle>systemctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>, + <citerefentry><refentrytitle>systemd-cgtop</refentrytitle><manvolnum>1</manvolnum></citerefentry>, + <citerefentry><refentrytitle>ps</refentrytitle><manvolnum>1</manvolnum></citerefentry> </para> </refsect1> diff --git a/man/systemd-cgtop.xml b/man/systemd-cgtop.xml new file mode 100644 index 0000000000..2d67ae5ef4 --- /dev/null +++ b/man/systemd-cgtop.xml @@ -0,0 +1,246 @@ +<?xml version='1.0'?> <!--*-nxml-*--> +<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" + "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"> + +<!-- + This file is part of systemd. + + Copyright 2012 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/>. +--> + +<refentry id="systemd-cgtop"> + + <refentryinfo> + <title>systemd-cgtop</title> + <productname>systemd</productname> + + <authorgroup> + <author> + <contrib>Developer</contrib> + <firstname>Lennart</firstname> + <surname>Poettering</surname> + <email>lennart@poettering.net</email> + </author> + </authorgroup> + </refentryinfo> + + <refmeta> + <refentrytitle>systemd-cgtop</refentrytitle> + <manvolnum>1</manvolnum> + </refmeta> + + <refnamediv> + <refname>systemd-cgtop</refname> + <refpurpose>Show top control groups by their resource usage</refpurpose> + </refnamediv> + + <refsynopsisdiv> + <cmdsynopsis> + <command>systemd-cgtop <arg choice="opt" rep="repeat">OPTIONS</arg></command> + </cmdsynopsis> + </refsynopsisdiv> + + <refsect1> + <title>Description</title> + + <para><command>systemd-cgtop</command> shows the top + control groups of the local Linux control group + hierarchy, ordered by their CPU, memory and disk I/O load. The + display is refreshed in regular intervals (by default + every 1s), similar in style to + <citerefentry><refentrytitle>top</refentrytitle><manvolnum>1</manvolnum></citerefentry>.</para> + + <para>Resource usage is only accounted for control + groups in the relevant hierarchy, i.e. CPU usage is + only accounted for control groups in the + <literal>cpuacct</literal> hierarchy, memory usage + only for those in <literal>memory</literal> and disk + I/O usage for those in + <literal>blkio</literal>. <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry> + by default places all services in their own control + group in the <literal>cpuacct</literal> hierarchy, but + not in <literal>memory</literal> nor + <literal>blkio</literal>. If resource monitoring for + these resources is required it is recommended to add + <literal>blkio</literal> and <literal>memory</literal> + to the <varname>DefaultControllers=</varname> setting + in <filename>/etc/systemd/system.conf</filename> (see + <citerefentry><refentrytitle>systemd.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry> + for details). Alternatively, it is possible to enable + resource accounting individually for services, by + making use of the <varname>ControlGroup=</varname> + option in the unit files (See + <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry> + for details).</para> + + <para>To emphasize this: unless + <literal>blkio</literal> and <literal>memory</literal> + are enabled for the services in question with either + of the options suggested above no resource accounting + will be available for system services and the data shown + by <command>systemd-cgtop</command> will be + incomplete.</para> + </refsect1> + + <refsect1> + <title>Options</title> + + <para>The following options are understood:</para> + + <variablelist> + <varlistentry> + <term><option>-h</option></term> + <term><option>--help</option></term> + + <listitem><para>Prints a short help + text and exits.</para></listitem> + </varlistentry> + + <varlistentry> + <term><option>-p</option></term> + + <listitem><para>Order by control group + path name.</para></listitem> + </varlistentry> + + <varlistentry> + <term><option>-t</option></term> + + <listitem><para>Order by number of + tasks in control + group (i.e. threads and processes).</para></listitem> + </varlistentry> + + <varlistentry> + <term><option>-c</option></term> + + <listitem><para>Order by CPU load.</para></listitem> + </varlistentry> + + <varlistentry> + <term><option>-m</option></term> + + <listitem><para>Order by memory usage.</para></listitem> + </varlistentry> + + <varlistentry> + <term><option>-i</option></term> + + <listitem><para>Order by disk I/O load.</para></listitem> + </varlistentry> + + <varlistentry> + <term><option>-d</option></term> + <term><option>--delay=</option></term> + + <listitem><para>Specify refresh delay + in seconds (or if one of + <literal>ms</literal>, + <literal>us</literal>, + <literal>min</literal> is specified as + unit in this time + unit).</para></listitem> + </varlistentry> + + <varlistentry> + <term><option>--depth=</option></term> + + <listitem><para>Maximum control group + tree traversal depth. Specifies how + deep <command>systemd-cgtop</command> + shall traverse the control group + hierarchies. If 0 is specified only + the root group is monitored, for 1 + only the first level of control groups + is monitored, and so on. Defaults to + 2.</para></listitem> + </varlistentry> + + </variablelist> + + </refsect1> + + + <refsect1> + <title>Keys</title> + + <para><command>systemd-cgtop</command> is an + interactive tool and may be controlled via user input + using the following keys:</para> + + <variablelist> + <varlistentry> + <term>h</term> + + <listitem><para>Shows a short help text.</para></listitem> + </varlistentry> + + <varlistentry> + <term>SPACE</term> + + <listitem><para>Immediately refresh output.</para></listitem> + </varlistentry> + + <varlistentry> + <term>q</term> + + <listitem><para>Terminate the program.</para></listitem> + </varlistentry> + + + <varlistentry> + <term>p</term> + <term>t</term> + <term>c</term> + <term>m</term> + <term>i</term> + + <listitem><para>Change ordering of control groups + by path, number of tasks, CPU load, + memory usage resp. IO + load.</para></listitem> + </varlistentry> + + <varlistentry> + <term>+</term> + <term>-</term> + + <listitem><para>Increase, + resp. decrease refresh + delay.</para></listitem> + </varlistentry> + + </variablelist> + </refsect1> + + <refsect1> + <title>Exit status</title> + + <para>On success 0 is returned, a non-zero failure + code otherwise.</para> + </refsect1> + + <refsect1> + <title>See Also</title> + <para> + <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>, + <citerefentry><refentrytitle>systemctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>, + <citerefentry><refentrytitle>systemd-cgls</refentrytitle><manvolnum>1</manvolnum></citerefentry>, + <citerefentry><refentrytitle>top</refentrytitle><manvolnum>1</manvolnum></citerefentry> + </para> + </refsect1> + +</refentry> diff --git a/man/systemd-loginctl.xml b/man/systemd-loginctl.xml index cf1ba89593..6a282769a3 100644 --- a/man/systemd-loginctl.xml +++ b/man/systemd-loginctl.xml @@ -226,8 +226,8 @@ </varlistentry> <varlistentry> - <term><command>lock [ID...]</command></term> - <term><command>unlock [ID...]</command></term> + <term><command>lock-session [ID...]</command></term> + <term><command>unlock-session [ID...]</command></term> <listitem><para>Activates/deactivates the screen lock on one or more diff --git a/man/systemd-nspawn.xml b/man/systemd-nspawn.xml index 03c39fc3de..dbd2ff5a8a 100644 --- a/man/systemd-nspawn.xml +++ b/man/systemd-nspawn.xml @@ -69,11 +69,12 @@ to various kernel interfaces in the container to read-only, such as <filename>/sys</filename>, <filename>/proc/sys</filename> or - <filename>/selinux</filename>. Network interfaces and - the system clock may not be changed from within the - container. Device nodes may not be created. The host - system cannot be rebooted and kernel modules may not - be loaded from within the container.</para> + <filename>/sys/fs/selinux</filename>. Network + interfaces and the system clock may not be changed + from within the container. Device nodes may not be + created. The host system cannot be rebooted and kernel + modules may not be loaded from within the + container.</para> <para>Note that even though these security precautions are taken <command>systemd-nspawn</command> is not @@ -123,6 +124,7 @@ <variablelist> <varlistentry> <term><option>--help</option></term> + <term><option>-h</option></term> <listitem><para>Prints a short help text and exits.</para></listitem> @@ -130,7 +132,7 @@ <varlistentry> <term><option>--directory=</option></term> - <term><option>--D</option></term> + <term><option>-D</option></term> <listitem><para>Directory to use as file system root for the namespace @@ -141,7 +143,7 @@ <varlistentry> <term><option>--user=</option></term> - <term><option>--u</option></term> + <term><option>-u</option></term> <listitem><para>Run the command under specified user, create home @@ -152,6 +154,16 @@ </para></listitem> </varlistentry> + <varlistentry> + <term><option>--private-network</option></term> + + <listitem><para>Turn off networking in + the container. This makes all network + interfaces unavailable in the + container, with the exception of the + loopback device.</para></listitem> + </varlistentry> + </variablelist> </refsect1> @@ -173,7 +185,7 @@ <title>Example 2</title> <programlisting># mock --init -# systemd-nspawn -D /var/lib/mock/fedora-rawhide-x86_64/root/ /bin/systemd systemd.log_level=debug</programlisting> +# systemd-nspawn -D /var/lib/mock/fedora-rawhide-x86_64/root/ /sbin/init systemd.log_level=debug</programlisting> <para>This installs a minimal Fedora distribution into a subdirectory of <filename>/var/lib/mock/</filename> @@ -195,7 +207,7 @@ <para> <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>, <citerefentry><refentrytitle>chroot</refentrytitle><manvolnum>1</manvolnum></citerefentry>, - <citerefentry><refentrytitle>debootstrap</refentrytitle><manvolnum>8</manvolnum></citerefentry> + <citerefentry><refentrytitle>debootstrap</refentrytitle><manvolnum>8</manvolnum></citerefentry>, <citerefentry><refentrytitle>mock</refentrytitle><manvolnum>1</manvolnum></citerefentry> </para> </refsect1> diff --git a/man/systemd-tmpfiles.xml b/man/systemd-tmpfiles.xml index fab8bcab70..bbb80b2f98 100644 --- a/man/systemd-tmpfiles.xml +++ b/man/systemd-tmpfiles.xml @@ -84,7 +84,9 @@ <listitem><para>If this option is passed all files and directories marked with f, F, d, D in the configuration files are - created.</para></listitem> + created. Files and directories marked with z, + Z have their ownership, access mode and security + labels set.</para></listitem> </varlistentry> <varlistentry> @@ -127,7 +129,7 @@ directories are removed and created according to the configuration file:</para> - <programlisting>systemctl-tmpfiles --remove --create</programlisting> + <programlisting>systemd-tmpfiles --remove --create</programlisting> </refsect1> diff --git a/man/systemd.conf.xml b/man/systemd.conf.xml index 8faedda6c2..ba144da8cf 100644 --- a/man/systemd.conf.xml +++ b/man/systemd.conf.xml @@ -81,7 +81,7 @@ <term><varname>ShowStatus=yes</varname></term> <term><varname>SysVConsole=yes</varname></term> <term><varname>CrashChVT=1</varname></term> - <term><varname>DefaultStandardOutput=null</varname></term> + <term><varname>DefaultStandardOutput=journal</varname></term> <term><varname>DefaultStandardError=inherit</varname></term> <listitem><para>Configures various @@ -129,6 +129,26 @@ touch any hierarchies but its own.</para></listitem> </varlistentry> + + <varlistentry> + <term><varname>JoinControllers=cpu,cpuacct</varname></term> + + <listitem><para>Configures controllers + that shall be mounted in a single + hierarchy. By default systemd will + mount all controllers which are + enabled in the kernel in individual + hierachies, with the exception of + those listed in this setting. Takes a + space separated list of comma + separated controller names, in order + to allow multiple joined + hierarchies. Defaults to + 'cpu,cpuacct'. Pass an empty string to + ensure that systemd mounts all + controllers in separate + hierarchies.</para></listitem> + </varlistentry> </variablelist> </refsect1> diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml index b9a37da38e..ac0f89fb85 100644 --- a/man/systemd.exec.xml +++ b/man/systemd.exec.xml @@ -251,7 +251,7 @@ octal notation. See <citerefentry><refentrytitle>umask</refentrytitle><manvolnum>2</manvolnum></citerefentry> for details. Defaults to - 0002.</para></listitem> + 0022.</para></listitem> </varlistentry> <varlistentry> @@ -279,6 +279,11 @@ assignments. Empty lines and lines starting with ; or # will be ignored, which may be used for commenting. The + parser strips leading and + trailing whitespace from the values + of assignments, unless you use + double quotes ("). + The argument passed should be an absolute file name, optionally prefixed with "-", which indicates that if the file @@ -361,8 +366,10 @@ <option>tty</option>, <option>syslog</option>, <option>kmsg</option>, + <option>journal</option>, + <option>syslog+console</option>, <option>kmsg+console</option>, - <option>syslog+console</option> or + <option>journal+console</option> or <option>socket</option>. If set to <option>inherit</option> the file descriptor of standard input is @@ -383,11 +390,21 @@ terminal. <option>syslog</option> connects standard output to the <citerefentry><refentrytitle>syslog</refentrytitle><manvolnum>3</manvolnum></citerefentry> - system logger. <option>kmsg</option> + system syslog + service. <option>kmsg</option> connects it with the kernel log buffer which is accessible via - <citerefentry><refentrytitle>dmesg</refentrytitle><manvolnum>1</manvolnum></citerefentry>. <option>syslog+console</option> - and <option>kmsg+console</option> work + <citerefentry><refentrytitle>dmesg</refentrytitle><manvolnum>1</manvolnum></citerefentry>. <option>journal</option> + connects it with the journal which is + accessible via + <citerefentry><refentrytitle>systemd-journalctl</refentrytitle><manvolnum>1</manvolnum></citerefentry> + (Note that everything that is written + to syslog or kmsg is implicitly stored + in the journal as well, those options + are hence supersets of this + one). <option>syslog+console</option>, + <option>journal+console</option> and + <option>kmsg+console</option> work similarly but copy the output to the system console as well. <option>socket</option> connects @@ -395,8 +412,13 @@ socket activation, semantics are similar to the respective option of <varname>StandardInput=</varname>. - This setting defaults to - <option>inherit</option>.</para></listitem> + This setting defaults to the value set + with + <option>DefaultStandardOutput=</option> + in + <citerefentry><refentrytitle>systemd.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>, + which defaults to + <option>journal</option>.</para></listitem> </varlistentry> <varlistentry> <term><varname>StandardError=</varname></term> @@ -410,7 +432,11 @@ <option>inherit</option> the file descriptor used for standard output is duplicated for standard error. This - setting defaults to + setting defaults to the value set with + <option>DefaultStandardError=</option> + in + <citerefentry><refentrytitle>systemd.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>, + which defaults to <option>inherit</option>.</para></listitem> </varlistentry> <varlistentry> @@ -619,26 +645,19 @@ conjunction with socket-activated services, and stream sockets (TCP) in particular. It has no effect on other - socket types (e.g. datagram/UDP) and on processes - unrelated to socket-based + socket types (e.g. datagram/UDP) and + on processes unrelated to socket-based activation. If the tcpwrap verification fails daemon start-up will fail and the connection is terminated. See <citerefentry><refentrytitle>tcpd</refentrytitle><manvolnum>8</manvolnum></citerefentry> - for details.</para></listitem> - </varlistentry> - - <varlistentry> - <term><varname>ControlGroupModify=</varname></term> - <listitem><para>Takes a boolean - argument. If true, the control groups - created for this unit will be owned by - ther user specified with - <varname>User=</varname> (and the - configured group), and he can create - subgroups as well as add processes to - the group.</para></listitem> + for details. Note that this option may + be used to do access control checks + only. Shell commands and commands + described in + <citerefentry><refentrytitle>hosts_options</refentrytitle><manvolnum>5</manvolnum></citerefentry> + are not supported.</para></listitem> </varlistentry> <varlistentry> @@ -682,8 +701,8 @@ <option>keep-caps-locked</option>, <option>no-setuid-fixup</option>, <option>no-setuid-fixup-locked</option>, - <option>no-setuid-noroot</option> and/or - <option>no-setuid-noroot-locked</option>. + <option>noroot</option> and/or + <option>noroot-locked</option>. </para></listitem> </varlistentry> @@ -718,9 +737,9 @@ where "cpu" identifies the kernel control group controller used, and <filename>/foo/bar</filename> is the - control group path. The controller name - and ":" may be omitted in which case - the named systemd control group + control group path. The controller + name and ":" may be omitted in which + case the named systemd control group hierarchy is implied. Alternatively, the path and ":" may be omitted, in which case the default control group @@ -728,21 +747,217 @@ option may be used to place executed processes in arbitrary groups in arbitrary hierarchies -- which can be - configured externally with additional execution limits. By default - systemd will place all executed - processes in separate per-unit control - groups (named after the unit) in the - systemd named hierarchy. Since every - process can be in one group per - hierarchy only overriding the control group - path in the named systemd hierarchy - will disable automatic placement in - the default group. For details about control - groups see <ulink + configured externally with additional + execution limits. By default systemd + will place all executed processes in + separate per-unit control groups + (named after the unit) in the systemd + named hierarchy. Since every process + can be in one group per hierarchy only + overriding the control group path in + the named systemd hierarchy will + disable automatic placement in the + default group. This option is + primarily intended to place executed + processes in specific paths in + specific kernel controller + hierarchies. It is however not + recommended to manipulate the service + control group path in the systemd + named hierarchy. For details about + control groups see <ulink url="http://www.kernel.org/doc/Documentation/cgroups/cgroups.txt">cgroups.txt</ulink>.</para></listitem> </varlistentry> <varlistentry> + <term><varname>ControlGroupModify=</varname></term> + <listitem><para>Takes a boolean + argument. If true, the control groups + created for this unit will be owned by + the user specified with + <varname>User=</varname> (and the + appropriate group), and he/she can create + subgroups as well as add processes to + the group.</para></listitem> + </varlistentry> + + <varlistentry> + <term><varname>ControlGroupPersistent=</varname></term> + <listitem><para>Takes a boolean + argument. If true, the control groups + created for this unit will be marked + to be persistent, i.e. systemd will + not remove them when stopping the + unit. The default is false, meaning + that the control groups will be + removed when the unit is stopped. For + details about the semantics of this + logic see <ulink + url="http://www.freedesktop.org/wiki/Software/systemd/PaxControlGroups">PaxControlGroups</ulink>.</para></listitem> + </varlistentry> + + <varlistentry> + <term><varname>ControlGroupAttribute=</varname></term> + + <listitem><para>Set a specific control + group attribute for executed + processes, and (if needed) add the the + executed processes to a cgroup in the + hierarchy of the controller the + attribute belongs to. Takes two + space-separated arguments: the + attribute name (syntax is + <literal>cpu.shares</literal> where + <literal>cpu</literal> refers to a + specific controller and + <literal>shares</literal> to the + attribute name), and the attribute + value. Example: + <literal>ControlGroupAttribute=cpu.shares + 512</literal>. If this option is used + for an attribute that belongs to a + kernel controller hierarchy the unit + is not already configured to be added + to (for example via the + <literal>ControlGroup=</literal> + option) then the unit will be added to + the controller and the default unit + cgroup path is implied. Thus, using + <varname>ControlGroupAttribute=</varname> + is in most case sufficient to make use + of control group enforcements, + explicit + <varname>ControlGroup=</varname> are + only necessary in case the implied + default control group path for a + service is not desirable. For details + about control group attributes see + <ulink + url="http://www.kernel.org/doc/Documentation/cgroups/cgroups.txt">cgroups.txt</ulink>. This + option may appear more than once, in + order to set multiple control group + attributes.</para></listitem> + </varlistentry> + + <varlistentry> + <term><varname>CPUShares=</varname></term> + + <listitem><para>Assign the specified + overall CPU time shares to the + processes executed. Takes an integer + value. This controls the + <literal>cpu.shares</literal> control + group attribute, which defaults to + 1024. For details about this control + group attribute see <ulink + url="http://www.kernel.org/doc/Documentation/scheduler/sched-design-CFS.txt">sched-design-CFS.txt</ulink>.</para></listitem> + </varlistentry> + + <varlistentry> + <term><varname>MemoryLimit=</varname></term> + <term><varname>MemorySoftLimit=</varname></term> + + <listitem><para>Limit the overall memory usage + of the executed processes to a certain + size. Takes a memory size in bytes. If + the value is suffixed with K, M, G or + T the specified memory size is parsed + as Kilobytes, Megabytes, Gigabytes, + resp. Terabytes (to the base + 1024). This controls the + <literal>memory.limit_in_bytes</literal> + and + <literal>memory.soft_limit_in_bytes</literal> + control group attributes. For details + about these control group attributes + see <ulink + url="http://www.kernel.org/doc/Documentation/cgroups/memory.txt">memory.txt</ulink>.</para></listitem> + </varlistentry> + + <varlistentry> + <term><varname>DeviceAllow=</varname></term> + <term><varname>DeviceDeny=</varname></term> + + <listitem><para>Control access to + specific device nodes by the executed processes. Takes two + space separated strings: a device node + path (such as + <filename>/dev/null</filename>) + followed by a combination of r, w, m + to control reading, writing resp. + creating of the specific device node + by the unit. This controls the + <literal>devices.allow</literal> + and + <literal>devices.deny</literal> + control group attributes. For details + about these control group attributes + see <ulink + url="http://www.kernel.org/doc/Documentation/cgroups/devices.txt">devices.txt</ulink>.</para></listitem> + </varlistentry> + + <varlistentry> + <term><varname>BlockIOWeight=</varname></term> + + <listitem><para>Set the default or + per-device overall block IO weight + value for the executed + processes. Takes either a single + weight value (between 10 and 1000) to + set the default block IO weight, or a + space separated pair of a file path + and a weight value to specify the + device specific weight value (Example: + "/dev/sda 500"). The file path may be + specified as path to a block device + node or as any other file in which + case the backing block device of the + file system of the file is + determined. This controls the + <literal>blkio.weight</literal> and + <literal>blkio.weight_device</literal> + control group attributes, which + default to 1000. Use this option + multiple times to set weights for + multiple devices. For details about + these control group attributes see + <ulink + url="http://www.kernel.org/doc/Documentation/cgroups/blkio-controller.txt">blkio-controller.txt</ulink>.</para></listitem> + </varlistentry> + + <varlistentry> + <term><varname>BlockIOReadBandwidth=</varname></term> + <term><varname>BlockIOWriteBandwidth=</varname></term> + + <listitem><para>Set the per-device + overall block IO bandwith limit for + the executed processes. Takes a space + separated pair of a file path and a + bandwith value (in bytes per second) + to specify the device specific + bandwidth. The file path may be + specified as path to a block device + node or as any other file in which + case the backing block device of the + file system of the file is determined. + If the bandwith is suffixed with K, M, + G, or T the specified bandwith is + parsed as Kilobytes, Megabytes, + Gigabytes, resp. Terabytes (Example: + "/dev/disk/by-path/pci-0000:00:1f.2-scsi-0:0:0:0 + 5M"). This controls the + <literal>blkio.read_bps_device</literal> + and + <literal>blkio.write_bps_device</literal> + control group attributes. Use this + option multiple times to set bandwith + limits for multiple devices. For + details about these control group + attributes see <ulink + url="http://www.kernel.org/doc/Documentation/cgroups/blkio-controller.txt">blkio-controller.txt</ulink>.</para></listitem> + </varlistentry> + + <varlistentry> <term><varname>ReadWriteDirectories=</varname></term> <term><varname>ReadOnlyDirectories=</varname></term> <term><varname>InaccessibleDirectories=</varname></term> @@ -783,9 +998,9 @@ <term><varname>PrivateTmp=</varname></term> <listitem><para>Takes a boolean - argument. If true sets up a new - namespace for the executed processes - and mounts a private + argument. If true sets up a new file + system namespace for the executed + processes and mounts a private <filename>/tmp</filename> directory inside it, that is not shared by processes outside of the @@ -794,7 +1009,25 @@ process, but makes sharing between processes via <filename>/tmp</filename> - impossible. Defaults to false.</para></listitem> + impossible. Defaults to + false.</para></listitem> + </varlistentry> + + <varlistentry> + <term><varname>PrivateNetwork=</varname></term> + + <listitem><para>Takes a boolean + argument. If true sets up a new + network namespace for the executed + processes and configures only the + loopback network device + <literal>lo</literal> inside it. No + other network devices will be + available to the executed process. + This is useful to securely turn off + network access by the executed + process. Defaults to + false.</para></listitem> </varlistentry> <varlistentry> @@ -842,6 +1075,17 @@ this service.</para></listitem> </varlistentry> + <varlistentry> + <term><varname>IgnoreSIGPIPE=</varname></term> + + <listitem><para>Takes a boolean + argument. If true causes SIGPIPE to be + ignored in the executed + process. Defaults to true, since + SIGPIPE generally is useful only in + shell pipelines.</para></listitem> + </varlistentry> + </variablelist> </refsect1> diff --git a/man/systemd.mount.xml b/man/systemd.mount.xml index d397c896da..8f1cc514cf 100644 --- a/man/systemd.mount.xml +++ b/man/systemd.mount.xml @@ -99,7 +99,7 @@ </refsect1> <refsect1> - <title><filename>fstab</filename></title> + <title><filename>/etc/fstab</filename></title> <para>Mount units may either be configured via unit files, or via <filename>/etc/fstab</filename> (see @@ -110,16 +110,27 @@ few special mount options are understood by systemd which influence how dependencies are created for mount points from <filename>/etc/fstab</filename>. If - <option>comment=systemd.mount</option> is specified as - mount option, then systemd will create a dependency of - type <option>Wants</option> from either + <option>MountAuto=yes</option> is set in + <filename>system.conf</filename> (which is the + default), or if <option>x-systemd.mount</option> is + specified as mount option, then systemd will create a + dependency of type <option>Wants</option> from either <filename>local-fs.target</filename> or <filename>remote-fs.target</filename>, depending whether the file system is local or remote. If - <option>comment=systemd.automount</option> is set, an - automount unit will be created for the file system. See + <option>x-systemd.automount</option> is set, an + automount unit will be created for the file + system. See <citerefentry><refentrytitle>systemd.automount</refentrytitle><manvolnum>5</manvolnum></citerefentry> - for details.</para> + for details. If + <option>x-systemd-device-timeout=</option> is + specified it may be used to configure how long systemd + should wait for a device to show up before giving up + on an entry from + <filename>/etc/fstab</filename>. Specify a time in + seconds or explicitly specifiy a unit as + <literal>s</literal>, <literal>min</literal>, + <literal>h</literal>, <literal>ms</literal>.</para> <para>If a mount point is configured in both <filename>/etc/fstab</filename> and a unit file, the diff --git a/man/systemd.path.xml b/man/systemd.path.xml index f99931ab1e..5b1ff75f7a 100644 --- a/man/systemd.path.xml +++ b/man/systemd.path.xml @@ -113,6 +113,7 @@ <term><varname>PathExists=</varname></term> <term><varname>PathExistsGlob=</varname></term> <term><varname>PathChanged=</varname></term> + <term><varname>PathModified=</varname></term> <term><varname>DirectoryNotEmpty=</varname></term> <listitem><para>Defines paths to @@ -129,8 +130,14 @@ specified. <varname>PathChanged=</varname> may be used to watch a file or directory and activate the configured - unit whenever it changes or is - modified. <varname>DirectoryNotEmpty=</varname> + unit whenever it changes. It is not activated + on every write to the watched file but it is + activated if the file which was open for writing + gets closed. <varname>PathModified=</varname> + is similar, but additionally it is activated + also on simple writes to the watched file. + + <varname>DirectoryNotEmpty=</varname> may be used to watch a directory and activate the configured unit whenever it contains at least one file.</para> diff --git a/man/systemd.service.xml b/man/systemd.service.xml index 4f1102021f..837a992ba4 100644 --- a/man/systemd.service.xml +++ b/man/systemd.service.xml @@ -198,8 +198,8 @@ below) should be set to open access to the notification socket provided by systemd. If - <varname>NotifyAccess=</varname> is not - set, it will implicitly be set to + <varname>NotifyAccess=</varname> is + not set, it will be implicitly set to <option>main</option>.</para> </listitem> </varlistentry> @@ -311,20 +311,28 @@ main process of the daemon. The command line accepts % specifiers as described in - <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>. On - top of that basic environment variable - substitution is supported, where - <literal>${FOO}</literal> is replaced - by the string value of the environment - variable of the same name. Also - <literal>$FOO</literal> may appear as - separate word on the command line in - which case the variable is replaced by - its value split at whitespaces. Note - that the first argument (i.e. the - binary to execute) may not be a - variable, and must be a literal and - absolute path name.</para></listitem> + <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para> + + <para>On top of that basic environment + variable substitution is + supported. Use + <literal>${FOO}</literal> as part of a + word, or as word of its own on the + command line, in which case it will be + replaced by the value of the + environment variable including all + whitespace it contains, resulting in a + single argument. Use + <literal>$FOO</literal> as a separate + word on the command line, in which + case it will be replaced by the value + of the environment variable split up + at whitespace, resulting in no or more + arguments. Note that the first + argument (i.e. the program to execute) + may not be a variable, and must be a + literal and absolute path + name.</para></listitem> </varlistentry> <varlistentry> @@ -452,6 +460,38 @@ </varlistentry> <varlistentry> + <term><varname>WatchdogSec=</varname></term> + <listitem><para>Configures the + watchdog timeout for a service. This + is activated when the start-up is + completed. The service must call + <citerefentry><refentrytitle>sd_notify</refentrytitle><manvolnum>3</manvolnum></citerefentry> + regularly with "WATCHDOG=1". If the + time between two such calls is larger + than the configured time then the + service is placed in a failure + state. By setting + <varname>Restart=</varname> + to <option>on-failure</option> or + <option>always</option> the service + will be automatically restarted. The + time configured here will be passed to + the executed service process in the + <varname>WATCHDOG_USEC=</varname> + environment variable. If + this option is used + <varname>NotifyAccess=</varname> (see + below) should be set to open access to + the notification socket provided by + systemd. If + <varname>NotifyAccess=</varname> is not + set, it will be implicitly set to + <option>main</option>. Defaults to 0, + which disables this + feature.</para></listitem> + </varlistentry> + + <varlistentry> <term><varname>Restart=</varname></term> <listitem><para>Configures whether the main service process shall be @@ -470,16 +510,19 @@ 0. If set to <option>on-failure</option> it will be restarted only when it exited with an - exit code not equalling 0, or when - terminated by a signal. If set to + exit code not equalling 0, when + terminated by a signal, when an + operation times out or when the + configured watchdog timeout is + triggered. If set to <option>on-abort</option> it will be restarted only if it exits due to reception of an uncaught signal. If set to <option>always</option> the service will be restarted regardless - whether it exited cleanly or not, or + whether it exited cleanly or not, got terminated abnormally by a - signal.</para></listitem> + signal or hit a timeout.</para></listitem> </varlistentry> <varlistentry> @@ -652,10 +695,16 @@ accepted. If <option>all</option> all services updates from all members of the service's control group are - accepted. This option must be set to + accepted. This option should be set to open access to the notification socket when using - <varname>Type=notify</varname> (see above).</para></listitem> + <varname>Type=notify</varname> or + <varname>WatchdogUsec=</varname> (see + above). If those options are used but + <varname>NotifyAccess=</varname> not + configured it will be implicitly set + to + <option>main</option>.</para></listitem> </varlistentry> <varlistentry> @@ -663,11 +712,11 @@ <listitem><para>Specifies the name of the socket units this service shall inherit the sockets from when the - service (ignoring the different suffix - of course) is started. Normally it + service is started. Normally it should not be necessary to use this setting as all sockets whose unit shares the same name as the service + (ignoring the different suffix of course) are passed to the spawned process.</para> @@ -712,6 +761,66 @@ for details.</para></listitem> </varlistentry> + <varlistentry> + <term><varname>StartLimitInterval=</varname></term> + <term><varname>StartLimitBurst=</varname></term> + + <listitem><para>Configure service + start rate limiting. By default + services which are started more often + than 5 times within 10s are not + permitted to start any more times + until the 10s interval ends. With + these two options this rate limiting + may be modified. Use + <varname>StartLimitInterval=</varname> + to configure the checking interval + (defaults to 10s, set to 0 to disable + any kind of rate limiting). Use + <varname>StartLimitBurst=</varname> to + configure how many starts per interval + are allowed (defaults to 5). These + configuration options are particularly + useful in conjunction with + <varname>Restart=</varname>.</para></listitem> + </varlistentry> + + <varlistentry> + <term><varname>StartLimitAction=</varname></term> + + <listitem><para>Configure the action + to take if the rate limit configured + with + <varname>StartLimitInterval=</varname> + and + <varname>StartLimitBurst=</varname> is + hit. Takes one of + <option>none</option>, + <option>reboot</option>, + <option>reboot-force</option> or + <option>reboot-immediate</option>. If + <option>none</option> is set, + hitting the rate limit will trigger no + action besides that the start will not + be + permitted. <option>reboot</option> + causes a reboot following the normal + shutdown procedure (i.e. equivalent to + <command>systemctl reboot</command>), + <option>reboot-force</option> causes + an forced reboot which will terminate + all processes forcibly but should + cause no dirty file systems on reboot + (i.e. equivalent to <command>systemctl + reboot -f</command>) and + <option>reboot-immediate</option> + causes immediate execution of the + <citerefentry><refentrytitle>reboot</refentrytitle><manvolnum>2</manvolnum></citerefentry> + system call, which might result in + data loss. Defaults to + <option>none</option>.</para></listitem> + </varlistentry> + </variablelist> </refsect1> diff --git a/man/systemd.socket.xml b/man/systemd.socket.xml index 28c8dc4566..ef5b28c771 100644 --- a/man/systemd.socket.xml +++ b/man/systemd.socket.xml @@ -156,7 +156,7 @@ family.</para> <para>If the address starts with an - ampersand (@) it is read as abstract + at symbol (@) it is read as abstract namespace socket in the AF_UNIX family. The @ is replaced with a NUL character before binding. For details @@ -510,7 +510,7 @@ <term><varname>Transparent=</varname></term> <listitem><para>Takes a boolean value. Controls the IP_TRANSPARENT - option. Defaults to + socket option. Defaults to <option>false</option>.</para></listitem> </varlistentry> @@ -518,13 +518,24 @@ <term><varname>Broadcast=</varname></term> <listitem><para>Takes a boolean value. This controls the SO_BROADCAST - option, which allows broadcast + socket option, which allows broadcast datagrams to be sent from this socket. Defaults to <option>false</option>.</para></listitem> </varlistentry> <varlistentry> + <term><varname>PassCredentials=</varname></term> + <listitem><para>Takes a boolean + value. This controls the SO_PASSCRED + socket option, which allows UNIX sockets to + receive the credentials of the sending + process in an ancillary message. + Defaults to + <option>false</option>.</para></listitem> + </varlistentry> + + <varlistentry> <term><varname>TCPCongestion=</varname></term> <listitem><para>Takes a string value. Controls the TCP congestion diff --git a/man/systemd.special.xml.in b/man/systemd.special.xml.in index ecc9ddee6a..116a43ccfb 100644 --- a/man/systemd.special.xml.in +++ b/man/systemd.special.xml.in @@ -59,6 +59,7 @@ <filename>halt.target</filename>, <filename>kbrequest.target</filename>, <filename>local-fs.target</filename>, + <filename>local-fs-pre.target</filename>, <filename>mail-transfer-agent.target</filename>, <filename>multi-user.target</filename>, <filename>network.target</filename>, @@ -66,9 +67,9 @@ <filename>poweroff.target</filename>, <filename>reboot.target</filename>, <filename>remote-fs.target</filename>, + <filename>remote-fs-pre.target</filename>, <filename>rescue.target</filename>, <filename>rpcbind.target</filename>, - <filename>time-sync.target</filename>, <filename>runlevel2.target</filename>, <filename>runlevel3.target</filename>, <filename>runlevel4.target</filename>, @@ -81,8 +82,9 @@ <filename>syslog.target</filename>, <filename>systemd-initctl.service</filename>, <filename>systemd-initctl.socket</filename>, - <filename>systemd-logger.service</filename>, - <filename>systemd-logger.socket</filename>, + <filename>systemd-stdout-syslog-bridge.service</filename>, + <filename>systemd-stdout-syslog-bridge.socket</filename>, + <filename>time-sync.target</filename>, <filename>umount.target</filename></para> </refsynopsisdiv> @@ -261,6 +263,18 @@ </listitem> </varlistentry> <varlistentry> + <term><filename>local-fs-pre.target</filename></term> + <listitem> + <para>This target unit is + automatically ordered before + all local mount points marked + with <option>auto</option> + (see above). It can be used to + execute certain units before + all local mounts.</para> + </listitem> + </varlistentry> + <varlistentry> <term><filename>mail-transfer-agent.target</filename></term> <listitem> <para>The mail transfer agent @@ -374,6 +388,18 @@ </listitem> </varlistentry> <varlistentry> + <term><filename>remote-fs-pre.target</filename></term> + <listitem> + <para>This target unit is + automatically ordered before + all remote mount points marked + with <option>auto</option> + (see above). It can be used to + execute certain units before + all remote mounts.</para> + </listitem> + </varlistentry> + <varlistentry> <term><filename>rescue.target</filename></term> <listitem> <para>A special target unit @@ -399,19 +425,6 @@ </listitem> </varlistentry> <varlistentry> - <term><filename>time-sync.target</filename></term> - <listitem> - <para>systemd automatically - adds dependencies of type - After for this target unit to - all SysV init script service - units with an LSB header - referring to the - <literal>$time</literal> - facility.</para> - </listitem> - </varlistentry> - <varlistentry> <term><filename>runlevel2.target</filename></term> <listitem> <para>This is a target that is @@ -576,7 +589,7 @@ </listitem> </varlistentry> <varlistentry> - <term><filename>systemd-logger.service</filename></term> + <term><filename>systemd-stdout-syslog-bridge.service</filename></term> <listitem> <para>This is internally used by systemd to provide syslog @@ -584,15 +597,15 @@ maintains.</para> <para>This is a socket-activated service, see - <filename>system-logger.socket</filename>.</para> + <filename>system-stdout-syslog-bridge.socket</filename>.</para> </listitem> </varlistentry> <varlistentry> - <term><filename>systemd-logger.socket</filename></term> + <term><filename>systemd-stdout-syslog-bridge.socket</filename></term> <listitem> <para>Socket activation unit for - <filename>system-logger.service</filename>. systemd + <filename>system-stdout-syslog-bridge.service</filename>. systemd will automatically add dependencies of types Requires and After to all units that @@ -623,6 +636,19 @@ </listitem> </varlistentry> <varlistentry> + <term><filename>time-sync.target</filename></term> + <listitem> + <para>systemd automatically + adds dependencies of type + After for this target unit to + all SysV init script service + units with an LSB header + referring to the + <literal>$time</literal> + facility.</para> + </listitem> + </varlistentry> + <varlistentry> <term><filename>umount.target</filename></term> <listitem> <para>A special target unit diff --git a/man/systemd.unit.xml b/man/systemd.unit.xml index d38a001366..eecff7373a 100644 --- a/man/systemd.unit.xml +++ b/man/systemd.unit.xml @@ -121,8 +121,9 @@ <para>If a line starts with <option>.include</option> followed by a file name, the specified file will be - read as if its contents were listed in place of the - <option>.include</option> directive.</para> + parsed at this point. Make sure that the file that is + included has the appropiate section headers before + any directives.</para> <para>Along with a unit file <filename>foo.service</filename> a directory @@ -180,30 +181,86 @@ and no file by that name is found, systemd will look for <filename>getty@.service</filename> and instantiate a service from that configuration file if - it is found. To refer to the instance string from + it is found.</para> + + <para>To refer to the instance string from within the configuration file you may use the special <literal>%i</literal> specifier in many of the - configuration options. Other specifiers that may be - used are <literal>%n</literal>, <literal>%N</literal>, - <literal>%p</literal>, <literal>%P</literal>, - <literal>%I</literal>, <literal>%f</literal>, - <literal>%c</literal>, <literal>%r</literal>, - <literal>%R</literal> and <literal>%t</literal> for - the full unit name, the unescaped unit name, the - prefix name, the unescaped prefix name, the unescaped - instance name, the unescaped filename, the control - group path of the unit, the root control group path of - systemd, and the parent directory of the root control - cgroup path of systemd and the runtime socket dir, - respectively. The unescaped filename is either the - unescaped instance name (if set) with / prepended (if - necessary), or the prefix name similarly prepended - with /. The prefix name here refers to the string - before the @, i.e. "getty" in the example above, where - "tty3" is the instance name. The runtime socket - directory is either <filename>/run</filename> (for the - system manager) or <literal>$XDG_RUNTIME_DIR</literal> - (for user managers).</para> + configuration options. Other specifiers exist, the + full list is:</para> + + <table> + <title>Specifiers available in unit files</title> + <tgroup cols='3' align='left' colsep='1' rowsep='1'> + <colspec colname="spec" /> + <colspec colname="mean" /> + <colspec colname="detail" /> + <thead> + <row> + <entry>Specifier</entry> + <entry>Meaning</entry> + <entry>Details</entry> + </row> + </thead> + <tbody> + <row> + <entry><literal>%n</literal></entry> + <entry>Full unit name</entry> + <entry></entry> + </row> + <row> + <entry><literal>%N</literal></entry> + <entry>Unescaped full unit name</entry> + <entry></entry> + </row> + <row> + <entry><literal>%p</literal></entry> + <entry>Prefix name</entry> + <entry>This refers to the string before the @, i.e. "getty" in the example above, where "tty3" is the instance name.</entry> + </row> + <row> + <entry><literal>%P</literal></entry> + <entry>Unescaped prefix name</entry> + <entry></entry> + </row> + <row> + <entry><literal>%i</literal></entry> + <entry>Instance name</entry> + <entry>This is the string between the @ character and the suffix.</entry> + </row> + <row> + <entry><literal>%I</literal></entry> + <entry>Unescaped instance name</entry> + <entry></entry> + </row> + <row> + <entry><literal>%f</literal></entry> + <entry>Unescaped file name</entry> + <entry>This is either the unescaped instance name (if set) with / prepended (if necessary), or the prefix name similarly prepended with /.</entry> + </row> + <row> + <entry><literal>%c</literal></entry> + <entry>Control group path of the unit</entry> + <entry></entry> + </row> + <row> + <entry><literal>%r</literal></entry> + <entry>Root control group path of systemd</entry> + <entry></entry> + </row> + <row> + <entry><literal>%R</literal></entry> + <entry>Parent directory of the root control group path of systemd</entry> + <entry></entry> + </row> + <row> + <entry><literal>%t</literal></entry> + <entry>Runtime socket dir</entry> + <entry>This is either /run (for the system manager) or $XDG_RUNTIME_DIR (for user managers).</entry> + </row> + </tbody> + </tgroup> + </table> <para>If a unit file is empty (i.e. has the file size 0) or is symlinked to <filename>/dev/null</filename> @@ -453,6 +510,22 @@ </varlistentry> <varlistentry> + <term><varname>PropagateReloadTo=</varname></term> + <term><varname>PropagateReloadFrom=</varname></term> + + <listitem><para>Lists one or more + units where reload requests on the + unit will be propagated to/on the + other unit will be propagated + from. Issuing a reload request on a + unit will automatically also enqueue a + reload request on all units that the + reload request shall be propagated to + via these two + settings.</para></listitem> + </varlistentry> + + <varlistentry> <term><varname>OnFailureIsolate=</varname></term> <listitem><para>Takes a boolean @@ -609,11 +682,14 @@ <term><varname>ConditionPathExists=</varname></term> <term><varname>ConditionPathExistsGlob=</varname></term> <term><varname>ConditionPathIsDirectory=</varname></term> + <term><varname>ConditionPathIsSymbolicLink=</varname></term> + <term><varname>ConditionPathIsMountPoint=</varname></term> <term><varname>ConditionDirectoryNotEmpty=</varname></term> <term><varname>ConditionFileIsExecutable=</varname></term> <term><varname>ConditionKernelCommandLine=</varname></term> <term><varname>ConditionVirtualization=</varname></term> <term><varname>ConditionSecurity=</varname></term> + <term><varname>ConditionCapability=</varname></term> <term><varname>ConditionNull=</varname></term> <listitem><para>Before starting a unit @@ -623,7 +699,7 @@ a file existence condition can be checked before a unit is started. If the specified absolute path name does - not exist startup of a unit will not + not exist, startup of a unit will not actually happen, however the unit is still useful for ordering purposes in this case. The condition is checked at @@ -633,10 +709,11 @@ <varname>ConditionPathExists=</varname> is prefixed with an exclamation mark (!), the test is negated, and the unit - only started if the path does not - exist. <varname>ConditionPathExistsGlob=</varname> - work in a similar way, but checks for - the existance of at least one file or + is only started if the path does not + exist. + <varname>ConditionPathExistsGlob=</varname> + works in a similar way, but checks for + the existence of at least one file or directory matching the specified globbing pattern. <varname>ConditionPathIsDirectory=</varname> @@ -644,7 +721,17 @@ <varname>ConditionPathExists=</varname> but verifies whether a certain path exists and is a - directory. <varname>ConditionFileIsExecutable=</varname> + directory. <varname>ConditionPathIsSymbolicLink=</varname> + is similar to + <varname>ConditionPathExists=</varname> + but verifies whether a certain path + exists and is a symbolic + link. <varname>ConditionPathIsMountPoint=</varname> + is similar to + <varname>ConditionPathExists=</varname> + but verifies whether a certain path + exists and is a mount + point. <varname>ConditionFileIsExecutable=</varname> is similar to <varname>ConditionPathExists=</varname> but verifies whether a certain path @@ -677,25 +764,48 @@ whether it is a specific implementation. Takes either boolean value to check if being executed in - any virtual environment or one of the + any virtualized environment, or one of + <varname>vm</varname> and + <varname>container</varname> to test + against a specific type of + virtualization solution, or one of <varname>qemu</varname>, <varname>kvm</varname>, <varname>vmware</varname>, <varname>microsoft</varname>, <varname>oracle</varname>, <varname>xen</varname>, - <varname>pidns</varname>, - <varname>openvz</varname> to test - against a specific implementation. The - test may be negated by prepending an - exclamation mark. + <varname>bochs</varname>, + <varname>chroot</varname>, + <varname>openvz</varname>, + <varname>lxc</varname>, + <varname>lxc-libvirt</varname>, + <varname>systemd-nspawn</varname>, + <varname>pidns</varname> to test + against a specific implementation. If + multiple virtualization technologies + are nested only the innermost is + considered. The test may be negated by + prepending an exclamation mark. <varname>ConditionSecurity=</varname> may be used to check whether the given security module is enabled on the system. Currently the only recognized value is <varname>selinux</varname>. The test may be negated by prepending - an exclamation mark. Finally, + an exclamation + mark. <varname>ConditionCapability=</varname> + may be used to check whether the given + capability exists in the capability + bounding set of the service manager + (i.e. this does not check whether + capability is actually available in + the permitted or effective sets, see + <citerefentry><refentrytitle>capabilities</refentrytitle><manvolnum>7</manvolnum></citerefentry> + for details). Pass a capability name + such as <literal>CAP_MKNOD</literal>, + possibly prefixed with an exclamation + mark to negate the check. Finally, <varname>ConditionNull=</varname> may be used to add a constant condition check value to the unit. It takes a @@ -717,7 +827,10 @@ prefix an argument with the pipe symbol and an exclamation mark the pipe symbol must be passed first, the - exclamation second.</para></listitem> + exclamation second. Except for + <varname>ConditionPathIsSymbolicLink=</varname>, + all path checks follow + symlinks.</para></listitem> </varlistentry> <varlistentry> @@ -849,7 +962,8 @@ <citerefentry><refentrytitle>systemd.target</refentrytitle><manvolnum>5</manvolnum></citerefentry>, <citerefentry><refentrytitle>systemd.path</refentrytitle><manvolnum>5</manvolnum></citerefentry>, <citerefentry><refentrytitle>systemd.timer</refentrytitle><manvolnum>5</manvolnum></citerefentry>, - <citerefentry><refentrytitle>systemd.snapshot</refentrytitle><manvolnum>5</manvolnum></citerefentry> + <citerefentry><refentrytitle>systemd.snapshot</refentrytitle><manvolnum>5</manvolnum></citerefentry>, + <citerefentry><refentrytitle>capabilities</refentrytitle><manvolnum>7</manvolnum></citerefentry> </para> </refsect1> diff --git a/man/systemd.xml b/man/systemd.xml index fc4810767a..f0bc552268 100644 --- a/man/systemd.xml +++ b/man/systemd.xml @@ -209,8 +209,10 @@ <listitem><para>Set log target. Argument must be one of <option>console</option>, + <option>journal</option>, <option>syslog</option>, <option>kmsg</option>, + <option>journal-or-kmsg</option>, <option>syslog-or-kmsg</option>, <option>null</option>.</para></listitem> </varlistentry> @@ -259,18 +261,25 @@ services and sockets, i.e. controls the default for <option>StandardOutput=</option> - resp. <option>StandardExecute=</option> + resp. <option>StandardError=</option> (see <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry> for details). Takes one of <option>inherit</option>, <option>null</option>, <option>tty</option>, + <option>journal</option>, + <option>journal+console</option>, <option>syslog</option>, <option>syslog+console</option>, <option>kmsg</option>, <option>kmsg+console</option>. If the - argument is omitted it defaults to + argument is omitted + <option>--default-standard-output=</option> + defaults to <option>journal</option> + and + <option>--default-standard-error=</option> + to <option>inherit</option>.</para></listitem> </varlistentry> </variablelist> @@ -808,7 +817,7 @@ <listitem><para>Sets the log level to <literal>debug</literal> (resp. <literal>info</literal> on - <literal>SIGRTMIN+32</literal>), as + <literal>SIGRTMIN+23</literal>), as controlled via <varname>systemd.log_level=debug</varname> (resp. <varname>systemd.log_level=info</varname> @@ -818,19 +827,24 @@ </varlistentry> <varlistentry> + <term>SIGRTMIN+26</term> <term>SIGRTMIN+27</term> <term>SIGRTMIN+28</term> <term>SIGRTMIN+29</term> <listitem><para>Sets the log level to - <literal>console</literal> - (resp. <literal>kmsg</literal> on + <literal>journal-or-kmsg</literal> + (resp. <literal>console</literal> on + <literal>SIGRTMIN+27</literal>; + resp. <literal>kmsg</literal> on <literal>SIGRTMIN+28</literal>; resp. <literal>syslog-or-kmsg</literal> on <literal>SIGRTMIN+29</literal>), as controlled via - <varname>systemd.log_target=console</varname> - (resp. <varname>systemd.log_target=kmsg</varname> + <varname>systemd.log_target=journal-or-kmsg</varname> + (resp. <varname>systemd.log_target=console</varname> + on <literal>SIGRTMIN+27</literal>; + resp. <varname>systemd.log_target=kmsg</varname> on <literal>SIGRTMIN+28</literal>; resp <varname>systemd.log_target=syslog-or-kmsg</varname> @@ -1054,6 +1068,22 @@ above.</para></listitem> </varlistentry> + <varlistentry> + <term><varname>systemd.setenv=</varname></term> + + <listitem><para>Takes a string + argument in the form + VARIABLE=VALUE. May be used to set + environment variables for the init + process and all its children at boot + time. May be used more than once to + set multiple variables. If the equal + sign and variable are missing unsets + an environment variable which might be + passed in from the initial ram + disk.</para></listitem> + </varlistentry> + </variablelist> </refsect1> @@ -1074,19 +1104,6 @@ </varlistentry> <varlistentry> - <term><filename>/run/systemd/logger</filename></term> - - <listitem><para>Used internally by the - <filename>systemd-logger.service</filename> - unit to connect STDOUT and/or STDERR - of spawned processes to - <citerefentry><refentrytitle>syslog</refentrytitle><manvolnum>3</manvolnum></citerefentry> - or the kernel log buffer. This is an - AF_UNIX stream - socket.</para></listitem> - </varlistentry> - - <varlistentry> <term><filename>/run/systemd/shutdownd</filename></term> <listitem><para>Used internally by the diff --git a/man/timezone.xml b/man/timezone.xml new file mode 100644 index 0000000000..4e33279158 --- /dev/null +++ b/man/timezone.xml @@ -0,0 +1,90 @@ +<?xml version='1.0'?> <!--*-nxml-*--> +<?xml-stylesheet type="text/xsl" href="http://docbook.sourceforge.net/release/xsl/current/xhtml/docbook.xsl"?> +<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" + "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"> + +<!-- + 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/>. +--> + +<refentry id="timezone"> + <refentryinfo> + <title>/etc/timezone</title> + <productname>systemd</productname> + + <authorgroup> + <author> + <contrib>Developer</contrib> + <firstname>Lennart</firstname> + <surname>Poettering</surname> + <email>lennart@poettering.net</email> + </author> + </authorgroup> + </refentryinfo> + + <refmeta> + <refentrytitle>timezone</refentrytitle> + <manvolnum>5</manvolnum> + </refmeta> + + <refnamediv> + <refname>timezone</refname> + <refpurpose>Local time zone configuration file</refpurpose> + </refnamediv> + + <refsynopsisdiv> + <para><filename>/etc/timezone</filename></para> + </refsynopsisdiv> + + <refsect1> + <title>Description</title> + + <para>The <filename>/etc/timezone</filename> file + configures the system-wide time zone of the local + system that is used by applications for presentation + to the user. It should contain a single + newline-terminated line consisting of a time zone + identifier such as + <literal>Europe/Berlin</literal>. The file + <filename>/etc/localtime</filename> corresponds with + <filename>/etc/timezone</filename> and contains the + binary time zone data for the time zone. These files + should always be changed simultaneously and kept in + sync.</para> + + <para>The time zone may be overridden for individual + programs by using the TZ environment variable. See + <citerefentry><refentrytitle>environ</refentrytitle><manvolnum>7</manvolnum></citerefentry>.</para> + </refsect1> + + <refsect1> + <title>History</title> + + <para>The simple configuration file format of + <filename>/etc/timezone</filename> originates from + Debian GNU/Linux.</para> + </refsect1> + + <refsect1> + <title>See Also</title> + <para> + <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry> + </para> + </refsect1> + +</refentry> diff --git a/man/tmpfiles.d.xml b/man/tmpfiles.d.xml index 7f4c45c960..25a7c9ba48 100644 --- a/man/tmpfiles.d.xml +++ b/man/tmpfiles.d.xml @@ -67,39 +67,48 @@ <title>Configuration Format</title> <para>Each configuration file is named in the style of - <filename><program>.conf</filename>. - Files in <filename>/etc/</filename> overwrite - files with the same name in <filename>/usr/lib/</filename>. - Files in <filename>/run</filename> overwrite files with - the same name in <filename>/etc/</filename> and - <filename>/usr/lib/</filename>. Packages should install their - configuration files in <filename>/usr/lib/</filename>, files - in <filename>/etc/</filename> are reserved for the local - administration, which possibly decides to overwrite the - configurations installed from packages. All files are sorted - by filename in alphabetical order, regardless in which of the - directories they reside, to ensure that a specific - configuration file takes precedence over another file with - an alphabetically later name.</para> + <filename><program>.conf</filename>. Files in + <filename>/etc/</filename> override files with the + same name in <filename>/usr/lib/</filename>. Files in + <filename>/run</filename> override files with the same + name in <filename>/etc/</filename> and + <filename>/usr/lib/</filename>. Packages should + install their configuration files in + <filename>/usr/lib/</filename>, files in + <filename>/etc/</filename> are reserved for the local + administrator, who may choose to override the + configurations installed from packages. The list of + configuration files are sorted by their filename in + alphabetical order, regardless in which of the + directories they reside, to guarantee that a + configuration file takes precedence over another + configuration file with an alphabetically later + name.</para> <para>The configuration format is one line per path - containing action, mode, ownership and age + containing action, path, mode, ownership, age and argument fields:</para> - <programlisting>Type Path Mode UID GID Age -d /run/user 0755 root root 10d</programlisting> + <programlisting>Type Path Mode UID GID Age Argument +d /run/user 0755 root root 10d - +L /tmp/foobar - - - - /dev/null</programlisting> <refsect2> <title>Type</title> <variablelist> <varlistentry> <term><varname>f</varname></term> - <listitem><para>Create a file if it doesn't exist yet</para></listitem> + <listitem><para>Create a file if it doesn't exist yet (optionally writing a short string into it, if the argument parameter is passed)</para></listitem> </varlistentry> <varlistentry> <term><varname>F</varname></term> - <listitem><para>Create or truncate a file</para></listitem> + <listitem><para>Create or truncate a file (optionally writing a short string into it, if the argument parameter is passed)</para></listitem> + </varlistentry> + + <varlistentry> + <term><varname>w</varname></term> + <listitem><para>Write the argument parameter to a file, if it exists.</para></listitem> </varlistentry> <varlistentry> @@ -118,6 +127,21 @@ d /run/user 0755 root root 10d</programlisting> </varlistentry> <varlistentry> + <term><varname>L</varname></term> + <listitem><para>Create a symlink if it doesn't exist yet</para></listitem> + </varlistentry> + + <varlistentry> + <term><varname>c</varname></term> + <listitem><para>Create a character device node if it doesn't exist yet</para></listitem> + </varlistentry> + + <varlistentry> + <term><varname>b</varname></term> + <listitem><para>Create a block device node if it doesn't exist yet</para></listitem> + </varlistentry> + + <varlistentry> <term><varname>x</varname></term> <listitem><para>Ignore a path during cleaning. Use this type @@ -155,6 +179,27 @@ d /run/user 0755 root root 10d</programlisting> place of normal path names.</para></listitem> </varlistentry> + + <varlistentry> + <term><varname>z</varname></term> + <listitem><para>Set ownership, access + mode and relabel security context of + a file or directory if it exists. + Lines of this type accept shell-style + globs in place of normal path names. + </para></listitem> + </varlistentry> + + <varlistentry> + <term><varname>Z</varname></term> + <listitem><para>Recursively set + ownership, access mode and relabel + security context of a path and + all its subdirectories (if it is a + directory). Lines of this type accept + shell-style globs in place of normal + path names.</para></listitem> + </varlistentry> </variablelist> </refsect2> @@ -164,8 +209,11 @@ d /run/user 0755 root root 10d</programlisting> <para>The file access mode to use when creating this file or directory. If omitted or when set to - the default is used: 0755 for - directories, 0644 for files. This parameter is - ignored for x, r, R lines.</para> + directories, 0644 for all other file + objects. For z, Z lines if omitted or when set + to - the file access mode will not be + modified. This parameter is ignored for x, r, + R, L lines.</para> </refsect2> <refsect2> @@ -175,8 +223,9 @@ d /run/user 0755 root root 10d</programlisting> or directory. This may either be a numeric user/group ID or a user or group name. If omitted or when set to - the default 0 (root) - is used. . These parameters are ignored for x, - r, R lines.</para> + is used. For z, Z lines when omitted or when set to - + the file ownership will not be modified. + These parameters are ignored for x, r, R, L lines.</para> </refsect2> <refsect2> @@ -209,6 +258,19 @@ d /run/user 0755 root root 10d</programlisting> is done.</para> </refsect2> + <refsect2> + <title>Argument</title> + + <para>For L lines determines the destination + path of the symlink. For c, b determines the + major/minor of the device node, with major and + minor formatted as integers, separated by :, + e.g. "1:3". For f, F, w may be used to specify + a short string that is written to the file, + suffixed by a newline. Ignored for all other + lines.</para> + </refsect2> + </refsect1> <refsect1> @@ -217,7 +279,7 @@ d /run/user 0755 root root 10d</programlisting> <title>/etc/tmpfiles.d/screen.conf example</title> <para><command>screen</command> needs two directories created at boot with specific modes and ownership.</para> - <programlisting>d /var/run/screens 1777 root root 10d + <programlisting>d /var/run/screens 1777 root root 10d d /var/run/uscreens 0755 root root 10d12h</programlisting> </example> </refsect1> diff --git a/po/.gitignore b/po/.gitignore index 251edd4c81..1fa8d3fd56 100644 --- a/po/.gitignore +++ b/po/.gitignore @@ -1,3 +1,5 @@ POTFILES Makefile.in.in .intltool-merge-cache +Makefile +systemd.pot diff --git a/po/POTFILES.in b/po/POTFILES.in index 29be44eed1..ed2c308db8 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -1,4 +1,4 @@ -src/org.freedesktop.hostname1.policy.in -src/org.freedesktop.locale1.policy.in -src/org.freedesktop.login1.policy.in -src/org.freedesktop.timedate1.policy.in +src/hostname/org.freedesktop.hostname1.policy.in +src/locale/org.freedesktop.locale1.policy.in +src/login/org.freedesktop.login1.policy.in +src/timedate/org.freedesktop.timedate1.policy.in diff --git a/po/POTFILES.skip b/po/POTFILES.skip index 192f3b6cc6..5a5d027493 100644 --- a/po/POTFILES.skip +++ b/po/POTFILES.skip @@ -11,9 +11,9 @@ src/dbus-swap.c src/dbus-target.c src/dbus-timer.c src/dbus-unit.c -src/hostnamed.c -src/localed.c +src/hostname/hostnamed.c +src/locale/localed.c src/org.freedesktop.systemd1.policy.in.in -src/timedated.c +src/timedate/timedated.c units/systemd-readahead-done.service.in units/user@.service.in diff --git a/po/pl.po b/po/pl.po new file mode 100644 index 0000000000..2581d01fcc --- /dev/null +++ b/po/pl.po @@ -0,0 +1,175 @@ +# translation of pl.po to Polish +# Piotr Drąg <piotrdrag@gmail.com>, 2011. +# Zbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>, 2011. +# +msgid "" +msgstr "" +"Project-Id-Version: systemd\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2011-10-14 16:18+0200\n" +"PO-Revision-Date: 2011-10-14 16:20+0200\n" +"Last-Translator: Piotr Drąg <piotrdrag@gmail.com>\n" +"Language-Team: Polish <trans-pl@lists.fedoraproject.org>\n" +"Language: pl\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: ../src/org.freedesktop.hostname1.policy.in.h:1 +msgid "Authentication is required to set local machine information." +msgstr "" +"Wymagane jest uwierzytelnienie, aby ustawić informacje o lokalnym komputerze." + +#: ../src/org.freedesktop.hostname1.policy.in.h:2 +msgid "Authentication is required to set the local host name." +msgstr "Wymagane jest uwierzytelnienie, aby ustawić nazwę lokalnego komputera." + +#: ../src/org.freedesktop.hostname1.policy.in.h:3 +msgid "" +"Authentication is required to set the statically configured local host name, " +"as well as the pretty host name." +msgstr "" +"Wymagane jest uwierzytelnienie, aby ustawić statycznie skonfigurowaną nazwę " +"lokalnego komputera, a także jego ładną nazwę." + +#: ../src/org.freedesktop.hostname1.policy.in.h:4 +msgid "Set host name" +msgstr "Ustawienie nazwy komputera" + +#: ../src/org.freedesktop.hostname1.policy.in.h:5 +msgid "Set machine information" +msgstr "Ustawienie informacji o komputerze" + +#: ../src/org.freedesktop.hostname1.policy.in.h:6 +msgid "Set static host name" +msgstr "Ustawienie statycznej nazwy komputera" + +#: ../src/org.freedesktop.locale1.policy.in.h:1 +msgid "Authentication is required to set the system keyboard settings." +msgstr "Wymagane jest uwierzytelnienie, aby ustawić klawiaturę systemu." + +#: ../src/org.freedesktop.locale1.policy.in.h:2 +msgid "Authentication is required to set the system locale." +msgstr "Wymagane jest uwierzytelnienie, aby ustawić lokalizację systemu." + +#: ../src/org.freedesktop.locale1.policy.in.h:3 +msgid "Set system keyboard settings" +msgstr "Ustawienie klawiatury systemu" + +#: ../src/org.freedesktop.locale1.policy.in.h:4 +msgid "Set system locale" +msgstr "Ustawienie lokalizacji systemu" + +#: ../src/org.freedesktop.login1.policy.in.h:1 +msgid "Allow attaching devices to seats" +msgstr "Zezwolenie na podłączanie urządzeń do stanowisk" + +#: ../src/org.freedesktop.login1.policy.in.h:2 +msgid "Allow non-logged-in users to run programs" +msgstr "Zezwolenie niezalogowanym użytkownikom na uruchamianie programów" + +#: ../src/org.freedesktop.login1.policy.in.h:3 +msgid "" +"Authentication is required to allow a non-logged-in user to run programs" +msgstr "" +"Wymagane jest uwierzytelnienie, aby ustawić zezwolić niezalogowanym " +"użytkownikom na uruchamianie programów" + +#: ../src/org.freedesktop.login1.policy.in.h:4 +msgid "Authentication is required to allow attaching a device to a seat" +msgstr "" +"Wymagane jest uwierzytelnienie, aby zezwolić na podłączenie urządzenia do " +"stanowiska" + +#: ../src/org.freedesktop.login1.policy.in.h:5 +msgid "Authentication is required to allow powering off the system" +msgstr "Wymagane jest uwierzytelnienie, aby zezwolić na wyłączanie systemu" + +#: ../src/org.freedesktop.login1.policy.in.h:6 +msgid "" +"Authentication is required to allow powering off the system while other " +"users are logged in" +msgstr "" +"Wymagane jest uwierzytelnienie, aby zezwolić na wyłączanie systemu, kiedy są " +"zalogowani inni użytkownicy" + +#: ../src/org.freedesktop.login1.policy.in.h:7 +msgid "Authentication is required to allow rebooting the system" +msgstr "" +"Wymagane jest uwierzytelnienie, aby zezwolić na ponowne uruchamianie systemu" + +#: ../src/org.freedesktop.login1.policy.in.h:8 +msgid "" +"Authentication is required to allow rebooting the system while other users " +"are logged in" +msgstr "" +"Wymagane jest uwierzytelnienie, aby zezwolić na ponowne uruchamianie " +"systemu, kiedy są zalogowani inni użytkownicy" + +#: ../src/org.freedesktop.login1.policy.in.h:9 +msgid "" +"Authentication is required to allow resetting how devices are attached to " +"seats" +msgstr "" +"Wymagane jest uwierzytelnienie, aby zezwolić na ponowne ustawianie sposobu " +"podłączenia urządzeń do stanowisk" + +#: ../src/org.freedesktop.login1.policy.in.h:10 +msgid "Flush device to seat attachments" +msgstr "Usunięcie podłączenia urządzeń do stanowisk" + +#: ../src/org.freedesktop.login1.policy.in.h:11 +msgid "Power off the system" +msgstr "Wyłączenie systemu" + +#: ../src/org.freedesktop.login1.policy.in.h:12 +msgid "Power off the system when other users are logged in" +msgstr "Wyłączenie systemu, kiedy są zalogowani inni użytkownicy" + +#: ../src/org.freedesktop.login1.policy.in.h:13 +msgid "Reboot the system" +msgstr "Ponowne uruchomienie systemu" + +#: ../src/org.freedesktop.login1.policy.in.h:14 +msgid "Reboot the system when other users are logged in" +msgstr "Ponowne uruchomienie systemu, kiedy są zalogowani inni użytkownicy" + +#: ../src/org.freedesktop.timedate1.policy.in.h:1 +msgid "" +"Authentication is required to control whether network time synchronization " +"shall be enabled." +msgstr "" +"Wymagane jest uwierzytelnienie, aby kontrolować, czy włączyć synchronizację " +"czasu przez sieć." + +#: ../src/org.freedesktop.timedate1.policy.in.h:2 +msgid "" +"Authentication is required to control whether the RTC stores the local or " +"UTC time." +msgstr "" +"Wymagane jest uwierzytelnienie, aby kontrolować, czy RTC przechowuje czas " +"lokalny lub czas UTC." + +#: ../src/org.freedesktop.timedate1.policy.in.h:3 +msgid "Authentication is required to set the system time." +msgstr "Wymagane jest uwierzytelnienie, aby ustawić czas systemu." + +#: ../src/org.freedesktop.timedate1.policy.in.h:4 +msgid "Authentication is required to set the system timezone." +msgstr "Wymagane jest uwierzytelnienie, aby ustawić strefę czasową systemu." + +#: ../src/org.freedesktop.timedate1.policy.in.h:5 +msgid "Set RTC to local timezone or UTC" +msgstr "Ustawienie RTC na lokalną strefę czasową lub strefę UTC" + +#: ../src/org.freedesktop.timedate1.policy.in.h:6 +msgid "Set system time" +msgstr "Ustawienie czasu systemu" + +#: ../src/org.freedesktop.timedate1.policy.in.h:7 +msgid "Set system timezone" +msgstr "Ustawienie strefy czasowej systemu" + +#: ../src/org.freedesktop.timedate1.policy.in.h:8 +msgid "Turn network time synchronization on or off" +msgstr "Włączenie lub wyłączenie synchronizacji czasu przez sieć" diff --git a/src/.gitignore b/src/.gitignore index 6c4ccaa2dd..4b123f86d2 100644 --- a/src/.gitignore +++ b/src/.gitignore @@ -1,10 +1,11 @@ -99-systemd.rules -org.freedesktop.hostname1.policy -org.freedesktop.locale1.policy -org.freedesktop.login1.policy -org.freedesktop.timedate1.policy +/*.pc +load-fragment-gperf-nulstr.c +load-fragment-gperf.c +load-fragment-gperf.gperf +org.freedesktop.systemd1.policy.in org.freedesktop.systemd1.policy gnome-ask-password-agent.c systemd-interfaces.c systemadm.c -73-seat-late.rules +wraplabel.c +99-systemd.rules diff --git a/src/99-systemd.rules.in b/src/99-systemd.rules.in index e0aa49da8c..d306f71b63 100644 --- a/src/99-systemd.rules.in +++ b/src/99-systemd.rules.in @@ -8,7 +8,7 @@ ACTION=="remove", GOTO="systemd_end" SUBSYSTEM=="tty", KERNEL=="tty[0-9]|tty1[0-2]", TAG+="systemd" -SUBSYSTEM=="tty", KERNEL=="tty[a-zA-Z]*|hvc*", TAG+="systemd" +SUBSYSTEM=="tty", KERNEL=="tty[a-zA-Z]*|hvc*|xvc*|hvsi*", TAG+="systemd" KERNEL=="vport*", TAG+="systemd" @@ -17,6 +17,7 @@ SUBSYSTEM=="block", KERNEL!="ram*|loop*", ENV{DM_UDEV_DISABLE_OTHER_RULES_FLAG}= # 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 @@ -30,7 +31,7 @@ SUBSYSTEM=="block", KERNEL!="ram*|loop*", ENV{DM_UUID}=="CRYPT-*", ENV{ID_PART_T # # http://git.kernel.org/?p=linux/hotplug/udev.git;a=blob;f=libudev/libudev-enumerate.c;h=da831449dcaf5e936a14409e8e68ab12d30a98e2;hb=HEAD#l742 -SUBSYSTEM=="net", KERNEL!="lo", TAG+="systemd", ENV{SYSTEMD_ALIAS}="/sys/subsystem/net/devices/%k" +SUBSYSTEM=="net", KERNEL!="lo", TAG+="systemd", ENV{SYSTEMD_ALIAS}="/sys/subsystem/net/devices/$name" SUBSYSTEM=="bluetooth", TAG+="systemd", ENV{SYSTEMD_ALIAS}="/sys/subsystem/bluetooth/devices/%k" SUBSYSTEM=="bluetooth", TAG+="systemd", ENV{SYSTEMD_WANTS}="bluetooth.target" @@ -41,6 +42,14 @@ SUBSYSTEM=="printer", TAG+="systemd", ENV{SYSTEMD_WANTS}="printer.target" SUBSYSTEM=="usb", KERNEL=="lp*", TAG+="systemd", ENV{SYSTEMD_WANTS}="printer.target" SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ENV{ID_USB_INTERFACES}=="*:0701??:*", TAG+="systemd", ENV{SYSTEMD_WANTS}="printer.target" -SUBSYSTEM=="net", RUN+="@rootlibexecdir@/systemd-sysctl --prefix=/proc/sys/net/ipv4/conf/%k --prefix=/proc/sys/net/ipv4/neigh/%k --prefix=/proc/sys/net/ipv6/conf/%k --prefix=/proc/sys/net/ipv6/neigh/%k" +# Apply sysctl variables to network devices (and only to those) as they appear. + +SUBSYSTEM=="net", KERNEL!="lo", RUN+="@rootlibexecdir@/systemd-sysctl --prefix=/proc/sys/net/ipv4/conf/$name --prefix=/proc/sys/net/ipv4/neigh/$name --prefix=/proc/sys/net/ipv6/conf/$name --prefix=/proc/sys/net/ipv6/neigh/$name" + +# Asynchronously mount file systems implemented by these modules as +# soon as they are loaded. + +SUBSYSTEM=="module", KERNEL=="fuse", ACTION=="add", TAG+="systemd", ENV{SYSTEMD_WANTS}="sys-fs-fuse-connections.mount" +SUBSYSTEM=="module", KERNEL=="configfs", ACTION=="add", TAG+="systemd", ENV{SYSTEMD_WANTS}="sys-kernel-config.mount" LABEL="systemd_end" diff --git a/src/acl-util.c b/src/acl-util.c new file mode 100644 index 0000000000..a2a9f9a22b --- /dev/null +++ b/src/acl-util.c @@ -0,0 +1,68 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2011 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 <assert.h> +#include <sys/acl.h> +#include <acl/libacl.h> +#include <errno.h> +#include <stdbool.h> + +#include "acl-util.h" + +int acl_find_uid(acl_t acl, uid_t uid, acl_entry_t *entry) { + acl_entry_t i; + int found; + + assert(acl); + assert(entry); + + for (found = acl_get_entry(acl, ACL_FIRST_ENTRY, &i); + found > 0; + found = acl_get_entry(acl, ACL_NEXT_ENTRY, &i)) { + + acl_tag_t tag; + uid_t *u; + bool b; + + if (acl_get_tag_type(i, &tag) < 0) + return -errno; + + if (tag != ACL_USER) + continue; + + u = acl_get_qualifier(i); + if (!u) + return -errno; + + b = *u == uid; + acl_free(u); + + if (b) { + *entry = i; + return 1; + } + } + + if (found < 0) + return -errno; + + return 0; +} diff --git a/src/acl-util.h b/src/acl-util.h new file mode 100644 index 0000000000..798ce43364 --- /dev/null +++ b/src/acl-util.h @@ -0,0 +1,27 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#ifndef fooaclutilhfoo +#define fooaclutilhfoo + +/*** + This file is part of systemd. + + Copyright 2011 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/>. +***/ + +int acl_find_uid(acl_t acl, uid_t uid, acl_entry_t *entry); + +#endif diff --git a/src/ask-password-api.c b/src/ask-password-api.c index 04d5623d9e..ce2f3cbe77 100644 --- a/src/ask-password-api.c +++ b/src/ask-password-api.c @@ -89,10 +89,10 @@ int ask_password_tty( goto finish; } - loop_write(ttyfd, "\x1B[1m", 4, false); + loop_write(ttyfd, ANSI_HIGHLIGHT_ON, sizeof(ANSI_HIGHLIGHT_ON)-1, false); loop_write(ttyfd, message, strlen(message), false); loop_write(ttyfd, " ", 1, false); - loop_write(ttyfd, "\x1B[0m", 4, false); + loop_write(ttyfd, ANSI_HIGHLIGHT_OFF, sizeof(ANSI_HIGHLIGHT_OFF)-1, false); new_termios = old_termios; new_termios.c_lflag &= ~(ICANON|ECHO); @@ -250,6 +250,7 @@ static int create_socket(char **name) { } sa; int one = 1, r; char *c; + mode_t u; assert(name); @@ -262,7 +263,11 @@ static int create_socket(char **name) { sa.un.sun_family = AF_UNIX; snprintf(sa.un.sun_path, sizeof(sa.un.sun_path)-1, "/run/systemd/ask-password/sck.%llu", random_ull()); - if (bind(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path)) < 0) { + u = umask(0177); + r = bind(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path)); + umask(u); + + if (r < 0) { r = -errno; log_error("bind() failed: %m"); goto fail; @@ -310,6 +315,7 @@ int ask_password_agent( int socket_fd = -1, signal_fd = -1; sigset_t mask, oldmask; struct pollfd pollfd[_FD_MAX]; + mode_t u; assert(_passphrases); @@ -319,7 +325,11 @@ int ask_password_agent( mkdir_p("/run/systemd/ask-password", 0755); - if ((fd = mkostemp(temp, O_CLOEXEC|O_CREAT|O_WRONLY)) < 0) { + u = umask(0022); + fd = mkostemp(temp, O_CLOEXEC|O_CREAT|O_WRONLY); + umask(u); + + if (fd < 0) { log_error("Failed to create password file: %m"); r = -errno; goto finish; diff --git a/src/automount.c b/src/automount.c index 51fa0030dd..cf2fb60cdf 100644 --- a/src/automount.c +++ b/src/automount.c @@ -52,14 +52,14 @@ static void automount_init(Unit *u) { Automount *a = AUTOMOUNT(u); assert(u); - assert(u->meta.load_state == UNIT_STUB); + assert(u->load_state == UNIT_STUB); a->pipe_watch.fd = a->pipe_fd = -1; a->pipe_watch.type = WATCH_INVALID; a->directory_mode = 0755; - a->meta.ignore_on_isolate = true; + UNIT(a)->ignore_on_isolate = true; } static void repeat_unmout(const char *path) { @@ -94,8 +94,8 @@ static void unmount_autofs(Automount *a) { /* If we reload/reexecute things we keep the mount point * around */ if (a->where && - (a->meta.manager->exit_code != MANAGER_RELOAD && - a->meta.manager->exit_code != MANAGER_REEXECUTE)) + (UNIT(a)->manager->exit_code != MANAGER_RELOAD && + UNIT(a)->manager->exit_code != MANAGER_REEXECUTE)) repeat_unmout(a->where); } @@ -105,7 +105,7 @@ static void automount_done(Unit *u) { assert(a); unmount_autofs(a); - a->mount = NULL; + unit_ref_unset(&a->mount); free(a->where); a->where = NULL; @@ -120,8 +120,8 @@ int automount_add_one_mount_link(Automount *a, Mount *m) { assert(a); assert(m); - if (a->meta.load_state != UNIT_LOADED || - m->meta.load_state != UNIT_LOADED) + if (UNIT(a)->load_state != UNIT_LOADED || + UNIT(m)->load_state != UNIT_LOADED) return 0; if (!path_startswith(a->where, m->where)) @@ -137,13 +137,13 @@ int automount_add_one_mount_link(Automount *a, Mount *m) { } static int automount_add_mount_links(Automount *a) { - Meta *other; + Unit *other; int r; assert(a); - LIST_FOREACH(units_by_type, other, a->meta.manager->units_by_type[UNIT_MOUNT]) - if ((r = automount_add_one_mount_link(a, (Mount*) other)) < 0) + LIST_FOREACH(units_by_type, other, UNIT(a)->manager->units_by_type[UNIT_MOUNT]) + if ((r = automount_add_one_mount_link(a, MOUNT(other))) < 0) return r; return 0; @@ -154,7 +154,7 @@ static int automount_add_default_dependencies(Automount *a) { assert(a); - if (a->meta.manager->running_as == MANAGER_SYSTEM) { + if (UNIT(a)->manager->running_as == MANAGER_SYSTEM) { if ((r = unit_add_dependency_by_name(UNIT(a), UNIT_BEFORE, SPECIAL_BASIC_TARGET, NULL, true)) < 0) return r; @@ -171,7 +171,7 @@ static int automount_verify(Automount *a) { char *e; assert(a); - if (a->meta.load_state != UNIT_LOADED) + if (UNIT(a)->load_state != UNIT_LOADED) return 0; if (path_equal(a->where, "/")) { @@ -186,7 +186,7 @@ static int automount_verify(Automount *a) { free(e); if (!b) { - log_error("%s's Where setting doesn't match unit name. Refusing.", a->meta.id); + log_error("%s's Where setting doesn't match unit name. Refusing.", UNIT(a)->id); return -EINVAL; } @@ -198,16 +198,17 @@ static int automount_load(Unit *u) { Automount *a = AUTOMOUNT(u); assert(u); - assert(u->meta.load_state == UNIT_STUB); + assert(u->load_state == UNIT_STUB); /* Load a .automount file */ if ((r = unit_load_fragment_and_dropin_optional(u)) < 0) return r; - if (u->meta.load_state == UNIT_LOADED) { + if (u->load_state == UNIT_LOADED) { + Unit *x; if (!a->where) - if (!(a->where = unit_name_to_path(u->meta.id))) + if (!(a->where = unit_name_to_path(u->id))) return -ENOMEM; path_kill_slashes(a->where); @@ -215,13 +216,17 @@ static int automount_load(Unit *u) { if ((r = automount_add_mount_links(a)) < 0) return r; - if ((r = unit_load_related_unit(u, ".mount", (Unit**) &a->mount)) < 0) + r = unit_load_related_unit(u, ".mount", &x); + if (r < 0) return r; - if ((r = unit_add_dependency(u, UNIT_BEFORE, UNIT(a->mount), true)) < 0) + unit_ref_set(&a->mount, x); + + r = unit_add_two_dependencies(u, UNIT_BEFORE, UNIT_TRIGGERS, UNIT_DEREF(a->mount), true); + if (r < 0) return r; - if (a->meta.default_dependencies) + if (UNIT(a)->default_dependencies) if ((r = automount_add_default_dependencies(a)) < 0) return r; } @@ -242,7 +247,7 @@ static void automount_set_state(Automount *a, AutomountState state) { if (state != old_state) log_debug("%s changed %s -> %s", - a->meta.id, + UNIT(a)->id, automount_state_to_string(old_state), automount_state_to_string(state)); @@ -258,7 +263,7 @@ static int automount_coldplug(Unit *u) { if (a->deserialized_state != a->state) { - if ((r = open_dev_autofs(u->meta.manager)) < 0) + if ((r = open_dev_autofs(u->manager)) < 0) return r; if (a->deserialized_state == AUTOMOUNT_WAITING || @@ -283,20 +288,22 @@ static void automount_dump(Unit *u, FILE *f, const char *prefix) { fprintf(f, "%sAutomount State: %s\n" + "%sResult: %s\n" "%sWhere: %s\n" "%sDirectoryMode: %04o\n", prefix, automount_state_to_string(a->state), + prefix, automount_result_to_string(a->result), prefix, a->where, prefix, a->directory_mode); } -static void automount_enter_dead(Automount *a, bool success) { +static void automount_enter_dead(Automount *a, AutomountResult f) { assert(a); - if (!success) - a->failure = true; + if (f != AUTOMOUNT_SUCCESS) + a->result = f; - automount_set_state(a, a->failure ? AUTOMOUNT_FAILED : AUTOMOUNT_DEAD); + automount_set_state(a, a->result != AUTOMOUNT_SUCCESS ? AUTOMOUNT_FAILED : AUTOMOUNT_DEAD); } static int open_dev_autofs(Manager *m) { @@ -437,7 +444,7 @@ int automount_send_ready(Automount *a, int status) { if (set_isempty(a->tokens)) return 0; - if ((ioctl_fd = open_ioctl_fd(a->meta.manager->dev_autofs_fd, a->where, a->dev_id)) < 0) { + if ((ioctl_fd = open_ioctl_fd(UNIT(a)->manager->dev_autofs_fd, a->where, a->dev_id)) < 0) { r = ioctl_fd; goto fail; } @@ -458,7 +465,7 @@ int automount_send_ready(Automount *a, int status) { * if you pass a positive status code here, the kernel will * freeze! Yay! */ - if ((k = autofs_send_ready(a->meta.manager->dev_autofs_fd, + if ((k = autofs_send_ready(UNIT(a)->manager->dev_autofs_fd, ioctl_fd, token, status)) < 0) @@ -486,7 +493,7 @@ static void automount_enter_waiting(Automount *a) { if (a->tokens) set_clear(a->tokens); - if ((dev_autofs_fd = open_dev_autofs(a->meta.manager)) < 0) { + if ((dev_autofs_fd = open_dev_autofs(UNIT(a)->manager)) < 0) { r = dev_autofs_fd; goto fail; } @@ -560,7 +567,7 @@ fail: repeat_unmout(a->where); log_error("Failed to initialize automounter: %s", strerror(-r)); - automount_enter_dead(a, false); + automount_enter_dead(a, AUTOMOUNT_FAILURE_RESOURCES); } static void automount_enter_runnning(Automount *a) { @@ -569,14 +576,14 @@ static void automount_enter_runnning(Automount *a) { DBusError error; assert(a); - assert(a->mount); + assert(UNIT_DEREF(a->mount)); dbus_error_init(&error); /* We don't take mount requests anymore if we are supposed to * shut down anyway */ if (unit_pending_inactive(UNIT(a))) { - log_debug("Suppressing automount request on %s since unit stop is scheduled.", a->meta.id); + log_debug("Suppressing automount request on %s since unit stop is scheduled.", UNIT(a)->id); automount_send_ready(a, -EHOSTDOWN); return; } @@ -585,14 +592,14 @@ 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 to stat automount point: %m", a->meta.id); + log_warning("%s failed to stat automount point: %m", UNIT(a)->id); goto fail; } if (!S_ISDIR(st.st_mode) || st.st_dev != a->dev_id) - log_info("%s's automount point already active?", a->meta.id); - else if ((r = manager_add_job(a->meta.manager, JOB_START, UNIT(a->mount), JOB_REPLACE, true, &error, NULL)) < 0) { - log_warning("%s failed to queue mount startup job: %s", a->meta.id, bus_error(&error, r)); + log_info("%s's automount point already active?", UNIT(a)->id); + else if ((r = manager_add_job(UNIT(a)->manager, JOB_START, UNIT_DEREF(a->mount), JOB_REPLACE, true, &error, NULL)) < 0) { + log_warning("%s failed to queue mount startup job: %s", UNIT(a)->id, bus_error(&error, r)); goto fail; } @@ -600,7 +607,7 @@ static void automount_enter_runnning(Automount *a) { return; fail: - automount_enter_dead(a, false); + automount_enter_dead(a, AUTOMOUNT_FAILURE_RESOURCES); dbus_error_free(&error); } @@ -611,15 +618,15 @@ static int automount_start(Unit *u) { assert(a->state == AUTOMOUNT_DEAD || a->state == AUTOMOUNT_FAILED); - if (path_is_mount_point(a->where)) { - log_error("Path %s is already a mount point, refusing start for %s", a->where, u->meta.id); + if (path_is_mount_point(a->where, false)) { + log_error("Path %s is already a mount point, refusing start for %s", a->where, u->id); return -EEXIST; } - if (a->mount->meta.load_state != UNIT_LOADED) + if (UNIT_DEREF(a->mount)->load_state != UNIT_LOADED) return -ENOENT; - a->failure = false; + a->result = AUTOMOUNT_SUCCESS; automount_enter_waiting(a); return 0; } @@ -631,7 +638,7 @@ static int automount_stop(Unit *u) { assert(a->state == AUTOMOUNT_WAITING || a->state == AUTOMOUNT_RUNNING); - automount_enter_dead(a, true); + automount_enter_dead(a, AUTOMOUNT_SUCCESS); return 0; } @@ -645,7 +652,7 @@ static int automount_serialize(Unit *u, FILE *f, FDSet *fds) { assert(fds); unit_serialize_item(u, f, "state", automount_state_to_string(a->state)); - unit_serialize_item(u, f, "failure", yes_no(a->failure)); + unit_serialize_item(u, f, "result", automount_result_to_string(a->result)); unit_serialize_item_format(u, f, "dev-id", "%u", (unsigned) a->dev_id); SET_FOREACH(p, a->tokens, i) @@ -677,13 +684,15 @@ static int automount_deserialize_item(Unit *u, const char *key, const char *valu log_debug("Failed to parse state value %s", value); else a->deserialized_state = state; - } else if (streq(key, "failure")) { - int b; + } else if (streq(key, "result")) { + AutomountResult f; + + f = automount_result_from_string(value); + if (f < 0) + log_debug("Failed to parse result value %s", value); + else if (f != AUTOMOUNT_SUCCESS) + a->result = f; - if ((b = parse_boolean(value)) < 0) - log_debug("Failed to parse failure value %s", value); - else - a->failure = b || a->failure; } else if (streq(key, "dev-id")) { unsigned d; @@ -738,10 +747,10 @@ static bool automount_check_gc(Unit *u) { assert(a); - if (!a->mount) + if (!UNIT_DEREF(a->mount)) return false; - return UNIT_VTABLE(UNIT(a->mount))->check_gc(UNIT(a->mount)); + return UNIT_VTABLE(UNIT_DEREF(a->mount))->check_gc(UNIT_DEREF(a->mount)); } static void automount_fd_event(Unit *u, int fd, uint32_t events, Watch *w) { @@ -770,7 +779,7 @@ static void automount_fd_event(Unit *u, int fd, uint32_t events, Watch *w) { if (packet.v5_packet.pid > 0) { char *p = NULL; - get_process_name(packet.v5_packet.pid, &p); + get_process_comm(packet.v5_packet.pid, &p); log_debug("Got direct mount request for %s, triggered by %lu (%s)", packet.v5_packet.name, (unsigned long) packet.v5_packet.pid, strna(p)); free(p); @@ -799,7 +808,7 @@ static void automount_fd_event(Unit *u, int fd, uint32_t events, Watch *w) { return; fail: - automount_enter_dead(a, false); + automount_enter_dead(a, AUTOMOUNT_FAILURE_RESOURCES); } static void automount_shutdown(Manager *m) { @@ -817,7 +826,7 @@ static void automount_reset_failed(Unit *u) { if (a->state == AUTOMOUNT_FAILED) automount_set_state(a, AUTOMOUNT_DEAD); - a->failure = false; + a->result = AUTOMOUNT_SUCCESS; } static const char* const automount_state_table[_AUTOMOUNT_STATE_MAX] = { @@ -829,8 +838,20 @@ static const char* const automount_state_table[_AUTOMOUNT_STATE_MAX] = { DEFINE_STRING_TABLE_LOOKUP(automount_state, AutomountState); +static const char* const automount_result_table[_AUTOMOUNT_RESULT_MAX] = { + [AUTOMOUNT_SUCCESS] = "success", + [AUTOMOUNT_FAILURE_RESOURCES] = "resources" +}; + +DEFINE_STRING_TABLE_LOOKUP(automount_result, AutomountResult); + const UnitVTable automount_vtable = { .suffix = ".automount", + .object_size = sizeof(Automount), + .sections = + "Unit\0" + "Automount\0" + "Install\0", .no_alias = true, .no_instances = true, @@ -860,6 +881,7 @@ const UnitVTable automount_vtable = { .bus_interface = "org.freedesktop.systemd1.Automount", .bus_message_handler = bus_automount_message_handler, + .bus_invalidating_properties = bus_automount_invalidating_properties, .shutdown = automount_shutdown }; diff --git a/src/automount.h b/src/automount.h index 1a6cc98fd1..19baee208b 100644 --- a/src/automount.h +++ b/src/automount.h @@ -35,24 +35,30 @@ typedef enum AutomountState { _AUTOMOUNT_STATE_INVALID = -1 } AutomountState; +typedef enum AutomountResult { + AUTOMOUNT_SUCCESS, + AUTOMOUNT_FAILURE_RESOURCES, + _AUTOMOUNT_RESULT_MAX, + _AUTOMOUNT_RESULT_INVALID = -1 +} AutomountResult; + struct Automount { - Meta meta; + Unit meta; AutomountState state, deserialized_state; char *where; - Mount *mount; + UnitRef mount; int pipe_fd; mode_t directory_mode; Watch pipe_watch; dev_t dev_id; - Set *tokens; - bool failure:1; + AutomountResult result; }; extern const UnitVTable automount_vtable; @@ -64,4 +70,7 @@ int automount_add_one_mount_link(Automount *a, Mount *m); const char* automount_state_to_string(AutomountState i); AutomountState automount_state_from_string(const char *s); +const char* automount_result_to_string(AutomountResult i); +AutomountResult automount_result_from_string(const char *s); + #endif diff --git a/src/binfmt/Makefile b/src/binfmt/Makefile new file mode 120000 index 0000000000..d0b0e8e008 --- /dev/null +++ b/src/binfmt/Makefile @@ -0,0 +1 @@ +../Makefile
\ No newline at end of file diff --git a/src/binfmt.c b/src/binfmt/binfmt.c index a815a112e8..e8d6524391 100644 --- a/src/binfmt.c +++ b/src/binfmt/binfmt.c @@ -33,7 +33,7 @@ #include "util.h" static int delete_rule(const char *rule) { - char *x, *fn, *e; + char *x, *fn = NULL, *e; int r; assert(rule[0]); @@ -127,6 +127,8 @@ int main(int argc, char *argv[]) { log_parse_environment(); log_open(); + umask(0022); + if (argc > 1) { r = apply_file(argv[1], false); } else { diff --git a/src/bridge.c b/src/bridge.c index 878856cfd6..1f7cf3a9b9 100644 --- a/src/bridge.c +++ b/src/bridge.c @@ -147,7 +147,7 @@ int main(int argc, char *argv[]) { return EXIT_FAILURE; } - log_set_target(LOG_TARGET_SYSLOG_OR_KMSG); + log_set_target(LOG_TARGET_JOURNAL_OR_KMSG); log_parse_environment(); log_open(); diff --git a/src/cgls.c b/src/cgls.c index 20d6531123..d5417f9505 100644 --- a/src/cgls.c +++ b/src/cgls.c @@ -33,13 +33,15 @@ #include "pager.h" static bool arg_no_pager = false; +static bool arg_kernel_threads = false; static void help(void) { printf("%s [OPTIONS...] [CGROUP...]\n\n" "Recursively show control group contents.\n\n" " -h --help Show this help\n" - " --no-pager Do not pipe output into a pager\n", + " --no-pager Do not pipe output into a pager\n" + " -k Include kernel threads in output\n", program_invocation_short_name); } @@ -60,7 +62,7 @@ static int parse_argv(int argc, char *argv[]) { assert(argc >= 1); assert(argv); - while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) { + while ((c = getopt_long(argc, argv, "hk", options, NULL)) >= 0) { switch (c) { @@ -72,6 +74,10 @@ static int parse_argv(int argc, char *argv[]) { arg_no_pager = true; break; + case 'k': + arg_kernel_threads = true; + break; + case '?': return -EINVAL; @@ -90,7 +96,8 @@ int main(int argc, char *argv[]) { log_parse_environment(); log_open(); - if ((r = parse_argv(argc, argv)) < 0) + r = parse_argv(argc, argv); + if (r < 0) goto finish; else if (r == 0) { retval = EXIT_SUCCESS; @@ -107,26 +114,29 @@ int main(int argc, char *argv[]) { int q; printf("%s:\n", argv[i]); - if ((q = show_cgroup_by_path(argv[i], NULL, 0)) < 0) + q = show_cgroup_by_path(argv[i], NULL, 0, arg_kernel_threads); + if (q < 0) r = q; } } else { char *p; - if (!(p = get_current_dir_name())) { + p = get_current_dir_name(); + if (!p) { log_error("Cannot determine current working directory: %m"); goto finish; } if (path_startswith(p, "/sys/fs/cgroup")) { printf("Working Directory %s:\n", p); - r = show_cgroup_by_path(p, NULL, 0); + r = show_cgroup_by_path(p, NULL, 0, arg_kernel_threads); } else { char *root = NULL; const char *t = NULL; - if ((r = cg_get_by_pid(SYSTEMD_CGROUP_CONTROLLER, 1, &root)) < 0) + r = cg_get_by_pid(SYSTEMD_CGROUP_CONTROLLER, 1, &root); + if (r < 0) t = "/"; else { if (endswith(root, "/system")) @@ -135,7 +145,7 @@ int main(int argc, char *argv[]) { t = root[0] ? root : "/"; } - r = show_cgroup(SYSTEMD_CGROUP_CONTROLLER, t, NULL, 0); + r = show_cgroup(SYSTEMD_CGROUP_CONTROLLER, t, NULL, 0, arg_kernel_threads); free(root); } diff --git a/src/cgroup-attr.c b/src/cgroup-attr.c new file mode 100644 index 0000000000..474a6865c4 --- /dev/null +++ b/src/cgroup-attr.c @@ -0,0 +1,102 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2011 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 "cgroup-attr.h" +#include "cgroup-util.h" +#include "list.h" + +int cgroup_attribute_apply(CGroupAttribute *a, CGroupBonding *b) { + int r; + char *path = NULL; + char *v = NULL; + + assert(a); + + b = cgroup_bonding_find_list(b, a->controller); + if (!b) + return 0; + + if (a->map_callback) { + r = a->map_callback(a->controller, a->name, a->value, &v); + if (r < 0) + return r; + } + + r = cg_get_path(a->controller, b->path, a->name, &path); + if (r < 0) { + free(v); + return r; + } + + r = write_one_line_file(path, v ? v : a->value); + if (r < 0) + log_warning("Failed to write '%s' to %s: %s", v ? v : a->value, path, strerror(-r)); + + free(path); + free(v); + + return r; +} + +int cgroup_attribute_apply_list(CGroupAttribute *first, CGroupBonding *b) { + CGroupAttribute *a; + int r = 0; + + LIST_FOREACH(by_unit, a, first) { + int k; + + k = cgroup_attribute_apply(a, b); + if (r == 0) + r = k; + } + + return r; +} + +CGroupAttribute *cgroup_attribute_find_list(CGroupAttribute *first, const char *controller, const char *name) { + CGroupAttribute *a; + + assert(controller); + assert(name); + + LIST_FOREACH(by_unit, a, first) + if (streq(a->controller, controller) && + streq(a->name, name)) + return a; + + return NULL; +} + +static void cgroup_attribute_free(CGroupAttribute *a) { + assert(a); + + free(a->controller); + free(a->name); + free(a->value); + free(a); +} + +void cgroup_attribute_free_list(CGroupAttribute *first) { + CGroupAttribute *a, *n; + + LIST_FOREACH_SAFE(by_unit, a, n, first) + cgroup_attribute_free(a); +} diff --git a/src/cgroup-attr.h b/src/cgroup-attr.h new file mode 100644 index 0000000000..63a73b8101 --- /dev/null +++ b/src/cgroup-attr.h @@ -0,0 +1,49 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#ifndef foocgroupattrhfoo +#define foocgroupattrhfoo + +/*** + This file is part of systemd. + + Copyright 2011 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/>. +***/ + +typedef struct CGroupAttribute CGroupAttribute; + +typedef int (*CGroupAttributeMapCallback)(const char *controller, const char*name, const char *value, char **ret); + +#include "unit.h" +#include "cgroup.h" + +struct CGroupAttribute { + char *controller; + char *name; + char *value; + + CGroupAttributeMapCallback map_callback; + + LIST_FIELDS(CGroupAttribute, by_unit); +}; + +int cgroup_attribute_apply(CGroupAttribute *a, CGroupBonding *b); +int cgroup_attribute_apply_list(CGroupAttribute *first, CGroupBonding *b); + +CGroupAttribute *cgroup_attribute_find_list(CGroupAttribute *first, const char *controller, const char *name); + +void cgroup_attribute_free_list(CGroupAttribute *first); + +#endif diff --git a/src/cgroup-show.c b/src/cgroup-show.c index bc9c216329..ee2a241c9f 100644 --- a/src/cgroup-show.c +++ b/src/cgroup-show.c @@ -50,7 +50,7 @@ static unsigned ilog10(unsigned long ul) { return n; } -static int show_cgroup_one_by_path(const char *path, const char *prefix, unsigned n_columns, bool more) { +static int show_cgroup_one_by_path(const char *path, const char *prefix, unsigned n_columns, bool more, bool kernel_threads) { char *fn; FILE *f; size_t n = 0, n_allocated = 0; @@ -82,6 +82,9 @@ static int show_cgroup_one_by_path(const char *path, const char *prefix, unsigne while ((r = cg_read_pid(f, &pid)) > 0) { + if (!kernel_threads && is_kernel_thread(pid) > 0) + continue; + if (n >= n_allocated) { pid_t *npids; @@ -133,7 +136,7 @@ static int show_cgroup_one_by_path(const char *path, const char *prefix, unsigne for (i = 0; i < n; i++) { char *t = NULL; - get_process_cmdline(pids[i], n_columns, &t); + get_process_cmdline(pids[i], n_columns, true, &t); printf("%s%s %*lu %s\n", prefix, @@ -157,7 +160,7 @@ finish: return r; } -int show_cgroup_by_path(const char *path, const char *prefix, unsigned n_columns) { +int show_cgroup_by_path(const char *path, const char *prefix, unsigned n_columns, bool kernel_threads) { DIR *d; char *last = NULL; char *p1 = NULL, *p2 = NULL, *fn = NULL, *gn = NULL; @@ -181,7 +184,7 @@ int show_cgroup_by_path(const char *path, const char *prefix, unsigned n_columns while ((r = cg_read_subgroup(d, &gn)) > 0) { if (!shown_pids) { - show_cgroup_one_by_path(path, prefix, n_columns, true); + show_cgroup_one_by_path(path, prefix, n_columns, true, kernel_threads); shown_pids = true; } @@ -194,7 +197,7 @@ int show_cgroup_by_path(const char *path, const char *prefix, unsigned n_columns goto finish; } - show_cgroup_by_path(last, p1, n_columns-2); + show_cgroup_by_path(last, p1, n_columns-2, kernel_threads); free(last); last = NULL; @@ -213,7 +216,7 @@ int show_cgroup_by_path(const char *path, const char *prefix, unsigned n_columns goto finish; if (!shown_pids) - show_cgroup_one_by_path(path, prefix, n_columns, !!last); + show_cgroup_one_by_path(path, prefix, n_columns, !!last, kernel_threads); if (last) { printf("%s\342\224\224 %s\n", prefix, file_name_from_path(last)); @@ -224,7 +227,7 @@ int show_cgroup_by_path(const char *path, const char *prefix, unsigned n_columns goto finish; } - show_cgroup_by_path(last, p2, n_columns-2); + show_cgroup_by_path(last, p2, n_columns-2, kernel_threads); } r = 0; @@ -240,17 +243,18 @@ finish: return r; } -int show_cgroup(const char *controller, const char *path, const char *prefix, unsigned n_columns) { +int show_cgroup(const char *controller, const char *path, const char *prefix, unsigned n_columns, bool kernel_threads) { char *p; int r; assert(controller); assert(path); - if ((r = cg_get_path(controller, path, NULL, &p)) < 0) + r = cg_get_path(controller, path, NULL, &p); + if (r < 0) return r; - r = show_cgroup_by_path(p, prefix, n_columns); + r = show_cgroup_by_path(p, prefix, n_columns, kernel_threads); free(p); return r; diff --git a/src/cgroup-show.h b/src/cgroup-show.h index ae5b13b564..992e17b986 100644 --- a/src/cgroup-show.h +++ b/src/cgroup-show.h @@ -22,7 +22,9 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -int show_cgroup_by_path(const char *path, const char *prefix, unsigned columns); -int show_cgroup(const char *controller, const char *path, const char *prefix, unsigned columns); +#include <stdbool.h> + +int show_cgroup_by_path(const char *path, const char *prefix, unsigned columns, bool kernel_threads); +int show_cgroup(const char *controller, const char *path, const char *prefix, unsigned columns, bool kernel_threads); #endif diff --git a/src/cgroup-util.c b/src/cgroup-util.c index 090573bd31..904d300952 100644 --- a/src/cgroup-util.c +++ b/src/cgroup-util.c @@ -27,6 +27,7 @@ #include <dirent.h> #include <sys/stat.h> #include <sys/types.h> +#include <ftw.h> #include "cgroup-util.h" #include "log.h" @@ -153,17 +154,38 @@ int cg_read_subgroup(DIR *d, char **fn) { return 0; } -int cg_rmdir(const char *controller, const char *path) { +int cg_rmdir(const char *controller, const char *path, bool honour_sticky) { char *p; int r; - if ((r = cg_get_path(controller, path, NULL, &p)) < 0) + r = cg_get_path(controller, path, NULL, &p); + if (r < 0) return r; + if (honour_sticky) { + char *tasks; + + /* If the sticky bit is set don't remove the directory */ + + tasks = strappend(p, "/tasks"); + if (!tasks) { + free(p); + return -ENOMEM; + } + + r = file_is_priv_sticky(tasks); + free(tasks); + + if (r > 0) { + free(p); + return 0; + } + } + r = rmdir(p); free(p); - return r < 0 ? -errno : 0; + return (r < 0 && errno != ENOENT) ? -errno : 0; } int cg_kill(const char *controller, const char *path, int sig, bool sigcont, bool ignore_self, Set *s) { @@ -302,7 +324,7 @@ int cg_kill_recursive(const char *controller, const char *path, int sig, bool si ret = r; if (rem) - if ((r = cg_rmdir(controller, path)) < 0) { + if ((r = cg_rmdir(controller, path, true)) < 0) { if (ret >= 0 && r != -ENOENT && r != -EBUSY) @@ -466,7 +488,7 @@ int cg_migrate_recursive(const char *controller, const char *from, const char *t ret = r; if (rem) - if ((r = cg_rmdir(controller, from)) < 0) { + if ((r = cg_rmdir(controller, from, true)) < 0) { if (ret >= 0 && r != -ENOENT && r != -EBUSY) @@ -482,13 +504,26 @@ finish: int cg_get_path(const char *controller, const char *path, const char *suffix, char **fs) { const char *p; - char *mp; - int r; + char *t; static __thread bool good = false; assert(controller); assert(fs); + if (_unlikely_(!good)) { + int r; + + r = path_is_mount_point("/sys/fs/cgroup", false); + if (r <= 0) + return r < 0 ? r : -ENOENT; + + /* Cache this to save a few stat()s */ + good = true; + } + + if (isempty(controller)) + return -EINVAL; + /* This is a very minimal lookup from controller names to * paths. Since we have mounted most hierarchies ourselves * should be kinda safe, but eventually we might want to @@ -502,50 +537,88 @@ int cg_get_path(const char *controller, const char *path, const char *suffix, ch else p = controller; - if (asprintf(&mp, "/sys/fs/cgroup/%s", p) < 0) + if (path && suffix) + t = join("/sys/fs/cgroup/", p, "/", path, "/", suffix, NULL); + else if (path) + t = join("/sys/fs/cgroup/", p, "/", path, NULL); + else if (suffix) + t = join("/sys/fs/cgroup/", p, "/", suffix, NULL); + else + t = join("/sys/fs/cgroup/", p, NULL); + + if (!t) return -ENOMEM; - if (!good) { - if ((r = path_is_mount_point(mp)) <= 0) { - free(mp); - return r < 0 ? r : -ENOENT; - } + path_kill_slashes(t); - /* Cache this to save a few stat()s */ - good = true; - } + *fs = t; + return 0; +} - if (path && suffix) - r = asprintf(fs, "%s/%s/%s", mp, path, suffix); - else if (path) - r = asprintf(fs, "%s/%s", mp, path); - else if (suffix) - r = asprintf(fs, "%s/%s", mp, suffix); - else { - path_kill_slashes(mp); - *fs = mp; +static int trim_cb(const char *path, const struct stat *sb, int typeflag, struct FTW *ftwbuf) { + char *p; + bool is_sticky; + + if (typeflag != FTW_DP) return 0; + + if (ftwbuf->level < 1) + return 0; + + p = strappend(path, "/tasks"); + if (!p) { + errno = ENOMEM; + return 1; } - free(mp); - path_kill_slashes(*fs); - return r < 0 ? -ENOMEM : 0; + is_sticky = file_is_priv_sticky(p) > 0; + free(p); + + if (is_sticky) + return 0; + + rmdir(path); + return 0; } int cg_trim(const char *controller, const char *path, bool delete_root) { char *fs; - int r; + int r = 0; assert(controller); assert(path); - if ((r = cg_get_path(controller, path, NULL, &fs)) < 0) + r = cg_get_path(controller, path, NULL, &fs); + if (r < 0) return r; - r = rm_rf(fs, true, delete_root); + errno = 0; + if (nftw(fs, trim_cb, 64, FTW_DEPTH|FTW_MOUNT|FTW_PHYS) < 0) + r = errno ? -errno : -EIO; + + if (delete_root) { + bool is_sticky; + char *p; + + p = strappend(fs, "/tasks"); + if (!p) { + free(fs); + return -ENOMEM; + } + + is_sticky = file_is_priv_sticky(p) > 0; + free(p); + + if (!is_sticky) + if (rmdir(fs) < 0 && errno != ENOENT) { + if (r == 0) + r = -errno; + } + } + free(fs); - return r == -ENOENT ? 0 : r; + return r; } int cg_delete(const char *controller, const char *path) { @@ -639,7 +712,11 @@ int cg_set_group_access(const char *controller, const char *path, mode_t mode, u assert(controller); assert(path); - if ((r = cg_get_path(controller, path, NULL, &fs)) < 0) + if (mode != (mode_t) -1) + mode &= 0777; + + r = cg_get_path(controller, path, NULL, &fs); + if (r < 0) return r; r = chmod_and_chown(fs, mode, uid, gid); @@ -648,16 +725,47 @@ int cg_set_group_access(const char *controller, const char *path, mode_t mode, u return r; } -int cg_set_task_access(const char *controller, const char *path, mode_t mode, uid_t uid, gid_t gid) { +int cg_set_task_access(const char *controller, const char *path, mode_t mode, uid_t uid, gid_t gid, int sticky) { char *fs; int r; assert(controller); assert(path); - if ((r = cg_get_path(controller, path, "tasks", &fs)) < 0) + if (mode == (mode_t) -1 && uid == (uid_t) -1 && gid == (gid_t) -1 && sticky < 0) + return 0; + + if (mode != (mode_t) -1) + mode &= 0666; + + r = cg_get_path(controller, path, "tasks", &fs); + if (r < 0) return r; + if (sticky >= 0 && mode != (mode_t) -1) + /* Both mode and sticky param are passed */ + mode |= (sticky ? S_ISVTX : 0); + else if ((sticky >= 0 && mode == (mode_t) -1) || + (mode != (mode_t) -1 && sticky < 0)) { + struct stat st; + + /* Only one param is passed, hence read the current + * mode from the file itself */ + + r = lstat(fs, &st); + if (r < 0) { + free(fs); + return -errno; + } + + if (mode == (mode_t) -1) + /* No mode set, we just shall set the sticky bit */ + mode = (st.st_mode & ~S_ISVTX) | (sticky ? S_ISVTX : 0); + else + /* Only mode set, leave sticky bit untouched */ + mode = (st.st_mode & ~0777) | mode; + } + r = chmod_and_chown(fs, mode, uid, gid); free(fs); diff --git a/src/cgroup-util.h b/src/cgroup-util.h index d142af34bc..37e4255a9c 100644 --- a/src/cgroup-util.h +++ b/src/cgroup-util.h @@ -52,7 +52,7 @@ int cg_get_by_pid(const char *controller, pid_t pid, char **path); int cg_trim(const char *controller, const char *path, bool delete_root); -int cg_rmdir(const char *controller, const char *path); +int cg_rmdir(const char *controller, const char *path, bool honour_sticky); int cg_delete(const char *controller, const char *path); int cg_create(const char *controller, const char *path); @@ -60,7 +60,7 @@ int cg_attach(const char *controller, const char *path, pid_t pid); int cg_create_and_attach(const char *controller, const char *path, pid_t pid); int cg_set_group_access(const char *controller, const char *path, mode_t mode, uid_t uid, gid_t gid); -int cg_set_task_access(const char *controller, const char *path, mode_t mode, uid_t uid, gid_t gid); +int cg_set_task_access(const char *controller, const char *path, mode_t mode, uid_t uid, gid_t gid, int sticky); int cg_install_release_agent(const char *controller, const char *agent); diff --git a/src/cgroup.c b/src/cgroup.c index 4aa01f1898..1f6139e25f 100644 --- a/src/cgroup.c +++ b/src/cgroup.c @@ -38,11 +38,11 @@ int cgroup_bonding_realize(CGroupBonding *b) { assert(b->path); assert(b->controller); - if (b->realized) - return 0; - - if ((r = cg_create(b->controller, b->path)) < 0) + r = cg_create(b->controller, b->path); + if (r < 0) { + log_warning("Failed to create cgroup %s:%s: %s", b->controller, b->path, strerror(-r)); return r; + } b->realized = true; @@ -60,32 +60,27 @@ int cgroup_bonding_realize_list(CGroupBonding *first) { return 0; } -void cgroup_bonding_free(CGroupBonding *b, bool remove_or_trim) { +void cgroup_bonding_free(CGroupBonding *b, bool trim) { assert(b); if (b->unit) { CGroupBonding *f; - LIST_REMOVE(CGroupBonding, by_unit, b->unit->meta.cgroup_bondings, b); + LIST_REMOVE(CGroupBonding, by_unit, b->unit->cgroup_bondings, b); if (streq(b->controller, SYSTEMD_CGROUP_CONTROLLER)) { - assert_se(f = hashmap_get(b->unit->meta.manager->cgroup_bondings, b->path)); + assert_se(f = hashmap_get(b->unit->manager->cgroup_bondings, b->path)); LIST_REMOVE(CGroupBonding, by_path, f, b); if (f) - hashmap_replace(b->unit->meta.manager->cgroup_bondings, b->path, f); + hashmap_replace(b->unit->manager->cgroup_bondings, b->path, f); else - hashmap_remove(b->unit->meta.manager->cgroup_bondings, b->path); + hashmap_remove(b->unit->manager->cgroup_bondings, b->path); } } - if (b->realized && b->ours && remove_or_trim) { - - if (cgroup_bonding_is_empty(b) > 0) - cg_delete(b->controller, b->path); - else - cg_trim(b->controller, b->path, false); - } + if (b->realized && b->ours && trim) + cg_trim(b->controller, b->path, false); free(b->controller); free(b->path); @@ -159,21 +154,21 @@ int cgroup_bonding_set_group_access_list(CGroupBonding *first, mode_t mode, uid_ return 0; } -int cgroup_bonding_set_task_access(CGroupBonding *b, mode_t mode, uid_t uid, gid_t gid) { +int cgroup_bonding_set_task_access(CGroupBonding *b, mode_t mode, uid_t uid, gid_t gid, int sticky) { assert(b); if (!b->realized) return -EINVAL; - return cg_set_task_access(b->controller, b->path, mode, uid, gid); + return cg_set_task_access(b->controller, b->path, mode, uid, gid, sticky); } -int cgroup_bonding_set_task_access_list(CGroupBonding *first, mode_t mode, uid_t uid, gid_t gid) { +int cgroup_bonding_set_task_access_list(CGroupBonding *first, mode_t mode, uid_t uid, gid_t gid, int sticky) { CGroupBonding *b; int r; LIST_FOREACH(by_unit, b, first) { - r = cgroup_bonding_set_task_access(b, mode, uid, gid); + r = cgroup_bonding_set_task_access(b, mode, uid, gid, sticky); if (r < 0) return r; } @@ -197,6 +192,9 @@ int cgroup_bonding_kill_list(CGroupBonding *first, int sig, bool sigcont, Set *s Set *allocated_set = NULL; int ret = -EAGAIN, r; + if (!first) + return 0; + if (!s) if (!(s = allocated_set = set_new(trivial_hash_func, trivial_compare_func))) return -ENOMEM; @@ -267,7 +265,7 @@ int manager_setup_cgroup(Manager *m) { assert(m); /* 0. Be nice to Ingo Molnar #628004 */ - if (path_is_mount_point("/sys/fs/cgroup/systemd") <= 0) { + if (path_is_mount_point("/sys/fs/cgroup/systemd", false) <= 0) { log_warning("No control group support available, not creating root group."); return 0; } @@ -357,14 +355,55 @@ void manager_shutdown_cgroup(Manager *m, bool delete) { m->cgroup_hierarchy = NULL; } +int cgroup_bonding_get(Manager *m, const char *cgroup, CGroupBonding **bonding) { + CGroupBonding *b; + char *p; + + assert(m); + assert(cgroup); + assert(bonding); + + b = hashmap_get(m->cgroup_bondings, cgroup); + if (b) { + *bonding = b; + return 1; + } + + p = strdup(cgroup); + if (!p) + return -ENOMEM; + + for (;;) { + char *e; + + e = strrchr(p, '/'); + if (!e || e == p) { + free(p); + *bonding = NULL; + return 0; + } + + *e = 0; + + b = hashmap_get(m->cgroup_bondings, p); + if (b) { + free(p); + *bonding = b; + return 1; + } + } +} + int cgroup_notify_empty(Manager *m, const char *group) { CGroupBonding *l, *b; + int r; assert(m); assert(group); - if (!(l = hashmap_get(m->cgroup_bondings, group))) - return 0; + r = cgroup_bonding_get(m, group, &l); + if (r <= 0) + return r; LIST_FOREACH(by_path, b, l) { int t; @@ -372,7 +411,8 @@ int cgroup_notify_empty(Manager *m, const char *group) { if (!b->unit) continue; - if ((t = cgroup_bonding_is_empty_list(b)) < 0) { + t = cgroup_bonding_is_empty_list(b); + if (t < 0) { /* If we don't know, we don't know */ if (t != -EAGAIN) @@ -381,9 +421,13 @@ int cgroup_notify_empty(Manager *m, const char *group) { continue; } - if (t > 0) + if (t > 0) { + /* If it is empty, let's delete it */ + cgroup_bonding_trim_list(b->unit->cgroup_bondings, true); + if (UNIT_VTABLE(b->unit)->cgroup_notify_empty) UNIT_VTABLE(b->unit)->cgroup_notify_empty(b->unit); + } } return 0; diff --git a/src/cgroup.h b/src/cgroup.h index f33d8440e6..5faa7dc0f7 100644 --- a/src/cgroup.h +++ b/src/cgroup.h @@ -53,8 +53,8 @@ struct CGroupBonding { int cgroup_bonding_realize(CGroupBonding *b); int cgroup_bonding_realize_list(CGroupBonding *first); -void cgroup_bonding_free(CGroupBonding *b, bool remove_or_trim); -void cgroup_bonding_free_list(CGroupBonding *first, bool remove_or_trim); +void cgroup_bonding_free(CGroupBonding *b, bool trim); +void cgroup_bonding_free_list(CGroupBonding *first, bool trim); int cgroup_bonding_install(CGroupBonding *b, pid_t pid); int cgroup_bonding_install_list(CGroupBonding *first, pid_t pid); @@ -62,8 +62,8 @@ int cgroup_bonding_install_list(CGroupBonding *first, pid_t pid); int cgroup_bonding_set_group_access(CGroupBonding *b, mode_t mode, uid_t uid, gid_t gid); int cgroup_bonding_set_group_access_list(CGroupBonding *b, mode_t mode, uid_t uid, gid_t gid); -int cgroup_bonding_set_task_access(CGroupBonding *b, mode_t mode, uid_t uid, gid_t gid); -int cgroup_bonding_set_task_access_list(CGroupBonding *b, mode_t mode, uid_t uid, gid_t gid); +int cgroup_bonding_set_task_access(CGroupBonding *b, mode_t mode, uid_t uid, gid_t gid, int sticky); +int cgroup_bonding_set_task_access_list(CGroupBonding *b, mode_t mode, uid_t uid, gid_t gid, int sticky); int cgroup_bonding_kill(CGroupBonding *b, int sig, bool sigcont, Set *s); int cgroup_bonding_kill_list(CGroupBonding *first, int sig, bool sigcont, Set *s); @@ -86,6 +86,7 @@ pid_t cgroup_bonding_search_main_pid_list(CGroupBonding *b); int manager_setup_cgroup(Manager *m); void manager_shutdown_cgroup(Manager *m, bool delete); +int cgroup_bonding_get(Manager *m, const char *cgroup, CGroupBonding **bonding); int cgroup_notify_empty(Manager *m, const char *group); Unit* cgroup_unit_by_pid(Manager *m, pid_t pid); diff --git a/src/cgroups-agent.c b/src/cgroups-agent.c index 7f001e67d1..1bbc8827d7 100644 --- a/src/cgroups-agent.c +++ b/src/cgroups-agent.c @@ -39,7 +39,7 @@ int main(int argc, char *argv[]) { goto finish; } - log_set_target(LOG_TARGET_SYSLOG_OR_KMSG); + log_set_target(LOG_TARGET_AUTO); log_parse_environment(); log_open(); diff --git a/src/cgtop.c b/src/cgtop.c new file mode 100644 index 0000000000..8b8617dc1c --- /dev/null +++ b/src/cgtop.c @@ -0,0 +1,729 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2012 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 <errno.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <alloca.h> +#include <getopt.h> + +#include "util.h" +#include "hashmap.h" +#include "cgroup-util.h" + +typedef struct Group { + char *path; + + bool n_tasks_valid:1; + bool cpu_valid:1; + bool memory_valid:1; + bool io_valid:1; + + unsigned n_tasks; + + unsigned cpu_iteration; + uint64_t cpu_usage; + struct timespec cpu_timestamp; + double cpu_fraction; + + uint64_t memory; + + unsigned io_iteration; + uint64_t io_input, io_output; + struct timespec io_timestamp; + uint64_t io_input_bps, io_output_bps; +} Group; + +static unsigned arg_depth = 2; +static usec_t arg_delay = 1*USEC_PER_SEC; + +static enum { + ORDER_PATH, + ORDER_TASKS, + ORDER_CPU, + ORDER_MEMORY, + ORDER_IO +} arg_order = ORDER_CPU; + +static void group_free(Group *g) { + assert(g); + + free(g->path); + free(g); +} + +static void group_hashmap_clear(Hashmap *h) { + Group *g; + + while ((g = hashmap_steal_first(h))) + group_free(g); +} + +static void group_hashmap_free(Hashmap *h) { + group_hashmap_clear(h); + hashmap_free(h); +} + +static int process(const char *controller, const char *path, Hashmap *a, Hashmap *b, unsigned iteration) { + Group *g; + int r; + FILE *f; + pid_t pid; + unsigned n; + + assert(controller); + assert(path); + assert(a); + + g = hashmap_get(a, path); + if (!g) { + g = hashmap_get(b, path); + if (!g) { + g = new0(Group, 1); + if (!g) + return -ENOMEM; + + g->path = strdup(path); + if (!g->path) { + group_free(g); + return -ENOMEM; + } + + r = hashmap_put(a, g->path, g); + if (r < 0) { + group_free(g); + return r; + } + } else { + assert_se(hashmap_move_one(a, b, path) == 0); + g->cpu_valid = g->memory_valid = g->io_valid = g->n_tasks_valid = false; + } + } + + /* Regardless which controller, let's find the maximum number + * of processes in any of it */ + + r = cg_enumerate_tasks(controller, path, &f); + if (r < 0) + return r; + + n = 0; + while (cg_read_pid(f, &pid) > 0) + n++; + fclose(f); + + if (n > 0) { + if (g->n_tasks_valid) + g->n_tasks = MAX(g->n_tasks, n); + else + g->n_tasks = n; + + g->n_tasks_valid = true; + } + + if (streq(controller, "cpuacct")) { + uint64_t new_usage; + char *p, *v; + struct timespec ts; + + r = cg_get_path(controller, path, "cpuacct.usage", &p); + if (r < 0) + return r; + + r = read_one_line_file(p, &v); + free(p); + if (r < 0) + return r; + + r = safe_atou64(v, &new_usage); + free(v); + if (r < 0) + return r; + + assert_se(clock_gettime(CLOCK_MONOTONIC, &ts) == 0); + + if (g->cpu_iteration == iteration - 1) { + uint64_t x, y; + + x = ((uint64_t) ts.tv_sec * 1000000000ULL + (uint64_t) ts.tv_nsec) - + ((uint64_t) g->cpu_timestamp.tv_sec * 1000000000ULL + (uint64_t) g->cpu_timestamp.tv_nsec); + + y = new_usage - g->cpu_usage; + + if (y > 0) { + g->cpu_fraction = (double) y / (double) x; + g->cpu_valid = true; + } + } + + g->cpu_usage = new_usage; + g->cpu_timestamp = ts; + g->cpu_iteration = iteration; + + } else if (streq(controller, "memory")) { + char *p, *v; + + r = cg_get_path(controller, path, "memory.usage_in_bytes", &p); + if (r < 0) + return r; + + r = read_one_line_file(p, &v); + free(p); + if (r < 0) + return r; + + r = safe_atou64(v, &g->memory); + free(v); + if (r < 0) + return r; + + if (g->memory > 0) + g->memory_valid = true; + + } else if (streq(controller, "blkio")) { + char *p; + uint64_t wr = 0, rd = 0; + struct timespec ts; + + r = cg_get_path(controller, path, "blkio.io_service_bytes", &p); + if (r < 0) + return r; + + f = fopen(p, "re"); + free(p); + + if (!f) + return -errno; + + for (;;) { + char line[LINE_MAX], *l; + uint64_t k, *q; + + if (!fgets(line, sizeof(line), f)) + break; + + l = strstrip(line); + l += strcspn(l, WHITESPACE); + l += strspn(l, WHITESPACE); + + if (first_word(l, "Read")) { + l += 4; + q = &rd; + } else if (first_word(l, "Write")) { + l += 5; + q = ≀ + } else + continue; + + l += strspn(l, WHITESPACE); + r = safe_atou64(l, &k); + if (r < 0) + continue; + + *q += k; + } + + fclose(f); + + assert_se(clock_gettime(CLOCK_MONOTONIC, &ts) == 0); + + if (g->io_iteration == iteration - 1) { + uint64_t x, yr, yw; + + x = ((uint64_t) ts.tv_sec * 1000000000ULL + (uint64_t) ts.tv_nsec) - + ((uint64_t) g->io_timestamp.tv_sec * 1000000000ULL + (uint64_t) g->io_timestamp.tv_nsec); + + yr = rd - g->io_input; + yw = wr - g->io_output; + + if (yr > 0 || yw > 0) { + g->io_input_bps = (yr * 1000000000ULL) / x; + g->io_output_bps = (yw * 1000000000ULL) / x; + g->io_valid = true; + + } + } + + g->io_input = rd; + g->io_output = wr; + g->io_timestamp = ts; + g->io_iteration = iteration; + } + + return 0; +} + +static int refresh_one( + const char *controller, + const char *path, + Hashmap *a, + Hashmap *b, + unsigned iteration, + unsigned depth) { + + DIR *d = NULL; + int r; + + assert(controller); + assert(path); + assert(a); + + if (depth > arg_depth) + return 0; + + r = process(controller, path, a, b, iteration); + if (r < 0) + return r; + + r = cg_enumerate_subgroups(controller, path, &d); + if (r < 0) { + if (r == ENOENT) + return 0; + + return r; + } + + for (;;) { + char *fn, *p; + + r = cg_read_subgroup(d, &fn); + if (r <= 0) + goto finish; + + p = join(path, "/", fn, NULL); + free(fn); + + if (!p) { + r = -ENOMEM; + goto finish; + } + + path_kill_slashes(p); + + r = refresh_one(controller, p, a, b, iteration, depth + 1); + free(p); + + if (r < 0) + goto finish; + } + +finish: + if (d) + closedir(d); + + return r; +} + +static int refresh(Hashmap *a, Hashmap *b, unsigned iteration) { + int r; + + assert(a); + + r = refresh_one("name=systemd", "/", a, b, iteration, 0); + if (r < 0) + return r; + + r = refresh_one("cpuacct", "/", a, b, iteration, 0); + if (r < 0) + return r; + + r = refresh_one("memory", "/", a, b, iteration, 0); + if (r < 0) + return r; + + return refresh_one("blkio", "/", a, b, iteration, 0); +} + +static int group_compare(const void*a, const void *b) { + const Group *x = *(Group**)a, *y = *(Group**)b; + + if (path_startswith(y->path, x->path)) + return -1; + if (path_startswith(x->path, y->path)) + return 1; + + if (arg_order == ORDER_CPU) { + if (x->cpu_valid && y->cpu_valid) { + + if (x->cpu_fraction > y->cpu_fraction) + return -1; + else if (x->cpu_fraction < y->cpu_fraction) + return 1; + } else if (x->cpu_valid) + return -1; + else if (y->cpu_valid) + return 1; + } + + if (arg_order == ORDER_TASKS) { + + if (x->n_tasks_valid && y->n_tasks_valid) { + if (x->n_tasks > y->n_tasks) + return -1; + else if (x->n_tasks < y->n_tasks) + return 1; + } else if (x->n_tasks_valid) + return -1; + else if (y->n_tasks_valid) + return 1; + } + + if (arg_order == ORDER_MEMORY) { + if (x->memory_valid && y->memory_valid) { + if (x->memory > y->memory) + return -1; + else if (x->memory < y->memory) + return 1; + } else if (x->memory_valid) + return -1; + else if (y->memory_valid) + return 1; + } + + if (arg_order == ORDER_IO) { + if (x->io_valid && y->io_valid) { + if (x->io_input_bps + x->io_output_bps > y->io_input_bps + y->io_output_bps) + return -1; + else if (x->io_input_bps + x->io_output_bps < y->io_input_bps + y->io_output_bps) + return 1; + } else if (x->io_valid) + return -1; + else if (y->io_valid) + return 1; + } + + return strcmp(x->path, y->path); +} + +static int display(Hashmap *a) { + Iterator i; + Group *g; + Group **array; + unsigned rows, n = 0, j; + + assert(a); + + /* Set cursor to top left corner and clear screen */ + fputs("\033[H" + "\033[2J", stdout); + + array = alloca(sizeof(Group*) * hashmap_size(a)); + + HASHMAP_FOREACH(g, a, i) + if (g->n_tasks_valid || g->cpu_valid || g->memory_valid || g->io_valid) + array[n++] = g; + + qsort(array, n, sizeof(Group*), group_compare); + + rows = fd_lines(STDOUT_FILENO); + if (rows <= 0) + rows = 25; + + printf("%s%-37s%s %s%7s%s %s%6s%s %s%8s%s %s%8s%s %s%8s%s\n\n", + arg_order == ORDER_PATH ? ANSI_HIGHLIGHT_ON : "", "Path", arg_order == ORDER_PATH ? ANSI_HIGHLIGHT_OFF : "", + arg_order == ORDER_TASKS ? ANSI_HIGHLIGHT_ON : "", "Tasks", arg_order == ORDER_TASKS ? ANSI_HIGHLIGHT_OFF : "", + arg_order == ORDER_CPU ? ANSI_HIGHLIGHT_ON : "", "%CPU", arg_order == ORDER_CPU ? ANSI_HIGHLIGHT_OFF : "", + arg_order == ORDER_MEMORY ? ANSI_HIGHLIGHT_ON : "", "Memory", arg_order == ORDER_MEMORY ? ANSI_HIGHLIGHT_OFF : "", + arg_order == ORDER_IO ? ANSI_HIGHLIGHT_ON : "", "Input/s", arg_order == ORDER_IO ? ANSI_HIGHLIGHT_OFF : "", + arg_order == ORDER_IO ? ANSI_HIGHLIGHT_ON : "", "Output/s", arg_order == ORDER_IO ? ANSI_HIGHLIGHT_OFF : ""); + + for (j = 0; j < n; j++) { + char *p; + char m[FORMAT_BYTES_MAX]; + + if (j + 5 > rows) + break; + + g = array[j]; + + p = ellipsize(g->path, 37, 33); + printf("%-37s", p ? p : g->path); + free(p); + + if (g->n_tasks_valid) + printf(" %7u", g->n_tasks); + else + fputs(" -", stdout); + + if (g->cpu_valid) + printf(" %6.1f", g->cpu_fraction*100); + else + fputs(" -", stdout); + + if (g->memory_valid) + printf(" %8s", format_bytes(m, sizeof(m), g->memory)); + else + fputs(" -", stdout); + + if (g->io_valid) { + printf(" %8s", + format_bytes(m, sizeof(m), g->io_input_bps)); + printf(" %8s", + format_bytes(m, sizeof(m), g->io_output_bps)); + } else + fputs(" - -", stdout); + + putchar('\n'); + } + + return 0; +} + +static void help(void) { + + printf("%s [OPTIONS...]\n\n" + "Show top control groups by their resource usage.\n\n" + " -h --help Show this help\n" + " -p Order by path\n" + " -t Order by number of tasks\n" + " -c Order by CPU load\n" + " -m Order by memory load\n" + " -i Order by IO load\n" + " -d --delay=DELAY Specify delay\n" + " --depth=DEPTH Maximum traversal depth (default: 2)\n", + program_invocation_short_name); +} + +static int parse_argv(int argc, char *argv[]) { + + enum { + ARG_DEPTH = 0x100 + }; + + static const struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "delay", required_argument, NULL, 'd' }, + { "depth", required_argument, NULL, ARG_DEPTH }, + { NULL, 0, NULL, 0 } + }; + + int c; + int r; + + assert(argc >= 1); + assert(argv); + + while ((c = getopt_long(argc, argv, "hptcmid:", options, NULL)) >= 0) { + + switch (c) { + + case 'h': + help(); + return 0; + + case ARG_DEPTH: + r = safe_atou(optarg, &arg_depth); + if (r < 0) { + log_error("Failed to parse depth parameter."); + return -EINVAL; + } + + break; + + case 'd': + r = parse_usec(optarg, &arg_delay); + if (r < 0 || arg_delay <= 0) { + log_error("Failed to parse delay parameter."); + return -EINVAL; + } + + break; + + case 'p': + arg_order = ORDER_PATH; + break; + + case 't': + arg_order = ORDER_TASKS; + break; + + case 'c': + arg_order = ORDER_CPU; + break; + + case 'm': + arg_order = ORDER_MEMORY; + break; + + case 'i': + arg_order = ORDER_IO; + break; + + case '?': + return -EINVAL; + + default: + log_error("Unknown option code %c", c); + return -EINVAL; + } + } + + if (optind < argc) { + log_error("Too many arguments."); + return -EINVAL; + } + + return 1; +} + +int main(int argc, char *argv[]) { + int r; + Hashmap *a = NULL, *b = NULL; + unsigned iteration = 0; + usec_t last_refresh = 0; + bool quit = false, immediate_refresh = false; + + log_parse_environment(); + log_open(); + + r = parse_argv(argc, argv); + if (r <= 0) + goto finish; + + a = hashmap_new(string_hash_func, string_compare_func); + b = hashmap_new(string_hash_func, string_compare_func); + if (!a || !b) { + log_error("Out of memory"); + r = -ENOMEM; + goto finish; + } + + while (!quit) { + Hashmap *c; + usec_t t; + char key; + char h[FORMAT_TIMESPAN_MAX]; + + t = now(CLOCK_MONOTONIC); + + if (t >= last_refresh + arg_delay || immediate_refresh) { + + r = refresh(a, b, iteration++); + if (r < 0) + goto finish; + + group_hashmap_clear(b); + + c = a; + a = b; + b = c; + + last_refresh = t; + immediate_refresh = false; + } + + r = display(b); + if (r < 0) + goto finish; + + r = read_one_char(stdin, &key, last_refresh + arg_delay - t, NULL); + if (r == -ETIMEDOUT) + continue; + if (r < 0) { + log_error("Couldn't read key: %s", strerror(-r)); + goto finish; + } + + fputs("\r \r", stdout); + fflush(stdout); + + switch (key) { + + case ' ': + immediate_refresh = true; + break; + + case 'q': + quit = true; + break; + + case 'p': + arg_order = ORDER_PATH; + break; + + case 't': + arg_order = ORDER_TASKS; + break; + + case 'c': + arg_order = ORDER_CPU; + break; + + case 'm': + arg_order = ORDER_MEMORY; + break; + + case 'i': + arg_order = ORDER_IO; + break; + + case '+': + if (arg_delay < USEC_PER_SEC) + arg_delay += USEC_PER_MSEC*250; + else + arg_delay += USEC_PER_SEC; + + fprintf(stdout, "\nIncreased delay to %s.", format_timespan(h, sizeof(h), arg_delay)); + fflush(stdout); + sleep(1); + break; + + case '-': + if (arg_delay <= USEC_PER_MSEC*500) + arg_delay = USEC_PER_MSEC*250; + else if (arg_delay < USEC_PER_MSEC*1250) + arg_delay -= USEC_PER_MSEC*250; + else + arg_delay -= USEC_PER_SEC; + + fprintf(stdout, "\nDecreased delay to %s.", format_timespan(h, sizeof(h), arg_delay)); + fflush(stdout); + sleep(1); + break; + + case '?': + case 'h': + fprintf(stdout, + "\t<" ANSI_HIGHLIGHT_ON "P" ANSI_HIGHLIGHT_OFF "> By path; <" ANSI_HIGHLIGHT_ON "T" ANSI_HIGHLIGHT_OFF "> By tasks; <" ANSI_HIGHLIGHT_ON "C" ANSI_HIGHLIGHT_OFF "> By CPU; <" ANSI_HIGHLIGHT_ON "M" ANSI_HIGHLIGHT_OFF "> By memory; <" ANSI_HIGHLIGHT_ON "I" ANSI_HIGHLIGHT_OFF "> By I/O\n" + "\t<" ANSI_HIGHLIGHT_ON "Q" ANSI_HIGHLIGHT_OFF "> Quit; <" ANSI_HIGHLIGHT_ON "+" ANSI_HIGHLIGHT_OFF "> Increase delay; <" ANSI_HIGHLIGHT_ON "-" ANSI_HIGHLIGHT_OFF "> Decrease delay; <" ANSI_HIGHLIGHT_ON "SPACE" ANSI_HIGHLIGHT_OFF "> Refresh"); + fflush(stdout); + sleep(3); + break; + + default: + fprintf(stdout, "\nUnknown key '%c'. Ignoring.", key); + fflush(stdout); + sleep(1); + break; + } + } + + log_info("Exiting."); + + r = 0; + +finish: + group_hashmap_free(a); + group_hashmap_free(b); + + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/condition.c b/src/condition.c index f9202f6850..2b51a16f17 100644 --- a/src/condition.c +++ b/src/condition.c @@ -23,6 +23,7 @@ #include <errno.h> #include <string.h> #include <unistd.h> +#include <sys/capability.h> #ifdef HAVE_SELINUX #include <selinux/selinux.h> @@ -30,24 +31,28 @@ #include "util.h" #include "condition.h" +#include "virt.h" Condition* condition_new(ConditionType type, const char *parameter, bool trigger, bool negate) { Condition *c; assert(type < _CONDITION_TYPE_MAX); - if (!(c = new0(Condition, 1))) + c = new0(Condition, 1); + if (!c) return NULL; c->type = type; c->trigger = trigger; c->negate = negate; - if (parameter) - if (!(c->parameter = strdup(parameter))) { + if (parameter) { + c->parameter = strdup(parameter); + if (!c->parameter) { free(c); return NULL; } + } return c; } @@ -75,10 +80,11 @@ static bool test_kernel_command_line(const char *parameter) { assert(parameter); - if (detect_virtualization(NULL) > 0) + if (detect_container(NULL) > 0) return false; - if ((r = read_one_line_file("/proc/cmdline", &line)) < 0) { + r = read_one_line_file("/proc/cmdline", &line); + if (r < 0) { log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r)); return false; } @@ -89,7 +95,8 @@ static bool test_kernel_command_line(const char *parameter) { FOREACH_WORD_QUOTED(w, l, line, state) { free(word); - if (!(word = strndup(w, l))) + word = strndup(w, l); + if (!word) break; if (equal) { @@ -113,25 +120,36 @@ static bool test_kernel_command_line(const char *parameter) { } static bool test_virtualization(const char *parameter) { - int r, b; + int b; + Virtualization v; const char *id; assert(parameter); - if ((r = detect_virtualization(&id)) < 0) { - log_warning("Failed to detect virtualization, ignoring: %s", strerror(-r)); + v = detect_virtualization(&id); + if (v < 0) { + log_warning("Failed to detect virtualization, ignoring: %s", strerror(-v)); return false; } + /* First, compare with yes/no */ b = parse_boolean(parameter); - if (r > 0 && b > 0) + if (v > 0 && b > 0) + return true; + + if (v == 0 && b == 0) + return true; + + /* Then, compare categorization */ + if (v == VIRTUALIZATION_VM && streq(parameter, "vm")) return true; - if (r == 0 && b == 0) + if (v == VIRTUALIZATION_CONTAINER && streq(parameter, "container")) return true; - return streq(parameter, id); + /* Finally compare id */ + return v > 0 && streq(parameter, id); } static bool test_security(const char *parameter) { @@ -142,6 +160,38 @@ static bool test_security(const char *parameter) { return false; } +static bool test_capability(const char *parameter) { + cap_value_t value; + FILE *f; + char line[LINE_MAX]; + unsigned long long capabilities = (unsigned long long) -1; + + /* If it's an invalid capability, we don't have it */ + + if (cap_from_name(parameter, &value) < 0) + return false; + + /* If it's a valid capability we default to assume + * that we have it */ + + f = fopen("/proc/self/status", "re"); + if (!f) + return true; + + while (fgets(line, sizeof(line), f)) { + truncate_nl(line); + + if (startswith(line, "CapBnd:")) { + (void) sscanf(line+7, "%llx", &capabilities); + break; + } + } + + fclose(f); + + return !!(capabilities & (1ULL << value)); +} + bool condition_test(Condition *c) { assert(c); @@ -156,11 +206,22 @@ bool condition_test(Condition *c) { case CONDITION_PATH_IS_DIRECTORY: { struct stat st; - if (lstat(c->parameter, &st) < 0) - return !c->negate; + if (stat(c->parameter, &st) < 0) + return c->negate; return S_ISDIR(st.st_mode) == !c->negate; } + case CONDITION_PATH_IS_SYMBOLIC_LINK: { + struct stat st; + + if (lstat(c->parameter, &st) < 0) + return c->negate; + return S_ISLNK(st.st_mode) == !c->negate; + } + + case CONDITION_PATH_IS_MOUNT_POINT: + return (path_is_mount_point(c->parameter, true) > 0) == !c->negate; + case CONDITION_DIRECTORY_NOT_EMPTY: { int k; @@ -171,8 +232,8 @@ bool condition_test(Condition *c) { case CONDITION_FILE_IS_EXECUTABLE: { struct stat st; - if (lstat(c->parameter, &st) < 0) - return !c->negate; + if (stat(c->parameter, &st) < 0) + return c->negate; return (S_ISREG(st.st_mode) && (st.st_mode & 0111)) == !c->negate; } @@ -186,6 +247,9 @@ bool condition_test(Condition *c) { case CONDITION_SECURITY: return test_security(c->parameter) == !c->negate; + case CONDITION_CAPABILITY: + return test_capability(c->parameter) == !c->negate; + case CONDITION_NULL: return !c->negate; @@ -247,6 +311,8 @@ static const char* const condition_type_table[_CONDITION_TYPE_MAX] = { [CONDITION_PATH_EXISTS] = "ConditionPathExists", [CONDITION_PATH_EXISTS_GLOB] = "ConditionPathExistsGlob", [CONDITION_PATH_IS_DIRECTORY] = "ConditionPathIsDirectory", + [CONDITION_PATH_IS_SYMBOLIC_LINK] = "ConditionPathIsSymbolicLink", + [CONDITION_PATH_IS_MOUNT_POINT] = "ConditionPathIsMountPoint", [CONDITION_DIRECTORY_NOT_EMPTY] = "ConditionDirectoryNotEmpty", [CONDITION_KERNEL_COMMAND_LINE] = "ConditionKernelCommandLine", [CONDITION_VIRTUALIZATION] = "ConditionVirtualization", diff --git a/src/condition.h b/src/condition.h index 672996e836..71b1c6761e 100644 --- a/src/condition.h +++ b/src/condition.h @@ -30,11 +30,14 @@ typedef enum ConditionType { CONDITION_PATH_EXISTS, CONDITION_PATH_EXISTS_GLOB, CONDITION_PATH_IS_DIRECTORY, + CONDITION_PATH_IS_SYMBOLIC_LINK, + CONDITION_PATH_IS_MOUNT_POINT, CONDITION_DIRECTORY_NOT_EMPTY, CONDITION_FILE_IS_EXECUTABLE, CONDITION_KERNEL_COMMAND_LINE, CONDITION_VIRTUALIZATION, CONDITION_SECURITY, + CONDITION_CAPABILITY, CONDITION_NULL, _CONDITION_TYPE_MAX, _CONDITION_TYPE_INVALID = -1 diff --git a/src/conf-parser.c b/src/conf-parser.c index 02f740a04f..c7dd01aa1d 100644 --- a/src/conf-parser.c +++ b/src/conf-parser.c @@ -31,50 +31,140 @@ #include "strv.h" #include "log.h" +int config_item_table_lookup( + void *table, + const char *section, + const char *lvalue, + ConfigParserCallback *func, + int *ltype, + void **data, + void *userdata) { + + ConfigTableItem *t; + + assert(table); + assert(lvalue); + assert(func); + assert(ltype); + assert(data); + + for (t = table; t->lvalue; t++) { + + if (!streq(lvalue, t->lvalue)) + continue; + + if (!streq_ptr(section, t->section)) + continue; + + *func = t->parse; + *ltype = t->ltype; + *data = t->data; + return 1; + } + + return 0; +} + +int config_item_perf_lookup( + void *table, + const char *section, + const char *lvalue, + ConfigParserCallback *func, + int *ltype, + void **data, + void *userdata) { + + ConfigPerfItemLookup lookup = (ConfigPerfItemLookup) table; + const ConfigPerfItem *p; + + assert(table); + assert(lvalue); + assert(func); + assert(ltype); + assert(data); + + if (!section) + p = lookup(lvalue, strlen(lvalue)); + else { + char *key; + + key = join(section, ".", lvalue, NULL); + if (!key) + return -ENOMEM; + + p = lookup(key, strlen(key)); + free(key); + } + + if (!p) + return 0; + + *func = p->parse; + *ltype = p->ltype; + *data = (uint8_t*) userdata + p->offset; + return 1; +} + /* Run the user supplied parser for an assignment */ static int next_assignment( const char *filename, unsigned line, + ConfigItemLookup lookup, + void *table, const char *section, - const ConfigItem *t, - bool relaxed, const char *lvalue, const char *rvalue, + bool relaxed, void *userdata) { + ConfigParserCallback func = NULL; + int ltype = 0; + void *data = NULL; + int r; + assert(filename); - assert(t); + assert(line > 0); + assert(lookup); assert(lvalue); assert(rvalue); - for (; t->parse || t->lvalue; t++) { - - if (t->lvalue && !streq(lvalue, t->lvalue)) - continue; - - if (t->section && !section) - continue; - - if (t->section && !streq(section, t->section)) - continue; + r = lookup(table, section, lvalue, &func, <ype, &data, userdata); + if (r < 0) + return r; - if (!t->parse) - return 0; + if (r > 0) { + if (func) + return func(filename, line, section, lvalue, ltype, rvalue, data, userdata); - return t->parse(filename, line, section, lvalue, t->ltype, rvalue, t->data, userdata); + return 0; } /* Warn about unknown non-extension fields. */ if (!relaxed && !startswith(lvalue, "X-")) - log_info("[%s:%u] Unknown lvalue '%s' in section '%s'. Ignoring.", filename, line, lvalue, strna(section)); + log_info("[%s:%u] Unknown lvalue '%s' in section '%s'. Ignoring.", filename, line, lvalue, section); return 0; } /* Parse a variable assignment line */ -static int parse_line(const char *filename, unsigned line, char **section, const char* const * sections, const ConfigItem *t, bool relaxed, char *l, void *userdata) { +static int parse_line( + const char *filename, + unsigned line, + const char *sections, + ConfigItemLookup lookup, + void *table, + bool relaxed, + char **section, + char *l, + void *userdata) { + char *e; + assert(filename); + assert(line > 0); + assert(lookup); + assert(l); + l = strstrip(l); if (!*l) @@ -87,10 +177,11 @@ static int parse_line(const char *filename, unsigned line, char **section, const char *fn; int r; - if (!(fn = file_in_same_dir(filename, strstrip(l+9)))) + fn = file_in_same_dir(filename, strstrip(l+9)); + if (!fn) return -ENOMEM; - r = config_parse(fn, NULL, sections, t, relaxed, userdata); + r = config_parse(fn, NULL, sections, lookup, table, relaxed, userdata); free(fn); return r; @@ -108,22 +199,30 @@ static int parse_line(const char *filename, unsigned line, char **section, const return -EBADMSG; } - if (!(n = strndup(l+1, k-2))) + n = strndup(l+1, k-2); + if (!n) return -ENOMEM; - if (!relaxed && sections && !strv_contains((char**) sections, n)) - log_info("[%s:%u] Unknown section '%s'. Ignoring.", filename, line, n); + if (sections && !nulstr_contains(sections, n)) { - free(*section); - *section = n; + if (!relaxed) + log_info("[%s:%u] Unknown section '%s'. Ignoring.", filename, line, n); + + free(n); + *section = NULL; + } else { + free(*section); + *section = n; + } return 0; } - if (sections && (!*section || !strv_contains((char**) sections, *section))) + if (sections && !*section) return 0; - if (!(e = strchr(l, '='))) { + e = strchr(l, '='); + if (!e) { log_error("[%s:%u] Missing '='.", filename, line); return -EBADMSG; } @@ -131,11 +230,28 @@ static int parse_line(const char *filename, unsigned line, char **section, const *e = 0; e++; - return next_assignment(filename, line, *section, t, relaxed, strstrip(l), strstrip(e), userdata); + return next_assignment( + filename, + line, + lookup, + table, + *section, + strstrip(l), + strstrip(e), + relaxed, + userdata); } /* Go through the file and parse each line */ -int config_parse(const char *filename, FILE *f, const char* const * sections, const ConfigItem *t, bool relaxed, void *userdata) { +int config_parse( + const char *filename, + FILE *f, + const char *sections, + ConfigItemLookup lookup, + void *table, + bool relaxed, + void *userdata) { + unsigned line = 0; char *section = NULL; int r; @@ -143,10 +259,11 @@ int config_parse(const char *filename, FILE *f, const char* const * sections, co char *continuation = NULL; assert(filename); - assert(t); + assert(lookup); if (!f) { - if (!(f = fopen(filename, "re"))) { + f = fopen(filename, "re"); + if (!f) { r = -errno; log_error("Failed to open configuration file '%s': %s", filename, strerror(-r)); goto finish; @@ -171,7 +288,8 @@ int config_parse(const char *filename, FILE *f, const char* const * sections, co truncate_nl(l); if (continuation) { - if (!(c = strappend(continuation, l))) { + c = strappend(continuation, l); + if (!c) { r = -ENOMEM; goto finish; } @@ -194,15 +312,26 @@ int config_parse(const char *filename, FILE *f, const char* const * sections, co if (c) continuation = c; - else if (!(continuation = strdup(l))) { - r = -ENOMEM; - goto finish; + else { + continuation = strdup(l); + if (!continuation) { + r = -ENOMEM; + goto finish; + } } continue; } - r = parse_line(filename, ++line, §ion, sections, t, relaxed, p, userdata); + r = parse_line(filename, + ++line, + sections, + lookup, + table, + relaxed, + §ion, + p, + userdata); free(c); if (r < 0) @@ -240,8 +369,8 @@ int config_parse_int( assert(data); if ((r = safe_atoi(rvalue, i)) < 0) { - log_error("[%s:%u] Failed to parse numeric value: %s", filename, line, rvalue); - return r; + log_error("[%s:%u] Failed to parse numeric value, ingoring: %s", filename, line, rvalue); + return 0; } return 0; @@ -266,8 +395,8 @@ int config_parse_long( assert(data); if ((r = safe_atoli(rvalue, i)) < 0) { - log_error("[%s:%u] Failed to parse numeric value: %s", filename, line, rvalue); - return r; + log_error("[%s:%u] Failed to parse numeric value, ignoring: %s", filename, line, rvalue); + return 0; } return 0; @@ -292,8 +421,8 @@ int config_parse_uint64( assert(data); if ((r = safe_atou64(rvalue, u)) < 0) { - log_error("[%s:%u] Failed to parse numeric value: %s", filename, line, rvalue); - return r; + log_error("[%s:%u] Failed to parse numeric value, ignoring: %s", filename, line, rvalue); + return 0; } return 0; @@ -325,7 +454,7 @@ int config_parse_unsigned( return 0; } -int config_parse_size( +int config_parse_bytes_size( const char *filename, unsigned line, const char *section, @@ -336,20 +465,47 @@ int config_parse_size( void *userdata) { size_t *sz = data; - unsigned u; - int r; + off_t o; assert(filename); assert(lvalue); assert(rvalue); assert(data); - if ((r = safe_atou(rvalue, &u)) < 0) { - log_error("[%s:%u] Failed to parse numeric value: %s", filename, line, rvalue); - return r; + if (parse_bytes(rvalue, &o) < 0 || (off_t) (size_t) o != o) { + log_error("[%s:%u] Failed to parse byte value, ignoring: %s", filename, line, rvalue); + return 0; + } + + *sz = (size_t) o; + return 0; +} + + +int config_parse_bytes_off( + const char *filename, + unsigned line, + const char *section, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + off_t *bytes = data; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + assert_cc(sizeof(off_t) == sizeof(uint64_t)); + + if (parse_bytes(rvalue, bytes) < 0) { + log_error("[%s:%u] Failed to parse bytes value, ignoring: %s", filename, line, rvalue); + return 0; } - *sz = (size_t) u; return 0; } @@ -372,8 +528,38 @@ int config_parse_bool( assert(data); if ((k = parse_boolean(rvalue)) < 0) { - log_error("[%s:%u] Failed to parse boolean value: %s", filename, line, rvalue); - return k; + log_error("[%s:%u] Failed to parse boolean value, ignoring: %s", filename, line, rvalue); + return 0; + } + + *b = !!k; + return 0; +} + +int config_parse_tristate( + const char *filename, + unsigned line, + const char *section, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + int k; + int *b = data; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + /* Tristates are like booleans, but can also take the 'default' value, i.e. "-1" */ + + k = parse_boolean(rvalue); + if (k < 0) { + log_error("[%s:%u] Failed to parse boolean value, ignoring: %s", filename, line, rvalue); + return 0; } *b = !!k; @@ -429,8 +615,8 @@ int config_parse_path( assert(data); if (!path_is_absolute(rvalue)) { - log_error("[%s:%u] Not an absolute path: %s", filename, line, rvalue); - return -EINVAL; + log_error("[%s:%u] Not an absolute path, ignoring: %s", filename, line, rvalue); + return 0; } if (!(n = strdup(rvalue))) @@ -539,9 +725,9 @@ int config_parse_path_strv( } if (!path_is_absolute(n[k])) { - log_error("[%s:%u] Not an absolute path: %s", filename, line, rvalue); - r = -EINVAL; - goto fail; + log_error("[%s:%u] Not an absolute path, ignoring: %s", filename, line, rvalue); + free(n[k]); + continue; } path_kill_slashes(n[k]); @@ -563,3 +749,63 @@ fail: return r; } + +int config_parse_usec( + const char *filename, + unsigned line, + const char *section, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + usec_t *usec = data; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + if (parse_usec(rvalue, usec) < 0) { + log_error("[%s:%u] Failed to parse time value, ignoring: %s", filename, line, rvalue); + return 0; + } + + return 0; +} + +int config_parse_mode( + const char *filename, + unsigned line, + const char *section, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + mode_t *m = data; + long l; + char *x = NULL; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + errno = 0; + l = strtol(rvalue, &x, 8); + if (!x || *x || errno) { + log_error("[%s:%u] Failed to parse mode value, ignoring: %s", filename, line, rvalue); + return 0; + } + + if (l < 0000 || l > 07777) { + log_error("[%s:%u] mode value out of range, ignoring: %s", filename, line, rvalue); + return 0; + } + + *m = (mode_t) l; + return 0; +} diff --git a/src/conf-parser.h b/src/conf-parser.h index 51efe00786..be7d708171 100644 --- a/src/conf-parser.h +++ b/src/conf-parser.h @@ -28,33 +28,81 @@ /* An abstract parser for simple, line based, shallow configuration * files consisting of variable assignments only. */ -typedef int (*ConfigParserCallback)(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); - -/* Wraps info for parsing a specific configuration variable */ -typedef struct ConfigItem { - const char *lvalue; /* name of the variable */ - ConfigParserCallback parse; /* Function that is called to parse the variable's value */ - int ltype; /* Distinguish differnt variables passed to the same callback */ - void *data; /* Where to store the variable's data */ - const char *section; -} ConfigItem; - -/* The configuration file parsing routine. Expects a table of - * config_items in *t that is terminated by an item where lvalue is - * NULL */ -int config_parse(const char *filename, FILE *f, const char* const *sections, const ConfigItem *t, bool relaxed, void *userdata); +/* Prototype for a parser for a specific configuration setting */ +typedef int (*ConfigParserCallback)( + const char *filename, + unsigned line, + const char *section, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata); + +/* Wraps information for parsing a specific configuration variable, to + * be stored in a simple array */ +typedef struct ConfigTableItem { + const char *section; /* Section */ + const char *lvalue; /* Name of the variable */ + ConfigParserCallback parse; /* Function that is called to parse the variable's value */ + int ltype; /* Distinguish different variables passed to the same callback */ + void *data; /* Where to store the variable's data */ +} ConfigTableItem; + +/* Wraps information for parsing a specific configuration variable, to + * ve srored in a gperf perfect hashtable */ +typedef struct ConfigPerfItem { + const char *section_and_lvalue; /* Section + "." + name of the variable */ + ConfigParserCallback parse; /* Function that is called to parse the variable's value */ + int ltype; /* Distinguish different variables passed to the same callback */ + size_t offset; /* Offset where to store data, from the beginning of userdata */ +} ConfigPerfItem; + +/* Prototype for a low-level gperf lookup function */ +typedef const ConfigPerfItem* (*ConfigPerfItemLookup)(const char *section_and_lvalue, unsigned length); + +/* Prototype for a generic high-level lookup function */ +typedef int (*ConfigItemLookup)( + void *table, + const char *section, + const char *lvalue, + ConfigParserCallback *func, + int *ltype, + void **data, + void *userdata); + +/* Linear table search implementation of ConfigItemLookup, based on + * ConfigTableItem arrays */ +int config_item_table_lookup(void *table, const char *section, const char *lvalue, ConfigParserCallback *func, int *ltype, void **data, void *userdata); + +/* gperf implementation of ConfigItemLookup, based on gperf + * ConfigPerfItem tables */ +int config_item_perf_lookup(void *table, const char *section, const char *lvalue, ConfigParserCallback *func, int *ltype, void **data, void *userdata); + +int config_parse( + const char *filename, + FILE *f, + const char *sections, /* nulstr */ + ConfigItemLookup lookup, + void *table, + bool relaxed, + void *userdata); /* Generic parsers */ int config_parse_int(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_unsigned(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_long(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_uint64(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_size(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_bytes_size(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_bytes_off(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_bool(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_tristate(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_string(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_path(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_strv(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_path_strv(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_usec(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_mode(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); #define DEFINE_CONFIG_PARSE_ENUM(function,name,type,msg) \ int function( \ diff --git a/src/cryptsetup/Makefile b/src/cryptsetup/Makefile new file mode 120000 index 0000000000..d0b0e8e008 --- /dev/null +++ b/src/cryptsetup/Makefile @@ -0,0 +1 @@ +../Makefile
\ No newline at end of file diff --git a/src/cryptsetup-generator.c b/src/cryptsetup/cryptsetup-generator.c index db8ebdfb18..ba59b49b01 100644 --- a/src/cryptsetup-generator.c +++ b/src/cryptsetup/cryptsetup-generator.c @@ -124,6 +124,9 @@ static int create_disk( streq(password, "/dev/hw_random"))) fprintf(f, "After=systemd-random-seed-load.service\n"); + else + fprintf(f, + "Before=local-fs.target\n"); fprintf(f, "\n[Service]\n" @@ -242,10 +245,12 @@ int main(int argc, char *argv[]) { if (argc > 1) arg_dest = argv[1]; - log_set_target(LOG_TARGET_SYSLOG_OR_KMSG); + log_set_target(LOG_TARGET_AUTO); log_parse_environment(); log_open(); + umask(0022); + if (!(f = fopen("/etc/crypttab", "re"))) { if (errno == ENOENT) diff --git a/src/cryptsetup.c b/src/cryptsetup/cryptsetup.c index c0caf9a930..ac7b6d6c38 100644 --- a/src/cryptsetup.c +++ b/src/cryptsetup/cryptsetup.c @@ -110,7 +110,7 @@ static int parse_one_option(const char *option) { return 0; } - } else + } else if (!streq(option, "none")) log_error("Encountered unknown /etc/crypttab option '%s', ignoring.", option); return 0; @@ -241,6 +241,8 @@ int main(int argc, char *argv[]) { log_parse_environment(); log_open(); + umask(0022); + if (streq(argv[1], "attach")) { uint32_t flags = 0; int k; diff --git a/src/dbus-automount.c b/src/dbus-automount.c index 826842560d..8e45f81fcf 100644 --- a/src/dbus-automount.c +++ b/src/dbus-automount.c @@ -19,6 +19,8 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <errno.h> + #include "dbus-unit.h" #include "dbus-automount.h" #include "dbus-common.h" @@ -27,6 +29,7 @@ " <interface name=\"org.freedesktop.systemd1.Automount\">\n" \ " <property name=\"Where\" type=\"s\" access=\"read\"/>\n" \ " <property name=\"DirectoryMode\" type=\"u\" access=\"read\"/>\n" \ + " <property name=\"Result\" type=\"s\" access=\"read\"/>\n" \ " </interface>\n" #define INTROSPECTION \ @@ -45,13 +48,25 @@ const char bus_automount_interface[] _introspect_("Automount") = BUS_AUTOMOUNT_INTERFACE; +const char bus_automount_invalidating_properties[] = + "Result\0"; + +static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_automount_append_automount_result, automount_result, AutomountResult); + +static const BusProperty bus_automount_properties[] = { + { "Where", bus_property_append_string, "s", offsetof(Automount, where), true }, + { "DirectoryMode", bus_property_append_mode, "u", offsetof(Automount, directory_mode) }, + { "Result", bus_automount_append_automount_result, "s", offsetof(Automount, result) }, + { NULL, } +}; + DBusHandlerResult bus_automount_message_handler(Unit *u, DBusConnection *c, DBusMessage *message) { - const BusProperty properties[] = { - BUS_UNIT_PROPERTIES, - { "org.freedesktop.systemd1.Automount", "Where", bus_property_append_string, "s", u->automount.where }, - { "org.freedesktop.systemd1.Automount", "DirectoryMode", bus_property_append_mode, "u", &u->automount.directory_mode }, - { NULL, NULL, NULL, NULL, NULL } + Automount *am = AUTOMOUNT(u); + const BusBoundProperties bps[] = { + { "org.freedesktop.systemd1.Unit", bus_unit_properties, u }, + { "org.freedesktop.systemd1.Automount", bus_automount_properties, am }, + { NULL, } }; - return bus_default_message_handler(c, message, INTROSPECTION, INTERFACES_LIST, properties); + return bus_default_message_handler(c, message, INTROSPECTION, INTERFACES_LIST, bps); } diff --git a/src/dbus-automount.h b/src/dbus-automount.h index 84b573c8d8..2fc8345048 100644 --- a/src/dbus-automount.h +++ b/src/dbus-automount.h @@ -29,5 +29,6 @@ DBusHandlerResult bus_automount_message_handler(Unit *u, DBusConnection *c, DBusMessage *message); extern const char bus_automount_interface[]; +extern const char bus_automount_invalidating_properties[]; #endif diff --git a/src/dbus-common.c b/src/dbus-common.c index 43729f0372..2905ac3c83 100644 --- a/src/dbus-common.c +++ b/src/dbus-common.c @@ -177,11 +177,11 @@ int bus_connect_system_ssh(const char *user, const char *host, DBusConnection ** assert(user || host); if (user && host) - asprintf(&p, "exec:path=ssh,argv1=-xT,argv2=%s@%s,argv3=systemd-stdio-bridge", user, host); + asprintf(&p, "unixexec:path=ssh,argv1=-xT,argv2=%s@%s,argv3=systemd-stdio-bridge", user, host); else if (user) - asprintf(&p, "exec:path=ssh,argv1=-xT,argv2=%s@localhost,argv3=systemd-stdio-bridge", user); + asprintf(&p, "unixexec:path=ssh,argv1=-xT,argv2=%s@localhost,argv3=systemd-stdio-bridge", user); else if (host) - asprintf(&p, "exec:path=ssh,argv1=-xT,argv2=%s,argv3=systemd-stdio-bridge", host); + asprintf(&p, "unixexec:path=ssh,argv1=-xT,argv2=%s,argv3=systemd-stdio-bridge", host); if (!p) { dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, NULL); @@ -222,7 +222,8 @@ int bus_connect_system_polkit(DBusConnection **_bus, DBusError *error) { if (geteuid() == 0) return bus_connect(DBUS_BUS_SYSTEM, _bus, NULL, error); - if (!(bus = dbus_connection_open_private("exec:path=pkexec,argv1=" SYSTEMD_STDIO_BRIDGE_BINARY_PATH, error))) + bus = dbus_connection_open_private("unixexec:path=pkexec,argv1=" SYSTEMD_STDIO_BRIDGE_BINARY_PATH, error); + if (!bus) return -EIO; dbus_connection_set_exit_on_disconnect(bus, FALSE); @@ -259,7 +260,7 @@ DBusHandlerResult bus_default_message_handler( DBusMessage *message, const char *introspection, const char *interfaces, - const BusProperty *properties) { + const BusBoundProperties *bound_properties) { DBusError error; DBusMessage *reply = NULL; @@ -278,9 +279,12 @@ DBusHandlerResult bus_default_message_handler( if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection, DBUS_TYPE_INVALID)) goto oom; - } else if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Properties", "Get") && properties) { + } else if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Properties", "Get") && bound_properties) { const char *interface, *property; + const BusBoundProperties *bp; const BusProperty *p; + void *data; + DBusMessageIter iter, sub; if (!dbus_message_get_args( message, @@ -290,43 +294,51 @@ DBusHandlerResult bus_default_message_handler( DBUS_TYPE_INVALID)) return bus_send_error_reply(c, message, &error, -EINVAL); - for (p = properties; p->property; p++) - if (streq(p->interface, interface) && streq(p->property, property)) - break; - - if (p->property) { - DBusMessageIter iter, sub; + for (bp = bound_properties; bp->interface; bp++) { + if (!streq(bp->interface, interface)) + continue; - if (!(reply = dbus_message_new_method_return(message))) - goto oom; + for (p = bp->properties; p->property; p++) + if (streq(p->property, property)) + goto get_prop; + } - dbus_message_iter_init_append(reply, &iter); + /* no match */ + if (!nulstr_contains(interfaces, interface)) + dbus_set_error_const(&error, DBUS_ERROR_UNKNOWN_INTERFACE, "Unknown interface"); + else + dbus_set_error_const(&error, DBUS_ERROR_UNKNOWN_PROPERTY, "Unknown property"); - if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT, p->signature, &sub)) - goto oom; + return bus_send_error_reply(c, message, &error, -EINVAL); - if ((r = p->append(&sub, property, (void*) p->data)) < 0) { +get_prop: + reply = dbus_message_new_method_return(message); + if (!reply) + goto oom; - if (r == -ENOMEM) - goto oom; + dbus_message_iter_init_append(reply, &iter); - dbus_message_unref(reply); - return bus_send_error_reply(c, message, NULL, r); - } + if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT, p->signature, &sub)) + goto oom; - if (!dbus_message_iter_close_container(&iter, &sub)) + data = (char*)bp->base + p->offset; + if (p->indirect) + data = *(void**)data; + r = p->append(&sub, property, data); + if (r < 0) { + if (r == -ENOMEM) goto oom; - } else { - if (!nulstr_contains(interfaces, interface)) - dbus_set_error_const(&error, DBUS_ERROR_UNKNOWN_INTERFACE, "Unknown interface"); - else - dbus_set_error_const(&error, DBUS_ERROR_UNKNOWN_PROPERTY, "Unknown property"); - return bus_send_error_reply(c, message, &error, -EINVAL); + dbus_message_unref(reply); + return bus_send_error_reply(c, message, NULL, r); } - } else if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Properties", "GetAll") && properties) { + if (!dbus_message_iter_close_container(&iter, &sub)) + goto oom; + + } else if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Properties", "GetAll") && bound_properties) { const char *interface; + const BusBoundProperties *bp; const BusProperty *p; DBusMessageIter iter, sub, sub2, sub3; @@ -350,36 +362,46 @@ DBusHandlerResult bus_default_message_handler( if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &sub)) goto oom; - for (p = properties; p->property; p++) { - if (interface[0] && !streq(p->interface, interface)) + for (bp = bound_properties; bp->interface; bp++) { + if (interface[0] && !streq(bp->interface, interface)) continue; - if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_DICT_ENTRY, NULL, &sub2) || - !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &p->property) || - !dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, p->signature, &sub3)) - goto oom; - - if ((r = p->append(&sub3, p->property, (void*) p->data)) < 0) { + for (p = bp->properties; p->property; p++) { + void *data; - if (r == -ENOMEM) + if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_DICT_ENTRY, NULL, &sub2) || + !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &p->property) || + !dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, p->signature, &sub3)) goto oom; - dbus_message_unref(reply); - return bus_send_error_reply(c, message, NULL, r); - } + data = (char*)bp->base + p->offset; + if (p->indirect) + data = *(void**)data; + r = p->append(&sub3, p->property, data); + if (r < 0) { + if (r == -ENOMEM) + goto oom; - if (!dbus_message_iter_close_container(&sub2, &sub3) || - !dbus_message_iter_close_container(&sub, &sub2)) - goto oom; + dbus_message_unref(reply); + return bus_send_error_reply(c, message, NULL, r); + } + + if (!dbus_message_iter_close_container(&sub2, &sub3) || + !dbus_message_iter_close_container(&sub, &sub2)) + goto oom; + } } if (!dbus_message_iter_close_container(&iter, &sub)) goto oom; - } else if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Properties", "Set") && properties) { + } else if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Properties", "Set") && bound_properties) { const char *interface, *property; DBusMessageIter iter; + const BusBoundProperties *bp; const BusProperty *p; + DBusMessageIter sub; + char *sig; if (!dbus_message_iter_init(message, &iter) || dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) @@ -398,45 +420,52 @@ DBusHandlerResult bus_default_message_handler( dbus_message_iter_has_next(&iter)) return bus_send_error_reply(c, message, NULL, -EINVAL); - for (p = properties; p->property; p++) - if (streq(p->interface, interface) && streq(p->property, property)) - break; + for (bp = bound_properties; bp->interface; bp++) { + if (!streq(bp->interface, interface)) + continue; - if (p->set) { - DBusMessageIter sub; - char *sig; + for (p = bp->properties; p->property; p++) + if (streq(p->property, property)) + goto set_prop; + } + + /* no match */ + if (!nulstr_contains(interfaces, interface)) + dbus_set_error_const(&error, DBUS_ERROR_UNKNOWN_INTERFACE, "Unknown interface"); + else + dbus_set_error_const(&error, DBUS_ERROR_UNKNOWN_PROPERTY, "Unknown property"); - dbus_message_iter_recurse(&iter, &sub); + return bus_send_error_reply(c, message, &error, -EINVAL); - if (!(sig = dbus_message_iter_get_signature(&sub))) - goto oom; +set_prop: + if (!p->set) { + dbus_set_error_const(&error, DBUS_ERROR_PROPERTY_READ_ONLY, "Property read-only"); + return bus_send_error_reply(c, message, &error, -EINVAL); + } - if (!streq(sig, p->signature)) { - dbus_free(sig); - return bus_send_error_reply(c, message, NULL, -EINVAL); - } + dbus_message_iter_recurse(&iter, &sub); + + sig = dbus_message_iter_get_signature(&sub); + if (!sig) + goto oom; + if (!streq(sig, p->signature)) { dbus_free(sig); + return bus_send_error_reply(c, message, NULL, -EINVAL); + } - if ((r = p->set(&sub, property)) < 0) { - if (r == -ENOMEM) - goto oom; - return bus_send_error_reply(c, message, NULL, r); - } + dbus_free(sig); - if (!(reply = dbus_message_new_method_return(message))) + r = p->set(&sub, property); + if (r < 0) { + if (r == -ENOMEM) goto oom; - } else { - if (p->property) - dbus_set_error_const(&error, DBUS_ERROR_PROPERTY_READ_ONLY, "Property read-only"); - else if (!nulstr_contains(interfaces, interface)) - dbus_set_error_const(&error, DBUS_ERROR_UNKNOWN_INTERFACE, "Unknown interface"); - else - dbus_set_error_const(&error, DBUS_ERROR_UNKNOWN_PROPERTY, "Unknown property"); - - return bus_send_error_reply(c, message, &error, -EINVAL); + return bus_send_error_reply(c, message, NULL, r); } + reply = dbus_message_new_method_return(message); + if (!reply) + goto oom; } else { const char *interface = dbus_message_get_interface(message); @@ -505,6 +534,22 @@ int bus_property_append_bool(DBusMessageIter *i, const char *property, void *dat return 0; } +int bus_property_append_tristate_false(DBusMessageIter *i, const char *property, void *data) { + int *b = data; + dbus_bool_t db; + + assert(i); + assert(property); + assert(b); + + db = *b > 0; + + if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &db)) + return -ENOMEM; + + return 0; +} + int bus_property_append_uint64(DBusMessageIter *i, const char *property, void *data) { assert(i); assert(property); @@ -971,3 +1016,77 @@ int generic_print_property(const char *name, DBusMessageIter *iter, bool all) { return 0; } + +static void release_name_pending_cb(DBusPendingCall *pending, void *userdata) { + DBusMessage *reply; + DBusConnection *bus = userdata; + + assert_se(reply = dbus_pending_call_steal_reply(pending)); + dbus_message_unref(reply); + + dbus_connection_close(bus); +} + +void bus_async_unregister_and_exit(DBusConnection *bus, const char *name) { + DBusMessage *m = NULL; + DBusPendingCall *pending = NULL; + + assert(bus); + + /* We unregister the name here, but we continue to process + * requests, until we get the response for it, so that all + * requests are guaranteed to be processed. */ + + m = dbus_message_new_method_call( + DBUS_SERVICE_DBUS, + DBUS_PATH_DBUS, + DBUS_INTERFACE_DBUS, + "ReleaseName"); + if (!m) + goto oom; + + if (!dbus_message_append_args( + m, + DBUS_TYPE_STRING, + &name, + DBUS_TYPE_INVALID)) + goto oom; + + if (!dbus_connection_send_with_reply(bus, m, &pending, -1)) + goto oom; + + if (!dbus_pending_call_set_notify(pending, release_name_pending_cb, bus, NULL)) + goto oom; + + dbus_message_unref(m); + dbus_pending_call_unref(pending); + + return; + +oom: + log_error("Out of memory"); + + if (pending) { + dbus_pending_call_cancel(pending); + dbus_pending_call_unref(pending); + } + + if (m) + dbus_message_unref(m); +} + +DBusHandlerResult bus_exit_idle_filter(DBusConnection *bus, DBusMessage *m, void *userdata) { + usec_t *remain_until = userdata; + + assert(bus); + assert(m); + assert(remain_until); + + /* Everytime we get a new message we reset out timeout */ + *remain_until = now(CLOCK_MONOTONIC) + DEFAULT_EXIT_USEC; + + if (dbus_message_is_signal(m, DBUS_INTERFACE_LOCAL, "Disconnected")) + dbus_connection_close(bus); + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} diff --git a/src/dbus-common.h b/src/dbus-common.h index aab65639c1..15811a7e50 100644 --- a/src/dbus-common.h +++ b/src/dbus-common.h @@ -96,14 +96,22 @@ typedef int (*BusPropertyCallback)(DBusMessageIter *iter, const char *property, typedef int (*BusPropertySetCallback)(DBusMessageIter *iter, const char *property); typedef struct BusProperty { - const char *interface; /* interface of the property */ const char *property; /* name of the property */ BusPropertyCallback append; /* Function that is called to serialize this property */ const char *signature; - const void *data; /* The data of this property */ + const uint16_t offset; /* Offset from BusBoundProperties::base address to the property data. + * uint16_t is sufficient, because we have no structs too big. + * -Werror=overflow will catch it if this does not hold. */ + bool indirect; /* data is indirect, ie. not base+offset, but *(base+offset) */ BusPropertySetCallback set; /* Optional: Function that is called to set this property */ } BusProperty; +typedef struct BusBoundProperties { + const char *interface; /* interface of the properties */ + const BusProperty *properties; /* array of properties, ended by a NULL-filled element */ + const void *const base; /* base pointer to which the offset must be added to reach data */ +} BusBoundProperties; + DBusHandlerResult bus_send_error_reply( DBusConnection *c, DBusMessage *message, @@ -115,11 +123,12 @@ DBusHandlerResult bus_default_message_handler( DBusMessage *message, const char *introspection, const char *interfaces, - const BusProperty *properties); + const BusBoundProperties *bound_properties); int bus_property_append_string(DBusMessageIter *i, const char *property, void *data); int bus_property_append_strv(DBusMessageIter *i, const char *property, void *data); int bus_property_append_bool(DBusMessageIter *i, const char *property, void *data); +int bus_property_append_tristate_false(DBusMessageIter *i, const char *property, void *data); int bus_property_append_int32(DBusMessageIter *i, const char *property, void *data); int bus_property_append_uint32(DBusMessageIter *i, const char *property, void *data); int bus_property_append_uint64(DBusMessageIter *i, const char *property, void *data); @@ -167,4 +176,8 @@ int bus_iter_get_basic_and_next(DBusMessageIter *iter, int type, void *data, boo int generic_print_property(const char *name, DBusMessageIter *iter, bool all); +void bus_async_unregister_and_exit(DBusConnection *bus, const char *name); + +DBusHandlerResult bus_exit_idle_filter(DBusConnection *bus, DBusMessage *m, void *userdata); + #endif diff --git a/src/dbus-device.c b/src/dbus-device.c index f85ad2daf8..b39fb9d381 100644 --- a/src/dbus-device.c +++ b/src/dbus-device.c @@ -47,12 +47,19 @@ const char bus_device_interface[] _introspect_("Device") = BUS_DEVICE_INTERFACE; const char bus_device_invalidating_properties[] = "SysFSPath\0"; +static const BusProperty bus_device_properties[] = { + { "SysFSPath", bus_property_append_string, "s", offsetof(Device, sysfs), true }, + { NULL, } +}; + + DBusHandlerResult bus_device_message_handler(Unit *u, DBusConnection *c, DBusMessage *message) { - const BusProperty properties[] = { - BUS_UNIT_PROPERTIES, - { "org.freedesktop.systemd1.Device", "SysFSPath", bus_property_append_string, "s", u->device.sysfs }, - { NULL, NULL, NULL, NULL, NULL } + Device *d = DEVICE(u); + const BusBoundProperties bps[] = { + { "org.freedesktop.systemd1.Unit", bus_unit_properties, u }, + { "org.freedesktop.systemd1.Device", bus_device_properties, d }, + { NULL, } }; - return bus_default_message_handler(c, message, INTROSPECTION, INTERFACES_LIST, properties); + return bus_default_message_handler(c, message, INTROSPECTION, INTERFACES_LIST, bps); } diff --git a/src/dbus-execute.c b/src/dbus-execute.c index 6ceffc57a7..1fd2b21336 100644 --- a/src/dbus-execute.c +++ b/src/dbus-execute.c @@ -308,13 +308,14 @@ int bus_execute_append_command(DBusMessageIter *i, const char *property, void *d assert(i); assert(property); - if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "(sasbttuii)", &sub)) + if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "(sasbttttuii)", &sub)) return -ENOMEM; LIST_FOREACH(command, c, c) { char **l; uint32_t pid; int32_t code, status; + dbus_bool_t b; if (!c->path) continue; @@ -332,8 +333,10 @@ int bus_execute_append_command(DBusMessageIter *i, const char *property, void *d code = (int32_t) c->exec_status.code; status = (int32_t) c->exec_status.status; + b = !!c->ignore; + if (!dbus_message_iter_close_container(&sub2, &sub3) || - !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_BOOLEAN, &c->ignore) || + !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_BOOLEAN, &b) || !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_UINT64, &c->exec_status.start_timestamp.realtime) || !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_UINT64, &c->exec_status.start_timestamp.monotonic) || !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_UINT64, &c->exec_status.exit_timestamp.realtime) || @@ -352,3 +355,68 @@ int bus_execute_append_command(DBusMessageIter *i, const char *property, void *d return 0; } + +const BusProperty bus_exec_context_properties[] = { + { "Environment", bus_property_append_strv, "as", offsetof(ExecContext, environment), true }, + { "EnvironmentFiles", bus_execute_append_env_files, "a(sb)", offsetof(ExecContext, environment_files), true }, + { "UMask", bus_property_append_mode, "u", offsetof(ExecContext, umask) }, + { "LimitCPU", bus_execute_append_rlimits, "t", 0 }, + { "LimitFSIZE", bus_execute_append_rlimits, "t", 0 }, + { "LimitDATA", bus_execute_append_rlimits, "t", 0 }, + { "LimitSTACK", bus_execute_append_rlimits, "t", 0 }, + { "LimitCORE", bus_execute_append_rlimits, "t", 0 }, + { "LimitRSS", bus_execute_append_rlimits, "t", 0 }, + { "LimitNOFILE", bus_execute_append_rlimits, "t", 0 }, + { "LimitAS", bus_execute_append_rlimits, "t", 0 }, + { "LimitNPROC", bus_execute_append_rlimits, "t", 0 }, + { "LimitMEMLOCK", bus_execute_append_rlimits, "t", 0 }, + { "LimitLOCKS", bus_execute_append_rlimits, "t", 0 }, + { "LimitSIGPENDING", bus_execute_append_rlimits, "t", 0 }, + { "LimitMSGQUEUE", bus_execute_append_rlimits, "t", 0 }, + { "LimitNICE", bus_execute_append_rlimits, "t", 0 }, + { "LimitRTPRIO", bus_execute_append_rlimits, "t", 0 }, + { "LimitRTTIME", bus_execute_append_rlimits, "t", 0 }, + { "WorkingDirectory", bus_property_append_string, "s", offsetof(ExecContext, working_directory), true }, + { "RootDirectory", bus_property_append_string, "s", offsetof(ExecContext, root_directory), true }, + { "OOMScoreAdjust", bus_execute_append_oom_score_adjust, "i", 0 }, + { "Nice", bus_execute_append_nice, "i", 0 }, + { "IOScheduling", bus_execute_append_ioprio, "i", 0 }, + { "CPUSchedulingPolicy", bus_execute_append_cpu_sched_policy, "i", 0 }, + { "CPUSchedulingPriority", bus_execute_append_cpu_sched_priority, "i", 0 }, + { "CPUAffinity", bus_execute_append_affinity, "ay", 0 }, + { "TimerSlackNSec", bus_execute_append_timer_slack_nsec, "t", 0 }, + { "CPUSchedulingResetOnFork", bus_property_append_bool, "b", offsetof(ExecContext, cpu_sched_reset_on_fork) }, + { "NonBlocking", bus_property_append_bool, "b", offsetof(ExecContext, non_blocking) }, + { "StandardInput", bus_execute_append_input, "s", offsetof(ExecContext, std_input) }, + { "StandardOutput", bus_execute_append_output, "s", offsetof(ExecContext, std_output) }, + { "StandardError", bus_execute_append_output, "s", offsetof(ExecContext, std_error) }, + { "TTYPath", bus_property_append_string, "s", offsetof(ExecContext, tty_path), true }, + { "TTYReset", bus_property_append_bool, "b", offsetof(ExecContext, tty_reset) }, + { "TTYVHangup", bus_property_append_bool, "b", offsetof(ExecContext, tty_vhangup) }, + { "TTYVTDisallocate", bus_property_append_bool, "b", offsetof(ExecContext, tty_vt_disallocate) }, + { "SyslogPriority", bus_property_append_int, "i", offsetof(ExecContext, syslog_priority) }, + { "SyslogIdentifier", bus_property_append_string, "s", offsetof(ExecContext, syslog_identifier), true }, + { "SyslogLevelPrefix", bus_property_append_bool, "b", offsetof(ExecContext, syslog_level_prefix) }, + { "Capabilities", bus_execute_append_capabilities, "s", 0 }, + { "SecureBits", bus_property_append_int, "i", offsetof(ExecContext, secure_bits) }, + { "CapabilityBoundingSet", bus_execute_append_capability_bs, "t", offsetof(ExecContext, capability_bounding_set_drop) }, + { "User", bus_property_append_string, "s", offsetof(ExecContext, user), true }, + { "Group", bus_property_append_string, "s", offsetof(ExecContext, group), true }, + { "SupplementaryGroups", bus_property_append_strv, "as", offsetof(ExecContext, supplementary_groups), true }, + { "TCPWrapName", bus_property_append_string, "s", offsetof(ExecContext, tcpwrap_name), true }, + { "PAMName", bus_property_append_string, "s", offsetof(ExecContext, pam_name), true }, + { "ReadWriteDirectories", bus_property_append_strv, "as", offsetof(ExecContext, read_write_dirs), true }, + { "ReadOnlyDirectories", bus_property_append_strv, "as", offsetof(ExecContext, read_only_dirs), true }, + { "InaccessibleDirectories", bus_property_append_strv, "as", offsetof(ExecContext, inaccessible_dirs), true }, + { "MountFlags", bus_property_append_ul, "t", offsetof(ExecContext, mount_flags) }, + { "PrivateTmp", bus_property_append_bool, "b", offsetof(ExecContext, private_tmp) }, + { "PrivateNetwork", bus_property_append_bool, "b", offsetof(ExecContext, private_network) }, + { "SameProcessGroup", bus_property_append_bool, "b", offsetof(ExecContext, same_pgrp) }, + { "KillMode", bus_execute_append_kill_mode, "s", offsetof(ExecContext, kill_mode) }, + { "KillSignal", bus_property_append_int, "i", offsetof(ExecContext, kill_signal) }, + { "UtmpIdentifier", bus_property_append_string, "s", offsetof(ExecContext, utmp_id), true }, + { "ControlGroupModify", bus_property_append_bool, "b", offsetof(ExecContext, control_group_modify) }, + { "ControlGroupPersistent", bus_property_append_tristate_false, "b", offsetof(ExecContext, control_group_persistent) }, + { "IgnoreSIGPIPE", bus_property_append_bool, "b", offsetof(ExecContext, ignore_sigpipe ) }, + { NULL, } +}; diff --git a/src/dbus-execute.h b/src/dbus-execute.h index 49ad6cb82a..03cd69d126 100644 --- a/src/dbus-execute.h +++ b/src/dbus-execute.h @@ -25,6 +25,7 @@ #include <dbus/dbus.h> #include "manager.h" +#include "dbus-common.h" #define BUS_EXEC_STATUS_INTERFACE(prefix) \ " <property name=\"" prefix "StartTimestamp\" type=\"t\" access=\"read\"/>\n" \ @@ -92,82 +93,18 @@ " <property name=\"KillMode\" type=\"s\" access=\"read\"/>\n" \ " <property name=\"KillSignal\" type=\"i\" access=\"read\"/>\n" \ " <property name=\"UtmpIdentifier\" type=\"s\" access=\"read\"/>\n" \ - " <property name=\"ControlGroupModify\" type=\"b\" access=\"read\"/>\n" + " <property name=\"ControlGroupModify\" type=\"b\" access=\"read\"/>\n" \ + " <property name=\"ControlGroupPersistent\" type=\"b\" access=\"read\"/>\n" \ + " <property name=\"PrivateNetwork\" type=\"b\" access=\"read\"/>\n" \ + " <property name=\"IgnoreSIGPIPE\" type=\"b\" access=\"read\"/>\n" #define BUS_EXEC_COMMAND_INTERFACE(name) \ " <property name=\"" name "\" type=\"a(sasbttuii)\" access=\"read\"/>\n" -#define BUS_EXEC_CONTEXT_PROPERTIES(interface, context) \ - { interface, "Environment", bus_property_append_strv, "as", (context).environment }, \ - { interface, "EnvironmentFiles", bus_execute_append_env_files, "a(sb)", (context).environment_files }, \ - { interface, "UMask", bus_property_append_mode, "u", &(context).umask }, \ - { interface, "LimitCPU", bus_execute_append_rlimits, "t", &(context) }, \ - { interface, "LimitFSIZE", bus_execute_append_rlimits, "t", &(context) }, \ - { interface, "LimitDATA", bus_execute_append_rlimits, "t", &(context) }, \ - { interface, "LimitSTACK", bus_execute_append_rlimits, "t", &(context) }, \ - { interface, "LimitCORE", bus_execute_append_rlimits, "t", &(context) }, \ - { interface, "LimitRSS", bus_execute_append_rlimits, "t", &(context) }, \ - { interface, "LimitNOFILE", bus_execute_append_rlimits, "t", &(context) }, \ - { interface, "LimitAS", bus_execute_append_rlimits, "t", &(context) }, \ - { interface, "LimitNPROC", bus_execute_append_rlimits, "t", &(context) }, \ - { interface, "LimitMEMLOCK", bus_execute_append_rlimits, "t", &(context) }, \ - { interface, "LimitLOCKS", bus_execute_append_rlimits, "t", &(context) }, \ - { interface, "LimitSIGPENDING", bus_execute_append_rlimits, "t", &(context) }, \ - { interface, "LimitMSGQUEUE", bus_execute_append_rlimits, "t", &(context) }, \ - { interface, "LimitNICE", bus_execute_append_rlimits, "t", &(context) }, \ - { interface, "LimitRTPRIO", bus_execute_append_rlimits, "t", &(context) }, \ - { interface, "LimitRTTIME", bus_execute_append_rlimits, "t", &(context) }, \ - { interface, "WorkingDirectory", bus_property_append_string, "s", (context).working_directory }, \ - { interface, "RootDirectory", bus_property_append_string, "s", (context).root_directory }, \ - { interface, "OOMScoreAdjust", bus_execute_append_oom_score_adjust, "i", &(context) }, \ - { interface, "Nice", bus_execute_append_nice, "i", &(context) }, \ - { interface, "IOScheduling", bus_execute_append_ioprio, "i", &(context) }, \ - { interface, "CPUSchedulingPolicy", bus_execute_append_cpu_sched_policy, "i", &(context) }, \ - { interface, "CPUSchedulingPriority", bus_execute_append_cpu_sched_priority, "i", &(context) }, \ - { interface, "CPUAffinity", bus_execute_append_affinity,"ay", &(context) }, \ - { interface, "TimerSlackNSec", bus_execute_append_timer_slack_nsec, "t", &(context) }, \ - { interface, "CPUSchedulingResetOnFork", bus_property_append_bool, "b", &(context).cpu_sched_reset_on_fork }, \ - { interface, "NonBlocking", bus_property_append_bool, "b", &(context).non_blocking }, \ - { interface, "StandardInput", bus_execute_append_input, "s", &(context).std_input }, \ - { interface, "StandardOutput", bus_execute_append_output, "s", &(context).std_output }, \ - { interface, "StandardError", bus_execute_append_output, "s", &(context).std_error }, \ - { interface, "TTYPath", bus_property_append_string, "s", (context).tty_path }, \ - { interface, "TTYReset", bus_property_append_bool, "b", &(context).tty_reset }, \ - { interface, "TTYVHangup", bus_property_append_bool, "b", &(context).tty_vhangup }, \ - { interface, "TTYVTDisallocate", bus_property_append_bool, "b", &(context).tty_vt_disallocate }, \ - { interface, "SyslogPriority", bus_property_append_int, "i", &(context).syslog_priority }, \ - { interface, "SyslogIdentifier", bus_property_append_string, "s", (context).syslog_identifier }, \ - { interface, "SyslogLevelPrefix", bus_property_append_bool, "b", &(context).syslog_level_prefix }, \ - { interface, "Capabilities", bus_execute_append_capabilities, "s",&(context) }, \ - { interface, "SecureBits", bus_property_append_int, "i", &(context).secure_bits }, \ - { interface, "CapabilityBoundingSet", bus_execute_append_capability_bs, "t", &(context).capability_bounding_set_drop }, \ - { interface, "User", bus_property_append_string, "s", (context).user }, \ - { interface, "Group", bus_property_append_string, "s", (context).group }, \ - { interface, "SupplementaryGroups", bus_property_append_strv, "as", (context).supplementary_groups }, \ - { interface, "TCPWrapName", bus_property_append_string, "s", (context).tcpwrap_name }, \ - { interface, "PAMName", bus_property_append_string, "s", (context).pam_name }, \ - { interface, "ReadWriteDirectories", bus_property_append_strv, "as", (context).read_write_dirs }, \ - { interface, "ReadOnlyDirectories", bus_property_append_strv, "as", (context).read_only_dirs }, \ - { interface, "InaccessibleDirectories", bus_property_append_strv, "as", (context).inaccessible_dirs }, \ - { interface, "MountFlags", bus_property_append_ul, "t", &(context).mount_flags }, \ - { interface, "PrivateTmp", bus_property_append_bool, "b", &(context).private_tmp }, \ - { interface, "SameProcessGroup", bus_property_append_bool, "b", &(context).same_pgrp }, \ - { interface, "KillMode", bus_execute_append_kill_mode, "s", &(context).kill_mode }, \ - { interface, "KillSignal", bus_property_append_int, "i", &(context).kill_signal }, \ - { interface, "UtmpIdentifier", bus_property_append_string, "s", (context).utmp_id }, \ - { interface, "ControlGroupModify", bus_property_append_bool, "b", &(context).control_group_modify } +extern const BusProperty bus_exec_context_properties[]; -#define BUS_EXEC_STATUS_PROPERTIES(interface, estatus, prefix) \ - { interface, prefix "StartTimestamp", bus_property_append_usec, "t", &(estatus).start_timestamp.realtime }, \ - { interface, prefix "StartTimestampMonotonic",bus_property_append_usec, "t", &(estatus).start_timestamp.monotonic }, \ - { interface, prefix "ExitTimestamp", bus_property_append_usec, "t", &(estatus).start_timestamp.realtime }, \ - { interface, prefix "ExitTimestampMonotonic", bus_property_append_usec, "t", &(estatus).start_timestamp.monotonic }, \ - { interface, prefix "PID", bus_property_append_pid, "u", &(estatus).pid }, \ - { interface, prefix "Code", bus_property_append_int, "i", &(estatus).code }, \ - { interface, prefix "Status", bus_property_append_int, "i", &(estatus).status } - -#define BUS_EXEC_COMMAND_PROPERTY(interface, command, name) \ - { interface, name, bus_execute_append_command, "a(sasbttttuii)", (command) } +#define BUS_EXEC_COMMAND_PROPERTY(name, command, indirect) \ + { name, bus_execute_append_command, "a(sasbttttuii)", (command), (indirect), NULL } int bus_execute_append_output(DBusMessageIter *i, const char *property, void *data); int bus_execute_append_input(DBusMessageIter *i, const char *property, void *data); diff --git a/src/dbus-job.c b/src/dbus-job.c index 2308be34fd..ab6d610243 100644 --- a/src/dbus-job.c +++ b/src/dbus-job.c @@ -71,7 +71,7 @@ static int bus_job_append_unit(DBusMessageIter *i, const char *property, void *d if (!(p = unit_dbus_path(j->unit))) return -ENOMEM; - if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &j->unit->meta.id) || + if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &j->unit->id) || !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &p)) { free(p); return -ENOMEM; @@ -85,15 +85,15 @@ static int bus_job_append_unit(DBusMessageIter *i, const char *property, void *d return 0; } -static DBusHandlerResult bus_job_message_dispatch(Job *j, DBusConnection *connection, DBusMessage *message) { - const BusProperty properties[] = { - { "org.freedesktop.systemd1.Job", "Id", bus_property_append_uint32, "u", &j->id }, - { "org.freedesktop.systemd1.Job", "State", bus_job_append_state, "s", &j->state }, - { "org.freedesktop.systemd1.Job", "JobType", bus_job_append_type, "s", &j->type }, - { "org.freedesktop.systemd1.Job", "Unit", bus_job_append_unit, "(so)", j }, - { NULL, NULL, NULL, NULL, NULL } - }; +static const BusProperty bus_job_properties[] = { + { "Id", bus_property_append_uint32, "u", offsetof(Job, id) }, + { "State", bus_job_append_state, "s", offsetof(Job, state) }, + { "JobType", bus_job_append_type, "s", offsetof(Job, type) }, + { "Unit", bus_job_append_unit, "(so)", 0 }, + { NULL, } +}; +static DBusHandlerResult bus_job_message_dispatch(Job *j, DBusConnection *connection, DBusMessage *message) { DBusMessage *reply = NULL; if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Job", "Cancel")) { @@ -102,8 +102,13 @@ static DBusHandlerResult bus_job_message_dispatch(Job *j, DBusConnection *connec job_finish_and_invalidate(j, JOB_CANCELED); - } else - return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, properties); + } else { + const BusBoundProperties bps[] = { + { "org.freedesktop.systemd1.Job", bus_job_properties, j }, + { NULL, } + }; + return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, bps); + } if (reply) { if (!dbus_connection_send(connection, reply, NULL)) diff --git a/src/dbus-manager.c b/src/dbus-manager.c index 66cf9ee67a..6d272cb321 100644 --- a/src/dbus-manager.c +++ b/src/dbus-manager.c @@ -145,7 +145,7 @@ " <arg name=\"files\" type=\"as\" direction=\"in\"/>\n" \ " <arg name=\"runtime\" type=\"b\" direction=\"in\"/>\n" \ " <arg name=\"force\" type=\"b\" direction=\"in\"/>\n" \ - " <arg name=\"carries_install_info\" type=\"b\" directrion=\"out\"/>\n" \ + " <arg name=\"carries_install_info\" type=\"b\" direction=\"out\"/>\n" \ " <arg name=\"changes\" type=\"a(sss)\" direction=\"out\"/>\n" \ " </method>\n" \ " <method name=\"DisableUnitFiles\">\n" \ @@ -157,7 +157,7 @@ " <arg name=\"files\" type=\"as\" direction=\"in\"/>\n" \ " <arg name=\"runtime\" type=\"b\" direction=\"in\"/>\n" \ " <arg name=\"force\" type=\"b\" direction=\"in\"/>\n" \ - " <arg name=\"carries_install_info\" type=\"b\" directrion=\"out\"/>\n" \ + " <arg name=\"carries_install_info\" type=\"b\" direction=\"out\"/>\n" \ " <arg name=\"changes\" type=\"a(sss)\" direction=\"out\"/>\n" \ " </method>\n" \ " <method name=\"LinkUnitFiles\">\n" \ @@ -170,7 +170,7 @@ " <arg name=\"files\" type=\"as\" direction=\"in\"/>\n" \ " <arg name=\"runtime\" type=\"b\" direction=\"in\"/>\n" \ " <arg name=\"force\" type=\"b\" direction=\"in\"/>\n" \ - " <arg name=\"carries_install_info\" type=\"b\" directrion=\"out\"/>\n" \ + " <arg name=\"carries_install_info\" type=\"b\" direction=\"out\"/>\n" \ " <arg name=\"changes\" type=\"a(sss)\" direction=\"out\"/>\n" \ " </method>\n" \ " <method name=\"MaskUnitFiles\">\n" \ @@ -300,7 +300,7 @@ static int bus_manager_append_tainted(DBusMessageIter *i, const char *property, free(p); if (access("/proc/cgroups", F_OK) < 0) - e = stpcpy(e, "cgroups-missing "); + stpcpy(e, "cgroups-missing "); t = strstrip(buf); @@ -435,8 +435,6 @@ static DBusMessage *message_from_file_changes( DBusMessage *reply; unsigned i; - assert(changes); - reply = dbus_message_new_method_return(m); if (!reply) return NULL; @@ -446,7 +444,7 @@ static DBusMessage *message_from_file_changes( if (carries_install_info >= 0) { dbus_bool_t b; - b = carries_install_info; + b = !!carries_install_info; if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_BOOLEAN, &b)) goto oom; } @@ -493,46 +491,52 @@ static int bus_manager_send_unit_files_changed(Manager *m) { return r; } -static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection, DBusMessage *message, void *data) { - Manager *m = data; +static const char systemd_property_string[] = PACKAGE_STRING "\0" DISTRIBUTION "\0" SYSTEMD_FEATURES; + +static const BusProperty bus_systemd_properties[] = { + { "Version", bus_property_append_string, "s", 0 }, + { "Distribution", bus_property_append_string, "s", sizeof(PACKAGE_STRING) }, + { "Features", bus_property_append_string, "s", sizeof(PACKAGE_STRING) + sizeof(DISTRIBUTION) }, + { NULL, } +}; - const BusProperty properties[] = { - { "org.freedesktop.systemd1.Manager", "Version", bus_property_append_string, "s", PACKAGE_STRING }, - { "org.freedesktop.systemd1.Manager", "Distribution", bus_property_append_string, "s", DISTRIBUTION }, - { "org.freedesktop.systemd1.Manager", "Features", bus_property_append_string, "s", SYSTEMD_FEATURES }, - { "org.freedesktop.systemd1.Manager", "RunningAs", bus_manager_append_running_as, "s", &m->running_as }, - { "org.freedesktop.systemd1.Manager", "Tainted", bus_manager_append_tainted, "s", m }, - { "org.freedesktop.systemd1.Manager", "InitRDTimestamp", bus_property_append_uint64, "t", &m->initrd_timestamp.realtime }, - { "org.freedesktop.systemd1.Manager", "InitRDTimestampMonotonic", bus_property_append_uint64, "t", &m->initrd_timestamp.monotonic }, - { "org.freedesktop.systemd1.Manager", "StartupTimestamp", bus_property_append_uint64, "t", &m->startup_timestamp.realtime }, - { "org.freedesktop.systemd1.Manager", "StartupTimestampMonotonic", bus_property_append_uint64, "t", &m->startup_timestamp.monotonic }, - { "org.freedesktop.systemd1.Manager", "FinishTimestamp", bus_property_append_uint64, "t", &m->finish_timestamp.realtime }, - { "org.freedesktop.systemd1.Manager", "FinishTimestampMonotonic", bus_property_append_uint64, "t",&m->finish_timestamp.monotonic }, - { "org.freedesktop.systemd1.Manager", "LogLevel", bus_manager_append_log_level, "s", m, bus_manager_set_log_level }, - { "org.freedesktop.systemd1.Manager", "LogTarget", bus_manager_append_log_target, "s", m, bus_manager_set_log_target }, - { "org.freedesktop.systemd1.Manager", "NNames", bus_manager_append_n_names, "u", m }, - { "org.freedesktop.systemd1.Manager", "NJobs", bus_manager_append_n_jobs, "u", m }, - { "org.freedesktop.systemd1.Manager", "NInstalledJobs",bus_property_append_uint32, "u", &m->n_installed_jobs }, - { "org.freedesktop.systemd1.Manager", "NFailedJobs", bus_property_append_uint32, "u", &m->n_failed_jobs }, - { "org.freedesktop.systemd1.Manager", "Progress", bus_manager_append_progress, "d", m }, - { "org.freedesktop.systemd1.Manager", "Environment", bus_property_append_strv, "as", m->environment }, - { "org.freedesktop.systemd1.Manager", "ConfirmSpawn", bus_property_append_bool, "b", &m->confirm_spawn }, - { "org.freedesktop.systemd1.Manager", "ShowStatus", bus_property_append_bool, "b", &m->show_status }, - { "org.freedesktop.systemd1.Manager", "UnitPath", bus_property_append_strv, "as", m->lookup_paths.unit_path }, - { "org.freedesktop.systemd1.Manager", "NotifySocket", bus_property_append_string, "s", m->notify_socket }, - { "org.freedesktop.systemd1.Manager", "ControlGroupHierarchy", bus_property_append_string, "s", m->cgroup_hierarchy }, - { "org.freedesktop.systemd1.Manager", "MountAuto", bus_property_append_bool, "b", &m->mount_auto }, - { "org.freedesktop.systemd1.Manager", "SwapAuto", bus_property_append_bool, "b", &m->swap_auto }, - { "org.freedesktop.systemd1.Manager", "DefaultControllers", bus_property_append_strv, "as", m->default_controllers }, - { "org.freedesktop.systemd1.Manager", "DefaultStandardOutput", bus_manager_append_exec_output, "s", &m->default_std_output }, - { "org.freedesktop.systemd1.Manager", "DefaultStandardError", bus_manager_append_exec_output, "s", &m->default_std_error }, +static const BusProperty bus_manager_properties[] = { + { "RunningAs", bus_manager_append_running_as, "s", offsetof(Manager, running_as) }, + { "Tainted", bus_manager_append_tainted, "s", 0 }, + { "InitRDTimestamp", bus_property_append_uint64, "t", offsetof(Manager, initrd_timestamp.realtime) }, + { "InitRDTimestampMonotonic", bus_property_append_uint64, "t", offsetof(Manager, initrd_timestamp.monotonic) }, + { "StartupTimestamp", bus_property_append_uint64, "t", offsetof(Manager, startup_timestamp.realtime) }, + { "StartupTimestampMonotonic", bus_property_append_uint64, "t", offsetof(Manager, startup_timestamp.monotonic) }, + { "FinishTimestamp", bus_property_append_uint64, "t", offsetof(Manager, finish_timestamp.realtime) }, + { "FinishTimestampMonotonic", bus_property_append_uint64, "t", offsetof(Manager, finish_timestamp.monotonic) }, + { "LogLevel", bus_manager_append_log_level, "s", 0, 0, bus_manager_set_log_level }, + { "LogTarget", bus_manager_append_log_target, "s", 0, 0, bus_manager_set_log_target }, + { "NNames", bus_manager_append_n_names, "u", 0 }, + { "NJobs", bus_manager_append_n_jobs, "u", 0 }, + { "NInstalledJobs",bus_property_append_uint32, "u", offsetof(Manager, n_installed_jobs) }, + { "NFailedJobs", bus_property_append_uint32, "u", offsetof(Manager, n_failed_jobs) }, + { "Progress", bus_manager_append_progress, "d", 0 }, + { "Environment", bus_property_append_strv, "as", offsetof(Manager, environment), true }, + { "ConfirmSpawn", bus_property_append_bool, "b", offsetof(Manager, confirm_spawn) }, + { "ShowStatus", bus_property_append_bool, "b", offsetof(Manager, show_status) }, + { "UnitPath", bus_property_append_strv, "as", offsetof(Manager, lookup_paths.unit_path), true }, + { "NotifySocket", bus_property_append_string, "s", offsetof(Manager, notify_socket), true }, + { "ControlGroupHierarchy", bus_property_append_string, "s", offsetof(Manager, cgroup_hierarchy), true }, + { "MountAuto", bus_property_append_bool, "b", offsetof(Manager, mount_auto) }, + { "SwapAuto", bus_property_append_bool, "b", offsetof(Manager, swap_auto) }, + { "DefaultControllers", bus_property_append_strv, "as", offsetof(Manager, default_controllers), true }, + { "DefaultStandardOutput", bus_manager_append_exec_output, "s", offsetof(Manager, default_std_output) }, + { "DefaultStandardError", bus_manager_append_exec_output, "s", offsetof(Manager, default_std_error) }, #ifdef HAVE_SYSV_COMPAT - { "org.freedesktop.systemd1.Manager", "SysVConsole", bus_property_append_bool, "b", &m->sysv_console }, - { "org.freedesktop.systemd1.Manager", "SysVInitPath", bus_property_append_strv, "as", m->lookup_paths.sysvinit_path }, - { "org.freedesktop.systemd1.Manager", "SysVRcndPath", bus_property_append_strv, "as", m->lookup_paths.sysvrcnd_path }, + { "SysVConsole", bus_property_append_bool, "b", offsetof(Manager, sysv_console) }, + { "SysVInitPath", bus_property_append_strv, "as", offsetof(Manager, lookup_paths.sysvinit_path), true }, + { "SysVRcndPath", bus_property_append_strv, "as", offsetof(Manager, lookup_paths.sysvrcnd_path), true }, #endif - { NULL, NULL, NULL, NULL, NULL } - }; + { NULL, } +}; + +static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection, DBusMessage *message, void *data) { + Manager *m = data; int r; DBusError error; @@ -779,39 +783,39 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection, uint32_t job_id; Unit *f; - if (k != u->meta.id) + if (k != u->id) continue; if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2)) goto oom; description = unit_description(u); - load_state = unit_load_state_to_string(u->meta.load_state); + load_state = unit_load_state_to_string(u->load_state); active_state = unit_active_state_to_string(unit_active_state(u)); sub_state = unit_sub_state_to_string(u); f = unit_following(u); - following = f ? f->meta.id : ""; + following = f ? f->id : ""; if (!(u_path = unit_dbus_path(u))) goto oom; - if (u->meta.job) { - job_id = (uint32_t) u->meta.job->id; + if (u->job) { + job_id = (uint32_t) u->job->id; - if (!(j_path = job_dbus_path(u->meta.job))) { + if (!(j_path = job_dbus_path(u->job))) { free(u_path); goto oom; } - sjob_type = job_type_to_string(u->meta.job->type); + sjob_type = job_type_to_string(u->job->type); } else { job_id = 0; j_path = u_path; sjob_type = ""; } - if (!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &u->meta.id) || + if (!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &u->id) || !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &description) || !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &load_state) || !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &active_state) || @@ -822,13 +826,13 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection, !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &sjob_type) || !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &j_path)) { free(u_path); - if (u->meta.job) + if (u->job) free(j_path); goto oom; } free(u_path); - if (u->meta.job) + if (u->job) free(j_path); if (!dbus_message_iter_close_container(&sub, &sub2)) @@ -873,7 +877,7 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection, } if (!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_UINT32, &id) || - !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &j->unit->meta.id) || + !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &j->unit->id) || !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &type) || !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &state) || !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &j_path) || @@ -1015,7 +1019,7 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection, HASHMAP_FOREACH_KEY(u, k, m->units, i) { char *p; - if (k != u->meta.id) + if (k != u->id) continue; if (!(p = bus_path_escape(k))) { @@ -1320,7 +1324,7 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection, UnitFileChange *changes = NULL; unsigned n_changes = 0; dbus_bool_t runtime, force; - bool carries_install_info = -1; + int carries_install_info = -1; if (!dbus_message_iter_init(message, &iter)) goto oom; @@ -1418,8 +1422,14 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection, if (!reply) goto oom; - } else - return bus_default_message_handler(connection, message, NULL, INTERFACES_LIST, properties); + } else { + const BusBoundProperties bps[] = { + { "org.freedesktop.systemd1.Manager", bus_systemd_properties, systemd_property_string }, + { "org.freedesktop.systemd1.Manager", bus_manager_properties, m }, + { NULL, } + }; + return bus_default_message_handler(connection, message, NULL, INTERFACES_LIST, bps); + } if (job_type != _JOB_TYPE_INVALID) { const char *name, *smode, *old_name = NULL; @@ -1449,8 +1459,8 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection, if (old_name) if (!(u = manager_get_unit(m, old_name)) || - !u->meta.job || - u->meta.job->type != JOB_START) { + !u->job || + u->job->type != JOB_START) { dbus_set_error(&error, BUS_ERROR_NO_SUCH_JOB, "No job queued for unit %s", old_name); return bus_send_error_reply(connection, message, &error, -ENOENT); } @@ -1471,10 +1481,10 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection, job_type = JOB_RELOAD; } - if ((job_type == JOB_START && u->meta.refuse_manual_start) || - (job_type == JOB_STOP && u->meta.refuse_manual_stop) || + if ((job_type == JOB_START && u->refuse_manual_start) || + (job_type == JOB_STOP && u->refuse_manual_stop) || ((job_type == JOB_RESTART || job_type == JOB_TRY_RESTART) && - (u->meta.refuse_manual_start || u->meta.refuse_manual_stop))) { + (u->refuse_manual_start || u->refuse_manual_stop))) { dbus_set_error(&error, BUS_ERROR_ONLY_BY_DEPENDENCY, "Operation refused, may be requested by dependency only."); return bus_send_error_reply(connection, message, &error, -EPERM); } diff --git a/src/dbus-mount.c b/src/dbus-mount.c index cfbfd4531c..35d6ea7a1d 100644 --- a/src/dbus-mount.c +++ b/src/dbus-mount.c @@ -39,6 +39,7 @@ BUS_EXEC_CONTEXT_INTERFACE \ " <property name=\"ControlPID\" type=\"u\" access=\"read\"/>\n" \ " <property name=\"DirectoryMode\" type=\"u\" access=\"read\"/>\n" \ + " <property name=\"Result\" type=\"s\" access=\"read\"/>\n" \ " </interface>\n" #define INTROSPECTION \ @@ -64,7 +65,8 @@ const char bus_mount_invalidating_properties[] = "ExecMount\0" "ExecUnmount\0" "ExecRemount\0" - "ControlPID\0"; + "ControlPID\0" + "Result\0"; static int bus_mount_append_what(DBusMessageIter *i, const char *property, void *data) { Mount *m = data; @@ -135,23 +137,32 @@ static int bus_mount_append_type(DBusMessageIter *i, const char *property, void return 0; } +static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_mount_append_mount_result, mount_result, MountResult); + +static const BusProperty bus_mount_properties[] = { + { "Where", bus_property_append_string, "s", offsetof(Mount, where), true }, + { "What", bus_mount_append_what, "s", 0 }, + { "Options", bus_mount_append_options, "s", 0 }, + { "Type", bus_mount_append_type, "s", 0 }, + { "TimeoutUSec", bus_property_append_usec, "t", offsetof(Mount, timeout_usec) }, + BUS_EXEC_COMMAND_PROPERTY("ExecMount", offsetof(Mount, exec_command[MOUNT_EXEC_MOUNT]), false), + BUS_EXEC_COMMAND_PROPERTY("ExecUnmount", offsetof(Mount, exec_command[MOUNT_EXEC_UNMOUNT]), false), + BUS_EXEC_COMMAND_PROPERTY("ExecRemount", offsetof(Mount, exec_command[MOUNT_EXEC_REMOUNT]), false), + { "ControlPID", bus_property_append_pid, "u", offsetof(Mount, control_pid) }, + { "DirectoryMode", bus_property_append_mode, "u", offsetof(Mount, directory_mode) }, + { "Result", bus_mount_append_mount_result, "s", offsetof(Mount, result) }, + { NULL, } +}; + DBusHandlerResult bus_mount_message_handler(Unit *u, DBusConnection *c, DBusMessage *message) { + Mount *m = MOUNT(u); - const BusProperty properties[] = { - BUS_UNIT_PROPERTIES, - { "org.freedesktop.systemd1.Mount", "Where", bus_property_append_string, "s", u->mount.where }, - { "org.freedesktop.systemd1.Mount", "What", bus_mount_append_what, "s", u }, - { "org.freedesktop.systemd1.Mount", "Options", bus_mount_append_options, "s", u }, - { "org.freedesktop.systemd1.Mount", "Type", bus_mount_append_type, "s", u }, - { "org.freedesktop.systemd1.Mount", "TimeoutUSec", bus_property_append_usec, "t", &u->mount.timeout_usec }, - BUS_EXEC_COMMAND_PROPERTY("org.freedesktop.systemd1.Mount", u->mount.exec_command+MOUNT_EXEC_MOUNT, "ExecMount"), - BUS_EXEC_COMMAND_PROPERTY("org.freedesktop.systemd1.Mount", u->mount.exec_command+MOUNT_EXEC_UNMOUNT, "ExecUnmount"), - BUS_EXEC_COMMAND_PROPERTY("org.freedesktop.systemd1.Mount", u->mount.exec_command+MOUNT_EXEC_REMOUNT, "ExecRemount"), - BUS_EXEC_CONTEXT_PROPERTIES("org.freedesktop.systemd1.Mount", u->mount.exec_context), - { "org.freedesktop.systemd1.Mount", "ControlPID", bus_property_append_pid, "u", &u->mount.control_pid }, - { "org.freedesktop.systemd1.Mount", "DirectoryMode", bus_property_append_mode, "u", &u->mount.directory_mode }, - { NULL, NULL, NULL, NULL, NULL } + const BusBoundProperties bps[] = { + { "org.freedesktop.systemd1.Unit", bus_unit_properties, u }, + { "org.freedesktop.systemd1.Mount", bus_mount_properties, m }, + { "org.freedesktop.systemd1.Mount", bus_exec_context_properties, &m->exec_context }, + { NULL, } }; - return bus_default_message_handler(c, message, INTROSPECTION, INTERFACES_LIST, properties); + return bus_default_message_handler(c, message, INTROSPECTION, INTERFACES_LIST, bps ); } diff --git a/src/dbus-path.c b/src/dbus-path.c index 15238798ae..5506784c38 100644 --- a/src/dbus-path.c +++ b/src/dbus-path.c @@ -32,6 +32,7 @@ " <property name=\"Paths\" type=\"a(ss)\" access=\"read\"/>\n" \ " <property name=\"MakeDirectory\" type=\"b\" access=\"read\"/>\n" \ " <property name=\"DirectoryMode\" type=\"u\" access=\"read\"/>\n" \ + " <property name=\"Result\" type=\"s\" access=\"read\"/>\n" \ " </interface>\n" #define INTROSPECTION \ @@ -50,6 +51,9 @@ const char bus_path_interface[] _introspect_("Path") = BUS_PATH_INTERFACE; +const char bus_path_invalidating_properties[] = + "Result\0"; + static int bus_path_append_paths(DBusMessageIter *i, const char *property, void *data) { Path *p = data; DBusMessageIter sub, sub2; @@ -80,26 +84,36 @@ static int bus_path_append_paths(DBusMessageIter *i, const char *property, void static int bus_path_append_unit(DBusMessageIter *i, const char *property, void *data) { Unit *u = data; + Path *p = PATH(u); const char *t; assert(i); assert(property); assert(u); - t = u->path.unit ? u->path.unit->meta.id : ""; + t = UNIT_DEREF(p->unit) ? UNIT_DEREF(p->unit)->id : ""; return dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &t) ? 0 : -ENOMEM; } +static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_path_append_path_result, path_result, PathResult); + +static const BusProperty bus_path_properties[] = { + { "Unit", bus_path_append_unit, "s", 0 }, + { "Paths", bus_path_append_paths, "a(ss)", 0 }, + { "MakeDirectory", bus_property_append_bool, "b", offsetof(Path, make_directory) }, + { "DirectoryMode", bus_property_append_mode, "u", offsetof(Path, directory_mode) }, + { "Result", bus_path_append_path_result, "s", offsetof(Path, result) }, + { NULL, } +}; + DBusHandlerResult bus_path_message_handler(Unit *u, DBusConnection *c, DBusMessage *message) { - const BusProperty properties[] = { - BUS_UNIT_PROPERTIES, - { "org.freedesktop.systemd1.Path", "Unit", bus_path_append_unit, "s", u }, - { "org.freedesktop.systemd1.Path", "Paths", bus_path_append_paths, "a(ss)", u }, - { "org.freedesktop.systemd1.Path", "MakeDirectory", bus_property_append_bool, "b", &u->path.make_directory }, - { "org.freedesktop.systemd1.Path", "DirectoryMode", bus_property_append_mode, "u", &u->path.directory_mode }, - { NULL, NULL, NULL, NULL, NULL } + Path *p = PATH(u); + const BusBoundProperties bps[] = { + { "org.freedesktop.systemd1.Unit", bus_unit_properties, u }, + { "org.freedesktop.systemd1.Path", bus_path_properties, p }, + { NULL, } }; - return bus_default_message_handler(c, message, INTROSPECTION, INTERFACES_LIST, properties); + return bus_default_message_handler(c, message, INTROSPECTION, INTERFACES_LIST, bps); } diff --git a/src/dbus-path.h b/src/dbus-path.h index f825e13f24..2888400a13 100644 --- a/src/dbus-path.h +++ b/src/dbus-path.h @@ -30,4 +30,6 @@ DBusHandlerResult bus_path_message_handler(Unit *u, DBusConnection *c, DBusMessa extern const char bus_path_interface[]; +extern const char bus_path_invalidating_properties[]; + #endif diff --git a/src/dbus-service.c b/src/dbus-service.c index 3486623e59..780916419b 100644 --- a/src/dbus-service.c +++ b/src/dbus-service.c @@ -43,6 +43,12 @@ " <property name=\"NotifyAccess\" type=\"s\" access=\"read\"/>\n" \ " <property name=\"RestartUSec\" type=\"t\" access=\"read\"/>\n" \ " <property name=\"TimeoutUSec\" type=\"t\" access=\"read\"/>\n" \ + " <property name=\"WatchdogUSec\" type=\"t\" access=\"read\"/>\n" \ + " <property name=\"WatchdogTimestamp\" type=\"t\" access=\"read\"/>\n" \ + " <property name=\"WatchdogTimestampMonotonic\" type=\"t\" access=\"read\"/>\n" \ + " <property name=\"StartLimitInterval\" type=\"t\" access=\"read\"/>\n" \ + " <property name=\"StartLimitBurst\" type=\"u\" access=\"read\"/>\n" \ + " <property name=\"StartLimitAction\" type=\"s\" access=\"read\"/>\n" \ BUS_EXEC_COMMAND_INTERFACE("ExecStartPre") \ BUS_EXEC_COMMAND_INTERFACE("ExecStart") \ BUS_EXEC_COMMAND_INTERFACE("ExecStartPost") \ @@ -59,8 +65,8 @@ " <property name=\"BusName\" type=\"s\" access=\"read\"/>\n" \ " <property name=\"StatusText\" type=\"s\" access=\"read\"/>\n" \ " <property name=\"FsckPassNo\" type=\"i\" access=\"read\"/>\n" \ - " <property name=\"Sockets\" type=\"as\" access=\"read\"/>\n" \ - BUS_SERVICE_SYSV_INTERFACE_FRAGMENT \ + " <property name=\"Result\" type=\"s\" access=\"read\"/>\n" \ + BUS_SERVICE_SYSV_INTERFACE_FRAGMENT \ " </interface>\n" #define INTROSPECTION \ @@ -87,48 +93,76 @@ const char bus_service_invalidating_properties[] = "ExecStop\0" "ExecStopPost\0" "ExecMain\0" + "WatchdogTimestamp\0" + "WatchdogTimestampMonotonic\0" "MainPID\0" "ControlPID\0" - "StatusText\0"; + "StatusText\0" + "Result\0"; static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_service_append_type, service_type, ServiceType); static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_service_append_restart, service_restart, ServiceRestart); static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_service_append_notify_access, notify_access, NotifyAccess); - -DBusHandlerResult bus_service_message_handler(Unit *u, DBusConnection *connection, DBusMessage *message) { - const BusProperty properties[] = { - BUS_UNIT_PROPERTIES, - { "org.freedesktop.systemd1.Service", "Type", bus_service_append_type, "s", &u->service.type }, - { "org.freedesktop.systemd1.Service", "Restart", bus_service_append_restart, "s", &u->service.restart }, - { "org.freedesktop.systemd1.Service", "PIDFile", bus_property_append_string, "s", u->service.pid_file }, - { "org.freedesktop.systemd1.Service", "NotifyAccess", bus_service_append_notify_access, "s", &u->service.notify_access }, - { "org.freedesktop.systemd1.Service", "RestartUSec", bus_property_append_usec, "t", &u->service.restart_usec }, - { "org.freedesktop.systemd1.Service", "TimeoutUSec", bus_property_append_usec, "t", &u->service.timeout_usec }, - BUS_EXEC_COMMAND_PROPERTY("org.freedesktop.systemd1.Service", u->service.exec_command[SERVICE_EXEC_START_PRE], "ExecStartPre"), - BUS_EXEC_COMMAND_PROPERTY("org.freedesktop.systemd1.Service", u->service.exec_command[SERVICE_EXEC_START], "ExecStart"), - BUS_EXEC_COMMAND_PROPERTY("org.freedesktop.systemd1.Service", u->service.exec_command[SERVICE_EXEC_START_POST], "ExecStartPost"), - BUS_EXEC_COMMAND_PROPERTY("org.freedesktop.systemd1.Service", u->service.exec_command[SERVICE_EXEC_RELOAD], "ExecReload"), - BUS_EXEC_COMMAND_PROPERTY("org.freedesktop.systemd1.Service", u->service.exec_command[SERVICE_EXEC_STOP], "ExecStop"), - BUS_EXEC_COMMAND_PROPERTY("org.freedesktop.systemd1.Service", u->service.exec_command[SERVICE_EXEC_STOP_POST], "ExecStopPost"), - BUS_EXEC_CONTEXT_PROPERTIES("org.freedesktop.systemd1.Service", u->service.exec_context), - { "org.freedesktop.systemd1.Service", "PermissionsStartOnly", bus_property_append_bool, "b", &u->service.permissions_start_only }, - { "org.freedesktop.systemd1.Service", "RootDirectoryStartOnly", bus_property_append_bool, "b", &u->service.root_directory_start_only }, - { "org.freedesktop.systemd1.Service", "RemainAfterExit", bus_property_append_bool, "b", &u->service.remain_after_exit }, - { "org.freedesktop.systemd1.Service", "GuessMainPID", bus_property_append_bool, "b", &u->service.guess_main_pid }, - BUS_EXEC_STATUS_PROPERTIES("org.freedesktop.systemd1.Service", u->service.main_exec_status, "ExecMain"), - { "org.freedesktop.systemd1.Service", "MainPID", bus_property_append_pid, "u", &u->service.main_pid }, - { "org.freedesktop.systemd1.Service", "ControlPID", bus_property_append_pid, "u", &u->service.control_pid }, - { "org.freedesktop.systemd1.Service", "BusName", bus_property_append_string, "s", u->service.bus_name }, - { "org.freedesktop.systemd1.Service", "StatusText", bus_property_append_string, "s", u->service.status_text }, - { "org.freedesktop.systemd1.Service", "Sockets", bus_unit_append_dependencies, "as", u->service.configured_sockets }, +static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_service_append_service_result, service_result, ServiceResult); +static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_service_append_start_limit_action, start_limit_action, StartLimitAction); + +static const BusProperty bus_exec_main_status_properties[] = { + { "ExecMainStartTimestamp", bus_property_append_usec, "t", offsetof(ExecStatus, start_timestamp.realtime) }, + { "ExecMainStartTimestampMonotonic",bus_property_append_usec, "t", offsetof(ExecStatus, start_timestamp.monotonic) }, + { "ExecMainExitTimestamp", bus_property_append_usec, "t", offsetof(ExecStatus, start_timestamp.realtime) }, + { "ExecMainExitTimestampMonotonic", bus_property_append_usec, "t", offsetof(ExecStatus, start_timestamp.monotonic) }, + { "ExecMainPID", bus_property_append_pid, "u", offsetof(ExecStatus, pid) }, + { "ExecMainCode", bus_property_append_int, "i", offsetof(ExecStatus, code) }, + { "ExecMainStatus", bus_property_append_int, "i", offsetof(ExecStatus, status) }, + { NULL, } +}; + +static const BusProperty bus_service_properties[] = { + { "Type", bus_service_append_type, "s", offsetof(Service, type) }, + { "Restart", bus_service_append_restart, "s", offsetof(Service, restart) }, + { "PIDFile", bus_property_append_string, "s", offsetof(Service, pid_file), true }, + { "NotifyAccess", bus_service_append_notify_access, "s", offsetof(Service, notify_access) }, + { "RestartUSec", bus_property_append_usec, "t", offsetof(Service, restart_usec) }, + { "TimeoutUSec", bus_property_append_usec, "t", offsetof(Service, timeout_usec) }, + { "WatchdogUSec", bus_property_append_usec, "t", offsetof(Service, watchdog_usec) }, + { "WatchdogTimestamp", bus_property_append_usec, "t", offsetof(Service, watchdog_timestamp.realtime) }, + { "WatchdogTimestampMonotonic",bus_property_append_usec, "t", offsetof(Service, watchdog_timestamp.monotonic) }, + { "StartLimitInterval", bus_property_append_usec, "t", offsetof(Service, start_limit.interval) }, + { "StartLimitBurst", bus_property_append_uint32, "u", offsetof(Service, start_limit.burst) }, + { "StartLimitAction", bus_service_append_start_limit_action,"s", offsetof(Service, start_limit_action) }, + BUS_EXEC_COMMAND_PROPERTY("ExecStartPre", offsetof(Service, exec_command[SERVICE_EXEC_START_PRE]), true ), + BUS_EXEC_COMMAND_PROPERTY("ExecStart", offsetof(Service, exec_command[SERVICE_EXEC_START]), true ), + BUS_EXEC_COMMAND_PROPERTY("ExecStartPost", offsetof(Service, exec_command[SERVICE_EXEC_START_POST]), true ), + BUS_EXEC_COMMAND_PROPERTY("ExecReload", offsetof(Service, exec_command[SERVICE_EXEC_RELOAD]), true ), + BUS_EXEC_COMMAND_PROPERTY("ExecStop", offsetof(Service, exec_command[SERVICE_EXEC_STOP]), true ), + BUS_EXEC_COMMAND_PROPERTY("ExecStopPost", offsetof(Service, exec_command[SERVICE_EXEC_STOP_POST]), true ), + { "PermissionsStartOnly", bus_property_append_bool, "b", offsetof(Service, permissions_start_only) }, + { "RootDirectoryStartOnly", bus_property_append_bool, "b", offsetof(Service, root_directory_start_only) }, + { "RemainAfterExit", bus_property_append_bool, "b", offsetof(Service, remain_after_exit) }, + { "GuessMainPID", bus_property_append_bool, "b", offsetof(Service, guess_main_pid) }, + { "MainPID", bus_property_append_pid, "u", offsetof(Service, main_pid) }, + { "ControlPID", bus_property_append_pid, "u", offsetof(Service, control_pid) }, + { "BusName", bus_property_append_string, "s", offsetof(Service, bus_name), true }, + { "StatusText", bus_property_append_string, "s", offsetof(Service, status_text), true }, #ifdef HAVE_SYSV_COMPAT - { "org.freedesktop.systemd1.Service", "SysVRunLevels", bus_property_append_string, "s", u->service.sysv_runlevels }, - { "org.freedesktop.systemd1.Service", "SysVStartPriority", bus_property_append_int, "i", &u->service.sysv_start_priority }, - { "org.freedesktop.systemd1.Service", "SysVPath", bus_property_append_string, "s", u->service.sysv_path }, + { "SysVRunLevels", bus_property_append_string, "s", offsetof(Service, sysv_runlevels), true }, + { "SysVStartPriority", bus_property_append_int, "i", offsetof(Service, sysv_start_priority) }, + { "SysVPath", bus_property_append_string, "s", offsetof(Service, sysv_path), true }, #endif - { "org.freedesktop.systemd1.Service", "FsckPassNo", bus_property_append_int, "i", &u->service.fsck_passno }, - { NULL, NULL, NULL, NULL, NULL } + { "FsckPassNo", bus_property_append_int, "i", offsetof(Service, fsck_passno) }, + { "Result", bus_service_append_service_result,"s", offsetof(Service, result) }, + { NULL, } +}; + +DBusHandlerResult bus_service_message_handler(Unit *u, DBusConnection *connection, DBusMessage *message) { + Service *s = SERVICE(u); + const BusBoundProperties bps[] = { + { "org.freedesktop.systemd1.Unit", bus_unit_properties, u }, + { "org.freedesktop.systemd1.Service", bus_service_properties, s }, + { "org.freedesktop.systemd1.Service", bus_exec_context_properties, &s->exec_context }, + { "org.freedesktop.systemd1.Service", bus_exec_main_status_properties, &s->main_exec_status }, + { NULL, } }; - return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, properties); + return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, bps); } diff --git a/src/dbus-snapshot.c b/src/dbus-snapshot.c index 0c2f349599..e69388a524 100644 --- a/src/dbus-snapshot.c +++ b/src/dbus-snapshot.c @@ -45,13 +45,13 @@ const char bus_snapshot_interface[] _introspect_("Snapshot") = BUS_SNAPSHOT_INTERFACE; -DBusHandlerResult bus_snapshot_message_handler(Unit *u, DBusConnection *c, DBusMessage *message) { +static const BusProperty bus_snapshot_properties[] = { + { "Cleanup", bus_property_append_bool, "b", offsetof(Snapshot, cleanup) }, + { NULL, } +}; - const BusProperty properties[] = { - BUS_UNIT_PROPERTIES, - { "org.freedesktop.systemd1.Snapshot", "Cleanup", bus_property_append_bool, "b", &u->snapshot.cleanup }, - { NULL, NULL, NULL, NULL, NULL } - }; +DBusHandlerResult bus_snapshot_message_handler(Unit *u, DBusConnection *c, DBusMessage *message) { + Snapshot *s = SNAPSHOT(u); DBusMessage *reply = NULL; DBusError error; @@ -65,8 +65,14 @@ DBusHandlerResult bus_snapshot_message_handler(Unit *u, DBusConnection *c, DBusM if (!(reply = dbus_message_new_method_return(message))) goto oom; - } else - return bus_default_message_handler(c, message, INTROSPECTION, INTERFACES_LIST, properties); + } else { + const BusBoundProperties bps[] = { + { "org.freedesktop.systemd1.Unit", bus_unit_properties, u }, + { "org.freedesktop.systemd1.Snapshot", bus_snapshot_properties, s }, + { NULL, } + }; + return bus_default_message_handler(c, message, INTROSPECTION, INTERFACES_LIST, bps); + } if (reply) { if (!dbus_connection_send(c, reply, NULL)) diff --git a/src/dbus-socket.c b/src/dbus-socket.c index 2a1a17d780..9fef6769f0 100644 --- a/src/dbus-socket.c +++ b/src/dbus-socket.c @@ -51,12 +51,14 @@ " <property name=\"FreeBind\" type=\"b\" access=\"read\"/>\n" \ " <property name=\"Transparent\" type=\"b\" access=\"read\"/>\n" \ " <property name=\"Broadcast\" type=\"b\" access=\"read\"/>\n" \ + " <property name=\"PassCredentials\" type=\"b\" access=\"read\"/>\n" \ " <property name=\"Mark\" type=\"i\" access=\"read\"/>\n" \ " <property name=\"MaxConnections\" type=\"u\" access=\"read\"/>\n" \ " <property name=\"NAccepted\" type=\"u\" access=\"read\"/>\n" \ " <property name=\"NConnections\" type=\"u\" access=\"read\"/>\n" \ " <property name=\"MessageQueueMaxMessages\" type=\"x\" access=\"read\"/>\n" \ " <property name=\"MessageQueueMessageSize\" type=\"x\" access=\"read\"/>\n" \ + " <property name=\"Result\" type=\"s\" access=\"read\"/>\n" \ " </interface>\n" \ #define INTROSPECTION \ @@ -82,45 +84,54 @@ const char bus_socket_invalidating_properties[] = "ExecStopPost\0" "ControlPID\0" "NAccepted\0" - "NConnections\0"; + "NConnections\0" + "Result\0"; static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_socket_append_bind_ipv6_only, socket_address_bind_ipv6_only, SocketAddressBindIPv6Only); +static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_socket_append_socket_result, socket_result, SocketResult); + +static const BusProperty bus_socket_properties[] = { + { "BindIPv6Only", bus_socket_append_bind_ipv6_only, "s", offsetof(Socket, bind_ipv6_only) }, + { "Backlog", bus_property_append_unsigned, "u", offsetof(Socket, backlog) }, + { "TimeoutUSec", bus_property_append_usec, "t", offsetof(Socket, timeout_usec) }, + BUS_EXEC_COMMAND_PROPERTY("ExecStartPre", offsetof(Socket, exec_command[SOCKET_EXEC_START_PRE]), true ), + BUS_EXEC_COMMAND_PROPERTY("ExecStartPost", offsetof(Socket, exec_command[SOCKET_EXEC_START_POST]), true ), + BUS_EXEC_COMMAND_PROPERTY("ExecStopPre", offsetof(Socket, exec_command[SOCKET_EXEC_STOP_PRE]), true ), + BUS_EXEC_COMMAND_PROPERTY("ExecStopPost", offsetof(Socket, exec_command[SOCKET_EXEC_STOP_POST]), true ), + { "ControlPID", bus_property_append_pid, "u", offsetof(Socket, control_pid) }, + { "BindToDevice", bus_property_append_string, "s", offsetof(Socket, bind_to_device), true }, + { "DirectoryMode", bus_property_append_mode, "u", offsetof(Socket, directory_mode) }, + { "SocketMode", bus_property_append_mode, "u", offsetof(Socket, socket_mode) }, + { "Accept", bus_property_append_bool, "b", offsetof(Socket, accept) }, + { "KeepAlive", bus_property_append_bool, "b", offsetof(Socket, keep_alive) }, + { "Priority", bus_property_append_int, "i", offsetof(Socket, priority) }, + { "ReceiveBuffer", bus_property_append_size, "t", offsetof(Socket, receive_buffer) }, + { "SendBuffer", bus_property_append_size, "t", offsetof(Socket, send_buffer) }, + { "IPTOS", bus_property_append_int, "i", offsetof(Socket, ip_tos) }, + { "IPTTL", bus_property_append_int, "i", offsetof(Socket, ip_ttl) }, + { "PipeSize", bus_property_append_size, "t", offsetof(Socket, pipe_size) }, + { "FreeBind", bus_property_append_bool, "b", offsetof(Socket, free_bind) }, + { "Transparent", bus_property_append_bool, "b", offsetof(Socket, transparent) }, + { "Broadcast", bus_property_append_bool, "b", offsetof(Socket, broadcast) }, + { "PassCredentials",bus_property_append_bool, "b", offsetof(Socket, pass_cred) }, + { "Mark", bus_property_append_int, "i", offsetof(Socket, mark) }, + { "MaxConnections", bus_property_append_unsigned, "u", offsetof(Socket, max_connections) }, + { "NConnections", bus_property_append_unsigned, "u", offsetof(Socket, n_connections) }, + { "NAccepted", bus_property_append_unsigned, "u", offsetof(Socket, n_accepted) }, + { "MessageQueueMaxMessages", bus_property_append_long, "x", offsetof(Socket, mq_maxmsg) }, + { "MessageQueueMessageSize", bus_property_append_long, "x", offsetof(Socket, mq_msgsize) }, + { "Result", bus_socket_append_socket_result, "s", offsetof(Socket, result) }, + { NULL, } +}; DBusHandlerResult bus_socket_message_handler(Unit *u, DBusConnection *c, DBusMessage *message) { - - const BusProperty properties[] = { - BUS_UNIT_PROPERTIES, - { "org.freedesktop.systemd1.Socket", "BindIPv6Only", bus_socket_append_bind_ipv6_only, "s", &u->socket.bind_ipv6_only }, - { "org.freedesktop.systemd1.Socket", "Backlog", bus_property_append_unsigned, "u", &u->socket.backlog }, - { "org.freedesktop.systemd1.Socket", "TimeoutUSec", bus_property_append_usec, "t", &u->socket.timeout_usec }, - BUS_EXEC_COMMAND_PROPERTY("org.freedesktop.systemd1.Socket", u->service.exec_command[SOCKET_EXEC_START_PRE], "ExecStartPre"), - BUS_EXEC_COMMAND_PROPERTY("org.freedesktop.systemd1.Socket", u->service.exec_command[SOCKET_EXEC_START_POST], "ExecStartPost"), - BUS_EXEC_COMMAND_PROPERTY("org.freedesktop.systemd1.Socket", u->service.exec_command[SOCKET_EXEC_STOP_PRE], "ExecStopPre"), - BUS_EXEC_COMMAND_PROPERTY("org.freedesktop.systemd1.Socket", u->service.exec_command[SOCKET_EXEC_STOP_POST], "ExecStopPost"), - BUS_EXEC_CONTEXT_PROPERTIES("org.freedesktop.systemd1.Socket", u->socket.exec_context), - { "org.freedesktop.systemd1.Socket", "ControlPID", bus_property_append_pid, "u", &u->socket.control_pid }, - { "org.freedesktop.systemd1.Socket", "BindToDevice", bus_property_append_string, "s", u->socket.bind_to_device }, - { "org.freedesktop.systemd1.Socket", "DirectoryMode", bus_property_append_mode, "u", &u->socket.directory_mode }, - { "org.freedesktop.systemd1.Socket", "SocketMode", bus_property_append_mode, "u", &u->socket.socket_mode }, - { "org.freedesktop.systemd1.Socket", "Accept", bus_property_append_bool, "b", &u->socket.accept }, - { "org.freedesktop.systemd1.Socket", "KeepAlive", bus_property_append_bool, "b", &u->socket.keep_alive }, - { "org.freedesktop.systemd1.Socket", "Priority", bus_property_append_int, "i", &u->socket.priority }, - { "org.freedesktop.systemd1.Socket", "ReceiveBuffer", bus_property_append_size, "t", &u->socket.receive_buffer }, - { "org.freedesktop.systemd1.Socket", "SendBuffer", bus_property_append_size, "t", &u->socket.send_buffer }, - { "org.freedesktop.systemd1.Socket", "IPTOS", bus_property_append_int, "i", &u->socket.ip_tos }, - { "org.freedesktop.systemd1.Socket", "IPTTL", bus_property_append_int, "i", &u->socket.ip_ttl }, - { "org.freedesktop.systemd1.Socket", "PipeSize", bus_property_append_size, "t", &u->socket.pipe_size }, - { "org.freedesktop.systemd1.Socket", "FreeBind", bus_property_append_bool, "b", &u->socket.free_bind }, - { "org.freedesktop.systemd1.Socket", "Transparent", bus_property_append_bool, "b", &u->socket.transparent }, - { "org.freedesktop.systemd1.Socket", "Broadcast", bus_property_append_bool, "b", &u->socket.broadcast }, - { "org.freedesktop.systemd1.Socket", "Mark", bus_property_append_int, "i", &u->socket.mark }, - { "org.freedesktop.systemd1.Socket", "MaxConnections", bus_property_append_unsigned, "u", &u->socket.max_connections }, - { "org.freedesktop.systemd1.Socket", "NConnections", bus_property_append_unsigned, "u", &u->socket.n_connections }, - { "org.freedesktop.systemd1.Socket", "NAccepted", bus_property_append_unsigned, "u", &u->socket.n_accepted }, - { "org.freedesktop.systemd1.Socket", "MessageQueueMaxMessages", bus_property_append_long,"x", &u->socket.mq_maxmsg }, - { "org.freedesktop.systemd1.Socket", "MessageQueueMessageSize", bus_property_append_long,"x", &u->socket.mq_msgsize }, - { NULL, NULL, NULL, NULL, NULL } + Socket *s = SOCKET(u); + const BusBoundProperties bps[] = { + { "org.freedesktop.systemd1.Unit", bus_unit_properties, u }, + { "org.freedesktop.systemd1.Socket", bus_socket_properties, s }, + { "org.freedesktop.systemd1.Socket", bus_exec_context_properties, &s->exec_context }, + { NULL, } }; - return bus_default_message_handler(c, message, INTROSPECTION, INTERFACES_LIST, properties); + return bus_default_message_handler(c, message, INTROSPECTION, INTERFACES_LIST, bps); } diff --git a/src/dbus-swap.c b/src/dbus-swap.c index 988ca58d4b..09cd1e8b9c 100644 --- a/src/dbus-swap.c +++ b/src/dbus-swap.c @@ -36,6 +36,7 @@ BUS_EXEC_COMMAND_INTERFACE("ExecDeactivate") \ BUS_EXEC_CONTEXT_INTERFACE \ " <property name=\"ControlPID\" type=\"u\" access=\"read\"/>\n" \ + " <property name=\"Result\" type=\"s\" access=\"read\"/>\n" \ " </interface>\n" #define INTROSPECTION \ @@ -59,7 +60,8 @@ const char bus_swap_invalidating_properties[] = "Priority\0" "ExecActivate\0" "ExecDeactivate\0" - "ControlPID\0"; + "ControlPID\0" + "Result\0"; static int bus_swap_append_priority(DBusMessageIter *i, const char *property, void *data) { Swap *s = data; @@ -84,17 +86,26 @@ static int bus_swap_append_priority(DBusMessageIter *i, const char *property, vo return 0; } +static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_swap_append_swap_result, swap_result, SwapResult); + +static const BusProperty bus_swap_properties[] = { + { "What", bus_property_append_string, "s", offsetof(Swap, what), true }, + { "Priority", bus_swap_append_priority, "i", 0 }, + BUS_EXEC_COMMAND_PROPERTY("ExecActivate", offsetof(Swap, exec_command[SWAP_EXEC_ACTIVATE]), false), + BUS_EXEC_COMMAND_PROPERTY("ExecDeactivate", offsetof(Swap, exec_command[SWAP_EXEC_DEACTIVATE]), false), + { "ControlPID", bus_property_append_pid, "u", offsetof(Swap, control_pid) }, + { "Result", bus_swap_append_swap_result,"s", offsetof(Swap, result) }, + { NULL, } +}; + DBusHandlerResult bus_swap_message_handler(Unit *u, DBusConnection *c, DBusMessage *message) { - const BusProperty properties[] = { - BUS_UNIT_PROPERTIES, - { "org.freedesktop.systemd1.Swap", "What", bus_property_append_string, "s", u->swap.what }, - { "org.freedesktop.systemd1.Swap", "Priority", bus_swap_append_priority, "i", u }, - BUS_EXEC_COMMAND_PROPERTY("org.freedesktop.systemd1.Swap", u->swap.exec_command+SWAP_EXEC_ACTIVATE, "ExecActivate"), - BUS_EXEC_COMMAND_PROPERTY("org.freedesktop.systemd1.Swap", u->swap.exec_command+SWAP_EXEC_DEACTIVATE, "ExecDeactivate"), - BUS_EXEC_CONTEXT_PROPERTIES("org.freedesktop.systemd1.Swap", u->swap.exec_context), - { "org.freedesktop.systemd1.Swap", "ControlPID", bus_property_append_pid, "u", &u->swap.control_pid }, - { NULL, NULL, NULL, NULL, NULL } + Swap *s = SWAP(u); + const BusBoundProperties bps[] = { + { "org.freedesktop.systemd1.Unit", bus_unit_properties, u }, + { "org.freedesktop.systemd1.Swap", bus_swap_properties, s }, + { "org.freedesktop.systemd1.Swap", bus_exec_context_properties, &s->exec_context }, + { NULL, } }; - return bus_default_message_handler(c, message, INTROSPECTION, INTERFACES_LIST, properties); + return bus_default_message_handler(c, message, INTROSPECTION, INTERFACES_LIST, bps); } diff --git a/src/dbus-target.c b/src/dbus-target.c index 1e00f2dbbb..55cf862de0 100644 --- a/src/dbus-target.c +++ b/src/dbus-target.c @@ -46,10 +46,10 @@ const char bus_target_interface[] _introspect_("Target") = BUS_TARGET_INTERFACE; DBusHandlerResult bus_target_message_handler(Unit *u, DBusConnection *c, DBusMessage *message) { - const BusProperty properties[] = { - BUS_UNIT_PROPERTIES, - { NULL, NULL, NULL, NULL, NULL } + const BusBoundProperties bps[] = { + { "org.freedesktop.systemd1.Unit", bus_unit_properties, u }, + { NULL, } }; - return bus_default_message_handler(c, message, INTROSPECTION, INTERFACES_LIST, properties); + return bus_default_message_handler(c, message, INTROSPECTION, INTERFACES_LIST, bps); } diff --git a/src/dbus-timer.c b/src/dbus-timer.c index abcbe6f9be..b396aed047 100644 --- a/src/dbus-timer.c +++ b/src/dbus-timer.c @@ -31,6 +31,7 @@ " <property name=\"Unit\" type=\"s\" access=\"read\"/>\n" \ " <property name=\"Timers\" type=\"a(stt)\" access=\"read\"/>\n" \ " <property name=\"NextElapseUSec\" type=\"t\" access=\"read\"/>\n" \ + " <property name=\"Result\" type=\"s\" access=\"read\"/>\n" \ " </interface>\n" #define INTROSPECTION \ @@ -51,7 +52,8 @@ const char bus_timer_interface[] _introspect_("Timer") = BUS_TIMER_INTERFACE; const char bus_timer_invalidating_properties[] = "Timers\0" - "NextElapseUSec\0"; + "NextElapseUSec\0" + "Result\0"; static int bus_timer_append_timers(DBusMessageIter *i, const char *property, void *data) { Timer *p = data; @@ -101,25 +103,35 @@ static int bus_timer_append_timers(DBusMessageIter *i, const char *property, voi static int bus_timer_append_unit(DBusMessageIter *i, const char *property, void *data) { Unit *u = data; + Timer *timer = TIMER(u); const char *t; assert(i); assert(property); assert(u); - t = u->timer.unit ? u->timer.unit->meta.id : ""; + t = UNIT_DEREF(timer->unit) ? UNIT_DEREF(timer->unit)->id : ""; return dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &t) ? 0 : -ENOMEM; } +static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_timer_append_timer_result, timer_result, TimerResult); + +static const BusProperty bus_timer_properties[] = { + { "Unit", bus_timer_append_unit, "s", 0 }, + { "Timers", bus_timer_append_timers, "a(stt)", 0 }, + { "NextElapseUSec", bus_property_append_usec, "t", offsetof(Timer, next_elapse) }, + { "Result", bus_timer_append_timer_result,"s", offsetof(Timer, result) }, + { NULL, } +}; + DBusHandlerResult bus_timer_message_handler(Unit *u, DBusConnection *c, DBusMessage *message) { - const BusProperty properties[] = { - BUS_UNIT_PROPERTIES, - { "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 } + Timer *t = TIMER(u); + const BusBoundProperties bps[] = { + { "org.freedesktop.systemd1.Unit", bus_unit_properties, u }, + { "org.freedesktop.systemd1.Timer", bus_timer_properties, t }, + { NULL, } }; - return bus_default_message_handler(c, message, INTROSPECTION, INTERFACES_LIST, properties); + return bus_default_message_handler(c, message, INTROSPECTION, INTERFACES_LIST, bps); } diff --git a/src/dbus-unit.c b/src/dbus-unit.c index 266fb39be2..c7532c7255 100644 --- a/src/dbus-unit.c +++ b/src/dbus-unit.c @@ -40,7 +40,7 @@ const char bus_unit_interface[] _introspect_("Unit") = BUS_UNIT_INTERFACE; "Job\0" \ "NeedDaemonReload\0" -int bus_unit_append_names(DBusMessageIter *i, const char *property, void *data) { +static int bus_unit_append_names(DBusMessageIter *i, const char *property, void *data) { char *t; Iterator j; DBusMessageIter sub; @@ -49,7 +49,7 @@ int bus_unit_append_names(DBusMessageIter *i, const char *property, void *data) if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "s", &sub)) return -ENOMEM; - SET_FOREACH(t, u->meta.names, j) + SET_FOREACH(t, u->names, j) if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &t)) return -ENOMEM; @@ -59,7 +59,7 @@ int bus_unit_append_names(DBusMessageIter *i, const char *property, void *data) return 0; } -int bus_unit_append_following(DBusMessageIter *i, const char *property, void *data) { +static int bus_unit_append_following(DBusMessageIter *i, const char *property, void *data) { Unit *u = data, *f; const char *d; @@ -68,7 +68,7 @@ int bus_unit_append_following(DBusMessageIter *i, const char *property, void *da assert(u); f = unit_following(u); - d = f ? f->meta.id : ""; + d = f ? f->id : ""; if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &d)) return -ENOMEM; @@ -76,7 +76,7 @@ int bus_unit_append_following(DBusMessageIter *i, const char *property, void *da return 0; } -int bus_unit_append_dependencies(DBusMessageIter *i, const char *property, void *data) { +static int bus_unit_append_dependencies(DBusMessageIter *i, const char *property, void *data) { Unit *u; Iterator j; DBusMessageIter sub; @@ -86,7 +86,7 @@ int bus_unit_append_dependencies(DBusMessageIter *i, const char *property, void return -ENOMEM; SET_FOREACH(u, s, j) - if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &u->meta.id)) + if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &u->id)) return -ENOMEM; if (!dbus_message_iter_close_container(i, &sub)) @@ -95,7 +95,7 @@ int bus_unit_append_dependencies(DBusMessageIter *i, const char *property, void return 0; } -int bus_unit_append_description(DBusMessageIter *i, const char *property, void *data) { +static int bus_unit_append_description(DBusMessageIter *i, const char *property, void *data) { Unit *u = data; const char *d; @@ -111,9 +111,9 @@ int bus_unit_append_description(DBusMessageIter *i, const char *property, void * return 0; } -DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_unit_append_load_state, unit_load_state, UnitLoadState); +static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_unit_append_load_state, unit_load_state, UnitLoadState); -int bus_unit_append_active_state(DBusMessageIter *i, const char *property, void *data) { +static int bus_unit_append_active_state(DBusMessageIter *i, const char *property, void *data) { Unit *u = data; const char *state; @@ -129,7 +129,7 @@ int bus_unit_append_active_state(DBusMessageIter *i, const char *property, void return 0; } -int bus_unit_append_sub_state(DBusMessageIter *i, const char *property, void *data) { +static int bus_unit_append_sub_state(DBusMessageIter *i, const char *property, void *data) { Unit *u = data; const char *state; @@ -145,7 +145,23 @@ int bus_unit_append_sub_state(DBusMessageIter *i, const char *property, void *da return 0; } -int bus_unit_append_can_start(DBusMessageIter *i, const char *property, void *data) { +static int bus_unit_append_file_state(DBusMessageIter *i, const char *property, void *data) { + Unit *u = data; + const char *state; + + assert(i); + assert(property); + assert(u); + + state = strempty(unit_file_state_to_string(unit_get_unit_file_state(u))); + + if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &state)) + return -ENOMEM; + + return 0; +} + +static int bus_unit_append_can_start(DBusMessageIter *i, const char *property, void *data) { Unit *u = data; dbus_bool_t b; @@ -154,7 +170,7 @@ int bus_unit_append_can_start(DBusMessageIter *i, const char *property, void *da assert(u); b = unit_can_start(u) && - !u->meta.refuse_manual_start; + !u->refuse_manual_start; if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b)) return -ENOMEM; @@ -162,7 +178,7 @@ int bus_unit_append_can_start(DBusMessageIter *i, const char *property, void *da return 0; } -int bus_unit_append_can_stop(DBusMessageIter *i, const char *property, void *data) { +static int bus_unit_append_can_stop(DBusMessageIter *i, const char *property, void *data) { Unit *u = data; dbus_bool_t b; @@ -174,7 +190,7 @@ int bus_unit_append_can_stop(DBusMessageIter *i, const char *property, void *dat * we can also stop */ b = unit_can_start(u) && - !u->meta.refuse_manual_stop; + !u->refuse_manual_stop; if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b)) return -ENOMEM; @@ -182,7 +198,7 @@ int bus_unit_append_can_stop(DBusMessageIter *i, const char *property, void *dat return 0; } -int bus_unit_append_can_reload(DBusMessageIter *i, const char *property, void *data) { +static int bus_unit_append_can_reload(DBusMessageIter *i, const char *property, void *data) { Unit *u = data; dbus_bool_t b; @@ -198,7 +214,7 @@ int bus_unit_append_can_reload(DBusMessageIter *i, const char *property, void *d return 0; } -int bus_unit_append_can_isolate(DBusMessageIter *i, const char *property, void *data) { +static int bus_unit_append_can_isolate(DBusMessageIter *i, const char *property, void *data) { Unit *u = data; dbus_bool_t b; @@ -207,7 +223,7 @@ int bus_unit_append_can_isolate(DBusMessageIter *i, const char *property, void * assert(u); b = unit_can_isolate(u) && - !u->meta.refuse_manual_start; + !u->refuse_manual_start; if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b)) return -ENOMEM; @@ -215,7 +231,7 @@ int bus_unit_append_can_isolate(DBusMessageIter *i, const char *property, void * return 0; } -int bus_unit_append_job(DBusMessageIter *i, const char *property, void *data) { +static int bus_unit_append_job(DBusMessageIter *i, const char *property, void *data) { Unit *u = data; DBusMessageIter sub; char *p; @@ -227,12 +243,12 @@ int bus_unit_append_job(DBusMessageIter *i, const char *property, void *data) { if (!dbus_message_iter_open_container(i, DBUS_TYPE_STRUCT, NULL, &sub)) return -ENOMEM; - if (u->meta.job) { + if (u->job) { - if (!(p = job_dbus_path(u->meta.job))) + if (!(p = job_dbus_path(u->job))) return -ENOMEM; - if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT32, &u->meta.job->id) || + if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT32, &u->job->id) || !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &p)) { free(p); return -ENOMEM; @@ -262,7 +278,7 @@ int bus_unit_append_job(DBusMessageIter *i, const char *property, void *data) { return 0; } -int bus_unit_append_default_cgroup(DBusMessageIter *i, const char *property, void *data) { +static int bus_unit_append_default_cgroup(DBusMessageIter *i, const char *property, void *data) { Unit *u = data; char *t; CGroupBonding *cgb; @@ -286,7 +302,7 @@ int bus_unit_append_default_cgroup(DBusMessageIter *i, const char *property, voi return success ? 0 : -ENOMEM; } -int bus_unit_append_cgroups(DBusMessageIter *i, const char *property, void *data) { +static int bus_unit_append_cgroups(DBusMessageIter *i, const char *property, void *data) { Unit *u = data; CGroupBonding *cgb; DBusMessageIter sub; @@ -294,7 +310,7 @@ int bus_unit_append_cgroups(DBusMessageIter *i, const char *property, void *data if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "s", &sub)) return -ENOMEM; - LIST_FOREACH(by_unit, cgb, u->meta.cgroup_bondings) { + LIST_FOREACH(by_unit, cgb, u->cgroup_bondings) { char *t; bool success; @@ -314,7 +330,41 @@ int bus_unit_append_cgroups(DBusMessageIter *i, const char *property, void *data return 0; } -int bus_unit_append_need_daemon_reload(DBusMessageIter *i, const char *property, void *data) { +static int bus_unit_append_cgroup_attrs(DBusMessageIter *i, const char *property, void *data) { + Unit *u = data; + CGroupAttribute *a; + DBusMessageIter sub, sub2; + + if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "(sss)", &sub)) + return -ENOMEM; + + LIST_FOREACH(by_unit, a, u->cgroup_attributes) { + char *v = NULL; + bool success; + + if (a->map_callback) + a->map_callback(a->controller, a->name, a->value, &v); + + success = + dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) && + dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &a->controller) && + dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &a->name) && + dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, v ? &v : &a->value) && + dbus_message_iter_close_container(&sub, &sub2); + + free(v); + + if (!success) + return -ENOMEM; + } + + if (!dbus_message_iter_close_container(i, &sub)) + return -ENOMEM; + + return 0; +} + +static int bus_unit_append_need_daemon_reload(DBusMessageIter *i, const char *property, void *data) { Unit *u = data; dbus_bool_t b; @@ -330,9 +380,33 @@ int bus_unit_append_need_daemon_reload(DBusMessageIter *i, const char *property, return 0; } +static int bus_unit_append_load_error(DBusMessageIter *i, const char *property, void *data) { + Unit *u = data; + const char *name, *message; + DBusMessageIter sub; + + assert(i); + assert(property); + assert(u); + + if (u->load_error != 0) { + name = bus_errno_to_dbus(u->load_error); + message = strempty(strerror(-u->load_error)); + } else + name = message = ""; + + if (!dbus_message_iter_open_container(i, DBUS_TYPE_STRUCT, NULL, &sub) || + !dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &name) || + !dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &message) || + !dbus_message_iter_close_container(i, &sub)) + return -ENOMEM; + + return 0; +} + static DBusHandlerResult bus_unit_message_dispatch(Unit *u, DBusConnection *connection, DBusMessage *message) { DBusMessage *reply = NULL; - Manager *m = u->meta.manager; + Manager *m = u->manager; DBusError error; JobType job_type = _JOB_TYPE_INVALID; char *path = NULL; @@ -415,10 +489,10 @@ static DBusHandlerResult bus_unit_message_dispatch(Unit *u, DBusConnection *conn Job *j; int r; - if ((job_type == JOB_START && u->meta.refuse_manual_start) || - (job_type == JOB_STOP && u->meta.refuse_manual_stop) || + if ((job_type == JOB_START && u->refuse_manual_start) || + (job_type == JOB_STOP && u->refuse_manual_stop) || ((job_type == JOB_RESTART || job_type == JOB_TRY_RESTART) && - (u->meta.refuse_manual_start || u->meta.refuse_manual_stop))) { + (u->refuse_manual_start || u->refuse_manual_stop))) { dbus_set_error(&error, BUS_ERROR_ONLY_BY_DEPENDENCY, "Operation refused, may be requested by dependency only."); return bus_send_error_reply(connection, message, &error, -EPERM); } @@ -520,7 +594,7 @@ static DBusHandlerResult bus_unit_message_handler(DBusConnection *connection, DB HASHMAP_FOREACH_KEY(u, k, m->units, i) { char *p; - if (k != u->meta.id) + if (k != u->id) continue; if (!(p = bus_path_escape(k))) { @@ -599,23 +673,23 @@ void bus_unit_send_change_signal(Unit *u) { assert(u); - if (u->meta.in_dbus_queue) { - LIST_REMOVE(Meta, dbus_queue, u->meta.manager->dbus_unit_queue, &u->meta); - u->meta.in_dbus_queue = false; + if (u->in_dbus_queue) { + LIST_REMOVE(Unit, dbus_queue, u->manager->dbus_unit_queue, u); + u->in_dbus_queue = false; } - if (!u->meta.id) + if (!u->id) return; - if (!bus_has_subscriber(u->meta.manager)) { - u->meta.sent_dbus_new_signal = true; + if (!bus_has_subscriber(u->manager)) { + u->sent_dbus_new_signal = true; return; } if (!(p = unit_dbus_path(u))) goto oom; - if (u->meta.sent_dbus_new_signal) { + if (u->sent_dbus_new_signal) { /* Send a properties changed signal. First for the * specific type, then for the generic unit. The * clients may rely on this order to get atomic @@ -628,7 +702,7 @@ void bus_unit_send_change_signal(Unit *u) { UNIT_VTABLE(u)->bus_invalidating_properties))) goto oom; - if (bus_broadcast(u->meta.manager, m) < 0) + if (bus_broadcast(u->manager, m) < 0) goto oom; dbus_message_unref(m); @@ -644,19 +718,19 @@ void bus_unit_send_change_signal(Unit *u) { goto oom; if (!dbus_message_append_args(m, - DBUS_TYPE_STRING, &u->meta.id, + DBUS_TYPE_STRING, &u->id, DBUS_TYPE_OBJECT_PATH, &p, DBUS_TYPE_INVALID)) goto oom; } - if (bus_broadcast(u->meta.manager, m) < 0) + if (bus_broadcast(u->manager, m) < 0) goto oom; free(p); dbus_message_unref(m); - u->meta.sent_dbus_new_signal = true; + u->sent_dbus_new_signal = true; return; @@ -675,13 +749,13 @@ void bus_unit_send_removed_signal(Unit *u) { assert(u); - if (!bus_has_subscriber(u->meta.manager)) + if (!bus_has_subscriber(u->manager)) return; - if (!u->meta.sent_dbus_new_signal) + if (!u->sent_dbus_new_signal) bus_unit_send_change_signal(u); - if (!u->meta.id) + if (!u->id) return; if (!(p = unit_dbus_path(u))) @@ -691,12 +765,12 @@ void bus_unit_send_removed_signal(Unit *u) { goto oom; if (!dbus_message_append_args(m, - DBUS_TYPE_STRING, &u->meta.id, + DBUS_TYPE_STRING, &u->id, DBUS_TYPE_OBJECT_PATH, &p, DBUS_TYPE_INVALID)) goto oom; - if (bus_broadcast(u->meta.manager, m) < 0) + if (bus_broadcast(u->manager, m) < 0) goto oom; free(p); @@ -712,3 +786,65 @@ oom: log_error("Failed to allocate unit remove signal."); } + +const BusProperty bus_unit_properties[] = { + { "Id", bus_property_append_string, "s", offsetof(Unit, id), true }, + { "Names", bus_unit_append_names, "as", 0 }, + { "Following", bus_unit_append_following, "s", 0 }, + { "Requires", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUIRES]), true }, + { "RequiresOverridable", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUIRES_OVERRIDABLE]), true }, + { "Requisite", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUISITE]), true }, + { "RequisiteOverridable", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUISITE_OVERRIDABLE]), true }, + { "Wants", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_WANTS]), true }, + { "BindTo", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_BIND_TO]), true }, + { "RequiredBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUIRED_BY]), true }, + { "RequiredByOverridable",bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUIRED_BY_OVERRIDABLE]), true }, + { "WantedBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_WANTED_BY]), true }, + { "BoundBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_BOUND_BY]), true }, + { "Conflicts", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_CONFLICTS]), true }, + { "ConflictedBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_CONFLICTED_BY]), true }, + { "Before", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_BEFORE]), true }, + { "After", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_AFTER]), true }, + { "OnFailure", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_ON_FAILURE]), true }, + { "Triggers", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_TRIGGERS]), true }, + { "TriggeredBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_TRIGGERED_BY]), true }, + { "PropagateReloadTo", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_PROPAGATE_RELOAD_TO]), true }, + { "PropagateReloadFrom", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_PROPAGATE_RELOAD_FROM]), true }, + { "Description", bus_unit_append_description, "s", 0 }, + { "LoadState", bus_unit_append_load_state, "s", offsetof(Unit, load_state) }, + { "ActiveState", bus_unit_append_active_state, "s", 0 }, + { "SubState", bus_unit_append_sub_state, "s", 0 }, + { "FragmentPath", bus_property_append_string, "s", offsetof(Unit, fragment_path), true }, + { "UnitFileState", bus_unit_append_file_state, "s", 0 }, + { "InactiveExitTimestamp",bus_property_append_usec, "t", offsetof(Unit, inactive_exit_timestamp.realtime) }, + { "InactiveExitTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, inactive_exit_timestamp.monotonic) }, + { "ActiveEnterTimestamp", bus_property_append_usec, "t", offsetof(Unit, active_enter_timestamp.realtime) }, + { "ActiveEnterTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, active_enter_timestamp.monotonic) }, + { "ActiveExitTimestamp", bus_property_append_usec, "t", offsetof(Unit, active_exit_timestamp.realtime) }, + { "ActiveExitTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, active_exit_timestamp.monotonic) }, + { "InactiveEnterTimestamp", bus_property_append_usec, "t", offsetof(Unit, inactive_enter_timestamp.realtime) }, + { "InactiveEnterTimestampMonotonic",bus_property_append_usec, "t", offsetof(Unit, inactive_enter_timestamp.monotonic) }, + { "CanStart", bus_unit_append_can_start, "b", 0 }, + { "CanStop", bus_unit_append_can_stop, "b", 0 }, + { "CanReload", bus_unit_append_can_reload, "b", 0 }, + { "CanIsolate", bus_unit_append_can_isolate, "b", 0 }, + { "Job", bus_unit_append_job, "(uo)", 0 }, + { "StopWhenUnneeded", bus_property_append_bool, "b", offsetof(Unit, stop_when_unneeded) }, + { "RefuseManualStart", bus_property_append_bool, "b", offsetof(Unit, refuse_manual_start) }, + { "RefuseManualStop", bus_property_append_bool, "b", offsetof(Unit, refuse_manual_stop) }, + { "AllowIsolate", bus_property_append_bool, "b", offsetof(Unit, allow_isolate) }, + { "DefaultDependencies", bus_property_append_bool, "b", offsetof(Unit, default_dependencies) }, + { "OnFailureIsolate", bus_property_append_bool, "b", offsetof(Unit, on_failure_isolate) }, + { "IgnoreOnIsolate", bus_property_append_bool, "b", offsetof(Unit, ignore_on_isolate) }, + { "IgnoreOnSnapshot", bus_property_append_bool, "b", offsetof(Unit, ignore_on_snapshot) }, + { "DefaultControlGroup", bus_unit_append_default_cgroup, "s", 0 }, + { "ControlGroup", bus_unit_append_cgroups, "as", 0 }, + { "ControlGroupAttributes", bus_unit_append_cgroup_attrs,"a(sss)", 0 }, + { "NeedDaemonReload", bus_unit_append_need_daemon_reload, "b", 0 }, + { "JobTimeoutUSec", bus_property_append_usec, "t", offsetof(Unit, job_timeout) }, + { "ConditionTimestamp", bus_property_append_usec, "t", offsetof(Unit, condition_timestamp.realtime) }, + { "ConditionTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, condition_timestamp.monotonic) }, + { "ConditionResult", bus_property_append_bool, "b", offsetof(Unit, condition_result) }, + { "LoadError", bus_unit_append_load_error, "(ss)", 0 }, + { NULL, } +}; diff --git a/src/dbus-unit.h b/src/dbus-unit.h index df8f6ae2ac..4f19a808bf 100644 --- a/src/dbus-unit.h +++ b/src/dbus-unit.h @@ -25,6 +25,7 @@ #include <dbus/dbus.h> #include "manager.h" +#include "dbus-common.h" #define BUS_UNIT_INTERFACE \ " <interface name=\"org.freedesktop.systemd1.Unit\">\n" \ @@ -80,11 +81,16 @@ " <property name=\"Before\" type=\"as\" access=\"read\"/>\n" \ " <property name=\"After\" type=\"as\" access=\"read\"/>\n" \ " <property name=\"OnFailure\" type=\"as\" access=\"read\"/>\n" \ + " <property name=\"Triggers\" type=\"as\" access=\"read\"/>\n" \ + " <property name=\"TriggeredBy\" type=\"as\" access=\"read\"/>\n" \ + " <property name=\"PropagateReloadTo\" type=\"as\" access=\"read\"/>\n" \ + " <property name=\"PropagateReloadFrom\" type=\"as\" access=\"read\"/>\n" \ " <property name=\"Description\" type=\"s\" access=\"read\"/>\n" \ " <property name=\"LoadState\" type=\"s\" access=\"read\"/>\n" \ " <property name=\"ActiveState\" type=\"s\" access=\"read\"/>\n" \ " <property name=\"SubState\" type=\"s\" access=\"read\"/>\n" \ " <property name=\"FragmentPath\" type=\"s\" access=\"read\"/>\n" \ + " <property name=\"UnitFileState\" type=\"s\" access=\"read\"/>\n" \ " <property name=\"InactiveExitTimestamp\" type=\"t\" access=\"read\"/>\n" \ " <property name=\"InactiveExitTimestampMonotonic\" type=\"t\" access=\"read\"/>\n" \ " <property name=\"ActiveEnterTimestamp\" type=\"t\" access=\"read\"/>\n" \ @@ -108,85 +114,20 @@ " <property name=\"IgnoreOnSnapshot\" type=\"b\" access=\"read\"/>\n" \ " <property name=\"DefaultControlGroup\" type=\"s\" access=\"read\"/>\n" \ " <property name=\"ControlGroup\" type=\"as\" access=\"read\"/>\n" \ + " <property name=\"ControlGroupAttributes\" type=\"a(sss)\" access=\"read\"/>\n" \ " <property name=\"NeedDaemonReload\" type=\"b\" access=\"read\"/>\n" \ " <property name=\"JobTimeoutUSec\" type=\"t\" access=\"read\"/>\n" \ " <property name=\"ConditionTimestamp\" type=\"t\" access=\"read\"/>\n" \ " <property name=\"ConditionTimestampMonotonic\" type=\"t\" access=\"read\"/>\n" \ " <property name=\"ConditionResult\" type=\"b\" access=\"read\"/>\n" \ + " <property name=\"LoadError\" type=\"(ss)\" access=\"read\"/>\n" \ " </interface>\n" #define BUS_UNIT_INTERFACES_LIST \ BUS_GENERIC_INTERFACES_LIST \ "org.freedesktop.systemd1.Unit\0" -#define BUS_UNIT_PROPERTIES \ - { "org.freedesktop.systemd1.Unit", "Id", bus_property_append_string, "s", u->meta.id }, \ - { "org.freedesktop.systemd1.Unit", "Names", bus_unit_append_names, "as", u }, \ - { "org.freedesktop.systemd1.Unit", "Following", bus_unit_append_following, "s", u }, \ - { "org.freedesktop.systemd1.Unit", "Requires", bus_unit_append_dependencies, "as", u->meta.dependencies[UNIT_REQUIRES] }, \ - { "org.freedesktop.systemd1.Unit", "RequiresOverridable", bus_unit_append_dependencies, "as", u->meta.dependencies[UNIT_REQUIRES_OVERRIDABLE] }, \ - { "org.freedesktop.systemd1.Unit", "Requisite", bus_unit_append_dependencies, "as", u->meta.dependencies[UNIT_REQUISITE] }, \ - { "org.freedesktop.systemd1.Unit", "RequisiteOverridable", bus_unit_append_dependencies, "as", u->meta.dependencies[UNIT_REQUISITE_OVERRIDABLE] }, \ - { "org.freedesktop.systemd1.Unit", "Wants", bus_unit_append_dependencies, "as", u->meta.dependencies[UNIT_WANTS] }, \ - { "org.freedesktop.systemd1.Unit", "BindTo", bus_unit_append_dependencies, "as", u->meta.dependencies[UNIT_BIND_TO] }, \ - { "org.freedesktop.systemd1.Unit", "RequiredBy", bus_unit_append_dependencies, "as", u->meta.dependencies[UNIT_REQUIRED_BY] }, \ - { "org.freedesktop.systemd1.Unit", "RequiredByOverridable",bus_unit_append_dependencies, "as", u->meta.dependencies[UNIT_REQUIRED_BY_OVERRIDABLE] }, \ - { "org.freedesktop.systemd1.Unit", "WantedBy", bus_unit_append_dependencies, "as", u->meta.dependencies[UNIT_WANTED_BY] }, \ - { "org.freedesktop.systemd1.Unit", "BoundBy", bus_unit_append_dependencies, "as", u->meta.dependencies[UNIT_BOUND_BY] }, \ - { "org.freedesktop.systemd1.Unit", "Conflicts", bus_unit_append_dependencies, "as", u->meta.dependencies[UNIT_CONFLICTS] }, \ - { "org.freedesktop.systemd1.Unit", "ConflictedBy", bus_unit_append_dependencies, "as", u->meta.dependencies[UNIT_CONFLICTED_BY] }, \ - { "org.freedesktop.systemd1.Unit", "Before", bus_unit_append_dependencies, "as", u->meta.dependencies[UNIT_BEFORE] }, \ - { "org.freedesktop.systemd1.Unit", "After", bus_unit_append_dependencies, "as", u->meta.dependencies[UNIT_AFTER] }, \ - { "org.freedesktop.systemd1.Unit", "OnFailure", bus_unit_append_dependencies, "as", u->meta.dependencies[UNIT_ON_FAILURE] }, \ - { "org.freedesktop.systemd1.Unit", "Description", bus_unit_append_description, "s", u }, \ - { "org.freedesktop.systemd1.Unit", "LoadState", bus_unit_append_load_state, "s", &u->meta.load_state }, \ - { "org.freedesktop.systemd1.Unit", "ActiveState", bus_unit_append_active_state, "s", u }, \ - { "org.freedesktop.systemd1.Unit", "SubState", bus_unit_append_sub_state, "s", u }, \ - { "org.freedesktop.systemd1.Unit", "FragmentPath", bus_property_append_string, "s", u->meta.fragment_path }, \ - { "org.freedesktop.systemd1.Unit", "InactiveExitTimestamp",bus_property_append_usec, "t", &u->meta.inactive_exit_timestamp.realtime }, \ - { "org.freedesktop.systemd1.Unit", "InactiveExitTimestampMonotonic",bus_property_append_usec, "t", &u->meta.inactive_exit_timestamp.monotonic }, \ - { "org.freedesktop.systemd1.Unit", "ActiveEnterTimestamp", bus_property_append_usec, "t", &u->meta.active_enter_timestamp.realtime }, \ - { "org.freedesktop.systemd1.Unit", "ActiveEnterTimestampMonotonic", bus_property_append_usec, "t", &u->meta.active_enter_timestamp.monotonic }, \ - { "org.freedesktop.systemd1.Unit", "ActiveExitTimestamp", bus_property_append_usec, "t", &u->meta.active_exit_timestamp.realtime }, \ - { "org.freedesktop.systemd1.Unit", "ActiveExitTimestampMonotonic", bus_property_append_usec, "t", &u->meta.active_exit_timestamp.monotonic }, \ - { "org.freedesktop.systemd1.Unit", "InactiveEnterTimestamp",bus_property_append_usec, "t", &u->meta.inactive_enter_timestamp.realtime }, \ - { "org.freedesktop.systemd1.Unit", "InactiveEnterTimestampMonotonic",bus_property_append_usec,"t", &u->meta.inactive_enter_timestamp.monotonic }, \ - { "org.freedesktop.systemd1.Unit", "CanStart", bus_unit_append_can_start, "b", u }, \ - { "org.freedesktop.systemd1.Unit", "CanStop", bus_unit_append_can_stop, "b", u }, \ - { "org.freedesktop.systemd1.Unit", "CanReload", bus_unit_append_can_reload, "b", u }, \ - { "org.freedesktop.systemd1.Unit", "CanIsolate", bus_unit_append_can_isolate, "b", u }, \ - { "org.freedesktop.systemd1.Unit", "Job", bus_unit_append_job, "(uo)", u }, \ - { "org.freedesktop.systemd1.Unit", "StopWhenUnneeded", bus_property_append_bool, "b", &u->meta.stop_when_unneeded }, \ - { "org.freedesktop.systemd1.Unit", "RefuseManualStart", bus_property_append_bool, "b", &u->meta.refuse_manual_start }, \ - { "org.freedesktop.systemd1.Unit", "RefuseManualStop", bus_property_append_bool, "b", &u->meta.refuse_manual_stop }, \ - { "org.freedesktop.systemd1.Unit", "AllowIsolate", bus_property_append_bool, "b", &u->meta.allow_isolate }, \ - { "org.freedesktop.systemd1.Unit", "DefaultDependencies", bus_property_append_bool, "b", &u->meta.default_dependencies }, \ - { "org.freedesktop.systemd1.Unit", "OnFailureIsolate", bus_property_append_bool, "b", &u->meta.on_failure_isolate }, \ - { "org.freedesktop.systemd1.Unit", "IgnoreOnIsolate", bus_property_append_bool, "b", &u->meta.ignore_on_isolate }, \ - { "org.freedesktop.systemd1.Unit", "IgnoreOnSnapshot", bus_property_append_bool, "b", &u->meta.ignore_on_snapshot }, \ - { "org.freedesktop.systemd1.Unit", "DefaultControlGroup", bus_unit_append_default_cgroup, "s", u }, \ - { "org.freedesktop.systemd1.Unit", "ControlGroup", bus_unit_append_cgroups, "as", u }, \ - { "org.freedesktop.systemd1.Unit", "NeedDaemonReload", bus_unit_append_need_daemon_reload, "b", u }, \ - { "org.freedesktop.systemd1.Unit", "JobTimeoutUSec", bus_property_append_usec, "t", &u->meta.job_timeout }, \ - { "org.freedesktop.systemd1.Unit", "ConditionTimestamp", bus_property_append_usec, "t", &u->meta.condition_timestamp.realtime }, \ - { "org.freedesktop.systemd1.Unit", "ConditionTimestampMonotonic", bus_property_append_usec,"t", &u->meta.condition_timestamp.monotonic }, \ - { "org.freedesktop.systemd1.Unit", "ConditionResult", bus_property_append_bool, "b", &u->meta.condition_result } - -int bus_unit_append_names(DBusMessageIter *i, const char *property, void *data); -int bus_unit_append_following(DBusMessageIter *i, const char *property, void *data); -int bus_unit_append_dependencies(DBusMessageIter *i, const char *property, void *data); -int bus_unit_append_description(DBusMessageIter *i, const char *property, void *data); -int bus_unit_append_load_state(DBusMessageIter *i, const char *property, void *data); -int bus_unit_append_active_state(DBusMessageIter *i, const char *property, void *data); -int bus_unit_append_sub_state(DBusMessageIter *i, const char *property, void *data); -int bus_unit_append_can_start(DBusMessageIter *i, const char *property, void *data); -int bus_unit_append_can_stop(DBusMessageIter *i, const char *property, void *data); -int bus_unit_append_can_reload(DBusMessageIter *i, const char *property, void *data); -int bus_unit_append_can_isolate(DBusMessageIter *i, const char *property, void *data); -int bus_unit_append_job(DBusMessageIter *i, const char *property, void *data); -int bus_unit_append_default_cgroup(DBusMessageIter *i, const char *property, void *data); -int bus_unit_append_cgroups(DBusMessageIter *i, const char *property, void *data); -int bus_unit_append_need_daemon_reload(DBusMessageIter *i, const char *property, void *data); +extern const BusProperty bus_unit_properties[]; void bus_unit_send_change_signal(Unit *u); void bus_unit_send_removed_signal(Unit *u); diff --git a/src/dbus.c b/src/dbus.c index daa2c84a05..8e6e9fd520 100644 --- a/src/dbus.c +++ b/src/dbus.c @@ -48,6 +48,11 @@ #define CONNECTIONS_MAX 52 +/* Well-known address (http://dbus.freedesktop.org/doc/dbus-specification.html#message-bus-types) */ +#define DBUS_SYSTEM_BUS_DEFAULT_ADDRESS "unix:path=/var/run/dbus/system_bus_socket" +/* Only used as a fallback */ +#define DBUS_SESSION_BUS_DEFAULT_ADDRESS "autolaunch:" + static const char bus_properties_interface[] = BUS_PROPERTIES_INTERFACE; static const char bus_introspectable_interface[] = BUS_INTROSPECTABLE_INTERFACE; @@ -365,7 +370,7 @@ static DBusHandlerResult api_bus_message_filter(DBusConnection *connection, DBus } else { r = manager_load_unit(m, name, NULL, &error, &u); - if (r >= 0 && u->meta.refuse_manual_start) + if (r >= 0 && u->refuse_manual_start) r = -EPERM; if (r >= 0) @@ -767,37 +772,19 @@ static void bus_new_connection( dbus_connection_ref(new_connection); } -static int bus_init_system(Manager *m) { - DBusError error; - int r; - - assert(m); - - dbus_error_init(&error); - - if (m->system_bus) - return 0; - - if (m->running_as == MANAGER_SYSTEM && m->api_bus) - m->system_bus = m->api_bus; - else { - if (!(m->system_bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error))) { - log_debug("Failed to get system D-Bus connection, retrying later: %s", bus_error_message(&error)); - r = 0; - goto fail; - } - - if ((r = bus_setup_loop(m, m->system_bus)) < 0) - goto fail; - } +static int init_registered_system_bus(Manager *m) { + char *id; if (!dbus_connection_add_filter(m->system_bus, system_bus_message_filter, m, NULL)) { log_error("Not enough memory"); - r = -ENOMEM; - goto fail; + return -ENOMEM; } if (m->running_as != MANAGER_SYSTEM) { + DBusError error; + + dbus_error_init(&error); + dbus_bus_add_match(m->system_bus, "type='signal'," "interface='org.freedesktop.systemd1.Agent'," @@ -807,59 +794,28 @@ static int bus_init_system(Manager *m) { if (dbus_error_is_set(&error)) { log_error("Failed to register match: %s", bus_error_message(&error)); - r = -EIO; - goto fail; + dbus_error_free(&error); + return -1; } } - if (m->api_bus != m->system_bus) { - char *id; - log_debug("Successfully connected to system D-Bus bus %s as %s", - strnull((id = dbus_connection_get_server_id(m->system_bus))), - strnull(dbus_bus_get_unique_name(m->system_bus))); - dbus_free(id); - } + log_debug("Successfully connected to system D-Bus bus %s as %s", + strnull((id = dbus_connection_get_server_id(m->system_bus))), + strnull(dbus_bus_get_unique_name(m->system_bus))); + dbus_free(id); return 0; - -fail: - bus_done_system(m); - dbus_error_free(&error); - - return r; } -static int bus_init_api(Manager *m) { - DBusError error; +static int init_registered_api_bus(Manager *m) { int r; - assert(m); - - dbus_error_init(&error); - - if (m->api_bus) - return 0; - - if (m->running_as == MANAGER_SYSTEM && m->system_bus) - m->api_bus = m->system_bus; - else { - if (!(m->api_bus = dbus_bus_get_private(m->running_as == MANAGER_USER ? DBUS_BUS_SESSION : DBUS_BUS_SYSTEM, &error))) { - log_debug("Failed to get API D-Bus connection, retrying later: %s", bus_error_message(&error)); - r = 0; - goto fail; - } - - if ((r = bus_setup_loop(m, m->api_bus)) < 0) - goto fail; - } - if (!dbus_connection_register_object_path(m->api_bus, "/org/freedesktop/systemd1", &bus_manager_vtable, m) || !dbus_connection_register_fallback(m->api_bus, "/org/freedesktop/systemd1/unit", &bus_unit_vtable, m) || !dbus_connection_register_fallback(m->api_bus, "/org/freedesktop/systemd1/job", &bus_job_vtable, m) || !dbus_connection_add_filter(m->api_bus, api_bus_message_filter, m, NULL)) { log_error("Not enough memory"); - r = -ENOMEM; - goto fail; + return -ENOMEM; } /* Get NameOwnerChange messages */ @@ -869,13 +825,7 @@ static int bus_init_api(Manager *m) { "interface='"DBUS_INTERFACE_DBUS"'," "member='NameOwnerChanged'," "path='"DBUS_PATH_DBUS"'", - &error); - - if (dbus_error_is_set(&error)) { - log_error("Failed to register match: %s", bus_error_message(&error)); - r = -EIO; - goto fail; - } + NULL); /* Get activation requests */ dbus_bus_add_match(m->api_bus, @@ -884,33 +834,225 @@ static int bus_init_api(Manager *m) { "interface='org.freedesktop.systemd1.Activator'," "member='ActivationRequest'," "path='"DBUS_PATH_DBUS"'", - &error); - - if (dbus_error_is_set(&error)) { - log_error("Failed to register match: %s", bus_error_message(&error)); - r = -EIO; - goto fail; - } + NULL); - if ((r = request_name(m)) < 0) - goto fail; + r = request_name(m); + if (r < 0) + return r; - if ((r = query_name_list(m)) < 0) - goto fail; + r = query_name_list(m); + if (r < 0) + return r; - if (m->api_bus != m->system_bus) { + if (m->running_as == MANAGER_USER) { char *id; log_debug("Successfully connected to API D-Bus bus %s as %s", strnull((id = dbus_connection_get_server_id(m->api_bus))), strnull(dbus_bus_get_unique_name(m->api_bus))); dbus_free(id); + } else + log_debug("Successfully initialized API on the system bus"); + + return 0; +} + +static void bus_register_cb(DBusPendingCall *pending, void *userdata) { + Manager *m = userdata; + DBusConnection **conn; + DBusMessage *reply; + DBusError error; + const char *name; + int r = 0; + + dbus_error_init(&error); + + conn = dbus_pending_call_get_data(pending, m->conn_data_slot); + assert(conn == &m->system_bus || conn == &m->api_bus); + + reply = dbus_pending_call_steal_reply(pending); + + switch (dbus_message_get_type(reply)) { + case DBUS_MESSAGE_TYPE_ERROR: + assert_se(dbus_set_error_from_message(&error, reply)); + log_warning("Failed to register to bus: %s", bus_error_message(&error)); + r = -1; + break; + case DBUS_MESSAGE_TYPE_METHOD_RETURN: + if (!dbus_message_get_args(reply, &error, + DBUS_TYPE_STRING, &name, + DBUS_TYPE_INVALID)) { + log_error("Failed to parse Hello reply: %s", bus_error_message(&error)); + r = -1; + break; + } + + log_debug("Received name %s in reply to Hello", name); + if (!dbus_bus_set_unique_name(*conn, name)) { + log_error("Failed to set unique name"); + r = -1; + break; + } + + if (conn == &m->system_bus) { + r = init_registered_system_bus(m); + if (r == 0 && m->running_as == MANAGER_SYSTEM) + r = init_registered_api_bus(m); + } else + r = init_registered_api_bus(m); + + break; + default: + assert_not_reached("Invalid reply message"); + } + + dbus_message_unref(reply); + dbus_error_free(&error); + + if (r < 0) { + if (conn == &m->system_bus) { + log_debug("Failed setting up the system bus"); + bus_done_system(m); + } else { + log_debug("Failed setting up the API bus"); + bus_done_api(m); + } + } +} + +static int manager_bus_async_register(Manager *m, DBusConnection **conn) { + DBusMessage *message = NULL; + DBusPendingCall *pending = NULL; + + message = dbus_message_new_method_call(DBUS_SERVICE_DBUS, + DBUS_PATH_DBUS, + DBUS_INTERFACE_DBUS, + "Hello"); + if (!message) + goto oom; + + if (!dbus_connection_send_with_reply(*conn, message, &pending, -1)) + goto oom; + + if (!dbus_pending_call_set_data(pending, m->conn_data_slot, conn, NULL)) + goto oom; + + if (!dbus_pending_call_set_notify(pending, bus_register_cb, m, NULL)) + goto oom; + + dbus_message_unref(message); + dbus_pending_call_unref(pending); + + return 0; +oom: + if (pending) { + dbus_pending_call_cancel(pending); + dbus_pending_call_unref(pending); + } + + if (message) + dbus_message_unref(message); + + return -ENOMEM; +} + +static DBusConnection* manager_bus_connect_private(Manager *m, DBusBusType type) { + const char *address; + DBusConnection *connection; + DBusError error; + + switch (type) { + case DBUS_BUS_SYSTEM: + address = getenv("DBUS_SYSTEM_BUS_ADDRESS"); + if (!address || !address[0]) + address = DBUS_SYSTEM_BUS_DEFAULT_ADDRESS; + break; + case DBUS_BUS_SESSION: + address = getenv("DBUS_SESSION_BUS_ADDRESS"); + if (!address || !address[0]) + address = DBUS_SESSION_BUS_DEFAULT_ADDRESS; + break; + default: + assert_not_reached("Invalid bus type"); + } + + dbus_error_init(&error); + + connection = dbus_connection_open_private(address, &error); + if (!connection) { + log_warning("Failed to open private bus connection: %s", bus_error_message(&error)); + goto fail; + } + + return connection; +fail: + if (connection) + dbus_connection_close(connection); + dbus_error_free(&error); + return NULL; +} + +static int bus_init_system(Manager *m) { + int r; + + if (m->system_bus) + return 0; + + m->system_bus = manager_bus_connect_private(m, DBUS_BUS_SYSTEM); + if (!m->system_bus) { + log_debug("Failed to connect to system D-Bus, retrying later"); + r = 0; + goto fail; } + r = bus_setup_loop(m, m->system_bus); + if (r < 0) + goto fail; + + r = manager_bus_async_register(m, &m->system_bus); + if (r < 0) + goto fail; + return 0; +fail: + bus_done_system(m); + + return r; +} + +static int bus_init_api(Manager *m) { + int r; + + if (m->api_bus) + return 0; + + if (m->running_as == MANAGER_SYSTEM) { + m->api_bus = m->system_bus; + /* In this mode there is no distinct connection to the API bus, + * the API is published on the system bus. + * bus_register_cb() is aware of that and will init the API + * when the system bus gets registered. + * No need to setup anything here. */ + return 0; + } + + m->api_bus = manager_bus_connect_private(m, DBUS_BUS_SESSION); + if (!m->api_bus) { + log_debug("Failed to connect to API D-Bus, retrying later"); + r = 0; + goto fail; + } + + r = bus_setup_loop(m, m->api_bus); + if (r < 0) + goto fail; + r = manager_bus_async_register(m, &m->api_bus); + if (r < 0) + goto fail; + + return 0; fail: bus_done_api(m); - dbus_error_free(&error); return r; } @@ -989,22 +1131,20 @@ int bus_init(Manager *m, bool try_bus_connect) { int r; if (set_ensure_allocated(&m->bus_connections, trivial_hash_func, trivial_compare_func) < 0 || - set_ensure_allocated(&m->bus_connections_for_dispatch, trivial_hash_func, trivial_compare_func) < 0) { - log_error("Not enough memory"); - return -ENOMEM; - } + set_ensure_allocated(&m->bus_connections_for_dispatch, trivial_hash_func, trivial_compare_func) < 0) + goto oom; if (m->name_data_slot < 0) - if (!dbus_pending_call_allocate_data_slot(&m->name_data_slot)) { - log_error("Not enough memory"); - return -ENOMEM; - } + if (!dbus_pending_call_allocate_data_slot(&m->name_data_slot)) + goto oom; + + if (m->conn_data_slot < 0) + if (!dbus_pending_call_allocate_data_slot(&m->conn_data_slot)) + goto oom; if (m->subscribed_data_slot < 0) - if (!dbus_connection_allocate_data_slot(&m->subscribed_data_slot)) { - log_error("Not enough memory"); - return -ENOMEM; - } + if (!dbus_connection_allocate_data_slot(&m->subscribed_data_slot)) + goto oom; if (try_bus_connect) { if ((r = bus_init_system(m)) < 0 || @@ -1016,6 +1156,9 @@ int bus_init(Manager *m, bool try_bus_connect) { return r; return 0; +oom: + log_error("Not enough memory"); + return -ENOMEM; } static void shutdown_connection(Manager *m, DBusConnection *c) { @@ -1053,48 +1196,46 @@ static void shutdown_connection(Manager *m, DBusConnection *c) { } dbus_connection_set_dispatch_status_function(c, NULL, NULL, NULL); - dbus_connection_flush(c); + /* system manager cannot afford to block on DBus */ + if (m->running_as != MANAGER_SYSTEM) + dbus_connection_flush(c); dbus_connection_close(c); dbus_connection_unref(c); } static void bus_done_api(Manager *m) { - assert(m); - - if (m->api_bus) { - if (m->system_bus == m->api_bus) - m->system_bus = NULL; + if (!m->api_bus) + return; + if (m->running_as == MANAGER_USER) shutdown_connection(m, m->api_bus); - m->api_bus = NULL; - } + m->api_bus = NULL; - if (m->queued_message) { - dbus_message_unref(m->queued_message); - m->queued_message = NULL; - } + if (m->queued_message) { + dbus_message_unref(m->queued_message); + m->queued_message = NULL; + } } static void bus_done_system(Manager *m) { - assert(m); + if (!m->system_bus) + return; - if (m->system_bus == m->api_bus) + if (m->running_as == MANAGER_SYSTEM) bus_done_api(m); - if (m->system_bus) { - shutdown_connection(m, m->system_bus); - m->system_bus = NULL; - } + shutdown_connection(m, m->system_bus); + m->system_bus = NULL; } static void bus_done_private(Manager *m) { + if (!m->private_bus) + return; - if (m->private_bus) { - dbus_server_disconnect(m->private_bus); - dbus_server_unref(m->private_bus); - m->private_bus = NULL; - } + dbus_server_disconnect(m->private_bus); + dbus_server_unref(m->private_bus); + m->private_bus = NULL; } void bus_done(Manager *m) { @@ -1116,6 +1257,9 @@ void bus_done(Manager *m) { if (m->name_data_slot >= 0) dbus_pending_call_free_data_slot(&m->name_data_slot); + if (m->conn_data_slot >= 0) + dbus_pending_call_free_data_slot(&m->conn_data_slot); + if (m->subscribed_data_slot >= 0) dbus_connection_free_data_slot(&m->subscribed_data_slot); } diff --git a/src/detect-virt.c b/src/detect-virt.c index 57f0176668..79cad5d8ab 100644 --- a/src/detect-virt.c +++ b/src/detect-virt.c @@ -25,16 +25,18 @@ #include <string.h> #include "util.h" +#include "virt.h" int main(int argc, char *argv[]) { - int r; + Virtualization 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) { + r = detect_virtualization(&id); + if (r < 0) { log_error("Failed to check for virtualization: %s", strerror(-r)); return EXIT_FAILURE; } @@ -42,5 +44,5 @@ int main(int argc, char *argv[]) { if (r > 0) puts(id); - return r == 0; + return r > 0 ? EXIT_SUCCESS : EXIT_FAILURE; } diff --git a/src/device.c b/src/device.c index 64b21903ed..0575379d80 100644 --- a/src/device.c +++ b/src/device.c @@ -46,13 +46,13 @@ static void device_unset_sysfs(Device *d) { /* Remove this unit from the chain of devices which share the * same sysfs path. */ - first = hashmap_get(d->meta.manager->devices_by_sysfs, d->sysfs); + first = hashmap_get(UNIT(d)->manager->devices_by_sysfs, d->sysfs); LIST_REMOVE(Device, same_sysfs, first, d); if (first) - hashmap_remove_and_replace(d->meta.manager->devices_by_sysfs, d->sysfs, first->sysfs, first); + hashmap_remove_and_replace(UNIT(d)->manager->devices_by_sysfs, d->sysfs, first->sysfs, first); else - hashmap_remove(d->meta.manager->devices_by_sysfs, d->sysfs); + hashmap_remove(UNIT(d)->manager->devices_by_sysfs, d->sysfs); free(d->sysfs); d->sysfs = NULL; @@ -62,17 +62,17 @@ static void device_init(Unit *u) { Device *d = DEVICE(u); assert(d); - assert(d->meta.load_state == UNIT_STUB); + assert(UNIT(d)->load_state == UNIT_STUB); /* In contrast to all other unit types we timeout jobs waiting * for devices by default. This is because they otherwise wait * 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; + UNIT(d)->job_timeout = DEFAULT_TIMEOUT_USEC; - d->meta.ignore_on_isolate = true; - d->meta.ignore_on_snapshot = true; + UNIT(d)->ignore_on_isolate = true; + UNIT(d)->ignore_on_snapshot = true; } static void device_done(Unit *u) { @@ -92,7 +92,7 @@ static void device_set_state(Device *d, DeviceState state) { if (state != old_state) log_debug("%s changed %s -> %s", - d->meta.id, + UNIT(d)->id, device_state_to_string(old_state), device_state_to_string(state)); @@ -198,10 +198,12 @@ static int device_update_unit(Manager *m, struct udev_device *dev, const char *p if (!u) { delete = true; - if (!(u = unit_new(m))) + u = unit_new(m, sizeof(Device)); + if (!u) return -ENOMEM; - if ((r = device_add_escaped_name(u, path)) < 0) + r = device_add_escaped_name(u, path); + if (r < 0) goto fail; unit_add_to_load_queue(u); @@ -387,16 +389,16 @@ static Unit *device_following(Unit *u) { assert(d); - if (startswith(u->meta.id, "sys-")) + if (startswith(u->id, "sys-")) return NULL; /* Make everybody follow the unit that's named after the sysfs path */ for (other = d->same_sysfs_next; other; other = other->same_sysfs_next) - if (startswith(other->meta.id, "sys-")) + if (startswith(UNIT(other)->id, "sys-")) return UNIT(other); for (other = d->same_sysfs_prev; other; other = other->same_sysfs_prev) { - if (startswith(other->meta.id, "sys-")) + if (startswith(UNIT(other)->id, "sys-")) return UNIT(other); first = other; @@ -583,6 +585,11 @@ DEFINE_STRING_TABLE_LOOKUP(device_state, DeviceState); const UnitVTable device_vtable = { .suffix = ".device", + .object_size = sizeof(Device), + .sections = + "Unit\0" + "Device\0" + "Install\0", .no_instances = true, diff --git a/src/device.h b/src/device.h index 9a56a5205b..a05c3d37b0 100644 --- a/src/device.h +++ b/src/device.h @@ -36,7 +36,7 @@ typedef enum DeviceState { } DeviceState; struct Device { - Meta meta; + Unit meta; char *sysfs; diff --git a/src/execute.c b/src/execute.c index 7b2567976d..dab485682b 100644 --- a/src/execute.c +++ b/src/execute.c @@ -56,6 +56,7 @@ #include "missing.h" #include "utmp-wtmp.h" #include "def.h" +#include "loopback-setup.h" /* This assumes there is a 'tty' group */ #define TTY_MODE 0620 @@ -172,24 +173,23 @@ static int open_null_as(int flags, int nfd) { static int connect_logger_as(const ExecContext *context, ExecOutput output, const char *ident, int nfd) { int fd, r; - union { - struct sockaddr sa; - struct sockaddr_un un; - } sa; + union sockaddr_union sa; assert(context); assert(output < _EXEC_OUTPUT_MAX); assert(ident); assert(nfd >= 0); - if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) + fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (fd < 0) return -errno; zero(sa); - sa.sa.sa_family = AF_UNIX; - strncpy(sa.un.sun_path, LOGGER_SOCKET, sizeof(sa.un.sun_path)); + sa.un.sun_family = AF_UNIX; + strncpy(sa.un.sun_path, "/run/systemd/journal/stdout", sizeof(sa.un.sun_path)); - if (connect(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + sizeof(LOGGER_SOCKET) - 1) < 0) { + r = connect(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path)); + if (r < 0) { close_nointr_nofail(fd); return -errno; } @@ -199,26 +199,19 @@ static int connect_logger_as(const ExecContext *context, ExecOutput output, cons return -errno; } - /* We speak a very simple protocol between log server - * and client: one line for the log destination (kmsg - * or syslog), followed by the priority field, - * followed by the process name. Since we replaced - * stdin/stderr we simple use stdio to write to - * it. Note that we use stderr, to minimize buffer - * flushing issues. */ - dprintf(fd, "%s\n" "%i\n" - "%s\n" + "%i\n" + "%i\n" + "%i\n" "%i\n", - output == EXEC_OUTPUT_KMSG ? "kmsg" : - output == EXEC_OUTPUT_KMSG_AND_CONSOLE ? "kmsg+console" : - output == EXEC_OUTPUT_SYSLOG ? "syslog" : - "syslog+console", - context->syslog_priority, context->syslog_identifier ? context->syslog_identifier : ident, - context->syslog_level_prefix); + context->syslog_priority, + !!context->syslog_level_prefix, + output == EXEC_OUTPUT_SYSLOG || output == EXEC_OUTPUT_SYSLOG_AND_CONSOLE, + output == EXEC_OUTPUT_KMSG || output == EXEC_OUTPUT_KMSG_AND_CONSOLE, + output == EXEC_OUTPUT_SYSLOG_AND_CONSOLE || output == EXEC_OUTPUT_KMSG_AND_CONSOLE || output == EXEC_OUTPUT_JOURNAL_AND_CONSOLE); if (fd != nfd) { r = dup2(fd, nfd) < 0 ? -errno : nfd; @@ -358,6 +351,8 @@ static int setup_output(const ExecContext *context, int socket_fd, const char *i case EXEC_OUTPUT_SYSLOG_AND_CONSOLE: case EXEC_OUTPUT_KMSG: case EXEC_OUTPUT_KMSG_AND_CONSOLE: + case EXEC_OUTPUT_JOURNAL: + case EXEC_OUTPUT_JOURNAL_AND_CONSOLE: return connect_logger_as(context, o, ident, STDOUT_FILENO); case EXEC_OUTPUT_SOCKET: @@ -411,6 +406,8 @@ static int setup_error(const ExecContext *context, int socket_fd, const char *id case EXEC_OUTPUT_SYSLOG_AND_CONSOLE: case EXEC_OUTPUT_KMSG: case EXEC_OUTPUT_KMSG_AND_CONSOLE: + case EXEC_OUTPUT_JOURNAL: + case EXEC_OUTPUT_JOURNAL_AND_CONSOLE: return connect_logger_as(context, e, ident, STDERR_FILENO); case EXEC_OUTPUT_SOCKET: @@ -715,6 +712,7 @@ static int setup_pam( pam_handle_t *handle = NULL; sigset_t ss, old_ss; int pam_code = PAM_SUCCESS; + int err; char **e = NULL; bool close_session = false; pid_t pam_pid = 0, parent_pid; @@ -772,8 +770,8 @@ static int setup_pam( * termination */ /* This string must fit in 10 chars (i.e. the length - * of "/sbin/init") */ - rename_process("sd(PAM)"); + * of "/sbin/init"), to look pretty in /bin/ps */ + rename_process("(sd-pam)"); /* Make sure we don't keep open the passed fds in this child. We assume that otherwise only those fds are @@ -834,6 +832,11 @@ static int setup_pam( return 0; fail: + if (pam_code != PAM_SUCCESS) + err = -EPERM; /* PAM errors do not map to errno */ + else + err = -errno; + if (handle) { if (close_session) pam_code = pam_close_session(handle, PAM_DATA_SILENT); @@ -850,7 +853,7 @@ fail: kill(pam_pid, SIGCONT); } - return EXIT_PAM; + return err; } #endif @@ -894,12 +897,9 @@ static int do_capability_bounding_set_drop(uint64_t drop) { } } - for (i = 0; i <= MAX(63LU, (unsigned long) CAP_LAST_CAP); i++) + for (i = 0; i <= cap_last_cap(); i++) if (drop & ((uint64_t) 1ULL << (uint64_t) i)) { if (prctl(PR_CAPBSET_DROP, i) < 0) { - if (errno == EINVAL) - break; - r = -errno; goto finish; } @@ -919,6 +919,37 @@ finish: return r; } +static void rename_process_from_path(const char *path) { + char process_name[11]; + const char *p; + size_t l; + + /* This resulting string must fit in 10 chars (i.e. the length + * of "/sbin/init") to look pretty in /bin/ps */ + + p = file_name_from_path(path); + if (isempty(p)) { + rename_process("(...)"); + return; + } + + l = strlen(p); + if (l > 8) { + /* The end of the process name is usually more + * interesting, since the first bit might just be + * "systemd-" */ + p = p + l - 8; + l = 8; + } + + process_name[0] = '('; + memcpy(process_name+1, p, l); + process_name[1+l] = ')'; + process_name[1+l+1] = 0; + + rename_process(process_name); +} + int exec_spawn(ExecCommand *command, char **argv, const ExecContext *context, @@ -929,6 +960,7 @@ int exec_spawn(ExecCommand *command, bool apply_tty_stdin, bool confirm_spawn, CGroupBonding *cgroup_bondings, + CGroupAttribute *cgroup_attributes, pid_t *ret) { pid_t pid; @@ -972,9 +1004,11 @@ int exec_spawn(ExecCommand *command, log_debug("About to execute: %s", line); free(line); - if (cgroup_bondings) - if ((r = cgroup_bonding_realize_list(cgroup_bondings))) - goto fail_parent; + r = cgroup_bonding_realize_list(cgroup_bondings); + if (r < 0) + goto fail_parent; + + cgroup_attribute_apply_list(cgroup_attributes, cgroup_bondings); if ((pid = fork()) < 0) { r = -errno; @@ -982,7 +1016,7 @@ int exec_spawn(ExecCommand *command, } if (pid == 0) { - int i; + int i, err; sigset_t ss; const char *username = NULL, *home = NULL; uid_t uid = (uid_t) -1; @@ -990,13 +1024,11 @@ int exec_spawn(ExecCommand *command, char **our_env = NULL, **pam_env = NULL, **final_env = NULL, **final_argv = NULL; unsigned n_env = 0; int saved_stdout = -1, saved_stdin = -1; - bool keep_stdout = false, keep_stdin = false; + bool keep_stdout = false, keep_stdin = false, set_access = false; /* child */ - /* This string must fit in 10 chars (i.e. the length - * of "/sbin/init") */ - rename_process("sd(EXEC)"); + rename_process_from_path(command->path); /* We reset exactly these signals, since they are the * only ones we set to SIG_IGN in the main daemon. All @@ -1006,8 +1038,12 @@ int exec_spawn(ExecCommand *command, default_signals(SIGNALS_CRASH_HANDLER, SIGNALS_IGNORE, -1); - if (sigemptyset(&ss) < 0 || - sigprocmask(SIG_SETMASK, &ss, NULL) < 0) { + if (context->ignore_sigpipe) + ignore_signals(SIGPIPE, -1); + + assert_se(sigemptyset(&ss) == 0); + if (sigprocmask(SIG_SETMASK, &ss, NULL) < 0) { + err = -errno; r = EXIT_SIGNAL_MASK; goto fail_child; } @@ -1015,14 +1051,17 @@ int exec_spawn(ExecCommand *command, /* Close sockets very early to make sure we don't * block init reexecution because it cannot bind its * sockets */ - if (close_all_fds(socket_fd >= 0 ? &socket_fd : fds, - socket_fd >= 0 ? 1 : n_fds) < 0) { + log_forget_fds(); + err = close_all_fds(socket_fd >= 0 ? &socket_fd : fds, + socket_fd >= 0 ? 1 : n_fds); + if (err < 0) { r = EXIT_FDS; goto fail_child; } if (!context->same_pgrp) if (setsid() < 0) { + err = -errno; r = EXIT_SETSID; goto fail_child; } @@ -1030,12 +1069,14 @@ int exec_spawn(ExecCommand *command, if (context->tcpwrap_name) { if (socket_fd >= 0) if (!socket_tcpwrap(socket_fd, context->tcpwrap_name)) { + err = -EACCES; r = EXIT_TCPWRAP; goto fail_child; } for (i = 0; i < (int) n_fds; i++) { if (!socket_tcpwrap(fds[i], context->tcpwrap_name)) { + err = -EACCES; r = EXIT_TCPWRAP; goto fail_child; } @@ -1051,11 +1092,14 @@ int exec_spawn(ExecCommand *command, /* Set up terminal for the question */ if ((r = setup_confirm_stdio(context, - &saved_stdin, &saved_stdout))) + &saved_stdin, &saved_stdout))) { + err = -errno; goto fail_child; + } /* Now ask the question. */ if (!(line = exec_command_line(argv))) { + err = -ENOMEM; r = EXIT_MEMORY; goto fail_child; } @@ -1064,18 +1108,21 @@ int exec_spawn(ExecCommand *command, free(line); if (r < 0 || response == 'n') { + err = -ECANCELED; r = EXIT_CONFIRM; goto fail_child; } else if (response == 's') { - r = 0; + err = r = 0; goto fail_child; } /* Release terminal for the question */ if ((r = restore_confirm_stdio(context, &saved_stdin, &saved_stdout, - &keep_stdin, &keep_stdout))) + &keep_stdin, &keep_stdout))) { + err = -errno; goto fail_child; + } } /* If a socket is connected to STDIN/STDOUT/STDERR, we @@ -1083,28 +1130,35 @@ int exec_spawn(ExecCommand *command, if (socket_fd >= 0) fd_nonblock(socket_fd, false); - if (!keep_stdin) - if (setup_input(context, socket_fd, apply_tty_stdin) < 0) { + if (!keep_stdin) { + err = setup_input(context, socket_fd, apply_tty_stdin); + if (err < 0) { r = EXIT_STDIN; goto fail_child; } + } - if (!keep_stdout) - if (setup_output(context, socket_fd, file_name_from_path(command->path), apply_tty_stdin) < 0) { + if (!keep_stdout) { + err = setup_output(context, socket_fd, file_name_from_path(command->path), apply_tty_stdin); + if (err < 0) { r = EXIT_STDOUT; goto fail_child; } + } - if (setup_error(context, socket_fd, file_name_from_path(command->path), apply_tty_stdin) < 0) { + err = setup_error(context, socket_fd, file_name_from_path(command->path), apply_tty_stdin); + if (err < 0) { r = EXIT_STDERR; goto fail_child; } - if (cgroup_bondings) - if (cgroup_bonding_install_list(cgroup_bondings, 0) < 0) { + if (cgroup_bondings) { + err = cgroup_bonding_install_list(cgroup_bondings, 0); + if (err < 0) { r = EXIT_CGROUP; goto fail_child; } + } if (context->oom_score_adjust_set) { char t[16]; @@ -1125,6 +1179,7 @@ int exec_spawn(ExecCommand *command, if (write_one_line_file("/proc/self/oom_adj", t) < 0 && errno != EACCES) { + err = -errno; r = EXIT_OOM_ADJUST; goto fail_child; } @@ -1133,6 +1188,7 @@ int exec_spawn(ExecCommand *command, if (context->nice_set) if (setpriority(PRIO_PROCESS, 0, context->nice) < 0) { + err = -errno; r = EXIT_NICE; goto fail_child; } @@ -1145,6 +1201,7 @@ int exec_spawn(ExecCommand *command, if (sched_setscheduler(0, context->cpu_sched_policy | (context->cpu_sched_reset_on_fork ? SCHED_RESET_ON_FORK : 0), ¶m) < 0) { + err = -errno; r = EXIT_SETSCHEDULER; goto fail_child; } @@ -1152,84 +1209,120 @@ int exec_spawn(ExecCommand *command, if (context->cpuset) if (sched_setaffinity(0, CPU_ALLOC_SIZE(context->cpuset_ncpus), context->cpuset) < 0) { + err = -errno; r = EXIT_CPUAFFINITY; goto fail_child; } if (context->ioprio_set) if (ioprio_set(IOPRIO_WHO_PROCESS, 0, context->ioprio) < 0) { + err = -errno; r = EXIT_IOPRIO; goto fail_child; } if (context->timer_slack_nsec_set) if (prctl(PR_SET_TIMERSLACK, context->timer_slack_nsec) < 0) { + err = -errno; r = EXIT_TIMERSLACK; goto fail_child; } if (context->utmp_id) - utmp_put_init_process(0, context->utmp_id, getpid(), getsid(0), context->tty_path); + utmp_put_init_process(context->utmp_id, getpid(), getsid(0), context->tty_path); if (context->user) { username = context->user; - if (get_user_creds(&username, &uid, &gid, &home) < 0) { + err = get_user_creds(&username, &uid, &gid, &home); + if (err < 0) { r = EXIT_USER; goto fail_child; } - if (is_terminal_input(context->std_input)) - if (chown_terminal(STDIN_FILENO, uid) < 0) { + if (is_terminal_input(context->std_input)) { + err = chown_terminal(STDIN_FILENO, uid); + if (err < 0) { r = EXIT_STDIN; goto fail_child; } + } - if (cgroup_bondings && context->control_group_modify) - if (cgroup_bonding_set_group_access_list(cgroup_bondings, 0755, uid, gid) < 0 || - cgroup_bonding_set_task_access_list(cgroup_bondings, 0644, uid, gid) < 0) { + if (cgroup_bondings && context->control_group_modify) { + err = cgroup_bonding_set_group_access_list(cgroup_bondings, 0755, uid, gid); + if (err >= 0) + err = cgroup_bonding_set_task_access_list(cgroup_bondings, 0644, uid, gid, context->control_group_persistent); + if (err < 0) { r = EXIT_CGROUP; goto fail_child; } + + set_access = true; + } + } + + if (cgroup_bondings && !set_access && context->control_group_persistent >= 0) { + err = cgroup_bonding_set_task_access_list(cgroup_bondings, (mode_t) -1, (uid_t) -1, (uid_t) -1, context->control_group_persistent); + if (err < 0) { + r = EXIT_CGROUP; + goto fail_child; + } } - if (apply_permissions) - if (enforce_groups(context, username, uid) < 0) { + if (apply_permissions) { + err = enforce_groups(context, username, gid); + if (err < 0) { r = EXIT_GROUP; goto fail_child; } + } umask(context->umask); #ifdef HAVE_PAM if (context->pam_name && username) { - if (setup_pam(context->pam_name, username, context->tty_path, &pam_env, fds, n_fds) != 0) { + err = setup_pam(context->pam_name, username, context->tty_path, &pam_env, fds, n_fds); + if (err < 0) { r = EXIT_PAM; goto fail_child; } } #endif + if (context->private_network) { + if (unshare(CLONE_NEWNET) < 0) { + err = -errno; + r = EXIT_NETWORK; + goto fail_child; + } + + loopback_setup(); + } if (strv_length(context->read_write_dirs) > 0 || strv_length(context->read_only_dirs) > 0 || strv_length(context->inaccessible_dirs) > 0 || context->mount_flags != MS_SHARED || - context->private_tmp) - if ((r = setup_namespace( - context->read_write_dirs, - context->read_only_dirs, - context->inaccessible_dirs, - context->private_tmp, - context->mount_flags)) < 0) + context->private_tmp) { + err = setup_namespace(context->read_write_dirs, + context->read_only_dirs, + context->inaccessible_dirs, + context->private_tmp, + context->mount_flags); + if (err < 0) { + r = EXIT_NAMESPACE; goto fail_child; + } + } if (apply_chroot) { if (context->root_directory) if (chroot(context->root_directory) < 0) { + err = -errno; r = EXIT_CHROOT; goto fail_child; } if (chdir(context->working_directory ? context->working_directory : "/") < 0) { + err = -errno; r = EXIT_CHDIR; goto fail_child; } @@ -1240,11 +1333,13 @@ int exec_spawn(ExecCommand *command, if (asprintf(&d, "%s/%s", context->root_directory ? context->root_directory : "", context->working_directory ? context->working_directory : "") < 0) { + err = -ENOMEM; r = EXIT_MEMORY; goto fail_child; } if (chdir(d) < 0) { + err = -errno; free(d); r = EXIT_CHDIR; goto fail_child; @@ -1255,9 +1350,12 @@ int exec_spawn(ExecCommand *command, /* We repeat the fd closing here, to make sure that * nothing is leaked from the PAM modules */ - if (close_all_fds(fds, n_fds) < 0 || - shift_fds(fds, n_fds) < 0 || - flags_fds(fds, n_fds, context->non_blocking) < 0) { + err = close_all_fds(fds, n_fds); + if (err >= 0) + err = shift_fds(fds, n_fds); + if (err >= 0) + err = flags_fds(fds, n_fds, context->non_blocking); + if (err < 0) { r = EXIT_FDS; goto fail_child; } @@ -1269,22 +1367,27 @@ int exec_spawn(ExecCommand *command, continue; if (setrlimit(i, context->rlimit[i]) < 0) { + err = -errno; r = EXIT_LIMITS; goto fail_child; } } - if (context->capability_bounding_set_drop) - if (do_capability_bounding_set_drop(context->capability_bounding_set_drop) < 0) { + if (context->capability_bounding_set_drop) { + err = do_capability_bounding_set_drop(context->capability_bounding_set_drop); + if (err < 0) { r = EXIT_CAPABILITIES; goto fail_child; } + } - if (context->user) - if (enforce_user(context, uid) < 0) { + if (context->user) { + err = enforce_user(context, uid); + if (err < 0) { r = EXIT_USER; goto fail_child; } + } /* PR_GET_SECUREBITS is not privileged, while * PR_SET_SECUREBITS is. So to suppress @@ -1292,18 +1395,21 @@ int exec_spawn(ExecCommand *command, * PR_SET_SECUREBITS unless necessary. */ if (prctl(PR_GET_SECUREBITS) != context->secure_bits) if (prctl(PR_SET_SECUREBITS, context->secure_bits) < 0) { + err = -errno; r = EXIT_SECUREBITS; goto fail_child; } if (context->capabilities) if (cap_set_proc(context->capabilities) < 0) { + err = -errno; r = EXIT_CAPABILITIES; goto fail_child; } } if (!(our_env = new0(char*, 7))) { + err = -ENOMEM; r = EXIT_MEMORY; goto fail_child; } @@ -1311,12 +1417,14 @@ int exec_spawn(ExecCommand *command, if (n_fds > 0) if (asprintf(our_env + n_env++, "LISTEN_PID=%lu", (unsigned long) getpid()) < 0 || asprintf(our_env + n_env++, "LISTEN_FDS=%u", n_fds) < 0) { + err = -ENOMEM; r = EXIT_MEMORY; goto fail_child; } if (home) if (asprintf(our_env + n_env++, "HOME=%s", home) < 0) { + err = -ENOMEM; r = EXIT_MEMORY; goto fail_child; } @@ -1324,6 +1432,7 @@ int exec_spawn(ExecCommand *command, if (username) if (asprintf(our_env + n_env++, "LOGNAME=%s", username) < 0 || asprintf(our_env + n_env++, "USER=%s", username) < 0) { + err = -ENOMEM; r = EXIT_MEMORY; goto fail_child; } @@ -1332,6 +1441,7 @@ int exec_spawn(ExecCommand *command, context->std_output == EXEC_OUTPUT_TTY || context->std_error == EXEC_OUTPUT_TTY) if (!(our_env[n_env++] = strdup(default_term_for_tty(tty_path(context))))) { + err = -ENOMEM; r = EXIT_MEMORY; goto fail_child; } @@ -1346,11 +1456,13 @@ int exec_spawn(ExecCommand *command, files_env, pam_env, NULL))) { + err = -ENOMEM; r = EXIT_MEMORY; goto fail_child; } if (!(final_argv = replace_env_argv(argv, final_env))) { + err = -ENOMEM; r = EXIT_MEMORY; goto fail_child; } @@ -1358,9 +1470,17 @@ int exec_spawn(ExecCommand *command, final_env = strv_env_clean(final_env); execve(command->path, final_argv, final_env); + err = -errno; r = EXIT_EXEC; fail_child: + if (r != 0) { + log_open(); + log_warning("Failed at step %s spawning %s: %s", + exit_status_to_string(r, EXIT_STATUS_SYSTEMD), + command->path, strerror(-err)); + } + strv_free(our_env); strv_free(final_env); strv_free(pam_env); @@ -1402,7 +1522,7 @@ fail_parent: void exec_context_init(ExecContext *c) { assert(c); - c->umask = 0002; + c->umask = 0022; c->ioprio = IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE, 0); c->cpu_sched_policy = SCHED_OTHER; c->syslog_priority = LOG_DAEMON|LOG_INFO; @@ -1410,6 +1530,8 @@ void exec_context_init(ExecContext *c) { c->mount_flags = MS_SHARED; c->kill_signal = SIGTERM; c->send_sigkill = true; + c->control_group_persistent = -1; + c->ignore_sigpipe = true; } void exec_context_done(ExecContext *c) { @@ -1594,13 +1716,17 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) { "%sRootDirectory: %s\n" "%sNonBlocking: %s\n" "%sPrivateTmp: %s\n" - "%sControlGroupModify: %s\n", + "%sControlGroupModify: %s\n" + "%sControlGroupPersistent: %s\n" + "%sPrivateNetwork: %s\n", prefix, c->umask, prefix, c->working_directory ? c->working_directory : "/", prefix, c->root_directory ? c->root_directory : "/", prefix, yes_no(c->non_blocking), prefix, yes_no(c->private_tmp), - prefix, yes_no(c->control_group_modify)); + prefix, yes_no(c->control_group_modify), + prefix, yes_no(c->control_group_persistent), + prefix, yes_no(c->private_network)); STRV_FOREACH(e, c->environment) fprintf(f, "%sEnvironment: %s\n", prefix, *e); @@ -1673,10 +1799,10 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) { prefix, yes_no(c->tty_vhangup), prefix, yes_no(c->tty_vt_disallocate)); - if (c->std_output == EXEC_OUTPUT_SYSLOG || c->std_output == EXEC_OUTPUT_KMSG || - c->std_output == EXEC_OUTPUT_SYSLOG_AND_CONSOLE || c->std_output == EXEC_OUTPUT_KMSG_AND_CONSOLE || - c->std_error == EXEC_OUTPUT_SYSLOG || c->std_error == EXEC_OUTPUT_KMSG || - c->std_error == EXEC_OUTPUT_SYSLOG_AND_CONSOLE || c->std_error == EXEC_OUTPUT_KMSG_AND_CONSOLE) + if (c->std_output == EXEC_OUTPUT_SYSLOG || c->std_output == EXEC_OUTPUT_KMSG || c->std_output == EXEC_OUTPUT_JOURNAL || + c->std_output == EXEC_OUTPUT_SYSLOG_AND_CONSOLE || c->std_output == EXEC_OUTPUT_KMSG_AND_CONSOLE || c->std_output == EXEC_OUTPUT_JOURNAL_AND_CONSOLE || + c->std_error == EXEC_OUTPUT_SYSLOG || c->std_error == EXEC_OUTPUT_KMSG || c->std_error == EXEC_OUTPUT_JOURNAL || + c->std_error == EXEC_OUTPUT_SYSLOG_AND_CONSOLE || c->std_error == EXEC_OUTPUT_KMSG_AND_CONSOLE || c->std_error == EXEC_OUTPUT_JOURNAL_AND_CONSOLE) fprintf(f, "%sSyslogFacility: %s\n" "%sSyslogLevel: %s\n", @@ -1706,7 +1832,7 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) { unsigned long l; fprintf(f, "%sCapabilityBoundingSet:", prefix); - for (l = 0; l <= (unsigned long) CAP_LAST_CAP; l++) + for (l = 0; l <= cap_last_cap(); l++) if (!(c->capability_bounding_set_drop & ((uint64_t) 1ULL << (uint64_t) l))) { char *t; @@ -1754,10 +1880,12 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) { fprintf(f, "%sKillMode: %s\n" "%sKillSignal: SIG%s\n" - "%sSendSIGKILL: %s\n", + "%sSendSIGKILL: %s\n" + "%sIgnoreSIGPIPE: %s\n", prefix, kill_mode_to_string(c->kill_mode), prefix, signal_to_string(c->kill_signal), - prefix, yes_no(c->send_sigkill)); + prefix, yes_no(c->send_sigkill), + prefix, yes_no(c->ignore_sigpipe)); if (c->utmp_id) fprintf(f, @@ -1776,8 +1904,7 @@ void exec_status_start(ExecStatus *s, pid_t pid) { void exec_status_exit(ExecStatus *s, ExecContext *context, pid_t pid, int code, int status) { assert(s); - if ((s->pid && s->pid != pid) || - !s->start_timestamp.realtime <= 0) + if (s->pid && s->pid != pid) zero(*s); s->pid = pid; @@ -1961,6 +2088,8 @@ static const char* const exec_output_table[_EXEC_OUTPUT_MAX] = { [EXEC_OUTPUT_SYSLOG_AND_CONSOLE] = "syslog+console", [EXEC_OUTPUT_KMSG] = "kmsg", [EXEC_OUTPUT_KMSG_AND_CONSOLE] = "kmsg+console", + [EXEC_OUTPUT_JOURNAL] = "journal", + [EXEC_OUTPUT_JOURNAL_AND_CONSOLE] = "journal+console", [EXEC_OUTPUT_SOCKET] = "socket" }; diff --git a/src/execute.h b/src/execute.h index a2d9072357..0d7e7dd65d 100644 --- a/src/execute.h +++ b/src/execute.h @@ -35,12 +35,11 @@ typedef struct ExecContext ExecContext; #include <sched.h> struct CGroupBonding; +struct CGroupAttribute; #include "list.h" #include "util.h" -#define LOGGER_SOCKET "/run/systemd/logger" - typedef enum KillMode { KILL_CONTROL_GROUP = 0, KILL_PROCESS, @@ -75,6 +74,8 @@ typedef enum ExecOutput { EXEC_OUTPUT_SYSLOG_AND_CONSOLE, EXEC_OUTPUT_KMSG, EXEC_OUTPUT_KMSG_AND_CONSOLE, + EXEC_OUTPUT_JOURNAL, + EXEC_OUTPUT_JOURNAL_AND_CONSOLE, EXEC_OUTPUT_SOCKET, _EXEC_OUTPUT_MAX, _EXEC_OUTPUT_INVALID = -1 @@ -127,6 +128,8 @@ struct ExecContext { bool tty_vhangup; bool tty_vt_disallocate; + bool ignore_sigpipe; + /* Since resolving these names might might involve socket * connections and we don't want to deadlock ourselves these * names are resolved on execution only and in the child @@ -159,8 +162,10 @@ struct ExecContext { bool cpu_sched_reset_on_fork; bool non_blocking; bool private_tmp; + bool private_network; bool control_group_modify; + int control_group_persistent; /* This is not exposed to the user but available * internally. We need it to make sure that whenever we spawn @@ -186,6 +191,7 @@ int exec_spawn(ExecCommand *command, bool apply_tty_stdin, bool confirm_spawn, struct CGroupBonding *cgroup_bondings, + struct CGroupAttribute *cgroup_attributes, pid_t *ret); void exec_command_done(ExecCommand *c); diff --git a/src/exit-status.c b/src/exit-status.c index 9b7c027b43..ab8907d32c 100644 --- a/src/exit-status.c +++ b/src/exit-status.c @@ -116,6 +116,12 @@ const char* exit_status_to_string(ExitStatus status, ExitStatusLevel level) { case EXIT_PAM: return "PAM"; + + case EXIT_NETWORK: + return "NETWORK"; + + case EXIT_NAMESPACE: + return "NAMESPACE"; } } diff --git a/src/exit-status.h b/src/exit-status.h index 28f03a5911..44ef879562 100644 --- a/src/exit-status.h +++ b/src/exit-status.h @@ -64,7 +64,9 @@ typedef enum ExitStatus { EXIT_CONFIRM, EXIT_STDERR, EXIT_TCPWRAP, - EXIT_PAM + EXIT_PAM, + EXIT_NETWORK, + EXIT_NAMESPACE } ExitStatus; diff --git a/src/fsck.c b/src/fsck.c index 19ca75311b..d3ac83c25e 100644 --- a/src/fsck.c +++ b/src/fsck.c @@ -25,6 +25,7 @@ #include <errno.h> #include <unistd.h> #include <fcntl.h> +#include <sys/file.h> #include <libudev.h> #include <dbus/dbus.h> @@ -33,9 +34,11 @@ #include "dbus-common.h" #include "special.h" #include "bus-errors.h" +#include "virt.h" static bool arg_skip = false; static bool arg_force = false; +static bool arg_show_progress = false; static void start_target(const char *target, bool isolate) { DBusMessage *m = NULL, *reply = NULL; @@ -77,7 +80,7 @@ static void start_target(const char *target, bool isolate) { if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) { - /* Don't print a waring if we aren't called during + /* Don't print a warning if we aren't called during * startup */ if (!dbus_error_has_name(&error, BUS_ERROR_NO_SUCH_JOB)) log_error("Failed to start unit: %s", bus_error_message(&error)); @@ -124,7 +127,7 @@ static int parse_proc_cmdline(void) { arg_skip = true; else if (startswith(w, "fsck.mode")) log_warning("Invalid fsck.mode= parameter. Ignoring."); -#if defined(TARGET_FEDORA) || defined(TARGET_MANDRIVA) +#if defined(TARGET_FEDORA) || defined(TARGET_MANDRIVA) || defined(TARGET_MAGEIA) else if (strneq(w, "fastboot", l)) arg_skip = true; else if (strneq(w, "forcefsck", l)) @@ -142,10 +145,103 @@ static void test_files(void) { if (access("/forcefsck", F_OK) >= 0) arg_force = true; + + if (access("/run/systemd/show-status", F_OK) >= 0 || plymouth_running()) + arg_show_progress = true; +} + +static double percent(int pass, unsigned long cur, unsigned long max) { + /* Values stolen from e2fsck */ + + static const int pass_table[] = { + 0, 70, 90, 92, 95, 100 + }; + + if (pass <= 0) + return 0.0; + + if ((unsigned) pass >= ELEMENTSOF(pass_table) || max == 0) + return 100.0; + + return (double) pass_table[pass-1] + + ((double) pass_table[pass] - (double) pass_table[pass-1]) * + (double) cur / (double) max; +} + +static int process_progress(int fd) { + FILE *f, *console; + usec_t last = 0; + bool locked = false; + int clear = 0; + + f = fdopen(fd, "r"); + if (!f) { + close_nointr_nofail(fd); + return -errno; + } + + console = fopen("/dev/console", "w"); + if (!console) { + fclose(f); + return -ENOMEM; + } + + while (!feof(f)) { + int pass, m; + unsigned long cur, max; + char *device; + double p; + usec_t t; + + if (fscanf(f, "%i %lu %lu %ms", &pass, &cur, &max, &device) != 4) + break; + + /* Only show one progress counter at max */ + if (!locked) { + if (flock(fileno(console), LOCK_EX|LOCK_NB) < 0) { + free(device); + continue; + } + + locked = true; + } + + /* Only update once every 50ms */ + t = now(CLOCK_MONOTONIC); + if (last + 50 * USEC_PER_MSEC > t) { + free(device); + continue; + } + + last = t; + + p = percent(pass, cur, max); + fprintf(console, "\r%s: fsck %3.1f%% complete...\r%n", device, p, &m); + fflush(console); + + free(device); + + if (m > clear) + clear = m; + } + + if (clear > 0) { + unsigned j; + + fputc('\r', console); + for (j = 0; j < (unsigned) clear; j++) + fputc(' ', console); + fputc('\r', console); + fflush(console); + } + + fclose(f); + fclose(console); + return 0; } int main(int argc, char *argv[]) { - const char *cmdline[8]; + const char *cmdline[9]; int i = 0, r = EXIT_FAILURE, q; pid_t pid; siginfo_t status; @@ -153,16 +249,20 @@ int main(int argc, char *argv[]) { struct udev_device *udev_device = NULL; const char *device; bool root_directory; + int progress_pipe[2] = { -1, -1 }; + char dash_c[2+10+1]; if (argc > 2) { log_error("This program expects one or no arguments."); return EXIT_FAILURE; } - log_set_target(LOG_TARGET_SYSLOG_OR_KMSG); + log_set_target(LOG_TARGET_AUTO); log_parse_environment(); log_open(); + umask(0022); + parse_proc_cmdline(); test_files(); @@ -213,6 +313,12 @@ int main(int argc, char *argv[]) { root_directory = true; } + if (arg_show_progress) + if (pipe(progress_pipe) < 0) { + log_error("pipe(): %m"); + goto finish; + } + cmdline[i++] = "/sbin/fsck"; cmdline[i++] = "-a"; cmdline[i++] = "-T"; @@ -224,19 +330,39 @@ int main(int argc, char *argv[]) { if (arg_force) cmdline[i++] = "-f"; + if (progress_pipe[1] >= 0) { + snprintf(dash_c, sizeof(dash_c), "-C%i", progress_pipe[1]); + char_array_0(dash_c); + cmdline[i++] = dash_c; + } + cmdline[i++] = device; cmdline[i++] = NULL; - if ((pid = fork()) < 0) { + pid = fork(); + if (pid < 0) { log_error("fork(): %m"); goto finish; } else if (pid == 0) { /* Child */ + if (progress_pipe[0] >= 0) + close_nointr_nofail(progress_pipe[0]); execv(cmdline[0], (char**) cmdline); _exit(8); /* Operational error */ } - if ((q = wait_for_terminate(pid, &status)) < 0) { + if (progress_pipe[1] >= 0) { + close_nointr_nofail(progress_pipe[1]); + progress_pipe[1] = -1; + } + + if (progress_pipe[0] >= 0) { + process_progress(progress_pipe[0]); + progress_pipe[0] = -1; + } + + q = wait_for_terminate(pid, &status); + if (q < 0) { log_error("waitid(): %s", strerror(-q)); goto finish; } @@ -274,5 +400,7 @@ finish: if (udev) udev_unref(udev); + close_pipe(progress_pipe); + return r; } diff --git a/src/getty-generator.c b/src/getty-generator.c index 683775a4d5..1263785fb5 100644 --- a/src/getty-generator.c +++ b/src/getty-generator.c @@ -26,6 +26,7 @@ #include "log.h" #include "util.h" #include "unit-name.h" +#include "virt.h" const char *arg_dest = "/tmp"; @@ -33,6 +34,9 @@ static int add_symlink(const char *fservice, const char *tservice) { char *from = NULL, *to = NULL; int r; + assert(fservice); + assert(tservice); + asprintf(&from, SYSTEM_DATA_UNIT_PATH "/%s", fservice); asprintf(&to, "%s/getty.target.wants/%s", arg_dest, tservice); @@ -44,9 +48,15 @@ static int add_symlink(const char *fservice, const char *tservice) { mkdir_parents(to, 0755); - if ((r = symlink(from, to)) < 0) { - log_error("Failed to create symlink from %s to %s: %m", from, to); - r = -errno; + r = symlink(from, to); + if (r < 0) { + if (errno == EEXIST) + /* In case console=hvc0 is passed this will very likely result in EEXIST */ + r = 0; + else { + log_error("Failed to create symlink from %s to %s: %m", from, to); + r = -errno; + } } finish: @@ -57,24 +67,53 @@ finish: return r; } +static int add_serial_getty(const char *tty) { + char *n; + int r; + + assert(tty); + + log_debug("Automatically adding serial getty for /dev/%s.", tty); + + n = unit_name_replace_instance("serial-getty@.service", tty); + if (!n) { + log_error("Out of memory"); + return -ENOMEM; + } + + r = add_symlink("serial-getty@.service", n); + free(n); + + return r; +} + int main(int argc, char *argv[]) { + + static const char virtualization_consoles[] = + "hvc0\0" + "xvc0\0" + "hvsi0\0"; + int r = EXIT_SUCCESS; char *active; + const char *j; if (argc > 2) { log_error("This program takes one or no arguments."); return EXIT_FAILURE; } - if (argc > 1) - arg_dest = argv[1]; - - log_set_target(LOG_TARGET_SYSLOG_OR_KMSG); + log_set_target(LOG_TARGET_AUTO); log_parse_environment(); log_open(); + umask(0022); + + if (argc > 1) + arg_dest = argv[1]; + if (detect_container(NULL) > 0) { - log_debug("Automatic adding console shell."); + log_debug("Automatically adding console shell."); if (add_symlink("console-shell.service", "console-shell.service") < 0) r = EXIT_FAILURE; @@ -86,39 +125,56 @@ int main(int argc, char *argv[]) { if (read_one_line_file("/sys/class/tty/console/active", &active) >= 0) { const char *tty; - if ((tty = strrchr(active, ' '))) + tty = strrchr(active, ' '); + if (tty) tty ++; else tty = active; /* Automatically add in a serial getty on the kernel * console */ - if (!tty_is_vc(tty)) { - char *n; + if (tty_is_vc(tty)) + free(active); + else { + int k; /* We assume that gettys on virtual terminals are * started via manual configuration and do this magic * only for non-VC terminals. */ - log_debug("Automatically adding serial getty for /dev/%s.", tty); + k = add_serial_getty(tty); + free(active); - if (!(n = unit_name_replace_instance("serial-getty@.service", tty)) || - add_symlink("serial-getty@.service", n) < 0) + if (k < 0) { r = EXIT_FAILURE; - - free(n); + goto finish; + } } - - free(active); } /* Automatically add in a serial getty on the first * virtualizer console */ - if (access("/sys/class/tty/hvc0", F_OK) == 0) { - log_debug("Automatic adding serial getty for hvc0."); + NULSTR_FOREACH(j, virtualization_consoles) { + char *p; + int k; - if (add_symlink("serial-getty@.service", "serial-getty@hvc0.service") < 0) + if (asprintf(&p, "/sys/class/tty/%s", j) < 0) { + log_error("Out of memory"); r = EXIT_FAILURE; + goto finish; + } + + k = access(p, F_OK); + free(p); + + if (k < 0) + continue; + + k = add_serial_getty(j); + if (k < 0) { + r = EXIT_FAILURE; + goto finish; + } } finish: diff --git a/src/gnome-ask-password-agent.vala b/src/gnome-ask-password-agent.vala index c31c07e3db..e23aedbfee 100644 --- a/src/gnome-ask-password-agent.vala +++ b/src/gnome-ask-password-agent.vala @@ -37,13 +37,8 @@ public class PasswordDialog : Dialog { set_default_response(ResponseType.OK); set_icon_name(icon); -#if LIBNOTIFY07 add_button(Stock.CANCEL, ResponseType.CANCEL); add_button(Stock.OK, ResponseType.OK); -#else - add_button(STOCK_CANCEL, ResponseType.CANCEL); - add_button(STOCK_OK, ResponseType.OK); -#endif Container content = (Container) get_content_area(); @@ -190,12 +185,7 @@ public class MyStatusIcon : StatusIcon { set_visible(true); -#if LIBNOTIFY07 Notification n = new Notification(title, message, icon); -#else - Notification n = new Notification(title, message, icon, null); - n.attach_to_status_icon(this); -#endif n.set_timeout(5000); n.show(); diff --git a/src/hashmap.c b/src/hashmap.c index ca83e93385..7ef809746d 100644 --- a/src/hashmap.c +++ b/src/hashmap.c @@ -43,16 +43,94 @@ struct Hashmap { struct hashmap_entry *iterate_list_head, *iterate_list_tail; unsigned n_entries; + + bool from_pool; }; #define BY_HASH(h) ((struct hashmap_entry**) ((uint8_t*) (h) + ALIGN(sizeof(Hashmap)))) +struct pool { + struct pool *next; + unsigned n_tiles; + unsigned n_used; +}; + +struct pool *first_hashmap_pool = NULL; +static void *first_hashmap_tile = NULL; + +struct pool *first_entry_pool = NULL; +static void *first_entry_tile = NULL; + +static void* allocate_tile(struct pool **first_pool, void **first_tile, size_t tile_size) { + unsigned i; + + if (*first_tile) { + void *r; + + r = *first_tile; + *first_tile = * (void**) (*first_tile); + return r; + } + + if (_unlikely_(!*first_pool) || _unlikely_((*first_pool)->n_used >= (*first_pool)->n_tiles)) { + unsigned n; + size_t size; + struct pool *p; + + n = *first_pool ? (*first_pool)->n_tiles : 0; + n = MAX(512U, n * 2); + size = PAGE_ALIGN(ALIGN(sizeof(struct pool)) + n*tile_size); + n = (size - ALIGN(sizeof(struct pool))) / tile_size; + + p = malloc(size); + if (!p) + return NULL; + + p->next = *first_pool; + p->n_tiles = n; + p->n_used = 0; + + *first_pool = p; + } + + i = (*first_pool)->n_used++; + + return ((uint8_t*) (*first_pool)) + ALIGN(sizeof(struct pool)) + i*tile_size; +} + +static void deallocate_tile(void **first_tile, void *p) { + * (void**) p = *first_tile; + *first_tile = p; +} + +#ifndef __OPTIMIZE__ + +static void drop_pool(struct pool *p) { + while (p) { + struct pool *n; + n = p->next; + free(p); + p = n; + } +} + +__attribute__((destructor)) static void cleanup_pool(void) { + /* Be nice to valgrind */ + + drop_pool(first_hashmap_pool); + drop_pool(first_entry_pool); +} + +#endif + unsigned string_hash_func(const void *p) { - unsigned hash = 0; - const char *c; + unsigned hash = 5381; + const signed char *c; + + /* DJB's hash function */ for (c = p; *c; c++) - hash = 31 * hash + (unsigned) *c; + hash = (hash << 5) + hash + (unsigned) *c; return hash; } @@ -70,10 +148,26 @@ int trivial_compare_func(const void *a, const void *b) { } Hashmap *hashmap_new(hash_func_t hash_func, compare_func_t compare_func) { + bool b; Hashmap *h; + size_t size; - if (!(h = malloc0(ALIGN(sizeof(Hashmap)) + NBUCKETS * ALIGN(sizeof(struct hashmap_entry*))))) - return NULL; + b = is_main_thread(); + + size = ALIGN(sizeof(Hashmap)) + NBUCKETS * sizeof(struct hashmap_entry*); + + if (b) { + h = allocate_tile(&first_hashmap_pool, &first_hashmap_tile, size); + if (!h) + return NULL; + + memset(h, 0, size); + } else { + h = malloc0(size); + + if (!h) + return NULL; + } h->hash_func = hash_func ? hash_func : trivial_hash_func; h->compare_func = compare_func ? compare_func : trivial_compare_func; @@ -81,6 +175,8 @@ Hashmap *hashmap_new(hash_func_t hash_func, compare_func_t compare_func) { h->n_entries = 0; h->iterate_list_head = h->iterate_list_tail = NULL; + h->from_pool = b; + return h; } @@ -160,7 +256,11 @@ static void remove_entry(Hashmap *h, struct hashmap_entry *e) { hash = h->hash_func(e->key) % NBUCKETS; unlink_entry(h, e, hash); - free(e); + + if (h->from_pool) + deallocate_tile(&first_entry_tile, e); + else + free(e); } void hashmap_free(Hashmap*h) { @@ -170,7 +270,10 @@ void hashmap_free(Hashmap*h) { hashmap_clear(h); - free(h); + if (h->from_pool) + deallocate_tile(&first_hashmap_tile, h); + else + free(h); } void hashmap_free_free(Hashmap *h) { @@ -218,7 +321,12 @@ int hashmap_put(Hashmap *h, const void *key, void *value) { return -EEXIST; } - if (!(e = new(struct hashmap_entry, 1))) + if (h->from_pool) + e = allocate_tile(&first_entry_pool, &first_entry_tile, sizeof(struct hashmap_entry)); + else + e = new(struct hashmap_entry, 1); + + if (!e) return -ENOMEM; e->key = key; @@ -450,6 +558,17 @@ void* hashmap_first(Hashmap *h) { return h->iterate_list_head->value; } +void* hashmap_first_key(Hashmap *h) { + + if (!h) + return NULL; + + if (!h->iterate_list_head) + return NULL; + + return (void*) h->iterate_list_head->key; +} + void* hashmap_last(Hashmap *h) { if (!h) diff --git a/src/hashmap.h b/src/hashmap.h index 16ffbd3922..ab4363a7a3 100644 --- a/src/hashmap.h +++ b/src/hashmap.h @@ -74,6 +74,7 @@ void hashmap_clear(Hashmap *h); void *hashmap_steal_first(Hashmap *h); void *hashmap_steal_first_key(Hashmap *h); void* hashmap_first(Hashmap *h); +void* hashmap_first_key(Hashmap *h); void* hashmap_last(Hashmap *h); char **hashmap_get_strv(Hashmap *h); diff --git a/src/hostname-setup.c b/src/hostname-setup.c index 57db9fbf7c..2c2f10cfd1 100644 --- a/src/hostname-setup.c +++ b/src/hostname-setup.c @@ -30,9 +30,9 @@ #include "util.h" #include "log.h" -#if defined(TARGET_FEDORA) || defined(TARGET_ALTLINUX) || defined(TARGET_MANDRIVA) || defined(TARGET_MEEGO) +#if defined(TARGET_FEDORA) || defined(TARGET_ALTLINUX) || defined(TARGET_MANDRIVA) || defined(TARGET_MEEGO) || defined(TARGET_MAGEIA) #define FILENAME "/etc/sysconfig/network" -#elif defined(TARGET_SUSE) || defined(TARGET_SLACKWARE) || defined(TARGET_FRUGALWARE) +#elif defined(TARGET_SUSE) || defined(TARGET_SLACKWARE) #define FILENAME "/etc/HOSTNAME" #elif defined(TARGET_ARCH) #define FILENAME "/etc/rc.conf" @@ -64,7 +64,7 @@ static int read_and_strip_hostname(const char *path, char **hn) { static int read_distro_hostname(char **hn) { -#if defined(TARGET_FEDORA) || defined(TARGET_ARCH) || defined(TARGET_GENTOO) || defined(TARGET_ALTLINUX) || defined(TARGET_MANDRIVA) || defined(TARGET_MEEGO) +#if defined(TARGET_FEDORA) || defined(TARGET_ARCH) || defined(TARGET_GENTOO) || defined(TARGET_ALTLINUX) || defined(TARGET_MANDRIVA) || defined(TARGET_MEEGO) || defined(TARGET_MAGEIA) int r; FILE *f; @@ -114,7 +114,7 @@ finish: fclose(f); return r; -#elif defined(TARGET_SUSE) || defined(TARGET_SLACKWARE) || defined(TARGET_FRUGALWARE) +#elif defined(TARGET_SUSE) || defined(TARGET_SLACKWARE) return read_and_strip_hostname(FILENAME, hn); #else return -ENOENT; diff --git a/src/hostname/.gitignore b/src/hostname/.gitignore new file mode 100644 index 0000000000..1ff281b231 --- /dev/null +++ b/src/hostname/.gitignore @@ -0,0 +1 @@ +org.freedesktop.hostname1.policy diff --git a/src/hostname/Makefile b/src/hostname/Makefile new file mode 120000 index 0000000000..d0b0e8e008 --- /dev/null +++ b/src/hostname/Makefile @@ -0,0 +1 @@ +../Makefile
\ No newline at end of file diff --git a/src/hostnamed.c b/src/hostname/hostnamed.c index 7b2ce691a3..ad72440845 100644 --- a/src/hostnamed.c +++ b/src/hostname/hostnamed.c @@ -30,6 +30,8 @@ #include "strv.h" #include "dbus-common.h" #include "polkit.h" +#include "def.h" +#include "virt.h" #define INTERFACE \ " <interface name=\"org.freedesktop.hostname1\">\n" \ @@ -85,6 +87,8 @@ static char *data[_PROP_MAX] = { NULL }; +static usec_t remain_until = 0; + static void free_data(void) { int p; @@ -230,7 +234,7 @@ static int write_data_other(void) { assert(name[p]); if (isempty(data[p])) { - l = strv_env_unset(l, name[p]); + strv_env_unset(l, name[p]); continue; } @@ -276,18 +280,24 @@ static int bus_hostname_append_icon_name(DBusMessageIter *i, const char *propert return bus_property_append_string(i, property, (void*) name); } +static const BusProperty bus_hostname_properties[] = { + { "Hostname", bus_property_append_string, "s", sizeof(data[0])*PROP_HOSTNAME, true }, + { "StaticHostname", bus_property_append_string, "s", sizeof(data[0])*PROP_STATIC_HOSTNAME, true }, + { "PrettyHostname", bus_property_append_string, "s", sizeof(data[0])*PROP_PRETTY_HOSTNAME, true }, + { "IconName", bus_hostname_append_icon_name, "s", sizeof(data[0])*PROP_ICON_NAME, true }, + { NULL, } +}; + +static const BusBoundProperties bps[] = { + { "org.freedesktop.hostname1", bus_hostname_properties, data }, + { NULL, } +}; + static DBusHandlerResult hostname_message_handler( DBusConnection *connection, DBusMessage *message, void *userdata) { - const BusProperty properties[] = { - { "org.freedesktop.hostname1", "Hostname", bus_property_append_string, "s", data[PROP_HOSTNAME]}, - { "org.freedesktop.hostname1", "StaticHostname", bus_property_append_string, "s", data[PROP_STATIC_HOSTNAME]}, - { "org.freedesktop.hostname1", "PrettyHostname", bus_property_append_string, "s", data[PROP_PRETTY_HOSTNAME]}, - { "org.freedesktop.hostname1", "IconName", bus_hostname_append_icon_name, "s", data[PROP_ICON_NAME]}, - { NULL, NULL, NULL, NULL, NULL } - }; DBusMessage *reply = NULL, *changed = NULL; DBusError error; @@ -322,7 +332,7 @@ static DBusHandlerResult hostname_message_handler( if (!streq_ptr(name, data[PROP_HOSTNAME])) { char *h; - r = verify_polkit(connection, message, "org.freedesktop.hostname1.set-hostname", interactive, &error); + r = verify_polkit(connection, message, "org.freedesktop.hostname1.set-hostname", interactive, NULL, &error); if (r < 0) return bus_send_error_reply(connection, message, &error, r); @@ -366,7 +376,7 @@ static DBusHandlerResult hostname_message_handler( if (!streq_ptr(name, data[PROP_STATIC_HOSTNAME])) { - r = verify_polkit(connection, message, "org.freedesktop.hostname1.set-static-hostname", interactive, &error); + r = verify_polkit(connection, message, "org.freedesktop.hostname1.set-static-hostname", interactive, NULL, &error); if (r < 0) return bus_send_error_reply(connection, message, &error, r); @@ -393,7 +403,7 @@ static DBusHandlerResult hostname_message_handler( return bus_send_error_reply(connection, message, NULL, r); } - log_info("Changed static host name to '%s'", strempty(data[PROP_HOSTNAME])); + log_info("Changed static host name to '%s'", strempty(data[PROP_STATIC_HOSTNAME])); changed = bus_properties_changed_new( "/org/freedesktop/hostname1", @@ -431,7 +441,7 @@ static DBusHandlerResult hostname_message_handler( r = verify_polkit(connection, message, k == PROP_PRETTY_HOSTNAME ? "org.freedesktop.hostname1.set-static-hostname" : - "org.freedesktop.hostname1.set-machine-info", interactive, &error); + "org.freedesktop.hostname1.set-machine-info", interactive, NULL, &error); if (r < 0) return bus_send_error_reply(connection, message, &error, r); @@ -466,7 +476,7 @@ static DBusHandlerResult hostname_message_handler( } } else - return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, properties); + return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, bps); if (!(reply = dbus_message_new_method_return(message))) goto oom; @@ -518,7 +528,10 @@ static int connect_bus(DBusConnection **_bus) { goto fail; } - if (!dbus_connection_register_object_path(bus, "/org/freedesktop/hostname1", &hostname_vtable, NULL)) { + dbus_connection_set_exit_on_disconnect(bus, FALSE); + + if (!dbus_connection_register_object_path(bus, "/org/freedesktop/hostname1", &hostname_vtable, NULL) || + !dbus_connection_add_filter(bus, bus_exit_idle_filter, &remain_until, NULL)) { log_error("Not enough memory"); r = -ENOMEM; goto fail; @@ -554,11 +567,14 @@ fail: int main(int argc, char *argv[]) { int r; DBusConnection *bus = NULL; + bool exiting = false; log_set_target(LOG_TARGET_AUTO); log_parse_environment(); log_open(); + umask(0022); + if (argc == 2 && streq(argv[1], "--introspect")) { fputs(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE "<node>\n", stdout); @@ -576,8 +592,6 @@ int main(int argc, char *argv[]) { if (!check_nss()) log_warning("Warning: nss-myhostname is not installed. Changing the local hostname might make it unresolveable. Please install nss-myhostname!"); - umask(0022); - r = read_data(); if (r < 0) { log_error("Failed to read hostname data: %s", strerror(-r)); @@ -588,8 +602,17 @@ int main(int argc, char *argv[]) { if (r < 0) goto finish; - while (dbus_connection_read_write_dispatch(bus, -1)) - ; + remain_until = now(CLOCK_MONOTONIC) + DEFAULT_EXIT_USEC; + for (;;) { + + if (!dbus_connection_read_write_dispatch(bus, exiting ? -1 : (int) (DEFAULT_EXIT_USEC/USEC_PER_MSEC))) + break; + + if (!exiting && remain_until < now(CLOCK_MONOTONIC)) { + exiting = true; + bus_async_unregister_and_exit(bus, "org.freedesktop.hostname1"); + } + } r = 0; diff --git a/src/org.freedesktop.hostname1.conf b/src/hostname/org.freedesktop.hostname1.conf index eb241c022f..eb241c022f 100644 --- a/src/org.freedesktop.hostname1.conf +++ b/src/hostname/org.freedesktop.hostname1.conf diff --git a/src/org.freedesktop.hostname1.policy.in b/src/hostname/org.freedesktop.hostname1.policy.in index 7d56b22c28..7d56b22c28 100644 --- a/src/org.freedesktop.hostname1.policy.in +++ b/src/hostname/org.freedesktop.hostname1.policy.in diff --git a/src/org.freedesktop.hostname1.service b/src/hostname/org.freedesktop.hostname1.service index 42e4adb2c9..42e4adb2c9 100644 --- a/src/org.freedesktop.hostname1.service +++ b/src/hostname/org.freedesktop.hostname1.service diff --git a/src/initctl.c b/src/initctl.c index dd743142fd..53d03a9e10 100644 --- a/src/initctl.c +++ b/src/initctl.c @@ -34,13 +34,13 @@ #include <ctype.h> #include <dbus/dbus.h> +#include <systemd/sd-daemon.h> #include "util.h" #include "log.h" #include "list.h" #include "initreq.h" #include "special.h" -#include "sd-daemon.h" #include "dbus-common.h" #include "def.h" @@ -56,6 +56,8 @@ typedef struct Server { unsigned n_fifos; DBusConnection *bus; + + bool quit; } Server; struct Fifo { @@ -93,6 +95,8 @@ static const char *translate_runlevel(int runlevel, bool *isolate) { for (i = 0; i < ELEMENTSOF(table); i++) if (table[i].runlevel == runlevel) { *isolate = table[i].isolate; + if (runlevel == '6' && kexec_loaded()) + return SPECIAL_KEXEC_TARGET; return table[i].special; } @@ -165,7 +169,31 @@ static void request_process(Server *s, const struct init_request *req) { if (!isprint(req->runlevel)) log_error("Got invalid runlevel. Ignoring."); else - change_runlevel(s, req->runlevel); + switch (req->runlevel) { + + /* we are async anyway, so just use kill for reexec/reload */ + case 'u': + case 'U': + if (kill(1, SIGTERM) < 0) + log_error("kill() failed: %m"); + + /* The bus connection will be + * terminated if PID 1 is reexecuted, + * hence let's just exit here, and + * rely on that we'll be restarted on + * the next request */ + s->quit = true; + break; + + case 'q': + case 'Q': + if (kill(1, SIGHUP) < 0) + log_error("kill() failed: %m"); + break; + + default: + change_runlevel(s, req->runlevel); + } return; case INIT_CMD_POWERFAIL: @@ -360,10 +388,12 @@ int main(int argc, char *argv[]) { return EXIT_FAILURE; } - log_set_target(LOG_TARGET_SYSLOG_OR_KMSG); + log_set_target(LOG_TARGET_AUTO); log_parse_environment(); log_open(); + umask(0022); + if ((n = sd_listen_fds(true)) < 0) { log_error("Failed to read listening file descriptors from environment: %s", strerror(-r)); return EXIT_FAILURE; @@ -383,7 +413,7 @@ int main(int argc, char *argv[]) { "READY=1\n" "STATUS=Processing requests..."); - for (;;) { + while (!server.quit) { struct epoll_event event; int k; diff --git a/src/install.c b/src/install.c index b843ee156b..174d79bab6 100644 --- a/src/install.c +++ b/src/install.c @@ -72,9 +72,8 @@ static int get_config_path(UnitFileScope scope, bool runtime, const char *root_d case UNIT_FILE_SYSTEM: if (root_dir && runtime) - return -EINVAL; - - if (runtime) + asprintf(&p, "%s/run/systemd/system", root_dir); + else if (runtime) p = strdup("/run/systemd/system"); else if (root_dir) asprintf(&p, "%s/%s", root_dir, SYSTEM_CONFIG_UNIT_PATH); @@ -479,7 +478,6 @@ static int find_symlinks_fd( t = path_make_absolute(name, config_path); if (!t) { free(p); - free(dest); r = -ENOMEM; break; } @@ -1019,11 +1017,11 @@ static int unit_file_load( const char *path, bool allow_symlink) { - const ConfigItem items[] = { - { "Alias", config_parse_strv, 0, &info->aliases, "Install" }, - { "WantedBy", config_parse_strv, 0, &info->wanted_by, "Install" }, - { "Also", config_parse_also, 0, c, "Install" }, - { NULL, NULL, 0, NULL, NULL } + const ConfigTableItem items[] = { + { "Install", "Alias", config_parse_strv, 0, &info->aliases }, + { "Install", "WantedBy", config_parse_strv, 0, &info->wanted_by }, + { "Install", "Also", config_parse_also, 0, c }, + { NULL, NULL, NULL, 0, NULL } }; int fd; @@ -1044,7 +1042,7 @@ static int unit_file_load( return -ENOMEM; } - r = config_parse(path, f, NULL, items, true, info); + r = config_parse(path, f, NULL, config_item_table_lookup, (void*) items, true, info); fclose(f); if (r < 0) return r; @@ -1573,10 +1571,10 @@ UnitFileState unit_file_get_state( } if (lstat(path, &st) < 0) { + r = -errno; if (errno == ENOENT) continue; - r = -errno; goto finish; } @@ -1905,7 +1903,7 @@ int unit_file_get_list( } else if (r > 0) { f->state = UNIT_FILE_DISABLED; goto found; - } else if (r == 0) { + } else { f->state = UNIT_FILE_STATIC; goto found; } @@ -62,8 +62,8 @@ void job_free(Job *j) { if (j->installed) { bus_job_send_removed_signal(j); - if (j->unit->meta.job == j) { - j->unit->meta.job = NULL; + if (j->unit->job == j) { + j->unit->job = NULL; unit_add_to_gc_queue(j->unit); } @@ -147,7 +147,7 @@ void job_dump(Job *j, FILE*f, const char *prefix) { "%s\tState: %s\n" "%s\tForced: %s\n", prefix, j->id, - prefix, j->unit->meta.id, job_type_to_string(j->type), + prefix, j->unit->id, job_type_to_string(j->type), prefix, job_state_to_string(j->state), prefix, yes_no(j->override)); } @@ -326,19 +326,19 @@ bool job_is_runnable(Job *j) { * dependencies, regardless whether they are * starting or stopping something. */ - SET_FOREACH(other, j->unit->meta.dependencies[UNIT_AFTER], i) - if (other->meta.job) + SET_FOREACH(other, j->unit->dependencies[UNIT_AFTER], i) + if (other->job) return false; } /* Also, if something else is being stopped and we should * change state after it, then lets wait. */ - SET_FOREACH(other, j->unit->meta.dependencies[UNIT_BEFORE], i) - if (other->meta.job && - (other->meta.job->type == JOB_STOP || - other->meta.job->type == JOB_RESTART || - other->meta.job->type == JOB_TRY_RESTART)) + SET_FOREACH(other, j->unit->dependencies[UNIT_BEFORE], i) + if (other->job && + (other->job->type == JOB_STOP || + other->job->type == JOB_RESTART || + other->job->type == JOB_TRY_RESTART)) return false; /* This means that for a service a and a service b where b @@ -484,19 +484,20 @@ static void job_print_status_message(Unit *u, JobType t, JobResult result) { switch (result) { case JOB_DONE: - unit_status_printf(u, "Started %s.\n", unit_description(u)); + unit_status_printf(u, ANSI_HIGHLIGHT_GREEN_ON " OK " ANSI_HIGHLIGHT_OFF, "Started %s", unit_description(u)); break; case JOB_FAILED: - 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); + unit_status_printf(u, ANSI_HIGHLIGHT_RED_ON "FAILED" ANSI_HIGHLIGHT_OFF, "Failed to start %s", unit_description(u)); + unit_status_printf(u, NULL, "See 'systemctl status %s' for details.", u->id); break; case JOB_DEPENDENCY: - unit_status_printf(u, "Starting %s " ANSI_HIGHLIGHT_ON "aborted" ANSI_HIGHLIGHT_OFF " because a dependency failed.\n", unit_description(u)); + unit_status_printf(u, ANSI_HIGHLIGHT_RED_ON " ABORT" ANSI_HIGHLIGHT_OFF, "Dependency failed. Aborted start of %s", unit_description(u)); break; case JOB_TIMEOUT: - unit_status_printf(u, "Starting %s " ANSI_HIGHLIGHT_ON "timed out" ANSI_HIGHLIGHT_OFF ".\n", unit_description(u), u->meta.id); + unit_status_printf(u, ANSI_HIGHLIGHT_RED_ON " TIME " ANSI_HIGHLIGHT_OFF, "Timed out starting %s", unit_description(u)); break; default: @@ -508,12 +509,12 @@ static void job_print_status_message(Unit *u, JobType t, JobResult result) { switch (result) { case JOB_TIMEOUT: - unit_status_printf(u, "Stopping %s " ANSI_HIGHLIGHT_ON "timed out" ANSI_HIGHLIGHT_OFF ".\n", unit_description(u), u->meta.id); + unit_status_printf(u, ANSI_HIGHLIGHT_RED_ON " TIME " ANSI_HIGHLIGHT_OFF, "Timed out stopping %s", unit_description(u)); break; case JOB_DONE: case JOB_FAILED: - unit_status_printf(u, "Stopped %s.\n", unit_description(u)); + unit_status_printf(u, ANSI_HIGHLIGHT_GREEN_ON " OK " ANSI_HIGHLIGHT_OFF, "Stopped %s", unit_description(u)); break; default: @@ -527,6 +528,7 @@ int job_finish_and_invalidate(Job *j, JobResult result) { Unit *other; JobType t; Iterator i; + bool recursed = false; assert(j); assert(j->installed); @@ -537,19 +539,21 @@ int job_finish_and_invalidate(Job *j, JobResult result) { 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), - j->unit->meta.id, job_type_to_string(JOB_START)); + j->unit->id, job_type_to_string(j->type), + j->unit->id, job_type_to_string(JOB_START)); j->state = JOB_WAITING; j->type = JOB_START; job_add_to_run_queue(j); - return 0; + + u = j->unit; + goto finish; } j->result = result; - log_debug("Job %s/%s finished, result=%s", j->unit->meta.id, job_type_to_string(j->type), job_result_to_string(result)); + log_debug("Job %s/%s finished, result=%s", j->unit->id, job_type_to_string(j->type), job_result_to_string(result)); if (result == JOB_FAILED) j->manager->n_failed_jobs ++; @@ -567,36 +571,44 @@ int job_finish_and_invalidate(Job *j, JobResult result) { t == JOB_VERIFY_ACTIVE || t == JOB_RELOAD_OR_START) { - SET_FOREACH(other, u->meta.dependencies[UNIT_REQUIRED_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, 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, JOB_DEPENDENCY); - - SET_FOREACH(other, u->meta.dependencies[UNIT_REQUIRED_BY_OVERRIDABLE], i) - if (other->meta.job && - !other->meta.job->override && - (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, JOB_DEPENDENCY); + SET_FOREACH(other, u->dependencies[UNIT_REQUIRED_BY], i) + if (other->job && + (other->job->type == JOB_START || + other->job->type == JOB_VERIFY_ACTIVE || + other->job->type == JOB_RELOAD_OR_START)) { + job_finish_and_invalidate(other->job, JOB_DEPENDENCY); + recursed = true; + } + + SET_FOREACH(other, u->dependencies[UNIT_BOUND_BY], i) + if (other->job && + (other->job->type == JOB_START || + other->job->type == JOB_VERIFY_ACTIVE || + other->job->type == JOB_RELOAD_OR_START)) { + job_finish_and_invalidate(other->job, JOB_DEPENDENCY); + recursed = true; + } + + SET_FOREACH(other, u->dependencies[UNIT_REQUIRED_BY_OVERRIDABLE], i) + if (other->job && + !other->job->override && + (other->job->type == JOB_START || + other->job->type == JOB_VERIFY_ACTIVE || + other->job->type == JOB_RELOAD_OR_START)) { + job_finish_and_invalidate(other->job, JOB_DEPENDENCY); + recursed = true; + } } else if (t == JOB_STOP) { - SET_FOREACH(other, u->meta.dependencies[UNIT_CONFLICTED_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, JOB_DEPENDENCY); + SET_FOREACH(other, u->dependencies[UNIT_CONFLICTED_BY], i) + if (other->job && + (other->job->type == JOB_START || + other->job->type == JOB_VERIFY_ACTIVE || + other->job->type == JOB_RELOAD_OR_START)) { + job_finish_and_invalidate(other->job, JOB_DEPENDENCY); + recursed = true; + } } } @@ -606,24 +618,25 @@ int job_finish_and_invalidate(Job *j, JobResult result) { * unit itself. */ if (result == JOB_TIMEOUT || result == JOB_DEPENDENCY) { log_notice("Job %s/%s failed with result '%s'.", - u->meta.id, + u->id, job_type_to_string(t), job_result_to_string(result)); unit_trigger_on_failure(u); } +finish: /* Try to start the next jobs that can be started */ - SET_FOREACH(other, u->meta.dependencies[UNIT_AFTER], i) - if (other->meta.job) - job_add_to_run_queue(other->meta.job); - SET_FOREACH(other, u->meta.dependencies[UNIT_BEFORE], i) - if (other->meta.job) - job_add_to_run_queue(other->meta.job); + SET_FOREACH(other, u->dependencies[UNIT_AFTER], i) + if (other->job) + job_add_to_run_queue(other->job); + SET_FOREACH(other, u->dependencies[UNIT_BEFORE], i) + if (other->job) + job_add_to_run_queue(other->job); - manager_check_finished(u->meta.manager); + manager_check_finished(u->manager); - return 0; + return recursed; } int job_start_timer(Job *j) { @@ -632,7 +645,7 @@ int job_start_timer(Job *j) { int fd, r; assert(j); - if (j->unit->meta.job_timeout <= 0 || + if (j->unit->job_timeout <= 0 || j->timer_watch.type == WATCH_JOB_TIMER) return 0; @@ -644,7 +657,7 @@ int job_start_timer(Job *j) { } zero(its); - timespec_store(&its.it_value, j->unit->meta.job_timeout); + timespec_store(&its.it_value, j->unit->job_timeout); if (timerfd_settime(fd, 0, &its, NULL) < 0) { r = -errno; @@ -714,7 +727,7 @@ void job_timer_event(Job *j, uint64_t n_elapsed, Watch *w) { assert(j); assert(w == &j->timer_watch); - log_warning("Job %s/%s timed out.", j->unit->meta.id, job_type_to_string(j->type)); + log_warning("Job %s/%s timed out.", j->unit->id, job_type_to_string(j->type)); job_finish_and_invalidate(j, JOB_TIMEOUT); } diff --git a/src/journal/.gitignore b/src/journal/.gitignore new file mode 100644 index 0000000000..d6a79460cd --- /dev/null +++ b/src/journal/.gitignore @@ -0,0 +1,2 @@ +/journald-gperf.c +/libsystemd-journal.pc diff --git a/src/journal/Makefile b/src/journal/Makefile new file mode 120000 index 0000000000..d0b0e8e008 --- /dev/null +++ b/src/journal/Makefile @@ -0,0 +1 @@ +../Makefile
\ No newline at end of file diff --git a/src/journal/cat.c b/src/journal/cat.c new file mode 100644 index 0000000000..6745f1ce0a --- /dev/null +++ b/src/journal/cat.c @@ -0,0 +1,181 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2012 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 <stdio.h> +#include <getopt.h> +#include <assert.h> +#include <unistd.h> +#include <stdlib.h> +#include <errno.h> +#include <sys/fcntl.h> + +#include <systemd/sd-journal.h> + +#include "util.h" +#include "build.h" + +static char *arg_identifier = NULL; +static char arg_priority = LOG_INFO; +static bool arg_level_prefix = true; + +static int help(void) { + + printf("%s [OPTIONS...] {COMMAND} ...\n\n" + "Execute process with stdout/stderr connected to the journal.\n\n" + " -h --help Show this help\n" + " --version Show package version\n" + " -t --identifier=STRING Set syslog identifier\n" + " -p --priority=PRIORITY Set priority value (0..7)\n" + " --level-prefix=BOOL Control whether level prefix shall be parsed\n", + program_invocation_short_name); + + return 0; +} + +static int parse_argv(int argc, char *argv[]) { + + enum { + ARG_VERSION = 0x100, + ARG_LEVEL_PREFIX + }; + + static const struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "version" , no_argument, NULL, ARG_VERSION }, + { "identifier", required_argument, NULL, 't' }, + { "priority", required_argument, NULL, 'p' }, + { "level-prefix", required_argument, NULL, ARG_LEVEL_PREFIX }, + { NULL, 0, NULL, 0 } + }; + + int c; + + assert(argc >= 0); + assert(argv); + + while ((c = getopt_long(argc, argv, "+ht:p:", options, NULL)) >= 0) { + + switch (c) { + + case 'h': + help(); + return 0; + + case ARG_VERSION: + puts(PACKAGE_STRING); + puts(DISTRIBUTION); + puts(SYSTEMD_FEATURES); + return 0; + + case 't': + free(arg_identifier); + if (isempty(optarg)) + arg_identifier = NULL; + else { + arg_identifier = strdup(optarg); + if (!arg_identifier) { + log_error("Out of memory."); + return -ENOMEM; + } + } + break; + + case 'p': + arg_priority = log_level_from_string(optarg); + if (arg_priority < 0) { + log_error("Failed to parse priority value."); + return arg_priority; + } + break; + + case ARG_LEVEL_PREFIX: { + int k; + + k = parse_boolean(optarg); + if (k < 0) { + log_error("Failed to parse level prefix value."); + return k; + } + arg_level_prefix = k; + break; + } + + default: + log_error("Unknown option code %c", c); + return -EINVAL; + } + } + + return 1; +} + +int main(int argc, char *argv[]) { + int r, fd = -1, saved_stderr = -1; + + log_parse_environment(); + log_open(); + + r = parse_argv(argc, argv); + if (r <= 0) + goto finish; + + fd = sd_journal_stream_fd(arg_identifier, arg_priority, arg_level_prefix); + if (fd < 0) { + log_error("Failed to create stream fd: %s", strerror(fd)); + r = fd; + goto finish; + } + + saved_stderr = fcntl(STDERR_FILENO, F_DUPFD_CLOEXEC, 3); + + if (dup3(fd, STDOUT_FILENO, 0) < 0 || + dup3(fd, STDERR_FILENO, 0) < 0) { + log_error("Failed to duplicate fd: %s", strerror(fd)); + r = -errno; + goto finish; + } + + if (fd >= 3) + close_nointr_nofail(fd); + + fd = -1; + + if (argc <= optind) + execl("/bin/cat", "/bin/cat", NULL); + else + execvp(argv[optind], argv + optind); + + /* Let's try to restore a working stderr, so we can print the error message */ + if (saved_stderr >= 0) + dup3(saved_stderr, STDERR_FILENO, 0); + + log_error("Failed to execute process: %m"); + r = -errno; + +finish: + if (fd >= 0) + close_nointr_nofail(fd); + + if (saved_stderr >= 0) + close_nointr_nofail(saved_stderr); + + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/journal/compress.c b/src/journal/compress.c new file mode 100644 index 0000000000..ff906581f0 --- /dev/null +++ b/src/journal/compress.c @@ -0,0 +1,208 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2011 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 <assert.h> +#include <stdlib.h> +#include <string.h> +#include <lzma.h> + +#include "compress.h" + +bool compress_blob(const void *src, uint64_t src_size, void *dst, uint64_t *dst_size) { + lzma_stream s = LZMA_STREAM_INIT; + lzma_ret ret; + bool b = false; + + assert(src); + assert(src_size > 0); + assert(dst); + assert(dst_size); + + /* Returns false if we couldn't compress the data or the + * compressed result is longer than the original */ + + ret = lzma_easy_encoder(&s, LZMA_PRESET_DEFAULT, LZMA_CHECK_NONE); + if (ret != LZMA_OK) + return false; + + s.next_in = src; + s.avail_in = src_size; + s.next_out = dst; + s.avail_out = src_size; + + /* Does it fit? */ + if (lzma_code(&s, LZMA_FINISH) != LZMA_STREAM_END) + goto fail; + + /* Is it actually shorter? */ + if (s.avail_out == 0) + goto fail; + + *dst_size = src_size - s.avail_out; + b = true; + +fail: + lzma_end(&s); + + return b; +} + +bool uncompress_blob(const void *src, uint64_t src_size, + void **dst, uint64_t *dst_alloc_size, uint64_t* dst_size) { + + lzma_stream s = LZMA_STREAM_INIT; + lzma_ret ret; + bool b = false; + + assert(src); + assert(src_size > 0); + assert(dst); + assert(dst_alloc_size); + assert(dst_size); + assert(*dst_alloc_size == 0 || *dst); + + ret = lzma_stream_decoder(&s, UINT64_MAX, 0); + if (ret != LZMA_OK) + return false; + + if (*dst_alloc_size <= src_size) { + void *p; + + p = realloc(*dst, src_size*2); + if (!p) + return false; + + *dst = p; + *dst_alloc_size = src_size*2; + } + + s.next_in = src; + s.avail_in = src_size; + + s.next_out = *dst; + s.avail_out = *dst_alloc_size; + + for (;;) { + void *p; + + ret = lzma_code(&s, LZMA_FINISH); + + if (ret == LZMA_STREAM_END) + break; + + if (ret != LZMA_OK) + goto fail; + + p = realloc(*dst, *dst_alloc_size*2); + if (!p) + goto fail; + + s.next_out = (uint8_t*) p + ((uint8_t*) s.next_out - (uint8_t*) *dst); + s.avail_out += *dst_alloc_size; + + *dst = p; + *dst_alloc_size *= 2; + } + + *dst_size = *dst_alloc_size - s.avail_out; + b = true; + +fail: + lzma_end(&s); + + return b; +} + +bool uncompress_startswith(const void *src, uint64_t src_size, + void **buffer, uint64_t *buffer_size, + const void *prefix, uint64_t prefix_len, + uint8_t extra) { + + lzma_stream s = LZMA_STREAM_INIT; + lzma_ret ret; + bool b = false; + + /* Checks whether the uncompressed blob starts with the + * mentioned prefix. The byte extra needs to follow the + * prefix */ + + assert(src); + assert(src_size > 0); + assert(buffer); + assert(buffer_size); + assert(prefix); + assert(*buffer_size == 0 || *buffer); + + ret = lzma_stream_decoder(&s, UINT64_MAX, 0); + if (ret != LZMA_OK) + return false; + + if (*buffer_size <= prefix_len) { + void *p; + + p = realloc(*buffer, prefix_len*2); + if (!p) + return false; + + *buffer = p; + *buffer_size = prefix_len*2; + } + + s.next_in = src; + s.avail_in = src_size; + + s.next_out = *buffer; + s.avail_out = *buffer_size; + + for (;;) { + void *p; + + ret = lzma_code(&s, LZMA_FINISH); + + if (ret != LZMA_STREAM_END && ret != LZMA_OK) + goto fail; + + if ((*buffer_size - s.avail_out > prefix_len) && + memcmp(*buffer, prefix, prefix_len) == 0 && + ((const uint8_t*) *buffer)[prefix_len] == extra) + break; + + if (ret == LZMA_STREAM_END) + goto fail; + + p = realloc(*buffer, *buffer_size*2); + if (!p) + goto fail; + + s.next_out = (uint8_t*) p + ((uint8_t*) s.next_out - (uint8_t*) *buffer); + s.avail_out += *buffer_size; + + *buffer = p; + *buffer_size *= 2; + } + + b = true; + +fail: + lzma_end(&s); + + return b; +} diff --git a/src/journal/compress.h b/src/journal/compress.h new file mode 100644 index 0000000000..f187a6e00c --- /dev/null +++ b/src/journal/compress.h @@ -0,0 +1,38 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#ifndef foocompresshfoo +#define foocompresshfoo + +/*** + This file is part of systemd. + + Copyright 2011 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 <inttypes.h> +#include <stdbool.h> + +bool compress_blob(const void *src, uint64_t src_size, void *dst, uint64_t *dst_size); + +bool uncompress_blob(const void *src, uint64_t src_size, + void **dst, uint64_t *dst_alloc_size, uint64_t* dst_size); + +bool uncompress_startswith(const void *src, uint64_t src_size, + void **buffer, uint64_t *buffer_size, + const void *prefix, uint64_t prefix_len, + uint8_t extra); + +#endif diff --git a/src/journal/coredump.c b/src/journal/coredump.c new file mode 100644 index 0000000000..7dea66e6fd --- /dev/null +++ b/src/journal/coredump.c @@ -0,0 +1,271 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2012 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 <errno.h> +#include <unistd.h> +#include <stdio.h> +#include <sys/prctl.h> + +#include <systemd/sd-journal.h> +#include <systemd/sd-login.h> + +#include "log.h" +#include "util.h" +#include "special.h" + +#define COREDUMP_MAX (24*1024*1024) + +enum { + ARG_PID = 1, + ARG_UID, + ARG_GID, + ARG_SIGNAL, + ARG_TIMESTAMP, + ARG_COMM, + _ARG_MAX +}; + +static int divert_coredump(void) { + FILE *f; + int r; + + log_info("Detected coredump of the journal daemon itself, diverting coredump to /var/lib/systemd/coredump/."); + + mkdir_p("/var/lib/systemd/coredump", 0755); + + f = fopen("/var/lib/systemd/coredump/core.systemd-journald", "we"); + if (!f) { + log_error("Failed to create coredump file: %m"); + return -errno; + } + + for (;;) { + uint8_t buffer[4096]; + size_t l, q; + + l = fread(buffer, 1, sizeof(buffer), stdin); + if (l <= 0) { + if (ferror(f)) { + log_error("Failed to read coredump: %m"); + r = -errno; + goto finish; + } + + r = 0; + break; + } + + q = fwrite(buffer, 1, l, f); + if (q != l) { + log_error("Failed to write coredump: %m"); + r = -errno; + goto finish; + } + } + + fflush(f); + + if (ferror(f)) { + log_error("Failed to write coredump: %m"); + r = -errno; + } + +finish: + fclose(f); + return r; +} + +int main(int argc, char* argv[]) { + int r, j = 0; + char *p = NULL; + ssize_t n; + pid_t pid; + uid_t uid; + gid_t gid; + struct iovec iovec[14]; + char *core_pid = NULL, *core_uid = NULL, *core_gid = NULL, *core_signal = NULL, + *core_timestamp = NULL, *core_comm = NULL, *core_exe = NULL, *core_unit = NULL, + *core_session = NULL, *core_message = NULL, *core_cmdline = NULL, *t; + + prctl(PR_SET_DUMPABLE, 0); + + if (argc != _ARG_MAX) { + log_set_target(LOG_TARGET_JOURNAL_OR_KMSG); + log_open(); + + log_error("Invalid number of arguments passed from kernel."); + r = -EINVAL; + goto finish; + } + + r = parse_pid(argv[ARG_PID], &pid); + if (r < 0) { + log_set_target(LOG_TARGET_JOURNAL_OR_KMSG); + log_open(); + + log_error("Failed to parse PID."); + goto finish; + } + + if (sd_pid_get_unit(pid, &t) >= 0) { + + if (streq(t, SPECIAL_JOURNALD_SERVICE)) { + /* Make sure we don't make use of the journal, + * if it's the journal which is crashing */ + log_set_target(LOG_TARGET_KMSG); + log_open(); + + r = divert_coredump(); + goto finish; + } + + core_unit = strappend("COREDUMP_UNIT=", t); + free(t); + + if (core_unit) + IOVEC_SET_STRING(iovec[j++], core_unit); + } + + /* OK, now we know it's not the journal, hence make use of + * it */ + log_set_target(LOG_TARGET_JOURNAL_OR_KMSG); + log_open(); + + r = parse_uid(argv[ARG_UID], &uid); + if (r < 0) { + log_error("Failed to parse UID."); + goto finish; + } + + r = parse_gid(argv[ARG_GID], &gid); + if (r < 0) { + log_error("Failed to parse GID."); + goto finish; + } + + core_pid = strappend("COREDUMP_PID=", argv[ARG_PID]); + if (core_pid) + IOVEC_SET_STRING(iovec[j++], core_pid); + + core_uid = strappend("COREDUMP_UID=", argv[ARG_UID]); + if (core_uid) + IOVEC_SET_STRING(iovec[j++], core_uid); + + core_gid = strappend("COREDUMP_GID=", argv[ARG_GID]); + if (core_gid) + IOVEC_SET_STRING(iovec[j++], core_gid); + + core_signal = strappend("COREDUMP_SIGNAL=", argv[ARG_SIGNAL]); + if (core_signal) + IOVEC_SET_STRING(iovec[j++], core_signal); + + core_comm = strappend("COREDUMP_COMM=", argv[ARG_COMM]); + if (core_comm) + IOVEC_SET_STRING(iovec[j++], core_comm); + + if (sd_pid_get_session(pid, &t) >= 0) { + core_session = strappend("COREDUMP_SESSION=", t); + free(t); + + if (core_session) + IOVEC_SET_STRING(iovec[j++], core_session); + } + + if (get_process_exe(pid, &t) >= 0) { + core_exe = strappend("COREDUMP_EXE=", t); + free(t); + + if (core_exe) + IOVEC_SET_STRING(iovec[j++], core_exe); + } + + if (get_process_cmdline(pid, LINE_MAX, false, &t) >= 0) { + core_cmdline = strappend("COREDUMP_CMDLINE=", t); + free(t); + + if (core_cmdline) + IOVEC_SET_STRING(iovec[j++], core_cmdline); + } + + core_timestamp = join("COREDUMP_TIMESTAMP=", argv[ARG_TIMESTAMP], "000000", NULL); + if (core_timestamp) + IOVEC_SET_STRING(iovec[j++], core_timestamp); + + IOVEC_SET_STRING(iovec[j++], "MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1"); + IOVEC_SET_STRING(iovec[j++], "PRIORITY=2"); + + core_message = join("MESSAGE=Process ", argv[ARG_PID], " (", argv[ARG_COMM], ") dumped core.", NULL); + if (core_message) + IOVEC_SET_STRING(iovec[j++], core_message); + + /* Now, let's drop privileges to become the user who owns the + * segfaulted process and allocate the coredump memory under + * his uid. This also ensures that the credentials journald + * will see are the ones of the coredumping user, thus making + * sure the user himself gets access to the core dump. */ + + if (setresgid(gid, gid, gid) < 0 || + setresuid(uid, uid, uid) < 0) { + log_error("Failed to drop privileges: %m"); + r = -errno; + goto finish; + } + + p = malloc(9 + COREDUMP_MAX); + if (!p) { + log_error("Out of memory"); + r = -ENOMEM; + goto finish; + } + + memcpy(p, "COREDUMP=", 9); + + n = loop_read(STDIN_FILENO, p + 9, COREDUMP_MAX, false); + if (n < 0) { + log_error("Failed to read core dump data: %s", strerror(-n)); + r = (int) n; + goto finish; + } + + iovec[j].iov_base = p; + iovec[j].iov_len = 9 + n; + j++; + + r = sd_journal_sendv(iovec, j); + if (r < 0) + log_error("Failed to send coredump: %s", strerror(-r)); + +finish: + free(p); + free(core_pid); + free(core_uid); + free(core_gid); + free(core_signal); + free(core_timestamp); + free(core_comm); + free(core_exe); + free(core_cmdline); + free(core_unit); + free(core_session); + free(core_message); + + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/journal/journal-def.h b/src/journal/journal-def.h new file mode 100644 index 0000000000..964e0c2b81 --- /dev/null +++ b/src/journal/journal-def.h @@ -0,0 +1,165 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#ifndef foojournaldefhfoo +#define foojournaldefhfoo + +/*** + This file is part of systemd. + + Copyright 2011 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 <inttypes.h> + +#include <systemd/sd-id128.h> + +#include "macro.h" + +typedef struct Header Header; +typedef struct ObjectHeader ObjectHeader; +typedef union Object Object; +typedef struct DataObject DataObject; +typedef struct FieldObject FieldObject; +typedef struct EntryObject EntryObject; +typedef struct HashTableObject HashTableObject; +typedef struct EntryArrayObject EntryArrayObject; +typedef struct EntryItem EntryItem; +typedef struct HashItem HashItem; + +/* Object types */ +enum { + OBJECT_UNUSED, + OBJECT_DATA, + OBJECT_FIELD, + OBJECT_ENTRY, + OBJECT_DATA_HASH_TABLE, + OBJECT_FIELD_HASH_TABLE, + OBJECT_ENTRY_ARRAY, + _OBJECT_TYPE_MAX +}; + +/* Object flags */ +enum { + OBJECT_COMPRESSED = 1 +}; + +_packed_ struct ObjectHeader { + uint8_t type; + uint8_t flags; + uint8_t reserved[6]; + uint64_t size; + uint8_t payload[]; +}; + +_packed_ struct DataObject { + ObjectHeader object; + uint64_t hash; + uint64_t next_hash_offset; + uint64_t next_field_offset; + uint64_t entry_offset; /* the first array entry we store inline */ + uint64_t entry_array_offset; + uint64_t n_entries; + uint8_t payload[]; +}; + +_packed_ struct FieldObject { + ObjectHeader object; + uint64_t hash; + uint64_t next_hash_offset; + uint64_t head_data_offset; + uint64_t tail_data_offset; + uint8_t payload[]; +}; + +_packed_ struct EntryItem { + uint64_t object_offset; + uint64_t hash; +}; + +_packed_ struct EntryObject { + ObjectHeader object; + uint64_t seqnum; + uint64_t realtime; + uint64_t monotonic; + sd_id128_t boot_id; + uint64_t xor_hash; + EntryItem items[]; +}; + +_packed_ struct HashItem { + uint64_t head_hash_offset; + uint64_t tail_hash_offset; +}; + +_packed_ struct HashTableObject { + ObjectHeader object; + HashItem items[]; +}; + +_packed_ struct EntryArrayObject { + ObjectHeader object; + uint64_t next_entry_array_offset; + uint64_t items[]; +}; + +union Object { + ObjectHeader object; + DataObject data; + FieldObject field; + EntryObject entry; + HashTableObject hash_table; + EntryArrayObject entry_array; +}; + +enum { + STATE_OFFLINE, + STATE_ONLINE, + STATE_ARCHIVED +}; + +/* Header flags */ +enum { + HEADER_INCOMPATIBLE_COMPRESSED = 1 +}; + +_packed_ struct Header { + uint8_t signature[8]; /* "LPKSHHRH" */ + uint32_t compatible_flags; + uint32_t incompatible_flags; + uint8_t state; + uint8_t reserved[7]; + sd_id128_t file_id; + sd_id128_t machine_id; + sd_id128_t boot_id; + sd_id128_t seqnum_id; + uint64_t arena_offset; + uint64_t arena_size; + uint64_t data_hash_table_offset; /* for looking up data objects */ + uint64_t data_hash_table_size; + uint64_t field_hash_table_offset; /* for looking up field objects */ + uint64_t field_hash_table_size; + uint64_t tail_object_offset; + uint64_t n_objects; + uint64_t n_entries; + uint64_t seqnum; + uint64_t first_seqnum; + uint64_t entry_array_offset; + uint64_t head_entry_realtime; + uint64_t tail_entry_realtime; + uint64_t tail_entry_monotonic; +}; + +#endif diff --git a/src/journal/journal-file.c b/src/journal/journal-file.c new file mode 100644 index 0000000000..20ca3f61cb --- /dev/null +++ b/src/journal/journal-file.c @@ -0,0 +1,2176 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2011 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 <sys/mman.h> +#include <errno.h> +#include <sys/uio.h> +#include <unistd.h> +#include <sys/statvfs.h> +#include <fcntl.h> +#include <stddef.h> + +#include "journal-def.h" +#include "journal-file.h" +#include "lookup3.h" +#include "compress.h" + +#define DEFAULT_DATA_HASH_TABLE_SIZE (2047ULL*16ULL) +#define DEFAULT_FIELD_HASH_TABLE_SIZE (2047ULL*16ULL) + +#define DEFAULT_WINDOW_SIZE (128ULL*1024ULL*1024ULL) + +#define COMPRESSION_SIZE_THRESHOLD (512ULL) + +/* This is the minimum journal file size */ +#define JOURNAL_FILE_SIZE_MIN (64ULL*1024ULL) + +/* These are the lower and upper bounds if we deduce the max_use value + * from the file system size */ +#define DEFAULT_MAX_USE_LOWER (1ULL*1024ULL*1024ULL) /* 1 MiB */ +#define DEFAULT_MAX_USE_UPPER (4ULL*1024ULL*1024ULL*1024ULL) /* 4 GiB */ + +/* This is the upper bound if we deduce max_size from max_use */ +#define DEFAULT_MAX_SIZE_UPPER (16ULL*1024ULL*1024ULL) /* 16 MiB */ + +/* This is the upper bound if we deduce the keep_free value from the + * file system size */ +#define DEFAULT_KEEP_FREE_UPPER (4ULL*1024ULL*1024ULL*1024ULL) /* 4 GiB */ + +/* This is the keep_free value when we can't determine the system + * size */ +#define DEFAULT_KEEP_FREE (1024ULL*1024ULL) /* 1 MB */ + +static const char signature[] = { 'L', 'P', 'K', 'S', 'H', 'H', 'R', 'H' }; + +#define ALIGN64(x) (((x) + 7ULL) & ~7ULL) + +void journal_file_close(JournalFile *f) { + int t; + + assert(f); + + if (f->header && f->writable) + f->header->state = STATE_OFFLINE; + + + for (t = 0; t < _WINDOW_MAX; t++) + if (f->windows[t].ptr) + munmap(f->windows[t].ptr, f->windows[t].size); + + if (f->fd >= 0) + close_nointr_nofail(f->fd); + + free(f->path); + +#ifdef HAVE_XZ + free(f->compress_buffer); +#endif + + free(f); +} + +static int journal_file_init_header(JournalFile *f, JournalFile *template) { + Header h; + ssize_t k; + int r; + + assert(f); + + zero(h); + memcpy(h.signature, signature, 8); + h.arena_offset = htole64(ALIGN64(sizeof(h))); + + r = sd_id128_randomize(&h.file_id); + if (r < 0) + return r; + + if (template) { + h.seqnum_id = template->header->seqnum_id; + h.seqnum = template->header->seqnum; + } else + h.seqnum_id = h.file_id; + + k = pwrite(f->fd, &h, sizeof(h), 0); + if (k < 0) + return -errno; + + if (k != sizeof(h)) + return -EIO; + + return 0; +} + +static int journal_file_refresh_header(JournalFile *f) { + int r; + sd_id128_t boot_id; + + assert(f); + + r = sd_id128_get_machine(&f->header->machine_id); + if (r < 0) + return r; + + r = sd_id128_get_boot(&boot_id); + if (r < 0) + return r; + + if (sd_id128_equal(boot_id, f->header->boot_id)) + f->tail_entry_monotonic_valid = true; + + f->header->boot_id = boot_id; + + f->header->state = STATE_ONLINE; + + __sync_synchronize(); + + return 0; +} + +static int journal_file_verify_header(JournalFile *f) { + assert(f); + + if (memcmp(f->header, signature, 8)) + return -EBADMSG; + +#ifdef HAVE_XZ + if ((le64toh(f->header->incompatible_flags) & ~HEADER_INCOMPATIBLE_COMPRESSED) != 0) + return -EPROTONOSUPPORT; +#else + if (f->header->incompatible_flags != 0) + return -EPROTONOSUPPORT; +#endif + + if ((uint64_t) f->last_stat.st_size < (le64toh(f->header->arena_offset) + le64toh(f->header->arena_size))) + return -ENODATA; + + if (f->writable) { + uint32_t state; + sd_id128_t machine_id; + int r; + + r = sd_id128_get_machine(&machine_id); + if (r < 0) + return r; + + if (!sd_id128_equal(machine_id, f->header->machine_id)) + return -EHOSTDOWN; + + state = f->header->state; + + if (state == STATE_ONLINE) + log_debug("Journal file %s is already online. Assuming unclean closing. Ignoring.", f->path); + else if (state == STATE_ARCHIVED) + return -ESHUTDOWN; + else if (state != STATE_OFFLINE) + log_debug("Journal file %s has unknown state %u. Ignoring.", f->path, state); + } + + return 0; +} + +static int journal_file_allocate(JournalFile *f, uint64_t offset, uint64_t size) { + uint64_t old_size, new_size; + + assert(f); + + /* We assume that this file is not sparse, and we know that + * for sure, since we always call posix_fallocate() + * ourselves */ + + old_size = + le64toh(f->header->arena_offset) + + le64toh(f->header->arena_size); + + new_size = PAGE_ALIGN(offset + size); + if (new_size < le64toh(f->header->arena_offset)) + new_size = le64toh(f->header->arena_offset); + + if (new_size <= old_size) + return 0; + + if (f->metrics.max_size > 0 && + new_size > f->metrics.max_size) + return -E2BIG; + + if (new_size > f->metrics.min_size && + f->metrics.keep_free > 0) { + struct statvfs svfs; + + if (fstatvfs(f->fd, &svfs) >= 0) { + uint64_t available; + + available = svfs.f_bfree * svfs.f_bsize; + + if (available >= f->metrics.keep_free) + available -= f->metrics.keep_free; + else + available = 0; + + if (new_size - old_size > available) + return -E2BIG; + } + } + + /* Note that the glibc fallocate() fallback is very + inefficient, hence we try to minimize the allocation area + as we can. */ + if (posix_fallocate(f->fd, old_size, new_size - old_size) < 0) + return -errno; + + if (fstat(f->fd, &f->last_stat) < 0) + return -errno; + + f->header->arena_size = new_size - htole64(f->header->arena_offset); + + return 0; +} + +static int journal_file_map( + JournalFile *f, + uint64_t offset, + uint64_t size, + void **_window, + uint64_t *_woffset, + uint64_t *_wsize, + void **ret) { + + uint64_t woffset, wsize; + void *window; + + assert(f); + assert(size > 0); + assert(ret); + + woffset = offset & ~((uint64_t) page_size() - 1ULL); + wsize = size + (offset - woffset); + wsize = PAGE_ALIGN(wsize); + + /* Avoid SIGBUS on invalid accesses */ + if (woffset + wsize > (uint64_t) PAGE_ALIGN(f->last_stat.st_size)) + return -EADDRNOTAVAIL; + + window = mmap(NULL, wsize, f->prot, MAP_SHARED, f->fd, woffset); + if (window == MAP_FAILED) + return -errno; + + if (_window) + *_window = window; + + if (_woffset) + *_woffset = woffset; + + if (_wsize) + *_wsize = wsize; + + *ret = (uint8_t*) window + (offset - woffset); + + return 0; +} + +static int journal_file_move_to(JournalFile *f, int wt, uint64_t offset, uint64_t size, void **ret) { + void *p = NULL; + uint64_t delta; + int r; + Window *w; + + assert(f); + assert(ret); + assert(wt >= 0); + assert(wt < _WINDOW_MAX); + + if (offset + size > (uint64_t) f->last_stat.st_size) { + /* Hmm, out of range? Let's refresh the fstat() data + * first, before we trust that check. */ + + if (fstat(f->fd, &f->last_stat) < 0 || + offset + size > (uint64_t) f->last_stat.st_size) + return -EADDRNOTAVAIL; + } + + w = f->windows + wt; + + if (_likely_(w->ptr && + w->offset <= offset && + w->offset + w->size >= offset + size)) { + + *ret = (uint8_t*) w->ptr + (offset - w->offset); + return 0; + } + + if (w->ptr) { + if (munmap(w->ptr, w->size) < 0) + return -errno; + + w->ptr = NULL; + w->size = w->offset = 0; + } + + if (size < DEFAULT_WINDOW_SIZE) { + /* If the default window size is larger then what was + * asked for extend the mapping a bit in the hope to + * minimize needed remappings later on. We add half + * the window space before and half behind the + * requested mapping */ + + delta = (DEFAULT_WINDOW_SIZE - size) / 2; + + if (delta > offset) + delta = offset; + + offset -= delta; + size = DEFAULT_WINDOW_SIZE; + } else + delta = 0; + + if (offset + size > (uint64_t) f->last_stat.st_size) + size = (uint64_t) f->last_stat.st_size - offset; + + if (size <= 0) + return -EADDRNOTAVAIL; + + r = journal_file_map(f, + offset, size, + &w->ptr, &w->offset, &w->size, + &p); + + if (r < 0) + return r; + + *ret = (uint8_t*) p + delta; + return 0; +} + +static bool verify_hash(Object *o) { + uint64_t h1, h2; + + assert(o); + + if (o->object.type == OBJECT_DATA && !(o->object.flags & OBJECT_COMPRESSED)) { + h1 = le64toh(o->data.hash); + h2 = hash64(o->data.payload, le64toh(o->object.size) - offsetof(Object, data.payload)); + } else if (o->object.type == OBJECT_FIELD) { + h1 = le64toh(o->field.hash); + h2 = hash64(o->field.payload, le64toh(o->object.size) - offsetof(Object, field.payload)); + } else + return true; + + return h1 == h2; +} + +int journal_file_move_to_object(JournalFile *f, int type, uint64_t offset, Object **ret) { + int r; + void *t; + Object *o; + uint64_t s; + + assert(f); + assert(ret); + assert(type < _OBJECT_TYPE_MAX); + + r = journal_file_move_to(f, type >= 0 ? type : WINDOW_UNKNOWN, offset, sizeof(ObjectHeader), &t); + if (r < 0) + return r; + + o = (Object*) t; + s = le64toh(o->object.size); + + if (s < sizeof(ObjectHeader)) + return -EBADMSG; + + if (type >= 0 && o->object.type != type) + return -EBADMSG; + + if (s > sizeof(ObjectHeader)) { + r = journal_file_move_to(f, o->object.type, offset, s, &t); + if (r < 0) + return r; + + o = (Object*) t; + } + + if (!verify_hash(o)) + return -EBADMSG; + + *ret = o; + return 0; +} + +static uint64_t journal_file_seqnum(JournalFile *f, uint64_t *seqnum) { + uint64_t r; + + assert(f); + + r = le64toh(f->header->seqnum) + 1; + + if (seqnum) { + /* If an external seqnum counter was passed, we update + * both the local and the external one, and set it to + * the maximum of both */ + + if (*seqnum + 1 > r) + r = *seqnum + 1; + + *seqnum = r; + } + + f->header->seqnum = htole64(r); + + if (f->header->first_seqnum == 0) + f->header->first_seqnum = htole64(r); + + return r; +} + +static int journal_file_append_object(JournalFile *f, int type, uint64_t size, Object **ret, uint64_t *offset) { + int r; + uint64_t p; + Object *tail, *o; + void *t; + + assert(f); + assert(size >= sizeof(ObjectHeader)); + assert(offset); + assert(ret); + + p = le64toh(f->header->tail_object_offset); + if (p == 0) + p = le64toh(f->header->arena_offset); + else { + r = journal_file_move_to_object(f, -1, p, &tail); + if (r < 0) + return r; + + p += ALIGN64(le64toh(tail->object.size)); + } + + r = journal_file_allocate(f, p, size); + if (r < 0) + return r; + + r = journal_file_move_to(f, type, p, size, &t); + if (r < 0) + return r; + + o = (Object*) t; + + zero(o->object); + o->object.type = type; + o->object.size = htole64(size); + + f->header->tail_object_offset = htole64(p); + f->header->n_objects = htole64(le64toh(f->header->n_objects) + 1); + + *ret = o; + *offset = p; + + return 0; +} + +static int journal_file_setup_data_hash_table(JournalFile *f) { + uint64_t s, p; + Object *o; + int r; + + assert(f); + + s = DEFAULT_DATA_HASH_TABLE_SIZE; + r = journal_file_append_object(f, + OBJECT_DATA_HASH_TABLE, + offsetof(Object, hash_table.items) + s, + &o, &p); + if (r < 0) + return r; + + memset(o->hash_table.items, 0, s); + + f->header->data_hash_table_offset = htole64(p + offsetof(Object, hash_table.items)); + f->header->data_hash_table_size = htole64(s); + + return 0; +} + +static int journal_file_setup_field_hash_table(JournalFile *f) { + uint64_t s, p; + Object *o; + int r; + + assert(f); + + s = DEFAULT_FIELD_HASH_TABLE_SIZE; + r = journal_file_append_object(f, + OBJECT_FIELD_HASH_TABLE, + offsetof(Object, hash_table.items) + s, + &o, &p); + if (r < 0) + return r; + + memset(o->hash_table.items, 0, s); + + f->header->field_hash_table_offset = htole64(p + offsetof(Object, hash_table.items)); + f->header->field_hash_table_size = htole64(s); + + return 0; +} + +static int journal_file_map_data_hash_table(JournalFile *f) { + uint64_t s, p; + void *t; + int r; + + assert(f); + + p = le64toh(f->header->data_hash_table_offset); + s = le64toh(f->header->data_hash_table_size); + + r = journal_file_move_to(f, + WINDOW_DATA_HASH_TABLE, + p, s, + &t); + if (r < 0) + return r; + + f->data_hash_table = t; + return 0; +} + +static int journal_file_map_field_hash_table(JournalFile *f) { + uint64_t s, p; + void *t; + int r; + + assert(f); + + p = le64toh(f->header->field_hash_table_offset); + s = le64toh(f->header->field_hash_table_size); + + r = journal_file_move_to(f, + WINDOW_FIELD_HASH_TABLE, + p, s, + &t); + if (r < 0) + return r; + + f->field_hash_table = t; + return 0; +} + +static int journal_file_link_data(JournalFile *f, Object *o, uint64_t offset, uint64_t hash) { + uint64_t p, h; + int r; + + assert(f); + assert(o); + assert(offset > 0); + assert(o->object.type == OBJECT_DATA); + + o->data.next_hash_offset = o->data.next_field_offset = 0; + o->data.entry_offset = o->data.entry_array_offset = 0; + o->data.n_entries = 0; + + h = hash % (le64toh(f->header->data_hash_table_size) / sizeof(HashItem)); + p = le64toh(f->data_hash_table[h].head_hash_offset); + if (p == 0) { + /* Only entry in the hash table is easy */ + f->data_hash_table[h].head_hash_offset = htole64(offset); + } else { + /* Temporarily move back to the previous data object, + * to patch in pointer */ + + r = journal_file_move_to_object(f, OBJECT_DATA, p, &o); + if (r < 0) + return r; + + o->data.next_hash_offset = htole64(offset); + + r = journal_file_move_to_object(f, OBJECT_DATA, offset, &o); + if (r < 0) + return r; + } + + f->data_hash_table[h].tail_hash_offset = htole64(offset); + + return 0; +} + +int journal_file_find_data_object_with_hash( + JournalFile *f, + const void *data, uint64_t size, uint64_t hash, + Object **ret, uint64_t *offset) { + uint64_t p, osize, h; + int r; + + assert(f); + assert(data || size == 0); + + osize = offsetof(Object, data.payload) + size; + + if (f->header->data_hash_table_size == 0) + return -EBADMSG; + + h = hash % (le64toh(f->header->data_hash_table_size) / sizeof(HashItem)); + p = le64toh(f->data_hash_table[h].head_hash_offset); + + while (p > 0) { + Object *o; + + r = journal_file_move_to_object(f, OBJECT_DATA, p, &o); + if (r < 0) + return r; + + if (le64toh(o->data.hash) != hash) + goto next; + + if (o->object.flags & OBJECT_COMPRESSED) { +#ifdef HAVE_XZ + uint64_t l, rsize; + + l = le64toh(o->object.size); + if (l <= offsetof(Object, data.payload)) + return -EBADMSG; + + l -= offsetof(Object, data.payload); + + if (!uncompress_blob(o->data.payload, l, &f->compress_buffer, &f->compress_buffer_size, &rsize)) + return -EBADMSG; + + if (rsize == size && + memcmp(f->compress_buffer, data, size) == 0) { + + if (ret) + *ret = o; + + if (offset) + *offset = p; + + return 1; + } +#else + return -EPROTONOSUPPORT; +#endif + + } else if (le64toh(o->object.size) == osize && + memcmp(o->data.payload, data, size) == 0) { + + if (ret) + *ret = o; + + if (offset) + *offset = p; + + return 1; + } + + next: + p = le64toh(o->data.next_hash_offset); + } + + return 0; +} + +int journal_file_find_data_object( + JournalFile *f, + const void *data, uint64_t size, + Object **ret, uint64_t *offset) { + + uint64_t hash; + + assert(f); + assert(data || size == 0); + + hash = hash64(data, size); + + return journal_file_find_data_object_with_hash(f, + data, size, hash, + ret, offset); +} + +static int journal_file_append_data(JournalFile *f, const void *data, uint64_t size, Object **ret, uint64_t *offset) { + uint64_t hash, p; + uint64_t osize; + Object *o; + int r; + bool compressed = false; + + assert(f); + assert(data || size == 0); + + hash = hash64(data, size); + + r = journal_file_find_data_object_with_hash(f, data, size, hash, &o, &p); + if (r < 0) + return r; + else if (r > 0) { + + if (ret) + *ret = o; + + if (offset) + *offset = p; + + return 0; + } + + osize = offsetof(Object, data.payload) + size; + r = journal_file_append_object(f, OBJECT_DATA, osize, &o, &p); + if (r < 0) + return r; + + o->data.hash = htole64(hash); + +#ifdef HAVE_XZ + if (f->compress && + size >= COMPRESSION_SIZE_THRESHOLD) { + uint64_t rsize; + + compressed = compress_blob(data, size, o->data.payload, &rsize); + + if (compressed) { + o->object.size = htole64(offsetof(Object, data.payload) + rsize); + o->object.flags |= OBJECT_COMPRESSED; + + f->header->incompatible_flags = htole32(le32toh(f->header->incompatible_flags) | HEADER_INCOMPATIBLE_COMPRESSED); + + log_debug("Compressed data object %lu -> %lu", (unsigned long) size, (unsigned long) rsize); + } + } +#endif + + if (!compressed) + memcpy(o->data.payload, data, size); + + r = journal_file_link_data(f, o, p, hash); + if (r < 0) + return r; + + if (ret) + *ret = o; + + if (offset) + *offset = p; + + return 0; +} + +uint64_t journal_file_entry_n_items(Object *o) { + assert(o); + assert(o->object.type == htole64(OBJECT_ENTRY)); + + return (le64toh(o->object.size) - offsetof(Object, entry.items)) / sizeof(EntryItem); +} + +static uint64_t journal_file_entry_array_n_items(Object *o) { + assert(o); + assert(o->object.type == htole64(OBJECT_ENTRY_ARRAY)); + + return (le64toh(o->object.size) - offsetof(Object, entry_array.items)) / sizeof(uint64_t); +} + +static int link_entry_into_array(JournalFile *f, + uint64_t *first, + uint64_t *idx, + uint64_t p) { + int r; + uint64_t n = 0, ap = 0, q, i, a, hidx; + Object *o; + + assert(f); + assert(first); + assert(idx); + assert(p > 0); + + a = le64toh(*first); + i = hidx = le64toh(*idx); + while (a > 0) { + + r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o); + if (r < 0) + return r; + + n = journal_file_entry_array_n_items(o); + if (i < n) { + o->entry_array.items[i] = htole64(p); + *idx = htole64(hidx + 1); + return 0; + } + + i -= n; + ap = a; + a = le64toh(o->entry_array.next_entry_array_offset); + } + + if (hidx > n) + n = (hidx+1) * 2; + else + n = n * 2; + + if (n < 4) + n = 4; + + r = journal_file_append_object(f, OBJECT_ENTRY_ARRAY, + offsetof(Object, entry_array.items) + n * sizeof(uint64_t), + &o, &q); + if (r < 0) + return r; + + o->entry_array.items[i] = htole64(p); + + if (ap == 0) + *first = q; + else { + r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, ap, &o); + if (r < 0) + return r; + + o->entry_array.next_entry_array_offset = htole64(q); + } + + *idx = htole64(hidx + 1); + + return 0; +} + +static int link_entry_into_array_plus_one(JournalFile *f, + uint64_t *extra, + uint64_t *first, + uint64_t *idx, + uint64_t p) { + + int r; + + assert(f); + assert(extra); + assert(first); + assert(idx); + assert(p > 0); + + if (*idx == 0) + *extra = htole64(p); + else { + uint64_t i; + + i = le64toh(*idx) - 1; + r = link_entry_into_array(f, first, &i, p); + if (r < 0) + return r; + } + + *idx = htole64(le64toh(*idx) + 1); + return 0; +} + +static int journal_file_link_entry_item(JournalFile *f, Object *o, uint64_t offset, uint64_t i) { + uint64_t p; + int r; + assert(f); + assert(o); + assert(offset > 0); + + p = le64toh(o->entry.items[i].object_offset); + if (p == 0) + return -EINVAL; + + r = journal_file_move_to_object(f, OBJECT_DATA, p, &o); + if (r < 0) + return r; + + return link_entry_into_array_plus_one(f, + &o->data.entry_offset, + &o->data.entry_array_offset, + &o->data.n_entries, + offset); +} + +static int journal_file_link_entry(JournalFile *f, Object *o, uint64_t offset) { + uint64_t n, i; + int r; + + assert(f); + assert(o); + assert(offset > 0); + assert(o->object.type == OBJECT_ENTRY); + + __sync_synchronize(); + + /* Link up the entry itself */ + r = link_entry_into_array(f, + &f->header->entry_array_offset, + &f->header->n_entries, + offset); + if (r < 0) + return r; + + /* log_debug("=> %s seqnr=%lu n_entries=%lu", f->path, (unsigned long) o->entry.seqnum, (unsigned long) f->header->n_entries); */ + + if (f->header->head_entry_realtime == 0) + f->header->head_entry_realtime = o->entry.realtime; + + f->header->tail_entry_realtime = o->entry.realtime; + f->header->tail_entry_monotonic = o->entry.monotonic; + + f->tail_entry_monotonic_valid = true; + + /* Link up the items */ + n = journal_file_entry_n_items(o); + for (i = 0; i < n; i++) { + r = journal_file_link_entry_item(f, o, offset, i); + if (r < 0) + return r; + } + + return 0; +} + +static int journal_file_append_entry_internal( + JournalFile *f, + const dual_timestamp *ts, + uint64_t xor_hash, + const EntryItem items[], unsigned n_items, + uint64_t *seqnum, + Object **ret, uint64_t *offset) { + uint64_t np; + uint64_t osize; + Object *o; + int r; + + assert(f); + assert(items || n_items == 0); + assert(ts); + + osize = offsetof(Object, entry.items) + (n_items * sizeof(EntryItem)); + + r = journal_file_append_object(f, OBJECT_ENTRY, osize, &o, &np); + if (r < 0) + return r; + + o->entry.seqnum = htole64(journal_file_seqnum(f, seqnum)); + memcpy(o->entry.items, items, n_items * sizeof(EntryItem)); + o->entry.realtime = htole64(ts->realtime); + o->entry.monotonic = htole64(ts->monotonic); + o->entry.xor_hash = htole64(xor_hash); + o->entry.boot_id = f->header->boot_id; + + r = journal_file_link_entry(f, o, np); + if (r < 0) + return r; + + if (ret) + *ret = o; + + if (offset) + *offset = np; + + return 0; +} + +void journal_file_post_change(JournalFile *f) { + assert(f); + + /* inotify() does not receive IN_MODIFY events from file + * accesses done via mmap(). After each access we hence + * trigger IN_MODIFY by truncating the journal file to its + * current size which triggers IN_MODIFY. */ + + __sync_synchronize(); + + if (ftruncate(f->fd, f->last_stat.st_size) < 0) + log_error("Failed to to truncate file to its own size: %m"); +} + +int journal_file_append_entry(JournalFile *f, const dual_timestamp *ts, const struct iovec iovec[], unsigned n_iovec, uint64_t *seqnum, Object **ret, uint64_t *offset) { + unsigned i; + EntryItem *items; + int r; + uint64_t xor_hash = 0; + struct dual_timestamp _ts; + + assert(f); + assert(iovec || n_iovec == 0); + + if (!f->writable) + return -EPERM; + + if (!ts) { + dual_timestamp_get(&_ts); + ts = &_ts; + } + + if (f->tail_entry_monotonic_valid && + ts->monotonic < le64toh(f->header->tail_entry_monotonic)) + return -EINVAL; + + items = alloca(sizeof(EntryItem) * n_iovec); + + for (i = 0; i < n_iovec; i++) { + uint64_t p; + Object *o; + + r = journal_file_append_data(f, iovec[i].iov_base, iovec[i].iov_len, &o, &p); + if (r < 0) + return r; + + xor_hash ^= le64toh(o->data.hash); + items[i].object_offset = htole64(p); + items[i].hash = o->data.hash; + } + + r = journal_file_append_entry_internal(f, ts, xor_hash, items, n_iovec, seqnum, ret, offset); + + journal_file_post_change(f); + + return r; +} + +static int generic_array_get(JournalFile *f, + uint64_t first, + uint64_t i, + Object **ret, uint64_t *offset) { + + Object *o; + uint64_t p = 0, a; + int r; + + assert(f); + + a = first; + while (a > 0) { + uint64_t n; + + r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o); + if (r < 0) + return r; + + n = journal_file_entry_array_n_items(o); + if (i < n) { + p = le64toh(o->entry_array.items[i]); + break; + } + + i -= n; + a = le64toh(o->entry_array.next_entry_array_offset); + } + + if (a <= 0 || p <= 0) + return 0; + + r = journal_file_move_to_object(f, OBJECT_ENTRY, p, &o); + if (r < 0) + return r; + + if (ret) + *ret = o; + + if (offset) + *offset = p; + + return 1; +} + +static int generic_array_get_plus_one(JournalFile *f, + uint64_t extra, + uint64_t first, + uint64_t i, + Object **ret, uint64_t *offset) { + + Object *o; + + assert(f); + + if (i == 0) { + int r; + + r = journal_file_move_to_object(f, OBJECT_ENTRY, extra, &o); + if (r < 0) + return r; + + if (ret) + *ret = o; + + if (offset) + *offset = extra; + + return 1; + } + + return generic_array_get(f, first, i-1, ret, offset); +} + +enum { + TEST_FOUND, + TEST_LEFT, + TEST_RIGHT +}; + +static int generic_array_bisect(JournalFile *f, + uint64_t first, + uint64_t n, + uint64_t needle, + int (*test_object)(JournalFile *f, uint64_t p, uint64_t needle), + direction_t direction, + Object **ret, + uint64_t *offset, + uint64_t *idx) { + + uint64_t a, p, t = 0, i = 0, last_p = 0; + bool subtract_one = false; + Object *o, *array = NULL; + int r; + + assert(f); + assert(test_object); + + a = first; + while (a > 0) { + uint64_t left, right, k, lp; + + r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &array); + if (r < 0) + return r; + + k = journal_file_entry_array_n_items(array); + right = MIN(k, n); + if (right <= 0) + return 0; + + i = right - 1; + lp = p = le64toh(array->entry_array.items[i]); + if (p <= 0) + return -EBADMSG; + + r = test_object(f, p, needle); + if (r < 0) + return r; + + if (r == TEST_FOUND) + r = direction == DIRECTION_DOWN ? TEST_RIGHT : TEST_LEFT; + + if (r == TEST_RIGHT) { + left = 0; + right -= 1; + for (;;) { + if (left == right) { + if (direction == DIRECTION_UP) + subtract_one = true; + + i = left; + goto found; + } + + assert(left < right); + + i = (left + right) / 2; + p = le64toh(array->entry_array.items[i]); + if (p <= 0) + return -EBADMSG; + + r = test_object(f, p, needle); + if (r < 0) + return r; + + if (r == TEST_FOUND) + r = direction == DIRECTION_DOWN ? TEST_RIGHT : TEST_LEFT; + + if (r == TEST_RIGHT) + right = i; + else + left = i + 1; + } + } + + if (k > n) + return 0; + + last_p = lp; + + n -= k; + t += k; + a = le64toh(array->entry_array.next_entry_array_offset); + } + + return 0; + +found: + if (subtract_one && t == 0 && i == 0) + return 0; + + if (subtract_one && i == 0) + p = last_p; + else if (subtract_one) + p = le64toh(array->entry_array.items[i-1]); + else + p = le64toh(array->entry_array.items[i]); + + r = journal_file_move_to_object(f, OBJECT_ENTRY, p, &o); + if (r < 0) + return r; + + if (ret) + *ret = o; + + if (offset) + *offset = p; + + if (idx) + *idx = t + i - (subtract_one ? 1 : 0); + + return 1; +} + +static int generic_array_bisect_plus_one(JournalFile *f, + uint64_t extra, + uint64_t first, + uint64_t n, + uint64_t needle, + int (*test_object)(JournalFile *f, uint64_t p, uint64_t needle), + direction_t direction, + Object **ret, + uint64_t *offset, + uint64_t *idx) { + + int r; + + assert(f); + assert(test_object); + + if (n <= 0) + return 0; + + /* This bisects the array in object 'first', but first checks + * an extra */ + r = test_object(f, extra, needle); + if (r < 0) + return r; + else if (r == TEST_FOUND) { + Object *o; + + r = journal_file_move_to_object(f, OBJECT_ENTRY, extra, &o); + if (r < 0) + return r; + + if (ret) + *ret = o; + + if (offset) + *offset = extra; + + if (idx) + *idx = 0; + + return 1; + } else if (r == TEST_RIGHT) + return 0; + + r = generic_array_bisect(f, first, n-1, needle, test_object, direction, ret, offset, idx); + + if (r > 0) + (*idx) ++; + + return r; +} + +static int test_object_seqnum(JournalFile *f, uint64_t p, uint64_t needle) { + Object *o; + int r; + + assert(f); + assert(p > 0); + + r = journal_file_move_to_object(f, OBJECT_ENTRY, p, &o); + if (r < 0) + return r; + + if (le64toh(o->entry.seqnum) == needle) + return TEST_FOUND; + else if (le64toh(o->entry.seqnum) < needle) + return TEST_LEFT; + else + return TEST_RIGHT; +} + +int journal_file_move_to_entry_by_seqnum( + JournalFile *f, + uint64_t seqnum, + direction_t direction, + Object **ret, + uint64_t *offset) { + + return generic_array_bisect(f, + le64toh(f->header->entry_array_offset), + le64toh(f->header->n_entries), + seqnum, + test_object_seqnum, + direction, + ret, offset, NULL); +} + +static int test_object_realtime(JournalFile *f, uint64_t p, uint64_t needle) { + Object *o; + int r; + + assert(f); + assert(p > 0); + + r = journal_file_move_to_object(f, OBJECT_ENTRY, p, &o); + if (r < 0) + return r; + + if (le64toh(o->entry.realtime) == needle) + return TEST_FOUND; + else if (le64toh(o->entry.realtime) < needle) + return TEST_LEFT; + else + return TEST_RIGHT; +} + +int journal_file_move_to_entry_by_realtime( + JournalFile *f, + uint64_t realtime, + direction_t direction, + Object **ret, + uint64_t *offset) { + + return generic_array_bisect(f, + le64toh(f->header->entry_array_offset), + le64toh(f->header->n_entries), + realtime, + test_object_realtime, + direction, + ret, offset, NULL); +} + +static int test_object_monotonic(JournalFile *f, uint64_t p, uint64_t needle) { + Object *o; + int r; + + assert(f); + assert(p > 0); + + r = journal_file_move_to_object(f, OBJECT_ENTRY, p, &o); + if (r < 0) + return r; + + if (le64toh(o->entry.monotonic) == needle) + return TEST_FOUND; + else if (le64toh(o->entry.monotonic) < needle) + return TEST_LEFT; + else + return TEST_RIGHT; +} + +int journal_file_move_to_entry_by_monotonic( + JournalFile *f, + sd_id128_t boot_id, + uint64_t monotonic, + direction_t direction, + Object **ret, + uint64_t *offset) { + + char t[8+32+1] = "_BOOT_ID="; + Object *o; + int r; + + sd_id128_to_string(boot_id, t + 8); + + r = journal_file_find_data_object(f, t, strlen(t), &o, NULL); + if (r < 0) + return r; + else if (r == 0) + return -ENOENT; + + return generic_array_bisect_plus_one(f, + le64toh(o->data.entry_offset), + le64toh(o->data.entry_array_offset), + le64toh(o->data.n_entries), + monotonic, + test_object_monotonic, + direction, + ret, offset, NULL); +} + +static int test_object_offset(JournalFile *f, uint64_t p, uint64_t needle) { + assert(f); + assert(p > 0); + + if (p == needle) + return TEST_FOUND; + else if (p < needle) + return TEST_LEFT; + else + return TEST_RIGHT; +} + +int journal_file_next_entry( + JournalFile *f, + Object *o, uint64_t p, + direction_t direction, + Object **ret, uint64_t *offset) { + + uint64_t i, n; + int r; + + assert(f); + assert(p > 0 || !o); + + n = le64toh(f->header->n_entries); + if (n <= 0) + return 0; + + if (!o) + i = direction == DIRECTION_DOWN ? 0 : n - 1; + else { + if (o->object.type != OBJECT_ENTRY) + return -EINVAL; + + r = generic_array_bisect(f, + le64toh(f->header->entry_array_offset), + le64toh(f->header->n_entries), + p, + test_object_offset, + DIRECTION_DOWN, + NULL, NULL, + &i); + if (r <= 0) + return r; + + if (direction == DIRECTION_DOWN) { + if (i >= n - 1) + return 0; + + i++; + } else { + if (i <= 0) + return 0; + + i--; + } + } + + /* And jump to it */ + return generic_array_get(f, + le64toh(f->header->entry_array_offset), + i, + ret, offset); +} + +int journal_file_skip_entry( + JournalFile *f, + Object *o, uint64_t p, + int64_t skip, + Object **ret, uint64_t *offset) { + + uint64_t i, n; + int r; + + assert(f); + assert(o); + assert(p > 0); + + if (o->object.type != OBJECT_ENTRY) + return -EINVAL; + + r = generic_array_bisect(f, + le64toh(f->header->entry_array_offset), + le64toh(f->header->n_entries), + p, + test_object_offset, + DIRECTION_DOWN, + NULL, NULL, + &i); + if (r <= 0) + return r; + + /* Calculate new index */ + if (skip < 0) { + if ((uint64_t) -skip >= i) + i = 0; + else + i = i - (uint64_t) -skip; + } else + i += (uint64_t) skip; + + n = le64toh(f->header->n_entries); + if (n <= 0) + return -EBADMSG; + + if (i >= n) + i = n-1; + + return generic_array_get(f, + le64toh(f->header->entry_array_offset), + i, + ret, offset); +} + +int journal_file_next_entry_for_data( + JournalFile *f, + Object *o, uint64_t p, + uint64_t data_offset, + direction_t direction, + Object **ret, uint64_t *offset) { + + uint64_t n, i; + int r; + Object *d; + + assert(f); + assert(p > 0 || !o); + + r = journal_file_move_to_object(f, OBJECT_DATA, data_offset, &d); + if (r < 0) + return r; + + n = le64toh(d->data.n_entries); + if (n <= 0) + return n; + + if (!o) + i = direction == DIRECTION_DOWN ? 0 : n - 1; + else { + if (o->object.type != OBJECT_ENTRY) + return -EINVAL; + + r = generic_array_bisect_plus_one(f, + le64toh(d->data.entry_offset), + le64toh(d->data.entry_array_offset), + le64toh(d->data.n_entries), + p, + test_object_offset, + DIRECTION_DOWN, + NULL, NULL, + &i); + + if (r <= 0) + return r; + + if (direction == DIRECTION_DOWN) { + if (i >= n - 1) + return 0; + + i++; + } else { + if (i <= 0) + return 0; + + i--; + } + + } + + return generic_array_get_plus_one(f, + le64toh(d->data.entry_offset), + le64toh(d->data.entry_array_offset), + i, + ret, offset); +} + +int journal_file_move_to_entry_by_seqnum_for_data( + JournalFile *f, + uint64_t data_offset, + uint64_t seqnum, + direction_t direction, + Object **ret, uint64_t *offset) { + + Object *d; + int r; + + r = journal_file_move_to_object(f, OBJECT_DATA, data_offset, &d); + if (r <= 0) + return r; + + return generic_array_bisect_plus_one(f, + le64toh(d->data.entry_offset), + le64toh(d->data.entry_array_offset), + le64toh(d->data.n_entries), + seqnum, + test_object_seqnum, + direction, + ret, offset, NULL); +} + +int journal_file_move_to_entry_by_realtime_for_data( + JournalFile *f, + uint64_t data_offset, + uint64_t realtime, + direction_t direction, + Object **ret, uint64_t *offset) { + + Object *d; + int r; + + r = journal_file_move_to_object(f, OBJECT_DATA, data_offset, &d); + if (r <= 0) + return r; + + return generic_array_bisect_plus_one(f, + le64toh(d->data.entry_offset), + le64toh(d->data.entry_array_offset), + le64toh(d->data.n_entries), + realtime, + test_object_realtime, + direction, + ret, offset, NULL); +} + +void journal_file_dump(JournalFile *f) { + char a[33], b[33], c[33]; + Object *o; + int r; + uint64_t p; + + assert(f); + + printf("File Path: %s\n" + "File ID: %s\n" + "Machine ID: %s\n" + "Boot ID: %s\n" + "Arena size: %llu\n" + "Objects: %lu\n" + "Entries: %lu\n", + f->path, + sd_id128_to_string(f->header->file_id, a), + sd_id128_to_string(f->header->machine_id, b), + sd_id128_to_string(f->header->boot_id, c), + (unsigned long long) le64toh(f->header->arena_size), + (unsigned long) le64toh(f->header->n_objects), + (unsigned long) le64toh(f->header->n_entries)); + + p = le64toh(f->header->arena_offset); + while (p != 0) { + r = journal_file_move_to_object(f, -1, p, &o); + if (r < 0) + goto fail; + + switch (o->object.type) { + + case OBJECT_UNUSED: + printf("Type: OBJECT_UNUSED\n"); + break; + + case OBJECT_DATA: + printf("Type: OBJECT_DATA\n"); + break; + + case OBJECT_ENTRY: + printf("Type: OBJECT_ENTRY %llu %llu %llu\n", + (unsigned long long) le64toh(o->entry.seqnum), + (unsigned long long) le64toh(o->entry.monotonic), + (unsigned long long) le64toh(o->entry.realtime)); + break; + + case OBJECT_FIELD_HASH_TABLE: + printf("Type: OBJECT_FIELD_HASH_TABLE\n"); + break; + + case OBJECT_DATA_HASH_TABLE: + printf("Type: OBJECT_DATA_HASH_TABLE\n"); + break; + + case OBJECT_ENTRY_ARRAY: + printf("Type: OBJECT_ENTRY_ARRAY\n"); + break; + } + + if (o->object.flags & OBJECT_COMPRESSED) + printf("Flags: COMPRESSED\n"); + + if (p == le64toh(f->header->tail_object_offset)) + p = 0; + else + p = p + ALIGN64(le64toh(o->object.size)); + } + + return; +fail: + log_error("File corrupt"); +} + +int journal_file_open( + const char *fname, + int flags, + mode_t mode, + JournalFile *template, + JournalFile **ret) { + + JournalFile *f; + int r; + bool newly_created = false; + + assert(fname); + + if ((flags & O_ACCMODE) != O_RDONLY && + (flags & O_ACCMODE) != O_RDWR) + return -EINVAL; + + f = new0(JournalFile, 1); + if (!f) + return -ENOMEM; + + f->fd = -1; + f->flags = flags; + f->mode = mode; + f->writable = (flags & O_ACCMODE) != O_RDONLY; + f->prot = prot_from_flags(flags); + + f->path = strdup(fname); + if (!f->path) { + r = -ENOMEM; + goto fail; + } + + f->fd = open(f->path, f->flags|O_CLOEXEC, f->mode); + if (f->fd < 0) { + r = -errno; + goto fail; + } + + if (fstat(f->fd, &f->last_stat) < 0) { + r = -errno; + goto fail; + } + + if (f->last_stat.st_size == 0 && f->writable) { + newly_created = true; + + r = journal_file_init_header(f, template); + if (r < 0) + goto fail; + + if (fstat(f->fd, &f->last_stat) < 0) { + r = -errno; + goto fail; + } + } + + if (f->last_stat.st_size < (off_t) sizeof(Header)) { + r = -EIO; + goto fail; + } + + f->header = mmap(NULL, PAGE_ALIGN(sizeof(Header)), prot_from_flags(flags), MAP_SHARED, f->fd, 0); + if (f->header == MAP_FAILED) { + f->header = NULL; + r = -errno; + goto fail; + } + + if (!newly_created) { + r = journal_file_verify_header(f); + if (r < 0) + goto fail; + } + + if (f->writable) { + r = journal_file_refresh_header(f); + if (r < 0) + goto fail; + } + + if (newly_created) { + + r = journal_file_setup_field_hash_table(f); + if (r < 0) + goto fail; + + r = journal_file_setup_data_hash_table(f); + if (r < 0) + goto fail; + } + + r = journal_file_map_field_hash_table(f); + if (r < 0) + goto fail; + + r = journal_file_map_data_hash_table(f); + if (r < 0) + goto fail; + + if (ret) + *ret = f; + + return 0; + +fail: + journal_file_close(f); + + return r; +} + +int journal_file_rotate(JournalFile **f) { + char *p; + size_t l; + JournalFile *old_file, *new_file = NULL; + int r; + + assert(f); + assert(*f); + + old_file = *f; + + if (!old_file->writable) + return -EINVAL; + + if (!endswith(old_file->path, ".journal")) + return -EINVAL; + + l = strlen(old_file->path); + + p = new(char, l + 1 + 16 + 1 + 32 + 1 + 16 + 1); + if (!p) + return -ENOMEM; + + memcpy(p, old_file->path, l - 8); + p[l-8] = '@'; + sd_id128_to_string(old_file->header->seqnum_id, p + l - 8 + 1); + snprintf(p + l - 8 + 1 + 32, 1 + 16 + 1 + 16 + 8 + 1, + "-%016llx-%016llx.journal", + (unsigned long long) le64toh((*f)->header->seqnum), + (unsigned long long) le64toh((*f)->header->tail_entry_realtime)); + + r = rename(old_file->path, p); + free(p); + + if (r < 0) + return -errno; + + old_file->header->state = le32toh(STATE_ARCHIVED); + + r = journal_file_open(old_file->path, old_file->flags, old_file->mode, old_file, &new_file); + journal_file_close(old_file); + + *f = new_file; + return r; +} + +struct vacuum_info { + off_t usage; + char *filename; + + uint64_t realtime; + sd_id128_t seqnum_id; + uint64_t seqnum; +}; + +static int vacuum_compare(const void *_a, const void *_b) { + const struct vacuum_info *a, *b; + + a = _a; + b = _b; + + if (sd_id128_equal(a->seqnum_id, b->seqnum_id)) { + if (a->seqnum < b->seqnum) + return -1; + else if (a->seqnum > b->seqnum) + return 1; + else + return 0; + } + + if (a->realtime < b->realtime) + return -1; + else if (a->realtime > b->realtime) + return 1; + else + return memcmp(&a->seqnum_id, &b->seqnum_id, 16); +} + +int journal_directory_vacuum(const char *directory, uint64_t max_use, uint64_t min_free) { + DIR *d; + int r = 0; + struct vacuum_info *list = NULL; + unsigned n_list = 0, n_allocated = 0, i; + uint64_t sum = 0; + + assert(directory); + + if (max_use <= 0) + return 0; + + d = opendir(directory); + if (!d) + return -errno; + + for (;;) { + int k; + struct dirent buf, *de; + size_t q; + struct stat st; + char *p; + unsigned long long seqnum, realtime; + sd_id128_t seqnum_id; + + k = readdir_r(d, &buf, &de); + if (k != 0) { + r = -k; + goto finish; + } + + if (!de) + break; + + if (!dirent_is_file_with_suffix(de, ".journal")) + continue; + + q = strlen(de->d_name); + + if (q < 1 + 32 + 1 + 16 + 1 + 16 + 8) + continue; + + if (de->d_name[q-8-16-1] != '-' || + de->d_name[q-8-16-1-16-1] != '-' || + de->d_name[q-8-16-1-16-1-32-1] != '@') + continue; + + if (fstatat(dirfd(d), de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) + continue; + + if (!S_ISREG(st.st_mode)) + continue; + + p = strdup(de->d_name); + if (!p) { + r = -ENOMEM; + goto finish; + } + + de->d_name[q-8-16-1-16-1] = 0; + if (sd_id128_from_string(de->d_name + q-8-16-1-16-1-32, &seqnum_id) < 0) { + free(p); + continue; + } + + if (sscanf(de->d_name + q-8-16-1-16, "%16llx-%16llx.journal", &seqnum, &realtime) != 2) { + free(p); + continue; + } + + if (n_list >= n_allocated) { + struct vacuum_info *j; + + n_allocated = MAX(n_allocated * 2U, 8U); + j = realloc(list, n_allocated * sizeof(struct vacuum_info)); + if (!j) { + free(p); + r = -ENOMEM; + goto finish; + } + + list = j; + } + + list[n_list].filename = p; + list[n_list].usage = (uint64_t) st.st_blksize * (uint64_t) st.st_blocks; + list[n_list].seqnum = seqnum; + list[n_list].realtime = realtime; + list[n_list].seqnum_id = seqnum_id; + + sum += list[n_list].usage; + + n_list ++; + } + + qsort(list, n_list, sizeof(struct vacuum_info), vacuum_compare); + + for(i = 0; i < n_list; i++) { + struct statvfs ss; + + if (fstatvfs(dirfd(d), &ss) < 0) { + r = -errno; + goto finish; + } + + if (sum <= max_use && + (uint64_t) ss.f_bavail * (uint64_t) ss.f_bsize >= min_free) + break; + + if (unlinkat(dirfd(d), list[i].filename, 0) >= 0) { + log_debug("Deleted archived journal %s/%s.", directory, list[i].filename); + sum -= list[i].usage; + } else if (errno != ENOENT) + log_warning("Failed to delete %s/%s: %m", directory, list[i].filename); + } + +finish: + for (i = 0; i < n_list; i++) + free(list[i].filename); + + free(list); + + if (d) + closedir(d); + + return r; +} + +int journal_file_copy_entry(JournalFile *from, JournalFile *to, Object *o, uint64_t p, uint64_t *seqnum, Object **ret, uint64_t *offset) { + uint64_t i, n; + uint64_t q, xor_hash = 0; + int r; + EntryItem *items; + dual_timestamp ts; + + assert(from); + assert(to); + assert(o); + assert(p); + + if (!to->writable) + return -EPERM; + + ts.monotonic = le64toh(o->entry.monotonic); + ts.realtime = le64toh(o->entry.realtime); + + if (to->tail_entry_monotonic_valid && + ts.monotonic < le64toh(to->header->tail_entry_monotonic)) + return -EINVAL; + + if (ts.realtime < le64toh(to->header->tail_entry_realtime)) + return -EINVAL; + + n = journal_file_entry_n_items(o); + items = alloca(sizeof(EntryItem) * n); + + for (i = 0; i < n; i++) { + uint64_t le_hash, l, h; + size_t t; + void *data; + Object *u; + + q = le64toh(o->entry.items[i].object_offset); + le_hash = o->entry.items[i].hash; + + r = journal_file_move_to_object(from, OBJECT_DATA, q, &o); + if (r < 0) + return r; + + if (le_hash != o->data.hash) + return -EBADMSG; + + l = le64toh(o->object.size) - offsetof(Object, data.payload); + t = (size_t) l; + + /* We hit the limit on 32bit machines */ + if ((uint64_t) t != l) + return -E2BIG; + + if (o->object.flags & OBJECT_COMPRESSED) { +#ifdef HAVE_XZ + uint64_t rsize; + + if (!uncompress_blob(o->data.payload, l, &from->compress_buffer, &from->compress_buffer_size, &rsize)) + return -EBADMSG; + + data = from->compress_buffer; + l = rsize; +#else + return -EPROTONOSUPPORT; +#endif + } else + data = o->data.payload; + + r = journal_file_append_data(to, data, l, &u, &h); + if (r < 0) + return r; + + xor_hash ^= le64toh(u->data.hash); + items[i].object_offset = htole64(h); + items[i].hash = u->data.hash; + + r = journal_file_move_to_object(from, OBJECT_ENTRY, p, &o); + if (r < 0) + return r; + } + + return journal_file_append_entry_internal(to, &ts, xor_hash, items, n, seqnum, ret, offset); +} + +void journal_default_metrics(JournalMetrics *m, int fd) { + uint64_t fs_size = 0; + struct statvfs ss; + char a[FORMAT_BYTES_MAX], b[FORMAT_BYTES_MAX], c[FORMAT_BYTES_MAX], d[FORMAT_BYTES_MAX]; + + assert(m); + assert(fd >= 0); + + if (fstatvfs(fd, &ss) >= 0) + fs_size = ss.f_frsize * ss.f_blocks; + + if (m->max_use == (uint64_t) -1) { + + if (fs_size > 0) { + m->max_use = PAGE_ALIGN(fs_size / 10); /* 10% of file system size */ + + if (m->max_use > DEFAULT_MAX_USE_UPPER) + m->max_use = DEFAULT_MAX_USE_UPPER; + + if (m->max_use < DEFAULT_MAX_USE_LOWER) + m->max_use = DEFAULT_MAX_USE_LOWER; + } else + m->max_use = DEFAULT_MAX_USE_LOWER; + } else { + m->max_use = PAGE_ALIGN(m->max_use); + + if (m->max_use < JOURNAL_FILE_SIZE_MIN*2) + m->max_use = JOURNAL_FILE_SIZE_MIN*2; + } + + if (m->max_size == (uint64_t) -1) { + m->max_size = PAGE_ALIGN(m->max_use / 8); /* 8 chunks */ + + if (m->max_size > DEFAULT_MAX_SIZE_UPPER) + m->max_size = DEFAULT_MAX_SIZE_UPPER; + } else + m->max_size = PAGE_ALIGN(m->max_size); + + if (m->max_size < JOURNAL_FILE_SIZE_MIN) + m->max_size = JOURNAL_FILE_SIZE_MIN; + + if (m->max_size*2 > m->max_use) + m->max_use = m->max_size*2; + + if (m->min_size == (uint64_t) -1) + m->min_size = JOURNAL_FILE_SIZE_MIN; + else { + m->min_size = PAGE_ALIGN(m->min_size); + + if (m->min_size < JOURNAL_FILE_SIZE_MIN) + m->min_size = JOURNAL_FILE_SIZE_MIN; + + if (m->min_size > m->max_size) + m->max_size = m->min_size; + } + + if (m->keep_free == (uint64_t) -1) { + + if (fs_size > 0) { + m->keep_free = PAGE_ALIGN(fs_size / 20); /* 5% of file system size */ + + if (m->keep_free > DEFAULT_KEEP_FREE_UPPER) + m->keep_free = DEFAULT_KEEP_FREE_UPPER; + + } else + m->keep_free = DEFAULT_KEEP_FREE; + } + + log_debug("Fixed max_use=%s max_size=%s min_size=%s keep_free=%s", + format_bytes(a, sizeof(a), m->max_use), + format_bytes(b, sizeof(b), m->max_size), + format_bytes(c, sizeof(c), m->min_size), + format_bytes(d, sizeof(d), m->keep_free)); +} diff --git a/src/journal/journal-file.h b/src/journal/journal-file.h new file mode 100644 index 0000000000..4ef4a14479 --- /dev/null +++ b/src/journal/journal-file.h @@ -0,0 +1,125 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#ifndef foojournalfilehfoo +#define foojournalfilehfoo + +/*** + This file is part of systemd. + + Copyright 2011 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 <inttypes.h> + +#include <systemd/sd-id128.h> + +#include "journal-def.h" +#include "util.h" + +typedef struct Window { + void *ptr; + uint64_t offset; + uint64_t size; +} Window; + +enum { + WINDOW_UNKNOWN = OBJECT_UNUSED, + WINDOW_DATA = OBJECT_DATA, + WINDOW_ENTRY = OBJECT_ENTRY, + WINDOW_DATA_HASH_TABLE = OBJECT_DATA_HASH_TABLE, + WINDOW_FIELD_HASH_TABLE = OBJECT_FIELD_HASH_TABLE, + WINDOW_ENTRY_ARRAY = OBJECT_ENTRY_ARRAY, + WINDOW_HEADER, + _WINDOW_MAX +}; + +typedef struct JournalMetrics { + uint64_t max_use; + uint64_t max_size; + uint64_t min_size; + uint64_t keep_free; +} JournalMetrics; + +typedef struct JournalFile { + int fd; + char *path; + struct stat last_stat; + mode_t mode; + int flags; + int prot; + bool writable; + bool tail_entry_monotonic_valid; + + Header *header; + HashItem *data_hash_table; + HashItem *field_hash_table; + + Window windows[_WINDOW_MAX]; + + uint64_t current_offset; + + JournalMetrics metrics; + + bool compress; + +#ifdef HAVE_XZ + void *compress_buffer; + uint64_t compress_buffer_size; +#endif +} JournalFile; + +typedef enum direction { + DIRECTION_UP, + DIRECTION_DOWN +} direction_t; + +int journal_file_open(const char *fname, int flags, mode_t mode, JournalFile *template, JournalFile **ret); +void journal_file_close(JournalFile *j); + +int journal_file_move_to_object(JournalFile *f, int type, uint64_t offset, Object **ret); + +uint64_t journal_file_entry_n_items(Object *o); + +int journal_file_append_entry(JournalFile *f, const dual_timestamp *ts, const struct iovec iovec[], unsigned n_iovec, uint64_t *seqno, Object **ret, uint64_t *offset); + +int journal_file_find_data_object(JournalFile *f, const void *data, uint64_t size, Object **ret, uint64_t *offset); +int journal_file_find_data_object_with_hash(JournalFile *f, const void *data, uint64_t size, uint64_t hash, Object **ret, uint64_t *offset); + +int journal_file_next_entry(JournalFile *f, Object *o, uint64_t p, direction_t direction, Object **ret, uint64_t *offset); +int journal_file_skip_entry(JournalFile *f, Object *o, uint64_t p, int64_t skip, Object **ret, uint64_t *offset); + +int journal_file_next_entry_for_data(JournalFile *f, Object *o, uint64_t p, uint64_t data_offset, direction_t direction, Object **ret, uint64_t *offset); + +int journal_file_move_to_entry_by_seqnum(JournalFile *f, uint64_t seqnum, direction_t direction, Object **ret, uint64_t *offset); +int journal_file_move_to_entry_by_realtime(JournalFile *f, uint64_t realtime, direction_t direction, Object **ret, uint64_t *offset); +int journal_file_move_to_entry_by_monotonic(JournalFile *f, sd_id128_t boot_id, uint64_t monotonic, direction_t direction, Object **ret, uint64_t *offset); + +int journal_file_move_to_entry_by_seqnum_for_data(JournalFile *f, uint64_t data_offset, uint64_t seqnum, direction_t direction, Object **ret, uint64_t *offset); +int journal_file_move_to_entry_by_realtime_for_data(JournalFile *f, uint64_t data_offset, uint64_t realtime, direction_t direction, Object **ret, uint64_t *offset); + +int journal_file_copy_entry(JournalFile *from, JournalFile *to, Object *o, uint64_t p, uint64_t *seqnum, Object **ret, uint64_t *offset); + +void journal_file_dump(JournalFile *f); + +int journal_file_rotate(JournalFile **f); + +int journal_directory_vacuum(const char *directory, uint64_t max_use, uint64_t min_free); + +void journal_file_post_change(JournalFile *f); + +void journal_default_metrics(JournalMetrics *m, int fd); + +#endif diff --git a/src/journal/journal-internal.h b/src/journal/journal-internal.h new file mode 100644 index 0000000000..e5914bfb63 --- /dev/null +++ b/src/journal/journal-internal.h @@ -0,0 +1,84 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#ifndef foojournalinternalhfoo +#define foojournalinternalhfoo + +/*** + This file is part of systemd. + + Copyright 2011 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 <sys/types.h> +#include <inttypes.h> +#include <stdbool.h> + +#include <systemd/sd-id128.h> + +#include "list.h" + +typedef struct Match Match; + +struct Match { + char *data; + size_t size; + uint64_t le_hash; + + LIST_FIELDS(Match, matches); +}; + +typedef enum location_type { + LOCATION_HEAD, + LOCATION_TAIL, + LOCATION_DISCRETE +} location_type_t; + +typedef struct Location { + location_type_t type; + + uint64_t seqnum; + sd_id128_t seqnum_id; + bool seqnum_set; + + uint64_t realtime; + bool realtime_set; + + uint64_t monotonic; + sd_id128_t boot_id; + bool monotonic_set; + + uint64_t xor_hash; + bool xor_hash_set; +} Location; + +struct sd_journal { + int flags; + + Hashmap *files; + + Location current_location; + JournalFile *current_file; + uint64_t current_field; + + int inotify_fd; + Hashmap *inotify_wd_dirs; + Hashmap *inotify_wd_roots; + + LIST_HEAD(Match, matches); + unsigned n_matches; +}; + +#endif diff --git a/src/journal/journal-rate-limit.c b/src/journal/journal-rate-limit.c new file mode 100644 index 0000000000..243ff2a378 --- /dev/null +++ b/src/journal/journal-rate-limit.c @@ -0,0 +1,275 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2011 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 <string.h> +#include <errno.h> + +#include "journal-rate-limit.h" +#include "list.h" +#include "util.h" +#include "hashmap.h" + +#define POOLS_MAX 5 +#define BUCKETS_MAX 127 +#define GROUPS_MAX 2047 + +static const int priority_map[] = { + [LOG_EMERG] = 0, + [LOG_ALERT] = 0, + [LOG_CRIT] = 0, + [LOG_ERR] = 1, + [LOG_WARNING] = 2, + [LOG_NOTICE] = 3, + [LOG_INFO] = 3, + [LOG_DEBUG] = 4 +}; + +typedef struct JournalRateLimitPool JournalRateLimitPool; +typedef struct JournalRateLimitGroup JournalRateLimitGroup; + +struct JournalRateLimitPool { + usec_t begin; + unsigned num; + unsigned suppressed; +}; + +struct JournalRateLimitGroup { + JournalRateLimit *parent; + + char *id; + JournalRateLimitPool pools[POOLS_MAX]; + unsigned hash; + + LIST_FIELDS(JournalRateLimitGroup, bucket); + LIST_FIELDS(JournalRateLimitGroup, lru); +}; + +struct JournalRateLimit { + usec_t interval; + unsigned burst; + + JournalRateLimitGroup* buckets[BUCKETS_MAX]; + JournalRateLimitGroup *lru, *lru_tail; + + unsigned n_groups; +}; + +JournalRateLimit *journal_rate_limit_new(usec_t interval, unsigned burst) { + JournalRateLimit *r; + + assert(interval > 0 || burst == 0); + + r = new0(JournalRateLimit, 1); + if (!r) + return NULL; + + r->interval = interval; + r->burst = burst; + + return r; +} + +static void journal_rate_limit_group_free(JournalRateLimitGroup *g) { + assert(g); + + if (g->parent) { + assert(g->parent->n_groups > 0); + + if (g->parent->lru_tail == g) + g->parent->lru_tail = g->lru_prev; + + LIST_REMOVE(JournalRateLimitGroup, lru, g->parent->lru, g); + LIST_REMOVE(JournalRateLimitGroup, bucket, g->parent->buckets[g->hash % BUCKETS_MAX], g); + + g->parent->n_groups --; + } + + free(g->id); + free(g); +} + +void journal_rate_limit_free(JournalRateLimit *r) { + assert(r); + + while (r->lru) + journal_rate_limit_group_free(r->lru); + + free(r); +} + +static bool journal_rate_limit_group_expired(JournalRateLimitGroup *g, usec_t ts) { + unsigned i; + + assert(g); + + for (i = 0; i < POOLS_MAX; i++) + if (g->pools[i].begin + g->parent->interval >= ts) + return false; + + return true; +} + +static void journal_rate_limit_vacuum(JournalRateLimit *r, usec_t ts) { + assert(r); + + /* Makes room for at least one new item, but drop all + * expored items too. */ + + while (r->n_groups >= GROUPS_MAX || + (r->lru_tail && journal_rate_limit_group_expired(r->lru_tail, ts))) + journal_rate_limit_group_free(r->lru_tail); +} + +static JournalRateLimitGroup* journal_rate_limit_group_new(JournalRateLimit *r, const char *id, usec_t ts) { + JournalRateLimitGroup *g; + + assert(r); + assert(id); + + g = new0(JournalRateLimitGroup, 1); + if (!g) + return NULL; + + g->id = strdup(id); + if (!g->id) + goto fail; + + g->hash = string_hash_func(g->id); + + journal_rate_limit_vacuum(r, ts); + + LIST_PREPEND(JournalRateLimitGroup, bucket, r->buckets[g->hash % BUCKETS_MAX], g); + LIST_PREPEND(JournalRateLimitGroup, lru, r->lru, g); + if (!g->lru_next) + r->lru_tail = g; + r->n_groups ++; + + g->parent = r; + return g; + +fail: + journal_rate_limit_group_free(g); + return NULL; +} + +static uint64_t u64log2(uint64_t n) { + unsigned r; + + if (n <= 1) + return 0; + + r = 0; + for (;;) { + n = n >> 1; + if (!n) + return r; + r++; + } +} + +static unsigned burst_modulate(unsigned burst, uint64_t available) { + unsigned k; + + /* Modulates the burst rate a bit with the amount of available + * disk space */ + + k = u64log2(available); + + /* 1MB */ + if (k <= 20) + return burst; + + burst = (burst * (k-20)) / 4; + + /* + * Example: + * + * <= 1MB = rate * 1 + * 16MB = rate * 2 + * 256MB = rate * 3 + * 4GB = rate * 4 + * 64GB = rate * 5 + * 1TB = rate * 6 + */ + + return burst; +} + +int journal_rate_limit_test(JournalRateLimit *r, const char *id, int priority, uint64_t available) { + unsigned h; + JournalRateLimitGroup *g; + JournalRateLimitPool *p; + unsigned burst; + usec_t ts; + + assert(id); + + if (!r) + return 1; + + if (r->interval == 0 || r->burst == 0) + return 1; + + burst = burst_modulate(r->burst, available); + + ts = now(CLOCK_MONOTONIC); + + h = string_hash_func(id); + g = r->buckets[h % BUCKETS_MAX]; + + LIST_FOREACH(bucket, g, g) + if (streq(g->id, id)) + break; + + if (!g) { + g = journal_rate_limit_group_new(r, id, ts); + if (!g) + return -ENOMEM; + } + + p = &g->pools[priority_map[priority]]; + + if (p->begin <= 0) { + p->suppressed = 0; + p->num = 1; + p->begin = ts; + return 1; + } + + if (p->begin + r->interval < ts) { + unsigned s; + + s = p->suppressed; + p->suppressed = 0; + p->num = 1; + p->begin = ts; + + return 1 + s; + } + + if (p->num <= burst) { + p->num++; + return 1; + } + + p->suppressed++; + return 0; +} diff --git a/src/journal/journal-rate-limit.h b/src/journal/journal-rate-limit.h new file mode 100644 index 0000000000..2bbdd5f9fe --- /dev/null +++ b/src/journal/journal-rate-limit.h @@ -0,0 +1,34 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#ifndef foojournalratelimithfoo +#define foojournalratelimithfoo + +/*** + This file is part of systemd. + + Copyright 2011 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 "macro.h" +#include "util.h" + +typedef struct JournalRateLimit JournalRateLimit; + +JournalRateLimit *journal_rate_limit_new(usec_t interval, unsigned burst); +void journal_rate_limit_free(JournalRateLimit *r); +int journal_rate_limit_test(JournalRateLimit *r, const char *id, int priority, uint64_t available); + +#endif diff --git a/src/journal/journal-send.c b/src/journal/journal-send.c new file mode 100644 index 0000000000..b5b4fbfa0d --- /dev/null +++ b/src/journal/journal-send.c @@ -0,0 +1,334 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2011 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 <sys/socket.h> +#include <sys/un.h> +#include <errno.h> +#include <stddef.h> +#include <unistd.h> +#include <fcntl.h> + +#include "sd-journal.h" +#include "util.h" +#include "socket-util.h" + +#define SNDBUF_SIZE (8*1024*1024) + +/* We open a single fd, and we'll share it with the current process, + * all its threads, and all its subprocesses. This means we need to + * initialize it atomically, and need to operate on it atomically + * never assuming we are the only user */ + +static int journal_fd(void) { + int fd; + static int fd_plus_one = 0; + +retry: + if (fd_plus_one > 0) + return fd_plus_one - 1; + + fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0); + if (fd < 0) + return -errno; + + fd_inc_sndbuf(fd, SNDBUF_SIZE); + + if (!__sync_bool_compare_and_swap(&fd_plus_one, 0, fd+1)) { + close_nointr_nofail(fd); + goto retry; + } + + return fd; +} + +_public_ int sd_journal_print(int priority, const char *format, ...) { + int r; + va_list ap; + + va_start(ap, format); + r = sd_journal_printv(priority, format, ap); + va_end(ap); + + return r; +} + +_public_ int sd_journal_printv(int priority, const char *format, va_list ap) { + char buffer[8 + LINE_MAX], p[11]; + struct iovec iov[2]; + + if (priority < 0 || priority > 7) + return -EINVAL; + + if (!format) + return -EINVAL; + + snprintf(p, sizeof(p), "PRIORITY=%i", priority & LOG_PRIMASK); + char_array_0(p); + + memcpy(buffer, "MESSAGE=", 8); + vsnprintf(buffer+8, sizeof(buffer) - 8, format, ap); + char_array_0(buffer); + + zero(iov); + IOVEC_SET_STRING(iov[0], buffer); + IOVEC_SET_STRING(iov[1], p); + + return sd_journal_sendv(iov, 2); +} + +_public_ int sd_journal_send(const char *format, ...) { + int r, n = 0, i = 0, j; + va_list ap; + struct iovec *iov = NULL; + + va_start(ap, format); + while (format) { + struct iovec *c; + char *buffer; + + if (i >= n) { + n = MAX(i*2, 4); + c = realloc(iov, n * sizeof(struct iovec)); + if (!c) { + r = -ENOMEM; + goto fail; + } + + iov = c; + } + + if (vasprintf(&buffer, format, ap) < 0) { + r = -ENOMEM; + goto fail; + } + + IOVEC_SET_STRING(iov[i++], buffer); + + format = va_arg(ap, char *); + } + va_end(ap); + + r = sd_journal_sendv(iov, i); + +fail: + for (j = 0; j < i; j++) + free(iov[j].iov_base); + + free(iov); + + return r; +} + +_public_ int sd_journal_sendv(const struct iovec *iov, int n) { + int fd, buffer_fd; + struct iovec *w; + uint64_t *l; + int i, j = 0; + struct msghdr mh; + struct sockaddr_un sa; + ssize_t k; + union { + struct cmsghdr cmsghdr; + uint8_t buf[CMSG_SPACE(sizeof(int))]; + } control; + struct cmsghdr *cmsg; + /* We use /dev/shm instead of /tmp here, since we want this to + * be a tmpfs, and one that is available from early boot on + * and where unprivileged users can create files. */ + char path[] = "/dev/shm/journal.XXXXXX"; + + if (!iov || n <= 0) + return -EINVAL; + + w = alloca(sizeof(struct iovec) * n * 5); + l = alloca(sizeof(uint64_t) * n); + + for (i = 0; i < n; i++) { + char *c, *nl; + + if (!iov[i].iov_base || + iov[i].iov_len <= 1) + return -EINVAL; + + c = memchr(iov[i].iov_base, '=', iov[i].iov_len); + if (!c || c == iov[i].iov_base) + return -EINVAL; + + nl = memchr(iov[i].iov_base, '\n', iov[i].iov_len); + if (nl) { + if (nl < c) + return -EINVAL; + + /* Already includes a newline? Bummer, then + * let's write the variable name, then a + * newline, then the size (64bit LE), followed + * by the data and a final newline */ + + w[j].iov_base = iov[i].iov_base; + w[j].iov_len = c - (char*) iov[i].iov_base; + j++; + + IOVEC_SET_STRING(w[j++], "\n"); + + l[i] = htole64(iov[i].iov_len - (c - (char*) iov[i].iov_base) - 1); + w[j].iov_base = &l[i]; + w[j].iov_len = sizeof(uint64_t); + j++; + + w[j].iov_base = c + 1; + w[j].iov_len = iov[i].iov_len - (c - (char*) iov[i].iov_base) - 1; + j++; + + } else + /* Nothing special? Then just add the line and + * append a newline */ + w[j++] = iov[i]; + + IOVEC_SET_STRING(w[j++], "\n"); + } + + fd = journal_fd(); + if (fd < 0) + return fd; + + zero(sa); + sa.sun_family = AF_UNIX; + strncpy(sa.sun_path, "/run/systemd/journal/socket", sizeof(sa.sun_path)); + + zero(mh); + mh.msg_name = &sa; + mh.msg_namelen = offsetof(struct sockaddr_un, sun_path) + strlen(sa.sun_path); + mh.msg_iov = w; + mh.msg_iovlen = j; + + k = sendmsg(fd, &mh, MSG_NOSIGNAL); + if (k >= 0) + return 0; + + if (errno != EMSGSIZE && errno != ENOBUFS) + return -errno; + + /* Message doesn't fit... Let's dump the data in a temporary + * file and just pass a file descriptor of it to the other + * side */ + + buffer_fd = mkostemp(path, O_CLOEXEC|O_RDWR); + if (buffer_fd < 0) + return -errno; + + if (unlink(path) < 0) { + close_nointr_nofail(buffer_fd); + return -errno; + } + + n = writev(buffer_fd, w, j); + if (n < 0) { + close_nointr_nofail(buffer_fd); + return -errno; + } + + mh.msg_iov = NULL; + mh.msg_iovlen = 0; + + zero(control); + mh.msg_control = &control; + mh.msg_controllen = sizeof(control); + + cmsg = CMSG_FIRSTHDR(&mh); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + cmsg->cmsg_len = CMSG_LEN(sizeof(int)); + memcpy(CMSG_DATA(cmsg), &buffer_fd, sizeof(int)); + + mh.msg_controllen = cmsg->cmsg_len; + + k = sendmsg(fd, &mh, MSG_NOSIGNAL); + close_nointr_nofail(buffer_fd); + + if (k < 0) + return -errno; + + return 0; +} + +_public_ int sd_journal_stream_fd(const char *identifier, int priority, int level_prefix) { + union sockaddr_union sa; + int fd; + char *header; + size_t l; + ssize_t r; + + if (priority < 0 || priority > 7) + return -EINVAL; + + fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0); + if (fd < 0) + return -errno; + + zero(sa); + sa.un.sun_family = AF_UNIX; + strncpy(sa.un.sun_path, "/run/systemd/journal/stdout", sizeof(sa.un.sun_path)); + + r = connect(fd, &sa.sa, offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path)); + if (r < 0) { + close_nointr_nofail(fd); + return -errno; + } + + if (shutdown(fd, SHUT_RD) < 0) { + close_nointr_nofail(fd); + return -errno; + } + + fd_inc_sndbuf(fd, SNDBUF_SIZE); + + if (!identifier) + identifier = ""; + + l = strlen(identifier); + header = alloca(l + 1 + 2 + 2 + 2 + 2 + 2); + + memcpy(header, identifier, l); + header[l++] = '\n'; + header[l++] = '0' + priority; + header[l++] = '\n'; + header[l++] = '0' + !!level_prefix; + header[l++] = '\n'; + header[l++] = '0'; + header[l++] = '\n'; + header[l++] = '0'; + header[l++] = '\n'; + header[l++] = '0'; + header[l++] = '\n'; + + r = loop_write(fd, header, l, false); + if (r < 0) { + close_nointr_nofail(fd); + return (int) r; + } + + if ((size_t) r != l) { + close_nointr_nofail(fd); + return -errno; + } + + return fd; +} diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c new file mode 100644 index 0000000000..8db3fc9201 --- /dev/null +++ b/src/journal/journalctl.c @@ -0,0 +1,308 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2011 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 <fcntl.h> +#include <errno.h> +#include <stddef.h> +#include <string.h> +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <sys/poll.h> +#include <time.h> +#include <getopt.h> + +#include <systemd/sd-journal.h> + +#include "log.h" +#include "util.h" +#include "build.h" +#include "pager.h" +#include "logs-show.h" + +static OutputMode arg_output = OUTPUT_SHORT; +static bool arg_follow = false; +static bool arg_show_all = false; +static bool arg_no_pager = false; +static int arg_lines = -1; +static bool arg_no_tail = false; +static bool arg_new_id128 = false; + +static int help(void) { + + printf("%s [OPTIONS...] {COMMAND} ...\n\n" + "Send control commands to or query the journal.\n\n" + " -h --help Show this help\n" + " --version Show package version\n" + " --no-pager Do not pipe output into a pager\n" + " -a --all Show all properties, including long and unprintable\n" + " -f --follow Follow journal\n" + " -n --lines=INTEGER Journal entries to show\n" + " --no-tail Show all lines, even in follow mode\n" + " -o --output=STRING Change journal output mode (short, short-monotonic,\n" + " verbose, export, json, cat)\n" + " --new-id128 Generate a new 128 Bit id\n", + program_invocation_short_name); + + return 0; +} + +static int parse_argv(int argc, char *argv[]) { + + enum { + ARG_VERSION = 0x100, + ARG_NO_PAGER, + ARG_NO_TAIL, + ARG_NEW_ID128 + }; + + static const struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "version" , no_argument, NULL, ARG_VERSION }, + { "no-pager", no_argument, NULL, ARG_NO_PAGER }, + { "follow", no_argument, NULL, 'f' }, + { "output", required_argument, NULL, 'o' }, + { "all", no_argument, NULL, 'a' }, + { "lines", required_argument, NULL, 'n' }, + { "no-tail", no_argument, NULL, ARG_NO_TAIL }, + { "new-id128", no_argument, NULL, ARG_NEW_ID128 }, + { NULL, 0, NULL, 0 } + }; + + int c, r; + + assert(argc >= 0); + assert(argv); + + while ((c = getopt_long(argc, argv, "hfo:an:", options, NULL)) >= 0) { + + switch (c) { + + case 'h': + help(); + return 0; + + case ARG_VERSION: + puts(PACKAGE_STRING); + puts(DISTRIBUTION); + puts(SYSTEMD_FEATURES); + return 0; + + case ARG_NO_PAGER: + arg_no_pager = true; + break; + + case 'f': + arg_follow = true; + break; + + case 'o': + arg_output = output_mode_from_string(optarg); + if (arg_output < 0) { + log_error("Unknown output '%s'.", optarg); + return -EINVAL; + } + + break; + + case 'a': + arg_show_all = true; + break; + + case 'n': + r = safe_atoi(optarg, &arg_lines); + if (r < 0 || arg_lines < 0) { + log_error("Failed to parse lines '%s'", optarg); + return -EINVAL; + } + break; + + case ARG_NO_TAIL: + arg_no_tail = true; + break; + + case ARG_NEW_ID128: + arg_new_id128 = true; + break; + + case '?': + return -EINVAL; + + default: + log_error("Unknown option code %c", c); + return -EINVAL; + } + } + + if (arg_follow && !arg_no_tail && arg_lines < 0) + arg_lines = 10; + + return 1; +} + +static int generate_new_id128(void) { + sd_id128_t id; + int r; + unsigned i; + + r = sd_id128_randomize(&id); + if (r < 0) { + log_error("Failed to generate ID: %s", strerror(-r)); + return r; + } + + printf("As string:\n" + SD_ID128_FORMAT_STR "\n\n" + "As UUID:\n" + "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n\n" + "As macro:\n" + "#define MESSAGE_XYZ SD_ID128_MAKE(", + SD_ID128_FORMAT_VAL(id), + SD_ID128_FORMAT_VAL(id)); + + for (i = 0; i < 16; i++) + printf("%02x%s", id.bytes[i], i != 15 ? "," : ""); + + fputs(")\n", stdout); + + return 0; +} + +int main(int argc, char *argv[]) { + int r, i, fd; + sd_journal *j = NULL; + unsigned line = 0; + bool need_seek = false; + + log_parse_environment(); + log_open(); + + r = parse_argv(argc, argv); + if (r <= 0) + goto finish; + + if (arg_new_id128) { + r = generate_new_id128(); + goto finish; + } + + r = sd_journal_open(&j, 0); + if (r < 0) { + log_error("Failed to open journal: %s", strerror(-r)); + goto finish; + } + + for (i = optind; i < argc; i++) { + r = sd_journal_add_match(j, argv[i], strlen(argv[i])); + if (r < 0) { + log_error("Failed to add match: %s", strerror(-r)); + goto finish; + } + } + + fd = sd_journal_get_fd(j); + if (fd < 0) { + log_error("Failed to get wakeup fd: %s", strerror(-fd)); + goto finish; + } + + if (arg_lines >= 0) { + r = sd_journal_seek_tail(j); + if (r < 0) { + log_error("Failed to seek to tail: %s", strerror(-r)); + goto finish; + } + + r = sd_journal_previous_skip(j, arg_lines); + } else { + r = sd_journal_seek_head(j); + if (r < 0) { + log_error("Failed to seek to head: %s", strerror(-r)); + goto finish; + } + + r = sd_journal_next(j); + } + + if (r < 0) { + log_error("Failed to iterate through journal: %s", strerror(-r)); + goto finish; + } + + if (!arg_no_pager && !arg_follow) { + columns(); + pager_open(); + } + + if (arg_output == OUTPUT_JSON) { + fputc('[', stdout); + fflush(stdout); + } + + for (;;) { + for (;;) { + if (need_seek) { + r = sd_journal_next(j); + if (r < 0) { + log_error("Failed to iterate through journal: %s", strerror(-r)); + goto finish; + } + } + + if (r == 0) + break; + + line ++; + + r = output_journal(j, arg_output, line, arg_show_all); + if (r < 0) + goto finish; + + need_seek = true; + } + + if (!arg_follow) + break; + + r = fd_wait_for_event(fd, POLLIN, (usec_t) -1); + if (r < 0) { + log_error("Couldn't wait for event: %s", strerror(-r)); + goto finish; + } + + r = sd_journal_process(j); + if (r < 0) { + log_error("Failed to process: %s", strerror(-r)); + goto finish; + } + } + + if (arg_output == OUTPUT_JSON) + fputs("\n]\n", stdout); + +finish: + if (j) + sd_journal_close(j); + + pager_close(); + + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/journal/journald-gperf.gperf b/src/journal/journald-gperf.gperf new file mode 100644 index 0000000000..a56f6d966e --- /dev/null +++ b/src/journal/journald-gperf.gperf @@ -0,0 +1,31 @@ +%{ +#include <stddef.h> +#include "conf-parser.h" +#include "journald.h" +%} +struct ConfigPerfItem; +%null_strings +%language=ANSI-C +%define slot-name section_and_lvalue +%define hash-function-name journald_gperf_hash +%define lookup-function-name journald_gperf_lookup +%readonly-tables +%omit-struct-type +%struct-type +%includes +%% +Journal.RateLimitInterval, config_parse_usec, 0, offsetof(Server, rate_limit_interval) +Journal.RateLimitBurst, config_parse_unsigned, 0, offsetof(Server, rate_limit_burst) +Journal.Compress, config_parse_bool, 0, offsetof(Server, compress) +Journal.SystemMaxUse, config_parse_bytes_off, 0, offsetof(Server, system_metrics.max_use) +Journal.SystemMaxFileSize, config_parse_bytes_off, 0, offsetof(Server, system_metrics.max_size) +Journal.SystemMinFileSize, config_parse_bytes_off, 0, offsetof(Server, system_metrics.min_size) +Journal.SystemKeepFree, config_parse_bytes_off, 0, offsetof(Server, system_metrics.keep_free) +Journal.RuntimeMaxUse, config_parse_bytes_off, 0, offsetof(Server, runtime_metrics.max_use) +Journal.RuntimeMaxFileSize, config_parse_bytes_off, 0, offsetof(Server, runtime_metrics.max_size) +Journal.RuntimeMinFileSize, config_parse_bytes_off, 0, offsetof(Server, runtime_metrics.min_size) +Journal.RuntimeKeepFree, config_parse_bytes_off, 0, offsetof(Server, runtime_metrics.keep_free) +Journal.ForwardToSyslog, config_parse_bool, 0, offsetof(Server, forward_to_syslog) +Journal.ForwardToKMsg, config_parse_bool, 0, offsetof(Server, forward_to_kmsg) +Journal.ForwardToConsole, config_parse_bool, 0, offsetof(Server, forward_to_console) +Journal.ImportKernel, config_parse_bool, 0, offsetof(Server, import_proc_kmsg) diff --git a/src/journal/journald.c b/src/journal/journald.c new file mode 100644 index 0000000000..73f8ed6ae8 --- /dev/null +++ b/src/journal/journald.c @@ -0,0 +1,2723 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2011 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 <sys/epoll.h> +#include <sys/socket.h> +#include <errno.h> +#include <sys/signalfd.h> +#include <unistd.h> +#include <fcntl.h> +#include <stddef.h> +#include <sys/ioctl.h> +#include <linux/sockios.h> +#include <sys/statvfs.h> + +#include <systemd/sd-journal.h> +#include <systemd/sd-login.h> +#include <systemd/sd-messages.h> +#include <systemd/sd-daemon.h> + +#include "hashmap.h" +#include "journal-file.h" +#include "socket-util.h" +#include "cgroup-util.h" +#include "list.h" +#include "journal-rate-limit.h" +#include "journal-internal.h" +#include "conf-parser.h" +#include "journald.h" +#include "virt.h" + +#ifdef HAVE_ACL +#include <sys/acl.h> +#include <acl/libacl.h> +#include "acl-util.h" +#endif + +#ifdef HAVE_SELINUX +#include <selinux/selinux.h> +#endif + +#define USER_JOURNALS_MAX 1024 +#define STDOUT_STREAMS_MAX 4096 + +#define DEFAULT_RATE_LIMIT_INTERVAL (10*USEC_PER_SEC) +#define DEFAULT_RATE_LIMIT_BURST 200 + +#define RECHECK_AVAILABLE_SPACE_USEC (30*USEC_PER_SEC) + +#define RECHECK_VAR_AVAILABLE_USEC (30*USEC_PER_SEC) + +#define N_IOVEC_META_FIELDS 17 + +#define ENTRY_SIZE_MAX (1024*1024*32) + +typedef enum StdoutStreamState { + STDOUT_STREAM_IDENTIFIER, + STDOUT_STREAM_PRIORITY, + STDOUT_STREAM_LEVEL_PREFIX, + STDOUT_STREAM_FORWARD_TO_SYSLOG, + STDOUT_STREAM_FORWARD_TO_KMSG, + STDOUT_STREAM_FORWARD_TO_CONSOLE, + STDOUT_STREAM_RUNNING +} StdoutStreamState; + +struct StdoutStream { + Server *server; + StdoutStreamState state; + + int fd; + + struct ucred ucred; + + char *identifier; + int priority; + bool level_prefix:1; + bool forward_to_syslog:1; + bool forward_to_kmsg:1; + bool forward_to_console:1; + + char buffer[LINE_MAX+1]; + size_t length; + + LIST_FIELDS(StdoutStream, stdout_stream); +}; + +static int server_flush_to_var(Server *s); + +static uint64_t available_space(Server *s) { + char ids[33], *p; + const char *f; + sd_id128_t machine; + struct statvfs ss; + uint64_t sum = 0, avail = 0, ss_avail = 0; + int r; + DIR *d; + usec_t ts; + JournalMetrics *m; + + ts = now(CLOCK_MONOTONIC); + + if (s->cached_available_space_timestamp + RECHECK_AVAILABLE_SPACE_USEC > ts) + return s->cached_available_space; + + r = sd_id128_get_machine(&machine); + if (r < 0) + return 0; + + if (s->system_journal) { + f = "/var/log/journal/"; + m = &s->system_metrics; + } else { + f = "/run/log/journal/"; + m = &s->runtime_metrics; + } + + assert(m); + + p = strappend(f, sd_id128_to_string(machine, ids)); + if (!p) + return 0; + + d = opendir(p); + free(p); + + if (!d) + return 0; + + if (fstatvfs(dirfd(d), &ss) < 0) + goto finish; + + for (;;) { + struct stat st; + struct dirent buf, *de; + int k; + + k = readdir_r(d, &buf, &de); + if (k != 0) { + r = -k; + goto finish; + } + + if (!de) + break; + + if (!dirent_is_file_with_suffix(de, ".journal")) + continue; + + if (fstatat(dirfd(d), de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) + continue; + + sum += (uint64_t) st.st_blocks * (uint64_t) st.st_blksize; + } + + avail = sum >= m->max_use ? 0 : m->max_use - sum; + + ss_avail = ss.f_bsize * ss.f_bavail; + + ss_avail = ss_avail < m->keep_free ? 0 : ss_avail - m->keep_free; + + if (ss_avail < avail) + avail = ss_avail; + + s->cached_available_space = avail; + s->cached_available_space_timestamp = ts; + +finish: + closedir(d); + + return avail; +} + +static void server_read_file_gid(Server *s) { + const char *adm = "adm"; + int r; + + assert(s); + + if (s->file_gid_valid) + return; + + r = get_group_creds(&adm, &s->file_gid); + if (r < 0) + log_warning("Failed to resolve 'adm' group: %s", strerror(-r)); + + /* if we couldn't read the gid, then it will be 0, but that's + * fine and we shouldn't try to resolve the group again, so + * let's just pretend it worked right-away. */ + s->file_gid_valid = true; +} + +static void server_fix_perms(Server *s, JournalFile *f, uid_t uid) { + int r; +#ifdef HAVE_ACL + acl_t acl; + acl_entry_t entry; + acl_permset_t permset; +#endif + + assert(f); + + server_read_file_gid(s); + + r = fchmod_and_fchown(f->fd, 0640, 0, s->file_gid); + if (r < 0) + log_warning("Failed to fix access mode/rights on %s, ignoring: %s", f->path, strerror(-r)); + +#ifdef HAVE_ACL + if (uid <= 0) + return; + + acl = acl_get_fd(f->fd); + if (!acl) { + log_warning("Failed to read ACL on %s, ignoring: %m", f->path); + return; + } + + r = acl_find_uid(acl, uid, &entry); + if (r <= 0) { + + if (acl_create_entry(&acl, &entry) < 0 || + acl_set_tag_type(entry, ACL_USER) < 0 || + acl_set_qualifier(entry, &uid) < 0) { + log_warning("Failed to patch ACL on %s, ignoring: %m", f->path); + goto finish; + } + } + + if (acl_get_permset(entry, &permset) < 0 || + acl_add_perm(permset, ACL_READ) < 0 || + acl_calc_mask(&acl) < 0) { + log_warning("Failed to patch ACL on %s, ignoring: %m", f->path); + goto finish; + } + + if (acl_set_fd(f->fd, acl) < 0) + log_warning("Failed to set ACL on %s, ignoring: %m", f->path); + +finish: + acl_free(acl); +#endif +} + +static JournalFile* find_journal(Server *s, uid_t uid) { + char *p; + int r; + JournalFile *f; + char ids[33]; + sd_id128_t machine; + + assert(s); + + /* We split up user logs only on /var, not on /run. If the + * runtime file is open, we write to it exclusively, in order + * to guarantee proper order as soon as we flush /run to + * /var and close the runtime file. */ + + if (s->runtime_journal) + return s->runtime_journal; + + if (uid <= 0) + return s->system_journal; + + r = sd_id128_get_machine(&machine); + if (r < 0) + return s->system_journal; + + f = hashmap_get(s->user_journals, UINT32_TO_PTR(uid)); + if (f) + return f; + + if (asprintf(&p, "/var/log/journal/%s/user-%lu.journal", sd_id128_to_string(machine, ids), (unsigned long) uid) < 0) + return s->system_journal; + + while (hashmap_size(s->user_journals) >= USER_JOURNALS_MAX) { + /* Too many open? Then let's close one */ + f = hashmap_steal_first(s->user_journals); + assert(f); + journal_file_close(f); + } + + r = journal_file_open(p, O_RDWR|O_CREAT, 0640, s->system_journal, &f); + free(p); + + if (r < 0) + return s->system_journal; + + server_fix_perms(s, f, uid); + f->metrics = s->system_metrics; + f->compress = s->compress; + + r = hashmap_put(s->user_journals, UINT32_TO_PTR(uid), f); + if (r < 0) { + journal_file_close(f); + return s->system_journal; + } + + return f; +} + +static void server_rotate(Server *s) { + JournalFile *f; + void *k; + Iterator i; + int r; + + log_info("Rotating..."); + + if (s->runtime_journal) { + r = journal_file_rotate(&s->runtime_journal); + if (r < 0) + log_error("Failed to rotate %s: %s", s->runtime_journal->path, strerror(-r)); + } + + if (s->system_journal) { + r = journal_file_rotate(&s->system_journal); + if (r < 0) + log_error("Failed to rotate %s: %s", s->system_journal->path, strerror(-r)); + } + + HASHMAP_FOREACH_KEY(f, k, s->user_journals, i) { + r = journal_file_rotate(&f); + if (r < 0) + log_error("Failed to rotate %s: %s", f->path, strerror(-r)); + else + hashmap_replace(s->user_journals, k, f); + } +} + +static void server_vacuum(Server *s) { + char *p; + char ids[33]; + sd_id128_t machine; + int r; + + log_info("Vacuuming..."); + + r = sd_id128_get_machine(&machine); + if (r < 0) { + log_error("Failed to get machine ID: %s", strerror(-r)); + return; + } + + sd_id128_to_string(machine, ids); + + if (s->system_journal) { + if (asprintf(&p, "/var/log/journal/%s", ids) < 0) { + log_error("Out of memory."); + return; + } + + r = journal_directory_vacuum(p, s->system_metrics.max_use, s->system_metrics.keep_free); + if (r < 0 && r != -ENOENT) + log_error("Failed to vacuum %s: %s", p, strerror(-r)); + free(p); + } + + + if (s->runtime_journal) { + if (asprintf(&p, "/run/log/journal/%s", ids) < 0) { + log_error("Out of memory."); + return; + } + + r = journal_directory_vacuum(p, s->runtime_metrics.max_use, s->runtime_metrics.keep_free); + if (r < 0 && r != -ENOENT) + log_error("Failed to vacuum %s: %s", p, strerror(-r)); + free(p); + } + + s->cached_available_space_timestamp = 0; +} + +static char *shortened_cgroup_path(pid_t pid) { + int r; + char *process_path, *init_path, *path; + + assert(pid > 0); + + r = cg_get_by_pid(SYSTEMD_CGROUP_CONTROLLER, pid, &process_path); + if (r < 0) + return NULL; + + r = cg_get_by_pid(SYSTEMD_CGROUP_CONTROLLER, 1, &init_path); + if (r < 0) { + free(process_path); + return NULL; + } + + if (endswith(init_path, "/system")) + init_path[strlen(init_path) - 7] = 0; + else if (streq(init_path, "/")) + init_path[0] = 0; + + if (startswith(process_path, init_path)) { + char *p; + + p = strdup(process_path + strlen(init_path)); + if (!p) { + free(process_path); + free(init_path); + return NULL; + } + path = p; + } else { + path = process_path; + process_path = NULL; + } + + free(process_path); + free(init_path); + + return path; +} + +static void dispatch_message_real(Server *s, + struct iovec *iovec, unsigned n, unsigned m, + struct ucred *ucred, + struct timeval *tv) { + + char *pid = NULL, *uid = NULL, *gid = NULL, + *source_time = NULL, *boot_id = NULL, *machine_id = NULL, + *comm = NULL, *cmdline = NULL, *hostname = NULL, + *audit_session = NULL, *audit_loginuid = NULL, + *exe = NULL, *cgroup = NULL, *session = NULL, + *owner_uid = NULL, *unit = NULL, *selinux_context = NULL; + + char idbuf[33]; + sd_id128_t id; + int r; + char *t; + uid_t loginuid = 0, realuid = 0; + JournalFile *f; + bool vacuumed = false; + + assert(s); + assert(iovec); + assert(n > 0); + assert(n + N_IOVEC_META_FIELDS <= m); + + if (ucred) { + uint32_t audit; + uid_t owner; +#ifdef HAVE_SELINUX + security_context_t con; +#endif + + realuid = ucred->uid; + + if (asprintf(&pid, "_PID=%lu", (unsigned long) ucred->pid) >= 0) + IOVEC_SET_STRING(iovec[n++], pid); + + if (asprintf(&uid, "_UID=%lu", (unsigned long) ucred->uid) >= 0) + IOVEC_SET_STRING(iovec[n++], uid); + + if (asprintf(&gid, "_GID=%lu", (unsigned long) ucred->gid) >= 0) + IOVEC_SET_STRING(iovec[n++], gid); + + r = get_process_comm(ucred->pid, &t); + if (r >= 0) { + comm = strappend("_COMM=", t); + free(t); + + if (comm) + IOVEC_SET_STRING(iovec[n++], comm); + } + + r = get_process_exe(ucred->pid, &t); + if (r >= 0) { + exe = strappend("_EXE=", t); + free(t); + + if (exe) + IOVEC_SET_STRING(iovec[n++], exe); + } + + r = get_process_cmdline(ucred->pid, LINE_MAX, false, &t); + if (r >= 0) { + cmdline = strappend("_CMDLINE=", t); + free(t); + + if (cmdline) + IOVEC_SET_STRING(iovec[n++], cmdline); + } + + r = audit_session_from_pid(ucred->pid, &audit); + if (r >= 0) + if (asprintf(&audit_session, "_AUDIT_SESSION=%lu", (unsigned long) audit) >= 0) + IOVEC_SET_STRING(iovec[n++], audit_session); + + r = audit_loginuid_from_pid(ucred->pid, &loginuid); + if (r >= 0) + if (asprintf(&audit_loginuid, "_AUDIT_LOGINUID=%lu", (unsigned long) loginuid) >= 0) + IOVEC_SET_STRING(iovec[n++], audit_loginuid); + + t = shortened_cgroup_path(ucred->pid); + if (t) { + cgroup = strappend("_SYSTEMD_CGROUP=", t); + free(t); + + if (cgroup) + IOVEC_SET_STRING(iovec[n++], cgroup); + } + + if (sd_pid_get_session(ucred->pid, &t) >= 0) { + session = strappend("_SYSTEMD_SESSION=", t); + free(t); + + if (session) + IOVEC_SET_STRING(iovec[n++], session); + } + + if (sd_pid_get_unit(ucred->pid, &t) >= 0) { + unit = strappend("_SYSTEMD_UNIT=", t); + free(t); + + if (unit) + IOVEC_SET_STRING(iovec[n++], unit); + } + + if (sd_pid_get_owner_uid(ucred->uid, &owner) >= 0) + if (asprintf(&owner_uid, "_SYSTEMD_OWNER_UID=%lu", (unsigned long) owner) >= 0) + IOVEC_SET_STRING(iovec[n++], owner_uid); + +#ifdef HAVE_SELINUX + if (getpidcon(ucred->pid, &con) >= 0) { + selinux_context = strappend("_SELINUX_CONTEXT=", con); + if (selinux_context) + IOVEC_SET_STRING(iovec[n++], selinux_context); + + freecon(con); + } +#endif + } + + if (tv) { + if (asprintf(&source_time, "_SOURCE_REALTIME_TIMESTAMP=%llu", + (unsigned long long) timeval_load(tv)) >= 0) + IOVEC_SET_STRING(iovec[n++], source_time); + } + + /* Note that strictly speaking storing the boot id here is + * redundant since the entry includes this in-line + * anyway. However, we need this indexed, too. */ + r = sd_id128_get_boot(&id); + if (r >= 0) + if (asprintf(&boot_id, "_BOOT_ID=%s", sd_id128_to_string(id, idbuf)) >= 0) + IOVEC_SET_STRING(iovec[n++], boot_id); + + r = sd_id128_get_machine(&id); + if (r >= 0) + if (asprintf(&machine_id, "_MACHINE_ID=%s", sd_id128_to_string(id, idbuf)) >= 0) + IOVEC_SET_STRING(iovec[n++], machine_id); + + t = gethostname_malloc(); + if (t) { + hostname = strappend("_HOSTNAME=", t); + free(t); + if (hostname) + IOVEC_SET_STRING(iovec[n++], hostname); + } + + assert(n <= m); + + server_flush_to_var(s); + +retry: + f = find_journal(s, realuid == 0 ? 0 : loginuid); + if (!f) + log_warning("Dropping message, as we can't find a place to store the data."); + else { + r = journal_file_append_entry(f, NULL, iovec, n, &s->seqnum, NULL, NULL); + + if (r == -E2BIG && !vacuumed) { + log_info("Allocation limit reached."); + + server_rotate(s); + server_vacuum(s); + vacuumed = true; + + log_info("Retrying write."); + goto retry; + } + + if (r < 0) + log_error("Failed to write entry, ignoring: %s", strerror(-r)); + } + + free(pid); + free(uid); + free(gid); + free(comm); + free(exe); + free(cmdline); + free(source_time); + free(boot_id); + free(machine_id); + free(hostname); + free(audit_session); + free(audit_loginuid); + free(cgroup); + free(session); + free(owner_uid); + free(unit); + free(selinux_context); +} + +static void driver_message(Server *s, sd_id128_t message_id, const char *format, ...) { + char mid[11 + 32 + 1]; + char buffer[16 + LINE_MAX + 1]; + struct iovec iovec[N_IOVEC_META_FIELDS + 4]; + int n = 0; + va_list ap; + struct ucred ucred; + + assert(s); + assert(format); + + IOVEC_SET_STRING(iovec[n++], "PRIORITY=5"); + IOVEC_SET_STRING(iovec[n++], "_TRANSPORT=driver"); + + memcpy(buffer, "MESSAGE=", 8); + va_start(ap, format); + vsnprintf(buffer + 8, sizeof(buffer) - 8, format, ap); + va_end(ap); + char_array_0(buffer); + IOVEC_SET_STRING(iovec[n++], buffer); + + snprintf(mid, sizeof(mid), "MESSAGE_ID=" SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(message_id)); + char_array_0(mid); + IOVEC_SET_STRING(iovec[n++], mid); + + zero(ucred); + ucred.pid = getpid(); + ucred.uid = getuid(); + ucred.gid = getgid(); + + dispatch_message_real(s, iovec, n, ELEMENTSOF(iovec), &ucred, NULL); +} + +static void dispatch_message(Server *s, + struct iovec *iovec, unsigned n, unsigned m, + struct ucred *ucred, + struct timeval *tv, + int priority) { + int rl; + char *path = NULL, *c; + + assert(s); + assert(iovec || n == 0); + + if (n == 0) + return; + + if (!ucred) + goto finish; + + path = shortened_cgroup_path(ucred->pid); + if (!path) + goto finish; + + /* example: /user/lennart/3/foobar + * /system/dbus.service/foobar + * + * So let's cut of everything past the third /, since that is + * wher user directories start */ + + c = strchr(path, '/'); + if (c) { + c = strchr(c+1, '/'); + if (c) { + c = strchr(c+1, '/'); + if (c) + *c = 0; + } + } + + rl = journal_rate_limit_test(s->rate_limit, path, priority & LOG_PRIMASK, available_space(s)); + + if (rl == 0) { + free(path); + return; + } + + /* Write a suppression message if we suppressed something */ + if (rl > 1) + driver_message(s, SD_MESSAGE_JOURNAL_DROPPED, "Suppressed %u messages from %s", rl - 1, path); + + free(path); + +finish: + dispatch_message_real(s, iovec, n, m, ucred, tv); +} + +static void forward_syslog_iovec(Server *s, const struct iovec *iovec, unsigned n_iovec, struct ucred *ucred, struct timeval *tv) { + struct msghdr msghdr; + struct cmsghdr *cmsg; + union { + struct cmsghdr cmsghdr; + uint8_t buf[CMSG_SPACE(sizeof(struct ucred))]; + } control; + union sockaddr_union sa; + + assert(s); + assert(iovec); + assert(n_iovec > 0); + + zero(msghdr); + msghdr.msg_iov = (struct iovec*) iovec; + msghdr.msg_iovlen = n_iovec; + + zero(sa); + sa.un.sun_family = AF_UNIX; + strncpy(sa.un.sun_path, "/run/systemd/journal/syslog", sizeof(sa.un.sun_path)); + msghdr.msg_name = &sa; + msghdr.msg_namelen = offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path); + + if (ucred) { + zero(control); + msghdr.msg_control = &control; + msghdr.msg_controllen = sizeof(control); + + cmsg = CMSG_FIRSTHDR(&msghdr); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_CREDENTIALS; + cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred)); + memcpy(CMSG_DATA(cmsg), ucred, sizeof(struct ucred)); + msghdr.msg_controllen = cmsg->cmsg_len; + } + + /* Forward the syslog message we received via /dev/log to + * /run/systemd/syslog. Unfortunately we currently can't set + * the SO_TIMESTAMP auxiliary data, and hence we don't. */ + + if (sendmsg(s->syslog_fd, &msghdr, MSG_NOSIGNAL) >= 0) + return; + + /* The socket is full? I guess the syslog implementation is + * too slow, and we shouldn't wait for that... */ + if (errno == EAGAIN) + return; + + if (ucred && errno == ESRCH) { + struct ucred u; + + /* Hmm, presumably the sender process vanished + * by now, so let's fix it as good as we + * can, and retry */ + + u = *ucred; + u.pid = getpid(); + memcpy(CMSG_DATA(cmsg), &u, sizeof(struct ucred)); + + if (sendmsg(s->syslog_fd, &msghdr, MSG_NOSIGNAL) >= 0) + return; + + if (errno == EAGAIN) + return; + } + + log_debug("Failed to forward syslog message: %m"); +} + +static void forward_syslog_raw(Server *s, const char *buffer, struct ucred *ucred, struct timeval *tv) { + struct iovec iovec; + + assert(s); + assert(buffer); + + IOVEC_SET_STRING(iovec, buffer); + forward_syslog_iovec(s, &iovec, 1, ucred, tv); +} + +static void forward_syslog(Server *s, int priority, const char *identifier, const char *message, struct ucred *ucred, struct timeval *tv) { + struct iovec iovec[5]; + char header_priority[6], header_time[64], header_pid[16]; + int n = 0; + time_t t; + struct tm *tm; + char *ident_buf = NULL; + + assert(s); + assert(priority >= 0); + assert(priority <= 999); + assert(message); + + /* First: priority field */ + snprintf(header_priority, sizeof(header_priority), "<%i>", priority); + char_array_0(header_priority); + IOVEC_SET_STRING(iovec[n++], header_priority); + + /* Second: timestamp */ + t = tv ? tv->tv_sec : ((time_t) (now(CLOCK_REALTIME) / USEC_PER_SEC)); + tm = localtime(&t); + if (!tm) + return; + if (strftime(header_time, sizeof(header_time), "%h %e %T ", tm) <= 0) + return; + IOVEC_SET_STRING(iovec[n++], header_time); + + /* Third: identifier and PID */ + if (ucred) { + if (!identifier) { + get_process_comm(ucred->pid, &ident_buf); + identifier = ident_buf; + } + + snprintf(header_pid, sizeof(header_pid), "[%lu]: ", (unsigned long) ucred->pid); + char_array_0(header_pid); + + if (identifier) + IOVEC_SET_STRING(iovec[n++], identifier); + + IOVEC_SET_STRING(iovec[n++], header_pid); + } else if (identifier) { + IOVEC_SET_STRING(iovec[n++], identifier); + IOVEC_SET_STRING(iovec[n++], ": "); + } + + /* Fourth: message */ + IOVEC_SET_STRING(iovec[n++], message); + + forward_syslog_iovec(s, iovec, n, ucred, tv); + + free(ident_buf); +} + +static int fixup_priority(int priority) { + + if ((priority & LOG_FACMASK) == 0) + return (priority & LOG_PRIMASK) | LOG_USER; + + return priority; +} + +static void forward_kmsg(Server *s, int priority, const char *identifier, const char *message, struct ucred *ucred) { + struct iovec iovec[5]; + char header_priority[6], header_pid[16]; + int n = 0; + char *ident_buf = NULL; + int fd; + + assert(s); + assert(priority >= 0); + assert(priority <= 999); + assert(message); + + /* Never allow messages with kernel facility to be written to + * kmsg, regardless where the data comes from. */ + priority = fixup_priority(priority); + + /* First: priority field */ + snprintf(header_priority, sizeof(header_priority), "<%i>", priority); + char_array_0(header_priority); + IOVEC_SET_STRING(iovec[n++], header_priority); + + /* Second: identifier and PID */ + if (ucred) { + if (!identifier) { + get_process_comm(ucred->pid, &ident_buf); + identifier = ident_buf; + } + + snprintf(header_pid, sizeof(header_pid), "[%lu]: ", (unsigned long) ucred->pid); + char_array_0(header_pid); + + if (identifier) + IOVEC_SET_STRING(iovec[n++], identifier); + + IOVEC_SET_STRING(iovec[n++], header_pid); + } else if (identifier) { + IOVEC_SET_STRING(iovec[n++], identifier); + IOVEC_SET_STRING(iovec[n++], ": "); + } + + /* Fourth: message */ + IOVEC_SET_STRING(iovec[n++], message); + IOVEC_SET_STRING(iovec[n++], "\n"); + + fd = open("/dev/kmsg", O_WRONLY|O_NOCTTY|O_CLOEXEC); + if (fd < 0) { + log_debug("Failed to open /dev/kmsg for logging: %s", strerror(errno)); + goto finish; + } + + if (writev(fd, iovec, n) < 0) + log_debug("Failed to write to /dev/kmsg for logging: %s", strerror(errno)); + + close_nointr_nofail(fd); + +finish: + free(ident_buf); +} + +static void forward_console(Server *s, const char *identifier, const char *message, struct ucred *ucred) { + struct iovec iovec[4]; + char header_pid[16]; + int n = 0, fd; + char *ident_buf = NULL; + + assert(s); + assert(message); + + /* First: identifier and PID */ + if (ucred) { + if (!identifier) { + get_process_comm(ucred->pid, &ident_buf); + identifier = ident_buf; + } + + snprintf(header_pid, sizeof(header_pid), "[%lu]: ", (unsigned long) ucred->pid); + char_array_0(header_pid); + + if (identifier) + IOVEC_SET_STRING(iovec[n++], identifier); + + IOVEC_SET_STRING(iovec[n++], header_pid); + } else if (identifier) { + IOVEC_SET_STRING(iovec[n++], identifier); + IOVEC_SET_STRING(iovec[n++], ": "); + } + + /* Third: message */ + IOVEC_SET_STRING(iovec[n++], message); + IOVEC_SET_STRING(iovec[n++], "\n"); + + fd = open_terminal("/dev/console", O_WRONLY|O_NOCTTY|O_CLOEXEC); + if (fd < 0) { + log_debug("Failed to open /dev/console for logging: %s", strerror(errno)); + goto finish; + } + + if (writev(fd, iovec, n) < 0) + log_debug("Failed to write to /dev/console for logging: %s", strerror(errno)); + + close_nointr_nofail(fd); + +finish: + free(ident_buf); +} + +static void read_identifier(const char **buf, char **identifier, char **pid) { + const char *p; + char *t; + size_t l, e; + + assert(buf); + assert(identifier); + assert(pid); + + p = *buf; + + p += strspn(p, WHITESPACE); + l = strcspn(p, WHITESPACE); + + if (l <= 0 || + p[l-1] != ':') + return; + + e = l; + l--; + + if (p[l-1] == ']') { + size_t k = l-1; + + for (;;) { + + if (p[k] == '[') { + t = strndup(p+k+1, l-k-2); + if (t) + *pid = t; + + l = k; + break; + } + + if (k == 0) + break; + + k--; + } + } + + t = strndup(p, l); + if (t) + *identifier = t; + + *buf = p + e; + *buf += strspn(*buf, WHITESPACE); +} + +static void process_syslog_message(Server *s, const char *buf, struct ucred *ucred, struct timeval *tv) { + char *message = NULL, *syslog_priority = NULL, *syslog_facility = NULL, *syslog_identifier = NULL, *syslog_pid = NULL; + struct iovec iovec[N_IOVEC_META_FIELDS + 6]; + unsigned n = 0; + int priority = LOG_USER | LOG_INFO; + char *identifier = NULL, *pid = NULL; + + assert(s); + assert(buf); + + if (s->forward_to_syslog) + forward_syslog_raw(s, buf, ucred, tv); + + parse_syslog_priority((char**) &buf, &priority); + skip_syslog_date((char**) &buf); + read_identifier(&buf, &identifier, &pid); + + if (s->forward_to_kmsg) + forward_kmsg(s, priority, identifier, buf, ucred); + + if (s->forward_to_console) + forward_console(s, identifier, buf, ucred); + + IOVEC_SET_STRING(iovec[n++], "_TRANSPORT=syslog"); + + if (asprintf(&syslog_priority, "PRIORITY=%i", priority & LOG_PRIMASK) >= 0) + IOVEC_SET_STRING(iovec[n++], syslog_priority); + + if (priority & LOG_FACMASK) + if (asprintf(&syslog_facility, "SYSLOG_FACILITY=%i", LOG_FAC(priority)) >= 0) + IOVEC_SET_STRING(iovec[n++], syslog_facility); + + if (identifier) { + syslog_identifier = strappend("SYSLOG_IDENTIFIER=", identifier); + if (syslog_identifier) + IOVEC_SET_STRING(iovec[n++], syslog_identifier); + } + + if (pid) { + syslog_pid = strappend("SYSLOG_PID=", pid); + if (syslog_pid) + IOVEC_SET_STRING(iovec[n++], syslog_pid); + } + + message = strappend("MESSAGE=", buf); + if (message) + IOVEC_SET_STRING(iovec[n++], message); + + dispatch_message(s, iovec, n, ELEMENTSOF(iovec), ucred, tv, priority); + + free(message); + free(identifier); + free(pid); + free(syslog_priority); + free(syslog_facility); + free(syslog_identifier); +} + +static bool valid_user_field(const char *p, size_t l) { + const char *a; + + /* We kinda enforce POSIX syntax recommendations for + environment variables here, but make a couple of additional + requirements. + + http://pubs.opengroup.org/onlinepubs/000095399/basedefs/xbd_chap08.html */ + + /* No empty field names */ + if (l <= 0) + return false; + + /* Don't allow names longer than 64 chars */ + if (l > 64) + return false; + + /* Variables starting with an underscore are protected */ + if (p[0] == '_') + return false; + + /* Don't allow digits as first character */ + if (p[0] >= '0' && p[0] <= '9') + return false; + + /* Only allow A-Z0-9 and '_' */ + for (a = p; a < p + l; a++) + if (!((*a >= 'A' && *a <= 'Z') || + (*a >= '0' && *a <= '9') || + *a == '_')) + return false; + + return true; +} + +static void process_native_message(Server *s, const void *buffer, size_t buffer_size, struct ucred *ucred, struct timeval *tv) { + struct iovec *iovec = NULL; + unsigned n = 0, m = 0, j, tn = (unsigned) -1; + const char *p; + size_t remaining; + int priority = LOG_INFO; + char *identifier = NULL, *message = NULL; + + assert(s); + assert(buffer || n == 0); + + p = buffer; + remaining = buffer_size; + + while (remaining > 0) { + const char *e, *q; + + e = memchr(p, '\n', remaining); + + if (!e) { + /* Trailing noise, let's ignore it, and flush what we collected */ + log_debug("Received message with trailing noise, ignoring."); + break; + } + + if (e == p) { + /* Entry separator */ + dispatch_message(s, iovec, n, m, ucred, tv, priority); + n = 0; + priority = LOG_INFO; + + p++; + remaining--; + continue; + } + + if (*p == '.' || *p == '#') { + /* Ignore control commands for now, and + * comments too. */ + remaining -= (e - p) + 1; + p = e + 1; + continue; + } + + /* A property follows */ + + if (n+N_IOVEC_META_FIELDS >= m) { + struct iovec *c; + unsigned u; + + u = MAX((n+N_IOVEC_META_FIELDS+1) * 2U, 4U); + c = realloc(iovec, u * sizeof(struct iovec)); + if (!c) { + log_error("Out of memory"); + break; + } + + iovec = c; + m = u; + } + + q = memchr(p, '=', e - p); + if (q) { + if (valid_user_field(p, q - p)) { + size_t l; + + l = e - p; + + /* If the field name starts with an + * underscore, skip the variable, + * since that indidates a trusted + * field */ + iovec[n].iov_base = (char*) p; + iovec[n].iov_len = l; + n++; + + /* We need to determine the priority + * of this entry for the rate limiting + * logic */ + if (l == 10 && + memcmp(p, "PRIORITY=", 9) == 0 && + p[9] >= '0' && p[9] <= '9') + priority = (priority & LOG_FACMASK) | (p[9] - '0'); + + else if (l == 17 && + memcmp(p, "SYSLOG_FACILITY=", 16) == 0 && + p[16] >= '0' && p[16] <= '9') + priority = (priority & LOG_PRIMASK) | ((p[16] - '0') << 3); + + else if (l == 18 && + memcmp(p, "SYSLOG_FACILITY=", 16) == 0 && + p[16] >= '0' && p[16] <= '9' && + p[17] >= '0' && p[17] <= '9') + priority = (priority & LOG_PRIMASK) | (((p[16] - '0')*10 + (p[17] - '0')) << 3); + + else if (l >= 12 && + memcmp(p, "SYSLOG_IDENTIFIER=", 11) == 0) { + char *t; + + t = strndup(p + 11, l - 11); + if (t) { + free(identifier); + identifier = t; + } + } else if (l >= 8 && + memcmp(p, "MESSAGE=", 8) == 0) { + char *t; + + t = strndup(p + 8, l - 8); + if (t) { + free(message); + message = t; + } + } + } + + remaining -= (e - p) + 1; + p = e + 1; + continue; + } else { + uint64_t l; + char *k; + + if (remaining < e - p + 1 + sizeof(uint64_t) + 1) { + log_debug("Failed to parse message, ignoring."); + break; + } + + memcpy(&l, e + 1, sizeof(uint64_t)); + l = le64toh(l); + + if (remaining < e - p + 1 + sizeof(uint64_t) + l + 1 || + e[1+sizeof(uint64_t)+l] != '\n') { + log_debug("Failed to parse message, ignoring."); + break; + } + + k = malloc((e - p) + 1 + l); + if (!k) { + log_error("Out of memory"); + break; + } + + memcpy(k, p, e - p); + k[e - p] = '='; + memcpy(k + (e - p) + 1, e + 1 + sizeof(uint64_t), l); + + if (valid_user_field(p, e - p)) { + iovec[n].iov_base = k; + iovec[n].iov_len = (e - p) + 1 + l; + n++; + } else + free(k); + + remaining -= (e - p) + 1 + sizeof(uint64_t) + l + 1; + p = e + 1 + sizeof(uint64_t) + l + 1; + } + } + + if (n <= 0) + goto finish; + + tn = n++; + IOVEC_SET_STRING(iovec[tn], "_TRANSPORT=journal"); + + if (message) { + if (s->forward_to_syslog) + forward_syslog(s, priority, identifier, message, ucred, tv); + + if (s->forward_to_kmsg) + forward_kmsg(s, priority, identifier, message, ucred); + + if (s->forward_to_console) + forward_console(s, identifier, message, ucred); + } + + dispatch_message(s, iovec, n, m, ucred, tv, priority); + +finish: + for (j = 0; j < n; j++) { + if (j == tn) + continue; + + if (iovec[j].iov_base < buffer || + (const uint8_t*) iovec[j].iov_base >= (const uint8_t*) buffer + buffer_size) + free(iovec[j].iov_base); + } + + free(identifier); + free(message); +} + +static void process_native_file(Server *s, int fd, struct ucred *ucred, struct timeval *tv) { + struct stat st; + void *p; + ssize_t n; + + assert(s); + assert(fd >= 0); + + /* Data is in the passed file, since it didn't fit in a + * datagram. We can't map the file here, since clients might + * then truncate it and trigger a SIGBUS for us. So let's + * stupidly read it */ + + if (fstat(fd, &st) < 0) { + log_error("Failed to stat passed file, ignoring: %m"); + return; + } + + if (!S_ISREG(st.st_mode)) { + log_error("File passed is not regular. Ignoring."); + return; + } + + if (st.st_size <= 0) + return; + + if (st.st_size > ENTRY_SIZE_MAX) { + log_error("File passed too large. Ignoring."); + return; + } + + p = malloc(st.st_size); + if (!p) { + log_error("Out of memory"); + return; + } + + n = pread(fd, p, st.st_size, 0); + if (n < 0) + log_error("Failed to read file, ignoring: %s", strerror(-n)); + else if (n > 0) + process_native_message(s, p, n, ucred, tv); + + free(p); +} + +static int stdout_stream_log(StdoutStream *s, const char *p) { + struct iovec iovec[N_IOVEC_META_FIELDS + 5]; + char *message = NULL, *syslog_priority = NULL, *syslog_facility = NULL, *syslog_identifier = NULL; + unsigned n = 0; + int priority; + + assert(s); + assert(p); + + if (isempty(p)) + return 0; + + priority = s->priority; + + if (s->level_prefix) + parse_syslog_priority((char**) &p, &priority); + + if (s->forward_to_syslog || s->server->forward_to_syslog) + forward_syslog(s->server, fixup_priority(priority), s->identifier, p, &s->ucred, NULL); + + if (s->forward_to_kmsg || s->server->forward_to_kmsg) + forward_kmsg(s->server, priority, s->identifier, p, &s->ucred); + + if (s->forward_to_console || s->server->forward_to_console) + forward_console(s->server, s->identifier, p, &s->ucred); + + IOVEC_SET_STRING(iovec[n++], "_TRANSPORT=stdout"); + + if (asprintf(&syslog_priority, "PRIORITY=%i", priority & LOG_PRIMASK) >= 0) + IOVEC_SET_STRING(iovec[n++], syslog_priority); + + if (priority & LOG_FACMASK) + if (asprintf(&syslog_facility, "SYSLOG_FACILITY=%i", LOG_FAC(priority)) >= 0) + IOVEC_SET_STRING(iovec[n++], syslog_facility); + + if (s->identifier) { + syslog_identifier = strappend("SYSLOG_IDENTIFIER=", s->identifier); + if (syslog_identifier) + IOVEC_SET_STRING(iovec[n++], syslog_identifier); + } + + message = strappend("MESSAGE=", p); + if (message) + IOVEC_SET_STRING(iovec[n++], message); + + dispatch_message(s->server, iovec, n, ELEMENTSOF(iovec), &s->ucred, NULL, priority); + + free(message); + free(syslog_priority); + free(syslog_facility); + free(syslog_identifier); + + return 0; +} + +static int stdout_stream_line(StdoutStream *s, char *p) { + int r; + + assert(s); + assert(p); + + p = strstrip(p); + + switch (s->state) { + + case STDOUT_STREAM_IDENTIFIER: + if (isempty(p)) + s->identifier = NULL; + else { + s->identifier = strdup(p); + if (!s->identifier) { + log_error("Out of memory"); + return -ENOMEM; + } + } + + s->state = STDOUT_STREAM_PRIORITY; + return 0; + + case STDOUT_STREAM_PRIORITY: + r = safe_atoi(p, &s->priority); + if (r < 0 || s->priority <= 0 || s->priority >= 999) { + log_warning("Failed to parse log priority line."); + return -EINVAL; + } + + s->state = STDOUT_STREAM_LEVEL_PREFIX; + return 0; + + case STDOUT_STREAM_LEVEL_PREFIX: + r = parse_boolean(p); + if (r < 0) { + log_warning("Failed to parse level prefix line."); + return -EINVAL; + } + + s->level_prefix = !!r; + s->state = STDOUT_STREAM_FORWARD_TO_SYSLOG; + return 0; + + case STDOUT_STREAM_FORWARD_TO_SYSLOG: + r = parse_boolean(p); + if (r < 0) { + log_warning("Failed to parse forward to syslog line."); + return -EINVAL; + } + + s->forward_to_syslog = !!r; + s->state = STDOUT_STREAM_FORWARD_TO_KMSG; + return 0; + + case STDOUT_STREAM_FORWARD_TO_KMSG: + r = parse_boolean(p); + if (r < 0) { + log_warning("Failed to parse copy to kmsg line."); + return -EINVAL; + } + + s->forward_to_kmsg = !!r; + s->state = STDOUT_STREAM_FORWARD_TO_CONSOLE; + return 0; + + case STDOUT_STREAM_FORWARD_TO_CONSOLE: + r = parse_boolean(p); + if (r < 0) { + log_warning("Failed to parse copy to console line."); + return -EINVAL; + } + + s->forward_to_console = !!r; + s->state = STDOUT_STREAM_RUNNING; + return 0; + + case STDOUT_STREAM_RUNNING: + return stdout_stream_log(s, p); + } + + assert_not_reached("Unknown stream state"); +} + +static int stdout_stream_scan(StdoutStream *s, bool force_flush) { + char *p; + size_t remaining; + int r; + + assert(s); + + p = s->buffer; + remaining = s->length; + for (;;) { + char *end; + size_t skip; + + end = memchr(p, '\n', remaining); + if (end) + skip = end - p + 1; + else if (remaining >= sizeof(s->buffer) - 1) { + end = p + sizeof(s->buffer) - 1; + skip = remaining; + } else + break; + + *end = 0; + + r = stdout_stream_line(s, p); + if (r < 0) + return r; + + remaining -= skip; + p += skip; + } + + if (force_flush && remaining > 0) { + p[remaining] = 0; + r = stdout_stream_line(s, p); + if (r < 0) + return r; + + p += remaining; + remaining = 0; + } + + if (p > s->buffer) { + memmove(s->buffer, p, remaining); + s->length = remaining; + } + + return 0; +} + +static int stdout_stream_process(StdoutStream *s) { + ssize_t l; + int r; + + assert(s); + + l = read(s->fd, s->buffer+s->length, sizeof(s->buffer)-1-s->length); + if (l < 0) { + + if (errno == EAGAIN) + return 0; + + log_warning("Failed to read from stream: %m"); + return -errno; + } + + if (l == 0) { + r = stdout_stream_scan(s, true); + if (r < 0) + return r; + + return 0; + } + + s->length += l; + r = stdout_stream_scan(s, false); + if (r < 0) + return r; + + return 1; + +} + +static void stdout_stream_free(StdoutStream *s) { + assert(s); + + if (s->server) { + assert(s->server->n_stdout_streams > 0); + s->server->n_stdout_streams --; + LIST_REMOVE(StdoutStream, stdout_stream, s->server->stdout_streams, s); + } + + if (s->fd >= 0) { + if (s->server) + epoll_ctl(s->server->epoll_fd, EPOLL_CTL_DEL, s->fd, NULL); + + close_nointr_nofail(s->fd); + } + + free(s->identifier); + free(s); +} + +static int stdout_stream_new(Server *s) { + StdoutStream *stream; + int fd, r; + socklen_t len; + struct epoll_event ev; + + assert(s); + + fd = accept4(s->stdout_fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC); + if (fd < 0) { + if (errno == EAGAIN) + return 0; + + log_error("Failed to accept stdout connection: %m"); + return -errno; + } + + if (s->n_stdout_streams >= STDOUT_STREAMS_MAX) { + log_warning("Too many stdout streams, refusing connection."); + close_nointr_nofail(fd); + return 0; + } + + stream = new0(StdoutStream, 1); + if (!stream) { + log_error("Out of memory."); + close_nointr_nofail(fd); + return -ENOMEM; + } + + stream->fd = fd; + + len = sizeof(stream->ucred); + if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &stream->ucred, &len) < 0) { + log_error("Failed to determine peer credentials: %m"); + r = -errno; + goto fail; + } + + if (shutdown(fd, SHUT_WR) < 0) { + log_error("Failed to shutdown writing side of socket: %m"); + r = -errno; + goto fail; + } + + zero(ev); + ev.data.ptr = stream; + ev.events = EPOLLIN; + if (epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0) { + log_error("Failed to add stream to event loop: %m"); + r = -errno; + goto fail; + } + + stream->server = s; + LIST_PREPEND(StdoutStream, stdout_stream, s->stdout_streams, stream); + s->n_stdout_streams ++; + + return 0; + +fail: + stdout_stream_free(stream); + return r; +} + +static int parse_kernel_timestamp(char **_p, usec_t *t) { + usec_t r; + int k, i; + char *p; + + assert(_p); + assert(*_p); + assert(t); + + p = *_p; + + if (strlen(p) < 14 || p[0] != '[' || p[13] != ']' || p[6] != '.') + return 0; + + r = 0; + + for (i = 1; i <= 5; i++) { + r *= 10; + + if (p[i] == ' ') + continue; + + k = undecchar(p[i]); + if (k < 0) + return 0; + + r += k; + } + + for (i = 7; i <= 12; i++) { + r *= 10; + + k = undecchar(p[i]); + if (k < 0) + return 0; + + r += k; + } + + *t = r; + *_p += 14; + *_p += strspn(*_p, WHITESPACE); + + return 1; +} + +static void proc_kmsg_line(Server *s, const char *p) { + struct iovec iovec[N_IOVEC_META_FIELDS + 7]; + char *message = NULL, *syslog_priority = NULL, *syslog_pid = NULL, *syslog_facility = NULL, *syslog_identifier = NULL, *source_time = NULL; + int priority = LOG_KERN | LOG_INFO; + unsigned n = 0; + usec_t usec; + char *identifier = NULL, *pid = NULL; + + assert(s); + assert(p); + + if (isempty(p)) + return; + + parse_syslog_priority((char **) &p, &priority); + + if (s->forward_to_kmsg && (priority & LOG_FACMASK) != LOG_KERN) + return; + + if (parse_kernel_timestamp((char **) &p, &usec) > 0) { + if (asprintf(&source_time, "_SOURCE_MONOTONIC_TIMESTAMP=%llu", + (unsigned long long) usec) >= 0) + IOVEC_SET_STRING(iovec[n++], source_time); + } + + IOVEC_SET_STRING(iovec[n++], "_TRANSPORT=kernel"); + + if (asprintf(&syslog_priority, "PRIORITY=%i", priority & LOG_PRIMASK) >= 0) + IOVEC_SET_STRING(iovec[n++], syslog_priority); + + if ((priority & LOG_FACMASK) == LOG_KERN) { + + if (s->forward_to_syslog) + forward_syslog(s, priority, "kernel", p, NULL, NULL); + + IOVEC_SET_STRING(iovec[n++], "SYSLOG_IDENTIFIER=kernel"); + } else { + read_identifier(&p, &identifier, &pid); + + if (s->forward_to_syslog) + forward_syslog(s, priority, identifier, p, NULL, NULL); + + if (identifier) { + syslog_identifier = strappend("SYSLOG_IDENTIFIER=", identifier); + if (syslog_identifier) + IOVEC_SET_STRING(iovec[n++], syslog_identifier); + } + + if (pid) { + syslog_pid = strappend("SYSLOG_PID=", pid); + if (syslog_pid) + IOVEC_SET_STRING(iovec[n++], syslog_pid); + } + + if (asprintf(&syslog_facility, "SYSLOG_FACILITY=%i", LOG_FAC(priority)) >= 0) + IOVEC_SET_STRING(iovec[n++], syslog_facility); + } + + message = strappend("MESSAGE=", p); + if (message) + IOVEC_SET_STRING(iovec[n++], message); + + dispatch_message(s, iovec, n, ELEMENTSOF(iovec), NULL, NULL, priority); + + free(message); + free(syslog_priority); + free(syslog_identifier); + free(syslog_pid); + free(syslog_facility); + free(source_time); + free(identifier); + free(pid); +} + +static void proc_kmsg_scan(Server *s) { + char *p; + size_t remaining; + + assert(s); + + p = s->proc_kmsg_buffer; + remaining = s->proc_kmsg_length; + for (;;) { + char *end; + size_t skip; + + end = memchr(p, '\n', remaining); + if (end) + skip = end - p + 1; + else if (remaining >= sizeof(s->proc_kmsg_buffer) - 1) { + end = p + sizeof(s->proc_kmsg_buffer) - 1; + skip = remaining; + } else + break; + + *end = 0; + + proc_kmsg_line(s, p); + + remaining -= skip; + p += skip; + } + + if (p > s->proc_kmsg_buffer) { + memmove(s->proc_kmsg_buffer, p, remaining); + s->proc_kmsg_length = remaining; + } +} + +static int system_journal_open(Server *s) { + int r; + char *fn; + sd_id128_t machine; + char ids[33]; + + r = sd_id128_get_machine(&machine); + if (r < 0) + return r; + + sd_id128_to_string(machine, ids); + + if (!s->system_journal) { + + /* First try to create the machine path, but not the prefix */ + fn = strappend("/var/log/journal/", ids); + if (!fn) + return -ENOMEM; + (void) mkdir(fn, 0755); + free(fn); + + /* The create the system journal file */ + fn = join("/var/log/journal/", ids, "/system.journal", NULL); + if (!fn) + return -ENOMEM; + + r = journal_file_open(fn, O_RDWR|O_CREAT, 0640, NULL, &s->system_journal); + free(fn); + + if (r >= 0) { + journal_default_metrics(&s->system_metrics, s->system_journal->fd); + + s->system_journal->metrics = s->system_metrics; + s->system_journal->compress = s->compress; + + server_fix_perms(s, s->system_journal, 0); + } else if (r < 0) { + + if (r != -ENOENT && r != -EROFS) + log_warning("Failed to open system journal: %s", strerror(-r)); + + r = 0; + } + } + + if (!s->runtime_journal) { + + fn = join("/run/log/journal/", ids, "/system.journal", NULL); + if (!fn) + return -ENOMEM; + + if (s->system_journal) { + + /* Try to open the runtime journal, but only + * if it already exists, so that we can flush + * it into the system journal */ + + r = journal_file_open(fn, O_RDWR, 0640, NULL, &s->runtime_journal); + free(fn); + + if (r < 0) { + if (r != -ENOENT) + log_warning("Failed to open runtime journal: %s", strerror(-r)); + + r = 0; + } + + } else { + + /* OK, we really need the runtime journal, so create + * it if necessary. */ + + (void) mkdir_parents(fn, 0755); + r = journal_file_open(fn, O_RDWR|O_CREAT, 0640, NULL, &s->runtime_journal); + free(fn); + + if (r < 0) { + log_error("Failed to open runtime journal: %s", strerror(-r)); + return r; + } + } + + if (s->runtime_journal) { + journal_default_metrics(&s->runtime_metrics, s->runtime_journal->fd); + + s->runtime_journal->metrics = s->runtime_metrics; + s->runtime_journal->compress = s->compress; + + server_fix_perms(s, s->runtime_journal, 0); + } + } + + return r; +} + +static int server_flush_to_var(Server *s) { + char path[] = "/run/log/journal/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; + Object *o = NULL; + int r; + sd_id128_t machine; + sd_journal *j; + usec_t ts; + + assert(s); + + if (!s->runtime_journal) + return 0; + + ts = now(CLOCK_MONOTONIC); + if (s->var_available_timestamp + RECHECK_VAR_AVAILABLE_USEC > ts) + return 0; + + s->var_available_timestamp = ts; + + system_journal_open(s); + + if (!s->system_journal) + return 0; + + log_info("Flushing to /var..."); + + r = sd_id128_get_machine(&machine); + if (r < 0) { + log_error("Failed to get machine id: %s", strerror(-r)); + return r; + } + + r = sd_journal_open(&j, SD_JOURNAL_RUNTIME_ONLY); + if (r < 0) { + log_error("Failed to read runtime journal: %s", strerror(-r)); + return r; + } + + SD_JOURNAL_FOREACH(j) { + JournalFile *f; + + f = j->current_file; + assert(f && f->current_offset > 0); + + r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o); + if (r < 0) { + log_error("Can't read entry: %s", strerror(-r)); + goto finish; + } + + r = journal_file_copy_entry(f, s->system_journal, o, f->current_offset, NULL, NULL, NULL); + if (r == -E2BIG) { + log_info("Allocation limit reached."); + + journal_file_post_change(s->system_journal); + server_rotate(s); + server_vacuum(s); + + r = journal_file_copy_entry(f, s->system_journal, o, f->current_offset, NULL, NULL, NULL); + } + + if (r < 0) { + log_error("Can't write entry: %s", strerror(-r)); + goto finish; + } + } + +finish: + journal_file_post_change(s->system_journal); + + journal_file_close(s->runtime_journal); + s->runtime_journal = NULL; + + if (r >= 0) { + sd_id128_to_string(machine, path + 17); + rm_rf(path, false, true, false); + } + + return r; +} + +static int server_read_proc_kmsg(Server *s) { + ssize_t l; + assert(s); + assert(s->proc_kmsg_fd >= 0); + + l = read(s->proc_kmsg_fd, s->proc_kmsg_buffer + s->proc_kmsg_length, sizeof(s->proc_kmsg_buffer) - 1 - s->proc_kmsg_length); + if (l < 0) { + + if (errno == EAGAIN || errno == EINTR) + return 0; + + log_error("Failed to read from kernel: %m"); + return -errno; + } + + s->proc_kmsg_length += l; + + proc_kmsg_scan(s); + return 1; +} + +static int server_flush_proc_kmsg(Server *s) { + int r; + + assert(s); + + if (s->proc_kmsg_fd < 0) + return 0; + + log_info("Flushing /proc/kmsg..."); + + for (;;) { + r = server_read_proc_kmsg(s); + if (r < 0) + return r; + + if (r == 0) + break; + } + + return 0; +} + +static int process_event(Server *s, struct epoll_event *ev) { + assert(s); + + if (ev->data.fd == s->signal_fd) { + struct signalfd_siginfo sfsi; + ssize_t n; + + if (ev->events != EPOLLIN) { + log_info("Got invalid event from epoll."); + return -EIO; + } + + n = read(s->signal_fd, &sfsi, sizeof(sfsi)); + if (n != sizeof(sfsi)) { + + if (n >= 0) + return -EIO; + + if (errno == EINTR || errno == EAGAIN) + return 1; + + return -errno; + } + + if (sfsi.ssi_signo == SIGUSR1) { + server_flush_to_var(s); + return 0; + } + + log_debug("Received SIG%s", signal_to_string(sfsi.ssi_signo)); + return 0; + + } else if (ev->data.fd == s->proc_kmsg_fd) { + int r; + + if (ev->events != EPOLLIN) { + log_info("Got invalid event from epoll."); + return -EIO; + } + + r = server_read_proc_kmsg(s); + if (r < 0) + return r; + + return 1; + + } else if (ev->data.fd == s->native_fd || + ev->data.fd == s->syslog_fd) { + + if (ev->events != EPOLLIN) { + log_info("Got invalid event from epoll."); + return -EIO; + } + + for (;;) { + struct msghdr msghdr; + struct iovec iovec; + struct ucred *ucred = NULL; + struct timeval *tv = NULL; + struct cmsghdr *cmsg; + union { + struct cmsghdr cmsghdr; + uint8_t buf[CMSG_SPACE(sizeof(struct ucred)) + + CMSG_SPACE(sizeof(struct timeval)) + + CMSG_SPACE(sizeof(int))]; + } control; + ssize_t n; + int v; + int *fds = NULL; + unsigned n_fds = 0; + + if (ioctl(ev->data.fd, SIOCINQ, &v) < 0) { + log_error("SIOCINQ failed: %m"); + return -errno; + } + + if (s->buffer_size < (size_t) v) { + void *b; + size_t l; + + l = MAX(LINE_MAX + (size_t) v, s->buffer_size * 2); + b = realloc(s->buffer, l+1); + + if (!b) { + log_error("Couldn't increase buffer."); + return -ENOMEM; + } + + s->buffer_size = l; + s->buffer = b; + } + + zero(iovec); + iovec.iov_base = s->buffer; + iovec.iov_len = s->buffer_size; + + zero(control); + zero(msghdr); + msghdr.msg_iov = &iovec; + msghdr.msg_iovlen = 1; + msghdr.msg_control = &control; + msghdr.msg_controllen = sizeof(control); + + n = recvmsg(ev->data.fd, &msghdr, MSG_DONTWAIT|MSG_CMSG_CLOEXEC); + if (n < 0) { + + if (errno == EINTR || errno == EAGAIN) + return 1; + + log_error("recvmsg() failed: %m"); + return -errno; + } + + for (cmsg = CMSG_FIRSTHDR(&msghdr); cmsg; cmsg = CMSG_NXTHDR(&msghdr, cmsg)) { + + if (cmsg->cmsg_level == SOL_SOCKET && + cmsg->cmsg_type == SCM_CREDENTIALS && + cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred))) + ucred = (struct ucred*) CMSG_DATA(cmsg); + else if (cmsg->cmsg_level == SOL_SOCKET && + cmsg->cmsg_type == SO_TIMESTAMP && + cmsg->cmsg_len == CMSG_LEN(sizeof(struct timeval))) + tv = (struct timeval*) CMSG_DATA(cmsg); + else if (cmsg->cmsg_level == SOL_SOCKET && + cmsg->cmsg_type == SCM_RIGHTS) { + fds = (int*) CMSG_DATA(cmsg); + n_fds = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int); + } + } + + if (ev->data.fd == s->syslog_fd) { + char *e; + + if (n > 0 && n_fds == 0) { + e = memchr(s->buffer, '\n', n); + if (e) + *e = 0; + else + s->buffer[n] = 0; + + process_syslog_message(s, strstrip(s->buffer), ucred, tv); + } else if (n_fds > 0) + log_warning("Got file descriptors via syslog socket. Ignoring."); + + } else { + if (n > 0 && n_fds == 0) + process_native_message(s, s->buffer, n, ucred, tv); + else if (n == 0 && n_fds == 1) + process_native_file(s, fds[0], ucred, tv); + else if (n_fds > 0) + log_warning("Got too many file descriptors via native socket. Ignoring."); + } + + close_many(fds, n_fds); + } + + return 1; + + } else if (ev->data.fd == s->stdout_fd) { + + if (ev->events != EPOLLIN) { + log_info("Got invalid event from epoll."); + return -EIO; + } + + stdout_stream_new(s); + return 1; + + } else { + StdoutStream *stream; + + if ((ev->events|EPOLLIN|EPOLLHUP) != (EPOLLIN|EPOLLHUP)) { + log_info("Got invalid event from epoll."); + return -EIO; + } + + /* If it is none of the well-known fds, it must be an + * stdout stream fd. Note that this is a bit ugly here + * (since we rely that none of the well-known fds + * could be interpreted as pointer), but nonetheless + * safe, since the well-known fds would never get an + * fd > 4096, i.e. beyond the first memory page */ + + stream = ev->data.ptr; + + if (stdout_stream_process(stream) <= 0) + stdout_stream_free(stream); + + return 1; + } + + log_error("Unknown event."); + return 0; +} + +static int open_syslog_socket(Server *s) { + union sockaddr_union sa; + int one, r; + struct epoll_event ev; + + assert(s); + + if (s->syslog_fd < 0) { + + s->syslog_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); + if (s->syslog_fd < 0) { + log_error("socket() failed: %m"); + return -errno; + } + + zero(sa); + sa.un.sun_family = AF_UNIX; + strncpy(sa.un.sun_path, "/dev/log", sizeof(sa.un.sun_path)); + + unlink(sa.un.sun_path); + + r = bind(s->syslog_fd, &sa.sa, offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path)); + if (r < 0) { + log_error("bind() failed: %m"); + return -errno; + } + + chmod(sa.un.sun_path, 0666); + } else + fd_nonblock(s->syslog_fd, 1); + + one = 1; + r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)); + if (r < 0) { + log_error("SO_PASSCRED failed: %m"); + return -errno; + } + + one = 1; + r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_TIMESTAMP, &one, sizeof(one)); + if (r < 0) { + log_error("SO_TIMESTAMP failed: %m"); + return -errno; + } + + zero(ev); + ev.events = EPOLLIN; + ev.data.fd = s->syslog_fd; + if (epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, s->syslog_fd, &ev) < 0) { + log_error("Failed to add syslog server fd to epoll object: %m"); + return -errno; + } + + return 0; +} + +static int open_native_socket(Server*s) { + union sockaddr_union sa; + int one, r; + struct epoll_event ev; + + assert(s); + + if (s->native_fd < 0) { + + s->native_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); + if (s->native_fd < 0) { + log_error("socket() failed: %m"); + return -errno; + } + + zero(sa); + sa.un.sun_family = AF_UNIX; + strncpy(sa.un.sun_path, "/run/systemd/journal/socket", sizeof(sa.un.sun_path)); + + unlink(sa.un.sun_path); + + r = bind(s->native_fd, &sa.sa, offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path)); + if (r < 0) { + log_error("bind() failed: %m"); + return -errno; + } + + chmod(sa.un.sun_path, 0666); + } else + fd_nonblock(s->native_fd, 1); + + one = 1; + r = setsockopt(s->native_fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)); + if (r < 0) { + log_error("SO_PASSCRED failed: %m"); + return -errno; + } + + one = 1; + r = setsockopt(s->native_fd, SOL_SOCKET, SO_TIMESTAMP, &one, sizeof(one)); + if (r < 0) { + log_error("SO_TIMESTAMP failed: %m"); + return -errno; + } + + zero(ev); + ev.events = EPOLLIN; + ev.data.fd = s->native_fd; + if (epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, s->native_fd, &ev) < 0) { + log_error("Failed to add native server fd to epoll object: %m"); + return -errno; + } + + return 0; +} + +static int open_stdout_socket(Server *s) { + union sockaddr_union sa; + int r; + struct epoll_event ev; + + assert(s); + + if (s->stdout_fd < 0) { + + s->stdout_fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); + if (s->stdout_fd < 0) { + log_error("socket() failed: %m"); + return -errno; + } + + zero(sa); + sa.un.sun_family = AF_UNIX; + strncpy(sa.un.sun_path, "/run/systemd/journal/stdout", sizeof(sa.un.sun_path)); + + unlink(sa.un.sun_path); + + r = bind(s->stdout_fd, &sa.sa, offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path)); + if (r < 0) { + log_error("bind() failed: %m"); + return -errno; + } + + chmod(sa.un.sun_path, 0666); + + if (listen(s->stdout_fd, SOMAXCONN) < 0) { + log_error("liste() failed: %m"); + return -errno; + } + } else + fd_nonblock(s->stdout_fd, 1); + + zero(ev); + ev.events = EPOLLIN; + ev.data.fd = s->stdout_fd; + if (epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, s->stdout_fd, &ev) < 0) { + log_error("Failed to add stdout server fd to epoll object: %m"); + return -errno; + } + + return 0; +} + +static int open_proc_kmsg(Server *s) { + struct epoll_event ev; + + assert(s); + + if (!s->import_proc_kmsg) + return 0; + + + s->proc_kmsg_fd = open("/proc/kmsg", O_CLOEXEC|O_NONBLOCK|O_NOCTTY); + if (s->proc_kmsg_fd < 0) { + log_warning("Failed to open /proc/kmsg, ignoring: %m"); + return 0; + } + + zero(ev); + ev.events = EPOLLIN; + ev.data.fd = s->proc_kmsg_fd; + if (epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, s->proc_kmsg_fd, &ev) < 0) { + log_error("Failed to add /proc/kmsg fd to epoll object: %m"); + return -errno; + } + + return 0; +} + +static int open_signalfd(Server *s) { + sigset_t mask; + struct epoll_event ev; + + assert(s); + + assert_se(sigemptyset(&mask) == 0); + sigset_add_many(&mask, SIGINT, SIGTERM, SIGUSR1, -1); + assert_se(sigprocmask(SIG_SETMASK, &mask, NULL) == 0); + + s->signal_fd = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC); + if (s->signal_fd < 0) { + log_error("signalfd(): %m"); + return -errno; + } + + zero(ev); + ev.events = EPOLLIN; + ev.data.fd = s->signal_fd; + + if (epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, s->signal_fd, &ev) < 0) { + log_error("epoll_ctl(): %m"); + return -errno; + } + + return 0; +} + +static int server_parse_proc_cmdline(Server *s) { + char *line, *w, *state; + int r; + size_t l; + + if (detect_container(NULL) > 0) + return 0; + + r = read_one_line_file("/proc/cmdline", &line); + if (r < 0) { + log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r)); + return 0; + } + + FOREACH_WORD_QUOTED(w, l, line, state) { + char *word; + + word = strndup(w, l); + if (!word) { + r = -ENOMEM; + goto finish; + } + + if (startswith(word, "systemd_journald.forward_to_syslog=")) { + r = parse_boolean(word + 35); + if (r < 0) + log_warning("Failed to parse forward to syslog switch %s. Ignoring.", word + 35); + else + s->forward_to_syslog = r; + } else if (startswith(word, "systemd_journald.forward_to_kmsg=")) { + r = parse_boolean(word + 33); + if (r < 0) + log_warning("Failed to parse forward to kmsg switch %s. Ignoring.", word + 33); + else + s->forward_to_kmsg = r; + } else if (startswith(word, "systemd_journald.forward_to_console=")) { + r = parse_boolean(word + 36); + if (r < 0) + log_warning("Failed to parse forward to console switch %s. Ignoring.", word + 36); + else + s->forward_to_console = r; + } + + free(word); + } + + r = 0; + +finish: + free(line); + return r; +} + +static int server_parse_config_file(Server *s) { + FILE *f; + const char *fn; + int r; + + assert(s); + + fn = "/etc/systemd/systemd-journald.conf"; + f = fopen(fn, "re"); + if (!f) { + if (errno == ENOENT) + return 0; + + log_warning("Failed to open configuration file %s: %m", fn); + return -errno; + } + + r = config_parse(fn, f, "Journal\0", config_item_perf_lookup, (void*) journald_gperf_lookup, false, s); + if (r < 0) + log_warning("Failed to parse configuration file: %s", strerror(-r)); + + fclose(f); + + return r; +} + +static int server_init(Server *s) { + int n, r, fd; + + assert(s); + + zero(*s); + s->syslog_fd = s->native_fd = s->stdout_fd = s->signal_fd = s->epoll_fd = s->proc_kmsg_fd = -1; + s->compress = true; + + s->rate_limit_interval = DEFAULT_RATE_LIMIT_INTERVAL; + s->rate_limit_burst = DEFAULT_RATE_LIMIT_BURST; + + s->forward_to_syslog = true; + s->import_proc_kmsg = true; + + memset(&s->system_metrics, 0xFF, sizeof(s->system_metrics)); + memset(&s->runtime_metrics, 0xFF, sizeof(s->runtime_metrics)); + + server_parse_config_file(s); + server_parse_proc_cmdline(s); + + s->user_journals = hashmap_new(trivial_hash_func, trivial_compare_func); + if (!s->user_journals) { + log_error("Out of memory."); + return -ENOMEM; + } + + s->epoll_fd = epoll_create1(EPOLL_CLOEXEC); + if (s->epoll_fd < 0) { + log_error("Failed to create epoll object: %m"); + return -errno; + } + + n = sd_listen_fds(true); + if (n < 0) { + log_error("Failed to read listening file descriptors from environment: %s", strerror(-n)); + return n; + } + + for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd++) { + + if (sd_is_socket_unix(fd, SOCK_DGRAM, -1, "/run/systemd/journal/socket", 0) > 0) { + + if (s->native_fd >= 0) { + log_error("Too many native sockets passed."); + return -EINVAL; + } + + s->native_fd = fd; + + } else if (sd_is_socket_unix(fd, SOCK_STREAM, 1, "/run/systemd/journal/stdout", 0) > 0) { + + if (s->stdout_fd >= 0) { + log_error("Too many stdout sockets passed."); + return -EINVAL; + } + + s->stdout_fd = fd; + + } else if (sd_is_socket_unix(fd, SOCK_DGRAM, -1, "/dev/log", 0) > 0) { + + if (s->syslog_fd >= 0) { + log_error("Too many /dev/log sockets passed."); + return -EINVAL; + } + + s->syslog_fd = fd; + + } else { + log_error("Unknown socket passed."); + return -EINVAL; + } + } + + r = open_syslog_socket(s); + if (r < 0) + return r; + + r = open_native_socket(s); + if (r < 0) + return r; + + r = open_stdout_socket(s); + if (r < 0) + return r; + + r = open_proc_kmsg(s); + if (r < 0) + return r; + + r = system_journal_open(s); + if (r < 0) + return r; + + r = open_signalfd(s); + if (r < 0) + return r; + + s->rate_limit = journal_rate_limit_new(s->rate_limit_interval, s->rate_limit_burst); + if (!s->rate_limit) + return -ENOMEM; + + return 0; +} + +static void server_done(Server *s) { + JournalFile *f; + assert(s); + + while (s->stdout_streams) + stdout_stream_free(s->stdout_streams); + + if (s->system_journal) + journal_file_close(s->system_journal); + + if (s->runtime_journal) + journal_file_close(s->runtime_journal); + + while ((f = hashmap_steal_first(s->user_journals))) + journal_file_close(f); + + hashmap_free(s->user_journals); + + if (s->epoll_fd >= 0) + close_nointr_nofail(s->epoll_fd); + + if (s->signal_fd >= 0) + close_nointr_nofail(s->signal_fd); + + if (s->syslog_fd >= 0) + close_nointr_nofail(s->syslog_fd); + + if (s->native_fd >= 0) + close_nointr_nofail(s->native_fd); + + if (s->stdout_fd >= 0) + close_nointr_nofail(s->stdout_fd); + + if (s->proc_kmsg_fd >= 0) + close_nointr_nofail(s->proc_kmsg_fd); + + if (s->rate_limit) + journal_rate_limit_free(s->rate_limit); + + free(s->buffer); +} + +int main(int argc, char *argv[]) { + Server server; + int r; + + /* if (getppid() != 1) { */ + /* log_error("This program should be invoked by init only."); */ + /* return EXIT_FAILURE; */ + /* } */ + + if (argc > 1) { + log_error("This program does not take arguments."); + return EXIT_FAILURE; + } + + log_set_target(LOG_TARGET_CONSOLE); + log_parse_environment(); + log_open(); + + umask(0022); + + r = server_init(&server); + if (r < 0) + goto finish; + + server_vacuum(&server); + server_flush_to_var(&server); + server_flush_proc_kmsg(&server); + + log_debug("systemd-journald running as pid %lu", (unsigned long) getpid()); + driver_message(&server, SD_MESSAGE_JOURNAL_START, "Journal started"); + + sd_notify(false, + "READY=1\n" + "STATUS=Processing requests..."); + + for (;;) { + struct epoll_event event; + + r = epoll_wait(server.epoll_fd, &event, 1, -1); + if (r < 0) { + + if (errno == EINTR) + continue; + + log_error("epoll_wait() failed: %m"); + r = -errno; + goto finish; + } else if (r == 0) + break; + + r = process_event(&server, &event); + if (r < 0) + goto finish; + else if (r == 0) + break; + } + + log_debug("systemd-journald stopped as pid %lu", (unsigned long) getpid()); + driver_message(&server, SD_MESSAGE_JOURNAL_STOP, "Journal stopped"); + +finish: + sd_notify(false, + "STATUS=Shutting down..."); + + server_done(&server); + + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/journal/journald.h b/src/journal/journald.h new file mode 100644 index 0000000000..6160991ed8 --- /dev/null +++ b/src/journal/journald.h @@ -0,0 +1,86 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#ifndef foojournaldhfoo +#define foojournaldhfoo + +/*** + This file is part of systemd. + + Copyright 2011 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 <inttypes.h> +#include <sys/types.h> +#include <stdbool.h> + +#include "journal-file.h" +#include "hashmap.h" +#include "util.h" +#include "journal-rate-limit.h" +#include "list.h" + +typedef struct StdoutStream StdoutStream; + +typedef struct Server { + int epoll_fd; + int signal_fd; + int syslog_fd; + int native_fd; + int stdout_fd; + int proc_kmsg_fd; + + JournalFile *runtime_journal; + JournalFile *system_journal; + Hashmap *user_journals; + + uint64_t seqnum; + + char *buffer; + size_t buffer_size; + + JournalRateLimit *rate_limit; + usec_t rate_limit_interval; + unsigned rate_limit_burst; + + JournalMetrics runtime_metrics; + JournalMetrics system_metrics; + + bool compress; + + bool forward_to_kmsg; + bool forward_to_syslog; + bool forward_to_console; + + bool import_proc_kmsg; + char proc_kmsg_buffer[LINE_MAX+1]; + size_t proc_kmsg_length; + + uint64_t cached_available_space; + usec_t cached_available_space_timestamp; + + uint64_t var_available_timestamp; + + gid_t file_gid; + bool file_gid_valid; + + LIST_HEAD(StdoutStream, stdout_streams); + unsigned n_stdout_streams; +} Server; + +/* gperf lookup function */ +const struct ConfigPerfItem* journald_gperf_lookup(const char *key, unsigned length); + +#endif diff --git a/src/journal/libsystemd-journal.pc.in b/src/journal/libsystemd-journal.pc.in new file mode 100644 index 0000000000..13cc8208df --- /dev/null +++ b/src/journal/libsystemd-journal.pc.in @@ -0,0 +1,19 @@ +# 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. + +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: systemd +Description: systemd Journal Utility Library +URL: @PACKAGE_URL@ +Version: @PACKAGE_VERSION@ +Requires: libsystemd-id128 = @PACKAGE_VERSION@ +Libs: -L${libdir} -lsystemd-journal +Cflags: -I${includedir} diff --git a/src/journal/libsystemd-journal.sym b/src/journal/libsystemd-journal.sym new file mode 100644 index 0000000000..7653880e8f --- /dev/null +++ b/src/journal/libsystemd-journal.sym @@ -0,0 +1,45 @@ +/*** + 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. +***/ + +/* Original symbols from systemd v38 */ + +LIBSYSTEMD_JOURNAL_38 { +global: + sd_journal_print; + sd_journal_printv; + sd_journal_send; + sd_journal_sendv; + sd_journal_stream_fd; + sd_journal_open; + sd_journal_close; + sd_journal_previous; + sd_journal_next; + sd_journal_previous_skip; + sd_journal_next_skip; + sd_journal_get_realtime_usec; + sd_journal_get_monotonic_usec; + sd_journal_get_data; + sd_journal_enumerate_data; + sd_journal_restart_data; + sd_journal_add_match; + sd_journal_flush_matches; + sd_journal_seek_head; + sd_journal_seek_tail; + sd_journal_seek_monotonic_usec; + sd_journal_seek_realtime_usec; + sd_journal_seek_cursor; + sd_journal_get_cursor; + sd_journal_query_unique; + sd_journal_enumerate_unique; + sd_journal_restart_unique; + sd_journal_get_fd; + sd_journal_process; +local: + *; +}; diff --git a/src/journal/lookup3.c b/src/journal/lookup3.c new file mode 100644 index 0000000000..b90093a5e2 --- /dev/null +++ b/src/journal/lookup3.c @@ -0,0 +1,1003 @@ +/* Slightly modified by Lennart Poettering, to avoid name clashes, and + * unexport a few functions. */ + +#include "lookup3.h" + +/* +------------------------------------------------------------------------------- +lookup3.c, by Bob Jenkins, May 2006, Public Domain. + +These are functions for producing 32-bit hashes for hash table lookup. +hashword(), hashlittle(), hashlittle2(), hashbig(), mix(), and final() +are externally useful functions. Routines to test the hash are included +if SELF_TEST is defined. You can use this free for any purpose. It's in +the public domain. It has no warranty. + +You probably want to use hashlittle(). hashlittle() and hashbig() +hash byte arrays. hashlittle() is is faster than hashbig() on +little-endian machines. Intel and AMD are little-endian machines. +On second thought, you probably want hashlittle2(), which is identical to +hashlittle() except it returns two 32-bit hashes for the price of one. +You could implement hashbig2() if you wanted but I haven't bothered here. + +If you want to find a hash of, say, exactly 7 integers, do + a = i1; b = i2; c = i3; + mix(a,b,c); + a += i4; b += i5; c += i6; + mix(a,b,c); + a += i7; + final(a,b,c); +then use c as the hash value. If you have a variable length array of +4-byte integers to hash, use hashword(). If you have a byte array (like +a character string), use hashlittle(). If you have several byte arrays, or +a mix of things, see the comments above hashlittle(). + +Why is this so big? I read 12 bytes at a time into 3 4-byte integers, +then mix those integers. This is fast (you can do a lot more thorough +mixing with 12*3 instructions on 3 integers than you can with 3 instructions +on 1 byte), but shoehorning those bytes into integers efficiently is messy. +------------------------------------------------------------------------------- +*/ +/* #define SELF_TEST 1 */ + +#include <stdio.h> /* defines printf for tests */ +#include <time.h> /* defines time_t for timings in the test */ +#include <stdint.h> /* defines uint32_t etc */ +#include <sys/param.h> /* attempt to define endianness */ +#ifdef linux +# include <endian.h> /* attempt to define endianness */ +#endif + +/* + * My best guess at if you are big-endian or little-endian. This may + * need adjustment. + */ +#if (defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && \ + __BYTE_ORDER == __LITTLE_ENDIAN) || \ + (defined(i386) || defined(__i386__) || defined(__i486__) || \ + defined(__i586__) || defined(__i686__) || defined(vax) || defined(MIPSEL)) +# define HASH_LITTLE_ENDIAN 1 +# define HASH_BIG_ENDIAN 0 +#elif (defined(__BYTE_ORDER) && defined(__BIG_ENDIAN) && \ + __BYTE_ORDER == __BIG_ENDIAN) || \ + (defined(sparc) || defined(POWERPC) || defined(mc68000) || defined(sel)) +# define HASH_LITTLE_ENDIAN 0 +# define HASH_BIG_ENDIAN 1 +#else +# define HASH_LITTLE_ENDIAN 0 +# define HASH_BIG_ENDIAN 0 +#endif + +#define hashsize(n) ((uint32_t)1<<(n)) +#define hashmask(n) (hashsize(n)-1) +#define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k)))) + +/* +------------------------------------------------------------------------------- +mix -- mix 3 32-bit values reversibly. + +This is reversible, so any information in (a,b,c) before mix() is +still in (a,b,c) after mix(). + +If four pairs of (a,b,c) inputs are run through mix(), or through +mix() in reverse, there are at least 32 bits of the output that +are sometimes the same for one pair and different for another pair. +This was tested for: +* pairs that differed by one bit, by two bits, in any combination + of top bits of (a,b,c), or in any combination of bottom bits of + (a,b,c). +* "differ" is defined as +, -, ^, or ~^. For + and -, I transformed + the output delta to a Gray code (a^(a>>1)) so a string of 1's (as + is commonly produced by subtraction) look like a single 1-bit + difference. +* the base values were pseudorandom, all zero but one bit set, or + all zero plus a counter that starts at zero. + +Some k values for my "a-=c; a^=rot(c,k); c+=b;" arrangement that +satisfy this are + 4 6 8 16 19 4 + 9 15 3 18 27 15 + 14 9 3 7 17 3 +Well, "9 15 3 18 27 15" didn't quite get 32 bits diffing +for "differ" defined as + with a one-bit base and a two-bit delta. I +used http://burtleburtle.net/bob/hash/avalanche.html to choose +the operations, constants, and arrangements of the variables. + +This does not achieve avalanche. There are input bits of (a,b,c) +that fail to affect some output bits of (a,b,c), especially of a. The +most thoroughly mixed value is c, but it doesn't really even achieve +avalanche in c. + +This allows some parallelism. Read-after-writes are good at doubling +the number of bits affected, so the goal of mixing pulls in the opposite +direction as the goal of parallelism. I did what I could. Rotates +seem to cost as much as shifts on every machine I could lay my hands +on, and rotates are much kinder to the top and bottom bits, so I used +rotates. +------------------------------------------------------------------------------- +*/ +#define mix(a,b,c) \ +{ \ + a -= c; a ^= rot(c, 4); c += b; \ + b -= a; b ^= rot(a, 6); a += c; \ + c -= b; c ^= rot(b, 8); b += a; \ + a -= c; a ^= rot(c,16); c += b; \ + b -= a; b ^= rot(a,19); a += c; \ + c -= b; c ^= rot(b, 4); b += a; \ +} + +/* +------------------------------------------------------------------------------- +final -- final mixing of 3 32-bit values (a,b,c) into c + +Pairs of (a,b,c) values differing in only a few bits will usually +produce values of c that look totally different. This was tested for +* pairs that differed by one bit, by two bits, in any combination + of top bits of (a,b,c), or in any combination of bottom bits of + (a,b,c). +* "differ" is defined as +, -, ^, or ~^. For + and -, I transformed + the output delta to a Gray code (a^(a>>1)) so a string of 1's (as + is commonly produced by subtraction) look like a single 1-bit + difference. +* the base values were pseudorandom, all zero but one bit set, or + all zero plus a counter that starts at zero. + +These constants passed: + 14 11 25 16 4 14 24 + 12 14 25 16 4 14 24 +and these came close: + 4 8 15 26 3 22 24 + 10 8 15 26 3 22 24 + 11 8 15 26 3 22 24 +------------------------------------------------------------------------------- +*/ +#define final(a,b,c) \ +{ \ + c ^= b; c -= rot(b,14); \ + a ^= c; a -= rot(c,11); \ + b ^= a; b -= rot(a,25); \ + c ^= b; c -= rot(b,16); \ + a ^= c; a -= rot(c,4); \ + b ^= a; b -= rot(a,14); \ + c ^= b; c -= rot(b,24); \ +} + +/* +-------------------------------------------------------------------- + This works on all machines. To be useful, it requires + -- that the key be an array of uint32_t's, and + -- that the length be the number of uint32_t's in the key + + The function hashword() is identical to hashlittle() on little-endian + machines, and identical to hashbig() on big-endian machines, + except that the length has to be measured in uint32_ts rather than in + bytes. hashlittle() is more complicated than hashword() only because + hashlittle() has to dance around fitting the key bytes into registers. +-------------------------------------------------------------------- +*/ +uint32_t jenkins_hashword( +const uint32_t *k, /* the key, an array of uint32_t values */ +size_t length, /* the length of the key, in uint32_ts */ +uint32_t initval) /* the previous hash, or an arbitrary value */ +{ + uint32_t a,b,c; + + /* Set up the internal state */ + a = b = c = 0xdeadbeef + (((uint32_t)length)<<2) + initval; + + /*------------------------------------------------- handle most of the key */ + while (length > 3) + { + a += k[0]; + b += k[1]; + c += k[2]; + mix(a,b,c); + length -= 3; + k += 3; + } + + /*------------------------------------------- handle the last 3 uint32_t's */ + switch(length) /* all the case statements fall through */ + { + case 3 : c+=k[2]; + case 2 : b+=k[1]; + case 1 : a+=k[0]; + final(a,b,c); + case 0: /* case 0: nothing left to add */ + break; + } + /*------------------------------------------------------ report the result */ + return c; +} + + +/* +-------------------------------------------------------------------- +hashword2() -- same as hashword(), but take two seeds and return two +32-bit values. pc and pb must both be nonnull, and *pc and *pb must +both be initialized with seeds. If you pass in (*pb)==0, the output +(*pc) will be the same as the return value from hashword(). +-------------------------------------------------------------------- +*/ +void jenkins_hashword2 ( +const uint32_t *k, /* the key, an array of uint32_t values */ +size_t length, /* the length of the key, in uint32_ts */ +uint32_t *pc, /* IN: seed OUT: primary hash value */ +uint32_t *pb) /* IN: more seed OUT: secondary hash value */ +{ + uint32_t a,b,c; + + /* Set up the internal state */ + a = b = c = 0xdeadbeef + ((uint32_t)(length<<2)) + *pc; + c += *pb; + + /*------------------------------------------------- handle most of the key */ + while (length > 3) + { + a += k[0]; + b += k[1]; + c += k[2]; + mix(a,b,c); + length -= 3; + k += 3; + } + + /*------------------------------------------- handle the last 3 uint32_t's */ + switch(length) /* all the case statements fall through */ + { + case 3 : c+=k[2]; + case 2 : b+=k[1]; + case 1 : a+=k[0]; + final(a,b,c); + case 0: /* case 0: nothing left to add */ + break; + } + /*------------------------------------------------------ report the result */ + *pc=c; *pb=b; +} + + +/* +------------------------------------------------------------------------------- +hashlittle() -- hash a variable-length key into a 32-bit value + k : the key (the unaligned variable-length array of bytes) + length : the length of the key, counting by bytes + initval : can be any 4-byte value +Returns a 32-bit value. Every bit of the key affects every bit of +the return value. Two keys differing by one or two bits will have +totally different hash values. + +The best hash table sizes are powers of 2. There is no need to do +mod a prime (mod is sooo slow!). If you need less than 32 bits, +use a bitmask. For example, if you need only 10 bits, do + h = (h & hashmask(10)); +In which case, the hash table should have hashsize(10) elements. + +If you are hashing n strings (uint8_t **)k, do it like this: + for (i=0, h=0; i<n; ++i) h = hashlittle( k[i], len[i], h); + +By Bob Jenkins, 2006. bob_jenkins@burtleburtle.net. You may use this +code any way you wish, private, educational, or commercial. It's free. + +Use for hash table lookup, or anything where one collision in 2^^32 is +acceptable. Do NOT use for cryptographic purposes. +------------------------------------------------------------------------------- +*/ + +uint32_t jenkins_hashlittle( const void *key, size_t length, uint32_t initval) +{ + uint32_t a,b,c; /* internal state */ + union { const void *ptr; size_t i; } u; /* needed for Mac Powerbook G4 */ + + /* Set up the internal state */ + a = b = c = 0xdeadbeef + ((uint32_t)length) + initval; + + u.ptr = key; + if (HASH_LITTLE_ENDIAN && ((u.i & 0x3) == 0)) { + const uint32_t *k = (const uint32_t *)key; /* read 32-bit chunks */ + + /*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */ + while (length > 12) + { + a += k[0]; + b += k[1]; + c += k[2]; + mix(a,b,c); + length -= 12; + k += 3; + } + + /*----------------------------- handle the last (probably partial) block */ + /* + * "k[2]&0xffffff" actually reads beyond the end of the string, but + * then masks off the part it's not allowed to read. Because the + * string is aligned, the masked-off tail is in the same word as the + * rest of the string. Every machine with memory protection I've seen + * does it on word boundaries, so is OK with this. But VALGRIND will + * still catch it and complain. The masking trick does make the hash + * noticably faster for short strings (like English words). + */ +#ifndef VALGRIND + + switch(length) + { + case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; + case 11: c+=k[2]&0xffffff; b+=k[1]; a+=k[0]; break; + case 10: c+=k[2]&0xffff; b+=k[1]; a+=k[0]; break; + case 9 : c+=k[2]&0xff; b+=k[1]; a+=k[0]; break; + case 8 : b+=k[1]; a+=k[0]; break; + case 7 : b+=k[1]&0xffffff; a+=k[0]; break; + case 6 : b+=k[1]&0xffff; a+=k[0]; break; + case 5 : b+=k[1]&0xff; a+=k[0]; break; + case 4 : a+=k[0]; break; + case 3 : a+=k[0]&0xffffff; break; + case 2 : a+=k[0]&0xffff; break; + case 1 : a+=k[0]&0xff; break; + case 0 : return c; /* zero length strings require no mixing */ + } + +#else /* make valgrind happy */ + + k8 = (const uint8_t *)k; + switch(length) + { + case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; + case 11: c+=((uint32_t)k8[10])<<16; /* fall through */ + case 10: c+=((uint32_t)k8[9])<<8; /* fall through */ + case 9 : c+=k8[8]; /* fall through */ + case 8 : b+=k[1]; a+=k[0]; break; + case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */ + case 6 : b+=((uint32_t)k8[5])<<8; /* fall through */ + case 5 : b+=k8[4]; /* fall through */ + case 4 : a+=k[0]; break; + case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */ + case 2 : a+=((uint32_t)k8[1])<<8; /* fall through */ + case 1 : a+=k8[0]; break; + case 0 : return c; + } + +#endif /* !valgrind */ + + } else if (HASH_LITTLE_ENDIAN && ((u.i & 0x1) == 0)) { + const uint16_t *k = (const uint16_t *)key; /* read 16-bit chunks */ + const uint8_t *k8; + + /*--------------- all but last block: aligned reads and different mixing */ + while (length > 12) + { + a += k[0] + (((uint32_t)k[1])<<16); + b += k[2] + (((uint32_t)k[3])<<16); + c += k[4] + (((uint32_t)k[5])<<16); + mix(a,b,c); + length -= 12; + k += 6; + } + + /*----------------------------- handle the last (probably partial) block */ + k8 = (const uint8_t *)k; + switch(length) + { + case 12: c+=k[4]+(((uint32_t)k[5])<<16); + b+=k[2]+(((uint32_t)k[3])<<16); + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 11: c+=((uint32_t)k8[10])<<16; /* fall through */ + case 10: c+=k[4]; + b+=k[2]+(((uint32_t)k[3])<<16); + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 9 : c+=k8[8]; /* fall through */ + case 8 : b+=k[2]+(((uint32_t)k[3])<<16); + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */ + case 6 : b+=k[2]; + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 5 : b+=k8[4]; /* fall through */ + case 4 : a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */ + case 2 : a+=k[0]; + break; + case 1 : a+=k8[0]; + break; + case 0 : return c; /* zero length requires no mixing */ + } + + } else { /* need to read the key one byte at a time */ + const uint8_t *k = (const uint8_t *)key; + + /*--------------- all but the last block: affect some 32 bits of (a,b,c) */ + while (length > 12) + { + a += k[0]; + a += ((uint32_t)k[1])<<8; + a += ((uint32_t)k[2])<<16; + a += ((uint32_t)k[3])<<24; + b += k[4]; + b += ((uint32_t)k[5])<<8; + b += ((uint32_t)k[6])<<16; + b += ((uint32_t)k[7])<<24; + c += k[8]; + c += ((uint32_t)k[9])<<8; + c += ((uint32_t)k[10])<<16; + c += ((uint32_t)k[11])<<24; + mix(a,b,c); + length -= 12; + k += 12; + } + + /*-------------------------------- last block: affect all 32 bits of (c) */ + switch(length) /* all the case statements fall through */ + { + case 12: c+=((uint32_t)k[11])<<24; + case 11: c+=((uint32_t)k[10])<<16; + case 10: c+=((uint32_t)k[9])<<8; + case 9 : c+=k[8]; + case 8 : b+=((uint32_t)k[7])<<24; + case 7 : b+=((uint32_t)k[6])<<16; + case 6 : b+=((uint32_t)k[5])<<8; + case 5 : b+=k[4]; + case 4 : a+=((uint32_t)k[3])<<24; + case 3 : a+=((uint32_t)k[2])<<16; + case 2 : a+=((uint32_t)k[1])<<8; + case 1 : a+=k[0]; + break; + case 0 : return c; + } + } + + final(a,b,c); + return c; +} + + +/* + * hashlittle2: return 2 32-bit hash values + * + * This is identical to hashlittle(), except it returns two 32-bit hash + * values instead of just one. This is good enough for hash table + * lookup with 2^^64 buckets, or if you want a second hash if you're not + * happy with the first, or if you want a probably-unique 64-bit ID for + * the key. *pc is better mixed than *pb, so use *pc first. If you want + * a 64-bit value do something like "*pc + (((uint64_t)*pb)<<32)". + */ +void jenkins_hashlittle2( + const void *key, /* the key to hash */ + size_t length, /* length of the key */ + uint32_t *pc, /* IN: primary initval, OUT: primary hash */ + uint32_t *pb) /* IN: secondary initval, OUT: secondary hash */ +{ + uint32_t a,b,c; /* internal state */ + union { const void *ptr; size_t i; } u; /* needed for Mac Powerbook G4 */ + + /* Set up the internal state */ + a = b = c = 0xdeadbeef + ((uint32_t)length) + *pc; + c += *pb; + + u.ptr = key; + if (HASH_LITTLE_ENDIAN && ((u.i & 0x3) == 0)) { + const uint32_t *k = (const uint32_t *)key; /* read 32-bit chunks */ + + /*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */ + while (length > 12) + { + a += k[0]; + b += k[1]; + c += k[2]; + mix(a,b,c); + length -= 12; + k += 3; + } + + /*----------------------------- handle the last (probably partial) block */ + /* + * "k[2]&0xffffff" actually reads beyond the end of the string, but + * then masks off the part it's not allowed to read. Because the + * string is aligned, the masked-off tail is in the same word as the + * rest of the string. Every machine with memory protection I've seen + * does it on word boundaries, so is OK with this. But VALGRIND will + * still catch it and complain. The masking trick does make the hash + * noticably faster for short strings (like English words). + */ +#ifndef VALGRIND + + switch(length) + { + case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; + case 11: c+=k[2]&0xffffff; b+=k[1]; a+=k[0]; break; + case 10: c+=k[2]&0xffff; b+=k[1]; a+=k[0]; break; + case 9 : c+=k[2]&0xff; b+=k[1]; a+=k[0]; break; + case 8 : b+=k[1]; a+=k[0]; break; + case 7 : b+=k[1]&0xffffff; a+=k[0]; break; + case 6 : b+=k[1]&0xffff; a+=k[0]; break; + case 5 : b+=k[1]&0xff; a+=k[0]; break; + case 4 : a+=k[0]; break; + case 3 : a+=k[0]&0xffffff; break; + case 2 : a+=k[0]&0xffff; break; + case 1 : a+=k[0]&0xff; break; + case 0 : *pc=c; *pb=b; return; /* zero length strings require no mixing */ + } + +#else /* make valgrind happy */ + + k8 = (const uint8_t *)k; + switch(length) + { + case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; + case 11: c+=((uint32_t)k8[10])<<16; /* fall through */ + case 10: c+=((uint32_t)k8[9])<<8; /* fall through */ + case 9 : c+=k8[8]; /* fall through */ + case 8 : b+=k[1]; a+=k[0]; break; + case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */ + case 6 : b+=((uint32_t)k8[5])<<8; /* fall through */ + case 5 : b+=k8[4]; /* fall through */ + case 4 : a+=k[0]; break; + case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */ + case 2 : a+=((uint32_t)k8[1])<<8; /* fall through */ + case 1 : a+=k8[0]; break; + case 0 : *pc=c; *pb=b; return; /* zero length strings require no mixing */ + } + +#endif /* !valgrind */ + + } else if (HASH_LITTLE_ENDIAN && ((u.i & 0x1) == 0)) { + const uint16_t *k = (const uint16_t *)key; /* read 16-bit chunks */ + const uint8_t *k8; + + /*--------------- all but last block: aligned reads and different mixing */ + while (length > 12) + { + a += k[0] + (((uint32_t)k[1])<<16); + b += k[2] + (((uint32_t)k[3])<<16); + c += k[4] + (((uint32_t)k[5])<<16); + mix(a,b,c); + length -= 12; + k += 6; + } + + /*----------------------------- handle the last (probably partial) block */ + k8 = (const uint8_t *)k; + switch(length) + { + case 12: c+=k[4]+(((uint32_t)k[5])<<16); + b+=k[2]+(((uint32_t)k[3])<<16); + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 11: c+=((uint32_t)k8[10])<<16; /* fall through */ + case 10: c+=k[4]; + b+=k[2]+(((uint32_t)k[3])<<16); + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 9 : c+=k8[8]; /* fall through */ + case 8 : b+=k[2]+(((uint32_t)k[3])<<16); + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */ + case 6 : b+=k[2]; + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 5 : b+=k8[4]; /* fall through */ + case 4 : a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */ + case 2 : a+=k[0]; + break; + case 1 : a+=k8[0]; + break; + case 0 : *pc=c; *pb=b; return; /* zero length strings require no mixing */ + } + + } else { /* need to read the key one byte at a time */ + const uint8_t *k = (const uint8_t *)key; + + /*--------------- all but the last block: affect some 32 bits of (a,b,c) */ + while (length > 12) + { + a += k[0]; + a += ((uint32_t)k[1])<<8; + a += ((uint32_t)k[2])<<16; + a += ((uint32_t)k[3])<<24; + b += k[4]; + b += ((uint32_t)k[5])<<8; + b += ((uint32_t)k[6])<<16; + b += ((uint32_t)k[7])<<24; + c += k[8]; + c += ((uint32_t)k[9])<<8; + c += ((uint32_t)k[10])<<16; + c += ((uint32_t)k[11])<<24; + mix(a,b,c); + length -= 12; + k += 12; + } + + /*-------------------------------- last block: affect all 32 bits of (c) */ + switch(length) /* all the case statements fall through */ + { + case 12: c+=((uint32_t)k[11])<<24; + case 11: c+=((uint32_t)k[10])<<16; + case 10: c+=((uint32_t)k[9])<<8; + case 9 : c+=k[8]; + case 8 : b+=((uint32_t)k[7])<<24; + case 7 : b+=((uint32_t)k[6])<<16; + case 6 : b+=((uint32_t)k[5])<<8; + case 5 : b+=k[4]; + case 4 : a+=((uint32_t)k[3])<<24; + case 3 : a+=((uint32_t)k[2])<<16; + case 2 : a+=((uint32_t)k[1])<<8; + case 1 : a+=k[0]; + break; + case 0 : *pc=c; *pb=b; return; /* zero length strings require no mixing */ + } + } + + final(a,b,c); + *pc=c; *pb=b; +} + + + +/* + * hashbig(): + * This is the same as hashword() on big-endian machines. It is different + * from hashlittle() on all machines. hashbig() takes advantage of + * big-endian byte ordering. + */ +uint32_t jenkins_hashbig( const void *key, size_t length, uint32_t initval) +{ + uint32_t a,b,c; + union { const void *ptr; size_t i; } u; /* to cast key to (size_t) happily */ + + /* Set up the internal state */ + a = b = c = 0xdeadbeef + ((uint32_t)length) + initval; + + u.ptr = key; + if (HASH_BIG_ENDIAN && ((u.i & 0x3) == 0)) { + const uint32_t *k = (const uint32_t *)key; /* read 32-bit chunks */ + + /*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */ + while (length > 12) + { + a += k[0]; + b += k[1]; + c += k[2]; + mix(a,b,c); + length -= 12; + k += 3; + } + + /*----------------------------- handle the last (probably partial) block */ + /* + * "k[2]<<8" actually reads beyond the end of the string, but + * then shifts out the part it's not allowed to read. Because the + * string is aligned, the illegal read is in the same word as the + * rest of the string. Every machine with memory protection I've seen + * does it on word boundaries, so is OK with this. But VALGRIND will + * still catch it and complain. The masking trick does make the hash + * noticably faster for short strings (like English words). + */ +#ifndef VALGRIND + + switch(length) + { + case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; + case 11: c+=k[2]&0xffffff00; b+=k[1]; a+=k[0]; break; + case 10: c+=k[2]&0xffff0000; b+=k[1]; a+=k[0]; break; + case 9 : c+=k[2]&0xff000000; b+=k[1]; a+=k[0]; break; + case 8 : b+=k[1]; a+=k[0]; break; + case 7 : b+=k[1]&0xffffff00; a+=k[0]; break; + case 6 : b+=k[1]&0xffff0000; a+=k[0]; break; + case 5 : b+=k[1]&0xff000000; a+=k[0]; break; + case 4 : a+=k[0]; break; + case 3 : a+=k[0]&0xffffff00; break; + case 2 : a+=k[0]&0xffff0000; break; + case 1 : a+=k[0]&0xff000000; break; + case 0 : return c; /* zero length strings require no mixing */ + } + +#else /* make valgrind happy */ + + k8 = (const uint8_t *)k; + switch(length) /* all the case statements fall through */ + { + case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; + case 11: c+=((uint32_t)k8[10])<<8; /* fall through */ + case 10: c+=((uint32_t)k8[9])<<16; /* fall through */ + case 9 : c+=((uint32_t)k8[8])<<24; /* fall through */ + case 8 : b+=k[1]; a+=k[0]; break; + case 7 : b+=((uint32_t)k8[6])<<8; /* fall through */ + case 6 : b+=((uint32_t)k8[5])<<16; /* fall through */ + case 5 : b+=((uint32_t)k8[4])<<24; /* fall through */ + case 4 : a+=k[0]; break; + case 3 : a+=((uint32_t)k8[2])<<8; /* fall through */ + case 2 : a+=((uint32_t)k8[1])<<16; /* fall through */ + case 1 : a+=((uint32_t)k8[0])<<24; break; + case 0 : return c; + } + +#endif /* !VALGRIND */ + + } else { /* need to read the key one byte at a time */ + const uint8_t *k = (const uint8_t *)key; + + /*--------------- all but the last block: affect some 32 bits of (a,b,c) */ + while (length > 12) + { + a += ((uint32_t)k[0])<<24; + a += ((uint32_t)k[1])<<16; + a += ((uint32_t)k[2])<<8; + a += ((uint32_t)k[3]); + b += ((uint32_t)k[4])<<24; + b += ((uint32_t)k[5])<<16; + b += ((uint32_t)k[6])<<8; + b += ((uint32_t)k[7]); + c += ((uint32_t)k[8])<<24; + c += ((uint32_t)k[9])<<16; + c += ((uint32_t)k[10])<<8; + c += ((uint32_t)k[11]); + mix(a,b,c); + length -= 12; + k += 12; + } + + /*-------------------------------- last block: affect all 32 bits of (c) */ + switch(length) /* all the case statements fall through */ + { + case 12: c+=k[11]; + case 11: c+=((uint32_t)k[10])<<8; + case 10: c+=((uint32_t)k[9])<<16; + case 9 : c+=((uint32_t)k[8])<<24; + case 8 : b+=k[7]; + case 7 : b+=((uint32_t)k[6])<<8; + case 6 : b+=((uint32_t)k[5])<<16; + case 5 : b+=((uint32_t)k[4])<<24; + case 4 : a+=k[3]; + case 3 : a+=((uint32_t)k[2])<<8; + case 2 : a+=((uint32_t)k[1])<<16; + case 1 : a+=((uint32_t)k[0])<<24; + break; + case 0 : return c; + } + } + + final(a,b,c); + return c; +} + + +#ifdef SELF_TEST + +/* used for timings */ +void driver1() +{ + uint8_t buf[256]; + uint32_t i; + uint32_t h=0; + time_t a,z; + + time(&a); + for (i=0; i<256; ++i) buf[i] = 'x'; + for (i=0; i<1; ++i) + { + h = hashlittle(&buf[0],1,h); + } + time(&z); + if (z-a > 0) printf("time %d %.8x\n", z-a, h); +} + +/* check that every input bit changes every output bit half the time */ +#define HASHSTATE 1 +#define HASHLEN 1 +#define MAXPAIR 60 +#define MAXLEN 70 +void driver2() +{ + uint8_t qa[MAXLEN+1], qb[MAXLEN+2], *a = &qa[0], *b = &qb[1]; + uint32_t c[HASHSTATE], d[HASHSTATE], i=0, j=0, k, l, m=0, z; + uint32_t e[HASHSTATE],f[HASHSTATE],g[HASHSTATE],h[HASHSTATE]; + uint32_t x[HASHSTATE],y[HASHSTATE]; + uint32_t hlen; + + printf("No more than %d trials should ever be needed \n",MAXPAIR/2); + for (hlen=0; hlen < MAXLEN; ++hlen) + { + z=0; + for (i=0; i<hlen; ++i) /*----------------------- for each input byte, */ + { + for (j=0; j<8; ++j) /*------------------------ for each input bit, */ + { + for (m=1; m<8; ++m) /*------------ for serveral possible initvals, */ + { + for (l=0; l<HASHSTATE; ++l) + e[l]=f[l]=g[l]=h[l]=x[l]=y[l]=~((uint32_t)0); + + /*---- check that every output bit is affected by that input bit */ + for (k=0; k<MAXPAIR; k+=2) + { + uint32_t finished=1; + /* keys have one bit different */ + for (l=0; l<hlen+1; ++l) {a[l] = b[l] = (uint8_t)0;} + /* have a and b be two keys differing in only one bit */ + a[i] ^= (k<<j); + a[i] ^= (k>>(8-j)); + c[0] = hashlittle(a, hlen, m); + b[i] ^= ((k+1)<<j); + b[i] ^= ((k+1)>>(8-j)); + d[0] = hashlittle(b, hlen, m); + /* check every bit is 1, 0, set, and not set at least once */ + for (l=0; l<HASHSTATE; ++l) + { + e[l] &= (c[l]^d[l]); + f[l] &= ~(c[l]^d[l]); + g[l] &= c[l]; + h[l] &= ~c[l]; + x[l] &= d[l]; + y[l] &= ~d[l]; + if (e[l]|f[l]|g[l]|h[l]|x[l]|y[l]) finished=0; + } + if (finished) break; + } + if (k>z) z=k; + if (k==MAXPAIR) + { + printf("Some bit didn't change: "); + printf("%.8x %.8x %.8x %.8x %.8x %.8x ", + e[0],f[0],g[0],h[0],x[0],y[0]); + printf("i %d j %d m %d len %d\n", i, j, m, hlen); + } + if (z==MAXPAIR) goto done; + } + } + } + done: + if (z < MAXPAIR) + { + printf("Mix success %2d bytes %2d initvals ",i,m); + printf("required %d trials\n", z/2); + } + } + printf("\n"); +} + +/* Check for reading beyond the end of the buffer and alignment problems */ +void driver3() +{ + uint8_t buf[MAXLEN+20], *b; + uint32_t len; + uint8_t q[] = "This is the time for all good men to come to the aid of their country..."; + uint32_t h; + uint8_t qq[] = "xThis is the time for all good men to come to the aid of their country..."; + uint32_t i; + uint8_t qqq[] = "xxThis is the time for all good men to come to the aid of their country..."; + uint32_t j; + uint8_t qqqq[] = "xxxThis is the time for all good men to come to the aid of their country..."; + uint32_t ref,x,y; + uint8_t *p; + + printf("Endianness. These lines should all be the same (for values filled in):\n"); + printf("%.8x %.8x %.8x\n", + hashword((const uint32_t *)q, (sizeof(q)-1)/4, 13), + hashword((const uint32_t *)q, (sizeof(q)-5)/4, 13), + hashword((const uint32_t *)q, (sizeof(q)-9)/4, 13)); + p = q; + printf("%.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x\n", + hashlittle(p, sizeof(q)-1, 13), hashlittle(p, sizeof(q)-2, 13), + hashlittle(p, sizeof(q)-3, 13), hashlittle(p, sizeof(q)-4, 13), + hashlittle(p, sizeof(q)-5, 13), hashlittle(p, sizeof(q)-6, 13), + hashlittle(p, sizeof(q)-7, 13), hashlittle(p, sizeof(q)-8, 13), + hashlittle(p, sizeof(q)-9, 13), hashlittle(p, sizeof(q)-10, 13), + hashlittle(p, sizeof(q)-11, 13), hashlittle(p, sizeof(q)-12, 13)); + p = &qq[1]; + printf("%.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x\n", + hashlittle(p, sizeof(q)-1, 13), hashlittle(p, sizeof(q)-2, 13), + hashlittle(p, sizeof(q)-3, 13), hashlittle(p, sizeof(q)-4, 13), + hashlittle(p, sizeof(q)-5, 13), hashlittle(p, sizeof(q)-6, 13), + hashlittle(p, sizeof(q)-7, 13), hashlittle(p, sizeof(q)-8, 13), + hashlittle(p, sizeof(q)-9, 13), hashlittle(p, sizeof(q)-10, 13), + hashlittle(p, sizeof(q)-11, 13), hashlittle(p, sizeof(q)-12, 13)); + p = &qqq[2]; + printf("%.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x\n", + hashlittle(p, sizeof(q)-1, 13), hashlittle(p, sizeof(q)-2, 13), + hashlittle(p, sizeof(q)-3, 13), hashlittle(p, sizeof(q)-4, 13), + hashlittle(p, sizeof(q)-5, 13), hashlittle(p, sizeof(q)-6, 13), + hashlittle(p, sizeof(q)-7, 13), hashlittle(p, sizeof(q)-8, 13), + hashlittle(p, sizeof(q)-9, 13), hashlittle(p, sizeof(q)-10, 13), + hashlittle(p, sizeof(q)-11, 13), hashlittle(p, sizeof(q)-12, 13)); + p = &qqqq[3]; + printf("%.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x\n", + hashlittle(p, sizeof(q)-1, 13), hashlittle(p, sizeof(q)-2, 13), + hashlittle(p, sizeof(q)-3, 13), hashlittle(p, sizeof(q)-4, 13), + hashlittle(p, sizeof(q)-5, 13), hashlittle(p, sizeof(q)-6, 13), + hashlittle(p, sizeof(q)-7, 13), hashlittle(p, sizeof(q)-8, 13), + hashlittle(p, sizeof(q)-9, 13), hashlittle(p, sizeof(q)-10, 13), + hashlittle(p, sizeof(q)-11, 13), hashlittle(p, sizeof(q)-12, 13)); + printf("\n"); + + /* check that hashlittle2 and hashlittle produce the same results */ + i=47; j=0; + hashlittle2(q, sizeof(q), &i, &j); + if (hashlittle(q, sizeof(q), 47) != i) + printf("hashlittle2 and hashlittle mismatch\n"); + + /* check that hashword2 and hashword produce the same results */ + len = 0xdeadbeef; + i=47, j=0; + hashword2(&len, 1, &i, &j); + if (hashword(&len, 1, 47) != i) + printf("hashword2 and hashword mismatch %x %x\n", + i, hashword(&len, 1, 47)); + + /* check hashlittle doesn't read before or after the ends of the string */ + for (h=0, b=buf+1; h<8; ++h, ++b) + { + for (i=0; i<MAXLEN; ++i) + { + len = i; + for (j=0; j<i; ++j) *(b+j)=0; + + /* these should all be equal */ + ref = hashlittle(b, len, (uint32_t)1); + *(b+i)=(uint8_t)~0; + *(b-1)=(uint8_t)~0; + x = hashlittle(b, len, (uint32_t)1); + y = hashlittle(b, len, (uint32_t)1); + if ((ref != x) || (ref != y)) + { + printf("alignment error: %.8x %.8x %.8x %d %d\n",ref,x,y, + h, i); + } + } + } +} + +/* check for problems with nulls */ + void driver4() +{ + uint8_t buf[1]; + uint32_t h,i,state[HASHSTATE]; + + + buf[0] = ~0; + for (i=0; i<HASHSTATE; ++i) state[i] = 1; + printf("These should all be different\n"); + for (i=0, h=0; i<8; ++i) + { + h = hashlittle(buf, 0, h); + printf("%2ld 0-byte strings, hash is %.8x\n", i, h); + } +} + +void driver5() +{ + uint32_t b,c; + b=0, c=0, hashlittle2("", 0, &c, &b); + printf("hash is %.8lx %.8lx\n", c, b); /* deadbeef deadbeef */ + b=0xdeadbeef, c=0, hashlittle2("", 0, &c, &b); + printf("hash is %.8lx %.8lx\n", c, b); /* bd5b7dde deadbeef */ + b=0xdeadbeef, c=0xdeadbeef, hashlittle2("", 0, &c, &b); + printf("hash is %.8lx %.8lx\n", c, b); /* 9c093ccd bd5b7dde */ + b=0, c=0, hashlittle2("Four score and seven years ago", 30, &c, &b); + printf("hash is %.8lx %.8lx\n", c, b); /* 17770551 ce7226e6 */ + b=1, c=0, hashlittle2("Four score and seven years ago", 30, &c, &b); + printf("hash is %.8lx %.8lx\n", c, b); /* e3607cae bd371de4 */ + b=0, c=1, hashlittle2("Four score and seven years ago", 30, &c, &b); + printf("hash is %.8lx %.8lx\n", c, b); /* cd628161 6cbea4b3 */ + c = hashlittle("Four score and seven years ago", 30, 0); + printf("hash is %.8lx\n", c); /* 17770551 */ + c = hashlittle("Four score and seven years ago", 30, 1); + printf("hash is %.8lx\n", c); /* cd628161 */ +} + + +int main() +{ + driver1(); /* test that the key is hashed: used for timings */ + driver2(); /* test that whole key is hashed thoroughly */ + driver3(); /* test that nothing but the key is hashed */ + driver4(); /* test hashing multiple buffers (all buffers are null) */ + driver5(); /* test the hash against known vectors */ + return 1; +} + +#endif /* SELF_TEST */ diff --git a/src/journal/lookup3.h b/src/journal/lookup3.h new file mode 100644 index 0000000000..31cc2f57b0 --- /dev/null +++ b/src/journal/lookup3.h @@ -0,0 +1,25 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#ifndef foolookup3hfoo +#define foolookup3hfoo + +#include <inttypes.h> +#include <sys/types.h> + +uint32_t jenkins_hashword(const uint32_t *k, size_t length, uint32_t initval); +void jenkins_hashword2(const uint32_t *k, size_t length, uint32_t *pc, uint32_t *pb); + +uint32_t jenkins_hashlittle(const void *key, size_t length, uint32_t initval); +void jenkins_hashlittle2(const void *key, size_t length, uint32_t *pc, uint32_t *pb); + +uint32_t jenkins_hashbig(const void *key, size_t length, uint32_t initval); + +static inline uint64_t hash64(const void *data, size_t length) { + uint32_t a = 0, b = 0; + + jenkins_hashlittle2(data, length, &a, &b); + + return ((uint64_t) a << 32ULL) | (uint64_t) b; +} + +#endif diff --git a/src/journal/sd-journal.c b/src/journal/sd-journal.c new file mode 100644 index 0000000000..baf51db1a8 --- /dev/null +++ b/src/journal/sd-journal.c @@ -0,0 +1,1616 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2011 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 <errno.h> +#include <fcntl.h> +#include <stddef.h> +#include <unistd.h> +#include <sys/inotify.h> + +#include "sd-journal.h" +#include "journal-def.h" +#include "journal-file.h" +#include "hashmap.h" +#include "list.h" +#include "lookup3.h" +#include "compress.h" +#include "journal-internal.h" + +#define JOURNAL_FILES_MAX 1024 + +static void detach_location(sd_journal *j) { + Iterator i; + JournalFile *f; + + assert(j); + + j->current_file = NULL; + j->current_field = 0; + + HASHMAP_FOREACH(f, j->files, i) + f->current_offset = 0; +} + +static void reset_location(sd_journal *j) { + assert(j); + + detach_location(j); + zero(j->current_location); +} + +static void init_location(Location *l, JournalFile *f, Object *o) { + assert(l); + assert(f); + assert(o->object.type == OBJECT_ENTRY); + + l->type = LOCATION_DISCRETE; + l->seqnum = le64toh(o->entry.seqnum); + l->seqnum_id = f->header->seqnum_id; + l->realtime = le64toh(o->entry.realtime); + l->monotonic = le64toh(o->entry.monotonic); + l->boot_id = o->entry.boot_id; + l->xor_hash = le64toh(o->entry.xor_hash); + + l->seqnum_set = l->realtime_set = l->monotonic_set = l->xor_hash_set = true; +} + +static void set_location(sd_journal *j, JournalFile *f, Object *o, uint64_t offset) { + assert(j); + assert(f); + assert(o); + + init_location(&j->current_location, f, o); + + j->current_file = f; + j->current_field = 0; + + f->current_offset = offset; +} + +static int same_field(const void *_a, size_t s, const void *_b, size_t t) { + const uint8_t *a = _a, *b = _b; + size_t j; + bool a_good = false, b_good = false, different = false; + + for (j = 0; j < s && j < t; j++) { + + if (a[j] == '=') + a_good = true; + if (b[j] == '=') + b_good = true; + if (a[j] != b[j]) + different = true; + + if (a_good && b_good) + return different ? 0 : 1; + } + + return -EINVAL; +} + +_public_ int sd_journal_add_match(sd_journal *j, const void *data, size_t size) { + Match *m, *after = NULL; + uint64_t le_hash; + + if (!j) + return -EINVAL; + if (!data) + return -EINVAL; + if (size <= 0) + return -EINVAL; + + le_hash = htole64(hash64(data, size)); + + LIST_FOREACH(matches, m, j->matches) { + int r; + + if (m->le_hash == le_hash && + m->size == size && + memcmp(m->data, data, size) == 0) + return 0; + + r = same_field(data, size, m->data, m->size); + if (r < 0) + return r; + else if (r > 0) + after = m; + } + + m = new0(Match, 1); + if (!m) + return -ENOMEM; + + m->size = size; + + m->data = malloc(m->size); + if (!m->data) { + free(m); + return -ENOMEM; + } + + memcpy(m->data, data, size); + m->le_hash = le_hash; + + /* Matches for the same fields we order adjacent to each + * other */ + LIST_INSERT_AFTER(Match, matches, j->matches, after, m); + j->n_matches ++; + + detach_location(j); + + return 0; +} + +_public_ void sd_journal_flush_matches(sd_journal *j) { + if (!j) + return; + + while (j->matches) { + Match *m = j->matches; + + LIST_REMOVE(Match, matches, j->matches, m); + free(m->data); + free(m); + } + + j->n_matches = 0; + + detach_location(j); +} + +static int compare_order(JournalFile *af, Object *ao, + JournalFile *bf, Object *bo) { + + uint64_t a, b; + + assert(af); + assert(ao); + assert(bf); + assert(bo); + + /* We operate on two different files here, hence we can access + * two objects at the same time, which we normally can't. + * + * If contents and timestamps match, these entries are + * identical, even if the seqnum does not match */ + + if (sd_id128_equal(ao->entry.boot_id, bo->entry.boot_id) && + ao->entry.monotonic == bo->entry.monotonic && + ao->entry.realtime == bo->entry.realtime && + ao->entry.xor_hash == bo->entry.xor_hash) + return 0; + + if (sd_id128_equal(af->header->seqnum_id, bf->header->seqnum_id)) { + + /* If this is from the same seqnum source, compare + * seqnums */ + a = le64toh(ao->entry.seqnum); + b = le64toh(bo->entry.seqnum); + + if (a < b) + return -1; + if (a > b) + return 1; + + /* Wow! This is weird, different data but the same + * seqnums? Something is borked, but let's make the + * best of it and compare by time. */ + } + + if (sd_id128_equal(ao->entry.boot_id, bo->entry.boot_id)) { + + /* If the boot id matches compare monotonic time */ + a = le64toh(ao->entry.monotonic); + b = le64toh(bo->entry.monotonic); + + if (a < b) + return -1; + if (a > b) + return 1; + } + + /* Otherwise compare UTC time */ + a = le64toh(ao->entry.realtime); + b = le64toh(ao->entry.realtime); + + if (a < b) + return -1; + if (a > b) + return 1; + + /* Finally, compare by contents */ + a = le64toh(ao->entry.xor_hash); + b = le64toh(ao->entry.xor_hash); + + if (a < b) + return -1; + if (a > b) + return 1; + + return 0; +} + +static int compare_with_location(JournalFile *af, Object *ao, Location *l) { + uint64_t a; + + assert(af); + assert(ao); + assert(l); + assert(l->type == LOCATION_DISCRETE); + + if (l->monotonic_set && + sd_id128_equal(ao->entry.boot_id, l->boot_id) && + l->realtime_set && + le64toh(ao->entry.realtime) == l->realtime && + l->xor_hash_set && + le64toh(ao->entry.xor_hash) == l->xor_hash) + return 0; + + if (l->seqnum_set && + sd_id128_equal(af->header->seqnum_id, l->seqnum_id)) { + + a = le64toh(ao->entry.seqnum); + + if (a < l->seqnum) + return -1; + if (a > l->seqnum) + return 1; + } + + if (l->monotonic_set && + sd_id128_equal(ao->entry.boot_id, l->boot_id)) { + + a = le64toh(ao->entry.monotonic); + + if (a < l->monotonic) + return -1; + if (a > l->monotonic) + return 1; + } + + if (l->realtime_set) { + + a = le64toh(ao->entry.realtime); + + if (a < l->realtime) + return -1; + if (a > l->realtime) + return 1; + } + + if (l->xor_hash_set) { + a = le64toh(ao->entry.xor_hash); + + if (a < l->xor_hash) + return -1; + if (a > l->xor_hash) + return 1; + } + + return 0; +} + +static int find_location(sd_journal *j, JournalFile *f, direction_t direction, Object **ret, uint64_t *offset) { + Object *o = NULL; + uint64_t p = 0; + int r; + + assert(j); + + if (!j->matches) { + /* No matches is simple */ + + if (j->current_location.type == LOCATION_HEAD) + r = journal_file_next_entry(f, NULL, 0, DIRECTION_DOWN, &o, &p); + else if (j->current_location.type == LOCATION_TAIL) + r = journal_file_next_entry(f, NULL, 0, DIRECTION_UP, &o, &p); + else if (j->current_location.seqnum_set && + sd_id128_equal(j->current_location.seqnum_id, f->header->seqnum_id)) + r = journal_file_move_to_entry_by_seqnum(f, j->current_location.seqnum, direction, &o, &p); + else if (j->current_location.monotonic_set) + r = journal_file_move_to_entry_by_monotonic(f, j->current_location.boot_id, j->current_location.monotonic, direction, &o, &p); + else if (j->current_location.realtime_set) + r = journal_file_move_to_entry_by_realtime(f, j->current_location.realtime, direction, &o, &p); + else + r = journal_file_next_entry(f, NULL, 0, direction, &o, &p); + + if (r <= 0) + return r; + + } else { + Match *m, *term_match = NULL; + Object *to = NULL; + uint64_t tp = 0; + + /* We have matches, first, let's jump to the monotonic + * position if we have any, since it implies a + * match. */ + + if (j->current_location.type == LOCATION_DISCRETE && + j->current_location.monotonic_set) { + + r = journal_file_move_to_entry_by_monotonic(f, j->current_location.boot_id, j->current_location.monotonic, direction, &o, &p); + if (r <= 0) + return r == -ENOENT ? 0 : r; + } + + LIST_FOREACH(matches, m, j->matches) { + Object *c, *d; + uint64_t cp, dp; + + r = journal_file_find_data_object_with_hash(f, m->data, m->size, m->le_hash, &d, &dp); + if (r <= 0) + return r; + + if (j->current_location.type == LOCATION_HEAD) + r = journal_file_next_entry_for_data(f, NULL, 0, dp, DIRECTION_DOWN, &c, &cp); + else if (j->current_location.type == LOCATION_TAIL) + r = journal_file_next_entry_for_data(f, NULL, 0, dp, DIRECTION_UP, &c, &cp); + else if (j->current_location.seqnum_set && + sd_id128_equal(j->current_location.seqnum_id, f->header->seqnum_id)) + r = journal_file_move_to_entry_by_seqnum_for_data(f, dp, j->current_location.seqnum, direction, &c, &cp); + else if (j->current_location.realtime_set) + r = journal_file_move_to_entry_by_realtime_for_data(f, dp, j->current_location.realtime, direction, &c, &cp); + else + r = journal_file_next_entry_for_data(f, NULL, 0, dp, direction, &c, &cp); + + if (r < 0) + return r; + + if (!term_match) { + term_match = m; + + if (r > 0) { + to = c; + tp = cp; + } + } else if (same_field(term_match->data, term_match->size, m->data, m->size)) { + + /* Same field as previous match... */ + if (r > 0) { + + /* Find the earliest of the OR matches */ + + if (!to || + (direction == DIRECTION_DOWN && cp < tp) || + (direction == DIRECTION_UP && cp > tp)) { + to = c; + tp = cp; + } + + } + + } else { + + /* Previous term is finished, did anything match? */ + if (!to) + return 0; + + /* Find the last of the AND matches */ + if (!o || + (direction == DIRECTION_DOWN && tp > p) || + (direction == DIRECTION_UP && tp < p)) { + o = to; + p = tp; + } + + term_match = m; + + if (r > 0) { + to = c; + tp = cp; + } else { + to = NULL; + tp = 0; + } + } + } + + /* Last term is finished, did anything match? */ + if (!to) + return 0; + + if (!o || + (direction == DIRECTION_DOWN && tp > p) || + (direction == DIRECTION_UP && tp < p)) { + o = to; + p = tp; + } + + if (!o) + return 0; + } + + if (ret) + *ret = o; + + if (offset) + *offset = p; + + return 1; +} + +static int next_with_matches(sd_journal *j, JournalFile *f, direction_t direction, Object **ret, uint64_t *offset) { + int r; + uint64_t cp; + Object *c; + + assert(j); + assert(f); + assert(ret); + assert(offset); + + c = *ret; + cp = *offset; + + if (!j->matches) { + /* No matches is easy */ + + r = journal_file_next_entry(f, c, cp, direction, &c, &cp); + if (r <= 0) + return r; + + if (ret) + *ret = c; + if (offset) + *offset = cp; + return 1; + } + + /* So there are matches we have to adhere to, let's find the + * first entry that matches all of them */ + + for (;;) { + uint64_t np, n; + bool found, term_result = false; + Match *m, *term_match = NULL; + Object *npo = NULL; + + n = journal_file_entry_n_items(c); + + /* Make sure we don't match the entry we are starting + * from. */ + found = cp != *offset; + + np = 0; + LIST_FOREACH(matches, m, j->matches) { + uint64_t q, k; + Object *qo = NULL; + + /* Let's check if this is the beginning of a + * new term, i.e. has a different field prefix + * as the preceeding match. */ + if (!term_match) { + term_match = m; + term_result = false; + } else if (!same_field(term_match->data, term_match->size, m->data, m->size)) { + if (!term_result) + found = false; + + term_match = m; + term_result = false; + } + + for (k = 0; k < n; k++) + if (c->entry.items[k].hash == m->le_hash) + break; + + if (k >= n) { + /* Hmm, didn't find any field that + * matched this rule, so ignore this + * match. Go on with next match */ + continue; + } + + term_result = true; + + /* Hmm, so, this field matched, let's remember + * where we'd have to try next, in case the other + * matches are not OK */ + + r = journal_file_next_entry_for_data(f, c, cp, le64toh(c->entry.items[k].object_offset), direction, &qo, &q); + if (r < 0) + return r; + + if (r > 0) { + + if (direction == DIRECTION_DOWN) { + if (q > np) { + np = q; + npo = qo; + } + } else { + if (np == 0 || q < np) { + np = q; + npo = qo; + } + } + } + } + + /* Check the last term */ + if (term_match && !term_result) + found = false; + + /* Did this entry match against all matches? */ + if (found) { + if (ret) + *ret = c; + if (offset) + *offset = cp; + return 1; + } + + /* Did we find a subsequent entry? */ + if (np == 0) + return 0; + + /* Hmm, ok, this entry only matched partially, so + * let's try another one */ + cp = np; + c = npo; + } +} + +static int next_beyond_location(sd_journal *j, JournalFile *f, direction_t direction, Object **ret, uint64_t *offset) { + Object *c; + uint64_t cp; + int compare_value, r; + + assert(j); + assert(f); + + if (f->current_offset > 0) { + cp = f->current_offset; + + r = journal_file_move_to_object(f, OBJECT_ENTRY, cp, &c); + if (r < 0) + return r; + + r = next_with_matches(j, f, direction, &c, &cp); + if (r <= 0) + return r; + + compare_value = 1; + } else { + r = find_location(j, f, direction, &c, &cp); + if (r <= 0) + return r; + + compare_value = 0; + } + + for (;;) { + bool found; + + if (j->current_location.type == LOCATION_DISCRETE) { + int k; + + k = compare_with_location(f, c, &j->current_location); + if (direction == DIRECTION_DOWN) + found = k >= compare_value; + else + found = k <= -compare_value; + } else + found = true; + + if (found) { + if (ret) + *ret = c; + if (offset) + *offset = cp; + return 1; + } + + r = next_with_matches(j, f, direction, &c, &cp); + if (r <= 0) + return r; + } +} + +static int real_journal_next(sd_journal *j, direction_t direction) { + JournalFile *f, *new_current = NULL; + Iterator i; + int r; + uint64_t new_offset = 0; + Object *new_entry = NULL; + + if (!j) + return -EINVAL; + + HASHMAP_FOREACH(f, j->files, i) { + Object *o; + uint64_t p; + bool found; + + r = next_beyond_location(j, f, direction, &o, &p); + if (r < 0) + return r; + else if (r == 0) + continue; + + if (!new_current) + found = true; + else { + int k; + + k = compare_order(f, o, new_current, new_entry); + + if (direction == DIRECTION_DOWN) + found = k < 0; + else + found = k > 0; + } + + if (found) { + new_current = f; + new_entry = o; + new_offset = p; + } + } + + if (!new_current) + return 0; + + set_location(j, new_current, new_entry, new_offset); + + return 1; +} + +_public_ int sd_journal_next(sd_journal *j) { + return real_journal_next(j, DIRECTION_DOWN); +} + +_public_ int sd_journal_previous(sd_journal *j) { + return real_journal_next(j, DIRECTION_UP); +} + +static int real_journal_next_skip(sd_journal *j, direction_t direction, uint64_t skip) { + int c = 0, r; + + if (!j) + return -EINVAL; + + if (skip == 0) { + /* If this is not a discrete skip, then at least + * resolve the current location */ + if (j->current_location.type != LOCATION_DISCRETE) + return real_journal_next(j, direction); + + return 0; + } + + do { + r = real_journal_next(j, direction); + if (r < 0) + return r; + + if (r == 0) + return c; + + skip--; + c++; + } while (skip > 0); + + return c; +} + +_public_ int sd_journal_next_skip(sd_journal *j, uint64_t skip) { + return real_journal_next_skip(j, DIRECTION_DOWN, skip); +} + +_public_ int sd_journal_previous_skip(sd_journal *j, uint64_t skip) { + return real_journal_next_skip(j, DIRECTION_UP, skip); +} + +_public_ int sd_journal_get_cursor(sd_journal *j, char **cursor) { + Object *o; + int r; + char bid[33], sid[33]; + + if (!j) + return -EINVAL; + if (!cursor) + return -EINVAL; + + if (!j->current_file || j->current_file->current_offset <= 0) + return -EADDRNOTAVAIL; + + r = journal_file_move_to_object(j->current_file, OBJECT_ENTRY, j->current_file->current_offset, &o); + if (r < 0) + return r; + + sd_id128_to_string(j->current_file->header->seqnum_id, sid); + sd_id128_to_string(o->entry.boot_id, bid); + + if (asprintf(cursor, + "s=%s;i=%llx;b=%s;m=%llx;t=%llx;x=%llx;p=%s", + sid, (unsigned long long) le64toh(o->entry.seqnum), + bid, (unsigned long long) le64toh(o->entry.monotonic), + (unsigned long long) le64toh(o->entry.realtime), + (unsigned long long) le64toh(o->entry.xor_hash), + file_name_from_path(j->current_file->path)) < 0) + return -ENOMEM; + + return 1; +} + +_public_ int sd_journal_seek_cursor(sd_journal *j, const char *cursor) { + char *w; + size_t l; + char *state; + unsigned long long seqnum, monotonic, realtime, xor_hash; + bool + seqnum_id_set = false, + seqnum_set = false, + boot_id_set = false, + monotonic_set = false, + realtime_set = false, + xor_hash_set = false; + sd_id128_t seqnum_id, boot_id; + + if (!j) + return -EINVAL; + if (!cursor) + return -EINVAL; + + FOREACH_WORD_SEPARATOR(w, l, cursor, ";", state) { + char *item; + int k = 0; + + if (l < 2 || w[1] != '=') + return -EINVAL; + + item = strndup(w, l); + if (!item) + return -ENOMEM; + + switch (w[0]) { + + case 's': + seqnum_id_set = true; + k = sd_id128_from_string(w+2, &seqnum_id); + break; + + case 'i': + seqnum_set = true; + if (sscanf(w+2, "%llx", &seqnum) != 1) + k = -EINVAL; + break; + + case 'b': + boot_id_set = true; + k = sd_id128_from_string(w+2, &boot_id); + break; + + case 'm': + monotonic_set = true; + if (sscanf(w+2, "%llx", &monotonic) != 1) + k = -EINVAL; + break; + + case 't': + realtime_set = true; + if (sscanf(w+2, "%llx", &realtime) != 1) + k = -EINVAL; + break; + + case 'x': + xor_hash_set = true; + if (sscanf(w+2, "%llx", &xor_hash) != 1) + k = -EINVAL; + break; + } + + free(item); + + if (k < 0) + return k; + } + + if ((!seqnum_set || !seqnum_id_set) && + (!monotonic_set || !boot_id_set) && + !realtime_set) + return -EINVAL; + + reset_location(j); + + j->current_location.type = LOCATION_DISCRETE; + + if (realtime_set) { + j->current_location.realtime = (uint64_t) realtime; + j->current_location.realtime_set = true; + } + + if (seqnum_set && seqnum_id_set) { + j->current_location.seqnum = (uint64_t) seqnum; + j->current_location.seqnum_id = seqnum_id; + j->current_location.seqnum_set = true; + } + + if (monotonic_set && boot_id_set) { + j->current_location.monotonic = (uint64_t) monotonic; + j->current_location.boot_id = boot_id; + j->current_location.monotonic_set = true; + } + + if (xor_hash_set) { + j->current_location.xor_hash = (uint64_t) xor_hash; + j->current_location.xor_hash_set = true; + } + + return 0; +} + +_public_ int sd_journal_seek_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t usec) { + if (!j) + return -EINVAL; + + reset_location(j); + j->current_location.type = LOCATION_DISCRETE; + j->current_location.boot_id = boot_id; + j->current_location.monotonic = usec; + j->current_location.monotonic_set = true; + + return 0; +} + +_public_ int sd_journal_seek_realtime_usec(sd_journal *j, uint64_t usec) { + if (!j) + return -EINVAL; + + reset_location(j); + j->current_location.type = LOCATION_DISCRETE; + j->current_location.realtime = usec; + j->current_location.realtime_set = true; + + return 0; +} + +_public_ int sd_journal_seek_head(sd_journal *j) { + if (!j) + return -EINVAL; + + reset_location(j); + j->current_location.type = LOCATION_HEAD; + + return 0; +} + +_public_ int sd_journal_seek_tail(sd_journal *j) { + if (!j) + return -EINVAL; + + reset_location(j); + j->current_location.type = LOCATION_TAIL; + + return 0; +} + +static int add_file(sd_journal *j, const char *prefix, const char *dir, const char *filename) { + char *fn; + int r; + JournalFile *f; + + assert(j); + assert(prefix); + assert(filename); + + if ((j->flags & SD_JOURNAL_SYSTEM_ONLY) && + !startswith(filename, "system.journal")) + return 0; + + if (dir) + fn = join(prefix, "/", dir, "/", filename, NULL); + else + fn = join(prefix, "/", filename, NULL); + + if (!fn) + return -ENOMEM; + + if (hashmap_get(j->files, fn)) { + free(fn); + return 0; + } + + if (hashmap_size(j->files) >= JOURNAL_FILES_MAX) { + log_debug("Too many open journal files, not adding %s, ignoring.", fn); + free(fn); + return 0; + } + + r = journal_file_open(fn, O_RDONLY, 0, NULL, &f); + free(fn); + + if (r < 0) { + if (errno == ENOENT) + return 0; + + return r; + } + + /* journal_file_dump(f); */ + + r = hashmap_put(j->files, f->path, f); + if (r < 0) { + journal_file_close(f); + return r; + } + + log_debug("File %s got added.", f->path); + + return 0; +} + +static int remove_file(sd_journal *j, const char *prefix, const char *dir, const char *filename) { + char *fn; + JournalFile *f; + + assert(j); + assert(prefix); + assert(filename); + + if (dir) + fn = join(prefix, "/", dir, "/", filename, NULL); + else + fn = join(prefix, "/", filename, NULL); + + if (!fn) + return -ENOMEM; + + f = hashmap_get(j->files, fn); + free(fn); + + if (!f) + return 0; + + hashmap_remove(j->files, f->path); + journal_file_close(f); + + log_debug("File %s got removed.", f->path); + return 0; +} + +static int add_directory(sd_journal *j, const char *prefix, const char *dir) { + char *fn; + int r; + DIR *d; + int wd; + sd_id128_t id, mid; + + assert(j); + assert(prefix); + assert(dir); + + if ((j->flags & SD_JOURNAL_LOCAL_ONLY) && + (sd_id128_from_string(dir, &id) < 0 || + sd_id128_get_machine(&mid) < 0 || + !sd_id128_equal(id, mid))) + return 0; + + fn = join(prefix, "/", dir, NULL); + if (!fn) + return -ENOMEM; + + d = opendir(fn); + + if (!d) { + free(fn); + if (errno == ENOENT) + return 0; + + return -errno; + } + + wd = inotify_add_watch(j->inotify_fd, fn, + IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE| + IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT| + IN_DONT_FOLLOW|IN_ONLYDIR); + if (wd > 0) { + if (hashmap_put(j->inotify_wd_dirs, INT_TO_PTR(wd), fn) < 0) + inotify_rm_watch(j->inotify_fd, wd); + else + fn = NULL; + } + + free(fn); + + for (;;) { + struct dirent buf, *de; + + r = readdir_r(d, &buf, &de); + if (r != 0 || !de) + break; + + if (!dirent_is_file_with_suffix(de, ".journal")) + continue; + + r = add_file(j, prefix, dir, de->d_name); + if (r < 0) + log_debug("Failed to add file %s/%s/%s: %s", prefix, dir, de->d_name, strerror(-r)); + } + + closedir(d); + + log_debug("Directory %s/%s got added.", prefix, dir); + + return 0; +} + +static void remove_directory_wd(sd_journal *j, int wd) { + char *p; + + assert(j); + assert(wd > 0); + + if (j->inotify_fd >= 0) + inotify_rm_watch(j->inotify_fd, wd); + + p = hashmap_remove(j->inotify_wd_dirs, INT_TO_PTR(wd)); + + if (p) { + log_debug("Directory %s got removed.", p); + free(p); + } +} + +static void add_root_wd(sd_journal *j, const char *p) { + int wd; + char *k; + + assert(j); + assert(p); + + wd = inotify_add_watch(j->inotify_fd, p, + IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE| + IN_DONT_FOLLOW|IN_ONLYDIR); + if (wd <= 0) + return; + + k = strdup(p); + if (!k || hashmap_put(j->inotify_wd_roots, INT_TO_PTR(wd), k) < 0) { + inotify_rm_watch(j->inotify_fd, wd); + free(k); + } +} + +static void remove_root_wd(sd_journal *j, int wd) { + char *p; + + assert(j); + assert(wd > 0); + + if (j->inotify_fd >= 0) + inotify_rm_watch(j->inotify_fd, wd); + + p = hashmap_remove(j->inotify_wd_roots, INT_TO_PTR(wd)); + + if (p) { + log_debug("Root %s got removed.", p); + free(p); + } +} + +_public_ int sd_journal_open(sd_journal **ret, int flags) { + sd_journal *j; + const char *p; + const char search_paths[] = + "/run/log/journal\0" + "/var/log/journal\0"; + int r; + + if (!ret) + return -EINVAL; + + if (flags & ~(SD_JOURNAL_LOCAL_ONLY| + SD_JOURNAL_RUNTIME_ONLY| + SD_JOURNAL_SYSTEM_ONLY)) + return -EINVAL; + + j = new0(sd_journal, 1); + if (!j) + return -ENOMEM; + + j->flags = flags; + + j->inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC); + if (j->inotify_fd < 0) { + r = -errno; + goto fail; + } + + j->files = hashmap_new(string_hash_func, string_compare_func); + if (!j->files) { + r = -ENOMEM; + goto fail; + } + + j->inotify_wd_dirs = hashmap_new(trivial_hash_func, trivial_compare_func); + j->inotify_wd_roots = hashmap_new(trivial_hash_func, trivial_compare_func); + + if (!j->inotify_wd_dirs || !j->inotify_wd_roots) { + r = -ENOMEM; + goto fail; + } + + /* We ignore most errors here, since the idea is to only open + * what's actually accessible, and ignore the rest. */ + + NULSTR_FOREACH(p, search_paths) { + DIR *d; + + if ((flags & SD_JOURNAL_RUNTIME_ONLY) && + !path_startswith(p, "/run")) + continue; + + d = opendir(p); + if (!d) { + if (errno != ENOENT) + log_debug("Failed to open %s: %m", p); + continue; + } + + add_root_wd(j, p); + + for (;;) { + struct dirent buf, *de; + sd_id128_t id; + + r = readdir_r(d, &buf, &de); + if (r != 0 || !de) + break; + + if (dirent_is_file_with_suffix(de, ".journal")) { + r = add_file(j, p, NULL, de->d_name); + if (r < 0) + log_debug("Failed to add file %s/%s: %s", p, de->d_name, strerror(-r)); + + } else if ((de->d_type == DT_DIR || de->d_type == DT_UNKNOWN) && + sd_id128_from_string(de->d_name, &id) >= 0) { + + r = add_directory(j, p, de->d_name); + if (r < 0) + log_debug("Failed to add directory %s/%s: %s", p, de->d_name, strerror(-r)); + } + } + + closedir(d); + } + + *ret = j; + return 0; + +fail: + sd_journal_close(j); + + return r; +}; + +_public_ void sd_journal_close(sd_journal *j) { + if (!j) + return; + + if (j->inotify_wd_dirs) { + void *k; + + while ((k = hashmap_first_key(j->inotify_wd_dirs))) + remove_directory_wd(j, PTR_TO_INT(k)); + + hashmap_free(j->inotify_wd_dirs); + } + + if (j->inotify_wd_roots) { + void *k; + + while ((k = hashmap_first_key(j->inotify_wd_roots))) + remove_root_wd(j, PTR_TO_INT(k)); + + hashmap_free(j->inotify_wd_roots); + } + + if (j->files) { + JournalFile *f; + + while ((f = hashmap_steal_first(j->files))) + journal_file_close(f); + + hashmap_free(j->files); + } + + sd_journal_flush_matches(j); + + if (j->inotify_fd >= 0) + close_nointr_nofail(j->inotify_fd); + + free(j); +} + +_public_ int sd_journal_get_realtime_usec(sd_journal *j, uint64_t *ret) { + Object *o; + JournalFile *f; + int r; + + if (!j) + return -EINVAL; + if (!ret) + return -EINVAL; + + f = j->current_file; + if (!f) + return -EADDRNOTAVAIL; + + if (f->current_offset <= 0) + return -EADDRNOTAVAIL; + + r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o); + if (r < 0) + return r; + + *ret = le64toh(o->entry.realtime); + return 0; +} + +_public_ int sd_journal_get_monotonic_usec(sd_journal *j, uint64_t *ret, sd_id128_t *ret_boot_id) { + Object *o; + JournalFile *f; + int r; + sd_id128_t id; + + if (!j) + return -EINVAL; + if (!ret) + return -EINVAL; + + f = j->current_file; + if (!f) + return -EADDRNOTAVAIL; + + if (f->current_offset <= 0) + return -EADDRNOTAVAIL; + + r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o); + if (r < 0) + return r; + + if (ret_boot_id) + *ret_boot_id = o->entry.boot_id; + else { + r = sd_id128_get_boot(&id); + if (r < 0) + return r; + + if (!sd_id128_equal(id, o->entry.boot_id)) + return -ESTALE; + } + + *ret = le64toh(o->entry.monotonic); + return 0; +} + +_public_ int sd_journal_get_data(sd_journal *j, const char *field, const void **data, size_t *size) { + JournalFile *f; + uint64_t i, n; + size_t field_length; + int r; + Object *o; + + if (!j) + return -EINVAL; + if (!field) + return -EINVAL; + if (!data) + return -EINVAL; + if (!size) + return -EINVAL; + + if (isempty(field) || strchr(field, '=')) + return -EINVAL; + + f = j->current_file; + if (!f) + return -EADDRNOTAVAIL; + + if (f->current_offset <= 0) + return -EADDRNOTAVAIL; + + r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o); + if (r < 0) + return r; + + field_length = strlen(field); + + n = journal_file_entry_n_items(o); + for (i = 0; i < n; i++) { + uint64_t p, l, le_hash; + size_t t; + + p = le64toh(o->entry.items[i].object_offset); + le_hash = o->entry.items[i].hash; + r = journal_file_move_to_object(f, OBJECT_DATA, p, &o); + if (r < 0) + return r; + + if (le_hash != o->data.hash) + return -EBADMSG; + + l = le64toh(o->object.size) - offsetof(Object, data.payload); + + if (o->object.flags & OBJECT_COMPRESSED) { + +#ifdef HAVE_XZ + if (uncompress_startswith(o->data.payload, l, + &f->compress_buffer, &f->compress_buffer_size, + field, field_length, '=')) { + + uint64_t rsize; + + if (!uncompress_blob(o->data.payload, l, + &f->compress_buffer, &f->compress_buffer_size, &rsize)) + return -EBADMSG; + + *data = f->compress_buffer; + *size = (size_t) rsize; + + return 0; + } +#else + return -EPROTONOSUPPORT; +#endif + + } else if (l >= field_length+1 && + memcmp(o->data.payload, field, field_length) == 0 && + o->data.payload[field_length] == '=') { + + t = (size_t) l; + + if ((uint64_t) t != l) + return -E2BIG; + + *data = o->data.payload; + *size = t; + + return 0; + } + + r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o); + if (r < 0) + return r; + } + + return -ENOENT; +} + +_public_ int sd_journal_enumerate_data(sd_journal *j, const void **data, size_t *size) { + JournalFile *f; + uint64_t p, l, n, le_hash; + int r; + Object *o; + size_t t; + + if (!j) + return -EINVAL; + if (!data) + return -EINVAL; + if (!size) + return -EINVAL; + + f = j->current_file; + if (!f) + return -EADDRNOTAVAIL; + + if (f->current_offset <= 0) + return -EADDRNOTAVAIL; + + r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o); + if (r < 0) + return r; + + n = journal_file_entry_n_items(o); + if (j->current_field >= n) + return 0; + + p = le64toh(o->entry.items[j->current_field].object_offset); + le_hash = o->entry.items[j->current_field].hash; + r = journal_file_move_to_object(f, OBJECT_DATA, p, &o); + if (r < 0) + return r; + + if (le_hash != o->data.hash) + return -EBADMSG; + + l = le64toh(o->object.size) - offsetof(Object, data.payload); + t = (size_t) l; + + /* We can't read objects larger than 4G on a 32bit machine */ + if ((uint64_t) t != l) + return -E2BIG; + + if (o->object.flags & OBJECT_COMPRESSED) { +#ifdef HAVE_XZ + uint64_t rsize; + + if (!uncompress_blob(o->data.payload, l, &f->compress_buffer, &f->compress_buffer_size, &rsize)) + return -EBADMSG; + + *data = f->compress_buffer; + *size = (size_t) rsize; +#else + return -EPROTONOSUPPORT; +#endif + } else { + *data = o->data.payload; + *size = t; + } + + j->current_field ++; + + return 1; +} + +_public_ void sd_journal_restart_data(sd_journal *j) { + if (!j) + return; + + j->current_field = 0; +} + +_public_ int sd_journal_get_fd(sd_journal *j) { + if (!j) + return -EINVAL; + + return j->inotify_fd; +} + +static void process_inotify_event(sd_journal *j, struct inotify_event *e) { + char *p; + int r; + + assert(j); + assert(e); + + /* Is this a subdirectory we watch? */ + p = hashmap_get(j->inotify_wd_dirs, INT_TO_PTR(e->wd)); + if (p) { + + if (!(e->mask & IN_ISDIR) && e->len > 0 && endswith(e->name, ".journal")) { + + /* Event for a journal file */ + + if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) { + r = add_file(j, p, NULL, e->name); + if (r < 0) + log_debug("Failed to add file %s/%s: %s", p, e->name, strerror(-r)); + } else if (e->mask & (IN_DELETE|IN_UNMOUNT)) { + + r = remove_file(j, p, NULL, e->name); + if (r < 0) + log_debug("Failed to remove file %s/%s: %s", p, e->name, strerror(-r)); + } + + } else if (e->len == 0) { + + /* Event for the directory itself */ + + if (e->mask & (IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT)) + remove_directory_wd(j, e->wd); + } + + return; + } + + /* Must be the root directory then? */ + p = hashmap_get(j->inotify_wd_roots, INT_TO_PTR(e->wd)); + if (p) { + sd_id128_t id; + + if (!(e->mask & IN_ISDIR) && e->len > 0 && endswith(e->name, ".journal")) { + + /* Event for a journal file */ + + if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) { + r = add_file(j, p, NULL, e->name); + if (r < 0) + log_debug("Failed to add file %s/%s: %s", p, e->name, strerror(-r)); + } else if (e->mask & (IN_DELETE|IN_UNMOUNT)) { + + r = remove_file(j, p, NULL, e->name); + if (r < 0) + log_debug("Failed to remove file %s/%s: %s", p, e->name, strerror(-r)); + } + + } else if ((e->mask & IN_ISDIR) && e->len > 0 && sd_id128_from_string(e->name, &id) >= 0) { + + /* Event for subdirectory */ + + if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) { + + r = add_directory(j, p, e->name); + if (r < 0) + log_debug("Failed to add directory %s/%s: %s", p, e->name, strerror(-r)); + } + } + + return; + } + + if (e->mask & IN_IGNORED) + return; + + log_warning("Unknown inotify event."); +} + +_public_ int sd_journal_process(sd_journal *j) { + uint8_t buffer[sizeof(struct inotify_event) + FILENAME_MAX]; + + if (!j) + return -EINVAL; + + for (;;) { + struct inotify_event *e; + ssize_t l; + + l = read(j->inotify_fd, buffer, sizeof(buffer)); + if (l < 0) { + if (errno == EINTR || errno == EAGAIN) + return 0; + + return -errno; + } + + e = (struct inotify_event*) buffer; + while (l > 0) { + size_t step; + + process_inotify_event(j, e); + + step = sizeof(struct inotify_event) + e->len; + assert(step <= (size_t) l); + + e = (struct inotify_event*) ((uint8_t*) e + step); + l -= step; + } + } +} + +/* _public_ int sd_journal_query_unique(sd_journal *j, const char *field) { */ +/* if (!j) */ +/* return -EINVAL; */ +/* if (!field) */ +/* return -EINVAL; */ + +/* return -ENOTSUP; */ +/* } */ + +/* _public_ int sd_journal_enumerate_unique(sd_journal *j, const void **data, size_t *l) { */ +/* if (!j) */ +/* return -EINVAL; */ +/* if (!data) */ +/* return -EINVAL; */ +/* if (!l) */ +/* return -EINVAL; */ + +/* return -ENOTSUP; */ +/* } */ + +/* _public_ void sd_journal_restart_unique(sd_journal *j) { */ +/* if (!j) */ +/* return; */ +/* } */ diff --git a/src/journal/systemd-journald.conf b/src/journal/systemd-journald.conf new file mode 100644 index 0000000000..710b0aa941 --- /dev/null +++ b/src/journal/systemd-journald.conf @@ -0,0 +1,25 @@ +# 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. +# +# See system-journald.conf(5) for details + +[Journal] +#Compress=yes +#RateLimitInterval=10s +#RateLimitBurst=200 +#SystemMaxUse= +#SystemKeepFree= +#SystemMaxFileSize= +#SystemMinFileSize= +#RuntimeMaxUse= +#RuntimeKeepFree= +#RuntimeMaxFileSize= +#RuntimeMinFileSize= +#ForwardToSyslog=yes +#ForwardToKMsg=no +#ForwardToConsole=no +#ImportKernel=yes diff --git a/src/journal/test-journal.c b/src/journal/test-journal.c new file mode 100644 index 0000000000..a023509b70 --- /dev/null +++ b/src/journal/test-journal.c @@ -0,0 +1,120 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2011 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 <fcntl.h> +#include <unistd.h> + +#include <systemd/sd-journal.h> + +#include "journal-file.h" +#include "log.h" + +int main(int argc, char *argv[]) { + dual_timestamp ts; + JournalFile *f; + struct iovec iovec; + static const char test[] = "test", test2[] = "test2"; + Object *o; + uint64_t p; + + log_set_max_level(LOG_DEBUG); + + unlink("test.journal"); + + assert_se(journal_file_open("test.journal", O_RDWR|O_CREAT, 0666, NULL, &f) == 0); + + dual_timestamp_get(&ts); + + iovec.iov_base = (void*) test; + iovec.iov_len = strlen(test); + assert_se(journal_file_append_entry(f, &ts, &iovec, 1, NULL, NULL, NULL) == 0); + + iovec.iov_base = (void*) test2; + iovec.iov_len = strlen(test2); + assert_se(journal_file_append_entry(f, &ts, &iovec, 1, NULL, NULL, NULL) == 0); + + iovec.iov_base = (void*) test; + iovec.iov_len = strlen(test); + assert_se(journal_file_append_entry(f, &ts, &iovec, 1, NULL, NULL, NULL) == 0); + + journal_file_dump(f); + + assert(journal_file_next_entry(f, NULL, 0, DIRECTION_DOWN, &o, &p) == 1); + assert(le64toh(o->entry.seqnum) == 1); + + assert(journal_file_next_entry(f, o, p, DIRECTION_DOWN, &o, &p) == 1); + assert(le64toh(o->entry.seqnum) == 2); + + assert(journal_file_next_entry(f, o, p, DIRECTION_DOWN, &o, &p) == 1); + assert(le64toh(o->entry.seqnum) == 3); + + assert(journal_file_next_entry(f, o, p, DIRECTION_DOWN, &o, &p) == 0); + + assert(journal_file_next_entry(f, NULL, 0, DIRECTION_DOWN, &o, &p) == 1); + assert(le64toh(o->entry.seqnum) == 1); + + assert(journal_file_skip_entry(f, o, p, 2, &o, &p) == 1); + assert(le64toh(o->entry.seqnum) == 3); + + assert(journal_file_skip_entry(f, o, p, -2, &o, &p) == 1); + assert(le64toh(o->entry.seqnum) == 1); + + assert(journal_file_skip_entry(f, o, p, -2, &o, &p) == 1); + assert(le64toh(o->entry.seqnum) == 1); + + assert(journal_file_find_data_object(f, test, strlen(test), NULL, &p) == 1); + assert(journal_file_next_entry_for_data(f, NULL, 0, p, DIRECTION_DOWN, &o, NULL) == 1); + assert(le64toh(o->entry.seqnum) == 1); + + assert(journal_file_next_entry_for_data(f, NULL, 0, p, DIRECTION_UP, &o, NULL) == 1); + assert(le64toh(o->entry.seqnum) == 3); + + assert(journal_file_find_data_object(f, test2, strlen(test2), NULL, &p) == 1); + assert(journal_file_next_entry_for_data(f, NULL, 0, p, DIRECTION_UP, &o, NULL) == 1); + assert(le64toh(o->entry.seqnum) == 2); + + assert(journal_file_next_entry_for_data(f, NULL, 0, p, DIRECTION_DOWN, &o, NULL) == 1); + assert(le64toh(o->entry.seqnum) == 2); + + assert(journal_file_find_data_object(f, "quux", 4, NULL, &p) == 0); + + assert(journal_file_move_to_entry_by_seqnum(f, 1, DIRECTION_DOWN, &o, NULL) == 1); + assert(le64toh(o->entry.seqnum) == 1); + + assert(journal_file_move_to_entry_by_seqnum(f, 3, DIRECTION_DOWN, &o, NULL) == 1); + assert(le64toh(o->entry.seqnum) == 3); + + assert(journal_file_move_to_entry_by_seqnum(f, 2, DIRECTION_DOWN, &o, NULL) == 1); + assert(le64toh(o->entry.seqnum) == 2); + + assert(journal_file_move_to_entry_by_seqnum(f, 10, DIRECTION_DOWN, &o, NULL) == 0); + + journal_file_rotate(&f); + journal_file_rotate(&f); + + journal_file_close(f); + + journal_directory_vacuum(".", 3000000, 0); + + log_error("Exiting..."); + + return 0; +} diff --git a/src/kmod-setup.c b/src/kmod-setup.c index 97b7c870b1..dc3515676f 100644 --- a/src/kmod-setup.c +++ b/src/kmod-setup.c @@ -23,6 +23,7 @@ #include <unistd.h> #include <string.h> #include <errno.h> +#include <libkmod.h> #include "macro.h" #include "execute.h" @@ -35,13 +36,20 @@ static const char * const kmod_table[] = { "unix", "/proc/net/unix" }; +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +static void systemd_kmod_log(void *data, int priority, const char *file, int line, + const char *fn, const char *format, va_list args) +{ + log_meta(priority, file, line, fn, format, args); +} +#pragma GCC diagnostic pop + int kmod_setup(void) { - unsigned i, n = 0; - const char * cmdline[3 + ELEMENTSOF(kmod_table) + 1]; - ExecCommand command; - ExecContext context; - pid_t pid; - int r; + unsigned i; + struct kmod_ctx *ctx = NULL; + struct kmod_module *mod; + int err; for (i = 0; i < ELEMENTSOF(kmod_table); i += 2) { @@ -49,34 +57,40 @@ int kmod_setup(void) { continue; log_debug("Your kernel apparently lacks built-in %s support. Might be a good idea to compile it in. " - "We'll now try to work around this by calling '/sbin/modprobe %s'...", - kmod_table[i], kmod_table[i]); - - cmdline[3 + n++] = kmod_table[i]; - } + "We'll now try to work around this by loading the module...", + kmod_table[i]); - if (n <= 0) - return 0; + if (!ctx) { + ctx = kmod_new(NULL, NULL); + if (!ctx) { + log_error("Failed to allocate memory for kmod"); + return -ENOMEM; + } - cmdline[0] = "/sbin/modprobe"; - cmdline[1] = "-qab"; - cmdline[2] = "--"; - cmdline[3 + n] = NULL; + kmod_set_log_fn(ctx, systemd_kmod_log, NULL); - zero(command); - zero(context); + kmod_load_resources(ctx); + } - command.path = (char*) cmdline[0]; - command.argv = (char**) cmdline; + err = kmod_module_new_from_name(ctx, kmod_table[i], &mod); + if (err < 0) { + log_error("Failed to load module '%s'", kmod_table[i]); + continue; + } - exec_context_init(&context); - r = exec_spawn(&command, NULL, &context, NULL, 0, NULL, false, false, false, false, NULL, &pid); - exec_context_done(&context); + err = kmod_module_probe_insert_module(mod, KMOD_PROBE_APPLY_BLACKLIST, NULL, NULL, NULL, NULL); + if (err == 0) + log_info("Inserted module '%s'", kmod_module_get_name(mod)); + else if (err == KMOD_PROBE_APPLY_BLACKLIST) + log_info("Module '%s' is blacklisted", kmod_module_get_name(mod)); + else + log_error("Failed to insert '%s'", kmod_module_get_name(mod)); - if (r < 0) { - log_error("Failed to spawn %s: %s", cmdline[0], strerror(-r)); - return r; + kmod_module_unref(mod); } - return wait_for_terminate_and_warn(cmdline[0], pid); + if (ctx) + ctx = kmod_unref(ctx); + + return 0; } diff --git a/src/kmsg-syslogd.c b/src/kmsg-syslogd.c deleted file mode 100644 index 60d3244b3b..0000000000 --- a/src/kmsg-syslogd.c +++ /dev/null @@ -1,511 +0,0 @@ -/*-*- 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 <sys/socket.h> -#include <sys/types.h> -#include <assert.h> -#include <time.h> -#include <string.h> -#include <stdio.h> -#include <errno.h> -#include <unistd.h> -#include <sys/poll.h> -#include <sys/epoll.h> -#include <sys/un.h> -#include <fcntl.h> -#include <sys/signalfd.h> - -#include "util.h" -#include "log.h" -#include "sd-daemon.h" -#include "fdset.h" - -#define SERVER_FD_MAX 16 - -typedef struct Stream Stream; - -typedef struct Server { - FDSet *syslog_fds; - int kmsg_fd; - int epoll_fd; - int signal_fd; -} Server; - -static void server_done(Server *s) { - assert(s); - - if (s->epoll_fd >= 0) - close_nointr_nofail(s->epoll_fd); - - if (s->kmsg_fd >= 0) - close_nointr_nofail(s->kmsg_fd); - - if (s->signal_fd >= 0) - close_nointr_nofail(s->signal_fd); - - if (s->syslog_fds) - fdset_free(s->syslog_fds); -} - -static int server_init(Server *s, unsigned n_sockets) { - int r; - unsigned i; - struct epoll_event ev; - sigset_t mask; - - assert(s); - assert(n_sockets > 0); - - zero(*s); - - s->kmsg_fd = s->signal_fd = -1; - - if ((s->epoll_fd = epoll_create1(EPOLL_CLOEXEC)) < 0) { - r = -errno; - log_error("Failed to create epoll object: %s", strerror(errno)); - goto fail; - } - - if (!(s->syslog_fds = fdset_new())) { - r = -ENOMEM; - log_error("Failed to allocate file descriptor set: %s", strerror(errno)); - goto fail; - } - - for (i = 0; i < n_sockets; i++) { - int fd, one = 1; - - fd = SD_LISTEN_FDS_START+i; - - if ((r = sd_is_socket(fd, AF_UNSPEC, SOCK_DGRAM, -1)) < 0) { - log_error("Failed to determine file descriptor type: %s", strerror(-r)); - goto fail; - } - - if (!r) { - log_error("Wrong file descriptor type."); - r = -EINVAL; - goto fail; - } - - if (setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)) < 0) - log_error("SO_PASSCRED failed: %m"); - - zero(ev); - ev.events = EPOLLIN; - ev.data.fd = fd; - if (epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0) { - r = -errno; - log_error("Failed to add server fd to epoll object: %s", strerror(errno)); - goto fail; - } - - if ((r = fdset_put(s->syslog_fds, fd)) < 0) { - log_error("Failed to store file descriptor in set: %s", strerror(-r)); - goto fail; - } - } - - if ((s->kmsg_fd = open("/dev/kmsg", O_WRONLY|O_NOCTTY|O_CLOEXEC)) < 0) { - log_error("Failed to open /dev/kmsg for logging: %m"); - return -errno; - } - - assert_se(sigemptyset(&mask) == 0); - sigset_add_many(&mask, SIGINT, SIGTERM, -1); - assert_se(sigprocmask(SIG_SETMASK, &mask, NULL) == 0); - - if ((s->signal_fd = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC)) < 0) { - log_error("signalfd(): %m"); - return -errno; - } - - zero(ev); - ev.events = EPOLLIN; - ev.data.fd = s->signal_fd; - - if (epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, s->signal_fd, &ev) < 0) { - log_error("epoll_ctl(): %m"); - return -errno; - } - - return 0; - -fail: - server_done(s); - return r; -} - -static void skip_date(const char **buf) { - enum { - LETTER, - SPACE, - NUMBER, - SPACE_OR_NUMBER, - COLON - } sequence[] = { - LETTER, LETTER, LETTER, - SPACE, - SPACE_OR_NUMBER, NUMBER, - SPACE, - SPACE_OR_NUMBER, NUMBER, - COLON, - SPACE_OR_NUMBER, NUMBER, - COLON, - SPACE_OR_NUMBER, NUMBER, - SPACE - }; - - const char *p; - unsigned i; - - assert(buf); - assert(*buf); - - p = *buf; - - for (i = 0; i < ELEMENTSOF(sequence); i++, p++) { - - if (!*p) - return; - - switch (sequence[i]) { - - case SPACE: - if (*p != ' ') - return; - break; - - case SPACE_OR_NUMBER: - if (*p == ' ') - break; - - /* fall through */ - - case NUMBER: - if (*p < '0' || *p > '9') - return; - - break; - - case LETTER: - if (!(*p >= 'A' && *p <= 'Z') && - !(*p >= 'a' && *p <= 'z')) - return; - - break; - - case COLON: - if (*p != ':') - return; - break; - - } - } - - *buf = p; -} - -static int read_process(const char **buf, struct iovec *iovec) { - const char *p; - size_t l; - - assert(buf); - assert(*buf); - assert(iovec); - - p = *buf; - - p += strspn(p, WHITESPACE); - l = strcspn(p, WHITESPACE); - - if (l <= 0 || - p[l-1] != ':') - return 0; - - l--; - - if (p[l-1] == ']') { - size_t k = l-1; - - for (;;) { - - if (p[k] == '[') { - l = k; - break; - } - - if (k == 0) - break; - - k--; - } - } - - iovec->iov_base = (char*) p; - iovec->iov_len = l; - *buf = p + l; - return 1; -} - -static void skip_pid(const char **buf) { - const char *p; - - assert(buf); - assert(*buf); - - p = *buf; - - if (*p != '[') - return; - - p++; - p += strspn(p, "0123456789"); - - if (*p != ']') - return; - - p++; - - *buf = p; -} - -static int write_message(Server *s, const char *buf, struct ucred *ucred) { - ssize_t k; - char priority[6], pid[16]; - struct iovec iovec[5]; - unsigned i = 0; - char *process = NULL; - int r = 0; - int prio = LOG_USER | LOG_INFO; - - assert(s); - assert(buf); - - parse_syslog_priority((char**) &buf, &prio); - - if (*buf == 0) - return 0; - - if ((prio & LOG_FACMASK) == 0) - prio = LOG_USER | LOG_PRI(prio); - - /* First, set priority field */ - snprintf(priority, sizeof(priority), "<%i>", prio); - char_array_0(priority); - IOVEC_SET_STRING(iovec[i++], priority); - - /* Second, skip date */ - skip_date(&buf); - - /* Then, add process if set */ - if (read_process(&buf, &iovec[i]) > 0) - i++; - else if (ucred && - ucred->pid > 0 && - get_process_name(ucred->pid, &process) >= 0) - IOVEC_SET_STRING(iovec[i++], process); - - /* Skip the stored PID if we have a better one */ - if (ucred) { - snprintf(pid, sizeof(pid), "[%lu]: ", (unsigned long) ucred->pid); - char_array_0(pid); - IOVEC_SET_STRING(iovec[i++], pid); - - skip_pid(&buf); - - if (*buf == ':') - buf++; - - buf += strspn(buf, WHITESPACE); - } - - /* Is the remaining message empty? */ - if (*buf) { - - /* And the rest is the message */ - IOVEC_SET_STRING(iovec[i++], buf); - IOVEC_SET_STRING(iovec[i++], "\n"); - - if ((k = writev(s->kmsg_fd, iovec, i)) <= 0) { - log_error("Failed to write log message to kmsg: %s", k < 0 ? strerror(errno) : "short write"); - r = k < 0 ? -errno : -EIO; - } - } - - free(process); - - return r; -} - -static int process_event(Server *s, struct epoll_event *ev) { - assert(s); - - if (ev->events != EPOLLIN) { - log_info("Got invalid event from epoll."); - return -EIO; - } - - if (ev->data.fd == s->signal_fd) { - struct signalfd_siginfo sfsi; - ssize_t n; - - if ((n = read(s->signal_fd, &sfsi, sizeof(sfsi))) != sizeof(sfsi)) { - - if (n >= 0) - return -EIO; - - if (errno == EINTR || errno == EAGAIN) - return 0; - - return -errno; - } - - log_debug("Received SIG%s", strna(signal_to_string(sfsi.ssi_signo))); - return 0; - - } else { - for (;;) { - char buf[LINE_MAX+1]; - struct msghdr msghdr; - struct iovec iovec; - struct ucred *ucred; - union { - struct cmsghdr cmsghdr; - uint8_t buf[CMSG_SPACE(sizeof(struct ucred))]; - } control; - ssize_t n; - int k; - char *e; - - zero(iovec); - iovec.iov_base = buf; - iovec.iov_len = sizeof(buf)-1; - - zero(control); - zero(msghdr); - msghdr.msg_iov = &iovec; - msghdr.msg_iovlen = 1; - msghdr.msg_control = &control; - msghdr.msg_controllen = sizeof(control); - - if ((n = recvmsg(ev->data.fd, &msghdr, MSG_DONTWAIT)) < 0) { - - if (errno == EINTR || errno == EAGAIN) - return 1; - - log_error("recvmsg() failed: %m"); - return -errno; - } - - if (msghdr.msg_controllen >= CMSG_LEN(sizeof(struct ucred)) && - control.cmsghdr.cmsg_level == SOL_SOCKET && - control.cmsghdr.cmsg_type == SCM_CREDENTIALS && - control.cmsghdr.cmsg_len == CMSG_LEN(sizeof(struct ucred))) - ucred = (struct ucred*) CMSG_DATA(&control.cmsghdr); - else - ucred = NULL; - - if ((e = memchr(buf, '\n', n))) - *e = 0; - else - buf[n] = 0; - - if ((k = write_message(s, strstrip(buf), ucred)) < 0) - return k; - } - } - - return 1; -} - -int main(int argc, char *argv[]) { - Server server; - int r = EXIT_FAILURE, n; - - if (getppid() != 1) { - log_error("This program should be invoked by init only."); - return EXIT_FAILURE; - } - - if (argc > 1) { - log_error("This program does not take arguments."); - return EXIT_FAILURE; - } - - log_set_target(LOG_TARGET_KMSG); - log_parse_environment(); - log_open(); - - if ((n = sd_listen_fds(true)) < 0) { - log_error("Failed to read listening file descriptors from environment: %s", strerror(-r)); - return EXIT_FAILURE; - } - - if (n <= 0 || n > SERVER_FD_MAX) { - log_error("No or too many file descriptors passed."); - return EXIT_FAILURE; - } - - if (server_init(&server, (unsigned) n) < 0) - return EXIT_FAILURE; - - log_debug("systemd-kmsg-syslogd running as pid %lu", (unsigned long) getpid()); - - sd_notify(false, - "READY=1\n" - "STATUS=Processing messages..."); - - for (;;) { - struct epoll_event event; - int k; - - if ((k = epoll_wait(server.epoll_fd, &event, 1, -1)) < 0) { - - if (errno == EINTR) - continue; - - log_error("epoll_wait() failed: %m"); - goto fail; - } - - if (k <= 0) - break; - - if ((k = process_event(&server, &event)) < 0) - goto fail; - - if (k == 0) - break; - } - - r = EXIT_SUCCESS; - - log_debug("systemd-kmsg-syslogd stopped as pid %lu", (unsigned long) getpid()); - -fail: - sd_notify(false, - "STATUS=Shutting down..."); - - server_done(&server); - - return r; -} diff --git a/src/label.c b/src/label.c index b7bb03322f..2c887a0fe5 100644 --- a/src/label.c +++ b/src/label.c @@ -23,6 +23,8 @@ #include <sys/stat.h> #include <unistd.h> #include <malloc.h> +#include <sys/socket.h> +#include <sys/un.h> #include "label.h" #include "util.h" @@ -33,13 +35,18 @@ static struct selabel_handle *label_hnd = NULL; +static int use_selinux_cached = -1; + static inline bool use_selinux(void) { - static int use_selinux_ind = -1; - if (use_selinux_ind < 0) - use_selinux_ind = is_selinux_enabled() > 0; + if (use_selinux_cached < 0) + use_selinux_cached = is_selinux_enabled() > 0; + + return use_selinux_cached; +} - return use_selinux_ind; +void label_retest_selinux(void) { + use_selinux_cached = -1; } #endif @@ -48,8 +55,8 @@ int label_init(void) { int r = 0; #ifdef HAVE_SELINUX - usec_t n; - struct mallinfo before; + usec_t before_timestamp, after_timestamp; + struct mallinfo before_mallinfo, after_mallinfo; if (!use_selinux()) return 0; @@ -57,8 +64,8 @@ int label_init(void) { if (label_hnd) return 0; - before = mallinfo(); - n = now(CLOCK_MONOTONIC); + before_mallinfo = mallinfo(); + before_timestamp = now(CLOCK_MONOTONIC); label_hnd = selabel_open(SELABEL_CTX_FILE, NULL, 0); if (!label_hnd) { @@ -66,18 +73,17 @@ int label_init(void) { "Failed to initialize SELinux context: %m"); r = security_getenforce() == 1 ? -errno : 0; } else { - char buf[FORMAT_TIMESPAN_MAX]; - struct mallinfo after; + char timespan[FORMAT_TIMESPAN_MAX]; int l; - n = now(CLOCK_MONOTONIC) - n; - after = mallinfo(); + after_timestamp = now(CLOCK_MONOTONIC); + after_mallinfo = mallinfo(); - l = after.uordblks > before.uordblks ? after.uordblks - before.uordblks : 0; + l = after_mallinfo.uordblks > before_mallinfo.uordblks ? after_mallinfo.uordblks - before_mallinfo.uordblks : 0; log_info("Successfully loaded SELinux database in %s, size on heap is %iK.", - format_timespan(buf, sizeof(buf), n), - l/1024); + format_timespan(timespan, sizeof(timespan), after_timestamp - before_timestamp), + (l+1023)/1024); } #endif @@ -103,7 +109,7 @@ int label_fix(const char *path, bool ignore_enoent) { return 0; if (r == 0) { - r = setfilecon(path, fcon); + r = lsetfilecon(path, fcon); freecon(fcon); /* If the FS doesn't support labels, then exit without warning */ @@ -134,7 +140,7 @@ void label_finish(void) { #endif } -int label_get_socket_label_from_exe(const char *exe, char **label) { +int label_get_create_label_from_exe(const char *exe, char **label) { int r = 0; @@ -180,8 +186,12 @@ int label_fifofile_set(const char *path) { if (!use_selinux() || !label_hnd) return 0; - if ((r = selabel_lookup_raw(label_hnd, &filecon, path, S_IFIFO)) == 0) { - if ((r = setfscreatecon(filecon)) < 0) { + r = selabel_lookup_raw(label_hnd, &filecon, path, S_IFIFO); + if (r < 0) + r = -errno; + else if (r == 0) { + r = setfscreatecon(filecon); + if (r < 0) { log_error("Failed to set SELinux file context on %s: %m", path); r = -errno; } @@ -205,8 +215,12 @@ int label_symlinkfile_set(const char *path) { if (!use_selinux() || !label_hnd) return 0; - if ((r = selabel_lookup_raw(label_hnd, &filecon, path, S_IFLNK)) == 0) { - if ((r = setfscreatecon(filecon)) < 0) { + r = selabel_lookup_raw(label_hnd, &filecon, path, S_IFLNK); + if (r < 0) + r = -errno; + else if (r == 0) { + r = setfscreatecon(filecon); + if (r < 0) { log_error("Failed to set SELinux file context on %s: %m", path); r = -errno; } @@ -269,9 +283,7 @@ void label_free(const char *label) { #endif } -int label_mkdir( - const char *path, - mode_t mode) { +int label_mkdir(const char *path, mode_t mode) { /* Creates a directory and labels it according to the SELinux policy */ @@ -279,43 +291,123 @@ int label_mkdir( int r; security_context_t fcon = NULL; - if (use_selinux() && label_hnd) { + if (!use_selinux() || !label_hnd) + goto skipped; - if (path_is_absolute(path)) - r = selabel_lookup_raw(label_hnd, &fcon, path, mode); - else { - char *newpath = NULL; + if (path_is_absolute(path)) + r = selabel_lookup_raw(label_hnd, &fcon, path, S_IFDIR); + else { + char *newpath; - if (!(newpath = path_make_absolute_cwd(path))) - return -ENOMEM; + newpath = path_make_absolute_cwd(path); + if (!newpath) + return -ENOMEM; - r = selabel_lookup_raw(label_hnd, &fcon, newpath, mode); - free(newpath); - } + r = selabel_lookup_raw(label_hnd, &fcon, newpath, S_IFDIR); + free(newpath); + } - if (r == 0) - r = setfscreatecon(fcon); + if (r == 0) + r = setfscreatecon(fcon); - if (r < 0 && errno != ENOENT) { - log_error("Failed to set security context %s for %s: %m", fcon, path); - r = -errno; + if (r < 0 && errno != ENOENT) { + log_error("Failed to set security context %s for %s: %m", fcon, path); - if (security_getenforce() == 1) - goto finish; + if (security_getenforce() == 1) { + r = -errno; + goto finish; } } - if ((r = mkdir(path, mode)) < 0) + r = mkdir(path, mode); + if (r < 0) r = -errno; finish: - if (use_selinux() && label_hnd) { - setfscreatecon(NULL); - freecon(fcon); + setfscreatecon(NULL); + freecon(fcon); + + return r; + +skipped: +#endif + return mkdir(path, mode) < 0 ? -errno : 0; +} + +int label_bind(int fd, const struct sockaddr *addr, socklen_t addrlen) { + + /* Binds a socket and label its file system object according to the SELinux policy */ + +#ifdef HAVE_SELINUX + int r; + security_context_t fcon = NULL; + const struct sockaddr_un *un; + char *path = NULL; + + assert(fd >= 0); + assert(addr); + assert(addrlen >= sizeof(sa_family_t)); + + if (!use_selinux() || !label_hnd) + goto skipped; + + /* Filter out non-local sockets */ + if (addr->sa_family != AF_UNIX) + goto skipped; + + /* Filter out anonymous sockets */ + if (addrlen < sizeof(sa_family_t) + 1) + goto skipped; + + /* Filter out abstract namespace sockets */ + un = (const struct sockaddr_un*) addr; + if (un->sun_path[0] == 0) + goto skipped; + + path = strndup(un->sun_path, addrlen - offsetof(struct sockaddr_un, sun_path)); + if (!path) + return -ENOMEM; + + if (path_is_absolute(path)) + r = selabel_lookup_raw(label_hnd, &fcon, path, S_IFSOCK); + else { + char *newpath; + + newpath = path_make_absolute_cwd(path); + + if (!newpath) { + free(path); + return -ENOMEM; + } + + r = selabel_lookup_raw(label_hnd, &fcon, newpath, S_IFSOCK); + free(newpath); } + if (r == 0) + r = setfscreatecon(fcon); + + if (r < 0 && errno != ENOENT) { + log_error("Failed to set security context %s for %s: %m", fcon, path); + + if (security_getenforce() == 1) { + r = -errno; + goto finish; + } + } + + r = bind(fd, addr, addrlen); + if (r < 0) + r = -errno; + +finish: + setfscreatecon(NULL); + freecon(fcon); + free(path); + return r; -#else - return mkdir(path, mode); + +skipped: #endif + return bind(fd, addr, addrlen) < 0 ? -errno : 0; } diff --git a/src/label.h b/src/label.h index 7ea11cdc55..ead44837a4 100644 --- a/src/label.h +++ b/src/label.h @@ -24,6 +24,7 @@ #include <sys/types.h> #include <stdbool.h> +#include <sys/socket.h> int label_init(void); void label_finish(void); @@ -39,8 +40,12 @@ void label_file_clear(void); void label_free(const char *label); -int label_get_socket_label_from_exe(const char *exe, char **label); +int label_get_create_label_from_exe(const char *exe, char **label); int label_mkdir(const char *path, mode_t mode); +void label_retest_selinux(void); + +int label_bind(int fd, const struct sockaddr *addr, socklen_t addrlen); + #endif diff --git a/libsystemd-daemon.pc.in b/src/libsystemd-daemon.pc.in index 8bb3a74c87..8bb3a74c87 100644 --- a/libsystemd-daemon.pc.in +++ b/src/libsystemd-daemon.pc.in diff --git a/units/sys-kernel-debug.automount b/src/libsystemd-id128.pc.in index 2f2de9829f..4d984fdff5 100644 --- a/units/sys-kernel-debug.automount +++ b/src/libsystemd-id128.pc.in @@ -5,11 +5,14 @@ # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. -[Unit] -Description=Debug File System Automount Point -DefaultDependencies=no -Before=sysinit.target -ConditionPathExists=/sys/kernel/debug +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ -[Automount] -Where=/sys/kernel/debug +Name: systemd +Description: systemd 128 Bit ID Utility Library +URL: @PACKAGE_URL@ +Version: @PACKAGE_VERSION@ +Libs: -L${libdir} -lsystemd-id128 +Cflags: -I${includedir} diff --git a/src/libsystemd-id128.sym b/src/libsystemd-id128.sym new file mode 100644 index 0000000000..2373fe6646 --- /dev/null +++ b/src/libsystemd-id128.sym @@ -0,0 +1,21 @@ +/*** + 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. +***/ + +/* Original symbols from systemd v38 */ + +LIBSYSTEMD_ID128_38 { +global: + sd_id128_to_string; + sd_id128_from_string; + sd_id128_randomize; + sd_id128_get_machine; + sd_id128_get_boot; +local: + *; +}; diff --git a/src/linux/Makefile b/src/linux/Makefile new file mode 120000 index 0000000000..d0b0e8e008 --- /dev/null +++ b/src/linux/Makefile @@ -0,0 +1 @@ +../Makefile
\ No newline at end of file diff --git a/src/load-dropin.c b/src/load-dropin.c index d30865cbc5..d869ee0c76 100644 --- a/src/load-dropin.c +++ b/src/load-dropin.c @@ -36,7 +36,8 @@ static int iterate_dir(Unit *u, const char *path, UnitDependency dependency) { assert(u); assert(path); - if (!(d = opendir(path))) { + d = opendir(path); + if (!d) { if (errno == ENOENT) return 0; @@ -50,7 +51,8 @@ static int iterate_dir(Unit *u, const char *path, UnitDependency dependency) { if (ignore_file(de->d_name)) continue; - if (asprintf(&f, "%s/%s", path, de->d_name) < 0) { + f = join(path, "/", de->d_name, NULL); + if (!f) { r = -ENOMEM; goto finish; } @@ -59,7 +61,7 @@ static int iterate_dir(Unit *u, const char *path, UnitDependency dependency) { free(f); if (r < 0) - goto finish; + log_error("Cannot add dependency %s to %s, ignoring: %s", de->d_name, u->id, strerror(-r)); } r = 0; @@ -78,11 +80,12 @@ static int process_dir(Unit *u, const char *unit_path, const char *name, const c assert(name); assert(suffix); - if (asprintf(&path, "%s/%s%s", unit_path, name, suffix) < 0) + path = join(unit_path, "/", name, suffix, NULL); + if (!path) return -ENOMEM; - if (u->meta.manager->unit_path_cache && - !set_get(u->meta.manager->unit_path_cache, path)) + if (u->manager->unit_path_cache && + !set_get(u->manager->unit_path_cache, path)) r = 0; else r = iterate_dir(u, path, dependency); @@ -91,21 +94,22 @@ static int process_dir(Unit *u, const char *unit_path, const char *name, const c if (r < 0) return r; - if (u->meta.instance) { + if (u->instance) { char *template; /* Also try the template dir */ - if (!(template = unit_name_template(name))) + template = unit_name_template(name); + if (!template) return -ENOMEM; - r = asprintf(&path, "%s/%s%s", unit_path, template, suffix); + path = join(unit_path, "/", template, suffix, NULL); free(template); - if (r < 0) + if (!path) return -ENOMEM; - if (u->meta.manager->unit_path_cache && - !set_get(u->meta.manager->unit_path_cache, path)) + if (u->manager->unit_path_cache && + !set_get(u->manager->unit_path_cache, path)) r = 0; else r = iterate_dir(u, path, dependency); @@ -126,16 +130,18 @@ int unit_load_dropin(Unit *u) { /* Load dependencies from supplementary drop-in directories */ - SET_FOREACH(t, u->meta.names, i) { + SET_FOREACH(t, u->names, i) { char **p; - STRV_FOREACH(p, u->meta.manager->lookup_paths.unit_path) { + STRV_FOREACH(p, u->manager->lookup_paths.unit_path) { int r; - if ((r = process_dir(u, *p, t, ".wants", UNIT_WANTS)) < 0) + r = process_dir(u, *p, t, ".wants", UNIT_WANTS); + if (r < 0) return r; - if ((r = process_dir(u, *p, t, ".requires", UNIT_REQUIRES)) < 0) + r = process_dir(u, *p, t, ".requires", UNIT_REQUIRES); + if (r < 0) return r; } } diff --git a/src/load-fragment-gperf.gperf.m4 b/src/load-fragment-gperf.gperf.m4 new file mode 100644 index 0000000000..44ce4bbbc4 --- /dev/null +++ b/src/load-fragment-gperf.gperf.m4 @@ -0,0 +1,229 @@ +%{ +#include <stddef.h> +#include "conf-parser.h" +#include "load-fragment.h" +#include "missing.h" +%} +struct ConfigPerfItem; +%null_strings +%language=ANSI-C +%define slot-name section_and_lvalue +%define hash-function-name load_fragment_gperf_hash +%define lookup-function-name load_fragment_gperf_lookup +%readonly-tables +%omit-struct-type +%struct-type +%includes +%% +m4_dnl Define the context options only once +m4_define(`EXEC_CONTEXT_CONFIG_ITEMS', +`$1.WorkingDirectory, config_parse_unit_path_printf, 0, offsetof($1, exec_context.working_directory) +$1.RootDirectory, config_parse_unit_path_printf, 0, offsetof($1, exec_context.root_directory) +$1.User, config_parse_unit_string_printf, 0, offsetof($1, exec_context.user) +$1.Group, config_parse_unit_string_printf, 0, offsetof($1, exec_context.group) +$1.SupplementaryGroups, config_parse_strv, 0, offsetof($1, exec_context.supplementary_groups) +$1.Nice, config_parse_exec_nice, 0, offsetof($1, exec_context) +$1.OOMScoreAdjust, config_parse_exec_oom_score_adjust, 0, offsetof($1, exec_context) +$1.IOSchedulingClass, config_parse_exec_io_class, 0, offsetof($1, exec_context) +$1.IOSchedulingPriority, config_parse_exec_io_priority, 0, offsetof($1, exec_context) +$1.CPUSchedulingPolicy, config_parse_exec_cpu_sched_policy, 0, offsetof($1, exec_context) +$1.CPUSchedulingPriority, config_parse_exec_cpu_sched_prio, 0, offsetof($1, exec_context) +$1.CPUSchedulingResetOnFork, config_parse_bool, 0, offsetof($1, exec_context.cpu_sched_reset_on_fork) +$1.CPUAffinity, config_parse_exec_cpu_affinity, 0, offsetof($1, exec_context) +$1.UMask, config_parse_mode, 0, offsetof($1, exec_context.umask) +$1.Environment, config_parse_unit_strv_printf, 0, offsetof($1, exec_context.environment) +$1.EnvironmentFile, config_parse_unit_env_file, 0, offsetof($1, exec_context.environment_files) +$1.StandardInput, config_parse_input, 0, offsetof($1, exec_context.std_input) +$1.StandardOutput, config_parse_output, 0, offsetof($1, exec_context.std_output) +$1.StandardError, config_parse_output, 0, offsetof($1, exec_context.std_error) +$1.TTYPath, config_parse_unit_path_printf, 0, offsetof($1, exec_context.tty_path) +$1.TTYReset, config_parse_bool, 0, offsetof($1, exec_context.tty_reset) +$1.TTYVHangup, config_parse_bool, 0, offsetof($1, exec_context.tty_vhangup) +$1.TTYVTDisallocate, config_parse_bool, 0, offsetof($1, exec_context.tty_vt_disallocate) +$1.SyslogIdentifier, config_parse_unit_string_printf, 0, offsetof($1, exec_context.syslog_identifier) +$1.SyslogFacility, config_parse_facility, 0, offsetof($1, exec_context.syslog_priority) +$1.SyslogLevel, config_parse_level, 0, offsetof($1, exec_context.syslog_priority) +$1.SyslogLevelPrefix, config_parse_bool, 0, offsetof($1, exec_context.syslog_level_prefix) +$1.Capabilities, config_parse_exec_capabilities, 0, offsetof($1, exec_context) +$1.SecureBits, config_parse_exec_secure_bits, 0, offsetof($1, exec_context) +$1.CapabilityBoundingSet, config_parse_exec_bounding_set, 0, offsetof($1, exec_context) +$1.TimerSlackNSec, config_parse_exec_timer_slack_nsec, 0, offsetof($1, exec_context) +$1.LimitCPU, config_parse_limit, RLIMIT_CPU, offsetof($1, exec_context.rlimit) +$1.LimitFSIZE, config_parse_limit, RLIMIT_FSIZE, offsetof($1, exec_context.rlimit) +$1.LimitDATA, config_parse_limit, RLIMIT_DATA, offsetof($1, exec_context.rlimit) +$1.LimitSTACK, config_parse_limit, RLIMIT_STACK, offsetof($1, exec_context.rlimit) +$1.LimitCORE, config_parse_limit, RLIMIT_CORE, offsetof($1, exec_context.rlimit) +$1.LimitRSS, config_parse_limit, RLIMIT_RSS, offsetof($1, exec_context.rlimit) +$1.LimitNOFILE, config_parse_limit, RLIMIT_NOFILE, offsetof($1, exec_context.rlimit) +$1.LimitAS, config_parse_limit, RLIMIT_AS, offsetof($1, exec_context.rlimit) +$1.LimitNPROC, config_parse_limit, RLIMIT_NPROC, offsetof($1, exec_context.rlimit) +$1.LimitMEMLOCK, config_parse_limit, RLIMIT_MEMLOCK, offsetof($1, exec_context.rlimit) +$1.LimitLOCKS, config_parse_limit, RLIMIT_LOCKS, offsetof($1, exec_context.rlimit) +$1.LimitSIGPENDING, config_parse_limit, RLIMIT_SIGPENDING, offsetof($1, exec_context.rlimit) +$1.LimitMSGQUEUE, config_parse_limit, RLIMIT_MSGQUEUE, offsetof($1, exec_context.rlimit) +$1.LimitNICE, config_parse_limit, RLIMIT_NICE, offsetof($1, exec_context.rlimit) +$1.LimitRTPRIO, config_parse_limit, RLIMIT_RTPRIO, offsetof($1, exec_context.rlimit) +$1.LimitRTTIME, config_parse_limit, RLIMIT_RTTIME, offsetof($1, exec_context.rlimit) +$1.ControlGroup, config_parse_unit_cgroup, 0, 0 +$1.ControlGroupAttribute, config_parse_unit_cgroup_attr, 0, 0 +$1.CPUShares, config_parse_unit_cpu_shares, 0, 0 +$1.MemoryLimit, config_parse_unit_memory_limit, 0, 0 +$1.MemorySoftLimit, config_parse_unit_memory_limit, 0, 0 +$1.DeviceAllow, config_parse_unit_device_allow, 0, 0 +$1.DeviceDeny, config_parse_unit_device_allow, 0, 0 +$1.BlockIOWeight, config_parse_unit_blkio_weight, 0, 0 +$1.BlockIOReadBandwidth, config_parse_unit_blkio_bandwidth, 0, 0 +$1.BlockIOWriteBandwidth, config_parse_unit_blkio_bandwidth, 0, 0 +$1.ReadWriteDirectories, config_parse_path_strv, 0, offsetof($1, exec_context.read_write_dirs) +$1.ReadOnlyDirectories, config_parse_path_strv, 0, offsetof($1, exec_context.read_only_dirs) +$1.InaccessibleDirectories, config_parse_path_strv, 0, offsetof($1, exec_context.inaccessible_dirs) +$1.PrivateTmp, config_parse_bool, 0, offsetof($1, exec_context.private_tmp) +$1.PrivateNetwork, config_parse_bool, 0, offsetof($1, exec_context.private_network) +$1.MountFlags, config_parse_exec_mount_flags, 0, offsetof($1, exec_context) +$1.TCPWrapName, config_parse_unit_string_printf, 0, offsetof($1, exec_context.tcpwrap_name) +$1.PAMName, config_parse_unit_string_printf, 0, offsetof($1, exec_context.pam_name) +$1.KillMode, config_parse_kill_mode, 0, offsetof($1, exec_context.kill_mode) +$1.KillSignal, config_parse_kill_signal, 0, offsetof($1, exec_context.kill_signal) +$1.SendSIGKILL, config_parse_bool, 0, offsetof($1, exec_context.send_sigkill) +$1.IgnoreSIGPIPE, config_parse_bool, 0, offsetof($1, exec_context.ignore_sigpipe) +$1.UtmpIdentifier, config_parse_unit_string_printf, 0, offsetof($1, exec_context.utmp_id) +$1.ControlGroupModify, config_parse_bool, 0, offsetof($1, exec_context.control_group_modify) +$1.ControlGroupPersistent, config_parse_tristate, 0, offsetof($1, exec_context.control_group_persistent)' +)m4_dnl +Unit.Names, config_parse_unit_names, 0, 0 +Unit.Description, config_parse_unit_string_printf, 0, offsetof(Unit, description) +Unit.Requires, config_parse_unit_deps, UNIT_REQUIRES, 0 +Unit.RequiresOverridable, config_parse_unit_deps, UNIT_REQUIRES_OVERRIDABLE, 0 +Unit.Requisite, config_parse_unit_deps, UNIT_REQUISITE, 0 +Unit.RequisiteOverridable, config_parse_unit_deps, UNIT_REQUISITE_OVERRIDABLE, 0 +Unit.Wants, config_parse_unit_deps, UNIT_WANTS, 0 +Unit.BindTo, config_parse_unit_deps, UNIT_BIND_TO, 0 +Unit.Conflicts, config_parse_unit_deps, UNIT_CONFLICTS, 0 +Unit.Before, config_parse_unit_deps, UNIT_BEFORE, 0 +Unit.After, config_parse_unit_deps, UNIT_AFTER, 0 +Unit.OnFailure, config_parse_unit_deps, UNIT_ON_FAILURE, 0 +Unit.PropagateReloadTo, config_parse_unit_deps, UNIT_PROPAGATE_RELOAD_TO, 0 +Unit.PropagateReloadFrom, config_parse_unit_deps, UNIT_PROPAGATE_RELOAD_FROM, 0 +Unit.StopWhenUnneeded, config_parse_bool, 0, offsetof(Unit, stop_when_unneeded) +Unit.RefuseManualStart, config_parse_bool, 0, offsetof(Unit, refuse_manual_start) +Unit.RefuseManualStop, config_parse_bool, 0, offsetof(Unit, refuse_manual_stop) +Unit.AllowIsolate, config_parse_bool, 0, offsetof(Unit, allow_isolate) +Unit.DefaultDependencies, config_parse_bool, 0, offsetof(Unit, default_dependencies) +Unit.OnFailureIsolate, config_parse_bool, 0, offsetof(Unit, on_failure_isolate) +Unit.IgnoreOnIsolate, config_parse_bool, 0, offsetof(Unit, ignore_on_isolate) +Unit.IgnoreOnSnapshot, config_parse_bool, 0, offsetof(Unit, ignore_on_snapshot) +Unit.JobTimeoutSec, config_parse_usec, 0, offsetof(Unit, job_timeout) +Unit.ConditionPathExists, config_parse_unit_condition_path, CONDITION_PATH_EXISTS, 0 +Unit.ConditionPathExistsGlob, config_parse_unit_condition_path, CONDITION_PATH_EXISTS_GLOB, 0 +Unit.ConditionPathIsDirectory, config_parse_unit_condition_path, CONDITION_PATH_IS_DIRECTORY, 0 +Unit.ConditionPathIsSymbolicLink,config_parse_unit_condition_path, CONDITION_PATH_IS_SYMBOLIC_LINK,0 +Unit.ConditionPathIsMountPoint, config_parse_unit_condition_path, CONDITION_PATH_IS_MOUNT_POINT, 0 +Unit.ConditionDirectoryNotEmpty, config_parse_unit_condition_path, CONDITION_DIRECTORY_NOT_EMPTY, 0 +Unit.ConditionFileIsExecutable, config_parse_unit_condition_path, CONDITION_FILE_IS_EXECUTABLE, 0 +Unit.ConditionKernelCommandLine, config_parse_unit_condition_string, CONDITION_KERNEL_COMMAND_LINE, 0 +Unit.ConditionVirtualization, config_parse_unit_condition_string, CONDITION_VIRTUALIZATION, 0 +Unit.ConditionSecurity, config_parse_unit_condition_string, CONDITION_SECURITY, 0 +Unit.ConditionCapability, config_parse_unit_condition_string, CONDITION_CAPABILITY, 0 +Unit.ConditionNull, config_parse_unit_condition_null, 0, 0 +m4_dnl +Service.PIDFile, config_parse_unit_path_printf, 0, offsetof(Service, pid_file) +Service.ExecStartPre, config_parse_exec, SERVICE_EXEC_START_PRE, offsetof(Service, exec_command) +Service.ExecStart, config_parse_exec, SERVICE_EXEC_START, offsetof(Service, exec_command) +Service.ExecStartPost, config_parse_exec, SERVICE_EXEC_START_POST, offsetof(Service, exec_command) +Service.ExecReload, config_parse_exec, SERVICE_EXEC_RELOAD, offsetof(Service, exec_command) +Service.ExecStop, config_parse_exec, SERVICE_EXEC_STOP, offsetof(Service, exec_command) +Service.ExecStopPost, config_parse_exec, SERVICE_EXEC_STOP_POST, offsetof(Service, exec_command) +Service.RestartSec, config_parse_usec, 0, offsetof(Service, restart_usec) +Service.TimeoutSec, config_parse_usec, 0, offsetof(Service, timeout_usec) +Service.WatchdogSec, config_parse_usec, 0, offsetof(Service, watchdog_usec) +Service.StartLimitInterval, config_parse_usec, 0, offsetof(Service, start_limit.interval) +Service.StartLimitBurst, config_parse_unsigned, 0, offsetof(Service, start_limit.burst) +Service.StartLimitAction, config_parse_start_limit_action, 0, offsetof(Service, start_limit_action) +Service.Type, config_parse_service_type, 0, offsetof(Service, type) +Service.Restart, config_parse_service_restart, 0, offsetof(Service, restart) +Service.PermissionsStartOnly, config_parse_bool, 0, offsetof(Service, permissions_start_only) +Service.RootDirectoryStartOnly, config_parse_bool, 0, offsetof(Service, root_directory_start_only) +Service.RemainAfterExit, config_parse_bool, 0, offsetof(Service, remain_after_exit) +Service.GuessMainPID, config_parse_bool, 0, offsetof(Service, guess_main_pid) +m4_ifdef(`HAVE_SYSV_COMPAT', +`Service.SysVStartPriority, config_parse_sysv_priority, 0, offsetof(Service, sysv_start_priority)', +`Service.SysVStartPriority, config_parse_warn_compat, 0, 0') +Service.NonBlocking, config_parse_bool, 0, offsetof(Service, exec_context.non_blocking) +Service.BusName, config_parse_unit_string_printf, 0, offsetof(Service, bus_name) +Service.NotifyAccess, config_parse_notify_access, 0, offsetof(Service, notify_access) +Service.Sockets, config_parse_service_sockets, 0, 0 +Service.FsckPassNo, config_parse_fsck_passno, 0, offsetof(Service, fsck_passno) +EXEC_CONTEXT_CONFIG_ITEMS(Service)m4_dnl +m4_dnl +Socket.ListenStream, config_parse_socket_listen, 0, 0 +Socket.ListenDatagram, config_parse_socket_listen, 0, 0 +Socket.ListenSequentialPacket, config_parse_socket_listen, 0, 0 +Socket.ListenFIFO, config_parse_socket_listen, 0, 0 +Socket.ListenNetlink, config_parse_socket_listen, 0, 0 +Socket.ListenSpecial, config_parse_socket_listen, 0, 0 +Socket.ListenMessageQueue, config_parse_socket_listen, 0, 0 +Socket.BindIPv6Only, config_parse_socket_bind, 0, 0, +Socket.Backlog, config_parse_unsigned, 0, offsetof(Socket, backlog) +Socket.BindToDevice, config_parse_socket_bindtodevice, 0, 0 +Socket.ExecStartPre, config_parse_exec, SOCKET_EXEC_START_PRE, offsetof(Socket, exec_command) +Socket.ExecStartPost, config_parse_exec, SOCKET_EXEC_START_POST, offsetof(Socket, exec_command) +Socket.ExecStopPre, config_parse_exec, SOCKET_EXEC_STOP_PRE, offsetof(Socket, exec_command) +Socket.ExecStopPost, config_parse_exec, SOCKET_EXEC_STOP_POST, offsetof(Socket, exec_command) +Socket.TimeoutSec, config_parse_usec, 0, offsetof(Socket, timeout_usec) +Socket.DirectoryMode, config_parse_mode, 0, offsetof(Socket, directory_mode) +Socket.SocketMode, config_parse_mode, 0, offsetof(Socket, socket_mode) +Socket.Accept, config_parse_bool, 0, offsetof(Socket, accept) +Socket.MaxConnections, config_parse_unsigned, 0, offsetof(Socket, max_connections) +Socket.KeepAlive, config_parse_bool, 0, offsetof(Socket, keep_alive) +Socket.Priority, config_parse_int, 0, offsetof(Socket, priority) +Socket.ReceiveBuffer, config_parse_bytes_size, 0, offsetof(Socket, receive_buffer) +Socket.SendBuffer, config_parse_bytes_size, 0, offsetof(Socket, send_buffer) +Socket.IPTOS, config_parse_ip_tos, 0, offsetof(Socket, ip_tos) +Socket.IPTTL, config_parse_int, 0, offsetof(Socket, ip_ttl) +Socket.Mark, config_parse_int, 0, offsetof(Socket, mark) +Socket.PipeSize, config_parse_bytes_size, 0, offsetof(Socket, pipe_size) +Socket.FreeBind, config_parse_bool, 0, offsetof(Socket, free_bind) +Socket.Transparent, config_parse_bool, 0, offsetof(Socket, transparent) +Socket.Broadcast, config_parse_bool, 0, offsetof(Socket, broadcast) +Socket.PassCredentials, config_parse_bool, 0, offsetof(Socket, pass_cred) +Socket.TCPCongestion, config_parse_string, 0, offsetof(Socket, tcp_congestion) +Socket.MessageQueueMaxMessages, config_parse_long, 0, offsetof(Socket, mq_maxmsg) +Socket.MessageQueueMessageSize, config_parse_long, 0, offsetof(Socket, mq_msgsize) +Socket.Service, config_parse_socket_service, 0, 0 +EXEC_CONTEXT_CONFIG_ITEMS(Socket)m4_dnl +m4_dnl +Mount.What, config_parse_string, 0, offsetof(Mount, parameters_fragment.what) +Mount.Where, config_parse_path, 0, offsetof(Mount, where) +Mount.Options, config_parse_string, 0, offsetof(Mount, parameters_fragment.options) +Mount.Type, config_parse_string, 0, offsetof(Mount, parameters_fragment.fstype) +Mount.TimeoutSec, config_parse_usec, 0, offsetof(Mount, timeout_usec) +Mount.DirectoryMode, config_parse_mode, 0, offsetof(Mount, directory_mode) +EXEC_CONTEXT_CONFIG_ITEMS(Mount)m4_dnl +m4_dnl +Automount.Where, config_parse_path, 0, offsetof(Automount, where) +Automount.DirectoryMode, config_parse_mode, 0, offsetof(Automount, directory_mode) +m4_dnl +Swap.What, config_parse_path, 0, offsetof(Swap, parameters_fragment.what) +Swap.Priority, config_parse_int, 0, offsetof(Swap, parameters_fragment.priority) +Swap.TimeoutSec, config_parse_usec, 0, offsetof(Swap, timeout_usec) +EXEC_CONTEXT_CONFIG_ITEMS(Swap)m4_dnl +m4_dnl +Timer.OnActiveSec, config_parse_timer, 0, 0 +Timer.OnBootSec, config_parse_timer, 0, 0 +Timer.OnStartupSec, config_parse_timer, 0, 0 +Timer.OnUnitActiveSec, config_parse_timer, 0, 0 +Timer.OnUnitInactiveSec, config_parse_timer, 0, 0 +Timer.Unit, config_parse_timer_unit, 0, 0 +m4_dnl +Path.PathExists, config_parse_path_spec, 0, 0 +Path.PathExistsGlob, config_parse_path_spec, 0, 0 +Path.PathChanged, config_parse_path_spec, 0, 0 +Path.PathModified, config_parse_path_spec, 0, 0 +Path.DirectoryNotEmpty, config_parse_path_spec, 0, 0 +Path.Unit, config_parse_path_unit, 0, 0 +Path.MakeDirectory, config_parse_bool, 0, offsetof(Path, make_directory) +Path.DirectoryMode, config_parse_mode, 0, offsetof(Path, directory_mode) +m4_dnl The [Install] section is ignored here. +Install.Alias, NULL, 0, 0 +Install.WantedBy, NULL, 0, 0 +Install.Also, NULL, 0, 0 diff --git a/src/load-fragment.c b/src/load-fragment.c index 5c1dff60b8..b963d7f995 100644 --- a/src/load-fragment.c +++ b/src/load-fragment.c @@ -45,7 +45,7 @@ #include "bus-errors.h" #ifndef HAVE_SYSV_COMPAT -static int config_parse_warn_compat( +int config_parse_warn_compat( const char *filename, unsigned line, const char *section, @@ -60,7 +60,7 @@ static int config_parse_warn_compat( } #endif -static int config_parse_deps( +int config_parse_unit_deps( const char *filename, unsigned line, const char *section, @@ -70,7 +70,7 @@ static int config_parse_deps( void *data, void *userdata) { - UnitDependency d = PTR_TO_UINT(data); + UnitDependency d = ltype; Unit *u = userdata; char *w; size_t l; @@ -84,7 +84,8 @@ static int config_parse_deps( char *t, *k; int r; - if (!(t = strndup(w, l))) + t = strndup(w, l); + if (!t) return -ENOMEM; k = unit_name_printf(u, t); @@ -94,12 +95,8 @@ static int config_parse_deps( return -ENOMEM; r = unit_add_dependency_by_name(u, d, k, NULL, true); - - if (r < 0) { - log_error("Failed to add dependency on %s, ignoring: %s", k, strerror(-r)); - free(k); - return 0; - } + if (r < 0) + log_error("[%s:%u] Failed to add dependency on %s, ignoring: %s", filename, line, k, strerror(-r)); free(k); } @@ -107,7 +104,7 @@ static int config_parse_deps( return 0; } -static int config_parse_names( +int config_parse_unit_names( const char *filename, unsigned line, const char *section, @@ -154,7 +151,7 @@ static int config_parse_names( return 0; } -static int config_parse_string_printf( +int config_parse_unit_string_printf( const char *filename, unsigned line, const char *section, @@ -188,7 +185,7 @@ static int config_parse_string_printf( return 0; } -static int config_parse_strv_printf( +int config_parse_unit_strv_printf( const char *filename, unsigned line, const char *section, @@ -217,7 +214,7 @@ static int config_parse_strv_printf( return r; } -static int config_parse_path_printf( +int config_parse_unit_path_printf( const char *filename, unsigned line, const char *section, @@ -254,7 +251,7 @@ static int config_parse_path_printf( return 0; } -static int config_parse_listen( +int config_parse_socket_listen( const char *filename, unsigned line, const char *section, @@ -272,7 +269,7 @@ static int config_parse_listen( assert(rvalue); assert(data); - s = (Socket*) data; + s = SOCKET(data); if (!(p = new0(SocketPort, 1))) return -ENOMEM; @@ -365,7 +362,7 @@ static int config_parse_listen( return 0; } -static int config_parse_socket_bind( +int config_parse_socket_bind( const char *filename, unsigned line, const char *section, @@ -383,7 +380,7 @@ static int config_parse_socket_bind( assert(rvalue); assert(data); - s = (Socket*) data; + s = SOCKET(data); if ((b = socket_address_bind_ipv6_only_from_string(rvalue)) < 0) { int r; @@ -400,7 +397,7 @@ static int config_parse_socket_bind( return 0; } -static int config_parse_nice( +int config_parse_exec_nice( const char *filename, unsigned line, const char *section, @@ -434,7 +431,7 @@ static int config_parse_nice( return 0; } -static int config_parse_oom_score_adjust( +int config_parse_exec_oom_score_adjust( const char *filename, unsigned line, const char *section, @@ -468,42 +465,7 @@ static int config_parse_oom_score_adjust( return 0; } -static int config_parse_mode( - const char *filename, - unsigned line, - const char *section, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - mode_t *m = data; - long l; - char *x = NULL; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - errno = 0; - l = strtol(rvalue, &x, 8); - if (!x || *x || errno) { - log_error("[%s:%u] Failed to parse mode value, ignoring: %s", filename, line, rvalue); - return 0; - } - - if (l < 0000 || l > 07777) { - log_error("[%s:%u] mode value out of range, ignoring: %s", filename, line, rvalue); - return 0; - } - - *m = (mode_t) l; - return 0; -} - -static int config_parse_exec( +int config_parse_exec( const char *filename, unsigned line, const char *section, @@ -526,6 +488,8 @@ static int config_parse_exec( * alternatively an absolute prefixed with @ to allow * overriding of argv[0]. */ + e += ltype; + for (;;) { char *w; size_t l; @@ -587,6 +551,7 @@ static int config_parse_exec( if (!n[0]) { log_error("[%s:%u] Invalid command line, ignoring: %s", filename, line, rvalue); strv_free(n); + free(path); return 0; } @@ -621,35 +586,10 @@ fail: return -ENOMEM; } -static int config_parse_usec( - const char *filename, - unsigned line, - const char *section, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - usec_t *usec = data; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - if (parse_usec(rvalue, usec) < 0) { - log_error("[%s:%u] Failed to parse time value, ignoring: %s", filename, line, rvalue); - return 0; - } - - return 0; -} - -static DEFINE_CONFIG_PARSE_ENUM(config_parse_service_type, service_type, ServiceType, "Failed to parse service type"); -static DEFINE_CONFIG_PARSE_ENUM(config_parse_service_restart, service_restart, ServiceRestart, "Failed to parse service restart specifier"); +DEFINE_CONFIG_PARSE_ENUM(config_parse_service_type, service_type, ServiceType, "Failed to parse service type"); +DEFINE_CONFIG_PARSE_ENUM(config_parse_service_restart, service_restart, ServiceRestart, "Failed to parse service restart specifier"); -static int config_parse_bindtodevice( +int config_parse_socket_bindtodevice( const char *filename, unsigned line, const char *section, @@ -679,10 +619,10 @@ static int config_parse_bindtodevice( return 0; } -static DEFINE_CONFIG_PARSE_ENUM(config_parse_output, exec_output, ExecOutput, "Failed to parse output specifier"); -static DEFINE_CONFIG_PARSE_ENUM(config_parse_input, exec_input, ExecInput, "Failed to parse input specifier"); +DEFINE_CONFIG_PARSE_ENUM(config_parse_output, exec_output, ExecOutput, "Failed to parse output specifier"); +DEFINE_CONFIG_PARSE_ENUM(config_parse_input, exec_input, ExecInput, "Failed to parse input specifier"); -static int config_parse_facility( +int config_parse_facility( const char *filename, unsigned line, const char *section, @@ -710,7 +650,7 @@ static int config_parse_facility( return 0; } -static int config_parse_level( +int config_parse_level( const char *filename, unsigned line, const char *section, @@ -737,7 +677,7 @@ static int config_parse_level( return 0; } -static int config_parse_io_class( +int config_parse_exec_io_class( const char *filename, unsigned line, const char *section, @@ -766,7 +706,7 @@ static int config_parse_io_class( return 0; } -static int config_parse_io_priority( +int config_parse_exec_io_priority( const char *filename, unsigned line, const char *section, @@ -795,7 +735,7 @@ static int config_parse_io_priority( return 0; } -static int config_parse_cpu_sched_policy( +int config_parse_exec_cpu_sched_policy( const char *filename, unsigned line, const char *section, @@ -825,7 +765,7 @@ static int config_parse_cpu_sched_policy( return 0; } -static int config_parse_cpu_sched_prio( +int config_parse_exec_cpu_sched_prio( const char *filename, unsigned line, const char *section, @@ -855,7 +795,7 @@ static int config_parse_cpu_sched_prio( return 0; } -static int config_parse_cpu_affinity( +int config_parse_exec_cpu_affinity( const char *filename, unsigned line, const char *section, @@ -901,7 +841,7 @@ static int config_parse_cpu_affinity( return 0; } -static int config_parse_capabilities( +int config_parse_exec_capabilities( const char *filename, unsigned line, const char *section, @@ -934,7 +874,7 @@ static int config_parse_capabilities( return 0; } -static int config_parse_secure_bits( +int config_parse_exec_secure_bits( const char *filename, unsigned line, const char *section, @@ -976,7 +916,7 @@ static int config_parse_secure_bits( return 0; } -static int config_parse_bounding_set( +int config_parse_exec_bounding_set( const char *filename, unsigned line, const char *section, @@ -1035,7 +975,7 @@ static int config_parse_bounding_set( return 0; } -static int config_parse_timer_slack_nsec( +int config_parse_exec_timer_slack_nsec( const char *filename, unsigned line, const char *section, @@ -1063,7 +1003,7 @@ static int config_parse_timer_slack_nsec( return 0; } -static int config_parse_limit( +int config_parse_limit( const char *filename, unsigned line, const char *section, @@ -1081,6 +1021,8 @@ static int config_parse_limit( assert(rvalue); assert(data); + rl += ltype; + if (streq(rvalue, "infinity")) u = (unsigned long long) RLIM_INFINITY; else if (safe_atollu(rvalue, &u) < 0) { @@ -1096,7 +1038,7 @@ static int config_parse_limit( return 0; } -static int config_parse_cgroup( +int config_parse_unit_cgroup( const char *filename, unsigned line, const char *section, @@ -1144,7 +1086,7 @@ static int config_parse_cgroup( } #ifdef HAVE_SYSV_COMPAT -static int config_parse_sysv_priority( +int config_parse_sysv_priority( const char *filename, unsigned line, const char *section, @@ -1172,7 +1114,7 @@ static int config_parse_sysv_priority( } #endif -static int config_parse_fsck_passno( +int config_parse_fsck_passno( const char *filename, unsigned line, const char *section, @@ -1199,9 +1141,9 @@ static int config_parse_fsck_passno( return 0; } -static DEFINE_CONFIG_PARSE_ENUM(config_parse_kill_mode, kill_mode, KillMode, "Failed to parse kill mode"); +DEFINE_CONFIG_PARSE_ENUM(config_parse_kill_mode, kill_mode, KillMode, "Failed to parse kill mode"); -static int config_parse_kill_signal( +int config_parse_kill_signal( const char *filename, unsigned line, const char *section, @@ -1228,7 +1170,7 @@ static int config_parse_kill_signal( return 0; } -static int config_parse_mount_flags( +int config_parse_exec_mount_flags( const char *filename, unsigned line, const char *section, @@ -1266,7 +1208,7 @@ static int config_parse_mount_flags( return 0; } -static int config_parse_timer( +int config_parse_timer( const char *filename, unsigned line, const char *section, @@ -1307,7 +1249,7 @@ static int config_parse_timer( return 0; } -static int config_parse_timer_unit( +int config_parse_timer_unit( const char *filename, unsigned line, const char *section, @@ -1320,6 +1262,7 @@ static int config_parse_timer_unit( Timer *t = data; int r; DBusError error; + Unit *u; assert(filename); assert(lvalue); @@ -1333,16 +1276,19 @@ static int config_parse_timer_unit( return 0; } - if ((r = manager_load_unit(t->meta.manager, rvalue, NULL, NULL, &t->unit)) < 0) { + r = manager_load_unit(UNIT(t)->manager, rvalue, NULL, NULL, &u); + if (r < 0) { log_error("[%s:%u] Failed to load unit %s, ignoring: %s", filename, line, rvalue, bus_error(&error, r)); dbus_error_free(&error); return 0; } + unit_ref_set(&t->unit, u); + return 0; } -static int config_parse_path_spec( +int config_parse_path_spec( const char *filename, unsigned line, const char *section, @@ -1389,7 +1335,7 @@ static int config_parse_path_spec( return 0; } -static int config_parse_path_unit( +int config_parse_path_unit( const char *filename, unsigned line, const char *section, @@ -1402,6 +1348,7 @@ static int config_parse_path_unit( Path *t = data; int r; DBusError error; + Unit *u; assert(filename); assert(lvalue); @@ -1415,16 +1362,18 @@ static int config_parse_path_unit( return 0; } - if ((r = manager_load_unit(t->meta.manager, rvalue, NULL, &error, &t->unit)) < 0) { + if ((r = manager_load_unit(UNIT(t)->manager, rvalue, NULL, &error, &u)) < 0) { log_error("[%s:%u] Failed to load unit %s, ignoring: %s", filename, line, rvalue, bus_error(&error, r)); dbus_error_free(&error); return 0; } + unit_ref_set(&t->unit, u); + return 0; } -static int config_parse_socket_service( +int config_parse_socket_service( const char *filename, unsigned line, const char *section, @@ -1437,6 +1386,7 @@ static int config_parse_socket_service( Socket *s = data; int r; DBusError error; + Unit *x; assert(filename); assert(lvalue); @@ -1450,16 +1400,19 @@ static int config_parse_socket_service( return 0; } - if ((r = manager_load_unit(s->meta.manager, rvalue, NULL, &error, (Unit**) &s->service)) < 0) { + r = manager_load_unit(UNIT(s)->manager, rvalue, NULL, &error, &x); + if (r < 0) { log_error("[%s:%u] Failed to load unit %s, ignoring: %s", filename, line, rvalue, bus_error(&error, r)); dbus_error_free(&error); return 0; } + unit_ref_set(&s->service, x); + return 0; } -static int config_parse_service_sockets( +int config_parse_service_sockets( const char *filename, unsigned line, const char *section, @@ -1471,7 +1424,6 @@ static int config_parse_service_sockets( Service *s = data; int r; - DBusError error; char *state, *w; size_t l; @@ -1480,41 +1432,40 @@ static int config_parse_service_sockets( assert(rvalue); assert(data); - dbus_error_init(&error); - FOREACH_WORD_QUOTED(w, l, rvalue, state) { - char *t; - Unit *sock; + char *t, *k; - if (!(t = strndup(w, l))) + t = strndup(w, l); + if (!t) return -ENOMEM; - if (!endswith(t, ".socket")) { - log_error("[%s:%u] Unit must be of type socket, ignoring: %s", filename, line, rvalue); - free(t); - continue; - } - - r = manager_load_unit(s->meta.manager, t, NULL, &error, &sock); + k = unit_name_printf(UNIT(s), t); free(t); - if (r < 0) { - log_error("[%s:%u] Failed to load unit %s, ignoring: %s", filename, line, rvalue, bus_error(&error, r)); - dbus_error_free(&error); + if (!k) + return -ENOMEM; + + if (!endswith(k, ".socket")) { + log_error("[%s:%u] Unit must be of type socket, ignoring: %s", filename, line, rvalue); + free(k); continue; } - if ((r = set_ensure_allocated(&s->configured_sockets, trivial_hash_func, trivial_compare_func)) < 0) - return r; + r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_WANTS, UNIT_AFTER, k, NULL, true); + if (r < 0) + log_error("[%s:%u] Failed to add dependency on %s, ignoring: %s", filename, line, k, strerror(-r)); - if ((r = set_put(s->configured_sockets, sock)) < 0) + r = unit_add_dependency_by_name(UNIT(s), UNIT_TRIGGERED_BY, k, NULL, true); + if (r < 0) return r; + + free(k); } return 0; } -static int config_parse_env_file( +int config_parse_unit_env_file( const char *filename, unsigned line, const char *section, @@ -1554,7 +1505,7 @@ static int config_parse_env_file( return 0; } -static int config_parse_ip_tos( +int config_parse_ip_tos( const char *filename, unsigned line, const char *section, @@ -1581,7 +1532,7 @@ static int config_parse_ip_tos( return 0; } -static int config_parse_condition_path( +int config_parse_unit_condition_path( const char *filename, unsigned line, const char *section, @@ -1601,10 +1552,12 @@ static int config_parse_condition_path( assert(rvalue); assert(data); - if ((trigger = rvalue[0] == '|')) + trigger = rvalue[0] == '|'; + if (trigger) rvalue++; - if ((negate = rvalue[0] == '!')) + negate = rvalue[0] == '!'; + if (negate) rvalue++; if (!path_is_absolute(rvalue)) { @@ -1612,14 +1565,15 @@ static int config_parse_condition_path( return 0; } - if (!(c = condition_new(cond, rvalue, trigger, negate))) + c = condition_new(cond, rvalue, trigger, negate); + if (!c) return -ENOMEM; - LIST_PREPEND(Condition, conditions, u->meta.conditions, c); + LIST_PREPEND(Condition, conditions, u->conditions, c); return 0; } -static int config_parse_condition_string( +int config_parse_unit_condition_string( const char *filename, unsigned line, const char *section, @@ -1648,11 +1602,11 @@ static int config_parse_condition_string( if (!(c = condition_new(cond, rvalue, trigger, negate))) return -ENOMEM; - LIST_PREPEND(Condition, conditions, u->meta.conditions, c); + LIST_PREPEND(Condition, conditions, u->conditions, c); return 0; } -static int config_parse_condition_null( +int config_parse_unit_condition_null( const char *filename, unsigned line, const char *section, @@ -1689,11 +1643,381 @@ static int config_parse_condition_null( if (!(c = condition_new(CONDITION_NULL, NULL, trigger, negate))) return -ENOMEM; - LIST_PREPEND(Condition, conditions, u->meta.conditions, c); + LIST_PREPEND(Condition, conditions, u->conditions, c); + return 0; +} + +DEFINE_CONFIG_PARSE_ENUM(config_parse_notify_access, notify_access, NotifyAccess, "Failed to parse notify access specifier"); +DEFINE_CONFIG_PARSE_ENUM(config_parse_start_limit_action, start_limit_action, StartLimitAction, "Failed to parse start limit action specifier"); + +int config_parse_unit_cgroup_attr( + const char *filename, + unsigned line, + const char *section, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + Unit *u = data; + char **l; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + l = strv_split_quoted(rvalue); + if (!l) + return -ENOMEM; + + if (strv_length(l) != 2) { + log_error("[%s:%u] Failed to parse cgroup attribute value, ignoring: %s", filename, line, rvalue); + strv_free(l); + return 0; + } + + r = unit_add_cgroup_attribute(u, NULL, l[0], l[1], NULL); + strv_free(l); + + if (r < 0) { + log_error("[%s:%u] Failed to add cgroup attribute value, ignoring: %s", filename, line, rvalue); + return 0; + } + + return 0; +} + +int config_parse_unit_cpu_shares(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata) { + Unit *u = data; + int r; + unsigned long ul; + char *t; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + if (safe_atolu(rvalue, &ul) < 0 || ul < 1) { + log_error("[%s:%u] Failed to parse CPU shares value, ignoring: %s", filename, line, rvalue); + return 0; + } + + if (asprintf(&t, "%lu", ul) < 0) + return -ENOMEM; + + r = unit_add_cgroup_attribute(u, "cpu", "cpu.shares", t, NULL); + free(t); + + if (r < 0) { + log_error("[%s:%u] Failed to add cgroup attribute value, ignoring: %s", filename, line, rvalue); + return 0; + } + + return 0; +} + +int config_parse_unit_memory_limit(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata) { + Unit *u = data; + int r; + off_t sz; + char *t; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + if (parse_bytes(rvalue, &sz) < 0 || sz <= 0) { + log_error("[%s:%u] Failed to parse memory limit value, ignoring: %s", filename, line, rvalue); + return 0; + } + + if (asprintf(&t, "%llu", (unsigned long long) sz) < 0) + return -ENOMEM; + + r = unit_add_cgroup_attribute(u, + "memory", + streq(lvalue, "MemorySoftLimit") ? "memory.soft_limit_in_bytes" : "memory.limit_in_bytes", + t, NULL); + free(t); + + if (r < 0) { + log_error("[%s:%u] Failed to add cgroup attribute value, ignoring: %s", filename, line, rvalue); + return 0; + } + + return 0; +} + +static int device_map(const char *controller, const char *name, const char *value, char **ret) { + char **l; + + assert(controller); + assert(name); + assert(value); + assert(ret); + + l = strv_split_quoted(value); + if (!l) + return -ENOMEM; + + assert(strv_length(l) >= 1); + + if (streq(l[0], "*")) { + + if (asprintf(ret, "a *:*%s%s", + isempty(l[1]) ? "" : " ", strempty(l[1])) < 0) { + strv_free(l); + return -ENOMEM; + } + + } else { + struct stat st; + + if (stat(l[0], &st) < 0) { + log_warning("Couldn't stat device %s", l[0]); + strv_free(l); + return -errno; + } + + if (!S_ISCHR(st.st_mode) && !S_ISBLK(st.st_mode)) { + log_warning("%s is not a device.", l[0]); + strv_free(l); + return -ENODEV; + } + + if (asprintf(ret, "%c %u:%u%s%s", + S_ISCHR(st.st_mode) ? 'c' : 'b', + major(st.st_rdev), minor(st.st_rdev), + isempty(l[1]) ? "" : " ", strempty(l[1])) < 0) { + + strv_free(l); + return -ENOMEM; + } + } + + strv_free(l); + return 0; +} + +int config_parse_unit_device_allow(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata) { + Unit *u = data; + char **l; + int r; + unsigned k; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + l = strv_split_quoted(rvalue); + if (!l) + return -ENOMEM; + + k = strv_length(l); + if (k < 1 || k > 2) { + log_error("[%s:%u] Failed to parse device value, ignoring: %s", filename, line, rvalue); + strv_free(l); + return 0; + } + + if (!streq(l[0], "*") && !path_startswith(l[0], "/dev")) { + log_error("[%s:%u] Device node path not absolute, ignoring: %s", filename, line, rvalue); + strv_free(l); + return 0; + } + + if (!isempty(l[1]) && !in_charset(l[1], "rwm")) { + log_error("[%s:%u] Device access string invalid, ignoring: %s", filename, line, rvalue); + strv_free(l); + return 0; + } + strv_free(l); + + r = unit_add_cgroup_attribute(u, "devices", + streq(lvalue, "DeviceAllow") ? "devices.allow" : "devices.deny", + rvalue, device_map); + + if (r < 0) { + log_error("[%s:%u] Failed to add cgroup attribute value, ignoring: %s", filename, line, rvalue); + return 0; + } + + return 0; +} + +static int blkio_map(const char *controller, const char *name, const char *value, char **ret) { + struct stat st; + char **l; + dev_t d; + + assert(controller); + assert(name); + assert(value); + assert(ret); + + l = strv_split_quoted(value); + if (!l) + return -ENOMEM; + + assert(strv_length(l) == 2); + + if (stat(l[0], &st) < 0) { + log_warning("Couldn't stat device %s", l[0]); + strv_free(l); + return -errno; + } + + if (S_ISBLK(st.st_mode)) + d = st.st_rdev; + else if (major(st.st_dev) != 0) { + /* If this is not a device node then find the block + * device this file is stored on */ + d = st.st_dev; + + /* If this is a partition, try to get the originating + * block device */ + block_get_whole_disk(d, &d); + } else { + log_warning("%s is not a block device and file system block device cannot be determined or is not local.", l[0]); + strv_free(l); + return -ENODEV; + } + + if (asprintf(ret, "%u:%u %s", major(d), minor(d), l[1]) < 0) { + strv_free(l); + return -ENOMEM; + } + + strv_free(l); + return 0; +} + +int config_parse_unit_blkio_weight(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata) { + Unit *u = data; + int r; + unsigned long ul; + const char *device = NULL, *weight; + unsigned k; + char *t, **l; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + l = strv_split_quoted(rvalue); + if (!l) + return -ENOMEM; + + k = strv_length(l); + if (k < 1 || k > 2) { + log_error("[%s:%u] Failed to parse weight value, ignoring: %s", filename, line, rvalue); + strv_free(l); + return 0; + } + + if (k == 1) + weight = l[0]; + else { + device = l[0]; + weight = l[1]; + } + + if (device && !path_is_absolute(device)) { + log_error("[%s:%u] Failed to parse block device node value, ignoring: %s", filename, line, rvalue); + strv_free(l); + return 0; + } + + if (safe_atolu(weight, &ul) < 0 || ul < 10 || ul > 1000) { + log_error("[%s:%u] Failed to parse block IO weight value, ignoring: %s", filename, line, rvalue); + strv_free(l); + return 0; + } + + if (device) + r = asprintf(&t, "%s %lu", device, ul); + else + r = asprintf(&t, "%lu", ul); + strv_free(l); + + if (r < 0) + return -ENOMEM; + + if (device) + r = unit_add_cgroup_attribute(u, "blkio", "blkio.weight_device", t, blkio_map); + else + r = unit_add_cgroup_attribute(u, "blkio", "blkio.weight", t, NULL); + free(t); + + if (r < 0) { + log_error("[%s:%u] Failed to add cgroup attribute value, ignoring: %s", filename, line, rvalue); + return 0; + } + + return 0; +} + +int config_parse_unit_blkio_bandwidth(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata) { + Unit *u = data; + int r; + off_t bytes; + unsigned k; + char *t, **l; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + l = strv_split_quoted(rvalue); + if (!l) + return -ENOMEM; + + k = strv_length(l); + if (k != 2) { + log_error("[%s:%u] Failed to parse bandwidth value, ignoring: %s", filename, line, rvalue); + strv_free(l); + return 0; + } + + if (!path_is_absolute(l[0])) { + log_error("[%s:%u] Failed to parse block device node value, ignoring: %s", filename, line, rvalue); + strv_free(l); + return 0; + } + + if (parse_bytes(l[1], &bytes) < 0 || bytes <= 0) { + log_error("[%s:%u] Failed to parse block IO bandwith value, ignoring: %s", filename, line, rvalue); + strv_free(l); + return 0; + } + + r = asprintf(&t, "%s %llu", l[0], (unsigned long long) bytes); + strv_free(l); + + if (r < 0) + return -ENOMEM; + + r = unit_add_cgroup_attribute(u, "blkio", + streq(lvalue, "BlockIOReadBandwidth") ? "blkio.read_bps_device" : "blkio.write_bps_device", + t, blkio_map); + free(t); + + if (r < 0) { + log_error("[%s:%u] Failed to add cgroup attribute value, ignoring: %s", filename, line, rvalue); + return 0; + } + return 0; } -static DEFINE_CONFIG_PARSE_ENUM(config_parse_notify_access, notify_access, NotifyAccess, "Failed to parse notify access specifier"); #define FOLLOW_MAX 8 @@ -1786,7 +2110,7 @@ static int merge_by_names(Unit **u, Set *names, const char *id) { * ours? Then let's try it the other way * round */ - other = manager_get_unit((*u)->meta.manager, k); + other = manager_get_unit((*u)->manager, k); free(k); if (other) @@ -1807,313 +2131,7 @@ static int merge_by_names(Unit **u, Set *names, const char *id) { return 0; } -static void dump_items(FILE *f, const ConfigItem *items) { - const ConfigItem *i; - const char *prev_section = NULL; - bool not_first = false; - - struct { - ConfigParserCallback callback; - const char *rvalue; - } table[] = { - { config_parse_int, "INTEGER" }, - { config_parse_unsigned, "UNSIGNED" }, - { config_parse_size, "SIZE" }, - { config_parse_bool, "BOOLEAN" }, - { config_parse_string, "STRING" }, - { config_parse_path, "PATH" }, - { config_parse_path_printf, "PATH" }, - { config_parse_strv, "STRING [...]" }, - { config_parse_nice, "NICE" }, - { config_parse_oom_score_adjust, "OOMSCOREADJUST" }, - { config_parse_io_class, "IOCLASS" }, - { config_parse_io_priority, "IOPRIORITY" }, - { config_parse_cpu_sched_policy, "CPUSCHEDPOLICY" }, - { config_parse_cpu_sched_prio, "CPUSCHEDPRIO" }, - { config_parse_cpu_affinity, "CPUAFFINITY" }, - { config_parse_mode, "MODE" }, - { config_parse_env_file, "FILE" }, - { config_parse_output, "OUTPUT" }, - { config_parse_input, "INPUT" }, - { config_parse_facility, "FACILITY" }, - { config_parse_level, "LEVEL" }, - { config_parse_capabilities, "CAPABILITIES" }, - { config_parse_secure_bits, "SECUREBITS" }, - { config_parse_bounding_set, "BOUNDINGSET" }, - { config_parse_timer_slack_nsec, "TIMERSLACK" }, - { config_parse_limit, "LIMIT" }, - { config_parse_cgroup, "CGROUP [...]" }, - { config_parse_deps, "UNIT [...]" }, - { config_parse_names, "UNIT [...]" }, - { config_parse_exec, "PATH [ARGUMENT [...]]" }, - { config_parse_service_type, "SERVICETYPE" }, - { config_parse_service_restart, "SERVICERESTART" }, -#ifdef HAVE_SYSV_COMPAT - { config_parse_sysv_priority, "SYSVPRIORITY" }, -#else - { config_parse_warn_compat, "NOTSUPPORTED" }, -#endif - { config_parse_kill_mode, "KILLMODE" }, - { config_parse_kill_signal, "SIGNAL" }, - { config_parse_listen, "SOCKET [...]" }, - { config_parse_socket_bind, "SOCKETBIND" }, - { config_parse_bindtodevice, "NETWORKINTERFACE" }, - { config_parse_usec, "SECONDS" }, - { config_parse_path_strv, "PATH [...]" }, - { config_parse_mount_flags, "MOUNTFLAG [...]" }, - { config_parse_string_printf, "STRING" }, - { config_parse_timer, "TIMER" }, - { config_parse_timer_unit, "NAME" }, - { config_parse_path_spec, "PATH" }, - { config_parse_path_unit, "UNIT" }, - { config_parse_notify_access, "ACCESS" }, - { config_parse_ip_tos, "TOS" }, - { config_parse_condition_path, "CONDITION" }, - { config_parse_condition_string, "CONDITION" }, - { config_parse_condition_null, "CONDITION" }, - }; - - assert(f); - assert(items); - - for (i = items; i->lvalue; i++) { - unsigned j; - const char *rvalue = "OTHER"; - - if (!streq_ptr(i->section, prev_section)) { - if (!not_first) - not_first = true; - else - fputc('\n', f); - - fprintf(f, "[%s]\n", i->section); - prev_section = i->section; - } - - for (j = 0; j < ELEMENTSOF(table); j++) - if (i->parse == table[j].callback) { - rvalue = table[j].rvalue; - break; - } - - fprintf(f, "%s=%s\n", i->lvalue, rvalue); - } -} - static int load_from_path(Unit *u, const char *path) { - - static const char* const section_table[_UNIT_TYPE_MAX] = { - [UNIT_SERVICE] = "Service", - [UNIT_TIMER] = "Timer", - [UNIT_SOCKET] = "Socket", - [UNIT_TARGET] = "Target", - [UNIT_DEVICE] = "Device", - [UNIT_MOUNT] = "Mount", - [UNIT_AUTOMOUNT] = "Automount", - [UNIT_SNAPSHOT] = "Snapshot", - [UNIT_SWAP] = "Swap", - [UNIT_PATH] = "Path" - }; - -#define EXEC_CONTEXT_CONFIG_ITEMS(context, section) \ - { "WorkingDirectory", config_parse_path_printf, 0, &(context).working_directory, section }, \ - { "RootDirectory", config_parse_path_printf, 0, &(context).root_directory, section }, \ - { "User", config_parse_string_printf, 0, &(context).user, section }, \ - { "Group", config_parse_string_printf, 0, &(context).group, section }, \ - { "SupplementaryGroups", config_parse_strv, 0, &(context).supplementary_groups, section }, \ - { "Nice", config_parse_nice, 0, &(context), section }, \ - { "OOMScoreAdjust", config_parse_oom_score_adjust,0, &(context), section }, \ - { "IOSchedulingClass", config_parse_io_class, 0, &(context), section }, \ - { "IOSchedulingPriority", config_parse_io_priority, 0, &(context), section }, \ - { "CPUSchedulingPolicy", config_parse_cpu_sched_policy,0, &(context), section }, \ - { "CPUSchedulingPriority", config_parse_cpu_sched_prio, 0, &(context), section }, \ - { "CPUSchedulingResetOnFork", config_parse_bool, 0, &(context).cpu_sched_reset_on_fork, section }, \ - { "CPUAffinity", config_parse_cpu_affinity, 0, &(context), section }, \ - { "UMask", config_parse_mode, 0, &(context).umask, section }, \ - { "Environment", config_parse_strv_printf, 0, &(context).environment, section }, \ - { "EnvironmentFile", config_parse_env_file, 0, &(context).environment_files, section }, \ - { "StandardInput", config_parse_input, 0, &(context).std_input, section }, \ - { "StandardOutput", config_parse_output, 0, &(context).std_output, section }, \ - { "StandardError", config_parse_output, 0, &(context).std_error, section }, \ - { "TTYPath", config_parse_path_printf, 0, &(context).tty_path, section }, \ - { "TTYReset", config_parse_bool, 0, &(context).tty_reset, section }, \ - { "TTYVHangup", config_parse_bool, 0, &(context).tty_vhangup, section }, \ - { "TTYVTDisallocate", config_parse_bool, 0, &(context).tty_vt_disallocate, section }, \ - { "SyslogIdentifier", config_parse_string_printf, 0, &(context).syslog_identifier, section }, \ - { "SyslogFacility", config_parse_facility, 0, &(context).syslog_priority, section }, \ - { "SyslogLevel", config_parse_level, 0, &(context).syslog_priority, section }, \ - { "SyslogLevelPrefix", config_parse_bool, 0, &(context).syslog_level_prefix, section }, \ - { "Capabilities", config_parse_capabilities, 0, &(context), section }, \ - { "SecureBits", config_parse_secure_bits, 0, &(context), section }, \ - { "CapabilityBoundingSet", config_parse_bounding_set, 0, &(context), section }, \ - { "TimerSlackNSec", config_parse_timer_slack_nsec,0, &(context), section }, \ - { "LimitCPU", config_parse_limit, 0, &(context).rlimit[RLIMIT_CPU], section }, \ - { "LimitFSIZE", config_parse_limit, 0, &(context).rlimit[RLIMIT_FSIZE], section }, \ - { "LimitDATA", config_parse_limit, 0, &(context).rlimit[RLIMIT_DATA], section }, \ - { "LimitSTACK", config_parse_limit, 0, &(context).rlimit[RLIMIT_STACK], section }, \ - { "LimitCORE", config_parse_limit, 0, &(context).rlimit[RLIMIT_CORE], section }, \ - { "LimitRSS", config_parse_limit, 0, &(context).rlimit[RLIMIT_RSS], section }, \ - { "LimitNOFILE", config_parse_limit, 0, &(context).rlimit[RLIMIT_NOFILE], section }, \ - { "LimitAS", config_parse_limit, 0, &(context).rlimit[RLIMIT_AS], section }, \ - { "LimitNPROC", config_parse_limit, 0, &(context).rlimit[RLIMIT_NPROC], section }, \ - { "LimitMEMLOCK", config_parse_limit, 0, &(context).rlimit[RLIMIT_MEMLOCK], section }, \ - { "LimitLOCKS", config_parse_limit, 0, &(context).rlimit[RLIMIT_LOCKS], section }, \ - { "LimitSIGPENDING", config_parse_limit, 0, &(context).rlimit[RLIMIT_SIGPENDING], section }, \ - { "LimitMSGQUEUE", config_parse_limit, 0, &(context).rlimit[RLIMIT_MSGQUEUE], section }, \ - { "LimitNICE", config_parse_limit, 0, &(context).rlimit[RLIMIT_NICE], section }, \ - { "LimitRTPRIO", config_parse_limit, 0, &(context).rlimit[RLIMIT_RTPRIO], section }, \ - { "LimitRTTIME", config_parse_limit, 0, &(context).rlimit[RLIMIT_RTTIME], section }, \ - { "ControlGroup", config_parse_cgroup, 0, u, section }, \ - { "ReadWriteDirectories", config_parse_path_strv, 0, &(context).read_write_dirs, section }, \ - { "ReadOnlyDirectories", config_parse_path_strv, 0, &(context).read_only_dirs, section }, \ - { "InaccessibleDirectories",config_parse_path_strv, 0, &(context).inaccessible_dirs, section }, \ - { "PrivateTmp", config_parse_bool, 0, &(context).private_tmp, section }, \ - { "MountFlags", config_parse_mount_flags, 0, &(context), section }, \ - { "TCPWrapName", config_parse_string_printf, 0, &(context).tcpwrap_name, section }, \ - { "PAMName", config_parse_string_printf, 0, &(context).pam_name, section }, \ - { "KillMode", config_parse_kill_mode, 0, &(context).kill_mode, section }, \ - { "KillSignal", config_parse_kill_signal, 0, &(context).kill_signal, section }, \ - { "SendSIGKILL", config_parse_bool, 0, &(context).send_sigkill, section }, \ - { "UtmpIdentifier", config_parse_string_printf, 0, &(context).utmp_id, section }, \ - { "ControlGroupModify", config_parse_bool, 0, &(context).control_group_modify, section } - - const ConfigItem items[] = { - { "Names", config_parse_names, 0, u, "Unit" }, - { "Description", config_parse_string_printf, 0, &u->meta.description, "Unit" }, - { "Requires", config_parse_deps, 0, UINT_TO_PTR(UNIT_REQUIRES), "Unit" }, - { "RequiresOverridable", config_parse_deps, 0, UINT_TO_PTR(UNIT_REQUIRES_OVERRIDABLE), "Unit" }, - { "Requisite", config_parse_deps, 0, UINT_TO_PTR(UNIT_REQUISITE), "Unit" }, - { "RequisiteOverridable", config_parse_deps, 0, UINT_TO_PTR(UNIT_REQUISITE_OVERRIDABLE), "Unit" }, - { "Wants", config_parse_deps, 0, UINT_TO_PTR(UNIT_WANTS), "Unit" }, - { "BindTo", config_parse_deps, 0, UINT_TO_PTR(UNIT_BIND_TO), "Unit" }, - { "Conflicts", config_parse_deps, 0, UINT_TO_PTR(UNIT_CONFLICTS), "Unit" }, - { "Before", config_parse_deps, 0, UINT_TO_PTR(UNIT_BEFORE), "Unit" }, - { "After", config_parse_deps, 0, UINT_TO_PTR(UNIT_AFTER), "Unit" }, - { "OnFailure", config_parse_deps, 0, UINT_TO_PTR(UNIT_ON_FAILURE), "Unit" }, - { "StopWhenUnneeded", config_parse_bool, 0, &u->meta.stop_when_unneeded, "Unit" }, - { "RefuseManualStart", config_parse_bool, 0, &u->meta.refuse_manual_start, "Unit" }, - { "RefuseManualStop", config_parse_bool, 0, &u->meta.refuse_manual_stop, "Unit" }, - { "AllowIsolate", config_parse_bool, 0, &u->meta.allow_isolate, "Unit" }, - { "DefaultDependencies", config_parse_bool, 0, &u->meta.default_dependencies, "Unit" }, - { "OnFailureIsolate", config_parse_bool, 0, &u->meta.on_failure_isolate, "Unit" }, - { "IgnoreOnIsolate", config_parse_bool, 0, &u->meta.ignore_on_isolate, "Unit" }, - { "IgnoreOnSnapshot", config_parse_bool, 0, &u->meta.ignore_on_snapshot, "Unit" }, - { "JobTimeoutSec", config_parse_usec, 0, &u->meta.job_timeout, "Unit" }, - { "ConditionPathExists", config_parse_condition_path, CONDITION_PATH_EXISTS, u, "Unit" }, - { "ConditionPathExistsGlob", config_parse_condition_path, CONDITION_PATH_EXISTS_GLOB, u, "Unit" }, - { "ConditionPathIsDirectory", config_parse_condition_path, CONDITION_PATH_IS_DIRECTORY, u, "Unit" }, - { "ConditionDirectoryNotEmpty", config_parse_condition_path, CONDITION_DIRECTORY_NOT_EMPTY, u, "Unit" }, - { "ConditionFileIsExecutable", config_parse_condition_path, CONDITION_FILE_IS_EXECUTABLE, u, "Unit" }, - { "ConditionKernelCommandLine", config_parse_condition_string, CONDITION_KERNEL_COMMAND_LINE, u, "Unit" }, - { "ConditionVirtualization", config_parse_condition_string, CONDITION_VIRTUALIZATION, u, "Unit" }, - { "ConditionSecurity", config_parse_condition_string, CONDITION_SECURITY, u, "Unit" }, - { "ConditionNull", config_parse_condition_null, 0, u, "Unit" }, - - { "PIDFile", config_parse_path_printf, 0, &u->service.pid_file, "Service" }, - { "ExecStartPre", config_parse_exec, 0, u->service.exec_command+SERVICE_EXEC_START_PRE, "Service" }, - { "ExecStart", config_parse_exec, 0, u->service.exec_command+SERVICE_EXEC_START, "Service" }, - { "ExecStartPost", config_parse_exec, 0, u->service.exec_command+SERVICE_EXEC_START_POST, "Service" }, - { "ExecReload", config_parse_exec, 0, u->service.exec_command+SERVICE_EXEC_RELOAD, "Service" }, - { "ExecStop", config_parse_exec, 0, u->service.exec_command+SERVICE_EXEC_STOP, "Service" }, - { "ExecStopPost", config_parse_exec, 0, u->service.exec_command+SERVICE_EXEC_STOP_POST, "Service" }, - { "RestartSec", config_parse_usec, 0, &u->service.restart_usec, "Service" }, - { "TimeoutSec", config_parse_usec, 0, &u->service.timeout_usec, "Service" }, - { "Type", config_parse_service_type, 0, &u->service.type, "Service" }, - { "Restart", config_parse_service_restart, 0, &u->service.restart, "Service" }, - { "PermissionsStartOnly", config_parse_bool, 0, &u->service.permissions_start_only, "Service" }, - { "RootDirectoryStartOnly", config_parse_bool, 0, &u->service.root_directory_start_only, "Service" }, - { "RemainAfterExit", config_parse_bool, 0, &u->service.remain_after_exit, "Service" }, - { "GuessMainPID", config_parse_bool, 0, &u->service.guess_main_pid, "Service" }, -#ifdef HAVE_SYSV_COMPAT - { "SysVStartPriority", config_parse_sysv_priority, 0, &u->service.sysv_start_priority, "Service" }, -#else - { "SysVStartPriority", config_parse_warn_compat, 0, NULL, "Service" }, -#endif - { "NonBlocking", config_parse_bool, 0, &u->service.exec_context.non_blocking, "Service" }, - { "BusName", config_parse_string_printf, 0, &u->service.bus_name, "Service" }, - { "NotifyAccess", config_parse_notify_access, 0, &u->service.notify_access, "Service" }, - { "Sockets", config_parse_service_sockets, 0, &u->service, "Service" }, - { "FsckPassNo", config_parse_fsck_passno, 0, &u->service.fsck_passno, "Service" }, - EXEC_CONTEXT_CONFIG_ITEMS(u->service.exec_context, "Service"), - - { "ListenStream", config_parse_listen, 0, &u->socket, "Socket" }, - { "ListenDatagram", config_parse_listen, 0, &u->socket, "Socket" }, - { "ListenSequentialPacket", config_parse_listen, 0, &u->socket, "Socket" }, - { "ListenFIFO", config_parse_listen, 0, &u->socket, "Socket" }, - { "ListenNetlink", config_parse_listen, 0, &u->socket, "Socket" }, - { "ListenSpecial", config_parse_listen, 0, &u->socket, "Socket" }, - { "ListenMessageQueue", config_parse_listen, 0, &u->socket, "Socket" }, - { "BindIPv6Only", config_parse_socket_bind, 0, &u->socket, "Socket" }, - { "Backlog", config_parse_unsigned, 0, &u->socket.backlog, "Socket" }, - { "BindToDevice", config_parse_bindtodevice, 0, &u->socket, "Socket" }, - { "ExecStartPre", config_parse_exec, 0, u->socket.exec_command+SOCKET_EXEC_START_PRE, "Socket" }, - { "ExecStartPost", config_parse_exec, 0, u->socket.exec_command+SOCKET_EXEC_START_POST, "Socket" }, - { "ExecStopPre", config_parse_exec, 0, u->socket.exec_command+SOCKET_EXEC_STOP_PRE, "Socket" }, - { "ExecStopPost", config_parse_exec, 0, u->socket.exec_command+SOCKET_EXEC_STOP_POST, "Socket" }, - { "TimeoutSec", config_parse_usec, 0, &u->socket.timeout_usec, "Socket" }, - { "DirectoryMode", config_parse_mode, 0, &u->socket.directory_mode, "Socket" }, - { "SocketMode", config_parse_mode, 0, &u->socket.socket_mode, "Socket" }, - { "Accept", config_parse_bool, 0, &u->socket.accept, "Socket" }, - { "MaxConnections", config_parse_unsigned, 0, &u->socket.max_connections, "Socket" }, - { "KeepAlive", config_parse_bool, 0, &u->socket.keep_alive, "Socket" }, - { "Priority", config_parse_int, 0, &u->socket.priority, "Socket" }, - { "ReceiveBuffer", config_parse_size, 0, &u->socket.receive_buffer, "Socket" }, - { "SendBuffer", config_parse_size, 0, &u->socket.send_buffer, "Socket" }, - { "IPTOS", config_parse_ip_tos, 0, &u->socket.ip_tos, "Socket" }, - { "IPTTL", config_parse_int, 0, &u->socket.ip_ttl, "Socket" }, - { "Mark", config_parse_int, 0, &u->socket.mark, "Socket" }, - { "PipeSize", config_parse_size, 0, &u->socket.pipe_size, "Socket" }, - { "FreeBind", config_parse_bool, 0, &u->socket.free_bind, "Socket" }, - { "Transparent", config_parse_bool, 0, &u->socket.transparent, "Socket" }, - { "Broadcast", config_parse_bool, 0, &u->socket.broadcast, "Socket" }, - { "TCPCongestion", config_parse_string, 0, &u->socket.tcp_congestion, "Socket" }, - { "MessageQueueMaxMessages", config_parse_long, 0, &u->socket.mq_maxmsg, "Socket" }, - { "MessageQueueMessageSize", config_parse_long, 0, &u->socket.mq_msgsize, "Socket" }, - { "Service", config_parse_socket_service, 0, &u->socket, "Socket" }, - EXEC_CONTEXT_CONFIG_ITEMS(u->socket.exec_context, "Socket"), - - { "What", config_parse_string, 0, &u->mount.parameters_fragment.what, "Mount" }, - { "Where", config_parse_path, 0, &u->mount.where, "Mount" }, - { "Options", config_parse_string, 0, &u->mount.parameters_fragment.options, "Mount" }, - { "Type", config_parse_string, 0, &u->mount.parameters_fragment.fstype, "Mount" }, - { "TimeoutSec", config_parse_usec, 0, &u->mount.timeout_usec, "Mount" }, - { "DirectoryMode", config_parse_mode, 0, &u->mount.directory_mode, "Mount" }, - EXEC_CONTEXT_CONFIG_ITEMS(u->mount.exec_context, "Mount"), - - { "Where", config_parse_path, 0, &u->automount.where, "Automount" }, - { "DirectoryMode", config_parse_mode, 0, &u->automount.directory_mode, "Automount" }, - - { "What", config_parse_path, 0, &u->swap.parameters_fragment.what, "Swap" }, - { "Priority", config_parse_int, 0, &u->swap.parameters_fragment.priority, "Swap" }, - { "TimeoutSec", config_parse_usec, 0, &u->swap.timeout_usec, "Swap" }, - EXEC_CONTEXT_CONFIG_ITEMS(u->swap.exec_context, "Swap"), - - { "OnActiveSec", config_parse_timer, 0, &u->timer, "Timer" }, - { "OnBootSec", config_parse_timer, 0, &u->timer, "Timer" }, - { "OnStartupSec", config_parse_timer, 0, &u->timer, "Timer" }, - { "OnUnitActiveSec", config_parse_timer, 0, &u->timer, "Timer" }, - { "OnUnitInactiveSec", config_parse_timer, 0, &u->timer, "Timer" }, - { "Unit", config_parse_timer_unit, 0, &u->timer, "Timer" }, - - { "PathExists", config_parse_path_spec, 0, &u->path, "Path" }, - { "PathExistsGlob", config_parse_path_spec, 0, &u->path, "Path" }, - { "PathChanged", config_parse_path_spec, 0, &u->path, "Path" }, - { "DirectoryNotEmpty", config_parse_path_spec, 0, &u->path, "Path" }, - { "Unit", config_parse_path_unit, 0, &u->path, "Path" }, - { "MakeDirectory", config_parse_bool, 0, &u->path.make_directory, "Path" }, - { "DirectoryMode", config_parse_mode, 0, &u->path.directory_mode, "Path" }, - - /* The [Install] section is ignored here. */ - { "Alias", NULL, 0, NULL, "Install" }, - { "WantedBy", NULL, 0, NULL, "Install" }, - { "Also", NULL, 0, NULL, "Install" }, - - { NULL, NULL, 0, NULL, NULL } - }; - -#undef EXEC_CONTEXT_CONFIG_ITEMS - - const char *sections[4]; int r; Set *symlink_names; FILE *f = NULL; @@ -2121,21 +2139,11 @@ static int load_from_path(Unit *u, const char *path) { Unit *merged; struct stat st; - if (!u) { - /* Dirty dirty hack. */ - dump_items((FILE*) path, items); - return 0; - } - assert(u); assert(path); - sections[0] = "Unit"; - sections[1] = section_table[u->meta.type]; - sections[2] = "Install"; - sections[3] = NULL; - - if (!(symlink_names = set_new(string_hash_func, string_compare_func))) + symlink_names = set_new(string_hash_func, string_compare_func); + if (!symlink_names) return -ENOMEM; if (path_is_absolute(path)) { @@ -2156,7 +2164,7 @@ static int load_from_path(Unit *u, const char *path) { } else { char **p; - STRV_FOREACH(p, u->meta.manager->lookup_paths.unit_path) { + STRV_FOREACH(p, u->manager->lookup_paths.unit_path) { /* Instead of opening the path right away, we manually * follow all symlinks and add their name to our unit @@ -2166,8 +2174,8 @@ static int load_from_path(Unit *u, const char *path) { goto finish; } - if (u->meta.manager->unit_path_cache && - !set_get(u->meta.manager->unit_path_cache, filename)) + if (u->manager->unit_path_cache && + !set_get(u->manager->unit_path_cache, filename)) r = -ENOENT; else r = open_follow(&filename, &f, symlink_names, &id); @@ -2203,7 +2211,7 @@ static int load_from_path(Unit *u, const char *path) { goto finish; if (merged != u) { - u->meta.load_state = UNIT_MERGED; + u->load_state = UNIT_MERGED; r = 0; goto finish; } @@ -2215,20 +2223,21 @@ static int load_from_path(Unit *u, const char *path) { } if (null_or_empty(&st)) - u->meta.load_state = UNIT_MASKED; + u->load_state = UNIT_MASKED; else { /* Now, parse the file contents */ - if ((r = config_parse(filename, f, sections, items, false, u)) < 0) + r = config_parse(filename, f, UNIT_VTABLE(u)->sections, config_item_perf_lookup, (void*) load_fragment_gperf_lookup, false, u); + if (r < 0) goto finish; - u->meta.load_state = UNIT_LOADED; + u->load_state = UNIT_LOADED; } - free(u->meta.fragment_path); - u->meta.fragment_path = filename; + free(u->fragment_path); + u->fragment_path = filename; filename = NULL; - u->meta.fragment_mtime = timespec_load(&st.st_mtim); + u->fragment_mtime = timespec_load(&st.st_mtim); r = 0; @@ -2248,49 +2257,49 @@ int unit_load_fragment(Unit *u) { const char *t; assert(u); - assert(u->meta.load_state == UNIT_STUB); - assert(u->meta.id); + assert(u->load_state == UNIT_STUB); + assert(u->id); /* First, try to find the unit under its id. We always look * for unit files in the default directories, to make it easy * to override things by placing things in /etc/systemd/system */ - if ((r = load_from_path(u, u->meta.id)) < 0) + if ((r = load_from_path(u, u->id)) < 0) return r; /* Try to find an alias we can load this with */ - if (u->meta.load_state == UNIT_STUB) - SET_FOREACH(t, u->meta.names, i) { + if (u->load_state == UNIT_STUB) + SET_FOREACH(t, u->names, i) { - if (t == u->meta.id) + if (t == u->id) continue; if ((r = load_from_path(u, t)) < 0) return r; - if (u->meta.load_state != UNIT_STUB) + if (u->load_state != UNIT_STUB) break; } /* And now, try looking for it under the suggested (originally linked) path */ - if (u->meta.load_state == UNIT_STUB && u->meta.fragment_path) { + if (u->load_state == UNIT_STUB && u->fragment_path) { - if ((r = load_from_path(u, u->meta.fragment_path)) < 0) + if ((r = load_from_path(u, u->fragment_path)) < 0) return r; - if (u->meta.load_state == UNIT_STUB) { + if (u->load_state == UNIT_STUB) { /* Hmm, this didn't work? Then let's get rid * of the fragment path stored for us, so that * we don't point to an invalid location. */ - free(u->meta.fragment_path); - u->meta.fragment_path = NULL; + free(u->fragment_path); + u->fragment_path = NULL; } } /* Look for a template */ - if (u->meta.load_state == UNIT_STUB && u->meta.instance) { + if (u->load_state == UNIT_STUB && u->instance) { char *k; - if (!(k = unit_name_template(u->meta.id))) + if (!(k = unit_name_template(u->id))) return -ENOMEM; r = load_from_path(u, k); @@ -2299,10 +2308,10 @@ int unit_load_fragment(Unit *u) { if (r < 0) return r; - if (u->meta.load_state == UNIT_STUB) - SET_FOREACH(t, u->meta.names, i) { + if (u->load_state == UNIT_STUB) + SET_FOREACH(t, u->names, i) { - if (t == u->meta.id) + if (t == u->id) continue; if (!(k = unit_name_template(t))) @@ -2314,7 +2323,7 @@ int unit_load_fragment(Unit *u) { if (r < 0) return r; - if (u->meta.load_state != UNIT_STUB) + if (u->load_state != UNIT_STUB) break; } } @@ -2323,7 +2332,100 @@ int unit_load_fragment(Unit *u) { } void unit_dump_config_items(FILE *f) { - /* OK, this wins a prize for extreme ugliness. */ + static const struct { + const ConfigParserCallback callback; + const char *rvalue; + } table[] = { + { config_parse_int, "INTEGER" }, + { config_parse_unsigned, "UNSIGNED" }, + { config_parse_bytes_size, "SIZE" }, + { config_parse_bool, "BOOLEAN" }, + { config_parse_string, "STRING" }, + { config_parse_path, "PATH" }, + { config_parse_unit_path_printf, "PATH" }, + { config_parse_strv, "STRING [...]" }, + { config_parse_exec_nice, "NICE" }, + { config_parse_exec_oom_score_adjust, "OOMSCOREADJUST" }, + { config_parse_exec_io_class, "IOCLASS" }, + { config_parse_exec_io_priority, "IOPRIORITY" }, + { config_parse_exec_cpu_sched_policy, "CPUSCHEDPOLICY" }, + { config_parse_exec_cpu_sched_prio, "CPUSCHEDPRIO" }, + { config_parse_exec_cpu_affinity, "CPUAFFINITY" }, + { config_parse_mode, "MODE" }, + { config_parse_unit_env_file, "FILE" }, + { config_parse_output, "OUTPUT" }, + { config_parse_input, "INPUT" }, + { config_parse_facility, "FACILITY" }, + { config_parse_level, "LEVEL" }, + { config_parse_exec_capabilities, "CAPABILITIES" }, + { config_parse_exec_secure_bits, "SECUREBITS" }, + { config_parse_exec_bounding_set, "BOUNDINGSET" }, + { config_parse_exec_timer_slack_nsec, "TIMERSLACK" }, + { config_parse_limit, "LIMIT" }, + { config_parse_unit_cgroup, "CGROUP [...]" }, + { config_parse_unit_deps, "UNIT [...]" }, + { config_parse_unit_names, "UNIT [...]" }, + { config_parse_exec, "PATH [ARGUMENT [...]]" }, + { config_parse_service_type, "SERVICETYPE" }, + { config_parse_service_restart, "SERVICERESTART" }, +#ifdef HAVE_SYSV_COMPAT + { config_parse_sysv_priority, "SYSVPRIORITY" }, +#else + { config_parse_warn_compat, "NOTSUPPORTED" }, +#endif + { config_parse_kill_mode, "KILLMODE" }, + { config_parse_kill_signal, "SIGNAL" }, + { config_parse_socket_listen, "SOCKET [...]" }, + { config_parse_socket_bind, "SOCKETBIND" }, + { config_parse_socket_bindtodevice, "NETWORKINTERFACE" }, + { config_parse_usec, "SECONDS" }, + { config_parse_path_strv, "PATH [...]" }, + { config_parse_exec_mount_flags, "MOUNTFLAG [...]" }, + { config_parse_unit_string_printf, "STRING" }, + { config_parse_timer, "TIMER" }, + { config_parse_timer_unit, "NAME" }, + { config_parse_path_spec, "PATH" }, + { config_parse_path_unit, "UNIT" }, + { config_parse_notify_access, "ACCESS" }, + { config_parse_ip_tos, "TOS" }, + { config_parse_unit_condition_path, "CONDITION" }, + { config_parse_unit_condition_string, "CONDITION" }, + { config_parse_unit_condition_null, "CONDITION" }, + }; + + const char *prev = NULL; + const char *i; + + assert(f); + + NULSTR_FOREACH(i, load_fragment_gperf_nulstr) { + const char *rvalue = "OTHER", *lvalue; + unsigned j; + size_t prefix_len; + const char *dot; + const ConfigPerfItem *p; + + assert_se(p = load_fragment_gperf_lookup(i, strlen(i))); - load_from_path(NULL, (const void*) f); + dot = strchr(i, '.'); + lvalue = dot ? dot + 1 : i; + prefix_len = dot-i; + + if (dot) + if (!prev || strncmp(prev, i, prefix_len+1) != 0) { + if (prev) + fputc('\n', f); + + fprintf(f, "[%.*s]\n", (int) prefix_len, i); + } + + for (j = 0; j < ELEMENTSOF(table); j++) + if (p->parse == table[j].callback) { + rvalue = table[j].rvalue; + break; + } + + fprintf(f, "%s=%s\n", lvalue, rvalue); + prev = i; + } } diff --git a/src/load-fragment.h b/src/load-fragment.h index bccfb11a7f..79fc76da92 100644 --- a/src/load-fragment.h +++ b/src/load-fragment.h @@ -30,4 +30,62 @@ int unit_load_fragment(Unit *u); void unit_dump_config_items(FILE *f); +int config_parse_warn_compat(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_unit_deps(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_unit_names(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_unit_string_printf(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_unit_strv_printf(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_unit_path_printf(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_socket_listen(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_socket_bind(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_exec_nice(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_exec_oom_score_adjust(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_exec(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_service_type(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_service_restart(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_socket_bindtodevice(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_output(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_input(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_facility(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_level(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_exec_io_class(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_exec_io_priority(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_exec_cpu_sched_policy(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_exec_cpu_sched_prio(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_exec_cpu_affinity(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_exec_capabilities(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_exec_secure_bits(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_exec_bounding_set(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_exec_timer_slack_nsec(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_limit(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_unit_cgroup(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_sysv_priority(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_fsck_passno(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_kill_signal(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_exec_mount_flags(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_timer(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_timer_unit(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_path_spec(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_path_unit(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_socket_service(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_service_sockets(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_unit_env_file(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_ip_tos(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_unit_condition_path(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_unit_condition_string(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_unit_condition_null(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_kill_mode(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_notify_access(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_start_limit_action(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_unit_cgroup_attr(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_unit_cpu_shares(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_unit_memory_limit(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_unit_device_allow(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_unit_blkio_weight(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_unit_blkio_bandwidth(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); + +/* gperf prototypes */ +const struct ConfigPerfItem* load_fragment_gperf_lookup(const char *key, unsigned length); +extern const char load_fragment_gperf_nulstr[]; + #endif diff --git a/src/locale-setup.c b/src/locale-setup.c index 33111da218..7f692e9c5d 100644 --- a/src/locale-setup.c +++ b/src/locale-setup.c @@ -26,12 +26,14 @@ #include "locale-setup.h" #include "util.h" #include "macro.h" +#include "virt.h" enum { /* We don't list LC_ALL here on purpose. People should be * using LANG instead. */ VARIABLE_LANG, + VARIABLE_LANGUAGE, VARIABLE_LC_CTYPE, VARIABLE_LC_NUMERIC, VARIABLE_LC_TIME, @@ -49,6 +51,7 @@ enum { static const char * const variable_names[_VARIABLE_MAX] = { [VARIABLE_LANG] = "LANG", + [VARIABLE_LANGUAGE] = "LANGUAGE", [VARIABLE_LC_CTYPE] = "LC_CTYPE", [VARIABLE_LC_NUMERIC] = "LC_NUMERIC", [VARIABLE_LC_TIME] = "LC_TIME", @@ -75,6 +78,7 @@ int locale_setup(void) { "LANG", &variables[VARIABLE_LANG], #endif "locale.LANG", &variables[VARIABLE_LANG], + "locale.LANGUAGE", &variables[VARIABLE_LANGUAGE], "locale.LC_CTYPE", &variables[VARIABLE_LC_CTYPE], "locale.LC_NUMERIC", &variables[VARIABLE_LC_NUMERIC], "locale.LC_TIME", &variables[VARIABLE_LC_TIME], @@ -98,6 +102,7 @@ int locale_setup(void) { if (r <= 0 && (r = parse_env_file("/etc/locale.conf", NEWLINE, "LANG", &variables[VARIABLE_LANG], + "LANGUAGE", &variables[VARIABLE_LANGUAGE], "LC_CTYPE", &variables[VARIABLE_LC_CTYPE], "LC_NUMERIC", &variables[VARIABLE_LC_NUMERIC], "LC_TIME", &variables[VARIABLE_LC_TIME], @@ -194,7 +199,7 @@ int locale_setup(void) { if (r != -ENOENT) log_warning("Failed to read /etc/profile.env: %s", strerror(-r)); } -#elif defined(TARGET_MANDRIVA) +#elif defined(TARGET_MANDRIVA) || defined(TARGET_MAGEIA ) if (r <= 0 && (r = parse_env_file("/etc/sysconfig/i18n", NEWLINE, "LANG", &variables[VARIABLE_LANG], @@ -212,8 +217,8 @@ int locale_setup(void) { "LC_IDENTIFICATION", &variables[VARIABLE_LC_IDENTIFICATION], NULL)) < 0) { - if (r != -ENOENT) - log_warning("Failed to read /etc/sysconfig/i18n: %s", strerror(-r)); + if (r != -ENOENT) + log_warning("Failed to read /etc/sysconfig/i18n: %s", strerror(-r)); } #endif diff --git a/src/locale/.gitignore b/src/locale/.gitignore new file mode 100644 index 0000000000..b1e0ba755e --- /dev/null +++ b/src/locale/.gitignore @@ -0,0 +1 @@ +org.freedesktop.locale1.policy diff --git a/src/locale/Makefile b/src/locale/Makefile new file mode 120000 index 0000000000..d0b0e8e008 --- /dev/null +++ b/src/locale/Makefile @@ -0,0 +1 @@ +../Makefile
\ No newline at end of file diff --git a/src/locale/generate-kbd-model-map b/src/locale/generate-kbd-model-map new file mode 100755 index 0000000000..624c5179fa --- /dev/null +++ b/src/locale/generate-kbd-model-map @@ -0,0 +1,33 @@ +#!/usr/bin/python + +import sys +import system_config_keyboard.keyboard_models + +def strdash(s): + return s.strip() or '-' + +def tab_extend(s, n=1): + s = strdash(s) + k = len(s) // 8 + + if k >= n: + f = 1 + else: + f = n - k + + return s + '\t'*f + + +models = system_config_keyboard.keyboard_models.KeyboardModels().get_models() + +print "# Generated from system-config-keyboard's model list" +print "# consolelayout\t\txlayout\txmodel\t\txvariant\txoptions" + +for key, value in reversed(models.items()): + options = "terminate:ctrl_alt_bksp" + if value[4]: + options += ',' + value[4] + + print ''.join((tab_extend(key, 3), tab_extend(value[1]), + tab_extend(value[2], 2), tab_extend(value[3], 2), + options)) diff --git a/src/locale/kbd-model-map b/src/locale/kbd-model-map new file mode 100644 index 0000000000..a895880269 --- /dev/null +++ b/src/locale/kbd-model-map @@ -0,0 +1,72 @@ +# Generated from system-config-keyboard's model list +# consolelayout xlayout xmodel xvariant xoptions +sg ch pc105 de_nodeadkeys terminate:ctrl_alt_bksp +nl nl pc105 - terminate:ctrl_alt_bksp +mk-utf mkd,us pc105 - terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll +trq tr pc105 - terminate:ctrl_alt_bksp +guj in,us pc105 guj terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll +uk gb pc105 - terminate:ctrl_alt_bksp +is-latin1 is pc105 - terminate:ctrl_alt_bksp +de de pc105 - terminate:ctrl_alt_bksp +gur gur,us pc105 - terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll +la-latin1 latam pc105 - terminate:ctrl_alt_bksp +us us pc105+inet - terminate:ctrl_alt_bksp +ko kr pc105 - terminate:ctrl_alt_bksp +ro-std ro pc105 std terminate:ctrl_alt_bksp +de-latin1 de pc105 - terminate:ctrl_alt_bksp +tml-inscript in,us pc105 tam terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll +slovene si pc105 - terminate:ctrl_alt_bksp +hu101 hu pc105 qwerty terminate:ctrl_alt_bksp +jp106 jp jp106 - terminate:ctrl_alt_bksp +croat hr pc105 - terminate:ctrl_alt_bksp +ben-probhat in,us pc105 ben_probhat terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll +fi-latin1 fi pc105 - terminate:ctrl_alt_bksp +it2 it pc105 - terminate:ctrl_alt_bksp +hu hu pc105 - terminate:ctrl_alt_bksp +sr-latin rs pc105 latin terminate:ctrl_alt_bksp +fi fi pc105 - terminate:ctrl_alt_bksp +fr_CH ch pc105 fr terminate:ctrl_alt_bksp +dk-latin1 dk pc105 - terminate:ctrl_alt_bksp +fr fr pc105 - terminate:ctrl_alt_bksp +it it pc105 - terminate:ctrl_alt_bksp +tml-uni in,us pc105 tam_TAB terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll +ua-utf ua,us pc105 - terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll +fr-latin1 fr pc105 - terminate:ctrl_alt_bksp +sg-latin1 ch pc105 de_nodeadkeys terminate:ctrl_alt_bksp +be-latin1 be pc105 - terminate:ctrl_alt_bksp +dk dk pc105 - terminate:ctrl_alt_bksp +fr-pc fr pc105 - terminate:ctrl_alt_bksp +bg_pho-utf8 bg,us pc105 ,phonetic terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll +it-ibm it pc105 - terminate:ctrl_alt_bksp +cz-us-qwertz cz,us pc105 - terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll +ar-digits ara,us pc105 digits terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll +br-abnt2 br abnt2 - terminate:ctrl_alt_bksp +ro ro pc105 - terminate:ctrl_alt_bksp +us-acentos us pc105 intl terminate:ctrl_alt_bksp +pt-latin1 pt pc105 - terminate:ctrl_alt_bksp +ro-std-cedilla ro pc105 std_cedilla terminate:ctrl_alt_bksp +tj tj pc105 - terminate:ctrl_alt_bksp +ar-qwerty ara,us pc105 qwerty terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll +ar-azerty-digits ara,us pc105 azerty_digits terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll +ben in,us pc105 ben terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll +de-latin1-nodeadkeys de pc105 nodeadkeys terminate:ctrl_alt_bksp +no no pc105 - terminate:ctrl_alt_bksp +bg_bds-utf8 bg,us pc105 - terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll +dvorak us pc105 dvorak terminate:ctrl_alt_bksp +ru ru,us pc105 - terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll +cz-lat2 cz pc105 qwerty terminate:ctrl_alt_bksp +pl2 pl pc105 - terminate:ctrl_alt_bksp +es es pc105 - terminate:ctrl_alt_bksp +ro-cedilla ro pc105 cedilla terminate:ctrl_alt_bksp +ie ie pc105 - terminate:ctrl_alt_bksp +ar-azerty ara,us pc105 azerty terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll +ar-qwerty-digits ara,us pc105 qwerty_digits terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll +et ee pc105 - terminate:ctrl_alt_bksp +sk-qwerty sk pc105 - terminate:ctrl_alt_bksp,qwerty +dev dev,us pc105 - terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll +fr-latin9 fr pc105 latin9 terminate:ctrl_alt_bksp +fr_CH-latin1 ch pc105 fr terminate:ctrl_alt_bksp +cf ca(fr) pc105 - terminate:ctrl_alt_bksp +sv-latin1 se pc105 - terminate:ctrl_alt_bksp +sr-cy rs pc105 - terminate:ctrl_alt_bksp +gr gr,us pc105 - terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll diff --git a/src/locale/localed.c b/src/locale/localed.c new file mode 100644 index 0000000000..e9f9f86878 --- /dev/null +++ b/src/locale/localed.c @@ -0,0 +1,1437 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2011 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 <dbus/dbus.h> + +#include <errno.h> +#include <string.h> +#include <unistd.h> + +#include "util.h" +#include "strv.h" +#include "dbus-common.h" +#include "polkit.h" +#include "def.h" + +#define INTERFACE \ + " <interface name=\"org.freedesktop.locale1\">\n" \ + " <property name=\"Locale\" type=\"as\" access=\"read\"/>\n" \ + " <property name=\"VConsoleKeymap\" type=\"s\" access=\"read\"/>\n" \ + " <property name=\"VConsoleKeymapToggle\" type=\"s\" access=\"read\"/>\n" \ + " <property name=\"X11Layout\" type=\"s\" access=\"read\"/>\n" \ + " <property name=\"X11Model\" type=\"s\" access=\"read\"/>\n" \ + " <property name=\"X11Variant\" type=\"s\" access=\"read\"/>\n" \ + " <property name=\"X11Options\" type=\"s\" access=\"read\"/>\n" \ + " <method name=\"SetLocale\">\n" \ + " <arg name=\"locale\" type=\"as\" direction=\"in\"/>\n" \ + " <arg name=\"user_interaction\" type=\"b\" direction=\"in\"/>\n" \ + " </method>\n" \ + " <method name=\"SetVConsoleKeyboard\">\n" \ + " <arg name=\"keymap\" type=\"s\" direction=\"in\"/>\n" \ + " <arg name=\"keymap_toggle\" type=\"s\" direction=\"in\"/>\n" \ + " <arg name=\"convert\" type=\"b\" direction=\"in\"/>\n" \ + " <arg name=\"user_interaction\" type=\"b\" direction=\"in\"/>\n" \ + " </method>\n" \ + " <method name=\"SetX11Keyboard\">\n" \ + " <arg name=\"layout\" type=\"s\" direction=\"in\"/>\n" \ + " <arg name=\"model\" type=\"s\" direction=\"in\"/>\n" \ + " <arg name=\"variant\" type=\"s\" direction=\"in\"/>\n" \ + " <arg name=\"options\" type=\"s\" direction=\"in\"/>\n" \ + " <arg name=\"convert\" type=\"b\" direction=\"in\"/>\n" \ + " <arg name=\"user_interaction\" type=\"b\" direction=\"in\"/>\n" \ + " </method>\n" \ + " </interface>\n" + +#define INTROSPECTION \ + DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \ + "<node>\n" \ + INTERFACE \ + BUS_PROPERTIES_INTERFACE \ + BUS_INTROSPECTABLE_INTERFACE \ + BUS_PEER_INTERFACE \ + "</node>\n" + +#define INTERFACES_LIST \ + BUS_GENERIC_INTERFACES_LIST \ + "org.freedesktop.locale1\0" + +const char locale_interface[] _introspect_("locale1") = INTERFACE; + +enum { + /* We don't list LC_ALL here on purpose. People should be + * using LANG instead. */ + + PROP_LANG, + PROP_LANGUAGE, + PROP_LC_CTYPE, + PROP_LC_NUMERIC, + PROP_LC_TIME, + PROP_LC_COLLATE, + PROP_LC_MONETARY, + PROP_LC_MESSAGES, + PROP_LC_PAPER, + PROP_LC_NAME, + PROP_LC_ADDRESS, + PROP_LC_TELEPHONE, + PROP_LC_MEASUREMENT, + PROP_LC_IDENTIFICATION, + _PROP_MAX +}; + +static const char * const names[_PROP_MAX] = { + [PROP_LANG] = "LANG", + [PROP_LANGUAGE] = "LANGUAGE", + [PROP_LC_CTYPE] = "LC_CTYPE", + [PROP_LC_NUMERIC] = "LC_NUMERIC", + [PROP_LC_TIME] = "LC_TIME", + [PROP_LC_COLLATE] = "LC_COLLATE", + [PROP_LC_MONETARY] = "LC_MONETARY", + [PROP_LC_MESSAGES] = "LC_MESSAGES", + [PROP_LC_PAPER] = "LC_PAPER", + [PROP_LC_NAME] = "LC_NAME", + [PROP_LC_ADDRESS] = "LC_ADDRESS", + [PROP_LC_TELEPHONE] = "LC_TELEPHONE", + [PROP_LC_MEASUREMENT] = "LC_MEASUREMENT", + [PROP_LC_IDENTIFICATION] = "LC_IDENTIFICATION" +}; + +static char *data[_PROP_MAX] = { + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL +}; + +typedef struct State { + char *x11_layout, *x11_model, *x11_variant, *x11_options; + char *vc_keymap, *vc_keymap_toggle; +} State; + +static State state; + +static usec_t remain_until = 0; + +static int free_and_set(char **s, const char *v) { + int r; + char *t; + + assert(s); + + r = strdup_or_null(isempty(v) ? NULL : v, &t); + if (r < 0) + return r; + + free(*s); + *s = t; + + return 0; +} + +static void free_data_locale(void) { + int p; + + for (p = 0; p < _PROP_MAX; p++) { + free(data[p]); + data[p] = NULL; + } +} + +static void free_data_x11(void) { + free(state.x11_layout); + free(state.x11_model); + free(state.x11_variant); + free(state.x11_options); + + state.x11_layout = state.x11_model = state.x11_variant = state.x11_options = NULL; +} + +static void free_data_vconsole(void) { + free(state.vc_keymap); + free(state.vc_keymap_toggle); + + state.vc_keymap = state.vc_keymap_toggle = NULL; +} + +static void simplify(void) { + int p; + + for (p = 1; p < _PROP_MAX; p++) + if (isempty(data[p]) || streq_ptr(data[PROP_LANG], data[p])) { + free(data[p]); + data[p] = NULL; + } +} + +static int read_data_locale(void) { + int r; + + free_data_locale(); + + r = parse_env_file("/etc/locale.conf", NEWLINE, + "LANG", &data[PROP_LANG], + "LANGUAGE", &data[PROP_LANGUAGE], + "LC_CTYPE", &data[PROP_LC_CTYPE], + "LC_NUMERIC", &data[PROP_LC_NUMERIC], + "LC_TIME", &data[PROP_LC_TIME], + "LC_COLLATE", &data[PROP_LC_COLLATE], + "LC_MONETARY", &data[PROP_LC_MONETARY], + "LC_MESSAGES", &data[PROP_LC_MESSAGES], + "LC_PAPER", &data[PROP_LC_PAPER], + "LC_NAME", &data[PROP_LC_NAME], + "LC_ADDRESS", &data[PROP_LC_ADDRESS], + "LC_TELEPHONE", &data[PROP_LC_TELEPHONE], + "LC_MEASUREMENT", &data[PROP_LC_MEASUREMENT], + "LC_IDENTIFICATION", &data[PROP_LC_IDENTIFICATION], + NULL); + + if (r == -ENOENT) { + int p; + + /* Fill in what we got passed from systemd. */ + + for (p = 0; p < _PROP_MAX; p++) { + char *e, *d; + + assert(names[p]); + + e = getenv(names[p]); + if (e) { + d = strdup(e); + if (!d) + return -ENOMEM; + } else + d = NULL; + + free(data[p]); + data[p] = d; + } + + r = 0; + } + + simplify(); + return r; +} + +static void free_data(void) { + free_data_locale(); + free_data_vconsole(); + free_data_x11(); +} + +static int read_data_vconsole(void) { + int r; + + free_data_vconsole(); + + r = parse_env_file("/etc/vconsole.conf", NEWLINE, + "KEYMAP", &state.vc_keymap, + "KEYMAP_TOGGLE", &state.vc_keymap_toggle, + NULL); + + if (r < 0 && r != -ENOENT) + return r; + + return 0; +} + +static int read_data_x11(void) { + FILE *f; + char line[LINE_MAX]; + bool in_section = false; + + free_data_x11(); + + f = fopen("/etc/X11/xorg.conf.d/00-keyboard.conf", "re"); + if (!f) { + if (errno == ENOENT) { + +#ifdef TARGET_FEDORA + f = fopen("/etc/X11/xorg.conf.d/00-system-setup-keyboard.conf", "re"); + if (!f) { + if (errno == ENOENT) + return 0; + else + return -errno; + } +#else + return 0; +#endif + + } else + return -errno; + } + + while (fgets(line, sizeof(line), f)) { + char *l; + + char_array_0(line); + l = strstrip(line); + + if (l[0] == 0 || l[0] == '#') + continue; + + if (in_section && first_word(l, "Option")) { + char **a; + + a = strv_split_quoted(l); + if (!a) { + fclose(f); + return -ENOMEM; + } + + if (strv_length(a) == 3) { + + if (streq(a[1], "XkbLayout")) { + free(state.x11_layout); + state.x11_layout = a[2]; + a[2] = NULL; + } else if (streq(a[1], "XkbModel")) { + free(state.x11_model); + state.x11_model = a[2]; + a[2] = NULL; + } else if (streq(a[1], "XkbVariant")) { + free(state.x11_variant); + state.x11_variant = a[2]; + a[2] = NULL; + } else if (streq(a[1], "XkbOptions")) { + free(state.x11_options); + state.x11_options = a[2]; + a[2] = NULL; + } + } + + strv_free(a); + + } else if (!in_section && first_word(l, "Section")) { + char **a; + + a = strv_split_quoted(l); + if (!a) { + fclose(f); + return -ENOMEM; + } + + if (strv_length(a) == 2 && streq(a[1], "InputClass")) + in_section = true; + + strv_free(a); + } else if (in_section && first_word(l, "EndSection")) + in_section = false; + } + + fclose(f); + + return 0; +} + +static int read_data(void) { + int r, q, p; + + r = read_data_locale(); + q = read_data_vconsole(); + p = read_data_x11(); + + return r < 0 ? r : q < 0 ? q : p; +} + +static int write_data_locale(void) { + int r, p; + char **l = NULL; + + r = load_env_file("/etc/locale.conf", &l); + if (r < 0 && r != -ENOENT) + return r; + + for (p = 0; p < _PROP_MAX; p++) { + char *t, **u; + + assert(names[p]); + + if (isempty(data[p])) { + l = strv_env_unset(l, names[p]); + continue; + } + + if (asprintf(&t, "%s=%s", names[p], data[p]) < 0) { + strv_free(l); + return -ENOMEM; + } + + u = strv_env_set(l, t); + free(t); + strv_free(l); + + if (!u) + return -ENOMEM; + + l = u; + } + + if (strv_isempty(l)) { + strv_free(l); + + if (unlink("/etc/locale.conf") < 0) + return errno == ENOENT ? 0 : -errno; + + return 0; + } + + r = write_env_file("/etc/locale.conf", l); + strv_free(l); + + return r; +} + +static void push_data(DBusConnection *bus) { + char **l_set = NULL, **l_unset = NULL, **t; + int c_set = 0, c_unset = 0, p; + DBusError error; + DBusMessage *m = NULL, *reply = NULL; + DBusMessageIter iter, sub; + + dbus_error_init(&error); + + assert(bus); + + l_set = new0(char*, _PROP_MAX); + l_unset = new0(char*, _PROP_MAX); + if (!l_set || !l_unset) { + log_error("Out of memory"); + goto finish; + } + + for (p = 0; p < _PROP_MAX; p++) { + assert(names[p]); + + if (isempty(data[p])) + l_unset[c_set++] = (char*) names[p]; + else { + char *s; + + if (asprintf(&s, "%s=%s", names[p], data[p]) < 0) { + log_error("Out of memory"); + goto finish; + } + + l_set[c_unset++] = s; + } + } + + assert(c_set + c_unset == _PROP_MAX); + m = dbus_message_new_method_call("org.freedesktop.systemd1", "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "UnsetAndSetEnvironment"); + if (!m) { + log_error("Could not allocate message."); + goto finish; + } + + dbus_message_iter_init_append(m, &iter); + + if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "s", &sub)) { + log_error("Out of memory."); + goto finish; + } + + STRV_FOREACH(t, l_unset) + if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, t)) { + log_error("Out of memory."); + goto finish; + } + + if (!dbus_message_iter_close_container(&iter, &sub) || + !dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "s", &sub)) { + log_error("Out of memory."); + goto finish; + } + + STRV_FOREACH(t, l_set) + if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, t)) { + log_error("Out of memory."); + goto finish; + } + + if (!dbus_message_iter_close_container(&iter, &sub)) { + log_error("Out of memory."); + goto finish; + } + + reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error); + if (!reply) { + log_error("Failed to set locale information: %s", bus_error_message(&error)); + goto finish; + } + +finish: + if (m) + dbus_message_unref(m); + + if (reply) + dbus_message_unref(reply); + + dbus_error_free(&error); + + strv_free(l_set); + free(l_unset); +} + +static int write_data_vconsole(void) { + int r; + char **l = NULL; + + r = load_env_file("/etc/vconsole.conf", &l); + if (r < 0 && r != -ENOENT) + return r; + + if (isempty(state.vc_keymap)) + l = strv_env_unset(l, "KEYMAP"); + else { + char *s, **u; + + s = strappend("KEYMAP=", state.vc_keymap); + if (!s) { + strv_free(l); + return -ENOMEM; + } + + u = strv_env_set(l, s); + free(s); + strv_free(l); + + if (!u) + return -ENOMEM; + + l = u; + } + + if (isempty(state.vc_keymap_toggle)) + l = strv_env_unset(l, "KEYMAP_TOGGLE"); + else { + char *s, **u; + + s = strappend("KEYMAP_TOGGLE=", state.vc_keymap_toggle); + if (!s) { + strv_free(l); + return -ENOMEM; + } + + u = strv_env_set(l, s); + free(s); + strv_free(l); + + if (!u) + return -ENOMEM; + + l = u; + } + + if (strv_isempty(l)) { + strv_free(l); + + if (unlink("/etc/vconsole.conf") < 0) + return errno == ENOENT ? 0 : -errno; + + return 0; + } + + r = write_env_file("/etc/vconsole.conf", l); + strv_free(l); + + return r; +} + +static int write_data_x11(void) { + FILE *f; + char *temp_path; + int r; + + if (isempty(state.x11_layout) && + isempty(state.x11_model) && + isempty(state.x11_variant) && + isempty(state.x11_options)) { + +#ifdef TARGET_FEDORA + unlink("/etc/X11/xorg.conf.d/00-system-setup-keyboard.conf"); + + /* Symlink this to /dev/null, so that s-s-k (if it is + * still running) doesn't recreate this. */ + symlink("/dev/null", "/etc/X11/xorg.conf.d/00-system-setup-keyboard.conf"); +#endif + + if (unlink("/etc/X11/xorg.conf.d/00-keyboard.conf") < 0) + return errno == ENOENT ? 0 : -errno; + + return 0; + } + + mkdir_parents("/etc/X11/xorg.conf.d", 0755); + + r = fopen_temporary("/etc/X11/xorg.conf.d/00-keyboard.conf", &f, &temp_path); + if (r < 0) + return r; + + fchmod(fileno(f), 0644); + + fputs("# Read and parsed by systemd-localed. It's probably wise not to edit this file\n" + "# manually too freely.\n" + "Section \"InputClass\"\n" + " Identifier \"system-keyboard\"\n" + " MatchIsKeyboard \"on\"\n", f); + + if (!isempty(state.x11_layout)) + fprintf(f, " Option \"XkbLayout\" \"%s\"\n", state.x11_layout); + + if (!isempty(state.x11_model)) + fprintf(f, " Option \"XkbModel\" \"%s\"\n", state.x11_model); + + if (!isempty(state.x11_variant)) + fprintf(f, " Option \"XkbVariant\" \"%s\"\n", state.x11_variant); + + if (!isempty(state.x11_options)) + fprintf(f, " Option \"XkbOptions\" \"%s\"\n", state.x11_options); + + fputs("EndSection\n", f); + fflush(f); + + if (ferror(f) || rename(temp_path, "/etc/X11/xorg.conf.d/00-keyboard.conf") < 0) { + r = -errno; + unlink("/etc/X11/xorg.conf.d/00-keyboard.conf"); + unlink(temp_path); + } else { + +#ifdef TARGET_FEDORA + unlink("/etc/X11/xorg.conf.d/00-system-setup-keyboard.conf"); + + /* Symlink this to /dev/null, so that s-s-k (if it is + * still running) doesn't recreate this. */ + symlink("/dev/null", "/etc/X11/xorg.conf.d/00-system-setup-keyboard.conf"); +#endif + + r = 0; + } + + fclose(f); + free(temp_path); + + return r; +} + +static int load_vconsole_keymap(DBusConnection *bus, DBusError *error) { + DBusMessage *m = NULL, *reply = NULL; + const char *name = "systemd-vconsole-setup.service", *mode = "replace"; + int r; + DBusError _error; + + assert(bus); + + if (!error) { + dbus_error_init(&_error); + error = &_error; + } + + m = dbus_message_new_method_call( + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "RestartUnit"); + if (!m) { + log_error("Could not allocate message."); + r = -ENOMEM; + goto finish; + } + + if (!dbus_message_append_args(m, + DBUS_TYPE_STRING, &name, + DBUS_TYPE_STRING, &mode, + DBUS_TYPE_INVALID)) { + log_error("Could not append arguments to message."); + r = -ENOMEM; + goto finish; + } + + reply = dbus_connection_send_with_reply_and_block(bus, m, -1, error); + if (!reply) { + log_error("Failed to issue method call: %s", bus_error_message(error)); + r = -EIO; + goto finish; + } + + r = 0; + +finish: + if (m) + dbus_message_unref(m); + + if (reply) + dbus_message_unref(reply); + + if (error == &_error) + dbus_error_free(error); + + return r; +} + +static char *strnulldash(const char *s) { + return s == NULL || *s == 0 || (s[0] == '-' && s[1] == 0) ? NULL : (char*) s; +} + +static int read_next_mapping(FILE *f, unsigned *n, char ***a) { + assert(f); + assert(n); + assert(a); + + for (;;) { + char line[LINE_MAX]; + char *l, **b; + + errno = 0; + if (!fgets(line, sizeof(line), f)) { + + if (ferror(f)) + return errno ? -errno : -EIO; + + return 0; + } + + (*n) ++; + + l = strstrip(line); + if (l[0] == 0 || l[0] == '#') + continue; + + b = strv_split_quoted(l); + if (!b) + return -ENOMEM; + + if (strv_length(b) < 5) { + log_error("Invalid line "SYSTEMD_KBD_MODEL_MAP":%u, ignoring.", *n); + strv_free(b); + continue; + + } + + *a = b; + return 1; + } +} + +static int convert_vconsole_to_x11(DBusConnection *connection) { + bool modified = false; + + assert(connection); + + if (isempty(state.vc_keymap)) { + + modified = + !isempty(state.x11_layout) || + !isempty(state.x11_model) || + !isempty(state.x11_variant) || + !isempty(state.x11_options); + + free_data_x11(); + } else { + FILE *f; + unsigned n = 0; + + f = fopen(SYSTEMD_KBD_MODEL_MAP, "re"); + if (!f) + return -errno; + + for (;;) { + char **a; + int r; + + r = read_next_mapping(f, &n, &a); + if (r < 0) { + fclose(f); + return r; + } + + if (r == 0) + break; + + if (!streq(state.vc_keymap, a[0])) { + strv_free(a); + continue; + } + + if (!streq_ptr(state.x11_layout, strnulldash(a[1])) || + !streq_ptr(state.x11_model, strnulldash(a[2])) || + !streq_ptr(state.x11_variant, strnulldash(a[3])) || + !streq_ptr(state.x11_options, strnulldash(a[4]))) { + + if (free_and_set(&state.x11_layout, strnulldash(a[1])) < 0 || + free_and_set(&state.x11_model, strnulldash(a[2])) < 0 || + free_and_set(&state.x11_variant, strnulldash(a[3])) < 0 || + free_and_set(&state.x11_options, strnulldash(a[4])) < 0) { + strv_free(a); + fclose(f); + return -ENOMEM; + } + + modified = true; + } + + strv_free(a); + break; + } + + fclose(f); + } + + if (modified) { + dbus_bool_t b; + DBusMessage *changed; + int r; + + r = write_data_x11(); + if (r < 0) + log_error("Failed to set X11 keyboard layout: %s", strerror(-r)); + + changed = bus_properties_changed_new( + "/org/freedesktop/locale1", + "org.freedesktop.locale1", + "X11Layout\0" + "X11Model\0" + "X11Variant\0" + "X11Options\0"); + + if (!changed) + return -ENOMEM; + + b = dbus_connection_send(connection, changed, NULL); + dbus_message_unref(changed); + + if (!b) + return -ENOMEM; + } + + return 0; +} + +static int convert_x11_to_vconsole(DBusConnection *connection) { + bool modified = false; + + assert(connection); + + if (isempty(state.x11_layout)) { + + modified = + !isempty(state.vc_keymap) || + !isempty(state.vc_keymap_toggle); + + free_data_x11(); + } else { + FILE *f; + unsigned n = 0; + unsigned best_matching = 0; + char *new_keymap = NULL; + + f = fopen(SYSTEMD_KBD_MODEL_MAP, "re"); + if (!f) + return -errno; + + for (;;) { + char **a; + unsigned matching = 0; + int r; + + r = read_next_mapping(f, &n, &a); + if (r < 0) { + fclose(f); + return r; + } + + if (r == 0) + break; + + /* Determine how well matching this entry is */ + if (streq_ptr(state.x11_layout, a[1])) + /* If we got an exact match, this is best */ + matching = 10; + else { + size_t x; + + x = strcspn(state.x11_layout, ","); + + /* We have multiple X layouts, look + * for an entry that matches our key + * with the everything but the first + * layout stripped off. */ + if (x > 0 && + strlen(a[1]) == x && + strncmp(state.x11_layout, a[1], x) == 0) + matching = 5; + else { + size_t w; + + /* If that didn't work, strip + * off the other layouts from + * the entry, too */ + + w = strcspn(a[1], ","); + + if (x > 0 && x == w && + memcmp(state.x11_layout, a[1], x) == 0) + matching = 1; + } + } + + if (matching > 0 && + streq_ptr(state.x11_model, a[2])) { + matching++; + + if (streq_ptr(state.x11_variant, a[3])) { + matching++; + + if (streq_ptr(state.x11_options, a[4])) + matching++; + } + } + + /* The best matching entry so far, then let's + * save that */ + if (matching > best_matching) { + best_matching = matching; + + free(new_keymap); + new_keymap = strdup(a[0]); + + if (!new_keymap) { + strv_free(a); + fclose(f); + return -ENOMEM; + } + } + + strv_free(a); + } + + fclose(f); + + if (!streq_ptr(state.vc_keymap, new_keymap)) { + free(state.vc_keymap); + state.vc_keymap = new_keymap; + + free(state.vc_keymap_toggle); + state.vc_keymap_toggle = NULL; + + modified = true; + } else + free(new_keymap); + } + + if (modified) { + dbus_bool_t b; + DBusMessage *changed; + int r; + + r = write_data_vconsole(); + if (r < 0) + log_error("Failed to set virtual console keymap: %s", strerror(-r)); + + changed = bus_properties_changed_new( + "/org/freedesktop/locale1", + "org.freedesktop.locale1", + "VConsoleKeymap\0" + "VConsoleKeymapToggle\0"); + + if (!changed) + return -ENOMEM; + + b = dbus_connection_send(connection, changed, NULL); + dbus_message_unref(changed); + + if (!b) + return -ENOMEM; + + return load_vconsole_keymap(connection, NULL); + } + + return 0; +} + +static int append_locale(DBusMessageIter *i, const char *property, void *userdata) { + int r, c = 0, p; + char **l; + + l = new0(char*, _PROP_MAX+1); + if (!l) + return -ENOMEM; + + for (p = 0; p < _PROP_MAX; p++) { + char *t; + + if (isempty(data[p])) + continue; + + if (asprintf(&t, "%s=%s", names[p], data[p]) < 0) { + strv_free(l); + return -ENOMEM; + } + + l[c++] = t; + } + + r = bus_property_append_strv(i, property, (void*) l); + strv_free(l); + + return r; +} + +static const BusProperty bus_locale_properties[] = { + { "Locale", append_locale, "as", 0 }, + { "X11Layout", bus_property_append_string, "s", offsetof(State, x11_layout), true }, + { "X11Model", bus_property_append_string, "s", offsetof(State, x11_model), true }, + { "X11Variant", bus_property_append_string, "s", offsetof(State, x11_variant), true }, + { "X11Options", bus_property_append_string, "s", offsetof(State, x11_options), true }, + { "VConsoleKeymap", bus_property_append_string, "s", offsetof(State, vc_keymap), true }, + { "VConsoleKeymapToggle", bus_property_append_string, "s", offsetof(State, vc_keymap_toggle), true }, + { NULL, } +}; + +static const BusBoundProperties bps[] = { + { "org.freedesktop.locale1", bus_locale_properties, &state }, + { NULL, } +}; + +static DBusHandlerResult locale_message_handler( + DBusConnection *connection, + DBusMessage *message, + void *userdata) { + + DBusMessage *reply = NULL, *changed = NULL; + DBusError error; + int r; + + assert(connection); + assert(message); + + dbus_error_init(&error); + + if (dbus_message_is_method_call(message, "org.freedesktop.locale1", "SetLocale")) { + char **l = NULL, **i; + dbus_bool_t interactive; + DBusMessageIter iter; + bool modified = false; + bool passed[_PROP_MAX]; + int p; + + if (!dbus_message_iter_init(message, &iter)) + return bus_send_error_reply(connection, message, NULL, -EINVAL); + + r = bus_parse_strv_iter(&iter, &l); + if (r < 0) { + if (r == -ENOMEM) + goto oom; + + return bus_send_error_reply(connection, message, NULL, r); + } + + if (!dbus_message_iter_next(&iter) || + dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_BOOLEAN) { + strv_free(l); + return bus_send_error_reply(connection, message, NULL, -EINVAL); + } + + dbus_message_iter_get_basic(&iter, &interactive); + + zero(passed); + + /* Check whether a variable changed and if so valid */ + STRV_FOREACH(i, l) { + bool valid = false; + + for (p = 0; p < _PROP_MAX; p++) { + size_t k; + + k = strlen(names[p]); + if (startswith(*i, names[p]) && (*i)[k] == '=') { + valid = true; + passed[p] = true; + + if (!streq_ptr(*i + k + 1, data[p])) + modified = true; + + break; + } + } + + if (!valid) { + strv_free(l); + return bus_send_error_reply(connection, message, NULL, -EINVAL); + } + } + + /* Check whether a variable is unset */ + if (!modified) { + for (p = 0; p < _PROP_MAX; p++) + if (!isempty(data[p]) && !passed[p]) { + modified = true; + break; + } + } + + if (modified) { + + r = verify_polkit(connection, message, "org.freedesktop.locale1.set-locale", interactive, NULL, &error); + if (r < 0) { + strv_free(l); + return bus_send_error_reply(connection, message, &error, r); + } + + STRV_FOREACH(i, l) { + for (p = 0; p < _PROP_MAX; p++) { + size_t k; + + k = strlen(names[p]); + if (startswith(*i, names[p]) && (*i)[k] == '=') { + char *t; + + t = strdup(*i + k + 1); + if (!t) { + strv_free(l); + goto oom; + } + + free(data[p]); + data[p] = t; + + break; + } + } + } + + strv_free(l); + + for (p = 0; p < _PROP_MAX; p++) { + if (passed[p]) + continue; + + free(data[p]); + data[p] = NULL; + } + + simplify(); + + r = write_data_locale(); + if (r < 0) { + log_error("Failed to set locale: %s", strerror(-r)); + return bus_send_error_reply(connection, message, NULL, r); + } + + push_data(connection); + + log_info("Changed locale information."); + + changed = bus_properties_changed_new( + "/org/freedesktop/locale1", + "org.freedesktop.locale1", + "Locale\0"); + if (!changed) + goto oom; + } + } else if (dbus_message_is_method_call(message, "org.freedesktop.locale1", "SetVConsoleKeyboard")) { + + const char *keymap, *keymap_toggle; + dbus_bool_t convert, interactive; + + if (!dbus_message_get_args( + message, + &error, + DBUS_TYPE_STRING, &keymap, + DBUS_TYPE_STRING, &keymap_toggle, + DBUS_TYPE_BOOLEAN, &convert, + DBUS_TYPE_BOOLEAN, &interactive, + DBUS_TYPE_INVALID)) + return bus_send_error_reply(connection, message, &error, -EINVAL); + + if (isempty(keymap)) + keymap = NULL; + + if (isempty(keymap_toggle)) + keymap_toggle = NULL; + + if (!streq_ptr(keymap, state.vc_keymap) || + !streq_ptr(keymap_toggle, state.vc_keymap_toggle)) { + + r = verify_polkit(connection, message, "org.freedesktop.locale1.set-keyboard", interactive, NULL, &error); + if (r < 0) + return bus_send_error_reply(connection, message, &error, r); + + if (free_and_set(&state.vc_keymap, keymap) < 0 || + free_and_set(&state.vc_keymap_toggle, keymap_toggle) < 0) + goto oom; + + r = write_data_vconsole(); + if (r < 0) { + log_error("Failed to set virtual console keymap: %s", strerror(-r)); + return bus_send_error_reply(connection, message, NULL, r); + } + + log_info("Changed virtual console keymap to '%s'", strempty(state.vc_keymap)); + + r = load_vconsole_keymap(connection, NULL); + if (r < 0) + log_error("Failed to request keymap reload: %s", strerror(-r)); + + changed = bus_properties_changed_new( + "/org/freedesktop/locale1", + "org.freedesktop.locale1", + "VConsoleKeymap\0" + "VConsoleKeymapToggle\0"); + if (!changed) + goto oom; + + if (convert) { + r = convert_vconsole_to_x11(connection); + + if (r < 0) + log_error("Failed to convert keymap data: %s", strerror(-r)); + } + } + + } else if (dbus_message_is_method_call(message, "org.freedesktop.locale1", "SetX11Keyboard")) { + + const char *layout, *model, *variant, *options; + dbus_bool_t convert, interactive; + + if (!dbus_message_get_args( + message, + &error, + DBUS_TYPE_STRING, &layout, + DBUS_TYPE_STRING, &model, + DBUS_TYPE_STRING, &variant, + DBUS_TYPE_STRING, &options, + DBUS_TYPE_BOOLEAN, &convert, + DBUS_TYPE_BOOLEAN, &interactive, + DBUS_TYPE_INVALID)) + return bus_send_error_reply(connection, message, &error, -EINVAL); + + if (isempty(layout)) + layout = NULL; + + if (isempty(model)) + model = NULL; + + if (isempty(variant)) + variant = NULL; + + if (isempty(options)) + options = NULL; + + if (!streq_ptr(layout, state.x11_layout) || + !streq_ptr(model, state.x11_model) || + !streq_ptr(variant, state.x11_variant) || + !streq_ptr(options, state.x11_options)) { + + r = verify_polkit(connection, message, "org.freedesktop.locale1.set-keyboard", interactive, NULL, &error); + if (r < 0) + return bus_send_error_reply(connection, message, &error, r); + + if (free_and_set(&state.x11_layout, layout) < 0 || + free_and_set(&state.x11_model, model) < 0 || + free_and_set(&state.x11_variant, variant) < 0 || + free_and_set(&state.x11_options, options) < 0) + goto oom; + + r = write_data_x11(); + if (r < 0) { + log_error("Failed to set X11 keyboard layout: %s", strerror(-r)); + return bus_send_error_reply(connection, message, NULL, r); + } + + log_info("Changed X11 keyboard layout to '%s'", strempty(state.x11_layout)); + + changed = bus_properties_changed_new( + "/org/freedesktop/locale1", + "org.freedesktop.locale1", + "X11Layout\0" + "X11Model\0" + "X11Variant\0" + "X11Options\0"); + if (!changed) + goto oom; + + if (convert) { + r = convert_x11_to_vconsole(connection); + + if (r < 0) + log_error("Failed to convert keymap data: %s", strerror(-r)); + } + } + } else + return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, bps); + + if (!(reply = dbus_message_new_method_return(message))) + goto oom; + + if (!dbus_connection_send(connection, reply, NULL)) + goto oom; + + dbus_message_unref(reply); + reply = NULL; + + if (changed) { + + if (!dbus_connection_send(connection, changed, NULL)) + goto oom; + + dbus_message_unref(changed); + } + + return DBUS_HANDLER_RESULT_HANDLED; + +oom: + if (reply) + dbus_message_unref(reply); + + if (changed) + dbus_message_unref(changed); + + dbus_error_free(&error); + + return DBUS_HANDLER_RESULT_NEED_MEMORY; +} + +static int connect_bus(DBusConnection **_bus) { + static const DBusObjectPathVTable locale_vtable = { + .message_function = locale_message_handler + }; + DBusError error; + DBusConnection *bus = NULL; + int r; + + assert(_bus); + + dbus_error_init(&error); + + bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error); + if (!bus) { + log_error("Failed to get system D-Bus connection: %s", bus_error_message(&error)); + r = -ECONNREFUSED; + goto fail; + } + + dbus_connection_set_exit_on_disconnect(bus, FALSE); + + if (!dbus_connection_register_object_path(bus, "/org/freedesktop/locale1", &locale_vtable, NULL) || + !dbus_connection_add_filter(bus, bus_exit_idle_filter, &remain_until, NULL)) { + log_error("Not enough memory"); + r = -ENOMEM; + goto fail; + } + + r = dbus_bus_request_name(bus, "org.freedesktop.locale1", DBUS_NAME_FLAG_DO_NOT_QUEUE, &error); + if (dbus_error_is_set(&error)) { + log_error("Failed to register name on bus: %s", bus_error_message(&error)); + r = -EEXIST; + goto fail; + } + + if (r != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) { + log_error("Failed to acquire name."); + r = -EEXIST; + goto fail; + } + + if (_bus) + *_bus = bus; + + return 0; + +fail: + dbus_connection_close(bus); + dbus_connection_unref(bus); + + dbus_error_free(&error); + + return r; +} + +int main(int argc, char *argv[]) { + int r; + DBusConnection *bus = NULL; + bool exiting = false; + + log_set_target(LOG_TARGET_AUTO); + log_parse_environment(); + log_open(); + + umask(0022); + + if (argc == 2 && streq(argv[1], "--introspect")) { + fputs(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE + "<node>\n", stdout); + fputs(locale_interface, stdout); + fputs("</node>\n", stdout); + return 0; + } + + if (argc != 1) { + log_error("This program takes no arguments."); + r = -EINVAL; + goto finish; + } + + r = read_data(); + if (r < 0) { + log_error("Failed to read locale data: %s", strerror(-r)); + goto finish; + } + + r = connect_bus(&bus); + if (r < 0) + goto finish; + + remain_until = now(CLOCK_MONOTONIC) + DEFAULT_EXIT_USEC; + for (;;) { + + if (!dbus_connection_read_write_dispatch(bus, exiting ? -1 : (int) (DEFAULT_EXIT_USEC/USEC_PER_MSEC))) + break; + + if (!exiting && remain_until < now(CLOCK_MONOTONIC)) { + exiting = true; + bus_async_unregister_and_exit(bus, "org.freedesktop.locale1"); + } + } + + r = 0; + +finish: + free_data(); + + if (bus) { + dbus_connection_flush(bus); + dbus_connection_close(bus); + dbus_connection_unref(bus); + } + + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/org.freedesktop.locale1.conf b/src/locale/org.freedesktop.locale1.conf index 68273311e7..68273311e7 100644 --- a/src/org.freedesktop.locale1.conf +++ b/src/locale/org.freedesktop.locale1.conf diff --git a/src/org.freedesktop.locale1.policy.in b/src/locale/org.freedesktop.locale1.policy.in index 186d7d34c5..1ac50bf86c 100644 --- a/src/org.freedesktop.locale1.policy.in +++ b/src/locale/org.freedesktop.locale1.policy.in @@ -26,4 +26,14 @@ </defaults> </action> + <action id="org.freedesktop.locale1.set-keyboard"> + <_description>Set system keyboard settings</_description> + <_message>Authentication is required to set the system keyboard settings.</_message> + <defaults> + <allow_any>auth_admin_keep</allow_any> + <allow_inactive>auth_admin_keep</allow_inactive> + <allow_active>auth_admin_keep</allow_active> + </defaults> + </action> + </policyconfig> diff --git a/src/org.freedesktop.locale1.service b/src/locale/org.freedesktop.locale1.service index 29bd582459..29bd582459 100644 --- a/src/org.freedesktop.locale1.service +++ b/src/locale/org.freedesktop.locale1.service diff --git a/src/localed.c b/src/localed.c deleted file mode 100644 index 93e4e9bd56..0000000000 --- a/src/localed.c +++ /dev/null @@ -1,619 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -/*** - This file is part of systemd. - - Copyright 2011 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 <dbus/dbus.h> - -#include <errno.h> -#include <string.h> -#include <unistd.h> - -#include "util.h" -#include "strv.h" -#include "dbus-common.h" -#include "polkit.h" - -#define INTERFACE \ - " <interface name=\"org.freedesktop.locale1\">\n" \ - " <property name=\"Locale\" type=\"as\" access=\"read\"/>\n" \ - " <method name=\"SetLocale\">\n" \ - " <arg name=\"locale\" type=\"as\" direction=\"in\"/>\n" \ - " <arg name=\"user_interaction\" type=\"b\" direction=\"in\"/>\n" \ - " </method>\n" \ - " </interface>\n" - -#define INTROSPECTION \ - DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \ - "<node>\n" \ - INTERFACE \ - BUS_PROPERTIES_INTERFACE \ - BUS_INTROSPECTABLE_INTERFACE \ - BUS_PEER_INTERFACE \ - "</node>\n" - -#define INTERFACES_LIST \ - BUS_GENERIC_INTERFACES_LIST \ - "org.freedesktop.locale1\0" - -const char locale_interface[] _introspect_("locale1") = INTERFACE; - -enum { - /* We don't list LC_ALL here on purpose. People should be - * using LANG instead. */ - - PROP_LANG, - PROP_LC_CTYPE, - PROP_LC_NUMERIC, - PROP_LC_TIME, - PROP_LC_COLLATE, - PROP_LC_MONETARY, - PROP_LC_MESSAGES, - PROP_LC_PAPER, - PROP_LC_NAME, - PROP_LC_ADDRESS, - PROP_LC_TELEPHONE, - PROP_LC_MEASUREMENT, - PROP_LC_IDENTIFICATION, - _PROP_MAX -}; - -static const char * const names[_PROP_MAX] = { - [PROP_LANG] = "LANG", - [PROP_LC_CTYPE] = "LC_CTYPE", - [PROP_LC_NUMERIC] = "LC_NUMERIC", - [PROP_LC_TIME] = "LC_TIME", - [PROP_LC_COLLATE] = "LC_COLLATE", - [PROP_LC_MONETARY] = "LC_MONETARY", - [PROP_LC_MESSAGES] = "LC_MESSAGES", - [PROP_LC_PAPER] = "LC_PAPER", - [PROP_LC_NAME] = "LC_NAME", - [PROP_LC_ADDRESS] = "LC_ADDRESS", - [PROP_LC_TELEPHONE] = "LC_TELEPHONE", - [PROP_LC_MEASUREMENT] = "LC_MEASUREMENT", - [PROP_LC_IDENTIFICATION] = "LC_IDENTIFICATION" -}; - -static char *data[_PROP_MAX] = { - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL -}; - -static void free_data(void) { - int p; - - for (p = 0; p < _PROP_MAX; p++) { - free(data[p]); - data[p] = NULL; - } -} - -static void simplify(void) { - int p; - - for (p = 1; p < _PROP_MAX; p++) - if (isempty(data[p]) || streq_ptr(data[PROP_LANG], data[p])) { - free(data[p]); - data[p] = NULL; - } -} - -static int read_data(void) { - int r; - - free_data(); - - r = parse_env_file("/etc/locale.conf", NEWLINE, - "LANG", &data[PROP_LANG], - "LC_CTYPE", &data[PROP_LC_CTYPE], - "LC_NUMERIC", &data[PROP_LC_NUMERIC], - "LC_TIME", &data[PROP_LC_TIME], - "LC_COLLATE", &data[PROP_LC_COLLATE], - "LC_MONETARY", &data[PROP_LC_MONETARY], - "LC_MESSAGES", &data[PROP_LC_MESSAGES], - "LC_PAPER", &data[PROP_LC_PAPER], - "LC_NAME", &data[PROP_LC_NAME], - "LC_ADDRESS", &data[PROP_LC_ADDRESS], - "LC_TELEPHONE", &data[PROP_LC_TELEPHONE], - "LC_MEASUREMENT", &data[PROP_LC_MEASUREMENT], - "LC_IDENTIFICATION", &data[PROP_LC_IDENTIFICATION], - NULL); - - if (r == -ENOENT) { - int p; - - /* Fill in what we got passed from systemd. */ - - for (p = 0; p < _PROP_MAX; p++) { - char *e, *d; - - assert(names[p]); - - e = getenv(names[p]); - if (e) { - d = strdup(e); - if (!d) - return -ENOMEM; - } else - d = NULL; - - free(data[p]); - data[p] = d; - } - - r = 0; - } - - simplify(); - return r; -} - -static int write_data(void) { - int r, p; - char **l = NULL; - - r = load_env_file("/etc/locale.conf", &l); - if (r < 0 && r != -ENOENT) - return r; - - for (p = 0; p < _PROP_MAX; p++) { - char *t, **u; - - assert(names[p]); - - if (isempty(data[p])) { - l = strv_env_unset(l, names[p]); - continue; - } - - if (asprintf(&t, "%s=%s", names[p], data[p]) < 0) { - strv_free(l); - return -ENOMEM; - } - - u = strv_env_set(l, t); - free(t); - strv_free(l); - - if (!u) - return -ENOMEM; - - l = u; - } - - if (strv_isempty(l)) { - - if (unlink("/etc/locale.conf") < 0) - return errno == ENOENT ? 0 : -errno; - - return 0; - } - - r = write_env_file("/etc/locale.conf", l); - strv_free(l); - - return r; -} - -static void push_data(DBusConnection *bus) { - char **l_set = NULL, **l_unset = NULL, **t; - int c_set = 0, c_unset = 0, p; - DBusError error; - DBusMessage *m = NULL, *reply = NULL; - DBusMessageIter iter, sub; - - dbus_error_init(&error); - - assert(bus); - - l_set = new0(char*, _PROP_MAX); - l_unset = new0(char*, _PROP_MAX); - if (!l_set || !l_unset) { - log_error("Out of memory"); - goto finish; - } - - for (p = 0; p < _PROP_MAX; p++) { - assert(names[p]); - - if (isempty(data[p])) - l_unset[c_set++] = (char*) names[p]; - else { - char *s; - - if (asprintf(&s, "%s=%s", names[p], data[p]) < 0) { - log_error("Out of memory"); - goto finish; - } - - l_set[c_unset++] = s; - } - } - - assert(c_set + c_unset == _PROP_MAX); - m = dbus_message_new_method_call("org.freedesktop.systemd1", "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "UnsetAndSetEnvironment"); - if (!m) { - log_error("Could not allocate message."); - goto finish; - } - - dbus_message_iter_init_append(m, &iter); - - if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "s", &sub)) { - log_error("Out of memory."); - goto finish; - } - - STRV_FOREACH(t, l_unset) - if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, t)) { - log_error("Out of memory."); - goto finish; - } - - if (!dbus_message_iter_close_container(&iter, &sub) || - !dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "s", &sub)) { - log_error("Out of memory."); - goto finish; - } - - STRV_FOREACH(t, l_set) - if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, t)) { - log_error("Out of memory."); - goto finish; - } - - if (!dbus_message_iter_close_container(&iter, &sub)) { - log_error("Out of memory."); - goto finish; - } - - reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error); - if (!reply) { - log_error("Failed to set locale information: %s", bus_error_message(&error)); - goto finish; - } - -finish: - if (m) - dbus_message_unref(m); - - if (reply) - dbus_message_unref(reply); - - dbus_error_free(&error); - - strv_free(l_set); - free(l_unset); -} - -static int append_locale(DBusMessageIter *i, const char *property, void *userdata) { - int r, c = 0, p; - char **l; - - l = new0(char*, _PROP_MAX+1); - if (!l) - return -ENOMEM; - - for (p = 0; p < _PROP_MAX; p++) { - char *t; - - if (isempty(data[p])) - continue; - - if (asprintf(&t, "%s=%s", names[p], data[p]) < 0) { - strv_free(l); - return -ENOMEM; - } - - l[c++] = t; - } - - r = bus_property_append_strv(i, property, (void*) l); - strv_free(l); - - return r; -} - -static DBusHandlerResult locale_message_handler( - DBusConnection *connection, - DBusMessage *message, - void *userdata) { - - const BusProperty properties[] = { - { "org.freedesktop.locale1", "Locale", append_locale, "as", NULL}, - { NULL, NULL, NULL, NULL, NULL } - }; - - DBusMessage *reply = NULL, *changed = NULL; - DBusError error; - int r; - - assert(connection); - assert(message); - - dbus_error_init(&error); - - if (dbus_message_is_method_call(message, "org.freedesktop.locale1", "SetLocale")) { - char **l = NULL, **i; - dbus_bool_t interactive; - DBusMessageIter iter; - bool modified = false; - bool passed[_PROP_MAX]; - int p; - - if (!dbus_message_iter_init(message, &iter)) - return bus_send_error_reply(connection, message, NULL, -EINVAL); - - r = bus_parse_strv_iter(&iter, &l); - if (r < 0) { - if (r == -ENOMEM) - goto oom; - - return bus_send_error_reply(connection, message, NULL, r); - } - - if (!dbus_message_iter_next(&iter) || - dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_BOOLEAN) { - strv_free(l); - return bus_send_error_reply(connection, message, NULL, -EINVAL); - } - - dbus_message_iter_get_basic(&iter, &interactive); - - zero(passed); - - /* Check whether a variable changed and if so valid */ - STRV_FOREACH(i, l) { - bool valid = false; - - for (p = 0; p < _PROP_MAX; p++) { - size_t k; - - k = strlen(names[p]); - if (startswith(*i, names[p]) && (*i)[k] == '=') { - valid = true; - passed[p] = true; - - if (!streq_ptr(*i + k + 1, data[p])) - modified = true; - - break; - } - } - - if (!valid) { - strv_free(l); - return bus_send_error_reply(connection, message, NULL, -EINVAL); - } - } - - /* Check whether a variable is unset */ - if (!modified) { - for (p = 0; p < _PROP_MAX; p++) - if (!isempty(data[p]) && !passed[p]) { - modified = true; - break; - } - } - - if (modified) { - - r = verify_polkit(connection, message, "org.freedesktop.locale1.set-locale", interactive, &error); - if (r < 0) { - strv_free(l); - return bus_send_error_reply(connection, message, &error, r); - } - - STRV_FOREACH(i, l) { - for (p = 0; p < _PROP_MAX; p++) { - size_t k; - - k = strlen(names[p]); - if (startswith(*i, names[p]) && (*i)[k] == '=') { - char *t; - - t = strdup(*i + k + 1); - if (!t) { - strv_free(l); - goto oom; - } - - free(data[p]); - data[p] = t; - - break; - } - } - } - - for (p = 0; p < _PROP_MAX; p++) { - if (passed[p]) - continue; - - free(data[p]); - data[p] = NULL; - } - - simplify(); - - r = write_data(); - if (r < 0) { - log_error("Failed to set locale: %s", strerror(-r)); - return bus_send_error_reply(connection, message, NULL, r); - } - - push_data(connection); - - log_info("Changed locale information."); - - changed = bus_properties_changed_new( - "/org/freedesktop/locale1", - "org.freedesktop.locale1", - "Locale\0"); - if (!changed) - goto oom; - } - - } else - return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, properties); - - if (!(reply = dbus_message_new_method_return(message))) - goto oom; - - if (!dbus_connection_send(connection, reply, NULL)) - goto oom; - - dbus_message_unref(reply); - reply = NULL; - - if (changed) { - - if (!dbus_connection_send(connection, changed, NULL)) - goto oom; - - dbus_message_unref(changed); - } - - return DBUS_HANDLER_RESULT_HANDLED; - -oom: - if (reply) - dbus_message_unref(reply); - - if (changed) - dbus_message_unref(changed); - - dbus_error_free(&error); - - return DBUS_HANDLER_RESULT_NEED_MEMORY; -} - -static int connect_bus(DBusConnection **_bus) { - static const DBusObjectPathVTable locale_vtable = { - .message_function = locale_message_handler - }; - DBusError error; - DBusConnection *bus = NULL; - int r; - - assert(_bus); - - dbus_error_init(&error); - - bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error); - if (!bus) { - log_error("Failed to get system D-Bus connection: %s", bus_error_message(&error)); - r = -ECONNREFUSED; - goto fail; - } - - if (!dbus_connection_register_object_path(bus, "/org/freedesktop/locale1", &locale_vtable, NULL)) { - log_error("Not enough memory"); - r = -ENOMEM; - goto fail; - } - - r = dbus_bus_request_name(bus, "org.freedesktop.locale1", DBUS_NAME_FLAG_DO_NOT_QUEUE, &error); - if (dbus_error_is_set(&error)) { - log_error("Failed to register name on bus: %s", bus_error_message(&error)); - r = -EEXIST; - goto fail; - } - - if (r != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) { - log_error("Failed to acquire name."); - r = -EEXIST; - goto fail; - } - - if (_bus) - *_bus = bus; - - return 0; - -fail: - dbus_connection_close(bus); - dbus_connection_unref(bus); - - dbus_error_free(&error); - - return r; -} - -int main(int argc, char *argv[]) { - int r; - DBusConnection *bus = NULL; - - log_set_target(LOG_TARGET_AUTO); - log_parse_environment(); - log_open(); - - if (argc == 2 && streq(argv[1], "--introspect")) { - fputs(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE - "<node>\n", stdout); - fputs(locale_interface, stdout); - fputs("</node>\n", stdout); - return 0; - } - - if (argc != 1) { - log_error("This program takes no arguments."); - r = -EINVAL; - goto finish; - } - - umask(0022); - - r = read_data(); - if (r < 0) { - log_error("Failed to read locale data: %s", strerror(-r)); - goto finish; - } - - r = connect_bus(&bus); - if (r < 0) - goto finish; - - while (dbus_connection_read_write_dispatch(bus, -1)) - ; - - r = 0; - -finish: - free_data(); - - if (bus) { - dbus_connection_flush(bus); - dbus_connection_close(bus); - dbus_connection_unref(bus); - } - - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; -} @@ -26,12 +26,14 @@ #include <fcntl.h> #include <sys/socket.h> #include <sys/un.h> +#include <stddef.h> #include "log.h" #include "util.h" #include "macro.h" +#include "socket-util.h" -#define SYSLOG_TIMEOUT_USEC (5*USEC_PER_SEC) +#define SNDBUF_SIZE (8*1024*1024) static LogTarget log_target = LOG_TARGET_CONSOLE; static int log_max_level = LOG_INFO; @@ -39,6 +41,7 @@ static int log_max_level = LOG_INFO; static int console_fd = STDERR_FILENO; static int syslog_fd = -1; static int kmsg_fd = -1; +static int journal_fd = -1; static bool syslog_is_stream = false; @@ -69,7 +72,8 @@ static int log_open_console(void) { if (getpid() == 1) { - if ((console_fd = open_terminal("/dev/console", O_WRONLY|O_NOCTTY|O_CLOEXEC)) < 0) { + console_fd = open_terminal("/dev/console", O_WRONLY|O_NOCTTY|O_CLOEXEC); + if (console_fd < 0) { log_error("Failed to open /dev/console for logging: %s", strerror(-console_fd)); return console_fd; } @@ -95,7 +99,8 @@ static int log_open_kmsg(void) { if (kmsg_fd >= 0) return 0; - if ((kmsg_fd = open("/dev/kmsg", O_WRONLY|O_NOCTTY|O_CLOEXEC)) < 0) { + kmsg_fd = open("/dev/kmsg", O_WRONLY|O_NOCTTY|O_CLOEXEC); + if (kmsg_fd < 0) { log_error("Failed to open /dev/kmsg for logging: %s", strerror(errno)); return -errno; } @@ -115,28 +120,22 @@ void log_close_syslog(void) { } static int create_log_socket(int type) { - struct timeval tv; int fd; - if ((fd = socket(AF_UNIX, type|SOCK_CLOEXEC, 0)) < 0) - return -errno; + /* All output to the syslog/journal fds we do asynchronously, + * and if the buffers are full we just drop the messages */ - /* Make sure we don't block for more than 5s when talking to - * syslog */ - timeval_store(&tv, SYSLOG_TIMEOUT_USEC); - if (setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) < 0) { - close_nointr_nofail(fd); + fd = socket(AF_UNIX, type|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); + if (fd < 0) return -errno; - } + + fd_inc_sndbuf(fd, SNDBUF_SIZE); return fd; } static int log_open_syslog(void) { - union { - struct sockaddr sa; - struct sockaddr_un un; - } sa; + union sockaddr_union sa; int r; if (syslog_fd >= 0) @@ -146,8 +145,9 @@ static int log_open_syslog(void) { sa.un.sun_family = AF_UNIX; strncpy(sa.un.sun_path, "/dev/log", sizeof(sa.un.sun_path)); - if ((syslog_fd = create_log_socket(SOCK_DGRAM)) < 0) { - r = -errno; + syslog_fd = create_log_socket(SOCK_DGRAM); + if (syslog_fd < 0) { + r = syslog_fd; goto fail; } @@ -157,8 +157,9 @@ static int log_open_syslog(void) { /* Some legacy syslog systems still use stream * sockets. They really shouldn't. But what can we * do... */ - if ((syslog_fd = create_log_socket(SOCK_STREAM)) < 0) { - r = -errno; + syslog_fd = create_log_socket(SOCK_STREAM); + if (syslog_fd < 0) { + r = syslog_fd; goto fail; } @@ -181,6 +182,47 @@ fail: return r; } +void log_close_journal(void) { + + if (journal_fd < 0) + return; + + close_nointr_nofail(journal_fd); + journal_fd = -1; +} + +static int log_open_journal(void) { + union sockaddr_union sa; + int r; + + if (journal_fd >= 0) + return 0; + + journal_fd = create_log_socket(SOCK_DGRAM); + if (journal_fd < 0) { + r = journal_fd; + goto fail; + } + + zero(sa); + sa.un.sun_family = AF_UNIX; + strncpy(sa.un.sun_path, "/run/systemd/journal/socket", sizeof(sa.un.sun_path)); + + if (connect(journal_fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path)) < 0) { + r = -errno; + goto fail; + } + + log_debug("Successfully opened journal for logging."); + + return 0; + +fail: + log_close_journal(); + log_debug("Failed to open journal for logging: %s", strerror(-r)); + return r; +} + int log_open(void) { int r; @@ -191,6 +233,7 @@ int log_open(void) { * because there is no reason to close it. */ if (log_target == LOG_TARGET_NULL) { + log_close_journal(); log_close_syslog(); log_close_console(); return 0; @@ -201,22 +244,41 @@ int log_open(void) { isatty(STDERR_FILENO) <= 0) { if (log_target == LOG_TARGET_AUTO || - log_target == LOG_TARGET_SYSLOG_OR_KMSG || - log_target == LOG_TARGET_SYSLOG) - if ((r = log_open_syslog()) >= 0) { + log_target == LOG_TARGET_JOURNAL_OR_KMSG || + log_target == LOG_TARGET_JOURNAL) { + r = log_open_journal(); + if (r >= 0) { + log_close_syslog(); log_close_console(); return r; } + } + + if (log_target == LOG_TARGET_SYSLOG_OR_KMSG || + log_target == LOG_TARGET_SYSLOG) { + r = log_open_syslog(); + if (r >= 0) { + log_close_journal(); + log_close_console(); + return r; + } + } + if (log_target == LOG_TARGET_AUTO || + log_target == LOG_TARGET_JOURNAL_OR_KMSG || log_target == LOG_TARGET_SYSLOG_OR_KMSG || - log_target == LOG_TARGET_KMSG) - if ((r = log_open_kmsg()) >= 0) { + log_target == LOG_TARGET_KMSG) { + r = log_open_kmsg(); + if (r >= 0) { + log_close_journal(); log_close_syslog(); log_close_console(); return r; } + } } + log_close_journal(); log_close_syslog(); /* Get the real /dev/console if we are PID=1, hence reopen */ @@ -232,9 +294,14 @@ void log_set_target(LogTarget target) { } void log_close(void) { - log_close_console(); - log_close_kmsg(); + log_close_journal(); log_close_syslog(); + log_close_kmsg(); + log_close_console(); +} + +void log_forget_fds(void) { + console_fd = kmsg_fd = syslog_fd = journal_fd = -1; } void log_set_max_level(int level) { @@ -258,16 +325,18 @@ static int write_to_console( if (console_fd < 0) return 0; - snprintf(location, sizeof(location), "(%s:%u) ", file, line); - char_array_0(location); - highlight = LOG_PRI(level) <= LOG_ERR && show_color; zero(iovec); - if (show_location) + + if (show_location) { + snprintf(location, sizeof(location), "(%s:%u) ", file, line); + char_array_0(location); IOVEC_SET_STRING(iovec[n++], location); + } + if (highlight) - IOVEC_SET_STRING(iovec[n++], ANSI_HIGHLIGHT_ON); + IOVEC_SET_STRING(iovec[n++], ANSI_HIGHLIGHT_RED_ON); IOVEC_SET_STRING(iovec[n++], buffer); if (highlight) IOVEC_SET_STRING(iovec[n++], ANSI_HIGHLIGHT_OFF); @@ -326,7 +395,8 @@ static int write_to_syslog( for (;;) { ssize_t n; - if ((n = sendmsg(syslog_fd, &msghdr, MSG_NOSIGNAL)) < 0) + n = sendmsg(syslog_fd, &msghdr, MSG_NOSIGNAL); + if (n < 0) return -errno; if (!syslog_is_stream || @@ -371,6 +441,48 @@ static int write_to_kmsg( return 1; } +static int write_to_journal( + int level, + const char*file, + int line, + const char *func, + const char *buffer) { + + char header[LINE_MAX]; + struct iovec iovec[3]; + struct msghdr mh; + + if (journal_fd < 0) + return 0; + + snprintf(header, sizeof(header), + "PRIORITY=%i\n" + "CODE_FILE=%s\n" + "CODE_LINE=%i\n" + "CODE_FUNCTION=%s\n" + "MESSAGE=", + LOG_PRI(level), + file, + line, + func); + + char_array_0(header); + + zero(iovec); + IOVEC_SET_STRING(iovec[0], header); + IOVEC_SET_STRING(iovec[1], buffer); + IOVEC_SET_STRING(iovec[2], "\n"); + + zero(mh); + mh.msg_iov = iovec; + mh.msg_iovlen = ELEMENTSOF(iovec); + + if (sendmsg(journal_fd, &mh, MSG_NOSIGNAL) < 0) + return -errno; + + return 1; +} + static int log_dispatch( int level, const char*file, @@ -400,11 +512,25 @@ static int log_dispatch( *(e++) = 0; if (log_target == LOG_TARGET_AUTO || - log_target == LOG_TARGET_SYSLOG_OR_KMSG || + log_target == LOG_TARGET_JOURNAL_OR_KMSG || + log_target == LOG_TARGET_JOURNAL) { + + k = write_to_journal(level, file, line, func, buffer); + if (k < 0) { + if (k != -EAGAIN) + log_close_journal(); + log_open_kmsg(); + } else if (k > 0) + r++; + } + + if (log_target == LOG_TARGET_SYSLOG_OR_KMSG || log_target == LOG_TARGET_SYSLOG) { - if ((k = write_to_syslog(level, file, line, func, buffer)) < 0) { - log_close_syslog(); + k = write_to_syslog(level, file, line, func, buffer); + if (k < 0) { + if (k != -EAGAIN) + log_close_syslog(); log_open_kmsg(); } else if (k > 0) r++; @@ -415,16 +541,19 @@ static int log_dispatch( log_target == LOG_TARGET_SYSLOG_OR_KMSG || log_target == LOG_TARGET_KMSG)) { - if ((k = write_to_kmsg(level, file, line, func, buffer)) < 0) { + k = write_to_kmsg(level, file, line, func, buffer); + if (k < 0) { log_close_kmsg(); log_open_console(); } else if (k > 0) r++; } - if (k <= 0 && - (k = write_to_console(level, file, line, func, buffer)) < 0) - return k; + if (k <= 0) { + k = write_to_console(level, file, line, func, buffer); + if (k < 0) + return k; + } buffer = e; } while (buffer); @@ -481,34 +610,34 @@ int log_meta( return r; } -void log_assert( - const char*file, - int line, - const char *func, - const char *format, ...) { - +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +_noreturn_ static void log_assert(const char *text, const char *file, int line, const char *func, const char *format) { static char buffer[LINE_MAX]; - int saved_errno = errno; - va_list ap; - va_start(ap, format); - vsnprintf(buffer, sizeof(buffer), format, ap); - va_end(ap); + snprintf(buffer, sizeof(buffer), format, text, file, line, func); char_array_0(buffer); log_abort_msg = buffer; log_dispatch(LOG_CRIT, file, line, func, buffer); abort(); +} +#pragma GCC diagnostic pop - /* If the user chose to ignore this SIGABRT, we are happy to go on, as if nothing happened. */ - errno = saved_errno; +void log_assert_failed(const char *text, const char *file, int line, const char *func) { + log_assert(text, file, line, func, "Assertion '%s' failed at %s:%u, function %s(). Aborting."); +} + +void log_assert_failed_unreachable(const char *text, const char *file, int line, const char *func) { + log_assert(text, file, line, func, "Code should not be reached '%s' at %s:%u, function %s(). Aborting."); } int log_set_target_from_string(const char *e) { LogTarget t; - if ((t = log_target_from_string(e)) < 0) + t = log_target_from_string(e); + if (t < 0) return -EINVAL; log_set_target(t); @@ -518,8 +647,9 @@ int log_set_target_from_string(const char *e) { int log_set_max_level_from_string(const char *e) { int t; - if ((t = log_level_from_string(e)) < 0) - return -EINVAL; + t = log_level_from_string(e); + if (t < 0) + return t; log_set_max_level(t); return 0; @@ -564,8 +694,9 @@ void log_show_location(bool b) { int log_show_color_from_string(const char *e) { int t; - if ((t = parse_boolean(e)) < 0) - return -EINVAL; + t = parse_boolean(e); + if (t < 0) + return t; log_show_color(t); return 0; @@ -574,8 +705,9 @@ int log_show_color_from_string(const char *e) { int log_show_location_from_string(const char *e) { int t; - if ((t = parse_boolean(e)) < 0) - return -EINVAL; + t = parse_boolean(e); + if (t < 0) + return t; log_show_location(t); return 0; @@ -583,11 +715,13 @@ int log_show_location_from_string(const char *e) { static const char *const log_target_table[] = { [LOG_TARGET_CONSOLE] = "console", - [LOG_TARGET_SYSLOG] = "syslog", [LOG_TARGET_KMSG] = "kmsg", + [LOG_TARGET_JOURNAL] = "journal", + [LOG_TARGET_JOURNAL_OR_KMSG] = "journal-or-kmsg", + [LOG_TARGET_SYSLOG] = "syslog", [LOG_TARGET_SYSLOG_OR_KMSG] = "syslog-or-kmsg", - [LOG_TARGET_NULL] = "null", - [LOG_TARGET_AUTO] = "auto" + [LOG_TARGET_AUTO] = "auto", + [LOG_TARGET_NULL] = "null" }; DEFINE_STRING_TABLE_LOOKUP(log_target, LogTarget); @@ -27,14 +27,14 @@ #include "macro.h" -/* If set to SYSLOG and /dev/log can not be opened we fall back to - * KSMG. If KMSG fails, we fall back to CONSOLE */ typedef enum LogTarget{ LOG_TARGET_CONSOLE, LOG_TARGET_KMSG, + LOG_TARGET_JOURNAL, + LOG_TARGET_JOURNAL_OR_KMSG, LOG_TARGET_SYSLOG, LOG_TARGET_SYSLOG_OR_KMSG, - LOG_TARGET_AUTO, /* console if stderr is tty, SYSLOG_OR_KMSG otherwise */ + LOG_TARGET_AUTO, /* console if stderr is tty, JOURNAL_OR_KMSG otherwise */ LOG_TARGET_NULL, _LOG_TARGET_MAX, _LOG_TARGET_INVALID = -1 @@ -57,8 +57,10 @@ int log_get_max_level(void); int log_open(void); void log_close(void); +void log_forget_fds(void); void log_close_syslog(void); +void log_close_journal(void); void log_close_kmsg(void); void log_close_console(void); @@ -71,11 +73,8 @@ int log_meta( const char *func, const char *format, ...) _printf_attr_(5,6); -_noreturn_ void log_assert( - const char*file, - int line, - const char *func, - const char *format, ...) _printf_attr_(4,5); +_noreturn_ void log_assert_failed(const char *text, const char *file, int line, const char *func); +_noreturn_ void log_assert_failed_unreachable(const char *text, const char *file, int line, const char *func); /* This modifies the buffer passed! */ int log_dump_internal( diff --git a/src/logger.c b/src/logger.c deleted file mode 100644 index 81196dbe00..0000000000 --- a/src/logger.c +++ /dev/null @@ -1,692 +0,0 @@ -/*-*- 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 <sys/socket.h> -#include <sys/types.h> -#include <assert.h> -#include <time.h> -#include <string.h> -#include <stdio.h> -#include <errno.h> -#include <unistd.h> -#include <sys/poll.h> -#include <sys/epoll.h> -#include <sys/un.h> -#include <fcntl.h> - -#include "util.h" -#include "log.h" -#include "list.h" -#include "sd-daemon.h" -#include "tcpwrap.h" -#include "def.h" - -#define STREAMS_MAX 4096 -#define SERVER_FD_MAX 16 -#define TIMEOUT_MSEC ((int) (DEFAULT_EXIT_USEC/USEC_PER_MSEC)) - -typedef struct Stream Stream; - -typedef struct Server { - int syslog_fd; - int kmsg_fd; - int epoll_fd; - - unsigned n_server_fd; - - bool syslog_is_stream; - - LIST_HEAD(Stream, streams); - unsigned n_streams; -} Server; - -typedef enum StreamTarget { - STREAM_SYSLOG, - STREAM_KMSG -} StreamTarget; - -typedef enum StreamState { - STREAM_TARGET, - STREAM_PRIORITY, - STREAM_PROCESS, - STREAM_PREFIX, - STREAM_RUNNING -} StreamState; - -struct Stream { - Server *server; - - StreamState state; - - int fd; - - StreamTarget target; - int priority; - char *process; - pid_t pid; - uid_t uid; - gid_t gid; - - bool prefix:1; - bool tee_console:1; - - char buffer[LINE_MAX]; - size_t length; - - LIST_FIELDS(Stream, stream); -}; - -static int stream_log(Stream *s, char *p, usec_t ts) { - - char header_priority[16], header_time[64], header_pid[16]; - struct iovec iovec[5]; - int priority; - - assert(s); - assert(p); - - priority = s->priority; - - if (s->prefix) - parse_syslog_priority(&p, &priority); - - if (*p == 0) - return 0; - - /* Patch in LOG_USER facility if necessary */ - if ((priority & LOG_FACMASK) == 0) - priority = LOG_USER | LOG_PRI(priority); - - /* - * The format glibc uses to talk to the syslog daemon is: - * - * <priority>time process[pid]: msg - * - * The format the kernel uses is: - * - * <priority>msg\n - * - * We extend the latter to include the process name and pid. - */ - - snprintf(header_priority, sizeof(header_priority), "<%i>", priority); - char_array_0(header_priority); - - if (s->target == STREAM_SYSLOG) { - time_t t; - struct tm *tm; - - t = (time_t) (ts / USEC_PER_SEC); - if (!(tm = localtime(&t))) - return -EINVAL; - - if (strftime(header_time, sizeof(header_time), "%h %e %T ", tm) <= 0) - return -EINVAL; - } - - snprintf(header_pid, sizeof(header_pid), "[%lu]: ", (unsigned long) s->pid); - char_array_0(header_pid); - - zero(iovec); - IOVEC_SET_STRING(iovec[0], header_priority); - - if (s->target == STREAM_SYSLOG) { - struct msghdr msghdr; - union { - struct cmsghdr cmsghdr; - uint8_t buf[CMSG_SPACE(sizeof(struct ucred))]; - } control; - struct ucred *ucred; - - zero(control); - control.cmsghdr.cmsg_level = SOL_SOCKET; - control.cmsghdr.cmsg_type = SCM_CREDENTIALS; - control.cmsghdr.cmsg_len = CMSG_LEN(sizeof(struct ucred)); - - ucred = (struct ucred*) CMSG_DATA(&control.cmsghdr); - ucred->pid = s->pid; - ucred->uid = s->uid; - ucred->gid = s->gid; - - IOVEC_SET_STRING(iovec[1], header_time); - IOVEC_SET_STRING(iovec[2], s->process); - IOVEC_SET_STRING(iovec[3], header_pid); - IOVEC_SET_STRING(iovec[4], p); - - /* When using syslog via SOCK_STREAM separate the messages by NUL chars */ - if (s->server->syslog_is_stream) - iovec[4].iov_len++; - - zero(msghdr); - msghdr.msg_iov = iovec; - msghdr.msg_iovlen = ELEMENTSOF(iovec); - msghdr.msg_control = &control; - msghdr.msg_controllen = control.cmsghdr.cmsg_len; - - for (;;) { - ssize_t n; - - if ((n = sendmsg(s->server->syslog_fd, &msghdr, MSG_NOSIGNAL)) < 0) { - - if (errno == ESRCH) { - pid_t our_pid; - - /* Hmm, maybe the process this - * line originates from is - * dead? Then let's patch in - * our own pid and retry, - * since we have nothing - * better */ - - our_pid = getpid(); - - if (ucred->pid != our_pid) { - ucred->pid = our_pid; - continue; - } - } - - return -errno; - } - - if (!s->server->syslog_is_stream || - (size_t) n >= IOVEC_TOTAL_SIZE(iovec, ELEMENTSOF(iovec))) - break; - - IOVEC_INCREMENT(iovec, ELEMENTSOF(iovec), n); - } - - } else if (s->target == STREAM_KMSG) { - IOVEC_SET_STRING(iovec[1], s->process); - IOVEC_SET_STRING(iovec[2], header_pid); - IOVEC_SET_STRING(iovec[3], p); - IOVEC_SET_STRING(iovec[4], (char*) "\n"); - - if (writev(s->server->kmsg_fd, iovec, ELEMENTSOF(iovec)) < 0) - return -errno; - } else - assert_not_reached("Unknown log target"); - - if (s->tee_console) { - int console; - - if ((console = open_terminal("/dev/console", O_WRONLY|O_NOCTTY|O_CLOEXEC)) >= 0) { - IOVEC_SET_STRING(iovec[0], s->process); - IOVEC_SET_STRING(iovec[1], header_pid); - IOVEC_SET_STRING(iovec[2], p); - IOVEC_SET_STRING(iovec[3], (char*) "\n"); - - writev(console, iovec, 4); - } - - } - - return 0; -} - -static int stream_line(Stream *s, char *p, usec_t ts) { - int r; - - assert(s); - assert(p); - - p = strstrip(p); - - switch (s->state) { - - case STREAM_TARGET: - if (streq(p, "syslog") || streq(p, "syslog+console")) - s->target = STREAM_SYSLOG; - else if (streq(p, "kmsg") || streq(p, "kmsg+console")) { - - if (s->server->kmsg_fd >= 0 && s->uid == 0) - s->target = STREAM_KMSG; - else { - log_warning("/dev/kmsg logging not available."); - return -EPERM; - } - } else { - log_warning("Failed to parse log target line."); - return -EBADMSG; - } - - if (endswith(p, "+console")) - s->tee_console = true; - - s->state = STREAM_PRIORITY; - return 0; - - case STREAM_PRIORITY: - if ((r = safe_atoi(p, &s->priority)) < 0) { - log_warning("Failed to parse log priority line: %m"); - return r; - } - - if (s->priority < 0) { - log_warning("Log priority negative: %m"); - return -ERANGE; - } - - s->state = STREAM_PROCESS; - return 0; - - case STREAM_PROCESS: - if (!(s->process = strdup(p))) - return -ENOMEM; - - s->state = STREAM_PREFIX; - return 0; - - case STREAM_PREFIX: - - if ((r = parse_boolean(p)) < 0) - return r; - - s->prefix = r; - s->state = STREAM_RUNNING; - return 0; - - case STREAM_RUNNING: - return stream_log(s, p, ts); - } - - assert_not_reached("Unknown stream state"); -} - -static int stream_scan(Stream *s, usec_t ts) { - char *p; - size_t remaining; - int r = 0; - - assert(s); - - p = s->buffer; - remaining = s->length; - for (;;) { - char *newline; - - if (!(newline = memchr(p, '\n', remaining))) - break; - - *newline = 0; - - if ((r = stream_line(s, p, ts)) >= 0) { - remaining -= newline-p+1; - p = newline+1; - } - } - - if (p > s->buffer) { - memmove(s->buffer, p, remaining); - s->length = remaining; - } - - return r; -} - -static int stream_process(Stream *s, usec_t ts) { - ssize_t l; - int r; - assert(s); - - if ((l = read(s->fd, s->buffer+s->length, LINE_MAX-s->length)) < 0) { - - if (errno == EAGAIN) - return 0; - - log_warning("Failed to read from stream: %m"); - return -errno; - } - - - if (l == 0) - return 0; - - s->length += l; - r = stream_scan(s, ts); - - if (r < 0) - return r; - - return 1; -} - -static void stream_free(Stream *s) { - assert(s); - - if (s->server) { - assert(s->server->n_streams > 0); - s->server->n_streams--; - LIST_REMOVE(Stream, stream, s->server->streams, s); - - } - - if (s->fd >= 0) { - if (s->server) - epoll_ctl(s->server->epoll_fd, EPOLL_CTL_DEL, s->fd, NULL); - - close_nointr_nofail(s->fd); - } - - free(s->process); - free(s); -} - -static int stream_new(Server *s, int server_fd) { - Stream *stream; - int fd; - struct ucred ucred; - socklen_t len = sizeof(ucred); - struct epoll_event ev; - int r; - - assert(s); - - if ((fd = accept4(server_fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC)) < 0) - return -errno; - - if (s->n_streams >= STREAMS_MAX) { - log_warning("Too many connections, refusing connection."); - close_nointr_nofail(fd); - return 0; - } - - if (!socket_tcpwrap(fd, "systemd-logger")) { - close_nointr_nofail(fd); - return 0; - } - - if (!(stream = new0(Stream, 1))) { - close_nointr_nofail(fd); - return -ENOMEM; - } - - stream->fd = fd; - - if (getsockopt(stream->fd, SOL_SOCKET, SO_PEERCRED, &ucred, &len) < 0) { - r = -errno; - goto fail; - } - - if (shutdown(fd, SHUT_WR) < 0) { - r = -errno; - goto fail; - } - - zero(ev); - ev.data.ptr = stream; - ev.events = EPOLLIN; - if (epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0) { - r = -errno; - goto fail; - } - - stream->pid = ucred.pid; - stream->uid = ucred.uid; - stream->gid = ucred.gid; - - stream->server = s; - LIST_PREPEND(Stream, stream, s->streams, stream); - s->n_streams ++; - - return 0; - -fail: - stream_free(stream); - return r; -} - -static void server_done(Server *s) { - unsigned i; - assert(s); - - while (s->streams) - stream_free(s->streams); - - for (i = 0; i < s->n_server_fd; i++) - close_nointr_nofail(SD_LISTEN_FDS_START+i); - - if (s->syslog_fd >= 0) - close_nointr_nofail(s->syslog_fd); - - if (s->epoll_fd >= 0) - close_nointr_nofail(s->epoll_fd); - - if (s->kmsg_fd >= 0) - close_nointr_nofail(s->kmsg_fd); -} - -static int server_init(Server *s, unsigned n_sockets) { - int r; - unsigned i; - union { - struct sockaddr sa; - struct sockaddr_un un; - } sa; - - assert(s); - assert(n_sockets > 0); - - zero(*s); - - s->n_server_fd = n_sockets; - s->syslog_fd = -1; - s->kmsg_fd = -1; - - if ((s->epoll_fd = epoll_create1(EPOLL_CLOEXEC)) < 0) { - r = -errno; - log_error("Failed to create epoll object: %m"); - goto fail; - } - - for (i = 0; i < n_sockets; i++) { - struct epoll_event ev; - int fd; - - fd = SD_LISTEN_FDS_START+i; - - if ((r = sd_is_socket(fd, AF_UNSPEC, SOCK_STREAM, 1)) < 0) { - log_error("Failed to determine file descriptor type: %s", strerror(-r)); - goto fail; - } - - if (!r) { - log_error("Wrong file descriptor type."); - r = -EINVAL; - goto fail; - } - - /* 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 distinguish fd - * values and pointer values we want to make sure to - * write the full field unconditionally. */ - - zero(ev); - ev.events = EPOLLIN; - ev.data.ptr = INT_TO_PTR(fd); - if (epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0) { - r = -errno; - log_error("Failed to add server fd to epoll object: %m"); - goto fail; - } - } - - zero(sa); - sa.un.sun_family = AF_UNIX; - strncpy(sa.un.sun_path, "/dev/log", sizeof(sa.un.sun_path)); - - if ((s->syslog_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0)) < 0) { - r = -errno; - log_error("Failed to create log fd: %m"); - goto fail; - } - - if (connect(s->syslog_fd, &sa.sa, sizeof(sa)) < 0) { - close_nointr_nofail(s->syslog_fd); - - if ((s->syslog_fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0)) < 0) { - r = -errno; - log_error("Failed to create log fd: %m"); - goto fail; - } - - if (connect(s->syslog_fd, &sa.sa, sizeof(sa)) < 0) { - r = -errno; - log_error("Failed to connect log socket to /dev/log: %m"); - goto fail; - } - - s->syslog_is_stream = true; - } else - s->syslog_is_stream = false; - - /* /dev/kmsg logging is strictly optional */ - if ((s->kmsg_fd = open("/dev/kmsg", O_WRONLY|O_NOCTTY|O_CLOEXEC)) < 0) - log_warning("Failed to open /dev/kmsg for logging, disabling kernel log buffer support: %m"); - - return 0; - -fail: - server_done(s); - return r; -} - -static int process_event(Server *s, struct epoll_event *ev) { - int r; - - assert(s); - - /* Yes, this is a bit ugly, we assume that that valid pointers - * are > SD_LISTEN_FDS_START+SERVER_FD_MAX. Which is certainly - * true on Linux (and probably most other OSes, too, since the - * first 4k usually are part of a separate null pointer - * dereference page. */ - - if (PTR_TO_INT(ev->data.ptr) >= SD_LISTEN_FDS_START && - PTR_TO_INT(ev->data.ptr) < SD_LISTEN_FDS_START+(int)s->n_server_fd) { - - if (ev->events != EPOLLIN) { - log_info("Got invalid event from epoll. (1)"); - return -EIO; - } - - if ((r = stream_new(s, PTR_TO_INT(ev->data.ptr))) < 0) { - log_info("Failed to accept new connection: %s", strerror(-r)); - return r; - } - - } else { - usec_t ts; - Stream *stream = ev->data.ptr; - - ts = now(CLOCK_REALTIME); - - if (!(ev->events & EPOLLIN)) { - log_info("Got invalid event from epoll. (2)"); - stream_free(stream); - return 0; - } - - if ((r = stream_process(stream, ts)) <= 0) { - - if (r < 0) - log_info("Got error on stream: %s", strerror(-r)); - - stream_free(stream); - return 0; - } - } - - return 0; -} - -int main(int argc, char *argv[]) { - Server server; - int r = EXIT_FAILURE, n; - - if (getppid() != 1) { - log_error("This program should be invoked by init only."); - return EXIT_FAILURE; - } - - if (argc > 1) { - log_error("This program does not take arguments."); - return EXIT_FAILURE; - } - - log_set_target(LOG_TARGET_SYSLOG_OR_KMSG); - log_parse_environment(); - log_open(); - - if ((n = sd_listen_fds(true)) < 0) { - log_error("Failed to read listening file descriptors from environment: %s", strerror(-r)); - return EXIT_FAILURE; - } - - if (n <= 0 || n > SERVER_FD_MAX) { - log_error("No or too many file descriptors passed."); - return EXIT_FAILURE; - } - - if (server_init(&server, (unsigned) n) < 0) - return EXIT_FAILURE; - - log_debug("systemd-logger running as pid %lu", (unsigned long) getpid()); - - sd_notify(false, - "READY=1\n" - "STATUS=Processing requests..."); - - for (;;) { - struct epoll_event event; - int k; - - if ((k = epoll_wait(server.epoll_fd, - &event, 1, - server.n_streams <= 0 ? TIMEOUT_MSEC : -1)) < 0) { - - if (errno == EINTR) - continue; - - log_error("epoll_wait() failed: %m"); - goto fail; - } - - if (k <= 0) - break; - - if (process_event(&server, &event) < 0) - goto fail; - } - - r = EXIT_SUCCESS; - - log_debug("systemd-logger stopped as pid %lu", (unsigned long) getpid()); - -fail: - sd_notify(false, - "STATUS=Shutting down..."); - - server_done(&server); - - return r; -} diff --git a/src/login/.gitignore b/src/login/.gitignore new file mode 100644 index 0000000000..1c0f3995ed --- /dev/null +++ b/src/login/.gitignore @@ -0,0 +1,3 @@ +logind-gperf.c +org.freedesktop.login1.policy +73-seat-late.rules diff --git a/src/70-uaccess.rules b/src/login/70-uaccess.rules index 6932492260..6932492260 100644 --- a/src/70-uaccess.rules +++ b/src/login/70-uaccess.rules diff --git a/src/71-seat.rules b/src/login/71-seat.rules index 99425adfb7..99425adfb7 100644 --- a/src/71-seat.rules +++ b/src/login/71-seat.rules diff --git a/src/73-seat-late.rules.in b/src/login/73-seat-late.rules.in index e93a0e6da9..0847932d77 100644 --- a/src/73-seat-late.rules.in +++ b/src/login/73-seat-late.rules.in @@ -12,6 +12,6 @@ ENV{ID_SEAT}=="", IMPORT{parent}="ID_SEAT" ENV{ID_SEAT}!="", TAG+="$env{ID_SEAT}" -TAG=="uaccess", RUN+="@rootlibexecdir@/systemd-uaccess $env{DEVNAME} $env{ID_SEAT}" +TAG=="uaccess", ENV{MAJOR}!="", RUN+="@rootlibexecdir@/systemd-uaccess $env{DEVNAME} $env{ID_SEAT}" LABEL="seat_late_end" diff --git a/src/login/Makefile b/src/login/Makefile new file mode 120000 index 0000000000..d0b0e8e008 --- /dev/null +++ b/src/login/Makefile @@ -0,0 +1 @@ +../Makefile
\ No newline at end of file diff --git a/libsystemd-login.pc.in b/src/login/libsystemd-login.pc.in index cd36a9cb35..cd36a9cb35 100644 --- a/libsystemd-login.pc.in +++ b/src/login/libsystemd-login.pc.in diff --git a/src/libsystemd-login.sym b/src/login/libsystemd-login.sym index 0d51fa76e7..a5e6c1e756 100644 --- a/src/libsystemd-login.sym +++ b/src/login/libsystemd-login.sym @@ -33,3 +33,16 @@ global: local: *; }; + +LIBSYSTEMD_LOGIN_38 { +global: + sd_pid_get_unit; + sd_session_get_service; +} LIBSYSTEMD_LOGIN_31; + +LIBSYSTEMD_LOGIN_43 { +global: + sd_session_get_type; + sd_session_get_class; + sd_session_get_display; +} LIBSYSTEMD_LOGIN_38; diff --git a/src/loginctl.c b/src/login/loginctl.c index 53058d07a7..30e97e352a 100644 --- a/src/loginctl.c +++ b/src/login/loginctl.c @@ -64,6 +64,8 @@ static bool on_tty(void) { } static void pager_open_if_enabled(void) { + + /* Cache result before we open the pager */ on_tty(); if (!arg_no_pager) @@ -347,6 +349,7 @@ typedef struct SessionStatusInfo { const char *service; pid_t leader; const char *type; + const char *class; bool active; } SessionStatusInfo; @@ -391,7 +394,7 @@ static void print_session_status_info(SessionStatusInfo *i) { printf("\t Leader: %u", (unsigned) i->leader); - get_process_name(i->leader, &t); + get_process_comm(i->leader, &t); if (t) { printf(" (%s)", t); free(t); @@ -429,10 +432,19 @@ static void print_session_status_info(SessionStatusInfo *i) { if (i->type) printf("; type %s", i->type); + if (i->class) + printf("; class %s", i->class); + printf("\n"); - } else if (i->type) + } else if (i->type) { printf("\t Type: %s\n", i->type); + if (i->class) + printf("; class %s", i->class); + } else if (i->class) + printf("\t Class: %s\n", i->class); + + printf("\t Active: %s\n", yes_no(i->active)); if (i->control_group) { @@ -447,7 +459,7 @@ static void print_session_status_info(SessionStatusInfo *i) { else c = 0; - show_cgroup_by_path(i->control_group, "\t\t ", c); + show_cgroup_by_path(i->control_group, "\t\t ", c, false); } } } @@ -499,7 +511,7 @@ static void print_user_status_info(UserStatusInfo *i) { else c = 0; - show_cgroup_by_path(i->control_group, "\t\t ", c); + show_cgroup_by_path(i->control_group, "\t\t ", c, false); } } } @@ -569,6 +581,8 @@ static int status_property_session(const char *name, DBusMessageIter *iter, Sess i->service = s; else if (streq(name, "Type")) i->type = s; + else if (streq(name, "Class")) + i->class = s; } break; } @@ -1061,10 +1075,9 @@ static int show(DBusConnection *bus, char **args, unsigned n) { uid_t uid; uint32_t u; - r = get_user_creds((const char**) (args+i), &uid, NULL, NULL); - if (r < 0) { + ret = get_user_creds((const char**) (args+i), &uid, NULL, NULL); + if (ret < 0) { log_error("User %s unknown.", args[i]); - r = -ENOENT; goto finish; } @@ -1146,7 +1159,7 @@ finish: } static int activate(DBusConnection *bus, char **args, unsigned n) { - DBusMessage *m = NULL, *reply = NULL; + DBusMessage *m = NULL; int ret = 0; DBusError error; unsigned i; @@ -1157,6 +1170,8 @@ static int activate(DBusConnection *bus, char **args, unsigned n) { dbus_error_init(&error); for (i = 1; i < n; i++) { + DBusMessage *reply; + m = dbus_message_new_method_call( "org.freedesktop.login1", "/org/freedesktop/login1", @@ -1195,16 +1210,13 @@ finish: if (m) dbus_message_unref(m); - if (reply) - dbus_message_unref(reply); - dbus_error_free(&error); return ret; } static int kill_session(DBusConnection *bus, char **args, unsigned n) { - DBusMessage *m = NULL, *reply = NULL; + DBusMessage *m = NULL; int ret = 0; DBusError error; unsigned i; @@ -1218,6 +1230,8 @@ static int kill_session(DBusConnection *bus, char **args, unsigned n) { arg_kill_who = "all"; for (i = 1; i < n; i++) { + DBusMessage *reply; + m = dbus_message_new_method_call( "org.freedesktop.login1", "/org/freedesktop/login1", @@ -1255,16 +1269,13 @@ finish: if (m) dbus_message_unref(m); - if (reply) - dbus_message_unref(reply); - dbus_error_free(&error); return ret; } static int enable_linger(DBusConnection *bus, char **args, unsigned n) { - DBusMessage *m = NULL, *reply = NULL; + DBusMessage *m = NULL; int ret = 0; DBusError error; unsigned i; @@ -1278,6 +1289,7 @@ static int enable_linger(DBusConnection *bus, char **args, unsigned n) { b = streq(args[0], "enable-linger"); for (i = 1; i < n; i++) { + DBusMessage *reply; uint32_t u; uid_t uid; @@ -1327,16 +1339,13 @@ finish: if (m) dbus_message_unref(m); - if (reply) - dbus_message_unref(reply); - dbus_error_free(&error); return ret; } static int terminate_user(DBusConnection *bus, char **args, unsigned n) { - DBusMessage *m = NULL, *reply = NULL; + DBusMessage *m = NULL; int ret = 0; DBusError error; unsigned i; @@ -1349,6 +1358,7 @@ static int terminate_user(DBusConnection *bus, char **args, unsigned n) { for (i = 1; i < n; i++) { uint32_t u; uid_t uid; + DBusMessage *reply; m = dbus_message_new_method_call( "org.freedesktop.login1", @@ -1394,16 +1404,13 @@ finish: if (m) dbus_message_unref(m); - if (reply) - dbus_message_unref(reply); - dbus_error_free(&error); return ret; } static int kill_user(DBusConnection *bus, char **args, unsigned n) { - DBusMessage *m = NULL, *reply = NULL; + DBusMessage *m = NULL; int ret = 0; DBusError error; unsigned i; @@ -1417,6 +1424,7 @@ static int kill_user(DBusConnection *bus, char **args, unsigned n) { arg_kill_who = "all"; for (i = 1; i < n; i++) { + DBusMessage *reply; uid_t uid; uint32_t u; @@ -1465,16 +1473,13 @@ finish: if (m) dbus_message_unref(m); - if (reply) - dbus_message_unref(reply); - dbus_error_free(&error); return ret; } static int attach(DBusConnection *bus, char **args, unsigned n) { - DBusMessage *m = NULL, *reply = NULL; + DBusMessage *m = NULL; int ret = 0; DBusError error; unsigned i; @@ -1486,6 +1491,8 @@ static int attach(DBusConnection *bus, char **args, unsigned n) { dbus_error_init(&error); for (i = 2; i < n; i++) { + DBusMessage *reply; + m = dbus_message_new_method_call( "org.freedesktop.login1", "/org/freedesktop/login1", @@ -1523,9 +1530,6 @@ finish: if (m) dbus_message_unref(m); - if (reply) - dbus_message_unref(reply); - dbus_error_free(&error); return ret; @@ -1581,7 +1585,7 @@ finish: } static int terminate_seat(DBusConnection *bus, char **args, unsigned n) { - DBusMessage *m = NULL, *reply = NULL; + DBusMessage *m = NULL; int ret = 0; DBusError error; unsigned i; @@ -1592,6 +1596,8 @@ static int terminate_seat(DBusConnection *bus, char **args, unsigned n) { dbus_error_init(&error); for (i = 1; i < n; i++) { + DBusMessage *reply; + m = dbus_message_new_method_call( "org.freedesktop.login1", "/org/freedesktop/login1", @@ -1627,9 +1633,6 @@ finish: if (m) dbus_message_unref(m); - if (reply) - dbus_message_unref(reply); - dbus_error_free(&error); return ret; diff --git a/src/logind-acl.c b/src/login/logind-acl.c index 7f9b0ca15e..eb8a48d191 100644 --- a/src/logind-acl.c +++ b/src/login/logind-acl.c @@ -27,46 +27,7 @@ #include "logind-acl.h" #include "util.h" - -static int find_acl(acl_t acl, uid_t uid, acl_entry_t *entry) { - acl_entry_t i; - int found; - - assert(acl); - assert(entry); - - for (found = acl_get_entry(acl, ACL_FIRST_ENTRY, &i); - found > 0; - found = acl_get_entry(acl, ACL_NEXT_ENTRY, &i)) { - - acl_tag_t tag; - uid_t *u; - bool b; - - if (acl_get_tag_type(i, &tag) < 0) - return -errno; - - if (tag != ACL_USER) - continue; - - u = acl_get_qualifier(i); - if (!u) - return -errno; - - b = *u == uid; - acl_free(u); - - if (b) { - *entry = i; - return 1; - } - } - - if (found < 0) - return -errno; - - return 0; -} +#include "acl-util.h" static int flush_acl(acl_t acl) { acl_entry_t i; @@ -125,7 +86,7 @@ int devnode_acl(const char *path, } else if (del && old_uid > 0) { acl_entry_t entry; - r = find_acl(acl, old_uid, &entry); + r = acl_find_uid(acl, old_uid, &entry); if (r < 0) goto finish; @@ -144,7 +105,7 @@ int devnode_acl(const char *path, acl_permset_t permset; int rd, wt; - r = find_acl(acl, new_uid, &entry); + r = acl_find_uid(acl, new_uid, &entry); if (r < 0) goto finish; @@ -265,9 +226,9 @@ int devnode_acl_all(struct udev *udev, node = udev_device_get_devnode(d); if (!node) { + /* In case people mistag devices with nodes, we need to ignore this */ udev_device_unref(d); - r = -ENOMEM; - goto finish; + continue; } log_debug("Fixing up %s for seat %s...", node, sn); diff --git a/src/logind-acl.h b/src/login/logind-acl.h index 72740f5b95..72740f5b95 100644 --- a/src/logind-acl.h +++ b/src/login/logind-acl.h diff --git a/src/logind-dbus.c b/src/login/logind-dbus.c index b8f7d6718b..d8f4d89474 100644 --- a/src/logind-dbus.c +++ b/src/login/logind-dbus.c @@ -36,6 +36,10 @@ " <arg name=\"id\" type=\"s\" direction=\"in\"/>\n" \ " <arg name=\"session\" type=\"o\" direction=\"out\"/>\n" \ " </method>\n" \ + " <method name=\"GetSessionByPID\">\n" \ + " <arg name=\"pid\" type=\"u\" direction=\"in\"/>\n" \ + " <arg name=\"session\" type=\"o\" direction=\"out\"/>\n" \ + " </method>\n" \ " <method name=\"GetUser\">\n" \ " <arg name=\"uid\" type=\"u\" direction=\"in\"/>\n" \ " <arg name=\"user\" type=\"o\" direction=\"out\"/>\n" \ @@ -58,6 +62,7 @@ " <arg name=\"leader\" type=\"u\" direction=\"in\"/>\n" \ " <arg name=\"sevice\" type=\"s\" direction=\"in\"/>\n" \ " <arg name=\"type\" type=\"s\" direction=\"in\"/>\n" \ + " <arg name=\"class\" type=\"s\" direction=\"in\"/>\n" \ " <arg name=\"seat\" type=\"s\" direction=\"in\"/>\n" \ " <arg name=\"vtnr\" type=\"u\" direction=\"in\"/>\n" \ " <arg name=\"tty\" type=\"s\" direction=\"in\"/>\n" \ @@ -78,6 +83,10 @@ " <method name=\"ActivateSession\">\n" \ " <arg name=\"id\" type=\"s\" direction=\"in\"/>\n" \ " </method>\n" \ + " <method name=\"ActivateSessionOnSeat\">\n" \ + " <arg name=\"id\" type=\"s\" direction=\"in\"/>\n" \ + " <arg name=\"seat\" type=\"s\" direction=\"in\"/>\n" \ + " </method>\n" \ " <method name=\"LockSession\">\n" \ " <arg name=\"id\" type=\"s\" direction=\"in\"/>\n" \ " </method>\n" \ @@ -86,12 +95,12 @@ " </method>\n" \ " <method name=\"KillSession\">\n" \ " <arg name=\"id\" type=\"s\" direction=\"in\"/>\n" \ - " <arg name=\"who\" type=\"s\"/>\n" \ - " <arg name=\"signal\" type=\"s\"/>\n" \ + " <arg name=\"who\" type=\"s\" direction=\"in\"/>\n" \ + " <arg name=\"signal\" type=\"s\" direction=\"in\"/>\n" \ " </method>\n" \ " <method name=\"KillUser\">\n" \ " <arg name=\"uid\" type=\"u\" direction=\"in\"/>\n" \ - " <arg name=\"signal\" type=\"s\"/>\n" \ + " <arg name=\"signal\" type=\"s\" direction=\"in\"/>\n" \ " </method>\n" \ " <method name=\"TerminateSession\">\n" \ " <arg name=\"id\" type=\"s\" direction=\"in\"/>\n" \ @@ -121,6 +130,12 @@ " <method name=\"Reboot\">\n" \ " <arg name=\"interactive\" type=\"b\" direction=\"in\"/>\n" \ " </method>\n" \ + " <method name=\"CanPowerOff\">\n" \ + " <arg name=\"result\" type=\"s\" direction=\"out\"/>\n" \ + " </method>\n" \ + " <method name=\"CanReboot\">\n" \ + " <arg name=\"result\" type=\"s\" direction=\"out\"/>\n" \ + " </method>\n" \ " <signal name=\"SessionNew\">\n" \ " <arg name=\"id\" type=\"s\"/>\n" \ " <arg name=\"path\" type=\"o\"/>\n" \ @@ -208,11 +223,12 @@ static int bus_manager_append_idle_hint_since(DBusMessageIter *i, const char *pr static int bus_manager_create_session(Manager *m, DBusMessage *message, DBusMessage **_reply) { Session *session = NULL; User *user = NULL; - const char *type, *seat, *tty, *display, *remote_user, *remote_host, *service; + const char *type, *class, *seat, *tty, *display, *remote_user, *remote_host, *service; uint32_t uid, leader, audit_id = 0; dbus_bool_t remote, kill_processes; char **controllers = NULL, **reset_controllers = NULL; SessionType t; + SessionClass c; Seat *s; DBusMessageIter iter; int r; @@ -257,6 +273,17 @@ static int bus_manager_create_session(Manager *m, DBusMessage *message, DBusMess dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) return -EINVAL; + dbus_message_iter_get_basic(&iter, &class); + if (isempty(class)) + c = SESSION_USER; + else + c = session_class_from_string(class); + + if (c < 0 || + !dbus_message_iter_next(&iter) || + dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) + return -EINVAL; + dbus_message_iter_get_basic(&iter, &seat); if (isempty(seat)) @@ -301,7 +328,7 @@ static int bus_manager_create_session(Manager *m, DBusMessage *message, DBusMess return -EINVAL; if (s) { - if (seat_is_vtconsole(s)) { + if (seat_can_multi_session(s)) { if (vtnr <= 0 || vtnr > 63) return -EINVAL; } else { @@ -381,6 +408,7 @@ static int bus_manager_create_session(Manager *m, DBusMessage *message, DBusMess session = hashmap_get(m->sessions, id); if (session) { + free(id); fifo_fd = session_create_fifo(session); if (fifo_fd < 0) { @@ -404,12 +432,16 @@ static int bus_manager_create_session(Manager *m, DBusMessage *message, DBusMess goto fail; } + seat = session->seat ? session->seat->id : ""; + vtnr = session->vtnr; b = dbus_message_append_args( reply, DBUS_TYPE_STRING, &session->id, DBUS_TYPE_OBJECT_PATH, &p, DBUS_TYPE_STRING, &session->user->runtime_path, DBUS_TYPE_UNIX_FD, &fifo_fd, + DBUS_TYPE_STRING, &seat, + DBUS_TYPE_UINT32, &vtnr, DBUS_TYPE_INVALID); free(p); @@ -421,6 +453,9 @@ static int bus_manager_create_session(Manager *m, DBusMessage *message, DBusMess close_nointr_nofail(fifo_fd); *_reply = reply; + strv_free(controllers); + strv_free(reset_controllers); + return 0; } @@ -445,6 +480,7 @@ static int bus_manager_create_session(Manager *m, DBusMessage *message, DBusMess session->leader = leader; session->audit_id = audit_id; session->type = t; + session->class = c; session->remote = remote; session->controllers = controllers; session->reset_controllers = reset_controllers; @@ -698,6 +734,51 @@ static int flush_devices(Manager *m) { return trigger_device(m, NULL); } +static int have_multiple_sessions( + DBusConnection *connection, + Manager *m, + DBusMessage *message, + DBusError *error) { + + Session *s; + + assert(m); + + if (hashmap_size(m->sessions) > 1) + return true; + + /* Hmm, there's only one session, but let's make sure it + * actually belongs to the user who is asking. If not, better + * be safe than sorry. */ + + s = hashmap_first(m->sessions); + if (s) { + unsigned long ul; + + ul = dbus_bus_get_unix_user(connection, dbus_message_get_sender(message), error); + if (ul == (unsigned long) -1) + return -EIO; + + return s->user->uid != ul; + } + + return false; +} + +static const BusProperty bus_login_manager_properties[] = { + { "ControlGroupHierarchy", bus_property_append_string, "s", offsetof(Manager, cgroup_path), true }, + { "Controllers", bus_property_append_strv, "as", offsetof(Manager, controllers), true }, + { "ResetControllers", bus_property_append_strv, "as", offsetof(Manager, reset_controllers), true }, + { "NAutoVTs", bus_property_append_unsigned, "u", offsetof(Manager, n_autovts) }, + { "KillOnlyUsers", bus_property_append_strv, "as", offsetof(Manager, kill_only_users), true }, + { "KillExcludeUsers", bus_property_append_strv, "as", offsetof(Manager, kill_exclude_users), true }, + { "KillUserProcesses", bus_property_append_bool, "b", offsetof(Manager, kill_user_processes) }, + { "IdleHint", bus_manager_append_idle_hint, "b", 0 }, + { "IdleSinceHint", bus_manager_append_idle_hint_since, "t", 0 }, + { "IdleSinceHintMonotonic", bus_manager_append_idle_hint_since, "t", 0 }, + { NULL, } +}; + static DBusHandlerResult manager_message_handler( DBusConnection *connection, DBusMessage *message, @@ -705,20 +786,6 @@ static DBusHandlerResult manager_message_handler( Manager *m = userdata; - const BusProperty properties[] = { - { "org.freedesktop.login1.Manager", "ControlGroupHierarchy", bus_property_append_string, "s", m->cgroup_path }, - { "org.freedesktop.login1.Manager", "Controllers", bus_property_append_strv, "as", m->controllers }, - { "org.freedesktop.login1.Manager", "ResetControllers", bus_property_append_strv, "as", m->reset_controllers }, - { "org.freedesktop.login1.Manager", "NAutoVTs", bus_property_append_unsigned, "u", &m->n_autovts }, - { "org.freedesktop.login1.Manager", "KillOnlyUsers", bus_property_append_strv, "as", m->kill_only_users }, - { "org.freedesktop.login1.Manager", "KillExcludeUsers", bus_property_append_strv, "as", m->kill_exclude_users }, - { "org.freedesktop.login1.Manager", "KillUserProcesses", bus_property_append_bool, "b", &m->kill_user_processes }, - { "org.freedesktop.login1.Manager", "IdleHint", bus_manager_append_idle_hint, "b", m }, - { "org.freedesktop.login1.Manager", "IdleSinceHint", bus_manager_append_idle_hint_since, "t", m }, - { "org.freedesktop.login1.Manager", "IdleSinceHintMonotonic", bus_manager_append_idle_hint_since, "t", m }, - { NULL, NULL, NULL, NULL, NULL } - }; - DBusError error; DBusMessage *reply = NULL; int r; @@ -763,6 +830,40 @@ static DBusHandlerResult manager_message_handler( if (!b) goto oom; + } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "GetSessionByPID")) { + uint32_t pid; + char *p; + Session *session; + bool b; + + if (!dbus_message_get_args( + message, + &error, + DBUS_TYPE_UINT32, &pid, + DBUS_TYPE_INVALID)) + return bus_send_error_reply(connection, message, &error, -EINVAL); + + r = manager_get_session_by_pid(m, pid, &session); + if (r <= 0) + return bus_send_error_reply(connection, message, NULL, r < 0 ? r : -ENOENT); + + reply = dbus_message_new_method_return(message); + if (!reply) + goto oom; + + p = session_bus_path(session); + if (!p) + goto oom; + + b = dbus_message_append_args( + reply, + DBUS_TYPE_OBJECT_PATH, &p, + DBUS_TYPE_INVALID); + free(p); + + if (!b) + goto oom; + } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "GetUser")) { uint32_t uid; char *p; @@ -965,8 +1066,11 @@ static DBusHandlerResult manager_message_handler( } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "CreateSession")) { r = bus_manager_create_session(m, message, &reply); - if (r == -ENOMEM) - goto oom; + + /* Don't delay the work on OOM here, since it might be + * triggered by a low RLIMIT_NOFILE here (since we + * send a dupped fd to the client), and we'd rather + * see this fail quickly then be retried later */ if (r < 0) return bus_send_error_reply(connection, message, &error, r); @@ -994,6 +1098,41 @@ static DBusHandlerResult manager_message_handler( if (!reply) goto oom; + } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "ActivateSessionOnSeat")) { + const char *session_name, *seat_name; + Session *session; + Seat *seat; + + /* Same as ActivateSession() but refuses to work if + * the seat doesn't match */ + + if (!dbus_message_get_args( + message, + &error, + DBUS_TYPE_STRING, &session_name, + DBUS_TYPE_STRING, &seat_name, + DBUS_TYPE_INVALID)) + return bus_send_error_reply(connection, message, &error, -EINVAL); + + session = hashmap_get(m->sessions, session_name); + if (!session) + return bus_send_error_reply(connection, message, &error, -ENOENT); + + seat = hashmap_get(m->seats, seat_name); + if (!seat) + return bus_send_error_reply(connection, message, &error, -ENOENT); + + if (session->seat != seat) + return bus_send_error_reply(connection, message, &error, -EINVAL); + + r = session_activate(session); + if (r < 0) + return bus_send_error_reply(connection, message, NULL, r); + + reply = dbus_message_new_method_return(message); + if (!reply) + goto oom; + } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "LockSession") || dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "UnlockSession")) { const char *name; @@ -1173,10 +1312,12 @@ static DBusHandlerResult manager_message_handler( if (!pw) return bus_send_error_reply(connection, message, NULL, errno ? -errno : -EINVAL); - r = verify_polkit(connection, message, "org.freedesktop.login1.set-user-linger", interactive, &error); + r = verify_polkit(connection, message, "org.freedesktop.login1.set-user-linger", interactive, NULL, &error); if (r < 0) return bus_send_error_reply(connection, message, &error, r); + mkdir_p("/var/lib/systemd", 0755); + r = safe_mkdir("/var/lib/systemd/linger", 0755, 0, 0); if (r < 0) return bus_send_error_reply(connection, message, &error, r); @@ -1231,7 +1372,7 @@ static DBusHandlerResult manager_message_handler( if (!path_startswith(sysfs, "/sys") || !seat_name_is_valid(seat)) return bus_send_error_reply(connection, message, NULL, -EINVAL); - r = verify_polkit(connection, message, "org.freedesktop.login1.attach-device", interactive, &error); + r = verify_polkit(connection, message, "org.freedesktop.login1.attach-device", interactive, NULL, &error); if (r < 0) return bus_send_error_reply(connection, message, &error, r); @@ -1254,7 +1395,7 @@ static DBusHandlerResult manager_message_handler( DBUS_TYPE_INVALID)) return bus_send_error_reply(connection, message, &error, -EINVAL); - r = verify_polkit(connection, message, "org.freedesktop.login1.flush-devices", interactive, &error); + r = verify_polkit(connection, message, "org.freedesktop.login1.flush-devices", interactive, NULL, &error); if (r < 0) return bus_send_error_reply(connection, message, &error, r); @@ -1282,27 +1423,11 @@ static DBusHandlerResult manager_message_handler( DBUS_TYPE_INVALID)) return bus_send_error_reply(connection, message, &error, -EINVAL); - multiple_sessions = hashmap_size(m->sessions) > 1; - - if (!multiple_sessions) { - Session *s; - - /* Hmm, there's only one session, but let's - * make sure it actually belongs to the user - * who is asking. If not, better be safe than - * sorry. */ - - s = hashmap_first(m->sessions); - if (s) { - unsigned long ul; - - ul = dbus_bus_get_unix_user(connection, dbus_message_get_sender(message), &error); - if (ul == (unsigned long) -1) - return bus_send_error_reply(connection, message, &error, -EIO); + r = have_multiple_sessions(connection, m, message, &error); + if (r < 0) + return bus_send_error_reply(connection, message, &error, r); - multiple_sessions = s->user->uid != ul; - } - } + multiple_sessions = r > 0; if (streq(dbus_message_get_member(message), "PowerOff")) { if (multiple_sessions) @@ -1320,7 +1445,7 @@ static DBusHandlerResult manager_message_handler( name = SPECIAL_REBOOT_TARGET; } - r = verify_polkit(connection, message, action, interactive, &error); + r = verify_polkit(connection, message, action, interactive, NULL, &error); if (r < 0) return bus_send_error_reply(connection, message, &error, r); @@ -1352,6 +1477,50 @@ static DBusHandlerResult manager_message_handler( if (!reply) goto oom; + } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "CanPowerOff") || + dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "CanReboot")) { + + bool multiple_sessions, challenge, b; + const char *t, *action; + + r = have_multiple_sessions(connection, m, message, &error); + if (r < 0) + return bus_send_error_reply(connection, message, &error, r); + + multiple_sessions = r > 0; + + if (streq(dbus_message_get_member(message), "CanPowerOff")) { + if (multiple_sessions) + action = "org.freedesktop.login1.power-off-multiple-sessions"; + else + action = "org.freedesktop.login1.power-off"; + + } else { + if (multiple_sessions) + action = "org.freedesktop.login1.reboot-multiple-sessions"; + else + action = "org.freedesktop.login1.reboot"; + } + + r = verify_polkit(connection, message, action, false, &challenge, &error); + if (r < 0) + return bus_send_error_reply(connection, message, &error, r); + + reply = dbus_message_new_method_return(message); + if (!reply) + goto oom; + + t = r > 0 ? "yes" : + challenge ? "challenge" : + "no"; + + b = dbus_message_append_args( + reply, + DBUS_TYPE_STRING, &t, + DBUS_TYPE_INVALID); + if (!b) + goto oom; + } else if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect")) { char *introspection = NULL; FILE *f; @@ -1415,8 +1584,13 @@ static DBusHandlerResult manager_message_handler( } free(introspection); - } else - return bus_default_message_handler(connection, message, NULL, INTERFACES_LIST, properties); + } else { + const BusBoundProperties bps[] = { + { "org.freedesktop.login1.Manager", bus_login_manager_properties, m }, + { NULL, } + }; + return bus_default_message_handler(connection, message, NULL, INTERFACES_LIST, bps); + } if (reply) { if (!dbus_connection_send(connection, reply, NULL)) diff --git a/src/logind-device.c b/src/login/logind-device.c index bbd370fbff..bbd370fbff 100644 --- a/src/logind-device.c +++ b/src/login/logind-device.c diff --git a/src/logind-device.h b/src/login/logind-device.h index e25a5344ef..e25a5344ef 100644 --- a/src/logind-device.h +++ b/src/login/logind-device.h diff --git a/src/login/logind-gperf.gperf b/src/login/logind-gperf.gperf new file mode 100644 index 0000000000..940fe10e89 --- /dev/null +++ b/src/login/logind-gperf.gperf @@ -0,0 +1,22 @@ +%{ +#include <stddef.h> +#include "conf-parser.h" +#include "logind.h" +%} +struct ConfigPerfItem; +%null_strings +%language=ANSI-C +%define slot-name section_and_lvalue +%define hash-function-name logind_gperf_hash +%define lookup-function-name logind_gperf_lookup +%readonly-tables +%omit-struct-type +%struct-type +%includes +%% +Login.NAutoVTs, config_parse_unsigned, 0, offsetof(Manager, n_autovts) +Login.KillUserProcesses, config_parse_bool, 0, offsetof(Manager, kill_user_processes) +Login.KillOnlyUsers, config_parse_strv, 0, offsetof(Manager, kill_only_users) +Login.KillExcludeUsers, config_parse_strv, 0, offsetof(Manager, kill_exclude_users) +Login.Controllers, config_parse_strv, 0, offsetof(Manager, controllers) +Login.ResetControllers, config_parse_strv, 0, offsetof(Manager, reset_controllers) diff --git a/src/logind-seat-dbus.c b/src/login/logind-seat-dbus.c index 3a916eef78..95ef5ffdb5 100644 --- a/src/logind-seat-dbus.c +++ b/src/login/logind-seat-dbus.c @@ -141,7 +141,7 @@ static int bus_seat_append_multi_session(DBusMessageIter *i, const char *propert assert(property); assert(s); - b = seat_is_vtconsole(s); + b = seat_can_multi_session(s); if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b)) return -ENOMEM; @@ -207,22 +207,22 @@ static int get_seat_for_path(Manager *m, const char *path, Seat **_s) { return 0; } +static const BusProperty bus_login_seat_properties[] = { + { "Id", bus_property_append_string, "s", offsetof(Seat, id), true }, + { "ActiveSession", bus_seat_append_active, "(so)", 0 }, + { "CanMultiSession", bus_seat_append_multi_session, "b", 0 }, + { "Sessions", bus_seat_append_sessions, "a(so)", 0 }, + { "IdleHint", bus_seat_append_idle_hint, "b", 0 }, + { "IdleSinceHint", bus_seat_append_idle_hint_since, "t", 0 }, + { "IdleSinceHintMonotonic", bus_seat_append_idle_hint_since, "t", 0 }, + { NULL, } +}; + static DBusHandlerResult seat_message_dispatch( Seat *s, DBusConnection *connection, DBusMessage *message) { - const BusProperty properties[] = { - { "org.freedesktop.login1.Seat", "Id", bus_property_append_string, "s", s->id }, - { "org.freedesktop.login1.Seat", "ActiveSession", bus_seat_append_active, "(so)", s }, - { "org.freedesktop.login1.Seat", "CanMultiSession", bus_seat_append_multi_session, "b", s }, - { "org.freedesktop.login1.Seat", "Sessions", bus_seat_append_sessions, "a(so)", s }, - { "org.freedesktop.login1.Seat", "IdleHint", bus_seat_append_idle_hint, "b", s }, - { "org.freedesktop.login1.Seat", "IdleSinceHint", bus_seat_append_idle_hint_since, "t", s }, - { "org.freedesktop.login1.Seat", "IdleSinceHintMonotonic", bus_seat_append_idle_hint_since, "t", s }, - { NULL, NULL, NULL, NULL, NULL } - }; - DBusError error; DBusMessage *reply = NULL; int r; @@ -265,8 +265,13 @@ static DBusHandlerResult seat_message_dispatch( reply = dbus_message_new_method_return(message); if (!reply) goto oom; - } else - return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, properties); + } else { + const BusBoundProperties bps[] = { + { "org.freedesktop.login1.Seat", bus_login_seat_properties, s }, + { NULL, } + }; + return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, bps); + } if (reply) { if (!dbus_connection_send(connection, reply, NULL)) diff --git a/src/logind-seat.c b/src/login/logind-seat.c index 3cf3958c8d..be37c1cc2e 100644 --- a/src/logind-seat.c +++ b/src/login/logind-seat.c @@ -101,8 +101,10 @@ int seat_save(Seat *s) { fprintf(f, "# This is private data. Do not parse.\n" - "IS_VTCONSOLE=%i\n", - seat_is_vtconsole(s)); + "IS_VTCONSOLE=%i\n" + "CAN_MULTI_SESSION=%i\n", + seat_is_vtconsole(s), + seat_can_multi_session(s)); if (s->active) { assert(s->active->user); @@ -191,7 +193,7 @@ int seat_preallocate_vts(Seat *s) { if (s->manager->n_autovts <= 0) return 0; - if (!seat_is_vtconsole(s)) + if (!seat_can_multi_session(s)) return 0; for (i = 1; i <= s->manager->n_autovts; i++) { @@ -266,7 +268,7 @@ int seat_active_vt_changed(Seat *s, int vtnr) { assert(s); assert(vtnr >= 1); - if (!seat_is_vtconsole(s)) + if (!seat_can_multi_session(s)) return -EINVAL; log_debug("VT changed to %i", vtnr); @@ -290,7 +292,7 @@ int seat_read_active_vt(Seat *s) { assert(s); - if (!seat_is_vtconsole(s)) + if (!seat_can_multi_session(s)) return 0; lseek(s->manager->console_active_fd, SEEK_SET, 0); @@ -388,18 +390,19 @@ int seat_attach_session(Seat *s, Session *session) { assert(session); assert(!session->seat); - if (!seat_is_vtconsole(s) && s->sessions) - return -EEXIST; - session->seat = s; LIST_PREPEND(Session, sessions_by_seat, s->sessions, session); seat_send_changed(s, "Sessions\0"); - if (!seat_is_vtconsole(s)) { - assert(!s->active); + /* Note that even if a seat is not multi-session capable it + * still might have multiple sessions on it since old, dead + * sessions might continue to be tracked until all their + * processes are gone. The most recently added session + * (i.e. the first in s->sessions) is the one that matters. */ + + if (!seat_can_multi_session(s)) seat_set_active(s, session); - } return 0; } @@ -410,6 +413,18 @@ bool seat_is_vtconsole(Seat *s) { return s->manager->vtconsole == s; } +bool seat_can_multi_session(Seat *s) { + assert(s); + + if (!seat_is_vtconsole(s)) + return false; + + /* If we can't watch which VT is in the foreground, we don't + * support VT switching */ + + return s->manager->console_active_fd >= 0; +} + int seat_get_idle_hint(Seat *s, dual_timestamp *t) { Session *session; bool idle_hint = true; diff --git a/src/logind-seat.h b/src/login/logind-seat.h index 5bce1434e9..3b2c7f0965 100644 --- a/src/logind-seat.h +++ b/src/login/logind-seat.h @@ -62,6 +62,7 @@ int seat_preallocate_vts(Seat *s); int seat_attach_session(Seat *s, Session *session); bool seat_is_vtconsole(Seat *s); +bool seat_can_multi_session(Seat *s); int seat_get_idle_hint(Seat *s, dual_timestamp *t); int seat_start(Seat *s); diff --git a/src/logind-session-dbus.c b/src/login/logind-session-dbus.c index dc0ef5b3d6..102f8ac99b 100644 --- a/src/logind-session-dbus.c +++ b/src/login/logind-session-dbus.c @@ -57,6 +57,7 @@ " <property name=\"Leader\" type=\"u\" access=\"read\"/>\n" \ " <property name=\"Audit\" type=\"u\" access=\"read\"/>\n" \ " <property name=\"Type\" type=\"s\" access=\"read\"/>\n" \ + " <property name=\"Class\" type=\"s\" access=\"read\"/>\n" \ " <property name=\"Active\" type=\"b\" access=\"read\"/>\n" \ " <property name=\"Controllers\" type=\"as\" access=\"read\"/>\n" \ " <property name=\"ResetControllers\" type=\"as\" access=\"read\"/>\n" \ @@ -119,21 +120,21 @@ static int bus_session_append_seat(DBusMessageIter *i, const char *property, voi static int bus_session_append_user(DBusMessageIter *i, const char *property, void *data) { DBusMessageIter sub; - Session *s = data; + User *u = data; char *p = NULL; assert(i); assert(property); - assert(s); + assert(u); if (!dbus_message_iter_open_container(i, DBUS_TYPE_STRUCT, NULL, &sub)) return -ENOMEM; - p = user_bus_path(s->user); + p = user_bus_path(u); if (!p) return -ENOMEM; - if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT32, &s->user->uid) || + if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT32, &u->uid) || !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &p)) { free(p); return -ENOMEM; @@ -196,6 +197,7 @@ static int bus_session_append_idle_hint_since(DBusMessageIter *i, const char *pr } static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_session_append_type, session_type, SessionType); +static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_session_append_class, session_class, SessionClass); static int get_session_for_path(Manager *m, const char *path, Session **_s) { Session *s; @@ -222,39 +224,44 @@ static int get_session_for_path(Manager *m, const char *path, Session **_s) { return 0; } +static const BusProperty bus_login_session_properties[] = { + { "Id", bus_property_append_string, "s", offsetof(Session, id), true }, + { "Timestamp", bus_property_append_usec, "t", offsetof(Session, timestamp.realtime) }, + { "TimestampMonotonic", bus_property_append_usec, "t", offsetof(Session, timestamp.monotonic) }, + { "ControlGroupPath", bus_property_append_string, "s", offsetof(Session, cgroup_path), true }, + { "VTNr", bus_property_append_uint32, "u", offsetof(Session, vtnr) }, + { "Seat", bus_session_append_seat, "(so)", 0 }, + { "TTY", bus_property_append_string, "s", offsetof(Session, tty), true }, + { "Display", bus_property_append_string, "s", offsetof(Session, display), true }, + { "Remote", bus_property_append_bool, "b", offsetof(Session, remote) }, + { "RemoteUser", bus_property_append_string, "s", offsetof(Session, remote_user), true }, + { "RemoteHost", bus_property_append_string, "s", offsetof(Session, remote_host), true }, + { "Service", bus_property_append_string, "s", offsetof(Session, service), true }, + { "Leader", bus_property_append_pid, "u", offsetof(Session, leader) }, + { "Audit", bus_property_append_uint32, "u", offsetof(Session, audit_id) }, + { "Type", bus_session_append_type, "s", offsetof(Session, type) }, + { "Class", bus_session_append_class, "s", offsetof(Session, class) }, + { "Active", bus_session_append_active, "b", 0 }, + { "Controllers", bus_property_append_strv, "as", offsetof(Session, controllers), true }, + { "ResetControllers", bus_property_append_strv, "as", offsetof(Session, reset_controllers), true }, + { "KillProcesses", bus_property_append_bool, "b", offsetof(Session, kill_processes) }, + { "IdleHint", bus_session_append_idle_hint, "b", 0 }, + { "IdleSinceHint", bus_session_append_idle_hint_since, "t", 0 }, + { "IdleSinceHintMonotonic", bus_session_append_idle_hint_since, "t", 0 }, + { NULL, } +}; + +static const BusProperty bus_login_session_user_properties[] = { + { "User", bus_session_append_user, "(uo)", 0 }, + { "Name", bus_property_append_string, "s", offsetof(User, name), true }, + { NULL, } +}; + static DBusHandlerResult session_message_dispatch( Session *s, DBusConnection *connection, DBusMessage *message) { - const BusProperty properties[] = { - { "org.freedesktop.login1.Session", "Id", bus_property_append_string, "s", s->id }, - { "org.freedesktop.login1.Session", "User", bus_session_append_user, "(uo)", s }, - { "org.freedesktop.login1.Session", "Name", bus_property_append_string, "s", s->user->name }, - { "org.freedesktop.login1.Session", "Timestamp", bus_property_append_usec, "t", &s->timestamp.realtime }, - { "org.freedesktop.login1.Session", "TimestampMonotonic", bus_property_append_usec, "t", &s->timestamp.monotonic }, - { "org.freedesktop.login1.Session", "ControlGroupPath", bus_property_append_string, "s", s->cgroup_path }, - { "org.freedesktop.login1.Session", "VTNr", bus_property_append_uint32, "u", &s->vtnr }, - { "org.freedesktop.login1.Session", "Seat", bus_session_append_seat, "(so)", s }, - { "org.freedesktop.login1.Session", "TTY", bus_property_append_string, "s", s->tty }, - { "org.freedesktop.login1.Session", "Display", bus_property_append_string, "s", s->display }, - { "org.freedesktop.login1.Session", "Remote", bus_property_append_bool, "b", &s->remote }, - { "org.freedesktop.login1.Session", "RemoteUser", bus_property_append_string, "s", s->remote_user }, - { "org.freedesktop.login1.Session", "RemoteHost", bus_property_append_string, "s", s->remote_host }, - { "org.freedesktop.login1.Session", "Service", bus_property_append_string, "s", s->service }, - { "org.freedesktop.login1.Session", "Leader", bus_property_append_pid, "u", &s->leader }, - { "org.freedesktop.login1.Session", "Audit", bus_property_append_uint32, "u", &s->audit_id }, - { "org.freedesktop.login1.Session", "Type", bus_session_append_type, "s", &s->type }, - { "org.freedesktop.login1.Session", "Active", bus_session_append_active, "b", s }, - { "org.freedesktop.login1.Session", "Controllers", bus_property_append_strv, "as", s->controllers }, - { "org.freedesktop.login1.Session", "ResetControllers", bus_property_append_strv, "as", s->reset_controllers }, - { "org.freedesktop.login1.Session", "KillProcesses", bus_property_append_bool, "b", &s->kill_processes }, - { "org.freedesktop.login1.Session", "IdleHint", bus_session_append_idle_hint, "b", s }, - { "org.freedesktop.login1.Session", "IdleSinceHint", bus_session_append_idle_hint_since, "t", s }, - { "org.freedesktop.login1.Session", "IdleSinceHintMonotonic", bus_session_append_idle_hint_since, "t", s }, - { NULL, NULL, NULL, NULL, NULL } - }; - DBusError error; DBusMessage *reply = NULL; int r; @@ -288,7 +295,7 @@ static DBusHandlerResult session_message_dispatch( } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Session", "Lock") || dbus_message_is_method_call(message, "org.freedesktop.login1.Session", "Unlock")) { - if (session_send_signal(s, streq(dbus_message_get_member(message), "Lock")) < 0) + if (session_send_lock(s, streq(dbus_message_get_member(message), "Lock")) < 0) goto oom; reply = dbus_message_new_method_return(message); @@ -351,8 +358,14 @@ static DBusHandlerResult session_message_dispatch( if (!reply) goto oom; - } else - return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, properties); + } else { + const BusBoundProperties bps[] = { + { "org.freedesktop.login1.Session", bus_login_session_properties, s }, + { "org.freedesktop.login1.Session", bus_login_session_user_properties, s->user }, + { NULL, } + }; + return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, bps); + } if (reply) { if (!dbus_connection_send(connection, reply, NULL)) diff --git a/src/logind-session.c b/src/login/logind-session.c index 011fc8f5b5..af9c12dcd5 100644 --- a/src/logind-session.c +++ b/src/login/logind-session.c @@ -145,6 +145,11 @@ int session_save(Session *s) { "TYPE=%s\n", session_type_to_string(s->type)); + if (s->class >= 0) + fprintf(f, + "CLASS=%s\n", + session_class_to_string(s->class)); + if (s->cgroup_path) fprintf(f, "CGROUP=%s\n", @@ -185,7 +190,7 @@ int session_save(Session *s) { "SERVICE=%s\n", s->service); - if (s->seat && seat_is_vtconsole(s->seat)) + if (s->seat && seat_can_multi_session(s->seat)) fprintf(f, "VTNR=%i\n", s->vtnr); @@ -225,7 +230,8 @@ int session_load(Session *s) { *vtnr = NULL, *leader = NULL, *audit_id = NULL, - *type = NULL; + *type = NULL, + *class = NULL; int k, r; @@ -245,6 +251,7 @@ int session_load(Session *s) { "VTNR", &vtnr, "LEADER", &leader, "TYPE", &type, + "CLASS", &class, NULL); if (r < 0) @@ -270,7 +277,7 @@ int session_load(Session *s) { seat_attach_session(o, s); } - if (vtnr && s->seat && seat_is_vtconsole(s->seat)) { + if (vtnr && s->seat && seat_can_multi_session(s->seat)) { int v; k = safe_atoi(vtnr, &v); @@ -297,6 +304,14 @@ int session_load(Session *s) { s->type = t; } + if (class) { + SessionClass c; + + c = session_class_from_string(class); + if (c >= 0) + s->class = c; + } + if (s->fifo_path) { int fd; @@ -324,7 +339,6 @@ finish: int session_activate(Session *s) { int r; - Session *old_active; assert(s); @@ -343,10 +357,7 @@ int session_activate(Session *s) { if (r < 0) return r; - old_active = s->seat->active; - s->seat->active = s; - - return seat_apply_acls(s->seat, old_active); + return seat_set_active(s->seat, s); } static int session_link_x11_socket(Session *s) { @@ -380,15 +391,13 @@ static int session_link_x11_socket(Session *s) { return -ENOENT; } - t = strappend(s->user->runtime_path, "/X11/display"); + t = strappend(s->user->runtime_path, "/X11-display"); if (!t) { log_error("Out of memory"); free(f); return -ENOMEM; } - mkdir_parents(t, 0755); - if (link(f, t) < 0) { if (errno == EEXIST) { unlink(t); @@ -440,7 +449,7 @@ static int session_create_one_group(Session *s, const char *controller, const ch if (r < 0) return r; - r = cg_set_task_access(controller, path, 0644, s->user->uid, s->user->gid); + r = cg_set_task_access(controller, path, 0644, s->user->uid, s->user->gid, -1); if (r >= 0) r = cg_set_group_access(controller, path, 0755, s->user->uid, s->user->gid); @@ -536,7 +545,8 @@ int session_start(Session *s) { if (r < 0) return r; - log_info("New session %s of user %s.", s->id, s->user->name); + log_full(s->type == SESSION_TTY || s->type == SESSION_X11 ? LOG_INFO : LOG_DEBUG, + "New session %s of user %s.", s->id, s->user->name); /* Create cgroup */ r = session_create_cgroup(s); @@ -606,6 +616,23 @@ static int session_terminate_cgroup(Session *s) { log_error("Failed to kill session cgroup: %s", strerror(-r)); } else { + if (s->leader > 0) { + Session *t; + + /* We still send a HUP to the leader process, + * even if we are not supposed to kill the + * whole cgroup. But let's first check the + * leader still exists and belongs to our + * session... */ + + r = manager_get_session_by_pid(s->manager, s->leader, &t); + if (r > 0 && t == s) { + kill(s->leader, SIGTERM); /* for normal processes */ + kill(s->leader, SIGHUP); /* for shells */ + kill(s->leader, SIGCONT); /* in case they are stopped */ + } + } + r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path, true); if (r < 0) log_error("Failed to check session cgroup: %s", strerror(-r)); @@ -613,8 +640,7 @@ static int session_terminate_cgroup(Session *s) { r = cg_delete(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path); if (r < 0) log_error("Failed to delete session cgroup: %s", strerror(-r)); - } else - r = -EBUSY; + } } STRV_FOREACH(k, s->user->manager->controllers) @@ -625,7 +651,7 @@ static int session_terminate_cgroup(Session *s) { free(s->cgroup_path); s->cgroup_path = NULL; - return r; + return 0; } static int session_unlink_x11_socket(Session *s) { @@ -640,7 +666,7 @@ static int session_unlink_x11_socket(Session *s) { s->user->display = NULL; - t = strappend(s->user->runtime_path, "/X11/display"); + t = strappend(s->user->runtime_path, "/X11-display"); if (!t) { log_error("Out of memory"); return -ENOMEM; @@ -658,7 +684,8 @@ int session_stop(Session *s) { assert(s); if (s->started) - log_info("Removed session %s.", s->id); + log_full(s->type == SESSION_TTY || s->type == SESSION_X11 ? LOG_INFO : LOG_DEBUG, + "Removed session %s.", s->id); /* Kill cgroup */ k = session_terminate_cgroup(s); @@ -935,6 +962,14 @@ static const char* const session_type_table[_SESSION_TYPE_MAX] = { DEFINE_STRING_TABLE_LOOKUP(session_type, SessionType); +static const char* const session_class_table[_SESSION_CLASS_MAX] = { + [SESSION_USER] = "user", + [SESSION_GREETER] = "greeter", + [SESSION_LOCK_SCREEN] = "lock-screen" +}; + +DEFINE_STRING_TABLE_LOOKUP(session_class, SessionClass); + static const char* const kill_who_table[_KILL_WHO_MAX] = { [KILL_LEADER] = "leader", [KILL_ALL] = "all" diff --git a/src/logind-session.h b/src/login/logind-session.h index 8e394ac0d8..d0b8c87fab 100644 --- a/src/logind-session.h +++ b/src/login/logind-session.h @@ -38,6 +38,14 @@ typedef enum SessionType { _SESSION_TYPE_INVALID = -1 } SessionType; +typedef enum SessionClass { + SESSION_USER, + SESSION_GREETER, + SESSION_LOCK_SCREEN, + _SESSION_CLASS_MAX, + _SESSION_CLASS_INVALID = -1 +} SessionClass; + typedef enum KillWho { KILL_LEADER, KILL_ALL, @@ -50,6 +58,7 @@ struct Session { char *id; SessionType type; + SessionClass class; char *state_file; @@ -118,6 +127,9 @@ int session_send_lock(Session *s, bool lock); const char* session_type_to_string(SessionType t); SessionType session_type_from_string(const char *s); +const char* session_class_to_string(SessionClass t); +SessionClass session_class_from_string(const char *s); + const char *kill_who_to_string(KillWho k); KillWho kill_who_from_string(const char *s); diff --git a/src/logind-user-dbus.c b/src/login/logind-user-dbus.c index 3673a28bd4..a9d680f899 100644 --- a/src/logind-user-dbus.c +++ b/src/login/logind-user-dbus.c @@ -213,29 +213,29 @@ static int get_user_for_path(Manager *m, const char *path, User **_u) { return 0; } +static const BusProperty bus_login_user_properties[] = { + { "UID", bus_property_append_uid, "u", offsetof(User, uid) }, + { "GID", bus_property_append_gid, "u", offsetof(User, gid) }, + { "Name", bus_property_append_string, "s", offsetof(User, name), true }, + { "Timestamp", bus_property_append_usec, "t", offsetof(User, timestamp.realtime) }, + { "TimestampMonotonic", bus_property_append_usec, "t", offsetof(User, timestamp.monotonic) }, + { "RuntimePath", bus_property_append_string, "s", offsetof(User, runtime_path), true }, + { "ControlGroupPath", bus_property_append_string, "s", offsetof(User, cgroup_path), true }, + { "Service", bus_property_append_string, "s", offsetof(User, service), true }, + { "Display", bus_user_append_display, "(so)", 0 }, + { "State", bus_user_append_state, "s", 0 }, + { "Sessions", bus_user_append_sessions, "a(so)", 0 }, + { "IdleHint", bus_user_append_idle_hint, "b", 0 }, + { "IdleSinceHint", bus_user_append_idle_hint_since, "t", 0 }, + { "IdleSinceHintMonotonic", bus_user_append_idle_hint_since, "t", 0 }, + { NULL, } +}; + static DBusHandlerResult user_message_dispatch( User *u, DBusConnection *connection, DBusMessage *message) { - const BusProperty properties[] = { - { "org.freedesktop.login1.User", "UID", bus_property_append_uid, "u", &u->uid }, - { "org.freedesktop.login1.User", "GID", bus_property_append_gid, "u", &u->gid }, - { "org.freedesktop.login1.User", "Name", bus_property_append_string, "s", u->name }, - { "org.freedesktop.login1.User", "Timestamp", bus_property_append_usec, "t", &u->timestamp.realtime }, - { "org.freedesktop.login1.User", "TimestampMonotonic", bus_property_append_usec, "t", &u->timestamp.monotonic }, - { "org.freedesktop.login1.User", "RuntimePath", bus_property_append_string, "s", u->runtime_path }, - { "org.freedesktop.login1.User", "ControlGroupPath", bus_property_append_string, "s", u->cgroup_path }, - { "org.freedesktop.login1.User", "Service", bus_property_append_string, "s", u->service }, - { "org.freedesktop.login1.User", "Display", bus_user_append_display, "(so)", u }, - { "org.freedesktop.login1.User", "State", bus_user_append_state, "s", u }, - { "org.freedesktop.login1.User", "Sessions", bus_user_append_sessions, "a(so)", u }, - { "org.freedesktop.login1.User", "IdleHint", bus_user_append_idle_hint, "b", u }, - { "org.freedesktop.login1.User", "IdleSinceHint", bus_user_append_idle_hint_since, "t", u }, - { "org.freedesktop.login1.User", "IdleSinceHintMonotonic", bus_user_append_idle_hint_since, "t", u }, - { NULL, NULL, NULL, NULL, NULL } - }; - DBusError error; DBusMessage *reply = NULL; int r; @@ -274,8 +274,14 @@ static DBusHandlerResult user_message_dispatch( if (!reply) goto oom; - } else - return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, properties); + } else { + const BusBoundProperties bps[] = { + { "org.freedesktop.login1.User", bus_login_user_properties, u }, + { NULL, } + }; + + return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, bps); + } if (reply) { if (!dbus_connection_send(connection, reply, NULL)) diff --git a/src/logind-user.c b/src/login/logind-user.c index 3c245c0927..717f0e20a2 100644 --- a/src/logind-user.c +++ b/src/login/logind-user.c @@ -142,7 +142,7 @@ int user_save(User *u) { fprintf(f, "%s%c", i->id, - i->sessions_by_seat_next ? ' ' : '\n'); + i->sessions_by_user_next ? ' ' : '\n'); } fputs("SEATS=", f); @@ -151,7 +151,7 @@ int user_save(User *u) { fprintf(f, "%s%c", i->seat->id, - i->sessions_by_seat_next ? ' ' : '\n'); + i->sessions_by_user_next ? ' ' : '\n'); } fputs("ACTIVE_SESSIONS=", f); @@ -160,7 +160,7 @@ int user_save(User *u) { fprintf(f, "%lu%c", (unsigned long) i->user->uid, - i->sessions_by_seat_next ? ' ' : '\n'); + i->sessions_by_user_next ? ' ' : '\n'); fputs("ACTIVE_SEATS=", f); LIST_FOREACH(sessions_by_user, i, u->sessions) { @@ -168,7 +168,7 @@ int user_save(User *u) { fprintf(f, "%s%c", i->seat->id, - i->sessions_by_seat_next ? ' ' : '\n'); + i->sessions_by_user_next ? ' ' : '\n'); } } @@ -299,6 +299,8 @@ static int user_create_cgroup(User *u) { static int user_start_service(User *u) { assert(u); + /* FIXME: Fill me in later ... */ + return 0; } @@ -310,7 +312,7 @@ int user_start(User *u) { if (u->started) return 0; - log_info("New user %s logged in.", u->name); + log_debug("New user %s logged in.", u->name); /* Make XDG_RUNTIME_DIR */ r = user_mkdir_runtime_path(u); @@ -409,7 +411,7 @@ static int user_remove_runtime_path(User *u) { if (!u->runtime_path) return 0; - r = rm_rf(u->runtime_path, false, true); + r = rm_rf(u->runtime_path, false, true, false); if (r < 0) log_error("Failed to remove runtime directory %s: %s", u->runtime_path, strerror(-r)); @@ -425,7 +427,7 @@ int user_stop(User *u) { assert(u); if (u->started) - log_info("User %s logged out.", u->name); + log_debug("User %s logged out.", u->name); LIST_FOREACH(sessions_by_user, s, u->sessions) { k = session_stop(s); diff --git a/src/logind-user.h b/src/login/logind-user.h index db9a5f6a34..db9a5f6a34 100644 --- a/src/logind-user.h +++ b/src/login/logind-user.h diff --git a/src/logind.c b/src/login/logind.c index 8b99065b23..7fd6515ffd 100644 --- a/src/logind.c +++ b/src/login/logind.c @@ -29,6 +29,8 @@ #include <sys/ioctl.h> #include <linux/vt.h> +#include <systemd/sd-daemon.h> + #include "logind.h" #include "dbus-common.h" #include "dbus-loop.h" @@ -274,8 +276,7 @@ int manager_process_seat_device(Manager *m, struct udev_device *d) { if (streq_ptr(udev_device_get_action(d), "remove")) { - /* FIXME: use syspath instead of sysname here, as soon as fb driver is fixed */ - device = hashmap_get(m->devices, udev_device_get_sysname(d)); + device = hashmap_get(m->devices, udev_device_get_syspath(d)); if (!device) return 0; @@ -295,7 +296,7 @@ int manager_process_seat_device(Manager *m, struct udev_device *d) { return 0; } - r = manager_add_device(m, udev_device_get_sysname(d), &device); + r = manager_add_device(m, udev_device_get_syspath(d), &device); if (r < 0) return r; @@ -414,6 +415,7 @@ static int manager_enumerate_users_from_cgroup(Manager *m) { int r = 0; char *name; DIR *d; + int k; r = cg_enumerate_subgroups(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_path, &d); if (r < 0) { @@ -424,9 +426,8 @@ static int manager_enumerate_users_from_cgroup(Manager *m) { return r; } - while ((r = cg_read_subgroup(d, &name)) > 0) { + while ((k = cg_read_subgroup(d, &name)) > 0) { User *user; - int k; k = manager_add_user_by_name(m, name, &user); if (k < 0) { @@ -447,6 +448,9 @@ static int manager_enumerate_users_from_cgroup(Manager *m) { free(name); } + if (r >= 0 && k < 0) + r = k; + closedir(d); return r; @@ -653,7 +657,6 @@ int manager_dispatch_seat_udev(Manager *m) { return r; } - int manager_dispatch_vcsa_udev(Manager *m) { struct udev_device *d; int r = 0; @@ -778,34 +781,74 @@ finish: return r; } -void manager_cgroup_notify_empty(Manager *m, const char *cgroup) { +int manager_get_session_by_cgroup(Manager *m, const char *cgroup, Session **session) { Session *s; char *p; assert(m); assert(cgroup); + assert(session); + + s = hashmap_get(m->cgroups, cgroup); + if (s) { + *session = s; + return 1; + } p = strdup(cgroup); if (!p) { log_error("Out of memory."); - return; + return -ENOMEM; } for (;;) { char *e; - if (isempty(p) || streq(p, "/")) - break; - - s = hashmap_get(m->cgroups, p); - if (s) - session_add_to_gc_queue(s); + e = strrchr(p, '/'); + if (!e || e == p) { + free(p); + *session = NULL; + return 0; + } - assert_se(e = strrchr(p, '/')); *e = 0; + + s = hashmap_get(m->cgroups, p); + if (s) { + free(p); + *session = s; + return 1; + } } +} + +int manager_get_session_by_pid(Manager *m, pid_t pid, Session **session) { + char *p; + int r; + assert(m); + assert(pid >= 1); + assert(session); + + r = cg_get_by_pid(SYSTEMD_CGROUP_CONTROLLER, pid, &p); + if (r < 0) + return r; + + r = manager_get_session_by_cgroup(m, p, session); free(p); + + return r; +} + +void manager_cgroup_notify_empty(Manager *m, const char *cgroup) { + Session *s; + int r; + + r = manager_get_session_by_cgroup(m, cgroup, &s); + if (r <= 0) + return; + + session_add_to_gc_queue(s); } static void manager_pipe_notify_eof(Manager *m, int fd) { @@ -904,6 +947,12 @@ static int manager_connect_console(Manager *m) { m->console_active_fd = open("/sys/class/tty/tty0/active", O_RDONLY|O_NOCTTY|O_CLOEXEC); if (m->console_active_fd < 0) { + + /* On certain architectures (S390 and Xen), /dev/tty0 + does not exist, so don't fail if we can't open it.*/ + if (errno == ENOENT) + return 0; + log_error("Failed to open /sys/class/tty/tty0/active: %m"); return -errno; } @@ -948,7 +997,8 @@ static int manager_connect_udev(Manager *m) { ev.events = EPOLLIN; ev.data.u32 = FD_SEAT_UDEV; - if (m->n_autovts <= 0) + /* Don't bother watching VCSA devices, if nobody cares */ + if (m->n_autovts <= 0 || m->console_active_fd < 0) return 0; if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->udev_seat_fd, &ev) < 0) @@ -1067,13 +1117,13 @@ int manager_startup(Manager *m) { if (m->epoll_fd < 0) return -errno; - /* Connect to udev */ - r = manager_connect_udev(m); + /* Connect to console */ + r = manager_connect_console(m); if (r < 0) return r; - /* Connect to console */ - r = manager_connect_console(m); + /* Connect to udev */ + r = manager_connect_udev(m); if (r < 0) return r; @@ -1160,22 +1210,6 @@ int manager_run(Manager *m) { } static int manager_parse_config_file(Manager *m) { - - const ConfigItem items[] = { - { "NAutoVTs", config_parse_unsigned, 0, &m->n_autovts, "Login" }, - { "KillUserProcesses", config_parse_bool, 0, &m->kill_user_processes, "Login" }, - { "KillOnlyUsers", config_parse_strv, 0, &m->kill_only_users, "Login" }, - { "KillExcludeUsers", config_parse_strv, 0, &m->kill_exclude_users, "Login" }, - { "Controllers", config_parse_strv, 0, &m->controllers, "Login" }, - { "ResetControllers", config_parse_strv, 0, &m->reset_controllers, "Login" }, - { NULL, NULL, 0, NULL, NULL } - }; - - static const char * const sections[] = { - "Login", - NULL - }; - FILE *f; const char *fn; int r; @@ -1192,7 +1226,7 @@ static int manager_parse_config_file(Manager *m) { return -errno; } - r = config_parse(fn, f, sections, items, false, NULL); + r = config_parse(fn, f, "Login\0", config_item_perf_lookup, (void*) logind_gperf_lookup, false, m); if (r < 0) log_warning("Failed to parse configuration file: %s", strerror(-r)); @@ -1209,14 +1243,14 @@ int main(int argc, char *argv[]) { log_parse_environment(); log_open(); + umask(0022); + if (argc != 1) { log_error("This program takes no arguments."); r = -EINVAL; goto finish; } - umask(0022); - m = manager_new(); if (!m) { log_error("Out of memory"); @@ -1232,9 +1266,20 @@ int main(int argc, char *argv[]) { goto finish; } + log_debug("systemd-logind running as pid %lu", (unsigned long) getpid()); + + sd_notify(false, + "READY=1\n" + "STATUS=Processing requests..."); + r = manager_run(m); + log_debug("systemd-logind stopped as pid %lu", (unsigned long) getpid()); + finish: + sd_notify(false, + "STATUS=Shutting down..."); + if (m) manager_free(m); diff --git a/src/logind.h b/src/login/logind.h index 5a48756982..a4b4602675 100644 --- a/src/logind.h +++ b/src/login/logind.h @@ -116,10 +116,16 @@ void manager_gc(Manager *m, bool drop_not_started); int manager_get_idle_hint(Manager *m, dual_timestamp *t); +int manager_get_session_by_cgroup(Manager *m, const char *cgroup, Session **session); +int manager_get_session_by_pid(Manager *m, pid_t pid, Session **session); + extern const DBusObjectPathVTable bus_manager_vtable; DBusHandlerResult bus_message_filter(DBusConnection *c, DBusMessage *message, void *userdata); int manager_send_changed(Manager *manager, const char *properties); +/* gperf lookup function */ +const struct ConfigPerfItem* logind_gperf_lookup(const char *key, unsigned length); + #endif diff --git a/src/login/multi-seat-x.c b/src/login/multi-seat-x.c new file mode 100644 index 0000000000..7133e026dc --- /dev/null +++ b/src/login/multi-seat-x.c @@ -0,0 +1,195 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2011 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 <string.h> +#include <unistd.h> + +#include <libudev.h> + +#include "util.h" + +int main(int argc, char *argv[]) { + + struct udev *udev = NULL; + struct udev_enumerate *enumerator = NULL; + struct udev_list_entry *first, *item; + int i; + const char *seat = NULL; + char **new_argv; + char *path = NULL, *device_node = NULL; + int r; + FILE *f = NULL; + + /* This binary will go away as soon as X natively supports + * display enumeration with udev in a way that covers both PCI + * and USB. */ + + /* This will simply determine the fb device id of the graphics + * device assigned to a seat and write a configuration file + * from it and then spawn the real X server. */ + + /* If this file is removed, don't forget to remove the code + * that invokes this in gdm and other display managers. */ + + for (i = 1; i < argc; i++) + if (streq(argv[i], "-seat")) + seat = argv[i+1]; + + if (isempty(seat) || streq(seat, "seat0")) { + argv[0] = (char*) X_SERVER; + execv(X_SERVER, argv); + log_error("Failed to execute real X server: %m"); + goto fail; + } + + udev = udev_new(); + if (!udev) { + log_error("Failed to allocate udev environment."); + goto fail; + } + + enumerator = udev_enumerate_new(udev); + if (!enumerator) { + log_error("Failed to allocate udev enumerator."); + goto fail; + } + + udev_enumerate_add_match_subsystem(enumerator, "graphics"); + udev_enumerate_add_match_tag(enumerator, seat); + + r = udev_enumerate_scan_devices(enumerator); + if (r < 0) { + log_error("Failed to enumerate devices."); + goto fail; + } + + first = udev_enumerate_get_list_entry(enumerator); + udev_list_entry_foreach(item, first) { + struct udev_device *d; + const char *dn; + + d = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item)); + if (!d) + continue; + + dn = udev_device_get_devnode(d); + + if (dn) { + device_node = strdup(dn); + if (!device_node) { + udev_device_unref(d); + log_error("Out of memory."); + goto fail; + } + } + + udev_device_unref(d); + + if (device_node) + break; + } + + if (!device_node) { + log_error("Failed to find device node for seat %s.", seat); + goto fail; + } + + r = safe_mkdir("/run/systemd/multi-session-x", 0755, 0, 0); + if (r < 0) { + log_error("Failed to create directory: %s", strerror(-r)); + goto fail; + } + + path = strappend("/run/systemd/multi-session-x/", seat); + if (!path) { + log_error("Out of memory"); + goto fail; + } + + f = fopen(path, "we"); + if (!f) { + log_error("Failed to write configuration file: %m"); + goto fail; + } + + fprintf(f, + "Section \"Device\"\n" + " Identifier \"udev\"\n" + " Driver \"fbdev\"\n" + " Option \"fbdev\" \"%s\"\n" + "EndSection\n" + "Section \"ServerFlags\"\n" + " Option \"AutoAddDevices\" \"True\"\n" + " Option \"AllowEmptyInput\" \"True\"\n" + " Option \"DontVTSwitch\" \"True\"\n" + "EndSection\n" + "Section \"InputClass\"\n" + " Identifier \"Force Input Devices to Seat\"\n" + " Option \"GrabDevice\" \"True\"\n" + "EndSection\n", + device_node); + + fflush(f); + + if (ferror(f)) { + log_error("Failed to write configuration file: %m"); + goto fail; + } + + fclose(f); + f = NULL; + + new_argv = alloca(sizeof(char*) * (argc + 3 + 1)); + memcpy(new_argv, argv, sizeof(char*) * (argc + 2 + 1)); + + new_argv[0] = (char*) X_SERVER; + new_argv[argc+0] = (char*) "-config"; + new_argv[argc+1] = path; + new_argv[argc+2] = (char*) "-sharevts"; + new_argv[argc+3] = NULL; + + udev_enumerate_unref(enumerator); + enumerator = NULL; + + udev_unref(udev); + udev = NULL; + + free(device_node); + device_node = NULL; + + execv(X_SERVER, new_argv); + log_error("Failed to execute real X server: %m"); + +fail: + if (enumerator) + udev_enumerate_unref(enumerator); + + if (udev) + udev_unref(udev); + + free(path); + free(device_node); + + if (f) + fclose(f); + + return EXIT_FAILURE; +} diff --git a/src/org.freedesktop.login1.conf b/src/login/org.freedesktop.login1.conf index c423ef5930..9ef852bb73 100644 --- a/src/org.freedesktop.login1.conf +++ b/src/login/org.freedesktop.login1.conf @@ -42,6 +42,10 @@ <allow send_destination="org.freedesktop.login1" send_interface="org.freedesktop.login1.Manager" + send_member="GetSessionByPID"/> + + <allow send_destination="org.freedesktop.login1" + send_interface="org.freedesktop.login1.Manager" send_member="GetUser"/> <allow send_destination="org.freedesktop.login1" @@ -69,6 +73,34 @@ send_member="ActivateSession"/> <allow send_destination="org.freedesktop.login1" + send_interface="org.freedesktop.login1.Manager" + send_member="ActivateSessionOnSeat"/> + + <allow send_destination="org.freedesktop.login1" + send_interface="org.freedesktop.login1.Manager" + send_member="PowerOff"/> + + <allow send_destination="org.freedesktop.login1" + send_interface="org.freedesktop.login1.Manager" + send_member="Reboot"/> + + <allow send_destination="org.freedesktop.login1" + send_interface="org.freedesktop.login1.Manager" + send_member="CanPowerOff"/> + + <allow send_destination="org.freedesktop.login1" + send_interface="org.freedesktop.login1.Manager" + send_member="CanReboot"/> + + <allow send_destination="org.freedesktop.login1" + send_interface="org.freedesktop.login1.Manager" + send_member="AttachDevice"/> + + <allow send_destination="org.freedesktop.login1" + send_interface="org.freedesktop.login1.Manager" + send_member="FlushDevices"/> + + <allow send_destination="org.freedesktop.login1" send_interface="org.freedesktop.login1.Seat" send_member="ActivateSession"/> diff --git a/src/org.freedesktop.login1.policy.in b/src/login/org.freedesktop.login1.policy.in index adc904886d..adc904886d 100644 --- a/src/org.freedesktop.login1.policy.in +++ b/src/login/org.freedesktop.login1.policy.in diff --git a/src/org.freedesktop.login1.service b/src/login/org.freedesktop.login1.service index 4a64177e30..4a64177e30 100644 --- a/src/org.freedesktop.login1.service +++ b/src/login/org.freedesktop.login1.service diff --git a/src/pam-module.c b/src/login/pam-module.c index dd05f93d42..8544413a08 100644 --- a/src/pam-module.c +++ b/src/login/pam-module.c @@ -32,9 +32,10 @@ #include <security/pam_ext.h> #include <security/pam_misc.h> +#include <systemd/sd-daemon.h> + #include "util.h" #include "macro.h" -#include "sd-daemon.h" #include "strv.h" #include "dbus-common.h" #include "def.h" @@ -163,42 +164,24 @@ static int get_user_data( const char *username = NULL; struct passwd *pw = NULL; + uid_t uid; int r; - bool have_loginuid = false; - char *s; assert(handle); assert(ret_username); assert(ret_pw); - if (have_effective_cap(CAP_AUDIT_CONTROL) > 0) { - /* Only use audit login uid if we are executed with - * sufficient capabilities so that pam_loginuid could - * do its job. If we are lacking the CAP_AUDIT_CONTROL - * capabality we most likely are being run in a - * container and /proc/self/loginuid is useless since - * it probably contains a uid of the host system. */ - - if (read_one_line_file("/proc/self/loginuid", &s) >= 0) { - uid_t uid; - - r = parse_uid(s, &uid); - free(s); - - if (r >= 0 && uid != (uint32_t) -1) { - have_loginuid = true; - pw = pam_modutil_getpwuid(handle, uid); - } - } - } - - if (!have_loginuid) { - if ((r = pam_get_user(handle, &username, NULL)) != PAM_SUCCESS) { + r = audit_loginuid_from_pid(0, &uid); + if (r >= 0) + pw = pam_modutil_getpwuid(handle, uid); + else { + r = pam_get_user(handle, &username, NULL); + if (r != PAM_SUCCESS) { pam_syslog(handle, LOG_ERR, "Failed to get user name."); return r; } - if (!username || !*username) { + if (isempty(username)) { pam_syslog(handle, LOG_ERR, "User name not valid."); return PAM_AUTH_ERR; } @@ -277,7 +260,6 @@ static int get_seat_from_display(const char *display, const char **seat, uint32_ int v; assert(display); - assert(seat); assert(vtnr); /* We deduce the X11 socket from the display name, then use @@ -325,7 +307,8 @@ static int get_seat_from_display(const char *display, const char **seat, uint32_ else if (v == 0) return -ENOENT; - *seat = "seat0"; + if (seat) + *seat = "seat0"; *vtnr = (uint32_t) v; return 0; @@ -338,7 +321,7 @@ _public_ PAM_EXTERN int pam_sm_open_session( struct passwd *pw; bool kill_processes = false, debug = false; - const char *username, *id, *object_path, *runtime_path, *service = NULL, *tty = NULL, *display = NULL, *remote_user = NULL, *remote_host = NULL, *seat = NULL, *type, *cvtnr = NULL; + const char *username, *id, *object_path, *runtime_path, *service = NULL, *tty = NULL, *display = NULL, *remote_user = NULL, *remote_host = NULL, *seat = NULL, *type, *class, *cvtnr = NULL; char **controllers = NULL, **reset_controllers = NULL, **kill_only_users = NULL, **kill_exclude_users = NULL; DBusError error; uint32_t uid, pid; @@ -463,24 +446,39 @@ _public_ PAM_EXTERN int pam_sm_open_session( if (isempty(display)) display = tty; tty = ""; + } else if (streq(tty, "cron")) { + /* cron has been setting PAM_TTY to "cron" for a very long time + * and it cannot stop doing that for compatibility reasons. */ + tty = ""; } if (!isempty(cvtnr)) safe_atou32(cvtnr, &vtnr); - if (!isempty(display) && isempty(seat) && vtnr <= 0) - get_seat_from_display(display, &seat, &vtnr); + if (!isempty(display) && vtnr <= 0) { + if (isempty(seat)) + get_seat_from_display(display, &seat, &vtnr); + else if (streq(seat, "seat0")) + get_seat_from_display(display, NULL, &vtnr); + } type = !isempty(display) ? "x11" : - !isempty(tty) ? "tty" : "other"; + !isempty(tty) ? "tty" : "unspecified"; + + class = pam_getenv(handle, "XDG_SESSION_CLASS"); + if (isempty(class)) + class = "user"; - remote = !isempty(remote_host) && !streq(remote_host, "localhost") && !streq(remote_host, "localhost.localdomain"); + remote = !isempty(remote_host) && + !streq(remote_host, "localhost") && + !streq(remote_host, "localhost.localdomain"); if (!dbus_message_append_args(m, DBUS_TYPE_UINT32, &uid, DBUS_TYPE_UINT32, &pid, DBUS_TYPE_STRING, &service, DBUS_TYPE_STRING, &type, + DBUS_TYPE_STRING, &class, DBUS_TYPE_STRING, &seat, DBUS_TYPE_UINT32, &vtnr, DBUS_TYPE_STRING, &tty, @@ -517,6 +515,11 @@ _public_ PAM_EXTERN int pam_sm_open_session( goto finish; } + if (debug) + pam_syslog(handle, LOG_DEBUG, "Asking logind to create session: " + "uid=%u pid=%u service=%s type=%s seat=%s vtnr=%u tty=%s display=%s remote=%s remote_user=%s remote_host=%s", + uid, pid, service, type, seat, vtnr, tty, display, yes_no(remote), remote_user, remote_host); + reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error); if (!reply) { pam_syslog(handle, LOG_ERR, "Failed to create session: %s", bus_error_message(&error)); @@ -537,6 +540,11 @@ _public_ PAM_EXTERN int pam_sm_open_session( goto finish; } + if (debug) + pam_syslog(handle, LOG_DEBUG, "Reply from logind: " + "id=%s object_path=%s runtime_path=%s session_fd=%d seat=%s vtnr=%u", + id, object_path, runtime_path, session_fd, seat, vtnr); + r = pam_misc_setenv(handle, "XDG_SESSION_ID", id, 0); if (r != PAM_SUCCESS) { pam_syslog(handle, LOG_ERR, "Failed to set session id."); diff --git a/src/sd-login.c b/src/login/sd-login.c index d44a1fcf9c..887c421009 100644 --- a/src/sd-login.c +++ b/src/login/sd-login.c @@ -121,6 +121,58 @@ _public_ int sd_pid_get_session(pid_t pid, char **session) { return 0; } +_public_ int sd_pid_get_unit(pid_t pid, char **unit) { + int r; + char *cgroup, *p, *at, *b; + size_t k; + + if (!unit) + return -EINVAL; + + r = pid_get_cgroup(pid, NULL, &cgroup); + if (r < 0) + return r; + + if (!startswith(cgroup, "/system/")) { + free(cgroup); + return -ENOENT; + } + + p = cgroup + 8; + k = strcspn(p, "/"); + + at = memchr(p, '@', k); + if (at && at[1] == '.') { + size_t j; + + /* This is a templated service */ + if (p[k] != '/') { + free(cgroup); + return -EIO; + } + + j = strcspn(p+k+1, "/"); + + b = malloc(k + j + 1); + + if (b) { + memcpy(b, p, at - p + 1); + memcpy(b + (at - p) + 1, p + k + 1, j); + memcpy(b + (at - p) + 1 + j, at + 1, k - (at - p) - 1); + b[k+j] = 0; + } + } else + b = strndup(p, k); + + free(cgroup); + + if (!b) + return -ENOMEM; + + *unit = b; + return 0; +} + _public_ int sd_pid_get_owner_uid(pid_t pid, uid_t *uid) { int r; char *root, *cgroup, *p, *cc; @@ -251,9 +303,6 @@ static int uid_get_array(uid_t uid, const char *variable, char ***array) { char **a; int r; - if (!array) - return -EINVAL; - if (asprintf(&p, "/run/systemd/users/%lu", (unsigned long) uid) < 0) return -ENOMEM; @@ -266,7 +315,8 @@ static int uid_get_array(uid_t uid, const char *variable, char ***array) { free(s); if (r == -ENOENT) { - *array = NULL; + if (array) + *array = NULL; return 0; } @@ -274,7 +324,8 @@ static int uid_get_array(uid_t uid, const char *variable, char ***array) { } if (!s) { - *array = NULL; + if (array) + *array = NULL; return 0; } @@ -284,8 +335,15 @@ static int uid_get_array(uid_t uid, const char *variable, char ***array) { if (!a) return -ENOMEM; - *array = a; - return 0; + strv_uniq(a); + r = strv_length(a); + + if (array) + *array = a; + else + strv_free(a); + + return r; } _public_ int sd_uid_get_sessions(uid_t uid, int require_active, char ***sessions) { @@ -296,17 +354,40 @@ _public_ int sd_uid_get_seats(uid_t uid, int require_active, char ***seats) { return uid_get_array(uid, require_active ? "ACTIVE_SEATS" : "SEATS", seats); } -_public_ int sd_session_is_active(const char *session) { +static int file_of_session(const char *session, char **_p) { + char *p; int r; - char *p, *s = NULL; - if (!session) - return -EINVAL; + assert(_p); + + if (session) + p = strappend("/run/systemd/sessions/", session); + else { + char *buf; + + r = sd_pid_get_session(0, &buf); + if (r < 0) + return r; + + p = strappend("/run/systemd/sessions/", buf); + free(buf); + } - p = strappend("/run/systemd/sessions/", session); if (!p) return -ENOMEM; + *_p = p; + return 0; +} + +_public_ int sd_session_is_active(const char *session) { + int r; + char *p, *s = NULL; + + r = file_of_session(session, &p); + if (r < 0) + return r; + r = parse_env_file(p, NEWLINE, "ACTIVE", &s, NULL); free(p); @@ -328,14 +409,12 @@ _public_ int sd_session_get_uid(const char *session, uid_t *uid) { int r; char *p, *s = NULL; - if (!session) - return -EINVAL; if (!uid) return -EINVAL; - p = strappend("/run/systemd/sessions/", session); - if (!p) - return -ENOMEM; + r = file_of_session(session, &p); + if (r < 0) + return r; r = parse_env_file(p, NEWLINE, "UID", &s, NULL); free(p); @@ -354,20 +433,18 @@ _public_ int sd_session_get_uid(const char *session, uid_t *uid) { return r; } -_public_ int sd_session_get_seat(const char *session, char **seat) { +static int session_get_string(const char *session, const char *field, char **value) { char *p, *s = NULL; int r; - if (!session) - return -EINVAL; - if (!seat) + if (!value) return -EINVAL; - p = strappend("/run/systemd/sessions/", session); - if (!p) - return -ENOMEM; + r = file_of_session(session, &p); + if (r < 0) + return r; - r = parse_env_file(p, NEWLINE, "SEAT", &s, NULL); + r = parse_env_file(p, NEWLINE, field, &s, NULL); free(p); if (r < 0) { @@ -378,7 +455,53 @@ _public_ int sd_session_get_seat(const char *session, char **seat) { if (isempty(s)) return -ENOENT; - *seat = s; + *value = s; + return 0; +} + +_public_ int sd_session_get_seat(const char *session, char **seat) { + return session_get_string(session, "SEAT", seat); +} + +_public_ int sd_session_get_service(const char *session, char **service) { + return session_get_string(session, "SERVICE", service); +} + +_public_ int sd_session_get_type(const char *session, char **type) { + return session_get_string(session, "TYPE", type); +} + +_public_ int sd_session_get_class(const char *session, char **class) { + return session_get_string(session, "CLASS", class); +} + +_public_ int sd_session_get_display(const char *session, char **display) { + return session_get_string(session, "DISPLAY", display); +} + +static int file_of_seat(const char *seat, char **_p) { + char *p; + int r; + + assert(_p); + + if (seat) + p = strappend("/run/systemd/seats/", seat); + else { + char *buf; + + r = sd_session_get_seat(NULL, &buf); + if (r < 0) + return r; + + p = strappend("/run/systemd/seats/", buf); + free(buf); + } + + if (!p) + return -ENOMEM; + + *_p = p; return 0; } @@ -386,14 +509,12 @@ _public_ int sd_seat_get_active(const char *seat, char **session, uid_t *uid) { char *p, *s = NULL, *t = NULL; int r; - if (!seat) - return -EINVAL; if (!session && !uid) return -EINVAL; - p = strappend("/run/systemd/seats/", seat); - if (!p) - return -ENOMEM; + r = file_of_seat(seat, &p); + if (r < 0) + return r; r = parse_env_file(p, NEWLINE, "ACTIVE", &s, @@ -442,15 +563,9 @@ _public_ int sd_seat_get_sessions(const char *seat, char ***sessions, uid_t **ui unsigned n = 0; int r; - if (!seat) - return -EINVAL; - - if (!sessions && !uids) - return -EINVAL; - - p = strappend("/run/systemd/seats/", seat); - if (!p) - return -ENOMEM; + r = file_of_seat(seat, &p); + if (r < 0) + return r; r = parse_env_file(p, NEWLINE, "SESSIONS", &s, @@ -464,7 +579,7 @@ _public_ int sd_seat_get_sessions(const char *seat, char ***sessions, uid_t **ui return r; } - if (sessions && s) { + if (s) { a = strv_split(s, " "); if (!a) { free(s); @@ -478,40 +593,50 @@ _public_ int sd_seat_get_sessions(const char *seat, char ***sessions, uid_t **ui if (uids && t) { char *w, *state; size_t l; - unsigned i = 0; FOREACH_WORD(w, l, t, state) n++; - b = new(uid_t, n); - if (!b) { - strv_free(a); - return -ENOMEM; - } + if (n == 0) + b = NULL; + else { + unsigned i = 0; - FOREACH_WORD(w, l, t, state) { - char *k; - - k = strndup(w, l); - if (!k) { - free(t); - free(b); + b = new(uid_t, n); + if (!b) { + strv_free(a); return -ENOMEM; } - r = parse_uid(k, b + i); - free(k); - if (r < 0) - continue; + FOREACH_WORD(w, l, t, state) { + char *k; + + k = strndup(w, l); + if (!k) { + free(t); + free(b); + strv_free(a); + return -ENOMEM; + } - i++; + r = parse_uid(k, b + i); + free(k); + if (r < 0) + continue; + + i++; + } } } free(t); + r = strv_length(a); + if (sessions) *sessions = a; + else + strv_free(a); if (uids) *uids = b; @@ -519,22 +644,19 @@ _public_ int sd_seat_get_sessions(const char *seat, char ***sessions, uid_t **ui if (n_uids) *n_uids = n; - return 0; + return r; } _public_ int sd_seat_can_multi_session(const char *seat) { char *p, *s = NULL; int r; - if (!seat) - return -EINVAL; - - p = strappend("/run/systemd/seats/", seat); - if (!p) - return -ENOMEM; + r = file_of_seat(seat, &p); + if (r < 0) + return r; r = parse_env_file(p, NEWLINE, - "IS_VTCONSOLE", &s, + "CAN_MULTI_SESSION", &s, NULL); free(p); @@ -553,18 +675,10 @@ _public_ int sd_seat_can_multi_session(const char *seat) { } _public_ int sd_get_seats(char ***seats) { - - if (!seats) - return -EINVAL; - return get_files_in_directory("/run/systemd/seats/", seats); } _public_ int sd_get_sessions(char ***sessions) { - - if (!sessions) - return -EINVAL; - return get_files_in_directory("/run/systemd/sessions/", sessions); } @@ -574,10 +688,10 @@ _public_ int sd_get_uids(uid_t **users) { unsigned n = 0; uid_t *l = NULL; - if (!users) - return -EINVAL; - d = opendir("/run/systemd/users/"); + if (!d) + return -errno; + for (;;) { struct dirent buffer, *de; int k; @@ -601,30 +715,34 @@ _public_ int sd_get_uids(uid_t **users) { if (k < 0) continue; - if ((unsigned) r >= n) { - uid_t *t; + if (users) { + if ((unsigned) r >= n) { + uid_t *t; - n = MAX(16, 2*r); - t = realloc(l, sizeof(uid_t) * n); - if (!t) { - r = -ENOMEM; - goto finish; - } + n = MAX(16, 2*r); + t = realloc(l, sizeof(uid_t) * n); + if (!t) { + r = -ENOMEM; + goto finish; + } - l = t; - } + l = t; + } - assert((unsigned) r < n); - l[r++] = uid; + assert((unsigned) r < n); + l[r++] = uid; + } else + r++; } finish: if (d) closedir(d); - if (r >= 0) - *users = l; - else + if (r >= 0) { + if (users) + *users = l; + } else free(l); return r; diff --git a/src/sysfs-show.c b/src/login/sysfs-show.c index ab866a4707..b8b356d77b 100644 --- a/src/sysfs-show.c +++ b/src/login/sysfs-show.c @@ -165,6 +165,9 @@ int show_sysfs(const char *seat, const char *prefix, unsigned n_columns) { else r = udev_enumerate_add_match_tag(e, "seat"); + if (r < 0) + goto finish; + r = udev_enumerate_scan_devices(e); if (r < 0) goto finish; diff --git a/src/systemd-logind.conf b/src/login/systemd-logind.conf index 99098040f4..99098040f4 100644 --- a/src/systemd-logind.conf +++ b/src/login/systemd-logind.conf diff --git a/src/test-login.c b/src/login/test-login.c index 9cd9c27a58..dd8404285b 100644 --- a/src/test-login.c +++ b/src/login/test-login.c @@ -22,14 +22,15 @@ #include <sys/poll.h> #include <string.h> -#include "sd-login.h" +#include <systemd/sd-login.h> + #include "util.h" #include "strv.h" int main(int argc, char* argv[]) { int r, k; uid_t u, u2; - char *seat; + char *seat, *type, *class, *display; char *session; char *state; char *session2; @@ -48,18 +49,24 @@ int main(int argc, char* argv[]) { r = sd_uid_get_sessions(u2, false, &sessions); assert_se(r >= 0); + assert_se(r == (int) strv_length(sessions)); assert_se(t = strv_join(sessions, ", ")); strv_free(sessions); printf("sessions = %s\n", t); free(t); + assert_se(r == sd_uid_get_sessions(u2, false, NULL)); + r = sd_uid_get_seats(u2, false, &seats); assert_se(r >= 0); + assert_se(r == (int) strv_length(seats)); assert_se(t = strv_join(seats, ", ")); strv_free(seats); printf("seats = %s\n", t); free(t); + assert_se(r == sd_uid_get_seats(u2, false, NULL)); + r = sd_session_is_active(session); assert_se(r >= 0); printf("active = %s\n", yes_no(r)); @@ -68,6 +75,18 @@ int main(int argc, char* argv[]) { printf("uid = %lu\n", (unsigned long) u); assert_se(u == u2); + assert_se(sd_session_get_type(session, &type) >= 0); + printf("type = %s\n", type); + free(type); + + assert_se(sd_session_get_class(session, &class) >= 0); + printf("class = %s\n", class); + free(class); + + assert_se(sd_session_get_display(session, &display) >= 0); + printf("display = %s\n", display); + free(display); + assert_se(sd_session_get_seat(session, &seat) >= 0); printf("seat = %s\n", seat); @@ -88,7 +107,10 @@ int main(int argc, char* argv[]) { printf("session2 = %s\n", session2); printf("uid2 = %lu\n", (unsigned long) u2); - assert_se(sd_seat_get_sessions(seat, &sessions, &uids, &n) >= 0); + r = sd_seat_get_sessions(seat, &sessions, &uids, &n); + assert_se(r >= 0); + printf("n_sessions = %i\n", r); + assert_se(r == (int) strv_length(sessions)); assert_se(t = strv_join(sessions, ", ")); strv_free(sessions); printf("sessions = %s\n", t); @@ -99,23 +121,40 @@ int main(int argc, char* argv[]) { printf("\n"); free(uids); + assert_se(sd_seat_get_sessions(seat, NULL, NULL, NULL) == r); + free(session); free(state); free(session2); free(seat); - assert_se(sd_get_seats(&seats) >= 0); + r = sd_get_seats(&seats); + assert_se(r >= 0); + assert_se(r == (int) strv_length(seats)); assert_se(t = strv_join(seats, ", ")); strv_free(seats); + printf("n_seats = %i\n", r); printf("seats = %s\n", t); free(t); - assert_se(sd_get_sessions(&sessions) >= 0); + assert_se(sd_get_seats(NULL) == r); + + r = sd_seat_get_active(NULL, &t, NULL); + assert_se(r >= 0); + printf("active session on current seat = %s\n", t); + free(t); + + r = sd_get_sessions(&sessions); + assert_se(r >= 0); + assert_se(r == (int) strv_length(sessions)); assert_se(t = strv_join(sessions, ", ")); strv_free(sessions); + printf("n_sessions = %i\n", r); printf("sessions = %s\n", t); free(t); + assert_se(sd_get_sessions(NULL) == r); + r = sd_get_uids(&uids); assert_se(r >= 0); @@ -123,9 +162,11 @@ int main(int argc, char* argv[]) { for (k = 0; k < r; k++) printf(" %lu", (unsigned long) uids[k]); printf("\n"); - free(uids); + printf("n_uids = %i\n", r); + assert_se(sd_get_uids(NULL) == r); + r = sd_login_monitor_new("session", &m); assert_se(r >= 0); diff --git a/src/uaccess.c b/src/login/uaccess.c index 786f0ef641..e1af5bf431 100644 --- a/src/uaccess.c +++ b/src/login/uaccess.c @@ -22,11 +22,12 @@ #include <errno.h> #include <string.h> +#include <systemd/sd-daemon.h> +#include <systemd/sd-login.h> + #include "logind-acl.h" #include "util.h" #include "log.h" -#include "sd-daemon.h" -#include "sd-login.h" int main(int argc, char *argv[]) { int r; @@ -38,6 +39,8 @@ int main(int argc, char *argv[]) { log_parse_environment(); log_open(); + umask(0022); + if (argc < 2 || argc > 3) { log_error("This program expects one or two arguments."); r = -EINVAL; diff --git a/src/user-sessions.c b/src/login/user-sessions.c index ffb8657436..64aa3bb1c6 100644 --- a/src/user-sessions.c +++ b/src/login/user-sessions.c @@ -35,10 +35,12 @@ int main(int argc, char*argv[]) { return EXIT_FAILURE; } - log_set_target(LOG_TARGET_SYSLOG_OR_KMSG); + log_set_target(LOG_TARGET_AUTO); log_parse_environment(); log_open(); + umask(0022); + if (streq(argv[1], "start")) { int q = 0, r = 0; diff --git a/src/logs-show.c b/src/logs-show.c new file mode 100644 index 0000000000..f023f0aaef --- /dev/null +++ b/src/logs-show.c @@ -0,0 +1,668 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2012 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 <time.h> +#include <assert.h> +#include <errno.h> +#include <sys/poll.h> +#include <string.h> + +#include "logs-show.h" +#include "log.h" +#include "util.h" + +#define PRINT_THRESHOLD 128 + +static bool contains_unprintable(const void *p, size_t l) { + const char *j; + + for (j = p; j < (const char *) p + l; j++) + if (*j < ' ' || *j >= 127) + return true; + + return false; +} + +static int parse_field(const void *data, size_t length, const char *field, char **target, size_t *target_size) { + size_t fl, nl; + void *buf; + + assert(data); + assert(field); + assert(target); + assert(target_size); + + fl = strlen(field); + if (length < fl) + return 0; + + if (memcmp(data, field, fl)) + return 0; + + nl = length - fl; + buf = malloc(nl+1); + memcpy(buf, (const char*) data + fl, nl); + ((char*)buf)[nl] = 0; + if (!buf) { + log_error("Out of memory"); + return -ENOMEM; + } + + free(*target); + *target = buf; + *target_size = nl; + + return 1; +} + +static bool shall_print(bool show_all, char *p, size_t l) { + if (show_all) + return true; + + if (l > PRINT_THRESHOLD) + return false; + + if (contains_unprintable(p, l)) + return false; + + return true; +} + +static int output_short(sd_journal *j, unsigned line, bool show_all, bool monotonic_mode) { + int r; + const void *data; + size_t length; + size_t n = 0; + char *hostname = NULL, *identifier = NULL, *comm = NULL, *pid = NULL, *fake_pid = NULL, *message = NULL, *realtime = NULL, *monotonic = NULL; + size_t hostname_len = 0, identifier_len = 0, comm_len = 0, pid_len = 0, fake_pid_len = 0, message_len = 0, realtime_len = 0, monotonic_len = 0; + + assert(j); + + SD_JOURNAL_FOREACH_DATA(j, data, length) { + + r = parse_field(data, length, "_HOSTNAME=", &hostname, &hostname_len); + if (r < 0) + goto finish; + else if (r > 0) + continue; + + r = parse_field(data, length, "SYSLOG_IDENTIFIER=", &identifier, &identifier_len); + if (r < 0) + goto finish; + else if (r > 0) + continue; + + r = parse_field(data, length, "_COMM=", &comm, &comm_len); + if (r < 0) + goto finish; + else if (r > 0) + continue; + + r = parse_field(data, length, "_PID=", &pid, &pid_len); + if (r < 0) + goto finish; + else if (r > 0) + continue; + + r = parse_field(data, length, "SYSLOG_PID=", &fake_pid, &fake_pid_len); + if (r < 0) + goto finish; + else if (r > 0) + continue; + + r = parse_field(data, length, "_SOURCE_REALTIME_TIMESTAMP=", &realtime, &realtime_len); + if (r < 0) + goto finish; + else if (r > 0) + continue; + + r = parse_field(data, length, "_SOURCE_MONOTONIC_TIMESTAMP=", &monotonic, &monotonic_len); + if (r < 0) + goto finish; + else if (r > 0) + continue; + + r = parse_field(data, length, "MESSAGE=", &message, &message_len); + if (r < 0) + goto finish; + } + + if (!message) { + r = 0; + goto finish; + } + + if (monotonic_mode) { + uint64_t t; + sd_id128_t boot_id; + + r = -ENOENT; + + if (monotonic) + r = safe_atou64(monotonic, &t); + + if (r < 0) + r = sd_journal_get_monotonic_usec(j, &t, &boot_id); + + if (r < 0) { + log_error("Failed to get monotonic: %s", strerror(-r)); + goto finish; + } + + printf("[%5llu.%06llu]", + (unsigned long long) (t / USEC_PER_SEC), + (unsigned long long) (t % USEC_PER_SEC)); + + n += 1 + 5 + 1 + 6 + 1; + + } else { + char buf[64]; + uint64_t x; + time_t t; + struct tm tm; + + r = -ENOENT; + + if (realtime) + r = safe_atou64(realtime, &x); + + if (r < 0) + r = sd_journal_get_realtime_usec(j, &x); + + if (r < 0) { + log_error("Failed to get realtime: %s", strerror(-r)); + goto finish; + } + + t = (time_t) (x / USEC_PER_SEC); + if (strftime(buf, sizeof(buf), "%b %d %H:%M:%S", localtime_r(&t, &tm)) <= 0) { + log_error("Failed to format time."); + goto finish; + } + + fputs(buf, stdout); + n += strlen(buf); + } + + if (hostname && shall_print(show_all, hostname, hostname_len)) { + printf(" %.*s", (int) hostname_len, hostname); + n += hostname_len + 1; + } + + if (identifier && shall_print(show_all, identifier, identifier_len)) { + printf(" %.*s", (int) identifier_len, identifier); + n += identifier_len + 1; + } else if (comm && shall_print(show_all, comm, comm_len)) { + printf(" %.*s", (int) comm_len, comm); + n += comm_len + 1; + } + + if (pid && shall_print(show_all, pid, pid_len)) { + printf("[%.*s]", (int) pid_len, pid); + n += pid_len + 2; + } else if (fake_pid && shall_print(show_all, fake_pid, fake_pid_len)) { + printf("[%.*s]", (int) fake_pid_len, fake_pid); + n += fake_pid_len + 2; + } + + if (show_all) + printf(": %.*s\n", (int) message_len, message); + else if (contains_unprintable(message, message_len)) { + char bytes[FORMAT_BYTES_MAX]; + printf(": [%s blob data]\n", format_bytes(bytes, sizeof(bytes), message_len)); + } else if (message_len + n < columns()) + printf(": %.*s\n", (int) message_len, message); + else if (n < columns()) { + char *e; + + e = ellipsize_mem(message, message_len, columns() - n - 2, 90); + + if (!e) + printf(": %.*s\n", (int) message_len, message); + else + printf(": %s\n", e); + + free(e); + } else + fputs("\n", stdout); + + r = 0; + +finish: + free(hostname); + free(identifier); + free(comm); + free(pid); + free(fake_pid); + free(message); + free(monotonic); + free(realtime); + + return r; +} + +static int output_short_realtime(sd_journal *j, unsigned line, bool show_all) { + return output_short(j, line, show_all, false); +} + +static int output_short_monotonic(sd_journal *j, unsigned line, bool show_all) { + return output_short(j, line, show_all, true); +} + +static int output_verbose(sd_journal *j, unsigned line, bool show_all) { + const void *data; + size_t length; + char *cursor; + uint64_t realtime; + char ts[FORMAT_TIMESTAMP_MAX]; + int r; + + assert(j); + + r = sd_journal_get_realtime_usec(j, &realtime); + if (r < 0) { + log_error("Failed to get realtime timestamp: %s", strerror(-r)); + return r; + } + + r = sd_journal_get_cursor(j, &cursor); + if (r < 0) { + log_error("Failed to get cursor: %s", strerror(-r)); + return r; + } + + printf("%s [%s]\n", + format_timestamp(ts, sizeof(ts), realtime), + cursor); + + free(cursor); + + SD_JOURNAL_FOREACH_DATA(j, data, length) { + if (!show_all && (length > PRINT_THRESHOLD || + contains_unprintable(data, length))) { + const char *c; + char bytes[FORMAT_BYTES_MAX]; + + c = memchr(data, '=', length); + if (!c) { + log_error("Invalid field."); + return -EINVAL; + } + + printf("\t%.*s=[%s blob data]\n", + (int) (c - (const char*) data), + (const char*) data, + format_bytes(bytes, sizeof(bytes), length - (c - (const char *) data) - 1)); + } else + printf("\t%.*s\n", (int) length, (const char*) data); + } + + return 0; +} + +static int output_export(sd_journal *j, unsigned line, bool show_all) { + sd_id128_t boot_id; + char sid[33]; + int r; + usec_t realtime, monotonic; + char *cursor; + const void *data; + size_t length; + + assert(j); + + r = sd_journal_get_realtime_usec(j, &realtime); + if (r < 0) { + log_error("Failed to get realtime timestamp: %s", strerror(-r)); + return r; + } + + r = sd_journal_get_monotonic_usec(j, &monotonic, &boot_id); + if (r < 0) { + log_error("Failed to get monotonic timestamp: %s", strerror(-r)); + return r; + } + + r = sd_journal_get_cursor(j, &cursor); + if (r < 0) { + log_error("Failed to get cursor: %s", strerror(-r)); + return r; + } + + printf(".cursor=%s\n" + ".realtime=%llu\n" + ".monotonic=%llu\n" + ".boot_id=%s\n", + cursor, + (unsigned long long) realtime, + (unsigned long long) monotonic, + sd_id128_to_string(boot_id, sid)); + + free(cursor); + + SD_JOURNAL_FOREACH_DATA(j, data, length) { + + if (contains_unprintable(data, length)) { + const char *c; + uint64_t le64; + + c = memchr(data, '=', length); + if (!c) { + log_error("Invalid field."); + return -EINVAL; + } + + fwrite(data, c - (const char*) data, 1, stdout); + fputc('\n', stdout); + le64 = htole64(length - (c - (const char*) data) - 1); + fwrite(&le64, sizeof(le64), 1, stdout); + fwrite(c + 1, length - (c - (const char*) data) - 1, 1, stdout); + } else + fwrite(data, length, 1, stdout); + + fputc('\n', stdout); + } + + fputc('\n', stdout); + + return 0; +} + +static void json_escape(const char* p, size_t l) { + + if (contains_unprintable(p, l)) { + bool not_first = false; + + fputs("[ ", stdout); + + while (l > 0) { + if (not_first) + printf(", %u", (uint8_t) *p); + else { + not_first = true; + printf("%u", (uint8_t) *p); + } + + p++; + l--; + } + + fputs(" ]", stdout); + } else { + fputc('\"', stdout); + + while (l > 0) { + if (*p == '"' || *p == '\\') { + fputc('\\', stdout); + fputc(*p, stdout); + } else + fputc(*p, stdout); + + p++; + l--; + } + + fputc('\"', stdout); + } +} + +static int output_json(sd_journal *j, unsigned line, bool show_all) { + uint64_t realtime, monotonic; + char *cursor; + const void *data; + size_t length; + sd_id128_t boot_id; + char sid[33]; + int r; + + assert(j); + + r = sd_journal_get_realtime_usec(j, &realtime); + if (r < 0) { + log_error("Failed to get realtime timestamp: %s", strerror(-r)); + return r; + } + + r = sd_journal_get_monotonic_usec(j, &monotonic, &boot_id); + if (r < 0) { + log_error("Failed to get monotonic timestamp: %s", strerror(-r)); + return r; + } + + r = sd_journal_get_cursor(j, &cursor); + if (r < 0) { + log_error("Failed to get cursor: %s", strerror(-r)); + return r; + } + + if (line == 1) + fputc('\n', stdout); + else + fputs(",\n", stdout); + + printf("{\n" + "\t\".cursor\" : \"%s\",\n" + "\t\".realtime\" : %llu,\n" + "\t\".monotonic\" : %llu,\n" + "\t\".boot_id\" : \"%s\"", + cursor, + (unsigned long long) realtime, + (unsigned long long) monotonic, + sd_id128_to_string(boot_id, sid)); + + free(cursor); + + SD_JOURNAL_FOREACH_DATA(j, data, length) { + const char *c; + + c = memchr(data, '=', length); + if (!c) { + log_error("Invalid field."); + return -EINVAL; + } + + fputs(",\n\t", stdout); + json_escape(data, c - (const char*) data); + fputs(" : ", stdout); + json_escape(c + 1, length - (c - (const char*) data) - 1); + } + + fputs("\n}", stdout); + fflush(stdout); + + return 0; +} + +static int output_cat(sd_journal *j, unsigned line, bool show_all) { + const void *data; + size_t l; + int r; + + assert(j); + + r = sd_journal_get_data(j, "MESSAGE", &data, &l); + if (r < 0) { + log_error("Failed to get data: %s", strerror(-r)); + return r; + } + + assert(l >= 8); + + fwrite((const char*) data + 8, 1, l - 8, stdout); + putchar('\n'); + + return 0; +} + +static int (*output_funcs[_OUTPUT_MODE_MAX])(sd_journal*j, unsigned line, bool show_all) = { + [OUTPUT_SHORT] = output_short_realtime, + [OUTPUT_SHORT_MONOTONIC] = output_short_monotonic, + [OUTPUT_VERBOSE] = output_verbose, + [OUTPUT_EXPORT] = output_export, + [OUTPUT_JSON] = output_json, + [OUTPUT_CAT] = output_cat +}; + +int output_journal(sd_journal *j, OutputMode mode, unsigned line, bool show_all) { + assert(mode >= 0); + assert(mode < _OUTPUT_MODE_MAX); + + return output_funcs[mode](j, line, show_all); +} + +int show_journal_by_unit( + const char *unit, + OutputMode mode, + const char *prefix, + unsigned n_columns, + usec_t not_before, + unsigned how_many, + bool show_all, + bool follow) { + + char *m = NULL; + sd_journal *j; + int r; + int fd; + unsigned line = 0; + bool need_seek = false; + + assert(mode >= 0); + assert(mode < _OUTPUT_MODE_MAX); + assert(unit); + + if (!endswith(unit, ".service") && + !endswith(unit, ".socket") && + !endswith(unit, ".mount") && + !endswith(unit, ".swap")) + return 0; + + if (how_many <= 0) + return 0; + + if (n_columns <= 0) + n_columns = columns(); + + if (!prefix) + prefix = ""; + + if (asprintf(&m, "_SYSTEMD_UNIT=%s", unit) < 0) { + r = -ENOMEM; + goto finish; + } + + r = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY|SD_JOURNAL_SYSTEM_ONLY); + if (r < 0) + goto finish; + + fd = sd_journal_get_fd(j); + if (fd < 0) + goto finish; + + r = sd_journal_add_match(j, m, strlen(m)); + if (r < 0) + goto finish; + + r = sd_journal_seek_tail(j); + if (r < 0) + goto finish; + + r = sd_journal_previous_skip(j, how_many); + if (r < 0) + goto finish; + + if (mode == OUTPUT_JSON) { + fputc('[', stdout); + fflush(stdout); + } + + for (;;) { + for (;;) { + usec_t usec; + + if (need_seek) { + r = sd_journal_next(j); + if (r < 0) + goto finish; + } + + if (r == 0) + break; + + need_seek = true; + + if (not_before > 0) { + r = sd_journal_get_monotonic_usec(j, &usec, NULL); + + /* -ESTALE is returned if the + timestamp is not from this boot */ + if (r == -ESTALE) + continue; + else if (r < 0) + goto finish; + + if (usec < not_before) + continue; + } + + line ++; + + r = output_journal(j, mode, line, show_all); + if (r < 0) + goto finish; + } + + if (!follow) + break; + + r = fd_wait_for_event(fd, POLLIN, (usec_t) -1); + if (r < 0) + goto finish; + + r = sd_journal_process(j); + if (r < 0) + goto finish; + + } + + if (mode == OUTPUT_JSON) + fputs("\n]\n", stdout); + +finish: + if (m) + free(m); + + if (j) + sd_journal_close(j); + + return r; +} + +static const char *const output_mode_table[_OUTPUT_MODE_MAX] = { + [OUTPUT_SHORT] = "short", + [OUTPUT_SHORT_MONOTONIC] = "short-monotonic", + [OUTPUT_VERBOSE] = "verbose", + [OUTPUT_EXPORT] = "export", + [OUTPUT_JSON] = "json", + [OUTPUT_CAT] = "cat" +}; + +DEFINE_STRING_TABLE_LOOKUP(output_mode, OutputMode); diff --git a/src/logs-show.h b/src/logs-show.h new file mode 100644 index 0000000000..abb82c8aac --- /dev/null +++ b/src/logs-show.h @@ -0,0 +1,57 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#ifndef foologsshowhfoo +#define foologsshowhfoo + +/*** + This file is part of systemd. + + Copyright 2012 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 <stdbool.h> + +#include <systemd/sd-journal.h> + +#include "util.h" + +typedef enum OutputMode { + OUTPUT_SHORT, + OUTPUT_SHORT_MONOTONIC, + OUTPUT_VERBOSE, + OUTPUT_EXPORT, + OUTPUT_JSON, + OUTPUT_CAT, + _OUTPUT_MODE_MAX, + _OUTPUT_MODE_INVALID = -1 +} OutputMode; + +int output_journal(sd_journal *j, OutputMode mode, unsigned line, bool show_all); + +int show_journal_by_unit( + const char *unit, + OutputMode mode, + const char *prefix, + unsigned n_columns, + usec_t not_before, + unsigned how_many, + bool show_all, + bool follow); + +const char* output_mode_to_string(OutputMode m); +OutputMode output_mode_from_string(const char *s); + +#endif diff --git a/src/machine-id-setup.c b/src/machine-id-setup.c index be51d0dec7..531f3b2073 100644 --- a/src/machine-id-setup.c +++ b/src/machine-id-setup.c @@ -27,25 +27,17 @@ #include <fcntl.h> #include <sys/mount.h> +#include <systemd/sd-id128.h> + #include "machine-id-setup.h" #include "macro.h" #include "util.h" #include "log.h" -static void make_v4_uuid(unsigned char *id) { - /* Stolen from generate_random_uuid() of drivers/char/random.c - * in the kernel sources */ - - /* Set UUID version to 4 --- truly random generation */ - id[6] = (id[6] & 0x0F) | 0x40; - - /* Set the UUID variant to DCE */ - id[8] = (id[8] & 0x3F) | 0x80; -} - static int generate(char id[34]) { - int fd; - unsigned char buf[16], *p; + int fd, r; + unsigned char *p; + sd_id128_t buf; char *q; ssize_t k; @@ -68,26 +60,13 @@ static int generate(char id[34]) { } /* If that didn't work, generate a random machine id */ - fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC|O_NOCTTY); - if (fd < 0) { - log_error("Failed to open /dev/urandom: %m"); - return -errno; - } - - k = loop_read(fd, buf, sizeof(buf), false); - close_nointr_nofail(fd); - - if (k != sizeof(buf)) { - log_error("Failed to read /dev/urandom: %s", strerror(k < 0 ? -k : EIO)); - return k < 0 ? (int) k : -EIO; + r = sd_id128_randomize(&buf); + if (r < 0) { + log_error("Failed to open /dev/urandom: %s", strerror(-r)); + return r; } - /* Turn this into a valid v4 UUID, to be nice. Note that we - * only guarantee this for newly generated UUIDs, not for - * pre-existing ones.*/ - make_v4_uuid(buf); - - for (p = buf, q = id; p < buf + sizeof(buf); p++, q += 2) { + for (p = buf.bytes, q = id; p < buf.bytes + sizeof(buf); p++, q += 2) { q[0] = hexchar(*p >> 4); q[1] = hexchar(*p & 15); } @@ -167,7 +146,10 @@ int machine_id_setup(void) { mkdir_p("/run/systemd", 0755); + m = umask(0022); r = write_one_line_file("/run/systemd/machine-id", id); + umask(m); + if (r < 0) { log_error("Cannot write /run/systemd/machine-id: %s", strerror(-r)); diff --git a/src/macro.h b/src/macro.h index e7a4d2cde1..58de001f26 100644 --- a/src/macro.h +++ b/src/macro.h @@ -91,9 +91,7 @@ static inline size_t ALIGN_TO(size_t l, size_t ali) { #define assert_se(expr) \ do { \ if (_unlikely_(!(expr))) \ - log_assert(__FILE__, __LINE__, __PRETTY_FUNCTION__, \ - "Assertion '%s' failed at %s:%u, function %s(). Aborting.", \ - #expr , __FILE__, __LINE__, __PRETTY_FUNCTION__); \ + log_assert_failed(#expr, __FILE__, __LINE__, __PRETTY_FUNCTION__); \ } while (false) \ /* We override the glibc assert() here. */ @@ -106,9 +104,7 @@ static inline size_t ALIGN_TO(size_t l, size_t ali) { #define assert_not_reached(t) \ do { \ - log_assert(__FILE__, __LINE__, __PRETTY_FUNCTION__, \ - "Code should not be reached '%s' at %s:%u, function %s(). Aborting.", \ - t, __FILE__, __LINE__, __PRETTY_FUNCTION__); \ + log_assert_failed_unreachable(t, __FILE__, __LINE__, __PRETTY_FUNCTION__); \ } while (false) #define assert_cc(expr) \ @@ -149,7 +145,7 @@ static inline size_t ALIGN_TO(size_t l, size_t ali) { char *_s = (char *)(s); \ _i->iov_base = _s; \ _i->iov_len = strlen(_s); \ - } while(false); + } while(false) static inline size_t IOVEC_TOTAL_SIZE(const struct iovec *i, unsigned n) { unsigned j; diff --git a/src/main.c b/src/main.c index b181447ca3..ed317b4413 100644 --- a/src/main.c +++ b/src/main.c @@ -52,6 +52,7 @@ #include "build.h" #include "strv.h" #include "def.h" +#include "virt.h" static enum { ACTION_RUN, @@ -75,7 +76,8 @@ static bool arg_sysv_console = true; static bool arg_mount_auto = true; static bool arg_swap_auto = true; static char **arg_default_controllers = NULL; -static ExecOutput arg_default_std_output = EXEC_OUTPUT_INHERIT; +static char ***arg_join_controllers = NULL; +static ExecOutput arg_default_std_output = EXEC_OUTPUT_JOURNAL; static ExecOutput arg_default_std_error = EXEC_OUTPUT_INHERIT; static FILE* serialization = NULL; @@ -198,12 +200,16 @@ static int console_setup(bool do_reset) { if (!do_reset) return 0; - if ((tty_fd = open_terminal("/dev/console", O_WRONLY|O_NOCTTY|O_CLOEXEC)) < 0) { + tty_fd = open_terminal("/dev/console", O_WRONLY|O_NOCTTY|O_CLOEXEC); + if (tty_fd < 0) { log_error("Failed to open /dev/console: %s", strerror(-tty_fd)); return -tty_fd; } - if ((r = reset_terminal_fd(tty_fd)) < 0) + /* We don't want to force text mode. + * plymouth may be showing pictures already from initrd. */ + r = reset_terminal_fd(tty_fd, false); + if (r < 0) log_error("Failed to reset /dev/console: %s", strerror(-r)); close_nointr_nofail(tty_fd); @@ -268,7 +274,7 @@ static int parse_proc_cmdline_word(const char *word) { int r; if ((r = parse_boolean(word + 18)) < 0) - log_warning("Failed to parse dump core switch %s, Ignoring.", word + 18); + log_warning("Failed to parse dump core switch %s. Ignoring.", word + 18); else arg_dump_core = r; @@ -276,7 +282,7 @@ static int parse_proc_cmdline_word(const char *word) { int r; if ((r = parse_boolean(word + 20)) < 0) - log_warning("Failed to parse crash shell switch %s, Ignoring.", word + 20); + log_warning("Failed to parse crash shell switch %s. Ignoring.", word + 20); else arg_crash_shell = r; @@ -284,7 +290,7 @@ static int parse_proc_cmdline_word(const char *word) { int r; if ((r = parse_boolean(word + 22)) < 0) - log_warning("Failed to parse confirm spawn switch %s, Ignoring.", word + 22); + log_warning("Failed to parse confirm spawn switch %s. Ignoring.", word + 22); else arg_confirm_spawn = r; @@ -292,7 +298,7 @@ static int parse_proc_cmdline_word(const char *word) { int k; if (safe_atoi(word + 19, &k) < 0) - log_warning("Failed to parse crash chvt switch %s, Ignoring.", word + 19); + log_warning("Failed to parse crash chvt switch %s. Ignoring.", word + 19); else arg_crash_chvt = k; @@ -300,29 +306,49 @@ static int parse_proc_cmdline_word(const char *word) { int r; if ((r = parse_boolean(word + 20)) < 0) - log_warning("Failed to parse show status switch %s, Ignoring.", word + 20); + log_warning("Failed to parse show status switch %s. Ignoring.", word + 20); else arg_show_status = r; } else if (startswith(word, "systemd.default_standard_output=")) { int r; if ((r = exec_output_from_string(word + 32)) < 0) - log_warning("Failed to parse default standard output switch %s, Ignoring.", word + 32); + log_warning("Failed to parse default standard output switch %s. Ignoring.", word + 32); else arg_default_std_output = r; } else if (startswith(word, "systemd.default_standard_error=")) { int r; if ((r = exec_output_from_string(word + 31)) < 0) - log_warning("Failed to parse default standard error switch %s, Ignoring.", word + 31); + log_warning("Failed to parse default standard error switch %s. Ignoring.", word + 31); else arg_default_std_error = r; + } else if (startswith(word, "systemd.setenv=")) { + char *cenv, *eq; + int r; + + cenv = strdup(word + 15); + if (!cenv) + return -ENOMEM; + + eq = strchr(cenv, '='); + if (!eq) { + r = unsetenv(cenv); + if (r < 0) + log_warning("unsetenv failed %s. Ignoring.", strerror(errno)); + } else { + *eq = 0; + r = setenv(cenv, eq + 1, 1); + if (r < 0) + log_warning("setenv failed %s. Ignoring.", strerror(errno)); + } + free(cenv); #ifdef HAVE_SYSV_COMPAT } else if (startswith(word, "systemd.sysv_console=")) { int r; if ((r = parse_boolean(word + 21)) < 0) - log_warning("Failed to parse SysV console switch %s, Ignoring.", word + 20); + log_warning("Failed to parse SysV console switch %s. Ignoring.", word + 20); else arg_sysv_console = r; #endif @@ -341,14 +367,14 @@ static int parse_proc_cmdline_word(const char *word) { #ifdef HAVE_SYSV_COMPAT "systemd.sysv_console=0|1 Connect output of SysV scripts to console\n" #endif - "systemd.log_target=console|kmsg|syslog|syslog-or-kmsg|null\n" + "systemd.log_target=console|kmsg|journal|journal-or-kmsg|syslog|syslog-or-kmsg|null\n" " Log target\n" "systemd.log_level=LEVEL Log level\n" "systemd.log_color=0|1 Highlight important log messages\n" "systemd.log_location=0|1 Include code location in log messages\n" - "systemd.default_standard_output=null|tty|syslog|syslog+console|kmsg|kmsg+console\n" + "systemd.default_standard_output=null|tty|syslog|syslog+console|kmsg|kmsg+console|journal|journal+console\n" " Set default log output for services\n" - "systemd.default_standard_error=null|tty|syslog|syslog+console|kmsg|kmsg+console\n" + "systemd.default_standard_error=null|tty|syslog|syslog+console|kmsg|kmsg+console|journal|journal+console\n" " Set default log error output for services\n"); } else if (streq(word, "quiet")) { @@ -368,7 +394,7 @@ static int parse_proc_cmdline_word(const char *word) { return 0; } -static int config_parse_level( +static int config_parse_level2( const char *filename, unsigned line, const char *section, @@ -440,7 +466,7 @@ static int config_parse_location( return 0; } -static int config_parse_cpu_affinity( +static int config_parse_cpu_affinity2( const char *filename, unsigned line, const char *section, @@ -494,34 +520,146 @@ static int config_parse_cpu_affinity( return 0; } -static DEFINE_CONFIG_PARSE_ENUM(config_parse_output, exec_output, ExecOutput, "Failed to parse output specifier"); +static void strv_free_free(char ***l) { + char ***i; + + if (!l) + return; + + for (i = l; *i; i++) + strv_free(*i); + + free(l); +} + +static void free_join_controllers(void) { + if (!arg_join_controllers) + return; + + strv_free_free(arg_join_controllers); + arg_join_controllers = NULL; +} + +static int config_parse_join_controllers( + const char *filename, + unsigned line, + const char *section, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + unsigned n = 0; + char *state, *w; + size_t length; + + assert(filename); + assert(lvalue); + assert(rvalue); + + free_join_controllers(); + + FOREACH_WORD_QUOTED(w, length, rvalue, state) { + char *s, **l; + + s = strndup(w, length); + if (!s) + return -ENOMEM; + + l = strv_split(s, ","); + free(s); + + strv_uniq(l); + + if (strv_length(l) <= 1) { + strv_free(l); + continue; + } + + if (!arg_join_controllers) { + arg_join_controllers = new(char**, 2); + if (!arg_join_controllers) { + strv_free(l); + return -ENOMEM; + } + + arg_join_controllers[0] = l; + arg_join_controllers[1] = NULL; + + n = 1; + } else { + char ***a; + char ***t; + + t = new0(char**, n+2); + if (!t) { + strv_free(l); + return -ENOMEM; + } + + n = 0; + + for (a = arg_join_controllers; *a; a++) { + + if (strv_overlap(*a, l)) { + char **c; + + c = strv_merge(*a, l); + if (!c) { + strv_free(l); + strv_free_free(t); + return -ENOMEM; + } + + strv_free(l); + l = c; + } else { + char **c; + + c = strv_copy(*a); + if (!c) { + strv_free(l); + strv_free_free(t); + return -ENOMEM; + } + + t[n++] = c; + } + } + + t[n++] = strv_uniq(l); + + strv_free_free(arg_join_controllers); + arg_join_controllers = t; + } + } + + return 0; +} static int parse_config_file(void) { - const ConfigItem items[] = { - { "LogLevel", config_parse_level, 0, NULL, "Manager" }, - { "LogTarget", config_parse_target, 0, NULL, "Manager" }, - { "LogColor", config_parse_color, 0, NULL, "Manager" }, - { "LogLocation", config_parse_location, 0, NULL, "Manager" }, - { "DumpCore", config_parse_bool, 0, &arg_dump_core, "Manager" }, - { "CrashShell", config_parse_bool, 0, &arg_crash_shell, "Manager" }, - { "ShowStatus", config_parse_bool, 0, &arg_show_status, "Manager" }, + const ConfigTableItem items[] = { + { "Manager", "LogLevel", config_parse_level2, 0, NULL }, + { "Manager", "LogTarget", config_parse_target, 0, NULL }, + { "Manager", "LogColor", config_parse_color, 0, NULL }, + { "Manager", "LogLocation", config_parse_location, 0, NULL }, + { "Manager", "DumpCore", config_parse_bool, 0, &arg_dump_core }, + { "Manager", "CrashShell", config_parse_bool, 0, &arg_crash_shell }, + { "Manager", "ShowStatus", config_parse_bool, 0, &arg_show_status }, #ifdef HAVE_SYSV_COMPAT - { "SysVConsole", config_parse_bool, 0, &arg_sysv_console, "Manager" }, + { "Manager", "SysVConsole", config_parse_bool, 0, &arg_sysv_console }, #endif - { "CrashChVT", config_parse_int, 0, &arg_crash_chvt, "Manager" }, - { "CPUAffinity", config_parse_cpu_affinity, 0, NULL, "Manager" }, - { "MountAuto", config_parse_bool, 0, &arg_mount_auto, "Manager" }, - { "SwapAuto", config_parse_bool, 0, &arg_swap_auto, "Manager" }, - { "DefaultControllers", config_parse_strv, 0, &arg_default_controllers, "Manager" }, - { "DefaultStandardOutput", config_parse_output, 0, &arg_default_std_output, "Manager" }, - { "DefaultStandardError", config_parse_output, 0, &arg_default_std_error, "Manager" }, - { NULL, NULL, 0, NULL, NULL } - }; - - static const char * const sections[] = { - "Manager", - NULL + { "Manager", "CrashChVT", config_parse_int, 0, &arg_crash_chvt }, + { "Manager", "CPUAffinity", config_parse_cpu_affinity2, 0, NULL }, + { "Manager", "MountAuto", config_parse_bool, 0, &arg_mount_auto }, + { "Manager", "SwapAuto", config_parse_bool, 0, &arg_swap_auto }, + { "Manager", "DefaultControllers", config_parse_strv, 0, &arg_default_controllers }, + { "Manager", "DefaultStandardOutput", config_parse_output, 0, &arg_default_std_output }, + { "Manager", "DefaultStandardError", config_parse_output, 0, &arg_default_std_error }, + { "Manager", "JoinControllers", config_parse_join_controllers, 0, &arg_join_controllers }, + { NULL, NULL, NULL, 0, NULL } }; FILE *f; @@ -529,8 +667,8 @@ static int parse_config_file(void) { int r; fn = arg_running_as == MANAGER_SYSTEM ? SYSTEM_CONFIG_FILE : USER_CONFIG_FILE; - - if (!(f = fopen(fn, "re"))) { + f = fopen(fn, "re"); + if (!f) { if (errno == ENOENT) return 0; @@ -538,7 +676,8 @@ static int parse_config_file(void) { return 0; } - if ((r = config_parse(fn, f, sections, items, false, NULL)) < 0) + r = config_parse(fn, f, "Manager\0", config_item_table_lookup, (void*) items, false, NULL); + if (r < 0) log_warning("Failed to parse configuration file: %s", strerror(-r)); fclose(f); @@ -878,7 +1017,7 @@ static int help(void) { #ifdef HAVE_SYSV_COMPAT " --sysv-console[=0|1] Connect output of SysV scripts to console\n" #endif - " --log-target=TARGET Set log target (console, syslog, kmsg, syslog-or-kmsg, null)\n" + " --log-target=TARGET Set log target (console, journal, syslog, kmsg, journal-or-kmsg, syslog-or-kmsg, null)\n" " --log-level=LEVEL Set log level (debug, info, notice, warning, err, crit, alert, emerg)\n" " --log-color[=0|1] Highlight important log messages\n" " --log-location[=0|1] Include code location in log messages\n" @@ -1013,12 +1152,18 @@ static void test_cgroups(void) { int main(int argc, char *argv[]) { Manager *m = NULL; int r, retval = EXIT_FAILURE; + usec_t before_startup, after_startup; + char timespan[FORMAT_TIMESPAN_MAX]; FDSet *fds = NULL; bool reexecute = false; const char *shutdown_verb = NULL; dual_timestamp initrd_timestamp = { 0ULL, 0ULL }; - char systemd[] = "systemd"; + static char systemd[] = "systemd"; + bool is_reexec = false; + int j; + bool loaded_policy = false; +#ifdef HAVE_SYSV_COMPAT if (getpid() != 1 && strstr(program_invocation_short_name, "init")) { /* This is compatibility support for SysV, where * calling init as a user is identical to telinit. */ @@ -1028,14 +1173,25 @@ int main(int argc, char *argv[]) { log_error("Failed to exec " SYSTEMCTL_BINARY_PATH ": %m"); return 1; } +#endif + + /* Determine if this is a reexecution or normal bootup. We do + * the full command line parsing much later, so let's just + * have a quick peek here. */ + + for (j = 1; j < argc; j++) + if (streq(argv[j], "--deserialize")) { + is_reexec = true; + break; + } /* If we get started via the /sbin/init symlink then we are called 'init'. After a subsequent reexecution we are then called 'systemd'. That is confusing, hence let's call us systemd right-away. */ - program_invocation_short_name = systemd; prctl(PR_SET_NAME, systemd); + saved_argv = argv; saved_argc = argc; @@ -1045,26 +1201,27 @@ int main(int argc, char *argv[]) { if (getpid() == 1) { arg_running_as = MANAGER_SYSTEM; - log_set_target(detect_container(NULL) > 0 ? LOG_TARGET_CONSOLE : LOG_TARGET_SYSLOG_OR_KMSG); - log_open(); + log_set_target(detect_container(NULL) > 0 ? LOG_TARGET_CONSOLE : LOG_TARGET_JOURNAL_OR_KMSG); - /* This might actually not return, but cause a - * reexecution */ - if (selinux_setup(argv) < 0) - goto finish; + if (!is_reexec) + if (selinux_setup(&loaded_policy) < 0) + goto finish; + + log_open(); if (label_init() < 0) goto finish; - if (hwclock_is_localtime() > 0) { - int err, min; + if (!is_reexec) + if (hwclock_is_localtime() > 0) { + int min; - err = hwclock_apply_localtime_delta(&min); - if (err < 0) - log_error("Failed to apply local time delta: %s", strerror(-err)); - else - log_info("RTC configured in localtime, applying delta of %i minutes to system time.", min); - } + r = hwclock_apply_localtime_delta(&min); + if (r < 0) + log_error("Failed to apply local time delta, ignoring: %s", strerror(-r)); + else + log_info("RTC configured in localtime, applying delta of %i minutes to system time.", min); + } } else { arg_running_as = MANAGER_USER; @@ -1072,14 +1229,28 @@ int main(int argc, char *argv[]) { log_open(); } + /* Initialize default unit */ if (set_default_unit(SPECIAL_DEFAULT_TARGET) < 0) goto finish; + /* By default, mount "cpu" and "cpuacct" together */ + arg_join_controllers = new(char**, 2); + if (!arg_join_controllers) + goto finish; + + arg_join_controllers[0] = strv_new("cpu", "cpuacct", NULL); + arg_join_controllers[1] = NULL; + + if (!arg_join_controllers[0]) + goto finish; + /* Mount /proc, /sys and friends, so that /proc/cmdline and * /proc/$PID/fd is available. */ - if (geteuid() == 0 && !getenv("SYSTEMD_SKIP_API_MOUNTS")) - if (mount_setup() < 0) + if (geteuid() == 0 && !getenv("SYSTEMD_SKIP_API_MOUNTS")) { + r = mount_setup(loaded_policy); + if (r < 0) goto finish; + } /* Reset all signal handlers. */ assert_se(reset_all_signal_handlers() == 0); @@ -1141,7 +1312,11 @@ int main(int argc, char *argv[]) { /* Set up PATH unless it is already set */ setenv("PATH", +#ifdef HAVE_SPLIT_USR "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", +#else + "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin", +#endif arg_running_as == MANAGER_SYSTEM); if (arg_running_as == MANAGER_SYSTEM) { @@ -1175,7 +1350,7 @@ int main(int argc, char *argv[]) { /* Reset the console, but only if this is really init and we * are freshly booted */ if (arg_running_as == MANAGER_SYSTEM && arg_action == ACTION_RUN) { - console_setup(getpid() == 1 && !serialization); + console_setup(getpid() == 1 && !is_reexec); make_null_stdio(); } @@ -1187,10 +1362,16 @@ int main(int argc, char *argv[]) { if (getpid() == 1) install_crash_handler(); + if (geteuid() == 0 && !getenv("SYSTEMD_SKIP_API_MOUNTS")) { + r = mount_cgroup_controllers(arg_join_controllers); + if (r < 0) + goto finish; + } + log_full(arg_running_as == MANAGER_SYSTEM ? LOG_INFO : LOG_DEBUG, PACKAGE_STRING " running in %s mode. (" SYSTEMD_FEATURES "; " DISTRIBUTION ")", manager_running_as_to_string(arg_running_as)); - if (arg_running_as == MANAGER_SYSTEM && !serialization) { + if (arg_running_as == MANAGER_SYSTEM && !is_reexec) { locale_setup(); if (arg_show_status || plymouth_running()) @@ -1212,7 +1393,6 @@ int main(int argc, char *argv[]) { } m->confirm_spawn = arg_confirm_spawn; - m->show_status = arg_show_status; #ifdef HAVE_SYSV_COMPAT m->sysv_console = arg_sysv_console; #endif @@ -1227,6 +1407,10 @@ int main(int argc, char *argv[]) { if (arg_default_controllers) manager_set_default_controllers(m, arg_default_controllers); + manager_set_show_status(m, arg_show_status); + + before_startup = now(CLOCK_MONOTONIC); + if ((r = manager_startup(m, serialization, fds)) < 0) log_error("Failed to fully start up daemon: %s", strerror(-r)); @@ -1244,6 +1428,7 @@ int main(int argc, char *argv[]) { } else { DBusError error; Unit *target = NULL; + Job *default_unit_job; dbus_error_init(&error); @@ -1252,39 +1437,46 @@ int main(int argc, char *argv[]) { if ((r = manager_load_unit(m, arg_default_unit, NULL, &error, &target)) < 0) { log_error("Failed to load default target: %s", bus_error(&error, r)); dbus_error_free(&error); - } else if (target->meta.load_state == UNIT_ERROR) - log_error("Failed to load default target: %s", strerror(-target->meta.load_error)); - else if (target->meta.load_state == UNIT_MASKED) + } else if (target->load_state == UNIT_ERROR) + log_error("Failed to load default target: %s", strerror(-target->load_error)); + else if (target->load_state == UNIT_MASKED) log_error("Default target masked."); - if (!target || target->meta.load_state != UNIT_LOADED) { + if (!target || target->load_state != UNIT_LOADED) { log_info("Trying to load rescue target..."); if ((r = manager_load_unit(m, SPECIAL_RESCUE_TARGET, NULL, &error, &target)) < 0) { log_error("Failed to load rescue target: %s", bus_error(&error, r)); dbus_error_free(&error); goto finish; - } else if (target->meta.load_state == UNIT_ERROR) { - log_error("Failed to load rescue target: %s", strerror(-target->meta.load_error)); + } else if (target->load_state == UNIT_ERROR) { + log_error("Failed to load rescue target: %s", strerror(-target->load_error)); goto finish; - } else if (target->meta.load_state == UNIT_MASKED) { + } else if (target->load_state == UNIT_MASKED) { log_error("Rescue target masked."); goto finish; } } - assert(target->meta.load_state == UNIT_LOADED); + assert(target->load_state == UNIT_LOADED); if (arg_action == ACTION_TEST) { printf("-> By units:\n"); manager_dump_units(m, stdout, "\t"); } - if ((r = manager_add_job(m, JOB_START, target, JOB_REPLACE, false, &error, NULL)) < 0) { + r = manager_add_job(m, JOB_START, target, JOB_REPLACE, false, &error, &default_unit_job); + if (r < 0) { log_error("Failed to start default target: %s", bus_error(&error, r)); dbus_error_free(&error); goto finish; } + m->default_unit_job_id = default_unit_job->id; + + after_startup = now(CLOCK_MONOTONIC); + log_full(arg_action == ACTION_TEST ? LOG_INFO : LOG_DEBUG, + "Loaded units and determined initial transaction in %s.", + format_timespan(timespan, sizeof(timespan), after_startup - before_startup)); if (arg_action == ACTION_TEST) { printf("-> By jobs:\n"); @@ -1349,6 +1541,7 @@ finish: free(arg_default_unit); strv_free(arg_default_controllers); + free_join_controllers(); dbus_shutdown(); diff --git a/src/manager.c b/src/manager.c index cdd618e148..74bd740747 100644 --- a/src/manager.c +++ b/src/manager.c @@ -41,6 +41,8 @@ #include <libaudit.h> #endif +#include <systemd/sd-daemon.h> + #include "manager.h" #include "hashmap.h" #include "macro.h" @@ -58,7 +60,7 @@ #include "special.h" #include "bus-errors.h" #include "exit-status.h" -#include "sd-daemon.h" +#include "virt.h" /* As soon as 16 units are in our GC queue, make sure to run a gc sweep */ #define GC_QUEUE_ENTRIES_MAX 16 @@ -76,7 +78,8 @@ static int manager_setup_notify(Manager *m) { struct sockaddr_un un; } sa; struct epoll_event ev; - int one = 1; + int one = 1, r; + mode_t u; assert(m); @@ -99,7 +102,11 @@ static int manager_setup_notify(Manager *m) { if (sa.un.sun_path[0] == '@') sa.un.sun_path[0] = 0; - if (bind(m->notify_watch.fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + 1 + strlen(sa.un.sun_path+1)) < 0) { + u = umask(0111); + r = bind(m->notify_watch.fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + 1 + strlen(sa.un.sun_path+1)); + umask(u); + + if (r < 0) { log_error("bind() failed: %m"); return -errno; } @@ -188,6 +195,7 @@ static int manager_setup_signals(Manager *m) { SIGRTMIN+21, /* systemd: disable status messages */ SIGRTMIN+22, /* systemd: set log level to LOG_DEBUG */ SIGRTMIN+23, /* systemd: set log level to LOG_INFO */ + SIGRTMIN+26, /* systemd: set log target to journal-or-kmsg */ SIGRTMIN+27, /* systemd: set log target to console */ SIGRTMIN+28, /* systemd: set log target to kmsg */ SIGRTMIN+29, /* systemd: set log target to syslog-or-kmsg */ @@ -225,7 +233,7 @@ int manager_new(ManagerRunningAs running_as, Manager **_m) { dual_timestamp_get(&m->startup_timestamp); m->running_as = running_as; - m->name_data_slot = m->subscribed_data_slot = -1; + m->name_data_slot = m->conn_data_slot = m->subscribed_data_slot = -1; m->exit_code = _MANAGER_EXIT_CODE_INVALID; m->pin_cgroupfs_fd = -1; @@ -239,8 +247,11 @@ int manager_new(ManagerRunningAs running_as, Manager **_m) { if (!(m->environment = strv_copy(environ))) goto fail; - if (!(m->default_controllers = strv_new("cpu", NULL))) - goto fail; + if (running_as == MANAGER_SYSTEM) { + m->default_controllers = strv_new("cpu", NULL); + if (!m->default_controllers) + goto fail; + } if (!(m->units = hashmap_new(string_hash_func, string_compare_func))) goto fail; @@ -280,7 +291,10 @@ int manager_new(ManagerRunningAs running_as, Manager **_m) { goto fail; #ifdef HAVE_AUDIT - if ((m->audit_fd = audit_open()) < 0) + if ((m->audit_fd = audit_open()) < 0 && + /* If the kernel lacks netlink or audit support, + * don't worry about it. */ + errno != EAFNOSUPPORT && errno != EPROTONOSUPPORT) log_error("Failed to connect to audit log: %m"); #endif @@ -295,15 +309,15 @@ fail: } static unsigned manager_dispatch_cleanup_queue(Manager *m) { - Meta *meta; + Unit *u; unsigned n = 0; assert(m); - while ((meta = m->cleanup_queue)) { - assert(meta->in_cleanup_queue); + while ((u = m->cleanup_queue)) { + assert(u->in_cleanup_queue); - unit_free((Unit*) meta); + unit_free(u); n++; } @@ -325,28 +339,28 @@ static void unit_gc_sweep(Unit *u, unsigned gc_marker) { assert(u); - if (u->meta.gc_marker == gc_marker + GC_OFFSET_GOOD || - u->meta.gc_marker == gc_marker + GC_OFFSET_BAD || - u->meta.gc_marker == gc_marker + GC_OFFSET_IN_PATH) + if (u->gc_marker == gc_marker + GC_OFFSET_GOOD || + u->gc_marker == gc_marker + GC_OFFSET_BAD || + u->gc_marker == gc_marker + GC_OFFSET_IN_PATH) return; - if (u->meta.in_cleanup_queue) + if (u->in_cleanup_queue) goto bad; if (unit_check_gc(u)) goto good; - u->meta.gc_marker = gc_marker + GC_OFFSET_IN_PATH; + u->gc_marker = gc_marker + GC_OFFSET_IN_PATH; is_bad = true; - SET_FOREACH(other, u->meta.dependencies[UNIT_REFERENCED_BY], i) { + SET_FOREACH(other, u->dependencies[UNIT_REFERENCED_BY], i) { unit_gc_sweep(other, gc_marker); - if (other->meta.gc_marker == gc_marker + GC_OFFSET_GOOD) + if (other->gc_marker == gc_marker + GC_OFFSET_GOOD) goto good; - if (other->meta.gc_marker != gc_marker + GC_OFFSET_BAD) + if (other->gc_marker != gc_marker + GC_OFFSET_BAD) is_bad = false; } @@ -355,23 +369,23 @@ static void unit_gc_sweep(Unit *u, unsigned gc_marker) { /* We were unable to find anything out about this entry, so * let's investigate it later */ - u->meta.gc_marker = gc_marker + GC_OFFSET_UNSURE; + u->gc_marker = gc_marker + GC_OFFSET_UNSURE; unit_add_to_gc_queue(u); return; bad: /* We definitely know that this one is not useful anymore, so * let's mark it for deletion */ - u->meta.gc_marker = gc_marker + GC_OFFSET_BAD; + u->gc_marker = gc_marker + GC_OFFSET_BAD; unit_add_to_cleanup_queue(u); return; good: - u->meta.gc_marker = gc_marker + GC_OFFSET_GOOD; + u->gc_marker = gc_marker + GC_OFFSET_GOOD; } static unsigned manager_dispatch_gc_queue(Manager *m) { - Meta *meta; + Unit *u; unsigned n = 0; unsigned gc_marker; @@ -390,21 +404,21 @@ static unsigned manager_dispatch_gc_queue(Manager *m) { gc_marker = m->gc_marker; - while ((meta = m->gc_queue)) { - assert(meta->in_gc_queue); + while ((u = m->gc_queue)) { + assert(u->in_gc_queue); - unit_gc_sweep((Unit*) meta, gc_marker); + unit_gc_sweep(u, gc_marker); - LIST_REMOVE(Meta, gc_queue, m->gc_queue, meta); - meta->in_gc_queue = false; + LIST_REMOVE(Unit, gc_queue, m->gc_queue, u); + u->in_gc_queue = false; n++; - if (meta->gc_marker == gc_marker + GC_OFFSET_BAD || - meta->gc_marker == gc_marker + GC_OFFSET_UNSURE) { - log_debug("Collecting %s", meta->id); - meta->gc_marker = gc_marker + GC_OFFSET_BAD; - unit_add_to_cleanup_queue((Unit*) meta); + if (u->gc_marker == gc_marker + GC_OFFSET_BAD || + u->gc_marker == gc_marker + GC_OFFSET_UNSURE) { + log_debug("Collecting %s", u->id); + u->gc_marker = gc_marker + GC_OFFSET_BAD; + unit_add_to_cleanup_queue(u); } } @@ -519,7 +533,7 @@ int manager_coldplug(Manager *m) { HASHMAP_FOREACH_KEY(u, k, m->units, i) { /* ignore aliases */ - if (u->meta.id != k) + if (u->id != k) continue; if ((q = unit_coldplug(u)) < 0) @@ -560,7 +574,8 @@ static void manager_build_unit_path_cache(Manager *m) { if (ignore_file(de->d_name)) continue; - if (asprintf(&p, "%s/%s", streq(*i, "/") ? "" : *i, de->d_name) < 0) { + p = join(streq(*i, "/") ? "" : *i, "/", de->d_name, NULL); + if (!p) { r = -ENOMEM; goto fail; } @@ -811,8 +826,8 @@ static int delete_one_unmergeable_job(Manager *m, Job *j) { * another unit in which case we * rather remove the start. */ - log_debug("Looking at job %s/%s conflicted_by=%s", j->unit->meta.id, job_type_to_string(j->type), yes_no(j->type == JOB_STOP && job_is_conflicted_by(j))); - log_debug("Looking at job %s/%s conflicted_by=%s", k->unit->meta.id, job_type_to_string(k->type), yes_no(k->type == JOB_STOP && job_is_conflicted_by(k))); + log_debug("Looking at job %s/%s conflicted_by=%s", j->unit->id, job_type_to_string(j->type), yes_no(j->type == JOB_STOP && job_is_conflicted_by(j))); + log_debug("Looking at job %s/%s conflicted_by=%s", k->unit->id, job_type_to_string(k->type), yes_no(k->type == JOB_STOP && job_is_conflicted_by(k))); if (j->type == JOB_STOP) { @@ -838,7 +853,7 @@ static int delete_one_unmergeable_job(Manager *m, Job *j) { return -ENOEXEC; /* Ok, we can drop one, so let's do so. */ - log_debug("Fixing conflicting jobs by deleting job %s/%s", d->unit->meta.id, job_type_to_string(d->type)); + log_debug("Fixing conflicting jobs by deleting job %s/%s", d->unit->id, job_type_to_string(d->type)); transaction_delete_job(m, d, true); return 0; } @@ -876,7 +891,7 @@ static int transaction_merge_jobs(Manager *m, DBusError *e) { /* We couldn't merge anything. Failure */ dbus_set_error(e, BUS_ERROR_TRANSACTION_JOBS_CONFLICTING, "Transaction contains conflicting jobs '%s' and '%s' for %s. Probably contradicting requirement dependencies configured.", - job_type_to_string(t), job_type_to_string(k->type), k->unit->meta.id); + job_type_to_string(t), job_type_to_string(k->type), k->unit->id); return r; } } @@ -891,8 +906,8 @@ static int transaction_merge_jobs(Manager *m, DBusError *e) { assert_se(job_type_merge(&t, k->type) == 0); /* If an active job is mergeable, merge it too */ - if (j->unit->meta.job) - job_type_merge(&t, j->unit->meta.job->type); /* Might fail. Which is OK */ + if (j->unit->job) + job_type_merge(&t, j->unit->job->type); /* Might fail. Which is OK */ while ((k = j->transaction_next)) { if (j->installed) { @@ -902,8 +917,8 @@ static int transaction_merge_jobs(Manager *m, DBusError *e) { transaction_merge_and_delete_job(m, j, k, t); } - if (j->unit->meta.job && !j->installed) - transaction_merge_and_delete_job(m, j, j->unit->meta.job, t); + if (j->unit->job && !j->installed) + transaction_merge_and_delete_job(m, j, j->unit->job, t); assert(!j->transaction_next); assert(!j->transaction_prev); @@ -934,7 +949,7 @@ static void transaction_drop_redundant(Manager *m) { if (!job_is_anchor(k) && (k->installed || job_type_is_redundant(k->type, unit_active_state(k->unit))) && - (!k->unit->meta.job || !job_type_is_conflicting(k->type, k->unit->meta.job->type))) + (!k->unit->job || !job_type_is_conflicting(k->type, k->unit->job->type))) continue; changes_something = true; @@ -944,7 +959,7 @@ static void transaction_drop_redundant(Manager *m) { if (changes_something) continue; - /* log_debug("Found redundant job %s/%s, dropping.", j->unit->meta.id, job_type_to_string(j->type)); */ + /* log_debug("Found redundant job %s/%s, dropping.", j->unit->id, job_type_to_string(j->type)); */ transaction_delete_job(m, j, false); again = true; break; @@ -995,12 +1010,12 @@ static int transaction_verify_order_one(Manager *m, Job *j, Job *from, unsigned * job to remove. We use the marker to find our way * back, since smart how we are we stored our way back * in there. */ - log_warning("Found ordering cycle on %s/%s", j->unit->meta.id, job_type_to_string(j->type)); + log_warning("Found ordering cycle on %s/%s", j->unit->id, job_type_to_string(j->type)); delete = NULL; for (k = from; k; k = ((k->generation == generation && k->marker != k) ? k->marker : NULL)) { - log_info("Walked on cycle path to %s/%s", k->unit->meta.id, job_type_to_string(k->type)); + log_info("Walked on cycle path to %s/%s", k->unit->id, job_type_to_string(k->type)); if (!delete && !k->installed && @@ -1018,7 +1033,7 @@ static int transaction_verify_order_one(Manager *m, Job *j, Job *from, unsigned if (delete) { - log_warning("Breaking ordering cycle by deleting job %s/%s", delete->unit->meta.id, job_type_to_string(delete->type)); + log_warning("Breaking ordering cycle by deleting job %s/%s", delete->unit->id, job_type_to_string(delete->type)); transaction_delete_unit(m, delete->unit); return -EAGAIN; } @@ -1038,7 +1053,7 @@ static int transaction_verify_order_one(Manager *m, Job *j, Job *from, unsigned /* We assume that the the dependencies are bidirectional, and * hence can ignore UNIT_AFTER */ - SET_FOREACH(u, j->unit->meta.dependencies[UNIT_BEFORE], i) { + SET_FOREACH(u, j->unit->dependencies[UNIT_BEFORE], i) { Job *o; /* Is there a job for this unit? */ @@ -1047,7 +1062,7 @@ static int transaction_verify_order_one(Manager *m, Job *j, Job *from, unsigned /* Ok, there is no job for this in the * transaction, but maybe there is already one * running? */ - if (!(o = u->meta.job)) + if (!(o = u->job)) continue; if ((r = transaction_verify_order_one(m, o, j, generation, e)) < 0) @@ -1098,13 +1113,13 @@ static void transaction_collect_garbage(Manager *m) { HASHMAP_FOREACH(j, m->transaction_jobs, i) { if (j->object_list) { /* log_debug("Keeping job %s/%s because of %s/%s", */ - /* j->unit->meta.id, job_type_to_string(j->type), */ - /* j->object_list->subject ? j->object_list->subject->unit->meta.id : "root", */ + /* j->unit->id, job_type_to_string(j->type), */ + /* j->object_list->subject ? j->object_list->subject->unit->id : "root", */ /* j->object_list->subject ? job_type_to_string(j->object_list->subject->type) : "root"); */ continue; } - /* log_debug("Garbage collecting job %s/%s", j->unit->meta.id, job_type_to_string(j->type)); */ + /* log_debug("Garbage collecting job %s/%s", j->unit->id, job_type_to_string(j->type)); */ transaction_delete_job(m, j, true); again = true; break; @@ -1128,9 +1143,9 @@ static int transaction_is_destructive(Manager *m, DBusError *e) { assert(!j->transaction_prev); assert(!j->transaction_next); - if (j->unit->meta.job && - j->unit->meta.job != j && - !job_type_is_superset(j->type, j->unit->meta.job->type)) { + if (j->unit->job && + j->unit->job != j && + !job_type_is_superset(j->type, j->unit->job->type)) { dbus_set_error(e, BUS_ERROR_TRANSACTION_IS_DESTRUCTIVE, "Transaction is destructive."); return -EEXIST; @@ -1169,20 +1184,20 @@ static void transaction_minimize_impact(Manager *m) { j->type == JOB_STOP && UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(j->unit)); changes_existing_job = - j->unit->meta.job && - job_type_is_conflicting(j->type, j->unit->meta.job->type); + j->unit->job && + job_type_is_conflicting(j->type, j->unit->job->type); if (!stops_running_service && !changes_existing_job) continue; if (stops_running_service) - log_debug("%s/%s would stop a running service.", j->unit->meta.id, job_type_to_string(j->type)); + log_debug("%s/%s would stop a running service.", j->unit->id, job_type_to_string(j->type)); if (changes_existing_job) - log_debug("%s/%s would change existing job.", j->unit->meta.id, job_type_to_string(j->type)); + log_debug("%s/%s would change existing job.", j->unit->id, job_type_to_string(j->type)); /* Ok, let's get rid of this */ - log_debug("Deleting %s/%s to minimize impact.", j->unit->meta.id, job_type_to_string(j->type)); + log_debug("Deleting %s/%s to minimize impact.", j->unit->id, job_type_to_string(j->type)); transaction_delete_job(m, j, true); again = true; @@ -1207,13 +1222,18 @@ static int transaction_apply(Manager *m, JobMode mode) { /* When isolating first kill all installed jobs which * aren't part of the new transaction */ + rescan: HASHMAP_FOREACH(j, m->jobs, i) { assert(j->installed); if (hashmap_get(m->transaction_jobs, j->unit)) continue; - job_finish_and_invalidate(j, JOB_CANCELED); + /* 'j' itself is safe to remove, but if other jobs + are invalidated recursively, our iterator may become + invalid and we need to start over. */ + if (job_finish_and_invalidate(j, JOB_CANCELED) > 0) + goto rescan; } } @@ -1231,14 +1251,14 @@ static int transaction_apply(Manager *m, JobMode mode) { while ((j = hashmap_steal_first(m->transaction_jobs))) { if (j->installed) { - /* log_debug("Skipping already installed job %s/%s as %u", j->unit->meta.id, job_type_to_string(j->type), (unsigned) j->id); */ + /* log_debug("Skipping already installed job %s/%s as %u", j->unit->id, job_type_to_string(j->type), (unsigned) j->id); */ continue; } - if (j->unit->meta.job) - job_free(j->unit->meta.job); + if (j->unit->job) + job_free(j->unit->job); - j->unit->meta.job = j; + j->unit->job = j; j->installed = true; m->n_installed_jobs ++; @@ -1252,7 +1272,7 @@ static int transaction_apply(Manager *m, JobMode mode) { job_add_to_dbus_queue(j); job_start_timer(j); - log_debug("Installed new job %s/%s as %u", j->unit->meta.id, job_type_to_string(j->type), (unsigned) j->id); + log_debug("Installed new job %s/%s as %u", j->unit->id, job_type_to_string(j->type), (unsigned) j->id); } /* As last step, kill all remaining job dependencies. */ @@ -1382,8 +1402,8 @@ static Job* transaction_add_one_job(Manager *m, JobType type, Unit *unit, bool o } } - if (unit->meta.job && unit->meta.job->type == type) - j = unit->meta.job; + if (unit->job && unit->job->type == type) + j = unit->job; else if (!(j = job_new(m, type, unit))) return NULL; @@ -1402,7 +1422,7 @@ static Job* transaction_add_one_job(Manager *m, JobType type, Unit *unit, bool o if (is_new) *is_new = true; - /* log_debug("Added job %s/%s to transaction.", unit->meta.id, job_type_to_string(type)); */ + /* log_debug("Added job %s/%s to transaction.", unit->id, job_type_to_string(type)); */ return j; } @@ -1433,8 +1453,8 @@ void manager_transaction_unlink_job(Manager *m, Job *j, bool delete_dependencies if (other && delete_dependencies) { log_debug("Deleting job %s/%s as dependency of job %s/%s", - other->unit->meta.id, job_type_to_string(other->type), - j->unit->meta.id, job_type_to_string(j->type)); + other->unit->id, job_type_to_string(other->type), + j->unit->id, job_type_to_string(j->type)); transaction_delete_job(m, other, delete_dependencies); } } @@ -1463,34 +1483,34 @@ static int transaction_add_job_and_dependencies( assert(unit); /* log_debug("Pulling in %s/%s from %s/%s", */ - /* unit->meta.id, job_type_to_string(type), */ - /* by ? by->unit->meta.id : "NA", */ + /* unit->id, job_type_to_string(type), */ + /* by ? by->unit->id : "NA", */ /* by ? job_type_to_string(by->type) : "NA"); */ - if (unit->meta.load_state != UNIT_LOADED && - unit->meta.load_state != UNIT_ERROR && - unit->meta.load_state != UNIT_MASKED) { - dbus_set_error(e, BUS_ERROR_LOAD_FAILED, "Unit %s is not loaded properly.", unit->meta.id); + if (unit->load_state != UNIT_LOADED && + unit->load_state != UNIT_ERROR && + unit->load_state != UNIT_MASKED) { + dbus_set_error(e, BUS_ERROR_LOAD_FAILED, "Unit %s is not loaded properly.", unit->id); return -EINVAL; } - if (type != JOB_STOP && unit->meta.load_state == UNIT_ERROR) { + if (type != JOB_STOP && unit->load_state == UNIT_ERROR) { dbus_set_error(e, BUS_ERROR_LOAD_FAILED, "Unit %s failed to load: %s. " "See system logs and 'systemctl status %s' for details.", - unit->meta.id, - strerror(-unit->meta.load_error), - unit->meta.id); + unit->id, + strerror(-unit->load_error), + unit->id); return -EINVAL; } - if (type != JOB_STOP && unit->meta.load_state == UNIT_MASKED) { - dbus_set_error(e, BUS_ERROR_MASKED, "Unit %s is masked.", unit->meta.id); + if (type != JOB_STOP && unit->load_state == UNIT_MASKED) { + dbus_set_error(e, BUS_ERROR_MASKED, "Unit %s is masked.", unit->id); return -EINVAL; } if (!unit_job_is_applicable(unit, type)) { - dbus_set_error(e, BUS_ERROR_JOB_TYPE_NOT_APPLICABLE, "Job type %s is not applicable for unit %s.", job_type_to_string(type), unit->meta.id); + dbus_set_error(e, BUS_ERROR_JOB_TYPE_NOT_APPLICABLE, "Job type %s is not applicable for unit %s.", job_type_to_string(type), unit->id); return -EBADR; } @@ -1512,7 +1532,7 @@ static int transaction_add_job_and_dependencies( if (unit_following_set(ret->unit, &following) > 0) { SET_FOREACH(dep, following, i) if ((r = transaction_add_job_and_dependencies(m, type, dep, ret, false, override, false, false, ignore_order, e, NULL)) < 0) { - log_warning("Cannot add dependency job for unit %s, ignoring: %s", dep->meta.id, bus_error(e, r)); + log_warning("Cannot add dependency job for unit %s, ignoring: %s", dep->id, bus_error(e, r)); if (e) dbus_error_free(e); @@ -1523,7 +1543,7 @@ static int transaction_add_job_and_dependencies( /* Finally, recursively add in all dependencies. */ if (type == JOB_START || type == JOB_RELOAD_OR_START) { - SET_FOREACH(dep, ret->unit->meta.dependencies[UNIT_REQUIRES], i) + SET_FOREACH(dep, ret->unit->dependencies[UNIT_REQUIRES], i) if ((r = transaction_add_job_and_dependencies(m, JOB_START, dep, ret, true, override, false, false, ignore_order, e, NULL)) < 0) { if (r != -EBADR) goto fail; @@ -1532,7 +1552,7 @@ static int transaction_add_job_and_dependencies( dbus_error_free(e); } - SET_FOREACH(dep, ret->unit->meta.dependencies[UNIT_BIND_TO], i) + SET_FOREACH(dep, ret->unit->dependencies[UNIT_BIND_TO], i) if ((r = transaction_add_job_and_dependencies(m, JOB_START, dep, ret, true, override, false, false, ignore_order, e, NULL)) < 0) { if (r != -EBADR) @@ -1542,23 +1562,23 @@ static int transaction_add_job_and_dependencies( dbus_error_free(e); } - SET_FOREACH(dep, ret->unit->meta.dependencies[UNIT_REQUIRES_OVERRIDABLE], i) + SET_FOREACH(dep, ret->unit->dependencies[UNIT_REQUIRES_OVERRIDABLE], i) if ((r = transaction_add_job_and_dependencies(m, JOB_START, dep, ret, !override, override, false, false, ignore_order, e, NULL)) < 0) { - log_warning("Cannot add dependency job for unit %s, ignoring: %s", dep->meta.id, bus_error(e, r)); + log_warning("Cannot add dependency job for unit %s, ignoring: %s", dep->id, bus_error(e, r)); if (e) dbus_error_free(e); } - SET_FOREACH(dep, ret->unit->meta.dependencies[UNIT_WANTS], i) + SET_FOREACH(dep, ret->unit->dependencies[UNIT_WANTS], i) if ((r = transaction_add_job_and_dependencies(m, JOB_START, dep, ret, false, false, false, false, ignore_order, e, NULL)) < 0) { - log_warning("Cannot add dependency job for unit %s, ignoring: %s", dep->meta.id, bus_error(e, r)); + log_warning("Cannot add dependency job for unit %s, ignoring: %s", dep->id, bus_error(e, r)); if (e) dbus_error_free(e); } - SET_FOREACH(dep, ret->unit->meta.dependencies[UNIT_REQUISITE], i) + SET_FOREACH(dep, ret->unit->dependencies[UNIT_REQUISITE], i) if ((r = transaction_add_job_and_dependencies(m, JOB_VERIFY_ACTIVE, dep, ret, true, override, false, false, ignore_order, e, NULL)) < 0) { if (r != -EBADR) @@ -1568,15 +1588,15 @@ static int transaction_add_job_and_dependencies( dbus_error_free(e); } - SET_FOREACH(dep, ret->unit->meta.dependencies[UNIT_REQUISITE_OVERRIDABLE], i) + SET_FOREACH(dep, ret->unit->dependencies[UNIT_REQUISITE_OVERRIDABLE], i) if ((r = transaction_add_job_and_dependencies(m, JOB_VERIFY_ACTIVE, dep, ret, !override, override, false, false, ignore_order, e, NULL)) < 0) { - log_warning("Cannot add dependency job for unit %s, ignoring: %s", dep->meta.id, bus_error(e, r)); + log_warning("Cannot add dependency job for unit %s, ignoring: %s", dep->id, bus_error(e, r)); if (e) dbus_error_free(e); } - SET_FOREACH(dep, ret->unit->meta.dependencies[UNIT_CONFLICTS], i) + SET_FOREACH(dep, ret->unit->dependencies[UNIT_CONFLICTS], i) if ((r = transaction_add_job_and_dependencies(m, JOB_STOP, dep, ret, true, override, true, false, ignore_order, e, NULL)) < 0) { if (r != -EBADR) @@ -1586,17 +1606,19 @@ static int transaction_add_job_and_dependencies( dbus_error_free(e); } - SET_FOREACH(dep, ret->unit->meta.dependencies[UNIT_CONFLICTED_BY], i) + SET_FOREACH(dep, ret->unit->dependencies[UNIT_CONFLICTED_BY], i) if ((r = transaction_add_job_and_dependencies(m, JOB_STOP, dep, ret, false, override, false, false, ignore_order, e, NULL)) < 0) { - log_warning("Cannot add dependency job for unit %s, ignoring: %s", dep->meta.id, bus_error(e, r)); + log_warning("Cannot add dependency job for unit %s, ignoring: %s", dep->id, bus_error(e, r)); if (e) dbus_error_free(e); } - } else if (type == JOB_STOP || type == JOB_RESTART || type == JOB_TRY_RESTART) { + } + + if (type == JOB_STOP || type == JOB_RESTART || type == JOB_TRY_RESTART) { - SET_FOREACH(dep, ret->unit->meta.dependencies[UNIT_REQUIRED_BY], i) + SET_FOREACH(dep, ret->unit->dependencies[UNIT_REQUIRED_BY], i) if ((r = transaction_add_job_and_dependencies(m, type, dep, ret, true, override, false, false, ignore_order, e, NULL)) < 0) { if (r != -EBADR) @@ -1606,7 +1628,7 @@ static int transaction_add_job_and_dependencies( dbus_error_free(e); } - SET_FOREACH(dep, ret->unit->meta.dependencies[UNIT_BOUND_BY], i) + SET_FOREACH(dep, ret->unit->dependencies[UNIT_BOUND_BY], i) if ((r = transaction_add_job_and_dependencies(m, type, dep, ret, true, override, false, false, ignore_order, e, NULL)) < 0) { if (r != -EBADR) @@ -1617,6 +1639,20 @@ static int transaction_add_job_and_dependencies( } } + if (type == JOB_RELOAD || type == JOB_RELOAD_OR_START) { + + SET_FOREACH(dep, ret->unit->dependencies[UNIT_PROPAGATE_RELOAD_TO], i) { + r = transaction_add_job_and_dependencies(m, JOB_RELOAD, dep, ret, false, override, false, false, ignore_order, e, NULL); + + if (r < 0) { + log_warning("Cannot add dependency reload job for unit %s, ignoring: %s", dep->id, bus_error(e, r)); + + if (e) + dbus_error_free(e); + } + } + } + /* JOB_VERIFY_STARTED, JOB_RELOAD require no dependency handling */ } @@ -1640,14 +1676,14 @@ static int transaction_add_isolate_jobs(Manager *m) { HASHMAP_FOREACH_KEY(u, k, m->units, i) { /* ignore aliases */ - if (u->meta.id != k) + if (u->id != k) continue; - if (u->meta.ignore_on_isolate) + if (u->ignore_on_isolate) continue; /* No need to stop inactive jobs */ - if (UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(u)) && !u->meta.job) + if (UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(u)) && !u->job) continue; /* Is there already something listed for this? */ @@ -1655,7 +1691,7 @@ static int transaction_add_isolate_jobs(Manager *m) { continue; if ((r = transaction_add_job_and_dependencies(m, JOB_STOP, u, NULL, true, false, false, false, false, NULL, NULL)) < 0) - log_warning("Cannot add isolate job for unit %s, ignoring: %s", u->meta.id, strerror(-r)); + log_warning("Cannot add isolate job for unit %s, ignoring: %s", u->id, strerror(-r)); } return 0; @@ -1675,12 +1711,12 @@ int manager_add_job(Manager *m, JobType type, Unit *unit, JobMode mode, bool ove return -EINVAL; } - if (mode == JOB_ISOLATE && !unit->meta.allow_isolate) { + if (mode == JOB_ISOLATE && !unit->allow_isolate) { dbus_set_error(e, BUS_ERROR_NO_ISOLATION, "Operation refused, unit may not be isolated."); return -EPERM; } - log_debug("Trying to enqueue job %s/%s/%s", unit->meta.id, job_type_to_string(type), job_mode_to_string(mode)); + log_debug("Trying to enqueue job %s/%s/%s", unit->id, job_type_to_string(type), job_mode_to_string(mode)); if ((r = transaction_add_job_and_dependencies(m, type, unit, NULL, true, override, false, mode == JOB_IGNORE_DEPENDENCIES || mode == JOB_IGNORE_REQUIREMENTS, @@ -1698,7 +1734,7 @@ int manager_add_job(Manager *m, JobType type, Unit *unit, JobMode mode, bool ove if ((r = transaction_activate(m, mode, e)) < 0) return r; - log_debug("Enqueued job %s/%s as %u", unit->meta.id, job_type_to_string(type), (unsigned) ret->id); + log_debug("Enqueued job %s/%s as %u", unit->id, job_type_to_string(type), (unsigned) ret->id); if (_ret) *_ret = ret; @@ -1735,7 +1771,7 @@ Unit *manager_get_unit(Manager *m, const char *name) { } unsigned manager_dispatch_load_queue(Manager *m) { - Meta *meta; + Unit *u; unsigned n = 0; assert(m); @@ -1749,10 +1785,10 @@ unsigned manager_dispatch_load_queue(Manager *m) { /* Dispatches the load queue. Takes a unit from the queue and * tries to load its data until the queue is empty */ - while ((meta = m->load_queue)) { - assert(meta->in_load_queue); + while ((u = m->load_queue)) { + assert(u->in_load_queue); - unit_load((Unit*) meta); + unit_load(u); n++; } @@ -1762,6 +1798,7 @@ unsigned manager_dispatch_load_queue(Manager *m) { int manager_load_unit_prepare(Manager *m, const char *name, const char *path, DBusError *e, Unit **_ret) { Unit *ret; + UnitType t; int r; assert(m); @@ -1778,24 +1815,30 @@ int manager_load_unit_prepare(Manager *m, const char *name, const char *path, DB if (!name) name = file_name_from_path(path); - if (!unit_name_is_valid(name, false)) { + t = unit_name_to_type(name); + + if (t == _UNIT_TYPE_INVALID || !unit_name_is_valid_no_type(name, false)) { dbus_set_error(e, BUS_ERROR_INVALID_NAME, "Unit name %s is not valid.", name); return -EINVAL; } - if ((ret = manager_get_unit(m, name))) { + ret = manager_get_unit(m, name); + if (ret) { *_ret = ret; return 1; } - if (!(ret = unit_new(m))) + ret = unit_new(m, unit_vtable[t]->object_size); + if (!ret) return -ENOMEM; - if (path) - if (!(ret->meta.fragment_path = strdup(path))) { + if (path) { + ret->fragment_path = strdup(path); + if (!ret->fragment_path) { unit_free(ret); return -ENOMEM; } + } if ((r = unit_add_name(ret, name)) < 0) { unit_free(ret); @@ -1851,7 +1894,7 @@ void manager_dump_units(Manager *s, FILE *f, const char *prefix) { assert(f); HASHMAP_FOREACH_KEY(u, t, s->units, i) - if (u->meta.id == t) + if (u->id == t) unit_dump(u, f, prefix); } @@ -1889,7 +1932,7 @@ unsigned manager_dispatch_run_queue(Manager *m) { unsigned manager_dispatch_dbus_queue(Manager *m) { Job *j; - Meta *meta; + Unit *u; unsigned n = 0; assert(m); @@ -1899,10 +1942,10 @@ unsigned manager_dispatch_dbus_queue(Manager *m) { m->dispatching_dbus_queue = true; - while ((meta = m->dbus_unit_queue)) { - assert(meta->in_dbus_queue); + while ((u = m->dbus_unit_queue)) { + assert(u->in_dbus_queue); - bus_unit_send_change_signal((Unit*) meta); + bus_unit_send_change_signal(u); n++; } @@ -1976,7 +2019,7 @@ static int manager_process_notify_fd(Manager *m) { if (!(tags = strv_split(buf, "\n\r"))) return -ENOMEM; - log_debug("Got notification message for unit %s", u->meta.id); + log_debug("Got notification message for unit %s", u->id); if (UNIT_VTABLE(u)->notify_message) UNIT_VTABLE(u)->notify_message(u, ucred->pid, tags); @@ -2017,7 +2060,7 @@ static int manager_dispatch_sigchld(Manager *m) { if (si.si_code == CLD_EXITED || si.si_code == CLD_KILLED || si.si_code == CLD_DUMPED) { char *name = NULL; - get_process_name(si.si_pid, &name); + get_process_comm(si.si_pid, &name); log_debug("Got SIGCHLD for process %lu (%s)", (unsigned long) si.si_pid, strna(name)); free(name); } @@ -2055,7 +2098,7 @@ static int manager_dispatch_sigchld(Manager *m) { if (!u) continue; - log_debug("Child %lu belongs to %s", (long unsigned) si.si_pid, u->meta.id); + log_debug("Child %lu belongs to %s", (long unsigned) si.si_pid, u->id); hashmap_remove(m->watch_pids, LONG_TO_PTR(si.si_pid)); UNIT_VTABLE(u)->sigchld_event(u, si.si_pid, si.si_code, si.si_status); @@ -2102,14 +2145,14 @@ static int manager_process_signal_fd(Manager *m) { if (sfsi.ssi_pid > 0) { char *p = NULL; - get_process_name(sfsi.ssi_pid, &p); + get_process_comm(sfsi.ssi_pid, &p); log_debug("Received SIG%s from PID %lu (%s).", - strna(signal_to_string(sfsi.ssi_signo)), + signal_to_string(sfsi.ssi_signo), (unsigned long) sfsi.ssi_pid, strna(p)); free(p); } else - log_debug("Received SIG%s.", strna(signal_to_string(sfsi.ssi_signo))); + log_debug("Received SIG%s.", signal_to_string(sfsi.ssi_signo)); switch (sfsi.ssi_signo) { @@ -2227,8 +2270,9 @@ static int manager_process_signal_fd(Manager *m) { if ((int) sfsi.ssi_signo >= SIGRTMIN+0 && (int) sfsi.ssi_signo < SIGRTMIN+(int) ELEMENTSOF(target_table)) { - manager_start_target(m, target_table[sfsi.ssi_signo - SIGRTMIN], - (sfsi.ssi_signo == 1 || sfsi.ssi_signo == 2) ? JOB_ISOLATE : JOB_REPLACE); + int idx = (int) sfsi.ssi_signo - SIGRTMIN; + manager_start_target(m, target_table[idx], + (idx == 1 || idx == 2) ? JOB_ISOLATE : JOB_REPLACE); break; } @@ -2242,12 +2286,12 @@ static int manager_process_signal_fd(Manager *m) { case 20: log_debug("Enabling showing of status."); - m->show_status = true; + manager_set_show_status(m, true); break; case 21: log_debug("Disabling showing of status."); - m->show_status = false; + manager_set_show_status(m, false); break; case 22: @@ -2260,6 +2304,11 @@ static int manager_process_signal_fd(Manager *m) { log_notice("Setting log level to info."); break; + case 26: + log_set_target(LOG_TARGET_JOURNAL_OR_KMSG); + log_notice("Setting log target to journal-or-kmsg."); + break; + case 27: log_set_target(LOG_TARGET_CONSOLE); log_notice("Setting log target to console."); @@ -2276,7 +2325,7 @@ static int manager_process_signal_fd(Manager *m) { break; default: - log_warning("Got unhandled signal <%s>.", strna(signal_to_string(sfsi.ssi_signo))); + log_warning("Got unhandled signal <%s>.", signal_to_string(sfsi.ssi_signo)); } } } @@ -2513,10 +2562,10 @@ void manager_send_unit_audit(Manager *m, Unit *u, int type, bool success) { if (m->running_as != MANAGER_SYSTEM) return; - if (u->meta.type != UNIT_SERVICE) + if (u->type != UNIT_SERVICE) return; - if (!(p = unit_name_to_prefix_and_instance(u->meta.id))) { + if (!(p = unit_name_to_prefix_and_instance(u->id))) { log_error("Failed to allocate unit name for audit message: %s", strerror(ENOMEM)); return; } @@ -2554,9 +2603,9 @@ void manager_send_unit_plymouth(Manager *m, Unit *u) { if (m->running_as != MANAGER_SYSTEM) return; - if (u->meta.type != UNIT_SERVICE && - u->meta.type != UNIT_MOUNT && - u->meta.type != UNIT_SWAP) + if (u->type != UNIT_SERVICE && + u->type != UNIT_MOUNT && + u->type != UNIT_SWAP) return; /* We set SOCK_NONBLOCK here so that we rather drop the @@ -2582,7 +2631,7 @@ void manager_send_unit_plymouth(Manager *m, Unit *u) { goto finish; } - if (asprintf(&message, "U\002%c%s%n", (int) (strlen(u->meta.id) + 1), u->meta.id, &n) < 0) { + if (asprintf(&message, "U\002%c%s%n", (int) (strlen(u->id) + 1), u->id, &n) < 0) { log_error("Out of memory"); goto finish; } @@ -2702,14 +2751,14 @@ int manager_serialize(Manager *m, FILE *f, FDSet *fds) { fputc('\n', f); HASHMAP_FOREACH_KEY(u, t, m->units, i) { - if (u->meta.id != t) + if (u->id != t) continue; if (!unit_can_serialize(u)) continue; /* Start marker */ - fputs(u->meta.id, f); + fputs(u->id, f); fputc('\n', f); if ((r = unit_serialize(u, f, fds)) < 0) { @@ -2894,12 +2943,13 @@ bool manager_is_booting_or_shutting_down(Manager *m) { assert(m); /* Is the initial job still around? */ - if (manager_get_job(m, 1)) + if (manager_get_job(m, m->default_unit_job_id)) return true; /* Is there a job for the shutdown target? */ - if (((u = manager_get_unit(m, SPECIAL_SHUTDOWN_TARGET)))) - return !!u->meta.job; + u = manager_get_unit(m, SPECIAL_SHUTDOWN_TARGET); + if (u) + return !!u->job; return false; } @@ -2984,6 +3034,7 @@ void manager_run_generators(Manager *m) { DIR *d = NULL; const char *generator_path; const char *argv[3]; + mode_t u; assert(m); @@ -3026,7 +3077,9 @@ void manager_run_generators(Manager *m) { argv[1] = m->generator_unit_path; argv[2] = NULL; + u = umask(0022); execute_directory(generator_path, d, (char**) argv); + umask(u); if (rmdir(m->generator_unit_path) >= 0) { /* Uh? we were able to remove this dir? I guess that @@ -3064,7 +3117,7 @@ void manager_undo_generators(Manager *m) { return; strv_remove(m->lookup_paths.unit_path, m->generator_unit_path); - rm_rf(m->generator_unit_path, false, true); + rm_rf(m->generator_unit_path, false, true, false); free(m->generator_unit_path); m->generator_unit_path = NULL; @@ -3084,7 +3137,7 @@ int manager_set_default_controllers(Manager *m, char **controllers) { return 0; } -void manager_recheck_syslog(Manager *m) { +void manager_recheck_journal(Manager *m) { Unit *u; assert(m); @@ -3092,37 +3145,50 @@ void manager_recheck_syslog(Manager *m) { if (m->running_as != MANAGER_SYSTEM) return; - if ((u = manager_get_unit(m, SPECIAL_SYSLOG_SOCKET))) { - SocketState state; + u = manager_get_unit(m, SPECIAL_JOURNALD_SOCKET); + if (u && SOCKET(u)->state != SOCKET_RUNNING) { + log_close_journal(); + return; + } - state = SOCKET(u)->state; + u = manager_get_unit(m, SPECIAL_JOURNALD_SERVICE); + if (u && SERVICE(u)->state != SERVICE_RUNNING) { + log_close_journal(); + return; + } - if (state != SOCKET_DEAD && - state != SOCKET_FAILED && - state != SOCKET_RUNNING) { + /* Hmm, OK, so the socket is fully up and the service is up + * too, then let's make use of the thing. */ + log_open(); +} - /* Hmm, the socket is not set up, or is still - * listening, let's better not try to use - * it. Note that we have no problem if the - * socket is completely down, since there - * might be a foreign /dev/log socket around - * and we want to make use of that. - */ +void manager_set_show_status(Manager *m, bool b) { + assert(m); - log_close_syslog(); - return; - } - } + if (m->running_as != MANAGER_SYSTEM) + return; - if ((u = manager_get_unit(m, SPECIAL_SYSLOG_TARGET))) - if (TARGET(u)->state != TARGET_ACTIVE) { - log_close_syslog(); - return; - } + m->show_status = b; - /* Hmm, OK, so the socket is either fully up, or fully down, - * and the target is up, then let's make use of the socket */ - log_open(); + if (b) + touch("/run/systemd/show-status"); + else + unlink("/run/systemd/show-status"); +} + +bool manager_get_show_status(Manager *m) { + assert(m); + + if (m->running_as != MANAGER_SYSTEM) + return false; + + if (m->show_status) + return true; + + /* If Plymouth is running make sure we show the status, so + * that there's something nice to see when people press Esc */ + + return plymouth_running(); } static const char* const manager_running_as_table[_MANAGER_RUNNING_AS_MAX] = { diff --git a/src/manager.h b/src/manager.h index 22730d2176..a9d08f0a25 100644 --- a/src/manager.h +++ b/src/manager.h @@ -74,7 +74,7 @@ struct Watch { int fd; WatchType type; union { - union Unit *unit; + struct Unit *unit; struct Job *job; DBusWatch *bus_watch; DBusTimeout *bus_timeout; @@ -102,10 +102,10 @@ struct Manager { /* To make it easy to iterate through the units of a specific * type we maintain a per type linked list */ - LIST_HEAD(Meta, units_by_type[_UNIT_TYPE_MAX]); + LIST_HEAD(Unit, units_by_type[_UNIT_TYPE_MAX]); /* Units that need to be loaded */ - LIST_HEAD(Meta, load_queue); /* this is actually more a stack than a queue, but uh. */ + LIST_HEAD(Unit, load_queue); /* this is actually more a stack than a queue, but uh. */ /* Jobs that need to be run */ LIST_HEAD(Job, run_queue); /* more a stack than a queue, too */ @@ -114,14 +114,14 @@ struct Manager { * D-Bus. When something about a job changes it is added here * if it is not in there yet. This allows easy coalescing of * D-Bus change signals. */ - LIST_HEAD(Meta, dbus_unit_queue); + LIST_HEAD(Unit, dbus_unit_queue); LIST_HEAD(Job, dbus_job_queue); /* Units to remove */ - LIST_HEAD(Meta, cleanup_queue); + LIST_HEAD(Unit, cleanup_queue); /* Units to check when doing GC */ - LIST_HEAD(Meta, gc_queue); + LIST_HEAD(Unit, gc_queue); /* Jobs to be added */ Hashmap *transaction_jobs; /* Unit object => Job object list 1:1 */ @@ -179,9 +179,11 @@ struct Manager { Hashmap *watch_bus; /* D-Bus names => Unit object n:1 */ int32_t name_data_slot; + int32_t conn_data_slot; int32_t subscribed_data_slot; uint32_t current_job_id; + uint32_t default_unit_job_id; /* Data specific to the Automount subsystem */ int dev_autofs_fd; @@ -288,7 +290,10 @@ void manager_check_finished(Manager *m); void manager_run_generators(Manager *m); void manager_undo_generators(Manager *m); -void manager_recheck_syslog(Manager *m); +void manager_recheck_journal(Manager *m); + +void manager_set_show_status(Manager *m, bool b); +bool manager_get_show_status(Manager *m); const char *manager_running_as_to_string(ManagerRunningAs i); ManagerRunningAs manager_running_as_from_string(const char *s); diff --git a/src/missing.h b/src/missing.h index a44390074f..213ef2f6a6 100644 --- a/src/missing.h +++ b/src/missing.h @@ -176,4 +176,8 @@ struct btrfs_ioctl_vol_args { #define MS_PRIVATE (1 << 18) #endif +static inline pid_t gettid(void) { + return (pid_t) syscall(SYS_gettid); +} + #endif diff --git a/src/modules-load.c b/src/modules-load.c index d76defa515..043e909d80 100644 --- a/src/modules-load.c +++ b/src/modules-load.c @@ -26,32 +26,46 @@ #include <sys/stat.h> #include <limits.h> #include <dirent.h> +#include <libkmod.h> #include "log.h" #include "util.h" #include "strv.h" +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +static void systemd_kmod_log(void *data, int priority, const char *file, int line, + const char *fn, const char *format, va_list args) +{ + log_meta(priority, file, line, fn, format, args); +} +#pragma GCC diagnostic pop + int main(int argc, char *argv[]) { int r = EXIT_FAILURE; - char **arguments = NULL; - unsigned n_arguments = 0, n_allocated = 0; char **files, **fn; + struct kmod_ctx *ctx; + const int probe_flags = KMOD_PROBE_APPLY_BLACKLIST|KMOD_PROBE_IGNORE_LOADED; if (argc > 1) { log_error("This program takes no argument."); return EXIT_FAILURE; } - log_set_target(LOG_TARGET_SYSLOG_OR_KMSG); + log_set_target(LOG_TARGET_AUTO); log_parse_environment(); log_open(); - if (!(arguments = strv_new("/sbin/modprobe", "-sab", "--", NULL))) { - log_error("Failed to allocate string array"); + umask(0022); + + if (!(ctx = kmod_new(NULL, NULL))) { + log_error("Failed to allocate memory for kmod."); goto finish; } - n_arguments = n_allocated = 3; + kmod_load_resources(ctx); + + kmod_set_log_fn(ctx, systemd_kmod_log, NULL); if (conf_files_list(&files, ".conf", "/run/modules-load.d", @@ -75,14 +89,15 @@ int main(int argc, char *argv[]) { continue; log_error("Failed to open %s: %m", *fn); - free(fn); r = EXIT_FAILURE; continue; } log_debug("apply: %s\n", *fn); for (;;) { - char line[LINE_MAX], *l, *t; + char line[LINE_MAX], *l; + struct kmod_list *itr, *modlist = NULL; + int err; if (!(fgets(line, sizeof(line), f))) break; @@ -91,51 +106,45 @@ int main(int argc, char *argv[]) { if (*l == '#' || *l == 0) continue; - if (!(t = strdup(l))) { - log_error("Failed to allocate module name."); + err = kmod_module_new_from_lookup(ctx, l, &modlist); + if (err < 0) { + log_error("Failed to lookup alias '%s'", l); + r = EXIT_FAILURE; continue; } - if (n_arguments >= n_allocated) { - char **a; - unsigned m; - - m = MAX(16U, n_arguments*2); - - if (!(a = realloc(arguments, sizeof(char*) * (m+1)))) { - log_error("Failed to increase module array size."); - free(t); + kmod_list_foreach(itr, modlist) { + struct kmod_module *mod = kmod_module_get_module(itr); + err = kmod_module_probe_insert_module(mod, probe_flags, + NULL, NULL, NULL, NULL); + + if (err == 0) + log_info("Inserted module '%s'", kmod_module_get_name(mod)); + else if (err == KMOD_PROBE_APPLY_BLACKLIST) + log_info("Module '%s' is blacklisted", kmod_module_get_name(mod)); + else { + log_error("Failed to insert '%s': %s", kmod_module_get_name(mod), + strerror(-err)); r = EXIT_FAILURE; - continue; } - arguments = a; - n_allocated = m; + kmod_module_unref(mod); } - arguments[n_arguments++] = t; + kmod_module_unref_list(modlist); } if (ferror(f)) { - r = EXIT_FAILURE; log_error("Failed to read from file: %m"); + r = EXIT_FAILURE; } fclose(f); } - strv_free(files); finish: - - if (n_arguments > 3) { - arguments[n_arguments] = NULL; - execv("/sbin/modprobe", arguments); - - log_error("Failed to execute /sbin/modprobe: %m"); - r = EXIT_FAILURE; - } - - strv_free(arguments); + strv_free(files); + kmod_unref(ctx); return r; } diff --git a/src/mount-setup.c b/src/mount-setup.c index f236ab741a..7c14ea8e55 100644 --- a/src/mount-setup.c +++ b/src/mount-setup.c @@ -34,6 +34,8 @@ #include "macro.h" #include "util.h" #include "label.h" +#include "set.h" +#include "strv.h" #ifndef TTY_GID #define TTY_GID 5 @@ -48,6 +50,10 @@ typedef struct MountPoint { bool fatal; } MountPoint; +/* The first three entries we might need before SELinux is up. The + * other ones we can delay until SELinux is loaded. */ +#define N_EARLY_MOUNT 3 + static const MountPoint mount_table[] = { { "proc", "/proc", "proc", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, true }, { "sysfs", "/sys", "sysfs", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, true }, @@ -91,15 +97,16 @@ bool mount_point_ignore(const char *path) { return false; } -static int mount_one(const MountPoint *p) { +static int mount_one(const MountPoint *p, bool relabel) { int r; assert(p); /* Relabel first, just in case */ - label_fix(p->where, true); + if (relabel) + label_fix(p->where, true); - if ((r = path_is_mount_point(p->where)) < 0) + if ((r = path_is_mount_point(p->where, true)) < 0) return r; if (r > 0) @@ -125,30 +132,58 @@ static int mount_one(const MountPoint *p) { } /* Relabel again, since we now mounted something fresh here */ - label_fix(p->where, false); + if (relabel) + label_fix(p->where, false); - return 0; + return 1; +} + +int mount_setup_early(void) { + unsigned i; + int r = 0; + + assert_cc(N_EARLY_MOUNT <= ELEMENTSOF(mount_table)); + + /* Do a minimal mount of /proc and friends to enable the most + * basic stuff, such as SELinux */ + for (i = 0; i < N_EARLY_MOUNT; i ++) { + int j; + + j = mount_one(mount_table + i, false); + if (r == 0) + r = j; + } + + return r; } -static int mount_cgroup_controllers(void) { +int mount_cgroup_controllers(char ***join_controllers) { int r; FILE *f; char buf[LINE_MAX]; + Set *controllers; /* Mount all available cgroup controllers that are built into the kernel. */ - if (!(f = fopen("/proc/cgroups", "re"))) { + f = fopen("/proc/cgroups", "re"); + if (!f) { log_error("Failed to enumerate cgroup controllers: %m"); return 0; } + controllers = set_new(string_hash_func, string_compare_func); + if (!controllers) { + r = -ENOMEM; + log_error("Failed to allocate controller set."); + goto finish; + } + /* Ignore the header line */ (void) fgets(buf, sizeof(buf), f); for (;;) { - MountPoint p; - char *controller, *where; - int enabled = false; + char *controller; + int enabled = 0; if (fscanf(f, "%ms %*i %*i %i", &controller, &enabled) != 2) { @@ -165,8 +200,66 @@ static int mount_cgroup_controllers(void) { continue; } - if (asprintf(&where, "/sys/fs/cgroup/%s", controller) < 0) { + r = set_put(controllers, controller); + if (r < 0) { + log_error("Failed to add controller to set."); free(controller); + goto finish; + } + } + + for (;;) { + MountPoint p; + char *controller, *where, *options; + char ***k = NULL; + + controller = set_steal_first(controllers); + if (!controller) + break; + + if (join_controllers) + for (k = join_controllers; *k; k++) + if (strv_find(*k, controller)) + break; + + if (k && *k) { + char **i, **j; + + for (i = *k, j = *k; *i; i++) { + + if (!streq(*i, controller)) { + char *t; + + t = set_remove(controllers, *i); + if (!t) { + free(*i); + continue; + } + free(t); + } + + *(j++) = *i; + } + + *j = NULL; + + options = strv_join(*k, ","); + if (!options) { + log_error("Failed to join options"); + free(controller); + r = -ENOMEM; + goto finish; + } + + } else { + options = controller; + controller = NULL; + } + + where = strappend("/sys/fs/cgroup/", options); + if (!where) { + log_error("Failed to build path"); + free(options); r = -ENOMEM; goto finish; } @@ -175,21 +268,53 @@ static int mount_cgroup_controllers(void) { p.what = "cgroup"; p.where = where; p.type = "cgroup"; - p.options = controller; + p.options = options; p.flags = MS_NOSUID|MS_NOEXEC|MS_NODEV; p.fatal = false; - r = mount_one(&p); + r = mount_one(&p, true); free(controller); free(where); - if (r < 0) + if (r < 0) { + free(options); goto finish; + } + + if (r > 0 && k && *k) { + char **i; + + for (i = *k; *i; i++) { + char *t; + + t = strappend("/sys/fs/cgroup/", *i); + if (!t) { + log_error("Failed to build path"); + r = -ENOMEM; + free(options); + goto finish; + } + + r = symlink(options, t); + free(t); + + if (r < 0 && errno != EEXIST) { + log_error("Failed to create symlink: %m"); + r = -errno; + free(options); + goto finish; + } + } + } + + free(options); } r = 0; finish: + set_free_free(controllers); + fclose(f); return r; @@ -219,37 +344,66 @@ static int nftw_cb( struct FTW *ftwbuf) { /* No need to label /dev twice in a row... */ - if (ftwbuf->level == 0) - return 0; + if (_unlikely_(ftwbuf->level == 0)) + return FTW_CONTINUE; label_fix(fpath, true); - return 0; + + /* /run/initramfs is static data and big, no need to + * dynamically relabel its contents at boot... */ + if (_unlikely_(ftwbuf->level == 1 && + tflag == FTW_D && + streq(fpath, "/run/initramfs"))) + return FTW_SKIP_SUBTREE; + + return FTW_CONTINUE; }; -int mount_setup(void) { +int mount_setup(bool loaded_policy) { - const char symlinks[] = + static const char symlinks[] = "/proc/kcore\0" "/dev/core\0" "/proc/self/fd\0" "/dev/fd\0" "/proc/self/fd/0\0" "/dev/stdin\0" "/proc/self/fd/1\0" "/dev/stdout\0" "/proc/self/fd/2\0" "/dev/stderr\0"; + static const char relabel[] = + "/run/initramfs/root-fsck\0" + "/run/initramfs/shutdown\0"; + int r; unsigned i; const char *j, *k; - for (i = 0; i < ELEMENTSOF(mount_table); i ++) - if ((r = mount_one(mount_table+i)) < 0) + for (i = 0; i < ELEMENTSOF(mount_table); i ++) { + r = mount_one(mount_table + i, true); + + if (r < 0) return r; + } /* Nodes in devtmpfs and /run need to be manually updated for * the appropriate labels, after mounting. The other virtual * API file systems like /sys and /proc do not need that, they * use the same label for all their files. */ - if (unlink("/dev/.systemd-relabel-run-dev") >= 0) { - nftw("/dev", nftw_cb, 64, FTW_MOUNT|FTW_PHYS); - nftw("/run", nftw_cb, 64, FTW_MOUNT|FTW_PHYS); + if (loaded_policy) { + usec_t before_relabel, after_relabel; + char timespan[FORMAT_TIMESPAN_MAX]; + + before_relabel = now(CLOCK_MONOTONIC); + + nftw("/dev", nftw_cb, 64, FTW_MOUNT|FTW_PHYS|FTW_ACTIONRETVAL); + nftw("/run", nftw_cb, 64, FTW_MOUNT|FTW_PHYS|FTW_ACTIONRETVAL); + + /* Explicitly relabel these */ + NULSTR_FOREACH(j, relabel) + label_fix(j, true); + + after_relabel = now(CLOCK_MONOTONIC); + + log_info("Relabelled /dev and /run in %s.", + format_timespan(timespan, sizeof(timespan), after_relabel - before_relabel)); } /* Create a few default symlinks, which are normally created @@ -259,8 +413,8 @@ int mount_setup(void) { symlink_and_label(j, k); /* Create a few directories we always want around */ - mkdir("/run/systemd", 0755); - mkdir("/run/systemd/system", 0755); + label_mkdir("/run/systemd", 0755); + label_mkdir("/run/systemd/system", 0755); - return mount_cgroup_controllers(); + return 0; } diff --git a/src/mount-setup.h b/src/mount-setup.h index 150ec408d2..c1a27ba6bd 100644 --- a/src/mount-setup.h +++ b/src/mount-setup.h @@ -24,7 +24,11 @@ #include <stdbool.h> -int mount_setup(void); +int mount_setup_early(void); + +int mount_setup(bool loaded_policy); + +int mount_cgroup_controllers(char ***join_controllers); bool mount_point_is_api(const char *path); bool mount_point_ignore(const char *path); diff --git a/src/mount.c b/src/mount.c index d26d45f038..0ae964b648 100644 --- a/src/mount.c +++ b/src/mount.c @@ -59,7 +59,7 @@ static void mount_init(Unit *u) { Mount *m = MOUNT(u); assert(u); - assert(u->meta.load_state == UNIT_STUB); + assert(u->load_state == UNIT_STUB); m->timeout_usec = DEFAULT_TIMEOUT_USEC; m->directory_mode = 0755; @@ -68,8 +68,10 @@ static void mount_init(Unit *u) { /* The stdio/kmsg bridge socket is on /, in order to avoid a * dep loop, don't use kmsg logging for -.mount */ - if (!unit_has_name(u, "-.mount")) - m->exec_context.std_output = EXEC_OUTPUT_KMSG; + if (!unit_has_name(u, "-.mount")) { + m->exec_context.std_output = u->manager->default_std_output; + m->exec_context.std_error = u->manager->default_std_error; + } /* We need to make sure that /bin/mount is always called in * the same process group as us, so that the autofs kernel @@ -81,7 +83,7 @@ static void mount_init(Unit *u) { m->control_command_id = _MOUNT_EXEC_COMMAND_INVALID; - m->meta.ignore_on_isolate = true; + UNIT(m)->ignore_on_isolate = true; } static void mount_unwatch_control_pid(Mount *m) { @@ -106,21 +108,12 @@ static void mount_parameters_done(MountParameters *p) { static void mount_done(Unit *u) { Mount *m = MOUNT(u); - Meta *other; assert(m); free(m->where); m->where = NULL; - /* Try to detach us from the automount unit if there is any */ - LIST_FOREACH(units_by_type, other, m->meta.manager->units_by_type[UNIT_AUTOMOUNT]) { - Automount *a = (Automount*) other; - - if (a->mount == m) - a->mount = NULL; - } - mount_parameters_done(&m->parameters_etc_fstab); mount_parameters_done(&m->parameters_proc_self_mountinfo); mount_parameters_done(&m->parameters_fragment); @@ -155,7 +148,7 @@ static MountParameters* get_mount_parameters(Mount *m) { } static int mount_add_mount_links(Mount *m) { - Meta *other; + Unit *other; int r; MountParameters *pm; @@ -166,14 +159,14 @@ static int mount_add_mount_links(Mount *m) { /* Adds in links to other mount points that might lie below or * above us in the hierarchy */ - LIST_FOREACH(units_by_type, other, m->meta.manager->units_by_type[UNIT_MOUNT]) { - Mount *n = (Mount*) other; + LIST_FOREACH(units_by_type, other, UNIT(m)->manager->units_by_type[UNIT_MOUNT]) { + Mount *n = MOUNT(other); MountParameters *pn; if (n == m) continue; - if (n->meta.load_state != UNIT_LOADED) + if (UNIT(n)->load_state != UNIT_LOADED) continue; pn = get_mount_parameters_configured(n); @@ -218,52 +211,52 @@ static int mount_add_mount_links(Mount *m) { } static int mount_add_swap_links(Mount *m) { - Meta *other; + Unit *other; int r; assert(m); - LIST_FOREACH(units_by_type, other, m->meta.manager->units_by_type[UNIT_SWAP]) - if ((r = swap_add_one_mount_link((Swap*) other, m)) < 0) + LIST_FOREACH(units_by_type, other, UNIT(m)->manager->units_by_type[UNIT_SWAP]) + if ((r = swap_add_one_mount_link(SWAP(other), m)) < 0) return r; return 0; } static int mount_add_path_links(Mount *m) { - Meta *other; + Unit *other; int r; assert(m); - LIST_FOREACH(units_by_type, other, m->meta.manager->units_by_type[UNIT_PATH]) - if ((r = path_add_one_mount_link((Path*) other, m)) < 0) + LIST_FOREACH(units_by_type, other, UNIT(m)->manager->units_by_type[UNIT_PATH]) + if ((r = path_add_one_mount_link(PATH(other), m)) < 0) return r; return 0; } static int mount_add_automount_links(Mount *m) { - Meta *other; + Unit *other; int r; assert(m); - LIST_FOREACH(units_by_type, other, m->meta.manager->units_by_type[UNIT_AUTOMOUNT]) - if ((r = automount_add_one_mount_link((Automount*) other, m)) < 0) + LIST_FOREACH(units_by_type, other, UNIT(m)->manager->units_by_type[UNIT_AUTOMOUNT]) + if ((r = automount_add_one_mount_link(AUTOMOUNT(other), m)) < 0) return r; return 0; } static int mount_add_socket_links(Mount *m) { - Meta *other; + Unit *other; int r; assert(m); - LIST_FOREACH(units_by_type, other, m->meta.manager->units_by_type[UNIT_SOCKET]) - if ((r = socket_add_one_mount_link((Socket*) other, m)) < 0) + LIST_FOREACH(units_by_type, other, UNIT(m)->manager->units_by_type[UNIT_SOCKET]) + if ((r = socket_add_one_mount_link(SOCKET(other), m)) < 0) return r; return 0; @@ -320,11 +313,14 @@ static bool needs_quota(MountParameters *p) { return false; return mount_test_option(p->options, "usrquota") || - mount_test_option(p->options, "grpquota"); + mount_test_option(p->options, "grpquota") || + mount_test_option(p->options, "quota") || + mount_test_option(p->options, "usrjquota") || + mount_test_option(p->options, "grpjquota"); } static int mount_add_fstab_links(Mount *m) { - const char *target, *after = NULL; + const char *target, *after, *tu_wants = NULL; MountParameters *p; Unit *tu; int r; @@ -332,7 +328,7 @@ static int mount_add_fstab_links(Mount *m) { assert(m); - if (m->meta.manager->running_as != MANAGER_SYSTEM) + if (UNIT(m)->manager->running_as != MANAGER_SYSTEM) return 0; if (!(p = get_mount_parameters_configured(m))) @@ -350,24 +346,31 @@ static int mount_add_fstab_links(Mount *m) { automount || mount_test_option(p->options, "comment=systemd.mount") || mount_test_option(p->options, "x-systemd-mount") || - m->meta.manager->mount_auto; + UNIT(m)->manager->mount_auto; if (mount_is_network(p)) { target = SPECIAL_REMOTE_FS_TARGET; - after = SPECIAL_NETWORK_TARGET; - } else + after = tu_wants = SPECIAL_REMOTE_FS_PRE_TARGET; + } else { target = SPECIAL_LOCAL_FS_TARGET; + after = SPECIAL_LOCAL_FS_PRE_TARGET; + } - if (!path_equal(m->where, "/")) - if ((r = unit_add_two_dependencies_by_name(UNIT(m), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_UMOUNT_TARGET, NULL, true)) < 0) - return r; - - if ((r = manager_load_unit(m->meta.manager, target, NULL, NULL, &tu)) < 0) + r = manager_load_unit(UNIT(m)->manager, target, NULL, NULL, &tu); + if (r < 0) return r; - if (after) - if ((r = unit_add_dependency_by_name(UNIT(m), UNIT_AFTER, after, NULL, true)) < 0) + if (tu_wants) { + r = unit_add_dependency_by_name(tu, UNIT_WANTS, tu_wants, NULL, true); + if (r < 0) + return r; + } + + if (after) { + r = unit_add_dependency_by_name(UNIT(m), UNIT_AFTER, after, NULL, true); + if (r < 0) return r; + } if (automount) { Unit *am; @@ -383,9 +386,9 @@ static int mount_add_fstab_links(Mount *m) { /* Install automount unit */ if (!nofail) /* automount + fail */ - return unit_add_two_dependencies(tu, UNIT_AFTER, UNIT_REQUIRES, UNIT(am), true); + return unit_add_two_dependencies(tu, UNIT_AFTER, UNIT_REQUIRES, am, true); else /* automount + nofail */ - return unit_add_two_dependencies(tu, UNIT_AFTER, UNIT_WANTS, UNIT(am), true); + return unit_add_two_dependencies(tu, UNIT_AFTER, UNIT_WANTS, am, true); } else if (handle && !noauto) { @@ -423,13 +426,13 @@ static int mount_add_device_links(Mount *m) { if ((r = unit_add_node_link(UNIT(m), p->what, !noauto && nofail && - UNIT(m)->meta.manager->running_as == MANAGER_SYSTEM)) < 0) + UNIT(m)->manager->running_as == MANAGER_SYSTEM)) < 0) return r; } if (p->passno > 0 && !mount_is_bind(p) && - UNIT(m)->meta.manager->running_as == MANAGER_SYSTEM && + UNIT(m)->manager->running_as == MANAGER_SYSTEM && !path_equal(m->where, "/")) { char *name; Unit *fsck; @@ -439,7 +442,7 @@ static int mount_add_device_links(Mount *m) { if (!(name = unit_name_from_path_instance("fsck", p->what, ".service"))) return -ENOMEM; - if ((r = manager_load_unit_prepare(m->meta.manager, name, NULL, NULL, &fsck)) < 0) { + if ((r = manager_load_unit_prepare(UNIT(m)->manager, name, NULL, NULL, &fsck)) < 0) { log_warning("Failed to prepare unit %s: %s", name, strerror(-r)); free(name); return r; @@ -458,24 +461,23 @@ static int mount_add_device_links(Mount *m) { static int mount_add_default_dependencies(Mount *m) { int r; + MountParameters *p; assert(m); - if (m->meta.manager->running_as == MANAGER_SYSTEM && - !path_equal(m->where, "/")) { - MountParameters *p; - - p = get_mount_parameters_configured(m); + if (UNIT(m)->manager->running_as != MANAGER_SYSTEM) + return 0; - if (p && needs_quota(p)) { - if ((r = unit_add_two_dependencies_by_name(UNIT(m), UNIT_BEFORE, UNIT_WANTS, SPECIAL_QUOTACHECK_SERVICE, NULL, true)) < 0 || - (r = unit_add_two_dependencies_by_name(UNIT(m), UNIT_BEFORE, UNIT_WANTS, SPECIAL_QUOTAON_SERVICE, NULL, true)) < 0) - return r; - } + p = get_mount_parameters_configured(m); + if (p && needs_quota(p)) { + if ((r = unit_add_two_dependencies_by_name(UNIT(m), UNIT_BEFORE, UNIT_WANTS, SPECIAL_QUOTACHECK_SERVICE, NULL, true)) < 0 || + (r = unit_add_two_dependencies_by_name(UNIT(m), UNIT_BEFORE, UNIT_WANTS, SPECIAL_QUOTAON_SERVICE, NULL, true)) < 0) + return r; + } + if (!path_equal(m->where, "/")) if ((r = unit_add_two_dependencies_by_name(UNIT(m), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_UMOUNT_TARGET, NULL, true)) < 0) return r; - } return 0; } @@ -518,11 +520,11 @@ static int mount_fix_timeouts(Mount *m) { return r; } - SET_FOREACH(other, m->meta.dependencies[UNIT_AFTER], i) { - if (other->meta.type != UNIT_DEVICE) + SET_FOREACH(other, UNIT(m)->dependencies[UNIT_AFTER], i) { + if (other->type != UNIT_DEVICE) continue; - other->meta.job_timeout = u; + other->job_timeout = u; } return 0; @@ -533,7 +535,7 @@ static int mount_verify(Mount *m) { char *e; assert(m); - if (m->meta.load_state != UNIT_LOADED) + if (UNIT(m)->load_state != UNIT_LOADED) return 0; if (!m->from_etc_fstab && !m->from_fragment && !m->from_proc_self_mountinfo) @@ -546,7 +548,7 @@ static int mount_verify(Mount *m) { free(e); if (!b) { - log_error("%s's Where setting doesn't match unit name. Refusing.", m->meta.id); + log_error("%s's Where setting doesn't match unit name. Refusing.", UNIT(m)->id); return -EINVAL; } @@ -555,13 +557,13 @@ static int mount_verify(Mount *m) { return -EINVAL; } - if (m->meta.fragment_path && !m->parameters_fragment.what) { - log_error("%s's What setting is missing. Refusing.", m->meta.id); + if (UNIT(m)->fragment_path && !m->parameters_fragment.what) { + log_error("%s's What setting is missing. Refusing.", UNIT(m)->id); return -EBADMSG; } if (m->exec_context.pam_name && m->exec_context.kill_mode != KILL_CONTROL_GROUP) { - log_error("%s has PAM enabled. Kill mode must be set to 'control-group'. Refusing.", m->meta.id); + log_error("%s has PAM enabled. Kill mode must be set to 'control-group'. Refusing.", UNIT(m)->id); return -EINVAL; } @@ -573,28 +575,31 @@ static int mount_load(Unit *u) { int r; assert(u); - assert(u->meta.load_state == UNIT_STUB); + assert(u->load_state == UNIT_STUB); if ((r = unit_load_fragment_and_dropin_optional(u)) < 0) return r; /* This is a new unit? Then let's add in some extras */ - if (u->meta.load_state == UNIT_LOADED) { + if (u->load_state == UNIT_LOADED) { if ((r = unit_add_exec_dependencies(u, &m->exec_context)) < 0) return r; - if (m->meta.fragment_path) + if (UNIT(m)->fragment_path) m->from_fragment = true; else if (m->from_etc_fstab) - m->meta.default_dependencies = false; + /* We always add several default dependencies to fstab mounts, + * but we do not want the implicit complementing of Wants= with After= + * in the target unit that this mount unit will be hooked into. */ + UNIT(m)->default_dependencies = false; if (!m->where) - if (!(m->where = unit_name_to_path(u->meta.id))) + if (!(m->where = unit_name_to_path(u->id))) return -ENOMEM; path_kill_slashes(m->where); - if (!m->meta.description) + if (!UNIT(m)->description) if ((r = unit_set_description(u, m->where)) < 0) return r; @@ -619,7 +624,7 @@ static int mount_load(Unit *u) { if ((r = mount_add_fstab_links(m)) < 0) return r; - if (m->meta.default_dependencies) + if (UNIT(m)->default_dependencies || m->from_etc_fstab) if ((r = mount_add_default_dependencies(m)) < 0) return r; @@ -635,13 +640,18 @@ static int mount_load(Unit *u) { static int mount_notify_automount(Mount *m, int status) { Unit *p; int r; + Iterator i; assert(m); - if ((r = unit_get_related_unit(UNIT(m), ".automount", &p)) < 0) - return r == -ENOENT ? 0 : r; + SET_FOREACH(p, UNIT(m)->dependencies[UNIT_TRIGGERED_BY], i) + if (p->type == UNIT_AUTOMOUNT) { + r = automount_send_ready(AUTOMOUNT(p), status); + if (r < 0) + return r; + } - return automount_send_ready(AUTOMOUNT(p), status); + return 0; } static void mount_set_state(Mount *m, MountState state) { @@ -683,12 +693,12 @@ static void mount_set_state(Mount *m, MountState state) { if (state != old_state) log_debug("%s changed %s -> %s", - m->meta.id, + UNIT(m)->id, mount_state_to_string(old_state), mount_state_to_string(state)); - unit_notify(UNIT(m), state_translation_table[old_state], state_translation_table[state], !m->reload_failure); - m->reload_failure = false; + unit_notify(UNIT(m), state_translation_table[old_state], state_translation_table[state], m->reload_result == MOUNT_SUCCESS); + m->reload_result = MOUNT_SUCCESS; } static int mount_coldplug(Unit *u) { @@ -744,6 +754,7 @@ static void mount_dump(Unit *u, FILE *f, const char *prefix) { fprintf(f, "%sMount State: %s\n" + "%sResult: %s\n" "%sWhere: %s\n" "%sWhat: %s\n" "%sFile System Type: %s\n" @@ -753,6 +764,7 @@ static void mount_dump(Unit *u, FILE *f, const char *prefix) { "%sFrom fragment: %s\n" "%sDirectoryMode: %04o\n", prefix, mount_state_to_string(m->state), + prefix, mount_result_to_string(m->result), prefix, m->where, prefix, strna(p->what), prefix, strna(p->fstype), @@ -785,12 +797,13 @@ static int mount_spawn(Mount *m, ExecCommand *c, pid_t *_pid) { NULL, &m->exec_context, NULL, 0, - m->meta.manager->environment, + UNIT(m)->manager->environment, true, true, true, - m->meta.manager->confirm_spawn, - m->meta.cgroup_bondings, + UNIT(m)->manager->confirm_spawn, + UNIT(m)->cgroup_bondings, + UNIT(m)->cgroup_attributes, &pid)) < 0) goto fail; @@ -808,33 +821,33 @@ fail: return r; } -static void mount_enter_dead(Mount *m, bool success) { +static void mount_enter_dead(Mount *m, MountResult f) { assert(m); - if (!success) - m->failure = true; + if (f != MOUNT_SUCCESS) + m->result = f; - mount_set_state(m, m->failure ? MOUNT_FAILED : MOUNT_DEAD); + mount_set_state(m, m->result != MOUNT_SUCCESS ? MOUNT_FAILED : MOUNT_DEAD); } -static void mount_enter_mounted(Mount *m, bool success) { +static void mount_enter_mounted(Mount *m, MountResult f) { assert(m); - if (!success) - m->failure = true; + if (f != MOUNT_SUCCESS) + m->result = f; mount_set_state(m, MOUNT_MOUNTED); } -static void mount_enter_signal(Mount *m, MountState state, bool success) { +static void mount_enter_signal(Mount *m, MountState state, MountResult f) { int r; Set *pid_set = NULL; bool wait_for_exit = false; assert(m); - if (!success) - m->failure = true; + if (f != MOUNT_SUCCESS) + m->result = f; if (m->exec_context.kill_mode != KILL_NONE) { int sig = (state == MOUNT_MOUNTING_SIGTERM || @@ -861,7 +874,7 @@ static void mount_enter_signal(Mount *m, MountState state, bool success) { if ((r = set_put(pid_set, LONG_TO_PTR(m->control_pid))) < 0) goto fail; - if ((r = cgroup_bonding_kill_list(m->meta.cgroup_bondings, sig, true, pid_set)) < 0) { + if ((r = cgroup_bonding_kill_list(UNIT(m)->cgroup_bondings, sig, true, pid_set)) < 0) { if (r != -EAGAIN && r != -ESRCH && r != -ENOENT) log_warning("Failed to kill control group: %s", strerror(-r)); } else if (r > 0) @@ -878,32 +891,29 @@ static void mount_enter_signal(Mount *m, MountState state, bool success) { mount_set_state(m, state); } else if (state == MOUNT_REMOUNTING_SIGTERM || state == MOUNT_REMOUNTING_SIGKILL) - mount_enter_mounted(m, true); + mount_enter_mounted(m, MOUNT_SUCCESS); else - mount_enter_dead(m, true); + mount_enter_dead(m, MOUNT_SUCCESS); return; fail: - log_warning("%s failed to kill processes: %s", m->meta.id, strerror(-r)); + log_warning("%s failed to kill processes: %s", UNIT(m)->id, strerror(-r)); if (state == MOUNT_REMOUNTING_SIGTERM || state == MOUNT_REMOUNTING_SIGKILL) - mount_enter_mounted(m, false); + mount_enter_mounted(m, MOUNT_FAILURE_RESOURCES); else - mount_enter_dead(m, false); + mount_enter_dead(m, MOUNT_FAILURE_RESOURCES); if (pid_set) set_free(pid_set); } -static void mount_enter_unmounting(Mount *m, bool success) { +static void mount_enter_unmounting(Mount *m) { int r; assert(m); - if (!success) - m->failure = true; - m->control_command_id = MOUNT_EXEC_UNMOUNT; m->control_command = m->exec_command + MOUNT_EXEC_UNMOUNT; @@ -924,8 +934,8 @@ static void mount_enter_unmounting(Mount *m, bool success) { return; fail: - log_warning("%s failed to run 'umount' task: %s", m->meta.id, strerror(-r)); - mount_enter_mounted(m, false); + log_warning("%s failed to run 'umount' task: %s", UNIT(m)->id, strerror(-r)); + mount_enter_mounted(m, MOUNT_FAILURE_RESOURCES); } static void mount_enter_mounting(Mount *m) { @@ -975,8 +985,8 @@ static void mount_enter_mounting(Mount *m) { return; fail: - log_warning("%s failed to run 'mount' task: %s", m->meta.id, strerror(-r)); - mount_enter_dead(m, false); + log_warning("%s failed to run 'mount' task: %s", UNIT(m)->id, strerror(-r)); + mount_enter_dead(m, MOUNT_FAILURE_RESOURCES); } static void mount_enter_mounting_done(Mount *m) { @@ -985,14 +995,11 @@ static void mount_enter_mounting_done(Mount *m) { mount_set_state(m, MOUNT_MOUNTING_DONE); } -static void mount_enter_remounting(Mount *m, bool success) { +static void mount_enter_remounting(Mount *m) { int r; assert(m); - if (!success) - m->failure = true; - m->control_command_id = MOUNT_EXEC_REMOUNT; m->control_command = m->exec_command + MOUNT_EXEC_REMOUNT; @@ -1043,9 +1050,9 @@ static void mount_enter_remounting(Mount *m, bool success) { return; fail: - log_warning("%s failed to run 'remount' task: %s", m->meta.id, strerror(-r)); - m->reload_failure = true; - mount_enter_mounted(m, true); + log_warning("%s failed to run 'remount' task: %s", UNIT(m)->id, strerror(-r)); + m->reload_result = MOUNT_FAILURE_RESOURCES; + mount_enter_mounted(m, MOUNT_SUCCESS); } static int mount_start(Unit *u) { @@ -1068,7 +1075,9 @@ static int mount_start(Unit *u) { assert(m->state == MOUNT_DEAD || m->state == MOUNT_FAILED); - m->failure = false; + m->result = MOUNT_SUCCESS; + m->reload_result = MOUNT_SUCCESS; + mount_enter_mounting(m); return 0; } @@ -1093,7 +1102,7 @@ static int mount_stop(Unit *u) { m->state == MOUNT_REMOUNTING_SIGTERM || m->state == MOUNT_REMOUNTING_SIGKILL); - mount_enter_unmounting(m, true); + mount_enter_unmounting(m); return 0; } @@ -1107,7 +1116,7 @@ static int mount_reload(Unit *u) { assert(m->state == MOUNT_MOUNTED); - mount_enter_remounting(m, true); + mount_enter_remounting(m); return 0; } @@ -1119,7 +1128,8 @@ static int mount_serialize(Unit *u, FILE *f, FDSet *fds) { assert(fds); unit_serialize_item(u, f, "state", mount_state_to_string(m->state)); - unit_serialize_item(u, f, "failure", yes_no(m->failure)); + unit_serialize_item(u, f, "result", mount_result_to_string(m->result)); + unit_serialize_item(u, f, "reload-result", mount_result_to_string(m->reload_result)); if (m->control_pid > 0) unit_serialize_item_format(u, f, "control-pid", "%lu", (unsigned long) m->control_pid); @@ -1145,13 +1155,23 @@ static int mount_deserialize_item(Unit *u, const char *key, const char *value, F log_debug("Failed to parse state value %s", value); else m->deserialized_state = state; - } else if (streq(key, "failure")) { - int b; + } else if (streq(key, "result")) { + MountResult f; - if ((b = parse_boolean(value)) < 0) - log_debug("Failed to parse failure value %s", value); - else - m->failure = b || m->failure; + f = mount_result_from_string(value); + if (f < 0) + log_debug("Failed to parse result value %s", value); + else if (f != MOUNT_SUCCESS) + m->result = f; + + } else if (streq(key, "reload-result")) { + MountResult f; + + f = mount_result_from_string(value); + if (f < 0) + log_debug("Failed to parse reload result value %s", value); + else if (f != MOUNT_SUCCESS) + m->reload_result = f; } else if (streq(key, "control-pid")) { pid_t pid; @@ -1198,7 +1218,7 @@ static bool mount_check_gc(Unit *u) { static void mount_sigchld_event(Unit *u, pid_t pid, int code, int status) { Mount *m = MOUNT(u); - bool success; + MountResult f; assert(m); assert(pid >= 0); @@ -1208,17 +1228,29 @@ static void mount_sigchld_event(Unit *u, pid_t pid, int code, int status) { m->control_pid = 0; - success = is_clean_exit(code, status); - m->failure = m->failure || !success; + if (is_clean_exit(code, status)) + f = MOUNT_SUCCESS; + else if (code == CLD_EXITED) + f = MOUNT_FAILURE_EXIT_CODE; + else if (code == CLD_KILLED) + f = MOUNT_FAILURE_SIGNAL; + else if (code == CLD_DUMPED) + f = MOUNT_FAILURE_CORE_DUMP; + else + assert_not_reached("Unknown code"); + + if (f != MOUNT_SUCCESS) + m->result = f; if (m->control_command) { exec_status_exit(&m->control_command->exec_status, &m->exec_context, pid, code, status); + m->control_command = NULL; m->control_command_id = _MOUNT_EXEC_COMMAND_INVALID; } - log_full(success ? LOG_DEBUG : LOG_NOTICE, - "%s mount process exited, code=%s status=%i", u->meta.id, sigchld_code_to_string(code), status); + log_full(f == MOUNT_SUCCESS ? LOG_DEBUG : LOG_NOTICE, + "%s mount process exited, code=%s status=%i", u->id, sigchld_code_to_string(code), status); /* Note that mount(8) returning and the kernel sending us a * mount table change event might happen out-of-order. If an @@ -1234,23 +1266,23 @@ static void mount_sigchld_event(Unit *u, pid_t pid, int code, int status) { case MOUNT_MOUNTING_SIGKILL: case MOUNT_MOUNTING_SIGTERM: - if (success) - mount_enter_mounted(m, true); + if (f == MOUNT_SUCCESS) + mount_enter_mounted(m, f); else if (m->from_proc_self_mountinfo) - mount_enter_mounted(m, false); + mount_enter_mounted(m, f); else - mount_enter_dead(m, false); + mount_enter_dead(m, f); break; case MOUNT_REMOUNTING: case MOUNT_REMOUNTING_SIGKILL: case MOUNT_REMOUNTING_SIGTERM: - m->reload_failure = !success; + m->reload_result = f; if (m->from_proc_self_mountinfo) - mount_enter_mounted(m, true); + mount_enter_mounted(m, MOUNT_SUCCESS); else - mount_enter_dead(m, true); + mount_enter_dead(m, MOUNT_SUCCESS); break; @@ -1258,12 +1290,12 @@ static void mount_sigchld_event(Unit *u, pid_t pid, int code, int status) { case MOUNT_UNMOUNTING_SIGKILL: case MOUNT_UNMOUNTING_SIGTERM: - if (success) - mount_enter_dead(m, true); + if (f == MOUNT_SUCCESS) + mount_enter_dead(m, f); else if (m->from_proc_self_mountinfo) - mount_enter_mounted(m, false); + mount_enter_mounted(m, f); else - mount_enter_dead(m, false); + mount_enter_dead(m, f); break; default: @@ -1285,72 +1317,72 @@ static void mount_timer_event(Unit *u, uint64_t elapsed, Watch *w) { case MOUNT_MOUNTING: case MOUNT_MOUNTING_DONE: - log_warning("%s mounting timed out. Stopping.", u->meta.id); - mount_enter_signal(m, MOUNT_MOUNTING_SIGTERM, false); + log_warning("%s mounting timed out. Stopping.", u->id); + mount_enter_signal(m, MOUNT_MOUNTING_SIGTERM, MOUNT_FAILURE_TIMEOUT); break; case MOUNT_REMOUNTING: - log_warning("%s remounting timed out. Stopping.", u->meta.id); - m->reload_failure = true; - mount_enter_mounted(m, true); + log_warning("%s remounting timed out. Stopping.", u->id); + m->reload_result = MOUNT_FAILURE_TIMEOUT; + mount_enter_mounted(m, MOUNT_SUCCESS); break; case MOUNT_UNMOUNTING: - log_warning("%s unmounting timed out. Stopping.", u->meta.id); - mount_enter_signal(m, MOUNT_UNMOUNTING_SIGTERM, false); + log_warning("%s unmounting timed out. Stopping.", u->id); + mount_enter_signal(m, MOUNT_UNMOUNTING_SIGTERM, MOUNT_FAILURE_TIMEOUT); break; case MOUNT_MOUNTING_SIGTERM: if (m->exec_context.send_sigkill) { - log_warning("%s mounting timed out. Killing.", u->meta.id); - mount_enter_signal(m, MOUNT_MOUNTING_SIGKILL, false); + log_warning("%s mounting timed out. Killing.", u->id); + mount_enter_signal(m, MOUNT_MOUNTING_SIGKILL, MOUNT_FAILURE_TIMEOUT); } else { - log_warning("%s mounting timed out. Skipping SIGKILL. Ignoring.", u->meta.id); + log_warning("%s mounting timed out. Skipping SIGKILL. Ignoring.", u->id); if (m->from_proc_self_mountinfo) - mount_enter_mounted(m, false); + mount_enter_mounted(m, MOUNT_FAILURE_TIMEOUT); else - mount_enter_dead(m, false); + mount_enter_dead(m, MOUNT_FAILURE_TIMEOUT); } break; case MOUNT_REMOUNTING_SIGTERM: if (m->exec_context.send_sigkill) { - log_warning("%s remounting timed out. Killing.", u->meta.id); - mount_enter_signal(m, MOUNT_REMOUNTING_SIGKILL, false); + log_warning("%s remounting timed out. Killing.", u->id); + mount_enter_signal(m, MOUNT_REMOUNTING_SIGKILL, MOUNT_FAILURE_TIMEOUT); } else { - log_warning("%s remounting timed out. Skipping SIGKILL. Ignoring.", u->meta.id); + log_warning("%s remounting timed out. Skipping SIGKILL. Ignoring.", u->id); if (m->from_proc_self_mountinfo) - mount_enter_mounted(m, false); + mount_enter_mounted(m, MOUNT_FAILURE_TIMEOUT); else - mount_enter_dead(m, false); + mount_enter_dead(m, MOUNT_FAILURE_TIMEOUT); } break; case MOUNT_UNMOUNTING_SIGTERM: if (m->exec_context.send_sigkill) { - log_warning("%s unmounting timed out. Killing.", u->meta.id); - mount_enter_signal(m, MOUNT_UNMOUNTING_SIGKILL, false); + log_warning("%s unmounting timed out. Killing.", u->id); + mount_enter_signal(m, MOUNT_UNMOUNTING_SIGKILL, MOUNT_FAILURE_TIMEOUT); } else { - log_warning("%s unmounting timed out. Skipping SIGKILL. Ignoring.", u->meta.id); + log_warning("%s unmounting timed out. Skipping SIGKILL. Ignoring.", u->id); if (m->from_proc_self_mountinfo) - mount_enter_mounted(m, false); + mount_enter_mounted(m, MOUNT_FAILURE_TIMEOUT); else - mount_enter_dead(m, false); + mount_enter_dead(m, MOUNT_FAILURE_TIMEOUT); } break; case MOUNT_MOUNTING_SIGKILL: case MOUNT_REMOUNTING_SIGKILL: case MOUNT_UNMOUNTING_SIGKILL: - log_warning("%s mount process still around after SIGKILL. Ignoring.", u->meta.id); + log_warning("%s mount process still around after SIGKILL. Ignoring.", u->id); if (m->from_proc_self_mountinfo) - mount_enter_mounted(m, false); + mount_enter_mounted(m, MOUNT_FAILURE_TIMEOUT); else - mount_enter_dead(m, false); + mount_enter_dead(m, MOUNT_FAILURE_TIMEOUT); break; default: @@ -1393,13 +1425,16 @@ static int mount_add_one( if (!is_path(where)) return 0; - if (!(e = unit_name_from_path(where, ".mount"))) + e = unit_name_from_path(where, ".mount"); + if (!e) return -ENOMEM; - if (!(u = manager_get_unit(m, e))) { + u = manager_get_unit(m, e); + if (!u) { delete = true; - if (!(u = unit_new(m))) { + u = unit_new(m, sizeof(Mount)); + if (!u) { free(e); return -ENOMEM; } @@ -1410,7 +1445,8 @@ static int mount_add_one( if (r < 0) goto fail; - if (!(MOUNT(u)->where = strdup(where))) { + MOUNT(u)->where = strdup(where); + if (!MOUNT(u)->where) { r = -ENOMEM; goto fail; } @@ -1473,7 +1509,7 @@ static int mount_find_pri(char *options) { char *end, *pri; unsigned long r; - if (!(pri = mount_test_option(options, "pri="))) + if (!(pri = mount_test_option(options, "pri"))) return 0; pri += 4; @@ -1673,7 +1709,7 @@ fail: } void mount_fd_event(Manager *m, int events) { - Meta *meta; + Unit *u; int r; assert(m); @@ -1687,8 +1723,8 @@ void mount_fd_event(Manager *m, int events) { log_error("Failed to reread /proc/self/mountinfo: %s", strerror(-r)); /* Reset flags, just in case, for later calls */ - LIST_FOREACH(units_by_type, meta, m->units_by_type[UNIT_MOUNT]) { - Mount *mount = (Mount*) meta; + LIST_FOREACH(units_by_type, u, m->units_by_type[UNIT_MOUNT]) { + Mount *mount = MOUNT(u); mount->is_mounted = mount->just_mounted = mount->just_changed = false; } @@ -1698,8 +1734,8 @@ void mount_fd_event(Manager *m, int events) { manager_dispatch_load_queue(m); - LIST_FOREACH(units_by_type, meta, m->units_by_type[UNIT_MOUNT]) { - Mount *mount = (Mount*) meta; + LIST_FOREACH(units_by_type, u, m->units_by_type[UNIT_MOUNT]) { + Mount *mount = MOUNT(u); if (!mount->is_mounted) { /* This has just been unmounted. */ @@ -1709,7 +1745,7 @@ void mount_fd_event(Manager *m, int events) { switch (mount->state) { case MOUNT_MOUNTED: - mount_enter_dead(mount, true); + mount_enter_dead(mount, MOUNT_SUCCESS); break; default: @@ -1726,7 +1762,7 @@ void mount_fd_event(Manager *m, int events) { case MOUNT_DEAD: case MOUNT_FAILED: - mount_enter_mounted(mount, true); + mount_enter_mounted(mount, MOUNT_SUCCESS); break; case MOUNT_MOUNTING: @@ -1757,7 +1793,8 @@ static void mount_reset_failed(Unit *u) { if (m->state == MOUNT_FAILED) mount_set_state(m, MOUNT_DEAD); - m->failure = false; + m->result = MOUNT_SUCCESS; + m->reload_result = MOUNT_SUCCESS; } static int mount_kill(Unit *u, KillWho who, KillMode mode, int signo, DBusError *error) { @@ -1795,7 +1832,7 @@ static int mount_kill(Unit *u, KillWho who, KillMode mode, int signo, DBusError goto finish; } - if ((q = cgroup_bonding_kill_list(m->meta.cgroup_bondings, signo, false, pid_set)) < 0) + if ((q = cgroup_bonding_kill_list(UNIT(m)->cgroup_bondings, signo, false, pid_set)) < 0) if (q != -EAGAIN && q != -ESRCH && q != -ENOENT) r = q; } @@ -1833,8 +1870,24 @@ static const char* const mount_exec_command_table[_MOUNT_EXEC_COMMAND_MAX] = { DEFINE_STRING_TABLE_LOOKUP(mount_exec_command, MountExecCommand); +static const char* const mount_result_table[_MOUNT_RESULT_MAX] = { + [MOUNT_SUCCESS] = "success", + [MOUNT_FAILURE_RESOURCES] = "resources", + [MOUNT_FAILURE_TIMEOUT] = "timeout", + [MOUNT_FAILURE_EXIT_CODE] = "exit-code", + [MOUNT_FAILURE_SIGNAL] = "signal", + [MOUNT_FAILURE_CORE_DUMP] = "core-dump" +}; + +DEFINE_STRING_TABLE_LOOKUP(mount_result, MountResult); + const UnitVTable mount_vtable = { .suffix = ".mount", + .object_size = sizeof(Mount), + .sections = + "Unit\0" + "Mount\0" + "Install\0", .no_alias = true, .no_instances = true, diff --git a/src/mount.h b/src/mount.h index 7c5d9d1f62..9318444249 100644 --- a/src/mount.h +++ b/src/mount.h @@ -59,8 +59,19 @@ typedef struct MountParameters { int passno; } MountParameters; +typedef enum MountResult { + MOUNT_SUCCESS, + MOUNT_FAILURE_RESOURCES, + MOUNT_FAILURE_TIMEOUT, + MOUNT_FAILURE_EXIT_CODE, + MOUNT_FAILURE_SIGNAL, + MOUNT_FAILURE_CORE_DUMP, + _MOUNT_RESULT_MAX, + _MOUNT_RESULT_INVALID = -1 +} MountResult; + struct Mount { - Meta meta; + Unit meta; char *where; @@ -78,8 +89,8 @@ struct Mount { bool just_mounted:1; bool just_changed:1; - bool failure:1; - bool reload_failure:1; + MountResult result; + MountResult reload_result; mode_t directory_mode; @@ -107,4 +118,7 @@ MountState mount_state_from_string(const char *s); const char* mount_exec_command_to_string(MountExecCommand i); MountExecCommand mount_exec_command_from_string(const char *s); +const char* mount_result_to_string(MountResult i); +MountResult mount_result_from_string(const char *s); + #endif diff --git a/src/namespace.c b/src/namespace.c index 54b22f494e..09bc82909f 100644 --- a/src/namespace.c +++ b/src/namespace.c @@ -253,11 +253,19 @@ int setup_namespace( } if (need_private) { + mode_t u; + memcpy(private_dir, tmp_dir, sizeof(tmp_dir)-1); + + u = umask(0000); if (mkdir(private_dir, 0777 + S_ISVTX) < 0) { + umask(u); + r = -errno; goto fail; } + + umask(u); remove_private = true; } @@ -266,8 +274,12 @@ int setup_namespace( goto fail; } - /* We assume that by default mount events from us won't be - * propagated to the root namespace. */ + /* Remount / as SLAVE so that nothing mounted in the namespace + shows up in the parent */ + if (mount(NULL, "/", NULL, MS_SLAVE|MS_REC, NULL) < 0) { + r = -errno; + goto fail; + } for (p = paths; p < paths + n; p++) if ((r = apply_mount(p, root_dir, inaccessible_dir, private_dir, flags)) < 0) diff --git a/src/notify.c b/src/notify.c index a9bc51e2db..9d52bdfdd3 100644 --- a/src/notify.c +++ b/src/notify.c @@ -27,10 +27,11 @@ #include <stdlib.h> #include <string.h> +#include <systemd/sd-daemon.h> + #include "strv.h" #include "util.h" #include "log.h" -#include "sd-daemon.h" #include "sd-readahead.h" static bool arg_ready = false; diff --git a/src/nspawn.c b/src/nspawn.c index 8d7e0d03a9..b8b379d991 100644 --- a/src/nspawn.c +++ b/src/nspawn.c @@ -37,16 +37,20 @@ #include <termios.h> #include <sys/signalfd.h> #include <grp.h> +#include <linux/fs.h> + +#include <systemd/sd-daemon.h> #include "log.h" #include "util.h" #include "missing.h" #include "cgroup-util.h" -#include "sd-daemon.h" #include "strv.h" +#include "loopback-setup.h" static char *arg_directory = NULL; static char *arg_user = NULL; +static bool arg_private_network = false; static int help(void) { @@ -54,7 +58,8 @@ static int help(void) { "Spawn a minimal namespace container for debugging, testing and building.\n\n" " -h --help Show this help\n" " -D --directory=NAME Root directory for the container\n" - " -u --user=USER Run the command under specified user or uid\n", + " -u --user=USER Run the command under specified user or uid\n" + " --private-network Disable network in container\n", program_invocation_short_name); return 0; @@ -62,11 +67,16 @@ static int help(void) { static int parse_argv(int argc, char *argv[]) { + enum { + ARG_PRIVATE_NETWORK = 0x100 + }; + static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "directory", required_argument, NULL, 'D' }, - { "user", optional_argument, NULL, 'u' }, - { NULL, 0, NULL, 0 } + { "help", no_argument, NULL, 'h' }, + { "directory", required_argument, NULL, 'D' }, + { "user", required_argument, NULL, 'u' }, + { "private-network", no_argument, NULL, ARG_PRIVATE_NETWORK }, + { NULL, 0, NULL, 0 } }; int c; @@ -100,6 +110,10 @@ static int parse_argv(int argc, char *argv[]) { break; + case ARG_PRIVATE_NETWORK: + arg_private_network = true; + break; + case '?': return -EINVAL; @@ -133,8 +147,8 @@ static int mount_all(const char *dest) { { "/dev/pts", "/dev/pts", "bind", NULL, MS_BIND, true }, { "tmpfs", "/run", "tmpfs", "mode=755", MS_NOSUID|MS_NODEV, true }, #ifdef HAVE_SELINUX - { "/selinux", "/selinux", "bind", NULL, MS_BIND, false }, /* Bind mount first */ - { "/selinux", "/selinux", "bind", NULL, MS_BIND|MS_RDONLY|MS_REMOUNT, false }, /* Then, make it r/o */ + { "/sys/fs/selinux", "/sys/fs/selinux", "bind", NULL, MS_BIND, false }, /* Bind mount first */ + { "/sys/fs/selinux", "/sys/fs/selinux", "bind", NULL, MS_BIND|MS_RDONLY|MS_REMOUNT, false }, /* Then, make it r/o */ #endif }; @@ -154,7 +168,7 @@ static int mount_all(const char *dest) { break; } - if ((t = path_is_mount_point(where)) < 0) { + if ((t = path_is_mount_point(where, false)) < 0) { log_error("Failed to detect whether %s is a mount point: %s", where, strerror(-t)); free(where); @@ -184,8 +198,10 @@ static int mount_all(const char *dest) { /* Fix the timezone, if possible */ if (asprintf(&where, "%s/%s", dest, "/etc/localtime") >= 0) { - mount("/etc/localtime", where, "bind", MS_BIND, NULL); - mount("/etc/localtime", where, "bind", MS_BIND|MS_REMOUNT|MS_RDONLY, NULL); + + if (mount("/etc/localtime", where, "bind", MS_BIND, NULL) >= 0) + mount("/etc/localtime", where, "bind", MS_BIND|MS_REMOUNT|MS_RDONLY, NULL); + free(where); } @@ -314,7 +330,6 @@ static int copy_devnodes(const char *dest, const char *console) { } finish: - umask(u); return r; @@ -347,7 +362,7 @@ static int drop_capabilities(void) { unsigned long l; - for (l = 0; l <= MAX(63LU, (unsigned long) CAP_LAST_CAP); l++) { + for (l = 0; l <= cap_last_cap(); l++) { unsigned i; for (i = 0; i < ELEMENTSOF(retain); i++) @@ -358,12 +373,6 @@ static int drop_capabilities(void) { continue; if (prctl(PR_CAPBSET_DROP, l) < 0) { - - /* If this capability is not known, EINVAL - * will be returned, let's ignore this. */ - if (errno == EINVAL) - break; - log_error("PR_CAPBSET_DROP failed: %m"); return -errno; } @@ -386,11 +395,9 @@ static int is_os_tree(const char *path) { return r < 0 ? 0 : 1; } -#define BUFFER_SIZE 1024 - static int process_pty(int master, sigset_t *mask) { - char in_buffer[BUFFER_SIZE], out_buffer[BUFFER_SIZE]; + char in_buffer[LINE_MAX], out_buffer[LINE_MAX]; size_t in_buffer_full = 0, out_buffer_full = 0; struct epoll_event stdin_ev, stdout_ev, master_ev, signal_ev; bool stdin_readable = false, stdout_writable = false, master_readable = false, master_writable = false; @@ -511,9 +518,9 @@ static int process_pty(int master, sigset_t *mask) { (master_readable && out_buffer_full <= 0) || (stdout_writable && out_buffer_full > 0)) { - if (stdin_readable && in_buffer_full < BUFFER_SIZE) { + if (stdin_readable && in_buffer_full < LINE_MAX) { - if ((k = read(STDIN_FILENO, in_buffer + in_buffer_full, BUFFER_SIZE - in_buffer_full)) < 0) { + if ((k = read(STDIN_FILENO, in_buffer + in_buffer_full, LINE_MAX - in_buffer_full)) < 0) { if (errno == EAGAIN || errno == EPIPE || errno == ECONNRESET || errno == EIO) stdin_readable = false; @@ -545,9 +552,9 @@ static int process_pty(int master, sigset_t *mask) { } } - if (master_readable && out_buffer_full < BUFFER_SIZE) { + if (master_readable && out_buffer_full < LINE_MAX) { - if ((k = read(master, out_buffer + out_buffer_full, BUFFER_SIZE - out_buffer_full)) < 0) { + if ((k = read(master, out_buffer + out_buffer_full, LINE_MAX - out_buffer_full)) < 0) { if (errno == EAGAIN || errno == EPIPE || errno == ECONNRESET || errno == EIO) master_readable = false; @@ -699,7 +706,7 @@ int main(int argc, char *argv[]) { sigset_add_many(&mask, SIGCHLD, SIGWINCH, SIGTERM, SIGINT, -1); assert_se(sigprocmask(SIG_BLOCK, &mask, NULL) == 0); - if ((pid = syscall(__NR_clone, SIGCHLD|CLONE_NEWIPC|CLONE_NEWNS|CLONE_NEWPID|CLONE_NEWUTS, NULL)) < 0) { + if ((pid = syscall(__NR_clone, SIGCHLD|CLONE_NEWIPC|CLONE_NEWNS|CLONE_NEWPID|CLONE_NEWUTS|(arg_private_network ? CLONE_NEWNET : 0), NULL)) < 0) { log_error("clone() failed: %m"); goto finish; } @@ -713,6 +720,7 @@ int main(int argc, char *argv[]) { gid_t gid = (gid_t) -1; const char *envp[] = { "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", + "container=systemd-nspawn", /* LXC sets container=lxc, so follow the scheme here */ NULL, /* TERM */ NULL, /* HOME */ NULL, /* USER */ @@ -720,7 +728,7 @@ int main(int argc, char *argv[]) { NULL }; - envp[1] = strv_find_prefix(environ, "TERM="); + envp[2] = strv_find_prefix(environ, "TERM="); close_nointr_nofail(master); @@ -776,7 +784,9 @@ int main(int argc, char *argv[]) { goto child_fail; } - umask(0002); + umask(0022); + + loopback_setup(); if (drop_capabilities() < 0) goto child_fail; @@ -814,9 +824,9 @@ int main(int argc, char *argv[]) { } } - if ((asprintf((char**)(envp + 2), "HOME=%s", home? home: "/root") < 0) || - (asprintf((char**)(envp + 3), "USER=%s", arg_user? arg_user : "root") < 0) || - (asprintf((char**)(envp + 4), "LOGNAME=%s", arg_user? arg_user : "root") < 0)) { + if ((asprintf((char**)(envp + 3), "HOME=%s", home? home: "/root") < 0) || + (asprintf((char**)(envp + 4), "USER=%s", arg_user? arg_user : "root") < 0) || + (asprintf((char**)(envp + 5), "LOGNAME=%s", arg_user? arg_user : "root") < 0)) { log_error("Out of memory"); goto child_fail; } diff --git a/src/pager.c b/src/pager.c index be284da962..3fc81820e9 100644 --- a/src/pager.c +++ b/src/pager.c @@ -20,6 +20,7 @@ ***/ #include <sys/types.h> +#include <fcntl.h> #include <stdlib.h> #include <unistd.h> #include <string.h> @@ -31,6 +32,18 @@ static pid_t pager_pid = 0; +_noreturn_ static void pager_fallback(void) { + ssize_t n; + do { + n = splice(STDIN_FILENO, NULL, STDOUT_FILENO, NULL, 64*1024, 0); + } while (n > 0); + if (n < 0) { + log_error("Internal pager failed: %m"); + _exit(EXIT_FAILURE); + } + _exit(EXIT_SUCCESS); +} + void pager_open(void) { int fd[2]; const char *pager; @@ -96,10 +109,9 @@ void pager_open(void) { execlp("less", "less", NULL); execlp("more", "more", NULL); - execlp("cat", "cat", NULL); - log_error("Unable to execute pager: %m"); - _exit(EXIT_FAILURE); + pager_fallback(); + /* not reached */ } /* Return in the parent */ diff --git a/src/path-lookup.c b/src/path-lookup.c index bed9175e3f..93fdf63699 100644 --- a/src/path-lookup.c +++ b/src/path-lookup.c @@ -209,7 +209,7 @@ int lookup_paths_init(LookupPaths *p, ManagerRunningAs running_as, bool personal * the arrays in user_dirs() above! */ "/run/systemd/user", USER_CONFIG_UNIT_PATH, - "/etc/systemd/system", + "/etc/systemd/user", "/usr/local/lib/systemd/user", "/usr/local/share/systemd/user", USER_DATA_UNIT_PATH, @@ -230,7 +230,9 @@ int lookup_paths_init(LookupPaths *p, ManagerRunningAs running_as, bool personal "/usr/local/lib/systemd/system", "/usr/lib/systemd/system", SYSTEM_DATA_UNIT_PATH, +#ifdef HAVE_SPLIT_USR "/lib/systemd/system", +#endif NULL))) return -ENOMEM; } diff --git a/src/path.c b/src/path.c index 200fc2bdcb..e97cd09810 100644 --- a/src/path.c +++ b/src/path.c @@ -39,36 +39,220 @@ static const UnitActiveState state_translation_table[_PATH_STATE_MAX] = { [PATH_FAILED] = UNIT_FAILED }; -static void path_init(Unit *u) { - Path *p = PATH(u); +int path_spec_watch(PathSpec *s, Unit *u) { + + static const int flags_table[_PATH_TYPE_MAX] = { + [PATH_EXISTS] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB, + [PATH_EXISTS_GLOB] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB, + [PATH_CHANGED] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB|IN_CLOSE_WRITE|IN_CREATE|IN_DELETE|IN_MOVED_FROM|IN_MOVED_TO, + [PATH_MODIFIED] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB|IN_CLOSE_WRITE|IN_CREATE|IN_DELETE|IN_MOVED_FROM|IN_MOVED_TO|IN_MODIFY, + [PATH_DIRECTORY_NOT_EMPTY] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB|IN_CREATE|IN_MOVED_TO + }; + + bool exists = false; + char *k, *slash; + int r; assert(u); - assert(u->meta.load_state == UNIT_STUB); + assert(s); - p->directory_mode = 0755; + path_spec_unwatch(s, u); + + if (!(k = strdup(s->path))) + return -ENOMEM; + + if ((s->inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC)) < 0) { + r = -errno; + goto fail; + } + + if (unit_watch_fd(u, s->inotify_fd, EPOLLIN, &s->watch) < 0) { + r = -errno; + goto fail; + } + + if ((s->primary_wd = inotify_add_watch(s->inotify_fd, k, flags_table[s->type])) >= 0) + exists = true; + + do { + int flags; + + /* This assumes the path was passed through path_kill_slashes()! */ + if (!(slash = strrchr(k, '/'))) + break; + + /* Trim the path at the last slash. Keep the slash if it's the root dir. */ + slash[slash == k] = 0; + + flags = IN_MOVE_SELF; + if (!exists) + flags |= IN_DELETE_SELF | IN_ATTRIB | IN_CREATE | IN_MOVED_TO; + + if (inotify_add_watch(s->inotify_fd, k, flags) >= 0) + exists = true; + } while (slash != k); + + return 0; + +fail: + free(k); + + path_spec_unwatch(s, u); + return r; } -static void path_unwatch_one(Path *p, PathSpec *s) { +void path_spec_unwatch(PathSpec *s, Unit *u) { if (s->inotify_fd < 0) return; - unit_unwatch_fd(UNIT(p), &s->watch); + unit_unwatch_fd(u, &s->watch); close_nointr_nofail(s->inotify_fd); s->inotify_fd = -1; } +int path_spec_fd_event(PathSpec *s, uint32_t events) { + uint8_t *buf = NULL; + struct inotify_event *e; + ssize_t k; + int l; + int r = 0; + + if (events != EPOLLIN) { + log_error("Got Invalid poll event on inotify."); + r = -EINVAL; + goto out; + } + + if (ioctl(s->inotify_fd, FIONREAD, &l) < 0) { + log_error("FIONREAD failed: %m"); + r = -errno; + goto out; + } + + assert(l > 0); + + if (!(buf = malloc(l))) { + log_error("Failed to allocate buffer: %m"); + r = -errno; + goto out; + } + + if ((k = read(s->inotify_fd, buf, l)) < 0) { + log_error("Failed to read inotify event: %m"); + r = -errno; + goto out; + } + + e = (struct inotify_event*) buf; + + while (k > 0) { + size_t step; + + if ((s->type == PATH_CHANGED || s->type == PATH_MODIFIED) && + s->primary_wd == e->wd) + r = 1; + + step = sizeof(struct inotify_event) + e->len; + assert(step <= (size_t) k); + + e = (struct inotify_event*) ((uint8_t*) e + step); + k -= step; + } +out: + free(buf); + return r; +} + +static bool path_spec_check_good(PathSpec *s, bool initial) { + bool good = false; + + switch (s->type) { + + case PATH_EXISTS: + good = access(s->path, F_OK) >= 0; + break; + + case PATH_EXISTS_GLOB: + good = glob_exists(s->path) > 0; + break; + + case PATH_DIRECTORY_NOT_EMPTY: { + int k; + + k = dir_is_empty(s->path); + good = !(k == -ENOENT || k > 0); + break; + } + + case PATH_CHANGED: + case PATH_MODIFIED: { + bool b; + + b = access(s->path, F_OK) >= 0; + good = !initial && b != s->previous_exists; + s->previous_exists = b; + break; + } + + default: + ; + } + + return good; +} + +static bool path_spec_startswith(PathSpec *s, const char *what) { + return path_startswith(s->path, what); +} + +static void path_spec_mkdir(PathSpec *s, mode_t mode) { + int r; + + if (s->type == PATH_EXISTS || s->type == PATH_EXISTS_GLOB) + return; + + if ((r = mkdir_p(s->path, mode)) < 0) + log_warning("mkdir(%s) failed: %s", s->path, strerror(-r)); +} + +static void path_spec_dump(PathSpec *s, FILE *f, const char *prefix) { + fprintf(f, + "%s%s: %s\n", + prefix, + path_type_to_string(s->type), + s->path); +} + +void path_spec_done(PathSpec *s) { + assert(s); + assert(s->inotify_fd == -1); + + free(s->path); +} + +static void path_init(Unit *u) { + Path *p = PATH(u); + + assert(u); + assert(u->load_state == UNIT_STUB); + + p->directory_mode = 0755; +} + static void path_done(Unit *u) { Path *p = PATH(u); PathSpec *s; assert(p); + unit_ref_unset(&p->unit); + while ((s = p->specs)) { - path_unwatch_one(p, s); + path_spec_unwatch(s, u); LIST_REMOVE(PathSpec, spec, p->specs, s); - free(s->path); + path_spec_done(s); free(s); } } @@ -80,13 +264,13 @@ int path_add_one_mount_link(Path *p, Mount *m) { assert(p); assert(m); - if (p->meta.load_state != UNIT_LOADED || - m->meta.load_state != UNIT_LOADED) + if (UNIT(p)->load_state != UNIT_LOADED || + UNIT(m)->load_state != UNIT_LOADED) return 0; LIST_FOREACH(spec, s, p->specs) { - if (!path_startswith(s->path, m->where)) + if (!path_spec_startswith(s, m->where)) continue; if ((r = unit_add_two_dependencies(UNIT(p), UNIT_AFTER, UNIT_REQUIRES, UNIT(m), true)) < 0) @@ -97,13 +281,13 @@ int path_add_one_mount_link(Path *p, Mount *m) { } static int path_add_mount_links(Path *p) { - Meta *other; + Unit *other; int r; assert(p); - LIST_FOREACH(units_by_type, other, p->meta.manager->units_by_type[UNIT_MOUNT]) - if ((r = path_add_one_mount_link(p, (Mount*) other)) < 0) + LIST_FOREACH(units_by_type, other, UNIT(p)->manager->units_by_type[UNIT_MOUNT]) + if ((r = path_add_one_mount_link(p, MOUNT(other))) < 0) return r; return 0; @@ -112,11 +296,11 @@ static int path_add_mount_links(Path *p) { static int path_verify(Path *p) { assert(p); - if (p->meta.load_state != UNIT_LOADED) + if (UNIT(p)->load_state != UNIT_LOADED) return 0; if (!p->specs) { - log_error("%s lacks path setting. Refusing.", p->meta.id); + log_error("%s lacks path setting. Refusing.", UNIT(p)->id); return -EINVAL; } @@ -128,7 +312,7 @@ static int path_add_default_dependencies(Path *p) { assert(p); - if (p->meta.manager->running_as == MANAGER_SYSTEM) { + if (UNIT(p)->manager->running_as == MANAGER_SYSTEM) { if ((r = unit_add_dependency_by_name(UNIT(p), UNIT_BEFORE, SPECIAL_BASIC_TARGET, NULL, true)) < 0) return r; @@ -144,24 +328,31 @@ static int path_load(Unit *u) { int r; assert(u); - assert(u->meta.load_state == UNIT_STUB); + assert(u->load_state == UNIT_STUB); if ((r = unit_load_fragment_and_dropin(u)) < 0) return r; - if (u->meta.load_state == UNIT_LOADED) { + if (u->load_state == UNIT_LOADED) { - if (!p->unit) - if ((r = unit_load_related_unit(u, ".service", &p->unit))) + if (!UNIT_DEREF(p->unit)) { + Unit *x; + + r = unit_load_related_unit(u, ".service", &x); + if (r < 0) return r; - if ((r = unit_add_dependency(u, UNIT_BEFORE, p->unit, true)) < 0) + unit_ref_set(&p->unit, x); + } + + r = unit_add_two_dependencies(u, UNIT_BEFORE, UNIT_TRIGGERS, UNIT_DEREF(p->unit), true); + if (r < 0) return r; if ((r = path_add_mount_links(p)) < 0) return r; - if (p->meta.default_dependencies) + if (UNIT(p)->default_dependencies) if ((r = path_add_default_dependencies(p)) < 0) return r; } @@ -178,80 +369,18 @@ static void path_dump(Unit *u, FILE *f, const char *prefix) { fprintf(f, "%sPath State: %s\n" + "%sResult: %s\n" "%sUnit: %s\n" "%sMakeDirectory: %s\n" "%sDirectoryMode: %04o\n", prefix, path_state_to_string(p->state), - prefix, p->unit->meta.id, + prefix, path_result_to_string(p->result), + prefix, UNIT_DEREF(p->unit)->id, prefix, yes_no(p->make_directory), prefix, p->directory_mode); LIST_FOREACH(spec, s, p->specs) - fprintf(f, - "%s%s: %s\n", - prefix, - path_type_to_string(s->type), - s->path); -} - -static int path_watch_one(Path *p, PathSpec *s) { - static const int flags_table[_PATH_TYPE_MAX] = { - [PATH_EXISTS] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB, - [PATH_EXISTS_GLOB] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB, - [PATH_CHANGED] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB|IN_CLOSE_WRITE|IN_CREATE|IN_DELETE|IN_MOVED_FROM|IN_MOVED_TO, - [PATH_DIRECTORY_NOT_EMPTY] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB|IN_CREATE|IN_MOVED_TO - }; - - bool exists = false; - char *k, *slash; - int r; - - assert(p); - assert(s); - - path_unwatch_one(p, s); - - if (!(k = strdup(s->path))) - return -ENOMEM; - - if ((s->inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC)) < 0) { - r = -errno; - goto fail; - } - - if (unit_watch_fd(UNIT(p), s->inotify_fd, EPOLLIN, &s->watch) < 0) { - r = -errno; - goto fail; - } - - if ((s->primary_wd = inotify_add_watch(s->inotify_fd, k, flags_table[s->type])) >= 0) - exists = true; - - do { - int flags; - - /* This assumes the path was passed through path_kill_slashes()! */ - if (!(slash = strrchr(k, '/'))) - break; - - /* Trim the path at the last slash. Keep the slash if it's the root dir. */ - slash[slash == k] = 0; - - flags = IN_MOVE_SELF; - if (!exists) - flags |= IN_DELETE_SELF | IN_ATTRIB | IN_CREATE | IN_MOVED_TO; - - if (inotify_add_watch(s->inotify_fd, k, flags) >= 0) - exists = true; - } while (slash != k); - - return 0; - -fail: - free(k); - - path_unwatch_one(p, s); - return r; + path_spec_dump(s, f, prefix); } static void path_unwatch(Path *p) { @@ -260,7 +389,7 @@ static void path_unwatch(Path *p) { assert(p); LIST_FOREACH(spec, s, p->specs) - path_unwatch_one(p, s); + path_spec_unwatch(s, UNIT(p)); } static int path_watch(Path *p) { @@ -270,7 +399,7 @@ static int path_watch(Path *p) { assert(p); LIST_FOREACH(spec, s, p->specs) - if ((r = path_watch_one(p, s)) < 0) + if ((r = path_spec_watch(s, UNIT(p))) < 0) return r; return 0; @@ -289,7 +418,7 @@ static void path_set_state(Path *p, PathState state) { if (state != old_state) log_debug("%s changed %s -> %s", - p->meta.id, + UNIT(p)->id, path_state_to_string(old_state), path_state_to_string(state)); @@ -316,13 +445,13 @@ static int path_coldplug(Unit *u) { return 0; } -static void path_enter_dead(Path *p, bool success) { +static void path_enter_dead(Path *p, PathResult f) { assert(p); - if (!success) - p->failure = true; + if (f != PATH_SUCCESS) + p->result = f; - path_set_state(p, p->failure ? PATH_FAILED : PATH_DEAD); + path_set_state(p, p->result != PATH_SUCCESS ? PATH_FAILED : PATH_DEAD); } static void path_enter_running(Path *p) { @@ -333,10 +462,10 @@ static void path_enter_running(Path *p) { dbus_error_init(&error); /* Don't start job if we are supposed to go down */ - if (p->meta.job && p->meta.job->type == JOB_STOP) + if (UNIT(p)->job && UNIT(p)->job->type == JOB_STOP) return; - if ((r = manager_add_job(p->meta.manager, JOB_START, p->unit, JOB_REPLACE, true, &error, NULL)) < 0) + if ((r = manager_add_job(UNIT(p)->manager, JOB_START, UNIT_DEREF(p->unit), JOB_REPLACE, true, &error, NULL)) < 0) goto fail; p->inotify_triggered = false; @@ -348,8 +477,8 @@ static void path_enter_running(Path *p) { return; fail: - log_warning("%s failed to queue unit startup job: %s", p->meta.id, bus_error(&error, r)); - path_enter_dead(p, false); + log_warning("%s failed to queue unit startup job: %s", UNIT(p)->id, bus_error(&error, r)); + path_enter_dead(p, PATH_FAILURE_RESOURCES); dbus_error_free(&error); } @@ -361,37 +490,7 @@ static bool path_check_good(Path *p, bool initial) { assert(p); LIST_FOREACH(spec, s, p->specs) { - - switch (s->type) { - - case PATH_EXISTS: - good = access(s->path, F_OK) >= 0; - break; - - case PATH_EXISTS_GLOB: - good = glob_exists(s->path) > 0; - break; - - case PATH_DIRECTORY_NOT_EMPTY: { - int k; - - k = dir_is_empty(s->path); - good = !(k == -ENOENT || k > 0); - break; - } - - case PATH_CHANGED: { - bool b; - - b = access(s->path, F_OK) >= 0; - good = !initial && b != s->previous_exists; - s->previous_exists = b; - break; - } - - default: - ; - } + good = path_spec_check_good(s, initial); if (good) break; @@ -405,7 +504,7 @@ static void path_enter_waiting(Path *p, bool initial, bool recheck) { if (recheck) if (path_check_good(p, initial)) { - log_debug("%s got triggered.", p->meta.id); + log_debug("%s got triggered.", UNIT(p)->id); path_enter_running(p); return; } @@ -419,7 +518,7 @@ static void path_enter_waiting(Path *p, bool initial, bool recheck) { if (recheck) if (path_check_good(p, false)) { - log_debug("%s got triggered.", p->meta.id); + log_debug("%s got triggered.", UNIT(p)->id); path_enter_running(p); return; } @@ -428,8 +527,8 @@ static void path_enter_waiting(Path *p, bool initial, bool recheck) { return; fail: - log_warning("%s failed to enter waiting state: %s", p->meta.id, strerror(-r)); - path_enter_dead(p, false); + log_warning("%s failed to enter waiting state: %s", UNIT(p)->id, strerror(-r)); + path_enter_dead(p, PATH_FAILURE_RESOURCES); } static void path_mkdir(Path *p) { @@ -440,15 +539,8 @@ static void path_mkdir(Path *p) { if (!p->make_directory) return; - LIST_FOREACH(spec, s, p->specs) { - int r; - - if (s->type == PATH_EXISTS || s->type == PATH_EXISTS_GLOB) - continue; - - if ((r = mkdir_p(s->path, p->directory_mode)) < 0) - log_warning("mkdir(%s) failed: %s", s->path, strerror(-r)); - } + LIST_FOREACH(spec, s, p->specs) + path_spec_mkdir(s, p->directory_mode); } static int path_start(Unit *u) { @@ -457,12 +549,12 @@ static int path_start(Unit *u) { assert(p); assert(p->state == PATH_DEAD || p->state == PATH_FAILED); - if (p->unit->meta.load_state != UNIT_LOADED) + if (UNIT_DEREF(p->unit)->load_state != UNIT_LOADED) return -ENOENT; path_mkdir(p); - p->failure = false; + p->result = PATH_SUCCESS; path_enter_waiting(p, true, true); return 0; @@ -474,7 +566,7 @@ static int path_stop(Unit *u) { assert(p); assert(p->state == PATH_WAITING || p->state == PATH_RUNNING); - path_enter_dead(p, true); + path_enter_dead(p, PATH_SUCCESS); return 0; } @@ -486,6 +578,7 @@ static int path_serialize(Unit *u, FILE *f, FDSet *fds) { assert(fds); unit_serialize_item(u, f, "state", path_state_to_string(p->state)); + unit_serialize_item(u, f, "result", path_result_to_string(p->result)); return 0; } @@ -505,6 +598,16 @@ static int path_deserialize_item(Unit *u, const char *key, const char *value, FD log_debug("Failed to parse state value %s", value); else p->deserialized_state = state; + + } else if (streq(key, "result")) { + PathResult f; + + f = path_result_from_string(value); + if (f < 0) + log_debug("Failed to parse result value %s", value); + else if (f != PATH_SUCCESS) + p->result = f; + } else log_debug("Unknown serialization key '%s'", key); @@ -525,12 +628,8 @@ static const char *path_sub_state_to_string(Unit *u) { static void path_fd_event(Unit *u, int fd, uint32_t events, Watch *w) { Path *p = PATH(u); - int l; - ssize_t k; - uint8_t *buf = NULL; - struct inotify_event *e; PathSpec *s; - bool changed; + int changed; assert(p); assert(fd >= 0); @@ -539,15 +638,10 @@ static void path_fd_event(Unit *u, int fd, uint32_t events, Watch *w) { p->state != PATH_RUNNING) return; - /* log_debug("inotify wakeup on %s.", u->meta.id); */ - - if (events != EPOLLIN) { - log_error("Got Invalid poll event on inotify."); - goto fail; - } + /* log_debug("inotify wakeup on %s.", u->id); */ LIST_FOREACH(spec, s, p->specs) - if (s->inotify_fd == fd) + if (path_spec_owns_inotify_fd(s, fd)) break; if (!s) { @@ -555,92 +649,46 @@ static void path_fd_event(Unit *u, int fd, uint32_t events, Watch *w) { goto fail; } - if (ioctl(fd, FIONREAD, &l) < 0) { - log_error("FIONREAD failed: %s", strerror(errno)); - goto fail; - } - - assert(l > 0); - - if (!(buf = malloc(l))) { - log_error("Failed to allocate buffer: %s", strerror(-ENOMEM)); + changed = path_spec_fd_event(s, events); + if (changed < 0) goto fail; - } - - if ((k = read(fd, buf, l)) < 0) { - log_error("Failed to read inotify event: %s", strerror(-errno)); - goto fail; - } /* If we are already running, then remember that one event was * dispatched so that we restart the service only if something * actually changed on disk */ p->inotify_triggered = true; - e = (struct inotify_event*) buf; - - changed = false; - while (k > 0) { - size_t step; - - if (s->type == PATH_CHANGED && s->primary_wd == e->wd) - changed = true; - - step = sizeof(struct inotify_event) + e->len; - assert(step <= (size_t) k); - - e = (struct inotify_event*) ((uint8_t*) e + step); - k -= step; - } - if (changed) path_enter_running(p); else path_enter_waiting(p, false, true); - free(buf); - return; fail: - free(buf); - path_enter_dead(p, false); + path_enter_dead(p, PATH_FAILURE_RESOURCES); } void path_unit_notify(Unit *u, UnitActiveState new_state) { - char *n; - int r; Iterator i; + Unit *k; - if (u->meta.type == UNIT_PATH) + if (u->type == UNIT_PATH) return; - SET_FOREACH(n, u->meta.names, i) { - char *k; - Unit *t; + SET_FOREACH(k, u->dependencies[UNIT_TRIGGERED_BY], i) { Path *p; - if (!(k = unit_name_change_suffix(n, ".path"))) { - r = -ENOMEM; - goto fail; - } - - t = manager_get_unit(u->meta.manager, k); - free(k); - - if (!t) + if (k->type != UNIT_PATH) continue; - if (t->meta.load_state != UNIT_LOADED) + if (k->load_state != UNIT_LOADED) continue; - p = PATH(t); - - if (p->unit != u) - continue; + p = PATH(k); if (p->state == PATH_RUNNING && new_state == UNIT_INACTIVE) { - log_debug("%s got notified about unit deactivation.", p->meta.id); + log_debug("%s got notified about unit deactivation.", UNIT(p)->id); /* Hmm, so inotify was triggered since the * last activation, so I guess we need to @@ -648,11 +696,6 @@ void path_unit_notify(Unit *u, UnitActiveState new_state) { path_enter_waiting(p, false, p->inotify_triggered); } } - - return; - -fail: - log_error("Failed find path unit: %s", strerror(-r)); } static void path_reset_failed(Unit *u) { @@ -663,7 +706,7 @@ static void path_reset_failed(Unit *u) { if (p->state == PATH_FAILED) path_set_state(p, PATH_DEAD); - p->failure = false; + p->result = PATH_SUCCESS; } static const char* const path_state_table[_PATH_STATE_MAX] = { @@ -679,13 +722,26 @@ static const char* const path_type_table[_PATH_TYPE_MAX] = { [PATH_EXISTS] = "PathExists", [PATH_EXISTS_GLOB] = "PathExistsGlob", [PATH_CHANGED] = "PathChanged", + [PATH_MODIFIED] = "PathModified", [PATH_DIRECTORY_NOT_EMPTY] = "DirectoryNotEmpty" }; DEFINE_STRING_TABLE_LOOKUP(path_type, PathType); +static const char* const path_result_table[_PATH_RESULT_MAX] = { + [PATH_SUCCESS] = "success", + [PATH_FAILURE_RESOURCES] = "resources" +}; + +DEFINE_STRING_TABLE_LOOKUP(path_result, PathResult); + const UnitVTable path_vtable = { .suffix = ".path", + .object_size = sizeof(Path), + .sections = + "Unit\0" + "Path\0" + "Install\0", .init = path_init, .done = path_done, @@ -709,5 +765,6 @@ const UnitVTable path_vtable = { .reset_failed = path_reset_failed, .bus_interface = "org.freedesktop.systemd1.Path", - .bus_message_handler = bus_path_message_handler + .bus_message_handler = bus_path_message_handler, + .bus_invalidating_properties = bus_path_invalidating_properties }; diff --git a/src/path.h b/src/path.h index 116fc63ff7..efb6b5eb44 100644 --- a/src/path.h +++ b/src/path.h @@ -41,6 +41,7 @@ typedef enum PathType { PATH_EXISTS_GLOB, PATH_DIRECTORY_NOT_EMPTY, PATH_CHANGED, + PATH_MODIFIED, _PATH_TYPE_MAX, _PATH_TYPE_INVALID = -1 } PathType; @@ -57,23 +58,39 @@ typedef struct PathSpec { int primary_wd; bool previous_exists; - } PathSpec; +int path_spec_watch(PathSpec *s, Unit *u); +void path_spec_unwatch(PathSpec *s, Unit *u); +int path_spec_fd_event(PathSpec *s, uint32_t events); +void path_spec_done(PathSpec *s); + +static inline bool path_spec_owns_inotify_fd(PathSpec *s, int fd) { + return s->inotify_fd == fd; +} + +typedef enum PathResult { + PATH_SUCCESS, + PATH_FAILURE_RESOURCES, + _PATH_RESULT_MAX, + _PATH_RESULT_INVALID = -1 +} PathResult; + struct Path { - Meta meta; + Unit meta; LIST_HEAD(PathSpec, specs); - Unit *unit; + UnitRef unit; PathState state, deserialized_state; - bool failure; bool inotify_triggered; bool make_directory; mode_t directory_mode; + + PathResult result; }; void path_unit_notify(Unit *u, UnitActiveState new_state); @@ -90,4 +107,7 @@ PathState path_state_from_string(const char *s); const char* path_type_to_string(PathType i); PathType path_type_from_string(const char *s); +const char* path_result_to_string(PathResult i); +PathResult path_result_from_string(const char *s); + #endif diff --git a/src/polkit.c b/src/polkit.c index 5b67480fe5..3acbdc6135 100644 --- a/src/polkit.c +++ b/src/polkit.c @@ -82,6 +82,7 @@ int verify_polkit( DBusMessage *request, const char *action, bool interactive, + bool *_challenge, DBusError *error) { DBusMessage *m = NULL, *reply = NULL; @@ -94,7 +95,7 @@ int verify_polkit( uint64_t starttime_u64; DBusMessageIter iter_msg, iter_struct, iter_array, iter_dict, iter_variant; int r; - dbus_bool_t authorized = FALSE; + dbus_bool_t authorized = FALSE, challenge = FALSE; assert(c); assert(request); @@ -176,7 +177,21 @@ int verify_polkit( dbus_message_iter_get_basic(&iter_struct, &authorized); - r = authorized ? 0 : -EPERM; + if (!dbus_message_iter_next(&iter_struct) || + dbus_message_iter_get_arg_type(&iter_struct) != DBUS_TYPE_BOOLEAN) { + r = -EIO; + goto finish; + } + + dbus_message_iter_get_basic(&iter_struct, &challenge); + + if (authorized) + r = 1; + else if (_challenge) { + *_challenge = !!challenge; + r = 0; + } else + r = -EPERM; finish: diff --git a/src/polkit.h b/src/polkit.h index fc4e77118f..0d08194b26 100644 --- a/src/polkit.h +++ b/src/polkit.h @@ -30,6 +30,7 @@ int verify_polkit( DBusMessage *request, const char *action, bool interactive, + bool *challenge, DBusError *error); #endif diff --git a/src/quotacheck.c b/src/quotacheck.c index ba12b27caa..b6648b8369 100644 --- a/src/quotacheck.c +++ b/src/quotacheck.c @@ -26,6 +26,7 @@ #include <unistd.h> #include "util.h" +#include "virt.h" static bool arg_skip = false; static bool arg_force = false; @@ -53,7 +54,7 @@ static int parse_proc_cmdline(void) { arg_skip = true; else if (startswith(w, "quotacheck.mode")) log_warning("Invalid quotacheck.mode= parameter. Ignoring."); -#if defined(TARGET_FEDORA) || defined(TARGET_MANDRIVA) +#if defined(TARGET_FEDORA) || defined(TARGET_MANDRIVA) || defined(TARGET_MAGEIA) else if (strneq(w, "forcequotacheck", l)) arg_force = true; #endif @@ -64,8 +65,8 @@ static int parse_proc_cmdline(void) { } static void test_files(void) { -#if defined(TARGET_FEDORA) || defined(TARGET_MANDRIVA) - /* This exists only on Fedora or Mandriva */ +#if defined(TARGET_FEDORA) || defined(TARGET_MANDRIVA) || defined(TARGET_MAGEIA) + /* This exists only on Fedora, Mandriva or Mageia */ if (access("/forcequotacheck", F_OK) >= 0) arg_force = true; #endif @@ -86,10 +87,12 @@ int main(int argc, char *argv[]) { return EXIT_FAILURE; } - log_set_target(LOG_TARGET_SYSLOG_OR_KMSG); + log_set_target(LOG_TARGET_AUTO); log_parse_environment(); log_open(); + umask(0022); + parse_proc_cmdline(); test_files(); diff --git a/src/random-seed.c b/src/random-seed.c index 054233e660..8b43bacadc 100644 --- a/src/random-seed.c +++ b/src/random-seed.c @@ -43,13 +43,19 @@ int main(int argc, char *argv[]) { return EXIT_FAILURE; } - log_set_target(LOG_TARGET_SYSLOG_OR_KMSG); + log_set_target(LOG_TARGET_AUTO); log_parse_environment(); log_open(); + umask(0022); + /* Read pool size, if possible */ if ((f = fopen("/proc/sys/kernel/random/poolsize", "re"))) { - fscanf(f, "%zu", &buf_size); + if (fscanf(f, "%zu", &buf_size) > 0) { + /* poolsize is in bits on 2.6, but we want bytes */ + buf_size /= 8; + } + fclose(f); } diff --git a/src/ratelimit.c b/src/ratelimit.c index 1ddc83187f..93157c7a2e 100644 --- a/src/ratelimit.c +++ b/src/ratelimit.c @@ -30,11 +30,12 @@ bool ratelimit_test(RateLimit *r) { usec_t ts; - ts = now(CLOCK_MONOTONIC); - assert(r); - assert(r->interval > 0); - assert(r->burst > 0); + + if (r->interval <= 0 || r->burst <= 0) + return true; + + ts = now(CLOCK_MONOTONIC); if (r->begin <= 0 || r->begin + r->interval < ts) { diff --git a/src/ratelimit.h b/src/ratelimit.h index a44ef70db4..a6443e7f87 100644 --- a/src/ratelimit.h +++ b/src/ratelimit.h @@ -46,7 +46,7 @@ typedef struct RateLimit { _r->burst = (_burst); \ _r->num = 0; \ _r->begin = 0; \ - } while (false); + } while (false) bool ratelimit_test(RateLimit *r); diff --git a/src/rc-local-generator.c b/src/rc-local-generator.c new file mode 100644 index 0000000000..56785cf402 --- /dev/null +++ b/src/rc-local-generator.c @@ -0,0 +1,107 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + Copyright 2011 Michal Schmidt + + 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 <errno.h> +#include <stdio.h> +#include <unistd.h> + +#include "log.h" +#include "util.h" + +#if defined(TARGET_FEDORA) || defined(TARGET_MANDRIVA) || defined(TARGET_MAGEIA) +#define SCRIPT_PATH "/etc/rc.d/rc.local" +#elif defined(TARGET_SUSE) +#define SCRIPT_PATH "/etc/init.d/boot.local" +#endif + +const char *arg_dest = "/tmp"; + +static int add_symlink(const char *service) { + char *from = NULL, *to = NULL; + int r; + + assert(service); + + asprintf(&from, SYSTEM_DATA_UNIT_PATH "/%s", service); + asprintf(&to, "%s/multi-user.target.wants/%s", arg_dest, service); + + if (!from || !to) { + log_error("Out of memory"); + r = -ENOMEM; + goto finish; + } + + mkdir_parents(to, 0755); + + r = symlink(from, to); + if (r < 0) { + if (errno == EEXIST) + r = 0; + else { + log_error("Failed to create symlink from %s to %s: %m", from, to); + r = -errno; + } + } + +finish: + + free(from); + free(to); + + return r; +} + +static bool file_is_executable(const char *f) { + struct stat st; + + if (stat(f, &st) < 0) + return false; + + return S_ISREG(st.st_mode) && (st.st_mode & 0111); +} + +int main(int argc, char *argv[]) { + + int r = EXIT_SUCCESS; + + if (argc > 2) { + log_error("This program takes one or no arguments."); + return EXIT_FAILURE; + } + + log_set_target(LOG_TARGET_AUTO); + log_parse_environment(); + log_open(); + + if (argc > 1) + arg_dest = argv[1]; + + if (file_is_executable(SCRIPT_PATH)) { + log_debug("Automatically adding rc-local.service."); + + if (add_symlink("rc-local.service") < 0) + r = EXIT_FAILURE; + + } + + return r; +} diff --git a/src/readahead/Makefile b/src/readahead/Makefile new file mode 120000 index 0000000000..d0b0e8e008 --- /dev/null +++ b/src/readahead/Makefile @@ -0,0 +1 @@ +../Makefile
\ No newline at end of file diff --git a/src/readahead-collect.c b/src/readahead/readahead-collect.c index 20881b3944..7e6c243b5e 100644 --- a/src/readahead-collect.c +++ b/src/readahead/readahead-collect.c @@ -43,12 +43,14 @@ #include <getopt.h> #include <sys/inotify.h> +#include <systemd/sd-daemon.h> + #include "missing.h" #include "util.h" #include "set.h" -#include "sd-daemon.h" #include "ioprio.h" #include "readahead-common.h" +#include "virt.h" /* fixme: * @@ -652,10 +654,12 @@ int main(int argc, char *argv[]) { int r; const char *root; - log_set_target(LOG_TARGET_SYSLOG_OR_KMSG); + log_set_target(LOG_TARGET_AUTO); log_parse_environment(); log_open(); + umask(0022); + if ((r = parse_argv(argc, argv)) <= 0) return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; diff --git a/src/readahead-common.c b/src/readahead/readahead-common.c index f0d57b4c6e..67214ec379 100644 --- a/src/readahead-common.c +++ b/src/readahead/readahead-common.c @@ -212,12 +212,10 @@ finish: int bump_request_nr(const char *p) { struct stat st; - struct udev *udev = NULL; - struct udev_device *udev_device = NULL, *look_at = NULL; - const char *nr_requests; uint64_t u; - char nr[64], *ap = NULL; + char *ap = NULL, *line = NULL; int r; + dev_t d; assert(p); @@ -227,54 +225,45 @@ int bump_request_nr(const char *p) { if (major(st.st_dev) == 0) return 0; - if (!(udev = udev_new())) - return -ENOMEM; + d = st.st_dev; + block_get_whole_disk(d, &d); - if (!(udev_device = udev_device_new_from_devnum(udev, 'b', st.st_dev))) { - r = -ENOENT; + if (asprintf(&ap, "/sys/dev/block/%u:%u/queue/nr_requests", major(d), minor(d)) < 0) { + r= -ENOMEM; goto finish; } - look_at = udev_device; - if (!(nr_requests = udev_device_get_sysattr_value(look_at, "queue/nr_requests"))) { - - /* Hmm, if the block device doesn't have a queue - * subdir, the let's look in the parent */ - look_at = udev_device_get_parent(udev_device); - nr_requests = udev_device_get_sysattr_value(look_at, "queue/nr_requests"); - } - - if (!nr_requests) { - r = -ENOENT; + r = read_one_line_file(ap, &line); + if (r < 0) { + if (r == -ENOENT) + r = 0; goto finish; } - if (safe_atou64(nr_requests, &u) >= 0 && u >= BUMP_REQUEST_NR) { + r = safe_atou64(line, &u); + if (r >= 0 && u >= BUMP_REQUEST_NR) { r = 0; goto finish; } - if (asprintf(&ap, "%s/queue/nr_requests", udev_device_get_syspath(look_at)) < 0) { + free(line); + line = NULL; + + if (asprintf(&line, "%lu", (unsigned long) BUMP_REQUEST_NR) < 0) { r = -ENOMEM; goto finish; } - snprintf(nr, sizeof(nr), "%lu", (unsigned long) BUMP_REQUEST_NR); - - if ((r = write_one_line_file(ap, nr)) < 0) + r = write_one_line_file(ap, line); + if (r < 0) goto finish; - log_info("Bumped block_nr parameter of %s to %lu. This is a temporary hack and should be removed one day.", udev_device_get_devnode(look_at), (unsigned long) BUMP_REQUEST_NR); + log_info("Bumped block_nr parameter of %u:%u to %lu. This is a temporary hack and should be removed one day.", major(d), minor(d), (unsigned long) BUMP_REQUEST_NR); r = 1; finish: - if (udev_device) - udev_device_unref(udev_device); - - if (udev) - udev_unref(udev); - free(ap); + free(line); return r; } diff --git a/src/readahead-common.h b/src/readahead/readahead-common.h index 167df316d9..9547ad201c 100644 --- a/src/readahead-common.h +++ b/src/readahead/readahead-common.h @@ -27,7 +27,7 @@ #include "macro.h" -#define READAHEAD_FILE_SIZE_MAX (128*1024*1024) +#define READAHEAD_FILE_SIZE_MAX (10*1024*1024) int file_verify(int fd, const char *fn, off_t file_size_max, struct stat *st); diff --git a/src/readahead-replay.c b/src/readahead/readahead-replay.c index 0b84528b0e..0c739c82be 100644 --- a/src/readahead-replay.c +++ b/src/readahead/readahead-replay.c @@ -35,12 +35,14 @@ #include <getopt.h> #include <sys/inotify.h> +#include <systemd/sd-daemon.h> + #include "missing.h" #include "util.h" #include "set.h" -#include "sd-daemon.h" #include "ioprio.h" #include "readahead-common.h" +#include "virt.h" static off_t arg_file_size_max = READAHEAD_FILE_SIZE_MAX; @@ -184,7 +186,13 @@ static int replay(const char *root) { if (on_ssd) prio = IOPRIO_PRIO_VALUE(IOPRIO_CLASS_IDLE, 0); else - prio = IOPRIO_PRIO_VALUE(IOPRIO_CLASS_RT, 7); + /* We are not using RT here, since we'd starve IO that + we didn't record (which is for example blkid, since + its disk accesses go directly to the block device and + are thus not visible in fallocate) to death. However, + we do ask for an IO prio that is slightly higher than + the default (which is BE. 4) */ + prio = IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE, 2); if (ioprio_set(IOPRIO_WHO_PROCESS, getpid(), prio) < 0) log_warning("Failed to set IDLE IO priority class: %m"); @@ -336,10 +344,12 @@ int main(int argc, char*argv[]) { int r; const char *root; - log_set_target(LOG_TARGET_SYSLOG_OR_KMSG); + log_set_target(LOG_TARGET_AUTO); log_parse_environment(); log_open(); + umask(0022); + if ((r = parse_argv(argc, argv)) <= 0) return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; diff --git a/src/sd-readahead.c b/src/readahead/sd-readahead.c index c5cfe67107..a3340666dd 100644 --- a/src/sd-readahead.c +++ b/src/readahead/sd-readahead.c @@ -37,6 +37,18 @@ #include "sd-readahead.h" +#if (__GNUC__ >= 4) +#ifdef SD_EXPORT_SYMBOLS +/* Export symbols */ +#define _sd_export_ __attribute__ ((visibility("default"))) +#else +/* Don't export the symbols */ +#define _sd_export_ __attribute__ ((visibility("hidden"))) +#endif +#else +#define _sd_export_ +#endif + static int touch(const char *path) { #if !defined(DISABLE_SYSTEMD) && defined(__linux__) @@ -60,7 +72,7 @@ static int touch(const char *path) { return 0; } -int sd_readahead(const char *action) { +_sd_export_ int sd_readahead(const char *action) { if (!action) return -EINVAL; diff --git a/src/remount-api-vfs.c b/src/remount-api-vfs.c index 5b1872833a..3e146ebb5c 100644 --- a/src/remount-api-vfs.c +++ b/src/remount-api-vfs.c @@ -48,16 +48,20 @@ int main(int argc, char *argv[]) { return EXIT_FAILURE; } - log_set_target(LOG_TARGET_SYSLOG_OR_KMSG); + log_set_target(LOG_TARGET_AUTO); log_parse_environment(); log_open(); - if (!(f = setmntent("/etc/fstab", "r"))) { + umask(0022); + + f = setmntent("/etc/fstab", "r"); + if (!f) { log_error("Failed to open /etc/fstab: %m"); goto finish; } - if (!(pids = hashmap_new(trivial_hash_func, trivial_compare_func))) { + pids = hashmap_new(trivial_hash_func, trivial_compare_func); + if (!pids) { log_error("Failed to allocate set"); goto finish; } @@ -74,9 +78,10 @@ int main(int argc, char *argv[]) { log_debug("Remounting %s", me->mnt_dir); - if ((pid = fork()) < 0) { + pid = fork(); + if (pid < 0) { log_error("Failed to fork: %m"); - ret = 1; + ret = EXIT_FAILURE; continue; } @@ -99,8 +104,15 @@ int main(int argc, char *argv[]) { /* Parent */ s = strdup(me->mnt_dir); + if (!s) { + log_error("Out of memory."); + ret = EXIT_FAILURE; + continue; + } + - if ((k = hashmap_put(pids, UINT_TO_PTR(pid), s)) < 0) { + k = hashmap_put(pids, UINT_TO_PTR(pid), s); + if (k < 0) { log_error("Failed to add PID to set: %s", strerror(-k)); ret = EXIT_FAILURE; continue; @@ -122,7 +134,8 @@ int main(int argc, char *argv[]) { break; } - if ((s = hashmap_remove(pids, UINT_TO_PTR(si.si_pid)))) { + s = hashmap_remove(pids, UINT_TO_PTR(si.si_pid)); + if (s) { if (!is_clean_exit(si.si_code, si.si_status)) { if (si.si_code == CLD_EXITED) log_error("/bin/mount for %s exited with exit status %i.", s, si.si_status); diff --git a/src/reply-password.c b/src/reply-password.c index bd55e65f3c..3a96049d7f 100644 --- a/src/reply-password.c +++ b/src/reply-password.c @@ -64,7 +64,7 @@ int main(int argc, char *argv[]) { char packet[LINE_MAX]; size_t length; - log_set_target(LOG_TARGET_SYSLOG_OR_KMSG); + log_set_target(LOG_TARGET_AUTO); log_parse_environment(); log_open(); diff --git a/src/sd-daemon.c b/src/sd-daemon.c index e68b70875c..763e079b4e 100644 --- a/src/sd-daemon.c +++ b/src/sd-daemon.c @@ -32,7 +32,11 @@ #include <sys/stat.h> #include <sys/socket.h> #include <sys/un.h> +#ifdef __BIONIC__ +#include <linux/fcntl.h> +#else #include <sys/fcntl.h> +#endif #include <netinet/in.h> #include <stdlib.h> #include <errno.h> diff --git a/src/sd-id128.c b/src/sd-id128.c new file mode 100644 index 0000000000..b4184e1c7e --- /dev/null +++ b/src/sd-id128.c @@ -0,0 +1,221 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2011 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 <errno.h> +#include <fcntl.h> +#include <unistd.h> + +#include "sd-id128.h" + +#include "util.h" +#include "macro.h" + +_public_ char *sd_id128_to_string(sd_id128_t id, char s[33]) { + unsigned n; + + if (!s) + return NULL; + + for (n = 0; n < 16; n++) { + s[n*2] = hexchar(id.bytes[n] >> 4); + s[n*2+1] = hexchar(id.bytes[n] & 0xF); + } + + s[32] = 0; + + return s; +} + +_public_ int sd_id128_from_string(const char s[33], sd_id128_t *ret) { + unsigned n; + sd_id128_t t; + + if (!s) + return -EINVAL; + if (!ret) + return -EINVAL; + + for (n = 0; n < 16; n++) { + int a, b; + + a = unhexchar(s[n*2]); + if (a < 0) + return -EINVAL; + + b = unhexchar(s[n*2+1]); + if (b < 0) + return -EINVAL; + + t.bytes[n] = (a << 4) | b; + } + + if (s[32] != 0) + return -EINVAL; + + *ret = t; + return 0; +} + +static sd_id128_t make_v4_uuid(sd_id128_t id) { + /* Stolen from generate_random_uuid() of drivers/char/random.c + * in the kernel sources */ + + /* Set UUID version to 4 --- truly random generation */ + id.bytes[6] = (id.bytes[6] & 0x0F) | 0x40; + + /* Set the UUID variant to DCE */ + id.bytes[8] = (id.bytes[8] & 0x3F) | 0x80; + + return id; +} + +_public_ int sd_id128_get_machine(sd_id128_t *ret) { + static __thread sd_id128_t saved_machine_id; + static __thread bool saved_machine_id_valid = false; + int fd; + char buf[32]; + ssize_t k; + unsigned j; + sd_id128_t t; + + if (!ret) + return -EINVAL; + + if (saved_machine_id_valid) { + *ret = saved_machine_id; + return 0; + } + + fd = open("/etc/machine-id", O_RDONLY|O_CLOEXEC|O_NOCTTY); + if (fd < 0) + return -errno; + + k = loop_read(fd, buf, 32, false); + close_nointr_nofail(fd); + + if (k < 0) + return (int) k; + + if (k < 32) + return -EIO; + + for (j = 0; j < 16; j++) { + int a, b; + + a = unhexchar(buf[j*2]); + b = unhexchar(buf[j*2+1]); + + if (a < 0 || b < 0) + return -EIO; + + t.bytes[j] = a << 4 | b; + } + + saved_machine_id = t; + saved_machine_id_valid = true; + + *ret = t; + return 0; +} + +_public_ int sd_id128_get_boot(sd_id128_t *ret) { + static __thread sd_id128_t saved_boot_id; + static __thread bool saved_boot_id_valid = false; + int fd; + char buf[36]; + ssize_t k; + unsigned j; + sd_id128_t t; + char *p; + + if (!ret) + return -EINVAL; + + if (saved_boot_id_valid) { + *ret = saved_boot_id; + return 0; + } + + fd = open("/proc/sys/kernel/random/boot_id", O_RDONLY|O_CLOEXEC|O_NOCTTY); + if (fd < 0) + return -errno; + + k = loop_read(fd, buf, 36, false); + close_nointr_nofail(fd); + + if (k < 0) + return (int) k; + + if (k < 36) + return -EIO; + + for (j = 0, p = buf; j < 16; j++) { + int a, b; + + if (*p == '-') + p++; + + a = unhexchar(p[0]); + b = unhexchar(p[1]); + + if (a < 0 || b < 0) + return -EIO; + + t.bytes[j] = a << 4 | b; + + p += 2; + } + + saved_boot_id = t; + saved_boot_id_valid = true; + + *ret = t; + return 0; +} + +_public_ int sd_id128_randomize(sd_id128_t *ret) { + int fd; + ssize_t k; + sd_id128_t t; + + if (!ret) + return -EINVAL; + + fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC|O_NOCTTY); + if (fd < 0) + return -errno; + + k = loop_read(fd, &t, 16, false); + close_nointr_nofail(fd); + + if (k < 0) + return (int) k; + + if (k < 16) + return -EIO; + + /* Turn this into a valid v4 UUID, to be nice. Note that we + * only guarantee this for newly generated UUIDs, not for + * pre-existing ones.*/ + + *ret = make_v4_uuid(t); + return 0; +} diff --git a/src/selinux-setup.c b/src/selinux-setup.c index 620c49e686..a7e1fa4007 100644 --- a/src/selinux-setup.c +++ b/src/selinux-setup.c @@ -30,18 +30,28 @@ #endif #include "selinux-setup.h" +#include "mount-setup.h" #include "macro.h" #include "util.h" #include "log.h" +#include "label.h" + +int selinux_setup(bool *loaded_policy) { -int selinux_setup(char *const argv[]) { #ifdef HAVE_SELINUX int enforce = 0; - usec_t n; + usec_t before_load, after_load; security_context_t con; + int r; + + assert(loaded_policy); + + /* Make sure getcon() works, which needs /proc and /sys */ + mount_setup_early(); - /* Already initialized? */ - if (getcon_raw(&con) == 0) { + /* Already initialized by somebody else? */ + r = getcon_raw(&con); + if (r == 0) { bool initialized; initialized = !streq(con, "kernel"); @@ -51,33 +61,50 @@ int selinux_setup(char *const argv[]) { return 0; } - /* Before we load the policy we create a flag file to ensure - * that after the reexec we iterate through /run and /dev to - * relabel things. */ - touch("/dev/.systemd-relabel-run-dev"); + /* Make sure we have no fds open while loading the policy and + * transitioning */ + log_close(); - n = now(CLOCK_MONOTONIC); - if (selinux_init_load_policy(&enforce) == 0) { - char buf[FORMAT_TIMESPAN_MAX]; + /* Now load the policy */ + before_load = now(CLOCK_MONOTONIC); + r = selinux_init_load_policy(&enforce); - n = now(CLOCK_MONOTONIC) - n; - log_info("Successfully loaded SELinux policy in %s, reexecuting.", - format_timespan(buf, sizeof(buf), n)); + if (r == 0) { + char timespan[FORMAT_TIMESPAN_MAX]; + char *label; - /* FIXME: Ideally we'd just call setcon() here instead - * of having to reexecute ourselves here. */ + label_retest_selinux(); - execv(SYSTEMD_BINARY_PATH, argv); - log_error("Failed to reexecute: %m"); - return -errno; + /* Transition to the new context */ + r = label_get_create_label_from_exe(SYSTEMD_BINARY_PATH, &label); + if (r < 0 || label == NULL) { + log_open(); + log_error("Failed to compute init label, ignoring."); + } else { + r = setcon(label); - } else { - log_full(enforce > 0 ? LOG_ERR : LOG_WARNING, "Failed to load SELinux policy."); + log_open(); + if (r < 0) + log_error("Failed to transition into init label '%s', ignoring.", label); + + label_free(label); + } - unlink("/dev/.systemd-relabel-run-dev"); + after_load = now(CLOCK_MONOTONIC); + + log_info("Successfully loaded SELinux policy in %s.", + format_timespan(timespan, sizeof(timespan), after_load - before_load)); + + *loaded_policy = true; + + } else { + log_open(); - if (enforce > 0) + if (enforce > 0) { + log_error("Failed to load SELinux policy. Freezing."); return -EIO; + } else + log_debug("Unable to load SELinux policy. Ignoring."); } #endif diff --git a/src/selinux-setup.h b/src/selinux-setup.h index 53dbb65f3e..6b8fe00b7d 100644 --- a/src/selinux-setup.h +++ b/src/selinux-setup.h @@ -22,6 +22,8 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -int selinux_setup(char *const argv[]); +#include <stdbool.h> + +int selinux_setup(bool *loaded_policy); #endif diff --git a/src/service.c b/src/service.c index 646c093906..ec2725a7ba 100644 --- a/src/service.c +++ b/src/service.c @@ -23,7 +23,9 @@ #include <signal.h> #include <dirent.h> #include <unistd.h> +#include <sys/reboot.h> +#include "manager.h" #include "unit.h" #include "service.h" #include "load-fragment.h" @@ -65,7 +67,7 @@ static const struct { { "boot.d", SPECIAL_SYSINIT_TARGET, RUNLEVEL_SYSINIT }, #endif -#if defined(TARGET_DEBIAN) || defined(TARGET_UBUNTU) || defined(TARGET_FRUGALWARE) || defined(TARGET_ANGSTROM) +#if defined(TARGET_DEBIAN) || defined(TARGET_UBUNTU) || defined(TARGET_ANGSTROM) /* Debian style rcS.d */ { "rcS.d", SPECIAL_SYSINIT_TARGET, RUNLEVEL_SYSINIT }, #endif @@ -83,7 +85,7 @@ static const struct { #define RUNLEVELS_UP "12345" /* #define RUNLEVELS_DOWN "06" */ -/* #define RUNLEVELS_BOOT "bBsS" */ +#define RUNLEVELS_BOOT "bBsS" #endif static const UnitActiveState state_translation_table[_SERVICE_STATE_MAX] = { @@ -108,10 +110,13 @@ static void service_init(Unit *u) { Service *s = SERVICE(u); assert(u); - assert(u->meta.load_state == UNIT_STUB); + assert(u->load_state == UNIT_STUB); s->timeout_usec = DEFAULT_TIMEOUT_USEC; s->restart_usec = DEFAULT_RESTART_USEC; + + s->watchdog_watch.type = WATCH_INVALID; + s->timer_watch.type = WATCH_INVALID; #ifdef HAVE_SYSV_COMPAT s->sysv_start_priority = -1; @@ -121,10 +126,8 @@ static void service_init(Unit *u) { s->guess_main_pid = true; exec_context_init(&s->exec_context); - s->exec_context.std_output = u->meta.manager->default_std_output; - s->exec_context.std_error = u->meta.manager->default_std_error; - RATELIMIT_INIT(s->ratelimit, 10*USEC_PER_SEC, 5); + RATELIMIT_INIT(s->start_limit, 10*USEC_PER_SEC, 5); s->control_command_id = _SERVICE_EXEC_COMMAND_INVALID; } @@ -149,6 +152,17 @@ static void service_unwatch_main_pid(Service *s) { s->main_pid = 0; } +static void service_unwatch_pid_file(Service *s) { + if (!s->pid_file_pathspec) + return; + + log_debug("Stopping watch for %s's PID file %s", UNIT(s)->id, s->pid_file_pathspec->path); + path_spec_unwatch(s->pid_file_pathspec, UNIT(s)); + path_spec_done(s->pid_file_pathspec); + free(s->pid_file_pathspec); + s->pid_file_pathspec = NULL; +} + static int service_set_main_pid(Service *s, pid_t pid) { pid_t ppid; @@ -165,7 +179,7 @@ static int service_set_main_pid(Service *s, pid_t pid) { if (get_parent_of_pid(pid, &ppid) >= 0 && ppid != getpid()) { log_warning("%s: Supervising process %lu which is not our child. We'll most likely not notice when it exits.", - s->meta.id, (unsigned long) pid); + UNIT(s)->id, (unsigned long) pid); s->main_pid_alien = true; } else @@ -189,11 +203,49 @@ static void service_close_socket_fd(Service *s) { static void service_connection_unref(Service *s) { assert(s); - if (!s->accept_socket) + if (!UNIT_DEREF(s->accept_socket)) + return; + + socket_connection_unref(SOCKET(UNIT_DEREF(s->accept_socket))); + unit_ref_unset(&s->accept_socket); +} + +static void service_stop_watchdog(Service *s) { + assert(s); + + unit_unwatch_timer(UNIT(s), &s->watchdog_watch); + s->watchdog_timestamp.realtime = 0; + s->watchdog_timestamp.monotonic = 0; +} + +static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart); + +static void service_handle_watchdog(Service *s) { + usec_t offset; + int r; + + assert(s); + + if (s->watchdog_usec == 0) return; - socket_connection_unref(s->accept_socket); - s->accept_socket = NULL; + offset = now(CLOCK_MONOTONIC) - s->watchdog_timestamp.monotonic; + if (offset >= s->watchdog_usec) { + log_error("%s watchdog timeout!", UNIT(s)->id); + service_enter_dead(s, SERVICE_FAILURE_WATCHDOG, true); + return; + } + + r = unit_watch_timer(UNIT(s), s->watchdog_usec - offset, &s->watchdog_watch); + if (r < 0) + log_warning("%s failed to install watchdog timer: %s", UNIT(s)->id, strerror(-r)); +} + +static void service_reset_watchdog(Service *s) { + assert(s); + + dual_timestamp_get(&s->watchdog_timestamp); + service_handle_watchdog(s); } static void service_done(Unit *u) { @@ -224,9 +276,10 @@ static void service_done(Unit *u) { * our resources */ service_unwatch_main_pid(s); service_unwatch_control_pid(s); + service_unwatch_pid_file(s); if (s->bus_name) { - unit_unwatch_bus_name(UNIT(u), s->bus_name); + unit_unwatch_bus_name(u, s->bus_name); free(s->bus_name); s->bus_name = NULL; } @@ -234,7 +287,9 @@ static void service_done(Unit *u) { service_close_socket_fd(s); service_connection_unref(s); - set_free(s->configured_sockets); + unit_ref_unset(&s->accept_socket); + + service_stop_watchdog(s); unit_unwatch_timer(u, &s->timer_watch); } @@ -279,7 +334,8 @@ static int sysv_translate_facility(const char *name, const char *filename, char static const char * const table[] = { /* LSB defined facilities */ "local_fs", SPECIAL_LOCAL_FS_TARGET, -#ifndef TARGET_MANDRIVA +#if defined(TARGET_MANDRIVA) || defined(TARGET_MAGEIA) +#else /* Due to unfortunate name selection in Mandriva, * $network is provided by network-up which is ordered * after network which actually starts interfaces. @@ -361,7 +417,7 @@ finish: } static int sysv_fix_order(Service *s) { - Meta *other; + Unit *other; int r; assert(s); @@ -372,17 +428,17 @@ static int sysv_fix_order(Service *s) { /* For each pair of services where at least one lacks a LSB * header, we use the start priority value to order things. */ - LIST_FOREACH(units_by_type, other, s->meta.manager->units_by_type[UNIT_SERVICE]) { + LIST_FOREACH(units_by_type, other, UNIT(s)->manager->units_by_type[UNIT_SERVICE]) { Service *t; UnitDependency d; bool special_s, special_t; - t = (Service*) other; + t = SERVICE(other); if (s == t) continue; - if (t->meta.load_state != UNIT_LOADED) + if (UNIT(t)->load_state != UNIT_LOADED) continue; if (t->sysv_start_priority < 0) @@ -390,8 +446,8 @@ static int sysv_fix_order(Service *s) { /* If both units have modern headers we don't care * about the priorities */ - if ((s->meta.fragment_path || s->sysv_has_lsb) && - (t->meta.fragment_path || t->sysv_has_lsb)) + if ((UNIT(s)->fragment_path || s->sysv_has_lsb) && + (UNIT(t)->fragment_path || t->sysv_has_lsb)) continue; special_s = s->sysv_runlevels && !chars_intersect(RUNLEVELS_UP, s->sysv_runlevels); @@ -497,7 +553,7 @@ static int service_load_sysv_path(Service *s, const char *path) { s->sysv_mtime = timespec_load(&st.st_mtim); if (null_or_empty(&st)) { - u->meta.load_state = UNIT_MASKED; + u->load_state = UNIT_MASKED; r = 0; goto finish; } @@ -635,7 +691,7 @@ static int service_load_sysv_path(Service *s, const char *path) { char *d = NULL; if (chkconfig_description) - asprintf(&d, "%s %s", chkconfig_description, j); + d = join(chkconfig_description, " ", j, NULL); else d = strdup(j); @@ -783,19 +839,6 @@ static int service_load_sysv_path(Service *s, const char *path) { free(short_description); short_description = d; - } else if (startswith_no_case(t, "X-Interactive:")) { - int b; - - if ((b = parse_boolean(strstrip(t+14))) < 0) { - log_warning("[%s:%u] Couldn't parse interactive flag. Ignoring.", path, line); - continue; - } - - if (b) - s->exec_context.std_input = EXEC_INPUT_TTY; - else - s->exec_context.std_input = EXEC_INPUT_NULL; - } else if (state == LSB_DESCRIPTION) { if (startswith(l, "#\t") || startswith(l, "# ")) { @@ -805,7 +848,7 @@ static int service_load_sysv_path(Service *s, const char *path) { char *d = NULL; if (long_description) - asprintf(&d, "%s %s", long_description, t); + d = join(long_description, " ", t, NULL); else d = strdup(j); @@ -826,6 +869,13 @@ static int service_load_sysv_path(Service *s, const char *path) { if ((r = sysv_exec_commands(s)) < 0) goto finish; + if (s->sysv_runlevels && + chars_intersect(RUNLEVELS_BOOT, s->sysv_runlevels) && + chars_intersect(RUNLEVELS_UP, s->sysv_runlevels)) { + /* Service has both boot and "up" runlevels + configured. Kill the "up" ones. */ + delete_chars(s->sysv_runlevels, RUNLEVELS_UP); + } if (s->sysv_runlevels && !chars_intersect(RUNLEVELS_UP, s->sysv_runlevels)) { /* If there a runlevels configured for this service @@ -834,7 +884,7 @@ static int service_load_sysv_path(Service *s, const char *path) { * needed for early boot) and don't create any links * to it. */ - s->meta.default_dependencies = false; + UNIT(s)->default_dependencies = false; /* Don't timeout special services during boot (like fsck) */ s->timeout_usec = 0; @@ -844,10 +894,13 @@ static int service_load_sysv_path(Service *s, const char *path) { /* Special setting for all SysV services */ s->type = SERVICE_FORKING; s->remain_after_exit = !s->pid_file; + s->guess_main_pid = false; s->restart = SERVICE_RESTART_NO; - s->exec_context.std_output = - (s->meta.manager->sysv_console || s->exec_context.std_input == EXEC_INPUT_TTY) - ? EXEC_OUTPUT_TTY : s->meta.manager->default_std_output; + s->exec_context.ignore_sigpipe = false; + + if (UNIT(s)->manager->sysv_console) + s->exec_context.std_output = EXEC_OUTPUT_JOURNAL_AND_CONSOLE; + s->exec_context.kill_mode = KILL_PROCESS; /* We use the long description only if @@ -870,7 +923,7 @@ static int service_load_sysv_path(Service *s, const char *path) { goto finish; } - u->meta.description = d; + u->description = d; } /* The priority that has been set in /etc/rcN.d/ hierarchies @@ -879,7 +932,7 @@ static int service_load_sysv_path(Service *s, const char *path) { if (s->sysv_start_priority_from_rcnd >= 0) s->sysv_start_priority = s->sysv_start_priority_from_rcnd; - u->meta.load_state = UNIT_LOADED; + u->load_state = UNIT_LOADED; r = 0; finish: @@ -917,11 +970,12 @@ static int service_load_sysv_name(Service *s, const char *name) { return -ENOENT; #endif - STRV_FOREACH(p, s->meta.manager->lookup_paths.sysvinit_path) { + STRV_FOREACH(p, UNIT(s)->manager->lookup_paths.sysvinit_path) { char *path; int r; - if (asprintf(&path, "%s/%s", *p, name) < 0) + path = join(*p, "/", name, NULL); + if (!path) return -ENOMEM; assert(endswith(path, ".service")); @@ -930,7 +984,7 @@ static int service_load_sysv_name(Service *s, const char *name) { r = service_load_sysv_path(s, path); #if defined(TARGET_DEBIAN) || defined(TARGET_UBUNTU) || defined(TARGET_ANGSTROM) - if (r >= 0 && s->meta.load_state == UNIT_STUB) { + if (r >= 0 && UNIT(s)->load_state == UNIT_STUB) { /* Try Debian style *.sh source'able init scripts */ strcat(path, ".sh"); r = service_load_sysv_path(s, path); @@ -939,10 +993,11 @@ static int service_load_sysv_name(Service *s, const char *name) { free(path); #ifdef TARGET_SUSE - if (r >= 0 && s->meta.load_state == UNIT_STUB) { + if (r >= 0 && UNIT(s)->load_state == UNIT_STUB) { /* Try SUSE style boot.* init scripts */ - if (asprintf(&path, "%s/boot.%s", *p, name) < 0) + path = join(*p, "/boot.", name, NULL); + if (!path) return -ENOMEM; /* Drop .service suffix */ @@ -953,10 +1008,11 @@ static int service_load_sysv_name(Service *s, const char *name) { #endif #ifdef TARGET_FRUGALWARE - if (r >= 0 && s->meta.load_state == UNIT_STUB) { + if (r >= 0 && UNIT(s)->load_state == UNIT_STUB) { /* Try Frugalware style rc.* init scripts */ - if (asprintf(&path, "%s/rc.%s", *p, name) < 0) + path = join(*p, "/rc.", name, NULL); + if (!path) return -ENOMEM; /* Drop .service suffix */ @@ -969,7 +1025,7 @@ static int service_load_sysv_name(Service *s, const char *name) { if (r < 0) return r; - if ((s->meta.load_state != UNIT_STUB)) + if ((UNIT(s)->load_state != UNIT_STUB)) break; } @@ -986,22 +1042,22 @@ static int service_load_sysv(Service *s) { /* Load service data from SysV init scripts, preferably with * LSB headers ... */ - if (strv_isempty(s->meta.manager->lookup_paths.sysvinit_path)) + if (strv_isempty(UNIT(s)->manager->lookup_paths.sysvinit_path)) return 0; - if ((t = s->meta.id)) + if ((t = UNIT(s)->id)) if ((r = service_load_sysv_name(s, t)) < 0) return r; - if (s->meta.load_state == UNIT_STUB) - SET_FOREACH(t, s->meta.names, i) { - if (t == s->meta.id) + if (UNIT(s)->load_state == UNIT_STUB) + SET_FOREACH(t, UNIT(s)->names, i) { + if (t == UNIT(s)->id) continue; if ((r = service_load_sysv_name(s, t)) < 0) return r; - if (s->meta.load_state != UNIT_STUB) + if (UNIT(s)->load_state != UNIT_STUB) break; } @@ -1010,7 +1066,7 @@ static int service_load_sysv(Service *s) { #endif static int fsck_fix_order(Service *s) { - Meta *other; + Unit *other; int r; assert(s); @@ -1021,16 +1077,16 @@ static int fsck_fix_order(Service *s) { /* For each pair of services where both have an fsck priority * we order things based on it. */ - LIST_FOREACH(units_by_type, other, s->meta.manager->units_by_type[UNIT_SERVICE]) { + LIST_FOREACH(units_by_type, other, UNIT(s)->manager->units_by_type[UNIT_SERVICE]) { Service *t; UnitDependency d; - t = (Service*) other; + t = SERVICE(other); if (s == t) continue; - if (t->meta.load_state != UNIT_LOADED) + if (UNIT(t)->load_state != UNIT_LOADED) continue; if (t->fsck_passno <= 0) @@ -1053,33 +1109,33 @@ static int fsck_fix_order(Service *s) { static int service_verify(Service *s) { assert(s); - if (s->meta.load_state != UNIT_LOADED) + if (UNIT(s)->load_state != UNIT_LOADED) return 0; if (!s->exec_command[SERVICE_EXEC_START]) { - log_error("%s lacks ExecStart setting. Refusing.", s->meta.id); + log_error("%s lacks ExecStart setting. Refusing.", UNIT(s)->id); return -EINVAL; } if (s->type != SERVICE_ONESHOT && s->exec_command[SERVICE_EXEC_START]->command_next) { - log_error("%s has more than one ExecStart setting, which is only allowed for Type=oneshot services. Refusing.", s->meta.id); + log_error("%s has more than one ExecStart setting, which is only allowed for Type=oneshot services. Refusing.", UNIT(s)->id); return -EINVAL; } if (s->type == SERVICE_ONESHOT && s->exec_command[SERVICE_EXEC_RELOAD]) { - log_error("%s has an ExecReload setting, which is not allowed for Type=oneshot services. Refusing.", s->meta.id); + log_error("%s has an ExecReload setting, which is not allowed for Type=oneshot services. Refusing.", UNIT(s)->id); return -EINVAL; } if (s->type == SERVICE_DBUS && !s->bus_name) { - log_error("%s is of type D-Bus but no D-Bus service name has been specified. Refusing.", s->meta.id); + log_error("%s is of type D-Bus but no D-Bus service name has been specified. Refusing.", UNIT(s)->id); return -EINVAL; } if (s->exec_context.pam_name && s->exec_context.kill_mode != KILL_CONTROL_GROUP) { - log_error("%s has PAM enabled. Kill mode must be set to 'control-group'. Refusing.", s->meta.id); + log_error("%s has PAM enabled. Kill mode must be set to 'control-group'. Refusing.", UNIT(s)->id); return -EINVAL; } @@ -1095,12 +1151,12 @@ static int service_add_default_dependencies(Service *s) { * majority of services. */ /* First, pull in base system */ - if (s->meta.manager->running_as == MANAGER_SYSTEM) { + if (UNIT(s)->manager->running_as == MANAGER_SYSTEM) { if ((r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_BASIC_TARGET, NULL, true)) < 0) return r; - } else if (s->meta.manager->running_as == MANAGER_USER) { + } else if (UNIT(s)->manager->running_as == MANAGER_USER) { if ((r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SOCKETS_TARGET, NULL, true)) < 0) return r; @@ -1110,6 +1166,24 @@ static int service_add_default_dependencies(Service *s) { return unit_add_two_dependencies_by_name(UNIT(s), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, NULL, true); } +static void service_fix_output(Service *s) { + assert(s); + + /* If nothing has been explicitly configured, patch default + * output in. If input is socket/tty we avoid this however, + * since in that case we want output to default to the same + * place as we read input from. */ + + if (s->exec_context.std_error == EXEC_OUTPUT_INHERIT && + s->exec_context.std_output == EXEC_OUTPUT_INHERIT && + s->exec_context.std_input == EXEC_INPUT_NULL) + s->exec_context.std_error = UNIT(s)->manager->default_std_error; + + if (s->exec_context.std_output == EXEC_OUTPUT_INHERIT && + s->exec_context.std_input == EXEC_INPUT_NULL) + s->exec_context.std_output = UNIT(s)->manager->default_std_output; +} + static int service_load(Unit *u) { int r; Service *s = SERVICE(u); @@ -1122,13 +1196,13 @@ static int service_load(Unit *u) { #ifdef HAVE_SYSV_COMPAT /* Load a classic init script as a fallback, if we couldn't find anything */ - if (u->meta.load_state == UNIT_STUB) + if (u->load_state == UNIT_STUB) if ((r = service_load_sysv(s)) < 0) return r; #endif /* Still nothing found? Then let's give up */ - if (u->meta.load_state == UNIT_STUB) + if (u->load_state == UNIT_STUB) return -ENOENT; /* We were able to load something, then let's add in the @@ -1137,7 +1211,9 @@ static int service_load(Unit *u) { return r; /* This is a new unit? Then let's add in some extras */ - if (u->meta.load_state == UNIT_LOADED) { + if (u->load_state == UNIT_LOADED) { + service_fix_output(s); + if ((r = unit_add_exec_dependencies(u, &s->exec_context)) < 0) return r; @@ -1159,11 +1235,14 @@ static int service_load(Unit *u) { if (s->type == SERVICE_NOTIFY && s->notify_access == NOTIFY_NONE) s->notify_access = NOTIFY_MAIN; + if (s->watchdog_usec > 0 && s->notify_access == NOTIFY_NONE) + s->notify_access = NOTIFY_MAIN; + if (s->type == SERVICE_DBUS || s->bus_name) if ((r = unit_add_two_dependencies_by_name(u, UNIT_AFTER, UNIT_REQUIRES, SPECIAL_DBUS_SOCKET, NULL, true)) < 0) return r; - if (s->meta.default_dependencies) + if (UNIT(s)->default_dependencies) if ((r = service_add_default_dependencies(s)) < 0) return r; } @@ -1185,6 +1264,8 @@ static void service_dump(Unit *u, FILE *f, const char *prefix) { fprintf(f, "%sService State: %s\n" + "%sResult: %s\n" + "%sReload Result: %s\n" "%sPermissionsStartOnly: %s\n" "%sRootDirectoryStartOnly: %s\n" "%sRemainAfterExit: %s\n" @@ -1193,6 +1274,8 @@ static void service_dump(Unit *u, FILE *f, const char *prefix) { "%sRestart: %s\n" "%sNotifyAccess: %s\n", prefix, service_state_to_string(s->state), + prefix, service_result_to_string(s->result), + prefix, service_result_to_string(s->reload_result), prefix, yes_no(s->permissions_start_only), prefix, yes_no(s->root_directory_start_only), prefix, yes_no(s->remain_after_exit), @@ -1272,21 +1355,22 @@ static void service_dump(Unit *u, FILE *f, const char *prefix) { free(p2); } -static int service_load_pid_file(Service *s) { +static int service_load_pid_file(Service *s, bool may_warn) { char *k; int r; pid_t pid; assert(s); - if (s->main_pid_known) - return 0; - if (!s->pid_file) - return 0; + return -ENOENT; - if ((r = read_one_line_file(s->pid_file, &k)) < 0) + if ((r = read_one_line_file(s->pid_file, &k)) < 0) { + if (may_warn) + log_info("PID file %s not readable (yet?) after %s.", + s->pid_file, service_state_to_string(s->state)); return r; + } r = parse_pid(k, &pid); free(k); @@ -1295,11 +1379,23 @@ static int service_load_pid_file(Service *s) { return r; if (kill(pid, 0) < 0 && errno != EPERM) { - log_warning("PID %lu read from file %s does not exist. Your service or init script might be broken.", - (unsigned long) pid, s->pid_file); + if (may_warn) + log_info("PID %lu read from file %s does not exist.", + (unsigned long) pid, s->pid_file); return -ESRCH; } + if (s->main_pid_known) { + if (pid == s->main_pid) + return 0; + + log_debug("Main PID changing: %lu -> %lu", + (unsigned long) s->main_pid, (unsigned long) pid); + service_unwatch_main_pid(s); + s->main_pid_known = false; + } else + log_debug("Main PID loaded: %lu", (unsigned long) pid); + if ((r = service_set_main_pid(s, pid)) < 0) return r; @@ -1326,9 +1422,10 @@ static int service_search_main_pid(Service *s) { assert(s->main_pid <= 0); - if ((pid = cgroup_bonding_search_main_pid_list(s->meta.cgroup_bondings)) <= 0) + if ((pid = cgroup_bonding_search_main_pid_list(UNIT(s)->cgroup_bondings)) <= 0) return -ENOENT; + log_debug("Main PID guessed: %lu", (unsigned long) pid); if ((r = service_set_main_pid(s, pid)) < 0) return r; @@ -1339,86 +1436,22 @@ static int service_search_main_pid(Service *s) { return 0; } -static int service_get_sockets(Service *s, Set **_set) { - Set *set; +static void service_notify_sockets_dead(Service *s) { Iterator i; - char *t; - int r; - - assert(s); - assert(_set); - - if (s->socket_fd >= 0) - return 0; - - if (!set_isempty(s->configured_sockets)) - return 0; - - /* Collects all Socket objects that belong to this - * service. Note that a service might have multiple sockets - * via multiple names. */ - - if (!(set = set_new(NULL, NULL))) - return -ENOMEM; - - SET_FOREACH(t, s->meta.names, i) { - char *k; - Unit *p; - - /* Look for all socket objects that go by any of our - * units and collect their fds */ - - if (!(k = unit_name_change_suffix(t, ".socket"))) { - r = -ENOMEM; - goto fail; - } - - p = manager_get_unit(s->meta.manager, k); - free(k); - - if (!p) - continue; - - if ((r = set_put(set, p)) < 0) - goto fail; - } - - *_set = set; - return 0; - -fail: - set_free(set); - return r; -} - -static int service_notify_sockets_dead(Service *s) { - Iterator i; - Set *set, *free_set = NULL; - Socket *sock; - int r; + Unit *u; assert(s); /* Notifies all our sockets when we die */ if (s->socket_fd >= 0) - return 0; - - if (!set_isempty(s->configured_sockets)) - set = s->configured_sockets; - else { - if ((r = service_get_sockets(s, &free_set)) < 0) - return r; - - set = free_set; - } - - SET_FOREACH(sock, set, i) - socket_notify_service_dead(sock); + return; - set_free(free_set); + SET_FOREACH(u, UNIT(s)->dependencies[UNIT_TRIGGERED_BY], i) + if (u->type == UNIT_SOCKET) + socket_notify_service_dead(SOCKET(u)); - return 0; + return; } static void service_set_state(Service *s, ServiceState state) { @@ -1428,6 +1461,8 @@ static void service_set_state(Service *s, ServiceState state) { old_state = s->state; s->state = state; + service_unwatch_pid_file(s); + if (state != SERVICE_START_PRE && state != SERVICE_START && state != SERVICE_START_POST && @@ -1489,21 +1524,24 @@ static void service_set_state(Service *s, ServiceState state) { state != SERVICE_STOP_POST && state != SERVICE_FINAL_SIGTERM && state != SERVICE_FINAL_SIGKILL && - !(state == SERVICE_DEAD && s->meta.job)) { + !(state == SERVICE_DEAD && UNIT(s)->job)) { service_close_socket_fd(s); service_connection_unref(s); } + if (state == SERVICE_STOP) + service_stop_watchdog(s); + /* For the inactive states unit_notify() will trim the cgroup, * but for exit we have to do that ourselves... */ - if (state == SERVICE_EXITED && s->meta.manager->n_reloading <= 0) - cgroup_bonding_trim_list(s->meta.cgroup_bondings, true); + if (state == SERVICE_EXITED && UNIT(s)->manager->n_reloading <= 0) + cgroup_bonding_trim_list(UNIT(s)->cgroup_bondings, true); if (old_state != state) - log_debug("%s changed %s -> %s", s->meta.id, service_state_to_string(old_state), service_state_to_string(state)); + log_debug("%s changed %s -> %s", UNIT(s)->id, service_state_to_string(old_state), service_state_to_string(state)); - unit_notify(UNIT(s), state_translation_table[old_state], state_translation_table[state], !s->reload_failure); - s->reload_failure = false; + unit_notify(UNIT(s), state_translation_table[old_state], state_translation_table[state], s->reload_result == SERVICE_SUCCESS); + s->reload_result = SERVICE_SUCCESS; } static int service_coldplug(Unit *u) { @@ -1566,9 +1604,12 @@ static int service_coldplug(Unit *u) { if ((r = unit_watch_pid(UNIT(s), s->control_pid)) < 0) return r; + if (s->deserialized_state == SERVICE_START_POST || + s->deserialized_state == SERVICE_RUNNING) + service_handle_watchdog(s); + service_set_state(s, s->deserialized_state); } - return 0; } @@ -1577,8 +1618,7 @@ static int service_collect_fds(Service *s, int **fds, unsigned *n_fds) { int r; int *rfds = NULL; unsigned rn_fds = 0; - Set *set, *free_set = NULL; - Socket *sock; + Unit *u; assert(s); assert(fds); @@ -1587,18 +1627,15 @@ static int service_collect_fds(Service *s, int **fds, unsigned *n_fds) { if (s->socket_fd >= 0) return 0; - if (!set_isempty(s->configured_sockets)) - set = s->configured_sockets; - else { - if ((r = service_get_sockets(s, &free_set)) < 0) - return r; - - set = free_set; - } - - SET_FOREACH(sock, set, i) { + SET_FOREACH(u, UNIT(s)->dependencies[UNIT_TRIGGERED_BY], i) { int *cfds; unsigned cn_fds; + Socket *sock; + + if (u->type != UNIT_SOCKET) + continue; + + sock = SOCKET(u); if ((r = socket_collect_fds(sock, &cfds, &cn_fds)) < 0) goto fail; @@ -1631,12 +1668,9 @@ static int service_collect_fds(Service *s, int **fds, unsigned *n_fds) { *fds = rfds; *n_fds = rn_fds; - set_free(free_set); - return 0; fail: - set_free(set); free(rfds); return r; @@ -1696,7 +1730,7 @@ static int service_spawn( } if (set_notify_socket) - if (asprintf(our_env + n_env++, "NOTIFY_SOCKET=%s", s->meta.manager->notify_socket) < 0) { + if (asprintf(our_env + n_env++, "NOTIFY_SOCKET=%s", UNIT(s)->manager->notify_socket) < 0) { r = -ENOMEM; goto fail; } @@ -1707,8 +1741,14 @@ static int service_spawn( goto fail; } + if (s->watchdog_usec > 0) + if (asprintf(our_env + n_env++, "WATCHDOG_USEC=%llu", (unsigned long long) s->watchdog_usec) < 0) { + r = -ENOMEM; + goto fail; + } + if (!(final_env = strv_env_merge(2, - s->meta.manager->environment, + UNIT(s)->manager->environment, our_env, NULL))) { r = -ENOMEM; @@ -1723,8 +1763,9 @@ static int service_spawn( apply_permissions, apply_chroot, apply_tty_stdin, - s->meta.manager->confirm_spawn, - s->meta.cgroup_bondings, + UNIT(s)->manager->confirm_spawn, + UNIT(s)->cgroup_bondings, + UNIT(s)->cgroup_attributes, &pid); if (r < 0) @@ -1792,52 +1833,52 @@ static int cgroup_good(Service *s) { assert(s); - if ((r = cgroup_bonding_is_empty_list(s->meta.cgroup_bondings)) < 0) + if ((r = cgroup_bonding_is_empty_list(UNIT(s)->cgroup_bondings)) < 0) return r; return !r; } -static void service_enter_dead(Service *s, bool success, bool allow_restart) { +static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart) { int r; assert(s); - if (!success) - s->failure = true; + if (f != SERVICE_SUCCESS) + s->result = f; if (allow_restart && !s->forbid_restart && (s->restart == SERVICE_RESTART_ALWAYS || - (s->restart == SERVICE_RESTART_ON_SUCCESS && !s->failure) || - (s->restart == SERVICE_RESTART_ON_FAILURE && s->failure) || - (s->restart == SERVICE_RESTART_ON_ABORT && s->failure && - (s->main_exec_status.code == CLD_KILLED || - s->main_exec_status.code == CLD_DUMPED)))) { + (s->restart == SERVICE_RESTART_ON_SUCCESS && s->result == SERVICE_SUCCESS) || + (s->restart == SERVICE_RESTART_ON_FAILURE && s->result != SERVICE_SUCCESS) || + (s->restart == SERVICE_RESTART_ON_ABORT && (s->result == SERVICE_FAILURE_SIGNAL || + s->result == SERVICE_FAILURE_CORE_DUMP)))) { - if ((r = unit_watch_timer(UNIT(s), s->restart_usec, &s->timer_watch)) < 0) + r = unit_watch_timer(UNIT(s), s->restart_usec, &s->timer_watch); + if (r < 0) goto fail; service_set_state(s, SERVICE_AUTO_RESTART); } else - service_set_state(s, s->failure ? SERVICE_FAILED : SERVICE_DEAD); + service_set_state(s, s->result != SERVICE_SUCCESS ? SERVICE_FAILED : SERVICE_DEAD); s->forbid_restart = false; return; fail: - log_warning("%s failed to run install restart timer: %s", s->meta.id, strerror(-r)); - service_enter_dead(s, false, false); + log_warning("%s failed to run install restart timer: %s", UNIT(s)->id, strerror(-r)); + service_enter_dead(s, SERVICE_FAILURE_RESOURCES, false); } -static void service_enter_signal(Service *s, ServiceState state, bool success); +static void service_enter_signal(Service *s, ServiceState state, ServiceResult f); -static void service_enter_stop_post(Service *s, bool success) { +static void service_enter_stop_post(Service *s, ServiceResult f) { int r; assert(s); - if (!success) - s->failure = true; + if (f != SERVICE_SUCCESS) + s->result = f; service_unwatch_control_pid(s); @@ -1858,24 +1899,24 @@ static void service_enter_stop_post(Service *s, bool success) { service_set_state(s, SERVICE_STOP_POST); } else - service_enter_signal(s, SERVICE_FINAL_SIGTERM, true); + service_enter_signal(s, SERVICE_FINAL_SIGTERM, SERVICE_SUCCESS); return; fail: - log_warning("%s failed to run 'stop-post' task: %s", s->meta.id, strerror(-r)); - service_enter_signal(s, SERVICE_FINAL_SIGTERM, false); + log_warning("%s failed to run 'stop-post' task: %s", UNIT(s)->id, strerror(-r)); + service_enter_signal(s, SERVICE_FINAL_SIGTERM, SERVICE_FAILURE_RESOURCES); } -static void service_enter_signal(Service *s, ServiceState state, bool success) { +static void service_enter_signal(Service *s, ServiceState state, ServiceResult f) { int r; Set *pid_set = NULL; bool wait_for_exit = false; assert(s); - if (!success) - s->failure = true; + if (f != SERVICE_SUCCESS) + s->result = f; if (s->exec_context.kill_mode != KILL_NONE) { int sig = (state == SERVICE_STOP_SIGTERM || state == SERVICE_FINAL_SIGTERM) ? s->exec_context.kill_signal : SIGKILL; @@ -1910,7 +1951,7 @@ static void service_enter_signal(Service *s, ServiceState state, bool success) { if ((r = set_put(pid_set, LONG_TO_PTR(s->control_pid))) < 0) goto fail; - if ((r = cgroup_bonding_kill_list(s->meta.cgroup_bondings, sig, true, pid_set)) < 0) { + if ((r = cgroup_bonding_kill_list(UNIT(s)->cgroup_bondings, sig, true, pid_set)) < 0) { if (r != -EAGAIN && r != -ESRCH && r != -ENOENT) log_warning("Failed to kill control group: %s", strerror(-r)); } else if (r > 0) @@ -1928,31 +1969,31 @@ static void service_enter_signal(Service *s, ServiceState state, bool success) { service_set_state(s, state); } else if (state == SERVICE_STOP_SIGTERM || state == SERVICE_STOP_SIGKILL) - service_enter_stop_post(s, true); + service_enter_stop_post(s, SERVICE_SUCCESS); else - service_enter_dead(s, true, true); + service_enter_dead(s, SERVICE_SUCCESS, true); return; fail: - log_warning("%s failed to kill processes: %s", s->meta.id, strerror(-r)); + log_warning("%s failed to kill processes: %s", UNIT(s)->id, strerror(-r)); if (state == SERVICE_STOP_SIGTERM || state == SERVICE_STOP_SIGKILL) - service_enter_stop_post(s, false); + service_enter_stop_post(s, SERVICE_FAILURE_RESOURCES); else - service_enter_dead(s, false, true); + service_enter_dead(s, SERVICE_FAILURE_RESOURCES, true); if (pid_set) set_free(pid_set); } -static void service_enter_stop(Service *s, bool success) { +static void service_enter_stop(Service *s, ServiceResult f) { int r; assert(s); - if (!success) - s->failure = true; + if (f != SERVICE_SUCCESS) + s->result = f; service_unwatch_control_pid(s); @@ -1972,21 +2013,21 @@ static void service_enter_stop(Service *s, bool success) { service_set_state(s, SERVICE_STOP); } else - service_enter_signal(s, SERVICE_STOP_SIGTERM, true); + service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_SUCCESS); return; fail: - log_warning("%s failed to run 'stop' task: %s", s->meta.id, strerror(-r)); - service_enter_signal(s, SERVICE_STOP_SIGTERM, false); + log_warning("%s failed to run 'stop' task: %s", UNIT(s)->id, strerror(-r)); + service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_FAILURE_RESOURCES); } -static void service_enter_running(Service *s, bool success) { +static void service_enter_running(Service *s, ServiceResult f) { int main_pid_ok, cgroup_ok; assert(s); - if (!success) - s->failure = true; + if (f != SERVICE_SUCCESS) + s->result = f; main_pid_ok = main_pid_good(s); cgroup_ok = cgroup_good(s); @@ -1997,7 +2038,7 @@ static void service_enter_running(Service *s, bool success) { else if (s->remain_after_exit) service_set_state(s, SERVICE_EXITED); else - service_enter_stop(s, true); + service_enter_stop(s, SERVICE_SUCCESS); } static void service_enter_start_post(Service *s) { @@ -2006,6 +2047,9 @@ static void service_enter_start_post(Service *s) { service_unwatch_control_pid(s); + if (s->watchdog_usec > 0) + service_reset_watchdog(s); + if ((s->control_command = s->exec_command[SERVICE_EXEC_START_POST])) { s->control_command_id = SERVICE_EXEC_START_POST; @@ -2022,13 +2066,13 @@ static void service_enter_start_post(Service *s) { service_set_state(s, SERVICE_START_POST); } else - service_enter_running(s, true); + service_enter_running(s, SERVICE_SUCCESS); return; fail: - log_warning("%s failed to run 'start-post' task: %s", s->meta.id, strerror(-r)); - service_enter_stop(s, false); + log_warning("%s failed to run 'start-post' task: %s", UNIT(s)->id, strerror(-r)); + service_enter_stop(s, SERVICE_FAILURE_RESOURCES); } static void service_enter_start(Service *s) { @@ -2046,6 +2090,11 @@ static void service_enter_start(Service *s) { else service_unwatch_main_pid(s); + /* We want to ensure that nobody leaks processes from + * START_PRE here, so let's go on a killing spree, People + * should not spawn long running processes from START_PRE. */ + cgroup_bonding_kill_list(UNIT(s)->cgroup_bondings, SIGKILL, true, NULL); + if (s->type == SERVICE_FORKING) { s->control_command_id = SERVICE_EXEC_START; c = s->control_command = s->exec_command[SERVICE_EXEC_START]; @@ -2103,8 +2152,8 @@ static void service_enter_start(Service *s) { return; fail: - log_warning("%s failed to run 'start' task: %s", s->meta.id, strerror(-r)); - service_enter_signal(s, SERVICE_FINAL_SIGTERM, false); + log_warning("%s failed to run 'start' task: %s", UNIT(s)->id, strerror(-r)); + service_enter_signal(s, SERVICE_FINAL_SIGTERM, SERVICE_FAILURE_RESOURCES); } static void service_enter_start_pre(Service *s) { @@ -2115,6 +2164,11 @@ static void service_enter_start_pre(Service *s) { service_unwatch_control_pid(s); if ((s->control_command = s->exec_command[SERVICE_EXEC_START_PRE])) { + + /* Before we start anything, let's clear up what might + * be left from previous runs. */ + cgroup_bonding_kill_list(UNIT(s)->cgroup_bondings, SIGKILL, true, NULL); + s->control_command_id = SERVICE_EXEC_START_PRE; if ((r = service_spawn(s, @@ -2135,8 +2189,8 @@ static void service_enter_start_pre(Service *s) { return; fail: - log_warning("%s failed to run 'start-pre' task: %s", s->meta.id, strerror(-r)); - service_enter_dead(s, false, true); + log_warning("%s failed to run 'start-pre' task: %s", UNIT(s)->id, strerror(-r)); + service_enter_dead(s, SERVICE_FAILURE_RESOURCES, true); } static void service_enter_restart(Service *s) { @@ -2146,24 +2200,24 @@ static void service_enter_restart(Service *s) { assert(s); dbus_error_init(&error); - if (s->meta.job) { + if (UNIT(s)->job) { log_info("Job pending for unit, delaying automatic restart."); if ((r = unit_watch_timer(UNIT(s), s->restart_usec, &s->timer_watch)) < 0) goto fail; } - service_enter_dead(s, true, false); + service_enter_dead(s, SERVICE_SUCCESS, false); - if ((r = manager_add_job(s->meta.manager, JOB_START, UNIT(s), JOB_FAIL, false, &error, NULL)) < 0) + if ((r = manager_add_job(UNIT(s)->manager, JOB_START, UNIT(s), JOB_FAIL, false, &error, NULL)) < 0) goto fail; - log_debug("%s scheduled restart job.", s->meta.id); + log_debug("%s scheduled restart job.", UNIT(s)->id); return; fail: - log_warning("%s failed to schedule restart job: %s", s->meta.id, bus_error(&error, -r)); - service_enter_dead(s, false, false); + log_warning("%s failed to schedule restart job: %s", UNIT(s)->id, bus_error(&error, -r)); + service_enter_dead(s, SERVICE_FAILURE_RESOURCES, false); dbus_error_free(&error); } @@ -2191,26 +2245,23 @@ static void service_enter_reload(Service *s) { service_set_state(s, SERVICE_RELOAD); } else - service_enter_running(s, true); + service_enter_running(s, SERVICE_SUCCESS); return; fail: - log_warning("%s failed to run 'reload' task: %s", s->meta.id, strerror(-r)); - s->reload_failure = true; - service_enter_running(s, true); + log_warning("%s failed to run 'reload' task: %s", UNIT(s)->id, strerror(-r)); + s->reload_result = SERVICE_FAILURE_RESOURCES; + service_enter_running(s, SERVICE_SUCCESS); } -static void service_run_next_control(Service *s, bool success) { +static void service_run_next_control(Service *s) { int r; assert(s); assert(s->control_command); assert(s->control_command->command_next); - if (!success) - s->failure = true; - assert(s->control_command_id != SERVICE_EXEC_START); s->control_command = s->control_command->command_next; @@ -2231,22 +2282,22 @@ static void service_run_next_control(Service *s, bool success) { return; fail: - log_warning("%s failed to run next control task: %s", s->meta.id, strerror(-r)); + log_warning("%s failed to run next control task: %s", UNIT(s)->id, strerror(-r)); if (s->state == SERVICE_START_PRE) - service_enter_signal(s, SERVICE_FINAL_SIGTERM, false); + service_enter_signal(s, SERVICE_FINAL_SIGTERM, SERVICE_FAILURE_RESOURCES); else if (s->state == SERVICE_STOP) - service_enter_signal(s, SERVICE_STOP_SIGTERM, false); + service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_FAILURE_RESOURCES); else if (s->state == SERVICE_STOP_POST) - service_enter_dead(s, false, true); + service_enter_dead(s, SERVICE_FAILURE_RESOURCES, true); else if (s->state == SERVICE_RELOAD) { - s->reload_failure = true; - service_enter_running(s, true); + s->reload_result = SERVICE_FAILURE_RESOURCES; + service_enter_running(s, SERVICE_SUCCESS); } else - service_enter_stop(s, false); + service_enter_stop(s, SERVICE_FAILURE_RESOURCES); } -static void service_run_next_main(Service *s, bool success) { +static void service_run_next_main(Service *s) { pid_t pid; int r; @@ -2255,9 +2306,6 @@ static void service_run_next_main(Service *s, bool success) { assert(s->main_command->command_next); assert(s->type == SERVICE_ONESHOT); - if (!success) - s->failure = true; - s->main_command = s->main_command->command_next; service_unwatch_main_pid(s); @@ -2277,12 +2325,60 @@ static void service_run_next_main(Service *s, bool success) { return; fail: - log_warning("%s failed to run next main task: %s", s->meta.id, strerror(-r)); - service_enter_stop(s, false); + log_warning("%s failed to run next main task: %s", UNIT(s)->id, strerror(-r)); + service_enter_stop(s, SERVICE_FAILURE_RESOURCES); +} + +static int service_start_limit_test(Service *s) { + assert(s); + + if (ratelimit_test(&s->start_limit)) + return 0; + + switch (s->start_limit_action) { + + case SERVICE_START_LIMIT_NONE: + log_warning("%s start request repeated too quickly, refusing to start.", UNIT(s)->id); + break; + + case SERVICE_START_LIMIT_REBOOT: { + DBusError error; + int r; + + dbus_error_init(&error); + + log_warning("%s start request repeated too quickly, rebooting.", UNIT(s)->id); + + r = manager_add_job_by_name(UNIT(s)->manager, JOB_START, SPECIAL_REBOOT_TARGET, JOB_REPLACE, true, &error, NULL); + if (r < 0) { + log_error("Failed to reboot: %s.", bus_error(&error, r)); + dbus_error_free(&error); + } + + break; + } + + case SERVICE_START_LIMIT_REBOOT_FORCE: + log_warning("%s start request repeated too quickly, force rebooting.", UNIT(s)->id); + UNIT(s)->manager->exit_code = MANAGER_REBOOT; + break; + + case SERVICE_START_LIMIT_REBOOT_IMMEDIATE: + log_warning("%s start request repeated too quickly, rebooting immediately.", UNIT(s)->id); + reboot(RB_AUTOBOOT); + break; + + default: + log_error("start limit action=%i", s->start_limit_action); + assert_not_reached("Unknown StartLimitAction."); + } + + return -ECANCELED; } static int service_start(Unit *u) { Service *s = SERVICE(u); + int r; assert(s); @@ -2305,12 +2401,12 @@ static int service_start(Unit *u) { assert(s->state == SERVICE_DEAD || s->state == SERVICE_FAILED || s->state == SERVICE_AUTO_RESTART); /* Make sure we don't enter a busy loop of some kind. */ - if (!ratelimit_test(&s->ratelimit)) { - log_warning("%s start request repeated too quickly, refusing to start.", u->meta.id); - return -ECANCELED; - } + r = service_start_limit_test(s); + if (r < 0) + return r; - s->failure = false; + s->result = SERVICE_SUCCESS; + s->reload_result = SERVICE_SUCCESS; s->main_pid_known = false; s->main_pid_alien = false; s->forbid_restart = false; @@ -2349,14 +2445,14 @@ static int service_stop(Unit *u) { s->state == SERVICE_START || s->state == SERVICE_START_POST || s->state == SERVICE_RELOAD) { - service_enter_signal(s, SERVICE_STOP_SIGTERM, true); + service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_SUCCESS); return 0; } assert(s->state == SERVICE_RUNNING || s->state == SERVICE_EXITED); - service_enter_stop(s, true); + service_enter_stop(s, SERVICE_SUCCESS); return 0; } @@ -2387,7 +2483,8 @@ static int service_serialize(Unit *u, FILE *f, FDSet *fds) { assert(fds); unit_serialize_item(u, f, "state", service_state_to_string(s->state)); - unit_serialize_item(u, f, "failure", yes_no(s->failure)); + unit_serialize_item(u, f, "result", service_result_to_string(s->result)); + unit_serialize_item(u, f, "reload-result", service_result_to_string(s->reload_result)); if (s->control_pid > 0) unit_serialize_item_format(u, f, "control-pid", "%lu", (unsigned long) s->control_pid); @@ -2400,9 +2497,9 @@ static int service_serialize(Unit *u, FILE *f, FDSet *fds) { if (s->status_text) unit_serialize_item(u, f, "status-text", s->status_text); - /* There's a minor uncleanliness here: if there are multiple - * commands attached here, we will start from the first one - * again */ + /* FIXME: There's a minor uncleanliness here: if there are + * multiple commands attached here, we will start from the + * first one again */ if (s->control_command_id >= 0) unit_serialize_item(u, f, "control-command", service_exec_command_to_string(s->control_command_id)); @@ -2425,6 +2522,8 @@ static int service_serialize(Unit *u, FILE *f, FDSet *fds) { unit_serialize_item_format(u, f, "main-exec-status-status", "%i", s->main_exec_status.status); } } + if (dual_timestamp_is_set(&s->watchdog_timestamp)) + dual_timestamp_serialize(f, "watchdog-timestamp", &s->watchdog_timestamp); return 0; } @@ -2444,13 +2543,24 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value, log_debug("Failed to parse state value %s", value); else s->deserialized_state = state; - } else if (streq(key, "failure")) { - int b; + } else if (streq(key, "result")) { + ServiceResult f; + + f = service_result_from_string(value); + if (f < 0) + log_debug("Failed to parse result value %s", value); + else if (f != SERVICE_SUCCESS) + s->result = f; + + } else if (streq(key, "reload-result")) { + ServiceResult f; + + f = service_result_from_string(value); + if (f < 0) + log_debug("Failed to parse reload result value %s", value); + else if (f != SERVICE_SUCCESS) + s->reload_result = f; - if ((b = parse_boolean(value)) < 0) - log_debug("Failed to parse failure value %s", value); - else - s->failure = b || s->failure; } else if (streq(key, "control-pid")) { pid_t pid; @@ -2525,6 +2635,8 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value, dual_timestamp_deserialize(value, &s->main_exec_status.start_timestamp); else if (streq(key, "main-exec-status-exit")) dual_timestamp_deserialize(value, &s->main_exec_status.exit_timestamp); + else if (streq(key, "watchdog-timestamp")) + dual_timestamp_deserialize(value, &s->watchdog_timestamp); else log_debug("Unknown serialization key '%s'", key); @@ -2571,19 +2683,119 @@ static bool service_check_snapshot(Unit *u) { return !s->got_socket_fd; } +static int service_retry_pid_file(Service *s) { + int r; + + assert(s->pid_file); + assert(s->state == SERVICE_START || s->state == SERVICE_START_POST); + + r = service_load_pid_file(s, false); + if (r < 0) + return r; + + service_unwatch_pid_file(s); + + service_enter_running(s, SERVICE_SUCCESS); + return 0; +} + +static int service_watch_pid_file(Service *s) { + int r; + + log_debug("Setting watch for %s's PID file %s", UNIT(s)->id, s->pid_file_pathspec->path); + r = path_spec_watch(s->pid_file_pathspec, UNIT(s)); + if (r < 0) + goto fail; + + /* the pidfile might have appeared just before we set the watch */ + service_retry_pid_file(s); + + return 0; +fail: + log_error("Failed to set a watch for %s's PID file %s: %s", + UNIT(s)->id, s->pid_file_pathspec->path, strerror(-r)); + service_unwatch_pid_file(s); + return r; +} + +static int service_demand_pid_file(Service *s) { + PathSpec *ps; + + assert(s->pid_file); + assert(!s->pid_file_pathspec); + + ps = new0(PathSpec, 1); + if (!ps) + return -ENOMEM; + + ps->path = strdup(s->pid_file); + if (!ps->path) { + free(ps); + return -ENOMEM; + } + + path_kill_slashes(ps->path); + + /* PATH_CHANGED would not be enough. There are daemons (sendmail) that + * keep their PID file open all the time. */ + ps->type = PATH_MODIFIED; + ps->inotify_fd = -1; + + s->pid_file_pathspec = ps; + + return service_watch_pid_file(s); +} + +static void service_fd_event(Unit *u, int fd, uint32_t events, Watch *w) { + Service *s = SERVICE(u); + + assert(s); + assert(fd >= 0); + assert(s->state == SERVICE_START || s->state == SERVICE_START_POST); + assert(s->pid_file_pathspec); + assert(path_spec_owns_inotify_fd(s->pid_file_pathspec, fd)); + + log_debug("inotify event for %s", u->id); + + if (path_spec_fd_event(s->pid_file_pathspec, events) < 0) + goto fail; + + if (service_retry_pid_file(s) == 0) + return; + + if (service_watch_pid_file(s) < 0) + goto fail; + + return; +fail: + service_unwatch_pid_file(s); + service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_FAILURE_RESOURCES); +} + static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { Service *s = SERVICE(u); - bool success; + ServiceResult f; assert(s); assert(pid >= 0); - if (!s->meta.fragment_path) - success = is_clean_exit_lsb(code, status); + if (UNIT(s)->fragment_path ? is_clean_exit(code, status) : is_clean_exit_lsb(code, status)) + f = SERVICE_SUCCESS; + else if (code == CLD_EXITED) + f = SERVICE_FAILURE_EXIT_CODE; + else if (code == CLD_KILLED) + f = SERVICE_FAILURE_SIGNAL; + else if (code == CLD_DUMPED) + f = SERVICE_FAILURE_CORE_DUMP; else - success = is_clean_exit(code, status); + assert_not_reached("Unknown code"); if (s->main_pid == pid) { + /* Forking services may occasionally move to a new PID. + * As long as they update the PID file before exiting the old + * PID, they're fine. */ + if (service_load_pid_file(s, false) == 0) + return; s->main_pid = 0; exec_status_exit(&s->main_exec_status, &s->exec_context, pid, code, status); @@ -2596,22 +2808,24 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { s->main_command->exec_status = s->main_exec_status; if (s->main_command->ignore) - success = true; + f = SERVICE_SUCCESS; } - log_full(success ? LOG_DEBUG : LOG_NOTICE, - "%s: main process exited, code=%s, status=%i", u->meta.id, sigchld_code_to_string(code), status); - s->failure = s->failure || !success; + log_full(f == SERVICE_SUCCESS ? LOG_DEBUG : LOG_NOTICE, + "%s: main process exited, code=%s, status=%i", u->id, sigchld_code_to_string(code), status); + + if (f != SERVICE_SUCCESS) + s->result = f; if (s->main_command && s->main_command->command_next && - success) { + f == SERVICE_SUCCESS) { /* There is another command to * * execute, so let's do that. */ - log_debug("%s running next main command for state %s", u->meta.id, service_state_to_string(s->state)); - service_run_next_main(s, success); + log_debug("%s running next main command for state %s", u->id, service_state_to_string(s->state)); + service_run_next_main(s); } else { @@ -2631,10 +2845,10 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { case SERVICE_START: if (s->type == SERVICE_ONESHOT) { /* This was our main goal, so let's go on */ - if (success) + if (f == SERVICE_SUCCESS) service_enter_start_post(s); else - service_enter_signal(s, SERVICE_FINAL_SIGTERM, false); + service_enter_signal(s, SERVICE_FINAL_SIGTERM, f); break; } else { assert(s->type == SERVICE_DBUS || s->type == SERVICE_NOTIFY); @@ -2643,14 +2857,14 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { } case SERVICE_RUNNING: - service_enter_running(s, success); + service_enter_running(s, f); break; case SERVICE_STOP_SIGTERM: case SERVICE_STOP_SIGKILL: if (!control_pid_good(s)) - service_enter_stop_post(s, success); + service_enter_stop_post(s, f); /* If there is still a control process, wait for that first */ break; @@ -2668,22 +2882,24 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { exec_status_exit(&s->control_command->exec_status, &s->exec_context, pid, code, status); if (s->control_command->ignore) - success = true; + f = SERVICE_SUCCESS; } - log_full(success ? LOG_DEBUG : LOG_NOTICE, - "%s: control process exited, code=%s status=%i", u->meta.id, sigchld_code_to_string(code), status); - s->failure = s->failure || !success; + log_full(f == SERVICE_SUCCESS ? LOG_DEBUG : LOG_NOTICE, + "%s: control process exited, code=%s status=%i", u->id, sigchld_code_to_string(code), status); + + if (f != SERVICE_SUCCESS) + s->result = f; if (s->control_command && s->control_command->command_next && - success) { + f == SERVICE_SUCCESS) { /* There is another command to * * execute, so let's do that. */ - log_debug("%s running next control command for state %s", u->meta.id, service_state_to_string(s->state)); - service_run_next_control(s, success); + log_debug("%s running next control command for state %s", u->id, service_state_to_string(s->state)); + service_run_next_control(s); } else { /* No further commands for this step, so let's @@ -2692,65 +2908,87 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { s->control_command = NULL; s->control_command_id = _SERVICE_EXEC_COMMAND_INVALID; - log_debug("%s got final SIGCHLD for state %s", u->meta.id, service_state_to_string(s->state)); + log_debug("%s got final SIGCHLD for state %s", u->id, service_state_to_string(s->state)); switch (s->state) { case SERVICE_START_PRE: - if (success) + if (f == SERVICE_SUCCESS) service_enter_start(s); else - service_enter_signal(s, SERVICE_FINAL_SIGTERM, false); + service_enter_signal(s, SERVICE_FINAL_SIGTERM, f); break; case SERVICE_START: assert(s->type == SERVICE_FORKING); - /* Let's try to load the pid - * file here if we can. We - * ignore the return value, - * since the PID file might - * actually be created by a - * START_POST script */ - - if (success) { - service_load_pid_file(s); - service_search_main_pid(s); + if (f != SERVICE_SUCCESS) { + service_enter_signal(s, SERVICE_FINAL_SIGTERM, f); + break; + } - service_enter_start_post(s); + if (s->pid_file) { + bool has_start_post; + int r; + + /* Let's try to load the pid file here if we can. + * The PID file might actually be created by a START_POST + * script. In that case don't worry if the loading fails. */ + + has_start_post = !!s->exec_command[SERVICE_EXEC_START_POST]; + r = service_load_pid_file(s, !has_start_post); + if (!has_start_post && r < 0) { + r = service_demand_pid_file(s); + if (r < 0 || !cgroup_good(s)) + service_enter_signal(s, SERVICE_FINAL_SIGTERM, SERVICE_FAILURE_RESOURCES); + break; + } } else - service_enter_signal(s, SERVICE_FINAL_SIGTERM, false); + service_search_main_pid(s); + service_enter_start_post(s); break; case SERVICE_START_POST: - if (success) { - service_load_pid_file(s); - service_search_main_pid(s); + if (f != SERVICE_SUCCESS) { + service_enter_stop(s, f); + break; } - s->reload_failure = !success; - service_enter_running(s, true); + if (s->pid_file) { + int r; + + r = service_load_pid_file(s, true); + if (r < 0) { + r = service_demand_pid_file(s); + if (r < 0 || !cgroup_good(s)) + service_enter_stop(s, SERVICE_FAILURE_RESOURCES); + break; + } + } else + service_search_main_pid(s); + + service_enter_running(s, SERVICE_SUCCESS); break; case SERVICE_RELOAD: - if (success) { - service_load_pid_file(s); + if (f == SERVICE_SUCCESS) { + service_load_pid_file(s, true); service_search_main_pid(s); } - s->reload_failure = !success; - service_enter_running(s, true); + s->reload_result = f; + service_enter_running(s, SERVICE_SUCCESS); break; case SERVICE_STOP: - service_enter_signal(s, SERVICE_STOP_SIGTERM, success); + service_enter_signal(s, SERVICE_STOP_SIGTERM, f); break; case SERVICE_STOP_SIGTERM: case SERVICE_STOP_SIGKILL: if (main_pid_good(s) <= 0) - service_enter_stop_post(s, success); + service_enter_stop_post(s, f); /* If there is still a service * process around, wait until @@ -2760,7 +2998,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { case SERVICE_STOP_POST: case SERVICE_FINAL_SIGTERM: case SERVICE_FINAL_SIGKILL: - service_enter_dead(s, success, true); + service_enter_dead(s, f, true); break; default: @@ -2779,39 +3017,44 @@ static void service_timer_event(Unit *u, uint64_t elapsed, Watch* w) { assert(s); assert(elapsed == 1); + if (w == &s->watchdog_watch) { + service_handle_watchdog(s); + return; + } + assert(w == &s->timer_watch); switch (s->state) { case SERVICE_START_PRE: case SERVICE_START: - log_warning("%s operation timed out. Terminating.", u->meta.id); - service_enter_signal(s, SERVICE_FINAL_SIGTERM, false); + log_warning("%s operation timed out. Terminating.", u->id); + service_enter_signal(s, SERVICE_FINAL_SIGTERM, SERVICE_FAILURE_TIMEOUT); break; case SERVICE_START_POST: - log_warning("%s operation timed out. Stopping.", u->meta.id); - service_enter_stop(s, false); + log_warning("%s operation timed out. Stopping.", u->id); + service_enter_stop(s, SERVICE_FAILURE_TIMEOUT); break; case SERVICE_RELOAD: - log_warning("%s operation timed out. Stopping.", u->meta.id); - s->reload_failure = true; - service_enter_running(s, true); + log_warning("%s operation timed out. Stopping.", u->id); + s->reload_result = SERVICE_FAILURE_TIMEOUT; + service_enter_running(s, SERVICE_SUCCESS); break; case SERVICE_STOP: - log_warning("%s stopping timed out. Terminating.", u->meta.id); - service_enter_signal(s, SERVICE_STOP_SIGTERM, false); + log_warning("%s stopping timed out. Terminating.", u->id); + service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_FAILURE_TIMEOUT); break; case SERVICE_STOP_SIGTERM: if (s->exec_context.send_sigkill) { - log_warning("%s stopping timed out. Killing.", u->meta.id); - service_enter_signal(s, SERVICE_STOP_SIGKILL, false); + log_warning("%s stopping timed out. Killing.", u->id); + service_enter_signal(s, SERVICE_STOP_SIGKILL, SERVICE_FAILURE_TIMEOUT); } else { - log_warning("%s stopping timed out. Skipping SIGKILL.", u->meta.id); - service_enter_stop_post(s, false); + log_warning("%s stopping timed out. Skipping SIGKILL.", u->id); + service_enter_stop_post(s, SERVICE_FAILURE_TIMEOUT); } break; @@ -2821,33 +3064,33 @@ static void service_timer_event(Unit *u, uint64_t elapsed, Watch* w) { * Must be something we cannot kill, so let's just be * weirded out and continue */ - log_warning("%s still around after SIGKILL. Ignoring.", u->meta.id); - service_enter_stop_post(s, false); + log_warning("%s still around after SIGKILL. Ignoring.", u->id); + service_enter_stop_post(s, SERVICE_FAILURE_TIMEOUT); break; case SERVICE_STOP_POST: - log_warning("%s stopping timed out (2). Terminating.", u->meta.id); - service_enter_signal(s, SERVICE_FINAL_SIGTERM, false); + log_warning("%s stopping timed out (2). Terminating.", u->id); + service_enter_signal(s, SERVICE_FINAL_SIGTERM, SERVICE_FAILURE_TIMEOUT); break; case SERVICE_FINAL_SIGTERM: if (s->exec_context.send_sigkill) { - log_warning("%s stopping timed out (2). Killing.", u->meta.id); - service_enter_signal(s, SERVICE_FINAL_SIGKILL, false); + log_warning("%s stopping timed out (2). Killing.", u->id); + service_enter_signal(s, SERVICE_FINAL_SIGKILL, SERVICE_FAILURE_TIMEOUT); } else { - log_warning("%s stopping timed out (2). Skipping SIGKILL. Entering failed mode.", u->meta.id); - service_enter_dead(s, false, true); + log_warning("%s stopping timed out (2). Skipping SIGKILL. Entering failed mode.", u->id); + service_enter_dead(s, SERVICE_FAILURE_TIMEOUT, false); } break; case SERVICE_FINAL_SIGKILL: - log_warning("%s still around after SIGKILL (2). Entering failed mode.", u->meta.id); - service_enter_dead(s, false, true); + log_warning("%s still around after SIGKILL (2). Entering failed mode.", u->id); + service_enter_dead(s, SERVICE_FAILURE_TIMEOUT, true); break; case SERVICE_AUTO_RESTART: - log_info("%s holdoff time over, scheduling restart.", u->meta.id); + log_info("%s holdoff time over, scheduling restart.", u->id); service_enter_restart(s); break; @@ -2861,7 +3104,7 @@ static void service_cgroup_notify_event(Unit *u) { assert(u); - log_debug("%s: cgroup is empty", u->meta.id); + log_debug("%s: cgroup is empty", u->id); switch (s->state) { @@ -2871,22 +3114,37 @@ static void service_cgroup_notify_event(Unit *u) { * except when we don't know pid which to expect the * SIGCHLD for. */ + case SERVICE_START: + case SERVICE_START_POST: + /* If we were hoping for the daemon to write its PID file, + * we can give up now. */ + if (s->pid_file_pathspec) { + log_warning("%s never wrote its PID file. Failing.", UNIT(s)->id); + service_unwatch_pid_file(s); + if (s->state == SERVICE_START) + service_enter_signal(s, SERVICE_FINAL_SIGTERM, SERVICE_FAILURE_RESOURCES); + else + service_enter_stop(s, SERVICE_FAILURE_RESOURCES); + } + break; + case SERVICE_RUNNING: - service_enter_running(s, true); + /* service_enter_running() will figure out what to do */ + service_enter_running(s, SERVICE_SUCCESS); break; case SERVICE_STOP_SIGTERM: case SERVICE_STOP_SIGKILL: if (main_pid_good(s) <= 0 && !control_pid_good(s)) - service_enter_stop_post(s, true); + service_enter_stop_post(s, SERVICE_SUCCESS); break; case SERVICE_FINAL_SIGTERM: case SERVICE_FINAL_SIGKILL: if (main_pid_good(s) <= 0 && !control_pid_good(s)) - service_enter_dead(s, true, true); + service_enter_dead(s, SERVICE_SUCCESS, SERVICE_SUCCESS); break; @@ -2903,17 +3161,17 @@ static void service_notify_message(Unit *u, pid_t pid, char **tags) { if (s->notify_access == NOTIFY_NONE) { log_warning("%s: Got notification message from PID %lu, but reception is disabled.", - u->meta.id, (unsigned long) pid); + u->id, (unsigned long) pid); return; } if (s->notify_access == NOTIFY_MAIN && pid != s->main_pid) { log_warning("%s: Got notification message from PID %lu, but reception only permitted for PID %lu", - u->meta.id, (unsigned long) pid, (unsigned long) s->main_pid); + u->id, (unsigned long) pid, (unsigned long) s->main_pid); return; } - log_debug("%s: Got message", u->meta.id); + log_debug("%s: Got message", u->id); /* Interpret MAINPID= */ if ((e = strv_find_prefix(tags, "MAINPID=")) && @@ -2925,7 +3183,7 @@ static void service_notify_message(Unit *u, pid_t pid, char **tags) { if (parse_pid(e + 8, &pid) < 0) log_warning("Failed to parse notification message %s", e); else { - log_debug("%s: got %s", u->meta.id, e); + log_debug("%s: got %s", u->id, e); service_set_main_pid(s, pid); } } @@ -2934,7 +3192,7 @@ static void service_notify_message(Unit *u, pid_t pid, char **tags) { if (s->type == SERVICE_NOTIFY && s->state == SERVICE_START && strv_find(tags, "READY=1")) { - log_debug("%s: got READY=1", u->meta.id); + log_debug("%s: got READY=1", u->id); service_enter_start_post(s); } @@ -2949,7 +3207,7 @@ static void service_notify_message(Unit *u, pid_t pid, char **tags) { return; } - log_debug("%s: got %s", u->meta.id, e); + log_debug("%s: got %s", u->id, e); free(s->status_text); s->status_text = t; @@ -2959,12 +3217,82 @@ static void service_notify_message(Unit *u, pid_t pid, char **tags) { } } + if (strv_find(tags, "WATCHDOG=1")) { + log_debug("%s: got WATCHDOG=1", u->id); + service_reset_watchdog(s); + } /* Notify clients about changed status or main pid */ unit_add_to_dbus_queue(u); } #ifdef HAVE_SYSV_COMPAT + +#ifdef TARGET_SUSE +static void sysv_facility_in_insserv_conf(Manager *mgr) { + FILE *f=NULL; + int r; + + if (!(f = fopen("/etc/insserv.conf", "re"))) { + r = errno == ENOENT ? 0 : -errno; + goto finish; + } + + while (!feof(f)) { + char l[LINE_MAX], *t; + char **parsed = NULL; + + if (!fgets(l, sizeof(l), f)) { + if (feof(f)) + break; + + r = -errno; + log_error("Failed to read configuration file '/etc/insserv.conf': %s", strerror(-r)); + goto finish; + } + + t = strstrip(l); + if (*t != '$' && *t != '<') + continue; + + parsed = strv_split(t,WHITESPACE); + /* we ignore <interactive>, not used, equivalent to X-Interactive */ + if (parsed && !startswith_no_case (parsed[0], "<interactive>")) { + char *facility; + Unit *u; + if (sysv_translate_facility(parsed[0], NULL, &facility) < 0) + continue; + if ((u = manager_get_unit(mgr, facility)) && (u->type == UNIT_TARGET)) { + UnitDependency e; + char *dep = NULL, *name, **j; + + STRV_FOREACH (j, parsed+1) { + if (*j[0]=='+') { + e = UNIT_WANTS; + name = *j+1; + } + else { + e = UNIT_REQUIRES; + name = *j; + } + if (sysv_translate_facility(name, NULL, &dep) < 0) + continue; + + r = unit_add_two_dependencies_by_name(u, UNIT_BEFORE, e, dep, NULL, true); + free(dep); + } + } + free(facility); + } + strv_free(parsed); + } +finish: + if (f) + fclose(f); + +} +#endif + static int service_enumerate(Manager *m) { char **p; unsigned i; @@ -2987,8 +3315,8 @@ static int service_enumerate(Manager *m) { struct dirent *de; free(path); - path = NULL; - if (asprintf(&path, "%s/%s", *p, rcnd_table[i].path) < 0) { + path = join(*p, "/", rcnd_table[i].path, NULL); + if (!path) { r = -ENOMEM; goto finish; } @@ -3022,8 +3350,8 @@ static int service_enumerate(Manager *m) { continue; free(fpath); - fpath = NULL; - if (asprintf(&fpath, "%s/%s/%s", *p, rcnd_table[i].path, de->d_name) < 0) { + fpath = join(path, "/", de->d_name, NULL); + if (!fpath) { r = -ENOMEM; goto finish; } @@ -3087,7 +3415,7 @@ static int service_enumerate(Manager *m) { SET_FOREACH(service, runlevel_services[i], j) { service = unit_follow_merge(service); - if (service->meta.fragment_path) + if (service->fragment_path) continue; if ((r = unit_add_two_dependencies_by_name_inverse(service, UNIT_AFTER, UNIT_WANTS, rcnd_table[i].target, NULL, true)) < 0) @@ -3104,7 +3432,7 @@ static int service_enumerate(Manager *m) { SET_FOREACH(service, shutdown_services, j) { service = unit_follow_merge(service); - if (service->meta.fragment_path) + if (service->fragment_path) continue; if ((r = unit_add_two_dependencies_by_name(service, UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, NULL, true)) < 0) @@ -3113,6 +3441,10 @@ static int service_enumerate(Manager *m) { r = 0; +#ifdef TARGET_SUSE + sysv_facility_in_insserv_conf (m); +#endif + finish: free(path); free(fpath); @@ -3144,11 +3476,11 @@ static void service_bus_name_owner_change( assert(old_owner || new_owner); if (old_owner && new_owner) - log_debug("%s's D-Bus name %s changed owner from %s to %s", u->meta.id, name, old_owner, new_owner); + log_debug("%s's D-Bus name %s changed owner from %s to %s", u->id, name, old_owner, new_owner); else if (old_owner) - log_debug("%s's D-Bus name %s no longer registered by %s", u->meta.id, name, old_owner); + log_debug("%s's D-Bus name %s no longer registered by %s", u->id, name, old_owner); else - log_debug("%s's D-Bus name %s now registered by %s", u->meta.id, name, new_owner); + log_debug("%s's D-Bus name %s now registered by %s", u->id, name, new_owner); s->bus_name_good = !!new_owner; @@ -3157,7 +3489,7 @@ static void service_bus_name_owner_change( /* service_enter_running() will figure out what to * do */ if (s->state == SERVICE_RUNNING) - service_enter_running(s, true); + service_enter_running(s, SERVICE_SUCCESS); else if (s->state == SERVICE_START && new_owner) service_enter_start_post(s); @@ -3171,7 +3503,7 @@ static void service_bus_name_owner_change( /* Try to acquire PID from bus service */ log_debug("Trying to acquire PID from D-Bus name..."); - bus_query_pid(u->meta.manager, name); + bus_query_pid(u->manager, name); } } @@ -3185,7 +3517,7 @@ static void service_bus_query_pid_done( assert(s); assert(name); - log_debug("%s's D-Bus name %s is now owned by process %u", u->meta.id, name, (unsigned) pid); + log_debug("%s's D-Bus name %s is now owned by process %u", u->id, name, (unsigned) pid); if (s->main_pid <= 0 && (s->state == SERVICE_START || @@ -3196,6 +3528,7 @@ static void service_bus_query_pid_done( } int service_set_socket_fd(Service *s, int fd, Socket *sock) { + assert(s); assert(fd >= 0); @@ -3203,7 +3536,7 @@ int service_set_socket_fd(Service *s, int fd, Socket *sock) { * service for a stream socket and the socket needs to be * configured. */ - if (s->meta.load_state != UNIT_LOADED) + if (UNIT(s)->load_state != UNIT_LOADED) return -EINVAL; if (s->socket_fd >= 0) @@ -3214,9 +3547,10 @@ int service_set_socket_fd(Service *s, int fd, Socket *sock) { s->socket_fd = fd; s->got_socket_fd = true; - s->accept_socket = sock; - return 0; + unit_ref_set(&s->accept_socket, UNIT(sock)); + + return unit_add_two_dependencies(UNIT(sock), UNIT_BEFORE, UNIT_TRIGGERS, UNIT(s), false); } static void service_reset_failed(Unit *u) { @@ -3227,7 +3561,8 @@ static void service_reset_failed(Unit *u) { if (s->state == SERVICE_FAILED) service_set_state(s, SERVICE_DEAD); - s->failure = false; + s->result = SERVICE_SUCCESS; + s->reload_result = SERVICE_SUCCESS; } static bool service_need_daemon_reload(Unit *u) { @@ -3299,7 +3634,7 @@ static int service_kill(Unit *u, KillWho who, KillMode mode, int signo, DBusErro goto finish; } - if ((q = cgroup_bonding_kill_list(s->meta.cgroup_bondings, signo, false, pid_set)) < 0) + if ((q = cgroup_bonding_kill_list(UNIT(s)->cgroup_bondings, signo, false, pid_set)) < 0) if (q != -EAGAIN && q != -ESRCH && q != -ENOENT) r = q; } @@ -3370,8 +3705,33 @@ static const char* const notify_access_table[_NOTIFY_ACCESS_MAX] = { DEFINE_STRING_TABLE_LOOKUP(notify_access, NotifyAccess); +static const char* const service_result_table[_SERVICE_RESULT_MAX] = { + [SERVICE_SUCCESS] = "success", + [SERVICE_FAILURE_RESOURCES] = "resources", + [SERVICE_FAILURE_TIMEOUT] = "timeout", + [SERVICE_FAILURE_EXIT_CODE] = "exit-code", + [SERVICE_FAILURE_SIGNAL] = "signal", + [SERVICE_FAILURE_CORE_DUMP] = "core-dump", + [SERVICE_FAILURE_WATCHDOG] = "watchdog" +}; + +DEFINE_STRING_TABLE_LOOKUP(service_result, ServiceResult); + +static const char* const start_limit_action_table[_SERVICE_START_LIMIT_MAX] = { + [SERVICE_START_LIMIT_NONE] = "none", + [SERVICE_START_LIMIT_REBOOT] = "reboot", + [SERVICE_START_LIMIT_REBOOT_FORCE] = "reboot-force", + [SERVICE_START_LIMIT_REBOOT_IMMEDIATE] = "reboot-immediate" +}; +DEFINE_STRING_TABLE_LOOKUP(start_limit_action, StartLimitAction); + const UnitVTable service_vtable = { .suffix = ".service", + .object_size = sizeof(Service), + .sections = + "Unit\0" + "Service\0" + "Install\0", .show_status = true, .init = service_init, @@ -3401,6 +3761,7 @@ const UnitVTable service_vtable = { .sigchld_event = service_sigchld_event, .timer_event = service_timer_event, + .fd_event = service_fd_event, .reset_failed = service_reset_failed, diff --git a/src/service.h b/src/service.h index e28f74b898..60b10516eb 100644 --- a/src/service.h +++ b/src/service.h @@ -25,7 +25,9 @@ typedef struct Service Service; #include "unit.h" +#include "path.h" #include "ratelimit.h" +#include "service.h" typedef enum ServiceState { SERVICE_DEAD, @@ -86,8 +88,29 @@ typedef enum NotifyAccess { _NOTIFY_ACCESS_INVALID = -1 } NotifyAccess; +typedef enum ServiceResult { + SERVICE_SUCCESS, + SERVICE_FAILURE_RESOURCES, + SERVICE_FAILURE_TIMEOUT, + SERVICE_FAILURE_EXIT_CODE, + SERVICE_FAILURE_SIGNAL, + SERVICE_FAILURE_CORE_DUMP, + SERVICE_FAILURE_WATCHDOG, + _SERVICE_RESULT_MAX, + _SERVICE_RESULT_INVALID = -1 +} ServiceResult; + +typedef enum StartLimitAction { + SERVICE_START_LIMIT_NONE, + SERVICE_START_LIMIT_REBOOT, + SERVICE_START_LIMIT_REBOOT_FORCE, + SERVICE_START_LIMIT_REBOOT_IMMEDIATE, + _SERVICE_START_LIMIT_MAX, + _SERVICE_START_LIMIT_INVALID = -1 +} StartLimitAction; + struct Service { - Meta meta; + Unit meta; ServiceType type; ServiceRestart restart; @@ -98,6 +121,10 @@ struct Service { usec_t restart_usec; usec_t timeout_usec; + dual_timestamp watchdog_timestamp; + usec_t watchdog_usec; + Watch watchdog_watch; + ExecCommand* exec_command[_SERVICE_EXEC_COMMAND_MAX]; ExecContext exec_context; @@ -128,8 +155,8 @@ struct Service { bool guess_main_pid; /* If we shut down, remember why */ - bool failure:1; - bool reload_failure:1; + ServiceResult result; + ServiceResult reload_result; bool main_pid_known:1; bool main_pid_alien:1; @@ -151,18 +178,22 @@ struct Service { char *status_text; - RateLimit ratelimit; + RateLimit start_limit; + StartLimitAction start_limit_action; + - struct Socket *accept_socket; - Set *configured_sockets; + UnitRef accept_socket; Watch timer_watch; + PathSpec *pid_file_pathspec; NotifyAccess notify_access; }; extern const UnitVTable service_vtable; +struct Socket; + int service_set_socket_fd(Service *s, int fd, struct Socket *socket); const char* service_state_to_string(ServiceState i); @@ -180,4 +211,10 @@ ServiceExecCommand service_exec_command_from_string(const char *s); const char* notify_access_to_string(NotifyAccess i); NotifyAccess notify_access_from_string(const char *s); +const char* service_result_to_string(ServiceResult i); +ServiceResult service_result_from_string(const char *s); + +const char* start_limit_action_to_string(StartLimitAction i); +StartLimitAction start_limit_action_from_string(const char *s); + #endif diff --git a/src/shutdown.c b/src/shutdown.c index 6714927f86..d157e0fbfe 100644 --- a/src/shutdown.c +++ b/src/shutdown.c @@ -41,34 +41,53 @@ #include "log.h" #include "umount.h" #include "util.h" +#include "virt.h" #define TIMEOUT_USEC (5 * USEC_PER_SEC) #define FINALIZE_ATTEMPTS 50 static bool ignore_proc(pid_t pid) { + char buf[PATH_MAX]; + FILE *f; + char c; + size_t count; + uid_t uid; + int r; + + /* We are PID 1, let's not commit suicide */ if (pid == 1) return true; - /* TODO: add more ignore rules here: device-mapper, etc */ + r = get_process_uid(pid, &uid); + if (r < 0) + return true; /* not really, but better safe than sorry */ - return false; -} + /* Non-root processes otherwise are always subject to be killed */ + if (uid != 0) + return false; -static bool is_kernel_thread(pid_t pid) -{ - char buf[PATH_MAX]; - FILE *f; - char c; - size_t count; + snprintf(buf, sizeof(buf), "/proc/%lu/cmdline", (unsigned long) pid); + char_array_0(buf); - snprintf(buf, sizeof(buf), "/proc/%lu/cmdline", (unsigned long)pid); f = fopen(buf, "re"); if (!f) return true; /* not really, but has the desired effect */ count = fread(&c, 1, 1, f); fclose(f); - return count != 1; + + /* Kernel threads have an empty cmdline */ + if (count <= 0) + return true; + + /* Processes with argv[0][0] = '@' we ignore from the killing + * spree. + * + * http://www.freedesktop.org/wiki/Software/systemd/RootStorageDaemons */ + if (count == 1 && c == '@') + return true; + + return false; } static int killall(int sign) { @@ -76,7 +95,8 @@ static int killall(int sign) { struct dirent *d; unsigned int n_processes = 0; - if ((dir = opendir("/proc")) == NULL) + dir = opendir("/proc"); + if (!dir) return -errno; while ((d = readdir(dir))) { @@ -85,9 +105,6 @@ static int killall(int sign) { if (parse_pid(d->d_name, &pid) < 0) continue; - if (is_kernel_thread(pid)) - continue; - if (ignore_proc(pid)) continue; @@ -295,6 +312,8 @@ int main(int argc, char *argv[]) { log_set_target(LOG_TARGET_CONSOLE); /* syslog will die if not gone yet */ log_open(); + umask(0022); + if (getpid() != 1) { log_error("Not executed by init (pid 1)."); r = -EPERM; @@ -384,9 +403,12 @@ int main(int argc, char *argv[]) { log_error("Failed to detach DM devices: %s", strerror(-r)); } - if (!need_umount && !need_swapoff && !need_loop_detach && !need_dm_detach) + if (!need_umount && !need_swapoff && !need_loop_detach && !need_dm_detach) { + if (retries > 0) + log_info("All filesystems, swaps, loop devices, DM devices detached."); /* Yay, done */ break; + } /* If in this iteration we didn't manage to * unmount/deactivate anything, we either kill more diff --git a/src/shutdownd.c b/src/shutdownd.c index 49ab8863e4..b4052d4933 100644 --- a/src/shutdownd.c +++ b/src/shutdownd.c @@ -29,11 +29,12 @@ #include <unistd.h> #include <fcntl.h> +#include <systemd/sd-daemon.h> + #include "shutdownd.h" #include "log.h" #include "macro.h" #include "util.h" -#include "sd-daemon.h" #include "utmp-wtmp.h" static int read_packet(int fd, struct shutdownd_command *_c) { @@ -173,7 +174,6 @@ int main(int argc, char *argv[]) { }; int r = EXIT_FAILURE, n_fds; - int one = 1; struct shutdownd_command c; struct pollfd pollfd[_FD_MAX]; bool exec_shutdown = false, unlink_nologin = false, failed = false; @@ -189,10 +189,12 @@ int main(int argc, char *argv[]) { return EXIT_FAILURE; } - log_set_target(LOG_TARGET_SYSLOG_OR_KMSG); + log_set_target(LOG_TARGET_AUTO); log_parse_environment(); log_open(); + umask(0022); + if ((n_fds = sd_listen_fds(true)) < 0) { log_error("Failed to read listening file descriptors from environment: %s", strerror(-r)); return EXIT_FAILURE; @@ -203,11 +205,6 @@ int main(int argc, char *argv[]) { return EXIT_FAILURE; } - if (setsockopt(SD_LISTEN_FDS_START, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)) < 0) { - log_error("SO_PASSCRED failed: %m"); - return EXIT_FAILURE; - } - zero(c); zero(pollfd); diff --git a/src/snapshot.c b/src/snapshot.c index 270dc4f252..82ec5104db 100644 --- a/src/snapshot.c +++ b/src/snapshot.c @@ -36,10 +36,10 @@ static void snapshot_init(Unit *u) { Snapshot *s = SNAPSHOT(u); assert(s); - assert(s->meta.load_state == UNIT_STUB); + assert(UNIT(s)->load_state == UNIT_STUB); - s->meta.ignore_on_isolate = true; - s->meta.ignore_on_snapshot = true; + UNIT(s)->ignore_on_isolate = true; + UNIT(s)->ignore_on_snapshot = true; } static void snapshot_set_state(Snapshot *s, SnapshotState state) { @@ -51,7 +51,7 @@ static void snapshot_set_state(Snapshot *s, SnapshotState state) { if (state != old_state) log_debug("%s changed %s -> %s", - s->meta.id, + UNIT(s)->id, snapshot_state_to_string(old_state), snapshot_state_to_string(state)); @@ -62,14 +62,14 @@ static int snapshot_load(Unit *u) { Snapshot *s = SNAPSHOT(u); assert(u); - assert(u->meta.load_state == UNIT_STUB); + assert(u->load_state == UNIT_STUB); /* Make sure that only snapshots created via snapshot_create() * can be loaded */ - if (!s->by_snapshot_create && s->meta.manager->n_reloading <= 0) + if (!s->by_snapshot_create && UNIT(s)->manager->n_reloading <= 0) return -ENOENT; - u->meta.load_state = UNIT_LOADED; + u->load_state = UNIT_LOADED; return 0; } @@ -133,8 +133,8 @@ static int snapshot_serialize(Unit *u, FILE *f, FDSet *fds) { unit_serialize_item(u, f, "state", snapshot_state_to_string(s->state)); unit_serialize_item(u, f, "cleanup", yes_no(s->cleanup)); - SET_FOREACH(other, u->meta.dependencies[UNIT_WANTS], i) - unit_serialize_item(u, f, "wants", other->meta.id); + SET_FOREACH(other, u->dependencies[UNIT_WANTS], i) + unit_serialize_item(u, f, "wants", other->id); return 0; } @@ -234,14 +234,14 @@ int snapshot_create(Manager *m, const char *name, bool cleanup, DBusError *e, Sn SNAPSHOT(u)->by_snapshot_create = true; manager_dispatch_load_queue(m); - assert(u->meta.load_state == UNIT_LOADED); + assert(u->load_state == UNIT_LOADED); HASHMAP_FOREACH_KEY(other, k, m->units, i) { - if (other->meta.ignore_on_snapshot) + if (other->ignore_on_snapshot) continue; - if (k != other->meta.id) + if (k != other->id) continue; if (UNIT_VTABLE(other)->check_snapshot) @@ -282,6 +282,7 @@ DEFINE_STRING_TABLE_LOOKUP(snapshot_state, SnapshotState); const UnitVTable snapshot_vtable = { .suffix = ".snapshot", + .object_size = sizeof(Snapshot), .no_alias = true, .no_instances = true, diff --git a/src/snapshot.h b/src/snapshot.h index 9a305204b9..bf92e99a8b 100644 --- a/src/snapshot.h +++ b/src/snapshot.h @@ -34,7 +34,7 @@ typedef enum SnapshotState { } SnapshotState; struct Snapshot { - Meta meta; + Unit meta; SnapshotState state, deserialized_state; diff --git a/src/socket-util.c b/src/socket-util.c index e2e89886d4..acc4d33372 100644 --- a/src/socket-util.c +++ b/src/socket-util.c @@ -458,7 +458,7 @@ int socket_address_listen( /* Include the original umask in our mask */ umask(~socket_mode | old_mask); - r = bind(fd, &a->sockaddr.sa, a->size); + r = label_bind(fd, &a->sockaddr.sa, a->size); if (r < 0 && errno == EADDRINUSE) { /* Unlink and try again */ diff --git a/src/socket.c b/src/socket.c index 64d1028d4f..15a517bed7 100644 --- a/src/socket.c +++ b/src/socket.c @@ -64,7 +64,7 @@ static void socket_init(Unit *u) { Socket *s = SOCKET(u); assert(u); - assert(u->meta.load_state == UNIT_STUB); + assert(u->load_state == UNIT_STUB); s->backlog = SOMAXCONN; s->timeout_usec = DEFAULT_TIMEOUT_USEC; @@ -79,8 +79,8 @@ static void socket_init(Unit *u) { s->mark = -1; exec_context_init(&s->exec_context); - s->exec_context.std_output = u->meta.manager->default_std_output; - s->exec_context.std_error = u->meta.manager->default_std_error; + s->exec_context.std_output = u->manager->default_std_output; + s->exec_context.std_error = u->manager->default_std_error; s->control_command_id = _SOCKET_EXEC_COMMAND_INVALID; } @@ -98,7 +98,6 @@ static void socket_unwatch_control_pid(Socket *s) { static void socket_done(Unit *u) { Socket *s = SOCKET(u); SocketPort *p; - Meta *i; assert(s); @@ -120,7 +119,7 @@ static void socket_done(Unit *u) { socket_unwatch_control_pid(s); - s->service = NULL; + unit_ref_unset(&s->service); free(s->tcp_congestion); s->tcp_congestion = NULL; @@ -129,16 +128,6 @@ static void socket_done(Unit *u) { s->bind_to_device = NULL; unit_unwatch_timer(u, &s->timer_watch); - - /* Make sure no service instance refers to us anymore. */ - LIST_FOREACH(units_by_type, i, u->meta.manager->units_by_type[UNIT_SERVICE]) { - Service *service = (Service *) i; - - if (service->accept_socket == s) - service->accept_socket = NULL; - - set_remove(service->configured_sockets, s); - } } static int socket_instantiate_service(Socket *s) { @@ -153,12 +142,12 @@ static int socket_instantiate_service(Socket *s) { * here. For Accept=no this is mostly a NOP since the service * is figured out at load time anyway. */ - if (s->service) + if (UNIT_DEREF(s->service)) return 0; assert(s->accept); - if (!(prefix = unit_name_to_prefix(s->meta.id))) + if (!(prefix = unit_name_to_prefix(UNIT(s)->id))) return -ENOMEM; r = asprintf(&name, "%s@%u.service", prefix, s->n_accepted); @@ -167,7 +156,7 @@ static int socket_instantiate_service(Socket *s) { if (r < 0) return -ENOMEM; - r = manager_load_unit(s->meta.manager, name, NULL, NULL, &u); + r = manager_load_unit(UNIT(s)->manager, name, NULL, NULL, &u); free(name); if (r < 0) @@ -180,9 +169,10 @@ static int socket_instantiate_service(Socket *s) { } #endif - u->meta.no_gc = true; - s->service = SERVICE(u); - return 0; + u->no_gc = true; + unit_ref_set(&s->service, u); + + return unit_add_two_dependencies(UNIT(s), UNIT_BEFORE, UNIT_TRIGGERS, u, false); } static bool have_non_accept_socket(Socket *s) { @@ -208,31 +198,31 @@ static bool have_non_accept_socket(Socket *s) { static int socket_verify(Socket *s) { assert(s); - if (s->meta.load_state != UNIT_LOADED) + if (UNIT(s)->load_state != UNIT_LOADED) return 0; if (!s->ports) { - log_error("%s lacks Listen setting. Refusing.", s->meta.id); + log_error("%s lacks Listen setting. Refusing.", UNIT(s)->id); return -EINVAL; } if (s->accept && have_non_accept_socket(s)) { - log_error("%s configured for accepting sockets, but sockets are non-accepting. Refusing.", s->meta.id); + log_error("%s configured for accepting sockets, but sockets are non-accepting. Refusing.", UNIT(s)->id); return -EINVAL; } if (s->accept && s->max_connections <= 0) { - log_error("%s's MaxConnection setting too small. Refusing.", s->meta.id); + log_error("%s's MaxConnection setting too small. Refusing.", UNIT(s)->id); return -EINVAL; } - if (s->accept && s->service) { - log_error("Explicit service configuration for accepting sockets not supported on %s. Refusing.", s->meta.id); + if (s->accept && UNIT_DEREF(s->service)) { + log_error("Explicit service configuration for accepting sockets not supported on %s. Refusing.", UNIT(s)->id); return -EINVAL; } if (s->exec_context.pam_name && s->exec_context.kill_mode != KILL_CONTROL_GROUP) { - log_error("%s has PAM enabled. Kill mode must be set to 'control-group'. Refusing.", s->meta.id); + log_error("%s has PAM enabled. Kill mode must be set to 'control-group'. Refusing.", UNIT(s)->id); return -EINVAL; } @@ -264,8 +254,8 @@ int socket_add_one_mount_link(Socket *s, Mount *m) { assert(s); assert(m); - if (s->meta.load_state != UNIT_LOADED || - m->meta.load_state != UNIT_LOADED) + if (UNIT(s)->load_state != UNIT_LOADED || + UNIT(m)->load_state != UNIT_LOADED) return 0; if (!socket_needs_mount(s, m->where)) @@ -278,13 +268,13 @@ int socket_add_one_mount_link(Socket *s, Mount *m) { } static int socket_add_mount_links(Socket *s) { - Meta *other; + Unit *other; int r; assert(s); - LIST_FOREACH(units_by_type, other, s->meta.manager->units_by_type[UNIT_MOUNT]) - if ((r = socket_add_one_mount_link(s, (Mount*) other)) < 0) + LIST_FOREACH(units_by_type, other, UNIT(s)->manager->units_by_type[UNIT_MOUNT]) + if ((r = socket_add_one_mount_link(s, MOUNT(other))) < 0) return r; return 0; @@ -312,7 +302,7 @@ static int socket_add_default_dependencies(Socket *s) { int r; assert(s); - if (s->meta.manager->running_as == MANAGER_SYSTEM) { + if (UNIT(s)->manager->running_as == MANAGER_SYSTEM) { if ((r = unit_add_dependency_by_name(UNIT(s), UNIT_BEFORE, SPECIAL_SOCKETS_TARGET, NULL, true)) < 0) return r; @@ -323,26 +313,44 @@ static int socket_add_default_dependencies(Socket *s) { return unit_add_two_dependencies_by_name(UNIT(s), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, NULL, true); } +static bool socket_has_exec(Socket *s) { + unsigned i; + assert(s); + + for (i = 0; i < _SOCKET_EXEC_COMMAND_MAX; i++) + if (s->exec_command[i]) + return true; + + return false; +} + static int socket_load(Unit *u) { Socket *s = SOCKET(u); int r; assert(u); - assert(u->meta.load_state == UNIT_STUB); + assert(u->load_state == UNIT_STUB); if ((r = unit_load_fragment_and_dropin(u)) < 0) return r; /* This is a new unit? Then let's add in some extras */ - if (u->meta.load_state == UNIT_LOADED) { + if (u->load_state == UNIT_LOADED) { if (have_non_accept_socket(s)) { - if (!s->service) - if ((r = unit_load_related_unit(u, ".service", (Unit**) &s->service)) < 0) + if (!UNIT_DEREF(s->service)) { + Unit *x; + + r = unit_load_related_unit(u, ".service", &x); + if (r < 0) return r; - if ((r = unit_add_dependency(u, UNIT_BEFORE, UNIT(s->service), true)) < 0) + unit_ref_set(&s->service, x); + } + + r = unit_add_two_dependencies(u, UNIT_BEFORE, UNIT_TRIGGERS, UNIT_DEREF(s->service), true); + if (r < 0) return r; } @@ -352,13 +360,14 @@ static int socket_load(Unit *u) { if ((r = socket_add_device_link(s)) < 0) return r; - if ((r = unit_add_exec_dependencies(u, &s->exec_context)) < 0) - return r; + if (socket_has_exec(s)) + if ((r = unit_add_exec_dependencies(u, &s->exec_context)) < 0) + return r; if ((r = unit_add_default_cgroups(u)) < 0) return r; - if (s->meta.default_dependencies) + if (UNIT(s)->default_dependencies) if ((r = socket_add_default_dependencies(s)) < 0) return r; } @@ -398,6 +407,7 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) { fprintf(f, "%sSocket State: %s\n" + "%sResult: %s\n" "%sBindIPv6Only: %s\n" "%sBacklog: %u\n" "%sSocketMode: %04o\n" @@ -406,8 +416,10 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) { "%sFreeBind: %s\n" "%sTransparent: %s\n" "%sBroadcast: %s\n" + "%sPassCredentials: %s\n" "%sTCPCongestion: %s\n", prefix, socket_state_to_string(s->state), + prefix, socket_result_to_string(s->result), prefix, socket_address_bind_ipv6_only_to_string(s->bind_ipv6_only), prefix, s->backlog, prefix, s->socket_mode, @@ -416,6 +428,7 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) { prefix, yes_no(s->free_bind), prefix, yes_no(s->transparent), prefix, yes_no(s->broadcast), + prefix, yes_no(s->pass_cred), prefix, strna(s->tcp_congestion)); if (s->control_pid > 0) @@ -657,20 +670,31 @@ static void socket_apply_socket_options(Socket *s, int fd) { log_warning("SO_BROADCAST failed: %m"); } + if (s->pass_cred) { + int one = 1; + if (setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)) < 0) + log_warning("SO_PASSCRED failed: %m"); + } + if (s->priority >= 0) if (setsockopt(fd, SOL_SOCKET, SO_PRIORITY, &s->priority, sizeof(s->priority)) < 0) log_warning("SO_PRIORITY failed: %m"); if (s->receive_buffer > 0) { int value = (int) s->receive_buffer; + + /* We first try with SO_RCVBUFFORCE, in case we have the perms for that */ + if (setsockopt(fd, SOL_SOCKET, SO_RCVBUFFORCE, &value, sizeof(value)) < 0) - log_warning("SO_RCVBUFFORCE failed: %m"); + if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &value, sizeof(value)) < 0) + log_warning("SO_RCVBUF failed: %m"); } if (s->send_buffer > 0) { int value = (int) s->send_buffer; if (setsockopt(fd, SOL_SOCKET, SO_SNDBUFFORCE, &value, sizeof(value)) < 0) - log_warning("SO_SNDBUFFORCE failed: %m"); + if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &value, sizeof(value)) < 0) + log_warning("SO_SNDBUF failed: %m"); } if (s->mark >= 0) @@ -844,7 +868,7 @@ static int mq_address_create( fd = mq_open(path, O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_CREAT, mq_mode, attr); umask(old_mask); - if (fd < 0 && errno != EEXIST) { + if (fd < 0) { r = -errno; goto fail; } @@ -892,11 +916,15 @@ static int socket_open_fds(Socket *s) { if ((r = socket_instantiate_service(s)) < 0) return r; - if (s->service && s->service->exec_command[SERVICE_EXEC_START]) - if ((r = label_get_socket_label_from_exe(s->service->exec_command[SERVICE_EXEC_START]->path, &label)) < 0) { + if (UNIT_DEREF(s->service) && + SERVICE(UNIT_DEREF(s->service))->exec_command[SERVICE_EXEC_START]) { + r = label_get_create_label_from_exe(SERVICE(UNIT_DEREF(s->service))->exec_command[SERVICE_EXEC_START]->path, &label); + + if (r < 0) { if (r != -EPERM) return r; } + } know_label = true; } @@ -1028,7 +1056,7 @@ static void socket_set_state(Socket *s, SocketState state) { if (state != old_state) log_debug("%s changed %s -> %s", - s->meta.id, + UNIT(s)->id, socket_state_to_string(old_state), socket_state_to_string(state)); @@ -1103,12 +1131,13 @@ static int socket_spawn(Socket *s, ExecCommand *c, pid_t *_pid) { argv, &s->exec_context, NULL, 0, - s->meta.manager->environment, + UNIT(s)->manager->environment, true, true, true, - s->meta.manager->confirm_spawn, - s->meta.cgroup_bondings, + UNIT(s)->manager->confirm_spawn, + UNIT(s)->cgroup_bondings, + UNIT(s)->cgroup_attributes, &pid); strv_free(argv); @@ -1129,23 +1158,23 @@ fail: return r; } -static void socket_enter_dead(Socket *s, bool success) { +static void socket_enter_dead(Socket *s, SocketResult f) { assert(s); - if (!success) - s->failure = true; + if (f != SOCKET_SUCCESS) + s->result = f; - socket_set_state(s, s->failure ? SOCKET_FAILED : SOCKET_DEAD); + socket_set_state(s, s->result != SOCKET_SUCCESS ? SOCKET_FAILED : SOCKET_DEAD); } -static void socket_enter_signal(Socket *s, SocketState state, bool success); +static void socket_enter_signal(Socket *s, SocketState state, SocketResult f); -static void socket_enter_stop_post(Socket *s, bool success) { +static void socket_enter_stop_post(Socket *s, SocketResult f) { int r; assert(s); - if (!success) - s->failure = true; + if (f != SOCKET_SUCCESS) + s->result = f; socket_unwatch_control_pid(s); @@ -1157,24 +1186,24 @@ static void socket_enter_stop_post(Socket *s, bool success) { socket_set_state(s, SOCKET_STOP_POST); } else - socket_enter_signal(s, SOCKET_FINAL_SIGTERM, true); + socket_enter_signal(s, SOCKET_FINAL_SIGTERM, SOCKET_SUCCESS); return; fail: - log_warning("%s failed to run 'stop-post' task: %s", s->meta.id, strerror(-r)); - socket_enter_signal(s, SOCKET_FINAL_SIGTERM, false); + log_warning("%s failed to run 'stop-post' task: %s", UNIT(s)->id, strerror(-r)); + socket_enter_signal(s, SOCKET_FINAL_SIGTERM, SOCKET_FAILURE_RESOURCES); } -static void socket_enter_signal(Socket *s, SocketState state, bool success) { +static void socket_enter_signal(Socket *s, SocketState state, SocketResult f) { int r; Set *pid_set = NULL; bool wait_for_exit = false; assert(s); - if (!success) - s->failure = true; + if (f != SOCKET_SUCCESS) + s->result = f; if (s->exec_context.kill_mode != KILL_NONE) { int sig = (state == SOCKET_STOP_PRE_SIGTERM || state == SOCKET_FINAL_SIGTERM) ? s->exec_context.kill_signal : SIGKILL; @@ -1199,7 +1228,7 @@ static void socket_enter_signal(Socket *s, SocketState state, bool success) { if ((r = set_put(pid_set, LONG_TO_PTR(s->control_pid))) < 0) goto fail; - if ((r = cgroup_bonding_kill_list(s->meta.cgroup_bondings, sig, true, pid_set)) < 0) { + if ((r = cgroup_bonding_kill_list(UNIT(s)->cgroup_bondings, sig, true, pid_set)) < 0) { if (r != -EAGAIN && r != -ESRCH && r != -ENOENT) log_warning("Failed to kill control group: %s", strerror(-r)); } else if (r > 0) @@ -1216,30 +1245,30 @@ static void socket_enter_signal(Socket *s, SocketState state, bool success) { socket_set_state(s, state); } else if (state == SOCKET_STOP_PRE_SIGTERM || state == SOCKET_STOP_PRE_SIGKILL) - socket_enter_stop_post(s, true); + socket_enter_stop_post(s, SOCKET_SUCCESS); else - socket_enter_dead(s, true); + socket_enter_dead(s, SOCKET_SUCCESS); return; fail: - log_warning("%s failed to kill processes: %s", s->meta.id, strerror(-r)); + log_warning("%s failed to kill processes: %s", UNIT(s)->id, strerror(-r)); if (state == SOCKET_STOP_PRE_SIGTERM || state == SOCKET_STOP_PRE_SIGKILL) - socket_enter_stop_post(s, false); + socket_enter_stop_post(s, SOCKET_FAILURE_RESOURCES); else - socket_enter_dead(s, false); + socket_enter_dead(s, SOCKET_FAILURE_RESOURCES); if (pid_set) set_free(pid_set); } -static void socket_enter_stop_pre(Socket *s, bool success) { +static void socket_enter_stop_pre(Socket *s, SocketResult f) { int r; assert(s); - if (!success) - s->failure = true; + if (f != SOCKET_SUCCESS) + s->result = f; socket_unwatch_control_pid(s); @@ -1251,21 +1280,22 @@ static void socket_enter_stop_pre(Socket *s, bool success) { socket_set_state(s, SOCKET_STOP_PRE); } else - socket_enter_stop_post(s, true); + socket_enter_stop_post(s, SOCKET_SUCCESS); return; fail: - log_warning("%s failed to run 'stop-pre' task: %s", s->meta.id, strerror(-r)); - socket_enter_stop_post(s, false); + log_warning("%s failed to run 'stop-pre' task: %s", UNIT(s)->id, strerror(-r)); + socket_enter_stop_post(s, SOCKET_FAILURE_RESOURCES); } static void socket_enter_listening(Socket *s) { int r; assert(s); - if ((r = socket_watch_fds(s)) < 0) { - log_warning("%s failed to watch sockets: %s", s->meta.id, strerror(-r)); + r = socket_watch_fds(s); + if (r < 0) { + log_warning("%s failed to watch sockets: %s", UNIT(s)->id, strerror(-r)); goto fail; } @@ -1273,15 +1303,16 @@ static void socket_enter_listening(Socket *s) { return; fail: - socket_enter_stop_pre(s, false); + socket_enter_stop_pre(s, SOCKET_FAILURE_RESOURCES); } static void socket_enter_start_post(Socket *s) { int r; assert(s); - if ((r = socket_open_fds(s)) < 0) { - log_warning("%s failed to listen on sockets: %s", s->meta.id, strerror(-r)); + r = socket_open_fds(s); + if (r < 0) { + log_warning("%s failed to listen on sockets: %s", UNIT(s)->id, strerror(-r)); goto fail; } @@ -1290,8 +1321,9 @@ static void socket_enter_start_post(Socket *s) { s->control_command_id = SOCKET_EXEC_START_POST; if ((s->control_command = s->exec_command[SOCKET_EXEC_START_POST])) { - if ((r = socket_spawn(s, s->control_command, &s->control_pid)) < 0) { - log_warning("%s failed to run 'start-post' task: %s", s->meta.id, strerror(-r)); + r = socket_spawn(s, s->control_command, &s->control_pid); + if (r < 0) { + log_warning("%s failed to run 'start-post' task: %s", UNIT(s)->id, strerror(-r)); goto fail; } @@ -1302,7 +1334,7 @@ static void socket_enter_start_post(Socket *s) { return; fail: - socket_enter_stop_pre(s, false); + socket_enter_stop_pre(s, SOCKET_FAILURE_RESOURCES); } static void socket_enter_start_pre(Socket *s) { @@ -1324,8 +1356,8 @@ static void socket_enter_start_pre(Socket *s) { return; fail: - log_warning("%s failed to run 'start-pre' task: %s", s->meta.id, strerror(-r)); - socket_enter_dead(s, false); + log_warning("%s failed to run 'start-pre' task: %s", UNIT(s)->id, strerror(-r)); + socket_enter_dead(s, SOCKET_FAILURE_RESOURCES); } static void socket_enter_running(Socket *s, int cfd) { @@ -1338,7 +1370,7 @@ static void socket_enter_running(Socket *s, int cfd) { /* We don't take connections anymore if we are supposed to * shut down anyway */ if (unit_pending_inactive(UNIT(s))) { - log_debug("Suppressing connection request on %s since unit stop is scheduled.", s->meta.id); + log_debug("Suppressing connection request on %s since unit stop is scheduled.", UNIT(s)->id); if (cfd >= 0) close_nointr_nofail(cfd); @@ -1346,9 +1378,10 @@ static void socket_enter_running(Socket *s, int cfd) { /* Flush all sockets by closing and reopening them */ socket_close_fds(s); - if ((r = socket_watch_fds(s)) < 0) { - log_warning("%s failed to watch sockets: %s", s->meta.id, strerror(-r)); - socket_enter_stop_pre(s, false); + r = socket_watch_fds(s); + if (r < 0) { + log_warning("%s failed to watch sockets: %s", UNIT(s)->id, strerror(-r)); + socket_enter_stop_pre(s, SOCKET_FAILURE_RESOURCES); } } @@ -1356,27 +1389,23 @@ static void socket_enter_running(Socket *s, int cfd) { } if (cfd < 0) { + Iterator i; + Unit *u; bool pending = false; - Meta *i; /* If there's already a start pending don't bother to * do anything */ - LIST_FOREACH(units_by_type, i, s->meta.manager->units_by_type[UNIT_SERVICE]) { - Service *service = (Service *) i; - - if (!set_get(service->configured_sockets, s)) - continue; - - if (!unit_pending_active(UNIT(service))) - continue; - - pending = true; - break; - } + SET_FOREACH(u, UNIT(s)->dependencies[UNIT_TRIGGERS], i) + if (unit_pending_active(u)) { + pending = true; + break; + } - if (!pending) - if ((r = manager_add_job(s->meta.manager, JOB_START, UNIT(s->service), JOB_REPLACE, true, &error, NULL)) < 0) + if (!pending) { + r = manager_add_job(UNIT(s)->manager, JOB_START, UNIT_DEREF(s->service), JOB_REPLACE, true, &error, NULL); + if (r < 0) goto fail; + } socket_set_state(s, SOCKET_RUNNING); } else { @@ -1389,13 +1418,23 @@ static void socket_enter_running(Socket *s, int cfd) { return; } - if ((r = socket_instantiate_service(s)) < 0) + r = socket_instantiate_service(s); + if (r < 0) goto fail; - if ((r = instance_from_socket(cfd, s->n_accepted, &instance)) < 0) - goto fail; + r = instance_from_socket(cfd, s->n_accepted, &instance); + if (r < 0) { + if (r != -ENOTCONN) + goto fail; - if (!(prefix = unit_name_to_prefix(s->meta.id))) { + /* ENOTCONN is legitimate if TCP RST was received. + * This connection is over, but the socket unit lives on. */ + close_nointr_nofail(cfd); + return; + } + + prefix = unit_name_to_prefix(UNIT(s)->id); + if (!prefix) { free(instance); r = -ENOMEM; goto fail; @@ -1410,27 +1449,30 @@ static void socket_enter_running(Socket *s, int cfd) { goto fail; } - if ((r = unit_add_name(UNIT(s->service), name)) < 0) { + r = unit_add_name(UNIT_DEREF(s->service), name); + if (r < 0) { free(name); goto fail; } - service = s->service; - s->service = NULL; + service = SERVICE(UNIT_DEREF(s->service)); + unit_ref_unset(&s->service); s->n_accepted ++; - service->meta.no_gc = false; + UNIT(service)->no_gc = false; unit_choose_id(UNIT(service), name); free(name); - if ((r = service_set_socket_fd(service, cfd, s)) < 0) + r = service_set_socket_fd(service, cfd, s); + if (r < 0) goto fail; cfd = -1; s->n_connections ++; - if ((r = manager_add_job(s->meta.manager, JOB_START, UNIT(service), JOB_REPLACE, true, &error, NULL)) < 0) + r = manager_add_job(UNIT(s)->manager, JOB_START, UNIT(service), JOB_REPLACE, true, &error, NULL); + if (r < 0) goto fail; /* Notify clients about changed counters */ @@ -1440,8 +1482,8 @@ static void socket_enter_running(Socket *s, int cfd) { return; fail: - log_warning("%s failed to queue socket startup job: %s", s->meta.id, bus_error(&error, r)); - socket_enter_stop_pre(s, false); + log_warning("%s failed to queue socket startup job: %s", UNIT(s)->id, bus_error(&error, r)); + socket_enter_stop_pre(s, SOCKET_FAILURE_RESOURCES); if (cfd >= 0) close_nointr_nofail(cfd); @@ -1449,16 +1491,13 @@ fail: dbus_error_free(&error); } -static void socket_run_next(Socket *s, bool success) { +static void socket_run_next(Socket *s) { int r; assert(s); assert(s->control_command); assert(s->control_command->command_next); - if (!success) - s->failure = true; - socket_unwatch_control_pid(s); s->control_command = s->control_command->command_next; @@ -1469,14 +1508,14 @@ static void socket_run_next(Socket *s, bool success) { return; fail: - log_warning("%s failed to run next task: %s", s->meta.id, strerror(-r)); + log_warning("%s failed to run next task: %s", UNIT(s)->id, strerror(-r)); if (s->state == SOCKET_START_POST) - socket_enter_stop_pre(s, false); + socket_enter_stop_pre(s, SOCKET_FAILURE_RESOURCES); else if (s->state == SOCKET_STOP_POST) - socket_enter_dead(s, false); + socket_enter_dead(s, SOCKET_FAILURE_RESOURCES); else - socket_enter_signal(s, SOCKET_FINAL_SIGTERM, false); + socket_enter_signal(s, SOCKET_FINAL_SIGTERM, SOCKET_FAILURE_RESOURCES); } static int socket_start(Unit *u) { @@ -1499,23 +1538,27 @@ static int socket_start(Unit *u) { return 0; /* Cannot run this without the service being around */ - if (s->service) { - if (s->service->meta.load_state != UNIT_LOADED) { - log_error("Socket service %s not loaded, refusing.", s->service->meta.id); + if (UNIT_DEREF(s->service)) { + Service *service; + + service = SERVICE(UNIT_DEREF(s->service)); + + if (UNIT(service)->load_state != UNIT_LOADED) { + log_error("Socket service %s not loaded, refusing.", UNIT(service)->id); return -ENOENT; } /* If the service is already active we cannot start the * socket */ - if (s->service->state != SERVICE_DEAD && - s->service->state != SERVICE_FAILED && - s->service->state != SERVICE_AUTO_RESTART) { - log_error("Socket service %s already active, refusing.", s->service->meta.id); + if (service->state != SERVICE_DEAD && + service->state != SERVICE_FAILED && + service->state != SERVICE_AUTO_RESTART) { + log_error("Socket service %s already active, refusing.", UNIT(service)->id); return -EBUSY; } #ifdef HAVE_SYSV_COMPAT - if (s->service->sysv_path) { + if (service->sysv_path) { log_error("Using SysV services for socket activation is not supported. Refusing."); return -ENOENT; } @@ -1524,7 +1567,7 @@ static int socket_start(Unit *u) { assert(s->state == SOCKET_DEAD || s->state == SOCKET_FAILED); - s->failure = false; + s->result = SOCKET_SUCCESS; socket_enter_start_pre(s); return 0; } @@ -1547,13 +1590,13 @@ static int socket_stop(Unit *u) { * kill mode. */ if (s->state == SOCKET_START_PRE || s->state == SOCKET_START_POST) { - socket_enter_signal(s, SOCKET_STOP_PRE_SIGTERM, true); + socket_enter_signal(s, SOCKET_STOP_PRE_SIGTERM, SOCKET_SUCCESS); return -EAGAIN; } assert(s->state == SOCKET_LISTENING || s->state == SOCKET_RUNNING); - socket_enter_stop_pre(s, true); + socket_enter_stop_pre(s, SOCKET_SUCCESS); return 0; } @@ -1567,7 +1610,7 @@ static int socket_serialize(Unit *u, FILE *f, FDSet *fds) { assert(fds); unit_serialize_item(u, f, "state", socket_state_to_string(s->state)); - unit_serialize_item(u, f, "failure", yes_no(s->failure)); + unit_serialize_item(u, f, "result", socket_result_to_string(s->result)); unit_serialize_item_format(u, f, "n-accepted", "%u", s->n_accepted); if (s->control_pid > 0) @@ -1622,13 +1665,14 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value, log_debug("Failed to parse state value %s", value); else s->deserialized_state = state; - } else if (streq(key, "failure")) { - int b; + } else if (streq(key, "result")) { + SocketResult f; - if ((b = parse_boolean(value)) < 0) - log_debug("Failed to parse failure value %s", value); - else - s->failure = b || s->failure; + f = socket_result_from_string(value); + if (f < 0) + log_debug("Failed to parse result value %s", value); + else if (f != SOCKET_SUCCESS) + s->result = f; } else if (streq(key, "n-accepted")) { unsigned k; @@ -1767,14 +1811,14 @@ static void socket_fd_event(Unit *u, int fd, uint32_t events, Watch *w) { if (s->state != SOCKET_LISTENING) return; - log_debug("Incoming traffic on %s", u->meta.id); + log_debug("Incoming traffic on %s", u->id); if (events != EPOLLIN) { if (events & EPOLLHUP) - log_error("%s: Got POLLHUP on a listening socket. The service probably invoked shutdown() on it, and should better not do that.", u->meta.id); + log_error("%s: Got POLLHUP on a listening socket. The service probably invoked shutdown() on it, and should better not do that.", u->id); else - log_error("%s: Got unexpected poll event (0x%x) on socket.", u->meta.id, events); + log_error("%s: Got unexpected poll event (0x%x) on socket.", u->id, events); goto fail; } @@ -1801,12 +1845,12 @@ static void socket_fd_event(Unit *u, int fd, uint32_t events, Watch *w) { return; fail: - socket_enter_stop_pre(s, false); + socket_enter_stop_pre(s, SOCKET_FAILURE_RESOURCES); } static void socket_sigchld_event(Unit *u, pid_t pid, int code, int status) { Socket *s = SOCKET(u); - bool success; + SocketResult f; assert(s); assert(pid >= 0); @@ -1816,22 +1860,36 @@ static void socket_sigchld_event(Unit *u, pid_t pid, int code, int status) { s->control_pid = 0; - success = is_clean_exit(code, status); + if (is_clean_exit(code, status)) + f = SOCKET_SUCCESS; + else if (code == CLD_EXITED) + f = SOCKET_FAILURE_EXIT_CODE; + else if (code == CLD_KILLED) + f = SOCKET_FAILURE_SIGNAL; + else if (code == CLD_DUMPED) + f = SOCKET_FAILURE_CORE_DUMP; + else + assert_not_reached("Unknown code"); if (s->control_command) { exec_status_exit(&s->control_command->exec_status, &s->exec_context, pid, code, status); if (s->control_command->ignore) - success = true; + f = SOCKET_SUCCESS; } - log_full(success ? LOG_DEBUG : LOG_NOTICE, - "%s control process exited, code=%s status=%i", u->meta.id, sigchld_code_to_string(code), status); - s->failure = s->failure || !success; + log_full(f == SOCKET_SUCCESS ? LOG_DEBUG : LOG_NOTICE, + "%s control process exited, code=%s status=%i", u->id, sigchld_code_to_string(code), status); + + if (f != SOCKET_SUCCESS) + s->result = f; + + if (s->control_command && + s->control_command->command_next && + f == SOCKET_SUCCESS) { - if (s->control_command && s->control_command->command_next && success) { - log_debug("%s running next command for state %s", u->meta.id, socket_state_to_string(s->state)); - socket_run_next(s, success); + log_debug("%s running next command for state %s", u->id, socket_state_to_string(s->state)); + socket_run_next(s); } else { s->control_command = NULL; s->control_command_id = _SOCKET_EXEC_COMMAND_INVALID; @@ -1839,34 +1897,34 @@ static void socket_sigchld_event(Unit *u, pid_t pid, int code, int status) { /* No further commands for this step, so let's figure * out what to do next */ - log_debug("%s got final SIGCHLD for state %s", u->meta.id, socket_state_to_string(s->state)); + log_debug("%s got final SIGCHLD for state %s", u->id, socket_state_to_string(s->state)); switch (s->state) { case SOCKET_START_PRE: - if (success) + if (f == SOCKET_SUCCESS) socket_enter_start_post(s); else - socket_enter_signal(s, SOCKET_FINAL_SIGTERM, false); + socket_enter_signal(s, SOCKET_FINAL_SIGTERM, f); break; case SOCKET_START_POST: - if (success) + if (f == SOCKET_SUCCESS) socket_enter_listening(s); else - socket_enter_stop_pre(s, false); + socket_enter_stop_pre(s, f); break; case SOCKET_STOP_PRE: case SOCKET_STOP_PRE_SIGTERM: case SOCKET_STOP_PRE_SIGKILL: - socket_enter_stop_post(s, success); + socket_enter_stop_post(s, f); break; case SOCKET_STOP_POST: case SOCKET_FINAL_SIGTERM: case SOCKET_FINAL_SIGKILL: - socket_enter_dead(s, success); + socket_enter_dead(s, f); break; default: @@ -1888,53 +1946,53 @@ static void socket_timer_event(Unit *u, uint64_t elapsed, Watch *w) { switch (s->state) { case SOCKET_START_PRE: - log_warning("%s starting timed out. Terminating.", u->meta.id); - socket_enter_signal(s, SOCKET_FINAL_SIGTERM, false); + log_warning("%s starting timed out. Terminating.", u->id); + socket_enter_signal(s, SOCKET_FINAL_SIGTERM, SOCKET_FAILURE_TIMEOUT); break; case SOCKET_START_POST: - log_warning("%s starting timed out. Stopping.", u->meta.id); - socket_enter_stop_pre(s, false); + log_warning("%s starting timed out. Stopping.", u->id); + socket_enter_stop_pre(s, SOCKET_FAILURE_TIMEOUT); break; case SOCKET_STOP_PRE: - log_warning("%s stopping timed out. Terminating.", u->meta.id); - socket_enter_signal(s, SOCKET_STOP_PRE_SIGTERM, false); + log_warning("%s stopping timed out. Terminating.", u->id); + socket_enter_signal(s, SOCKET_STOP_PRE_SIGTERM, SOCKET_FAILURE_TIMEOUT); break; case SOCKET_STOP_PRE_SIGTERM: if (s->exec_context.send_sigkill) { - log_warning("%s stopping timed out. Killing.", u->meta.id); - socket_enter_signal(s, SOCKET_STOP_PRE_SIGKILL, false); + log_warning("%s stopping timed out. Killing.", u->id); + socket_enter_signal(s, SOCKET_STOP_PRE_SIGKILL, SOCKET_FAILURE_TIMEOUT); } else { - log_warning("%s stopping timed out. Skipping SIGKILL. Ignoring.", u->meta.id); - socket_enter_stop_post(s, false); + log_warning("%s stopping timed out. Skipping SIGKILL. Ignoring.", u->id); + socket_enter_stop_post(s, SOCKET_FAILURE_TIMEOUT); } break; case SOCKET_STOP_PRE_SIGKILL: - log_warning("%s still around after SIGKILL. Ignoring.", u->meta.id); - socket_enter_stop_post(s, false); + log_warning("%s still around after SIGKILL. Ignoring.", u->id); + socket_enter_stop_post(s, SOCKET_FAILURE_TIMEOUT); break; case SOCKET_STOP_POST: - log_warning("%s stopping timed out (2). Terminating.", u->meta.id); - socket_enter_signal(s, SOCKET_FINAL_SIGTERM, false); + log_warning("%s stopping timed out (2). Terminating.", u->id); + socket_enter_signal(s, SOCKET_FINAL_SIGTERM, SOCKET_FAILURE_TIMEOUT); break; case SOCKET_FINAL_SIGTERM: if (s->exec_context.send_sigkill) { - log_warning("%s stopping timed out (2). Killing.", u->meta.id); - socket_enter_signal(s, SOCKET_FINAL_SIGKILL, false); + log_warning("%s stopping timed out (2). Killing.", u->id); + socket_enter_signal(s, SOCKET_FINAL_SIGKILL, SOCKET_FAILURE_TIMEOUT); } else { - log_warning("%s stopping timed out (2). Skipping SIGKILL. Ignoring.", u->meta.id); - socket_enter_dead(s, false); + log_warning("%s stopping timed out (2). Skipping SIGKILL. Ignoring.", u->id); + socket_enter_dead(s, SOCKET_FAILURE_TIMEOUT); } break; case SOCKET_FINAL_SIGKILL: - log_warning("%s still around after SIGKILL (2). Entering failed mode.", u->meta.id); - socket_enter_dead(s, false); + log_warning("%s still around after SIGKILL (2). Entering failed mode.", u->id); + socket_enter_dead(s, SOCKET_FAILURE_TIMEOUT); break; default: @@ -1958,6 +2016,12 @@ int socket_collect_fds(Socket *s, int **fds, unsigned *n_fds) { if (p->fd >= 0) rn_fds++; + if (rn_fds <= 0) { + *fds = NULL; + *n_fds = 0; + return 0; + } + if (!(rfds = new(int, rn_fds))) return -ENOMEM; @@ -1983,7 +2047,7 @@ void socket_notify_service_dead(Socket *s) { * services. */ if (s->state == SOCKET_RUNNING) { - log_debug("%s got notified about service death.", s->meta.id); + log_debug("%s got notified about service death.", UNIT(s)->id); socket_enter_listening(s); } } @@ -1999,7 +2063,7 @@ void socket_connection_unref(Socket *s) { assert(s->n_connections > 0); s->n_connections--; - log_debug("%s: One connection closed, %u left.", s->meta.id, s->n_connections); + log_debug("%s: One connection closed, %u left.", UNIT(s)->id, s->n_connections); } static void socket_reset_failed(Unit *u) { @@ -2010,7 +2074,7 @@ static void socket_reset_failed(Unit *u) { if (s->state == SOCKET_FAILED) socket_set_state(s, SOCKET_DEAD); - s->failure = false; + s->result = SOCKET_SUCCESS; } static int socket_kill(Unit *u, KillWho who, KillMode mode, int signo, DBusError *error) { @@ -2048,7 +2112,7 @@ static int socket_kill(Unit *u, KillWho who, KillMode mode, int signo, DBusError goto finish; } - if ((q = cgroup_bonding_kill_list(s->meta.cgroup_bondings, signo, false, pid_set)) < 0) + if ((q = cgroup_bonding_kill_list(UNIT(s)->cgroup_bondings, signo, false, pid_set)) < 0) if (q != -EAGAIN && q != -ESRCH && q != -ENOENT) r = q; } @@ -2086,8 +2150,24 @@ static const char* const socket_exec_command_table[_SOCKET_EXEC_COMMAND_MAX] = { DEFINE_STRING_TABLE_LOOKUP(socket_exec_command, SocketExecCommand); +static const char* const socket_result_table[_SOCKET_RESULT_MAX] = { + [SOCKET_SUCCESS] = "success", + [SOCKET_FAILURE_RESOURCES] = "resources", + [SOCKET_FAILURE_TIMEOUT] = "timeout", + [SOCKET_FAILURE_EXIT_CODE] = "exit-code", + [SOCKET_FAILURE_SIGNAL] = "signal", + [SOCKET_FAILURE_CORE_DUMP] = "core-dump" +}; + +DEFINE_STRING_TABLE_LOOKUP(socket_result, SocketResult); + const UnitVTable socket_vtable = { .suffix = ".socket", + .object_size = sizeof(Socket), + .sections = + "Unit\0" + "Socket\0" + "Install\0", .init = socket_init, .done = socket_done, diff --git a/src/socket.h b/src/socket.h index fd13ac4e4c..d242796a31 100644 --- a/src/socket.h +++ b/src/socket.h @@ -28,6 +28,7 @@ typedef struct Socket Socket; #include "unit.h" #include "socket-util.h" #include "mount.h" +#include "service.h" typedef enum SocketState { SOCKET_DEAD, @@ -64,6 +65,17 @@ typedef enum SocketType { _SOCKET_FIFO_INVALID = -1 } SocketType; +typedef enum SocketResult { + SOCKET_SUCCESS, + SOCKET_FAILURE_RESOURCES, + SOCKET_FAILURE_TIMEOUT, + SOCKET_FAILURE_EXIT_CODE, + SOCKET_FAILURE_SIGNAL, + SOCKET_FAILURE_CORE_DUMP, + _SOCKET_RESULT_MAX, + _SOCKET_RESULT_INVALID = -1 +} SocketResult; + typedef struct SocketPort { SocketType type; int fd; @@ -76,7 +88,7 @@ typedef struct SocketPort { } SocketPort; struct Socket { - Meta meta; + Unit meta; LIST_HEAD(SocketPort, ports); @@ -93,7 +105,7 @@ struct Socket { /* For Accept=no sockets refers to the one service we'll activate. For Accept=yes sockets is either NULL, or filled when the next service we spawn. */ - Service *service; + UnitRef service; SocketState state, deserialized_state; @@ -103,13 +115,10 @@ struct Socket { SocketExecCommand control_command_id; pid_t control_pid; - /* Only for INET6 sockets: issue IPV6_V6ONLY sockopt */ - SocketAddressBindIPv6Only bind_ipv6_only; - mode_t directory_mode; mode_t socket_mode; - bool failure; + SocketResult result; bool accept; @@ -118,6 +127,7 @@ struct Socket { bool free_bind; bool transparent; bool broadcast; + bool pass_cred; int priority; int mark; size_t receive_buffer; @@ -129,6 +139,9 @@ struct Socket { char *tcp_congestion; long mq_maxmsg; long mq_msgsize; + + /* Only for INET6 sockets: issue IPV6_V6ONLY sockopt */ + SocketAddressBindIPv6Only bind_ipv6_only; }; /* Called from the service code when collecting fds */ @@ -152,4 +165,7 @@ SocketState socket_state_from_string(const char *s); const char* socket_exec_command_to_string(SocketExecCommand i); SocketExecCommand socket_exec_command_from_string(const char *s); +const char* socket_result_to_string(SocketResult i); +SocketResult socket_result_from_string(const char *s); + #endif diff --git a/src/special.h b/src/special.h index 08dae11a25..8185eaf60e 100644 --- a/src/special.h +++ b/src/special.h @@ -45,7 +45,9 @@ #define SPECIAL_SYSINIT_TARGET "sysinit.target" #define SPECIAL_SOCKETS_TARGET "sockets.target" #define SPECIAL_LOCAL_FS_TARGET "local-fs.target" /* LSB's $local_fs */ +#define SPECIAL_LOCAL_FS_PRE_TARGET "local-fs-pre.target" #define SPECIAL_REMOTE_FS_TARGET "remote-fs.target" /* LSB's $remote_fs */ +#define SPECIAL_REMOTE_FS_PRE_TARGET "remote-fs-pre.target" #define SPECIAL_SWAP_TARGET "swap.target" #define SPECIAL_BASIC_TARGET "basic.target" @@ -68,8 +70,8 @@ /* Services systemd relies on */ #define SPECIAL_DBUS_SERVICE "dbus.service" #define SPECIAL_DBUS_SOCKET "dbus.socket" -#define SPECIAL_LOGGER_SOCKET "systemd-logger.socket" -#define SPECIAL_SYSLOG_SOCKET "syslog.socket" +#define SPECIAL_JOURNALD_SOCKET "systemd-journald.socket" +#define SPECIAL_JOURNALD_SERVICE "systemd-journald.service" /* Magic init signals */ #define SPECIAL_KBREQUEST_TARGET "kbrequest.target" diff --git a/src/specifier.c b/src/specifier.c index 497d3956bd..a9fff88d35 100644 --- a/src/specifier.c +++ b/src/specifier.c @@ -104,7 +104,5 @@ char *specifier_printf(const char *text, const Specifier table[], void *userdata /* Generic handler for simple string replacements */ char* specifier_string(char specifier, void *data, void *userdata) { - assert(data); - return strdup(strempty(data)); } diff --git a/src/strv.c b/src/strv.c index f15aa8736a..bb309d9f92 100644 --- a/src/strv.c +++ b/src/strv.c @@ -67,7 +67,8 @@ void strv_free(char **l) { char **strv_copy(char **l) { char **r, **k; - if (!(k = r = new(char*, strv_length(l)+1))) + k = r = new(char*, strv_length(l)+1); + if (!k) return NULL; if (l) @@ -105,7 +106,6 @@ char **strv_new_ap(const char *x, va_list ap) { unsigned n = 0, i = 0; va_list aq; - if (x) { n = 1; @@ -199,15 +199,23 @@ char **strv_merge_concat(char **a, char **b, const char *suffix) { if (!b) return strv_copy(a); - if (!(r = new(char*, strv_length(a)+strv_length(b)+1))) + r = new(char*, strv_length(a) + strv_length(b) + 1); + if (!r) return NULL; - for (k = r; *a; k++, a++) - if (!(*k = strdup(*a))) - goto fail; - for (; *b; k++, b++) - if (!(*k = strappend(*b, suffix))) + k = r; + if (a) + for (; *a; k++, a++) { + *k = strdup(*a); + if (!*k) + goto fail; + } + + for (; *b; k++, b++) { + *k = strappend(*b, suffix); + if (!*k) goto fail; + } *k = NULL; return r; @@ -318,7 +326,8 @@ char **strv_append(char **l, const char *s) { if (!s) return strv_copy(l); - if (!(r = new(char*, strv_length(l)+2))) + r = new(char*, strv_length(l)+2); + if (!r) return NULL; for (k = r; *l; k++, l++) @@ -483,8 +492,8 @@ static bool env_match(const char *t, const char *pattern) { } char **strv_env_delete(char **x, unsigned n_lists, ...) { - size_t n = 0, i = 0; - char **l, **k, **r, **j; + size_t n, i = 0; + char **k, **r; va_list ap; /* Deletes every entry from x that is mentioned in the other @@ -492,29 +501,34 @@ char **strv_env_delete(char **x, unsigned n_lists, ...) { n = strv_length(x); - if (!(r = new(char*, n+1))) + r = new(char*, n+1); + if (!r) return NULL; STRV_FOREACH(k, x) { + unsigned v; + va_start(ap, n_lists); + for (v = 0; v < n_lists; v++) { + char **l, **j; - for (i = 0; i < n_lists; i++) { l = va_arg(ap, char**); STRV_FOREACH(j, l) if (env_match(*k, *j)) - goto delete; + goto skip; } - va_end(ap); - if (!(r[i++] = strdup(*k))) { + r[i] = strdup(*k); + if (!r[i]) { strv_free(r); return NULL; } + i++; continue; - delete: + skip: va_end(ap); } @@ -661,3 +675,16 @@ char **strv_parse_nulstr(const char *s, size_t l) { return v; } + +bool strv_overlap(char **a, char **b) { + char **i, **j; + + STRV_FOREACH(i, a) { + STRV_FOREACH(j, b) { + if (streq(*i, *j)) + return true; + } + } + + return false; +} diff --git a/src/strv.h b/src/strv.h index 46436a52d9..d038c9f3b1 100644 --- a/src/strv.h +++ b/src/strv.h @@ -68,6 +68,8 @@ char **strv_env_clean(char **l); char **strv_parse_nulstr(const char *s, size_t l); +bool strv_overlap(char **a, char **b); + #define STRV_FOREACH(s, l) \ for ((s) = (l); (s) && *(s); (s)++) diff --git a/src/swap.c b/src/swap.c index 6e41f9b68e..9c72732b94 100644 --- a/src/swap.c +++ b/src/swap.c @@ -62,36 +62,37 @@ static void swap_unset_proc_swaps(Swap *s) { /* Remove this unit from the chain of swaps which share the * same kernel swap device. */ - first = hashmap_get(s->meta.manager->swaps_by_proc_swaps, s->parameters_proc_swaps.what); + first = hashmap_get(UNIT(s)->manager->swaps_by_proc_swaps, s->parameters_proc_swaps.what); LIST_REMOVE(Swap, same_proc_swaps, first, s); if (first) - hashmap_remove_and_replace(s->meta.manager->swaps_by_proc_swaps, s->parameters_proc_swaps.what, first->parameters_proc_swaps.what, first); + hashmap_remove_and_replace(UNIT(s)->manager->swaps_by_proc_swaps, s->parameters_proc_swaps.what, first->parameters_proc_swaps.what, first); else - hashmap_remove(s->meta.manager->swaps_by_proc_swaps, s->parameters_proc_swaps.what); + hashmap_remove(UNIT(s)->manager->swaps_by_proc_swaps, s->parameters_proc_swaps.what); free(s->parameters_proc_swaps.what); s->parameters_proc_swaps.what = NULL; } - static void swap_init(Unit *u) { +static void swap_init(Unit *u) { Swap *s = SWAP(u); assert(s); - assert(s->meta.load_state == UNIT_STUB); + assert(UNIT(s)->load_state == UNIT_STUB); s->timeout_usec = DEFAULT_TIMEOUT_USEC; exec_context_init(&s->exec_context); - s->exec_context.std_output = EXEC_OUTPUT_KMSG; + s->exec_context.std_output = u->manager->default_std_output; + s->exec_context.std_error = u->manager->default_std_error; s->parameters_etc_fstab.priority = s->parameters_proc_swaps.priority = s->parameters_fragment.priority = -1; s->timer_watch.type = WATCH_INVALID; - s->control_command_id = _MOUNT_EXEC_COMMAND_INVALID; + s->control_command_id = _SWAP_EXEC_COMMAND_INVALID; - s->meta.ignore_on_isolate = true; + UNIT(s)->ignore_on_isolate = true; } static void swap_unwatch_control_pid(Swap *s) { @@ -133,8 +134,8 @@ int swap_add_one_mount_link(Swap *s, Mount *m) { assert(s); assert(m); - if (s->meta.load_state != UNIT_LOADED || - m->meta.load_state != UNIT_LOADED) + if (UNIT(s)->load_state != UNIT_LOADED || + UNIT(m)->load_state != UNIT_LOADED) return 0; if (is_device_path(s->what)) @@ -150,13 +151,13 @@ int swap_add_one_mount_link(Swap *s, Mount *m) { } static int swap_add_mount_links(Swap *s) { - Meta *other; + Unit *other; int r; assert(s); - LIST_FOREACH(units_by_type, other, s->meta.manager->units_by_type[UNIT_MOUNT]) - if ((r = swap_add_one_mount_link(s, (Mount*) other)) < 0) + LIST_FOREACH(units_by_type, other, UNIT(s)->manager->units_by_type[UNIT_MOUNT]) + if ((r = swap_add_one_mount_link(s, MOUNT(other))) < 0) return r; return 0; @@ -176,14 +177,14 @@ static int swap_add_target_links(Swap *s) { else return 0; - if ((r = manager_load_unit(s->meta.manager, SPECIAL_SWAP_TARGET, NULL, NULL, &tu)) < 0) + if ((r = manager_load_unit(UNIT(s)->manager, SPECIAL_SWAP_TARGET, NULL, NULL, &tu)) < 0) return r; if (!p->noauto && !p->nofail && - (p->handle || s->meta.manager->swap_auto) && + (p->handle || UNIT(s)->manager->swap_auto) && s->from_etc_fstab && - s->meta.manager->running_as == MANAGER_SYSTEM) + UNIT(s)->manager->running_as == MANAGER_SYSTEM) if ((r = unit_add_dependency(tu, UNIT_WANTS, UNIT(s), true)) < 0) return r; @@ -208,7 +209,7 @@ static int swap_add_device_links(Swap *s) { if (is_device_path(s->what)) return unit_add_node_link(UNIT(s), s->what, !p->noauto && p->nofail && - s->meta.manager->running_as == MANAGER_SYSTEM); + UNIT(s)->manager->running_as == MANAGER_SYSTEM); else /* File based swap devices need to be ordered after * remount-rootfs.service, since they might need a @@ -221,7 +222,7 @@ static int swap_add_default_dependencies(Swap *s) { assert(s); - if (s->meta.manager->running_as == MANAGER_SYSTEM) { + if (UNIT(s)->manager->running_as == MANAGER_SYSTEM) { if ((r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_UMOUNT_TARGET, NULL, true)) < 0) return r; @@ -234,7 +235,7 @@ static int swap_verify(Swap *s) { bool b; char *e; - if (s->meta.load_state != UNIT_LOADED) + if (UNIT(s)->load_state != UNIT_LOADED) return 0; if (!(e = unit_name_from_path(s->what, ".swap"))) @@ -244,12 +245,12 @@ static int swap_verify(Swap *s) { free(e); if (!b) { - log_error("%s: Value of \"What\" and unit name do not match, not loading.\n", s->meta.id); + log_error("%s: Value of \"What\" and unit name do not match, not loading.\n", UNIT(s)->id); return -EINVAL; } if (s->exec_context.pam_name && s->exec_context.kill_mode != KILL_CONTROL_GROUP) { - log_error("%s has PAM enabled. Kill mode must be set to 'control-group'. Refusing.", s->meta.id); + log_error("%s has PAM enabled. Kill mode must be set to 'control-group'. Refusing.", UNIT(s)->id); return -EINVAL; } @@ -261,17 +262,17 @@ static int swap_load(Unit *u) { Swap *s = SWAP(u); assert(s); - assert(u->meta.load_state == UNIT_STUB); + assert(u->load_state == UNIT_STUB); /* Load a .swap file */ if ((r = unit_load_fragment_and_dropin_optional(u)) < 0) return r; - if (u->meta.load_state == UNIT_LOADED) { + if (u->load_state == UNIT_LOADED) { if ((r = unit_add_exec_dependencies(u, &s->exec_context)) < 0) return r; - if (s->meta.fragment_path) + if (UNIT(s)->fragment_path) s->from_fragment = true; if (!s->what) { @@ -282,7 +283,7 @@ static int swap_load(Unit *u) { else if (s->parameters_proc_swaps.what) s->what = strdup(s->parameters_proc_swaps.what); else - s->what = unit_name_to_path(u->meta.id); + s->what = unit_name_to_path(u->id); if (!s->what) return -ENOMEM; @@ -290,7 +291,7 @@ static int swap_load(Unit *u) { path_kill_slashes(s->what); - if (!s->meta.description) + if (!UNIT(s)->description) if ((r = unit_set_description(u, s->what)) < 0) return r; @@ -306,7 +307,7 @@ static int swap_load(Unit *u) { if ((r = unit_add_default_cgroups(u)) < 0) return r; - if (s->meta.default_dependencies) + if (UNIT(s)->default_dependencies) if ((r = swap_add_default_dependencies(s)) < 0) return r; } @@ -333,7 +334,8 @@ int swap_add_one( assert(m); assert(what); - if (!(e = unit_name_from_path(what, ".swap"))) + e = unit_name_from_path(what, ".swap"); + if (!e) return -ENOMEM; u = manager_get_unit(m, e); @@ -347,15 +349,18 @@ int swap_add_one( if (!u) { delete = true; - if (!(u = unit_new(m))) { + u = unit_new(m, sizeof(Swap)); + if (!u) { free(e); return -ENOMEM; } - if ((r = unit_add_name(u, e)) < 0) + r = unit_add_name(u, e); + if (r < 0) goto fail; - if (!(SWAP(u)->what = strdup(what))) { + SWAP(u)->what = strdup(what); + if (!SWAP(u)->what) { r = -ENOMEM; goto fail; } @@ -505,7 +510,7 @@ static void swap_set_state(Swap *s, SwapState state) { if (state != old_state) log_debug("%s changed %s -> %s", - s->meta.id, + UNIT(s)->id, swap_state_to_string(old_state), swap_state_to_string(state)); @@ -566,6 +571,7 @@ static void swap_dump(Unit *u, FILE *f, const char *prefix) { fprintf(f, "%sSwap State: %s\n" + "%sResult: %s\n" "%sWhat: %s\n" "%sPriority: %i\n" "%sNoAuto: %s\n" @@ -575,6 +581,7 @@ static void swap_dump(Unit *u, FILE *f, const char *prefix) { "%sFrom /proc/swaps: %s\n" "%sFrom fragment: %s\n", prefix, swap_state_to_string(s->state), + prefix, swap_result_to_string(s->result), prefix, s->what, prefix, p->priority, prefix, yes_no(p->noauto), @@ -607,12 +614,13 @@ static int swap_spawn(Swap *s, ExecCommand *c, pid_t *_pid) { NULL, &s->exec_context, NULL, 0, - s->meta.manager->environment, + UNIT(s)->manager->environment, true, true, true, - s->meta.manager->confirm_spawn, - s->meta.cgroup_bondings, + UNIT(s)->manager->confirm_spawn, + UNIT(s)->cgroup_bondings, + UNIT(s)->cgroup_attributes, &pid)) < 0) goto fail; @@ -630,33 +638,33 @@ fail: return r; } -static void swap_enter_dead(Swap *s, bool success) { +static void swap_enter_dead(Swap *s, SwapResult f) { assert(s); - if (!success) - s->failure = true; + if (f != SWAP_SUCCESS) + s->result = f; - swap_set_state(s, s->failure ? SWAP_FAILED : SWAP_DEAD); + swap_set_state(s, s->result != SWAP_SUCCESS ? SWAP_FAILED : SWAP_DEAD); } -static void swap_enter_active(Swap *s, bool success) { +static void swap_enter_active(Swap *s, SwapResult f) { assert(s); - if (!success) - s->failure = true; + if (f != SWAP_SUCCESS) + s->result = f; swap_set_state(s, SWAP_ACTIVE); } -static void swap_enter_signal(Swap *s, SwapState state, bool success) { +static void swap_enter_signal(Swap *s, SwapState state, SwapResult f) { int r; Set *pid_set = NULL; bool wait_for_exit = false; assert(s); - if (!success) - s->failure = true; + if (f != SWAP_SUCCESS) + s->result = f; if (s->exec_context.kill_mode != KILL_NONE) { int sig = (state == SWAP_ACTIVATING_SIGTERM || @@ -682,7 +690,7 @@ static void swap_enter_signal(Swap *s, SwapState state, bool success) { if ((r = set_put(pid_set, LONG_TO_PTR(s->control_pid))) < 0) goto fail; - if ((r = cgroup_bonding_kill_list(s->meta.cgroup_bondings, sig, true, pid_set)) < 0) { + if ((r = cgroup_bonding_kill_list(UNIT(s)->cgroup_bondings, sig, true, pid_set)) < 0) { if (r != -EAGAIN && r != -ESRCH && r != -ENOENT) log_warning("Failed to kill control group: %s", strerror(-r)); } else if (r > 0) @@ -699,14 +707,14 @@ static void swap_enter_signal(Swap *s, SwapState state, bool success) { swap_set_state(s, state); } else - swap_enter_dead(s, true); + swap_enter_dead(s, SWAP_SUCCESS); return; fail: - log_warning("%s failed to kill processes: %s", s->meta.id, strerror(-r)); + log_warning("%s failed to kill processes: %s", UNIT(s)->id, strerror(-r)); - swap_enter_dead(s, false); + swap_enter_dead(s, SWAP_FAILURE_RESOURCES); if (pid_set) set_free(pid_set); @@ -760,18 +768,15 @@ static void swap_enter_activating(Swap *s) { return; fail: - log_warning("%s failed to run 'swapon' task: %s", s->meta.id, strerror(-r)); - swap_enter_dead(s, false); + log_warning("%s failed to run 'swapon' task: %s", UNIT(s)->id, strerror(-r)); + swap_enter_dead(s, SWAP_FAILURE_RESOURCES); } -static void swap_enter_deactivating(Swap *s, bool success) { +static void swap_enter_deactivating(Swap *s) { int r; assert(s); - if (!success) - s->failure = true; - s->control_command_id = SWAP_EXEC_DEACTIVATE; s->control_command = s->exec_command + SWAP_EXEC_DEACTIVATE; @@ -792,8 +797,8 @@ static void swap_enter_deactivating(Swap *s, bool success) { return; fail: - log_warning("%s failed to run 'swapoff' task: %s", s->meta.id, strerror(-r)); - swap_enter_active(s, false); + log_warning("%s failed to run 'swapoff' task: %s", UNIT(s)->id, strerror(-r)); + swap_enter_active(s, SWAP_FAILURE_RESOURCES); } static int swap_start(Unit *u) { @@ -816,7 +821,7 @@ static int swap_start(Unit *u) { assert(s->state == SWAP_DEAD || s->state == SWAP_FAILED); - s->failure = false; + s->result = SWAP_SUCCESS; swap_enter_activating(s); return 0; } @@ -836,7 +841,7 @@ static int swap_stop(Unit *u) { assert(s->state == SWAP_ACTIVATING || s->state == SWAP_ACTIVE); - swap_enter_deactivating(s, true); + swap_enter_deactivating(s); return 0; } @@ -848,7 +853,7 @@ static int swap_serialize(Unit *u, FILE *f, FDSet *fds) { assert(fds); unit_serialize_item(u, f, "state", swap_state_to_string(s->state)); - unit_serialize_item(u, f, "failure", yes_no(s->failure)); + unit_serialize_item(u, f, "result", swap_result_to_string(s->result)); if (s->control_pid > 0) unit_serialize_item_format(u, f, "control-pid", "%lu", (unsigned long) s->control_pid); @@ -872,14 +877,14 @@ static int swap_deserialize_item(Unit *u, const char *key, const char *value, FD log_debug("Failed to parse state value %s", value); else s->deserialized_state = state; - } else if (streq(key, "failure")) { - int b; - - if ((b = parse_boolean(value)) < 0) - log_debug("Failed to parse failure value %s", value); - else - s->failure = b || s->failure; - + } else if (streq(key, "result")) { + SwapResult f; + + f = swap_result_from_string(value); + if (f < 0) + log_debug("Failed to parse result value %s", value); + else if (f != SWAP_SUCCESS) + s->result = f; } else if (streq(key, "control-pid")) { pid_t pid; @@ -926,7 +931,7 @@ static bool swap_check_gc(Unit *u) { static void swap_sigchld_event(Unit *u, pid_t pid, int code, int status) { Swap *s = SWAP(u); - bool success; + SwapResult f; assert(s); assert(pid >= 0); @@ -936,17 +941,29 @@ static void swap_sigchld_event(Unit *u, pid_t pid, int code, int status) { s->control_pid = 0; - success = is_clean_exit(code, status); - s->failure = s->failure || !success; + if (is_clean_exit(code, status)) + f = SWAP_SUCCESS; + else if (code == CLD_EXITED) + f = SWAP_FAILURE_EXIT_CODE; + else if (code == CLD_KILLED) + f = SWAP_FAILURE_SIGNAL; + else if (code == CLD_DUMPED) + f = SWAP_FAILURE_CORE_DUMP; + else + assert_not_reached("Unknown code"); + + if (f != SWAP_SUCCESS) + s->result = f; if (s->control_command) { exec_status_exit(&s->control_command->exec_status, &s->exec_context, pid, code, status); + s->control_command = NULL; s->control_command_id = _SWAP_EXEC_COMMAND_INVALID; } - log_full(success ? LOG_DEBUG : LOG_NOTICE, - "%s swap process exited, code=%s status=%i", u->meta.id, sigchld_code_to_string(code), status); + log_full(f == SWAP_SUCCESS ? LOG_DEBUG : LOG_NOTICE, + "%s swap process exited, code=%s status=%i", u->id, sigchld_code_to_string(code), status); switch (s->state) { @@ -954,20 +971,20 @@ static void swap_sigchld_event(Unit *u, pid_t pid, int code, int status) { case SWAP_ACTIVATING_SIGTERM: case SWAP_ACTIVATING_SIGKILL: - if (success) - swap_enter_active(s, true); + if (f == SWAP_SUCCESS) + swap_enter_active(s, f); else - swap_enter_dead(s, false); + swap_enter_dead(s, f); break; case SWAP_DEACTIVATING: case SWAP_DEACTIVATING_SIGKILL: case SWAP_DEACTIVATING_SIGTERM: - if (success) - swap_enter_dead(s, true); + if (f == SWAP_SUCCESS) + swap_enter_dead(s, f); else - swap_enter_dead(s, false); + swap_enter_dead(s, f); break; default: @@ -979,7 +996,7 @@ static void swap_sigchld_event(Unit *u, pid_t pid, int code, int status) { /* Request a reload of /proc/swaps, so that following units * can follow our state change */ - u->meta.manager->request_reload = true; + u->manager->request_reload = true; } static void swap_timer_event(Unit *u, uint64_t elapsed, Watch *w) { @@ -992,39 +1009,39 @@ static void swap_timer_event(Unit *u, uint64_t elapsed, Watch *w) { switch (s->state) { case SWAP_ACTIVATING: - log_warning("%s activation timed out. Stopping.", u->meta.id); - swap_enter_signal(s, SWAP_ACTIVATING_SIGTERM, false); + log_warning("%s activation timed out. Stopping.", u->id); + swap_enter_signal(s, SWAP_ACTIVATING_SIGTERM, SWAP_FAILURE_TIMEOUT); break; case SWAP_DEACTIVATING: - log_warning("%s deactivation timed out. Stopping.", u->meta.id); - swap_enter_signal(s, SWAP_DEACTIVATING_SIGTERM, false); + log_warning("%s deactivation timed out. Stopping.", u->id); + swap_enter_signal(s, SWAP_DEACTIVATING_SIGTERM, SWAP_FAILURE_TIMEOUT); break; case SWAP_ACTIVATING_SIGTERM: if (s->exec_context.send_sigkill) { - log_warning("%s activation timed out. Killing.", u->meta.id); - swap_enter_signal(s, SWAP_ACTIVATING_SIGKILL, false); + log_warning("%s activation timed out. Killing.", u->id); + swap_enter_signal(s, SWAP_ACTIVATING_SIGKILL, SWAP_FAILURE_TIMEOUT); } else { - log_warning("%s activation timed out. Skipping SIGKILL. Ignoring.", u->meta.id); - swap_enter_dead(s, false); + log_warning("%s activation timed out. Skipping SIGKILL. Ignoring.", u->id); + swap_enter_dead(s, SWAP_FAILURE_TIMEOUT); } break; case SWAP_DEACTIVATING_SIGTERM: if (s->exec_context.send_sigkill) { - log_warning("%s deactivation timed out. Killing.", u->meta.id); - swap_enter_signal(s, SWAP_DEACTIVATING_SIGKILL, false); + log_warning("%s deactivation timed out. Killing.", u->id); + swap_enter_signal(s, SWAP_DEACTIVATING_SIGKILL, SWAP_FAILURE_TIMEOUT); } else { - log_warning("%s deactivation timed out. Skipping SIGKILL. Ignoring.", u->meta.id); - swap_enter_dead(s, false); + log_warning("%s deactivation timed out. Skipping SIGKILL. Ignoring.", u->id); + swap_enter_dead(s, SWAP_FAILURE_TIMEOUT); } break; case SWAP_ACTIVATING_SIGKILL: case SWAP_DEACTIVATING_SIGKILL: - log_warning("%s swap process still around after SIGKILL. Ignoring.", u->meta.id); - swap_enter_dead(s, false); + log_warning("%s swap process still around after SIGKILL. Ignoring.", u->id); + swap_enter_dead(s, SWAP_FAILURE_TIMEOUT); break; default: @@ -1090,7 +1107,7 @@ int swap_dispatch_reload(Manager *m) { } int swap_fd_event(Manager *m, int events) { - Meta *meta; + Unit *u; int r; assert(m); @@ -1100,8 +1117,8 @@ int swap_fd_event(Manager *m, int events) { log_error("Failed to reread /proc/swaps: %s", strerror(-r)); /* Reset flags, just in case, for late calls */ - LIST_FOREACH(units_by_type, meta, m->units_by_type[UNIT_SWAP]) { - Swap *swap = (Swap*) meta; + LIST_FOREACH(units_by_type, u, m->units_by_type[UNIT_SWAP]) { + Swap *swap = SWAP(u); swap->is_active = swap->just_activated = false; } @@ -1111,8 +1128,8 @@ int swap_fd_event(Manager *m, int events) { manager_dispatch_load_queue(m); - LIST_FOREACH(units_by_type, meta, m->units_by_type[UNIT_SWAP]) { - Swap *swap = (Swap*) meta; + LIST_FOREACH(units_by_type, u, m->units_by_type[UNIT_SWAP]) { + Swap *swap = SWAP(u); if (!swap->is_active) { /* This has just been deactivated */ @@ -1123,7 +1140,7 @@ int swap_fd_event(Manager *m, int events) { switch (swap->state) { case SWAP_ACTIVE: - swap_enter_dead(swap, true); + swap_enter_dead(swap, SWAP_SUCCESS); break; default: @@ -1139,7 +1156,7 @@ int swap_fd_event(Manager *m, int events) { case SWAP_DEAD: case SWAP_FAILED: - swap_enter_active(swap, true); + swap_enter_active(swap, SWAP_SUCCESS); break; default: @@ -1266,7 +1283,7 @@ static void swap_reset_failed(Unit *u) { if (s->state == SWAP_FAILED) swap_set_state(s, SWAP_DEAD); - s->failure = false; + s->result = SWAP_SUCCESS; } static int swap_kill(Unit *u, KillWho who, KillMode mode, int signo, DBusError *error) { @@ -1304,7 +1321,7 @@ static int swap_kill(Unit *u, KillWho who, KillMode mode, int signo, DBusError * goto finish; } - if ((q = cgroup_bonding_kill_list(s->meta.cgroup_bondings, signo, false, pid_set)) < 0) + if ((q = cgroup_bonding_kill_list(UNIT(s)->cgroup_bondings, signo, false, pid_set)) < 0) if (q != -EAGAIN && q != -ESRCH && q != -ENOENT) r = q; } @@ -1337,8 +1354,24 @@ static const char* const swap_exec_command_table[_SWAP_EXEC_COMMAND_MAX] = { DEFINE_STRING_TABLE_LOOKUP(swap_exec_command, SwapExecCommand); +static const char* const swap_result_table[_SWAP_RESULT_MAX] = { + [SWAP_SUCCESS] = "success", + [SWAP_FAILURE_RESOURCES] = "resources", + [SWAP_FAILURE_TIMEOUT] = "timeout", + [SWAP_FAILURE_EXIT_CODE] = "exit-code", + [SWAP_FAILURE_SIGNAL] = "signal", + [SWAP_FAILURE_CORE_DUMP] = "core-dump" +}; + +DEFINE_STRING_TABLE_LOOKUP(swap_result, SwapResult); + const UnitVTable swap_vtable = { .suffix = ".swap", + .object_size = sizeof(Swap), + .sections = + "Unit\0" + "Swap\0" + "Install\0", .no_alias = true, .no_instances = true, diff --git a/src/swap.h b/src/swap.h index 0d5c9a23f0..62d08da30b 100644 --- a/src/swap.h +++ b/src/swap.h @@ -56,8 +56,19 @@ typedef struct SwapParameters { bool handle:1; } SwapParameters; +typedef enum SwapResult { + SWAP_SUCCESS, + SWAP_FAILURE_RESOURCES, + SWAP_FAILURE_TIMEOUT, + SWAP_FAILURE_EXIT_CODE, + SWAP_FAILURE_SIGNAL, + SWAP_FAILURE_CORE_DUMP, + _SWAP_RESULT_MAX, + _SWAP_RESULT_INVALID = -1 +} SwapResult; + struct Swap { - Meta meta; + Unit meta; char *what; @@ -69,13 +80,13 @@ struct Swap { bool from_proc_swaps:1; bool from_fragment:1; - bool failure:1; - /* Used while looking for swaps that vanished or got added * from/to /proc/swaps */ bool is_active:1; bool just_activated:1; + SwapResult result; + usec_t timeout_usec; ExecCommand exec_command[_SWAP_EXEC_COMMAND_MAX]; @@ -111,4 +122,7 @@ SwapState swap_state_from_string(const char *s); const char* swap_exec_command_to_string(SwapExecCommand i); SwapExecCommand swap_exec_command_from_string(const char *s); +const char* swap_result_to_string(SwapResult i); +SwapResult swap_result_from_string(const char *s); + #endif diff --git a/src/sysctl.c b/src/sysctl.c index 9f7acfce8b..8bdfb0811c 100644 --- a/src/sysctl.c +++ b/src/sysctl.c @@ -228,6 +228,8 @@ int main(int argc, char *argv[]) { log_parse_environment(); log_open(); + umask(0022); + if (argc > optind) r = apply_file(argv[optind], false); else { diff --git a/src/system.conf b/src/system.conf index 4e06319b35..33d09bccea 100644 --- a/src/system.conf +++ b/src/system.conf @@ -9,7 +9,7 @@ [Manager] #LogLevel=info -#LogTarget=syslog-or-kmsg +#LogTarget=journal-or-kmsg #LogColor=yes #LogLocation=no #DumpCore=yes @@ -21,5 +21,6 @@ #MountAuto=yes #SwapAuto=yes #DefaultControllers=cpu -#DefaultStandardOutput=inherit +#DefaultStandardOutput=journal #DefaultStandardError=inherit +#JoinControllers=cpu,cpuacct diff --git a/src/systemadm.vala b/src/systemadm.vala index d45ec64ca4..5971ac07e5 100644 --- a/src/systemadm.vala +++ b/src/systemadm.vala @@ -23,6 +23,20 @@ using Pango; static bool user = false; +public string format_time(uint64 time_ns) { + if (time_ns <= 0) + return ""; + Time timestamp = Time.local((time_t) (time_ns / 1000000)); + return timestamp.format("%a, %d %b %Y %H:%M:%S"); +} + +public void new_column(TreeView view, int column_id, string title) { + TreeViewColumn col; + col = new TreeViewColumn.with_attributes(title, new CellRendererText(), "text", column_id); + col.set_sort_column_id(column_id); + view.insert_column(col, -1); +} + public class LeftLabel : Label { public LeftLabel(string? text = null) { if (text != null) @@ -32,12 +46,11 @@ public class LeftLabel : Label { } } -public class RightLabel : Label { +public class RightLabel : WrapLabel { + public RightLabel(string? text = null) { - set_text_or_na(text); - set_alignment(0, 0); - set_ellipsize(EllipsizeMode.START); set_selectable(true); + set_text_or_na(text); } public void set_text_or_na(string? text = null) { @@ -66,6 +79,8 @@ public class MainWindow : Window { private ListStore unit_model; private ListStore job_model; + private Gee.HashMap<string, Unit> unit_map; + private Button start_button; private Button stop_button; private Button restart_button; @@ -81,7 +96,6 @@ public class MainWindow : Window { private Manager manager; private RightLabel unit_id_label; - private RightLabel unit_aliases_label; private RightLabel unit_dependency_label; private RightLabel unit_description_label; private RightLabel unit_load_state_label; @@ -99,6 +113,7 @@ public class MainWindow : Window { private RightLabel job_type_label; private ComboBox unit_type_combo_box; + private CheckButton inactive_checkbox; public MainWindow() throws IOError { title = user ? "systemd User Service Manager" : "systemd System Manager"; @@ -123,18 +138,24 @@ public class MainWindow : Window { type_hbox.pack_start(unit_type_combo_box, false, false, 0); unit_vbox.pack_start(type_hbox, false, false, 0); - unit_type_combo_box.append_text("Show All Units"); - unit_type_combo_box.append_text("Show Only Live Units"); + unit_type_combo_box.append_text("All unit types"); + unit_type_combo_box.append_text("Targets"); unit_type_combo_box.append_text("Services"); - unit_type_combo_box.append_text("Sockets"); unit_type_combo_box.append_text("Devices"); unit_type_combo_box.append_text("Mounts"); unit_type_combo_box.append_text("Automounts"); - unit_type_combo_box.append_text("Targets"); + unit_type_combo_box.append_text("Swaps"); + unit_type_combo_box.append_text("Sockets"); + unit_type_combo_box.append_text("Paths"); + unit_type_combo_box.append_text("Timers"); unit_type_combo_box.append_text("Snapshots"); - unit_type_combo_box.set_active(1); + unit_type_combo_box.set_active(0); // Show All unit_type_combo_box.changed.connect(unit_type_changed); + inactive_checkbox = new CheckButton.with_label("inactive too"); + inactive_checkbox.toggled.connect(unit_type_changed); + type_hbox.pack_start(inactive_checkbox, false, false, 0); + unit_load_entry = new Entry(); unit_load_button = new Button.with_mnemonic("_Load"); unit_load_button.set_sensitive(false); @@ -160,26 +181,30 @@ public class MainWindow : Window { unit_model = new ListStore(7, typeof(string), typeof(string), typeof(string), typeof(string), typeof(string), typeof(string), typeof(Unit)); job_model = new ListStore(6, typeof(string), typeof(string), typeof(string), typeof(string), typeof(Job), typeof(uint32)); + unit_map = new Gee.HashMap<string, Unit>(); + TreeModelFilter unit_model_filter; unit_model_filter = new TreeModelFilter(unit_model, null); unit_model_filter.set_visible_func(unit_filter); - unit_view = new TreeView.with_model(unit_model_filter); + TreeModelSort unit_model_sort = new TreeModelSort.with_model(unit_model_filter); + + unit_view = new TreeView.with_model(unit_model_sort); job_view = new TreeView.with_model(job_model); unit_view.cursor_changed.connect(unit_changed); job_view.cursor_changed.connect(job_changed); - unit_view.insert_column_with_attributes(-1, "Load State", new CellRendererText(), "text", 2); - unit_view.insert_column_with_attributes(-1, "Active State", new CellRendererText(), "text", 3); - unit_view.insert_column_with_attributes(-1, "Unit State", new CellRendererText(), "text", 4); - unit_view.insert_column_with_attributes(-1, "Unit", new CellRendererText(), "text", 0); - unit_view.insert_column_with_attributes(-1, "Job", new CellRendererText(), "text", 5); + new_column(unit_view, 2, "Load State"); + new_column(unit_view, 3, "Active State"); + new_column(unit_view, 4, "Unit State"); + new_column(unit_view, 0, "Unit"); + new_column(unit_view, 5, "Job"); - job_view.insert_column_with_attributes(-1, "Job", new CellRendererText(), "text", 0); - job_view.insert_column_with_attributes(-1, "Unit", new CellRendererText(), "text", 1); - job_view.insert_column_with_attributes(-1, "Type", new CellRendererText(), "text", 2); - job_view.insert_column_with_attributes(-1, "State", new CellRendererText(), "text", 3); + new_column(job_view, 0, "Job"); + new_column(job_view, 1, "Unit"); + new_column(job_view, 2, "Type"); + new_column(job_view, 3, "State"); ScrolledWindow scroll = new ScrolledWindow(null, null); scroll.set_policy(PolicyType.AUTOMATIC, PolicyType.AUTOMATIC); @@ -194,7 +219,6 @@ public class MainWindow : Window { job_vbox.pack_start(scroll, true, true, 0); unit_id_label = new RightLabel(); - unit_aliases_label = new RightLabel(); unit_dependency_label = new RightLabel(); unit_description_label = new RightLabel(); unit_load_state_label = new RightLabel(); @@ -212,7 +236,7 @@ public class MainWindow : Window { job_type_label = new RightLabel(); unit_dependency_label.set_track_visited_links(false); - unit_dependency_label.set_selectable(false); + unit_dependency_label.set_selectable(true); unit_dependency_label.activate_link.connect(on_activate_link); unit_fragment_path_label.set_track_visited_links(false); @@ -229,33 +253,31 @@ public class MainWindow : Window { unit_table.attach(new LeftLabel("Id:"), 0, 1, 0, 1, AttachOptions.FILL, AttachOptions.FILL, 0, 0); unit_table.attach(unit_id_label, 1, 6, 0, 1, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0); - unit_table.attach(new LeftLabel("Aliases:"), 0, 1, 1, 2, AttachOptions.FILL, AttachOptions.FILL, 0, 0); - unit_table.attach(unit_aliases_label, 1, 6, 1, 2, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0); - unit_table.attach(new LeftLabel("Description:"), 0, 1, 2, 3, AttachOptions.FILL, AttachOptions.FILL, 0, 0); - unit_table.attach(unit_description_label, 1, 6, 2, 3, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0); - unit_table.attach(new LeftLabel("Dependencies:"), 0, 1, 3, 4, AttachOptions.FILL, AttachOptions.FILL, 0, 0); - unit_table.attach(unit_dependency_label, 1, 6, 3, 4, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0); - unit_table.attach(new LeftLabel("Fragment Path:"), 0, 1, 4, 5, AttachOptions.FILL, AttachOptions.FILL, 0, 0); - unit_table.attach(unit_fragment_path_label, 1, 6, 4, 5, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0); - unit_table.attach(new LeftLabel("Control Group:"), 0, 1, 5, 6, AttachOptions.FILL, AttachOptions.FILL, 0, 0); - unit_table.attach(unit_cgroup_label, 1, 6, 5, 6, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0); - - unit_table.attach(new LeftLabel("Load State:"), 0, 1, 6, 7, AttachOptions.FILL, AttachOptions.FILL, 0, 0); - unit_table.attach(unit_load_state_label, 1, 2, 6, 7, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0); - unit_table.attach(new LeftLabel("Active State:"), 0, 1, 7, 8, AttachOptions.FILL, AttachOptions.FILL, 0, 0); - unit_table.attach(unit_active_state_label, 1, 2, 7, 8, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0); - unit_table.attach(new LeftLabel("Unit State:"), 0, 1, 8, 9, AttachOptions.FILL, AttachOptions.FILL, 0, 0); - unit_table.attach(unit_sub_state_label, 1, 2, 8, 9, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0); - - unit_table.attach(new LeftLabel("Active Enter Timestamp:"), 2, 3, 7, 8, AttachOptions.FILL, AttachOptions.FILL, 0, 0); - unit_table.attach(unit_active_enter_timestamp_label, 3, 4, 7, 8, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0); - unit_table.attach(new LeftLabel("Active Exit Timestamp:"), 2, 3, 8, 9, AttachOptions.FILL, AttachOptions.FILL, 0, 0); - unit_table.attach(unit_active_exit_timestamp_label, 3, 4, 8, 9, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0); - - unit_table.attach(new LeftLabel("Can Start/Stop:"), 4, 5, 7, 8, AttachOptions.FILL, AttachOptions.FILL, 0, 0); - unit_table.attach(unit_can_start_label, 5, 6, 7, 8, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0); - unit_table.attach(new LeftLabel("Can Reload:"), 4, 5, 8, 9, AttachOptions.FILL, AttachOptions.FILL, 0, 0); - unit_table.attach(unit_can_reload_label, 5, 6, 8, 9, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0); + unit_table.attach(new LeftLabel("Description:"), 0, 1, 1, 2, AttachOptions.FILL, AttachOptions.FILL, 0, 0); + unit_table.attach(unit_description_label, 1, 6, 1, 2, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0); + unit_table.attach(new LeftLabel("Dependencies:"), 0, 1, 2, 3, AttachOptions.FILL, AttachOptions.FILL, 0, 0); + unit_table.attach(unit_dependency_label, 1, 6, 2, 3, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0); + unit_table.attach(new LeftLabel("Fragment Path:"), 0, 1, 3, 4, AttachOptions.FILL, AttachOptions.FILL, 0, 0); + unit_table.attach(unit_fragment_path_label, 1, 6, 3, 4, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0); + unit_table.attach(new LeftLabel("Control Group:"), 0, 1, 4, 5, AttachOptions.FILL, AttachOptions.FILL, 0, 0); + unit_table.attach(unit_cgroup_label, 1, 6, 4, 5, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0); + + unit_table.attach(new LeftLabel("Load State:"), 0, 1, 5, 6, AttachOptions.FILL, AttachOptions.FILL, 0, 0); + unit_table.attach(unit_load_state_label, 1, 2, 5, 6, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0); + unit_table.attach(new LeftLabel("Active State:"), 0, 1, 6, 7, AttachOptions.FILL, AttachOptions.FILL, 0, 0); + unit_table.attach(unit_active_state_label, 1, 2, 6, 7, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0); + unit_table.attach(new LeftLabel("Unit State:"), 0, 1, 7, 8, AttachOptions.FILL, AttachOptions.FILL, 0, 0); + unit_table.attach(unit_sub_state_label, 1, 2, 7, 8, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0); + + unit_table.attach(new LeftLabel("Activated:"), 2, 3, 6, 7, AttachOptions.FILL, AttachOptions.FILL, 0, 0); + unit_table.attach(unit_active_enter_timestamp_label, 3, 4, 6, 7, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0); + unit_table.attach(new LeftLabel("Deactivated:"), 2, 3, 7, 8, AttachOptions.FILL, AttachOptions.FILL, 0, 0); + unit_table.attach(unit_active_exit_timestamp_label, 3, 4, 7, 8, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0); + + unit_table.attach(new LeftLabel("Can Start/Stop:"), 4, 5, 6, 7, AttachOptions.FILL, AttachOptions.FILL, 0, 0); + unit_table.attach(unit_can_start_label, 5, 6, 6, 7, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0); + unit_table.attach(new LeftLabel("Can Reload:"), 4, 5, 7, 8, AttachOptions.FILL, AttachOptions.FILL, 0, 0); + unit_table.attach(unit_can_reload_label, 5, 6, 7, 8, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0); job_table.attach(new LeftLabel("Id:"), 0, 1, 0, 1, AttachOptions.FILL, AttachOptions.FILL, 0, 0); job_table.attach(job_id_label, 1, 2, 0, 1, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0); @@ -333,6 +355,8 @@ public class MainWindow : Window { "org.freedesktop.systemd1", i.unit_path); + unit_map[i.id] = u; + unit_model.append(out iter); unit_model.set(iter, 0, i.id, @@ -393,6 +417,10 @@ public class MainWindow : Window { return u; } + public Unit? get_unit(string id) { + return this.unit_map[id]; + } + public void unit_changed() { Unit u = get_current_unit(); @@ -411,7 +439,6 @@ public class MainWindow : Window { restart_button.set_sensitive(false); unit_id_label.set_text_or_na(); - unit_aliases_label.set_text_or_na(); unit_description_label.set_text_or_na(); unit_description_label.set_text_or_na(); unit_load_state_label.set_text_or_na(); @@ -425,7 +452,33 @@ public class MainWindow : Window { unit_cgroup_label.set_text_or_na(); } + public string format_unit_link(string i, bool link) { + Unit? u = get_unit(i); + if(u == null) + return "<span color='grey'>" + i + "</span"; + + string color; + switch (u.sub_state) { + case "active": color = "blue"; break; + case "dead": color = "red"; break; + case "running": color = "green"; break; + default: color = "black"; break; + } + string span = "<span underline='none' color='" + color + "'>" + + i + "(" + + u.sub_state + ")" + "</span>"; + if(link) + return " <a href='" + i + "'>" + span + "</a>"; + else + return span; + } + + public string make_dependency_string(string? prefix, string word, string[] dependencies) { + Gee.Collection<unowned string> sorted = new Gee.TreeSet<string>(); + foreach (string i in dependencies) + sorted.add(i); + bool first = true; string r; @@ -434,16 +487,16 @@ public class MainWindow : Window { else r = prefix; - foreach (string i in dependencies) { + foreach (string i in sorted) { if (r != "") r += first ? "\n" : ","; if (first) { - r += word; + r += "<b>" + word + ":</b>"; first = false; } - r += " <a href=\"" + i + "\">" + i + "</a>"; + r += format_unit_link(i, true); } return r; @@ -452,20 +505,23 @@ public class MainWindow : Window { public void show_unit(Unit unit) { current_unit_id = unit.id; - unit_id_label.set_text_or_na(current_unit_id); - - string a = ""; + string id_display = format_unit_link(current_unit_id, false); + bool has_alias = false; foreach (string i in unit.names) { if (i == current_unit_id) continue; - if (a == "") - a = i; - else - a += "\n" + i; + if (!has_alias) { + id_display += " (aliases:"; + has_alias = true; + } + + id_display += " " + i; } + if(has_alias) + id_display += ")"; - unit_aliases_label.set_text_or_na(a); + unit_id_label.set_markup_or_na(id_display); string[] requires = unit.requires, @@ -511,23 +567,16 @@ public class MainWindow : Window { string fp = unit.fragment_path; if (fp != "") - unit_fragment_path_label.set_markup_or_na("<a href=\"file://" + fp +"\">" + fp + "</a>" ); + unit_fragment_path_label.set_markup_or_na( + "<a href=\"file://" + fp +"\">" + + "<span underline='none' color='black'>" + fp + "</span></a>"); else unit_fragment_path_label.set_text_or_na(); - uint64 t = unit.active_enter_timestamp; - if (t > 0) { - Time timestamp = Time.local((time_t) (t / 1000000)); - unit_active_enter_timestamp_label.set_text_or_na(timestamp.format("%a, %d %b %Y %H:%M:%S %z")); - } else - unit_active_enter_timestamp_label.set_text_or_na(); - t = unit.active_exit_timestamp; - if (t > 0) { - Time timestamp = Time.local((time_t) (t / 1000000)); - unit_active_exit_timestamp_label.set_text_or_na(timestamp.format("%a, %d %b %Y %H:%M:%S %z")); - } else - unit_active_exit_timestamp_label.set_text_or_na(); + unit_active_enter_timestamp_label.set_text_or_na(format_time(unit.active_enter_timestamp)); + + unit_active_exit_timestamp_label.set_text_or_na(format_time(unit.active_exit_timestamp)); bool b = unit.can_start; start_button.set_sensitive(b); @@ -596,7 +645,7 @@ public class MainWindow : Window { try { u.start("replace"); - } catch (IOError e) { + } catch (Error e) { show_error(e.message); } } @@ -609,7 +658,7 @@ public class MainWindow : Window { try { u.stop("replace"); - } catch (IOError e) { + } catch (Error e) { show_error(e.message); } } @@ -622,7 +671,7 @@ public class MainWindow : Window { try { u.reload("replace"); - } catch (IOError e) { + } catch (Error e) { show_error(e.message); } } @@ -635,7 +684,7 @@ public class MainWindow : Window { try { u.restart("replace"); - } catch (IOError e) { + } catch (Error e) { show_error(e.message); } } @@ -648,7 +697,7 @@ public class MainWindow : Window { try { j.cancel(); - } catch (IOError e) { + } catch (Error e) { show_error(e.message); } } @@ -676,7 +725,7 @@ public class MainWindow : Window { 4, u.sub_state, 5, t != "" ? "→ %s".printf(t) : "", 6, u); - } catch (IOError e) { + } catch (Error e) { show_error(e.message); } } @@ -699,8 +748,10 @@ public class MainWindow : Window { "org.freedesktop.systemd1", path); + unit_map[id] = u; + update_unit_iter(iter, id, u); - } catch (IOError e) { + } catch (Error e) { show_error(e.message); } } @@ -736,7 +787,7 @@ public class MainWindow : Window { update_job_iter(iter, id, j); - } catch (IOError e) { + } catch (Error e) { show_error(e.message); } } @@ -760,6 +811,8 @@ public class MainWindow : Window { } } while (unit_model.iter_next(ref iter)); + + unit_map.unset(id); } public void on_job_removed(uint32 id, ObjectPath path, string res) { @@ -816,7 +869,7 @@ public class MainWindow : Window { } while (unit_model.iter_next(ref iter)); - } catch (IOError e) { + } catch (Error e) { show_error(e.message); } } @@ -852,7 +905,7 @@ public class MainWindow : Window { } while (job_model.iter_next(ref iter)); - } catch (IOError e) { + } catch (Error e) { show_error(e.message); } } @@ -865,41 +918,41 @@ public class MainWindow : Window { if (id == null) return false; - switch (unit_type_combo_box.get_active()) { - - case 0: - return true; - - case 1: - return active_state != "inactive" || job != ""; - - case 2: - return id.has_suffix(".service"); - - case 3: - return id.has_suffix(".socket"); - - case 4: - return id.has_suffix(".device"); - - case 5: - return id.has_suffix(".mount"); - - case 6: - return id.has_suffix(".automount"); - - case 7: - return id.has_suffix(".target"); + if (!inactive_checkbox.get_active() + && active_state == "inactive" && job == "") + return false; - case 8: - return id.has_suffix(".snapshot"); + switch (unit_type_combo_box.get_active()) { + case 0: + return true; + case 1: + return id.has_suffix(".target"); + case 2: + return id.has_suffix(".service"); + case 3: + return id.has_suffix(".device"); + case 4: + return id.has_suffix(".mount"); + case 5: + return id.has_suffix(".automount"); + case 6: + return id.has_suffix(".swap"); + case 7: + return id.has_suffix(".socket"); + case 8: + return id.has_suffix(".path"); + case 9: + return id.has_suffix(".timer"); + case 10: + return id.has_suffix(".snapshot"); + default: + assert(false); + return false; } - - return false; } public void unit_type_changed() { - TreeModelFilter model = (TreeModelFilter) unit_view.get_model(); + TreeModelFilter model = (TreeModelFilter) ((TreeModelSort) unit_view.get_model()).get_model(); model.refilter(); } @@ -907,7 +960,7 @@ public class MainWindow : Window { public void on_server_reload() { try { manager.reload(); - } catch (IOError e) { + } catch (Error e) { show_error(e.message); } } @@ -919,7 +972,7 @@ public class MainWindow : Window { if (unit_type_combo_box.get_active() != 0) unit_type_combo_box.set_active(8); - } catch (IOError e) { + } catch (Error e) { show_error(e.message); } } @@ -948,7 +1001,7 @@ public class MainWindow : Window { m.destroy(); show_unit(u); - } catch (IOError e) { + } catch (Error e) { show_error(e.message); } } @@ -968,7 +1021,7 @@ public class MainWindow : Window { path); show_unit(u); - } catch (IOError e) { + } catch (Error e) { show_error(e.message); } diff --git a/src/systemctl-bash-completion.sh b/src/systemctl-bash-completion.sh deleted file mode 100644 index acdc0866cd..0000000000 --- a/src/systemctl-bash-completion.sh +++ /dev/null @@ -1,145 +0,0 @@ -# This file is part of systemd. -# -# Copyright 2010 Ran Benita -# -# 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/>. - -__contains_word () { - local word=$1; shift - for w in $*; do [[ $w = $word ]] && return 0; done - return 1 -} - -__filter_units_by_property () { - local property=$1 value=$2 ; shift ; shift - local -a units=( $* ) - local -a props=( $(systemctl show --property "$property" -- ${units[*]} | grep -v ^$) ) - for ((i=0; $i < ${#units[*]}; i++)); do - if [[ "${props[i]}" = "$property=$value" ]]; then - echo "${units[i]}" - fi - done -} - -__get_all_units () { systemctl list-units --full --all | awk ' {print $1}' ; } -__get_active_units () { systemctl list-units --full | awk ' {print $1}' ; } -__get_inactive_units () { systemctl list-units --full --all | awk '$3 == "inactive" {print $1}' ; } -__get_failed_units () { systemctl list-units --full | awk '$3 == "failed" {print $1}' ; } - -_systemctl () { - local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]} - local verb comps - - local -A OPTS=( - [STANDALONE]='--all -a --defaults --fail --ignore-dependencies --failed --force -f --full --global - --help -h --no-ask-password --no-block --no-pager --no-reload --no-wall - --order --require --quiet -q --privileged -P --system --user --version' - [ARG]='--host -H --kill-mode --kill-who --property -p --signal -s --type -t' - ) - - if __contains_word "$prev" ${OPTS[ARG]}; then - case $prev in - --signal|-s) - comps=$(compgen -A signal | grep '^SIG' | grep -Ev 'RTMIN|RTMAX|JUNK') - ;; - --type|-t) - comps='automount device mount path service snapshot socket swap target timer' - ;; - --kill-who) - comps='all control main' - ;; - --kill-mode) - comps='control-group process' - ;; - --property|-p|--host|-H) - comps='' - ;; - esac - COMPREPLY=( $(compgen -W "$comps" -- "$cur") ) - return 0 - fi - - - if [[ "$cur" = -* ]]; then - COMPREPLY=( $(compgen -W "${OPTS[*]}" -- "$cur") ) - return 0 - fi - - local -A VERBS=( - [ALL_UNITS]='enable disable is-active is-enabled status show' - [FAILED_UNITS]='reset-failed' - [STARTABLE_UNITS]='start restart reload-or-restart' - [STOPPABLE_UNITS]='stop kill try-restart condrestart' - [ISOLATABLE_UNITS]='isolate' - [RELOADABLE_UNITS]='reload reload-or-try-restart force-reload' - [JOBS]='cancel' - [SNAPSHOTS]='delete' - [ENVS]='set-environment unset-environment' - [STANDALONE]='daemon-reexec daemon-reload default dot dump emergency exit halt kexec - list-jobs list-units poweroff reboot rescue show-environment' - [NAME]='snapshot load' - ) - - local verb - for ((i=0; $i <= $COMP_CWORD; i++)); do - if __contains_word "${COMP_WORDS[i]}" ${VERBS[*]} && - ! __contains_word "${COMP_WORDS[i-1]}" ${OPTS[ARG}]}; then - verb=${COMP_WORDS[i]} - break - fi - done - - if [[ -z $verb ]]; then - comps="${VERBS[*]}" - - elif __contains_word "$verb" ${VERBS[ALL_UNITS]}; then - comps=$( __get_all_units ) - - elif __contains_word "$verb" ${VERBS[STARTABLE_UNITS]}; then - comps=$( __filter_units_by_property CanStart yes \ - $( __get_inactive_units | grep -Ev '\.(device|snapshot)$' )) - - elif __contains_word "$verb" ${VERBS[STOPPABLE_UNITS]}; then - comps=$( __filter_units_by_property CanStop yes \ - $( __get_active_units ) ) - - elif __contains_word "$verb" ${VERBS[RELOADABLE_UNITS]}; then - comps=$( __filter_units_by_property CanReload yes \ - $( __get_active_units ) ) - - elif __contains_word "$verb" ${VERBS[ISOLATABLE_UNITS]}; then - comps=$( __filter_units_by_property AllowIsolate yes \ - $( __get_all_units ) ) - - elif __contains_word "$verb" ${VERBS[FAILED_UNITS]}; then - comps=$( __get_failed_units ) - - elif __contains_word "$verb" ${VERBS[STANDALONE]} ${VERBS[NAME]}; then - comps='' - - elif __contains_word "$verb" ${VERBS[JOBS]}; then - comps=$( systemctl list-jobs | awk '{print $1}' ) - - elif __contains_word "$verb" ${VERBS[SNAPSHOTS]}; then - comps=$( systemctl list-units --type snapshot --full --all | awk '{print $1}' ) - - elif __contains_word "$verb" ${VERBS[ENVS]}; then - comps=$( systemctl show-environment | sed 's_\([^=]\+=\).*_\1_' ) - compopt -o nospace - fi - - COMPREPLY=( $(compgen -W "$comps" -- "$cur") ) - return 0 -} -complete -F _systemctl systemctl diff --git a/src/systemctl.c b/src/systemctl.c index 620b91ff63..ab6d126a26 100644 --- a/src/systemctl.c +++ b/src/systemctl.c @@ -35,6 +35,8 @@ #include <sys/prctl.h> #include <dbus/dbus.h> +#include <systemd/sd-daemon.h> + #include "log.h" #include "util.h" #include "macro.h" @@ -49,7 +51,6 @@ #include "list.h" #include "path-lookup.h" #include "conf-parser.h" -#include "sd-daemon.h" #include "shutdownd.h" #include "exit-status.h" #include "bus-errors.h" @@ -58,6 +59,7 @@ #include "pager.h" #include "spawn-agent.h" #include "install.h" +#include "logs-show.h" static const char *arg_type = NULL; static char **arg_property = NULL; @@ -66,6 +68,7 @@ static const char *arg_job_mode = "replace"; static UnitFileScope arg_scope = UNIT_FILE_SYSTEM; static bool arg_immediate = false; static bool arg_no_block = false; +static bool arg_no_legend = false; static bool arg_no_pager = false; static bool arg_no_wtmp = false; static bool arg_no_sync = false; @@ -116,6 +119,9 @@ static enum transport { TRANSPORT_POLKIT } arg_transport = TRANSPORT_NORMAL; static const char *arg_host = NULL; +static bool arg_follow = false; +static unsigned arg_lines = 10; +static OutputMode arg_output = OUTPUT_SHORT; static bool private_bus = false; @@ -137,6 +143,8 @@ static bool on_tty(void) { } static void pager_open_if_enabled(void) { + + /* Cache result before we open the pager */ on_tty(); if (arg_no_pager) @@ -158,12 +166,12 @@ static void agent_open_if_enabled(void) { agent_open(); } -static const char *ansi_highlight(bool b) { +static const char *ansi_highlight_red(bool b) { if (!on_tty()) return ""; - return b ? ANSI_HIGHLIGHT_ON : ANSI_HIGHLIGHT_OFF; + return b ? ANSI_HIGHLIGHT_RED_ON : ANSI_HIGHLIGHT_OFF; } static const char *ansi_highlight_green(bool b) { @@ -312,35 +320,60 @@ static bool output_show_unit(const struct unit_info *u) { } static void output_units_list(const struct unit_info *unit_infos, unsigned c) { - unsigned active_len, sub_len, job_len, n_shown = 0; + unsigned id_len, max_id_len, active_len, sub_len, job_len, desc_len, n_shown = 0; const struct unit_info *u; + max_id_len = sizeof("UNIT")-1; active_len = sizeof("ACTIVE")-1; sub_len = sizeof("SUB")-1; job_len = sizeof("JOB")-1; + desc_len = 0; for (u = unit_infos; u < unit_infos + c; u++) { if (!output_show_unit(u)) continue; + max_id_len = MAX(max_id_len, strlen(u->id)); active_len = MAX(active_len, strlen(u->active_state)); sub_len = MAX(sub_len, strlen(u->sub_state)); if (u->job_id != 0) job_len = MAX(job_len, strlen(u->job_type)); } - if (on_tty()) { - printf("%-25s %-6s %-*s %-*s %-*s", "UNIT", "LOAD", + if (!arg_full) { + unsigned basic_len; + id_len = MIN(max_id_len, 25); + basic_len = 5 + id_len + 6 + active_len + sub_len + job_len; + if (basic_len < (unsigned) columns()) { + unsigned extra_len, incr; + extra_len = columns() - basic_len; + /* Either UNIT already got 25, or is fully satisfied. + * Grant up to 25 to DESC now. */ + incr = MIN(extra_len, 25); + desc_len += incr; + extra_len -= incr; + /* split the remaining space between UNIT and DESC, + * but do not give UNIT more than it needs. */ + if (extra_len > 0) { + incr = MIN(extra_len / 2, max_id_len - id_len); + id_len += incr; + desc_len += extra_len - incr; + } + } + } else + id_len = max_id_len; + + if (!arg_no_legend) { + printf("%-*s %-6s %-*s %-*s %-*s ", id_len, "UNIT", "LOAD", active_len, "ACTIVE", sub_len, "SUB", job_len, "JOB"); - if (columns() >= 80+12 || arg_full || !arg_no_pager) - printf(" %s\n", "DESCRIPTION"); + if (!arg_full && arg_no_pager) + printf("%.*s\n", desc_len, "DESCRIPTION"); else - printf("\n"); + printf("%s\n", "DESCRIPTION"); } for (u = unit_infos; u < unit_infos + c; u++) { char *e; - int a = 0, b = 0; const char *on_loaded, *off_loaded; const char *on_active, *off_active; @@ -349,52 +382,35 @@ static void output_units_list(const struct unit_info *unit_infos, unsigned c) { n_shown++; - if (!streq(u->load_state, "loaded") && - !streq(u->load_state, "banned")) { - on_loaded = ansi_highlight(true); - off_loaded = ansi_highlight(false); + if (streq(u->load_state, "error")) { + on_loaded = ansi_highlight_red(true); + off_loaded = ansi_highlight_red(false); } else on_loaded = off_loaded = ""; if (streq(u->active_state, "failed")) { - on_active = ansi_highlight(true); - off_active = ansi_highlight(false); + on_active = ansi_highlight_red(true); + off_active = ansi_highlight_red(false); } else on_active = off_active = ""; - e = arg_full ? NULL : ellipsize(u->id, 25, 33); + e = arg_full ? NULL : ellipsize(u->id, id_len, 33); - printf("%-25s %s%-6s%s %s%-*s %-*s%s%n", - e ? e : u->id, + printf("%-*s %s%-6s%s %s%-*s %-*s%s %-*s ", + id_len, e ? e : u->id, on_loaded, u->load_state, off_loaded, on_active, active_len, u->active_state, sub_len, u->sub_state, off_active, - &a); - - free(e); - - a -= strlen(on_loaded) + strlen(off_loaded); - a -= strlen(on_active) + strlen(off_active); - - if (u->job_id != 0) - printf(" %-*s", job_len, u->job_type); + job_len, u->job_id ? u->job_type : ""); + if (!arg_full && arg_no_pager) + printf("%.*s\n", desc_len, u->description); else - b = 1 + job_len; - - if (a + b + 1 < columns()) { - if (u->job_id == 0) - printf(" %-*s", job_len, ""); - - if (arg_full || !arg_no_pager) - printf(" %s", u->description); - else - printf(" %.*s", columns() - a - b - 1, u->description); - } + printf("%s\n", u->description); - fputs("\n", stdout); + free(e); } - if (on_tty()) { + if (!arg_no_legend) { printf("\nLOAD = Reflects whether the unit definition was properly loaded.\n" "ACTIVE = The high-level unit activation state, i.e. generalization of SUB.\n" "SUB = The low-level unit activation state, values depend on unit type.\n" @@ -539,11 +555,30 @@ static bool output_show_unit_file(const UnitFileList *u) { } static void output_unit_file_list(const UnitFileList *units, unsigned c) { - unsigned n_shown = 0; + unsigned max_id_len, id_cols, state_cols, n_shown = 0; const UnitFileList *u; - if (on_tty()) - printf("%-25s %-6s\n", "UNIT FILE", "STATE"); + max_id_len = sizeof("UNIT FILE")-1; + state_cols = sizeof("STATE")-1; + for (u = units; u < units + c; u++) { + if (!output_show_unit_file(u)) + continue; + + max_id_len = MAX(max_id_len, strlen(file_name_from_path(u->path))); + state_cols = MAX(state_cols, strlen(unit_file_state_to_string(u->state))); + } + + if (!arg_full) { + unsigned basic_cols; + id_cols = MIN(max_id_len, 25); + basic_cols = 1 + id_cols + state_cols; + if (basic_cols < (unsigned) columns()) + id_cols += MIN(columns() - basic_cols, max_id_len - id_cols); + } else + id_cols = max_id_len; + + if (!arg_no_legend) + printf("%-*s %-*s\n", id_cols, "UNIT FILE", state_cols, "STATE"); for (u = units; u < units + c; u++) { char *e; @@ -558,8 +593,8 @@ static void output_unit_file_list(const UnitFileList *units, unsigned c) { if (u->state == UNIT_FILE_MASKED || u->state == UNIT_FILE_MASKED_RUNTIME || u->state == UNIT_FILE_DISABLED) { - on = ansi_highlight(true); - off = ansi_highlight(false); + on = ansi_highlight_red(true); + off = ansi_highlight_red(false); } else if (u->state == UNIT_FILE_ENABLED) { on = ansi_highlight_green(true); off = ansi_highlight_green(false); @@ -568,16 +603,16 @@ static void output_unit_file_list(const UnitFileList *units, unsigned c) { id = file_name_from_path(u->path); - e = arg_full ? NULL : ellipsize(id, 25, 33); + e = arg_full ? NULL : ellipsize(id, id_cols, 33); - printf("%-25s %s%-6s%s\n", - e ? e : id, - on, unit_file_state_to_string(u->state), off); + printf("%-*s %s%-*s%s\n", + id_cols, e ? e : id, + on, state_cols, unit_file_state_to_string(u->state), off); free(e); } - if (on_tty()) + if (!arg_no_legend) printf("\n%u unit files listed.\n", n_shown); } @@ -591,8 +626,6 @@ static int list_unit_files(DBusConnection *bus, char **args) { dbus_error_init(&error); - assert(bus); - pager_open_if_enabled(); if (avoid_bus()) { @@ -608,6 +641,7 @@ static int list_unit_files(DBusConnection *bus, char **args) { r = unit_file_get_list(arg_scope, arg_root, h); if (r < 0) { + unit_file_list_free(h); log_error("Failed to get unit file list: %s", strerror(-r)); return r; } @@ -627,6 +661,8 @@ static int list_unit_files(DBusConnection *bus, char **args) { hashmap_free(h); } else { + assert(bus); + m = dbus_message_new_method_call( "org.freedesktop.systemd1", "/org/freedesktop/systemd1", @@ -1054,7 +1090,7 @@ finish: } static int load_unit(DBusConnection *bus, char **args) { - DBusMessage *m = NULL, *reply = NULL; + DBusMessage *m = NULL; DBusError error; int r; char **name; @@ -1065,6 +1101,7 @@ static int load_unit(DBusConnection *bus, char **args) { assert(args); STRV_FOREACH(name, args+1) { + DBusMessage *reply; if (!(m = dbus_message_new_method_call( "org.freedesktop.systemd1", @@ -1102,9 +1139,6 @@ finish: if (m) dbus_message_unref(m); - if (reply) - dbus_message_unref(reply); - dbus_error_free(&error); return r; @@ -1403,9 +1437,9 @@ static int wait_for_jobs(DBusConnection *bus, Set *s) { 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."); + log_error("A dependency job failed. See system journal for details."); else if (!streq(d.result, "done") && !streq(d.result, "skipped")) - log_error("Job failed. See system logs and 'systemctl status' for details."); + log_error("Job failed. See system journal and 'systemctl status' for details."); } if (streq_ptr(d.result, "timeout")) @@ -1794,7 +1828,7 @@ finish: } static int kill_unit(DBusConnection *bus, char **args) { - DBusMessage *m = NULL, *reply = NULL; + DBusMessage *m = NULL; int r = 0; DBusError error; char **name; @@ -1811,6 +1845,7 @@ static int kill_unit(DBusConnection *bus, char **args) { arg_kill_mode = streq(arg_kill_who, "all") ? "control-group" : "process"; STRV_FOREACH(name, args+1) { + DBusMessage *reply; if (!(m = dbus_message_new_method_call( "org.freedesktop.systemd1", @@ -1850,9 +1885,6 @@ finish: if (m) dbus_message_unref(m); - if (reply) - dbus_message_unref(reply); - dbus_error_free(&error); return r; @@ -1962,6 +1994,7 @@ typedef struct UnitStatusInfo { const char *load_state; const char *active_state; const char *sub_state; + const char *unit_file_state; const char *description; const char *following; @@ -1969,7 +2002,11 @@ typedef struct UnitStatusInfo { const char *path; const char *default_control_group; + const char *load_error; + const char *result; + usec_t inactive_exit_timestamp; + usec_t inactive_exit_timestamp_monotonic; usec_t active_enter_timestamp; usec_t active_exit_timestamp; usec_t inactive_enter_timestamp; @@ -2032,14 +2069,17 @@ static void print_status_info(UnitStatusInfo *i) { if (i->following) printf("\t Follow: unit currently follows state of %s\n", i->following); - if (streq_ptr(i->load_state, "failed") || - streq_ptr(i->load_state, "banned")) { - on = ansi_highlight(true); - off = ansi_highlight(false); + if (streq_ptr(i->load_state, "error")) { + on = ansi_highlight_red(true); + off = ansi_highlight_red(false); } else on = off = ""; - if (i->path) + if (i->load_error) + printf("\t Loaded: %s%s%s (Reason: %s)\n", on, strna(i->load_state), off, i->load_error); + else if (i->path && i->unit_file_state) + printf("\t Loaded: %s%s%s (%s; %s)\n", on, strna(i->load_state), off, i->path, i->unit_file_state); + else if (i->path) printf("\t Loaded: %s%s%s (%s)\n", on, strna(i->load_state), off, i->path); else printf("\t Loaded: %s%s%s\n", on, strna(i->load_state), off); @@ -2047,8 +2087,8 @@ static void print_status_info(UnitStatusInfo *i) { ss = streq_ptr(i->active_state, i->sub_state) ? NULL : i->sub_state; if (streq_ptr(i->active_state, "failed")) { - on = ansi_highlight(true); - off = ansi_highlight(false); + on = ansi_highlight_red(true); + off = ansi_highlight_red(false); } else if (streq_ptr(i->active_state, "active") || streq_ptr(i->active_state, "reloading")) { on = ansi_highlight_green(true); off = ansi_highlight_green(false); @@ -2067,6 +2107,9 @@ static void print_status_info(UnitStatusInfo *i) { strna(i->active_state), off); + if (!isempty(i->result) && !streq(i->result, "success")) + printf(" (Result: %s)", i->result); + timestamp = (streq_ptr(i->active_state, "active") || streq_ptr(i->active_state, "reloading")) ? i->active_enter_timestamp : (streq_ptr(i->active_state, "inactive") || @@ -2124,8 +2167,8 @@ static void print_status_info(UnitStatusInfo *i) { good = is_clean_exit(p->code, p->status); if (!good) { - on = ansi_highlight(true); - off = ansi_highlight(false); + on = ansi_highlight_red(true); + off = ansi_highlight_red(false); } else on = off = ""; @@ -2148,8 +2191,6 @@ static void print_status_info(UnitStatusInfo *i) { printf(")%s\n", off); - on = off = NULL; - if (i->main_pid == p->pid && i->start_timestamp == p->start_timestamp && i->exit_timestamp == p->start_timestamp) @@ -2168,7 +2209,7 @@ static void print_status_info(UnitStatusInfo *i) { if (i->running) { char *t = NULL; - get_process_name(i->main_pid, &t); + get_process_comm(i->main_pid, &t); if (t) { printf(" (%s)", t); free(t); @@ -2202,7 +2243,7 @@ static void print_status_info(UnitStatusInfo *i) { printf(" Control: %u", (unsigned) i->control_pid); - get_process_name(i->control_pid, &t); + get_process_comm(i->control_pid, &t); if (t) { printf(" (%s)", t); free(t); @@ -2226,14 +2267,19 @@ static void print_status_info(UnitStatusInfo *i) { else c = 0; - show_cgroup_by_path(i->default_control_group, "\t\t ", c); + show_cgroup_by_path(i->default_control_group, "\t\t ", c, false); } } + if (i->id && arg_transport != TRANSPORT_SSH) { + printf("\n"); + show_journal_by_unit(i->id, arg_output, NULL, 0, i->inactive_exit_timestamp_monotonic, arg_lines, arg_all, arg_follow); + } + if (i->need_daemon_reload) printf("\n%sWarning:%s Unit file changed on disk, 'systemctl %s daemon-reload' recommended.\n", - ansi_highlight(true), - ansi_highlight(false), + ansi_highlight_red(true), + ansi_highlight_red(false), arg_scope == UNIT_FILE_SYSTEM ? "--system" : "--user"); } @@ -2281,6 +2327,10 @@ static int status_property(const char *name, DBusMessageIter *iter, UnitStatusIn i->what = s; else if (streq(name, "Following")) i->following = s; + else if (streq(name, "UnitFileState")) + i->unit_file_state = s; + else if (streq(name, "Result")) + i->result = s; } break; @@ -2352,6 +2402,8 @@ static int status_property(const char *name, DBusMessageIter *iter, UnitStatusIn i->inactive_enter_timestamp = (usec_t) u; else if (streq(name, "InactiveExitTimestamp")) i->inactive_exit_timestamp = (usec_t) u; + else if (streq(name, "InactiveExitTimestampMonotonic")) + i->inactive_exit_timestamp_monotonic = (usec_t) u; else if (streq(name, "ActiveExitTimestamp")) i->active_exit_timestamp = (usec_t) u; else if (streq(name, "ConditionTimestamp")) @@ -2392,6 +2444,30 @@ static int status_property(const char *name, DBusMessageIter *iter, UnitStatusIn break; } + + case DBUS_TYPE_STRUCT: { + + if (streq(name, "LoadError")) { + DBusMessageIter sub; + const char *n, *message; + int r; + + dbus_message_iter_recurse(iter, &sub); + + r = bus_iter_get_basic_and_next(&sub, DBUS_TYPE_STRING, &n, true); + if (r < 0) + return r; + + r = bus_iter_get_basic_and_next(&sub, DBUS_TYPE_STRING, &message, false); + if (r < 0) + return r; + + if (!isempty(message)) + i->load_error = message; + } + + break; + } } return 0; @@ -2433,6 +2509,16 @@ static int print_property(const char *name, DBusMessageIter *iter) { printf("%s=%s\n", name, s); return 0; + } else if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING && streq(name, "LoadError")) { + const char *a = NULL, *b = NULL; + + if (bus_iter_get_basic_and_next(&sub, DBUS_TYPE_STRING, &a, true) >= 0) + bus_iter_get_basic_and_next(&sub, DBUS_TYPE_STRING, &b, false); + + if (arg_all || !isempty(a) || !isempty(b)) + printf("%s=%s \"%s\"\n", name, strempty(a), strempty(b)); + + return 0; } break; @@ -2452,7 +2538,7 @@ static int print_property(const char *name, DBusMessageIter *iter) { if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &path, true) >= 0 && bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_BOOLEAN, &ignore, false) >= 0) - printf("EnvironmentFile=%s (ignore=%s)\n", path, yes_no(ignore)); + printf("EnvironmentFile=%s (ignore_errors=%s)\n", path, yes_no(ignore)); dbus_message_iter_next(&sub); } @@ -2503,6 +2589,30 @@ static int print_property(const char *name, DBusMessageIter *iter) { return 0; + } else if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "ControlGroupAttributes")) { + DBusMessageIter sub, sub2; + + dbus_message_iter_recurse(iter, &sub); + while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) { + const char *controller, *attr, *value; + + dbus_message_iter_recurse(&sub, &sub2); + + if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &controller, true) >= 0 && + bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &attr, true) >= 0 && + bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &value, false) >= 0) { + + printf("ControlGroupAttribute={ controller=%s ; attribute=%s ; value=\"%s\" }\n", + controller, + attr, + value); + } + + dbus_message_iter_next(&sub); + } + + return 0; + } else if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && startswith(name, "Exec")) { DBusMessageIter sub; @@ -2517,7 +2627,7 @@ static int print_property(const char *name, DBusMessageIter *iter) { t = strv_join(info.argv, " "); - printf("%s={ path=%s ; argv[]=%s ; ignore=%s ; start_time=[%s] ; stop_time=[%s] ; pid=%u ; code=%s ; status=%i%s%s }\n", + printf("%s={ path=%s ; argv[]=%s ; ignore_errors=%s ; start_time=[%s] ; stop_time=[%s] ; pid=%u ; code=%s ; status=%i%s%s }\n", name, strna(info.path), strna(t), @@ -3163,7 +3273,7 @@ finish: } static int reset_failed(DBusConnection *bus, char **args) { - DBusMessage *m = NULL, *reply = NULL; + DBusMessage *m = NULL; int r; DBusError error; char **name; @@ -3175,6 +3285,7 @@ static int reset_failed(DBusConnection *bus, char **args) { return daemon_reload(bus, args); STRV_FOREACH(name, args+1) { + DBusMessage *reply; if (!(m = dbus_message_new_method_call( "org.freedesktop.systemd1", @@ -3211,9 +3322,6 @@ finish: if (m) dbus_message_unref(m); - if (reply) - dbus_message_unref(reply); - dbus_error_free(&error); return r; @@ -3371,7 +3479,7 @@ finish: static int enable_sysv_units(char **args) { int r = 0; -#if defined (HAVE_SYSV_COMPAT) && (defined(TARGET_FEDORA) || defined(TARGET_MANDRIVA) || defined(TARGET_SUSE) || defined(TARGET_MEEGO) || defined(TARGET_ALTLINUX)) +#if defined (HAVE_SYSV_COMPAT) && (defined(TARGET_FEDORA) || defined(TARGET_MANDRIVA) || defined(TARGET_SUSE) || defined(TARGET_MEEGO) || defined(TARGET_ALTLINUX) || defined(TARGET_MAGEIA)) const char *verb = args[0]; unsigned f = 1, t = 1; LookupPaths paths; @@ -3554,12 +3662,15 @@ static int enable_unit(DBusConnection *bus, char **args) { int r; DBusError error; - dbus_error_init(&error); - r = enable_sysv_units(args); if (r < 0) return r; + if (!args[1]) + return 0; + + dbus_error_init(&error); + if (!bus || avoid_bus()) { if (streq(verb, "enable")) { r = unit_file_enable(arg_scope, arg_runtime, arg_root, args+1, arg_force, &changes, &n_changes); @@ -3586,11 +3697,13 @@ static int enable_unit(DBusConnection *bus, char **args) { goto finish; } - for (i = 0; i < n_changes; i++) { - if (changes[i].type == UNIT_FILE_SYMLINK) - log_info("ln -s '%s' '%s'", changes[i].source, changes[i].path); - else - log_info("rm '%s'", changes[i].path); + if (!arg_quiet) { + for (i = 0; i < n_changes; i++) { + if (changes[i].type == UNIT_FILE_SYMLINK) + log_info("ln -s '%s' '%s'", changes[i].source, changes[i].path); + else + log_info("rm '%s'", changes[i].path); + } } } else { @@ -3706,10 +3819,12 @@ static int enable_unit(DBusConnection *bus, char **args) { goto finish; } - if (streq(type, "symlink")) - log_info("ln -s '%s' '%s'", source, path); - else - log_info("rm '%s'", path); + if (!arg_quiet) { + if (streq(type, "symlink")) + log_info("ln -s '%s' '%s'", source, path); + else + log_info("rm '%s'", path); + } dbus_message_iter_next(&sub); } @@ -3862,6 +3977,7 @@ static int systemctl_help(void) { " --no-wall Don't send wall message before halt/power-off/reboot\n" " --no-reload When enabling/disabling unit files, don't reload daemon\n" " configuration\n" + " --no-legend Do not print a legend (column headers and hints)\n" " --no-pager Do not pipe output into a pager\n" " --no-ask-password\n" " Do not ask for system passwords\n" @@ -3873,7 +3989,11 @@ static int systemctl_help(void) { " -f --force When enabling unit files, override existing symlinks\n" " When shutting down, execute action immediately\n" " --root=PATH Enable unit files in the specified root directory\n" - " --runtime Enable unit files only temporarily until next reboot\n\n" + " --runtime Enable unit files only temporarily until next reboot\n" + " -n --lines=INTEGER Journal entries to show\n" + " --follow Follow journal\n" + " -o --output=STRING Change journal output mode (short, short-monotonic,\n" + " verbose, export, json, cat)\n\n" "Unit Commands:\n" " list-units List loaded units\n" " start [NAME...] Start (activate) one or more units\n" @@ -4012,6 +4132,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) { ARG_SYSTEM, ARG_GLOBAL, ARG_NO_BLOCK, + ARG_NO_LEGEND, ARG_NO_PAGER, ARG_NO_WALL, ARG_ORDER, @@ -4023,7 +4144,8 @@ static int systemctl_parse_argv(int argc, char *argv[]) { ARG_KILL_WHO, ARG_NO_ASK_PASSWORD, ARG_FAILED, - ARG_RUNTIME + ARG_RUNTIME, + ARG_FOLLOW }; static const struct option options[] = { @@ -4040,6 +4162,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) { { "system", no_argument, NULL, ARG_SYSTEM }, { "global", no_argument, NULL, ARG_GLOBAL }, { "no-block", no_argument, NULL, ARG_NO_BLOCK }, + { "no-legend", no_argument, NULL, ARG_NO_LEGEND }, { "no-pager", no_argument, NULL, ARG_NO_PAGER }, { "no-wall", no_argument, NULL, ARG_NO_WALL }, { "quiet", no_argument, NULL, 'q' }, @@ -4055,6 +4178,9 @@ static int systemctl_parse_argv(int argc, char *argv[]) { { "host", required_argument, NULL, 'H' }, { "privileged",no_argument, NULL, 'P' }, { "runtime", no_argument, NULL, ARG_RUNTIME }, + { "lines", required_argument, NULL, 'n' }, + { "follow", no_argument, NULL, ARG_FOLLOW }, + { "output", required_argument, NULL, 'o' }, { NULL, 0, NULL, 0 } }; @@ -4066,7 +4192,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) { /* Only when running as systemctl we ask for passwords */ arg_ask_password = true; - while ((c = getopt_long(argc, argv, "ht:p:aqfs:H:P", options, NULL)) >= 0) { + while ((c = getopt_long(argc, argv, "ht:p:aqfs:H:Pn:o:", options, NULL)) >= 0) { switch (c) { @@ -4128,6 +4254,10 @@ static int systemctl_parse_argv(int argc, char *argv[]) { arg_no_block = true; break; + case ARG_NO_LEGEND: + arg_no_legend = true; + break; + case ARG_NO_PAGER: arg_no_pager = true; break; @@ -4200,6 +4330,25 @@ static int systemctl_parse_argv(int argc, char *argv[]) { arg_runtime = true; break; + case 'n': + if (safe_atou(optarg, &arg_lines) < 0) { + log_error("Failed to parse lines '%s'", optarg); + return -EINVAL; + } + break; + + case ARG_FOLLOW: + arg_follow = true; + break; + + case 'o': + arg_output = output_mode_from_string(optarg); + if (arg_output < 0) { + log_error("Unknown output '%s'.", optarg); + return -EINVAL; + } + break; + case '?': return -EINVAL; @@ -4360,18 +4509,6 @@ 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 { @@ -4914,7 +5051,8 @@ static int systemctl_main(DBusConnection *bus, int argc, char *argv[], DBusError * enable/disable */ if (!streq(verbs[i].verb, "enable") && !streq(verbs[i].verb, "disable") && - !streq(verbs[i].verb, "is-enable") && + !streq(verbs[i].verb, "is-enabled") && + !streq(verbs[i].verb, "list-unit-files") && !streq(verbs[i].verb, "reenable") && !streq(verbs[i].verb, "preset") && !streq(verbs[i].verb, "mask") && @@ -4926,8 +5064,17 @@ static int systemctl_main(DBusConnection *bus, int argc, char *argv[], DBusError return 0; } + if (!bus) { + log_error("Failed to get D-Bus connection: %s", + dbus_error_is_set(error) ? error->message : "No connection to service manager."); + return -EIO; + } + + } else { + if (!bus && !avoid_bus()) { - log_error("Failed to get D-Bus connection: %s", error->message); + log_error("Failed to get D-Bus connection: %s", + dbus_error_is_set(error) ? error->message : "No connection to service manager."); return -EIO; } } @@ -4983,7 +5130,7 @@ static int reload_with_fallback(DBusConnection *bus) { if (bus) { /* First, try systemd via D-Bus. */ - if (daemon_reload(bus, NULL) > 0) + if (daemon_reload(bus, NULL) >= 0) return 0; } @@ -5061,7 +5208,7 @@ static int halt_main(DBusConnection *bus) { if (!arg_no_wtmp) { if (sd_booted() > 0) log_debug("Not writing utmp record, assuming that systemd-update-utmp is used."); - else if ((r = utmp_put_shutdown(0)) < 0) + else if ((r = utmp_put_shutdown()) < 0) log_warning("Failed to write utmp record: %s", strerror(-r)); } diff --git a/src/systemd-analyze b/src/systemd-analyze index ae7dcfbd8a..729aa05ca1 100755 --- a/src/systemd-analyze +++ b/src/systemd-analyze @@ -82,7 +82,7 @@ if len(sys.argv) <= 1 or sys.argv[1] == 'time': initrd_time, start_time, finish_time = acquire_start_time() if initrd_time > 0: - print "Startup finished in %lums (kernel) + %lums (initrd) + %lums (userspace) = %lums" % ( \ + print "Startup finished in %lums (kernel) + %lums (initramfs) + %lums (userspace) = %lums" % ( \ initrd_time/1000, \ (start_time - initrd_time)/1000, \ (finish_time - start_time)/1000, \ @@ -116,7 +116,11 @@ elif sys.argv[1] == 'plot': data = acquire_time_data() s = sorted(data, key = lambda i: i[1]) - count = 0 + # Account for kernel and initramfs bars if they exist + if initrd_time > 0: + count = 3 + else: + count = 2 for name, ixt, aet, axt, iet in s: @@ -130,7 +134,7 @@ elif sys.argv[1] == 'plot': bar_space = bar_height * 0.1 # 1000px = 10s, 1px = 10ms - width = (finish_time - start_time)/10000 + border*2 + width = finish_time/10000 + border*2 height = count * (bar_height + bar_space) + border * 2 if width < 1000: @@ -147,7 +151,7 @@ elif sys.argv[1] == 'plot': context.set_line_width(1) context.set_source_rgb(0.7, 0.7, 0.7) - for x in range(0, (finish_time - start_time)/10000, 100): + for x in range(0, finish_time/10000 + 100, 100): context.move_to(x, 0) context.line_to(x, height-border*2) @@ -163,11 +167,30 @@ elif sys.argv[1] == 'plot': banner = "Running on %s (%s %s) %s" % (os.uname()[1], os.uname()[2], os.uname()[3], os.uname()[4]) draw_text(context, 0, -15, banner, hcenter = 0, vcenter = 1) - for x in range(0, (finish_time - start_time)/10000, 100): + for x in range(0, finish_time/10000 + 100, 100): draw_text(context, x, -5, "%lus" % (x/100), vcenter = 0, hcenter = 0) y = 0 + # draw boxes for kernel and initramfs boot time + if initrd_time > 0: + draw_box(context, 0, y, initrd_time/10000, bar_height, 0.7, 0.7, 0.7) + draw_text(context, 10, y + bar_height/2, "kernel", hcenter = 0) + y += bar_height + bar_space + + draw_box(context, initrd_time/10000, y, start_time/10000-initrd_time/10000, bar_height, 0.7, 0.7, 0.7) + draw_text(context, initrd_time/10000 + 10, y + bar_height/2, "initramfs", hcenter = 0) + y += bar_height + bar_space + + else: + draw_box(context, 0, y, start_time/10000, bar_height, 0.6, 0.6, 0.6) + draw_text(context, 10, y + bar_height/2, "kernel", hcenter = 0) + y += bar_height + bar_space + + draw_box(context, start_time/10000, y, finish_time/10000-start_time/10000, bar_height, 0.7, 0.7, 0.7) + draw_text(context, start_time/10000 + 10, y + bar_height/2, "userspace", hcenter = 0) + y += bar_height + bar_space + for name, ixt, aet, axt, iet in s: drawn = False @@ -176,7 +199,7 @@ elif sys.argv[1] == 'plot': if ixt >= start_time and ixt <= finish_time: # Activating - a = ixt - start_time + a = ixt b = min(filter(lambda x: x >= ixt, (aet, axt, iet, finish_time))) - ixt draw_box(context, a/10000, y, b/10000, bar_height, 1, 0, 0) @@ -188,7 +211,7 @@ elif sys.argv[1] == 'plot': if aet >= start_time and aet <= finish_time: # Active - a = aet - start_time + a = aet b = min(filter(lambda x: x >= aet, (axt, iet, finish_time))) - aet draw_box(context, a/10000, y, b/10000, bar_height, .8, .6, .6) @@ -200,7 +223,7 @@ elif sys.argv[1] == 'plot': if axt >= start_time and axt <= finish_time: # Deactivating - a = axt - start_time + a = axt b = min(filter(lambda x: x >= axt, (iet, finish_time))) - axt draw_box(context, a/10000, y, b/10000, bar_height, .6, .4, .4) @@ -221,6 +244,18 @@ elif sys.argv[1] == 'plot': draw_text(context, 0, height-border*2, "Legend: Red = Activating; Pink = Active; Dark Pink = Deactivating", hcenter = 0, vcenter = -1) + if initrd_time > 0: + draw_text(context, 0, height-border*2 + bar_height, "Startup finished in %lums (kernel) + %lums (initramfs) + %lums (userspace) = %lums" % ( \ + initrd_time/1000, \ + (start_time - initrd_time)/1000, \ + (finish_time - start_time)/1000, \ + finish_time/1000), hcenter = 0, vcenter = -1) + else: + draw_text(context, 0, height-border*2 + bar_height, "Startup finished in %lums (kernel) + %lums (userspace) = %lums" % ( \ + start_time/1000, \ + (finish_time - start_time)/1000, \ + finish_time/1000), hcenter = 0, vcenter = -1) + surface.finish() elif sys.argv[1] in ("help", "--help", "-h"): help() diff --git a/src/systemd-bash-completion.sh b/src/systemd-bash-completion.sh new file mode 100644 index 0000000000..176591f281 --- /dev/null +++ b/src/systemd-bash-completion.sh @@ -0,0 +1,264 @@ +# This file is part of systemd. +# +# Copyright 2010 Ran Benita +# +# 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/>. + +__systemctl() { + systemctl --full --no-legend "$@" +} + +__contains_word () { + local word=$1; shift + for w in $*; do [[ $w = $word ]] && return 0; done + return 1 +} + +__filter_units_by_property () { + local property=$1 value=$2 ; shift ; shift + local -a units=( $* ) + local -a props=( $(__systemctl show --property "$property" -- ${units[*]} | grep -v ^$) ) + for ((i=0; $i < ${#units[*]}; i++)); do + if [[ "${props[i]}" = "$property=$value" ]]; then + echo "${units[i]}" + fi + done +} + +__get_all_units () { __systemctl list-units --all | awk ' {print $1}' ; } +__get_active_units () { __systemctl list-units | awk ' {print $1}' ; } +__get_inactive_units () { __systemctl list-units --all | awk '$3 == "inactive" {print $1}' ; } +__get_failed_units () { __systemctl list-units | awk '$3 == "failed" {print $1}' ; } +__get_enabled_units () { __systemctl list-unit-files | awk '$2 == "enabled" {print $1}' ; } +__get_disabled_units () { __systemctl list-unit-files | awk '$2 == "disabled" {print $1}' ; } +__get_masked_units () { __systemctl list-unit-files | awk '$2 == "masked" {print $1}' ; } + +_systemctl () { + local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]} + local verb comps + + local -A OPTS=( + [STANDALONE]='--all -a --defaults --fail --ignore-dependencies --failed --force -f --full --global + --help -h --no-ask-password --no-block --no-legend --no-pager --no-reload --no-wall + --order --require --quiet -q --privileged -P --system --user --version --runtime' + [ARG]='--host -H --kill-mode --kill-who --property -p --signal -s --type -t --root' + ) + + if __contains_word "$prev" ${OPTS[ARG]}; then + case $prev in + --signal|-s) + comps=$(compgen -A signal) + ;; + --type|-t) + comps='automount device mount path service snapshot socket swap target timer' + ;; + --kill-who) + comps='all control main' + ;; + --kill-mode) + comps='control-group process' + ;; + --root) + comps=$(compgen -A directory -- "$cur" ) + compopt -o filenames + ;; + --host|-H) + comps=$(compgen -A hostname) + ;; + --property|-p) + comps='' + ;; + esac + COMPREPLY=( $(compgen -W "$comps" -- "$cur") ) + return 0 + fi + + + if [[ "$cur" = -* ]]; then + COMPREPLY=( $(compgen -W "${OPTS[*]}" -- "$cur") ) + return 0 + fi + + local -A VERBS=( + [ALL_UNITS]='is-active is-enabled status show mask preset' + [ENABLED_UNITS]='disable reenable' + [DISABLED_UNITS]='enable' + [FAILED_UNITS]='reset-failed' + [STARTABLE_UNITS]='start' + [STOPPABLE_UNITS]='stop condstop kill try-restart condrestart' + [ISOLATABLE_UNITS]='isolate' + [RELOADABLE_UNITS]='reload condreload reload-or-try-restart force-reload' + [RESTARTABLE_UNITS]='restart reload-or-restart' + [MASKED_UNITS]='unmask' + [JOBS]='cancel' + [SNAPSHOTS]='delete' + [ENVS]='set-environment unset-environment' + [STANDALONE]='daemon-reexec daemon-reload default dot dump + emergency exit halt kexec list-jobs list-units + list-unit-files poweroff reboot rescue show-environment' + [NAME]='snapshot load' + [FILE]='link' + ) + + for ((i=0; $i <= $COMP_CWORD; i++)); do + if __contains_word "${COMP_WORDS[i]}" ${VERBS[*]} && + ! __contains_word "${COMP_WORDS[i-1]}" ${OPTS[ARG}]}; then + verb=${COMP_WORDS[i]} + break + fi + done + + if [[ -z $verb ]]; then + comps="${VERBS[*]}" + + elif __contains_word "$verb" ${VERBS[ALL_UNITS]}; then + comps=$( __get_all_units ) + + elif __contains_word "$verb" ${VERBS[ENABLED_UNITS]}; then + comps=$( __get_enabled_units ) + + elif __contains_word "$verb" ${VERBS[DISABLED_UNITS]}; then + comps=$( __get_disabled_units ) + + elif __contains_word "$verb" ${VERBS[STARTABLE_UNITS]}; then + comps=$( __filter_units_by_property CanStart yes \ + $( __get_inactive_units | grep -Ev '\.(device|snapshot)$' )) + + elif __contains_word "$verb" ${VERBS[RESTARTABLE_UNITS]}; then + comps=$( __filter_units_by_property CanStart yes \ + $( __get_all_units | grep -Ev '\.(device|snapshot|socket|timer)$' )) + + elif __contains_word "$verb" ${VERBS[STOPPABLE_UNITS]}; then + comps=$( __filter_units_by_property CanStop yes \ + $( __get_active_units ) ) + + elif __contains_word "$verb" ${VERBS[RELOADABLE_UNITS]}; then + comps=$( __filter_units_by_property CanReload yes \ + $( __get_active_units ) ) + + elif __contains_word "$verb" ${VERBS[ISOLATABLE_UNITS]}; then + comps=$( __filter_units_by_property AllowIsolate yes \ + $( __get_all_units ) ) + + elif __contains_word "$verb" ${VERBS[FAILED_UNITS]}; then + comps=$( __get_failed_units ) + + elif __contains_word "$verb" ${VERBS[MASKED_UNITS]}; then + comps=$( __get_masked_units ) + + elif __contains_word "$verb" ${VERBS[STANDALONE]} ${VERBS[NAME]}; then + comps='' + + elif __contains_word "$verb" ${VERBS[JOBS]}; then + comps=$( __systemctl list-jobs | awk '{print $1}' ) + + elif __contains_word "$verb" ${VERBS[SNAPSHOTS]}; then + comps=$( __systemctl list-units --type snapshot --full --all | awk '{print $1}' ) + + elif __contains_word "$verb" ${VERBS[ENVS]}; then + comps=$( __systemctl show-environment | sed 's_\([^=]\+=\).*_\1_' ) + compopt -o nospace + + elif __contains_word "$verb" ${VERBS[FILE]}; then + comps=$( compgen -A file -- "$cur" ) + compopt -o filenames + fi + + COMPREPLY=( $(compgen -W "$comps" -- "$cur") ) + return 0 +} +complete -F _systemctl systemctl + +__get_all_sessions () { systemd-loginctl list-sessions | awk '{print $1}' ; } +__get_all_users () { systemd-loginctl list-users | awk '{print $2}' ; } +__get_all_seats () { systemd-loginctl list-seats | awk '{print $1}' ; } + +_systemd_loginctl () { + local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]} + local verb comps + + local -A OPTS=( + [STANDALONE]='--all -a --help -h --no-pager --privileged -P --version' + [ARG]='--host -H --kill-who --property -p --signal -s' + ) + + if __contains_word "$prev" ${OPTS[ARG]}; then + case $prev in + --signal|-s) + comps=$(compgen -A signal) + ;; + --kill-who) + comps='all leader' + ;; + --host|-H) + comps=$(compgen -A hostname) + ;; + --property|-p) + comps='' + ;; + esac + COMPREPLY=( $(compgen -W "$comps" -- "$cur") ) + return 0 + fi + + + if [[ "$cur" = -* ]]; then + COMPREPLY=( $(compgen -W "${OPTS[*]}" -- "$cur") ) + return 0 + fi + + local -A VERBS=( + [SESSIONS]='session-status show-session activate lock-session unlock-session terminate-session kill-session' + [USERS]='user-status show-user enable-linger disable-linger terminate-user kill-user' + [SEATS]='seat-status show-seat terminate-seat' + [STANDALONE]='list-sessions list-users list-seats flush-devices' + [ATTACH]='attach' + ) + + for ((i=0; $i <= $COMP_CWORD; i++)); do + if __contains_word "${COMP_WORDS[i]}" ${VERBS[*]} && + ! __contains_word "${COMP_WORDS[i-1]}" ${OPTS[ARG}]}; then + verb=${COMP_WORDS[i]} + break + fi + done + + if [[ -z $verb ]]; then + comps="${VERBS[*]}" + + elif __contains_word "$verb" ${VERBS[SESSIONS]}; then + comps=$( __get_all_sessions ) + + elif __contains_word "$verb" ${VERBS[USERS]}; then + comps=$( __get_all_users ) + + elif __contains_word "$verb" ${VERBS[SEATS]}; then + comps=$( __get_all_seats ) + + elif __contains_word "$verb" ${VERBS[STANDALONE]}; then + comps='' + + elif __contains_word "$verb" ${VERBS[ATTACH]}; then + if [[ $prev = $verb ]]; then + comps=$( __get_all_seats ) + else + comps=$(compgen -A file -- "$cur" ) + compopt -o filenames + fi + fi + + COMPREPLY=( $(compgen -W "$comps" -- "$cur") ) + return 0 +} +complete -F _systemd_loginctl systemd-loginctl diff --git a/systemd.pc.in b/src/systemd.pc.in index 29376e55b0..4f2abb0868 100644 --- a/systemd.pc.in +++ b/src/systemd.pc.in @@ -7,6 +7,7 @@ prefix=@prefix@ exec_prefix=@exec_prefix@ +systemdutildir=@rootlibexecdir@ systemdsystemunitdir=@systemunitdir@ systemduserunitdir=@userunitdir@ systemdsystemconfdir=@pkgsysconfdir@/system diff --git a/src/systemd/Makefile b/src/systemd/Makefile new file mode 120000 index 0000000000..d0b0e8e008 --- /dev/null +++ b/src/systemd/Makefile @@ -0,0 +1 @@ +../Makefile
\ No newline at end of file diff --git a/src/sd-daemon.h b/src/systemd/sd-daemon.h index 46dc7fd7e5..fe51159ee6 100644 --- a/src/sd-daemon.h +++ b/src/systemd/sd-daemon.h @@ -58,8 +58,8 @@ extern "C" { You may find an up-to-date version of these source files online: - http://cgit.freedesktop.org/systemd/plain/src/sd-daemon.h - http://cgit.freedesktop.org/systemd/plain/src/sd-daemon.c + http://cgit.freedesktop.org/systemd/systemd/plain/src/systemd/sd-daemon.h + http://cgit.freedesktop.org/systemd/systemd/plain/src/sd-daemon.c This should compile on non-Linux systems, too, but with the exception of the sd_is_xxx() calls all functions will become NOPs. @@ -217,6 +217,11 @@ int sd_is_mq(int fd, const char *path); MAINPID=... The main pid of a daemon, in case systemd did not fork off the process itself. Example: "MAINPID=4711" + WATCHDOG=1 Tells systemd to update the watchdog timestamp. + Services using this feature should do this in + regular intervals. A watchdog framework can use the + timestamps to detect failed services. + Daemons can choose to send additional variables. However, it is recommended to prefix variable names not listed above with X_. diff --git a/src/systemd/sd-id128.h b/src/systemd/sd-id128.h new file mode 100644 index 0000000000..af2841eb77 --- /dev/null +++ b/src/systemd/sd-id128.h @@ -0,0 +1,69 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#ifndef fooid128hfoo +#define fooid128hfoo + +/*** + This file is part of systemd. + + Copyright 2011 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 <inttypes.h> +#include <stdbool.h> +#include <string.h> + +#ifdef __cplusplus +extern "C" { +#endif + +typedef union sd_id128 sd_id128_t; + +union sd_id128 { + uint8_t bytes[16]; + uint64_t qwords[2]; +}; + +char *sd_id128_to_string(sd_id128_t id, char s[33]); + +int sd_id128_from_string(const char s[33], sd_id128_t *ret); + +int sd_id128_randomize(sd_id128_t *ret); + +int sd_id128_get_machine(sd_id128_t *ret); + +int sd_id128_get_boot(sd_id128_t *ret); + +#define SD_ID128_MAKE(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) \ + ((sd_id128_t) { .bytes = { 0x##v0, 0x##v1, 0x##v2, 0x##v3, 0x##v4, 0x##v5, 0x##v6, 0x##v7, \ + 0x##v8, 0x##v9, 0x##v10, 0x##v11, 0x##v12, 0x##v13, 0x##v14, 0x##v15 }}) + +/* Note that SD_ID128_FORMAT_VAL will evaluate the passed argument 16 + * times. It is hence not a good idea to call this macro with an + * expensive function as paramater or an expression with side + * effects */ +#define SD_ID128_FORMAT_STR "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x" +#define SD_ID128_FORMAT_VAL(x) (x).bytes[0], (x).bytes[1], (x).bytes[2], (x).bytes[3], (x).bytes[4], (x).bytes[5], (x).bytes[6], (x).bytes[7], (x).bytes[8], (x).bytes[9], (x).bytes[10], (x).bytes[11], (x).bytes[12], (x).bytes[13], (x).bytes[14], (x).bytes[15] + +static inline bool sd_id128_equal(sd_id128_t a, sd_id128_t b) { + return memcmp(&a, &b, 16) == 0; +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/systemd/sd-journal.h b/src/systemd/sd-journal.h new file mode 100644 index 0000000000..5a2dab1569 --- /dev/null +++ b/src/systemd/sd-journal.h @@ -0,0 +1,114 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#ifndef foojournalhfoo +#define foojournalhfoo + +/*** + This file is part of systemd. + + Copyright 2011 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 <inttypes.h> +#include <sys/types.h> +#include <stdarg.h> +#include <sys/uio.h> + +#include <systemd/sd-id128.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* Write to daemon */ + +int sd_journal_print(int piority, const char *format, ...) __attribute__ ((format (printf, 2, 3))); +int sd_journal_printv(int priority, const char *format, va_list ap); + +int sd_journal_send(const char *format, ...) __attribute__((sentinel)); +int sd_journal_sendv(const struct iovec *iov, int n); + +int sd_journal_stream_fd(const char *identifier, int priority, int level_prefix); + +/* Browse journal stream */ + +typedef struct sd_journal sd_journal; + +enum { + SD_JOURNAL_LOCAL_ONLY = 1, + SD_JOURNAL_RUNTIME_ONLY = 2, + SD_JOURNAL_SYSTEM_ONLY = 4 +}; + +int sd_journal_open(sd_journal **ret, int flags); +void sd_journal_close(sd_journal *j); + +int sd_journal_previous(sd_journal *j); +int sd_journal_next(sd_journal *j); + +int sd_journal_previous_skip(sd_journal *j, uint64_t skip); +int sd_journal_next_skip(sd_journal *j, uint64_t skip); + +int sd_journal_get_realtime_usec(sd_journal *j, uint64_t *ret); +int sd_journal_get_monotonic_usec(sd_journal *j, uint64_t *ret, sd_id128_t *ret_boot_id); +int sd_journal_get_data(sd_journal *j, const char *field, const void **data, size_t *l); +int sd_journal_enumerate_data(sd_journal *j, const void **data, size_t *l); +void sd_journal_restart_data(sd_journal *j); + +int sd_journal_add_match(sd_journal *j, const void *data, size_t size); +void sd_journal_flush_matches(sd_journal *j); + +int sd_journal_seek_head(sd_journal *j); +int sd_journal_seek_tail(sd_journal *j); +int sd_journal_seek_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t usec); +int sd_journal_seek_realtime_usec(sd_journal *j, uint64_t usec); +int sd_journal_seek_cursor(sd_journal *j, const char *cursor); + +int sd_journal_get_cursor(sd_journal *j, char **cursor); + +/* int sd_journal_query_unique(sd_journal *j, const char *field); /\* missing *\/ */ +/* int sd_journal_enumerate_unique(sd_journal *j, const void **data, size_t *l); /\* missing *\/ */ +/* void sd_journal_restart_unique(sd_journal *j); /\* missing *\/ */ + +enum { + SD_JOURNAL_NOP, + SD_JOURNAL_APPEND, + SD_JOURNAL_INVALIDATE_ADD, + SD_JOURNAL_INVALIDATE_REMOVE +}; + +int sd_journal_get_fd(sd_journal *j); +int sd_journal_process(sd_journal *j); + +#define SD_JOURNAL_FOREACH(j) \ + if (sd_journal_seek_head(j) >= 0) \ + while (sd_journal_next(j) > 0) + +#define SD_JOURNAL_FOREACH_BACKWARDS(j) \ + if (sd_journal_seek_tail(j) >= 0) \ + while (sd_journal_previous(j) > 0) + +#define SD_JOURNAL_FOREACH_DATA(j, data, l) \ + for (sd_journal_restart_data(j); sd_journal_enumerate_data((j), &(data), &(l)) > 0; ) + +#define SD_JOURNAL_FOREACH_UNIQUE(j, data, l) \ + for (sd_journal_restart_unique(j); sd_journal_enumerate_data((j), &(data), &(l)) > 0; ) + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/sd-login.h b/src/systemd/sd-login.h index 1623a7dbf6..2f3c90c129 100644 --- a/src/sd-login.h +++ b/src/systemd/sd-login.h @@ -24,6 +24,10 @@ #include <sys/types.h> +#ifdef __cplusplus +extern "C" { +#endif + /* * A few points: * @@ -32,7 +36,8 @@ * * Free the data we return with libc free(). * - * We return error codes as negative errno, kernel-style. + * We return error codes as negative errno, kernel-style. 0 or + * positive on success. * * These functions access data in /proc, /sys/fs/cgroup and /run. All * of these are virtual file systems, hence the accesses are @@ -52,6 +57,10 @@ int sd_pid_get_session(pid_t pid, char **session); * return an error for system processes. */ int sd_pid_get_owner_uid(pid_t pid, uid_t *uid); +/* Get systemd unit (i.e. service) name from PID. This will return an + * error for non-service processes. */ +int sd_pid_get_unit(pid_t, char **unit); + /* Get state from uid. Possible states: offline, lingering, online, active */ int sd_uid_get_state(uid_t uid, char**state); @@ -59,12 +68,14 @@ int sd_uid_get_state(uid_t uid, char**state); * look for active sessions only. */ int sd_uid_is_on_seat(uid_t uid, int require_active, const char *seat); -/* Return sessions of user. If require_active is true will look - * for active sessions only. */ +/* Return sessions of user. If require_active is true will look for + * active sessions only. Returns number of sessions as return + * value. If sessions is NULL will just return number of sessions. */ int sd_uid_get_sessions(uid_t uid, int require_active, char ***sessions); /* Return seats of user is on. If require_active is true will look for - * active seats only. */ + * active seats only. Returns number of seats. If seats is NULL will + * just return number of seats.*/ int sd_uid_get_seats(uid_t uid, int require_active, char ***seats); /* Return 1 if the session is a active */ @@ -76,22 +87,39 @@ int sd_session_get_uid(const char *session, uid_t *uid); /* Determine seat of session */ int sd_session_get_seat(const char *session, char **seat); +/* Determine the (PAM) service name this session was registered by. */ +int sd_session_get_service(const char *session, char **service); + +/* Determine the type of this session, i.e. one of "tty", "x11" or "unspecified". */ +int sd_session_get_type(const char *session, char **type); + +/* Determine the class of this session, i.e. one of "user", "greeter" or "lock-screen". */ +int sd_session_get_class(const char *session, char **class); + +/* Determine the X11 display of this session. */ +int sd_session_get_display(const char *session, char **display); + /* Return active session and user of seat */ int sd_seat_get_active(const char *seat, char **session, uid_t *uid); -/* Return sessions and users on seat */ +/* Return sessions and users on seat. Returns number of sessions as + * return value. If sessions is NULL returns only the number of + * sessions. */ int sd_seat_get_sessions(const char *seat, char ***sessions, uid_t **uid, unsigned *n_uids); /* Return whether the seat is multi-session capable */ int sd_seat_can_multi_session(const char *seat); -/* Get all seats */ +/* Get all seats, store in *seats. Returns the number of seats. If + * seats is NULL only returns number of seats. */ int sd_get_seats(char ***seats); -/* Get all sessions */ +/* Get all sessions, store in *sessions. Returns the number of + * sessions. If sessions is NULL only returns number of sessions. */ int sd_get_sessions(char ***sessions); -/* Get all logged in users */ +/* Get all logged in users, store in *users. Returns the number of + * users. If users is NULL only returns the number of users. */ int sd_get_uids(uid_t **users); /* Monitor object */ @@ -110,4 +138,8 @@ int sd_login_monitor_flush(sd_login_monitor *m); /* Get FD from monitor */ int sd_login_monitor_get_fd(sd_login_monitor *m); +#ifdef __cplusplus +} +#endif + #endif diff --git a/src/systemd/sd-messages.h b/src/systemd/sd-messages.h new file mode 100644 index 0000000000..c5ac3abd0c --- /dev/null +++ b/src/systemd/sd-messages.h @@ -0,0 +1,41 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#ifndef foosdmessageshfoo +#define foosdmessageshfoo + +/*** + This file is part of systemd. + + Copyright 2012 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 <systemd/sd-id128.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#define SD_MESSAGE_JOURNAL_START SD_ID128_MAKE(f7,73,79,a8,49,0b,40,8b,be,5f,69,40,50,5a,77,7b) +#define SD_MESSAGE_JOURNAL_STOP SD_ID128_MAKE(d9,3f,b3,c9,c2,4d,45,1a,97,ce,a6,15,ce,59,c0,0b) +#define SD_MESSAGE_JOURNAL_DROPPED SD_ID128_MAKE(a5,96,d6,fe,7b,fa,49,94,82,8e,72,30,9e,95,d6,1e) + +#define SD_MESSAGE_COREDUMP SD_ID128_MAKE(fc,2e,22,bc,6e,e6,47,b6,b9,07,29,ab,34,a2,50,b1) + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/sd-readahead.h b/src/systemd/sd-readahead.h index 5bf975a741..1f8c5a0caf 100644 --- a/src/sd-readahead.h +++ b/src/systemd/sd-readahead.h @@ -47,8 +47,8 @@ extern "C" { You may find an up-to-date version of these source files online: - http://cgit.freedesktop.org/systemd/plain/src/sd-readahead.h - http://cgit.freedesktop.org/systemd/plain/src/sd-readahead.c + http://cgit.freedesktop.org/systemd/systemd/plain/src/systemd/sd-readahead.h + http://cgit.freedesktop.org/systemd/systemd/plain/src/readahead/sd-readahead.c This should compile on non-Linux systems, too, but all functions will become NOPs. @@ -56,14 +56,6 @@ extern "C" { See sd-readahead(7) for more information. */ -#ifndef _sd_hidden_ -#if (__GNUC__ >= 4) && !defined(SD_EXPORT_SYMBOLS) -#define _sd_hidden_ __attribute__ ((visibility("hidden"))) -#else -#define _sd_hidden_ -#endif -#endif - /* Controls ongoing disk read-ahead operations during boot-up. The argument must be a string, and either "cancel", "done" or "noreplay". @@ -72,7 +64,7 @@ extern "C" { done = terminate read-ahead data collection, keep collected information noreplay = terminate read-ahead replay */ -int sd_readahead(const char *action) _sd_hidden_; +int sd_readahead(const char *action); #ifdef __cplusplus } diff --git a/src/target.c b/src/target.c index 54c34daa0d..6c1e0c368a 100644 --- a/src/target.c +++ b/src/target.c @@ -45,7 +45,7 @@ static void target_set_state(Target *t, TargetState state) { if (state != old_state) log_debug("%s changed %s -> %s", - t->meta.id, + UNIT(t)->id, target_state_to_string(old_state), target_state_to_string(state)); @@ -75,7 +75,7 @@ static int target_add_default_dependencies(Target *t) { * sure we don't create a loop. */ for (k = 0; k < ELEMENTSOF(deps); k++) - SET_FOREACH(other, t->meta.dependencies[deps[k]], i) + SET_FOREACH(other, UNIT(t)->dependencies[deps[k]], i) if ((r = unit_add_default_target_dependency(other, UNIT(t))) < 0) return r; @@ -93,8 +93,8 @@ static int target_load(Unit *u) { return r; /* This is a new unit? Then let's add in some extras */ - if (u->meta.load_state == UNIT_LOADED) { - if (u->meta.default_dependencies) + if (u->load_state == UNIT_LOADED) { + if (u->default_dependencies) if ((r = target_add_default_dependencies(t)) < 0) return r; } @@ -199,6 +199,11 @@ DEFINE_STRING_TABLE_LOOKUP(target_state, TargetState); const UnitVTable target_vtable = { .suffix = ".target", + .object_size = sizeof(Target), + .sections = + "Unit\0" + "Target\0" + "Install\0", .load = target_load, .coldplug = target_coldplug, diff --git a/src/target.h b/src/target.h index b1d055f9a8..5b97c86fbf 100644 --- a/src/target.h +++ b/src/target.h @@ -34,7 +34,7 @@ typedef enum TargetState { } TargetState; struct Target { - Meta meta; + Unit meta; TargetState state, deserialized_state; }; diff --git a/src/test-daemon.c b/src/test-daemon.c index c7600d4d63..20c5d1517e 100644 --- a/src/test-daemon.c +++ b/src/test-daemon.c @@ -21,7 +21,7 @@ #include <unistd.h> -#include "sd-daemon.h" +#include <systemd/sd-daemon.h> int main(int argc, char*argv[]) { diff --git a/src/test-engine.c b/src/test-engine.c index 60619b9d36..46a2d2cda0 100644 --- a/src/test-engine.c +++ b/src/test-engine.c @@ -31,7 +31,7 @@ int main(int argc, char *argv[]) { Unit *a = NULL, *b = NULL, *c = NULL, *d = NULL, *e = NULL, *g = NULL, *h = NULL; Job *j; - assert_se(set_unit_path("test2") >= 0); + assert_se(set_unit_path("test") >= 0); assert_se(manager_new(MANAGER_SYSTEM, &m) >= 0); diff --git a/src/test-id128.c b/src/test-id128.c new file mode 100644 index 0000000000..617c95568b --- /dev/null +++ b/src/test-id128.c @@ -0,0 +1,52 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2011 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 <string.h> + +#include <systemd/sd-id128.h> + +#include "util.h" +#include "macro.h" + +#define ID128_WALDI SD_ID128_MAKE(01, 02, 03, 04, 05, 06, 07, 08, 09, 0a, 0b, 0c, 0d, 0e, 0f, 10) + +int main(int argc, char *argv[]) { + sd_id128_t id, id2; + char t[33]; + + assert_se(sd_id128_randomize(&id) == 0); + printf("random: %s\n", sd_id128_to_string(id, t)); + + assert_se(sd_id128_from_string(t, &id2) == 0); + assert_se(sd_id128_equal(id, id2)); + + assert_se(sd_id128_get_machine(&id) == 0); + printf("machine: %s\n", sd_id128_to_string(id, t)); + + assert_se(sd_id128_get_boot(&id) == 0); + printf("boot: %s\n", sd_id128_to_string(id, t)); + + printf("waldi: %s\n", sd_id128_to_string(ID128_WALDI, t)); + + printf("waldi2: " SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(ID128_WALDI)); + + return 0; +} diff --git a/src/timedate/.gitignore b/src/timedate/.gitignore new file mode 100644 index 0000000000..48757f0968 --- /dev/null +++ b/src/timedate/.gitignore @@ -0,0 +1 @@ +org.freedesktop.timedate1.policy diff --git a/src/timedate/Makefile b/src/timedate/Makefile new file mode 120000 index 0000000000..d0b0e8e008 --- /dev/null +++ b/src/timedate/Makefile @@ -0,0 +1 @@ +../Makefile
\ No newline at end of file diff --git a/src/org.freedesktop.timedate1.conf b/src/timedate/org.freedesktop.timedate1.conf index c9c221b644..c9c221b644 100644 --- a/src/org.freedesktop.timedate1.conf +++ b/src/timedate/org.freedesktop.timedate1.conf diff --git a/src/org.freedesktop.timedate1.policy.in b/src/timedate/org.freedesktop.timedate1.policy.in index f73e1aa7e0..3d9c2081d6 100644 --- a/src/org.freedesktop.timedate1.policy.in +++ b/src/timedate/org.freedesktop.timedate1.policy.in @@ -47,4 +47,15 @@ </defaults> </action> + <action id="org.freedesktop.timedate1.set-ntp"> + <_description>Turn network time synchronization on or off</_description> + <_message>Authentication is required to control whether + network time synchronization shall be enabled.</_message> + <defaults> + <allow_any>auth_admin_keep</allow_any> + <allow_inactive>auth_admin_keep</allow_inactive> + <allow_active>auth_admin_keep</allow_active> + </defaults> + </action> + </policyconfig> diff --git a/src/org.freedesktop.timedate1.service b/src/timedate/org.freedesktop.timedate1.service index c3120b66cb..c3120b66cb 100644 --- a/src/org.freedesktop.timedate1.service +++ b/src/timedate/org.freedesktop.timedate1.service diff --git a/src/timedated.c b/src/timedate/timedated.c index 4abcf1af73..6a7d980c3e 100644 --- a/src/timedated.c +++ b/src/timedate/timedated.c @@ -29,6 +29,7 @@ #include "strv.h" #include "dbus-common.h" #include "polkit.h" +#include "def.h" #define NULL_ADJTIME_UTC "0.0 0 0\n0\nUTC\n" #define NULL_ADJTIME_LOCAL "0.0 0 0\n0\nLOCAL\n" @@ -37,6 +38,7 @@ " <interface name=\"org.freedesktop.timedate1\">\n" \ " <property name=\"Timezone\" type=\"s\" access=\"read\"/>\n" \ " <property name=\"LocalRTC\" type=\"b\" access=\"read\"/>\n" \ + " <property name=\"NTP\" type=\"b\" access=\"read\"/>\n" \ " <method name=\"SetTime\">\n" \ " <arg name=\"usec_utc\" type=\"x\" direction=\"in\"/>\n" \ " <arg name=\"relative\" type=\"b\" direction=\"in\"/>\n" \ @@ -51,6 +53,10 @@ " <arg name=\"fix_system\" type=\"b\" direction=\"in\"/>\n" \ " <arg name=\"user_interaction\" type=\"b\" direction=\"in\"/>\n" \ " </method>\n" \ + " <method name=\"SetNTP\">\n" \ + " <arg name=\"use_ntp\" type=\"b\" direction=\"in\"/>\n" \ + " <arg name=\"user_interaction\" type=\"b\" direction=\"in\"/>\n" \ + " </method>\n" \ " </interface>\n" #define INTROSPECTION \ @@ -64,18 +70,27 @@ #define INTERFACES_LIST \ BUS_GENERIC_INTERFACES_LIST \ - "org.freedesktop.locale1\0" + "org.freedesktop.timedate1\0" const char timedate_interface[] _introspect_("timedate1") = INTERFACE; -static char *zone = NULL; -static bool local_rtc = false; +typedef struct TZ { + char *zone; + bool local_rtc; + int use_ntp; +} TZ; + +static TZ tz = { + .use_ntp = -1, +}; + +static usec_t remain_until; static void free_data(void) { - free(zone); - zone = NULL; + free(tz.zone); + tz.zone = NULL; - local_rtc = false; + tz.local_rtc = false; } static bool valid_timezone(const char *name) { @@ -131,10 +146,10 @@ static void verify_timezone(void) { size_t l, q; int j, k; - if (!zone) + if (!tz.zone) return; - p = strappend("/usr/share/zoneinfo/", zone); + p = strappend("/usr/share/zoneinfo/", tz.zone); if (!p) { log_error("Out of memory"); return; @@ -147,8 +162,8 @@ static void verify_timezone(void) { if (j < 0 || k < 0 || l != q || memcmp(a, b, l)) { log_warning("/etc/localtime and /etc/timezone out of sync."); - free(zone); - zone = NULL; + free(tz.zone); + tz.zone = NULL; } free(a); @@ -160,13 +175,29 @@ static int read_data(void) { free_data(); - r = read_one_line_file("/etc/timezone", &zone); - if (r < 0 && r != -ENOENT) - return r; + r = read_one_line_file("/etc/timezone", &tz.zone); + if (r < 0) { + if (r != -ENOENT) + log_warning("Failed to read /etc/timezone: %s", strerror(-r)); + +#ifdef TARGET_FEDORA + r = parse_env_file("/etc/sysconfig/clock", NEWLINE, + "ZONE", &tz.zone, + NULL); + + if (r < 0 && r != -ENOENT) + log_warning("Failed to read /etc/sysconfig/clock: %s", strerror(-r)); +#endif + } + + if (isempty(tz.zone)) { + free(tz.zone); + tz.zone = NULL; + } verify_timezone(); - local_rtc = hwclock_is_localtime() > 0; + tz.local_rtc = hwclock_is_localtime() > 0; return 0; } @@ -175,7 +206,7 @@ static int write_data_timezone(void) { int r = 0; char *p; - if (!zone) { + if (!tz.zone) { if (unlink("/etc/timezone") < 0 && errno != ENOENT) r = -errno; @@ -185,7 +216,7 @@ static int write_data_timezone(void) { return r; } - p = strappend("/usr/share/zoneinfo/", zone); + p = strappend("/usr/share/zoneinfo/", tz.zone); if (!p) { log_error("Out of memory"); return -ENOMEM; @@ -197,7 +228,7 @@ static int write_data_timezone(void) { if (r < 0) return r; - r = write_one_line_file_atomic("/etc/timezone", zone); + r = write_one_line_file_atomic("/etc/timezone", tz.zone); if (r < 0) return r; @@ -213,7 +244,7 @@ static int write_data_local_rtc(void) { if (r != -ENOENT) return r; - if (!local_rtc) + if (!tz.local_rtc) return 0; w = strdup(NULL_ADJTIME_LOCAL); @@ -237,7 +268,7 @@ static int write_data_local_rtc(void) { p++; e = strchr(p, '\n'); - if (!p) { + if (!e) { free(s); return -EIO; } @@ -245,13 +276,13 @@ static int write_data_local_rtc(void) { a = p - s; b = strlen(e); - w = new(char, a + (local_rtc ? 5 : 3) + b + 1); + w = new(char, a + (tz.local_rtc ? 5 : 3) + b + 1); if (!w) { free(s); return -ENOMEM; } - *(char*) mempcpy(stpcpy(mempcpy(w, s, a), local_rtc ? "LOCAL" : "UTC"), e, b) = 0; + *(char*) mempcpy(stpcpy(mempcpy(w, s, a), tz.local_rtc ? "LOCAL" : "UTC"), e, b) = 0; if (streq(w, NULL_ADJTIME_UTC)) { free(w); @@ -271,17 +302,238 @@ static int write_data_local_rtc(void) { return r; } +static int read_ntp(DBusConnection *bus) { + DBusMessage *m = NULL, *reply = NULL; + const char *name = "ntpd.service", *s; + DBusError error; + int r; + + assert(bus); + + dbus_error_init(&error); + + m = dbus_message_new_method_call( + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "GetUnitFileState"); + + if (!m) { + log_error("Out of memory"); + r = -ENOMEM; + goto finish; + } + + if (!dbus_message_append_args(m, + DBUS_TYPE_STRING, &name, + DBUS_TYPE_INVALID)) { + log_error("Could not append arguments to message."); + r = -ENOMEM; + goto finish; + } + + reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error); + if (!reply) { + + if (streq(error.name, "org.freedesktop.DBus.Error.FileNotFound")) { + /* NTP is not installed. */ + tz.use_ntp = false; + r = 0; + goto finish; + } + + log_error("Failed to issue method call: %s", bus_error_message(&error)); + r = -EIO; + goto finish; + } + + if (!dbus_message_get_args(reply, &error, + DBUS_TYPE_STRING, &s, + DBUS_TYPE_INVALID)) { + log_error("Failed to parse reply: %s", bus_error_message(&error)); + r = -EIO; + goto finish; + } + + tz.use_ntp = + streq(s, "enabled") || + streq(s, "enabled-runtime"); + r = 0; + +finish: + if (m) + dbus_message_unref(m); + + if (reply) + dbus_message_unref(reply); + + dbus_error_free(&error); + + return r; +} + +static int start_ntp(DBusConnection *bus, DBusError *error) { + DBusMessage *m = NULL, *reply = NULL; + const char *name = "ntpd.service", *mode = "replace"; + int r; + + assert(bus); + assert(error); + + m = dbus_message_new_method_call( + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + tz.use_ntp ? "StartUnit" : "StopUnit"); + if (!m) { + log_error("Could not allocate message."); + r = -ENOMEM; + goto finish; + } + + if (!dbus_message_append_args(m, + DBUS_TYPE_STRING, &name, + DBUS_TYPE_STRING, &mode, + DBUS_TYPE_INVALID)) { + log_error("Could not append arguments to message."); + r = -ENOMEM; + goto finish; + } + + reply = dbus_connection_send_with_reply_and_block(bus, m, -1, error); + if (!reply) { + log_error("Failed to issue method call: %s", bus_error_message(error)); + r = -EIO; + goto finish; + } + + r = 0; + +finish: + if (m) + dbus_message_unref(m); + + if (reply) + dbus_message_unref(reply); + + return r; +} + +static int enable_ntp(DBusConnection *bus, DBusError *error) { + DBusMessage *m = NULL, *reply = NULL; + const char * const names[] = { "ntpd.service", NULL }; + int r; + DBusMessageIter iter; + dbus_bool_t f = FALSE, t = TRUE; + + assert(bus); + assert(error); + + m = dbus_message_new_method_call( + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + tz.use_ntp ? "EnableUnitFiles" : "DisableUnitFiles"); + + if (!m) { + log_error("Could not allocate message."); + r = -ENOMEM; + goto finish; + } + + dbus_message_iter_init_append(m, &iter); + + r = bus_append_strv_iter(&iter, (char**) names); + if (r < 0) { + log_error("Failed to append unit files."); + goto finish; + } + /* send runtime bool */ + if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_BOOLEAN, &f)) { + log_error("Failed to append runtime boolean."); + r = -ENOMEM; + goto finish; + } + + if (tz.use_ntp) { + /* send force bool */ + if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_BOOLEAN, &t)) { + log_error("Failed to append force boolean."); + r = -ENOMEM; + goto finish; + } + } + + reply = dbus_connection_send_with_reply_and_block(bus, m, -1, error); + if (!reply) { + log_error("Failed to issue method call: %s", bus_error_message(error)); + r = -EIO; + goto finish; + } + + dbus_message_unref(m); + m = dbus_message_new_method_call( + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "Reload"); + if (!m) { + log_error("Could not allocate message."); + r = -ENOMEM; + goto finish; + } + + dbus_message_unref(reply); + reply = dbus_connection_send_with_reply_and_block(bus, m, -1, error); + if (!reply) { + log_error("Failed to issue method call: %s", bus_error_message(error)); + r = -EIO; + goto finish; + } + + r = 0; + +finish: + if (m) + dbus_message_unref(m); + + if (reply) + dbus_message_unref(reply); + + return r; +} + +static int property_append_ntp(DBusMessageIter *i, const char *property, void *data) { + dbus_bool_t db; + + assert(i); + assert(property); + + db = tz.use_ntp > 0; + + if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &db)) + return -ENOMEM; + + return 0; +} + +static const BusProperty bus_timedate_properties[] = { + { "Timezone", bus_property_append_string, "s", offsetof(TZ, zone), true }, + { "LocalRTC", bus_property_append_bool, "b", offsetof(TZ, local_rtc) }, + { "NTP", property_append_ntp, "b", offsetof(TZ, use_ntp) }, + { NULL, } +}; + +static const BusBoundProperties bps[] = { + { "org.freedesktop.timedate1", bus_timedate_properties, &tz }, + { NULL, } +}; + static DBusHandlerResult timedate_message_handler( DBusConnection *connection, DBusMessage *message, void *userdata) { - const BusProperty properties[] = { - { "org.freedesktop.timedate1", "Timezone", bus_property_append_string, "s", zone }, - { "org.freedesktop.timedate1", "LocalRTC", bus_property_append_bool, "b", &local_rtc }, - { NULL, NULL, NULL, NULL, NULL } - }; - DBusMessage *reply = NULL, *changed = NULL; DBusError error; int r; @@ -306,10 +558,10 @@ static DBusHandlerResult timedate_message_handler( if (!valid_timezone(z)) return bus_send_error_reply(connection, message, NULL, -EINVAL); - if (!streq_ptr(z, zone)) { + if (!streq_ptr(z, tz.zone)) { char *t; - r = verify_polkit(connection, message, "org.freedesktop.timedate1.set-timezone", interactive, &error); + r = verify_polkit(connection, message, "org.freedesktop.timedate1.set-timezone", interactive, NULL, &error); if (r < 0) return bus_send_error_reply(connection, message, &error, r); @@ -317,8 +569,8 @@ static DBusHandlerResult timedate_message_handler( if (!t) goto oom; - free(zone); - zone = t; + free(tz.zone); + tz.zone = t; /* 1. Write new configuration file */ r = write_data_timezone(); @@ -327,7 +579,7 @@ static DBusHandlerResult timedate_message_handler( return bus_send_error_reply(connection, message, NULL, r); } - if (local_rtc) { + if (tz.local_rtc) { struct timespec ts; struct tm *tm; @@ -340,7 +592,7 @@ static DBusHandlerResult timedate_message_handler( hwclock_set_time(tm); } - log_info("Changed timezone to '%s'.", zone); + log_info("Changed timezone to '%s'.", tz.zone); changed = bus_properties_changed_new( "/org/freedesktop/timedate1", @@ -364,14 +616,14 @@ static DBusHandlerResult timedate_message_handler( DBUS_TYPE_INVALID)) return bus_send_error_reply(connection, message, &error, -EINVAL); - if (lrtc != local_rtc) { + if (lrtc != tz.local_rtc) { struct timespec ts; - r = verify_polkit(connection, message, "org.freedesktop.timedate1.set-local-rtc", interactive, &error); + r = verify_polkit(connection, message, "org.freedesktop.timedate1.set-local-rtc", interactive, NULL, &error); if (r < 0) return bus_send_error_reply(connection, message, &error, r); - local_rtc = lrtc; + tz.local_rtc = lrtc; /* 1. Write new configuration file */ r = write_data_local_rtc(); @@ -381,7 +633,7 @@ static DBusHandlerResult timedate_message_handler( } /* 2. Teach kernel new timezone */ - if (local_rtc) + if (tz.local_rtc) hwclock_apply_localtime_delta(NULL); else hwclock_reset_localtime_delta(); @@ -395,7 +647,7 @@ static DBusHandlerResult timedate_message_handler( /* Sync system clock from RTC; first, * initialize the timezone fields of * struct tm. */ - if (local_rtc) + if (tz.local_rtc) tm = *localtime(&ts.tv_sec); else tm = *gmtime(&ts.tv_sec); @@ -407,7 +659,7 @@ static DBusHandlerResult timedate_message_handler( /* And set the system clock * with this */ - if (local_rtc) + if (tz.local_rtc) ts.tv_sec = mktime(&tm); else ts.tv_sec = timegm(&tm); @@ -419,7 +671,7 @@ static DBusHandlerResult timedate_message_handler( struct tm *tm; /* Sync RTC from system clock */ - if (local_rtc) + if (tz.local_rtc) tm = localtime(&ts.tv_sec); else tm = gmtime(&ts.tv_sec); @@ -427,7 +679,7 @@ static DBusHandlerResult timedate_message_handler( hwclock_set_time(tm); } - log_error("RTC configured to %s time.", local_rtc ? "local" : "UTC"); + log_info("RTC configured to %s time.", tz.local_rtc ? "local" : "UTC"); changed = bus_properties_changed_new( "/org/freedesktop/timedate1", @@ -458,7 +710,7 @@ static DBusHandlerResult timedate_message_handler( struct timespec ts; struct tm* tm; - r = verify_polkit(connection, message, "org.freedesktop.timedate1.set-time", interactive, &error); + r = verify_polkit(connection, message, "org.freedesktop.timedate1.set-time", interactive, NULL, &error); if (r < 0) return bus_send_error_reply(connection, message, &error, r); @@ -474,7 +726,7 @@ static DBusHandlerResult timedate_message_handler( } /* Sync down to RTC */ - if (local_rtc) + if (tz.local_rtc) tm = localtime(&ts.tv_sec); else tm = gmtime(&ts.tv_sec); @@ -483,9 +735,46 @@ static DBusHandlerResult timedate_message_handler( log_info("Changed local time to %s", ctime(&ts.tv_sec)); } + } else if (dbus_message_is_method_call(message, "org.freedesktop.timedate1", "SetNTP")) { + dbus_bool_t ntp; + dbus_bool_t interactive; + + if (!dbus_message_get_args( + message, + &error, + DBUS_TYPE_BOOLEAN, &ntp, + DBUS_TYPE_BOOLEAN, &interactive, + DBUS_TYPE_INVALID)) + return bus_send_error_reply(connection, message, &error, -EINVAL); + + if (ntp != !!tz.use_ntp) { + + r = verify_polkit(connection, message, "org.freedesktop.timedate1.set-ntp", interactive, NULL, &error); + if (r < 0) + return bus_send_error_reply(connection, message, &error, r); + + tz.use_ntp = !!ntp; + + r = enable_ntp(connection, &error); + if (r < 0) + return bus_send_error_reply(connection, message, &error, r); + + r = start_ntp(connection, &error); + if (r < 0) + return bus_send_error_reply(connection, message, &error, r); + + log_info("Set NTP to %s", tz.use_ntp ? "enabled" : "disabled"); + + changed = bus_properties_changed_new( + "/org/freedesktop/timedate1", + "org.freedesktop.timedate1", + "NTP\0"); + if (!changed) + goto oom; + } } else - return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, properties); + return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, bps); if (!(reply = dbus_message_new_method_return(message))) goto oom; @@ -537,7 +826,10 @@ static int connect_bus(DBusConnection **_bus) { goto fail; } - if (!dbus_connection_register_object_path(bus, "/org/freedesktop/timedate1", &timedate_vtable, NULL)) { + dbus_connection_set_exit_on_disconnect(bus, FALSE); + + if (!dbus_connection_register_object_path(bus, "/org/freedesktop/timedate1", &timedate_vtable, NULL) || + !dbus_connection_add_filter(bus, bus_exit_idle_filter, &remain_until, NULL)) { log_error("Not enough memory"); r = -ENOMEM; goto fail; @@ -573,11 +865,14 @@ fail: int main(int argc, char *argv[]) { int r; DBusConnection *bus = NULL; + bool exiting = false; log_set_target(LOG_TARGET_AUTO); log_parse_environment(); log_open(); + umask(0022); + if (argc == 2 && streq(argv[1], "--introspect")) { fputs(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE "<node>\n", stdout); @@ -592,8 +887,6 @@ int main(int argc, char *argv[]) { goto finish; } - umask(0022); - r = read_data(); if (r < 0) { log_error("Failed to read timezone data: %s", strerror(-r)); @@ -604,8 +897,23 @@ int main(int argc, char *argv[]) { if (r < 0) goto finish; - while (dbus_connection_read_write_dispatch(bus, -1)) - ; + r = read_ntp(bus); + if (r < 0) { + log_error("Failed to determine whether NTP is enabled: %s", strerror(-r)); + goto finish; + } + + remain_until = now(CLOCK_MONOTONIC) + DEFAULT_EXIT_USEC; + for (;;) { + + if (!dbus_connection_read_write_dispatch(bus, exiting ? -1 : (int) (DEFAULT_EXIT_USEC/USEC_PER_MSEC))) + break; + + if (!exiting && remain_until < now(CLOCK_MONOTONIC)) { + exiting = true; + bus_async_unregister_and_exit(bus, "org.freedesktop.hostname1"); + } + } r = 0; diff --git a/src/timer.c b/src/timer.c index 1477aa5256..e318fee201 100644 --- a/src/timer.c +++ b/src/timer.c @@ -40,7 +40,7 @@ static void timer_init(Unit *u) { Timer *t = TIMER(u); assert(u); - assert(u->meta.load_state == UNIT_STUB); + assert(u->load_state == UNIT_STUB); t->next_elapse = (usec_t) -1; } @@ -57,16 +57,18 @@ static void timer_done(Unit *u) { } unit_unwatch_timer(u, &t->timer_watch); + + unit_ref_unset(&t->unit); } static int timer_verify(Timer *t) { assert(t); - if (t->meta.load_state != UNIT_LOADED) + if (UNIT(t)->load_state != UNIT_LOADED) return 0; if (!t->values) { - log_error("%s lacks value setting. Refusing.", t->meta.id); + log_error("%s lacks value setting. Refusing.", UNIT(t)->id); return -EINVAL; } @@ -78,7 +80,7 @@ static int timer_add_default_dependencies(Timer *t) { assert(t); - if (t->meta.manager->running_as == MANAGER_SYSTEM) { + if (UNIT(t)->manager->running_as == MANAGER_SYSTEM) { if ((r = unit_add_dependency_by_name(UNIT(t), UNIT_BEFORE, SPECIAL_BASIC_TARGET, NULL, true)) < 0) return r; @@ -94,21 +96,28 @@ static int timer_load(Unit *u) { int r; assert(u); - assert(u->meta.load_state == UNIT_STUB); + assert(u->load_state == UNIT_STUB); if ((r = unit_load_fragment_and_dropin(u)) < 0) return r; - if (u->meta.load_state == UNIT_LOADED) { + if (u->load_state == UNIT_LOADED) { + + if (!UNIT_DEREF(t->unit)) { + Unit *x; - if (!t->unit) - if ((r = unit_load_related_unit(u, ".service", &t->unit))) + r = unit_load_related_unit(u, ".service", &x); + if (r < 0) return r; - if ((r = unit_add_dependency(u, UNIT_BEFORE, t->unit, true)) < 0) + unit_ref_set(&t->unit, x); + } + + r = unit_add_two_dependencies(u, UNIT_BEFORE, UNIT_TRIGGERS, UNIT_DEREF(t->unit), true); + if (r < 0) return r; - if (t->meta.default_dependencies) + if (UNIT(t)->default_dependencies) if ((r = timer_add_default_dependencies(t)) < 0) return r; } @@ -124,9 +133,11 @@ static void timer_dump(Unit *u, FILE *f, const char *prefix) { fprintf(f, "%sTimer State: %s\n" + "%sResult: %s\n" "%sUnit: %s\n", prefix, timer_state_to_string(t->state), - prefix, t->unit->meta.id); + prefix, timer_result_to_string(t->result), + prefix, UNIT_DEREF(t->unit)->id); LIST_FOREACH(value, v, t->values) fprintf(f, @@ -148,7 +159,7 @@ static void timer_set_state(Timer *t, TimerState state) { if (state != old_state) log_debug("%s changed %s -> %s", - t->meta.id, + UNIT(t)->id, timer_state_to_string(old_state), timer_state_to_string(state)); @@ -174,13 +185,13 @@ static int timer_coldplug(Unit *u) { return 0; } -static void timer_enter_dead(Timer *t, bool success) { +static void timer_enter_dead(Timer *t, TimerResult f) { assert(t); - if (!success) - t->failure = true; + if (f != TIMER_SUCCESS) + t->result = f; - timer_set_state(t, t->failure ? TIMER_FAILED : TIMER_DEAD); + timer_set_state(t, t->result != TIMER_SUCCESS ? TIMER_FAILED : TIMER_DEAD); } static void timer_enter_waiting(Timer *t, bool initial) { @@ -200,7 +211,7 @@ static void timer_enter_waiting(Timer *t, bool initial) { case TIMER_ACTIVE: if (state_translation_table[t->state] == UNIT_ACTIVE) - base = t->meta.inactive_exit_timestamp.monotonic; + base = UNIT(t)->inactive_exit_timestamp.monotonic; else base = n; break; @@ -211,23 +222,23 @@ static void timer_enter_waiting(Timer *t, bool initial) { break; case TIMER_STARTUP: - base = t->meta.manager->startup_timestamp.monotonic; + base = UNIT(t)->manager->startup_timestamp.monotonic; break; case TIMER_UNIT_ACTIVE: - if (t->unit->meta.inactive_exit_timestamp.monotonic <= 0) + if (UNIT_DEREF(t->unit)->inactive_exit_timestamp.monotonic <= 0) continue; - base = t->unit->meta.inactive_exit_timestamp.monotonic; + base = UNIT_DEREF(t->unit)->inactive_exit_timestamp.monotonic; break; case TIMER_UNIT_INACTIVE: - if (t->unit->meta.inactive_enter_timestamp.monotonic <= 0) + if (UNIT_DEREF(t->unit)->inactive_enter_timestamp.monotonic <= 0) continue; - base = t->unit->meta.inactive_enter_timestamp.monotonic; + base = UNIT_DEREF(t->unit)->inactive_enter_timestamp.monotonic; break; default: @@ -263,8 +274,8 @@ static void timer_enter_waiting(Timer *t, bool initial) { return; fail: - log_warning("%s failed to enter waiting state: %s", t->meta.id, strerror(-r)); - timer_enter_dead(t, false); + log_warning("%s failed to enter waiting state: %s", UNIT(t)->id, strerror(-r)); + timer_enter_dead(t, TIMER_FAILURE_RESOURCES); } static void timer_enter_running(Timer *t) { @@ -275,18 +286,18 @@ static void timer_enter_running(Timer *t) { dbus_error_init(&error); /* Don't start job if we are supposed to go down */ - if (t->meta.job && t->meta.job->type == JOB_STOP) + if (UNIT(t)->job && UNIT(t)->job->type == JOB_STOP) return; - if ((r = manager_add_job(t->meta.manager, JOB_START, t->unit, JOB_REPLACE, true, &error, NULL)) < 0) + if ((r = manager_add_job(UNIT(t)->manager, JOB_START, UNIT_DEREF(t->unit), JOB_REPLACE, true, &error, NULL)) < 0) goto fail; timer_set_state(t, TIMER_RUNNING); return; fail: - log_warning("%s failed to queue unit startup job: %s", t->meta.id, bus_error(&error, r)); - timer_enter_dead(t, false); + log_warning("%s failed to queue unit startup job: %s", UNIT(t)->id, bus_error(&error, r)); + timer_enter_dead(t, TIMER_FAILURE_RESOURCES); dbus_error_free(&error); } @@ -297,10 +308,10 @@ static int timer_start(Unit *u) { assert(t); assert(t->state == TIMER_DEAD || t->state == TIMER_FAILED); - if (t->unit->meta.load_state != UNIT_LOADED) + if (UNIT_DEREF(t->unit)->load_state != UNIT_LOADED) return -ENOENT; - t->failure = false; + t->result = TIMER_SUCCESS; timer_enter_waiting(t, true); return 0; } @@ -311,7 +322,7 @@ static int timer_stop(Unit *u) { assert(t); assert(t->state == TIMER_WAITING || t->state == TIMER_RUNNING || t->state == TIMER_ELAPSED); - timer_enter_dead(t, true); + timer_enter_dead(t, TIMER_SUCCESS); return 0; } @@ -323,6 +334,7 @@ static int timer_serialize(Unit *u, FILE *f, FDSet *fds) { assert(fds); unit_serialize_item(u, f, "state", timer_state_to_string(t->state)); + unit_serialize_item(u, f, "result", timer_result_to_string(t->result)); return 0; } @@ -342,6 +354,15 @@ static int timer_deserialize_item(Unit *u, const char *key, const char *value, F log_debug("Failed to parse state value %s", value); else t->deserialized_state = state; + } else if (streq(key, "result")) { + TimerResult f; + + f = timer_result_from_string(value); + if (f < 0) + log_debug("Failed to parse result value %s", value); + else if (f != TIMER_SUCCESS) + t->result = f; + } else log_debug("Unknown serialization key '%s'", key); @@ -369,42 +390,28 @@ static void timer_timer_event(Unit *u, uint64_t elapsed, Watch *w) { if (t->state != TIMER_WAITING) return; - log_debug("Timer elapsed on %s", u->meta.id); + log_debug("Timer elapsed on %s", u->id); timer_enter_running(t); } void timer_unit_notify(Unit *u, UnitActiveState new_state) { - char *n; - int r; Iterator i; + Unit *k; - if (u->meta.type == UNIT_TIMER) + if (u->type == UNIT_TIMER) return; - SET_FOREACH(n, u->meta.names, i) { - char *k; - Unit *p; + SET_FOREACH(k, u->dependencies[UNIT_TRIGGERED_BY], i) { Timer *t; TimerValue *v; - if (!(k = unit_name_change_suffix(n, ".timer"))) { - r = -ENOMEM; - goto fail; - } - - p = manager_get_unit(u->meta.manager, k); - free(k); - - if (!p) + if (k->type != UNIT_TIMER) continue; - if (p->meta.load_state != UNIT_LOADED) + if (k->load_state != UNIT_LOADED) continue; - t = TIMER(p); - - if (t->unit != u) - continue; + t = TIMER(k); /* Reenable all timers that depend on unit state */ LIST_FOREACH(value, v, t->values) @@ -424,7 +431,7 @@ void timer_unit_notify(Unit *u, UnitActiveState new_state) { case TIMER_RUNNING: if (UNIT_IS_INACTIVE_OR_FAILED(new_state)) { - log_debug("%s got notified about unit deactivation.", t->meta.id); + log_debug("%s got notified about unit deactivation.", UNIT(t)->id); timer_enter_waiting(t, false); } @@ -438,11 +445,6 @@ void timer_unit_notify(Unit *u, UnitActiveState new_state) { assert_not_reached("Unknown timer state"); } } - - return; - -fail: - log_error("Failed find timer unit: %s", strerror(-r)); } static void timer_reset_failed(Unit *u) { @@ -453,7 +455,7 @@ static void timer_reset_failed(Unit *u) { if (t->state == TIMER_FAILED) timer_set_state(t, TIMER_DEAD); - t->failure = false; + t->result = TIMER_SUCCESS; } static const char* const timer_state_table[_TIMER_STATE_MAX] = { @@ -476,8 +478,20 @@ static const char* const timer_base_table[_TIMER_BASE_MAX] = { DEFINE_STRING_TABLE_LOOKUP(timer_base, TimerBase); +static const char* const timer_result_table[_TIMER_RESULT_MAX] = { + [TIMER_SUCCESS] = "success", + [TIMER_FAILURE_RESOURCES] = "resources" +}; + +DEFINE_STRING_TABLE_LOOKUP(timer_result, TimerResult); + const UnitVTable timer_vtable = { .suffix = ".timer", + .object_size = sizeof(Timer), + .sections = + "Unit\0" + "Timer\0" + "Install\0", .init = timer_init, .done = timer_done, diff --git a/src/timer.h b/src/timer.h index 6295605510..f5c5c64f25 100644 --- a/src/timer.h +++ b/src/timer.h @@ -56,18 +56,25 @@ typedef struct TimerValue { bool disabled; } TimerValue; +typedef enum TimerResult { + TIMER_SUCCESS, + TIMER_FAILURE_RESOURCES, + _TIMER_RESULT_MAX, + _TIMER_RESULT_INVALID = -1 +} TimerResult; + struct Timer { - Meta meta; + Unit meta; LIST_HEAD(TimerValue, values); usec_t next_elapse; TimerState state, deserialized_state; - Unit *unit; + UnitRef unit; Watch timer_watch; - bool failure; + TimerResult result; }; void timer_unit_notify(Unit *u, UnitActiveState new_state); @@ -80,4 +87,7 @@ TimerState timer_state_from_string(const char *s); const char *timer_base_to_string(TimerBase i); TimerBase timer_base_from_string(const char *s); +const char* timer_result_to_string(TimerResult i); +TimerResult timer_result_from_string(const char *s); + #endif diff --git a/src/tmpfiles.c b/src/tmpfiles.c index 3a1985a363..8cbce12dcd 100644 --- a/src/tmpfiles.c +++ b/src/tmpfiles.c @@ -50,29 +50,38 @@ * properly owned directories beneath /tmp, /var/tmp, /run, which are * volatile and hence need to be recreated on bootup. */ -enum { +typedef enum ItemType { /* These ones take file names */ CREATE_FILE = 'f', TRUNCATE_FILE = 'F', + WRITE_FILE = 'w', CREATE_DIRECTORY = 'd', TRUNCATE_DIRECTORY = 'D', CREATE_FIFO = 'p', + CREATE_SYMLINK = 'L', + CREATE_CHAR_DEVICE = 'c', + CREATE_BLOCK_DEVICE = 'b', /* These ones take globs */ IGNORE_PATH = 'x', REMOVE_PATH = 'r', - RECURSIVE_REMOVE_PATH = 'R' -}; + RECURSIVE_REMOVE_PATH = 'R', + RELABEL_PATH = 'z', + RECURSIVE_RELABEL_PATH = 'Z' +} ItemType; typedef struct Item { - char type; + ItemType type; char *path; + char *argument; uid_t uid; gid_t gid; mode_t mode; usec_t age; + dev_t major_minor; + bool uid_set:1; bool gid_set:1; bool mode_set:1; @@ -90,8 +99,8 @@ static const char *arg_prefix = NULL; #define MAX_DEPTH 256 -static bool needs_glob(int t) { - return t == IGNORE_PATH || t == REMOVE_PATH || t == RECURSIVE_REMOVE_PATH; +static bool needs_glob(ItemType t) { + return t == IGNORE_PATH || t == REMOVE_PATH || t == RECURSIVE_REMOVE_PATH || t == RELABEL_PATH || t == RECURSIVE_RELABEL_PATH; } static struct Item* find_glob(Hashmap *h, const char *match) { @@ -115,41 +124,50 @@ static void load_unix_sockets(void) { /* We maintain a cache of the sockets we found in * /proc/net/unix to speed things up a little. */ - if (!(unix_sockets = set_new(string_hash_func, string_compare_func))) + unix_sockets = set_new(string_hash_func, string_compare_func); + if (!unix_sockets) return; - if (!(f = fopen("/proc/net/unix", "re"))) + f = fopen("/proc/net/unix", "re"); + if (!f) return; - if (!(fgets(line, sizeof(line), f))) + /* Skip header */ + if (!fgets(line, sizeof(line), f)) goto fail; for (;;) { char *p, *s; int k; - if (!(fgets(line, sizeof(line), f))) + if (!fgets(line, sizeof(line), f)) break; truncate_nl(line); - if (strlen(line) < 53) + p = strchr(line, ':'); + if (!p) + continue; + + if (strlen(p) < 37) continue; - p = line + 53; + p += 37; p += strspn(p, WHITESPACE); - p += strcspn(p, WHITESPACE); + p += strcspn(p, WHITESPACE); /* skip one more word */ p += strspn(p, WHITESPACE); if (*p != '/') continue; - if (!(s = strdup(p))) + s = strdup(p); + if (!s) goto fail; path_kill_slashes(s); - if ((k = set_put(unix_sockets, s)) < 0) { + k = set_put(unix_sockets, s); + if (k < 0) { free(s); if (k != -EEXIST) @@ -157,6 +175,7 @@ static void load_unix_sockets(void) { } } + fclose(f); return; fail: @@ -404,8 +423,145 @@ finish: return r; } +static int item_set_perms(Item *i, const char *path) { + /* not using i->path directly because it may be a glob */ + if (i->mode_set) + if (chmod(path, i->mode) < 0) { + log_error("chmod(%s) failed: %m", path); + return -errno; + } + + if (i->uid_set || i->gid_set) + if (chown(path, + i->uid_set ? i->uid : (uid_t) -1, + i->gid_set ? i->gid : (gid_t) -1) < 0) { + + log_error("chown(%s) failed: %m", path); + return -errno; + } + + return label_fix(path, false); +} + +static int recursive_relabel_children(Item *i, const char *path) { + DIR *d; + int ret = 0; + + /* This returns the first error we run into, but nevertheless + * tries to go on */ + + d = opendir(path); + if (!d) + return errno == ENOENT ? 0 : -errno; + + for (;;) { + struct dirent buf, *de; + bool is_dir; + int r; + char *entry_path; + + r = readdir_r(d, &buf, &de); + if (r != 0) { + if (ret == 0) + ret = -r; + break; + } + + if (!de) + break; + + if (streq(de->d_name, ".") || streq(de->d_name, "..")) + continue; + + if (asprintf(&entry_path, "%s/%s", path, de->d_name) < 0) { + if (ret == 0) + ret = -ENOMEM; + continue; + } + + if (de->d_type == DT_UNKNOWN) { + struct stat st; + + if (lstat(entry_path, &st) < 0) { + if (ret == 0 && errno != ENOENT) + ret = -errno; + free(entry_path); + continue; + } + + is_dir = S_ISDIR(st.st_mode); + + } else + is_dir = de->d_type == DT_DIR; + + r = item_set_perms(i, entry_path); + if (r < 0) { + if (ret == 0 && r != -ENOENT) + ret = r; + free(entry_path); + continue; + } + + if (is_dir) { + r = recursive_relabel_children(i, entry_path); + if (r < 0 && ret == 0) + ret = r; + } + + free(entry_path); + } + + closedir(d); + + return ret; +} + +static int recursive_relabel(Item *i, const char *path) { + int r; + struct stat st; + + r = item_set_perms(i, path); + if (r < 0) + return r; + + if (lstat(path, &st) < 0) + return -errno; + + if (S_ISDIR(st.st_mode)) + r = recursive_relabel_children(i, path); + + return r; +} + +static int glob_item(Item *i, int (*action)(Item *, const char *)) { + int r = 0, k; + glob_t g; + char **fn; + + zero(g); + + errno = 0; + if ((k = glob(i->path, GLOB_NOSORT|GLOB_BRACE, NULL, &g)) != 0) { + + if (k != GLOB_NOMATCH) { + if (errno != 0) + errno = EIO; + + log_error("glob(%s) failed: %m", i->path); + return -errno; + } + } + + STRV_FOREACH(fn, g.gl_pathv) + if ((k = action(i, *fn)) < 0) + r = k; + + globfree(&g); + return r; +} + static int create_item(Item *i) { - int fd = -1, r; + int r; mode_t u; struct stat st; @@ -420,47 +576,65 @@ static int create_item(Item *i) { case CREATE_FILE: case TRUNCATE_FILE: + case WRITE_FILE: { + int fd, flags; + + flags = i->type == CREATE_FILE ? O_CREAT|O_APPEND : + i->type == TRUNCATE_FILE ? O_CREAT|O_TRUNC : 0; u = umask(0); - fd = open(i->path, O_CREAT|O_NDELAY|O_CLOEXEC|O_WRONLY|O_NOCTTY|O_NOFOLLOW| - (i->type == TRUNCATE_FILE ? O_TRUNC : 0), i->mode); + fd = open(i->path, flags|O_NDELAY|O_CLOEXEC|O_WRONLY|O_NOCTTY|O_NOFOLLOW, i->mode); umask(u); if (fd < 0) { + if (i->type == WRITE_FILE && errno == ENOENT) + break; + log_error("Failed to create file %s: %m", i->path); - r = -errno; - goto finish; + return -errno; + } + + if (i->argument) { + ssize_t n; + size_t l; + struct iovec iovec[2]; + static const char new_line = '\n'; + + l = strlen(i->argument); + + zero(iovec); + iovec[0].iov_base = i->argument; + iovec[0].iov_len = l; + + iovec[1].iov_base = (void*) &new_line; + iovec[1].iov_len = 1; + + n = writev(fd, iovec, 2); + if (n < 0 || (size_t) n != l+1) { + log_error("Failed to write file %s: %s", i->path, n < 0 ? strerror(-n) : "Short"); + close_nointr_nofail(fd); + return n < 0 ? n : -EIO; + } } - if (fstat(fd, &st) < 0) { + close_nointr_nofail(fd); + + if (stat(i->path, &st) < 0) { log_error("stat(%s) failed: %m", i->path); - r = -errno; - goto finish; + return -errno; } if (!S_ISREG(st.st_mode)) { log_error("%s is not a file.", i->path); - r = -EEXIST; - goto finish; + return -EEXIST; } - if (i->mode_set) - if (fchmod(fd, i->mode) < 0) { - log_error("chmod(%s) failed: %m", i->path); - r = -errno; - goto finish; - } - - if (i->uid_set || i->gid_set) - if (fchown(fd, - i->uid_set ? i->uid : (uid_t) -1, - i->gid_set ? i->gid : (gid_t) -1) < 0) { - log_error("chown(%s) failed: %m", i->path); - r = -errno; - goto finish; - } + r = item_set_perms(i, i->path); + if (r < 0) + return r; break; + } case TRUNCATE_DIRECTORY: case CREATE_DIRECTORY: @@ -472,38 +646,22 @@ static int create_item(Item *i) { if (r < 0 && errno != EEXIST) { log_error("Failed to create directory %s: %m", i->path); - r = -errno; - goto finish; + return -errno; } if (stat(i->path, &st) < 0) { log_error("stat(%s) failed: %m", i->path); - r = -errno; - goto finish; + return -errno; } if (!S_ISDIR(st.st_mode)) { log_error("%s is not a directory.", i->path); - r = -EEXIST; - goto finish; + return -EEXIST; } - if (i->mode_set) - if (chmod(i->path, i->mode) < 0) { - log_error("chmod(%s) failed: %m", i->path); - r = -errno; - goto finish; - } - - if (i->uid_set || i->gid_set) - if (chown(i->path, - i->uid_set ? i->uid : (uid_t) -1, - i->gid_set ? i->gid : (gid_t) -1) < 0) { - - log_error("chown(%s) failed: %m", i->path); - r = -errno; - goto finish; - } + r = item_set_perms(i, i->path); + if (r < 0) + return r; break; @@ -515,54 +673,99 @@ static int create_item(Item *i) { if (r < 0 && errno != EEXIST) { log_error("Failed to create fifo %s: %m", i->path); - r = -errno; - goto finish; + return -errno; } if (stat(i->path, &st) < 0) { log_error("stat(%s) failed: %m", i->path); - r = -errno; - goto finish; + return -errno; } if (!S_ISFIFO(st.st_mode)) { log_error("%s is not a fifo.", i->path); - r = -EEXIST; - goto finish; + return -EEXIST; } - if (i->mode_set) - if (chmod(i->path, i->mode) < 0) { - log_error("chmod(%s) failed: %m", i->path); - r = -errno; - goto finish; - } + r = item_set_perms(i, i->path); + if (r < 0) + return r; - if (i->uid_set || i->gid_set) - if (chown(i->path, - i->uid_set ? i->uid : (uid_t) -1, - i->gid_set ? i->gid : (gid_t) -1) < 0) { - log_error("chown(%s) failed: %m", i->path); - r = -errno; - goto finish; - } + break; + + case CREATE_SYMLINK: { + char *x; + r = symlink(i->argument, i->path); + if (r < 0 && errno != EEXIST) { + log_error("symlink(%s, %s) failed: %m", i->argument, i->path); + return -errno; + } + + r = readlink_malloc(i->path, &x); + if (r < 0) { + log_error("readlink(%s) failed: %s", i->path, strerror(-r)); + return -errno; + } + + if (!streq(i->argument, x)) { + free(x); + log_error("%s is not the right symlinks.", i->path); + return -EEXIST; + } + + free(x); break; } - if ((r = label_fix(i->path, false)) < 0) - goto finish; + case CREATE_BLOCK_DEVICE: + case CREATE_CHAR_DEVICE: { - log_debug("%s created successfully.", i->path); + u = umask(0); + r = mknod(i->path, i->mode | (i->type == CREATE_BLOCK_DEVICE ? S_IFBLK : S_IFCHR), i->major_minor); + umask(u); -finish: - if (fd >= 0) - close_nointr_nofail(fd); + if (r < 0 && errno != EEXIST) { + log_error("Failed to create device node %s: %m", i->path); + return -errno; + } - return r; + if (stat(i->path, &st) < 0) { + log_error("stat(%s) failed: %m", i->path); + return -errno; + } + + if (i->type == CREATE_BLOCK_DEVICE ? !S_ISBLK(st.st_mode) : !S_ISCHR(st.st_mode)) { + log_error("%s is not a device node.", i->path); + return -EEXIST; + } + + r = item_set_perms(i, i->path); + if (r < 0) + return r; + + break; + } + + case RELABEL_PATH: + + r = glob_item(i, item_set_perms); + if (r < 0) + return 0; + break; + + case RECURSIVE_RELABEL_PATH: + + r = glob_item(i, recursive_relabel); + if (r < 0) + return r; + } + + log_debug("%s created successfully.", i->path); + + return 0; } -static int remove_item(Item *i, const char *instance) { +static int remove_item_instance(Item *i, const char *instance) { int r; assert(i); @@ -573,7 +776,13 @@ static int remove_item(Item *i, const char *instance) { case TRUNCATE_FILE: case CREATE_DIRECTORY: case CREATE_FIFO: + case CREATE_SYMLINK: + case CREATE_BLOCK_DEVICE: + case CREATE_CHAR_DEVICE: case IGNORE_PATH: + case RELABEL_PATH: + case RECURSIVE_RELABEL_PATH: + case WRITE_FILE: break; case REMOVE_PATH: @@ -586,8 +795,8 @@ static int remove_item(Item *i, const char *instance) { case TRUNCATE_DIRECTORY: case RECURSIVE_REMOVE_PATH: - if ((r = rm_rf(instance, false, i->type == RECURSIVE_REMOVE_PATH)) < 0 && - r != -ENOENT) { + r = rm_rf(instance, false, i->type == RECURSIVE_REMOVE_PATH, false); + if (r < 0 && r != -ENOENT) { log_error("rm_rf(%s): %s", instance, strerror(-r)); return r; } @@ -598,7 +807,9 @@ static int remove_item(Item *i, const char *instance) { return 0; } -static int remove_item_glob(Item *i) { +static int remove_item(Item *i) { + int r = 0; + assert(i); switch (i->type) { @@ -607,40 +818,23 @@ static int remove_item_glob(Item *i) { case TRUNCATE_FILE: case CREATE_DIRECTORY: case CREATE_FIFO: + case CREATE_SYMLINK: + case CREATE_CHAR_DEVICE: + case CREATE_BLOCK_DEVICE: case IGNORE_PATH: + case RELABEL_PATH: + case RECURSIVE_RELABEL_PATH: + case WRITE_FILE: break; case REMOVE_PATH: case TRUNCATE_DIRECTORY: - case RECURSIVE_REMOVE_PATH: { - int r = 0, k; - glob_t g; - char **fn; - - zero(g); - - errno = 0; - if ((k = glob(i->path, GLOB_NOSORT|GLOB_BRACE, NULL, &g)) != 0) { - - if (k != GLOB_NOMATCH) { - if (errno != 0) - errno = EIO; - - log_error("glob(%s) failed: %m", i->path); - return -errno; - } - } - - STRV_FOREACH(fn, g.gl_pathv) - if ((k = remove_item(i, *fn)) < 0) - r = k; - - globfree(&g); - return r; - } + case RECURSIVE_REMOVE_PATH: + r = glob_item(i, remove_item_instance); + break; } - return 0; + return r; } static int process_item(Item *i) { @@ -649,7 +843,7 @@ static int process_item(Item *i) { assert(i); r = arg_create ? create_item(i) : 0; - q = arg_remove ? remove_item_glob(i) : 0; + q = arg_remove ? remove_item(i) : 0; p = arg_clean ? clean_item(i) : 0; if (r < 0) @@ -665,6 +859,7 @@ static void item_free(Item *i) { assert(i); free(i->path); + free(i->argument); free(i); } @@ -694,20 +889,34 @@ static bool item_equal(Item *a, Item *b) { (a->age_set && a->age != b->age)) return false; + if ((a->type == CREATE_FILE || + a->type == TRUNCATE_FILE || + a->type == WRITE_FILE || + a->type == CREATE_SYMLINK) && + !streq_ptr(a->argument, b->argument)) + return false; + + if ((a->type == CREATE_CHAR_DEVICE || + a->type == CREATE_BLOCK_DEVICE) && + a->major_minor != b->major_minor) + return false; + return true; } static int parse_line(const char *fname, unsigned line, const char *buffer) { Item *i, *existing; char *mode = NULL, *user = NULL, *group = NULL, *age = NULL; + char type; Hashmap *h; - int r; + int r, n = -1; assert(fname); assert(line >= 1); assert(buffer); - if (!(i = new0(Item, 1))) { + i = new0(Item, 1); + if (!i) { log_error("Out of memory"); return -ENOMEM; } @@ -718,31 +927,89 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) { "%ms " "%ms " "%ms " - "%ms", - &i->type, + "%ms " + "%n", + &type, &i->path, &mode, &user, &group, - &age) < 2) { + &age, + &n) < 2) { log_error("[%s:%u] Syntax error.", fname, line); r = -EIO; goto finish; } - if (i->type != CREATE_FILE && - i->type != TRUNCATE_FILE && - i->type != CREATE_DIRECTORY && - i->type != TRUNCATE_DIRECTORY && - i->type != CREATE_FIFO && - i->type != IGNORE_PATH && - i->type != REMOVE_PATH && - i->type != RECURSIVE_REMOVE_PATH) { - log_error("[%s:%u] Unknown file type '%c'.", fname, line, i->type); + if (n >= 0) { + n += strspn(buffer+n, WHITESPACE); + if (buffer[n] != 0 && (buffer[n] != '-' || buffer[n+1] != 0)) { + i->argument = unquote(buffer+n, "\""); + if (!i->argument) { + log_error("Out of memory"); + return -ENOMEM; + } + } + } + + switch(type) { + + case CREATE_FILE: + case TRUNCATE_FILE: + case CREATE_DIRECTORY: + case TRUNCATE_DIRECTORY: + case CREATE_FIFO: + case IGNORE_PATH: + case REMOVE_PATH: + case RECURSIVE_REMOVE_PATH: + case RELABEL_PATH: + case RECURSIVE_RELABEL_PATH: + break; + + case CREATE_SYMLINK: + if (!i->argument) { + log_error("[%s:%u] Symlink file requires argument.", fname, line); + r = -EBADMSG; + goto finish; + } + break; + + case WRITE_FILE: + if (!i->argument) { + log_error("[%s:%u] Write file requires argument.", fname, line); + r = -EBADMSG; + goto finish; + } + break; + + case CREATE_CHAR_DEVICE: + case CREATE_BLOCK_DEVICE: { + unsigned major, minor; + + if (!i->argument) { + log_error("[%s:%u] Device file requires argument.", fname, line); + r = -EBADMSG; + goto finish; + } + + if (sscanf(i->argument, "%u:%u", &major, &minor) != 2) { + log_error("[%s:%u] Can't parse device file major/minor '%s'.", fname, line, i->argument); + r = -EBADMSG; + goto finish; + } + + i->major_minor = makedev(major, minor); + break; + } + + default: + log_error("[%s:%u] Unknown file type '%c'.", fname, line, type); r = -EBADMSG; goto finish; } + i->type = type; + if (!path_is_absolute(i->path)) { log_error("[%s:%u] Path '%s' not absolute.", fname, line, i->path); r = -EBADMSG; @@ -792,7 +1059,9 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) { i->mode = m; i->mode_set = true; } else - i->mode = i->type == CREATE_DIRECTORY ? 0755 : 0644; + i->mode = + i->type == CREATE_DIRECTORY || + i->type == TRUNCATE_DIRECTORY ? 0755 : 0644; if (age && !streq(age, "-")) { if (parse_usec(age, &i->age) < 0) { @@ -806,7 +1075,8 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) { h = needs_glob(i->type) ? globs : items; - if ((existing = hashmap_get(h, i->path))) { + existing = hashmap_get(h, i->path); + if (existing) { /* Two identical items are fine */ if (!item_equal(existing, i)) @@ -816,7 +1086,8 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) { goto finish; } - if ((r = hashmap_put(h, i->path, i)) < 0) { + r = hashmap_put(h, i->path, i); + if (r < 0) { log_error("Failed to insert item %s: %s", i->path, strerror(-r)); goto finish; } @@ -921,7 +1192,8 @@ static int read_config_file(const char *fn, bool ignore_enoent) { assert(fn); - if (!(f = fopen(fn, "re"))) { + f = fopen(fn, "re"); + if (!f) { if (ignore_enoent && errno == ENOENT) return 0; @@ -965,13 +1237,16 @@ int main(int argc, char *argv[]) { Item *i; Iterator iterator; - if ((r = parse_argv(argc, argv)) <= 0) + r = parse_argv(argc, argv); + if (r <= 0) return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; log_set_target(LOG_TARGET_AUTO); log_parse_environment(); log_open(); + umask(0022); + label_init(); items = hashmap_new(string_hash_func, string_compare_func); diff --git a/src/tty-ask-password-agent.c b/src/tty-ask-password-agent.c index 02b959ea59..13481b29e9 100644 --- a/src/tty-ask-password-agent.c +++ b/src/tty-ask-password-agent.c @@ -206,6 +206,7 @@ static int ask_password_plymouth( continue; memcpy(&size, buffer+1, sizeof(size)); + size = le32toh(size); if (size+5 > sizeof(buffer)) { r = -EIO; goto finish; @@ -250,13 +251,13 @@ static int parse_password(const char *filename, char **wall) { int socket_fd = -1; bool accept_cached = false; - const ConfigItem items[] = { - { "Socket", config_parse_string, 0, &socket_name, "Ask" }, - { "NotAfter", config_parse_uint64, 0, ¬_after, "Ask" }, - { "Message", config_parse_string, 0, &message, "Ask" }, - { "PID", config_parse_unsigned, 0, &pid, "Ask" }, - { "AcceptCached", config_parse_bool, 0, &accept_cached, "Ask" }, - { NULL, NULL, 0, NULL, NULL } + const ConfigTableItem items[] = { + { "Ask", "Socket", config_parse_string, 0, &socket_name }, + { "Ask", "NotAfter", config_parse_uint64, 0, ¬_after }, + { "Ask", "Message", config_parse_string, 0, &message }, + { "Ask", "PID", config_parse_unsigned, 0, &pid }, + { "Ask", "AcceptCached", config_parse_bool, 0, &accept_cached }, + { NULL, NULL, NULL, 0, NULL } }; FILE *f; @@ -264,8 +265,8 @@ static int parse_password(const char *filename, char **wall) { assert(filename); - if (!(f = fopen(filename, "re"))) { - + f = fopen(filename, "re"); + if (!f) { if (errno == ENOENT) return 0; @@ -273,7 +274,8 @@ static int parse_password(const char *filename, char **wall) { return -errno; } - if ((r = config_parse(filename, f, NULL, items, true, NULL)) < 0) { + r = config_parse(filename, f, NULL, config_item_table_lookup, (void*) items, true, NULL); + if (r < 0) { log_error("Failed to parse password file %s: %s", filename, strerror(-r)); goto finish; } @@ -727,6 +729,8 @@ int main(int argc, char *argv[]) { log_parse_environment(); log_open(); + umask(0022); + if ((r = parse_argv(argc, argv)) <= 0) goto finish; diff --git a/src/umount.c b/src/umount.c index 67be42ea33..4e036d82a3 100644 --- a/src/umount.c +++ b/src/umount.c @@ -565,10 +565,13 @@ int umount_all(bool *changed) { /* retry umount, until nothing can be umounted anymore */ do { umount_changed = false; - r = mount_points_list_umount(&mp_list_head, &umount_changed, false); + + mount_points_list_umount(&mp_list_head, &umount_changed, false); if (umount_changed) *changed = true; - } while(umount_changed); + + } while (umount_changed); + /* umount one more time with logging enabled */ r = mount_points_list_umount(&mp_list_head, &umount_changed, true); if (r <= 0) diff --git a/src/unit-name.c b/src/unit-name.c index 6d45576f85..1cbb804561 100644 --- a/src/unit-name.c +++ b/src/unit-name.c @@ -167,8 +167,6 @@ char *unit_name_change_suffix(const char *n, const char *suffix) { } char *unit_name_build(const char *prefix, const char *instance, const char *suffix) { - char *r; - assert(prefix); assert(unit_prefix_is_valid(prefix)); assert(!instance || unit_instance_is_valid(instance)); @@ -177,10 +175,7 @@ char *unit_name_build(const char *prefix, const char *instance, const char *suff if (!instance) return strappend(prefix, suffix); - if (asprintf(&r, "%s@%s%s", prefix, instance, suffix) < 0) - return NULL; - - return r; + return join(prefix, "@", instance, suffix, NULL); } static char* do_escape(const char *f, char *t) { diff --git a/src/unit.c b/src/unit.c index d4142098d1..9e33701c8f 100644 --- a/src/unit.c +++ b/src/unit.c @@ -42,6 +42,7 @@ #include "special.h" #include "cgroup-util.h" #include "missing.h" +#include "cgroup-attr.h" const UnitVTable * const unit_vtable[_UNIT_TYPE_MAX] = { [UNIT_SERVICE] = &service_vtable, @@ -56,23 +57,27 @@ const UnitVTable * const unit_vtable[_UNIT_TYPE_MAX] = { [UNIT_PATH] = &path_vtable }; -Unit *unit_new(Manager *m) { +Unit *unit_new(Manager *m, size_t size) { Unit *u; assert(m); + assert(size >= sizeof(Unit)); - if (!(u = new0(Unit, 1))) + u = malloc0(size); + if (!u) return NULL; - if (!(u->meta.names = set_new(string_hash_func, string_compare_func))) { + u->names = set_new(string_hash_func, string_compare_func); + if (!u->names) { free(u); return NULL; } - u->meta.manager = m; - u->meta.type = _UNIT_TYPE_INVALID; - u->meta.deserialized_job = _JOB_TYPE_INVALID; - u->meta.default_dependencies = true; + u->manager = m; + u->type = _UNIT_TYPE_INVALID; + u->deserialized_job = _JOB_TYPE_INVALID; + u->default_dependencies = true; + u->unit_file_state = _UNIT_FILE_STATE_INVALID; return u; } @@ -81,7 +86,7 @@ bool unit_has_name(Unit *u, const char *name) { assert(u); assert(name); - return !!set_get(u->meta.names, (char*) name); + return !!set_get(u->names, (char*) name); } int unit_add_name(Unit *u, const char *text) { @@ -93,10 +98,10 @@ int unit_add_name(Unit *u, const char *text) { assert(text); if (unit_name_is_template(text)) { - if (!u->meta.instance) + if (!u->instance) return -EINVAL; - s = unit_name_replace_instance(text, u->meta.instance); + s = unit_name_replace_instance(text, u->instance); } else s = strdup(text); @@ -110,7 +115,7 @@ int unit_add_name(Unit *u, const char *text) { assert_se((t = unit_name_to_type(s)) >= 0); - if (u->meta.type != _UNIT_TYPE_INVALID && t != u->meta.type) { + if (u->type != _UNIT_TYPE_INVALID && t != u->type) { r = -EINVAL; goto fail; } @@ -125,41 +130,41 @@ int unit_add_name(Unit *u, const char *text) { /* Ensure that this unit is either instanced or not instanced, * but not both. */ - if (u->meta.type != _UNIT_TYPE_INVALID && !u->meta.instance != !i) { + if (u->type != _UNIT_TYPE_INVALID && !u->instance != !i) { r = -EINVAL; goto fail; } if (unit_vtable[t]->no_alias && - !set_isempty(u->meta.names) && - !set_get(u->meta.names, s)) { + !set_isempty(u->names) && + !set_get(u->names, s)) { r = -EEXIST; goto fail; } - if (hashmap_size(u->meta.manager->units) >= MANAGER_MAX_NAMES) { + if (hashmap_size(u->manager->units) >= MANAGER_MAX_NAMES) { r = -E2BIG; goto fail; } - if ((r = set_put(u->meta.names, s)) < 0) { + if ((r = set_put(u->names, s)) < 0) { if (r == -EEXIST) r = 0; goto fail; } - if ((r = hashmap_put(u->meta.manager->units, s, u)) < 0) { - set_remove(u->meta.names, s); + if ((r = hashmap_put(u->manager->units, s, u)) < 0) { + set_remove(u->names, s); goto fail; } - if (u->meta.type == _UNIT_TYPE_INVALID) { + if (u->type == _UNIT_TYPE_INVALID) { - u->meta.type = t; - u->meta.id = s; - u->meta.instance = i; + u->type = t; + u->id = s; + u->instance = i; - LIST_PREPEND(Meta, units_by_type, u->meta.manager->units_by_type[t], &u->meta); + LIST_PREPEND(Unit, units_by_type, u->manager->units_by_type[t], u); if (UNIT_VTABLE(u)->init) UNIT_VTABLE(u)->init(u); @@ -185,17 +190,17 @@ int unit_choose_id(Unit *u, const char *name) { if (unit_name_is_template(name)) { - if (!u->meta.instance) + if (!u->instance) return -EINVAL; - if (!(t = unit_name_replace_instance(name, u->meta.instance))) + if (!(t = unit_name_replace_instance(name, u->instance))) return -ENOMEM; name = t; } /* Selects one of the names of this unit as the id */ - s = set_get(u->meta.names, (char*) name); + s = set_get(u->names, (char*) name); free(t); if (!s) @@ -204,10 +209,10 @@ int unit_choose_id(Unit *u, const char *name) { if ((r = unit_name_to_instance(s, &i)) < 0) return r; - u->meta.id = s; + u->id = s; - free(u->meta.instance); - u->meta.instance = i; + free(u->instance); + u->instance = i; unit_add_to_dbus_queue(u); @@ -222,8 +227,8 @@ int unit_set_description(Unit *u, const char *description) { if (!(s = strdup(description))) return -ENOMEM; - free(u->meta.description); - u->meta.description = s; + free(u->description); + u->description = s; unit_add_to_dbus_queue(u); return 0; @@ -232,16 +237,16 @@ int unit_set_description(Unit *u, const char *description) { bool unit_check_gc(Unit *u) { assert(u); - if (u->meta.load_state == UNIT_STUB) + if (u->load_state == UNIT_STUB) return true; if (UNIT_VTABLE(u)->no_gc) return true; - if (u->meta.no_gc) + if (u->no_gc) return true; - if (u->meta.job) + if (u->job) return true; if (unit_active_state(u) != UNIT_INACTIVE) @@ -256,58 +261,58 @@ bool unit_check_gc(Unit *u) { void unit_add_to_load_queue(Unit *u) { assert(u); - assert(u->meta.type != _UNIT_TYPE_INVALID); + assert(u->type != _UNIT_TYPE_INVALID); - if (u->meta.load_state != UNIT_STUB || u->meta.in_load_queue) + if (u->load_state != UNIT_STUB || u->in_load_queue) return; - LIST_PREPEND(Meta, load_queue, u->meta.manager->load_queue, &u->meta); - u->meta.in_load_queue = true; + LIST_PREPEND(Unit, load_queue, u->manager->load_queue, u); + u->in_load_queue = true; } void unit_add_to_cleanup_queue(Unit *u) { assert(u); - if (u->meta.in_cleanup_queue) + if (u->in_cleanup_queue) return; - LIST_PREPEND(Meta, cleanup_queue, u->meta.manager->cleanup_queue, &u->meta); - u->meta.in_cleanup_queue = true; + LIST_PREPEND(Unit, cleanup_queue, u->manager->cleanup_queue, u); + u->in_cleanup_queue = true; } void unit_add_to_gc_queue(Unit *u) { assert(u); - if (u->meta.in_gc_queue || u->meta.in_cleanup_queue) + if (u->in_gc_queue || u->in_cleanup_queue) return; if (unit_check_gc(u)) return; - LIST_PREPEND(Meta, gc_queue, u->meta.manager->gc_queue, &u->meta); - u->meta.in_gc_queue = true; + LIST_PREPEND(Unit, gc_queue, u->manager->gc_queue, u); + u->in_gc_queue = true; - u->meta.manager->n_in_gc_queue ++; + u->manager->n_in_gc_queue ++; - if (u->meta.manager->gc_queue_timestamp <= 0) - u->meta.manager->gc_queue_timestamp = now(CLOCK_MONOTONIC); + if (u->manager->gc_queue_timestamp <= 0) + u->manager->gc_queue_timestamp = now(CLOCK_MONOTONIC); } void unit_add_to_dbus_queue(Unit *u) { assert(u); - assert(u->meta.type != _UNIT_TYPE_INVALID); + assert(u->type != _UNIT_TYPE_INVALID); - if (u->meta.load_state == UNIT_STUB || u->meta.in_dbus_queue) + if (u->load_state == UNIT_STUB || u->in_dbus_queue) return; /* Shortcut things if nobody cares */ - if (!bus_has_subscriber(u->meta.manager)) { - u->meta.sent_dbus_new_signal = true; + if (!bus_has_subscriber(u->manager)) { + u->sent_dbus_new_signal = true; return; } - LIST_PREPEND(Meta, dbus_queue, u->meta.manager->dbus_unit_queue, &u->meta); - u->meta.in_dbus_queue = true; + LIST_PREPEND(Unit, dbus_queue, u->manager->dbus_unit_queue, u); + u->in_dbus_queue = true; } static void bidi_set_free(Unit *u, Set *s) { @@ -323,7 +328,7 @@ static void bidi_set_free(Unit *u, Set *s) { UnitDependency d; for (d = 0; d < _UNIT_DEPENDENCY_MAX; d++) - set_remove(other->meta.dependencies[d], u); + set_remove(other->dependencies[d], u); unit_add_to_gc_queue(other); } @@ -340,53 +345,57 @@ void unit_free(Unit *u) { bus_unit_send_removed_signal(u); - if (u->meta.load_state != UNIT_STUB) + if (u->load_state != UNIT_STUB) if (UNIT_VTABLE(u)->done) UNIT_VTABLE(u)->done(u); - SET_FOREACH(t, u->meta.names, i) - hashmap_remove_value(u->meta.manager->units, t, u); + SET_FOREACH(t, u->names, i) + hashmap_remove_value(u->manager->units, t, u); - if (u->meta.job) - job_free(u->meta.job); + if (u->job) + job_free(u->job); for (d = 0; d < _UNIT_DEPENDENCY_MAX; d++) - bidi_set_free(u, u->meta.dependencies[d]); + bidi_set_free(u, u->dependencies[d]); - if (u->meta.type != _UNIT_TYPE_INVALID) - LIST_REMOVE(Meta, units_by_type, u->meta.manager->units_by_type[u->meta.type], &u->meta); + if (u->type != _UNIT_TYPE_INVALID) + LIST_REMOVE(Unit, units_by_type, u->manager->units_by_type[u->type], u); - if (u->meta.in_load_queue) - LIST_REMOVE(Meta, load_queue, u->meta.manager->load_queue, &u->meta); + if (u->in_load_queue) + LIST_REMOVE(Unit, load_queue, u->manager->load_queue, u); - if (u->meta.in_dbus_queue) - LIST_REMOVE(Meta, dbus_queue, u->meta.manager->dbus_unit_queue, &u->meta); + if (u->in_dbus_queue) + LIST_REMOVE(Unit, dbus_queue, u->manager->dbus_unit_queue, u); - if (u->meta.in_cleanup_queue) - LIST_REMOVE(Meta, cleanup_queue, u->meta.manager->cleanup_queue, &u->meta); + if (u->in_cleanup_queue) + LIST_REMOVE(Unit, cleanup_queue, u->manager->cleanup_queue, u); - if (u->meta.in_gc_queue) { - LIST_REMOVE(Meta, gc_queue, u->meta.manager->gc_queue, &u->meta); - u->meta.manager->n_in_gc_queue--; + if (u->in_gc_queue) { + LIST_REMOVE(Unit, gc_queue, u->manager->gc_queue, u); + u->manager->n_in_gc_queue--; } - cgroup_bonding_free_list(u->meta.cgroup_bondings, u->meta.manager->n_reloading <= 0); + cgroup_bonding_free_list(u->cgroup_bondings, u->manager->n_reloading <= 0); + cgroup_attribute_free_list(u->cgroup_attributes); - free(u->meta.description); - free(u->meta.fragment_path); + free(u->description); + free(u->fragment_path); + free(u->instance); - set_free_free(u->meta.names); + set_free_free(u->names); - condition_free_list(u->meta.conditions); + condition_free_list(u->conditions); + + while (u->refs) + unit_ref_unset(u->refs); - free(u->meta.instance); free(u); } UnitActiveState unit_active_state(Unit *u) { assert(u); - if (u->meta.load_state == UNIT_MERGED) + if (u->load_state == UNIT_MERGED) return unit_active_state(unit_follow_merge(u)); /* After a reload it might happen that a unit is not correctly @@ -424,14 +433,14 @@ static void merge_names(Unit *u, Unit *other) { assert(u); assert(other); - complete_move(&u->meta.names, &other->meta.names); + complete_move(&u->names, &other->names); - set_free_free(other->meta.names); - other->meta.names = NULL; - other->meta.id = NULL; + set_free_free(other->names); + other->names = NULL; + other->id = NULL; - SET_FOREACH(t, u->meta.names, i) - assert_se(hashmap_replace(u->meta.manager->units, t, u) == 0); + SET_FOREACH(t, u->names, i) + assert_se(hashmap_replace(u->manager->units, t, u) == 0); } static void merge_dependencies(Unit *u, Unit *other, UnitDependency d) { @@ -444,23 +453,23 @@ static void merge_dependencies(Unit *u, Unit *other, UnitDependency d) { assert(d < _UNIT_DEPENDENCY_MAX); /* Fix backwards pointers */ - SET_FOREACH(back, other->meta.dependencies[d], i) { + SET_FOREACH(back, other->dependencies[d], i) { UnitDependency k; for (k = 0; k < _UNIT_DEPENDENCY_MAX; k++) - if ((r = set_remove_and_put(back->meta.dependencies[k], other, u)) < 0) { + if ((r = set_remove_and_put(back->dependencies[k], other, u)) < 0) { if (r == -EEXIST) - set_remove(back->meta.dependencies[k], other); + set_remove(back->dependencies[k], other); else assert(r == -ENOENT); } } - complete_move(&u->meta.dependencies[d], &other->meta.dependencies[d]); + complete_move(&u->dependencies[d], &other->dependencies[d]); - set_free(other->meta.dependencies[d]); - other->meta.dependencies[d] = NULL; + set_free(other->dependencies[d]); + other->dependencies[d] = NULL; } int unit_merge(Unit *u, Unit *other) { @@ -468,25 +477,25 @@ int unit_merge(Unit *u, Unit *other) { assert(u); assert(other); - assert(u->meta.manager == other->meta.manager); - assert(u->meta.type != _UNIT_TYPE_INVALID); + assert(u->manager == other->manager); + assert(u->type != _UNIT_TYPE_INVALID); other = unit_follow_merge(other); if (other == u) return 0; - if (u->meta.type != other->meta.type) + if (u->type != other->type) return -EINVAL; - if (!u->meta.instance != !other->meta.instance) + if (!u->instance != !other->instance) return -EINVAL; - if (other->meta.load_state != UNIT_STUB && - other->meta.load_state != UNIT_ERROR) + if (other->load_state != UNIT_STUB && + other->load_state != UNIT_ERROR) return -EEXIST; - if (other->meta.job) + if (other->job) return -EEXIST; if (!UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(other))) @@ -495,16 +504,20 @@ int unit_merge(Unit *u, Unit *other) { /* Merge names */ merge_names(u, other); + /* Redirect all references */ + while (other->refs) + unit_ref_set(other->refs, u); + /* Merge dependencies */ for (d = 0; d < _UNIT_DEPENDENCY_MAX; d++) merge_dependencies(u, other, d); - other->meta.load_state = UNIT_MERGED; - other->meta.merged_into = u; + other->load_state = UNIT_MERGED; + other->merged_into = u; /* If there is still some data attached to the other node, we * don't need it anymore, and can free it. */ - if (other->meta.load_state != UNIT_STUB) + if (other->load_state != UNIT_STUB) if (UNIT_VTABLE(other)->done) UNIT_VTABLE(other)->done(other); @@ -523,16 +536,16 @@ int unit_merge_by_name(Unit *u, const char *name) { assert(name); if (unit_name_is_template(name)) { - if (!u->meta.instance) + if (!u->instance) return -EINVAL; - if (!(s = unit_name_replace_instance(name, u->meta.instance))) + if (!(s = unit_name_replace_instance(name, u->instance))) return -ENOMEM; name = s; } - if (!(other = manager_get_unit(u->meta.manager, name))) + if (!(other = manager_get_unit(u->manager, name))) r = unit_add_name(u, name); else r = unit_merge(u, other); @@ -544,8 +557,8 @@ int unit_merge_by_name(Unit *u, const char *name) { Unit* unit_follow_merge(Unit *u) { assert(u); - while (u->meta.load_state == UNIT_MERGED) - assert_se(u = u->meta.merged_into); + while (u->load_state == UNIT_MERGED) + assert_se(u = u->merged_into); return u; } @@ -558,19 +571,23 @@ int unit_add_exec_dependencies(Unit *u, ExecContext *c) { if (c->std_output != EXEC_OUTPUT_KMSG && c->std_output != EXEC_OUTPUT_SYSLOG && + c->std_output != EXEC_OUTPUT_JOURNAL && c->std_output != EXEC_OUTPUT_KMSG_AND_CONSOLE && c->std_output != EXEC_OUTPUT_SYSLOG_AND_CONSOLE && + c->std_output != EXEC_OUTPUT_JOURNAL_AND_CONSOLE && c->std_error != EXEC_OUTPUT_KMSG && - c->std_error != EXEC_OUTPUT_SYSLOG_AND_CONSOLE && - c->std_error != EXEC_OUTPUT_KMSG && + c->std_error != EXEC_OUTPUT_SYSLOG && + c->std_error != EXEC_OUTPUT_JOURNAL && + c->std_error != EXEC_OUTPUT_KMSG_AND_CONSOLE && + c->std_error != EXEC_OUTPUT_JOURNAL_AND_CONSOLE && c->std_error != EXEC_OUTPUT_SYSLOG_AND_CONSOLE) return 0; /* If syslog or kernel logging is requested, make sure our own * logging daemon is run first. */ - if (u->meta.manager->running_as == MANAGER_SYSTEM) - if ((r = unit_add_two_dependencies_by_name(u, UNIT_REQUIRES, UNIT_AFTER, SPECIAL_LOGGER_SOCKET, NULL, true)) < 0) + if (u->manager->running_as == MANAGER_SYSTEM) + if ((r = unit_add_two_dependencies_by_name(u, UNIT_REQUIRES, UNIT_AFTER, SPECIAL_JOURNALD_SOCKET, NULL, true)) < 0) return r; return 0; @@ -579,10 +596,10 @@ int unit_add_exec_dependencies(Unit *u, ExecContext *c) { const char *unit_description(Unit *u) { assert(u); - if (u->meta.description) - return u->meta.description; + if (u->description) + return u->description; - return strna(u->meta.id); + return strna(u->id); } void unit_dump(Unit *u, FILE *f, const char *prefix) { @@ -591,7 +608,6 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) { Iterator i; char *p2; const char *prefix2; - CGroupBonding *b; char timestamp1[FORMAT_TIMESTAMP_MAX], timestamp2[FORMAT_TIMESTAMP_MAX], @@ -601,7 +617,7 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) { Unit *following; assert(u); - assert(u->meta.type >= 0); + assert(u->type >= 0); if (!prefix) prefix = ""; @@ -620,47 +636,50 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) { "%s\tInactive Enter Timestamp: %s\n" "%s\tGC Check Good: %s\n" "%s\tNeed Daemon Reload: %s\n", - prefix, u->meta.id, + prefix, u->id, prefix, unit_description(u), - prefix, strna(u->meta.instance), - prefix, unit_load_state_to_string(u->meta.load_state), + prefix, strna(u->instance), + prefix, unit_load_state_to_string(u->load_state), prefix, unit_active_state_to_string(unit_active_state(u)), - prefix, strna(format_timestamp(timestamp1, sizeof(timestamp1), u->meta.inactive_exit_timestamp.realtime)), - prefix, strna(format_timestamp(timestamp2, sizeof(timestamp2), u->meta.active_enter_timestamp.realtime)), - prefix, strna(format_timestamp(timestamp3, sizeof(timestamp3), u->meta.active_exit_timestamp.realtime)), - prefix, strna(format_timestamp(timestamp4, sizeof(timestamp4), u->meta.inactive_enter_timestamp.realtime)), + prefix, strna(format_timestamp(timestamp1, sizeof(timestamp1), u->inactive_exit_timestamp.realtime)), + prefix, strna(format_timestamp(timestamp2, sizeof(timestamp2), u->active_enter_timestamp.realtime)), + prefix, strna(format_timestamp(timestamp3, sizeof(timestamp3), u->active_exit_timestamp.realtime)), + prefix, strna(format_timestamp(timestamp4, sizeof(timestamp4), u->inactive_enter_timestamp.realtime)), prefix, yes_no(unit_check_gc(u)), prefix, yes_no(unit_need_daemon_reload(u))); - SET_FOREACH(t, u->meta.names, i) + SET_FOREACH(t, u->names, i) fprintf(f, "%s\tName: %s\n", prefix, t); if ((following = unit_following(u))) - fprintf(f, "%s\tFollowing: %s\n", prefix, following->meta.id); + fprintf(f, "%s\tFollowing: %s\n", prefix, following->id); - if (u->meta.fragment_path) - fprintf(f, "%s\tFragment Path: %s\n", prefix, u->meta.fragment_path); + if (u->fragment_path) + fprintf(f, "%s\tFragment Path: %s\n", prefix, u->fragment_path); - if (u->meta.job_timeout > 0) - fprintf(f, "%s\tJob Timeout: %s\n", prefix, format_timespan(timespan, sizeof(timespan), u->meta.job_timeout)); + if (u->job_timeout > 0) + fprintf(f, "%s\tJob Timeout: %s\n", prefix, format_timespan(timespan, sizeof(timespan), u->job_timeout)); - condition_dump_list(u->meta.conditions, f, prefix); + condition_dump_list(u->conditions, f, prefix); - if (dual_timestamp_is_set(&u->meta.condition_timestamp)) + if (dual_timestamp_is_set(&u->condition_timestamp)) fprintf(f, "%s\tCondition Timestamp: %s\n" "%s\tCondition Result: %s\n", - prefix, strna(format_timestamp(timestamp1, sizeof(timestamp1), u->meta.condition_timestamp.realtime)), - prefix, yes_no(u->meta.condition_result)); + prefix, strna(format_timestamp(timestamp1, sizeof(timestamp1), u->condition_timestamp.realtime)), + prefix, yes_no(u->condition_result)); for (d = 0; d < _UNIT_DEPENDENCY_MAX; d++) { Unit *other; - SET_FOREACH(other, u->meta.dependencies[d], i) - fprintf(f, "%s\t%s: %s\n", prefix, unit_dependency_to_string(d), other->meta.id); + SET_FOREACH(other, u->dependencies[d], i) + fprintf(f, "%s\t%s: %s\n", prefix, unit_dependency_to_string(d), other->id); } - if (u->meta.load_state == UNIT_LOADED) { + if (u->load_state == UNIT_LOADED) { + CGroupBonding *b; + CGroupAttribute *a; + fprintf(f, "%s\tStopWhenUnneeded: %s\n" "%s\tRefuseManualStart: %s\n" @@ -669,31 +688,43 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) { "%s\tOnFailureIsolate: %s\n" "%s\tIgnoreOnIsolate: %s\n" "%s\tIgnoreOnSnapshot: %s\n", - prefix, yes_no(u->meta.stop_when_unneeded), - prefix, yes_no(u->meta.refuse_manual_start), - prefix, yes_no(u->meta.refuse_manual_stop), - prefix, yes_no(u->meta.default_dependencies), - prefix, yes_no(u->meta.on_failure_isolate), - prefix, yes_no(u->meta.ignore_on_isolate), - prefix, yes_no(u->meta.ignore_on_snapshot)); - - LIST_FOREACH(by_unit, b, u->meta.cgroup_bondings) + prefix, yes_no(u->stop_when_unneeded), + prefix, yes_no(u->refuse_manual_start), + prefix, yes_no(u->refuse_manual_stop), + prefix, yes_no(u->default_dependencies), + prefix, yes_no(u->on_failure_isolate), + prefix, yes_no(u->ignore_on_isolate), + prefix, yes_no(u->ignore_on_snapshot)); + + LIST_FOREACH(by_unit, b, u->cgroup_bondings) fprintf(f, "%s\tControlGroup: %s:%s\n", prefix, b->controller, b->path); + LIST_FOREACH(by_unit, a, u->cgroup_attributes) { + char *v = NULL; + + if (a->map_callback) + a->map_callback(a->controller, a->name, a->value, &v); + + fprintf(f, "%s\tControlGroupAttribute: %s %s \"%s\"\n", + prefix, a->controller, a->name, v ? v : a->value); + + free(v); + } + if (UNIT_VTABLE(u)->dump) UNIT_VTABLE(u)->dump(u, f, prefix2); - } else if (u->meta.load_state == UNIT_MERGED) + } else if (u->load_state == UNIT_MERGED) fprintf(f, "%s\tMerged into: %s\n", - prefix, u->meta.merged_into->meta.id); - else if (u->meta.load_state == UNIT_ERROR) - fprintf(f, "%s\tLoad Error Code: %s\n", prefix, strerror(-u->meta.load_error)); + prefix, u->merged_into->id); + else if (u->load_state == UNIT_ERROR) + fprintf(f, "%s\tLoad Error Code: %s\n", prefix, strerror(-u->load_error)); - if (u->meta.job) - job_dump(u->meta.job, f, prefix2); + if (u->job) + job_dump(u->job, f, prefix2); free(p2); } @@ -708,7 +739,7 @@ int unit_load_fragment_and_dropin(Unit *u) { if ((r = unit_load_fragment(u)) < 0) return r; - if (u->meta.load_state == UNIT_STUB) + if (u->load_state == UNIT_STUB) return -ENOENT; /* Load drop-in directory data */ @@ -731,8 +762,8 @@ int unit_load_fragment_and_dropin_optional(Unit *u) { if ((r = unit_load_fragment(u)) < 0) return r; - if (u->meta.load_state == UNIT_STUB) - u->meta.load_state = UNIT_LOADED; + if (u->load_state == UNIT_STUB) + u->load_state = UNIT_LOADED; /* Load drop-in directory data */ if ((r = unit_load_dropin(unit_follow_merge(u))) < 0) @@ -745,23 +776,23 @@ int unit_add_default_target_dependency(Unit *u, Unit *target) { assert(u); assert(target); - if (target->meta.type != UNIT_TARGET) + if (target->type != UNIT_TARGET) return 0; /* 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) + if (u->load_state != UNIT_LOADED || + target->load_state != UNIT_LOADED) return 0; /* If either side wants no automatic dependencies, then let's * skip this */ - if (!u->meta.default_dependencies || - target->meta.default_dependencies) + if (!u->default_dependencies || + !target->default_dependencies) return 0; /* Don't create loops */ - if (set_get(target->meta.dependencies[UNIT_BEFORE], u)) + if (set_get(target->dependencies[UNIT_BEFORE], u)) return 0; return unit_add_dependency(target, UNIT_AFTER, u, true); @@ -783,7 +814,7 @@ static int unit_add_default_dependencies(Unit *u) { assert(u); for (k = 0; k < ELEMENTSOF(deps); k++) - SET_FOREACH(target, u->meta.dependencies[deps[k]], i) + SET_FOREACH(target, u->dependencies[deps[k]], i) if ((r = unit_add_default_target_dependency(u, target)) < 0) return r; @@ -795,42 +826,42 @@ int unit_load(Unit *u) { assert(u); - if (u->meta.in_load_queue) { - LIST_REMOVE(Meta, load_queue, u->meta.manager->load_queue, &u->meta); - u->meta.in_load_queue = false; + if (u->in_load_queue) { + LIST_REMOVE(Unit, load_queue, u->manager->load_queue, u); + u->in_load_queue = false; } - if (u->meta.type == _UNIT_TYPE_INVALID) + if (u->type == _UNIT_TYPE_INVALID) return -EINVAL; - if (u->meta.load_state != UNIT_STUB) + if (u->load_state != UNIT_STUB) return 0; if (UNIT_VTABLE(u)->load) if ((r = UNIT_VTABLE(u)->load(u)) < 0) goto fail; - if (u->meta.load_state == UNIT_STUB) { + if (u->load_state == UNIT_STUB) { r = -ENOENT; goto fail; } - if (u->meta.load_state == UNIT_LOADED && - u->meta.default_dependencies) + if (u->load_state == UNIT_LOADED && + u->default_dependencies) if ((r = unit_add_default_dependencies(u)) < 0) goto fail; - if (u->meta.on_failure_isolate && - set_size(u->meta.dependencies[UNIT_ON_FAILURE]) > 1) { + if (u->on_failure_isolate && + set_size(u->dependencies[UNIT_ON_FAILURE]) > 1) { log_error("More than one OnFailure= dependencies specified for %s but OnFailureIsolate= enabled. Refusing.", - u->meta.id); + u->id); r = -EINVAL; goto fail; } - assert((u->meta.load_state != UNIT_MERGED) == !u->meta.merged_into); + assert((u->load_state != UNIT_MERGED) == !u->merged_into); unit_add_to_dbus_queue(unit_follow_merge(u)); unit_add_to_gc_queue(u); @@ -838,11 +869,12 @@ int unit_load(Unit *u) { return 0; fail: - u->meta.load_state = UNIT_ERROR; - u->meta.load_error = r; + u->load_state = UNIT_ERROR; + u->load_error = r; unit_add_to_dbus_queue(u); + unit_add_to_gc_queue(u); - log_debug("Failed to load configuration for %s: %s", u->meta.id, strerror(-r)); + log_debug("Failed to load configuration for %s: %s", u->id, strerror(-r)); return r; } @@ -850,10 +882,10 @@ fail: bool unit_condition_test(Unit *u) { assert(u); - dual_timestamp_get(&u->meta.condition_timestamp); - u->meta.condition_result = condition_test_list(u->meta.conditions); + dual_timestamp_get(&u->condition_timestamp); + u->condition_result = condition_test_list(u->conditions); - return u->meta.condition_result; + return u->condition_result; } /* Errors: @@ -868,26 +900,30 @@ int unit_start(Unit *u) { assert(u); - if (u->meta.load_state != UNIT_LOADED) + if (u->load_state != UNIT_LOADED) return -EINVAL; - /* If this is already (being) started, then this will - * succeed. Note that this will even succeed if this unit is - * not startable by the user. This is relied on to detect when - * we need to wait for units and when waiting is finished. */ + /* If this is already started, then this will succeed. Note + * that this will even succeed if this unit is not startable + * by the user. This is relied on to detect when we need to + * wait for units and when waiting is finished. */ state = unit_active_state(u); if (UNIT_IS_ACTIVE_OR_RELOADING(state)) return -EALREADY; - /* If the conditions failed, don't do anything at all */ - if (!unit_condition_test(u)) { - log_debug("Starting of %s requested but condition failed. Ignoring.", u->meta.id); + /* If the conditions failed, don't do anything at all. If we + * already are activating this call might still be useful to + * speed up activation in case there is some hold-off time, + * but we don't want to recheck the condition in that case. */ + if (state != UNIT_ACTIVATING && + !unit_condition_test(u)) { + log_debug("Starting of %s requested but condition failed. Ignoring.", u->id); return -EALREADY; } /* Forward to the main object, if we aren't it. */ if ((following = unit_following(u))) { - log_debug("Redirecting start request from %s to %s.", u->meta.id, following->meta.id); + log_debug("Redirecting start request from %s to %s.", u->id, following->id); return unit_start(following); } @@ -903,7 +939,7 @@ int unit_start(Unit *u) { unit_add_to_dbus_queue(u); - unit_status_printf(u, "Starting %s...\n", unit_description(u)); + unit_status_printf(u, NULL, "Starting %s...", unit_description(u)); return UNIT_VTABLE(u)->start(u); } @@ -917,7 +953,7 @@ bool unit_can_isolate(Unit *u) { assert(u); return unit_can_start(u) && - u->meta.allow_isolate; + u->allow_isolate; } /* Errors: @@ -936,7 +972,7 @@ int unit_stop(Unit *u) { return -EALREADY; if ((following = unit_following(u))) { - log_debug("Redirecting stop request from %s to %s.", u->meta.id, following->meta.id); + log_debug("Redirecting stop request from %s to %s.", u->id, following->id); return unit_stop(following); } @@ -945,7 +981,7 @@ int unit_stop(Unit *u) { unit_add_to_dbus_queue(u); - unit_status_printf(u, "Stopping %s...\n", unit_description(u)); + unit_status_printf(u, NULL, "Stopping %s...", unit_description(u)); return UNIT_VTABLE(u)->stop(u); } @@ -960,7 +996,7 @@ int unit_reload(Unit *u) { assert(u); - if (u->meta.load_state != UNIT_LOADED) + if (u->load_state != UNIT_LOADED) return -EINVAL; if (!unit_can_reload(u)) @@ -974,7 +1010,7 @@ int unit_reload(Unit *u) { return -ENOEXEC; if ((following = unit_following(u))) { - log_debug("Redirecting reload request from %s to %s.", u->meta.id, following->meta.id); + log_debug("Redirecting reload request from %s to %s.", u->id, following->id); return unit_reload(following); } @@ -1003,32 +1039,32 @@ static void unit_check_unneeded(Unit *u) { /* If this service shall be shut down when unneeded then do * so. */ - if (!u->meta.stop_when_unneeded) + if (!u->stop_when_unneeded) return; if (!UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(u))) return; - SET_FOREACH(other, u->meta.dependencies[UNIT_REQUIRED_BY], i) - if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other))) + SET_FOREACH(other, u->dependencies[UNIT_REQUIRED_BY], i) + if (unit_pending_active(other)) return; - SET_FOREACH(other, u->meta.dependencies[UNIT_REQUIRED_BY_OVERRIDABLE], i) - if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other))) + SET_FOREACH(other, u->dependencies[UNIT_REQUIRED_BY_OVERRIDABLE], i) + if (unit_pending_active(other)) return; - SET_FOREACH(other, u->meta.dependencies[UNIT_WANTED_BY], i) - if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other))) + SET_FOREACH(other, u->dependencies[UNIT_WANTED_BY], i) + if (unit_pending_active(other)) return; - SET_FOREACH(other, u->meta.dependencies[UNIT_BOUND_BY], i) - if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other))) + SET_FOREACH(other, u->dependencies[UNIT_BOUND_BY], i) + if (unit_pending_active(other)) return; - log_info("Service %s is not needed anymore. Stopping.", u->meta.id); + log_info("Service %s is not needed anymore. Stopping.", u->id); /* Ok, nobody needs us anymore. Sniff. Then let's commit suicide */ - manager_add_job(u->meta.manager, JOB_STOP, u, JOB_FAIL, true, NULL, NULL); + manager_add_job(u->manager, JOB_STOP, u, JOB_FAIL, true, NULL, NULL); } static void retroactively_start_dependencies(Unit *u) { @@ -1038,38 +1074,38 @@ static void retroactively_start_dependencies(Unit *u) { assert(u); assert(UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(u))); - SET_FOREACH(other, u->meta.dependencies[UNIT_REQUIRES], i) - if (!set_get(u->meta.dependencies[UNIT_AFTER], other) && + SET_FOREACH(other, u->dependencies[UNIT_REQUIRES], i) + if (!set_get(u->dependencies[UNIT_AFTER], other) && !UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other))) - manager_add_job(u->meta.manager, JOB_START, other, JOB_REPLACE, true, NULL, NULL); + manager_add_job(u->manager, JOB_START, other, JOB_REPLACE, true, NULL, NULL); - SET_FOREACH(other, u->meta.dependencies[UNIT_BIND_TO], i) - if (!set_get(u->meta.dependencies[UNIT_AFTER], other) && + SET_FOREACH(other, u->dependencies[UNIT_BIND_TO], i) + if (!set_get(u->dependencies[UNIT_AFTER], other) && !UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other))) - manager_add_job(u->meta.manager, JOB_START, other, JOB_REPLACE, true, NULL, NULL); + manager_add_job(u->manager, JOB_START, other, JOB_REPLACE, true, NULL, NULL); - SET_FOREACH(other, u->meta.dependencies[UNIT_REQUIRES_OVERRIDABLE], i) - if (!set_get(u->meta.dependencies[UNIT_AFTER], other) && + SET_FOREACH(other, u->dependencies[UNIT_REQUIRES_OVERRIDABLE], i) + if (!set_get(u->dependencies[UNIT_AFTER], other) && !UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other))) - manager_add_job(u->meta.manager, JOB_START, other, JOB_FAIL, false, NULL, NULL); + manager_add_job(u->manager, JOB_START, other, JOB_FAIL, false, NULL, NULL); - SET_FOREACH(other, u->meta.dependencies[UNIT_REQUISITE], i) - if (!set_get(u->meta.dependencies[UNIT_AFTER], other) && + SET_FOREACH(other, u->dependencies[UNIT_REQUISITE], i) + if (!set_get(u->dependencies[UNIT_AFTER], other) && !UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other))) - manager_add_job(u->meta.manager, JOB_START, other, JOB_REPLACE, true, NULL, NULL); + manager_add_job(u->manager, JOB_START, other, JOB_REPLACE, true, NULL, NULL); - SET_FOREACH(other, u->meta.dependencies[UNIT_WANTS], i) - if (!set_get(u->meta.dependencies[UNIT_AFTER], other) && + SET_FOREACH(other, u->dependencies[UNIT_WANTS], i) + if (!set_get(u->dependencies[UNIT_AFTER], other) && !UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other))) - manager_add_job(u->meta.manager, JOB_START, other, JOB_FAIL, false, NULL, NULL); + manager_add_job(u->manager, JOB_START, other, JOB_FAIL, false, NULL, NULL); - SET_FOREACH(other, u->meta.dependencies[UNIT_CONFLICTS], i) + SET_FOREACH(other, u->dependencies[UNIT_CONFLICTS], i) if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other))) - manager_add_job(u->meta.manager, JOB_STOP, other, JOB_REPLACE, true, NULL, NULL); + manager_add_job(u->manager, JOB_STOP, other, JOB_REPLACE, true, NULL, NULL); - SET_FOREACH(other, u->meta.dependencies[UNIT_CONFLICTED_BY], i) + SET_FOREACH(other, u->dependencies[UNIT_CONFLICTED_BY], i) if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other))) - manager_add_job(u->meta.manager, JOB_STOP, other, JOB_REPLACE, true, NULL, NULL); + manager_add_job(u->manager, JOB_STOP, other, JOB_REPLACE, true, NULL, NULL); } static void retroactively_stop_dependencies(Unit *u) { @@ -1080,27 +1116,35 @@ static void retroactively_stop_dependencies(Unit *u) { assert(UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(u))); /* Pull down units which are bound to us recursively if enabled */ - SET_FOREACH(other, u->meta.dependencies[UNIT_BOUND_BY], i) + SET_FOREACH(other, u->dependencies[UNIT_BOUND_BY], i) if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other))) - manager_add_job(u->meta.manager, JOB_STOP, other, JOB_REPLACE, true, NULL, NULL); + manager_add_job(u->manager, JOB_STOP, other, JOB_REPLACE, true, NULL, NULL); +} + +static void check_unneeded_dependencies(Unit *u) { + Iterator i; + Unit *other; + + assert(u); + assert(UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(u))); /* Garbage collect services that might not be needed anymore, if enabled */ - SET_FOREACH(other, u->meta.dependencies[UNIT_REQUIRES], i) + SET_FOREACH(other, u->dependencies[UNIT_REQUIRES], i) if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other))) unit_check_unneeded(other); - SET_FOREACH(other, u->meta.dependencies[UNIT_REQUIRES_OVERRIDABLE], i) + SET_FOREACH(other, u->dependencies[UNIT_REQUIRES_OVERRIDABLE], i) if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other))) unit_check_unneeded(other); - SET_FOREACH(other, u->meta.dependencies[UNIT_WANTS], i) + SET_FOREACH(other, u->dependencies[UNIT_WANTS], i) if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other))) unit_check_unneeded(other); - SET_FOREACH(other, u->meta.dependencies[UNIT_REQUISITE], i) + SET_FOREACH(other, u->dependencies[UNIT_REQUISITE], i) if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other))) unit_check_unneeded(other); - SET_FOREACH(other, u->meta.dependencies[UNIT_REQUISITE_OVERRIDABLE], i) + SET_FOREACH(other, u->dependencies[UNIT_REQUISITE_OVERRIDABLE], i) if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other))) unit_check_unneeded(other); - SET_FOREACH(other, u->meta.dependencies[UNIT_BIND_TO], i) + SET_FOREACH(other, u->dependencies[UNIT_BIND_TO], i) if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other))) unit_check_unneeded(other); } @@ -1111,15 +1155,15 @@ void unit_trigger_on_failure(Unit *u) { assert(u); - if (set_size(u->meta.dependencies[UNIT_ON_FAILURE]) <= 0) + if (set_size(u->dependencies[UNIT_ON_FAILURE]) <= 0) return; - log_info("Triggering OnFailure= dependencies of %s.", u->meta.id); + log_info("Triggering OnFailure= dependencies of %s.", u->id); - SET_FOREACH(other, u->meta.dependencies[UNIT_ON_FAILURE], i) { + SET_FOREACH(other, u->dependencies[UNIT_ON_FAILURE], i) { int r; - if ((r = manager_add_job(u->meta.manager, JOB_START, other, u->meta.on_failure_isolate ? JOB_ISOLATE : JOB_REPLACE, true, NULL, NULL)) < 0) + if ((r = manager_add_job(u->manager, JOB_START, other, u->on_failure_isolate ? JOB_ISOLATE : JOB_REPLACE, true, NULL, NULL)) < 0) log_error("Failed to enqueue OnFailure= job: %s", strerror(-r)); } } @@ -1137,54 +1181,54 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su * behaviour here. For example: if a mount point is remounted * this function will be called too! */ - if (u->meta.manager->n_reloading <= 0) { + if (u->manager->n_reloading <= 0) { dual_timestamp ts; dual_timestamp_get(&ts); if (UNIT_IS_INACTIVE_OR_FAILED(os) && !UNIT_IS_INACTIVE_OR_FAILED(ns)) - u->meta.inactive_exit_timestamp = ts; + u->inactive_exit_timestamp = ts; else if (!UNIT_IS_INACTIVE_OR_FAILED(os) && UNIT_IS_INACTIVE_OR_FAILED(ns)) - u->meta.inactive_enter_timestamp = ts; + u->inactive_enter_timestamp = ts; if (!UNIT_IS_ACTIVE_OR_RELOADING(os) && UNIT_IS_ACTIVE_OR_RELOADING(ns)) - u->meta.active_enter_timestamp = ts; + u->active_enter_timestamp = ts; else if (UNIT_IS_ACTIVE_OR_RELOADING(os) && !UNIT_IS_ACTIVE_OR_RELOADING(ns)) - u->meta.active_exit_timestamp = ts; + u->active_exit_timestamp = ts; timer_unit_notify(u, ns); path_unit_notify(u, ns); } if (UNIT_IS_INACTIVE_OR_FAILED(ns)) - cgroup_bonding_trim_list(u->meta.cgroup_bondings, true); + cgroup_bonding_trim_list(u->cgroup_bondings, true); - if (u->meta.job) { + if (u->job) { unexpected = false; - if (u->meta.job->state == JOB_WAITING) + if (u->job->state == JOB_WAITING) /* So we reached a different state for this * job. Let's see if we can run it now if it * failed previously due to EAGAIN. */ - job_add_to_run_queue(u->meta.job); + job_add_to_run_queue(u->job); /* Let's check whether this state change constitutes a * finished job, or maybe contradicts a running job and * hence needs to invalidate jobs. */ - switch (u->meta.job->type) { + switch (u->job->type) { case JOB_START: case JOB_VERIFY_ACTIVE: if (UNIT_IS_ACTIVE_OR_RELOADING(ns)) - job_finish_and_invalidate(u->meta.job, JOB_DONE); - else if (u->meta.job->state == JOB_RUNNING && ns != UNIT_ACTIVATING) { + job_finish_and_invalidate(u->job, JOB_DONE); + else if (u->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_FAILED : JOB_DONE); + job_finish_and_invalidate(u->job, ns == UNIT_FAILED ? JOB_FAILED : JOB_DONE); } break; @@ -1192,14 +1236,14 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su case JOB_RELOAD: case JOB_RELOAD_OR_START: - if (u->meta.job->state == JOB_RUNNING) { + if (u->job->state == JOB_RUNNING) { if (ns == UNIT_ACTIVE) - job_finish_and_invalidate(u->meta.job, reload_success ? JOB_DONE : JOB_FAILED); + job_finish_and_invalidate(u->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_FAILED : JOB_DONE); + job_finish_and_invalidate(u->job, ns == UNIT_FAILED ? JOB_FAILED : JOB_DONE); } } @@ -1210,10 +1254,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, JOB_DONE); - else if (u->meta.job->state == JOB_RUNNING && ns != UNIT_DEACTIVATING) { + job_finish_and_invalidate(u->job, JOB_DONE); + else if (u->job->state == JOB_RUNNING && ns != UNIT_DEACTIVATING) { unexpected = true; - job_finish_and_invalidate(u->meta.job, JOB_FAILED); + job_finish_and_invalidate(u->job, JOB_FAILED); } break; @@ -1225,7 +1269,7 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su } else unexpected = true; - if (u->meta.manager->n_reloading <= 0) { + if (u->manager->n_reloading <= 0) { /* If this state change happened without being * requested by a job, then let's retroactively start @@ -1241,8 +1285,12 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su retroactively_stop_dependencies(u); } + /* stop unneeded units regardless if going down was expected or not */ + if (UNIT_IS_ACTIVE_OR_ACTIVATING(os) && UNIT_IS_INACTIVE_OR_DEACTIVATING(ns)) + check_unneeded_dependencies(u); + if (ns != os && ns == UNIT_FAILED) { - log_notice("Unit %s entered failed state.", u->meta.id); + log_notice("Unit %s entered failed state.", u->id); unit_trigger_on_failure(u); } } @@ -1254,46 +1302,46 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su /* The bus just might have become available, * hence try to connect to it, if we aren't * yet connected. */ - bus_init(u->meta.manager, true); + bus_init(u->manager, true); - if (u->meta.type == UNIT_SERVICE && + if (u->type == UNIT_SERVICE && !UNIT_IS_ACTIVE_OR_RELOADING(os) && - u->meta.manager->n_reloading <= 0) { + u->manager->n_reloading <= 0) { /* Write audit record if we have just finished starting up */ - manager_send_unit_audit(u->meta.manager, u, AUDIT_SERVICE_START, true); - u->meta.in_audit = true; + manager_send_unit_audit(u->manager, u, AUDIT_SERVICE_START, true); + u->in_audit = true; } if (!UNIT_IS_ACTIVE_OR_RELOADING(os)) - manager_send_unit_plymouth(u->meta.manager, u); + manager_send_unit_plymouth(u->manager, u); } else { /* We don't care about D-Bus here, since we'll get an * asynchronous notification for it anyway. */ - if (u->meta.type == UNIT_SERVICE && + if (u->type == UNIT_SERVICE && UNIT_IS_INACTIVE_OR_FAILED(ns) && !UNIT_IS_INACTIVE_OR_FAILED(os) && - u->meta.manager->n_reloading <= 0) { + u->manager->n_reloading <= 0) { /* Hmm, if there was no start record written * write it now, so that we always have a nice * pair */ - if (!u->meta.in_audit) { - manager_send_unit_audit(u->meta.manager, u, AUDIT_SERVICE_START, ns == UNIT_INACTIVE); + if (!u->in_audit) { + manager_send_unit_audit(u->manager, u, AUDIT_SERVICE_START, ns == UNIT_INACTIVE); if (ns == UNIT_INACTIVE) - manager_send_unit_audit(u->meta.manager, u, AUDIT_SERVICE_STOP, true); + manager_send_unit_audit(u->manager, u, AUDIT_SERVICE_STOP, true); } else /* Write audit record if we have just finished shutting down */ - manager_send_unit_audit(u->meta.manager, u, AUDIT_SERVICE_STOP, ns == UNIT_INACTIVE); + manager_send_unit_audit(u->manager, u, AUDIT_SERVICE_STOP, ns == UNIT_INACTIVE); - u->meta.in_audit = false; + u->in_audit = false; } } - manager_recheck_syslog(u->meta.manager); + manager_recheck_journal(u->manager); /* Maybe we finished startup and are now ready for being * stopped because unneeded? */ @@ -1315,7 +1363,7 @@ int unit_watch_fd(Unit *u, int fd, uint32_t events, Watch *w) { ev.data.ptr = w; ev.events = events; - if (epoll_ctl(u->meta.manager->epoll_fd, + if (epoll_ctl(u->manager->epoll_fd, w->type == WATCH_INVALID ? EPOLL_CTL_ADD : EPOLL_CTL_MOD, fd, &ev) < 0) @@ -1337,7 +1385,7 @@ void unit_unwatch_fd(Unit *u, Watch *w) { assert(w->type == WATCH_FD); assert(w->data.unit == u); - assert_se(epoll_ctl(u->meta.manager->epoll_fd, EPOLL_CTL_DEL, w->fd, NULL) >= 0); + assert_se(epoll_ctl(u->manager->epoll_fd, EPOLL_CTL_DEL, w->fd, NULL) >= 0); w->fd = -1; w->type = WATCH_INVALID; @@ -1351,14 +1399,14 @@ int unit_watch_pid(Unit *u, pid_t pid) { /* Watch a specific PID. We only support one unit watching * each PID for now. */ - return hashmap_put(u->meta.manager->watch_pids, LONG_TO_PTR(pid), u); + return hashmap_put(u->manager->watch_pids, LONG_TO_PTR(pid), u); } void unit_unwatch_pid(Unit *u, pid_t pid) { assert(u); assert(pid >= 1); - hashmap_remove_value(u->meta.manager->watch_pids, LONG_TO_PTR(pid), u); + hashmap_remove_value(u->manager->watch_pids, LONG_TO_PTR(pid), u); } int unit_watch_timer(Unit *u, usec_t delay, Watch *w) { @@ -1411,7 +1459,7 @@ int unit_watch_timer(Unit *u, usec_t delay, Watch *w) { ev.data.ptr = w; ev.events = EPOLLIN; - if (epoll_ctl(u->meta.manager->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0) + if (epoll_ctl(u->manager->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0) goto fail; } @@ -1439,7 +1487,7 @@ void unit_unwatch_timer(Unit *u, Watch *w) { assert(w->data.unit == u); assert(w->fd >= 0); - assert_se(epoll_ctl(u->meta.manager->epoll_fd, EPOLL_CTL_DEL, w->fd, NULL) >= 0); + assert_se(epoll_ctl(u->manager->epoll_fd, EPOLL_CTL_DEL, w->fd, NULL) >= 0); close_nointr_nofail(w->fd); w->fd = -1; @@ -1492,7 +1540,11 @@ int unit_add_dependency(Unit *u, UnitDependency d, Unit *other, bool add_referen [UNIT_AFTER] = UNIT_BEFORE, [UNIT_ON_FAILURE] = _UNIT_DEPENDENCY_INVALID, [UNIT_REFERENCES] = UNIT_REFERENCED_BY, - [UNIT_REFERENCED_BY] = UNIT_REFERENCES + [UNIT_REFERENCED_BY] = UNIT_REFERENCES, + [UNIT_TRIGGERS] = UNIT_TRIGGERED_BY, + [UNIT_TRIGGERED_BY] = UNIT_TRIGGERS, + [UNIT_PROPAGATE_RELOAD_TO] = UNIT_PROPAGATE_RELOAD_FROM, + [UNIT_PROPAGATE_RELOAD_FROM] = UNIT_PROPAGATE_RELOAD_TO }; int r, q = 0, v = 0, w = 0; @@ -1508,34 +1560,34 @@ int unit_add_dependency(Unit *u, UnitDependency d, Unit *other, bool add_referen if (u == other) return 0; - if ((r = set_ensure_allocated(&u->meta.dependencies[d], trivial_hash_func, trivial_compare_func)) < 0) + if ((r = set_ensure_allocated(&u->dependencies[d], trivial_hash_func, trivial_compare_func)) < 0) return r; if (inverse_table[d] != _UNIT_DEPENDENCY_INVALID) - if ((r = set_ensure_allocated(&other->meta.dependencies[inverse_table[d]], trivial_hash_func, trivial_compare_func)) < 0) + if ((r = set_ensure_allocated(&other->dependencies[inverse_table[d]], trivial_hash_func, trivial_compare_func)) < 0) return r; if (add_reference) - if ((r = set_ensure_allocated(&u->meta.dependencies[UNIT_REFERENCES], trivial_hash_func, trivial_compare_func)) < 0 || - (r = set_ensure_allocated(&other->meta.dependencies[UNIT_REFERENCED_BY], trivial_hash_func, trivial_compare_func)) < 0) + if ((r = set_ensure_allocated(&u->dependencies[UNIT_REFERENCES], trivial_hash_func, trivial_compare_func)) < 0 || + (r = set_ensure_allocated(&other->dependencies[UNIT_REFERENCED_BY], trivial_hash_func, trivial_compare_func)) < 0) return r; - if ((q = set_put(u->meta.dependencies[d], other)) < 0) + if ((q = set_put(u->dependencies[d], other)) < 0) return q; if (inverse_table[d] != _UNIT_DEPENDENCY_INVALID) - if ((v = set_put(other->meta.dependencies[inverse_table[d]], u)) < 0) { + if ((v = set_put(other->dependencies[inverse_table[d]], u)) < 0) { r = v; goto fail; } if (add_reference) { - if ((w = set_put(u->meta.dependencies[UNIT_REFERENCES], other)) < 0) { + if ((w = set_put(u->dependencies[UNIT_REFERENCES], other)) < 0) { r = w; goto fail; } - if ((r = set_put(other->meta.dependencies[UNIT_REFERENCED_BY], u)) < 0) + if ((r = set_put(other->dependencies[UNIT_REFERENCED_BY], u)) < 0) goto fail; } @@ -1544,13 +1596,13 @@ int unit_add_dependency(Unit *u, UnitDependency d, Unit *other, bool add_referen fail: if (q > 0) - set_remove(u->meta.dependencies[d], other); + set_remove(u->dependencies[d], other); if (v > 0) - set_remove(other->meta.dependencies[inverse_table[d]], u); + set_remove(other->dependencies[inverse_table[d]], u); if (w > 0) - set_remove(u->meta.dependencies[UNIT_REFERENCES], other); + set_remove(u->dependencies[UNIT_REFERENCES], other); return r; } @@ -1583,12 +1635,12 @@ static const char *resolve_template(Unit *u, const char *name, const char*path, return name; } - if (u->meta.instance) - s = unit_name_replace_instance(name, u->meta.instance); + if (u->instance) + s = unit_name_replace_instance(name, u->instance); else { char *i; - if (!(i = unit_name_to_prefix(u->meta.id))) + if (!(i = unit_name_to_prefix(u->id))) return NULL; s = unit_name_replace_instance(name, i); @@ -1613,7 +1665,7 @@ int unit_add_dependency_by_name(Unit *u, UnitDependency d, const char *name, con if (!(name = resolve_template(u, name, path, &s))) return -ENOMEM; - if ((r = manager_load_unit(u->meta.manager, name, path, NULL, &other)) < 0) + if ((r = manager_load_unit(u->manager, name, path, NULL, &other)) < 0) goto finish; r = unit_add_dependency(u, d, other, add_reference); @@ -1634,7 +1686,7 @@ int unit_add_two_dependencies_by_name(Unit *u, UnitDependency d, UnitDependency if (!(name = resolve_template(u, name, path, &s))) return -ENOMEM; - if ((r = manager_load_unit(u->meta.manager, name, path, NULL, &other)) < 0) + if ((r = manager_load_unit(u->manager, name, path, NULL, &other)) < 0) goto finish; r = unit_add_two_dependencies(u, d, e, other, add_reference); @@ -1655,7 +1707,7 @@ int unit_add_dependency_by_name_inverse(Unit *u, UnitDependency d, const char *n if (!(name = resolve_template(u, name, path, &s))) return -ENOMEM; - if ((r = manager_load_unit(u->meta.manager, name, path, NULL, &other)) < 0) + if ((r = manager_load_unit(u->manager, name, path, NULL, &other)) < 0) goto finish; r = unit_add_dependency(other, d, u, add_reference); @@ -1676,7 +1728,7 @@ int unit_add_two_dependencies_by_name_inverse(Unit *u, UnitDependency d, UnitDep if (!(name = resolve_template(u, name, path, &s))) return -ENOMEM; - if ((r = manager_load_unit(u->meta.manager, name, path, NULL, &other)) < 0) + if ((r = manager_load_unit(u->manager, name, path, NULL, &other)) < 0) goto finish; if ((r = unit_add_two_dependencies(other, d, e, u, add_reference)) < 0) @@ -1721,10 +1773,10 @@ char *unit_dbus_path(Unit *u) { assert(u); - if (!u->meta.id) + if (!u->id) return NULL; - if (!(e = bus_path_escape(u->meta.id))) + if (!(e = bus_path_escape(u->id))) return NULL; p = strappend("/org/freedesktop/systemd1/unit/", e); @@ -1754,16 +1806,16 @@ int unit_add_cgroup(Unit *u, CGroupBonding *b) { if (streq(b->controller, SYSTEMD_CGROUP_CONTROLLER)) { CGroupBonding *l; - l = hashmap_get(u->meta.manager->cgroup_bondings, b->path); + l = hashmap_get(u->manager->cgroup_bondings, b->path); LIST_PREPEND(CGroupBonding, by_path, l, b); - if ((r = hashmap_replace(u->meta.manager->cgroup_bondings, b->path, l)) < 0) { + if ((r = hashmap_replace(u->manager->cgroup_bondings, b->path, l)) < 0) { LIST_REMOVE(CGroupBonding, by_path, l, b); return r; } } - LIST_PREPEND(CGroupBonding, by_unit, u->meta.cgroup_bondings, b); + LIST_PREPEND(CGroupBonding, by_unit, u->cgroup_bondings, b); b->unit = u; return 0; @@ -1771,22 +1823,22 @@ int unit_add_cgroup(Unit *u, CGroupBonding *b) { static char *default_cgroup_path(Unit *u) { char *p; - int r; assert(u); - if (u->meta.instance) { + if (u->instance) { char *t; - if (!(t = unit_name_template(u->meta.id))) + t = unit_name_template(u->id); + if (!t) return NULL; - r = asprintf(&p, "%s/%s/%s", u->meta.manager->cgroup_hierarchy, t, u->meta.instance); + p = join(u->manager->cgroup_hierarchy, "/", t, "/", u->instance, NULL); free(t); } else - r = asprintf(&p, "%s/%s", u->meta.manager->cgroup_hierarchy, u->meta.id); + p = join(u->manager->cgroup_hierarchy, "/", u->id, NULL); - return r < 0 ? NULL : p; + return p; } int unit_add_cgroup_from_text(Unit *u, const char *name) { @@ -1818,7 +1870,7 @@ int unit_add_cgroup_from_text(Unit *u, const char *name) { return -ENOMEM; } - if (cgroup_bonding_find_list(u->meta.cgroup_bondings, controller)) { + if (cgroup_bonding_find_list(u->cgroup_bondings, controller)) { r = -EEXIST; goto fail; } @@ -1855,7 +1907,7 @@ static int unit_add_one_default_cgroup(Unit *u, const char *controller) { if (!controller) controller = SYSTEMD_CGROUP_CONTROLLER; - if (cgroup_bonding_find_list(u->meta.cgroup_bondings, controller)) + if (cgroup_bonding_find_list(u->cgroup_bondings, controller)) return 0; if (!(b = new0(CGroupBonding, 1))) @@ -1884,22 +1936,26 @@ fail: } int unit_add_default_cgroups(Unit *u) { + CGroupAttribute *a; char **c; int r; + assert(u); /* Adds in the default cgroups, if they weren't specified * otherwise. */ - if (!u->meta.manager->cgroup_hierarchy) + if (!u->manager->cgroup_hierarchy) return 0; if ((r = unit_add_one_default_cgroup(u, NULL)) < 0) return r; - STRV_FOREACH(c, u->meta.manager->default_controllers) - if ((r = unit_add_one_default_cgroup(u, *c)) < 0) - return r; + STRV_FOREACH(c, u->manager->default_controllers) + unit_add_one_default_cgroup(u, *c); + + LIST_FOREACH(by_unit, a, u->cgroup_attributes) + unit_add_one_default_cgroup(u, a->controller); return 0; } @@ -1907,7 +1963,70 @@ int unit_add_default_cgroups(Unit *u) { CGroupBonding* unit_get_default_cgroup(Unit *u) { assert(u); - return cgroup_bonding_find_list(u->meta.cgroup_bondings, SYSTEMD_CGROUP_CONTROLLER); + return cgroup_bonding_find_list(u->cgroup_bondings, SYSTEMD_CGROUP_CONTROLLER); +} + +int unit_add_cgroup_attribute(Unit *u, const char *controller, const char *name, const char *value, CGroupAttributeMapCallback map_callback) { + int r; + char *c = NULL; + CGroupAttribute *a; + + assert(u); + assert(name); + assert(value); + + if (!controller) { + const char *dot; + + dot = strchr(name, '.'); + if (!dot) + return -EINVAL; + + c = strndup(name, dot - name); + if (!c) + return -ENOMEM; + + controller = c; + } + + if (streq(controller, SYSTEMD_CGROUP_CONTROLLER)) { + r = -EINVAL; + goto finish; + } + + a = new0(CGroupAttribute, 1); + if (!a) { + r = -ENOMEM; + goto finish; + } + + if (c) { + a->controller = c; + c = NULL; + } else + a->controller = strdup(controller); + + a->name = strdup(name); + a->value = strdup(value); + + if (!a->controller || !a->name || !a->value) { + free(a->controller); + free(a->name); + free(a->value); + free(a); + + return -ENOMEM; + } + + a->map_callback = map_callback; + + LIST_PREPEND(CGroupAttribute, by_unit, u->cgroup_attributes, a); + + r = 0; + +finish: + free(c); + return r; } int unit_load_related_unit(Unit *u, const char *type, Unit **_found) { @@ -1918,12 +2037,12 @@ int unit_load_related_unit(Unit *u, const char *type, Unit **_found) { assert(type); assert(_found); - if (!(t = unit_name_change_suffix(u->meta.id, type))) + if (!(t = unit_name_change_suffix(u->id, type))) return -ENOMEM; assert(!unit_has_name(u, t)); - r = manager_load_unit(u->meta.manager, t, NULL, NULL, _found); + r = manager_load_unit(u->manager, t, NULL, NULL, _found); free(t); assert(r < 0 || *_found != u); @@ -1939,12 +2058,12 @@ int unit_get_related_unit(Unit *u, const char *type, Unit **_found) { assert(type); assert(_found); - if (!(t = unit_name_change_suffix(u->meta.id, type))) + if (!(t = unit_name_change_suffix(u->id, type))) return -ENOMEM; assert(!unit_has_name(u, t)); - found = manager_get_unit(u->meta.manager, t); + found = manager_get_unit(u->manager, t); free(t); if (!found) @@ -1958,14 +2077,14 @@ static char *specifier_prefix_and_instance(char specifier, void *data, void *use Unit *u = userdata; assert(u); - return unit_name_to_prefix_and_instance(u->meta.id); + return unit_name_to_prefix_and_instance(u->id); } static char *specifier_prefix(char specifier, void *data, void *userdata) { Unit *u = userdata; assert(u); - return unit_name_to_prefix(u->meta.id); + return unit_name_to_prefix(u->id); } static char *specifier_prefix_unescaped(char specifier, void *data, void *userdata) { @@ -1974,7 +2093,7 @@ static char *specifier_prefix_unescaped(char specifier, void *data, void *userda assert(u); - if (!(p = unit_name_to_prefix(u->meta.id))) + if (!(p = unit_name_to_prefix(u->id))) return NULL; r = unit_name_unescape(p); @@ -1987,8 +2106,8 @@ static char *specifier_instance_unescaped(char specifier, void *data, void *user Unit *u = userdata; assert(u); - if (u->meta.instance) - return unit_name_unescape(u->meta.instance); + if (u->instance) + return unit_name_unescape(u->instance); return strdup(""); } @@ -1997,10 +2116,10 @@ static char *specifier_filename(char specifier, void *data, void *userdata) { Unit *u = userdata; assert(u); - if (u->meta.instance) - return unit_name_path_unescape(u->meta.instance); + if (u->instance) + return unit_name_path_unescape(u->instance); - return unit_name_to_path(u->meta.instance); + return unit_name_to_path(u->instance); } static char *specifier_cgroup(char specifier, void *data, void *userdata) { @@ -2016,9 +2135,9 @@ static char *specifier_cgroup_root(char specifier, void *data, void *userdata) { assert(u); if (specifier == 'r') - return strdup(u->meta.manager->cgroup_hierarchy); + return strdup(u->manager->cgroup_hierarchy); - if (parent_of_path(u->meta.manager->cgroup_hierarchy, &p) < 0) + if (parent_of_path(u->manager->cgroup_hierarchy, &p) < 0) return strdup(""); if (streq(p, "/")) { @@ -2033,7 +2152,7 @@ static char *specifier_runtime(char specifier, void *data, void *userdata) { Unit *u = userdata; assert(u); - if (u->meta.manager->running_as == MANAGER_USER) { + if (u->manager->running_as == MANAGER_USER) { const char *e; e = getenv("XDG_RUNTIME_DIR"); @@ -2057,10 +2176,10 @@ char *unit_name_printf(Unit *u, const char* format) { */ const Specifier table[] = { - { 'n', specifier_string, u->meta.id }, + { 'n', specifier_string, u->id }, { 'N', specifier_prefix_and_instance, NULL }, { 'p', specifier_prefix, NULL }, - { 'i', specifier_string, u->meta.instance }, + { 'i', specifier_string, u->instance }, { 0, NULL, NULL } }; @@ -2082,11 +2201,11 @@ char *unit_full_printf(Unit *u, const char *format) { */ const Specifier table[] = { - { 'n', specifier_string, u->meta.id }, + { 'n', specifier_string, u->id }, { 'N', specifier_prefix_and_instance, NULL }, { 'p', specifier_prefix, NULL }, { 'P', specifier_prefix_unescaped, NULL }, - { 'i', specifier_string, u->meta.instance }, + { 'i', specifier_string, u->instance }, { 'I', specifier_instance_unescaped, NULL }, { 'f', specifier_filename, NULL }, { 'c', specifier_cgroup, NULL }, @@ -2137,14 +2256,14 @@ int unit_watch_bus_name(Unit *u, const char *name) { /* Watch a specific name on the bus. We only support one unit * watching each name for now. */ - return hashmap_put(u->meta.manager->watch_bus, name, u); + return hashmap_put(u->manager->watch_bus, name, u); } void unit_unwatch_bus_name(Unit *u, const char *name) { assert(u); assert(name); - hashmap_remove_value(u->meta.manager->watch_bus, name, u); + hashmap_remove_value(u->manager->watch_bus, name, u); } bool unit_can_serialize(Unit *u) { @@ -2166,17 +2285,17 @@ int unit_serialize(Unit *u, FILE *f, FDSet *fds) { if ((r = UNIT_VTABLE(u)->serialize(u, f, fds)) < 0) return r; - if (u->meta.job) - unit_serialize_item(u, f, "job", job_type_to_string(u->meta.job->type)); + if (u->job) + unit_serialize_item(u, f, "job", job_type_to_string(u->job->type)); - dual_timestamp_serialize(f, "inactive-exit-timestamp", &u->meta.inactive_exit_timestamp); - dual_timestamp_serialize(f, "active-enter-timestamp", &u->meta.active_enter_timestamp); - dual_timestamp_serialize(f, "active-exit-timestamp", &u->meta.active_exit_timestamp); - dual_timestamp_serialize(f, "inactive-enter-timestamp", &u->meta.inactive_enter_timestamp); - dual_timestamp_serialize(f, "condition-timestamp", &u->meta.condition_timestamp); + dual_timestamp_serialize(f, "inactive-exit-timestamp", &u->inactive_exit_timestamp); + dual_timestamp_serialize(f, "active-enter-timestamp", &u->active_enter_timestamp); + dual_timestamp_serialize(f, "active-exit-timestamp", &u->active_exit_timestamp); + dual_timestamp_serialize(f, "inactive-enter-timestamp", &u->inactive_enter_timestamp); + dual_timestamp_serialize(f, "condition-timestamp", &u->condition_timestamp); - if (dual_timestamp_is_set(&u->meta.condition_timestamp)) - unit_serialize_item(u, f, "condition-result", yes_no(u->meta.condition_result)); + if (dual_timestamp_is_set(&u->condition_timestamp)) + unit_serialize_item(u, f, "condition-result", yes_no(u->condition_result)); /* End marker */ fputc('\n', f); @@ -2251,23 +2370,23 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) { if ((type = job_type_from_string(v)) < 0) log_debug("Failed to parse job type value %s", v); else - u->meta.deserialized_job = type; + u->deserialized_job = type; continue; } else if (streq(l, "inactive-exit-timestamp")) { - dual_timestamp_deserialize(v, &u->meta.inactive_exit_timestamp); + dual_timestamp_deserialize(v, &u->inactive_exit_timestamp); continue; } else if (streq(l, "active-enter-timestamp")) { - dual_timestamp_deserialize(v, &u->meta.active_enter_timestamp); + dual_timestamp_deserialize(v, &u->active_enter_timestamp); continue; } else if (streq(l, "active-exit-timestamp")) { - dual_timestamp_deserialize(v, &u->meta.active_exit_timestamp); + dual_timestamp_deserialize(v, &u->active_exit_timestamp); continue; } else if (streq(l, "inactive-enter-timestamp")) { - dual_timestamp_deserialize(v, &u->meta.inactive_enter_timestamp); + dual_timestamp_deserialize(v, &u->inactive_enter_timestamp); continue; } else if (streq(l, "condition-timestamp")) { - dual_timestamp_deserialize(v, &u->meta.condition_timestamp); + dual_timestamp_deserialize(v, &u->condition_timestamp); continue; } else if (streq(l, "condition-result")) { int b; @@ -2275,7 +2394,7 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) { if ((b = parse_boolean(v)) < 0) log_debug("Failed to parse condition result value %s", v); else - u->meta.condition_result = b; + u->condition_result = b; continue; } @@ -2303,7 +2422,7 @@ int unit_add_node_link(Unit *u, const char *what, bool wants) { if (!(e = unit_name_build_escape(what+1, NULL, ".device"))) return -ENOMEM; - r = manager_load_unit(u->meta.manager, e, NULL, NULL, &device); + r = manager_load_unit(u->manager, e, NULL, NULL, &device); free(e); if (r < 0) @@ -2328,17 +2447,17 @@ int unit_coldplug(Unit *u) { if ((r = UNIT_VTABLE(u)->coldplug(u)) < 0) return r; - if (u->meta.deserialized_job >= 0) { - if ((r = manager_add_job(u->meta.manager, u->meta.deserialized_job, u, JOB_IGNORE_REQUIREMENTS, false, NULL, NULL)) < 0) + if (u->deserialized_job >= 0) { + if ((r = manager_add_job(u->manager, u->deserialized_job, u, JOB_IGNORE_REQUIREMENTS, false, NULL, NULL)) < 0) return r; - u->meta.deserialized_job = _JOB_TYPE_INVALID; + u->deserialized_job = _JOB_TYPE_INVALID; } return 0; } -void unit_status_printf(Unit *u, const char *format, ...) { +void unit_status_printf(Unit *u, const char *status, const char *format, ...) { va_list ap; assert(u); @@ -2347,36 +2466,30 @@ void unit_status_printf(Unit *u, const char *format, ...) { if (!UNIT_VTABLE(u)->show_status) return; - if (u->meta.manager->running_as != MANAGER_SYSTEM) + if (!manager_get_show_status(u->manager)) return; - /* If Plymouth is running make sure we show the status, so - * that there's something nice to see when people press Esc */ - - if (!u->meta.manager->show_status && !plymouth_running()) - return; - - if (!manager_is_booting_or_shutting_down(u->meta.manager)) + if (!manager_is_booting_or_shutting_down(u->manager)) return; va_start(ap, format); - status_vprintf(format, ap); + status_vprintf(status, true, format, ap); va_end(ap); } bool unit_need_daemon_reload(Unit *u) { assert(u); - if (u->meta.fragment_path) { + if (u->fragment_path) { struct stat st; zero(st); - if (stat(u->meta.fragment_path, &st) < 0) + if (stat(u->fragment_path, &st) < 0) /* What, cannot access this anymore? */ return true; - if (u->meta.fragment_mtime > 0 && - timespec_load(&st.st_mtim) != u->meta.fragment_mtime) + if (u->fragment_mtime > 0 && + timespec_load(&st.st_mtim) != u->fragment_mtime) return true; } @@ -2410,7 +2523,7 @@ bool unit_pending_inactive(Unit *u) { if (UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(u))) return true; - if (u->meta.job && u->meta.job->type == JOB_STOP) + if (u->job && u->job->type == JOB_STOP) return true; return false; @@ -2419,15 +2532,15 @@ bool unit_pending_inactive(Unit *u) { bool unit_pending_active(Unit *u) { assert(u); - /* Returns true if the unit is inactive or going down */ + /* Returns true if the unit is active or going up */ if (UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(u))) return true; - if (u->meta.job && - (u->meta.job->type == JOB_START || - u->meta.job->type == JOB_RELOAD_OR_START || - u->meta.job->type == JOB_RESTART)) + if (u->job && + (u->job->type == JOB_START || + u->job->type == JOB_RELOAD_OR_START || + u->job->type == JOB_RESTART)) return true; return false; @@ -2482,6 +2595,39 @@ int unit_following_set(Unit *u, Set **s) { return 0; } +UnitFileState unit_get_unit_file_state(Unit *u) { + assert(u); + + if (u->unit_file_state < 0 && u->fragment_path) + u->unit_file_state = unit_file_get_state( + u->manager->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER, + NULL, file_name_from_path(u->fragment_path)); + + return u->unit_file_state; +} + +Unit* unit_ref_set(UnitRef *ref, Unit *u) { + assert(ref); + assert(u); + + if (ref->unit) + unit_ref_unset(ref); + + ref->unit = u; + LIST_PREPEND(UnitRef, refs, u->refs, ref); + return u; +} + +void unit_ref_unset(UnitRef *ref) { + assert(ref); + + if (!ref->unit) + return; + + LIST_REMOVE(UnitRef, refs, ref->unit->refs, ref); + ref->unit = NULL; +} + static const char* const unit_load_state_table[_UNIT_LOAD_STATE_MAX] = { [UNIT_STUB] = "stub", [UNIT_LOADED] = "loaded", @@ -2520,7 +2666,11 @@ static const char* const unit_dependency_table[_UNIT_DEPENDENCY_MAX] = { [UNIT_AFTER] = "After", [UNIT_REFERENCES] = "References", [UNIT_REFERENCED_BY] = "ReferencedBy", - [UNIT_ON_FAILURE] = "OnFailure" + [UNIT_ON_FAILURE] = "OnFailure", + [UNIT_TRIGGERS] = "Triggers", + [UNIT_TRIGGERED_BY] = "TriggeredBy", + [UNIT_PROPAGATE_RELOAD_TO] = "PropagateReloadTo", + [UNIT_PROPAGATE_RELOAD_FROM] = "PropagateReloadFrom" }; DEFINE_STRING_TABLE_LOOKUP(unit_dependency, UnitDependency); diff --git a/src/unit.h b/src/unit.h index 79f15103ba..756f465da3 100644 --- a/src/unit.h +++ b/src/unit.h @@ -25,13 +25,13 @@ #include <stdbool.h> #include <stdlib.h> -typedef union Unit Unit; -typedef struct Meta Meta; +typedef struct Unit Unit; typedef struct UnitVTable UnitVTable; typedef enum UnitType UnitType; typedef enum UnitLoadState UnitLoadState; typedef enum UnitActiveState UnitActiveState; typedef enum UnitDependency UnitDependency; +typedef struct UnitRef UnitRef; #include "set.h" #include "util.h" @@ -39,6 +39,7 @@ typedef enum UnitDependency UnitDependency; #include "socket-util.h" #include "execute.h" #include "condition.h" +#include "install.h" enum UnitType { UNIT_SERVICE = 0, @@ -118,6 +119,14 @@ enum UnitDependency { /* On Failure */ UNIT_ON_FAILURE, + /* Triggers (i.e. a socket triggers a service) */ + UNIT_TRIGGERS, + UNIT_TRIGGERED_BY, + + /* Propagate reloads */ + UNIT_PROPAGATE_RELOAD_TO, + UNIT_PROPAGATE_RELOAD_FROM, + /* Reference information for GC logic */ UNIT_REFERENCES, /* Inverse of 'references' is 'referenced_by' */ UNIT_REFERENCED_BY, @@ -129,8 +138,9 @@ enum UnitDependency { #include "manager.h" #include "job.h" #include "cgroup.h" +#include "cgroup-attr.h" -struct Meta { +struct Unit { Manager *manager; UnitType type; @@ -154,6 +164,9 @@ struct Meta { usec_t job_timeout; + /* References to this */ + LIST_HEAD(UnitRef, refs); + /* Conditions to check */ LIST_HEAD(Condition, conditions); @@ -166,21 +179,22 @@ struct Meta { /* Counterparts in the cgroup filesystem */ CGroupBonding *cgroup_bondings; + CGroupAttribute *cgroup_attributes; /* Per type list */ - LIST_FIELDS(Meta, units_by_type); + LIST_FIELDS(Unit, units_by_type); /* Load queue */ - LIST_FIELDS(Meta, load_queue); + LIST_FIELDS(Unit, load_queue); /* D-Bus queue */ - LIST_FIELDS(Meta, dbus_queue); + LIST_FIELDS(Unit, dbus_queue); /* Cleanup queue */ - LIST_FIELDS(Meta, cleanup_queue); + LIST_FIELDS(Unit, cleanup_queue); /* GC queue */ - LIST_FIELDS(Meta, gc_queue); + LIST_FIELDS(Unit, gc_queue); /* Used during GC sweeps */ unsigned gc_marker; @@ -192,6 +206,9 @@ struct Meta { /* Error code when we didn't manage to load the unit (negative) */ int load_error; + /* Cached unit file state */ + UnitFileState unit_file_state; + /* Garbage collect us we nobody wants or requires us anymore */ bool stop_when_unneeded; @@ -231,6 +248,15 @@ struct Meta { bool in_audit:1; }; +struct UnitRef { + /* Keeps tracks of references to a unit. This is useful so + * that we can merge two units if necessary and correct all + * references to them */ + + Unit* unit; + LIST_FIELDS(UnitRef, refs); +}; + #include "service.h" #include "timer.h" #include "socket.h" @@ -242,23 +268,16 @@ struct Meta { #include "swap.h" #include "path.h" -union Unit { - Meta meta; - Service service; - Timer timer; - Socket socket; - Target target; - Device device; - Mount mount; - Automount automount; - Snapshot snapshot; - Swap swap; - Path path; -}; - struct UnitVTable { const char *suffix; + /* How much memory does an object of this unit type need */ + size_t object_size; + + /* Config file sections this unit type understands, separated + * by NUL chars */ + const char *sections; + /* This should reset all type-specific variables. This should * not allocate memory, and is called with zero-initialized * data. It should hence only initialize variables that need @@ -379,19 +398,19 @@ struct UnitVTable { extern const UnitVTable * const unit_vtable[_UNIT_TYPE_MAX]; -#define UNIT_VTABLE(u) unit_vtable[(u)->meta.type] +#define UNIT_VTABLE(u) unit_vtable[(u)->type] /* For casting a unit into the various unit types */ #define DEFINE_CAST(UPPERCASE, MixedCase) \ static inline MixedCase* UPPERCASE(Unit *u) { \ - if (_unlikely_(!u || u->meta.type != UNIT_##UPPERCASE)) \ + if (_unlikely_(!u || u->type != UNIT_##UPPERCASE)) \ return NULL; \ \ return (MixedCase*) u; \ } /* For casting the various unit types into a unit */ -#define UNIT(u) ((Unit*) (&(u)->meta)) +#define UNIT(u) (&(u)->meta) DEFINE_CAST(SOCKET, Socket); DEFINE_CAST(TIMER, Timer); @@ -404,7 +423,7 @@ DEFINE_CAST(SNAPSHOT, Snapshot); DEFINE_CAST(SWAP, Swap); DEFINE_CAST(PATH, Path); -Unit *unit_new(Manager *m); +Unit *unit_new(Manager *m, size_t size); void unit_free(Unit *u); int unit_add_name(Unit *u, const char *name); @@ -424,6 +443,7 @@ int unit_add_cgroup(Unit *u, CGroupBonding *b); int unit_add_cgroup_from_text(Unit *u, const char *name); int unit_add_default_cgroups(Unit *u); CGroupBonding* unit_get_default_cgroup(Unit *u); +int unit_add_cgroup_attribute(Unit *u, const char *controller, const char *name, const char *value, CGroupAttributeMapCallback map_callback); int unit_choose_id(Unit *u, const char *name); int unit_set_description(Unit *u, const char *description); @@ -501,7 +521,7 @@ int unit_add_node_link(Unit *u, const char *what, bool wants); int unit_coldplug(Unit *u); -void unit_status_printf(Unit *u, const char *format, ...); +void unit_status_printf(Unit *u, const char *status, const char *format, ...); bool unit_need_daemon_reload(Unit *u); @@ -523,6 +543,13 @@ void unit_trigger_on_failure(Unit *u); bool unit_condition_test(Unit *u); +UnitFileState unit_get_unit_file_state(Unit *u); + +Unit* unit_ref_set(UnitRef *ref, Unit *u); +void unit_ref_unset(UnitRef *ref); + +#define UNIT_DEREF(ref) ((ref).unit) + const char *unit_load_state_to_string(UnitLoadState i); UnitLoadState unit_load_state_from_string(const char *s); diff --git a/src/update-utmp.c b/src/update-utmp.c index b06f5a06cb..0d177d6164 100644 --- a/src/update-utmp.c +++ b/src/update-utmp.c @@ -284,7 +284,7 @@ static int on_shutdown(Context *c) { } #endif - if ((q = utmp_put_shutdown(0)) < 0) { + if ((q = utmp_put_shutdown()) < 0) { log_error("Failed to write utmp record: %s", strerror(-q)); r = q; } @@ -339,7 +339,7 @@ static int on_runlevel(Context *c) { } #endif - if ((q = utmp_put_runlevel(0, runlevel, previous)) < 0) { + if ((q = utmp_put_runlevel(runlevel, previous)) < 0) { log_error("Failed to write utmp record: %s", strerror(-q)); r = q; } @@ -369,12 +369,17 @@ int main(int argc, char *argv[]) { return EXIT_FAILURE; } - log_set_target(LOG_TARGET_SYSLOG_OR_KMSG); + log_set_target(LOG_TARGET_AUTO); log_parse_environment(); log_open(); + umask(0022); + #ifdef HAVE_AUDIT - if ((c.audit_fd = audit_open()) < 0) + if ((c.audit_fd = audit_open()) < 0 && + /* If the kernel lacks netlink or audit support, + * don't worry about it. */ + errno != EAFNOSUPPORT && errno != EPROTONOSUPPORT) log_error("Failed to connect to audit log: %m"); #endif diff --git a/src/util.c b/src/util.c index 45b578bd03..e9869ea4fa 100644 --- a/src/util.c +++ b/src/util.c @@ -55,6 +55,7 @@ #include <linux/rtc.h> #include <glob.h> #include <grp.h> +#include <sys/mman.h> #include "macro.h" #include "util.h" @@ -73,7 +74,7 @@ size_t page_size(void) { static __thread size_t pgsz = 0; long r; - if (pgsz) + if (_likely_(pgsz > 0)) return pgsz; assert_se((r = sysconf(_SC_PAGESIZE)) > 0); @@ -516,7 +517,7 @@ int get_parent_of_pid(pid_t pid, pid_t *_ppid) { return -errno; if (!(fgets(line, sizeof(line), f))) { - r = -errno; + r = feof(f) ? -EIO : -errno; fclose(f); return r; } @@ -561,7 +562,7 @@ int get_starttime_of_pid(pid_t pid, unsigned long long *st) { return -errno; if (!(fgets(line, sizeof(line), f))) { - r = -errno; + r = feof(f) ? -EIO : -errno; fclose(f); return r; } @@ -704,15 +705,22 @@ int read_one_line_file(const char *fn, char **line) { assert(fn); assert(line); - if (!(f = fopen(fn, "re"))) + f = fopen(fn, "re"); + if (!f) return -errno; - if (!(fgets(t, sizeof(t), f))) { - r = -errno; - goto finish; + if (!fgets(t, sizeof(t), f)) { + + if (ferror(f)) { + r = -errno; + goto finish; + } + + t[0] = 0; } - if (!(c = strdup(t))) { + c = strdup(t); + if (!c) { r = -ENOMEM; goto finish; } @@ -782,13 +790,7 @@ int read_full_file(const char *fn, char **contents, size_t *size) { } } - if (buf) - buf[l] = 0; - else if (!(buf = calloc(1, 1))) { - r = -errno; - goto finish; - } - + buf[l] = 0; *contents = buf; buf = NULL; @@ -809,7 +811,7 @@ int parse_env_file( const char *separator, ...) { int r = 0; - char *contents, *p; + char *contents = NULL, *p; assert(fname); assert(separator); @@ -999,46 +1001,51 @@ char *truncate_nl(char *s) { return s; } -int get_process_name(pid_t pid, char **name) { - char *p; +int get_process_comm(pid_t pid, char **name) { int r; - assert(pid >= 1); assert(name); - if (asprintf(&p, "/proc/%lu/comm", (unsigned long) pid) < 0) - return -ENOMEM; - - r = read_one_line_file(p, name); - free(p); + if (pid == 0) + r = read_one_line_file("/proc/self/comm", name); + else { + char *p; + if (asprintf(&p, "/proc/%lu/comm", (unsigned long) pid) < 0) + return -ENOMEM; - if (r < 0) - return r; + r = read_one_line_file(p, name); + free(p); + } - return 0; + return r; } -int get_process_cmdline(pid_t pid, size_t max_length, char **line) { - char *p, *r, *k; +int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char **line) { + char *r, *k; int c; bool space = false; size_t left; FILE *f; - assert(pid >= 1); assert(max_length > 0); assert(line); - if (asprintf(&p, "/proc/%lu/cmdline", (unsigned long) pid) < 0) - return -ENOMEM; + if (pid == 0) + f = fopen("/proc/self/cmdline", "re"); + else { + char *p; + if (asprintf(&p, "/proc/%lu/cmdline", (unsigned long) pid) < 0) + return -ENOMEM; - f = fopen(p, "re"); - free(p); + f = fopen(p, "re"); + free(p); + } if (!f) return -errno; - if (!(r = new(char, max_length))) { + r = new(char, max_length); + if (!r) { fclose(f); return -ENOMEM; } @@ -1082,13 +1089,17 @@ int get_process_cmdline(pid_t pid, size_t max_length, char **line) { free(r); - if ((h = get_process_name(pid, &t)) < 0) + if (!comm_fallback) + return -ENOENT; + + h = get_process_comm(pid, &t); + if (h < 0) return h; - h = asprintf(&r, "[%s]", t); + r = join("[", t, "]", NULL); free(t); - if (h < 0) + if (!r) return -ENOMEM; } @@ -1096,6 +1107,107 @@ int get_process_cmdline(pid_t pid, size_t max_length, char **line) { return 0; } +int is_kernel_thread(pid_t pid) { + char *p; + size_t count; + char c; + bool eof; + FILE *f; + + if (pid == 0) + return 0; + + if (asprintf(&p, "/proc/%lu/cmdline", (unsigned long) pid) < 0) + return -ENOMEM; + + f = fopen(p, "re"); + free(p); + + if (!f) + return -errno; + + count = fread(&c, 1, 1, f); + eof = feof(f); + fclose(f); + + /* Kernel threads have an empty cmdline */ + + if (count <= 0) + return eof ? 1 : -errno; + + return 0; +} + +int get_process_exe(pid_t pid, char **name) { + int r; + + assert(name); + + if (pid == 0) + r = readlink_malloc("/proc/self/exe", name); + else { + char *p; + if (asprintf(&p, "/proc/%lu/exe", (unsigned long) pid) < 0) + return -ENOMEM; + + r = readlink_malloc(p, name); + free(p); + } + + return r; +} + +int get_process_uid(pid_t pid, uid_t *uid) { + char *p; + FILE *f; + int r; + + assert(uid); + + if (pid == 0) + return getuid(); + + if (asprintf(&p, "/proc/%lu/status", (unsigned long) pid) < 0) + return -ENOMEM; + + f = fopen(p, "re"); + free(p); + + if (!f) + return -errno; + + while (!feof(f)) { + char line[LINE_MAX], *l; + + if (!fgets(line, sizeof(line), f)) { + if (feof(f)) + break; + + r = -errno; + goto finish; + } + + l = strstrip(line); + + if (startswith(l, "Uid:")) { + l += 4; + l += strspn(l, WHITESPACE); + + l[strcspn(l, WHITESPACE)] = 0; + + r = parse_uid(l, uid); + goto finish; + } + } + + r = -EIO; + +finish: + fclose(f); + + return r; +} + char *strnappend(const char *s, const char *suffix, size_t b) { size_t a; char *r; @@ -1266,8 +1378,6 @@ bool is_path(const char *p) { } char *path_make_absolute(const char *p, const char *prefix) { - char *r; - assert(p); /* Makes every item in the list an absolute path by prepending @@ -1276,10 +1386,7 @@ char *path_make_absolute(const char *p, const char *prefix) { if (path_is_absolute(p) || !prefix) return strdup(p); - if (asprintf(&r, "%s/%s", prefix, p) < 0) - return NULL; - - return r; + return join(prefix, "/", p, NULL); } char *path_make_absolute_cwd(const char *p) { @@ -1411,21 +1518,18 @@ int reset_all_signal_handlers(void) { } char *strstrip(char *s) { - char *e, *l = NULL; + char *e; /* Drops trailing whitespace. Modifies the string in * place. Returns pointer to first non-space character */ s += strspn(s, WHITESPACE); - for (e = s; *e; e++) - if (!strchr(WHITESPACE, *e)) - l = e; + for (e = strchr(s, 0); e > s; e --) + if (!strchr(WHITESPACE, e[-1])) + break; - if (l) - *(l+1) = 0; - else - *s = 0; + *e = 0; return s; } @@ -1447,6 +1551,19 @@ char *delete_chars(char *s, const char *bad) { return s; } +bool in_charset(const char *s, const char* charset) { + const char *i; + + assert(s); + assert(charset); + + for (i = s; *i; i++) + if (!strchr(charset, *i)) + return false; + + return true; +} + char *file_in_same_dir(const char *path, const char *filename) { char *e, *r; size_t k; @@ -2308,8 +2425,10 @@ int chvt(int vt) { 0 }; - if (ioctl(fd, TIOCLINUX, tiocl) < 0) - return -errno; + if (ioctl(fd, TIOCLINUX, tiocl) < 0) { + r = -errno; + goto fail; + } vt = tiocl[0] <= 0 ? 1 : tiocl[0]; } @@ -2317,11 +2436,12 @@ int chvt(int vt) { if (ioctl(fd, VT_ACTIVATE, vt) < 0) r = -errno; - close_nointr_nofail(r); +fail: + close_nointr_nofail(fd); return r; } -int read_one_char(FILE *f, char *ret, bool *need_nl) { +int read_one_char(FILE *f, char *ret, usec_t t, bool *need_nl) { struct termios old_termios, new_termios; char c; char line[LINE_MAX]; @@ -2339,6 +2459,13 @@ int read_one_char(FILE *f, char *ret, bool *need_nl) { if (tcsetattr(fileno(f), TCSADRAIN, &new_termios) >= 0) { size_t k; + if (t != (usec_t) -1) { + if (fd_wait_for_event(fileno(f), POLLIN, t) <= 0) { + tcsetattr(fileno(f), TCSADRAIN, &old_termios); + return -ETIMEDOUT; + } + } + k = fread(&c, 1, 1, f); tcsetattr(fileno(f), TCSADRAIN, &old_termios); @@ -2354,7 +2481,11 @@ int read_one_char(FILE *f, char *ret, bool *need_nl) { } } - if (!(fgets(line, sizeof(line), f))) + if (t != (usec_t) -1) + if (fd_wait_for_event(fileno(f), POLLIN, t) <= 0) + return -ETIMEDOUT; + + if (!fgets(line, sizeof(line), f)) return -EIO; truncate_nl(line); @@ -2385,18 +2516,19 @@ int ask(char *ret, const char *replies, const char *text, ...) { bool need_nl = true; if (on_tty) - fputs("\x1B[1m", stdout); + fputs(ANSI_HIGHLIGHT_ON, stdout); va_start(ap, text); vprintf(text, ap); va_end(ap); if (on_tty) - fputs("\x1B[0m", stdout); + fputs(ANSI_HIGHLIGHT_OFF, stdout); fflush(stdout); - if ((r = read_one_char(stdin, &c, &need_nl)) < 0) { + r = read_one_char(stdin, &c, (usec_t) -1, &need_nl); + if (r < 0) { if (r == -EBADMSG) { puts("Bad input, please try again."); @@ -2419,10 +2551,9 @@ int ask(char *ret, const char *replies, const char *text, ...) { } } -int reset_terminal_fd(int fd) { +int reset_terminal_fd(int fd, bool switch_to_text) { struct termios termios; int r = 0; - long arg; /* Set terminal to some sane defaults */ @@ -2435,9 +2566,12 @@ int reset_terminal_fd(int fd) { /* Disable exclusive mode, just in case */ ioctl(fd, TIOCNXCL); + /* Switch to text mode */ + if (switch_to_text) + ioctl(fd, KDSETMODE, KD_TEXT); + /* Enable console unicode mode */ - arg = K_UNICODE; - ioctl(fd, KDSKBMODE, &arg); + ioctl(fd, KDSKBMODE, K_UNICODE); if (tcgetattr(fd, &termios) < 0) { r = -errno; @@ -2488,7 +2622,7 @@ int reset_terminal(const char *name) { if (fd < 0) return fd; - r = reset_terminal_fd(fd); + r = reset_terminal_fd(fd, true); close_nointr_nofail(fd); return r; @@ -2643,7 +2777,7 @@ int acquire_terminal(const char *name, bool fail, bool force, bool ignore_tiocst ssize_t l; struct inotify_event *e; - if ((l = read(notify, &inotify_buffer, sizeof(inotify_buffer))) < 0) { + if ((l = read(notify, inotify_buffer, sizeof(inotify_buffer))) < 0) { if (errno == EINTR) continue; @@ -2682,7 +2816,8 @@ int acquire_terminal(const char *name, bool fail, bool force, bool ignore_tiocst if (notify >= 0) close_nointr_nofail(notify); - if ((r = reset_terminal_fd(fd)) < 0) + r = reset_terminal_fd(fd, true); + if (r < 0) log_warning("Failed to reset terminal: %s", strerror(-r)); return fd; @@ -2854,7 +2989,8 @@ ssize_t loop_write(int fd, const void *buf, size_t nbytes, bool do_poll) { while (nbytes > 0) { ssize_t k; - if ((k = write(fd, p, nbytes)) <= 0) { + k = write(fd, p, nbytes); + if (k <= 0) { if (k < 0 && errno == EINTR) continue; @@ -2890,19 +3026,25 @@ ssize_t loop_write(int fd, const void *buf, size_t nbytes, bool do_poll) { return n; } -int path_is_mount_point(const char *t) { +int path_is_mount_point(const char *t, bool allow_symlink) { struct stat a, b; char *parent; int r; - if (lstat(t, &a) < 0) { + if (allow_symlink) + r = stat(t, &a); + else + r = lstat(t, &a); + + if (r < 0) { if (errno == ENOENT) return 0; return -errno; } - if ((r = parent_of_path(t, &parent)) < 0) + r = parent_of_path(t, &parent); + if (r < 0) return r; r = lstat(parent, &b); @@ -2977,6 +3119,64 @@ int parse_usec(const char *t, usec_t *usec) { return 0; } +int parse_bytes(const char *t, off_t *bytes) { + static const struct { + const char *suffix; + off_t factor; + } table[] = { + { "B", 1 }, + { "K", 1024ULL }, + { "M", 1024ULL*1024ULL }, + { "G", 1024ULL*1024ULL*1024ULL }, + { "T", 1024ULL*1024ULL*1024ULL*1024ULL }, + { "P", 1024ULL*1024ULL*1024ULL*1024ULL*1024ULL }, + { "E", 1024ULL*1024ULL*1024ULL*1024ULL*1024ULL*1024ULL }, + { "", 1 }, + }; + + const char *p; + off_t r = 0; + + assert(t); + assert(bytes); + + p = t; + do { + long long l; + char *e; + unsigned i; + + errno = 0; + l = strtoll(p, &e, 10); + + if (errno != 0) + return -errno; + + if (l < 0) + return -ERANGE; + + if (e == p) + return -EINVAL; + + e += strspn(e, WHITESPACE); + + for (i = 0; i < ELEMENTSOF(table); i++) + if (startswith(e, table[i].suffix)) { + r += (off_t) l * table[i].factor; + p = e + strlen(table[i].suffix); + break; + } + + if (i >= ELEMENTSOF(table)) + return -EINVAL; + + } while (*p != 0); + + *bytes = r; + + return 0; +} + int make_stdio(int fd) { int r, s, t; @@ -3070,11 +3270,15 @@ fallback: void rename_process(const char name[8]) { assert(name); - prctl(PR_SET_NAME, name); + /* This is a like a poor man's setproctitle(). It changes the + * comm field, argv[0], and also the glibc's internally used + * name of the process. For the first one a limit of 16 chars + * applies, to the second one usually one of 10 (i.e. length + * of "/sbin/init"), to the third one one of 7 (i.e. length of + * "systemd"). If you pass a longer string it will be + * truncated */ - /* This is a like a poor man's setproctitle(). The string - * passed should fit in 7 chars (i.e. the length of - * "systemd") */ + prctl(PR_SET_NAME, name); if (program_invocation_name) strncpy(program_invocation_name, name, strlen(program_invocation_name)); @@ -3202,7 +3406,7 @@ int get_ctty_devnr(pid_t pid, dev_t *d) { return -errno; if (!fgets(line, sizeof(line), f)) { - k = -errno; + k = feof(f) ? -EIO : -errno; fclose(f); return k; } @@ -3293,7 +3497,7 @@ int get_ctty(pid_t pid, dev_t *_devnr, char **r) { return 0; } -static int rm_rf_children(int fd, bool only_dirs) { +static int rm_rf_children(int fd, bool only_dirs, bool honour_sticky) { DIR *d; int ret = 0; @@ -3310,7 +3514,7 @@ static int rm_rf_children(int fd, bool only_dirs) { for (;;) { struct dirent buf, *de; - bool is_dir; + bool is_dir, keep_around = false; int r; if ((r = readdir_r(d, &buf, &de)) != 0) { @@ -3334,9 +3538,30 @@ static int rm_rf_children(int fd, bool only_dirs) { continue; } + if (honour_sticky) + keep_around = + (st.st_uid == 0 || st.st_uid == getuid()) && + (st.st_mode & S_ISVTX); + is_dir = S_ISDIR(st.st_mode); - } else + + } else { + if (honour_sticky) { + struct stat st; + + if (fstatat(fd, de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) { + if (ret == 0 && errno != ENOENT) + ret = -errno; + continue; + } + + keep_around = + (st.st_uid == 0 || st.st_uid == getuid()) && + (st.st_mode & S_ISVTX); + } + is_dir = de->d_type == DT_DIR; + } if (is_dir) { int subdir_fd; @@ -3347,16 +3572,18 @@ static int rm_rf_children(int fd, bool only_dirs) { continue; } - if ((r = rm_rf_children(subdir_fd, only_dirs)) < 0) { + if ((r = rm_rf_children(subdir_fd, only_dirs, honour_sticky)) < 0) { if (ret == 0) ret = r; } - if (unlinkat(fd, de->d_name, AT_REMOVEDIR) < 0) { - if (ret == 0 && errno != ENOENT) - ret = -errno; - } - } else if (!only_dirs) { + if (!keep_around) + if (unlinkat(fd, de->d_name, AT_REMOVEDIR) < 0) { + if (ret == 0 && errno != ENOENT) + ret = -errno; + } + + } else if (!only_dirs && !keep_around) { if (unlinkat(fd, de->d_name, 0) < 0) { if (ret == 0 && errno != ENOENT) @@ -3370,7 +3597,7 @@ static int rm_rf_children(int fd, bool only_dirs) { return ret; } -int rm_rf(const char *path, bool only_dirs, bool delete_root) { +int rm_rf(const char *path, bool only_dirs, bool delete_root, bool honour_sticky) { int fd; int r; @@ -3388,13 +3615,18 @@ int rm_rf(const char *path, bool only_dirs, bool delete_root) { return 0; } - r = rm_rf_children(fd, only_dirs); + r = rm_rf_children(fd, only_dirs, honour_sticky); + + if (delete_root) { - if (delete_root) - if (rmdir(path) < 0) { + if (honour_sticky && file_is_priv_sticky(path) > 0) + return r; + + if (rmdir(path) < 0 && errno != ENOENT) { if (r == 0) r = -errno; } + } return r; } @@ -3406,10 +3638,28 @@ int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid) { * first change the access mode and only then hand out * ownership to avoid a window where access is too open. */ - if (chmod(path, mode) < 0) + if (mode != (mode_t) -1) + if (chmod(path, mode) < 0) + return -errno; + + if (uid != (uid_t) -1 || gid != (gid_t) -1) + if (chown(path, uid, gid) < 0) + return -errno; + + return 0; +} + +int fchmod_and_fchown(int fd, mode_t mode, uid_t uid, gid_t gid) { + assert(fd >= 0); + + /* Under the assumption that we are running privileged we + * first change the access mode and only then hand out + * ownership to avoid a window where access is too open. */ + + if (fchmod(fd, mode) < 0) return -errno; - if (chown(path, uid, gid) < 0) + if (fchown(fd, uid, gid) < 0) return -errno; return 0; @@ -3443,9 +3693,12 @@ cpu_set_t* cpu_set_malloc(unsigned *ncpus) { } } -void status_vprintf(const char *format, va_list ap) { - char *s = NULL; - int fd = -1; +void status_vprintf(const char *status, bool ellipse, const char *format, va_list ap) { + char *s = NULL, *spaces = NULL, *e; + int fd = -1, c; + size_t emax, sl, left; + struct iovec iovec[5]; + int n = 0; assert(format); @@ -3455,25 +3708,69 @@ void status_vprintf(const char *format, va_list ap) { if (vasprintf(&s, format, ap) < 0) goto finish; - if ((fd = open_terminal("/dev/console", O_WRONLY|O_NOCTTY|O_CLOEXEC)) < 0) + fd = open_terminal("/dev/console", O_WRONLY|O_NOCTTY|O_CLOEXEC); + if (fd < 0) goto finish; - write(fd, s, strlen(s)); + if (ellipse) { + c = fd_columns(fd); + if (c <= 0) + c = 80; + + if (status) { + sl = 2 + 6 + 1; /* " [" status "]" */ + emax = (size_t) c > sl ? c - sl - 1 : 0; + } else + emax = c - 1; + + e = ellipsize(s, emax, 75); + if (e) { + free(s); + s = e; + } + } + + zero(iovec); + IOVEC_SET_STRING(iovec[n++], s); + + if (ellipse) { + sl = strlen(s); + left = emax > sl ? emax - sl : 0; + if (left > 0) { + spaces = malloc(left); + if (spaces) { + memset(spaces, ' ', left); + iovec[n].iov_base = spaces; + iovec[n].iov_len = left; + n++; + } + } + } + + if (status) { + IOVEC_SET_STRING(iovec[n++], " ["); + IOVEC_SET_STRING(iovec[n++], status); + IOVEC_SET_STRING(iovec[n++], "]\n"); + } else + IOVEC_SET_STRING(iovec[n++], "\n"); + + writev(fd, iovec, n); finish: free(s); + free(spaces); if (fd >= 0) close_nointr_nofail(fd); } -void status_printf(const char *format, ...) { +void status_printf(const char *status, bool ellipse, const char *format, ...) { va_list ap; assert(format); va_start(ap, format); - status_vprintf(format, ap); + status_vprintf(status, ellipse, format, ap); va_end(ap); } @@ -3491,146 +3788,15 @@ void status_welcome(void) { log_warning("Failed to read /etc/os-release: %s", strerror(-r)); } -#if defined(TARGET_FEDORA) - if (!pretty_name) { - if ((r = read_one_line_file("/etc/system-release", &pretty_name)) < 0) { - - if (r != -ENOENT) - log_warning("Failed to read /etc/system-release: %s", strerror(-r)); - } - } - - if (!ansi_color && pretty_name) { - - /* This tries to mimic the color magic the old Red Hat sysinit - * script did. */ - - if (startswith(pretty_name, "Red Hat")) - const_color = "0;31"; /* Red for RHEL */ - else if (startswith(pretty_name, "Fedora")) - const_color = "0;34"; /* Blue for Fedora */ - } - -#elif defined(TARGET_SUSE) - - if (!pretty_name) { - if ((r = read_one_line_file("/etc/SuSE-release", &pretty_name)) < 0) { - - if (r != -ENOENT) - log_warning("Failed to read /etc/SuSE-release: %s", strerror(-r)); - } - } - - if (!ansi_color) - const_color = "0;32"; /* Green for openSUSE */ - -#elif defined(TARGET_GENTOO) - - if (!pretty_name) { - if ((r = read_one_line_file("/etc/gentoo-release", &pretty_name)) < 0) { - - if (r != -ENOENT) - log_warning("Failed to read /etc/gentoo-release: %s", strerror(-r)); - } - } - - if (!ansi_color) - const_color = "1;34"; /* Light Blue for Gentoo */ - -#elif defined(TARGET_ALTLINUX) - - if (!pretty_name) { - if ((r = read_one_line_file("/etc/altlinux-release", &pretty_name)) < 0) { - - if (r != -ENOENT) - log_warning("Failed to read /etc/altlinux-release: %s", strerror(-r)); - } - } - - if (!ansi_color) - const_color = "0;36"; /* Cyan for ALTLinux */ - - -#elif defined(TARGET_DEBIAN) - - if (!pretty_name) { - char *version; - - if ((r = read_one_line_file("/etc/debian_version", &version)) < 0) { - - if (r != -ENOENT) - log_warning("Failed to read /etc/debian_version: %s", strerror(-r)); - } else { - pretty_name = strappend("Debian ", version); - free(version); - - if (!pretty_name) - log_warning("Failed to allocate Debian version string."); - } - } - - if (!ansi_color) - const_color = "1;31"; /* Light Red for Debian */ - -#elif defined(TARGET_UBUNTU) - - if ((r = parse_env_file("/etc/lsb-release", NEWLINE, - "DISTRIB_DESCRIPTION", &pretty_name, - NULL)) < 0) { - - if (r != -ENOENT) - log_warning("Failed to read /etc/lsb-release: %s", strerror(-r)); - } - - if (!ansi_color) - const_color = "0;33"; /* Orange/Brown for Ubuntu */ - -#elif defined(TARGET_MANDRIVA) - - if (!pretty_name) { - char *s, *p; - - if ((r = read_one_line_file("/etc/mandriva-release", &s) < 0)) { - if (r != -ENOENT) - log_warning("Failed to read /etc/mandriva-release: %s", strerror(-r)); - } else { - p = strstr(s, " release "); - if (p) { - *p = '\0'; - p += 9; - p[strcspn(p, " ")] = '\0'; - - /* This corresponds to standard rc.sysinit */ - if (asprintf(&pretty_name, "%s\x1B[0;39m %s", s, p) > 0) - const_color = "1;36"; - else - log_warning("Failed to allocate Mandriva version string."); - } else - log_warning("Failed to parse /etc/mandriva-release"); - free(s); - } - } -#elif defined(TARGET_MEEGO) - - if (!pretty_name) { - if ((r = read_one_line_file("/etc/meego-release", &pretty_name)) < 0) { - - if (r != -ENOENT) - log_warning("Failed to read /etc/meego-release: %s", strerror(-r)); - } - } - - if (!ansi_color) - const_color = "1;35"; /* Bright Magenta for MeeGo */ -#endif - if (!pretty_name && !const_pretty) const_pretty = "Linux"; if (!ansi_color && !const_color) const_color = "1"; - status_printf("\nWelcome to \x1B[%sm%s\x1B[0m!\n\n", + status_printf(NULL, + false, + "\nWelcome to \x1B[%sm%s\x1B[0m!\n", const_color ? const_color : ansi_color, const_pretty ? const_pretty : pretty_name); @@ -3772,23 +3938,32 @@ char **replace_env_argv(char **argv, char **env) { return r; } -int columns(void) { +int fd_columns(int fd) { + struct winsize ws; + zero(ws); + + if (ioctl(fd, TIOCGWINSZ, &ws) < 0) + return -errno; + + if (ws.ws_col <= 0) + return -EIO; + + return ws.ws_col; +} + +unsigned columns(void) { static __thread int parsed_columns = 0; const char *e; - if (parsed_columns > 0) + if (_likely_(parsed_columns > 0)) return parsed_columns; - if ((e = getenv("COLUMNS"))) + e = getenv("COLUMNS"); + if (e) parsed_columns = atoi(e); - if (parsed_columns <= 0) { - struct winsize ws; - zero(ws); - - if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) >= 0) - parsed_columns = ws.ws_col; - } + if (parsed_columns <= 0) + parsed_columns = fd_columns(STDOUT_FILENO); if (parsed_columns <= 0) parsed_columns = 80; @@ -3796,6 +3971,39 @@ int columns(void) { return parsed_columns; } +int fd_lines(int fd) { + struct winsize ws; + zero(ws); + + if (ioctl(fd, TIOCGWINSZ, &ws) < 0) + return -errno; + + if (ws.ws_row <= 0) + return -EIO; + + return ws.ws_row; +} + +unsigned lines(void) { + static __thread int parsed_lines = 0; + const char *e; + + if (_likely_(parsed_lines > 0)) + return parsed_lines; + + e = getenv("LINES"); + if (e) + parsed_lines = atoi(e); + + if (parsed_lines <= 0) + parsed_lines = fd_lines(STDOUT_FILENO); + + if (parsed_lines <= 0) + parsed_lines = 25; + + return parsed_lines; +} + int running_in_chroot(void) { struct stat a, b; @@ -3815,38 +4023,41 @@ int running_in_chroot(void) { a.st_ino != b.st_ino; } -char *ellipsize(const char *s, unsigned length, unsigned percent) { - size_t l, x; +char *ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigned percent) { + size_t x; char *r; assert(s); assert(percent <= 100); - assert(length >= 3); + assert(new_length >= 3); - l = strlen(s); + if (old_length <= 3 || old_length <= new_length) + return strndup(s, old_length); - if (l <= 3 || l <= length) - return strdup(s); - - if (!(r = new0(char, length+1))) + r = new0(char, new_length+1); + if (!r) return r; - x = (length * percent) / 100; + x = (new_length * percent) / 100; - if (x > length - 3) - x = length - 3; + if (x > new_length - 3) + x = new_length - 3; memcpy(r, s, x); r[x] = '.'; r[x+1] = '.'; r[x+2] = '.'; memcpy(r + x + 3, - s + l - (length - x - 3), - length - x - 3); + s + old_length - (new_length - x - 3), + new_length - x - 3); return r; } +char *ellipsize(const char *s, size_t length, unsigned percent) { + return ellipsize_mem(s, strlen(s), length, percent); +} + int touch(const char *path) { int fd; @@ -3863,7 +4074,8 @@ char *unquote(const char *s, const char* quotes) { size_t l; assert(s); - if ((l = strlen(s)) < 2) + l = strlen(s); + if (l < 2) return strdup(s); if (strchr(quotes, s[0]) && s[l-1] == s[0]) @@ -4155,252 +4367,40 @@ int vtnr_from_tty(const char *tty) { return i; } -const char *default_term_for_tty(const char *tty) { +bool tty_is_vc_resolve(const char *tty) { char *active = NULL; - const char *term; + bool b; assert(tty); if (startswith(tty, "/dev/")) tty += 5; - /* Resolve where /dev/console is pointing when determining - * TERM */ + /* Resolve where /dev/console is pointing to */ if (streq(tty, "console")) if (read_one_line_file("/sys/class/tty/console/active", &active) >= 0) { /* If multiple log outputs are configured the * last one is what /dev/console points to */ - if ((tty = strrchr(active, ' '))) + tty = strrchr(active, ' '); + if (tty) tty++; else tty = active; } - term = tty_is_vc(tty) ? "TERM=linux" : "TERM=vt100"; + b = tty_is_vc(tty); free(active); - return term; -} - -/* 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... */ - - static const char *const dmi_vendors[] = { - "/sys/class/dmi/id/sys_vendor", - "/sys/class/dmi/id/board_vendor", - "/sys/class/dmi/id/bios_vendor" - }; - - 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"; - - 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"; - - uint32_t eax, ecx; - union { - uint32_t sig32[3]; - char text[13]; - } sig; - unsigned i; - const char *j, *k; - bool hypervisor; - - /* http://lwn.net/Articles/301888/ */ - zero(sig); - -#if defined (__i386__) -#define REG_a "eax" -#define REG_b "ebx" -#elif defined (__amd64__) -#define REG_a "rax" -#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" - " pop %%"REG_b" \n\t" - - : "=a" (eax), "=c" (ecx) - : "0" (eax) - ); - - hypervisor = !!(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; -} - -int detect_container(const char **id) { - FILE *f; - - /* Unfortunately many of these operations require root access - * in one way or another */ - - if (geteuid() != 0) - return -EPERM; - - if (running_in_chroot() > 0) { - - if (id) - *id = "chroot"; - - return 1; - } - - /* /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; - } - - if ((f = fopen("/proc/self/cgroup", "re"))) { - - for (;;) { - char line[LINE_MAX], *p; - - if (!fgets(line, sizeof(line), f)) - break; - - if (!(p = strchr(strstrip(line), ':'))) - continue; - - if (strncmp(p, ":ns:", 4)) - continue; - - if (!streq(p, ":ns:/")) { - fclose(f); - - if (id) - *id = "pidns"; - - return 1; - } - } - - fclose(f); - } - - return 0; + return b; } -/* Returns a short identifier for the various VM/container implementations */ -int detect_virtualization(const char **id) { - static __thread const char *cached_id = NULL; - const char *_id; - int r; - - if (cached_id) { - - if (cached_id == (const char*) -1) - return 0; - - if (id) - *id = cached_id; - - return 1; - } - - if ((r = detect_container(&_id)) != 0) - goto finish; - - r = detect_vm(&_id); - -finish: - if (r > 0) { - cached_id = _id; - - if (id) - *id = _id; - } else if (r == 0) - cached_id = (const char*) -1; +const char *default_term_for_tty(const char *tty) { + assert(tty); - return r; + return tty_is_vc_resolve(tty) ? "TERM=linux" : "TERM=vt100"; } -bool dirent_is_file(struct dirent *de) { +bool dirent_is_file(const struct dirent *de) { assert(de); if (ignore_file(de->d_name)) @@ -4414,6 +4414,15 @@ bool dirent_is_file(struct dirent *de) { return true; } +bool dirent_is_file_with_suffix(const struct dirent *de, const char *suffix) { + assert(de); + + if (!dirent_is_file(de)) + return false; + + return endswith(de->d_name, suffix); +} + void execute_directory(const char *directory, DIR *d, char *argv[]) { DIR *_d = NULL; struct dirent *de; @@ -4488,11 +4497,12 @@ void execute_directory(const char *directory, DIR *d, char *argv[]) { } while (!hashmap_isempty(pids)) { + pid_t pid = PTR_TO_UINT(hashmap_first_key(pids)); siginfo_t si; char *path; zero(si); - if (waitid(P_ALL, 0, &si, WEXITED) < 0) { + if (waitid(P_PID, pid, &si, WEXITED) < 0) { if (errno == EINTR) continue; @@ -4586,6 +4596,98 @@ void parse_syslog_priority(char **p, int *priority) { *p += k; } +void skip_syslog_pid(char **buf) { + char *p; + + assert(buf); + assert(*buf); + + p = *buf; + + if (*p != '[') + return; + + p++; + p += strspn(p, "0123456789"); + + if (*p != ']') + return; + + p++; + + *buf = p; +} + +void skip_syslog_date(char **buf) { + enum { + LETTER, + SPACE, + NUMBER, + SPACE_OR_NUMBER, + COLON + } sequence[] = { + LETTER, LETTER, LETTER, + SPACE, + SPACE_OR_NUMBER, NUMBER, + SPACE, + SPACE_OR_NUMBER, NUMBER, + COLON, + SPACE_OR_NUMBER, NUMBER, + COLON, + SPACE_OR_NUMBER, NUMBER, + SPACE + }; + + char *p; + unsigned i; + + assert(buf); + assert(*buf); + + p = *buf; + + for (i = 0; i < ELEMENTSOF(sequence); i++, p++) { + + if (!*p) + return; + + switch (sequence[i]) { + + case SPACE: + if (*p != ' ') + return; + break; + + case SPACE_OR_NUMBER: + if (*p == ' ') + break; + + /* fall through */ + + case NUMBER: + if (*p < '0' || *p > '9') + return; + + break; + + case LETTER: + if (!(*p >= 'A' && *p <= 'Z') && + !(*p >= 'a' && *p <= 'z')) + return; + + break; + + case COLON: + if (*p != ':') + return; + break; + + } + } + + *buf = p; +} + int have_effective_cap(int value) { cap_t cap; cap_flag_value_t fv; @@ -4674,6 +4776,24 @@ int pipe_eof(int fd) { return pollfd.revents & POLLHUP; } +int fd_wait_for_event(int fd, int event, usec_t t) { + struct pollfd pollfd; + int r; + + zero(pollfd); + pollfd.fd = fd; + pollfd.events = event; + + r = poll(&pollfd, 1, t == (usec_t) -1 ? -1 : (int) (t / USEC_PER_MSEC)); + if (r < 0) + return -errno; + + if (r == 0) + return 0; + + return pollfd.revents; +} + int fopen_temporary(const char *path, FILE **_f, char **_temp_path) { FILE *f; char *t; @@ -4805,21 +4925,6 @@ int vt_disallocate(const char *name) { return 0; } - -static int file_is_conf(const struct dirent *d, const char *suffix) { - assert(d); - - if (ignore_file(d->d_name)) - return 0; - - if (d->d_type != DT_REG && - d->d_type != DT_LNK && - d->d_type != DT_UNKNOWN) - return 0; - - return endswith(d->d_name, suffix); -} - static int files_add(Hashmap *h, const char *path, const char *suffix) { DIR *dir; struct dirent buffer, *de; @@ -4845,7 +4950,7 @@ static int files_add(Hashmap *h, const char *path, const char *suffix) { if (!de) break; - if (!file_is_conf(de, suffix)) + if (!dirent_is_file_with_suffix(de, suffix)) continue; if (asprintf(&p, "%s/%s", path, de->d_name) < 0) { @@ -5007,13 +5112,84 @@ int hwclock_reset_localtime_delta(void) { return 0; } +int rtc_open(int flags) { + int fd; + DIR *d; + + /* First, we try to make use of the /dev/rtc symlink. If that + * doesn't exist, we open the first RTC which has hctosys=1 + * set. If we don't find any we just take the first RTC that + * exists at all. */ + + fd = open("/dev/rtc", flags); + if (fd >= 0) + return fd; + + d = opendir("/sys/class/rtc"); + if (!d) + goto fallback; + + for (;;) { + char *p, *v; + struct dirent buf, *de; + int r; + + r = readdir_r(d, &buf, &de); + if (r != 0) + goto fallback; + + if (!de) + goto fallback; + + if (ignore_file(de->d_name)) + continue; + + p = join("/sys/class/rtc/", de->d_name, "/hctosys", NULL); + if (!p) { + closedir(d); + return -ENOMEM; + } + + r = read_one_line_file(p, &v); + free(p); + + if (r < 0) + continue; + + r = parse_boolean(v); + free(v); + + if (r <= 0) + continue; + + p = strappend("/dev/", de->d_name); + fd = open(p, flags); + free(p); + + if (fd >= 0) { + closedir(d); + return fd; + } + } + +fallback: + if (d) + closedir(d); + + fd = open("/dev/rtc0", flags); + if (fd < 0) + return -errno; + + return fd; +} + int hwclock_get_time(struct tm *tm) { int fd; int err = 0; assert(tm); - fd = open("/dev/rtc0", O_RDONLY|O_CLOEXEC); + fd = rtc_open(O_RDONLY|O_CLOEXEC); if (fd < 0) return -errno; @@ -5037,7 +5213,7 @@ int hwclock_set_time(const struct tm *tm) { assert(tm); - fd = open("/dev/rtc0", O_RDONLY|O_CLOEXEC); + fd = rtc_open(O_RDONLY|O_CLOEXEC); if (fd < 0) return -errno; @@ -5196,21 +5372,27 @@ int symlink_or_copy_atomic(const char *from, const char *to) { } int audit_session_from_pid(pid_t pid, uint32_t *id) { - char *p, *s; + char *s; uint32_t u; int r; - assert(pid >= 1); assert(id); if (have_effective_cap(CAP_AUDIT_CONTROL) <= 0) return -ENOENT; - if (asprintf(&p, "/proc/%lu/sessionid", (unsigned long) pid) < 0) - return -ENOMEM; + if (pid == 0) + r = read_one_line_file("/proc/self/sessionid", &s); + else { + char *p; + + if (asprintf(&p, "/proc/%lu/sessionid", (unsigned long) pid) < 0) + return -ENOMEM; + + r = read_one_line_file(p, &s); + free(p); + } - r = read_one_line_file(p, &s); - free(p); if (r < 0) return r; @@ -5227,6 +5409,51 @@ int audit_session_from_pid(pid_t pid, uint32_t *id) { return 0; } +int audit_loginuid_from_pid(pid_t pid, uid_t *uid) { + char *s; + uid_t u; + int r; + + assert(uid); + + /* Only use audit login uid if we are executed with sufficient + * capabilities so that pam_loginuid could do its job. If we + * are lacking the CAP_AUDIT_CONTROL capabality we most likely + * are being run in a container and /proc/self/loginuid is + * useless since it probably contains a uid of the host + * system. */ + + if (have_effective_cap(CAP_AUDIT_CONTROL) <= 0) + return -ENOENT; + + if (pid == 0) + r = read_one_line_file("/proc/self/loginuid", &s); + else { + char *p; + + if (asprintf(&p, "/proc/%lu/loginuid", (unsigned long) pid) < 0) + return -ENOMEM; + + r = read_one_line_file(p, &s); + free(p); + } + + if (r < 0) + return r; + + r = parse_uid(s, &u); + free(s); + + if (r < 0) + return r; + + if (u == (uid_t) -1) + return -ENOENT; + + *uid = (uid_t) u; + return 0; +} + bool display_is_local(const char *display) { assert(display); @@ -5432,9 +5659,15 @@ int get_files_in_directory(const char *path, char ***list) { char **l = NULL; assert(path); - assert(list); + + /* Returns all files in a directory in *list, and the number + * of files as return value. If list is NULL returns only the + * number */ d = opendir(path); + if (!d) + return -errno; + for (;;) { struct dirent buffer, *de; int k; @@ -5453,42 +5686,178 @@ int get_files_in_directory(const char *path, char ***list) { if (!dirent_is_file(de)) continue; - if ((unsigned) r >= n) { - char **t; + if (list) { + if ((unsigned) r >= n) { + char **t; - n = MAX(16, 2*r); - t = realloc(l, sizeof(char*) * n); - if (!t) { - r = -ENOMEM; - goto finish; - } + n = MAX(16, 2*r); + t = realloc(l, sizeof(char*) * n); + if (!t) { + r = -ENOMEM; + goto finish; + } - l = t; - } + l = t; + } - assert((unsigned) r < n); + assert((unsigned) r < n); - l[r] = strdup(de->d_name); - if (!l[r]) { - r = -ENOMEM; - goto finish; - } + l[r] = strdup(de->d_name); + if (!l[r]) { + r = -ENOMEM; + goto finish; + } - l[++r] = NULL; + l[++r] = NULL; + } else + r++; } finish: if (d) closedir(d); - if (r >= 0) - *list = l; - else + if (r >= 0) { + if (list) + *list = l; + } else strv_free(l); return r; } +char *join(const char *x, ...) { + va_list ap; + size_t l; + char *r, *p; + + va_start(ap, x); + + if (x) { + l = strlen(x); + + for (;;) { + const char *t; + + t = va_arg(ap, const char *); + if (!t) + break; + + l += strlen(t); + } + } else + l = 0; + + va_end(ap); + + r = new(char, l+1); + if (!r) + return NULL; + + if (x) { + p = stpcpy(r, x); + + va_start(ap, x); + + for (;;) { + const char *t; + + t = va_arg(ap, const char *); + if (!t) + break; + + p = stpcpy(p, t); + } + + va_end(ap); + } else + r[0] = 0; + + return r; +} + +bool is_main_thread(void) { + static __thread int cached = 0; + + if (_unlikely_(cached == 0)) + cached = getpid() == gettid() ? 1 : -1; + + return cached > 0; +} + +int block_get_whole_disk(dev_t d, dev_t *ret) { + char *p, *s; + int r; + unsigned n, m; + + assert(ret); + + /* If it has a queue this is good enough for us */ + if (asprintf(&p, "/sys/dev/block/%u:%u/queue", major(d), minor(d)) < 0) + return -ENOMEM; + + r = access(p, F_OK); + free(p); + + if (r >= 0) { + *ret = d; + return 0; + } + + /* If it is a partition find the originating device */ + if (asprintf(&p, "/sys/dev/block/%u:%u/partition", major(d), minor(d)) < 0) + return -ENOMEM; + + r = access(p, F_OK); + free(p); + + if (r < 0) + return -ENOENT; + + /* Get parent dev_t */ + if (asprintf(&p, "/sys/dev/block/%u:%u/../dev", major(d), minor(d)) < 0) + return -ENOMEM; + + r = read_one_line_file(p, &s); + free(p); + + if (r < 0) + return r; + + r = sscanf(s, "%u:%u", &m, &n); + free(s); + + if (r != 2) + return -EINVAL; + + /* Only return this if it is really good enough for us. */ + if (asprintf(&p, "/sys/dev/block/%u:%u/queue", m, n) < 0) + return -ENOMEM; + + r = access(p, F_OK); + free(p); + + if (r >= 0) { + *ret = makedev(m, n); + return 0; + } + + return -ENOENT; +} + +int file_is_priv_sticky(const char *p) { + struct stat st; + + assert(p); + + if (lstat(p, &st) < 0) + return -errno; + + return + (st.st_uid == 0 || st.st_uid == getuid()) && + (st.st_mode & S_ISVTX); +} + static const char *const ioprio_class_table[] = { [IOPRIO_CLASS_NONE] = "none", [IOPRIO_CLASS_RT] = "realtime", @@ -5587,7 +5956,7 @@ static const char* const ip_tos_table[] = { DEFINE_STRING_TABLE_LOOKUP(ip_tos, int); -static const char *const signal_table[] = { +static const char *const __signal_table[] = { [SIGHUP] = "HUP", [SIGINT] = "INT", [SIGQUIT] = "QUIT", @@ -5623,4 +5992,207 @@ static const char *const signal_table[] = { [SIGSYS] = "SYS" }; -DEFINE_STRING_TABLE_LOOKUP(signal, int); +DEFINE_PRIVATE_STRING_TABLE_LOOKUP(__signal, int); + +const char *signal_to_string(int signo) { + static __thread char buf[12]; + const char *name; + + name = __signal_to_string(signo); + if (name) + return name; + + if (signo >= SIGRTMIN && signo <= SIGRTMAX) + snprintf(buf, sizeof(buf) - 1, "RTMIN+%d", signo - SIGRTMIN); + else + snprintf(buf, sizeof(buf) - 1, "%d", signo); + char_array_0(buf); + return buf; +} + +int signal_from_string(const char *s) { + int signo; + int offset = 0; + unsigned u; + + signo =__signal_from_string(s); + if (signo > 0) + return signo; + + if (startswith(s, "RTMIN+")) { + s += 6; + offset = SIGRTMIN; + } + if (safe_atou(s, &u) >= 0) { + signo = (int) u + offset; + if (signo > 0 && signo < _NSIG) + return signo; + } + return -1; +} + +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; +} + +int strdup_or_null(const char *a, char **b) { + char *c; + + assert(b); + + if (!a) { + *b = NULL; + return 0; + } + + c = strdup(a); + if (!c) + return -ENOMEM; + + *b = c; + return 0; +} + +int prot_from_flags(int flags) { + + switch (flags & O_ACCMODE) { + + case O_RDONLY: + return PROT_READ; + + case O_WRONLY: + return PROT_WRITE; + + case O_RDWR: + return PROT_READ|PROT_WRITE; + + default: + return -EINVAL; + } +} + +unsigned long cap_last_cap(void) { + static __thread unsigned long saved; + static __thread bool valid = false; + unsigned long p; + + if (valid) + return saved; + + p = (unsigned long) CAP_LAST_CAP; + + if (prctl(PR_CAPBSET_READ, p) < 0) { + + /* Hmm, look downwards, until we find one that + * works */ + for (p--; p > 0; p --) + if (prctl(PR_CAPBSET_READ, p) >= 0) + break; + + } else { + + /* Hmm, look upwards, until we find one that doesn't + * work */ + for (;; p++) + if (prctl(PR_CAPBSET_READ, p+1) < 0) + break; + } + + saved = p; + valid = true; + + return p; +} + +char *format_bytes(char *buf, size_t l, off_t t) { + unsigned i; + + static const struct { + const char *suffix; + off_t factor; + } table[] = { + { "E", 1024ULL*1024ULL*1024ULL*1024ULL*1024ULL*1024ULL }, + { "P", 1024ULL*1024ULL*1024ULL*1024ULL*1024ULL }, + { "T", 1024ULL*1024ULL*1024ULL*1024ULL }, + { "G", 1024ULL*1024ULL*1024ULL }, + { "M", 1024ULL*1024ULL }, + { "K", 1024ULL }, + }; + + for (i = 0; i < ELEMENTSOF(table); i++) { + + if (t >= table[i].factor) { + snprintf(buf, l, + "%llu.%llu%s", + (unsigned long long) (t / table[i].factor), + (unsigned long long) (((t*10ULL) / table[i].factor) % 10ULL), + table[i].suffix); + + goto finish; + } + } + + snprintf(buf, l, "%lluB", (unsigned long long) t); + +finish: + buf[l-1] = 0; + return buf; + +} + +void* memdup(const void *p, size_t l) { + void *r; + + assert(p); + + r = malloc(l); + if (!r) + return NULL; + + memcpy(r, p, l); + return r; +} + +int fd_inc_sndbuf(int fd, size_t n) { + int r, value; + socklen_t l = sizeof(value); + + r = getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &value, &l); + if (r >= 0 && + l == sizeof(value) && + (size_t) value >= n*2) + return 0; + + value = (int) n; + r = setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &value, sizeof(value)); + if (r < 0) + return -errno; + + return 1; +} + +int fd_inc_rcvbuf(int fd, size_t n) { + int r, value; + socklen_t l = sizeof(value); + + r = getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &value, &l); + if (r >= 0 && + l == sizeof(value) && + (size_t) value >= n*2) + return 0; + + value = (int) n; + r = setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &value, sizeof(value)); + if (r < 0) + return -errno; + + return 1; +} diff --git a/src/util.h b/src/util.h index bf5703c5aa..890a3b5d46 100644 --- a/src/util.h +++ b/src/util.h @@ -67,8 +67,10 @@ typedef struct dual_timestamp { #define FORMAT_TIMESTAMP_MAX 64 #define FORMAT_TIMESTAMP_PRETTY_MAX 256 #define FORMAT_TIMESPAN_MAX 64 +#define FORMAT_BYTES_MAX 8 -#define ANSI_HIGHLIGHT_ON "\x1B[1;31m" +#define ANSI_HIGHLIGHT_ON "\x1B[1;39m" +#define ANSI_HIGHLIGHT_RED_ON "\x1B[1;31m" #define ANSI_HIGHLIGHT_GREEN_ON "\x1B[1;32m" #define ANSI_HIGHLIGHT_OFF "\x1B[0m" @@ -135,6 +137,7 @@ void close_many(const int fds[], unsigned n_fd); int parse_boolean(const char *v); int parse_usec(const char *t, usec_t *usec); +int parse_bytes(const char *t, off_t *bytes); int parse_pid(const char *s, pid_t* ret_pid); int parse_uid(const char *s, uid_t* ret_uid); #define parse_gid(s, ret_uid) parse_uid(s, ret_uid) @@ -247,8 +250,10 @@ int parent_of_path(const char *path, char **parent); int rmdir_parents(const char *path, const char *stop); -int get_process_name(pid_t pid, char **name); -int get_process_cmdline(pid_t pid, size_t max_length, char **line); +int get_process_comm(pid_t pid, char **name); +int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char **line); +int get_process_exe(pid_t pid, char **name); +int get_process_uid(pid_t pid, uid_t *uid); char hexchar(int x); int unhexchar(char c); @@ -273,7 +278,9 @@ bool path_equal(const char *a, const char *b); char *ascii_strlower(char *path); -bool dirent_is_file(struct dirent *de); +bool dirent_is_file(const struct dirent *de); +bool dirent_is_file_with_suffix(const struct dirent *de, const char *suffix); + bool ignore_file(const char *filename); bool chars_intersect(const char *a, const char *b); @@ -287,13 +294,13 @@ int make_null_stdio(void); unsigned long long random_ull(void); -#define DEFINE_STRING_TABLE_LOOKUP(name,type) \ - const char *name##_to_string(type i) { \ +#define __DEFINE_STRING_TABLE_LOOKUP(name,type,scope) \ + scope const char *name##_to_string(type i) { \ if (i < 0 || i >= (type) ELEMENTSOF(name##_table)) \ return NULL; \ return name##_table[i]; \ } \ - type name##_from_string(const char *s) { \ + scope type name##_from_string(const char *s) { \ type i; \ unsigned u = 0; \ assert(s); \ @@ -308,6 +315,8 @@ unsigned long long random_ull(void); } \ struct __useless_struct_to_allow_trailing_semicolon__ +#define DEFINE_STRING_TABLE_LOOKUP(name,type) __DEFINE_STRING_TABLE_LOOKUP(name,type,) +#define DEFINE_PRIVATE_STRING_TABLE_LOOKUP(name,type) __DEFINE_STRING_TABLE_LOOKUP(name,type,static) int fd_nonblock(int fd, bool nonblock); int fd_cloexec(int fd, bool cloexec); @@ -318,10 +327,10 @@ bool fstype_is_network(const char *fstype); int chvt(int vt); -int read_one_char(FILE *f, char *ret, bool *need_nl); +int read_one_char(FILE *f, char *ret, usec_t timeout, bool *need_nl); int ask(char *ret, const char *replies, const char *text, ...); -int reset_terminal_fd(int fd); +int reset_terminal_fd(int fd, bool switch_to_text); int reset_terminal(const char *name); int open_terminal(const char *name, int mode); @@ -340,7 +349,7 @@ int fopen_temporary(const char *path, FILE **_f, char **_temp_path); ssize_t loop_read(int fd, void *buf, size_t nbytes, bool do_poll); ssize_t loop_write(int fd, const void *buf, size_t nbytes, bool do_poll); -int path_is_mount_point(const char *path); +int path_is_mount_point(const char *path, bool allow_symlink); bool is_device_path(const char *path); @@ -360,22 +369,28 @@ int get_ctty_devnr(pid_t pid, dev_t *d); int get_ctty(pid_t, dev_t *_devnr, char **r); int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid); +int fchmod_and_fchown(int fd, mode_t mode, uid_t uid, gid_t gid); -int rm_rf(const char *path, bool only_dirs, bool delete_root); +int rm_rf(const char *path, bool only_dirs, bool delete_root, bool honour_sticky); int pipe_eof(int fd); cpu_set_t* cpu_set_malloc(unsigned *ncpus); -void status_vprintf(const char *format, va_list ap); -void status_printf(const char *format, ...); +void status_vprintf(const char *status, bool ellipse, const char *format, va_list ap); +void status_printf(const char *status, bool ellipse, const char *format, ...); void status_welcome(void); -int columns(void); +int fd_columns(int fd); +unsigned columns(void); + +int fd_lines(int fd); +unsigned lines(void); int running_in_chroot(void); -char *ellipsize(const char *s, unsigned length, unsigned percent); +char *ellipsize(const char *s, size_t length, unsigned percent); +char *ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigned percent); int touch(const char *path); @@ -400,13 +415,10 @@ char *fstab_node_to_udev_node(const char *p); void filter_environ(const char *prefix); bool tty_is_vc(const char *tty); +bool tty_is_vc_resolve(const char *tty); int vtnr_from_tty(const char *tty); const char *default_term_for_tty(const char *tty); -int detect_vm(const char **id); -int detect_container(const char **id); -int detect_virtualization(const char **id); - void execute_directory(const char *directory, DIR *_d, char *argv[]); int kill_and_sigcont(pid_t pid, int sig); @@ -416,6 +428,8 @@ bool nulstr_contains(const char*nulstr, const char *needle); bool plymouth_running(void); void parse_syslog_priority(char **p, int *priority); +void skip_syslog_pid(char **buf); +void skip_syslog_date(char **buf); int have_effective_cap(int value); @@ -444,6 +458,7 @@ int hwclock_get_time(struct tm *tm); int hwclock_set_time(const struct tm *tm); int audit_session_from_pid(pid_t pid, uint32_t *id); +int audit_loginuid_from_pid(pid_t pid, uid_t *uid); bool display_is_local(const char *display); int socket_from_display(const char *display, char **path); @@ -458,6 +473,18 @@ int dirent_ensure_type(DIR *d, struct dirent *de); int in_search_path(const char *path, char **search); int get_files_in_directory(const char *path, char ***list); +char *join(const char *x, ...) _sentinel_; + +bool is_main_thread(void); + +bool in_charset(const char *s, const char* charset); + +int block_get_whole_disk(dev_t d, dev_t *ret); + +int file_is_priv_sticky(const char *p); + +int strdup_or_null(const char *a, char **b); + #define NULSTR_FOREACH(i, l) \ for ((i) = (l); (i) && *(i); (i) = strchr((i), 0)+1) @@ -493,4 +520,23 @@ int signal_from_string_try_harder(const char *s); extern int saved_argc; extern char **saved_argv; +bool kexec_loaded(void); + +int prot_from_flags(int flags); + +unsigned long cap_last_cap(void); + +char *format_bytes(char *buf, size_t l, off_t t); + +int fd_wait_for_event(int fd, int event, usec_t timeout); + +void* memdup(const void *p, size_t l); + +int rtc_open(int flags); + +int is_kernel_thread(pid_t pid); + +int fd_inc_sndbuf(int fd, size_t n); +int fd_inc_rcvbuf(int fd, size_t n); + #endif diff --git a/src/utmp-wtmp.c b/src/utmp-wtmp.c index b03a3e70af..217ae1e2c7 100644 --- a/src/utmp-wtmp.c +++ b/src/utmp-wtmp.c @@ -155,11 +155,11 @@ static int write_entry_wtmp(const struct utmpx *store) { return -errno; } -static int write_entry_both(const struct utmpx *store) { +static int write_utmp_wtmp(const struct utmpx *store_utmp, const struct utmpx *store_wtmp) { int r, s; - r = write_entry_utmp(store); - s = write_entry_wtmp(store); + r = write_entry_utmp(store_utmp); + s = write_entry_wtmp(store_wtmp); if (r >= 0) r = s; @@ -172,10 +172,14 @@ static int write_entry_both(const struct utmpx *store) { return r; } -int utmp_put_shutdown(usec_t t) { +static int write_entry_both(const struct utmpx *store) { + return write_utmp_wtmp(store, store); +} + +int utmp_put_shutdown(void) { struct utmpx store; - init_entry(&store, t); + init_entry(&store, 0); store.ut_type = RUN_LVL; strncpy(store.ut_user, "shutdown", sizeof(store.ut_user)); @@ -206,12 +210,12 @@ static const char *sanitize_id(const char *id) { return id + l - sizeof(((struct utmpx*) NULL)->ut_id); } -int utmp_put_init_process(usec_t t, const char *id, pid_t pid, pid_t sid, const char *line) { +int utmp_put_init_process(const char *id, pid_t pid, pid_t sid, const char *line) { struct utmpx store; assert(id); - init_timestamp(&store, t); + init_timestamp(&store, 0); store.ut_type = INIT_PROCESS; store.ut_pid = pid; @@ -226,7 +230,7 @@ int utmp_put_init_process(usec_t t, const char *id, pid_t pid, pid_t sid, const } int utmp_put_dead_process(const char *id, pid_t pid, int code, int status) { - struct utmpx lookup, store, *found; + struct utmpx lookup, store, store_wtmp, *found; assert(id); @@ -242,9 +246,7 @@ int utmp_put_dead_process(const char *id, pid_t pid, int code, int status) { if (found->ut_pid != pid) return 0; - zero(store); - - memcpy(&store, &lookup, sizeof(store)); + memcpy(&store, found, sizeof(store)); store.ut_type = DEAD_PROCESS; store.ut_exit.e_termination = code; store.ut_exit.e_exit = status; @@ -253,11 +255,15 @@ int utmp_put_dead_process(const char *id, pid_t pid, int code, int status) { zero(store.ut_host); zero(store.ut_tv); - return write_entry_both(&store); + memcpy(&store_wtmp, &store, sizeof(store_wtmp)); + /* wtmp wants the current time */ + init_timestamp(&store_wtmp, 0); + + return write_utmp_wtmp(&store, &store_wtmp); } -int utmp_put_runlevel(usec_t t, int runlevel, int previous) { +int utmp_put_runlevel(int runlevel, int previous) { struct utmpx store; int r; @@ -277,7 +283,7 @@ int utmp_put_runlevel(usec_t t, int runlevel, int previous) { if (previous == runlevel) return 0; - init_entry(&store, t); + init_entry(&store, 0); store.ut_type = RUN_LVL; store.ut_pid = (runlevel & 0xFF) | ((previous & 0xFF) << 8); diff --git a/src/utmp-wtmp.h b/src/utmp-wtmp.h index 4054aff7ea..a5998ebb21 100644 --- a/src/utmp-wtmp.h +++ b/src/utmp-wtmp.h @@ -26,12 +26,12 @@ int utmp_get_runlevel(int *runlevel, int *previous); -int utmp_put_shutdown(usec_t timestamp); +int utmp_put_shutdown(void); int utmp_put_reboot(usec_t timestamp); -int utmp_put_runlevel(usec_t timestamp, int runlevel, int previous); +int utmp_put_runlevel(int runlevel, int previous); int utmp_put_dead_process(const char *id, pid_t pid, int code, int status); -int utmp_put_init_process(usec_t timestamp, const char *id, pid_t pid, pid_t sid, const char *line); +int utmp_put_init_process(const char *id, pid_t pid, pid_t sid, const char *line); int utmp_wall(const char *message, bool (*match_tty)(const char *tty)); diff --git a/src/vconsole/Makefile b/src/vconsole/Makefile new file mode 120000 index 0000000000..d0b0e8e008 --- /dev/null +++ b/src/vconsole/Makefile @@ -0,0 +1 @@ +../Makefile
\ No newline at end of file diff --git a/src/vconsole-setup.c b/src/vconsole/vconsole-setup.c index 68ebac9ae4..91967891f1 100644 --- a/src/vconsole-setup.c +++ b/src/vconsole/vconsole-setup.c @@ -39,6 +39,7 @@ #include "util.h" #include "log.h" #include "macro.h" +#include "virt.h" static bool is_vconsole(int fd) { unsigned char data[1]; @@ -159,7 +160,7 @@ int main(int argc, char **argv) { #ifdef TARGET_GENTOO char *vc_unicode = NULL; #endif -#ifdef TARGET_MANDRIVA +#if defined(TARGET_MANDRIVA) || defined(TARGET_MAGEIA) char *vc_keytable = NULL; #endif int fd = -1; @@ -171,6 +172,8 @@ int main(int argc, char **argv) { log_parse_environment(); log_open(); + umask(0022); + if (argv[1]) vc = argv[1]; else @@ -200,10 +203,6 @@ int main(int argc, char **argv) { if (detect_container(NULL) <= 0) if ((r = parse_env_file("/proc/cmdline", WHITESPACE, -#if defined(TARGET_FEDORA) || defined(TARGET_MEEGO) - "SYSFONT", &vc_font, - "KEYTABLE", &vc_keymap, -#endif "vconsole.keymap", &vc_keymap, "vconsole.keymap.toggle", &vc_keymap_toggle, "vconsole.font", &vc_font, @@ -372,7 +371,7 @@ int main(int argc, char **argv) { log_warning("Failed to read /etc/conf.d/keymaps: %s", strerror(-r)); } -#elif defined(TARGET_MANDRIVA) +#elif defined(TARGET_MANDRIVA) || defined (TARGET_MAGEIA) if ((r = parse_env_file("/etc/sysconfig/i18n", NEWLINE, "SYSFONT", &vc_font, diff --git a/src/virt.c b/src/virt.c new file mode 100644 index 0000000000..3f0912a8a6 --- /dev/null +++ b/src/virt.c @@ -0,0 +1,321 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2011 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 <string.h> +#include <errno.h> +#include <unistd.h> + +#include "util.h" +#include "virt.h" + +/* 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... */ + + static const char *const dmi_vendors[] = { + "/sys/class/dmi/id/sys_vendor", + "/sys/class/dmi/id/board_vendor", + "/sys/class/dmi/id/bios_vendor" + }; + + 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"; + + 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"; + + uint32_t eax, ecx; + union { + uint32_t sig32[3]; + char text[13]; + } sig; + unsigned i; + const char *j, *k; + bool hypervisor; + + /* http://lwn.net/Articles/301888/ */ + zero(sig); + +#if defined (__i386__) +#define REG_a "eax" +#define REG_b "ebx" +#elif defined (__amd64__) +#define REG_a "rax" +#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" + " pop %%"REG_b" \n\t" + + : "=a" (eax), "=c" (ecx) + : "0" (eax) + ); + + hypervisor = !!(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; +} + +int detect_container(const char **id) { + FILE *f; + + /* Unfortunately many of these operations require root access + * in one way or another */ + + if (geteuid() != 0) + return -EPERM; + + if (running_in_chroot() > 0) { + + if (id) + *id = "chroot"; + + return 1; + } + + /* /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; + } + + f = fopen("/proc/1/environ", "re"); + if (f) { + bool done = false; + + do { + char line[LINE_MAX]; + unsigned i; + + for (i = 0; i < sizeof(line)-1; i++) { + int c; + + c = getc(f); + if (_unlikely_(c == EOF)) { + done = true; + break; + } else if (c == 0) + break; + + line[i] = c; + } + line[i] = 0; + + if (streq(line, "container=lxc")) { + fclose(f); + + if (id) + *id = "lxc"; + return 1; + + } else if (streq(line, "container=lxc-libvirt")) { + fclose(f); + + if (id) + *id = "lxc-libvirt"; + return 1; + + } else if (streq(line, "container=systemd-nspawn")) { + fclose(f); + + if (id) + *id = "systemd-nspawn"; + return 1; + + } else if (startswith(line, "container=")) { + fclose(f); + + if (id) + *id = "other"; + return 1; + } + + } while (!done); + + fclose(f); + } + + f = fopen("/proc/self/cgroup", "re"); + if (f) { + + for (;;) { + char line[LINE_MAX], *p; + + if (!fgets(line, sizeof(line), f)) + break; + + p = strchr(strstrip(line), ':'); + if (!p) + continue; + + if (strncmp(p, ":ns:", 4)) + continue; + + if (!streq(p, ":ns:/")) { + fclose(f); + + if (id) + *id = "pidns"; + + return 1; + } + } + + fclose(f); + } + + return 0; +} + +/* Returns a short identifier for the various VM/container implementations */ +Virtualization detect_virtualization(const char **id) { + + static __thread Virtualization cached_virt = _VIRTUALIZATION_INVALID; + static __thread const char *cached_id = NULL; + + const char *_id; + int r; + Virtualization v; + + if (_likely_(cached_virt >= 0)) { + + if (id && cached_virt > 0) + *id = cached_id; + + return cached_virt; + } + + r = detect_container(&_id); + if (r < 0) { + v = r; + goto finish; + } else if (r > 0) { + v = VIRTUALIZATION_CONTAINER; + goto finish; + } + + r = detect_vm(&_id); + if (r < 0) { + v = r; + goto finish; + } else if (r > 0) { + v = VIRTUALIZATION_VM; + goto finish; + } + + v = VIRTUALIZATION_NONE; + +finish: + if (v > 0) { + cached_id = _id; + + if (id) + *id = _id; + } + + if (v >= 0) + cached_virt = v; + + return v; +} diff --git a/src/virt.h b/src/virt.h new file mode 100644 index 0000000000..f55c9a68fd --- /dev/null +++ b/src/virt.h @@ -0,0 +1,38 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#ifndef foovirthfoo +#define foovirthfoo + +/*** + This file is part of systemd. + + Copyright 2011 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/>. +***/ + +int detect_vm(const char **id); +int detect_container(const char **id); + +typedef enum Virtualization { + VIRTUALIZATION_NONE = 0, + VIRTUALIZATION_VM, + VIRTUALIZATION_CONTAINER, + _VIRTUALIZATION_MAX, + _VIRTUALIZATION_INVALID = -1 +} Virtualization; + +Virtualization detect_virtualization(const char **id); + +#endif diff --git a/src/wraplabel.vala b/src/wraplabel.vala new file mode 100644 index 0000000000..49858c3222 --- /dev/null +++ b/src/wraplabel.vala @@ -0,0 +1,73 @@ +// Copyright (c) 2005 VMware, Inc. + +// This is a translation of http://git.gnome.org/browse/meld/tree/meld/ui/wraplabel.py, +// which in turn is a translation of WrapLabel from libview. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +// Python translation from wrapLabel.{cc|h} by Gian Mario Tagliaretti +// Vala translation from wraplabel.py by Zbigniew Jędrzejewski-Szmek + +public class WrapLabel : Gtk.Label { + private int _wrap_width; + + public WrapLabel(string? text = null) { + this._wrap_width = 0; + var layout = get_layout(); + layout.set_wrap(Pango.WrapMode.WORD_CHAR); + if (text != null) + this.set_text(text); + this.set_alignment(0, 0); + } + + public override void size_request(out Gtk.Requisition requisition) { + int width, height; + var layout = get_layout(); + layout.get_pixel_size(out width, out height); + requisition.width = 0; + requisition.height = height; + } + + public override void size_allocate(Gdk.Rectangle allocation) { + base.size_allocate (allocation); + this._set_wrap_width(allocation.width); + } + + public new void set_text(string str) { + base.set_text(str); + this._set_wrap_width(this._wrap_width); + } + + public new void set_markup(string str) { + base.set_markup(str); + this._set_wrap_width(this._wrap_width); + } + + private void _set_wrap_width(int width) { + if (width == 0) + return; + + var layout = get_layout(); + layout.set_width(width * Pango.SCALE); + if (_wrap_width != width) { + this._wrap_width = width; + this.queue_resize(); + } + } +} diff --git a/sysctl.d/.gitignore b/sysctl.d/.gitignore new file mode 100644 index 0000000000..7563539ab0 --- /dev/null +++ b/sysctl.d/.gitignore @@ -0,0 +1 @@ +/coredump.conf diff --git a/sysctl.d/Makefile b/sysctl.d/Makefile new file mode 120000 index 0000000000..bd1047548b --- /dev/null +++ b/sysctl.d/Makefile @@ -0,0 +1 @@ +../src/Makefile
\ No newline at end of file diff --git a/sysctl.d/coredump.conf.in b/sysctl.d/coredump.conf.in new file mode 100644 index 0000000000..ab19b1e988 --- /dev/null +++ b/sysctl.d/coredump.conf.in @@ -0,0 +1,10 @@ +# 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. + +# See sysctl.d(5) for details + +kernel.core_pattern=|@rootlibexecdir@/systemd-coredump %p %u %g %s %t %e diff --git a/test2/Makefile b/test/Makefile index 9aa46b4eb6..9aa46b4eb6 100644 --- a/test2/Makefile +++ b/test/Makefile diff --git a/test2/a.service b/test/a.service index 4168d2d051..4168d2d051 100644 --- a/test2/a.service +++ b/test/a.service diff --git a/test2/b.service b/test/b.service index e03bae36be..e03bae36be 100644 --- a/test2/b.service +++ b/test/b.service diff --git a/test2/c.service b/test/c.service index e2f60a8fbf..e2f60a8fbf 100644 --- a/test2/c.service +++ b/test/c.service diff --git a/test2/d.service b/test/d.service index 921fd2ee1b..921fd2ee1b 100644 --- a/test2/d.service +++ b/test/d.service diff --git a/test2/e.service b/test/e.service index 5ba98c7c43..5ba98c7c43 100644 --- a/test2/e.service +++ b/test/e.service diff --git a/test2/f.service b/test/f.service index 7dde681c17..7dde681c17 100644 --- a/test2/f.service +++ b/test/f.service diff --git a/test2/g.service b/test/g.service index cbfa82a454..cbfa82a454 100644 --- a/test2/g.service +++ b/test/g.service diff --git a/test2/h.service b/test/h.service index 74a7751cad..74a7751cad 100644 --- a/test2/h.service +++ b/test/h.service diff --git a/test1/default.target b/test1/default.target deleted file mode 120000 index 264d98085b..0000000000 --- a/test1/default.target +++ /dev/null @@ -1 +0,0 @@ -multiuser.target
\ No newline at end of file diff --git a/test1/exec-demo.service b/test1/exec-demo.service deleted file mode 100644 index 5802e37697..0000000000 --- a/test1/exec-demo.service +++ /dev/null @@ -1,9 +0,0 @@ -[Meta] -Description=Simple Execution Demo - -[Service] -ExecStartPre=/bin/ps -eo pid,uid,args,cgroup -ExecStartPre=/bin/cat /etc/hosts -ExecStart=/bin/bash -c '/bin/sleep 5 &' -Type=forking -Output=syslog diff --git a/test1/fifo-demo.service b/test1/fifo-demo.service deleted file mode 100644 index 51b19961fa..0000000000 --- a/test1/fifo-demo.service +++ /dev/null @@ -1,8 +0,0 @@ -[Meta] -Description=FIFO Activation Demo Service - -[Service] -ExecStartPre=/bin/echo "Wow, es geht los!" -#ExecStart=/bin/bash -c 'read a <&3 ; echo "GOT: $a"' -ExecStart=/bin/bash -c 'cat <&3' -Type=simple diff --git a/test1/fifo-demo.socket b/test1/fifo-demo.socket deleted file mode 100644 index 75ce7a6999..0000000000 --- a/test1/fifo-demo.socket +++ /dev/null @@ -1,6 +0,0 @@ -[Meta] -Description=FIFO Activation Demo Socket - -[Socket] -ListenFIFO=/tmp/systemd-fifo-demo -ExecStartPost=/bin/echo "OK, we're ready, now write something to /tmp/systemd-fifo-demo" diff --git a/test1/mail-transfer-agent.service b/test1/mail-transfer-agent.service deleted file mode 120000 index bca89da28e..0000000000 --- a/test1/mail-transfer-agent.service +++ /dev/null @@ -1 +0,0 @@ -postfix.service
\ No newline at end of file diff --git a/test1/mail-transfer-agent.socket b/test1/mail-transfer-agent.socket deleted file mode 120000 index daf3277d02..0000000000 --- a/test1/mail-transfer-agent.socket +++ /dev/null @@ -1 +0,0 @@ -postfix.socket
\ No newline at end of file diff --git a/test1/multiuser.target b/test1/multiuser.target deleted file mode 100644 index f105e3c2f0..0000000000 --- a/test1/multiuser.target +++ /dev/null @@ -1,2 +0,0 @@ -[Meta] -Description=Multi-User Target diff --git a/test1/multiuser.target.wants/exec-demo.service b/test1/multiuser.target.wants/exec-demo.service deleted file mode 120000 index 0ec1e35824..0000000000 --- a/test1/multiuser.target.wants/exec-demo.service +++ /dev/null @@ -1 +0,0 @@ -../exec-demo.service
\ No newline at end of file diff --git a/test1/multiuser.target.wants/fifo-demo.socket b/test1/multiuser.target.wants/fifo-demo.socket deleted file mode 120000 index 4c3d621e90..0000000000 --- a/test1/multiuser.target.wants/fifo-demo.socket +++ /dev/null @@ -1 +0,0 @@ -../fifo-demo.socket
\ No newline at end of file diff --git a/test1/multiuser.target.wants/mail-transfer-agent.socket b/test1/multiuser.target.wants/mail-transfer-agent.socket deleted file mode 120000 index 3c3a2db350..0000000000 --- a/test1/multiuser.target.wants/mail-transfer-agent.socket +++ /dev/null @@ -1 +0,0 @@ -../mail-transfer-agent.socket
\ No newline at end of file diff --git a/test1/multiuser.target.wants/permissions.service b/test1/multiuser.target.wants/permissions.service deleted file mode 120000 index 98f3d5fab2..0000000000 --- a/test1/multiuser.target.wants/permissions.service +++ /dev/null @@ -1 +0,0 @@ -../permissions.service
\ No newline at end of file diff --git a/test1/multiuser.target.wants/systemd-logger.socket b/test1/multiuser.target.wants/systemd-logger.socket deleted file mode 120000 index 4ce0a61aa1..0000000000 --- a/test1/multiuser.target.wants/systemd-logger.socket +++ /dev/null @@ -1 +0,0 @@ -../systemd-logger.socket
\ No newline at end of file diff --git a/test1/permissions.service b/test1/permissions.service deleted file mode 100644 index cb0664fa4f..0000000000 --- a/test1/permissions.service +++ /dev/null @@ -1,11 +0,0 @@ -[Meta] -Description=Permission Enforcement checker - -[Service] -ExecStart=/usr/bin/id -ExecStartPost=/usr/bin/env -ExecStartPost=/bin/sleep 5 -Type=oneshot -Capabilities=all= cap_dac_override=eip -User=nobody -Group=nobody diff --git a/test1/postfix.service b/test1/postfix.service deleted file mode 100644 index fbf39ecb58..0000000000 --- a/test1/postfix.service +++ /dev/null @@ -1,7 +0,0 @@ -[Meta] -Names=mail-transfer-agent.service -Description=Postfix Mail Server -Requires=syslog.socket - -[Service] -ExecStart=/usr/bin/postfix diff --git a/test1/postfix.socket b/test1/postfix.socket deleted file mode 100644 index 9d650a15eb..0000000000 --- a/test1/postfix.socket +++ /dev/null @@ -1,13 +0,0 @@ -[Meta] -Description=Postfix SMTP Socket - -[Socket] -ListenStream=53333 -ListenFIFO=/tmp/systemd-postfix-fifo -ExecStartPre=/bin/echo "About to create sockets..." -ExecStartPre=/bin/echo "About to create sockets... 2" -ExecStartPost=/bin/echo "Created sockets..." -ExecStopPre=/bin/echo "About to delete sockets..." -ExecStopPost=/bin/echo "Deleted sockets..." -ExecStopPost=/bin/echo "Deleted sockets... 2" -#BindToDevice=eth0 diff --git a/test1/syslog.service b/test1/syslog.service deleted file mode 100644 index 8fe47aedba..0000000000 --- a/test1/syslog.service +++ /dev/null @@ -1,5 +0,0 @@ -[Meta] -Description=System Logging Daemon - -[Service] -ExecStart=/usr/bin/rsyslogd --foobar 'waldo' diff --git a/test1/syslog.socket b/test1/syslog.socket deleted file mode 100644 index f2d3297acd..0000000000 --- a/test1/syslog.socket +++ /dev/null @@ -1,8 +0,0 @@ -[Meta] -Description=Syslog Socket - -[Socket] -ListenDatagram=/tmp/foobar/waldo/systemd-syslog-socket -ListenStream=eth0:3456 -DirectoryMode=0700 -SocketMode=0600 diff --git a/test1/systemd-logger.service b/test1/systemd-logger.service deleted file mode 100644 index c406d26f35..0000000000 --- a/test1/systemd-logger.service +++ /dev/null @@ -1,13 +0,0 @@ -[Meta] -Description=systemd Logging Daemon - -[Service] -ExecStart=/home/lennart/projects/systemd/systemd-logger -Type=simple -TimerSlackNS=1000000 -OOMScoreAdjust=40 -LimitCORE=0 -LimitFSIZE=0 -LimitLOCKS=0 -LimitMEMLOCK=0 -LimitNOFILE=512 diff --git a/test1/systemd-logger.socket b/test1/systemd-logger.socket deleted file mode 100644 index eb012c8dde..0000000000 --- a/test1/systemd-logger.socket +++ /dev/null @@ -1,5 +0,0 @@ -[Meta] -Description=systemd Logging Socket - -[Socket] -ListenStream=@/org/freedesktop/systemd1/logger diff --git a/tmpfiles.d/Makefile b/tmpfiles.d/Makefile new file mode 120000 index 0000000000..bd1047548b --- /dev/null +++ b/tmpfiles.d/Makefile @@ -0,0 +1 @@ +../src/Makefile
\ No newline at end of file diff --git a/tmpfiles.d/systemd.conf b/tmpfiles.d/systemd.conf index 7d4b356a12..be29c068af 100644 --- a/tmpfiles.d/systemd.conf +++ b/tmpfiles.d/systemd.conf @@ -13,9 +13,6 @@ F /run/utmp 0664 root utmp - f /var/log/wtmp 0664 root utmp - f /var/log/btmp 0600 root utmp - -d /tmp 1777 root root 10d -d /var/tmp 1777 root root 30d - d /var/cache/man - - - 30d r /forcefsck diff --git a/tmpfiles.d/tmp.conf b/tmpfiles.d/tmp.conf new file mode 100644 index 0000000000..8915b82ab5 --- /dev/null +++ b/tmpfiles.d/tmp.conf @@ -0,0 +1,12 @@ +# 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. + +# See tmpfiles.d(5) for details + +# Clear tmp directories separately, to make them easier to override +d /tmp 1777 root root 10d +d /var/tmp 1777 root root 30d diff --git a/tmpfiles.d/x11.conf b/tmpfiles.d/x11.conf index 5072b582aa..7f81af62da 100644 --- a/tmpfiles.d/x11.conf +++ b/tmpfiles.d/x11.conf @@ -15,4 +15,4 @@ d /tmp/.font-unix 1777 root root 10d d /tmp/.Test-unix 1777 root root 10d # Unlink the X11 lock files -r /tmp/.X[0-9]-lock +r /tmp/.X[0-9]*-lock diff --git a/units/.gitignore b/units/.gitignore index ac700e8044..94412d52e7 100644 --- a/units/.gitignore +++ b/units/.gitignore @@ -1,3 +1,4 @@ +/systemd-journald.service user@.service systemd-logind.service systemd-localed.service @@ -33,7 +34,7 @@ systemd-shutdownd.service systemd-random-seed-load.service systemd-random-seed-save.service systemd-initctl.service -systemd-logger.service +systemd-stdout-syslog-bridge.service getty@.service systemd-update-utmp-runlevel.service systemd-update-utmp-shutdown.service diff --git a/units/console-shell.service.m4 b/units/console-shell.service.m4 index cce2d5a5a4..fef9e1b176 100644 --- a/units/console-shell.service.m4 +++ b/units/console-shell.service.m4 @@ -23,6 +23,9 @@ After=rc-local.service m4_ifdef(`TARGET_MANDRIVA', After=rc-local.service )m4_dnl +m4_ifdef(`TARGET_MAGEIA', +After=rc-local.service +)m4_dnl Before=getty.target [Service] @@ -31,7 +34,10 @@ WorkingDirectory=/root ExecStart=-/sbin/sulogin ExecStopPost=-/bin/systemctl poweroff StandardInput=tty-force +StandardOutput=inherit +StandardError=inherit KillMode=process +IgnoreSIGPIPE=no # Bash ignores SIGTERM, so we send SIGHUP instead, to ensure that bash # terminates cleanly. diff --git a/units/dev-hugepages.mount b/units/dev-hugepages.mount index e6014e54a0..72a522e69c 100644 --- a/units/dev-hugepages.mount +++ b/units/dev-hugepages.mount @@ -8,6 +8,8 @@ [Unit] Description=Huge Pages File System DefaultDependencies=no +Before=sysinit.target +ConditionPathExists=/sys/kernel/mm/hugepages [Mount] What=hugetlbfs diff --git a/units/dev-mqueue.mount b/units/dev-mqueue.mount index 8519df5aca..cffdaf773f 100644 --- a/units/dev-mqueue.mount +++ b/units/dev-mqueue.mount @@ -8,6 +8,8 @@ [Unit] Description=POSIX Message Queue File System DefaultDependencies=no +Before=sysinit.target +ConditionPathExists=/proc/sys/fs/mqueue [Mount] What=mqueue diff --git a/units/emergency.service b/units/emergency.service index eff5261868..234bafcc88 100644 --- a/units/emergency.service +++ b/units/emergency.service @@ -21,7 +21,10 @@ ExecStartPre=-/bin/echo 'Welcome to emergency mode. Use "systemctl default" or ^ ExecStart=-/sbin/sulogin ExecStopPost=/bin/systemctl --fail --no-block default StandardInput=tty-force +StandardOutput=inherit +StandardError=inherit KillMode=process +IgnoreSIGPIPE=no # Bash ignores SIGTERM, so we send SIGHUP instead, to ensure that bash # terminates cleanly. diff --git a/units/fedora/prefdm.service b/units/fedora/prefdm.service index e7bd1231f8..77a0e9ad7c 100644 --- a/units/fedora/prefdm.service +++ b/units/fedora/prefdm.service @@ -7,7 +7,7 @@ [Unit] Description=Display Manager -After=syslog.target livesys-late.service rc-local.service systemd-user-sessions.service +After=livesys-late.service rc-local.service systemd-user-sessions.service # On Fedora gdm/X11 is on tty1. We explicitly cancel the getty here to # avoid any races around that. @@ -18,3 +18,4 @@ After=getty@tty1.service plymouth-quit.service ExecStart=/etc/X11/prefdm -nodaemon Restart=always RestartSec=0 +IgnoreSIGPIPE=no diff --git a/units/fedora/rc-local.service b/units/fedora/rc-local.service index f5f940f7ee..0bef5c72ab 100644 --- a/units/fedora/rc-local.service +++ b/units/fedora/rc-local.service @@ -5,14 +5,15 @@ # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. +# This unit gets pulled automatically into multi-user.target by +# systemd-rc-local-generator if /etc/rc.d/rc.local is executable. [Unit] -Description=/etc/rc.local Compatibility -ConditionFileIsExecutable=/etc/rc.d/rc.local +Description=/etc/rc.d/rc.local Compatibility +After=network.target [Service] Type=forking ExecStart=/etc/rc.d/rc.local start TimeoutSec=0 -StandardOutput=tty RemainAfterExit=yes SysVStartPriority=99 diff --git a/units/frugalware/display-manager.service b/units/frugalware/display-manager.service index e61422587a..2376e1977c 100644 --- a/units/frugalware/display-manager.service +++ b/units/frugalware/display-manager.service @@ -7,7 +7,7 @@ [Unit] Description=Display Manager -After=syslog.target local.service systemd-user-sessions.service +After=local.service systemd-user-sessions.service [Service] EnvironmentFile=/etc/sysconfig/desktop diff --git a/units/fsck-root.service.in b/units/fsck-root.service.in index 7b3529db07..4086149128 100644 --- a/units/fsck-root.service.in +++ b/units/fsck-root.service.in @@ -18,6 +18,6 @@ ConditionPathExists=!/run/initramfs/root-fsck Type=oneshot RemainAfterExit=no ExecStart=@rootlibexecdir@/systemd-fsck -StandardOutput=syslog+console +StandardOutput=journal+console FsckPassNo=1 TimeoutSec=0 diff --git a/units/fsck@.service.in b/units/fsck@.service.in index e1f773639b..c06684b634 100644 --- a/units/fsck@.service.in +++ b/units/fsck@.service.in @@ -16,5 +16,5 @@ Before=shutdown.target Type=oneshot RemainAfterExit=no ExecStart=@rootlibexecdir@/systemd-fsck %f -StandardOutput=syslog+console +StandardOutput=journal+console TimeoutSec=0 diff --git a/units/getty@.service.m4 b/units/getty@.service.m4 index ea80784144..8df7ec4639 100644 --- a/units/getty@.service.m4 +++ b/units/getty@.service.m4 @@ -24,6 +24,9 @@ After=rc-local.service m4_ifdef(`TARGET_MANDRIVA', After=rc-local.service )m4_dnl +m4_ifdef(`TARGET_MAGEIA', +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 @@ -41,10 +44,11 @@ TTYReset=yes TTYVHangup=yes TTYVTDisallocate=yes KillMode=process +IgnoreSIGPIPE=no # Unset locale for the console getty since the console has problems # displaying some internationalized messages. -Environment=LANG= LC_CTYPE= LC_NUMERIC= LC_TIME= LC_COLLATE= LC_MONETARY= LC_MESSAGES= LC_PAPER= LC_NAME= LC_ADDRESS= LC_TELEPHONE= LC_MEASUREMENT= LC_IDENTIFICATION= +Environment=LANG= LANGUAGE= LC_CTYPE= LC_NUMERIC= LC_TIME= LC_COLLATE= LC_MONETARY= LC_MESSAGES= LC_PAPER= LC_NAME= LC_ADDRESS= LC_TELEPHONE= LC_MEASUREMENT= LC_IDENTIFICATION= # Some login implementations ignore SIGTERM, so we send SIGHUP # instead, to ensure that login terminates cleanly. diff --git a/units/local-fs-pre.target b/units/local-fs-pre.target new file mode 100644 index 0000000000..11e67bac1c --- /dev/null +++ b/units/local-fs-pre.target @@ -0,0 +1,11 @@ +# 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. + +# See systemd.special(7) for details + +[Unit] +Description=Local File Systems (Pre) diff --git a/units/mageia/prefdm.service b/units/mageia/prefdm.service new file mode 100644 index 0000000000..4a896bf582 --- /dev/null +++ b/units/mageia/prefdm.service @@ -0,0 +1,21 @@ +# 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=Display Manager +After=livesys-late.service rc-local.service systemd-user-sessions.service +After=network.target acpid.service fs.service haldaemon.service + +# Do not stop plymouth, it is done in prefdm if required +Conflicts=plymouth-quit.service +After=plymouth-quit.service + +[Service] +ExecStart=/etc/X11/prefdm +Type=forking +Restart=always +RestartSec=0 diff --git a/units/mandriva/prefdm.service b/units/mandriva/prefdm.service index d0c7f99c6e..4a896bf582 100644 --- a/units/mandriva/prefdm.service +++ b/units/mandriva/prefdm.service @@ -7,7 +7,7 @@ [Unit] Description=Display Manager -After=syslog.target livesys-late.service rc-local.service systemd-user-sessions.service +After=livesys-late.service rc-local.service systemd-user-sessions.service After=network.target acpid.service fs.service haldaemon.service # Do not stop plymouth, it is done in prefdm if required diff --git a/units/quotacheck.service.in b/units/quotacheck.service.in index 27dcb1e4c6..c97b7a4687 100644 --- a/units/quotacheck.service.in +++ b/units/quotacheck.service.in @@ -16,5 +16,4 @@ ConditionPathExists=/sbin/quotacheck Type=oneshot RemainAfterExit=yes ExecStart=@rootlibexecdir@/systemd-quotacheck -StandardOutput=syslog TimeoutSec=0 diff --git a/units/quotaon.service b/units/quotaon.service index 2c7b36b4fe..ef2fc8c976 100644 --- a/units/quotaon.service +++ b/units/quotaon.service @@ -16,4 +16,3 @@ ConditionPathExists=/sbin/quotaon Type=oneshot RemainAfterExit=yes ExecStart=/sbin/quotaon -aug -StandardOutput=syslog diff --git a/units/remote-fs-pre.target b/units/remote-fs-pre.target new file mode 100644 index 0000000000..8aceb08b9d --- /dev/null +++ b/units/remote-fs-pre.target @@ -0,0 +1,12 @@ +# 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. + +# See systemd.special(7) for details + +[Unit] +Description=Remote File Systems (Pre) +After=network.target diff --git a/units/remount-rootfs.service b/units/remount-rootfs.service index 6dbcaeb701..53d0f31531 100644 --- a/units/remount-rootfs.service +++ b/units/remount-rootfs.service @@ -10,10 +10,10 @@ Description=Remount Root FS DefaultDependencies=no Conflicts=shutdown.target After=systemd-readahead-collect.service systemd-readahead-replay.service fsck-root.service -Before=local-fs.target shutdown.target mountoverflowtmp.service +Before=local-fs-pre.target local-fs.target shutdown.target mountoverflowtmp.service +Wants=local-fs-pre.target [Service] Type=oneshot RemainAfterExit=yes ExecStart=/bin/mount / -o remount -StandardOutput=syslog diff --git a/units/rescue.service.m4 b/units/rescue.service.m4 index d2fd582e86..7dd8a220b8 100644 --- a/units/rescue.service.m4 +++ b/units/rescue.service.m4 @@ -11,7 +11,7 @@ Description=Rescue Shell DefaultDependencies=no Conflicts=shutdown.target -After=basic.target +After=basic.target plymouth-start.service Before=shutdown.target [Service] @@ -25,12 +25,17 @@ ExecStart=-/bin/bash -c "exec ${SINGLE}"', m4_ifdef(`TARGET_MANDRIVA', `EnvironmentFile=/etc/sysconfig/init ExecStart=-/bin/bash -c "exec ${SINGLE}"', -`ExecStart=-/sbin/sulogin' +m4_ifdef(`TARGET_MAGEIA', +`EnvironmentFile=/etc/sysconfig/init +ExecStart=-/bin/bash -c "exec ${SINGLE}"', m4_ifdef(`TARGET_MEEGO', `EnvironmentFile=/etc/sysconfig/init -ExecStart=-/bin/bash -c "exec ${SINGLE}"',))) +ExecStart=-/bin/bash -c "exec ${SINGLE}"', +`ExecStart=-/sbin/sulogin')))) ExecStopPost=-/bin/systemctl --fail --no-block default StandardInput=tty-force +StandardOutput=inherit +StandardError=inherit KillMode=process # Bash ignores SIGTERM, so we send SIGHUP instead, to ensure that bash diff --git a/units/serial-getty@.service.m4 b/units/serial-getty@.service.m4 index 082290cb8b..fc8b57b93e 100644 --- a/units/serial-getty@.service.m4 +++ b/units/serial-getty@.service.m4 @@ -24,6 +24,9 @@ After=rc-local.service m4_ifdef(`TARGET_MANDRIVA', After=rc-local.service )m4_dnl +m4_ifdef(`TARGET_MAGEIA', +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 @@ -40,6 +43,7 @@ TTYPath=/dev/%I TTYReset=yes TTYVHangup=yes KillMode=process +IgnoreSIGPIPE=no # Some login implementations ignore SIGTERM, so we send SIGHUP # instead, to ensure that login terminates cleanly. diff --git a/units/suse/rc-local.service b/units/suse/rc-local.service index fe4c00716e..2384a18f94 100644 --- a/units/suse/rc-local.service +++ b/units/suse/rc-local.service @@ -5,14 +5,15 @@ # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. +# This unit gets pulled automatically into multi-user.target by +# systemd-rc-local-generator if /etc/init.d/boot.local is executable. [Unit] Description=/etc/init.d/boot.local Compatibility -ConditionFileIsExecutable=/etc/init.d/boot.local +After=network.target [Service] Type=oneshot ExecStart=/etc/init.d/boot.local TimeoutSec=0 -StandardOutput=tty RemainAfterExit=yes SysVStartPriority=99 diff --git a/units/dev-mqueue.automount b/units/sys-fs-fuse-connections.mount index 1061597571..037471537b 100644 --- a/units/dev-mqueue.automount +++ b/units/sys-fs-fuse-connections.mount @@ -6,10 +6,13 @@ # (at your option) any later version. [Unit] -Description=POSIX Message Queue File System Automount Point +Description=FUSE Control File System DefaultDependencies=no +ConditionPathExists=/sys/fs/fuse/connections +After=systemd-modules-load.service Before=sysinit.target -ConditionPathExists=/proc/sys/fs/mqueue -[Automount] -Where=/dev/mqueue +[Mount] +What=fusectl +Where=/sys/fs/fuse/connections +Type=fusectl diff --git a/units/dev-hugepages.automount b/units/sys-kernel-config.mount index 6e03df3560..d6862bf6bd 100644 --- a/units/dev-hugepages.automount +++ b/units/sys-kernel-config.mount @@ -6,10 +6,13 @@ # (at your option) any later version. [Unit] -Description=Huge Pages File System Automount Point +Description=Configuration File System DefaultDependencies=no +ConditionPathExists=/sys/kernel/config +After=systemd-modules-load.service Before=sysinit.target -ConditionPathExists=/sys/kernel/mm/hugepages -[Automount] -Where=/dev/hugepages +[Mount] +What=configfs +Where=/sys/kernel/config +Type=configfs diff --git a/units/sys-kernel-debug.mount b/units/sys-kernel-debug.mount index 53d107260b..d9fca1ff3d 100644 --- a/units/sys-kernel-debug.mount +++ b/units/sys-kernel-debug.mount @@ -8,6 +8,8 @@ [Unit] Description=Debug File System DefaultDependencies=no +ConditionPathExists=/sys/kernel/debug +Before=sysinit.target [Mount] What=debugfs diff --git a/units/sys-kernel-security.automount b/units/sys-kernel-security.automount deleted file mode 100644 index b7b16e19aa..0000000000 --- a/units/sys-kernel-security.automount +++ /dev/null @@ -1,15 +0,0 @@ -# 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=Security File System Automount Point -DefaultDependencies=no -Before=sysinit.target -ConditionPathExists=/sys/kernel/security - -[Automount] -Where=/sys/kernel/security diff --git a/units/sys-kernel-security.mount b/units/sys-kernel-security.mount index 770207f004..80cd7617d4 100644 --- a/units/sys-kernel-security.mount +++ b/units/sys-kernel-security.mount @@ -8,6 +8,8 @@ [Unit] Description=Security File System DefaultDependencies=no +ConditionPathExists=/sys/kernel/security +Before=sysinit.target [Mount] What=securityfs diff --git a/units/syslog.socket b/units/syslog.socket index 500bb7c314..1c54857762 100644 --- a/units/syslog.socket +++ b/units/syslog.socket @@ -11,20 +11,29 @@ Description=Syslog Socket DefaultDependencies=no Before=sockets.target syslog.target +Conflicts=shutdown.target +Before=shutdown.target # Pull in syslog.target to tell people that /dev/log is now accessible Wants=syslog.target [Socket] -ListenDatagram=/dev/log +ListenDatagram=/run/systemd/journal/syslog SocketMode=0666 +PassCredentials=yes +ReceiveBuffer=8M -# The service we activate on incoming traffic is -# systemd-kmsg-syslogd.service. That doesn't mean however, that this -# is the main syslog daemon in the system. Another syslog -# implementation (which might be started via on-boot or another -# non-socket activation) can take over possession of the socket and -# terminate systemd-kmsg-syslogd. It could also simply replace the -# socket in the file system, and leave systemd-kmsg-syslogd untouched. - -Service=systemd-kmsg-syslogd.service +# The default syslog implementation should make syslog.service a +# symlink to itself, so that this socket activates the right actual +# syslog service. +# +# Examples: +# +# /etc/systemd/system/syslog.service -> /lib/systemd/system/rsyslog.service +# /etc/systemd/system/syslog.service -> /lib/systemd/system/syslog-ng.service +# +# Best way to achieve that is by adding this to your unit file +# (i.e. to rsyslog.service or syslog-ng.service): +# +# [Install] +# Alias=syslog.service diff --git a/units/systemd-ask-password-console.path b/units/systemd-ask-password-console.path index b5acf943b4..c3143d1da6 100644 --- a/units/systemd-ask-password-console.path +++ b/units/systemd-ask-password-console.path @@ -9,7 +9,9 @@ Description=Dispatch Password Requests to Console Directory Watch DefaultDependencies=no Conflicts=shutdown.target +After=plymouth-start.service Before=basic.target shutdown.target +ConditionPathExists=!/run/plymouth/pid [Path] DirectoryNotEmpty=/run/systemd/ask-password diff --git a/units/systemd-ask-password-console.service.in b/units/systemd-ask-password-console.service.in index a2ac09cd6a..5ff3ed55d7 100644 --- a/units/systemd-ask-password-console.service.in +++ b/units/systemd-ask-password-console.service.in @@ -9,7 +9,9 @@ Description=Dispatch Password Requests to Console DefaultDependencies=no Conflicts=shutdown.target +After=plymouth-start.service Before=shutdown.target +ConditionPathExists=!/run/plymouth/pid [Service] ExecStart=@rootbindir@/systemd-tty-ask-password-agent --watch --console diff --git a/units/systemd-ask-password-plymouth.path b/units/systemd-ask-password-plymouth.path index 6a96520921..06a587620f 100644 --- a/units/systemd-ask-password-plymouth.path +++ b/units/systemd-ask-password-plymouth.path @@ -8,8 +8,11 @@ [Unit] Description=Forward Password Requests to Plymouth Directory Watch DefaultDependencies=no -Conflicts=shutdown.target systemd-ask-password-console.path systemd-ask-password-console.service +Conflicts=shutdown.target +After=plymouth-start.service Before=basic.target shutdown.target +ConditionKernelCommandLine=!plymouth.enable=0 +ConditionPathExists=/run/plymouth/pid [Path] DirectoryNotEmpty=/run/systemd/ask-password diff --git a/units/systemd-ask-password-plymouth.service.in b/units/systemd-ask-password-plymouth.service.in index a7cd451aed..92cbfdbf09 100644 --- a/units/systemd-ask-password-plymouth.service.in +++ b/units/systemd-ask-password-plymouth.service.in @@ -8,8 +8,11 @@ [Unit] Description=Forward Password Requests to Plymouth DefaultDependencies=no -Conflicts=shutdown.target systemd-ask-password-console.path systemd-ask-password-console.service +Conflicts=shutdown.target +After=plymouth-start.service Before=shutdown.target +ConditionKernelCommandLine=!plymouth.enable=0 +ConditionPathExists=/run/plymouth/pid [Service] ExecStart=@rootbindir@/systemd-tty-ask-password-agent --watch --plymouth diff --git a/units/systemd-journald.service.in b/units/systemd-journald.service.in new file mode 100644 index 0000000000..92606b0d88 --- /dev/null +++ b/units/systemd-journald.service.in @@ -0,0 +1,25 @@ +# 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. + +# See systemd.special(7) for details + +[Unit] +Description=Journal Service +DefaultDependencies=no +Requires=systemd-journald.socket +After=systemd-journald.socket +After=syslog.socket + +[Service] +ExecStart=@rootlibexecdir@/systemd-journald +NotifyAccess=all +StandardOutput=null +CapabilityBoundingSet=CAP_SYS_ADMIN CAP_DAC_OVERRIDE CAP_SYS_PTRACE CAP_SYSLOG CAP_AUDIT_CONTROL CAP_CHOWN CAP_DAC_READ_SEARCH CAP_FOWNER CAP_SETUID CAP_SETGID + +# Increase the default a bit in order to allow many simultaneous +# services being run since we keep one fd open per service. +LimitNOFILE=16384 diff --git a/units/systemd-logger.socket b/units/systemd-journald.socket index 7178cc8246..c752505d9f 100644 --- a/units/systemd-logger.socket +++ b/units/systemd-journald.socket @@ -8,9 +8,9 @@ # See systemd.special(7) for details [Unit] -Description=Stdio Syslog Bridge Socket +Description=Journal Socket DefaultDependencies=no -Before=sockets.target +Before=sockets.target syslog.target # Mount and swap units need this. If this socket unit is removed by an # isolate request the mount and and swap units would be removed too, @@ -18,4 +18,9 @@ Before=sockets.target IgnoreOnIsolate=yes [Socket] -ListenStream=/run/systemd/logger +ListenStream=/run/systemd/journal/stdout +ListenDatagram=/run/systemd/journal/socket +ListenDatagram=/dev/log +SocketMode=0666 +PassCredentials=yes +ReceiveBuffer=8M diff --git a/units/systemd-kmsg-syslogd.service.in b/units/systemd-kmsg-syslogd.service.in deleted file mode 100644 index b20889e5e5..0000000000 --- a/units/systemd-kmsg-syslogd.service.in +++ /dev/null @@ -1,19 +0,0 @@ -# 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. - -# See systemd.special(7) for details - -[Unit] -Description=Syslog Kernel Log Buffer Bridge -DefaultDependencies=no - -[Service] -ExecStart=@rootlibexecdir@/systemd-kmsg-syslogd -NotifyAccess=all -StandardOutput=null -Sockets=syslog.socket -CapabilityBoundingSet=CAP_DAC_OVERRIDE diff --git a/units/systemd-logger.service.in b/units/systemd-logger.service.in deleted file mode 100644 index 5f7fe40939..0000000000 --- a/units/systemd-logger.service.in +++ /dev/null @@ -1,20 +0,0 @@ -# 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. - -# See systemd.special(7) for details - -[Unit] -Description=Stdio Syslog Bridge -DefaultDependencies=no -Requires=syslog.socket -After=syslog.socket - -[Service] -ExecStart=@rootlibexecdir@/systemd-logger -NotifyAccess=all -StandardOutput=null -CapabilityBoundingSet=CAP_SYS_ADMIN CAP_SETUID CAP_SETGID diff --git a/units/systemd-logind.service.in b/units/systemd-logind.service.in index 82a2c6a0ca..531b8f7e93 100644 --- a/units/systemd-logind.service.in +++ b/units/systemd-logind.service.in @@ -14,5 +14,8 @@ Description=Login Service ExecStart=@rootlibexecdir@/systemd-logind Type=dbus BusName=org.freedesktop.login1 -CapabilityBoundingSet=CAP_AUDIT_CONTROL CAP_CHOWN CAP_KILL CAP_DAC_READ_SEARCH CAP_DAC_OVERRIDE CAP_FOWNER -StandardOutput=syslog +CapabilityBoundingSet=CAP_AUDIT_CONTROL CAP_CHOWN CAP_KILL CAP_DAC_READ_SEARCH CAP_DAC_OVERRIDE CAP_FOWNER CAP_SYS_TTY_CONFIG + +# Increase the default a bit in order to allow many simultaneous +# logins since we keep one fd open per session. +LimitNOFILE=16384 diff --git a/units/systemd-readahead-collect.service.in b/units/systemd-readahead-collect.service.in index 1a66f9fa6f..56ba54f0b3 100644 --- a/units/systemd-readahead-collect.service.in +++ b/units/systemd-readahead-collect.service.in @@ -16,6 +16,7 @@ Before=sysinit.target shutdown.target Type=notify ExecStart=@rootlibexecdir@/systemd-readahead-collect RemainAfterExit=yes +StandardOutput=null [Install] WantedBy=default.target diff --git a/units/systemd-readahead-replay.service.in b/units/systemd-readahead-replay.service.in index 5cc6defd9b..7c82e408e2 100644 --- a/units/systemd-readahead-replay.service.in +++ b/units/systemd-readahead-replay.service.in @@ -16,6 +16,7 @@ ConditionPathExists=/.readahead Type=notify ExecStart=@rootlibexecdir@/systemd-readahead-replay RemainAfterExit=yes +StandardOutput=null [Install] WantedBy=default.target diff --git a/units/systemd-remount-api-vfs.service.in b/units/systemd-remount-api-vfs.service.in index 2ccbe23c8c..f4df0ca263 100644 --- a/units/systemd-remount-api-vfs.service.in +++ b/units/systemd-remount-api-vfs.service.in @@ -10,10 +10,10 @@ Description=Remount API VFS DefaultDependencies=no Conflicts=shutdown.target After=systemd-readahead-collect.service systemd-readahead-replay.service -Before=local-fs.target shutdown.target +Before=local-fs-pre.target local-fs.target shutdown.target +Wants=local-fs-pre.target [Service] Type=oneshot RemainAfterExit=yes ExecStart=@rootlibexecdir@/systemd-remount-api-vfs -StandardOutput=syslog diff --git a/units/systemd-shutdownd.socket b/units/systemd-shutdownd.socket index b30a6657cf..532a6f0c73 100644 --- a/units/systemd-shutdownd.socket +++ b/units/systemd-shutdownd.socket @@ -14,3 +14,5 @@ Before=sockets.target [Socket] ListenDatagram=/run/systemd/shutdownd +SocketMode=0600 +PassCredentials=yes diff --git a/units/systemd-vconsole-setup.service.in b/units/systemd-vconsole-setup.service.in index 91d95d66e3..673fb6ccf6 100644 --- a/units/systemd-vconsole-setup.service.in +++ b/units/systemd-vconsole-setup.service.in @@ -16,4 +16,3 @@ Before=sysinit.target shutdown.target Type=oneshot RemainAfterExit=yes ExecStart=@rootlibexecdir@/systemd-vconsole-setup -StandardOutput=syslog diff --git a/units/user@.service.in b/units/user@.service.in index 59fe5242d6..91e3b25158 100644 --- a/units/user@.service.in +++ b/units/user@.service.in @@ -15,5 +15,5 @@ PAMName=systemd-shared ControlGroup=%R/user/%I/shared cpu:/ ControlGroupModify=yes Type=notify -ExecStart=-@rootbindir@/systemd --user +ExecStart=-@rootlibexecdir@/systemd --user Environment=DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/%I/dbus/user_bus_socket diff --git a/units/var-lock.mount b/units/var-lock.mount index 80e1bab261..07277adac3 100644 --- a/units/var-lock.mount +++ b/units/var-lock.mount @@ -10,6 +10,7 @@ Description=Lock Directory Before=local-fs.target # skip mounting if the directory does not exist or is a symlink ConditionPathIsDirectory=/var/lock +ConditionPathIsSymbolicLink=!/var/lock [Mount] What=/run/lock diff --git a/units/var-run.mount b/units/var-run.mount index c513dfecd2..ab4da424c9 100644 --- a/units/var-run.mount +++ b/units/var-run.mount @@ -10,6 +10,7 @@ Description=Runtime Directory Before=local-fs.target # skip mounting if the directory does not exist or is a symlink ConditionPathIsDirectory=/var/run +ConditionPathIsSymbolicLink=!/var/run [Mount] What=/run |