summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTollef Fog Heen <tfheen@err.no>2012-02-20 20:00:20 +0100
committerTollef Fog Heen <tfheen@err.no>2012-02-20 20:00:20 +0100
commit14466291560ece602232c1ae960033025f3bbb1c (patch)
tree735095b834ee1c726072bda7f969aa540da7ec94
parentdbe879eae939311429293dcd433c5ad99c53f54f (diff)
parent437b7dee328738b7aca89a9c7527f228ff8f2d34 (diff)
downloadsystemd-14466291560ece602232c1ae960033025f3bbb1c.tar.gz
Merge tag 'v43' into debian-units
systemd 43 Conflicts: units/remount-rootfs.service
-rw-r--r--.gitignore24
-rw-r--r--.vimrc4
-rw-r--r--Makefile.am1876
-rw-r--r--NEWS225
-rw-r--r--README22
-rw-r--r--TODO317
-rwxr-xr-xautogen.sh79
-rw-r--r--configure.ac254
-rw-r--r--man/custom-html.xsl29
-rw-r--r--man/daemon.xml20
-rw-r--r--man/hostname.xml2
-rw-r--r--man/locale.conf.xml2
-rw-r--r--man/os-release.xml140
-rw-r--r--man/sd-daemon.xml33
-rw-r--r--man/sd-login.xml146
-rw-r--r--man/sd-readahead.xml4
-rw-r--r--man/sd_booted.xml32
-rw-r--r--man/sd_get_seats.xml127
-rw-r--r--man/sd_is_fifo.xml26
-rw-r--r--man/sd_listen_fds.xml32
-rw-r--r--man/sd_login_monitor_new.xml172
-rw-r--r--man/sd_notify.xml43
-rw-r--r--man/sd_pid_get_session.xml160
-rw-r--r--man/sd_readahead.xml6
-rw-r--r--man/sd_seat_get_active.xml157
-rw-r--r--man/sd_session_is_active.xml157
-rw-r--r--man/sd_uid_get_state.xml185
-rw-r--r--man/systemctl.xml15
-rw-r--r--man/systemd-cgls.xml19
-rw-r--r--man/systemd-cgtop.xml246
-rw-r--r--man/systemd-loginctl.xml4
-rw-r--r--man/systemd-nspawn.xml30
-rw-r--r--man/systemd-tmpfiles.xml6
-rw-r--r--man/systemd.conf.xml22
-rw-r--r--man/systemd.exec.xml330
-rw-r--r--man/systemd.mount.xml25
-rw-r--r--man/systemd.path.xml11
-rw-r--r--man/systemd.service.xml157
-rw-r--r--man/systemd.socket.xml17
-rw-r--r--man/systemd.special.xml.in66
-rw-r--r--man/systemd.unit.xml192
-rw-r--r--man/systemd.xml57
-rw-r--r--man/timezone.xml90
-rw-r--r--man/tmpfiles.d.xml110
-rw-r--r--po/.gitignore2
-rw-r--r--po/POTFILES.in8
-rw-r--r--po/POTFILES.skip6
-rw-r--r--po/pl.po175
-rw-r--r--src/.gitignore13
-rw-r--r--src/99-systemd.rules.in15
-rw-r--r--src/acl-util.c68
-rw-r--r--src/acl-util.h27
-rw-r--r--src/ask-password-api.c18
-rw-r--r--src/automount.c128
-rw-r--r--src/automount.h17
l---------src/binfmt/Makefile1
-rw-r--r--src/binfmt/binfmt.c (renamed from src/binfmt.c)4
-rw-r--r--src/bridge.c2
-rw-r--r--src/cgls.c26
-rw-r--r--src/cgroup-attr.c102
-rw-r--r--src/cgroup-attr.h49
-rw-r--r--src/cgroup-show.c24
-rw-r--r--src/cgroup-show.h6
-rw-r--r--src/cgroup-util.c178
-rw-r--r--src/cgroup-util.h4
-rw-r--r--src/cgroup.c94
-rw-r--r--src/cgroup.h9
-rw-r--r--src/cgroups-agent.c2
-rw-r--r--src/cgtop.c729
-rw-r--r--src/condition.c98
-rw-r--r--src/condition.h3
-rw-r--r--src/conf-parser.c358
-rw-r--r--src/conf-parser.h80
l---------src/cryptsetup/Makefile1
-rw-r--r--src/cryptsetup/cryptsetup-generator.c (renamed from src/cryptsetup-generator.c)7
-rw-r--r--src/cryptsetup/cryptsetup.c (renamed from src/cryptsetup.c)4
-rw-r--r--src/dbus-automount.c27
-rw-r--r--src/dbus-automount.h1
-rw-r--r--src/dbus-common.c269
-rw-r--r--src/dbus-common.h19
-rw-r--r--src/dbus-device.c17
-rw-r--r--src/dbus-execute.c72
-rw-r--r--src/dbus-execute.h79
-rw-r--r--src/dbus-job.c27
-rw-r--r--src/dbus-manager.c138
-rw-r--r--src/dbus-mount.c43
-rw-r--r--src/dbus-path.c32
-rw-r--r--src/dbus-path.h2
-rw-r--r--src/dbus-service.c106
-rw-r--r--src/dbus-snapshot.c22
-rw-r--r--src/dbus-socket.c81
-rw-r--r--src/dbus-swap.c33
-rw-r--r--src/dbus-target.c8
-rw-r--r--src/dbus-timer.c30
-rw-r--r--src/dbus-unit.c228
-rw-r--r--src/dbus-unit.h77
-rw-r--r--src/dbus.c384
-rw-r--r--src/detect-virt.c8
-rw-r--r--src/device.c33
-rw-r--r--src/device.h2
-rw-r--r--src/execute.c305
-rw-r--r--src/execute.h10
-rw-r--r--src/exit-status.c6
-rw-r--r--src/exit-status.h4
-rw-r--r--src/fsck.c140
-rw-r--r--src/getty-generator.c98
-rw-r--r--src/gnome-ask-password-agent.vala10
-rw-r--r--src/hashmap.c135
-rw-r--r--src/hashmap.h1
-rw-r--r--src/hostname-setup.c8
-rw-r--r--src/hostname/.gitignore1
l---------src/hostname/Makefile1
-rw-r--r--src/hostname/hostnamed.c (renamed from src/hostnamed.c)59
-rw-r--r--src/hostname/org.freedesktop.hostname1.conf (renamed from src/org.freedesktop.hostname1.conf)0
-rw-r--r--src/hostname/org.freedesktop.hostname1.policy.in (renamed from src/org.freedesktop.hostname1.policy.in)0
-rw-r--r--src/hostname/org.freedesktop.hostname1.service (renamed from src/org.freedesktop.hostname1.service)0
-rw-r--r--src/initctl.c38
-rw-r--r--src/install.c22
-rw-r--r--src/job.c131
-rw-r--r--src/journal/.gitignore2
l---------src/journal/Makefile1
-rw-r--r--src/journal/cat.c181
-rw-r--r--src/journal/compress.c208
-rw-r--r--src/journal/compress.h38
-rw-r--r--src/journal/coredump.c271
-rw-r--r--src/journal/journal-def.h165
-rw-r--r--src/journal/journal-file.c2176
-rw-r--r--src/journal/journal-file.h125
-rw-r--r--src/journal/journal-internal.h84
-rw-r--r--src/journal/journal-rate-limit.c275
-rw-r--r--src/journal/journal-rate-limit.h34
-rw-r--r--src/journal/journal-send.c334
-rw-r--r--src/journal/journalctl.c308
-rw-r--r--src/journal/journald-gperf.gperf31
-rw-r--r--src/journal/journald.c2723
-rw-r--r--src/journal/journald.h86
-rw-r--r--src/journal/libsystemd-journal.pc.in19
-rw-r--r--src/journal/libsystemd-journal.sym45
-rw-r--r--src/journal/lookup3.c1003
-rw-r--r--src/journal/lookup3.h25
-rw-r--r--src/journal/sd-journal.c1616
-rw-r--r--src/journal/systemd-journald.conf25
-rw-r--r--src/journal/test-journal.c120
-rw-r--r--src/kmod-setup.c70
-rw-r--r--src/kmsg-syslogd.c511
-rw-r--r--src/label.c186
-rw-r--r--src/label.h7
-rw-r--r--src/libsystemd-daemon.pc.in (renamed from libsystemd-daemon.pc.in)0
-rw-r--r--src/libsystemd-id128.pc.in (renamed from units/sys-kernel-debug.automount)17
-rw-r--r--src/libsystemd-id128.sym21
l---------src/linux/Makefile1
-rw-r--r--src/load-dropin.c38
-rw-r--r--src/load-fragment-gperf.gperf.m4229
-rw-r--r--src/load-fragment.c1088
-rw-r--r--src/load-fragment.h58
-rw-r--r--src/locale-setup.c11
-rw-r--r--src/locale/.gitignore1
l---------src/locale/Makefile1
-rwxr-xr-xsrc/locale/generate-kbd-model-map33
-rw-r--r--src/locale/kbd-model-map72
-rw-r--r--src/locale/localed.c1437
-rw-r--r--src/locale/org.freedesktop.locale1.conf (renamed from src/org.freedesktop.locale1.conf)0
-rw-r--r--src/locale/org.freedesktop.locale1.policy.in (renamed from src/org.freedesktop.locale1.policy.in)10
-rw-r--r--src/locale/org.freedesktop.locale1.service (renamed from src/org.freedesktop.locale1.service)0
-rw-r--r--src/localed.c619
-rw-r--r--src/log.c260
-rw-r--r--src/log.h15
-rw-r--r--src/logger.c692
-rw-r--r--src/login/.gitignore3
-rw-r--r--src/login/70-uaccess.rules (renamed from src/70-uaccess.rules)0
-rw-r--r--src/login/71-seat.rules (renamed from src/71-seat.rules)0
-rw-r--r--src/login/73-seat-late.rules.in (renamed from src/73-seat-late.rules.in)2
l---------src/login/Makefile1
-rw-r--r--src/login/libsystemd-login.pc.in (renamed from libsystemd-login.pc.in)0
-rw-r--r--src/login/libsystemd-login.sym (renamed from src/libsystemd-login.sym)13
-rw-r--r--src/login/loginctl.c (renamed from src/loginctl.c)73
-rw-r--r--src/login/logind-acl.c (renamed from src/logind-acl.c)49
-rw-r--r--src/login/logind-acl.h (renamed from src/logind-acl.h)0
-rw-r--r--src/login/logind-dbus.c (renamed from src/logind-dbus.c)268
-rw-r--r--src/login/logind-device.c (renamed from src/logind-device.c)0
-rw-r--r--src/login/logind-device.h (renamed from src/logind-device.h)0
-rw-r--r--src/login/logind-gperf.gperf22
-rw-r--r--src/login/logind-seat-dbus.c (renamed from src/logind-seat-dbus.c)33
-rw-r--r--src/login/logind-seat.c (renamed from src/logind-seat.c)37
-rw-r--r--src/login/logind-seat.h (renamed from src/logind-seat.h)1
-rw-r--r--src/login/logind-session-dbus.c (renamed from src/logind-session-dbus.c)83
-rw-r--r--src/login/logind-session.c (renamed from src/logind-session.c)71
-rw-r--r--src/login/logind-session.h (renamed from src/logind-session.h)12
-rw-r--r--src/login/logind-user-dbus.c (renamed from src/logind-user-dbus.c)46
-rw-r--r--src/login/logind-user.c (renamed from src/logind-user.c)16
-rw-r--r--src/login/logind-user.h (renamed from src/logind-user.h)0
-rw-r--r--src/login/logind.c (renamed from src/logind.c)123
-rw-r--r--src/login/logind.h (renamed from src/logind.h)6
-rw-r--r--src/login/multi-seat-x.c195
-rw-r--r--src/login/org.freedesktop.login1.conf (renamed from src/org.freedesktop.login1.conf)32
-rw-r--r--src/login/org.freedesktop.login1.policy.in (renamed from src/org.freedesktop.login1.policy.in)0
-rw-r--r--src/login/org.freedesktop.login1.service (renamed from src/org.freedesktop.login1.service)0
-rw-r--r--src/login/pam-module.c (renamed from src/pam-module.c)76
-rw-r--r--src/login/sd-login.c (renamed from src/sd-login.c)304
-rw-r--r--src/login/sysfs-show.c (renamed from src/sysfs-show.c)3
-rw-r--r--src/login/systemd-logind.conf (renamed from src/systemd-logind.conf)0
-rw-r--r--src/login/test-login.c (renamed from src/test-login.c)53
-rw-r--r--src/login/uaccess.c (renamed from src/uaccess.c)7
-rw-r--r--src/login/user-sessions.c (renamed from src/user-sessions.c)4
-rw-r--r--src/logs-show.c668
-rw-r--r--src/logs-show.h57
-rw-r--r--src/machine-id-setup.c44
-rw-r--r--src/macro.h10
-rw-r--r--src/main.c339
-rw-r--r--src/manager.c408
-rw-r--r--src/manager.h19
-rw-r--r--src/missing.h4
-rw-r--r--src/modules-load.c79
-rw-r--r--src/mount-setup.c208
-rw-r--r--src/mount-setup.h6
-rw-r--r--src/mount.c409
-rw-r--r--src/mount.h20
-rw-r--r--src/namespace.c16
-rw-r--r--src/notify.c3
-rw-r--r--src/nspawn.c74
-rw-r--r--src/pager.c18
-rw-r--r--src/path-lookup.c4
-rw-r--r--src/path.c501
-rw-r--r--src/path.h28
-rw-r--r--src/polkit.c19
-rw-r--r--src/polkit.h1
-rw-r--r--src/quotacheck.c11
-rw-r--r--src/random-seed.c10
-rw-r--r--src/ratelimit.c9
-rw-r--r--src/ratelimit.h2
-rw-r--r--src/rc-local-generator.c107
l---------src/readahead/Makefile1
-rw-r--r--src/readahead/readahead-collect.c (renamed from src/readahead-collect.c)8
-rw-r--r--src/readahead/readahead-common.c (renamed from src/readahead-common.c)51
-rw-r--r--src/readahead/readahead-common.h (renamed from src/readahead-common.h)2
-rw-r--r--src/readahead/readahead-replay.c (renamed from src/readahead-replay.c)16
-rw-r--r--src/readahead/sd-readahead.c (renamed from src/sd-readahead.c)14
-rw-r--r--src/remount-api-vfs.c27
-rw-r--r--src/reply-password.c2
-rw-r--r--src/sd-daemon.c4
-rw-r--r--src/sd-id128.c221
-rw-r--r--src/selinux-setup.c73
-rw-r--r--src/selinux-setup.h4
-rw-r--r--src/service.c1095
-rw-r--r--src/service.h49
-rw-r--r--src/shutdown.c54
-rw-r--r--src/shutdownd.c13
-rw-r--r--src/snapshot.c25
-rw-r--r--src/snapshot.h2
-rw-r--r--src/socket-util.c2
-rw-r--r--src/socket.c456
-rw-r--r--src/socket.h28
-rw-r--r--src/special.h6
-rw-r--r--src/specifier.c2
-rw-r--r--src/strv.c61
-rw-r--r--src/strv.h2
-rw-r--r--src/swap.c239
-rw-r--r--src/swap.h20
-rw-r--r--src/sysctl.c2
-rw-r--r--src/system.conf5
-rw-r--r--src/systemadm.vala289
-rw-r--r--src/systemctl-bash-completion.sh145
-rw-r--r--src/systemctl.c379
-rwxr-xr-xsrc/systemd-analyze51
-rw-r--r--src/systemd-bash-completion.sh264
-rw-r--r--src/systemd.pc.in (renamed from systemd.pc.in)1
l---------src/systemd/Makefile1
-rw-r--r--src/systemd/sd-daemon.h (renamed from src/sd-daemon.h)9
-rw-r--r--src/systemd/sd-id128.h69
-rw-r--r--src/systemd/sd-journal.h114
-rw-r--r--src/systemd/sd-login.h (renamed from src/sd-login.h)48
-rw-r--r--src/systemd/sd-messages.h41
-rw-r--r--src/systemd/sd-readahead.h (renamed from src/sd-readahead.h)14
-rw-r--r--src/target.c13
-rw-r--r--src/target.h2
-rw-r--r--src/test-daemon.c2
-rw-r--r--src/test-engine.c2
-rw-r--r--src/test-id128.c52
-rw-r--r--src/timedate/.gitignore1
l---------src/timedate/Makefile1
-rw-r--r--src/timedate/org.freedesktop.timedate1.conf (renamed from src/org.freedesktop.timedate1.conf)0
-rw-r--r--src/timedate/org.freedesktop.timedate1.policy.in (renamed from src/org.freedesktop.timedate1.policy.in)11
-rw-r--r--src/timedate/org.freedesktop.timedate1.service (renamed from src/org.freedesktop.timedate1.service)0
-rw-r--r--src/timedate/timedated.c (renamed from src/timedated.c)406
-rw-r--r--src/timer.c132
-rw-r--r--src/timer.h16
-rw-r--r--src/tmpfiles.c555
-rw-r--r--src/tty-ask-password-agent.c24
-rw-r--r--src/umount.c7
-rw-r--r--src/unit-name.c7
-rw-r--r--src/unit.c898
-rw-r--r--src/unit.h81
-rw-r--r--src/update-utmp.c13
-rw-r--r--src/util.c1626
-rw-r--r--src/util.h84
-rw-r--r--src/utmp-wtmp.c34
-rw-r--r--src/utmp-wtmp.h6
l---------src/vconsole/Makefile1
-rw-r--r--src/vconsole/vconsole-setup.c (renamed from src/vconsole-setup.c)11
-rw-r--r--src/virt.c321
-rw-r--r--src/virt.h38
-rw-r--r--src/wraplabel.vala73
-rw-r--r--sysctl.d/.gitignore1
l---------sysctl.d/Makefile1
-rw-r--r--sysctl.d/coredump.conf.in10
-rw-r--r--test/Makefile (renamed from test2/Makefile)0
-rw-r--r--test/a.service (renamed from test2/a.service)0
-rw-r--r--test/b.service (renamed from test2/b.service)0
-rw-r--r--test/c.service (renamed from test2/c.service)0
-rw-r--r--test/d.service (renamed from test2/d.service)0
-rw-r--r--test/e.service (renamed from test2/e.service)0
-rw-r--r--test/f.service (renamed from test2/f.service)0
-rw-r--r--test/g.service (renamed from test2/g.service)0
-rw-r--r--test/h.service (renamed from test2/h.service)0
l---------test1/default.target1
-rw-r--r--test1/exec-demo.service9
-rw-r--r--test1/fifo-demo.service8
-rw-r--r--test1/fifo-demo.socket6
l---------test1/mail-transfer-agent.service1
l---------test1/mail-transfer-agent.socket1
-rw-r--r--test1/multiuser.target2
l---------test1/multiuser.target.wants/exec-demo.service1
l---------test1/multiuser.target.wants/fifo-demo.socket1
l---------test1/multiuser.target.wants/mail-transfer-agent.socket1
l---------test1/multiuser.target.wants/permissions.service1
l---------test1/multiuser.target.wants/systemd-logger.socket1
-rw-r--r--test1/permissions.service11
-rw-r--r--test1/postfix.service7
-rw-r--r--test1/postfix.socket13
-rw-r--r--test1/syslog.service5
-rw-r--r--test1/syslog.socket8
-rw-r--r--test1/systemd-logger.service13
-rw-r--r--test1/systemd-logger.socket5
l---------tmpfiles.d/Makefile1
-rw-r--r--tmpfiles.d/systemd.conf3
-rw-r--r--tmpfiles.d/tmp.conf12
-rw-r--r--tmpfiles.d/x11.conf2
-rw-r--r--units/.gitignore3
-rw-r--r--units/console-shell.service.m46
-rw-r--r--units/dev-hugepages.mount2
-rw-r--r--units/dev-mqueue.mount2
-rw-r--r--units/emergency.service3
-rw-r--r--units/fedora/prefdm.service3
-rw-r--r--units/fedora/rc-local.service7
-rw-r--r--units/frugalware/display-manager.service2
-rw-r--r--units/fsck-root.service.in2
-rw-r--r--units/fsck@.service.in2
-rw-r--r--units/getty@.service.m46
-rw-r--r--units/local-fs-pre.target11
-rw-r--r--units/mageia/prefdm.service21
-rw-r--r--units/mandriva/prefdm.service2
-rw-r--r--units/quotacheck.service.in1
-rw-r--r--units/quotaon.service1
-rw-r--r--units/remote-fs-pre.target12
-rw-r--r--units/remount-rootfs.service4
-rw-r--r--units/rescue.service.m411
-rw-r--r--units/serial-getty@.service.m44
-rw-r--r--units/suse/rc-local.service5
-rw-r--r--units/sys-fs-fuse-connections.mount (renamed from units/dev-mqueue.automount)11
-rw-r--r--units/sys-kernel-config.mount (renamed from units/dev-hugepages.automount)11
-rw-r--r--units/sys-kernel-debug.mount2
-rw-r--r--units/sys-kernel-security.automount15
-rw-r--r--units/sys-kernel-security.mount2
-rw-r--r--units/syslog.socket29
-rw-r--r--units/systemd-ask-password-console.path2
-rw-r--r--units/systemd-ask-password-console.service.in2
-rw-r--r--units/systemd-ask-password-plymouth.path5
-rw-r--r--units/systemd-ask-password-plymouth.service.in5
-rw-r--r--units/systemd-journald.service.in25
-rw-r--r--units/systemd-journald.socket (renamed from units/systemd-logger.socket)11
-rw-r--r--units/systemd-kmsg-syslogd.service.in19
-rw-r--r--units/systemd-logger.service.in20
-rw-r--r--units/systemd-logind.service.in7
-rw-r--r--units/systemd-readahead-collect.service.in1
-rw-r--r--units/systemd-readahead-replay.service.in1
-rw-r--r--units/systemd-remount-api-vfs.service.in4
-rw-r--r--units/systemd-shutdownd.socket2
-rw-r--r--units/systemd-vconsole-setup.service.in1
-rw-r--r--units/user@.service.in2
-rw-r--r--units/var-lock.mount1
-rw-r--r--units/var-run.mount1
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:
diff --git a/NEWS b/NEWS
new file mode 100644
index 0000000000..53ef982276
--- /dev/null
+++ b/NEWS
@@ -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
diff --git a/README b/README
index b7b0db763e..10cbc56208 100644
--- a/README
+++ b/README
@@ -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
diff --git a/TODO b/TODO
index a9b6939a30..cba2c8cfec 100644
--- a/TODO
+++ b/TODO
@@ -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 &lt;systemd/sd-daemon.h&gt;</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 &lt;systemd/sd-login.h&gt;</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 &lt;systemd/sd-daemon.h&gt;</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 &lt;systemd/sd-login.h&gt;</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 &lt;systemd/sd-daemon.h&gt;</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 &lt;systemd/sd-daemon.h&gt;</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 &lt;systemd/sd-login.h&gt;</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 &lt;systemd/sd-daemon.h&gt;</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 &lt;systemd/sd-login.h&gt;</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 &lt;systemd/sd-login.h&gt;</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 &lt;systemd/sd-login.h&gt;</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 &lt;systemd/sd-login.h&gt;</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>&lt;program&gt;.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>&lt;program&gt;.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 = &wr;
+ } 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, &ltype, &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, &section, sections, t, relaxed, p, userdata);
+ r = parse_line(filename,
+ ++line,
+ sections,
+ lookup,
+ table,
+ relaxed,
+ &section,
+ 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), &param) < 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;
}
diff --git a/src/job.c b/src/job.c
index 26e1a7ce99..e57286fe89 100644
--- a/src/job.c
+++ b/src/job.c
@@ -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;
-}
diff --git a/src/log.c b/src/log.c
index b8ce122f3d..c37ab22640 100644
--- a/src/log.c
+++ b/src/log.c
@@ -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);
diff --git a/src/log.h b/src/log.h
index c402afb8ea..7028a13275 100644
--- a/src/log.h
+++ b/src/log.h
@@ -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, &not_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, &not_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