From 610d801297fe7e46c319e6da4e6571aaded630dd Mon Sep 17 00:00:00 2001 From: Luke Shumaker Date: Wed, 17 Aug 2016 01:33:02 -0400 Subject: more --- src/Makefile | 4 +- src/grp-coredump/systemd-coredump/Makefile | 2 +- .../systemd-coredump/systemd-coredump.socket | 17 + .../systemd-coredump/systemd-coredump.tmpfiles | 10 + .../systemd-coredump/systemd-coredump@.service.in | 24 + .../systemd-hostnamed/systemd-hostnamed.service.in | 22 + src/grp-initprogs/Makefile | 44 + src/grp-initprogs/grp-sleep/Makefile | 30 + .../systemd-hibernate-resume-generator/Makefile | 40 + .../hibernate-resume-generator.c | 99 + .../grp-sleep/systemd-hibernate-resume/Makefile | 45 + .../systemd-hibernate-resume/hibernate-resume.c | 82 + .../systemd-hibernate-resume@.service.in | 20 + src/grp-initprogs/grp-sleep/systemd-sleep/Makefile | 58 + .../grp-sleep/systemd-sleep/hibernate.target | 13 + .../grp-sleep/systemd-sleep/hybrid-sleep.target | 13 + src/grp-initprogs/grp-sleep/systemd-sleep/sleep.c | 215 ++ .../grp-sleep/systemd-sleep/suspend.target | 13 + .../systemd-sleep/systemd-hibernate.service.in | 17 + .../systemd-sleep/systemd-hybrid-sleep.service.in | 17 + .../systemd-sleep/systemd-suspend.service.in | 17 + src/grp-initprogs/systemd-backlight/Makefile | 43 + src/grp-initprogs/systemd-backlight/backlight.c | 434 +++ .../systemd-backlight@.service.in | 22 + src/grp-initprogs/systemd-binfmt/Makefile | 56 + src/grp-initprogs/systemd-binfmt/binfmt.c | 203 ++ .../proc-sys-fs-binfmt_misc.automount | 18 + .../systemd-binfmt/proc-sys-fs-binfmt_misc.mount | 17 + .../systemd-binfmt/systemd-binfmt.service.in | 27 + src/grp-initprogs/systemd-detect-virt/Makefile | 36 + .../systemd-detect-virt/detect-virt.c | 169 ++ .../systemd-detect-virt.completion.bash | 40 + .../systemd-detect-virt.completion.zsh | 11 + src/grp-initprogs/systemd-firstboot/Makefile | 47 + src/grp-initprogs/systemd-firstboot/firstboot.c | 870 ++++++ .../systemd-firstboot/systemd-firstboot.service.in | 24 + src/grp-initprogs/systemd-fsck/Makefile | 33 + src/grp-initprogs/systemd-fsck/fsck.c | 487 +++ .../systemd-fsck/systemd-fsck@.service.in | 20 + src/grp-initprogs/systemd-modules-load/Makefile | 60 + .../kmod-static-nodes.service.in | 18 + .../systemd-modules-load/modules-load.c | 283 ++ .../systemd-modules-load.service.in | 27 + src/grp-initprogs/systemd-quotacheck/Makefile | 49 + src/grp-initprogs/systemd-quotacheck/quotacheck.c | 124 + .../systemd-quotacheck/quotaon.service.in | 19 + .../systemd-quotacheck.service.in | 20 + src/grp-initprogs/systemd-random-seed/Makefile | 50 + .../systemd-random-seed/random-seed.c | 176 ++ .../systemd-random-seed.service.in | 22 + src/grp-initprogs/systemd-rfkill/Makefile | 46 + src/grp-initprogs/systemd-rfkill/rfkill.c | 427 +++ .../systemd-rfkill/systemd-rfkill.service.in | 21 + .../systemd-rfkill/systemd-rfkill.socket | 19 + src/grp-initprogs/systemd-sysctl/50-default.sysctl | 40 + src/grp-initprogs/systemd-sysctl/Makefile | 33 + src/grp-initprogs/systemd-sysctl/sysctl.c | 287 ++ .../systemd-sysctl/systemd-sysctl.service.in | 21 + src/grp-initprogs/systemd-sysusers/Makefile | 53 + .../systemd-sysusers/basic.sysusers.in | 36 + .../systemd-sysusers/systemd-sysusers.service.in | 21 + src/grp-initprogs/systemd-sysusers/sysusers.c | 1919 ++++++++++++ src/grp-initprogs/systemd-tmpfiles/Makefile | 80 + src/grp-initprogs/systemd-tmpfiles/etc.tmpfiles.m4 | 19 + src/grp-initprogs/systemd-tmpfiles/home.tmpfiles | 11 + src/grp-initprogs/systemd-tmpfiles/legacy.tmpfiles | 27 + .../systemd-tmpfiles/systemd-nologin.tmpfiles | 11 + .../systemd-tmpfiles-clean.service.in | 19 + .../systemd-tmpfiles/systemd-tmpfiles-clean.timer | 14 + .../systemd-tmpfiles-setup-dev.service.in | 20 + .../systemd-tmpfiles-setup.service.in | 20 + .../systemd-tmpfiles.completion.zsh | 13 + src/grp-initprogs/systemd-tmpfiles/tmp.tmpfiles | 12 + src/grp-initprogs/systemd-tmpfiles/tmpfiles.c | 2344 +++++++++++++++ src/grp-initprogs/systemd-tmpfiles/var.tmpfiles | 22 + src/grp-initprogs/systemd-tmpfiles/x11.tmpfiles | 18 + src/grp-initprogs/systemd-update-done/Makefile | 34 + .../systemd-update-done.service.in | 21 + .../systemd-update-done/update-done.c | 115 + src/grp-initprogs/systemd-update-utmp/Makefile | 42 + .../systemd-update-utmp.service.in | 21 + .../systemd-update-utmp/update-utmp.c | 283 ++ src/grp-initprogs/systemd-user-sessions/Makefile | 48 + .../systemd-user-sessions.service.in | 17 + .../systemd-user-sessions/user-sessions.c | 84 + .../systemd-vconsole-setup/.gitignore | 1 + .../systemd-vconsole-setup/90-vconsole.rules.in | 10 + src/grp-initprogs/systemd-vconsole-setup/Makefile | 50 + .../systemd-vconsole-setup.service.in | 19 + .../systemd-vconsole-setup/vconsole-setup.c | 333 ++ src/grp-initutils/Makefile | 43 - src/grp-initutils/systemd-backlight/Makefile | 43 - src/grp-initutils/systemd-backlight/backlight.c | 434 --- src/grp-initutils/systemd-binfmt/Makefile | 56 - src/grp-initutils/systemd-binfmt/binfmt.c | 203 -- src/grp-initutils/systemd-detect-virt/Makefile | 36 - .../systemd-detect-virt/detect-virt.c | 169 -- .../systemd-detect-virt.completion.bash | 40 - .../systemd-detect-virt.completion.zsh | 11 - src/grp-initutils/systemd-firstboot/Makefile | 47 - src/grp-initutils/systemd-firstboot/firstboot.c | 870 ------ src/grp-initutils/systemd-fsck/Makefile | 33 - src/grp-initutils/systemd-fsck/fsck.c | 487 --- src/grp-initutils/systemd-quotacheck/Makefile | 48 - src/grp-initutils/systemd-quotacheck/quotacheck.c | 124 - src/grp-initutils/systemd-random-seed/Makefile | 50 - .../systemd-random-seed/random-seed.c | 176 -- src/grp-initutils/systemd-rfkill/Makefile | 46 - src/grp-initutils/systemd-rfkill/rfkill.c | 427 --- src/grp-initutils/systemd-sleep/Makefile | 35 - src/grp-initutils/systemd-sleep/sleep.c | 215 -- src/grp-initutils/systemd-sysctl/50-default.sysctl | 40 - src/grp-initutils/systemd-sysctl/Makefile | 33 - src/grp-initutils/systemd-sysctl/sysctl.c | 287 -- src/grp-initutils/systemd-sysusers/Makefile | 61 - .../systemd-sysusers/basic.sysusers.in | 36 - src/grp-initutils/systemd-sysusers/sysusers.c | 1919 ------------ src/grp-initutils/systemd-tmpfiles/Makefile | 84 - .../systemd-tmpfiles.completion.zsh | 13 - src/grp-initutils/systemd-tmpfiles/tmpfiles.c | 2344 --------------- src/grp-initutils/systemd-update-done/Makefile | 34 - .../systemd-update-done/update-done.c | 115 - src/grp-initutils/systemd-update-utmp/Makefile | 42 - .../systemd-update-utmp/update-utmp.c | 283 -- src/grp-initutils/systemd-user-sessions/Makefile | 48 - .../systemd-user-sessions/user-sessions.c | 84 - .../systemd-vconsole-setup/.gitignore | 1 - .../systemd-vconsole-setup/90-vconsole.rules.in | 10 - src/grp-initutils/systemd-vconsole-setup/Makefile | 50 - .../systemd-vconsole-setup/vconsole-setup.c | 333 -- src/grp-journal/Makefile | 3 + .../grp-remote/systemd-journal-gatewayd/Makefile | 3 + .../systemd-journal-gateway.sysusers | 9 + .../systemd-journal-gatewayd.service.in | 29 + .../systemd-journal-gatewayd.socket | 16 + .../grp-remote/systemd-journal-remote/Makefile | 3 + .../systemd-journal-remote.service.in | 25 + .../systemd-journal-remote.socket | 15 + .../systemd-journal-remote.sysusers | 8 + .../grp-remote/systemd-journal-upload/Makefile | 6 +- .../systemd-journal-upload.service.in | 27 + .../systemd-journal-upload.sysusers | 8 + .../grp-remote/systemd-remote.sysusers.m4 | 14 - src/grp-journal/journal-nocow.tmpfiles | 27 + src/grp-journal/journalctl/Makefile | 12 + .../systemd-journal-catalog-update.service.in | 21 + .../journalctl/systemd-journal-flush.service.in | 22 + src/grp-journal/libjournal-core/journald.conf | 41 - src/grp-journal/systemd-journald/Makefile | 15 +- src/grp-journal/systemd-journald/journald.conf | 41 + .../systemd-journald/systemd-journald-audit.socket | 20 + .../systemd-journald-dev-log.socket | 32 + .../systemd-journald/systemd-journald.service.in | 32 + .../systemd-journald/systemd-journald.socket | 26 + .../systemd-journald/systemd-journald.tmpfiles.m4 | 55 + .../systemd-localed/systemd-localed.service.in | 22 + src/grp-login/70-power-switch.rules | 18 - src/grp-login/70-uaccess.rules | 81 - src/grp-login/71-seat.rules.in | 54 - src/grp-login/73-seat-late.rules.in | 17 - src/grp-login/Makefile | 55 +- src/grp-login/liblogind-core/Makefile | 62 - src/grp-login/liblogind-core/logind-acl.c | 293 -- src/grp-login/liblogind-core/logind-acl.h | 56 - src/grp-login/liblogind-core/logind-action.c | 179 -- src/grp-login/liblogind-core/logind-action.h | 50 - src/grp-login/liblogind-core/logind-button.c | 290 -- src/grp-login/liblogind-core/logind-button.h | 44 - src/grp-login/liblogind-core/logind-core.c | 559 ---- src/grp-login/liblogind-core/logind-dbus.c | 3169 -------------------- src/grp-login/liblogind-core/logind-device.c | 125 - src/grp-login/liblogind-core/logind-device.h | 44 - src/grp-login/liblogind-core/logind-gperf.gperf | 39 - src/grp-login/liblogind-core/logind-inhibit.c | 486 --- src/grp-login/liblogind-core/logind-inhibit.h | 89 - src/grp-login/liblogind-core/logind-seat-dbus.c | 475 --- src/grp-login/liblogind-core/logind-seat.c | 696 ----- src/grp-login/liblogind-core/logind-seat.h | 96 - src/grp-login/liblogind-core/logind-session-dbus.c | 799 ----- .../liblogind-core/logind-session-device.c | 482 --- .../liblogind-core/logind-session-device.h | 54 - src/grp-login/liblogind-core/logind-session.c | 1273 -------- src/grp-login/liblogind-core/logind-session.h | 186 -- src/grp-login/liblogind-core/logind-user-dbus.c | 399 --- src/grp-login/liblogind-core/logind-user.c | 886 ------ src/grp-login/liblogind-core/logind-user.h | 94 - src/grp-login/liblogind-core/logind-utmp.c | 183 -- src/grp-login/liblogind-core/logind.h | 198 -- src/grp-login/logind.conf.in | 37 - src/grp-login/org.freedesktop.login1.conf | 274 -- src/grp-login/org.freedesktop.login1.policy.in | 323 -- src/grp-login/org.freedesktop.login1.service | 12 - src/grp-login/systemd-logind/70-power-switch.rules | 18 + src/grp-login/systemd-logind/70-uaccess.rules | 81 + src/grp-login/systemd-logind/71-seat.rules.in | 54 + src/grp-login/systemd-logind/73-seat-late.rules.in | 17 + src/grp-login/systemd-logind/Makefile | 91 +- src/grp-login/systemd-logind/logind-acl.c | 293 ++ src/grp-login/systemd-logind/logind-acl.h | 56 + src/grp-login/systemd-logind/logind-action.c | 179 ++ src/grp-login/systemd-logind/logind-action.h | 50 + src/grp-login/systemd-logind/logind-button.c | 290 ++ src/grp-login/systemd-logind/logind-button.h | 44 + src/grp-login/systemd-logind/logind-core.c | 559 ++++ src/grp-login/systemd-logind/logind-dbus.c | 3169 ++++++++++++++++++++ src/grp-login/systemd-logind/logind-device.c | 125 + src/grp-login/systemd-logind/logind-device.h | 44 + src/grp-login/systemd-logind/logind-gperf.c | 144 + src/grp-login/systemd-logind/logind-gperf.gperf | 39 + src/grp-login/systemd-logind/logind-inhibit.c | 486 +++ src/grp-login/systemd-logind/logind-inhibit.h | 89 + src/grp-login/systemd-logind/logind-seat-dbus.c | 475 +++ src/grp-login/systemd-logind/logind-seat.c | 696 +++++ src/grp-login/systemd-logind/logind-seat.h | 96 + src/grp-login/systemd-logind/logind-session-dbus.c | 799 +++++ .../systemd-logind/logind-session-device.c | 482 +++ .../systemd-logind/logind-session-device.h | 54 + src/grp-login/systemd-logind/logind-session.c | 1273 ++++++++ src/grp-login/systemd-logind/logind-session.h | 186 ++ src/grp-login/systemd-logind/logind-user-dbus.c | 399 +++ src/grp-login/systemd-logind/logind-user.c | 886 ++++++ src/grp-login/systemd-logind/logind-user.h | 94 + src/grp-login/systemd-logind/logind-utmp.c | 183 ++ src/grp-login/systemd-logind/logind.conf.in | 37 + src/grp-login/systemd-logind/logind.h | 198 ++ .../systemd-logind/org.freedesktop.login1.conf | 274 ++ .../org.freedesktop.login1.policy.in | 323 ++ .../systemd-logind/org.freedesktop.login1.service | 12 + .../systemd-logind/systemd-logind.service.in | 31 + src/grp-login/systemd-logind/systemd-user.pam.m4 | 12 + src/grp-login/systemd-logind/user.slice | 11 + src/grp-login/systemd-user.m4 | 12 - .../systemd-importd/systemd-importd.service.in | 19 + src/grp-machine/systemd-machined/machine.slice | 11 + .../systemd-machined/systemd-machined.service.in | 23 + src/grp-network/.gitignore | 3 - src/grp-network/Makefile | 30 +- src/grp-network/libnetworkd-core/.gitignore | 3 + src/grp-network/network/network.target | 13 + src/grp-network/org.freedesktop.network1.conf | 42 - src/grp-network/org.freedesktop.network1.service | 12 - .../systemd-networkd-wait-online/Makefile | 9 + .../systemd-networkd-wait-online.service.in | 23 + src/grp-network/systemd-networkd/Makefile | 31 + .../systemd-networkd/org.freedesktop.network1.conf | 42 + .../org.freedesktop.network1.service | 12 + .../systemd-networkd.service.m4.in | 37 + .../systemd-networkd/systemd-networkd.socket | 21 + .../systemd-networkd/systemd-networkd.tmpfiles | 12 + .../systemd-resolved.service.m4.in | 32 + .../systemd-resolved/systemd-resolved.tmpfiles | 10 + src/grp-system/systemd/Makefile | 6 +- src/grp-system/systemd/systemd-tmp.tmpfiles | 16 + src/grp-system/systemd/systemd.tmpfiles | 20 + .../systemd-timedated/systemd-timedated.service.in | 20 + src/grp-udev/50-udev-default.rules | 77 + src/grp-udev/60-block.rules | 11 + src/grp-udev/60-drm.rules | 3 + src/grp-udev/60-evdev.rules | 19 + src/grp-udev/60-persistent-alsa.rules | 14 + src/grp-udev/60-persistent-input.rules | 38 + src/grp-udev/60-persistent-storage-tape.rules | 25 + src/grp-udev/60-persistent-storage.rules | 87 + src/grp-udev/60-serial.rules | 26 + src/grp-udev/64-btrfs.rules | 13 + src/grp-udev/70-mouse.rules | 18 + src/grp-udev/75-net-description.rules | 14 + src/grp-udev/78-sound-card.rules | 89 + src/grp-udev/80-drivers.rules | 13 + src/grp-udev/80-net-setup-link.rules | 13 + src/grp-udev/99-systemd.rules.in | 67 + src/grp-udev/Makefile | 6 +- src/grp-udev/cdrom_id/60-cdrom_id.rules | 25 + src/grp-udev/libudev-core/Makefile | 3 - src/grp-udev/libudev-core/logind-acl.c | 2 +- src/grp-udev/libudev-core/logind-acl.h | 2 +- src/grp-udev/mtd_probe/75-probe_mtd.rules | 7 + src/grp-udev/rules/50-udev-default.rules | 77 - src/grp-udev/rules/60-block.rules | 11 - src/grp-udev/rules/60-cdrom_id.rules | 25 - src/grp-udev/rules/60-drm.rules | 3 - src/grp-udev/rules/60-evdev.rules | 19 - src/grp-udev/rules/60-persistent-alsa.rules | 14 - src/grp-udev/rules/60-persistent-input.rules | 38 - .../rules/60-persistent-storage-tape.rules | 25 - src/grp-udev/rules/60-persistent-storage.rules | 87 - src/grp-udev/rules/60-persistent-v4l.rules | 20 - src/grp-udev/rules/60-serial.rules | 26 - src/grp-udev/rules/64-btrfs.rules | 13 - src/grp-udev/rules/70-mouse.rules | 18 - src/grp-udev/rules/75-net-description.rules | 14 - src/grp-udev/rules/75-probe_mtd.rules | 7 - src/grp-udev/rules/78-sound-card.rules | 89 - src/grp-udev/rules/80-drivers.rules | 13 - src/grp-udev/rules/80-net-setup-link.rules | 13 - src/grp-udev/rules/99-systemd.rules.in | 67 - .../systemd-udevd/systemd-udevd.service.in | 26 + src/grp-udev/v4l_id/60-persistent-v4l.rules | 20 + src/libbasic/include/basic/basic.target | 20 + src/libsystemd/Makefile | 1 - src/libudev/Makefile | 2 - src/systemd-cryptsetup/cryptsetup-pre.target | 11 + src/systemd-cryptsetup/cryptsetup.target | 10 + src/systemd-dbus1-generator/Makefile | 3 +- src/systemd-hibernate-resume/Makefile | 62 - .../hibernate-resume-generator.c | 99 - src/systemd-hibernate-resume/hibernate-resume.c | 82 - src/systemd-initctl/systemd-initctl.service.in | 15 + src/systemd-initctl/systemd-initctl.socket | 17 + src/systemd-modules-load/Makefile | 60 - src/systemd-modules-load/modules-load.c | 283 -- src/systemd-nspawn/Makefile | 3 + src/systemd-nspawn/systemd-nspawn.tmpfiles | 23 + src/systemd-nspawn/systemd-nspawn@.service.in | 46 + .../systemd-remount-fs.service.in | 22 + src/systemd-timesyncd/systemd-timesyncd.service.in | 33 + 316 files changed, 24824 insertions(+), 22930 deletions(-) create mode 100644 src/grp-coredump/systemd-coredump/systemd-coredump.socket create mode 100644 src/grp-coredump/systemd-coredump/systemd-coredump.tmpfiles create mode 100644 src/grp-coredump/systemd-coredump/systemd-coredump@.service.in create mode 100644 src/grp-hostname/systemd-hostnamed/systemd-hostnamed.service.in create mode 100644 src/grp-initprogs/Makefile create mode 100644 src/grp-initprogs/grp-sleep/Makefile create mode 100644 src/grp-initprogs/grp-sleep/systemd-hibernate-resume-generator/Makefile create mode 100644 src/grp-initprogs/grp-sleep/systemd-hibernate-resume-generator/hibernate-resume-generator.c create mode 100644 src/grp-initprogs/grp-sleep/systemd-hibernate-resume/Makefile create mode 100644 src/grp-initprogs/grp-sleep/systemd-hibernate-resume/hibernate-resume.c create mode 100644 src/grp-initprogs/grp-sleep/systemd-hibernate-resume/systemd-hibernate-resume@.service.in create mode 100644 src/grp-initprogs/grp-sleep/systemd-sleep/Makefile create mode 100644 src/grp-initprogs/grp-sleep/systemd-sleep/hibernate.target create mode 100644 src/grp-initprogs/grp-sleep/systemd-sleep/hybrid-sleep.target create mode 100644 src/grp-initprogs/grp-sleep/systemd-sleep/sleep.c create mode 100644 src/grp-initprogs/grp-sleep/systemd-sleep/suspend.target create mode 100644 src/grp-initprogs/grp-sleep/systemd-sleep/systemd-hibernate.service.in create mode 100644 src/grp-initprogs/grp-sleep/systemd-sleep/systemd-hybrid-sleep.service.in create mode 100644 src/grp-initprogs/grp-sleep/systemd-sleep/systemd-suspend.service.in create mode 100644 src/grp-initprogs/systemd-backlight/Makefile create mode 100644 src/grp-initprogs/systemd-backlight/backlight.c create mode 100644 src/grp-initprogs/systemd-backlight/systemd-backlight@.service.in create mode 100644 src/grp-initprogs/systemd-binfmt/Makefile create mode 100644 src/grp-initprogs/systemd-binfmt/binfmt.c create mode 100644 src/grp-initprogs/systemd-binfmt/proc-sys-fs-binfmt_misc.automount create mode 100644 src/grp-initprogs/systemd-binfmt/proc-sys-fs-binfmt_misc.mount create mode 100644 src/grp-initprogs/systemd-binfmt/systemd-binfmt.service.in create mode 100644 src/grp-initprogs/systemd-detect-virt/Makefile create mode 100644 src/grp-initprogs/systemd-detect-virt/detect-virt.c create mode 100644 src/grp-initprogs/systemd-detect-virt/systemd-detect-virt.completion.bash create mode 100644 src/grp-initprogs/systemd-detect-virt/systemd-detect-virt.completion.zsh create mode 100644 src/grp-initprogs/systemd-firstboot/Makefile create mode 100644 src/grp-initprogs/systemd-firstboot/firstboot.c create mode 100644 src/grp-initprogs/systemd-firstboot/systemd-firstboot.service.in create mode 100644 src/grp-initprogs/systemd-fsck/Makefile create mode 100644 src/grp-initprogs/systemd-fsck/fsck.c create mode 100644 src/grp-initprogs/systemd-fsck/systemd-fsck@.service.in create mode 100644 src/grp-initprogs/systemd-modules-load/Makefile create mode 100644 src/grp-initprogs/systemd-modules-load/kmod-static-nodes.service.in create mode 100644 src/grp-initprogs/systemd-modules-load/modules-load.c create mode 100644 src/grp-initprogs/systemd-modules-load/systemd-modules-load.service.in create mode 100644 src/grp-initprogs/systemd-quotacheck/Makefile create mode 100644 src/grp-initprogs/systemd-quotacheck/quotacheck.c create mode 100644 src/grp-initprogs/systemd-quotacheck/quotaon.service.in create mode 100644 src/grp-initprogs/systemd-quotacheck/systemd-quotacheck.service.in create mode 100644 src/grp-initprogs/systemd-random-seed/Makefile create mode 100644 src/grp-initprogs/systemd-random-seed/random-seed.c create mode 100644 src/grp-initprogs/systemd-random-seed/systemd-random-seed.service.in create mode 100644 src/grp-initprogs/systemd-rfkill/Makefile create mode 100644 src/grp-initprogs/systemd-rfkill/rfkill.c create mode 100644 src/grp-initprogs/systemd-rfkill/systemd-rfkill.service.in create mode 100644 src/grp-initprogs/systemd-rfkill/systemd-rfkill.socket create mode 100644 src/grp-initprogs/systemd-sysctl/50-default.sysctl create mode 100644 src/grp-initprogs/systemd-sysctl/Makefile create mode 100644 src/grp-initprogs/systemd-sysctl/sysctl.c create mode 100644 src/grp-initprogs/systemd-sysctl/systemd-sysctl.service.in create mode 100644 src/grp-initprogs/systemd-sysusers/Makefile create mode 100644 src/grp-initprogs/systemd-sysusers/basic.sysusers.in create mode 100644 src/grp-initprogs/systemd-sysusers/systemd-sysusers.service.in create mode 100644 src/grp-initprogs/systemd-sysusers/sysusers.c create mode 100644 src/grp-initprogs/systemd-tmpfiles/Makefile create mode 100644 src/grp-initprogs/systemd-tmpfiles/etc.tmpfiles.m4 create mode 100644 src/grp-initprogs/systemd-tmpfiles/home.tmpfiles create mode 100644 src/grp-initprogs/systemd-tmpfiles/legacy.tmpfiles create mode 100644 src/grp-initprogs/systemd-tmpfiles/systemd-nologin.tmpfiles create mode 100644 src/grp-initprogs/systemd-tmpfiles/systemd-tmpfiles-clean.service.in create mode 100644 src/grp-initprogs/systemd-tmpfiles/systemd-tmpfiles-clean.timer create mode 100644 src/grp-initprogs/systemd-tmpfiles/systemd-tmpfiles-setup-dev.service.in create mode 100644 src/grp-initprogs/systemd-tmpfiles/systemd-tmpfiles-setup.service.in create mode 100644 src/grp-initprogs/systemd-tmpfiles/systemd-tmpfiles.completion.zsh create mode 100644 src/grp-initprogs/systemd-tmpfiles/tmp.tmpfiles create mode 100644 src/grp-initprogs/systemd-tmpfiles/tmpfiles.c create mode 100644 src/grp-initprogs/systemd-tmpfiles/var.tmpfiles create mode 100644 src/grp-initprogs/systemd-tmpfiles/x11.tmpfiles create mode 100644 src/grp-initprogs/systemd-update-done/Makefile create mode 100644 src/grp-initprogs/systemd-update-done/systemd-update-done.service.in create mode 100644 src/grp-initprogs/systemd-update-done/update-done.c create mode 100644 src/grp-initprogs/systemd-update-utmp/Makefile create mode 100644 src/grp-initprogs/systemd-update-utmp/systemd-update-utmp.service.in create mode 100644 src/grp-initprogs/systemd-update-utmp/update-utmp.c create mode 100644 src/grp-initprogs/systemd-user-sessions/Makefile create mode 100644 src/grp-initprogs/systemd-user-sessions/systemd-user-sessions.service.in create mode 100644 src/grp-initprogs/systemd-user-sessions/user-sessions.c create mode 100644 src/grp-initprogs/systemd-vconsole-setup/.gitignore create mode 100644 src/grp-initprogs/systemd-vconsole-setup/90-vconsole.rules.in create mode 100644 src/grp-initprogs/systemd-vconsole-setup/Makefile create mode 100644 src/grp-initprogs/systemd-vconsole-setup/systemd-vconsole-setup.service.in create mode 100644 src/grp-initprogs/systemd-vconsole-setup/vconsole-setup.c delete mode 100644 src/grp-initutils/Makefile delete mode 100644 src/grp-initutils/systemd-backlight/Makefile delete mode 100644 src/grp-initutils/systemd-backlight/backlight.c delete mode 100644 src/grp-initutils/systemd-binfmt/Makefile delete mode 100644 src/grp-initutils/systemd-binfmt/binfmt.c delete mode 100644 src/grp-initutils/systemd-detect-virt/Makefile delete mode 100644 src/grp-initutils/systemd-detect-virt/detect-virt.c delete mode 100644 src/grp-initutils/systemd-detect-virt/systemd-detect-virt.completion.bash delete mode 100644 src/grp-initutils/systemd-detect-virt/systemd-detect-virt.completion.zsh delete mode 100644 src/grp-initutils/systemd-firstboot/Makefile delete mode 100644 src/grp-initutils/systemd-firstboot/firstboot.c delete mode 100644 src/grp-initutils/systemd-fsck/Makefile delete mode 100644 src/grp-initutils/systemd-fsck/fsck.c delete mode 100644 src/grp-initutils/systemd-quotacheck/Makefile delete mode 100644 src/grp-initutils/systemd-quotacheck/quotacheck.c delete mode 100644 src/grp-initutils/systemd-random-seed/Makefile delete mode 100644 src/grp-initutils/systemd-random-seed/random-seed.c delete mode 100644 src/grp-initutils/systemd-rfkill/Makefile delete mode 100644 src/grp-initutils/systemd-rfkill/rfkill.c delete mode 100644 src/grp-initutils/systemd-sleep/Makefile delete mode 100644 src/grp-initutils/systemd-sleep/sleep.c delete mode 100644 src/grp-initutils/systemd-sysctl/50-default.sysctl delete mode 100644 src/grp-initutils/systemd-sysctl/Makefile delete mode 100644 src/grp-initutils/systemd-sysctl/sysctl.c delete mode 100644 src/grp-initutils/systemd-sysusers/Makefile delete mode 100644 src/grp-initutils/systemd-sysusers/basic.sysusers.in delete mode 100644 src/grp-initutils/systemd-sysusers/sysusers.c delete mode 100644 src/grp-initutils/systemd-tmpfiles/Makefile delete mode 100644 src/grp-initutils/systemd-tmpfiles/systemd-tmpfiles.completion.zsh delete mode 100644 src/grp-initutils/systemd-tmpfiles/tmpfiles.c delete mode 100644 src/grp-initutils/systemd-update-done/Makefile delete mode 100644 src/grp-initutils/systemd-update-done/update-done.c delete mode 100644 src/grp-initutils/systemd-update-utmp/Makefile delete mode 100644 src/grp-initutils/systemd-update-utmp/update-utmp.c delete mode 100644 src/grp-initutils/systemd-user-sessions/Makefile delete mode 100644 src/grp-initutils/systemd-user-sessions/user-sessions.c delete mode 100644 src/grp-initutils/systemd-vconsole-setup/.gitignore delete mode 100644 src/grp-initutils/systemd-vconsole-setup/90-vconsole.rules.in delete mode 100644 src/grp-initutils/systemd-vconsole-setup/Makefile delete mode 100644 src/grp-initutils/systemd-vconsole-setup/vconsole-setup.c create mode 100644 src/grp-journal/grp-remote/systemd-journal-gatewayd/systemd-journal-gateway.sysusers create mode 100644 src/grp-journal/grp-remote/systemd-journal-gatewayd/systemd-journal-gatewayd.service.in create mode 100644 src/grp-journal/grp-remote/systemd-journal-gatewayd/systemd-journal-gatewayd.socket create mode 100644 src/grp-journal/grp-remote/systemd-journal-remote/systemd-journal-remote.service.in create mode 100644 src/grp-journal/grp-remote/systemd-journal-remote/systemd-journal-remote.socket create mode 100644 src/grp-journal/grp-remote/systemd-journal-remote/systemd-journal-remote.sysusers create mode 100644 src/grp-journal/grp-remote/systemd-journal-upload/systemd-journal-upload.service.in create mode 100644 src/grp-journal/grp-remote/systemd-journal-upload/systemd-journal-upload.sysusers delete mode 100644 src/grp-journal/grp-remote/systemd-remote.sysusers.m4 create mode 100644 src/grp-journal/journal-nocow.tmpfiles create mode 100644 src/grp-journal/journalctl/systemd-journal-catalog-update.service.in create mode 100644 src/grp-journal/journalctl/systemd-journal-flush.service.in delete mode 100644 src/grp-journal/libjournal-core/journald.conf create mode 100644 src/grp-journal/systemd-journald/journald.conf create mode 100644 src/grp-journal/systemd-journald/systemd-journald-audit.socket create mode 100644 src/grp-journal/systemd-journald/systemd-journald-dev-log.socket create mode 100644 src/grp-journal/systemd-journald/systemd-journald.service.in create mode 100644 src/grp-journal/systemd-journald/systemd-journald.socket create mode 100644 src/grp-journal/systemd-journald/systemd-journald.tmpfiles.m4 create mode 100644 src/grp-locale/systemd-localed/systemd-localed.service.in delete mode 100644 src/grp-login/70-power-switch.rules delete mode 100644 src/grp-login/70-uaccess.rules delete mode 100644 src/grp-login/71-seat.rules.in delete mode 100644 src/grp-login/73-seat-late.rules.in delete mode 100644 src/grp-login/liblogind-core/Makefile delete mode 100644 src/grp-login/liblogind-core/logind-acl.c delete mode 100644 src/grp-login/liblogind-core/logind-acl.h delete mode 100644 src/grp-login/liblogind-core/logind-action.c delete mode 100644 src/grp-login/liblogind-core/logind-action.h delete mode 100644 src/grp-login/liblogind-core/logind-button.c delete mode 100644 src/grp-login/liblogind-core/logind-button.h delete mode 100644 src/grp-login/liblogind-core/logind-core.c delete mode 100644 src/grp-login/liblogind-core/logind-dbus.c delete mode 100644 src/grp-login/liblogind-core/logind-device.c delete mode 100644 src/grp-login/liblogind-core/logind-device.h delete mode 100644 src/grp-login/liblogind-core/logind-gperf.gperf delete mode 100644 src/grp-login/liblogind-core/logind-inhibit.c delete mode 100644 src/grp-login/liblogind-core/logind-inhibit.h delete mode 100644 src/grp-login/liblogind-core/logind-seat-dbus.c delete mode 100644 src/grp-login/liblogind-core/logind-seat.c delete mode 100644 src/grp-login/liblogind-core/logind-seat.h delete mode 100644 src/grp-login/liblogind-core/logind-session-dbus.c delete mode 100644 src/grp-login/liblogind-core/logind-session-device.c delete mode 100644 src/grp-login/liblogind-core/logind-session-device.h delete mode 100644 src/grp-login/liblogind-core/logind-session.c delete mode 100644 src/grp-login/liblogind-core/logind-session.h delete mode 100644 src/grp-login/liblogind-core/logind-user-dbus.c delete mode 100644 src/grp-login/liblogind-core/logind-user.c delete mode 100644 src/grp-login/liblogind-core/logind-user.h delete mode 100644 src/grp-login/liblogind-core/logind-utmp.c delete mode 100644 src/grp-login/liblogind-core/logind.h delete mode 100644 src/grp-login/logind.conf.in delete mode 100644 src/grp-login/org.freedesktop.login1.conf delete mode 100644 src/grp-login/org.freedesktop.login1.policy.in delete mode 100644 src/grp-login/org.freedesktop.login1.service create mode 100644 src/grp-login/systemd-logind/70-power-switch.rules create mode 100644 src/grp-login/systemd-logind/70-uaccess.rules create mode 100644 src/grp-login/systemd-logind/71-seat.rules.in create mode 100644 src/grp-login/systemd-logind/73-seat-late.rules.in create mode 100644 src/grp-login/systemd-logind/logind-acl.c create mode 100644 src/grp-login/systemd-logind/logind-acl.h create mode 100644 src/grp-login/systemd-logind/logind-action.c create mode 100644 src/grp-login/systemd-logind/logind-action.h create mode 100644 src/grp-login/systemd-logind/logind-button.c create mode 100644 src/grp-login/systemd-logind/logind-button.h create mode 100644 src/grp-login/systemd-logind/logind-core.c create mode 100644 src/grp-login/systemd-logind/logind-dbus.c create mode 100644 src/grp-login/systemd-logind/logind-device.c create mode 100644 src/grp-login/systemd-logind/logind-device.h create mode 100644 src/grp-login/systemd-logind/logind-gperf.c create mode 100644 src/grp-login/systemd-logind/logind-gperf.gperf create mode 100644 src/grp-login/systemd-logind/logind-inhibit.c create mode 100644 src/grp-login/systemd-logind/logind-inhibit.h create mode 100644 src/grp-login/systemd-logind/logind-seat-dbus.c create mode 100644 src/grp-login/systemd-logind/logind-seat.c create mode 100644 src/grp-login/systemd-logind/logind-seat.h create mode 100644 src/grp-login/systemd-logind/logind-session-dbus.c create mode 100644 src/grp-login/systemd-logind/logind-session-device.c create mode 100644 src/grp-login/systemd-logind/logind-session-device.h create mode 100644 src/grp-login/systemd-logind/logind-session.c create mode 100644 src/grp-login/systemd-logind/logind-session.h create mode 100644 src/grp-login/systemd-logind/logind-user-dbus.c create mode 100644 src/grp-login/systemd-logind/logind-user.c create mode 100644 src/grp-login/systemd-logind/logind-user.h create mode 100644 src/grp-login/systemd-logind/logind-utmp.c create mode 100644 src/grp-login/systemd-logind/logind.conf.in create mode 100644 src/grp-login/systemd-logind/logind.h create mode 100644 src/grp-login/systemd-logind/org.freedesktop.login1.conf create mode 100644 src/grp-login/systemd-logind/org.freedesktop.login1.policy.in create mode 100644 src/grp-login/systemd-logind/org.freedesktop.login1.service create mode 100644 src/grp-login/systemd-logind/systemd-logind.service.in create mode 100644 src/grp-login/systemd-logind/systemd-user.pam.m4 create mode 100644 src/grp-login/systemd-logind/user.slice delete mode 100644 src/grp-login/systemd-user.m4 create mode 100644 src/grp-machine/grp-import/systemd-importd/systemd-importd.service.in create mode 100644 src/grp-machine/systemd-machined/machine.slice create mode 100644 src/grp-machine/systemd-machined/systemd-machined.service.in delete mode 100644 src/grp-network/.gitignore create mode 100644 src/grp-network/libnetworkd-core/.gitignore create mode 100644 src/grp-network/network/network.target delete mode 100644 src/grp-network/org.freedesktop.network1.conf delete mode 100644 src/grp-network/org.freedesktop.network1.service create mode 100644 src/grp-network/systemd-networkd-wait-online/systemd-networkd-wait-online.service.in create mode 100644 src/grp-network/systemd-networkd/org.freedesktop.network1.conf create mode 100644 src/grp-network/systemd-networkd/org.freedesktop.network1.service create mode 100644 src/grp-network/systemd-networkd/systemd-networkd.service.m4.in create mode 100644 src/grp-network/systemd-networkd/systemd-networkd.socket create mode 100644 src/grp-network/systemd-networkd/systemd-networkd.tmpfiles create mode 100644 src/grp-resolve/systemd-resolved/systemd-resolved.service.m4.in create mode 100644 src/grp-resolve/systemd-resolved/systemd-resolved.tmpfiles create mode 100644 src/grp-system/systemd/systemd-tmp.tmpfiles create mode 100644 src/grp-system/systemd/systemd.tmpfiles create mode 100644 src/grp-timedate/systemd-timedated/systemd-timedated.service.in create mode 100644 src/grp-udev/50-udev-default.rules create mode 100644 src/grp-udev/60-block.rules create mode 100644 src/grp-udev/60-drm.rules create mode 100644 src/grp-udev/60-evdev.rules create mode 100644 src/grp-udev/60-persistent-alsa.rules create mode 100644 src/grp-udev/60-persistent-input.rules create mode 100644 src/grp-udev/60-persistent-storage-tape.rules create mode 100644 src/grp-udev/60-persistent-storage.rules create mode 100644 src/grp-udev/60-serial.rules create mode 100644 src/grp-udev/64-btrfs.rules create mode 100644 src/grp-udev/70-mouse.rules create mode 100644 src/grp-udev/75-net-description.rules create mode 100644 src/grp-udev/78-sound-card.rules create mode 100644 src/grp-udev/80-drivers.rules create mode 100644 src/grp-udev/80-net-setup-link.rules create mode 100644 src/grp-udev/99-systemd.rules.in create mode 100644 src/grp-udev/cdrom_id/60-cdrom_id.rules create mode 100644 src/grp-udev/mtd_probe/75-probe_mtd.rules delete mode 100644 src/grp-udev/rules/50-udev-default.rules delete mode 100644 src/grp-udev/rules/60-block.rules delete mode 100644 src/grp-udev/rules/60-cdrom_id.rules delete mode 100644 src/grp-udev/rules/60-drm.rules delete mode 100644 src/grp-udev/rules/60-evdev.rules delete mode 100644 src/grp-udev/rules/60-persistent-alsa.rules delete mode 100644 src/grp-udev/rules/60-persistent-input.rules delete mode 100644 src/grp-udev/rules/60-persistent-storage-tape.rules delete mode 100644 src/grp-udev/rules/60-persistent-storage.rules delete mode 100644 src/grp-udev/rules/60-persistent-v4l.rules delete mode 100644 src/grp-udev/rules/60-serial.rules delete mode 100644 src/grp-udev/rules/64-btrfs.rules delete mode 100644 src/grp-udev/rules/70-mouse.rules delete mode 100644 src/grp-udev/rules/75-net-description.rules delete mode 100644 src/grp-udev/rules/75-probe_mtd.rules delete mode 100644 src/grp-udev/rules/78-sound-card.rules delete mode 100644 src/grp-udev/rules/80-drivers.rules delete mode 100644 src/grp-udev/rules/80-net-setup-link.rules delete mode 100644 src/grp-udev/rules/99-systemd.rules.in create mode 100644 src/grp-udev/systemd-udevd/systemd-udevd.service.in create mode 100644 src/grp-udev/v4l_id/60-persistent-v4l.rules create mode 100644 src/libbasic/include/basic/basic.target create mode 100644 src/systemd-cryptsetup/cryptsetup-pre.target create mode 100644 src/systemd-cryptsetup/cryptsetup.target delete mode 100644 src/systemd-hibernate-resume/Makefile delete mode 100644 src/systemd-hibernate-resume/hibernate-resume-generator.c delete mode 100644 src/systemd-hibernate-resume/hibernate-resume.c create mode 100644 src/systemd-initctl/systemd-initctl.service.in create mode 100644 src/systemd-initctl/systemd-initctl.socket delete mode 100644 src/systemd-modules-load/Makefile delete mode 100644 src/systemd-modules-load/modules-load.c create mode 100644 src/systemd-nspawn/systemd-nspawn.tmpfiles create mode 100644 src/systemd-nspawn/systemd-nspawn@.service.in create mode 100644 src/systemd-remount-fs/systemd-remount-fs.service.in create mode 100644 src/systemd-timesyncd/systemd-timesyncd.service.in (limited to 'src') diff --git a/src/Makefile b/src/Makefile index 58e747aaff..2ea58046bc 100644 --- a/src/Makefile +++ b/src/Makefile @@ -27,7 +27,7 @@ nested.subdirs += busctl nested.subdirs += grp-boot nested.subdirs += grp-coredump nested.subdirs += grp-hostname -nested.subdirs += grp-initutils +nested.subdirs += grp-initprogs nested.subdirs += grp-journal nested.subdirs += grp-locale nested.subdirs += grp-login @@ -54,10 +54,8 @@ nested.subdirs += systemd-dbus1-generator nested.subdirs += systemd-debug-generator nested.subdirs += systemd-getty-generator nested.subdirs += systemd-gpt-auto-generator -nested.subdirs += systemd-hibernate-resume nested.subdirs += systemd-initctl nested.subdirs += systemd-machine-id-setup -nested.subdirs += systemd-modules-load nested.subdirs += systemd-nspawn nested.subdirs += systemd-rc-local-generator nested.subdirs += systemd-remount-fs diff --git a/src/grp-coredump/systemd-coredump/Makefile b/src/grp-coredump/systemd-coredump/Makefile index 54fe3aeb0e..108186488c 100644 --- a/src/grp-coredump/systemd-coredump/Makefile +++ b/src/grp-coredump/systemd-coredump/Makefile @@ -75,7 +75,7 @@ CLEANFILES += \ endif # ENABLE_COREDUMP EXTRA_DIST += \ - sysctl.d/50-coredump.conf.in \ + sysctl.d/50-coredump.sysctl.in \ units/systemd-coredump@.service.in sd.CPPFLAGS += -I$(topsrcdir)/src/grp-journal/libjournal-core diff --git a/src/grp-coredump/systemd-coredump/systemd-coredump.socket b/src/grp-coredump/systemd-coredump/systemd-coredump.socket new file mode 100644 index 0000000000..4cb2460471 --- /dev/null +++ b/src/grp-coredump/systemd-coredump/systemd-coredump.socket @@ -0,0 +1,17 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Process Core Dump Socket +Documentation=man:systemd-coredump(8) +DefaultDependencies=no + +[Socket] +ListenSequentialPacket=/run/systemd/coredump +SocketMode=0600 +Accept=yes +MaxConnections=16 diff --git a/src/grp-coredump/systemd-coredump/systemd-coredump.tmpfiles b/src/grp-coredump/systemd-coredump/systemd-coredump.tmpfiles new file mode 100644 index 0000000000..02b052583d --- /dev/null +++ b/src/grp-coredump/systemd-coredump/systemd-coredump.tmpfiles @@ -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 Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +# See tmpfiles.d(5) for details + +d /var/lib/systemd/coredump 0755 root root 3d diff --git a/src/grp-coredump/systemd-coredump/systemd-coredump@.service.in b/src/grp-coredump/systemd-coredump/systemd-coredump@.service.in new file mode 100644 index 0000000000..588c8d629c --- /dev/null +++ b/src/grp-coredump/systemd-coredump/systemd-coredump@.service.in @@ -0,0 +1,24 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Process Core Dump +Documentation=man:systemd-coredump(8) +DefaultDependencies=no +RequiresMountsFor=/var/lib/systemd/coredump +Conflicts=shutdown.target +After=systemd-remount-fs.service systemd-journald.socket +Requires=systemd-journald.socket +Before=shutdown.target + +[Service] +ExecStart=-@rootlibexecdir@/systemd-coredump +Nice=9 +OOMScoreAdjust=500 +PrivateNetwork=yes +ProtectSystem=full +RuntimeMaxSec=5min diff --git a/src/grp-hostname/systemd-hostnamed/systemd-hostnamed.service.in b/src/grp-hostname/systemd-hostnamed/systemd-hostnamed.service.in new file mode 100644 index 0000000000..b7079e4a7c --- /dev/null +++ b/src/grp-hostname/systemd-hostnamed/systemd-hostnamed.service.in @@ -0,0 +1,22 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Hostname Service +Documentation=man:systemd-hostnamed.service(8) man:hostname(5) man:machine-info(5) +Documentation=http://www.freedesktop.org/wiki/Software/systemd/hostnamed + +[Service] +ExecStart=@rootlibexecdir@/systemd-hostnamed +BusName=org.freedesktop.hostname1 +CapabilityBoundingSet=CAP_SYS_ADMIN +WatchdogSec=3min +PrivateTmp=yes +PrivateDevices=yes +PrivateNetwork=yes +ProtectSystem=yes +ProtectHome=yes diff --git a/src/grp-initprogs/Makefile b/src/grp-initprogs/Makefile new file mode 100644 index 0000000000..2cf2214e2a --- /dev/null +++ b/src/grp-initprogs/Makefile @@ -0,0 +1,44 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +nested.subdirs += grp-sleep +nested.subdirs += systemd-backlight +nested.subdirs += systemd-binfmt +nested.subdirs += systemd-detect-virt +nested.subdirs += systemd-firstboot +nested.subdirs += systemd-fsck +nested.subdirs += systemd-modules-load +nested.subdirs += systemd-quotacheck +nested.subdirs += systemd-random-seed +nested.subdirs += systemd-rfkill +nested.subdirs += systemd-sysctl +nested.subdirs += systemd-sysusers +nested.subdirs += systemd-tmpfiles +nested.subdirs += systemd-update-done +nested.subdirs += systemd-update-utmp +nested.subdirs += systemd-user-sessions +nested.subdirs += systemd-vconsole-setup + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-initprogs/grp-sleep/Makefile b/src/grp-initprogs/grp-sleep/Makefile new file mode 100644 index 0000000000..5a3a87d2bf --- /dev/null +++ b/src/grp-initprogs/grp-sleep/Makefile @@ -0,0 +1,30 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +nested.subdirs += systemd-hibernate-resume +nested.subdirs += systemd-hibernate-resume-generator +nested.subdirs += systemd-sleep + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-initprogs/grp-sleep/systemd-hibernate-resume-generator/Makefile b/src/grp-initprogs/grp-sleep/systemd-hibernate-resume-generator/Makefile new file mode 100644 index 0000000000..8c0eba2b1a --- /dev/null +++ b/src/grp-initprogs/grp-sleep/systemd-hibernate-resume-generator/Makefile @@ -0,0 +1,40 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +ifneq ($(ENABLE_HIBERNATE),) +systemgenerator_PROGRAMS += \ + systemd-hibernate-resume-generator + +systemd_hibernate_resume_generator_SOURCES = \ + src/hibernate-resume/hibernate-resume-generator.c + +systemd_hibernate_resume_generator_LDADD = \ + libshared.la + +endif # ENABLE_HIBERNATE + +sd.CPPFLAGS += -DSYSTEM_DATA_UNIT_PATH=\"$(systemunitdir)\" + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-initprogs/grp-sleep/systemd-hibernate-resume-generator/hibernate-resume-generator.c b/src/grp-initprogs/grp-sleep/systemd-hibernate-resume-generator/hibernate-resume-generator.c new file mode 100644 index 0000000000..6380c297c2 --- /dev/null +++ b/src/grp-initprogs/grp-sleep/systemd-hibernate-resume-generator/hibernate-resume-generator.c @@ -0,0 +1,99 @@ +/*** + This file is part of systemd. + + Copyright 2014 Ivan Shapovalov + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include + +#include "basic/alloc-util.h" +#include "basic/log.h" +#include "basic/mkdir.h" +#include "basic/proc-cmdline.h" +#include "basic/special.h" +#include "basic/string-util.h" +#include "basic/unit-name.h" +#include "basic/util.h" +#include "shared/fstab-util.h" + +static const char *arg_dest = "/tmp"; +static char *arg_resume_dev = NULL; + +static int parse_proc_cmdline_item(const char *key, const char *value) { + + if (streq(key, "resume") && value) { + free(arg_resume_dev); + arg_resume_dev = fstab_node_to_udev_node(value); + if (!arg_resume_dev) + return log_oom(); + } + + return 0; +} + +static int process_resume(void) { + _cleanup_free_ char *name = NULL, *lnk = NULL; + int r; + + if (!arg_resume_dev) + return 0; + + r = unit_name_from_path_instance("systemd-hibernate-resume", arg_resume_dev, ".service", &name); + if (r < 0) + return log_error_errno(r, "Failed to generate unit name: %m"); + + lnk = strjoin(arg_dest, "/" SPECIAL_SYSINIT_TARGET ".wants/", name, NULL); + if (!lnk) + return log_oom(); + + mkdir_parents_label(lnk, 0755); + if (symlink(SYSTEM_DATA_UNIT_PATH "/systemd-hibernate-resume@.service", lnk) < 0) + return log_error_errno(errno, "Failed to create symlink %s: %m", lnk); + + return 0; +} + +int main(int argc, char *argv[]) { + int r = 0; + + if (argc > 1 && argc != 4) { + log_error("This program takes three or no arguments."); + return EXIT_FAILURE; + } + + if (argc > 1) + arg_dest = argv[1]; + + log_set_target(LOG_TARGET_SAFE); + log_parse_environment(); + log_open(); + + umask(0022); + + /* Don't even consider resuming outside of initramfs. */ + if (!in_initrd()) + return EXIT_SUCCESS; + + r = parse_proc_cmdline(parse_proc_cmdline_item); + if (r < 0) + log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m"); + + r = process_resume(); + free(arg_resume_dev); + + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/grp-initprogs/grp-sleep/systemd-hibernate-resume/Makefile b/src/grp-initprogs/grp-sleep/systemd-hibernate-resume/Makefile new file mode 100644 index 0000000000..3b41b82e4b --- /dev/null +++ b/src/grp-initprogs/grp-sleep/systemd-hibernate-resume/Makefile @@ -0,0 +1,45 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +ifneq ($(ENABLE_HIBERNATE),) + +rootlibexec_PROGRAMS += \ + systemd-hibernate-resume + +systemd_hibernate_resume_SOURCES = \ + src/hibernate-resume/hibernate-resume.c + +systemd_hibernate_resume_LDADD = \ + libshared.la + +nodist_systemunit_DATA += \ + units/systemd-hibernate-resume@.service + +endif # ENABLE_HIBERNATE + +EXTRA_DIST += \ + units/systemd-hibernate-resume@.service.in + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-initprogs/grp-sleep/systemd-hibernate-resume/hibernate-resume.c b/src/grp-initprogs/grp-sleep/systemd-hibernate-resume/hibernate-resume.c new file mode 100644 index 0000000000..64d14bf64b --- /dev/null +++ b/src/grp-initprogs/grp-sleep/systemd-hibernate-resume/hibernate-resume.c @@ -0,0 +1,82 @@ +/*** + This file is part of systemd. + + Copyright 2014 Ivan Shapovalov + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/fileio.h" +#include "basic/log.h" +#include "basic/util.h" + +int main(int argc, char *argv[]) { + struct stat st; + const char *device; + _cleanup_free_ char *major_minor = NULL; + int r; + + if (argc != 2) { + log_error("This program expects one argument."); + return EXIT_FAILURE; + } + + log_set_target(LOG_TARGET_AUTO); + log_parse_environment(); + log_open(); + + umask(0022); + + /* Refuse to run unless we are in an initrd() */ + if (!in_initrd()) + return EXIT_SUCCESS; + + device = argv[1]; + + if (stat(device, &st) < 0) { + log_error_errno(errno, "Failed to stat '%s': %m", device); + return EXIT_FAILURE; + } + + if (!S_ISBLK(st.st_mode)) { + log_error("Resume device '%s' is not a block device.", device); + return EXIT_FAILURE; + } + + if (asprintf(&major_minor, "%d:%d", major(st.st_rdev), minor(st.st_rdev)) < 0) { + log_oom(); + return EXIT_FAILURE; + } + + r = write_string_file("/sys/power/resume", major_minor, WRITE_STRING_FILE_CREATE); + if (r < 0) { + log_error_errno(r, "Failed to write '%s' to /sys/power/resume: %m", major_minor); + return EXIT_FAILURE; + } + + /* + * The write above shall not return. + * + * However, failed resume is a normal condition (may mean that there is + * no hibernation image). + */ + + log_info("Could not resume from '%s' (%s).", device, major_minor); + return EXIT_SUCCESS; +} diff --git a/src/grp-initprogs/grp-sleep/systemd-hibernate-resume/systemd-hibernate-resume@.service.in b/src/grp-initprogs/grp-sleep/systemd-hibernate-resume/systemd-hibernate-resume@.service.in new file mode 100644 index 0000000000..65e8eb83f1 --- /dev/null +++ b/src/grp-initprogs/grp-sleep/systemd-hibernate-resume/systemd-hibernate-resume@.service.in @@ -0,0 +1,20 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Resume from hibernation using device %f +Documentation=man:systemd-hibernate-resume@.service(8) +DefaultDependencies=no +BindsTo=%i.device +Wants=local-fs-pre.target +After=%i.device +Before=local-fs-pre.target +ConditionPathExists=/etc/initrd-release + +[Service] +Type=oneshot +ExecStart=@rootlibexecdir@/systemd-hibernate-resume %f diff --git a/src/grp-initprogs/grp-sleep/systemd-sleep/Makefile b/src/grp-initprogs/grp-sleep/systemd-sleep/Makefile new file mode 100644 index 0000000000..ca484794a0 --- /dev/null +++ b/src/grp-initprogs/grp-sleep/systemd-sleep/Makefile @@ -0,0 +1,58 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +rootlibexec_PROGRAMS += systemd-sleep +systemd_sleep_SOURCES = \ + src/sleep/sleep.c + +systemd_sleep_LDADD = \ + libshared.la + +ifneq ($(ENABLE_HIBERNATE),) + +dist_systemunit_DATA += \ + units/hibernate.target \ + units/hybrid-sleep.target + +nodist_systemunit_DATA += \ + units/systemd-hibernate.service \ + units/systemd-hybrid-sleep.service + +endif # ENABLE_HIBERNATE + +dist_systemunit_DATA += \ + units/suspend.target \ + +nodist_systemunit_DATA += \ + units/systemd-suspend.service \ + +EXTRA_DIST += \ + units/systemd-suspend.service.in \ + units/systemd-hibernate.service.in \ + units/systemd-hybrid-sleep.service.in + +sd.CPPFLAGS += -DSYSTEM_SLEEP_PATH=\"$(systemsleepdir)\" + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-initprogs/grp-sleep/systemd-sleep/hibernate.target b/src/grp-initprogs/grp-sleep/systemd-sleep/hibernate.target new file mode 100644 index 0000000000..143eb59230 --- /dev/null +++ b/src/grp-initprogs/grp-sleep/systemd-sleep/hibernate.target @@ -0,0 +1,13 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Hibernate +Documentation=man:systemd.special(7) +DefaultDependencies=no +BindsTo=systemd-hibernate.service +After=systemd-hibernate.service diff --git a/src/grp-initprogs/grp-sleep/systemd-sleep/hybrid-sleep.target b/src/grp-initprogs/grp-sleep/systemd-sleep/hybrid-sleep.target new file mode 100644 index 0000000000..d2d3409225 --- /dev/null +++ b/src/grp-initprogs/grp-sleep/systemd-sleep/hybrid-sleep.target @@ -0,0 +1,13 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Hybrid Suspend+Hibernate +Documentation=man:systemd.special(7) +DefaultDependencies=no +BindsTo=systemd-hybrid-sleep.service +After=systemd-hybrid-sleep.service diff --git a/src/grp-initprogs/grp-sleep/systemd-sleep/sleep.c b/src/grp-initprogs/grp-sleep/systemd-sleep/sleep.c new file mode 100644 index 0000000000..e9e84bc893 --- /dev/null +++ b/src/grp-initprogs/grp-sleep/systemd-sleep/sleep.c @@ -0,0 +1,215 @@ +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + Copyright 2013 Zbigniew Jędrzejewski-Szmek + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include + +#include + +#include "basic/def.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/log.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/util.h" +#include "shared/sleep-config.h" + +static char* arg_verb = NULL; + +static int write_mode(char **modes) { + int r = 0; + char **mode; + + STRV_FOREACH(mode, modes) { + int k; + + k = write_string_file("/sys/power/disk", *mode, 0); + if (k == 0) + return 0; + + log_debug_errno(k, "Failed to write '%s' to /sys/power/disk: %m", + *mode); + if (r == 0) + r = k; + } + + if (r < 0) + log_error_errno(r, "Failed to write mode to /sys/power/disk: %m"); + + return r; +} + +static int write_state(FILE **f, char **states) { + char **state; + int r = 0; + + STRV_FOREACH(state, states) { + int k; + + k = write_string_stream(*f, *state, true); + if (k == 0) + return 0; + log_debug_errno(k, "Failed to write '%s' to /sys/power/state: %m", + *state); + if (r == 0) + r = k; + + fclose(*f); + *f = fopen("/sys/power/state", "we"); + if (!*f) + return log_error_errno(errno, "Failed to open /sys/power/state: %m"); + } + + return r; +} + +static int execute(char **modes, char **states) { + + char *arguments[] = { + NULL, + (char*) "pre", + arg_verb, + NULL + }; + static const char* const dirs[] = {SYSTEM_SLEEP_PATH, NULL}; + + int r; + _cleanup_fclose_ FILE *f = NULL; + + /* This file is opened first, so that if we hit an error, + * we can abort before modifying any state. */ + f = fopen("/sys/power/state", "we"); + if (!f) + return log_error_errno(errno, "Failed to open /sys/power/state: %m"); + + /* Configure the hibernation mode */ + r = write_mode(modes); + if (r < 0) + return r; + + execute_directories(dirs, DEFAULT_TIMEOUT_USEC, arguments); + + log_struct(LOG_INFO, + LOG_MESSAGE_ID(SD_MESSAGE_SLEEP_START), + LOG_MESSAGE("Suspending system..."), + "SLEEP=%s", arg_verb, + NULL); + + r = write_state(&f, states); + if (r < 0) + return r; + + log_struct(LOG_INFO, + LOG_MESSAGE_ID(SD_MESSAGE_SLEEP_STOP), + LOG_MESSAGE("System resumed."), + "SLEEP=%s", arg_verb, + NULL); + + arguments[1] = (char*) "post"; + execute_directories(dirs, DEFAULT_TIMEOUT_USEC, arguments); + + return r; +} + +static void help(void) { + printf("%s COMMAND\n\n" + "Suspend the system, hibernate the system, or both.\n\n" + "Commands:\n" + " -h --help Show this help and exit\n" + " --version Print version string and exit\n" + " suspend Suspend the system\n" + " hibernate Hibernate the system\n" + " hybrid-sleep Both hibernate and suspend the system\n" + , program_invocation_short_name); +} + +static int parse_argv(int argc, char *argv[]) { + enum { + ARG_VERSION = 0x100, + }; + + static const struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, ARG_VERSION }, + {} + }; + + int c; + + assert(argc >= 0); + assert(argv); + + while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) + switch(c) { + case 'h': + help(); + return 0; /* done */ + + case ARG_VERSION: + return version(); + + case '?': + return -EINVAL; + + default: + assert_not_reached("Unhandled option"); + } + + if (argc - optind != 1) { + log_error("Usage: %s COMMAND", + program_invocation_short_name); + return -EINVAL; + } + + arg_verb = argv[optind]; + + if (!streq(arg_verb, "suspend") && + !streq(arg_verb, "hibernate") && + !streq(arg_verb, "hybrid-sleep")) { + log_error("Unknown command '%s'.", arg_verb); + return -EINVAL; + } + + return 1 /* work to do */; +} + +int main(int argc, char *argv[]) { + _cleanup_strv_free_ char **modes = NULL, **states = NULL; + int r; + + log_set_target(LOG_TARGET_AUTO); + log_parse_environment(); + log_open(); + + r = parse_argv(argc, argv); + if (r <= 0) + goto finish; + + r = parse_sleep_config(arg_verb, &modes, &states); + if (r < 0) + goto finish; + + r = execute(modes, states); + +finish: + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/grp-initprogs/grp-sleep/systemd-sleep/suspend.target b/src/grp-initprogs/grp-sleep/systemd-sleep/suspend.target new file mode 100644 index 0000000000..f50cb2264f --- /dev/null +++ b/src/grp-initprogs/grp-sleep/systemd-sleep/suspend.target @@ -0,0 +1,13 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Suspend +Documentation=man:systemd.special(7) +DefaultDependencies=no +BindsTo=systemd-suspend.service +After=systemd-suspend.service diff --git a/src/grp-initprogs/grp-sleep/systemd-sleep/systemd-hibernate.service.in b/src/grp-initprogs/grp-sleep/systemd-sleep/systemd-hibernate.service.in new file mode 100644 index 0000000000..29d9b696a8 --- /dev/null +++ b/src/grp-initprogs/grp-sleep/systemd-sleep/systemd-hibernate.service.in @@ -0,0 +1,17 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Hibernate +Documentation=man:systemd-suspend.service(8) +DefaultDependencies=no +Requires=sleep.target +After=sleep.target + +[Service] +Type=oneshot +ExecStart=@rootlibexecdir@/systemd-sleep hibernate diff --git a/src/grp-initprogs/grp-sleep/systemd-sleep/systemd-hybrid-sleep.service.in b/src/grp-initprogs/grp-sleep/systemd-sleep/systemd-hybrid-sleep.service.in new file mode 100644 index 0000000000..914b686c36 --- /dev/null +++ b/src/grp-initprogs/grp-sleep/systemd-sleep/systemd-hybrid-sleep.service.in @@ -0,0 +1,17 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Hybrid Suspend+Hibernate +Documentation=man:systemd-suspend.service(8) +DefaultDependencies=no +Requires=sleep.target +After=sleep.target + +[Service] +Type=oneshot +ExecStart=@rootlibexecdir@/systemd-sleep hybrid-sleep diff --git a/src/grp-initprogs/grp-sleep/systemd-sleep/systemd-suspend.service.in b/src/grp-initprogs/grp-sleep/systemd-sleep/systemd-suspend.service.in new file mode 100644 index 0000000000..3a702d2e22 --- /dev/null +++ b/src/grp-initprogs/grp-sleep/systemd-sleep/systemd-suspend.service.in @@ -0,0 +1,17 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Suspend +Documentation=man:systemd-suspend.service(8) +DefaultDependencies=no +Requires=sleep.target +After=sleep.target + +[Service] +Type=oneshot +ExecStart=@rootlibexecdir@/systemd-sleep suspend diff --git a/src/grp-initprogs/systemd-backlight/Makefile b/src/grp-initprogs/systemd-backlight/Makefile new file mode 100644 index 0000000000..cf55ac4db9 --- /dev/null +++ b/src/grp-initprogs/systemd-backlight/Makefile @@ -0,0 +1,43 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +ifneq ($(ENABLE_BACKLIGHT),) +rootlibexec_PROGRAMS += \ + systemd-backlight + +nodist_systemunit_DATA += \ + units/systemd-backlight@.service + +systemd_backlight_SOURCES = \ + src/backlight/backlight.c + +systemd_backlight_LDADD = \ + libshared.la +endif # ENABLE_BACKLIGHT + +EXTRA_DIST += \ + units/systemd-backlight@.service.in + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-initprogs/systemd-backlight/backlight.c b/src/grp-initprogs/systemd-backlight/backlight.c new file mode 100644 index 0000000000..aa8d852024 --- /dev/null +++ b/src/grp-initprogs/systemd-backlight/backlight.c @@ -0,0 +1,434 @@ +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include "basic/alloc-util.h" +#include "basic/def.h" +#include "basic/escape.h" +#include "basic/fileio.h" +#include "basic/mkdir.h" +#include "basic/parse-util.h" +#include "basic/proc-cmdline.h" +#include "basic/string-util.h" +#include "basic/util.h" +#include "shared/udev-util.h" + +static struct udev_device *find_pci_or_platform_parent(struct udev_device *device) { + struct udev_device *parent; + const char *subsystem, *sysname; + + assert(device); + + parent = udev_device_get_parent(device); + if (!parent) + return NULL; + + subsystem = udev_device_get_subsystem(parent); + if (!subsystem) + return NULL; + + sysname = udev_device_get_sysname(parent); + if (!sysname) + return NULL; + + if (streq(subsystem, "drm")) { + const char *c; + + c = startswith(sysname, "card"); + if (!c) + return NULL; + + c += strspn(c, DIGITS); + if (*c == '-') { + /* A connector DRM device, let's ignore all but LVDS and eDP! */ + + if (!startswith(c, "-LVDS-") && + !startswith(c, "-Embedded DisplayPort-")) + return NULL; + } + + } else if (streq(subsystem, "pci")) { + const char *value; + + value = udev_device_get_sysattr_value(parent, "class"); + if (value) { + unsigned long class = 0; + + if (safe_atolu(value, &class) < 0) { + log_warning("Cannot parse PCI class %s of device %s:%s.", + value, subsystem, sysname); + return NULL; + } + + /* Graphics card */ + if (class == 0x30000) + return parent; + } + + } else if (streq(subsystem, "platform")) + return parent; + + return find_pci_or_platform_parent(parent); +} + +static bool same_device(struct udev_device *a, struct udev_device *b) { + assert(a); + assert(b); + + if (!streq_ptr(udev_device_get_subsystem(a), udev_device_get_subsystem(b))) + return false; + + if (!streq_ptr(udev_device_get_sysname(a), udev_device_get_sysname(b))) + return false; + + return true; +} + +static bool validate_device(struct udev *udev, struct udev_device *device) { + _cleanup_udev_enumerate_unref_ struct udev_enumerate *enumerate = NULL; + struct udev_list_entry *item = NULL, *first = NULL; + struct udev_device *parent; + const char *v, *subsystem; + int r; + + assert(udev); + assert(device); + + /* Verify whether we should actually care for a specific + * backlight device. For backlight devices there might be + * multiple ways to access the same control: "firmware" + * (i.e. ACPI), "platform" (i.e. via the machine's EC) and + * "raw" (via the graphics card). In general we should prefer + * "firmware" (i.e. ACPI) or "platform" access over "raw" + * access, in order not to confuse the BIOS/EC, and + * compatibility with possible low-level hotkey handling of + * screen brightness. The kernel will already make sure to + * expose only one of "firmware" and "platform" for the same + * device to userspace. However, we still need to make sure + * that we use "raw" only if no "firmware" or "platform" + * device for the same device exists. */ + + subsystem = udev_device_get_subsystem(device); + if (!streq_ptr(subsystem, "backlight")) + return true; + + v = udev_device_get_sysattr_value(device, "type"); + if (!streq_ptr(v, "raw")) + return true; + + parent = find_pci_or_platform_parent(device); + if (!parent) + return true; + + subsystem = udev_device_get_subsystem(parent); + if (!subsystem) + return true; + + enumerate = udev_enumerate_new(udev); + if (!enumerate) + return true; + + r = udev_enumerate_add_match_subsystem(enumerate, "backlight"); + if (r < 0) + return true; + + r = udev_enumerate_scan_devices(enumerate); + if (r < 0) + return true; + + first = udev_enumerate_get_list_entry(enumerate); + udev_list_entry_foreach(item, first) { + _cleanup_udev_device_unref_ struct udev_device *other; + struct udev_device *other_parent; + const char *other_subsystem; + + other = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item)); + if (!other) + return true; + + if (same_device(device, other)) + continue; + + v = udev_device_get_sysattr_value(other, "type"); + if (!streq_ptr(v, "platform") && !streq_ptr(v, "firmware")) + continue; + + /* OK, so there's another backlight device, and it's a + * platform or firmware device, so, let's see if we + * can verify it belongs to the same device as + * ours. */ + other_parent = find_pci_or_platform_parent(other); + if (!other_parent) + continue; + + if (same_device(parent, other_parent)) { + /* Both have the same PCI parent, that means + * we are out. */ + log_debug("Skipping backlight device %s, since device %s is on same PCI device and takes precedence.", + udev_device_get_sysname(device), + udev_device_get_sysname(other)); + return false; + } + + other_subsystem = udev_device_get_subsystem(other_parent); + if (streq_ptr(other_subsystem, "platform") && streq_ptr(subsystem, "pci")) { + /* The other is connected to the platform bus + * and we are a PCI device, that also means we + * are out. */ + log_debug("Skipping backlight device %s, since device %s is a platform device and takes precedence.", + udev_device_get_sysname(device), + udev_device_get_sysname(other)); + return false; + } + } + + return true; +} + +static unsigned get_max_brightness(struct udev_device *device) { + int r; + const char *max_brightness_str; + unsigned max_brightness; + + max_brightness_str = udev_device_get_sysattr_value(device, "max_brightness"); + if (!max_brightness_str) { + log_warning("Failed to read 'max_brightness' attribute."); + return 0; + } + + r = safe_atou(max_brightness_str, &max_brightness); + if (r < 0) { + log_warning_errno(r, "Failed to parse 'max_brightness' \"%s\": %m", max_brightness_str); + return 0; + } + + if (max_brightness <= 0) { + log_warning("Maximum brightness is 0, ignoring device."); + return 0; + } + + return max_brightness; +} + +/* Some systems turn the backlight all the way off at the lowest levels. + * clamp_brightness clamps the saved brightness to at least 1 or 5% of + * max_brightness in case of 'backlight' subsystem. This avoids preserving + * an unreadably dim screen, which would otherwise force the user to + * disable state restoration. */ +static void clamp_brightness(struct udev_device *device, char **value, unsigned max_brightness) { + int r; + unsigned brightness, new_brightness, min_brightness; + const char *subsystem; + + r = safe_atou(*value, &brightness); + if (r < 0) { + log_warning_errno(r, "Failed to parse brightness \"%s\": %m", *value); + return; + } + + subsystem = udev_device_get_subsystem(device); + if (streq_ptr(subsystem, "backlight")) + min_brightness = MAX(1U, max_brightness/20); + else + min_brightness = 0; + + new_brightness = CLAMP(brightness, min_brightness, max_brightness); + if (new_brightness != brightness) { + char *old_value = *value; + + r = asprintf(value, "%u", new_brightness); + if (r < 0) { + log_oom(); + return; + } + + log_info("Saved brightness %s %s to %s.", old_value, + new_brightness > brightness ? + "too low; increasing" : "too high; decreasing", + *value); + + free(old_value); + } +} + +int main(int argc, char *argv[]) { + _cleanup_udev_unref_ struct udev *udev = NULL; + _cleanup_udev_device_unref_ struct udev_device *device = NULL; + _cleanup_free_ char *saved = NULL, *ss = NULL, *escaped_ss = NULL, *escaped_sysname = NULL, *escaped_path_id = NULL; + const char *sysname, *path_id; + unsigned max_brightness; + int r; + + if (argc != 3) { + log_error("This program requires two arguments."); + return EXIT_FAILURE; + } + + log_set_target(LOG_TARGET_AUTO); + log_parse_environment(); + log_open(); + + umask(0022); + + r = mkdir_p("/var/lib/systemd/backlight", 0755); + if (r < 0) { + log_error_errno(r, "Failed to create backlight directory /var/lib/systemd/backlight: %m"); + return EXIT_FAILURE; + } + + udev = udev_new(); + if (!udev) { + log_oom(); + return EXIT_FAILURE; + } + + sysname = strchr(argv[2], ':'); + if (!sysname) { + log_error("Requires a subsystem and sysname pair specifying a backlight device."); + return EXIT_FAILURE; + } + + ss = strndup(argv[2], sysname - argv[2]); + if (!ss) { + log_oom(); + return EXIT_FAILURE; + } + + sysname++; + + if (!streq(ss, "backlight") && !streq(ss, "leds")) { + log_error("Not a backlight or LED device: '%s:%s'", ss, sysname); + return EXIT_FAILURE; + } + + errno = 0; + device = udev_device_new_from_subsystem_sysname(udev, ss, sysname); + if (!device) { + if (errno > 0) + log_error_errno(errno, "Failed to get backlight or LED device '%s:%s': %m", ss, sysname); + else + log_oom(); + + return EXIT_FAILURE; + } + + /* If max_brightness is 0, then there is no actual backlight + * device. This happens on desktops with Asus mainboards + * that load the eeepc-wmi module. + */ + max_brightness = get_max_brightness(device); + if (max_brightness == 0) + return EXIT_SUCCESS; + + escaped_ss = cescape(ss); + if (!escaped_ss) { + log_oom(); + return EXIT_FAILURE; + } + + escaped_sysname = cescape(sysname); + if (!escaped_sysname) { + log_oom(); + return EXIT_FAILURE; + } + + path_id = udev_device_get_property_value(device, "ID_PATH"); + if (path_id) { + escaped_path_id = cescape(path_id); + if (!escaped_path_id) { + log_oom(); + return EXIT_FAILURE; + } + + saved = strjoin("/var/lib/systemd/backlight/", escaped_path_id, ":", escaped_ss, ":", escaped_sysname, NULL); + } else + saved = strjoin("/var/lib/systemd/backlight/", escaped_ss, ":", escaped_sysname, NULL); + + if (!saved) { + log_oom(); + return EXIT_FAILURE; + } + + /* If there are multiple conflicting backlight devices, then + * their probing at boot-time might happen in any order. This + * means the validity checking of the device then is not + * reliable, since it might not see other devices conflicting + * with a specific backlight. To deal with this, we will + * actively delete backlight state files at shutdown (where + * device probing should be complete), so that the validity + * check at boot time doesn't have to be reliable. */ + + if (streq(argv[1], "load")) { + _cleanup_free_ char *value = NULL; + const char *clamp; + + if (shall_restore_state() == 0) + return EXIT_SUCCESS; + + if (!validate_device(udev, device)) + return EXIT_SUCCESS; + + r = read_one_line_file(saved, &value); + if (r < 0) { + + if (r == -ENOENT) + return EXIT_SUCCESS; + + log_error_errno(r, "Failed to read %s: %m", saved); + return EXIT_FAILURE; + } + + clamp = udev_device_get_property_value(device, "ID_BACKLIGHT_CLAMP"); + if (!clamp || parse_boolean(clamp) != 0) /* default to clamping */ + clamp_brightness(device, &value, max_brightness); + + r = udev_device_set_sysattr_value(device, "brightness", value); + if (r < 0) { + log_error_errno(r, "Failed to write system 'brightness' attribute: %m"); + return EXIT_FAILURE; + } + + } else if (streq(argv[1], "save")) { + const char *value; + + if (!validate_device(udev, device)) { + unlink(saved); + return EXIT_SUCCESS; + } + + value = udev_device_get_sysattr_value(device, "brightness"); + if (!value) { + log_error("Failed to read system 'brightness' attribute"); + return EXIT_FAILURE; + } + + r = write_string_file(saved, value, WRITE_STRING_FILE_CREATE); + if (r < 0) { + log_error_errno(r, "Failed to write %s: %m", saved); + return EXIT_FAILURE; + } + + } else { + log_error("Unknown verb %s.", argv[1]); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} diff --git a/src/grp-initprogs/systemd-backlight/systemd-backlight@.service.in b/src/grp-initprogs/systemd-backlight/systemd-backlight@.service.in new file mode 100644 index 0000000000..5e6706c11c --- /dev/null +++ b/src/grp-initprogs/systemd-backlight/systemd-backlight@.service.in @@ -0,0 +1,22 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Load/Save Screen Backlight Brightness of %i +Documentation=man:systemd-backlight@.service(8) +DefaultDependencies=no +RequiresMountsFor=/var/lib/systemd/backlight +Conflicts=shutdown.target +After=systemd-remount-fs.service +Before=sysinit.target shutdown.target + +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStart=@rootlibexecdir@/systemd-backlight load %i +ExecStop=@rootlibexecdir@/systemd-backlight save %i +TimeoutSec=90s diff --git a/src/grp-initprogs/systemd-binfmt/Makefile b/src/grp-initprogs/systemd-binfmt/Makefile new file mode 100644 index 0000000000..3e5c1ac270 --- /dev/null +++ b/src/grp-initprogs/systemd-binfmt/Makefile @@ -0,0 +1,56 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +ifneq ($(ENABLE_BINFMT),) +systemd_binfmt_SOURCES = \ + src/binfmt/binfmt.c + +systemd_binfmt_LDADD = \ + libshared.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 + +INSTALL_DIRS += \ + $(prefix)/lib/binfmt.d \ + $(sysconfdir)/binfmt.d + +SYSINIT_TARGET_WANTS += \ + systemd-binfmt.service \ + proc-sys-fs-binfmt_misc.automount + +endif # ENABLE_BINFMT + +EXTRA_DIST += \ + units/systemd-binfmt.service.in + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-initprogs/systemd-binfmt/binfmt.c b/src/grp-initprogs/systemd-binfmt/binfmt.c new file mode 100644 index 0000000000..39bbdb5335 --- /dev/null +++ b/src/grp-initprogs/systemd-binfmt/binfmt.c @@ -0,0 +1,203 @@ +/*** + 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 Lesser General Public License as published by + the Free Software Foundation; either version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/conf-files.h" +#include "basic/def.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/log.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/util.h" + +static const char conf_file_dirs[] = CONF_PATHS_NULSTR("binfmt.d"); + +static int delete_rule(const char *rule) { + _cleanup_free_ char *x = NULL, *fn = NULL; + char *e; + + assert(rule[0]); + + x = strdup(rule); + if (!x) + return log_oom(); + + e = strchrnul(x+1, x[0]); + *e = 0; + + fn = strappend("/proc/sys/fs/binfmt_misc/", x+1); + if (!fn) + return log_oom(); + + return write_string_file(fn, "-1", 0); +} + +static int apply_rule(const char *rule) { + int r; + + delete_rule(rule); + + r = write_string_file("/proc/sys/fs/binfmt_misc/register", rule, 0); + if (r < 0) + return log_error_errno(r, "Failed to add binary format: %m"); + + return 0; +} + +static int apply_file(const char *path, bool ignore_enoent) { + _cleanup_fclose_ FILE *f = NULL; + int r; + + assert(path); + + r = search_and_fopen_nulstr(path, "re", NULL, conf_file_dirs, &f); + if (r < 0) { + if (ignore_enoent && r == -ENOENT) + return 0; + + return log_error_errno(r, "Failed to open file '%s', ignoring: %m", path); + } + + log_debug("apply: %s", path); + for (;;) { + char l[LINE_MAX], *p; + int k; + + if (!fgets(l, sizeof(l), f)) { + if (feof(f)) + break; + + return log_error_errno(errno, "Failed to read file '%s', ignoring: %m", path); + } + + p = strstrip(l); + if (!*p) + continue; + if (strchr(COMMENTS "\n", *p)) + continue; + + k = apply_rule(p); + if (k < 0 && r == 0) + r = k; + } + + return r; +} + +static void help(void) { + printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n" + "Registers binary formats.\n\n" + " -h --help Show this help\n" + " --version Show package version\n" + , program_invocation_short_name); +} + +static int parse_argv(int argc, char *argv[]) { + + enum { + ARG_VERSION = 0x100, + }; + + static const struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, ARG_VERSION }, + {} + }; + + int c; + + assert(argc >= 0); + assert(argv); + + while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) + + switch (c) { + + case 'h': + help(); + return 0; + + case ARG_VERSION: + return version(); + + case '?': + return -EINVAL; + + default: + assert_not_reached("Unhandled option"); + } + + return 1; +} + +int main(int argc, char *argv[]) { + int r, k; + + 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); + + r = 0; + + if (argc > optind) { + int i; + + for (i = optind; i < argc; i++) { + k = apply_file(argv[i], false); + if (k < 0 && r == 0) + r = k; + } + } else { + _cleanup_strv_free_ char **files = NULL; + char **f; + + r = conf_files_list_nulstr(&files, ".conf", NULL, conf_file_dirs); + if (r < 0) { + log_error_errno(r, "Failed to enumerate binfmt.d files: %m"); + goto finish; + } + + /* Flush out all rules */ + write_string_file("/proc/sys/fs/binfmt_misc/status", "-1", 0); + + STRV_FOREACH(f, files) { + k = apply_file(*f, true); + if (k < 0 && r == 0) + r = k; + } + } + +finish: + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/grp-initprogs/systemd-binfmt/proc-sys-fs-binfmt_misc.automount b/src/grp-initprogs/systemd-binfmt/proc-sys-fs-binfmt_misc.automount new file mode 100644 index 0000000000..6be38937b1 --- /dev/null +++ b/src/grp-initprogs/systemd-binfmt/proc-sys-fs-binfmt_misc.automount @@ -0,0 +1,18 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Arbitrary Executable File Formats File System Automount Point +Documentation=https://www.kernel.org/doc/Documentation/binfmt_misc.txt +Documentation=http://www.freedesktop.org/wiki/Software/systemd/APIFileSystems +DefaultDependencies=no +Before=sysinit.target +ConditionPathExists=/proc/sys/fs/binfmt_misc/ +ConditionPathIsReadWrite=/proc/sys/ + +[Automount] +Where=/proc/sys/fs/binfmt_misc diff --git a/src/grp-initprogs/systemd-binfmt/proc-sys-fs-binfmt_misc.mount b/src/grp-initprogs/systemd-binfmt/proc-sys-fs-binfmt_misc.mount new file mode 100644 index 0000000000..8c7c386318 --- /dev/null +++ b/src/grp-initprogs/systemd-binfmt/proc-sys-fs-binfmt_misc.mount @@ -0,0 +1,17 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Arbitrary Executable File Formats File System +Documentation=https://www.kernel.org/doc/Documentation/binfmt_misc.txt +Documentation=http://www.freedesktop.org/wiki/Software/systemd/APIFileSystems +DefaultDependencies=no + +[Mount] +What=binfmt_misc +Where=/proc/sys/fs/binfmt_misc +Type=binfmt_misc diff --git a/src/grp-initprogs/systemd-binfmt/systemd-binfmt.service.in b/src/grp-initprogs/systemd-binfmt/systemd-binfmt.service.in new file mode 100644 index 0000000000..d53073ee61 --- /dev/null +++ b/src/grp-initprogs/systemd-binfmt/systemd-binfmt.service.in @@ -0,0 +1,27 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Set Up Additional Binary Formats +Documentation=man:systemd-binfmt.service(8) man:binfmt.d(5) +Documentation=https://www.kernel.org/doc/Documentation/binfmt_misc.txt +DefaultDependencies=no +Conflicts=shutdown.target +After=proc-sys-fs-binfmt_misc.automount +Before=sysinit.target shutdown.target +ConditionPathIsReadWrite=/proc/sys/ +ConditionDirectoryNotEmpty=|/lib/binfmt.d +ConditionDirectoryNotEmpty=|/usr/lib/binfmt.d +ConditionDirectoryNotEmpty=|/usr/local/lib/binfmt.d +ConditionDirectoryNotEmpty=|/etc/binfmt.d +ConditionDirectoryNotEmpty=|/run/binfmt.d + +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStart=@rootlibexecdir@/systemd-binfmt +TimeoutSec=90s diff --git a/src/grp-initprogs/systemd-detect-virt/Makefile b/src/grp-initprogs/systemd-detect-virt/Makefile new file mode 100644 index 0000000000..7b9b9f667b --- /dev/null +++ b/src/grp-initprogs/systemd-detect-virt/Makefile @@ -0,0 +1,36 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +bin_PROGRAMS += systemd-detect-virt +systemd_detect_virt_SOURCES = \ + src/detect-virt/detect-virt.c + +systemd_detect_virt_LDADD = \ + libshared.la + +INSTALL_EXEC_HOOKS += \ + systemd-detect-virt-install-hook + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-initprogs/systemd-detect-virt/detect-virt.c b/src/grp-initprogs/systemd-detect-virt/detect-virt.c new file mode 100644 index 0000000000..06cdab5297 --- /dev/null +++ b/src/grp-initprogs/systemd-detect-virt/detect-virt.c @@ -0,0 +1,169 @@ +/*** + 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 Lesser General Public License as published by + the Free Software Foundation; either version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include + +#include "basic/util.h" +#include "basic/virt.h" + +static bool arg_quiet = false; +static enum { + ANY_VIRTUALIZATION, + ONLY_VM, + ONLY_CONTAINER, + ONLY_CHROOT, +} arg_mode = ANY_VIRTUALIZATION; + +static void help(void) { + printf("%s [OPTIONS...]\n\n" + "Detect execution in a virtualized environment.\n\n" + " -h --help Show this help\n" + " --version Show package version\n" + " -c --container Only detect whether we are run in a container\n" + " -v --vm Only detect whether we are run in a VM\n" + " -r --chroot Detect whether we are run in a chroot() environment\n" + " -q --quiet Don't output anything, just set return value\n" + , program_invocation_short_name); +} + +static int parse_argv(int argc, char *argv[]) { + + enum { + ARG_VERSION = 0x100 + }; + + static const struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, ARG_VERSION }, + { "container", no_argument, NULL, 'c' }, + { "vm", no_argument, NULL, 'v' }, + { "chroot", no_argument, NULL, 'r' }, + { "quiet", no_argument, NULL, 'q' }, + {} + }; + + int c; + + assert(argc >= 0); + assert(argv); + + while ((c = getopt_long(argc, argv, "hqcvr", options, NULL)) >= 0) + + switch (c) { + + case 'h': + help(); + return 0; + + case ARG_VERSION: + return version(); + + case 'q': + arg_quiet = true; + break; + + case 'c': + arg_mode = ONLY_CONTAINER; + break; + + case 'v': + arg_mode = ONLY_VM; + break; + + case 'r': + arg_mode = ONLY_CHROOT; + break; + + case '?': + return -EINVAL; + + default: + assert_not_reached("Unhandled option"); + } + + if (optind < argc) { + log_error("%s takes no arguments.", program_invocation_short_name); + return -EINVAL; + } + + return 1; +} + +int main(int argc, char *argv[]) { + int r; + + /* This is mostly intended to be used for scripts which want + * to detect whether we are being run in a virtualized + * environment or not */ + + log_parse_environment(); + log_open(); + + r = parse_argv(argc, argv); + if (r <= 0) + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; + + switch (arg_mode) { + + case ONLY_VM: + r = detect_vm(); + if (r < 0) { + log_error_errno(r, "Failed to check for VM: %m"); + return EXIT_FAILURE; + } + + break; + + case ONLY_CONTAINER: + r = detect_container(); + if (r < 0) { + log_error_errno(r, "Failed to check for container: %m"); + return EXIT_FAILURE; + } + + break; + + case ONLY_CHROOT: + r = running_in_chroot(); + if (r < 0) { + log_error_errno(r, "Failed to check for chroot() environment: %m"); + return EXIT_FAILURE; + } + + return r ? EXIT_SUCCESS : EXIT_FAILURE; + + case ANY_VIRTUALIZATION: + default: + r = detect_virtualization(); + if (r < 0) { + log_error_errno(r, "Failed to check for virtualization: %m"); + return EXIT_FAILURE; + } + + break; + } + + if (!arg_quiet) + puts(virtualization_to_string(r)); + + return r != VIRTUALIZATION_NONE ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/src/grp-initprogs/systemd-detect-virt/systemd-detect-virt.completion.bash b/src/grp-initprogs/systemd-detect-virt/systemd-detect-virt.completion.bash new file mode 100644 index 0000000000..df06c29841 --- /dev/null +++ b/src/grp-initprogs/systemd-detect-virt/systemd-detect-virt.completion.bash @@ -0,0 +1,40 @@ +# systemd-detect-virt(1) completion -*- shell-script -*- +# +# This file is part of systemd. +# +# Copyright 2014 Thomas H.P. Andersen +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (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 Lesser General Public License +# along with systemd; If not, see . + +__contains_word() { + local w word=$1; shift + for w in "$@"; do + [[ $w = "$word" ]] && return + done +} + +_systemd_detect_virt() { + local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]} + local i verb comps + + local -A OPTS=( + [STANDALONE]='-h --help --version -c --container -v --vm -q --quiet' + ) + + _init_completion || return + + COMPREPLY=( $(compgen -W '${OPTS[*]}' -- "$cur") ) +} + +complete -F _systemd_detect_virt systemd-detect-virt diff --git a/src/grp-initprogs/systemd-detect-virt/systemd-detect-virt.completion.zsh b/src/grp-initprogs/systemd-detect-virt/systemd-detect-virt.completion.zsh new file mode 100644 index 0000000000..a0c7df727c --- /dev/null +++ b/src/grp-initprogs/systemd-detect-virt/systemd-detect-virt.completion.zsh @@ -0,0 +1,11 @@ +#compdef systemd-detect-virt + +local curcontext="$curcontext" state lstate line +_arguments \ + {-h,--help}'[Show this help]' \ + '--version[Show package version]' \ + {-c,--container}'[Only detect whether we are run in a container]' \ + {-v,--vm}'[Only detect whether we are run in a VM]' \ + {-q,--quiet}"[Don't output anything, just set return value]" + +#vim: set ft=zsh sw=4 ts=4 et diff --git a/src/grp-initprogs/systemd-firstboot/Makefile b/src/grp-initprogs/systemd-firstboot/Makefile new file mode 100644 index 0000000000..b94b344bb9 --- /dev/null +++ b/src/grp-initprogs/systemd-firstboot/Makefile @@ -0,0 +1,47 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +ifneq ($(ENABLE_FIRSTBOOT),) +systemd_firstboot_SOURCES = \ + src/firstboot/firstboot.c + +systemd_firstboot_LDADD = \ + libshared.la \ + -lcrypt + +rootbin_PROGRAMS += \ + systemd-firstboot + +nodist_systemunit_DATA += \ + units/systemd-firstboot.service + +SYSINIT_TARGET_WANTS += \ + systemd-firstboot.service +endif # ENABLE_FIRSTBOOT + +EXTRA_DIST += \ + units/systemd-firstboot.service.in + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-initprogs/systemd-firstboot/firstboot.c b/src/grp-initprogs/systemd-firstboot/firstboot.c new file mode 100644 index 0000000000..d2059a943f --- /dev/null +++ b/src/grp-initprogs/systemd-firstboot/firstboot.c @@ -0,0 +1,870 @@ +/*** + This file is part of systemd. + + Copyright 2014 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/copy.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/fs-util.h" +#include "basic/hostname-util.h" +#include "basic/locale-util.h" +#include "basic/mkdir.h" +#include "basic/parse-util.h" +#include "basic/path-util.h" +#include "basic/random-util.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/terminal-util.h" +#include "basic/time-util.h" +#include "basic/umask-util.h" +#include "basic/user-util.h" +#include "shared/ask-password-api.h" + +static char *arg_root = NULL; +static char *arg_locale = NULL; /* $LANG */ +static char *arg_locale_messages = NULL; /* $LC_MESSAGES */ +static char *arg_timezone = NULL; +static char *arg_hostname = NULL; +static sd_id128_t arg_machine_id = {}; +static char *arg_root_password = NULL; +static bool arg_prompt_locale = false; +static bool arg_prompt_timezone = false; +static bool arg_prompt_hostname = false; +static bool arg_prompt_root_password = false; +static bool arg_copy_locale = false; +static bool arg_copy_timezone = false; +static bool arg_copy_root_password = false; + +static bool press_any_key(void) { + char k = 0; + bool need_nl = true; + + printf("-- Press any key to proceed --"); + fflush(stdout); + + (void) read_one_char(stdin, &k, USEC_INFINITY, &need_nl); + + if (need_nl) + putchar('\n'); + + return k != 'q'; +} + +static void print_welcome(void) { + _cleanup_free_ char *pretty_name = NULL; + const char *os_release = NULL; + static bool done = false; + int r; + + if (done) + return; + + os_release = prefix_roota(arg_root, "/etc/os-release"); + r = parse_env_file(os_release, NEWLINE, + "PRETTY_NAME", &pretty_name, + NULL); + if (r == -ENOENT) { + + os_release = prefix_roota(arg_root, "/usr/lib/os-release"); + r = parse_env_file(os_release, NEWLINE, + "PRETTY_NAME", &pretty_name, + NULL); + } + + if (r < 0 && r != -ENOENT) + log_warning_errno(r, "Failed to read os-release file: %m"); + + printf("\nWelcome to your new installation of %s!\nPlease configure a few basic system settings:\n\n", + isempty(pretty_name) ? "GNU/Linux" : pretty_name); + + press_any_key(); + + done = true; +} + +static int show_menu(char **x, unsigned n_columns, unsigned width, unsigned percentage) { + unsigned n, per_column, i, j; + unsigned break_lines, break_modulo; + + assert(n_columns > 0); + + n = strv_length(x); + per_column = (n + n_columns - 1) / n_columns; + + break_lines = lines(); + if (break_lines > 2) + break_lines--; + + /* The first page gets two extra lines, since we want to show + * a title */ + break_modulo = break_lines; + if (break_modulo > 3) + break_modulo -= 3; + + for (i = 0; i < per_column; i++) { + + for (j = 0; j < n_columns; j ++) { + _cleanup_free_ char *e = NULL; + + if (j * per_column + i >= n) + break; + + e = ellipsize(x[j * per_column + i], width, percentage); + if (!e) + return log_oom(); + + printf("%4u) %-*s", j * per_column + i + 1, width, e); + } + + putchar('\n'); + + /* on the first screen we reserve 2 extra lines for the title */ + if (i % break_lines == break_modulo) { + if (!press_any_key()) + return 0; + } + } + + return 0; +} + +static int prompt_loop(const char *text, char **l, bool (*is_valid)(const char *name), char **ret) { + int r; + + assert(text); + assert(is_valid); + assert(ret); + + for (;;) { + _cleanup_free_ char *p = NULL; + unsigned u; + + r = ask_string(&p, "%s %s (empty to skip): ", special_glyph(TRIANGULAR_BULLET), text); + if (r < 0) + return log_error_errno(r, "Failed to query user: %m"); + + if (isempty(p)) { + log_warning("No data entered, skipping."); + return 0; + } + + r = safe_atou(p, &u); + if (r >= 0) { + char *c; + + if (u <= 0 || u > strv_length(l)) { + log_error("Specified entry number out of range."); + continue; + } + + log_info("Selected '%s'.", l[u-1]); + + c = strdup(l[u-1]); + if (!c) + return log_oom(); + + free(*ret); + *ret = c; + return 0; + } + + if (!is_valid(p)) { + log_error("Entered data invalid."); + continue; + } + + free(*ret); + *ret = p; + p = 0; + return 0; + } +} + +static int prompt_locale(void) { + _cleanup_strv_free_ char **locales = NULL; + int r; + + if (arg_locale || arg_locale_messages) + return 0; + + if (!arg_prompt_locale) + return 0; + + r = get_locales(&locales); + if (r < 0) + return log_error_errno(r, "Cannot query locales list: %m"); + + print_welcome(); + + printf("\nAvailable Locales:\n\n"); + r = show_menu(locales, 3, 22, 60); + if (r < 0) + return r; + + putchar('\n'); + + r = prompt_loop("Please enter system locale name or number", locales, locale_is_valid, &arg_locale); + if (r < 0) + return r; + + if (isempty(arg_locale)) + return 0; + + r = prompt_loop("Please enter system message locale name or number", locales, locale_is_valid, &arg_locale_messages); + if (r < 0) + return r; + + return 0; +} + +static int process_locale(void) { + const char *etc_localeconf; + char* locales[3]; + unsigned i = 0; + int r; + + etc_localeconf = prefix_roota(arg_root, "/etc/locale.conf"); + if (laccess(etc_localeconf, F_OK) >= 0) + return 0; + + if (arg_copy_locale && arg_root) { + + mkdir_parents(etc_localeconf, 0755); + r = copy_file("/etc/locale.conf", etc_localeconf, 0, 0644, 0); + if (r != -ENOENT) { + if (r < 0) + return log_error_errno(r, "Failed to copy %s: %m", etc_localeconf); + + log_info("%s copied.", etc_localeconf); + return 0; + } + } + + r = prompt_locale(); + if (r < 0) + return r; + + if (!isempty(arg_locale)) + locales[i++] = strjoina("LANG=", arg_locale); + if (!isempty(arg_locale_messages) && !streq(arg_locale_messages, arg_locale)) + locales[i++] = strjoina("LC_MESSAGES=", arg_locale_messages); + + if (i == 0) + return 0; + + locales[i] = NULL; + + mkdir_parents(etc_localeconf, 0755); + r = write_env_file(etc_localeconf, locales); + if (r < 0) + return log_error_errno(r, "Failed to write %s: %m", etc_localeconf); + + log_info("%s written.", etc_localeconf); + return 0; +} + +static int prompt_timezone(void) { + _cleanup_strv_free_ char **zones = NULL; + int r; + + if (arg_timezone) + return 0; + + if (!arg_prompt_timezone) + return 0; + + r = get_timezones(&zones); + if (r < 0) + return log_error_errno(r, "Cannot query timezone list: %m"); + + print_welcome(); + + printf("\nAvailable Time Zones:\n\n"); + r = show_menu(zones, 3, 22, 30); + if (r < 0) + return r; + + putchar('\n'); + + r = prompt_loop("Please enter timezone name or number", zones, timezone_is_valid, &arg_timezone); + if (r < 0) + return r; + + return 0; +} + +static int process_timezone(void) { + const char *etc_localtime, *e; + int r; + + etc_localtime = prefix_roota(arg_root, "/etc/localtime"); + if (laccess(etc_localtime, F_OK) >= 0) + return 0; + + if (arg_copy_timezone && arg_root) { + _cleanup_free_ char *p = NULL; + + r = readlink_malloc("/etc/localtime", &p); + if (r != -ENOENT) { + if (r < 0) + return log_error_errno(r, "Failed to read host timezone: %m"); + + mkdir_parents(etc_localtime, 0755); + if (symlink(p, etc_localtime) < 0) + return log_error_errno(errno, "Failed to create %s symlink: %m", etc_localtime); + + log_info("%s copied.", etc_localtime); + return 0; + } + } + + r = prompt_timezone(); + if (r < 0) + return r; + + if (isempty(arg_timezone)) + return 0; + + e = strjoina("../usr/share/zoneinfo/", arg_timezone); + + mkdir_parents(etc_localtime, 0755); + if (symlink(e, etc_localtime) < 0) + return log_error_errno(errno, "Failed to create %s symlink: %m", etc_localtime); + + log_info("%s written", etc_localtime); + return 0; +} + +static int prompt_hostname(void) { + int r; + + if (arg_hostname) + return 0; + + if (!arg_prompt_hostname) + return 0; + + print_welcome(); + putchar('\n'); + + for (;;) { + _cleanup_free_ char *h = NULL; + + r = ask_string(&h, "%s Please enter hostname for new system (empty to skip): ", special_glyph(TRIANGULAR_BULLET)); + if (r < 0) + return log_error_errno(r, "Failed to query hostname: %m"); + + if (isempty(h)) { + log_warning("No hostname entered, skipping."); + break; + } + + if (!hostname_is_valid(h, true)) { + log_error("Specified hostname invalid."); + continue; + } + + /* Get rid of the trailing dot that we allow, but don't want to see */ + arg_hostname = hostname_cleanup(h); + h = NULL; + break; + } + + return 0; +} + +static int process_hostname(void) { + const char *etc_hostname; + int r; + + etc_hostname = prefix_roota(arg_root, "/etc/hostname"); + if (laccess(etc_hostname, F_OK) >= 0) + return 0; + + r = prompt_hostname(); + if (r < 0) + return r; + + if (isempty(arg_hostname)) + return 0; + + mkdir_parents(etc_hostname, 0755); + r = write_string_file(etc_hostname, arg_hostname, WRITE_STRING_FILE_CREATE); + if (r < 0) + return log_error_errno(r, "Failed to write %s: %m", etc_hostname); + + log_info("%s written.", etc_hostname); + return 0; +} + +static int process_machine_id(void) { + const char *etc_machine_id; + char id[SD_ID128_STRING_MAX]; + int r; + + etc_machine_id = prefix_roota(arg_root, "/etc/machine-id"); + if (laccess(etc_machine_id, F_OK) >= 0) + return 0; + + if (sd_id128_equal(arg_machine_id, SD_ID128_NULL)) + return 0; + + mkdir_parents(etc_machine_id, 0755); + r = write_string_file(etc_machine_id, sd_id128_to_string(arg_machine_id, id), WRITE_STRING_FILE_CREATE); + if (r < 0) + return log_error_errno(r, "Failed to write machine id: %m"); + + log_info("%s written.", etc_machine_id); + return 0; +} + +static int prompt_root_password(void) { + const char *msg1, *msg2, *etc_shadow; + int r; + + if (arg_root_password) + return 0; + + if (!arg_prompt_root_password) + return 0; + + etc_shadow = prefix_roota(arg_root, "/etc/shadow"); + if (laccess(etc_shadow, F_OK) >= 0) + return 0; + + print_welcome(); + putchar('\n'); + + msg1 = strjoina(special_glyph(TRIANGULAR_BULLET), " Please enter a new root password (empty to skip): "); + msg2 = strjoina(special_glyph(TRIANGULAR_BULLET), " Please enter new root password again: "); + + for (;;) { + _cleanup_string_free_erase_ char *a = NULL, *b = NULL; + + r = ask_password_tty(msg1, NULL, 0, 0, NULL, &a); + if (r < 0) + return log_error_errno(r, "Failed to query root password: %m"); + + if (isempty(a)) { + log_warning("No password entered, skipping."); + break; + } + + r = ask_password_tty(msg2, NULL, 0, 0, NULL, &b); + if (r < 0) + return log_error_errno(r, "Failed to query root password: %m"); + + if (!streq(a, b)) { + log_error("Entered passwords did not match, please try again."); + continue; + } + + arg_root_password = a; + a = NULL; + break; + } + + return 0; +} + +static int write_root_shadow(const char *path, const struct spwd *p) { + _cleanup_fclose_ FILE *f = NULL; + assert(path); + assert(p); + + RUN_WITH_UMASK(0777) + f = fopen(path, "wex"); + if (!f) + return -errno; + + errno = 0; + if (putspent(p, f) != 0) + return errno > 0 ? -errno : -EIO; + + return fflush_and_check(f); +} + +static int process_root_password(void) { + + static const char table[] = + "abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "0123456789" + "./"; + + struct spwd item = { + .sp_namp = (char*) "root", + .sp_min = -1, + .sp_max = -1, + .sp_warn = -1, + .sp_inact = -1, + .sp_expire = -1, + .sp_flag = (unsigned long) -1, /* this appears to be what everybody does ... */ + }; + + _cleanup_close_ int lock = -1; + char salt[3+16+1+1]; + uint8_t raw[16]; + unsigned i; + char *j; + + const char *etc_shadow; + int r; + + etc_shadow = prefix_roota(arg_root, "/etc/shadow"); + if (laccess(etc_shadow, F_OK) >= 0) + return 0; + + mkdir_parents(etc_shadow, 0755); + + lock = take_etc_passwd_lock(arg_root); + if (lock < 0) + return log_error_errno(lock, "Failed to take a lock: %m"); + + if (arg_copy_root_password && arg_root) { + struct spwd *p; + + errno = 0; + p = getspnam("root"); + if (p || errno != ENOENT) { + if (!p) { + if (!errno) + errno = EIO; + + return log_error_errno(errno, "Failed to find shadow entry for root: %m"); + } + + r = write_root_shadow(etc_shadow, p); + if (r < 0) + return log_error_errno(r, "Failed to write %s: %m", etc_shadow); + + log_info("%s copied.", etc_shadow); + return 0; + } + } + + r = prompt_root_password(); + if (r < 0) + return r; + + if (!arg_root_password) + return 0; + + r = dev_urandom(raw, 16); + if (r < 0) + return log_error_errno(r, "Failed to get salt: %m"); + + /* We only bother with SHA512 hashed passwords, the rest is legacy, and we don't do legacy. */ + assert_cc(sizeof(table) == 64 + 1); + j = stpcpy(salt, "$6$"); + for (i = 0; i < 16; i++) + j[i] = table[raw[i] & 63]; + j[i++] = '$'; + j[i] = 0; + + errno = 0; + item.sp_pwdp = crypt(arg_root_password, salt); + if (!item.sp_pwdp) { + if (!errno) + errno = EINVAL; + + return log_error_errno(errno, "Failed to encrypt password: %m"); + } + + item.sp_lstchg = (long) (now(CLOCK_REALTIME) / USEC_PER_DAY); + + r = write_root_shadow(etc_shadow, &item); + if (r < 0) + return log_error_errno(r, "Failed to write %s: %m", etc_shadow); + + log_info("%s written.", etc_shadow); + return 0; +} + +static void help(void) { + printf("%s [OPTIONS...]\n\n" + "Configures basic settings of the system.\n\n" + " -h --help Show this help\n" + " --version Show package version\n" + " --root=PATH Operate on an alternate filesystem root\n" + " --locale=LOCALE Set primary locale (LANG=)\n" + " --locale-messages=LOCALE Set message locale (LC_MESSAGES=)\n" + " --timezone=TIMEZONE Set timezone\n" + " --hostname=NAME Set host name\n" + " --machine-ID=ID Set machine ID\n" + " --root-password=PASSWORD Set root password\n" + " --root-password-file=FILE Set root password from file\n" + " --prompt-locale Prompt the user for locale settings\n" + " --prompt-timezone Prompt the user for timezone\n" + " --prompt-hostname Prompt the user for hostname\n" + " --prompt-root-password Prompt the user for root password\n" + " --prompt Prompt for all of the above\n" + " --copy-locale Copy locale from host\n" + " --copy-timezone Copy timezone from host\n" + " --copy-root-password Copy root password from host\n" + " --copy Copy locale, timezone, root password\n" + " --setup-machine-id Generate a new random machine ID\n" + , program_invocation_short_name); +} + +static int parse_argv(int argc, char *argv[]) { + + enum { + ARG_VERSION = 0x100, + ARG_ROOT, + ARG_LOCALE, + ARG_LOCALE_MESSAGES, + ARG_TIMEZONE, + ARG_HOSTNAME, + ARG_MACHINE_ID, + ARG_ROOT_PASSWORD, + ARG_ROOT_PASSWORD_FILE, + ARG_PROMPT, + ARG_PROMPT_LOCALE, + ARG_PROMPT_TIMEZONE, + ARG_PROMPT_HOSTNAME, + ARG_PROMPT_ROOT_PASSWORD, + ARG_COPY, + ARG_COPY_LOCALE, + ARG_COPY_TIMEZONE, + ARG_COPY_ROOT_PASSWORD, + ARG_SETUP_MACHINE_ID, + }; + + static const struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, ARG_VERSION }, + { "root", required_argument, NULL, ARG_ROOT }, + { "locale", required_argument, NULL, ARG_LOCALE }, + { "locale-messages", required_argument, NULL, ARG_LOCALE_MESSAGES }, + { "timezone", required_argument, NULL, ARG_TIMEZONE }, + { "hostname", required_argument, NULL, ARG_HOSTNAME }, + { "machine-id", required_argument, NULL, ARG_MACHINE_ID }, + { "root-password", required_argument, NULL, ARG_ROOT_PASSWORD }, + { "root-password-file", required_argument, NULL, ARG_ROOT_PASSWORD_FILE }, + { "prompt", no_argument, NULL, ARG_PROMPT }, + { "prompt-locale", no_argument, NULL, ARG_PROMPT_LOCALE }, + { "prompt-timezone", no_argument, NULL, ARG_PROMPT_TIMEZONE }, + { "prompt-hostname", no_argument, NULL, ARG_PROMPT_HOSTNAME }, + { "prompt-root-password", no_argument, NULL, ARG_PROMPT_ROOT_PASSWORD }, + { "copy", no_argument, NULL, ARG_COPY }, + { "copy-locale", no_argument, NULL, ARG_COPY_LOCALE }, + { "copy-timezone", no_argument, NULL, ARG_COPY_TIMEZONE }, + { "copy-root-password", no_argument, NULL, ARG_COPY_ROOT_PASSWORD }, + { "setup-machine-id", no_argument, NULL, ARG_SETUP_MACHINE_ID }, + {} + }; + + int r, c; + + assert(argc >= 0); + assert(argv); + + while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) + + switch (c) { + + case 'h': + help(); + return 0; + + case ARG_VERSION: + return version(); + + case ARG_ROOT: + r = parse_path_argument_and_warn(optarg, true, &arg_root); + if (r < 0) + return r; + break; + + case ARG_LOCALE: + if (!locale_is_valid(optarg)) { + log_error("Locale %s is not valid.", optarg); + return -EINVAL; + } + + r = free_and_strdup(&arg_locale, optarg); + if (r < 0) + return log_oom(); + + break; + + case ARG_LOCALE_MESSAGES: + if (!locale_is_valid(optarg)) { + log_error("Locale %s is not valid.", optarg); + return -EINVAL; + } + + r = free_and_strdup(&arg_locale_messages, optarg); + if (r < 0) + return log_oom(); + + break; + + case ARG_TIMEZONE: + if (!timezone_is_valid(optarg)) { + log_error("Timezone %s is not valid.", optarg); + return -EINVAL; + } + + r = free_and_strdup(&arg_timezone, optarg); + if (r < 0) + return log_oom(); + + break; + + case ARG_ROOT_PASSWORD: + r = free_and_strdup(&arg_root_password, optarg); + if (r < 0) + return log_oom(); + break; + + case ARG_ROOT_PASSWORD_FILE: + arg_root_password = mfree(arg_root_password); + + r = read_one_line_file(optarg, &arg_root_password); + if (r < 0) + return log_error_errno(r, "Failed to read %s: %m", optarg); + + break; + + case ARG_HOSTNAME: + if (!hostname_is_valid(optarg, true)) { + log_error("Host name %s is not valid.", optarg); + return -EINVAL; + } + + hostname_cleanup(optarg); + r = free_and_strdup(&arg_hostname, optarg); + if (r < 0) + return log_oom(); + + break; + + case ARG_MACHINE_ID: + if (sd_id128_from_string(optarg, &arg_machine_id) < 0) { + log_error("Failed to parse machine id %s.", optarg); + return -EINVAL; + } + + break; + + case ARG_PROMPT: + arg_prompt_locale = arg_prompt_timezone = arg_prompt_hostname = arg_prompt_root_password = true; + break; + + case ARG_PROMPT_LOCALE: + arg_prompt_locale = true; + break; + + case ARG_PROMPT_TIMEZONE: + arg_prompt_timezone = true; + break; + + case ARG_PROMPT_HOSTNAME: + arg_prompt_hostname = true; + break; + + case ARG_PROMPT_ROOT_PASSWORD: + arg_prompt_root_password = true; + break; + + case ARG_COPY: + arg_copy_locale = arg_copy_timezone = arg_copy_root_password = true; + break; + + case ARG_COPY_LOCALE: + arg_copy_locale = true; + break; + + case ARG_COPY_TIMEZONE: + arg_copy_timezone = true; + break; + + case ARG_COPY_ROOT_PASSWORD: + arg_copy_root_password = true; + break; + + case ARG_SETUP_MACHINE_ID: + + r = sd_id128_randomize(&arg_machine_id); + if (r < 0) + return log_error_errno(r, "Failed to generate randomized machine ID: %m"); + + break; + + case '?': + return -EINVAL; + + default: + assert_not_reached("Unhandled option"); + } + + return 1; +} + +int main(int argc, char *argv[]) { + int r; + + r = parse_argv(argc, argv); + if (r <= 0) + goto finish; + + log_set_target(LOG_TARGET_AUTO); + log_parse_environment(); + log_open(); + + umask(0022); + + r = process_locale(); + if (r < 0) + goto finish; + + r = process_timezone(); + if (r < 0) + goto finish; + + r = process_hostname(); + if (r < 0) + goto finish; + + r = process_machine_id(); + if (r < 0) + goto finish; + + r = process_root_password(); + if (r < 0) + goto finish; + +finish: + free(arg_root); + free(arg_locale); + free(arg_locale_messages); + free(arg_timezone); + free(arg_hostname); + string_erase(arg_root_password); + free(arg_root_password); + + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/grp-initprogs/systemd-firstboot/systemd-firstboot.service.in b/src/grp-initprogs/systemd-firstboot/systemd-firstboot.service.in new file mode 100644 index 0000000000..405c6f3fd2 --- /dev/null +++ b/src/grp-initprogs/systemd-firstboot/systemd-firstboot.service.in @@ -0,0 +1,24 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=First Boot Wizard +Documentation=man:systemd-firstboot(1) +DefaultDependencies=no +Conflicts=shutdown.target +After=systemd-remount-fs.service +Before=systemd-sysusers.service sysinit.target shutdown.target +ConditionPathIsReadWrite=/etc +ConditionFirstBoot=yes + +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStart=@rootbindir@/systemd-firstboot --prompt-locale --prompt-timezone --prompt-root-password +StandardOutput=tty +StandardInput=tty +StandardError=tty diff --git a/src/grp-initprogs/systemd-fsck/Makefile b/src/grp-initprogs/systemd-fsck/Makefile new file mode 100644 index 0000000000..f29fba4d20 --- /dev/null +++ b/src/grp-initprogs/systemd-fsck/Makefile @@ -0,0 +1,33 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +rootlibexec_PROGRAMS += systemd-fsck +systemd_fsck_SOURCES = \ + src/fsck/fsck.c + +systemd_fsck_LDADD = \ + libshared.la + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-initprogs/systemd-fsck/fsck.c b/src/grp-initprogs/systemd-fsck/fsck.c new file mode 100644 index 0000000000..4ca018763f --- /dev/null +++ b/src/grp-initprogs/systemd-fsck/fsck.c @@ -0,0 +1,487 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + Copyright 2014 Holger Hans Peter Freyther + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/fs-util.h" +#include "basic/parse-util.h" +#include "basic/path-util.h" +#include "basic/proc-cmdline.h" +#include "basic/process-util.h" +#include "basic/signal-util.h" +#include "basic/socket-util.h" +#include "basic/special.h" +#include "basic/stdio-util.h" +#include "basic/util.h" +#include "sd-bus/bus-common-errors.h" +#include "sd-bus/bus-error.h" +#include "sd-device/device-util.h" +#include "sd-device/sd-device.h" +#include "shared/bus-util.h" + +/* exit codes as defined in fsck(8) */ +enum { + FSCK_SUCCESS = 0, + FSCK_ERROR_CORRECTED = 1, + FSCK_SYSTEM_SHOULD_REBOOT = 2, + FSCK_ERRORS_LEFT_UNCORRECTED = 4, + FSCK_OPERATIONAL_ERROR = 8, + FSCK_USAGE_OR_SYNTAX_ERROR = 16, + FSCK_USER_CANCELLED = 32, + FSCK_SHARED_LIB_ERROR = 128, +}; + +static bool arg_skip = false; +static bool arg_force = false; +static bool arg_show_progress = false; +static const char *arg_repair = "-a"; + +static void start_target(const char *target, const char *mode) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; + int r; + + assert(target); + + r = bus_connect_system_systemd(&bus); + if (r < 0) { + log_error_errno(r, "Failed to get D-Bus connection: %m"); + return; + } + + log_info("Running request %s/start/replace", target); + + /* Start these units only if we can replace base.target with it */ + r = sd_bus_call_method(bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "StartUnitReplace", + &error, + NULL, + "sss", "basic.target", target, mode); + + /* Don't print a warning if we aren't called during startup */ + if (r < 0 && !sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_JOB)) + log_error("Failed to start unit: %s", bus_error_message(&error, r)); +} + +static int parse_proc_cmdline_item(const char *key, const char *value) { + int r; + + assert(key); + + if (streq(key, "fsck.mode") && value) { + + if (streq(value, "auto")) + arg_force = arg_skip = false; + else if (streq(value, "force")) + arg_force = true; + else if (streq(value, "skip")) + arg_skip = true; + else + log_warning("Invalid fsck.mode= parameter '%s'. Ignoring.", value); + + } else if (streq(key, "fsck.repair") && value) { + + if (streq(value, "preen")) + arg_repair = "-a"; + else { + r = parse_boolean(value); + if (r > 0) + arg_repair = "-y"; + else if (r == 0) + arg_repair = "-n"; + else + log_warning("Invalid fsck.repair= parameter '%s'. Ignoring.", value); + } + } + +#ifdef HAVE_SYSV_COMPAT + else if (streq(key, "fastboot") && !value) { + log_warning("Please pass 'fsck.mode=skip' rather than 'fastboot' on the kernel command line."); + arg_skip = true; + + } else if (streq(key, "forcefsck") && !value) { + log_warning("Please pass 'fsck.mode=force' rather than 'forcefsck' on the kernel command line."); + arg_force = true; + } +#endif + + return 0; +} + +static void test_files(void) { + +#ifdef HAVE_SYSV_COMPAT + if (access("/fastboot", F_OK) >= 0) { + log_error("Please pass 'fsck.mode=skip' on the kernel command line rather than creating /fastboot on the root file system."); + arg_skip = true; + } + + if (access("/forcefsck", F_OK) >= 0) { + log_error("Please pass 'fsck.mode=force' on the kernel command line rather than creating /forcefsck on the root file system."); + arg_force = true; + } +#endif + + arg_show_progress = access("/run/systemd/show-status", F_OK) >= 0; +} + +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) { + _cleanup_fclose_ FILE *console = NULL, *f = NULL; + usec_t last = 0; + bool locked = false; + int clear = 0, r; + + /* No progress pipe to process? Then we are a NOP. */ + if (fd < 0) + return 0; + + f = fdopen(fd, "re"); + if (!f) { + safe_close(fd); + return -errno; + } + + console = fopen("/dev/console", "we"); + if (!console) + return -ENOMEM; + + for (;;) { + int pass, m; + unsigned long cur, max; + _cleanup_free_ char *device = NULL; + double p; + usec_t t; + + if (fscanf(f, "%i %lu %lu %ms", &pass, &cur, &max, &device) != 4) { + + if (ferror(f)) + r = log_warning_errno(errno, "Failed to read from progress pipe: %m"); + else if (feof(f)) + r = 0; + else { + log_warning("Failed to parse progress pipe data"); + r = -EBADMSG; + } + break; + } + + /* Only show one progress counter at max */ + if (!locked) { + if (flock(fileno(console), LOCK_EX|LOCK_NB) < 0) + continue; + + locked = true; + } + + /* Only update once every 50ms */ + t = now(CLOCK_MONOTONIC); + if (last + 50 * USEC_PER_MSEC > t) + continue; + + last = t; + + p = percent(pass, cur, max); + fprintf(console, "\r%s: fsck %3.1f%% complete...\r%n", device, p, &m); + fflush(console); + + 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); + } + + return r; +} + +static int fsck_progress_socket(void) { + static const union sockaddr_union sa = { + .un.sun_family = AF_UNIX, + .un.sun_path = "/run/systemd/fsck.progress", + }; + + int fd, r; + + fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (fd < 0) + return log_warning_errno(errno, "socket(): %m"); + + if (connect(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0) { + r = log_full_errno(errno == ECONNREFUSED || errno == ENOENT ? LOG_DEBUG : LOG_WARNING, + errno, "Failed to connect to progress socket %s, ignoring: %m", sa.un.sun_path); + safe_close(fd); + return r; + } + + return fd; +} + +int main(int argc, char *argv[]) { + _cleanup_close_pair_ int progress_pipe[2] = { -1, -1 }; + _cleanup_(sd_device_unrefp) sd_device *dev = NULL; + const char *device, *type; + bool root_directory; + siginfo_t status; + struct stat st; + int r; + pid_t pid; + + if (argc > 2) { + log_error("This program expects one or no arguments."); + return EXIT_FAILURE; + } + + log_set_target(LOG_TARGET_AUTO); + log_parse_environment(); + log_open(); + + umask(0022); + + r = parse_proc_cmdline(parse_proc_cmdline_item); + if (r < 0) + log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m"); + + test_files(); + + if (!arg_force && arg_skip) { + r = 0; + goto finish; + } + + if (argc > 1) { + device = argv[1]; + + if (stat(device, &st) < 0) { + r = log_error_errno(errno, "Failed to stat %s: %m", device); + goto finish; + } + + if (!S_ISBLK(st.st_mode)) { + log_error("%s is not a block device.", device); + r = -EINVAL; + goto finish; + } + + r = sd_device_new_from_devnum(&dev, 'b', st.st_rdev); + if (r < 0) { + log_error_errno(r, "Failed to detect device %s: %m", device); + goto finish; + } + + root_directory = false; + } else { + struct timespec times[2]; + + /* Find root device */ + + if (stat("/", &st) < 0) { + r = log_error_errno(errno, "Failed to stat() the root directory: %m"); + goto finish; + } + + /* Virtual root devices don't need an fsck */ + if (major(st.st_dev) == 0) { + log_debug("Root directory is virtual or btrfs, skipping check."); + r = 0; + goto finish; + } + + /* check if we are already writable */ + times[0] = st.st_atim; + times[1] = st.st_mtim; + + if (utimensat(AT_FDCWD, "/", times, 0) == 0) { + log_info("Root directory is writable, skipping check."); + r = 0; + goto finish; + } + + r = sd_device_new_from_devnum(&dev, 'b', st.st_dev); + if (r < 0) { + log_error_errno(r, "Failed to detect root device: %m"); + goto finish; + } + + r = sd_device_get_devname(dev, &device); + if (r < 0) { + log_error_errno(r, "Failed to detect device node of root directory: %m"); + goto finish; + } + + root_directory = true; + } + + r = sd_device_get_property_value(dev, "ID_FS_TYPE", &type); + if (r >= 0) { + r = fsck_exists(type); + if (r < 0) + log_warning_errno(r, "Couldn't detect if fsck.%s may be used for %s, proceeding: %m", type, device); + else if (r == 0) { + log_info("fsck.%s doesn't exist, not checking file system on %s.", type, device); + goto finish; + } + } + + if (arg_show_progress) { + if (pipe(progress_pipe) < 0) { + r = log_error_errno(errno, "pipe(): %m"); + goto finish; + } + } + + pid = fork(); + if (pid < 0) { + r = log_error_errno(errno, "fork(): %m"); + goto finish; + } + if (pid == 0) { + char dash_c[sizeof("-C")-1 + DECIMAL_STR_MAX(int) + 1]; + int progress_socket = -1; + const char *cmdline[9]; + int i = 0; + + /* Child */ + + (void) reset_all_signal_handlers(); + (void) reset_signal_mask(); + assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0); + + /* Close the reading side of the progress pipe */ + progress_pipe[0] = safe_close(progress_pipe[0]); + + /* Try to connect to a progress management daemon, if there is one */ + progress_socket = fsck_progress_socket(); + if (progress_socket >= 0) { + /* If this worked we close the progress pipe early, and just use the socket */ + progress_pipe[1] = safe_close(progress_pipe[1]); + xsprintf(dash_c, "-C%i", progress_socket); + } else if (progress_pipe[1] >= 0) { + /* Otherwise if we have the progress pipe to our own local handle, we use it */ + xsprintf(dash_c, "-C%i", progress_pipe[1]); + } else + dash_c[0] = 0; + + cmdline[i++] = "/sbin/fsck"; + cmdline[i++] = arg_repair; + cmdline[i++] = "-T"; + + /* + * Since util-linux v2.25 fsck uses /run/fsck/.lock files. + * The previous versions use flock for the device and conflict with + * udevd, see https://bugs.freedesktop.org/show_bug.cgi?id=79576#c5 + */ + cmdline[i++] = "-l"; + + if (!root_directory) + cmdline[i++] = "-M"; + + if (arg_force) + cmdline[i++] = "-f"; + + if (!isempty(dash_c)) + cmdline[i++] = dash_c; + + cmdline[i++] = device; + cmdline[i++] = NULL; + + execv(cmdline[0], (char**) cmdline); + _exit(FSCK_OPERATIONAL_ERROR); + } + + progress_pipe[1] = safe_close(progress_pipe[1]); + (void) process_progress(progress_pipe[0]); + progress_pipe[0] = -1; + + r = wait_for_terminate(pid, &status); + if (r < 0) { + log_error_errno(r, "waitid(): %m"); + goto finish; + } + + if (status.si_code != CLD_EXITED || (status.si_status & ~1)) { + + if (status.si_code == CLD_KILLED || status.si_code == CLD_DUMPED) + log_error("fsck terminated by signal %s.", signal_to_string(status.si_status)); + else if (status.si_code == CLD_EXITED) + log_error("fsck failed with error code %i.", status.si_status); + else + log_error("fsck failed due to unknown reason."); + + r = -EINVAL; + + if (status.si_code == CLD_EXITED && (status.si_status & FSCK_SYSTEM_SHOULD_REBOOT) && root_directory) + /* System should be rebooted. */ + start_target(SPECIAL_REBOOT_TARGET, "replace-irreversibly"); + else if (status.si_code == CLD_EXITED && (status.si_status & (FSCK_SYSTEM_SHOULD_REBOOT | FSCK_ERRORS_LEFT_UNCORRECTED))) + /* Some other problem */ + start_target(SPECIAL_EMERGENCY_TARGET, "replace"); + else { + log_warning("Ignoring error."); + r = 0; + } + + } else + r = 0; + + if (status.si_code == CLD_EXITED && (status.si_status & FSCK_ERROR_CORRECTED)) + (void) touch("/run/systemd/quotacheck"); + +finish: + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/grp-initprogs/systemd-fsck/systemd-fsck@.service.in b/src/grp-initprogs/systemd-fsck/systemd-fsck@.service.in new file mode 100644 index 0000000000..6ca6b07e9e --- /dev/null +++ b/src/grp-initprogs/systemd-fsck/systemd-fsck@.service.in @@ -0,0 +1,20 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=File System Check on %f +Documentation=man:systemd-fsck@.service(8) +DefaultDependencies=no +BindsTo=%i.device +After=%i.device systemd-fsck-root.service local-fs-pre.target +Before=systemd-quotacheck.service shutdown.target + +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStart=@rootlibexecdir@/systemd-fsck %f +TimeoutSec=0 diff --git a/src/grp-initprogs/systemd-modules-load/Makefile b/src/grp-initprogs/systemd-modules-load/Makefile new file mode 100644 index 0000000000..8503fb32a2 --- /dev/null +++ b/src/grp-initprogs/systemd-modules-load/Makefile @@ -0,0 +1,60 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +ifneq ($(HAVE_KMOD),) +systemd_modules_load_SOURCES = \ + src/modules-load/modules-load.c + +systemd_modules_load_CFLAGS = \ + $(AM_CFLAGS) \ + $(KMOD_CFLAGS) + +systemd_modules_load_LDADD = \ + libshared.la \ + $(KMOD_LIBS) + +rootlibexec_PROGRAMS += \ + systemd-modules-load + +nodist_systemunit_DATA += \ + units/systemd-modules-load.service + +SYSINIT_TARGET_WANTS += \ + systemd-modules-load.service + +ifneq ($(ENABLE_TMPFILES),) +nodist_systemunit_DATA += \ + units/kmod-static-nodes.service + +SYSINIT_TARGET_WANTS += \ + kmod-static-nodes.service +endif # ENABLE_TMPFILES +endif # HAVE_KMOD + +EXTRA_DIST += \ + units/systemd-modules-load.service.in \ + units/kmod-static-nodes.service.in + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-initprogs/systemd-modules-load/kmod-static-nodes.service.in b/src/grp-initprogs/systemd-modules-load/kmod-static-nodes.service.in new file mode 100644 index 0000000000..a9c8df1184 --- /dev/null +++ b/src/grp-initprogs/systemd-modules-load/kmod-static-nodes.service.in @@ -0,0 +1,18 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Create list of required static device nodes for the current kernel +DefaultDependencies=no +Before=sysinit.target systemd-tmpfiles-setup-dev.service +ConditionCapability=CAP_SYS_MODULE +ConditionFileNotEmpty=/lib/modules/%v/modules.devname + +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStart=@KMOD@ static-nodes --format=tmpfiles --output=/run/tmpfiles.d/kmod.conf diff --git a/src/grp-initprogs/systemd-modules-load/modules-load.c b/src/grp-initprogs/systemd-modules-load/modules-load.c new file mode 100644 index 0000000000..d7ea7886c6 --- /dev/null +++ b/src/grp-initprogs/systemd-modules-load/modules-load.c @@ -0,0 +1,283 @@ +/*** + 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 Lesser General Public License as published by + the Free Software Foundation; either version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include + +#include "basic/conf-files.h" +#include "basic/def.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/log.h" +#include "basic/proc-cmdline.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/util.h" + +static char **arg_proc_cmdline_modules = NULL; + +static const char conf_file_dirs[] = CONF_PATHS_NULSTR("modules-load.d"); + +static void systemd_kmod_log(void *data, int priority, const char *file, int line, + const char *fn, const char *format, va_list args) { + + DISABLE_WARNING_FORMAT_NONLITERAL; + log_internalv(priority, 0, file, line, fn, format, args); + REENABLE_WARNING; +} + +static int add_modules(const char *p) { + _cleanup_strv_free_ char **k = NULL; + + k = strv_split(p, ","); + if (!k) + return log_oom(); + + if (strv_extend_strv(&arg_proc_cmdline_modules, k, true) < 0) + return log_oom(); + + return 0; +} + +static int parse_proc_cmdline_item(const char *key, const char *value) { + int r; + + if (STR_IN_SET(key, "modules-load", "rd.modules-load") && value) { + r = add_modules(value); + if (r < 0) + return r; + } + + return 0; +} + +static int load_module(struct kmod_ctx *ctx, const char *m) { + const int probe_flags = KMOD_PROBE_APPLY_BLACKLIST; + struct kmod_list *itr, *modlist = NULL; + int r = 0; + + log_debug("load: %s", m); + + r = kmod_module_new_from_lookup(ctx, m, &modlist); + if (r < 0) + return log_error_errno(r, "Failed to lookup alias '%s': %m", m); + + if (!modlist) { + log_error("Failed to find module '%s'", m); + return -ENOENT; + } + + kmod_list_foreach(itr, modlist) { + struct kmod_module *mod; + int state, err; + + mod = kmod_module_get_module(itr); + state = kmod_module_get_initstate(mod); + + switch (state) { + case KMOD_MODULE_BUILTIN: + log_info("Module '%s' is builtin", kmod_module_get_name(mod)); + break; + + case KMOD_MODULE_LIVE: + log_debug("Module '%s' is already loaded", kmod_module_get_name(mod)); + break; + + default: + 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_errno(err, "Failed to insert '%s': %m", kmod_module_get_name(mod)); + r = err; + } + } + + kmod_module_unref(mod); + } + + kmod_module_unref_list(modlist); + + return r; +} + +static int apply_file(struct kmod_ctx *ctx, const char *path, bool ignore_enoent) { + _cleanup_fclose_ FILE *f = NULL; + int r; + + assert(ctx); + assert(path); + + r = search_and_fopen_nulstr(path, "re", NULL, conf_file_dirs, &f); + if (r < 0) { + if (ignore_enoent && r == -ENOENT) + return 0; + + return log_error_errno(r, "Failed to open %s, ignoring: %m", path); + } + + log_debug("apply: %s", path); + for (;;) { + char line[LINE_MAX], *l; + int k; + + if (!fgets(line, sizeof(line), f)) { + if (feof(f)) + break; + + return log_error_errno(errno, "Failed to read file '%s', ignoring: %m", path); + } + + l = strstrip(line); + if (!*l) + continue; + if (strchr(COMMENTS "\n", *l)) + continue; + + k = load_module(ctx, l); + if (k < 0 && r == 0) + r = k; + } + + return r; +} + +static void help(void) { + printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n" + "Loads statically configured kernel modules.\n\n" + " -h --help Show this help\n" + " --version Show package version\n", + program_invocation_short_name); +} + +static int parse_argv(int argc, char *argv[]) { + + enum { + ARG_VERSION = 0x100, + }; + + static const struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, ARG_VERSION }, + {} + }; + + int c; + + assert(argc >= 0); + assert(argv); + + while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) + + switch (c) { + + case 'h': + help(); + return 0; + + case ARG_VERSION: + return version(); + + case '?': + return -EINVAL; + + default: + assert_not_reached("Unhandled option"); + } + + return 1; +} + +int main(int argc, char *argv[]) { + int r, k; + struct kmod_ctx *ctx; + + 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); + + r = parse_proc_cmdline(parse_proc_cmdline_item); + if (r < 0) + log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m"); + + ctx = kmod_new(NULL, NULL); + if (!ctx) { + log_error("Failed to allocate memory for kmod."); + goto finish; + } + + kmod_load_resources(ctx); + kmod_set_log_fn(ctx, systemd_kmod_log, NULL); + + r = 0; + + if (argc > optind) { + int i; + + for (i = optind; i < argc; i++) { + k = apply_file(ctx, argv[i], false); + if (k < 0 && r == 0) + r = k; + } + + } else { + _cleanup_strv_free_ char **files = NULL; + char **fn, **i; + + STRV_FOREACH(i, arg_proc_cmdline_modules) { + k = load_module(ctx, *i); + if (k < 0 && r == 0) + r = k; + } + + k = conf_files_list_nulstr(&files, ".conf", NULL, conf_file_dirs); + if (k < 0) { + log_error_errno(k, "Failed to enumerate modules-load.d files: %m"); + if (r == 0) + r = k; + goto finish; + } + + STRV_FOREACH(fn, files) { + k = apply_file(ctx, *fn, true); + if (k < 0 && r == 0) + r = k; + } + } + +finish: + kmod_unref(ctx); + strv_free(arg_proc_cmdline_modules); + + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/grp-initprogs/systemd-modules-load/systemd-modules-load.service.in b/src/grp-initprogs/systemd-modules-load/systemd-modules-load.service.in new file mode 100644 index 0000000000..9de6d31349 --- /dev/null +++ b/src/grp-initprogs/systemd-modules-load/systemd-modules-load.service.in @@ -0,0 +1,27 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Load Kernel Modules +Documentation=man:systemd-modules-load.service(8) man:modules-load.d(5) +DefaultDependencies=no +Conflicts=shutdown.target +Before=sysinit.target shutdown.target +ConditionCapability=CAP_SYS_MODULE +ConditionDirectoryNotEmpty=|/lib/modules-load.d +ConditionDirectoryNotEmpty=|/usr/lib/modules-load.d +ConditionDirectoryNotEmpty=|/usr/local/lib/modules-load.d +ConditionDirectoryNotEmpty=|/etc/modules-load.d +ConditionDirectoryNotEmpty=|/run/modules-load.d +ConditionKernelCommandLine=|modules-load +ConditionKernelCommandLine=|rd.modules-load + +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStart=@rootlibexecdir@/systemd-modules-load +TimeoutSec=90s diff --git a/src/grp-initprogs/systemd-quotacheck/Makefile b/src/grp-initprogs/systemd-quotacheck/Makefile new file mode 100644 index 0000000000..076d33a587 --- /dev/null +++ b/src/grp-initprogs/systemd-quotacheck/Makefile @@ -0,0 +1,49 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +ifneq ($(ENABLE_QUOTACHECK),) +rootlibexec_PROGRAMS += \ + systemd-quotacheck + +nodist_systemunit_DATA += \ + units/systemd-quotacheck.service + +systemd_quotacheck_SOURCES = \ + src/quotacheck/quotacheck.c + +systemd_quotacheck_LDADD = \ + libshared.la +endif # ENABLE_QUOTACHECK + +EXTRA_DIST += \ + units/quotaon.service.in \ + units/systemd-quotacheck.service.in + +nodist_systemunit_DATA += \ + units/quotaon.service + +sd.CPPFLAGS += -DQUOTACHECK=\"$(QUOTACHECK)\" + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-initprogs/systemd-quotacheck/quotacheck.c b/src/grp-initprogs/systemd-quotacheck/quotacheck.c new file mode 100644 index 0000000000..3c2292b0c4 --- /dev/null +++ b/src/grp-initprogs/systemd-quotacheck/quotacheck.c @@ -0,0 +1,124 @@ +/*** + 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 Lesser General Public License as published by + the Free Software Foundation; either version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include + +#include "basic/proc-cmdline.h" +#include "basic/process-util.h" +#include "basic/signal-util.h" +#include "basic/string-util.h" +#include "basic/util.h" + +static bool arg_skip = false; +static bool arg_force = false; + +static int parse_proc_cmdline_item(const char *key, const char *value) { + + if (streq(key, "quotacheck.mode") && value) { + + if (streq(value, "auto")) + arg_force = arg_skip = false; + else if (streq(value, "force")) + arg_force = true; + else if (streq(value, "skip")) + arg_skip = true; + else + log_warning("Invalid quotacheck.mode= parameter '%s'. Ignoring.", value); + } + +#ifdef HAVE_SYSV_COMPAT + else if (streq(key, "forcequotacheck") && !value) { + log_warning("Please use 'quotacheck.mode=force' rather than 'forcequotacheck' on the kernel command line."); + arg_force = true; + } +#endif + + return 0; +} + +static void test_files(void) { + +#ifdef HAVE_SYSV_COMPAT + if (access("/forcequotacheck", F_OK) >= 0) { + log_error("Please pass 'quotacheck.mode=force' on the kernel command line rather than creating /forcequotacheck on the root file system."); + arg_force = true; + } +#endif +} + +int main(int argc, char *argv[]) { + + static const char * const cmdline[] = { + QUOTACHECK, + "-anug", + NULL + }; + + pid_t pid; + int r; + + if (argc > 1) { + log_error("This program takes no arguments."); + return EXIT_FAILURE; + } + + log_set_target(LOG_TARGET_AUTO); + log_parse_environment(); + log_open(); + + umask(0022); + + r = parse_proc_cmdline(parse_proc_cmdline_item); + if (r < 0) + log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m"); + + test_files(); + + if (!arg_force) { + if (arg_skip) + return EXIT_SUCCESS; + + if (access("/run/systemd/quotacheck", F_OK) < 0) + return EXIT_SUCCESS; + } + + pid = fork(); + if (pid < 0) { + log_error_errno(errno, "fork(): %m"); + return EXIT_FAILURE; + } else if (pid == 0) { + + /* Child */ + + (void) reset_all_signal_handlers(); + (void) reset_signal_mask(); + assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0); + + execv(cmdline[0], (char**) cmdline); + _exit(1); /* Operational error */ + } + + r = wait_for_terminate_and_warn("quotacheck", pid, true); + + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/grp-initprogs/systemd-quotacheck/quotaon.service.in b/src/grp-initprogs/systemd-quotacheck/quotaon.service.in new file mode 100644 index 0000000000..7d59a40195 --- /dev/null +++ b/src/grp-initprogs/systemd-quotacheck/quotaon.service.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 Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Enable File System Quotas +Documentation=man:quotaon(8) +DefaultDependencies=no +After=systemd-quotacheck.service +Before=local-fs.target shutdown.target +ConditionPathExists=@QUOTAON@ + +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStart=@QUOTAON@ -aug diff --git a/src/grp-initprogs/systemd-quotacheck/systemd-quotacheck.service.in b/src/grp-initprogs/systemd-quotacheck/systemd-quotacheck.service.in new file mode 100644 index 0000000000..5cb9bc3bc9 --- /dev/null +++ b/src/grp-initprogs/systemd-quotacheck/systemd-quotacheck.service.in @@ -0,0 +1,20 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=File System Quota Check +Documentation=man:systemd-quotacheck.service(8) +DefaultDependencies=no +After=systemd-remount-fs.service +Before=local-fs.target shutdown.target +ConditionPathExists=@QUOTACHECK@ + +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStart=@rootlibexecdir@/systemd-quotacheck +TimeoutSec=0 diff --git a/src/grp-initprogs/systemd-random-seed/Makefile b/src/grp-initprogs/systemd-random-seed/Makefile new file mode 100644 index 0000000000..489a8ab007 --- /dev/null +++ b/src/grp-initprogs/systemd-random-seed/Makefile @@ -0,0 +1,50 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +ifneq ($(ENABLE_RANDOMSEED),) +rootlibexec_PROGRAMS += \ + systemd-random-seed + +nodist_systemunit_DATA += \ + units/systemd-random-seed.service + +systemd_random_seed_SOURCES = \ + src/random-seed/random-seed.c + +systemd_random_seed_LDADD = \ + libshared.la + +SYSINIT_TARGET_WANTS += \ + systemd-random-seed.service + +endif # ENABLE_RANDOMSEED + +EXTRA_DIST += \ + units/systemd-random-seed.service.in + +sd.CPPFLAGS += -DRANDOM_SEED_DIR=\"$(localstatedir)/lib/systemd/\" +sd.CPPFLAGS += -DRANDOM_SEED=\"$(localstatedir)/lib/systemd/random-seed\" + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-initprogs/systemd-random-seed/random-seed.c b/src/grp-initprogs/systemd-random-seed/random-seed.c new file mode 100644 index 0000000000..7fea6069f3 --- /dev/null +++ b/src/grp-initprogs/systemd-random-seed/random-seed.c @@ -0,0 +1,176 @@ +/*** + 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 Lesser General Public License as published by + the Free Software Foundation; either version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/io-util.h" +#include "basic/log.h" +#include "basic/mkdir.h" +#include "basic/string-util.h" +#include "basic/util.h" + +#define POOL_SIZE_MIN 512 + +int main(int argc, char *argv[]) { + _cleanup_close_ int seed_fd = -1, random_fd = -1; + _cleanup_free_ void* buf = NULL; + size_t buf_size = 0; + ssize_t k; + int r, open_rw_error; + FILE *f; + bool refresh_seed_file = true; + + if (argc != 2) { + log_error("This program requires one argument."); + return EXIT_FAILURE; + } + + log_set_target(LOG_TARGET_AUTO); + log_parse_environment(); + log_open(); + + umask(0022); + + /* Read pool size, if possible */ + f = fopen("/proc/sys/kernel/random/poolsize", "re"); + if (f) { + if (fscanf(f, "%zu", &buf_size) > 0) + /* poolsize is in bits on 2.6, but we want bytes */ + buf_size /= 8; + + fclose(f); + } + + if (buf_size <= POOL_SIZE_MIN) + buf_size = POOL_SIZE_MIN; + + buf = malloc(buf_size); + if (!buf) { + r = log_oom(); + goto finish; + } + + r = mkdir_parents_label(RANDOM_SEED, 0755); + if (r < 0) { + log_error_errno(r, "Failed to create directory " RANDOM_SEED_DIR ": %m"); + goto finish; + } + + /* When we load the seed we read it and write it to the device + * and then immediately update the saved seed with new data, + * to make sure the next boot gets seeded differently. */ + + if (streq(argv[1], "load")) { + + seed_fd = open(RANDOM_SEED, O_RDWR|O_CLOEXEC|O_NOCTTY|O_CREAT, 0600); + open_rw_error = -errno; + if (seed_fd < 0) { + refresh_seed_file = false; + + seed_fd = open(RANDOM_SEED, O_RDONLY|O_CLOEXEC|O_NOCTTY); + if (seed_fd < 0) { + bool missing = errno == ENOENT; + + log_full_errno(missing ? LOG_DEBUG : LOG_ERR, + open_rw_error, "Failed to open " RANDOM_SEED " for writing: %m"); + r = log_full_errno(missing ? LOG_DEBUG : LOG_ERR, + errno, "Failed to open " RANDOM_SEED " for reading: %m"); + if (missing) + r = 0; + + goto finish; + } + } + + random_fd = open("/dev/urandom", O_RDWR|O_CLOEXEC|O_NOCTTY, 0600); + if (random_fd < 0) { + random_fd = open("/dev/urandom", O_WRONLY|O_CLOEXEC|O_NOCTTY, 0600); + if (random_fd < 0) { + r = log_error_errno(errno, "Failed to open /dev/urandom: %m"); + goto finish; + } + } + + k = loop_read(seed_fd, buf, buf_size, false); + if (k < 0) + r = log_error_errno(k, "Failed to read seed from " RANDOM_SEED ": %m"); + else if (k == 0) { + r = 0; + log_debug("Seed file " RANDOM_SEED " not yet initialized, proceeding."); + } else { + (void) lseek(seed_fd, 0, SEEK_SET); + + r = loop_write(random_fd, buf, (size_t) k, false); + if (r < 0) + log_error_errno(r, "Failed to write seed to /dev/urandom: %m"); + } + + } else if (streq(argv[1], "save")) { + + seed_fd = open(RANDOM_SEED, O_WRONLY|O_CLOEXEC|O_NOCTTY|O_CREAT, 0600); + if (seed_fd < 0) { + r = log_error_errno(errno, "Failed to open " RANDOM_SEED ": %m"); + goto finish; + } + + random_fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC|O_NOCTTY); + if (random_fd < 0) { + r = log_error_errno(errno, "Failed to open /dev/urandom: %m"); + goto finish; + } + + } else { + log_error("Unknown verb '%s'.", argv[1]); + r = -EINVAL; + goto finish; + } + + if (refresh_seed_file) { + + /* This is just a safety measure. Given that we are root and + * most likely created the file ourselves the mode and owner + * should be correct anyway. */ + (void) fchmod(seed_fd, 0600); + (void) fchown(seed_fd, 0, 0); + + k = loop_read(random_fd, buf, buf_size, false); + if (k < 0) { + r = log_error_errno(k, "Failed to read new seed from /dev/urandom: %m"); + goto finish; + } + if (k == 0) { + log_error("Got EOF while reading from /dev/urandom."); + r = -EIO; + goto finish; + } + + r = loop_write(seed_fd, buf, (size_t) k, false); + if (r < 0) + log_error_errno(r, "Failed to write new random seed file: %m"); + } + +finish: + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/grp-initprogs/systemd-random-seed/systemd-random-seed.service.in b/src/grp-initprogs/systemd-random-seed/systemd-random-seed.service.in new file mode 100644 index 0000000000..115233268d --- /dev/null +++ b/src/grp-initprogs/systemd-random-seed/systemd-random-seed.service.in @@ -0,0 +1,22 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Load/Save Random Seed +Documentation=man:systemd-random-seed.service(8) man:random(4) +DefaultDependencies=no +RequiresMountsFor=@RANDOM_SEED@ +Conflicts=shutdown.target +After=systemd-remount-fs.service +Before=sysinit.target shutdown.target + +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStart=@rootlibexecdir@/systemd-random-seed load +ExecStop=@rootlibexecdir@/systemd-random-seed save +TimeoutSec=30s diff --git a/src/grp-initprogs/systemd-rfkill/Makefile b/src/grp-initprogs/systemd-rfkill/Makefile new file mode 100644 index 0000000000..2b4430b712 --- /dev/null +++ b/src/grp-initprogs/systemd-rfkill/Makefile @@ -0,0 +1,46 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +ifneq ($(ENABLE_RFKILL),) +rootlibexec_PROGRAMS += \ + systemd-rfkill + +nodist_systemunit_DATA += \ + units/systemd-rfkill.service + +dist_systemunit_DATA += \ + units/systemd-rfkill.socket + +systemd_rfkill_SOURCES = \ + src/rfkill/rfkill.c + +systemd_rfkill_LDADD = \ + libshared.la +endif # ENABLE_RFKILL + +EXTRA_DIST += \ + units/systemd-rfkill.service.in + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-initprogs/systemd-rfkill/rfkill.c b/src/grp-initprogs/systemd-rfkill/rfkill.c new file mode 100644 index 0000000000..86779e9ac8 --- /dev/null +++ b/src/grp-initprogs/systemd-rfkill/rfkill.c @@ -0,0 +1,427 @@ +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include + +#include +#include + +#include "basic/alloc-util.h" +#include "basic/escape.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/io-util.h" +#include "basic/mkdir.h" +#include "basic/parse-util.h" +#include "basic/proc-cmdline.h" +#include "basic/string-table.h" +#include "basic/string-util.h" +#include "basic/util.h" +#include "shared/udev-util.h" + +#define EXIT_USEC (5 * USEC_PER_SEC) + +static const char* const rfkill_type_table[NUM_RFKILL_TYPES] = { + [RFKILL_TYPE_ALL] = "all", + [RFKILL_TYPE_WLAN] = "wlan", + [RFKILL_TYPE_BLUETOOTH] = "bluetooth", + [RFKILL_TYPE_UWB] = "uwb", + [RFKILL_TYPE_WIMAX] = "wimax", + [RFKILL_TYPE_WWAN] = "wwan", + [RFKILL_TYPE_GPS] = "gps", + [RFKILL_TYPE_FM] = "fm", + [RFKILL_TYPE_NFC] = "nfc", +}; + +DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(rfkill_type, int); + +static int find_device( + struct udev *udev, + const struct rfkill_event *event, + struct udev_device **ret) { + + _cleanup_free_ char *sysname = NULL; + struct udev_device *device; + const char *name; + + assert(udev); + assert(event); + assert(ret); + + if (asprintf(&sysname, "rfkill%i", event->idx) < 0) + return log_oom(); + + device = udev_device_new_from_subsystem_sysname(udev, "rfkill", sysname); + if (!device) + return log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_ERR, errno, "Failed to open device: %m"); + + name = udev_device_get_sysattr_value(device, "name"); + if (!name) { + log_debug("Device has no name, ignoring."); + udev_device_unref(device); + return -ENOENT; + } + + log_debug("Operating on rfkill device '%s'.", name); + + *ret = device; + return 0; +} + +static int wait_for_initialized( + struct udev *udev, + struct udev_device *device, + struct udev_device **ret) { + + _cleanup_udev_monitor_unref_ struct udev_monitor *monitor = NULL; + struct udev_device *d; + const char *sysname; + int watch_fd, r; + + assert(udev); + assert(device); + assert(ret); + + if (udev_device_get_is_initialized(device) != 0) { + *ret = udev_device_ref(device); + return 0; + } + + assert_se(sysname = udev_device_get_sysname(device)); + + /* Wait until the device is initialized, so that we can get + * access to the ID_PATH property */ + + monitor = udev_monitor_new_from_netlink(udev, "udev"); + if (!monitor) + return log_error_errno(errno, "Failed to acquire monitor: %m"); + + r = udev_monitor_filter_add_match_subsystem_devtype(monitor, "rfkill", NULL); + if (r < 0) + return log_error_errno(r, "Failed to add rfkill udev match to monitor: %m"); + + r = udev_monitor_enable_receiving(monitor); + if (r < 0) + return log_error_errno(r, "Failed to enable udev receiving: %m"); + + watch_fd = udev_monitor_get_fd(monitor); + if (watch_fd < 0) + return log_error_errno(watch_fd, "Failed to get watch fd: %m"); + + /* Check again, maybe things changed */ + d = udev_device_new_from_subsystem_sysname(udev, "rfkill", sysname); + if (!d) + return log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_ERR, errno, "Failed to open device: %m"); + + if (udev_device_get_is_initialized(d) != 0) { + *ret = d; + return 0; + } + + for (;;) { + _cleanup_udev_device_unref_ struct udev_device *t = NULL; + + r = fd_wait_for_event(watch_fd, POLLIN, USEC_INFINITY); + if (r == -EINTR) + continue; + if (r < 0) + return log_error_errno(r, "Failed to watch udev monitor: %m"); + + t = udev_monitor_receive_device(monitor); + if (!t) + continue; + + if (streq_ptr(udev_device_get_sysname(device), sysname)) { + *ret = udev_device_ref(t); + return 0; + } + } +} + +static int determine_state_file( + struct udev *udev, + const struct rfkill_event *event, + struct udev_device *d, + char **ret) { + + _cleanup_udev_device_unref_ struct udev_device *device = NULL; + const char *path_id, *type; + char *state_file; + int r; + + assert(event); + assert(d); + assert(ret); + + r = wait_for_initialized(udev, d, &device); + if (r < 0) + return r; + + assert_se(type = rfkill_type_to_string(event->type)); + + path_id = udev_device_get_property_value(device, "ID_PATH"); + if (path_id) { + _cleanup_free_ char *escaped_path_id = NULL; + + escaped_path_id = cescape(path_id); + if (!escaped_path_id) + return log_oom(); + + state_file = strjoin("/var/lib/systemd/rfkill/", escaped_path_id, ":", type, NULL); + } else + state_file = strjoin("/var/lib/systemd/rfkill/", type, NULL); + + if (!state_file) + return log_oom(); + + *ret = state_file; + return 0; +} + +static int load_state( + int rfkill_fd, + struct udev *udev, + const struct rfkill_event *event) { + + _cleanup_udev_device_unref_ struct udev_device *device = NULL; + _cleanup_free_ char *state_file = NULL, *value = NULL; + struct rfkill_event we; + ssize_t l; + int b, r; + + assert(rfkill_fd >= 0); + assert(udev); + assert(event); + + if (shall_restore_state() == 0) + return 0; + + r = find_device(udev, event, &device); + if (r < 0) + return r; + + r = determine_state_file(udev, event, device, &state_file); + if (r < 0) + return r; + + r = read_one_line_file(state_file, &value); + if (r == -ENOENT) { + /* No state file? Then save the current state */ + + r = write_string_file(state_file, one_zero(event->soft), WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC); + if (r < 0) + return log_error_errno(r, "Failed to write state file %s: %m", state_file); + + log_debug("Saved state '%s' to %s.", one_zero(event->soft), state_file); + return 0; + } + if (r < 0) + return log_error_errno(r, "Failed to read state file %s: %m", state_file); + + b = parse_boolean(value); + if (b < 0) + return log_error_errno(b, "Failed to parse state file %s: %m", state_file); + + we = (struct rfkill_event) { + .op = RFKILL_OP_CHANGE, + .idx = event->idx, + .soft = b, + }; + + l = write(rfkill_fd, &we, sizeof(we)); + if (l < 0) + return log_error_errno(errno, "Failed to restore rfkill state for %i: %m", event->idx); + if (l != sizeof(we)) { + log_error("Couldn't write rfkill event structure, too short."); + return -EIO; + } + + log_debug("Loaded state '%s' from %s.", one_zero(b), state_file); + return 0; +} + +static int save_state( + int rfkill_fd, + struct udev *udev, + const struct rfkill_event *event) { + + _cleanup_udev_device_unref_ struct udev_device *device = NULL; + _cleanup_free_ char *state_file = NULL; + int r; + + assert(rfkill_fd >= 0); + assert(udev); + assert(event); + + r = find_device(udev, event, &device); + if (r < 0) + return r; + + r = determine_state_file(udev, event, device, &state_file); + if (r < 0) + return r; + + r = write_string_file(state_file, one_zero(event->soft), WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC); + if (r < 0) + return log_error_errno(r, "Failed to write state file %s: %m", state_file); + + log_debug("Saved state '%s' to %s.", one_zero(event->soft), state_file); + return 0; +} + +int main(int argc, char *argv[]) { + _cleanup_udev_unref_ struct udev *udev = NULL; + _cleanup_close_ int rfkill_fd = -1; + bool ready = false; + int r, n; + + if (argc > 1) { + log_error("This program requires no arguments."); + return EXIT_FAILURE; + } + + log_set_target(LOG_TARGET_AUTO); + log_parse_environment(); + log_open(); + + umask(0022); + + udev = udev_new(); + if (!udev) { + r = log_oom(); + goto finish; + } + + r = mkdir_p("/var/lib/systemd/rfkill", 0755); + if (r < 0) { + log_error_errno(r, "Failed to create rfkill directory: %m"); + goto finish; + } + + n = sd_listen_fds(false); + if (n < 0) { + r = log_error_errno(n, "Failed to determine whether we got any file descriptors passed: %m"); + goto finish; + } + if (n > 1) { + log_error("Got too many file descriptors."); + r = -EINVAL; + goto finish; + } + + if (n == 0) { + rfkill_fd = open("/dev/rfkill", O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK); + if (rfkill_fd < 0) { + if (errno == ENOENT) { + log_debug_errno(errno, "Missing rfkill subsystem, or no device present, exiting."); + r = 0; + goto finish; + } + + r = log_error_errno(errno, "Failed to open /dev/rfkill: %m"); + goto finish; + } + } else { + rfkill_fd = SD_LISTEN_FDS_START; + + r = fd_nonblock(rfkill_fd, 1); + if (r < 0) { + log_error_errno(r, "Failed to make /dev/rfkill socket non-blocking: %m"); + goto finish; + } + } + + for (;;) { + struct rfkill_event event; + const char *type; + ssize_t l; + + l = read(rfkill_fd, &event, sizeof(event)); + if (l < 0) { + if (errno == EAGAIN) { + + if (!ready) { + /* Notify manager that we are + * now finished with + * processing whatever was + * queued */ + (void) sd_notify(false, "READY=1"); + ready = true; + } + + /* Hang around for a bit, maybe there's more coming */ + + r = fd_wait_for_event(rfkill_fd, POLLIN, EXIT_USEC); + if (r == -EINTR) + continue; + if (r < 0) { + log_error_errno(r, "Failed to poll() on device: %m"); + goto finish; + } + if (r > 0) + continue; + + log_debug("All events read and idle, exiting."); + break; + } + + log_error_errno(errno, "Failed to read from /dev/rfkill: %m"); + } + + if (l != RFKILL_EVENT_SIZE_V1) { + log_error("Read event structure of invalid size."); + r = -EIO; + goto finish; + } + + type = rfkill_type_to_string(event.type); + if (!type) { + log_debug("An rfkill device of unknown type %i discovered, ignoring.", event.type); + continue; + } + + switch (event.op) { + + case RFKILL_OP_ADD: + log_debug("A new rfkill device has been added with index %i and type %s.", event.idx, type); + (void) load_state(rfkill_fd, udev, &event); + break; + + case RFKILL_OP_DEL: + log_debug("An rfkill device has been removed with index %i and type %s", event.idx, type); + break; + + case RFKILL_OP_CHANGE: + log_debug("An rfkill device has changed state with index %i and type %s", event.idx, type); + (void) save_state(rfkill_fd, udev, &event); + break; + + default: + log_debug("Unknown event %i from /dev/rfkill for index %i and type %s, ignoring.", event.op, event.idx, type); + break; + } + } + + r = 0; + +finish: + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/grp-initprogs/systemd-rfkill/systemd-rfkill.service.in b/src/grp-initprogs/systemd-rfkill/systemd-rfkill.service.in new file mode 100644 index 0000000000..780a19b996 --- /dev/null +++ b/src/grp-initprogs/systemd-rfkill/systemd-rfkill.service.in @@ -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 Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Load/Save RF Kill Switch Status +Documentation=man:systemd-rfkill.service(8) +DefaultDependencies=no +RequiresMountsFor=/var/lib/systemd/rfkill +BindsTo=sys-devices-virtual-misc-rfkill.device +Conflicts=shutdown.target +After=sys-devices-virtual-misc-rfkill.device systemd-remount-fs.service +Before=shutdown.target + +[Service] +Type=notify +ExecStart=@rootlibexecdir@/systemd-rfkill +TimeoutSec=30s diff --git a/src/grp-initprogs/systemd-rfkill/systemd-rfkill.socket b/src/grp-initprogs/systemd-rfkill/systemd-rfkill.socket new file mode 100644 index 0000000000..20ae2f8adb --- /dev/null +++ b/src/grp-initprogs/systemd-rfkill/systemd-rfkill.socket @@ -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 Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Load/Save RF Kill Switch Status /dev/rfkill Watch +Documentation=man:systemd-rfkill.socket(8) +DefaultDependencies=no +BindsTo=sys-devices-virtual-misc-rfkill.device +After=sys-devices-virtual-misc-rfkill.device +Conflicts=shutdown.target +Before=shutdown.target + +[Socket] +ListenSpecial=/dev/rfkill +Writable=yes diff --git a/src/grp-initprogs/systemd-sysctl/50-default.sysctl b/src/grp-initprogs/systemd-sysctl/50-default.sysctl new file mode 100644 index 0000000000..def151bb84 --- /dev/null +++ b/src/grp-initprogs/systemd-sysctl/50-default.sysctl @@ -0,0 +1,40 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +# See sysctl.d(5) and core(5) for for documentation. + +# To override settings in this file, create a local file in /etc +# (e.g. /etc/sysctl.d/90-override.conf), and put any assignments +# there. + +# System Request functionality of the kernel (SYNC) +# +# Use kernel.sysrq = 1 to allow all keys. +# See http://fedoraproject.org/wiki/QA/Sysrq for a list of values and keys. +kernel.sysrq = 16 + +# Append the PID to the core filename +kernel.core_uses_pid = 1 + +# Source route verification +net.ipv4.conf.default.rp_filter = 1 +net.ipv4.conf.all.rp_filter = 1 + +# Do not accept source routing +net.ipv4.conf.default.accept_source_route = 0 +net.ipv4.conf.all.accept_source_route = 0 + +# Promote secondary addresses when the primary address is removed +net.ipv4.conf.default.promote_secondaries = 1 +net.ipv4.conf.all.promote_secondaries = 1 + +# Fair Queue CoDel packet scheduler to fight bufferbloat +net.core.default_qdisc = fq_codel + +# Enable hard and soft link protection +fs.protected_hardlinks = 1 +fs.protected_symlinks = 1 diff --git a/src/grp-initprogs/systemd-sysctl/Makefile b/src/grp-initprogs/systemd-sysctl/Makefile new file mode 100644 index 0000000000..3af80e7bef --- /dev/null +++ b/src/grp-initprogs/systemd-sysctl/Makefile @@ -0,0 +1,33 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +rootlibexec_PROGRAMS += systemd-sysctl +systemd_sysctl_SOURCES = \ + src/sysctl/sysctl.c + +systemd_sysctl_LDADD = \ + libshared.la + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-initprogs/systemd-sysctl/sysctl.c b/src/grp-initprogs/systemd-sysctl/sysctl.c new file mode 100644 index 0000000000..c44aa0aabb --- /dev/null +++ b/src/grp-initprogs/systemd-sysctl/sysctl.c @@ -0,0 +1,287 @@ +/*** + 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 Lesser General Public License as published by + the Free Software Foundation; either version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include + +#include "basic/conf-files.h" +#include "basic/def.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/hashmap.h" +#include "basic/log.h" +#include "basic/path-util.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/util.h" +#include "shared/sysctl-util.h" + +static char **arg_prefixes = NULL; + +static const char conf_file_dirs[] = CONF_PATHS_NULSTR("sysctl.d"); + +static int apply_all(Hashmap *sysctl_options) { + char *property, *value; + Iterator i; + int r = 0; + + HASHMAP_FOREACH_KEY(value, property, sysctl_options, i) { + int k; + + k = sysctl_write(property, value); + if (k < 0) { + log_full_errno(k == -ENOENT ? LOG_INFO : LOG_WARNING, k, + "Couldn't write '%s' to '%s', ignoring: %m", value, property); + + if (r == 0 && k != -ENOENT) + r = k; + } + } + + return r; +} + +static int parse_file(Hashmap *sysctl_options, const char *path, bool ignore_enoent) { + _cleanup_fclose_ FILE *f = NULL; + int r; + + assert(path); + + r = search_and_fopen_nulstr(path, "re", NULL, conf_file_dirs, &f); + if (r < 0) { + if (ignore_enoent && r == -ENOENT) + return 0; + + return log_error_errno(r, "Failed to open file '%s', ignoring: %m", path); + } + + log_debug("Parsing %s", path); + while (!feof(f)) { + char l[LINE_MAX], *p, *value, *new_value, *property, *existing; + void *v; + int k; + + if (!fgets(l, sizeof(l), f)) { + if (feof(f)) + break; + + return log_error_errno(errno, "Failed to read file '%s', ignoring: %m", path); + } + + p = strstrip(l); + if (!*p) + continue; + + if (strchr(COMMENTS "\n", *p)) + continue; + + value = strchr(p, '='); + if (!value) { + log_error("Line is not an assignment in file '%s': %s", path, value); + + if (r == 0) + r = -EINVAL; + continue; + } + + *value = 0; + value++; + + p = sysctl_normalize(strstrip(p)); + value = strstrip(value); + + if (!strv_isempty(arg_prefixes)) { + char **i, *t; + STRV_FOREACH(i, arg_prefixes) { + t = path_startswith(*i, "/proc/sys/"); + if (t == NULL) + t = *i; + if (path_startswith(p, t)) + goto found; + } + /* not found */ + continue; + } + +found: + existing = hashmap_get2(sysctl_options, p, &v); + if (existing) { + if (streq(value, existing)) + continue; + + log_debug("Overwriting earlier assignment of %s in file '%s'.", p, path); + free(hashmap_remove(sysctl_options, p)); + free(v); + } + + property = strdup(p); + if (!property) + return log_oom(); + + new_value = strdup(value); + if (!new_value) { + free(property); + return log_oom(); + } + + k = hashmap_put(sysctl_options, property, new_value); + if (k < 0) { + log_error_errno(k, "Failed to add sysctl variable %s to hashmap: %m", property); + free(property); + free(new_value); + return k; + } + } + + return r; +} + +static void help(void) { + printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n" + "Applies kernel sysctl settings.\n\n" + " -h --help Show this help\n" + " --version Show package version\n" + " --prefix=PATH Only apply rules with the specified prefix\n" + , program_invocation_short_name); +} + +static int parse_argv(int argc, char *argv[]) { + + enum { + ARG_VERSION = 0x100, + ARG_PREFIX + }; + + static const struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, ARG_VERSION }, + { "prefix", required_argument, NULL, ARG_PREFIX }, + {} + }; + + int c; + + assert(argc >= 0); + assert(argv); + + while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) + + switch (c) { + + case 'h': + help(); + return 0; + + case ARG_VERSION: + return version(); + + case ARG_PREFIX: { + char *p; + + /* We used to require people to specify absolute paths + * in /proc/sys in the past. This is kinda useless, but + * we need to keep compatibility. We now support any + * sysctl name available. */ + sysctl_normalize(optarg); + + if (startswith(optarg, "/proc/sys")) + p = strdup(optarg); + else + p = strappend("/proc/sys/", optarg); + if (!p) + return log_oom(); + + if (strv_consume(&arg_prefixes, p) < 0) + return log_oom(); + + break; + } + + case '?': + return -EINVAL; + + default: + assert_not_reached("Unhandled option"); + } + + return 1; +} + +int main(int argc, char *argv[]) { + int r = 0, k; + Hashmap *sysctl_options; + + 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); + + sysctl_options = hashmap_new(&string_hash_ops); + if (!sysctl_options) { + r = log_oom(); + goto finish; + } + + r = 0; + + if (argc > optind) { + int i; + + for (i = optind; i < argc; i++) { + k = parse_file(sysctl_options, argv[i], false); + if (k < 0 && r == 0) + r = k; + } + } else { + _cleanup_strv_free_ char **files = NULL; + char **f; + + r = conf_files_list_nulstr(&files, ".conf", NULL, conf_file_dirs); + if (r < 0) { + log_error_errno(r, "Failed to enumerate sysctl.d files: %m"); + goto finish; + } + + STRV_FOREACH(f, files) { + k = parse_file(sysctl_options, *f, true); + if (k < 0 && r == 0) + r = k; + } + } + + k = apply_all(sysctl_options); + if (k < 0 && r == 0) + r = k; + +finish: + hashmap_free_free_free(sysctl_options); + strv_free(arg_prefixes); + + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/grp-initprogs/systemd-sysctl/systemd-sysctl.service.in b/src/grp-initprogs/systemd-sysctl/systemd-sysctl.service.in new file mode 100644 index 0000000000..d784c6426d --- /dev/null +++ b/src/grp-initprogs/systemd-sysctl/systemd-sysctl.service.in @@ -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 Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Apply Kernel Variables +Documentation=man:systemd-sysctl.service(8) man:sysctl.d(5) +DefaultDependencies=no +Conflicts=shutdown.target +After=systemd-modules-load.service +Before=sysinit.target shutdown.target +ConditionPathIsReadWrite=/proc/sys/ + +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStart=@rootlibexecdir@/systemd-sysctl +TimeoutSec=90s diff --git a/src/grp-initprogs/systemd-sysusers/Makefile b/src/grp-initprogs/systemd-sysusers/Makefile new file mode 100644 index 0000000000..9339b9fd8b --- /dev/null +++ b/src/grp-initprogs/systemd-sysusers/Makefile @@ -0,0 +1,53 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +ifneq ($(ENABLE_SYSUSERS),) +systemd_sysusers_SOURCES = \ + src/sysusers/sysusers.c + +systemd_sysusers_LDADD = \ + libshared.la + +rootbin_PROGRAMS += \ + systemd-sysusers + +nodist_systemunit_DATA += \ + units/systemd-sysusers.service + +SYSINIT_TARGET_WANTS += \ + systemd-sysusers.service + +nodist_sysusers_DATA = \ + sysusers.d/basic.conf + +INSTALL_DIRS += \ + $(sysusersdir) +endif # ENABLE_SYSUSERS + +EXTRA_DIST += \ + units/systemd-sysusers.service.in \ + sysusers.d/basic.sysusers.in + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-initprogs/systemd-sysusers/basic.sysusers.in b/src/grp-initprogs/systemd-sysusers/basic.sysusers.in new file mode 100644 index 0000000000..b2dc5ebd4f --- /dev/null +++ b/src/grp-initprogs/systemd-sysusers/basic.sysusers.in @@ -0,0 +1,36 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +# The superuser +u root 0 "Super User" /root + +# The nobody user for NFS file systems +u nobody 65534 "Nobody" - + +# Administrator group: can *see* more than normal users +g adm - - - + +# Administrator group: can *do* more than normal users +g wheel - - - + +# Access to certain kernel and userspace facilities +g kmem - - - +g tty @TTY_GID@ - - +g utmp - - - + +# Hardware access groups +g audio - - - +g cdrom - - - +g dialout - - - +g disk - - - +g input - - - +g lp - - - +g tape - - - +g video - - - + +# Default group for normal users +g users - - - diff --git a/src/grp-initprogs/systemd-sysusers/systemd-sysusers.service.in b/src/grp-initprogs/systemd-sysusers/systemd-sysusers.service.in new file mode 100644 index 0000000000..4d8309ab6b --- /dev/null +++ b/src/grp-initprogs/systemd-sysusers/systemd-sysusers.service.in @@ -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 Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Create System Users +Documentation=man:sysusers.d(5) man:systemd-sysusers.service(8) +DefaultDependencies=no +Conflicts=shutdown.target +After=systemd-remount-fs.service +Before=sysinit.target shutdown.target systemd-update-done.service +ConditionNeedsUpdate=/etc + +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStart=@rootbindir@/systemd-sysusers +TimeoutSec=90s diff --git a/src/grp-initprogs/systemd-sysusers/sysusers.c b/src/grp-initprogs/systemd-sysusers/sysusers.c new file mode 100644 index 0000000000..a71d777720 --- /dev/null +++ b/src/grp-initprogs/systemd-sysusers/sysusers.c @@ -0,0 +1,1919 @@ +/*** + This file is part of systemd. + + Copyright 2014 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/conf-files.h" +#include "basic/copy.h" +#include "basic/def.h" +#include "basic/fd-util.h" +#include "basic/fileio-label.h" +#include "basic/formats-util.h" +#include "basic/hashmap.h" +#include "basic/path-util.h" +#include "basic/selinux-util.h" +#include "basic/smack-util.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/user-util.h" +#include "basic/utf8.h" +#include "basic/util.h" +#include "shared/specifier.h" +#include "shared/uid-range.h" + +typedef enum ItemType { + ADD_USER = 'u', + ADD_GROUP = 'g', + ADD_MEMBER = 'm', + ADD_RANGE = 'r', +} ItemType; +typedef struct Item { + ItemType type; + + char *name; + char *uid_path; + char *gid_path; + char *description; + char *home; + + gid_t gid; + uid_t uid; + + bool gid_set:1; + bool uid_set:1; + + bool todo_user:1; + bool todo_group:1; +} Item; + +static char *arg_root = NULL; + +static const char conf_file_dirs[] = CONF_PATHS_NULSTR("sysusers.d"); + +static Hashmap *users = NULL, *groups = NULL; +static Hashmap *todo_uids = NULL, *todo_gids = NULL; +static Hashmap *members = NULL; + +static Hashmap *database_uid = NULL, *database_user = NULL; +static Hashmap *database_gid = NULL, *database_group = NULL; + +static uid_t search_uid = UID_INVALID; +static UidRange *uid_range = NULL; +static unsigned n_uid_range = 0; + +static int load_user_database(void) { + _cleanup_fclose_ FILE *f = NULL; + const char *passwd_path; + struct passwd *pw; + int r; + + passwd_path = prefix_roota(arg_root, "/etc/passwd"); + f = fopen(passwd_path, "re"); + if (!f) + return errno == ENOENT ? 0 : -errno; + + r = hashmap_ensure_allocated(&database_user, &string_hash_ops); + if (r < 0) + return r; + + r = hashmap_ensure_allocated(&database_uid, NULL); + if (r < 0) + return r; + + errno = 0; + while ((pw = fgetpwent(f))) { + char *n; + int k, q; + + n = strdup(pw->pw_name); + if (!n) + return -ENOMEM; + + k = hashmap_put(database_user, n, UID_TO_PTR(pw->pw_uid)); + if (k < 0 && k != -EEXIST) { + free(n); + return k; + } + + q = hashmap_put(database_uid, UID_TO_PTR(pw->pw_uid), n); + if (q < 0 && q != -EEXIST) { + if (k < 0) + free(n); + return q; + } + + if (q < 0 && k < 0) + free(n); + + errno = 0; + } + if (!IN_SET(errno, 0, ENOENT)) + return -errno; + + return 0; +} + +static int load_group_database(void) { + _cleanup_fclose_ FILE *f = NULL; + const char *group_path; + struct group *gr; + int r; + + group_path = prefix_roota(arg_root, "/etc/group"); + f = fopen(group_path, "re"); + if (!f) + return errno == ENOENT ? 0 : -errno; + + r = hashmap_ensure_allocated(&database_group, &string_hash_ops); + if (r < 0) + return r; + + r = hashmap_ensure_allocated(&database_gid, NULL); + if (r < 0) + return r; + + errno = 0; + while ((gr = fgetgrent(f))) { + char *n; + int k, q; + + n = strdup(gr->gr_name); + if (!n) + return -ENOMEM; + + k = hashmap_put(database_group, n, GID_TO_PTR(gr->gr_gid)); + if (k < 0 && k != -EEXIST) { + free(n); + return k; + } + + q = hashmap_put(database_gid, GID_TO_PTR(gr->gr_gid), n); + if (q < 0 && q != -EEXIST) { + if (k < 0) + free(n); + return q; + } + + if (q < 0 && k < 0) + free(n); + + errno = 0; + } + if (!IN_SET(errno, 0, ENOENT)) + return -errno; + + return 0; +} + +static int make_backup(const char *target, const char *x) { + _cleanup_close_ int src = -1; + _cleanup_fclose_ FILE *dst = NULL; + char *backup, *temp; + struct timespec ts[2]; + struct stat st; + int r; + + src = open(x, O_RDONLY|O_CLOEXEC|O_NOCTTY); + if (src < 0) { + if (errno == ENOENT) /* No backup necessary... */ + return 0; + + return -errno; + } + + if (fstat(src, &st) < 0) + return -errno; + + r = fopen_temporary_label(target, x, &dst, &temp); + if (r < 0) + return r; + + r = copy_bytes(src, fileno(dst), (uint64_t) -1, true); + if (r < 0) + goto fail; + + /* Don't fail on chmod() or chown(). If it stays owned by us + * and/or unreadable by others, then it isn't too bad... */ + + backup = strjoina(x, "-"); + + /* Copy over the access mask */ + if (fchmod(fileno(dst), st.st_mode & 07777) < 0) + log_warning_errno(errno, "Failed to change mode on %s: %m", backup); + + if (fchown(fileno(dst), st.st_uid, st.st_gid)< 0) + log_warning_errno(errno, "Failed to change ownership of %s: %m", backup); + + ts[0] = st.st_atim; + ts[1] = st.st_mtim; + if (futimens(fileno(dst), ts) < 0) + log_warning_errno(errno, "Failed to fix access and modification time of %s: %m", backup); + + if (rename(temp, backup) < 0) + goto fail; + + return 0; + +fail: + unlink(temp); + return r; +} + +static int putgrent_with_members(const struct group *gr, FILE *group) { + char **a; + + assert(gr); + assert(group); + + a = hashmap_get(members, gr->gr_name); + if (a) { + _cleanup_strv_free_ char **l = NULL; + bool added = false; + char **i; + + l = strv_copy(gr->gr_mem); + if (!l) + return -ENOMEM; + + STRV_FOREACH(i, a) { + if (strv_find(l, *i)) + continue; + + if (strv_extend(&l, *i) < 0) + return -ENOMEM; + + added = true; + } + + if (added) { + struct group t; + + strv_uniq(l); + strv_sort(l); + + t = *gr; + t.gr_mem = l; + + errno = 0; + if (putgrent(&t, group) != 0) + return errno > 0 ? -errno : -EIO; + + return 1; + } + } + + errno = 0; + if (putgrent(gr, group) != 0) + return errno > 0 ? -errno : -EIO; + + return 0; +} + +static int putsgent_with_members(const struct sgrp *sg, FILE *gshadow) { + char **a; + + assert(sg); + assert(gshadow); + + a = hashmap_get(members, sg->sg_namp); + if (a) { + _cleanup_strv_free_ char **l = NULL; + bool added = false; + char **i; + + l = strv_copy(sg->sg_mem); + if (!l) + return -ENOMEM; + + STRV_FOREACH(i, a) { + if (strv_find(l, *i)) + continue; + + if (strv_extend(&l, *i) < 0) + return -ENOMEM; + + added = true; + } + + if (added) { + struct sgrp t; + + strv_uniq(l); + strv_sort(l); + + t = *sg; + t.sg_mem = l; + + errno = 0; + if (putsgent(&t, gshadow) != 0) + return errno > 0 ? -errno : -EIO; + + return 1; + } + } + + errno = 0; + if (putsgent(sg, gshadow) != 0) + return errno > 0 ? -errno : -EIO; + + return 0; +} + +static int sync_rights(FILE *from, FILE *to) { + struct stat st; + + if (fstat(fileno(from), &st) < 0) + return -errno; + + if (fchmod(fileno(to), st.st_mode & 07777) < 0) + return -errno; + + if (fchown(fileno(to), st.st_uid, st.st_gid) < 0) + return -errno; + + return 0; +} + +static int rename_and_apply_smack(const char *temp_path, const char *dest_path) { + int r = 0; + if (rename(temp_path, dest_path) < 0) + return -errno; + +#ifdef SMACK_RUN_LABEL + r = mac_smack_apply(dest_path, SMACK_ATTR_ACCESS, SMACK_FLOOR_LABEL); + if (r < 0) + return r; +#endif + return r; +} + +static int write_files(void) { + + _cleanup_fclose_ FILE *passwd = NULL, *group = NULL, *shadow = NULL, *gshadow = NULL; + _cleanup_free_ char *passwd_tmp = NULL, *group_tmp = NULL, *shadow_tmp = NULL, *gshadow_tmp = NULL; + const char *passwd_path = NULL, *group_path = NULL, *shadow_path = NULL, *gshadow_path = NULL; + bool group_changed = false; + Iterator iterator; + Item *i; + int r; + + if (hashmap_size(todo_gids) > 0 || hashmap_size(members) > 0) { + _cleanup_fclose_ FILE *original = NULL; + + /* First we update the actual group list file */ + group_path = prefix_roota(arg_root, "/etc/group"); + r = fopen_temporary_label("/etc/group", group_path, &group, &group_tmp); + if (r < 0) + goto finish; + + original = fopen(group_path, "re"); + if (original) { + struct group *gr; + + r = sync_rights(original, group); + if (r < 0) + goto finish; + + errno = 0; + while ((gr = fgetgrent(original))) { + /* Safety checks against name and GID + * collisions. Normally, this should + * be unnecessary, but given that we + * look at the entries anyway here, + * let's make an extra verification + * step that we don't generate + * duplicate entries. */ + + i = hashmap_get(groups, gr->gr_name); + if (i && i->todo_group) { + log_error("%s: Group \"%s\" already exists.", group_path, gr->gr_name); + r = -EEXIST; + goto finish; + } + + if (hashmap_contains(todo_gids, GID_TO_PTR(gr->gr_gid))) { + log_error("%s: Detected collision for GID " GID_FMT ".", group_path, gr->gr_gid); + r = -EEXIST; + goto finish; + } + + r = putgrent_with_members(gr, group); + if (r < 0) + goto finish; + if (r > 0) + group_changed = true; + + errno = 0; + } + if (!IN_SET(errno, 0, ENOENT)) { + r = -errno; + goto finish; + } + + } else if (errno != ENOENT) { + r = -errno; + goto finish; + } else if (fchmod(fileno(group), 0644) < 0) { + r = -errno; + goto finish; + } + + HASHMAP_FOREACH(i, todo_gids, iterator) { + struct group n = { + .gr_name = i->name, + .gr_gid = i->gid, + .gr_passwd = (char*) "x", + }; + + r = putgrent_with_members(&n, group); + if (r < 0) + goto finish; + + group_changed = true; + } + + r = fflush_and_check(group); + if (r < 0) + goto finish; + + if (original) { + fclose(original); + original = NULL; + } + + /* OK, now also update the shadow file for the group list */ + gshadow_path = prefix_roota(arg_root, "/etc/gshadow"); + r = fopen_temporary_label("/etc/gshadow", gshadow_path, &gshadow, &gshadow_tmp); + if (r < 0) + goto finish; + + original = fopen(gshadow_path, "re"); + if (original) { + struct sgrp *sg; + + r = sync_rights(original, gshadow); + if (r < 0) + goto finish; + + errno = 0; + while ((sg = fgetsgent(original))) { + + i = hashmap_get(groups, sg->sg_namp); + if (i && i->todo_group) { + log_error("%s: Group \"%s\" already exists.", gshadow_path, sg->sg_namp); + r = -EEXIST; + goto finish; + } + + r = putsgent_with_members(sg, gshadow); + if (r < 0) + goto finish; + if (r > 0) + group_changed = true; + + errno = 0; + } + if (!IN_SET(errno, 0, ENOENT)) { + r = -errno; + goto finish; + } + + } else if (errno != ENOENT) { + r = -errno; + goto finish; + } else if (fchmod(fileno(gshadow), 0000) < 0) { + r = -errno; + goto finish; + } + + HASHMAP_FOREACH(i, todo_gids, iterator) { + struct sgrp n = { + .sg_namp = i->name, + .sg_passwd = (char*) "!!", + }; + + r = putsgent_with_members(&n, gshadow); + if (r < 0) + goto finish; + + group_changed = true; + } + + r = fflush_and_check(gshadow); + if (r < 0) + goto finish; + } + + if (hashmap_size(todo_uids) > 0) { + _cleanup_fclose_ FILE *original = NULL; + long lstchg; + + /* First we update the user database itself */ + passwd_path = prefix_roota(arg_root, "/etc/passwd"); + r = fopen_temporary_label("/etc/passwd", passwd_path, &passwd, &passwd_tmp); + if (r < 0) + goto finish; + + original = fopen(passwd_path, "re"); + if (original) { + struct passwd *pw; + + r = sync_rights(original, passwd); + if (r < 0) + goto finish; + + errno = 0; + while ((pw = fgetpwent(original))) { + + i = hashmap_get(users, pw->pw_name); + if (i && i->todo_user) { + log_error("%s: User \"%s\" already exists.", passwd_path, pw->pw_name); + r = -EEXIST; + goto finish; + } + + if (hashmap_contains(todo_uids, UID_TO_PTR(pw->pw_uid))) { + log_error("%s: Detected collision for UID " UID_FMT ".", passwd_path, pw->pw_uid); + r = -EEXIST; + goto finish; + } + + errno = 0; + if (putpwent(pw, passwd) < 0) { + r = errno ? -errno : -EIO; + goto finish; + } + + errno = 0; + } + if (!IN_SET(errno, 0, ENOENT)) { + r = -errno; + goto finish; + } + + } else if (errno != ENOENT) { + r = -errno; + goto finish; + } else if (fchmod(fileno(passwd), 0644) < 0) { + r = -errno; + goto finish; + } + + HASHMAP_FOREACH(i, todo_uids, iterator) { + struct passwd n = { + .pw_name = i->name, + .pw_uid = i->uid, + .pw_gid = i->gid, + .pw_gecos = i->description, + + /* "x" means the password is stored in + * the shadow file */ + .pw_passwd = (char*) "x", + + /* We default to the root directory as home */ + .pw_dir = i->home ? i->home : (char*) "/", + + /* Initialize the shell to nologin, + * with one exception: for root we + * patch in something special */ + .pw_shell = i->uid == 0 ? (char*) "/bin/sh" : (char*) "/sbin/nologin", + }; + + errno = 0; + if (putpwent(&n, passwd) != 0) { + r = errno ? -errno : -EIO; + goto finish; + } + } + + r = fflush_and_check(passwd); + if (r < 0) + goto finish; + + if (original) { + fclose(original); + original = NULL; + } + + /* The we update the shadow database */ + shadow_path = prefix_roota(arg_root, "/etc/shadow"); + r = fopen_temporary_label("/etc/shadow", shadow_path, &shadow, &shadow_tmp); + if (r < 0) + goto finish; + + lstchg = (long) (now(CLOCK_REALTIME) / USEC_PER_DAY); + + original = fopen(shadow_path, "re"); + if (original) { + struct spwd *sp; + + r = sync_rights(original, shadow); + if (r < 0) + goto finish; + + errno = 0; + while ((sp = fgetspent(original))) { + + i = hashmap_get(users, sp->sp_namp); + if (i && i->todo_user) { + /* we will update the existing entry */ + sp->sp_lstchg = lstchg; + + /* only the /etc/shadow stage is left, so we can + * safely remove the item from the todo set */ + i->todo_user = false; + hashmap_remove(todo_uids, UID_TO_PTR(i->uid)); + } + + errno = 0; + if (putspent(sp, shadow) < 0) { + r = errno ? -errno : -EIO; + goto finish; + } + + errno = 0; + } + if (!IN_SET(errno, 0, ENOENT)) { + r = -errno; + goto finish; + } + } else if (errno != ENOENT) { + r = -errno; + goto finish; + } else if (fchmod(fileno(shadow), 0000) < 0) { + r = -errno; + goto finish; + } + + HASHMAP_FOREACH(i, todo_uids, iterator) { + struct spwd n = { + .sp_namp = i->name, + .sp_pwdp = (char*) "!!", + .sp_lstchg = lstchg, + .sp_min = -1, + .sp_max = -1, + .sp_warn = -1, + .sp_inact = -1, + .sp_expire = -1, + .sp_flag = (unsigned long) -1, /* this appears to be what everybody does ... */ + }; + + errno = 0; + if (putspent(&n, shadow) != 0) { + r = errno ? -errno : -EIO; + goto finish; + } + } + + r = fflush_and_check(shadow); + if (r < 0) + goto finish; + } + + /* Make a backup of the old files */ + if (group_changed) { + if (group) { + r = make_backup("/etc/group", group_path); + if (r < 0) + goto finish; + } + if (gshadow) { + r = make_backup("/etc/gshadow", gshadow_path); + if (r < 0) + goto finish; + } + } + + if (passwd) { + r = make_backup("/etc/passwd", passwd_path); + if (r < 0) + goto finish; + } + if (shadow) { + r = make_backup("/etc/shadow", shadow_path); + if (r < 0) + goto finish; + } + + /* And make the new files count */ + if (group_changed) { + if (group) { + r = rename_and_apply_smack(group_tmp, group_path); + if (r < 0) + goto finish; + + group_tmp = mfree(group_tmp); + } + if (gshadow) { + r = rename_and_apply_smack(gshadow_tmp, gshadow_path); + if (r < 0) + goto finish; + + gshadow_tmp = mfree(gshadow_tmp); + } + } + + if (passwd) { + r = rename_and_apply_smack(passwd_tmp, passwd_path); + if (r < 0) + goto finish; + + passwd_tmp = mfree(passwd_tmp); + } + if (shadow) { + r = rename_and_apply_smack(shadow_tmp, shadow_path); + if (r < 0) + goto finish; + + shadow_tmp = mfree(shadow_tmp); + } + + r = 0; + +finish: + if (passwd_tmp) + unlink(passwd_tmp); + if (shadow_tmp) + unlink(shadow_tmp); + if (group_tmp) + unlink(group_tmp); + if (gshadow_tmp) + unlink(gshadow_tmp); + + return r; +} + +static int uid_is_ok(uid_t uid, const char *name) { + struct passwd *p; + struct group *g; + const char *n; + Item *i; + + /* Let's see if we already have assigned the UID a second time */ + if (hashmap_get(todo_uids, UID_TO_PTR(uid))) + return 0; + + /* Try to avoid using uids that are already used by a group + * that doesn't have the same name as our new user. */ + i = hashmap_get(todo_gids, GID_TO_PTR(uid)); + if (i && !streq(i->name, name)) + return 0; + + /* Let's check the files directly */ + if (hashmap_contains(database_uid, UID_TO_PTR(uid))) + return 0; + + n = hashmap_get(database_gid, GID_TO_PTR(uid)); + if (n && !streq(n, name)) + return 0; + + /* Let's also check via NSS, to avoid UID clashes over LDAP and such, just in case */ + if (!arg_root) { + errno = 0; + p = getpwuid(uid); + if (p) + return 0; + if (!IN_SET(errno, 0, ENOENT)) + return -errno; + + errno = 0; + g = getgrgid((gid_t) uid); + if (g) { + if (!streq(g->gr_name, name)) + return 0; + } else if (!IN_SET(errno, 0, ENOENT)) + return -errno; + } + + return 1; +} + +static int root_stat(const char *p, struct stat *st) { + const char *fix; + + fix = prefix_roota(arg_root, p); + if (stat(fix, st) < 0) + return -errno; + + return 0; +} + +static int read_id_from_file(Item *i, uid_t *_uid, gid_t *_gid) { + struct stat st; + bool found_uid = false, found_gid = false; + uid_t uid = 0; + gid_t gid = 0; + + assert(i); + + /* First, try to get the gid directly */ + if (_gid && i->gid_path && root_stat(i->gid_path, &st) >= 0) { + gid = st.st_gid; + found_gid = true; + } + + /* Then, try to get the uid directly */ + if ((_uid || (_gid && !found_gid)) + && i->uid_path + && root_stat(i->uid_path, &st) >= 0) { + + uid = st.st_uid; + found_uid = true; + + /* If we need the gid, but had no success yet, also derive it from the uid path */ + if (_gid && !found_gid) { + gid = st.st_gid; + found_gid = true; + } + } + + /* If that didn't work yet, then let's reuse the gid as uid */ + if (_uid && !found_uid && i->gid_path) { + + if (found_gid) { + uid = (uid_t) gid; + found_uid = true; + } else if (root_stat(i->gid_path, &st) >= 0) { + uid = (uid_t) st.st_gid; + found_uid = true; + } + } + + if (_uid) { + if (!found_uid) + return 0; + + *_uid = uid; + } + + if (_gid) { + if (!found_gid) + return 0; + + *_gid = gid; + } + + return 1; +} + +static int add_user(Item *i) { + void *z; + int r; + + assert(i); + + /* Check the database directly */ + z = hashmap_get(database_user, i->name); + if (z) { + log_debug("User %s already exists.", i->name); + i->uid = PTR_TO_UID(z); + i->uid_set = true; + return 0; + } + + if (!arg_root) { + struct passwd *p; + + /* Also check NSS */ + errno = 0; + p = getpwnam(i->name); + if (p) { + log_debug("User %s already exists.", i->name); + i->uid = p->pw_uid; + i->uid_set = true; + + r = free_and_strdup(&i->description, p->pw_gecos); + if (r < 0) + return log_oom(); + + return 0; + } + if (!IN_SET(errno, 0, ENOENT)) + return log_error_errno(errno, "Failed to check if user %s already exists: %m", i->name); + } + + /* Try to use the suggested numeric uid */ + if (i->uid_set) { + r = uid_is_ok(i->uid, i->name); + if (r < 0) + return log_error_errno(r, "Failed to verify uid " UID_FMT ": %m", i->uid); + if (r == 0) { + log_debug("Suggested user ID " UID_FMT " for %s already used.", i->uid, i->name); + i->uid_set = false; + } + } + + /* If that didn't work, try to read it from the specified path */ + if (!i->uid_set) { + uid_t c; + + if (read_id_from_file(i, &c, NULL) > 0) { + + if (c <= 0 || !uid_range_contains(uid_range, n_uid_range, c)) + log_debug("User ID " UID_FMT " of file not suitable for %s.", c, i->name); + else { + r = uid_is_ok(c, i->name); + if (r < 0) + return log_error_errno(r, "Failed to verify uid " UID_FMT ": %m", i->uid); + else if (r > 0) { + i->uid = c; + i->uid_set = true; + } else + log_debug("User ID " UID_FMT " of file for %s is already used.", c, i->name); + } + } + } + + /* Otherwise, try to reuse the group ID */ + if (!i->uid_set && i->gid_set) { + r = uid_is_ok((uid_t) i->gid, i->name); + if (r < 0) + return log_error_errno(r, "Failed to verify uid " UID_FMT ": %m", i->uid); + if (r > 0) { + i->uid = (uid_t) i->gid; + i->uid_set = true; + } + } + + /* And if that didn't work either, let's try to find a free one */ + if (!i->uid_set) { + for (;;) { + r = uid_range_next_lower(uid_range, n_uid_range, &search_uid); + if (r < 0) { + log_error("No free user ID available for %s.", i->name); + return r; + } + + r = uid_is_ok(search_uid, i->name); + if (r < 0) + return log_error_errno(r, "Failed to verify uid " UID_FMT ": %m", i->uid); + else if (r > 0) + break; + } + + i->uid_set = true; + i->uid = search_uid; + } + + r = hashmap_ensure_allocated(&todo_uids, NULL); + if (r < 0) + return log_oom(); + + r = hashmap_put(todo_uids, UID_TO_PTR(i->uid), i); + if (r < 0) + return log_oom(); + + i->todo_user = true; + log_info("Creating user %s (%s) with uid " UID_FMT " and gid " GID_FMT ".", i->name, strna(i->description), i->uid, i->gid); + + return 0; +} + +static int gid_is_ok(gid_t gid) { + struct group *g; + struct passwd *p; + + if (hashmap_get(todo_gids, GID_TO_PTR(gid))) + return 0; + + /* Avoid reusing gids that are already used by a different user */ + if (hashmap_get(todo_uids, UID_TO_PTR(gid))) + return 0; + + if (hashmap_contains(database_gid, GID_TO_PTR(gid))) + return 0; + + if (hashmap_contains(database_uid, UID_TO_PTR(gid))) + return 0; + + if (!arg_root) { + errno = 0; + g = getgrgid(gid); + if (g) + return 0; + if (!IN_SET(errno, 0, ENOENT)) + return -errno; + + errno = 0; + p = getpwuid((uid_t) gid); + if (p) + return 0; + if (!IN_SET(errno, 0, ENOENT)) + return -errno; + } + + return 1; +} + +static int add_group(Item *i) { + void *z; + int r; + + assert(i); + + /* Check the database directly */ + z = hashmap_get(database_group, i->name); + if (z) { + log_debug("Group %s already exists.", i->name); + i->gid = PTR_TO_GID(z); + i->gid_set = true; + return 0; + } + + /* Also check NSS */ + if (!arg_root) { + struct group *g; + + errno = 0; + g = getgrnam(i->name); + if (g) { + log_debug("Group %s already exists.", i->name); + i->gid = g->gr_gid; + i->gid_set = true; + return 0; + } + if (!IN_SET(errno, 0, ENOENT)) + return log_error_errno(errno, "Failed to check if group %s already exists: %m", i->name); + } + + /* Try to use the suggested numeric gid */ + if (i->gid_set) { + r = gid_is_ok(i->gid); + if (r < 0) + return log_error_errno(r, "Failed to verify gid " GID_FMT ": %m", i->gid); + if (r == 0) { + log_debug("Suggested group ID " GID_FMT " for %s already used.", i->gid, i->name); + i->gid_set = false; + } + } + + /* Try to reuse the numeric uid, if there's one */ + if (!i->gid_set && i->uid_set) { + r = gid_is_ok((gid_t) i->uid); + if (r < 0) + return log_error_errno(r, "Failed to verify gid " GID_FMT ": %m", i->gid); + if (r > 0) { + i->gid = (gid_t) i->uid; + i->gid_set = true; + } + } + + /* If that didn't work, try to read it from the specified path */ + if (!i->gid_set) { + gid_t c; + + if (read_id_from_file(i, NULL, &c) > 0) { + + if (c <= 0 || !uid_range_contains(uid_range, n_uid_range, c)) + log_debug("Group ID " GID_FMT " of file not suitable for %s.", c, i->name); + else { + r = gid_is_ok(c); + if (r < 0) + return log_error_errno(r, "Failed to verify gid " GID_FMT ": %m", i->gid); + else if (r > 0) { + i->gid = c; + i->gid_set = true; + } else + log_debug("Group ID " GID_FMT " of file for %s already used.", c, i->name); + } + } + } + + /* And if that didn't work either, let's try to find a free one */ + if (!i->gid_set) { + for (;;) { + /* We look for new GIDs in the UID pool! */ + r = uid_range_next_lower(uid_range, n_uid_range, &search_uid); + if (r < 0) { + log_error("No free group ID available for %s.", i->name); + return r; + } + + r = gid_is_ok(search_uid); + if (r < 0) + return log_error_errno(r, "Failed to verify gid " GID_FMT ": %m", i->gid); + else if (r > 0) + break; + } + + i->gid_set = true; + i->gid = search_uid; + } + + r = hashmap_ensure_allocated(&todo_gids, NULL); + if (r < 0) + return log_oom(); + + r = hashmap_put(todo_gids, GID_TO_PTR(i->gid), i); + if (r < 0) + return log_oom(); + + i->todo_group = true; + log_info("Creating group %s with gid " GID_FMT ".", i->name, i->gid); + + return 0; +} + +static int process_item(Item *i) { + int r; + + assert(i); + + switch (i->type) { + + case ADD_USER: + r = add_group(i); + if (r < 0) + return r; + + return add_user(i); + + case ADD_GROUP: { + Item *j; + + j = hashmap_get(users, i->name); + if (j) { + /* There's already user to be created for this + * name, let's process that in one step */ + + if (i->gid_set) { + j->gid = i->gid; + j->gid_set = true; + } + + if (i->gid_path) { + r = free_and_strdup(&j->gid_path, i->gid_path); + if (r < 0) + return log_oom(); + } + + return 0; + } + + return add_group(i); + } + + default: + assert_not_reached("Unknown item type"); + } +} + +static void item_free(Item *i) { + + if (!i) + return; + + free(i->name); + free(i->uid_path); + free(i->gid_path); + free(i->description); + free(i); +} + +DEFINE_TRIVIAL_CLEANUP_FUNC(Item*, item_free); + +static int add_implicit(void) { + char *g, **l; + Iterator iterator; + int r; + + /* Implicitly create additional users and groups, if they were listed in "m" lines */ + + HASHMAP_FOREACH_KEY(l, g, members, iterator) { + Item *i; + char **m; + + i = hashmap_get(groups, g); + if (!i) { + _cleanup_(item_freep) Item *j = NULL; + + r = hashmap_ensure_allocated(&groups, &string_hash_ops); + if (r < 0) + return log_oom(); + + j = new0(Item, 1); + if (!j) + return log_oom(); + + j->type = ADD_GROUP; + j->name = strdup(g); + if (!j->name) + return log_oom(); + + r = hashmap_put(groups, j->name, j); + if (r < 0) + return log_oom(); + + log_debug("Adding implicit group '%s' due to m line", j->name); + j = NULL; + } + + STRV_FOREACH(m, l) { + + i = hashmap_get(users, *m); + if (!i) { + _cleanup_(item_freep) Item *j = NULL; + + r = hashmap_ensure_allocated(&users, &string_hash_ops); + if (r < 0) + return log_oom(); + + j = new0(Item, 1); + if (!j) + return log_oom(); + + j->type = ADD_USER; + j->name = strdup(*m); + if (!j->name) + return log_oom(); + + r = hashmap_put(users, j->name, j); + if (r < 0) + return log_oom(); + + log_debug("Adding implicit user '%s' due to m line", j->name); + j = NULL; + } + } + } + + return 0; +} + +static bool item_equal(Item *a, Item *b) { + assert(a); + assert(b); + + if (a->type != b->type) + return false; + + if (!streq_ptr(a->name, b->name)) + return false; + + if (!streq_ptr(a->uid_path, b->uid_path)) + return false; + + if (!streq_ptr(a->gid_path, b->gid_path)) + return false; + + if (!streq_ptr(a->description, b->description)) + return false; + + if (a->uid_set != b->uid_set) + return false; + + if (a->uid_set && a->uid != b->uid) + return false; + + if (a->gid_set != b->gid_set) + return false; + + if (a->gid_set && a->gid != b->gid) + return false; + + if (!streq_ptr(a->home, b->home)) + return false; + + return true; +} + +static bool valid_user_group_name(const char *u) { + const char *i; + long sz; + + if (isempty(u)) + return false; + + if (!(u[0] >= 'a' && u[0] <= 'z') && + !(u[0] >= 'A' && u[0] <= 'Z') && + u[0] != '_') + return false; + + for (i = u+1; *i; i++) { + if (!(*i >= 'a' && *i <= 'z') && + !(*i >= 'A' && *i <= 'Z') && + !(*i >= '0' && *i <= '9') && + *i != '_' && + *i != '-') + return false; + } + + sz = sysconf(_SC_LOGIN_NAME_MAX); + assert_se(sz > 0); + + if ((size_t) (i-u) > (size_t) sz) + return false; + + if ((size_t) (i-u) > UT_NAMESIZE - 1) + return false; + + return true; +} + +static bool valid_gecos(const char *d) { + + if (!d) + return false; + + if (!utf8_is_valid(d)) + return false; + + if (string_has_cc(d, NULL)) + return false; + + /* Colons are used as field separators, and hence not OK */ + if (strchr(d, ':')) + return false; + + return true; +} + +static bool valid_home(const char *p) { + + if (isempty(p)) + return false; + + if (!utf8_is_valid(p)) + return false; + + if (string_has_cc(p, NULL)) + return false; + + if (!path_is_absolute(p)) + return false; + + if (!path_is_safe(p)) + return false; + + /* Colons are used as field separators, and hence not OK */ + if (strchr(p, ':')) + return false; + + return true; +} + +static int parse_line(const char *fname, unsigned line, const char *buffer) { + + static const Specifier specifier_table[] = { + { 'm', specifier_machine_id, NULL }, + { 'b', specifier_boot_id, NULL }, + { 'H', specifier_host_name, NULL }, + { 'v', specifier_kernel_release, NULL }, + {} + }; + + _cleanup_free_ char *action = NULL, *name = NULL, *id = NULL, *resolved_name = NULL, *resolved_id = NULL, *description = NULL, *home = NULL; + _cleanup_(item_freep) Item *i = NULL; + Item *existing; + Hashmap *h; + int r; + const char *p; + + assert(fname); + assert(line >= 1); + assert(buffer); + + /* Parse columns */ + p = buffer; + r = extract_many_words(&p, NULL, EXTRACT_QUOTES, &action, &name, &id, &description, &home, NULL); + if (r < 0) { + log_error("[%s:%u] Syntax error.", fname, line); + return r; + } + if (r < 2) { + log_error("[%s:%u] Missing action and name columns.", fname, line); + return -EINVAL; + } + if (!isempty(p)) { + log_error("[%s:%u] Trailing garbage.", fname, line); + return -EINVAL; + } + + /* Verify action */ + if (strlen(action) != 1) { + log_error("[%s:%u] Unknown modifier '%s'", fname, line, action); + return -EINVAL; + } + + if (!IN_SET(action[0], ADD_USER, ADD_GROUP, ADD_MEMBER, ADD_RANGE)) { + log_error("[%s:%u] Unknown command command type '%c'.", fname, line, action[0]); + return -EBADMSG; + } + + /* Verify name */ + if (isempty(name) || streq(name, "-")) + name = mfree(name); + + if (name) { + r = specifier_printf(name, specifier_table, NULL, &resolved_name); + if (r < 0) { + log_error("[%s:%u] Failed to replace specifiers: %s", fname, line, name); + return r; + } + + if (!valid_user_group_name(resolved_name)) { + log_error("[%s:%u] '%s' is not a valid user or group name.", fname, line, resolved_name); + return -EINVAL; + } + } + + /* Verify id */ + if (isempty(id) || streq(id, "-")) + id = mfree(id); + + if (id) { + r = specifier_printf(id, specifier_table, NULL, &resolved_id); + if (r < 0) { + log_error("[%s:%u] Failed to replace specifiers: %s", fname, line, name); + return r; + } + } + + /* Verify description */ + if (isempty(description) || streq(description, "-")) + description = mfree(description); + + if (description) { + if (!valid_gecos(description)) { + log_error("[%s:%u] '%s' is not a valid GECOS field.", fname, line, description); + return -EINVAL; + } + } + + /* Verify home */ + if (isempty(home) || streq(home, "-")) + home = mfree(home); + + if (home) { + if (!valid_home(home)) { + log_error("[%s:%u] '%s' is not a valid home directory field.", fname, line, home); + return -EINVAL; + } + } + + switch (action[0]) { + + case ADD_RANGE: + if (resolved_name) { + log_error("[%s:%u] Lines of type 'r' don't take a name field.", fname, line); + return -EINVAL; + } + + if (!resolved_id) { + log_error("[%s:%u] Lines of type 'r' require a ID range in the third field.", fname, line); + return -EINVAL; + } + + if (description) { + log_error("[%s:%u] Lines of type 'r' don't take a GECOS field.", fname, line); + return -EINVAL; + } + + if (home) { + log_error("[%s:%u] Lines of type 'r' don't take a home directory field.", fname, line); + return -EINVAL; + } + + r = uid_range_add_str(&uid_range, &n_uid_range, resolved_id); + if (r < 0) { + log_error("[%s:%u] Invalid UID range %s.", fname, line, resolved_id); + return -EINVAL; + } + + return 0; + + case ADD_MEMBER: { + char **l; + + /* Try to extend an existing member or group item */ + if (!name) { + log_error("[%s:%u] Lines of type 'm' require a user name in the second field.", fname, line); + return -EINVAL; + } + + if (!resolved_id) { + log_error("[%s:%u] Lines of type 'm' require a group name in the third field.", fname, line); + return -EINVAL; + } + + if (!valid_user_group_name(resolved_id)) { + log_error("[%s:%u] '%s' is not a valid user or group name.", fname, line, resolved_id); + return -EINVAL; + } + + if (description) { + log_error("[%s:%u] Lines of type 'm' don't take a GECOS field.", fname, line); + return -EINVAL; + } + + if (home) { + log_error("[%s:%u] Lines of type 'm' don't take a home directory field.", fname, line); + return -EINVAL; + } + + r = hashmap_ensure_allocated(&members, &string_hash_ops); + if (r < 0) + return log_oom(); + + l = hashmap_get(members, resolved_id); + if (l) { + /* A list for this group name already exists, let's append to it */ + r = strv_push(&l, resolved_name); + if (r < 0) + return log_oom(); + + resolved_name = NULL; + + assert_se(hashmap_update(members, resolved_id, l) >= 0); + } else { + /* No list for this group name exists yet, create one */ + + l = new0(char *, 2); + if (!l) + return -ENOMEM; + + l[0] = resolved_name; + l[1] = NULL; + + r = hashmap_put(members, resolved_id, l); + if (r < 0) { + free(l); + return log_oom(); + } + + resolved_id = resolved_name = NULL; + } + + return 0; + } + + case ADD_USER: + if (!name) { + log_error("[%s:%u] Lines of type 'u' require a user name in the second field.", fname, line); + return -EINVAL; + } + + r = hashmap_ensure_allocated(&users, &string_hash_ops); + if (r < 0) + return log_oom(); + + i = new0(Item, 1); + if (!i) + return log_oom(); + + if (resolved_id) { + if (path_is_absolute(resolved_id)) { + i->uid_path = resolved_id; + resolved_id = NULL; + + path_kill_slashes(i->uid_path); + } else { + r = parse_uid(resolved_id, &i->uid); + if (r < 0) { + log_error("Failed to parse UID: %s", id); + return -EBADMSG; + } + + i->uid_set = true; + } + } + + i->description = description; + description = NULL; + + i->home = home; + home = NULL; + + h = users; + break; + + case ADD_GROUP: + if (!name) { + log_error("[%s:%u] Lines of type 'g' require a user name in the second field.", fname, line); + return -EINVAL; + } + + if (description) { + log_error("[%s:%u] Lines of type 'g' don't take a GECOS field.", fname, line); + return -EINVAL; + } + + if (home) { + log_error("[%s:%u] Lines of type 'g' don't take a home directory field.", fname, line); + return -EINVAL; + } + + r = hashmap_ensure_allocated(&groups, &string_hash_ops); + if (r < 0) + return log_oom(); + + i = new0(Item, 1); + if (!i) + return log_oom(); + + if (resolved_id) { + if (path_is_absolute(resolved_id)) { + i->gid_path = resolved_id; + resolved_id = NULL; + + path_kill_slashes(i->gid_path); + } else { + r = parse_gid(resolved_id, &i->gid); + if (r < 0) { + log_error("Failed to parse GID: %s", id); + return -EBADMSG; + } + + i->gid_set = true; + } + } + + h = groups; + break; + + default: + return -EBADMSG; + } + + i->type = action[0]; + i->name = resolved_name; + resolved_name = NULL; + + existing = hashmap_get(h, i->name); + if (existing) { + + /* Two identical items are fine */ + if (!item_equal(existing, i)) + log_warning("Two or more conflicting lines for %s configured, ignoring.", i->name); + + return 0; + } + + r = hashmap_put(h, i->name, i); + if (r < 0) + return log_oom(); + + i = NULL; + return 0; +} + +static int read_config_file(const char *fn, bool ignore_enoent) { + _cleanup_fclose_ FILE *rf = NULL; + FILE *f = NULL; + char line[LINE_MAX]; + unsigned v = 0; + int r = 0; + + assert(fn); + + if (streq(fn, "-")) + f = stdin; + else { + r = search_and_fopen_nulstr(fn, "re", arg_root, conf_file_dirs, &rf); + if (r < 0) { + if (ignore_enoent && r == -ENOENT) + return 0; + + return log_error_errno(r, "Failed to open '%s', ignoring: %m", fn); + } + + f = rf; + } + + FOREACH_LINE(line, f, break) { + char *l; + int k; + + v++; + + l = strstrip(line); + if (*l == '#' || *l == 0) + continue; + + k = parse_line(fn, v, l); + if (k < 0 && r == 0) + r = k; + } + + if (ferror(f)) { + log_error_errno(errno, "Failed to read from file %s: %m", fn); + if (r == 0) + r = -EIO; + } + + return r; +} + +static void free_database(Hashmap *by_name, Hashmap *by_id) { + char *name; + + for (;;) { + name = hashmap_first(by_id); + if (!name) + break; + + hashmap_remove(by_name, name); + + hashmap_steal_first_key(by_id); + free(name); + } + + while ((name = hashmap_steal_first_key(by_name))) + free(name); + + hashmap_free(by_name); + hashmap_free(by_id); +} + +static void help(void) { + printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n" + "Creates system user accounts.\n\n" + " -h --help Show this help\n" + " --version Show package version\n" + " --root=PATH Operate on an alternate filesystem root\n" + , program_invocation_short_name); +} + +static int parse_argv(int argc, char *argv[]) { + + enum { + ARG_VERSION = 0x100, + ARG_ROOT, + }; + + static const struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, ARG_VERSION }, + { "root", required_argument, NULL, ARG_ROOT }, + {} + }; + + int c, r; + + assert(argc >= 0); + assert(argv); + + while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) + + switch (c) { + + case 'h': + help(); + return 0; + + case ARG_VERSION: + return version(); + + case ARG_ROOT: + r = parse_path_argument_and_warn(optarg, true, &arg_root); + if (r < 0) + return r; + break; + + case '?': + return -EINVAL; + + default: + assert_not_reached("Unhandled option"); + } + + return 1; +} + +int main(int argc, char *argv[]) { + + _cleanup_close_ int lock = -1; + Iterator iterator; + int r, k; + Item *i; + char *n; + + r = parse_argv(argc, argv); + if (r <= 0) + goto finish; + + log_set_target(LOG_TARGET_AUTO); + log_parse_environment(); + log_open(); + + umask(0022); + + r = mac_selinux_init(); + if (r < 0) { + log_error_errno(r, "SELinux setup failed: %m"); + goto finish; + } + + if (optind < argc) { + int j; + + for (j = optind; j < argc; j++) { + k = read_config_file(argv[j], false); + if (k < 0 && r == 0) + r = k; + } + } else { + _cleanup_strv_free_ char **files = NULL; + char **f; + + r = conf_files_list_nulstr(&files, ".conf", arg_root, conf_file_dirs); + if (r < 0) { + log_error_errno(r, "Failed to enumerate sysusers.d files: %m"); + goto finish; + } + + STRV_FOREACH(f, files) { + k = read_config_file(*f, true); + if (k < 0 && r == 0) + r = k; + } + } + + if (!uid_range) { + /* Default to default range of 1..SYSTEMD_UID_MAX */ + r = uid_range_add(&uid_range, &n_uid_range, 1, SYSTEM_UID_MAX); + if (r < 0) { + log_oom(); + goto finish; + } + } + + r = add_implicit(); + if (r < 0) + goto finish; + + lock = take_etc_passwd_lock(arg_root); + if (lock < 0) { + log_error_errno(lock, "Failed to take lock: %m"); + goto finish; + } + + r = load_user_database(); + if (r < 0) { + log_error_errno(r, "Failed to load user database: %m"); + goto finish; + } + + r = load_group_database(); + if (r < 0) { + log_error_errno(r, "Failed to read group database: %m"); + goto finish; + } + + HASHMAP_FOREACH(i, groups, iterator) + process_item(i); + + HASHMAP_FOREACH(i, users, iterator) + process_item(i); + + r = write_files(); + if (r < 0) + log_error_errno(r, "Failed to write files: %m"); + +finish: + while ((i = hashmap_steal_first(groups))) + item_free(i); + + while ((i = hashmap_steal_first(users))) + item_free(i); + + while ((n = hashmap_first_key(members))) { + strv_free(hashmap_steal_first(members)); + free(n); + } + + hashmap_free(groups); + hashmap_free(users); + hashmap_free(members); + hashmap_free(todo_uids); + hashmap_free(todo_gids); + + free_database(database_user, database_uid); + free_database(database_group, database_gid); + + free(arg_root); + + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/grp-initprogs/systemd-tmpfiles/Makefile b/src/grp-initprogs/systemd-tmpfiles/Makefile new file mode 100644 index 0000000000..de085fa334 --- /dev/null +++ b/src/grp-initprogs/systemd-tmpfiles/Makefile @@ -0,0 +1,80 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +ifneq ($(ENABLE_TMPFILES),) +systemd_tmpfiles_SOURCES = \ + src/tmpfiles/tmpfiles.c + +systemd_tmpfiles_LDADD = \ + libshared.la + +rootbin_PROGRAMS += \ + systemd-tmpfiles + +dist_systemunit_DATA += \ + units/systemd-tmpfiles-clean.timer + +nodist_systemunit_DATA += \ + units/systemd-tmpfiles-setup-dev.service \ + units/systemd-tmpfiles-setup.service \ + units/systemd-tmpfiles-clean.service + +nodist_tmpfiles_DATA = \ + tmpfiles.d/etc.conf + +dist_tmpfiles_DATA = \ + tmpfiles.d/systemd-nologin.conf \ + tmpfiles.d/tmp.conf \ + tmpfiles.d/x11.conf \ + tmpfiles.d/var.conf \ + tmpfiles.d/home.conf + +ifneq ($(HAVE_SYSV_COMPAT),) +dist_tmpfiles_DATA += \ + tmpfiles.d/legacy.conf +endif # HAVE_SYSV_COMPAT + +SYSINIT_TARGET_WANTS += \ + systemd-tmpfiles-setup-dev.service \ + systemd-tmpfiles-setup.service + +dist_zshcompletion_data += \ + shell-completion/zsh/_systemd-tmpfiles + +TIMERS_TARGET_WANTS += \ + systemd-tmpfiles-clean.timer + +INSTALL_DIRS += \ + $(tmpfilesdir) \ + $(sysconfdir)/tmpfiles.d +endif # ENABLE_TMPFILES + +EXTRA_DIST += \ + tmpfiles.d/etc.tmpfiles.m4 \ + units/systemd-tmpfiles-setup-dev.service.in \ + units/systemd-tmpfiles-setup.service.in \ + units/systemd-tmpfiles-clean.service.in + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-initprogs/systemd-tmpfiles/etc.tmpfiles.m4 b/src/grp-initprogs/systemd-tmpfiles/etc.tmpfiles.m4 new file mode 100644 index 0000000000..928105ea8d --- /dev/null +++ b/src/grp-initprogs/systemd-tmpfiles/etc.tmpfiles.m4 @@ -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 Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +# See tmpfiles.d(5) for details + +L /etc/os-release - - - - ../usr/lib/os-release +L /etc/localtime - - - - ../usr/share/zoneinfo/UTC +L+ /etc/mtab - - - - ../proc/self/mounts +m4_ifdef(`HAVE_SMACK_RUN_LABEL', +t /etc/mtab - - - - security.SMACK64=_ +)m4_dnl +C /etc/nsswitch.conf - - - - +m4_ifdef(`HAVE_PAM', +C /etc/pam.d - - - - +)m4_dnl diff --git a/src/grp-initprogs/systemd-tmpfiles/home.tmpfiles b/src/grp-initprogs/systemd-tmpfiles/home.tmpfiles new file mode 100644 index 0000000000..9f25b83392 --- /dev/null +++ b/src/grp-initprogs/systemd-tmpfiles/home.tmpfiles @@ -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 Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +# See tmpfiles.d(5) for details + +Q /home 0755 - - - +q /srv 0755 - - - diff --git a/src/grp-initprogs/systemd-tmpfiles/legacy.tmpfiles b/src/grp-initprogs/systemd-tmpfiles/legacy.tmpfiles new file mode 100644 index 0000000000..62e2ae0986 --- /dev/null +++ b/src/grp-initprogs/systemd-tmpfiles/legacy.tmpfiles @@ -0,0 +1,27 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +# See tmpfiles.d(5) for details + +# These files are considered legacy and are unnecessary on legacy-free +# systems. + +d /run/lock 0755 root root - +L /var/lock - - - - ../run/lock + +# /run/lock/subsys is used for serializing SysV service execution, and +# hence without use on SysV-less systems. + +d /run/lock/subsys 0755 root root - + +# /forcefsck, /fastboot and /forcequotacheck are deprecated in favor of the +# kernel command line options 'fsck.mode=force', 'fsck.mode=skip' and +# 'quotacheck.mode=force' + +r! /forcefsck +r! /fastboot +r! /forcequotacheck diff --git a/src/grp-initprogs/systemd-tmpfiles/systemd-nologin.tmpfiles b/src/grp-initprogs/systemd-tmpfiles/systemd-nologin.tmpfiles new file mode 100644 index 0000000000..a30a8da604 --- /dev/null +++ b/src/grp-initprogs/systemd-tmpfiles/systemd-nologin.tmpfiles @@ -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 Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +# See tmpfiles.d(5), systemd-user-session.service(5) and pam_nologin(8). +# This file has special suffix so it is not run by mistake. + +F! /run/nologin 0644 - - - "System is booting up. See pam_nologin(8)" diff --git a/src/grp-initprogs/systemd-tmpfiles/systemd-tmpfiles-clean.service.in b/src/grp-initprogs/systemd-tmpfiles/systemd-tmpfiles-clean.service.in new file mode 100644 index 0000000000..133c8c94c4 --- /dev/null +++ b/src/grp-initprogs/systemd-tmpfiles/systemd-tmpfiles-clean.service.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 Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Cleanup of Temporary Directories +Documentation=man:tmpfiles.d(5) man:systemd-tmpfiles(8) +DefaultDependencies=no +Conflicts=shutdown.target +After=local-fs.target time-sync.target +Before=shutdown.target + +[Service] +Type=oneshot +ExecStart=@rootbindir@/systemd-tmpfiles --clean +IOSchedulingClass=idle diff --git a/src/grp-initprogs/systemd-tmpfiles/systemd-tmpfiles-clean.timer b/src/grp-initprogs/systemd-tmpfiles/systemd-tmpfiles-clean.timer new file mode 100644 index 0000000000..9975dcfaca --- /dev/null +++ b/src/grp-initprogs/systemd-tmpfiles/systemd-tmpfiles-clean.timer @@ -0,0 +1,14 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Daily Cleanup of Temporary Directories +Documentation=man:tmpfiles.d(5) man:systemd-tmpfiles(8) + +[Timer] +OnBootSec=15min +OnUnitActiveSec=1d diff --git a/src/grp-initprogs/systemd-tmpfiles/systemd-tmpfiles-setup-dev.service.in b/src/grp-initprogs/systemd-tmpfiles/systemd-tmpfiles-setup-dev.service.in new file mode 100644 index 0000000000..0123a030e4 --- /dev/null +++ b/src/grp-initprogs/systemd-tmpfiles/systemd-tmpfiles-setup-dev.service.in @@ -0,0 +1,20 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Create Static Device Nodes in /dev +Documentation=man:tmpfiles.d(5) man:systemd-tmpfiles(8) +DefaultDependencies=no +Conflicts=shutdown.target +After=systemd-sysusers.service +Before=sysinit.target local-fs-pre.target systemd-udevd.service shutdown.target +ConditionCapability=CAP_SYS_MODULE + +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStart=@rootbindir@/systemd-tmpfiles --prefix=/dev --create --boot diff --git a/src/grp-initprogs/systemd-tmpfiles/systemd-tmpfiles-setup.service.in b/src/grp-initprogs/systemd-tmpfiles/systemd-tmpfiles-setup.service.in new file mode 100644 index 0000000000..e895cda0e6 --- /dev/null +++ b/src/grp-initprogs/systemd-tmpfiles/systemd-tmpfiles-setup.service.in @@ -0,0 +1,20 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Create Volatile Files and Directories +Documentation=man:tmpfiles.d(5) man:systemd-tmpfiles(8) +DefaultDependencies=no +Conflicts=shutdown.target +After=local-fs.target systemd-sysusers.service +Before=sysinit.target shutdown.target +RefuseManualStop=yes + +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStart=@rootbindir@/systemd-tmpfiles --create --remove --boot --exclude-prefix=/dev diff --git a/src/grp-initprogs/systemd-tmpfiles/systemd-tmpfiles.completion.zsh b/src/grp-initprogs/systemd-tmpfiles/systemd-tmpfiles.completion.zsh new file mode 100644 index 0000000000..6ff02e5d98 --- /dev/null +++ b/src/grp-initprogs/systemd-tmpfiles/systemd-tmpfiles.completion.zsh @@ -0,0 +1,13 @@ +#compdef systemd-tmpfiles + +_arguments \ + {-h,--help}'[Show help]' \ + '--version[Show package version]' \ + '--create[Create, set ownership/permissions based on the config files.]' \ + '--clean[Clean up all files and directories with an age parameter configured.]' \ + '--remove[All files and directories marked with r, R in the configuration files are removed.]' \ + '--boot[Execute actions only safe at boot]' \ + '--prefix=[Only apply rules that apply to paths with the specified prefix.]' \ + '--exclude-prefix=[Ignore rules that apply to paths with the specified prefix.]' \ + '--root=[Operate on an alternate filesystem root]:directory:_directories' \ + '*::files:_files' diff --git a/src/grp-initprogs/systemd-tmpfiles/tmp.tmpfiles b/src/grp-initprogs/systemd-tmpfiles/tmp.tmpfiles new file mode 100644 index 0000000000..fe5225d751 --- /dev/null +++ b/src/grp-initprogs/systemd-tmpfiles/tmp.tmpfiles @@ -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 Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 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 +q /tmp 1777 root root 10d +q /var/tmp 1777 root root 30d diff --git a/src/grp-initprogs/systemd-tmpfiles/tmpfiles.c b/src/grp-initprogs/systemd-tmpfiles/tmpfiles.c new file mode 100644 index 0000000000..f865b9cc3e --- /dev/null +++ b/src/grp-initprogs/systemd-tmpfiles/tmpfiles.c @@ -0,0 +1,2344 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering, Kay Sievers + Copyright 2015 Zbigniew Jędrzejewski-Szmek + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "basic/alloc-util.h" +#include "basic/btrfs-util.h" +#include "basic/capability-util.h" +#include "basic/chattr-util.h" +#include "basic/conf-files.h" +#include "basic/copy.h" +#include "basic/def.h" +#include "basic/escape.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/formats-util.h" +#include "basic/fs-util.h" +#include "basic/glob-util.h" +#include "basic/io-util.h" +#include "basic/label.h" +#include "basic/log.h" +#include "basic/macro.h" +#include "basic/missing.h" +#include "basic/mkdir.h" +#include "basic/mount-util.h" +#include "basic/parse-util.h" +#include "basic/path-util.h" +#include "basic/rm-rf.h" +#include "basic/selinux-util.h" +#include "basic/set.h" +#include "basic/stat-util.h" +#include "basic/stdio-util.h" +#include "basic/string-table.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/umask-util.h" +#include "basic/user-util.h" +#include "basic/util.h" +#include "shared/acl-util.h" +#include "shared/specifier.h" + +/* This reads all files listed in /etc/tmpfiles.d/?*.conf and creates + * them in the file system. This is intended to be used to create + * properly owned directories beneath /tmp, /var/tmp, /run, which are + * volatile and hence need to be recreated on bootup. */ + +typedef enum ItemType { + /* These ones take file names */ + CREATE_FILE = 'f', + TRUNCATE_FILE = 'F', + CREATE_DIRECTORY = 'd', + TRUNCATE_DIRECTORY = 'D', + CREATE_SUBVOLUME = 'v', + CREATE_SUBVOLUME_INHERIT_QUOTA = 'q', + CREATE_SUBVOLUME_NEW_QUOTA = 'Q', + CREATE_FIFO = 'p', + CREATE_SYMLINK = 'L', + CREATE_CHAR_DEVICE = 'c', + CREATE_BLOCK_DEVICE = 'b', + COPY_FILES = 'C', + + /* These ones take globs */ + WRITE_FILE = 'w', + EMPTY_DIRECTORY = 'e', + SET_XATTR = 't', + RECURSIVE_SET_XATTR = 'T', + SET_ACL = 'a', + RECURSIVE_SET_ACL = 'A', + SET_ATTRIBUTE = 'h', + RECURSIVE_SET_ATTRIBUTE = 'H', + IGNORE_PATH = 'x', + IGNORE_DIRECTORY_PATH = 'X', + REMOVE_PATH = 'r', + RECURSIVE_REMOVE_PATH = 'R', + RELABEL_PATH = 'z', + RECURSIVE_RELABEL_PATH = 'Z', + ADJUST_MODE = 'm', /* legacy, 'z' is identical to this */ +} ItemType; + +typedef struct Item { + ItemType type; + + char *path; + char *argument; + char **xattrs; +#ifdef HAVE_ACL + acl_t acl_access; + acl_t acl_default; +#endif + uid_t uid; + gid_t gid; + mode_t mode; + usec_t age; + + dev_t major_minor; + unsigned attribute_value; + unsigned attribute_mask; + + bool uid_set:1; + bool gid_set:1; + bool mode_set:1; + bool age_set:1; + bool mask_perms:1; + bool attribute_set:1; + + bool keep_first_level:1; + + bool force:1; + + bool done:1; +} Item; + +typedef struct ItemArray { + Item *items; + size_t count; + size_t size; +} ItemArray; + +static bool arg_create = false; +static bool arg_clean = false; +static bool arg_remove = false; +static bool arg_boot = false; + +static char **arg_include_prefixes = NULL; +static char **arg_exclude_prefixes = NULL; +static char *arg_root = NULL; + +static const char conf_file_dirs[] = CONF_PATHS_NULSTR("tmpfiles.d"); + +#define MAX_DEPTH 256 + +static OrderedHashmap *items = NULL, *globs = NULL; +static Set *unix_sockets = NULL; + +static const Specifier specifier_table[] = { + { 'm', specifier_machine_id, NULL }, + { 'b', specifier_boot_id, NULL }, + { 'H', specifier_host_name, NULL }, + { 'v', specifier_kernel_release, NULL }, + {} +}; + +static bool needs_glob(ItemType t) { + return IN_SET(t, + WRITE_FILE, + IGNORE_PATH, + IGNORE_DIRECTORY_PATH, + REMOVE_PATH, + RECURSIVE_REMOVE_PATH, + EMPTY_DIRECTORY, + ADJUST_MODE, + RELABEL_PATH, + RECURSIVE_RELABEL_PATH, + SET_XATTR, + RECURSIVE_SET_XATTR, + SET_ACL, + RECURSIVE_SET_ACL, + SET_ATTRIBUTE, + RECURSIVE_SET_ATTRIBUTE); +} + +static bool takes_ownership(ItemType t) { + return IN_SET(t, + CREATE_FILE, + TRUNCATE_FILE, + CREATE_DIRECTORY, + EMPTY_DIRECTORY, + TRUNCATE_DIRECTORY, + CREATE_SUBVOLUME, + CREATE_SUBVOLUME_INHERIT_QUOTA, + CREATE_SUBVOLUME_NEW_QUOTA, + CREATE_FIFO, + CREATE_SYMLINK, + CREATE_CHAR_DEVICE, + CREATE_BLOCK_DEVICE, + COPY_FILES, + WRITE_FILE, + IGNORE_PATH, + IGNORE_DIRECTORY_PATH, + REMOVE_PATH, + RECURSIVE_REMOVE_PATH); +} + +static struct Item* find_glob(OrderedHashmap *h, const char *match) { + ItemArray *j; + Iterator i; + + ORDERED_HASHMAP_FOREACH(j, h, i) { + unsigned n; + + for (n = 0; n < j->count; n++) { + Item *item = j->items + n; + + if (fnmatch(item->path, match, FNM_PATHNAME|FNM_PERIOD) == 0) + return item; + } + } + + return NULL; +} + +static void load_unix_sockets(void) { + _cleanup_fclose_ FILE *f = NULL; + char line[LINE_MAX]; + + if (unix_sockets) + return; + + /* We maintain a cache of the sockets we found in + * /proc/net/unix to speed things up a little. */ + + unix_sockets = set_new(&string_hash_ops); + if (!unix_sockets) + return; + + f = fopen("/proc/net/unix", "re"); + if (!f) + return; + + /* Skip header */ + if (!fgets(line, sizeof(line), f)) + goto fail; + + for (;;) { + char *p, *s; + int k; + + if (!fgets(line, sizeof(line), f)) + break; + + truncate_nl(line); + + p = strchr(line, ':'); + if (!p) + continue; + + if (strlen(p) < 37) + continue; + + p += 37; + p += strspn(p, WHITESPACE); + p += strcspn(p, WHITESPACE); /* skip one more word */ + p += strspn(p, WHITESPACE); + + if (*p != '/') + continue; + + s = strdup(p); + if (!s) + goto fail; + + path_kill_slashes(s); + + k = set_consume(unix_sockets, s); + if (k < 0 && k != -EEXIST) + goto fail; + } + + return; + +fail: + set_free_free(unix_sockets); + unix_sockets = NULL; +} + +static bool unix_socket_alive(const char *fn) { + assert(fn); + + load_unix_sockets(); + + if (unix_sockets) + return !!set_get(unix_sockets, (char*) fn); + + /* We don't know, so assume yes */ + return true; +} + +static int dir_is_mount_point(DIR *d, const char *subdir) { + + union file_handle_union h = FILE_HANDLE_INIT; + int mount_id_parent, mount_id; + int r_p, r; + + r_p = name_to_handle_at(dirfd(d), ".", &h.handle, &mount_id_parent, 0); + if (r_p < 0) + r_p = -errno; + + h.handle.handle_bytes = MAX_HANDLE_SZ; + r = name_to_handle_at(dirfd(d), subdir, &h.handle, &mount_id, 0); + if (r < 0) + r = -errno; + + /* got no handle; make no assumptions, return error */ + if (r_p < 0 && r < 0) + return r_p; + + /* got both handles; if they differ, it is a mount point */ + if (r_p >= 0 && r >= 0) + return mount_id_parent != mount_id; + + /* got only one handle; assume different mount points if one + * of both queries was not supported by the filesystem */ + if (r_p == -ENOSYS || r_p == -EOPNOTSUPP || r == -ENOSYS || r == -EOPNOTSUPP) + return true; + + /* return error */ + if (r_p < 0) + return r_p; + return r; +} + +static DIR* xopendirat_nomod(int dirfd, const char *path) { + DIR *dir; + + dir = xopendirat(dirfd, path, O_NOFOLLOW|O_NOATIME); + if (dir) + return dir; + + log_debug_errno(errno, "Cannot open %sdirectory \"%s\": %m", dirfd == AT_FDCWD ? "" : "sub", path); + if (errno != EPERM) + return NULL; + + dir = xopendirat(dirfd, path, O_NOFOLLOW); + if (!dir) + log_debug_errno(errno, "Cannot open %sdirectory \"%s\": %m", dirfd == AT_FDCWD ? "" : "sub", path); + + return dir; +} + +static DIR* opendir_nomod(const char *path) { + return xopendirat_nomod(AT_FDCWD, path); +} + +static int dir_cleanup( + Item *i, + const char *p, + DIR *d, + const struct stat *ds, + usec_t cutoff, + dev_t rootdev, + bool mountpoint, + int maxdepth, + bool keep_this_level) { + + struct dirent *dent; + struct timespec times[2]; + bool deleted = false; + int r = 0; + + while ((dent = readdir(d))) { + struct stat s; + usec_t age; + _cleanup_free_ char *sub_path = NULL; + + if (STR_IN_SET(dent->d_name, ".", "..")) + continue; + + if (fstatat(dirfd(d), dent->d_name, &s, AT_SYMLINK_NOFOLLOW) < 0) { + if (errno == ENOENT) + continue; + + /* FUSE, NFS mounts, SELinux might return EACCES */ + if (errno == EACCES) + log_debug_errno(errno, "stat(%s/%s) failed: %m", p, dent->d_name); + else + log_error_errno(errno, "stat(%s/%s) failed: %m", p, dent->d_name); + r = -errno; + continue; + } + + /* Stay on the same filesystem */ + if (s.st_dev != rootdev) { + log_debug("Ignoring \"%s/%s\": different filesystem.", p, dent->d_name); + continue; + } + + /* Try to detect bind mounts of the same filesystem instance; they + * do not differ in device major/minors. This type of query is not + * supported on all kernels or filesystem types though. */ + if (S_ISDIR(s.st_mode) && dir_is_mount_point(d, dent->d_name) > 0) { + log_debug("Ignoring \"%s/%s\": different mount of the same filesystem.", + p, dent->d_name); + continue; + } + + /* Do not delete read-only files owned by root */ + if (s.st_uid == 0 && !(s.st_mode & S_IWUSR)) { + log_debug("Ignoring \"%s/%s\": read-only and owner by root.", p, dent->d_name); + continue; + } + + sub_path = strjoin(p, "/", dent->d_name, NULL); + if (!sub_path) { + r = log_oom(); + goto finish; + } + + /* Is there an item configured for this path? */ + if (ordered_hashmap_get(items, sub_path)) { + log_debug("Ignoring \"%s\": a separate entry exists.", sub_path); + continue; + } + + if (find_glob(globs, sub_path)) { + log_debug("Ignoring \"%s\": a separate glob exists.", sub_path); + continue; + } + + if (S_ISDIR(s.st_mode)) { + + if (mountpoint && + streq(dent->d_name, "lost+found") && + s.st_uid == 0) { + log_debug("Ignoring \"%s\".", sub_path); + continue; + } + + if (maxdepth <= 0) + log_warning("Reached max depth on \"%s\".", sub_path); + else { + _cleanup_closedir_ DIR *sub_dir; + int q; + + sub_dir = xopendirat_nomod(dirfd(d), dent->d_name); + if (!sub_dir) { + if (errno != ENOENT) + r = log_error_errno(errno, "opendir(%s) failed: %m", sub_path); + + continue; + } + + q = dir_cleanup(i, sub_path, sub_dir, &s, cutoff, rootdev, false, maxdepth-1, false); + if (q < 0) + r = q; + } + + /* Note: if you are wondering why we don't + * support the sticky bit for excluding + * directories from cleaning like we do it for + * other file system objects: well, the sticky + * bit already has a meaning for directories, + * so we don't want to overload that. */ + + if (keep_this_level) { + log_debug("Keeping \"%s\".", sub_path); + continue; + } + + /* Ignore ctime, we change it when deleting */ + age = timespec_load(&s.st_mtim); + if (age >= cutoff) { + char a[FORMAT_TIMESTAMP_MAX]; + /* Follows spelling in stat(1). */ + log_debug("Directory \"%s\": modify time %s is too new.", + sub_path, + format_timestamp_us(a, sizeof(a), age)); + continue; + } + + age = timespec_load(&s.st_atim); + if (age >= cutoff) { + char a[FORMAT_TIMESTAMP_MAX]; + log_debug("Directory \"%s\": access time %s is too new.", + sub_path, + format_timestamp_us(a, sizeof(a), age)); + continue; + } + + log_debug("Removing directory \"%s\".", sub_path); + if (unlinkat(dirfd(d), dent->d_name, AT_REMOVEDIR) < 0) + if (errno != ENOENT && errno != ENOTEMPTY) { + log_error_errno(errno, "rmdir(%s): %m", sub_path); + r = -errno; + } + + } else { + /* Skip files for which the sticky bit is + * set. These are semantics we define, and are + * unknown elsewhere. See XDG_RUNTIME_DIR + * specification for details. */ + if (s.st_mode & S_ISVTX) { + log_debug("Skipping \"%s\": sticky bit set.", sub_path); + continue; + } + + if (mountpoint && S_ISREG(s.st_mode)) + if (s.st_uid == 0 && STR_IN_SET(dent->d_name, + ".journal", + "aquota.user", + "aquota.group")) { + log_debug("Skipping \"%s\".", sub_path); + continue; + } + + /* Ignore sockets that are listed in /proc/net/unix */ + if (S_ISSOCK(s.st_mode) && unix_socket_alive(sub_path)) { + log_debug("Skipping \"%s\": live socket.", sub_path); + continue; + } + + /* Ignore device nodes */ + if (S_ISCHR(s.st_mode) || S_ISBLK(s.st_mode)) { + log_debug("Skipping \"%s\": a device.", sub_path); + continue; + } + + /* Keep files on this level around if this is + * requested */ + if (keep_this_level) { + log_debug("Keeping \"%s\".", sub_path); + continue; + } + + age = timespec_load(&s.st_mtim); + if (age >= cutoff) { + char a[FORMAT_TIMESTAMP_MAX]; + /* Follows spelling in stat(1). */ + log_debug("File \"%s\": modify time %s is too new.", + sub_path, + format_timestamp_us(a, sizeof(a), age)); + continue; + } + + age = timespec_load(&s.st_atim); + if (age >= cutoff) { + char a[FORMAT_TIMESTAMP_MAX]; + log_debug("File \"%s\": access time %s is too new.", + sub_path, + format_timestamp_us(a, sizeof(a), age)); + continue; + } + + age = timespec_load(&s.st_ctim); + if (age >= cutoff) { + char a[FORMAT_TIMESTAMP_MAX]; + log_debug("File \"%s\": change time %s is too new.", + sub_path, + format_timestamp_us(a, sizeof(a), age)); + continue; + } + + log_debug("unlink \"%s\"", sub_path); + + if (unlinkat(dirfd(d), dent->d_name, 0) < 0) + if (errno != ENOENT) + r = log_error_errno(errno, "unlink(%s): %m", sub_path); + + deleted = true; + } + } + +finish: + if (deleted) { + usec_t age1, age2; + char a[FORMAT_TIMESTAMP_MAX], b[FORMAT_TIMESTAMP_MAX]; + + /* Restore original directory timestamps */ + times[0] = ds->st_atim; + times[1] = ds->st_mtim; + + age1 = timespec_load(&ds->st_atim); + age2 = timespec_load(&ds->st_mtim); + log_debug("Restoring access and modification time on \"%s\": %s, %s", + p, + format_timestamp_us(a, sizeof(a), age1), + format_timestamp_us(b, sizeof(b), age2)); + if (futimens(dirfd(d), times) < 0) + log_error_errno(errno, "utimensat(%s): %m", p); + } + + return r; +} + +static int path_set_perms(Item *i, const char *path) { + _cleanup_close_ int fd = -1; + struct stat st; + + assert(i); + assert(path); + + /* We open the file with O_PATH here, to make the operation + * somewhat atomic. Also there's unfortunately no fchmodat() + * with AT_SYMLINK_NOFOLLOW, hence we emulate it here via + * O_PATH. */ + + fd = open(path, O_NOFOLLOW|O_CLOEXEC|O_PATH); + if (fd < 0) + return log_error_errno(errno, "Adjusting owner and mode for %s failed: %m", path); + + if (fstatat(fd, "", &st, AT_EMPTY_PATH) < 0) + return log_error_errno(errno, "Failed to fstat() file %s: %m", path); + + if (S_ISLNK(st.st_mode)) + log_debug("Skipping mode an owner fix for symlink %s.", path); + else { + char fn[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; + xsprintf(fn, "/proc/self/fd/%i", fd); + + /* not using i->path directly because it may be a glob */ + if (i->mode_set) { + mode_t m = i->mode; + + if (i->mask_perms) { + if (!(st.st_mode & 0111)) + m &= ~0111; + if (!(st.st_mode & 0222)) + m &= ~0222; + if (!(st.st_mode & 0444)) + m &= ~0444; + if (!S_ISDIR(st.st_mode)) + m &= ~07000; /* remove sticky/sgid/suid bit, unless directory */ + } + + if (m == (st.st_mode & 07777)) + log_debug("\"%s\" has right mode %o", path, st.st_mode); + else { + log_debug("chmod \"%s\" to mode %o", path, m); + if (chmod(fn, m) < 0) + return log_error_errno(errno, "chmod(%s) failed: %m", path); + } + } + + if ((i->uid != st.st_uid || i->gid != st.st_gid) && + (i->uid_set || i->gid_set)) { + log_debug("chown \"%s\" to "UID_FMT"."GID_FMT, + path, + i->uid_set ? i->uid : UID_INVALID, + i->gid_set ? i->gid : GID_INVALID); + if (chown(fn, + i->uid_set ? i->uid : UID_INVALID, + i->gid_set ? i->gid : GID_INVALID) < 0) + return log_error_errno(errno, "chown(%s) failed: %m", path); + } + } + + fd = safe_close(fd); + + return label_fix(path, false, false); +} + +static int parse_xattrs_from_arg(Item *i) { + const char *p; + int r; + + assert(i); + assert(i->argument); + + p = i->argument; + + for (;;) { + _cleanup_free_ char *name = NULL, *value = NULL, *xattr = NULL, *xattr_replaced = NULL; + + r = extract_first_word(&p, &xattr, NULL, EXTRACT_QUOTES|EXTRACT_CUNESCAPE); + if (r < 0) + log_warning_errno(r, "Failed to parse extended attribute '%s', ignoring: %m", p); + if (r <= 0) + break; + + r = specifier_printf(xattr, specifier_table, NULL, &xattr_replaced); + if (r < 0) + return log_error_errno(r, "Failed to replace specifiers in extended attribute '%s': %m", xattr); + + r = split_pair(xattr_replaced, "=", &name, &value); + if (r < 0) { + log_warning_errno(r, "Failed to parse extended attribute, ignoring: %s", xattr); + continue; + } + + if (isempty(name) || isempty(value)) { + log_warning("Malformed extended attribute found, ignoring: %s", xattr); + continue; + } + + if (strv_push_pair(&i->xattrs, name, value) < 0) + return log_oom(); + + name = value = NULL; + } + + return 0; +} + +static int path_set_xattrs(Item *i, const char *path) { + char **name, **value; + + assert(i); + assert(path); + + STRV_FOREACH_PAIR(name, value, i->xattrs) { + int n; + + n = strlen(*value); + log_debug("Setting extended attribute '%s=%s' on %s.", *name, *value, path); + if (lsetxattr(path, *name, *value, n, 0) < 0) { + log_error("Setting extended attribute %s=%s on %s failed: %m", *name, *value, path); + return -errno; + } + } + return 0; +} + +static int parse_acls_from_arg(Item *item) { +#ifdef HAVE_ACL + int r; + + assert(item); + + /* If force (= modify) is set, we will not modify the acl + * afterwards, so the mask can be added now if necessary. */ + + r = parse_acl(item->argument, &item->acl_access, &item->acl_default, !item->force); + if (r < 0) + log_warning_errno(r, "Failed to parse ACL \"%s\": %m. Ignoring", item->argument); +#else + log_warning_errno(ENOSYS, "ACLs are not supported. Ignoring"); +#endif + + return 0; +} + +#ifdef HAVE_ACL +static int path_set_acl(const char *path, const char *pretty, acl_type_t type, acl_t acl, bool modify) { + _cleanup_(acl_free_charpp) char *t = NULL; + _cleanup_(acl_freep) acl_t dup = NULL; + int r; + + /* Returns 0 for success, positive error if already warned, + * negative error otherwise. */ + + if (modify) { + r = acls_for_file(path, type, acl, &dup); + if (r < 0) + return r; + + r = calc_acl_mask_if_needed(&dup); + if (r < 0) + return r; + } else { + dup = acl_dup(acl); + if (!dup) + return -errno; + + /* the mask was already added earlier if needed */ + } + + r = add_base_acls_if_needed(&dup, path); + if (r < 0) + return r; + + t = acl_to_any_text(dup, NULL, ',', TEXT_ABBREVIATE); + log_debug("Setting %s ACL %s on %s.", + type == ACL_TYPE_ACCESS ? "access" : "default", + strna(t), pretty); + + r = acl_set_file(path, type, dup); + if (r < 0) + /* Return positive to indicate we already warned */ + return -log_error_errno(errno, + "Setting %s ACL \"%s\" on %s failed: %m", + type == ACL_TYPE_ACCESS ? "access" : "default", + strna(t), pretty); + + return 0; +} +#endif + +static int path_set_acls(Item *item, const char *path) { + int r = 0; +#ifdef HAVE_ACL + char fn[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; + _cleanup_close_ int fd = -1; + struct stat st; + + assert(item); + assert(path); + + fd = open(path, O_NOFOLLOW|O_CLOEXEC|O_PATH); + if (fd < 0) + return log_error_errno(errno, "Adjusting ACL of %s failed: %m", path); + + if (fstatat(fd, "", &st, AT_EMPTY_PATH) < 0) + return log_error_errno(errno, "Failed to fstat() file %s: %m", path); + + if (S_ISLNK(st.st_mode)) { + log_debug("Skipping ACL fix for symlink %s.", path); + return 0; + } + + xsprintf(fn, "/proc/self/fd/%i", fd); + + if (item->acl_access) + r = path_set_acl(fn, path, ACL_TYPE_ACCESS, item->acl_access, item->force); + + if (r == 0 && item->acl_default) + r = path_set_acl(fn, path, ACL_TYPE_DEFAULT, item->acl_default, item->force); + + if (r > 0) + return -r; /* already warned */ + else if (r == -EOPNOTSUPP) { + log_debug_errno(r, "ACLs not supported by file system at %s", path); + return 0; + } else if (r < 0) + log_error_errno(r, "ACL operation on \"%s\" failed: %m", path); +#endif + return r; +} + +#define ATTRIBUTES_ALL \ + (FS_NOATIME_FL | \ + FS_SYNC_FL | \ + FS_DIRSYNC_FL | \ + FS_APPEND_FL | \ + FS_COMPR_FL | \ + FS_NODUMP_FL | \ + FS_EXTENT_FL | \ + FS_IMMUTABLE_FL | \ + FS_JOURNAL_DATA_FL | \ + FS_SECRM_FL | \ + FS_UNRM_FL | \ + FS_NOTAIL_FL | \ + FS_TOPDIR_FL | \ + FS_NOCOW_FL) + +static int parse_attribute_from_arg(Item *item) { + + static const struct { + char character; + unsigned value; + } attributes[] = { + { 'A', FS_NOATIME_FL }, /* do not update atime */ + { 'S', FS_SYNC_FL }, /* Synchronous updates */ + { 'D', FS_DIRSYNC_FL }, /* dirsync behaviour (directories only) */ + { 'a', FS_APPEND_FL }, /* writes to file may only append */ + { 'c', FS_COMPR_FL }, /* Compress file */ + { 'd', FS_NODUMP_FL }, /* do not dump file */ + { 'e', FS_EXTENT_FL }, /* Top of directory hierarchies*/ + { 'i', FS_IMMUTABLE_FL }, /* Immutable file */ + { 'j', FS_JOURNAL_DATA_FL }, /* Reserved for ext3 */ + { 's', FS_SECRM_FL }, /* Secure deletion */ + { 'u', FS_UNRM_FL }, /* Undelete */ + { 't', FS_NOTAIL_FL }, /* file tail should not be merged */ + { 'T', FS_TOPDIR_FL }, /* Top of directory hierarchies*/ + { 'C', FS_NOCOW_FL }, /* Do not cow file */ + }; + + enum { + MODE_ADD, + MODE_DEL, + MODE_SET + } mode = MODE_ADD; + + unsigned value = 0, mask = 0; + const char *p; + + assert(item); + + p = item->argument; + if (p) { + if (*p == '+') { + mode = MODE_ADD; + p++; + } else if (*p == '-') { + mode = MODE_DEL; + p++; + } else if (*p == '=') { + mode = MODE_SET; + p++; + } + } + + if (isempty(p) && mode != MODE_SET) { + log_error("Setting file attribute on '%s' needs an attribute specification.", item->path); + return -EINVAL; + } + + for (; p && *p ; p++) { + unsigned i, v; + + for (i = 0; i < ELEMENTSOF(attributes); i++) + if (*p == attributes[i].character) + break; + + if (i >= ELEMENTSOF(attributes)) { + log_error("Unknown file attribute '%c' on '%s'.", *p, item->path); + return -EINVAL; + } + + v = attributes[i].value; + + SET_FLAG(value, v, (mode == MODE_ADD || mode == MODE_SET)); + + mask |= v; + } + + if (mode == MODE_SET) + mask |= ATTRIBUTES_ALL; + + assert(mask != 0); + + item->attribute_mask = mask; + item->attribute_value = value; + item->attribute_set = true; + + return 0; +} + +static int path_set_attribute(Item *item, const char *path) { + _cleanup_close_ int fd = -1; + struct stat st; + unsigned f; + int r; + + if (!item->attribute_set || item->attribute_mask == 0) + return 0; + + fd = open(path, O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_NOATIME|O_NOFOLLOW); + if (fd < 0) { + if (errno == ELOOP) + return log_error_errno(errno, "Skipping file attributes adjustment on symlink %s.", path); + + return log_error_errno(errno, "Cannot open '%s': %m", path); + } + + if (fstat(fd, &st) < 0) + return log_error_errno(errno, "Cannot stat '%s': %m", path); + + /* Issuing the file attribute ioctls on device nodes is not + * safe, as that will be delivered to the drivers, not the + * file system containing the device node. */ + if (!S_ISREG(st.st_mode) && !S_ISDIR(st.st_mode)) { + log_error("Setting file flags is only supported on regular files and directories, cannot set on '%s'.", path); + return -EINVAL; + } + + f = item->attribute_value & item->attribute_mask; + + /* Mask away directory-specific flags */ + if (!S_ISDIR(st.st_mode)) + f &= ~FS_DIRSYNC_FL; + + r = chattr_fd(fd, f, item->attribute_mask); + if (r < 0) + log_full_errno(r == -ENOTTY ? LOG_DEBUG : LOG_WARNING, + r, + "Cannot set file attribute for '%s', value=0x%08x, mask=0x%08x: %m", + path, item->attribute_value, item->attribute_mask); + + return 0; +} + +static int write_one_file(Item *i, const char *path) { + _cleanup_close_ int fd = -1; + int flags, r = 0; + struct stat st; + + assert(i); + assert(path); + + flags = i->type == CREATE_FILE ? O_CREAT|O_APPEND|O_NOFOLLOW : + i->type == TRUNCATE_FILE ? O_CREAT|O_TRUNC|O_NOFOLLOW : 0; + + RUN_WITH_UMASK(0000) { + mac_selinux_create_file_prepare(path, S_IFREG); + fd = open(path, flags|O_NDELAY|O_CLOEXEC|O_WRONLY|O_NOCTTY, i->mode); + mac_selinux_create_file_clear(); + } + + if (fd < 0) { + if (i->type == WRITE_FILE && errno == ENOENT) { + log_debug_errno(errno, "Not writing \"%s\": %m", path); + return 0; + } + + r = -errno; + if (!i->argument && errno == EROFS && stat(path, &st) == 0 && + (i->type == CREATE_FILE || st.st_size == 0)) + goto check_mode; + + return log_error_errno(r, "Failed to create file %s: %m", path); + } + + if (i->argument) { + _cleanup_free_ char *unescaped = NULL, *replaced = NULL; + + log_debug("%s to \"%s\".", i->type == CREATE_FILE ? "Appending" : "Writing", path); + + r = cunescape(i->argument, 0, &unescaped); + if (r < 0) + return log_error_errno(r, "Failed to unescape parameter to write: %s", i->argument); + + r = specifier_printf(unescaped, specifier_table, NULL, &replaced); + if (r < 0) + return log_error_errno(r, "Failed to replace specifiers in parameter to write '%s': %m", unescaped); + + r = loop_write(fd, replaced, strlen(replaced), false); + if (r < 0) + return log_error_errno(r, "Failed to write file \"%s\": %m", path); + } else + log_debug("\"%s\" has been created.", path); + + fd = safe_close(fd); + + if (stat(path, &st) < 0) + return log_error_errno(errno, "stat(%s) failed: %m", path); + + check_mode: + if (!S_ISREG(st.st_mode)) { + log_error("%s is not a file.", path); + return -EEXIST; + } + + r = path_set_perms(i, path); + if (r < 0) + return r; + + return 0; +} + +typedef int (*action_t)(Item *, const char *); + +static int item_do_children(Item *i, const char *path, action_t action) { + _cleanup_closedir_ DIR *d; + int r = 0; + + assert(i); + assert(path); + + /* This returns the first error we run into, but nevertheless + * tries to go on */ + + d = opendir_nomod(path); + if (!d) + return errno == ENOENT || errno == ENOTDIR ? 0 : -errno; + + for (;;) { + _cleanup_free_ char *p = NULL; + struct dirent *de; + int q; + + errno = 0; + de = readdir(d); + if (!de) { + if (errno > 0 && r == 0) + r = -errno; + + break; + } + + if (STR_IN_SET(de->d_name, ".", "..")) + continue; + + p = strjoin(path, "/", de->d_name, NULL); + if (!p) + return -ENOMEM; + + q = action(i, p); + if (q < 0 && q != -ENOENT && r == 0) + r = q; + + if (IN_SET(de->d_type, DT_UNKNOWN, DT_DIR)) { + q = item_do_children(i, p, action); + if (q < 0 && r == 0) + r = q; + } + } + + return r; +} + +static int glob_item(Item *i, action_t action, bool recursive) { + _cleanup_globfree_ glob_t g = { + .gl_closedir = (void (*)(void *)) closedir, + .gl_readdir = (struct dirent *(*)(void *)) readdir, + .gl_opendir = (void *(*)(const char *)) opendir_nomod, + .gl_lstat = lstat, + .gl_stat = stat, + }; + int r = 0, k; + char **fn; + + errno = 0; + k = glob(i->path, GLOB_NOSORT|GLOB_BRACE|GLOB_ALTDIRFUNC, NULL, &g); + if (k != 0 && k != GLOB_NOMATCH) + return log_error_errno(errno ?: EIO, "glob(%s) failed: %m", i->path); + + STRV_FOREACH(fn, g.gl_pathv) { + k = action(i, *fn); + if (k < 0 && r == 0) + r = k; + + if (recursive) { + k = item_do_children(i, *fn, action); + if (k < 0 && r == 0) + r = k; + } + } + + return r; +} + +typedef enum { + CREATION_NORMAL, + CREATION_EXISTING, + CREATION_FORCE, + _CREATION_MODE_MAX, + _CREATION_MODE_INVALID = -1 +} CreationMode; + +static const char *creation_mode_verb_table[_CREATION_MODE_MAX] = { + [CREATION_NORMAL] = "Created", + [CREATION_EXISTING] = "Found existing", + [CREATION_FORCE] = "Created replacement", +}; + +DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(creation_mode_verb, CreationMode); + +static int create_item(Item *i) { + _cleanup_free_ char *resolved = NULL; + struct stat st; + int r = 0; + int q = 0; + CreationMode creation; + + assert(i); + + log_debug("Running create action for entry %c %s", (char) i->type, i->path); + + switch (i->type) { + + case IGNORE_PATH: + case IGNORE_DIRECTORY_PATH: + case REMOVE_PATH: + case RECURSIVE_REMOVE_PATH: + return 0; + + case CREATE_FILE: + case TRUNCATE_FILE: + r = write_one_file(i, i->path); + if (r < 0) + return r; + break; + + case COPY_FILES: { + r = specifier_printf(i->argument, specifier_table, NULL, &resolved); + if (r < 0) + return log_error_errno(r, "Failed to substitute specifiers in copy source %s: %m", i->argument); + + log_debug("Copying tree \"%s\" to \"%s\".", resolved, i->path); + r = copy_tree(resolved, i->path, false); + + if (r == -EROFS && stat(i->path, &st) == 0) + r = -EEXIST; + + if (r < 0) { + struct stat a, b; + + if (r != -EEXIST) + return log_error_errno(r, "Failed to copy files to %s: %m", i->path); + + if (stat(resolved, &a) < 0) + return log_error_errno(errno, "stat(%s) failed: %m", resolved); + + if (stat(i->path, &b) < 0) + return log_error_errno(errno, "stat(%s) failed: %m", i->path); + + if ((a.st_mode ^ b.st_mode) & S_IFMT) { + log_debug("Can't copy to %s, file exists already and is of different type", i->path); + return 0; + } + } + + r = path_set_perms(i, i->path); + if (r < 0) + return r; + + break; + + case WRITE_FILE: + r = glob_item(i, write_one_file, false); + if (r < 0) + return r; + + break; + + case CREATE_DIRECTORY: + case TRUNCATE_DIRECTORY: + case CREATE_SUBVOLUME: + case CREATE_SUBVOLUME_INHERIT_QUOTA: + case CREATE_SUBVOLUME_NEW_QUOTA: + RUN_WITH_UMASK(0000) + mkdir_parents_label(i->path, 0755); + + if (IN_SET(i->type, CREATE_SUBVOLUME, CREATE_SUBVOLUME_INHERIT_QUOTA, CREATE_SUBVOLUME_NEW_QUOTA)) { + + if (btrfs_is_subvol(isempty(arg_root) ? "/" : arg_root) <= 0) + + /* Don't create a subvolume unless the + * root directory is one, too. We do + * this under the assumption that if + * the root directory is just a plain + * directory (i.e. very light-weight), + * we shouldn't try to split it up + * into subvolumes (i.e. more + * heavy-weight). Thus, chroot() + * environments and suchlike will get + * a full brtfs subvolume set up below + * their tree only if they + * specifically set up a btrfs + * subvolume for the root dir too. */ + + r = -ENOTTY; + else { + RUN_WITH_UMASK((~i->mode) & 0777) + r = btrfs_subvol_make(i->path); + } + } else + r = 0; + + if (IN_SET(i->type, CREATE_DIRECTORY, TRUNCATE_DIRECTORY) || r == -ENOTTY) + RUN_WITH_UMASK(0000) + r = mkdir_label(i->path, i->mode); + + if (r < 0) { + int k; + + if (r != -EEXIST && r != -EROFS) + return log_error_errno(r, "Failed to create directory or subvolume \"%s\": %m", i->path); + + k = is_dir(i->path, false); + if (k == -ENOENT && r == -EROFS) + return log_error_errno(r, "%s does not exist and cannot be created as the file system is read-only.", i->path); + if (k < 0) + return log_error_errno(k, "Failed to check if %s exists: %m", i->path); + if (!k) { + log_warning("\"%s\" already exists and is not a directory.", i->path); + return 0; + } + + creation = CREATION_EXISTING; + } else + creation = CREATION_NORMAL; + + log_debug("%s directory \"%s\".", creation_mode_verb_to_string(creation), i->path); + + if (IN_SET(i->type, CREATE_SUBVOLUME_NEW_QUOTA, CREATE_SUBVOLUME_INHERIT_QUOTA)) { + r = btrfs_subvol_auto_qgroup(i->path, 0, i->type == CREATE_SUBVOLUME_NEW_QUOTA); + if (r == -ENOTTY) + log_debug_errno(r, "Couldn't adjust quota for subvolume \"%s\" (unsupported fs or dir not a subvolume): %m", i->path); + else if (r == -EROFS) + log_debug_errno(r, "Couldn't adjust quota for subvolume \"%s\" (fs is read-only).", i->path); + else if (r == -ENOPROTOOPT) + log_debug_errno(r, "Couldn't adjust quota for subvolume \"%s\" (quota support is disabled).", i->path); + else if (r < 0) + q = log_error_errno(r, "Failed to adjust quota for subvolume \"%s\": %m", i->path); + else if (r > 0) + log_debug("Adjusted quota for subvolume \"%s\".", i->path); + else if (r == 0) + log_debug("Quota for subvolume \"%s\" already in place, no change made.", i->path); + } + + /* fall through */ + + case EMPTY_DIRECTORY: + r = path_set_perms(i, i->path); + if (q < 0) + return q; + if (r < 0) + return r; + + break; + + case CREATE_FIFO: + RUN_WITH_UMASK(0000) { + mac_selinux_create_file_prepare(i->path, S_IFIFO); + r = mkfifo(i->path, i->mode); + mac_selinux_create_file_clear(); + } + + if (r < 0) { + if (errno != EEXIST) + return log_error_errno(errno, "Failed to create fifo %s: %m", i->path); + + if (lstat(i->path, &st) < 0) + return log_error_errno(errno, "stat(%s) failed: %m", i->path); + + if (!S_ISFIFO(st.st_mode)) { + + if (i->force) { + RUN_WITH_UMASK(0000) { + mac_selinux_create_file_prepare(i->path, S_IFIFO); + r = mkfifo_atomic(i->path, i->mode); + mac_selinux_create_file_clear(); + } + + if (r < 0) + return log_error_errno(r, "Failed to create fifo %s: %m", i->path); + creation = CREATION_FORCE; + } else { + log_warning("\"%s\" already exists and is not a fifo.", i->path); + return 0; + } + } else + creation = CREATION_EXISTING; + } else + creation = CREATION_NORMAL; + log_debug("%s fifo \"%s\".", creation_mode_verb_to_string(creation), i->path); + + r = path_set_perms(i, i->path); + if (r < 0) + return r; + + break; + } + + case CREATE_SYMLINK: { + r = specifier_printf(i->argument, specifier_table, NULL, &resolved); + if (r < 0) + return log_error_errno(r, "Failed to substitute specifiers in symlink target %s: %m", i->argument); + + mac_selinux_create_file_prepare(i->path, S_IFLNK); + r = symlink(resolved, i->path); + mac_selinux_create_file_clear(); + + if (r < 0) { + _cleanup_free_ char *x = NULL; + + if (errno != EEXIST) + return log_error_errno(errno, "symlink(%s, %s) failed: %m", resolved, i->path); + + r = readlink_malloc(i->path, &x); + if (r < 0 || !streq(resolved, x)) { + + if (i->force) { + mac_selinux_create_file_prepare(i->path, S_IFLNK); + r = symlink_atomic(resolved, i->path); + mac_selinux_create_file_clear(); + + if (r < 0) + return log_error_errno(r, "symlink(%s, %s) failed: %m", resolved, i->path); + + creation = CREATION_FORCE; + } else { + log_debug("\"%s\" is not a symlink or does not point to the correct path.", i->path); + return 0; + } + } else + creation = CREATION_EXISTING; + } else + + creation = CREATION_NORMAL; + log_debug("%s symlink \"%s\".", creation_mode_verb_to_string(creation), i->path); + break; + } + + case CREATE_BLOCK_DEVICE: + case CREATE_CHAR_DEVICE: { + mode_t file_type; + + if (have_effective_cap(CAP_MKNOD) == 0) { + /* In a container we lack CAP_MKNOD. We + shouldn't attempt to create the device node in + that case to avoid noise, and we don't support + virtualized devices in containers anyway. */ + + log_debug("We lack CAP_MKNOD, skipping creation of device node %s.", i->path); + return 0; + } + + file_type = i->type == CREATE_BLOCK_DEVICE ? S_IFBLK : S_IFCHR; + + RUN_WITH_UMASK(0000) { + mac_selinux_create_file_prepare(i->path, file_type); + r = mknod(i->path, i->mode | file_type, i->major_minor); + mac_selinux_create_file_clear(); + } + + if (r < 0) { + if (errno == EPERM) { + log_debug("We lack permissions, possibly because of cgroup configuration; " + "skipping creation of device node %s.", i->path); + return 0; + } + + if (errno != EEXIST) + return log_error_errno(errno, "Failed to create device node %s: %m", i->path); + + if (lstat(i->path, &st) < 0) + return log_error_errno(errno, "stat(%s) failed: %m", i->path); + + if ((st.st_mode & S_IFMT) != file_type) { + + if (i->force) { + + RUN_WITH_UMASK(0000) { + mac_selinux_create_file_prepare(i->path, file_type); + r = mknod_atomic(i->path, i->mode | file_type, i->major_minor); + mac_selinux_create_file_clear(); + } + + if (r < 0) + return log_error_errno(r, "Failed to create device node \"%s\": %m", i->path); + creation = CREATION_FORCE; + } else { + log_debug("%s is not a device node.", i->path); + return 0; + } + } else + creation = CREATION_EXISTING; + } else + creation = CREATION_NORMAL; + + log_debug("%s %s device node \"%s\" %u:%u.", + creation_mode_verb_to_string(creation), + i->type == CREATE_BLOCK_DEVICE ? "block" : "char", + i->path, major(i->mode), minor(i->mode)); + + r = path_set_perms(i, i->path); + if (r < 0) + return r; + + break; + } + + case ADJUST_MODE: + case RELABEL_PATH: + r = glob_item(i, path_set_perms, false); + if (r < 0) + return r; + break; + + case RECURSIVE_RELABEL_PATH: + r = glob_item(i, path_set_perms, true); + if (r < 0) + return r; + break; + + case SET_XATTR: + r = glob_item(i, path_set_xattrs, false); + if (r < 0) + return r; + break; + + case RECURSIVE_SET_XATTR: + r = glob_item(i, path_set_xattrs, true); + if (r < 0) + return r; + break; + + case SET_ACL: + r = glob_item(i, path_set_acls, false); + if (r < 0) + return r; + break; + + case RECURSIVE_SET_ACL: + r = glob_item(i, path_set_acls, true); + if (r < 0) + return r; + break; + + case SET_ATTRIBUTE: + r = glob_item(i, path_set_attribute, false); + if (r < 0) + return r; + break; + + case RECURSIVE_SET_ATTRIBUTE: + r = glob_item(i, path_set_attribute, true); + if (r < 0) + return r; + break; + } + + return 0; +} + +static int remove_item_instance(Item *i, const char *instance) { + int r; + + assert(i); + + switch (i->type) { + + case REMOVE_PATH: + if (remove(instance) < 0 && errno != ENOENT) + return log_error_errno(errno, "rm(%s): %m", instance); + + break; + + case TRUNCATE_DIRECTORY: + case RECURSIVE_REMOVE_PATH: + /* FIXME: we probably should use dir_cleanup() here + * instead of rm_rf() so that 'x' is honoured. */ + log_debug("rm -rf \"%s\"", instance); + r = rm_rf(instance, (i->type == RECURSIVE_REMOVE_PATH ? REMOVE_ROOT|REMOVE_SUBVOLUME : 0) | REMOVE_PHYSICAL); + if (r < 0 && r != -ENOENT) + return log_error_errno(r, "rm_rf(%s): %m", instance); + + break; + + default: + assert_not_reached("wut?"); + } + + return 0; +} + +static int remove_item(Item *i) { + assert(i); + + log_debug("Running remove action for entry %c %s", (char) i->type, i->path); + + switch (i->type) { + + case REMOVE_PATH: + case TRUNCATE_DIRECTORY: + case RECURSIVE_REMOVE_PATH: + return glob_item(i, remove_item_instance, false); + + default: + return 0; + } +} + +static int clean_item_instance(Item *i, const char* instance) { + _cleanup_closedir_ DIR *d = NULL; + struct stat s, ps; + bool mountpoint; + usec_t cutoff, n; + char timestamp[FORMAT_TIMESTAMP_MAX]; + + assert(i); + + if (!i->age_set) + return 0; + + n = now(CLOCK_REALTIME); + if (n < i->age) + return 0; + + cutoff = n - i->age; + + d = opendir_nomod(instance); + if (!d) { + if (errno == ENOENT || errno == ENOTDIR) { + log_debug_errno(errno, "Directory \"%s\": %m", instance); + return 0; + } + + log_error_errno(errno, "Failed to open directory %s: %m", instance); + return -errno; + } + + if (fstat(dirfd(d), &s) < 0) + return log_error_errno(errno, "stat(%s) failed: %m", i->path); + + if (!S_ISDIR(s.st_mode)) { + log_error("%s is not a directory.", i->path); + return -ENOTDIR; + } + + if (fstatat(dirfd(d), "..", &ps, AT_SYMLINK_NOFOLLOW) != 0) + return log_error_errno(errno, "stat(%s/..) failed: %m", i->path); + + mountpoint = s.st_dev != ps.st_dev || s.st_ino == ps.st_ino; + + log_debug("Cleanup threshold for %s \"%s\" is %s", + mountpoint ? "mount point" : "directory", + instance, + format_timestamp_us(timestamp, sizeof(timestamp), cutoff)); + + return dir_cleanup(i, instance, d, &s, cutoff, s.st_dev, mountpoint, + MAX_DEPTH, i->keep_first_level); +} + +static int clean_item(Item *i) { + assert(i); + + log_debug("Running clean action for entry %c %s", (char) i->type, i->path); + + switch (i->type) { + case CREATE_DIRECTORY: + case CREATE_SUBVOLUME: + case CREATE_SUBVOLUME_INHERIT_QUOTA: + case CREATE_SUBVOLUME_NEW_QUOTA: + case EMPTY_DIRECTORY: + case TRUNCATE_DIRECTORY: + case IGNORE_PATH: + case COPY_FILES: + clean_item_instance(i, i->path); + return 0; + case IGNORE_DIRECTORY_PATH: + return glob_item(i, clean_item_instance, false); + default: + return 0; + } +} + +static int process_item_array(ItemArray *array); + +static int process_item(Item *i) { + int r, q, p, t = 0; + _cleanup_free_ char *prefix = NULL; + + assert(i); + + if (i->done) + return 0; + + i->done = true; + + prefix = malloc(strlen(i->path) + 1); + if (!prefix) + return log_oom(); + + PATH_FOREACH_PREFIX(prefix, i->path) { + ItemArray *j; + + j = ordered_hashmap_get(items, prefix); + if (j) { + int s; + + s = process_item_array(j); + if (s < 0 && t == 0) + t = s; + } + } + + r = arg_create ? create_item(i) : 0; + q = arg_remove ? remove_item(i) : 0; + p = arg_clean ? clean_item(i) : 0; + + return t < 0 ? t : + r < 0 ? r : + q < 0 ? q : + p; +} + +static int process_item_array(ItemArray *array) { + unsigned n; + int r = 0, k; + + assert(array); + + for (n = 0; n < array->count; n++) { + k = process_item(array->items + n); + if (k < 0 && r == 0) + r = k; + } + + return r; +} + +static void item_free_contents(Item *i) { + assert(i); + free(i->path); + free(i->argument); + strv_free(i->xattrs); + +#ifdef HAVE_ACL + acl_free(i->acl_access); + acl_free(i->acl_default); +#endif +} + +static void item_array_free(ItemArray *a) { + unsigned n; + + if (!a) + return; + + for (n = 0; n < a->count; n++) + item_free_contents(a->items + n); + free(a->items); + free(a); +} + +static int item_compare(const void *a, const void *b) { + const Item *x = a, *y = b; + + /* Make sure that the ownership taking item is put first, so + * that we first create the node, and then can adjust it */ + + if (takes_ownership(x->type) && !takes_ownership(y->type)) + return -1; + if (!takes_ownership(x->type) && takes_ownership(y->type)) + return 1; + + return (int) x->type - (int) y->type; +} + +static bool item_compatible(Item *a, Item *b) { + assert(a); + assert(b); + assert(streq(a->path, b->path)); + + if (takes_ownership(a->type) && takes_ownership(b->type)) + /* check if the items are the same */ + return streq_ptr(a->argument, b->argument) && + + a->uid_set == b->uid_set && + a->uid == b->uid && + + a->gid_set == b->gid_set && + a->gid == b->gid && + + a->mode_set == b->mode_set && + a->mode == b->mode && + + a->age_set == b->age_set && + a->age == b->age && + + a->mask_perms == b->mask_perms && + + a->keep_first_level == b->keep_first_level && + + a->major_minor == b->major_minor; + + return true; +} + +static bool should_include_path(const char *path) { + char **prefix; + + STRV_FOREACH(prefix, arg_exclude_prefixes) + if (path_startswith(path, *prefix)) { + log_debug("Entry \"%s\" matches exclude prefix \"%s\", skipping.", + path, *prefix); + return false; + } + + STRV_FOREACH(prefix, arg_include_prefixes) + if (path_startswith(path, *prefix)) { + log_debug("Entry \"%s\" matches include prefix \"%s\".", path, *prefix); + return true; + } + + /* no matches, so we should include this path only if we + * have no whitelist at all */ + if (strv_length(arg_include_prefixes) == 0) + return true; + + log_debug("Entry \"%s\" does not match any include prefix, skipping.", path); + return false; +} + +static int parse_line(const char *fname, unsigned line, const char *buffer) { + + _cleanup_free_ char *action = NULL, *mode = NULL, *user = NULL, *group = NULL, *age = NULL, *path = NULL; + _cleanup_(item_free_contents) Item i = {}; + ItemArray *existing; + OrderedHashmap *h; + int r, pos; + bool force = false, boot = false; + + assert(fname); + assert(line >= 1); + assert(buffer); + + r = extract_many_words( + &buffer, + NULL, + EXTRACT_QUOTES, + &action, + &path, + &mode, + &user, + &group, + &age, + NULL); + if (r < 0) + return log_error_errno(r, "[%s:%u] Failed to parse line: %m", fname, line); + else if (r < 2) { + log_error("[%s:%u] Syntax error.", fname, line); + return -EIO; + } + + if (!isempty(buffer) && !streq(buffer, "-")) { + i.argument = strdup(buffer); + if (!i.argument) + return log_oom(); + } + + if (isempty(action)) { + log_error("[%s:%u] Command too short '%s'.", fname, line, action); + return -EINVAL; + } + + for (pos = 1; action[pos]; pos++) { + if (action[pos] == '!' && !boot) + boot = true; + else if (action[pos] == '+' && !force) + force = true; + else { + log_error("[%s:%u] Unknown modifiers in command '%s'", + fname, line, action); + return -EINVAL; + } + } + + if (boot && !arg_boot) { + log_debug("Ignoring entry %s \"%s\" because --boot is not specified.", + action, path); + return 0; + } + + i.type = action[0]; + i.force = force; + + r = specifier_printf(path, specifier_table, NULL, &i.path); + if (r < 0) { + log_error("[%s:%u] Failed to replace specifiers: %s", fname, line, path); + return r; + } + + switch (i.type) { + + case CREATE_DIRECTORY: + case CREATE_SUBVOLUME: + case CREATE_SUBVOLUME_INHERIT_QUOTA: + case CREATE_SUBVOLUME_NEW_QUOTA: + case EMPTY_DIRECTORY: + case TRUNCATE_DIRECTORY: + case CREATE_FIFO: + case IGNORE_PATH: + case IGNORE_DIRECTORY_PATH: + case REMOVE_PATH: + case RECURSIVE_REMOVE_PATH: + case ADJUST_MODE: + case RELABEL_PATH: + case RECURSIVE_RELABEL_PATH: + if (i.argument) + log_warning("[%s:%u] %c lines don't take argument fields, ignoring.", fname, line, i.type); + + break; + + case CREATE_FILE: + case TRUNCATE_FILE: + break; + + case CREATE_SYMLINK: + if (!i.argument) { + i.argument = strappend("/usr/share/factory/", i.path); + if (!i.argument) + return log_oom(); + } + break; + + case WRITE_FILE: + if (!i.argument) { + log_error("[%s:%u] Write file requires argument.", fname, line); + return -EBADMSG; + } + break; + + case COPY_FILES: + if (!i.argument) { + i.argument = strappend("/usr/share/factory/", i.path); + if (!i.argument) + return log_oom(); + } else if (!path_is_absolute(i.argument)) { + log_error("[%s:%u] Source path is not absolute.", fname, line); + return -EBADMSG; + } + + path_kill_slashes(i.argument); + 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); + return -EBADMSG; + } + + 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); + return -EBADMSG; + } + + i.major_minor = makedev(major, minor); + break; + } + + case SET_XATTR: + case RECURSIVE_SET_XATTR: + if (!i.argument) { + log_error("[%s:%u] Set extended attribute requires argument.", fname, line); + return -EBADMSG; + } + r = parse_xattrs_from_arg(&i); + if (r < 0) + return r; + break; + + case SET_ACL: + case RECURSIVE_SET_ACL: + if (!i.argument) { + log_error("[%s:%u] Set ACLs requires argument.", fname, line); + return -EBADMSG; + } + r = parse_acls_from_arg(&i); + if (r < 0) + return r; + break; + + case SET_ATTRIBUTE: + case RECURSIVE_SET_ATTRIBUTE: + if (!i.argument) { + log_error("[%s:%u] Set file attribute requires argument.", fname, line); + return -EBADMSG; + } + r = parse_attribute_from_arg(&i); + if (r < 0) + return r; + break; + + default: + log_error("[%s:%u] Unknown command type '%c'.", fname, line, (char) i.type); + return -EBADMSG; + } + + if (!path_is_absolute(i.path)) { + log_error("[%s:%u] Path '%s' not absolute.", fname, line, i.path); + return -EBADMSG; + } + + path_kill_slashes(i.path); + + if (!should_include_path(i.path)) + return 0; + + if (arg_root) { + char *p; + + p = prefix_root(arg_root, i.path); + if (!p) + return log_oom(); + + free(i.path); + i.path = p; + } + + if (!isempty(user) && !streq(user, "-")) { + const char *u = user; + + r = get_user_creds(&u, &i.uid, NULL, NULL, NULL); + if (r < 0) { + log_error("[%s:%u] Unknown user '%s'.", fname, line, user); + return r; + } + + i.uid_set = true; + } + + if (!isempty(group) && !streq(group, "-")) { + const char *g = group; + + r = get_group_creds(&g, &i.gid); + if (r < 0) { + log_error("[%s:%u] Unknown group '%s'.", fname, line, group); + return r; + } + + i.gid_set = true; + } + + if (!isempty(mode) && !streq(mode, "-")) { + const char *mm = mode; + unsigned m; + + if (*mm == '~') { + i.mask_perms = true; + mm++; + } + + if (parse_mode(mm, &m) < 0) { + log_error("[%s:%u] Invalid mode '%s'.", fname, line, mode); + return -EBADMSG; + } + + i.mode = m; + i.mode_set = true; + } else + i.mode = IN_SET(i.type, CREATE_DIRECTORY, TRUNCATE_DIRECTORY, CREATE_SUBVOLUME, CREATE_SUBVOLUME_INHERIT_QUOTA, CREATE_SUBVOLUME_NEW_QUOTA) ? 0755 : 0644; + + if (!isempty(age) && !streq(age, "-")) { + const char *a = age; + + if (*a == '~') { + i.keep_first_level = true; + a++; + } + + if (parse_sec(a, &i.age) < 0) { + log_error("[%s:%u] Invalid age '%s'.", fname, line, age); + return -EBADMSG; + } + + i.age_set = true; + } + + h = needs_glob(i.type) ? globs : items; + + existing = ordered_hashmap_get(h, i.path); + if (existing) { + unsigned n; + + for (n = 0; n < existing->count; n++) { + if (!item_compatible(existing->items + n, &i)) { + log_warning("[%s:%u] Duplicate line for path \"%s\", ignoring.", + fname, line, i.path); + return 0; + } + } + } else { + existing = new0(ItemArray, 1); + r = ordered_hashmap_put(h, i.path, existing); + if (r < 0) + return log_oom(); + } + + if (!GREEDY_REALLOC(existing->items, existing->size, existing->count + 1)) + return log_oom(); + + memcpy(existing->items + existing->count++, &i, sizeof(i)); + + /* Sort item array, to enforce stable ordering of application */ + qsort_safe(existing->items, existing->count, sizeof(Item), item_compare); + + zero(i); + return 0; +} + +static void help(void) { + printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n" + "Creates, deletes and cleans up volatile and temporary files and directories.\n\n" + " -h --help Show this help\n" + " --version Show package version\n" + " --create Create marked files/directories\n" + " --clean Clean up marked directories\n" + " --remove Remove marked files/directories\n" + " --boot Execute actions only safe at boot\n" + " --prefix=PATH Only apply rules with the specified prefix\n" + " --exclude-prefix=PATH Ignore rules with the specified prefix\n" + " --root=PATH Operate on an alternate filesystem root\n", + program_invocation_short_name); +} + +static int parse_argv(int argc, char *argv[]) { + + enum { + ARG_VERSION = 0x100, + ARG_CREATE, + ARG_CLEAN, + ARG_REMOVE, + ARG_BOOT, + ARG_PREFIX, + ARG_EXCLUDE_PREFIX, + ARG_ROOT, + }; + + static const struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, ARG_VERSION }, + { "create", no_argument, NULL, ARG_CREATE }, + { "clean", no_argument, NULL, ARG_CLEAN }, + { "remove", no_argument, NULL, ARG_REMOVE }, + { "boot", no_argument, NULL, ARG_BOOT }, + { "prefix", required_argument, NULL, ARG_PREFIX }, + { "exclude-prefix", required_argument, NULL, ARG_EXCLUDE_PREFIX }, + { "root", required_argument, NULL, ARG_ROOT }, + {} + }; + + int c, r; + + assert(argc >= 0); + assert(argv); + + while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) + + switch (c) { + + case 'h': + help(); + return 0; + + case ARG_VERSION: + return version(); + + case ARG_CREATE: + arg_create = true; + break; + + case ARG_CLEAN: + arg_clean = true; + break; + + case ARG_REMOVE: + arg_remove = true; + break; + + case ARG_BOOT: + arg_boot = true; + break; + + case ARG_PREFIX: + if (strv_push(&arg_include_prefixes, optarg) < 0) + return log_oom(); + break; + + case ARG_EXCLUDE_PREFIX: + if (strv_push(&arg_exclude_prefixes, optarg) < 0) + return log_oom(); + break; + + case ARG_ROOT: + r = parse_path_argument_and_warn(optarg, true, &arg_root); + if (r < 0) + return r; + break; + + case '?': + return -EINVAL; + + default: + assert_not_reached("Unhandled option"); + } + + if (!arg_clean && !arg_create && !arg_remove) { + log_error("You need to specify at least one of --clean, --create or --remove."); + return -EINVAL; + } + + return 1; +} + +static int read_config_file(const char *fn, bool ignore_enoent) { + _cleanup_fclose_ FILE *_f = NULL; + FILE *f; + char line[LINE_MAX]; + Iterator iterator; + unsigned v = 0; + Item *i; + int r = 0; + + assert(fn); + + if (streq(fn, "-")) { + log_debug("Reading config from stdin."); + fn = ""; + f = stdin; + } else { + r = search_and_fopen_nulstr(fn, "re", arg_root, conf_file_dirs, &_f); + if (r < 0) { + if (ignore_enoent && r == -ENOENT) { + log_debug_errno(r, "Failed to open \"%s\", ignoring: %m", fn); + return 0; + } + + return log_error_errno(r, "Failed to open '%s': %m", fn); + } + log_debug("Reading config file \"%s\".", fn); + f = _f; + } + + FOREACH_LINE(line, f, break) { + char *l; + int k; + + v++; + + l = strstrip(line); + if (*l == '#' || *l == 0) + continue; + + k = parse_line(fn, v, l); + if (k < 0 && r == 0) + r = k; + } + + /* we have to determine age parameter for each entry of type X */ + ORDERED_HASHMAP_FOREACH(i, globs, iterator) { + Iterator iter; + Item *j, *candidate_item = NULL; + + if (i->type != IGNORE_DIRECTORY_PATH) + continue; + + ORDERED_HASHMAP_FOREACH(j, items, iter) { + if (!IN_SET(j->type, CREATE_DIRECTORY, TRUNCATE_DIRECTORY, CREATE_SUBVOLUME, CREATE_SUBVOLUME_INHERIT_QUOTA, CREATE_SUBVOLUME_NEW_QUOTA)) + continue; + + if (path_equal(j->path, i->path)) { + candidate_item = j; + break; + } + + if ((!candidate_item && path_startswith(i->path, j->path)) || + (candidate_item && path_startswith(j->path, candidate_item->path) && (fnmatch(i->path, j->path, FNM_PATHNAME | FNM_PERIOD) == 0))) + candidate_item = j; + } + + if (candidate_item && candidate_item->age_set) { + i->age = candidate_item->age; + i->age_set = true; + } + } + + if (ferror(f)) { + log_error_errno(errno, "Failed to read from file %s: %m", fn); + if (r == 0) + r = -EIO; + } + + return r; +} + +int main(int argc, char *argv[]) { + int r, k; + ItemArray *a; + Iterator iterator; + + r = parse_argv(argc, argv); + if (r <= 0) + goto finish; + + log_set_target(LOG_TARGET_AUTO); + log_parse_environment(); + log_open(); + + umask(0022); + + mac_selinux_init(); + + items = ordered_hashmap_new(&string_hash_ops); + globs = ordered_hashmap_new(&string_hash_ops); + + if (!items || !globs) { + r = log_oom(); + goto finish; + } + + r = 0; + + if (optind < argc) { + int j; + + for (j = optind; j < argc; j++) { + k = read_config_file(argv[j], false); + if (k < 0 && r == 0) + r = k; + } + + } else { + _cleanup_strv_free_ char **files = NULL; + char **f; + + r = conf_files_list_nulstr(&files, ".conf", arg_root, conf_file_dirs); + if (r < 0) { + log_error_errno(r, "Failed to enumerate tmpfiles.d files: %m"); + goto finish; + } + + STRV_FOREACH(f, files) { + k = read_config_file(*f, true); + if (k < 0 && r == 0) + r = k; + } + } + + /* The non-globbing ones usually create things, hence we apply + * them first */ + ORDERED_HASHMAP_FOREACH(a, items, iterator) { + k = process_item_array(a); + if (k < 0 && r == 0) + r = k; + } + + /* The globbing ones usually alter things, hence we apply them + * second. */ + ORDERED_HASHMAP_FOREACH(a, globs, iterator) { + k = process_item_array(a); + if (k < 0 && r == 0) + r = k; + } + +finish: + while ((a = ordered_hashmap_steal_first(items))) + item_array_free(a); + + while ((a = ordered_hashmap_steal_first(globs))) + item_array_free(a); + + ordered_hashmap_free(items); + ordered_hashmap_free(globs); + + free(arg_include_prefixes); + free(arg_exclude_prefixes); + free(arg_root); + + set_free_free(unix_sockets); + + mac_selinux_finish(); + + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/grp-initprogs/systemd-tmpfiles/var.tmpfiles b/src/grp-initprogs/systemd-tmpfiles/var.tmpfiles new file mode 100644 index 0000000000..ae7952e77a --- /dev/null +++ b/src/grp-initprogs/systemd-tmpfiles/var.tmpfiles @@ -0,0 +1,22 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +# See tmpfiles.d(5) for details + +q /var 0755 - - - + +L /var/run - - - - ../run + +d /var/log 0755 - - - +f /var/log/wtmp 0664 root utmp - +f /var/log/btmp 0600 root utmp - + +d /var/cache 0755 - - - + +d /var/lib 0755 - - - + +d /var/spool 0755 - - - diff --git a/src/grp-initprogs/systemd-tmpfiles/x11.tmpfiles b/src/grp-initprogs/systemd-tmpfiles/x11.tmpfiles new file mode 100644 index 0000000000..4c96a54a13 --- /dev/null +++ b/src/grp-initprogs/systemd-tmpfiles/x11.tmpfiles @@ -0,0 +1,18 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +# See tmpfiles.d(5) for details + +# Make sure these are created by default so that nobody else can +d /tmp/.X11-unix 1777 root root 10d +d /tmp/.ICE-unix 1777 root root 10d +d /tmp/.XIM-unix 1777 root root 10d +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 diff --git a/src/grp-initprogs/systemd-update-done/Makefile b/src/grp-initprogs/systemd-update-done/Makefile new file mode 100644 index 0000000000..90cb440874 --- /dev/null +++ b/src/grp-initprogs/systemd-update-done/Makefile @@ -0,0 +1,34 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +rootlibexec_PROGRAMS += systemd-update-done + +systemd_update_done_SOURCES = \ + src/update-done/update-done.c + +systemd_update_done_LDADD = \ + libshared.la + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-initprogs/systemd-update-done/systemd-update-done.service.in b/src/grp-initprogs/systemd-update-done/systemd-update-done.service.in new file mode 100644 index 0000000000..ec7d906392 --- /dev/null +++ b/src/grp-initprogs/systemd-update-done/systemd-update-done.service.in @@ -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 Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Update is Completed +Documentation=man:systemd-update-done.service(8) +DefaultDependencies=no +Conflicts=shutdown.target +After=local-fs.target +Before=sysinit.target shutdown.target +ConditionNeedsUpdate=|/etc +ConditionNeedsUpdate=|/var + +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStart=@rootlibexecdir@/systemd-update-done diff --git a/src/grp-initprogs/systemd-update-done/update-done.c b/src/grp-initprogs/systemd-update-done/update-done.c new file mode 100644 index 0000000000..39d19ec048 --- /dev/null +++ b/src/grp-initprogs/systemd-update-done/update-done.c @@ -0,0 +1,115 @@ +/*** + This file is part of systemd. + + Copyright 2014 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "basic/fd-util.h" +#include "basic/io-util.h" +#include "basic/selinux-util.h" +#include "basic/util.h" + +#define MESSAGE \ + "This file was created by systemd-update-done. Its only \n" \ + "purpose is to hold a timestamp of the time this directory\n" \ + "was updated. See systemd-update-done.service(8).\n" + +static int apply_timestamp(const char *path, struct timespec *ts) { + struct timespec twice[2] = { + *ts, + *ts + }; + struct stat st; + + assert(path); + assert(ts); + + if (stat(path, &st) >= 0) { + /* Is the timestamp file already newer than the OS? If + * so, there's nothing to do. We ignore the nanosecond + * component of the timestamp, since some file systems + * do not support any better accuracy than 1s and we + * have no way to identify the accuracy + * available. Most notably ext4 on small disks (where + * 128 byte inodes are used) does not support better + * accuracy than 1s. */ + if (st.st_mtim.tv_sec > ts->tv_sec) + return 0; + + /* It is older? Then let's update it */ + if (utimensat(AT_FDCWD, path, twice, AT_SYMLINK_NOFOLLOW) < 0) { + + if (errno == EROFS) + return log_debug("Can't update timestamp file %s, file system is read-only.", path); + + return log_error_errno(errno, "Failed to update timestamp on %s: %m", path); + } + + } else if (errno == ENOENT) { + _cleanup_close_ int fd = -1; + int r; + + /* The timestamp file doesn't exist yet? Then let's create it. */ + + r = mac_selinux_create_file_prepare(path, S_IFREG); + if (r < 0) + return log_error_errno(r, "Failed to set SELinux context for %s: %m", path); + + fd = open(path, O_CREAT|O_EXCL|O_WRONLY|O_TRUNC|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0644); + mac_selinux_create_file_clear(); + + if (fd < 0) { + if (errno == EROFS) + return log_debug("Can't create timestamp file %s, file system is read-only.", path); + + return log_error_errno(errno, "Failed to create timestamp file %s: %m", path); + } + + (void) loop_write(fd, MESSAGE, strlen(MESSAGE), false); + + if (futimens(fd, twice) < 0) + return log_error_errno(errno, "Failed to update timestamp on %s: %m", path); + } else + log_error_errno(errno, "Failed to stat() timestamp file %s: %m", path); + + return 0; +} + +int main(int argc, char *argv[]) { + struct stat st; + int r, q = 0; + + log_set_target(LOG_TARGET_AUTO); + log_parse_environment(); + log_open(); + + if (stat("/usr", &st) < 0) { + log_error_errno(errno, "Failed to stat /usr: %m"); + return EXIT_FAILURE; + } + + r = mac_selinux_init(); + if (r < 0) { + log_error_errno(r, "SELinux setup failed: %m"); + goto finish; + } + + r = apply_timestamp("/etc/.updated", &st.st_mtim); + q = apply_timestamp("/var/.updated", &st.st_mtim); + +finish: + return r < 0 || q < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/grp-initprogs/systemd-update-utmp/Makefile b/src/grp-initprogs/systemd-update-utmp/Makefile new file mode 100644 index 0000000000..0107808281 --- /dev/null +++ b/src/grp-initprogs/systemd-update-utmp/Makefile @@ -0,0 +1,42 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +ifneq ($(HAVE_UTMP),) +rootlibexec_PROGRAMS += \ + systemd-update-utmp +endif # HAVE_UTMP + +systemd_update_utmp_SOURCES = \ + src/update-utmp/update-utmp.c + +systemd_update_utmp_CFLAGS = \ + $(AM_CFLAGS) \ + $(AUDIT_CFLAGS) + +systemd_update_utmp_LDADD = \ + libshared.la \ + $(AUDIT_LIBS) + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-initprogs/systemd-update-utmp/systemd-update-utmp.service.in b/src/grp-initprogs/systemd-update-utmp/systemd-update-utmp.service.in new file mode 100644 index 0000000000..163eccd91f --- /dev/null +++ b/src/grp-initprogs/systemd-update-utmp/systemd-update-utmp.service.in @@ -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 Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Update UTMP about System Boot/Shutdown +Documentation=man:systemd-update-utmp.service(8) man:utmp(5) +DefaultDependencies=no +RequiresMountsFor=/var/log/wtmp +Conflicts=shutdown.target +After=systemd-remount-fs.service systemd-tmpfiles-setup.service auditd.service +Before=sysinit.target shutdown.target + +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStart=@rootlibexecdir@/systemd-update-utmp reboot +ExecStop=@rootlibexecdir@/systemd-update-utmp shutdown diff --git a/src/grp-initprogs/systemd-update-utmp/update-utmp.c b/src/grp-initprogs/systemd-update-utmp/update-utmp.c new file mode 100644 index 0000000000..69264c3a26 --- /dev/null +++ b/src/grp-initprogs/systemd-update-utmp/update-utmp.c @@ -0,0 +1,283 @@ +/*** + 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 Lesser General Public License as published by + the Free Software Foundation; either version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include + +#ifdef HAVE_AUDIT +#include +#endif + +#include + +#include "basic/alloc-util.h" +#include "sd-bus/bus-error.h" +#include "shared/bus-util.h" +#include "basic/formats-util.h" +#include "basic/log.h" +#include "basic/macro.h" +#include "basic/special.h" +#include "basic/unit-name.h" +#include "basic/util.h" +#include "shared/utmp-wtmp.h" + +typedef struct Context { + sd_bus *bus; +#ifdef HAVE_AUDIT + int audit_fd; +#endif +} Context; + +static usec_t get_startup_time(Context *c) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + usec_t t = 0; + int r; + + assert(c); + + r = sd_bus_get_property_trivial( + c->bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "UserspaceTimestamp", + &error, + 't', &t); + if (r < 0) { + log_error_errno(r, "Failed to get timestamp: %s", bus_error_message(&error, r)); + return 0; + } + + return t; +} + +static int get_current_runlevel(Context *c) { + static const struct { + const int runlevel; + const char *special; + } table[] = { + /* The first target of this list that is active or has + * a job scheduled wins. We prefer runlevels 5 and 3 + * here over the others, since these are the main + * runlevels used on Fedora. It might make sense to + * change the order on some distributions. */ + { '5', SPECIAL_GRAPHICAL_TARGET }, + { '3', SPECIAL_MULTI_USER_TARGET }, + { '1', SPECIAL_RESCUE_TARGET }, + }; + + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + int r; + unsigned i; + + assert(c); + + for (i = 0; i < ELEMENTSOF(table); i++) { + _cleanup_free_ char *state = NULL, *path = NULL; + + path = unit_dbus_path_from_name(table[i].special); + if (!path) + return log_oom(); + + r = sd_bus_get_property_string( + c->bus, + "org.freedesktop.systemd1", + path, + "org.freedesktop.systemd1.Unit", + "ActiveState", + &error, + &state); + if (r < 0) + return log_warning_errno(r, "Failed to get state: %s", bus_error_message(&error, r)); + + if (streq(state, "active") || streq(state, "reloading")) + return table[i].runlevel; + } + + return 0; +} + +static int on_reboot(Context *c) { + int r = 0, q; + usec_t t; + + assert(c); + + /* We finished start-up, so let's write the utmp + * record and send the audit msg */ + +#ifdef HAVE_AUDIT + if (c->audit_fd >= 0) + if (audit_log_user_comm_message(c->audit_fd, AUDIT_SYSTEM_BOOT, "", "systemd-update-utmp", NULL, NULL, NULL, 1) < 0 && + errno != EPERM) { + r = log_error_errno(errno, "Failed to send audit message: %m"); + } +#endif + + /* If this call fails it will return 0, which + * utmp_put_reboot() will then fix to the current time */ + t = get_startup_time(c); + + q = utmp_put_reboot(t); + if (q < 0) { + log_error_errno(q, "Failed to write utmp record: %m"); + r = q; + } + + return r; +} + +static int on_shutdown(Context *c) { + int r = 0, q; + + assert(c); + + /* We started shut-down, so let's write the utmp + * record and send the audit msg */ + +#ifdef HAVE_AUDIT + if (c->audit_fd >= 0) + if (audit_log_user_comm_message(c->audit_fd, AUDIT_SYSTEM_SHUTDOWN, "", "systemd-update-utmp", NULL, NULL, NULL, 1) < 0 && + errno != EPERM) { + r = log_error_errno(errno, "Failed to send audit message: %m"); + } +#endif + + q = utmp_put_shutdown(); + if (q < 0) { + log_error_errno(q, "Failed to write utmp record: %m"); + r = q; + } + + return r; +} + +static int on_runlevel(Context *c) { + int r = 0, q, previous, runlevel; + + assert(c); + + /* We finished changing runlevel, so let's write the + * utmp record and send the audit msg */ + + /* First, get last runlevel */ + q = utmp_get_runlevel(&previous, NULL); + + if (q < 0) { + if (q != -ESRCH && q != -ENOENT) + return log_error_errno(q, "Failed to get current runlevel: %m"); + + previous = 0; + } + + /* Secondly, get new runlevel */ + runlevel = get_current_runlevel(c); + + if (runlevel < 0) + return runlevel; + + if (previous == runlevel) + return 0; + +#ifdef HAVE_AUDIT + if (c->audit_fd >= 0) { + _cleanup_free_ char *s = NULL; + + if (asprintf(&s, "old-level=%c new-level=%c", + previous > 0 ? previous : 'N', + runlevel > 0 ? runlevel : 'N') < 0) + return log_oom(); + + if (audit_log_user_comm_message(c->audit_fd, AUDIT_SYSTEM_RUNLEVEL, s, "systemd-update-utmp", NULL, NULL, NULL, 1) < 0 && errno != EPERM) + r = log_error_errno(errno, "Failed to send audit message: %m"); + } +#endif + + q = utmp_put_runlevel(runlevel, previous); + if (q < 0 && q != -ESRCH && q != -ENOENT) { + log_error_errno(q, "Failed to write utmp record: %m"); + r = q; + } + + return r; +} + +int main(int argc, char *argv[]) { + Context c = { +#ifdef HAVE_AUDIT + .audit_fd = -1 +#endif + }; + int r; + + if (getppid() != 1) { + log_error("This program should be invoked by init only."); + return EXIT_FAILURE; + } + + if (argc != 2) { + log_error("This program requires one argument."); + return EXIT_FAILURE; + } + + log_set_target(LOG_TARGET_AUTO); + log_parse_environment(); + log_open(); + + umask(0022); + +#ifdef HAVE_AUDIT + /* If the kernel lacks netlink or audit support, + * don't worry about it. */ + c.audit_fd = audit_open(); + if (c.audit_fd < 0 && errno != EAFNOSUPPORT && errno != EPROTONOSUPPORT) + log_error_errno(errno, "Failed to connect to audit log: %m"); +#endif + r = bus_connect_system_systemd(&c.bus); + if (r < 0) { + log_error_errno(r, "Failed to get D-Bus connection: %m"); + r = -EIO; + goto finish; + } + + log_debug("systemd-update-utmp running as pid "PID_FMT, getpid()); + + if (streq(argv[1], "reboot")) + r = on_reboot(&c); + else if (streq(argv[1], "shutdown")) + r = on_shutdown(&c); + else if (streq(argv[1], "runlevel")) + r = on_runlevel(&c); + else { + log_error("Unknown command %s", argv[1]); + r = -EINVAL; + } + + log_debug("systemd-update-utmp stopped as pid "PID_FMT, getpid()); + +finish: +#ifdef HAVE_AUDIT + if (c.audit_fd >= 0) + audit_close(c.audit_fd); +#endif + + sd_bus_flush_close_unref(c.bus); + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/grp-initprogs/systemd-user-sessions/Makefile b/src/grp-initprogs/systemd-user-sessions/Makefile new file mode 100644 index 0000000000..66fa3b7059 --- /dev/null +++ b/src/grp-initprogs/systemd-user-sessions/Makefile @@ -0,0 +1,48 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +ifneq ($(HAVE_PAM),) + +systemd_user_sessions_SOURCES = \ + src/user-sessions/user-sessions.c + +systemd_user_sessions_LDADD = \ + libshared.la + +rootlibexec_PROGRAMS += \ + systemd-user-sessions + +nodist_systemunit_DATA += \ + units/systemd-user-sessions.service + +MULTI_USER_TARGET_WANTS += \ + systemd-user-sessions.service + +endif # HAVE_PAM + +EXTRA_DIST += \ + units/systemd-user-sessions.service.in + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-initprogs/systemd-user-sessions/systemd-user-sessions.service.in b/src/grp-initprogs/systemd-user-sessions/systemd-user-sessions.service.in new file mode 100644 index 0000000000..b4ea5a134b --- /dev/null +++ b/src/grp-initprogs/systemd-user-sessions/systemd-user-sessions.service.in @@ -0,0 +1,17 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Permit User Sessions +Documentation=man:systemd-user-sessions.service(8) +After=remote-fs.target nss-user-lookup.target network.target + +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStart=@rootlibexecdir@/systemd-user-sessions start +ExecStop=@rootlibexecdir@/systemd-user-sessions stop diff --git a/src/grp-initprogs/systemd-user-sessions/user-sessions.c b/src/grp-initprogs/systemd-user-sessions/user-sessions.c new file mode 100644 index 0000000000..639c0200e6 --- /dev/null +++ b/src/grp-initprogs/systemd-user-sessions/user-sessions.c @@ -0,0 +1,84 @@ +/*** + 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 Lesser General Public License as published by + the Free Software Foundation; either version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include + +#include "basic/fileio-label.h" +#include "basic/fileio.h" +#include "basic/log.h" +#include "basic/selinux-util.h" +#include "basic/string-util.h" +#include "basic/util.h" + +int main(int argc, char*argv[]) { + + if (argc != 2) { + log_error("This program requires one argument."); + return EXIT_FAILURE; + } + + log_set_target(LOG_TARGET_AUTO); + log_parse_environment(); + log_open(); + + umask(0022); + + mac_selinux_init(); + + if (streq(argv[1], "start")) { + int r = 0; + + if (unlink("/run/nologin") < 0 && errno != ENOENT) + r = log_error_errno(errno, + "Failed to remove /run/nologin file: %m"); + + if (unlink("/etc/nologin") < 0 && errno != ENOENT) { + /* If the file doesn't exist and /etc simply + * was read-only (in which case unlink() + * returns EROFS even if the file doesn't + * exist), don't complain */ + + if (errno != EROFS || access("/etc/nologin", F_OK) >= 0) { + log_error_errno(errno, "Failed to remove /etc/nologin file: %m"); + return EXIT_FAILURE; + } + } + + if (r < 0) + return EXIT_FAILURE; + + } else if (streq(argv[1], "stop")) { + int r; + + r = write_string_file_atomic_label("/run/nologin", "System is going down."); + if (r < 0) { + log_error_errno(r, "Failed to create /run/nologin: %m"); + return EXIT_FAILURE; + } + + } else { + log_error("Unknown verb %s.", argv[1]); + return EXIT_FAILURE; + } + + mac_selinux_finish(); + + return EXIT_SUCCESS; +} diff --git a/src/grp-initprogs/systemd-vconsole-setup/.gitignore b/src/grp-initprogs/systemd-vconsole-setup/.gitignore new file mode 100644 index 0000000000..82741b2fb3 --- /dev/null +++ b/src/grp-initprogs/systemd-vconsole-setup/.gitignore @@ -0,0 +1 @@ +/90-vconsole.rules diff --git a/src/grp-initprogs/systemd-vconsole-setup/90-vconsole.rules.in b/src/grp-initprogs/systemd-vconsole-setup/90-vconsole.rules.in new file mode 100644 index 0000000000..35b9ad5151 --- /dev/null +++ b/src/grp-initprogs/systemd-vconsole-setup/90-vconsole.rules.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 Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +# Each vtcon keeps its own state of fonts. +# +ACTION=="add", SUBSYSTEM=="vtconsole", KERNEL=="vtcon*", RUN+="@rootlibexecdir@/systemd-vconsole-setup" diff --git a/src/grp-initprogs/systemd-vconsole-setup/Makefile b/src/grp-initprogs/systemd-vconsole-setup/Makefile new file mode 100644 index 0000000000..99b369967e --- /dev/null +++ b/src/grp-initprogs/systemd-vconsole-setup/Makefile @@ -0,0 +1,50 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +ifneq ($(ENABLE_VCONSOLE),) +systemd_vconsole_setup_SOURCES = \ + src/vconsole/vconsole-setup.c + +systemd_vconsole_setup_LDADD = \ + libshared.la + +rootlibexec_PROGRAMS += \ + systemd-vconsole-setup + +nodist_udevrules_DATA += \ + src/vconsole/90-vconsole.rules + +nodist_systemunit_DATA += \ + units/systemd-vconsole-setup.service + +SYSINIT_TARGET_WANTS += \ + systemd-vconsole-setup.service +endif # ENABLE_VCONSOLE + +EXTRA_DIST += \ + src/vconsole/90-vconsole.rules.in \ + units/systemd-vconsole-setup.service.in + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-initprogs/systemd-vconsole-setup/systemd-vconsole-setup.service.in b/src/grp-initprogs/systemd-vconsole-setup/systemd-vconsole-setup.service.in new file mode 100644 index 0000000000..6160361871 --- /dev/null +++ b/src/grp-initprogs/systemd-vconsole-setup/systemd-vconsole-setup.service.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 Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Setup Virtual Console +Documentation=man:systemd-vconsole-setup.service(8) man:vconsole.conf(5) +DefaultDependencies=no +Conflicts=shutdown.target +Before=sysinit.target shutdown.target +ConditionPathExists=/dev/tty0 + +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStart=@rootlibexecdir@/systemd-vconsole-setup diff --git a/src/grp-initprogs/systemd-vconsole-setup/vconsole-setup.c b/src/grp-initprogs/systemd-vconsole-setup/vconsole-setup.c new file mode 100644 index 0000000000..87c0c2f066 --- /dev/null +++ b/src/grp-initprogs/systemd-vconsole-setup/vconsole-setup.c @@ -0,0 +1,333 @@ +/*** + This file is part of systemd. + + Copyright 2010 Kay Sievers + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/io-util.h" +#include "basic/locale-util.h" +#include "basic/log.h" +#include "basic/process-util.h" +#include "basic/signal-util.h" +#include "basic/stdio-util.h" +#include "basic/string-util.h" +#include "basic/terminal-util.h" +#include "basic/util.h" +#include "basic/virt.h" + +static bool is_vconsole(int fd) { + unsigned char data[1]; + + data[0] = TIOCL_GETFGCONSOLE; + return ioctl(fd, TIOCLINUX, data) >= 0; +} + +static int disable_utf8(int fd) { + int r = 0, k; + + if (ioctl(fd, KDSKBMODE, K_XLATE) < 0) + r = -errno; + + k = loop_write(fd, "\033%@", 3, false); + if (k < 0) + r = k; + + k = write_string_file("/sys/module/vt/parameters/default_utf8", "0", 0); + if (k < 0) + r = k; + + if (r < 0) + log_warning_errno(r, "Failed to disable UTF-8: %m"); + + return r; +} + +static int enable_utf8(int fd) { + int r = 0, k; + long current = 0; + + if (ioctl(fd, KDGKBMODE, ¤t) < 0 || current == K_XLATE) { + /* + * Change the current keyboard to unicode, unless it + * is currently in raw or off mode anyway. We + * shouldn't interfere with X11's processing of the + * key events. + * + * http://lists.freedesktop.org/archives/systemd-devel/2013-February/008573.html + * + */ + + if (ioctl(fd, KDSKBMODE, K_UNICODE) < 0) + r = -errno; + } + + k = loop_write(fd, "\033%G", 3, false); + if (k < 0) + r = k; + + k = write_string_file("/sys/module/vt/parameters/default_utf8", "1", 0); + if (k < 0) + r = k; + + if (r < 0) + log_warning_errno(r, "Failed to enable UTF-8: %m"); + + return r; +} + +static int keyboard_load_and_wait(const char *vc, const char *map, const char *map_toggle, bool utf8) { + const char *args[8]; + int i = 0, r; + pid_t pid; + + /* An empty map means kernel map */ + if (isempty(map)) + return 1; + + args[i++] = KBD_LOADKEYS; + args[i++] = "-q"; + args[i++] = "-C"; + args[i++] = vc; + if (utf8) + args[i++] = "-u"; + args[i++] = map; + if (map_toggle) + args[i++] = map_toggle; + args[i++] = NULL; + + pid = fork(); + if (pid < 0) + return log_error_errno(errno, "Failed to fork: %m"); + else if (pid == 0) { + + (void) reset_all_signal_handlers(); + (void) reset_signal_mask(); + + execv(args[0], (char **) args); + _exit(EXIT_FAILURE); + } + + r = wait_for_terminate_and_warn(KBD_LOADKEYS, pid, true); + if (r < 0) + return r; + + return r == 0; +} + +static int font_load_and_wait(const char *vc, const char *font, const char *map, const char *unimap) { + const char *args[9]; + int i = 0, r; + pid_t pid; + + /* An empty font means kernel font */ + if (isempty(font)) + return 1; + + args[i++] = KBD_SETFONT; + args[i++] = "-C"; + args[i++] = vc; + args[i++] = font; + if (map) { + args[i++] = "-m"; + args[i++] = map; + } + if (unimap) { + args[i++] = "-u"; + args[i++] = unimap; + } + args[i++] = NULL; + + pid = fork(); + if (pid < 0) + return log_error_errno(errno, "Failed to fork: %m"); + else if (pid == 0) { + + (void) reset_all_signal_handlers(); + (void) reset_signal_mask(); + + execv(args[0], (char **) args); + _exit(EXIT_FAILURE); + } + + r = wait_for_terminate_and_warn(KBD_SETFONT, pid, true); + if (r < 0) + return r; + + return r == 0; +} + +/* + * A newly allocated VT uses the font from the active VT. Here + * we update all possibly already allocated VTs with the configured + * font. It also allows to restart systemd-vconsole-setup.service, + * to apply a new font to all VTs. + */ +static void font_copy_to_all_vcs(int fd) { + struct vt_stat vcs = {}; + unsigned char map8[E_TABSZ]; + unsigned short map16[E_TABSZ]; + struct unimapdesc unimapd; + _cleanup_free_ struct unipair* unipairs = NULL; + int i, r; + + unipairs = new(struct unipair, USHRT_MAX); + if (!unipairs) { + log_oom(); + return; + } + + /* get active, and 16 bit mask of used VT numbers */ + r = ioctl(fd, VT_GETSTATE, &vcs); + if (r < 0) { + log_debug_errno(errno, "VT_GETSTATE failed, ignoring: %m"); + return; + } + + for (i = 1; i <= 15; i++) { + char vcname[strlen("/dev/vcs") + DECIMAL_STR_MAX(int)]; + _cleanup_close_ int vcfd = -1; + struct console_font_op cfo = {}; + + if (i == vcs.v_active) + continue; + + /* skip non-allocated ttys */ + xsprintf(vcname, "/dev/vcs%i", i); + if (access(vcname, F_OK) < 0) + continue; + + xsprintf(vcname, "/dev/tty%i", i); + vcfd = open_terminal(vcname, O_RDWR|O_CLOEXEC); + if (vcfd < 0) + continue; + + /* copy font from active VT, where the font was uploaded to */ + cfo.op = KD_FONT_OP_COPY; + cfo.height = vcs.v_active-1; /* tty1 == index 0 */ + (void) ioctl(vcfd, KDFONTOP, &cfo); + + /* copy map of 8bit chars */ + if (ioctl(fd, GIO_SCRNMAP, map8) >= 0) + (void) ioctl(vcfd, PIO_SCRNMAP, map8); + + /* copy map of 8bit chars -> 16bit Unicode values */ + if (ioctl(fd, GIO_UNISCRNMAP, map16) >= 0) + (void) ioctl(vcfd, PIO_UNISCRNMAP, map16); + + /* copy unicode translation table */ + /* unimapd is a ushort count and a pointer to an + array of struct unipair { ushort, ushort } */ + unimapd.entries = unipairs; + unimapd.entry_ct = USHRT_MAX; + if (ioctl(fd, GIO_UNIMAP, &unimapd) >= 0) { + struct unimapinit adv = { 0, 0, 0 }; + + (void) ioctl(vcfd, PIO_UNIMAPCLR, &adv); + (void) ioctl(vcfd, PIO_UNIMAP, &unimapd); + } + } +} + +int main(int argc, char **argv) { + const char *vc; + _cleanup_free_ char + *vc_keymap = NULL, *vc_keymap_toggle = NULL, + *vc_font = NULL, *vc_font_map = NULL, *vc_font_unimap = NULL; + _cleanup_close_ int fd = -1; + bool utf8, font_copy = false, font_ok, keyboard_ok; + int r = EXIT_FAILURE; + + log_set_target(LOG_TARGET_AUTO); + log_parse_environment(); + log_open(); + + umask(0022); + + if (argv[1]) + vc = argv[1]; + else { + vc = "/dev/tty0"; + font_copy = true; + } + + fd = open_terminal(vc, O_RDWR|O_CLOEXEC); + if (fd < 0) { + log_error_errno(fd, "Failed to open %s: %m", vc); + return EXIT_FAILURE; + } + + if (!is_vconsole(fd)) { + log_error("Device %s is not a virtual console.", vc); + return EXIT_FAILURE; + } + + utf8 = is_locale_utf8(); + + r = parse_env_file("/etc/vconsole.conf", NEWLINE, + "KEYMAP", &vc_keymap, + "KEYMAP_TOGGLE", &vc_keymap_toggle, + "FONT", &vc_font, + "FONT_MAP", &vc_font_map, + "FONT_UNIMAP", &vc_font_unimap, + NULL); + + if (r < 0 && r != -ENOENT) + log_warning_errno(r, "Failed to read /etc/vconsole.conf: %m"); + + /* Let the kernel command line override /etc/vconsole.conf */ + if (detect_container() <= 0) { + r = parse_env_file("/proc/cmdline", WHITESPACE, + "vconsole.keymap", &vc_keymap, + "vconsole.keymap.toggle", &vc_keymap_toggle, + "vconsole.font", &vc_font, + "vconsole.font.map", &vc_font_map, + "vconsole.font.unimap", &vc_font_unimap, + NULL); + + if (r < 0 && r != -ENOENT) + log_warning_errno(r, "Failed to read /proc/cmdline: %m"); + } + + if (utf8) + (void) enable_utf8(fd); + else + (void) disable_utf8(fd); + + font_ok = font_load_and_wait(vc, vc_font, vc_font_map, vc_font_unimap) > 0; + keyboard_ok = keyboard_load_and_wait(vc, vc_keymap, vc_keymap_toggle, utf8) > 0; + + /* Only copy the font when we executed setfont successfully */ + if (font_copy && font_ok) + (void) font_copy_to_all_vcs(fd); + + return font_ok && keyboard_ok ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/src/grp-initutils/Makefile b/src/grp-initutils/Makefile deleted file mode 100644 index 0bc22bb42c..0000000000 --- a/src/grp-initutils/Makefile +++ /dev/null @@ -1,43 +0,0 @@ -# -*- Mode: makefile; indent-tabs-mode: t -*- -# -# This file is part of systemd. -# -# Copyright 2010-2012 Lennart Poettering -# Copyright 2010-2012 Kay Sievers -# Copyright 2013 Zbigniew Jędrzejewski-Szmek -# Copyright 2013 David Strauss -# Copyright 2016 Luke Shumaker -# -# systemd is free software; you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation; either version 2.1 of the License, or -# (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 -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with systemd; If not, see . -include $(dir $(lastword $(MAKEFILE_LIST)))/../../config.mk -include $(topsrcdir)/build-aux/Makefile.head.mk - -nested.subdirs += systemd-backlight -nested.subdirs += systemd-binfmt -nested.subdirs += systemd-detect-virt -nested.subdirs += systemd-firstboot -nested.subdirs += systemd-fsck -nested.subdirs += systemd-quotacheck -nested.subdirs += systemd-random-seed -nested.subdirs += systemd-rfkill -nested.subdirs += systemd-sleep -nested.subdirs += systemd-sysctl -nested.subdirs += systemd-sysusers -nested.subdirs += systemd-tmpfiles -nested.subdirs += systemd-update-done -nested.subdirs += systemd-update-utmp -nested.subdirs += systemd-user-sessions -nested.subdirs += systemd-vconsole-setup - -include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-initutils/systemd-backlight/Makefile b/src/grp-initutils/systemd-backlight/Makefile deleted file mode 100644 index cf55ac4db9..0000000000 --- a/src/grp-initutils/systemd-backlight/Makefile +++ /dev/null @@ -1,43 +0,0 @@ -# -*- Mode: makefile; indent-tabs-mode: t -*- -# -# This file is part of systemd. -# -# Copyright 2010-2012 Lennart Poettering -# Copyright 2010-2012 Kay Sievers -# Copyright 2013 Zbigniew Jędrzejewski-Szmek -# Copyright 2013 David Strauss -# Copyright 2016 Luke Shumaker -# -# systemd is free software; you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation; either version 2.1 of the License, or -# (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 -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with systemd; If not, see . -include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk -include $(topsrcdir)/build-aux/Makefile.head.mk - -ifneq ($(ENABLE_BACKLIGHT),) -rootlibexec_PROGRAMS += \ - systemd-backlight - -nodist_systemunit_DATA += \ - units/systemd-backlight@.service - -systemd_backlight_SOURCES = \ - src/backlight/backlight.c - -systemd_backlight_LDADD = \ - libshared.la -endif # ENABLE_BACKLIGHT - -EXTRA_DIST += \ - units/systemd-backlight@.service.in - -include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-initutils/systemd-backlight/backlight.c b/src/grp-initutils/systemd-backlight/backlight.c deleted file mode 100644 index aa8d852024..0000000000 --- a/src/grp-initutils/systemd-backlight/backlight.c +++ /dev/null @@ -1,434 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include - -#include "basic/alloc-util.h" -#include "basic/def.h" -#include "basic/escape.h" -#include "basic/fileio.h" -#include "basic/mkdir.h" -#include "basic/parse-util.h" -#include "basic/proc-cmdline.h" -#include "basic/string-util.h" -#include "basic/util.h" -#include "shared/udev-util.h" - -static struct udev_device *find_pci_or_platform_parent(struct udev_device *device) { - struct udev_device *parent; - const char *subsystem, *sysname; - - assert(device); - - parent = udev_device_get_parent(device); - if (!parent) - return NULL; - - subsystem = udev_device_get_subsystem(parent); - if (!subsystem) - return NULL; - - sysname = udev_device_get_sysname(parent); - if (!sysname) - return NULL; - - if (streq(subsystem, "drm")) { - const char *c; - - c = startswith(sysname, "card"); - if (!c) - return NULL; - - c += strspn(c, DIGITS); - if (*c == '-') { - /* A connector DRM device, let's ignore all but LVDS and eDP! */ - - if (!startswith(c, "-LVDS-") && - !startswith(c, "-Embedded DisplayPort-")) - return NULL; - } - - } else if (streq(subsystem, "pci")) { - const char *value; - - value = udev_device_get_sysattr_value(parent, "class"); - if (value) { - unsigned long class = 0; - - if (safe_atolu(value, &class) < 0) { - log_warning("Cannot parse PCI class %s of device %s:%s.", - value, subsystem, sysname); - return NULL; - } - - /* Graphics card */ - if (class == 0x30000) - return parent; - } - - } else if (streq(subsystem, "platform")) - return parent; - - return find_pci_or_platform_parent(parent); -} - -static bool same_device(struct udev_device *a, struct udev_device *b) { - assert(a); - assert(b); - - if (!streq_ptr(udev_device_get_subsystem(a), udev_device_get_subsystem(b))) - return false; - - if (!streq_ptr(udev_device_get_sysname(a), udev_device_get_sysname(b))) - return false; - - return true; -} - -static bool validate_device(struct udev *udev, struct udev_device *device) { - _cleanup_udev_enumerate_unref_ struct udev_enumerate *enumerate = NULL; - struct udev_list_entry *item = NULL, *first = NULL; - struct udev_device *parent; - const char *v, *subsystem; - int r; - - assert(udev); - assert(device); - - /* Verify whether we should actually care for a specific - * backlight device. For backlight devices there might be - * multiple ways to access the same control: "firmware" - * (i.e. ACPI), "platform" (i.e. via the machine's EC) and - * "raw" (via the graphics card). In general we should prefer - * "firmware" (i.e. ACPI) or "platform" access over "raw" - * access, in order not to confuse the BIOS/EC, and - * compatibility with possible low-level hotkey handling of - * screen brightness. The kernel will already make sure to - * expose only one of "firmware" and "platform" for the same - * device to userspace. However, we still need to make sure - * that we use "raw" only if no "firmware" or "platform" - * device for the same device exists. */ - - subsystem = udev_device_get_subsystem(device); - if (!streq_ptr(subsystem, "backlight")) - return true; - - v = udev_device_get_sysattr_value(device, "type"); - if (!streq_ptr(v, "raw")) - return true; - - parent = find_pci_or_platform_parent(device); - if (!parent) - return true; - - subsystem = udev_device_get_subsystem(parent); - if (!subsystem) - return true; - - enumerate = udev_enumerate_new(udev); - if (!enumerate) - return true; - - r = udev_enumerate_add_match_subsystem(enumerate, "backlight"); - if (r < 0) - return true; - - r = udev_enumerate_scan_devices(enumerate); - if (r < 0) - return true; - - first = udev_enumerate_get_list_entry(enumerate); - udev_list_entry_foreach(item, first) { - _cleanup_udev_device_unref_ struct udev_device *other; - struct udev_device *other_parent; - const char *other_subsystem; - - other = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item)); - if (!other) - return true; - - if (same_device(device, other)) - continue; - - v = udev_device_get_sysattr_value(other, "type"); - if (!streq_ptr(v, "platform") && !streq_ptr(v, "firmware")) - continue; - - /* OK, so there's another backlight device, and it's a - * platform or firmware device, so, let's see if we - * can verify it belongs to the same device as - * ours. */ - other_parent = find_pci_or_platform_parent(other); - if (!other_parent) - continue; - - if (same_device(parent, other_parent)) { - /* Both have the same PCI parent, that means - * we are out. */ - log_debug("Skipping backlight device %s, since device %s is on same PCI device and takes precedence.", - udev_device_get_sysname(device), - udev_device_get_sysname(other)); - return false; - } - - other_subsystem = udev_device_get_subsystem(other_parent); - if (streq_ptr(other_subsystem, "platform") && streq_ptr(subsystem, "pci")) { - /* The other is connected to the platform bus - * and we are a PCI device, that also means we - * are out. */ - log_debug("Skipping backlight device %s, since device %s is a platform device and takes precedence.", - udev_device_get_sysname(device), - udev_device_get_sysname(other)); - return false; - } - } - - return true; -} - -static unsigned get_max_brightness(struct udev_device *device) { - int r; - const char *max_brightness_str; - unsigned max_brightness; - - max_brightness_str = udev_device_get_sysattr_value(device, "max_brightness"); - if (!max_brightness_str) { - log_warning("Failed to read 'max_brightness' attribute."); - return 0; - } - - r = safe_atou(max_brightness_str, &max_brightness); - if (r < 0) { - log_warning_errno(r, "Failed to parse 'max_brightness' \"%s\": %m", max_brightness_str); - return 0; - } - - if (max_brightness <= 0) { - log_warning("Maximum brightness is 0, ignoring device."); - return 0; - } - - return max_brightness; -} - -/* Some systems turn the backlight all the way off at the lowest levels. - * clamp_brightness clamps the saved brightness to at least 1 or 5% of - * max_brightness in case of 'backlight' subsystem. This avoids preserving - * an unreadably dim screen, which would otherwise force the user to - * disable state restoration. */ -static void clamp_brightness(struct udev_device *device, char **value, unsigned max_brightness) { - int r; - unsigned brightness, new_brightness, min_brightness; - const char *subsystem; - - r = safe_atou(*value, &brightness); - if (r < 0) { - log_warning_errno(r, "Failed to parse brightness \"%s\": %m", *value); - return; - } - - subsystem = udev_device_get_subsystem(device); - if (streq_ptr(subsystem, "backlight")) - min_brightness = MAX(1U, max_brightness/20); - else - min_brightness = 0; - - new_brightness = CLAMP(brightness, min_brightness, max_brightness); - if (new_brightness != brightness) { - char *old_value = *value; - - r = asprintf(value, "%u", new_brightness); - if (r < 0) { - log_oom(); - return; - } - - log_info("Saved brightness %s %s to %s.", old_value, - new_brightness > brightness ? - "too low; increasing" : "too high; decreasing", - *value); - - free(old_value); - } -} - -int main(int argc, char *argv[]) { - _cleanup_udev_unref_ struct udev *udev = NULL; - _cleanup_udev_device_unref_ struct udev_device *device = NULL; - _cleanup_free_ char *saved = NULL, *ss = NULL, *escaped_ss = NULL, *escaped_sysname = NULL, *escaped_path_id = NULL; - const char *sysname, *path_id; - unsigned max_brightness; - int r; - - if (argc != 3) { - log_error("This program requires two arguments."); - return EXIT_FAILURE; - } - - log_set_target(LOG_TARGET_AUTO); - log_parse_environment(); - log_open(); - - umask(0022); - - r = mkdir_p("/var/lib/systemd/backlight", 0755); - if (r < 0) { - log_error_errno(r, "Failed to create backlight directory /var/lib/systemd/backlight: %m"); - return EXIT_FAILURE; - } - - udev = udev_new(); - if (!udev) { - log_oom(); - return EXIT_FAILURE; - } - - sysname = strchr(argv[2], ':'); - if (!sysname) { - log_error("Requires a subsystem and sysname pair specifying a backlight device."); - return EXIT_FAILURE; - } - - ss = strndup(argv[2], sysname - argv[2]); - if (!ss) { - log_oom(); - return EXIT_FAILURE; - } - - sysname++; - - if (!streq(ss, "backlight") && !streq(ss, "leds")) { - log_error("Not a backlight or LED device: '%s:%s'", ss, sysname); - return EXIT_FAILURE; - } - - errno = 0; - device = udev_device_new_from_subsystem_sysname(udev, ss, sysname); - if (!device) { - if (errno > 0) - log_error_errno(errno, "Failed to get backlight or LED device '%s:%s': %m", ss, sysname); - else - log_oom(); - - return EXIT_FAILURE; - } - - /* If max_brightness is 0, then there is no actual backlight - * device. This happens on desktops with Asus mainboards - * that load the eeepc-wmi module. - */ - max_brightness = get_max_brightness(device); - if (max_brightness == 0) - return EXIT_SUCCESS; - - escaped_ss = cescape(ss); - if (!escaped_ss) { - log_oom(); - return EXIT_FAILURE; - } - - escaped_sysname = cescape(sysname); - if (!escaped_sysname) { - log_oom(); - return EXIT_FAILURE; - } - - path_id = udev_device_get_property_value(device, "ID_PATH"); - if (path_id) { - escaped_path_id = cescape(path_id); - if (!escaped_path_id) { - log_oom(); - return EXIT_FAILURE; - } - - saved = strjoin("/var/lib/systemd/backlight/", escaped_path_id, ":", escaped_ss, ":", escaped_sysname, NULL); - } else - saved = strjoin("/var/lib/systemd/backlight/", escaped_ss, ":", escaped_sysname, NULL); - - if (!saved) { - log_oom(); - return EXIT_FAILURE; - } - - /* If there are multiple conflicting backlight devices, then - * their probing at boot-time might happen in any order. This - * means the validity checking of the device then is not - * reliable, since it might not see other devices conflicting - * with a specific backlight. To deal with this, we will - * actively delete backlight state files at shutdown (where - * device probing should be complete), so that the validity - * check at boot time doesn't have to be reliable. */ - - if (streq(argv[1], "load")) { - _cleanup_free_ char *value = NULL; - const char *clamp; - - if (shall_restore_state() == 0) - return EXIT_SUCCESS; - - if (!validate_device(udev, device)) - return EXIT_SUCCESS; - - r = read_one_line_file(saved, &value); - if (r < 0) { - - if (r == -ENOENT) - return EXIT_SUCCESS; - - log_error_errno(r, "Failed to read %s: %m", saved); - return EXIT_FAILURE; - } - - clamp = udev_device_get_property_value(device, "ID_BACKLIGHT_CLAMP"); - if (!clamp || parse_boolean(clamp) != 0) /* default to clamping */ - clamp_brightness(device, &value, max_brightness); - - r = udev_device_set_sysattr_value(device, "brightness", value); - if (r < 0) { - log_error_errno(r, "Failed to write system 'brightness' attribute: %m"); - return EXIT_FAILURE; - } - - } else if (streq(argv[1], "save")) { - const char *value; - - if (!validate_device(udev, device)) { - unlink(saved); - return EXIT_SUCCESS; - } - - value = udev_device_get_sysattr_value(device, "brightness"); - if (!value) { - log_error("Failed to read system 'brightness' attribute"); - return EXIT_FAILURE; - } - - r = write_string_file(saved, value, WRITE_STRING_FILE_CREATE); - if (r < 0) { - log_error_errno(r, "Failed to write %s: %m", saved); - return EXIT_FAILURE; - } - - } else { - log_error("Unknown verb %s.", argv[1]); - return EXIT_FAILURE; - } - - return EXIT_SUCCESS; -} diff --git a/src/grp-initutils/systemd-binfmt/Makefile b/src/grp-initutils/systemd-binfmt/Makefile deleted file mode 100644 index 3e5c1ac270..0000000000 --- a/src/grp-initutils/systemd-binfmt/Makefile +++ /dev/null @@ -1,56 +0,0 @@ -# -*- Mode: makefile; indent-tabs-mode: t -*- -# -# This file is part of systemd. -# -# Copyright 2010-2012 Lennart Poettering -# Copyright 2010-2012 Kay Sievers -# Copyright 2013 Zbigniew Jędrzejewski-Szmek -# Copyright 2013 David Strauss -# Copyright 2016 Luke Shumaker -# -# systemd is free software; you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation; either version 2.1 of the License, or -# (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 -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with systemd; If not, see . -include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk -include $(topsrcdir)/build-aux/Makefile.head.mk - -ifneq ($(ENABLE_BINFMT),) -systemd_binfmt_SOURCES = \ - src/binfmt/binfmt.c - -systemd_binfmt_LDADD = \ - libshared.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 - -INSTALL_DIRS += \ - $(prefix)/lib/binfmt.d \ - $(sysconfdir)/binfmt.d - -SYSINIT_TARGET_WANTS += \ - systemd-binfmt.service \ - proc-sys-fs-binfmt_misc.automount - -endif # ENABLE_BINFMT - -EXTRA_DIST += \ - units/systemd-binfmt.service.in - -include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-initutils/systemd-binfmt/binfmt.c b/src/grp-initutils/systemd-binfmt/binfmt.c deleted file mode 100644 index 39bbdb5335..0000000000 --- a/src/grp-initutils/systemd-binfmt/binfmt.c +++ /dev/null @@ -1,203 +0,0 @@ -/*** - 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 Lesser General Public License as published by - the Free Software Foundation; either version 2.1 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include -#include - -#include "basic/alloc-util.h" -#include "basic/conf-files.h" -#include "basic/def.h" -#include "basic/fd-util.h" -#include "basic/fileio.h" -#include "basic/log.h" -#include "basic/string-util.h" -#include "basic/strv.h" -#include "basic/util.h" - -static const char conf_file_dirs[] = CONF_PATHS_NULSTR("binfmt.d"); - -static int delete_rule(const char *rule) { - _cleanup_free_ char *x = NULL, *fn = NULL; - char *e; - - assert(rule[0]); - - x = strdup(rule); - if (!x) - return log_oom(); - - e = strchrnul(x+1, x[0]); - *e = 0; - - fn = strappend("/proc/sys/fs/binfmt_misc/", x+1); - if (!fn) - return log_oom(); - - return write_string_file(fn, "-1", 0); -} - -static int apply_rule(const char *rule) { - int r; - - delete_rule(rule); - - r = write_string_file("/proc/sys/fs/binfmt_misc/register", rule, 0); - if (r < 0) - return log_error_errno(r, "Failed to add binary format: %m"); - - return 0; -} - -static int apply_file(const char *path, bool ignore_enoent) { - _cleanup_fclose_ FILE *f = NULL; - int r; - - assert(path); - - r = search_and_fopen_nulstr(path, "re", NULL, conf_file_dirs, &f); - if (r < 0) { - if (ignore_enoent && r == -ENOENT) - return 0; - - return log_error_errno(r, "Failed to open file '%s', ignoring: %m", path); - } - - log_debug("apply: %s", path); - for (;;) { - char l[LINE_MAX], *p; - int k; - - if (!fgets(l, sizeof(l), f)) { - if (feof(f)) - break; - - return log_error_errno(errno, "Failed to read file '%s', ignoring: %m", path); - } - - p = strstrip(l); - if (!*p) - continue; - if (strchr(COMMENTS "\n", *p)) - continue; - - k = apply_rule(p); - if (k < 0 && r == 0) - r = k; - } - - return r; -} - -static void help(void) { - printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n" - "Registers binary formats.\n\n" - " -h --help Show this help\n" - " --version Show package version\n" - , program_invocation_short_name); -} - -static int parse_argv(int argc, char *argv[]) { - - enum { - ARG_VERSION = 0x100, - }; - - static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, ARG_VERSION }, - {} - }; - - int c; - - assert(argc >= 0); - assert(argv); - - while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) - - switch (c) { - - case 'h': - help(); - return 0; - - case ARG_VERSION: - return version(); - - case '?': - return -EINVAL; - - default: - assert_not_reached("Unhandled option"); - } - - return 1; -} - -int main(int argc, char *argv[]) { - int r, k; - - 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); - - r = 0; - - if (argc > optind) { - int i; - - for (i = optind; i < argc; i++) { - k = apply_file(argv[i], false); - if (k < 0 && r == 0) - r = k; - } - } else { - _cleanup_strv_free_ char **files = NULL; - char **f; - - r = conf_files_list_nulstr(&files, ".conf", NULL, conf_file_dirs); - if (r < 0) { - log_error_errno(r, "Failed to enumerate binfmt.d files: %m"); - goto finish; - } - - /* Flush out all rules */ - write_string_file("/proc/sys/fs/binfmt_misc/status", "-1", 0); - - STRV_FOREACH(f, files) { - k = apply_file(*f, true); - if (k < 0 && r == 0) - r = k; - } - } - -finish: - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; -} diff --git a/src/grp-initutils/systemd-detect-virt/Makefile b/src/grp-initutils/systemd-detect-virt/Makefile deleted file mode 100644 index 7b9b9f667b..0000000000 --- a/src/grp-initutils/systemd-detect-virt/Makefile +++ /dev/null @@ -1,36 +0,0 @@ -# -*- Mode: makefile; indent-tabs-mode: t -*- -# -# This file is part of systemd. -# -# Copyright 2010-2012 Lennart Poettering -# Copyright 2010-2012 Kay Sievers -# Copyright 2013 Zbigniew Jędrzejewski-Szmek -# Copyright 2013 David Strauss -# Copyright 2016 Luke Shumaker -# -# systemd is free software; you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation; either version 2.1 of the License, or -# (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 -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with systemd; If not, see . -include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk -include $(topsrcdir)/build-aux/Makefile.head.mk - -bin_PROGRAMS += systemd-detect-virt -systemd_detect_virt_SOURCES = \ - src/detect-virt/detect-virt.c - -systemd_detect_virt_LDADD = \ - libshared.la - -INSTALL_EXEC_HOOKS += \ - systemd-detect-virt-install-hook - -include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-initutils/systemd-detect-virt/detect-virt.c b/src/grp-initutils/systemd-detect-virt/detect-virt.c deleted file mode 100644 index 06cdab5297..0000000000 --- a/src/grp-initutils/systemd-detect-virt/detect-virt.c +++ /dev/null @@ -1,169 +0,0 @@ -/*** - 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 Lesser General Public License as published by - the Free Software Foundation; either version 2.1 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include - -#include "basic/util.h" -#include "basic/virt.h" - -static bool arg_quiet = false; -static enum { - ANY_VIRTUALIZATION, - ONLY_VM, - ONLY_CONTAINER, - ONLY_CHROOT, -} arg_mode = ANY_VIRTUALIZATION; - -static void help(void) { - printf("%s [OPTIONS...]\n\n" - "Detect execution in a virtualized environment.\n\n" - " -h --help Show this help\n" - " --version Show package version\n" - " -c --container Only detect whether we are run in a container\n" - " -v --vm Only detect whether we are run in a VM\n" - " -r --chroot Detect whether we are run in a chroot() environment\n" - " -q --quiet Don't output anything, just set return value\n" - , program_invocation_short_name); -} - -static int parse_argv(int argc, char *argv[]) { - - enum { - ARG_VERSION = 0x100 - }; - - static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, ARG_VERSION }, - { "container", no_argument, NULL, 'c' }, - { "vm", no_argument, NULL, 'v' }, - { "chroot", no_argument, NULL, 'r' }, - { "quiet", no_argument, NULL, 'q' }, - {} - }; - - int c; - - assert(argc >= 0); - assert(argv); - - while ((c = getopt_long(argc, argv, "hqcvr", options, NULL)) >= 0) - - switch (c) { - - case 'h': - help(); - return 0; - - case ARG_VERSION: - return version(); - - case 'q': - arg_quiet = true; - break; - - case 'c': - arg_mode = ONLY_CONTAINER; - break; - - case 'v': - arg_mode = ONLY_VM; - break; - - case 'r': - arg_mode = ONLY_CHROOT; - break; - - case '?': - return -EINVAL; - - default: - assert_not_reached("Unhandled option"); - } - - if (optind < argc) { - log_error("%s takes no arguments.", program_invocation_short_name); - return -EINVAL; - } - - return 1; -} - -int main(int argc, char *argv[]) { - int r; - - /* This is mostly intended to be used for scripts which want - * to detect whether we are being run in a virtualized - * environment or not */ - - log_parse_environment(); - log_open(); - - r = parse_argv(argc, argv); - if (r <= 0) - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; - - switch (arg_mode) { - - case ONLY_VM: - r = detect_vm(); - if (r < 0) { - log_error_errno(r, "Failed to check for VM: %m"); - return EXIT_FAILURE; - } - - break; - - case ONLY_CONTAINER: - r = detect_container(); - if (r < 0) { - log_error_errno(r, "Failed to check for container: %m"); - return EXIT_FAILURE; - } - - break; - - case ONLY_CHROOT: - r = running_in_chroot(); - if (r < 0) { - log_error_errno(r, "Failed to check for chroot() environment: %m"); - return EXIT_FAILURE; - } - - return r ? EXIT_SUCCESS : EXIT_FAILURE; - - case ANY_VIRTUALIZATION: - default: - r = detect_virtualization(); - if (r < 0) { - log_error_errno(r, "Failed to check for virtualization: %m"); - return EXIT_FAILURE; - } - - break; - } - - if (!arg_quiet) - puts(virtualization_to_string(r)); - - return r != VIRTUALIZATION_NONE ? EXIT_SUCCESS : EXIT_FAILURE; -} diff --git a/src/grp-initutils/systemd-detect-virt/systemd-detect-virt.completion.bash b/src/grp-initutils/systemd-detect-virt/systemd-detect-virt.completion.bash deleted file mode 100644 index df06c29841..0000000000 --- a/src/grp-initutils/systemd-detect-virt/systemd-detect-virt.completion.bash +++ /dev/null @@ -1,40 +0,0 @@ -# systemd-detect-virt(1) completion -*- shell-script -*- -# -# This file is part of systemd. -# -# Copyright 2014 Thomas H.P. Andersen -# -# systemd is free software; you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation; either version 2.1 of the License, or -# (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 Lesser General Public License -# along with systemd; If not, see . - -__contains_word() { - local w word=$1; shift - for w in "$@"; do - [[ $w = "$word" ]] && return - done -} - -_systemd_detect_virt() { - local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]} - local i verb comps - - local -A OPTS=( - [STANDALONE]='-h --help --version -c --container -v --vm -q --quiet' - ) - - _init_completion || return - - COMPREPLY=( $(compgen -W '${OPTS[*]}' -- "$cur") ) -} - -complete -F _systemd_detect_virt systemd-detect-virt diff --git a/src/grp-initutils/systemd-detect-virt/systemd-detect-virt.completion.zsh b/src/grp-initutils/systemd-detect-virt/systemd-detect-virt.completion.zsh deleted file mode 100644 index a0c7df727c..0000000000 --- a/src/grp-initutils/systemd-detect-virt/systemd-detect-virt.completion.zsh +++ /dev/null @@ -1,11 +0,0 @@ -#compdef systemd-detect-virt - -local curcontext="$curcontext" state lstate line -_arguments \ - {-h,--help}'[Show this help]' \ - '--version[Show package version]' \ - {-c,--container}'[Only detect whether we are run in a container]' \ - {-v,--vm}'[Only detect whether we are run in a VM]' \ - {-q,--quiet}"[Don't output anything, just set return value]" - -#vim: set ft=zsh sw=4 ts=4 et diff --git a/src/grp-initutils/systemd-firstboot/Makefile b/src/grp-initutils/systemd-firstboot/Makefile deleted file mode 100644 index b94b344bb9..0000000000 --- a/src/grp-initutils/systemd-firstboot/Makefile +++ /dev/null @@ -1,47 +0,0 @@ -# -*- Mode: makefile; indent-tabs-mode: t -*- -# -# This file is part of systemd. -# -# Copyright 2010-2012 Lennart Poettering -# Copyright 2010-2012 Kay Sievers -# Copyright 2013 Zbigniew Jędrzejewski-Szmek -# Copyright 2013 David Strauss -# Copyright 2016 Luke Shumaker -# -# systemd is free software; you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation; either version 2.1 of the License, or -# (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 -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with systemd; If not, see . -include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk -include $(topsrcdir)/build-aux/Makefile.head.mk - -ifneq ($(ENABLE_FIRSTBOOT),) -systemd_firstboot_SOURCES = \ - src/firstboot/firstboot.c - -systemd_firstboot_LDADD = \ - libshared.la \ - -lcrypt - -rootbin_PROGRAMS += \ - systemd-firstboot - -nodist_systemunit_DATA += \ - units/systemd-firstboot.service - -SYSINIT_TARGET_WANTS += \ - systemd-firstboot.service -endif # ENABLE_FIRSTBOOT - -EXTRA_DIST += \ - units/systemd-firstboot.service.in - -include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-initutils/systemd-firstboot/firstboot.c b/src/grp-initutils/systemd-firstboot/firstboot.c deleted file mode 100644 index d2059a943f..0000000000 --- a/src/grp-initutils/systemd-firstboot/firstboot.c +++ /dev/null @@ -1,870 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include - -#include "basic/alloc-util.h" -#include "basic/copy.h" -#include "basic/fd-util.h" -#include "basic/fileio.h" -#include "basic/fs-util.h" -#include "basic/hostname-util.h" -#include "basic/locale-util.h" -#include "basic/mkdir.h" -#include "basic/parse-util.h" -#include "basic/path-util.h" -#include "basic/random-util.h" -#include "basic/string-util.h" -#include "basic/strv.h" -#include "basic/terminal-util.h" -#include "basic/time-util.h" -#include "basic/umask-util.h" -#include "basic/user-util.h" -#include "shared/ask-password-api.h" - -static char *arg_root = NULL; -static char *arg_locale = NULL; /* $LANG */ -static char *arg_locale_messages = NULL; /* $LC_MESSAGES */ -static char *arg_timezone = NULL; -static char *arg_hostname = NULL; -static sd_id128_t arg_machine_id = {}; -static char *arg_root_password = NULL; -static bool arg_prompt_locale = false; -static bool arg_prompt_timezone = false; -static bool arg_prompt_hostname = false; -static bool arg_prompt_root_password = false; -static bool arg_copy_locale = false; -static bool arg_copy_timezone = false; -static bool arg_copy_root_password = false; - -static bool press_any_key(void) { - char k = 0; - bool need_nl = true; - - printf("-- Press any key to proceed --"); - fflush(stdout); - - (void) read_one_char(stdin, &k, USEC_INFINITY, &need_nl); - - if (need_nl) - putchar('\n'); - - return k != 'q'; -} - -static void print_welcome(void) { - _cleanup_free_ char *pretty_name = NULL; - const char *os_release = NULL; - static bool done = false; - int r; - - if (done) - return; - - os_release = prefix_roota(arg_root, "/etc/os-release"); - r = parse_env_file(os_release, NEWLINE, - "PRETTY_NAME", &pretty_name, - NULL); - if (r == -ENOENT) { - - os_release = prefix_roota(arg_root, "/usr/lib/os-release"); - r = parse_env_file(os_release, NEWLINE, - "PRETTY_NAME", &pretty_name, - NULL); - } - - if (r < 0 && r != -ENOENT) - log_warning_errno(r, "Failed to read os-release file: %m"); - - printf("\nWelcome to your new installation of %s!\nPlease configure a few basic system settings:\n\n", - isempty(pretty_name) ? "GNU/Linux" : pretty_name); - - press_any_key(); - - done = true; -} - -static int show_menu(char **x, unsigned n_columns, unsigned width, unsigned percentage) { - unsigned n, per_column, i, j; - unsigned break_lines, break_modulo; - - assert(n_columns > 0); - - n = strv_length(x); - per_column = (n + n_columns - 1) / n_columns; - - break_lines = lines(); - if (break_lines > 2) - break_lines--; - - /* The first page gets two extra lines, since we want to show - * a title */ - break_modulo = break_lines; - if (break_modulo > 3) - break_modulo -= 3; - - for (i = 0; i < per_column; i++) { - - for (j = 0; j < n_columns; j ++) { - _cleanup_free_ char *e = NULL; - - if (j * per_column + i >= n) - break; - - e = ellipsize(x[j * per_column + i], width, percentage); - if (!e) - return log_oom(); - - printf("%4u) %-*s", j * per_column + i + 1, width, e); - } - - putchar('\n'); - - /* on the first screen we reserve 2 extra lines for the title */ - if (i % break_lines == break_modulo) { - if (!press_any_key()) - return 0; - } - } - - return 0; -} - -static int prompt_loop(const char *text, char **l, bool (*is_valid)(const char *name), char **ret) { - int r; - - assert(text); - assert(is_valid); - assert(ret); - - for (;;) { - _cleanup_free_ char *p = NULL; - unsigned u; - - r = ask_string(&p, "%s %s (empty to skip): ", special_glyph(TRIANGULAR_BULLET), text); - if (r < 0) - return log_error_errno(r, "Failed to query user: %m"); - - if (isempty(p)) { - log_warning("No data entered, skipping."); - return 0; - } - - r = safe_atou(p, &u); - if (r >= 0) { - char *c; - - if (u <= 0 || u > strv_length(l)) { - log_error("Specified entry number out of range."); - continue; - } - - log_info("Selected '%s'.", l[u-1]); - - c = strdup(l[u-1]); - if (!c) - return log_oom(); - - free(*ret); - *ret = c; - return 0; - } - - if (!is_valid(p)) { - log_error("Entered data invalid."); - continue; - } - - free(*ret); - *ret = p; - p = 0; - return 0; - } -} - -static int prompt_locale(void) { - _cleanup_strv_free_ char **locales = NULL; - int r; - - if (arg_locale || arg_locale_messages) - return 0; - - if (!arg_prompt_locale) - return 0; - - r = get_locales(&locales); - if (r < 0) - return log_error_errno(r, "Cannot query locales list: %m"); - - print_welcome(); - - printf("\nAvailable Locales:\n\n"); - r = show_menu(locales, 3, 22, 60); - if (r < 0) - return r; - - putchar('\n'); - - r = prompt_loop("Please enter system locale name or number", locales, locale_is_valid, &arg_locale); - if (r < 0) - return r; - - if (isempty(arg_locale)) - return 0; - - r = prompt_loop("Please enter system message locale name or number", locales, locale_is_valid, &arg_locale_messages); - if (r < 0) - return r; - - return 0; -} - -static int process_locale(void) { - const char *etc_localeconf; - char* locales[3]; - unsigned i = 0; - int r; - - etc_localeconf = prefix_roota(arg_root, "/etc/locale.conf"); - if (laccess(etc_localeconf, F_OK) >= 0) - return 0; - - if (arg_copy_locale && arg_root) { - - mkdir_parents(etc_localeconf, 0755); - r = copy_file("/etc/locale.conf", etc_localeconf, 0, 0644, 0); - if (r != -ENOENT) { - if (r < 0) - return log_error_errno(r, "Failed to copy %s: %m", etc_localeconf); - - log_info("%s copied.", etc_localeconf); - return 0; - } - } - - r = prompt_locale(); - if (r < 0) - return r; - - if (!isempty(arg_locale)) - locales[i++] = strjoina("LANG=", arg_locale); - if (!isempty(arg_locale_messages) && !streq(arg_locale_messages, arg_locale)) - locales[i++] = strjoina("LC_MESSAGES=", arg_locale_messages); - - if (i == 0) - return 0; - - locales[i] = NULL; - - mkdir_parents(etc_localeconf, 0755); - r = write_env_file(etc_localeconf, locales); - if (r < 0) - return log_error_errno(r, "Failed to write %s: %m", etc_localeconf); - - log_info("%s written.", etc_localeconf); - return 0; -} - -static int prompt_timezone(void) { - _cleanup_strv_free_ char **zones = NULL; - int r; - - if (arg_timezone) - return 0; - - if (!arg_prompt_timezone) - return 0; - - r = get_timezones(&zones); - if (r < 0) - return log_error_errno(r, "Cannot query timezone list: %m"); - - print_welcome(); - - printf("\nAvailable Time Zones:\n\n"); - r = show_menu(zones, 3, 22, 30); - if (r < 0) - return r; - - putchar('\n'); - - r = prompt_loop("Please enter timezone name or number", zones, timezone_is_valid, &arg_timezone); - if (r < 0) - return r; - - return 0; -} - -static int process_timezone(void) { - const char *etc_localtime, *e; - int r; - - etc_localtime = prefix_roota(arg_root, "/etc/localtime"); - if (laccess(etc_localtime, F_OK) >= 0) - return 0; - - if (arg_copy_timezone && arg_root) { - _cleanup_free_ char *p = NULL; - - r = readlink_malloc("/etc/localtime", &p); - if (r != -ENOENT) { - if (r < 0) - return log_error_errno(r, "Failed to read host timezone: %m"); - - mkdir_parents(etc_localtime, 0755); - if (symlink(p, etc_localtime) < 0) - return log_error_errno(errno, "Failed to create %s symlink: %m", etc_localtime); - - log_info("%s copied.", etc_localtime); - return 0; - } - } - - r = prompt_timezone(); - if (r < 0) - return r; - - if (isempty(arg_timezone)) - return 0; - - e = strjoina("../usr/share/zoneinfo/", arg_timezone); - - mkdir_parents(etc_localtime, 0755); - if (symlink(e, etc_localtime) < 0) - return log_error_errno(errno, "Failed to create %s symlink: %m", etc_localtime); - - log_info("%s written", etc_localtime); - return 0; -} - -static int prompt_hostname(void) { - int r; - - if (arg_hostname) - return 0; - - if (!arg_prompt_hostname) - return 0; - - print_welcome(); - putchar('\n'); - - for (;;) { - _cleanup_free_ char *h = NULL; - - r = ask_string(&h, "%s Please enter hostname for new system (empty to skip): ", special_glyph(TRIANGULAR_BULLET)); - if (r < 0) - return log_error_errno(r, "Failed to query hostname: %m"); - - if (isempty(h)) { - log_warning("No hostname entered, skipping."); - break; - } - - if (!hostname_is_valid(h, true)) { - log_error("Specified hostname invalid."); - continue; - } - - /* Get rid of the trailing dot that we allow, but don't want to see */ - arg_hostname = hostname_cleanup(h); - h = NULL; - break; - } - - return 0; -} - -static int process_hostname(void) { - const char *etc_hostname; - int r; - - etc_hostname = prefix_roota(arg_root, "/etc/hostname"); - if (laccess(etc_hostname, F_OK) >= 0) - return 0; - - r = prompt_hostname(); - if (r < 0) - return r; - - if (isempty(arg_hostname)) - return 0; - - mkdir_parents(etc_hostname, 0755); - r = write_string_file(etc_hostname, arg_hostname, WRITE_STRING_FILE_CREATE); - if (r < 0) - return log_error_errno(r, "Failed to write %s: %m", etc_hostname); - - log_info("%s written.", etc_hostname); - return 0; -} - -static int process_machine_id(void) { - const char *etc_machine_id; - char id[SD_ID128_STRING_MAX]; - int r; - - etc_machine_id = prefix_roota(arg_root, "/etc/machine-id"); - if (laccess(etc_machine_id, F_OK) >= 0) - return 0; - - if (sd_id128_equal(arg_machine_id, SD_ID128_NULL)) - return 0; - - mkdir_parents(etc_machine_id, 0755); - r = write_string_file(etc_machine_id, sd_id128_to_string(arg_machine_id, id), WRITE_STRING_FILE_CREATE); - if (r < 0) - return log_error_errno(r, "Failed to write machine id: %m"); - - log_info("%s written.", etc_machine_id); - return 0; -} - -static int prompt_root_password(void) { - const char *msg1, *msg2, *etc_shadow; - int r; - - if (arg_root_password) - return 0; - - if (!arg_prompt_root_password) - return 0; - - etc_shadow = prefix_roota(arg_root, "/etc/shadow"); - if (laccess(etc_shadow, F_OK) >= 0) - return 0; - - print_welcome(); - putchar('\n'); - - msg1 = strjoina(special_glyph(TRIANGULAR_BULLET), " Please enter a new root password (empty to skip): "); - msg2 = strjoina(special_glyph(TRIANGULAR_BULLET), " Please enter new root password again: "); - - for (;;) { - _cleanup_string_free_erase_ char *a = NULL, *b = NULL; - - r = ask_password_tty(msg1, NULL, 0, 0, NULL, &a); - if (r < 0) - return log_error_errno(r, "Failed to query root password: %m"); - - if (isempty(a)) { - log_warning("No password entered, skipping."); - break; - } - - r = ask_password_tty(msg2, NULL, 0, 0, NULL, &b); - if (r < 0) - return log_error_errno(r, "Failed to query root password: %m"); - - if (!streq(a, b)) { - log_error("Entered passwords did not match, please try again."); - continue; - } - - arg_root_password = a; - a = NULL; - break; - } - - return 0; -} - -static int write_root_shadow(const char *path, const struct spwd *p) { - _cleanup_fclose_ FILE *f = NULL; - assert(path); - assert(p); - - RUN_WITH_UMASK(0777) - f = fopen(path, "wex"); - if (!f) - return -errno; - - errno = 0; - if (putspent(p, f) != 0) - return errno > 0 ? -errno : -EIO; - - return fflush_and_check(f); -} - -static int process_root_password(void) { - - static const char table[] = - "abcdefghijklmnopqrstuvwxyz" - "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "0123456789" - "./"; - - struct spwd item = { - .sp_namp = (char*) "root", - .sp_min = -1, - .sp_max = -1, - .sp_warn = -1, - .sp_inact = -1, - .sp_expire = -1, - .sp_flag = (unsigned long) -1, /* this appears to be what everybody does ... */ - }; - - _cleanup_close_ int lock = -1; - char salt[3+16+1+1]; - uint8_t raw[16]; - unsigned i; - char *j; - - const char *etc_shadow; - int r; - - etc_shadow = prefix_roota(arg_root, "/etc/shadow"); - if (laccess(etc_shadow, F_OK) >= 0) - return 0; - - mkdir_parents(etc_shadow, 0755); - - lock = take_etc_passwd_lock(arg_root); - if (lock < 0) - return log_error_errno(lock, "Failed to take a lock: %m"); - - if (arg_copy_root_password && arg_root) { - struct spwd *p; - - errno = 0; - p = getspnam("root"); - if (p || errno != ENOENT) { - if (!p) { - if (!errno) - errno = EIO; - - return log_error_errno(errno, "Failed to find shadow entry for root: %m"); - } - - r = write_root_shadow(etc_shadow, p); - if (r < 0) - return log_error_errno(r, "Failed to write %s: %m", etc_shadow); - - log_info("%s copied.", etc_shadow); - return 0; - } - } - - r = prompt_root_password(); - if (r < 0) - return r; - - if (!arg_root_password) - return 0; - - r = dev_urandom(raw, 16); - if (r < 0) - return log_error_errno(r, "Failed to get salt: %m"); - - /* We only bother with SHA512 hashed passwords, the rest is legacy, and we don't do legacy. */ - assert_cc(sizeof(table) == 64 + 1); - j = stpcpy(salt, "$6$"); - for (i = 0; i < 16; i++) - j[i] = table[raw[i] & 63]; - j[i++] = '$'; - j[i] = 0; - - errno = 0; - item.sp_pwdp = crypt(arg_root_password, salt); - if (!item.sp_pwdp) { - if (!errno) - errno = EINVAL; - - return log_error_errno(errno, "Failed to encrypt password: %m"); - } - - item.sp_lstchg = (long) (now(CLOCK_REALTIME) / USEC_PER_DAY); - - r = write_root_shadow(etc_shadow, &item); - if (r < 0) - return log_error_errno(r, "Failed to write %s: %m", etc_shadow); - - log_info("%s written.", etc_shadow); - return 0; -} - -static void help(void) { - printf("%s [OPTIONS...]\n\n" - "Configures basic settings of the system.\n\n" - " -h --help Show this help\n" - " --version Show package version\n" - " --root=PATH Operate on an alternate filesystem root\n" - " --locale=LOCALE Set primary locale (LANG=)\n" - " --locale-messages=LOCALE Set message locale (LC_MESSAGES=)\n" - " --timezone=TIMEZONE Set timezone\n" - " --hostname=NAME Set host name\n" - " --machine-ID=ID Set machine ID\n" - " --root-password=PASSWORD Set root password\n" - " --root-password-file=FILE Set root password from file\n" - " --prompt-locale Prompt the user for locale settings\n" - " --prompt-timezone Prompt the user for timezone\n" - " --prompt-hostname Prompt the user for hostname\n" - " --prompt-root-password Prompt the user for root password\n" - " --prompt Prompt for all of the above\n" - " --copy-locale Copy locale from host\n" - " --copy-timezone Copy timezone from host\n" - " --copy-root-password Copy root password from host\n" - " --copy Copy locale, timezone, root password\n" - " --setup-machine-id Generate a new random machine ID\n" - , program_invocation_short_name); -} - -static int parse_argv(int argc, char *argv[]) { - - enum { - ARG_VERSION = 0x100, - ARG_ROOT, - ARG_LOCALE, - ARG_LOCALE_MESSAGES, - ARG_TIMEZONE, - ARG_HOSTNAME, - ARG_MACHINE_ID, - ARG_ROOT_PASSWORD, - ARG_ROOT_PASSWORD_FILE, - ARG_PROMPT, - ARG_PROMPT_LOCALE, - ARG_PROMPT_TIMEZONE, - ARG_PROMPT_HOSTNAME, - ARG_PROMPT_ROOT_PASSWORD, - ARG_COPY, - ARG_COPY_LOCALE, - ARG_COPY_TIMEZONE, - ARG_COPY_ROOT_PASSWORD, - ARG_SETUP_MACHINE_ID, - }; - - static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, ARG_VERSION }, - { "root", required_argument, NULL, ARG_ROOT }, - { "locale", required_argument, NULL, ARG_LOCALE }, - { "locale-messages", required_argument, NULL, ARG_LOCALE_MESSAGES }, - { "timezone", required_argument, NULL, ARG_TIMEZONE }, - { "hostname", required_argument, NULL, ARG_HOSTNAME }, - { "machine-id", required_argument, NULL, ARG_MACHINE_ID }, - { "root-password", required_argument, NULL, ARG_ROOT_PASSWORD }, - { "root-password-file", required_argument, NULL, ARG_ROOT_PASSWORD_FILE }, - { "prompt", no_argument, NULL, ARG_PROMPT }, - { "prompt-locale", no_argument, NULL, ARG_PROMPT_LOCALE }, - { "prompt-timezone", no_argument, NULL, ARG_PROMPT_TIMEZONE }, - { "prompt-hostname", no_argument, NULL, ARG_PROMPT_HOSTNAME }, - { "prompt-root-password", no_argument, NULL, ARG_PROMPT_ROOT_PASSWORD }, - { "copy", no_argument, NULL, ARG_COPY }, - { "copy-locale", no_argument, NULL, ARG_COPY_LOCALE }, - { "copy-timezone", no_argument, NULL, ARG_COPY_TIMEZONE }, - { "copy-root-password", no_argument, NULL, ARG_COPY_ROOT_PASSWORD }, - { "setup-machine-id", no_argument, NULL, ARG_SETUP_MACHINE_ID }, - {} - }; - - int r, c; - - assert(argc >= 0); - assert(argv); - - while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) - - switch (c) { - - case 'h': - help(); - return 0; - - case ARG_VERSION: - return version(); - - case ARG_ROOT: - r = parse_path_argument_and_warn(optarg, true, &arg_root); - if (r < 0) - return r; - break; - - case ARG_LOCALE: - if (!locale_is_valid(optarg)) { - log_error("Locale %s is not valid.", optarg); - return -EINVAL; - } - - r = free_and_strdup(&arg_locale, optarg); - if (r < 0) - return log_oom(); - - break; - - case ARG_LOCALE_MESSAGES: - if (!locale_is_valid(optarg)) { - log_error("Locale %s is not valid.", optarg); - return -EINVAL; - } - - r = free_and_strdup(&arg_locale_messages, optarg); - if (r < 0) - return log_oom(); - - break; - - case ARG_TIMEZONE: - if (!timezone_is_valid(optarg)) { - log_error("Timezone %s is not valid.", optarg); - return -EINVAL; - } - - r = free_and_strdup(&arg_timezone, optarg); - if (r < 0) - return log_oom(); - - break; - - case ARG_ROOT_PASSWORD: - r = free_and_strdup(&arg_root_password, optarg); - if (r < 0) - return log_oom(); - break; - - case ARG_ROOT_PASSWORD_FILE: - arg_root_password = mfree(arg_root_password); - - r = read_one_line_file(optarg, &arg_root_password); - if (r < 0) - return log_error_errno(r, "Failed to read %s: %m", optarg); - - break; - - case ARG_HOSTNAME: - if (!hostname_is_valid(optarg, true)) { - log_error("Host name %s is not valid.", optarg); - return -EINVAL; - } - - hostname_cleanup(optarg); - r = free_and_strdup(&arg_hostname, optarg); - if (r < 0) - return log_oom(); - - break; - - case ARG_MACHINE_ID: - if (sd_id128_from_string(optarg, &arg_machine_id) < 0) { - log_error("Failed to parse machine id %s.", optarg); - return -EINVAL; - } - - break; - - case ARG_PROMPT: - arg_prompt_locale = arg_prompt_timezone = arg_prompt_hostname = arg_prompt_root_password = true; - break; - - case ARG_PROMPT_LOCALE: - arg_prompt_locale = true; - break; - - case ARG_PROMPT_TIMEZONE: - arg_prompt_timezone = true; - break; - - case ARG_PROMPT_HOSTNAME: - arg_prompt_hostname = true; - break; - - case ARG_PROMPT_ROOT_PASSWORD: - arg_prompt_root_password = true; - break; - - case ARG_COPY: - arg_copy_locale = arg_copy_timezone = arg_copy_root_password = true; - break; - - case ARG_COPY_LOCALE: - arg_copy_locale = true; - break; - - case ARG_COPY_TIMEZONE: - arg_copy_timezone = true; - break; - - case ARG_COPY_ROOT_PASSWORD: - arg_copy_root_password = true; - break; - - case ARG_SETUP_MACHINE_ID: - - r = sd_id128_randomize(&arg_machine_id); - if (r < 0) - return log_error_errno(r, "Failed to generate randomized machine ID: %m"); - - break; - - case '?': - return -EINVAL; - - default: - assert_not_reached("Unhandled option"); - } - - return 1; -} - -int main(int argc, char *argv[]) { - int r; - - r = parse_argv(argc, argv); - if (r <= 0) - goto finish; - - log_set_target(LOG_TARGET_AUTO); - log_parse_environment(); - log_open(); - - umask(0022); - - r = process_locale(); - if (r < 0) - goto finish; - - r = process_timezone(); - if (r < 0) - goto finish; - - r = process_hostname(); - if (r < 0) - goto finish; - - r = process_machine_id(); - if (r < 0) - goto finish; - - r = process_root_password(); - if (r < 0) - goto finish; - -finish: - free(arg_root); - free(arg_locale); - free(arg_locale_messages); - free(arg_timezone); - free(arg_hostname); - string_erase(arg_root_password); - free(arg_root_password); - - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; -} diff --git a/src/grp-initutils/systemd-fsck/Makefile b/src/grp-initutils/systemd-fsck/Makefile deleted file mode 100644 index f29fba4d20..0000000000 --- a/src/grp-initutils/systemd-fsck/Makefile +++ /dev/null @@ -1,33 +0,0 @@ -# -*- Mode: makefile; indent-tabs-mode: t -*- -# -# This file is part of systemd. -# -# Copyright 2010-2012 Lennart Poettering -# Copyright 2010-2012 Kay Sievers -# Copyright 2013 Zbigniew Jędrzejewski-Szmek -# Copyright 2013 David Strauss -# Copyright 2016 Luke Shumaker -# -# systemd is free software; you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation; either version 2.1 of the License, or -# (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 -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with systemd; If not, see . -include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk -include $(topsrcdir)/build-aux/Makefile.head.mk - -rootlibexec_PROGRAMS += systemd-fsck -systemd_fsck_SOURCES = \ - src/fsck/fsck.c - -systemd_fsck_LDADD = \ - libshared.la - -include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-initutils/systemd-fsck/fsck.c b/src/grp-initutils/systemd-fsck/fsck.c deleted file mode 100644 index 4ca018763f..0000000000 --- a/src/grp-initutils/systemd-fsck/fsck.c +++ /dev/null @@ -1,487 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - Copyright 2014 Holger Hans Peter Freyther - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "basic/alloc-util.h" -#include "basic/fd-util.h" -#include "basic/fs-util.h" -#include "basic/parse-util.h" -#include "basic/path-util.h" -#include "basic/proc-cmdline.h" -#include "basic/process-util.h" -#include "basic/signal-util.h" -#include "basic/socket-util.h" -#include "basic/special.h" -#include "basic/stdio-util.h" -#include "basic/util.h" -#include "sd-bus/bus-common-errors.h" -#include "sd-bus/bus-error.h" -#include "sd-device/device-util.h" -#include "sd-device/sd-device.h" -#include "shared/bus-util.h" - -/* exit codes as defined in fsck(8) */ -enum { - FSCK_SUCCESS = 0, - FSCK_ERROR_CORRECTED = 1, - FSCK_SYSTEM_SHOULD_REBOOT = 2, - FSCK_ERRORS_LEFT_UNCORRECTED = 4, - FSCK_OPERATIONAL_ERROR = 8, - FSCK_USAGE_OR_SYNTAX_ERROR = 16, - FSCK_USER_CANCELLED = 32, - FSCK_SHARED_LIB_ERROR = 128, -}; - -static bool arg_skip = false; -static bool arg_force = false; -static bool arg_show_progress = false; -static const char *arg_repair = "-a"; - -static void start_target(const char *target, const char *mode) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; - int r; - - assert(target); - - r = bus_connect_system_systemd(&bus); - if (r < 0) { - log_error_errno(r, "Failed to get D-Bus connection: %m"); - return; - } - - log_info("Running request %s/start/replace", target); - - /* Start these units only if we can replace base.target with it */ - r = sd_bus_call_method(bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "StartUnitReplace", - &error, - NULL, - "sss", "basic.target", target, mode); - - /* Don't print a warning if we aren't called during startup */ - if (r < 0 && !sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_JOB)) - log_error("Failed to start unit: %s", bus_error_message(&error, r)); -} - -static int parse_proc_cmdline_item(const char *key, const char *value) { - int r; - - assert(key); - - if (streq(key, "fsck.mode") && value) { - - if (streq(value, "auto")) - arg_force = arg_skip = false; - else if (streq(value, "force")) - arg_force = true; - else if (streq(value, "skip")) - arg_skip = true; - else - log_warning("Invalid fsck.mode= parameter '%s'. Ignoring.", value); - - } else if (streq(key, "fsck.repair") && value) { - - if (streq(value, "preen")) - arg_repair = "-a"; - else { - r = parse_boolean(value); - if (r > 0) - arg_repair = "-y"; - else if (r == 0) - arg_repair = "-n"; - else - log_warning("Invalid fsck.repair= parameter '%s'. Ignoring.", value); - } - } - -#ifdef HAVE_SYSV_COMPAT - else if (streq(key, "fastboot") && !value) { - log_warning("Please pass 'fsck.mode=skip' rather than 'fastboot' on the kernel command line."); - arg_skip = true; - - } else if (streq(key, "forcefsck") && !value) { - log_warning("Please pass 'fsck.mode=force' rather than 'forcefsck' on the kernel command line."); - arg_force = true; - } -#endif - - return 0; -} - -static void test_files(void) { - -#ifdef HAVE_SYSV_COMPAT - if (access("/fastboot", F_OK) >= 0) { - log_error("Please pass 'fsck.mode=skip' on the kernel command line rather than creating /fastboot on the root file system."); - arg_skip = true; - } - - if (access("/forcefsck", F_OK) >= 0) { - log_error("Please pass 'fsck.mode=force' on the kernel command line rather than creating /forcefsck on the root file system."); - arg_force = true; - } -#endif - - arg_show_progress = access("/run/systemd/show-status", F_OK) >= 0; -} - -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) { - _cleanup_fclose_ FILE *console = NULL, *f = NULL; - usec_t last = 0; - bool locked = false; - int clear = 0, r; - - /* No progress pipe to process? Then we are a NOP. */ - if (fd < 0) - return 0; - - f = fdopen(fd, "re"); - if (!f) { - safe_close(fd); - return -errno; - } - - console = fopen("/dev/console", "we"); - if (!console) - return -ENOMEM; - - for (;;) { - int pass, m; - unsigned long cur, max; - _cleanup_free_ char *device = NULL; - double p; - usec_t t; - - if (fscanf(f, "%i %lu %lu %ms", &pass, &cur, &max, &device) != 4) { - - if (ferror(f)) - r = log_warning_errno(errno, "Failed to read from progress pipe: %m"); - else if (feof(f)) - r = 0; - else { - log_warning("Failed to parse progress pipe data"); - r = -EBADMSG; - } - break; - } - - /* Only show one progress counter at max */ - if (!locked) { - if (flock(fileno(console), LOCK_EX|LOCK_NB) < 0) - continue; - - locked = true; - } - - /* Only update once every 50ms */ - t = now(CLOCK_MONOTONIC); - if (last + 50 * USEC_PER_MSEC > t) - continue; - - last = t; - - p = percent(pass, cur, max); - fprintf(console, "\r%s: fsck %3.1f%% complete...\r%n", device, p, &m); - fflush(console); - - 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); - } - - return r; -} - -static int fsck_progress_socket(void) { - static const union sockaddr_union sa = { - .un.sun_family = AF_UNIX, - .un.sun_path = "/run/systemd/fsck.progress", - }; - - int fd, r; - - fd = socket(AF_UNIX, SOCK_STREAM, 0); - if (fd < 0) - return log_warning_errno(errno, "socket(): %m"); - - if (connect(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0) { - r = log_full_errno(errno == ECONNREFUSED || errno == ENOENT ? LOG_DEBUG : LOG_WARNING, - errno, "Failed to connect to progress socket %s, ignoring: %m", sa.un.sun_path); - safe_close(fd); - return r; - } - - return fd; -} - -int main(int argc, char *argv[]) { - _cleanup_close_pair_ int progress_pipe[2] = { -1, -1 }; - _cleanup_(sd_device_unrefp) sd_device *dev = NULL; - const char *device, *type; - bool root_directory; - siginfo_t status; - struct stat st; - int r; - pid_t pid; - - if (argc > 2) { - log_error("This program expects one or no arguments."); - return EXIT_FAILURE; - } - - log_set_target(LOG_TARGET_AUTO); - log_parse_environment(); - log_open(); - - umask(0022); - - r = parse_proc_cmdline(parse_proc_cmdline_item); - if (r < 0) - log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m"); - - test_files(); - - if (!arg_force && arg_skip) { - r = 0; - goto finish; - } - - if (argc > 1) { - device = argv[1]; - - if (stat(device, &st) < 0) { - r = log_error_errno(errno, "Failed to stat %s: %m", device); - goto finish; - } - - if (!S_ISBLK(st.st_mode)) { - log_error("%s is not a block device.", device); - r = -EINVAL; - goto finish; - } - - r = sd_device_new_from_devnum(&dev, 'b', st.st_rdev); - if (r < 0) { - log_error_errno(r, "Failed to detect device %s: %m", device); - goto finish; - } - - root_directory = false; - } else { - struct timespec times[2]; - - /* Find root device */ - - if (stat("/", &st) < 0) { - r = log_error_errno(errno, "Failed to stat() the root directory: %m"); - goto finish; - } - - /* Virtual root devices don't need an fsck */ - if (major(st.st_dev) == 0) { - log_debug("Root directory is virtual or btrfs, skipping check."); - r = 0; - goto finish; - } - - /* check if we are already writable */ - times[0] = st.st_atim; - times[1] = st.st_mtim; - - if (utimensat(AT_FDCWD, "/", times, 0) == 0) { - log_info("Root directory is writable, skipping check."); - r = 0; - goto finish; - } - - r = sd_device_new_from_devnum(&dev, 'b', st.st_dev); - if (r < 0) { - log_error_errno(r, "Failed to detect root device: %m"); - goto finish; - } - - r = sd_device_get_devname(dev, &device); - if (r < 0) { - log_error_errno(r, "Failed to detect device node of root directory: %m"); - goto finish; - } - - root_directory = true; - } - - r = sd_device_get_property_value(dev, "ID_FS_TYPE", &type); - if (r >= 0) { - r = fsck_exists(type); - if (r < 0) - log_warning_errno(r, "Couldn't detect if fsck.%s may be used for %s, proceeding: %m", type, device); - else if (r == 0) { - log_info("fsck.%s doesn't exist, not checking file system on %s.", type, device); - goto finish; - } - } - - if (arg_show_progress) { - if (pipe(progress_pipe) < 0) { - r = log_error_errno(errno, "pipe(): %m"); - goto finish; - } - } - - pid = fork(); - if (pid < 0) { - r = log_error_errno(errno, "fork(): %m"); - goto finish; - } - if (pid == 0) { - char dash_c[sizeof("-C")-1 + DECIMAL_STR_MAX(int) + 1]; - int progress_socket = -1; - const char *cmdline[9]; - int i = 0; - - /* Child */ - - (void) reset_all_signal_handlers(); - (void) reset_signal_mask(); - assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0); - - /* Close the reading side of the progress pipe */ - progress_pipe[0] = safe_close(progress_pipe[0]); - - /* Try to connect to a progress management daemon, if there is one */ - progress_socket = fsck_progress_socket(); - if (progress_socket >= 0) { - /* If this worked we close the progress pipe early, and just use the socket */ - progress_pipe[1] = safe_close(progress_pipe[1]); - xsprintf(dash_c, "-C%i", progress_socket); - } else if (progress_pipe[1] >= 0) { - /* Otherwise if we have the progress pipe to our own local handle, we use it */ - xsprintf(dash_c, "-C%i", progress_pipe[1]); - } else - dash_c[0] = 0; - - cmdline[i++] = "/sbin/fsck"; - cmdline[i++] = arg_repair; - cmdline[i++] = "-T"; - - /* - * Since util-linux v2.25 fsck uses /run/fsck/.lock files. - * The previous versions use flock for the device and conflict with - * udevd, see https://bugs.freedesktop.org/show_bug.cgi?id=79576#c5 - */ - cmdline[i++] = "-l"; - - if (!root_directory) - cmdline[i++] = "-M"; - - if (arg_force) - cmdline[i++] = "-f"; - - if (!isempty(dash_c)) - cmdline[i++] = dash_c; - - cmdline[i++] = device; - cmdline[i++] = NULL; - - execv(cmdline[0], (char**) cmdline); - _exit(FSCK_OPERATIONAL_ERROR); - } - - progress_pipe[1] = safe_close(progress_pipe[1]); - (void) process_progress(progress_pipe[0]); - progress_pipe[0] = -1; - - r = wait_for_terminate(pid, &status); - if (r < 0) { - log_error_errno(r, "waitid(): %m"); - goto finish; - } - - if (status.si_code != CLD_EXITED || (status.si_status & ~1)) { - - if (status.si_code == CLD_KILLED || status.si_code == CLD_DUMPED) - log_error("fsck terminated by signal %s.", signal_to_string(status.si_status)); - else if (status.si_code == CLD_EXITED) - log_error("fsck failed with error code %i.", status.si_status); - else - log_error("fsck failed due to unknown reason."); - - r = -EINVAL; - - if (status.si_code == CLD_EXITED && (status.si_status & FSCK_SYSTEM_SHOULD_REBOOT) && root_directory) - /* System should be rebooted. */ - start_target(SPECIAL_REBOOT_TARGET, "replace-irreversibly"); - else if (status.si_code == CLD_EXITED && (status.si_status & (FSCK_SYSTEM_SHOULD_REBOOT | FSCK_ERRORS_LEFT_UNCORRECTED))) - /* Some other problem */ - start_target(SPECIAL_EMERGENCY_TARGET, "replace"); - else { - log_warning("Ignoring error."); - r = 0; - } - - } else - r = 0; - - if (status.si_code == CLD_EXITED && (status.si_status & FSCK_ERROR_CORRECTED)) - (void) touch("/run/systemd/quotacheck"); - -finish: - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; -} diff --git a/src/grp-initutils/systemd-quotacheck/Makefile b/src/grp-initutils/systemd-quotacheck/Makefile deleted file mode 100644 index bb3d8dce6b..0000000000 --- a/src/grp-initutils/systemd-quotacheck/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# -*- Mode: makefile; indent-tabs-mode: t -*- -# -# This file is part of systemd. -# -# Copyright 2010-2012 Lennart Poettering -# Copyright 2010-2012 Kay Sievers -# Copyright 2013 Zbigniew Jędrzejewski-Szmek -# Copyright 2013 David Strauss -# Copyright 2016 Luke Shumaker -# -# systemd is free software; you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation; either version 2.1 of the License, or -# (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 -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with systemd; If not, see . -include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk -include $(topsrcdir)/build-aux/Makefile.head.mk - -ifneq ($(ENABLE_QUOTACHECK),) -rootlibexec_PROGRAMS += \ - systemd-quotacheck - -nodist_systemunit_DATA += \ - units/systemd-quotacheck.service - -systemd_quotacheck_SOURCES = \ - src/quotacheck/quotacheck.c - -systemd_quotacheck_LDADD = \ - libshared.la -endif # ENABLE_QUOTACHECK - -EXTRA_DIST += \ - units/systemd-quotacheck.service.in - -nodist_systemunit_DATA += \ - units/quotaon.service - -sd.CPPFLAGS += -DQUOTACHECK=\"$(QUOTACHECK)\" - -include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-initutils/systemd-quotacheck/quotacheck.c b/src/grp-initutils/systemd-quotacheck/quotacheck.c deleted file mode 100644 index 3c2292b0c4..0000000000 --- a/src/grp-initutils/systemd-quotacheck/quotacheck.c +++ /dev/null @@ -1,124 +0,0 @@ -/*** - 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 Lesser General Public License as published by - the Free Software Foundation; either version 2.1 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include - -#include "basic/proc-cmdline.h" -#include "basic/process-util.h" -#include "basic/signal-util.h" -#include "basic/string-util.h" -#include "basic/util.h" - -static bool arg_skip = false; -static bool arg_force = false; - -static int parse_proc_cmdline_item(const char *key, const char *value) { - - if (streq(key, "quotacheck.mode") && value) { - - if (streq(value, "auto")) - arg_force = arg_skip = false; - else if (streq(value, "force")) - arg_force = true; - else if (streq(value, "skip")) - arg_skip = true; - else - log_warning("Invalid quotacheck.mode= parameter '%s'. Ignoring.", value); - } - -#ifdef HAVE_SYSV_COMPAT - else if (streq(key, "forcequotacheck") && !value) { - log_warning("Please use 'quotacheck.mode=force' rather than 'forcequotacheck' on the kernel command line."); - arg_force = true; - } -#endif - - return 0; -} - -static void test_files(void) { - -#ifdef HAVE_SYSV_COMPAT - if (access("/forcequotacheck", F_OK) >= 0) { - log_error("Please pass 'quotacheck.mode=force' on the kernel command line rather than creating /forcequotacheck on the root file system."); - arg_force = true; - } -#endif -} - -int main(int argc, char *argv[]) { - - static const char * const cmdline[] = { - QUOTACHECK, - "-anug", - NULL - }; - - pid_t pid; - int r; - - if (argc > 1) { - log_error("This program takes no arguments."); - return EXIT_FAILURE; - } - - log_set_target(LOG_TARGET_AUTO); - log_parse_environment(); - log_open(); - - umask(0022); - - r = parse_proc_cmdline(parse_proc_cmdline_item); - if (r < 0) - log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m"); - - test_files(); - - if (!arg_force) { - if (arg_skip) - return EXIT_SUCCESS; - - if (access("/run/systemd/quotacheck", F_OK) < 0) - return EXIT_SUCCESS; - } - - pid = fork(); - if (pid < 0) { - log_error_errno(errno, "fork(): %m"); - return EXIT_FAILURE; - } else if (pid == 0) { - - /* Child */ - - (void) reset_all_signal_handlers(); - (void) reset_signal_mask(); - assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0); - - execv(cmdline[0], (char**) cmdline); - _exit(1); /* Operational error */ - } - - r = wait_for_terminate_and_warn("quotacheck", pid, true); - - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; -} diff --git a/src/grp-initutils/systemd-random-seed/Makefile b/src/grp-initutils/systemd-random-seed/Makefile deleted file mode 100644 index 489a8ab007..0000000000 --- a/src/grp-initutils/systemd-random-seed/Makefile +++ /dev/null @@ -1,50 +0,0 @@ -# -*- Mode: makefile; indent-tabs-mode: t -*- -# -# This file is part of systemd. -# -# Copyright 2010-2012 Lennart Poettering -# Copyright 2010-2012 Kay Sievers -# Copyright 2013 Zbigniew Jędrzejewski-Szmek -# Copyright 2013 David Strauss -# Copyright 2016 Luke Shumaker -# -# systemd is free software; you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation; either version 2.1 of the License, or -# (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 -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with systemd; If not, see . -include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk -include $(topsrcdir)/build-aux/Makefile.head.mk - -ifneq ($(ENABLE_RANDOMSEED),) -rootlibexec_PROGRAMS += \ - systemd-random-seed - -nodist_systemunit_DATA += \ - units/systemd-random-seed.service - -systemd_random_seed_SOURCES = \ - src/random-seed/random-seed.c - -systemd_random_seed_LDADD = \ - libshared.la - -SYSINIT_TARGET_WANTS += \ - systemd-random-seed.service - -endif # ENABLE_RANDOMSEED - -EXTRA_DIST += \ - units/systemd-random-seed.service.in - -sd.CPPFLAGS += -DRANDOM_SEED_DIR=\"$(localstatedir)/lib/systemd/\" -sd.CPPFLAGS += -DRANDOM_SEED=\"$(localstatedir)/lib/systemd/random-seed\" - -include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-initutils/systemd-random-seed/random-seed.c b/src/grp-initutils/systemd-random-seed/random-seed.c deleted file mode 100644 index 7fea6069f3..0000000000 --- a/src/grp-initutils/systemd-random-seed/random-seed.c +++ /dev/null @@ -1,176 +0,0 @@ -/*** - 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 Lesser General Public License as published by - the Free Software Foundation; either version 2.1 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include - -#include "basic/alloc-util.h" -#include "basic/fd-util.h" -#include "basic/io-util.h" -#include "basic/log.h" -#include "basic/mkdir.h" -#include "basic/string-util.h" -#include "basic/util.h" - -#define POOL_SIZE_MIN 512 - -int main(int argc, char *argv[]) { - _cleanup_close_ int seed_fd = -1, random_fd = -1; - _cleanup_free_ void* buf = NULL; - size_t buf_size = 0; - ssize_t k; - int r, open_rw_error; - FILE *f; - bool refresh_seed_file = true; - - if (argc != 2) { - log_error("This program requires one argument."); - return EXIT_FAILURE; - } - - log_set_target(LOG_TARGET_AUTO); - log_parse_environment(); - log_open(); - - umask(0022); - - /* Read pool size, if possible */ - f = fopen("/proc/sys/kernel/random/poolsize", "re"); - if (f) { - if (fscanf(f, "%zu", &buf_size) > 0) - /* poolsize is in bits on 2.6, but we want bytes */ - buf_size /= 8; - - fclose(f); - } - - if (buf_size <= POOL_SIZE_MIN) - buf_size = POOL_SIZE_MIN; - - buf = malloc(buf_size); - if (!buf) { - r = log_oom(); - goto finish; - } - - r = mkdir_parents_label(RANDOM_SEED, 0755); - if (r < 0) { - log_error_errno(r, "Failed to create directory " RANDOM_SEED_DIR ": %m"); - goto finish; - } - - /* When we load the seed we read it and write it to the device - * and then immediately update the saved seed with new data, - * to make sure the next boot gets seeded differently. */ - - if (streq(argv[1], "load")) { - - seed_fd = open(RANDOM_SEED, O_RDWR|O_CLOEXEC|O_NOCTTY|O_CREAT, 0600); - open_rw_error = -errno; - if (seed_fd < 0) { - refresh_seed_file = false; - - seed_fd = open(RANDOM_SEED, O_RDONLY|O_CLOEXEC|O_NOCTTY); - if (seed_fd < 0) { - bool missing = errno == ENOENT; - - log_full_errno(missing ? LOG_DEBUG : LOG_ERR, - open_rw_error, "Failed to open " RANDOM_SEED " for writing: %m"); - r = log_full_errno(missing ? LOG_DEBUG : LOG_ERR, - errno, "Failed to open " RANDOM_SEED " for reading: %m"); - if (missing) - r = 0; - - goto finish; - } - } - - random_fd = open("/dev/urandom", O_RDWR|O_CLOEXEC|O_NOCTTY, 0600); - if (random_fd < 0) { - random_fd = open("/dev/urandom", O_WRONLY|O_CLOEXEC|O_NOCTTY, 0600); - if (random_fd < 0) { - r = log_error_errno(errno, "Failed to open /dev/urandom: %m"); - goto finish; - } - } - - k = loop_read(seed_fd, buf, buf_size, false); - if (k < 0) - r = log_error_errno(k, "Failed to read seed from " RANDOM_SEED ": %m"); - else if (k == 0) { - r = 0; - log_debug("Seed file " RANDOM_SEED " not yet initialized, proceeding."); - } else { - (void) lseek(seed_fd, 0, SEEK_SET); - - r = loop_write(random_fd, buf, (size_t) k, false); - if (r < 0) - log_error_errno(r, "Failed to write seed to /dev/urandom: %m"); - } - - } else if (streq(argv[1], "save")) { - - seed_fd = open(RANDOM_SEED, O_WRONLY|O_CLOEXEC|O_NOCTTY|O_CREAT, 0600); - if (seed_fd < 0) { - r = log_error_errno(errno, "Failed to open " RANDOM_SEED ": %m"); - goto finish; - } - - random_fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC|O_NOCTTY); - if (random_fd < 0) { - r = log_error_errno(errno, "Failed to open /dev/urandom: %m"); - goto finish; - } - - } else { - log_error("Unknown verb '%s'.", argv[1]); - r = -EINVAL; - goto finish; - } - - if (refresh_seed_file) { - - /* This is just a safety measure. Given that we are root and - * most likely created the file ourselves the mode and owner - * should be correct anyway. */ - (void) fchmod(seed_fd, 0600); - (void) fchown(seed_fd, 0, 0); - - k = loop_read(random_fd, buf, buf_size, false); - if (k < 0) { - r = log_error_errno(k, "Failed to read new seed from /dev/urandom: %m"); - goto finish; - } - if (k == 0) { - log_error("Got EOF while reading from /dev/urandom."); - r = -EIO; - goto finish; - } - - r = loop_write(seed_fd, buf, (size_t) k, false); - if (r < 0) - log_error_errno(r, "Failed to write new random seed file: %m"); - } - -finish: - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; -} diff --git a/src/grp-initutils/systemd-rfkill/Makefile b/src/grp-initutils/systemd-rfkill/Makefile deleted file mode 100644 index 2b4430b712..0000000000 --- a/src/grp-initutils/systemd-rfkill/Makefile +++ /dev/null @@ -1,46 +0,0 @@ -# -*- Mode: makefile; indent-tabs-mode: t -*- -# -# This file is part of systemd. -# -# Copyright 2010-2012 Lennart Poettering -# Copyright 2010-2012 Kay Sievers -# Copyright 2013 Zbigniew Jędrzejewski-Szmek -# Copyright 2013 David Strauss -# Copyright 2016 Luke Shumaker -# -# systemd is free software; you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation; either version 2.1 of the License, or -# (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 -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with systemd; If not, see . -include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk -include $(topsrcdir)/build-aux/Makefile.head.mk - -ifneq ($(ENABLE_RFKILL),) -rootlibexec_PROGRAMS += \ - systemd-rfkill - -nodist_systemunit_DATA += \ - units/systemd-rfkill.service - -dist_systemunit_DATA += \ - units/systemd-rfkill.socket - -systemd_rfkill_SOURCES = \ - src/rfkill/rfkill.c - -systemd_rfkill_LDADD = \ - libshared.la -endif # ENABLE_RFKILL - -EXTRA_DIST += \ - units/systemd-rfkill.service.in - -include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-initutils/systemd-rfkill/rfkill.c b/src/grp-initutils/systemd-rfkill/rfkill.c deleted file mode 100644 index 86779e9ac8..0000000000 --- a/src/grp-initutils/systemd-rfkill/rfkill.c +++ /dev/null @@ -1,427 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include - -#include - -#include -#include - -#include "basic/alloc-util.h" -#include "basic/escape.h" -#include "basic/fd-util.h" -#include "basic/fileio.h" -#include "basic/io-util.h" -#include "basic/mkdir.h" -#include "basic/parse-util.h" -#include "basic/proc-cmdline.h" -#include "basic/string-table.h" -#include "basic/string-util.h" -#include "basic/util.h" -#include "shared/udev-util.h" - -#define EXIT_USEC (5 * USEC_PER_SEC) - -static const char* const rfkill_type_table[NUM_RFKILL_TYPES] = { - [RFKILL_TYPE_ALL] = "all", - [RFKILL_TYPE_WLAN] = "wlan", - [RFKILL_TYPE_BLUETOOTH] = "bluetooth", - [RFKILL_TYPE_UWB] = "uwb", - [RFKILL_TYPE_WIMAX] = "wimax", - [RFKILL_TYPE_WWAN] = "wwan", - [RFKILL_TYPE_GPS] = "gps", - [RFKILL_TYPE_FM] = "fm", - [RFKILL_TYPE_NFC] = "nfc", -}; - -DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(rfkill_type, int); - -static int find_device( - struct udev *udev, - const struct rfkill_event *event, - struct udev_device **ret) { - - _cleanup_free_ char *sysname = NULL; - struct udev_device *device; - const char *name; - - assert(udev); - assert(event); - assert(ret); - - if (asprintf(&sysname, "rfkill%i", event->idx) < 0) - return log_oom(); - - device = udev_device_new_from_subsystem_sysname(udev, "rfkill", sysname); - if (!device) - return log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_ERR, errno, "Failed to open device: %m"); - - name = udev_device_get_sysattr_value(device, "name"); - if (!name) { - log_debug("Device has no name, ignoring."); - udev_device_unref(device); - return -ENOENT; - } - - log_debug("Operating on rfkill device '%s'.", name); - - *ret = device; - return 0; -} - -static int wait_for_initialized( - struct udev *udev, - struct udev_device *device, - struct udev_device **ret) { - - _cleanup_udev_monitor_unref_ struct udev_monitor *monitor = NULL; - struct udev_device *d; - const char *sysname; - int watch_fd, r; - - assert(udev); - assert(device); - assert(ret); - - if (udev_device_get_is_initialized(device) != 0) { - *ret = udev_device_ref(device); - return 0; - } - - assert_se(sysname = udev_device_get_sysname(device)); - - /* Wait until the device is initialized, so that we can get - * access to the ID_PATH property */ - - monitor = udev_monitor_new_from_netlink(udev, "udev"); - if (!monitor) - return log_error_errno(errno, "Failed to acquire monitor: %m"); - - r = udev_monitor_filter_add_match_subsystem_devtype(monitor, "rfkill", NULL); - if (r < 0) - return log_error_errno(r, "Failed to add rfkill udev match to monitor: %m"); - - r = udev_monitor_enable_receiving(monitor); - if (r < 0) - return log_error_errno(r, "Failed to enable udev receiving: %m"); - - watch_fd = udev_monitor_get_fd(monitor); - if (watch_fd < 0) - return log_error_errno(watch_fd, "Failed to get watch fd: %m"); - - /* Check again, maybe things changed */ - d = udev_device_new_from_subsystem_sysname(udev, "rfkill", sysname); - if (!d) - return log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_ERR, errno, "Failed to open device: %m"); - - if (udev_device_get_is_initialized(d) != 0) { - *ret = d; - return 0; - } - - for (;;) { - _cleanup_udev_device_unref_ struct udev_device *t = NULL; - - r = fd_wait_for_event(watch_fd, POLLIN, USEC_INFINITY); - if (r == -EINTR) - continue; - if (r < 0) - return log_error_errno(r, "Failed to watch udev monitor: %m"); - - t = udev_monitor_receive_device(monitor); - if (!t) - continue; - - if (streq_ptr(udev_device_get_sysname(device), sysname)) { - *ret = udev_device_ref(t); - return 0; - } - } -} - -static int determine_state_file( - struct udev *udev, - const struct rfkill_event *event, - struct udev_device *d, - char **ret) { - - _cleanup_udev_device_unref_ struct udev_device *device = NULL; - const char *path_id, *type; - char *state_file; - int r; - - assert(event); - assert(d); - assert(ret); - - r = wait_for_initialized(udev, d, &device); - if (r < 0) - return r; - - assert_se(type = rfkill_type_to_string(event->type)); - - path_id = udev_device_get_property_value(device, "ID_PATH"); - if (path_id) { - _cleanup_free_ char *escaped_path_id = NULL; - - escaped_path_id = cescape(path_id); - if (!escaped_path_id) - return log_oom(); - - state_file = strjoin("/var/lib/systemd/rfkill/", escaped_path_id, ":", type, NULL); - } else - state_file = strjoin("/var/lib/systemd/rfkill/", type, NULL); - - if (!state_file) - return log_oom(); - - *ret = state_file; - return 0; -} - -static int load_state( - int rfkill_fd, - struct udev *udev, - const struct rfkill_event *event) { - - _cleanup_udev_device_unref_ struct udev_device *device = NULL; - _cleanup_free_ char *state_file = NULL, *value = NULL; - struct rfkill_event we; - ssize_t l; - int b, r; - - assert(rfkill_fd >= 0); - assert(udev); - assert(event); - - if (shall_restore_state() == 0) - return 0; - - r = find_device(udev, event, &device); - if (r < 0) - return r; - - r = determine_state_file(udev, event, device, &state_file); - if (r < 0) - return r; - - r = read_one_line_file(state_file, &value); - if (r == -ENOENT) { - /* No state file? Then save the current state */ - - r = write_string_file(state_file, one_zero(event->soft), WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC); - if (r < 0) - return log_error_errno(r, "Failed to write state file %s: %m", state_file); - - log_debug("Saved state '%s' to %s.", one_zero(event->soft), state_file); - return 0; - } - if (r < 0) - return log_error_errno(r, "Failed to read state file %s: %m", state_file); - - b = parse_boolean(value); - if (b < 0) - return log_error_errno(b, "Failed to parse state file %s: %m", state_file); - - we = (struct rfkill_event) { - .op = RFKILL_OP_CHANGE, - .idx = event->idx, - .soft = b, - }; - - l = write(rfkill_fd, &we, sizeof(we)); - if (l < 0) - return log_error_errno(errno, "Failed to restore rfkill state for %i: %m", event->idx); - if (l != sizeof(we)) { - log_error("Couldn't write rfkill event structure, too short."); - return -EIO; - } - - log_debug("Loaded state '%s' from %s.", one_zero(b), state_file); - return 0; -} - -static int save_state( - int rfkill_fd, - struct udev *udev, - const struct rfkill_event *event) { - - _cleanup_udev_device_unref_ struct udev_device *device = NULL; - _cleanup_free_ char *state_file = NULL; - int r; - - assert(rfkill_fd >= 0); - assert(udev); - assert(event); - - r = find_device(udev, event, &device); - if (r < 0) - return r; - - r = determine_state_file(udev, event, device, &state_file); - if (r < 0) - return r; - - r = write_string_file(state_file, one_zero(event->soft), WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC); - if (r < 0) - return log_error_errno(r, "Failed to write state file %s: %m", state_file); - - log_debug("Saved state '%s' to %s.", one_zero(event->soft), state_file); - return 0; -} - -int main(int argc, char *argv[]) { - _cleanup_udev_unref_ struct udev *udev = NULL; - _cleanup_close_ int rfkill_fd = -1; - bool ready = false; - int r, n; - - if (argc > 1) { - log_error("This program requires no arguments."); - return EXIT_FAILURE; - } - - log_set_target(LOG_TARGET_AUTO); - log_parse_environment(); - log_open(); - - umask(0022); - - udev = udev_new(); - if (!udev) { - r = log_oom(); - goto finish; - } - - r = mkdir_p("/var/lib/systemd/rfkill", 0755); - if (r < 0) { - log_error_errno(r, "Failed to create rfkill directory: %m"); - goto finish; - } - - n = sd_listen_fds(false); - if (n < 0) { - r = log_error_errno(n, "Failed to determine whether we got any file descriptors passed: %m"); - goto finish; - } - if (n > 1) { - log_error("Got too many file descriptors."); - r = -EINVAL; - goto finish; - } - - if (n == 0) { - rfkill_fd = open("/dev/rfkill", O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK); - if (rfkill_fd < 0) { - if (errno == ENOENT) { - log_debug_errno(errno, "Missing rfkill subsystem, or no device present, exiting."); - r = 0; - goto finish; - } - - r = log_error_errno(errno, "Failed to open /dev/rfkill: %m"); - goto finish; - } - } else { - rfkill_fd = SD_LISTEN_FDS_START; - - r = fd_nonblock(rfkill_fd, 1); - if (r < 0) { - log_error_errno(r, "Failed to make /dev/rfkill socket non-blocking: %m"); - goto finish; - } - } - - for (;;) { - struct rfkill_event event; - const char *type; - ssize_t l; - - l = read(rfkill_fd, &event, sizeof(event)); - if (l < 0) { - if (errno == EAGAIN) { - - if (!ready) { - /* Notify manager that we are - * now finished with - * processing whatever was - * queued */ - (void) sd_notify(false, "READY=1"); - ready = true; - } - - /* Hang around for a bit, maybe there's more coming */ - - r = fd_wait_for_event(rfkill_fd, POLLIN, EXIT_USEC); - if (r == -EINTR) - continue; - if (r < 0) { - log_error_errno(r, "Failed to poll() on device: %m"); - goto finish; - } - if (r > 0) - continue; - - log_debug("All events read and idle, exiting."); - break; - } - - log_error_errno(errno, "Failed to read from /dev/rfkill: %m"); - } - - if (l != RFKILL_EVENT_SIZE_V1) { - log_error("Read event structure of invalid size."); - r = -EIO; - goto finish; - } - - type = rfkill_type_to_string(event.type); - if (!type) { - log_debug("An rfkill device of unknown type %i discovered, ignoring.", event.type); - continue; - } - - switch (event.op) { - - case RFKILL_OP_ADD: - log_debug("A new rfkill device has been added with index %i and type %s.", event.idx, type); - (void) load_state(rfkill_fd, udev, &event); - break; - - case RFKILL_OP_DEL: - log_debug("An rfkill device has been removed with index %i and type %s", event.idx, type); - break; - - case RFKILL_OP_CHANGE: - log_debug("An rfkill device has changed state with index %i and type %s", event.idx, type); - (void) save_state(rfkill_fd, udev, &event); - break; - - default: - log_debug("Unknown event %i from /dev/rfkill for index %i and type %s, ignoring.", event.op, event.idx, type); - break; - } - } - - r = 0; - -finish: - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; -} diff --git a/src/grp-initutils/systemd-sleep/Makefile b/src/grp-initutils/systemd-sleep/Makefile deleted file mode 100644 index 6097d09f88..0000000000 --- a/src/grp-initutils/systemd-sleep/Makefile +++ /dev/null @@ -1,35 +0,0 @@ -# -*- Mode: makefile; indent-tabs-mode: t -*- -# -# This file is part of systemd. -# -# Copyright 2010-2012 Lennart Poettering -# Copyright 2010-2012 Kay Sievers -# Copyright 2013 Zbigniew Jędrzejewski-Szmek -# Copyright 2013 David Strauss -# Copyright 2016 Luke Shumaker -# -# systemd is free software; you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation; either version 2.1 of the License, or -# (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 -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with systemd; If not, see . -include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk -include $(topsrcdir)/build-aux/Makefile.head.mk - -rootlibexec_PROGRAMS += systemd-sleep -systemd_sleep_SOURCES = \ - src/sleep/sleep.c - -systemd_sleep_LDADD = \ - libshared.la - -sd.CPPFLAGS += -DSYSTEM_SLEEP_PATH=\"$(systemsleepdir)\" - -include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-initutils/systemd-sleep/sleep.c b/src/grp-initutils/systemd-sleep/sleep.c deleted file mode 100644 index e9e84bc893..0000000000 --- a/src/grp-initutils/systemd-sleep/sleep.c +++ /dev/null @@ -1,215 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2012 Lennart Poettering - Copyright 2013 Zbigniew Jędrzejewski-Szmek - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include - -#include - -#include "basic/def.h" -#include "basic/fd-util.h" -#include "basic/fileio.h" -#include "basic/log.h" -#include "basic/string-util.h" -#include "basic/strv.h" -#include "basic/util.h" -#include "shared/sleep-config.h" - -static char* arg_verb = NULL; - -static int write_mode(char **modes) { - int r = 0; - char **mode; - - STRV_FOREACH(mode, modes) { - int k; - - k = write_string_file("/sys/power/disk", *mode, 0); - if (k == 0) - return 0; - - log_debug_errno(k, "Failed to write '%s' to /sys/power/disk: %m", - *mode); - if (r == 0) - r = k; - } - - if (r < 0) - log_error_errno(r, "Failed to write mode to /sys/power/disk: %m"); - - return r; -} - -static int write_state(FILE **f, char **states) { - char **state; - int r = 0; - - STRV_FOREACH(state, states) { - int k; - - k = write_string_stream(*f, *state, true); - if (k == 0) - return 0; - log_debug_errno(k, "Failed to write '%s' to /sys/power/state: %m", - *state); - if (r == 0) - r = k; - - fclose(*f); - *f = fopen("/sys/power/state", "we"); - if (!*f) - return log_error_errno(errno, "Failed to open /sys/power/state: %m"); - } - - return r; -} - -static int execute(char **modes, char **states) { - - char *arguments[] = { - NULL, - (char*) "pre", - arg_verb, - NULL - }; - static const char* const dirs[] = {SYSTEM_SLEEP_PATH, NULL}; - - int r; - _cleanup_fclose_ FILE *f = NULL; - - /* This file is opened first, so that if we hit an error, - * we can abort before modifying any state. */ - f = fopen("/sys/power/state", "we"); - if (!f) - return log_error_errno(errno, "Failed to open /sys/power/state: %m"); - - /* Configure the hibernation mode */ - r = write_mode(modes); - if (r < 0) - return r; - - execute_directories(dirs, DEFAULT_TIMEOUT_USEC, arguments); - - log_struct(LOG_INFO, - LOG_MESSAGE_ID(SD_MESSAGE_SLEEP_START), - LOG_MESSAGE("Suspending system..."), - "SLEEP=%s", arg_verb, - NULL); - - r = write_state(&f, states); - if (r < 0) - return r; - - log_struct(LOG_INFO, - LOG_MESSAGE_ID(SD_MESSAGE_SLEEP_STOP), - LOG_MESSAGE("System resumed."), - "SLEEP=%s", arg_verb, - NULL); - - arguments[1] = (char*) "post"; - execute_directories(dirs, DEFAULT_TIMEOUT_USEC, arguments); - - return r; -} - -static void help(void) { - printf("%s COMMAND\n\n" - "Suspend the system, hibernate the system, or both.\n\n" - "Commands:\n" - " -h --help Show this help and exit\n" - " --version Print version string and exit\n" - " suspend Suspend the system\n" - " hibernate Hibernate the system\n" - " hybrid-sleep Both hibernate and suspend the system\n" - , program_invocation_short_name); -} - -static int parse_argv(int argc, char *argv[]) { - enum { - ARG_VERSION = 0x100, - }; - - static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, ARG_VERSION }, - {} - }; - - int c; - - assert(argc >= 0); - assert(argv); - - while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) - switch(c) { - case 'h': - help(); - return 0; /* done */ - - case ARG_VERSION: - return version(); - - case '?': - return -EINVAL; - - default: - assert_not_reached("Unhandled option"); - } - - if (argc - optind != 1) { - log_error("Usage: %s COMMAND", - program_invocation_short_name); - return -EINVAL; - } - - arg_verb = argv[optind]; - - if (!streq(arg_verb, "suspend") && - !streq(arg_verb, "hibernate") && - !streq(arg_verb, "hybrid-sleep")) { - log_error("Unknown command '%s'.", arg_verb); - return -EINVAL; - } - - return 1 /* work to do */; -} - -int main(int argc, char *argv[]) { - _cleanup_strv_free_ char **modes = NULL, **states = NULL; - int r; - - log_set_target(LOG_TARGET_AUTO); - log_parse_environment(); - log_open(); - - r = parse_argv(argc, argv); - if (r <= 0) - goto finish; - - r = parse_sleep_config(arg_verb, &modes, &states); - if (r < 0) - goto finish; - - r = execute(modes, states); - -finish: - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; -} diff --git a/src/grp-initutils/systemd-sysctl/50-default.sysctl b/src/grp-initutils/systemd-sysctl/50-default.sysctl deleted file mode 100644 index def151bb84..0000000000 --- a/src/grp-initutils/systemd-sysctl/50-default.sysctl +++ /dev/null @@ -1,40 +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 Lesser General Public License as published by -# the Free Software Foundation; either version 2.1 of the License, or -# (at your option) any later version. - -# See sysctl.d(5) and core(5) for for documentation. - -# To override settings in this file, create a local file in /etc -# (e.g. /etc/sysctl.d/90-override.conf), and put any assignments -# there. - -# System Request functionality of the kernel (SYNC) -# -# Use kernel.sysrq = 1 to allow all keys. -# See http://fedoraproject.org/wiki/QA/Sysrq for a list of values and keys. -kernel.sysrq = 16 - -# Append the PID to the core filename -kernel.core_uses_pid = 1 - -# Source route verification -net.ipv4.conf.default.rp_filter = 1 -net.ipv4.conf.all.rp_filter = 1 - -# Do not accept source routing -net.ipv4.conf.default.accept_source_route = 0 -net.ipv4.conf.all.accept_source_route = 0 - -# Promote secondary addresses when the primary address is removed -net.ipv4.conf.default.promote_secondaries = 1 -net.ipv4.conf.all.promote_secondaries = 1 - -# Fair Queue CoDel packet scheduler to fight bufferbloat -net.core.default_qdisc = fq_codel - -# Enable hard and soft link protection -fs.protected_hardlinks = 1 -fs.protected_symlinks = 1 diff --git a/src/grp-initutils/systemd-sysctl/Makefile b/src/grp-initutils/systemd-sysctl/Makefile deleted file mode 100644 index 3af80e7bef..0000000000 --- a/src/grp-initutils/systemd-sysctl/Makefile +++ /dev/null @@ -1,33 +0,0 @@ -# -*- Mode: makefile; indent-tabs-mode: t -*- -# -# This file is part of systemd. -# -# Copyright 2010-2012 Lennart Poettering -# Copyright 2010-2012 Kay Sievers -# Copyright 2013 Zbigniew Jędrzejewski-Szmek -# Copyright 2013 David Strauss -# Copyright 2016 Luke Shumaker -# -# systemd is free software; you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation; either version 2.1 of the License, or -# (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 -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with systemd; If not, see . -include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk -include $(topsrcdir)/build-aux/Makefile.head.mk - -rootlibexec_PROGRAMS += systemd-sysctl -systemd_sysctl_SOURCES = \ - src/sysctl/sysctl.c - -systemd_sysctl_LDADD = \ - libshared.la - -include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-initutils/systemd-sysctl/sysctl.c b/src/grp-initutils/systemd-sysctl/sysctl.c deleted file mode 100644 index c44aa0aabb..0000000000 --- a/src/grp-initutils/systemd-sysctl/sysctl.c +++ /dev/null @@ -1,287 +0,0 @@ -/*** - 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 Lesser General Public License as published by - the Free Software Foundation; either version 2.1 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include -#include - -#include "basic/conf-files.h" -#include "basic/def.h" -#include "basic/fd-util.h" -#include "basic/fileio.h" -#include "basic/hashmap.h" -#include "basic/log.h" -#include "basic/path-util.h" -#include "basic/string-util.h" -#include "basic/strv.h" -#include "basic/util.h" -#include "shared/sysctl-util.h" - -static char **arg_prefixes = NULL; - -static const char conf_file_dirs[] = CONF_PATHS_NULSTR("sysctl.d"); - -static int apply_all(Hashmap *sysctl_options) { - char *property, *value; - Iterator i; - int r = 0; - - HASHMAP_FOREACH_KEY(value, property, sysctl_options, i) { - int k; - - k = sysctl_write(property, value); - if (k < 0) { - log_full_errno(k == -ENOENT ? LOG_INFO : LOG_WARNING, k, - "Couldn't write '%s' to '%s', ignoring: %m", value, property); - - if (r == 0 && k != -ENOENT) - r = k; - } - } - - return r; -} - -static int parse_file(Hashmap *sysctl_options, const char *path, bool ignore_enoent) { - _cleanup_fclose_ FILE *f = NULL; - int r; - - assert(path); - - r = search_and_fopen_nulstr(path, "re", NULL, conf_file_dirs, &f); - if (r < 0) { - if (ignore_enoent && r == -ENOENT) - return 0; - - return log_error_errno(r, "Failed to open file '%s', ignoring: %m", path); - } - - log_debug("Parsing %s", path); - while (!feof(f)) { - char l[LINE_MAX], *p, *value, *new_value, *property, *existing; - void *v; - int k; - - if (!fgets(l, sizeof(l), f)) { - if (feof(f)) - break; - - return log_error_errno(errno, "Failed to read file '%s', ignoring: %m", path); - } - - p = strstrip(l); - if (!*p) - continue; - - if (strchr(COMMENTS "\n", *p)) - continue; - - value = strchr(p, '='); - if (!value) { - log_error("Line is not an assignment in file '%s': %s", path, value); - - if (r == 0) - r = -EINVAL; - continue; - } - - *value = 0; - value++; - - p = sysctl_normalize(strstrip(p)); - value = strstrip(value); - - if (!strv_isempty(arg_prefixes)) { - char **i, *t; - STRV_FOREACH(i, arg_prefixes) { - t = path_startswith(*i, "/proc/sys/"); - if (t == NULL) - t = *i; - if (path_startswith(p, t)) - goto found; - } - /* not found */ - continue; - } - -found: - existing = hashmap_get2(sysctl_options, p, &v); - if (existing) { - if (streq(value, existing)) - continue; - - log_debug("Overwriting earlier assignment of %s in file '%s'.", p, path); - free(hashmap_remove(sysctl_options, p)); - free(v); - } - - property = strdup(p); - if (!property) - return log_oom(); - - new_value = strdup(value); - if (!new_value) { - free(property); - return log_oom(); - } - - k = hashmap_put(sysctl_options, property, new_value); - if (k < 0) { - log_error_errno(k, "Failed to add sysctl variable %s to hashmap: %m", property); - free(property); - free(new_value); - return k; - } - } - - return r; -} - -static void help(void) { - printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n" - "Applies kernel sysctl settings.\n\n" - " -h --help Show this help\n" - " --version Show package version\n" - " --prefix=PATH Only apply rules with the specified prefix\n" - , program_invocation_short_name); -} - -static int parse_argv(int argc, char *argv[]) { - - enum { - ARG_VERSION = 0x100, - ARG_PREFIX - }; - - static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, ARG_VERSION }, - { "prefix", required_argument, NULL, ARG_PREFIX }, - {} - }; - - int c; - - assert(argc >= 0); - assert(argv); - - while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) - - switch (c) { - - case 'h': - help(); - return 0; - - case ARG_VERSION: - return version(); - - case ARG_PREFIX: { - char *p; - - /* We used to require people to specify absolute paths - * in /proc/sys in the past. This is kinda useless, but - * we need to keep compatibility. We now support any - * sysctl name available. */ - sysctl_normalize(optarg); - - if (startswith(optarg, "/proc/sys")) - p = strdup(optarg); - else - p = strappend("/proc/sys/", optarg); - if (!p) - return log_oom(); - - if (strv_consume(&arg_prefixes, p) < 0) - return log_oom(); - - break; - } - - case '?': - return -EINVAL; - - default: - assert_not_reached("Unhandled option"); - } - - return 1; -} - -int main(int argc, char *argv[]) { - int r = 0, k; - Hashmap *sysctl_options; - - 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); - - sysctl_options = hashmap_new(&string_hash_ops); - if (!sysctl_options) { - r = log_oom(); - goto finish; - } - - r = 0; - - if (argc > optind) { - int i; - - for (i = optind; i < argc; i++) { - k = parse_file(sysctl_options, argv[i], false); - if (k < 0 && r == 0) - r = k; - } - } else { - _cleanup_strv_free_ char **files = NULL; - char **f; - - r = conf_files_list_nulstr(&files, ".conf", NULL, conf_file_dirs); - if (r < 0) { - log_error_errno(r, "Failed to enumerate sysctl.d files: %m"); - goto finish; - } - - STRV_FOREACH(f, files) { - k = parse_file(sysctl_options, *f, true); - if (k < 0 && r == 0) - r = k; - } - } - - k = apply_all(sysctl_options); - if (k < 0 && r == 0) - r = k; - -finish: - hashmap_free_free_free(sysctl_options); - strv_free(arg_prefixes); - - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; -} diff --git a/src/grp-initutils/systemd-sysusers/Makefile b/src/grp-initutils/systemd-sysusers/Makefile deleted file mode 100644 index 4753ca75d0..0000000000 --- a/src/grp-initutils/systemd-sysusers/Makefile +++ /dev/null @@ -1,61 +0,0 @@ -# -*- Mode: makefile; indent-tabs-mode: t -*- -# -# This file is part of systemd. -# -# Copyright 2010-2012 Lennart Poettering -# Copyright 2010-2012 Kay Sievers -# Copyright 2013 Zbigniew Jędrzejewski-Szmek -# Copyright 2013 David Strauss -# Copyright 2016 Luke Shumaker -# -# systemd is free software; you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation; either version 2.1 of the License, or -# (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 -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with systemd; If not, see . -include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk -include $(topsrcdir)/build-aux/Makefile.head.mk - -ifneq ($(ENABLE_SYSUSERS),) -systemd_sysusers_SOURCES = \ - src/sysusers/sysusers.c - -systemd_sysusers_LDADD = \ - libshared.la - -rootbin_PROGRAMS += \ - systemd-sysusers - -nodist_systemunit_DATA += \ - units/systemd-sysusers.service - -SYSINIT_TARGET_WANTS += \ - systemd-sysusers.service - -nodist_sysusers_DATA = \ - sysusers.d/systemd.conf \ - sysusers.d/basic.conf - -ifneq ($(HAVE_REMOTE),) -nodist_sysusers_DATA += \ - sysusers.d/systemd-remote.conf -endif # HAVE_REMOTE - -INSTALL_DIRS += \ - $(sysusersdir) -endif # ENABLE_SYSUSERS - -EXTRA_DIST += \ - units/systemd-sysusers.service.in \ - sysusers.d/systemd.conf.m4 \ - sysusers.d/systemd-remote.conf.m4 \ - sysusers.d/basic.conf.in - -include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-initutils/systemd-sysusers/basic.sysusers.in b/src/grp-initutils/systemd-sysusers/basic.sysusers.in deleted file mode 100644 index b2dc5ebd4f..0000000000 --- a/src/grp-initutils/systemd-sysusers/basic.sysusers.in +++ /dev/null @@ -1,36 +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 Lesser General Public License as published by -# the Free Software Foundation; either version 2.1 of the License, or -# (at your option) any later version. - -# The superuser -u root 0 "Super User" /root - -# The nobody user for NFS file systems -u nobody 65534 "Nobody" - - -# Administrator group: can *see* more than normal users -g adm - - - - -# Administrator group: can *do* more than normal users -g wheel - - - - -# Access to certain kernel and userspace facilities -g kmem - - - -g tty @TTY_GID@ - - -g utmp - - - - -# Hardware access groups -g audio - - - -g cdrom - - - -g dialout - - - -g disk - - - -g input - - - -g lp - - - -g tape - - - -g video - - - - -# Default group for normal users -g users - - - diff --git a/src/grp-initutils/systemd-sysusers/sysusers.c b/src/grp-initutils/systemd-sysusers/sysusers.c deleted file mode 100644 index a71d777720..0000000000 --- a/src/grp-initutils/systemd-sysusers/sysusers.c +++ /dev/null @@ -1,1919 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include - -#include "basic/alloc-util.h" -#include "basic/conf-files.h" -#include "basic/copy.h" -#include "basic/def.h" -#include "basic/fd-util.h" -#include "basic/fileio-label.h" -#include "basic/formats-util.h" -#include "basic/hashmap.h" -#include "basic/path-util.h" -#include "basic/selinux-util.h" -#include "basic/smack-util.h" -#include "basic/string-util.h" -#include "basic/strv.h" -#include "basic/user-util.h" -#include "basic/utf8.h" -#include "basic/util.h" -#include "shared/specifier.h" -#include "shared/uid-range.h" - -typedef enum ItemType { - ADD_USER = 'u', - ADD_GROUP = 'g', - ADD_MEMBER = 'm', - ADD_RANGE = 'r', -} ItemType; -typedef struct Item { - ItemType type; - - char *name; - char *uid_path; - char *gid_path; - char *description; - char *home; - - gid_t gid; - uid_t uid; - - bool gid_set:1; - bool uid_set:1; - - bool todo_user:1; - bool todo_group:1; -} Item; - -static char *arg_root = NULL; - -static const char conf_file_dirs[] = CONF_PATHS_NULSTR("sysusers.d"); - -static Hashmap *users = NULL, *groups = NULL; -static Hashmap *todo_uids = NULL, *todo_gids = NULL; -static Hashmap *members = NULL; - -static Hashmap *database_uid = NULL, *database_user = NULL; -static Hashmap *database_gid = NULL, *database_group = NULL; - -static uid_t search_uid = UID_INVALID; -static UidRange *uid_range = NULL; -static unsigned n_uid_range = 0; - -static int load_user_database(void) { - _cleanup_fclose_ FILE *f = NULL; - const char *passwd_path; - struct passwd *pw; - int r; - - passwd_path = prefix_roota(arg_root, "/etc/passwd"); - f = fopen(passwd_path, "re"); - if (!f) - return errno == ENOENT ? 0 : -errno; - - r = hashmap_ensure_allocated(&database_user, &string_hash_ops); - if (r < 0) - return r; - - r = hashmap_ensure_allocated(&database_uid, NULL); - if (r < 0) - return r; - - errno = 0; - while ((pw = fgetpwent(f))) { - char *n; - int k, q; - - n = strdup(pw->pw_name); - if (!n) - return -ENOMEM; - - k = hashmap_put(database_user, n, UID_TO_PTR(pw->pw_uid)); - if (k < 0 && k != -EEXIST) { - free(n); - return k; - } - - q = hashmap_put(database_uid, UID_TO_PTR(pw->pw_uid), n); - if (q < 0 && q != -EEXIST) { - if (k < 0) - free(n); - return q; - } - - if (q < 0 && k < 0) - free(n); - - errno = 0; - } - if (!IN_SET(errno, 0, ENOENT)) - return -errno; - - return 0; -} - -static int load_group_database(void) { - _cleanup_fclose_ FILE *f = NULL; - const char *group_path; - struct group *gr; - int r; - - group_path = prefix_roota(arg_root, "/etc/group"); - f = fopen(group_path, "re"); - if (!f) - return errno == ENOENT ? 0 : -errno; - - r = hashmap_ensure_allocated(&database_group, &string_hash_ops); - if (r < 0) - return r; - - r = hashmap_ensure_allocated(&database_gid, NULL); - if (r < 0) - return r; - - errno = 0; - while ((gr = fgetgrent(f))) { - char *n; - int k, q; - - n = strdup(gr->gr_name); - if (!n) - return -ENOMEM; - - k = hashmap_put(database_group, n, GID_TO_PTR(gr->gr_gid)); - if (k < 0 && k != -EEXIST) { - free(n); - return k; - } - - q = hashmap_put(database_gid, GID_TO_PTR(gr->gr_gid), n); - if (q < 0 && q != -EEXIST) { - if (k < 0) - free(n); - return q; - } - - if (q < 0 && k < 0) - free(n); - - errno = 0; - } - if (!IN_SET(errno, 0, ENOENT)) - return -errno; - - return 0; -} - -static int make_backup(const char *target, const char *x) { - _cleanup_close_ int src = -1; - _cleanup_fclose_ FILE *dst = NULL; - char *backup, *temp; - struct timespec ts[2]; - struct stat st; - int r; - - src = open(x, O_RDONLY|O_CLOEXEC|O_NOCTTY); - if (src < 0) { - if (errno == ENOENT) /* No backup necessary... */ - return 0; - - return -errno; - } - - if (fstat(src, &st) < 0) - return -errno; - - r = fopen_temporary_label(target, x, &dst, &temp); - if (r < 0) - return r; - - r = copy_bytes(src, fileno(dst), (uint64_t) -1, true); - if (r < 0) - goto fail; - - /* Don't fail on chmod() or chown(). If it stays owned by us - * and/or unreadable by others, then it isn't too bad... */ - - backup = strjoina(x, "-"); - - /* Copy over the access mask */ - if (fchmod(fileno(dst), st.st_mode & 07777) < 0) - log_warning_errno(errno, "Failed to change mode on %s: %m", backup); - - if (fchown(fileno(dst), st.st_uid, st.st_gid)< 0) - log_warning_errno(errno, "Failed to change ownership of %s: %m", backup); - - ts[0] = st.st_atim; - ts[1] = st.st_mtim; - if (futimens(fileno(dst), ts) < 0) - log_warning_errno(errno, "Failed to fix access and modification time of %s: %m", backup); - - if (rename(temp, backup) < 0) - goto fail; - - return 0; - -fail: - unlink(temp); - return r; -} - -static int putgrent_with_members(const struct group *gr, FILE *group) { - char **a; - - assert(gr); - assert(group); - - a = hashmap_get(members, gr->gr_name); - if (a) { - _cleanup_strv_free_ char **l = NULL; - bool added = false; - char **i; - - l = strv_copy(gr->gr_mem); - if (!l) - return -ENOMEM; - - STRV_FOREACH(i, a) { - if (strv_find(l, *i)) - continue; - - if (strv_extend(&l, *i) < 0) - return -ENOMEM; - - added = true; - } - - if (added) { - struct group t; - - strv_uniq(l); - strv_sort(l); - - t = *gr; - t.gr_mem = l; - - errno = 0; - if (putgrent(&t, group) != 0) - return errno > 0 ? -errno : -EIO; - - return 1; - } - } - - errno = 0; - if (putgrent(gr, group) != 0) - return errno > 0 ? -errno : -EIO; - - return 0; -} - -static int putsgent_with_members(const struct sgrp *sg, FILE *gshadow) { - char **a; - - assert(sg); - assert(gshadow); - - a = hashmap_get(members, sg->sg_namp); - if (a) { - _cleanup_strv_free_ char **l = NULL; - bool added = false; - char **i; - - l = strv_copy(sg->sg_mem); - if (!l) - return -ENOMEM; - - STRV_FOREACH(i, a) { - if (strv_find(l, *i)) - continue; - - if (strv_extend(&l, *i) < 0) - return -ENOMEM; - - added = true; - } - - if (added) { - struct sgrp t; - - strv_uniq(l); - strv_sort(l); - - t = *sg; - t.sg_mem = l; - - errno = 0; - if (putsgent(&t, gshadow) != 0) - return errno > 0 ? -errno : -EIO; - - return 1; - } - } - - errno = 0; - if (putsgent(sg, gshadow) != 0) - return errno > 0 ? -errno : -EIO; - - return 0; -} - -static int sync_rights(FILE *from, FILE *to) { - struct stat st; - - if (fstat(fileno(from), &st) < 0) - return -errno; - - if (fchmod(fileno(to), st.st_mode & 07777) < 0) - return -errno; - - if (fchown(fileno(to), st.st_uid, st.st_gid) < 0) - return -errno; - - return 0; -} - -static int rename_and_apply_smack(const char *temp_path, const char *dest_path) { - int r = 0; - if (rename(temp_path, dest_path) < 0) - return -errno; - -#ifdef SMACK_RUN_LABEL - r = mac_smack_apply(dest_path, SMACK_ATTR_ACCESS, SMACK_FLOOR_LABEL); - if (r < 0) - return r; -#endif - return r; -} - -static int write_files(void) { - - _cleanup_fclose_ FILE *passwd = NULL, *group = NULL, *shadow = NULL, *gshadow = NULL; - _cleanup_free_ char *passwd_tmp = NULL, *group_tmp = NULL, *shadow_tmp = NULL, *gshadow_tmp = NULL; - const char *passwd_path = NULL, *group_path = NULL, *shadow_path = NULL, *gshadow_path = NULL; - bool group_changed = false; - Iterator iterator; - Item *i; - int r; - - if (hashmap_size(todo_gids) > 0 || hashmap_size(members) > 0) { - _cleanup_fclose_ FILE *original = NULL; - - /* First we update the actual group list file */ - group_path = prefix_roota(arg_root, "/etc/group"); - r = fopen_temporary_label("/etc/group", group_path, &group, &group_tmp); - if (r < 0) - goto finish; - - original = fopen(group_path, "re"); - if (original) { - struct group *gr; - - r = sync_rights(original, group); - if (r < 0) - goto finish; - - errno = 0; - while ((gr = fgetgrent(original))) { - /* Safety checks against name and GID - * collisions. Normally, this should - * be unnecessary, but given that we - * look at the entries anyway here, - * let's make an extra verification - * step that we don't generate - * duplicate entries. */ - - i = hashmap_get(groups, gr->gr_name); - if (i && i->todo_group) { - log_error("%s: Group \"%s\" already exists.", group_path, gr->gr_name); - r = -EEXIST; - goto finish; - } - - if (hashmap_contains(todo_gids, GID_TO_PTR(gr->gr_gid))) { - log_error("%s: Detected collision for GID " GID_FMT ".", group_path, gr->gr_gid); - r = -EEXIST; - goto finish; - } - - r = putgrent_with_members(gr, group); - if (r < 0) - goto finish; - if (r > 0) - group_changed = true; - - errno = 0; - } - if (!IN_SET(errno, 0, ENOENT)) { - r = -errno; - goto finish; - } - - } else if (errno != ENOENT) { - r = -errno; - goto finish; - } else if (fchmod(fileno(group), 0644) < 0) { - r = -errno; - goto finish; - } - - HASHMAP_FOREACH(i, todo_gids, iterator) { - struct group n = { - .gr_name = i->name, - .gr_gid = i->gid, - .gr_passwd = (char*) "x", - }; - - r = putgrent_with_members(&n, group); - if (r < 0) - goto finish; - - group_changed = true; - } - - r = fflush_and_check(group); - if (r < 0) - goto finish; - - if (original) { - fclose(original); - original = NULL; - } - - /* OK, now also update the shadow file for the group list */ - gshadow_path = prefix_roota(arg_root, "/etc/gshadow"); - r = fopen_temporary_label("/etc/gshadow", gshadow_path, &gshadow, &gshadow_tmp); - if (r < 0) - goto finish; - - original = fopen(gshadow_path, "re"); - if (original) { - struct sgrp *sg; - - r = sync_rights(original, gshadow); - if (r < 0) - goto finish; - - errno = 0; - while ((sg = fgetsgent(original))) { - - i = hashmap_get(groups, sg->sg_namp); - if (i && i->todo_group) { - log_error("%s: Group \"%s\" already exists.", gshadow_path, sg->sg_namp); - r = -EEXIST; - goto finish; - } - - r = putsgent_with_members(sg, gshadow); - if (r < 0) - goto finish; - if (r > 0) - group_changed = true; - - errno = 0; - } - if (!IN_SET(errno, 0, ENOENT)) { - r = -errno; - goto finish; - } - - } else if (errno != ENOENT) { - r = -errno; - goto finish; - } else if (fchmod(fileno(gshadow), 0000) < 0) { - r = -errno; - goto finish; - } - - HASHMAP_FOREACH(i, todo_gids, iterator) { - struct sgrp n = { - .sg_namp = i->name, - .sg_passwd = (char*) "!!", - }; - - r = putsgent_with_members(&n, gshadow); - if (r < 0) - goto finish; - - group_changed = true; - } - - r = fflush_and_check(gshadow); - if (r < 0) - goto finish; - } - - if (hashmap_size(todo_uids) > 0) { - _cleanup_fclose_ FILE *original = NULL; - long lstchg; - - /* First we update the user database itself */ - passwd_path = prefix_roota(arg_root, "/etc/passwd"); - r = fopen_temporary_label("/etc/passwd", passwd_path, &passwd, &passwd_tmp); - if (r < 0) - goto finish; - - original = fopen(passwd_path, "re"); - if (original) { - struct passwd *pw; - - r = sync_rights(original, passwd); - if (r < 0) - goto finish; - - errno = 0; - while ((pw = fgetpwent(original))) { - - i = hashmap_get(users, pw->pw_name); - if (i && i->todo_user) { - log_error("%s: User \"%s\" already exists.", passwd_path, pw->pw_name); - r = -EEXIST; - goto finish; - } - - if (hashmap_contains(todo_uids, UID_TO_PTR(pw->pw_uid))) { - log_error("%s: Detected collision for UID " UID_FMT ".", passwd_path, pw->pw_uid); - r = -EEXIST; - goto finish; - } - - errno = 0; - if (putpwent(pw, passwd) < 0) { - r = errno ? -errno : -EIO; - goto finish; - } - - errno = 0; - } - if (!IN_SET(errno, 0, ENOENT)) { - r = -errno; - goto finish; - } - - } else if (errno != ENOENT) { - r = -errno; - goto finish; - } else if (fchmod(fileno(passwd), 0644) < 0) { - r = -errno; - goto finish; - } - - HASHMAP_FOREACH(i, todo_uids, iterator) { - struct passwd n = { - .pw_name = i->name, - .pw_uid = i->uid, - .pw_gid = i->gid, - .pw_gecos = i->description, - - /* "x" means the password is stored in - * the shadow file */ - .pw_passwd = (char*) "x", - - /* We default to the root directory as home */ - .pw_dir = i->home ? i->home : (char*) "/", - - /* Initialize the shell to nologin, - * with one exception: for root we - * patch in something special */ - .pw_shell = i->uid == 0 ? (char*) "/bin/sh" : (char*) "/sbin/nologin", - }; - - errno = 0; - if (putpwent(&n, passwd) != 0) { - r = errno ? -errno : -EIO; - goto finish; - } - } - - r = fflush_and_check(passwd); - if (r < 0) - goto finish; - - if (original) { - fclose(original); - original = NULL; - } - - /* The we update the shadow database */ - shadow_path = prefix_roota(arg_root, "/etc/shadow"); - r = fopen_temporary_label("/etc/shadow", shadow_path, &shadow, &shadow_tmp); - if (r < 0) - goto finish; - - lstchg = (long) (now(CLOCK_REALTIME) / USEC_PER_DAY); - - original = fopen(shadow_path, "re"); - if (original) { - struct spwd *sp; - - r = sync_rights(original, shadow); - if (r < 0) - goto finish; - - errno = 0; - while ((sp = fgetspent(original))) { - - i = hashmap_get(users, sp->sp_namp); - if (i && i->todo_user) { - /* we will update the existing entry */ - sp->sp_lstchg = lstchg; - - /* only the /etc/shadow stage is left, so we can - * safely remove the item from the todo set */ - i->todo_user = false; - hashmap_remove(todo_uids, UID_TO_PTR(i->uid)); - } - - errno = 0; - if (putspent(sp, shadow) < 0) { - r = errno ? -errno : -EIO; - goto finish; - } - - errno = 0; - } - if (!IN_SET(errno, 0, ENOENT)) { - r = -errno; - goto finish; - } - } else if (errno != ENOENT) { - r = -errno; - goto finish; - } else if (fchmod(fileno(shadow), 0000) < 0) { - r = -errno; - goto finish; - } - - HASHMAP_FOREACH(i, todo_uids, iterator) { - struct spwd n = { - .sp_namp = i->name, - .sp_pwdp = (char*) "!!", - .sp_lstchg = lstchg, - .sp_min = -1, - .sp_max = -1, - .sp_warn = -1, - .sp_inact = -1, - .sp_expire = -1, - .sp_flag = (unsigned long) -1, /* this appears to be what everybody does ... */ - }; - - errno = 0; - if (putspent(&n, shadow) != 0) { - r = errno ? -errno : -EIO; - goto finish; - } - } - - r = fflush_and_check(shadow); - if (r < 0) - goto finish; - } - - /* Make a backup of the old files */ - if (group_changed) { - if (group) { - r = make_backup("/etc/group", group_path); - if (r < 0) - goto finish; - } - if (gshadow) { - r = make_backup("/etc/gshadow", gshadow_path); - if (r < 0) - goto finish; - } - } - - if (passwd) { - r = make_backup("/etc/passwd", passwd_path); - if (r < 0) - goto finish; - } - if (shadow) { - r = make_backup("/etc/shadow", shadow_path); - if (r < 0) - goto finish; - } - - /* And make the new files count */ - if (group_changed) { - if (group) { - r = rename_and_apply_smack(group_tmp, group_path); - if (r < 0) - goto finish; - - group_tmp = mfree(group_tmp); - } - if (gshadow) { - r = rename_and_apply_smack(gshadow_tmp, gshadow_path); - if (r < 0) - goto finish; - - gshadow_tmp = mfree(gshadow_tmp); - } - } - - if (passwd) { - r = rename_and_apply_smack(passwd_tmp, passwd_path); - if (r < 0) - goto finish; - - passwd_tmp = mfree(passwd_tmp); - } - if (shadow) { - r = rename_and_apply_smack(shadow_tmp, shadow_path); - if (r < 0) - goto finish; - - shadow_tmp = mfree(shadow_tmp); - } - - r = 0; - -finish: - if (passwd_tmp) - unlink(passwd_tmp); - if (shadow_tmp) - unlink(shadow_tmp); - if (group_tmp) - unlink(group_tmp); - if (gshadow_tmp) - unlink(gshadow_tmp); - - return r; -} - -static int uid_is_ok(uid_t uid, const char *name) { - struct passwd *p; - struct group *g; - const char *n; - Item *i; - - /* Let's see if we already have assigned the UID a second time */ - if (hashmap_get(todo_uids, UID_TO_PTR(uid))) - return 0; - - /* Try to avoid using uids that are already used by a group - * that doesn't have the same name as our new user. */ - i = hashmap_get(todo_gids, GID_TO_PTR(uid)); - if (i && !streq(i->name, name)) - return 0; - - /* Let's check the files directly */ - if (hashmap_contains(database_uid, UID_TO_PTR(uid))) - return 0; - - n = hashmap_get(database_gid, GID_TO_PTR(uid)); - if (n && !streq(n, name)) - return 0; - - /* Let's also check via NSS, to avoid UID clashes over LDAP and such, just in case */ - if (!arg_root) { - errno = 0; - p = getpwuid(uid); - if (p) - return 0; - if (!IN_SET(errno, 0, ENOENT)) - return -errno; - - errno = 0; - g = getgrgid((gid_t) uid); - if (g) { - if (!streq(g->gr_name, name)) - return 0; - } else if (!IN_SET(errno, 0, ENOENT)) - return -errno; - } - - return 1; -} - -static int root_stat(const char *p, struct stat *st) { - const char *fix; - - fix = prefix_roota(arg_root, p); - if (stat(fix, st) < 0) - return -errno; - - return 0; -} - -static int read_id_from_file(Item *i, uid_t *_uid, gid_t *_gid) { - struct stat st; - bool found_uid = false, found_gid = false; - uid_t uid = 0; - gid_t gid = 0; - - assert(i); - - /* First, try to get the gid directly */ - if (_gid && i->gid_path && root_stat(i->gid_path, &st) >= 0) { - gid = st.st_gid; - found_gid = true; - } - - /* Then, try to get the uid directly */ - if ((_uid || (_gid && !found_gid)) - && i->uid_path - && root_stat(i->uid_path, &st) >= 0) { - - uid = st.st_uid; - found_uid = true; - - /* If we need the gid, but had no success yet, also derive it from the uid path */ - if (_gid && !found_gid) { - gid = st.st_gid; - found_gid = true; - } - } - - /* If that didn't work yet, then let's reuse the gid as uid */ - if (_uid && !found_uid && i->gid_path) { - - if (found_gid) { - uid = (uid_t) gid; - found_uid = true; - } else if (root_stat(i->gid_path, &st) >= 0) { - uid = (uid_t) st.st_gid; - found_uid = true; - } - } - - if (_uid) { - if (!found_uid) - return 0; - - *_uid = uid; - } - - if (_gid) { - if (!found_gid) - return 0; - - *_gid = gid; - } - - return 1; -} - -static int add_user(Item *i) { - void *z; - int r; - - assert(i); - - /* Check the database directly */ - z = hashmap_get(database_user, i->name); - if (z) { - log_debug("User %s already exists.", i->name); - i->uid = PTR_TO_UID(z); - i->uid_set = true; - return 0; - } - - if (!arg_root) { - struct passwd *p; - - /* Also check NSS */ - errno = 0; - p = getpwnam(i->name); - if (p) { - log_debug("User %s already exists.", i->name); - i->uid = p->pw_uid; - i->uid_set = true; - - r = free_and_strdup(&i->description, p->pw_gecos); - if (r < 0) - return log_oom(); - - return 0; - } - if (!IN_SET(errno, 0, ENOENT)) - return log_error_errno(errno, "Failed to check if user %s already exists: %m", i->name); - } - - /* Try to use the suggested numeric uid */ - if (i->uid_set) { - r = uid_is_ok(i->uid, i->name); - if (r < 0) - return log_error_errno(r, "Failed to verify uid " UID_FMT ": %m", i->uid); - if (r == 0) { - log_debug("Suggested user ID " UID_FMT " for %s already used.", i->uid, i->name); - i->uid_set = false; - } - } - - /* If that didn't work, try to read it from the specified path */ - if (!i->uid_set) { - uid_t c; - - if (read_id_from_file(i, &c, NULL) > 0) { - - if (c <= 0 || !uid_range_contains(uid_range, n_uid_range, c)) - log_debug("User ID " UID_FMT " of file not suitable for %s.", c, i->name); - else { - r = uid_is_ok(c, i->name); - if (r < 0) - return log_error_errno(r, "Failed to verify uid " UID_FMT ": %m", i->uid); - else if (r > 0) { - i->uid = c; - i->uid_set = true; - } else - log_debug("User ID " UID_FMT " of file for %s is already used.", c, i->name); - } - } - } - - /* Otherwise, try to reuse the group ID */ - if (!i->uid_set && i->gid_set) { - r = uid_is_ok((uid_t) i->gid, i->name); - if (r < 0) - return log_error_errno(r, "Failed to verify uid " UID_FMT ": %m", i->uid); - if (r > 0) { - i->uid = (uid_t) i->gid; - i->uid_set = true; - } - } - - /* And if that didn't work either, let's try to find a free one */ - if (!i->uid_set) { - for (;;) { - r = uid_range_next_lower(uid_range, n_uid_range, &search_uid); - if (r < 0) { - log_error("No free user ID available for %s.", i->name); - return r; - } - - r = uid_is_ok(search_uid, i->name); - if (r < 0) - return log_error_errno(r, "Failed to verify uid " UID_FMT ": %m", i->uid); - else if (r > 0) - break; - } - - i->uid_set = true; - i->uid = search_uid; - } - - r = hashmap_ensure_allocated(&todo_uids, NULL); - if (r < 0) - return log_oom(); - - r = hashmap_put(todo_uids, UID_TO_PTR(i->uid), i); - if (r < 0) - return log_oom(); - - i->todo_user = true; - log_info("Creating user %s (%s) with uid " UID_FMT " and gid " GID_FMT ".", i->name, strna(i->description), i->uid, i->gid); - - return 0; -} - -static int gid_is_ok(gid_t gid) { - struct group *g; - struct passwd *p; - - if (hashmap_get(todo_gids, GID_TO_PTR(gid))) - return 0; - - /* Avoid reusing gids that are already used by a different user */ - if (hashmap_get(todo_uids, UID_TO_PTR(gid))) - return 0; - - if (hashmap_contains(database_gid, GID_TO_PTR(gid))) - return 0; - - if (hashmap_contains(database_uid, UID_TO_PTR(gid))) - return 0; - - if (!arg_root) { - errno = 0; - g = getgrgid(gid); - if (g) - return 0; - if (!IN_SET(errno, 0, ENOENT)) - return -errno; - - errno = 0; - p = getpwuid((uid_t) gid); - if (p) - return 0; - if (!IN_SET(errno, 0, ENOENT)) - return -errno; - } - - return 1; -} - -static int add_group(Item *i) { - void *z; - int r; - - assert(i); - - /* Check the database directly */ - z = hashmap_get(database_group, i->name); - if (z) { - log_debug("Group %s already exists.", i->name); - i->gid = PTR_TO_GID(z); - i->gid_set = true; - return 0; - } - - /* Also check NSS */ - if (!arg_root) { - struct group *g; - - errno = 0; - g = getgrnam(i->name); - if (g) { - log_debug("Group %s already exists.", i->name); - i->gid = g->gr_gid; - i->gid_set = true; - return 0; - } - if (!IN_SET(errno, 0, ENOENT)) - return log_error_errno(errno, "Failed to check if group %s already exists: %m", i->name); - } - - /* Try to use the suggested numeric gid */ - if (i->gid_set) { - r = gid_is_ok(i->gid); - if (r < 0) - return log_error_errno(r, "Failed to verify gid " GID_FMT ": %m", i->gid); - if (r == 0) { - log_debug("Suggested group ID " GID_FMT " for %s already used.", i->gid, i->name); - i->gid_set = false; - } - } - - /* Try to reuse the numeric uid, if there's one */ - if (!i->gid_set && i->uid_set) { - r = gid_is_ok((gid_t) i->uid); - if (r < 0) - return log_error_errno(r, "Failed to verify gid " GID_FMT ": %m", i->gid); - if (r > 0) { - i->gid = (gid_t) i->uid; - i->gid_set = true; - } - } - - /* If that didn't work, try to read it from the specified path */ - if (!i->gid_set) { - gid_t c; - - if (read_id_from_file(i, NULL, &c) > 0) { - - if (c <= 0 || !uid_range_contains(uid_range, n_uid_range, c)) - log_debug("Group ID " GID_FMT " of file not suitable for %s.", c, i->name); - else { - r = gid_is_ok(c); - if (r < 0) - return log_error_errno(r, "Failed to verify gid " GID_FMT ": %m", i->gid); - else if (r > 0) { - i->gid = c; - i->gid_set = true; - } else - log_debug("Group ID " GID_FMT " of file for %s already used.", c, i->name); - } - } - } - - /* And if that didn't work either, let's try to find a free one */ - if (!i->gid_set) { - for (;;) { - /* We look for new GIDs in the UID pool! */ - r = uid_range_next_lower(uid_range, n_uid_range, &search_uid); - if (r < 0) { - log_error("No free group ID available for %s.", i->name); - return r; - } - - r = gid_is_ok(search_uid); - if (r < 0) - return log_error_errno(r, "Failed to verify gid " GID_FMT ": %m", i->gid); - else if (r > 0) - break; - } - - i->gid_set = true; - i->gid = search_uid; - } - - r = hashmap_ensure_allocated(&todo_gids, NULL); - if (r < 0) - return log_oom(); - - r = hashmap_put(todo_gids, GID_TO_PTR(i->gid), i); - if (r < 0) - return log_oom(); - - i->todo_group = true; - log_info("Creating group %s with gid " GID_FMT ".", i->name, i->gid); - - return 0; -} - -static int process_item(Item *i) { - int r; - - assert(i); - - switch (i->type) { - - case ADD_USER: - r = add_group(i); - if (r < 0) - return r; - - return add_user(i); - - case ADD_GROUP: { - Item *j; - - j = hashmap_get(users, i->name); - if (j) { - /* There's already user to be created for this - * name, let's process that in one step */ - - if (i->gid_set) { - j->gid = i->gid; - j->gid_set = true; - } - - if (i->gid_path) { - r = free_and_strdup(&j->gid_path, i->gid_path); - if (r < 0) - return log_oom(); - } - - return 0; - } - - return add_group(i); - } - - default: - assert_not_reached("Unknown item type"); - } -} - -static void item_free(Item *i) { - - if (!i) - return; - - free(i->name); - free(i->uid_path); - free(i->gid_path); - free(i->description); - free(i); -} - -DEFINE_TRIVIAL_CLEANUP_FUNC(Item*, item_free); - -static int add_implicit(void) { - char *g, **l; - Iterator iterator; - int r; - - /* Implicitly create additional users and groups, if they were listed in "m" lines */ - - HASHMAP_FOREACH_KEY(l, g, members, iterator) { - Item *i; - char **m; - - i = hashmap_get(groups, g); - if (!i) { - _cleanup_(item_freep) Item *j = NULL; - - r = hashmap_ensure_allocated(&groups, &string_hash_ops); - if (r < 0) - return log_oom(); - - j = new0(Item, 1); - if (!j) - return log_oom(); - - j->type = ADD_GROUP; - j->name = strdup(g); - if (!j->name) - return log_oom(); - - r = hashmap_put(groups, j->name, j); - if (r < 0) - return log_oom(); - - log_debug("Adding implicit group '%s' due to m line", j->name); - j = NULL; - } - - STRV_FOREACH(m, l) { - - i = hashmap_get(users, *m); - if (!i) { - _cleanup_(item_freep) Item *j = NULL; - - r = hashmap_ensure_allocated(&users, &string_hash_ops); - if (r < 0) - return log_oom(); - - j = new0(Item, 1); - if (!j) - return log_oom(); - - j->type = ADD_USER; - j->name = strdup(*m); - if (!j->name) - return log_oom(); - - r = hashmap_put(users, j->name, j); - if (r < 0) - return log_oom(); - - log_debug("Adding implicit user '%s' due to m line", j->name); - j = NULL; - } - } - } - - return 0; -} - -static bool item_equal(Item *a, Item *b) { - assert(a); - assert(b); - - if (a->type != b->type) - return false; - - if (!streq_ptr(a->name, b->name)) - return false; - - if (!streq_ptr(a->uid_path, b->uid_path)) - return false; - - if (!streq_ptr(a->gid_path, b->gid_path)) - return false; - - if (!streq_ptr(a->description, b->description)) - return false; - - if (a->uid_set != b->uid_set) - return false; - - if (a->uid_set && a->uid != b->uid) - return false; - - if (a->gid_set != b->gid_set) - return false; - - if (a->gid_set && a->gid != b->gid) - return false; - - if (!streq_ptr(a->home, b->home)) - return false; - - return true; -} - -static bool valid_user_group_name(const char *u) { - const char *i; - long sz; - - if (isempty(u)) - return false; - - if (!(u[0] >= 'a' && u[0] <= 'z') && - !(u[0] >= 'A' && u[0] <= 'Z') && - u[0] != '_') - return false; - - for (i = u+1; *i; i++) { - if (!(*i >= 'a' && *i <= 'z') && - !(*i >= 'A' && *i <= 'Z') && - !(*i >= '0' && *i <= '9') && - *i != '_' && - *i != '-') - return false; - } - - sz = sysconf(_SC_LOGIN_NAME_MAX); - assert_se(sz > 0); - - if ((size_t) (i-u) > (size_t) sz) - return false; - - if ((size_t) (i-u) > UT_NAMESIZE - 1) - return false; - - return true; -} - -static bool valid_gecos(const char *d) { - - if (!d) - return false; - - if (!utf8_is_valid(d)) - return false; - - if (string_has_cc(d, NULL)) - return false; - - /* Colons are used as field separators, and hence not OK */ - if (strchr(d, ':')) - return false; - - return true; -} - -static bool valid_home(const char *p) { - - if (isempty(p)) - return false; - - if (!utf8_is_valid(p)) - return false; - - if (string_has_cc(p, NULL)) - return false; - - if (!path_is_absolute(p)) - return false; - - if (!path_is_safe(p)) - return false; - - /* Colons are used as field separators, and hence not OK */ - if (strchr(p, ':')) - return false; - - return true; -} - -static int parse_line(const char *fname, unsigned line, const char *buffer) { - - static const Specifier specifier_table[] = { - { 'm', specifier_machine_id, NULL }, - { 'b', specifier_boot_id, NULL }, - { 'H', specifier_host_name, NULL }, - { 'v', specifier_kernel_release, NULL }, - {} - }; - - _cleanup_free_ char *action = NULL, *name = NULL, *id = NULL, *resolved_name = NULL, *resolved_id = NULL, *description = NULL, *home = NULL; - _cleanup_(item_freep) Item *i = NULL; - Item *existing; - Hashmap *h; - int r; - const char *p; - - assert(fname); - assert(line >= 1); - assert(buffer); - - /* Parse columns */ - p = buffer; - r = extract_many_words(&p, NULL, EXTRACT_QUOTES, &action, &name, &id, &description, &home, NULL); - if (r < 0) { - log_error("[%s:%u] Syntax error.", fname, line); - return r; - } - if (r < 2) { - log_error("[%s:%u] Missing action and name columns.", fname, line); - return -EINVAL; - } - if (!isempty(p)) { - log_error("[%s:%u] Trailing garbage.", fname, line); - return -EINVAL; - } - - /* Verify action */ - if (strlen(action) != 1) { - log_error("[%s:%u] Unknown modifier '%s'", fname, line, action); - return -EINVAL; - } - - if (!IN_SET(action[0], ADD_USER, ADD_GROUP, ADD_MEMBER, ADD_RANGE)) { - log_error("[%s:%u] Unknown command command type '%c'.", fname, line, action[0]); - return -EBADMSG; - } - - /* Verify name */ - if (isempty(name) || streq(name, "-")) - name = mfree(name); - - if (name) { - r = specifier_printf(name, specifier_table, NULL, &resolved_name); - if (r < 0) { - log_error("[%s:%u] Failed to replace specifiers: %s", fname, line, name); - return r; - } - - if (!valid_user_group_name(resolved_name)) { - log_error("[%s:%u] '%s' is not a valid user or group name.", fname, line, resolved_name); - return -EINVAL; - } - } - - /* Verify id */ - if (isempty(id) || streq(id, "-")) - id = mfree(id); - - if (id) { - r = specifier_printf(id, specifier_table, NULL, &resolved_id); - if (r < 0) { - log_error("[%s:%u] Failed to replace specifiers: %s", fname, line, name); - return r; - } - } - - /* Verify description */ - if (isempty(description) || streq(description, "-")) - description = mfree(description); - - if (description) { - if (!valid_gecos(description)) { - log_error("[%s:%u] '%s' is not a valid GECOS field.", fname, line, description); - return -EINVAL; - } - } - - /* Verify home */ - if (isempty(home) || streq(home, "-")) - home = mfree(home); - - if (home) { - if (!valid_home(home)) { - log_error("[%s:%u] '%s' is not a valid home directory field.", fname, line, home); - return -EINVAL; - } - } - - switch (action[0]) { - - case ADD_RANGE: - if (resolved_name) { - log_error("[%s:%u] Lines of type 'r' don't take a name field.", fname, line); - return -EINVAL; - } - - if (!resolved_id) { - log_error("[%s:%u] Lines of type 'r' require a ID range in the third field.", fname, line); - return -EINVAL; - } - - if (description) { - log_error("[%s:%u] Lines of type 'r' don't take a GECOS field.", fname, line); - return -EINVAL; - } - - if (home) { - log_error("[%s:%u] Lines of type 'r' don't take a home directory field.", fname, line); - return -EINVAL; - } - - r = uid_range_add_str(&uid_range, &n_uid_range, resolved_id); - if (r < 0) { - log_error("[%s:%u] Invalid UID range %s.", fname, line, resolved_id); - return -EINVAL; - } - - return 0; - - case ADD_MEMBER: { - char **l; - - /* Try to extend an existing member or group item */ - if (!name) { - log_error("[%s:%u] Lines of type 'm' require a user name in the second field.", fname, line); - return -EINVAL; - } - - if (!resolved_id) { - log_error("[%s:%u] Lines of type 'm' require a group name in the third field.", fname, line); - return -EINVAL; - } - - if (!valid_user_group_name(resolved_id)) { - log_error("[%s:%u] '%s' is not a valid user or group name.", fname, line, resolved_id); - return -EINVAL; - } - - if (description) { - log_error("[%s:%u] Lines of type 'm' don't take a GECOS field.", fname, line); - return -EINVAL; - } - - if (home) { - log_error("[%s:%u] Lines of type 'm' don't take a home directory field.", fname, line); - return -EINVAL; - } - - r = hashmap_ensure_allocated(&members, &string_hash_ops); - if (r < 0) - return log_oom(); - - l = hashmap_get(members, resolved_id); - if (l) { - /* A list for this group name already exists, let's append to it */ - r = strv_push(&l, resolved_name); - if (r < 0) - return log_oom(); - - resolved_name = NULL; - - assert_se(hashmap_update(members, resolved_id, l) >= 0); - } else { - /* No list for this group name exists yet, create one */ - - l = new0(char *, 2); - if (!l) - return -ENOMEM; - - l[0] = resolved_name; - l[1] = NULL; - - r = hashmap_put(members, resolved_id, l); - if (r < 0) { - free(l); - return log_oom(); - } - - resolved_id = resolved_name = NULL; - } - - return 0; - } - - case ADD_USER: - if (!name) { - log_error("[%s:%u] Lines of type 'u' require a user name in the second field.", fname, line); - return -EINVAL; - } - - r = hashmap_ensure_allocated(&users, &string_hash_ops); - if (r < 0) - return log_oom(); - - i = new0(Item, 1); - if (!i) - return log_oom(); - - if (resolved_id) { - if (path_is_absolute(resolved_id)) { - i->uid_path = resolved_id; - resolved_id = NULL; - - path_kill_slashes(i->uid_path); - } else { - r = parse_uid(resolved_id, &i->uid); - if (r < 0) { - log_error("Failed to parse UID: %s", id); - return -EBADMSG; - } - - i->uid_set = true; - } - } - - i->description = description; - description = NULL; - - i->home = home; - home = NULL; - - h = users; - break; - - case ADD_GROUP: - if (!name) { - log_error("[%s:%u] Lines of type 'g' require a user name in the second field.", fname, line); - return -EINVAL; - } - - if (description) { - log_error("[%s:%u] Lines of type 'g' don't take a GECOS field.", fname, line); - return -EINVAL; - } - - if (home) { - log_error("[%s:%u] Lines of type 'g' don't take a home directory field.", fname, line); - return -EINVAL; - } - - r = hashmap_ensure_allocated(&groups, &string_hash_ops); - if (r < 0) - return log_oom(); - - i = new0(Item, 1); - if (!i) - return log_oom(); - - if (resolved_id) { - if (path_is_absolute(resolved_id)) { - i->gid_path = resolved_id; - resolved_id = NULL; - - path_kill_slashes(i->gid_path); - } else { - r = parse_gid(resolved_id, &i->gid); - if (r < 0) { - log_error("Failed to parse GID: %s", id); - return -EBADMSG; - } - - i->gid_set = true; - } - } - - h = groups; - break; - - default: - return -EBADMSG; - } - - i->type = action[0]; - i->name = resolved_name; - resolved_name = NULL; - - existing = hashmap_get(h, i->name); - if (existing) { - - /* Two identical items are fine */ - if (!item_equal(existing, i)) - log_warning("Two or more conflicting lines for %s configured, ignoring.", i->name); - - return 0; - } - - r = hashmap_put(h, i->name, i); - if (r < 0) - return log_oom(); - - i = NULL; - return 0; -} - -static int read_config_file(const char *fn, bool ignore_enoent) { - _cleanup_fclose_ FILE *rf = NULL; - FILE *f = NULL; - char line[LINE_MAX]; - unsigned v = 0; - int r = 0; - - assert(fn); - - if (streq(fn, "-")) - f = stdin; - else { - r = search_and_fopen_nulstr(fn, "re", arg_root, conf_file_dirs, &rf); - if (r < 0) { - if (ignore_enoent && r == -ENOENT) - return 0; - - return log_error_errno(r, "Failed to open '%s', ignoring: %m", fn); - } - - f = rf; - } - - FOREACH_LINE(line, f, break) { - char *l; - int k; - - v++; - - l = strstrip(line); - if (*l == '#' || *l == 0) - continue; - - k = parse_line(fn, v, l); - if (k < 0 && r == 0) - r = k; - } - - if (ferror(f)) { - log_error_errno(errno, "Failed to read from file %s: %m", fn); - if (r == 0) - r = -EIO; - } - - return r; -} - -static void free_database(Hashmap *by_name, Hashmap *by_id) { - char *name; - - for (;;) { - name = hashmap_first(by_id); - if (!name) - break; - - hashmap_remove(by_name, name); - - hashmap_steal_first_key(by_id); - free(name); - } - - while ((name = hashmap_steal_first_key(by_name))) - free(name); - - hashmap_free(by_name); - hashmap_free(by_id); -} - -static void help(void) { - printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n" - "Creates system user accounts.\n\n" - " -h --help Show this help\n" - " --version Show package version\n" - " --root=PATH Operate on an alternate filesystem root\n" - , program_invocation_short_name); -} - -static int parse_argv(int argc, char *argv[]) { - - enum { - ARG_VERSION = 0x100, - ARG_ROOT, - }; - - static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, ARG_VERSION }, - { "root", required_argument, NULL, ARG_ROOT }, - {} - }; - - int c, r; - - assert(argc >= 0); - assert(argv); - - while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) - - switch (c) { - - case 'h': - help(); - return 0; - - case ARG_VERSION: - return version(); - - case ARG_ROOT: - r = parse_path_argument_and_warn(optarg, true, &arg_root); - if (r < 0) - return r; - break; - - case '?': - return -EINVAL; - - default: - assert_not_reached("Unhandled option"); - } - - return 1; -} - -int main(int argc, char *argv[]) { - - _cleanup_close_ int lock = -1; - Iterator iterator; - int r, k; - Item *i; - char *n; - - r = parse_argv(argc, argv); - if (r <= 0) - goto finish; - - log_set_target(LOG_TARGET_AUTO); - log_parse_environment(); - log_open(); - - umask(0022); - - r = mac_selinux_init(); - if (r < 0) { - log_error_errno(r, "SELinux setup failed: %m"); - goto finish; - } - - if (optind < argc) { - int j; - - for (j = optind; j < argc; j++) { - k = read_config_file(argv[j], false); - if (k < 0 && r == 0) - r = k; - } - } else { - _cleanup_strv_free_ char **files = NULL; - char **f; - - r = conf_files_list_nulstr(&files, ".conf", arg_root, conf_file_dirs); - if (r < 0) { - log_error_errno(r, "Failed to enumerate sysusers.d files: %m"); - goto finish; - } - - STRV_FOREACH(f, files) { - k = read_config_file(*f, true); - if (k < 0 && r == 0) - r = k; - } - } - - if (!uid_range) { - /* Default to default range of 1..SYSTEMD_UID_MAX */ - r = uid_range_add(&uid_range, &n_uid_range, 1, SYSTEM_UID_MAX); - if (r < 0) { - log_oom(); - goto finish; - } - } - - r = add_implicit(); - if (r < 0) - goto finish; - - lock = take_etc_passwd_lock(arg_root); - if (lock < 0) { - log_error_errno(lock, "Failed to take lock: %m"); - goto finish; - } - - r = load_user_database(); - if (r < 0) { - log_error_errno(r, "Failed to load user database: %m"); - goto finish; - } - - r = load_group_database(); - if (r < 0) { - log_error_errno(r, "Failed to read group database: %m"); - goto finish; - } - - HASHMAP_FOREACH(i, groups, iterator) - process_item(i); - - HASHMAP_FOREACH(i, users, iterator) - process_item(i); - - r = write_files(); - if (r < 0) - log_error_errno(r, "Failed to write files: %m"); - -finish: - while ((i = hashmap_steal_first(groups))) - item_free(i); - - while ((i = hashmap_steal_first(users))) - item_free(i); - - while ((n = hashmap_first_key(members))) { - strv_free(hashmap_steal_first(members)); - free(n); - } - - hashmap_free(groups); - hashmap_free(users); - hashmap_free(members); - hashmap_free(todo_uids); - hashmap_free(todo_gids); - - free_database(database_user, database_uid); - free_database(database_group, database_gid); - - free(arg_root); - - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; -} diff --git a/src/grp-initutils/systemd-tmpfiles/Makefile b/src/grp-initutils/systemd-tmpfiles/Makefile deleted file mode 100644 index d12c910883..0000000000 --- a/src/grp-initutils/systemd-tmpfiles/Makefile +++ /dev/null @@ -1,84 +0,0 @@ -# -*- Mode: makefile; indent-tabs-mode: t -*- -# -# This file is part of systemd. -# -# Copyright 2010-2012 Lennart Poettering -# Copyright 2010-2012 Kay Sievers -# Copyright 2013 Zbigniew Jędrzejewski-Szmek -# Copyright 2013 David Strauss -# Copyright 2016 Luke Shumaker -# -# systemd is free software; you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation; either version 2.1 of the License, or -# (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 -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with systemd; If not, see . -include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk -include $(topsrcdir)/build-aux/Makefile.head.mk - -ifneq ($(ENABLE_TMPFILES),) -systemd_tmpfiles_SOURCES = \ - src/tmpfiles/tmpfiles.c - -systemd_tmpfiles_LDADD = \ - libshared.la - -rootbin_PROGRAMS += \ - systemd-tmpfiles - -dist_systemunit_DATA += \ - units/systemd-tmpfiles-clean.timer - -nodist_systemunit_DATA += \ - units/systemd-tmpfiles-setup-dev.service \ - units/systemd-tmpfiles-setup.service \ - units/systemd-tmpfiles-clean.service - -nodist_tmpfiles_DATA = \ - tmpfiles.d/systemd.conf \ - tmpfiles.d/etc.conf - -dist_tmpfiles_DATA = \ - tmpfiles.d/systemd-nologin.conf \ - tmpfiles.d/tmp.conf \ - tmpfiles.d/x11.conf \ - tmpfiles.d/var.conf \ - tmpfiles.d/home.conf \ - tmpfiles.d/systemd-nspawn.conf \ - tmpfiles.d/journal-nocow.conf - -ifneq ($(HAVE_SYSV_COMPAT),) -dist_tmpfiles_DATA += \ - tmpfiles.d/legacy.conf -endif # HAVE_SYSV_COMPAT - -SYSINIT_TARGET_WANTS += \ - systemd-tmpfiles-setup-dev.service \ - systemd-tmpfiles-setup.service - -dist_zshcompletion_data += \ - shell-completion/zsh/_systemd-tmpfiles - -TIMERS_TARGET_WANTS += \ - systemd-tmpfiles-clean.timer - -INSTALL_DIRS += \ - $(tmpfilesdir) \ - $(sysconfdir)/tmpfiles.d -endif # ENABLE_TMPFILES - -EXTRA_DIST += \ - tmpfiles.d/systemd.conf.m4 \ - tmpfiles.d/etc.conf.m4 \ - units/systemd-tmpfiles-setup-dev.service.in \ - units/systemd-tmpfiles-setup.service.in \ - units/systemd-tmpfiles-clean.service.in - -include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-initutils/systemd-tmpfiles/systemd-tmpfiles.completion.zsh b/src/grp-initutils/systemd-tmpfiles/systemd-tmpfiles.completion.zsh deleted file mode 100644 index 6ff02e5d98..0000000000 --- a/src/grp-initutils/systemd-tmpfiles/systemd-tmpfiles.completion.zsh +++ /dev/null @@ -1,13 +0,0 @@ -#compdef systemd-tmpfiles - -_arguments \ - {-h,--help}'[Show help]' \ - '--version[Show package version]' \ - '--create[Create, set ownership/permissions based on the config files.]' \ - '--clean[Clean up all files and directories with an age parameter configured.]' \ - '--remove[All files and directories marked with r, R in the configuration files are removed.]' \ - '--boot[Execute actions only safe at boot]' \ - '--prefix=[Only apply rules that apply to paths with the specified prefix.]' \ - '--exclude-prefix=[Ignore rules that apply to paths with the specified prefix.]' \ - '--root=[Operate on an alternate filesystem root]:directory:_directories' \ - '*::files:_files' diff --git a/src/grp-initutils/systemd-tmpfiles/tmpfiles.c b/src/grp-initutils/systemd-tmpfiles/tmpfiles.c deleted file mode 100644 index f865b9cc3e..0000000000 --- a/src/grp-initutils/systemd-tmpfiles/tmpfiles.c +++ /dev/null @@ -1,2344 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering, Kay Sievers - Copyright 2015 Zbigniew Jędrzejewski-Szmek - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "basic/alloc-util.h" -#include "basic/btrfs-util.h" -#include "basic/capability-util.h" -#include "basic/chattr-util.h" -#include "basic/conf-files.h" -#include "basic/copy.h" -#include "basic/def.h" -#include "basic/escape.h" -#include "basic/fd-util.h" -#include "basic/fileio.h" -#include "basic/formats-util.h" -#include "basic/fs-util.h" -#include "basic/glob-util.h" -#include "basic/io-util.h" -#include "basic/label.h" -#include "basic/log.h" -#include "basic/macro.h" -#include "basic/missing.h" -#include "basic/mkdir.h" -#include "basic/mount-util.h" -#include "basic/parse-util.h" -#include "basic/path-util.h" -#include "basic/rm-rf.h" -#include "basic/selinux-util.h" -#include "basic/set.h" -#include "basic/stat-util.h" -#include "basic/stdio-util.h" -#include "basic/string-table.h" -#include "basic/string-util.h" -#include "basic/strv.h" -#include "basic/umask-util.h" -#include "basic/user-util.h" -#include "basic/util.h" -#include "shared/acl-util.h" -#include "shared/specifier.h" - -/* This reads all files listed in /etc/tmpfiles.d/?*.conf and creates - * them in the file system. This is intended to be used to create - * properly owned directories beneath /tmp, /var/tmp, /run, which are - * volatile and hence need to be recreated on bootup. */ - -typedef enum ItemType { - /* These ones take file names */ - CREATE_FILE = 'f', - TRUNCATE_FILE = 'F', - CREATE_DIRECTORY = 'd', - TRUNCATE_DIRECTORY = 'D', - CREATE_SUBVOLUME = 'v', - CREATE_SUBVOLUME_INHERIT_QUOTA = 'q', - CREATE_SUBVOLUME_NEW_QUOTA = 'Q', - CREATE_FIFO = 'p', - CREATE_SYMLINK = 'L', - CREATE_CHAR_DEVICE = 'c', - CREATE_BLOCK_DEVICE = 'b', - COPY_FILES = 'C', - - /* These ones take globs */ - WRITE_FILE = 'w', - EMPTY_DIRECTORY = 'e', - SET_XATTR = 't', - RECURSIVE_SET_XATTR = 'T', - SET_ACL = 'a', - RECURSIVE_SET_ACL = 'A', - SET_ATTRIBUTE = 'h', - RECURSIVE_SET_ATTRIBUTE = 'H', - IGNORE_PATH = 'x', - IGNORE_DIRECTORY_PATH = 'X', - REMOVE_PATH = 'r', - RECURSIVE_REMOVE_PATH = 'R', - RELABEL_PATH = 'z', - RECURSIVE_RELABEL_PATH = 'Z', - ADJUST_MODE = 'm', /* legacy, 'z' is identical to this */ -} ItemType; - -typedef struct Item { - ItemType type; - - char *path; - char *argument; - char **xattrs; -#ifdef HAVE_ACL - acl_t acl_access; - acl_t acl_default; -#endif - uid_t uid; - gid_t gid; - mode_t mode; - usec_t age; - - dev_t major_minor; - unsigned attribute_value; - unsigned attribute_mask; - - bool uid_set:1; - bool gid_set:1; - bool mode_set:1; - bool age_set:1; - bool mask_perms:1; - bool attribute_set:1; - - bool keep_first_level:1; - - bool force:1; - - bool done:1; -} Item; - -typedef struct ItemArray { - Item *items; - size_t count; - size_t size; -} ItemArray; - -static bool arg_create = false; -static bool arg_clean = false; -static bool arg_remove = false; -static bool arg_boot = false; - -static char **arg_include_prefixes = NULL; -static char **arg_exclude_prefixes = NULL; -static char *arg_root = NULL; - -static const char conf_file_dirs[] = CONF_PATHS_NULSTR("tmpfiles.d"); - -#define MAX_DEPTH 256 - -static OrderedHashmap *items = NULL, *globs = NULL; -static Set *unix_sockets = NULL; - -static const Specifier specifier_table[] = { - { 'm', specifier_machine_id, NULL }, - { 'b', specifier_boot_id, NULL }, - { 'H', specifier_host_name, NULL }, - { 'v', specifier_kernel_release, NULL }, - {} -}; - -static bool needs_glob(ItemType t) { - return IN_SET(t, - WRITE_FILE, - IGNORE_PATH, - IGNORE_DIRECTORY_PATH, - REMOVE_PATH, - RECURSIVE_REMOVE_PATH, - EMPTY_DIRECTORY, - ADJUST_MODE, - RELABEL_PATH, - RECURSIVE_RELABEL_PATH, - SET_XATTR, - RECURSIVE_SET_XATTR, - SET_ACL, - RECURSIVE_SET_ACL, - SET_ATTRIBUTE, - RECURSIVE_SET_ATTRIBUTE); -} - -static bool takes_ownership(ItemType t) { - return IN_SET(t, - CREATE_FILE, - TRUNCATE_FILE, - CREATE_DIRECTORY, - EMPTY_DIRECTORY, - TRUNCATE_DIRECTORY, - CREATE_SUBVOLUME, - CREATE_SUBVOLUME_INHERIT_QUOTA, - CREATE_SUBVOLUME_NEW_QUOTA, - CREATE_FIFO, - CREATE_SYMLINK, - CREATE_CHAR_DEVICE, - CREATE_BLOCK_DEVICE, - COPY_FILES, - WRITE_FILE, - IGNORE_PATH, - IGNORE_DIRECTORY_PATH, - REMOVE_PATH, - RECURSIVE_REMOVE_PATH); -} - -static struct Item* find_glob(OrderedHashmap *h, const char *match) { - ItemArray *j; - Iterator i; - - ORDERED_HASHMAP_FOREACH(j, h, i) { - unsigned n; - - for (n = 0; n < j->count; n++) { - Item *item = j->items + n; - - if (fnmatch(item->path, match, FNM_PATHNAME|FNM_PERIOD) == 0) - return item; - } - } - - return NULL; -} - -static void load_unix_sockets(void) { - _cleanup_fclose_ FILE *f = NULL; - char line[LINE_MAX]; - - if (unix_sockets) - return; - - /* We maintain a cache of the sockets we found in - * /proc/net/unix to speed things up a little. */ - - unix_sockets = set_new(&string_hash_ops); - if (!unix_sockets) - return; - - f = fopen("/proc/net/unix", "re"); - if (!f) - return; - - /* Skip header */ - if (!fgets(line, sizeof(line), f)) - goto fail; - - for (;;) { - char *p, *s; - int k; - - if (!fgets(line, sizeof(line), f)) - break; - - truncate_nl(line); - - p = strchr(line, ':'); - if (!p) - continue; - - if (strlen(p) < 37) - continue; - - p += 37; - p += strspn(p, WHITESPACE); - p += strcspn(p, WHITESPACE); /* skip one more word */ - p += strspn(p, WHITESPACE); - - if (*p != '/') - continue; - - s = strdup(p); - if (!s) - goto fail; - - path_kill_slashes(s); - - k = set_consume(unix_sockets, s); - if (k < 0 && k != -EEXIST) - goto fail; - } - - return; - -fail: - set_free_free(unix_sockets); - unix_sockets = NULL; -} - -static bool unix_socket_alive(const char *fn) { - assert(fn); - - load_unix_sockets(); - - if (unix_sockets) - return !!set_get(unix_sockets, (char*) fn); - - /* We don't know, so assume yes */ - return true; -} - -static int dir_is_mount_point(DIR *d, const char *subdir) { - - union file_handle_union h = FILE_HANDLE_INIT; - int mount_id_parent, mount_id; - int r_p, r; - - r_p = name_to_handle_at(dirfd(d), ".", &h.handle, &mount_id_parent, 0); - if (r_p < 0) - r_p = -errno; - - h.handle.handle_bytes = MAX_HANDLE_SZ; - r = name_to_handle_at(dirfd(d), subdir, &h.handle, &mount_id, 0); - if (r < 0) - r = -errno; - - /* got no handle; make no assumptions, return error */ - if (r_p < 0 && r < 0) - return r_p; - - /* got both handles; if they differ, it is a mount point */ - if (r_p >= 0 && r >= 0) - return mount_id_parent != mount_id; - - /* got only one handle; assume different mount points if one - * of both queries was not supported by the filesystem */ - if (r_p == -ENOSYS || r_p == -EOPNOTSUPP || r == -ENOSYS || r == -EOPNOTSUPP) - return true; - - /* return error */ - if (r_p < 0) - return r_p; - return r; -} - -static DIR* xopendirat_nomod(int dirfd, const char *path) { - DIR *dir; - - dir = xopendirat(dirfd, path, O_NOFOLLOW|O_NOATIME); - if (dir) - return dir; - - log_debug_errno(errno, "Cannot open %sdirectory \"%s\": %m", dirfd == AT_FDCWD ? "" : "sub", path); - if (errno != EPERM) - return NULL; - - dir = xopendirat(dirfd, path, O_NOFOLLOW); - if (!dir) - log_debug_errno(errno, "Cannot open %sdirectory \"%s\": %m", dirfd == AT_FDCWD ? "" : "sub", path); - - return dir; -} - -static DIR* opendir_nomod(const char *path) { - return xopendirat_nomod(AT_FDCWD, path); -} - -static int dir_cleanup( - Item *i, - const char *p, - DIR *d, - const struct stat *ds, - usec_t cutoff, - dev_t rootdev, - bool mountpoint, - int maxdepth, - bool keep_this_level) { - - struct dirent *dent; - struct timespec times[2]; - bool deleted = false; - int r = 0; - - while ((dent = readdir(d))) { - struct stat s; - usec_t age; - _cleanup_free_ char *sub_path = NULL; - - if (STR_IN_SET(dent->d_name, ".", "..")) - continue; - - if (fstatat(dirfd(d), dent->d_name, &s, AT_SYMLINK_NOFOLLOW) < 0) { - if (errno == ENOENT) - continue; - - /* FUSE, NFS mounts, SELinux might return EACCES */ - if (errno == EACCES) - log_debug_errno(errno, "stat(%s/%s) failed: %m", p, dent->d_name); - else - log_error_errno(errno, "stat(%s/%s) failed: %m", p, dent->d_name); - r = -errno; - continue; - } - - /* Stay on the same filesystem */ - if (s.st_dev != rootdev) { - log_debug("Ignoring \"%s/%s\": different filesystem.", p, dent->d_name); - continue; - } - - /* Try to detect bind mounts of the same filesystem instance; they - * do not differ in device major/minors. This type of query is not - * supported on all kernels or filesystem types though. */ - if (S_ISDIR(s.st_mode) && dir_is_mount_point(d, dent->d_name) > 0) { - log_debug("Ignoring \"%s/%s\": different mount of the same filesystem.", - p, dent->d_name); - continue; - } - - /* Do not delete read-only files owned by root */ - if (s.st_uid == 0 && !(s.st_mode & S_IWUSR)) { - log_debug("Ignoring \"%s/%s\": read-only and owner by root.", p, dent->d_name); - continue; - } - - sub_path = strjoin(p, "/", dent->d_name, NULL); - if (!sub_path) { - r = log_oom(); - goto finish; - } - - /* Is there an item configured for this path? */ - if (ordered_hashmap_get(items, sub_path)) { - log_debug("Ignoring \"%s\": a separate entry exists.", sub_path); - continue; - } - - if (find_glob(globs, sub_path)) { - log_debug("Ignoring \"%s\": a separate glob exists.", sub_path); - continue; - } - - if (S_ISDIR(s.st_mode)) { - - if (mountpoint && - streq(dent->d_name, "lost+found") && - s.st_uid == 0) { - log_debug("Ignoring \"%s\".", sub_path); - continue; - } - - if (maxdepth <= 0) - log_warning("Reached max depth on \"%s\".", sub_path); - else { - _cleanup_closedir_ DIR *sub_dir; - int q; - - sub_dir = xopendirat_nomod(dirfd(d), dent->d_name); - if (!sub_dir) { - if (errno != ENOENT) - r = log_error_errno(errno, "opendir(%s) failed: %m", sub_path); - - continue; - } - - q = dir_cleanup(i, sub_path, sub_dir, &s, cutoff, rootdev, false, maxdepth-1, false); - if (q < 0) - r = q; - } - - /* Note: if you are wondering why we don't - * support the sticky bit for excluding - * directories from cleaning like we do it for - * other file system objects: well, the sticky - * bit already has a meaning for directories, - * so we don't want to overload that. */ - - if (keep_this_level) { - log_debug("Keeping \"%s\".", sub_path); - continue; - } - - /* Ignore ctime, we change it when deleting */ - age = timespec_load(&s.st_mtim); - if (age >= cutoff) { - char a[FORMAT_TIMESTAMP_MAX]; - /* Follows spelling in stat(1). */ - log_debug("Directory \"%s\": modify time %s is too new.", - sub_path, - format_timestamp_us(a, sizeof(a), age)); - continue; - } - - age = timespec_load(&s.st_atim); - if (age >= cutoff) { - char a[FORMAT_TIMESTAMP_MAX]; - log_debug("Directory \"%s\": access time %s is too new.", - sub_path, - format_timestamp_us(a, sizeof(a), age)); - continue; - } - - log_debug("Removing directory \"%s\".", sub_path); - if (unlinkat(dirfd(d), dent->d_name, AT_REMOVEDIR) < 0) - if (errno != ENOENT && errno != ENOTEMPTY) { - log_error_errno(errno, "rmdir(%s): %m", sub_path); - r = -errno; - } - - } else { - /* Skip files for which the sticky bit is - * set. These are semantics we define, and are - * unknown elsewhere. See XDG_RUNTIME_DIR - * specification for details. */ - if (s.st_mode & S_ISVTX) { - log_debug("Skipping \"%s\": sticky bit set.", sub_path); - continue; - } - - if (mountpoint && S_ISREG(s.st_mode)) - if (s.st_uid == 0 && STR_IN_SET(dent->d_name, - ".journal", - "aquota.user", - "aquota.group")) { - log_debug("Skipping \"%s\".", sub_path); - continue; - } - - /* Ignore sockets that are listed in /proc/net/unix */ - if (S_ISSOCK(s.st_mode) && unix_socket_alive(sub_path)) { - log_debug("Skipping \"%s\": live socket.", sub_path); - continue; - } - - /* Ignore device nodes */ - if (S_ISCHR(s.st_mode) || S_ISBLK(s.st_mode)) { - log_debug("Skipping \"%s\": a device.", sub_path); - continue; - } - - /* Keep files on this level around if this is - * requested */ - if (keep_this_level) { - log_debug("Keeping \"%s\".", sub_path); - continue; - } - - age = timespec_load(&s.st_mtim); - if (age >= cutoff) { - char a[FORMAT_TIMESTAMP_MAX]; - /* Follows spelling in stat(1). */ - log_debug("File \"%s\": modify time %s is too new.", - sub_path, - format_timestamp_us(a, sizeof(a), age)); - continue; - } - - age = timespec_load(&s.st_atim); - if (age >= cutoff) { - char a[FORMAT_TIMESTAMP_MAX]; - log_debug("File \"%s\": access time %s is too new.", - sub_path, - format_timestamp_us(a, sizeof(a), age)); - continue; - } - - age = timespec_load(&s.st_ctim); - if (age >= cutoff) { - char a[FORMAT_TIMESTAMP_MAX]; - log_debug("File \"%s\": change time %s is too new.", - sub_path, - format_timestamp_us(a, sizeof(a), age)); - continue; - } - - log_debug("unlink \"%s\"", sub_path); - - if (unlinkat(dirfd(d), dent->d_name, 0) < 0) - if (errno != ENOENT) - r = log_error_errno(errno, "unlink(%s): %m", sub_path); - - deleted = true; - } - } - -finish: - if (deleted) { - usec_t age1, age2; - char a[FORMAT_TIMESTAMP_MAX], b[FORMAT_TIMESTAMP_MAX]; - - /* Restore original directory timestamps */ - times[0] = ds->st_atim; - times[1] = ds->st_mtim; - - age1 = timespec_load(&ds->st_atim); - age2 = timespec_load(&ds->st_mtim); - log_debug("Restoring access and modification time on \"%s\": %s, %s", - p, - format_timestamp_us(a, sizeof(a), age1), - format_timestamp_us(b, sizeof(b), age2)); - if (futimens(dirfd(d), times) < 0) - log_error_errno(errno, "utimensat(%s): %m", p); - } - - return r; -} - -static int path_set_perms(Item *i, const char *path) { - _cleanup_close_ int fd = -1; - struct stat st; - - assert(i); - assert(path); - - /* We open the file with O_PATH here, to make the operation - * somewhat atomic. Also there's unfortunately no fchmodat() - * with AT_SYMLINK_NOFOLLOW, hence we emulate it here via - * O_PATH. */ - - fd = open(path, O_NOFOLLOW|O_CLOEXEC|O_PATH); - if (fd < 0) - return log_error_errno(errno, "Adjusting owner and mode for %s failed: %m", path); - - if (fstatat(fd, "", &st, AT_EMPTY_PATH) < 0) - return log_error_errno(errno, "Failed to fstat() file %s: %m", path); - - if (S_ISLNK(st.st_mode)) - log_debug("Skipping mode an owner fix for symlink %s.", path); - else { - char fn[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; - xsprintf(fn, "/proc/self/fd/%i", fd); - - /* not using i->path directly because it may be a glob */ - if (i->mode_set) { - mode_t m = i->mode; - - if (i->mask_perms) { - if (!(st.st_mode & 0111)) - m &= ~0111; - if (!(st.st_mode & 0222)) - m &= ~0222; - if (!(st.st_mode & 0444)) - m &= ~0444; - if (!S_ISDIR(st.st_mode)) - m &= ~07000; /* remove sticky/sgid/suid bit, unless directory */ - } - - if (m == (st.st_mode & 07777)) - log_debug("\"%s\" has right mode %o", path, st.st_mode); - else { - log_debug("chmod \"%s\" to mode %o", path, m); - if (chmod(fn, m) < 0) - return log_error_errno(errno, "chmod(%s) failed: %m", path); - } - } - - if ((i->uid != st.st_uid || i->gid != st.st_gid) && - (i->uid_set || i->gid_set)) { - log_debug("chown \"%s\" to "UID_FMT"."GID_FMT, - path, - i->uid_set ? i->uid : UID_INVALID, - i->gid_set ? i->gid : GID_INVALID); - if (chown(fn, - i->uid_set ? i->uid : UID_INVALID, - i->gid_set ? i->gid : GID_INVALID) < 0) - return log_error_errno(errno, "chown(%s) failed: %m", path); - } - } - - fd = safe_close(fd); - - return label_fix(path, false, false); -} - -static int parse_xattrs_from_arg(Item *i) { - const char *p; - int r; - - assert(i); - assert(i->argument); - - p = i->argument; - - for (;;) { - _cleanup_free_ char *name = NULL, *value = NULL, *xattr = NULL, *xattr_replaced = NULL; - - r = extract_first_word(&p, &xattr, NULL, EXTRACT_QUOTES|EXTRACT_CUNESCAPE); - if (r < 0) - log_warning_errno(r, "Failed to parse extended attribute '%s', ignoring: %m", p); - if (r <= 0) - break; - - r = specifier_printf(xattr, specifier_table, NULL, &xattr_replaced); - if (r < 0) - return log_error_errno(r, "Failed to replace specifiers in extended attribute '%s': %m", xattr); - - r = split_pair(xattr_replaced, "=", &name, &value); - if (r < 0) { - log_warning_errno(r, "Failed to parse extended attribute, ignoring: %s", xattr); - continue; - } - - if (isempty(name) || isempty(value)) { - log_warning("Malformed extended attribute found, ignoring: %s", xattr); - continue; - } - - if (strv_push_pair(&i->xattrs, name, value) < 0) - return log_oom(); - - name = value = NULL; - } - - return 0; -} - -static int path_set_xattrs(Item *i, const char *path) { - char **name, **value; - - assert(i); - assert(path); - - STRV_FOREACH_PAIR(name, value, i->xattrs) { - int n; - - n = strlen(*value); - log_debug("Setting extended attribute '%s=%s' on %s.", *name, *value, path); - if (lsetxattr(path, *name, *value, n, 0) < 0) { - log_error("Setting extended attribute %s=%s on %s failed: %m", *name, *value, path); - return -errno; - } - } - return 0; -} - -static int parse_acls_from_arg(Item *item) { -#ifdef HAVE_ACL - int r; - - assert(item); - - /* If force (= modify) is set, we will not modify the acl - * afterwards, so the mask can be added now if necessary. */ - - r = parse_acl(item->argument, &item->acl_access, &item->acl_default, !item->force); - if (r < 0) - log_warning_errno(r, "Failed to parse ACL \"%s\": %m. Ignoring", item->argument); -#else - log_warning_errno(ENOSYS, "ACLs are not supported. Ignoring"); -#endif - - return 0; -} - -#ifdef HAVE_ACL -static int path_set_acl(const char *path, const char *pretty, acl_type_t type, acl_t acl, bool modify) { - _cleanup_(acl_free_charpp) char *t = NULL; - _cleanup_(acl_freep) acl_t dup = NULL; - int r; - - /* Returns 0 for success, positive error if already warned, - * negative error otherwise. */ - - if (modify) { - r = acls_for_file(path, type, acl, &dup); - if (r < 0) - return r; - - r = calc_acl_mask_if_needed(&dup); - if (r < 0) - return r; - } else { - dup = acl_dup(acl); - if (!dup) - return -errno; - - /* the mask was already added earlier if needed */ - } - - r = add_base_acls_if_needed(&dup, path); - if (r < 0) - return r; - - t = acl_to_any_text(dup, NULL, ',', TEXT_ABBREVIATE); - log_debug("Setting %s ACL %s on %s.", - type == ACL_TYPE_ACCESS ? "access" : "default", - strna(t), pretty); - - r = acl_set_file(path, type, dup); - if (r < 0) - /* Return positive to indicate we already warned */ - return -log_error_errno(errno, - "Setting %s ACL \"%s\" on %s failed: %m", - type == ACL_TYPE_ACCESS ? "access" : "default", - strna(t), pretty); - - return 0; -} -#endif - -static int path_set_acls(Item *item, const char *path) { - int r = 0; -#ifdef HAVE_ACL - char fn[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; - _cleanup_close_ int fd = -1; - struct stat st; - - assert(item); - assert(path); - - fd = open(path, O_NOFOLLOW|O_CLOEXEC|O_PATH); - if (fd < 0) - return log_error_errno(errno, "Adjusting ACL of %s failed: %m", path); - - if (fstatat(fd, "", &st, AT_EMPTY_PATH) < 0) - return log_error_errno(errno, "Failed to fstat() file %s: %m", path); - - if (S_ISLNK(st.st_mode)) { - log_debug("Skipping ACL fix for symlink %s.", path); - return 0; - } - - xsprintf(fn, "/proc/self/fd/%i", fd); - - if (item->acl_access) - r = path_set_acl(fn, path, ACL_TYPE_ACCESS, item->acl_access, item->force); - - if (r == 0 && item->acl_default) - r = path_set_acl(fn, path, ACL_TYPE_DEFAULT, item->acl_default, item->force); - - if (r > 0) - return -r; /* already warned */ - else if (r == -EOPNOTSUPP) { - log_debug_errno(r, "ACLs not supported by file system at %s", path); - return 0; - } else if (r < 0) - log_error_errno(r, "ACL operation on \"%s\" failed: %m", path); -#endif - return r; -} - -#define ATTRIBUTES_ALL \ - (FS_NOATIME_FL | \ - FS_SYNC_FL | \ - FS_DIRSYNC_FL | \ - FS_APPEND_FL | \ - FS_COMPR_FL | \ - FS_NODUMP_FL | \ - FS_EXTENT_FL | \ - FS_IMMUTABLE_FL | \ - FS_JOURNAL_DATA_FL | \ - FS_SECRM_FL | \ - FS_UNRM_FL | \ - FS_NOTAIL_FL | \ - FS_TOPDIR_FL | \ - FS_NOCOW_FL) - -static int parse_attribute_from_arg(Item *item) { - - static const struct { - char character; - unsigned value; - } attributes[] = { - { 'A', FS_NOATIME_FL }, /* do not update atime */ - { 'S', FS_SYNC_FL }, /* Synchronous updates */ - { 'D', FS_DIRSYNC_FL }, /* dirsync behaviour (directories only) */ - { 'a', FS_APPEND_FL }, /* writes to file may only append */ - { 'c', FS_COMPR_FL }, /* Compress file */ - { 'd', FS_NODUMP_FL }, /* do not dump file */ - { 'e', FS_EXTENT_FL }, /* Top of directory hierarchies*/ - { 'i', FS_IMMUTABLE_FL }, /* Immutable file */ - { 'j', FS_JOURNAL_DATA_FL }, /* Reserved for ext3 */ - { 's', FS_SECRM_FL }, /* Secure deletion */ - { 'u', FS_UNRM_FL }, /* Undelete */ - { 't', FS_NOTAIL_FL }, /* file tail should not be merged */ - { 'T', FS_TOPDIR_FL }, /* Top of directory hierarchies*/ - { 'C', FS_NOCOW_FL }, /* Do not cow file */ - }; - - enum { - MODE_ADD, - MODE_DEL, - MODE_SET - } mode = MODE_ADD; - - unsigned value = 0, mask = 0; - const char *p; - - assert(item); - - p = item->argument; - if (p) { - if (*p == '+') { - mode = MODE_ADD; - p++; - } else if (*p == '-') { - mode = MODE_DEL; - p++; - } else if (*p == '=') { - mode = MODE_SET; - p++; - } - } - - if (isempty(p) && mode != MODE_SET) { - log_error("Setting file attribute on '%s' needs an attribute specification.", item->path); - return -EINVAL; - } - - for (; p && *p ; p++) { - unsigned i, v; - - for (i = 0; i < ELEMENTSOF(attributes); i++) - if (*p == attributes[i].character) - break; - - if (i >= ELEMENTSOF(attributes)) { - log_error("Unknown file attribute '%c' on '%s'.", *p, item->path); - return -EINVAL; - } - - v = attributes[i].value; - - SET_FLAG(value, v, (mode == MODE_ADD || mode == MODE_SET)); - - mask |= v; - } - - if (mode == MODE_SET) - mask |= ATTRIBUTES_ALL; - - assert(mask != 0); - - item->attribute_mask = mask; - item->attribute_value = value; - item->attribute_set = true; - - return 0; -} - -static int path_set_attribute(Item *item, const char *path) { - _cleanup_close_ int fd = -1; - struct stat st; - unsigned f; - int r; - - if (!item->attribute_set || item->attribute_mask == 0) - return 0; - - fd = open(path, O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_NOATIME|O_NOFOLLOW); - if (fd < 0) { - if (errno == ELOOP) - return log_error_errno(errno, "Skipping file attributes adjustment on symlink %s.", path); - - return log_error_errno(errno, "Cannot open '%s': %m", path); - } - - if (fstat(fd, &st) < 0) - return log_error_errno(errno, "Cannot stat '%s': %m", path); - - /* Issuing the file attribute ioctls on device nodes is not - * safe, as that will be delivered to the drivers, not the - * file system containing the device node. */ - if (!S_ISREG(st.st_mode) && !S_ISDIR(st.st_mode)) { - log_error("Setting file flags is only supported on regular files and directories, cannot set on '%s'.", path); - return -EINVAL; - } - - f = item->attribute_value & item->attribute_mask; - - /* Mask away directory-specific flags */ - if (!S_ISDIR(st.st_mode)) - f &= ~FS_DIRSYNC_FL; - - r = chattr_fd(fd, f, item->attribute_mask); - if (r < 0) - log_full_errno(r == -ENOTTY ? LOG_DEBUG : LOG_WARNING, - r, - "Cannot set file attribute for '%s', value=0x%08x, mask=0x%08x: %m", - path, item->attribute_value, item->attribute_mask); - - return 0; -} - -static int write_one_file(Item *i, const char *path) { - _cleanup_close_ int fd = -1; - int flags, r = 0; - struct stat st; - - assert(i); - assert(path); - - flags = i->type == CREATE_FILE ? O_CREAT|O_APPEND|O_NOFOLLOW : - i->type == TRUNCATE_FILE ? O_CREAT|O_TRUNC|O_NOFOLLOW : 0; - - RUN_WITH_UMASK(0000) { - mac_selinux_create_file_prepare(path, S_IFREG); - fd = open(path, flags|O_NDELAY|O_CLOEXEC|O_WRONLY|O_NOCTTY, i->mode); - mac_selinux_create_file_clear(); - } - - if (fd < 0) { - if (i->type == WRITE_FILE && errno == ENOENT) { - log_debug_errno(errno, "Not writing \"%s\": %m", path); - return 0; - } - - r = -errno; - if (!i->argument && errno == EROFS && stat(path, &st) == 0 && - (i->type == CREATE_FILE || st.st_size == 0)) - goto check_mode; - - return log_error_errno(r, "Failed to create file %s: %m", path); - } - - if (i->argument) { - _cleanup_free_ char *unescaped = NULL, *replaced = NULL; - - log_debug("%s to \"%s\".", i->type == CREATE_FILE ? "Appending" : "Writing", path); - - r = cunescape(i->argument, 0, &unescaped); - if (r < 0) - return log_error_errno(r, "Failed to unescape parameter to write: %s", i->argument); - - r = specifier_printf(unescaped, specifier_table, NULL, &replaced); - if (r < 0) - return log_error_errno(r, "Failed to replace specifiers in parameter to write '%s': %m", unescaped); - - r = loop_write(fd, replaced, strlen(replaced), false); - if (r < 0) - return log_error_errno(r, "Failed to write file \"%s\": %m", path); - } else - log_debug("\"%s\" has been created.", path); - - fd = safe_close(fd); - - if (stat(path, &st) < 0) - return log_error_errno(errno, "stat(%s) failed: %m", path); - - check_mode: - if (!S_ISREG(st.st_mode)) { - log_error("%s is not a file.", path); - return -EEXIST; - } - - r = path_set_perms(i, path); - if (r < 0) - return r; - - return 0; -} - -typedef int (*action_t)(Item *, const char *); - -static int item_do_children(Item *i, const char *path, action_t action) { - _cleanup_closedir_ DIR *d; - int r = 0; - - assert(i); - assert(path); - - /* This returns the first error we run into, but nevertheless - * tries to go on */ - - d = opendir_nomod(path); - if (!d) - return errno == ENOENT || errno == ENOTDIR ? 0 : -errno; - - for (;;) { - _cleanup_free_ char *p = NULL; - struct dirent *de; - int q; - - errno = 0; - de = readdir(d); - if (!de) { - if (errno > 0 && r == 0) - r = -errno; - - break; - } - - if (STR_IN_SET(de->d_name, ".", "..")) - continue; - - p = strjoin(path, "/", de->d_name, NULL); - if (!p) - return -ENOMEM; - - q = action(i, p); - if (q < 0 && q != -ENOENT && r == 0) - r = q; - - if (IN_SET(de->d_type, DT_UNKNOWN, DT_DIR)) { - q = item_do_children(i, p, action); - if (q < 0 && r == 0) - r = q; - } - } - - return r; -} - -static int glob_item(Item *i, action_t action, bool recursive) { - _cleanup_globfree_ glob_t g = { - .gl_closedir = (void (*)(void *)) closedir, - .gl_readdir = (struct dirent *(*)(void *)) readdir, - .gl_opendir = (void *(*)(const char *)) opendir_nomod, - .gl_lstat = lstat, - .gl_stat = stat, - }; - int r = 0, k; - char **fn; - - errno = 0; - k = glob(i->path, GLOB_NOSORT|GLOB_BRACE|GLOB_ALTDIRFUNC, NULL, &g); - if (k != 0 && k != GLOB_NOMATCH) - return log_error_errno(errno ?: EIO, "glob(%s) failed: %m", i->path); - - STRV_FOREACH(fn, g.gl_pathv) { - k = action(i, *fn); - if (k < 0 && r == 0) - r = k; - - if (recursive) { - k = item_do_children(i, *fn, action); - if (k < 0 && r == 0) - r = k; - } - } - - return r; -} - -typedef enum { - CREATION_NORMAL, - CREATION_EXISTING, - CREATION_FORCE, - _CREATION_MODE_MAX, - _CREATION_MODE_INVALID = -1 -} CreationMode; - -static const char *creation_mode_verb_table[_CREATION_MODE_MAX] = { - [CREATION_NORMAL] = "Created", - [CREATION_EXISTING] = "Found existing", - [CREATION_FORCE] = "Created replacement", -}; - -DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(creation_mode_verb, CreationMode); - -static int create_item(Item *i) { - _cleanup_free_ char *resolved = NULL; - struct stat st; - int r = 0; - int q = 0; - CreationMode creation; - - assert(i); - - log_debug("Running create action for entry %c %s", (char) i->type, i->path); - - switch (i->type) { - - case IGNORE_PATH: - case IGNORE_DIRECTORY_PATH: - case REMOVE_PATH: - case RECURSIVE_REMOVE_PATH: - return 0; - - case CREATE_FILE: - case TRUNCATE_FILE: - r = write_one_file(i, i->path); - if (r < 0) - return r; - break; - - case COPY_FILES: { - r = specifier_printf(i->argument, specifier_table, NULL, &resolved); - if (r < 0) - return log_error_errno(r, "Failed to substitute specifiers in copy source %s: %m", i->argument); - - log_debug("Copying tree \"%s\" to \"%s\".", resolved, i->path); - r = copy_tree(resolved, i->path, false); - - if (r == -EROFS && stat(i->path, &st) == 0) - r = -EEXIST; - - if (r < 0) { - struct stat a, b; - - if (r != -EEXIST) - return log_error_errno(r, "Failed to copy files to %s: %m", i->path); - - if (stat(resolved, &a) < 0) - return log_error_errno(errno, "stat(%s) failed: %m", resolved); - - if (stat(i->path, &b) < 0) - return log_error_errno(errno, "stat(%s) failed: %m", i->path); - - if ((a.st_mode ^ b.st_mode) & S_IFMT) { - log_debug("Can't copy to %s, file exists already and is of different type", i->path); - return 0; - } - } - - r = path_set_perms(i, i->path); - if (r < 0) - return r; - - break; - - case WRITE_FILE: - r = glob_item(i, write_one_file, false); - if (r < 0) - return r; - - break; - - case CREATE_DIRECTORY: - case TRUNCATE_DIRECTORY: - case CREATE_SUBVOLUME: - case CREATE_SUBVOLUME_INHERIT_QUOTA: - case CREATE_SUBVOLUME_NEW_QUOTA: - RUN_WITH_UMASK(0000) - mkdir_parents_label(i->path, 0755); - - if (IN_SET(i->type, CREATE_SUBVOLUME, CREATE_SUBVOLUME_INHERIT_QUOTA, CREATE_SUBVOLUME_NEW_QUOTA)) { - - if (btrfs_is_subvol(isempty(arg_root) ? "/" : arg_root) <= 0) - - /* Don't create a subvolume unless the - * root directory is one, too. We do - * this under the assumption that if - * the root directory is just a plain - * directory (i.e. very light-weight), - * we shouldn't try to split it up - * into subvolumes (i.e. more - * heavy-weight). Thus, chroot() - * environments and suchlike will get - * a full brtfs subvolume set up below - * their tree only if they - * specifically set up a btrfs - * subvolume for the root dir too. */ - - r = -ENOTTY; - else { - RUN_WITH_UMASK((~i->mode) & 0777) - r = btrfs_subvol_make(i->path); - } - } else - r = 0; - - if (IN_SET(i->type, CREATE_DIRECTORY, TRUNCATE_DIRECTORY) || r == -ENOTTY) - RUN_WITH_UMASK(0000) - r = mkdir_label(i->path, i->mode); - - if (r < 0) { - int k; - - if (r != -EEXIST && r != -EROFS) - return log_error_errno(r, "Failed to create directory or subvolume \"%s\": %m", i->path); - - k = is_dir(i->path, false); - if (k == -ENOENT && r == -EROFS) - return log_error_errno(r, "%s does not exist and cannot be created as the file system is read-only.", i->path); - if (k < 0) - return log_error_errno(k, "Failed to check if %s exists: %m", i->path); - if (!k) { - log_warning("\"%s\" already exists and is not a directory.", i->path); - return 0; - } - - creation = CREATION_EXISTING; - } else - creation = CREATION_NORMAL; - - log_debug("%s directory \"%s\".", creation_mode_verb_to_string(creation), i->path); - - if (IN_SET(i->type, CREATE_SUBVOLUME_NEW_QUOTA, CREATE_SUBVOLUME_INHERIT_QUOTA)) { - r = btrfs_subvol_auto_qgroup(i->path, 0, i->type == CREATE_SUBVOLUME_NEW_QUOTA); - if (r == -ENOTTY) - log_debug_errno(r, "Couldn't adjust quota for subvolume \"%s\" (unsupported fs or dir not a subvolume): %m", i->path); - else if (r == -EROFS) - log_debug_errno(r, "Couldn't adjust quota for subvolume \"%s\" (fs is read-only).", i->path); - else if (r == -ENOPROTOOPT) - log_debug_errno(r, "Couldn't adjust quota for subvolume \"%s\" (quota support is disabled).", i->path); - else if (r < 0) - q = log_error_errno(r, "Failed to adjust quota for subvolume \"%s\": %m", i->path); - else if (r > 0) - log_debug("Adjusted quota for subvolume \"%s\".", i->path); - else if (r == 0) - log_debug("Quota for subvolume \"%s\" already in place, no change made.", i->path); - } - - /* fall through */ - - case EMPTY_DIRECTORY: - r = path_set_perms(i, i->path); - if (q < 0) - return q; - if (r < 0) - return r; - - break; - - case CREATE_FIFO: - RUN_WITH_UMASK(0000) { - mac_selinux_create_file_prepare(i->path, S_IFIFO); - r = mkfifo(i->path, i->mode); - mac_selinux_create_file_clear(); - } - - if (r < 0) { - if (errno != EEXIST) - return log_error_errno(errno, "Failed to create fifo %s: %m", i->path); - - if (lstat(i->path, &st) < 0) - return log_error_errno(errno, "stat(%s) failed: %m", i->path); - - if (!S_ISFIFO(st.st_mode)) { - - if (i->force) { - RUN_WITH_UMASK(0000) { - mac_selinux_create_file_prepare(i->path, S_IFIFO); - r = mkfifo_atomic(i->path, i->mode); - mac_selinux_create_file_clear(); - } - - if (r < 0) - return log_error_errno(r, "Failed to create fifo %s: %m", i->path); - creation = CREATION_FORCE; - } else { - log_warning("\"%s\" already exists and is not a fifo.", i->path); - return 0; - } - } else - creation = CREATION_EXISTING; - } else - creation = CREATION_NORMAL; - log_debug("%s fifo \"%s\".", creation_mode_verb_to_string(creation), i->path); - - r = path_set_perms(i, i->path); - if (r < 0) - return r; - - break; - } - - case CREATE_SYMLINK: { - r = specifier_printf(i->argument, specifier_table, NULL, &resolved); - if (r < 0) - return log_error_errno(r, "Failed to substitute specifiers in symlink target %s: %m", i->argument); - - mac_selinux_create_file_prepare(i->path, S_IFLNK); - r = symlink(resolved, i->path); - mac_selinux_create_file_clear(); - - if (r < 0) { - _cleanup_free_ char *x = NULL; - - if (errno != EEXIST) - return log_error_errno(errno, "symlink(%s, %s) failed: %m", resolved, i->path); - - r = readlink_malloc(i->path, &x); - if (r < 0 || !streq(resolved, x)) { - - if (i->force) { - mac_selinux_create_file_prepare(i->path, S_IFLNK); - r = symlink_atomic(resolved, i->path); - mac_selinux_create_file_clear(); - - if (r < 0) - return log_error_errno(r, "symlink(%s, %s) failed: %m", resolved, i->path); - - creation = CREATION_FORCE; - } else { - log_debug("\"%s\" is not a symlink or does not point to the correct path.", i->path); - return 0; - } - } else - creation = CREATION_EXISTING; - } else - - creation = CREATION_NORMAL; - log_debug("%s symlink \"%s\".", creation_mode_verb_to_string(creation), i->path); - break; - } - - case CREATE_BLOCK_DEVICE: - case CREATE_CHAR_DEVICE: { - mode_t file_type; - - if (have_effective_cap(CAP_MKNOD) == 0) { - /* In a container we lack CAP_MKNOD. We - shouldn't attempt to create the device node in - that case to avoid noise, and we don't support - virtualized devices in containers anyway. */ - - log_debug("We lack CAP_MKNOD, skipping creation of device node %s.", i->path); - return 0; - } - - file_type = i->type == CREATE_BLOCK_DEVICE ? S_IFBLK : S_IFCHR; - - RUN_WITH_UMASK(0000) { - mac_selinux_create_file_prepare(i->path, file_type); - r = mknod(i->path, i->mode | file_type, i->major_minor); - mac_selinux_create_file_clear(); - } - - if (r < 0) { - if (errno == EPERM) { - log_debug("We lack permissions, possibly because of cgroup configuration; " - "skipping creation of device node %s.", i->path); - return 0; - } - - if (errno != EEXIST) - return log_error_errno(errno, "Failed to create device node %s: %m", i->path); - - if (lstat(i->path, &st) < 0) - return log_error_errno(errno, "stat(%s) failed: %m", i->path); - - if ((st.st_mode & S_IFMT) != file_type) { - - if (i->force) { - - RUN_WITH_UMASK(0000) { - mac_selinux_create_file_prepare(i->path, file_type); - r = mknod_atomic(i->path, i->mode | file_type, i->major_minor); - mac_selinux_create_file_clear(); - } - - if (r < 0) - return log_error_errno(r, "Failed to create device node \"%s\": %m", i->path); - creation = CREATION_FORCE; - } else { - log_debug("%s is not a device node.", i->path); - return 0; - } - } else - creation = CREATION_EXISTING; - } else - creation = CREATION_NORMAL; - - log_debug("%s %s device node \"%s\" %u:%u.", - creation_mode_verb_to_string(creation), - i->type == CREATE_BLOCK_DEVICE ? "block" : "char", - i->path, major(i->mode), minor(i->mode)); - - r = path_set_perms(i, i->path); - if (r < 0) - return r; - - break; - } - - case ADJUST_MODE: - case RELABEL_PATH: - r = glob_item(i, path_set_perms, false); - if (r < 0) - return r; - break; - - case RECURSIVE_RELABEL_PATH: - r = glob_item(i, path_set_perms, true); - if (r < 0) - return r; - break; - - case SET_XATTR: - r = glob_item(i, path_set_xattrs, false); - if (r < 0) - return r; - break; - - case RECURSIVE_SET_XATTR: - r = glob_item(i, path_set_xattrs, true); - if (r < 0) - return r; - break; - - case SET_ACL: - r = glob_item(i, path_set_acls, false); - if (r < 0) - return r; - break; - - case RECURSIVE_SET_ACL: - r = glob_item(i, path_set_acls, true); - if (r < 0) - return r; - break; - - case SET_ATTRIBUTE: - r = glob_item(i, path_set_attribute, false); - if (r < 0) - return r; - break; - - case RECURSIVE_SET_ATTRIBUTE: - r = glob_item(i, path_set_attribute, true); - if (r < 0) - return r; - break; - } - - return 0; -} - -static int remove_item_instance(Item *i, const char *instance) { - int r; - - assert(i); - - switch (i->type) { - - case REMOVE_PATH: - if (remove(instance) < 0 && errno != ENOENT) - return log_error_errno(errno, "rm(%s): %m", instance); - - break; - - case TRUNCATE_DIRECTORY: - case RECURSIVE_REMOVE_PATH: - /* FIXME: we probably should use dir_cleanup() here - * instead of rm_rf() so that 'x' is honoured. */ - log_debug("rm -rf \"%s\"", instance); - r = rm_rf(instance, (i->type == RECURSIVE_REMOVE_PATH ? REMOVE_ROOT|REMOVE_SUBVOLUME : 0) | REMOVE_PHYSICAL); - if (r < 0 && r != -ENOENT) - return log_error_errno(r, "rm_rf(%s): %m", instance); - - break; - - default: - assert_not_reached("wut?"); - } - - return 0; -} - -static int remove_item(Item *i) { - assert(i); - - log_debug("Running remove action for entry %c %s", (char) i->type, i->path); - - switch (i->type) { - - case REMOVE_PATH: - case TRUNCATE_DIRECTORY: - case RECURSIVE_REMOVE_PATH: - return glob_item(i, remove_item_instance, false); - - default: - return 0; - } -} - -static int clean_item_instance(Item *i, const char* instance) { - _cleanup_closedir_ DIR *d = NULL; - struct stat s, ps; - bool mountpoint; - usec_t cutoff, n; - char timestamp[FORMAT_TIMESTAMP_MAX]; - - assert(i); - - if (!i->age_set) - return 0; - - n = now(CLOCK_REALTIME); - if (n < i->age) - return 0; - - cutoff = n - i->age; - - d = opendir_nomod(instance); - if (!d) { - if (errno == ENOENT || errno == ENOTDIR) { - log_debug_errno(errno, "Directory \"%s\": %m", instance); - return 0; - } - - log_error_errno(errno, "Failed to open directory %s: %m", instance); - return -errno; - } - - if (fstat(dirfd(d), &s) < 0) - return log_error_errno(errno, "stat(%s) failed: %m", i->path); - - if (!S_ISDIR(s.st_mode)) { - log_error("%s is not a directory.", i->path); - return -ENOTDIR; - } - - if (fstatat(dirfd(d), "..", &ps, AT_SYMLINK_NOFOLLOW) != 0) - return log_error_errno(errno, "stat(%s/..) failed: %m", i->path); - - mountpoint = s.st_dev != ps.st_dev || s.st_ino == ps.st_ino; - - log_debug("Cleanup threshold for %s \"%s\" is %s", - mountpoint ? "mount point" : "directory", - instance, - format_timestamp_us(timestamp, sizeof(timestamp), cutoff)); - - return dir_cleanup(i, instance, d, &s, cutoff, s.st_dev, mountpoint, - MAX_DEPTH, i->keep_first_level); -} - -static int clean_item(Item *i) { - assert(i); - - log_debug("Running clean action for entry %c %s", (char) i->type, i->path); - - switch (i->type) { - case CREATE_DIRECTORY: - case CREATE_SUBVOLUME: - case CREATE_SUBVOLUME_INHERIT_QUOTA: - case CREATE_SUBVOLUME_NEW_QUOTA: - case EMPTY_DIRECTORY: - case TRUNCATE_DIRECTORY: - case IGNORE_PATH: - case COPY_FILES: - clean_item_instance(i, i->path); - return 0; - case IGNORE_DIRECTORY_PATH: - return glob_item(i, clean_item_instance, false); - default: - return 0; - } -} - -static int process_item_array(ItemArray *array); - -static int process_item(Item *i) { - int r, q, p, t = 0; - _cleanup_free_ char *prefix = NULL; - - assert(i); - - if (i->done) - return 0; - - i->done = true; - - prefix = malloc(strlen(i->path) + 1); - if (!prefix) - return log_oom(); - - PATH_FOREACH_PREFIX(prefix, i->path) { - ItemArray *j; - - j = ordered_hashmap_get(items, prefix); - if (j) { - int s; - - s = process_item_array(j); - if (s < 0 && t == 0) - t = s; - } - } - - r = arg_create ? create_item(i) : 0; - q = arg_remove ? remove_item(i) : 0; - p = arg_clean ? clean_item(i) : 0; - - return t < 0 ? t : - r < 0 ? r : - q < 0 ? q : - p; -} - -static int process_item_array(ItemArray *array) { - unsigned n; - int r = 0, k; - - assert(array); - - for (n = 0; n < array->count; n++) { - k = process_item(array->items + n); - if (k < 0 && r == 0) - r = k; - } - - return r; -} - -static void item_free_contents(Item *i) { - assert(i); - free(i->path); - free(i->argument); - strv_free(i->xattrs); - -#ifdef HAVE_ACL - acl_free(i->acl_access); - acl_free(i->acl_default); -#endif -} - -static void item_array_free(ItemArray *a) { - unsigned n; - - if (!a) - return; - - for (n = 0; n < a->count; n++) - item_free_contents(a->items + n); - free(a->items); - free(a); -} - -static int item_compare(const void *a, const void *b) { - const Item *x = a, *y = b; - - /* Make sure that the ownership taking item is put first, so - * that we first create the node, and then can adjust it */ - - if (takes_ownership(x->type) && !takes_ownership(y->type)) - return -1; - if (!takes_ownership(x->type) && takes_ownership(y->type)) - return 1; - - return (int) x->type - (int) y->type; -} - -static bool item_compatible(Item *a, Item *b) { - assert(a); - assert(b); - assert(streq(a->path, b->path)); - - if (takes_ownership(a->type) && takes_ownership(b->type)) - /* check if the items are the same */ - return streq_ptr(a->argument, b->argument) && - - a->uid_set == b->uid_set && - a->uid == b->uid && - - a->gid_set == b->gid_set && - a->gid == b->gid && - - a->mode_set == b->mode_set && - a->mode == b->mode && - - a->age_set == b->age_set && - a->age == b->age && - - a->mask_perms == b->mask_perms && - - a->keep_first_level == b->keep_first_level && - - a->major_minor == b->major_minor; - - return true; -} - -static bool should_include_path(const char *path) { - char **prefix; - - STRV_FOREACH(prefix, arg_exclude_prefixes) - if (path_startswith(path, *prefix)) { - log_debug("Entry \"%s\" matches exclude prefix \"%s\", skipping.", - path, *prefix); - return false; - } - - STRV_FOREACH(prefix, arg_include_prefixes) - if (path_startswith(path, *prefix)) { - log_debug("Entry \"%s\" matches include prefix \"%s\".", path, *prefix); - return true; - } - - /* no matches, so we should include this path only if we - * have no whitelist at all */ - if (strv_length(arg_include_prefixes) == 0) - return true; - - log_debug("Entry \"%s\" does not match any include prefix, skipping.", path); - return false; -} - -static int parse_line(const char *fname, unsigned line, const char *buffer) { - - _cleanup_free_ char *action = NULL, *mode = NULL, *user = NULL, *group = NULL, *age = NULL, *path = NULL; - _cleanup_(item_free_contents) Item i = {}; - ItemArray *existing; - OrderedHashmap *h; - int r, pos; - bool force = false, boot = false; - - assert(fname); - assert(line >= 1); - assert(buffer); - - r = extract_many_words( - &buffer, - NULL, - EXTRACT_QUOTES, - &action, - &path, - &mode, - &user, - &group, - &age, - NULL); - if (r < 0) - return log_error_errno(r, "[%s:%u] Failed to parse line: %m", fname, line); - else if (r < 2) { - log_error("[%s:%u] Syntax error.", fname, line); - return -EIO; - } - - if (!isempty(buffer) && !streq(buffer, "-")) { - i.argument = strdup(buffer); - if (!i.argument) - return log_oom(); - } - - if (isempty(action)) { - log_error("[%s:%u] Command too short '%s'.", fname, line, action); - return -EINVAL; - } - - for (pos = 1; action[pos]; pos++) { - if (action[pos] == '!' && !boot) - boot = true; - else if (action[pos] == '+' && !force) - force = true; - else { - log_error("[%s:%u] Unknown modifiers in command '%s'", - fname, line, action); - return -EINVAL; - } - } - - if (boot && !arg_boot) { - log_debug("Ignoring entry %s \"%s\" because --boot is not specified.", - action, path); - return 0; - } - - i.type = action[0]; - i.force = force; - - r = specifier_printf(path, specifier_table, NULL, &i.path); - if (r < 0) { - log_error("[%s:%u] Failed to replace specifiers: %s", fname, line, path); - return r; - } - - switch (i.type) { - - case CREATE_DIRECTORY: - case CREATE_SUBVOLUME: - case CREATE_SUBVOLUME_INHERIT_QUOTA: - case CREATE_SUBVOLUME_NEW_QUOTA: - case EMPTY_DIRECTORY: - case TRUNCATE_DIRECTORY: - case CREATE_FIFO: - case IGNORE_PATH: - case IGNORE_DIRECTORY_PATH: - case REMOVE_PATH: - case RECURSIVE_REMOVE_PATH: - case ADJUST_MODE: - case RELABEL_PATH: - case RECURSIVE_RELABEL_PATH: - if (i.argument) - log_warning("[%s:%u] %c lines don't take argument fields, ignoring.", fname, line, i.type); - - break; - - case CREATE_FILE: - case TRUNCATE_FILE: - break; - - case CREATE_SYMLINK: - if (!i.argument) { - i.argument = strappend("/usr/share/factory/", i.path); - if (!i.argument) - return log_oom(); - } - break; - - case WRITE_FILE: - if (!i.argument) { - log_error("[%s:%u] Write file requires argument.", fname, line); - return -EBADMSG; - } - break; - - case COPY_FILES: - if (!i.argument) { - i.argument = strappend("/usr/share/factory/", i.path); - if (!i.argument) - return log_oom(); - } else if (!path_is_absolute(i.argument)) { - log_error("[%s:%u] Source path is not absolute.", fname, line); - return -EBADMSG; - } - - path_kill_slashes(i.argument); - 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); - return -EBADMSG; - } - - 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); - return -EBADMSG; - } - - i.major_minor = makedev(major, minor); - break; - } - - case SET_XATTR: - case RECURSIVE_SET_XATTR: - if (!i.argument) { - log_error("[%s:%u] Set extended attribute requires argument.", fname, line); - return -EBADMSG; - } - r = parse_xattrs_from_arg(&i); - if (r < 0) - return r; - break; - - case SET_ACL: - case RECURSIVE_SET_ACL: - if (!i.argument) { - log_error("[%s:%u] Set ACLs requires argument.", fname, line); - return -EBADMSG; - } - r = parse_acls_from_arg(&i); - if (r < 0) - return r; - break; - - case SET_ATTRIBUTE: - case RECURSIVE_SET_ATTRIBUTE: - if (!i.argument) { - log_error("[%s:%u] Set file attribute requires argument.", fname, line); - return -EBADMSG; - } - r = parse_attribute_from_arg(&i); - if (r < 0) - return r; - break; - - default: - log_error("[%s:%u] Unknown command type '%c'.", fname, line, (char) i.type); - return -EBADMSG; - } - - if (!path_is_absolute(i.path)) { - log_error("[%s:%u] Path '%s' not absolute.", fname, line, i.path); - return -EBADMSG; - } - - path_kill_slashes(i.path); - - if (!should_include_path(i.path)) - return 0; - - if (arg_root) { - char *p; - - p = prefix_root(arg_root, i.path); - if (!p) - return log_oom(); - - free(i.path); - i.path = p; - } - - if (!isempty(user) && !streq(user, "-")) { - const char *u = user; - - r = get_user_creds(&u, &i.uid, NULL, NULL, NULL); - if (r < 0) { - log_error("[%s:%u] Unknown user '%s'.", fname, line, user); - return r; - } - - i.uid_set = true; - } - - if (!isempty(group) && !streq(group, "-")) { - const char *g = group; - - r = get_group_creds(&g, &i.gid); - if (r < 0) { - log_error("[%s:%u] Unknown group '%s'.", fname, line, group); - return r; - } - - i.gid_set = true; - } - - if (!isempty(mode) && !streq(mode, "-")) { - const char *mm = mode; - unsigned m; - - if (*mm == '~') { - i.mask_perms = true; - mm++; - } - - if (parse_mode(mm, &m) < 0) { - log_error("[%s:%u] Invalid mode '%s'.", fname, line, mode); - return -EBADMSG; - } - - i.mode = m; - i.mode_set = true; - } else - i.mode = IN_SET(i.type, CREATE_DIRECTORY, TRUNCATE_DIRECTORY, CREATE_SUBVOLUME, CREATE_SUBVOLUME_INHERIT_QUOTA, CREATE_SUBVOLUME_NEW_QUOTA) ? 0755 : 0644; - - if (!isempty(age) && !streq(age, "-")) { - const char *a = age; - - if (*a == '~') { - i.keep_first_level = true; - a++; - } - - if (parse_sec(a, &i.age) < 0) { - log_error("[%s:%u] Invalid age '%s'.", fname, line, age); - return -EBADMSG; - } - - i.age_set = true; - } - - h = needs_glob(i.type) ? globs : items; - - existing = ordered_hashmap_get(h, i.path); - if (existing) { - unsigned n; - - for (n = 0; n < existing->count; n++) { - if (!item_compatible(existing->items + n, &i)) { - log_warning("[%s:%u] Duplicate line for path \"%s\", ignoring.", - fname, line, i.path); - return 0; - } - } - } else { - existing = new0(ItemArray, 1); - r = ordered_hashmap_put(h, i.path, existing); - if (r < 0) - return log_oom(); - } - - if (!GREEDY_REALLOC(existing->items, existing->size, existing->count + 1)) - return log_oom(); - - memcpy(existing->items + existing->count++, &i, sizeof(i)); - - /* Sort item array, to enforce stable ordering of application */ - qsort_safe(existing->items, existing->count, sizeof(Item), item_compare); - - zero(i); - return 0; -} - -static void help(void) { - printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n" - "Creates, deletes and cleans up volatile and temporary files and directories.\n\n" - " -h --help Show this help\n" - " --version Show package version\n" - " --create Create marked files/directories\n" - " --clean Clean up marked directories\n" - " --remove Remove marked files/directories\n" - " --boot Execute actions only safe at boot\n" - " --prefix=PATH Only apply rules with the specified prefix\n" - " --exclude-prefix=PATH Ignore rules with the specified prefix\n" - " --root=PATH Operate on an alternate filesystem root\n", - program_invocation_short_name); -} - -static int parse_argv(int argc, char *argv[]) { - - enum { - ARG_VERSION = 0x100, - ARG_CREATE, - ARG_CLEAN, - ARG_REMOVE, - ARG_BOOT, - ARG_PREFIX, - ARG_EXCLUDE_PREFIX, - ARG_ROOT, - }; - - static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, ARG_VERSION }, - { "create", no_argument, NULL, ARG_CREATE }, - { "clean", no_argument, NULL, ARG_CLEAN }, - { "remove", no_argument, NULL, ARG_REMOVE }, - { "boot", no_argument, NULL, ARG_BOOT }, - { "prefix", required_argument, NULL, ARG_PREFIX }, - { "exclude-prefix", required_argument, NULL, ARG_EXCLUDE_PREFIX }, - { "root", required_argument, NULL, ARG_ROOT }, - {} - }; - - int c, r; - - assert(argc >= 0); - assert(argv); - - while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) - - switch (c) { - - case 'h': - help(); - return 0; - - case ARG_VERSION: - return version(); - - case ARG_CREATE: - arg_create = true; - break; - - case ARG_CLEAN: - arg_clean = true; - break; - - case ARG_REMOVE: - arg_remove = true; - break; - - case ARG_BOOT: - arg_boot = true; - break; - - case ARG_PREFIX: - if (strv_push(&arg_include_prefixes, optarg) < 0) - return log_oom(); - break; - - case ARG_EXCLUDE_PREFIX: - if (strv_push(&arg_exclude_prefixes, optarg) < 0) - return log_oom(); - break; - - case ARG_ROOT: - r = parse_path_argument_and_warn(optarg, true, &arg_root); - if (r < 0) - return r; - break; - - case '?': - return -EINVAL; - - default: - assert_not_reached("Unhandled option"); - } - - if (!arg_clean && !arg_create && !arg_remove) { - log_error("You need to specify at least one of --clean, --create or --remove."); - return -EINVAL; - } - - return 1; -} - -static int read_config_file(const char *fn, bool ignore_enoent) { - _cleanup_fclose_ FILE *_f = NULL; - FILE *f; - char line[LINE_MAX]; - Iterator iterator; - unsigned v = 0; - Item *i; - int r = 0; - - assert(fn); - - if (streq(fn, "-")) { - log_debug("Reading config from stdin."); - fn = ""; - f = stdin; - } else { - r = search_and_fopen_nulstr(fn, "re", arg_root, conf_file_dirs, &_f); - if (r < 0) { - if (ignore_enoent && r == -ENOENT) { - log_debug_errno(r, "Failed to open \"%s\", ignoring: %m", fn); - return 0; - } - - return log_error_errno(r, "Failed to open '%s': %m", fn); - } - log_debug("Reading config file \"%s\".", fn); - f = _f; - } - - FOREACH_LINE(line, f, break) { - char *l; - int k; - - v++; - - l = strstrip(line); - if (*l == '#' || *l == 0) - continue; - - k = parse_line(fn, v, l); - if (k < 0 && r == 0) - r = k; - } - - /* we have to determine age parameter for each entry of type X */ - ORDERED_HASHMAP_FOREACH(i, globs, iterator) { - Iterator iter; - Item *j, *candidate_item = NULL; - - if (i->type != IGNORE_DIRECTORY_PATH) - continue; - - ORDERED_HASHMAP_FOREACH(j, items, iter) { - if (!IN_SET(j->type, CREATE_DIRECTORY, TRUNCATE_DIRECTORY, CREATE_SUBVOLUME, CREATE_SUBVOLUME_INHERIT_QUOTA, CREATE_SUBVOLUME_NEW_QUOTA)) - continue; - - if (path_equal(j->path, i->path)) { - candidate_item = j; - break; - } - - if ((!candidate_item && path_startswith(i->path, j->path)) || - (candidate_item && path_startswith(j->path, candidate_item->path) && (fnmatch(i->path, j->path, FNM_PATHNAME | FNM_PERIOD) == 0))) - candidate_item = j; - } - - if (candidate_item && candidate_item->age_set) { - i->age = candidate_item->age; - i->age_set = true; - } - } - - if (ferror(f)) { - log_error_errno(errno, "Failed to read from file %s: %m", fn); - if (r == 0) - r = -EIO; - } - - return r; -} - -int main(int argc, char *argv[]) { - int r, k; - ItemArray *a; - Iterator iterator; - - r = parse_argv(argc, argv); - if (r <= 0) - goto finish; - - log_set_target(LOG_TARGET_AUTO); - log_parse_environment(); - log_open(); - - umask(0022); - - mac_selinux_init(); - - items = ordered_hashmap_new(&string_hash_ops); - globs = ordered_hashmap_new(&string_hash_ops); - - if (!items || !globs) { - r = log_oom(); - goto finish; - } - - r = 0; - - if (optind < argc) { - int j; - - for (j = optind; j < argc; j++) { - k = read_config_file(argv[j], false); - if (k < 0 && r == 0) - r = k; - } - - } else { - _cleanup_strv_free_ char **files = NULL; - char **f; - - r = conf_files_list_nulstr(&files, ".conf", arg_root, conf_file_dirs); - if (r < 0) { - log_error_errno(r, "Failed to enumerate tmpfiles.d files: %m"); - goto finish; - } - - STRV_FOREACH(f, files) { - k = read_config_file(*f, true); - if (k < 0 && r == 0) - r = k; - } - } - - /* The non-globbing ones usually create things, hence we apply - * them first */ - ORDERED_HASHMAP_FOREACH(a, items, iterator) { - k = process_item_array(a); - if (k < 0 && r == 0) - r = k; - } - - /* The globbing ones usually alter things, hence we apply them - * second. */ - ORDERED_HASHMAP_FOREACH(a, globs, iterator) { - k = process_item_array(a); - if (k < 0 && r == 0) - r = k; - } - -finish: - while ((a = ordered_hashmap_steal_first(items))) - item_array_free(a); - - while ((a = ordered_hashmap_steal_first(globs))) - item_array_free(a); - - ordered_hashmap_free(items); - ordered_hashmap_free(globs); - - free(arg_include_prefixes); - free(arg_exclude_prefixes); - free(arg_root); - - set_free_free(unix_sockets); - - mac_selinux_finish(); - - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; -} diff --git a/src/grp-initutils/systemd-update-done/Makefile b/src/grp-initutils/systemd-update-done/Makefile deleted file mode 100644 index 90cb440874..0000000000 --- a/src/grp-initutils/systemd-update-done/Makefile +++ /dev/null @@ -1,34 +0,0 @@ -# -*- Mode: makefile; indent-tabs-mode: t -*- -# -# This file is part of systemd. -# -# Copyright 2010-2012 Lennart Poettering -# Copyright 2010-2012 Kay Sievers -# Copyright 2013 Zbigniew Jędrzejewski-Szmek -# Copyright 2013 David Strauss -# Copyright 2016 Luke Shumaker -# -# systemd is free software; you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation; either version 2.1 of the License, or -# (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 -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with systemd; If not, see . -include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk -include $(topsrcdir)/build-aux/Makefile.head.mk - -rootlibexec_PROGRAMS += systemd-update-done - -systemd_update_done_SOURCES = \ - src/update-done/update-done.c - -systemd_update_done_LDADD = \ - libshared.la - -include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-initutils/systemd-update-done/update-done.c b/src/grp-initutils/systemd-update-done/update-done.c deleted file mode 100644 index 39d19ec048..0000000000 --- a/src/grp-initutils/systemd-update-done/update-done.c +++ /dev/null @@ -1,115 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "basic/fd-util.h" -#include "basic/io-util.h" -#include "basic/selinux-util.h" -#include "basic/util.h" - -#define MESSAGE \ - "This file was created by systemd-update-done. Its only \n" \ - "purpose is to hold a timestamp of the time this directory\n" \ - "was updated. See systemd-update-done.service(8).\n" - -static int apply_timestamp(const char *path, struct timespec *ts) { - struct timespec twice[2] = { - *ts, - *ts - }; - struct stat st; - - assert(path); - assert(ts); - - if (stat(path, &st) >= 0) { - /* Is the timestamp file already newer than the OS? If - * so, there's nothing to do. We ignore the nanosecond - * component of the timestamp, since some file systems - * do not support any better accuracy than 1s and we - * have no way to identify the accuracy - * available. Most notably ext4 on small disks (where - * 128 byte inodes are used) does not support better - * accuracy than 1s. */ - if (st.st_mtim.tv_sec > ts->tv_sec) - return 0; - - /* It is older? Then let's update it */ - if (utimensat(AT_FDCWD, path, twice, AT_SYMLINK_NOFOLLOW) < 0) { - - if (errno == EROFS) - return log_debug("Can't update timestamp file %s, file system is read-only.", path); - - return log_error_errno(errno, "Failed to update timestamp on %s: %m", path); - } - - } else if (errno == ENOENT) { - _cleanup_close_ int fd = -1; - int r; - - /* The timestamp file doesn't exist yet? Then let's create it. */ - - r = mac_selinux_create_file_prepare(path, S_IFREG); - if (r < 0) - return log_error_errno(r, "Failed to set SELinux context for %s: %m", path); - - fd = open(path, O_CREAT|O_EXCL|O_WRONLY|O_TRUNC|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0644); - mac_selinux_create_file_clear(); - - if (fd < 0) { - if (errno == EROFS) - return log_debug("Can't create timestamp file %s, file system is read-only.", path); - - return log_error_errno(errno, "Failed to create timestamp file %s: %m", path); - } - - (void) loop_write(fd, MESSAGE, strlen(MESSAGE), false); - - if (futimens(fd, twice) < 0) - return log_error_errno(errno, "Failed to update timestamp on %s: %m", path); - } else - log_error_errno(errno, "Failed to stat() timestamp file %s: %m", path); - - return 0; -} - -int main(int argc, char *argv[]) { - struct stat st; - int r, q = 0; - - log_set_target(LOG_TARGET_AUTO); - log_parse_environment(); - log_open(); - - if (stat("/usr", &st) < 0) { - log_error_errno(errno, "Failed to stat /usr: %m"); - return EXIT_FAILURE; - } - - r = mac_selinux_init(); - if (r < 0) { - log_error_errno(r, "SELinux setup failed: %m"); - goto finish; - } - - r = apply_timestamp("/etc/.updated", &st.st_mtim); - q = apply_timestamp("/var/.updated", &st.st_mtim); - -finish: - return r < 0 || q < 0 ? EXIT_FAILURE : EXIT_SUCCESS; -} diff --git a/src/grp-initutils/systemd-update-utmp/Makefile b/src/grp-initutils/systemd-update-utmp/Makefile deleted file mode 100644 index 0107808281..0000000000 --- a/src/grp-initutils/systemd-update-utmp/Makefile +++ /dev/null @@ -1,42 +0,0 @@ -# -*- Mode: makefile; indent-tabs-mode: t -*- -# -# This file is part of systemd. -# -# Copyright 2010-2012 Lennart Poettering -# Copyright 2010-2012 Kay Sievers -# Copyright 2013 Zbigniew Jędrzejewski-Szmek -# Copyright 2013 David Strauss -# Copyright 2016 Luke Shumaker -# -# systemd is free software; you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation; either version 2.1 of the License, or -# (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 -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with systemd; If not, see . -include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk -include $(topsrcdir)/build-aux/Makefile.head.mk - -ifneq ($(HAVE_UTMP),) -rootlibexec_PROGRAMS += \ - systemd-update-utmp -endif # HAVE_UTMP - -systemd_update_utmp_SOURCES = \ - src/update-utmp/update-utmp.c - -systemd_update_utmp_CFLAGS = \ - $(AM_CFLAGS) \ - $(AUDIT_CFLAGS) - -systemd_update_utmp_LDADD = \ - libshared.la \ - $(AUDIT_LIBS) - -include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-initutils/systemd-update-utmp/update-utmp.c b/src/grp-initutils/systemd-update-utmp/update-utmp.c deleted file mode 100644 index 69264c3a26..0000000000 --- a/src/grp-initutils/systemd-update-utmp/update-utmp.c +++ /dev/null @@ -1,283 +0,0 @@ -/*** - 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 Lesser General Public License as published by - the Free Software Foundation; either version 2.1 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include - -#ifdef HAVE_AUDIT -#include -#endif - -#include - -#include "basic/alloc-util.h" -#include "sd-bus/bus-error.h" -#include "shared/bus-util.h" -#include "basic/formats-util.h" -#include "basic/log.h" -#include "basic/macro.h" -#include "basic/special.h" -#include "basic/unit-name.h" -#include "basic/util.h" -#include "shared/utmp-wtmp.h" - -typedef struct Context { - sd_bus *bus; -#ifdef HAVE_AUDIT - int audit_fd; -#endif -} Context; - -static usec_t get_startup_time(Context *c) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - usec_t t = 0; - int r; - - assert(c); - - r = sd_bus_get_property_trivial( - c->bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "UserspaceTimestamp", - &error, - 't', &t); - if (r < 0) { - log_error_errno(r, "Failed to get timestamp: %s", bus_error_message(&error, r)); - return 0; - } - - return t; -} - -static int get_current_runlevel(Context *c) { - static const struct { - const int runlevel; - const char *special; - } table[] = { - /* The first target of this list that is active or has - * a job scheduled wins. We prefer runlevels 5 and 3 - * here over the others, since these are the main - * runlevels used on Fedora. It might make sense to - * change the order on some distributions. */ - { '5', SPECIAL_GRAPHICAL_TARGET }, - { '3', SPECIAL_MULTI_USER_TARGET }, - { '1', SPECIAL_RESCUE_TARGET }, - }; - - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - int r; - unsigned i; - - assert(c); - - for (i = 0; i < ELEMENTSOF(table); i++) { - _cleanup_free_ char *state = NULL, *path = NULL; - - path = unit_dbus_path_from_name(table[i].special); - if (!path) - return log_oom(); - - r = sd_bus_get_property_string( - c->bus, - "org.freedesktop.systemd1", - path, - "org.freedesktop.systemd1.Unit", - "ActiveState", - &error, - &state); - if (r < 0) - return log_warning_errno(r, "Failed to get state: %s", bus_error_message(&error, r)); - - if (streq(state, "active") || streq(state, "reloading")) - return table[i].runlevel; - } - - return 0; -} - -static int on_reboot(Context *c) { - int r = 0, q; - usec_t t; - - assert(c); - - /* We finished start-up, so let's write the utmp - * record and send the audit msg */ - -#ifdef HAVE_AUDIT - if (c->audit_fd >= 0) - if (audit_log_user_comm_message(c->audit_fd, AUDIT_SYSTEM_BOOT, "", "systemd-update-utmp", NULL, NULL, NULL, 1) < 0 && - errno != EPERM) { - r = log_error_errno(errno, "Failed to send audit message: %m"); - } -#endif - - /* If this call fails it will return 0, which - * utmp_put_reboot() will then fix to the current time */ - t = get_startup_time(c); - - q = utmp_put_reboot(t); - if (q < 0) { - log_error_errno(q, "Failed to write utmp record: %m"); - r = q; - } - - return r; -} - -static int on_shutdown(Context *c) { - int r = 0, q; - - assert(c); - - /* We started shut-down, so let's write the utmp - * record and send the audit msg */ - -#ifdef HAVE_AUDIT - if (c->audit_fd >= 0) - if (audit_log_user_comm_message(c->audit_fd, AUDIT_SYSTEM_SHUTDOWN, "", "systemd-update-utmp", NULL, NULL, NULL, 1) < 0 && - errno != EPERM) { - r = log_error_errno(errno, "Failed to send audit message: %m"); - } -#endif - - q = utmp_put_shutdown(); - if (q < 0) { - log_error_errno(q, "Failed to write utmp record: %m"); - r = q; - } - - return r; -} - -static int on_runlevel(Context *c) { - int r = 0, q, previous, runlevel; - - assert(c); - - /* We finished changing runlevel, so let's write the - * utmp record and send the audit msg */ - - /* First, get last runlevel */ - q = utmp_get_runlevel(&previous, NULL); - - if (q < 0) { - if (q != -ESRCH && q != -ENOENT) - return log_error_errno(q, "Failed to get current runlevel: %m"); - - previous = 0; - } - - /* Secondly, get new runlevel */ - runlevel = get_current_runlevel(c); - - if (runlevel < 0) - return runlevel; - - if (previous == runlevel) - return 0; - -#ifdef HAVE_AUDIT - if (c->audit_fd >= 0) { - _cleanup_free_ char *s = NULL; - - if (asprintf(&s, "old-level=%c new-level=%c", - previous > 0 ? previous : 'N', - runlevel > 0 ? runlevel : 'N') < 0) - return log_oom(); - - if (audit_log_user_comm_message(c->audit_fd, AUDIT_SYSTEM_RUNLEVEL, s, "systemd-update-utmp", NULL, NULL, NULL, 1) < 0 && errno != EPERM) - r = log_error_errno(errno, "Failed to send audit message: %m"); - } -#endif - - q = utmp_put_runlevel(runlevel, previous); - if (q < 0 && q != -ESRCH && q != -ENOENT) { - log_error_errno(q, "Failed to write utmp record: %m"); - r = q; - } - - return r; -} - -int main(int argc, char *argv[]) { - Context c = { -#ifdef HAVE_AUDIT - .audit_fd = -1 -#endif - }; - int r; - - if (getppid() != 1) { - log_error("This program should be invoked by init only."); - return EXIT_FAILURE; - } - - if (argc != 2) { - log_error("This program requires one argument."); - return EXIT_FAILURE; - } - - log_set_target(LOG_TARGET_AUTO); - log_parse_environment(); - log_open(); - - umask(0022); - -#ifdef HAVE_AUDIT - /* If the kernel lacks netlink or audit support, - * don't worry about it. */ - c.audit_fd = audit_open(); - if (c.audit_fd < 0 && errno != EAFNOSUPPORT && errno != EPROTONOSUPPORT) - log_error_errno(errno, "Failed to connect to audit log: %m"); -#endif - r = bus_connect_system_systemd(&c.bus); - if (r < 0) { - log_error_errno(r, "Failed to get D-Bus connection: %m"); - r = -EIO; - goto finish; - } - - log_debug("systemd-update-utmp running as pid "PID_FMT, getpid()); - - if (streq(argv[1], "reboot")) - r = on_reboot(&c); - else if (streq(argv[1], "shutdown")) - r = on_shutdown(&c); - else if (streq(argv[1], "runlevel")) - r = on_runlevel(&c); - else { - log_error("Unknown command %s", argv[1]); - r = -EINVAL; - } - - log_debug("systemd-update-utmp stopped as pid "PID_FMT, getpid()); - -finish: -#ifdef HAVE_AUDIT - if (c.audit_fd >= 0) - audit_close(c.audit_fd); -#endif - - sd_bus_flush_close_unref(c.bus); - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; -} diff --git a/src/grp-initutils/systemd-user-sessions/Makefile b/src/grp-initutils/systemd-user-sessions/Makefile deleted file mode 100644 index 66fa3b7059..0000000000 --- a/src/grp-initutils/systemd-user-sessions/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# -*- Mode: makefile; indent-tabs-mode: t -*- -# -# This file is part of systemd. -# -# Copyright 2010-2012 Lennart Poettering -# Copyright 2010-2012 Kay Sievers -# Copyright 2013 Zbigniew Jędrzejewski-Szmek -# Copyright 2013 David Strauss -# Copyright 2016 Luke Shumaker -# -# systemd is free software; you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation; either version 2.1 of the License, or -# (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 -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with systemd; If not, see . -include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk -include $(topsrcdir)/build-aux/Makefile.head.mk - -ifneq ($(HAVE_PAM),) - -systemd_user_sessions_SOURCES = \ - src/user-sessions/user-sessions.c - -systemd_user_sessions_LDADD = \ - libshared.la - -rootlibexec_PROGRAMS += \ - systemd-user-sessions - -nodist_systemunit_DATA += \ - units/systemd-user-sessions.service - -MULTI_USER_TARGET_WANTS += \ - systemd-user-sessions.service - -endif # HAVE_PAM - -EXTRA_DIST += \ - units/systemd-user-sessions.service.in - -include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-initutils/systemd-user-sessions/user-sessions.c b/src/grp-initutils/systemd-user-sessions/user-sessions.c deleted file mode 100644 index 639c0200e6..0000000000 --- a/src/grp-initutils/systemd-user-sessions/user-sessions.c +++ /dev/null @@ -1,84 +0,0 @@ -/*** - 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 Lesser General Public License as published by - the Free Software Foundation; either version 2.1 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include - -#include "basic/fileio-label.h" -#include "basic/fileio.h" -#include "basic/log.h" -#include "basic/selinux-util.h" -#include "basic/string-util.h" -#include "basic/util.h" - -int main(int argc, char*argv[]) { - - if (argc != 2) { - log_error("This program requires one argument."); - return EXIT_FAILURE; - } - - log_set_target(LOG_TARGET_AUTO); - log_parse_environment(); - log_open(); - - umask(0022); - - mac_selinux_init(); - - if (streq(argv[1], "start")) { - int r = 0; - - if (unlink("/run/nologin") < 0 && errno != ENOENT) - r = log_error_errno(errno, - "Failed to remove /run/nologin file: %m"); - - if (unlink("/etc/nologin") < 0 && errno != ENOENT) { - /* If the file doesn't exist and /etc simply - * was read-only (in which case unlink() - * returns EROFS even if the file doesn't - * exist), don't complain */ - - if (errno != EROFS || access("/etc/nologin", F_OK) >= 0) { - log_error_errno(errno, "Failed to remove /etc/nologin file: %m"); - return EXIT_FAILURE; - } - } - - if (r < 0) - return EXIT_FAILURE; - - } else if (streq(argv[1], "stop")) { - int r; - - r = write_string_file_atomic_label("/run/nologin", "System is going down."); - if (r < 0) { - log_error_errno(r, "Failed to create /run/nologin: %m"); - return EXIT_FAILURE; - } - - } else { - log_error("Unknown verb %s.", argv[1]); - return EXIT_FAILURE; - } - - mac_selinux_finish(); - - return EXIT_SUCCESS; -} diff --git a/src/grp-initutils/systemd-vconsole-setup/.gitignore b/src/grp-initutils/systemd-vconsole-setup/.gitignore deleted file mode 100644 index 82741b2fb3..0000000000 --- a/src/grp-initutils/systemd-vconsole-setup/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/90-vconsole.rules diff --git a/src/grp-initutils/systemd-vconsole-setup/90-vconsole.rules.in b/src/grp-initutils/systemd-vconsole-setup/90-vconsole.rules.in deleted file mode 100644 index 35b9ad5151..0000000000 --- a/src/grp-initutils/systemd-vconsole-setup/90-vconsole.rules.in +++ /dev/null @@ -1,10 +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 Lesser General Public License as published by -# the Free Software Foundation; either version 2.1 of the License, or -# (at your option) any later version. - -# Each vtcon keeps its own state of fonts. -# -ACTION=="add", SUBSYSTEM=="vtconsole", KERNEL=="vtcon*", RUN+="@rootlibexecdir@/systemd-vconsole-setup" diff --git a/src/grp-initutils/systemd-vconsole-setup/Makefile b/src/grp-initutils/systemd-vconsole-setup/Makefile deleted file mode 100644 index 99b369967e..0000000000 --- a/src/grp-initutils/systemd-vconsole-setup/Makefile +++ /dev/null @@ -1,50 +0,0 @@ -# -*- Mode: makefile; indent-tabs-mode: t -*- -# -# This file is part of systemd. -# -# Copyright 2010-2012 Lennart Poettering -# Copyright 2010-2012 Kay Sievers -# Copyright 2013 Zbigniew Jędrzejewski-Szmek -# Copyright 2013 David Strauss -# Copyright 2016 Luke Shumaker -# -# systemd is free software; you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation; either version 2.1 of the License, or -# (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 -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with systemd; If not, see . -include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk -include $(topsrcdir)/build-aux/Makefile.head.mk - -ifneq ($(ENABLE_VCONSOLE),) -systemd_vconsole_setup_SOURCES = \ - src/vconsole/vconsole-setup.c - -systemd_vconsole_setup_LDADD = \ - libshared.la - -rootlibexec_PROGRAMS += \ - systemd-vconsole-setup - -nodist_udevrules_DATA += \ - src/vconsole/90-vconsole.rules - -nodist_systemunit_DATA += \ - units/systemd-vconsole-setup.service - -SYSINIT_TARGET_WANTS += \ - systemd-vconsole-setup.service -endif # ENABLE_VCONSOLE - -EXTRA_DIST += \ - src/vconsole/90-vconsole.rules.in \ - units/systemd-vconsole-setup.service.in - -include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-initutils/systemd-vconsole-setup/vconsole-setup.c b/src/grp-initutils/systemd-vconsole-setup/vconsole-setup.c deleted file mode 100644 index 87c0c2f066..0000000000 --- a/src/grp-initutils/systemd-vconsole-setup/vconsole-setup.c +++ /dev/null @@ -1,333 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Kay Sievers - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "basic/alloc-util.h" -#include "basic/fd-util.h" -#include "basic/fileio.h" -#include "basic/io-util.h" -#include "basic/locale-util.h" -#include "basic/log.h" -#include "basic/process-util.h" -#include "basic/signal-util.h" -#include "basic/stdio-util.h" -#include "basic/string-util.h" -#include "basic/terminal-util.h" -#include "basic/util.h" -#include "basic/virt.h" - -static bool is_vconsole(int fd) { - unsigned char data[1]; - - data[0] = TIOCL_GETFGCONSOLE; - return ioctl(fd, TIOCLINUX, data) >= 0; -} - -static int disable_utf8(int fd) { - int r = 0, k; - - if (ioctl(fd, KDSKBMODE, K_XLATE) < 0) - r = -errno; - - k = loop_write(fd, "\033%@", 3, false); - if (k < 0) - r = k; - - k = write_string_file("/sys/module/vt/parameters/default_utf8", "0", 0); - if (k < 0) - r = k; - - if (r < 0) - log_warning_errno(r, "Failed to disable UTF-8: %m"); - - return r; -} - -static int enable_utf8(int fd) { - int r = 0, k; - long current = 0; - - if (ioctl(fd, KDGKBMODE, ¤t) < 0 || current == K_XLATE) { - /* - * Change the current keyboard to unicode, unless it - * is currently in raw or off mode anyway. We - * shouldn't interfere with X11's processing of the - * key events. - * - * http://lists.freedesktop.org/archives/systemd-devel/2013-February/008573.html - * - */ - - if (ioctl(fd, KDSKBMODE, K_UNICODE) < 0) - r = -errno; - } - - k = loop_write(fd, "\033%G", 3, false); - if (k < 0) - r = k; - - k = write_string_file("/sys/module/vt/parameters/default_utf8", "1", 0); - if (k < 0) - r = k; - - if (r < 0) - log_warning_errno(r, "Failed to enable UTF-8: %m"); - - return r; -} - -static int keyboard_load_and_wait(const char *vc, const char *map, const char *map_toggle, bool utf8) { - const char *args[8]; - int i = 0, r; - pid_t pid; - - /* An empty map means kernel map */ - if (isempty(map)) - return 1; - - args[i++] = KBD_LOADKEYS; - args[i++] = "-q"; - args[i++] = "-C"; - args[i++] = vc; - if (utf8) - args[i++] = "-u"; - args[i++] = map; - if (map_toggle) - args[i++] = map_toggle; - args[i++] = NULL; - - pid = fork(); - if (pid < 0) - return log_error_errno(errno, "Failed to fork: %m"); - else if (pid == 0) { - - (void) reset_all_signal_handlers(); - (void) reset_signal_mask(); - - execv(args[0], (char **) args); - _exit(EXIT_FAILURE); - } - - r = wait_for_terminate_and_warn(KBD_LOADKEYS, pid, true); - if (r < 0) - return r; - - return r == 0; -} - -static int font_load_and_wait(const char *vc, const char *font, const char *map, const char *unimap) { - const char *args[9]; - int i = 0, r; - pid_t pid; - - /* An empty font means kernel font */ - if (isempty(font)) - return 1; - - args[i++] = KBD_SETFONT; - args[i++] = "-C"; - args[i++] = vc; - args[i++] = font; - if (map) { - args[i++] = "-m"; - args[i++] = map; - } - if (unimap) { - args[i++] = "-u"; - args[i++] = unimap; - } - args[i++] = NULL; - - pid = fork(); - if (pid < 0) - return log_error_errno(errno, "Failed to fork: %m"); - else if (pid == 0) { - - (void) reset_all_signal_handlers(); - (void) reset_signal_mask(); - - execv(args[0], (char **) args); - _exit(EXIT_FAILURE); - } - - r = wait_for_terminate_and_warn(KBD_SETFONT, pid, true); - if (r < 0) - return r; - - return r == 0; -} - -/* - * A newly allocated VT uses the font from the active VT. Here - * we update all possibly already allocated VTs with the configured - * font. It also allows to restart systemd-vconsole-setup.service, - * to apply a new font to all VTs. - */ -static void font_copy_to_all_vcs(int fd) { - struct vt_stat vcs = {}; - unsigned char map8[E_TABSZ]; - unsigned short map16[E_TABSZ]; - struct unimapdesc unimapd; - _cleanup_free_ struct unipair* unipairs = NULL; - int i, r; - - unipairs = new(struct unipair, USHRT_MAX); - if (!unipairs) { - log_oom(); - return; - } - - /* get active, and 16 bit mask of used VT numbers */ - r = ioctl(fd, VT_GETSTATE, &vcs); - if (r < 0) { - log_debug_errno(errno, "VT_GETSTATE failed, ignoring: %m"); - return; - } - - for (i = 1; i <= 15; i++) { - char vcname[strlen("/dev/vcs") + DECIMAL_STR_MAX(int)]; - _cleanup_close_ int vcfd = -1; - struct console_font_op cfo = {}; - - if (i == vcs.v_active) - continue; - - /* skip non-allocated ttys */ - xsprintf(vcname, "/dev/vcs%i", i); - if (access(vcname, F_OK) < 0) - continue; - - xsprintf(vcname, "/dev/tty%i", i); - vcfd = open_terminal(vcname, O_RDWR|O_CLOEXEC); - if (vcfd < 0) - continue; - - /* copy font from active VT, where the font was uploaded to */ - cfo.op = KD_FONT_OP_COPY; - cfo.height = vcs.v_active-1; /* tty1 == index 0 */ - (void) ioctl(vcfd, KDFONTOP, &cfo); - - /* copy map of 8bit chars */ - if (ioctl(fd, GIO_SCRNMAP, map8) >= 0) - (void) ioctl(vcfd, PIO_SCRNMAP, map8); - - /* copy map of 8bit chars -> 16bit Unicode values */ - if (ioctl(fd, GIO_UNISCRNMAP, map16) >= 0) - (void) ioctl(vcfd, PIO_UNISCRNMAP, map16); - - /* copy unicode translation table */ - /* unimapd is a ushort count and a pointer to an - array of struct unipair { ushort, ushort } */ - unimapd.entries = unipairs; - unimapd.entry_ct = USHRT_MAX; - if (ioctl(fd, GIO_UNIMAP, &unimapd) >= 0) { - struct unimapinit adv = { 0, 0, 0 }; - - (void) ioctl(vcfd, PIO_UNIMAPCLR, &adv); - (void) ioctl(vcfd, PIO_UNIMAP, &unimapd); - } - } -} - -int main(int argc, char **argv) { - const char *vc; - _cleanup_free_ char - *vc_keymap = NULL, *vc_keymap_toggle = NULL, - *vc_font = NULL, *vc_font_map = NULL, *vc_font_unimap = NULL; - _cleanup_close_ int fd = -1; - bool utf8, font_copy = false, font_ok, keyboard_ok; - int r = EXIT_FAILURE; - - log_set_target(LOG_TARGET_AUTO); - log_parse_environment(); - log_open(); - - umask(0022); - - if (argv[1]) - vc = argv[1]; - else { - vc = "/dev/tty0"; - font_copy = true; - } - - fd = open_terminal(vc, O_RDWR|O_CLOEXEC); - if (fd < 0) { - log_error_errno(fd, "Failed to open %s: %m", vc); - return EXIT_FAILURE; - } - - if (!is_vconsole(fd)) { - log_error("Device %s is not a virtual console.", vc); - return EXIT_FAILURE; - } - - utf8 = is_locale_utf8(); - - r = parse_env_file("/etc/vconsole.conf", NEWLINE, - "KEYMAP", &vc_keymap, - "KEYMAP_TOGGLE", &vc_keymap_toggle, - "FONT", &vc_font, - "FONT_MAP", &vc_font_map, - "FONT_UNIMAP", &vc_font_unimap, - NULL); - - if (r < 0 && r != -ENOENT) - log_warning_errno(r, "Failed to read /etc/vconsole.conf: %m"); - - /* Let the kernel command line override /etc/vconsole.conf */ - if (detect_container() <= 0) { - r = parse_env_file("/proc/cmdline", WHITESPACE, - "vconsole.keymap", &vc_keymap, - "vconsole.keymap.toggle", &vc_keymap_toggle, - "vconsole.font", &vc_font, - "vconsole.font.map", &vc_font_map, - "vconsole.font.unimap", &vc_font_unimap, - NULL); - - if (r < 0 && r != -ENOENT) - log_warning_errno(r, "Failed to read /proc/cmdline: %m"); - } - - if (utf8) - (void) enable_utf8(fd); - else - (void) disable_utf8(fd); - - font_ok = font_load_and_wait(vc, vc_font, vc_font_map, vc_font_unimap) > 0; - keyboard_ok = keyboard_load_and_wait(vc, vc_keymap, vc_keymap_toggle, utf8) > 0; - - /* Only copy the font when we executed setfont successfully */ - if (font_copy && font_ok) - (void) font_copy_to_all_vcs(fd); - - return font_ok && keyboard_ok ? EXIT_SUCCESS : EXIT_FAILURE; -} diff --git a/src/grp-journal/Makefile b/src/grp-journal/Makefile index 6538713813..f1f3453d33 100644 --- a/src/grp-journal/Makefile +++ b/src/grp-journal/Makefile @@ -167,6 +167,9 @@ tests += \ test-compress-benchmark endif # HAVE_COMPRESSION +dist_tmpfiles_DATA = \ + tmpfiles.d/journal-nocow.conf + nested.subdirs += journalctl systemd-journald libjournal-core nested.subdirs += systemd-cat #nested.subdirs += grp-remote diff --git a/src/grp-journal/grp-remote/systemd-journal-gatewayd/Makefile b/src/grp-journal/grp-remote/systemd-journal-gatewayd/Makefile index 05df34a71d..f6e07621a8 100644 --- a/src/grp-journal/grp-remote/systemd-journal-gatewayd/Makefile +++ b/src/grp-journal/grp-remote/systemd-journal-gatewayd/Makefile @@ -60,6 +60,9 @@ nodist_systemunit_DATA += \ dist_gatewayddocumentroot_DATA = \ src/journal-remote/browse.html +dist_sysusers_DATA += \ + sysusers.d/systemd-journal-gateway.conf + endif # HAVE_MICROHTTPD EXTRA_DIST += \ diff --git a/src/grp-journal/grp-remote/systemd-journal-gatewayd/systemd-journal-gateway.sysusers b/src/grp-journal/grp-remote/systemd-journal-gatewayd/systemd-journal-gateway.sysusers new file mode 100644 index 0000000000..a60fcf4d42 --- /dev/null +++ b/src/grp-journal/grp-remote/systemd-journal-gatewayd/systemd-journal-gateway.sysusers @@ -0,0 +1,9 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +m4_ifdef(`HAVE_MICROHTTPD', +u systemd-journal-gateway - "systemd Journal Gateway" diff --git a/src/grp-journal/grp-remote/systemd-journal-gatewayd/systemd-journal-gatewayd.service.in b/src/grp-journal/grp-remote/systemd-journal-gatewayd/systemd-journal-gatewayd.service.in new file mode 100644 index 0000000000..f4f845841d --- /dev/null +++ b/src/grp-journal/grp-remote/systemd-journal-gatewayd/systemd-journal-gatewayd.service.in @@ -0,0 +1,29 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Journal Gateway Service +Documentation=man:systemd-journal-gatewayd(8) +Requires=systemd-journal-gatewayd.socket + +[Service] +ExecStart=@rootlibexecdir@/systemd-journal-gatewayd +User=systemd-journal-gateway +Group=systemd-journal-gateway +SupplementaryGroups=systemd-journal +PrivateTmp=yes +PrivateDevices=yes +PrivateNetwork=yes +ProtectSystem=full +ProtectHome=yes + +# If there are many split upjournal files we need a lot of fds to +# access them all and combine +LimitNOFILE=16384 + +[Install] +Also=systemd-journal-gatewayd.socket diff --git a/src/grp-journal/grp-remote/systemd-journal-gatewayd/systemd-journal-gatewayd.socket b/src/grp-journal/grp-remote/systemd-journal-gatewayd/systemd-journal-gatewayd.socket new file mode 100644 index 0000000000..79d9b04210 --- /dev/null +++ b/src/grp-journal/grp-remote/systemd-journal-gatewayd/systemd-journal-gatewayd.socket @@ -0,0 +1,16 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Journal Gateway Service Socket +Documentation=man:systemd-journal-gatewayd(8) + +[Socket] +ListenStream=19531 + +[Install] +WantedBy=sockets.target diff --git a/src/grp-journal/grp-remote/systemd-journal-remote/Makefile b/src/grp-journal/grp-remote/systemd-journal-remote/Makefile index b75eeedae8..7a9d9b19df 100644 --- a/src/grp-journal/grp-remote/systemd-journal-remote/Makefile +++ b/src/grp-journal/grp-remote/systemd-journal-remote/Makefile @@ -63,6 +63,9 @@ endif # HAVE_GNUTLS dist_systemunit_DATA += \ units/systemd-journal-remote.socket +dist_sysusers_DATA += \ + sysusers.d/systemd-journal-remote.conf + nodist_systemunit_DATA += \ units/systemd-journal-remote.service diff --git a/src/grp-journal/grp-remote/systemd-journal-remote/systemd-journal-remote.service.in b/src/grp-journal/grp-remote/systemd-journal-remote/systemd-journal-remote.service.in new file mode 100644 index 0000000000..fdf3da4b64 --- /dev/null +++ b/src/grp-journal/grp-remote/systemd-journal-remote/systemd-journal-remote.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 Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Journal Remote Sink Service +Documentation=man:systemd-journal-remote(8) man:journal-remote.conf(5) +Requires=systemd-journal-remote.socket + +[Service] +ExecStart=@rootlibexecdir@/systemd-journal-remote \ + --listen-https=-3 \ + --output=/var/log/journal/remote/ +User=systemd-journal-remote +Group=systemd-journal-remote +PrivateTmp=yes +PrivateDevices=yes +PrivateNetwork=yes +WatchdogSec=3min + +[Install] +Also=systemd-journal-remote.socket diff --git a/src/grp-journal/grp-remote/systemd-journal-remote/systemd-journal-remote.socket b/src/grp-journal/grp-remote/systemd-journal-remote/systemd-journal-remote.socket new file mode 100644 index 0000000000..076dcae8a3 --- /dev/null +++ b/src/grp-journal/grp-remote/systemd-journal-remote/systemd-journal-remote.socket @@ -0,0 +1,15 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Journal Remote Sink Socket + +[Socket] +ListenStream=19532 + +[Install] +WantedBy=sockets.target diff --git a/src/grp-journal/grp-remote/systemd-journal-remote/systemd-journal-remote.sysusers b/src/grp-journal/grp-remote/systemd-journal-remote/systemd-journal-remote.sysusers new file mode 100644 index 0000000000..ca20c24896 --- /dev/null +++ b/src/grp-journal/grp-remote/systemd-journal-remote/systemd-journal-remote.sysusers @@ -0,0 +1,8 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +u systemd-journal-remote - "systemd Journal Remote" diff --git a/src/grp-journal/grp-remote/systemd-journal-upload/Makefile b/src/grp-journal/grp-remote/systemd-journal-upload/Makefile index c47fcf0128..845a3a566d 100644 --- a/src/grp-journal/grp-remote/systemd-journal-upload/Makefile +++ b/src/grp-journal/grp-remote/systemd-journal-upload/Makefile @@ -45,7 +45,11 @@ nodist_systemunit_DATA += \ nodist_pkgsysconf_DATA += \ src/journal-remote/journal-upload.conf -endif + +dist_sysusers_DATA += \ + sysusers.d/systemd-journal-upload.conf + +endif # HAVE_LIBCURL EXTRA_DIST += \ units/systemd-journal-upload.service.in \ diff --git a/src/grp-journal/grp-remote/systemd-journal-upload/systemd-journal-upload.service.in b/src/grp-journal/grp-remote/systemd-journal-upload/systemd-journal-upload.service.in new file mode 100644 index 0000000000..1f488ff425 --- /dev/null +++ b/src/grp-journal/grp-remote/systemd-journal-upload/systemd-journal-upload.service.in @@ -0,0 +1,27 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Journal Remote Upload Service +Documentation=man:systemd-journal-upload(8) +After=network.target + +[Service] +ExecStart=@rootlibexecdir@/systemd-journal-upload \ + --save-state +User=systemd-journal-upload +SupplementaryGroups=systemd-journal +PrivateTmp=yes +PrivateDevices=yes +WatchdogSec=3min + +# If there are many split up journal files we need a lot of fds to +# access them all and combine +LimitNOFILE=16384 + +[Install] +WantedBy=multi-user.target diff --git a/src/grp-journal/grp-remote/systemd-journal-upload/systemd-journal-upload.sysusers b/src/grp-journal/grp-remote/systemd-journal-upload/systemd-journal-upload.sysusers new file mode 100644 index 0000000000..927d400279 --- /dev/null +++ b/src/grp-journal/grp-remote/systemd-journal-upload/systemd-journal-upload.sysusers @@ -0,0 +1,8 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +u systemd-journal-upload - "systemd Journal Upload" diff --git a/src/grp-journal/grp-remote/systemd-remote.sysusers.m4 b/src/grp-journal/grp-remote/systemd-remote.sysusers.m4 deleted file mode 100644 index 0e9d71cdd0..0000000000 --- a/src/grp-journal/grp-remote/systemd-remote.sysusers.m4 +++ /dev/null @@ -1,14 +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 Lesser General Public License as published by -# the Free Software Foundation; either version 2.1 of the License, or -# (at your option) any later version. - -m4_ifdef(`HAVE_MICROHTTPD', -u systemd-journal-gateway - "systemd Journal Gateway" -u systemd-journal-remote - "systemd Journal Remote" -)m4_dnl -m4_ifdef(`HAVE_LIBCURL', -u systemd-journal-upload - "systemd Journal Upload" -)m4_dnl diff --git a/src/grp-journal/journal-nocow.tmpfiles b/src/grp-journal/journal-nocow.tmpfiles new file mode 100644 index 0000000000..e7938c8911 --- /dev/null +++ b/src/grp-journal/journal-nocow.tmpfiles @@ -0,0 +1,27 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +# See tmpfiles.d(5) for details + +# Set the NOCOW attribute for directories of journal files. This flag +# is inheredited by their new files and sub-directories. Matters only +# for btrfs filesystems. +# +# WARNING: Enabling the NOCOW attribute improves journal performance +# substantially, but also disables the btrfs checksum logic. In +# btrfs RAID filesystems the checksums are needed for rebuilding +# corrupted files. Without checksums such rebuilds are not +# possible. +# +# In a single-disk filesystem (or a filesystem without redundancy) +# enabling the NOCOW attribute for journal files is safe, because +# they have their own checksums and a rebuilding wouldn't be possible +# in any case. + +h /var/log/journal - - - - +C +h /var/log/journal/%m - - - - +C +h /var/log/journal/remote - - - - +C diff --git a/src/grp-journal/journalctl/Makefile b/src/grp-journal/journalctl/Makefile index ce650ae733..a7aa51403e 100644 --- a/src/grp-journal/journalctl/Makefile +++ b/src/grp-journal/journalctl/Makefile @@ -49,6 +49,18 @@ journalctl_LDADD += \ $(QRENCODE_LIBS) endif # HAVE_QRENCODE +nodist_systemunit_DATA += \ + units/systemd-journal-flush.service \ + units/systemd-journal-catalog-update.service + +SYSINIT_TARGET_WANTS += \ + systemd-journal-flush.service \ + systemd-journal-catalog-update.service + +EXTRA_DIST += \ + units/systemd-journal-flush.service.in \ + units/systemd-journal-catalog-update.service.in + sd.CPPFLAGS += -DCATALOG_DATABASE=\"$(catalogstatedir)/database\" include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-journal/journalctl/systemd-journal-catalog-update.service.in b/src/grp-journal/journalctl/systemd-journal-catalog-update.service.in new file mode 100644 index 0000000000..6370dd478f --- /dev/null +++ b/src/grp-journal/journalctl/systemd-journal-catalog-update.service.in @@ -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 Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Rebuild Journal Catalog +Documentation=man:systemd-journald.service(8) man:journald.conf(5) +DefaultDependencies=no +Conflicts=shutdown.target +After=local-fs.target +Before=sysinit.target shutdown.target systemd-update-done.service +ConditionNeedsUpdate=/etc + +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStart=@rootbindir@/journalctl --update-catalog +TimeoutSec=90s diff --git a/src/grp-journal/journalctl/systemd-journal-flush.service.in b/src/grp-journal/journalctl/systemd-journal-flush.service.in new file mode 100644 index 0000000000..a0a2e3fdb4 --- /dev/null +++ b/src/grp-journal/journalctl/systemd-journal-flush.service.in @@ -0,0 +1,22 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Flush Journal to Persistent Storage +Documentation=man:systemd-journald.service(8) man:journald.conf(5) +DefaultDependencies=no +Requires=systemd-journald.service +After=systemd-journald.service +After=systemd-remount-fs.service +Before=systemd-user-sessions.service systemd-tmpfiles-setup.service +RequiresMountsFor=/var/log/journal + +[Service] +ExecStart=@rootbindir@/journalctl --flush +Type=oneshot +RemainAfterExit=yes +TimeoutSec=90s diff --git a/src/grp-journal/libjournal-core/journald.conf b/src/grp-journal/libjournal-core/journald.conf deleted file mode 100644 index 2541b949be..0000000000 --- a/src/grp-journal/libjournal-core/journald.conf +++ /dev/null @@ -1,41 +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 Lesser General Public License as published by -# the Free Software Foundation; either version 2.1 of the License, or -# (at your option) any later version. -# -# Entries in this file show the compile time defaults. -# You can change settings by editing this file. -# Defaults can be restored by simply deleting this file. -# -# See journald.conf(5) for details. - -[Journal] -#Storage=auto -#Compress=yes -#Seal=yes -#SplitMode=uid -#SyncIntervalSec=5m -#RateLimitIntervalSec=30s -#RateLimitBurst=1000 -#SystemMaxUse= -#SystemKeepFree= -#SystemMaxFileSize= -#SystemMaxFiles=100 -#RuntimeMaxUse= -#RuntimeKeepFree= -#RuntimeMaxFileSize= -#RuntimeMaxFiles=100 -#MaxRetentionSec= -#MaxFileSec=1month -#ForwardToSyslog=no -#ForwardToKMsg=no -#ForwardToConsole=no -#ForwardToWall=yes -#TTYPath=/dev/console -#MaxLevelStore=debug -#MaxLevelSyslog=debug -#MaxLevelKMsg=notice -#MaxLevelConsole=info -#MaxLevelWall=emerg diff --git a/src/grp-journal/systemd-journald/Makefile b/src/grp-journal/systemd-journald/Makefile index 640528bae4..62f19aa1b8 100644 --- a/src/grp-journal/systemd-journald/Makefile +++ b/src/grp-journal/systemd-journald/Makefile @@ -40,14 +40,13 @@ dist_systemunit_DATA += \ units/systemd-journald-audit.socket nodist_systemunit_DATA += \ - units/systemd-journald.service \ - units/systemd-journal-flush.service \ - units/systemd-journal-catalog-update.service + units/systemd-journald.service dist_pkgsysconf_DATA += \ src/journal/journald.conf -dist_catalog_DATA = \ +# TODO +_dist_catalog_DATA = \ catalog/systemd.bg.catalog \ catalog/systemd.be.catalog \ catalog/systemd.be@latin.catalog \ @@ -66,14 +65,10 @@ SOCKETS_TARGET_WANTS += \ systemd-journald-audit.socket SYSINIT_TARGET_WANTS += \ - systemd-journald.service \ - systemd-journal-flush.service \ - systemd-journal-catalog-update.service + systemd-journald.service EXTRA_DIST += \ - units/systemd-journald.service.in \ - units/systemd-journal-flush.service.in \ - units/systemd-journal-catalog-update.service.in + units/systemd-journald.service.in gperf_gperf_sources += \ src/journal/journald-gperf.gperf diff --git a/src/grp-journal/systemd-journald/journald.conf b/src/grp-journal/systemd-journald/journald.conf new file mode 100644 index 0000000000..2541b949be --- /dev/null +++ b/src/grp-journal/systemd-journald/journald.conf @@ -0,0 +1,41 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# Entries in this file show the compile time defaults. +# You can change settings by editing this file. +# Defaults can be restored by simply deleting this file. +# +# See journald.conf(5) for details. + +[Journal] +#Storage=auto +#Compress=yes +#Seal=yes +#SplitMode=uid +#SyncIntervalSec=5m +#RateLimitIntervalSec=30s +#RateLimitBurst=1000 +#SystemMaxUse= +#SystemKeepFree= +#SystemMaxFileSize= +#SystemMaxFiles=100 +#RuntimeMaxUse= +#RuntimeKeepFree= +#RuntimeMaxFileSize= +#RuntimeMaxFiles=100 +#MaxRetentionSec= +#MaxFileSec=1month +#ForwardToSyslog=no +#ForwardToKMsg=no +#ForwardToConsole=no +#ForwardToWall=yes +#TTYPath=/dev/console +#MaxLevelStore=debug +#MaxLevelSyslog=debug +#MaxLevelKMsg=notice +#MaxLevelConsole=info +#MaxLevelWall=emerg diff --git a/src/grp-journal/systemd-journald/systemd-journald-audit.socket b/src/grp-journal/systemd-journald/systemd-journald-audit.socket new file mode 100644 index 0000000000..541f2cf38d --- /dev/null +++ b/src/grp-journal/systemd-journald/systemd-journald-audit.socket @@ -0,0 +1,20 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Journal Audit Socket +Documentation=man:systemd-journald.service(8) man:journald.conf(5) +DefaultDependencies=no +Before=sockets.target +ConditionSecurity=audit +ConditionCapability=CAP_AUDIT_READ + +[Socket] +Service=systemd-journald.service +ReceiveBuffer=128M +ListenNetlink=audit 1 +PassCredentials=yes diff --git a/src/grp-journal/systemd-journald/systemd-journald-dev-log.socket b/src/grp-journal/systemd-journald/systemd-journald-dev-log.socket new file mode 100644 index 0000000000..ffd44bb507 --- /dev/null +++ b/src/grp-journal/systemd-journald/systemd-journald-dev-log.socket @@ -0,0 +1,32 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Journal Socket (/dev/log) +Documentation=man:systemd-journald.service(8) man:journald.conf(5) +DefaultDependencies=no +Before=sockets.target + +# Mount and swap units need this. If this socket unit is removed by an +# isolate request the mount and swap units would be removed too, +# hence let's exclude this from isolate requests. +IgnoreOnIsolate=yes + +[Socket] +Service=systemd-journald.service +ListenDatagram=/run/systemd/journal/dev-log +Symlinks=/dev/log +SocketMode=0666 +PassCredentials=yes +PassSecurity=yes + +# Increase both the send and receive buffer, so that things don't +# block early. Note that journald internally uses the this socket both +# for receiving syslog messages, and for forwarding them to any other +# syslog, hence we bump both values. +ReceiveBuffer=8M +SendBuffer=8M diff --git a/src/grp-journal/systemd-journald/systemd-journald.service.in b/src/grp-journal/systemd-journald/systemd-journald.service.in new file mode 100644 index 0000000000..41bfde5be3 --- /dev/null +++ b/src/grp-journal/systemd-journald/systemd-journald.service.in @@ -0,0 +1,32 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Journal Service +Documentation=man:systemd-journald.service(8) man:journald.conf(5) +DefaultDependencies=no +Requires=systemd-journald.socket +After=systemd-journald.socket systemd-journald-dev-log.socket systemd-journald-audit.socket syslog.socket +Before=sysinit.target + +[Service] +Type=notify +Sockets=systemd-journald.socket systemd-journald-dev-log.socket systemd-journald-audit.socket +ExecStart=@rootlibexecdir@/systemd-journald +Restart=always +RestartSec=0 +NotifyAccess=all +StandardOutput=null +CapabilityBoundingSet=CAP_SYS_ADMIN CAP_DAC_OVERRIDE CAP_SYS_PTRACE CAP_SYSLOG CAP_AUDIT_CONTROL CAP_AUDIT_READ CAP_CHOWN CAP_DAC_READ_SEARCH CAP_FOWNER CAP_SETUID CAP_SETGID CAP_MAC_OVERRIDE +WatchdogSec=3min +FileDescriptorStoreMax=1024 + +# Increase the default a bit in order to allow many simultaneous +# services being run since we keep one fd open per service. Also, when +# flushing journal files to disk, we might need a lot of fds when many +# journal files are combined. +LimitNOFILE=16384 diff --git a/src/grp-journal/systemd-journald/systemd-journald.socket b/src/grp-journal/systemd-journald/systemd-journald.socket new file mode 100644 index 0000000000..71737014ca --- /dev/null +++ b/src/grp-journal/systemd-journald/systemd-journald.socket @@ -0,0 +1,26 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Journal Socket +Documentation=man:systemd-journald.service(8) man:journald.conf(5) +DefaultDependencies=no +Before=sockets.target + +# Mount and swap units need this. If this socket unit is removed by an +# isolate request the mount and swap units would be removed too, +# hence let's exclude this from isolate requests. +IgnoreOnIsolate=yes + +[Socket] +ListenStream=/run/systemd/journal/stdout +ListenDatagram=/run/systemd/journal/socket +SocketMode=0666 +PassCredentials=yes +PassSecurity=yes +ReceiveBuffer=8M +Service=systemd-journald.service diff --git a/src/grp-journal/systemd-journald/systemd-journald.tmpfiles.m4 b/src/grp-journal/systemd-journald/systemd-journald.tmpfiles.m4 new file mode 100644 index 0000000000..2e8bd8cbef --- /dev/null +++ b/src/grp-journal/systemd-journald/systemd-journald.tmpfiles.m4 @@ -0,0 +1,55 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +# See tmpfiles.d(5) for details + +d /run/log 0755 root root - + +z /run/log/journal 2755 root systemd-journal - - +Z /run/log/journal/%m ~2750 root systemd-journal - - +m4_ifdef(`HAVE_ACL',`m4_dnl +m4_ifdef(`ENABLE_ADM_GROUP',`m4_dnl +m4_ifdef(`ENABLE_WHEEL_GROUP',`` +a+ /run/log/journal/%m - - - - d:group:adm:r-x,d:group:wheel:r-x +a+ /run/log/journal/%m - - - - group:adm:r-x,group:wheel:r-x +a+ /run/log/journal/%m/*.journal* - - - - group:adm:r--,group:wheel:r-- +'',`` +a+ /run/log/journal/%m - - - - d:group:adm:r-x +a+ /run/log/journal/%m - - - - group:adm:r-x +a+ /run/log/journal/%m/*.journal* - - - - group:adm:r-- +'')',`m4_dnl +m4_ifdef(`ENABLE_WHEEL_GROUP',`` +a+ /run/log/journal/%m - - - - d:group:wheel:r-x +a+ /run/log/journal/%m - - - - group:wheel:r-x +a+ /run/log/journal/%m/*.journal* - - - - group:wheel:r-- +'')')')m4_dnl + +z /var/log/journal 2755 root systemd-journal - - +z /var/log/journal/%m 2755 root systemd-journal - - +z /var/log/journal/%m/system.journal 0640 root systemd-journal - - +m4_ifdef(`HAVE_ACL',`m4_dnl +m4_ifdef(`ENABLE_ADM_GROUP',`m4_dnl +m4_ifdef(`ENABLE_WHEEL_GROUP',`` +a+ /var/log/journal - - - - d:group:adm:r-x,d:group:wheel:r-x +a+ /var/log/journal - - - - group:adm:r-x,group:wheel:r-x +a+ /var/log/journal/%m - - - - d:group:adm:r-x,d:group:wheel:r-x +a+ /var/log/journal/%m - - - - group:adm:r-x,group:wheel:r-x +a+ /var/log/journal/%m/system.journal - - - - group:adm:r--,group:wheel:r-- +'', `` +a+ /var/log/journal - - - - d:group:adm:r-x +a+ /var/log/journal - - - - group:adm:r-x +a+ /var/log/journal/%m - - - - d:group:adm:r-x +a+ /var/log/journal/%m - - - - group:adm:r-x +a+ /var/log/journal/%m/system.journal - - - - group:adm:r-- +'')',`m4_dnl +m4_ifdef(`ENABLE_WHEEL_GROUP',`` +a+ /var/log/journal - - - - d:group:wheel:r-x +a+ /var/log/journal - - - - group:wheel:r-x +a+ /var/log/journal/%m - - - - d:group:wheel:r-x +a+ /var/log/journal/%m - - - - group:wheel:r-x +a+ /var/log/journal/%m/system.journal - - - - group:wheel:r-- +'')')')m4_dnl diff --git a/src/grp-locale/systemd-localed/systemd-localed.service.in b/src/grp-locale/systemd-localed/systemd-localed.service.in new file mode 100644 index 0000000000..9b13f901a3 --- /dev/null +++ b/src/grp-locale/systemd-localed/systemd-localed.service.in @@ -0,0 +1,22 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Locale Service +Documentation=man:systemd-localed.service(8) man:locale.conf(5) man:vconsole.conf(5) +Documentation=http://www.freedesktop.org/wiki/Software/systemd/localed + +[Service] +ExecStart=@rootlibexecdir@/systemd-localed +BusName=org.freedesktop.locale1 +CapabilityBoundingSet= +WatchdogSec=3min +PrivateTmp=yes +PrivateDevices=yes +PrivateNetwork=yes +ProtectSystem=yes +ProtectHome=yes diff --git a/src/grp-login/70-power-switch.rules b/src/grp-login/70-power-switch.rules deleted file mode 100644 index e2855b50f7..0000000000 --- a/src/grp-login/70-power-switch.rules +++ /dev/null @@ -1,18 +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 Lesser General Public License as published by -# the Free Software Foundation; either version 2.1 of the License, or -# (at your option) any later version. - -ACTION=="remove", GOTO="power_switch_end" - -SUBSYSTEM=="input", KERNEL=="event*", SUBSYSTEMS=="acpi", TAG+="power-switch" -SUBSYSTEM=="input", KERNEL=="event*", KERNELS=="thinkpad_acpi", TAG+="power-switch" -SUBSYSTEM=="input", KERNEL=="event*", ATTRS{name}=="twl4030_pwrbutton", TAG+="power-switch" -SUBSYSTEM=="input", KERNEL=="event*", ATTRS{name}=="tps65217_pwr_but", TAG+="power-switch" -SUBSYSTEM=="input", KERNEL=="event*", ATTRS{name}=="* WMI hotkeys", TAG+="power-switch" -SUBSYSTEM=="input", KERNEL=="event*", \ - SUBSYSTEMS=="platform", DRIVERS=="gpio-keys", ATTRS{keys}=="*,116|116,*|116|*,116,*", TAG+="power-switch" - -LABEL="power_switch_end" diff --git a/src/grp-login/70-uaccess.rules b/src/grp-login/70-uaccess.rules deleted file mode 100644 index 50dcd2e275..0000000000 --- a/src/grp-login/70-uaccess.rules +++ /dev/null @@ -1,81 +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 Lesser General Public License as published by -# the Free Software Foundation; either version 2.1 of the License, or -# (at your option) any later version. - -ACTION=="remove", GOTO="uaccess_end" -ENV{MAJOR}=="", GOTO="uaccess_end" - -# PTP/MTP protocol devices, cameras, portable media players -SUBSYSTEM=="usb", ENV{ID_USB_INTERFACES}=="*:060101:*", TAG+="uaccess" - -# Digicams with proprietary protocol -ENV{ID_GPHOTO2}=="?*", TAG+="uaccess" - -# SCSI and USB scanners -ENV{libsane_matched}=="yes", TAG+="uaccess" - -# HPLIP devices (necessary for ink level check and HP tool maintenance) -ENV{ID_HPLIP}=="1", TAG+="uaccess" - -# optical drives -SUBSYSTEM=="block", ENV{ID_CDROM}=="1", TAG+="uaccess" -SUBSYSTEM=="scsi_generic", SUBSYSTEMS=="scsi", ATTRS{type}=="4|5", TAG+="uaccess" - -# Sound devices -SUBSYSTEM=="sound", TAG+="uaccess" \ - OPTIONS+="static_node=snd/timer", OPTIONS+="static_node=snd/seq" - -# ffado is an userspace driver for firewire sound cards -SUBSYSTEM=="firewire", ENV{ID_FFADO}=="1", TAG+="uaccess" - -# Webcams, frame grabber, TV cards -SUBSYSTEM=="video4linux", TAG+="uaccess" -SUBSYSTEM=="dvb", TAG+="uaccess" - -# IIDC devices: industrial cameras and some webcams -SUBSYSTEM=="firewire", ATTR{units}=="*0x00a02d:0x00010*", TAG+="uaccess" -SUBSYSTEM=="firewire", ATTR{units}=="*0x00b09d:0x00010*", TAG+="uaccess" -# AV/C devices: camcorders, set-top boxes, TV sets, audio devices, and more -SUBSYSTEM=="firewire", ATTR{units}=="*0x00a02d:0x010001*", TAG+="uaccess" -SUBSYSTEM=="firewire", ATTR{units}=="*0x00a02d:0x014001*", TAG+="uaccess" - -# DRI video devices -SUBSYSTEM=="drm", KERNEL=="card*|renderD*", TAG+="uaccess" - -# KVM -SUBSYSTEM=="misc", KERNEL=="kvm", TAG+="uaccess" - -# smart-card readers -ENV{ID_SMARTCARD_READER}=="?*", TAG+="uaccess" - -# (USB) authentication devices -ENV{ID_SECURITY_TOKEN}=="?*", TAG+="uaccess" - -# PDA devices -ENV{ID_PDA}=="?*", TAG+="uaccess" - -# Programmable remote control -ENV{ID_REMOTE_CONTROL}=="1", TAG+="uaccess" - -# joysticks -SUBSYSTEM=="input", ENV{ID_INPUT_JOYSTICK}=="?*", TAG+="uaccess" - -# color measurement devices -ENV{COLOR_MEASUREMENT_DEVICE}=="?*", TAG+="uaccess" - -# DDC/CI device, usually high-end monitors such as the DreamColor -ENV{DDC_DEVICE}=="?*", TAG+="uaccess" - -# media player raw devices (for user-mode drivers, Android SDK, etc.) -SUBSYSTEM=="usb", ENV{ID_MEDIA_PLAYER}=="?*", TAG+="uaccess" - -# software-defined radio communication devices -ENV{ID_SOFTWARE_RADIO}=="?*", TAG+="uaccess" - -# 3D printers, CNC machines, laser cutters, 3D scanners, etc. -ENV{ID_MAKER_TOOL}=="?*", TAG+="uaccess" - -LABEL="uaccess_end" diff --git a/src/grp-login/71-seat.rules.in b/src/grp-login/71-seat.rules.in deleted file mode 100644 index de55c9a4ec..0000000000 --- a/src/grp-login/71-seat.rules.in +++ /dev/null @@ -1,54 +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 Lesser General Public License as published by -# the Free Software Foundation; either version 2.1 of the License, or -# (at your option) any later version. - -ACTION=="remove", GOTO="seat_end" - -TAG=="uaccess", SUBSYSTEM!="sound", TAG+="seat" -SUBSYSTEM=="sound", KERNEL=="card*", TAG+="seat" -SUBSYSTEM=="input", KERNEL=="input*", TAG+="seat" -SUBSYSTEM=="graphics", KERNEL=="fb[0-9]*", TAG+="seat", TAG+="master-of-seat" -SUBSYSTEM=="drm", KERNEL=="card[0-9]*", TAG+="seat", TAG+="master-of-seat" -SUBSYSTEM=="usb", ATTR{bDeviceClass}=="09", TAG+="seat" - -# 'Plugable' USB hub, sound, network, graphics adapter -SUBSYSTEM=="usb", ATTR{idVendor}=="2230", ATTR{idProduct}=="000[13]", ENV{ID_AUTOSEAT}="1" - -# qemu (version 2.4+) has a PCI-PCI bridge (-device pci-bridge-seat) to group -# devices belonging to one seat. See: -# http://git.qemu.org/?p=qemu.git;a=blob;f=docs/multiseat.txt -SUBSYSTEM=="pci", ATTR{vendor}=="0x1b36", ATTR{device}=="0x000a", TAG+="seat", ENV{ID_AUTOSEAT}="1" - -# Mimo 720, with integrated USB hub, displaylink graphics, and e2i -# touchscreen. This device carries no proper VID/PID in the USB hub, -# but it does carry good ID data in the graphics component, hence we -# check it from the parent. There's a bit of a race here however, -# given that the child devices might not exist yet at the time this -# rule is executed. To work around this we'll trigger the parent from -# the child if we notice that the parent wasn't recognized yet. - -# Match parent -SUBSYSTEM=="usb", ATTR{idVendor}=="058f", ATTR{idProduct}=="6254", \ - ATTR{%k.2/idVendor}=="17e9", ATTR{%k.2/idProduct}=="401a", ATTR{%k.2/product}=="mimo inc", \ - ENV{ID_AUTOSEAT}="1", ENV{ID_AVOID_LOOP}="1" - -# Match child, look for parent's ID_AVOID_LOOP -SUBSYSTEM=="usb", ATTR{idVendor}=="17e9", ATTR{idProduct}=="401a", ATTR{product}=="mimo inc", \ - ATTR{../idVendor}=="058f", ATTR{../idProduct}=="6254", \ - IMPORT{parent}="ID_AVOID_LOOP" - -# Match child, retrigger parent -SUBSYSTEM=="usb", ATTR{idVendor}=="17e9", ATTR{idProduct}=="401a", ATTR{product}=="mimo inc", \ - ATTR{../idVendor}=="058f", ATTR{../idProduct}=="6254", \ - ENV{ID_AVOID_LOOP}=="", \ - RUN+="@rootbindir@/udevadm trigger --parent-match=%p/.." - -TAG=="seat", ENV{ID_PATH}=="", IMPORT{builtin}="path_id" -TAG=="seat", ENV{ID_FOR_SEAT}=="", ENV{ID_PATH_TAG}!="", ENV{ID_FOR_SEAT}="$env{SUBSYSTEM}-$env{ID_PATH_TAG}" - -SUBSYSTEM=="input", ATTR{name}=="Wiebetech LLC Wiebetech", RUN+="@rootbindir@/loginctl lock-sessions" - -LABEL="seat_end" diff --git a/src/grp-login/73-seat-late.rules.in b/src/grp-login/73-seat-late.rules.in deleted file mode 100644 index 901df750fd..0000000000 --- a/src/grp-login/73-seat-late.rules.in +++ /dev/null @@ -1,17 +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 Lesser General Public License as published by -# the Free Software Foundation; either version 2.1 of the License, or -# (at your option) any later version. - -ACTION=="remove", GOTO="seat_late_end" - -ENV{ID_SEAT}=="", ENV{ID_AUTOSEAT}=="1", ENV{ID_FOR_SEAT}!="", ENV{ID_SEAT}="seat-$env{ID_FOR_SEAT}" -ENV{ID_SEAT}=="", IMPORT{parent}="ID_SEAT" - -ENV{ID_SEAT}!="", TAG+="$env{ID_SEAT}" - -TAG=="uaccess", ENV{MAJOR}!="", RUN{builtin}+="uaccess" - -LABEL="seat_late_end" diff --git a/src/grp-login/Makefile b/src/grp-login/Makefile index 381d653166..be6adc4880 100644 --- a/src/grp-login/Makefile +++ b/src/grp-login/Makefile @@ -56,60 +56,9 @@ tests += \ test-login-tables \ test-login-shared - -nodist_systemunit_DATA += \ - units/systemd-logind.service - -dist_systemunit_DATA += \ - units/user.slice - -dist_systemunit_DATA_busnames += \ - units/org.freedesktop.login1.busname - -dist_dbussystemservice_DATA += \ - src/login/org.freedesktop.login1.service - -dist_dbuspolicy_DATA += \ - src/login/org.freedesktop.login1.conf - -nodist_pkgsysconf_DATA += \ - src/login/logind.conf - -polkitpolicy_files += \ - src/login/org.freedesktop.login1.policy - -INSTALL_DIRS += \ - $(systemdstatedir) - -MULTI_USER_TARGET_WANTS += \ - systemd-logind.service - -SYSTEM_UNIT_ALIASES += \ - systemd-logind.service dbus-org.freedesktop.login1.service - -BUSNAMES_TARGET_WANTS += \ - org.freedesktop.login1.busname - -dist_udevrules_DATA += \ - src/login/70-uaccess.rules \ - src/login/70-power-switch.rules - -nodist_udevrules_DATA += \ - src/login/71-seat.rules \ - src/login/73-seat-late.rules +nested.subdirs += loginctl systemd-logind +nested.subdirs += pam_systemd systemd-inhibit endif # ENABLE_LOGIND -polkitpolicy_in_files += \ - src/login/org.freedesktop.login1.policy.in - -gperf_gperf_sources += \ - src/login/logind-gperf.gperf - -EXTRA_DIST += \ - src/login/71-seat.rules.in \ - src/login/73-seat-late.rules.in \ - units/systemd-logind.service.in \ - src/login/logind.conf.in - include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-login/liblogind-core/Makefile b/src/grp-login/liblogind-core/Makefile deleted file mode 100644 index 1575518aec..0000000000 --- a/src/grp-login/liblogind-core/Makefile +++ /dev/null @@ -1,62 +0,0 @@ -# -*- Mode: makefile; indent-tabs-mode: t -*- -# -# This file is part of systemd. -# -# Copyright 2010-2012 Lennart Poettering -# Copyright 2010-2012 Kay Sievers -# Copyright 2013 Zbigniew Jędrzejewski-Szmek -# Copyright 2013 David Strauss -# Copyright 2016 Luke Shumaker -# -# systemd is free software; you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation; either version 2.1 of the License, or -# (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 -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with systemd; If not, see . -include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk -include $(topsrcdir)/build-aux/Makefile.head.mk - -liblogind_core_la_SOURCES = \ - src/login/logind-core.c \ - src/login/logind-device.c \ - src/login/logind-device.h \ - src/login/logind-button.c \ - src/login/logind-button.h \ - src/login/logind-action.c \ - src/login/logind-action.h \ - src/login/logind-seat.c \ - src/login/logind-seat.h \ - src/login/logind-session.c \ - src/login/logind-session.h \ - src/login/logind-session-device.c \ - src/login/logind-session-device.h \ - src/login/logind-user.c \ - src/login/logind-user.h \ - src/login/logind-inhibit.c \ - src/login/logind-inhibit.h \ - src/login/logind-dbus.c \ - src/login/logind-session-dbus.c \ - src/login/logind-seat-dbus.c \ - src/login/logind-user-dbus.c \ - src/login/logind-utmp.c \ - src/login/logind-acl.h - -liblogind_core_la_LIBADD = \ - libshared.la - -ifneq ($(HAVE_ACL),) -liblogind_core_la_SOURCES += \ - src/login/logind-acl.c -endif # HAVE_ACL - -noinst_LTLIBRARIES += \ - liblogind-core.la - -include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-login/liblogind-core/logind-acl.c b/src/grp-login/liblogind-core/logind-acl.c deleted file mode 100644 index 44e49c1dad..0000000000 --- a/src/grp-login/liblogind-core/logind-acl.c +++ /dev/null @@ -1,293 +0,0 @@ -/*** - 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 Lesser General Public License as published by - the Free Software Foundation; either version 2.1 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include - -#include "basic/alloc-util.h" -#include "basic/dirent-util.h" -#include "basic/escape.h" -#include "basic/fd-util.h" -#include "basic/formats-util.h" -#include "basic/set.h" -#include "basic/string-util.h" -#include "basic/util.h" -#include "shared/acl-util.h" -#include "shared/udev-util.h" - -#include "logind-acl.h" - -static int flush_acl(acl_t acl) { - acl_entry_t i; - int found; - bool changed = false; - - assert(acl); - - 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; - - if (acl_get_tag_type(i, &tag) < 0) - return -errno; - - if (tag != ACL_USER) - continue; - - if (acl_delete_entry(acl, i) < 0) - return -errno; - - changed = true; - } - - if (found < 0) - return -errno; - - return changed; -} - -int devnode_acl(const char *path, - bool flush, - bool del, uid_t old_uid, - bool add, uid_t new_uid) { - - acl_t acl; - int r = 0; - bool changed = false; - - assert(path); - - acl = acl_get_file(path, ACL_TYPE_ACCESS); - if (!acl) - return -errno; - - if (flush) { - - r = flush_acl(acl); - if (r < 0) - goto finish; - if (r > 0) - changed = true; - - } else if (del && old_uid > 0) { - acl_entry_t entry; - - r = acl_find_uid(acl, old_uid, &entry); - if (r < 0) - goto finish; - - if (r > 0) { - if (acl_delete_entry(acl, entry) < 0) { - r = -errno; - goto finish; - } - - changed = true; - } - } - - if (add && new_uid > 0) { - acl_entry_t entry; - acl_permset_t permset; - int rd, wt; - - r = acl_find_uid(acl, new_uid, &entry); - if (r < 0) - goto finish; - - if (r == 0) { - if (acl_create_entry(&acl, &entry) < 0) { - r = -errno; - goto finish; - } - - if (acl_set_tag_type(entry, ACL_USER) < 0 || - acl_set_qualifier(entry, &new_uid) < 0) { - r = -errno; - goto finish; - } - } - - if (acl_get_permset(entry, &permset) < 0) { - r = -errno; - goto finish; - } - - rd = acl_get_perm(permset, ACL_READ); - if (rd < 0) { - r = -errno; - goto finish; - } - - wt = acl_get_perm(permset, ACL_WRITE); - if (wt < 0) { - r = -errno; - goto finish; - } - - if (!rd || !wt) { - - if (acl_add_perm(permset, ACL_READ|ACL_WRITE) < 0) { - r = -errno; - goto finish; - } - - changed = true; - } - } - - if (!changed) - goto finish; - - if (acl_calc_mask(&acl) < 0) { - r = -errno; - goto finish; - } - - if (acl_set_file(path, ACL_TYPE_ACCESS, acl) < 0) { - r = -errno; - goto finish; - } - - r = 0; - -finish: - acl_free(acl); - - return r; -} - -int devnode_acl_all(struct udev *udev, - const char *seat, - bool flush, - bool del, uid_t old_uid, - bool add, uid_t new_uid) { - - _cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL; - struct udev_list_entry *item = NULL, *first = NULL; - _cleanup_set_free_free_ Set *nodes = NULL; - _cleanup_closedir_ DIR *dir = NULL; - struct dirent *dent; - Iterator i; - char *n; - int r; - - assert(udev); - - nodes = set_new(&string_hash_ops); - if (!nodes) - return -ENOMEM; - - e = udev_enumerate_new(udev); - if (!e) - return -ENOMEM; - - if (isempty(seat)) - seat = "seat0"; - - /* We can only match by one tag in libudev. We choose - * "uaccess" for that. If we could match for two tags here we - * could add the seat name as second match tag, but this would - * be hardly optimizable in libudev, and hence checking the - * second tag manually in our loop is a good solution. */ - r = udev_enumerate_add_match_tag(e, "uaccess"); - if (r < 0) - return r; - - r = udev_enumerate_add_match_is_initialized(e); - if (r < 0) - return r; - - r = udev_enumerate_scan_devices(e); - if (r < 0) - return r; - - first = udev_enumerate_get_list_entry(e); - udev_list_entry_foreach(item, first) { - _cleanup_udev_device_unref_ struct udev_device *d = NULL; - const char *node, *sn; - - d = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item)); - if (!d) - return -ENOMEM; - - sn = udev_device_get_property_value(d, "ID_SEAT"); - if (isempty(sn)) - sn = "seat0"; - - if (!streq(seat, sn)) - continue; - - node = udev_device_get_devnode(d); - /* In case people mistag devices with nodes, we need to ignore this */ - if (!node) - continue; - - n = strdup(node); - if (!n) - return -ENOMEM; - - log_debug("Found udev node %s for seat %s", n, seat); - r = set_consume(nodes, n); - if (r < 0) - return r; - } - - /* udev exports "dead" device nodes to allow module on-demand loading, - * these devices are not known to the kernel at this moment */ - dir = opendir("/run/udev/static_node-tags/uaccess"); - if (dir) { - FOREACH_DIRENT(dent, dir, return -errno) { - _cleanup_free_ char *unescaped_devname = NULL; - - if (cunescape(dent->d_name, UNESCAPE_RELAX, &unescaped_devname) < 0) - return -ENOMEM; - - n = strappend("/dev/", unescaped_devname); - if (!n) - return -ENOMEM; - - log_debug("Found static node %s for seat %s", n, seat); - r = set_consume(nodes, n); - if (r == -EEXIST) - continue; - if (r < 0) - return r; - } - } - - r = 0; - SET_FOREACH(n, nodes, i) { - int k; - - log_debug("Changing ACLs at %s for seat %s (uid "UID_FMT"→"UID_FMT"%s%s)", - n, seat, old_uid, new_uid, - del ? " del" : "", add ? " add" : ""); - - k = devnode_acl(n, flush, del, old_uid, add, new_uid); - if (k == -ENOENT) - log_debug("Device %s disappeared while setting ACLs", n); - else if (k < 0 && r == 0) - r = k; - } - - return r; -} diff --git a/src/grp-login/liblogind-core/logind-acl.h b/src/grp-login/liblogind-core/logind-acl.h deleted file mode 100644 index 7d324ea90a..0000000000 --- a/src/grp-login/liblogind-core/logind-acl.h +++ /dev/null @@ -1,56 +0,0 @@ -#pragma once - -/*** - 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 Lesser General Public License as published by - the Free Software Foundation; either version 2.1 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include - -#include - -#ifdef HAVE_ACL - -int devnode_acl(const char *path, - bool flush, - bool del, uid_t old_uid, - bool add, uid_t new_uid); - -int devnode_acl_all(struct udev *udev, - const char *seat, - bool flush, - bool del, uid_t old_uid, - bool add, uid_t new_uid); -#else - -static inline int devnode_acl(const char *path, - bool flush, - bool del, uid_t old_uid, - bool add, uid_t new_uid) { - return 0; -} - -static inline int devnode_acl_all(struct udev *udev, - const char *seat, - bool flush, - bool del, uid_t old_uid, - bool add, uid_t new_uid) { - return 0; -} - -#endif diff --git a/src/grp-login/liblogind-core/logind-action.c b/src/grp-login/liblogind-core/logind-action.c deleted file mode 100644 index 0cfeab3fdb..0000000000 --- a/src/grp-login/liblogind-core/logind-action.c +++ /dev/null @@ -1,179 +0,0 @@ -/*** - 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 Lesser General Public License as published by - the Free Software Foundation; either version 2.1 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include - -#include "basic/alloc-util.h" -#include "basic/formats-util.h" -#include "basic/process-util.h" -#include "basic/special.h" -#include "basic/string-table.h" -#include "basic/terminal-util.h" -#include "basic/user-util.h" -#include "sd-bus/bus-error.h" -#include "shared/bus-util.h" -#include "shared/conf-parser.h" -#include "shared/sleep-config.h" - -#include "logind-action.h" - -int manager_handle_action( - Manager *m, - InhibitWhat inhibit_key, - HandleAction handle, - bool ignore_inhibited, - bool is_edge) { - - static const char * const message_table[_HANDLE_ACTION_MAX] = { - [HANDLE_POWEROFF] = "Powering Off...", - [HANDLE_REBOOT] = "Rebooting...", - [HANDLE_HALT] = "Halting...", - [HANDLE_KEXEC] = "Rebooting via kexec...", - [HANDLE_SUSPEND] = "Suspending...", - [HANDLE_HIBERNATE] = "Hibernating...", - [HANDLE_HYBRID_SLEEP] = "Hibernating and suspending..." - }; - - static const char * const target_table[_HANDLE_ACTION_MAX] = { - [HANDLE_POWEROFF] = SPECIAL_POWEROFF_TARGET, - [HANDLE_REBOOT] = SPECIAL_REBOOT_TARGET, - [HANDLE_HALT] = SPECIAL_HALT_TARGET, - [HANDLE_KEXEC] = SPECIAL_KEXEC_TARGET, - [HANDLE_SUSPEND] = SPECIAL_SUSPEND_TARGET, - [HANDLE_HIBERNATE] = SPECIAL_HIBERNATE_TARGET, - [HANDLE_HYBRID_SLEEP] = SPECIAL_HYBRID_SLEEP_TARGET - }; - - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - InhibitWhat inhibit_operation; - Inhibitor *offending = NULL; - bool supported; - int r; - - assert(m); - - /* If the key handling is turned off, don't do anything */ - if (handle == HANDLE_IGNORE) { - log_debug("Refusing operation, as it is turned off."); - return 0; - } - - if (inhibit_key == INHIBIT_HANDLE_LID_SWITCH) { - /* If the last system suspend or startup is too close, - * let's not suspend for now, to give USB docking - * stations some time to settle so that we can - * properly watch its displays. */ - if (m->lid_switch_ignore_event_source) { - log_debug("Ignoring lid switch request, system startup or resume too close."); - return 0; - } - } - - /* If the key handling is inhibited, don't do anything */ - if (inhibit_key > 0) { - if (manager_is_inhibited(m, inhibit_key, INHIBIT_BLOCK, NULL, true, false, 0, NULL)) { - log_debug("Refusing operation, %s is inhibited.", inhibit_what_to_string(inhibit_key)); - return 0; - } - } - - /* Locking is handled differently from the rest. */ - if (handle == HANDLE_LOCK) { - - if (!is_edge) - return 0; - - log_info("Locking sessions..."); - session_send_lock_all(m, true); - return 1; - } - - if (handle == HANDLE_SUSPEND) - supported = can_sleep("suspend") > 0; - else if (handle == HANDLE_HIBERNATE) - supported = can_sleep("hibernate") > 0; - else if (handle == HANDLE_HYBRID_SLEEP) - supported = can_sleep("hybrid-sleep") > 0; - else if (handle == HANDLE_KEXEC) - supported = access(KEXEC, X_OK) >= 0; - else - supported = true; - - if (!supported) { - log_warning("Requested operation not supported, ignoring."); - return -EOPNOTSUPP; - } - - if (m->action_what) { - log_debug("Action already in progress, ignoring."); - return -EALREADY; - } - - inhibit_operation = handle == HANDLE_SUSPEND || handle == HANDLE_HIBERNATE || handle == HANDLE_HYBRID_SLEEP ? INHIBIT_SLEEP : INHIBIT_SHUTDOWN; - - /* If the actual operation is inhibited, warn and fail */ - if (!ignore_inhibited && - manager_is_inhibited(m, inhibit_operation, INHIBIT_BLOCK, NULL, false, false, 0, &offending)) { - _cleanup_free_ char *comm = NULL, *u = NULL; - - get_process_comm(offending->pid, &comm); - u = uid_to_name(offending->uid); - - /* If this is just a recheck of the lid switch then don't warn about anything */ - if (!is_edge) { - log_debug("Refusing operation, %s is inhibited by UID "UID_FMT"/%s, PID "PID_FMT"/%s.", - inhibit_what_to_string(inhibit_operation), - offending->uid, strna(u), - offending->pid, strna(comm)); - return 0; - } - - log_error("Refusing operation, %s is inhibited by UID "UID_FMT"/%s, PID "PID_FMT"/%s.", - inhibit_what_to_string(inhibit_operation), - offending->uid, strna(u), - offending->pid, strna(comm)); - - return -EPERM; - } - - log_info("%s", message_table[handle]); - - r = bus_manager_shutdown_or_sleep_now_or_later(m, target_table[handle], inhibit_operation, &error); - if (r < 0) { - log_error("Failed to execute operation: %s", bus_error_message(&error, r)); - return r; - } - - return 1; -} - -static const char* const handle_action_table[_HANDLE_ACTION_MAX] = { - [HANDLE_IGNORE] = "ignore", - [HANDLE_POWEROFF] = "poweroff", - [HANDLE_REBOOT] = "reboot", - [HANDLE_HALT] = "halt", - [HANDLE_KEXEC] = "kexec", - [HANDLE_SUSPEND] = "suspend", - [HANDLE_HIBERNATE] = "hibernate", - [HANDLE_HYBRID_SLEEP] = "hybrid-sleep", - [HANDLE_LOCK] = "lock" -}; - -DEFINE_STRING_TABLE_LOOKUP(handle_action, HandleAction); -DEFINE_CONFIG_PARSE_ENUM(config_parse_handle_action, handle_action, HandleAction, "Failed to parse handle action setting"); diff --git a/src/grp-login/liblogind-core/logind-action.h b/src/grp-login/liblogind-core/logind-action.h deleted file mode 100644 index 39ba3cf0f6..0000000000 --- a/src/grp-login/liblogind-core/logind-action.h +++ /dev/null @@ -1,50 +0,0 @@ -#pragma once - -/*** - 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 Lesser General Public License as published by - the Free Software Foundation; either version 2.1 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -typedef enum HandleAction { - HANDLE_IGNORE, - HANDLE_POWEROFF, - HANDLE_REBOOT, - HANDLE_HALT, - HANDLE_KEXEC, - HANDLE_SUSPEND, - HANDLE_HIBERNATE, - HANDLE_HYBRID_SLEEP, - HANDLE_LOCK, - _HANDLE_ACTION_MAX, - _HANDLE_ACTION_INVALID = -1 -} HandleAction; - -#include "logind.h" - -#include "logind-inhibit.h" - -int manager_handle_action( - Manager *m, - InhibitWhat inhibit_key, - HandleAction handle, - bool ignore_inhibited, - bool is_edge); - -const char* handle_action_to_string(HandleAction h) _const_; -HandleAction handle_action_from_string(const char *s) _pure_; - -int config_parse_handle_action(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); diff --git a/src/grp-login/liblogind-core/logind-button.c b/src/grp-login/liblogind-core/logind-button.c deleted file mode 100644 index 81c82117fc..0000000000 --- a/src/grp-login/liblogind-core/logind-button.c +++ /dev/null @@ -1,290 +0,0 @@ -/*** - 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 Lesser General Public License as published by - the Free Software Foundation; either version 2.1 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include - -#include - -#include - -#include "basic/alloc-util.h" -#include "basic/fd-util.h" -#include "basic/string-util.h" -#include "basic/util.h" - -#include "logind-button.h" - -Button* button_new(Manager *m, const char *name) { - Button *b; - - assert(m); - assert(name); - - b = new0(Button, 1); - if (!b) - return NULL; - - b->name = strdup(name); - if (!b->name) { - free(b); - return NULL; - } - - if (hashmap_put(m->buttons, b->name, b) < 0) { - free(b->name); - free(b); - return NULL; - } - - b->manager = m; - b->fd = -1; - - return b; -} - -void button_free(Button *b) { - assert(b); - - hashmap_remove(b->manager->buttons, b->name); - - sd_event_source_unref(b->io_event_source); - sd_event_source_unref(b->check_event_source); - - if (b->fd >= 0) - /* If the device has been unplugged close() returns - * ENODEV, let's ignore this, hence we don't use - * safe_close() */ - (void) close(b->fd); - - free(b->name); - free(b->seat); - free(b); -} - -int button_set_seat(Button *b, const char *sn) { - char *s; - - assert(b); - assert(sn); - - s = strdup(sn); - if (!s) - return -ENOMEM; - - free(b->seat); - b->seat = s; - - return 0; -} - -static void button_lid_switch_handle_action(Manager *manager, bool is_edge) { - HandleAction handle_action; - - assert(manager); - - /* If we are docked, handle the lid switch differently */ - if (manager_is_docked_or_external_displays(manager)) - handle_action = manager->handle_lid_switch_docked; - else - handle_action = manager->handle_lid_switch; - - manager_handle_action(manager, INHIBIT_HANDLE_LID_SWITCH, handle_action, manager->lid_switch_ignore_inhibited, is_edge); -} - -static int button_recheck(sd_event_source *e, void *userdata) { - Button *b = userdata; - - assert(b); - assert(b->lid_closed); - - button_lid_switch_handle_action(b->manager, false); - return 1; -} - -static int button_install_check_event_source(Button *b) { - int r; - assert(b); - - /* Install a post handler, so that we keep rechecking as long as the lid is closed. */ - - if (b->check_event_source) - return 0; - - r = sd_event_add_post(b->manager->event, &b->check_event_source, button_recheck, b); - if (r < 0) - return r; - - return sd_event_source_set_priority(b->check_event_source, SD_EVENT_PRIORITY_IDLE+1); -} - -static int button_dispatch(sd_event_source *s, int fd, uint32_t revents, void *userdata) { - Button *b = userdata; - struct input_event ev; - ssize_t l; - - assert(s); - assert(fd == b->fd); - assert(b); - - l = read(b->fd, &ev, sizeof(ev)); - if (l < 0) - return errno != EAGAIN ? -errno : 0; - if ((size_t) l < sizeof(ev)) - return -EIO; - - if (ev.type == EV_KEY && ev.value > 0) { - - switch (ev.code) { - - case KEY_POWER: - case KEY_POWER2: - log_struct(LOG_INFO, - LOG_MESSAGE("Power key pressed."), - LOG_MESSAGE_ID(SD_MESSAGE_POWER_KEY), - NULL); - - manager_handle_action(b->manager, INHIBIT_HANDLE_POWER_KEY, b->manager->handle_power_key, b->manager->power_key_ignore_inhibited, true); - break; - - /* The kernel is a bit confused here: - - KEY_SLEEP = suspend-to-ram, which everybody else calls "suspend" - KEY_SUSPEND = suspend-to-disk, which everybody else calls "hibernate" - */ - - case KEY_SLEEP: - log_struct(LOG_INFO, - LOG_MESSAGE("Suspend key pressed."), - LOG_MESSAGE_ID(SD_MESSAGE_SUSPEND_KEY), - NULL); - - manager_handle_action(b->manager, INHIBIT_HANDLE_SUSPEND_KEY, b->manager->handle_suspend_key, b->manager->suspend_key_ignore_inhibited, true); - break; - - case KEY_SUSPEND: - log_struct(LOG_INFO, - LOG_MESSAGE("Hibernate key pressed."), - LOG_MESSAGE_ID(SD_MESSAGE_HIBERNATE_KEY), - NULL); - - manager_handle_action(b->manager, INHIBIT_HANDLE_HIBERNATE_KEY, b->manager->handle_hibernate_key, b->manager->hibernate_key_ignore_inhibited, true); - break; - } - - } else if (ev.type == EV_SW && ev.value > 0) { - - if (ev.code == SW_LID) { - log_struct(LOG_INFO, - LOG_MESSAGE("Lid closed."), - LOG_MESSAGE_ID(SD_MESSAGE_LID_CLOSED), - NULL); - - b->lid_closed = true; - button_lid_switch_handle_action(b->manager, true); - button_install_check_event_source(b); - - } else if (ev.code == SW_DOCK) { - log_struct(LOG_INFO, - LOG_MESSAGE("System docked."), - LOG_MESSAGE_ID(SD_MESSAGE_SYSTEM_DOCKED), - NULL); - - b->docked = true; - } - - } else if (ev.type == EV_SW && ev.value == 0) { - - if (ev.code == SW_LID) { - log_struct(LOG_INFO, - LOG_MESSAGE("Lid opened."), - LOG_MESSAGE_ID(SD_MESSAGE_LID_OPENED), - NULL); - - b->lid_closed = false; - b->check_event_source = sd_event_source_unref(b->check_event_source); - - } else if (ev.code == SW_DOCK) { - log_struct(LOG_INFO, - LOG_MESSAGE("System undocked."), - LOG_MESSAGE_ID(SD_MESSAGE_SYSTEM_UNDOCKED), - NULL); - - b->docked = false; - } - } - - return 0; -} - -int button_open(Button *b) { - char *p, name[256]; - int r; - - assert(b); - - b->fd = safe_close(b->fd); - - p = strjoina("/dev/input/", b->name); - - b->fd = open(p, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK); - if (b->fd < 0) - return log_warning_errno(errno, "Failed to open %s: %m", b->name); - - if (ioctl(b->fd, EVIOCGNAME(sizeof(name)), name) < 0) { - r = log_error_errno(errno, "Failed to get input name: %m"); - goto fail; - } - - r = sd_event_add_io(b->manager->event, &b->io_event_source, b->fd, EPOLLIN, button_dispatch, b); - if (r < 0) { - log_error_errno(r, "Failed to add button event: %m"); - goto fail; - } - - log_info("Watching system buttons on /dev/input/%s (%s)", b->name, name); - - return 0; - -fail: - b->fd = safe_close(b->fd); - return r; -} - -int button_check_switches(Button *b) { - uint8_t switches[SW_MAX/8+1] = {}; - assert(b); - - if (b->fd < 0) - return -EINVAL; - - if (ioctl(b->fd, EVIOCGSW(sizeof(switches)), switches) < 0) - return -errno; - - b->lid_closed = (switches[SW_LID/8] >> (SW_LID % 8)) & 1; - b->docked = (switches[SW_DOCK/8] >> (SW_DOCK % 8)) & 1; - - if (b->lid_closed) - button_install_check_event_source(b); - - return 0; -} diff --git a/src/grp-login/liblogind-core/logind-button.h b/src/grp-login/liblogind-core/logind-button.h deleted file mode 100644 index f30cba2959..0000000000 --- a/src/grp-login/liblogind-core/logind-button.h +++ /dev/null @@ -1,44 +0,0 @@ -#pragma once - -/*** - 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 Lesser General Public License as published by - the Free Software Foundation; either version 2.1 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -typedef struct Button Button; - -#include "logind.h" - -struct Button { - Manager *manager; - - sd_event_source *io_event_source; - sd_event_source *check_event_source; - - char *name; - char *seat; - int fd; - - bool lid_closed; - bool docked; -}; - -Button* button_new(Manager *m, const char *name); -void button_free(Button*b); -int button_open(Button *b); -int button_set_seat(Button *b, const char *sn); -int button_check_switches(Button *b); diff --git a/src/grp-login/liblogind-core/logind-core.c b/src/grp-login/liblogind-core/logind-core.c deleted file mode 100644 index 1d6475f8d7..0000000000 --- a/src/grp-login/liblogind-core/logind-core.c +++ /dev/null @@ -1,559 +0,0 @@ -/*** - 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 Lesser General Public License as published by - the Free Software Foundation; either version 2.1 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include - -#include - -#include "basic/alloc-util.h" -#include "basic/cgroup-util.h" -#include "basic/fd-util.h" -#include "basic/strv.h" -#include "basic/terminal-util.h" -#include "basic/user-util.h" -#include "logind.h" -#include "sd-bus/bus-error.h" -#include "shared/bus-util.h" -#include "shared/udev-util.h" - -int manager_add_device(Manager *m, const char *sysfs, bool master, Device **_device) { - Device *d; - - assert(m); - assert(sysfs); - - d = hashmap_get(m->devices, sysfs); - if (d) - /* we support adding master-flags, but not removing them */ - d->master = d->master || master; - else { - d = device_new(m, sysfs, master); - if (!d) - return -ENOMEM; - } - - if (_device) - *_device = d; - - return 0; -} - -int manager_add_seat(Manager *m, const char *id, Seat **_seat) { - Seat *s; - - assert(m); - assert(id); - - s = hashmap_get(m->seats, id); - if (!s) { - s = seat_new(m, id); - if (!s) - return -ENOMEM; - } - - if (_seat) - *_seat = s; - - return 0; -} - -int manager_add_session(Manager *m, const char *id, Session **_session) { - Session *s; - - assert(m); - assert(id); - - s = hashmap_get(m->sessions, id); - if (!s) { - s = session_new(m, id); - if (!s) - return -ENOMEM; - } - - if (_session) - *_session = s; - - return 0; -} - -int manager_add_user(Manager *m, uid_t uid, gid_t gid, const char *name, User **_user) { - User *u; - int r; - - assert(m); - assert(name); - - u = hashmap_get(m->users, UID_TO_PTR(uid)); - if (!u) { - r = user_new(&u, m, uid, gid, name); - if (r < 0) - return r; - } - - if (_user) - *_user = u; - - return 0; -} - -int manager_add_user_by_name(Manager *m, const char *name, User **_user) { - uid_t uid; - gid_t gid; - int r; - - assert(m); - assert(name); - - r = get_user_creds(&name, &uid, &gid, NULL, NULL); - if (r < 0) - return r; - - return manager_add_user(m, uid, gid, name, _user); -} - -int manager_add_user_by_uid(Manager *m, uid_t uid, User **_user) { - struct passwd *p; - - assert(m); - - errno = 0; - p = getpwuid(uid); - if (!p) - return errno > 0 ? -errno : -ENOENT; - - return manager_add_user(m, uid, p->pw_gid, p->pw_name, _user); -} - -int manager_add_inhibitor(Manager *m, const char* id, Inhibitor **_inhibitor) { - Inhibitor *i; - - assert(m); - assert(id); - - i = hashmap_get(m->inhibitors, id); - if (i) { - if (_inhibitor) - *_inhibitor = i; - - return 0; - } - - i = inhibitor_new(m, id); - if (!i) - return -ENOMEM; - - if (_inhibitor) - *_inhibitor = i; - - return 0; -} - -int manager_add_button(Manager *m, const char *name, Button **_button) { - Button *b; - - assert(m); - assert(name); - - b = hashmap_get(m->buttons, name); - if (!b) { - b = button_new(m, name); - if (!b) - return -ENOMEM; - } - - if (_button) - *_button = b; - - return 0; -} - -int manager_process_seat_device(Manager *m, struct udev_device *d) { - Device *device; - int r; - - assert(m); - - if (streq_ptr(udev_device_get_action(d), "remove")) { - - device = hashmap_get(m->devices, udev_device_get_syspath(d)); - if (!device) - return 0; - - seat_add_to_gc_queue(device->seat); - device_free(device); - - } else { - const char *sn; - Seat *seat = NULL; - bool master; - - sn = udev_device_get_property_value(d, "ID_SEAT"); - if (isempty(sn)) - sn = "seat0"; - - if (!seat_name_is_valid(sn)) { - log_warning("Device with invalid seat name %s found, ignoring.", sn); - return 0; - } - - seat = hashmap_get(m->seats, sn); - master = udev_device_has_tag(d, "master-of-seat"); - - /* Ignore non-master devices for unknown seats */ - if (!master && !seat) - return 0; - - r = manager_add_device(m, udev_device_get_syspath(d), master, &device); - if (r < 0) - return r; - - if (!seat) { - r = manager_add_seat(m, sn, &seat); - if (r < 0) { - if (!device->seat) - device_free(device); - - return r; - } - } - - device_attach(device, seat); - seat_start(seat); - } - - return 0; -} - -int manager_process_button_device(Manager *m, struct udev_device *d) { - Button *b; - - int r; - - assert(m); - - if (streq_ptr(udev_device_get_action(d), "remove")) { - - b = hashmap_get(m->buttons, udev_device_get_sysname(d)); - if (!b) - return 0; - - button_free(b); - - } else { - const char *sn; - - r = manager_add_button(m, udev_device_get_sysname(d), &b); - if (r < 0) - return r; - - sn = udev_device_get_property_value(d, "ID_SEAT"); - if (isempty(sn)) - sn = "seat0"; - - button_set_seat(b, sn); - button_open(b); - } - - return 0; -} - -int manager_get_session_by_pid(Manager *m, pid_t pid, Session **session) { - _cleanup_free_ char *unit = NULL; - Session *s; - int r; - - assert(m); - - if (pid < 1) - return -EINVAL; - - r = cg_pid_get_unit(pid, &unit); - if (r < 0) - return 0; - - s = hashmap_get(m->session_units, unit); - if (!s) - return 0; - - if (session) - *session = s; - return 1; -} - -int manager_get_user_by_pid(Manager *m, pid_t pid, User **user) { - _cleanup_free_ char *unit = NULL; - User *u; - int r; - - assert(m); - assert(user); - - if (pid < 1) - return -EINVAL; - - r = cg_pid_get_slice(pid, &unit); - if (r < 0) - return 0; - - u = hashmap_get(m->user_units, unit); - if (!u) - return 0; - - *user = u; - return 1; -} - -int manager_get_idle_hint(Manager *m, dual_timestamp *t) { - Session *s; - bool idle_hint; - dual_timestamp ts = DUAL_TIMESTAMP_NULL; - Iterator i; - - assert(m); - - idle_hint = !manager_is_inhibited(m, INHIBIT_IDLE, INHIBIT_BLOCK, t, false, false, 0, NULL); - - HASHMAP_FOREACH(s, m->sessions, i) { - dual_timestamp k; - int ih; - - ih = session_get_idle_hint(s, &k); - if (ih < 0) - return ih; - - if (!ih) { - if (!idle_hint) { - if (k.monotonic < ts.monotonic) - ts = k; - } else { - idle_hint = false; - ts = k; - } - } else if (idle_hint) { - - if (k.monotonic > ts.monotonic) - ts = k; - } - } - - if (t) - *t = ts; - - return idle_hint; -} - -bool manager_shall_kill(Manager *m, const char *user) { - assert(m); - assert(user); - - if (!m->kill_exclude_users && streq(user, "root")) - return false; - - if (strv_contains(m->kill_exclude_users, user)) - return false; - - if (!strv_isempty(m->kill_only_users)) - return strv_contains(m->kill_only_users, user); - - return m->kill_user_processes; -} - -static int vt_is_busy(unsigned int vtnr) { - struct vt_stat vt_stat; - int r = 0; - _cleanup_close_ int fd; - - assert(vtnr >= 1); - - /* We explicitly open /dev/tty1 here instead of /dev/tty0. If - * we'd open the latter we'd open the foreground tty which - * hence would be unconditionally busy. By opening /dev/tty1 - * we avoid this. Since tty1 is special and needs to be an - * explicitly loaded getty or DM this is safe. */ - - fd = open_terminal("/dev/tty1", O_RDWR|O_NOCTTY|O_CLOEXEC); - if (fd < 0) - return -errno; - - if (ioctl(fd, VT_GETSTATE, &vt_stat) < 0) - r = -errno; - else - r = !!(vt_stat.v_state & (1 << vtnr)); - - return r; -} - -int manager_spawn_autovt(Manager *m, unsigned int vtnr) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - char name[sizeof("autovt@tty.service") + DECIMAL_STR_MAX(unsigned int)]; - int r; - - assert(m); - assert(vtnr >= 1); - - if (vtnr > m->n_autovts && - vtnr != m->reserve_vt) - return 0; - - if (vtnr != m->reserve_vt) { - /* If this is the reserved TTY, we'll start the getty - * on it in any case, but otherwise only if it is not - * busy. */ - - r = vt_is_busy(vtnr); - if (r < 0) - return r; - else if (r > 0) - return -EBUSY; - } - - snprintf(name, sizeof(name), "autovt@tty%u.service", vtnr); - r = sd_bus_call_method( - m->bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "StartUnit", - &error, - NULL, - "ss", name, "fail"); - if (r < 0) - log_error("Failed to start %s: %s", name, bus_error_message(&error, r)); - - return r; -} - -static bool manager_is_docked(Manager *m) { - Iterator i; - Button *b; - - HASHMAP_FOREACH(b, m->buttons, i) - if (b->docked) - return true; - - return false; -} - -static int manager_count_external_displays(Manager *m) { - _cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL; - struct udev_list_entry *item = NULL, *first = NULL; - int r; - int n = 0; - - e = udev_enumerate_new(m->udev); - if (!e) - return -ENOMEM; - - r = udev_enumerate_add_match_subsystem(e, "drm"); - if (r < 0) - return r; - - r = udev_enumerate_scan_devices(e); - if (r < 0) - return r; - - first = udev_enumerate_get_list_entry(e); - udev_list_entry_foreach(item, first) { - _cleanup_udev_device_unref_ struct udev_device *d = NULL; - struct udev_device *p; - const char *status, *enabled, *dash, *nn, *i; - bool external = false; - - d = udev_device_new_from_syspath(m->udev, udev_list_entry_get_name(item)); - if (!d) - return -ENOMEM; - - p = udev_device_get_parent(d); - if (!p) - continue; - - /* If the parent shares the same subsystem as the - * device we are looking at then it is a connector, - * which is what we are interested in. */ - if (!streq_ptr(udev_device_get_subsystem(p), "drm")) - continue; - - nn = udev_device_get_sysname(d); - if (!nn) - continue; - - /* Ignore internal displays: the type is encoded in - * the sysfs name, as the second dash seperated item - * (the first is the card name, the last the connector - * number). We implement a whitelist of external - * displays here, rather than a whitelist, to ensure - * we don't block suspends too eagerly. */ - dash = strchr(nn, '-'); - if (!dash) - continue; - - dash++; - FOREACH_STRING(i, "VGA-", "DVI-I-", "DVI-D-", "DVI-A-" - "Composite-", "SVIDEO-", "Component-", - "DIN-", "DP-", "HDMI-A-", "HDMI-B-", "TV-") { - - if (startswith(dash, i)) { - external = true; - break; - } - } - if (!external) - continue; - - /* Ignore ports that are not enabled */ - enabled = udev_device_get_sysattr_value(d, "enabled"); - if (!enabled) - continue; - if (!streq_ptr(enabled, "enabled")) - continue; - - /* We count any connector which is not explicitly - * "disconnected" as connected. */ - status = udev_device_get_sysattr_value(d, "status"); - if (!streq_ptr(status, "disconnected")) - n++; - } - - return n; -} - -bool manager_is_docked_or_external_displays(Manager *m) { - int n; - - /* If we are docked don't react to lid closing */ - if (manager_is_docked(m)) { - log_debug("System is docked."); - return true; - } - - /* If we have more than one display connected, - * assume that we are docked. */ - n = manager_count_external_displays(m); - if (n < 0) - log_warning_errno(n, "Display counting failed: %m"); - else if (n >= 1) { - log_debug("External (%i) displays connected.", n); - return true; - } - - return false; -} diff --git a/src/grp-login/liblogind-core/logind-dbus.c b/src/grp-login/liblogind-core/logind-dbus.c deleted file mode 100644 index cb51e43a5c..0000000000 --- a/src/grp-login/liblogind-core/logind-dbus.c +++ /dev/null @@ -1,3169 +0,0 @@ -/*** - 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 Lesser General Public License as published by - the Free Software Foundation; either version 2.1 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include - -#include - -#include "basic/alloc-util.h" -#include "basic/audit-util.h" -#include "basic/dirent-util.h" -#include "basic/escape.h" -#include "basic/fd-util.h" -#include "basic/fileio-label.h" -#include "basic/formats-util.h" -#include "basic/fs-util.h" -#include "basic/mkdir.h" -#include "basic/path-util.h" -#include "basic/process-util.h" -#include "basic/selinux-util.h" -#include "basic/special.h" -#include "basic/strv.h" -#include "basic/terminal-util.h" -#include "basic/unit-name.h" -#include "basic/user-util.h" -#include "logind.h" -#include "sd-bus/bus-common-errors.h" -#include "sd-bus/bus-error.h" -#include "shared/bus-util.h" -#include "shared/efivars.h" -#include "shared/sleep-config.h" -#include "shared/udev-util.h" -#include "shared/utmp-wtmp.h" - -int manager_get_session_from_creds(Manager *m, sd_bus_message *message, const char *name, sd_bus_error *error, Session **ret) { - _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; - Session *session; - int r; - - assert(m); - assert(message); - assert(ret); - - if (isempty(name)) { - r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_SESSION|SD_BUS_CREDS_AUGMENT, &creds); - if (r < 0) - return r; - - r = sd_bus_creds_get_session(creds, &name); - if (r < 0) - return r; - } - - session = hashmap_get(m->sessions, name); - if (!session) - return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_SESSION, "No session '%s' known", name); - - *ret = session; - return 0; -} - -int manager_get_user_from_creds(Manager *m, sd_bus_message *message, uid_t uid, sd_bus_error *error, User **ret) { - User *user; - int r; - - assert(m); - assert(message); - assert(ret); - - if (uid == UID_INVALID) { - _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; - - /* Note that we get the owner UID of the session, not the actual client UID here! */ - r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_OWNER_UID|SD_BUS_CREDS_AUGMENT, &creds); - if (r < 0) - return r; - - r = sd_bus_creds_get_owner_uid(creds, &uid); - if (r < 0) - return r; - } - - user = hashmap_get(m->users, UID_TO_PTR(uid)); - if (!user) - return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_USER, "No user "UID_FMT" known or logged in", uid); - - *ret = user; - return 0; -} - -int manager_get_seat_from_creds(Manager *m, sd_bus_message *message, const char *name, sd_bus_error *error, Seat **ret) { - Seat *seat; - int r; - - assert(m); - assert(message); - assert(ret); - - if (isempty(name)) { - Session *session; - - r = manager_get_session_from_creds(m, message, NULL, error, &session); - if (r < 0) - return r; - - seat = session->seat; - if (!seat) - return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_SEAT, "Session has no seat."); - } else { - seat = hashmap_get(m->seats, name); - if (!seat) - return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_SEAT, "No seat '%s' known", name); - } - - *ret = seat; - return 0; -} - -static int property_get_idle_hint( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - Manager *m = userdata; - - assert(bus); - assert(reply); - assert(m); - - return sd_bus_message_append(reply, "b", manager_get_idle_hint(m, NULL) > 0); -} - -static int property_get_idle_since_hint( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - Manager *m = userdata; - dual_timestamp t = DUAL_TIMESTAMP_NULL; - - assert(bus); - assert(reply); - assert(m); - - manager_get_idle_hint(m, &t); - - return sd_bus_message_append(reply, "t", streq(property, "IdleSinceHint") ? t.realtime : t.monotonic); -} - -static int property_get_inhibited( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - Manager *m = userdata; - InhibitWhat w; - - assert(bus); - assert(reply); - assert(m); - - w = manager_inhibit_what(m, streq(property, "BlockInhibited") ? INHIBIT_BLOCK : INHIBIT_DELAY); - - return sd_bus_message_append(reply, "s", inhibit_what_to_string(w)); -} - -static int property_get_preparing( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - Manager *m = userdata; - bool b; - - assert(bus); - assert(reply); - assert(m); - - if (streq(property, "PreparingForShutdown")) - b = !!(m->action_what & INHIBIT_SHUTDOWN); - else - b = !!(m->action_what & INHIBIT_SLEEP); - - return sd_bus_message_append(reply, "b", b); -} - -static int property_get_scheduled_shutdown( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - Manager *m = userdata; - int r; - - assert(bus); - assert(reply); - assert(m); - - r = sd_bus_message_open_container(reply, 'r', "st"); - if (r < 0) - return r; - - r = sd_bus_message_append(reply, "st", m->scheduled_shutdown_type, m->scheduled_shutdown_timeout); - if (r < 0) - return r; - - return sd_bus_message_close_container(reply); -} - -static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_handle_action, handle_action, HandleAction); - -static int property_get_docked( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - Manager *m = userdata; - - assert(bus); - assert(reply); - assert(m); - - return sd_bus_message_append(reply, "b", manager_is_docked_or_external_displays(m)); -} - -static int property_get_current_sessions( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - Manager *m = userdata; - - assert(bus); - assert(reply); - assert(m); - - return sd_bus_message_append(reply, "t", (uint64_t) hashmap_size(m->sessions)); -} - -static int property_get_current_inhibitors( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - Manager *m = userdata; - - assert(bus); - assert(reply); - assert(m); - - return sd_bus_message_append(reply, "t", (uint64_t) hashmap_size(m->inhibitors)); -} - -static int method_get_session(sd_bus_message *message, void *userdata, sd_bus_error *error) { - _cleanup_free_ char *p = NULL; - Manager *m = userdata; - const char *name; - Session *session; - int r; - - assert(message); - assert(m); - - r = sd_bus_message_read(message, "s", &name); - if (r < 0) - return r; - - r = manager_get_session_from_creds(m, message, name, error, &session); - if (r < 0) - return r; - - p = session_bus_path(session); - if (!p) - return -ENOMEM; - - return sd_bus_reply_method_return(message, "o", p); -} - -static int method_get_session_by_pid(sd_bus_message *message, void *userdata, sd_bus_error *error) { - _cleanup_free_ char *p = NULL; - Session *session = NULL; - Manager *m = userdata; - pid_t pid; - int r; - - assert(message); - assert(m); - - assert_cc(sizeof(pid_t) == sizeof(uint32_t)); - - r = sd_bus_message_read(message, "u", &pid); - if (r < 0) - return r; - if (pid < 0) - return -EINVAL; - - if (pid == 0) { - r = manager_get_session_from_creds(m, message, NULL, error, &session); - if (r < 0) - return r; - } else { - r = manager_get_session_by_pid(m, pid, &session); - if (r < 0) - return r; - - if (!session) - return sd_bus_error_setf(error, BUS_ERROR_NO_SESSION_FOR_PID, "PID "PID_FMT" does not belong to any known session", pid); - } - - p = session_bus_path(session); - if (!p) - return -ENOMEM; - - return sd_bus_reply_method_return(message, "o", p); -} - -static int method_get_user(sd_bus_message *message, void *userdata, sd_bus_error *error) { - _cleanup_free_ char *p = NULL; - Manager *m = userdata; - uint32_t uid; - User *user; - int r; - - assert(message); - assert(m); - - r = sd_bus_message_read(message, "u", &uid); - if (r < 0) - return r; - - r = manager_get_user_from_creds(m, message, uid, error, &user); - if (r < 0) - return r; - - p = user_bus_path(user); - if (!p) - return -ENOMEM; - - return sd_bus_reply_method_return(message, "o", p); -} - -static int method_get_user_by_pid(sd_bus_message *message, void *userdata, sd_bus_error *error) { - _cleanup_free_ char *p = NULL; - Manager *m = userdata; - User *user = NULL; - pid_t pid; - int r; - - assert(message); - assert(m); - - assert_cc(sizeof(pid_t) == sizeof(uint32_t)); - - r = sd_bus_message_read(message, "u", &pid); - if (r < 0) - return r; - if (pid < 0) - return -EINVAL; - - if (pid == 0) { - r = manager_get_user_from_creds(m, message, UID_INVALID, error, &user); - if (r < 0) - return r; - } else { - r = manager_get_user_by_pid(m, pid, &user); - if (r < 0) - return r; - if (!user) - return sd_bus_error_setf(error, BUS_ERROR_NO_USER_FOR_PID, "PID "PID_FMT" does not belong to any known or logged in user", pid); - } - - p = user_bus_path(user); - if (!p) - return -ENOMEM; - - return sd_bus_reply_method_return(message, "o", p); -} - -static int method_get_seat(sd_bus_message *message, void *userdata, sd_bus_error *error) { - _cleanup_free_ char *p = NULL; - Manager *m = userdata; - const char *name; - Seat *seat; - int r; - - assert(message); - assert(m); - - r = sd_bus_message_read(message, "s", &name); - if (r < 0) - return r; - - r = manager_get_seat_from_creds(m, message, name, error, &seat); - if (r < 0) - return r; - - p = seat_bus_path(seat); - if (!p) - return -ENOMEM; - - return sd_bus_reply_method_return(message, "o", p); -} - -static int method_list_sessions(sd_bus_message *message, void *userdata, sd_bus_error *error) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - Manager *m = userdata; - Session *session; - Iterator i; - int r; - - assert(message); - assert(m); - - r = sd_bus_message_new_method_return(message, &reply); - if (r < 0) - return r; - - r = sd_bus_message_open_container(reply, 'a', "(susso)"); - if (r < 0) - return r; - - HASHMAP_FOREACH(session, m->sessions, i) { - _cleanup_free_ char *p = NULL; - - p = session_bus_path(session); - if (!p) - return -ENOMEM; - - r = sd_bus_message_append(reply, "(susso)", - session->id, - (uint32_t) session->user->uid, - session->user->name, - session->seat ? session->seat->id : "", - p); - if (r < 0) - return r; - } - - r = sd_bus_message_close_container(reply); - if (r < 0) - return r; - - return sd_bus_send(NULL, reply, NULL); -} - -static int method_list_users(sd_bus_message *message, void *userdata, sd_bus_error *error) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - Manager *m = userdata; - User *user; - Iterator i; - int r; - - assert(message); - assert(m); - - r = sd_bus_message_new_method_return(message, &reply); - if (r < 0) - return r; - - r = sd_bus_message_open_container(reply, 'a', "(uso)"); - if (r < 0) - return r; - - HASHMAP_FOREACH(user, m->users, i) { - _cleanup_free_ char *p = NULL; - - p = user_bus_path(user); - if (!p) - return -ENOMEM; - - r = sd_bus_message_append(reply, "(uso)", - (uint32_t) user->uid, - user->name, - p); - if (r < 0) - return r; - } - - r = sd_bus_message_close_container(reply); - if (r < 0) - return r; - - return sd_bus_send(NULL, reply, NULL); -} - -static int method_list_seats(sd_bus_message *message, void *userdata, sd_bus_error *error) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - Manager *m = userdata; - Seat *seat; - Iterator i; - int r; - - assert(message); - assert(m); - - r = sd_bus_message_new_method_return(message, &reply); - if (r < 0) - return r; - - r = sd_bus_message_open_container(reply, 'a', "(so)"); - if (r < 0) - return r; - - HASHMAP_FOREACH(seat, m->seats, i) { - _cleanup_free_ char *p = NULL; - - p = seat_bus_path(seat); - if (!p) - return -ENOMEM; - - r = sd_bus_message_append(reply, "(so)", seat->id, p); - if (r < 0) - return r; - } - - r = sd_bus_message_close_container(reply); - if (r < 0) - return r; - - return sd_bus_send(NULL, reply, NULL); -} - -static int method_list_inhibitors(sd_bus_message *message, void *userdata, sd_bus_error *error) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - Manager *m = userdata; - Inhibitor *inhibitor; - Iterator i; - int r; - - assert(message); - assert(m); - - r = sd_bus_message_new_method_return(message, &reply); - if (r < 0) - return r; - - r = sd_bus_message_open_container(reply, 'a', "(ssssuu)"); - if (r < 0) - return r; - - HASHMAP_FOREACH(inhibitor, m->inhibitors, i) { - - r = sd_bus_message_append(reply, "(ssssuu)", - strempty(inhibit_what_to_string(inhibitor->what)), - strempty(inhibitor->who), - strempty(inhibitor->why), - strempty(inhibit_mode_to_string(inhibitor->mode)), - (uint32_t) inhibitor->uid, - (uint32_t) inhibitor->pid); - if (r < 0) - return r; - } - - r = sd_bus_message_close_container(reply); - if (r < 0) - return r; - - return sd_bus_send(NULL, reply, NULL); -} - -static int method_create_session(sd_bus_message *message, void *userdata, sd_bus_error *error) { - const char *service, *type, *class, *cseat, *tty, *display, *remote_user, *remote_host, *desktop; - uint32_t audit_id = 0; - _cleanup_free_ char *id = NULL; - Session *session = NULL; - Manager *m = userdata; - User *user = NULL; - Seat *seat = NULL; - pid_t leader; - uid_t uid; - int remote; - uint32_t vtnr = 0; - SessionType t; - SessionClass c; - int r; - - assert(message); - assert(m); - - assert_cc(sizeof(pid_t) == sizeof(uint32_t)); - assert_cc(sizeof(uid_t) == sizeof(uint32_t)); - - r = sd_bus_message_read(message, "uusssssussbss", &uid, &leader, &service, &type, &class, &desktop, &cseat, &vtnr, &tty, &display, &remote, &remote_user, &remote_host); - if (r < 0) - return r; - - if (!uid_is_valid(uid)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid UID"); - if (leader < 0 || leader == 1) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid leader PID"); - - if (isempty(type)) - t = _SESSION_TYPE_INVALID; - else { - t = session_type_from_string(type); - if (t < 0) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid session type %s", type); - } - - if (isempty(class)) - c = _SESSION_CLASS_INVALID; - else { - c = session_class_from_string(class); - if (c < 0) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid session class %s", class); - } - - if (isempty(desktop)) - desktop = NULL; - else { - if (!string_is_safe(desktop)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid desktop string %s", desktop); - } - - if (isempty(cseat)) - seat = NULL; - else { - seat = hashmap_get(m->seats, cseat); - if (!seat) - return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_SEAT, "No seat '%s' known", cseat); - } - - if (tty_is_vc(tty)) { - int v; - - if (!seat) - seat = m->seat0; - else if (seat != m->seat0) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "TTY %s is virtual console but seat %s is not seat0", tty, seat->id); - - v = vtnr_from_tty(tty); - if (v <= 0) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Cannot determine VT number from virtual console TTY %s", tty); - - if (!vtnr) - vtnr = (uint32_t) v; - else if (vtnr != (uint32_t) v) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Specified TTY and VT number do not match"); - - } else if (tty_is_console(tty)) { - - if (!seat) - seat = m->seat0; - else if (seat != m->seat0) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Console TTY specified but seat is not seat0"); - - if (vtnr != 0) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Console TTY specified but VT number is not 0"); - } - - if (seat) { - if (seat_has_vts(seat)) { - if (!vtnr || vtnr > 63) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "VT number out of range"); - } else { - if (vtnr != 0) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Seat has no VTs but VT number not 0"); - } - } - - r = sd_bus_message_enter_container(message, 'a', "(sv)"); - if (r < 0) - return r; - - if (t == _SESSION_TYPE_INVALID) { - if (!isempty(display)) - t = SESSION_X11; - else if (!isempty(tty)) - t = SESSION_TTY; - else - t = SESSION_UNSPECIFIED; - } - - if (c == _SESSION_CLASS_INVALID) { - if (t == SESSION_UNSPECIFIED) - c = SESSION_BACKGROUND; - else - c = SESSION_USER; - } - - if (leader == 0) { - _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; - - r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_PID, &creds); - if (r < 0) - return r; - - r = sd_bus_creds_get_pid(creds, (pid_t*) &leader); - if (r < 0) - return r; - } - - r = manager_get_session_by_pid(m, leader, NULL); - if (r > 0) - return sd_bus_error_setf(error, BUS_ERROR_SESSION_BUSY, "Already running in a session"); - - /* - * Old gdm and lightdm start the user-session on the same VT as - * the greeter session. But they destroy the greeter session - * after the user-session and want the user-session to take - * over the VT. We need to support this for - * backwards-compatibility, so make sure we allow new sessions - * on a VT that a greeter is running on. Furthermore, to allow - * re-logins, we have to allow a greeter to take over a used VT for - * the exact same reasons. - */ - if (c != SESSION_GREETER && - vtnr > 0 && - vtnr < m->seat0->position_count && - m->seat0->positions[vtnr] && - m->seat0->positions[vtnr]->class != SESSION_GREETER) - return sd_bus_error_setf(error, BUS_ERROR_SESSION_BUSY, "Already occupied by a session"); - - if (hashmap_size(m->sessions) >= m->sessions_max) - return sd_bus_error_setf(error, SD_BUS_ERROR_LIMITS_EXCEEDED, "Maximum number of sessions (%" PRIu64 ") reached, refusing further sessions.", m->sessions_max); - - audit_session_from_pid(leader, &audit_id); - if (audit_id > 0) { - /* Keep our session IDs and the audit session IDs in sync */ - - if (asprintf(&id, "%"PRIu32, audit_id) < 0) - return -ENOMEM; - - /* Wut? There's already a session by this name and we - * didn't find it above? Weird, then let's not trust - * the audit data and let's better register a new - * ID */ - if (hashmap_get(m->sessions, id)) { - log_warning("Existing logind session ID %s used by new audit session, ignoring", id); - audit_id = 0; - - id = mfree(id); - } - } - - if (!id) { - do { - id = mfree(id); - - if (asprintf(&id, "c%lu", ++m->session_counter) < 0) - return -ENOMEM; - - } while (hashmap_get(m->sessions, id)); - } - - r = manager_add_user_by_uid(m, uid, &user); - if (r < 0) - goto fail; - - r = manager_add_session(m, id, &session); - if (r < 0) - goto fail; - - session_set_user(session, user); - - session->leader = leader; - session->audit_id = audit_id; - session->type = t; - session->class = c; - session->remote = remote; - session->vtnr = vtnr; - - if (!isempty(tty)) { - session->tty = strdup(tty); - if (!session->tty) { - r = -ENOMEM; - goto fail; - } - } - - if (!isempty(display)) { - session->display = strdup(display); - if (!session->display) { - r = -ENOMEM; - goto fail; - } - } - - if (!isempty(remote_user)) { - session->remote_user = strdup(remote_user); - if (!session->remote_user) { - r = -ENOMEM; - goto fail; - } - } - - if (!isempty(remote_host)) { - session->remote_host = strdup(remote_host); - if (!session->remote_host) { - r = -ENOMEM; - goto fail; - } - } - - if (!isempty(service)) { - session->service = strdup(service); - if (!session->service) { - r = -ENOMEM; - goto fail; - } - } - - if (!isempty(desktop)) { - session->desktop = strdup(desktop); - if (!session->desktop) { - r = -ENOMEM; - goto fail; - } - } - - if (seat) { - r = seat_attach_session(seat, session); - if (r < 0) - goto fail; - } - - r = session_start(session); - if (r < 0) - goto fail; - - session->create_message = sd_bus_message_ref(message); - - /* Now, let's wait until the slice unit and stuff got - * created. We send the reply back from - * session_send_create_reply(). */ - - return 1; - -fail: - if (session) - session_add_to_gc_queue(session); - - if (user) - user_add_to_gc_queue(user); - - return r; -} - -static int method_release_session(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Manager *m = userdata; - Session *session; - const char *name; - int r; - - assert(message); - assert(m); - - r = sd_bus_message_read(message, "s", &name); - if (r < 0) - return r; - - r = manager_get_session_from_creds(m, message, name, error, &session); - if (r < 0) - return r; - - r = session_release(session); - if (r < 0) - return r; - - return sd_bus_reply_method_return(message, NULL); -} - -static int method_activate_session(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Manager *m = userdata; - Session *session; - const char *name; - int r; - - assert(message); - assert(m); - - r = sd_bus_message_read(message, "s", &name); - if (r < 0) - return r; - - r = manager_get_session_from_creds(m, message, name, error, &session); - if (r < 0) - return r; - - return bus_session_method_activate(message, session, error); -} - -static int method_activate_session_on_seat(sd_bus_message *message, void *userdata, sd_bus_error *error) { - const char *session_name, *seat_name; - Manager *m = userdata; - Session *session; - Seat *seat; - int r; - - assert(message); - assert(m); - - /* Same as ActivateSession() but refuses to work if - * the seat doesn't match */ - - r = sd_bus_message_read(message, "ss", &session_name, &seat_name); - if (r < 0) - return r; - - r = manager_get_session_from_creds(m, message, session_name, error, &session); - if (r < 0) - return r; - - r = manager_get_seat_from_creds(m, message, seat_name, error, &seat); - if (r < 0) - return r; - - if (session->seat != seat) - return sd_bus_error_setf(error, BUS_ERROR_SESSION_NOT_ON_SEAT, "Session %s not on seat %s", session_name, seat_name); - - r = session_activate(session); - if (r < 0) - return r; - - return sd_bus_reply_method_return(message, NULL); -} - -static int method_lock_session(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Manager *m = userdata; - Session *session; - const char *name; - int r; - - assert(message); - assert(m); - - r = sd_bus_message_read(message, "s", &name); - if (r < 0) - return r; - - r = manager_get_session_from_creds(m, message, name, error, &session); - if (r < 0) - return r; - - return bus_session_method_lock(message, session, error); -} - -static int method_lock_sessions(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Manager *m = userdata; - int r; - - assert(message); - assert(m); - - r = bus_verify_polkit_async( - message, - CAP_SYS_ADMIN, - "org.freedesktop.login1.lock-sessions", - NULL, - false, - UID_INVALID, - &m->polkit_registry, - error); - if (r < 0) - return r; - if (r == 0) - return 1; /* Will call us back */ - - r = session_send_lock_all(m, streq(sd_bus_message_get_member(message), "LockSessions")); - if (r < 0) - return r; - - return sd_bus_reply_method_return(message, NULL); -} - -static int method_kill_session(sd_bus_message *message, void *userdata, sd_bus_error *error) { - const char *name; - Manager *m = userdata; - Session *session; - int r; - - assert(message); - assert(m); - - r = sd_bus_message_read(message, "s", &name); - if (r < 0) - return r; - - r = manager_get_session_from_creds(m, message, name, error, &session); - if (r < 0) - return r; - - return bus_session_method_kill(message, session, error); -} - -static int method_kill_user(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Manager *m = userdata; - uint32_t uid; - User *user; - int r; - - assert(message); - assert(m); - - r = sd_bus_message_read(message, "u", &uid); - if (r < 0) - return r; - - r = manager_get_user_from_creds(m, message, uid, error, &user); - if (r < 0) - return r; - - return bus_user_method_kill(message, user, error); -} - -static int method_terminate_session(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Manager *m = userdata; - const char *name; - Session *session; - int r; - - assert(message); - assert(m); - - r = sd_bus_message_read(message, "s", &name); - if (r < 0) - return r; - - r = manager_get_session_from_creds(m, message, name, error, &session); - if (r < 0) - return r; - - return bus_session_method_terminate(message, session, error); -} - -static int method_terminate_user(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Manager *m = userdata; - uint32_t uid; - User *user; - int r; - - assert(message); - assert(m); - - r = sd_bus_message_read(message, "u", &uid); - if (r < 0) - return r; - - r = manager_get_user_from_creds(m, message, uid, error, &user); - if (r < 0) - return r; - - return bus_user_method_terminate(message, user, error); -} - -static int method_terminate_seat(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Manager *m = userdata; - const char *name; - Seat *seat; - int r; - - assert(message); - assert(m); - - r = sd_bus_message_read(message, "s", &name); - if (r < 0) - return r; - - r = manager_get_seat_from_creds(m, message, name, error, &seat); - if (r < 0) - return r; - - return bus_seat_method_terminate(message, seat, error); -} - -static int method_set_user_linger(sd_bus_message *message, void *userdata, sd_bus_error *error) { - _cleanup_free_ char *cc = NULL; - Manager *m = userdata; - int r, b, interactive; - struct passwd *pw; - const char *path; - uint32_t uid; - bool self = false; - - assert(message); - assert(m); - - r = sd_bus_message_read(message, "ubb", &uid, &b, &interactive); - if (r < 0) - return r; - - if (uid == UID_INVALID) { - _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; - - /* Note that we get the owner UID of the session, not the actual client UID here! */ - r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_OWNER_UID|SD_BUS_CREDS_AUGMENT, &creds); - if (r < 0) - return r; - - r = sd_bus_creds_get_owner_uid(creds, &uid); - if (r < 0) - return r; - - self = true; - - } else if (!uid_is_valid(uid)) - return -EINVAL; - - errno = 0; - pw = getpwuid(uid); - if (!pw) - return errno > 0 ? -errno : -ENOENT; - - r = bus_verify_polkit_async( - message, - CAP_SYS_ADMIN, - self ? "org.freedesktop.login1.set-self-linger" : "org.freedesktop.login1.set-user-linger", - NULL, - interactive, - UID_INVALID, - &m->polkit_registry, - error); - if (r < 0) - return r; - if (r == 0) - return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ - - mkdir_p_label("/var/lib/systemd", 0755); - - r = mkdir_safe_label("/var/lib/systemd/linger", 0755, 0, 0); - if (r < 0) - return r; - - cc = cescape(pw->pw_name); - if (!cc) - return -ENOMEM; - - path = strjoina("/var/lib/systemd/linger/", cc); - if (b) { - User *u; - - r = touch(path); - if (r < 0) - return r; - - if (manager_add_user_by_uid(m, uid, &u) >= 0) - user_start(u); - - } else { - User *u; - - r = unlink(path); - if (r < 0 && errno != ENOENT) - return -errno; - - u = hashmap_get(m->users, UID_TO_PTR(uid)); - if (u) - user_add_to_gc_queue(u); - } - - return sd_bus_reply_method_return(message, NULL); -} - -static int trigger_device(Manager *m, struct udev_device *d) { - _cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL; - struct udev_list_entry *first, *item; - int r; - - assert(m); - - e = udev_enumerate_new(m->udev); - if (!e) - return -ENOMEM; - - if (d) { - r = udev_enumerate_add_match_parent(e, d); - if (r < 0) - return r; - } - - r = udev_enumerate_scan_devices(e); - if (r < 0) - return r; - - first = udev_enumerate_get_list_entry(e); - udev_list_entry_foreach(item, first) { - _cleanup_free_ char *t = NULL; - const char *p; - - p = udev_list_entry_get_name(item); - - t = strappend(p, "/uevent"); - if (!t) - return -ENOMEM; - - write_string_file(t, "change", WRITE_STRING_FILE_CREATE); - } - - return 0; -} - -static int attach_device(Manager *m, const char *seat, const char *sysfs) { - _cleanup_udev_device_unref_ struct udev_device *d = NULL; - _cleanup_free_ char *rule = NULL, *file = NULL; - const char *id_for_seat; - int r; - - assert(m); - assert(seat); - assert(sysfs); - - d = udev_device_new_from_syspath(m->udev, sysfs); - if (!d) - return -ENODEV; - - if (!udev_device_has_tag(d, "seat")) - return -ENODEV; - - id_for_seat = udev_device_get_property_value(d, "ID_FOR_SEAT"); - if (!id_for_seat) - return -ENODEV; - - if (asprintf(&file, "/etc/udev/rules.d/72-seat-%s.rules", id_for_seat) < 0) - return -ENOMEM; - - if (asprintf(&rule, "TAG==\"seat\", ENV{ID_FOR_SEAT}==\"%s\", ENV{ID_SEAT}=\"%s\"", id_for_seat, seat) < 0) - return -ENOMEM; - - mkdir_p_label("/etc/udev/rules.d", 0755); - r = write_string_file_atomic_label(file, rule); - if (r < 0) - return r; - - return trigger_device(m, d); -} - -static int flush_devices(Manager *m) { - _cleanup_closedir_ DIR *d; - - assert(m); - - d = opendir("/etc/udev/rules.d"); - if (!d) { - if (errno != ENOENT) - log_warning_errno(errno, "Failed to open /etc/udev/rules.d: %m"); - } else { - struct dirent *de; - - while ((de = readdir(d))) { - - if (!dirent_is_file(de)) - continue; - - if (!startswith(de->d_name, "72-seat-")) - continue; - - if (!endswith(de->d_name, ".rules")) - continue; - - if (unlinkat(dirfd(d), de->d_name, 0) < 0) - log_warning_errno(errno, "Failed to unlink %s: %m", de->d_name); - } - } - - return trigger_device(m, NULL); -} - -static int method_attach_device(sd_bus_message *message, void *userdata, sd_bus_error *error) { - const char *sysfs, *seat; - Manager *m = userdata; - int interactive, r; - - assert(message); - assert(m); - - r = sd_bus_message_read(message, "ssb", &seat, &sysfs, &interactive); - if (r < 0) - return r; - - if (!path_startswith(sysfs, "/sys")) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Path %s is not in /sys", sysfs); - - if (!seat_name_is_valid(seat)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Seat %s is not valid", seat); - - r = bus_verify_polkit_async( - message, - CAP_SYS_ADMIN, - "org.freedesktop.login1.attach-device", - NULL, - interactive, - UID_INVALID, - &m->polkit_registry, - error); - if (r < 0) - return r; - if (r == 0) - return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ - - r = attach_device(m, seat, sysfs); - if (r < 0) - return r; - - return sd_bus_reply_method_return(message, NULL); -} - -static int method_flush_devices(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Manager *m = userdata; - int interactive, r; - - assert(message); - assert(m); - - r = sd_bus_message_read(message, "b", &interactive); - if (r < 0) - return r; - - r = bus_verify_polkit_async( - message, - CAP_SYS_ADMIN, - "org.freedesktop.login1.flush-devices", - NULL, - interactive, - UID_INVALID, - &m->polkit_registry, - error); - if (r < 0) - return r; - if (r == 0) - return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ - - r = flush_devices(m); - if (r < 0) - return r; - - return sd_bus_reply_method_return(message, NULL); -} - -static int have_multiple_sessions( - Manager *m, - uid_t uid) { - - Session *session; - Iterator i; - - assert(m); - - /* Check for other users' sessions. Greeter sessions do not - * count, and non-login sessions do not count either. */ - HASHMAP_FOREACH(session, m->sessions, i) - if (session->class == SESSION_USER && - session->user->uid != uid) - return true; - - return false; -} - -static int bus_manager_log_shutdown( - Manager *m, - InhibitWhat w, - const char *unit_name) { - - const char *p, *q; - - assert(m); - assert(unit_name); - - if (w != INHIBIT_SHUTDOWN) - return 0; - - if (streq(unit_name, SPECIAL_POWEROFF_TARGET)) { - p = "MESSAGE=System is powering down"; - q = "SHUTDOWN=power-off"; - } else if (streq(unit_name, SPECIAL_HALT_TARGET)) { - p = "MESSAGE=System is halting"; - q = "SHUTDOWN=halt"; - } else if (streq(unit_name, SPECIAL_REBOOT_TARGET)) { - p = "MESSAGE=System is rebooting"; - q = "SHUTDOWN=reboot"; - } else if (streq(unit_name, SPECIAL_KEXEC_TARGET)) { - p = "MESSAGE=System is rebooting with kexec"; - q = "SHUTDOWN=kexec"; - } else { - p = "MESSAGE=System is shutting down"; - q = NULL; - } - - if (isempty(m->wall_message)) - p = strjoina(p, "."); - else - p = strjoina(p, " (", m->wall_message, ")."); - - return log_struct(LOG_NOTICE, - LOG_MESSAGE_ID(SD_MESSAGE_SHUTDOWN), - p, - q, - NULL); -} - -static int lid_switch_ignore_handler(sd_event_source *e, uint64_t usec, void *userdata) { - Manager *m = userdata; - - assert(e); - assert(m); - - m->lid_switch_ignore_event_source = sd_event_source_unref(m->lid_switch_ignore_event_source); - return 0; -} - -int manager_set_lid_switch_ignore(Manager *m, usec_t until) { - int r; - - assert(m); - - if (until <= now(CLOCK_MONOTONIC)) - return 0; - - /* We want to ignore the lid switch for a while after each - * suspend, and after boot-up. Hence let's install a timer for - * this. As long as the event source exists we ignore the lid - * switch. */ - - if (m->lid_switch_ignore_event_source) { - usec_t u; - - r = sd_event_source_get_time(m->lid_switch_ignore_event_source, &u); - if (r < 0) - return r; - - if (until <= u) - return 0; - - r = sd_event_source_set_time(m->lid_switch_ignore_event_source, until); - } else - r = sd_event_add_time( - m->event, - &m->lid_switch_ignore_event_source, - CLOCK_MONOTONIC, - until, 0, - lid_switch_ignore_handler, m); - - return r; -} - -static void reset_scheduled_shutdown(Manager *m) { - m->scheduled_shutdown_timeout_source = sd_event_source_unref(m->scheduled_shutdown_timeout_source); - m->wall_message_timeout_source = sd_event_source_unref(m->wall_message_timeout_source); - m->nologin_timeout_source = sd_event_source_unref(m->nologin_timeout_source); - m->scheduled_shutdown_type = mfree(m->scheduled_shutdown_type); - m->scheduled_shutdown_timeout = 0; - m->shutdown_dry_run = false; - - if (m->unlink_nologin) { - (void) unlink("/run/nologin"); - m->unlink_nologin = false; - } -} - -static int execute_shutdown_or_sleep( - Manager *m, - InhibitWhat w, - const char *unit_name, - sd_bus_error *error) { - - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - char *c = NULL; - const char *p; - int r; - - assert(m); - assert(w >= 0); - assert(w < _INHIBIT_WHAT_MAX); - assert(unit_name); - - bus_manager_log_shutdown(m, w, unit_name); - - if (m->shutdown_dry_run) { - log_info("Running in dry run, suppressing action."); - reset_scheduled_shutdown(m); - } else { - r = sd_bus_call_method( - m->bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "StartUnit", - error, - &reply, - "ss", unit_name, "replace-irreversibly"); - if (r < 0) - return r; - - r = sd_bus_message_read(reply, "o", &p); - if (r < 0) - return r; - - c = strdup(p); - if (!c) - return -ENOMEM; - } - - m->action_unit = unit_name; - free(m->action_job); - m->action_job = c; - m->action_what = w; - - /* Make sure the lid switch is ignored for a while */ - manager_set_lid_switch_ignore(m, now(CLOCK_MONOTONIC) + m->holdoff_timeout_usec); - - return 0; -} - -int manager_dispatch_delayed(Manager *manager, bool timeout) { - - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - Inhibitor *offending = NULL; - int r; - - assert(manager); - - if (manager->action_what == 0 || manager->action_job) - return 0; - - if (manager_is_inhibited(manager, manager->action_what, INHIBIT_DELAY, NULL, false, false, 0, &offending)) { - _cleanup_free_ char *comm = NULL, *u = NULL; - - if (!timeout) - return 0; - - (void) get_process_comm(offending->pid, &comm); - u = uid_to_name(offending->uid); - - log_notice("Delay lock is active (UID "UID_FMT"/%s, PID "PID_FMT"/%s) but inhibitor timeout is reached.", - offending->uid, strna(u), - offending->pid, strna(comm)); - } - - /* Actually do the operation */ - r = execute_shutdown_or_sleep(manager, manager->action_what, manager->action_unit, &error); - if (r < 0) { - log_warning("Failed to send delayed message: %s", bus_error_message(&error, r)); - - manager->action_unit = NULL; - manager->action_what = 0; - return r; - } - - return 1; -} - -static int manager_inhibit_timeout_handler( - sd_event_source *s, - uint64_t usec, - void *userdata) { - - Manager *manager = userdata; - int r; - - assert(manager); - assert(manager->inhibit_timeout_source == s); - - r = manager_dispatch_delayed(manager, true); - return (r < 0) ? r : 0; -} - -static int delay_shutdown_or_sleep( - Manager *m, - InhibitWhat w, - const char *unit_name) { - - int r; - usec_t timeout_val; - - assert(m); - assert(w >= 0); - assert(w < _INHIBIT_WHAT_MAX); - assert(unit_name); - - timeout_val = now(CLOCK_MONOTONIC) + m->inhibit_delay_max; - - if (m->inhibit_timeout_source) { - r = sd_event_source_set_time(m->inhibit_timeout_source, timeout_val); - if (r < 0) - return log_error_errno(r, "sd_event_source_set_time() failed: %m"); - - r = sd_event_source_set_enabled(m->inhibit_timeout_source, SD_EVENT_ONESHOT); - if (r < 0) - return log_error_errno(r, "sd_event_source_set_enabled() failed: %m"); - } else { - r = sd_event_add_time(m->event, &m->inhibit_timeout_source, CLOCK_MONOTONIC, - timeout_val, 0, manager_inhibit_timeout_handler, m); - if (r < 0) - return r; - } - - m->action_unit = unit_name; - m->action_what = w; - - return 0; -} - -static int send_prepare_for(Manager *m, InhibitWhat w, bool _active) { - - static const char * const signal_name[_INHIBIT_WHAT_MAX] = { - [INHIBIT_SHUTDOWN] = "PrepareForShutdown", - [INHIBIT_SLEEP] = "PrepareForSleep" - }; - - int active = _active; - - assert(m); - assert(w >= 0); - assert(w < _INHIBIT_WHAT_MAX); - assert(signal_name[w]); - - return sd_bus_emit_signal(m->bus, - "/org/freedesktop/login1", - "org.freedesktop.login1.Manager", - signal_name[w], - "b", - active); -} - -int bus_manager_shutdown_or_sleep_now_or_later( - Manager *m, - const char *unit_name, - InhibitWhat w, - sd_bus_error *error) { - - bool delayed; - int r; - - assert(m); - assert(unit_name); - assert(w >= 0); - assert(w <= _INHIBIT_WHAT_MAX); - assert(!m->action_job); - - /* Tell everybody to prepare for shutdown/sleep */ - send_prepare_for(m, w, true); - - delayed = - m->inhibit_delay_max > 0 && - manager_is_inhibited(m, w, INHIBIT_DELAY, NULL, false, false, 0, NULL); - - if (delayed) - /* Shutdown is delayed, keep in mind what we - * want to do, and start a timeout */ - r = delay_shutdown_or_sleep(m, w, unit_name); - else - /* Shutdown is not delayed, execute it - * immediately */ - r = execute_shutdown_or_sleep(m, w, unit_name, error); - - return r; -} - -static int verify_shutdown_creds( - Manager *m, - sd_bus_message *message, - InhibitWhat w, - bool interactive, - const char *action, - const char *action_multiple_sessions, - const char *action_ignore_inhibit, - sd_bus_error *error) { - - _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; - bool multiple_sessions, blocked; - uid_t uid; - int r; - - assert(m); - assert(message); - assert(w >= 0); - assert(w <= _INHIBIT_WHAT_MAX); - - r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_EUID, &creds); - if (r < 0) - return r; - - r = sd_bus_creds_get_euid(creds, &uid); - if (r < 0) - return r; - - r = have_multiple_sessions(m, uid); - if (r < 0) - return r; - - multiple_sessions = r > 0; - blocked = manager_is_inhibited(m, w, INHIBIT_BLOCK, NULL, false, true, uid, NULL); - - if (multiple_sessions && action_multiple_sessions) { - r = bus_verify_polkit_async(message, CAP_SYS_BOOT, action_multiple_sessions, NULL, interactive, UID_INVALID, &m->polkit_registry, error); - if (r < 0) - return r; - if (r == 0) - return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ - } - - if (blocked && action_ignore_inhibit) { - r = bus_verify_polkit_async(message, CAP_SYS_BOOT, action_ignore_inhibit, NULL, interactive, UID_INVALID, &m->polkit_registry, error); - if (r < 0) - return r; - if (r == 0) - return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ - } - - if (!multiple_sessions && !blocked && action) { - r = bus_verify_polkit_async(message, CAP_SYS_BOOT, action, NULL, interactive, UID_INVALID, &m->polkit_registry, error); - if (r < 0) - return r; - if (r == 0) - return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ - } - - return 0; -} - -static int method_do_shutdown_or_sleep( - Manager *m, - sd_bus_message *message, - const char *unit_name, - InhibitWhat w, - const char *action, - const char *action_multiple_sessions, - const char *action_ignore_inhibit, - const char *sleep_verb, - sd_bus_error *error) { - - int interactive, r; - - assert(m); - assert(message); - assert(unit_name); - assert(w >= 0); - assert(w <= _INHIBIT_WHAT_MAX); - - r = sd_bus_message_read(message, "b", &interactive); - if (r < 0) - return r; - - /* Don't allow multiple jobs being executed at the same time */ - if (m->action_what) - return sd_bus_error_setf(error, BUS_ERROR_OPERATION_IN_PROGRESS, "There's already a shutdown or sleep operation in progress"); - - if (sleep_verb) { - r = can_sleep(sleep_verb); - if (r < 0) - return r; - - if (r == 0) - return sd_bus_error_setf(error, BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED, "Sleep verb not supported"); - } - - r = verify_shutdown_creds(m, message, w, interactive, action, action_multiple_sessions, - action_ignore_inhibit, error); - if (r != 0) - return r; - - r = bus_manager_shutdown_or_sleep_now_or_later(m, unit_name, w, error); - if (r < 0) - return r; - - return sd_bus_reply_method_return(message, NULL); -} - -static int method_poweroff(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Manager *m = userdata; - - return method_do_shutdown_or_sleep( - m, message, - SPECIAL_POWEROFF_TARGET, - INHIBIT_SHUTDOWN, - "org.freedesktop.login1.power-off", - "org.freedesktop.login1.power-off-multiple-sessions", - "org.freedesktop.login1.power-off-ignore-inhibit", - NULL, - error); -} - -static int method_reboot(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Manager *m = userdata; - - return method_do_shutdown_or_sleep( - m, message, - SPECIAL_REBOOT_TARGET, - INHIBIT_SHUTDOWN, - "org.freedesktop.login1.reboot", - "org.freedesktop.login1.reboot-multiple-sessions", - "org.freedesktop.login1.reboot-ignore-inhibit", - NULL, - error); -} - -static int method_suspend(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Manager *m = userdata; - - return method_do_shutdown_or_sleep( - m, message, - SPECIAL_SUSPEND_TARGET, - INHIBIT_SLEEP, - "org.freedesktop.login1.suspend", - "org.freedesktop.login1.suspend-multiple-sessions", - "org.freedesktop.login1.suspend-ignore-inhibit", - "suspend", - error); -} - -static int nologin_timeout_handler( - sd_event_source *s, - uint64_t usec, - void *userdata) { - - Manager *m = userdata; - int r; - - log_info("Creating /run/nologin, blocking further logins..."); - - r = write_string_file_atomic_label("/run/nologin", "System is going down."); - if (r < 0) - log_error_errno(r, "Failed to create /run/nologin: %m"); - else - m->unlink_nologin = true; - - return 0; -} - -static int update_schedule_file(Manager *m) { - _cleanup_free_ char *temp_path = NULL; - _cleanup_fclose_ FILE *f = NULL; - int r; - - assert(m); - - r = mkdir_safe_label("/run/systemd/shutdown", 0755, 0, 0); - if (r < 0) - return log_error_errno(r, "Failed to create shutdown subdirectory: %m"); - - r = fopen_temporary("/run/systemd/shutdown/scheduled", &f, &temp_path); - if (r < 0) - return log_error_errno(r, "Failed to save information about scheduled shutdowns: %m"); - - (void) fchmod(fileno(f), 0644); - - fprintf(f, - "USEC="USEC_FMT"\n" - "WARN_WALL=%i\n" - "MODE=%s\n", - m->scheduled_shutdown_timeout, - m->enable_wall_messages, - m->scheduled_shutdown_type); - - if (!isempty(m->wall_message)) { - _cleanup_free_ char *t; - - t = cescape(m->wall_message); - if (!t) { - r = -ENOMEM; - goto fail; - } - - fprintf(f, "WALL_MESSAGE=%s\n", t); - } - - r = fflush_and_check(f); - if (r < 0) - goto fail; - - if (rename(temp_path, "/run/systemd/shutdown/scheduled") < 0) { - r = -errno; - goto fail; - } - - return 0; - -fail: - (void) unlink(temp_path); - (void) unlink("/run/systemd/shutdown/scheduled"); - - return log_error_errno(r, "Failed to write information about scheduled shutdowns: %m"); -} - -static int manager_scheduled_shutdown_handler( - sd_event_source *s, - uint64_t usec, - void *userdata) { - - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - Manager *m = userdata; - const char *target; - int r; - - assert(m); - - if (isempty(m->scheduled_shutdown_type)) - return 0; - - if (streq(m->scheduled_shutdown_type, "halt")) - target = SPECIAL_HALT_TARGET; - else if (streq(m->scheduled_shutdown_type, "poweroff")) - target = SPECIAL_POWEROFF_TARGET; - else - target = SPECIAL_REBOOT_TARGET; - - r = execute_shutdown_or_sleep(m, 0, target, &error); - if (r < 0) - return log_error_errno(r, "Unable to execute transition to %s: %m", target); - - return 0; -} - -static int method_schedule_shutdown(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Manager *m = userdata; - _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; - const char *action_multiple_sessions = NULL; - const char *action_ignore_inhibit = NULL; - const char *action = NULL; - uint64_t elapse; - char *type; - int r; - - assert(m); - assert(message); - - r = sd_bus_message_read(message, "st", &type, &elapse); - if (r < 0) - return r; - - if (startswith(type, "dry-")) { - type += 4; - m->shutdown_dry_run = true; - } - - if (streq(type, "reboot")) { - action = "org.freedesktop.login1.reboot"; - action_multiple_sessions = "org.freedesktop.login1.reboot-multiple-sessions"; - action_ignore_inhibit = "org.freedesktop.login1.reboot-ignore-inhibit"; - } else if (streq(type, "halt")) { - action = "org.freedesktop.login1.halt"; - action_multiple_sessions = "org.freedesktop.login1.halt-multiple-sessions"; - action_ignore_inhibit = "org.freedesktop.login1.halt-ignore-inhibit"; - } else if (streq(type, "poweroff")) { - action = "org.freedesktop.login1.power-off"; - action_multiple_sessions = "org.freedesktop.login1.power-off-multiple-sessions"; - action_ignore_inhibit = "org.freedesktop.login1.power-off-ignore-inhibit"; - } else - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unsupported shutdown type"); - - r = verify_shutdown_creds(m, message, INHIBIT_SHUTDOWN, false, - action, action_multiple_sessions, action_ignore_inhibit, error); - if (r != 0) - return r; - - if (m->scheduled_shutdown_timeout_source) { - r = sd_event_source_set_time(m->scheduled_shutdown_timeout_source, elapse); - if (r < 0) - return log_error_errno(r, "sd_event_source_set_time() failed: %m"); - - r = sd_event_source_set_enabled(m->scheduled_shutdown_timeout_source, SD_EVENT_ONESHOT); - if (r < 0) - return log_error_errno(r, "sd_event_source_set_enabled() failed: %m"); - } else { - r = sd_event_add_time(m->event, &m->scheduled_shutdown_timeout_source, - CLOCK_REALTIME, elapse, 0, manager_scheduled_shutdown_handler, m); - if (r < 0) - return log_error_errno(r, "sd_event_add_time() failed: %m"); - } - - r = free_and_strdup(&m->scheduled_shutdown_type, type); - if (r < 0) { - m->scheduled_shutdown_timeout_source = sd_event_source_unref(m->scheduled_shutdown_timeout_source); - return log_oom(); - } - - if (m->nologin_timeout_source) { - r = sd_event_source_set_time(m->nologin_timeout_source, elapse); - if (r < 0) - return log_error_errno(r, "sd_event_source_set_time() failed: %m"); - - r = sd_event_source_set_enabled(m->nologin_timeout_source, SD_EVENT_ONESHOT); - if (r < 0) - return log_error_errno(r, "sd_event_source_set_enabled() failed: %m"); - } else { - r = sd_event_add_time(m->event, &m->nologin_timeout_source, - CLOCK_REALTIME, elapse - 5 * USEC_PER_MINUTE, 0, nologin_timeout_handler, m); - if (r < 0) - return log_error_errno(r, "sd_event_add_time() failed: %m"); - } - - m->scheduled_shutdown_timeout = elapse; - - r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_AUGMENT|SD_BUS_CREDS_TTY|SD_BUS_CREDS_UID, &creds); - if (r >= 0) { - const char *tty = NULL; - - (void) sd_bus_creds_get_uid(creds, &m->scheduled_shutdown_uid); - (void) sd_bus_creds_get_tty(creds, &tty); - - r = free_and_strdup(&m->scheduled_shutdown_tty, tty); - if (r < 0) { - m->scheduled_shutdown_timeout_source = sd_event_source_unref(m->scheduled_shutdown_timeout_source); - return log_oom(); - } - } - - r = manager_setup_wall_message_timer(m); - if (r < 0) - return r; - - if (!isempty(type)) { - r = update_schedule_file(m); - if (r < 0) - return r; - } else - (void) unlink("/run/systemd/shutdown/scheduled"); - - return sd_bus_reply_method_return(message, NULL); -} - -static int method_cancel_scheduled_shutdown(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Manager *m = userdata; - bool cancelled; - - assert(m); - assert(message); - - cancelled = m->scheduled_shutdown_type != NULL; - reset_scheduled_shutdown(m); - - if (cancelled) { - _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; - const char *tty = NULL; - uid_t uid = 0; - int r; - - r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_AUGMENT|SD_BUS_CREDS_TTY|SD_BUS_CREDS_UID, &creds); - if (r >= 0) { - (void) sd_bus_creds_get_uid(creds, &uid); - (void) sd_bus_creds_get_tty(creds, &tty); - } - - utmp_wall("The system shutdown has been cancelled", - uid_to_name(uid), tty, logind_wall_tty_filter, m); - } - - return sd_bus_reply_method_return(message, "b", cancelled); -} - -static int method_hibernate(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Manager *m = userdata; - - return method_do_shutdown_or_sleep( - m, message, - SPECIAL_HIBERNATE_TARGET, - INHIBIT_SLEEP, - "org.freedesktop.login1.hibernate", - "org.freedesktop.login1.hibernate-multiple-sessions", - "org.freedesktop.login1.hibernate-ignore-inhibit", - "hibernate", - error); -} - -static int method_hybrid_sleep(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Manager *m = userdata; - - return method_do_shutdown_or_sleep( - m, message, - SPECIAL_HYBRID_SLEEP_TARGET, - INHIBIT_SLEEP, - "org.freedesktop.login1.hibernate", - "org.freedesktop.login1.hibernate-multiple-sessions", - "org.freedesktop.login1.hibernate-ignore-inhibit", - "hybrid-sleep", - error); -} - -static int method_can_shutdown_or_sleep( - Manager *m, - sd_bus_message *message, - InhibitWhat w, - const char *action, - const char *action_multiple_sessions, - const char *action_ignore_inhibit, - const char *sleep_verb, - sd_bus_error *error) { - - _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; - bool multiple_sessions, challenge, blocked; - const char *result = NULL; - uid_t uid; - int r; - - assert(m); - assert(message); - assert(w >= 0); - assert(w <= _INHIBIT_WHAT_MAX); - assert(action); - assert(action_multiple_sessions); - assert(action_ignore_inhibit); - - if (sleep_verb) { - r = can_sleep(sleep_verb); - if (r < 0) - return r; - if (r == 0) - return sd_bus_reply_method_return(message, "s", "na"); - } - - r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_EUID, &creds); - if (r < 0) - return r; - - r = sd_bus_creds_get_euid(creds, &uid); - if (r < 0) - return r; - - r = have_multiple_sessions(m, uid); - if (r < 0) - return r; - - multiple_sessions = r > 0; - blocked = manager_is_inhibited(m, w, INHIBIT_BLOCK, NULL, false, true, uid, NULL); - - if (multiple_sessions) { - r = bus_test_polkit(message, CAP_SYS_BOOT, action_multiple_sessions, NULL, UID_INVALID, &challenge, error); - if (r < 0) - return r; - - if (r > 0) - result = "yes"; - else if (challenge) - result = "challenge"; - else - result = "no"; - } - - if (blocked) { - r = bus_test_polkit(message, CAP_SYS_BOOT, action_ignore_inhibit, NULL, UID_INVALID, &challenge, error); - if (r < 0) - return r; - - if (r > 0 && !result) - result = "yes"; - else if (challenge && (!result || streq(result, "yes"))) - result = "challenge"; - else - result = "no"; - } - - if (!multiple_sessions && !blocked) { - /* If neither inhibit nor multiple sessions - * apply then just check the normal policy */ - - r = bus_test_polkit(message, CAP_SYS_BOOT, action, NULL, UID_INVALID, &challenge, error); - if (r < 0) - return r; - - if (r > 0) - result = "yes"; - else if (challenge) - result = "challenge"; - else - result = "no"; - } - - return sd_bus_reply_method_return(message, "s", result); -} - -static int method_can_poweroff(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Manager *m = userdata; - - return method_can_shutdown_or_sleep( - m, message, - INHIBIT_SHUTDOWN, - "org.freedesktop.login1.power-off", - "org.freedesktop.login1.power-off-multiple-sessions", - "org.freedesktop.login1.power-off-ignore-inhibit", - NULL, - error); -} - -static int method_can_reboot(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Manager *m = userdata; - - return method_can_shutdown_or_sleep( - m, message, - INHIBIT_SHUTDOWN, - "org.freedesktop.login1.reboot", - "org.freedesktop.login1.reboot-multiple-sessions", - "org.freedesktop.login1.reboot-ignore-inhibit", - NULL, - error); -} - -static int method_can_suspend(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Manager *m = userdata; - - return method_can_shutdown_or_sleep( - m, message, - INHIBIT_SLEEP, - "org.freedesktop.login1.suspend", - "org.freedesktop.login1.suspend-multiple-sessions", - "org.freedesktop.login1.suspend-ignore-inhibit", - "suspend", - error); -} - -static int method_can_hibernate(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Manager *m = userdata; - - return method_can_shutdown_or_sleep( - m, message, - INHIBIT_SLEEP, - "org.freedesktop.login1.hibernate", - "org.freedesktop.login1.hibernate-multiple-sessions", - "org.freedesktop.login1.hibernate-ignore-inhibit", - "hibernate", - error); -} - -static int method_can_hybrid_sleep(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Manager *m = userdata; - - return method_can_shutdown_or_sleep( - m, message, - INHIBIT_SLEEP, - "org.freedesktop.login1.hibernate", - "org.freedesktop.login1.hibernate-multiple-sessions", - "org.freedesktop.login1.hibernate-ignore-inhibit", - "hybrid-sleep", - error); -} - -static int property_get_reboot_to_firmware_setup( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - int r; - - assert(bus); - assert(reply); - assert(userdata); - - r = efi_get_reboot_to_firmware(); - if (r < 0 && r != -EOPNOTSUPP) - return r; - - return sd_bus_message_append(reply, "b", r > 0); -} - -static int method_set_reboot_to_firmware_setup( - sd_bus_message *message, - void *userdata, - sd_bus_error *error) { - - int b, r; - Manager *m = userdata; - - assert(message); - assert(m); - - r = sd_bus_message_read(message, "b", &b); - if (r < 0) - return r; - - r = bus_verify_polkit_async(message, - CAP_SYS_ADMIN, - "org.freedesktop.login1.set-reboot-to-firmware-setup", - NULL, - false, - UID_INVALID, - &m->polkit_registry, - error); - if (r < 0) - return r; - if (r == 0) - return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ - - r = efi_set_reboot_to_firmware(b); - if (r < 0) - return r; - - return sd_bus_reply_method_return(message, NULL); -} - -static int method_can_reboot_to_firmware_setup( - sd_bus_message *message, - void *userdata, - sd_bus_error *error) { - - int r; - bool challenge; - const char *result; - Manager *m = userdata; - - assert(message); - assert(m); - - r = efi_reboot_to_firmware_supported(); - if (r == -EOPNOTSUPP) - return sd_bus_reply_method_return(message, "s", "na"); - else if (r < 0) - return r; - - r = bus_test_polkit(message, - CAP_SYS_ADMIN, - "org.freedesktop.login1.set-reboot-to-firmware-setup", - NULL, - UID_INVALID, - &challenge, - error); - if (r < 0) - return r; - - if (r > 0) - result = "yes"; - else if (challenge) - result = "challenge"; - else - result = "no"; - - return sd_bus_reply_method_return(message, "s", result); -} - -static int method_set_wall_message( - sd_bus_message *message, - void *userdata, - sd_bus_error *error) { - - int r; - Manager *m = userdata; - char *wall_message; - int enable_wall_messages; - - assert(message); - assert(m); - - r = sd_bus_message_read(message, "sb", &wall_message, &enable_wall_messages); - if (r < 0) - return r; - - r = bus_verify_polkit_async(message, - CAP_SYS_ADMIN, - "org.freedesktop.login1.set-wall-message", - NULL, - false, - UID_INVALID, - &m->polkit_registry, - error); - if (r < 0) - return r; - if (r == 0) - return 1; /* Will call us back */ - - if (isempty(wall_message)) - m->wall_message = mfree(m->wall_message); - else { - r = free_and_strdup(&m->wall_message, wall_message); - if (r < 0) - return log_oom(); - } - - m->enable_wall_messages = enable_wall_messages; - - return sd_bus_reply_method_return(message, NULL); -} - -static int method_inhibit(sd_bus_message *message, void *userdata, sd_bus_error *error) { - _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; - const char *who, *why, *what, *mode; - _cleanup_free_ char *id = NULL; - _cleanup_close_ int fifo_fd = -1; - Manager *m = userdata; - Inhibitor *i = NULL; - InhibitMode mm; - InhibitWhat w; - pid_t pid; - uid_t uid; - int r; - - assert(message); - assert(m); - - r = sd_bus_message_read(message, "ssss", &what, &who, &why, &mode); - if (r < 0) - return r; - - w = inhibit_what_from_string(what); - if (w <= 0) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid what specification %s", what); - - mm = inhibit_mode_from_string(mode); - if (mm < 0) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid mode specification %s", mode); - - /* Delay is only supported for shutdown/sleep */ - if (mm == INHIBIT_DELAY && (w & ~(INHIBIT_SHUTDOWN|INHIBIT_SLEEP))) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Delay inhibitors only supported for shutdown and sleep"); - - /* Don't allow taking delay locks while we are already - * executing the operation. We shouldn't create the impression - * that the lock was successful if the machine is about to go - * down/suspend any moment. */ - if (m->action_what & w) - return sd_bus_error_setf(error, BUS_ERROR_OPERATION_IN_PROGRESS, "The operation inhibition has been requested for is already running"); - - r = bus_verify_polkit_async( - message, - CAP_SYS_BOOT, - w == INHIBIT_SHUTDOWN ? (mm == INHIBIT_BLOCK ? "org.freedesktop.login1.inhibit-block-shutdown" : "org.freedesktop.login1.inhibit-delay-shutdown") : - w == INHIBIT_SLEEP ? (mm == INHIBIT_BLOCK ? "org.freedesktop.login1.inhibit-block-sleep" : "org.freedesktop.login1.inhibit-delay-sleep") : - w == INHIBIT_IDLE ? "org.freedesktop.login1.inhibit-block-idle" : - w == INHIBIT_HANDLE_POWER_KEY ? "org.freedesktop.login1.inhibit-handle-power-key" : - w == INHIBIT_HANDLE_SUSPEND_KEY ? "org.freedesktop.login1.inhibit-handle-suspend-key" : - w == INHIBIT_HANDLE_HIBERNATE_KEY ? "org.freedesktop.login1.inhibit-handle-hibernate-key" : - "org.freedesktop.login1.inhibit-handle-lid-switch", - NULL, - false, - UID_INVALID, - &m->polkit_registry, - error); - if (r < 0) - return r; - if (r == 0) - return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ - - r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_EUID|SD_BUS_CREDS_PID, &creds); - if (r < 0) - return r; - - r = sd_bus_creds_get_euid(creds, &uid); - if (r < 0) - return r; - - r = sd_bus_creds_get_pid(creds, &pid); - if (r < 0) - return r; - - if (hashmap_size(m->inhibitors) >= m->inhibitors_max) - return sd_bus_error_setf(error, SD_BUS_ERROR_LIMITS_EXCEEDED, "Maximum number of inhibitors (%" PRIu64 ") reached, refusing further inhibitors.", m->inhibitors_max); - - do { - id = mfree(id); - - if (asprintf(&id, "%lu", ++m->inhibit_counter) < 0) - return -ENOMEM; - - } while (hashmap_get(m->inhibitors, id)); - - r = manager_add_inhibitor(m, id, &i); - if (r < 0) - return r; - - i->what = w; - i->mode = mm; - i->pid = pid; - i->uid = uid; - i->why = strdup(why); - i->who = strdup(who); - - if (!i->why || !i->who) { - r = -ENOMEM; - goto fail; - } - - fifo_fd = inhibitor_create_fifo(i); - if (fifo_fd < 0) { - r = fifo_fd; - goto fail; - } - - inhibitor_start(i); - - return sd_bus_reply_method_return(message, "h", fifo_fd); - -fail: - if (i) - inhibitor_free(i); - - return r; -} - -const sd_bus_vtable manager_vtable[] = { - SD_BUS_VTABLE_START(0), - - SD_BUS_WRITABLE_PROPERTY("EnableWallMessages", "b", NULL, NULL, offsetof(Manager, enable_wall_messages), 0), - SD_BUS_WRITABLE_PROPERTY("WallMessage", "s", NULL, NULL, offsetof(Manager, wall_message), 0), - - SD_BUS_PROPERTY("NAutoVTs", "u", NULL, offsetof(Manager, n_autovts), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("KillOnlyUsers", "as", NULL, offsetof(Manager, kill_only_users), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("KillExcludeUsers", "as", NULL, offsetof(Manager, kill_exclude_users), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("KillUserProcesses", "b", NULL, offsetof(Manager, kill_user_processes), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("RebootToFirmwareSetup", "b", property_get_reboot_to_firmware_setup, 0, SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("IdleHint", "b", property_get_idle_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("IdleSinceHint", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("IdleSinceHintMonotonic", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("BlockInhibited", "s", property_get_inhibited, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("DelayInhibited", "s", property_get_inhibited, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("InhibitDelayMaxUSec", "t", NULL, offsetof(Manager, inhibit_delay_max), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("HandlePowerKey", "s", property_get_handle_action, offsetof(Manager, handle_power_key), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("HandleSuspendKey", "s", property_get_handle_action, offsetof(Manager, handle_suspend_key), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("HandleHibernateKey", "s", property_get_handle_action, offsetof(Manager, handle_hibernate_key), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("HandleLidSwitch", "s", property_get_handle_action, offsetof(Manager, handle_lid_switch), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("HandleLidSwitchDocked", "s", property_get_handle_action, offsetof(Manager, handle_lid_switch_docked), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("HoldoffTimeoutUSec", "t", NULL, offsetof(Manager, holdoff_timeout_usec), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("IdleAction", "s", property_get_handle_action, offsetof(Manager, idle_action), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("IdleActionUSec", "t", NULL, offsetof(Manager, idle_action_usec), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("PreparingForShutdown", "b", property_get_preparing, 0, 0), - SD_BUS_PROPERTY("PreparingForSleep", "b", property_get_preparing, 0, 0), - SD_BUS_PROPERTY("ScheduledShutdown", "(st)", property_get_scheduled_shutdown, 0, 0), - SD_BUS_PROPERTY("Docked", "b", property_get_docked, 0, 0), - SD_BUS_PROPERTY("RemoveIPC", "b", bus_property_get_bool, offsetof(Manager, remove_ipc), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("RuntimeDirectorySize", "t", bus_property_get_size, offsetof(Manager, runtime_dir_size), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("InhibitorsMax", "t", NULL, offsetof(Manager, inhibitors_max), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("NCurrentInhibitors", "t", property_get_current_inhibitors, 0, 0), - SD_BUS_PROPERTY("SessionsMax", "t", NULL, offsetof(Manager, sessions_max), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("NCurrentSessions", "t", property_get_current_sessions, 0, 0), - SD_BUS_PROPERTY("UserTasksMax", "t", NULL, offsetof(Manager, user_tasks_max), SD_BUS_VTABLE_PROPERTY_CONST), - - SD_BUS_METHOD("GetSession", "s", "o", method_get_session, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("GetSessionByPID", "u", "o", method_get_session_by_pid, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("GetUser", "u", "o", method_get_user, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("GetUserByPID", "u", "o", method_get_user_by_pid, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("GetSeat", "s", "o", method_get_seat, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("ListSessions", NULL, "a(susso)", method_list_sessions, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("ListUsers", NULL, "a(uso)", method_list_users, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("ListSeats", NULL, "a(so)", method_list_seats, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("ListInhibitors", NULL, "a(ssssuu)", method_list_inhibitors, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("CreateSession", "uusssssussbssa(sv)", "soshusub", method_create_session, 0), - SD_BUS_METHOD("ReleaseSession", "s", NULL, method_release_session, 0), - SD_BUS_METHOD("ActivateSession", "s", NULL, method_activate_session, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("ActivateSessionOnSeat", "ss", NULL, method_activate_session_on_seat, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("LockSession", "s", NULL, method_lock_session, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("UnlockSession", "s", NULL, method_lock_session, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("LockSessions", NULL, NULL, method_lock_sessions, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("UnlockSessions", NULL, NULL, method_lock_sessions, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("KillSession", "ssi", NULL, method_kill_session, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("KillUser", "ui", NULL, method_kill_user, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("TerminateSession", "s", NULL, method_terminate_session, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("TerminateUser", "u", NULL, method_terminate_user, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("TerminateSeat", "s", NULL, method_terminate_seat, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("SetUserLinger", "ubb", NULL, method_set_user_linger, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("AttachDevice", "ssb", NULL, method_attach_device, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("FlushDevices", "b", NULL, method_flush_devices, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("PowerOff", "b", NULL, method_poweroff, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("Reboot", "b", NULL, method_reboot, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("Suspend", "b", NULL, method_suspend, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("Hibernate", "b", NULL, method_hibernate, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("HybridSleep", "b", NULL, method_hybrid_sleep, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("CanPowerOff", NULL, "s", method_can_poweroff, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("CanReboot", NULL, "s", method_can_reboot, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("CanSuspend", NULL, "s", method_can_suspend, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("CanHibernate", NULL, "s", method_can_hibernate, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("CanHybridSleep", NULL, "s", method_can_hybrid_sleep, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("ScheduleShutdown", "st", NULL, method_schedule_shutdown, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("CancelScheduledShutdown", NULL, "b", method_cancel_scheduled_shutdown, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("Inhibit", "ssss", "h", method_inhibit, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("CanRebootToFirmwareSetup", NULL, "s", method_can_reboot_to_firmware_setup, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("SetRebootToFirmwareSetup", "b", NULL, method_set_reboot_to_firmware_setup, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("SetWallMessage", "sb", NULL, method_set_wall_message, SD_BUS_VTABLE_UNPRIVILEGED), - - SD_BUS_SIGNAL("SessionNew", "so", 0), - SD_BUS_SIGNAL("SessionRemoved", "so", 0), - SD_BUS_SIGNAL("UserNew", "uo", 0), - SD_BUS_SIGNAL("UserRemoved", "uo", 0), - SD_BUS_SIGNAL("SeatNew", "so", 0), - SD_BUS_SIGNAL("SeatRemoved", "so", 0), - SD_BUS_SIGNAL("PrepareForShutdown", "b", 0), - SD_BUS_SIGNAL("PrepareForSleep", "b", 0), - - SD_BUS_VTABLE_END -}; - -static int session_jobs_reply(Session *s, const char *unit, const char *result) { - int r = 0; - - assert(s); - assert(unit); - - if (!s->started) - return r; - - if (streq(result, "done")) - r = session_send_create_reply(s, NULL); - else { - _cleanup_(sd_bus_error_free) sd_bus_error e = SD_BUS_ERROR_NULL; - - sd_bus_error_setf(&e, BUS_ERROR_JOB_FAILED, "Start job for unit %s failed with '%s'", unit, result); - r = session_send_create_reply(s, &e); - } - - return r; -} - -int match_job_removed(sd_bus_message *message, void *userdata, sd_bus_error *error) { - const char *path, *result, *unit; - Manager *m = userdata; - Session *session; - uint32_t id; - User *user; - int r; - - assert(message); - assert(m); - - r = sd_bus_message_read(message, "uoss", &id, &path, &unit, &result); - if (r < 0) { - bus_log_parse_error(r); - return 0; - } - - if (m->action_job && streq(m->action_job, path)) { - log_info("Operation '%s' finished.", inhibit_what_to_string(m->action_what)); - - /* Tell people that they now may take a lock again */ - send_prepare_for(m, m->action_what, false); - - m->action_job = mfree(m->action_job); - m->action_unit = NULL; - m->action_what = 0; - return 0; - } - - session = hashmap_get(m->session_units, unit); - if (session && streq_ptr(path, session->scope_job)) { - session->scope_job = mfree(session->scope_job); - session_jobs_reply(session, unit, result); - - session_save(session); - user_save(session->user); - session_add_to_gc_queue(session); - } - - user = hashmap_get(m->user_units, unit); - if (user && - (streq_ptr(path, user->service_job) || - streq_ptr(path, user->slice_job))) { - - if (streq_ptr(path, user->service_job)) - user->service_job = mfree(user->service_job); - - if (streq_ptr(path, user->slice_job)) - user->slice_job = mfree(user->slice_job); - - LIST_FOREACH(sessions_by_user, session, user->sessions) - session_jobs_reply(session, unit, result); - - user_save(user); - user_add_to_gc_queue(user); - } - - return 0; -} - -int match_unit_removed(sd_bus_message *message, void *userdata, sd_bus_error *error) { - const char *path, *unit; - Manager *m = userdata; - Session *session; - User *user; - int r; - - assert(message); - assert(m); - - r = sd_bus_message_read(message, "so", &unit, &path); - if (r < 0) { - bus_log_parse_error(r); - return 0; - } - - session = hashmap_get(m->session_units, unit); - if (session) - session_add_to_gc_queue(session); - - user = hashmap_get(m->user_units, unit); - if (user) - user_add_to_gc_queue(user); - - return 0; -} - -int match_properties_changed(sd_bus_message *message, void *userdata, sd_bus_error *error) { - _cleanup_free_ char *unit = NULL; - Manager *m = userdata; - const char *path; - Session *session; - User *user; - int r; - - assert(message); - assert(m); - - path = sd_bus_message_get_path(message); - if (!path) - return 0; - - r = unit_name_from_dbus_path(path, &unit); - if (r == -EINVAL) /* not a unit */ - return 0; - if (r < 0) { - log_oom(); - return 0; - } - - session = hashmap_get(m->session_units, unit); - if (session) - session_add_to_gc_queue(session); - - user = hashmap_get(m->user_units, unit); - if (user) - user_add_to_gc_queue(user); - - return 0; -} - -int match_reloading(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Manager *m = userdata; - Session *session; - Iterator i; - int b, r; - - assert(message); - assert(m); - - r = sd_bus_message_read(message, "b", &b); - if (r < 0) { - bus_log_parse_error(r); - return 0; - } - - if (b) - return 0; - - /* systemd finished reloading, let's recheck all our sessions */ - log_debug("System manager has been reloaded, rechecking sessions..."); - - HASHMAP_FOREACH(session, m->sessions, i) - session_add_to_gc_queue(session); - - return 0; -} - -int manager_send_changed(Manager *manager, const char *property, ...) { - char **l; - - assert(manager); - - l = strv_from_stdarg_alloca(property); - - return sd_bus_emit_properties_changed_strv( - manager->bus, - "/org/freedesktop/login1", - "org.freedesktop.login1.Manager", - l); -} - -static int strdup_job(sd_bus_message *reply, char **job) { - const char *j; - char *copy; - int r; - - r = sd_bus_message_read(reply, "o", &j); - if (r < 0) - return r; - - copy = strdup(j); - if (!copy) - return -ENOMEM; - - *job = copy; - return 1; -} - -int manager_start_slice( - Manager *manager, - const char *slice, - const char *description, - const char *after, - const char *after2, - uint64_t tasks_max, - sd_bus_error *error, - char **job) { - - _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL; - int r; - - assert(manager); - assert(slice); - assert(job); - - r = sd_bus_message_new_method_call( - manager->bus, - &m, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "StartTransientUnit"); - if (r < 0) - return r; - - r = sd_bus_message_append(m, "ss", strempty(slice), "fail"); - if (r < 0) - return r; - - r = sd_bus_message_open_container(m, 'a', "(sv)"); - if (r < 0) - return r; - - if (!isempty(description)) { - r = sd_bus_message_append(m, "(sv)", "Description", "s", description); - if (r < 0) - return r; - } - - if (!isempty(after)) { - r = sd_bus_message_append(m, "(sv)", "After", "as", 1, after); - if (r < 0) - return r; - } - - if (!isempty(after2)) { - r = sd_bus_message_append(m, "(sv)", "After", "as", 1, after2); - if (r < 0) - return r; - } - - r = sd_bus_message_append(m, "(sv)", "TasksMax", "t", tasks_max); - if (r < 0) - return r; - - r = sd_bus_message_close_container(m); - if (r < 0) - return r; - - r = sd_bus_message_append(m, "a(sa(sv))", 0); - if (r < 0) - return r; - - r = sd_bus_call(manager->bus, m, 0, error, &reply); - if (r < 0) - return r; - - return strdup_job(reply, job); -} - -int manager_start_scope( - Manager *manager, - const char *scope, - pid_t pid, - const char *slice, - const char *description, - const char *after, - const char *after2, - uint64_t tasks_max, - sd_bus_error *error, - char **job) { - - _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL; - int r; - - assert(manager); - assert(scope); - assert(pid > 1); - assert(job); - - r = sd_bus_message_new_method_call( - manager->bus, - &m, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "StartTransientUnit"); - if (r < 0) - return r; - - r = sd_bus_message_append(m, "ss", strempty(scope), "fail"); - if (r < 0) - return r; - - r = sd_bus_message_open_container(m, 'a', "(sv)"); - if (r < 0) - return r; - - if (!isempty(slice)) { - r = sd_bus_message_append(m, "(sv)", "Slice", "s", slice); - if (r < 0) - return r; - } - - if (!isempty(description)) { - r = sd_bus_message_append(m, "(sv)", "Description", "s", description); - if (r < 0) - return r; - } - - if (!isempty(after)) { - r = sd_bus_message_append(m, "(sv)", "After", "as", 1, after); - if (r < 0) - return r; - } - - if (!isempty(after2)) { - r = sd_bus_message_append(m, "(sv)", "After", "as", 1, after2); - if (r < 0) - return r; - } - - /* cgroup empty notification is not available in containers - * currently. To make this less problematic, let's shorten the - * stop timeout for sessions, so that we don't wait - * forever. */ - - /* Make sure that the session shells are terminated with - * SIGHUP since bash and friends tend to ignore SIGTERM */ - r = sd_bus_message_append(m, "(sv)", "SendSIGHUP", "b", true); - if (r < 0) - return r; - - r = sd_bus_message_append(m, "(sv)", "PIDs", "au", 1, pid); - if (r < 0) - return r; - - r = sd_bus_message_append(m, "(sv)", "TasksMax", "t", tasks_max); - if (r < 0) - return r; - - r = sd_bus_message_close_container(m); - if (r < 0) - return r; - - r = sd_bus_message_append(m, "a(sa(sv))", 0); - if (r < 0) - return r; - - r = sd_bus_call(manager->bus, m, 0, error, &reply); - if (r < 0) - return r; - - return strdup_job(reply, job); -} - -int manager_start_unit(Manager *manager, const char *unit, sd_bus_error *error, char **job) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - int r; - - assert(manager); - assert(unit); - assert(job); - - r = sd_bus_call_method( - manager->bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "StartUnit", - error, - &reply, - "ss", unit, "replace"); - if (r < 0) - return r; - - return strdup_job(reply, job); -} - -int manager_stop_unit(Manager *manager, const char *unit, sd_bus_error *error, char **job) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - int r; - - assert(manager); - assert(unit); - assert(job); - - r = sd_bus_call_method( - manager->bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "StopUnit", - error, - &reply, - "ss", unit, "fail"); - if (r < 0) { - if (sd_bus_error_has_name(error, BUS_ERROR_NO_SUCH_UNIT) || - sd_bus_error_has_name(error, BUS_ERROR_LOAD_FAILED)) { - - *job = NULL; - sd_bus_error_free(error); - return 0; - } - - return r; - } - - return strdup_job(reply, job); -} - -int manager_abandon_scope(Manager *manager, const char *scope, sd_bus_error *error) { - _cleanup_free_ char *path = NULL; - int r; - - assert(manager); - assert(scope); - - path = unit_dbus_path_from_name(scope); - if (!path) - return -ENOMEM; - - r = sd_bus_call_method( - manager->bus, - "org.freedesktop.systemd1", - path, - "org.freedesktop.systemd1.Scope", - "Abandon", - error, - NULL, - NULL); - if (r < 0) { - if (sd_bus_error_has_name(error, BUS_ERROR_NO_SUCH_UNIT) || - sd_bus_error_has_name(error, BUS_ERROR_LOAD_FAILED) || - sd_bus_error_has_name(error, BUS_ERROR_SCOPE_NOT_RUNNING)) { - sd_bus_error_free(error); - return 0; - } - - return r; - } - - return 1; -} - -int manager_kill_unit(Manager *manager, const char *unit, KillWho who, int signo, sd_bus_error *error) { - assert(manager); - assert(unit); - - return sd_bus_call_method( - manager->bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "KillUnit", - error, - NULL, - "ssi", unit, who == KILL_LEADER ? "main" : "all", signo); -} - -int manager_unit_is_active(Manager *manager, const char *unit) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - _cleanup_free_ char *path = NULL; - const char *state; - int r; - - assert(manager); - assert(unit); - - path = unit_dbus_path_from_name(unit); - if (!path) - return -ENOMEM; - - r = sd_bus_get_property( - manager->bus, - "org.freedesktop.systemd1", - path, - "org.freedesktop.systemd1.Unit", - "ActiveState", - &error, - &reply, - "s"); - if (r < 0) { - /* systemd might have droppped off momentarily, let's - * not make this an error */ - if (sd_bus_error_has_name(&error, SD_BUS_ERROR_NO_REPLY) || - sd_bus_error_has_name(&error, SD_BUS_ERROR_DISCONNECTED)) - return true; - - /* If the unit is already unloaded then it's not - * active */ - if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_UNIT) || - sd_bus_error_has_name(&error, BUS_ERROR_LOAD_FAILED)) - return false; - - return r; - } - - r = sd_bus_message_read(reply, "s", &state); - if (r < 0) - return -EINVAL; - - return !streq(state, "inactive") && !streq(state, "failed"); -} - -int manager_job_is_active(Manager *manager, const char *path) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - int r; - - assert(manager); - assert(path); - - r = sd_bus_get_property( - manager->bus, - "org.freedesktop.systemd1", - path, - "org.freedesktop.systemd1.Job", - "State", - &error, - &reply, - "s"); - if (r < 0) { - if (sd_bus_error_has_name(&error, SD_BUS_ERROR_NO_REPLY) || - sd_bus_error_has_name(&error, SD_BUS_ERROR_DISCONNECTED)) - return true; - - if (sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_OBJECT)) - return false; - - return r; - } - - /* We don't actually care about the state really. The fact - * that we could read the job state is enough for us */ - - return true; -} diff --git a/src/grp-login/liblogind-core/logind-device.c b/src/grp-login/liblogind-core/logind-device.c deleted file mode 100644 index 6165a9ea6e..0000000000 --- a/src/grp-login/liblogind-core/logind-device.c +++ /dev/null @@ -1,125 +0,0 @@ -/*** - 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 Lesser General Public License as published by - the Free Software Foundation; either version 2.1 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include - -#include "basic/alloc-util.h" -#include "basic/util.h" - -#include "logind-device.h" - -Device* device_new(Manager *m, const char *sysfs, bool master) { - Device *d; - - assert(m); - assert(sysfs); - - d = new0(Device, 1); - if (!d) - return NULL; - - d->sysfs = strdup(sysfs); - if (!d->sysfs) { - free(d); - return NULL; - } - - if (hashmap_put(m->devices, d->sysfs, d) < 0) { - free(d->sysfs); - free(d); - return NULL; - } - - d->manager = m; - d->master = master; - dual_timestamp_get(&d->timestamp); - - return d; -} - -static void device_detach(Device *d) { - Seat *s; - SessionDevice *sd; - - assert(d); - - if (!d->seat) - return; - - while ((sd = d->session_devices)) - session_device_free(sd); - - s = d->seat; - LIST_REMOVE(devices, d->seat->devices, d); - d->seat = NULL; - - if (!seat_has_master_device(s)) { - seat_add_to_gc_queue(s); - seat_send_changed(s, "CanGraphical", NULL); - } -} - -void device_free(Device *d) { - assert(d); - - device_detach(d); - - hashmap_remove(d->manager->devices, d->sysfs); - - free(d->sysfs); - free(d); -} - -void device_attach(Device *d, Seat *s) { - Device *i; - bool had_master; - - assert(d); - assert(s); - - if (d->seat == s) - return; - - if (d->seat) - device_detach(d); - - d->seat = s; - had_master = seat_has_master_device(s); - - /* We keep the device list sorted by the "master" flag. That is, master - * devices are at the front, other devices at the tail. As there is no - * way to easily add devices at the list-tail, we need to iterate the - * list to find the first non-master device when adding non-master - * devices. We assume there is only a few (normally 1) master devices - * per seat, so we iterate only a few times. */ - - if (d->master || !s->devices) - LIST_PREPEND(devices, s->devices, d); - else { - LIST_FOREACH(devices, i, s->devices) { - if (!i->devices_next || !i->master) { - LIST_INSERT_AFTER(devices, s->devices, i, d); - break; - } - } - } - - if (!had_master && d->master) - seat_send_changed(s, "CanGraphical", NULL); -} diff --git a/src/grp-login/liblogind-core/logind-device.h b/src/grp-login/liblogind-core/logind-device.h deleted file mode 100644 index 9e2678d801..0000000000 --- a/src/grp-login/liblogind-core/logind-device.h +++ /dev/null @@ -1,44 +0,0 @@ -#pragma once - -/*** - 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 Lesser General Public License as published by - the Free Software Foundation; either version 2.1 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "basic/list.h" - -typedef struct Device Device; - -#include "logind-seat.h" -#include "logind-session-device.h" - -struct Device { - Manager *manager; - - char *sysfs; - Seat *seat; - bool master; - - dual_timestamp timestamp; - - LIST_FIELDS(struct Device, devices); - LIST_HEAD(SessionDevice, session_devices); -}; - -Device* device_new(Manager *m, const char *sysfs, bool master); -void device_free(Device *d); -void device_attach(Device *d, Seat *s); diff --git a/src/grp-login/liblogind-core/logind-gperf.gperf b/src/grp-login/liblogind-core/logind-gperf.gperf deleted file mode 100644 index 6d5c484439..0000000000 --- a/src/grp-login/liblogind-core/logind-gperf.gperf +++ /dev/null @@ -1,39 +0,0 @@ -%{ -#include -#include "shared/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.ReserveVT, config_parse_unsigned, 0, offsetof(Manager, reserve_vt) -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.InhibitDelayMaxSec, config_parse_sec, 0, offsetof(Manager, inhibit_delay_max) -Login.HandlePowerKey, config_parse_handle_action, 0, offsetof(Manager, handle_power_key) -Login.HandleSuspendKey, config_parse_handle_action, 0, offsetof(Manager, handle_suspend_key) -Login.HandleHibernateKey, config_parse_handle_action, 0, offsetof(Manager, handle_hibernate_key) -Login.HandleLidSwitch, config_parse_handle_action, 0, offsetof(Manager, handle_lid_switch) -Login.HandleLidSwitchDocked, config_parse_handle_action, 0, offsetof(Manager, handle_lid_switch_docked) -Login.PowerKeyIgnoreInhibited, config_parse_bool, 0, offsetof(Manager, power_key_ignore_inhibited) -Login.SuspendKeyIgnoreInhibited, config_parse_bool, 0, offsetof(Manager, suspend_key_ignore_inhibited) -Login.HibernateKeyIgnoreInhibited, config_parse_bool, 0, offsetof(Manager, hibernate_key_ignore_inhibited) -Login.LidSwitchIgnoreInhibited, config_parse_bool, 0, offsetof(Manager, lid_switch_ignore_inhibited) -Login.HoldoffTimeoutSec, config_parse_sec, 0, offsetof(Manager, holdoff_timeout_usec) -Login.IdleAction, config_parse_handle_action, 0, offsetof(Manager, idle_action) -Login.IdleActionSec, config_parse_sec, 0, offsetof(Manager, idle_action_usec) -Login.RuntimeDirectorySize, config_parse_tmpfs_size, 0, offsetof(Manager, runtime_dir_size) -Login.RemoveIPC, config_parse_bool, 0, offsetof(Manager, remove_ipc) -Login.InhibitorsMax, config_parse_uint64, 0, offsetof(Manager, inhibitors_max) -Login.SessionsMax, config_parse_uint64, 0, offsetof(Manager, sessions_max) -Login.UserTasksMax, config_parse_uint64, 0, offsetof(Manager, user_tasks_max) diff --git a/src/grp-login/liblogind-core/logind-inhibit.c b/src/grp-login/liblogind-core/logind-inhibit.c deleted file mode 100644 index 8a35db0dc8..0000000000 --- a/src/grp-login/liblogind-core/logind-inhibit.c +++ /dev/null @@ -1,486 +0,0 @@ -/*** - 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 Lesser General Public License as published by - the Free Software Foundation; either version 2.1 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include - -#include "basic/alloc-util.h" -#include "basic/escape.h" -#include "basic/fd-util.h" -#include "basic/fileio.h" -#include "basic/formats-util.h" -#include "basic/mkdir.h" -#include "basic/parse-util.h" -#include "basic/string-table.h" -#include "basic/string-util.h" -#include "basic/user-util.h" -#include "basic/util.h" - -#include "logind-inhibit.h" - -Inhibitor* inhibitor_new(Manager *m, const char* id) { - Inhibitor *i; - - assert(m); - - i = new0(Inhibitor, 1); - if (!i) - return NULL; - - i->state_file = strappend("/run/systemd/inhibit/", id); - if (!i->state_file) { - free(i); - return NULL; - } - - i->id = basename(i->state_file); - - if (hashmap_put(m->inhibitors, i->id, i) < 0) { - free(i->state_file); - free(i); - return NULL; - } - - i->manager = m; - i->fifo_fd = -1; - - return i; -} - -void inhibitor_free(Inhibitor *i) { - assert(i); - - hashmap_remove(i->manager->inhibitors, i->id); - - inhibitor_remove_fifo(i); - - free(i->who); - free(i->why); - - if (i->state_file) { - unlink(i->state_file); - free(i->state_file); - } - - free(i); -} - -int inhibitor_save(Inhibitor *i) { - _cleanup_free_ char *temp_path = NULL; - _cleanup_fclose_ FILE *f = NULL; - int r; - - assert(i); - - r = mkdir_safe_label("/run/systemd/inhibit", 0755, 0, 0); - if (r < 0) - goto fail; - - r = fopen_temporary(i->state_file, &f, &temp_path); - if (r < 0) - goto fail; - - fchmod(fileno(f), 0644); - - fprintf(f, - "# This is private data. Do not parse.\n" - "WHAT=%s\n" - "MODE=%s\n" - "UID="UID_FMT"\n" - "PID="PID_FMT"\n", - inhibit_what_to_string(i->what), - inhibit_mode_to_string(i->mode), - i->uid, - i->pid); - - if (i->who) { - _cleanup_free_ char *cc = NULL; - - cc = cescape(i->who); - if (!cc) { - r = -ENOMEM; - goto fail; - } - - fprintf(f, "WHO=%s\n", cc); - } - - if (i->why) { - _cleanup_free_ char *cc = NULL; - - cc = cescape(i->why); - if (!cc) { - r = -ENOMEM; - goto fail; - } - - fprintf(f, "WHY=%s\n", cc); - } - - if (i->fifo_path) - fprintf(f, "FIFO=%s\n", i->fifo_path); - - r = fflush_and_check(f); - if (r < 0) - goto fail; - - if (rename(temp_path, i->state_file) < 0) { - r = -errno; - goto fail; - } - - return 0; - -fail: - (void) unlink(i->state_file); - - if (temp_path) - (void) unlink(temp_path); - - return log_error_errno(r, "Failed to save inhibit data %s: %m", i->state_file); -} - -int inhibitor_start(Inhibitor *i) { - assert(i); - - if (i->started) - return 0; - - dual_timestamp_get(&i->since); - - log_debug("Inhibitor %s (%s) pid="PID_FMT" uid="UID_FMT" mode=%s started.", - strna(i->who), strna(i->why), - i->pid, i->uid, - inhibit_mode_to_string(i->mode)); - - inhibitor_save(i); - - i->started = true; - - manager_send_changed(i->manager, i->mode == INHIBIT_BLOCK ? "BlockInhibited" : "DelayInhibited", NULL); - - return 0; -} - -int inhibitor_stop(Inhibitor *i) { - assert(i); - - if (i->started) - log_debug("Inhibitor %s (%s) pid="PID_FMT" uid="UID_FMT" mode=%s stopped.", - strna(i->who), strna(i->why), - i->pid, i->uid, - inhibit_mode_to_string(i->mode)); - - if (i->state_file) - unlink(i->state_file); - - i->started = false; - - manager_send_changed(i->manager, i->mode == INHIBIT_BLOCK ? "BlockInhibited" : "DelayInhibited", NULL); - - return 0; -} - -int inhibitor_load(Inhibitor *i) { - - _cleanup_free_ char - *what = NULL, - *uid = NULL, - *pid = NULL, - *who = NULL, - *why = NULL, - *mode = NULL; - - InhibitWhat w; - InhibitMode mm; - char *cc; - int r; - - r = parse_env_file(i->state_file, NEWLINE, - "WHAT", &what, - "UID", &uid, - "PID", &pid, - "WHO", &who, - "WHY", &why, - "MODE", &mode, - "FIFO", &i->fifo_path, - NULL); - if (r < 0) - return r; - - w = what ? inhibit_what_from_string(what) : 0; - if (w >= 0) - i->what = w; - - mm = mode ? inhibit_mode_from_string(mode) : INHIBIT_BLOCK; - if (mm >= 0) - i->mode = mm; - - if (uid) { - r = parse_uid(uid, &i->uid); - if (r < 0) - return r; - } - - if (pid) { - r = parse_pid(pid, &i->pid); - if (r < 0) - return r; - } - - if (who) { - r = cunescape(who, 0, &cc); - if (r < 0) - return r; - - free(i->who); - i->who = cc; - } - - if (why) { - r = cunescape(why, 0, &cc); - if (r < 0) - return r; - - free(i->why); - i->why = cc; - } - - if (i->fifo_path) { - int fd; - - fd = inhibitor_create_fifo(i); - safe_close(fd); - } - - return 0; -} - -static int inhibitor_dispatch_fifo(sd_event_source *s, int fd, uint32_t revents, void *userdata) { - Inhibitor *i = userdata; - - assert(s); - assert(fd == i->fifo_fd); - assert(i); - - inhibitor_stop(i); - inhibitor_free(i); - - return 0; -} - -int inhibitor_create_fifo(Inhibitor *i) { - int r; - - assert(i); - - /* Create FIFO */ - if (!i->fifo_path) { - r = mkdir_safe_label("/run/systemd/inhibit", 0755, 0, 0); - if (r < 0) - return r; - - i->fifo_path = strjoin("/run/systemd/inhibit/", i->id, ".ref", NULL); - if (!i->fifo_path) - return -ENOMEM; - - if (mkfifo(i->fifo_path, 0600) < 0 && errno != EEXIST) - return -errno; - } - - /* Open reading side */ - if (i->fifo_fd < 0) { - i->fifo_fd = open(i->fifo_path, O_RDONLY|O_CLOEXEC|O_NDELAY); - if (i->fifo_fd < 0) - return -errno; - } - - if (!i->event_source) { - r = sd_event_add_io(i->manager->event, &i->event_source, i->fifo_fd, 0, inhibitor_dispatch_fifo, i); - if (r < 0) - return r; - - r = sd_event_source_set_priority(i->event_source, SD_EVENT_PRIORITY_IDLE-10); - if (r < 0) - return r; - } - - /* Open writing side */ - r = open(i->fifo_path, O_WRONLY|O_CLOEXEC|O_NDELAY); - if (r < 0) - return -errno; - - return r; -} - -void inhibitor_remove_fifo(Inhibitor *i) { - assert(i); - - i->event_source = sd_event_source_unref(i->event_source); - i->fifo_fd = safe_close(i->fifo_fd); - - if (i->fifo_path) { - unlink(i->fifo_path); - i->fifo_path = mfree(i->fifo_path); - } -} - -InhibitWhat manager_inhibit_what(Manager *m, InhibitMode mm) { - Inhibitor *i; - Iterator j; - InhibitWhat what = 0; - - assert(m); - - HASHMAP_FOREACH(i, m->inhibitors, j) - if (i->mode == mm) - what |= i->what; - - return what; -} - -static int pid_is_active(Manager *m, pid_t pid) { - Session *s; - int r; - - r = manager_get_session_by_pid(m, pid, &s); - if (r < 0) - return r; - - /* If there's no session assigned to it, then it's globally - * active on all ttys */ - if (r == 0) - return 1; - - return session_is_active(s); -} - -bool manager_is_inhibited( - Manager *m, - InhibitWhat w, - InhibitMode mm, - dual_timestamp *since, - bool ignore_inactive, - bool ignore_uid, - uid_t uid, - Inhibitor **offending) { - - Inhibitor *i; - Iterator j; - struct dual_timestamp ts = DUAL_TIMESTAMP_NULL; - bool inhibited = false; - - assert(m); - assert(w > 0 && w < _INHIBIT_WHAT_MAX); - - HASHMAP_FOREACH(i, m->inhibitors, j) { - if (!(i->what & w)) - continue; - - if (i->mode != mm) - continue; - - if (ignore_inactive && pid_is_active(m, i->pid) <= 0) - continue; - - if (ignore_uid && i->uid == uid) - continue; - - if (!inhibited || - i->since.monotonic < ts.monotonic) - ts = i->since; - - inhibited = true; - - if (offending) - *offending = i; - } - - if (since) - *since = ts; - - return inhibited; -} - -const char *inhibit_what_to_string(InhibitWhat w) { - static thread_local char buffer[97]; - char *p; - - if (w < 0 || w >= _INHIBIT_WHAT_MAX) - return NULL; - - p = buffer; - if (w & INHIBIT_SHUTDOWN) - p = stpcpy(p, "shutdown:"); - if (w & INHIBIT_SLEEP) - p = stpcpy(p, "sleep:"); - if (w & INHIBIT_IDLE) - p = stpcpy(p, "idle:"); - if (w & INHIBIT_HANDLE_POWER_KEY) - p = stpcpy(p, "handle-power-key:"); - if (w & INHIBIT_HANDLE_SUSPEND_KEY) - p = stpcpy(p, "handle-suspend-key:"); - if (w & INHIBIT_HANDLE_HIBERNATE_KEY) - p = stpcpy(p, "handle-hibernate-key:"); - if (w & INHIBIT_HANDLE_LID_SWITCH) - p = stpcpy(p, "handle-lid-switch:"); - - if (p > buffer) - *(p-1) = 0; - else - *p = 0; - - return buffer; -} - -InhibitWhat inhibit_what_from_string(const char *s) { - InhibitWhat what = 0; - const char *word, *state; - size_t l; - - FOREACH_WORD_SEPARATOR(word, l, s, ":", state) { - if (l == 8 && strneq(word, "shutdown", l)) - what |= INHIBIT_SHUTDOWN; - else if (l == 5 && strneq(word, "sleep", l)) - what |= INHIBIT_SLEEP; - else if (l == 4 && strneq(word, "idle", l)) - what |= INHIBIT_IDLE; - else if (l == 16 && strneq(word, "handle-power-key", l)) - what |= INHIBIT_HANDLE_POWER_KEY; - else if (l == 18 && strneq(word, "handle-suspend-key", l)) - what |= INHIBIT_HANDLE_SUSPEND_KEY; - else if (l == 20 && strneq(word, "handle-hibernate-key", l)) - what |= INHIBIT_HANDLE_HIBERNATE_KEY; - else if (l == 17 && strneq(word, "handle-lid-switch", l)) - what |= INHIBIT_HANDLE_LID_SWITCH; - else - return _INHIBIT_WHAT_INVALID; - } - - return what; -} - -static const char* const inhibit_mode_table[_INHIBIT_MODE_MAX] = { - [INHIBIT_BLOCK] = "block", - [INHIBIT_DELAY] = "delay" -}; - -DEFINE_STRING_TABLE_LOOKUP(inhibit_mode, InhibitMode); diff --git a/src/grp-login/liblogind-core/logind-inhibit.h b/src/grp-login/liblogind-core/logind-inhibit.h deleted file mode 100644 index 70de199c60..0000000000 --- a/src/grp-login/liblogind-core/logind-inhibit.h +++ /dev/null @@ -1,89 +0,0 @@ -#pragma once - -/*** - 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 Lesser General Public License as published by - the Free Software Foundation; either version 2.1 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -typedef struct Inhibitor Inhibitor; - - -typedef enum InhibitWhat { - INHIBIT_SHUTDOWN = 1, - INHIBIT_SLEEP = 2, - INHIBIT_IDLE = 4, - INHIBIT_HANDLE_POWER_KEY = 8, - INHIBIT_HANDLE_SUSPEND_KEY = 16, - INHIBIT_HANDLE_HIBERNATE_KEY = 32, - INHIBIT_HANDLE_LID_SWITCH = 64, - _INHIBIT_WHAT_MAX = 128, - _INHIBIT_WHAT_INVALID = -1 -} InhibitWhat; - -typedef enum InhibitMode { - INHIBIT_BLOCK, - INHIBIT_DELAY, - _INHIBIT_MODE_MAX, - _INHIBIT_MODE_INVALID = -1 -} InhibitMode; - -#include "logind.h" - -struct Inhibitor { - Manager *manager; - - sd_event_source *event_source; - - char *id; - char *state_file; - - bool started; - - InhibitWhat what; - char *who; - char *why; - InhibitMode mode; - - pid_t pid; - uid_t uid; - - dual_timestamp since; - - char *fifo_path; - int fifo_fd; -}; - -Inhibitor* inhibitor_new(Manager *m, const char *id); -void inhibitor_free(Inhibitor *i); - -int inhibitor_save(Inhibitor *i); -int inhibitor_load(Inhibitor *i); - -int inhibitor_start(Inhibitor *i); -int inhibitor_stop(Inhibitor *i); - -int inhibitor_create_fifo(Inhibitor *i); -void inhibitor_remove_fifo(Inhibitor *i); - -InhibitWhat manager_inhibit_what(Manager *m, InhibitMode mm); -bool manager_is_inhibited(Manager *m, InhibitWhat w, InhibitMode mm, dual_timestamp *since, bool ignore_inactive, bool ignore_uid, uid_t uid, Inhibitor **offending); - -const char *inhibit_what_to_string(InhibitWhat k); -InhibitWhat inhibit_what_from_string(const char *s); - -const char *inhibit_mode_to_string(InhibitMode k); -InhibitMode inhibit_mode_from_string(const char *s); diff --git a/src/grp-login/liblogind-core/logind-seat-dbus.c b/src/grp-login/liblogind-core/logind-seat-dbus.c deleted file mode 100644 index fbebe6fcff..0000000000 --- a/src/grp-login/liblogind-core/logind-seat-dbus.c +++ /dev/null @@ -1,475 +0,0 @@ -/*** - 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 Lesser General Public License as published by - the Free Software Foundation; either version 2.1 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include - -#include "basic/alloc-util.h" -#include "basic/bus-label.h" -#include "basic/strv.h" -#include "basic/user-util.h" -#include "basic/util.h" -#include "logind.h" -#include "sd-bus/bus-common-errors.h" -#include "shared/bus-util.h" - -#include "logind-seat.h" - -static int property_get_active_session( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - _cleanup_free_ char *p = NULL; - Seat *s = userdata; - - assert(bus); - assert(reply); - assert(s); - - p = s->active ? session_bus_path(s->active) : strdup("/"); - if (!p) - return -ENOMEM; - - return sd_bus_message_append(reply, "(so)", s->active ? s->active->id : "", p); -} - -static int property_get_can_multi_session( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - Seat *s = userdata; - - assert(bus); - assert(reply); - assert(s); - - return sd_bus_message_append(reply, "b", seat_can_multi_session(s)); -} - -static int property_get_can_tty( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - Seat *s = userdata; - - assert(bus); - assert(reply); - assert(s); - - return sd_bus_message_append(reply, "b", seat_can_tty(s)); -} - -static int property_get_can_graphical( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - Seat *s = userdata; - - assert(bus); - assert(reply); - assert(s); - - return sd_bus_message_append(reply, "b", seat_can_graphical(s)); -} - -static int property_get_sessions( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - Seat *s = userdata; - Session *session; - int r; - - assert(bus); - assert(reply); - assert(s); - - r = sd_bus_message_open_container(reply, 'a', "(so)"); - if (r < 0) - return r; - - LIST_FOREACH(sessions_by_seat, session, s->sessions) { - _cleanup_free_ char *p = NULL; - - p = session_bus_path(session); - if (!p) - return -ENOMEM; - - r = sd_bus_message_append(reply, "(so)", session->id, p); - if (r < 0) - return r; - - } - - r = sd_bus_message_close_container(reply); - if (r < 0) - return r; - - return 1; -} - -static int property_get_idle_hint( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - Seat *s = userdata; - - assert(bus); - assert(reply); - assert(s); - - return sd_bus_message_append(reply, "b", seat_get_idle_hint(s, NULL) > 0); -} - -static int property_get_idle_since_hint( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - Seat *s = userdata; - dual_timestamp t; - uint64_t u; - int r; - - assert(bus); - assert(reply); - assert(s); - - r = seat_get_idle_hint(s, &t); - if (r < 0) - return r; - - u = streq(property, "IdleSinceHint") ? t.realtime : t.monotonic; - - return sd_bus_message_append(reply, "t", u); -} - -int bus_seat_method_terminate(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Seat *s = userdata; - int r; - - assert(message); - assert(s); - - r = bus_verify_polkit_async( - message, - CAP_KILL, - "org.freedesktop.login1.manage", - NULL, - false, - UID_INVALID, - &s->manager->polkit_registry, - error); - if (r < 0) - return r; - if (r == 0) - return 1; /* Will call us back */ - - r = seat_stop_sessions(s, true); - if (r < 0) - return r; - - return sd_bus_reply_method_return(message, NULL); -} - -static int method_activate_session(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Seat *s = userdata; - const char *name; - Session *session; - int r; - - assert(message); - assert(s); - - r = sd_bus_message_read(message, "s", &name); - if (r < 0) - return r; - - session = hashmap_get(s->manager->sessions, name); - if (!session) - return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_SESSION, "No session '%s' known", name); - - if (session->seat != s) - return sd_bus_error_setf(error, BUS_ERROR_SESSION_NOT_ON_SEAT, "Session %s not on seat %s", name, s->id); - - r = session_activate(session); - if (r < 0) - return r; - - return sd_bus_reply_method_return(message, NULL); -} - -static int method_switch_to(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Seat *s = userdata; - unsigned int to; - int r; - - assert(message); - assert(s); - - r = sd_bus_message_read(message, "u", &to); - if (r < 0) - return r; - - if (to <= 0) - return -EINVAL; - - r = seat_switch_to(s, to); - if (r < 0) - return r; - - return sd_bus_reply_method_return(message, NULL); -} - -static int method_switch_to_next(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Seat *s = userdata; - int r; - - assert(message); - assert(s); - - r = seat_switch_to_next(s); - if (r < 0) - return r; - - return sd_bus_reply_method_return(message, NULL); -} - -static int method_switch_to_previous(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Seat *s = userdata; - int r; - - assert(message); - assert(s); - - r = seat_switch_to_previous(s); - if (r < 0) - return r; - - return sd_bus_reply_method_return(message, NULL); -} - -const sd_bus_vtable seat_vtable[] = { - SD_BUS_VTABLE_START(0), - - SD_BUS_PROPERTY("Id", "s", NULL, offsetof(Seat, id), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("ActiveSession", "(so)", property_get_active_session, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("CanMultiSession", "b", property_get_can_multi_session, 0, SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("CanTTY", "b", property_get_can_tty, 0, SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("CanGraphical", "b", property_get_can_graphical, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("Sessions", "a(so)", property_get_sessions, 0, 0), - SD_BUS_PROPERTY("IdleHint", "b", property_get_idle_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("IdleSinceHint", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("IdleSinceHintMonotonic", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - - SD_BUS_METHOD("Terminate", NULL, NULL, bus_seat_method_terminate, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("ActivateSession", "s", NULL, method_activate_session, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("SwitchTo", "u", NULL, method_switch_to, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("SwitchToNext", NULL, NULL, method_switch_to_next, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("SwitchToPrevious", NULL, NULL, method_switch_to_previous, SD_BUS_VTABLE_UNPRIVILEGED), - - SD_BUS_VTABLE_END -}; - -int seat_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) { - Manager *m = userdata; - Seat *seat; - int r; - - assert(bus); - assert(path); - assert(interface); - assert(found); - assert(m); - - if (streq(path, "/org/freedesktop/login1/seat/self")) { - _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; - sd_bus_message *message; - Session *session; - const char *name; - - message = sd_bus_get_current_message(bus); - if (!message) - return 0; - - r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_SESSION|SD_BUS_CREDS_AUGMENT, &creds); - if (r < 0) - return r; - - r = sd_bus_creds_get_session(creds, &name); - if (r < 0) - return r; - - session = hashmap_get(m->sessions, name); - if (!session) - return 0; - - seat = session->seat; - } else { - _cleanup_free_ char *e = NULL; - const char *p; - - p = startswith(path, "/org/freedesktop/login1/seat/"); - if (!p) - return 0; - - e = bus_label_unescape(p); - if (!e) - return -ENOMEM; - - seat = hashmap_get(m->seats, e); - } - - if (!seat) - return 0; - - *found = seat; - return 1; -} - -char *seat_bus_path(Seat *s) { - _cleanup_free_ char *t = NULL; - - assert(s); - - t = bus_label_escape(s->id); - if (!t) - return NULL; - - return strappend("/org/freedesktop/login1/seat/", t); -} - -int seat_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) { - _cleanup_strv_free_ char **l = NULL; - sd_bus_message *message; - Manager *m = userdata; - Seat *seat; - Iterator i; - int r; - - assert(bus); - assert(path); - assert(nodes); - - HASHMAP_FOREACH(seat, m->seats, i) { - char *p; - - p = seat_bus_path(seat); - if (!p) - return -ENOMEM; - - r = strv_consume(&l, p); - if (r < 0) - return r; - } - - message = sd_bus_get_current_message(bus); - if (message) { - _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; - const char *name; - Session *session; - - r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_SESSION|SD_BUS_CREDS_AUGMENT, &creds); - if (r >= 0) { - r = sd_bus_creds_get_session(creds, &name); - if (r >= 0) { - session = hashmap_get(m->sessions, name); - if (session && session->seat) { - r = strv_extend(&l, "/org/freedesktop/login1/seat/self"); - if (r < 0) - return r; - } - } - } - } - - *nodes = l; - l = NULL; - - return 1; -} - -int seat_send_signal(Seat *s, bool new_seat) { - _cleanup_free_ char *p = NULL; - - assert(s); - - p = seat_bus_path(s); - if (!p) - return -ENOMEM; - - return sd_bus_emit_signal( - s->manager->bus, - "/org/freedesktop/login1", - "org.freedesktop.login1.Manager", - new_seat ? "SeatNew" : "SeatRemoved", - "so", s->id, p); -} - -int seat_send_changed(Seat *s, const char *properties, ...) { - _cleanup_free_ char *p = NULL; - char **l; - - assert(s); - - if (!s->started) - return 0; - - p = seat_bus_path(s); - if (!p) - return -ENOMEM; - - l = strv_from_stdarg_alloca(properties); - - return sd_bus_emit_properties_changed_strv(s->manager->bus, p, "org.freedesktop.login1.Seat", l); -} diff --git a/src/grp-login/liblogind-core/logind-seat.c b/src/grp-login/liblogind-core/logind-seat.c deleted file mode 100644 index 6525ad5d7b..0000000000 --- a/src/grp-login/liblogind-core/logind-seat.c +++ /dev/null @@ -1,696 +0,0 @@ -/*** - 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 Lesser General Public License as published by - the Free Software Foundation; either version 2.1 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include - -#include - -#include "basic/alloc-util.h" -#include "basic/fd-util.h" -#include "basic/fileio.h" -#include "basic/formats-util.h" -#include "basic/mkdir.h" -#include "basic/parse-util.h" -#include "basic/stdio-util.h" -#include "basic/string-util.h" -#include "basic/terminal-util.h" -#include "basic/util.h" - -#include "logind-acl.h" -#include "logind-seat.h" - -Seat *seat_new(Manager *m, const char *id) { - Seat *s; - - assert(m); - assert(id); - - s = new0(Seat, 1); - if (!s) - return NULL; - - s->state_file = strappend("/run/systemd/seats/", id); - if (!s->state_file) { - free(s); - return NULL; - } - - s->id = basename(s->state_file); - s->manager = m; - - if (hashmap_put(m->seats, s->id, s) < 0) { - free(s->state_file); - free(s); - return NULL; - } - - return s; -} - -void seat_free(Seat *s) { - assert(s); - - if (s->in_gc_queue) - LIST_REMOVE(gc_queue, s->manager->seat_gc_queue, s); - - while (s->sessions) - session_free(s->sessions); - - assert(!s->active); - - while (s->devices) - device_free(s->devices); - - hashmap_remove(s->manager->seats, s->id); - - free(s->positions); - free(s->state_file); - free(s); -} - -int seat_save(Seat *s) { - _cleanup_free_ char *temp_path = NULL; - _cleanup_fclose_ FILE *f = NULL; - int r; - - assert(s); - - if (!s->started) - return 0; - - r = mkdir_safe_label("/run/systemd/seats", 0755, 0, 0); - if (r < 0) - goto fail; - - r = fopen_temporary(s->state_file, &f, &temp_path); - if (r < 0) - goto fail; - - fchmod(fileno(f), 0644); - - fprintf(f, - "# This is private data. Do not parse.\n" - "IS_SEAT0=%i\n" - "CAN_MULTI_SESSION=%i\n" - "CAN_TTY=%i\n" - "CAN_GRAPHICAL=%i\n", - seat_is_seat0(s), - seat_can_multi_session(s), - seat_can_tty(s), - seat_can_graphical(s)); - - if (s->active) { - assert(s->active->user); - - fprintf(f, - "ACTIVE=%s\n" - "ACTIVE_UID="UID_FMT"\n", - s->active->id, - s->active->user->uid); - } - - if (s->sessions) { - Session *i; - - fputs("SESSIONS=", f); - LIST_FOREACH(sessions_by_seat, i, s->sessions) { - fprintf(f, - "%s%c", - i->id, - i->sessions_by_seat_next ? ' ' : '\n'); - } - - fputs("UIDS=", f); - LIST_FOREACH(sessions_by_seat, i, s->sessions) - fprintf(f, - UID_FMT"%c", - i->user->uid, - i->sessions_by_seat_next ? ' ' : '\n'); - } - - r = fflush_and_check(f); - if (r < 0) - goto fail; - - if (rename(temp_path, s->state_file) < 0) { - r = -errno; - goto fail; - } - - return 0; - -fail: - (void) unlink(s->state_file); - - if (temp_path) - (void) unlink(temp_path); - - return log_error_errno(r, "Failed to save seat data %s: %m", s->state_file); -} - -int seat_load(Seat *s) { - assert(s); - - /* There isn't actually anything to read here ... */ - - return 0; -} - -static int vt_allocate(unsigned int vtnr) { - char p[sizeof("/dev/tty") + DECIMAL_STR_MAX(unsigned int)]; - _cleanup_close_ int fd = -1; - - assert(vtnr >= 1); - - xsprintf(p, "/dev/tty%u", vtnr); - fd = open_terminal(p, O_RDWR|O_NOCTTY|O_CLOEXEC); - if (fd < 0) - return -errno; - - return 0; -} - -int seat_preallocate_vts(Seat *s) { - int r = 0; - unsigned i; - - assert(s); - assert(s->manager); - - log_debug("Preallocating VTs..."); - - if (s->manager->n_autovts <= 0) - return 0; - - if (!seat_has_vts(s)) - return 0; - - for (i = 1; i <= s->manager->n_autovts; i++) { - int q; - - q = vt_allocate(i); - if (q < 0) { - log_error_errno(q, "Failed to preallocate VT %u: %m", i); - r = q; - } - } - - return r; -} - -int seat_apply_acls(Seat *s, Session *old_active) { - int r; - - assert(s); - - r = devnode_acl_all(s->manager->udev, - s->id, - false, - !!old_active, old_active ? old_active->user->uid : 0, - !!s->active, s->active ? s->active->user->uid : 0); - - if (r < 0) - log_error_errno(r, "Failed to apply ACLs: %m"); - - return r; -} - -int seat_set_active(Seat *s, Session *session) { - Session *old_active; - - assert(s); - assert(!session || session->seat == s); - - if (session == s->active) - return 0; - - old_active = s->active; - s->active = session; - - if (old_active) { - session_device_pause_all(old_active); - session_send_changed(old_active, "Active", NULL); - } - - seat_apply_acls(s, old_active); - - if (session && session->started) { - session_send_changed(session, "Active", NULL); - session_device_resume_all(session); - } - - if (!session || session->started) - seat_send_changed(s, "ActiveSession", NULL); - - seat_save(s); - - if (session) { - session_save(session); - user_save(session->user); - } - - if (old_active) { - session_save(old_active); - if (!session || session->user != old_active->user) - user_save(old_active->user); - } - - return 0; -} - -int seat_switch_to(Seat *s, unsigned int num) { - /* Public session positions skip 0 (there is only F1-F12). Maybe it - * will get reassigned in the future, so return error for now. */ - if (num == 0) - return -EINVAL; - - if (num >= s->position_count || !s->positions[num]) { - /* allow switching to unused VTs to trigger auto-activate */ - if (seat_has_vts(s) && num < 64) - return chvt(num); - - return -EINVAL; - } - - return session_activate(s->positions[num]); -} - -int seat_switch_to_next(Seat *s) { - unsigned int start, i; - - if (s->position_count == 0) - return -EINVAL; - - start = 1; - if (s->active && s->active->position > 0) - start = s->active->position; - - for (i = start + 1; i < s->position_count; ++i) - if (s->positions[i]) - return session_activate(s->positions[i]); - - for (i = 1; i < start; ++i) - if (s->positions[i]) - return session_activate(s->positions[i]); - - return -EINVAL; -} - -int seat_switch_to_previous(Seat *s) { - unsigned int start, i; - - if (s->position_count == 0) - return -EINVAL; - - start = 1; - if (s->active && s->active->position > 0) - start = s->active->position; - - for (i = start - 1; i > 0; --i) - if (s->positions[i]) - return session_activate(s->positions[i]); - - for (i = s->position_count - 1; i > start; --i) - if (s->positions[i]) - return session_activate(s->positions[i]); - - return -EINVAL; -} - -int seat_active_vt_changed(Seat *s, unsigned int vtnr) { - Session *i, *new_active = NULL; - int r; - - assert(s); - assert(vtnr >= 1); - - if (!seat_has_vts(s)) - return -EINVAL; - - log_debug("VT changed to %u", vtnr); - - /* we might have earlier closing sessions on the same VT, so try to - * find a running one first */ - LIST_FOREACH(sessions_by_seat, i, s->sessions) - if (i->vtnr == vtnr && !i->stopping) { - new_active = i; - break; - } - - if (!new_active) { - /* no running one? then we can't decide which one is the - * active one, let the first one win */ - LIST_FOREACH(sessions_by_seat, i, s->sessions) - if (i->vtnr == vtnr) { - new_active = i; - break; - } - } - - r = seat_set_active(s, new_active); - manager_spawn_autovt(s->manager, vtnr); - - return r; -} - -int seat_read_active_vt(Seat *s) { - char t[64]; - ssize_t k; - unsigned int vtnr; - int r; - - assert(s); - - if (!seat_has_vts(s)) - return 0; - - lseek(s->manager->console_active_fd, SEEK_SET, 0); - - k = read(s->manager->console_active_fd, t, sizeof(t)-1); - if (k <= 0) { - log_error("Failed to read current console: %s", k < 0 ? strerror(-errno) : "EOF"); - return k < 0 ? -errno : -EIO; - } - - t[k] = 0; - truncate_nl(t); - - if (!startswith(t, "tty")) { - log_error("Hm, /sys/class/tty/tty0/active is badly formatted."); - return -EIO; - } - - r = safe_atou(t+3, &vtnr); - if (r < 0) { - log_error("Failed to parse VT number %s", t+3); - return r; - } - - if (!vtnr) { - log_error("VT number invalid: %s", t+3); - return -EIO; - } - - return seat_active_vt_changed(s, vtnr); -} - -int seat_start(Seat *s) { - assert(s); - - if (s->started) - return 0; - - log_struct(LOG_INFO, - LOG_MESSAGE_ID(SD_MESSAGE_SEAT_START), - "SEAT_ID=%s", s->id, - LOG_MESSAGE("New seat %s.", s->id), - NULL); - - /* Initialize VT magic stuff */ - seat_preallocate_vts(s); - - /* Read current VT */ - seat_read_active_vt(s); - - s->started = true; - - /* Save seat data */ - seat_save(s); - - seat_send_signal(s, true); - - return 0; -} - -int seat_stop(Seat *s, bool force) { - int r = 0; - - assert(s); - - if (s->started) - log_struct(LOG_INFO, - LOG_MESSAGE_ID(SD_MESSAGE_SEAT_STOP), - "SEAT_ID=%s", s->id, - LOG_MESSAGE("Removed seat %s.", s->id), - NULL); - - seat_stop_sessions(s, force); - - unlink(s->state_file); - seat_add_to_gc_queue(s); - - if (s->started) - seat_send_signal(s, false); - - s->started = false; - - return r; -} - -int seat_stop_sessions(Seat *s, bool force) { - Session *session; - int r = 0, k; - - assert(s); - - LIST_FOREACH(sessions_by_seat, session, s->sessions) { - k = session_stop(session, force); - if (k < 0) - r = k; - } - - return r; -} - -void seat_evict_position(Seat *s, Session *session) { - Session *iter; - unsigned int pos = session->position; - - session->position = 0; - - if (pos == 0) - return; - - if (pos < s->position_count && s->positions[pos] == session) { - s->positions[pos] = NULL; - - /* There might be another session claiming the same - * position (eg., during gdm->session transition), so let's look - * for it and set it on the free slot. */ - LIST_FOREACH(sessions_by_seat, iter, s->sessions) { - if (iter->position == pos && session_get_state(iter) != SESSION_CLOSING) { - s->positions[pos] = iter; - break; - } - } - } -} - -void seat_claim_position(Seat *s, Session *session, unsigned int pos) { - /* with VTs, the position is always the same as the VTnr */ - if (seat_has_vts(s)) - pos = session->vtnr; - - if (!GREEDY_REALLOC0(s->positions, s->position_count, pos + 1)) - return; - - seat_evict_position(s, session); - - session->position = pos; - if (pos > 0) - s->positions[pos] = session; -} - -static void seat_assign_position(Seat *s, Session *session) { - unsigned int pos; - - if (session->position > 0) - return; - - for (pos = 1; pos < s->position_count; ++pos) - if (!s->positions[pos]) - break; - - seat_claim_position(s, session, pos); -} - -int seat_attach_session(Seat *s, Session *session) { - assert(s); - assert(session); - assert(!session->seat); - - if (!seat_has_vts(s) != !session->vtnr) - return -EINVAL; - - session->seat = s; - LIST_PREPEND(sessions_by_seat, s->sessions, session); - seat_assign_position(s, session); - - seat_send_changed(s, "Sessions", NULL); - - /* On seats with VTs, the VT logic defines which session is active. On - * seats without VTs, we automatically activate new sessions. */ - if (!seat_has_vts(s)) - seat_set_active(s, session); - - return 0; -} - -void seat_complete_switch(Seat *s) { - Session *session; - - assert(s); - - /* if no session-switch is pending or if it got canceled, do nothing */ - if (!s->pending_switch) - return; - - session = s->pending_switch; - s->pending_switch = NULL; - - seat_set_active(s, session); -} - -bool seat_has_vts(Seat *s) { - assert(s); - - return seat_is_seat0(s) && s->manager->console_active_fd >= 0; -} - -bool seat_is_seat0(Seat *s) { - assert(s); - - return s->manager->seat0 == s; -} - -bool seat_can_multi_session(Seat *s) { - assert(s); - - return seat_has_vts(s); -} - -bool seat_can_tty(Seat *s) { - assert(s); - - return seat_has_vts(s); -} - -bool seat_has_master_device(Seat *s) { - assert(s); - - /* device list is ordered by "master" flag */ - return !!s->devices && s->devices->master; -} - -bool seat_can_graphical(Seat *s) { - assert(s); - - return seat_has_master_device(s); -} - -int seat_get_idle_hint(Seat *s, dual_timestamp *t) { - Session *session; - bool idle_hint = true; - dual_timestamp ts = DUAL_TIMESTAMP_NULL; - - assert(s); - - LIST_FOREACH(sessions_by_seat, session, s->sessions) { - dual_timestamp k; - int ih; - - ih = session_get_idle_hint(session, &k); - if (ih < 0) - return ih; - - if (!ih) { - if (!idle_hint) { - if (k.monotonic > ts.monotonic) - ts = k; - } else { - idle_hint = false; - ts = k; - } - } else if (idle_hint) { - - if (k.monotonic > ts.monotonic) - ts = k; - } - } - - if (t) - *t = ts; - - return idle_hint; -} - -bool seat_check_gc(Seat *s, bool drop_not_started) { - assert(s); - - if (drop_not_started && !s->started) - return false; - - if (seat_is_seat0(s)) - return true; - - return seat_has_master_device(s); -} - -void seat_add_to_gc_queue(Seat *s) { - assert(s); - - if (s->in_gc_queue) - return; - - LIST_PREPEND(gc_queue, s->manager->seat_gc_queue, s); - s->in_gc_queue = true; -} - -static bool seat_name_valid_char(char c) { - return - (c >= 'a' && c <= 'z') || - (c >= 'A' && c <= 'Z') || - (c >= '0' && c <= '9') || - c == '-' || - c == '_'; -} - -bool seat_name_is_valid(const char *name) { - const char *p; - - assert(name); - - if (!startswith(name, "seat")) - return false; - - if (!name[4]) - return false; - - for (p = name; *p; p++) - if (!seat_name_valid_char(*p)) - return false; - - if (strlen(name) > 255) - return false; - - return true; -} diff --git a/src/grp-login/liblogind-core/logind-seat.h b/src/grp-login/liblogind-core/logind-seat.h deleted file mode 100644 index 7fbeb5a727..0000000000 --- a/src/grp-login/liblogind-core/logind-seat.h +++ /dev/null @@ -1,96 +0,0 @@ -#pragma once - -/*** - 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 Lesser General Public License as published by - the Free Software Foundation; either version 2.1 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "basic/list.h" - -typedef struct Seat Seat; - -#include "logind-session.h" - -struct Seat { - Manager *manager; - char *id; - - char *state_file; - - LIST_HEAD(Device, devices); - - Session *active; - Session *pending_switch; - LIST_HEAD(Session, sessions); - - Session **positions; - size_t position_count; - - bool in_gc_queue:1; - bool started:1; - - LIST_FIELDS(Seat, gc_queue); -}; - -Seat *seat_new(Manager *m, const char *id); -void seat_free(Seat *s); - -int seat_save(Seat *s); -int seat_load(Seat *s); - -int seat_apply_acls(Seat *s, Session *old_active); -int seat_set_active(Seat *s, Session *session); -int seat_switch_to(Seat *s, unsigned int num); -int seat_switch_to_next(Seat *s); -int seat_switch_to_previous(Seat *s); -int seat_active_vt_changed(Seat *s, unsigned int vtnr); -int seat_read_active_vt(Seat *s); -int seat_preallocate_vts(Seat *s); - -int seat_attach_session(Seat *s, Session *session); -void seat_complete_switch(Seat *s); -void seat_evict_position(Seat *s, Session *session); -void seat_claim_position(Seat *s, Session *session, unsigned int pos); - -bool seat_has_vts(Seat *s); -bool seat_is_seat0(Seat *s); -bool seat_can_multi_session(Seat *s); -bool seat_can_tty(Seat *s); -bool seat_has_master_device(Seat *s); -bool seat_can_graphical(Seat *s); - -int seat_get_idle_hint(Seat *s, dual_timestamp *t); - -int seat_start(Seat *s); -int seat_stop(Seat *s, bool force); -int seat_stop_sessions(Seat *s, bool force); - -bool seat_check_gc(Seat *s, bool drop_not_started); -void seat_add_to_gc_queue(Seat *s); - -bool seat_name_is_valid(const char *name); - -extern const sd_bus_vtable seat_vtable[]; - -int seat_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error); -int seat_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error); -char *seat_bus_path(Seat *s); - -int seat_send_signal(Seat *s, bool new_seat); -int seat_send_changed(Seat *s, const char *properties, ...) _sentinel_; - -int bus_seat_method_terminate(sd_bus_message *message, void *userdata, sd_bus_error *error); diff --git a/src/grp-login/liblogind-core/logind-session-dbus.c b/src/grp-login/liblogind-core/logind-session-dbus.c deleted file mode 100644 index 76228d4474..0000000000 --- a/src/grp-login/liblogind-core/logind-session-dbus.c +++ /dev/null @@ -1,799 +0,0 @@ -/*** - 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 Lesser General Public License as published by - the Free Software Foundation; either version 2.1 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include - -#include "basic/alloc-util.h" -#include "basic/bus-label.h" -#include "basic/fd-util.h" -#include "basic/signal-util.h" -#include "basic/strv.h" -#include "basic/util.h" -#include "logind.h" -#include "sd-bus/bus-common-errors.h" -#include "shared/bus-util.h" - -#include "logind-session-device.h" -#include "logind-session.h" - -static int property_get_user( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - _cleanup_free_ char *p = NULL; - Session *s = userdata; - - assert(bus); - assert(reply); - assert(s); - - p = user_bus_path(s->user); - if (!p) - return -ENOMEM; - - return sd_bus_message_append(reply, "(uo)", (uint32_t) s->user->uid, p); -} - -static int property_get_name( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - Session *s = userdata; - - assert(bus); - assert(reply); - assert(s); - - return sd_bus_message_append(reply, "s", s->user->name); -} - -static int property_get_seat( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - _cleanup_free_ char *p = NULL; - Session *s = userdata; - - assert(bus); - assert(reply); - assert(s); - - p = s->seat ? seat_bus_path(s->seat) : strdup("/"); - if (!p) - return -ENOMEM; - - return sd_bus_message_append(reply, "(so)", s->seat ? s->seat->id : "", p); -} - -static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_type, session_type, SessionType); -static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_class, session_class, SessionClass); - -static int property_get_active( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - Session *s = userdata; - - assert(bus); - assert(reply); - assert(s); - - return sd_bus_message_append(reply, "b", session_is_active(s)); -} - -static int property_get_state( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - Session *s = userdata; - - assert(bus); - assert(reply); - assert(s); - - return sd_bus_message_append(reply, "s", session_state_to_string(session_get_state(s))); -} - -static int property_get_idle_hint( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - Session *s = userdata; - - assert(bus); - assert(reply); - assert(s); - - return sd_bus_message_append(reply, "b", session_get_idle_hint(s, NULL) > 0); -} - -static int property_get_idle_since_hint( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - Session *s = userdata; - dual_timestamp t = DUAL_TIMESTAMP_NULL; - uint64_t u; - int r; - - assert(bus); - assert(reply); - assert(s); - - r = session_get_idle_hint(s, &t); - if (r < 0) - return r; - - u = streq(property, "IdleSinceHint") ? t.realtime : t.monotonic; - - return sd_bus_message_append(reply, "t", u); -} - -static int property_get_locked_hint( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - Session *s = userdata; - - assert(bus); - assert(reply); - assert(s); - - return sd_bus_message_append(reply, "b", session_get_locked_hint(s) > 0); -} - -int bus_session_method_terminate(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Session *s = userdata; - int r; - - assert(message); - assert(s); - - r = bus_verify_polkit_async( - message, - CAP_KILL, - "org.freedesktop.login1.manage", - NULL, - false, - s->user->uid, - &s->manager->polkit_registry, - error); - if (r < 0) - return r; - if (r == 0) - return 1; /* Will call us back */ - - r = session_stop(s, true); - if (r < 0) - return r; - - return sd_bus_reply_method_return(message, NULL); -} - -int bus_session_method_activate(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Session *s = userdata; - int r; - - assert(message); - assert(s); - - r = session_activate(s); - if (r < 0) - return r; - - return sd_bus_reply_method_return(message, NULL); -} - -int bus_session_method_lock(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Session *s = userdata; - int r; - - assert(message); - assert(s); - - r = bus_verify_polkit_async( - message, - CAP_SYS_ADMIN, - "org.freedesktop.login1.lock-sessions", - NULL, - false, - s->user->uid, - &s->manager->polkit_registry, - error); - if (r < 0) - return r; - if (r == 0) - return 1; /* Will call us back */ - - r = session_send_lock(s, strstr(sd_bus_message_get_member(message), "Lock")); - if (r < 0) - return r; - - return sd_bus_reply_method_return(message, NULL); -} - -static int method_set_idle_hint(sd_bus_message *message, void *userdata, sd_bus_error *error) { - _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; - Session *s = userdata; - uid_t uid; - int r, b; - - assert(message); - assert(s); - - r = sd_bus_message_read(message, "b", &b); - if (r < 0) - return r; - - r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_EUID, &creds); - if (r < 0) - return r; - - r = sd_bus_creds_get_euid(creds, &uid); - if (r < 0) - return r; - - if (uid != 0 && uid != s->user->uid) - return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Only owner of session may set idle hint"); - - session_set_idle_hint(s, b); - - return sd_bus_reply_method_return(message, NULL); -} - -static int method_set_locked_hint(sd_bus_message *message, void *userdata, sd_bus_error *error) { - _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; - Session *s = userdata; - uid_t uid; - int r, b; - - assert(message); - assert(s); - - r = sd_bus_message_read(message, "b", &b); - if (r < 0) - return r; - - r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_EUID, &creds); - if (r < 0) - return r; - - r = sd_bus_creds_get_euid(creds, &uid); - if (r < 0) - return r; - - if (uid != 0 && uid != s->user->uid) - return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Only owner of session may set locked hint"); - - session_set_locked_hint(s, b); - - return sd_bus_reply_method_return(message, NULL); -} - -int bus_session_method_kill(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Session *s = userdata; - const char *swho; - int32_t signo; - KillWho who; - int r; - - assert(message); - assert(s); - - r = sd_bus_message_read(message, "si", &swho, &signo); - if (r < 0) - return r; - - if (isempty(swho)) - who = KILL_ALL; - else { - who = kill_who_from_string(swho); - if (who < 0) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid kill parameter '%s'", swho); - } - - if (!SIGNAL_VALID(signo)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid signal %i", signo); - - r = bus_verify_polkit_async( - message, - CAP_KILL, - "org.freedesktop.login1.manage", - NULL, - false, - s->user->uid, - &s->manager->polkit_registry, - error); - if (r < 0) - return r; - if (r == 0) - return 1; /* Will call us back */ - - r = session_kill(s, who, signo); - if (r < 0) - return r; - - return sd_bus_reply_method_return(message, NULL); -} - -static int method_take_control(sd_bus_message *message, void *userdata, sd_bus_error *error) { - _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; - Session *s = userdata; - int r, force; - uid_t uid; - - assert(message); - assert(s); - - r = sd_bus_message_read(message, "b", &force); - if (r < 0) - return r; - - r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_EUID, &creds); - if (r < 0) - return r; - - r = sd_bus_creds_get_euid(creds, &uid); - if (r < 0) - return r; - - if (uid != 0 && (force || uid != s->user->uid)) - return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Only owner of session may take control"); - - r = session_set_controller(s, sd_bus_message_get_sender(message), force); - if (r < 0) - return r; - - return sd_bus_reply_method_return(message, NULL); -} - -static int method_release_control(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Session *s = userdata; - - assert(message); - assert(s); - - if (!session_is_controller(s, sd_bus_message_get_sender(message))) - return sd_bus_error_setf(error, BUS_ERROR_NOT_IN_CONTROL, "You are not in control of this session"); - - session_drop_controller(s); - - return sd_bus_reply_method_return(message, NULL); -} - -static int method_take_device(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Session *s = userdata; - uint32_t major, minor; - SessionDevice *sd; - dev_t dev; - int r; - - assert(message); - assert(s); - - r = sd_bus_message_read(message, "uu", &major, &minor); - if (r < 0) - return r; - - if (!session_is_controller(s, sd_bus_message_get_sender(message))) - return sd_bus_error_setf(error, BUS_ERROR_NOT_IN_CONTROL, "You are not in control of this session"); - - dev = makedev(major, minor); - sd = hashmap_get(s->devices, &dev); - if (sd) - /* We don't allow retrieving a device multiple times. - * The related ReleaseDevice call is not ref-counted. - * The caller should use dup() if it requires more - * than one fd (it would be functionally - * equivalent). */ - return sd_bus_error_setf(error, BUS_ERROR_DEVICE_IS_TAKEN, "Device already taken"); - - r = session_device_new(s, dev, &sd); - if (r < 0) - return r; - - r = sd_bus_reply_method_return(message, "hb", sd->fd, !sd->active); - if (r < 0) - session_device_free(sd); - - return r; -} - -static int method_release_device(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Session *s = userdata; - uint32_t major, minor; - SessionDevice *sd; - dev_t dev; - int r; - - assert(message); - assert(s); - - r = sd_bus_message_read(message, "uu", &major, &minor); - if (r < 0) - return r; - - if (!session_is_controller(s, sd_bus_message_get_sender(message))) - return sd_bus_error_setf(error, BUS_ERROR_NOT_IN_CONTROL, "You are not in control of this session"); - - dev = makedev(major, minor); - sd = hashmap_get(s->devices, &dev); - if (!sd) - return sd_bus_error_setf(error, BUS_ERROR_DEVICE_NOT_TAKEN, "Device not taken"); - - session_device_free(sd); - return sd_bus_reply_method_return(message, NULL); -} - -static int method_pause_device_complete(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Session *s = userdata; - uint32_t major, minor; - SessionDevice *sd; - dev_t dev; - int r; - - assert(message); - assert(s); - - r = sd_bus_message_read(message, "uu", &major, &minor); - if (r < 0) - return r; - - if (!session_is_controller(s, sd_bus_message_get_sender(message))) - return sd_bus_error_setf(error, BUS_ERROR_NOT_IN_CONTROL, "You are not in control of this session"); - - dev = makedev(major, minor); - sd = hashmap_get(s->devices, &dev); - if (!sd) - return sd_bus_error_setf(error, BUS_ERROR_DEVICE_NOT_TAKEN, "Device not taken"); - - session_device_complete_pause(sd); - - return sd_bus_reply_method_return(message, NULL); -} - -const sd_bus_vtable session_vtable[] = { - SD_BUS_VTABLE_START(0), - - SD_BUS_PROPERTY("Id", "s", NULL, offsetof(Session, id), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("User", "(uo)", property_get_user, 0, SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Name", "s", property_get_name, 0, SD_BUS_VTABLE_PROPERTY_CONST), - BUS_PROPERTY_DUAL_TIMESTAMP("Timestamp", offsetof(Session, timestamp), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("VTNr", "u", NULL, offsetof(Session, vtnr), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Seat", "(so)", property_get_seat, 0, SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("TTY", "s", NULL, offsetof(Session, tty), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Display", "s", NULL, offsetof(Session, display), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Remote", "b", bus_property_get_bool, offsetof(Session, remote), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("RemoteHost", "s", NULL, offsetof(Session, remote_host), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("RemoteUser", "s", NULL, offsetof(Session, remote_user), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Service", "s", NULL, offsetof(Session, service), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Desktop", "s", NULL, offsetof(Session, desktop), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Scope", "s", NULL, offsetof(Session, scope), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Leader", "u", bus_property_get_pid, offsetof(Session, leader), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Audit", "u", NULL, offsetof(Session, audit_id), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Type", "s", property_get_type, offsetof(Session, type), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Class", "s", property_get_class, offsetof(Session, class), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Active", "b", property_get_active, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("State", "s", property_get_state, 0, 0), - SD_BUS_PROPERTY("IdleHint", "b", property_get_idle_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("IdleSinceHint", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("IdleSinceHintMonotonic", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("LockedHint", "b", property_get_locked_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - - SD_BUS_METHOD("Terminate", NULL, NULL, bus_session_method_terminate, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("Activate", NULL, NULL, bus_session_method_activate, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("Lock", NULL, NULL, bus_session_method_lock, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("Unlock", NULL, NULL, bus_session_method_lock, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("SetIdleHint", "b", NULL, method_set_idle_hint, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("SetLockedHint", "b", NULL, method_set_locked_hint, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("Kill", "si", NULL, bus_session_method_kill, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("TakeControl", "b", NULL, method_take_control, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("ReleaseControl", NULL, NULL, method_release_control, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("TakeDevice", "uu", "hb", method_take_device, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("ReleaseDevice", "uu", NULL, method_release_device, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("PauseDeviceComplete", "uu", NULL, method_pause_device_complete, SD_BUS_VTABLE_UNPRIVILEGED), - - SD_BUS_SIGNAL("PauseDevice", "uus", 0), - SD_BUS_SIGNAL("ResumeDevice", "uuh", 0), - SD_BUS_SIGNAL("Lock", NULL, 0), - SD_BUS_SIGNAL("Unlock", NULL, 0), - - SD_BUS_VTABLE_END -}; - -int session_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) { - Manager *m = userdata; - Session *session; - int r; - - assert(bus); - assert(path); - assert(interface); - assert(found); - assert(m); - - if (streq(path, "/org/freedesktop/login1/session/self")) { - _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; - sd_bus_message *message; - const char *name; - - message = sd_bus_get_current_message(bus); - if (!message) - return 0; - - r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_SESSION|SD_BUS_CREDS_AUGMENT, &creds); - if (r < 0) - return r; - - r = sd_bus_creds_get_session(creds, &name); - if (r < 0) - return r; - - session = hashmap_get(m->sessions, name); - } else { - _cleanup_free_ char *e = NULL; - const char *p; - - p = startswith(path, "/org/freedesktop/login1/session/"); - if (!p) - return 0; - - e = bus_label_unescape(p); - if (!e) - return -ENOMEM; - - session = hashmap_get(m->sessions, e); - } - - if (!session) - return 0; - - *found = session; - return 1; -} - -char *session_bus_path(Session *s) { - _cleanup_free_ char *t = NULL; - - assert(s); - - t = bus_label_escape(s->id); - if (!t) - return NULL; - - return strappend("/org/freedesktop/login1/session/", t); -} - -int session_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) { - _cleanup_strv_free_ char **l = NULL; - sd_bus_message *message; - Manager *m = userdata; - Session *session; - Iterator i; - int r; - - assert(bus); - assert(path); - assert(nodes); - - HASHMAP_FOREACH(session, m->sessions, i) { - char *p; - - p = session_bus_path(session); - if (!p) - return -ENOMEM; - - r = strv_consume(&l, p); - if (r < 0) - return r; - } - - message = sd_bus_get_current_message(bus); - if (message) { - _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; - const char *name; - - r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_SESSION|SD_BUS_CREDS_AUGMENT, &creds); - if (r >= 0) { - r = sd_bus_creds_get_session(creds, &name); - if (r >= 0) { - session = hashmap_get(m->sessions, name); - if (session) { - r = strv_extend(&l, "/org/freedesktop/login1/session/self"); - if (r < 0) - return r; - } - } - } - } - - *nodes = l; - l = NULL; - - return 1; -} - -int session_send_signal(Session *s, bool new_session) { - _cleanup_free_ char *p = NULL; - - assert(s); - - p = session_bus_path(s); - if (!p) - return -ENOMEM; - - return sd_bus_emit_signal( - s->manager->bus, - "/org/freedesktop/login1", - "org.freedesktop.login1.Manager", - new_session ? "SessionNew" : "SessionRemoved", - "so", s->id, p); -} - -int session_send_changed(Session *s, const char *properties, ...) { - _cleanup_free_ char *p = NULL; - char **l; - - assert(s); - - if (!s->started) - return 0; - - p = session_bus_path(s); - if (!p) - return -ENOMEM; - - l = strv_from_stdarg_alloca(properties); - - return sd_bus_emit_properties_changed_strv(s->manager->bus, p, "org.freedesktop.login1.Session", l); -} - -int session_send_lock(Session *s, bool lock) { - _cleanup_free_ char *p = NULL; - - assert(s); - - p = session_bus_path(s); - if (!p) - return -ENOMEM; - - return sd_bus_emit_signal( - s->manager->bus, - p, - "org.freedesktop.login1.Session", - lock ? "Lock" : "Unlock", - NULL); -} - -int session_send_lock_all(Manager *m, bool lock) { - Session *session; - Iterator i; - int r = 0; - - assert(m); - - HASHMAP_FOREACH(session, m->sessions, i) { - int k; - - k = session_send_lock(session, lock); - if (k < 0) - r = k; - } - - return r; -} - -int session_send_create_reply(Session *s, sd_bus_error *error) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *c = NULL; - _cleanup_close_ int fifo_fd = -1; - _cleanup_free_ char *p = NULL; - - assert(s); - - /* This is called after the session scope and the user service - * were successfully created, and finishes where - * bus_manager_create_session() left off. */ - - if (!s->create_message) - return 0; - - if (!sd_bus_error_is_set(error) && (s->scope_job || s->user->service_job)) - return 0; - - c = s->create_message; - s->create_message = NULL; - - if (error) - return sd_bus_reply_method_error(c, error); - - fifo_fd = session_create_fifo(s); - if (fifo_fd < 0) - return fifo_fd; - - /* Update the session state file before we notify the client - * about the result. */ - session_save(s); - - p = session_bus_path(s); - if (!p) - return -ENOMEM; - - log_debug("Sending reply about created session: " - "id=%s object_path=%s uid=%u runtime_path=%s " - "session_fd=%d seat=%s vtnr=%u", - s->id, - p, - (uint32_t) s->user->uid, - s->user->runtime_path, - fifo_fd, - s->seat ? s->seat->id : "", - (uint32_t) s->vtnr); - - return sd_bus_reply_method_return( - c, "soshusub", - s->id, - p, - s->user->runtime_path, - fifo_fd, - (uint32_t) s->user->uid, - s->seat ? s->seat->id : "", - (uint32_t) s->vtnr, - false); -} diff --git a/src/grp-login/liblogind-core/logind-session-device.c b/src/grp-login/liblogind-core/logind-session-device.c deleted file mode 100644 index 99528488c0..0000000000 --- a/src/grp-login/liblogind-core/logind-session-device.c +++ /dev/null @@ -1,482 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 David Herrmann - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include - -#include - -#include - -#include "basic/alloc-util.h" -#include "basic/fd-util.h" -#include "basic/missing.h" -#include "basic/util.h" -#include "shared/bus-util.h" - -#include "logind-session-device.h" - -enum SessionDeviceNotifications { - SESSION_DEVICE_RESUME, - SESSION_DEVICE_TRY_PAUSE, - SESSION_DEVICE_PAUSE, - SESSION_DEVICE_RELEASE, -}; - -static int session_device_notify(SessionDevice *sd, enum SessionDeviceNotifications type) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; - _cleanup_free_ char *path = NULL; - const char *t = NULL; - uint32_t major, minor; - int r; - - assert(sd); - - major = major(sd->dev); - minor = minor(sd->dev); - - if (!sd->session->controller) - return 0; - - path = session_bus_path(sd->session); - if (!path) - return -ENOMEM; - - r = sd_bus_message_new_signal( - sd->session->manager->bus, - &m, path, - "org.freedesktop.login1.Session", - (type == SESSION_DEVICE_RESUME) ? "ResumeDevice" : "PauseDevice"); - if (!m) - return r; - - r = sd_bus_message_set_destination(m, sd->session->controller); - if (r < 0) - return r; - - switch (type) { - case SESSION_DEVICE_RESUME: - r = sd_bus_message_append(m, "uuh", major, minor, sd->fd); - if (r < 0) - return r; - break; - case SESSION_DEVICE_TRY_PAUSE: - t = "pause"; - break; - case SESSION_DEVICE_PAUSE: - t = "force"; - break; - case SESSION_DEVICE_RELEASE: - t = "gone"; - break; - default: - return -EINVAL; - } - - if (t) { - r = sd_bus_message_append(m, "uus", major, minor, t); - if (r < 0) - return r; - } - - return sd_bus_send(sd->session->manager->bus, m, NULL); -} - -static int sd_eviocrevoke(int fd) { - static bool warned; - int r; - - assert(fd >= 0); - - r = ioctl(fd, EVIOCREVOKE, NULL); - if (r < 0) { - r = -errno; - if (r == -EINVAL && !warned) { - warned = true; - log_warning("kernel does not support evdev-revocation"); - } - } - - return 0; -} - -static int sd_drmsetmaster(int fd) { - int r; - - assert(fd >= 0); - - r = ioctl(fd, DRM_IOCTL_SET_MASTER, 0); - if (r < 0) - return -errno; - - return 0; -} - -static int sd_drmdropmaster(int fd) { - int r; - - assert(fd >= 0); - - r = ioctl(fd, DRM_IOCTL_DROP_MASTER, 0); - if (r < 0) - return -errno; - - return 0; -} - -static int session_device_open(SessionDevice *sd, bool active) { - int fd, r; - - assert(sd->type != DEVICE_TYPE_UNKNOWN); - - /* open device and try to get an udev_device from it */ - fd = open(sd->node, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK); - if (fd < 0) - return -errno; - - switch (sd->type) { - case DEVICE_TYPE_DRM: - if (active) { - /* Weird legacy DRM semantics might return an error - * even though we're master. No way to detect that so - * fail at all times and let caller retry in inactive - * state. */ - r = sd_drmsetmaster(fd); - if (r < 0) { - close_nointr(fd); - return r; - } - } else { - /* DRM-Master is granted to the first user who opens a - * device automatically (ughh, racy!). Hence, we just - * drop DRM-Master in case we were the first. */ - sd_drmdropmaster(fd); - } - break; - case DEVICE_TYPE_EVDEV: - if (!active) - sd_eviocrevoke(fd); - break; - case DEVICE_TYPE_UNKNOWN: - default: - /* fallback for devices wihout synchronizations */ - break; - } - - return fd; -} - -static int session_device_start(SessionDevice *sd) { - int r; - - assert(sd); - assert(session_is_active(sd->session)); - - if (sd->active) - return 0; - - switch (sd->type) { - case DEVICE_TYPE_DRM: - /* Device is kept open. Simply call drmSetMaster() and hope - * there is no-one else. In case it fails, we keep the device - * paused. Maybe at some point we have a drmStealMaster(). */ - r = sd_drmsetmaster(sd->fd); - if (r < 0) - return r; - break; - case DEVICE_TYPE_EVDEV: - /* Evdev devices are revoked while inactive. Reopen it and we - * are fine. */ - r = session_device_open(sd, true); - if (r < 0) - return r; - close_nointr(sd->fd); - sd->fd = r; - break; - case DEVICE_TYPE_UNKNOWN: - default: - /* fallback for devices wihout synchronizations */ - break; - } - - sd->active = true; - return 0; -} - -static void session_device_stop(SessionDevice *sd) { - assert(sd); - - if (!sd->active) - return; - - switch (sd->type) { - case DEVICE_TYPE_DRM: - /* On DRM devices we simply drop DRM-Master but keep it open. - * This allows the user to keep resources allocated. The - * CAP_SYS_ADMIN restriction to DRM-Master prevents users from - * circumventing this. */ - sd_drmdropmaster(sd->fd); - break; - case DEVICE_TYPE_EVDEV: - /* Revoke access on evdev file-descriptors during deactivation. - * This will basically prevent any operations on the fd and - * cannot be undone. Good side is: it needs no CAP_SYS_ADMIN - * protection this way. */ - sd_eviocrevoke(sd->fd); - break; - case DEVICE_TYPE_UNKNOWN: - default: - /* fallback for devices without synchronization */ - break; - } - - sd->active = false; -} - -static DeviceType detect_device_type(struct udev_device *dev) { - const char *sysname, *subsystem; - DeviceType type; - - sysname = udev_device_get_sysname(dev); - subsystem = udev_device_get_subsystem(dev); - type = DEVICE_TYPE_UNKNOWN; - - if (streq_ptr(subsystem, "drm")) { - if (startswith(sysname, "card")) - type = DEVICE_TYPE_DRM; - } else if (streq_ptr(subsystem, "input")) { - if (startswith(sysname, "event")) - type = DEVICE_TYPE_EVDEV; - } - - return type; -} - -static int session_device_verify(SessionDevice *sd) { - struct udev_device *dev, *p = NULL; - const char *sp, *node; - int r; - - dev = udev_device_new_from_devnum(sd->session->manager->udev, 'c', sd->dev); - if (!dev) - return -ENODEV; - - sp = udev_device_get_syspath(dev); - node = udev_device_get_devnode(dev); - if (!node) { - r = -EINVAL; - goto err_dev; - } - - /* detect device type so we can find the correct sysfs parent */ - sd->type = detect_device_type(dev); - if (sd->type == DEVICE_TYPE_UNKNOWN) { - r = -ENODEV; - goto err_dev; - } else if (sd->type == DEVICE_TYPE_EVDEV) { - /* for evdev devices we need the parent node as device */ - p = dev; - dev = udev_device_get_parent_with_subsystem_devtype(p, "input", NULL); - if (!dev) { - r = -ENODEV; - goto err_dev; - } - sp = udev_device_get_syspath(dev); - } else if (sd->type != DEVICE_TYPE_DRM) { - /* Prevent opening unsupported devices. Especially devices of - * subsystem "input" must be opened via the evdev node as - * we require EVIOCREVOKE. */ - r = -ENODEV; - goto err_dev; - } - - /* search for an existing seat device and return it if available */ - sd->device = hashmap_get(sd->session->manager->devices, sp); - if (!sd->device) { - /* The caller might have gotten the udev event before we were - * able to process it. Hence, fake the "add" event and let the - * logind-manager handle the new device. */ - r = manager_process_seat_device(sd->session->manager, dev); - if (r < 0) - goto err_dev; - - /* if it's still not available, then the device is invalid */ - sd->device = hashmap_get(sd->session->manager->devices, sp); - if (!sd->device) { - r = -ENODEV; - goto err_dev; - } - } - - if (sd->device->seat != sd->session->seat) { - r = -EPERM; - goto err_dev; - } - - sd->node = strdup(node); - if (!sd->node) { - r = -ENOMEM; - goto err_dev; - } - - r = 0; -err_dev: - udev_device_unref(p ? : dev); - return r; -} - -int session_device_new(Session *s, dev_t dev, SessionDevice **out) { - SessionDevice *sd; - int r; - - assert(s); - assert(out); - - if (!s->seat) - return -EPERM; - - sd = new0(SessionDevice, 1); - if (!sd) - return -ENOMEM; - - sd->session = s; - sd->dev = dev; - sd->fd = -1; - sd->type = DEVICE_TYPE_UNKNOWN; - - r = session_device_verify(sd); - if (r < 0) - goto error; - - r = hashmap_put(s->devices, &sd->dev, sd); - if (r < 0) { - r = -ENOMEM; - goto error; - } - - /* Open the device for the first time. We need a valid fd to pass back - * to the caller. If the session is not active, this _might_ immediately - * revoke access and thus invalidate the fd. But this is still needed - * to pass a valid fd back. */ - sd->active = session_is_active(s); - r = session_device_open(sd, sd->active); - if (r < 0) { - /* EINVAL _may_ mean a master is active; retry inactive */ - if (sd->active && r == -EINVAL) { - sd->active = false; - r = session_device_open(sd, false); - } - if (r < 0) - goto error; - } - sd->fd = r; - - LIST_PREPEND(sd_by_device, sd->device->session_devices, sd); - - *out = sd; - return 0; - -error: - hashmap_remove(s->devices, &sd->dev); - free(sd->node); - free(sd); - return r; -} - -void session_device_free(SessionDevice *sd) { - assert(sd); - - session_device_stop(sd); - session_device_notify(sd, SESSION_DEVICE_RELEASE); - close_nointr(sd->fd); - - LIST_REMOVE(sd_by_device, sd->device->session_devices, sd); - - hashmap_remove(sd->session->devices, &sd->dev); - - free(sd->node); - free(sd); -} - -void session_device_complete_pause(SessionDevice *sd) { - SessionDevice *iter; - Iterator i; - - if (!sd->active) - return; - - session_device_stop(sd); - - /* if not all devices are paused, wait for further completion events */ - HASHMAP_FOREACH(iter, sd->session->devices, i) - if (iter->active) - return; - - /* complete any pending session switch */ - seat_complete_switch(sd->session->seat); -} - -void session_device_resume_all(Session *s) { - SessionDevice *sd; - Iterator i; - int r; - - assert(s); - - HASHMAP_FOREACH(sd, s->devices, i) { - if (!sd->active) { - r = session_device_start(sd); - if (!r) - session_device_notify(sd, SESSION_DEVICE_RESUME); - } - } -} - -void session_device_pause_all(Session *s) { - SessionDevice *sd; - Iterator i; - - assert(s); - - HASHMAP_FOREACH(sd, s->devices, i) { - if (sd->active) { - session_device_stop(sd); - session_device_notify(sd, SESSION_DEVICE_PAUSE); - } - } -} - -unsigned int session_device_try_pause_all(Session *s) { - SessionDevice *sd; - Iterator i; - unsigned int num_pending = 0; - - assert(s); - - HASHMAP_FOREACH(sd, s->devices, i) { - if (sd->active) { - session_device_notify(sd, SESSION_DEVICE_TRY_PAUSE); - ++num_pending; - } - } - - return num_pending; -} diff --git a/src/grp-login/liblogind-core/logind-session-device.h b/src/grp-login/liblogind-core/logind-session-device.h deleted file mode 100644 index 4a22decb1d..0000000000 --- a/src/grp-login/liblogind-core/logind-session-device.h +++ /dev/null @@ -1,54 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2013 David Herrmann - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "basic/list.h" - -typedef enum DeviceType DeviceType; -typedef struct SessionDevice SessionDevice; - -#include "logind.h" - -enum DeviceType { - DEVICE_TYPE_UNKNOWN, - DEVICE_TYPE_DRM, - DEVICE_TYPE_EVDEV, -}; - -struct SessionDevice { - Session *session; - Device *device; - - dev_t dev; - char *node; - int fd; - bool active; - DeviceType type; - - LIST_FIELDS(struct SessionDevice, sd_by_device); -}; - -int session_device_new(Session *s, dev_t dev, SessionDevice **out); -void session_device_free(SessionDevice *sd); -void session_device_complete_pause(SessionDevice *sd); - -void session_device_resume_all(Session *s); -void session_device_pause_all(Session *s); -unsigned int session_device_try_pause_all(Session *s); diff --git a/src/grp-login/liblogind-core/logind-session.c b/src/grp-login/liblogind-core/logind-session.c deleted file mode 100644 index 928f4a6314..0000000000 --- a/src/grp-login/liblogind-core/logind-session.c +++ /dev/null @@ -1,1273 +0,0 @@ -/*** - 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 Lesser General Public License as published by - the Free Software Foundation; either version 2.1 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include - -#include -#include - -#include - -#include "basic/alloc-util.h" -#include "basic/audit-util.h" -#include "basic/escape.h" -#include "basic/fd-util.h" -#include "basic/fileio.h" -#include "basic/formats-util.h" -#include "basic/io-util.h" -#include "basic/mkdir.h" -#include "basic/parse-util.h" -#include "basic/path-util.h" -#include "basic/string-table.h" -#include "basic/terminal-util.h" -#include "basic/user-util.h" -#include "basic/util.h" -#include "sd-bus/bus-error.h" -#include "shared/bus-util.h" - -#include "logind-session.h" - -#define RELEASE_USEC (20*USEC_PER_SEC) - -static void session_remove_fifo(Session *s); - -Session* session_new(Manager *m, const char *id) { - Session *s; - - assert(m); - assert(id); - assert(session_id_valid(id)); - - s = new0(Session, 1); - if (!s) - return NULL; - - s->state_file = strappend("/run/systemd/sessions/", id); - if (!s->state_file) { - free(s); - return NULL; - } - - s->devices = hashmap_new(&devt_hash_ops); - if (!s->devices) { - free(s->state_file); - free(s); - return NULL; - } - - s->id = basename(s->state_file); - - if (hashmap_put(m->sessions, s->id, s) < 0) { - hashmap_free(s->devices); - free(s->state_file); - free(s); - return NULL; - } - - s->manager = m; - s->fifo_fd = -1; - s->vtfd = -1; - - return s; -} - -void session_free(Session *s) { - SessionDevice *sd; - - assert(s); - - if (s->in_gc_queue) - LIST_REMOVE(gc_queue, s->manager->session_gc_queue, s); - - s->timer_event_source = sd_event_source_unref(s->timer_event_source); - - session_remove_fifo(s); - - session_drop_controller(s); - - while ((sd = hashmap_first(s->devices))) - session_device_free(sd); - - hashmap_free(s->devices); - - if (s->user) { - LIST_REMOVE(sessions_by_user, s->user->sessions, s); - - if (s->user->display == s) - s->user->display = NULL; - } - - if (s->seat) { - if (s->seat->active == s) - s->seat->active = NULL; - if (s->seat->pending_switch == s) - s->seat->pending_switch = NULL; - - seat_evict_position(s->seat, s); - LIST_REMOVE(sessions_by_seat, s->seat->sessions, s); - } - - if (s->scope) { - hashmap_remove(s->manager->session_units, s->scope); - free(s->scope); - } - - free(s->scope_job); - - sd_bus_message_unref(s->create_message); - - free(s->tty); - free(s->display); - free(s->remote_host); - free(s->remote_user); - free(s->service); - free(s->desktop); - - hashmap_remove(s->manager->sessions, s->id); - - free(s->state_file); - free(s); -} - -void session_set_user(Session *s, User *u) { - assert(s); - assert(!s->user); - - s->user = u; - LIST_PREPEND(sessions_by_user, u->sessions, s); -} - -int session_save(Session *s) { - _cleanup_free_ char *temp_path = NULL; - _cleanup_fclose_ FILE *f = NULL; - int r = 0; - - assert(s); - - if (!s->user) - return -ESTALE; - - if (!s->started) - return 0; - - r = mkdir_safe_label("/run/systemd/sessions", 0755, 0, 0); - if (r < 0) - goto fail; - - r = fopen_temporary(s->state_file, &f, &temp_path); - if (r < 0) - goto fail; - - assert(s->user); - - fchmod(fileno(f), 0644); - - fprintf(f, - "# This is private data. Do not parse.\n" - "UID="UID_FMT"\n" - "USER=%s\n" - "ACTIVE=%i\n" - "STATE=%s\n" - "REMOTE=%i\n", - s->user->uid, - s->user->name, - session_is_active(s), - session_state_to_string(session_get_state(s)), - s->remote); - - if (s->type >= 0) - fprintf(f, "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->scope) - fprintf(f, "SCOPE=%s\n", s->scope); - if (s->scope_job) - fprintf(f, "SCOPE_JOB=%s\n", s->scope_job); - - if (s->fifo_path) - fprintf(f, "FIFO=%s\n", s->fifo_path); - - if (s->seat) - fprintf(f, "SEAT=%s\n", s->seat->id); - - if (s->tty) - fprintf(f, "TTY=%s\n", s->tty); - - if (s->display) - fprintf(f, "DISPLAY=%s\n", s->display); - - if (s->remote_host) { - _cleanup_free_ char *escaped; - - escaped = cescape(s->remote_host); - if (!escaped) { - r = -ENOMEM; - goto fail; - } - - fprintf(f, "REMOTE_HOST=%s\n", escaped); - } - - if (s->remote_user) { - _cleanup_free_ char *escaped; - - escaped = cescape(s->remote_user); - if (!escaped) { - r = -ENOMEM; - goto fail; - } - - fprintf(f, "REMOTE_USER=%s\n", escaped); - } - - if (s->service) { - _cleanup_free_ char *escaped; - - escaped = cescape(s->service); - if (!escaped) { - r = -ENOMEM; - goto fail; - } - - fprintf(f, "SERVICE=%s\n", escaped); - } - - if (s->desktop) { - _cleanup_free_ char *escaped; - - - escaped = cescape(s->desktop); - if (!escaped) { - r = -ENOMEM; - goto fail; - } - - fprintf(f, "DESKTOP=%s\n", escaped); - } - - if (s->seat && seat_has_vts(s->seat)) - fprintf(f, "VTNR=%u\n", s->vtnr); - - if (!s->vtnr) - fprintf(f, "POSITION=%u\n", s->position); - - if (s->leader > 0) - fprintf(f, "LEADER="PID_FMT"\n", s->leader); - - if (s->audit_id > 0) - fprintf(f, "AUDIT=%"PRIu32"\n", s->audit_id); - - if (dual_timestamp_is_set(&s->timestamp)) - fprintf(f, - "REALTIME="USEC_FMT"\n" - "MONOTONIC="USEC_FMT"\n", - s->timestamp.realtime, - s->timestamp.monotonic); - - if (s->controller) - fprintf(f, "CONTROLLER=%s\n", s->controller); - - r = fflush_and_check(f); - if (r < 0) - goto fail; - - if (rename(temp_path, s->state_file) < 0) { - r = -errno; - goto fail; - } - - return 0; - -fail: - (void) unlink(s->state_file); - - if (temp_path) - (void) unlink(temp_path); - - return log_error_errno(r, "Failed to save session data %s: %m", s->state_file); -} - - -int session_load(Session *s) { - _cleanup_free_ char *remote = NULL, - *seat = NULL, - *vtnr = NULL, - *state = NULL, - *position = NULL, - *leader = NULL, - *type = NULL, - *class = NULL, - *uid = NULL, - *realtime = NULL, - *monotonic = NULL, - *controller = NULL; - - int k, r; - - assert(s); - - r = parse_env_file(s->state_file, NEWLINE, - "REMOTE", &remote, - "SCOPE", &s->scope, - "SCOPE_JOB", &s->scope_job, - "FIFO", &s->fifo_path, - "SEAT", &seat, - "TTY", &s->tty, - "DISPLAY", &s->display, - "REMOTE_HOST", &s->remote_host, - "REMOTE_USER", &s->remote_user, - "SERVICE", &s->service, - "DESKTOP", &s->desktop, - "VTNR", &vtnr, - "STATE", &state, - "POSITION", &position, - "LEADER", &leader, - "TYPE", &type, - "CLASS", &class, - "UID", &uid, - "REALTIME", &realtime, - "MONOTONIC", &monotonic, - "CONTROLLER", &controller, - NULL); - - if (r < 0) - return log_error_errno(r, "Failed to read %s: %m", s->state_file); - - if (!s->user) { - uid_t u; - User *user; - - if (!uid) { - log_error("UID not specified for session %s", s->id); - return -ENOENT; - } - - r = parse_uid(uid, &u); - if (r < 0) { - log_error("Failed to parse UID value %s for session %s.", uid, s->id); - return r; - } - - user = hashmap_get(s->manager->users, UID_TO_PTR(u)); - if (!user) { - log_error("User of session %s not known.", s->id); - return -ENOENT; - } - - session_set_user(s, user); - } - - if (remote) { - k = parse_boolean(remote); - if (k >= 0) - s->remote = k; - } - - if (vtnr) - safe_atou(vtnr, &s->vtnr); - - if (seat && !s->seat) { - Seat *o; - - o = hashmap_get(s->manager->seats, seat); - if (o) - r = seat_attach_session(o, s); - if (!o || r < 0) - log_error("Cannot attach session %s to seat %s", s->id, seat); - } - - if (!s->seat || !seat_has_vts(s->seat)) - s->vtnr = 0; - - if (position && s->seat) { - unsigned int npos; - - safe_atou(position, &npos); - seat_claim_position(s->seat, s, npos); - } - - if (leader) { - k = parse_pid(leader, &s->leader); - if (k >= 0) - audit_session_from_pid(s->leader, &s->audit_id); - } - - if (type) { - SessionType t; - - t = session_type_from_string(type); - if (t >= 0) - s->type = t; - } - - if (class) { - SessionClass c; - - c = session_class_from_string(class); - if (c >= 0) - s->class = c; - } - - if (state && streq(state, "closing")) - s->stopping = true; - - if (s->fifo_path) { - int fd; - - /* If we open an unopened pipe for reading we will not - get an EOF. to trigger an EOF we hence open it for - writing, but close it right away which then will - trigger the EOF. This will happen immediately if no - other process has the FIFO open for writing, i. e. - when the session died before logind (re)started. */ - - fd = session_create_fifo(s); - safe_close(fd); - } - - if (realtime) - timestamp_deserialize(realtime, &s->timestamp.realtime); - if (monotonic) - timestamp_deserialize(monotonic, &s->timestamp.monotonic); - - if (controller) { - if (bus_name_has_owner(s->manager->bus, controller, NULL) > 0) - session_set_controller(s, controller, false); - else - session_restore_vt(s); - } - - return r; -} - -int session_activate(Session *s) { - unsigned int num_pending; - - assert(s); - assert(s->user); - - if (!s->seat) - return -EOPNOTSUPP; - - if (s->seat->active == s) - return 0; - - /* on seats with VTs, we let VTs manage session-switching */ - if (seat_has_vts(s->seat)) { - if (!s->vtnr) - return -EOPNOTSUPP; - - return chvt(s->vtnr); - } - - /* On seats without VTs, we implement session-switching in logind. We - * try to pause all session-devices and wait until the session - * controller acknowledged them. Once all devices are asleep, we simply - * switch the active session and be done. - * We save the session we want to switch to in seat->pending_switch and - * seat_complete_switch() will perform the final switch. */ - - s->seat->pending_switch = s; - - /* if no devices are running, immediately perform the session switch */ - num_pending = session_device_try_pause_all(s); - if (!num_pending) - seat_complete_switch(s->seat); - - return 0; -} - -static int session_start_scope(Session *s) { - int r; - - assert(s); - assert(s->user); - - if (!s->scope) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - char *scope, *job = NULL; - const char *description; - - scope = strjoin("session-", s->id, ".scope", NULL); - if (!scope) - return log_oom(); - - description = strjoina("Session ", s->id, " of user ", s->user->name); - - r = manager_start_scope( - s->manager, - scope, - s->leader, - s->user->slice, - description, - "systemd-logind.service", - "systemd-user-sessions.service", - (uint64_t) -1, /* disable TasksMax= for the scope, rely on the slice setting for it */ - &error, - &job); - if (r < 0) { - log_error_errno(r, "Failed to start session scope %s: %s", scope, bus_error_message(&error, r)); - free(scope); - return r; - } else { - s->scope = scope; - - free(s->scope_job); - s->scope_job = job; - } - } - - if (s->scope) - (void) hashmap_put(s->manager->session_units, s->scope, s); - - return 0; -} - -int session_start(Session *s) { - int r; - - assert(s); - - if (!s->user) - return -ESTALE; - - if (s->started) - return 0; - - r = user_start(s->user); - if (r < 0) - return r; - - /* Create cgroup */ - r = session_start_scope(s); - if (r < 0) - return r; - - log_struct(s->class == SESSION_BACKGROUND ? LOG_DEBUG : LOG_INFO, - LOG_MESSAGE_ID(SD_MESSAGE_SESSION_START), - "SESSION_ID=%s", s->id, - "USER_ID=%s", s->user->name, - "LEADER="PID_FMT, s->leader, - LOG_MESSAGE("New session %s of user %s.", s->id, s->user->name), - NULL); - - if (!dual_timestamp_is_set(&s->timestamp)) - dual_timestamp_get(&s->timestamp); - - if (s->seat) - seat_read_active_vt(s->seat); - - s->started = true; - - user_elect_display(s->user); - - /* Save data */ - session_save(s); - user_save(s->user); - if (s->seat) - seat_save(s->seat); - - /* Send signals */ - session_send_signal(s, true); - user_send_changed(s->user, "Sessions", "Display", NULL); - if (s->seat) { - if (s->seat->active == s) - seat_send_changed(s->seat, "Sessions", "ActiveSession", NULL); - else - seat_send_changed(s->seat, "Sessions", NULL); - } - - return 0; -} - -static int session_stop_scope(Session *s, bool force) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - char *job = NULL; - int r; - - assert(s); - - if (!s->scope) - return 0; - - if (force || manager_shall_kill(s->manager, s->user->name)) { - r = manager_stop_unit(s->manager, s->scope, &error, &job); - if (r < 0) { - log_error("Failed to stop session scope: %s", bus_error_message(&error, r)); - return r; - } - - free(s->scope_job); - s->scope_job = job; - } else { - r = manager_abandon_scope(s->manager, s->scope, &error); - if (r < 0) { - log_error("Failed to abandon session scope: %s", bus_error_message(&error, r)); - return r; - } - } - - return 0; -} - -int session_stop(Session *s, bool force) { - int r; - - assert(s); - - if (!s->user) - return -ESTALE; - - s->timer_event_source = sd_event_source_unref(s->timer_event_source); - - if (s->seat) - seat_evict_position(s->seat, s); - - /* We are going down, don't care about FIFOs anymore */ - session_remove_fifo(s); - - /* Kill cgroup */ - r = session_stop_scope(s, force); - - s->stopping = true; - - user_elect_display(s->user); - - session_save(s); - user_save(s->user); - - return r; -} - -int session_finalize(Session *s) { - SessionDevice *sd; - - assert(s); - - if (!s->user) - return -ESTALE; - - if (s->started) - log_struct(s->class == SESSION_BACKGROUND ? LOG_DEBUG : LOG_INFO, - LOG_MESSAGE_ID(SD_MESSAGE_SESSION_STOP), - "SESSION_ID=%s", s->id, - "USER_ID=%s", s->user->name, - "LEADER="PID_FMT, s->leader, - LOG_MESSAGE("Removed session %s.", s->id), - NULL); - - s->timer_event_source = sd_event_source_unref(s->timer_event_source); - - if (s->seat) - seat_evict_position(s->seat, s); - - /* Kill session devices */ - while ((sd = hashmap_first(s->devices))) - session_device_free(sd); - - (void) unlink(s->state_file); - session_add_to_gc_queue(s); - user_add_to_gc_queue(s->user); - - if (s->started) { - session_send_signal(s, false); - s->started = false; - } - - if (s->seat) { - if (s->seat->active == s) - seat_set_active(s->seat, NULL); - - seat_save(s->seat); - seat_send_changed(s->seat, "Sessions", NULL); - } - - user_save(s->user); - user_send_changed(s->user, "Sessions", "Display", NULL); - - return 0; -} - -static int release_timeout_callback(sd_event_source *es, uint64_t usec, void *userdata) { - Session *s = userdata; - - assert(es); - assert(s); - - session_stop(s, false); - return 0; -} - -int session_release(Session *s) { - assert(s); - - if (!s->started || s->stopping) - return 0; - - if (s->timer_event_source) - return 0; - - return sd_event_add_time(s->manager->event, - &s->timer_event_source, - CLOCK_MONOTONIC, - now(CLOCK_MONOTONIC) + RELEASE_USEC, 0, - release_timeout_callback, s); -} - -bool session_is_active(Session *s) { - assert(s); - - if (!s->seat) - return true; - - return s->seat->active == s; -} - -static int get_tty_atime(const char *tty, usec_t *atime) { - _cleanup_free_ char *p = NULL; - struct stat st; - - assert(tty); - assert(atime); - - if (!path_is_absolute(tty)) { - p = strappend("/dev/", tty); - if (!p) - return -ENOMEM; - - tty = p; - } else if (!path_startswith(tty, "/dev/")) - return -ENOENT; - - if (lstat(tty, &st) < 0) - return -errno; - - *atime = timespec_load(&st.st_atim); - return 0; -} - -static int get_process_ctty_atime(pid_t pid, usec_t *atime) { - _cleanup_free_ char *p = NULL; - int r; - - assert(pid > 0); - assert(atime); - - r = get_ctty(pid, NULL, &p); - if (r < 0) - return r; - - return get_tty_atime(p, atime); -} - -int session_get_idle_hint(Session *s, dual_timestamp *t) { - usec_t atime = 0, n; - int r; - - assert(s); - - /* Explicit idle hint is set */ - if (s->idle_hint) { - if (t) - *t = s->idle_hint_timestamp; - - return s->idle_hint; - } - - /* Graphical sessions should really implement a real - * idle hint logic */ - if (SESSION_TYPE_IS_GRAPHICAL(s->type)) - goto dont_know; - - /* For sessions with an explicitly configured tty, let's check - * its atime */ - if (s->tty) { - r = get_tty_atime(s->tty, &atime); - if (r >= 0) - goto found_atime; - } - - /* For sessions with a leader but no explicitly configured - * tty, let's check the controlling tty of the leader */ - if (s->leader > 0) { - r = get_process_ctty_atime(s->leader, &atime); - if (r >= 0) - goto found_atime; - } - -dont_know: - if (t) - *t = s->idle_hint_timestamp; - - return 0; - -found_atime: - if (t) - dual_timestamp_from_realtime(t, atime); - - n = now(CLOCK_REALTIME); - - if (s->manager->idle_action_usec <= 0) - return 0; - - return atime + s->manager->idle_action_usec <= n; -} - -void session_set_idle_hint(Session *s, bool b) { - assert(s); - - if (s->idle_hint == b) - return; - - s->idle_hint = b; - dual_timestamp_get(&s->idle_hint_timestamp); - - session_send_changed(s, "IdleHint", "IdleSinceHint", "IdleSinceHintMonotonic", NULL); - - if (s->seat) - seat_send_changed(s->seat, "IdleHint", "IdleSinceHint", "IdleSinceHintMonotonic", NULL); - - user_send_changed(s->user, "IdleHint", "IdleSinceHint", "IdleSinceHintMonotonic", NULL); - manager_send_changed(s->manager, "IdleHint", "IdleSinceHint", "IdleSinceHintMonotonic", NULL); -} - -int session_get_locked_hint(Session *s) { - assert(s); - - return s->locked_hint; -} - -void session_set_locked_hint(Session *s, bool b) { - assert(s); - - if (s->locked_hint == b) - return; - - s->locked_hint = b; - - session_send_changed(s, "LockedHint", NULL); -} - -static int session_dispatch_fifo(sd_event_source *es, int fd, uint32_t revents, void *userdata) { - Session *s = userdata; - - assert(s); - assert(s->fifo_fd == fd); - - /* EOF on the FIFO means the session died abnormally. */ - - session_remove_fifo(s); - session_stop(s, false); - - return 1; -} - -int session_create_fifo(Session *s) { - int r; - - assert(s); - - /* Create FIFO */ - if (!s->fifo_path) { - r = mkdir_safe_label("/run/systemd/sessions", 0755, 0, 0); - if (r < 0) - return r; - - if (asprintf(&s->fifo_path, "/run/systemd/sessions/%s.ref", s->id) < 0) - return -ENOMEM; - - if (mkfifo(s->fifo_path, 0600) < 0 && errno != EEXIST) - return -errno; - } - - /* Open reading side */ - if (s->fifo_fd < 0) { - s->fifo_fd = open(s->fifo_path, O_RDONLY|O_CLOEXEC|O_NDELAY); - if (s->fifo_fd < 0) - return -errno; - - } - - if (!s->fifo_event_source) { - r = sd_event_add_io(s->manager->event, &s->fifo_event_source, s->fifo_fd, 0, session_dispatch_fifo, s); - if (r < 0) - return r; - - /* Let's make sure we noticed dead sessions before we process new bus requests (which might create new - * sessions). */ - r = sd_event_source_set_priority(s->fifo_event_source, SD_EVENT_PRIORITY_NORMAL-10); - if (r < 0) - return r; - } - - /* Open writing side */ - r = open(s->fifo_path, O_WRONLY|O_CLOEXEC|O_NDELAY); - if (r < 0) - return -errno; - - return r; -} - -static void session_remove_fifo(Session *s) { - assert(s); - - s->fifo_event_source = sd_event_source_unref(s->fifo_event_source); - s->fifo_fd = safe_close(s->fifo_fd); - - if (s->fifo_path) { - unlink(s->fifo_path); - s->fifo_path = mfree(s->fifo_path); - } -} - -bool session_check_gc(Session *s, bool drop_not_started) { - assert(s); - - if (drop_not_started && !s->started) - return false; - - if (!s->user) - return false; - - if (s->fifo_fd >= 0) { - if (pipe_eof(s->fifo_fd) <= 0) - return true; - } - - if (s->scope_job && manager_job_is_active(s->manager, s->scope_job)) - return true; - - if (s->scope && manager_unit_is_active(s->manager, s->scope)) - return true; - - return false; -} - -void session_add_to_gc_queue(Session *s) { - assert(s); - - if (s->in_gc_queue) - return; - - LIST_PREPEND(gc_queue, s->manager->session_gc_queue, s); - s->in_gc_queue = true; -} - -SessionState session_get_state(Session *s) { - assert(s); - - /* always check closing first */ - if (s->stopping || s->timer_event_source) - return SESSION_CLOSING; - - if (s->scope_job || s->fifo_fd < 0) - return SESSION_OPENING; - - if (session_is_active(s)) - return SESSION_ACTIVE; - - return SESSION_ONLINE; -} - -int session_kill(Session *s, KillWho who, int signo) { - assert(s); - - if (!s->scope) - return -ESRCH; - - return manager_kill_unit(s->manager, s->scope, who, signo, NULL); -} - -static int session_open_vt(Session *s) { - char path[sizeof("/dev/tty") + DECIMAL_STR_MAX(s->vtnr)]; - - if (s->vtnr < 1) - return -ENODEV; - - if (s->vtfd >= 0) - return s->vtfd; - - sprintf(path, "/dev/tty%u", s->vtnr); - s->vtfd = open_terminal(path, O_RDWR | O_CLOEXEC | O_NONBLOCK | O_NOCTTY); - if (s->vtfd < 0) - return log_error_errno(s->vtfd, "cannot open VT %s of session %s: %m", path, s->id); - - return s->vtfd; -} - -int session_prepare_vt(Session *s) { - int vt, r; - struct vt_mode mode = { 0 }; - - if (s->vtnr < 1) - return 0; - - vt = session_open_vt(s); - if (vt < 0) - return vt; - - r = fchown(vt, s->user->uid, -1); - if (r < 0) { - r = log_error_errno(errno, - "Cannot change owner of /dev/tty%u: %m", - s->vtnr); - goto error; - } - - r = ioctl(vt, KDSKBMODE, K_OFF); - if (r < 0) { - r = log_error_errno(errno, - "Cannot set K_OFF on /dev/tty%u: %m", - s->vtnr); - goto error; - } - - r = ioctl(vt, KDSETMODE, KD_GRAPHICS); - if (r < 0) { - r = log_error_errno(errno, - "Cannot set KD_GRAPHICS on /dev/tty%u: %m", - s->vtnr); - goto error; - } - - /* Oh, thanks to the VT layer, VT_AUTO does not work with KD_GRAPHICS. - * So we need a dummy handler here which just acknowledges *all* VT - * switch requests. */ - mode.mode = VT_PROCESS; - mode.relsig = SIGRTMIN; - mode.acqsig = SIGRTMIN + 1; - r = ioctl(vt, VT_SETMODE, &mode); - if (r < 0) { - r = log_error_errno(errno, - "Cannot set VT_PROCESS on /dev/tty%u: %m", - s->vtnr); - goto error; - } - - return 0; - -error: - session_restore_vt(s); - return r; -} - -void session_restore_vt(Session *s) { - - static const struct vt_mode mode = { - .mode = VT_AUTO, - }; - - _cleanup_free_ char *utf8 = NULL; - int vt, kb, old_fd; - - /* We need to get a fresh handle to the virtual terminal, - * since the old file-descriptor is potentially in a hung-up - * state after the controlling process exited; we do a - * little dance to avoid having the terminal be available - * for reuse before we've cleaned it up. - */ - old_fd = s->vtfd; - s->vtfd = -1; - - vt = session_open_vt(s); - safe_close(old_fd); - - if (vt < 0) - return; - - (void) ioctl(vt, KDSETMODE, KD_TEXT); - - if (read_one_line_file("/sys/module/vt/parameters/default_utf8", &utf8) >= 0 && *utf8 == '1') - kb = K_UNICODE; - else - kb = K_XLATE; - - (void) ioctl(vt, KDSKBMODE, kb); - - (void) ioctl(vt, VT_SETMODE, &mode); - (void) fchown(vt, 0, (gid_t) -1); - - s->vtfd = safe_close(s->vtfd); -} - -void session_leave_vt(Session *s) { - int r; - - assert(s); - - /* This is called whenever we get a VT-switch signal from the kernel. - * We acknowledge all of them unconditionally. Note that session are - * free to overwrite those handlers and we only register them for - * sessions with controllers. Legacy sessions are not affected. - * However, if we switch from a non-legacy to a legacy session, we must - * make sure to pause all device before acknowledging the switch. We - * process the real switch only after we are notified via sysfs, so the - * legacy session might have already started using the devices. If we - * don't pause the devices before the switch, we might confuse the - * session we switch to. */ - - if (s->vtfd < 0) - return; - - session_device_pause_all(s); - r = ioctl(s->vtfd, VT_RELDISP, 1); - if (r < 0) - log_debug_errno(errno, "Cannot release VT of session %s: %m", s->id); -} - -bool session_is_controller(Session *s, const char *sender) { - assert(s); - - return streq_ptr(s->controller, sender); -} - -static void session_release_controller(Session *s, bool notify) { - _cleanup_free_ char *name = NULL; - SessionDevice *sd; - - if (!s->controller) - return; - - name = s->controller; - - /* By resetting the controller before releasing the devices, we won't - * send notification signals. This avoids sending useless notifications - * if the controller is released on disconnects. */ - if (!notify) - s->controller = NULL; - - while ((sd = hashmap_first(s->devices))) - session_device_free(sd); - - s->controller = NULL; - s->track = sd_bus_track_unref(s->track); -} - -static int on_bus_track(sd_bus_track *track, void *userdata) { - Session *s = userdata; - - assert(track); - assert(s); - - session_drop_controller(s); - - return 0; -} - -int session_set_controller(Session *s, const char *sender, bool force) { - _cleanup_free_ char *name = NULL; - int r; - - assert(s); - assert(sender); - - if (session_is_controller(s, sender)) - return 0; - if (s->controller && !force) - return -EBUSY; - - name = strdup(sender); - if (!name) - return -ENOMEM; - - s->track = sd_bus_track_unref(s->track); - r = sd_bus_track_new(s->manager->bus, &s->track, on_bus_track, s); - if (r < 0) - return r; - - r = sd_bus_track_add_name(s->track, name); - if (r < 0) - return r; - - /* When setting a session controller, we forcibly mute the VT and set - * it into graphics-mode. Applications can override that by changing - * VT state after calling TakeControl(). However, this serves as a good - * default and well-behaving controllers can now ignore VTs entirely. - * Note that we reset the VT on ReleaseControl() and if the controller - * exits. - * If logind crashes/restarts, we restore the controller during restart - * or reset the VT in case it crashed/exited, too. */ - r = session_prepare_vt(s); - if (r < 0) { - s->track = sd_bus_track_unref(s->track); - return r; - } - - session_release_controller(s, true); - s->controller = name; - name = NULL; - session_save(s); - - return 0; -} - -void session_drop_controller(Session *s) { - assert(s); - - if (!s->controller) - return; - - s->track = sd_bus_track_unref(s->track); - session_release_controller(s, false); - session_save(s); - session_restore_vt(s); -} - -static const char* const session_state_table[_SESSION_STATE_MAX] = { - [SESSION_OPENING] = "opening", - [SESSION_ONLINE] = "online", - [SESSION_ACTIVE] = "active", - [SESSION_CLOSING] = "closing" -}; - -DEFINE_STRING_TABLE_LOOKUP(session_state, SessionState); - -static const char* const session_type_table[_SESSION_TYPE_MAX] = { - [SESSION_UNSPECIFIED] = "unspecified", - [SESSION_TTY] = "tty", - [SESSION_X11] = "x11", - [SESSION_WAYLAND] = "wayland", - [SESSION_MIR] = "mir", - [SESSION_WEB] = "web", -}; - -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", - [SESSION_BACKGROUND] = "background" -}; - -DEFINE_STRING_TABLE_LOOKUP(session_class, SessionClass); - -static const char* const kill_who_table[_KILL_WHO_MAX] = { - [KILL_LEADER] = "leader", - [KILL_ALL] = "all" -}; - -DEFINE_STRING_TABLE_LOOKUP(kill_who, KillWho); diff --git a/src/grp-login/liblogind-core/logind-session.h b/src/grp-login/liblogind-core/logind-session.h deleted file mode 100644 index 23ff6576c9..0000000000 --- a/src/grp-login/liblogind-core/logind-session.h +++ /dev/null @@ -1,186 +0,0 @@ -#pragma once - -/*** - 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 Lesser General Public License as published by - the Free Software Foundation; either version 2.1 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -typedef struct Session Session; -typedef enum KillWho KillWho; - -#include "basic/list.h" -#include "basic/login-util.h" - -#include "logind-user.h" - -typedef enum SessionState { - SESSION_OPENING, /* Session scope is being created */ - SESSION_ONLINE, /* Logged in */ - SESSION_ACTIVE, /* Logged in and in the fg */ - SESSION_CLOSING, /* Logged out, but scope is still there */ - _SESSION_STATE_MAX, - _SESSION_STATE_INVALID = -1 -} SessionState; - -typedef enum SessionClass { - SESSION_USER, - SESSION_GREETER, - SESSION_LOCK_SCREEN, - SESSION_BACKGROUND, - _SESSION_CLASS_MAX, - _SESSION_CLASS_INVALID = -1 -} SessionClass; - -typedef enum SessionType { - SESSION_UNSPECIFIED, - SESSION_TTY, - SESSION_X11, - SESSION_WAYLAND, - SESSION_MIR, - SESSION_WEB, - _SESSION_TYPE_MAX, - _SESSION_TYPE_INVALID = -1 -} SessionType; - -#define SESSION_TYPE_IS_GRAPHICAL(type) IN_SET(type, SESSION_X11, SESSION_WAYLAND, SESSION_MIR) - -enum KillWho { - KILL_LEADER, - KILL_ALL, - _KILL_WHO_MAX, - _KILL_WHO_INVALID = -1 -}; - -struct Session { - Manager *manager; - - const char *id; - unsigned int position; - SessionType type; - SessionClass class; - - char *state_file; - - User *user; - - dual_timestamp timestamp; - - char *tty; - char *display; - - bool remote; - char *remote_user; - char *remote_host; - char *service; - char *desktop; - - char *scope; - char *scope_job; - - Seat *seat; - unsigned int vtnr; - int vtfd; - - pid_t leader; - uint32_t audit_id; - - int fifo_fd; - char *fifo_path; - - sd_event_source *fifo_event_source; - - bool idle_hint; - dual_timestamp idle_hint_timestamp; - - bool locked_hint; - - bool in_gc_queue:1; - bool started:1; - bool stopping:1; - - sd_bus_message *create_message; - - sd_event_source *timer_event_source; - - char *controller; - Hashmap *devices; - sd_bus_track *track; - - LIST_FIELDS(Session, sessions_by_user); - LIST_FIELDS(Session, sessions_by_seat); - - LIST_FIELDS(Session, gc_queue); -}; - -Session *session_new(Manager *m, const char *id); -void session_free(Session *s); -void session_set_user(Session *s, User *u); -bool session_check_gc(Session *s, bool drop_not_started); -void session_add_to_gc_queue(Session *s); -int session_activate(Session *s); -bool session_is_active(Session *s); -int session_get_idle_hint(Session *s, dual_timestamp *t); -void session_set_idle_hint(Session *s, bool b); -int session_get_locked_hint(Session *s); -void session_set_locked_hint(Session *s, bool b); -int session_create_fifo(Session *s); -int session_start(Session *s); -int session_stop(Session *s, bool force); -int session_finalize(Session *s); -int session_release(Session *s); -int session_save(Session *s); -int session_load(Session *s); -int session_kill(Session *s, KillWho who, int signo); - -SessionState session_get_state(Session *u); - -extern const sd_bus_vtable session_vtable[]; -int session_node_enumerator(sd_bus *bus, const char *path,void *userdata, char ***nodes, sd_bus_error *error); -int session_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error); -char *session_bus_path(Session *s); - -int session_send_signal(Session *s, bool new_session); -int session_send_changed(Session *s, const char *properties, ...) _sentinel_; -int session_send_lock(Session *s, bool lock); -int session_send_lock_all(Manager *m, bool lock); - -int session_send_create_reply(Session *s, sd_bus_error *error); - -const char* session_state_to_string(SessionState t) _const_; -SessionState session_state_from_string(const char *s) _pure_; - -const char* session_type_to_string(SessionType t) _const_; -SessionType session_type_from_string(const char *s) _pure_; - -const char* session_class_to_string(SessionClass t) _const_; -SessionClass session_class_from_string(const char *s) _pure_; - -const char *kill_who_to_string(KillWho k) _const_; -KillWho kill_who_from_string(const char *s) _pure_; - -int session_prepare_vt(Session *s); -void session_restore_vt(Session *s); -void session_leave_vt(Session *s); - -bool session_is_controller(Session *s, const char *sender); -int session_set_controller(Session *s, const char *sender, bool force); -void session_drop_controller(Session *s); - -int bus_session_method_activate(sd_bus_message *message, void *userdata, sd_bus_error *error); -int bus_session_method_lock(sd_bus_message *message, void *userdata, sd_bus_error *error); -int bus_session_method_terminate(sd_bus_message *message, void *userdata, sd_bus_error *error); -int bus_session_method_kill(sd_bus_message *message, void *userdata, sd_bus_error *error); diff --git a/src/grp-login/liblogind-core/logind-user-dbus.c b/src/grp-login/liblogind-core/logind-user-dbus.c deleted file mode 100644 index d82ced605e..0000000000 --- a/src/grp-login/liblogind-core/logind-user-dbus.c +++ /dev/null @@ -1,399 +0,0 @@ -/*** - 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 Lesser General Public License as published by - the Free Software Foundation; either version 2.1 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include - -#include "basic/alloc-util.h" -#include "basic/formats-util.h" -#include "basic/signal-util.h" -#include "basic/strv.h" -#include "basic/user-util.h" -#include "logind.h" -#include "shared/bus-util.h" - -#include "logind-user.h" - -static int property_get_display( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - _cleanup_free_ char *p = NULL; - User *u = userdata; - - assert(bus); - assert(reply); - assert(u); - - p = u->display ? session_bus_path(u->display) : strdup("/"); - if (!p) - return -ENOMEM; - - return sd_bus_message_append(reply, "(so)", u->display ? u->display->id : "", p); -} - -static int property_get_state( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - User *u = userdata; - - assert(bus); - assert(reply); - assert(u); - - return sd_bus_message_append(reply, "s", user_state_to_string(user_get_state(u))); -} - -static int property_get_sessions( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - User *u = userdata; - Session *session; - int r; - - assert(bus); - assert(reply); - assert(u); - - r = sd_bus_message_open_container(reply, 'a', "(so)"); - if (r < 0) - return r; - - LIST_FOREACH(sessions_by_user, session, u->sessions) { - _cleanup_free_ char *p = NULL; - - p = session_bus_path(session); - if (!p) - return -ENOMEM; - - r = sd_bus_message_append(reply, "(so)", session->id, p); - if (r < 0) - return r; - - } - - return sd_bus_message_close_container(reply); -} - -static int property_get_idle_hint( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - User *u = userdata; - - assert(bus); - assert(reply); - assert(u); - - return sd_bus_message_append(reply, "b", user_get_idle_hint(u, NULL) > 0); -} - -static int property_get_idle_since_hint( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - User *u = userdata; - dual_timestamp t = DUAL_TIMESTAMP_NULL; - uint64_t k; - - assert(bus); - assert(reply); - assert(u); - - user_get_idle_hint(u, &t); - k = streq(property, "IdleSinceHint") ? t.realtime : t.monotonic; - - return sd_bus_message_append(reply, "t", k); -} - -static int property_get_linger( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - User *u = userdata; - int r; - - assert(bus); - assert(reply); - assert(u); - - r = user_check_linger_file(u); - - return sd_bus_message_append(reply, "b", r > 0); -} - -int bus_user_method_terminate(sd_bus_message *message, void *userdata, sd_bus_error *error) { - User *u = userdata; - int r; - - assert(message); - assert(u); - - r = bus_verify_polkit_async( - message, - CAP_KILL, - "org.freedesktop.login1.manage", - NULL, - false, - u->uid, - &u->manager->polkit_registry, - error); - if (r < 0) - return r; - if (r == 0) - return 1; /* Will call us back */ - - r = user_stop(u, true); - if (r < 0) - return r; - - return sd_bus_reply_method_return(message, NULL); -} - -int bus_user_method_kill(sd_bus_message *message, void *userdata, sd_bus_error *error) { - User *u = userdata; - int32_t signo; - int r; - - assert(message); - assert(u); - - r = bus_verify_polkit_async( - message, - CAP_KILL, - "org.freedesktop.login1.manage", - NULL, - false, - u->uid, - &u->manager->polkit_registry, - error); - if (r < 0) - return r; - if (r == 0) - return 1; /* Will call us back */ - - r = sd_bus_message_read(message, "i", &signo); - if (r < 0) - return r; - - if (!SIGNAL_VALID(signo)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid signal %i", signo); - - r = user_kill(u, signo); - if (r < 0) - return r; - - return sd_bus_reply_method_return(message, NULL); -} - -const sd_bus_vtable user_vtable[] = { - SD_BUS_VTABLE_START(0), - - SD_BUS_PROPERTY("UID", "u", bus_property_get_uid, offsetof(User, uid), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("GID", "u", bus_property_get_gid, offsetof(User, gid), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Name", "s", NULL, offsetof(User, name), SD_BUS_VTABLE_PROPERTY_CONST), - BUS_PROPERTY_DUAL_TIMESTAMP("Timestamp", offsetof(User, timestamp), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("RuntimePath", "s", NULL, offsetof(User, runtime_path), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Service", "s", NULL, offsetof(User, service), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Slice", "s", NULL, offsetof(User, slice), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Display", "(so)", property_get_display, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("State", "s", property_get_state, 0, 0), - SD_BUS_PROPERTY("Sessions", "a(so)", property_get_sessions, 0, 0), - SD_BUS_PROPERTY("IdleHint", "b", property_get_idle_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("IdleSinceHint", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("IdleSinceHintMonotonic", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("Linger", "b", property_get_linger, 0, 0), - - SD_BUS_METHOD("Terminate", NULL, NULL, bus_user_method_terminate, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("Kill", "i", NULL, bus_user_method_kill, SD_BUS_VTABLE_UNPRIVILEGED), - - SD_BUS_VTABLE_END -}; - -int user_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) { - Manager *m = userdata; - uid_t uid; - User *user; - int r; - - assert(bus); - assert(path); - assert(interface); - assert(found); - assert(m); - - if (streq(path, "/org/freedesktop/login1/user/self")) { - _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; - sd_bus_message *message; - - message = sd_bus_get_current_message(bus); - if (!message) - return 0; - - r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_OWNER_UID|SD_BUS_CREDS_AUGMENT, &creds); - if (r < 0) - return r; - - r = sd_bus_creds_get_owner_uid(creds, &uid); - } else { - const char *p; - - p = startswith(path, "/org/freedesktop/login1/user/_"); - if (!p) - return 0; - - r = parse_uid(p, &uid); - } - if (r < 0) - return 0; - - user = hashmap_get(m->users, UID_TO_PTR(uid)); - if (!user) - return 0; - - *found = user; - return 1; -} - -char *user_bus_path(User *u) { - char *s; - - assert(u); - - if (asprintf(&s, "/org/freedesktop/login1/user/_"UID_FMT, u->uid) < 0) - return NULL; - - return s; -} - -int user_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) { - _cleanup_strv_free_ char **l = NULL; - sd_bus_message *message; - Manager *m = userdata; - User *user; - Iterator i; - int r; - - assert(bus); - assert(path); - assert(nodes); - - HASHMAP_FOREACH(user, m->users, i) { - char *p; - - p = user_bus_path(user); - if (!p) - return -ENOMEM; - - r = strv_consume(&l, p); - if (r < 0) - return r; - } - - message = sd_bus_get_current_message(bus); - if (message) { - _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; - uid_t uid; - - r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_OWNER_UID|SD_BUS_CREDS_AUGMENT, &creds); - if (r >= 0) { - r = sd_bus_creds_get_owner_uid(creds, &uid); - if (r >= 0) { - user = hashmap_get(m->users, UID_TO_PTR(uid)); - if (user) { - r = strv_extend(&l, "/org/freedesktop/login1/user/self"); - if (r < 0) - return r; - } - } - } - } - - *nodes = l; - l = NULL; - - return 1; -} - -int user_send_signal(User *u, bool new_user) { - _cleanup_free_ char *p = NULL; - - assert(u); - - p = user_bus_path(u); - if (!p) - return -ENOMEM; - - return sd_bus_emit_signal( - u->manager->bus, - "/org/freedesktop/login1", - "org.freedesktop.login1.Manager", - new_user ? "UserNew" : "UserRemoved", - "uo", (uint32_t) u->uid, p); -} - -int user_send_changed(User *u, const char *properties, ...) { - _cleanup_free_ char *p = NULL; - char **l; - - assert(u); - - if (!u->started) - return 0; - - p = user_bus_path(u); - if (!p) - return -ENOMEM; - - l = strv_from_stdarg_alloca(properties); - - return sd_bus_emit_properties_changed_strv(u->manager->bus, p, "org.freedesktop.login1.User", l); -} diff --git a/src/grp-login/liblogind-core/logind-user.c b/src/grp-login/liblogind-core/logind-user.c deleted file mode 100644 index 015276efc8..0000000000 --- a/src/grp-login/liblogind-core/logind-user.c +++ /dev/null @@ -1,886 +0,0 @@ -/*** - 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 Lesser General Public License as published by - the Free Software Foundation; either version 2.1 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include - -#include "basic/alloc-util.h" -#include "basic/escape.h" -#include "basic/fd-util.h" -#include "basic/fileio.h" -#include "basic/formats-util.h" -#include "basic/fs-util.h" -#include "basic/hashmap.h" -#include "basic/label.h" -#include "basic/mkdir.h" -#include "basic/mount-util.h" -#include "basic/parse-util.h" -#include "basic/path-util.h" -#include "basic/rm-rf.h" -#include "basic/smack-util.h" -#include "basic/special.h" -#include "basic/stdio-util.h" -#include "basic/string-table.h" -#include "basic/unit-name.h" -#include "basic/user-util.h" -#include "basic/util.h" -#include "sd-bus/bus-common-errors.h" -#include "sd-bus/bus-error.h" -#include "shared/bus-util.h" -#include "shared/clean-ipc.h" -#include "shared/conf-parser.h" - -#include "logind-user.h" - -int user_new(User **out, Manager *m, uid_t uid, gid_t gid, const char *name) { - _cleanup_(user_freep) User *u = NULL; - char lu[DECIMAL_STR_MAX(uid_t) + 1]; - int r; - - assert(out); - assert(m); - assert(name); - - u = new0(User, 1); - if (!u) - return -ENOMEM; - - u->manager = m; - u->uid = uid; - u->gid = gid; - xsprintf(lu, UID_FMT, uid); - - u->name = strdup(name); - if (!u->name) - return -ENOMEM; - - if (asprintf(&u->state_file, "/run/systemd/users/"UID_FMT, uid) < 0) - return -ENOMEM; - - if (asprintf(&u->runtime_path, "/run/user/"UID_FMT, uid) < 0) - return -ENOMEM; - - r = slice_build_subslice(SPECIAL_USER_SLICE, lu, &u->slice); - if (r < 0) - return r; - - r = unit_name_build("user", lu, ".service", &u->service); - if (r < 0) - return r; - - r = hashmap_put(m->users, UID_TO_PTR(uid), u); - if (r < 0) - return r; - - r = hashmap_put(m->user_units, u->slice, u); - if (r < 0) - return r; - - r = hashmap_put(m->user_units, u->service, u); - if (r < 0) - return r; - - *out = u; - u = NULL; - return 0; -} - -User *user_free(User *u) { - if (!u) - return NULL; - - if (u->in_gc_queue) - LIST_REMOVE(gc_queue, u->manager->user_gc_queue, u); - - while (u->sessions) - session_free(u->sessions); - - if (u->service) - hashmap_remove_value(u->manager->user_units, u->service, u); - - if (u->slice) - hashmap_remove_value(u->manager->user_units, u->slice, u); - - hashmap_remove_value(u->manager->users, UID_TO_PTR(u->uid), u); - - u->slice_job = mfree(u->slice_job); - u->service_job = mfree(u->service_job); - - u->service = mfree(u->service); - u->slice = mfree(u->slice); - u->runtime_path = mfree(u->runtime_path); - u->state_file = mfree(u->state_file); - u->name = mfree(u->name); - - return mfree(u); -} - -static int user_save_internal(User *u) { - _cleanup_free_ char *temp_path = NULL; - _cleanup_fclose_ FILE *f = NULL; - int r; - - assert(u); - assert(u->state_file); - - r = mkdir_safe_label("/run/systemd/users", 0755, 0, 0); - if (r < 0) - goto fail; - - r = fopen_temporary(u->state_file, &f, &temp_path); - if (r < 0) - goto fail; - - fchmod(fileno(f), 0644); - - fprintf(f, - "# This is private data. Do not parse.\n" - "NAME=%s\n" - "STATE=%s\n", - u->name, - user_state_to_string(user_get_state(u))); - - /* LEGACY: no-one reads RUNTIME= anymore, drop it at some point */ - if (u->runtime_path) - fprintf(f, "RUNTIME=%s\n", u->runtime_path); - - if (u->service_job) - fprintf(f, "SERVICE_JOB=%s\n", u->service_job); - - if (u->slice_job) - fprintf(f, "SLICE_JOB=%s\n", u->slice_job); - - if (u->display) - fprintf(f, "DISPLAY=%s\n", u->display->id); - - if (dual_timestamp_is_set(&u->timestamp)) - fprintf(f, - "REALTIME="USEC_FMT"\n" - "MONOTONIC="USEC_FMT"\n", - u->timestamp.realtime, - u->timestamp.monotonic); - - if (u->sessions) { - Session *i; - bool first; - - fputs("SESSIONS=", f); - first = true; - LIST_FOREACH(sessions_by_user, i, u->sessions) { - if (first) - first = false; - else - fputc(' ', f); - - fputs(i->id, f); - } - - fputs("\nSEATS=", f); - first = true; - LIST_FOREACH(sessions_by_user, i, u->sessions) { - if (!i->seat) - continue; - - if (first) - first = false; - else - fputc(' ', f); - - fputs(i->seat->id, f); - } - - fputs("\nACTIVE_SESSIONS=", f); - first = true; - LIST_FOREACH(sessions_by_user, i, u->sessions) { - if (!session_is_active(i)) - continue; - - if (first) - first = false; - else - fputc(' ', f); - - fputs(i->id, f); - } - - fputs("\nONLINE_SESSIONS=", f); - first = true; - LIST_FOREACH(sessions_by_user, i, u->sessions) { - if (session_get_state(i) == SESSION_CLOSING) - continue; - - if (first) - first = false; - else - fputc(' ', f); - - fputs(i->id, f); - } - - fputs("\nACTIVE_SEATS=", f); - first = true; - LIST_FOREACH(sessions_by_user, i, u->sessions) { - if (!session_is_active(i) || !i->seat) - continue; - - if (first) - first = false; - else - fputc(' ', f); - - fputs(i->seat->id, f); - } - - fputs("\nONLINE_SEATS=", f); - first = true; - LIST_FOREACH(sessions_by_user, i, u->sessions) { - if (session_get_state(i) == SESSION_CLOSING || !i->seat) - continue; - - if (first) - first = false; - else - fputc(' ', f); - - fputs(i->seat->id, f); - } - fputc('\n', f); - } - - r = fflush_and_check(f); - if (r < 0) - goto fail; - - if (rename(temp_path, u->state_file) < 0) { - r = -errno; - goto fail; - } - - return 0; - -fail: - (void) unlink(u->state_file); - - if (temp_path) - (void) unlink(temp_path); - - return log_error_errno(r, "Failed to save user data %s: %m", u->state_file); -} - -int user_save(User *u) { - assert(u); - - if (!u->started) - return 0; - - return user_save_internal (u); -} - -int user_load(User *u) { - _cleanup_free_ char *display = NULL, *realtime = NULL, *monotonic = NULL; - Session *s = NULL; - int r; - - assert(u); - - r = parse_env_file(u->state_file, NEWLINE, - "SERVICE_JOB", &u->service_job, - "SLICE_JOB", &u->slice_job, - "DISPLAY", &display, - "REALTIME", &realtime, - "MONOTONIC", &monotonic, - NULL); - if (r < 0) { - if (r == -ENOENT) - return 0; - - log_error_errno(r, "Failed to read %s: %m", u->state_file); - return r; - } - - if (display) - s = hashmap_get(u->manager->sessions, display); - - if (s && s->display && display_is_local(s->display)) - u->display = s; - - if (realtime) - timestamp_deserialize(realtime, &u->timestamp.realtime); - if (monotonic) - timestamp_deserialize(monotonic, &u->timestamp.monotonic); - - return r; -} - -static int user_mkdir_runtime_path(User *u) { - int r; - - assert(u); - - r = mkdir_safe_label("/run/user", 0755, 0, 0); - if (r < 0) - return log_error_errno(r, "Failed to create /run/user: %m"); - - if (path_is_mount_point(u->runtime_path, 0) <= 0) { - _cleanup_free_ char *t = NULL; - - (void) mkdir_label(u->runtime_path, 0700); - - if (mac_smack_use()) - r = asprintf(&t, "mode=0700,smackfsroot=*,uid=" UID_FMT ",gid=" GID_FMT ",size=%zu", u->uid, u->gid, u->manager->runtime_dir_size); - else - r = asprintf(&t, "mode=0700,uid=" UID_FMT ",gid=" GID_FMT ",size=%zu", u->uid, u->gid, u->manager->runtime_dir_size); - if (r < 0) { - r = log_oom(); - goto fail; - } - - r = mount("tmpfs", u->runtime_path, "tmpfs", MS_NODEV|MS_NOSUID, t); - if (r < 0) { - if (errno != EPERM) { - r = log_error_errno(errno, "Failed to mount per-user tmpfs directory %s: %m", u->runtime_path); - goto fail; - } - - /* Lacking permissions, maybe - * CAP_SYS_ADMIN-less container? In this case, - * just use a normal directory. */ - - r = chmod_and_chown(u->runtime_path, 0700, u->uid, u->gid); - if (r < 0) { - log_error_errno(r, "Failed to change runtime directory ownership and mode: %m"); - goto fail; - } - } - - r = label_fix(u->runtime_path, false, false); - if (r < 0) - log_warning_errno(r, "Failed to fix label of '%s', ignoring: %m", u->runtime_path); - } - - return 0; - -fail: - /* Try to clean up, but ignore errors */ - (void) rmdir(u->runtime_path); - return r; -} - -static int user_start_slice(User *u) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - const char *description; - char *job; - int r; - - assert(u); - - u->slice_job = mfree(u->slice_job); - description = strjoina("User Slice of ", u->name); - - r = manager_start_slice( - u->manager, - u->slice, - description, - "systemd-logind.service", - "systemd-user-sessions.service", - u->manager->user_tasks_max, - &error, - &job); - if (r >= 0) - u->slice_job = job; - else if (!sd_bus_error_has_name(&error, BUS_ERROR_UNIT_EXISTS)) - /* we don't fail due to this, let's try to continue */ - log_error_errno(r, "Failed to start user slice %s, ignoring: %s (%s)", - u->slice, bus_error_message(&error, r), error.name); - - return 0; -} - -static int user_start_service(User *u) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - char *job; - int r; - - assert(u); - - u->service_job = mfree(u->service_job); - - r = manager_start_unit( - u->manager, - u->service, - &error, - &job); - if (r < 0) { - /* we don't fail due to this, let's try to continue */ - log_error_errno(r, "Failed to start user service, ignoring: %s", bus_error_message(&error, r)); - } else { - u->service_job = job; - } - - return 0; -} - -int user_start(User *u) { - int r; - - assert(u); - - if (u->started && !u->stopping) - return 0; - - /* - * If u->stopping is set, the user is marked for removal and the slice - * and service stop-jobs are queued. We have to clear that flag before - * queing the start-jobs again. If they succeed, the user object can be - * re-used just fine (pid1 takes care of job-ordering and proper - * restart), but if they fail, we want to force another user_stop() so - * possibly pending units are stopped. - * Note that we don't clear u->started, as we have no clue what state - * the user is in on failure here. Hence, we pretend the user is - * running so it will be properly taken down by GC. However, we clearly - * return an error from user_start() in that case, so no further - * reference to the user is taken. - */ - u->stopping = false; - - if (!u->started) { - log_debug("New user %s logged in.", u->name); - - /* Make XDG_RUNTIME_DIR */ - r = user_mkdir_runtime_path(u); - if (r < 0) - return r; - } - - /* Create cgroup */ - r = user_start_slice(u); - if (r < 0) - return r; - - /* Save the user data so far, because pam_systemd will read the - * XDG_RUNTIME_DIR out of it while starting up systemd --user. - * We need to do user_save_internal() because we have not - * "officially" started yet. */ - user_save_internal(u); - - /* Spawn user systemd */ - r = user_start_service(u); - if (r < 0) - return r; - - if (!u->started) { - if (!dual_timestamp_is_set(&u->timestamp)) - dual_timestamp_get(&u->timestamp); - user_send_signal(u, true); - u->started = true; - } - - /* Save new user data */ - user_save(u); - - return 0; -} - -static int user_stop_slice(User *u) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - char *job; - int r; - - assert(u); - - r = manager_stop_unit(u->manager, u->slice, &error, &job); - if (r < 0) { - log_error("Failed to stop user slice: %s", bus_error_message(&error, r)); - return r; - } - - free(u->slice_job); - u->slice_job = job; - - return r; -} - -static int user_stop_service(User *u) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - char *job; - int r; - - assert(u); - - r = manager_stop_unit(u->manager, u->service, &error, &job); - if (r < 0) { - log_error("Failed to stop user service: %s", bus_error_message(&error, r)); - return r; - } - - free(u->service_job); - u->service_job = job; - - return r; -} - -static int user_remove_runtime_path(User *u) { - int r; - - assert(u); - - r = rm_rf(u->runtime_path, 0); - if (r < 0) - log_error_errno(r, "Failed to remove runtime directory %s: %m", u->runtime_path); - - /* Ignore cases where the directory isn't mounted, as that's - * quite possible, if we lacked the permissions to mount - * something */ - r = umount2(u->runtime_path, MNT_DETACH); - if (r < 0 && errno != EINVAL && errno != ENOENT) - log_error_errno(errno, "Failed to unmount user runtime directory %s: %m", u->runtime_path); - - r = rm_rf(u->runtime_path, REMOVE_ROOT); - if (r < 0) - log_error_errno(r, "Failed to remove runtime directory %s: %m", u->runtime_path); - - return r; -} - -int user_stop(User *u, bool force) { - Session *s; - int r = 0, k; - assert(u); - - /* Stop jobs have already been queued */ - if (u->stopping) { - user_save(u); - return r; - } - - LIST_FOREACH(sessions_by_user, s, u->sessions) { - k = session_stop(s, force); - if (k < 0) - r = k; - } - - /* Kill systemd */ - k = user_stop_service(u); - if (k < 0) - r = k; - - /* Kill cgroup */ - k = user_stop_slice(u); - if (k < 0) - r = k; - - u->stopping = true; - - user_save(u); - - return r; -} - -int user_finalize(User *u) { - Session *s; - int r = 0, k; - - assert(u); - - if (u->started) - log_debug("User %s logged out.", u->name); - - LIST_FOREACH(sessions_by_user, s, u->sessions) { - k = session_finalize(s); - if (k < 0) - r = k; - } - - /* Kill XDG_RUNTIME_DIR */ - k = user_remove_runtime_path(u); - if (k < 0) - r = k; - - /* Clean SysV + POSIX IPC objects */ - if (u->manager->remove_ipc) { - k = clean_ipc(u->uid); - if (k < 0) - r = k; - } - - unlink(u->state_file); - user_add_to_gc_queue(u); - - if (u->started) { - user_send_signal(u, false); - u->started = false; - } - - return r; -} - -int user_get_idle_hint(User *u, dual_timestamp *t) { - Session *s; - bool idle_hint = true; - dual_timestamp ts = DUAL_TIMESTAMP_NULL; - - assert(u); - - LIST_FOREACH(sessions_by_user, s, u->sessions) { - dual_timestamp k; - int ih; - - ih = session_get_idle_hint(s, &k); - if (ih < 0) - return ih; - - if (!ih) { - if (!idle_hint) { - if (k.monotonic < ts.monotonic) - ts = k; - } else { - idle_hint = false; - ts = k; - } - } else if (idle_hint) { - - if (k.monotonic > ts.monotonic) - ts = k; - } - } - - if (t) - *t = ts; - - return idle_hint; -} - -int user_check_linger_file(User *u) { - _cleanup_free_ char *cc = NULL; - char *p = NULL; - - cc = cescape(u->name); - if (!cc) - return -ENOMEM; - - p = strjoina("/var/lib/systemd/linger/", cc); - - return access(p, F_OK) >= 0; -} - -bool user_check_gc(User *u, bool drop_not_started) { - assert(u); - - if (drop_not_started && !u->started) - return false; - - if (u->sessions) - return true; - - if (user_check_linger_file(u) > 0) - return true; - - if (u->slice_job && manager_job_is_active(u->manager, u->slice_job)) - return true; - - if (u->service_job && manager_job_is_active(u->manager, u->service_job)) - return true; - - return false; -} - -void user_add_to_gc_queue(User *u) { - assert(u); - - if (u->in_gc_queue) - return; - - LIST_PREPEND(gc_queue, u->manager->user_gc_queue, u); - u->in_gc_queue = true; -} - -UserState user_get_state(User *u) { - Session *i; - - assert(u); - - if (u->stopping) - return USER_CLOSING; - - if (!u->started || u->slice_job || u->service_job) - return USER_OPENING; - - if (u->sessions) { - bool all_closing = true; - - LIST_FOREACH(sessions_by_user, i, u->sessions) { - SessionState state; - - state = session_get_state(i); - if (state == SESSION_ACTIVE) - return USER_ACTIVE; - if (state != SESSION_CLOSING) - all_closing = false; - } - - return all_closing ? USER_CLOSING : USER_ONLINE; - } - - if (user_check_linger_file(u) > 0) - return USER_LINGERING; - - return USER_CLOSING; -} - -int user_kill(User *u, int signo) { - assert(u); - - return manager_kill_unit(u->manager, u->slice, KILL_ALL, signo, NULL); -} - -static bool elect_display_filter(Session *s) { - /* Return true if the session is a candidate for the user’s ‘primary - * session’ or ‘display’. */ - assert(s); - - return (s->class == SESSION_USER && !s->stopping); -} - -static int elect_display_compare(Session *s1, Session *s2) { - /* Indexed by SessionType. Lower numbers mean more preferred. */ - const int type_ranks[_SESSION_TYPE_MAX] = { - [SESSION_UNSPECIFIED] = 0, - [SESSION_TTY] = -2, - [SESSION_X11] = -3, - [SESSION_WAYLAND] = -3, - [SESSION_MIR] = -3, - [SESSION_WEB] = -1, - }; - - /* Calculate the partial order relationship between s1 and s2, - * returning < 0 if s1 is preferred as the user’s ‘primary session’, - * 0 if s1 and s2 are equally preferred or incomparable, or > 0 if s2 - * is preferred. - * - * s1 or s2 may be NULL. */ - if (!s1 && !s2) - return 0; - - if ((s1 == NULL) != (s2 == NULL)) - return (s1 == NULL) - (s2 == NULL); - - if (s1->stopping != s2->stopping) - return s1->stopping - s2->stopping; - - if ((s1->class != SESSION_USER) != (s2->class != SESSION_USER)) - return (s1->class != SESSION_USER) - (s2->class != SESSION_USER); - - if ((s1->type == _SESSION_TYPE_INVALID) != (s2->type == _SESSION_TYPE_INVALID)) - return (s1->type == _SESSION_TYPE_INVALID) - (s2->type == _SESSION_TYPE_INVALID); - - if (s1->type != s2->type) - return type_ranks[s1->type] - type_ranks[s2->type]; - - return 0; -} - -void user_elect_display(User *u) { - Session *s; - - assert(u); - - /* This elects a primary session for each user, which we call - * the "display". We try to keep the assignment stable, but we - * "upgrade" to better choices. */ - log_debug("Electing new display for user %s", u->name); - - LIST_FOREACH(sessions_by_user, s, u->sessions) { - if (!elect_display_filter(s)) { - log_debug("Ignoring session %s", s->id); - continue; - } - - if (elect_display_compare(s, u->display) < 0) { - log_debug("Choosing session %s in preference to %s", s->id, u->display ? u->display->id : "-"); - u->display = s; - } - } -} - -static const char* const user_state_table[_USER_STATE_MAX] = { - [USER_OFFLINE] = "offline", - [USER_OPENING] = "opening", - [USER_LINGERING] = "lingering", - [USER_ONLINE] = "online", - [USER_ACTIVE] = "active", - [USER_CLOSING] = "closing" -}; - -DEFINE_STRING_TABLE_LOOKUP(user_state, UserState); - -int config_parse_tmpfs_size( - const char* unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - size_t *sz = data; - const char *e; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - e = endswith(rvalue, "%"); - if (e) { - unsigned long ul; - char *f; - - errno = 0; - ul = strtoul(rvalue, &f, 10); - if (errno > 0 || f != e) { - log_syntax(unit, LOG_ERR, filename, line, errno, "Failed to parse percentage value, ignoring: %s", rvalue); - return 0; - } - - if (ul <= 0 || ul >= 100) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Percentage value out of range, ignoring: %s", rvalue); - return 0; - } - - *sz = PAGE_ALIGN((size_t) ((physical_memory() * (uint64_t) ul) / (uint64_t) 100)); - } else { - uint64_t k; - - r = parse_size(rvalue, 1024, &k); - if (r < 0 || (uint64_t) (size_t) k != k) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse size value, ignoring: %s", rvalue); - return 0; - } - - *sz = PAGE_ALIGN((size_t) k); - } - - return 0; -} diff --git a/src/grp-login/liblogind-core/logind-user.h b/src/grp-login/liblogind-core/logind-user.h deleted file mode 100644 index b23c59fa1d..0000000000 --- a/src/grp-login/liblogind-core/logind-user.h +++ /dev/null @@ -1,94 +0,0 @@ -#pragma once - -/*** - 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 Lesser General Public License as published by - the Free Software Foundation; either version 2.1 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "basic/list.h" - -typedef struct User User; - -#include "logind.h" - -typedef enum UserState { - USER_OFFLINE, /* Not logged in at all */ - USER_OPENING, /* Is logging in */ - USER_LINGERING, /* Lingering has been enabled by the admin for this user */ - USER_ONLINE, /* User logged in */ - USER_ACTIVE, /* User logged in and has a session in the fg */ - USER_CLOSING, /* User logged out, but processes still remain and lingering is not enabled */ - _USER_STATE_MAX, - _USER_STATE_INVALID = -1 -} UserState; - -struct User { - Manager *manager; - uid_t uid; - gid_t gid; - char *name; - char *state_file; - char *runtime_path; - char *slice; - char *service; - - char *service_job; - char *slice_job; - - Session *display; - - dual_timestamp timestamp; - - bool in_gc_queue:1; - bool started:1; - bool stopping:1; - - LIST_HEAD(Session, sessions); - LIST_FIELDS(User, gc_queue); -}; - -int user_new(User **out, Manager *m, uid_t uid, gid_t gid, const char *name); -User *user_free(User *u); - -DEFINE_TRIVIAL_CLEANUP_FUNC(User *, user_free); - -bool user_check_gc(User *u, bool drop_not_started); -void user_add_to_gc_queue(User *u); -int user_start(User *u); -int user_stop(User *u, bool force); -int user_finalize(User *u); -UserState user_get_state(User *u); -int user_get_idle_hint(User *u, dual_timestamp *t); -int user_save(User *u); -int user_load(User *u); -int user_kill(User *u, int signo); -int user_check_linger_file(User *u); -void user_elect_display(User *u); - -extern const sd_bus_vtable user_vtable[]; -int user_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error); -int user_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error); -char *user_bus_path(User *s); - -int user_send_signal(User *u, bool new_user); -int user_send_changed(User *u, const char *properties, ...) _sentinel_; - -const char* user_state_to_string(UserState s) _const_; -UserState user_state_from_string(const char *s) _pure_; - -int bus_user_method_terminate(sd_bus_message *message, void *userdata, sd_bus_error *error); -int bus_user_method_kill(sd_bus_message *message, void *userdata, sd_bus_error *error); diff --git a/src/grp-login/liblogind-core/logind-utmp.c b/src/grp-login/liblogind-core/logind-utmp.c deleted file mode 100644 index c5dc137d61..0000000000 --- a/src/grp-login/liblogind-core/logind-utmp.c +++ /dev/null @@ -1,183 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2015 Daniel Mack - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include - -#include - -#include "basic/alloc-util.h" -#include "basic/audit-util.h" -#include "basic/formats-util.h" -#include "basic/special.h" -#include "basic/strv.h" -#include "basic/unit-name.h" -#include "basic/user-util.h" -#include "logind.h" -#include "sd-bus/bus-common-errors.h" -#include "sd-bus/bus-error.h" -#include "shared/bus-util.h" -#include "shared/utmp-wtmp.h" - -_const_ static usec_t when_wall(usec_t n, usec_t elapse) { - - usec_t left; - unsigned int i; - static const int wall_timers[] = { - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, - 25, 40, 55, 70, 100, 130, 150, 180, - }; - - /* If the time is already passed, then don't announce */ - if (n >= elapse) - return 0; - - left = elapse - n; - - for (i = 1; i < ELEMENTSOF(wall_timers); i++) - if (wall_timers[i] * USEC_PER_MINUTE >= left) - return left - wall_timers[i-1] * USEC_PER_MINUTE; - - return left % USEC_PER_HOUR; -} - -bool logind_wall_tty_filter(const char *tty, void *userdata) { - - Manager *m = userdata; - - assert(m); - - if (!startswith(tty, "/dev/") || !m->scheduled_shutdown_tty) - return true; - - return !streq(tty + 5, m->scheduled_shutdown_tty); -} - -static int warn_wall(Manager *m, usec_t n) { - char date[FORMAT_TIMESTAMP_MAX] = {}; - _cleanup_free_ char *l = NULL; - usec_t left; - int r; - - assert(m); - - if (!m->enable_wall_messages) - return 0; - - left = m->scheduled_shutdown_timeout > n; - - r = asprintf(&l, "%s%sThe system is going down for %s %s%s!", - strempty(m->wall_message), - isempty(m->wall_message) ? "" : "\n", - m->scheduled_shutdown_type, - left ? "at " : "NOW", - left ? format_timestamp(date, sizeof(date), m->scheduled_shutdown_timeout) : ""); - if (r < 0) { - log_oom(); - return 0; - } - - utmp_wall(l, uid_to_name(m->scheduled_shutdown_uid), - m->scheduled_shutdown_tty, logind_wall_tty_filter, m); - - return 1; -} - -static int wall_message_timeout_handler( - sd_event_source *s, - uint64_t usec, - void *userdata) { - - Manager *m = userdata; - usec_t n, next; - int r; - - assert(m); - assert(s == m->wall_message_timeout_source); - - n = now(CLOCK_REALTIME); - - r = warn_wall(m, n); - if (r == 0) - return 0; - - next = when_wall(n, m->scheduled_shutdown_timeout); - if (next > 0) { - r = sd_event_source_set_time(s, n + next); - if (r < 0) - return log_error_errno(r, "sd_event_source_set_time() failed. %m"); - - r = sd_event_source_set_enabled(s, SD_EVENT_ONESHOT); - if (r < 0) - return log_error_errno(r, "sd_event_source_set_enabled() failed. %m"); - } - - return 0; -} - -int manager_setup_wall_message_timer(Manager *m) { - - usec_t n, elapse; - int r; - - assert(m); - - n = now(CLOCK_REALTIME); - elapse = m->scheduled_shutdown_timeout; - - /* wall message handling */ - - if (isempty(m->scheduled_shutdown_type)) { - warn_wall(m, n); - return 0; - } - - if (elapse < n) - return 0; - - /* Warn immediately if less than 15 minutes are left */ - if (elapse - n < 15 * USEC_PER_MINUTE) { - r = warn_wall(m, n); - if (r == 0) - return 0; - } - - elapse = when_wall(n, elapse); - if (elapse == 0) - return 0; - - if (m->wall_message_timeout_source) { - r = sd_event_source_set_time(m->wall_message_timeout_source, n + elapse); - if (r < 0) - return log_error_errno(r, "sd_event_source_set_time() failed. %m"); - - r = sd_event_source_set_enabled(m->wall_message_timeout_source, SD_EVENT_ONESHOT); - if (r < 0) - return log_error_errno(r, "sd_event_source_set_enabled() failed. %m"); - } else { - r = sd_event_add_time(m->event, &m->wall_message_timeout_source, - CLOCK_REALTIME, n + elapse, 0, wall_message_timeout_handler, m); - if (r < 0) - return log_error_errno(r, "sd_event_add_time() failed. %m"); - } - - return 0; -} diff --git a/src/grp-login/liblogind-core/logind.h b/src/grp-login/liblogind-core/logind.h deleted file mode 100644 index effc498118..0000000000 --- a/src/grp-login/liblogind-core/logind.h +++ /dev/null @@ -1,198 +0,0 @@ -#pragma once - -/*** - 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 Lesser General Public License as published by - the Free Software Foundation; either version 2.1 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include - -#include -#include -#include - -#include "basic/hashmap.h" -#include "basic/list.h" -#include "basic/set.h" - -typedef struct Manager Manager; - -#include "logind-action.h" -#include "logind-button.h" -#include "logind-device.h" -#include "logind-inhibit.h" - -struct Manager { - sd_event *event; - sd_bus *bus; - - Hashmap *devices; - Hashmap *seats; - Hashmap *sessions; - Hashmap *users; - Hashmap *inhibitors; - Hashmap *buttons; - - LIST_HEAD(Seat, seat_gc_queue); - LIST_HEAD(Session, session_gc_queue); - LIST_HEAD(User, user_gc_queue); - - struct udev *udev; - struct udev_monitor *udev_seat_monitor, *udev_device_monitor, *udev_vcsa_monitor, *udev_button_monitor; - - sd_event_source *console_active_event_source; - sd_event_source *udev_seat_event_source; - sd_event_source *udev_device_event_source; - sd_event_source *udev_vcsa_event_source; - sd_event_source *udev_button_event_source; - - int console_active_fd; - - unsigned n_autovts; - - unsigned reserve_vt; - int reserve_vt_fd; - - Seat *seat0; - - char **kill_only_users, **kill_exclude_users; - bool kill_user_processes; - - unsigned long session_counter; - unsigned long inhibit_counter; - - Hashmap *session_units; - Hashmap *user_units; - - usec_t inhibit_delay_max; - - /* If an action is currently being executed or is delayed, - * this is != 0 and encodes what is being done */ - InhibitWhat action_what; - - /* If a shutdown/suspend was delayed due to a inhibitor this - contains the unit name we are supposed to start after the - delay is over */ - const char *action_unit; - - /* If a shutdown/suspend is currently executed, then this is - * the job of it */ - char *action_job; - sd_event_source *inhibit_timeout_source; - - char *scheduled_shutdown_type; - usec_t scheduled_shutdown_timeout; - sd_event_source *scheduled_shutdown_timeout_source; - uid_t scheduled_shutdown_uid; - char *scheduled_shutdown_tty; - sd_event_source *nologin_timeout_source; - bool unlink_nologin; - - char *wall_message; - unsigned enable_wall_messages; - sd_event_source *wall_message_timeout_source; - - bool shutdown_dry_run; - - sd_event_source *idle_action_event_source; - usec_t idle_action_usec; - usec_t idle_action_not_before_usec; - HandleAction idle_action; - - HandleAction handle_power_key; - HandleAction handle_suspend_key; - HandleAction handle_hibernate_key; - HandleAction handle_lid_switch; - HandleAction handle_lid_switch_docked; - - bool power_key_ignore_inhibited; - bool suspend_key_ignore_inhibited; - bool hibernate_key_ignore_inhibited; - bool lid_switch_ignore_inhibited; - - bool remove_ipc; - - Hashmap *polkit_registry; - - usec_t holdoff_timeout_usec; - sd_event_source *lid_switch_ignore_event_source; - - size_t runtime_dir_size; - uint64_t user_tasks_max; - uint64_t sessions_max; - uint64_t inhibitors_max; -}; - -int manager_add_device(Manager *m, const char *sysfs, bool master, Device **_device); -int manager_add_button(Manager *m, const char *name, Button **_button); -int manager_add_seat(Manager *m, const char *id, Seat **_seat); -int manager_add_session(Manager *m, const char *id, Session **_session); -int manager_add_user(Manager *m, uid_t uid, gid_t gid, const char *name, User **_user); -int manager_add_user_by_name(Manager *m, const char *name, User **_user); -int manager_add_user_by_uid(Manager *m, uid_t uid, User **_user); -int manager_add_inhibitor(Manager *m, const char* id, Inhibitor **_inhibitor); - -int manager_process_seat_device(Manager *m, struct udev_device *d); -int manager_process_button_device(Manager *m, struct udev_device *d); - -int manager_spawn_autovt(Manager *m, unsigned int vtnr); - -bool manager_shall_kill(Manager *m, const char *user); - -int manager_get_idle_hint(Manager *m, dual_timestamp *t); - -int manager_get_user_by_pid(Manager *m, pid_t pid, User **user); -int manager_get_session_by_pid(Manager *m, pid_t pid, Session **session); - -bool manager_is_docked_or_external_displays(Manager *m); - -extern const sd_bus_vtable manager_vtable[]; - -int match_job_removed(sd_bus_message *message, void *userdata, sd_bus_error *error); -int match_unit_removed(sd_bus_message *message, void *userdata, sd_bus_error *error); -int match_properties_changed(sd_bus_message *message, void *userdata, sd_bus_error *error); -int match_reloading(sd_bus_message *message, void *userdata, sd_bus_error *error); -int match_name_owner_changed(sd_bus_message *message, void *userdata, sd_bus_error *error); - -int bus_manager_shutdown_or_sleep_now_or_later(Manager *m, const char *unit_name, InhibitWhat w, sd_bus_error *error); - -int manager_send_changed(Manager *manager, const char *property, ...) _sentinel_; - -int manager_start_slice(Manager *manager, const char *slice, const char *description, const char *after, const char *after2, uint64_t tasks_max, sd_bus_error *error, char **job); -int manager_start_scope(Manager *manager, const char *scope, pid_t pid, const char *slice, const char *description, const char *after, const char *after2, uint64_t tasks_max, sd_bus_error *error, char **job); -int manager_start_unit(Manager *manager, const char *unit, sd_bus_error *error, char **job); -int manager_stop_unit(Manager *manager, const char *unit, sd_bus_error *error, char **job); -int manager_abandon_scope(Manager *manager, const char *scope, sd_bus_error *error); -int manager_kill_unit(Manager *manager, const char *unit, KillWho who, int signo, sd_bus_error *error); -int manager_unit_is_active(Manager *manager, const char *unit); -int manager_job_is_active(Manager *manager, const char *path); - -/* gperf lookup function */ -const struct ConfigPerfItem* logind_gperf_lookup(const char *key, unsigned length); - -int manager_set_lid_switch_ignore(Manager *m, usec_t until); - -int config_parse_tmpfs_size(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); - -int manager_get_session_from_creds(Manager *m, sd_bus_message *message, const char *name, sd_bus_error *error, Session **ret); -int manager_get_user_from_creds(Manager *m, sd_bus_message *message, uid_t uid, sd_bus_error *error, User **ret); -int manager_get_seat_from_creds(Manager *m, sd_bus_message *message, const char *name, sd_bus_error *error, Seat **ret); - -int manager_setup_wall_message_timer(Manager *m); -bool logind_wall_tty_filter(const char *tty, void *userdata); - -int manager_dispatch_delayed(Manager *manager, bool timeout); diff --git a/src/grp-login/logind.conf.in b/src/grp-login/logind.conf.in deleted file mode 100644 index 32c0844cb6..0000000000 --- a/src/grp-login/logind.conf.in +++ /dev/null @@ -1,37 +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 Lesser General Public License as published by -# the Free Software Foundation; either version 2.1 of the License, or -# (at your option) any later version. -# -# Entries in this file show the compile time defaults. -# You can change settings by editing this file. -# Defaults can be restored by simply deleting this file. -# -# See logind.conf(5) for details. - -[Login] -#NAutoVTs=6 -#ReserveVT=6 -#KillUserProcesses=@KILL_USER_PROCESSES@ -#KillOnlyUsers= -#KillExcludeUsers=root -#InhibitDelayMaxSec=5 -#HandlePowerKey=poweroff -#HandleSuspendKey=suspend -#HandleHibernateKey=hibernate -#HandleLidSwitch=suspend -#HandleLidSwitchDocked=ignore -#PowerKeyIgnoreInhibited=no -#SuspendKeyIgnoreInhibited=no -#HibernateKeyIgnoreInhibited=no -#LidSwitchIgnoreInhibited=yes -#HoldoffTimeoutSec=30s -#IdleAction=ignore -#IdleActionSec=30min -#RuntimeDirectorySize=10% -#RemoveIPC=yes -#InhibitorsMax=8192 -#SessionsMax=8192 -#UserTasksMax=12288 diff --git a/src/grp-login/org.freedesktop.login1.conf b/src/grp-login/org.freedesktop.login1.conf deleted file mode 100644 index c89e40457e..0000000000 --- a/src/grp-login/org.freedesktop.login1.conf +++ /dev/null @@ -1,274 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/grp-login/org.freedesktop.login1.policy.in b/src/grp-login/org.freedesktop.login1.policy.in deleted file mode 100644 index 1fa6441629..0000000000 --- a/src/grp-login/org.freedesktop.login1.policy.in +++ /dev/null @@ -1,323 +0,0 @@ - - - - - - - - The systemd Project - http://www.freedesktop.org/wiki/Software/systemd - - - <_description>Allow applications to inhibit system shutdown - <_message>Authentication is required for an application to inhibit system shutdown. - - no - yes - yes - - org.freedesktop.login1.inhibit-delay-shutdown org.freedesktop.login1.inhibit-block-sleep org.freedesktop.login1.inhibit-delay-sleep org.freedesktop.login1.inhibit-block-idle - - - - <_description>Allow applications to delay system shutdown - <_message>Authentication is required for an application to delay system shutdown. - - yes - yes - yes - - org.freedesktop.login1.inhibit-delay-sleep - - - - <_description>Allow applications to inhibit system sleep - <_message>Authentication is required for an application to inhibit system sleep. - - no - yes - yes - - org.freedesktop.login1.inhibit-delay-sleep org.freedesktop.login1.inhibit-block-idle - - - - <_description>Allow applications to delay system sleep - <_message>Authentication is required for an application to delay system sleep. - - yes - yes - yes - - - - - <_description>Allow applications to inhibit automatic system suspend - <_message>Authentication is required for an application to inhibit automatic system suspend. - - yes - yes - yes - - - - - <_description>Allow applications to inhibit system handling of the power key - <_message>Authentication is required for an application to inhibit system handling of the power key. - - no - yes - yes - - org.freedesktop.login1.inhibit-handle-suspend-key org.freedesktop.login1.inhibit-handle-hibernate-key org.freedesktop.login1.inhibit-handle-lid-switch - - - - <_description>Allow applications to inhibit system handling of the suspend key - <_message>Authentication is required for an application to inhibit system handling of the suspend key. - - no - yes - yes - - org.freedesktop.login1.inhibit-handle-hibernate-key org.freedesktop.login1.inhibit-handle-lid-switch - - - - <_description>Allow applications to inhibit system handling of the hibernate key - <_message>Authentication is required for an application to inhibit system handling of the hibernate key. - - no - yes - yes - - - - - <_description>Allow applications to inhibit system handling of the lid switch - <_message>Authentication is required for an application to inhibit system handling of the lid switch. - - no - yes - yes - - - - - <_description>Allow non-logged-in user to run programs - <_message>Explicit request is required to run programs as a non-logged-in user. - - yes - - - - - <_description>Allow non-logged-in users to run programs - <_message>Authentication is required to run programs as a non-logged-in user. - - auth_admin_keep - auth_admin_keep - auth_admin_keep - - - - - <_description>Allow attaching devices to seats - <_message>Authentication is required for attaching a device to a seat. - - auth_admin_keep - auth_admin_keep - auth_admin_keep - - org.freedesktop.login1.flush-devices - - - - <_description>Flush device to seat attachments - <_message>Authentication is required for resetting how devices are attached to seats. - - auth_admin_keep - auth_admin_keep - auth_admin_keep - - - - - <_description>Power off the system - <_message>Authentication is required for powering off the system. - - auth_admin_keep - auth_admin_keep - yes - - org.freedesktop.login1.set-wall-message - - - - <_description>Power off the system while other users are logged in - <_message>Authentication is required for powering off the system while other users are logged in. - - auth_admin_keep - auth_admin_keep - yes - - org.freedesktop.login1.power-off - - - - <_description>Power off the system while an application asked to inhibit it - <_message>Authentication is required for powering off the system while an application asked to inhibit it. - - auth_admin_keep - auth_admin_keep - auth_admin_keep - - org.freedesktop.login1.power-off - - - - <_description>Reboot the system - <_message>Authentication is required for rebooting the system. - - auth_admin_keep - auth_admin_keep - yes - - org.freedesktop.login1.set-wall-message - - - - <_description>Reboot the system while other users are logged in - <_message>Authentication is required for rebooting the system while other users are logged in. - - auth_admin_keep - auth_admin_keep - yes - - org.freedesktop.login1.reboot - - - - <_description>Reboot the system while an application asked to inhibit it - <_message>Authentication is required for rebooting the system while an application asked to inhibit it. - - auth_admin_keep - auth_admin_keep - auth_admin_keep - - org.freedesktop.login1.reboot - - - - <_description>Suspend the system - <_message>Authentication is required for suspending the system. - - auth_admin_keep - auth_admin_keep - yes - - - - - <_description>Suspend the system while other users are logged in - <_message>Authentication is required for suspending the system while other users are logged in. - - auth_admin_keep - auth_admin_keep - yes - - org.freedesktop.login1.suspend - - - - <_description>Suspend the system while an application asked to inhibit it - <_message>Authentication is required for suspending the system while an application asked to inhibit it. - - auth_admin_keep - auth_admin_keep - auth_admin_keep - - org.freedesktop.login1.suspend - - - - <_description>Hibernate the system - <_message>Authentication is required for hibernating the system. - - auth_admin_keep - auth_admin_keep - yes - - - - - <_description>Hibernate the system while other users are logged in - <_message>Authentication is required for hibernating the system while other users are logged in. - - auth_admin_keep - auth_admin_keep - yes - - org.freedesktop.login1.hibernate - - - - <_description>Hibernate the system while an application asked to inhibit it - <_message>Authentication is required for hibernating the system while an application asked to inhibit it. - - auth_admin_keep - auth_admin_keep - auth_admin_keep - - org.freedesktop.login1.hibernate - - - - <_description>Manage active sessions, users and seats - <_message>Authentication is required for managing active sessions, users and seats. - - auth_admin_keep - auth_admin_keep - auth_admin_keep - - - - - <_description>Lock or unlock active sessions - <_message>Authentication is required to lock or unlock active sessions. - - auth_admin_keep - auth_admin_keep - auth_admin_keep - - - - - <_description>Allow indication to the firmware to boot to setup interface - <_message>Authentication is required to indicate to the firmware to boot to setup interface. - - auth_admin_keep - auth_admin_keep - auth_admin_keep - - - - - <_description>Set a wall message - <_message>Authentication is required to set a wall message - - auth_admin_keep - auth_admin_keep - auth_admin_keep - - - - diff --git a/src/grp-login/org.freedesktop.login1.service b/src/grp-login/org.freedesktop.login1.service deleted file mode 100644 index 762dae2bb3..0000000000 --- a/src/grp-login/org.freedesktop.login1.service +++ /dev/null @@ -1,12 +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 Lesser General Public License as published by -# the Free Software Foundation; either version 2.1 of the License, or -# (at your option) any later version. - -[D-BUS Service] -Name=org.freedesktop.login1 -Exec=/bin/false -User=root -SystemdService=dbus-org.freedesktop.login1.service diff --git a/src/grp-login/systemd-logind/70-power-switch.rules b/src/grp-login/systemd-logind/70-power-switch.rules new file mode 100644 index 0000000000..e2855b50f7 --- /dev/null +++ b/src/grp-login/systemd-logind/70-power-switch.rules @@ -0,0 +1,18 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +ACTION=="remove", GOTO="power_switch_end" + +SUBSYSTEM=="input", KERNEL=="event*", SUBSYSTEMS=="acpi", TAG+="power-switch" +SUBSYSTEM=="input", KERNEL=="event*", KERNELS=="thinkpad_acpi", TAG+="power-switch" +SUBSYSTEM=="input", KERNEL=="event*", ATTRS{name}=="twl4030_pwrbutton", TAG+="power-switch" +SUBSYSTEM=="input", KERNEL=="event*", ATTRS{name}=="tps65217_pwr_but", TAG+="power-switch" +SUBSYSTEM=="input", KERNEL=="event*", ATTRS{name}=="* WMI hotkeys", TAG+="power-switch" +SUBSYSTEM=="input", KERNEL=="event*", \ + SUBSYSTEMS=="platform", DRIVERS=="gpio-keys", ATTRS{keys}=="*,116|116,*|116|*,116,*", TAG+="power-switch" + +LABEL="power_switch_end" diff --git a/src/grp-login/systemd-logind/70-uaccess.rules b/src/grp-login/systemd-logind/70-uaccess.rules new file mode 100644 index 0000000000..50dcd2e275 --- /dev/null +++ b/src/grp-login/systemd-logind/70-uaccess.rules @@ -0,0 +1,81 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +ACTION=="remove", GOTO="uaccess_end" +ENV{MAJOR}=="", GOTO="uaccess_end" + +# PTP/MTP protocol devices, cameras, portable media players +SUBSYSTEM=="usb", ENV{ID_USB_INTERFACES}=="*:060101:*", TAG+="uaccess" + +# Digicams with proprietary protocol +ENV{ID_GPHOTO2}=="?*", TAG+="uaccess" + +# SCSI and USB scanners +ENV{libsane_matched}=="yes", TAG+="uaccess" + +# HPLIP devices (necessary for ink level check and HP tool maintenance) +ENV{ID_HPLIP}=="1", TAG+="uaccess" + +# optical drives +SUBSYSTEM=="block", ENV{ID_CDROM}=="1", TAG+="uaccess" +SUBSYSTEM=="scsi_generic", SUBSYSTEMS=="scsi", ATTRS{type}=="4|5", TAG+="uaccess" + +# Sound devices +SUBSYSTEM=="sound", TAG+="uaccess" \ + OPTIONS+="static_node=snd/timer", OPTIONS+="static_node=snd/seq" + +# ffado is an userspace driver for firewire sound cards +SUBSYSTEM=="firewire", ENV{ID_FFADO}=="1", TAG+="uaccess" + +# Webcams, frame grabber, TV cards +SUBSYSTEM=="video4linux", TAG+="uaccess" +SUBSYSTEM=="dvb", TAG+="uaccess" + +# IIDC devices: industrial cameras and some webcams +SUBSYSTEM=="firewire", ATTR{units}=="*0x00a02d:0x00010*", TAG+="uaccess" +SUBSYSTEM=="firewire", ATTR{units}=="*0x00b09d:0x00010*", TAG+="uaccess" +# AV/C devices: camcorders, set-top boxes, TV sets, audio devices, and more +SUBSYSTEM=="firewire", ATTR{units}=="*0x00a02d:0x010001*", TAG+="uaccess" +SUBSYSTEM=="firewire", ATTR{units}=="*0x00a02d:0x014001*", TAG+="uaccess" + +# DRI video devices +SUBSYSTEM=="drm", KERNEL=="card*|renderD*", TAG+="uaccess" + +# KVM +SUBSYSTEM=="misc", KERNEL=="kvm", TAG+="uaccess" + +# smart-card readers +ENV{ID_SMARTCARD_READER}=="?*", TAG+="uaccess" + +# (USB) authentication devices +ENV{ID_SECURITY_TOKEN}=="?*", TAG+="uaccess" + +# PDA devices +ENV{ID_PDA}=="?*", TAG+="uaccess" + +# Programmable remote control +ENV{ID_REMOTE_CONTROL}=="1", TAG+="uaccess" + +# joysticks +SUBSYSTEM=="input", ENV{ID_INPUT_JOYSTICK}=="?*", TAG+="uaccess" + +# color measurement devices +ENV{COLOR_MEASUREMENT_DEVICE}=="?*", TAG+="uaccess" + +# DDC/CI device, usually high-end monitors such as the DreamColor +ENV{DDC_DEVICE}=="?*", TAG+="uaccess" + +# media player raw devices (for user-mode drivers, Android SDK, etc.) +SUBSYSTEM=="usb", ENV{ID_MEDIA_PLAYER}=="?*", TAG+="uaccess" + +# software-defined radio communication devices +ENV{ID_SOFTWARE_RADIO}=="?*", TAG+="uaccess" + +# 3D printers, CNC machines, laser cutters, 3D scanners, etc. +ENV{ID_MAKER_TOOL}=="?*", TAG+="uaccess" + +LABEL="uaccess_end" diff --git a/src/grp-login/systemd-logind/71-seat.rules.in b/src/grp-login/systemd-logind/71-seat.rules.in new file mode 100644 index 0000000000..de55c9a4ec --- /dev/null +++ b/src/grp-login/systemd-logind/71-seat.rules.in @@ -0,0 +1,54 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +ACTION=="remove", GOTO="seat_end" + +TAG=="uaccess", SUBSYSTEM!="sound", TAG+="seat" +SUBSYSTEM=="sound", KERNEL=="card*", TAG+="seat" +SUBSYSTEM=="input", KERNEL=="input*", TAG+="seat" +SUBSYSTEM=="graphics", KERNEL=="fb[0-9]*", TAG+="seat", TAG+="master-of-seat" +SUBSYSTEM=="drm", KERNEL=="card[0-9]*", TAG+="seat", TAG+="master-of-seat" +SUBSYSTEM=="usb", ATTR{bDeviceClass}=="09", TAG+="seat" + +# 'Plugable' USB hub, sound, network, graphics adapter +SUBSYSTEM=="usb", ATTR{idVendor}=="2230", ATTR{idProduct}=="000[13]", ENV{ID_AUTOSEAT}="1" + +# qemu (version 2.4+) has a PCI-PCI bridge (-device pci-bridge-seat) to group +# devices belonging to one seat. See: +# http://git.qemu.org/?p=qemu.git;a=blob;f=docs/multiseat.txt +SUBSYSTEM=="pci", ATTR{vendor}=="0x1b36", ATTR{device}=="0x000a", TAG+="seat", ENV{ID_AUTOSEAT}="1" + +# Mimo 720, with integrated USB hub, displaylink graphics, and e2i +# touchscreen. This device carries no proper VID/PID in the USB hub, +# but it does carry good ID data in the graphics component, hence we +# check it from the parent. There's a bit of a race here however, +# given that the child devices might not exist yet at the time this +# rule is executed. To work around this we'll trigger the parent from +# the child if we notice that the parent wasn't recognized yet. + +# Match parent +SUBSYSTEM=="usb", ATTR{idVendor}=="058f", ATTR{idProduct}=="6254", \ + ATTR{%k.2/idVendor}=="17e9", ATTR{%k.2/idProduct}=="401a", ATTR{%k.2/product}=="mimo inc", \ + ENV{ID_AUTOSEAT}="1", ENV{ID_AVOID_LOOP}="1" + +# Match child, look for parent's ID_AVOID_LOOP +SUBSYSTEM=="usb", ATTR{idVendor}=="17e9", ATTR{idProduct}=="401a", ATTR{product}=="mimo inc", \ + ATTR{../idVendor}=="058f", ATTR{../idProduct}=="6254", \ + IMPORT{parent}="ID_AVOID_LOOP" + +# Match child, retrigger parent +SUBSYSTEM=="usb", ATTR{idVendor}=="17e9", ATTR{idProduct}=="401a", ATTR{product}=="mimo inc", \ + ATTR{../idVendor}=="058f", ATTR{../idProduct}=="6254", \ + ENV{ID_AVOID_LOOP}=="", \ + RUN+="@rootbindir@/udevadm trigger --parent-match=%p/.." + +TAG=="seat", ENV{ID_PATH}=="", IMPORT{builtin}="path_id" +TAG=="seat", ENV{ID_FOR_SEAT}=="", ENV{ID_PATH_TAG}!="", ENV{ID_FOR_SEAT}="$env{SUBSYSTEM}-$env{ID_PATH_TAG}" + +SUBSYSTEM=="input", ATTR{name}=="Wiebetech LLC Wiebetech", RUN+="@rootbindir@/loginctl lock-sessions" + +LABEL="seat_end" diff --git a/src/grp-login/systemd-logind/73-seat-late.rules.in b/src/grp-login/systemd-logind/73-seat-late.rules.in new file mode 100644 index 0000000000..901df750fd --- /dev/null +++ b/src/grp-login/systemd-logind/73-seat-late.rules.in @@ -0,0 +1,17 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +ACTION=="remove", GOTO="seat_late_end" + +ENV{ID_SEAT}=="", ENV{ID_AUTOSEAT}=="1", ENV{ID_FOR_SEAT}!="", ENV{ID_SEAT}="seat-$env{ID_FOR_SEAT}" +ENV{ID_SEAT}=="", IMPORT{parent}="ID_SEAT" + +ENV{ID_SEAT}!="", TAG+="$env{ID_SEAT}" + +TAG=="uaccess", ENV{MAJOR}!="", RUN{builtin}+="uaccess" + +LABEL="seat_late_end" diff --git a/src/grp-login/systemd-logind/Makefile b/src/grp-login/systemd-logind/Makefile index 9dd5855e88..18b18a1683 100644 --- a/src/grp-login/systemd-logind/Makefile +++ b/src/grp-login/systemd-logind/Makefile @@ -23,7 +23,38 @@ include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk include $(topsrcdir)/build-aux/Makefile.head.mk +_liblogind_core_la_SOURCES = \ + src/login/logind-core.c \ + src/login/logind-device.c \ + src/login/logind-device.h \ + src/login/logind-button.c \ + src/login/logind-button.h \ + src/login/logind-action.c \ + src/login/logind-action.h \ + src/login/logind-seat.c \ + src/login/logind-seat.h \ + src/login/logind-session.c \ + src/login/logind-session.h \ + src/login/logind-session-device.c \ + src/login/logind-session-device.h \ + src/login/logind-user.c \ + src/login/logind-user.h \ + src/login/logind-inhibit.c \ + src/login/logind-inhibit.h \ + src/login/logind-dbus.c \ + src/login/logind-session-dbus.c \ + src/login/logind-seat-dbus.c \ + src/login/logind-user-dbus.c \ + src/login/logind-utmp.c \ + src/login/logind-acl.h + +ifneq ($(HAVE_ACL),) +_liblogind_core_la_SOURCES += \ + src/login/logind-acl.c +endif # HAVE_ACL + systemd_logind_SOURCES = \ + $(_liblogind_core_la_SOURCES) \ src/login/logind.c \ src/login/logind.h @@ -31,9 +62,67 @@ nodist_systemd_logind_SOURCES = \ src/login/logind-gperf.c systemd_logind_LDADD = \ - liblogind-core.la + libshared.la rootlibexec_PROGRAMS += \ systemd-logind + +nodist_systemunit_DATA += \ + units/systemd-logind.service + +dist_systemunit_DATA += \ + units/user.slice + +dist_systemunit_DATA_busnames += \ + units/org.freedesktop.login1.busname + +dist_dbussystemservice_DATA += \ + src/login/org.freedesktop.login1.service + +dist_dbuspolicy_DATA += \ + src/login/org.freedesktop.login1.conf + +nodist_pkgsysconf_DATA += \ + src/login/logind.conf + +polkitpolicy_files += \ + src/login/org.freedesktop.login1.policy + +INSTALL_DIRS += \ + $(systemdstatedir) + +MULTI_USER_TARGET_WANTS += \ + systemd-logind.service + +SYSTEM_UNIT_ALIASES += \ + systemd-logind.service dbus-org.freedesktop.login1.service + +BUSNAMES_TARGET_WANTS += \ + org.freedesktop.login1.busname + +dist_udevrules_DATA += \ + src/login/70-uaccess.rules \ + src/login/70-power-switch.rules + +nodist_udevrules_DATA += \ + src/login/71-seat.rules \ + src/login/73-seat-late.rules + + +polkitpolicy_in_files += \ + src/login/org.freedesktop.login1.policy.in + +gperf_gperf_sources += \ + src/login/logind-gperf.gperf + +EXTRA_DIST += \ + src/login/71-seat.rules.in \ + src/login/73-seat-late.rules.in \ + units/systemd-logind.service.in \ + src/login/logind.conf.in + +sd.CPPFLAGS += -DPKGSYSCONFDIR=\"$(pkgsysconfdir)\" +sd.CPPFLAGS += -DKEXEC=\"$(KEXEC)\" + include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-login/systemd-logind/logind-acl.c b/src/grp-login/systemd-logind/logind-acl.c new file mode 100644 index 0000000000..44e49c1dad --- /dev/null +++ b/src/grp-login/systemd-logind/logind-acl.c @@ -0,0 +1,293 @@ +/*** + 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 Lesser General Public License as published by + the Free Software Foundation; either version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include + +#include "basic/alloc-util.h" +#include "basic/dirent-util.h" +#include "basic/escape.h" +#include "basic/fd-util.h" +#include "basic/formats-util.h" +#include "basic/set.h" +#include "basic/string-util.h" +#include "basic/util.h" +#include "shared/acl-util.h" +#include "shared/udev-util.h" + +#include "logind-acl.h" + +static int flush_acl(acl_t acl) { + acl_entry_t i; + int found; + bool changed = false; + + assert(acl); + + 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; + + if (acl_get_tag_type(i, &tag) < 0) + return -errno; + + if (tag != ACL_USER) + continue; + + if (acl_delete_entry(acl, i) < 0) + return -errno; + + changed = true; + } + + if (found < 0) + return -errno; + + return changed; +} + +int devnode_acl(const char *path, + bool flush, + bool del, uid_t old_uid, + bool add, uid_t new_uid) { + + acl_t acl; + int r = 0; + bool changed = false; + + assert(path); + + acl = acl_get_file(path, ACL_TYPE_ACCESS); + if (!acl) + return -errno; + + if (flush) { + + r = flush_acl(acl); + if (r < 0) + goto finish; + if (r > 0) + changed = true; + + } else if (del && old_uid > 0) { + acl_entry_t entry; + + r = acl_find_uid(acl, old_uid, &entry); + if (r < 0) + goto finish; + + if (r > 0) { + if (acl_delete_entry(acl, entry) < 0) { + r = -errno; + goto finish; + } + + changed = true; + } + } + + if (add && new_uid > 0) { + acl_entry_t entry; + acl_permset_t permset; + int rd, wt; + + r = acl_find_uid(acl, new_uid, &entry); + if (r < 0) + goto finish; + + if (r == 0) { + if (acl_create_entry(&acl, &entry) < 0) { + r = -errno; + goto finish; + } + + if (acl_set_tag_type(entry, ACL_USER) < 0 || + acl_set_qualifier(entry, &new_uid) < 0) { + r = -errno; + goto finish; + } + } + + if (acl_get_permset(entry, &permset) < 0) { + r = -errno; + goto finish; + } + + rd = acl_get_perm(permset, ACL_READ); + if (rd < 0) { + r = -errno; + goto finish; + } + + wt = acl_get_perm(permset, ACL_WRITE); + if (wt < 0) { + r = -errno; + goto finish; + } + + if (!rd || !wt) { + + if (acl_add_perm(permset, ACL_READ|ACL_WRITE) < 0) { + r = -errno; + goto finish; + } + + changed = true; + } + } + + if (!changed) + goto finish; + + if (acl_calc_mask(&acl) < 0) { + r = -errno; + goto finish; + } + + if (acl_set_file(path, ACL_TYPE_ACCESS, acl) < 0) { + r = -errno; + goto finish; + } + + r = 0; + +finish: + acl_free(acl); + + return r; +} + +int devnode_acl_all(struct udev *udev, + const char *seat, + bool flush, + bool del, uid_t old_uid, + bool add, uid_t new_uid) { + + _cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL; + struct udev_list_entry *item = NULL, *first = NULL; + _cleanup_set_free_free_ Set *nodes = NULL; + _cleanup_closedir_ DIR *dir = NULL; + struct dirent *dent; + Iterator i; + char *n; + int r; + + assert(udev); + + nodes = set_new(&string_hash_ops); + if (!nodes) + return -ENOMEM; + + e = udev_enumerate_new(udev); + if (!e) + return -ENOMEM; + + if (isempty(seat)) + seat = "seat0"; + + /* We can only match by one tag in libudev. We choose + * "uaccess" for that. If we could match for two tags here we + * could add the seat name as second match tag, but this would + * be hardly optimizable in libudev, and hence checking the + * second tag manually in our loop is a good solution. */ + r = udev_enumerate_add_match_tag(e, "uaccess"); + if (r < 0) + return r; + + r = udev_enumerate_add_match_is_initialized(e); + if (r < 0) + return r; + + r = udev_enumerate_scan_devices(e); + if (r < 0) + return r; + + first = udev_enumerate_get_list_entry(e); + udev_list_entry_foreach(item, first) { + _cleanup_udev_device_unref_ struct udev_device *d = NULL; + const char *node, *sn; + + d = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item)); + if (!d) + return -ENOMEM; + + sn = udev_device_get_property_value(d, "ID_SEAT"); + if (isempty(sn)) + sn = "seat0"; + + if (!streq(seat, sn)) + continue; + + node = udev_device_get_devnode(d); + /* In case people mistag devices with nodes, we need to ignore this */ + if (!node) + continue; + + n = strdup(node); + if (!n) + return -ENOMEM; + + log_debug("Found udev node %s for seat %s", n, seat); + r = set_consume(nodes, n); + if (r < 0) + return r; + } + + /* udev exports "dead" device nodes to allow module on-demand loading, + * these devices are not known to the kernel at this moment */ + dir = opendir("/run/udev/static_node-tags/uaccess"); + if (dir) { + FOREACH_DIRENT(dent, dir, return -errno) { + _cleanup_free_ char *unescaped_devname = NULL; + + if (cunescape(dent->d_name, UNESCAPE_RELAX, &unescaped_devname) < 0) + return -ENOMEM; + + n = strappend("/dev/", unescaped_devname); + if (!n) + return -ENOMEM; + + log_debug("Found static node %s for seat %s", n, seat); + r = set_consume(nodes, n); + if (r == -EEXIST) + continue; + if (r < 0) + return r; + } + } + + r = 0; + SET_FOREACH(n, nodes, i) { + int k; + + log_debug("Changing ACLs at %s for seat %s (uid "UID_FMT"→"UID_FMT"%s%s)", + n, seat, old_uid, new_uid, + del ? " del" : "", add ? " add" : ""); + + k = devnode_acl(n, flush, del, old_uid, add, new_uid); + if (k == -ENOENT) + log_debug("Device %s disappeared while setting ACLs", n); + else if (k < 0 && r == 0) + r = k; + } + + return r; +} diff --git a/src/grp-login/systemd-logind/logind-acl.h b/src/grp-login/systemd-logind/logind-acl.h new file mode 100644 index 0000000000..7d324ea90a --- /dev/null +++ b/src/grp-login/systemd-logind/logind-acl.h @@ -0,0 +1,56 @@ +#pragma once + +/*** + 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 Lesser General Public License as published by + the Free Software Foundation; either version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include + +#include + +#ifdef HAVE_ACL + +int devnode_acl(const char *path, + bool flush, + bool del, uid_t old_uid, + bool add, uid_t new_uid); + +int devnode_acl_all(struct udev *udev, + const char *seat, + bool flush, + bool del, uid_t old_uid, + bool add, uid_t new_uid); +#else + +static inline int devnode_acl(const char *path, + bool flush, + bool del, uid_t old_uid, + bool add, uid_t new_uid) { + return 0; +} + +static inline int devnode_acl_all(struct udev *udev, + const char *seat, + bool flush, + bool del, uid_t old_uid, + bool add, uid_t new_uid) { + return 0; +} + +#endif diff --git a/src/grp-login/systemd-logind/logind-action.c b/src/grp-login/systemd-logind/logind-action.c new file mode 100644 index 0000000000..0cfeab3fdb --- /dev/null +++ b/src/grp-login/systemd-logind/logind-action.c @@ -0,0 +1,179 @@ +/*** + 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 Lesser General Public License as published by + the Free Software Foundation; either version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include "basic/alloc-util.h" +#include "basic/formats-util.h" +#include "basic/process-util.h" +#include "basic/special.h" +#include "basic/string-table.h" +#include "basic/terminal-util.h" +#include "basic/user-util.h" +#include "sd-bus/bus-error.h" +#include "shared/bus-util.h" +#include "shared/conf-parser.h" +#include "shared/sleep-config.h" + +#include "logind-action.h" + +int manager_handle_action( + Manager *m, + InhibitWhat inhibit_key, + HandleAction handle, + bool ignore_inhibited, + bool is_edge) { + + static const char * const message_table[_HANDLE_ACTION_MAX] = { + [HANDLE_POWEROFF] = "Powering Off...", + [HANDLE_REBOOT] = "Rebooting...", + [HANDLE_HALT] = "Halting...", + [HANDLE_KEXEC] = "Rebooting via kexec...", + [HANDLE_SUSPEND] = "Suspending...", + [HANDLE_HIBERNATE] = "Hibernating...", + [HANDLE_HYBRID_SLEEP] = "Hibernating and suspending..." + }; + + static const char * const target_table[_HANDLE_ACTION_MAX] = { + [HANDLE_POWEROFF] = SPECIAL_POWEROFF_TARGET, + [HANDLE_REBOOT] = SPECIAL_REBOOT_TARGET, + [HANDLE_HALT] = SPECIAL_HALT_TARGET, + [HANDLE_KEXEC] = SPECIAL_KEXEC_TARGET, + [HANDLE_SUSPEND] = SPECIAL_SUSPEND_TARGET, + [HANDLE_HIBERNATE] = SPECIAL_HIBERNATE_TARGET, + [HANDLE_HYBRID_SLEEP] = SPECIAL_HYBRID_SLEEP_TARGET + }; + + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + InhibitWhat inhibit_operation; + Inhibitor *offending = NULL; + bool supported; + int r; + + assert(m); + + /* If the key handling is turned off, don't do anything */ + if (handle == HANDLE_IGNORE) { + log_debug("Refusing operation, as it is turned off."); + return 0; + } + + if (inhibit_key == INHIBIT_HANDLE_LID_SWITCH) { + /* If the last system suspend or startup is too close, + * let's not suspend for now, to give USB docking + * stations some time to settle so that we can + * properly watch its displays. */ + if (m->lid_switch_ignore_event_source) { + log_debug("Ignoring lid switch request, system startup or resume too close."); + return 0; + } + } + + /* If the key handling is inhibited, don't do anything */ + if (inhibit_key > 0) { + if (manager_is_inhibited(m, inhibit_key, INHIBIT_BLOCK, NULL, true, false, 0, NULL)) { + log_debug("Refusing operation, %s is inhibited.", inhibit_what_to_string(inhibit_key)); + return 0; + } + } + + /* Locking is handled differently from the rest. */ + if (handle == HANDLE_LOCK) { + + if (!is_edge) + return 0; + + log_info("Locking sessions..."); + session_send_lock_all(m, true); + return 1; + } + + if (handle == HANDLE_SUSPEND) + supported = can_sleep("suspend") > 0; + else if (handle == HANDLE_HIBERNATE) + supported = can_sleep("hibernate") > 0; + else if (handle == HANDLE_HYBRID_SLEEP) + supported = can_sleep("hybrid-sleep") > 0; + else if (handle == HANDLE_KEXEC) + supported = access(KEXEC, X_OK) >= 0; + else + supported = true; + + if (!supported) { + log_warning("Requested operation not supported, ignoring."); + return -EOPNOTSUPP; + } + + if (m->action_what) { + log_debug("Action already in progress, ignoring."); + return -EALREADY; + } + + inhibit_operation = handle == HANDLE_SUSPEND || handle == HANDLE_HIBERNATE || handle == HANDLE_HYBRID_SLEEP ? INHIBIT_SLEEP : INHIBIT_SHUTDOWN; + + /* If the actual operation is inhibited, warn and fail */ + if (!ignore_inhibited && + manager_is_inhibited(m, inhibit_operation, INHIBIT_BLOCK, NULL, false, false, 0, &offending)) { + _cleanup_free_ char *comm = NULL, *u = NULL; + + get_process_comm(offending->pid, &comm); + u = uid_to_name(offending->uid); + + /* If this is just a recheck of the lid switch then don't warn about anything */ + if (!is_edge) { + log_debug("Refusing operation, %s is inhibited by UID "UID_FMT"/%s, PID "PID_FMT"/%s.", + inhibit_what_to_string(inhibit_operation), + offending->uid, strna(u), + offending->pid, strna(comm)); + return 0; + } + + log_error("Refusing operation, %s is inhibited by UID "UID_FMT"/%s, PID "PID_FMT"/%s.", + inhibit_what_to_string(inhibit_operation), + offending->uid, strna(u), + offending->pid, strna(comm)); + + return -EPERM; + } + + log_info("%s", message_table[handle]); + + r = bus_manager_shutdown_or_sleep_now_or_later(m, target_table[handle], inhibit_operation, &error); + if (r < 0) { + log_error("Failed to execute operation: %s", bus_error_message(&error, r)); + return r; + } + + return 1; +} + +static const char* const handle_action_table[_HANDLE_ACTION_MAX] = { + [HANDLE_IGNORE] = "ignore", + [HANDLE_POWEROFF] = "poweroff", + [HANDLE_REBOOT] = "reboot", + [HANDLE_HALT] = "halt", + [HANDLE_KEXEC] = "kexec", + [HANDLE_SUSPEND] = "suspend", + [HANDLE_HIBERNATE] = "hibernate", + [HANDLE_HYBRID_SLEEP] = "hybrid-sleep", + [HANDLE_LOCK] = "lock" +}; + +DEFINE_STRING_TABLE_LOOKUP(handle_action, HandleAction); +DEFINE_CONFIG_PARSE_ENUM(config_parse_handle_action, handle_action, HandleAction, "Failed to parse handle action setting"); diff --git a/src/grp-login/systemd-logind/logind-action.h b/src/grp-login/systemd-logind/logind-action.h new file mode 100644 index 0000000000..39ba3cf0f6 --- /dev/null +++ b/src/grp-login/systemd-logind/logind-action.h @@ -0,0 +1,50 @@ +#pragma once + +/*** + 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 Lesser General Public License as published by + the Free Software Foundation; either version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +typedef enum HandleAction { + HANDLE_IGNORE, + HANDLE_POWEROFF, + HANDLE_REBOOT, + HANDLE_HALT, + HANDLE_KEXEC, + HANDLE_SUSPEND, + HANDLE_HIBERNATE, + HANDLE_HYBRID_SLEEP, + HANDLE_LOCK, + _HANDLE_ACTION_MAX, + _HANDLE_ACTION_INVALID = -1 +} HandleAction; + +#include "logind.h" + +#include "logind-inhibit.h" + +int manager_handle_action( + Manager *m, + InhibitWhat inhibit_key, + HandleAction handle, + bool ignore_inhibited, + bool is_edge); + +const char* handle_action_to_string(HandleAction h) _const_; +HandleAction handle_action_from_string(const char *s) _pure_; + +int config_parse_handle_action(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); diff --git a/src/grp-login/systemd-logind/logind-button.c b/src/grp-login/systemd-logind/logind-button.c new file mode 100644 index 0000000000..81c82117fc --- /dev/null +++ b/src/grp-login/systemd-logind/logind-button.c @@ -0,0 +1,290 @@ +/*** + 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 Lesser General Public License as published by + the Free Software Foundation; either version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include + +#include + +#include + +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/string-util.h" +#include "basic/util.h" + +#include "logind-button.h" + +Button* button_new(Manager *m, const char *name) { + Button *b; + + assert(m); + assert(name); + + b = new0(Button, 1); + if (!b) + return NULL; + + b->name = strdup(name); + if (!b->name) { + free(b); + return NULL; + } + + if (hashmap_put(m->buttons, b->name, b) < 0) { + free(b->name); + free(b); + return NULL; + } + + b->manager = m; + b->fd = -1; + + return b; +} + +void button_free(Button *b) { + assert(b); + + hashmap_remove(b->manager->buttons, b->name); + + sd_event_source_unref(b->io_event_source); + sd_event_source_unref(b->check_event_source); + + if (b->fd >= 0) + /* If the device has been unplugged close() returns + * ENODEV, let's ignore this, hence we don't use + * safe_close() */ + (void) close(b->fd); + + free(b->name); + free(b->seat); + free(b); +} + +int button_set_seat(Button *b, const char *sn) { + char *s; + + assert(b); + assert(sn); + + s = strdup(sn); + if (!s) + return -ENOMEM; + + free(b->seat); + b->seat = s; + + return 0; +} + +static void button_lid_switch_handle_action(Manager *manager, bool is_edge) { + HandleAction handle_action; + + assert(manager); + + /* If we are docked, handle the lid switch differently */ + if (manager_is_docked_or_external_displays(manager)) + handle_action = manager->handle_lid_switch_docked; + else + handle_action = manager->handle_lid_switch; + + manager_handle_action(manager, INHIBIT_HANDLE_LID_SWITCH, handle_action, manager->lid_switch_ignore_inhibited, is_edge); +} + +static int button_recheck(sd_event_source *e, void *userdata) { + Button *b = userdata; + + assert(b); + assert(b->lid_closed); + + button_lid_switch_handle_action(b->manager, false); + return 1; +} + +static int button_install_check_event_source(Button *b) { + int r; + assert(b); + + /* Install a post handler, so that we keep rechecking as long as the lid is closed. */ + + if (b->check_event_source) + return 0; + + r = sd_event_add_post(b->manager->event, &b->check_event_source, button_recheck, b); + if (r < 0) + return r; + + return sd_event_source_set_priority(b->check_event_source, SD_EVENT_PRIORITY_IDLE+1); +} + +static int button_dispatch(sd_event_source *s, int fd, uint32_t revents, void *userdata) { + Button *b = userdata; + struct input_event ev; + ssize_t l; + + assert(s); + assert(fd == b->fd); + assert(b); + + l = read(b->fd, &ev, sizeof(ev)); + if (l < 0) + return errno != EAGAIN ? -errno : 0; + if ((size_t) l < sizeof(ev)) + return -EIO; + + if (ev.type == EV_KEY && ev.value > 0) { + + switch (ev.code) { + + case KEY_POWER: + case KEY_POWER2: + log_struct(LOG_INFO, + LOG_MESSAGE("Power key pressed."), + LOG_MESSAGE_ID(SD_MESSAGE_POWER_KEY), + NULL); + + manager_handle_action(b->manager, INHIBIT_HANDLE_POWER_KEY, b->manager->handle_power_key, b->manager->power_key_ignore_inhibited, true); + break; + + /* The kernel is a bit confused here: + + KEY_SLEEP = suspend-to-ram, which everybody else calls "suspend" + KEY_SUSPEND = suspend-to-disk, which everybody else calls "hibernate" + */ + + case KEY_SLEEP: + log_struct(LOG_INFO, + LOG_MESSAGE("Suspend key pressed."), + LOG_MESSAGE_ID(SD_MESSAGE_SUSPEND_KEY), + NULL); + + manager_handle_action(b->manager, INHIBIT_HANDLE_SUSPEND_KEY, b->manager->handle_suspend_key, b->manager->suspend_key_ignore_inhibited, true); + break; + + case KEY_SUSPEND: + log_struct(LOG_INFO, + LOG_MESSAGE("Hibernate key pressed."), + LOG_MESSAGE_ID(SD_MESSAGE_HIBERNATE_KEY), + NULL); + + manager_handle_action(b->manager, INHIBIT_HANDLE_HIBERNATE_KEY, b->manager->handle_hibernate_key, b->manager->hibernate_key_ignore_inhibited, true); + break; + } + + } else if (ev.type == EV_SW && ev.value > 0) { + + if (ev.code == SW_LID) { + log_struct(LOG_INFO, + LOG_MESSAGE("Lid closed."), + LOG_MESSAGE_ID(SD_MESSAGE_LID_CLOSED), + NULL); + + b->lid_closed = true; + button_lid_switch_handle_action(b->manager, true); + button_install_check_event_source(b); + + } else if (ev.code == SW_DOCK) { + log_struct(LOG_INFO, + LOG_MESSAGE("System docked."), + LOG_MESSAGE_ID(SD_MESSAGE_SYSTEM_DOCKED), + NULL); + + b->docked = true; + } + + } else if (ev.type == EV_SW && ev.value == 0) { + + if (ev.code == SW_LID) { + log_struct(LOG_INFO, + LOG_MESSAGE("Lid opened."), + LOG_MESSAGE_ID(SD_MESSAGE_LID_OPENED), + NULL); + + b->lid_closed = false; + b->check_event_source = sd_event_source_unref(b->check_event_source); + + } else if (ev.code == SW_DOCK) { + log_struct(LOG_INFO, + LOG_MESSAGE("System undocked."), + LOG_MESSAGE_ID(SD_MESSAGE_SYSTEM_UNDOCKED), + NULL); + + b->docked = false; + } + } + + return 0; +} + +int button_open(Button *b) { + char *p, name[256]; + int r; + + assert(b); + + b->fd = safe_close(b->fd); + + p = strjoina("/dev/input/", b->name); + + b->fd = open(p, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK); + if (b->fd < 0) + return log_warning_errno(errno, "Failed to open %s: %m", b->name); + + if (ioctl(b->fd, EVIOCGNAME(sizeof(name)), name) < 0) { + r = log_error_errno(errno, "Failed to get input name: %m"); + goto fail; + } + + r = sd_event_add_io(b->manager->event, &b->io_event_source, b->fd, EPOLLIN, button_dispatch, b); + if (r < 0) { + log_error_errno(r, "Failed to add button event: %m"); + goto fail; + } + + log_info("Watching system buttons on /dev/input/%s (%s)", b->name, name); + + return 0; + +fail: + b->fd = safe_close(b->fd); + return r; +} + +int button_check_switches(Button *b) { + uint8_t switches[SW_MAX/8+1] = {}; + assert(b); + + if (b->fd < 0) + return -EINVAL; + + if (ioctl(b->fd, EVIOCGSW(sizeof(switches)), switches) < 0) + return -errno; + + b->lid_closed = (switches[SW_LID/8] >> (SW_LID % 8)) & 1; + b->docked = (switches[SW_DOCK/8] >> (SW_DOCK % 8)) & 1; + + if (b->lid_closed) + button_install_check_event_source(b); + + return 0; +} diff --git a/src/grp-login/systemd-logind/logind-button.h b/src/grp-login/systemd-logind/logind-button.h new file mode 100644 index 0000000000..f30cba2959 --- /dev/null +++ b/src/grp-login/systemd-logind/logind-button.h @@ -0,0 +1,44 @@ +#pragma once + +/*** + 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 Lesser General Public License as published by + the Free Software Foundation; either version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +typedef struct Button Button; + +#include "logind.h" + +struct Button { + Manager *manager; + + sd_event_source *io_event_source; + sd_event_source *check_event_source; + + char *name; + char *seat; + int fd; + + bool lid_closed; + bool docked; +}; + +Button* button_new(Manager *m, const char *name); +void button_free(Button*b); +int button_open(Button *b); +int button_set_seat(Button *b, const char *sn); +int button_check_switches(Button *b); diff --git a/src/grp-login/systemd-logind/logind-core.c b/src/grp-login/systemd-logind/logind-core.c new file mode 100644 index 0000000000..1d6475f8d7 --- /dev/null +++ b/src/grp-login/systemd-logind/logind-core.c @@ -0,0 +1,559 @@ +/*** + 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 Lesser General Public License as published by + the Free Software Foundation; either version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include + +#include + +#include "basic/alloc-util.h" +#include "basic/cgroup-util.h" +#include "basic/fd-util.h" +#include "basic/strv.h" +#include "basic/terminal-util.h" +#include "basic/user-util.h" +#include "logind.h" +#include "sd-bus/bus-error.h" +#include "shared/bus-util.h" +#include "shared/udev-util.h" + +int manager_add_device(Manager *m, const char *sysfs, bool master, Device **_device) { + Device *d; + + assert(m); + assert(sysfs); + + d = hashmap_get(m->devices, sysfs); + if (d) + /* we support adding master-flags, but not removing them */ + d->master = d->master || master; + else { + d = device_new(m, sysfs, master); + if (!d) + return -ENOMEM; + } + + if (_device) + *_device = d; + + return 0; +} + +int manager_add_seat(Manager *m, const char *id, Seat **_seat) { + Seat *s; + + assert(m); + assert(id); + + s = hashmap_get(m->seats, id); + if (!s) { + s = seat_new(m, id); + if (!s) + return -ENOMEM; + } + + if (_seat) + *_seat = s; + + return 0; +} + +int manager_add_session(Manager *m, const char *id, Session **_session) { + Session *s; + + assert(m); + assert(id); + + s = hashmap_get(m->sessions, id); + if (!s) { + s = session_new(m, id); + if (!s) + return -ENOMEM; + } + + if (_session) + *_session = s; + + return 0; +} + +int manager_add_user(Manager *m, uid_t uid, gid_t gid, const char *name, User **_user) { + User *u; + int r; + + assert(m); + assert(name); + + u = hashmap_get(m->users, UID_TO_PTR(uid)); + if (!u) { + r = user_new(&u, m, uid, gid, name); + if (r < 0) + return r; + } + + if (_user) + *_user = u; + + return 0; +} + +int manager_add_user_by_name(Manager *m, const char *name, User **_user) { + uid_t uid; + gid_t gid; + int r; + + assert(m); + assert(name); + + r = get_user_creds(&name, &uid, &gid, NULL, NULL); + if (r < 0) + return r; + + return manager_add_user(m, uid, gid, name, _user); +} + +int manager_add_user_by_uid(Manager *m, uid_t uid, User **_user) { + struct passwd *p; + + assert(m); + + errno = 0; + p = getpwuid(uid); + if (!p) + return errno > 0 ? -errno : -ENOENT; + + return manager_add_user(m, uid, p->pw_gid, p->pw_name, _user); +} + +int manager_add_inhibitor(Manager *m, const char* id, Inhibitor **_inhibitor) { + Inhibitor *i; + + assert(m); + assert(id); + + i = hashmap_get(m->inhibitors, id); + if (i) { + if (_inhibitor) + *_inhibitor = i; + + return 0; + } + + i = inhibitor_new(m, id); + if (!i) + return -ENOMEM; + + if (_inhibitor) + *_inhibitor = i; + + return 0; +} + +int manager_add_button(Manager *m, const char *name, Button **_button) { + Button *b; + + assert(m); + assert(name); + + b = hashmap_get(m->buttons, name); + if (!b) { + b = button_new(m, name); + if (!b) + return -ENOMEM; + } + + if (_button) + *_button = b; + + return 0; +} + +int manager_process_seat_device(Manager *m, struct udev_device *d) { + Device *device; + int r; + + assert(m); + + if (streq_ptr(udev_device_get_action(d), "remove")) { + + device = hashmap_get(m->devices, udev_device_get_syspath(d)); + if (!device) + return 0; + + seat_add_to_gc_queue(device->seat); + device_free(device); + + } else { + const char *sn; + Seat *seat = NULL; + bool master; + + sn = udev_device_get_property_value(d, "ID_SEAT"); + if (isempty(sn)) + sn = "seat0"; + + if (!seat_name_is_valid(sn)) { + log_warning("Device with invalid seat name %s found, ignoring.", sn); + return 0; + } + + seat = hashmap_get(m->seats, sn); + master = udev_device_has_tag(d, "master-of-seat"); + + /* Ignore non-master devices for unknown seats */ + if (!master && !seat) + return 0; + + r = manager_add_device(m, udev_device_get_syspath(d), master, &device); + if (r < 0) + return r; + + if (!seat) { + r = manager_add_seat(m, sn, &seat); + if (r < 0) { + if (!device->seat) + device_free(device); + + return r; + } + } + + device_attach(device, seat); + seat_start(seat); + } + + return 0; +} + +int manager_process_button_device(Manager *m, struct udev_device *d) { + Button *b; + + int r; + + assert(m); + + if (streq_ptr(udev_device_get_action(d), "remove")) { + + b = hashmap_get(m->buttons, udev_device_get_sysname(d)); + if (!b) + return 0; + + button_free(b); + + } else { + const char *sn; + + r = manager_add_button(m, udev_device_get_sysname(d), &b); + if (r < 0) + return r; + + sn = udev_device_get_property_value(d, "ID_SEAT"); + if (isempty(sn)) + sn = "seat0"; + + button_set_seat(b, sn); + button_open(b); + } + + return 0; +} + +int manager_get_session_by_pid(Manager *m, pid_t pid, Session **session) { + _cleanup_free_ char *unit = NULL; + Session *s; + int r; + + assert(m); + + if (pid < 1) + return -EINVAL; + + r = cg_pid_get_unit(pid, &unit); + if (r < 0) + return 0; + + s = hashmap_get(m->session_units, unit); + if (!s) + return 0; + + if (session) + *session = s; + return 1; +} + +int manager_get_user_by_pid(Manager *m, pid_t pid, User **user) { + _cleanup_free_ char *unit = NULL; + User *u; + int r; + + assert(m); + assert(user); + + if (pid < 1) + return -EINVAL; + + r = cg_pid_get_slice(pid, &unit); + if (r < 0) + return 0; + + u = hashmap_get(m->user_units, unit); + if (!u) + return 0; + + *user = u; + return 1; +} + +int manager_get_idle_hint(Manager *m, dual_timestamp *t) { + Session *s; + bool idle_hint; + dual_timestamp ts = DUAL_TIMESTAMP_NULL; + Iterator i; + + assert(m); + + idle_hint = !manager_is_inhibited(m, INHIBIT_IDLE, INHIBIT_BLOCK, t, false, false, 0, NULL); + + HASHMAP_FOREACH(s, m->sessions, i) { + dual_timestamp k; + int ih; + + ih = session_get_idle_hint(s, &k); + if (ih < 0) + return ih; + + if (!ih) { + if (!idle_hint) { + if (k.monotonic < ts.monotonic) + ts = k; + } else { + idle_hint = false; + ts = k; + } + } else if (idle_hint) { + + if (k.monotonic > ts.monotonic) + ts = k; + } + } + + if (t) + *t = ts; + + return idle_hint; +} + +bool manager_shall_kill(Manager *m, const char *user) { + assert(m); + assert(user); + + if (!m->kill_exclude_users && streq(user, "root")) + return false; + + if (strv_contains(m->kill_exclude_users, user)) + return false; + + if (!strv_isempty(m->kill_only_users)) + return strv_contains(m->kill_only_users, user); + + return m->kill_user_processes; +} + +static int vt_is_busy(unsigned int vtnr) { + struct vt_stat vt_stat; + int r = 0; + _cleanup_close_ int fd; + + assert(vtnr >= 1); + + /* We explicitly open /dev/tty1 here instead of /dev/tty0. If + * we'd open the latter we'd open the foreground tty which + * hence would be unconditionally busy. By opening /dev/tty1 + * we avoid this. Since tty1 is special and needs to be an + * explicitly loaded getty or DM this is safe. */ + + fd = open_terminal("/dev/tty1", O_RDWR|O_NOCTTY|O_CLOEXEC); + if (fd < 0) + return -errno; + + if (ioctl(fd, VT_GETSTATE, &vt_stat) < 0) + r = -errno; + else + r = !!(vt_stat.v_state & (1 << vtnr)); + + return r; +} + +int manager_spawn_autovt(Manager *m, unsigned int vtnr) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + char name[sizeof("autovt@tty.service") + DECIMAL_STR_MAX(unsigned int)]; + int r; + + assert(m); + assert(vtnr >= 1); + + if (vtnr > m->n_autovts && + vtnr != m->reserve_vt) + return 0; + + if (vtnr != m->reserve_vt) { + /* If this is the reserved TTY, we'll start the getty + * on it in any case, but otherwise only if it is not + * busy. */ + + r = vt_is_busy(vtnr); + if (r < 0) + return r; + else if (r > 0) + return -EBUSY; + } + + snprintf(name, sizeof(name), "autovt@tty%u.service", vtnr); + r = sd_bus_call_method( + m->bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "StartUnit", + &error, + NULL, + "ss", name, "fail"); + if (r < 0) + log_error("Failed to start %s: %s", name, bus_error_message(&error, r)); + + return r; +} + +static bool manager_is_docked(Manager *m) { + Iterator i; + Button *b; + + HASHMAP_FOREACH(b, m->buttons, i) + if (b->docked) + return true; + + return false; +} + +static int manager_count_external_displays(Manager *m) { + _cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL; + struct udev_list_entry *item = NULL, *first = NULL; + int r; + int n = 0; + + e = udev_enumerate_new(m->udev); + if (!e) + return -ENOMEM; + + r = udev_enumerate_add_match_subsystem(e, "drm"); + if (r < 0) + return r; + + r = udev_enumerate_scan_devices(e); + if (r < 0) + return r; + + first = udev_enumerate_get_list_entry(e); + udev_list_entry_foreach(item, first) { + _cleanup_udev_device_unref_ struct udev_device *d = NULL; + struct udev_device *p; + const char *status, *enabled, *dash, *nn, *i; + bool external = false; + + d = udev_device_new_from_syspath(m->udev, udev_list_entry_get_name(item)); + if (!d) + return -ENOMEM; + + p = udev_device_get_parent(d); + if (!p) + continue; + + /* If the parent shares the same subsystem as the + * device we are looking at then it is a connector, + * which is what we are interested in. */ + if (!streq_ptr(udev_device_get_subsystem(p), "drm")) + continue; + + nn = udev_device_get_sysname(d); + if (!nn) + continue; + + /* Ignore internal displays: the type is encoded in + * the sysfs name, as the second dash seperated item + * (the first is the card name, the last the connector + * number). We implement a whitelist of external + * displays here, rather than a whitelist, to ensure + * we don't block suspends too eagerly. */ + dash = strchr(nn, '-'); + if (!dash) + continue; + + dash++; + FOREACH_STRING(i, "VGA-", "DVI-I-", "DVI-D-", "DVI-A-" + "Composite-", "SVIDEO-", "Component-", + "DIN-", "DP-", "HDMI-A-", "HDMI-B-", "TV-") { + + if (startswith(dash, i)) { + external = true; + break; + } + } + if (!external) + continue; + + /* Ignore ports that are not enabled */ + enabled = udev_device_get_sysattr_value(d, "enabled"); + if (!enabled) + continue; + if (!streq_ptr(enabled, "enabled")) + continue; + + /* We count any connector which is not explicitly + * "disconnected" as connected. */ + status = udev_device_get_sysattr_value(d, "status"); + if (!streq_ptr(status, "disconnected")) + n++; + } + + return n; +} + +bool manager_is_docked_or_external_displays(Manager *m) { + int n; + + /* If we are docked don't react to lid closing */ + if (manager_is_docked(m)) { + log_debug("System is docked."); + return true; + } + + /* If we have more than one display connected, + * assume that we are docked. */ + n = manager_count_external_displays(m); + if (n < 0) + log_warning_errno(n, "Display counting failed: %m"); + else if (n >= 1) { + log_debug("External (%i) displays connected.", n); + return true; + } + + return false; +} diff --git a/src/grp-login/systemd-logind/logind-dbus.c b/src/grp-login/systemd-logind/logind-dbus.c new file mode 100644 index 0000000000..cb51e43a5c --- /dev/null +++ b/src/grp-login/systemd-logind/logind-dbus.c @@ -0,0 +1,3169 @@ +/*** + 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 Lesser General Public License as published by + the Free Software Foundation; either version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include + +#include + +#include "basic/alloc-util.h" +#include "basic/audit-util.h" +#include "basic/dirent-util.h" +#include "basic/escape.h" +#include "basic/fd-util.h" +#include "basic/fileio-label.h" +#include "basic/formats-util.h" +#include "basic/fs-util.h" +#include "basic/mkdir.h" +#include "basic/path-util.h" +#include "basic/process-util.h" +#include "basic/selinux-util.h" +#include "basic/special.h" +#include "basic/strv.h" +#include "basic/terminal-util.h" +#include "basic/unit-name.h" +#include "basic/user-util.h" +#include "logind.h" +#include "sd-bus/bus-common-errors.h" +#include "sd-bus/bus-error.h" +#include "shared/bus-util.h" +#include "shared/efivars.h" +#include "shared/sleep-config.h" +#include "shared/udev-util.h" +#include "shared/utmp-wtmp.h" + +int manager_get_session_from_creds(Manager *m, sd_bus_message *message, const char *name, sd_bus_error *error, Session **ret) { + _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; + Session *session; + int r; + + assert(m); + assert(message); + assert(ret); + + if (isempty(name)) { + r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_SESSION|SD_BUS_CREDS_AUGMENT, &creds); + if (r < 0) + return r; + + r = sd_bus_creds_get_session(creds, &name); + if (r < 0) + return r; + } + + session = hashmap_get(m->sessions, name); + if (!session) + return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_SESSION, "No session '%s' known", name); + + *ret = session; + return 0; +} + +int manager_get_user_from_creds(Manager *m, sd_bus_message *message, uid_t uid, sd_bus_error *error, User **ret) { + User *user; + int r; + + assert(m); + assert(message); + assert(ret); + + if (uid == UID_INVALID) { + _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; + + /* Note that we get the owner UID of the session, not the actual client UID here! */ + r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_OWNER_UID|SD_BUS_CREDS_AUGMENT, &creds); + if (r < 0) + return r; + + r = sd_bus_creds_get_owner_uid(creds, &uid); + if (r < 0) + return r; + } + + user = hashmap_get(m->users, UID_TO_PTR(uid)); + if (!user) + return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_USER, "No user "UID_FMT" known or logged in", uid); + + *ret = user; + return 0; +} + +int manager_get_seat_from_creds(Manager *m, sd_bus_message *message, const char *name, sd_bus_error *error, Seat **ret) { + Seat *seat; + int r; + + assert(m); + assert(message); + assert(ret); + + if (isempty(name)) { + Session *session; + + r = manager_get_session_from_creds(m, message, NULL, error, &session); + if (r < 0) + return r; + + seat = session->seat; + if (!seat) + return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_SEAT, "Session has no seat."); + } else { + seat = hashmap_get(m->seats, name); + if (!seat) + return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_SEAT, "No seat '%s' known", name); + } + + *ret = seat; + return 0; +} + +static int property_get_idle_hint( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Manager *m = userdata; + + assert(bus); + assert(reply); + assert(m); + + return sd_bus_message_append(reply, "b", manager_get_idle_hint(m, NULL) > 0); +} + +static int property_get_idle_since_hint( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Manager *m = userdata; + dual_timestamp t = DUAL_TIMESTAMP_NULL; + + assert(bus); + assert(reply); + assert(m); + + manager_get_idle_hint(m, &t); + + return sd_bus_message_append(reply, "t", streq(property, "IdleSinceHint") ? t.realtime : t.monotonic); +} + +static int property_get_inhibited( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Manager *m = userdata; + InhibitWhat w; + + assert(bus); + assert(reply); + assert(m); + + w = manager_inhibit_what(m, streq(property, "BlockInhibited") ? INHIBIT_BLOCK : INHIBIT_DELAY); + + return sd_bus_message_append(reply, "s", inhibit_what_to_string(w)); +} + +static int property_get_preparing( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Manager *m = userdata; + bool b; + + assert(bus); + assert(reply); + assert(m); + + if (streq(property, "PreparingForShutdown")) + b = !!(m->action_what & INHIBIT_SHUTDOWN); + else + b = !!(m->action_what & INHIBIT_SLEEP); + + return sd_bus_message_append(reply, "b", b); +} + +static int property_get_scheduled_shutdown( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Manager *m = userdata; + int r; + + assert(bus); + assert(reply); + assert(m); + + r = sd_bus_message_open_container(reply, 'r', "st"); + if (r < 0) + return r; + + r = sd_bus_message_append(reply, "st", m->scheduled_shutdown_type, m->scheduled_shutdown_timeout); + if (r < 0) + return r; + + return sd_bus_message_close_container(reply); +} + +static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_handle_action, handle_action, HandleAction); + +static int property_get_docked( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Manager *m = userdata; + + assert(bus); + assert(reply); + assert(m); + + return sd_bus_message_append(reply, "b", manager_is_docked_or_external_displays(m)); +} + +static int property_get_current_sessions( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Manager *m = userdata; + + assert(bus); + assert(reply); + assert(m); + + return sd_bus_message_append(reply, "t", (uint64_t) hashmap_size(m->sessions)); +} + +static int property_get_current_inhibitors( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Manager *m = userdata; + + assert(bus); + assert(reply); + assert(m); + + return sd_bus_message_append(reply, "t", (uint64_t) hashmap_size(m->inhibitors)); +} + +static int method_get_session(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_free_ char *p = NULL; + Manager *m = userdata; + const char *name; + Session *session; + int r; + + assert(message); + assert(m); + + r = sd_bus_message_read(message, "s", &name); + if (r < 0) + return r; + + r = manager_get_session_from_creds(m, message, name, error, &session); + if (r < 0) + return r; + + p = session_bus_path(session); + if (!p) + return -ENOMEM; + + return sd_bus_reply_method_return(message, "o", p); +} + +static int method_get_session_by_pid(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_free_ char *p = NULL; + Session *session = NULL; + Manager *m = userdata; + pid_t pid; + int r; + + assert(message); + assert(m); + + assert_cc(sizeof(pid_t) == sizeof(uint32_t)); + + r = sd_bus_message_read(message, "u", &pid); + if (r < 0) + return r; + if (pid < 0) + return -EINVAL; + + if (pid == 0) { + r = manager_get_session_from_creds(m, message, NULL, error, &session); + if (r < 0) + return r; + } else { + r = manager_get_session_by_pid(m, pid, &session); + if (r < 0) + return r; + + if (!session) + return sd_bus_error_setf(error, BUS_ERROR_NO_SESSION_FOR_PID, "PID "PID_FMT" does not belong to any known session", pid); + } + + p = session_bus_path(session); + if (!p) + return -ENOMEM; + + return sd_bus_reply_method_return(message, "o", p); +} + +static int method_get_user(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_free_ char *p = NULL; + Manager *m = userdata; + uint32_t uid; + User *user; + int r; + + assert(message); + assert(m); + + r = sd_bus_message_read(message, "u", &uid); + if (r < 0) + return r; + + r = manager_get_user_from_creds(m, message, uid, error, &user); + if (r < 0) + return r; + + p = user_bus_path(user); + if (!p) + return -ENOMEM; + + return sd_bus_reply_method_return(message, "o", p); +} + +static int method_get_user_by_pid(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_free_ char *p = NULL; + Manager *m = userdata; + User *user = NULL; + pid_t pid; + int r; + + assert(message); + assert(m); + + assert_cc(sizeof(pid_t) == sizeof(uint32_t)); + + r = sd_bus_message_read(message, "u", &pid); + if (r < 0) + return r; + if (pid < 0) + return -EINVAL; + + if (pid == 0) { + r = manager_get_user_from_creds(m, message, UID_INVALID, error, &user); + if (r < 0) + return r; + } else { + r = manager_get_user_by_pid(m, pid, &user); + if (r < 0) + return r; + if (!user) + return sd_bus_error_setf(error, BUS_ERROR_NO_USER_FOR_PID, "PID "PID_FMT" does not belong to any known or logged in user", pid); + } + + p = user_bus_path(user); + if (!p) + return -ENOMEM; + + return sd_bus_reply_method_return(message, "o", p); +} + +static int method_get_seat(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_free_ char *p = NULL; + Manager *m = userdata; + const char *name; + Seat *seat; + int r; + + assert(message); + assert(m); + + r = sd_bus_message_read(message, "s", &name); + if (r < 0) + return r; + + r = manager_get_seat_from_creds(m, message, name, error, &seat); + if (r < 0) + return r; + + p = seat_bus_path(seat); + if (!p) + return -ENOMEM; + + return sd_bus_reply_method_return(message, "o", p); +} + +static int method_list_sessions(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + Manager *m = userdata; + Session *session; + Iterator i; + int r; + + assert(message); + assert(m); + + r = sd_bus_message_new_method_return(message, &reply); + if (r < 0) + return r; + + r = sd_bus_message_open_container(reply, 'a', "(susso)"); + if (r < 0) + return r; + + HASHMAP_FOREACH(session, m->sessions, i) { + _cleanup_free_ char *p = NULL; + + p = session_bus_path(session); + if (!p) + return -ENOMEM; + + r = sd_bus_message_append(reply, "(susso)", + session->id, + (uint32_t) session->user->uid, + session->user->name, + session->seat ? session->seat->id : "", + p); + if (r < 0) + return r; + } + + r = sd_bus_message_close_container(reply); + if (r < 0) + return r; + + return sd_bus_send(NULL, reply, NULL); +} + +static int method_list_users(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + Manager *m = userdata; + User *user; + Iterator i; + int r; + + assert(message); + assert(m); + + r = sd_bus_message_new_method_return(message, &reply); + if (r < 0) + return r; + + r = sd_bus_message_open_container(reply, 'a', "(uso)"); + if (r < 0) + return r; + + HASHMAP_FOREACH(user, m->users, i) { + _cleanup_free_ char *p = NULL; + + p = user_bus_path(user); + if (!p) + return -ENOMEM; + + r = sd_bus_message_append(reply, "(uso)", + (uint32_t) user->uid, + user->name, + p); + if (r < 0) + return r; + } + + r = sd_bus_message_close_container(reply); + if (r < 0) + return r; + + return sd_bus_send(NULL, reply, NULL); +} + +static int method_list_seats(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + Manager *m = userdata; + Seat *seat; + Iterator i; + int r; + + assert(message); + assert(m); + + r = sd_bus_message_new_method_return(message, &reply); + if (r < 0) + return r; + + r = sd_bus_message_open_container(reply, 'a', "(so)"); + if (r < 0) + return r; + + HASHMAP_FOREACH(seat, m->seats, i) { + _cleanup_free_ char *p = NULL; + + p = seat_bus_path(seat); + if (!p) + return -ENOMEM; + + r = sd_bus_message_append(reply, "(so)", seat->id, p); + if (r < 0) + return r; + } + + r = sd_bus_message_close_container(reply); + if (r < 0) + return r; + + return sd_bus_send(NULL, reply, NULL); +} + +static int method_list_inhibitors(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + Manager *m = userdata; + Inhibitor *inhibitor; + Iterator i; + int r; + + assert(message); + assert(m); + + r = sd_bus_message_new_method_return(message, &reply); + if (r < 0) + return r; + + r = sd_bus_message_open_container(reply, 'a', "(ssssuu)"); + if (r < 0) + return r; + + HASHMAP_FOREACH(inhibitor, m->inhibitors, i) { + + r = sd_bus_message_append(reply, "(ssssuu)", + strempty(inhibit_what_to_string(inhibitor->what)), + strempty(inhibitor->who), + strempty(inhibitor->why), + strempty(inhibit_mode_to_string(inhibitor->mode)), + (uint32_t) inhibitor->uid, + (uint32_t) inhibitor->pid); + if (r < 0) + return r; + } + + r = sd_bus_message_close_container(reply); + if (r < 0) + return r; + + return sd_bus_send(NULL, reply, NULL); +} + +static int method_create_session(sd_bus_message *message, void *userdata, sd_bus_error *error) { + const char *service, *type, *class, *cseat, *tty, *display, *remote_user, *remote_host, *desktop; + uint32_t audit_id = 0; + _cleanup_free_ char *id = NULL; + Session *session = NULL; + Manager *m = userdata; + User *user = NULL; + Seat *seat = NULL; + pid_t leader; + uid_t uid; + int remote; + uint32_t vtnr = 0; + SessionType t; + SessionClass c; + int r; + + assert(message); + assert(m); + + assert_cc(sizeof(pid_t) == sizeof(uint32_t)); + assert_cc(sizeof(uid_t) == sizeof(uint32_t)); + + r = sd_bus_message_read(message, "uusssssussbss", &uid, &leader, &service, &type, &class, &desktop, &cseat, &vtnr, &tty, &display, &remote, &remote_user, &remote_host); + if (r < 0) + return r; + + if (!uid_is_valid(uid)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid UID"); + if (leader < 0 || leader == 1) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid leader PID"); + + if (isempty(type)) + t = _SESSION_TYPE_INVALID; + else { + t = session_type_from_string(type); + if (t < 0) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid session type %s", type); + } + + if (isempty(class)) + c = _SESSION_CLASS_INVALID; + else { + c = session_class_from_string(class); + if (c < 0) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid session class %s", class); + } + + if (isempty(desktop)) + desktop = NULL; + else { + if (!string_is_safe(desktop)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid desktop string %s", desktop); + } + + if (isempty(cseat)) + seat = NULL; + else { + seat = hashmap_get(m->seats, cseat); + if (!seat) + return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_SEAT, "No seat '%s' known", cseat); + } + + if (tty_is_vc(tty)) { + int v; + + if (!seat) + seat = m->seat0; + else if (seat != m->seat0) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "TTY %s is virtual console but seat %s is not seat0", tty, seat->id); + + v = vtnr_from_tty(tty); + if (v <= 0) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Cannot determine VT number from virtual console TTY %s", tty); + + if (!vtnr) + vtnr = (uint32_t) v; + else if (vtnr != (uint32_t) v) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Specified TTY and VT number do not match"); + + } else if (tty_is_console(tty)) { + + if (!seat) + seat = m->seat0; + else if (seat != m->seat0) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Console TTY specified but seat is not seat0"); + + if (vtnr != 0) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Console TTY specified but VT number is not 0"); + } + + if (seat) { + if (seat_has_vts(seat)) { + if (!vtnr || vtnr > 63) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "VT number out of range"); + } else { + if (vtnr != 0) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Seat has no VTs but VT number not 0"); + } + } + + r = sd_bus_message_enter_container(message, 'a', "(sv)"); + if (r < 0) + return r; + + if (t == _SESSION_TYPE_INVALID) { + if (!isempty(display)) + t = SESSION_X11; + else if (!isempty(tty)) + t = SESSION_TTY; + else + t = SESSION_UNSPECIFIED; + } + + if (c == _SESSION_CLASS_INVALID) { + if (t == SESSION_UNSPECIFIED) + c = SESSION_BACKGROUND; + else + c = SESSION_USER; + } + + if (leader == 0) { + _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; + + r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_PID, &creds); + if (r < 0) + return r; + + r = sd_bus_creds_get_pid(creds, (pid_t*) &leader); + if (r < 0) + return r; + } + + r = manager_get_session_by_pid(m, leader, NULL); + if (r > 0) + return sd_bus_error_setf(error, BUS_ERROR_SESSION_BUSY, "Already running in a session"); + + /* + * Old gdm and lightdm start the user-session on the same VT as + * the greeter session. But they destroy the greeter session + * after the user-session and want the user-session to take + * over the VT. We need to support this for + * backwards-compatibility, so make sure we allow new sessions + * on a VT that a greeter is running on. Furthermore, to allow + * re-logins, we have to allow a greeter to take over a used VT for + * the exact same reasons. + */ + if (c != SESSION_GREETER && + vtnr > 0 && + vtnr < m->seat0->position_count && + m->seat0->positions[vtnr] && + m->seat0->positions[vtnr]->class != SESSION_GREETER) + return sd_bus_error_setf(error, BUS_ERROR_SESSION_BUSY, "Already occupied by a session"); + + if (hashmap_size(m->sessions) >= m->sessions_max) + return sd_bus_error_setf(error, SD_BUS_ERROR_LIMITS_EXCEEDED, "Maximum number of sessions (%" PRIu64 ") reached, refusing further sessions.", m->sessions_max); + + audit_session_from_pid(leader, &audit_id); + if (audit_id > 0) { + /* Keep our session IDs and the audit session IDs in sync */ + + if (asprintf(&id, "%"PRIu32, audit_id) < 0) + return -ENOMEM; + + /* Wut? There's already a session by this name and we + * didn't find it above? Weird, then let's not trust + * the audit data and let's better register a new + * ID */ + if (hashmap_get(m->sessions, id)) { + log_warning("Existing logind session ID %s used by new audit session, ignoring", id); + audit_id = 0; + + id = mfree(id); + } + } + + if (!id) { + do { + id = mfree(id); + + if (asprintf(&id, "c%lu", ++m->session_counter) < 0) + return -ENOMEM; + + } while (hashmap_get(m->sessions, id)); + } + + r = manager_add_user_by_uid(m, uid, &user); + if (r < 0) + goto fail; + + r = manager_add_session(m, id, &session); + if (r < 0) + goto fail; + + session_set_user(session, user); + + session->leader = leader; + session->audit_id = audit_id; + session->type = t; + session->class = c; + session->remote = remote; + session->vtnr = vtnr; + + if (!isempty(tty)) { + session->tty = strdup(tty); + if (!session->tty) { + r = -ENOMEM; + goto fail; + } + } + + if (!isempty(display)) { + session->display = strdup(display); + if (!session->display) { + r = -ENOMEM; + goto fail; + } + } + + if (!isempty(remote_user)) { + session->remote_user = strdup(remote_user); + if (!session->remote_user) { + r = -ENOMEM; + goto fail; + } + } + + if (!isempty(remote_host)) { + session->remote_host = strdup(remote_host); + if (!session->remote_host) { + r = -ENOMEM; + goto fail; + } + } + + if (!isempty(service)) { + session->service = strdup(service); + if (!session->service) { + r = -ENOMEM; + goto fail; + } + } + + if (!isempty(desktop)) { + session->desktop = strdup(desktop); + if (!session->desktop) { + r = -ENOMEM; + goto fail; + } + } + + if (seat) { + r = seat_attach_session(seat, session); + if (r < 0) + goto fail; + } + + r = session_start(session); + if (r < 0) + goto fail; + + session->create_message = sd_bus_message_ref(message); + + /* Now, let's wait until the slice unit and stuff got + * created. We send the reply back from + * session_send_create_reply(). */ + + return 1; + +fail: + if (session) + session_add_to_gc_queue(session); + + if (user) + user_add_to_gc_queue(user); + + return r; +} + +static int method_release_session(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + Session *session; + const char *name; + int r; + + assert(message); + assert(m); + + r = sd_bus_message_read(message, "s", &name); + if (r < 0) + return r; + + r = manager_get_session_from_creds(m, message, name, error, &session); + if (r < 0) + return r; + + r = session_release(session); + if (r < 0) + return r; + + return sd_bus_reply_method_return(message, NULL); +} + +static int method_activate_session(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + Session *session; + const char *name; + int r; + + assert(message); + assert(m); + + r = sd_bus_message_read(message, "s", &name); + if (r < 0) + return r; + + r = manager_get_session_from_creds(m, message, name, error, &session); + if (r < 0) + return r; + + return bus_session_method_activate(message, session, error); +} + +static int method_activate_session_on_seat(sd_bus_message *message, void *userdata, sd_bus_error *error) { + const char *session_name, *seat_name; + Manager *m = userdata; + Session *session; + Seat *seat; + int r; + + assert(message); + assert(m); + + /* Same as ActivateSession() but refuses to work if + * the seat doesn't match */ + + r = sd_bus_message_read(message, "ss", &session_name, &seat_name); + if (r < 0) + return r; + + r = manager_get_session_from_creds(m, message, session_name, error, &session); + if (r < 0) + return r; + + r = manager_get_seat_from_creds(m, message, seat_name, error, &seat); + if (r < 0) + return r; + + if (session->seat != seat) + return sd_bus_error_setf(error, BUS_ERROR_SESSION_NOT_ON_SEAT, "Session %s not on seat %s", session_name, seat_name); + + r = session_activate(session); + if (r < 0) + return r; + + return sd_bus_reply_method_return(message, NULL); +} + +static int method_lock_session(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + Session *session; + const char *name; + int r; + + assert(message); + assert(m); + + r = sd_bus_message_read(message, "s", &name); + if (r < 0) + return r; + + r = manager_get_session_from_creds(m, message, name, error, &session); + if (r < 0) + return r; + + return bus_session_method_lock(message, session, error); +} + +static int method_lock_sessions(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + int r; + + assert(message); + assert(m); + + r = bus_verify_polkit_async( + message, + CAP_SYS_ADMIN, + "org.freedesktop.login1.lock-sessions", + NULL, + false, + UID_INVALID, + &m->polkit_registry, + error); + if (r < 0) + return r; + if (r == 0) + return 1; /* Will call us back */ + + r = session_send_lock_all(m, streq(sd_bus_message_get_member(message), "LockSessions")); + if (r < 0) + return r; + + return sd_bus_reply_method_return(message, NULL); +} + +static int method_kill_session(sd_bus_message *message, void *userdata, sd_bus_error *error) { + const char *name; + Manager *m = userdata; + Session *session; + int r; + + assert(message); + assert(m); + + r = sd_bus_message_read(message, "s", &name); + if (r < 0) + return r; + + r = manager_get_session_from_creds(m, message, name, error, &session); + if (r < 0) + return r; + + return bus_session_method_kill(message, session, error); +} + +static int method_kill_user(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + uint32_t uid; + User *user; + int r; + + assert(message); + assert(m); + + r = sd_bus_message_read(message, "u", &uid); + if (r < 0) + return r; + + r = manager_get_user_from_creds(m, message, uid, error, &user); + if (r < 0) + return r; + + return bus_user_method_kill(message, user, error); +} + +static int method_terminate_session(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + const char *name; + Session *session; + int r; + + assert(message); + assert(m); + + r = sd_bus_message_read(message, "s", &name); + if (r < 0) + return r; + + r = manager_get_session_from_creds(m, message, name, error, &session); + if (r < 0) + return r; + + return bus_session_method_terminate(message, session, error); +} + +static int method_terminate_user(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + uint32_t uid; + User *user; + int r; + + assert(message); + assert(m); + + r = sd_bus_message_read(message, "u", &uid); + if (r < 0) + return r; + + r = manager_get_user_from_creds(m, message, uid, error, &user); + if (r < 0) + return r; + + return bus_user_method_terminate(message, user, error); +} + +static int method_terminate_seat(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + const char *name; + Seat *seat; + int r; + + assert(message); + assert(m); + + r = sd_bus_message_read(message, "s", &name); + if (r < 0) + return r; + + r = manager_get_seat_from_creds(m, message, name, error, &seat); + if (r < 0) + return r; + + return bus_seat_method_terminate(message, seat, error); +} + +static int method_set_user_linger(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_free_ char *cc = NULL; + Manager *m = userdata; + int r, b, interactive; + struct passwd *pw; + const char *path; + uint32_t uid; + bool self = false; + + assert(message); + assert(m); + + r = sd_bus_message_read(message, "ubb", &uid, &b, &interactive); + if (r < 0) + return r; + + if (uid == UID_INVALID) { + _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; + + /* Note that we get the owner UID of the session, not the actual client UID here! */ + r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_OWNER_UID|SD_BUS_CREDS_AUGMENT, &creds); + if (r < 0) + return r; + + r = sd_bus_creds_get_owner_uid(creds, &uid); + if (r < 0) + return r; + + self = true; + + } else if (!uid_is_valid(uid)) + return -EINVAL; + + errno = 0; + pw = getpwuid(uid); + if (!pw) + return errno > 0 ? -errno : -ENOENT; + + r = bus_verify_polkit_async( + message, + CAP_SYS_ADMIN, + self ? "org.freedesktop.login1.set-self-linger" : "org.freedesktop.login1.set-user-linger", + NULL, + interactive, + UID_INVALID, + &m->polkit_registry, + error); + if (r < 0) + return r; + if (r == 0) + return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ + + mkdir_p_label("/var/lib/systemd", 0755); + + r = mkdir_safe_label("/var/lib/systemd/linger", 0755, 0, 0); + if (r < 0) + return r; + + cc = cescape(pw->pw_name); + if (!cc) + return -ENOMEM; + + path = strjoina("/var/lib/systemd/linger/", cc); + if (b) { + User *u; + + r = touch(path); + if (r < 0) + return r; + + if (manager_add_user_by_uid(m, uid, &u) >= 0) + user_start(u); + + } else { + User *u; + + r = unlink(path); + if (r < 0 && errno != ENOENT) + return -errno; + + u = hashmap_get(m->users, UID_TO_PTR(uid)); + if (u) + user_add_to_gc_queue(u); + } + + return sd_bus_reply_method_return(message, NULL); +} + +static int trigger_device(Manager *m, struct udev_device *d) { + _cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL; + struct udev_list_entry *first, *item; + int r; + + assert(m); + + e = udev_enumerate_new(m->udev); + if (!e) + return -ENOMEM; + + if (d) { + r = udev_enumerate_add_match_parent(e, d); + if (r < 0) + return r; + } + + r = udev_enumerate_scan_devices(e); + if (r < 0) + return r; + + first = udev_enumerate_get_list_entry(e); + udev_list_entry_foreach(item, first) { + _cleanup_free_ char *t = NULL; + const char *p; + + p = udev_list_entry_get_name(item); + + t = strappend(p, "/uevent"); + if (!t) + return -ENOMEM; + + write_string_file(t, "change", WRITE_STRING_FILE_CREATE); + } + + return 0; +} + +static int attach_device(Manager *m, const char *seat, const char *sysfs) { + _cleanup_udev_device_unref_ struct udev_device *d = NULL; + _cleanup_free_ char *rule = NULL, *file = NULL; + const char *id_for_seat; + int r; + + assert(m); + assert(seat); + assert(sysfs); + + d = udev_device_new_from_syspath(m->udev, sysfs); + if (!d) + return -ENODEV; + + if (!udev_device_has_tag(d, "seat")) + return -ENODEV; + + id_for_seat = udev_device_get_property_value(d, "ID_FOR_SEAT"); + if (!id_for_seat) + return -ENODEV; + + if (asprintf(&file, "/etc/udev/rules.d/72-seat-%s.rules", id_for_seat) < 0) + return -ENOMEM; + + if (asprintf(&rule, "TAG==\"seat\", ENV{ID_FOR_SEAT}==\"%s\", ENV{ID_SEAT}=\"%s\"", id_for_seat, seat) < 0) + return -ENOMEM; + + mkdir_p_label("/etc/udev/rules.d", 0755); + r = write_string_file_atomic_label(file, rule); + if (r < 0) + return r; + + return trigger_device(m, d); +} + +static int flush_devices(Manager *m) { + _cleanup_closedir_ DIR *d; + + assert(m); + + d = opendir("/etc/udev/rules.d"); + if (!d) { + if (errno != ENOENT) + log_warning_errno(errno, "Failed to open /etc/udev/rules.d: %m"); + } else { + struct dirent *de; + + while ((de = readdir(d))) { + + if (!dirent_is_file(de)) + continue; + + if (!startswith(de->d_name, "72-seat-")) + continue; + + if (!endswith(de->d_name, ".rules")) + continue; + + if (unlinkat(dirfd(d), de->d_name, 0) < 0) + log_warning_errno(errno, "Failed to unlink %s: %m", de->d_name); + } + } + + return trigger_device(m, NULL); +} + +static int method_attach_device(sd_bus_message *message, void *userdata, sd_bus_error *error) { + const char *sysfs, *seat; + Manager *m = userdata; + int interactive, r; + + assert(message); + assert(m); + + r = sd_bus_message_read(message, "ssb", &seat, &sysfs, &interactive); + if (r < 0) + return r; + + if (!path_startswith(sysfs, "/sys")) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Path %s is not in /sys", sysfs); + + if (!seat_name_is_valid(seat)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Seat %s is not valid", seat); + + r = bus_verify_polkit_async( + message, + CAP_SYS_ADMIN, + "org.freedesktop.login1.attach-device", + NULL, + interactive, + UID_INVALID, + &m->polkit_registry, + error); + if (r < 0) + return r; + if (r == 0) + return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ + + r = attach_device(m, seat, sysfs); + if (r < 0) + return r; + + return sd_bus_reply_method_return(message, NULL); +} + +static int method_flush_devices(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + int interactive, r; + + assert(message); + assert(m); + + r = sd_bus_message_read(message, "b", &interactive); + if (r < 0) + return r; + + r = bus_verify_polkit_async( + message, + CAP_SYS_ADMIN, + "org.freedesktop.login1.flush-devices", + NULL, + interactive, + UID_INVALID, + &m->polkit_registry, + error); + if (r < 0) + return r; + if (r == 0) + return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ + + r = flush_devices(m); + if (r < 0) + return r; + + return sd_bus_reply_method_return(message, NULL); +} + +static int have_multiple_sessions( + Manager *m, + uid_t uid) { + + Session *session; + Iterator i; + + assert(m); + + /* Check for other users' sessions. Greeter sessions do not + * count, and non-login sessions do not count either. */ + HASHMAP_FOREACH(session, m->sessions, i) + if (session->class == SESSION_USER && + session->user->uid != uid) + return true; + + return false; +} + +static int bus_manager_log_shutdown( + Manager *m, + InhibitWhat w, + const char *unit_name) { + + const char *p, *q; + + assert(m); + assert(unit_name); + + if (w != INHIBIT_SHUTDOWN) + return 0; + + if (streq(unit_name, SPECIAL_POWEROFF_TARGET)) { + p = "MESSAGE=System is powering down"; + q = "SHUTDOWN=power-off"; + } else if (streq(unit_name, SPECIAL_HALT_TARGET)) { + p = "MESSAGE=System is halting"; + q = "SHUTDOWN=halt"; + } else if (streq(unit_name, SPECIAL_REBOOT_TARGET)) { + p = "MESSAGE=System is rebooting"; + q = "SHUTDOWN=reboot"; + } else if (streq(unit_name, SPECIAL_KEXEC_TARGET)) { + p = "MESSAGE=System is rebooting with kexec"; + q = "SHUTDOWN=kexec"; + } else { + p = "MESSAGE=System is shutting down"; + q = NULL; + } + + if (isempty(m->wall_message)) + p = strjoina(p, "."); + else + p = strjoina(p, " (", m->wall_message, ")."); + + return log_struct(LOG_NOTICE, + LOG_MESSAGE_ID(SD_MESSAGE_SHUTDOWN), + p, + q, + NULL); +} + +static int lid_switch_ignore_handler(sd_event_source *e, uint64_t usec, void *userdata) { + Manager *m = userdata; + + assert(e); + assert(m); + + m->lid_switch_ignore_event_source = sd_event_source_unref(m->lid_switch_ignore_event_source); + return 0; +} + +int manager_set_lid_switch_ignore(Manager *m, usec_t until) { + int r; + + assert(m); + + if (until <= now(CLOCK_MONOTONIC)) + return 0; + + /* We want to ignore the lid switch for a while after each + * suspend, and after boot-up. Hence let's install a timer for + * this. As long as the event source exists we ignore the lid + * switch. */ + + if (m->lid_switch_ignore_event_source) { + usec_t u; + + r = sd_event_source_get_time(m->lid_switch_ignore_event_source, &u); + if (r < 0) + return r; + + if (until <= u) + return 0; + + r = sd_event_source_set_time(m->lid_switch_ignore_event_source, until); + } else + r = sd_event_add_time( + m->event, + &m->lid_switch_ignore_event_source, + CLOCK_MONOTONIC, + until, 0, + lid_switch_ignore_handler, m); + + return r; +} + +static void reset_scheduled_shutdown(Manager *m) { + m->scheduled_shutdown_timeout_source = sd_event_source_unref(m->scheduled_shutdown_timeout_source); + m->wall_message_timeout_source = sd_event_source_unref(m->wall_message_timeout_source); + m->nologin_timeout_source = sd_event_source_unref(m->nologin_timeout_source); + m->scheduled_shutdown_type = mfree(m->scheduled_shutdown_type); + m->scheduled_shutdown_timeout = 0; + m->shutdown_dry_run = false; + + if (m->unlink_nologin) { + (void) unlink("/run/nologin"); + m->unlink_nologin = false; + } +} + +static int execute_shutdown_or_sleep( + Manager *m, + InhibitWhat w, + const char *unit_name, + sd_bus_error *error) { + + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + char *c = NULL; + const char *p; + int r; + + assert(m); + assert(w >= 0); + assert(w < _INHIBIT_WHAT_MAX); + assert(unit_name); + + bus_manager_log_shutdown(m, w, unit_name); + + if (m->shutdown_dry_run) { + log_info("Running in dry run, suppressing action."); + reset_scheduled_shutdown(m); + } else { + r = sd_bus_call_method( + m->bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "StartUnit", + error, + &reply, + "ss", unit_name, "replace-irreversibly"); + if (r < 0) + return r; + + r = sd_bus_message_read(reply, "o", &p); + if (r < 0) + return r; + + c = strdup(p); + if (!c) + return -ENOMEM; + } + + m->action_unit = unit_name; + free(m->action_job); + m->action_job = c; + m->action_what = w; + + /* Make sure the lid switch is ignored for a while */ + manager_set_lid_switch_ignore(m, now(CLOCK_MONOTONIC) + m->holdoff_timeout_usec); + + return 0; +} + +int manager_dispatch_delayed(Manager *manager, bool timeout) { + + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + Inhibitor *offending = NULL; + int r; + + assert(manager); + + if (manager->action_what == 0 || manager->action_job) + return 0; + + if (manager_is_inhibited(manager, manager->action_what, INHIBIT_DELAY, NULL, false, false, 0, &offending)) { + _cleanup_free_ char *comm = NULL, *u = NULL; + + if (!timeout) + return 0; + + (void) get_process_comm(offending->pid, &comm); + u = uid_to_name(offending->uid); + + log_notice("Delay lock is active (UID "UID_FMT"/%s, PID "PID_FMT"/%s) but inhibitor timeout is reached.", + offending->uid, strna(u), + offending->pid, strna(comm)); + } + + /* Actually do the operation */ + r = execute_shutdown_or_sleep(manager, manager->action_what, manager->action_unit, &error); + if (r < 0) { + log_warning("Failed to send delayed message: %s", bus_error_message(&error, r)); + + manager->action_unit = NULL; + manager->action_what = 0; + return r; + } + + return 1; +} + +static int manager_inhibit_timeout_handler( + sd_event_source *s, + uint64_t usec, + void *userdata) { + + Manager *manager = userdata; + int r; + + assert(manager); + assert(manager->inhibit_timeout_source == s); + + r = manager_dispatch_delayed(manager, true); + return (r < 0) ? r : 0; +} + +static int delay_shutdown_or_sleep( + Manager *m, + InhibitWhat w, + const char *unit_name) { + + int r; + usec_t timeout_val; + + assert(m); + assert(w >= 0); + assert(w < _INHIBIT_WHAT_MAX); + assert(unit_name); + + timeout_val = now(CLOCK_MONOTONIC) + m->inhibit_delay_max; + + if (m->inhibit_timeout_source) { + r = sd_event_source_set_time(m->inhibit_timeout_source, timeout_val); + if (r < 0) + return log_error_errno(r, "sd_event_source_set_time() failed: %m"); + + r = sd_event_source_set_enabled(m->inhibit_timeout_source, SD_EVENT_ONESHOT); + if (r < 0) + return log_error_errno(r, "sd_event_source_set_enabled() failed: %m"); + } else { + r = sd_event_add_time(m->event, &m->inhibit_timeout_source, CLOCK_MONOTONIC, + timeout_val, 0, manager_inhibit_timeout_handler, m); + if (r < 0) + return r; + } + + m->action_unit = unit_name; + m->action_what = w; + + return 0; +} + +static int send_prepare_for(Manager *m, InhibitWhat w, bool _active) { + + static const char * const signal_name[_INHIBIT_WHAT_MAX] = { + [INHIBIT_SHUTDOWN] = "PrepareForShutdown", + [INHIBIT_SLEEP] = "PrepareForSleep" + }; + + int active = _active; + + assert(m); + assert(w >= 0); + assert(w < _INHIBIT_WHAT_MAX); + assert(signal_name[w]); + + return sd_bus_emit_signal(m->bus, + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + signal_name[w], + "b", + active); +} + +int bus_manager_shutdown_or_sleep_now_or_later( + Manager *m, + const char *unit_name, + InhibitWhat w, + sd_bus_error *error) { + + bool delayed; + int r; + + assert(m); + assert(unit_name); + assert(w >= 0); + assert(w <= _INHIBIT_WHAT_MAX); + assert(!m->action_job); + + /* Tell everybody to prepare for shutdown/sleep */ + send_prepare_for(m, w, true); + + delayed = + m->inhibit_delay_max > 0 && + manager_is_inhibited(m, w, INHIBIT_DELAY, NULL, false, false, 0, NULL); + + if (delayed) + /* Shutdown is delayed, keep in mind what we + * want to do, and start a timeout */ + r = delay_shutdown_or_sleep(m, w, unit_name); + else + /* Shutdown is not delayed, execute it + * immediately */ + r = execute_shutdown_or_sleep(m, w, unit_name, error); + + return r; +} + +static int verify_shutdown_creds( + Manager *m, + sd_bus_message *message, + InhibitWhat w, + bool interactive, + const char *action, + const char *action_multiple_sessions, + const char *action_ignore_inhibit, + sd_bus_error *error) { + + _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; + bool multiple_sessions, blocked; + uid_t uid; + int r; + + assert(m); + assert(message); + assert(w >= 0); + assert(w <= _INHIBIT_WHAT_MAX); + + r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_EUID, &creds); + if (r < 0) + return r; + + r = sd_bus_creds_get_euid(creds, &uid); + if (r < 0) + return r; + + r = have_multiple_sessions(m, uid); + if (r < 0) + return r; + + multiple_sessions = r > 0; + blocked = manager_is_inhibited(m, w, INHIBIT_BLOCK, NULL, false, true, uid, NULL); + + if (multiple_sessions && action_multiple_sessions) { + r = bus_verify_polkit_async(message, CAP_SYS_BOOT, action_multiple_sessions, NULL, interactive, UID_INVALID, &m->polkit_registry, error); + if (r < 0) + return r; + if (r == 0) + return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ + } + + if (blocked && action_ignore_inhibit) { + r = bus_verify_polkit_async(message, CAP_SYS_BOOT, action_ignore_inhibit, NULL, interactive, UID_INVALID, &m->polkit_registry, error); + if (r < 0) + return r; + if (r == 0) + return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ + } + + if (!multiple_sessions && !blocked && action) { + r = bus_verify_polkit_async(message, CAP_SYS_BOOT, action, NULL, interactive, UID_INVALID, &m->polkit_registry, error); + if (r < 0) + return r; + if (r == 0) + return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ + } + + return 0; +} + +static int method_do_shutdown_or_sleep( + Manager *m, + sd_bus_message *message, + const char *unit_name, + InhibitWhat w, + const char *action, + const char *action_multiple_sessions, + const char *action_ignore_inhibit, + const char *sleep_verb, + sd_bus_error *error) { + + int interactive, r; + + assert(m); + assert(message); + assert(unit_name); + assert(w >= 0); + assert(w <= _INHIBIT_WHAT_MAX); + + r = sd_bus_message_read(message, "b", &interactive); + if (r < 0) + return r; + + /* Don't allow multiple jobs being executed at the same time */ + if (m->action_what) + return sd_bus_error_setf(error, BUS_ERROR_OPERATION_IN_PROGRESS, "There's already a shutdown or sleep operation in progress"); + + if (sleep_verb) { + r = can_sleep(sleep_verb); + if (r < 0) + return r; + + if (r == 0) + return sd_bus_error_setf(error, BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED, "Sleep verb not supported"); + } + + r = verify_shutdown_creds(m, message, w, interactive, action, action_multiple_sessions, + action_ignore_inhibit, error); + if (r != 0) + return r; + + r = bus_manager_shutdown_or_sleep_now_or_later(m, unit_name, w, error); + if (r < 0) + return r; + + return sd_bus_reply_method_return(message, NULL); +} + +static int method_poweroff(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + + return method_do_shutdown_or_sleep( + m, message, + SPECIAL_POWEROFF_TARGET, + INHIBIT_SHUTDOWN, + "org.freedesktop.login1.power-off", + "org.freedesktop.login1.power-off-multiple-sessions", + "org.freedesktop.login1.power-off-ignore-inhibit", + NULL, + error); +} + +static int method_reboot(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + + return method_do_shutdown_or_sleep( + m, message, + SPECIAL_REBOOT_TARGET, + INHIBIT_SHUTDOWN, + "org.freedesktop.login1.reboot", + "org.freedesktop.login1.reboot-multiple-sessions", + "org.freedesktop.login1.reboot-ignore-inhibit", + NULL, + error); +} + +static int method_suspend(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + + return method_do_shutdown_or_sleep( + m, message, + SPECIAL_SUSPEND_TARGET, + INHIBIT_SLEEP, + "org.freedesktop.login1.suspend", + "org.freedesktop.login1.suspend-multiple-sessions", + "org.freedesktop.login1.suspend-ignore-inhibit", + "suspend", + error); +} + +static int nologin_timeout_handler( + sd_event_source *s, + uint64_t usec, + void *userdata) { + + Manager *m = userdata; + int r; + + log_info("Creating /run/nologin, blocking further logins..."); + + r = write_string_file_atomic_label("/run/nologin", "System is going down."); + if (r < 0) + log_error_errno(r, "Failed to create /run/nologin: %m"); + else + m->unlink_nologin = true; + + return 0; +} + +static int update_schedule_file(Manager *m) { + _cleanup_free_ char *temp_path = NULL; + _cleanup_fclose_ FILE *f = NULL; + int r; + + assert(m); + + r = mkdir_safe_label("/run/systemd/shutdown", 0755, 0, 0); + if (r < 0) + return log_error_errno(r, "Failed to create shutdown subdirectory: %m"); + + r = fopen_temporary("/run/systemd/shutdown/scheduled", &f, &temp_path); + if (r < 0) + return log_error_errno(r, "Failed to save information about scheduled shutdowns: %m"); + + (void) fchmod(fileno(f), 0644); + + fprintf(f, + "USEC="USEC_FMT"\n" + "WARN_WALL=%i\n" + "MODE=%s\n", + m->scheduled_shutdown_timeout, + m->enable_wall_messages, + m->scheduled_shutdown_type); + + if (!isempty(m->wall_message)) { + _cleanup_free_ char *t; + + t = cescape(m->wall_message); + if (!t) { + r = -ENOMEM; + goto fail; + } + + fprintf(f, "WALL_MESSAGE=%s\n", t); + } + + r = fflush_and_check(f); + if (r < 0) + goto fail; + + if (rename(temp_path, "/run/systemd/shutdown/scheduled") < 0) { + r = -errno; + goto fail; + } + + return 0; + +fail: + (void) unlink(temp_path); + (void) unlink("/run/systemd/shutdown/scheduled"); + + return log_error_errno(r, "Failed to write information about scheduled shutdowns: %m"); +} + +static int manager_scheduled_shutdown_handler( + sd_event_source *s, + uint64_t usec, + void *userdata) { + + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + Manager *m = userdata; + const char *target; + int r; + + assert(m); + + if (isempty(m->scheduled_shutdown_type)) + return 0; + + if (streq(m->scheduled_shutdown_type, "halt")) + target = SPECIAL_HALT_TARGET; + else if (streq(m->scheduled_shutdown_type, "poweroff")) + target = SPECIAL_POWEROFF_TARGET; + else + target = SPECIAL_REBOOT_TARGET; + + r = execute_shutdown_or_sleep(m, 0, target, &error); + if (r < 0) + return log_error_errno(r, "Unable to execute transition to %s: %m", target); + + return 0; +} + +static int method_schedule_shutdown(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; + const char *action_multiple_sessions = NULL; + const char *action_ignore_inhibit = NULL; + const char *action = NULL; + uint64_t elapse; + char *type; + int r; + + assert(m); + assert(message); + + r = sd_bus_message_read(message, "st", &type, &elapse); + if (r < 0) + return r; + + if (startswith(type, "dry-")) { + type += 4; + m->shutdown_dry_run = true; + } + + if (streq(type, "reboot")) { + action = "org.freedesktop.login1.reboot"; + action_multiple_sessions = "org.freedesktop.login1.reboot-multiple-sessions"; + action_ignore_inhibit = "org.freedesktop.login1.reboot-ignore-inhibit"; + } else if (streq(type, "halt")) { + action = "org.freedesktop.login1.halt"; + action_multiple_sessions = "org.freedesktop.login1.halt-multiple-sessions"; + action_ignore_inhibit = "org.freedesktop.login1.halt-ignore-inhibit"; + } else if (streq(type, "poweroff")) { + action = "org.freedesktop.login1.power-off"; + action_multiple_sessions = "org.freedesktop.login1.power-off-multiple-sessions"; + action_ignore_inhibit = "org.freedesktop.login1.power-off-ignore-inhibit"; + } else + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unsupported shutdown type"); + + r = verify_shutdown_creds(m, message, INHIBIT_SHUTDOWN, false, + action, action_multiple_sessions, action_ignore_inhibit, error); + if (r != 0) + return r; + + if (m->scheduled_shutdown_timeout_source) { + r = sd_event_source_set_time(m->scheduled_shutdown_timeout_source, elapse); + if (r < 0) + return log_error_errno(r, "sd_event_source_set_time() failed: %m"); + + r = sd_event_source_set_enabled(m->scheduled_shutdown_timeout_source, SD_EVENT_ONESHOT); + if (r < 0) + return log_error_errno(r, "sd_event_source_set_enabled() failed: %m"); + } else { + r = sd_event_add_time(m->event, &m->scheduled_shutdown_timeout_source, + CLOCK_REALTIME, elapse, 0, manager_scheduled_shutdown_handler, m); + if (r < 0) + return log_error_errno(r, "sd_event_add_time() failed: %m"); + } + + r = free_and_strdup(&m->scheduled_shutdown_type, type); + if (r < 0) { + m->scheduled_shutdown_timeout_source = sd_event_source_unref(m->scheduled_shutdown_timeout_source); + return log_oom(); + } + + if (m->nologin_timeout_source) { + r = sd_event_source_set_time(m->nologin_timeout_source, elapse); + if (r < 0) + return log_error_errno(r, "sd_event_source_set_time() failed: %m"); + + r = sd_event_source_set_enabled(m->nologin_timeout_source, SD_EVENT_ONESHOT); + if (r < 0) + return log_error_errno(r, "sd_event_source_set_enabled() failed: %m"); + } else { + r = sd_event_add_time(m->event, &m->nologin_timeout_source, + CLOCK_REALTIME, elapse - 5 * USEC_PER_MINUTE, 0, nologin_timeout_handler, m); + if (r < 0) + return log_error_errno(r, "sd_event_add_time() failed: %m"); + } + + m->scheduled_shutdown_timeout = elapse; + + r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_AUGMENT|SD_BUS_CREDS_TTY|SD_BUS_CREDS_UID, &creds); + if (r >= 0) { + const char *tty = NULL; + + (void) sd_bus_creds_get_uid(creds, &m->scheduled_shutdown_uid); + (void) sd_bus_creds_get_tty(creds, &tty); + + r = free_and_strdup(&m->scheduled_shutdown_tty, tty); + if (r < 0) { + m->scheduled_shutdown_timeout_source = sd_event_source_unref(m->scheduled_shutdown_timeout_source); + return log_oom(); + } + } + + r = manager_setup_wall_message_timer(m); + if (r < 0) + return r; + + if (!isempty(type)) { + r = update_schedule_file(m); + if (r < 0) + return r; + } else + (void) unlink("/run/systemd/shutdown/scheduled"); + + return sd_bus_reply_method_return(message, NULL); +} + +static int method_cancel_scheduled_shutdown(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + bool cancelled; + + assert(m); + assert(message); + + cancelled = m->scheduled_shutdown_type != NULL; + reset_scheduled_shutdown(m); + + if (cancelled) { + _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; + const char *tty = NULL; + uid_t uid = 0; + int r; + + r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_AUGMENT|SD_BUS_CREDS_TTY|SD_BUS_CREDS_UID, &creds); + if (r >= 0) { + (void) sd_bus_creds_get_uid(creds, &uid); + (void) sd_bus_creds_get_tty(creds, &tty); + } + + utmp_wall("The system shutdown has been cancelled", + uid_to_name(uid), tty, logind_wall_tty_filter, m); + } + + return sd_bus_reply_method_return(message, "b", cancelled); +} + +static int method_hibernate(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + + return method_do_shutdown_or_sleep( + m, message, + SPECIAL_HIBERNATE_TARGET, + INHIBIT_SLEEP, + "org.freedesktop.login1.hibernate", + "org.freedesktop.login1.hibernate-multiple-sessions", + "org.freedesktop.login1.hibernate-ignore-inhibit", + "hibernate", + error); +} + +static int method_hybrid_sleep(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + + return method_do_shutdown_or_sleep( + m, message, + SPECIAL_HYBRID_SLEEP_TARGET, + INHIBIT_SLEEP, + "org.freedesktop.login1.hibernate", + "org.freedesktop.login1.hibernate-multiple-sessions", + "org.freedesktop.login1.hibernate-ignore-inhibit", + "hybrid-sleep", + error); +} + +static int method_can_shutdown_or_sleep( + Manager *m, + sd_bus_message *message, + InhibitWhat w, + const char *action, + const char *action_multiple_sessions, + const char *action_ignore_inhibit, + const char *sleep_verb, + sd_bus_error *error) { + + _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; + bool multiple_sessions, challenge, blocked; + const char *result = NULL; + uid_t uid; + int r; + + assert(m); + assert(message); + assert(w >= 0); + assert(w <= _INHIBIT_WHAT_MAX); + assert(action); + assert(action_multiple_sessions); + assert(action_ignore_inhibit); + + if (sleep_verb) { + r = can_sleep(sleep_verb); + if (r < 0) + return r; + if (r == 0) + return sd_bus_reply_method_return(message, "s", "na"); + } + + r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_EUID, &creds); + if (r < 0) + return r; + + r = sd_bus_creds_get_euid(creds, &uid); + if (r < 0) + return r; + + r = have_multiple_sessions(m, uid); + if (r < 0) + return r; + + multiple_sessions = r > 0; + blocked = manager_is_inhibited(m, w, INHIBIT_BLOCK, NULL, false, true, uid, NULL); + + if (multiple_sessions) { + r = bus_test_polkit(message, CAP_SYS_BOOT, action_multiple_sessions, NULL, UID_INVALID, &challenge, error); + if (r < 0) + return r; + + if (r > 0) + result = "yes"; + else if (challenge) + result = "challenge"; + else + result = "no"; + } + + if (blocked) { + r = bus_test_polkit(message, CAP_SYS_BOOT, action_ignore_inhibit, NULL, UID_INVALID, &challenge, error); + if (r < 0) + return r; + + if (r > 0 && !result) + result = "yes"; + else if (challenge && (!result || streq(result, "yes"))) + result = "challenge"; + else + result = "no"; + } + + if (!multiple_sessions && !blocked) { + /* If neither inhibit nor multiple sessions + * apply then just check the normal policy */ + + r = bus_test_polkit(message, CAP_SYS_BOOT, action, NULL, UID_INVALID, &challenge, error); + if (r < 0) + return r; + + if (r > 0) + result = "yes"; + else if (challenge) + result = "challenge"; + else + result = "no"; + } + + return sd_bus_reply_method_return(message, "s", result); +} + +static int method_can_poweroff(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + + return method_can_shutdown_or_sleep( + m, message, + INHIBIT_SHUTDOWN, + "org.freedesktop.login1.power-off", + "org.freedesktop.login1.power-off-multiple-sessions", + "org.freedesktop.login1.power-off-ignore-inhibit", + NULL, + error); +} + +static int method_can_reboot(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + + return method_can_shutdown_or_sleep( + m, message, + INHIBIT_SHUTDOWN, + "org.freedesktop.login1.reboot", + "org.freedesktop.login1.reboot-multiple-sessions", + "org.freedesktop.login1.reboot-ignore-inhibit", + NULL, + error); +} + +static int method_can_suspend(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + + return method_can_shutdown_or_sleep( + m, message, + INHIBIT_SLEEP, + "org.freedesktop.login1.suspend", + "org.freedesktop.login1.suspend-multiple-sessions", + "org.freedesktop.login1.suspend-ignore-inhibit", + "suspend", + error); +} + +static int method_can_hibernate(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + + return method_can_shutdown_or_sleep( + m, message, + INHIBIT_SLEEP, + "org.freedesktop.login1.hibernate", + "org.freedesktop.login1.hibernate-multiple-sessions", + "org.freedesktop.login1.hibernate-ignore-inhibit", + "hibernate", + error); +} + +static int method_can_hybrid_sleep(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + + return method_can_shutdown_or_sleep( + m, message, + INHIBIT_SLEEP, + "org.freedesktop.login1.hibernate", + "org.freedesktop.login1.hibernate-multiple-sessions", + "org.freedesktop.login1.hibernate-ignore-inhibit", + "hybrid-sleep", + error); +} + +static int property_get_reboot_to_firmware_setup( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + int r; + + assert(bus); + assert(reply); + assert(userdata); + + r = efi_get_reboot_to_firmware(); + if (r < 0 && r != -EOPNOTSUPP) + return r; + + return sd_bus_message_append(reply, "b", r > 0); +} + +static int method_set_reboot_to_firmware_setup( + sd_bus_message *message, + void *userdata, + sd_bus_error *error) { + + int b, r; + Manager *m = userdata; + + assert(message); + assert(m); + + r = sd_bus_message_read(message, "b", &b); + if (r < 0) + return r; + + r = bus_verify_polkit_async(message, + CAP_SYS_ADMIN, + "org.freedesktop.login1.set-reboot-to-firmware-setup", + NULL, + false, + UID_INVALID, + &m->polkit_registry, + error); + if (r < 0) + return r; + if (r == 0) + return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ + + r = efi_set_reboot_to_firmware(b); + if (r < 0) + return r; + + return sd_bus_reply_method_return(message, NULL); +} + +static int method_can_reboot_to_firmware_setup( + sd_bus_message *message, + void *userdata, + sd_bus_error *error) { + + int r; + bool challenge; + const char *result; + Manager *m = userdata; + + assert(message); + assert(m); + + r = efi_reboot_to_firmware_supported(); + if (r == -EOPNOTSUPP) + return sd_bus_reply_method_return(message, "s", "na"); + else if (r < 0) + return r; + + r = bus_test_polkit(message, + CAP_SYS_ADMIN, + "org.freedesktop.login1.set-reboot-to-firmware-setup", + NULL, + UID_INVALID, + &challenge, + error); + if (r < 0) + return r; + + if (r > 0) + result = "yes"; + else if (challenge) + result = "challenge"; + else + result = "no"; + + return sd_bus_reply_method_return(message, "s", result); +} + +static int method_set_wall_message( + sd_bus_message *message, + void *userdata, + sd_bus_error *error) { + + int r; + Manager *m = userdata; + char *wall_message; + int enable_wall_messages; + + assert(message); + assert(m); + + r = sd_bus_message_read(message, "sb", &wall_message, &enable_wall_messages); + if (r < 0) + return r; + + r = bus_verify_polkit_async(message, + CAP_SYS_ADMIN, + "org.freedesktop.login1.set-wall-message", + NULL, + false, + UID_INVALID, + &m->polkit_registry, + error); + if (r < 0) + return r; + if (r == 0) + return 1; /* Will call us back */ + + if (isempty(wall_message)) + m->wall_message = mfree(m->wall_message); + else { + r = free_and_strdup(&m->wall_message, wall_message); + if (r < 0) + return log_oom(); + } + + m->enable_wall_messages = enable_wall_messages; + + return sd_bus_reply_method_return(message, NULL); +} + +static int method_inhibit(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; + const char *who, *why, *what, *mode; + _cleanup_free_ char *id = NULL; + _cleanup_close_ int fifo_fd = -1; + Manager *m = userdata; + Inhibitor *i = NULL; + InhibitMode mm; + InhibitWhat w; + pid_t pid; + uid_t uid; + int r; + + assert(message); + assert(m); + + r = sd_bus_message_read(message, "ssss", &what, &who, &why, &mode); + if (r < 0) + return r; + + w = inhibit_what_from_string(what); + if (w <= 0) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid what specification %s", what); + + mm = inhibit_mode_from_string(mode); + if (mm < 0) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid mode specification %s", mode); + + /* Delay is only supported for shutdown/sleep */ + if (mm == INHIBIT_DELAY && (w & ~(INHIBIT_SHUTDOWN|INHIBIT_SLEEP))) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Delay inhibitors only supported for shutdown and sleep"); + + /* Don't allow taking delay locks while we are already + * executing the operation. We shouldn't create the impression + * that the lock was successful if the machine is about to go + * down/suspend any moment. */ + if (m->action_what & w) + return sd_bus_error_setf(error, BUS_ERROR_OPERATION_IN_PROGRESS, "The operation inhibition has been requested for is already running"); + + r = bus_verify_polkit_async( + message, + CAP_SYS_BOOT, + w == INHIBIT_SHUTDOWN ? (mm == INHIBIT_BLOCK ? "org.freedesktop.login1.inhibit-block-shutdown" : "org.freedesktop.login1.inhibit-delay-shutdown") : + w == INHIBIT_SLEEP ? (mm == INHIBIT_BLOCK ? "org.freedesktop.login1.inhibit-block-sleep" : "org.freedesktop.login1.inhibit-delay-sleep") : + w == INHIBIT_IDLE ? "org.freedesktop.login1.inhibit-block-idle" : + w == INHIBIT_HANDLE_POWER_KEY ? "org.freedesktop.login1.inhibit-handle-power-key" : + w == INHIBIT_HANDLE_SUSPEND_KEY ? "org.freedesktop.login1.inhibit-handle-suspend-key" : + w == INHIBIT_HANDLE_HIBERNATE_KEY ? "org.freedesktop.login1.inhibit-handle-hibernate-key" : + "org.freedesktop.login1.inhibit-handle-lid-switch", + NULL, + false, + UID_INVALID, + &m->polkit_registry, + error); + if (r < 0) + return r; + if (r == 0) + return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ + + r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_EUID|SD_BUS_CREDS_PID, &creds); + if (r < 0) + return r; + + r = sd_bus_creds_get_euid(creds, &uid); + if (r < 0) + return r; + + r = sd_bus_creds_get_pid(creds, &pid); + if (r < 0) + return r; + + if (hashmap_size(m->inhibitors) >= m->inhibitors_max) + return sd_bus_error_setf(error, SD_BUS_ERROR_LIMITS_EXCEEDED, "Maximum number of inhibitors (%" PRIu64 ") reached, refusing further inhibitors.", m->inhibitors_max); + + do { + id = mfree(id); + + if (asprintf(&id, "%lu", ++m->inhibit_counter) < 0) + return -ENOMEM; + + } while (hashmap_get(m->inhibitors, id)); + + r = manager_add_inhibitor(m, id, &i); + if (r < 0) + return r; + + i->what = w; + i->mode = mm; + i->pid = pid; + i->uid = uid; + i->why = strdup(why); + i->who = strdup(who); + + if (!i->why || !i->who) { + r = -ENOMEM; + goto fail; + } + + fifo_fd = inhibitor_create_fifo(i); + if (fifo_fd < 0) { + r = fifo_fd; + goto fail; + } + + inhibitor_start(i); + + return sd_bus_reply_method_return(message, "h", fifo_fd); + +fail: + if (i) + inhibitor_free(i); + + return r; +} + +const sd_bus_vtable manager_vtable[] = { + SD_BUS_VTABLE_START(0), + + SD_BUS_WRITABLE_PROPERTY("EnableWallMessages", "b", NULL, NULL, offsetof(Manager, enable_wall_messages), 0), + SD_BUS_WRITABLE_PROPERTY("WallMessage", "s", NULL, NULL, offsetof(Manager, wall_message), 0), + + SD_BUS_PROPERTY("NAutoVTs", "u", NULL, offsetof(Manager, n_autovts), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("KillOnlyUsers", "as", NULL, offsetof(Manager, kill_only_users), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("KillExcludeUsers", "as", NULL, offsetof(Manager, kill_exclude_users), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("KillUserProcesses", "b", NULL, offsetof(Manager, kill_user_processes), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("RebootToFirmwareSetup", "b", property_get_reboot_to_firmware_setup, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("IdleHint", "b", property_get_idle_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("IdleSinceHint", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("IdleSinceHintMonotonic", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("BlockInhibited", "s", property_get_inhibited, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("DelayInhibited", "s", property_get_inhibited, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("InhibitDelayMaxUSec", "t", NULL, offsetof(Manager, inhibit_delay_max), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("HandlePowerKey", "s", property_get_handle_action, offsetof(Manager, handle_power_key), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("HandleSuspendKey", "s", property_get_handle_action, offsetof(Manager, handle_suspend_key), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("HandleHibernateKey", "s", property_get_handle_action, offsetof(Manager, handle_hibernate_key), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("HandleLidSwitch", "s", property_get_handle_action, offsetof(Manager, handle_lid_switch), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("HandleLidSwitchDocked", "s", property_get_handle_action, offsetof(Manager, handle_lid_switch_docked), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("HoldoffTimeoutUSec", "t", NULL, offsetof(Manager, holdoff_timeout_usec), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("IdleAction", "s", property_get_handle_action, offsetof(Manager, idle_action), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("IdleActionUSec", "t", NULL, offsetof(Manager, idle_action_usec), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("PreparingForShutdown", "b", property_get_preparing, 0, 0), + SD_BUS_PROPERTY("PreparingForSleep", "b", property_get_preparing, 0, 0), + SD_BUS_PROPERTY("ScheduledShutdown", "(st)", property_get_scheduled_shutdown, 0, 0), + SD_BUS_PROPERTY("Docked", "b", property_get_docked, 0, 0), + SD_BUS_PROPERTY("RemoveIPC", "b", bus_property_get_bool, offsetof(Manager, remove_ipc), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("RuntimeDirectorySize", "t", bus_property_get_size, offsetof(Manager, runtime_dir_size), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("InhibitorsMax", "t", NULL, offsetof(Manager, inhibitors_max), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("NCurrentInhibitors", "t", property_get_current_inhibitors, 0, 0), + SD_BUS_PROPERTY("SessionsMax", "t", NULL, offsetof(Manager, sessions_max), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("NCurrentSessions", "t", property_get_current_sessions, 0, 0), + SD_BUS_PROPERTY("UserTasksMax", "t", NULL, offsetof(Manager, user_tasks_max), SD_BUS_VTABLE_PROPERTY_CONST), + + SD_BUS_METHOD("GetSession", "s", "o", method_get_session, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("GetSessionByPID", "u", "o", method_get_session_by_pid, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("GetUser", "u", "o", method_get_user, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("GetUserByPID", "u", "o", method_get_user_by_pid, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("GetSeat", "s", "o", method_get_seat, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("ListSessions", NULL, "a(susso)", method_list_sessions, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("ListUsers", NULL, "a(uso)", method_list_users, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("ListSeats", NULL, "a(so)", method_list_seats, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("ListInhibitors", NULL, "a(ssssuu)", method_list_inhibitors, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("CreateSession", "uusssssussbssa(sv)", "soshusub", method_create_session, 0), + SD_BUS_METHOD("ReleaseSession", "s", NULL, method_release_session, 0), + SD_BUS_METHOD("ActivateSession", "s", NULL, method_activate_session, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("ActivateSessionOnSeat", "ss", NULL, method_activate_session_on_seat, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("LockSession", "s", NULL, method_lock_session, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("UnlockSession", "s", NULL, method_lock_session, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("LockSessions", NULL, NULL, method_lock_sessions, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("UnlockSessions", NULL, NULL, method_lock_sessions, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("KillSession", "ssi", NULL, method_kill_session, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("KillUser", "ui", NULL, method_kill_user, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("TerminateSession", "s", NULL, method_terminate_session, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("TerminateUser", "u", NULL, method_terminate_user, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("TerminateSeat", "s", NULL, method_terminate_seat, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("SetUserLinger", "ubb", NULL, method_set_user_linger, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("AttachDevice", "ssb", NULL, method_attach_device, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("FlushDevices", "b", NULL, method_flush_devices, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("PowerOff", "b", NULL, method_poweroff, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("Reboot", "b", NULL, method_reboot, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("Suspend", "b", NULL, method_suspend, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("Hibernate", "b", NULL, method_hibernate, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("HybridSleep", "b", NULL, method_hybrid_sleep, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("CanPowerOff", NULL, "s", method_can_poweroff, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("CanReboot", NULL, "s", method_can_reboot, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("CanSuspend", NULL, "s", method_can_suspend, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("CanHibernate", NULL, "s", method_can_hibernate, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("CanHybridSleep", NULL, "s", method_can_hybrid_sleep, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("ScheduleShutdown", "st", NULL, method_schedule_shutdown, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("CancelScheduledShutdown", NULL, "b", method_cancel_scheduled_shutdown, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("Inhibit", "ssss", "h", method_inhibit, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("CanRebootToFirmwareSetup", NULL, "s", method_can_reboot_to_firmware_setup, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("SetRebootToFirmwareSetup", "b", NULL, method_set_reboot_to_firmware_setup, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("SetWallMessage", "sb", NULL, method_set_wall_message, SD_BUS_VTABLE_UNPRIVILEGED), + + SD_BUS_SIGNAL("SessionNew", "so", 0), + SD_BUS_SIGNAL("SessionRemoved", "so", 0), + SD_BUS_SIGNAL("UserNew", "uo", 0), + SD_BUS_SIGNAL("UserRemoved", "uo", 0), + SD_BUS_SIGNAL("SeatNew", "so", 0), + SD_BUS_SIGNAL("SeatRemoved", "so", 0), + SD_BUS_SIGNAL("PrepareForShutdown", "b", 0), + SD_BUS_SIGNAL("PrepareForSleep", "b", 0), + + SD_BUS_VTABLE_END +}; + +static int session_jobs_reply(Session *s, const char *unit, const char *result) { + int r = 0; + + assert(s); + assert(unit); + + if (!s->started) + return r; + + if (streq(result, "done")) + r = session_send_create_reply(s, NULL); + else { + _cleanup_(sd_bus_error_free) sd_bus_error e = SD_BUS_ERROR_NULL; + + sd_bus_error_setf(&e, BUS_ERROR_JOB_FAILED, "Start job for unit %s failed with '%s'", unit, result); + r = session_send_create_reply(s, &e); + } + + return r; +} + +int match_job_removed(sd_bus_message *message, void *userdata, sd_bus_error *error) { + const char *path, *result, *unit; + Manager *m = userdata; + Session *session; + uint32_t id; + User *user; + int r; + + assert(message); + assert(m); + + r = sd_bus_message_read(message, "uoss", &id, &path, &unit, &result); + if (r < 0) { + bus_log_parse_error(r); + return 0; + } + + if (m->action_job && streq(m->action_job, path)) { + log_info("Operation '%s' finished.", inhibit_what_to_string(m->action_what)); + + /* Tell people that they now may take a lock again */ + send_prepare_for(m, m->action_what, false); + + m->action_job = mfree(m->action_job); + m->action_unit = NULL; + m->action_what = 0; + return 0; + } + + session = hashmap_get(m->session_units, unit); + if (session && streq_ptr(path, session->scope_job)) { + session->scope_job = mfree(session->scope_job); + session_jobs_reply(session, unit, result); + + session_save(session); + user_save(session->user); + session_add_to_gc_queue(session); + } + + user = hashmap_get(m->user_units, unit); + if (user && + (streq_ptr(path, user->service_job) || + streq_ptr(path, user->slice_job))) { + + if (streq_ptr(path, user->service_job)) + user->service_job = mfree(user->service_job); + + if (streq_ptr(path, user->slice_job)) + user->slice_job = mfree(user->slice_job); + + LIST_FOREACH(sessions_by_user, session, user->sessions) + session_jobs_reply(session, unit, result); + + user_save(user); + user_add_to_gc_queue(user); + } + + return 0; +} + +int match_unit_removed(sd_bus_message *message, void *userdata, sd_bus_error *error) { + const char *path, *unit; + Manager *m = userdata; + Session *session; + User *user; + int r; + + assert(message); + assert(m); + + r = sd_bus_message_read(message, "so", &unit, &path); + if (r < 0) { + bus_log_parse_error(r); + return 0; + } + + session = hashmap_get(m->session_units, unit); + if (session) + session_add_to_gc_queue(session); + + user = hashmap_get(m->user_units, unit); + if (user) + user_add_to_gc_queue(user); + + return 0; +} + +int match_properties_changed(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_free_ char *unit = NULL; + Manager *m = userdata; + const char *path; + Session *session; + User *user; + int r; + + assert(message); + assert(m); + + path = sd_bus_message_get_path(message); + if (!path) + return 0; + + r = unit_name_from_dbus_path(path, &unit); + if (r == -EINVAL) /* not a unit */ + return 0; + if (r < 0) { + log_oom(); + return 0; + } + + session = hashmap_get(m->session_units, unit); + if (session) + session_add_to_gc_queue(session); + + user = hashmap_get(m->user_units, unit); + if (user) + user_add_to_gc_queue(user); + + return 0; +} + +int match_reloading(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + Session *session; + Iterator i; + int b, r; + + assert(message); + assert(m); + + r = sd_bus_message_read(message, "b", &b); + if (r < 0) { + bus_log_parse_error(r); + return 0; + } + + if (b) + return 0; + + /* systemd finished reloading, let's recheck all our sessions */ + log_debug("System manager has been reloaded, rechecking sessions..."); + + HASHMAP_FOREACH(session, m->sessions, i) + session_add_to_gc_queue(session); + + return 0; +} + +int manager_send_changed(Manager *manager, const char *property, ...) { + char **l; + + assert(manager); + + l = strv_from_stdarg_alloca(property); + + return sd_bus_emit_properties_changed_strv( + manager->bus, + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + l); +} + +static int strdup_job(sd_bus_message *reply, char **job) { + const char *j; + char *copy; + int r; + + r = sd_bus_message_read(reply, "o", &j); + if (r < 0) + return r; + + copy = strdup(j); + if (!copy) + return -ENOMEM; + + *job = copy; + return 1; +} + +int manager_start_slice( + Manager *manager, + const char *slice, + const char *description, + const char *after, + const char *after2, + uint64_t tasks_max, + sd_bus_error *error, + char **job) { + + _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL; + int r; + + assert(manager); + assert(slice); + assert(job); + + r = sd_bus_message_new_method_call( + manager->bus, + &m, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "StartTransientUnit"); + if (r < 0) + return r; + + r = sd_bus_message_append(m, "ss", strempty(slice), "fail"); + if (r < 0) + return r; + + r = sd_bus_message_open_container(m, 'a', "(sv)"); + if (r < 0) + return r; + + if (!isempty(description)) { + r = sd_bus_message_append(m, "(sv)", "Description", "s", description); + if (r < 0) + return r; + } + + if (!isempty(after)) { + r = sd_bus_message_append(m, "(sv)", "After", "as", 1, after); + if (r < 0) + return r; + } + + if (!isempty(after2)) { + r = sd_bus_message_append(m, "(sv)", "After", "as", 1, after2); + if (r < 0) + return r; + } + + r = sd_bus_message_append(m, "(sv)", "TasksMax", "t", tasks_max); + if (r < 0) + return r; + + r = sd_bus_message_close_container(m); + if (r < 0) + return r; + + r = sd_bus_message_append(m, "a(sa(sv))", 0); + if (r < 0) + return r; + + r = sd_bus_call(manager->bus, m, 0, error, &reply); + if (r < 0) + return r; + + return strdup_job(reply, job); +} + +int manager_start_scope( + Manager *manager, + const char *scope, + pid_t pid, + const char *slice, + const char *description, + const char *after, + const char *after2, + uint64_t tasks_max, + sd_bus_error *error, + char **job) { + + _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL; + int r; + + assert(manager); + assert(scope); + assert(pid > 1); + assert(job); + + r = sd_bus_message_new_method_call( + manager->bus, + &m, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "StartTransientUnit"); + if (r < 0) + return r; + + r = sd_bus_message_append(m, "ss", strempty(scope), "fail"); + if (r < 0) + return r; + + r = sd_bus_message_open_container(m, 'a', "(sv)"); + if (r < 0) + return r; + + if (!isempty(slice)) { + r = sd_bus_message_append(m, "(sv)", "Slice", "s", slice); + if (r < 0) + return r; + } + + if (!isempty(description)) { + r = sd_bus_message_append(m, "(sv)", "Description", "s", description); + if (r < 0) + return r; + } + + if (!isempty(after)) { + r = sd_bus_message_append(m, "(sv)", "After", "as", 1, after); + if (r < 0) + return r; + } + + if (!isempty(after2)) { + r = sd_bus_message_append(m, "(sv)", "After", "as", 1, after2); + if (r < 0) + return r; + } + + /* cgroup empty notification is not available in containers + * currently. To make this less problematic, let's shorten the + * stop timeout for sessions, so that we don't wait + * forever. */ + + /* Make sure that the session shells are terminated with + * SIGHUP since bash and friends tend to ignore SIGTERM */ + r = sd_bus_message_append(m, "(sv)", "SendSIGHUP", "b", true); + if (r < 0) + return r; + + r = sd_bus_message_append(m, "(sv)", "PIDs", "au", 1, pid); + if (r < 0) + return r; + + r = sd_bus_message_append(m, "(sv)", "TasksMax", "t", tasks_max); + if (r < 0) + return r; + + r = sd_bus_message_close_container(m); + if (r < 0) + return r; + + r = sd_bus_message_append(m, "a(sa(sv))", 0); + if (r < 0) + return r; + + r = sd_bus_call(manager->bus, m, 0, error, &reply); + if (r < 0) + return r; + + return strdup_job(reply, job); +} + +int manager_start_unit(Manager *manager, const char *unit, sd_bus_error *error, char **job) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + int r; + + assert(manager); + assert(unit); + assert(job); + + r = sd_bus_call_method( + manager->bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "StartUnit", + error, + &reply, + "ss", unit, "replace"); + if (r < 0) + return r; + + return strdup_job(reply, job); +} + +int manager_stop_unit(Manager *manager, const char *unit, sd_bus_error *error, char **job) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + int r; + + assert(manager); + assert(unit); + assert(job); + + r = sd_bus_call_method( + manager->bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "StopUnit", + error, + &reply, + "ss", unit, "fail"); + if (r < 0) { + if (sd_bus_error_has_name(error, BUS_ERROR_NO_SUCH_UNIT) || + sd_bus_error_has_name(error, BUS_ERROR_LOAD_FAILED)) { + + *job = NULL; + sd_bus_error_free(error); + return 0; + } + + return r; + } + + return strdup_job(reply, job); +} + +int manager_abandon_scope(Manager *manager, const char *scope, sd_bus_error *error) { + _cleanup_free_ char *path = NULL; + int r; + + assert(manager); + assert(scope); + + path = unit_dbus_path_from_name(scope); + if (!path) + return -ENOMEM; + + r = sd_bus_call_method( + manager->bus, + "org.freedesktop.systemd1", + path, + "org.freedesktop.systemd1.Scope", + "Abandon", + error, + NULL, + NULL); + if (r < 0) { + if (sd_bus_error_has_name(error, BUS_ERROR_NO_SUCH_UNIT) || + sd_bus_error_has_name(error, BUS_ERROR_LOAD_FAILED) || + sd_bus_error_has_name(error, BUS_ERROR_SCOPE_NOT_RUNNING)) { + sd_bus_error_free(error); + return 0; + } + + return r; + } + + return 1; +} + +int manager_kill_unit(Manager *manager, const char *unit, KillWho who, int signo, sd_bus_error *error) { + assert(manager); + assert(unit); + + return sd_bus_call_method( + manager->bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "KillUnit", + error, + NULL, + "ssi", unit, who == KILL_LEADER ? "main" : "all", signo); +} + +int manager_unit_is_active(Manager *manager, const char *unit) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + _cleanup_free_ char *path = NULL; + const char *state; + int r; + + assert(manager); + assert(unit); + + path = unit_dbus_path_from_name(unit); + if (!path) + return -ENOMEM; + + r = sd_bus_get_property( + manager->bus, + "org.freedesktop.systemd1", + path, + "org.freedesktop.systemd1.Unit", + "ActiveState", + &error, + &reply, + "s"); + if (r < 0) { + /* systemd might have droppped off momentarily, let's + * not make this an error */ + if (sd_bus_error_has_name(&error, SD_BUS_ERROR_NO_REPLY) || + sd_bus_error_has_name(&error, SD_BUS_ERROR_DISCONNECTED)) + return true; + + /* If the unit is already unloaded then it's not + * active */ + if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_UNIT) || + sd_bus_error_has_name(&error, BUS_ERROR_LOAD_FAILED)) + return false; + + return r; + } + + r = sd_bus_message_read(reply, "s", &state); + if (r < 0) + return -EINVAL; + + return !streq(state, "inactive") && !streq(state, "failed"); +} + +int manager_job_is_active(Manager *manager, const char *path) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + int r; + + assert(manager); + assert(path); + + r = sd_bus_get_property( + manager->bus, + "org.freedesktop.systemd1", + path, + "org.freedesktop.systemd1.Job", + "State", + &error, + &reply, + "s"); + if (r < 0) { + if (sd_bus_error_has_name(&error, SD_BUS_ERROR_NO_REPLY) || + sd_bus_error_has_name(&error, SD_BUS_ERROR_DISCONNECTED)) + return true; + + if (sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_OBJECT)) + return false; + + return r; + } + + /* We don't actually care about the state really. The fact + * that we could read the job state is enough for us */ + + return true; +} diff --git a/src/grp-login/systemd-logind/logind-device.c b/src/grp-login/systemd-logind/logind-device.c new file mode 100644 index 0000000000..6165a9ea6e --- /dev/null +++ b/src/grp-login/systemd-logind/logind-device.c @@ -0,0 +1,125 @@ +/*** + 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 Lesser General Public License as published by + the Free Software Foundation; either version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include "basic/alloc-util.h" +#include "basic/util.h" + +#include "logind-device.h" + +Device* device_new(Manager *m, const char *sysfs, bool master) { + Device *d; + + assert(m); + assert(sysfs); + + d = new0(Device, 1); + if (!d) + return NULL; + + d->sysfs = strdup(sysfs); + if (!d->sysfs) { + free(d); + return NULL; + } + + if (hashmap_put(m->devices, d->sysfs, d) < 0) { + free(d->sysfs); + free(d); + return NULL; + } + + d->manager = m; + d->master = master; + dual_timestamp_get(&d->timestamp); + + return d; +} + +static void device_detach(Device *d) { + Seat *s; + SessionDevice *sd; + + assert(d); + + if (!d->seat) + return; + + while ((sd = d->session_devices)) + session_device_free(sd); + + s = d->seat; + LIST_REMOVE(devices, d->seat->devices, d); + d->seat = NULL; + + if (!seat_has_master_device(s)) { + seat_add_to_gc_queue(s); + seat_send_changed(s, "CanGraphical", NULL); + } +} + +void device_free(Device *d) { + assert(d); + + device_detach(d); + + hashmap_remove(d->manager->devices, d->sysfs); + + free(d->sysfs); + free(d); +} + +void device_attach(Device *d, Seat *s) { + Device *i; + bool had_master; + + assert(d); + assert(s); + + if (d->seat == s) + return; + + if (d->seat) + device_detach(d); + + d->seat = s; + had_master = seat_has_master_device(s); + + /* We keep the device list sorted by the "master" flag. That is, master + * devices are at the front, other devices at the tail. As there is no + * way to easily add devices at the list-tail, we need to iterate the + * list to find the first non-master device when adding non-master + * devices. We assume there is only a few (normally 1) master devices + * per seat, so we iterate only a few times. */ + + if (d->master || !s->devices) + LIST_PREPEND(devices, s->devices, d); + else { + LIST_FOREACH(devices, i, s->devices) { + if (!i->devices_next || !i->master) { + LIST_INSERT_AFTER(devices, s->devices, i, d); + break; + } + } + } + + if (!had_master && d->master) + seat_send_changed(s, "CanGraphical", NULL); +} diff --git a/src/grp-login/systemd-logind/logind-device.h b/src/grp-login/systemd-logind/logind-device.h new file mode 100644 index 0000000000..9e2678d801 --- /dev/null +++ b/src/grp-login/systemd-logind/logind-device.h @@ -0,0 +1,44 @@ +#pragma once + +/*** + 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 Lesser General Public License as published by + the Free Software Foundation; either version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "basic/list.h" + +typedef struct Device Device; + +#include "logind-seat.h" +#include "logind-session-device.h" + +struct Device { + Manager *manager; + + char *sysfs; + Seat *seat; + bool master; + + dual_timestamp timestamp; + + LIST_FIELDS(struct Device, devices); + LIST_HEAD(SessionDevice, session_devices); +}; + +Device* device_new(Manager *m, const char *sysfs, bool master); +void device_free(Device *d); +void device_attach(Device *d, Seat *s); diff --git a/src/grp-login/systemd-logind/logind-gperf.c b/src/grp-login/systemd-logind/logind-gperf.c new file mode 100644 index 0000000000..9cac645686 --- /dev/null +++ b/src/grp-login/systemd-logind/logind-gperf.c @@ -0,0 +1,144 @@ +/* ANSI-C code produced by gperf version 3.0.4 */ +/* Command-line: gperf */ +/* Computed positions: -k'10' */ + +#if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) \ + && ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) \ + && (')' == 41) && ('*' == 42) && ('+' == 43) && (',' == 44) \ + && ('-' == 45) && ('.' == 46) && ('/' == 47) && ('0' == 48) \ + && ('1' == 49) && ('2' == 50) && ('3' == 51) && ('4' == 52) \ + && ('5' == 53) && ('6' == 54) && ('7' == 55) && ('8' == 56) \ + && ('9' == 57) && (':' == 58) && (';' == 59) && ('<' == 60) \ + && ('=' == 61) && ('>' == 62) && ('?' == 63) && ('A' == 65) \ + && ('B' == 66) && ('C' == 67) && ('D' == 68) && ('E' == 69) \ + && ('F' == 70) && ('G' == 71) && ('H' == 72) && ('I' == 73) \ + && ('J' == 74) && ('K' == 75) && ('L' == 76) && ('M' == 77) \ + && ('N' == 78) && ('O' == 79) && ('P' == 80) && ('Q' == 81) \ + && ('R' == 82) && ('S' == 83) && ('T' == 84) && ('U' == 85) \ + && ('V' == 86) && ('W' == 87) && ('X' == 88) && ('Y' == 89) \ + && ('Z' == 90) && ('[' == 91) && ('\\' == 92) && (']' == 93) \ + && ('^' == 94) && ('_' == 95) && ('a' == 97) && ('b' == 98) \ + && ('c' == 99) && ('d' == 100) && ('e' == 101) && ('f' == 102) \ + && ('g' == 103) && ('h' == 104) && ('i' == 105) && ('j' == 106) \ + && ('k' == 107) && ('l' == 108) && ('m' == 109) && ('n' == 110) \ + && ('o' == 111) && ('p' == 112) && ('q' == 113) && ('r' == 114) \ + && ('s' == 115) && ('t' == 116) && ('u' == 117) && ('v' == 118) \ + && ('w' == 119) && ('x' == 120) && ('y' == 121) && ('z' == 122) \ + && ('{' == 123) && ('|' == 124) && ('}' == 125) && ('~' == 126)) +/* The character set is not based on ISO-646. */ +#error "gperf generated tables don't work with this execution character set. Please report a bug to ." +#endif + + +#include +#include "shared/conf-parser.h" +#include "logind.h" +#include + +#define TOTAL_KEYWORDS 23 +#define MIN_WORD_LENGTH 14 +#define MAX_WORD_LENGTH 33 +#define MIN_HASH_VALUE 14 +#define MAX_HASH_VALUE 44 +/* maximum key range = 31, duplicates = 0 */ + +#ifdef __GNUC__ +__inline +#else +#ifdef __cplusplus +inline +#endif +#endif +static unsigned int +logind_gperf_hash (register const char *str, register unsigned int len) +{ + static const unsigned char asso_values[] = + { + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 0, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 0, 0, 45, 45, 45, 20, 45, 45, 15, 45, + 45, 10, 0, 45, 0, 0, 0, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45 + }; + return len + asso_values[(unsigned char)str[9]]; +} + +#ifdef __GNUC__ +__inline +#if defined __GNUC_STDC_INLINE__ || defined __GNUC_GNU_INLINE__ +__attribute__ ((__gnu_inline__)) +#endif +#endif +const struct ConfigPerfItem * +logind_gperf_lookup (register const char *str, register unsigned int len) +{ + static const struct ConfigPerfItem wordlist[] = + { + {(char*)0}, {(char*)0}, {(char*)0}, {(char*)0}, + {(char*)0}, {(char*)0}, {(char*)0}, {(char*)0}, + {(char*)0}, {(char*)0}, {(char*)0}, {(char*)0}, + {(char*)0}, {(char*)0}, + {"Login.NAutoVTs", config_parse_unsigned, 0, offsetof(Manager, n_autovts)}, + {"Login.ReserveVT", config_parse_unsigned, 0, offsetof(Manager, reserve_vt)}, + {"Login.IdleAction", config_parse_handle_action, 0, offsetof(Manager, idle_action)}, + {"Login.SessionsMax", config_parse_uint64, 0, offsetof(Manager, sessions_max)}, + {"Login.UserTasksMax", config_parse_uint64, 0, offsetof(Manager, user_tasks_max)}, + {"Login.IdleActionSec", config_parse_sec, 0, offsetof(Manager, idle_action_usec)}, + {"Login.HandlePowerKey", config_parse_handle_action, 0, offsetof(Manager, handle_power_key)}, + {"Login.HandleLidSwitch", config_parse_handle_action, 0, offsetof(Manager, handle_lid_switch)}, + {"Login.HandleSuspendKey", config_parse_handle_action, 0, offsetof(Manager, handle_suspend_key)}, + {"Login.HoldoffTimeoutSec", config_parse_sec, 0, offsetof(Manager, holdoff_timeout_usec)}, + {"Login.HandleHibernateKey", config_parse_handle_action, 0, offsetof(Manager, handle_hibernate_key)}, + {"Login.RemoveIPC", config_parse_bool, 0, offsetof(Manager, remove_ipc)}, + {"Login.RuntimeDirectorySize", config_parse_tmpfs_size, 0, offsetof(Manager, runtime_dir_size)}, + {"Login.HandleLidSwitchDocked", config_parse_handle_action, 0, offsetof(Manager, handle_lid_switch_docked)}, + {(char*)0}, + {"Login.PowerKeyIgnoreInhibited", config_parse_bool, 0, offsetof(Manager, power_key_ignore_inhibited)}, + {"Login.LidSwitchIgnoreInhibited", config_parse_bool, 0, offsetof(Manager, lid_switch_ignore_inhibited)}, + {"Login.SuspendKeyIgnoreInhibited", config_parse_bool, 0, offsetof(Manager, suspend_key_ignore_inhibited)}, + {(char*)0}, + {"Login.HibernateKeyIgnoreInhibited", config_parse_bool, 0, offsetof(Manager, hibernate_key_ignore_inhibited)}, + {"Login.KillOnlyUsers", config_parse_strv, 0, offsetof(Manager, kill_only_users)}, + {(char*)0}, {(char*)0}, + {"Login.KillExcludeUsers", config_parse_strv, 0, offsetof(Manager, kill_exclude_users)}, + {"Login.KillUserProcesses", config_parse_bool, 0, offsetof(Manager, kill_user_processes)}, + {"Login.InhibitorsMax", config_parse_uint64, 0, offsetof(Manager, inhibitors_max)}, + {(char*)0}, {(char*)0}, {(char*)0}, {(char*)0}, + {"Login.InhibitDelayMaxSec", config_parse_sec, 0, offsetof(Manager, inhibit_delay_max)} + }; + + if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH) + { + register int key = logind_gperf_hash (str, len); + + if (key <= MAX_HASH_VALUE && key >= 0) + { + register const char *s = wordlist[key].section_and_lvalue; + + if (s && *str == *s && !strcmp (str + 1, s + 1)) + return &wordlist[key]; + } + } + return 0; +} diff --git a/src/grp-login/systemd-logind/logind-gperf.gperf b/src/grp-login/systemd-logind/logind-gperf.gperf new file mode 100644 index 0000000000..6d5c484439 --- /dev/null +++ b/src/grp-login/systemd-logind/logind-gperf.gperf @@ -0,0 +1,39 @@ +%{ +#include +#include "shared/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.ReserveVT, config_parse_unsigned, 0, offsetof(Manager, reserve_vt) +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.InhibitDelayMaxSec, config_parse_sec, 0, offsetof(Manager, inhibit_delay_max) +Login.HandlePowerKey, config_parse_handle_action, 0, offsetof(Manager, handle_power_key) +Login.HandleSuspendKey, config_parse_handle_action, 0, offsetof(Manager, handle_suspend_key) +Login.HandleHibernateKey, config_parse_handle_action, 0, offsetof(Manager, handle_hibernate_key) +Login.HandleLidSwitch, config_parse_handle_action, 0, offsetof(Manager, handle_lid_switch) +Login.HandleLidSwitchDocked, config_parse_handle_action, 0, offsetof(Manager, handle_lid_switch_docked) +Login.PowerKeyIgnoreInhibited, config_parse_bool, 0, offsetof(Manager, power_key_ignore_inhibited) +Login.SuspendKeyIgnoreInhibited, config_parse_bool, 0, offsetof(Manager, suspend_key_ignore_inhibited) +Login.HibernateKeyIgnoreInhibited, config_parse_bool, 0, offsetof(Manager, hibernate_key_ignore_inhibited) +Login.LidSwitchIgnoreInhibited, config_parse_bool, 0, offsetof(Manager, lid_switch_ignore_inhibited) +Login.HoldoffTimeoutSec, config_parse_sec, 0, offsetof(Manager, holdoff_timeout_usec) +Login.IdleAction, config_parse_handle_action, 0, offsetof(Manager, idle_action) +Login.IdleActionSec, config_parse_sec, 0, offsetof(Manager, idle_action_usec) +Login.RuntimeDirectorySize, config_parse_tmpfs_size, 0, offsetof(Manager, runtime_dir_size) +Login.RemoveIPC, config_parse_bool, 0, offsetof(Manager, remove_ipc) +Login.InhibitorsMax, config_parse_uint64, 0, offsetof(Manager, inhibitors_max) +Login.SessionsMax, config_parse_uint64, 0, offsetof(Manager, sessions_max) +Login.UserTasksMax, config_parse_uint64, 0, offsetof(Manager, user_tasks_max) diff --git a/src/grp-login/systemd-logind/logind-inhibit.c b/src/grp-login/systemd-logind/logind-inhibit.c new file mode 100644 index 0000000000..8a35db0dc8 --- /dev/null +++ b/src/grp-login/systemd-logind/logind-inhibit.c @@ -0,0 +1,486 @@ +/*** + 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 Lesser General Public License as published by + the Free Software Foundation; either version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/escape.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/formats-util.h" +#include "basic/mkdir.h" +#include "basic/parse-util.h" +#include "basic/string-table.h" +#include "basic/string-util.h" +#include "basic/user-util.h" +#include "basic/util.h" + +#include "logind-inhibit.h" + +Inhibitor* inhibitor_new(Manager *m, const char* id) { + Inhibitor *i; + + assert(m); + + i = new0(Inhibitor, 1); + if (!i) + return NULL; + + i->state_file = strappend("/run/systemd/inhibit/", id); + if (!i->state_file) { + free(i); + return NULL; + } + + i->id = basename(i->state_file); + + if (hashmap_put(m->inhibitors, i->id, i) < 0) { + free(i->state_file); + free(i); + return NULL; + } + + i->manager = m; + i->fifo_fd = -1; + + return i; +} + +void inhibitor_free(Inhibitor *i) { + assert(i); + + hashmap_remove(i->manager->inhibitors, i->id); + + inhibitor_remove_fifo(i); + + free(i->who); + free(i->why); + + if (i->state_file) { + unlink(i->state_file); + free(i->state_file); + } + + free(i); +} + +int inhibitor_save(Inhibitor *i) { + _cleanup_free_ char *temp_path = NULL; + _cleanup_fclose_ FILE *f = NULL; + int r; + + assert(i); + + r = mkdir_safe_label("/run/systemd/inhibit", 0755, 0, 0); + if (r < 0) + goto fail; + + r = fopen_temporary(i->state_file, &f, &temp_path); + if (r < 0) + goto fail; + + fchmod(fileno(f), 0644); + + fprintf(f, + "# This is private data. Do not parse.\n" + "WHAT=%s\n" + "MODE=%s\n" + "UID="UID_FMT"\n" + "PID="PID_FMT"\n", + inhibit_what_to_string(i->what), + inhibit_mode_to_string(i->mode), + i->uid, + i->pid); + + if (i->who) { + _cleanup_free_ char *cc = NULL; + + cc = cescape(i->who); + if (!cc) { + r = -ENOMEM; + goto fail; + } + + fprintf(f, "WHO=%s\n", cc); + } + + if (i->why) { + _cleanup_free_ char *cc = NULL; + + cc = cescape(i->why); + if (!cc) { + r = -ENOMEM; + goto fail; + } + + fprintf(f, "WHY=%s\n", cc); + } + + if (i->fifo_path) + fprintf(f, "FIFO=%s\n", i->fifo_path); + + r = fflush_and_check(f); + if (r < 0) + goto fail; + + if (rename(temp_path, i->state_file) < 0) { + r = -errno; + goto fail; + } + + return 0; + +fail: + (void) unlink(i->state_file); + + if (temp_path) + (void) unlink(temp_path); + + return log_error_errno(r, "Failed to save inhibit data %s: %m", i->state_file); +} + +int inhibitor_start(Inhibitor *i) { + assert(i); + + if (i->started) + return 0; + + dual_timestamp_get(&i->since); + + log_debug("Inhibitor %s (%s) pid="PID_FMT" uid="UID_FMT" mode=%s started.", + strna(i->who), strna(i->why), + i->pid, i->uid, + inhibit_mode_to_string(i->mode)); + + inhibitor_save(i); + + i->started = true; + + manager_send_changed(i->manager, i->mode == INHIBIT_BLOCK ? "BlockInhibited" : "DelayInhibited", NULL); + + return 0; +} + +int inhibitor_stop(Inhibitor *i) { + assert(i); + + if (i->started) + log_debug("Inhibitor %s (%s) pid="PID_FMT" uid="UID_FMT" mode=%s stopped.", + strna(i->who), strna(i->why), + i->pid, i->uid, + inhibit_mode_to_string(i->mode)); + + if (i->state_file) + unlink(i->state_file); + + i->started = false; + + manager_send_changed(i->manager, i->mode == INHIBIT_BLOCK ? "BlockInhibited" : "DelayInhibited", NULL); + + return 0; +} + +int inhibitor_load(Inhibitor *i) { + + _cleanup_free_ char + *what = NULL, + *uid = NULL, + *pid = NULL, + *who = NULL, + *why = NULL, + *mode = NULL; + + InhibitWhat w; + InhibitMode mm; + char *cc; + int r; + + r = parse_env_file(i->state_file, NEWLINE, + "WHAT", &what, + "UID", &uid, + "PID", &pid, + "WHO", &who, + "WHY", &why, + "MODE", &mode, + "FIFO", &i->fifo_path, + NULL); + if (r < 0) + return r; + + w = what ? inhibit_what_from_string(what) : 0; + if (w >= 0) + i->what = w; + + mm = mode ? inhibit_mode_from_string(mode) : INHIBIT_BLOCK; + if (mm >= 0) + i->mode = mm; + + if (uid) { + r = parse_uid(uid, &i->uid); + if (r < 0) + return r; + } + + if (pid) { + r = parse_pid(pid, &i->pid); + if (r < 0) + return r; + } + + if (who) { + r = cunescape(who, 0, &cc); + if (r < 0) + return r; + + free(i->who); + i->who = cc; + } + + if (why) { + r = cunescape(why, 0, &cc); + if (r < 0) + return r; + + free(i->why); + i->why = cc; + } + + if (i->fifo_path) { + int fd; + + fd = inhibitor_create_fifo(i); + safe_close(fd); + } + + return 0; +} + +static int inhibitor_dispatch_fifo(sd_event_source *s, int fd, uint32_t revents, void *userdata) { + Inhibitor *i = userdata; + + assert(s); + assert(fd == i->fifo_fd); + assert(i); + + inhibitor_stop(i); + inhibitor_free(i); + + return 0; +} + +int inhibitor_create_fifo(Inhibitor *i) { + int r; + + assert(i); + + /* Create FIFO */ + if (!i->fifo_path) { + r = mkdir_safe_label("/run/systemd/inhibit", 0755, 0, 0); + if (r < 0) + return r; + + i->fifo_path = strjoin("/run/systemd/inhibit/", i->id, ".ref", NULL); + if (!i->fifo_path) + return -ENOMEM; + + if (mkfifo(i->fifo_path, 0600) < 0 && errno != EEXIST) + return -errno; + } + + /* Open reading side */ + if (i->fifo_fd < 0) { + i->fifo_fd = open(i->fifo_path, O_RDONLY|O_CLOEXEC|O_NDELAY); + if (i->fifo_fd < 0) + return -errno; + } + + if (!i->event_source) { + r = sd_event_add_io(i->manager->event, &i->event_source, i->fifo_fd, 0, inhibitor_dispatch_fifo, i); + if (r < 0) + return r; + + r = sd_event_source_set_priority(i->event_source, SD_EVENT_PRIORITY_IDLE-10); + if (r < 0) + return r; + } + + /* Open writing side */ + r = open(i->fifo_path, O_WRONLY|O_CLOEXEC|O_NDELAY); + if (r < 0) + return -errno; + + return r; +} + +void inhibitor_remove_fifo(Inhibitor *i) { + assert(i); + + i->event_source = sd_event_source_unref(i->event_source); + i->fifo_fd = safe_close(i->fifo_fd); + + if (i->fifo_path) { + unlink(i->fifo_path); + i->fifo_path = mfree(i->fifo_path); + } +} + +InhibitWhat manager_inhibit_what(Manager *m, InhibitMode mm) { + Inhibitor *i; + Iterator j; + InhibitWhat what = 0; + + assert(m); + + HASHMAP_FOREACH(i, m->inhibitors, j) + if (i->mode == mm) + what |= i->what; + + return what; +} + +static int pid_is_active(Manager *m, pid_t pid) { + Session *s; + int r; + + r = manager_get_session_by_pid(m, pid, &s); + if (r < 0) + return r; + + /* If there's no session assigned to it, then it's globally + * active on all ttys */ + if (r == 0) + return 1; + + return session_is_active(s); +} + +bool manager_is_inhibited( + Manager *m, + InhibitWhat w, + InhibitMode mm, + dual_timestamp *since, + bool ignore_inactive, + bool ignore_uid, + uid_t uid, + Inhibitor **offending) { + + Inhibitor *i; + Iterator j; + struct dual_timestamp ts = DUAL_TIMESTAMP_NULL; + bool inhibited = false; + + assert(m); + assert(w > 0 && w < _INHIBIT_WHAT_MAX); + + HASHMAP_FOREACH(i, m->inhibitors, j) { + if (!(i->what & w)) + continue; + + if (i->mode != mm) + continue; + + if (ignore_inactive && pid_is_active(m, i->pid) <= 0) + continue; + + if (ignore_uid && i->uid == uid) + continue; + + if (!inhibited || + i->since.monotonic < ts.monotonic) + ts = i->since; + + inhibited = true; + + if (offending) + *offending = i; + } + + if (since) + *since = ts; + + return inhibited; +} + +const char *inhibit_what_to_string(InhibitWhat w) { + static thread_local char buffer[97]; + char *p; + + if (w < 0 || w >= _INHIBIT_WHAT_MAX) + return NULL; + + p = buffer; + if (w & INHIBIT_SHUTDOWN) + p = stpcpy(p, "shutdown:"); + if (w & INHIBIT_SLEEP) + p = stpcpy(p, "sleep:"); + if (w & INHIBIT_IDLE) + p = stpcpy(p, "idle:"); + if (w & INHIBIT_HANDLE_POWER_KEY) + p = stpcpy(p, "handle-power-key:"); + if (w & INHIBIT_HANDLE_SUSPEND_KEY) + p = stpcpy(p, "handle-suspend-key:"); + if (w & INHIBIT_HANDLE_HIBERNATE_KEY) + p = stpcpy(p, "handle-hibernate-key:"); + if (w & INHIBIT_HANDLE_LID_SWITCH) + p = stpcpy(p, "handle-lid-switch:"); + + if (p > buffer) + *(p-1) = 0; + else + *p = 0; + + return buffer; +} + +InhibitWhat inhibit_what_from_string(const char *s) { + InhibitWhat what = 0; + const char *word, *state; + size_t l; + + FOREACH_WORD_SEPARATOR(word, l, s, ":", state) { + if (l == 8 && strneq(word, "shutdown", l)) + what |= INHIBIT_SHUTDOWN; + else if (l == 5 && strneq(word, "sleep", l)) + what |= INHIBIT_SLEEP; + else if (l == 4 && strneq(word, "idle", l)) + what |= INHIBIT_IDLE; + else if (l == 16 && strneq(word, "handle-power-key", l)) + what |= INHIBIT_HANDLE_POWER_KEY; + else if (l == 18 && strneq(word, "handle-suspend-key", l)) + what |= INHIBIT_HANDLE_SUSPEND_KEY; + else if (l == 20 && strneq(word, "handle-hibernate-key", l)) + what |= INHIBIT_HANDLE_HIBERNATE_KEY; + else if (l == 17 && strneq(word, "handle-lid-switch", l)) + what |= INHIBIT_HANDLE_LID_SWITCH; + else + return _INHIBIT_WHAT_INVALID; + } + + return what; +} + +static const char* const inhibit_mode_table[_INHIBIT_MODE_MAX] = { + [INHIBIT_BLOCK] = "block", + [INHIBIT_DELAY] = "delay" +}; + +DEFINE_STRING_TABLE_LOOKUP(inhibit_mode, InhibitMode); diff --git a/src/grp-login/systemd-logind/logind-inhibit.h b/src/grp-login/systemd-logind/logind-inhibit.h new file mode 100644 index 0000000000..70de199c60 --- /dev/null +++ b/src/grp-login/systemd-logind/logind-inhibit.h @@ -0,0 +1,89 @@ +#pragma once + +/*** + 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 Lesser General Public License as published by + the Free Software Foundation; either version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +typedef struct Inhibitor Inhibitor; + + +typedef enum InhibitWhat { + INHIBIT_SHUTDOWN = 1, + INHIBIT_SLEEP = 2, + INHIBIT_IDLE = 4, + INHIBIT_HANDLE_POWER_KEY = 8, + INHIBIT_HANDLE_SUSPEND_KEY = 16, + INHIBIT_HANDLE_HIBERNATE_KEY = 32, + INHIBIT_HANDLE_LID_SWITCH = 64, + _INHIBIT_WHAT_MAX = 128, + _INHIBIT_WHAT_INVALID = -1 +} InhibitWhat; + +typedef enum InhibitMode { + INHIBIT_BLOCK, + INHIBIT_DELAY, + _INHIBIT_MODE_MAX, + _INHIBIT_MODE_INVALID = -1 +} InhibitMode; + +#include "logind.h" + +struct Inhibitor { + Manager *manager; + + sd_event_source *event_source; + + char *id; + char *state_file; + + bool started; + + InhibitWhat what; + char *who; + char *why; + InhibitMode mode; + + pid_t pid; + uid_t uid; + + dual_timestamp since; + + char *fifo_path; + int fifo_fd; +}; + +Inhibitor* inhibitor_new(Manager *m, const char *id); +void inhibitor_free(Inhibitor *i); + +int inhibitor_save(Inhibitor *i); +int inhibitor_load(Inhibitor *i); + +int inhibitor_start(Inhibitor *i); +int inhibitor_stop(Inhibitor *i); + +int inhibitor_create_fifo(Inhibitor *i); +void inhibitor_remove_fifo(Inhibitor *i); + +InhibitWhat manager_inhibit_what(Manager *m, InhibitMode mm); +bool manager_is_inhibited(Manager *m, InhibitWhat w, InhibitMode mm, dual_timestamp *since, bool ignore_inactive, bool ignore_uid, uid_t uid, Inhibitor **offending); + +const char *inhibit_what_to_string(InhibitWhat k); +InhibitWhat inhibit_what_from_string(const char *s); + +const char *inhibit_mode_to_string(InhibitMode k); +InhibitMode inhibit_mode_from_string(const char *s); diff --git a/src/grp-login/systemd-logind/logind-seat-dbus.c b/src/grp-login/systemd-logind/logind-seat-dbus.c new file mode 100644 index 0000000000..fbebe6fcff --- /dev/null +++ b/src/grp-login/systemd-logind/logind-seat-dbus.c @@ -0,0 +1,475 @@ +/*** + 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 Lesser General Public License as published by + the Free Software Foundation; either version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include + +#include "basic/alloc-util.h" +#include "basic/bus-label.h" +#include "basic/strv.h" +#include "basic/user-util.h" +#include "basic/util.h" +#include "logind.h" +#include "sd-bus/bus-common-errors.h" +#include "shared/bus-util.h" + +#include "logind-seat.h" + +static int property_get_active_session( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + _cleanup_free_ char *p = NULL; + Seat *s = userdata; + + assert(bus); + assert(reply); + assert(s); + + p = s->active ? session_bus_path(s->active) : strdup("/"); + if (!p) + return -ENOMEM; + + return sd_bus_message_append(reply, "(so)", s->active ? s->active->id : "", p); +} + +static int property_get_can_multi_session( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Seat *s = userdata; + + assert(bus); + assert(reply); + assert(s); + + return sd_bus_message_append(reply, "b", seat_can_multi_session(s)); +} + +static int property_get_can_tty( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Seat *s = userdata; + + assert(bus); + assert(reply); + assert(s); + + return sd_bus_message_append(reply, "b", seat_can_tty(s)); +} + +static int property_get_can_graphical( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Seat *s = userdata; + + assert(bus); + assert(reply); + assert(s); + + return sd_bus_message_append(reply, "b", seat_can_graphical(s)); +} + +static int property_get_sessions( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Seat *s = userdata; + Session *session; + int r; + + assert(bus); + assert(reply); + assert(s); + + r = sd_bus_message_open_container(reply, 'a', "(so)"); + if (r < 0) + return r; + + LIST_FOREACH(sessions_by_seat, session, s->sessions) { + _cleanup_free_ char *p = NULL; + + p = session_bus_path(session); + if (!p) + return -ENOMEM; + + r = sd_bus_message_append(reply, "(so)", session->id, p); + if (r < 0) + return r; + + } + + r = sd_bus_message_close_container(reply); + if (r < 0) + return r; + + return 1; +} + +static int property_get_idle_hint( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Seat *s = userdata; + + assert(bus); + assert(reply); + assert(s); + + return sd_bus_message_append(reply, "b", seat_get_idle_hint(s, NULL) > 0); +} + +static int property_get_idle_since_hint( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Seat *s = userdata; + dual_timestamp t; + uint64_t u; + int r; + + assert(bus); + assert(reply); + assert(s); + + r = seat_get_idle_hint(s, &t); + if (r < 0) + return r; + + u = streq(property, "IdleSinceHint") ? t.realtime : t.monotonic; + + return sd_bus_message_append(reply, "t", u); +} + +int bus_seat_method_terminate(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Seat *s = userdata; + int r; + + assert(message); + assert(s); + + r = bus_verify_polkit_async( + message, + CAP_KILL, + "org.freedesktop.login1.manage", + NULL, + false, + UID_INVALID, + &s->manager->polkit_registry, + error); + if (r < 0) + return r; + if (r == 0) + return 1; /* Will call us back */ + + r = seat_stop_sessions(s, true); + if (r < 0) + return r; + + return sd_bus_reply_method_return(message, NULL); +} + +static int method_activate_session(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Seat *s = userdata; + const char *name; + Session *session; + int r; + + assert(message); + assert(s); + + r = sd_bus_message_read(message, "s", &name); + if (r < 0) + return r; + + session = hashmap_get(s->manager->sessions, name); + if (!session) + return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_SESSION, "No session '%s' known", name); + + if (session->seat != s) + return sd_bus_error_setf(error, BUS_ERROR_SESSION_NOT_ON_SEAT, "Session %s not on seat %s", name, s->id); + + r = session_activate(session); + if (r < 0) + return r; + + return sd_bus_reply_method_return(message, NULL); +} + +static int method_switch_to(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Seat *s = userdata; + unsigned int to; + int r; + + assert(message); + assert(s); + + r = sd_bus_message_read(message, "u", &to); + if (r < 0) + return r; + + if (to <= 0) + return -EINVAL; + + r = seat_switch_to(s, to); + if (r < 0) + return r; + + return sd_bus_reply_method_return(message, NULL); +} + +static int method_switch_to_next(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Seat *s = userdata; + int r; + + assert(message); + assert(s); + + r = seat_switch_to_next(s); + if (r < 0) + return r; + + return sd_bus_reply_method_return(message, NULL); +} + +static int method_switch_to_previous(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Seat *s = userdata; + int r; + + assert(message); + assert(s); + + r = seat_switch_to_previous(s); + if (r < 0) + return r; + + return sd_bus_reply_method_return(message, NULL); +} + +const sd_bus_vtable seat_vtable[] = { + SD_BUS_VTABLE_START(0), + + SD_BUS_PROPERTY("Id", "s", NULL, offsetof(Seat, id), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("ActiveSession", "(so)", property_get_active_session, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("CanMultiSession", "b", property_get_can_multi_session, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("CanTTY", "b", property_get_can_tty, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("CanGraphical", "b", property_get_can_graphical, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("Sessions", "a(so)", property_get_sessions, 0, 0), + SD_BUS_PROPERTY("IdleHint", "b", property_get_idle_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("IdleSinceHint", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("IdleSinceHintMonotonic", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + + SD_BUS_METHOD("Terminate", NULL, NULL, bus_seat_method_terminate, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("ActivateSession", "s", NULL, method_activate_session, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("SwitchTo", "u", NULL, method_switch_to, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("SwitchToNext", NULL, NULL, method_switch_to_next, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("SwitchToPrevious", NULL, NULL, method_switch_to_previous, SD_BUS_VTABLE_UNPRIVILEGED), + + SD_BUS_VTABLE_END +}; + +int seat_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) { + Manager *m = userdata; + Seat *seat; + int r; + + assert(bus); + assert(path); + assert(interface); + assert(found); + assert(m); + + if (streq(path, "/org/freedesktop/login1/seat/self")) { + _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; + sd_bus_message *message; + Session *session; + const char *name; + + message = sd_bus_get_current_message(bus); + if (!message) + return 0; + + r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_SESSION|SD_BUS_CREDS_AUGMENT, &creds); + if (r < 0) + return r; + + r = sd_bus_creds_get_session(creds, &name); + if (r < 0) + return r; + + session = hashmap_get(m->sessions, name); + if (!session) + return 0; + + seat = session->seat; + } else { + _cleanup_free_ char *e = NULL; + const char *p; + + p = startswith(path, "/org/freedesktop/login1/seat/"); + if (!p) + return 0; + + e = bus_label_unescape(p); + if (!e) + return -ENOMEM; + + seat = hashmap_get(m->seats, e); + } + + if (!seat) + return 0; + + *found = seat; + return 1; +} + +char *seat_bus_path(Seat *s) { + _cleanup_free_ char *t = NULL; + + assert(s); + + t = bus_label_escape(s->id); + if (!t) + return NULL; + + return strappend("/org/freedesktop/login1/seat/", t); +} + +int seat_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) { + _cleanup_strv_free_ char **l = NULL; + sd_bus_message *message; + Manager *m = userdata; + Seat *seat; + Iterator i; + int r; + + assert(bus); + assert(path); + assert(nodes); + + HASHMAP_FOREACH(seat, m->seats, i) { + char *p; + + p = seat_bus_path(seat); + if (!p) + return -ENOMEM; + + r = strv_consume(&l, p); + if (r < 0) + return r; + } + + message = sd_bus_get_current_message(bus); + if (message) { + _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; + const char *name; + Session *session; + + r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_SESSION|SD_BUS_CREDS_AUGMENT, &creds); + if (r >= 0) { + r = sd_bus_creds_get_session(creds, &name); + if (r >= 0) { + session = hashmap_get(m->sessions, name); + if (session && session->seat) { + r = strv_extend(&l, "/org/freedesktop/login1/seat/self"); + if (r < 0) + return r; + } + } + } + } + + *nodes = l; + l = NULL; + + return 1; +} + +int seat_send_signal(Seat *s, bool new_seat) { + _cleanup_free_ char *p = NULL; + + assert(s); + + p = seat_bus_path(s); + if (!p) + return -ENOMEM; + + return sd_bus_emit_signal( + s->manager->bus, + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + new_seat ? "SeatNew" : "SeatRemoved", + "so", s->id, p); +} + +int seat_send_changed(Seat *s, const char *properties, ...) { + _cleanup_free_ char *p = NULL; + char **l; + + assert(s); + + if (!s->started) + return 0; + + p = seat_bus_path(s); + if (!p) + return -ENOMEM; + + l = strv_from_stdarg_alloca(properties); + + return sd_bus_emit_properties_changed_strv(s->manager->bus, p, "org.freedesktop.login1.Seat", l); +} diff --git a/src/grp-login/systemd-logind/logind-seat.c b/src/grp-login/systemd-logind/logind-seat.c new file mode 100644 index 0000000000..6525ad5d7b --- /dev/null +++ b/src/grp-login/systemd-logind/logind-seat.c @@ -0,0 +1,696 @@ +/*** + 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 Lesser General Public License as published by + the Free Software Foundation; either version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include + +#include + +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/formats-util.h" +#include "basic/mkdir.h" +#include "basic/parse-util.h" +#include "basic/stdio-util.h" +#include "basic/string-util.h" +#include "basic/terminal-util.h" +#include "basic/util.h" + +#include "logind-acl.h" +#include "logind-seat.h" + +Seat *seat_new(Manager *m, const char *id) { + Seat *s; + + assert(m); + assert(id); + + s = new0(Seat, 1); + if (!s) + return NULL; + + s->state_file = strappend("/run/systemd/seats/", id); + if (!s->state_file) { + free(s); + return NULL; + } + + s->id = basename(s->state_file); + s->manager = m; + + if (hashmap_put(m->seats, s->id, s) < 0) { + free(s->state_file); + free(s); + return NULL; + } + + return s; +} + +void seat_free(Seat *s) { + assert(s); + + if (s->in_gc_queue) + LIST_REMOVE(gc_queue, s->manager->seat_gc_queue, s); + + while (s->sessions) + session_free(s->sessions); + + assert(!s->active); + + while (s->devices) + device_free(s->devices); + + hashmap_remove(s->manager->seats, s->id); + + free(s->positions); + free(s->state_file); + free(s); +} + +int seat_save(Seat *s) { + _cleanup_free_ char *temp_path = NULL; + _cleanup_fclose_ FILE *f = NULL; + int r; + + assert(s); + + if (!s->started) + return 0; + + r = mkdir_safe_label("/run/systemd/seats", 0755, 0, 0); + if (r < 0) + goto fail; + + r = fopen_temporary(s->state_file, &f, &temp_path); + if (r < 0) + goto fail; + + fchmod(fileno(f), 0644); + + fprintf(f, + "# This is private data. Do not parse.\n" + "IS_SEAT0=%i\n" + "CAN_MULTI_SESSION=%i\n" + "CAN_TTY=%i\n" + "CAN_GRAPHICAL=%i\n", + seat_is_seat0(s), + seat_can_multi_session(s), + seat_can_tty(s), + seat_can_graphical(s)); + + if (s->active) { + assert(s->active->user); + + fprintf(f, + "ACTIVE=%s\n" + "ACTIVE_UID="UID_FMT"\n", + s->active->id, + s->active->user->uid); + } + + if (s->sessions) { + Session *i; + + fputs("SESSIONS=", f); + LIST_FOREACH(sessions_by_seat, i, s->sessions) { + fprintf(f, + "%s%c", + i->id, + i->sessions_by_seat_next ? ' ' : '\n'); + } + + fputs("UIDS=", f); + LIST_FOREACH(sessions_by_seat, i, s->sessions) + fprintf(f, + UID_FMT"%c", + i->user->uid, + i->sessions_by_seat_next ? ' ' : '\n'); + } + + r = fflush_and_check(f); + if (r < 0) + goto fail; + + if (rename(temp_path, s->state_file) < 0) { + r = -errno; + goto fail; + } + + return 0; + +fail: + (void) unlink(s->state_file); + + if (temp_path) + (void) unlink(temp_path); + + return log_error_errno(r, "Failed to save seat data %s: %m", s->state_file); +} + +int seat_load(Seat *s) { + assert(s); + + /* There isn't actually anything to read here ... */ + + return 0; +} + +static int vt_allocate(unsigned int vtnr) { + char p[sizeof("/dev/tty") + DECIMAL_STR_MAX(unsigned int)]; + _cleanup_close_ int fd = -1; + + assert(vtnr >= 1); + + xsprintf(p, "/dev/tty%u", vtnr); + fd = open_terminal(p, O_RDWR|O_NOCTTY|O_CLOEXEC); + if (fd < 0) + return -errno; + + return 0; +} + +int seat_preallocate_vts(Seat *s) { + int r = 0; + unsigned i; + + assert(s); + assert(s->manager); + + log_debug("Preallocating VTs..."); + + if (s->manager->n_autovts <= 0) + return 0; + + if (!seat_has_vts(s)) + return 0; + + for (i = 1; i <= s->manager->n_autovts; i++) { + int q; + + q = vt_allocate(i); + if (q < 0) { + log_error_errno(q, "Failed to preallocate VT %u: %m", i); + r = q; + } + } + + return r; +} + +int seat_apply_acls(Seat *s, Session *old_active) { + int r; + + assert(s); + + r = devnode_acl_all(s->manager->udev, + s->id, + false, + !!old_active, old_active ? old_active->user->uid : 0, + !!s->active, s->active ? s->active->user->uid : 0); + + if (r < 0) + log_error_errno(r, "Failed to apply ACLs: %m"); + + return r; +} + +int seat_set_active(Seat *s, Session *session) { + Session *old_active; + + assert(s); + assert(!session || session->seat == s); + + if (session == s->active) + return 0; + + old_active = s->active; + s->active = session; + + if (old_active) { + session_device_pause_all(old_active); + session_send_changed(old_active, "Active", NULL); + } + + seat_apply_acls(s, old_active); + + if (session && session->started) { + session_send_changed(session, "Active", NULL); + session_device_resume_all(session); + } + + if (!session || session->started) + seat_send_changed(s, "ActiveSession", NULL); + + seat_save(s); + + if (session) { + session_save(session); + user_save(session->user); + } + + if (old_active) { + session_save(old_active); + if (!session || session->user != old_active->user) + user_save(old_active->user); + } + + return 0; +} + +int seat_switch_to(Seat *s, unsigned int num) { + /* Public session positions skip 0 (there is only F1-F12). Maybe it + * will get reassigned in the future, so return error for now. */ + if (num == 0) + return -EINVAL; + + if (num >= s->position_count || !s->positions[num]) { + /* allow switching to unused VTs to trigger auto-activate */ + if (seat_has_vts(s) && num < 64) + return chvt(num); + + return -EINVAL; + } + + return session_activate(s->positions[num]); +} + +int seat_switch_to_next(Seat *s) { + unsigned int start, i; + + if (s->position_count == 0) + return -EINVAL; + + start = 1; + if (s->active && s->active->position > 0) + start = s->active->position; + + for (i = start + 1; i < s->position_count; ++i) + if (s->positions[i]) + return session_activate(s->positions[i]); + + for (i = 1; i < start; ++i) + if (s->positions[i]) + return session_activate(s->positions[i]); + + return -EINVAL; +} + +int seat_switch_to_previous(Seat *s) { + unsigned int start, i; + + if (s->position_count == 0) + return -EINVAL; + + start = 1; + if (s->active && s->active->position > 0) + start = s->active->position; + + for (i = start - 1; i > 0; --i) + if (s->positions[i]) + return session_activate(s->positions[i]); + + for (i = s->position_count - 1; i > start; --i) + if (s->positions[i]) + return session_activate(s->positions[i]); + + return -EINVAL; +} + +int seat_active_vt_changed(Seat *s, unsigned int vtnr) { + Session *i, *new_active = NULL; + int r; + + assert(s); + assert(vtnr >= 1); + + if (!seat_has_vts(s)) + return -EINVAL; + + log_debug("VT changed to %u", vtnr); + + /* we might have earlier closing sessions on the same VT, so try to + * find a running one first */ + LIST_FOREACH(sessions_by_seat, i, s->sessions) + if (i->vtnr == vtnr && !i->stopping) { + new_active = i; + break; + } + + if (!new_active) { + /* no running one? then we can't decide which one is the + * active one, let the first one win */ + LIST_FOREACH(sessions_by_seat, i, s->sessions) + if (i->vtnr == vtnr) { + new_active = i; + break; + } + } + + r = seat_set_active(s, new_active); + manager_spawn_autovt(s->manager, vtnr); + + return r; +} + +int seat_read_active_vt(Seat *s) { + char t[64]; + ssize_t k; + unsigned int vtnr; + int r; + + assert(s); + + if (!seat_has_vts(s)) + return 0; + + lseek(s->manager->console_active_fd, SEEK_SET, 0); + + k = read(s->manager->console_active_fd, t, sizeof(t)-1); + if (k <= 0) { + log_error("Failed to read current console: %s", k < 0 ? strerror(-errno) : "EOF"); + return k < 0 ? -errno : -EIO; + } + + t[k] = 0; + truncate_nl(t); + + if (!startswith(t, "tty")) { + log_error("Hm, /sys/class/tty/tty0/active is badly formatted."); + return -EIO; + } + + r = safe_atou(t+3, &vtnr); + if (r < 0) { + log_error("Failed to parse VT number %s", t+3); + return r; + } + + if (!vtnr) { + log_error("VT number invalid: %s", t+3); + return -EIO; + } + + return seat_active_vt_changed(s, vtnr); +} + +int seat_start(Seat *s) { + assert(s); + + if (s->started) + return 0; + + log_struct(LOG_INFO, + LOG_MESSAGE_ID(SD_MESSAGE_SEAT_START), + "SEAT_ID=%s", s->id, + LOG_MESSAGE("New seat %s.", s->id), + NULL); + + /* Initialize VT magic stuff */ + seat_preallocate_vts(s); + + /* Read current VT */ + seat_read_active_vt(s); + + s->started = true; + + /* Save seat data */ + seat_save(s); + + seat_send_signal(s, true); + + return 0; +} + +int seat_stop(Seat *s, bool force) { + int r = 0; + + assert(s); + + if (s->started) + log_struct(LOG_INFO, + LOG_MESSAGE_ID(SD_MESSAGE_SEAT_STOP), + "SEAT_ID=%s", s->id, + LOG_MESSAGE("Removed seat %s.", s->id), + NULL); + + seat_stop_sessions(s, force); + + unlink(s->state_file); + seat_add_to_gc_queue(s); + + if (s->started) + seat_send_signal(s, false); + + s->started = false; + + return r; +} + +int seat_stop_sessions(Seat *s, bool force) { + Session *session; + int r = 0, k; + + assert(s); + + LIST_FOREACH(sessions_by_seat, session, s->sessions) { + k = session_stop(session, force); + if (k < 0) + r = k; + } + + return r; +} + +void seat_evict_position(Seat *s, Session *session) { + Session *iter; + unsigned int pos = session->position; + + session->position = 0; + + if (pos == 0) + return; + + if (pos < s->position_count && s->positions[pos] == session) { + s->positions[pos] = NULL; + + /* There might be another session claiming the same + * position (eg., during gdm->session transition), so let's look + * for it and set it on the free slot. */ + LIST_FOREACH(sessions_by_seat, iter, s->sessions) { + if (iter->position == pos && session_get_state(iter) != SESSION_CLOSING) { + s->positions[pos] = iter; + break; + } + } + } +} + +void seat_claim_position(Seat *s, Session *session, unsigned int pos) { + /* with VTs, the position is always the same as the VTnr */ + if (seat_has_vts(s)) + pos = session->vtnr; + + if (!GREEDY_REALLOC0(s->positions, s->position_count, pos + 1)) + return; + + seat_evict_position(s, session); + + session->position = pos; + if (pos > 0) + s->positions[pos] = session; +} + +static void seat_assign_position(Seat *s, Session *session) { + unsigned int pos; + + if (session->position > 0) + return; + + for (pos = 1; pos < s->position_count; ++pos) + if (!s->positions[pos]) + break; + + seat_claim_position(s, session, pos); +} + +int seat_attach_session(Seat *s, Session *session) { + assert(s); + assert(session); + assert(!session->seat); + + if (!seat_has_vts(s) != !session->vtnr) + return -EINVAL; + + session->seat = s; + LIST_PREPEND(sessions_by_seat, s->sessions, session); + seat_assign_position(s, session); + + seat_send_changed(s, "Sessions", NULL); + + /* On seats with VTs, the VT logic defines which session is active. On + * seats without VTs, we automatically activate new sessions. */ + if (!seat_has_vts(s)) + seat_set_active(s, session); + + return 0; +} + +void seat_complete_switch(Seat *s) { + Session *session; + + assert(s); + + /* if no session-switch is pending or if it got canceled, do nothing */ + if (!s->pending_switch) + return; + + session = s->pending_switch; + s->pending_switch = NULL; + + seat_set_active(s, session); +} + +bool seat_has_vts(Seat *s) { + assert(s); + + return seat_is_seat0(s) && s->manager->console_active_fd >= 0; +} + +bool seat_is_seat0(Seat *s) { + assert(s); + + return s->manager->seat0 == s; +} + +bool seat_can_multi_session(Seat *s) { + assert(s); + + return seat_has_vts(s); +} + +bool seat_can_tty(Seat *s) { + assert(s); + + return seat_has_vts(s); +} + +bool seat_has_master_device(Seat *s) { + assert(s); + + /* device list is ordered by "master" flag */ + return !!s->devices && s->devices->master; +} + +bool seat_can_graphical(Seat *s) { + assert(s); + + return seat_has_master_device(s); +} + +int seat_get_idle_hint(Seat *s, dual_timestamp *t) { + Session *session; + bool idle_hint = true; + dual_timestamp ts = DUAL_TIMESTAMP_NULL; + + assert(s); + + LIST_FOREACH(sessions_by_seat, session, s->sessions) { + dual_timestamp k; + int ih; + + ih = session_get_idle_hint(session, &k); + if (ih < 0) + return ih; + + if (!ih) { + if (!idle_hint) { + if (k.monotonic > ts.monotonic) + ts = k; + } else { + idle_hint = false; + ts = k; + } + } else if (idle_hint) { + + if (k.monotonic > ts.monotonic) + ts = k; + } + } + + if (t) + *t = ts; + + return idle_hint; +} + +bool seat_check_gc(Seat *s, bool drop_not_started) { + assert(s); + + if (drop_not_started && !s->started) + return false; + + if (seat_is_seat0(s)) + return true; + + return seat_has_master_device(s); +} + +void seat_add_to_gc_queue(Seat *s) { + assert(s); + + if (s->in_gc_queue) + return; + + LIST_PREPEND(gc_queue, s->manager->seat_gc_queue, s); + s->in_gc_queue = true; +} + +static bool seat_name_valid_char(char c) { + return + (c >= 'a' && c <= 'z') || + (c >= 'A' && c <= 'Z') || + (c >= '0' && c <= '9') || + c == '-' || + c == '_'; +} + +bool seat_name_is_valid(const char *name) { + const char *p; + + assert(name); + + if (!startswith(name, "seat")) + return false; + + if (!name[4]) + return false; + + for (p = name; *p; p++) + if (!seat_name_valid_char(*p)) + return false; + + if (strlen(name) > 255) + return false; + + return true; +} diff --git a/src/grp-login/systemd-logind/logind-seat.h b/src/grp-login/systemd-logind/logind-seat.h new file mode 100644 index 0000000000..7fbeb5a727 --- /dev/null +++ b/src/grp-login/systemd-logind/logind-seat.h @@ -0,0 +1,96 @@ +#pragma once + +/*** + 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 Lesser General Public License as published by + the Free Software Foundation; either version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "basic/list.h" + +typedef struct Seat Seat; + +#include "logind-session.h" + +struct Seat { + Manager *manager; + char *id; + + char *state_file; + + LIST_HEAD(Device, devices); + + Session *active; + Session *pending_switch; + LIST_HEAD(Session, sessions); + + Session **positions; + size_t position_count; + + bool in_gc_queue:1; + bool started:1; + + LIST_FIELDS(Seat, gc_queue); +}; + +Seat *seat_new(Manager *m, const char *id); +void seat_free(Seat *s); + +int seat_save(Seat *s); +int seat_load(Seat *s); + +int seat_apply_acls(Seat *s, Session *old_active); +int seat_set_active(Seat *s, Session *session); +int seat_switch_to(Seat *s, unsigned int num); +int seat_switch_to_next(Seat *s); +int seat_switch_to_previous(Seat *s); +int seat_active_vt_changed(Seat *s, unsigned int vtnr); +int seat_read_active_vt(Seat *s); +int seat_preallocate_vts(Seat *s); + +int seat_attach_session(Seat *s, Session *session); +void seat_complete_switch(Seat *s); +void seat_evict_position(Seat *s, Session *session); +void seat_claim_position(Seat *s, Session *session, unsigned int pos); + +bool seat_has_vts(Seat *s); +bool seat_is_seat0(Seat *s); +bool seat_can_multi_session(Seat *s); +bool seat_can_tty(Seat *s); +bool seat_has_master_device(Seat *s); +bool seat_can_graphical(Seat *s); + +int seat_get_idle_hint(Seat *s, dual_timestamp *t); + +int seat_start(Seat *s); +int seat_stop(Seat *s, bool force); +int seat_stop_sessions(Seat *s, bool force); + +bool seat_check_gc(Seat *s, bool drop_not_started); +void seat_add_to_gc_queue(Seat *s); + +bool seat_name_is_valid(const char *name); + +extern const sd_bus_vtable seat_vtable[]; + +int seat_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error); +int seat_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error); +char *seat_bus_path(Seat *s); + +int seat_send_signal(Seat *s, bool new_seat); +int seat_send_changed(Seat *s, const char *properties, ...) _sentinel_; + +int bus_seat_method_terminate(sd_bus_message *message, void *userdata, sd_bus_error *error); diff --git a/src/grp-login/systemd-logind/logind-session-dbus.c b/src/grp-login/systemd-logind/logind-session-dbus.c new file mode 100644 index 0000000000..76228d4474 --- /dev/null +++ b/src/grp-login/systemd-logind/logind-session-dbus.c @@ -0,0 +1,799 @@ +/*** + 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 Lesser General Public License as published by + the Free Software Foundation; either version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include + +#include "basic/alloc-util.h" +#include "basic/bus-label.h" +#include "basic/fd-util.h" +#include "basic/signal-util.h" +#include "basic/strv.h" +#include "basic/util.h" +#include "logind.h" +#include "sd-bus/bus-common-errors.h" +#include "shared/bus-util.h" + +#include "logind-session-device.h" +#include "logind-session.h" + +static int property_get_user( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + _cleanup_free_ char *p = NULL; + Session *s = userdata; + + assert(bus); + assert(reply); + assert(s); + + p = user_bus_path(s->user); + if (!p) + return -ENOMEM; + + return sd_bus_message_append(reply, "(uo)", (uint32_t) s->user->uid, p); +} + +static int property_get_name( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Session *s = userdata; + + assert(bus); + assert(reply); + assert(s); + + return sd_bus_message_append(reply, "s", s->user->name); +} + +static int property_get_seat( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + _cleanup_free_ char *p = NULL; + Session *s = userdata; + + assert(bus); + assert(reply); + assert(s); + + p = s->seat ? seat_bus_path(s->seat) : strdup("/"); + if (!p) + return -ENOMEM; + + return sd_bus_message_append(reply, "(so)", s->seat ? s->seat->id : "", p); +} + +static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_type, session_type, SessionType); +static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_class, session_class, SessionClass); + +static int property_get_active( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Session *s = userdata; + + assert(bus); + assert(reply); + assert(s); + + return sd_bus_message_append(reply, "b", session_is_active(s)); +} + +static int property_get_state( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Session *s = userdata; + + assert(bus); + assert(reply); + assert(s); + + return sd_bus_message_append(reply, "s", session_state_to_string(session_get_state(s))); +} + +static int property_get_idle_hint( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Session *s = userdata; + + assert(bus); + assert(reply); + assert(s); + + return sd_bus_message_append(reply, "b", session_get_idle_hint(s, NULL) > 0); +} + +static int property_get_idle_since_hint( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Session *s = userdata; + dual_timestamp t = DUAL_TIMESTAMP_NULL; + uint64_t u; + int r; + + assert(bus); + assert(reply); + assert(s); + + r = session_get_idle_hint(s, &t); + if (r < 0) + return r; + + u = streq(property, "IdleSinceHint") ? t.realtime : t.monotonic; + + return sd_bus_message_append(reply, "t", u); +} + +static int property_get_locked_hint( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Session *s = userdata; + + assert(bus); + assert(reply); + assert(s); + + return sd_bus_message_append(reply, "b", session_get_locked_hint(s) > 0); +} + +int bus_session_method_terminate(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Session *s = userdata; + int r; + + assert(message); + assert(s); + + r = bus_verify_polkit_async( + message, + CAP_KILL, + "org.freedesktop.login1.manage", + NULL, + false, + s->user->uid, + &s->manager->polkit_registry, + error); + if (r < 0) + return r; + if (r == 0) + return 1; /* Will call us back */ + + r = session_stop(s, true); + if (r < 0) + return r; + + return sd_bus_reply_method_return(message, NULL); +} + +int bus_session_method_activate(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Session *s = userdata; + int r; + + assert(message); + assert(s); + + r = session_activate(s); + if (r < 0) + return r; + + return sd_bus_reply_method_return(message, NULL); +} + +int bus_session_method_lock(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Session *s = userdata; + int r; + + assert(message); + assert(s); + + r = bus_verify_polkit_async( + message, + CAP_SYS_ADMIN, + "org.freedesktop.login1.lock-sessions", + NULL, + false, + s->user->uid, + &s->manager->polkit_registry, + error); + if (r < 0) + return r; + if (r == 0) + return 1; /* Will call us back */ + + r = session_send_lock(s, strstr(sd_bus_message_get_member(message), "Lock")); + if (r < 0) + return r; + + return sd_bus_reply_method_return(message, NULL); +} + +static int method_set_idle_hint(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; + Session *s = userdata; + uid_t uid; + int r, b; + + assert(message); + assert(s); + + r = sd_bus_message_read(message, "b", &b); + if (r < 0) + return r; + + r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_EUID, &creds); + if (r < 0) + return r; + + r = sd_bus_creds_get_euid(creds, &uid); + if (r < 0) + return r; + + if (uid != 0 && uid != s->user->uid) + return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Only owner of session may set idle hint"); + + session_set_idle_hint(s, b); + + return sd_bus_reply_method_return(message, NULL); +} + +static int method_set_locked_hint(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; + Session *s = userdata; + uid_t uid; + int r, b; + + assert(message); + assert(s); + + r = sd_bus_message_read(message, "b", &b); + if (r < 0) + return r; + + r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_EUID, &creds); + if (r < 0) + return r; + + r = sd_bus_creds_get_euid(creds, &uid); + if (r < 0) + return r; + + if (uid != 0 && uid != s->user->uid) + return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Only owner of session may set locked hint"); + + session_set_locked_hint(s, b); + + return sd_bus_reply_method_return(message, NULL); +} + +int bus_session_method_kill(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Session *s = userdata; + const char *swho; + int32_t signo; + KillWho who; + int r; + + assert(message); + assert(s); + + r = sd_bus_message_read(message, "si", &swho, &signo); + if (r < 0) + return r; + + if (isempty(swho)) + who = KILL_ALL; + else { + who = kill_who_from_string(swho); + if (who < 0) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid kill parameter '%s'", swho); + } + + if (!SIGNAL_VALID(signo)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid signal %i", signo); + + r = bus_verify_polkit_async( + message, + CAP_KILL, + "org.freedesktop.login1.manage", + NULL, + false, + s->user->uid, + &s->manager->polkit_registry, + error); + if (r < 0) + return r; + if (r == 0) + return 1; /* Will call us back */ + + r = session_kill(s, who, signo); + if (r < 0) + return r; + + return sd_bus_reply_method_return(message, NULL); +} + +static int method_take_control(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; + Session *s = userdata; + int r, force; + uid_t uid; + + assert(message); + assert(s); + + r = sd_bus_message_read(message, "b", &force); + if (r < 0) + return r; + + r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_EUID, &creds); + if (r < 0) + return r; + + r = sd_bus_creds_get_euid(creds, &uid); + if (r < 0) + return r; + + if (uid != 0 && (force || uid != s->user->uid)) + return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Only owner of session may take control"); + + r = session_set_controller(s, sd_bus_message_get_sender(message), force); + if (r < 0) + return r; + + return sd_bus_reply_method_return(message, NULL); +} + +static int method_release_control(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Session *s = userdata; + + assert(message); + assert(s); + + if (!session_is_controller(s, sd_bus_message_get_sender(message))) + return sd_bus_error_setf(error, BUS_ERROR_NOT_IN_CONTROL, "You are not in control of this session"); + + session_drop_controller(s); + + return sd_bus_reply_method_return(message, NULL); +} + +static int method_take_device(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Session *s = userdata; + uint32_t major, minor; + SessionDevice *sd; + dev_t dev; + int r; + + assert(message); + assert(s); + + r = sd_bus_message_read(message, "uu", &major, &minor); + if (r < 0) + return r; + + if (!session_is_controller(s, sd_bus_message_get_sender(message))) + return sd_bus_error_setf(error, BUS_ERROR_NOT_IN_CONTROL, "You are not in control of this session"); + + dev = makedev(major, minor); + sd = hashmap_get(s->devices, &dev); + if (sd) + /* We don't allow retrieving a device multiple times. + * The related ReleaseDevice call is not ref-counted. + * The caller should use dup() if it requires more + * than one fd (it would be functionally + * equivalent). */ + return sd_bus_error_setf(error, BUS_ERROR_DEVICE_IS_TAKEN, "Device already taken"); + + r = session_device_new(s, dev, &sd); + if (r < 0) + return r; + + r = sd_bus_reply_method_return(message, "hb", sd->fd, !sd->active); + if (r < 0) + session_device_free(sd); + + return r; +} + +static int method_release_device(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Session *s = userdata; + uint32_t major, minor; + SessionDevice *sd; + dev_t dev; + int r; + + assert(message); + assert(s); + + r = sd_bus_message_read(message, "uu", &major, &minor); + if (r < 0) + return r; + + if (!session_is_controller(s, sd_bus_message_get_sender(message))) + return sd_bus_error_setf(error, BUS_ERROR_NOT_IN_CONTROL, "You are not in control of this session"); + + dev = makedev(major, minor); + sd = hashmap_get(s->devices, &dev); + if (!sd) + return sd_bus_error_setf(error, BUS_ERROR_DEVICE_NOT_TAKEN, "Device not taken"); + + session_device_free(sd); + return sd_bus_reply_method_return(message, NULL); +} + +static int method_pause_device_complete(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Session *s = userdata; + uint32_t major, minor; + SessionDevice *sd; + dev_t dev; + int r; + + assert(message); + assert(s); + + r = sd_bus_message_read(message, "uu", &major, &minor); + if (r < 0) + return r; + + if (!session_is_controller(s, sd_bus_message_get_sender(message))) + return sd_bus_error_setf(error, BUS_ERROR_NOT_IN_CONTROL, "You are not in control of this session"); + + dev = makedev(major, minor); + sd = hashmap_get(s->devices, &dev); + if (!sd) + return sd_bus_error_setf(error, BUS_ERROR_DEVICE_NOT_TAKEN, "Device not taken"); + + session_device_complete_pause(sd); + + return sd_bus_reply_method_return(message, NULL); +} + +const sd_bus_vtable session_vtable[] = { + SD_BUS_VTABLE_START(0), + + SD_BUS_PROPERTY("Id", "s", NULL, offsetof(Session, id), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("User", "(uo)", property_get_user, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Name", "s", property_get_name, 0, SD_BUS_VTABLE_PROPERTY_CONST), + BUS_PROPERTY_DUAL_TIMESTAMP("Timestamp", offsetof(Session, timestamp), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("VTNr", "u", NULL, offsetof(Session, vtnr), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Seat", "(so)", property_get_seat, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("TTY", "s", NULL, offsetof(Session, tty), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Display", "s", NULL, offsetof(Session, display), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Remote", "b", bus_property_get_bool, offsetof(Session, remote), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("RemoteHost", "s", NULL, offsetof(Session, remote_host), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("RemoteUser", "s", NULL, offsetof(Session, remote_user), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Service", "s", NULL, offsetof(Session, service), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Desktop", "s", NULL, offsetof(Session, desktop), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Scope", "s", NULL, offsetof(Session, scope), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Leader", "u", bus_property_get_pid, offsetof(Session, leader), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Audit", "u", NULL, offsetof(Session, audit_id), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Type", "s", property_get_type, offsetof(Session, type), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Class", "s", property_get_class, offsetof(Session, class), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Active", "b", property_get_active, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("State", "s", property_get_state, 0, 0), + SD_BUS_PROPERTY("IdleHint", "b", property_get_idle_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("IdleSinceHint", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("IdleSinceHintMonotonic", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("LockedHint", "b", property_get_locked_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + + SD_BUS_METHOD("Terminate", NULL, NULL, bus_session_method_terminate, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("Activate", NULL, NULL, bus_session_method_activate, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("Lock", NULL, NULL, bus_session_method_lock, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("Unlock", NULL, NULL, bus_session_method_lock, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("SetIdleHint", "b", NULL, method_set_idle_hint, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("SetLockedHint", "b", NULL, method_set_locked_hint, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("Kill", "si", NULL, bus_session_method_kill, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("TakeControl", "b", NULL, method_take_control, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("ReleaseControl", NULL, NULL, method_release_control, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("TakeDevice", "uu", "hb", method_take_device, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("ReleaseDevice", "uu", NULL, method_release_device, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("PauseDeviceComplete", "uu", NULL, method_pause_device_complete, SD_BUS_VTABLE_UNPRIVILEGED), + + SD_BUS_SIGNAL("PauseDevice", "uus", 0), + SD_BUS_SIGNAL("ResumeDevice", "uuh", 0), + SD_BUS_SIGNAL("Lock", NULL, 0), + SD_BUS_SIGNAL("Unlock", NULL, 0), + + SD_BUS_VTABLE_END +}; + +int session_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) { + Manager *m = userdata; + Session *session; + int r; + + assert(bus); + assert(path); + assert(interface); + assert(found); + assert(m); + + if (streq(path, "/org/freedesktop/login1/session/self")) { + _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; + sd_bus_message *message; + const char *name; + + message = sd_bus_get_current_message(bus); + if (!message) + return 0; + + r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_SESSION|SD_BUS_CREDS_AUGMENT, &creds); + if (r < 0) + return r; + + r = sd_bus_creds_get_session(creds, &name); + if (r < 0) + return r; + + session = hashmap_get(m->sessions, name); + } else { + _cleanup_free_ char *e = NULL; + const char *p; + + p = startswith(path, "/org/freedesktop/login1/session/"); + if (!p) + return 0; + + e = bus_label_unescape(p); + if (!e) + return -ENOMEM; + + session = hashmap_get(m->sessions, e); + } + + if (!session) + return 0; + + *found = session; + return 1; +} + +char *session_bus_path(Session *s) { + _cleanup_free_ char *t = NULL; + + assert(s); + + t = bus_label_escape(s->id); + if (!t) + return NULL; + + return strappend("/org/freedesktop/login1/session/", t); +} + +int session_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) { + _cleanup_strv_free_ char **l = NULL; + sd_bus_message *message; + Manager *m = userdata; + Session *session; + Iterator i; + int r; + + assert(bus); + assert(path); + assert(nodes); + + HASHMAP_FOREACH(session, m->sessions, i) { + char *p; + + p = session_bus_path(session); + if (!p) + return -ENOMEM; + + r = strv_consume(&l, p); + if (r < 0) + return r; + } + + message = sd_bus_get_current_message(bus); + if (message) { + _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; + const char *name; + + r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_SESSION|SD_BUS_CREDS_AUGMENT, &creds); + if (r >= 0) { + r = sd_bus_creds_get_session(creds, &name); + if (r >= 0) { + session = hashmap_get(m->sessions, name); + if (session) { + r = strv_extend(&l, "/org/freedesktop/login1/session/self"); + if (r < 0) + return r; + } + } + } + } + + *nodes = l; + l = NULL; + + return 1; +} + +int session_send_signal(Session *s, bool new_session) { + _cleanup_free_ char *p = NULL; + + assert(s); + + p = session_bus_path(s); + if (!p) + return -ENOMEM; + + return sd_bus_emit_signal( + s->manager->bus, + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + new_session ? "SessionNew" : "SessionRemoved", + "so", s->id, p); +} + +int session_send_changed(Session *s, const char *properties, ...) { + _cleanup_free_ char *p = NULL; + char **l; + + assert(s); + + if (!s->started) + return 0; + + p = session_bus_path(s); + if (!p) + return -ENOMEM; + + l = strv_from_stdarg_alloca(properties); + + return sd_bus_emit_properties_changed_strv(s->manager->bus, p, "org.freedesktop.login1.Session", l); +} + +int session_send_lock(Session *s, bool lock) { + _cleanup_free_ char *p = NULL; + + assert(s); + + p = session_bus_path(s); + if (!p) + return -ENOMEM; + + return sd_bus_emit_signal( + s->manager->bus, + p, + "org.freedesktop.login1.Session", + lock ? "Lock" : "Unlock", + NULL); +} + +int session_send_lock_all(Manager *m, bool lock) { + Session *session; + Iterator i; + int r = 0; + + assert(m); + + HASHMAP_FOREACH(session, m->sessions, i) { + int k; + + k = session_send_lock(session, lock); + if (k < 0) + r = k; + } + + return r; +} + +int session_send_create_reply(Session *s, sd_bus_error *error) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *c = NULL; + _cleanup_close_ int fifo_fd = -1; + _cleanup_free_ char *p = NULL; + + assert(s); + + /* This is called after the session scope and the user service + * were successfully created, and finishes where + * bus_manager_create_session() left off. */ + + if (!s->create_message) + return 0; + + if (!sd_bus_error_is_set(error) && (s->scope_job || s->user->service_job)) + return 0; + + c = s->create_message; + s->create_message = NULL; + + if (error) + return sd_bus_reply_method_error(c, error); + + fifo_fd = session_create_fifo(s); + if (fifo_fd < 0) + return fifo_fd; + + /* Update the session state file before we notify the client + * about the result. */ + session_save(s); + + p = session_bus_path(s); + if (!p) + return -ENOMEM; + + log_debug("Sending reply about created session: " + "id=%s object_path=%s uid=%u runtime_path=%s " + "session_fd=%d seat=%s vtnr=%u", + s->id, + p, + (uint32_t) s->user->uid, + s->user->runtime_path, + fifo_fd, + s->seat ? s->seat->id : "", + (uint32_t) s->vtnr); + + return sd_bus_reply_method_return( + c, "soshusub", + s->id, + p, + s->user->runtime_path, + fifo_fd, + (uint32_t) s->user->uid, + s->seat ? s->seat->id : "", + (uint32_t) s->vtnr, + false); +} diff --git a/src/grp-login/systemd-logind/logind-session-device.c b/src/grp-login/systemd-logind/logind-session-device.c new file mode 100644 index 0000000000..99528488c0 --- /dev/null +++ b/src/grp-login/systemd-logind/logind-session-device.c @@ -0,0 +1,482 @@ +/*** + This file is part of systemd. + + Copyright 2013 David Herrmann + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include + +#include + +#include + +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/missing.h" +#include "basic/util.h" +#include "shared/bus-util.h" + +#include "logind-session-device.h" + +enum SessionDeviceNotifications { + SESSION_DEVICE_RESUME, + SESSION_DEVICE_TRY_PAUSE, + SESSION_DEVICE_PAUSE, + SESSION_DEVICE_RELEASE, +}; + +static int session_device_notify(SessionDevice *sd, enum SessionDeviceNotifications type) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; + _cleanup_free_ char *path = NULL; + const char *t = NULL; + uint32_t major, minor; + int r; + + assert(sd); + + major = major(sd->dev); + minor = minor(sd->dev); + + if (!sd->session->controller) + return 0; + + path = session_bus_path(sd->session); + if (!path) + return -ENOMEM; + + r = sd_bus_message_new_signal( + sd->session->manager->bus, + &m, path, + "org.freedesktop.login1.Session", + (type == SESSION_DEVICE_RESUME) ? "ResumeDevice" : "PauseDevice"); + if (!m) + return r; + + r = sd_bus_message_set_destination(m, sd->session->controller); + if (r < 0) + return r; + + switch (type) { + case SESSION_DEVICE_RESUME: + r = sd_bus_message_append(m, "uuh", major, minor, sd->fd); + if (r < 0) + return r; + break; + case SESSION_DEVICE_TRY_PAUSE: + t = "pause"; + break; + case SESSION_DEVICE_PAUSE: + t = "force"; + break; + case SESSION_DEVICE_RELEASE: + t = "gone"; + break; + default: + return -EINVAL; + } + + if (t) { + r = sd_bus_message_append(m, "uus", major, minor, t); + if (r < 0) + return r; + } + + return sd_bus_send(sd->session->manager->bus, m, NULL); +} + +static int sd_eviocrevoke(int fd) { + static bool warned; + int r; + + assert(fd >= 0); + + r = ioctl(fd, EVIOCREVOKE, NULL); + if (r < 0) { + r = -errno; + if (r == -EINVAL && !warned) { + warned = true; + log_warning("kernel does not support evdev-revocation"); + } + } + + return 0; +} + +static int sd_drmsetmaster(int fd) { + int r; + + assert(fd >= 0); + + r = ioctl(fd, DRM_IOCTL_SET_MASTER, 0); + if (r < 0) + return -errno; + + return 0; +} + +static int sd_drmdropmaster(int fd) { + int r; + + assert(fd >= 0); + + r = ioctl(fd, DRM_IOCTL_DROP_MASTER, 0); + if (r < 0) + return -errno; + + return 0; +} + +static int session_device_open(SessionDevice *sd, bool active) { + int fd, r; + + assert(sd->type != DEVICE_TYPE_UNKNOWN); + + /* open device and try to get an udev_device from it */ + fd = open(sd->node, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK); + if (fd < 0) + return -errno; + + switch (sd->type) { + case DEVICE_TYPE_DRM: + if (active) { + /* Weird legacy DRM semantics might return an error + * even though we're master. No way to detect that so + * fail at all times and let caller retry in inactive + * state. */ + r = sd_drmsetmaster(fd); + if (r < 0) { + close_nointr(fd); + return r; + } + } else { + /* DRM-Master is granted to the first user who opens a + * device automatically (ughh, racy!). Hence, we just + * drop DRM-Master in case we were the first. */ + sd_drmdropmaster(fd); + } + break; + case DEVICE_TYPE_EVDEV: + if (!active) + sd_eviocrevoke(fd); + break; + case DEVICE_TYPE_UNKNOWN: + default: + /* fallback for devices wihout synchronizations */ + break; + } + + return fd; +} + +static int session_device_start(SessionDevice *sd) { + int r; + + assert(sd); + assert(session_is_active(sd->session)); + + if (sd->active) + return 0; + + switch (sd->type) { + case DEVICE_TYPE_DRM: + /* Device is kept open. Simply call drmSetMaster() and hope + * there is no-one else. In case it fails, we keep the device + * paused. Maybe at some point we have a drmStealMaster(). */ + r = sd_drmsetmaster(sd->fd); + if (r < 0) + return r; + break; + case DEVICE_TYPE_EVDEV: + /* Evdev devices are revoked while inactive. Reopen it and we + * are fine. */ + r = session_device_open(sd, true); + if (r < 0) + return r; + close_nointr(sd->fd); + sd->fd = r; + break; + case DEVICE_TYPE_UNKNOWN: + default: + /* fallback for devices wihout synchronizations */ + break; + } + + sd->active = true; + return 0; +} + +static void session_device_stop(SessionDevice *sd) { + assert(sd); + + if (!sd->active) + return; + + switch (sd->type) { + case DEVICE_TYPE_DRM: + /* On DRM devices we simply drop DRM-Master but keep it open. + * This allows the user to keep resources allocated. The + * CAP_SYS_ADMIN restriction to DRM-Master prevents users from + * circumventing this. */ + sd_drmdropmaster(sd->fd); + break; + case DEVICE_TYPE_EVDEV: + /* Revoke access on evdev file-descriptors during deactivation. + * This will basically prevent any operations on the fd and + * cannot be undone. Good side is: it needs no CAP_SYS_ADMIN + * protection this way. */ + sd_eviocrevoke(sd->fd); + break; + case DEVICE_TYPE_UNKNOWN: + default: + /* fallback for devices without synchronization */ + break; + } + + sd->active = false; +} + +static DeviceType detect_device_type(struct udev_device *dev) { + const char *sysname, *subsystem; + DeviceType type; + + sysname = udev_device_get_sysname(dev); + subsystem = udev_device_get_subsystem(dev); + type = DEVICE_TYPE_UNKNOWN; + + if (streq_ptr(subsystem, "drm")) { + if (startswith(sysname, "card")) + type = DEVICE_TYPE_DRM; + } else if (streq_ptr(subsystem, "input")) { + if (startswith(sysname, "event")) + type = DEVICE_TYPE_EVDEV; + } + + return type; +} + +static int session_device_verify(SessionDevice *sd) { + struct udev_device *dev, *p = NULL; + const char *sp, *node; + int r; + + dev = udev_device_new_from_devnum(sd->session->manager->udev, 'c', sd->dev); + if (!dev) + return -ENODEV; + + sp = udev_device_get_syspath(dev); + node = udev_device_get_devnode(dev); + if (!node) { + r = -EINVAL; + goto err_dev; + } + + /* detect device type so we can find the correct sysfs parent */ + sd->type = detect_device_type(dev); + if (sd->type == DEVICE_TYPE_UNKNOWN) { + r = -ENODEV; + goto err_dev; + } else if (sd->type == DEVICE_TYPE_EVDEV) { + /* for evdev devices we need the parent node as device */ + p = dev; + dev = udev_device_get_parent_with_subsystem_devtype(p, "input", NULL); + if (!dev) { + r = -ENODEV; + goto err_dev; + } + sp = udev_device_get_syspath(dev); + } else if (sd->type != DEVICE_TYPE_DRM) { + /* Prevent opening unsupported devices. Especially devices of + * subsystem "input" must be opened via the evdev node as + * we require EVIOCREVOKE. */ + r = -ENODEV; + goto err_dev; + } + + /* search for an existing seat device and return it if available */ + sd->device = hashmap_get(sd->session->manager->devices, sp); + if (!sd->device) { + /* The caller might have gotten the udev event before we were + * able to process it. Hence, fake the "add" event and let the + * logind-manager handle the new device. */ + r = manager_process_seat_device(sd->session->manager, dev); + if (r < 0) + goto err_dev; + + /* if it's still not available, then the device is invalid */ + sd->device = hashmap_get(sd->session->manager->devices, sp); + if (!sd->device) { + r = -ENODEV; + goto err_dev; + } + } + + if (sd->device->seat != sd->session->seat) { + r = -EPERM; + goto err_dev; + } + + sd->node = strdup(node); + if (!sd->node) { + r = -ENOMEM; + goto err_dev; + } + + r = 0; +err_dev: + udev_device_unref(p ? : dev); + return r; +} + +int session_device_new(Session *s, dev_t dev, SessionDevice **out) { + SessionDevice *sd; + int r; + + assert(s); + assert(out); + + if (!s->seat) + return -EPERM; + + sd = new0(SessionDevice, 1); + if (!sd) + return -ENOMEM; + + sd->session = s; + sd->dev = dev; + sd->fd = -1; + sd->type = DEVICE_TYPE_UNKNOWN; + + r = session_device_verify(sd); + if (r < 0) + goto error; + + r = hashmap_put(s->devices, &sd->dev, sd); + if (r < 0) { + r = -ENOMEM; + goto error; + } + + /* Open the device for the first time. We need a valid fd to pass back + * to the caller. If the session is not active, this _might_ immediately + * revoke access and thus invalidate the fd. But this is still needed + * to pass a valid fd back. */ + sd->active = session_is_active(s); + r = session_device_open(sd, sd->active); + if (r < 0) { + /* EINVAL _may_ mean a master is active; retry inactive */ + if (sd->active && r == -EINVAL) { + sd->active = false; + r = session_device_open(sd, false); + } + if (r < 0) + goto error; + } + sd->fd = r; + + LIST_PREPEND(sd_by_device, sd->device->session_devices, sd); + + *out = sd; + return 0; + +error: + hashmap_remove(s->devices, &sd->dev); + free(sd->node); + free(sd); + return r; +} + +void session_device_free(SessionDevice *sd) { + assert(sd); + + session_device_stop(sd); + session_device_notify(sd, SESSION_DEVICE_RELEASE); + close_nointr(sd->fd); + + LIST_REMOVE(sd_by_device, sd->device->session_devices, sd); + + hashmap_remove(sd->session->devices, &sd->dev); + + free(sd->node); + free(sd); +} + +void session_device_complete_pause(SessionDevice *sd) { + SessionDevice *iter; + Iterator i; + + if (!sd->active) + return; + + session_device_stop(sd); + + /* if not all devices are paused, wait for further completion events */ + HASHMAP_FOREACH(iter, sd->session->devices, i) + if (iter->active) + return; + + /* complete any pending session switch */ + seat_complete_switch(sd->session->seat); +} + +void session_device_resume_all(Session *s) { + SessionDevice *sd; + Iterator i; + int r; + + assert(s); + + HASHMAP_FOREACH(sd, s->devices, i) { + if (!sd->active) { + r = session_device_start(sd); + if (!r) + session_device_notify(sd, SESSION_DEVICE_RESUME); + } + } +} + +void session_device_pause_all(Session *s) { + SessionDevice *sd; + Iterator i; + + assert(s); + + HASHMAP_FOREACH(sd, s->devices, i) { + if (sd->active) { + session_device_stop(sd); + session_device_notify(sd, SESSION_DEVICE_PAUSE); + } + } +} + +unsigned int session_device_try_pause_all(Session *s) { + SessionDevice *sd; + Iterator i; + unsigned int num_pending = 0; + + assert(s); + + HASHMAP_FOREACH(sd, s->devices, i) { + if (sd->active) { + session_device_notify(sd, SESSION_DEVICE_TRY_PAUSE); + ++num_pending; + } + } + + return num_pending; +} diff --git a/src/grp-login/systemd-logind/logind-session-device.h b/src/grp-login/systemd-logind/logind-session-device.h new file mode 100644 index 0000000000..4a22decb1d --- /dev/null +++ b/src/grp-login/systemd-logind/logind-session-device.h @@ -0,0 +1,54 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2013 David Herrmann + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "basic/list.h" + +typedef enum DeviceType DeviceType; +typedef struct SessionDevice SessionDevice; + +#include "logind.h" + +enum DeviceType { + DEVICE_TYPE_UNKNOWN, + DEVICE_TYPE_DRM, + DEVICE_TYPE_EVDEV, +}; + +struct SessionDevice { + Session *session; + Device *device; + + dev_t dev; + char *node; + int fd; + bool active; + DeviceType type; + + LIST_FIELDS(struct SessionDevice, sd_by_device); +}; + +int session_device_new(Session *s, dev_t dev, SessionDevice **out); +void session_device_free(SessionDevice *sd); +void session_device_complete_pause(SessionDevice *sd); + +void session_device_resume_all(Session *s); +void session_device_pause_all(Session *s); +unsigned int session_device_try_pause_all(Session *s); diff --git a/src/grp-login/systemd-logind/logind-session.c b/src/grp-login/systemd-logind/logind-session.c new file mode 100644 index 0000000000..928f4a6314 --- /dev/null +++ b/src/grp-login/systemd-logind/logind-session.c @@ -0,0 +1,1273 @@ +/*** + 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 Lesser General Public License as published by + the Free Software Foundation; either version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include "basic/alloc-util.h" +#include "basic/audit-util.h" +#include "basic/escape.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/formats-util.h" +#include "basic/io-util.h" +#include "basic/mkdir.h" +#include "basic/parse-util.h" +#include "basic/path-util.h" +#include "basic/string-table.h" +#include "basic/terminal-util.h" +#include "basic/user-util.h" +#include "basic/util.h" +#include "sd-bus/bus-error.h" +#include "shared/bus-util.h" + +#include "logind-session.h" + +#define RELEASE_USEC (20*USEC_PER_SEC) + +static void session_remove_fifo(Session *s); + +Session* session_new(Manager *m, const char *id) { + Session *s; + + assert(m); + assert(id); + assert(session_id_valid(id)); + + s = new0(Session, 1); + if (!s) + return NULL; + + s->state_file = strappend("/run/systemd/sessions/", id); + if (!s->state_file) { + free(s); + return NULL; + } + + s->devices = hashmap_new(&devt_hash_ops); + if (!s->devices) { + free(s->state_file); + free(s); + return NULL; + } + + s->id = basename(s->state_file); + + if (hashmap_put(m->sessions, s->id, s) < 0) { + hashmap_free(s->devices); + free(s->state_file); + free(s); + return NULL; + } + + s->manager = m; + s->fifo_fd = -1; + s->vtfd = -1; + + return s; +} + +void session_free(Session *s) { + SessionDevice *sd; + + assert(s); + + if (s->in_gc_queue) + LIST_REMOVE(gc_queue, s->manager->session_gc_queue, s); + + s->timer_event_source = sd_event_source_unref(s->timer_event_source); + + session_remove_fifo(s); + + session_drop_controller(s); + + while ((sd = hashmap_first(s->devices))) + session_device_free(sd); + + hashmap_free(s->devices); + + if (s->user) { + LIST_REMOVE(sessions_by_user, s->user->sessions, s); + + if (s->user->display == s) + s->user->display = NULL; + } + + if (s->seat) { + if (s->seat->active == s) + s->seat->active = NULL; + if (s->seat->pending_switch == s) + s->seat->pending_switch = NULL; + + seat_evict_position(s->seat, s); + LIST_REMOVE(sessions_by_seat, s->seat->sessions, s); + } + + if (s->scope) { + hashmap_remove(s->manager->session_units, s->scope); + free(s->scope); + } + + free(s->scope_job); + + sd_bus_message_unref(s->create_message); + + free(s->tty); + free(s->display); + free(s->remote_host); + free(s->remote_user); + free(s->service); + free(s->desktop); + + hashmap_remove(s->manager->sessions, s->id); + + free(s->state_file); + free(s); +} + +void session_set_user(Session *s, User *u) { + assert(s); + assert(!s->user); + + s->user = u; + LIST_PREPEND(sessions_by_user, u->sessions, s); +} + +int session_save(Session *s) { + _cleanup_free_ char *temp_path = NULL; + _cleanup_fclose_ FILE *f = NULL; + int r = 0; + + assert(s); + + if (!s->user) + return -ESTALE; + + if (!s->started) + return 0; + + r = mkdir_safe_label("/run/systemd/sessions", 0755, 0, 0); + if (r < 0) + goto fail; + + r = fopen_temporary(s->state_file, &f, &temp_path); + if (r < 0) + goto fail; + + assert(s->user); + + fchmod(fileno(f), 0644); + + fprintf(f, + "# This is private data. Do not parse.\n" + "UID="UID_FMT"\n" + "USER=%s\n" + "ACTIVE=%i\n" + "STATE=%s\n" + "REMOTE=%i\n", + s->user->uid, + s->user->name, + session_is_active(s), + session_state_to_string(session_get_state(s)), + s->remote); + + if (s->type >= 0) + fprintf(f, "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->scope) + fprintf(f, "SCOPE=%s\n", s->scope); + if (s->scope_job) + fprintf(f, "SCOPE_JOB=%s\n", s->scope_job); + + if (s->fifo_path) + fprintf(f, "FIFO=%s\n", s->fifo_path); + + if (s->seat) + fprintf(f, "SEAT=%s\n", s->seat->id); + + if (s->tty) + fprintf(f, "TTY=%s\n", s->tty); + + if (s->display) + fprintf(f, "DISPLAY=%s\n", s->display); + + if (s->remote_host) { + _cleanup_free_ char *escaped; + + escaped = cescape(s->remote_host); + if (!escaped) { + r = -ENOMEM; + goto fail; + } + + fprintf(f, "REMOTE_HOST=%s\n", escaped); + } + + if (s->remote_user) { + _cleanup_free_ char *escaped; + + escaped = cescape(s->remote_user); + if (!escaped) { + r = -ENOMEM; + goto fail; + } + + fprintf(f, "REMOTE_USER=%s\n", escaped); + } + + if (s->service) { + _cleanup_free_ char *escaped; + + escaped = cescape(s->service); + if (!escaped) { + r = -ENOMEM; + goto fail; + } + + fprintf(f, "SERVICE=%s\n", escaped); + } + + if (s->desktop) { + _cleanup_free_ char *escaped; + + + escaped = cescape(s->desktop); + if (!escaped) { + r = -ENOMEM; + goto fail; + } + + fprintf(f, "DESKTOP=%s\n", escaped); + } + + if (s->seat && seat_has_vts(s->seat)) + fprintf(f, "VTNR=%u\n", s->vtnr); + + if (!s->vtnr) + fprintf(f, "POSITION=%u\n", s->position); + + if (s->leader > 0) + fprintf(f, "LEADER="PID_FMT"\n", s->leader); + + if (s->audit_id > 0) + fprintf(f, "AUDIT=%"PRIu32"\n", s->audit_id); + + if (dual_timestamp_is_set(&s->timestamp)) + fprintf(f, + "REALTIME="USEC_FMT"\n" + "MONOTONIC="USEC_FMT"\n", + s->timestamp.realtime, + s->timestamp.monotonic); + + if (s->controller) + fprintf(f, "CONTROLLER=%s\n", s->controller); + + r = fflush_and_check(f); + if (r < 0) + goto fail; + + if (rename(temp_path, s->state_file) < 0) { + r = -errno; + goto fail; + } + + return 0; + +fail: + (void) unlink(s->state_file); + + if (temp_path) + (void) unlink(temp_path); + + return log_error_errno(r, "Failed to save session data %s: %m", s->state_file); +} + + +int session_load(Session *s) { + _cleanup_free_ char *remote = NULL, + *seat = NULL, + *vtnr = NULL, + *state = NULL, + *position = NULL, + *leader = NULL, + *type = NULL, + *class = NULL, + *uid = NULL, + *realtime = NULL, + *monotonic = NULL, + *controller = NULL; + + int k, r; + + assert(s); + + r = parse_env_file(s->state_file, NEWLINE, + "REMOTE", &remote, + "SCOPE", &s->scope, + "SCOPE_JOB", &s->scope_job, + "FIFO", &s->fifo_path, + "SEAT", &seat, + "TTY", &s->tty, + "DISPLAY", &s->display, + "REMOTE_HOST", &s->remote_host, + "REMOTE_USER", &s->remote_user, + "SERVICE", &s->service, + "DESKTOP", &s->desktop, + "VTNR", &vtnr, + "STATE", &state, + "POSITION", &position, + "LEADER", &leader, + "TYPE", &type, + "CLASS", &class, + "UID", &uid, + "REALTIME", &realtime, + "MONOTONIC", &monotonic, + "CONTROLLER", &controller, + NULL); + + if (r < 0) + return log_error_errno(r, "Failed to read %s: %m", s->state_file); + + if (!s->user) { + uid_t u; + User *user; + + if (!uid) { + log_error("UID not specified for session %s", s->id); + return -ENOENT; + } + + r = parse_uid(uid, &u); + if (r < 0) { + log_error("Failed to parse UID value %s for session %s.", uid, s->id); + return r; + } + + user = hashmap_get(s->manager->users, UID_TO_PTR(u)); + if (!user) { + log_error("User of session %s not known.", s->id); + return -ENOENT; + } + + session_set_user(s, user); + } + + if (remote) { + k = parse_boolean(remote); + if (k >= 0) + s->remote = k; + } + + if (vtnr) + safe_atou(vtnr, &s->vtnr); + + if (seat && !s->seat) { + Seat *o; + + o = hashmap_get(s->manager->seats, seat); + if (o) + r = seat_attach_session(o, s); + if (!o || r < 0) + log_error("Cannot attach session %s to seat %s", s->id, seat); + } + + if (!s->seat || !seat_has_vts(s->seat)) + s->vtnr = 0; + + if (position && s->seat) { + unsigned int npos; + + safe_atou(position, &npos); + seat_claim_position(s->seat, s, npos); + } + + if (leader) { + k = parse_pid(leader, &s->leader); + if (k >= 0) + audit_session_from_pid(s->leader, &s->audit_id); + } + + if (type) { + SessionType t; + + t = session_type_from_string(type); + if (t >= 0) + s->type = t; + } + + if (class) { + SessionClass c; + + c = session_class_from_string(class); + if (c >= 0) + s->class = c; + } + + if (state && streq(state, "closing")) + s->stopping = true; + + if (s->fifo_path) { + int fd; + + /* If we open an unopened pipe for reading we will not + get an EOF. to trigger an EOF we hence open it for + writing, but close it right away which then will + trigger the EOF. This will happen immediately if no + other process has the FIFO open for writing, i. e. + when the session died before logind (re)started. */ + + fd = session_create_fifo(s); + safe_close(fd); + } + + if (realtime) + timestamp_deserialize(realtime, &s->timestamp.realtime); + if (monotonic) + timestamp_deserialize(monotonic, &s->timestamp.monotonic); + + if (controller) { + if (bus_name_has_owner(s->manager->bus, controller, NULL) > 0) + session_set_controller(s, controller, false); + else + session_restore_vt(s); + } + + return r; +} + +int session_activate(Session *s) { + unsigned int num_pending; + + assert(s); + assert(s->user); + + if (!s->seat) + return -EOPNOTSUPP; + + if (s->seat->active == s) + return 0; + + /* on seats with VTs, we let VTs manage session-switching */ + if (seat_has_vts(s->seat)) { + if (!s->vtnr) + return -EOPNOTSUPP; + + return chvt(s->vtnr); + } + + /* On seats without VTs, we implement session-switching in logind. We + * try to pause all session-devices and wait until the session + * controller acknowledged them. Once all devices are asleep, we simply + * switch the active session and be done. + * We save the session we want to switch to in seat->pending_switch and + * seat_complete_switch() will perform the final switch. */ + + s->seat->pending_switch = s; + + /* if no devices are running, immediately perform the session switch */ + num_pending = session_device_try_pause_all(s); + if (!num_pending) + seat_complete_switch(s->seat); + + return 0; +} + +static int session_start_scope(Session *s) { + int r; + + assert(s); + assert(s->user); + + if (!s->scope) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + char *scope, *job = NULL; + const char *description; + + scope = strjoin("session-", s->id, ".scope", NULL); + if (!scope) + return log_oom(); + + description = strjoina("Session ", s->id, " of user ", s->user->name); + + r = manager_start_scope( + s->manager, + scope, + s->leader, + s->user->slice, + description, + "systemd-logind.service", + "systemd-user-sessions.service", + (uint64_t) -1, /* disable TasksMax= for the scope, rely on the slice setting for it */ + &error, + &job); + if (r < 0) { + log_error_errno(r, "Failed to start session scope %s: %s", scope, bus_error_message(&error, r)); + free(scope); + return r; + } else { + s->scope = scope; + + free(s->scope_job); + s->scope_job = job; + } + } + + if (s->scope) + (void) hashmap_put(s->manager->session_units, s->scope, s); + + return 0; +} + +int session_start(Session *s) { + int r; + + assert(s); + + if (!s->user) + return -ESTALE; + + if (s->started) + return 0; + + r = user_start(s->user); + if (r < 0) + return r; + + /* Create cgroup */ + r = session_start_scope(s); + if (r < 0) + return r; + + log_struct(s->class == SESSION_BACKGROUND ? LOG_DEBUG : LOG_INFO, + LOG_MESSAGE_ID(SD_MESSAGE_SESSION_START), + "SESSION_ID=%s", s->id, + "USER_ID=%s", s->user->name, + "LEADER="PID_FMT, s->leader, + LOG_MESSAGE("New session %s of user %s.", s->id, s->user->name), + NULL); + + if (!dual_timestamp_is_set(&s->timestamp)) + dual_timestamp_get(&s->timestamp); + + if (s->seat) + seat_read_active_vt(s->seat); + + s->started = true; + + user_elect_display(s->user); + + /* Save data */ + session_save(s); + user_save(s->user); + if (s->seat) + seat_save(s->seat); + + /* Send signals */ + session_send_signal(s, true); + user_send_changed(s->user, "Sessions", "Display", NULL); + if (s->seat) { + if (s->seat->active == s) + seat_send_changed(s->seat, "Sessions", "ActiveSession", NULL); + else + seat_send_changed(s->seat, "Sessions", NULL); + } + + return 0; +} + +static int session_stop_scope(Session *s, bool force) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + char *job = NULL; + int r; + + assert(s); + + if (!s->scope) + return 0; + + if (force || manager_shall_kill(s->manager, s->user->name)) { + r = manager_stop_unit(s->manager, s->scope, &error, &job); + if (r < 0) { + log_error("Failed to stop session scope: %s", bus_error_message(&error, r)); + return r; + } + + free(s->scope_job); + s->scope_job = job; + } else { + r = manager_abandon_scope(s->manager, s->scope, &error); + if (r < 0) { + log_error("Failed to abandon session scope: %s", bus_error_message(&error, r)); + return r; + } + } + + return 0; +} + +int session_stop(Session *s, bool force) { + int r; + + assert(s); + + if (!s->user) + return -ESTALE; + + s->timer_event_source = sd_event_source_unref(s->timer_event_source); + + if (s->seat) + seat_evict_position(s->seat, s); + + /* We are going down, don't care about FIFOs anymore */ + session_remove_fifo(s); + + /* Kill cgroup */ + r = session_stop_scope(s, force); + + s->stopping = true; + + user_elect_display(s->user); + + session_save(s); + user_save(s->user); + + return r; +} + +int session_finalize(Session *s) { + SessionDevice *sd; + + assert(s); + + if (!s->user) + return -ESTALE; + + if (s->started) + log_struct(s->class == SESSION_BACKGROUND ? LOG_DEBUG : LOG_INFO, + LOG_MESSAGE_ID(SD_MESSAGE_SESSION_STOP), + "SESSION_ID=%s", s->id, + "USER_ID=%s", s->user->name, + "LEADER="PID_FMT, s->leader, + LOG_MESSAGE("Removed session %s.", s->id), + NULL); + + s->timer_event_source = sd_event_source_unref(s->timer_event_source); + + if (s->seat) + seat_evict_position(s->seat, s); + + /* Kill session devices */ + while ((sd = hashmap_first(s->devices))) + session_device_free(sd); + + (void) unlink(s->state_file); + session_add_to_gc_queue(s); + user_add_to_gc_queue(s->user); + + if (s->started) { + session_send_signal(s, false); + s->started = false; + } + + if (s->seat) { + if (s->seat->active == s) + seat_set_active(s->seat, NULL); + + seat_save(s->seat); + seat_send_changed(s->seat, "Sessions", NULL); + } + + user_save(s->user); + user_send_changed(s->user, "Sessions", "Display", NULL); + + return 0; +} + +static int release_timeout_callback(sd_event_source *es, uint64_t usec, void *userdata) { + Session *s = userdata; + + assert(es); + assert(s); + + session_stop(s, false); + return 0; +} + +int session_release(Session *s) { + assert(s); + + if (!s->started || s->stopping) + return 0; + + if (s->timer_event_source) + return 0; + + return sd_event_add_time(s->manager->event, + &s->timer_event_source, + CLOCK_MONOTONIC, + now(CLOCK_MONOTONIC) + RELEASE_USEC, 0, + release_timeout_callback, s); +} + +bool session_is_active(Session *s) { + assert(s); + + if (!s->seat) + return true; + + return s->seat->active == s; +} + +static int get_tty_atime(const char *tty, usec_t *atime) { + _cleanup_free_ char *p = NULL; + struct stat st; + + assert(tty); + assert(atime); + + if (!path_is_absolute(tty)) { + p = strappend("/dev/", tty); + if (!p) + return -ENOMEM; + + tty = p; + } else if (!path_startswith(tty, "/dev/")) + return -ENOENT; + + if (lstat(tty, &st) < 0) + return -errno; + + *atime = timespec_load(&st.st_atim); + return 0; +} + +static int get_process_ctty_atime(pid_t pid, usec_t *atime) { + _cleanup_free_ char *p = NULL; + int r; + + assert(pid > 0); + assert(atime); + + r = get_ctty(pid, NULL, &p); + if (r < 0) + return r; + + return get_tty_atime(p, atime); +} + +int session_get_idle_hint(Session *s, dual_timestamp *t) { + usec_t atime = 0, n; + int r; + + assert(s); + + /* Explicit idle hint is set */ + if (s->idle_hint) { + if (t) + *t = s->idle_hint_timestamp; + + return s->idle_hint; + } + + /* Graphical sessions should really implement a real + * idle hint logic */ + if (SESSION_TYPE_IS_GRAPHICAL(s->type)) + goto dont_know; + + /* For sessions with an explicitly configured tty, let's check + * its atime */ + if (s->tty) { + r = get_tty_atime(s->tty, &atime); + if (r >= 0) + goto found_atime; + } + + /* For sessions with a leader but no explicitly configured + * tty, let's check the controlling tty of the leader */ + if (s->leader > 0) { + r = get_process_ctty_atime(s->leader, &atime); + if (r >= 0) + goto found_atime; + } + +dont_know: + if (t) + *t = s->idle_hint_timestamp; + + return 0; + +found_atime: + if (t) + dual_timestamp_from_realtime(t, atime); + + n = now(CLOCK_REALTIME); + + if (s->manager->idle_action_usec <= 0) + return 0; + + return atime + s->manager->idle_action_usec <= n; +} + +void session_set_idle_hint(Session *s, bool b) { + assert(s); + + if (s->idle_hint == b) + return; + + s->idle_hint = b; + dual_timestamp_get(&s->idle_hint_timestamp); + + session_send_changed(s, "IdleHint", "IdleSinceHint", "IdleSinceHintMonotonic", NULL); + + if (s->seat) + seat_send_changed(s->seat, "IdleHint", "IdleSinceHint", "IdleSinceHintMonotonic", NULL); + + user_send_changed(s->user, "IdleHint", "IdleSinceHint", "IdleSinceHintMonotonic", NULL); + manager_send_changed(s->manager, "IdleHint", "IdleSinceHint", "IdleSinceHintMonotonic", NULL); +} + +int session_get_locked_hint(Session *s) { + assert(s); + + return s->locked_hint; +} + +void session_set_locked_hint(Session *s, bool b) { + assert(s); + + if (s->locked_hint == b) + return; + + s->locked_hint = b; + + session_send_changed(s, "LockedHint", NULL); +} + +static int session_dispatch_fifo(sd_event_source *es, int fd, uint32_t revents, void *userdata) { + Session *s = userdata; + + assert(s); + assert(s->fifo_fd == fd); + + /* EOF on the FIFO means the session died abnormally. */ + + session_remove_fifo(s); + session_stop(s, false); + + return 1; +} + +int session_create_fifo(Session *s) { + int r; + + assert(s); + + /* Create FIFO */ + if (!s->fifo_path) { + r = mkdir_safe_label("/run/systemd/sessions", 0755, 0, 0); + if (r < 0) + return r; + + if (asprintf(&s->fifo_path, "/run/systemd/sessions/%s.ref", s->id) < 0) + return -ENOMEM; + + if (mkfifo(s->fifo_path, 0600) < 0 && errno != EEXIST) + return -errno; + } + + /* Open reading side */ + if (s->fifo_fd < 0) { + s->fifo_fd = open(s->fifo_path, O_RDONLY|O_CLOEXEC|O_NDELAY); + if (s->fifo_fd < 0) + return -errno; + + } + + if (!s->fifo_event_source) { + r = sd_event_add_io(s->manager->event, &s->fifo_event_source, s->fifo_fd, 0, session_dispatch_fifo, s); + if (r < 0) + return r; + + /* Let's make sure we noticed dead sessions before we process new bus requests (which might create new + * sessions). */ + r = sd_event_source_set_priority(s->fifo_event_source, SD_EVENT_PRIORITY_NORMAL-10); + if (r < 0) + return r; + } + + /* Open writing side */ + r = open(s->fifo_path, O_WRONLY|O_CLOEXEC|O_NDELAY); + if (r < 0) + return -errno; + + return r; +} + +static void session_remove_fifo(Session *s) { + assert(s); + + s->fifo_event_source = sd_event_source_unref(s->fifo_event_source); + s->fifo_fd = safe_close(s->fifo_fd); + + if (s->fifo_path) { + unlink(s->fifo_path); + s->fifo_path = mfree(s->fifo_path); + } +} + +bool session_check_gc(Session *s, bool drop_not_started) { + assert(s); + + if (drop_not_started && !s->started) + return false; + + if (!s->user) + return false; + + if (s->fifo_fd >= 0) { + if (pipe_eof(s->fifo_fd) <= 0) + return true; + } + + if (s->scope_job && manager_job_is_active(s->manager, s->scope_job)) + return true; + + if (s->scope && manager_unit_is_active(s->manager, s->scope)) + return true; + + return false; +} + +void session_add_to_gc_queue(Session *s) { + assert(s); + + if (s->in_gc_queue) + return; + + LIST_PREPEND(gc_queue, s->manager->session_gc_queue, s); + s->in_gc_queue = true; +} + +SessionState session_get_state(Session *s) { + assert(s); + + /* always check closing first */ + if (s->stopping || s->timer_event_source) + return SESSION_CLOSING; + + if (s->scope_job || s->fifo_fd < 0) + return SESSION_OPENING; + + if (session_is_active(s)) + return SESSION_ACTIVE; + + return SESSION_ONLINE; +} + +int session_kill(Session *s, KillWho who, int signo) { + assert(s); + + if (!s->scope) + return -ESRCH; + + return manager_kill_unit(s->manager, s->scope, who, signo, NULL); +} + +static int session_open_vt(Session *s) { + char path[sizeof("/dev/tty") + DECIMAL_STR_MAX(s->vtnr)]; + + if (s->vtnr < 1) + return -ENODEV; + + if (s->vtfd >= 0) + return s->vtfd; + + sprintf(path, "/dev/tty%u", s->vtnr); + s->vtfd = open_terminal(path, O_RDWR | O_CLOEXEC | O_NONBLOCK | O_NOCTTY); + if (s->vtfd < 0) + return log_error_errno(s->vtfd, "cannot open VT %s of session %s: %m", path, s->id); + + return s->vtfd; +} + +int session_prepare_vt(Session *s) { + int vt, r; + struct vt_mode mode = { 0 }; + + if (s->vtnr < 1) + return 0; + + vt = session_open_vt(s); + if (vt < 0) + return vt; + + r = fchown(vt, s->user->uid, -1); + if (r < 0) { + r = log_error_errno(errno, + "Cannot change owner of /dev/tty%u: %m", + s->vtnr); + goto error; + } + + r = ioctl(vt, KDSKBMODE, K_OFF); + if (r < 0) { + r = log_error_errno(errno, + "Cannot set K_OFF on /dev/tty%u: %m", + s->vtnr); + goto error; + } + + r = ioctl(vt, KDSETMODE, KD_GRAPHICS); + if (r < 0) { + r = log_error_errno(errno, + "Cannot set KD_GRAPHICS on /dev/tty%u: %m", + s->vtnr); + goto error; + } + + /* Oh, thanks to the VT layer, VT_AUTO does not work with KD_GRAPHICS. + * So we need a dummy handler here which just acknowledges *all* VT + * switch requests. */ + mode.mode = VT_PROCESS; + mode.relsig = SIGRTMIN; + mode.acqsig = SIGRTMIN + 1; + r = ioctl(vt, VT_SETMODE, &mode); + if (r < 0) { + r = log_error_errno(errno, + "Cannot set VT_PROCESS on /dev/tty%u: %m", + s->vtnr); + goto error; + } + + return 0; + +error: + session_restore_vt(s); + return r; +} + +void session_restore_vt(Session *s) { + + static const struct vt_mode mode = { + .mode = VT_AUTO, + }; + + _cleanup_free_ char *utf8 = NULL; + int vt, kb, old_fd; + + /* We need to get a fresh handle to the virtual terminal, + * since the old file-descriptor is potentially in a hung-up + * state after the controlling process exited; we do a + * little dance to avoid having the terminal be available + * for reuse before we've cleaned it up. + */ + old_fd = s->vtfd; + s->vtfd = -1; + + vt = session_open_vt(s); + safe_close(old_fd); + + if (vt < 0) + return; + + (void) ioctl(vt, KDSETMODE, KD_TEXT); + + if (read_one_line_file("/sys/module/vt/parameters/default_utf8", &utf8) >= 0 && *utf8 == '1') + kb = K_UNICODE; + else + kb = K_XLATE; + + (void) ioctl(vt, KDSKBMODE, kb); + + (void) ioctl(vt, VT_SETMODE, &mode); + (void) fchown(vt, 0, (gid_t) -1); + + s->vtfd = safe_close(s->vtfd); +} + +void session_leave_vt(Session *s) { + int r; + + assert(s); + + /* This is called whenever we get a VT-switch signal from the kernel. + * We acknowledge all of them unconditionally. Note that session are + * free to overwrite those handlers and we only register them for + * sessions with controllers. Legacy sessions are not affected. + * However, if we switch from a non-legacy to a legacy session, we must + * make sure to pause all device before acknowledging the switch. We + * process the real switch only after we are notified via sysfs, so the + * legacy session might have already started using the devices. If we + * don't pause the devices before the switch, we might confuse the + * session we switch to. */ + + if (s->vtfd < 0) + return; + + session_device_pause_all(s); + r = ioctl(s->vtfd, VT_RELDISP, 1); + if (r < 0) + log_debug_errno(errno, "Cannot release VT of session %s: %m", s->id); +} + +bool session_is_controller(Session *s, const char *sender) { + assert(s); + + return streq_ptr(s->controller, sender); +} + +static void session_release_controller(Session *s, bool notify) { + _cleanup_free_ char *name = NULL; + SessionDevice *sd; + + if (!s->controller) + return; + + name = s->controller; + + /* By resetting the controller before releasing the devices, we won't + * send notification signals. This avoids sending useless notifications + * if the controller is released on disconnects. */ + if (!notify) + s->controller = NULL; + + while ((sd = hashmap_first(s->devices))) + session_device_free(sd); + + s->controller = NULL; + s->track = sd_bus_track_unref(s->track); +} + +static int on_bus_track(sd_bus_track *track, void *userdata) { + Session *s = userdata; + + assert(track); + assert(s); + + session_drop_controller(s); + + return 0; +} + +int session_set_controller(Session *s, const char *sender, bool force) { + _cleanup_free_ char *name = NULL; + int r; + + assert(s); + assert(sender); + + if (session_is_controller(s, sender)) + return 0; + if (s->controller && !force) + return -EBUSY; + + name = strdup(sender); + if (!name) + return -ENOMEM; + + s->track = sd_bus_track_unref(s->track); + r = sd_bus_track_new(s->manager->bus, &s->track, on_bus_track, s); + if (r < 0) + return r; + + r = sd_bus_track_add_name(s->track, name); + if (r < 0) + return r; + + /* When setting a session controller, we forcibly mute the VT and set + * it into graphics-mode. Applications can override that by changing + * VT state after calling TakeControl(). However, this serves as a good + * default and well-behaving controllers can now ignore VTs entirely. + * Note that we reset the VT on ReleaseControl() and if the controller + * exits. + * If logind crashes/restarts, we restore the controller during restart + * or reset the VT in case it crashed/exited, too. */ + r = session_prepare_vt(s); + if (r < 0) { + s->track = sd_bus_track_unref(s->track); + return r; + } + + session_release_controller(s, true); + s->controller = name; + name = NULL; + session_save(s); + + return 0; +} + +void session_drop_controller(Session *s) { + assert(s); + + if (!s->controller) + return; + + s->track = sd_bus_track_unref(s->track); + session_release_controller(s, false); + session_save(s); + session_restore_vt(s); +} + +static const char* const session_state_table[_SESSION_STATE_MAX] = { + [SESSION_OPENING] = "opening", + [SESSION_ONLINE] = "online", + [SESSION_ACTIVE] = "active", + [SESSION_CLOSING] = "closing" +}; + +DEFINE_STRING_TABLE_LOOKUP(session_state, SessionState); + +static const char* const session_type_table[_SESSION_TYPE_MAX] = { + [SESSION_UNSPECIFIED] = "unspecified", + [SESSION_TTY] = "tty", + [SESSION_X11] = "x11", + [SESSION_WAYLAND] = "wayland", + [SESSION_MIR] = "mir", + [SESSION_WEB] = "web", +}; + +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", + [SESSION_BACKGROUND] = "background" +}; + +DEFINE_STRING_TABLE_LOOKUP(session_class, SessionClass); + +static const char* const kill_who_table[_KILL_WHO_MAX] = { + [KILL_LEADER] = "leader", + [KILL_ALL] = "all" +}; + +DEFINE_STRING_TABLE_LOOKUP(kill_who, KillWho); diff --git a/src/grp-login/systemd-logind/logind-session.h b/src/grp-login/systemd-logind/logind-session.h new file mode 100644 index 0000000000..23ff6576c9 --- /dev/null +++ b/src/grp-login/systemd-logind/logind-session.h @@ -0,0 +1,186 @@ +#pragma once + +/*** + 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 Lesser General Public License as published by + the Free Software Foundation; either version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +typedef struct Session Session; +typedef enum KillWho KillWho; + +#include "basic/list.h" +#include "basic/login-util.h" + +#include "logind-user.h" + +typedef enum SessionState { + SESSION_OPENING, /* Session scope is being created */ + SESSION_ONLINE, /* Logged in */ + SESSION_ACTIVE, /* Logged in and in the fg */ + SESSION_CLOSING, /* Logged out, but scope is still there */ + _SESSION_STATE_MAX, + _SESSION_STATE_INVALID = -1 +} SessionState; + +typedef enum SessionClass { + SESSION_USER, + SESSION_GREETER, + SESSION_LOCK_SCREEN, + SESSION_BACKGROUND, + _SESSION_CLASS_MAX, + _SESSION_CLASS_INVALID = -1 +} SessionClass; + +typedef enum SessionType { + SESSION_UNSPECIFIED, + SESSION_TTY, + SESSION_X11, + SESSION_WAYLAND, + SESSION_MIR, + SESSION_WEB, + _SESSION_TYPE_MAX, + _SESSION_TYPE_INVALID = -1 +} SessionType; + +#define SESSION_TYPE_IS_GRAPHICAL(type) IN_SET(type, SESSION_X11, SESSION_WAYLAND, SESSION_MIR) + +enum KillWho { + KILL_LEADER, + KILL_ALL, + _KILL_WHO_MAX, + _KILL_WHO_INVALID = -1 +}; + +struct Session { + Manager *manager; + + const char *id; + unsigned int position; + SessionType type; + SessionClass class; + + char *state_file; + + User *user; + + dual_timestamp timestamp; + + char *tty; + char *display; + + bool remote; + char *remote_user; + char *remote_host; + char *service; + char *desktop; + + char *scope; + char *scope_job; + + Seat *seat; + unsigned int vtnr; + int vtfd; + + pid_t leader; + uint32_t audit_id; + + int fifo_fd; + char *fifo_path; + + sd_event_source *fifo_event_source; + + bool idle_hint; + dual_timestamp idle_hint_timestamp; + + bool locked_hint; + + bool in_gc_queue:1; + bool started:1; + bool stopping:1; + + sd_bus_message *create_message; + + sd_event_source *timer_event_source; + + char *controller; + Hashmap *devices; + sd_bus_track *track; + + LIST_FIELDS(Session, sessions_by_user); + LIST_FIELDS(Session, sessions_by_seat); + + LIST_FIELDS(Session, gc_queue); +}; + +Session *session_new(Manager *m, const char *id); +void session_free(Session *s); +void session_set_user(Session *s, User *u); +bool session_check_gc(Session *s, bool drop_not_started); +void session_add_to_gc_queue(Session *s); +int session_activate(Session *s); +bool session_is_active(Session *s); +int session_get_idle_hint(Session *s, dual_timestamp *t); +void session_set_idle_hint(Session *s, bool b); +int session_get_locked_hint(Session *s); +void session_set_locked_hint(Session *s, bool b); +int session_create_fifo(Session *s); +int session_start(Session *s); +int session_stop(Session *s, bool force); +int session_finalize(Session *s); +int session_release(Session *s); +int session_save(Session *s); +int session_load(Session *s); +int session_kill(Session *s, KillWho who, int signo); + +SessionState session_get_state(Session *u); + +extern const sd_bus_vtable session_vtable[]; +int session_node_enumerator(sd_bus *bus, const char *path,void *userdata, char ***nodes, sd_bus_error *error); +int session_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error); +char *session_bus_path(Session *s); + +int session_send_signal(Session *s, bool new_session); +int session_send_changed(Session *s, const char *properties, ...) _sentinel_; +int session_send_lock(Session *s, bool lock); +int session_send_lock_all(Manager *m, bool lock); + +int session_send_create_reply(Session *s, sd_bus_error *error); + +const char* session_state_to_string(SessionState t) _const_; +SessionState session_state_from_string(const char *s) _pure_; + +const char* session_type_to_string(SessionType t) _const_; +SessionType session_type_from_string(const char *s) _pure_; + +const char* session_class_to_string(SessionClass t) _const_; +SessionClass session_class_from_string(const char *s) _pure_; + +const char *kill_who_to_string(KillWho k) _const_; +KillWho kill_who_from_string(const char *s) _pure_; + +int session_prepare_vt(Session *s); +void session_restore_vt(Session *s); +void session_leave_vt(Session *s); + +bool session_is_controller(Session *s, const char *sender); +int session_set_controller(Session *s, const char *sender, bool force); +void session_drop_controller(Session *s); + +int bus_session_method_activate(sd_bus_message *message, void *userdata, sd_bus_error *error); +int bus_session_method_lock(sd_bus_message *message, void *userdata, sd_bus_error *error); +int bus_session_method_terminate(sd_bus_message *message, void *userdata, sd_bus_error *error); +int bus_session_method_kill(sd_bus_message *message, void *userdata, sd_bus_error *error); diff --git a/src/grp-login/systemd-logind/logind-user-dbus.c b/src/grp-login/systemd-logind/logind-user-dbus.c new file mode 100644 index 0000000000..d82ced605e --- /dev/null +++ b/src/grp-login/systemd-logind/logind-user-dbus.c @@ -0,0 +1,399 @@ +/*** + 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 Lesser General Public License as published by + the Free Software Foundation; either version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include + +#include "basic/alloc-util.h" +#include "basic/formats-util.h" +#include "basic/signal-util.h" +#include "basic/strv.h" +#include "basic/user-util.h" +#include "logind.h" +#include "shared/bus-util.h" + +#include "logind-user.h" + +static int property_get_display( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + _cleanup_free_ char *p = NULL; + User *u = userdata; + + assert(bus); + assert(reply); + assert(u); + + p = u->display ? session_bus_path(u->display) : strdup("/"); + if (!p) + return -ENOMEM; + + return sd_bus_message_append(reply, "(so)", u->display ? u->display->id : "", p); +} + +static int property_get_state( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + User *u = userdata; + + assert(bus); + assert(reply); + assert(u); + + return sd_bus_message_append(reply, "s", user_state_to_string(user_get_state(u))); +} + +static int property_get_sessions( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + User *u = userdata; + Session *session; + int r; + + assert(bus); + assert(reply); + assert(u); + + r = sd_bus_message_open_container(reply, 'a', "(so)"); + if (r < 0) + return r; + + LIST_FOREACH(sessions_by_user, session, u->sessions) { + _cleanup_free_ char *p = NULL; + + p = session_bus_path(session); + if (!p) + return -ENOMEM; + + r = sd_bus_message_append(reply, "(so)", session->id, p); + if (r < 0) + return r; + + } + + return sd_bus_message_close_container(reply); +} + +static int property_get_idle_hint( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + User *u = userdata; + + assert(bus); + assert(reply); + assert(u); + + return sd_bus_message_append(reply, "b", user_get_idle_hint(u, NULL) > 0); +} + +static int property_get_idle_since_hint( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + User *u = userdata; + dual_timestamp t = DUAL_TIMESTAMP_NULL; + uint64_t k; + + assert(bus); + assert(reply); + assert(u); + + user_get_idle_hint(u, &t); + k = streq(property, "IdleSinceHint") ? t.realtime : t.monotonic; + + return sd_bus_message_append(reply, "t", k); +} + +static int property_get_linger( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + User *u = userdata; + int r; + + assert(bus); + assert(reply); + assert(u); + + r = user_check_linger_file(u); + + return sd_bus_message_append(reply, "b", r > 0); +} + +int bus_user_method_terminate(sd_bus_message *message, void *userdata, sd_bus_error *error) { + User *u = userdata; + int r; + + assert(message); + assert(u); + + r = bus_verify_polkit_async( + message, + CAP_KILL, + "org.freedesktop.login1.manage", + NULL, + false, + u->uid, + &u->manager->polkit_registry, + error); + if (r < 0) + return r; + if (r == 0) + return 1; /* Will call us back */ + + r = user_stop(u, true); + if (r < 0) + return r; + + return sd_bus_reply_method_return(message, NULL); +} + +int bus_user_method_kill(sd_bus_message *message, void *userdata, sd_bus_error *error) { + User *u = userdata; + int32_t signo; + int r; + + assert(message); + assert(u); + + r = bus_verify_polkit_async( + message, + CAP_KILL, + "org.freedesktop.login1.manage", + NULL, + false, + u->uid, + &u->manager->polkit_registry, + error); + if (r < 0) + return r; + if (r == 0) + return 1; /* Will call us back */ + + r = sd_bus_message_read(message, "i", &signo); + if (r < 0) + return r; + + if (!SIGNAL_VALID(signo)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid signal %i", signo); + + r = user_kill(u, signo); + if (r < 0) + return r; + + return sd_bus_reply_method_return(message, NULL); +} + +const sd_bus_vtable user_vtable[] = { + SD_BUS_VTABLE_START(0), + + SD_BUS_PROPERTY("UID", "u", bus_property_get_uid, offsetof(User, uid), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("GID", "u", bus_property_get_gid, offsetof(User, gid), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Name", "s", NULL, offsetof(User, name), SD_BUS_VTABLE_PROPERTY_CONST), + BUS_PROPERTY_DUAL_TIMESTAMP("Timestamp", offsetof(User, timestamp), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("RuntimePath", "s", NULL, offsetof(User, runtime_path), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Service", "s", NULL, offsetof(User, service), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Slice", "s", NULL, offsetof(User, slice), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Display", "(so)", property_get_display, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("State", "s", property_get_state, 0, 0), + SD_BUS_PROPERTY("Sessions", "a(so)", property_get_sessions, 0, 0), + SD_BUS_PROPERTY("IdleHint", "b", property_get_idle_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("IdleSinceHint", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("IdleSinceHintMonotonic", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("Linger", "b", property_get_linger, 0, 0), + + SD_BUS_METHOD("Terminate", NULL, NULL, bus_user_method_terminate, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("Kill", "i", NULL, bus_user_method_kill, SD_BUS_VTABLE_UNPRIVILEGED), + + SD_BUS_VTABLE_END +}; + +int user_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) { + Manager *m = userdata; + uid_t uid; + User *user; + int r; + + assert(bus); + assert(path); + assert(interface); + assert(found); + assert(m); + + if (streq(path, "/org/freedesktop/login1/user/self")) { + _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; + sd_bus_message *message; + + message = sd_bus_get_current_message(bus); + if (!message) + return 0; + + r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_OWNER_UID|SD_BUS_CREDS_AUGMENT, &creds); + if (r < 0) + return r; + + r = sd_bus_creds_get_owner_uid(creds, &uid); + } else { + const char *p; + + p = startswith(path, "/org/freedesktop/login1/user/_"); + if (!p) + return 0; + + r = parse_uid(p, &uid); + } + if (r < 0) + return 0; + + user = hashmap_get(m->users, UID_TO_PTR(uid)); + if (!user) + return 0; + + *found = user; + return 1; +} + +char *user_bus_path(User *u) { + char *s; + + assert(u); + + if (asprintf(&s, "/org/freedesktop/login1/user/_"UID_FMT, u->uid) < 0) + return NULL; + + return s; +} + +int user_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) { + _cleanup_strv_free_ char **l = NULL; + sd_bus_message *message; + Manager *m = userdata; + User *user; + Iterator i; + int r; + + assert(bus); + assert(path); + assert(nodes); + + HASHMAP_FOREACH(user, m->users, i) { + char *p; + + p = user_bus_path(user); + if (!p) + return -ENOMEM; + + r = strv_consume(&l, p); + if (r < 0) + return r; + } + + message = sd_bus_get_current_message(bus); + if (message) { + _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; + uid_t uid; + + r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_OWNER_UID|SD_BUS_CREDS_AUGMENT, &creds); + if (r >= 0) { + r = sd_bus_creds_get_owner_uid(creds, &uid); + if (r >= 0) { + user = hashmap_get(m->users, UID_TO_PTR(uid)); + if (user) { + r = strv_extend(&l, "/org/freedesktop/login1/user/self"); + if (r < 0) + return r; + } + } + } + } + + *nodes = l; + l = NULL; + + return 1; +} + +int user_send_signal(User *u, bool new_user) { + _cleanup_free_ char *p = NULL; + + assert(u); + + p = user_bus_path(u); + if (!p) + return -ENOMEM; + + return sd_bus_emit_signal( + u->manager->bus, + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + new_user ? "UserNew" : "UserRemoved", + "uo", (uint32_t) u->uid, p); +} + +int user_send_changed(User *u, const char *properties, ...) { + _cleanup_free_ char *p = NULL; + char **l; + + assert(u); + + if (!u->started) + return 0; + + p = user_bus_path(u); + if (!p) + return -ENOMEM; + + l = strv_from_stdarg_alloca(properties); + + return sd_bus_emit_properties_changed_strv(u->manager->bus, p, "org.freedesktop.login1.User", l); +} diff --git a/src/grp-login/systemd-logind/logind-user.c b/src/grp-login/systemd-logind/logind-user.c new file mode 100644 index 0000000000..015276efc8 --- /dev/null +++ b/src/grp-login/systemd-logind/logind-user.c @@ -0,0 +1,886 @@ +/*** + 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 Lesser General Public License as published by + the Free Software Foundation; either version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/escape.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/formats-util.h" +#include "basic/fs-util.h" +#include "basic/hashmap.h" +#include "basic/label.h" +#include "basic/mkdir.h" +#include "basic/mount-util.h" +#include "basic/parse-util.h" +#include "basic/path-util.h" +#include "basic/rm-rf.h" +#include "basic/smack-util.h" +#include "basic/special.h" +#include "basic/stdio-util.h" +#include "basic/string-table.h" +#include "basic/unit-name.h" +#include "basic/user-util.h" +#include "basic/util.h" +#include "sd-bus/bus-common-errors.h" +#include "sd-bus/bus-error.h" +#include "shared/bus-util.h" +#include "shared/clean-ipc.h" +#include "shared/conf-parser.h" + +#include "logind-user.h" + +int user_new(User **out, Manager *m, uid_t uid, gid_t gid, const char *name) { + _cleanup_(user_freep) User *u = NULL; + char lu[DECIMAL_STR_MAX(uid_t) + 1]; + int r; + + assert(out); + assert(m); + assert(name); + + u = new0(User, 1); + if (!u) + return -ENOMEM; + + u->manager = m; + u->uid = uid; + u->gid = gid; + xsprintf(lu, UID_FMT, uid); + + u->name = strdup(name); + if (!u->name) + return -ENOMEM; + + if (asprintf(&u->state_file, "/run/systemd/users/"UID_FMT, uid) < 0) + return -ENOMEM; + + if (asprintf(&u->runtime_path, "/run/user/"UID_FMT, uid) < 0) + return -ENOMEM; + + r = slice_build_subslice(SPECIAL_USER_SLICE, lu, &u->slice); + if (r < 0) + return r; + + r = unit_name_build("user", lu, ".service", &u->service); + if (r < 0) + return r; + + r = hashmap_put(m->users, UID_TO_PTR(uid), u); + if (r < 0) + return r; + + r = hashmap_put(m->user_units, u->slice, u); + if (r < 0) + return r; + + r = hashmap_put(m->user_units, u->service, u); + if (r < 0) + return r; + + *out = u; + u = NULL; + return 0; +} + +User *user_free(User *u) { + if (!u) + return NULL; + + if (u->in_gc_queue) + LIST_REMOVE(gc_queue, u->manager->user_gc_queue, u); + + while (u->sessions) + session_free(u->sessions); + + if (u->service) + hashmap_remove_value(u->manager->user_units, u->service, u); + + if (u->slice) + hashmap_remove_value(u->manager->user_units, u->slice, u); + + hashmap_remove_value(u->manager->users, UID_TO_PTR(u->uid), u); + + u->slice_job = mfree(u->slice_job); + u->service_job = mfree(u->service_job); + + u->service = mfree(u->service); + u->slice = mfree(u->slice); + u->runtime_path = mfree(u->runtime_path); + u->state_file = mfree(u->state_file); + u->name = mfree(u->name); + + return mfree(u); +} + +static int user_save_internal(User *u) { + _cleanup_free_ char *temp_path = NULL; + _cleanup_fclose_ FILE *f = NULL; + int r; + + assert(u); + assert(u->state_file); + + r = mkdir_safe_label("/run/systemd/users", 0755, 0, 0); + if (r < 0) + goto fail; + + r = fopen_temporary(u->state_file, &f, &temp_path); + if (r < 0) + goto fail; + + fchmod(fileno(f), 0644); + + fprintf(f, + "# This is private data. Do not parse.\n" + "NAME=%s\n" + "STATE=%s\n", + u->name, + user_state_to_string(user_get_state(u))); + + /* LEGACY: no-one reads RUNTIME= anymore, drop it at some point */ + if (u->runtime_path) + fprintf(f, "RUNTIME=%s\n", u->runtime_path); + + if (u->service_job) + fprintf(f, "SERVICE_JOB=%s\n", u->service_job); + + if (u->slice_job) + fprintf(f, "SLICE_JOB=%s\n", u->slice_job); + + if (u->display) + fprintf(f, "DISPLAY=%s\n", u->display->id); + + if (dual_timestamp_is_set(&u->timestamp)) + fprintf(f, + "REALTIME="USEC_FMT"\n" + "MONOTONIC="USEC_FMT"\n", + u->timestamp.realtime, + u->timestamp.monotonic); + + if (u->sessions) { + Session *i; + bool first; + + fputs("SESSIONS=", f); + first = true; + LIST_FOREACH(sessions_by_user, i, u->sessions) { + if (first) + first = false; + else + fputc(' ', f); + + fputs(i->id, f); + } + + fputs("\nSEATS=", f); + first = true; + LIST_FOREACH(sessions_by_user, i, u->sessions) { + if (!i->seat) + continue; + + if (first) + first = false; + else + fputc(' ', f); + + fputs(i->seat->id, f); + } + + fputs("\nACTIVE_SESSIONS=", f); + first = true; + LIST_FOREACH(sessions_by_user, i, u->sessions) { + if (!session_is_active(i)) + continue; + + if (first) + first = false; + else + fputc(' ', f); + + fputs(i->id, f); + } + + fputs("\nONLINE_SESSIONS=", f); + first = true; + LIST_FOREACH(sessions_by_user, i, u->sessions) { + if (session_get_state(i) == SESSION_CLOSING) + continue; + + if (first) + first = false; + else + fputc(' ', f); + + fputs(i->id, f); + } + + fputs("\nACTIVE_SEATS=", f); + first = true; + LIST_FOREACH(sessions_by_user, i, u->sessions) { + if (!session_is_active(i) || !i->seat) + continue; + + if (first) + first = false; + else + fputc(' ', f); + + fputs(i->seat->id, f); + } + + fputs("\nONLINE_SEATS=", f); + first = true; + LIST_FOREACH(sessions_by_user, i, u->sessions) { + if (session_get_state(i) == SESSION_CLOSING || !i->seat) + continue; + + if (first) + first = false; + else + fputc(' ', f); + + fputs(i->seat->id, f); + } + fputc('\n', f); + } + + r = fflush_and_check(f); + if (r < 0) + goto fail; + + if (rename(temp_path, u->state_file) < 0) { + r = -errno; + goto fail; + } + + return 0; + +fail: + (void) unlink(u->state_file); + + if (temp_path) + (void) unlink(temp_path); + + return log_error_errno(r, "Failed to save user data %s: %m", u->state_file); +} + +int user_save(User *u) { + assert(u); + + if (!u->started) + return 0; + + return user_save_internal (u); +} + +int user_load(User *u) { + _cleanup_free_ char *display = NULL, *realtime = NULL, *monotonic = NULL; + Session *s = NULL; + int r; + + assert(u); + + r = parse_env_file(u->state_file, NEWLINE, + "SERVICE_JOB", &u->service_job, + "SLICE_JOB", &u->slice_job, + "DISPLAY", &display, + "REALTIME", &realtime, + "MONOTONIC", &monotonic, + NULL); + if (r < 0) { + if (r == -ENOENT) + return 0; + + log_error_errno(r, "Failed to read %s: %m", u->state_file); + return r; + } + + if (display) + s = hashmap_get(u->manager->sessions, display); + + if (s && s->display && display_is_local(s->display)) + u->display = s; + + if (realtime) + timestamp_deserialize(realtime, &u->timestamp.realtime); + if (monotonic) + timestamp_deserialize(monotonic, &u->timestamp.monotonic); + + return r; +} + +static int user_mkdir_runtime_path(User *u) { + int r; + + assert(u); + + r = mkdir_safe_label("/run/user", 0755, 0, 0); + if (r < 0) + return log_error_errno(r, "Failed to create /run/user: %m"); + + if (path_is_mount_point(u->runtime_path, 0) <= 0) { + _cleanup_free_ char *t = NULL; + + (void) mkdir_label(u->runtime_path, 0700); + + if (mac_smack_use()) + r = asprintf(&t, "mode=0700,smackfsroot=*,uid=" UID_FMT ",gid=" GID_FMT ",size=%zu", u->uid, u->gid, u->manager->runtime_dir_size); + else + r = asprintf(&t, "mode=0700,uid=" UID_FMT ",gid=" GID_FMT ",size=%zu", u->uid, u->gid, u->manager->runtime_dir_size); + if (r < 0) { + r = log_oom(); + goto fail; + } + + r = mount("tmpfs", u->runtime_path, "tmpfs", MS_NODEV|MS_NOSUID, t); + if (r < 0) { + if (errno != EPERM) { + r = log_error_errno(errno, "Failed to mount per-user tmpfs directory %s: %m", u->runtime_path); + goto fail; + } + + /* Lacking permissions, maybe + * CAP_SYS_ADMIN-less container? In this case, + * just use a normal directory. */ + + r = chmod_and_chown(u->runtime_path, 0700, u->uid, u->gid); + if (r < 0) { + log_error_errno(r, "Failed to change runtime directory ownership and mode: %m"); + goto fail; + } + } + + r = label_fix(u->runtime_path, false, false); + if (r < 0) + log_warning_errno(r, "Failed to fix label of '%s', ignoring: %m", u->runtime_path); + } + + return 0; + +fail: + /* Try to clean up, but ignore errors */ + (void) rmdir(u->runtime_path); + return r; +} + +static int user_start_slice(User *u) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + const char *description; + char *job; + int r; + + assert(u); + + u->slice_job = mfree(u->slice_job); + description = strjoina("User Slice of ", u->name); + + r = manager_start_slice( + u->manager, + u->slice, + description, + "systemd-logind.service", + "systemd-user-sessions.service", + u->manager->user_tasks_max, + &error, + &job); + if (r >= 0) + u->slice_job = job; + else if (!sd_bus_error_has_name(&error, BUS_ERROR_UNIT_EXISTS)) + /* we don't fail due to this, let's try to continue */ + log_error_errno(r, "Failed to start user slice %s, ignoring: %s (%s)", + u->slice, bus_error_message(&error, r), error.name); + + return 0; +} + +static int user_start_service(User *u) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + char *job; + int r; + + assert(u); + + u->service_job = mfree(u->service_job); + + r = manager_start_unit( + u->manager, + u->service, + &error, + &job); + if (r < 0) { + /* we don't fail due to this, let's try to continue */ + log_error_errno(r, "Failed to start user service, ignoring: %s", bus_error_message(&error, r)); + } else { + u->service_job = job; + } + + return 0; +} + +int user_start(User *u) { + int r; + + assert(u); + + if (u->started && !u->stopping) + return 0; + + /* + * If u->stopping is set, the user is marked for removal and the slice + * and service stop-jobs are queued. We have to clear that flag before + * queing the start-jobs again. If they succeed, the user object can be + * re-used just fine (pid1 takes care of job-ordering and proper + * restart), but if they fail, we want to force another user_stop() so + * possibly pending units are stopped. + * Note that we don't clear u->started, as we have no clue what state + * the user is in on failure here. Hence, we pretend the user is + * running so it will be properly taken down by GC. However, we clearly + * return an error from user_start() in that case, so no further + * reference to the user is taken. + */ + u->stopping = false; + + if (!u->started) { + log_debug("New user %s logged in.", u->name); + + /* Make XDG_RUNTIME_DIR */ + r = user_mkdir_runtime_path(u); + if (r < 0) + return r; + } + + /* Create cgroup */ + r = user_start_slice(u); + if (r < 0) + return r; + + /* Save the user data so far, because pam_systemd will read the + * XDG_RUNTIME_DIR out of it while starting up systemd --user. + * We need to do user_save_internal() because we have not + * "officially" started yet. */ + user_save_internal(u); + + /* Spawn user systemd */ + r = user_start_service(u); + if (r < 0) + return r; + + if (!u->started) { + if (!dual_timestamp_is_set(&u->timestamp)) + dual_timestamp_get(&u->timestamp); + user_send_signal(u, true); + u->started = true; + } + + /* Save new user data */ + user_save(u); + + return 0; +} + +static int user_stop_slice(User *u) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + char *job; + int r; + + assert(u); + + r = manager_stop_unit(u->manager, u->slice, &error, &job); + if (r < 0) { + log_error("Failed to stop user slice: %s", bus_error_message(&error, r)); + return r; + } + + free(u->slice_job); + u->slice_job = job; + + return r; +} + +static int user_stop_service(User *u) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + char *job; + int r; + + assert(u); + + r = manager_stop_unit(u->manager, u->service, &error, &job); + if (r < 0) { + log_error("Failed to stop user service: %s", bus_error_message(&error, r)); + return r; + } + + free(u->service_job); + u->service_job = job; + + return r; +} + +static int user_remove_runtime_path(User *u) { + int r; + + assert(u); + + r = rm_rf(u->runtime_path, 0); + if (r < 0) + log_error_errno(r, "Failed to remove runtime directory %s: %m", u->runtime_path); + + /* Ignore cases where the directory isn't mounted, as that's + * quite possible, if we lacked the permissions to mount + * something */ + r = umount2(u->runtime_path, MNT_DETACH); + if (r < 0 && errno != EINVAL && errno != ENOENT) + log_error_errno(errno, "Failed to unmount user runtime directory %s: %m", u->runtime_path); + + r = rm_rf(u->runtime_path, REMOVE_ROOT); + if (r < 0) + log_error_errno(r, "Failed to remove runtime directory %s: %m", u->runtime_path); + + return r; +} + +int user_stop(User *u, bool force) { + Session *s; + int r = 0, k; + assert(u); + + /* Stop jobs have already been queued */ + if (u->stopping) { + user_save(u); + return r; + } + + LIST_FOREACH(sessions_by_user, s, u->sessions) { + k = session_stop(s, force); + if (k < 0) + r = k; + } + + /* Kill systemd */ + k = user_stop_service(u); + if (k < 0) + r = k; + + /* Kill cgroup */ + k = user_stop_slice(u); + if (k < 0) + r = k; + + u->stopping = true; + + user_save(u); + + return r; +} + +int user_finalize(User *u) { + Session *s; + int r = 0, k; + + assert(u); + + if (u->started) + log_debug("User %s logged out.", u->name); + + LIST_FOREACH(sessions_by_user, s, u->sessions) { + k = session_finalize(s); + if (k < 0) + r = k; + } + + /* Kill XDG_RUNTIME_DIR */ + k = user_remove_runtime_path(u); + if (k < 0) + r = k; + + /* Clean SysV + POSIX IPC objects */ + if (u->manager->remove_ipc) { + k = clean_ipc(u->uid); + if (k < 0) + r = k; + } + + unlink(u->state_file); + user_add_to_gc_queue(u); + + if (u->started) { + user_send_signal(u, false); + u->started = false; + } + + return r; +} + +int user_get_idle_hint(User *u, dual_timestamp *t) { + Session *s; + bool idle_hint = true; + dual_timestamp ts = DUAL_TIMESTAMP_NULL; + + assert(u); + + LIST_FOREACH(sessions_by_user, s, u->sessions) { + dual_timestamp k; + int ih; + + ih = session_get_idle_hint(s, &k); + if (ih < 0) + return ih; + + if (!ih) { + if (!idle_hint) { + if (k.monotonic < ts.monotonic) + ts = k; + } else { + idle_hint = false; + ts = k; + } + } else if (idle_hint) { + + if (k.monotonic > ts.monotonic) + ts = k; + } + } + + if (t) + *t = ts; + + return idle_hint; +} + +int user_check_linger_file(User *u) { + _cleanup_free_ char *cc = NULL; + char *p = NULL; + + cc = cescape(u->name); + if (!cc) + return -ENOMEM; + + p = strjoina("/var/lib/systemd/linger/", cc); + + return access(p, F_OK) >= 0; +} + +bool user_check_gc(User *u, bool drop_not_started) { + assert(u); + + if (drop_not_started && !u->started) + return false; + + if (u->sessions) + return true; + + if (user_check_linger_file(u) > 0) + return true; + + if (u->slice_job && manager_job_is_active(u->manager, u->slice_job)) + return true; + + if (u->service_job && manager_job_is_active(u->manager, u->service_job)) + return true; + + return false; +} + +void user_add_to_gc_queue(User *u) { + assert(u); + + if (u->in_gc_queue) + return; + + LIST_PREPEND(gc_queue, u->manager->user_gc_queue, u); + u->in_gc_queue = true; +} + +UserState user_get_state(User *u) { + Session *i; + + assert(u); + + if (u->stopping) + return USER_CLOSING; + + if (!u->started || u->slice_job || u->service_job) + return USER_OPENING; + + if (u->sessions) { + bool all_closing = true; + + LIST_FOREACH(sessions_by_user, i, u->sessions) { + SessionState state; + + state = session_get_state(i); + if (state == SESSION_ACTIVE) + return USER_ACTIVE; + if (state != SESSION_CLOSING) + all_closing = false; + } + + return all_closing ? USER_CLOSING : USER_ONLINE; + } + + if (user_check_linger_file(u) > 0) + return USER_LINGERING; + + return USER_CLOSING; +} + +int user_kill(User *u, int signo) { + assert(u); + + return manager_kill_unit(u->manager, u->slice, KILL_ALL, signo, NULL); +} + +static bool elect_display_filter(Session *s) { + /* Return true if the session is a candidate for the user’s ‘primary + * session’ or ‘display’. */ + assert(s); + + return (s->class == SESSION_USER && !s->stopping); +} + +static int elect_display_compare(Session *s1, Session *s2) { + /* Indexed by SessionType. Lower numbers mean more preferred. */ + const int type_ranks[_SESSION_TYPE_MAX] = { + [SESSION_UNSPECIFIED] = 0, + [SESSION_TTY] = -2, + [SESSION_X11] = -3, + [SESSION_WAYLAND] = -3, + [SESSION_MIR] = -3, + [SESSION_WEB] = -1, + }; + + /* Calculate the partial order relationship between s1 and s2, + * returning < 0 if s1 is preferred as the user’s ‘primary session’, + * 0 if s1 and s2 are equally preferred or incomparable, or > 0 if s2 + * is preferred. + * + * s1 or s2 may be NULL. */ + if (!s1 && !s2) + return 0; + + if ((s1 == NULL) != (s2 == NULL)) + return (s1 == NULL) - (s2 == NULL); + + if (s1->stopping != s2->stopping) + return s1->stopping - s2->stopping; + + if ((s1->class != SESSION_USER) != (s2->class != SESSION_USER)) + return (s1->class != SESSION_USER) - (s2->class != SESSION_USER); + + if ((s1->type == _SESSION_TYPE_INVALID) != (s2->type == _SESSION_TYPE_INVALID)) + return (s1->type == _SESSION_TYPE_INVALID) - (s2->type == _SESSION_TYPE_INVALID); + + if (s1->type != s2->type) + return type_ranks[s1->type] - type_ranks[s2->type]; + + return 0; +} + +void user_elect_display(User *u) { + Session *s; + + assert(u); + + /* This elects a primary session for each user, which we call + * the "display". We try to keep the assignment stable, but we + * "upgrade" to better choices. */ + log_debug("Electing new display for user %s", u->name); + + LIST_FOREACH(sessions_by_user, s, u->sessions) { + if (!elect_display_filter(s)) { + log_debug("Ignoring session %s", s->id); + continue; + } + + if (elect_display_compare(s, u->display) < 0) { + log_debug("Choosing session %s in preference to %s", s->id, u->display ? u->display->id : "-"); + u->display = s; + } + } +} + +static const char* const user_state_table[_USER_STATE_MAX] = { + [USER_OFFLINE] = "offline", + [USER_OPENING] = "opening", + [USER_LINGERING] = "lingering", + [USER_ONLINE] = "online", + [USER_ACTIVE] = "active", + [USER_CLOSING] = "closing" +}; + +DEFINE_STRING_TABLE_LOOKUP(user_state, UserState); + +int config_parse_tmpfs_size( + const char* unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + size_t *sz = data; + const char *e; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + e = endswith(rvalue, "%"); + if (e) { + unsigned long ul; + char *f; + + errno = 0; + ul = strtoul(rvalue, &f, 10); + if (errno > 0 || f != e) { + log_syntax(unit, LOG_ERR, filename, line, errno, "Failed to parse percentage value, ignoring: %s", rvalue); + return 0; + } + + if (ul <= 0 || ul >= 100) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Percentage value out of range, ignoring: %s", rvalue); + return 0; + } + + *sz = PAGE_ALIGN((size_t) ((physical_memory() * (uint64_t) ul) / (uint64_t) 100)); + } else { + uint64_t k; + + r = parse_size(rvalue, 1024, &k); + if (r < 0 || (uint64_t) (size_t) k != k) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse size value, ignoring: %s", rvalue); + return 0; + } + + *sz = PAGE_ALIGN((size_t) k); + } + + return 0; +} diff --git a/src/grp-login/systemd-logind/logind-user.h b/src/grp-login/systemd-logind/logind-user.h new file mode 100644 index 0000000000..b23c59fa1d --- /dev/null +++ b/src/grp-login/systemd-logind/logind-user.h @@ -0,0 +1,94 @@ +#pragma once + +/*** + 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 Lesser General Public License as published by + the Free Software Foundation; either version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "basic/list.h" + +typedef struct User User; + +#include "logind.h" + +typedef enum UserState { + USER_OFFLINE, /* Not logged in at all */ + USER_OPENING, /* Is logging in */ + USER_LINGERING, /* Lingering has been enabled by the admin for this user */ + USER_ONLINE, /* User logged in */ + USER_ACTIVE, /* User logged in and has a session in the fg */ + USER_CLOSING, /* User logged out, but processes still remain and lingering is not enabled */ + _USER_STATE_MAX, + _USER_STATE_INVALID = -1 +} UserState; + +struct User { + Manager *manager; + uid_t uid; + gid_t gid; + char *name; + char *state_file; + char *runtime_path; + char *slice; + char *service; + + char *service_job; + char *slice_job; + + Session *display; + + dual_timestamp timestamp; + + bool in_gc_queue:1; + bool started:1; + bool stopping:1; + + LIST_HEAD(Session, sessions); + LIST_FIELDS(User, gc_queue); +}; + +int user_new(User **out, Manager *m, uid_t uid, gid_t gid, const char *name); +User *user_free(User *u); + +DEFINE_TRIVIAL_CLEANUP_FUNC(User *, user_free); + +bool user_check_gc(User *u, bool drop_not_started); +void user_add_to_gc_queue(User *u); +int user_start(User *u); +int user_stop(User *u, bool force); +int user_finalize(User *u); +UserState user_get_state(User *u); +int user_get_idle_hint(User *u, dual_timestamp *t); +int user_save(User *u); +int user_load(User *u); +int user_kill(User *u, int signo); +int user_check_linger_file(User *u); +void user_elect_display(User *u); + +extern const sd_bus_vtable user_vtable[]; +int user_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error); +int user_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error); +char *user_bus_path(User *s); + +int user_send_signal(User *u, bool new_user); +int user_send_changed(User *u, const char *properties, ...) _sentinel_; + +const char* user_state_to_string(UserState s) _const_; +UserState user_state_from_string(const char *s) _pure_; + +int bus_user_method_terminate(sd_bus_message *message, void *userdata, sd_bus_error *error); +int bus_user_method_kill(sd_bus_message *message, void *userdata, sd_bus_error *error); diff --git a/src/grp-login/systemd-logind/logind-utmp.c b/src/grp-login/systemd-logind/logind-utmp.c new file mode 100644 index 0000000000..c5dc137d61 --- /dev/null +++ b/src/grp-login/systemd-logind/logind-utmp.c @@ -0,0 +1,183 @@ +/*** + This file is part of systemd. + + Copyright 2015 Daniel Mack + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include + +#include + +#include "basic/alloc-util.h" +#include "basic/audit-util.h" +#include "basic/formats-util.h" +#include "basic/special.h" +#include "basic/strv.h" +#include "basic/unit-name.h" +#include "basic/user-util.h" +#include "logind.h" +#include "sd-bus/bus-common-errors.h" +#include "sd-bus/bus-error.h" +#include "shared/bus-util.h" +#include "shared/utmp-wtmp.h" + +_const_ static usec_t when_wall(usec_t n, usec_t elapse) { + + usec_t left; + unsigned int i; + static const int wall_timers[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, + 25, 40, 55, 70, 100, 130, 150, 180, + }; + + /* If the time is already passed, then don't announce */ + if (n >= elapse) + return 0; + + left = elapse - n; + + for (i = 1; i < ELEMENTSOF(wall_timers); i++) + if (wall_timers[i] * USEC_PER_MINUTE >= left) + return left - wall_timers[i-1] * USEC_PER_MINUTE; + + return left % USEC_PER_HOUR; +} + +bool logind_wall_tty_filter(const char *tty, void *userdata) { + + Manager *m = userdata; + + assert(m); + + if (!startswith(tty, "/dev/") || !m->scheduled_shutdown_tty) + return true; + + return !streq(tty + 5, m->scheduled_shutdown_tty); +} + +static int warn_wall(Manager *m, usec_t n) { + char date[FORMAT_TIMESTAMP_MAX] = {}; + _cleanup_free_ char *l = NULL; + usec_t left; + int r; + + assert(m); + + if (!m->enable_wall_messages) + return 0; + + left = m->scheduled_shutdown_timeout > n; + + r = asprintf(&l, "%s%sThe system is going down for %s %s%s!", + strempty(m->wall_message), + isempty(m->wall_message) ? "" : "\n", + m->scheduled_shutdown_type, + left ? "at " : "NOW", + left ? format_timestamp(date, sizeof(date), m->scheduled_shutdown_timeout) : ""); + if (r < 0) { + log_oom(); + return 0; + } + + utmp_wall(l, uid_to_name(m->scheduled_shutdown_uid), + m->scheduled_shutdown_tty, logind_wall_tty_filter, m); + + return 1; +} + +static int wall_message_timeout_handler( + sd_event_source *s, + uint64_t usec, + void *userdata) { + + Manager *m = userdata; + usec_t n, next; + int r; + + assert(m); + assert(s == m->wall_message_timeout_source); + + n = now(CLOCK_REALTIME); + + r = warn_wall(m, n); + if (r == 0) + return 0; + + next = when_wall(n, m->scheduled_shutdown_timeout); + if (next > 0) { + r = sd_event_source_set_time(s, n + next); + if (r < 0) + return log_error_errno(r, "sd_event_source_set_time() failed. %m"); + + r = sd_event_source_set_enabled(s, SD_EVENT_ONESHOT); + if (r < 0) + return log_error_errno(r, "sd_event_source_set_enabled() failed. %m"); + } + + return 0; +} + +int manager_setup_wall_message_timer(Manager *m) { + + usec_t n, elapse; + int r; + + assert(m); + + n = now(CLOCK_REALTIME); + elapse = m->scheduled_shutdown_timeout; + + /* wall message handling */ + + if (isempty(m->scheduled_shutdown_type)) { + warn_wall(m, n); + return 0; + } + + if (elapse < n) + return 0; + + /* Warn immediately if less than 15 minutes are left */ + if (elapse - n < 15 * USEC_PER_MINUTE) { + r = warn_wall(m, n); + if (r == 0) + return 0; + } + + elapse = when_wall(n, elapse); + if (elapse == 0) + return 0; + + if (m->wall_message_timeout_source) { + r = sd_event_source_set_time(m->wall_message_timeout_source, n + elapse); + if (r < 0) + return log_error_errno(r, "sd_event_source_set_time() failed. %m"); + + r = sd_event_source_set_enabled(m->wall_message_timeout_source, SD_EVENT_ONESHOT); + if (r < 0) + return log_error_errno(r, "sd_event_source_set_enabled() failed. %m"); + } else { + r = sd_event_add_time(m->event, &m->wall_message_timeout_source, + CLOCK_REALTIME, n + elapse, 0, wall_message_timeout_handler, m); + if (r < 0) + return log_error_errno(r, "sd_event_add_time() failed. %m"); + } + + return 0; +} diff --git a/src/grp-login/systemd-logind/logind.conf.in b/src/grp-login/systemd-logind/logind.conf.in new file mode 100644 index 0000000000..32c0844cb6 --- /dev/null +++ b/src/grp-login/systemd-logind/logind.conf.in @@ -0,0 +1,37 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# Entries in this file show the compile time defaults. +# You can change settings by editing this file. +# Defaults can be restored by simply deleting this file. +# +# See logind.conf(5) for details. + +[Login] +#NAutoVTs=6 +#ReserveVT=6 +#KillUserProcesses=@KILL_USER_PROCESSES@ +#KillOnlyUsers= +#KillExcludeUsers=root +#InhibitDelayMaxSec=5 +#HandlePowerKey=poweroff +#HandleSuspendKey=suspend +#HandleHibernateKey=hibernate +#HandleLidSwitch=suspend +#HandleLidSwitchDocked=ignore +#PowerKeyIgnoreInhibited=no +#SuspendKeyIgnoreInhibited=no +#HibernateKeyIgnoreInhibited=no +#LidSwitchIgnoreInhibited=yes +#HoldoffTimeoutSec=30s +#IdleAction=ignore +#IdleActionSec=30min +#RuntimeDirectorySize=10% +#RemoveIPC=yes +#InhibitorsMax=8192 +#SessionsMax=8192 +#UserTasksMax=12288 diff --git a/src/grp-login/systemd-logind/logind.h b/src/grp-login/systemd-logind/logind.h new file mode 100644 index 0000000000..effc498118 --- /dev/null +++ b/src/grp-login/systemd-logind/logind.h @@ -0,0 +1,198 @@ +#pragma once + +/*** + 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 Lesser General Public License as published by + the Free Software Foundation; either version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include +#include +#include + +#include "basic/hashmap.h" +#include "basic/list.h" +#include "basic/set.h" + +typedef struct Manager Manager; + +#include "logind-action.h" +#include "logind-button.h" +#include "logind-device.h" +#include "logind-inhibit.h" + +struct Manager { + sd_event *event; + sd_bus *bus; + + Hashmap *devices; + Hashmap *seats; + Hashmap *sessions; + Hashmap *users; + Hashmap *inhibitors; + Hashmap *buttons; + + LIST_HEAD(Seat, seat_gc_queue); + LIST_HEAD(Session, session_gc_queue); + LIST_HEAD(User, user_gc_queue); + + struct udev *udev; + struct udev_monitor *udev_seat_monitor, *udev_device_monitor, *udev_vcsa_monitor, *udev_button_monitor; + + sd_event_source *console_active_event_source; + sd_event_source *udev_seat_event_source; + sd_event_source *udev_device_event_source; + sd_event_source *udev_vcsa_event_source; + sd_event_source *udev_button_event_source; + + int console_active_fd; + + unsigned n_autovts; + + unsigned reserve_vt; + int reserve_vt_fd; + + Seat *seat0; + + char **kill_only_users, **kill_exclude_users; + bool kill_user_processes; + + unsigned long session_counter; + unsigned long inhibit_counter; + + Hashmap *session_units; + Hashmap *user_units; + + usec_t inhibit_delay_max; + + /* If an action is currently being executed or is delayed, + * this is != 0 and encodes what is being done */ + InhibitWhat action_what; + + /* If a shutdown/suspend was delayed due to a inhibitor this + contains the unit name we are supposed to start after the + delay is over */ + const char *action_unit; + + /* If a shutdown/suspend is currently executed, then this is + * the job of it */ + char *action_job; + sd_event_source *inhibit_timeout_source; + + char *scheduled_shutdown_type; + usec_t scheduled_shutdown_timeout; + sd_event_source *scheduled_shutdown_timeout_source; + uid_t scheduled_shutdown_uid; + char *scheduled_shutdown_tty; + sd_event_source *nologin_timeout_source; + bool unlink_nologin; + + char *wall_message; + unsigned enable_wall_messages; + sd_event_source *wall_message_timeout_source; + + bool shutdown_dry_run; + + sd_event_source *idle_action_event_source; + usec_t idle_action_usec; + usec_t idle_action_not_before_usec; + HandleAction idle_action; + + HandleAction handle_power_key; + HandleAction handle_suspend_key; + HandleAction handle_hibernate_key; + HandleAction handle_lid_switch; + HandleAction handle_lid_switch_docked; + + bool power_key_ignore_inhibited; + bool suspend_key_ignore_inhibited; + bool hibernate_key_ignore_inhibited; + bool lid_switch_ignore_inhibited; + + bool remove_ipc; + + Hashmap *polkit_registry; + + usec_t holdoff_timeout_usec; + sd_event_source *lid_switch_ignore_event_source; + + size_t runtime_dir_size; + uint64_t user_tasks_max; + uint64_t sessions_max; + uint64_t inhibitors_max; +}; + +int manager_add_device(Manager *m, const char *sysfs, bool master, Device **_device); +int manager_add_button(Manager *m, const char *name, Button **_button); +int manager_add_seat(Manager *m, const char *id, Seat **_seat); +int manager_add_session(Manager *m, const char *id, Session **_session); +int manager_add_user(Manager *m, uid_t uid, gid_t gid, const char *name, User **_user); +int manager_add_user_by_name(Manager *m, const char *name, User **_user); +int manager_add_user_by_uid(Manager *m, uid_t uid, User **_user); +int manager_add_inhibitor(Manager *m, const char* id, Inhibitor **_inhibitor); + +int manager_process_seat_device(Manager *m, struct udev_device *d); +int manager_process_button_device(Manager *m, struct udev_device *d); + +int manager_spawn_autovt(Manager *m, unsigned int vtnr); + +bool manager_shall_kill(Manager *m, const char *user); + +int manager_get_idle_hint(Manager *m, dual_timestamp *t); + +int manager_get_user_by_pid(Manager *m, pid_t pid, User **user); +int manager_get_session_by_pid(Manager *m, pid_t pid, Session **session); + +bool manager_is_docked_or_external_displays(Manager *m); + +extern const sd_bus_vtable manager_vtable[]; + +int match_job_removed(sd_bus_message *message, void *userdata, sd_bus_error *error); +int match_unit_removed(sd_bus_message *message, void *userdata, sd_bus_error *error); +int match_properties_changed(sd_bus_message *message, void *userdata, sd_bus_error *error); +int match_reloading(sd_bus_message *message, void *userdata, sd_bus_error *error); +int match_name_owner_changed(sd_bus_message *message, void *userdata, sd_bus_error *error); + +int bus_manager_shutdown_or_sleep_now_or_later(Manager *m, const char *unit_name, InhibitWhat w, sd_bus_error *error); + +int manager_send_changed(Manager *manager, const char *property, ...) _sentinel_; + +int manager_start_slice(Manager *manager, const char *slice, const char *description, const char *after, const char *after2, uint64_t tasks_max, sd_bus_error *error, char **job); +int manager_start_scope(Manager *manager, const char *scope, pid_t pid, const char *slice, const char *description, const char *after, const char *after2, uint64_t tasks_max, sd_bus_error *error, char **job); +int manager_start_unit(Manager *manager, const char *unit, sd_bus_error *error, char **job); +int manager_stop_unit(Manager *manager, const char *unit, sd_bus_error *error, char **job); +int manager_abandon_scope(Manager *manager, const char *scope, sd_bus_error *error); +int manager_kill_unit(Manager *manager, const char *unit, KillWho who, int signo, sd_bus_error *error); +int manager_unit_is_active(Manager *manager, const char *unit); +int manager_job_is_active(Manager *manager, const char *path); + +/* gperf lookup function */ +const struct ConfigPerfItem* logind_gperf_lookup(const char *key, unsigned length); + +int manager_set_lid_switch_ignore(Manager *m, usec_t until); + +int config_parse_tmpfs_size(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); + +int manager_get_session_from_creds(Manager *m, sd_bus_message *message, const char *name, sd_bus_error *error, Session **ret); +int manager_get_user_from_creds(Manager *m, sd_bus_message *message, uid_t uid, sd_bus_error *error, User **ret); +int manager_get_seat_from_creds(Manager *m, sd_bus_message *message, const char *name, sd_bus_error *error, Seat **ret); + +int manager_setup_wall_message_timer(Manager *m); +bool logind_wall_tty_filter(const char *tty, void *userdata); + +int manager_dispatch_delayed(Manager *manager, bool timeout); diff --git a/src/grp-login/systemd-logind/org.freedesktop.login1.conf b/src/grp-login/systemd-logind/org.freedesktop.login1.conf new file mode 100644 index 0000000000..c89e40457e --- /dev/null +++ b/src/grp-login/systemd-logind/org.freedesktop.login1.conf @@ -0,0 +1,274 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/grp-login/systemd-logind/org.freedesktop.login1.policy.in b/src/grp-login/systemd-logind/org.freedesktop.login1.policy.in new file mode 100644 index 0000000000..1fa6441629 --- /dev/null +++ b/src/grp-login/systemd-logind/org.freedesktop.login1.policy.in @@ -0,0 +1,323 @@ + + + + + + + + The systemd Project + http://www.freedesktop.org/wiki/Software/systemd + + + <_description>Allow applications to inhibit system shutdown + <_message>Authentication is required for an application to inhibit system shutdown. + + no + yes + yes + + org.freedesktop.login1.inhibit-delay-shutdown org.freedesktop.login1.inhibit-block-sleep org.freedesktop.login1.inhibit-delay-sleep org.freedesktop.login1.inhibit-block-idle + + + + <_description>Allow applications to delay system shutdown + <_message>Authentication is required for an application to delay system shutdown. + + yes + yes + yes + + org.freedesktop.login1.inhibit-delay-sleep + + + + <_description>Allow applications to inhibit system sleep + <_message>Authentication is required for an application to inhibit system sleep. + + no + yes + yes + + org.freedesktop.login1.inhibit-delay-sleep org.freedesktop.login1.inhibit-block-idle + + + + <_description>Allow applications to delay system sleep + <_message>Authentication is required for an application to delay system sleep. + + yes + yes + yes + + + + + <_description>Allow applications to inhibit automatic system suspend + <_message>Authentication is required for an application to inhibit automatic system suspend. + + yes + yes + yes + + + + + <_description>Allow applications to inhibit system handling of the power key + <_message>Authentication is required for an application to inhibit system handling of the power key. + + no + yes + yes + + org.freedesktop.login1.inhibit-handle-suspend-key org.freedesktop.login1.inhibit-handle-hibernate-key org.freedesktop.login1.inhibit-handle-lid-switch + + + + <_description>Allow applications to inhibit system handling of the suspend key + <_message>Authentication is required for an application to inhibit system handling of the suspend key. + + no + yes + yes + + org.freedesktop.login1.inhibit-handle-hibernate-key org.freedesktop.login1.inhibit-handle-lid-switch + + + + <_description>Allow applications to inhibit system handling of the hibernate key + <_message>Authentication is required for an application to inhibit system handling of the hibernate key. + + no + yes + yes + + + + + <_description>Allow applications to inhibit system handling of the lid switch + <_message>Authentication is required for an application to inhibit system handling of the lid switch. + + no + yes + yes + + + + + <_description>Allow non-logged-in user to run programs + <_message>Explicit request is required to run programs as a non-logged-in user. + + yes + + + + + <_description>Allow non-logged-in users to run programs + <_message>Authentication is required to run programs as a non-logged-in user. + + auth_admin_keep + auth_admin_keep + auth_admin_keep + + + + + <_description>Allow attaching devices to seats + <_message>Authentication is required for attaching a device to a seat. + + auth_admin_keep + auth_admin_keep + auth_admin_keep + + org.freedesktop.login1.flush-devices + + + + <_description>Flush device to seat attachments + <_message>Authentication is required for resetting how devices are attached to seats. + + auth_admin_keep + auth_admin_keep + auth_admin_keep + + + + + <_description>Power off the system + <_message>Authentication is required for powering off the system. + + auth_admin_keep + auth_admin_keep + yes + + org.freedesktop.login1.set-wall-message + + + + <_description>Power off the system while other users are logged in + <_message>Authentication is required for powering off the system while other users are logged in. + + auth_admin_keep + auth_admin_keep + yes + + org.freedesktop.login1.power-off + + + + <_description>Power off the system while an application asked to inhibit it + <_message>Authentication is required for powering off the system while an application asked to inhibit it. + + auth_admin_keep + auth_admin_keep + auth_admin_keep + + org.freedesktop.login1.power-off + + + + <_description>Reboot the system + <_message>Authentication is required for rebooting the system. + + auth_admin_keep + auth_admin_keep + yes + + org.freedesktop.login1.set-wall-message + + + + <_description>Reboot the system while other users are logged in + <_message>Authentication is required for rebooting the system while other users are logged in. + + auth_admin_keep + auth_admin_keep + yes + + org.freedesktop.login1.reboot + + + + <_description>Reboot the system while an application asked to inhibit it + <_message>Authentication is required for rebooting the system while an application asked to inhibit it. + + auth_admin_keep + auth_admin_keep + auth_admin_keep + + org.freedesktop.login1.reboot + + + + <_description>Suspend the system + <_message>Authentication is required for suspending the system. + + auth_admin_keep + auth_admin_keep + yes + + + + + <_description>Suspend the system while other users are logged in + <_message>Authentication is required for suspending the system while other users are logged in. + + auth_admin_keep + auth_admin_keep + yes + + org.freedesktop.login1.suspend + + + + <_description>Suspend the system while an application asked to inhibit it + <_message>Authentication is required for suspending the system while an application asked to inhibit it. + + auth_admin_keep + auth_admin_keep + auth_admin_keep + + org.freedesktop.login1.suspend + + + + <_description>Hibernate the system + <_message>Authentication is required for hibernating the system. + + auth_admin_keep + auth_admin_keep + yes + + + + + <_description>Hibernate the system while other users are logged in + <_message>Authentication is required for hibernating the system while other users are logged in. + + auth_admin_keep + auth_admin_keep + yes + + org.freedesktop.login1.hibernate + + + + <_description>Hibernate the system while an application asked to inhibit it + <_message>Authentication is required for hibernating the system while an application asked to inhibit it. + + auth_admin_keep + auth_admin_keep + auth_admin_keep + + org.freedesktop.login1.hibernate + + + + <_description>Manage active sessions, users and seats + <_message>Authentication is required for managing active sessions, users and seats. + + auth_admin_keep + auth_admin_keep + auth_admin_keep + + + + + <_description>Lock or unlock active sessions + <_message>Authentication is required to lock or unlock active sessions. + + auth_admin_keep + auth_admin_keep + auth_admin_keep + + + + + <_description>Allow indication to the firmware to boot to setup interface + <_message>Authentication is required to indicate to the firmware to boot to setup interface. + + auth_admin_keep + auth_admin_keep + auth_admin_keep + + + + + <_description>Set a wall message + <_message>Authentication is required to set a wall message + + auth_admin_keep + auth_admin_keep + auth_admin_keep + + + + diff --git a/src/grp-login/systemd-logind/org.freedesktop.login1.service b/src/grp-login/systemd-logind/org.freedesktop.login1.service new file mode 100644 index 0000000000..762dae2bb3 --- /dev/null +++ b/src/grp-login/systemd-logind/org.freedesktop.login1.service @@ -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 Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[D-BUS Service] +Name=org.freedesktop.login1 +Exec=/bin/false +User=root +SystemdService=dbus-org.freedesktop.login1.service diff --git a/src/grp-login/systemd-logind/systemd-logind.service.in b/src/grp-login/systemd-logind/systemd-logind.service.in new file mode 100644 index 0000000000..ff049134ee --- /dev/null +++ b/src/grp-login/systemd-logind/systemd-logind.service.in @@ -0,0 +1,31 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Login Service +Documentation=man:systemd-logind.service(8) man:logind.conf(5) +Documentation=http://www.freedesktop.org/wiki/Software/systemd/logind +Documentation=http://www.freedesktop.org/wiki/Software/systemd/multiseat +Wants=user.slice +After=nss-user-lookup.target user.slice + +# Ask for the dbus socket. If running over kdbus, the socket will +# not be actually used. +Wants=dbus.socket +After=dbus.socket + +[Service] +ExecStart=@rootlibexecdir@/systemd-logind +Restart=always +RestartSec=0 +BusName=org.freedesktop.login1 +CapabilityBoundingSet=CAP_SYS_ADMIN CAP_MAC_ADMIN CAP_AUDIT_CONTROL CAP_CHOWN CAP_KILL CAP_DAC_READ_SEARCH CAP_DAC_OVERRIDE CAP_FOWNER CAP_SYS_TTY_CONFIG +WatchdogSec=3min + +# 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/src/grp-login/systemd-logind/systemd-user.pam.m4 b/src/grp-login/systemd-logind/systemd-user.pam.m4 new file mode 100644 index 0000000000..f188a8e548 --- /dev/null +++ b/src/grp-login/systemd-logind/systemd-user.pam.m4 @@ -0,0 +1,12 @@ +# This file is part of systemd. +# +# Used by systemd --user instances. + +account include system-auth + +m4_ifdef(`HAVE_SELINUX', +session required pam_selinux.so close +session required pam_selinux.so nottys open +)m4_dnl +session required pam_loginuid.so +session include system-auth diff --git a/src/grp-login/systemd-logind/user.slice b/src/grp-login/systemd-logind/user.slice new file mode 100644 index 0000000000..9fa6284c12 --- /dev/null +++ b/src/grp-login/systemd-logind/user.slice @@ -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 Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=User and Session Slice +Documentation=man:systemd.special(7) +Before=slices.target diff --git a/src/grp-login/systemd-user.m4 b/src/grp-login/systemd-user.m4 deleted file mode 100644 index f188a8e548..0000000000 --- a/src/grp-login/systemd-user.m4 +++ /dev/null @@ -1,12 +0,0 @@ -# This file is part of systemd. -# -# Used by systemd --user instances. - -account include system-auth - -m4_ifdef(`HAVE_SELINUX', -session required pam_selinux.so close -session required pam_selinux.so nottys open -)m4_dnl -session required pam_loginuid.so -session include system-auth diff --git a/src/grp-machine/grp-import/systemd-importd/systemd-importd.service.in b/src/grp-machine/grp-import/systemd-importd/systemd-importd.service.in new file mode 100644 index 0000000000..b74ad72cdc --- /dev/null +++ b/src/grp-machine/grp-import/systemd-importd/systemd-importd.service.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 Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Virtual Machine and Container Download Service +Documentation=man:systemd-importd.service(8) +Documentation=http://www.freedesktop.org/wiki/Software/systemd/importd + +[Service] +ExecStart=@rootlibexecdir@/systemd-importd +BusName=org.freedesktop.import1 +CapabilityBoundingSet=CAP_CHOWN CAP_FOWNER CAP_FSETID CAP_MKNOD CAP_SETFCAP CAP_SYS_ADMIN CAP_SETPCAP CAP_DAC_OVERRIDE +NoNewPrivileges=yes +WatchdogSec=3min +KillMode=mixed diff --git a/src/grp-machine/systemd-machined/machine.slice b/src/grp-machine/systemd-machined/machine.slice new file mode 100644 index 0000000000..3d40dfd73b --- /dev/null +++ b/src/grp-machine/systemd-machined/machine.slice @@ -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 Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Virtual Machine and Container Slice +Documentation=man:systemd.special(7) +Before=slices.target diff --git a/src/grp-machine/systemd-machined/systemd-machined.service.in b/src/grp-machine/systemd-machined/systemd-machined.service.in new file mode 100644 index 0000000000..685baab21d --- /dev/null +++ b/src/grp-machine/systemd-machined/systemd-machined.service.in @@ -0,0 +1,23 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Virtual Machine and Container Registration Service +Documentation=man:systemd-machined.service(8) +Documentation=http://www.freedesktop.org/wiki/Software/systemd/machined +Wants=machine.slice +After=machine.slice + +[Service] +ExecStart=@rootlibexecdir@/systemd-machined +BusName=org.freedesktop.machine1 +CapabilityBoundingSet=CAP_KILL CAP_SYS_PTRACE CAP_SYS_ADMIN CAP_SETGID CAP_SYS_CHROOT CAP_DAC_READ_SEARCH CAP_DAC_OVERRIDE CAP_CHOWN CAP_FOWNER CAP_FSETID CAP_MKNOD +WatchdogSec=3min + +# Note that machined cannot be placed in a mount namespace, since it +# needs access to the host's mount namespace in order to implement the +# "machinectl bind" operation. diff --git a/src/grp-network/.gitignore b/src/grp-network/.gitignore deleted file mode 100644 index aca55206b7..0000000000 --- a/src/grp-network/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -/networkd-network-gperf.c -/networkd-netdev-gperf.c -/networkd-gperf.c diff --git a/src/grp-network/Makefile b/src/grp-network/Makefile index 7cbd9142d0..54f06b1822 100644 --- a/src/grp-network/Makefile +++ b/src/grp-network/Makefile @@ -23,7 +23,8 @@ include $(dir $(lastword $(MAKEFILE_LIST)))/../../config.mk include $(topsrcdir)/build-aux/Makefile.head.mk -dist_network_DATA = \ +# TODO +_dist_network_DATA = \ network/99-default.link \ network/80-container-host0.network \ network/80-container-ve.network \ @@ -65,33 +66,6 @@ tests += \ test-network \ test-network-tables -dist_systemunit_DATA += \ - units/systemd-networkd.socket - -nodist_systemunit_DATA += \ - units/systemd-networkd.service \ - units/systemd-networkd-wait-online.service - -dist_systemunit_DATA_busnames += \ - units/org.freedesktop.network1.busname - -dist_dbussystemservice_DATA += \ - src/network/org.freedesktop.network1.service - -dist_dbuspolicy_DATA += \ - src/network/org.freedesktop.network1.conf - -GENERAL_ALIASES += \ - $(systemunitdir)/systemd-networkd.socket $(pkgsysconfdir)/system/sockets.target.wants/systemd-networkd.socket \ - $(systemunitdir)/systemd-networkd.service $(pkgsysconfdir)/system/multi-user.target.wants/systemd-networkd.service \ - $(systemunitdir)/systemd-networkd-wait-online.service $(pkgsysconfdir)/system/network-online.target.wants/systemd-networkd-wait-online.service - -SYSTEM_UNIT_ALIASES += \ - systemd-networkd.service dbus-org.freedesktop.network1.service - -BUSNAMES_TARGET_WANTS += \ - org.freedesktop.network1.busname - endif # ENABLE_NETWORKD gperf_gperf_sources += \ diff --git a/src/grp-network/libnetworkd-core/.gitignore b/src/grp-network/libnetworkd-core/.gitignore new file mode 100644 index 0000000000..aca55206b7 --- /dev/null +++ b/src/grp-network/libnetworkd-core/.gitignore @@ -0,0 +1,3 @@ +/networkd-network-gperf.c +/networkd-netdev-gperf.c +/networkd-gperf.c diff --git a/src/grp-network/network/network.target b/src/grp-network/network/network.target new file mode 100644 index 0000000000..61ebdcadd0 --- /dev/null +++ b/src/grp-network/network/network.target @@ -0,0 +1,13 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Network +Documentation=man:systemd.special(7) +Documentation=http://www.freedesktop.org/wiki/Software/systemd/NetworkTarget +After=network-pre.target +RefuseManualStart=yes diff --git a/src/grp-network/org.freedesktop.network1.conf b/src/grp-network/org.freedesktop.network1.conf deleted file mode 100644 index 52dad33668..0000000000 --- a/src/grp-network/org.freedesktop.network1.conf +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/grp-network/org.freedesktop.network1.service b/src/grp-network/org.freedesktop.network1.service deleted file mode 100644 index bea885fe53..0000000000 --- a/src/grp-network/org.freedesktop.network1.service +++ /dev/null @@ -1,12 +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 Lesser General Public License as published by -# the Free Software Foundation; either version 2.1 of the License, or -# (at your option) any later version. - -[D-BUS Service] -Name=org.freedesktop.network1 -Exec=/bin/false -User=root -SystemdService=dbus-org.freedesktop.network1.service diff --git a/src/grp-network/systemd-networkd-wait-online/Makefile b/src/grp-network/systemd-networkd-wait-online/Makefile index 12aa13c48f..8d1870c862 100644 --- a/src/grp-network/systemd-networkd-wait-online/Makefile +++ b/src/grp-network/systemd-networkd-wait-online/Makefile @@ -41,4 +41,13 @@ systemd_networkd_wait_online_LDADD = \ libsystemd-network.la \ libshared.la +nodist_systemunit_DATA += \ + units/systemd-networkd-wait-online.service + +GENERAL_ALIASES += \ + $(systemunitdir)/systemd-networkd-wait-online.service $(pkgsysconfdir)/system/network-online.target.wants/systemd-networkd-wait-online.service + +EXTRA_DIST += \ + units/systemd-networkd-wait-online.service.in + include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-network/systemd-networkd-wait-online/systemd-networkd-wait-online.service.in b/src/grp-network/systemd-networkd-wait-online/systemd-networkd-wait-online.service.in new file mode 100644 index 0000000000..a9bad7aa8f --- /dev/null +++ b/src/grp-network/systemd-networkd-wait-online/systemd-networkd-wait-online.service.in @@ -0,0 +1,23 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Wait for Network to be Configured +Documentation=man:systemd-networkd-wait-online.service(8) +DefaultDependencies=no +Conflicts=shutdown.target +Requisite=systemd-networkd.service +After=systemd-networkd.service +Before=network-online.target + +[Service] +Type=oneshot +ExecStart=@rootlibexecdir@/systemd-networkd-wait-online +RemainAfterExit=yes + +[Install] +WantedBy=network-online.target diff --git a/src/grp-network/systemd-networkd/Makefile b/src/grp-network/systemd-networkd/Makefile index 8b7e44062b..764b51a2ba 100644 --- a/src/grp-network/systemd-networkd/Makefile +++ b/src/grp-network/systemd-networkd/Makefile @@ -37,4 +37,35 @@ systemd_networkd_LDADD += \ libfirewall.la endif # HAVE_LIBIPTC +dist_systemunit_DATA += \ + units/systemd-networkd.socket + +nodist_systemunit_DATA += \ + units/systemd-networkd.service \ + units/systemd-networkd-wait-online.service + +dist_systemunit_DATA_busnames += \ + units/org.freedesktop.network1.busname + +dist_dbussystemservice_DATA += \ + src/network/org.freedesktop.network1.service + +dist_dbuspolicy_DATA += \ + src/network/org.freedesktop.network1.conf + +GENERAL_ALIASES += \ + $(systemunitdir)/systemd-networkd.socket $(pkgsysconfdir)/system/sockets.target.wants/systemd-networkd.socket \ + $(systemunitdir)/systemd-networkd.service $(pkgsysconfdir)/system/multi-user.target.wants/systemd-networkd.service \ + $(systemunitdir)/systemd-networkd-wait-online.service $(pkgsysconfdir)/system/network-online.target.wants/systemd-networkd-wait-online.service + +SYSTEM_UNIT_ALIASES += \ + systemd-networkd.service dbus-org.freedesktop.network1.service + +BUSNAMES_TARGET_WANTS += \ + org.freedesktop.network1.busname + +EXTRA_DIST += \ + units/systemd-networkd.service.m4.in \ + test/networkd-test.py + include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-network/systemd-networkd/org.freedesktop.network1.conf b/src/grp-network/systemd-networkd/org.freedesktop.network1.conf new file mode 100644 index 0000000000..52dad33668 --- /dev/null +++ b/src/grp-network/systemd-networkd/org.freedesktop.network1.conf @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/grp-network/systemd-networkd/org.freedesktop.network1.service b/src/grp-network/systemd-networkd/org.freedesktop.network1.service new file mode 100644 index 0000000000..bea885fe53 --- /dev/null +++ b/src/grp-network/systemd-networkd/org.freedesktop.network1.service @@ -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 Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[D-BUS Service] +Name=org.freedesktop.network1 +Exec=/bin/false +User=root +SystemdService=dbus-org.freedesktop.network1.service diff --git a/src/grp-network/systemd-networkd/systemd-networkd.service.m4.in b/src/grp-network/systemd-networkd/systemd-networkd.service.m4.in new file mode 100644 index 0000000000..27d4d58962 --- /dev/null +++ b/src/grp-network/systemd-networkd/systemd-networkd.service.m4.in @@ -0,0 +1,37 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Network Service +Documentation=man:systemd-networkd.service(8) +ConditionCapability=CAP_NET_ADMIN +DefaultDependencies=no +# dbus.service can be dropped once on kdbus, and systemd-udevd.service can be +# dropped once tuntap is moved to netlink +After=systemd-udevd.service dbus.service network-pre.target systemd-sysusers.service systemd-sysctl.service +Before=network.target multi-user.target shutdown.target +Conflicts=shutdown.target +Wants=network.target + +# On kdbus systems we pull in the busname explicitly, because it +# carries policy that allows the daemon to acquire its name. +Wants=org.freedesktop.network1.busname +After=org.freedesktop.network1.busname + +[Service] +Type=notify +Restart=on-failure +RestartSec=0 +ExecStart=@rootlibexecdir@/systemd-networkd +CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_NET_BROADCAST CAP_NET_RAW CAP_SETUID CAP_SETGID CAP_SETPCAP CAP_CHOWN CAP_DAC_OVERRIDE CAP_FOWNER +ProtectSystem=full +ProtectHome=yes +WatchdogSec=3min + +[Install] +WantedBy=multi-user.target +Also=systemd-networkd.socket diff --git a/src/grp-network/systemd-networkd/systemd-networkd.socket b/src/grp-network/systemd-networkd/systemd-networkd.socket new file mode 100644 index 0000000000..9e4e9dd338 --- /dev/null +++ b/src/grp-network/systemd-networkd/systemd-networkd.socket @@ -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 Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Network Service Netlink Socket +Documentation=man:systemd-networkd.service(8) man:rtnetlink(7) +ConditionCapability=CAP_NET_ADMIN +DefaultDependencies=no +Before=sockets.target + +[Socket] +ReceiveBuffer=8M +ListenNetlink=route 1361 +PassCredentials=yes + +[Install] +WantedBy=sockets.target diff --git a/src/grp-network/systemd-networkd/systemd-networkd.tmpfiles b/src/grp-network/systemd-networkd/systemd-networkd.tmpfiles new file mode 100644 index 0000000000..24197555ee --- /dev/null +++ b/src/grp-network/systemd-networkd/systemd-networkd.tmpfiles @@ -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 Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +# See tmpfiles.d(5) for details + +d /run/systemd/netif 0755 systemd-network systemd-network - +d /run/systemd/netif/links 0755 systemd-network systemd-network - +d /run/systemd/netif/leases 0755 systemd-network systemd-network - diff --git a/src/grp-resolve/systemd-resolved/systemd-resolved.service.m4.in b/src/grp-resolve/systemd-resolved/systemd-resolved.service.m4.in new file mode 100644 index 0000000000..8e1c1dea79 --- /dev/null +++ b/src/grp-resolve/systemd-resolved/systemd-resolved.service.m4.in @@ -0,0 +1,32 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Network Name Resolution +Documentation=man:systemd-resolved.service(8) +Documentation=http://www.freedesktop.org/wiki/Software/systemd/resolved +Documentation=http://www.freedesktop.org/wiki/Software/systemd/writing-network-configuration-managers +Documentation=http://www.freedesktop.org/wiki/Software/systemd/writing-resolver-clients +After=systemd-networkd.service network.target + +# On kdbus systems we pull in the busname explicitly, because it +# carries policy that allows the daemon to acquire its name. +Wants=org.freedesktop.resolve1.busname +After=org.freedesktop.resolve1.busname + +[Service] +Type=notify +Restart=always +RestartSec=0 +ExecStart=@rootlibexecdir@/systemd-resolved +CapabilityBoundingSet=CAP_SETUID CAP_SETGID CAP_SETPCAP CAP_CHOWN CAP_DAC_OVERRIDE CAP_FOWNER +ProtectSystem=full +ProtectHome=yes +WatchdogSec=3min + +[Install] +WantedBy=multi-user.target diff --git a/src/grp-resolve/systemd-resolved/systemd-resolved.tmpfiles b/src/grp-resolve/systemd-resolved/systemd-resolved.tmpfiles new file mode 100644 index 0000000000..3160f5cf7e --- /dev/null +++ b/src/grp-resolve/systemd-resolved/systemd-resolved.tmpfiles @@ -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 Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +# See tmpfiles.d(5) for details + +L! /etc/resolv.conf - - - - ../run/systemd/resolve/resolv.conf diff --git a/src/grp-system/systemd/Makefile b/src/grp-system/systemd/Makefile index 4637b39de6..dc2eaba720 100644 --- a/src/grp-system/systemd/Makefile +++ b/src/grp-system/systemd/Makefile @@ -35,6 +35,10 @@ systemd_CFLAGS = \ systemd_LDADD = \ libcore.la +dist_tmpfiles_DATA = \ + tmpfiles.d/systemd.conf \ + systemd-tmp.conf + dist_pkgsysconf_DATA += \ src/core/system.conf \ src/core/user.conf @@ -68,8 +72,6 @@ dist_systemunit_DATA_busnames += \ BUSNAMES_TARGET_WANTS += \ org.freedesktop.systemd1.busname -sd.sed_files += $(notdir $(patsubst %.in,%,$(filter %.in,$(EXTRA_DIST)))) - sd.CPPFLAGS += -DPKGSYSCONFDIR=\"$(pkgsysconfdir)\" sd.CPPFLAGS += -DSYSTEMD_BINARY_PATH=\"$(rootlibexecdir)/systemd\" diff --git a/src/grp-system/systemd/systemd-tmp.tmpfiles b/src/grp-system/systemd/systemd-tmp.tmpfiles new file mode 100644 index 0000000000..52f6743d56 --- /dev/null +++ b/src/grp-system/systemd/systemd-tmp.tmpfiles @@ -0,0 +1,16 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +# See tmpfiles.d(5) for details + +# Clear tmp directories separately, to make them easier to override + +# Exclude namespace mountpoints created with PrivateTmp=yes +x /tmp/systemd-private-%b-* +X /tmp/systemd-private-%b-*/tmp +x /var/tmp/systemd-private-%b-* +X /var/tmp/systemd-private-%b-*/tmp diff --git a/src/grp-system/systemd/systemd.tmpfiles b/src/grp-system/systemd/systemd.tmpfiles new file mode 100644 index 0000000000..00951c92c9 --- /dev/null +++ b/src/grp-system/systemd/systemd.tmpfiles @@ -0,0 +1,20 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +# See tmpfiles.d(5) for details + +d /run/user 0755 root root - +F! /run/utmp 0664 root utmp - + +d /run/systemd/ask-password 0755 root root - +d /run/systemd/seats 0755 root root - +d /run/systemd/sessions 0755 root root - +d /run/systemd/users 0755 root root - +d /run/systemd/machines 0755 root root - +d /run/systemd/shutdown 0755 root root - + +d /var/lib/systemd 0755 root root - diff --git a/src/grp-timedate/systemd-timedated/systemd-timedated.service.in b/src/grp-timedate/systemd-timedated/systemd-timedated.service.in new file mode 100644 index 0000000000..0c9599db20 --- /dev/null +++ b/src/grp-timedate/systemd-timedated/systemd-timedated.service.in @@ -0,0 +1,20 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Time & Date Service +Documentation=man:systemd-timedated.service(8) man:localtime(5) +Documentation=http://www.freedesktop.org/wiki/Software/systemd/timedated + +[Service] +ExecStart=@rootlibexecdir@/systemd-timedated +BusName=org.freedesktop.timedate1 +CapabilityBoundingSet=CAP_SYS_TIME +WatchdogSec=3min +PrivateTmp=yes +ProtectSystem=yes +ProtectHome=yes diff --git a/src/grp-udev/50-udev-default.rules b/src/grp-udev/50-udev-default.rules new file mode 100644 index 0000000000..e9eeb8518e --- /dev/null +++ b/src/grp-udev/50-udev-default.rules @@ -0,0 +1,77 @@ +# do not edit this file, it will be overwritten on update + +# run a command on remove events +ACTION=="remove", ENV{REMOVE_CMD}!="", RUN+="$env{REMOVE_CMD}" +ACTION=="remove", GOTO="default_end" + +SUBSYSTEM=="virtio-ports", KERNEL=="vport*", ATTR{name}=="?*", SYMLINK+="virtio-ports/$attr{name}" + +# select "system RTC" or just use the first one +SUBSYSTEM=="rtc", ATTR{hctosys}=="1", SYMLINK+="rtc" +SUBSYSTEM=="rtc", KERNEL=="rtc0", SYMLINK+="rtc", OPTIONS+="link_priority=-100" + +SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", IMPORT{builtin}="usb_id", IMPORT{builtin}="hwdb --subsystem=usb" +SUBSYSTEM=="input", ENV{ID_INPUT}=="", IMPORT{builtin}="input_id" +ENV{MODALIAS}!="", IMPORT{builtin}="hwdb --subsystem=$env{SUBSYSTEM}" + +ACTION!="add", GOTO="default_end" + +SUBSYSTEM=="tty", KERNEL=="ptmx", GROUP="tty", MODE="0666" +SUBSYSTEM=="tty", KERNEL=="tty", GROUP="tty", MODE="0666" +SUBSYSTEM=="tty", KERNEL=="tty[0-9]*", GROUP="tty", MODE="0620" +SUBSYSTEM=="tty", KERNEL=="sclp_line[0-9]*", GROUP="tty", MODE="0620" +SUBSYSTEM=="tty", KERNEL=="ttysclp[0-9]*", GROUP="tty", MODE="0620" +SUBSYSTEM=="tty", KERNEL=="3270/tty[0-9]*", GROUP="tty", MODE="0620" +SUBSYSTEM=="vc", KERNEL=="vcs*|vcsa*", GROUP="tty" +KERNEL=="tty[A-Z]*[0-9]|pppox[0-9]*|ircomm[0-9]*|noz[0-9]*|rfcomm[0-9]*", GROUP="dialout" + +SUBSYSTEM=="mem", KERNEL=="mem|kmem|port", GROUP="kmem", MODE="0640" + +SUBSYSTEM=="input", GROUP="input" +SUBSYSTEM=="input", KERNEL=="js[0-9]*", MODE="0664" + +SUBSYSTEM=="video4linux", GROUP="video" +SUBSYSTEM=="graphics", GROUP="video" +SUBSYSTEM=="drm", GROUP="video" +SUBSYSTEM=="dvb", GROUP="video" + +SUBSYSTEM=="sound", GROUP="audio", \ + OPTIONS+="static_node=snd/seq", OPTIONS+="static_node=snd/timer" + +SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", MODE="0664" + +SUBSYSTEM=="firewire", ATTR{units}=="*0x00a02d:0x00010*", GROUP="video" +SUBSYSTEM=="firewire", ATTR{units}=="*0x00b09d:0x00010*", GROUP="video" +SUBSYSTEM=="firewire", ATTR{units}=="*0x00a02d:0x010001*", GROUP="video" +SUBSYSTEM=="firewire", ATTR{units}=="*0x00a02d:0x014001*", GROUP="video" + +KERNEL=="parport[0-9]*", GROUP="lp" +SUBSYSTEM=="printer", KERNEL=="lp*", GROUP="lp" +SUBSYSTEM=="ppdev", GROUP="lp" +KERNEL=="lp[0-9]*", GROUP="lp" +KERNEL=="irlpt[0-9]*", GROUP="lp" +SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ENV{ID_USB_INTERFACES}=="*:0701??:*", GROUP="lp" + +SUBSYSTEM=="block", GROUP="disk" +SUBSYSTEM=="block", KERNEL=="sr[0-9]*", GROUP="cdrom" +SUBSYSTEM=="scsi_generic", SUBSYSTEMS=="scsi", ATTRS{type}=="4|5", GROUP="cdrom" +KERNEL=="sch[0-9]*", GROUP="cdrom" +KERNEL=="pktcdvd[0-9]*", GROUP="cdrom" +KERNEL=="pktcdvd", GROUP="cdrom" + +SUBSYSTEM=="scsi_generic|scsi_tape", SUBSYSTEMS=="scsi", ATTRS{type}=="1|8", GROUP="tape" +SUBSYSTEM=="scsi_generic", SUBSYSTEMS=="scsi", ATTRS{type}=="0", GROUP="disk" +KERNEL=="qft[0-9]*|nqft[0-9]*|zqft[0-9]*|nzqft[0-9]*|rawqft[0-9]*|nrawqft[0-9]*", GROUP="disk" +KERNEL=="loop-control", GROUP="disk", OPTIONS+="static_node=loop-control" +KERNEL=="btrfs-control", GROUP="disk" +KERNEL=="rawctl", GROUP="disk" +SUBSYSTEM=="raw", KERNEL=="raw[0-9]*", GROUP="disk" +SUBSYSTEM=="aoe", GROUP="disk", MODE="0220" +SUBSYSTEM=="aoe", KERNEL=="err", MODE="0440" + +KERNEL=="rfkill", MODE="0664" +KERNEL=="tun", MODE="0666", OPTIONS+="static_node=net/tun" + +KERNEL=="fuse", MODE="0666", OPTIONS+="static_node=fuse" + +LABEL="default_end" diff --git a/src/grp-udev/60-block.rules b/src/grp-udev/60-block.rules new file mode 100644 index 0000000000..c74caca49f --- /dev/null +++ b/src/grp-udev/60-block.rules @@ -0,0 +1,11 @@ +# do not edit this file, it will be overwritten on update + +# enable in-kernel media-presence polling +ACTION=="add", SUBSYSTEM=="module", KERNEL=="block", ATTR{parameters/events_dfl_poll_msecs}=="0", \ + ATTR{parameters/events_dfl_poll_msecs}="2000" + +# forward scsi device event to corresponding block device +ACTION=="change", SUBSYSTEM=="scsi", ENV{DEVTYPE}=="scsi_device", TEST=="block", ATTR{block/*/uevent}="change" + +# watch metadata changes, caused by tools closing the device node which was opened for writing +ACTION!="remove", SUBSYSTEM=="block", KERNEL=="loop*|nvme*|sd*|vd*|xvd*", OPTIONS+="watch" diff --git a/src/grp-udev/60-drm.rules b/src/grp-udev/60-drm.rules new file mode 100644 index 0000000000..1ed3e445f2 --- /dev/null +++ b/src/grp-udev/60-drm.rules @@ -0,0 +1,3 @@ +# do not edit this file, it will be overwritten on update + +ACTION!="remove", SUBSYSTEM=="drm", SUBSYSTEMS=="pci|usb|platform", IMPORT{builtin}="path_id" diff --git a/src/grp-udev/60-evdev.rules b/src/grp-udev/60-evdev.rules new file mode 100644 index 0000000000..ade7e7f646 --- /dev/null +++ b/src/grp-udev/60-evdev.rules @@ -0,0 +1,19 @@ +# do not edit this file, it will be overwritten on update + +ACTION=="remove", GOTO="evdev_end" +KERNEL!="event*", GOTO="evdev_end" + +# skip later rules when we find something for this input device +IMPORT{builtin}="hwdb --subsystem=input --lookup-prefix=evdev:", \ + RUN{builtin}+="keyboard", GOTO="evdev_end" + +# AT keyboard matching by the machine's DMI data +ENV{ID_INPUT_KEY}=="?*", DRIVERS=="atkbd", \ + IMPORT{builtin}="hwdb 'evdev:atkbd:$attr{[dmi/id]modalias}'", \ + RUN{builtin}+="keyboard", GOTO="evdev_end" + +# device matching the input device name and the machine's DMI data +KERNELS=="input*", IMPORT{builtin}="hwdb 'evdev:name:$attr{name}:$attr{[dmi/id]modalias}'", \ + RUN{builtin}+="keyboard", GOTO="evdev_end" + +LABEL="evdev_end" diff --git a/src/grp-udev/60-persistent-alsa.rules b/src/grp-udev/60-persistent-alsa.rules new file mode 100644 index 0000000000..8154e2dbb5 --- /dev/null +++ b/src/grp-udev/60-persistent-alsa.rules @@ -0,0 +1,14 @@ +# do not edit this file, it will be overwritten on update + +ACTION=="remove", GOTO="persistent_alsa_end" +SUBSYSTEM!="sound", GOTO="persistent_alsa_end" +KERNEL!="controlC[0-9]*", GOTO="persistent_alsa_end" + +SUBSYSTEMS=="usb", ENV{ID_MODEL}=="", IMPORT{builtin}="usb_id" +ENV{ID_SERIAL}=="?*", ENV{ID_USB_INTERFACE_NUM}=="?*", SYMLINK+="snd/by-id/$env{ID_BUS}-$env{ID_SERIAL}-$env{ID_USB_INTERFACE_NUM}" +ENV{ID_SERIAL}=="?*", ENV{ID_USB_INTERFACE_NUM}=="", SYMLINK+="snd/by-id/$env{ID_BUS}-$env{ID_SERIAL}" + +IMPORT{builtin}="path_id" +ENV{ID_PATH}=="?*", SYMLINK+="snd/by-path/$env{ID_PATH}" + +LABEL="persistent_alsa_end" diff --git a/src/grp-udev/60-persistent-input.rules b/src/grp-udev/60-persistent-input.rules new file mode 100644 index 0000000000..0e33e68384 --- /dev/null +++ b/src/grp-udev/60-persistent-input.rules @@ -0,0 +1,38 @@ +# do not edit this file, it will be overwritten on update + +ACTION=="remove", GOTO="persistent_input_end" +SUBSYSTEM!="input", GOTO="persistent_input_end" +SUBSYSTEMS=="bluetooth", GOTO="persistent_input_end" + +SUBSYSTEMS=="usb", ENV{ID_BUS}=="", IMPORT{builtin}="usb_id" + +# determine class name for persistent symlinks +ENV{ID_INPUT_KEYBOARD}=="?*", ENV{.INPUT_CLASS}="kbd" +ENV{ID_INPUT_MOUSE}=="?*", ENV{.INPUT_CLASS}="mouse" +ENV{ID_INPUT_TOUCHPAD}=="?*", ENV{.INPUT_CLASS}="mouse" +ENV{ID_INPUT_TABLET}=="?*", ENV{.INPUT_CLASS}="mouse" +ENV{ID_INPUT_JOYSTICK}=="?*", ENV{.INPUT_CLASS}="joystick" +DRIVERS=="pcspkr", ENV{.INPUT_CLASS}="spkr" +ATTRS{name}=="*dvb*|*DVB*|* IR *", ENV{.INPUT_CLASS}="ir" + +# fill empty serial number +ENV{.INPUT_CLASS}=="?*", ENV{ID_SERIAL}=="", ENV{ID_SERIAL}="noserial" + +# by-id links +KERNEL=="mouse*|js*", ENV{ID_BUS}=="?*", ENV{.INPUT_CLASS}=="?*", ATTRS{bInterfaceNumber}=="|00", SYMLINK+="input/by-id/$env{ID_BUS}-$env{ID_SERIAL}-$env{.INPUT_CLASS}" +KERNEL=="mouse*|js*", ENV{ID_BUS}=="?*", ENV{.INPUT_CLASS}=="?*", ATTRS{bInterfaceNumber}=="?*", ATTRS{bInterfaceNumber}!="00", SYMLINK+="input/by-id/$env{ID_BUS}-$env{ID_SERIAL}-if$attr{bInterfaceNumber}-$env{.INPUT_CLASS}" +KERNEL=="event*", ENV{ID_BUS}=="?*", ENV{.INPUT_CLASS}=="?*", ATTRS{bInterfaceNumber}=="|00", SYMLINK+="input/by-id/$env{ID_BUS}-$env{ID_SERIAL}-event-$env{.INPUT_CLASS}" +KERNEL=="event*", ENV{ID_BUS}=="?*", ENV{.INPUT_CLASS}=="?*", ATTRS{bInterfaceNumber}=="?*", ATTRS{bInterfaceNumber}!="00", SYMLINK+="input/by-id/$env{ID_BUS}-$env{ID_SERIAL}-if$attr{bInterfaceNumber}-event-$env{.INPUT_CLASS}" +# allow empty class for USB devices, by appending the interface number +SUBSYSTEMS=="usb", ENV{ID_BUS}=="?*", KERNEL=="event*", ENV{.INPUT_CLASS}=="", ATTRS{bInterfaceNumber}=="?*", \ + SYMLINK+="input/by-id/$env{ID_BUS}-$env{ID_SERIAL}-event-if$attr{bInterfaceNumber}" + +# by-path +SUBSYSTEMS=="pci|usb|platform|acpi", IMPORT{builtin}="path_id" +ENV{ID_PATH}=="?*", KERNEL=="mouse*|js*", ENV{.INPUT_CLASS}=="?*", SYMLINK+="input/by-path/$env{ID_PATH}-$env{.INPUT_CLASS}" +ENV{ID_PATH}=="?*", KERNEL=="event*", ENV{.INPUT_CLASS}=="?*", SYMLINK+="input/by-path/$env{ID_PATH}-event-$env{.INPUT_CLASS}" +# allow empty class for platform and usb devices; platform supports only a single interface that way +SUBSYSTEMS=="usb|platform", ENV{ID_PATH}=="?*", KERNEL=="event*", ENV{.INPUT_CLASS}=="", \ + SYMLINK+="input/by-path/$env{ID_PATH}-event" + +LABEL="persistent_input_end" diff --git a/src/grp-udev/60-persistent-storage-tape.rules b/src/grp-udev/60-persistent-storage-tape.rules new file mode 100644 index 0000000000..f2eabd92a8 --- /dev/null +++ b/src/grp-udev/60-persistent-storage-tape.rules @@ -0,0 +1,25 @@ +# do not edit this file, it will be overwritten on update + +# persistent storage links: /dev/tape/{by-id,by-path} + +ACTION=="remove", GOTO="persistent_storage_tape_end" + +# type 8 devices are "Medium Changers" +SUBSYSTEM=="scsi_generic", SUBSYSTEMS=="scsi", ATTRS{type}=="8", IMPORT{program}="scsi_id --sg-version=3 --export --whitelisted -d $devnode", \ + SYMLINK+="tape/by-id/scsi-$env{ID_SERIAL}" + +SUBSYSTEM!="scsi_tape", GOTO="persistent_storage_tape_end" + +KERNEL=="st*[0-9]|nst*[0-9]", ATTRS{ieee1394_id}=="?*", ENV{ID_SERIAL}="$attr{ieee1394_id}", ENV{ID_BUS}="ieee1394" +KERNEL=="st*[0-9]|nst*[0-9]", ENV{ID_SERIAL}!="?*", SUBSYSTEMS=="usb", IMPORT{builtin}="usb_id" +KERNEL=="st*[0-9]|nst*[0-9]", ENV{ID_SERIAL}!="?*", SUBSYSTEMS=="scsi", KERNELS=="[0-9]*:*[0-9]", ENV{.BSG_DEV}="$root/bsg/$id" +KERNEL=="st*[0-9]|nst*[0-9]", ENV{ID_SERIAL}!="?*", IMPORT{program}="scsi_id --whitelisted --export --device=$env{.BSG_DEV}", ENV{ID_BUS}="scsi" +KERNEL=="st*[0-9]", ENV{ID_SERIAL}=="?*", SYMLINK+="tape/by-id/$env{ID_BUS}-$env{ID_SERIAL}" +KERNEL=="nst*[0-9]", ENV{ID_SERIAL}=="?*", SYMLINK+="tape/by-id/$env{ID_BUS}-$env{ID_SERIAL}-nst" + +# by-path (parent device path) +KERNEL=="st*[0-9]|nst*[0-9]", IMPORT{builtin}="path_id" +KERNEL=="st*[0-9]", ENV{ID_PATH}=="?*", SYMLINK+="tape/by-path/$env{ID_PATH}" +KERNEL=="nst*[0-9]", ENV{ID_PATH}=="?*", SYMLINK+="tape/by-path/$env{ID_PATH}-nst" + +LABEL="persistent_storage_tape_end" diff --git a/src/grp-udev/60-persistent-storage.rules b/src/grp-udev/60-persistent-storage.rules new file mode 100644 index 0000000000..408733915c --- /dev/null +++ b/src/grp-udev/60-persistent-storage.rules @@ -0,0 +1,87 @@ +# do not edit this file, it will be overwritten on update + +# persistent storage links: /dev/disk/{by-id,by-uuid,by-label,by-path} +# scheme based on "Linux persistent device names", 2004, Hannes Reinecke + +ACTION=="remove", GOTO="persistent_storage_end" + +SUBSYSTEM!="block", GOTO="persistent_storage_end" +KERNEL!="loop*|mmcblk*[0-9]|msblk*[0-9]|mspblk*[0-9]|nvme*|sd*|sr*|vd*|xvd*|bcache*|cciss*|dasd*|ubd*", GOTO="persistent_storage_end" + +# ignore partitions that span the entire disk +TEST=="whole_disk", GOTO="persistent_storage_end" + +# for partitions import parent information +ENV{DEVTYPE}=="partition", IMPORT{parent}="ID_*" + +# NVMe +KERNEL=="nvme*[0-9]n*[0-9]", ATTR{wwid}=="?*", SYMLINK+="disk/by-id/nvme-$attr{wwid}" +KERNEL=="nvme*[0-9]n*[0-9]p*[0-9]", ENV{DEVTYPE}=="partition", ATTRS{wwid}=="?*", SYMLINK+="disk/by-id/nvme-$attr{wwid}-part%n" + +# virtio-blk +KERNEL=="vd*[!0-9]", ATTRS{serial}=="?*", ENV{ID_SERIAL}="$attr{serial}", SYMLINK+="disk/by-id/virtio-$env{ID_SERIAL}" +KERNEL=="vd*[0-9]", ATTRS{serial}=="?*", ENV{ID_SERIAL}="$attr{serial}", SYMLINK+="disk/by-id/virtio-$env{ID_SERIAL}-part%n" + +# ATA +KERNEL=="sd*[!0-9]|sr*", ENV{ID_SERIAL}!="?*", SUBSYSTEMS=="scsi", ATTRS{vendor}=="ATA", IMPORT{program}="ata_id --export $devnode" + +# ATAPI devices (SPC-3 or later) +KERNEL=="sd*[!0-9]|sr*", ENV{ID_SERIAL}!="?*", SUBSYSTEMS=="scsi", ATTRS{type}=="5", ATTRS{scsi_level}=="[6-9]*", IMPORT{program}="ata_id --export $devnode" + +# Run ata_id on non-removable USB Mass Storage (SATA/PATA disks in enclosures) +KERNEL=="sd*[!0-9]|sr*", ENV{ID_SERIAL}!="?*", ATTR{removable}=="0", SUBSYSTEMS=="usb", IMPORT{program}="ata_id --export $devnode" + +# Fall back usb_id for USB devices +KERNEL=="sd*[!0-9]|sr*", ENV{ID_SERIAL}!="?*", SUBSYSTEMS=="usb", IMPORT{builtin}="usb_id" + +# SCSI devices +KERNEL=="sd*[!0-9]|sr*", ENV{ID_SERIAL}!="?*", IMPORT{program}="scsi_id --export --whitelisted -d $devnode", ENV{ID_BUS}="scsi" +KERNEL=="cciss*", ENV{DEVTYPE}=="disk", ENV{ID_SERIAL}!="?*", IMPORT{program}="scsi_id --export --whitelisted -d $devnode", ENV{ID_BUS}="cciss" +KERNEL=="sd*|sr*|cciss*", ENV{DEVTYPE}=="disk", ENV{ID_SERIAL}=="?*", SYMLINK+="disk/by-id/$env{ID_BUS}-$env{ID_SERIAL}" +KERNEL=="sd*|cciss*", ENV{DEVTYPE}=="partition", ENV{ID_SERIAL}=="?*", SYMLINK+="disk/by-id/$env{ID_BUS}-$env{ID_SERIAL}-part%n" + +# FireWire +KERNEL=="sd*[!0-9]|sr*", ATTRS{ieee1394_id}=="?*", SYMLINK+="disk/by-id/ieee1394-$attr{ieee1394_id}" +KERNEL=="sd*[0-9]", ATTRS{ieee1394_id}=="?*", SYMLINK+="disk/by-id/ieee1394-$attr{ieee1394_id}-part%n" + +# MMC +KERNEL=="mmcblk[0-9]", SUBSYSTEMS=="mmc", ATTRS{name}=="?*", ATTRS{serial}=="?*", \ + ENV{ID_NAME}="$attr{name}", ENV{ID_SERIAL}="$attr{serial}", SYMLINK+="disk/by-id/mmc-$env{ID_NAME}_$env{ID_SERIAL}" +KERNEL=="mmcblk[0-9]p[0-9]", ENV{ID_NAME}=="?*", ENV{ID_SERIAL}=="?*", SYMLINK+="disk/by-id/mmc-$env{ID_NAME}_$env{ID_SERIAL}-part%n" + +# Memstick +KERNEL=="msblk[0-9]|mspblk[0-9]", SUBSYSTEMS=="memstick", ATTRS{name}=="?*", ATTRS{serial}=="?*", \ + ENV{ID_NAME}="$attr{name}", ENV{ID_SERIAL}="$attr{serial}", SYMLINK+="disk/by-id/memstick-$env{ID_NAME}_$env{ID_SERIAL}" +KERNEL=="msblk[0-9]p[0-9]|mspblk[0-9]p[0-9]", ENV{ID_NAME}=="?*", ENV{ID_SERIAL}=="?*", SYMLINK+="disk/by-id/memstick-$env{ID_NAME}_$env{ID_SERIAL}-part%n" + +# by-path +ENV{DEVTYPE}=="disk", DEVPATH!="*/virtual/*", IMPORT{builtin}="path_id" +ENV{DEVTYPE}=="disk", ENV{ID_PATH}=="?*", SYMLINK+="disk/by-path/$env{ID_PATH}" +ENV{DEVTYPE}=="partition", ENV{ID_PATH}=="?*", SYMLINK+="disk/by-path/$env{ID_PATH}-part%n" + +# probe filesystem metadata of optical drives which have a media inserted +KERNEL=="sr*", ENV{DISK_EJECT_REQUEST}!="?*", ENV{ID_CDROM_MEDIA_TRACK_COUNT_DATA}=="?*", ENV{ID_CDROM_MEDIA_SESSION_LAST_OFFSET}=="?*", \ + IMPORT{builtin}="blkid --offset=$env{ID_CDROM_MEDIA_SESSION_LAST_OFFSET}" +# single-session CDs do not have ID_CDROM_MEDIA_SESSION_LAST_OFFSET +KERNEL=="sr*", ENV{DISK_EJECT_REQUEST}!="?*", ENV{ID_CDROM_MEDIA_TRACK_COUNT_DATA}=="?*", ENV{ID_CDROM_MEDIA_SESSION_LAST_OFFSET}=="", \ + IMPORT{builtin}="blkid --noraid" + +# probe filesystem metadata of disks +KERNEL!="sr*", IMPORT{builtin}="blkid" + +# by-label/by-uuid links (filesystem metadata) +ENV{ID_FS_USAGE}=="filesystem|other|crypto", ENV{ID_FS_UUID_ENC}=="?*", SYMLINK+="disk/by-uuid/$env{ID_FS_UUID_ENC}" +ENV{ID_FS_USAGE}=="filesystem|other", ENV{ID_FS_LABEL_ENC}=="?*", SYMLINK+="disk/by-label/$env{ID_FS_LABEL_ENC}" + +# by-id (World Wide Name) +ENV{DEVTYPE}=="disk", ENV{ID_WWN_WITH_EXTENSION}=="?*", SYMLINK+="disk/by-id/wwn-$env{ID_WWN_WITH_EXTENSION}" +ENV{DEVTYPE}=="partition", ENV{ID_WWN_WITH_EXTENSION}=="?*", SYMLINK+="disk/by-id/wwn-$env{ID_WWN_WITH_EXTENSION}-part%n" + +# by-partlabel/by-partuuid links (partition metadata) +ENV{ID_PART_ENTRY_UUID}=="?*", SYMLINK+="disk/by-partuuid/$env{ID_PART_ENTRY_UUID}" +ENV{ID_PART_ENTRY_SCHEME}=="gpt", ENV{ID_PART_ENTRY_NAME}=="?*", SYMLINK+="disk/by-partlabel/$env{ID_PART_ENTRY_NAME}" + +# add symlink to GPT root disk +ENV{ID_PART_ENTRY_SCHEME}=="gpt", ENV{ID_PART_GPT_AUTO_ROOT}=="1", SYMLINK+="gpt-auto-root" + +LABEL="persistent_storage_end" diff --git a/src/grp-udev/60-serial.rules b/src/grp-udev/60-serial.rules new file mode 100644 index 0000000000..f303e27fd5 --- /dev/null +++ b/src/grp-udev/60-serial.rules @@ -0,0 +1,26 @@ +# do not edit this file, it will be overwritten on update + +ACTION=="remove", GOTO="serial_end" +SUBSYSTEM!="tty", GOTO="serial_end" + +SUBSYSTEMS=="pci", ENV{ID_BUS}="pci", ENV{ID_VENDOR_ID}="$attr{vendor}", ENV{ID_MODEL_ID}="$attr{device}" +SUBSYSTEMS=="pci", IMPORT{builtin}="hwdb --subsystem=pci" +SUBSYSTEMS=="usb", IMPORT{builtin}="usb_id", IMPORT{builtin}="hwdb --subsystem=usb" + +# /dev/serial/by-path/, /dev/serial/by-id/ for USB devices +KERNEL!="ttyUSB[0-9]*|ttyACM[0-9]*", GOTO="serial_end" + +SUBSYSTEMS=="usb-serial", ENV{.ID_PORT}="$attr{port_number}" + +IMPORT{builtin}="path_id" +ENV{ID_PATH}=="?*", ENV{.ID_PORT}=="", SYMLINK+="serial/by-path/$env{ID_PATH}" +ENV{ID_PATH}=="?*", ENV{.ID_PORT}=="?*", SYMLINK+="serial/by-path/$env{ID_PATH}-port$env{.ID_PORT}" + +IMPORT{builtin}="usb_id" +ENV{ID_SERIAL}=="", GOTO="serial_end" +SUBSYSTEMS=="usb", ENV{ID_USB_INTERFACE_NUM}="$attr{bInterfaceNumber}" +ENV{ID_USB_INTERFACE_NUM}=="", GOTO="serial_end" +ENV{.ID_PORT}=="", SYMLINK+="serial/by-id/$env{ID_BUS}-$env{ID_SERIAL}-if$env{ID_USB_INTERFACE_NUM}" +ENV{.ID_PORT}=="?*", SYMLINK+="serial/by-id/$env{ID_BUS}-$env{ID_SERIAL}-if$env{ID_USB_INTERFACE_NUM}-port$env{.ID_PORT}" + +LABEL="serial_end" diff --git a/src/grp-udev/64-btrfs.rules b/src/grp-udev/64-btrfs.rules new file mode 100644 index 0000000000..fe0100131e --- /dev/null +++ b/src/grp-udev/64-btrfs.rules @@ -0,0 +1,13 @@ +# do not edit this file, it will be overwritten on update + +SUBSYSTEM!="block", GOTO="btrfs_end" +ACTION=="remove", GOTO="btrfs_end" +ENV{ID_FS_TYPE}!="btrfs", GOTO="btrfs_end" + +# let the kernel know about this btrfs filesystem, and check if it is complete +IMPORT{builtin}="btrfs ready $devnode" + +# mark the device as not ready to be used by the system +ENV{ID_BTRFS_READY}=="0", ENV{SYSTEMD_READY}="0" + +LABEL="btrfs_end" diff --git a/src/grp-udev/70-mouse.rules b/src/grp-udev/70-mouse.rules new file mode 100644 index 0000000000..3ea743aff9 --- /dev/null +++ b/src/grp-udev/70-mouse.rules @@ -0,0 +1,18 @@ +# do not edit this file, it will be overwritten on update + +ACTION=="remove", GOTO="mouse_end" +KERNEL!="event*", GOTO="mouse_end" +ENV{ID_INPUT_MOUSE}=="", GOTO="mouse_end" + +# mouse::vp:name::* +KERNELS=="input*", ENV{ID_BUS}=="usb", \ + IMPORT{builtin}="hwdb 'mouse:$env{ID_BUS}:v$attr{id/vendor}p$attr{id/product}:name:$attr{name}:'", \ + GOTO="mouse_end" +KERNELS=="input*", ENV{ID_BUS}=="bluetooth", \ + IMPORT{builtin}="hwdb 'mouse:$env{ID_BUS}:v$attr{id/vendor}p$attr{id/product}:name:$attr{name}:'", \ + GOTO="mouse_end" +DRIVERS=="psmouse", SUBSYSTEMS=="serio", \ + IMPORT{builtin}="hwdb 'mouse:ps2::name:$attr{device/name}:'", \ + GOTO="mouse_end" + +LABEL="mouse_end" diff --git a/src/grp-udev/75-net-description.rules b/src/grp-udev/75-net-description.rules new file mode 100644 index 0000000000..7e62f8b26b --- /dev/null +++ b/src/grp-udev/75-net-description.rules @@ -0,0 +1,14 @@ +# do not edit this file, it will be overwritten on update + +ACTION=="remove", GOTO="net_end" +SUBSYSTEM!="net", GOTO="net_end" + +IMPORT{builtin}="net_id" + +SUBSYSTEMS=="usb", IMPORT{builtin}="usb_id", IMPORT{builtin}="hwdb --subsystem=usb" +SUBSYSTEMS=="usb", GOTO="net_end" + +SUBSYSTEMS=="pci", ENV{ID_BUS}="pci", ENV{ID_VENDOR_ID}="$attr{vendor}", ENV{ID_MODEL_ID}="$attr{device}" +SUBSYSTEMS=="pci", IMPORT{builtin}="hwdb --subsystem=pci" + +LABEL="net_end" diff --git a/src/grp-udev/78-sound-card.rules b/src/grp-udev/78-sound-card.rules new file mode 100644 index 0000000000..04740e8b97 --- /dev/null +++ b/src/grp-udev/78-sound-card.rules @@ -0,0 +1,89 @@ +# do not edit this file, it will be overwritten on update + +SUBSYSTEM!="sound", GOTO="sound_end" + +ACTION=="add|change", KERNEL=="controlC*", ATTR{../uevent}="change" +ACTION!="change", GOTO="sound_end" + +# Ok, we probably need a little explanation here for what the two lines above +# are good for. +# +# The story goes like this: when ALSA registers a new sound card it emits a +# series of 'add' events to userspace, for the main card device and for all the +# child device nodes that belong to it. udev relays those to applications, +# however only maintains the order between father and child, but not between +# the siblings. The control device node creation can be used as synchronization +# point. All other devices that belong to a card are created in the kernel +# before it. However unfortunately due to the fact that siblings are forwarded +# out of order by udev this fact is lost to applications. +# +# OTOH before an application can open a device it needs to make sure that all +# its device nodes are completely created and set up. +# +# As a workaround for this issue we have added the udev rule above which will +# generate a 'change' event on the main card device from the 'add' event of the +# card's control device. Due to the ordering semantics of udev this event will +# only be relayed after all child devices have finished processing properly. +# When an application needs to listen for appearing devices it can hence look +# for 'change' events only, and ignore the actual 'add' events. +# +# When the application is initialized at the same time as a device is plugged +# in it may need to figure out if the 'change' event has already been triggered +# or not for a card. To find that out we store the flag environment variable +# SOUND_INITIALIZED on the device which simply tells us if the card 'change' +# event has already been processed. + +KERNEL!="card*", GOTO="sound_end" + +ENV{SOUND_INITIALIZED}="1" + +IMPORT{builtin}="hwdb" +SUBSYSTEMS=="usb", IMPORT{builtin}="usb_id" +SUBSYSTEMS=="usb", GOTO="skip_pci" + +SUBSYSTEMS=="firewire", ATTRS{guid}=="?*", \ + ENV{ID_BUS}="firewire", ENV{ID_SERIAL}="$attr{guid}", ENV{ID_SERIAL_SHORT}="$attr{guid}", \ + ENV{ID_VENDOR_ID}="$attr{vendor}", ENV{ID_MODEL_ID}="$attr{model}", \ + ENV{ID_VENDOR}="$attr{vendor_name}", ENV{ID_MODEL}="$attr{model_name}" +SUBSYSTEMS=="firewire", GOTO="skip_pci" + +SUBSYSTEMS=="pci", ENV{ID_BUS}="pci", ENV{ID_VENDOR_ID}="$attr{vendor}", ENV{ID_MODEL_ID}="$attr{device}" +LABEL="skip_pci" + +# Define ID_ID if ID_BUS and ID_SERIAL are set. This will work for both +# USB and firewire. +ENV{ID_SERIAL}=="?*", ENV{ID_USB_INTERFACE_NUM}=="?*", ENV{ID_ID}="$env{ID_BUS}-$env{ID_SERIAL}-$env{ID_USB_INTERFACE_NUM}" +ENV{ID_SERIAL}=="?*", ENV{ID_USB_INTERFACE_NUM}=="", ENV{ID_ID}="$env{ID_BUS}-$env{ID_SERIAL}" + +IMPORT{builtin}="path_id" + +# The values used here for $SOUND_FORM_FACTOR and $SOUND_CLASS should be kept +# in sync with those defined for PulseAudio's src/pulse/proplist.h +# PA_PROP_DEVICE_FORM_FACTOR, PA_PROP_DEVICE_CLASS properties. + +# If the first PCM device of this card has the pcm class 'modem', then the card is a modem +ATTR{pcmC%nD0p/pcm_class}=="modem", ENV{SOUND_CLASS}="modem", GOTO="sound_end" + +# Identify cards on the internal PCI bus as internal +SUBSYSTEMS=="pci", DEVPATH=="*/0000:00:??.?/sound/*", ENV{SOUND_FORM_FACTOR}="internal", GOTO="sound_end" + +# Devices that also support Image/Video interfaces are most likely webcams +SUBSYSTEMS=="usb", ENV{ID_USB_INTERFACES}=="*:0e????:*", ENV{SOUND_FORM_FACTOR}="webcam", GOTO="sound_end" + +# Matching on the model strings is a bit ugly, I admit +ENV{ID_MODEL}=="*[Ss]peaker*", ENV{SOUND_FORM_FACTOR}="speaker", GOTO="sound_end" +ENV{ID_MODEL_FROM_DATABASE}=="*[Ss]peaker*", ENV{SOUND_FORM_FACTOR}="speaker", GOTO="sound_end" + +ENV{ID_MODEL}=="*[Hh]eadphone*", ENV{SOUND_FORM_FACTOR}="headphone", GOTO="sound_end" +ENV{ID_MODEL_FROM_DATABASE}=="*[Hh]eadphone*", ENV{SOUND_FORM_FACTOR}="headphone", GOTO="sound_end" + +ENV{ID_MODEL}=="*[Hh]eadset*", ENV{SOUND_FORM_FACTOR}="headset", GOTO="sound_end" +ENV{ID_MODEL_FROM_DATABASE}=="*[Hh]eadset*", ENV{SOUND_FORM_FACTOR}="headset", GOTO="sound_end" + +ENV{ID_MODEL}=="*[Hh]andset*", ENV{SOUND_FORM_FACTOR}="handset", GOTO="sound_end" +ENV{ID_MODEL_FROM_DATABASE}=="*[Hh]andset*", ENV{SOUND_FORM_FACTOR}="handset", GOTO="sound_end" + +ENV{ID_MODEL}=="*[Mm]icrophone*", ENV{SOUND_FORM_FACTOR}="microphone", GOTO="sound_end" +ENV{ID_MODEL_FROM_DATABASE}=="*[Mm]icrophone*", ENV{SOUND_FORM_FACTOR}="microphone", GOTO="sound_end" + +LABEL="sound_end" diff --git a/src/grp-udev/80-drivers.rules b/src/grp-udev/80-drivers.rules new file mode 100644 index 0000000000..8551f47a4b --- /dev/null +++ b/src/grp-udev/80-drivers.rules @@ -0,0 +1,13 @@ +# do not edit this file, it will be overwritten on update + +ACTION=="remove", GOTO="drivers_end" + +ENV{MODALIAS}=="?*", RUN{builtin}+="kmod load $env{MODALIAS}" +SUBSYSTEM=="tifm", ENV{TIFM_CARD_TYPE}=="SD", RUN{builtin}+="kmod load tifm_sd" +SUBSYSTEM=="tifm", ENV{TIFM_CARD_TYPE}=="MS", RUN{builtin}+="kmod load tifm_ms" +SUBSYSTEM=="memstick", RUN{builtin}+="kmod load ms_block mspro_block" +SUBSYSTEM=="i2o", RUN{builtin}+="kmod load i2o_block" +SUBSYSTEM=="module", KERNEL=="parport_pc", RUN{builtin}+="kmod load ppdev" +KERNEL=="mtd*ro", ENV{MTD_FTL}=="smartmedia", RUN{builtin}+="kmod load sm_ftl" + +LABEL="drivers_end" diff --git a/src/grp-udev/80-net-setup-link.rules b/src/grp-udev/80-net-setup-link.rules new file mode 100644 index 0000000000..6e411a91f0 --- /dev/null +++ b/src/grp-udev/80-net-setup-link.rules @@ -0,0 +1,13 @@ +# do not edit this file, it will be overwritten on update + +SUBSYSTEM!="net", GOTO="net_setup_link_end" + +IMPORT{builtin}="path_id" + +ACTION!="add", GOTO="net_setup_link_end" + +IMPORT{builtin}="net_setup_link" + +NAME=="", ENV{ID_NET_NAME}!="", NAME="$env{ID_NET_NAME}" + +LABEL="net_setup_link_end" diff --git a/src/grp-udev/99-systemd.rules.in b/src/grp-udev/99-systemd.rules.in new file mode 100644 index 0000000000..fb4517606d --- /dev/null +++ b/src/grp-udev/99-systemd.rules.in @@ -0,0 +1,67 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +ACTION=="remove", GOTO="systemd_end" + +SUBSYSTEM=="tty", KERNEL=="tty[a-zA-Z]*|hvc*|xvc*|hvsi*|ttysclp*|sclp_line*|3270/tty[0-9]*", TAG+="systemd" +KERNEL=="vport*", TAG+="systemd" + +SUBSYSTEM=="block", TAG+="systemd" +SUBSYSTEM=="block", ACTION=="add", ENV{DM_UDEV_DISABLE_OTHER_RULES_FLAG}=="1", ENV{SYSTEMD_READY}="0" + +# Ignore encrypted devices with no identified superblock on it, since +# we are probably still calling mke2fs or mkswap on it. +SUBSYSTEM=="block", ENV{DM_UUID}=="CRYPT-*", ENV{ID_PART_TABLE_TYPE}=="", ENV{ID_FS_USAGE}=="", ENV{SYSTEMD_READY}="0" + +# Ignore raid devices that are not yet assembled and started +SUBSYSTEM=="block", ENV{DEVTYPE}=="disk", KERNEL=="md*", TEST!="md/array_state", ENV{SYSTEMD_READY}="0" +SUBSYSTEM=="block", ENV{DEVTYPE}=="disk", KERNEL=="md*", ATTR{md/array_state}=="|clear|inactive", ENV{SYSTEMD_READY}="0" + +# Ignore loop devices that don't have any file attached +SUBSYSTEM=="block", KERNEL=="loop[0-9]*", ENV{DEVTYPE}=="disk", TEST!="loop/backing_file", ENV{SYSTEMD_READY}="0" + +# Ignore nbd devices until the PID file exists (which signals a connected device) +SUBSYSTEM=="block", KERNEL=="nbd*", ENV{DEVTYPE}=="disk", TEST!="pid", ENV{SYSTEMD_READY}="0" + +# We need a hardware independent way to identify network devices. We +# use the /sys/subsystem/ path for this. Kernel "bus" and "class" names +# should be treated as one namespace, like udev handles it. This is mostly +# just an identification string for systemd, so whether the path actually is +# accessible or not does not matter as long as it is unique and in the +# filesystem namespace. +# +# http://cgit.freedesktop.org/systemd/systemd/tree/src/libudev/libudev-enumerate.c#n955 + +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" +ENV{ID_SMARTCARD_READER}=="?*", TAG+="systemd", ENV{SYSTEMD_WANTS}+="smartcard.target" +SUBSYSTEM=="sound", KERNEL=="card*", TAG+="systemd", ENV{SYSTEMD_WANTS}+="sound.target" + +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" + +# Apply sysctl variables to network devices (and only to those) as they appear. +ACTION=="add", SUBSYSTEM=="net", KERNEL!="lo", RUN+="@rootlibexecdir@/systemd-sysctl --prefix=/net/ipv4/conf/$name --prefix=/net/ipv4/neigh/$name --prefix=/net/ipv6/conf/$name --prefix=/net/ipv6/neigh/$name" + +# Pull in backlight save/restore for all backlight devices and +# keyboard backlights +SUBSYSTEM=="backlight", TAG+="systemd", IMPORT{builtin}="path_id", ENV{SYSTEMD_WANTS}+="systemd-backlight@backlight:$name.service" +SUBSYSTEM=="leds", KERNEL=="*kbd_backlight", TAG+="systemd", IMPORT{builtin}="path_id", ENV{SYSTEMD_WANTS}+="systemd-backlight@leds:$name.service" + +# Pull in rfkill save/restore for all rfkill devices + +SUBSYSTEM=="rfkill", IMPORT{builtin}="path_id" +SUBSYSTEM=="misc", KERNEL=="rfkill", TAG+="systemd", ENV{SYSTEMD_WANTS}+="systemd-rfkill.socket" + +# Asynchronously mount file systems implemented by these modules as soon as they are loaded. +SUBSYSTEM=="module", KERNEL=="fuse", TAG+="systemd", ENV{SYSTEMD_WANTS}+="sys-fs-fuse-connections.mount" +SUBSYSTEM=="module", KERNEL=="configfs", TAG+="systemd", ENV{SYSTEMD_WANTS}+="sys-kernel-config.mount" + +LABEL="systemd_end" diff --git a/src/grp-udev/Makefile b/src/grp-udev/Makefile index 67e20c65a2..965c9b2f22 100644 --- a/src/grp-udev/Makefile +++ b/src/grp-udev/Makefile @@ -42,6 +42,11 @@ dist_udevrules_DATA += \ rules/78-sound-card.rules \ rules/80-net-setup-link.rules +ifneq ($(HAVE_KMOD),) +dist_udevrules_DATA += \ + rules/80-drivers.rules +endif # HAVE_KMOD + nodist_udevrules_DATA += \ rules/99-systemd.rules @@ -69,7 +74,6 @@ SYSINIT_TARGET_WANTS += \ systemd-udevd.service \ systemd-udev-trigger.service -sd.sed_files += $(notdir $(patsubst %.in,%,$(filter %.in,$(EXTRA_DIST)))) nested.subdirs += systemd-udevd udevadm nested.subdirs += ata_id cdrom_id collect mtd_probe scsi_id v4l_id diff --git a/src/grp-udev/cdrom_id/60-cdrom_id.rules b/src/grp-udev/cdrom_id/60-cdrom_id.rules new file mode 100644 index 0000000000..5c3b52ebb9 --- /dev/null +++ b/src/grp-udev/cdrom_id/60-cdrom_id.rules @@ -0,0 +1,25 @@ +# do not edit this file, it will be overwritten on update + +ACTION=="remove", GOTO="cdrom_end" +SUBSYSTEM!="block", GOTO="cdrom_end" +KERNEL!="sr[0-9]*|xvd*", GOTO="cdrom_end" +ENV{DEVTYPE}!="disk", GOTO="cdrom_end" + +# unconditionally tag device as CDROM +KERNEL=="sr[0-9]*", ENV{ID_CDROM}="1" + +# media eject button pressed +ENV{DISK_EJECT_REQUEST}=="?*", RUN+="cdrom_id --eject-media $devnode", GOTO="cdrom_end" + +# import device and media properties and lock tray to +# enable the receiving of media eject button events +IMPORT{program}="cdrom_id --lock-media $devnode" + +# ejecting a CD does not remove the device node, so mark the systemd device +# unit as inactive while there is no medium; this automatically cleans up of +# stale mounts after ejecting +ENV{DISK_MEDIA_CHANGE}=="?*", ENV{ID_CDROM_MEDIA}!="?*", ENV{SYSTEMD_READY}="0" + +KERNEL=="sr0", SYMLINK+="cdrom", OPTIONS+="link_priority=-100" + +LABEL="cdrom_end" diff --git a/src/grp-udev/libudev-core/Makefile b/src/grp-udev/libudev-core/Makefile index da72594005..36aaa55431 100644 --- a/src/grp-udev/libudev-core/Makefile +++ b/src/grp-udev/libudev-core/Makefile @@ -80,9 +80,6 @@ libudev_core_la_LIBADD = \ ifneq ($(HAVE_KMOD),) libudev_core_la_SOURCES += \ src/udev/udev-builtin-kmod.c - -dist_udevrules_DATA += \ - rules/80-drivers.rules endif # HAVE_KMOD ifneq ($(HAVE_BLKID),) diff --git a/src/grp-udev/libudev-core/logind-acl.c b/src/grp-udev/libudev-core/logind-acl.c index 9dd626440f..dd15b7973f 120000 --- a/src/grp-udev/libudev-core/logind-acl.c +++ b/src/grp-udev/libudev-core/logind-acl.c @@ -1 +1 @@ -../../grp-login/liblogind-core/logind-acl.c \ No newline at end of file +../../grp-login/systemd-logind/logind-acl.c \ No newline at end of file diff --git a/src/grp-udev/libudev-core/logind-acl.h b/src/grp-udev/libudev-core/logind-acl.h index 60f96cbe0a..6065dde301 120000 --- a/src/grp-udev/libudev-core/logind-acl.h +++ b/src/grp-udev/libudev-core/logind-acl.h @@ -1 +1 @@ -../../grp-login/liblogind-core/logind-acl.h \ No newline at end of file +../../grp-login/systemd-logind/logind-acl.h \ No newline at end of file diff --git a/src/grp-udev/mtd_probe/75-probe_mtd.rules b/src/grp-udev/mtd_probe/75-probe_mtd.rules new file mode 100644 index 0000000000..8848aeeaed --- /dev/null +++ b/src/grp-udev/mtd_probe/75-probe_mtd.rules @@ -0,0 +1,7 @@ +# do not edit this file, it will be overwritten on update + +ACTION!="add", GOTO="mtd_probe_end" + +KERNEL=="mtd*ro", IMPORT{program}="mtd_probe $devnode" + +LABEL="mtd_probe_end" diff --git a/src/grp-udev/rules/50-udev-default.rules b/src/grp-udev/rules/50-udev-default.rules deleted file mode 100644 index e9eeb8518e..0000000000 --- a/src/grp-udev/rules/50-udev-default.rules +++ /dev/null @@ -1,77 +0,0 @@ -# do not edit this file, it will be overwritten on update - -# run a command on remove events -ACTION=="remove", ENV{REMOVE_CMD}!="", RUN+="$env{REMOVE_CMD}" -ACTION=="remove", GOTO="default_end" - -SUBSYSTEM=="virtio-ports", KERNEL=="vport*", ATTR{name}=="?*", SYMLINK+="virtio-ports/$attr{name}" - -# select "system RTC" or just use the first one -SUBSYSTEM=="rtc", ATTR{hctosys}=="1", SYMLINK+="rtc" -SUBSYSTEM=="rtc", KERNEL=="rtc0", SYMLINK+="rtc", OPTIONS+="link_priority=-100" - -SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", IMPORT{builtin}="usb_id", IMPORT{builtin}="hwdb --subsystem=usb" -SUBSYSTEM=="input", ENV{ID_INPUT}=="", IMPORT{builtin}="input_id" -ENV{MODALIAS}!="", IMPORT{builtin}="hwdb --subsystem=$env{SUBSYSTEM}" - -ACTION!="add", GOTO="default_end" - -SUBSYSTEM=="tty", KERNEL=="ptmx", GROUP="tty", MODE="0666" -SUBSYSTEM=="tty", KERNEL=="tty", GROUP="tty", MODE="0666" -SUBSYSTEM=="tty", KERNEL=="tty[0-9]*", GROUP="tty", MODE="0620" -SUBSYSTEM=="tty", KERNEL=="sclp_line[0-9]*", GROUP="tty", MODE="0620" -SUBSYSTEM=="tty", KERNEL=="ttysclp[0-9]*", GROUP="tty", MODE="0620" -SUBSYSTEM=="tty", KERNEL=="3270/tty[0-9]*", GROUP="tty", MODE="0620" -SUBSYSTEM=="vc", KERNEL=="vcs*|vcsa*", GROUP="tty" -KERNEL=="tty[A-Z]*[0-9]|pppox[0-9]*|ircomm[0-9]*|noz[0-9]*|rfcomm[0-9]*", GROUP="dialout" - -SUBSYSTEM=="mem", KERNEL=="mem|kmem|port", GROUP="kmem", MODE="0640" - -SUBSYSTEM=="input", GROUP="input" -SUBSYSTEM=="input", KERNEL=="js[0-9]*", MODE="0664" - -SUBSYSTEM=="video4linux", GROUP="video" -SUBSYSTEM=="graphics", GROUP="video" -SUBSYSTEM=="drm", GROUP="video" -SUBSYSTEM=="dvb", GROUP="video" - -SUBSYSTEM=="sound", GROUP="audio", \ - OPTIONS+="static_node=snd/seq", OPTIONS+="static_node=snd/timer" - -SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", MODE="0664" - -SUBSYSTEM=="firewire", ATTR{units}=="*0x00a02d:0x00010*", GROUP="video" -SUBSYSTEM=="firewire", ATTR{units}=="*0x00b09d:0x00010*", GROUP="video" -SUBSYSTEM=="firewire", ATTR{units}=="*0x00a02d:0x010001*", GROUP="video" -SUBSYSTEM=="firewire", ATTR{units}=="*0x00a02d:0x014001*", GROUP="video" - -KERNEL=="parport[0-9]*", GROUP="lp" -SUBSYSTEM=="printer", KERNEL=="lp*", GROUP="lp" -SUBSYSTEM=="ppdev", GROUP="lp" -KERNEL=="lp[0-9]*", GROUP="lp" -KERNEL=="irlpt[0-9]*", GROUP="lp" -SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ENV{ID_USB_INTERFACES}=="*:0701??:*", GROUP="lp" - -SUBSYSTEM=="block", GROUP="disk" -SUBSYSTEM=="block", KERNEL=="sr[0-9]*", GROUP="cdrom" -SUBSYSTEM=="scsi_generic", SUBSYSTEMS=="scsi", ATTRS{type}=="4|5", GROUP="cdrom" -KERNEL=="sch[0-9]*", GROUP="cdrom" -KERNEL=="pktcdvd[0-9]*", GROUP="cdrom" -KERNEL=="pktcdvd", GROUP="cdrom" - -SUBSYSTEM=="scsi_generic|scsi_tape", SUBSYSTEMS=="scsi", ATTRS{type}=="1|8", GROUP="tape" -SUBSYSTEM=="scsi_generic", SUBSYSTEMS=="scsi", ATTRS{type}=="0", GROUP="disk" -KERNEL=="qft[0-9]*|nqft[0-9]*|zqft[0-9]*|nzqft[0-9]*|rawqft[0-9]*|nrawqft[0-9]*", GROUP="disk" -KERNEL=="loop-control", GROUP="disk", OPTIONS+="static_node=loop-control" -KERNEL=="btrfs-control", GROUP="disk" -KERNEL=="rawctl", GROUP="disk" -SUBSYSTEM=="raw", KERNEL=="raw[0-9]*", GROUP="disk" -SUBSYSTEM=="aoe", GROUP="disk", MODE="0220" -SUBSYSTEM=="aoe", KERNEL=="err", MODE="0440" - -KERNEL=="rfkill", MODE="0664" -KERNEL=="tun", MODE="0666", OPTIONS+="static_node=net/tun" - -KERNEL=="fuse", MODE="0666", OPTIONS+="static_node=fuse" - -LABEL="default_end" diff --git a/src/grp-udev/rules/60-block.rules b/src/grp-udev/rules/60-block.rules deleted file mode 100644 index c74caca49f..0000000000 --- a/src/grp-udev/rules/60-block.rules +++ /dev/null @@ -1,11 +0,0 @@ -# do not edit this file, it will be overwritten on update - -# enable in-kernel media-presence polling -ACTION=="add", SUBSYSTEM=="module", KERNEL=="block", ATTR{parameters/events_dfl_poll_msecs}=="0", \ - ATTR{parameters/events_dfl_poll_msecs}="2000" - -# forward scsi device event to corresponding block device -ACTION=="change", SUBSYSTEM=="scsi", ENV{DEVTYPE}=="scsi_device", TEST=="block", ATTR{block/*/uevent}="change" - -# watch metadata changes, caused by tools closing the device node which was opened for writing -ACTION!="remove", SUBSYSTEM=="block", KERNEL=="loop*|nvme*|sd*|vd*|xvd*", OPTIONS+="watch" diff --git a/src/grp-udev/rules/60-cdrom_id.rules b/src/grp-udev/rules/60-cdrom_id.rules deleted file mode 100644 index 5c3b52ebb9..0000000000 --- a/src/grp-udev/rules/60-cdrom_id.rules +++ /dev/null @@ -1,25 +0,0 @@ -# do not edit this file, it will be overwritten on update - -ACTION=="remove", GOTO="cdrom_end" -SUBSYSTEM!="block", GOTO="cdrom_end" -KERNEL!="sr[0-9]*|xvd*", GOTO="cdrom_end" -ENV{DEVTYPE}!="disk", GOTO="cdrom_end" - -# unconditionally tag device as CDROM -KERNEL=="sr[0-9]*", ENV{ID_CDROM}="1" - -# media eject button pressed -ENV{DISK_EJECT_REQUEST}=="?*", RUN+="cdrom_id --eject-media $devnode", GOTO="cdrom_end" - -# import device and media properties and lock tray to -# enable the receiving of media eject button events -IMPORT{program}="cdrom_id --lock-media $devnode" - -# ejecting a CD does not remove the device node, so mark the systemd device -# unit as inactive while there is no medium; this automatically cleans up of -# stale mounts after ejecting -ENV{DISK_MEDIA_CHANGE}=="?*", ENV{ID_CDROM_MEDIA}!="?*", ENV{SYSTEMD_READY}="0" - -KERNEL=="sr0", SYMLINK+="cdrom", OPTIONS+="link_priority=-100" - -LABEL="cdrom_end" diff --git a/src/grp-udev/rules/60-drm.rules b/src/grp-udev/rules/60-drm.rules deleted file mode 100644 index 1ed3e445f2..0000000000 --- a/src/grp-udev/rules/60-drm.rules +++ /dev/null @@ -1,3 +0,0 @@ -# do not edit this file, it will be overwritten on update - -ACTION!="remove", SUBSYSTEM=="drm", SUBSYSTEMS=="pci|usb|platform", IMPORT{builtin}="path_id" diff --git a/src/grp-udev/rules/60-evdev.rules b/src/grp-udev/rules/60-evdev.rules deleted file mode 100644 index ade7e7f646..0000000000 --- a/src/grp-udev/rules/60-evdev.rules +++ /dev/null @@ -1,19 +0,0 @@ -# do not edit this file, it will be overwritten on update - -ACTION=="remove", GOTO="evdev_end" -KERNEL!="event*", GOTO="evdev_end" - -# skip later rules when we find something for this input device -IMPORT{builtin}="hwdb --subsystem=input --lookup-prefix=evdev:", \ - RUN{builtin}+="keyboard", GOTO="evdev_end" - -# AT keyboard matching by the machine's DMI data -ENV{ID_INPUT_KEY}=="?*", DRIVERS=="atkbd", \ - IMPORT{builtin}="hwdb 'evdev:atkbd:$attr{[dmi/id]modalias}'", \ - RUN{builtin}+="keyboard", GOTO="evdev_end" - -# device matching the input device name and the machine's DMI data -KERNELS=="input*", IMPORT{builtin}="hwdb 'evdev:name:$attr{name}:$attr{[dmi/id]modalias}'", \ - RUN{builtin}+="keyboard", GOTO="evdev_end" - -LABEL="evdev_end" diff --git a/src/grp-udev/rules/60-persistent-alsa.rules b/src/grp-udev/rules/60-persistent-alsa.rules deleted file mode 100644 index 8154e2dbb5..0000000000 --- a/src/grp-udev/rules/60-persistent-alsa.rules +++ /dev/null @@ -1,14 +0,0 @@ -# do not edit this file, it will be overwritten on update - -ACTION=="remove", GOTO="persistent_alsa_end" -SUBSYSTEM!="sound", GOTO="persistent_alsa_end" -KERNEL!="controlC[0-9]*", GOTO="persistent_alsa_end" - -SUBSYSTEMS=="usb", ENV{ID_MODEL}=="", IMPORT{builtin}="usb_id" -ENV{ID_SERIAL}=="?*", ENV{ID_USB_INTERFACE_NUM}=="?*", SYMLINK+="snd/by-id/$env{ID_BUS}-$env{ID_SERIAL}-$env{ID_USB_INTERFACE_NUM}" -ENV{ID_SERIAL}=="?*", ENV{ID_USB_INTERFACE_NUM}=="", SYMLINK+="snd/by-id/$env{ID_BUS}-$env{ID_SERIAL}" - -IMPORT{builtin}="path_id" -ENV{ID_PATH}=="?*", SYMLINK+="snd/by-path/$env{ID_PATH}" - -LABEL="persistent_alsa_end" diff --git a/src/grp-udev/rules/60-persistent-input.rules b/src/grp-udev/rules/60-persistent-input.rules deleted file mode 100644 index 0e33e68384..0000000000 --- a/src/grp-udev/rules/60-persistent-input.rules +++ /dev/null @@ -1,38 +0,0 @@ -# do not edit this file, it will be overwritten on update - -ACTION=="remove", GOTO="persistent_input_end" -SUBSYSTEM!="input", GOTO="persistent_input_end" -SUBSYSTEMS=="bluetooth", GOTO="persistent_input_end" - -SUBSYSTEMS=="usb", ENV{ID_BUS}=="", IMPORT{builtin}="usb_id" - -# determine class name for persistent symlinks -ENV{ID_INPUT_KEYBOARD}=="?*", ENV{.INPUT_CLASS}="kbd" -ENV{ID_INPUT_MOUSE}=="?*", ENV{.INPUT_CLASS}="mouse" -ENV{ID_INPUT_TOUCHPAD}=="?*", ENV{.INPUT_CLASS}="mouse" -ENV{ID_INPUT_TABLET}=="?*", ENV{.INPUT_CLASS}="mouse" -ENV{ID_INPUT_JOYSTICK}=="?*", ENV{.INPUT_CLASS}="joystick" -DRIVERS=="pcspkr", ENV{.INPUT_CLASS}="spkr" -ATTRS{name}=="*dvb*|*DVB*|* IR *", ENV{.INPUT_CLASS}="ir" - -# fill empty serial number -ENV{.INPUT_CLASS}=="?*", ENV{ID_SERIAL}=="", ENV{ID_SERIAL}="noserial" - -# by-id links -KERNEL=="mouse*|js*", ENV{ID_BUS}=="?*", ENV{.INPUT_CLASS}=="?*", ATTRS{bInterfaceNumber}=="|00", SYMLINK+="input/by-id/$env{ID_BUS}-$env{ID_SERIAL}-$env{.INPUT_CLASS}" -KERNEL=="mouse*|js*", ENV{ID_BUS}=="?*", ENV{.INPUT_CLASS}=="?*", ATTRS{bInterfaceNumber}=="?*", ATTRS{bInterfaceNumber}!="00", SYMLINK+="input/by-id/$env{ID_BUS}-$env{ID_SERIAL}-if$attr{bInterfaceNumber}-$env{.INPUT_CLASS}" -KERNEL=="event*", ENV{ID_BUS}=="?*", ENV{.INPUT_CLASS}=="?*", ATTRS{bInterfaceNumber}=="|00", SYMLINK+="input/by-id/$env{ID_BUS}-$env{ID_SERIAL}-event-$env{.INPUT_CLASS}" -KERNEL=="event*", ENV{ID_BUS}=="?*", ENV{.INPUT_CLASS}=="?*", ATTRS{bInterfaceNumber}=="?*", ATTRS{bInterfaceNumber}!="00", SYMLINK+="input/by-id/$env{ID_BUS}-$env{ID_SERIAL}-if$attr{bInterfaceNumber}-event-$env{.INPUT_CLASS}" -# allow empty class for USB devices, by appending the interface number -SUBSYSTEMS=="usb", ENV{ID_BUS}=="?*", KERNEL=="event*", ENV{.INPUT_CLASS}=="", ATTRS{bInterfaceNumber}=="?*", \ - SYMLINK+="input/by-id/$env{ID_BUS}-$env{ID_SERIAL}-event-if$attr{bInterfaceNumber}" - -# by-path -SUBSYSTEMS=="pci|usb|platform|acpi", IMPORT{builtin}="path_id" -ENV{ID_PATH}=="?*", KERNEL=="mouse*|js*", ENV{.INPUT_CLASS}=="?*", SYMLINK+="input/by-path/$env{ID_PATH}-$env{.INPUT_CLASS}" -ENV{ID_PATH}=="?*", KERNEL=="event*", ENV{.INPUT_CLASS}=="?*", SYMLINK+="input/by-path/$env{ID_PATH}-event-$env{.INPUT_CLASS}" -# allow empty class for platform and usb devices; platform supports only a single interface that way -SUBSYSTEMS=="usb|platform", ENV{ID_PATH}=="?*", KERNEL=="event*", ENV{.INPUT_CLASS}=="", \ - SYMLINK+="input/by-path/$env{ID_PATH}-event" - -LABEL="persistent_input_end" diff --git a/src/grp-udev/rules/60-persistent-storage-tape.rules b/src/grp-udev/rules/60-persistent-storage-tape.rules deleted file mode 100644 index f2eabd92a8..0000000000 --- a/src/grp-udev/rules/60-persistent-storage-tape.rules +++ /dev/null @@ -1,25 +0,0 @@ -# do not edit this file, it will be overwritten on update - -# persistent storage links: /dev/tape/{by-id,by-path} - -ACTION=="remove", GOTO="persistent_storage_tape_end" - -# type 8 devices are "Medium Changers" -SUBSYSTEM=="scsi_generic", SUBSYSTEMS=="scsi", ATTRS{type}=="8", IMPORT{program}="scsi_id --sg-version=3 --export --whitelisted -d $devnode", \ - SYMLINK+="tape/by-id/scsi-$env{ID_SERIAL}" - -SUBSYSTEM!="scsi_tape", GOTO="persistent_storage_tape_end" - -KERNEL=="st*[0-9]|nst*[0-9]", ATTRS{ieee1394_id}=="?*", ENV{ID_SERIAL}="$attr{ieee1394_id}", ENV{ID_BUS}="ieee1394" -KERNEL=="st*[0-9]|nst*[0-9]", ENV{ID_SERIAL}!="?*", SUBSYSTEMS=="usb", IMPORT{builtin}="usb_id" -KERNEL=="st*[0-9]|nst*[0-9]", ENV{ID_SERIAL}!="?*", SUBSYSTEMS=="scsi", KERNELS=="[0-9]*:*[0-9]", ENV{.BSG_DEV}="$root/bsg/$id" -KERNEL=="st*[0-9]|nst*[0-9]", ENV{ID_SERIAL}!="?*", IMPORT{program}="scsi_id --whitelisted --export --device=$env{.BSG_DEV}", ENV{ID_BUS}="scsi" -KERNEL=="st*[0-9]", ENV{ID_SERIAL}=="?*", SYMLINK+="tape/by-id/$env{ID_BUS}-$env{ID_SERIAL}" -KERNEL=="nst*[0-9]", ENV{ID_SERIAL}=="?*", SYMLINK+="tape/by-id/$env{ID_BUS}-$env{ID_SERIAL}-nst" - -# by-path (parent device path) -KERNEL=="st*[0-9]|nst*[0-9]", IMPORT{builtin}="path_id" -KERNEL=="st*[0-9]", ENV{ID_PATH}=="?*", SYMLINK+="tape/by-path/$env{ID_PATH}" -KERNEL=="nst*[0-9]", ENV{ID_PATH}=="?*", SYMLINK+="tape/by-path/$env{ID_PATH}-nst" - -LABEL="persistent_storage_tape_end" diff --git a/src/grp-udev/rules/60-persistent-storage.rules b/src/grp-udev/rules/60-persistent-storage.rules deleted file mode 100644 index 408733915c..0000000000 --- a/src/grp-udev/rules/60-persistent-storage.rules +++ /dev/null @@ -1,87 +0,0 @@ -# do not edit this file, it will be overwritten on update - -# persistent storage links: /dev/disk/{by-id,by-uuid,by-label,by-path} -# scheme based on "Linux persistent device names", 2004, Hannes Reinecke - -ACTION=="remove", GOTO="persistent_storage_end" - -SUBSYSTEM!="block", GOTO="persistent_storage_end" -KERNEL!="loop*|mmcblk*[0-9]|msblk*[0-9]|mspblk*[0-9]|nvme*|sd*|sr*|vd*|xvd*|bcache*|cciss*|dasd*|ubd*", GOTO="persistent_storage_end" - -# ignore partitions that span the entire disk -TEST=="whole_disk", GOTO="persistent_storage_end" - -# for partitions import parent information -ENV{DEVTYPE}=="partition", IMPORT{parent}="ID_*" - -# NVMe -KERNEL=="nvme*[0-9]n*[0-9]", ATTR{wwid}=="?*", SYMLINK+="disk/by-id/nvme-$attr{wwid}" -KERNEL=="nvme*[0-9]n*[0-9]p*[0-9]", ENV{DEVTYPE}=="partition", ATTRS{wwid}=="?*", SYMLINK+="disk/by-id/nvme-$attr{wwid}-part%n" - -# virtio-blk -KERNEL=="vd*[!0-9]", ATTRS{serial}=="?*", ENV{ID_SERIAL}="$attr{serial}", SYMLINK+="disk/by-id/virtio-$env{ID_SERIAL}" -KERNEL=="vd*[0-9]", ATTRS{serial}=="?*", ENV{ID_SERIAL}="$attr{serial}", SYMLINK+="disk/by-id/virtio-$env{ID_SERIAL}-part%n" - -# ATA -KERNEL=="sd*[!0-9]|sr*", ENV{ID_SERIAL}!="?*", SUBSYSTEMS=="scsi", ATTRS{vendor}=="ATA", IMPORT{program}="ata_id --export $devnode" - -# ATAPI devices (SPC-3 or later) -KERNEL=="sd*[!0-9]|sr*", ENV{ID_SERIAL}!="?*", SUBSYSTEMS=="scsi", ATTRS{type}=="5", ATTRS{scsi_level}=="[6-9]*", IMPORT{program}="ata_id --export $devnode" - -# Run ata_id on non-removable USB Mass Storage (SATA/PATA disks in enclosures) -KERNEL=="sd*[!0-9]|sr*", ENV{ID_SERIAL}!="?*", ATTR{removable}=="0", SUBSYSTEMS=="usb", IMPORT{program}="ata_id --export $devnode" - -# Fall back usb_id for USB devices -KERNEL=="sd*[!0-9]|sr*", ENV{ID_SERIAL}!="?*", SUBSYSTEMS=="usb", IMPORT{builtin}="usb_id" - -# SCSI devices -KERNEL=="sd*[!0-9]|sr*", ENV{ID_SERIAL}!="?*", IMPORT{program}="scsi_id --export --whitelisted -d $devnode", ENV{ID_BUS}="scsi" -KERNEL=="cciss*", ENV{DEVTYPE}=="disk", ENV{ID_SERIAL}!="?*", IMPORT{program}="scsi_id --export --whitelisted -d $devnode", ENV{ID_BUS}="cciss" -KERNEL=="sd*|sr*|cciss*", ENV{DEVTYPE}=="disk", ENV{ID_SERIAL}=="?*", SYMLINK+="disk/by-id/$env{ID_BUS}-$env{ID_SERIAL}" -KERNEL=="sd*|cciss*", ENV{DEVTYPE}=="partition", ENV{ID_SERIAL}=="?*", SYMLINK+="disk/by-id/$env{ID_BUS}-$env{ID_SERIAL}-part%n" - -# FireWire -KERNEL=="sd*[!0-9]|sr*", ATTRS{ieee1394_id}=="?*", SYMLINK+="disk/by-id/ieee1394-$attr{ieee1394_id}" -KERNEL=="sd*[0-9]", ATTRS{ieee1394_id}=="?*", SYMLINK+="disk/by-id/ieee1394-$attr{ieee1394_id}-part%n" - -# MMC -KERNEL=="mmcblk[0-9]", SUBSYSTEMS=="mmc", ATTRS{name}=="?*", ATTRS{serial}=="?*", \ - ENV{ID_NAME}="$attr{name}", ENV{ID_SERIAL}="$attr{serial}", SYMLINK+="disk/by-id/mmc-$env{ID_NAME}_$env{ID_SERIAL}" -KERNEL=="mmcblk[0-9]p[0-9]", ENV{ID_NAME}=="?*", ENV{ID_SERIAL}=="?*", SYMLINK+="disk/by-id/mmc-$env{ID_NAME}_$env{ID_SERIAL}-part%n" - -# Memstick -KERNEL=="msblk[0-9]|mspblk[0-9]", SUBSYSTEMS=="memstick", ATTRS{name}=="?*", ATTRS{serial}=="?*", \ - ENV{ID_NAME}="$attr{name}", ENV{ID_SERIAL}="$attr{serial}", SYMLINK+="disk/by-id/memstick-$env{ID_NAME}_$env{ID_SERIAL}" -KERNEL=="msblk[0-9]p[0-9]|mspblk[0-9]p[0-9]", ENV{ID_NAME}=="?*", ENV{ID_SERIAL}=="?*", SYMLINK+="disk/by-id/memstick-$env{ID_NAME}_$env{ID_SERIAL}-part%n" - -# by-path -ENV{DEVTYPE}=="disk", DEVPATH!="*/virtual/*", IMPORT{builtin}="path_id" -ENV{DEVTYPE}=="disk", ENV{ID_PATH}=="?*", SYMLINK+="disk/by-path/$env{ID_PATH}" -ENV{DEVTYPE}=="partition", ENV{ID_PATH}=="?*", SYMLINK+="disk/by-path/$env{ID_PATH}-part%n" - -# probe filesystem metadata of optical drives which have a media inserted -KERNEL=="sr*", ENV{DISK_EJECT_REQUEST}!="?*", ENV{ID_CDROM_MEDIA_TRACK_COUNT_DATA}=="?*", ENV{ID_CDROM_MEDIA_SESSION_LAST_OFFSET}=="?*", \ - IMPORT{builtin}="blkid --offset=$env{ID_CDROM_MEDIA_SESSION_LAST_OFFSET}" -# single-session CDs do not have ID_CDROM_MEDIA_SESSION_LAST_OFFSET -KERNEL=="sr*", ENV{DISK_EJECT_REQUEST}!="?*", ENV{ID_CDROM_MEDIA_TRACK_COUNT_DATA}=="?*", ENV{ID_CDROM_MEDIA_SESSION_LAST_OFFSET}=="", \ - IMPORT{builtin}="blkid --noraid" - -# probe filesystem metadata of disks -KERNEL!="sr*", IMPORT{builtin}="blkid" - -# by-label/by-uuid links (filesystem metadata) -ENV{ID_FS_USAGE}=="filesystem|other|crypto", ENV{ID_FS_UUID_ENC}=="?*", SYMLINK+="disk/by-uuid/$env{ID_FS_UUID_ENC}" -ENV{ID_FS_USAGE}=="filesystem|other", ENV{ID_FS_LABEL_ENC}=="?*", SYMLINK+="disk/by-label/$env{ID_FS_LABEL_ENC}" - -# by-id (World Wide Name) -ENV{DEVTYPE}=="disk", ENV{ID_WWN_WITH_EXTENSION}=="?*", SYMLINK+="disk/by-id/wwn-$env{ID_WWN_WITH_EXTENSION}" -ENV{DEVTYPE}=="partition", ENV{ID_WWN_WITH_EXTENSION}=="?*", SYMLINK+="disk/by-id/wwn-$env{ID_WWN_WITH_EXTENSION}-part%n" - -# by-partlabel/by-partuuid links (partition metadata) -ENV{ID_PART_ENTRY_UUID}=="?*", SYMLINK+="disk/by-partuuid/$env{ID_PART_ENTRY_UUID}" -ENV{ID_PART_ENTRY_SCHEME}=="gpt", ENV{ID_PART_ENTRY_NAME}=="?*", SYMLINK+="disk/by-partlabel/$env{ID_PART_ENTRY_NAME}" - -# add symlink to GPT root disk -ENV{ID_PART_ENTRY_SCHEME}=="gpt", ENV{ID_PART_GPT_AUTO_ROOT}=="1", SYMLINK+="gpt-auto-root" - -LABEL="persistent_storage_end" diff --git a/src/grp-udev/rules/60-persistent-v4l.rules b/src/grp-udev/rules/60-persistent-v4l.rules deleted file mode 100644 index 93c5ee8c27..0000000000 --- a/src/grp-udev/rules/60-persistent-v4l.rules +++ /dev/null @@ -1,20 +0,0 @@ -# do not edit this file, it will be overwritten on update - -ACTION=="remove", GOTO="persistent_v4l_end" -SUBSYSTEM!="video4linux", GOTO="persistent_v4l_end" -ENV{MAJOR}=="", GOTO="persistent_v4l_end" - -IMPORT{program}="v4l_id $devnode" - -SUBSYSTEMS=="usb", IMPORT{builtin}="usb_id" -KERNEL=="video*", ENV{ID_SERIAL}=="?*", SYMLINK+="v4l/by-id/$env{ID_BUS}-$env{ID_SERIAL}-video-index$attr{index}" - -# check for valid "index" number -TEST!="index", GOTO="persistent_v4l_end" -ATTR{index}!="?*", GOTO="persistent_v4l_end" - -IMPORT{builtin}="path_id" -ENV{ID_PATH}=="?*", KERNEL=="video*|vbi*", SYMLINK+="v4l/by-path/$env{ID_PATH}-video-index$attr{index}" -ENV{ID_PATH}=="?*", KERNEL=="audio*", SYMLINK+="v4l/by-path/$env{ID_PATH}-audio-index$attr{index}" - -LABEL="persistent_v4l_end" diff --git a/src/grp-udev/rules/60-serial.rules b/src/grp-udev/rules/60-serial.rules deleted file mode 100644 index f303e27fd5..0000000000 --- a/src/grp-udev/rules/60-serial.rules +++ /dev/null @@ -1,26 +0,0 @@ -# do not edit this file, it will be overwritten on update - -ACTION=="remove", GOTO="serial_end" -SUBSYSTEM!="tty", GOTO="serial_end" - -SUBSYSTEMS=="pci", ENV{ID_BUS}="pci", ENV{ID_VENDOR_ID}="$attr{vendor}", ENV{ID_MODEL_ID}="$attr{device}" -SUBSYSTEMS=="pci", IMPORT{builtin}="hwdb --subsystem=pci" -SUBSYSTEMS=="usb", IMPORT{builtin}="usb_id", IMPORT{builtin}="hwdb --subsystem=usb" - -# /dev/serial/by-path/, /dev/serial/by-id/ for USB devices -KERNEL!="ttyUSB[0-9]*|ttyACM[0-9]*", GOTO="serial_end" - -SUBSYSTEMS=="usb-serial", ENV{.ID_PORT}="$attr{port_number}" - -IMPORT{builtin}="path_id" -ENV{ID_PATH}=="?*", ENV{.ID_PORT}=="", SYMLINK+="serial/by-path/$env{ID_PATH}" -ENV{ID_PATH}=="?*", ENV{.ID_PORT}=="?*", SYMLINK+="serial/by-path/$env{ID_PATH}-port$env{.ID_PORT}" - -IMPORT{builtin}="usb_id" -ENV{ID_SERIAL}=="", GOTO="serial_end" -SUBSYSTEMS=="usb", ENV{ID_USB_INTERFACE_NUM}="$attr{bInterfaceNumber}" -ENV{ID_USB_INTERFACE_NUM}=="", GOTO="serial_end" -ENV{.ID_PORT}=="", SYMLINK+="serial/by-id/$env{ID_BUS}-$env{ID_SERIAL}-if$env{ID_USB_INTERFACE_NUM}" -ENV{.ID_PORT}=="?*", SYMLINK+="serial/by-id/$env{ID_BUS}-$env{ID_SERIAL}-if$env{ID_USB_INTERFACE_NUM}-port$env{.ID_PORT}" - -LABEL="serial_end" diff --git a/src/grp-udev/rules/64-btrfs.rules b/src/grp-udev/rules/64-btrfs.rules deleted file mode 100644 index fe0100131e..0000000000 --- a/src/grp-udev/rules/64-btrfs.rules +++ /dev/null @@ -1,13 +0,0 @@ -# do not edit this file, it will be overwritten on update - -SUBSYSTEM!="block", GOTO="btrfs_end" -ACTION=="remove", GOTO="btrfs_end" -ENV{ID_FS_TYPE}!="btrfs", GOTO="btrfs_end" - -# let the kernel know about this btrfs filesystem, and check if it is complete -IMPORT{builtin}="btrfs ready $devnode" - -# mark the device as not ready to be used by the system -ENV{ID_BTRFS_READY}=="0", ENV{SYSTEMD_READY}="0" - -LABEL="btrfs_end" diff --git a/src/grp-udev/rules/70-mouse.rules b/src/grp-udev/rules/70-mouse.rules deleted file mode 100644 index 3ea743aff9..0000000000 --- a/src/grp-udev/rules/70-mouse.rules +++ /dev/null @@ -1,18 +0,0 @@ -# do not edit this file, it will be overwritten on update - -ACTION=="remove", GOTO="mouse_end" -KERNEL!="event*", GOTO="mouse_end" -ENV{ID_INPUT_MOUSE}=="", GOTO="mouse_end" - -# mouse::vp:name::* -KERNELS=="input*", ENV{ID_BUS}=="usb", \ - IMPORT{builtin}="hwdb 'mouse:$env{ID_BUS}:v$attr{id/vendor}p$attr{id/product}:name:$attr{name}:'", \ - GOTO="mouse_end" -KERNELS=="input*", ENV{ID_BUS}=="bluetooth", \ - IMPORT{builtin}="hwdb 'mouse:$env{ID_BUS}:v$attr{id/vendor}p$attr{id/product}:name:$attr{name}:'", \ - GOTO="mouse_end" -DRIVERS=="psmouse", SUBSYSTEMS=="serio", \ - IMPORT{builtin}="hwdb 'mouse:ps2::name:$attr{device/name}:'", \ - GOTO="mouse_end" - -LABEL="mouse_end" diff --git a/src/grp-udev/rules/75-net-description.rules b/src/grp-udev/rules/75-net-description.rules deleted file mode 100644 index 7e62f8b26b..0000000000 --- a/src/grp-udev/rules/75-net-description.rules +++ /dev/null @@ -1,14 +0,0 @@ -# do not edit this file, it will be overwritten on update - -ACTION=="remove", GOTO="net_end" -SUBSYSTEM!="net", GOTO="net_end" - -IMPORT{builtin}="net_id" - -SUBSYSTEMS=="usb", IMPORT{builtin}="usb_id", IMPORT{builtin}="hwdb --subsystem=usb" -SUBSYSTEMS=="usb", GOTO="net_end" - -SUBSYSTEMS=="pci", ENV{ID_BUS}="pci", ENV{ID_VENDOR_ID}="$attr{vendor}", ENV{ID_MODEL_ID}="$attr{device}" -SUBSYSTEMS=="pci", IMPORT{builtin}="hwdb --subsystem=pci" - -LABEL="net_end" diff --git a/src/grp-udev/rules/75-probe_mtd.rules b/src/grp-udev/rules/75-probe_mtd.rules deleted file mode 100644 index 8848aeeaed..0000000000 --- a/src/grp-udev/rules/75-probe_mtd.rules +++ /dev/null @@ -1,7 +0,0 @@ -# do not edit this file, it will be overwritten on update - -ACTION!="add", GOTO="mtd_probe_end" - -KERNEL=="mtd*ro", IMPORT{program}="mtd_probe $devnode" - -LABEL="mtd_probe_end" diff --git a/src/grp-udev/rules/78-sound-card.rules b/src/grp-udev/rules/78-sound-card.rules deleted file mode 100644 index 04740e8b97..0000000000 --- a/src/grp-udev/rules/78-sound-card.rules +++ /dev/null @@ -1,89 +0,0 @@ -# do not edit this file, it will be overwritten on update - -SUBSYSTEM!="sound", GOTO="sound_end" - -ACTION=="add|change", KERNEL=="controlC*", ATTR{../uevent}="change" -ACTION!="change", GOTO="sound_end" - -# Ok, we probably need a little explanation here for what the two lines above -# are good for. -# -# The story goes like this: when ALSA registers a new sound card it emits a -# series of 'add' events to userspace, for the main card device and for all the -# child device nodes that belong to it. udev relays those to applications, -# however only maintains the order between father and child, but not between -# the siblings. The control device node creation can be used as synchronization -# point. All other devices that belong to a card are created in the kernel -# before it. However unfortunately due to the fact that siblings are forwarded -# out of order by udev this fact is lost to applications. -# -# OTOH before an application can open a device it needs to make sure that all -# its device nodes are completely created and set up. -# -# As a workaround for this issue we have added the udev rule above which will -# generate a 'change' event on the main card device from the 'add' event of the -# card's control device. Due to the ordering semantics of udev this event will -# only be relayed after all child devices have finished processing properly. -# When an application needs to listen for appearing devices it can hence look -# for 'change' events only, and ignore the actual 'add' events. -# -# When the application is initialized at the same time as a device is plugged -# in it may need to figure out if the 'change' event has already been triggered -# or not for a card. To find that out we store the flag environment variable -# SOUND_INITIALIZED on the device which simply tells us if the card 'change' -# event has already been processed. - -KERNEL!="card*", GOTO="sound_end" - -ENV{SOUND_INITIALIZED}="1" - -IMPORT{builtin}="hwdb" -SUBSYSTEMS=="usb", IMPORT{builtin}="usb_id" -SUBSYSTEMS=="usb", GOTO="skip_pci" - -SUBSYSTEMS=="firewire", ATTRS{guid}=="?*", \ - ENV{ID_BUS}="firewire", ENV{ID_SERIAL}="$attr{guid}", ENV{ID_SERIAL_SHORT}="$attr{guid}", \ - ENV{ID_VENDOR_ID}="$attr{vendor}", ENV{ID_MODEL_ID}="$attr{model}", \ - ENV{ID_VENDOR}="$attr{vendor_name}", ENV{ID_MODEL}="$attr{model_name}" -SUBSYSTEMS=="firewire", GOTO="skip_pci" - -SUBSYSTEMS=="pci", ENV{ID_BUS}="pci", ENV{ID_VENDOR_ID}="$attr{vendor}", ENV{ID_MODEL_ID}="$attr{device}" -LABEL="skip_pci" - -# Define ID_ID if ID_BUS and ID_SERIAL are set. This will work for both -# USB and firewire. -ENV{ID_SERIAL}=="?*", ENV{ID_USB_INTERFACE_NUM}=="?*", ENV{ID_ID}="$env{ID_BUS}-$env{ID_SERIAL}-$env{ID_USB_INTERFACE_NUM}" -ENV{ID_SERIAL}=="?*", ENV{ID_USB_INTERFACE_NUM}=="", ENV{ID_ID}="$env{ID_BUS}-$env{ID_SERIAL}" - -IMPORT{builtin}="path_id" - -# The values used here for $SOUND_FORM_FACTOR and $SOUND_CLASS should be kept -# in sync with those defined for PulseAudio's src/pulse/proplist.h -# PA_PROP_DEVICE_FORM_FACTOR, PA_PROP_DEVICE_CLASS properties. - -# If the first PCM device of this card has the pcm class 'modem', then the card is a modem -ATTR{pcmC%nD0p/pcm_class}=="modem", ENV{SOUND_CLASS}="modem", GOTO="sound_end" - -# Identify cards on the internal PCI bus as internal -SUBSYSTEMS=="pci", DEVPATH=="*/0000:00:??.?/sound/*", ENV{SOUND_FORM_FACTOR}="internal", GOTO="sound_end" - -# Devices that also support Image/Video interfaces are most likely webcams -SUBSYSTEMS=="usb", ENV{ID_USB_INTERFACES}=="*:0e????:*", ENV{SOUND_FORM_FACTOR}="webcam", GOTO="sound_end" - -# Matching on the model strings is a bit ugly, I admit -ENV{ID_MODEL}=="*[Ss]peaker*", ENV{SOUND_FORM_FACTOR}="speaker", GOTO="sound_end" -ENV{ID_MODEL_FROM_DATABASE}=="*[Ss]peaker*", ENV{SOUND_FORM_FACTOR}="speaker", GOTO="sound_end" - -ENV{ID_MODEL}=="*[Hh]eadphone*", ENV{SOUND_FORM_FACTOR}="headphone", GOTO="sound_end" -ENV{ID_MODEL_FROM_DATABASE}=="*[Hh]eadphone*", ENV{SOUND_FORM_FACTOR}="headphone", GOTO="sound_end" - -ENV{ID_MODEL}=="*[Hh]eadset*", ENV{SOUND_FORM_FACTOR}="headset", GOTO="sound_end" -ENV{ID_MODEL_FROM_DATABASE}=="*[Hh]eadset*", ENV{SOUND_FORM_FACTOR}="headset", GOTO="sound_end" - -ENV{ID_MODEL}=="*[Hh]andset*", ENV{SOUND_FORM_FACTOR}="handset", GOTO="sound_end" -ENV{ID_MODEL_FROM_DATABASE}=="*[Hh]andset*", ENV{SOUND_FORM_FACTOR}="handset", GOTO="sound_end" - -ENV{ID_MODEL}=="*[Mm]icrophone*", ENV{SOUND_FORM_FACTOR}="microphone", GOTO="sound_end" -ENV{ID_MODEL_FROM_DATABASE}=="*[Mm]icrophone*", ENV{SOUND_FORM_FACTOR}="microphone", GOTO="sound_end" - -LABEL="sound_end" diff --git a/src/grp-udev/rules/80-drivers.rules b/src/grp-udev/rules/80-drivers.rules deleted file mode 100644 index 8551f47a4b..0000000000 --- a/src/grp-udev/rules/80-drivers.rules +++ /dev/null @@ -1,13 +0,0 @@ -# do not edit this file, it will be overwritten on update - -ACTION=="remove", GOTO="drivers_end" - -ENV{MODALIAS}=="?*", RUN{builtin}+="kmod load $env{MODALIAS}" -SUBSYSTEM=="tifm", ENV{TIFM_CARD_TYPE}=="SD", RUN{builtin}+="kmod load tifm_sd" -SUBSYSTEM=="tifm", ENV{TIFM_CARD_TYPE}=="MS", RUN{builtin}+="kmod load tifm_ms" -SUBSYSTEM=="memstick", RUN{builtin}+="kmod load ms_block mspro_block" -SUBSYSTEM=="i2o", RUN{builtin}+="kmod load i2o_block" -SUBSYSTEM=="module", KERNEL=="parport_pc", RUN{builtin}+="kmod load ppdev" -KERNEL=="mtd*ro", ENV{MTD_FTL}=="smartmedia", RUN{builtin}+="kmod load sm_ftl" - -LABEL="drivers_end" diff --git a/src/grp-udev/rules/80-net-setup-link.rules b/src/grp-udev/rules/80-net-setup-link.rules deleted file mode 100644 index 6e411a91f0..0000000000 --- a/src/grp-udev/rules/80-net-setup-link.rules +++ /dev/null @@ -1,13 +0,0 @@ -# do not edit this file, it will be overwritten on update - -SUBSYSTEM!="net", GOTO="net_setup_link_end" - -IMPORT{builtin}="path_id" - -ACTION!="add", GOTO="net_setup_link_end" - -IMPORT{builtin}="net_setup_link" - -NAME=="", ENV{ID_NET_NAME}!="", NAME="$env{ID_NET_NAME}" - -LABEL="net_setup_link_end" diff --git a/src/grp-udev/rules/99-systemd.rules.in b/src/grp-udev/rules/99-systemd.rules.in deleted file mode 100644 index fb4517606d..0000000000 --- a/src/grp-udev/rules/99-systemd.rules.in +++ /dev/null @@ -1,67 +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 Lesser General Public License as published by -# the Free Software Foundation; either version 2.1 of the License, or -# (at your option) any later version. - -ACTION=="remove", GOTO="systemd_end" - -SUBSYSTEM=="tty", KERNEL=="tty[a-zA-Z]*|hvc*|xvc*|hvsi*|ttysclp*|sclp_line*|3270/tty[0-9]*", TAG+="systemd" -KERNEL=="vport*", TAG+="systemd" - -SUBSYSTEM=="block", TAG+="systemd" -SUBSYSTEM=="block", ACTION=="add", ENV{DM_UDEV_DISABLE_OTHER_RULES_FLAG}=="1", ENV{SYSTEMD_READY}="0" - -# Ignore encrypted devices with no identified superblock on it, since -# we are probably still calling mke2fs or mkswap on it. -SUBSYSTEM=="block", ENV{DM_UUID}=="CRYPT-*", ENV{ID_PART_TABLE_TYPE}=="", ENV{ID_FS_USAGE}=="", ENV{SYSTEMD_READY}="0" - -# Ignore raid devices that are not yet assembled and started -SUBSYSTEM=="block", ENV{DEVTYPE}=="disk", KERNEL=="md*", TEST!="md/array_state", ENV{SYSTEMD_READY}="0" -SUBSYSTEM=="block", ENV{DEVTYPE}=="disk", KERNEL=="md*", ATTR{md/array_state}=="|clear|inactive", ENV{SYSTEMD_READY}="0" - -# Ignore loop devices that don't have any file attached -SUBSYSTEM=="block", KERNEL=="loop[0-9]*", ENV{DEVTYPE}=="disk", TEST!="loop/backing_file", ENV{SYSTEMD_READY}="0" - -# Ignore nbd devices until the PID file exists (which signals a connected device) -SUBSYSTEM=="block", KERNEL=="nbd*", ENV{DEVTYPE}=="disk", TEST!="pid", ENV{SYSTEMD_READY}="0" - -# We need a hardware independent way to identify network devices. We -# use the /sys/subsystem/ path for this. Kernel "bus" and "class" names -# should be treated as one namespace, like udev handles it. This is mostly -# just an identification string for systemd, so whether the path actually is -# accessible or not does not matter as long as it is unique and in the -# filesystem namespace. -# -# http://cgit.freedesktop.org/systemd/systemd/tree/src/libudev/libudev-enumerate.c#n955 - -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" -ENV{ID_SMARTCARD_READER}=="?*", TAG+="systemd", ENV{SYSTEMD_WANTS}+="smartcard.target" -SUBSYSTEM=="sound", KERNEL=="card*", TAG+="systemd", ENV{SYSTEMD_WANTS}+="sound.target" - -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" - -# Apply sysctl variables to network devices (and only to those) as they appear. -ACTION=="add", SUBSYSTEM=="net", KERNEL!="lo", RUN+="@rootlibexecdir@/systemd-sysctl --prefix=/net/ipv4/conf/$name --prefix=/net/ipv4/neigh/$name --prefix=/net/ipv6/conf/$name --prefix=/net/ipv6/neigh/$name" - -# Pull in backlight save/restore for all backlight devices and -# keyboard backlights -SUBSYSTEM=="backlight", TAG+="systemd", IMPORT{builtin}="path_id", ENV{SYSTEMD_WANTS}+="systemd-backlight@backlight:$name.service" -SUBSYSTEM=="leds", KERNEL=="*kbd_backlight", TAG+="systemd", IMPORT{builtin}="path_id", ENV{SYSTEMD_WANTS}+="systemd-backlight@leds:$name.service" - -# Pull in rfkill save/restore for all rfkill devices - -SUBSYSTEM=="rfkill", IMPORT{builtin}="path_id" -SUBSYSTEM=="misc", KERNEL=="rfkill", TAG+="systemd", ENV{SYSTEMD_WANTS}+="systemd-rfkill.socket" - -# Asynchronously mount file systems implemented by these modules as soon as they are loaded. -SUBSYSTEM=="module", KERNEL=="fuse", TAG+="systemd", ENV{SYSTEMD_WANTS}+="sys-fs-fuse-connections.mount" -SUBSYSTEM=="module", KERNEL=="configfs", TAG+="systemd", ENV{SYSTEMD_WANTS}+="sys-kernel-config.mount" - -LABEL="systemd_end" diff --git a/src/grp-udev/systemd-udevd/systemd-udevd.service.in b/src/grp-udev/systemd-udevd/systemd-udevd.service.in new file mode 100644 index 0000000000..79f28c87c6 --- /dev/null +++ b/src/grp-udev/systemd-udevd/systemd-udevd.service.in @@ -0,0 +1,26 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=udev Kernel Device Manager +Documentation=man:systemd-udevd.service(8) man:udev(7) +DefaultDependencies=no +Wants=systemd-udevd-control.socket systemd-udevd-kernel.socket +After=systemd-udevd-control.socket systemd-udevd-kernel.socket systemd-sysusers.service +Before=sysinit.target +ConditionPathIsReadWrite=/sys + +[Service] +Type=notify +OOMScoreAdjust=-1000 +Sockets=systemd-udevd-control.socket systemd-udevd-kernel.socket +Restart=always +RestartSec=0 +ExecStart=@rootlibexecdir@/systemd-udevd +MountFlags=slave +KillMode=mixed +WatchdogSec=3min diff --git a/src/grp-udev/v4l_id/60-persistent-v4l.rules b/src/grp-udev/v4l_id/60-persistent-v4l.rules new file mode 100644 index 0000000000..93c5ee8c27 --- /dev/null +++ b/src/grp-udev/v4l_id/60-persistent-v4l.rules @@ -0,0 +1,20 @@ +# do not edit this file, it will be overwritten on update + +ACTION=="remove", GOTO="persistent_v4l_end" +SUBSYSTEM!="video4linux", GOTO="persistent_v4l_end" +ENV{MAJOR}=="", GOTO="persistent_v4l_end" + +IMPORT{program}="v4l_id $devnode" + +SUBSYSTEMS=="usb", IMPORT{builtin}="usb_id" +KERNEL=="video*", ENV{ID_SERIAL}=="?*", SYMLINK+="v4l/by-id/$env{ID_BUS}-$env{ID_SERIAL}-video-index$attr{index}" + +# check for valid "index" number +TEST!="index", GOTO="persistent_v4l_end" +ATTR{index}!="?*", GOTO="persistent_v4l_end" + +IMPORT{builtin}="path_id" +ENV{ID_PATH}=="?*", KERNEL=="video*|vbi*", SYMLINK+="v4l/by-path/$env{ID_PATH}-video-index$attr{index}" +ENV{ID_PATH}=="?*", KERNEL=="audio*", SYMLINK+="v4l/by-path/$env{ID_PATH}-audio-index$attr{index}" + +LABEL="persistent_v4l_end" diff --git a/src/libbasic/include/basic/basic.target b/src/libbasic/include/basic/basic.target new file mode 100644 index 0000000000..3e3527f894 --- /dev/null +++ b/src/libbasic/include/basic/basic.target @@ -0,0 +1,20 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Basic System +Documentation=man:systemd.special(7) +Requires=sysinit.target +Wants=sockets.target timers.target paths.target slices.target +After=sysinit.target sockets.target paths.target slices.target tmp.mount + +# We support /var, /tmp, /var/tmp, being on NFS, but we don't pull in +# remote-fs.target by default, hence pull them in explicitly here. Note that we +# require /var and /var/tmp, but only add a Wants= type dependency on /tmp, as +# we support that unit being masked, and this should not be considered an error. +RequiresMountsFor=/var /var/tmp +Wants=tmp.mount diff --git a/src/libsystemd/Makefile b/src/libsystemd/Makefile index 5c5d03a51d..5c26912ef1 100644 --- a/src/libsystemd/Makefile +++ b/src/libsystemd/Makefile @@ -119,6 +119,5 @@ $(DESTDIR)$(pkgincludedir)/%.h: $(srcdir)/include/systemd/%.h files.sys.all += $(addprefix $(pkgincludedir)/,$(notdir $(_pkginclude_HEADERS))) $(outdir)/libsystemd.la: $(srcdir)/libsystemd.sym nested.subdirs += src -sd.sed_files += libsystemd.pc include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/libudev/Makefile b/src/libudev/Makefile index b4c91d1738..0ca3d15841 100644 --- a/src/libudev/Makefile +++ b/src/libudev/Makefile @@ -57,8 +57,6 @@ test_libudev_sym_CFLAGS = \ test_libudev_sym_LDADD = \ libudev.la -sd.sed_files += libudev.pc - nested.subdirs += src include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/systemd-cryptsetup/cryptsetup-pre.target b/src/systemd-cryptsetup/cryptsetup-pre.target new file mode 100644 index 0000000000..65353419fc --- /dev/null +++ b/src/systemd-cryptsetup/cryptsetup-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 Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Encrypted Volumes (Pre) +Documentation=man:systemd.special(7) +RefuseManualStart=yes diff --git a/src/systemd-cryptsetup/cryptsetup.target b/src/systemd-cryptsetup/cryptsetup.target new file mode 100644 index 0000000000..25d3e33f6a --- /dev/null +++ b/src/systemd-cryptsetup/cryptsetup.target @@ -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 Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Encrypted Volumes +Documentation=man:systemd.special(7) diff --git a/src/systemd-dbus1-generator/Makefile b/src/systemd-dbus1-generator/Makefile index e2c8ca1efc..8a4193621a 100644 --- a/src/systemd-dbus1-generator/Makefile +++ b/src/systemd-dbus1-generator/Makefile @@ -40,7 +40,8 @@ dbus1-generator-install-hook: dbus1-generator-uninstall-hook: rm -f $(DESTDIR)$(usergeneratordir)/systemd-dbus1-generator -dist_xinitrc_SCRIPTS = \ +# TODO +_dist_xinitrc_SCRIPTS = \ xorg/50-systemd-user.sh INSTALL_EXEC_HOOKS += dbus1-generator-install-hook diff --git a/src/systemd-hibernate-resume/Makefile b/src/systemd-hibernate-resume/Makefile deleted file mode 100644 index 1e085e4b28..0000000000 --- a/src/systemd-hibernate-resume/Makefile +++ /dev/null @@ -1,62 +0,0 @@ -# -*- Mode: makefile; indent-tabs-mode: t -*- -# -# This file is part of systemd. -# -# Copyright 2010-2012 Lennart Poettering -# Copyright 2010-2012 Kay Sievers -# Copyright 2013 Zbigniew Jędrzejewski-Szmek -# Copyright 2013 David Strauss -# Copyright 2016 Luke Shumaker -# -# systemd is free software; you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation; either version 2.1 of the License, or -# (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 -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with systemd; If not, see . -include $(dir $(lastword $(MAKEFILE_LIST)))/../../config.mk -include $(topsrcdir)/build-aux/Makefile.head.mk - -ifneq ($(ENABLE_HIBERNATE),) -systemgenerator_PROGRAMS += \ - systemd-hibernate-resume-generator - -rootlibexec_PROGRAMS += \ - systemd-hibernate-resume - -systemd_hibernate_resume_SOURCES = \ - src/hibernate-resume/hibernate-resume.c - -systemd_hibernate_resume_LDADD = \ - libshared.la - -systemd_hibernate_resume_generator_SOURCES = \ - src/hibernate-resume/hibernate-resume-generator.c - -systemd_hibernate_resume_generator_LDADD = \ - libshared.la - -dist_systemunit_DATA += \ - units/hibernate.target \ - units/hybrid-sleep.target - -nodist_systemunit_DATA += \ - units/systemd-hibernate.service \ - units/systemd-hibernate-resume@.service \ - units/systemd-hybrid-sleep.service -endif # ENABLE_HIBERNATE - -EXTRA_DIST += \ - units/systemd-hibernate.service.in \ - units/systemd-hibernate-resume@.service.in \ - units/systemd-hybrid-sleep.service.in - -sd.CPPFLAGS += -DSYSTEM_DATA_UNIT_PATH=\"$(systemunitdir)\" - -include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/systemd-hibernate-resume/hibernate-resume-generator.c b/src/systemd-hibernate-resume/hibernate-resume-generator.c deleted file mode 100644 index 6380c297c2..0000000000 --- a/src/systemd-hibernate-resume/hibernate-resume-generator.c +++ /dev/null @@ -1,99 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2014 Ivan Shapovalov - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include - -#include "basic/alloc-util.h" -#include "basic/log.h" -#include "basic/mkdir.h" -#include "basic/proc-cmdline.h" -#include "basic/special.h" -#include "basic/string-util.h" -#include "basic/unit-name.h" -#include "basic/util.h" -#include "shared/fstab-util.h" - -static const char *arg_dest = "/tmp"; -static char *arg_resume_dev = NULL; - -static int parse_proc_cmdline_item(const char *key, const char *value) { - - if (streq(key, "resume") && value) { - free(arg_resume_dev); - arg_resume_dev = fstab_node_to_udev_node(value); - if (!arg_resume_dev) - return log_oom(); - } - - return 0; -} - -static int process_resume(void) { - _cleanup_free_ char *name = NULL, *lnk = NULL; - int r; - - if (!arg_resume_dev) - return 0; - - r = unit_name_from_path_instance("systemd-hibernate-resume", arg_resume_dev, ".service", &name); - if (r < 0) - return log_error_errno(r, "Failed to generate unit name: %m"); - - lnk = strjoin(arg_dest, "/" SPECIAL_SYSINIT_TARGET ".wants/", name, NULL); - if (!lnk) - return log_oom(); - - mkdir_parents_label(lnk, 0755); - if (symlink(SYSTEM_DATA_UNIT_PATH "/systemd-hibernate-resume@.service", lnk) < 0) - return log_error_errno(errno, "Failed to create symlink %s: %m", lnk); - - return 0; -} - -int main(int argc, char *argv[]) { - int r = 0; - - if (argc > 1 && argc != 4) { - log_error("This program takes three or no arguments."); - return EXIT_FAILURE; - } - - if (argc > 1) - arg_dest = argv[1]; - - log_set_target(LOG_TARGET_SAFE); - log_parse_environment(); - log_open(); - - umask(0022); - - /* Don't even consider resuming outside of initramfs. */ - if (!in_initrd()) - return EXIT_SUCCESS; - - r = parse_proc_cmdline(parse_proc_cmdline_item); - if (r < 0) - log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m"); - - r = process_resume(); - free(arg_resume_dev); - - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; -} diff --git a/src/systemd-hibernate-resume/hibernate-resume.c b/src/systemd-hibernate-resume/hibernate-resume.c deleted file mode 100644 index 64d14bf64b..0000000000 --- a/src/systemd-hibernate-resume/hibernate-resume.c +++ /dev/null @@ -1,82 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2014 Ivan Shapovalov - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include - -#include "basic/alloc-util.h" -#include "basic/fileio.h" -#include "basic/log.h" -#include "basic/util.h" - -int main(int argc, char *argv[]) { - struct stat st; - const char *device; - _cleanup_free_ char *major_minor = NULL; - int r; - - if (argc != 2) { - log_error("This program expects one argument."); - return EXIT_FAILURE; - } - - log_set_target(LOG_TARGET_AUTO); - log_parse_environment(); - log_open(); - - umask(0022); - - /* Refuse to run unless we are in an initrd() */ - if (!in_initrd()) - return EXIT_SUCCESS; - - device = argv[1]; - - if (stat(device, &st) < 0) { - log_error_errno(errno, "Failed to stat '%s': %m", device); - return EXIT_FAILURE; - } - - if (!S_ISBLK(st.st_mode)) { - log_error("Resume device '%s' is not a block device.", device); - return EXIT_FAILURE; - } - - if (asprintf(&major_minor, "%d:%d", major(st.st_rdev), minor(st.st_rdev)) < 0) { - log_oom(); - return EXIT_FAILURE; - } - - r = write_string_file("/sys/power/resume", major_minor, WRITE_STRING_FILE_CREATE); - if (r < 0) { - log_error_errno(r, "Failed to write '%s' to /sys/power/resume: %m", major_minor); - return EXIT_FAILURE; - } - - /* - * The write above shall not return. - * - * However, failed resume is a normal condition (may mean that there is - * no hibernation image). - */ - - log_info("Could not resume from '%s' (%s).", device, major_minor); - return EXIT_SUCCESS; -} diff --git a/src/systemd-initctl/systemd-initctl.service.in b/src/systemd-initctl/systemd-initctl.service.in new file mode 100644 index 0000000000..27e663c8dc --- /dev/null +++ b/src/systemd-initctl/systemd-initctl.service.in @@ -0,0 +1,15 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=/dev/initctl Compatibility Daemon +Documentation=man:systemd-initctl.service(8) +DefaultDependencies=no + +[Service] +ExecStart=@rootlibexecdir@/systemd-initctl +NotifyAccess=all diff --git a/src/systemd-initctl/systemd-initctl.socket b/src/systemd-initctl/systemd-initctl.socket new file mode 100644 index 0000000000..f628c2e867 --- /dev/null +++ b/src/systemd-initctl/systemd-initctl.socket @@ -0,0 +1,17 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=/dev/initctl Compatibility Named Pipe +Documentation=man:systemd-initctl.service(8) +DefaultDependencies=no +Before=sockets.target + +[Socket] +ListenFIFO=/run/systemd/initctl/fifo +Symlinks=/dev/initctl +SocketMode=0600 diff --git a/src/systemd-modules-load/Makefile b/src/systemd-modules-load/Makefile deleted file mode 100644 index 1c51b134ad..0000000000 --- a/src/systemd-modules-load/Makefile +++ /dev/null @@ -1,60 +0,0 @@ -# -*- Mode: makefile; indent-tabs-mode: t -*- -# -# This file is part of systemd. -# -# Copyright 2010-2012 Lennart Poettering -# Copyright 2010-2012 Kay Sievers -# Copyright 2013 Zbigniew Jędrzejewski-Szmek -# Copyright 2013 David Strauss -# Copyright 2016 Luke Shumaker -# -# systemd is free software; you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation; either version 2.1 of the License, or -# (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 -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with systemd; If not, see . -include $(dir $(lastword $(MAKEFILE_LIST)))/../../config.mk -include $(topsrcdir)/build-aux/Makefile.head.mk - -ifneq ($(HAVE_KMOD),) -systemd_modules_load_SOURCES = \ - src/modules-load/modules-load.c - -systemd_modules_load_CFLAGS = \ - $(AM_CFLAGS) \ - $(KMOD_CFLAGS) - -systemd_modules_load_LDADD = \ - libshared.la \ - $(KMOD_LIBS) - -rootlibexec_PROGRAMS += \ - systemd-modules-load - -nodist_systemunit_DATA += \ - units/systemd-modules-load.service - -SYSINIT_TARGET_WANTS += \ - systemd-modules-load.service - -ifneq ($(ENABLE_TMPFILES),) -nodist_systemunit_DATA += \ - units/kmod-static-nodes.service - -SYSINIT_TARGET_WANTS += \ - kmod-static-nodes.service -endif # ENABLE_TMPFILES -endif # HAVE_KMOD - -EXTRA_DIST += \ - units/systemd-modules-load.service.in \ - units/kmod-static-nodes.service.in - -include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/systemd-modules-load/modules-load.c b/src/systemd-modules-load/modules-load.c deleted file mode 100644 index d7ea7886c6..0000000000 --- a/src/systemd-modules-load/modules-load.c +++ /dev/null @@ -1,283 +0,0 @@ -/*** - 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 Lesser General Public License as published by - the Free Software Foundation; either version 2.1 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include - -#include "basic/conf-files.h" -#include "basic/def.h" -#include "basic/fd-util.h" -#include "basic/fileio.h" -#include "basic/log.h" -#include "basic/proc-cmdline.h" -#include "basic/string-util.h" -#include "basic/strv.h" -#include "basic/util.h" - -static char **arg_proc_cmdline_modules = NULL; - -static const char conf_file_dirs[] = CONF_PATHS_NULSTR("modules-load.d"); - -static void systemd_kmod_log(void *data, int priority, const char *file, int line, - const char *fn, const char *format, va_list args) { - - DISABLE_WARNING_FORMAT_NONLITERAL; - log_internalv(priority, 0, file, line, fn, format, args); - REENABLE_WARNING; -} - -static int add_modules(const char *p) { - _cleanup_strv_free_ char **k = NULL; - - k = strv_split(p, ","); - if (!k) - return log_oom(); - - if (strv_extend_strv(&arg_proc_cmdline_modules, k, true) < 0) - return log_oom(); - - return 0; -} - -static int parse_proc_cmdline_item(const char *key, const char *value) { - int r; - - if (STR_IN_SET(key, "modules-load", "rd.modules-load") && value) { - r = add_modules(value); - if (r < 0) - return r; - } - - return 0; -} - -static int load_module(struct kmod_ctx *ctx, const char *m) { - const int probe_flags = KMOD_PROBE_APPLY_BLACKLIST; - struct kmod_list *itr, *modlist = NULL; - int r = 0; - - log_debug("load: %s", m); - - r = kmod_module_new_from_lookup(ctx, m, &modlist); - if (r < 0) - return log_error_errno(r, "Failed to lookup alias '%s': %m", m); - - if (!modlist) { - log_error("Failed to find module '%s'", m); - return -ENOENT; - } - - kmod_list_foreach(itr, modlist) { - struct kmod_module *mod; - int state, err; - - mod = kmod_module_get_module(itr); - state = kmod_module_get_initstate(mod); - - switch (state) { - case KMOD_MODULE_BUILTIN: - log_info("Module '%s' is builtin", kmod_module_get_name(mod)); - break; - - case KMOD_MODULE_LIVE: - log_debug("Module '%s' is already loaded", kmod_module_get_name(mod)); - break; - - default: - 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_errno(err, "Failed to insert '%s': %m", kmod_module_get_name(mod)); - r = err; - } - } - - kmod_module_unref(mod); - } - - kmod_module_unref_list(modlist); - - return r; -} - -static int apply_file(struct kmod_ctx *ctx, const char *path, bool ignore_enoent) { - _cleanup_fclose_ FILE *f = NULL; - int r; - - assert(ctx); - assert(path); - - r = search_and_fopen_nulstr(path, "re", NULL, conf_file_dirs, &f); - if (r < 0) { - if (ignore_enoent && r == -ENOENT) - return 0; - - return log_error_errno(r, "Failed to open %s, ignoring: %m", path); - } - - log_debug("apply: %s", path); - for (;;) { - char line[LINE_MAX], *l; - int k; - - if (!fgets(line, sizeof(line), f)) { - if (feof(f)) - break; - - return log_error_errno(errno, "Failed to read file '%s', ignoring: %m", path); - } - - l = strstrip(line); - if (!*l) - continue; - if (strchr(COMMENTS "\n", *l)) - continue; - - k = load_module(ctx, l); - if (k < 0 && r == 0) - r = k; - } - - return r; -} - -static void help(void) { - printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n" - "Loads statically configured kernel modules.\n\n" - " -h --help Show this help\n" - " --version Show package version\n", - program_invocation_short_name); -} - -static int parse_argv(int argc, char *argv[]) { - - enum { - ARG_VERSION = 0x100, - }; - - static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, ARG_VERSION }, - {} - }; - - int c; - - assert(argc >= 0); - assert(argv); - - while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) - - switch (c) { - - case 'h': - help(); - return 0; - - case ARG_VERSION: - return version(); - - case '?': - return -EINVAL; - - default: - assert_not_reached("Unhandled option"); - } - - return 1; -} - -int main(int argc, char *argv[]) { - int r, k; - struct kmod_ctx *ctx; - - 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); - - r = parse_proc_cmdline(parse_proc_cmdline_item); - if (r < 0) - log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m"); - - ctx = kmod_new(NULL, NULL); - if (!ctx) { - log_error("Failed to allocate memory for kmod."); - goto finish; - } - - kmod_load_resources(ctx); - kmod_set_log_fn(ctx, systemd_kmod_log, NULL); - - r = 0; - - if (argc > optind) { - int i; - - for (i = optind; i < argc; i++) { - k = apply_file(ctx, argv[i], false); - if (k < 0 && r == 0) - r = k; - } - - } else { - _cleanup_strv_free_ char **files = NULL; - char **fn, **i; - - STRV_FOREACH(i, arg_proc_cmdline_modules) { - k = load_module(ctx, *i); - if (k < 0 && r == 0) - r = k; - } - - k = conf_files_list_nulstr(&files, ".conf", NULL, conf_file_dirs); - if (k < 0) { - log_error_errno(k, "Failed to enumerate modules-load.d files: %m"); - if (r == 0) - r = k; - goto finish; - } - - STRV_FOREACH(fn, files) { - k = apply_file(ctx, *fn, true); - if (k < 0 && r == 0) - r = k; - } - } - -finish: - kmod_unref(ctx); - strv_free(arg_proc_cmdline_modules); - - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; -} diff --git a/src/systemd-nspawn/Makefile b/src/systemd-nspawn/Makefile index 15db3ca3b2..2fe9bcc143 100644 --- a/src/systemd-nspawn/Makefile +++ b/src/systemd-nspawn/Makefile @@ -82,6 +82,9 @@ test_patch_uid_LDADD = \ manual_tests += \ test-patch-uid +dist_tmpfiles_DATA = \ + tmpfiles.d/systemd-nspawn.conf + sd.CPPFLAGS += -I$(topsrcdir)/src/libudev/src include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/systemd-nspawn/systemd-nspawn.tmpfiles b/src/systemd-nspawn/systemd-nspawn.tmpfiles new file mode 100644 index 0000000000..9fa3878d6b --- /dev/null +++ b/src/systemd-nspawn/systemd-nspawn.tmpfiles @@ -0,0 +1,23 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +# See tmpfiles.d(5) for details + +Q /var/lib/machines 0700 - - - + +# Remove old temporary snapshots, but only at boot. Ideally we'd have +# "self-destroying" btrfs snapshots that go away if the last last +# reference to it does. To mimic a scheme like this at least remove +# the old snapshots on fresh boots, where we know they cannot be +# referenced anymore. Note that we actually remove all temporary files +# in /var/lib/machines/ at boot, which should be safe since the +# directory has defined semantics. In the root directory (where +# systemd-nspawn --ephemeral places snapshots) we are more strict, to +# avoid removing unrelated temporary files. + +R! /var/lib/machines/.#* +R! /.#machine.* diff --git a/src/systemd-nspawn/systemd-nspawn@.service.in b/src/systemd-nspawn/systemd-nspawn@.service.in new file mode 100644 index 0000000000..ea28941507 --- /dev/null +++ b/src/systemd-nspawn/systemd-nspawn@.service.in @@ -0,0 +1,46 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Container %i +Documentation=man:systemd-nspawn(1) +PartOf=machines.target +Before=machines.target +After=network.target + +[Service] +ExecStart=@bindir@/systemd-nspawn --quiet --keep-unit --boot --link-journal=try-guest --network-veth -U --settings=override --machine=%i +KillMode=mixed +Type=notify +RestartForceExitStatus=133 +SuccessExitStatus=133 +Slice=machine.slice +Delegate=yes +TasksMax=8192 + +# Enforce a strict device policy, similar to the one nspawn configures +# when it allocates its own scope unit. Make sure to keep these +# policies in sync if you change them! +DevicePolicy=strict +DeviceAllow=/dev/null rwm +DeviceAllow=/dev/zero rwm +DeviceAllow=/dev/full rwm +DeviceAllow=/dev/random rwm +DeviceAllow=/dev/urandom rwm +DeviceAllow=/dev/tty rwm +DeviceAllow=/dev/net/tun rwm +DeviceAllow=/dev/pts/ptmx rw +DeviceAllow=char-pts rw + +# nspawn itself needs access to /dev/loop-control and /dev/loop, to +# implement the --image= option. Add these here, too. +DeviceAllow=/dev/loop-control rw +DeviceAllow=block-loop rw +DeviceAllow=block-blkext rw + +[Install] +WantedBy=machines.target diff --git a/src/systemd-remount-fs/systemd-remount-fs.service.in b/src/systemd-remount-fs/systemd-remount-fs.service.in new file mode 100644 index 0000000000..8d9daacaa5 --- /dev/null +++ b/src/systemd-remount-fs/systemd-remount-fs.service.in @@ -0,0 +1,22 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Remount Root and Kernel File Systems +Documentation=man:systemd-remount-fs.service(8) +Documentation=http://www.freedesktop.org/wiki/Software/systemd/APIFileSystems +DefaultDependencies=no +Conflicts=shutdown.target +After=systemd-fsck-root.service +Before=local-fs-pre.target local-fs.target shutdown.target +Wants=local-fs-pre.target +ConditionPathExists=/etc/fstab + +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStart=@rootlibexecdir@/systemd-remount-fs diff --git a/src/systemd-timesyncd/systemd-timesyncd.service.in b/src/systemd-timesyncd/systemd-timesyncd.service.in new file mode 100644 index 0000000000..a856dad709 --- /dev/null +++ b/src/systemd-timesyncd/systemd-timesyncd.service.in @@ -0,0 +1,33 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Network Time Synchronization +Documentation=man:systemd-timesyncd.service(8) +ConditionCapability=CAP_SYS_TIME +ConditionVirtualization=!container +DefaultDependencies=no +RequiresMountsFor=/var/lib/systemd/clock +After=systemd-remount-fs.service systemd-tmpfiles-setup.service systemd-sysusers.service +Before=time-sync.target sysinit.target shutdown.target +Conflicts=shutdown.target +Wants=time-sync.target + +[Service] +Type=notify +Restart=always +RestartSec=0 +ExecStart=@rootlibexecdir@/systemd-timesyncd +CapabilityBoundingSet=CAP_SYS_TIME CAP_SETUID CAP_SETGID CAP_SETPCAP CAP_CHOWN CAP_DAC_OVERRIDE CAP_FOWNER +PrivateTmp=yes +PrivateDevices=yes +ProtectSystem=full +ProtectHome=yes +WatchdogSec=3min + +[Install] +WantedBy=sysinit.target -- cgit v1.2.3-54-g00ecf