summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/activate/activate.c443
-rw-r--r--src/analyze/systemd-analyze.c1270
-rwxr-xr-xsrc/analyze/systemd-analyze.in328
-rw-r--r--src/ask-password/ask-password.c2
-rw-r--r--src/binfmt/binfmt.c135
l---------src/boot/Makefile1
-rw-r--r--src/boot/boot-efi.c190
-rw-r--r--src/boot/boot-loader.c132
-rw-r--r--src/boot/boot-loader.h25
-rw-r--r--src/boot/boot.h64
-rw-r--r--src/boot/bootctl.c288
l---------src/bootchart/Makefile1
-rw-r--r--src/bootchart/README83
-rw-r--r--src/bootchart/bootchart.c765
-rw-r--r--src/bootchart/bootchart.conf24
-rw-r--r--src/bootchart/bootchart.h164
-rw-r--r--src/bootchart/log.c420
-rwxr-xr-xsrc/bootchart/store.c504
-rw-r--r--src/bootchart/store.h35
-rw-r--r--src/bootchart/svg.c2234
-rw-r--r--src/bootchart/svg.h27
-rw-r--r--src/cgls/cgls.c89
-rw-r--r--src/cgtop/cgtop.c155
-rw-r--r--src/core/automount.c42
-rw-r--r--src/core/automount.h10
-rw-r--r--src/core/bus-errors.h13
-rw-r--r--src/core/cgroup-attr.c62
-rw-r--r--src/core/cgroup-attr.h12
-rw-r--r--src/core/cgroup-semantics.c333
-rw-r--r--src/core/cgroup-semantics.h43
-rw-r--r--src/core/cgroup.c112
-rw-r--r--src/core/cgroup.h5
-rw-r--r--src/core/condition.c5
-rw-r--r--src/core/condition.h4
-rw-r--r--src/core/dbus-execute.c10
-rw-r--r--src/core/dbus-execute.h5
-rw-r--r--src/core/dbus-job.c25
-rw-r--r--src/core/dbus-manager.c306
-rw-r--r--src/core/dbus-mount.c2
-rw-r--r--src/core/dbus-path.c6
-rw-r--r--src/core/dbus-service.c2
-rw-r--r--src/core/dbus-snapshot.c20
-rw-r--r--src/core/dbus-socket.c64
-rw-r--r--src/core/dbus-swap.c2
-rw-r--r--src/core/dbus-timer.c79
-rw-r--r--src/core/dbus-unit.c467
-rw-r--r--src/core/dbus-unit.h42
-rw-r--r--src/core/dbus.c38
-rw-r--r--src/core/device.c24
-rw-r--r--src/core/device.h4
-rw-r--r--src/core/execute.c370
-rw-r--r--src/core/execute.h20
-rw-r--r--src/core/hostname-setup.c3
-rw-r--r--src/core/ima-setup.c5
-rw-r--r--src/core/job.c119
-rw-r--r--src/core/job.h30
-rw-r--r--src/core/kill.h10
-rw-r--r--src/core/killall.c99
-rw-r--r--src/core/kmod-setup.c72
-rw-r--r--src/core/load-dropin.c129
-rw-r--r--src/core/load-dropin.h1
-rw-r--r--src/core/load-fragment-gperf.gperf.m454
-rw-r--r--src/core/load-fragment.c1772
-rw-r--r--src/core/load-fragment.h102
-rw-r--r--src/core/locale-setup.c5
-rw-r--r--src/core/loopback-setup.c93
-rw-r--r--src/core/machine-id-setup.c119
-rw-r--r--src/core/macros.systemd.in4
-rw-r--r--src/core/main.c395
-rw-r--r--src/core/manager.c422
-rw-r--r--src/core/manager.h18
-rw-r--r--src/core/mount-setup.c114
-rw-r--r--src/core/mount.c226
-rw-r--r--src/core/mount.h12
-rw-r--r--src/core/namespace.c237
-rw-r--r--src/core/namespace.h14
-rw-r--r--src/core/path.c258
-rw-r--r--src/core/path.h18
-rw-r--r--src/core/selinux-access.c28
-rw-r--r--src/core/selinux-setup.c5
-rw-r--r--src/core/service.c519
-rw-r--r--src/core/service.h28
-rw-r--r--src/core/shutdown.c5
-rw-r--r--src/core/smack-setup.c151
-rw-r--r--src/core/smack-setup.h26
-rw-r--r--src/core/snapshot.c5
-rw-r--r--src/core/snapshot.h4
-rw-r--r--src/core/socket.c199
-rw-r--r--src/core/socket.h23
-rw-r--r--src/core/special.h10
-rw-r--r--src/core/swap.c149
-rw-r--r--src/core/swap.h12
-rw-r--r--src/core/sync.c65
-rw-r--r--src/core/sync.h24
-rw-r--r--src/core/syscall-list.c4
-rw-r--r--src/core/syscall-list.h14
-rw-r--r--src/core/system.conf2
-rw-r--r--src/core/target.c4
-rw-r--r--src/core/target.h4
-rw-r--r--src/core/timer.c162
-rw-r--r--src/core/timer.h21
-rw-r--r--src/core/transaction.c30
-rw-r--r--src/core/transaction.h3
-rw-r--r--src/core/umount.c35
-rw-r--r--src/core/unit-printf.c82
-rw-r--r--src/core/unit.c714
-rw-r--r--src/core/unit.h64
-rw-r--r--src/core/user.conf2
-rw-r--r--src/cryptsetup/cryptsetup-generator.c365
-rw-r--r--src/cryptsetup/cryptsetup.c103
-rw-r--r--src/delta/delta.c14
l---------src/efi-boot-generator/Makefile1
-rw-r--r--src/efi-boot-generator/efi-boot-generator.c123
-rw-r--r--src/fsck/fsck.c14
-rw-r--r--src/fstab-generator/fstab-generator.c233
-rw-r--r--src/getty-generator/getty-generator.c3
-rw-r--r--src/hostname/hostnamectl.c69
-rw-r--r--src/hostname/hostnamed.c21
-rw-r--r--src/initctl/initctl.c22
-rw-r--r--src/journal/cat.c2
-rw-r--r--src/journal/catalog.c268
-rw-r--r--src/journal/catalog.h17
-rw-r--r--src/journal/coredump.c85
-rw-r--r--src/journal/coredumpctl.c35
-rw-r--r--src/journal/fsprg.c2
-rw-r--r--src/journal/fsprg.h10
-rw-r--r--src/journal/journal-file.c95
-rw-r--r--src/journal/journal-file.h8
-rw-r--r--src/journal/journal-gatewayd.c258
-rw-r--r--src/journal/journal-internal.h22
-rw-r--r--src/journal/journal-send.c96
-rw-r--r--src/journal/journal-vacuum.c4
-rw-r--r--src/journal/journalctl.c458
-rw-r--r--src/journal/journald-gperf.gperf7
-rw-r--r--src/journal/journald-kmsg.c8
-rw-r--r--src/journal/journald-native.c11
-rw-r--r--src/journal/journald-rate-limit.c17
-rw-r--r--src/journal/journald-server.c466
-rw-r--r--src/journal/journald-server.h20
-rw-r--r--src/journal/journald-stream.c11
-rw-r--r--src/journal/journald-syslog.c33
-rw-r--r--src/journal/journald-syslog.h2
-rw-r--r--src/journal/journald.c1
-rw-r--r--src/journal/journald.conf1
-rw-r--r--src/journal/libsystemd-journal.sym17
-rw-r--r--src/journal/lookup3.h8
-rw-r--r--src/journal/microhttpd-util.c37
-rw-r--r--src/journal/microhttpd-util.h28
-rw-r--r--src/journal/mmap-cache.c7
-rw-r--r--src/journal/sd-journal.c314
-rw-r--r--src/journal/test-catalog.c98
-rw-r--r--src/journal/test-journal-enum.c6
-rw-r--r--src/journal/test-journal-match.c21
-rw-r--r--src/journal/test-journal-stream.c10
-rw-r--r--src/journal/test-journal-syslog.c6
-rw-r--r--src/journal/test-journal-verify.c2
-rw-r--r--src/kernel-install/50-depmod.install8
-rw-r--r--src/kernel-install/90-loaderentry.install77
-rw-r--r--src/kernel-install/kernel-install123
l---------src/libsystemd-bus/Makefile1
-rw-r--r--src/libsystemd-bus/bus-bloom.c93
-rw-r--r--src/libsystemd-bus/bus-bloom.h30
-rw-r--r--src/libsystemd-bus/bus-control.c378
-rw-r--r--src/libsystemd-bus/bus-control.h27
-rw-r--r--src/libsystemd-bus/bus-error.c177
-rw-r--r--src/libsystemd-bus/bus-error.h31
-rw-r--r--src/libsystemd-bus/bus-internal.c256
-rw-r--r--src/libsystemd-bus/bus-internal.h198
-rw-r--r--src/libsystemd-bus/bus-kernel.c618
-rw-r--r--src/libsystemd-bus/bus-kernel.h32
-rw-r--r--src/libsystemd-bus/bus-match.c994
-rw-r--r--src/libsystemd-bus/bus-match.h82
-rw-r--r--src/libsystemd-bus/bus-message.c3494
-rw-r--r--src/libsystemd-bus/bus-message.h198
-rw-r--r--src/libsystemd-bus/bus-signature.c153
-rw-r--r--src/libsystemd-bus/bus-signature.h31
-rw-r--r--src/libsystemd-bus/bus-socket.c1059
-rw-r--r--src/libsystemd-bus/bus-socket.h36
-rw-r--r--src/libsystemd-bus/bus-type.c163
-rw-r--r--src/libsystemd-bus/bus-type.h34
-rw-r--r--src/libsystemd-bus/busctl.c340
-rw-r--r--src/libsystemd-bus/kdbus.h418
-rw-r--r--src/libsystemd-bus/sd-bus.c2425
-rw-r--r--src/libsystemd-bus/test-bus-chat.c576
-rw-r--r--src/libsystemd-bus/test-bus-kernel.c162
-rw-r--r--src/libsystemd-bus/test-bus-marshal.c175
-rw-r--r--src/libsystemd-bus/test-bus-match.c114
-rw-r--r--src/libsystemd-bus/test-bus-server.c223
-rw-r--r--src/libsystemd-bus/test-bus-signature.c116
-rw-r--r--src/libsystemd-daemon/sd-daemon.c66
-rw-r--r--src/libsystemd-id128/sd-id128.c64
-rw-r--r--src/libudev/libudev-device-private.c8
-rw-r--r--src/libudev/libudev-device.c156
-rw-r--r--src/libudev/libudev-enumerate.c14
-rw-r--r--src/libudev/libudev-hwdb.c10
-rw-r--r--src/libudev/libudev-monitor.c8
-rw-r--r--src/libudev/libudev-private.h6
-rw-r--r--src/libudev/libudev-queue.c4
-rw-r--r--src/libudev/libudev-util.c117
-rw-r--r--src/libudev/libudev.c2
-rw-r--r--src/libudev/libudev.h1
-rw-r--r--src/libudev/libudev.pc.in4
-rw-r--r--src/libudev/libudev.sym5
-rw-r--r--src/locale/kbd-model-map1
-rw-r--r--src/locale/localectl.c196
-rw-r--r--src/locale/localed.c38
-rw-r--r--src/login/70-uaccess.rules1
-rw-r--r--src/login/71-seat.rules.in4
-rw-r--r--src/login/inhibit.c85
-rw-r--r--src/login/libsystemd-login.sym22
-rw-r--r--src/login/loginctl.c328
-rw-r--r--src/login/logind-action.c18
-rw-r--r--src/login/logind-action.h6
-rw-r--r--src/login/logind-button.c36
-rw-r--r--src/login/logind-dbus.c607
-rw-r--r--src/login/logind-gperf.gperf4
-rw-r--r--src/login/logind-inhibit.c25
-rw-r--r--src/login/logind-seat-dbus.c70
-rw-r--r--src/login/logind-session-dbus.c78
-rw-r--r--src/login/logind-session.c32
-rw-r--r--src/login/logind-session.h33
-rw-r--r--src/login/logind-user-dbus.c58
-rw-r--r--src/login/logind-user.c17
-rw-r--r--src/login/logind-user.h4
-rw-r--r--src/login/logind.c163
-rw-r--r--src/login/logind.h23
-rw-r--r--src/login/org.freedesktop.login1.policy.in2
-rw-r--r--src/login/pam-module.c44
-rw-r--r--src/login/sd-login.c366
-rw-r--r--src/login/sysfs-show.c8
-rw-r--r--src/login/test-login.c31
-rw-r--r--src/login/uaccess.c91
-rw-r--r--src/login/user-sessions.c3
-rw-r--r--src/modules-load/modules-load.c188
-rw-r--r--src/notify/notify.c1
-rw-r--r--src/nspawn/nspawn.c489
l---------src/nss-myhostname/Makefile1
-rw-r--r--src/nss-myhostname/netlink.c256
-rw-r--r--src/nss-myhostname/nss-myhostname.c203
-rw-r--r--src/python-systemd/.gitignore2
-rw-r--r--src/python-systemd/_daemon.c325
-rw-r--r--src/python-systemd/_journal.c25
-rw-r--r--src/python-systemd/_reader.c1131
-rw-r--r--src/python-systemd/daemon.py54
-rw-r--r--src/python-systemd/docs/.gitignore1
-rw-r--r--src/python-systemd/docs/conf.py279
-rw-r--r--src/python-systemd/docs/daemon.rst16
-rw-r--r--src/python-systemd/docs/default.css196
-rw-r--r--src/python-systemd/docs/id128.rst40
-rw-r--r--src/python-systemd/docs/index.rst24
-rw-r--r--src/python-systemd/docs/journal.rst63
-rw-r--r--src/python-systemd/docs/layout.html17
-rw-r--r--src/python-systemd/docs/login.rst5
-rw-r--r--src/python-systemd/id128.c161
-rw-r--r--src/python-systemd/journal.py412
-rw-r--r--src/python-systemd/login.c176
-rw-r--r--src/python-systemd/pyutil.c46
-rw-r--r--src/python-systemd/pyutil.h49
-rw-r--r--src/quotacheck/quotacheck.c29
-rw-r--r--src/rc-local-generator/rc-local-generator.c2
-rw-r--r--src/readahead/readahead-analyze.c22
-rw-r--r--src/readahead/readahead-collect.c67
-rw-r--r--src/readahead/readahead-common.c40
-rw-r--r--src/readahead/readahead.c2
-rw-r--r--src/readahead/sd-readahead.c16
-rw-r--r--src/remount-fs/remount-fs.c3
-rw-r--r--src/reply-password/reply-password.c6
-rw-r--r--src/shared/MurmurHash3.c349
-rw-r--r--src/shared/MurmurHash3.h38
-rw-r--r--src/shared/acl-util.c60
-rw-r--r--src/shared/acl-util.h3
-rw-r--r--src/shared/ask-password-api.c27
-rw-r--r--src/shared/audit.c10
-rw-r--r--src/shared/calendarspec.c16
-rw-r--r--src/shared/capability.c3
-rw-r--r--src/shared/cgroup-label.c37
-rw-r--r--src/shared/cgroup-show.c167
-rw-r--r--src/shared/cgroup-show.h10
-rw-r--r--src/shared/cgroup-util.c1125
-rw-r--r--src/shared/cgroup-util.h52
-rw-r--r--src/shared/conf-files.c135
-rw-r--r--src/shared/conf-files.h5
-rw-r--r--src/shared/conf-parser.c817
-rw-r--r--src/shared/conf-parser.h107
-rw-r--r--src/shared/dbus-common.c105
-rw-r--r--src/shared/dbus-common.h26
-rw-r--r--src/shared/dbus-loop.c38
-rw-r--r--src/shared/efivars.c503
-rw-r--r--src/shared/efivars.h47
-rw-r--r--src/shared/env-util.c415
-rw-r--r--src/shared/env-util.h44
-rw-r--r--src/shared/exit-status.h2
-rw-r--r--src/shared/fileio-label.c55
-rw-r--r--src/shared/fileio-label.h29
-rw-r--r--src/shared/fileio.c596
-rw-r--r--src/shared/fileio.h37
-rw-r--r--src/shared/hashmap.c27
-rw-r--r--src/shared/hashmap.h37
-rw-r--r--src/shared/hwclock.c94
-rw-r--r--src/shared/install-printf.c132
-rw-r--r--src/shared/install-printf.h25
-rw-r--r--src/shared/install.c552
-rw-r--r--src/shared/install.h18
-rw-r--r--src/shared/label.c6
-rw-r--r--src/shared/label.h4
-rw-r--r--src/shared/log.c161
-rw-r--r--src/shared/log.h16
-rw-r--r--src/shared/logs-show.c186
-rw-r--r--src/shared/logs-show.h42
-rw-r--r--src/shared/macro.h87
-rw-r--r--src/shared/missing.h8
-rw-r--r--src/shared/output-mode.h44
-rw-r--r--src/shared/pager.c7
-rw-r--r--src/shared/pager.h6
-rw-r--r--src/shared/path-lookup.c30
-rw-r--r--src/shared/path-lookup.h4
-rw-r--r--src/shared/path-util.c56
-rw-r--r--src/shared/path-util.h34
-rw-r--r--src/shared/polkit.c10
-rw-r--r--src/shared/prioq.c305
-rw-r--r--src/shared/prioq.h40
-rw-r--r--src/shared/ratelimit.c2
-rw-r--r--src/shared/set.c15
-rw-r--r--src/shared/set.h13
-rw-r--r--src/shared/sleep-config.c179
-rw-r--r--src/shared/sleep-config.h26
-rw-r--r--src/shared/socket-util.c48
-rw-r--r--src/shared/socket-util.h12
-rw-r--r--src/shared/spawn-polkit-agent.c12
-rw-r--r--src/shared/specifier.c36
-rw-r--r--src/shared/specifier.h6
-rw-r--r--src/shared/strbuf.c51
-rw-r--r--src/shared/strv.c343
-rw-r--r--src/shared/strv.h49
-rw-r--r--src/shared/strxcpyx.c104
-rw-r--r--src/shared/strxcpyx.h31
-rw-r--r--src/shared/time-dst.c69
-rw-r--r--src/shared/time-util.c164
-rw-r--r--src/shared/time-util.h9
-rw-r--r--src/shared/unit-name.c34
-rw-r--r--src/shared/unit-name.h25
-rw-r--r--src/shared/utf8.c37
-rw-r--r--src/shared/utf8.h2
-rw-r--r--src/shared/util.c1520
-rw-r--r--src/shared/util.h287
-rw-r--r--src/shared/utmp-wtmp.c75
-rw-r--r--src/shared/virt.c33
-rw-r--r--src/shared/watchdog.c2
-rw-r--r--src/shutdownd/shutdownd.c69
-rw-r--r--src/sleep/sleep.c245
-rw-r--r--src/stdio-bridge/stdio-bridge.c418
-rw-r--r--src/sysctl/sysctl.c159
-rw-r--r--src/system-update-generator/system-update-generator.c7
-rw-r--r--src/systemctl/systemctl.c2611
-rw-r--r--src/systemd/sd-bus-protocol.h153
-rw-r--r--src/systemd/sd-bus.h210
-rw-r--r--src/systemd/sd-daemon.h10
-rw-r--r--src/systemd/sd-id128.h2
-rw-r--r--src/systemd/sd-journal.h29
-rw-r--r--src/systemd/sd-login.h31
-rw-r--r--src/systemd/sd-messages.h10
-rw-r--r--src/systemd/sd-shutdown.h4
-rw-r--r--src/test/test-cgroup-util.c183
-rw-r--r--src/test/test-cgroup.c16
-rw-r--r--src/test/test-efivars.c47
-rw-r--r--src/test/test-env-replace.c247
-rw-r--r--src/test/test-fileio.c145
-rw-r--r--src/test/test-hashmap.c508
-rw-r--r--src/test/test-id128.c27
-rw-r--r--src/test/test-ns.c14
-rw-r--r--src/test/test-path-util.c89
-rw-r--r--src/test/test-prioq.c166
-rw-r--r--src/test/test-sched-prio.c12
-rw-r--r--src/test/test-sleep.c30
-rw-r--r--src/test/test-strbuf.c93
-rw-r--r--src/test/test-strv.c251
-rw-r--r--src/test/test-strxcpyx.c101
-rw-r--r--src/test/test-time.c136
-rw-r--r--src/test/test-udev.c7
-rw-r--r--src/test/test-unit-file.c204
-rw-r--r--src/test/test-unit-name.c314
-rw-r--r--src/test/test-util.c472
-rw-r--r--src/timedate/timedatectl.c17
-rw-r--r--src/timedate/timedated.c113
-rw-r--r--src/tmpfiles/tmpfiles.c493
-rw-r--r--src/tty-ask-password-agent/tty-ask-password-agent.c16
-rw-r--r--src/udev/accelerometer/accelerometer.c48
-rw-r--r--src/udev/ata_id/ata_id.c320
-rw-r--r--src/udev/cdrom_id/cdrom_id.c6
-rw-r--r--src/udev/collect/collect.c20
-rw-r--r--src/udev/keymap/95-keyboard-force-release.rules2
-rw-r--r--src/udev/keymap/95-keymap.rules11
-rwxr-xr-xsrc/udev/keymap/findkeyboards2
-rw-r--r--src/udev/keymap/keymap.c2
-rw-r--r--src/udev/scsi_id/scsi_id.c34
-rw-r--r--src/udev/scsi_id/scsi_id.h18
-rw-r--r--src/udev/scsi_id/scsi_serial.c4
-rw-r--r--src/udev/udev-builtin-blkid.c18
-rw-r--r--src/udev/udev-builtin-btrfs.c2
-rw-r--r--src/udev/udev-builtin-firmware.c26
-rw-r--r--src/udev/udev-builtin-kmod.c2
-rw-r--r--src/udev/udev-builtin-net_id.c91
-rw-r--r--src/udev/udev-builtin-path_id.c45
-rw-r--r--src/udev/udev-builtin-uaccess.c3
-rw-r--r--src/udev/udev-builtin-usb_id.c59
-rw-r--r--src/udev/udev-builtin.c8
-rw-r--r--src/udev/udev-ctrl.c6
-rw-r--r--src/udev/udev-event.c71
-rw-r--r--src/udev/udev-node.c54
-rw-r--r--src/udev/udev-rules.c34
-rw-r--r--src/udev/udev-watch.c2
-rw-r--r--src/udev/udev.h13
-rw-r--r--src/udev/udevadm-hwdb.c99
-rw-r--r--src/udev/udevadm-info.c24
-rw-r--r--src/udev/udevadm-monitor.c2
-rw-r--r--src/udev/udevadm-test-builtin.c4
-rw-r--r--src/udev/udevadm-test.c10
-rw-r--r--src/udev/udevadm-trigger.c14
-rw-r--r--src/udev/udevadm.c2
-rw-r--r--src/udev/udevd.c155
-rw-r--r--src/update-utmp/update-utmp.c3
-rw-r--r--src/vconsole/vconsole-setup.c119
422 files changed, 43905 insertions, 14798 deletions
diff --git a/src/activate/activate.c b/src/activate/activate.c
new file mode 100644
index 0000000000..87526d47cc
--- /dev/null
+++ b/src/activate/activate.c
@@ -0,0 +1,443 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ 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 <http://www.gnu.org/licenses/>.
+***/
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/epoll.h>
+#include <sys/prctl.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <getopt.h>
+
+#include <systemd/sd-daemon.h>
+
+#include "socket-util.h"
+#include "build.h"
+#include "log.h"
+#include "strv.h"
+#include "macro.h"
+
+static char** arg_listen = NULL;
+static bool arg_accept = false;
+static char** arg_args = NULL;
+static char** arg_environ = NULL;
+
+static int add_epoll(int epoll_fd, int fd) {
+ int r;
+ struct epoll_event ev = {EPOLLIN};
+ ev.data.fd = fd;
+
+ assert(epoll_fd >= 0);
+ assert(fd >= 0);
+
+ r = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev);
+ if (r < 0)
+ log_error("Failed to add event on epoll fd:%d for fd:%d: %s",
+ epoll_fd, fd, strerror(-r));
+ return r;
+}
+
+static int set_nocloexec(int fd) {
+ int flags;
+
+ flags = fcntl(fd, F_GETFD);
+ if (flags < 0) {
+ log_error("Querying flags for fd:%d: %m", fd);
+ return -errno;
+ }
+
+ if (!(flags & FD_CLOEXEC))
+ return 0;
+
+ if (fcntl(fd, F_SETFD, flags & ~FD_CLOEXEC) < 0) {
+ log_error("Settings flags for fd:%d: %m", fd);
+ return -errno;
+ }
+
+ return 0;
+}
+
+static int print_socket(const char* desc, int fd) {
+ int r;
+ SocketAddress addr = {
+ .size = sizeof(union sockaddr_union),
+ .type = SOCK_STREAM,
+ };
+ int family;
+
+ r = getsockname(fd, &addr.sockaddr.sa, &addr.size);
+ if (r < 0) {
+ log_warning("Failed to query socket on fd:%d: %m", fd);
+ return 0;
+ }
+
+ family = socket_address_family(&addr);
+ switch(family) {
+ case AF_INET:
+ case AF_INET6: {
+ char* _cleanup_free_ a = NULL;
+ r = socket_address_print(&addr, &a);
+ if (r < 0)
+ log_warning("socket_address_print(): %s", strerror(-r));
+ else
+ log_info("%s %s address %s",
+ desc,
+ family == AF_INET ? "IP" : "IPv6",
+ a);
+ break;
+ }
+ default:
+ log_warning("Connection with unknown family %d", family);
+ }
+
+ return 0;
+}
+
+static int open_sockets(int *epoll_fd, bool accept) {
+ int n, fd;
+ int count = 0;
+ char **address;
+
+ n = sd_listen_fds(true);
+ if (n < 0) {
+ log_error("Failed to read listening file descriptors from environment: %s",
+ strerror(-n));
+ return n;
+ }
+ log_info("Received %d descriptors", n);
+
+ for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd++) {
+ log_debug("Received descriptor fd:%d", fd);
+ print_socket("Listening on", fd);
+
+ if (!arg_accept) {
+ int r = set_nocloexec(fd);
+ if (r < 0)
+ return r;
+ }
+
+ count ++;
+ }
+
+ /** Note: we leak some fd's on error here. I doesn't matter
+ * much, since the program will exit immediately anyway, but
+ * would be a pain to fix.
+ */
+
+ STRV_FOREACH(address, arg_listen) {
+ log_info("Opening address %s", *address);
+
+ fd = make_socket_fd(*address, SOCK_STREAM | (arg_accept*SOCK_CLOEXEC));
+ if (fd < 0) {
+ log_error("Failed to open '%s': %s", *address, strerror(-fd));
+ return fd;
+ }
+
+ count ++;
+ }
+
+ *epoll_fd = epoll_create1(EPOLL_CLOEXEC);
+ if (*epoll_fd < 0) {
+ log_error("Failed to create epoll object: %m");
+ return -errno;
+ }
+
+ for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + count; fd++) {
+ int r = add_epoll(*epoll_fd, fd);
+ if (r < 0)
+ return r;
+ }
+
+ return count;
+}
+
+static int launch(char* name, char **argv, char **env, int fds) {
+ unsigned n_env = 0, length;
+ _cleanup_strv_free_ char **envp = NULL;
+ char **s;
+ static const char* tocopy[] = {"TERM=", "PATH=", "USER=", "HOME="};
+ _cleanup_free_ char *tmp = NULL;
+ unsigned i;
+
+ length = strv_length(arg_environ);
+ /* PATH, TERM, HOME, USER, LISTEN_FDS, LISTEN_PID, NULL */
+ envp = new(char *, length + 7);
+
+ STRV_FOREACH(s, arg_environ) {
+ if (strchr(*s, '='))
+ envp[n_env++] = *s;
+ else {
+ _cleanup_free_ char *p = strappend(*s, "=");
+ if (!p)
+ return log_oom();
+ envp[n_env] = strv_find_prefix(env, p);
+ if (envp[n_env])
+ n_env ++;
+ }
+ }
+
+ for (i = 0; i < ELEMENTSOF(tocopy); i++) {
+ envp[n_env] = strv_find_prefix(env, tocopy[i]);
+ if (envp[n_env])
+ n_env ++;
+ }
+
+ if ((asprintf((char**)(envp + n_env++), "LISTEN_FDS=%d", fds) < 0) ||
+ (asprintf((char**)(envp + n_env++), "LISTEN_PID=%d", getpid()) < 0))
+ return log_oom();
+
+ tmp = strv_join(argv, " ");
+ if (!tmp)
+ return log_oom();
+
+ log_info("Execing %s (%s)", name, tmp);
+ execvpe(name, argv, envp);
+ log_error("Failed to execp %s (%s): %m", name, tmp);
+ return -errno;
+}
+
+static int launch1(const char* child, char** argv, char **env, int fd) {
+ pid_t parent_pid, child_pid;
+ int r;
+
+ _cleanup_free_ char *tmp = NULL;
+ tmp = strv_join(argv, " ");
+ if (!tmp)
+ return log_oom();
+
+ parent_pid = getpid();
+
+ child_pid = fork();
+ if (child_pid < 0) {
+ log_error("Failed to fork: %m");
+ return -errno;
+ }
+
+ /* In the child */
+ if (child_pid == 0) {
+ r = dup2(fd, STDIN_FILENO);
+ if (r < 0) {
+ log_error("Failed to dup connection to stdin: %m");
+ _exit(EXIT_FAILURE);
+ }
+
+ r = dup2(fd, STDOUT_FILENO);
+ if (r < 0) {
+ log_error("Failed to dup connection to stdout: %m");
+ _exit(EXIT_FAILURE);
+ }
+
+ r = close(fd);
+ if (r < 0) {
+ log_error("Failed to close dupped connection: %m");
+ _exit(EXIT_FAILURE);
+ }
+
+ /* Make sure the child goes away when the parent dies */
+ if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0)
+ _exit(EXIT_FAILURE);
+
+ /* Check whether our parent died before we were able
+ * to set the death signal */
+ if (getppid() != parent_pid)
+ _exit(EXIT_SUCCESS);
+
+ execvp(child, argv);
+ log_error("Failed to exec child %s: %m", child);
+ _exit(EXIT_FAILURE);
+ }
+
+ log_info("Spawned %s (%s) as PID %d", child, tmp, child_pid);
+
+ return 0;
+}
+
+static int do_accept(const char* name, char **argv, char **envp, int fd) {
+ SocketAddress addr = {
+ .size = sizeof(union sockaddr_union),
+ .type = SOCK_STREAM,
+ };
+ int fd2, r;
+
+ fd2 = accept(fd, &addr.sockaddr.sa, &addr.size);
+ if (fd2 < 0) {
+ log_error("Failed to accept connection on fd:%d: %m", fd);
+ return fd2;
+ }
+
+ print_socket("Connection from", fd2);
+
+ r = launch1(name, argv, envp, fd2);
+ return r;
+}
+
+/* SIGCHLD handler. */
+static void sigchld_hdl(int sig, siginfo_t *t, void *data)
+{
+ log_info("Child %d died with code %d", t->si_pid, t->si_status);
+ /* Wait for a dead child. */
+ waitpid(t->si_pid, NULL, 0);
+}
+
+static int install_chld_handler(void) {
+ int r;
+ struct sigaction act;
+ zero(act);
+ act.sa_flags = SA_SIGINFO;
+ act.sa_sigaction = sigchld_hdl;
+
+ r = sigaction(SIGCHLD, &act, 0);
+ if (r < 0)
+ log_error("Failed to install SIGCHLD handler: %m");
+ return r;
+}
+
+static int help(void) {
+ printf("%s [OPTIONS...]\n\n"
+ "Listen on sockets and launch child on connection.\n\n"
+ "Options:\n"
+ " -l --listen=ADDR Listen for raw connections at ADDR\n"
+ " -a --accept Spawn separate child for each connection\n"
+ " -h --help Show this help and exit\n"
+ " --version Print version string and exit\n"
+ "\n"
+ "Note: file descriptors from sd_listen_fds() will be passed through.\n"
+ , program_invocation_short_name
+ );
+
+ return 0;
+}
+
+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 },
+ { "listen", required_argument, NULL, 'l' },
+ { "accept", no_argument, NULL, 'a' },
+ { "environment", required_argument, NULL, 'E' },
+ { NULL, 0, NULL, 0 }
+ };
+
+ int c;
+
+ assert(argc >= 0);
+ assert(argv);
+
+ while ((c = getopt_long(argc, argv, "+hl:saE:", options, NULL)) >= 0)
+ switch(c) {
+ case 'h':
+ help();
+ return 0 /* done */;
+
+ case ARG_VERSION:
+ puts(PACKAGE_STRING);
+ puts(SYSTEMD_FEATURES);
+ return 0 /* done */;
+
+ case 'l': {
+ int r = strv_extend(&arg_listen, optarg);
+ if (r < 0)
+ return r;
+
+ break;
+ }
+
+ case 'a':
+ arg_accept = true;
+ break;
+
+ case 'E': {
+ int r = strv_extend(&arg_environ, optarg);
+ if (r < 0)
+ return r;
+
+ break;
+ }
+
+ case '?':
+ return -EINVAL;
+
+ default:
+ log_error("Unknown option code %c", c);
+ return -EINVAL;
+ }
+
+ if (optind == argc) {
+ log_error("Usage: %s [OPTION...] PROGRAM [OPTION...]",
+ program_invocation_short_name);
+ return -EINVAL;
+ }
+
+ arg_args = argv + optind;
+
+ return 1 /* work to do */;
+}
+
+int main(int argc, char **argv, char **envp) {
+ int r, n;
+ int epoll_fd = -1;
+
+ log_set_max_level(LOG_DEBUG);
+ log_show_color(true);
+ log_parse_environment();
+
+ r = parse_argv(argc, argv);
+ if (r <= 0)
+ return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
+
+ r = install_chld_handler();
+ if (r < 0)
+ return EXIT_FAILURE;
+
+ n = open_sockets(&epoll_fd, arg_accept);
+ if (n < 0)
+ return EXIT_FAILURE;
+
+ while (true) {
+ struct epoll_event event;
+
+ r = epoll_wait(epoll_fd, &event, 1, -1);
+ if (r < 0) {
+ if (errno == EINTR)
+ continue;
+
+ log_error("epoll_wait() failed: %m");
+ return EXIT_FAILURE;
+ }
+
+ log_info("Communication attempt on fd:%d", event.data.fd);
+ if (arg_accept) {
+ r = do_accept(argv[optind], argv + optind, envp,
+ event.data.fd);
+ if (r < 0)
+ return EXIT_FAILURE;
+ } else
+ break;
+ }
+
+ launch(argv[optind], argv + optind, envp, n);
+
+ return EXIT_SUCCESS;
+}
diff --git a/src/analyze/systemd-analyze.c b/src/analyze/systemd-analyze.c
new file mode 100644
index 0000000000..bb86ec7da8
--- /dev/null
+++ b/src/analyze/systemd-analyze.c
@@ -0,0 +1,1270 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010-2013 Lennart Poettering
+ Copyright 2013 Simon Peeters
+
+ 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 <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <locale.h>
+#include <sys/utsname.h>
+#include <fnmatch.h>
+
+#include "install.h"
+#include "log.h"
+#include "dbus-common.h"
+#include "build.h"
+#include "util.h"
+#include "strxcpyx.h"
+#include "fileio.h"
+#include "strv.h"
+#include "unit-name.h"
+#include "special.h"
+#include "hashmap.h"
+
+#define SCALE_X (0.1 / 1000.0) /* pixels per us */
+#define SCALE_Y 20.0
+
+#define compare(a, b) (((a) > (b))? 1 : (((b) > (a))? -1 : 0))
+
+#define svg(...) printf(__VA_ARGS__)
+
+#define svg_bar(class, x1, x2, y) \
+ svg(" <rect class=\"%s\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n", \
+ (class), \
+ SCALE_X * (x1), SCALE_Y * (y), \
+ SCALE_X * ((x2) - (x1)), SCALE_Y - 1.0)
+
+#define svg_text(b, x, y, format, ...) \
+ do { \
+ svg(" <text class=\"%s\" x=\"%.03f\" y=\"%.03f\">", (b) ? "left" : "right", SCALE_X * (x) + (b ? 5.0 : -5.0), SCALE_Y * (y) + 14.0); \
+ svg(format, ## __VA_ARGS__); \
+ svg("</text>\n"); \
+ } while(false)
+
+static UnitFileScope arg_scope = UNIT_FILE_SYSTEM;
+static enum dot {
+ DEP_ALL,
+ DEP_ORDER,
+ DEP_REQUIRE
+} arg_dot = DEP_ALL;
+static char** arg_dot_from_patterns = NULL;
+static char** arg_dot_to_patterns = NULL;
+
+usec_t arg_fuzz = 0;
+
+struct boot_times {
+ usec_t firmware_time;
+ usec_t loader_time;
+ usec_t kernel_time;
+ usec_t kernel_done_time;
+ usec_t initrd_time;
+ usec_t userspace_time;
+ usec_t finish_time;
+};
+struct unit_times {
+ char *name;
+ usec_t ixt;
+ usec_t iet;
+ usec_t axt;
+ usec_t aet;
+ usec_t time;
+};
+
+static int bus_get_uint64_property(DBusConnection *bus, const char *path, const char *interface, const char *property, uint64_t *val) {
+ _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
+ DBusMessageIter iter, sub;
+ int r;
+
+ r = bus_method_call_with_reply(
+ bus,
+ "org.freedesktop.systemd1",
+ path,
+ "org.freedesktop.DBus.Properties",
+ "Get",
+ &reply,
+ NULL,
+ DBUS_TYPE_STRING, &interface,
+ DBUS_TYPE_STRING, &property,
+ DBUS_TYPE_INVALID);
+ if (r < 0)
+ return r;
+
+ if (!dbus_message_iter_init(reply, &iter) ||
+ dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
+ log_error("Failed to parse reply.");
+ return -EIO;
+ }
+
+ dbus_message_iter_recurse(&iter, &sub);
+
+ if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_UINT64) {
+ log_error("Failed to parse reply.");
+ return -EIO;
+ }
+
+ dbus_message_iter_get_basic(&sub, val);
+
+ return 0;
+}
+
+static int compare_unit_time(const void *a, const void *b) {
+ return compare(((struct unit_times *)b)->time,
+ ((struct unit_times *)a)->time);
+}
+
+static int compare_unit_start(const void *a, const void *b) {
+ return compare(((struct unit_times *)a)->ixt,
+ ((struct unit_times *)b)->ixt);
+}
+
+static int get_os_name(char **_n) {
+ char *n = NULL;
+ int r;
+
+ r = parse_env_file("/etc/os-release", NEWLINE, "PRETTY_NAME", &n, NULL);
+ if (r < 0)
+ return r;
+
+ if (!n)
+ return -ENOENT;
+
+ *_n = n;
+ return 0;
+}
+
+static void free_unit_times(struct unit_times *t, unsigned n) {
+ struct unit_times *p;
+
+ for (p = t; p < t + n; p++)
+ free(p->name);
+
+ free(t);
+}
+
+static int acquire_time_data(DBusConnection *bus, struct unit_times **out) {
+ _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
+ DBusMessageIter iter, sub;
+ int r, c = 0, n_units = 0;
+ struct unit_times *unit_times = NULL;
+
+ r = bus_method_call_with_reply(
+ bus,
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "ListUnits",
+ &reply,
+ NULL,
+ DBUS_TYPE_INVALID);
+ if (r < 0)
+ goto fail;
+
+ if (!dbus_message_iter_init(reply, &iter) ||
+ dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
+ dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) {
+ log_error("Failed to parse reply.");
+ r = -EIO;
+ goto fail;
+ }
+
+ for (dbus_message_iter_recurse(&iter, &sub);
+ dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID;
+ dbus_message_iter_next(&sub)) {
+ struct unit_info u;
+ struct unit_times *t;
+
+ if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
+ log_error("Failed to parse reply.");
+ r = -EIO;
+ goto fail;
+ }
+
+ if (c >= n_units) {
+ struct unit_times *w;
+
+ n_units = MAX(2*c, 16);
+ w = realloc(unit_times, sizeof(struct unit_times) * n_units);
+
+ if (!w) {
+ r = log_oom();
+ goto fail;
+ }
+
+ unit_times = w;
+ }
+ t = unit_times+c;
+ t->name = NULL;
+
+ r = bus_parse_unit_info(&sub, &u);
+ if (r < 0)
+ goto fail;
+
+ assert_cc(sizeof(usec_t) == sizeof(uint64_t));
+
+ if (bus_get_uint64_property(bus, u.unit_path,
+ "org.freedesktop.systemd1.Unit",
+ "InactiveExitTimestampMonotonic",
+ &t->ixt) < 0 ||
+ bus_get_uint64_property(bus, u.unit_path,
+ "org.freedesktop.systemd1.Unit",
+ "ActiveEnterTimestampMonotonic",
+ &t->aet) < 0 ||
+ bus_get_uint64_property(bus, u.unit_path,
+ "org.freedesktop.systemd1.Unit",
+ "ActiveExitTimestampMonotonic",
+ &t->axt) < 0 ||
+ bus_get_uint64_property(bus, u.unit_path,
+ "org.freedesktop.systemd1.Unit",
+ "InactiveEnterTimestampMonotonic",
+ &t->iet) < 0) {
+ r = -EIO;
+ goto fail;
+ }
+
+ if (t->aet >= t->ixt)
+ t->time = t->aet - t->ixt;
+ else if (t->iet >= t->ixt)
+ t->time = t->iet - t->ixt;
+ else
+ t->time = 0;
+
+ if (t->ixt == 0)
+ continue;
+
+ t->name = strdup(u.id);
+ if (t->name == NULL) {
+ r = log_oom();
+ goto fail;
+ }
+ c++;
+ }
+
+ *out = unit_times;
+ return c;
+
+fail:
+ free_unit_times(unit_times, (unsigned) c);
+ return r;
+}
+
+static int acquire_boot_times(DBusConnection *bus, struct boot_times **bt) {
+ static struct boot_times times;
+ static bool cached = false;
+
+ if (cached)
+ goto finish;
+
+ assert_cc(sizeof(usec_t) == sizeof(uint64_t));
+
+ if (bus_get_uint64_property(bus,
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "FirmwareTimestampMonotonic",
+ &times.firmware_time) < 0 ||
+ bus_get_uint64_property(bus,
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "LoaderTimestampMonotonic",
+ &times.loader_time) < 0 ||
+ bus_get_uint64_property(bus,
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "KernelTimestamp",
+ &times.kernel_time) < 0 ||
+ bus_get_uint64_property(bus,
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "InitRDTimestampMonotonic",
+ &times.initrd_time) < 0 ||
+ bus_get_uint64_property(bus,
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "UserspaceTimestampMonotonic",
+ &times.userspace_time) < 0 ||
+ bus_get_uint64_property(bus,
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "FinishTimestampMonotonic",
+ &times.finish_time) < 0)
+ return -EIO;
+
+ if (times.finish_time <= 0) {
+ log_error("Bootup is not yet finished. Please try again later.");
+ return -EAGAIN;
+ }
+
+ if (times.initrd_time)
+ times.kernel_done_time = times.initrd_time;
+ else
+ times.kernel_done_time = times.userspace_time;
+
+ cached = true;
+
+finish:
+ *bt = &times;
+ return 0;
+}
+
+static int pretty_boot_time(DBusConnection *bus, char **_buf) {
+ char ts[FORMAT_TIMESPAN_MAX];
+ struct boot_times *t;
+ static char buf[4096];
+ size_t size;
+ char *ptr;
+ int r;
+
+ r = acquire_boot_times(bus, &t);
+ if (r < 0)
+ return r;
+
+ ptr = buf;
+ size = sizeof(buf);
+
+ size = strpcpyf(&ptr, size, "Startup finished in ");
+ if (t->firmware_time)
+ size = strpcpyf(&ptr, size, "%s (firmware) + ", format_timespan(ts, sizeof(ts), t->firmware_time - t->loader_time, USEC_PER_MSEC));
+ if (t->loader_time)
+ size = strpcpyf(&ptr, size, "%s (loader) + ", format_timespan(ts, sizeof(ts), t->loader_time, USEC_PER_MSEC));
+ if (t->kernel_time)
+ size = strpcpyf(&ptr, size, "%s (kernel) + ", format_timespan(ts, sizeof(ts), t->kernel_done_time, USEC_PER_MSEC));
+ if (t->initrd_time > 0)
+ size = strpcpyf(&ptr, size, "%s (initrd) + ", format_timespan(ts, sizeof(ts), t->userspace_time - t->initrd_time, USEC_PER_MSEC));
+
+ size = strpcpyf(&ptr, size, "%s (userspace) ", format_timespan(ts, sizeof(ts), t->finish_time - t->userspace_time, USEC_PER_MSEC));
+ if (t->kernel_time > 0)
+ size = strpcpyf(&ptr, size, "= %s", format_timespan(ts, sizeof(ts), t->firmware_time + t->finish_time, USEC_PER_MSEC));
+ else
+ size = strpcpyf(&ptr, size, "= %s", format_timespan(ts, sizeof(ts), t->finish_time - t->userspace_time, USEC_PER_MSEC));
+
+ ptr = strdup(buf);
+ if (!ptr)
+ return log_oom();
+
+ *_buf = ptr;
+ return 0;
+}
+
+static void svg_graph_box(double height, double begin, double end) {
+ long long i;
+
+ /* outside box, fill */
+ svg("<rect class=\"box\" x=\"0\" y=\"0\" width=\"%.03f\" height=\"%.03f\" />\n",
+ SCALE_X * (end - begin), SCALE_Y * height);
+
+ for (i = ((long long) (begin / 100000)) * 100000; i <= end; i+=100000) {
+ /* lines for each second */
+ if (i % 5000000 == 0)
+ svg(" <line class=\"sec5\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n"
+ " <text class=\"sec\" x=\"%.03f\" y=\"%.03f\" >%.01fs</text>\n",
+ SCALE_X * i, SCALE_X * i, SCALE_Y * height, SCALE_X * i, -5.0, 0.000001 * i);
+ else if (i % 1000000 == 0)
+ svg(" <line class=\"sec1\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n"
+ " <text class=\"sec\" x=\"%.03f\" y=\"%.03f\" >%.01fs</text>\n",
+ SCALE_X * i, SCALE_X * i, SCALE_Y * height, SCALE_X * i, -5.0, 0.000001 * i);
+ else
+ svg(" <line class=\"sec01\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
+ SCALE_X * i, SCALE_X * i, SCALE_Y * height);
+ }
+}
+
+static int analyze_plot(DBusConnection *bus) {
+ struct unit_times *times;
+ struct boot_times *boot;
+ struct utsname name;
+ int n, m = 1, y=0;
+ double width;
+ _cleanup_free_ char *pretty_times = NULL, *osname = NULL;
+ struct unit_times *u;
+
+ n = acquire_boot_times(bus, &boot);
+ if (n < 0)
+ return n;
+
+ n = pretty_boot_time(bus, &pretty_times);
+ if (n < 0)
+ return n;
+
+ get_os_name(&osname);
+ assert_se(uname(&name) >= 0);
+
+ n = acquire_time_data(bus, &times);
+ if (n <= 0)
+ return n;
+
+ qsort(times, n, sizeof(struct unit_times), compare_unit_start);
+
+ width = SCALE_X * (boot->firmware_time + boot->finish_time);
+ if (width < 800.0)
+ width = 800.0;
+
+ if (boot->firmware_time > boot->loader_time)
+ m++;
+ if (boot->loader_time) {
+ m++;
+ if (width < 1000.0)
+ width = 1000.0;
+ }
+ if (boot->initrd_time)
+ m++;
+ if (boot->kernel_time)
+ m++;
+
+ for (u = times; u < times + n; u++) {
+ double len;
+
+ if (u->ixt < boot->userspace_time ||
+ u->ixt > boot->finish_time) {
+ free(u->name);
+ u->name = NULL;
+ continue;
+ }
+ len = ((boot->firmware_time + u->ixt) * SCALE_X)
+ + (10.0 * strlen(u->name));
+ if (len > width)
+ width = len;
+
+ if (u->iet > u->ixt && u->iet <= boot->finish_time
+ && u->aet == 0 && u->axt == 0)
+ u->aet = u->axt = u->iet;
+ if (u->aet < u->ixt || u->aet > boot->finish_time)
+ u->aet = boot->finish_time;
+ if (u->axt < u->aet || u->aet > boot->finish_time)
+ u->axt = boot->finish_time;
+ if (u->iet < u->axt || u->iet > boot->finish_time)
+ u->iet = boot->finish_time;
+ m++;
+ }
+
+ svg("<?xml version=\"1.0\" standalone=\"no\"?>\n"
+ "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" "
+ "\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n");
+
+ svg("<svg width=\"%.0fpx\" height=\"%.0fpx\" version=\"1.1\" "
+ "xmlns=\"http://www.w3.org/2000/svg\">\n\n",
+ 80.0 + width, 150.0 + (m * SCALE_Y));
+
+ /* write some basic info as a comment, including some help */
+ svg("<!-- This file is a systemd-analyze SVG file. It is best rendered in a -->\n"
+ "<!-- browser such as Chrome, Chromium or Firefox. Other applications -->\n"
+ "<!-- that render these files properly but much slower are ImageMagick, -->\n"
+ "<!-- gimp, inkscape, etc. To display the files on your system, just -->\n"
+ "<!-- point your browser to this file. -->\n\n"
+ "<!-- This plot was generated by systemd-analyze version %-16.16s -->\n\n", VERSION);
+
+ /* style sheet */
+ svg("<defs>\n <style type=\"text/css\">\n <![CDATA[\n"
+ " rect { stroke-width: 1; stroke-opacity: 0; }\n"
+ " rect.activating { fill: rgb(255,0,0); fill-opacity: 0.7; }\n"
+ " rect.active { fill: rgb(200,150,150); fill-opacity: 0.7; }\n"
+ " rect.deactivating { fill: rgb(150,100,100); fill-opacity: 0.7; }\n"
+ " rect.kernel { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
+ " rect.initrd { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
+ " rect.firmware { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
+ " rect.loader { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
+ " rect.userspace { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
+ " rect.box { fill: rgb(240,240,240); stroke: rgb(192,192,192); }\n"
+ " line { stroke: rgb(64,64,64); stroke-width: 1; }\n"
+ "// line.sec1 { }\n"
+ " line.sec5 { stroke-width: 2; }\n"
+ " line.sec01 { stroke: rgb(224,224,224); stroke-width: 1; }\n"
+ " text { font-family: Verdana, Helvetica; font-size: 10; }\n"
+ " text.left { font-family: Verdana, Helvetica; font-size: 10; text-anchor: start; }\n"
+ " text.right { font-family: Verdana, Helvetica; font-size: 10; text-anchor: end; }\n"
+ " text.sec { font-size: 8; }\n"
+ " ]]>\n </style>\n</defs>\n\n");
+
+ svg("<text x=\"20\" y=\"50\">%s</text>", pretty_times);
+ svg("<text x=\"20\" y=\"30\">%s %s (%s %s) %s</text>",
+ isempty(osname) ? "Linux" : osname,
+ name.nodename, name.release, name.version, name.machine);
+ svg("<text x=\"20\" y=\"%.0f\">Legend: Red = Activating; Pink = Active; Dark Pink = Deactivating</text>",
+ 120.0 + (m *SCALE_Y));
+
+ svg("<g transform=\"translate(%.3f,100)\">\n", 20.0 + (SCALE_X * boot->firmware_time));
+ svg_graph_box(m, -boot->firmware_time, boot->finish_time);
+
+ if (boot->firmware_time) {
+ svg_bar("firmware", -(double) boot->firmware_time, -(double) boot->loader_time, y);
+ svg_text(true, -(double) boot->firmware_time, y, "firmware");
+ y++;
+ }
+ if (boot->loader_time) {
+ svg_bar("loader", -(double) boot->loader_time, 0, y);
+ svg_text(true, -(double) boot->loader_time, y, "loader");
+ y++;
+ }
+ if (boot->kernel_time) {
+ svg_bar("kernel", 0, boot->kernel_done_time, y);
+ svg_text(true, 0, y, "kernel");
+ y++;
+ }
+ if (boot->initrd_time) {
+ svg_bar("initrd", boot->initrd_time, boot->userspace_time, y);
+ svg_text(true, boot->initrd_time, y, "initrd");
+ y++;
+ }
+ svg_bar("userspace", boot->userspace_time, boot->finish_time, y);
+ svg_text("left", boot->userspace_time, y, "userspace");
+ y++;
+
+ for (u = times; u < times + n; u++) {
+ char ts[FORMAT_TIMESPAN_MAX];
+ bool b;
+
+ if (!u->name)
+ continue;
+
+ svg_bar("activating", u->ixt, u->aet, y);
+ svg_bar("active", u->aet, u->axt, y);
+ svg_bar("deactivating", u->axt, u->iet, y);
+
+ b = u->ixt * SCALE_X > width * 2 / 3;
+ if (u->time)
+ svg_text(b, u->ixt, y, "%s (%s)",
+ u->name, format_timespan(ts, sizeof(ts), u->time, USEC_PER_MSEC));
+ else
+ svg_text(b, u->ixt, y, "%s", u->name);
+ y++;
+ }
+ svg("</g>\n\n");
+
+ svg("</svg>");
+
+ free_unit_times(times, (unsigned) n);
+
+ return 0;
+}
+
+
+static int list_dependencies_print(const char *name, unsigned int level, unsigned int branches,
+ bool last, struct unit_times *times, struct boot_times *boot) {
+ unsigned int i;
+ char ts[FORMAT_TIMESPAN_MAX], ts2[FORMAT_TIMESPAN_MAX];
+
+ for (i = level; i != 0; i--)
+ printf("%s", draw_special_char(branches & (1 << (i-1)) ? DRAW_TREE_VERT : DRAW_TREE_SPACE));
+
+ printf("%s", draw_special_char(last ? DRAW_TREE_RIGHT : DRAW_TREE_BRANCH));
+
+ if (times) {
+ if (times->time)
+ printf("%s%s @%s +%s%s", ANSI_HIGHLIGHT_RED_ON, name,
+ format_timespan(ts, sizeof(ts), times->ixt - boot->userspace_time, USEC_PER_MSEC),
+ format_timespan(ts2, sizeof(ts2), times->time, USEC_PER_MSEC), ANSI_HIGHLIGHT_OFF);
+ else if (times->aet > boot->userspace_time)
+ printf("%s @%s", name, format_timespan(ts, sizeof(ts), times->aet - boot->userspace_time, USEC_PER_MSEC));
+ else
+ printf("%s", name);
+ } else printf("%s", name);
+ printf("\n");
+
+ return 0;
+}
+
+static int list_dependencies_get_dependencies(DBusConnection *bus, const char *name, char ***deps) {
+ static const char dependencies[] =
+ "After\0";
+
+ _cleanup_free_ char *path;
+ const char *interface = "org.freedesktop.systemd1.Unit";
+
+ _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
+ DBusMessageIter iter, sub, sub2, sub3;
+
+ int r = 0;
+ char **ret = NULL;
+
+ assert(bus);
+ assert(name);
+ assert(deps);
+
+ path = unit_dbus_path_from_name(name);
+ if (path == NULL) {
+ r = -EINVAL;
+ goto finish;
+ }
+
+ r = bus_method_call_with_reply(
+ bus,
+ "org.freedesktop.systemd1",
+ path,
+ "org.freedesktop.DBus.Properties",
+ "GetAll",
+ &reply,
+ NULL,
+ DBUS_TYPE_STRING, &interface,
+ DBUS_TYPE_INVALID);
+ if (r < 0)
+ goto finish;
+
+ if (!dbus_message_iter_init(reply, &iter) ||
+ dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
+ dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_DICT_ENTRY) {
+ log_error("Failed to parse reply.");
+ r = -EIO;
+ goto finish;
+ }
+
+ dbus_message_iter_recurse(&iter, &sub);
+
+ while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
+ const char *prop;
+
+ assert(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_DICT_ENTRY);
+ dbus_message_iter_recurse(&sub, &sub2);
+
+ if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &prop, true) < 0) {
+ log_error("Failed to parse reply.");
+ r = -EIO;
+ goto finish;
+ }
+
+ if (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_VARIANT) {
+ log_error("Failed to parse reply.");
+ r = -EIO;
+ goto finish;
+ }
+
+ dbus_message_iter_recurse(&sub2, &sub3);
+ dbus_message_iter_next(&sub);
+
+ if (!nulstr_contains(dependencies, prop))
+ continue;
+
+ if (dbus_message_iter_get_arg_type(&sub3) == DBUS_TYPE_ARRAY) {
+ if (dbus_message_iter_get_element_type(&sub3) == DBUS_TYPE_STRING) {
+ DBusMessageIter sub4;
+ dbus_message_iter_recurse(&sub3, &sub4);
+
+ while (dbus_message_iter_get_arg_type(&sub4) != DBUS_TYPE_INVALID) {
+ const char *s;
+
+ assert(dbus_message_iter_get_arg_type(&sub4) == DBUS_TYPE_STRING);
+ dbus_message_iter_get_basic(&sub4, &s);
+
+ r = strv_extend(&ret, s);
+ if (r < 0) {
+ log_oom();
+ goto finish;
+ }
+
+ dbus_message_iter_next(&sub4);
+ }
+ }
+ }
+ }
+finish:
+ if (r < 0)
+ strv_free(ret);
+ else
+ *deps = ret;
+ return r;
+}
+
+static Hashmap *unit_times_hashmap;
+
+static int list_dependencies_compare(const void *_a, const void *_b) {
+ const char **a = (const char**) _a, **b = (const char**) _b;
+ usec_t usa = 0, usb = 0;
+ struct unit_times *times;
+
+ times = hashmap_get(unit_times_hashmap, *a);
+ if (times)
+ usa = times->aet;
+ times = hashmap_get(unit_times_hashmap, *b);
+ if (times)
+ usb = times->aet;
+
+ return usb - usa;
+}
+
+static int list_dependencies_one(DBusConnection *bus, const char *name, unsigned int level, char ***units,
+ unsigned int branches) {
+ _cleanup_strv_free_ char **deps = NULL;
+ char **c;
+ int r = 0;
+ usec_t service_longest = 0;
+ int to_print = 0;
+ struct unit_times *times;
+ struct boot_times *boot;
+
+ if(strv_extend(units, name))
+ return log_oom();
+
+ r = list_dependencies_get_dependencies(bus, name, &deps);
+ if (r < 0)
+ return r;
+
+ qsort(deps, strv_length(deps), sizeof (char*), list_dependencies_compare);
+
+ r = acquire_boot_times(bus, &boot);
+ if (r < 0)
+ return r;
+
+ STRV_FOREACH(c, deps) {
+ times = hashmap_get(unit_times_hashmap, *c);
+ if (times
+ && times->aet
+ && times->aet <= boot->finish_time
+ && (times->aet >= service_longest
+ || service_longest == 0)) {
+ service_longest = times->aet;
+ break;
+ }
+ }
+
+ if (service_longest == 0 )
+ return r;
+
+ STRV_FOREACH(c, deps) {
+ times = hashmap_get(unit_times_hashmap, *c);
+ if (times && times->aet
+ && times->aet <= boot->finish_time
+ && (service_longest - times->aet) <= arg_fuzz) {
+ to_print++;
+ }
+ }
+
+ if(!to_print)
+ return r;
+
+ STRV_FOREACH(c, deps) {
+ times = hashmap_get(unit_times_hashmap, *c);
+ if (!times
+ || !times->aet
+ || times->aet > boot->finish_time
+ || service_longest - times->aet > arg_fuzz)
+ continue;
+
+ to_print--;
+
+ r = list_dependencies_print(*c, level, branches, to_print == 0, times, boot);
+ if (r < 0)
+ return r;
+
+ if (strv_contains(*units, *c)) {
+ r = list_dependencies_print("...", level + 1, (branches << 1) | (to_print ? 1 : 0),
+ true, NULL, boot);
+ continue;
+ }
+
+ r = list_dependencies_one(bus, *c, level + 1, units,
+ (branches << 1) | (to_print ? 1 : 0));
+ if(r < 0)
+ return r;
+
+
+ if(!to_print)
+ break;
+
+ }
+ return 0;
+}
+
+static int list_dependencies(DBusConnection *bus) {
+ _cleanup_strv_free_ char **units = NULL;
+ char ts[FORMAT_TIMESPAN_MAX];
+ struct unit_times *times;
+ int r;
+ const char
+ *path, *id,
+ *interface = "org.freedesktop.systemd1.Unit",
+ *property = "Id";
+ DBusMessageIter iter, sub;
+ _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
+ struct boot_times *boot;
+
+ assert(bus);
+
+ path = unit_dbus_path_from_name(SPECIAL_DEFAULT_TARGET);
+ if (path == NULL)
+ return -EINVAL;
+
+ r = bus_method_call_with_reply (
+ bus,
+ "org.freedesktop.systemd1",
+ path,
+ "org.freedesktop.DBus.Properties",
+ "Get",
+ &reply,
+ NULL,
+ DBUS_TYPE_STRING, &interface,
+ DBUS_TYPE_STRING, &property,
+ DBUS_TYPE_INVALID);
+ if (r < 0)
+ return r;
+
+ if (!dbus_message_iter_init(reply, &iter) ||
+ dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
+ log_error("Failed to parse reply.");
+ return -EIO;
+ }
+
+ dbus_message_iter_recurse(&iter, &sub);
+
+ if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING) {
+ log_error("Failed to parse reply.");
+ return -EIO;
+ }
+
+ dbus_message_iter_get_basic(&sub, &id);
+
+ times = hashmap_get(unit_times_hashmap, id);
+
+ r = acquire_boot_times(bus, &boot);
+ if (r < 0)
+ return r;
+
+ if (times) {
+ if (times->time)
+ printf("%s%s +%s%s\n", ANSI_HIGHLIGHT_RED_ON, id,
+ format_timespan(ts, sizeof(ts), times->time, USEC_PER_MSEC), ANSI_HIGHLIGHT_OFF);
+ else if (times->aet > boot->userspace_time)
+ printf("%s @%s\n", id, format_timespan(ts, sizeof(ts), times->aet - boot->userspace_time, USEC_PER_MSEC));
+ else
+ printf("%s\n", id);
+ }
+
+ return list_dependencies_one(bus, SPECIAL_DEFAULT_TARGET, 0, &units, 0);
+}
+
+static int analyze_critical_chain(DBusConnection *bus) {
+ struct unit_times *times;
+ int n, r;
+ unsigned int i;
+ Hashmap *h;
+
+ n = acquire_time_data(bus, &times);
+ if (n <= 0)
+ return n;
+
+ h = hashmap_new(string_hash_func, string_compare_func);
+ if (!h)
+ return -ENOMEM;
+
+ for (i = 0; i < (unsigned)n; i++) {
+ r = hashmap_put(h, times[i].name, &times[i]);
+ if (r < 0)
+ return r;
+ }
+ unit_times_hashmap = h;
+
+ puts("The time after the unit is active or started is printed after the \"@\" character.\n"
+ "The time the unit takes to start is printed after the \"+\" character.\n");
+
+ list_dependencies(bus);
+
+ hashmap_free(h);
+ free_unit_times(times, (unsigned) n);
+ return 0;
+}
+
+static int analyze_blame(DBusConnection *bus) {
+ struct unit_times *times;
+ unsigned i;
+ int n;
+
+ n = acquire_time_data(bus, &times);
+ if (n <= 0)
+ return n;
+
+ qsort(times, n, sizeof(struct unit_times), compare_unit_time);
+
+ for (i = 0; i < (unsigned) n; i++) {
+ char ts[FORMAT_TIMESPAN_MAX];
+
+ if (times[i].time > 0)
+ printf("%16s %s\n", format_timespan(ts, sizeof(ts), times[i].time, USEC_PER_MSEC), times[i].name);
+ }
+
+ free_unit_times(times, (unsigned) n);
+ return 0;
+}
+
+static int analyze_time(DBusConnection *bus) {
+ _cleanup_free_ char *buf = NULL;
+ int r;
+
+ r = pretty_boot_time(bus, &buf);
+ if (r < 0)
+ return r;
+
+ puts(buf);
+ return 0;
+}
+
+static int graph_one_property(const char *name, const char *prop, DBusMessageIter *iter, char* patterns[]) {
+
+ static const char * const colors[] = {
+ "Requires", "[color=\"black\"]",
+ "RequiresOverridable", "[color=\"black\"]",
+ "Requisite", "[color=\"darkblue\"]",
+ "RequisiteOverridable", "[color=\"darkblue\"]",
+ "Wants", "[color=\"grey66\"]",
+ "Conflicts", "[color=\"red\"]",
+ "ConflictedBy", "[color=\"red\"]",
+ "After", "[color=\"green\"]"
+ };
+
+ const char *c = NULL;
+ unsigned i;
+
+ assert(name);
+ assert(prop);
+ assert(iter);
+
+ for (i = 0; i < ELEMENTSOF(colors); i += 2)
+ if (streq(colors[i], prop)) {
+ c = colors[i+1];
+ break;
+ }
+
+ if (!c)
+ return 0;
+
+ if (arg_dot != DEP_ALL)
+ if ((arg_dot == DEP_ORDER) != streq(prop, "After"))
+ return 0;
+
+ if (dbus_message_iter_get_arg_type(iter) == DBUS_TYPE_ARRAY &&
+ dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRING) {
+ DBusMessageIter sub;
+
+ dbus_message_iter_recurse(iter, &sub);
+
+ for (dbus_message_iter_recurse(iter, &sub);
+ dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID;
+ dbus_message_iter_next(&sub)) {
+ const char *s;
+ char **p;
+ bool match_found;
+
+ assert(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING);
+ dbus_message_iter_get_basic(&sub, &s);
+
+ if (!strv_isempty(arg_dot_from_patterns)) {
+ match_found = false;
+
+ STRV_FOREACH(p, arg_dot_from_patterns)
+ if (fnmatch(*p, name, 0) == 0) {
+ match_found = true;
+ break;
+ }
+
+ if (!match_found)
+ continue;
+ }
+
+ if (!strv_isempty(arg_dot_to_patterns)) {
+ match_found = false;
+
+ STRV_FOREACH(p, arg_dot_to_patterns)
+ if (fnmatch(*p, s, 0) == 0) {
+ match_found = true;
+ break;
+ }
+
+ if (!match_found)
+ continue;
+ }
+
+ if (!strv_isempty(patterns)) {
+ match_found = false;
+
+ STRV_FOREACH(p, patterns)
+ if (fnmatch(*p, name, 0) == 0 || fnmatch(*p, s, 0) == 0) {
+ match_found = true;
+ break;
+ }
+ if (!match_found)
+ continue;
+ }
+
+ printf("\t\"%s\"->\"%s\" %s;\n", name, s, c);
+ }
+ }
+
+ return 0;
+}
+
+static int graph_one(DBusConnection *bus, const struct unit_info *u, char *patterns[]) {
+ _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
+ const char *interface = "org.freedesktop.systemd1.Unit";
+ int r;
+ DBusMessageIter iter, sub, sub2, sub3;
+
+ assert(bus);
+ assert(u);
+
+ r = bus_method_call_with_reply(
+ bus,
+ "org.freedesktop.systemd1",
+ u->unit_path,
+ "org.freedesktop.DBus.Properties",
+ "GetAll",
+ &reply,
+ NULL,
+ DBUS_TYPE_STRING, &interface,
+ DBUS_TYPE_INVALID);
+ if (r < 0)
+ return r;
+
+ if (!dbus_message_iter_init(reply, &iter) ||
+ dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
+ dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_DICT_ENTRY) {
+ log_error("Failed to parse reply.");
+ return -EIO;
+ }
+
+ for (dbus_message_iter_recurse(&iter, &sub);
+ dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID;
+ dbus_message_iter_next(&sub)) {
+ const char *prop;
+
+ assert(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_DICT_ENTRY);
+ dbus_message_iter_recurse(&sub, &sub2);
+
+ if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &prop, true) < 0 ||
+ dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_VARIANT) {
+ log_error("Failed to parse reply.");
+ return -EIO;
+ }
+
+ dbus_message_iter_recurse(&sub2, &sub3);
+ r = graph_one_property(u->id, prop, &sub3, patterns);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+static int dot(DBusConnection *bus, char* patterns[]) {
+ _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
+ DBusMessageIter iter, sub;
+ int r;
+
+ r = bus_method_call_with_reply(
+ bus,
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "ListUnits",
+ &reply,
+ NULL,
+ DBUS_TYPE_INVALID);
+ if (r < 0)
+ return r;
+
+ if (!dbus_message_iter_init(reply, &iter) ||
+ dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
+ dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) {
+ log_error("Failed to parse reply.");
+ return -EIO;
+ }
+
+ printf("digraph systemd {\n");
+
+ for (dbus_message_iter_recurse(&iter, &sub);
+ dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID;
+ dbus_message_iter_next(&sub)) {
+ struct unit_info u;
+
+ r = bus_parse_unit_info(&sub, &u);
+ if (r < 0)
+ return -EIO;
+
+ r = graph_one(bus, &u, patterns);
+ if (r < 0)
+ return r;
+ }
+
+ printf("}\n");
+
+ log_info(" Color legend: black = Requires\n"
+ " dark blue = Requisite\n"
+ " dark grey = Wants\n"
+ " red = Conflicts\n"
+ " green = After\n");
+
+ if (on_tty())
+ log_notice("-- You probably want to process this output with graphviz' dot tool.\n"
+ "-- Try a shell pipeline like 'systemd-analyze dot | dot -Tsvg > systemd.svg'!\n");
+
+ return 0;
+}
+
+static void analyze_help(void)
+{
+ printf("%s [OPTIONS...] {COMMAND} ...\n\n"
+ "Process systemd profiling information\n\n"
+ " -h --help Show this help\n"
+ " --version Show package version\n"
+ " --system Connect to system manager\n"
+ " --user Connect to user service manager\n"
+ " --order When generating a dependency graph, show only order\n"
+ " --require When generating a dependency graph, show only requirement\n"
+ " --from-pattern=GLOB, --to-pattern=GLOB\n"
+ " When generating a dependency graph, filter only origins\n"
+ " or destinations, respectively\n"
+ " --fuzz=TIMESPAN When printing the tree of the critical chain, print also\n"
+ " services, which finished TIMESPAN earlier, than the\n"
+ " latest in the branch. The unit of TIMESPAN is seconds\n"
+ " unless specified with a different unit, i.e. 50ms\n\n"
+ "Commands:\n"
+ " time Print time spent in the kernel before reaching userspace\n"
+ " blame Print list of running units ordered by time to init\n"
+ " critical-chain Print a tree of the time critical chain of units\n"
+ " plot Output SVG graphic showing service initialization\n"
+ " dot Dump dependency graph (in dot(1) format)\n\n",
+ program_invocation_short_name);
+
+ /* When updating this list, including descriptions, apply
+ * changes to shell-completion/bash/systemd and
+ * shell-completion/systemd-zsh-completion.zsh too. */
+}
+
+static int parse_argv(int argc, char *argv[])
+{
+ int r;
+
+ enum {
+ ARG_VERSION = 0x100,
+ ARG_ORDER,
+ ARG_REQUIRE,
+ ARG_USER,
+ ARG_SYSTEM,
+ ARG_DOT_FROM_PATTERN,
+ ARG_DOT_TO_PATTERN,
+ ARG_FUZZ
+ };
+
+ static const struct option options[] = {
+ { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, ARG_VERSION },
+ { "order", no_argument, NULL, ARG_ORDER },
+ { "require", no_argument, NULL, ARG_REQUIRE },
+ { "user", no_argument, NULL, ARG_USER },
+ { "system", no_argument, NULL, ARG_SYSTEM },
+ { "from-pattern", required_argument, NULL, ARG_DOT_FROM_PATTERN},
+ { "to-pattern", required_argument, NULL, ARG_DOT_TO_PATTERN },
+ { "fuzz", required_argument, NULL, ARG_FUZZ },
+ { NULL, 0, NULL, 0 }
+ };
+
+ assert(argc >= 0);
+ assert(argv);
+
+ for (;;) {
+ switch (getopt_long(argc, argv, "h", options, NULL)) {
+
+ case 'h':
+ analyze_help();
+ return 0;
+
+ case ARG_VERSION:
+ puts(PACKAGE_STRING "\n" SYSTEMD_FEATURES);
+ return 0;
+
+ case ARG_USER:
+ arg_scope = UNIT_FILE_USER;
+ break;
+
+ case ARG_SYSTEM:
+ arg_scope = UNIT_FILE_SYSTEM;
+ break;
+
+ case ARG_ORDER:
+ arg_dot = DEP_ORDER;
+ break;
+
+ case ARG_REQUIRE:
+ arg_dot = DEP_REQUIRE;
+ break;
+
+ case ARG_DOT_FROM_PATTERN:
+ if (strv_extend(&arg_dot_from_patterns, optarg) < 0)
+ return log_oom();
+
+ break;
+
+ case ARG_DOT_TO_PATTERN:
+ if (strv_extend(&arg_dot_to_patterns, optarg) < 0)
+ return log_oom();
+
+ break;
+
+ case ARG_FUZZ:
+ r = parse_sec(optarg, &arg_fuzz);
+ if (r < 0)
+ return r;
+ break;
+
+ case -1:
+ return 1;
+
+ case '?':
+ return -EINVAL;
+
+ default:
+ assert_not_reached("Unhandled option");
+ }
+ }
+}
+
+int main(int argc, char *argv[]) {
+ int r;
+ DBusConnection *bus = NULL;
+
+ setlocale(LC_ALL, "");
+ setlocale(LC_NUMERIC, "C"); /* we want to format/parse floats in C style */
+ log_parse_environment();
+ log_open();
+
+ r = parse_argv(argc, argv);
+ if (r < 0)
+ return EXIT_FAILURE;
+ else if (r <= 0)
+ return EXIT_SUCCESS;
+
+ bus = dbus_bus_get(arg_scope == UNIT_FILE_SYSTEM ? DBUS_BUS_SYSTEM : DBUS_BUS_SESSION, NULL);
+ if (!bus)
+ return EXIT_FAILURE;
+
+ if (!argv[optind] || streq(argv[optind], "time"))
+ r = analyze_time(bus);
+ else if (streq(argv[optind], "blame"))
+ r = analyze_blame(bus);
+ else if (streq(argv[optind], "critical-chain"))
+ r = analyze_critical_chain(bus);
+ else if (streq(argv[optind], "plot"))
+ r = analyze_plot(bus);
+ else if (streq(argv[optind], "dot"))
+ r = dot(bus, argv+optind+1);
+ else
+ log_error("Unknown operation '%s'.", argv[optind]);
+
+ strv_free(arg_dot_from_patterns);
+ strv_free(arg_dot_to_patterns);
+ dbus_connection_unref(bus);
+
+ return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+}
diff --git a/src/analyze/systemd-analyze.in b/src/analyze/systemd-analyze.in
deleted file mode 100755
index bcced18c2c..0000000000
--- a/src/analyze/systemd-analyze.in
+++ /dev/null
@@ -1,328 +0,0 @@
-#!@PYTHON_BINARY@
-# -*-python-*-
-
-# This file is part of systemd.
-#
-# Copyright 2010-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 <http://www.gnu.org/licenses/>.
-
-import sys, os
-import argparse
-from gi.repository import Gio
-try:
- import cairo
-except ImportError:
- cairo = None
-
-def acquire_time_data():
- manager = Gio.DBusProxy.new_for_bus_sync(bus, Gio.DBusProxyFlags.NONE,
- None, 'org.freedesktop.systemd1', '/org/freedesktop/systemd1', 'org.freedesktop.systemd1.Manager', None)
- units = manager.ListUnits()
-
- l = []
-
- for i in units:
- if i[5] != "":
- continue
-
- properties = Gio.DBusProxy.new_for_bus_sync(bus, Gio.DBusProxyFlags.NONE,
- None, 'org.freedesktop.systemd1', i[6], 'org.freedesktop.DBus.Properties', None)
-
- ixt = properties.Get('(ss)', 'org.freedesktop.systemd1.Unit', 'InactiveExitTimestampMonotonic')
- aet = properties.Get('(ss)', 'org.freedesktop.systemd1.Unit', 'ActiveEnterTimestampMonotonic')
- axt = properties.Get('(ss)', 'org.freedesktop.systemd1.Unit', 'ActiveExitTimestampMonotonic')
- iet = properties.Get('(ss)', 'org.freedesktop.systemd1.Unit', 'InactiveEnterTimestampMonotonic')
-
- l.append((str(i[0]), ixt, aet, axt, iet))
-
- return l
-
-def acquire_start_time():
- properties = Gio.DBusProxy.new_for_bus_sync(bus, Gio.DBusProxyFlags.NONE,
- None, 'org.freedesktop.systemd1', '/org/freedesktop/systemd1', 'org.freedesktop.DBus.Properties', None)
-
- # Note that the firmware/loader times are returned as positive
- # values but are actually considered negative from the point
- # in time of kernel initialization. Also, the monotonic kernel
- # time will always be 0 since that's the epoch of the
- # monotonic clock. Since we want to know whether the kernel
- # timestamp is set at all we will instead ask for the realtime
- # clock for this timestamp.
-
- firmware_time = properties.Get('(ss)', 'org.freedesktop.systemd1.Manager', 'FirmwareTimestampMonotonic')
- loader_time = properties.Get('(ss)', 'org.freedesktop.systemd1.Manager', 'LoaderTimestampMonotonic')
- kernel_time = properties.Get('(ss)', 'org.freedesktop.systemd1.Manager', 'KernelTimestamp')
- initrd_time = properties.Get('(ss)', 'org.freedesktop.systemd1.Manager', 'InitRDTimestampMonotonic')
- userspace_time = properties.Get('(ss)', 'org.freedesktop.systemd1.Manager', 'UserspaceTimestampMonotonic')
- finish_time = properties.Get('(ss)', 'org.freedesktop.systemd1.Manager', 'FinishTimestampMonotonic')
-
- if finish_time == 0:
- sys.exit("Bootup is not yet finished. Please try again later.")
-
- assert firmware_time >= loader_time
- assert initrd_time <= userspace_time
- assert userspace_time <= finish_time
-
- return firmware_time, loader_time, kernel_time, initrd_time, userspace_time, finish_time
-
-def draw_box(context, j, k, l, m, r = 0, g = 0, b = 0):
- context.save()
- context.set_source_rgb(r, g, b)
- context.rectangle(j, k, l, m)
- context.fill()
- context.restore()
-
-def draw_text(context, x, y, text, size = 12, r = 0, g = 0, b = 0, vcenter = 0.5, hcenter = 0.5):
- context.save()
-
- context.set_source_rgb(r, g, b)
- context.select_font_face("Sans", cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_NORMAL)
- context.set_font_size(size)
-
- if vcenter or hcenter:
- x_bearing, y_bearing, width, height = context.text_extents(text)[:4]
-
- if hcenter:
- x = x - width*hcenter - x_bearing
-
- if vcenter:
- y = y - height*vcenter - y_bearing
-
- context.move_to(x, y)
- context.show_text(text)
-
- context.restore()
-
-def time():
-
- firmware_time, loader_time, kernel_time, initrd_time, userspace_time, finish_time = acquire_start_time()
-
- sys.stdout.write("Startup finished in ")
-
- if firmware_time > 0:
- sys.stdout.write("%lums (firmware) + " % ((firmware_time - loader_time) / 1000))
- if loader_time > 0:
- sys.stdout.write("%lums (loader) + " % (loader_time / 1000))
- if initrd_time > 0:
- sys.stdout.write("%lums (kernel) + %lums (initrd) + " % (initrd_time / 1000, (userspace_time - initrd_time) / 1000))
- elif kernel_time > 0:
- sys.stdout.write("%lums (kernel) + " % (userspace_time / 1000))
-
- sys.stdout.write("%lums (userspace) " % ((finish_time - userspace_time) / 1000))
-
- if kernel_time > 0:
- sys.stdout.write("= %lums\n" % ((firmware_time + finish_time) / 1000))
- else:
- sys.stdout.write("= %lums\n" % ((finish_time - userspace_time) / 1000))
-
-def blame():
-
- data = acquire_time_data()
- s = sorted(data, key = lambda i: i[2] - i[1], reverse = True)
-
- for name, ixt, aet, axt, iet in s:
-
- if ixt <= 0 or aet <= 0:
- continue
-
- if aet <= ixt:
- continue
-
- sys.stdout.write("%6lums %s\n" % ((aet - ixt) / 1000, name))
-
-def plot():
- if cairo is None:
- sys.exit("Failed to initilize python-cairo required for 'plot' verb.")
- firmware_time, loader_time, kernel_time, initrd_time, userspace_time, finish_time = acquire_start_time()
- data = acquire_time_data()
- s = sorted(data, key = lambda i: i[1])
-
- # Account for kernel and initramfs bars if they exist
- if initrd_time > 0:
- count = 3
- else:
- count = 2
-
- for name, ixt, aet, axt, iet in s:
-
- if (ixt >= userspace_time and ixt <= finish_time) or \
- (aet >= userspace_time and aet <= finish_time) or \
- (axt >= userspace_time and axt <= finish_time):
- count += 1
-
- border = 100
- bar_height = 20
- bar_space = bar_height * 0.1
-
- # 1000px = 10s, 1px = 10ms
- width = finish_time/10000 + border*2
- height = count * (bar_height + bar_space) + border * 2
-
- if width < 1000:
- width = 1000
-
- surface = cairo.SVGSurface(sys.stdout, width, height)
- context = cairo.Context(surface)
-
- draw_box(context, 0, 0, width, height, 1, 1, 1)
-
- context.translate(border + 0.5, border + 0.5)
-
- context.save()
- context.set_line_width(1)
- context.set_source_rgb(0.7, 0.7, 0.7)
-
- for x in range(0, int(finish_time/10000) + 100, 100):
- context.move_to(x, 0)
- context.line_to(x, height-border*2)
-
- context.move_to(0, 0)
- context.line_to(width-border*2, 0)
-
- context.move_to(0, height-border*2)
- context.line_to(width-border*2, height-border*2)
-
- context.stroke()
- context.restore()
-
- osrel = "Linux"
- if os.path.exists("/etc/os-release"):
- for line in open("/etc/os-release"):
- if line.startswith('PRETTY_NAME='):
- osrel = line[12:]
- osrel = osrel.strip('\"\n')
- break
-
- banner = "{} {} ({} {}) {}".format(osrel, *(os.uname()[1:5]))
- draw_text(context, 0, -15, banner, hcenter = 0, vcenter = 1)
-
- for x in range(0, int(finish_time/10000) + 100, 100):
- draw_text(context, x, -5, "%lus" % (x/100), vcenter = 0, hcenter = 0)
-
- y = 0
-
- # draw boxes for kernel and initramfs boot time
- if initrd_time > 0:
- draw_box(context, 0, y, initrd_time/10000, bar_height, 0.7, 0.7, 0.7)
- draw_text(context, 10, y + bar_height/2, "kernel", hcenter = 0)
- y += bar_height + bar_space
-
- draw_box(context, initrd_time/10000, y, userspace_time/10000-initrd_time/10000, bar_height, 0.7, 0.7, 0.7)
- draw_text(context, initrd_time/10000 + 10, y + bar_height/2, "initramfs", hcenter = 0)
- y += bar_height + bar_space
-
- else:
- draw_box(context, 0, y, userspace_time/10000, bar_height, 0.6, 0.6, 0.6)
- draw_text(context, 10, y + bar_height/2, "kernel", hcenter = 0)
- y += bar_height + bar_space
-
- draw_box(context, userspace_time/10000, y, finish_time/10000-userspace_time/10000, bar_height, 0.7, 0.7, 0.7)
- draw_text(context, userspace_time/10000 + 10, y + bar_height/2, "userspace", hcenter = 0)
- y += bar_height + bar_space
-
- for name, ixt, aet, axt, iet in s:
-
- drawn = False
- left = -1
-
- if ixt >= userspace_time and ixt <= finish_time:
-
- # Activating
- a = ixt
- b = min(filter(lambda x: x >= ixt, (aet, axt, iet, finish_time))) - ixt
-
- draw_box(context, a/10000, y, b/10000, bar_height, 1, 0, 0)
- drawn = True
-
- if left < 0:
- left = a
-
- if aet >= userspace_time and aet <= finish_time:
-
- # Active
- a = aet
- b = min(filter(lambda x: x >= aet, (axt, iet, finish_time))) - aet
-
- draw_box(context, a/10000, y, b/10000, bar_height, .8, .6, .6)
- drawn = True
-
- if left < 0:
- left = a
-
- if axt >= userspace_time and axt <= finish_time:
-
- # Deactivating
- a = axt
- b = min(filter(lambda x: x >= axt, (iet, finish_time))) - axt
-
- draw_box(context, a/10000, y, b/10000, bar_height, .6, .4, .4)
- drawn = True
-
- if left < 0:
- left = a
-
- if drawn:
- x = left/10000
-
- if x < width/2-border:
- draw_text(context, x + 10, y + bar_height/2, name, hcenter = 0)
- else:
- draw_text(context, x - 10, y + bar_height/2, name, hcenter = 1)
-
- y += bar_height + bar_space
-
- draw_text(context, 0, height-border*2, "Legend: Red = Activating; Pink = Active; Dark Pink = Deactivating", hcenter = 0, vcenter = -1)
-
- if initrd_time > 0:
- draw_text(context, 0, height-border*2 + bar_height, "Startup finished in %lums (kernel) + %lums (initramfs) + %lums (userspace) = %lums" % ( \
- initrd_time/1000, \
- (userspace_time - initrd_time)/1000, \
- (finish_time - userspace_time)/1000, \
- finish_time/1000), hcenter = 0, vcenter = -1)
- else:
- draw_text(context, 0, height-border*2 + bar_height, "Startup finished in %lums (kernel) + %lums (userspace) = %lums" % ( \
- userspace_time/1000, \
- (finish_time - userspace_time)/1000, \
- finish_time/1000), hcenter = 0, vcenter = -1)
-
- surface.finish()
-
-parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter,
- version='systemd-analyze @PACKAGE_VERSION@',
- description='Process systemd profiling information',
- epilog='''\
-time - print time spent in the kernel before reaching userspace
-blame - print list of running units ordered by time to init
-plot - output SVG graphic showing service initialization
-''')
-
-parser.add_argument('action', choices=('time', 'blame', 'plot'),
- default='time', nargs='?',
- help='action to perform (default: time)')
-parser.add_argument('--user', action='store_true',
- help='use the session bus')
-
-args = parser.parse_args()
-
-if args.user:
- bus = Gio.BusType.SESSION
-else:
- bus = Gio.BusType.SYSTEM
-
-verb = {'time' : time,
- 'blame': blame,
- 'plot' : plot,
- }
-verb.get(args.action)()
diff --git a/src/ask-password/ask-password.c b/src/ask-password/ask-password.c
index 5f675700f8..238cf12080 100644
--- a/src/ask-password/ask-password.c
+++ b/src/ask-password/ask-password.c
@@ -102,7 +102,7 @@ static int parse_argv(int argc, char *argv[]) {
break;
case ARG_TIMEOUT:
- if (parse_usec(optarg, &arg_timeout) < 0) {
+ if (parse_sec(optarg, &arg_timeout) < 0) {
log_error("Failed to parse --timeout parameter %s", optarg);
return -EINVAL;
}
diff --git a/src/binfmt/binfmt.c b/src/binfmt/binfmt.c
index 788fd4b1a4..5a42b3dbef 100644
--- a/src/binfmt/binfmt.c
+++ b/src/binfmt/binfmt.c
@@ -26,35 +26,43 @@
#include <stdio.h>
#include <limits.h>
#include <stdarg.h>
+#include <getopt.h>
#include "log.h"
#include "hashmap.h"
#include "strv.h"
#include "util.h"
#include "conf-files.h"
+#include "fileio.h"
+
+static const char conf_file_dirs[] =
+ "/etc/binfmt.d\0"
+ "/run/binfmt.d\0"
+ "/usr/local/lib/binfmt.d\0"
+ "/usr/lib/binfmt.d\0"
+#ifdef HAVE_SPLIT_USR
+ "/lib/binfmt.d\0"
+#endif
+ ;
static int delete_rule(const char *rule) {
- char *x, *fn = NULL, *e;
- int r;
+ _cleanup_free_ char *x = NULL, *fn = NULL;
+ char *e;
assert(rule[0]);
- if (!(x = strdup(rule)))
+ x = strdup(rule);
+ if (!x)
return log_oom();
e = strchrnul(x+1, x[0]);
*e = 0;
- asprintf(&fn, "/proc/sys/fs/binfmt_misc/%s", x+1);
- free(x);
-
+ fn = strappend("/proc/sys/fs/binfmt_misc/", x+1);
if (!fn)
return log_oom();
- r = write_one_line_file(fn, "-1");
- free(fn);
-
- return r;
+ return write_string_file(fn, "-1");
}
static int apply_rule(const char *rule) {
@@ -62,7 +70,8 @@ static int apply_rule(const char *rule) {
delete_rule(rule);
- if ((r = write_one_line_file("/proc/sys/fs/binfmt_misc/register", rule)) < 0) {
+ r = write_string_file("/proc/sys/fs/binfmt_misc/register", rule);
+ if (r < 0) {
log_error("Failed to add binary format: %s", strerror(-r));
return r;
}
@@ -71,21 +80,22 @@ static int apply_rule(const char *rule) {
}
static int apply_file(const char *path, bool ignore_enoent) {
- FILE *f;
- int r = 0;
+ _cleanup_fclose_ FILE *f = NULL;
+ int r;
assert(path);
- if (!(f = fopen(path, "re"))) {
- if (ignore_enoent && errno == ENOENT)
+ r = search_and_fopen_nulstr(path, "re", conf_file_dirs, &f);
+ if (r < 0) {
+ if (ignore_enoent && r == -ENOENT)
return 0;
- log_error("Failed to open file '%s', ignoring: %m", path);
- return -errno;
+ log_error("Failed to open file '%s', ignoring: %s", path, strerror(-r));
+ return r;
}
log_debug("apply: %s\n", path);
- while (!feof(f)) {
+ for (;;) {
char l[LINE_MAX], *p;
int k;
@@ -94,30 +104,71 @@ static int apply_file(const char *path, bool ignore_enoent) {
break;
log_error("Failed to read file '%s', ignoring: %m", path);
- r = -errno;
- goto finish;
+ return -errno;
}
p = strstrip(l);
-
if (!*p)
continue;
-
- if (strchr(COMMENTS, *p))
+ if (strchr(COMMENTS "\n", *p))
continue;
- if ((k = apply_rule(p)) < 0 && r == 0)
+ k = apply_rule(p);
+ if (k < 0 && r == 0)
r = k;
}
-finish:
- fclose(f);
-
return r;
}
+static int help(void) {
+
+ printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
+ "Registers binary formats.\n\n"
+ " -h --help Show this help\n",
+ program_invocation_short_name);
+
+ return 0;
+}
+
+static int parse_argv(int argc, char *argv[]) {
+
+ static const struct option options[] = {
+ { "help", no_argument, NULL, 'h' },
+ { NULL, 0, NULL, 0 }
+ };
+
+ 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 '?':
+ return -EINVAL;
+
+ default:
+ log_error("Unknown option code %c", c);
+ return -EINVAL;
+ }
+ }
+
+ return 1;
+}
+
int main(int argc, char *argv[]) {
- int r = 0;
+ 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();
@@ -125,46 +176,36 @@ int main(int argc, char *argv[]) {
umask(0022);
- if (argc > 1) {
- int i;
+ r = 0;
- for (i = 1; i < argc; i++) {
- int k;
+ 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 {
- char **files, **f;
+ _cleanup_strv_free_ char **files = NULL;
+ char **f;
- r = conf_files_list(&files, ".conf",
- "/etc/binfmt.d",
- "/run/binfmt.d",
- "/usr/local/lib/binfmt.d",
- "/usr/lib/binfmt.d",
-#ifdef HAVE_SPLIT_USR
- "/lib/binfmt.d",
-#endif
- NULL);
+ r = conf_files_list_nulstr(&files, ".conf", NULL, conf_file_dirs);
if (r < 0) {
log_error("Failed to enumerate binfmt.d files: %s", strerror(-r));
goto finish;
}
/* Flush out all rules */
- write_one_line_file("/proc/sys/fs/binfmt_misc/status", "-1");
+ write_string_file("/proc/sys/fs/binfmt_misc/status", "-1");
STRV_FOREACH(f, files) {
- int k;
-
k = apply_file(*f, true);
if (k < 0 && r == 0)
r = k;
}
-
- strv_free(files);
}
+
finish:
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}
diff --git a/src/boot/Makefile b/src/boot/Makefile
new file mode 120000
index 0000000000..d0b0e8e008
--- /dev/null
+++ b/src/boot/Makefile
@@ -0,0 +1 @@
+../Makefile \ No newline at end of file
diff --git a/src/boot/boot-efi.c b/src/boot/boot-efi.c
new file mode 100644
index 0000000000..9960c4d742
--- /dev/null
+++ b/src/boot/boot-efi.c
@@ -0,0 +1,190 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 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 <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdlib.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <locale.h>
+#include <string.h>
+#include <fnmatch.h>
+#include <fcntl.h>
+#include <sys/timex.h>
+
+#include "boot.h"
+#include "boot-loader.h"
+#include "build.h"
+#include "util.h"
+#include "strv.h"
+#include "efivars.h"
+#include "conf-files.h"
+
+static char *tilt_slashes(char *s) {
+ char *p;
+
+ if (!s)
+ return NULL;
+
+ for (p = s; *p; p++)
+ if (*p == '\\')
+ *p = '/';
+ return s;
+}
+
+static int get_boot_entries(struct boot_info *info) {
+ uint16_t *list;
+ int i, n;
+ int err = 0;
+
+ n = efi_get_boot_options(&list);
+ if (n < 0)
+ return n;
+
+ for (i = 0; i < n; i++) {
+ struct boot_info_entry *e;
+
+ e = realloc(info->fw_entries, (info->fw_entries_count+1) * sizeof(struct boot_info_entry));
+ if (!e) {
+ err = -ENOMEM;
+ break;
+ }
+ info->fw_entries = e;
+
+ e = &info->fw_entries[info->fw_entries_count];
+ memset(e, 0, sizeof(struct boot_info_entry));
+ e->order = -1;
+
+ err = efi_get_boot_option(list[i], &e->title, &e->part_uuid, &e->path);
+ if (err < 0)
+ continue;
+
+ if (isempty(e->title)) {
+ free(e->title);
+ e->title = NULL;
+ }
+ tilt_slashes(e->path);
+
+ e->id = list[i];
+ info->fw_entries_count++;
+ }
+
+ free(list);
+ return err;
+}
+
+static int find_active_entry(struct boot_info *info) {
+ uint16_t boot_cur;
+ void *buf;
+ size_t l;
+ size_t i;
+ int err = -ENOENT;
+
+ err = efi_get_variable(EFI_VENDOR_GLOBAL, "BootCurrent", NULL, &buf, &l);
+ if (err < 0)
+ return err;
+
+ memcpy(&boot_cur, buf, sizeof(uint16_t));
+ for (i = 0; i < info->fw_entries_count; i++) {
+ if (info->fw_entries[i].id != boot_cur)
+ continue;
+ info->fw_entry_active = i;
+ err = 0;
+ break;
+ }
+ free(buf);
+ return err;
+}
+
+static int get_boot_order(struct boot_info *info) {
+ size_t i, k;
+ int r;
+
+ r = efi_get_boot_order(&info->fw_entries_order);
+ if (r < 0)
+ return r;
+
+ info->fw_entries_order_count = r;
+
+ for (i = 0; i < info->fw_entries_order_count; i++) {
+ for (k = 0; k < info->fw_entries_count; k++) {
+ if (info->fw_entries[k].id != info->fw_entries_order[i])
+ continue;
+ info->fw_entries[k].order = i;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static int entry_cmp(const void *a, const void *b) {
+ const struct boot_info_entry *e1 = a;
+ const struct boot_info_entry *e2 = b;
+
+ /* boot order of active entries */
+ if (e1->order > 0 && e2->order > 0)
+ return e1->order - e2->order;
+
+ /* sort active entries before inactive ones */
+ if (e1->order > 0)
+ return 1;
+ if (e2->order > 0)
+ return -1;
+
+ /* order of inactive entries */
+ return e1->id - e2->id;
+}
+
+int boot_info_query(struct boot_info *info) {
+ char str[64];
+ char buf[64];
+ char *loader_active = NULL;
+
+ info->fw_secure_boot = is_efi_secure_boot();
+ info->fw_secure_boot_setup_mode = is_efi_secure_boot_setup_mode();
+
+ efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderInfo", &info->loader);
+
+ get_boot_entries(info);
+ if (info->fw_entries_count > 0) {
+ get_boot_order(info);
+ qsort(info->fw_entries, info->fw_entries_count, sizeof(struct boot_info_entry), entry_cmp);
+ find_active_entry(info);
+ }
+
+ efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderFirmwareType", &info->fw_type);
+ efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderFirmwareInfo", &info->fw_info);
+ efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderImageIdentifier", &info->loader_image_path);
+ tilt_slashes(info->loader_image_path);
+ efi_get_loader_device_part_uuid(&info->loader_part_uuid);
+
+ boot_loader_read_entries(info);
+ efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderEntrySelected", &loader_active);
+ if (loader_active) {
+ boot_loader_find_active_entry(info, loader_active);
+ free(loader_active);
+ }
+
+ snprintf(str, sizeof(str), "LoaderEntryOptions-%s", sd_id128_to_string(info->machine_id, buf));
+ efi_get_variable_string(EFI_VENDOR_LOADER, str, &info->loader_options_added);
+
+ return 0;
+}
diff --git a/src/boot/boot-loader.c b/src/boot/boot-loader.c
new file mode 100644
index 0000000000..d44fdb3aef
--- /dev/null
+++ b/src/boot/boot-loader.c
@@ -0,0 +1,132 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 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 <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdlib.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <locale.h>
+#include <string.h>
+#include <ctype.h>
+#include <sys/timex.h>
+
+#include "boot.h"
+#include "boot-loader.h"
+#include "build.h"
+#include "util.h"
+#include "strv.h"
+#include "conf-files.h"
+
+static char *loader_fragment_read_title(const char *fragment) {
+ FILE *f;
+ char line[LINE_MAX];
+ char *title = NULL;
+
+ f = fopen(fragment, "re");
+ if (!f)
+ return NULL;
+
+ while (fgets(line, sizeof(line), f) != NULL) {
+ char *s;
+ size_t l;
+
+ l = strlen(line);
+ if (l < 1)
+ continue;
+ if (line[l-1] == '\n')
+ line[l-1] = '\0';
+
+ s = line;
+ while (isspace(s[0]))
+ s++;
+
+ if (s[0] == '#')
+ continue;
+
+ if (!startswith(s, "title"))
+ continue;
+
+ s += strlen("title");
+ if (!isspace(s[0]))
+ continue;
+ while (isspace(s[0]))
+ s++;
+
+ title = strdup(s);
+ break;
+ }
+
+ fclose(f);
+ return title;
+}
+
+int boot_loader_read_entries(struct boot_info *info) {
+ _cleanup_strv_free_ char **files = NULL;
+ static const char *loader_dir[] = { "/boot/loader/entries", NULL};
+ unsigned int count;
+ unsigned int i;
+ int err;
+
+ err = conf_files_list_strv(&files, ".conf", NULL, loader_dir);
+ if (err < 0)
+ return err;
+
+ count = strv_length(files);
+ info->loader_entries = new0(struct boot_info_entry, count);
+ if (!info->loader_entries)
+ return -ENOMEM;
+
+ for (i = 0; i < count; i++) {
+ info->loader_entries[i].title = loader_fragment_read_title(files[i]);
+ info->loader_entries[i].path = strdup(files[i]);
+ if (!info->loader_entries[i].title || !info->loader_entries[i].path) {
+ free(info->loader_entries[i].title);
+ free(info->loader_entries[i].path);
+ return -ENOMEM;
+ }
+ info->loader_entries_count++;
+ }
+
+ return 0;
+}
+
+int boot_loader_find_active_entry(struct boot_info *info, const char *loader_active) {
+ char *fn;
+ unsigned int i;
+
+ if (!loader_active)
+ return -ENOENT;
+ if (info->loader_entries_count == 0)
+ return -ENOENT;
+
+ if (asprintf(&fn, "/boot/loader/entries/%s.conf", loader_active) < 0)
+ return -ENOMEM;
+
+ for (i = 0; i < info->loader_entries_count; i++) {
+ if (streq(fn, info->loader_entries[i].path)) {
+ info->loader_entry_active = i;
+ break;
+ }
+ }
+
+ free(fn);
+ return 0;
+}
diff --git a/src/boot/boot-loader.h b/src/boot/boot-loader.h
new file mode 100644
index 0000000000..08827c30ad
--- /dev/null
+++ b/src/boot/boot-loader.h
@@ -0,0 +1,25 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 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 <http://www.gnu.org/licenses/>.
+***/
+
+int boot_loader_read_entries(struct boot_info *info);
+int boot_loader_find_active_entry(struct boot_info *info, const char *loader_active);
diff --git a/src/boot/boot.h b/src/boot/boot.h
new file mode 100644
index 0000000000..bd8dc69d3d
--- /dev/null
+++ b/src/boot/boot.h
@@ -0,0 +1,64 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 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 <http://www.gnu.org/licenses/>.
+***/
+
+#include "sd-id128.h"
+
+/*
+ * Firmware and boot manager information to be filled in
+ * by the platform.
+ *
+ * This is partly EFI specific, if you add things, keep this
+ * as generic as possible to be able to re-use it on other
+ * platforms.
+ */
+
+struct boot_info_entry {
+ uint16_t id;
+ uint16_t order;
+ char *title;
+ sd_id128_t part_uuid;
+ char *path;
+};
+
+struct boot_info {
+ sd_id128_t machine_id;
+ sd_id128_t boot_id;
+ char *fw_type;
+ char *fw_info;
+ int fw_secure_boot;
+ int fw_secure_boot_setup_mode;
+ struct boot_info_entry *fw_entries;
+ size_t fw_entries_count;
+ uint16_t *fw_entries_order;
+ size_t fw_entries_order_count;
+ ssize_t fw_entry_active;
+ char *loader;
+ char *loader_image_path;
+ sd_id128_t loader_part_uuid;
+ struct boot_info_entry *loader_entries;
+ size_t loader_entries_count;
+ ssize_t loader_entry_active;
+ char *loader_options_added;
+};
+
+int boot_info_query(struct boot_info *info);
diff --git a/src/boot/bootctl.c b/src/boot/bootctl.c
new file mode 100644
index 0000000000..af694fdf64
--- /dev/null
+++ b/src/boot/bootctl.c
@@ -0,0 +1,288 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 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 <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdlib.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <locale.h>
+#include <string.h>
+#include <sys/timex.h>
+
+#include "boot.h"
+#include "build.h"
+#include "util.h"
+#include "utf8.h"
+
+static int help(void) {
+ printf("%s [OPTIONS...] COMMAND ...\n\n"
+ "Query or change firmware and boot mananger settings.\n\n"
+ " -h --help Show this help\n"
+ " --version Show package version\n"
+ "Commands:\n"
+ " status Show current boot settings\n",
+ program_invocation_short_name);
+
+ return 0;
+}
+
+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 },
+ { NULL, 0, NULL, 0 }
+ };
+
+ int c;
+
+ assert(argc >= 0);
+ assert(argv);
+ while ((c = getopt_long(argc, argv, "+hH:P", options, NULL)) >= 0) {
+
+ switch (c) {
+
+ case 'h':
+ help();
+ return 0;
+
+ case ARG_VERSION:
+ puts(PACKAGE_STRING);
+ puts(SYSTEMD_FEATURES);
+ return 0;
+
+ case '?':
+ return -EINVAL;
+
+ default:
+ log_error("Unknown option code %c", c);
+ return -EINVAL;
+ }
+ }
+
+ return 1;
+}
+
+static int boot_info_new(struct boot_info **info) {
+ struct boot_info *in;
+ int err;
+
+ in = new0(struct boot_info, 1);
+ if (!in)
+ return -ENOMEM;
+
+ err = sd_id128_get_machine(&in->machine_id);
+ if (err < 0)
+ goto err;
+
+ err = sd_id128_get_boot(&in->boot_id);
+ if (err < 0)
+ goto err;
+
+ in->fw_entry_active = -1;
+ in->loader_entry_active = -1;
+
+ *info = in;
+ return 0;
+err:
+ free(in);
+ return err;
+}
+
+static void boot_info_entries_free(struct boot_info_entry *entries, size_t n) {
+ size_t i;
+
+ for (i = 0; i < n; i++) {
+ free(entries[i].title);
+ free(entries[i].path);
+ }
+ free(entries);
+}
+
+static void boot_info_free(struct boot_info *info) {
+ free(info->fw_type);
+ free(info->fw_info);
+ boot_info_entries_free(info->fw_entries, info->fw_entries_count);
+ free(info->fw_entries_order);
+ free(info->loader);
+ free(info->loader_image_path);
+ free(info->loader_options_added);
+ boot_info_entries_free(info->loader_entries, info->loader_entries_count);
+ free(info);
+}
+
+static int show_status(char **args, unsigned n) {
+ char buf[64];
+ struct boot_info *info;
+ int err;
+
+ err = boot_info_new(&info);
+ if (err < 0)
+ return -ENOMEM;
+
+ err = boot_info_query(info);
+
+ printf("System:\n");
+ printf(" Machine ID: %s\n", sd_id128_to_string(info->machine_id, buf));
+ printf(" Boot ID: %s\n", sd_id128_to_string(info->boot_id, buf));
+ if (info->fw_type)
+ printf(" Firmware: %s (%s)\n", info->fw_type, strna(info->fw_info));
+ if (info->fw_secure_boot >= 0)
+ printf(" Secure Boot: %s\n", info->fw_secure_boot ? "enabled" : "disabled");
+ if (info->fw_secure_boot_setup_mode >= 0)
+ printf(" Setup Mode: %s\n", info->fw_secure_boot_setup_mode ? "setup" : "user");
+ printf("\n");
+
+ if (info->fw_entry_active >= 0) {
+ printf("Selected Firmware Entry:\n");
+ printf(" Title: %s\n", strna(info->fw_entries[info->fw_entry_active].title));
+ if (!sd_id128_equal(info->fw_entries[info->fw_entry_active].part_uuid, SD_ID128_NULL))
+ printf(" Partition: /dev/disk/by-partuuid/%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n",
+ SD_ID128_FORMAT_VAL(info->fw_entries[info->fw_entry_active].part_uuid));
+ else
+ printf(" Partition: n/a\n");
+ if (info->fw_entries[info->fw_entry_active].path)
+ printf(" File: %s%s\n", draw_special_char(DRAW_TREE_RIGHT), info->fw_entries[info->fw_entry_active].path);
+ }
+ printf("\n");
+
+ if (info->loader) {
+ printf("Boot Loader:\n");
+ printf(" Product: %s\n", info->loader);
+ if (!sd_id128_equal(info->loader_part_uuid, SD_ID128_NULL))
+ printf(" Partition: /dev/disk/by-partuuid/%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n",
+ SD_ID128_FORMAT_VAL(info->loader_part_uuid));
+ else
+ printf(" Partition: n/a\n");
+ printf(" File: %s%s\n", draw_special_char(DRAW_TREE_RIGHT), strna(info->loader_image_path));
+ printf("\n");
+
+ if (info->loader_entry_active >= 0) {
+ printf("Selected Boot Loader Entry:\n");
+ printf(" Title: %s\n", strna(info->loader_entries[info->loader_entry_active].title));
+ printf(" File: %s\n", info->loader_entries[info->loader_entry_active].path);
+ if (info->loader_options_added)
+ printf(" Options: %s\n", info->loader_options_added);
+ }
+ } else
+ printf("No suitable data is provided by the boot manager. See:\n"
+ " http://www.freedesktop.org/wiki/Software/systemd/BootLoaderInterface\n"
+ " http://www.freedesktop.org/wiki/Specifications/BootLoaderSpec\n"
+ "for details.\n");
+ printf("\n");
+
+ boot_info_free(info);
+ return err;
+}
+
+static int bootctl_main(int argc, char *argv[]) {
+ static const struct {
+ const char* verb;
+ const enum {
+ MORE,
+ LESS,
+ EQUAL
+ } argc_cmp;
+ const int argc;
+ int (* const dispatch)(char **args, unsigned n);
+ } verbs[] = {
+ { "status", LESS, 1, show_status },
+ };
+
+ int left;
+ unsigned i;
+
+ assert(argc >= 0);
+ assert(argv);
+
+ left = argc - optind;
+
+ if (left <= 0)
+ /* Special rule: no arguments means "status" */
+ i = 0;
+ else {
+ if (streq(argv[optind], "help")) {
+ help();
+ return 0;
+ }
+
+ for (i = 0; i < ELEMENTSOF(verbs); i++)
+ if (streq(argv[optind], verbs[i].verb))
+ break;
+
+ if (i >= ELEMENTSOF(verbs)) {
+ log_error("Unknown operation %s", argv[optind]);
+ return -EINVAL;
+ }
+ }
+
+ switch (verbs[i].argc_cmp) {
+
+ case EQUAL:
+ if (left != verbs[i].argc) {
+ log_error("Invalid number of arguments.");
+ return -EINVAL;
+ }
+ break;
+
+ case MORE:
+ if (left < verbs[i].argc) {
+ log_error("Too few arguments.");
+ return -EINVAL;
+ }
+ break;
+
+ case LESS:
+ if (left > verbs[i].argc) {
+ log_error("Too many arguments.");
+ return -EINVAL;
+ }
+ break;
+
+ default:
+ assert_not_reached("Unknown comparison operator.");
+ }
+
+ return verbs[i].dispatch(argv + optind, left);
+}
+
+int main(int argc, char *argv[]) {
+ int r, retval = EXIT_FAILURE;
+
+ log_parse_environment();
+ log_open();
+
+ r = parse_argv(argc, argv);
+ if (r < 0)
+ goto finish;
+ else if (r == 0) {
+ retval = EXIT_SUCCESS;
+ goto finish;
+ }
+
+ r = bootctl_main(argc, argv);
+ retval = r < 0 ? EXIT_FAILURE : r;
+finish:
+ return retval;
+}
diff --git a/src/bootchart/Makefile b/src/bootchart/Makefile
new file mode 120000
index 0000000000..d0b0e8e008
--- /dev/null
+++ b/src/bootchart/Makefile
@@ -0,0 +1 @@
+../Makefile \ No newline at end of file
diff --git a/src/bootchart/README b/src/bootchart/README
deleted file mode 100644
index a209caf46b..0000000000
--- a/src/bootchart/README
+++ /dev/null
@@ -1,83 +0,0 @@
-
-Bootchart - a 'startup' graphing tool
-
---
-
-Bootchart is a tool, usually run at system startup, that collects and graphs
-the CPU and disk load of the system as it works. The output of bootchart is
-an SVG graph. Normally, bootchart is invoked as `bootchartd` by the kernel
-by passing "init=/sbin/bootchartd" to the kernel. Bootchart will then fork
-init off to resume normal system startup, while monitoring and logging
-startup information in the background.
-
-After collecting a certain amount of data (usually 15-30 seconds) the logging
-stops and a graph is generated from the logged information. This graph
-contains vital clues to which resources are being used, in which order, and
-where possible problems exist in the startup sequence of the system.
-
-Of course, bootchart can also be used at any moment in time to collect and
-graph some data for an amount of time. Bootchart does not even require root
-privileges to do so, and will happily run as a normal user. Bootchart graphs
-are by default written time-stamped in /var/log.
-
---
-
-This version of bootchart was implemented from scratch and inspired by former
-incantations of bootchart:
-
-- The original bash/shell code implemented bootchart. This version logged all
-data into a compressed tarball for later processing, and did not create a graph
-on it's own.
-
-- The C-code implementation found in Ubuntu. This version replaced above shell
-code version with a faster and efficient data logger, but still did not graph
-code itself.
-
-- the original Java-based bootchart, the original graphing program that created
-a bootchart graph from logged data.
-
-- the pybootchartgui.py program, which created a graph based on the data logged
-by either standalone data logger.
-
-The version you are looking at combines these 2 parts into a single program,
-which makes running it and creating graphs a bit more efficient and simple.
-You can now run a single program at startup instead of 2. There are no timing
-problems (the graphing stage will never run if the logging stage didn't
-finish). The logged data isn't being written to disc first, then read again.
-Also, the data kept in memory is reduced to the absolute minimum needed to
-keep memory use low.
-
---
-
-Requirements: glibc. Your kernel must have procfs support and several
-proc output options enabled:
- CONFIG_PROC_FS
- CONFIG_SCHEDSTATS
- CONFIG_SCHED_DEBUG
-at a minimum. bootchartd itself does not require any graphics library
-to generate the SVG output file.
-
---
-
-Configuration: please see bootchartd --help, as well as /etc/bootchartd.conf
-and/or /usr/share/doc/bootchart/bootchartd.conf.example for a list of
-configurable options.
-
---
-
-Many thanks to those who contributed ideas and code:
- - Ziga Mahkovec - Original bootchart author
- - Anders Norgaard - PyBootchartgui
- - Michael Meeks - bootchart2
- - Scott James Remnant - Ubuntu C-based logger
- - Arjan van der Ven - for the idea to merge bootgraph.pl functionality
-
---
-
-For bugs, please contact the author or current maintainer:
-Auke Kok <auke-jan.h.kok@intel.com>
-
---
-
-Download bootchart releases here: http://foo-projects.org/~sofar/bootchart/
-Source code is hosted here: git://github.com/sofar/bootchart
diff --git a/src/bootchart/bootchart.c b/src/bootchart/bootchart.c
index 2eeb37ae53..8be5a27afa 100644
--- a/src/bootchart/bootchart.c
+++ b/src/bootchart/bootchart.c
@@ -1,21 +1,42 @@
-/*
- * bootchart.c
- *
- * Copyright (C) 2009-2012 Intel Coproration
- *
- * Authors:
- * Auke Kok <auke-jan.h.kok@intel.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; version 2
- * of the License.
- */
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+/***
+ This file is part of systemd.
+
+ Copyright (C) 2009-2013 Intel Coproration
+
+ Authors:
+ Auke Kok <auke-jan.h.kok@intel.com>
+
+ 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 <http://www.gnu.org/licenses/>.
+ ***/
+
+/***
+
+ Many thanks to those who contributed ideas and code:
+ - Ziga Mahkovec - Original bootchart author
+ - Anders Norgaard - PyBootchartgui
+ - Michael Meeks - bootchart2
+ - Scott James Remnant - Ubuntu C-based logger
+ - Arjan van der Ven - for the idea to merge bootgraph.pl functionality
+
+ ***/
#include <sys/time.h>
#include <sys/types.h>
#include <sys/resource.h>
+#include <sys/stat.h>
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
@@ -25,328 +46,436 @@
#include <getopt.h>
#include <limits.h>
#include <errno.h>
-
-
+#include <fcntl.h>
+#include <stdbool.h>
+#include <systemd/sd-journal.h>
+
+#include "util.h"
+#include "fileio.h"
+#include "macro.h"
+#include "conf-parser.h"
+#include "strxcpyx.h"
+#include "path-util.h"
+#include "store.h"
+#include "svg.h"
#include "bootchart.h"
+#include "list.h"
double graph_start;
double log_start;
-double sampletime[MAXSAMPLES];
struct ps_struct *ps_first;
-struct block_stat_struct blockstat[MAXSAMPLES];
-int entropy_avail[MAXSAMPLES];
-struct cpu_stat_struct cpustat[MAXCPUS];
int pscount;
int cpus;
double interval;
-FILE *of;
+FILE *of = NULL;
int overrun = 0;
static int exiting = 0;
+int sysfd=-1;
/* graph defaults */
-int entropy = 0;
-int initcall = 1;
-int relative;
-int filter = 1;
-int pss = 0;
+bool arg_entropy = false;
+bool initcall = true;
+bool arg_relative = false;
+bool arg_filter = true;
+bool arg_show_cmdline = false;
+bool arg_pss = false;
int samples;
-int len = 500; /* we record len+1 (1 start sample) */
-double hz = 25.0; /* 20 seconds log time */
-double scale_x = 100.0; /* 100px = 1sec */
-double scale_y = 20.0; /* 16px = 1 process bar */
+int arg_samples_len = 500; /* we record len+1 (1 start sample) */
+double arg_hz = 25.0; /* 20 seconds log time */
+double arg_scale_x = 100.0; /* 100px = 1sec */
+double arg_scale_y = 20.0; /* 16px = 1 process bar */
+static struct list_sample_data *sampledata;
+struct list_sample_data *head;
+
+char arg_init_path[PATH_MAX] = "/sbin/init";
+char arg_output_path[PATH_MAX] = "/run/log";
+
+static void signal_handler(int sig) {
+ if (sig++)
+ sig--;
+ exiting = 1;
+}
-char init_path[PATH_MAX] = "/sbin/init";
-char output_path[PATH_MAX] = "/var/log";
+#define BOOTCHART_CONF "/etc/systemd/bootchart.conf"
+
+#define BOOTCHART_MAX (16*1024*1024)
+
+static void parse_conf(void) {
+ char *init = NULL, *output = NULL;
+ const ConfigTableItem items[] = {
+ { "Bootchart", "Samples", config_parse_int, 0, &arg_samples_len },
+ { "Bootchart", "Frequency", config_parse_double, 0, &arg_hz },
+ { "Bootchart", "Relative", config_parse_bool, 0, &arg_relative },
+ { "Bootchart", "Filter", config_parse_bool, 0, &arg_filter },
+ { "Bootchart", "Output", config_parse_path, 0, &output },
+ { "Bootchart", "Init", config_parse_path, 0, &init },
+ { "Bootchart", "PlotMemoryUsage", config_parse_bool, 0, &arg_pss },
+ { "Bootchart", "PlotEntropyGraph", config_parse_bool, 0, &arg_entropy },
+ { "Bootchart", "ScaleX", config_parse_double, 0, &arg_scale_x },
+ { "Bootchart", "ScaleY", config_parse_double, 0, &arg_scale_y },
+ { NULL, NULL, NULL, 0, NULL }
+ };
+ _cleanup_fclose_ FILE *f;
+ int r;
+
+ f = fopen(BOOTCHART_CONF, "re");
+ if (!f)
+ return;
+
+ r = config_parse(NULL, BOOTCHART_CONF, f,
+ NULL, config_item_table_lookup, (void*) items, true, false, NULL);
+ if (r < 0)
+ log_warning("Failed to parse configuration file: %s", strerror(-r));
+
+ if (init != NULL)
+ strscpy(arg_init_path, sizeof(arg_init_path), init);
+ if (output != NULL)
+ strscpy(arg_output_path, sizeof(arg_output_path), output);
+}
-static struct rlimit rlim;
+static int parse_args(int argc, char *argv[]) {
+ static struct option options[] = {
+ {"rel", no_argument, NULL, 'r'},
+ {"freq", required_argument, NULL, 'f'},
+ {"samples", required_argument, NULL, 'n'},
+ {"pss", no_argument, NULL, 'p'},
+ {"output", required_argument, NULL, 'o'},
+ {"init", required_argument, NULL, 'i'},
+ {"no-filter", no_argument, NULL, 'F'},
+ {"cmdline", no_argument, NULL, 'C'},
+ {"help", no_argument, NULL, 'h'},
+ {"scale-x", required_argument, NULL, 'x'},
+ {"scale-y", required_argument, NULL, 'y'},
+ {"entropy", no_argument, NULL, 'e'},
+ {NULL, 0, NULL, 0}
+ };
+ int c;
+
+ while ((c = getopt_long(argc, argv, "erpf:n:o:i:FChx:y:", options, NULL)) >= 0) {
+ int r;
+
+ switch (c) {
+ case 'r':
+ arg_relative = true;
+ break;
+ case 'f':
+ r = safe_atod(optarg, &arg_hz);
+ if (r < 0)
+ log_warning("failed to parse --freq/-f argument '%s': %s",
+ optarg, strerror(-r));
+ break;
+ case 'F':
+ arg_filter = false;
+ break;
+ case 'C':
+ arg_show_cmdline = true;
+ break;
+ case 'n':
+ r = safe_atoi(optarg, &arg_samples_len);
+ if (r < 0)
+ log_warning("failed to parse --samples/-n argument '%s': %s",
+ optarg, strerror(-r));
+ break;
+ case 'o':
+ path_kill_slashes(optarg);
+ strscpy(arg_output_path, sizeof(arg_output_path), optarg);
+ break;
+ case 'i':
+ path_kill_slashes(optarg);
+ strscpy(arg_init_path, sizeof(arg_init_path), optarg);
+ break;
+ case 'p':
+ arg_pss = true;
+ break;
+ case 'x':
+ r = safe_atod(optarg, &arg_scale_x);
+ if (r < 0)
+ log_warning("failed to parse --scale-x/-x argument '%s': %s",
+ optarg, strerror(-r));
+ break;
+ case 'y':
+ r = safe_atod(optarg, &arg_scale_y);
+ if (r < 0)
+ log_warning("failed to parse --scale-y/-y argument '%s': %s",
+ optarg, strerror(-r));
+ break;
+ case 'e':
+ arg_entropy = true;
+ break;
+ case 'h':
+ fprintf(stderr, "Usage: %s [OPTIONS]\n", argv[0]);
+ fprintf(stderr, " --rel, -r Record time relative to recording\n");
+ fprintf(stderr, " --freq, -f f Sample frequency [%f]\n", arg_hz);
+ fprintf(stderr, " --samples, -n N Stop sampling at [%d] samples\n", arg_samples_len);
+ fprintf(stderr, " --scale-x, -x N Scale the graph horizontally [%f] \n", arg_scale_x);
+ fprintf(stderr, " --scale-y, -y N Scale the graph vertically [%f] \n", arg_scale_y);
+ fprintf(stderr, " --pss, -p Enable PSS graph (CPU intensive)\n");
+ fprintf(stderr, " --entropy, -e Enable the entropy_avail graph\n");
+ fprintf(stderr, " --output, -o [PATH] Path to output files [%s]\n", arg_output_path);
+ fprintf(stderr, " --init, -i [PATH] Path to init executable [%s]\n", arg_init_path);
+ fprintf(stderr, " --no-filter, -F Disable filtering of processes from the graph\n");
+ fprintf(stderr, " that are of less importance or short-lived\n");
+ fprintf(stderr, " --cmdline, -C Display the full command line with arguments\n");
+ fprintf(stderr, " of processes, instead of only the process name\n");
+ fprintf(stderr, " --help, -h Display this message\n");
+ fprintf(stderr, "See bootchart.conf for more information.\n");
+ exit (EXIT_SUCCESS);
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (arg_hz <= 0.0) {
+ fprintf(stderr, "Error: Frequency needs to be > 0\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
-static void signal_handler(int sig)
+static void do_journal_append(char *file)
{
- if (sig++)
- sig--;
- exiting = 1;
+ struct iovec iovec[5];
+ int r, f, j = 0;
+ ssize_t n;
+ _cleanup_free_ char *bootchart_file = NULL, *bootchart_message = NULL,
+ *p = NULL;
+
+ bootchart_file = strappend("BOOTCHART_FILE=", file);
+ if (bootchart_file)
+ IOVEC_SET_STRING(iovec[j++], bootchart_file);
+
+ IOVEC_SET_STRING(iovec[j++], "MESSAGE_ID=9f26aa562cf440c2b16c773d0479b518");
+ IOVEC_SET_STRING(iovec[j++], "PRIORITY=7");
+ bootchart_message = strjoin("MESSAGE=Bootchart created: ", file, NULL);
+ if (bootchart_message)
+ IOVEC_SET_STRING(iovec[j++], bootchart_message);
+
+ p = malloc(9 + BOOTCHART_MAX);
+ if (!p) {
+ r = log_oom();
+ return;
+ }
+
+ memcpy(p, "BOOTCHART=", 10);
+
+ f = open(file, O_RDONLY);
+ if (f < 0) {
+ log_error("Failed to read bootchart data: %m\n");
+ return;
+ }
+ n = loop_read(f, p + 10, BOOTCHART_MAX, false);
+ if (n < 0) {
+ log_error("Failed to read bootchart data: %s\n", strerror(-n));
+ close(f);
+ return;
+ }
+ close(f);
+
+ iovec[j].iov_base = p;
+ iovec[j].iov_len = 10 + n;
+ j++;
+
+ r = sd_journal_sendv(iovec, j);
+ if (r < 0)
+ log_error("Failed to send bootchart: %s", strerror(-r));
}
-
-int main(int argc, char *argv[])
-{
- struct sigaction sig;
- struct ps_struct *ps;
- char output_file[PATH_MAX];
- char datestr[200];
- time_t t;
- FILE *f;
- int gind;
- int i;
-
- memset(&t, 0, sizeof(time_t));
-
- rlim.rlim_cur = 4096;
- rlim.rlim_max = 4096;
- (void) setrlimit(RLIMIT_NOFILE, &rlim);
-
- f = fopen("/etc/systemd/bootchart.conf", "r");
- if (f) {
- char buf[256];
- char *key;
- char *val;
-
- while (fgets(buf, 80, f) != NULL) {
- char *c;
-
- c = strchr(buf, '\n');
- if (c) *c = 0; /* remove trailing \n */
-
- if (buf[0] == '#')
- continue; /* comment line */
-
- key = strtok(buf, "=");
- if (!key)
- continue;
- val = strtok(NULL, "=");
- if (!val)
- continue;
-
- // todo: filter leading/trailing whitespace
-
- if (!strcmp(key, "samples"))
- len = atoi(val);
- if (!strcmp(key, "freq"))
- hz = atof(val);
- if (!strcmp(key, "rel"))
- relative = atoi(val);
- if (!strcmp(key, "filter"))
- filter = atoi(val);
- if (!strcmp(key, "pss"))
- pss = atoi(val);
- if (!strcmp(key, "output"))
- strncpy(output_path, val, PATH_MAX - 1);
- if (!strcmp(key, "init"))
- strncpy(init_path, val, PATH_MAX - 1);
- if (!strcmp(key, "scale_x"))
- scale_x = atof(val);
- if (!strcmp(key, "scale_y"))
- scale_y = atof(val);
- if (!strcmp(key, "entropy"))
- entropy = atoi(val);
- }
- fclose(f);
- }
-
- while (1) {
- static struct option opts[] = {
- {"rel", 0, NULL, 'r'},
- {"freq", 1, NULL, 'f'},
- {"samples", 1, NULL, 'n'},
- {"pss", 0, NULL, 'p'},
- {"output", 1, NULL, 'o'},
- {"init", 1, NULL, 'i'},
- {"filter", 0, NULL, 'F'},
- {"help", 0, NULL, 'h'},
- {"scale-x", 1, NULL, 'x'},
- {"scale-y", 1, NULL, 'y'},
- {"entropy", 0, NULL, 'e'},
- {NULL, 0, NULL, 0}
- };
-
- gind = 0;
-
- i = getopt_long(argc, argv, "erpf:n:o:i:Fhx:y:", opts, &gind);
- if (i == -1)
- break;
- switch (i) {
- case 'r':
- relative = 1;
- break;
- case 'f':
- hz = atof(optarg);
- break;
- case 'F':
- filter = 0;
- break;
- case 'n':
- len = atoi(optarg);
- break;
- case 'o':
- strncpy(output_path, optarg, PATH_MAX - 1);
- break;
- case 'i':
- strncpy(init_path, optarg, PATH_MAX - 1);
- break;
- case 'p':
- pss = 1;
- break;
- case 'x':
- scale_x = atof(optarg);
- break;
- case 'y':
- scale_y = atof(optarg);
- break;
- case 'e':
- entropy = 1;
- break;
- case 'h':
- fprintf(stderr, "Usage: %s [OPTIONS]\n", argv[0]);
- fprintf(stderr, " --rel, -r Record time relative to recording\n");
- fprintf(stderr, " --freq, -f N Sample frequency [%f]\n", hz);
- fprintf(stderr, " --samples, -n N Stop sampling at [%d] samples\n", len);
- fprintf(stderr, " --scale-x, -x N Scale the graph horizontally [%f] \n", scale_x);
- fprintf(stderr, " --scale-y, -y N Scale the graph vertically [%f] \n", scale_y);
- fprintf(stderr, " --pss, -p Enable PSS graph (CPU intensive)\n");
- fprintf(stderr, " --entropy, -e Enable the entropy_avail graph\n");
- fprintf(stderr, " --output, -o [PATH] Path to output files [%s]\n", output_path);
- fprintf(stderr, " --init, -i [PATH] Path to init executable [%s]\n", init_path);
- fprintf(stderr, " --filter, -F Disable filtering of processes from the graph\n");
- fprintf(stderr, " that are of less importance or short-lived\n");
- fprintf(stderr, " --help, -h Display this message\n");
- fprintf(stderr, "See the installed README and bootchartd.conf.example for more information.\n");
- exit (EXIT_SUCCESS);
- break;
- default:
- break;
- }
- }
-
- if (len > MAXSAMPLES) {
- fprintf(stderr, "Error: samples exceeds maximum\n");
- exit(EXIT_FAILURE);
- }
-
- if (hz <= 0.0) {
- fprintf(stderr, "Error: Frequency needs to be > 0\n");
- exit(EXIT_FAILURE);
- }
-
- /*
- * If the kernel executed us through init=/sbin/bootchartd, then
- * fork:
- * - parent execs executable specified via init_path[] (/sbin/init by default) as pid=1
- * - child logs data
- */
- if (getpid() == 1) {
- if (fork()) {
- /* parent */
- execl(init_path, init_path, NULL);
- }
- }
-
- /* start with empty ps LL */
- ps_first = malloc(sizeof(struct ps_struct));
- if (!ps_first) {
- perror("malloc(ps_struct)");
- exit(EXIT_FAILURE);
- }
- memset(ps_first, 0, sizeof(struct ps_struct));
-
- /* handle TERM/INT nicely */
- memset(&sig, 0, sizeof(struct sigaction));
- sig.sa_handler = signal_handler;
- sigaction(SIGHUP, &sig, NULL);
-
- interval = (1.0 / hz) * 1000000000.0;
-
- log_uptime();
-
- /* main program loop */
- while (!exiting) {
- int res;
- double sample_stop;
- struct timespec req;
- time_t newint_s;
- long newint_ns;
- double elapsed;
- double timeleft;
-
- sampletime[samples] = gettime_ns();
-
- /* wait for /proc to become available, discarding samples */
- if (!(graph_start > 0.0))
- log_uptime();
- else
- log_sample(samples);
-
- sample_stop = gettime_ns();
-
- elapsed = (sample_stop - sampletime[samples]) * 1000000000.0;
- timeleft = interval - elapsed;
-
- newint_s = (time_t)(timeleft / 1000000000.0);
- newint_ns = (long)(timeleft - (newint_s * 1000000000.0));
-
- /*
- * check if we have not consumed our entire timeslice. If we
- * do, don't sleep and take a new sample right away.
- * we'll lose all the missed samples and overrun our total
- * time
- */
- if ((newint_ns > 0) || (newint_s > 0)) {
- req.tv_sec = newint_s;
- req.tv_nsec = newint_ns;
-
- res = nanosleep(&req, NULL);
- if (res) {
- if (errno == EINTR) {
- /* caught signal, probably HUP! */
- break;
- }
- perror("nanosleep()");
- exit (EXIT_FAILURE);
- }
- } else {
- overrun++;
- /* calculate how many samples we lost and scrap them */
- len = len + ((int)(newint_ns / interval));
- }
-
- samples++;
-
- if (samples > len)
- break;
-
- }
-
- /* do some cleanup, close fd's */
- ps = ps_first;
- while (ps->next_ps) {
- ps = ps->next_ps;
- if (ps->schedstat)
- close(ps->schedstat);
- if (ps->sched)
- close(ps->sched);
- if (ps->smaps)
- fclose(ps->smaps);
- }
- closedir(proc);
-
- t = time(NULL);
- strftime(datestr, sizeof(datestr), "%Y%m%d-%H%M", localtime(&t));
- snprintf(output_file, PATH_MAX, "%s/bootchart-%s.svg", output_path, datestr);
-
- of = fopen(output_file, "w");
- if (!of) {
- perror("open output_file");
- exit (EXIT_FAILURE);
- }
-
- svg_do();
-
- fprintf(stderr, "bootchartd: Wrote %s\n", output_file);
- fclose(of);
-
- /* nitpic cleanups */
- ps = ps_first;
- while (ps->next_ps) {
- struct ps_struct *old = ps;
- ps = ps->next_ps;
- free(old->sample);
- free(old);
- }
- free(ps->sample);
- free(ps);
-
- /* don't complain when overrun once, happens most commonly on 1st sample */
- if (overrun > 1)
- fprintf(stderr, "bootchartd: Warning: sample time overrun %i times\n", overrun);
-
- return 0;
+int main(int argc, char *argv[]) {
+ _cleanup_free_ char *build = NULL;
+ struct sigaction sig = {
+ .sa_handler = signal_handler,
+ };
+ struct ps_struct *ps;
+ char output_file[PATH_MAX];
+ char datestr[200];
+ time_t t = 0;
+ int r;
+ struct rlimit rlim;
+
+ parse_conf();
+
+ r = parse_args(argc, argv);
+ if (r < 0)
+ return EXIT_FAILURE;
+
+ /*
+ * If the kernel executed us through init=/usr/lib/systemd/systemd-bootchart, then
+ * fork:
+ * - parent execs executable specified via init_path[] (/sbin/init by default) as pid=1
+ * - child logs data
+ */
+ if (getpid() == 1) {
+ if (fork()) {
+ /* parent */
+ execl(arg_init_path, arg_init_path, NULL);
+ }
+ }
+ argv[0][0] = '@';
+
+ rlim.rlim_cur = 4096;
+ rlim.rlim_max = 4096;
+ (void) setrlimit(RLIMIT_NOFILE, &rlim);
+
+ /* start with empty ps LL */
+ ps_first = calloc(1, sizeof(struct ps_struct));
+ if (!ps_first) {
+ perror("calloc(ps_struct)");
+ exit(EXIT_FAILURE);
+ }
+
+ /* handle TERM/INT nicely */
+ sigaction(SIGHUP, &sig, NULL);
+
+ interval = (1.0 / arg_hz) * 1000000000.0;
+
+ log_uptime();
+
+ LIST_HEAD_INIT(struct list_sample_data, head);
+
+ /* main program loop */
+ for (samples = 0; !exiting && samples < arg_samples_len; samples++) {
+ int res;
+ double sample_stop;
+ struct timespec req;
+ time_t newint_s;
+ long newint_ns;
+ double elapsed;
+ double timeleft;
+
+ sampledata = new0(struct list_sample_data, 1);
+ if (sampledata == NULL) {
+ log_error("Failed to allocate memory for a node: %m");
+ return -1;
+ }
+
+ sampledata->sampletime = gettime_ns();
+ sampledata->counter = samples;
+
+ if (!of && (access(arg_output_path, R_OK|W_OK|X_OK) == 0)) {
+ t = time(NULL);
+ strftime(datestr, sizeof(datestr), "%Y%m%d-%H%M", localtime(&t));
+ snprintf(output_file, PATH_MAX, "%s/bootchart-%s.svg", arg_output_path, datestr);
+ of = fopen(output_file, "w");
+ }
+
+ if (sysfd < 0)
+ sysfd = open("/sys", O_RDONLY);
+
+ if (!build)
+ parse_env_file("/etc/os-release", NEWLINE,
+ "PRETTY_NAME", &build,
+ NULL);
+
+ /* wait for /proc to become available, discarding samples */
+ if (graph_start <= 0.0)
+ log_uptime();
+ else
+ log_sample(samples, &sampledata);
+
+ sample_stop = gettime_ns();
+
+ elapsed = (sample_stop - sampledata->sampletime) * 1000000000.0;
+ timeleft = interval - elapsed;
+
+ newint_s = (time_t)(timeleft / 1000000000.0);
+ newint_ns = (long)(timeleft - (newint_s * 1000000000.0));
+
+ /*
+ * check if we have not consumed our entire timeslice. If we
+ * do, don't sleep and take a new sample right away.
+ * we'll lose all the missed samples and overrun our total
+ * time
+ */
+ if (newint_ns > 0 || newint_s > 0) {
+ req.tv_sec = newint_s;
+ req.tv_nsec = newint_ns;
+
+ res = nanosleep(&req, NULL);
+ if (res) {
+ if (errno == EINTR) {
+ /* caught signal, probably HUP! */
+ break;
+ }
+ perror("nanosleep()");
+ exit (EXIT_FAILURE);
+ }
+ } else {
+ overrun++;
+ /* calculate how many samples we lost and scrap them */
+ arg_samples_len -= (int)(newint_ns / interval);
+ }
+ LIST_PREPEND(struct list_sample_data, link, head, sampledata);
+ }
+
+ /* do some cleanup, close fd's */
+ ps = ps_first;
+ while (ps->next_ps) {
+ ps = ps->next_ps;
+ if (ps->schedstat)
+ close(ps->schedstat);
+ if (ps->sched)
+ close(ps->sched);
+ if (ps->smaps)
+ fclose(ps->smaps);
+ }
+
+ if (!of) {
+ t = time(NULL);
+ strftime(datestr, sizeof(datestr), "%Y%m%d-%H%M", localtime(&t));
+ snprintf(output_file, PATH_MAX, "%s/bootchart-%s.svg", arg_output_path, datestr);
+ of = fopen(output_file, "w");
+ }
+
+ if (!of) {
+ fprintf(stderr, "opening output file '%s': %m\n", output_file);
+ exit (EXIT_FAILURE);
+ }
+
+ svg_do(build);
+
+ fprintf(stderr, "systemd-bootchart wrote %s\n", output_file);
+
+ do_journal_append(output_file);
+
+ if (of)
+ fclose(of);
+
+ closedir(proc);
+ if (sysfd >= 0)
+ close(sysfd);
+
+ /* nitpic cleanups */
+ ps = ps_first->next_ps;
+ while (ps->next_ps) {
+ struct ps_struct *old;
+
+ old = ps;
+ old->sample = ps->first;
+ ps = ps->next_ps;
+ while (old->sample->next) {
+ struct ps_sched_struct *oldsample = old->sample;
+
+ old->sample = old->sample->next;
+ free(oldsample);
+ }
+ free(old->sample);
+ free(old);
+ }
+ free(ps->sample);
+ free(ps);
+
+ sampledata = head;
+ while (sampledata->link_prev) {
+ struct list_sample_data *old_sampledata = sampledata;
+ sampledata = sampledata->link_prev;
+ free(old_sampledata);
+ }
+ free(sampledata);
+ /* don't complain when overrun once, happens most commonly on 1st sample */
+ if (overrun > 1)
+ fprintf(stderr, "systemd-boochart: Warning: sample time overrun %i times\n", overrun);
+
+ return 0;
}
diff --git a/src/bootchart/bootchart.conf b/src/bootchart/bootchart.conf
index 06c1b47510..48fad02726 100644
--- a/src/bootchart/bootchart.conf
+++ b/src/bootchart/bootchart.conf
@@ -1,4 +1,3 @@
-
# This file is part of systemd.
#
# systemd is free software; you can redistribute it and/or modify it
@@ -6,15 +5,16 @@
# the Free Software Foundation; either version 2.1 of the License, or
# (at your option) any later version.
#
-# See systemd-bootchart.conf(5) for details
+# See bootchart.conf(5) for details
-#samples=500
-#freq=25
-#rel=0
-#filter=1
-#output=<folder name, defaults to /var/log>
-#init=/path/to/init-binary
-#pss=0
-#entropy=0
-#scale_x=100
-#scale_y=20
+[Bootchart]
+#Samples=500
+#Frequency=25
+#Relative=no
+#Filter=yes
+#Output=<folder name, defaults to /run/log>
+#Init=/path/to/init-binary
+#PlotMemoryUsage=no
+#PlotEntropyGraph=no
+#ScaleX=100
+#ScaleY=20
diff --git a/src/bootchart/bootchart.h b/src/bootchart/bootchart.h
index 0d8bed16b0..d0273421de 100644
--- a/src/bootchart/bootchart.h
+++ b/src/bootchart/bootchart.h
@@ -1,84 +1,106 @@
-/*
- * bootchart.h
- *
- * Copyright (C) 2009-2012 Intel Coproration
- *
- * Authors:
- * Auke Kok <auke-jan.h.kok@intel.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; version 2
- * of the License.
- */
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright (C) 2009-2013 Intel Coproration
+
+ Authors:
+ Auke Kok <auke-jan.h.kok@intel.com>
+
+ 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 <http://www.gnu.org/licenses/>.
+***/
#include <dirent.h>
+#include <stdbool.h>
+#include "list.h"
#define MAXCPUS 16
#define MAXPIDS 65535
-#define MAXSAMPLES 8192
-
struct block_stat_struct {
- /* /proc/vmstat pgpgin & pgpgout */
- int bi;
- int bo;
+ /* /proc/vmstat pgpgin & pgpgout */
+ int bi;
+ int bo;
};
struct cpu_stat_sample_struct {
- /* /proc/schedstat fields 10 & 11 (after name) */
- double runtime;
- double waittime;
-};
-
-struct cpu_stat_struct {
- /* per cpu array */
- struct cpu_stat_sample_struct sample[MAXSAMPLES];
+ /* /proc/schedstat fields 10 & 11 (after name) */
+ double runtime;
+ double waittime;
};
/* per process, per sample data we will log */
struct ps_sched_struct {
- /* /proc/<n>/schedstat fields 1 & 2 */
- double runtime;
- double waittime;
- int pss;
+ /* /proc/<n>/schedstat fields 1 & 2 */
+ double runtime;
+ double waittime;
+ int pss;
+ struct list_sample_data *sampledata;
+ struct ps_sched_struct *next;
+ struct ps_sched_struct *prev;
+ struct ps_sched_struct *cross; /* cross pointer */
+ struct ps_struct *ps_new;
+};
+
+struct list_sample_data {
+ double runtime[MAXCPUS];
+ double waittime[MAXCPUS];
+ double sampletime;
+ int entropy_avail;
+ struct block_stat_struct blockstat;
+ LIST_FIELDS(struct list_sample_data, link); /* DLL */
+ int counter;
};
/* process info */
struct ps_struct {
- struct ps_struct *next_ps; /* SLL pointer */
- struct ps_struct *parent; /* ppid ref */
- struct ps_struct *children; /* children */
- struct ps_struct *next; /* siblings */
+ struct ps_struct *next_ps; /* SLL pointer */
+ struct ps_struct *parent; /* ppid ref */
+ struct ps_struct *children; /* children */
+ struct ps_struct *next; /* siblings */
- /* must match - otherwise it's a new process with same PID */
- char name[16];
- int pid;
- int ppid;
+ /* must match - otherwise it's a new process with same PID */
+ char name[256];
+ int pid;
+ int ppid;
- /* cache fd's */
- int sched;
- int schedstat;
- FILE *smaps;
+ /* cache fd's */
+ int sched;
+ int schedstat;
+ FILE *smaps;
- /* index to first/last seen timestamps */
- int first;
- int last;
+ /* pointers to first/last seen timestamps */
+ struct ps_sched_struct *first;
+ struct ps_sched_struct *last;
- /* records actual start time, may be way before bootchart runs */
- double starttime;
+ /* records actual start time, may be way before bootchart runs */
+ double starttime;
- /* record human readable total cpu time */
- double total;
+ /* record human readable total cpu time */
+ double total;
- /* largest PSS size found */
- int pss_max;
+ /* largest PSS size found */
+ int pss_max;
- /* for drawing connection lines later */
- double pos_x;
- double pos_y;
+ /* for drawing connection lines later */
+ double pos_x;
+ double pos_y;
- struct ps_sched_struct *sample;
+ struct ps_sched_struct *sample;
};
extern int entropy_avail[];
@@ -88,30 +110,24 @@ extern double log_start;
extern double sampletime[];
extern struct ps_struct *ps_first;
extern struct block_stat_struct blockstat[];
-extern struct cpu_stat_struct cpustat[];
extern int pscount;
-extern int relative;
-extern int filter;
-extern int pss;
-extern int entropy;
-extern int initcall;
+extern bool arg_relative;
+extern bool arg_filter;
+extern bool arg_show_cmdline;
+extern bool arg_pss;
+extern bool arg_entropy;
+extern bool initcall;
extern int samples;
extern int cpus;
-extern int len;
-extern double hz;
-extern double scale_x;
-extern double scale_y;
+extern int arg_samples_len;
+extern double arg_hz;
+extern double arg_scale_x;
+extern double arg_scale_y;
extern int overrun;
extern double interval;
-extern char output_path[PATH_MAX];
-extern char init_path[PATH_MAX];
+extern char arg_output_path[PATH_MAX];
+extern char arg_init_path[PATH_MAX];
extern FILE *of;
-extern DIR *proc;
-
-extern double gettime_ns(void);
-extern void log_uptime(void);
-extern void log_sample(int sample);
-
-extern void svg_do(void);
+extern int sysfd;
diff --git a/src/bootchart/log.c b/src/bootchart/log.c
deleted file mode 100644
index 89c7b3523c..0000000000
--- a/src/bootchart/log.c
+++ /dev/null
@@ -1,420 +0,0 @@
-/*
- * log.c
- *
- * Copyright (C) 2009-2012 Intel Coproration
- *
- * Authors:
- * Auke Kok <auke-jan.h.kok@intel.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; version 2
- * of the License.
- */
-
-#define _GNU_SOURCE 1
-#include <unistd.h>
-#include <stdlib.h>
-#include <limits.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <stdio.h>
-#include <string.h>
-#include <dirent.h>
-#include <fcntl.h>
-#include <time.h>
-
-
-#include "bootchart.h"
-
-/*
- * Alloc a static 4k buffer for stdio - primarily used to increase
- * PSS buffering from the default 1k stdin buffer to reduce
- * read() overhead.
- */
-static char smaps_buf[4096];
-DIR *proc;
-
-
-double gettime_ns(void)
-{
- struct timespec now;
-
- clock_gettime(CLOCK_MONOTONIC, &now);
-
- return (now.tv_sec + (now.tv_nsec / 1000000000.0));
-}
-
-
-void log_uptime(void)
-{
- FILE *f;
- char str[32];
- double uptime;
-
- f = fopen("/proc/uptime", "r");
- if (!f)
- return;
- if (!fscanf(f, "%s %*s", str)) {
- fclose(f);
- return;
- }
- fclose(f);
- uptime = strtod(str, NULL);
-
- log_start = gettime_ns();
-
- /* start graph at kernel boot time */
- if (relative)
- graph_start = log_start;
- else
- graph_start = log_start - uptime;
-}
-
-
-static char *bufgetline(char *buf)
-{
- char *c;
-
- if (!buf)
- return NULL;
-
- c = strchr(buf, '\n');
- if (c)
- c++;
- return c;
-}
-
-
-void log_sample(int sample)
-{
- static int vmstat;
- static int schedstat;
- FILE *st;
- char buf[4095];
- char key[256];
- char val[256];
- char rt[256];
- char wt[256];
- char *m;
- int c;
- int p;
- int mod;
- static int e_fd;
- ssize_t s;
- ssize_t n;
- struct dirent *ent;
-
- if (!vmstat) {
- /* block stuff */
- vmstat = open("/proc/vmstat", O_RDONLY);
- if (vmstat == -1) {
- perror("open /proc/vmstat");
- exit (EXIT_FAILURE);
- }
- }
-
- n = pread(vmstat, buf, sizeof(buf) - 1, 0);
- if (n <= 0) {
- close(vmstat);
- return;
- }
- buf[n] = '\0';
-
- m = buf;
- while (m) {
- if (sscanf(m, "%s %s", key, val) < 2)
- goto vmstat_next;
- if (!strcmp(key, "pgpgin"))
- blockstat[sample].bi = atoi(val);
- if (!strcmp(key, "pgpgout")) {
- blockstat[sample].bo = atoi(val);
- break;
- }
-vmstat_next:
- m = bufgetline(m);
- if (!m)
- break;
- }
-
- if (!schedstat) {
- /* overall CPU utilization */
- schedstat = open("/proc/schedstat", O_RDONLY);
- if (schedstat == -1) {
- perror("open /proc/schedstat");
- exit (EXIT_FAILURE);
- }
- }
-
- n = pread(schedstat, buf, sizeof(buf) - 1, 0);
- if (n <= 0) {
- close(schedstat);
- return;
- }
- buf[n] = '\0';
-
- m = buf;
- while (m) {
- if (sscanf(m, "%s %*s %*s %*s %*s %*s %*s %s %s", key, rt, wt) < 3)
- goto schedstat_next;
-
- if (strstr(key, "cpu")) {
- c = atoi((const char*)(key+3));
- if (c > MAXCPUS)
- /* Oops, we only have room for MAXCPUS data */
- break;
- cpustat[c].sample[sample].runtime = atoll(rt);
- cpustat[c].sample[sample].waittime = atoll(wt);
-
- if (c == cpus)
- cpus = c + 1;
- }
-schedstat_next:
- m = bufgetline(m);
- if (!m)
- break;
- }
-
- if (entropy) {
- if (!e_fd) {
- e_fd = open("/proc/sys/kernel/random/entropy_avail", O_RDONLY);
- }
-
- if (e_fd) {
- n = pread(e_fd, buf, sizeof(buf) - 1, 0);
- if (n > 0)
- entropy_avail[sample] = atoi(buf);
- }
- }
-
- /* all the per-process stuff goes here */
- if (!proc) {
- /* find all processes */
- proc = opendir("/proc");
- if (!proc)
- return;
- } else {
- rewinddir(proc);
- }
-
- while ((ent = readdir(proc)) != NULL) {
- char filename[PATH_MAX];
- int pid;
- struct ps_struct *ps;
-
- if ((ent->d_name[0] < '0') || (ent->d_name[0] > '9'))
- continue;
-
- pid = atoi(ent->d_name);
-
- if (pid >= MAXPIDS)
- continue;
-
- ps = ps_first;
- while (ps->next_ps) {
- ps = ps->next_ps;
- if (ps->pid == pid)
- break;
- }
-
- /* end of our LL? then append a new record */
- if (ps->pid != pid) {
- char t[32];
- struct ps_struct *parent;
-
- ps->next_ps = malloc(sizeof(struct ps_struct));
- if (!ps->next_ps) {
- perror("malloc(ps_struct)");
- exit (EXIT_FAILURE);
- }
- memset(ps->next_ps, 0, sizeof(struct ps_struct));
- ps = ps->next_ps;
- ps->pid = pid;
-
- ps->sample = malloc(sizeof(struct ps_sched_struct) * (len + 1));
- if (!ps->sample) {
- perror("malloc(ps_struct)");
- exit (EXIT_FAILURE);
- }
- memset(ps->sample, 0, sizeof(struct ps_sched_struct) * (len + 1));
-
- pscount++;
-
- /* mark our first sample */
- ps->first = sample;
-
- /* get name, start time */
- if (!ps->sched) {
- sprintf(filename, "/proc/%d/sched", pid);
- ps->sched = open(filename, O_RDONLY);
- if (ps->sched == -1)
- continue;
- }
-
- s = pread(ps->sched, buf, sizeof(buf) - 1, 0);
- if (s <= 0) {
- close(ps->sched);
- continue;
- }
-
- if (!sscanf(buf, "%s %*s %*s", key))
- continue;
-
- strncpy(ps->name, key, 16);
- /* discard line 2 */
- m = bufgetline(buf);
- if (!m)
- continue;
-
- m = bufgetline(m);
- if (!m)
- continue;
-
- if (!sscanf(m, "%*s %*s %s", t))
- continue;
-
- ps->starttime = strtod(t, NULL) / 1000.0;
-
- /* ppid */
- sprintf(filename, "/proc/%d/stat", pid);
- st = fopen(filename, "r");
- if (!st)
- continue;
- if (!fscanf(st, "%*s %*s %*s %i", &p)) {
- fclose(st);
- continue;
- }
- fclose(st);
- ps->ppid = p;
-
- /*
- * setup child pointers
- *
- * these are used to paint the tree coherently later
- * each parent has a LL of children, and a LL of siblings
- */
- if (pid == 1)
- continue; /* nothing to do for init atm */
-
- /* kthreadd has ppid=0, which breaks our tree ordering */
- if (ps->ppid == 0)
- ps->ppid = 1;
-
- parent = ps_first;
- while ((parent->next_ps && parent->pid != ps->ppid))
- parent = parent->next_ps;
-
- if ((!parent) || (parent->pid != ps->ppid)) {
- /* orphan */
- ps->ppid = 1;
- parent = ps_first->next_ps;
- }
-
- ps->parent = parent;
-
- if (!parent->children) {
- /* it's the first child */
- parent->children = ps;
- } else {
- /* walk all children and append */
- struct ps_struct *children;
- children = parent->children;
- while (children->next)
- children = children->next;
- children->next = ps;
- }
- }
-
- /* else -> found pid, append data in ps */
-
- /* below here is all continuous logging parts - we get here on every
- * iteration */
-
- /* rt, wt */
- if (!ps->schedstat) {
- sprintf(filename, "/proc/%d/schedstat", pid);
- ps->schedstat = open(filename, O_RDONLY);
- if (ps->schedstat == -1)
- continue;
- }
-
- if (pread(ps->schedstat, buf, sizeof(buf) - 1, 0) <= 0) {
- /* clean up our file descriptors - assume that the process exited */
- close(ps->schedstat);
- if (ps->sched)
- close(ps->sched);
- //if (ps->smaps)
- // fclose(ps->smaps);
- continue;
- }
- if (!sscanf(buf, "%s %s %*s", rt, wt))
- continue;
-
- ps->last = sample;
- ps->sample[sample].runtime = atoll(rt);
- ps->sample[sample].waittime = atoll(wt);
-
- ps->total = (ps->sample[ps->last].runtime
- - ps->sample[ps->first].runtime)
- / 1000000000.0;
-
- if (!pss)
- goto catch_rename;
- /* Pss */
- if (!ps->smaps) {
- sprintf(filename, "/proc/%d/smaps", pid);
- ps->smaps = fopen(filename, "r");
- setvbuf(ps->smaps, smaps_buf, _IOFBF, sizeof(smaps_buf));
- if (!ps->smaps)
- continue;
- } else {
- rewind(ps->smaps);
- }
-
- while (1) {
- int pss_kb;
-
- /* skip one line, this contains the object mapped */
- if (fgets(buf, sizeof(buf), ps->smaps) == NULL)
- break;
- /* then there's a 28 char 14 line block */
- if (fread(buf, 1, 28 * 14, ps->smaps) != 28 * 14)
- break;
-
- pss_kb = atoi(&buf[61]);
- ps->sample[sample].pss += pss_kb;
- }
-
- if (ps->sample[sample].pss > ps->pss_max)
- ps->pss_max = ps->sample[sample].pss;
-
-catch_rename:
- /* catch process rename, try to randomize time */
- mod = (hz < 4.0) ? 4.0 : (hz / 4.0);
- if (((samples - ps->first) + pid) % (int)(mod) == 0) {
-
- /* re-fetch name */
- /* get name, start time */
- if (!ps->sched) {
- sprintf(filename, "/proc/%d/sched", pid);
- ps->sched = open(filename, O_RDONLY);
- if (ps->sched == -1)
- continue;
- }
- if (pread(ps->sched, buf, sizeof(buf) - 1, 0) <= 0) {
- /* clean up file descriptors */
- close(ps->sched);
- if (ps->schedstat)
- close(ps->schedstat);
- //if (ps->smaps)
- // fclose(ps->smaps);
- continue;
- }
-
- if (!sscanf(buf, "%s %*s %*s", key))
- continue;
-
- strncpy(ps->name, key, 16);
- }
- }
-}
diff --git a/src/bootchart/store.c b/src/bootchart/store.c
new file mode 100755
index 0000000000..b2afb8d13b
--- /dev/null
+++ b/src/bootchart/store.c
@@ -0,0 +1,504 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright (C) 2009-2013 Intel Coproration
+
+ Authors:
+ Auke Kok <auke-jan.h.kok@intel.com>
+
+ 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 <http://www.gnu.org/licenses/>.
+ ***/
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <string.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <time.h>
+
+#include "util.h"
+#include "strxcpyx.h"
+#include "store.h"
+#include "bootchart.h"
+
+/*
+ * Alloc a static 4k buffer for stdio - primarily used to increase
+ * PSS buffering from the default 1k stdin buffer to reduce
+ * read() overhead.
+ */
+static char smaps_buf[4096];
+static int skip = 0;
+DIR *proc;
+int procfd = -1;
+
+double gettime_ns(void) {
+ struct timespec n;
+
+ clock_gettime(CLOCK_MONOTONIC, &n);
+
+ return (n.tv_sec + (n.tv_nsec / 1000000000.0));
+}
+
+void log_uptime(void) {
+ _cleanup_fclose_ FILE *f = NULL;
+ char str[32];
+ double uptime;
+
+ f = fopen("/proc/uptime", "r");
+
+ if (!f)
+ return;
+ if (!fscanf(f, "%s %*s", str))
+ return;
+
+ uptime = strtod(str, NULL);
+
+ log_start = gettime_ns();
+
+ /* start graph at kernel boot time */
+ if (arg_relative)
+ graph_start = log_start;
+ else
+ graph_start = log_start - uptime;
+}
+
+static char *bufgetline(char *buf) {
+ char *c;
+
+ if (!buf)
+ return NULL;
+
+ c = strchr(buf, '\n');
+ if (c)
+ c++;
+ return c;
+}
+
+static int pid_cmdline_strscpy(char *buffer, size_t buf_len, int pid) {
+ char filename[PATH_MAX];
+ _cleanup_close_ int fd=-1;
+ ssize_t n;
+
+ sprintf(filename, "%d/cmdline", pid);
+ fd = openat(procfd, filename, O_RDONLY);
+ if (fd < 0)
+ return -errno;
+
+ n = read(fd, buffer, buf_len-1);
+ if (n > 0) {
+ int i;
+ for (i = 0; i < n; i++)
+ if (buffer[i] == '\0')
+ buffer[i] = ' ';
+ buffer[n] = '\0';
+ }
+ return 0;
+}
+
+void log_sample(int sample, struct list_sample_data **ptr) {
+ static int vmstat;
+ static int schedstat;
+ char buf[4096];
+ char key[256];
+ char val[256];
+ char rt[256];
+ char wt[256];
+ char *m;
+ int c;
+ int p;
+ int mod;
+ static int e_fd;
+ ssize_t s;
+ ssize_t n;
+ struct dirent *ent;
+ int fd;
+ struct list_sample_data *sampledata;
+ struct ps_sched_struct *ps_prev = NULL;
+
+
+
+ sampledata = *ptr;
+
+ /* all the per-process stuff goes here */
+ if (!proc) {
+ /* find all processes */
+ proc = opendir("/proc");
+ if (!proc)
+ return;
+ procfd = dirfd(proc);
+ } else {
+ rewinddir(proc);
+ }
+
+ if (!vmstat) {
+ /* block stuff */
+ vmstat = openat(procfd, "vmstat", O_RDONLY);
+ if (vmstat == -1) {
+ perror("open /proc/vmstat");
+ exit (EXIT_FAILURE);
+ }
+ }
+
+ n = pread(vmstat, buf, sizeof(buf) - 1, 0);
+ if (n <= 0) {
+ close(vmstat);
+ return;
+ }
+ buf[n] = '\0';
+
+ m = buf;
+ while (m) {
+ if (sscanf(m, "%s %s", key, val) < 2)
+ goto vmstat_next;
+ if (streq(key, "pgpgin"))
+ sampledata->blockstat.bi = atoi(val);
+ if (streq(key, "pgpgout")) {
+ sampledata->blockstat.bo = atoi(val);
+ break;
+ }
+vmstat_next:
+ m = bufgetline(m);
+ if (!m)
+ break;
+ }
+
+ if (!schedstat) {
+ /* overall CPU utilization */
+ schedstat = openat(procfd, "schedstat", O_RDONLY);
+ if (schedstat == -1) {
+ perror("open /proc/schedstat");
+ exit (EXIT_FAILURE);
+ }
+ }
+
+ n = pread(schedstat, buf, sizeof(buf) - 1, 0);
+ if (n <= 0) {
+ close(schedstat);
+ return;
+ }
+ buf[n] = '\0';
+
+ m = buf;
+ while (m) {
+ if (sscanf(m, "%s %*s %*s %*s %*s %*s %*s %s %s", key, rt, wt) < 3)
+ goto schedstat_next;
+
+ if (strstr(key, "cpu")) {
+ c = atoi((const char*)(key+3));
+ if (c > MAXCPUS)
+ /* Oops, we only have room for MAXCPUS data */
+ break;
+ sampledata->runtime[c] = atoll(rt);
+ sampledata->waittime[c] = atoll(wt);
+
+ if (c == cpus)
+ cpus = c + 1;
+ }
+schedstat_next:
+ m = bufgetline(m);
+ if (!m)
+ break;
+ }
+
+ if (arg_entropy) {
+ if (!e_fd) {
+ e_fd = openat(procfd, "sys/kernel/random/entropy_avail", O_RDONLY);
+ }
+
+ if (e_fd) {
+ n = pread(e_fd, buf, sizeof(buf) - 1, 0);
+ if (n > 0) {
+ buf[n] = '\0';
+ sampledata->entropy_avail = atoi(buf);
+ }
+ }
+ }
+
+ while ((ent = readdir(proc)) != NULL) {
+ char filename[PATH_MAX];
+ int pid;
+ struct ps_struct *ps;
+
+ if ((ent->d_name[0] < '0') || (ent->d_name[0] > '9'))
+ continue;
+
+ pid = atoi(ent->d_name);
+
+ if (pid >= MAXPIDS)
+ continue;
+
+ ps = ps_first;
+ while (ps->next_ps) {
+ ps = ps->next_ps;
+ if (ps->pid == pid)
+ break;
+ }
+
+ /* end of our LL? then append a new record */
+ if (ps->pid != pid) {
+ _cleanup_fclose_ FILE *st = NULL;
+ char t[32];
+ struct ps_struct *parent;
+
+ ps->next_ps = calloc(1, sizeof(struct ps_struct));
+ if (!ps->next_ps) {
+ perror("calloc(ps_struct)");
+ exit (EXIT_FAILURE);
+ }
+ ps = ps->next_ps;
+ ps->pid = pid;
+
+ ps->sample = calloc(1, sizeof(struct ps_sched_struct));
+ if (!ps->sample) {
+ perror("calloc(ps_struct)");
+ exit (EXIT_FAILURE);
+ }
+ ps->sample->sampledata = sampledata;
+
+ pscount++;
+
+ /* mark our first sample */
+ ps->first = ps->sample;
+ ps->sample->runtime = atoll(rt);
+ ps->sample->waittime = atoll(wt);
+
+ /* get name, start time */
+ if (!ps->sched) {
+ sprintf(filename, "%d/sched", pid);
+ ps->sched = openat(procfd, filename, O_RDONLY);
+ if (ps->sched == -1)
+ continue;
+ }
+
+ s = pread(ps->sched, buf, sizeof(buf) - 1, 0);
+ if (s <= 0) {
+ close(ps->sched);
+ continue;
+ }
+ buf[s] = '\0';
+
+ if (!sscanf(buf, "%s %*s %*s", key))
+ continue;
+
+ strscpy(ps->name, sizeof(ps->name), key);
+
+ /* cmdline */
+ if (arg_show_cmdline)
+ pid_cmdline_strscpy(ps->name, sizeof(ps->name), pid);
+
+ /* discard line 2 */
+ m = bufgetline(buf);
+ if (!m)
+ continue;
+
+ m = bufgetline(m);
+ if (!m)
+ continue;
+
+ if (!sscanf(m, "%*s %*s %s", t))
+ continue;
+
+ ps->starttime = strtod(t, NULL) / 1000.0;
+
+ /* ppid */
+ sprintf(filename, "%d/stat", pid);
+ fd = openat(procfd, filename, O_RDONLY);
+ st = fdopen(fd, "r");
+ if (!st)
+ continue;
+ if (!fscanf(st, "%*s %*s %*s %i", &p)) {
+ continue;
+ }
+ ps->ppid = p;
+
+ /*
+ * setup child pointers
+ *
+ * these are used to paint the tree coherently later
+ * each parent has a LL of children, and a LL of siblings
+ */
+ if (pid == 1)
+ continue; /* nothing to do for init atm */
+
+ /* kthreadd has ppid=0, which breaks our tree ordering */
+ if (ps->ppid == 0)
+ ps->ppid = 1;
+
+ parent = ps_first;
+ while ((parent->next_ps && parent->pid != ps->ppid))
+ parent = parent->next_ps;
+
+ if ((!parent) || (parent->pid != ps->ppid)) {
+ /* orphan */
+ ps->ppid = 1;
+ parent = ps_first->next_ps;
+ }
+
+ ps->parent = parent;
+
+ if (!parent->children) {
+ /* it's the first child */
+ parent->children = ps;
+ } else {
+ /* walk all children and append */
+ struct ps_struct *children;
+ children = parent->children;
+ while (children->next)
+ children = children->next;
+ children->next = ps;
+ }
+ }
+
+ /* else -> found pid, append data in ps */
+
+ /* below here is all continuous logging parts - we get here on every
+ * iteration */
+
+ /* rt, wt */
+ if (!ps->schedstat) {
+ sprintf(filename, "%d/schedstat", pid);
+ ps->schedstat = openat(procfd, filename, O_RDONLY);
+ if (ps->schedstat == -1)
+ continue;
+ }
+ s = pread(ps->schedstat, buf, sizeof(buf) - 1, 0);
+ if (s <= 0) {
+ /* clean up our file descriptors - assume that the process exited */
+ close(ps->schedstat);
+ if (ps->sched)
+ close(ps->sched);
+ //if (ps->smaps)
+ // fclose(ps->smaps);
+ continue;
+ }
+ buf[s] = '\0';
+
+ if (!sscanf(buf, "%s %s %*s", rt, wt))
+ continue;
+
+ ps->sample->next = calloc(1, sizeof(struct ps_sched_struct));
+ if (!ps->sample) {
+ perror("calloc(ps_struct)");
+ exit (EXIT_FAILURE);
+ }
+ ps->sample->next->prev = ps->sample;
+ ps->sample = ps->sample->next;
+ ps->last = ps->sample;
+ ps->sample->runtime = atoll(rt);
+ ps->sample->waittime = atoll(wt);
+ ps->sample->sampledata = sampledata;
+ ps->sample->ps_new = ps;
+ if (ps_prev) {
+ ps_prev->cross = ps->sample;
+ }
+ ps_prev = ps->sample;
+ ps->total = (ps->last->runtime - ps->first->runtime)
+ / 1000000000.0;
+
+ if (!arg_pss)
+ goto catch_rename;
+
+ /* Pss */
+ if (!ps->smaps) {
+ sprintf(filename, "%d/smaps", pid);
+ fd = openat(procfd, filename, O_RDONLY);
+ ps->smaps = fdopen(fd, "r");
+ if (!ps->smaps)
+ continue;
+ setvbuf(ps->smaps, smaps_buf, _IOFBF, sizeof(smaps_buf));
+ }
+ else {
+ rewind(ps->smaps);
+ }
+ /* test to see if we need to skip another field */
+ if (skip == 0) {
+ if (fgets(buf, sizeof(buf), ps->smaps) == NULL) {
+ continue;
+ }
+ if (fread(buf, 1, 28 * 15, ps->smaps) != (28 * 15)) {
+ continue;
+ }
+ if (buf[392] == 'V') {
+ skip = 2;
+ }
+ else {
+ skip = 1;
+ }
+ rewind(ps->smaps);
+ }
+ while (1) {
+ int pss_kb;
+
+ /* skip one line, this contains the object mapped. */
+ if (fgets(buf, sizeof(buf), ps->smaps) == NULL) {
+ break;
+ }
+ /* then there's a 28 char 14 line block */
+ if (fread(buf, 1, 28 * 14, ps->smaps) != 28 * 14) {
+ break;
+ }
+ pss_kb = atoi(&buf[61]);
+ ps->sample->pss += pss_kb;
+
+ /* skip one more line if this is a newer kernel */
+ if (skip == 2) {
+ if (fgets(buf, sizeof(buf), ps->smaps) == NULL)
+ break;
+ }
+ }
+ if (ps->sample->pss > ps->pss_max)
+ ps->pss_max = ps->sample->pss;
+
+catch_rename:
+ /* catch process rename, try to randomize time */
+ mod = (arg_hz < 4.0) ? 4.0 : (arg_hz / 4.0);
+ if (((samples - ps->pid) + pid) % (int)(mod) == 0) {
+
+ /* re-fetch name */
+ /* get name, start time */
+ if (!ps->sched) {
+ sprintf(filename, "%d/sched", pid);
+ ps->sched = openat(procfd, filename, O_RDONLY);
+ if (ps->sched == -1)
+ continue;
+ }
+ s = pread(ps->sched, buf, sizeof(buf) - 1, 0);
+ if (s <= 0) {
+ /* clean up file descriptors */
+ close(ps->sched);
+ if (ps->schedstat)
+ close(ps->schedstat);
+ //if (ps->smaps)
+ // fclose(ps->smaps);
+ continue;
+ }
+ buf[s] = '\0';
+
+ if (!sscanf(buf, "%s %*s %*s", key))
+ continue;
+
+ strscpy(ps->name, sizeof(ps->name), key);
+
+ /* cmdline */
+ if (arg_show_cmdline)
+ pid_cmdline_strscpy(ps->name, sizeof(ps->name), pid);
+ }
+ }
+}
diff --git a/src/bootchart/store.h b/src/bootchart/store.h
new file mode 100644
index 0000000000..7c8ad284da
--- /dev/null
+++ b/src/bootchart/store.h
@@ -0,0 +1,35 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright (C) 2009-2013 Intel Coproration
+
+ Authors:
+ Auke Kok <auke-jan.h.kok@intel.com>
+
+ 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 <http://www.gnu.org/licenses/>.
+***/
+
+#include <dirent.h>
+#include "bootchart.h"
+
+extern DIR *proc;
+extern int procfd;
+
+double gettime_ns(void);
+void log_uptime(void);
+void log_sample(int sample, struct list_sample_data **ptr);
diff --git a/src/bootchart/svg.c b/src/bootchart/svg.c
index 68ec5399ac..859cf81c22 100644
--- a/src/bootchart/svg.c
+++ b/src/bootchart/svg.c
@@ -1,16 +1,26 @@
-/*
- * svg.c
- *
- * Copyright (C) 2009-2012 Intel Coproration
- *
- * Authors:
- * Auke Kok <auke-jan.h.kok@intel.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; version 2
- * of the License.
- */
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright (C) 2009-2013 Intel Coproration
+
+ Authors:
+ Auke Kok <auke-jan.h.kok@intel.com>
+
+ 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 <http://www.gnu.org/licenses/>.
+ ***/
#include <stdio.h>
#include <stdarg.h>
@@ -20,35 +30,38 @@
#include <limits.h>
#include <unistd.h>
#include <sys/utsname.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include "util.h"
+#include "macro.h"
+#include "store.h"
+#include "svg.h"
#include "bootchart.h"
+#include "list.h"
-
-#define time_to_graph(t) ((t) * scale_x)
-#define ps_to_graph(n) ((n) * scale_y)
-#define kb_to_graph(m) ((m) * scale_y * 0.0001)
+#define time_to_graph(t) ((t) * arg_scale_x)
+#define ps_to_graph(n) ((n) * arg_scale_y)
+#define kb_to_graph(m) ((m) * arg_scale_y * 0.0001)
#define to_color(n) (192.0 - ((n) * 192.0))
-#define max(x, y) (((x) > (y)) ? (x) : (y))
-#define min(x, y) (((x) < (y)) ? (x) : (y))
-
static char str[8092];
#define svg(a...) do { snprintf(str, 8092, ## a); fputs(str, of); fflush(of); } while (0)
-static const char *colorwheel[12] = {
- "rgb(255,32,32)", // red
- "rgb(32,192,192)", // cyan
- "rgb(255,128,32)", // orange
- "rgb(128,32,192)", // blue-violet
- "rgb(255,255,32)", // yellow
- "rgb(192,32,128)", // red-violet
- "rgb(32,255,32)", // green
- "rgb(255,64,32)", // red-orange
- "rgb(32,32,255)", // blue
- "rgb(255,192,32)", // yellow-orange
- "rgb(192,32,192)", // violet
- "rgb(32,192,32)" // yellow-green
+static const char * const colorwheel[12] = {
+ "rgb(255,32,32)", // red
+ "rgb(32,192,192)", // cyan
+ "rgb(255,128,32)", // orange
+ "rgb(128,32,192)", // blue-violet
+ "rgb(255,255,32)", // yellow
+ "rgb(192,32,128)", // red-violet
+ "rgb(32,255,32)", // green
+ "rgb(255,64,32)", // red-orange
+ "rgb(32,32,255)", // blue
+ "rgb(255,192,32)", // yellow-orange
+ "rgb(192,32,192)", // violet
+ "rgb(32,192,32)" // yellow-green
};
static double idletime = -1.0;
@@ -58,1063 +71,1262 @@ static int kcount = 0;
static float psize = 0;
static float ksize = 0;
static float esize = 0;
+static struct list_sample_data *sampledata;
+static struct list_sample_data *prev_sampledata;
+extern struct list_sample_data *head;
+
+static void svg_header(void) {
+ float w;
+ float h;
+ struct list_sample_data *sampledata_last;
+
+ sampledata = head;
+ LIST_FIND_TAIL(struct list_sample_data, link, sampledata, head);
+ sampledata_last = head;
+ LIST_FOREACH_BEFORE(link, sampledata, head) {
+ sampledata_last = sampledata;
+ }
+
+ /* min width is about 1600px due to the label */
+ w = 150.0 + 10.0 + time_to_graph(sampledata_last->sampletime - graph_start);
+ w = ((w < 1600.0) ? 1600.0 : w);
+
+ /* height is variable based on pss, psize, ksize */
+ h = 400.0 + (arg_scale_y * 30.0) /* base graphs and title */
+ + (arg_pss ? (100.0 * arg_scale_y) + (arg_scale_y * 7.0) : 0.0) /* pss estimate */
+ + psize + ksize + esize;
+
+ svg("<?xml version=\"1.0\" standalone=\"no\"?>\n");
+ svg("<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" ");
+ svg("\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n");
+
+ //svg("<g transform=\"translate(10,%d)\">\n", 1000 + 150 + (pcount * 20));
+ svg("<svg width=\"%.0fpx\" height=\"%.0fpx\" version=\"1.1\" ",
+ w, h);
+ svg("xmlns=\"http://www.w3.org/2000/svg\">\n\n");
+
+ /* write some basic info as a comment, including some help */
+ svg("<!-- This file is a bootchart SVG file. It is best rendered in a browser -->\n");
+ svg("<!-- such as Chrome, Chromium, or Firefox. Other applications that -->\n");
+ svg("<!-- render these files properly but more slowly are ImageMagick, gimp, -->\n");
+ svg("<!-- inkscape, etc. To display the files on your system, just point -->\n");
+ svg("<!-- your browser to file:///run/log/ and click. This bootchart was -->\n\n");
+
+ svg("<!-- generated by bootchart version %s, running with options: -->\n", VERSION);
+ svg("<!-- hz=\"%f\" n=\"%d\" -->\n", arg_hz, arg_samples_len);
+ svg("<!-- x=\"%f\" y=\"%f\" -->\n", arg_scale_x, arg_scale_y);
+ svg("<!-- rel=\"%d\" f=\"%d\" -->\n", arg_relative, arg_filter);
+ svg("<!-- p=\"%d\" e=\"%d\" -->\n", arg_pss, arg_entropy);
+ svg("<!-- o=\"%s\" i=\"%s\" -->\n\n", arg_output_path, arg_init_path);
+
+ /* style sheet */
+ svg("<defs>\n <style type=\"text/css\">\n <![CDATA[\n");
+
+ svg(" rect { stroke-width: 1; }\n");
+ svg(" rect.cpu { fill: rgb(64,64,240); stroke-width: 0; fill-opacity: 0.7; }\n");
+ svg(" rect.wait { fill: rgb(240,240,0); stroke-width: 0; fill-opacity: 0.7; }\n");
+ svg(" rect.bi { fill: rgb(240,128,128); stroke-width: 0; fill-opacity: 0.7; }\n");
+ svg(" rect.bo { fill: rgb(192,64,64); stroke-width: 0; fill-opacity: 0.7; }\n");
+ svg(" rect.ps { fill: rgb(192,192,192); stroke: rgb(128,128,128); fill-opacity: 0.7; }\n");
+ svg(" rect.krnl { fill: rgb(240,240,0); stroke: rgb(128,128,128); fill-opacity: 0.7; }\n");
+ svg(" rect.box { fill: rgb(240,240,240); stroke: rgb(192,192,192); }\n");
+ svg(" rect.clrw { stroke-width: 0; fill-opacity: 0.7;}\n");
+ svg(" line { stroke: rgb(64,64,64); stroke-width: 1; }\n");
+ svg("// line.sec1 { }\n");
+ svg(" line.sec5 { stroke-width: 2; }\n");
+ svg(" line.sec01 { stroke: rgb(224,224,224); stroke-width: 1; }\n");
+ svg(" line.dot { stroke-dasharray: 2 4; }\n");
+ svg(" line.idle { stroke: rgb(64,64,64); stroke-dasharray: 10 6; stroke-opacity: 0.7; }\n");
+
+ svg(" .run { font-size: 8; font-style: italic; }\n");
+ svg(" text { font-family: Verdana, Helvetica; font-size: 10; }\n");
+ svg(" text.sec { font-size: 8; }\n");
+ svg(" text.t1 { font-size: 24; }\n");
+ svg(" text.t2 { font-size: 12; }\n");
+ svg(" text.idle { font-size: 18; }\n");
+
+ svg(" ]]>\n </style>\n</defs>\n\n");
+}
+static void svg_title(const char *build) {
+ char cmdline[256] = "";
+ char filename[PATH_MAX];
+ char buf[256];
+ char rootbdev[16] = "Unknown";
+ char model[256] = "Unknown";
+ char date[256] = "Unknown";
+ char cpu[256] = "Unknown";
+ char *c;
+ FILE *f;
+ time_t t;
+ int fd;
+ struct utsname uts;
+
+ /* grab /proc/cmdline */
+ fd = openat(procfd, "cmdline", O_RDONLY);
+ f = fdopen(fd, "r");
+ if (f) {
+ if (!fgets(cmdline, 255, f))
+ sprintf(cmdline, "Unknown");
+ fclose(f);
+ }
+
+ /* extract root fs so we can find disk model name in sysfs */
+ /* FIXME: this works only in the simple case */
+ c = strstr(cmdline, "root=/dev/");
+ if (c) {
+ strncpy(rootbdev, &c[10], 3);
+ rootbdev[3] = '\0';
+ sprintf(filename, "block/%s/device/model", rootbdev);
+ fd = openat(sysfd, filename, O_RDONLY);
+ f = fdopen(fd, "r");
+ if (f) {
+ if (!fgets(model, 255, f))
+ fprintf(stderr, "Error reading disk model for %s\n", rootbdev);
+ fclose(f);
+ }
+ }
+
+ /* various utsname parameters */
+ if (uname(&uts))
+ fprintf(stderr, "Error getting uname info\n");
+
+ /* date */
+ t = time(NULL);
+ strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", localtime(&t));
+
+ /* CPU type */
+ fd = openat(procfd, "cpuinfo", O_RDONLY);
+ f = fdopen(fd, "r");
+ if (f) {
+ while (fgets(buf, 255, f)) {
+ if (strstr(buf, "model name")) {
+ strncpy(cpu, &buf[13], 255);
+ break;
+ }
+ }
+ fclose(f);
+ }
+
+ svg("<text class=\"t1\" x=\"0\" y=\"30\">Bootchart for %s - %s</text>\n",
+ uts.nodename, date);
+ svg("<text class=\"t2\" x=\"20\" y=\"50\">System: %s %s %s %s</text>\n",
+ uts.sysname, uts.release, uts.version, uts.machine);
+ svg("<text class=\"t2\" x=\"20\" y=\"65\">CPU: %s</text>\n",
+ cpu);
+ svg("<text class=\"t2\" x=\"20\" y=\"80\">Disk: %s</text>\n",
+ model);
+ svg("<text class=\"t2\" x=\"20\" y=\"95\">Boot options: %s</text>\n",
+ cmdline);
+ svg("<text class=\"t2\" x=\"20\" y=\"110\">Build: %s</text>\n",
+ build);
+ svg("<text class=\"t2\" x=\"20\" y=\"125\">Log start time: %.03fs</text>\n", log_start);
+ svg("<text class=\"t2\" x=\"20\" y=\"140\">Idle time: ");
+
+ if (idletime >= 0.0)
+ svg("%.03fs", idletime);
+ else
+ svg("Not detected");
+ svg("</text>\n");
+ svg("<text class=\"sec\" x=\"20\" y=\"155\">Graph data: %.03f samples/sec, recorded %i total, dropped %i samples, %i processes, %i filtered</text>\n",
+ arg_hz, arg_samples_len, overrun, pscount, pfiltered);
+}
-static void svg_header(void)
-{
- float w;
- float h;
-
- /* min width is about 1600px due to the label */
- w = 150.0 + 10.0 + time_to_graph(sampletime[samples-1] - graph_start);
- w = ((w < 1600.0) ? 1600.0 : w);
-
- /* height is variable based on pss, psize, ksize */
- h = 400.0 + (scale_y * 30.0) /* base graphs and title */
- + (pss ? (100.0 * scale_y) + (scale_y * 7.0) : 0.0) /* pss estimate */
- + psize + ksize + esize;
-
- svg("<?xml version=\"1.0\" standalone=\"no\"?>\n");
- svg("<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" ");
- svg("\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n");
-
- //svg("<g transform=\"translate(10,%d)\">\n", 1000 + 150 + (pcount * 20));
- svg("<svg width=\"%.0fpx\" height=\"%.0fpx\" version=\"1.1\" ",
- w, h);
- svg("xmlns=\"http://www.w3.org/2000/svg\">\n\n");
-
- /* write some basic info as a comment, including some help */
- svg("<!-- This file is a bootchart SVG file. It is best rendered in a browser -->\n");
- svg("<!-- such as Chrome/Chromium, firefox. Other applications that render -->\n");
- svg("<!-- these files properly but much more slow are ImageMagick, gimp, -->\n");
- svg("<!-- inkscape, etc.. To display the files on your system, just point -->\n");
- svg("<!-- your browser to file:///var/log/ and click. This bootchart was -->\n\n");
-
- svg("<!-- generated by bootchart version %s, running with options: -->\n", VERSION);
- svg("<!-- hz=\"%f\" n=\"%d\" -->\n", hz, len);
- svg("<!-- x=\"%f\" y=\"%f\" -->\n", scale_x, scale_y);
- svg("<!-- rel=\"%d\" f=\"%d\" -->\n", relative, filter);
- svg("<!-- p=\"%d\" e=\"%d\" -->\n", pss, entropy);
- svg("<!-- o=\"%s\" i=\"%s\" -->\n\n", output_path, init_path);
-
- /* style sheet */
- svg("<defs>\n <style type=\"text/css\">\n <![CDATA[\n");
-
- svg(" rect { stroke-width: 1; }\n");
- svg(" rect.cpu { fill: rgb(64,64,240); stroke-width: 0; fill-opacity: 0.7; }\n");
- svg(" rect.wait { fill: rgb(240,240,0); stroke-width: 0; fill-opacity: 0.7; }\n");
- svg(" rect.bi { fill: rgb(240,128,128); stroke-width: 0; fill-opacity: 0.7; }\n");
- svg(" rect.bo { fill: rgb(192,64,64); stroke-width: 0; fill-opacity: 0.7; }\n");
- svg(" rect.ps { fill: rgb(192,192,192); stroke: rgb(128,128,128); fill-opacity: 0.7; }\n");
- svg(" rect.krnl { fill: rgb(240,240,0); stroke: rgb(128,128,128); fill-opacity: 0.7; }\n");
- svg(" rect.box { fill: rgb(240,240,240); stroke: rgb(192,192,192); }\n");
- svg(" rect.clrw { stroke-width: 0; fill-opacity: 0.7;}\n");
- svg(" line { stroke: rgb(64,64,64); stroke-width: 1; }\n");
- svg("// line.sec1 { }\n");
- svg(" line.sec5 { stroke-width: 2; }\n");
- svg(" line.sec01 { stroke: rgb(224,224,224); stroke-width: 1; }\n");
- svg(" line.dot { stroke-dasharray: 2 4; }\n");
- svg(" line.idle { stroke: rgb(64,64,64); stroke-dasharray: 10 6; stroke-opacity: 0.7; }\n");
-
- svg(" .run { font-size: 8; font-style: italic; }\n");
- svg(" text { font-family: Verdana, Helvetica; font-size: 10; }\n");
- svg(" text.sec { font-size: 8; }\n");
- svg(" text.t1 { font-size: 24; }\n");
- svg(" text.t2 { font-size: 12; }\n");
- svg(" text.idle { font-size: 18; }\n");
-
- svg(" ]]>\n </style>\n</defs>\n\n");
-
+static void svg_graph_box(int height) {
+ double d = 0.0;
+ int i = 0;
+ double finalsample = 0.0;
+ struct list_sample_data *sampledata_last;
+
+ sampledata_last = head;
+ LIST_FOREACH_BEFORE(link, sampledata, head) {
+ sampledata_last = sampledata;
+ }
+
+ finalsample = sampledata_last->sampletime;
+
+ /* outside box, fill */
+ svg("<rect class=\"box\" x=\"%.03f\" y=\"0\" width=\"%.03f\" height=\"%.03f\" />\n",
+ time_to_graph(0.0),
+ time_to_graph(finalsample - graph_start),
+ ps_to_graph(height));
+
+ for (d = graph_start; d <= finalsample;
+ d += (arg_scale_x < 2.0 ? 60.0 : arg_scale_x < 10.0 ? 1.0 : 0.1)) {
+ /* lines for each second */
+ if (i % 50 == 0)
+ svg(" <line class=\"sec5\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
+ time_to_graph(d - graph_start),
+ time_to_graph(d - graph_start),
+ ps_to_graph(height));
+ else if (i % 10 == 0)
+ svg(" <line class=\"sec1\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
+ time_to_graph(d - graph_start),
+ time_to_graph(d - graph_start),
+ ps_to_graph(height));
+ else
+ svg(" <line class=\"sec01\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
+ time_to_graph(d - graph_start),
+ time_to_graph(d - graph_start),
+ ps_to_graph(height));
+
+ /* time label */
+ if (i % 10 == 0)
+ svg(" <text class=\"sec\" x=\"%.03f\" y=\"%.03f\" >%.01fs</text>\n",
+ time_to_graph(d - graph_start),
+ -5.0,
+ d - graph_start);
+
+ i++;
+ }
}
+/* xml comments must not contain "--" */
+static char* xml_comment_encode(const char* name) {
+ char *enc_name, *p;
-static void svg_title(void)
-{
- char cmdline[256] = "";
- char filename[PATH_MAX];
- char buf[256];
- char rootbdev[16] = "Unknown";
- char model[256] = "Unknown";
- char date[256] = "Unknown";
- char cpu[256] = "Unknown";
- char build[256] = "Unknown";
- char *c;
- FILE *f;
- time_t t;
- struct utsname uts;
-
- /* grab /proc/cmdline */
- f = fopen("/proc/cmdline", "r");
- if (f) {
- if (!fgets(cmdline, 255, f))
- sprintf(cmdline, "Unknown");
- fclose(f);
- }
-
- /* extract root fs so we can find disk model name in sysfs */
- c = strstr(cmdline, "root=/dev/");
- if (c) {
- strncpy(rootbdev, &c[10], 3);
- rootbdev[3] = '\0';
- }
- sprintf(filename, "/sys/block/%s/device/model", rootbdev);
- f = fopen(filename, "r");
- if (f) {
- if (!fgets(model, 255, f))
- fprintf(stderr, "Error reading disk model for %s\n", rootbdev);
- fclose(f);
- }
-
- /* various utsname parameters */
- if (uname(&uts))
- fprintf(stderr, "Error getting uname info\n");
-
- /* date */
- t = time(NULL);
- strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", localtime(&t));
-
- /* CPU type */
- f = fopen("/proc/cpuinfo", "r");
- if (f) {
- while (fgets(buf, 255, f)) {
- if (strstr(buf, "model name")) {
- strncpy(cpu, &buf[13], 255);
- break;
- }
- }
- fclose(f);
- }
-
- /* Build - 1st line from /etc/system-release */
- f = fopen("/etc/system-release", "r");
- if (f) {
- if (fgets(buf, 255, f))
- strncpy(build, buf, 255);
- fclose(f);
- }
-
- svg("<text class=\"t1\" x=\"0\" y=\"30\">Bootchart for %s - %s</text>\n",
- uts.nodename, date);
- svg("<text class=\"t2\" x=\"20\" y=\"50\">System: %s %s %s %s</text>\n",
- uts.sysname, uts.release, uts.version, uts.machine);
- svg("<text class=\"t2\" x=\"20\" y=\"65\">CPU: %s</text>\n",
- cpu);
- svg("<text class=\"t2\" x=\"20\" y=\"80\">Disk: %s</text>\n",
- model);
- svg("<text class=\"t2\" x=\"20\" y=\"95\">Boot options: %s</text>\n",
- cmdline);
- svg("<text class=\"t2\" x=\"20\" y=\"110\">Build: %s</text>\n",
- build);
- svg("<text class=\"t2\" x=\"20\" y=\"125\">Log start time: %.03fs</text>\n", log_start);
- svg("<text class=\"t2\" x=\"20\" y=\"140\">Idle time: ");
-
- if (idletime >= 0.0)
- svg("%.03fs", idletime);
- else
- svg("Not detected");
- svg("</text>\n");
- svg("<text class=\"sec\" x=\"20\" y=\"155\">Graph data: %.03f samples/sec, recorded %i total, dropped %i samples, %i processes, %i filtered</text>\n",
- hz, len, overrun, pscount, pfiltered);
-}
+ enc_name = strdup(name);
+ if (!enc_name)
+ return NULL;
+ for (p = enc_name; *p; p++)
+ if (p[0] == '-' && p[1] == '-')
+ p[1] = '_';
-static void svg_graph_box(int height)
-{
- double d = 0.0;
- int i = 0;
-
- /* outside box, fill */
- svg("<rect class=\"box\" x=\"%.03f\" y=\"0\" width=\"%.03f\" height=\"%.03f\" />\n",
- time_to_graph(0.0),
- time_to_graph(sampletime[samples-1] - graph_start),
- ps_to_graph(height));
-
- for (d = graph_start; d <= sampletime[samples-1];
- d += (scale_x < 2.0 ? 60.0 : scale_x < 10.0 ? 1.0 : 0.1)) {
- /* lines for each second */
- if (i % 50 == 0)
- svg(" <line class=\"sec5\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
- time_to_graph(d - graph_start),
- time_to_graph(d - graph_start),
- ps_to_graph(height));
- else if (i % 10 == 0)
- svg(" <line class=\"sec1\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
- time_to_graph(d - graph_start),
- time_to_graph(d - graph_start),
- ps_to_graph(height));
- else
- svg(" <line class=\"sec01\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
- time_to_graph(d - graph_start),
- time_to_graph(d - graph_start),
- ps_to_graph(height));
-
- /* time label */
- if (i % 10 == 0)
- svg(" <text class=\"sec\" x=\"%.03f\" y=\"%.03f\" >%.01fs</text>\n",
- time_to_graph(d - graph_start),
- -5.0,
- d - graph_start);
-
- i++;
- }
+ return enc_name;
}
-
-static void svg_pss_graph(void)
-{
- struct ps_struct *ps;
- int i;
-
- svg("\n\n<!-- Pss memory size graph -->\n");
-
- svg("\n <text class=\"t2\" x=\"5\" y=\"-15\">Memory allocation - Pss</text>\n");
-
- /* vsize 1000 == 1000mb */
- svg_graph_box(100);
- /* draw some hlines for usable memory sizes */
- for (i = 100000; i < 1000000; i += 100000) {
- svg(" <line class=\"sec01\" x1=\"%.03f\" y1=\"%.0f\" x2=\"%.03f\" y2=\"%.0f\"/>\n",
- time_to_graph(.0),
- kb_to_graph(i),
- time_to_graph(sampletime[samples-1] - graph_start),
- kb_to_graph(i));
- svg(" <text class=\"sec\" x=\"%.03f\" y=\"%.0f\">%dM</text>\n",
- time_to_graph(sampletime[samples-1] - graph_start) + 5,
- kb_to_graph(i), (1000000 - i) / 1000);
- }
- svg("\n");
-
- /* now plot the graph itself */
- for (i = 1; i < samples ; i++) {
- int bottom;
- int top;
-
- bottom = 0;
- top = 0;
-
- /* put all the small pss blocks into the bottom */
- ps = ps_first;
- while (ps->next_ps) {
- ps = ps->next_ps;
- if (!ps)
- continue;
- if (ps->sample[i].pss <= (100 * scale_y))
- top += ps->sample[i].pss;
- };
- svg(" <rect class=\"clrw\" style=\"fill: %s\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
- "rgb(64,64,64)",
- time_to_graph(sampletime[i - 1] - graph_start),
- kb_to_graph(1000000.0 - top),
- time_to_graph(sampletime[i] - sampletime[i - 1]),
- kb_to_graph(top - bottom));
-
- bottom = top;
-
- /* now plot the ones that are of significant size */
- ps = ps_first;
- while (ps->next_ps) {
- ps = ps->next_ps;
- if (!ps)
- continue;
- /* don't draw anything smaller than 2mb */
- if (ps->sample[i].pss > (100 * scale_y)) {
- top = bottom + ps->sample[i].pss;
- svg(" <rect class=\"clrw\" style=\"fill: %s\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
- colorwheel[ps->pid % 12],
- time_to_graph(sampletime[i - 1] - graph_start),
- kb_to_graph(1000000.0 - top),
- time_to_graph(sampletime[i] - sampletime[i - 1]),
- kb_to_graph(top - bottom));
- bottom = top;
- }
- }
- }
-
- /* overlay all the text labels */
- for (i = 1; i < samples ; i++) {
- int bottom;
- int top;
-
- bottom = 0;
- top = 0;
-
- /* put all the small pss blocks into the bottom */
- ps = ps_first;
- while (ps->next_ps) {
- ps = ps->next_ps;
- if (!ps)
- continue;
- if (ps->sample[i].pss <= (100 * scale_y))
- top += ps->sample[i].pss;
- };
-
- bottom = top;
-
- /* now plot the ones that are of significant size */
- ps = ps_first;
- while (ps->next_ps) {
- ps = ps->next_ps;
- if (!ps)
- continue;
- /* don't draw anything smaller than 2mb */
- if (ps->sample[i].pss > (100 * scale_y)) {
- top = bottom + ps->sample[i].pss;
- /* draw a label with the process / PID */
- if ((i == 1) || (ps->sample[i - 1].pss <= (100 * scale_y)))
- svg(" <text x=\"%.03f\" y=\"%.03f\">%s [%i]</text>\n",
- time_to_graph(sampletime[i] - graph_start),
- kb_to_graph(1000000.0 - bottom - ((top - bottom) / 2)),
- ps->name,
- ps->pid);
- bottom = top;
- }
- }
- }
-
- /* debug output - full data dump */
- svg("\n\n<!-- PSS map - csv format -->\n");
- ps = ps_first;
- while (ps->next_ps) {
- ps = ps->next_ps;
- if (!ps)
- continue;
- svg("<!-- %s [%d] pss=", ps->name, ps->pid);
- for (i = 0; i < samples ; i++) {
- svg("%d," , ps->sample[i].pss);
- }
- svg(" -->\n");
- }
+static void svg_pss_graph(void) {
+ struct ps_struct *ps;
+ int i;
+ struct list_sample_data *sampledata_last;
+
+ sampledata_last = head;
+ LIST_FOREACH_BEFORE(link, sampledata, head) {
+ sampledata_last = sampledata;
+ }
+
+
+ svg("\n\n<!-- Pss memory size graph -->\n");
+
+ svg("\n <text class=\"t2\" x=\"5\" y=\"-15\">Memory allocation - Pss</text>\n");
+
+ /* vsize 1000 == 1000mb */
+ svg_graph_box(100);
+ /* draw some hlines for usable memory sizes */
+ for (i = 100000; i < 1000000; i += 100000) {
+ svg(" <line class=\"sec01\" x1=\"%.03f\" y1=\"%.0f\" x2=\"%.03f\" y2=\"%.0f\"/>\n",
+ time_to_graph(.0),
+ kb_to_graph(i),
+ time_to_graph(sampledata_last->sampletime - graph_start),
+ kb_to_graph(i));
+ svg(" <text class=\"sec\" x=\"%.03f\" y=\"%.0f\">%dM</text>\n",
+ time_to_graph(sampledata_last->sampletime - graph_start) + 5,
+ kb_to_graph(i), (1000000 - i) / 1000);
+ }
+ svg("\n");
+
+ /* now plot the graph itself */
+ i = 1;
+ prev_sampledata = head;
+ LIST_FOREACH_BEFORE(link, sampledata, head) {
+ int bottom;
+ int top;
+ struct ps_sched_struct *cross_place;
+
+ bottom = 0;
+ top = 0;
+
+ /* put all the small pss blocks into the bottom */
+ ps = ps_first;
+ while (ps->next_ps) {
+ ps = ps->next_ps;
+ if (!ps)
+ continue;
+ ps->sample = ps->first;
+ while (ps->sample->next) {
+ ps->sample = ps->sample->next;
+ if (ps->sample->sampledata == sampledata)
+ break;
+ }
+ if (ps->sample->sampledata == sampledata) {
+ if (ps->sample->pss <= (100 * arg_scale_y))
+ top += ps->sample->pss;
+ break;
+ }
+ }
+ while (ps->sample->cross) {
+ cross_place = ps->sample->cross;
+ ps = ps->sample->cross->ps_new;
+ ps->sample = cross_place;
+ if (ps->sample->pss <= (100 * arg_scale_y))
+ top += ps->sample->pss;
+ }
+
+ svg(" <rect class=\"clrw\" style=\"fill: %s\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
+ "rgb(64,64,64)",
+ time_to_graph(prev_sampledata->sampletime - graph_start),
+ kb_to_graph(1000000.0 - top),
+ time_to_graph(sampledata->sampletime - prev_sampledata->sampletime),
+ kb_to_graph(top - bottom));
+ bottom = top;
+
+ /* now plot the ones that are of significant size */
+ ps = ps_first;
+ while (ps->next_ps) {
+ ps = ps->next_ps;
+ if (!ps)
+ continue;
+ ps->sample = ps->first;
+ while (ps->sample->next) {
+ ps->sample = ps->sample->next;
+ if (ps->sample->sampledata == sampledata)
+ break;
+ }
+ /* don't draw anything smaller than 2mb */
+ if (ps->sample->sampledata == sampledata) {
+ if (ps->sample->pss > (100 * arg_scale_y)) {
+ top = bottom + ps->sample->pss;
+ svg(" <rect class=\"clrw\" style=\"fill: %s\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
+ colorwheel[ps->pid % 12],
+ time_to_graph(prev_sampledata->sampletime - graph_start),
+ kb_to_graph(1000000.0 - top),
+ time_to_graph(sampledata->sampletime - prev_sampledata->sampletime),
+ kb_to_graph(top - bottom));
+ bottom = top;
+ }
+ break;
+ }
+ }
+ while ((cross_place = ps->sample->cross)) {
+ ps = ps->sample->cross->ps_new;
+ ps->sample = cross_place;
+ if (ps->sample->pss > (100 * arg_scale_y)) {
+ top = bottom + ps->sample->pss;
+ svg(" <rect class=\"clrw\" style=\"fill: %s\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
+ colorwheel[ps->pid % 12],
+ time_to_graph(prev_sampledata->sampletime - graph_start),
+ kb_to_graph(1000000.0 - top),
+ time_to_graph(sampledata->sampletime - prev_sampledata->sampletime),
+ kb_to_graph(top - bottom));
+ bottom = top;
+ }
+ }
+ prev_sampledata = sampledata;
+ i++;
+ }
+
+ /* overlay all the text labels */
+ i = 1;
+ LIST_FOREACH_BEFORE(link, sampledata, head) {
+ int bottom;
+ int top;
+ struct ps_sched_struct *prev_sample;
+ struct ps_sched_struct *cross_place;
+
+ bottom = 0;
+ top = 0;
+
+ /* put all the small pss blocks into the bottom */
+ ps = ps_first->next_ps;
+ while (ps->next_ps) {
+ ps = ps->next_ps;
+ if (!ps)
+ continue;
+ ps->sample = ps->first;
+ while (ps->sample->next) {
+ ps->sample = ps->sample->next;
+ if (ps->sample->sampledata == sampledata)
+ break;
+ }
+ if (ps->sample->sampledata == sampledata) {
+ if (ps->sample->pss <= (100 * arg_scale_y))
+ top += ps->sample->pss;
+ break;
+ }
+ }
+ while ((cross_place = ps->sample->cross)) {
+ ps = ps->sample->cross->ps_new;
+ ps->sample = cross_place;
+ if (ps->sample->pss <= (100 * arg_scale_y))
+ top += ps->sample->pss;
+ }
+ bottom = top;
+
+ /* now plot the ones that are of significant size */
+ ps = ps_first;
+ while (ps->next_ps) {
+ prev_sample = ps->sample;
+ ps = ps->next_ps;
+ if (!ps)
+ continue;
+ ps->sample = ps->first;
+ while (ps->sample->next) {
+ prev_sample = ps->sample;
+ ps->sample = ps->sample->next;
+ if (ps->sample->sampledata == sampledata)
+ break;
+ }
+ /* don't draw anything smaller than 2mb */
+ if (ps->sample->sampledata == sampledata) {
+ if (ps->sample->pss > (100 * arg_scale_y)) {
+ top = bottom + ps->sample->pss;
+ /* draw a label with the process / PID */
+ if ((i == 1) || (prev_sample->pss <= (100 * arg_scale_y)))
+ svg(" <text x=\"%.03f\" y=\"%.03f\"><![CDATA[%s]]> [%i]</text>\n",
+ time_to_graph(sampledata->sampletime - graph_start),
+ kb_to_graph(1000000.0 - bottom - ((top - bottom) / 2)),
+ ps->name,
+ ps->pid);
+ bottom = top;
+ }
+ break;
+ }
+ }
+ while ((cross_place = ps->sample->cross)) {
+ ps = ps->sample->cross->ps_new;
+ ps->sample = cross_place;
+ prev_sample = ps->sample->prev;
+ if (ps->sample->pss > (100 * arg_scale_y)) {
+ top = bottom + ps->sample->pss;
+ /* draw a label with the process / PID */
+ if ((i == 1) || (prev_sample->pss <= (100 * arg_scale_y)))
+ svg(" <text x=\"%.03f\" y=\"%.03f\"><![CDATA[%s]]> [%i]</text>\n",
+ time_to_graph(sampledata->sampletime - graph_start),
+ kb_to_graph(1000000.0 - bottom - ((top - bottom) / 2)),
+ ps->name,
+ ps->pid);
+ bottom = top;
+ }
+ }
+ i++;
+ }
+
+ /* debug output - full data dump */
+ svg("\n\n<!-- PSS map - csv format -->\n");
+ ps = ps_first;
+ while (ps->next_ps) {
+ _cleanup_free_ char *enc_name = NULL;
+ ps = ps->next_ps;
+ if (!ps)
+ continue;
+
+ enc_name = xml_comment_encode(ps->name);
+ if(!enc_name)
+ continue;
+
+ svg("<!-- %s [%d] pss=", enc_name, ps->pid);
+
+ ps->sample = ps->first;
+ while (ps->sample->next) {
+ ps->sample = ps->sample->next;
+ svg("%d," , ps->sample->pss);
+ }
+ svg(" -->\n");
+ }
}
-static void svg_io_bi_bar(void)
-{
- double max = 0.0;
- double range;
- int max_here = 0;
- int i;
-
- svg("<!-- IO utilization graph - In -->\n");
-
- svg("<text class=\"t2\" x=\"5\" y=\"-15\">IO utilization - read</text>\n");
-
- /*
- * calculate rounding range
- *
- * We need to round IO data since IO block data is not updated on
- * each poll. Applying a smoothing function loses some burst data,
- * so keep the smoothing range short.
- */
- range = 0.25 / (1.0 / hz);
- if (range < 2.0)
- range = 2.0; /* no smoothing */
-
- /* surrounding box */
- svg_graph_box(5);
-
- /* find the max IO first */
- for (i = 1; i < samples; i++) {
- int start;
- int stop;
- double tot;
-
- start = max(i - ((range / 2) - 1), 0);
- stop = min(i + (range / 2), samples - 1);
-
- tot = (double)(blockstat[stop].bi - blockstat[start].bi)
- / (stop - start);
- if (tot > max) {
- max = tot;
- max_here = i;
- }
- tot = (double)(blockstat[stop].bo - blockstat[start].bo)
- / (stop - start);
- if (tot > max)
- max = tot;
- }
-
- /* plot bi */
- for (i = 1; i < samples; i++) {
- int start;
- int stop;
- double tot;
- double pbi;
-
- start = max(i - ((range / 2) - 1), 0);
- stop = min(i + (range / 2), samples);
-
- tot = (double)(blockstat[stop].bi - blockstat[start].bi)
- / (stop - start);
- pbi = tot / max;
-
- if (pbi > 0.001)
- svg("<rect class=\"bi\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
- time_to_graph(sampletime[i - 1] - graph_start),
- (scale_y * 5) - (pbi * (scale_y * 5)),
- time_to_graph(sampletime[i] - sampletime[i - 1]),
- pbi * (scale_y * 5));
-
- /* labels around highest value */
- if (i == max_here) {
- svg(" <text class=\"sec\" x=\"%.03f\" y=\"%.03f\">%0.2fmb/sec</text>\n",
- time_to_graph(sampletime[i] - graph_start) + 5,
- ((scale_y * 5) - (pbi * (scale_y * 5))) + 15,
- max / 1024.0 / (interval / 1000000000.0));
- }
- }
+static void svg_io_bi_bar(void) {
+ double max = 0.0;
+ double range;
+ int max_here = 0;
+ int i;
+ int k;
+ struct list_sample_data *start_sampledata = sampledata;
+ struct list_sample_data *stop_sampledata = sampledata;
+
+ svg("<!-- IO utilization graph - In -->\n");
+
+ svg("<text class=\"t2\" x=\"5\" y=\"-15\">IO utilization - read</text>\n");
+
+ /*
+ * calculate rounding range
+ *
+ * We need to round IO data since IO block data is not updated on
+ * each poll. Applying a smoothing function loses some burst data,
+ * so keep the smoothing range short.
+ */
+ range = 0.25 / (1.0 / arg_hz);
+ if (range < 2.0)
+ range = 2.0; /* no smoothing */
+
+ /* surrounding box */
+ svg_graph_box(5);
+
+ /* find the max IO first */
+ i = 1;
+ LIST_FOREACH_BEFORE(link, sampledata, head) {
+ int start;
+ int stop;
+ int diff;
+ double tot;
+
+ start = MAX(i - ((range / 2) - 1), 0);
+ stop = MIN(i + (range / 2), samples - 1);
+ diff = (stop - start);
+
+ start_sampledata = sampledata;
+ stop_sampledata = sampledata;
+
+ for (k=0;(k<((range/2)-1))&&(start_sampledata->link_next);k++)
+ start_sampledata = start_sampledata->link_next;
+ for (k=0;(k<(range/2))&&(stop_sampledata->link_prev);k++)
+ stop_sampledata = stop_sampledata->link_prev;
+
+ tot = (double)(stop_sampledata->blockstat.bi - start_sampledata->blockstat.bi)
+ / diff;
+
+ if (tot > max) {
+ max = tot;
+ max_here = i;
+ }
+
+ tot = (double)(stop_sampledata->blockstat.bo - start_sampledata->blockstat.bo)
+ / diff;
+
+ if (tot > max)
+ max = tot;
+
+ i++;
+ }
+
+ /* plot bi */
+ i = 1;
+ prev_sampledata = head;
+ LIST_FOREACH_BEFORE(link, sampledata, head) {
+ int start;
+ int stop;
+ int diff;
+ double tot;
+ double pbi;
+
+ tot = 0;
+ pbi = 0;
+
+ start = MAX(i - ((range / 2) - 1), 0);
+ stop = MIN(i + (range / 2), samples);
+ diff = (stop - start);
+
+ start_sampledata = sampledata;
+ stop_sampledata = sampledata;
+
+ for (k=0;(k<((range/2)-1))&&(start_sampledata->link_next);k++)
+ start_sampledata = start_sampledata->link_next;
+ for (k=0;(k<(range/2))&&(stop_sampledata->link_prev);k++)
+ stop_sampledata = stop_sampledata->link_prev;
+
+ tot = (double)(stop_sampledata->blockstat.bi - start_sampledata->blockstat.bi)
+ / diff;
+
+ if (max > 0)
+ pbi = tot / max;
+
+ if (pbi > 0.001)
+ svg("<rect class=\"bi\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
+ time_to_graph(prev_sampledata->sampletime - graph_start),
+ (arg_scale_y * 5) - (pbi * (arg_scale_y * 5)),
+ time_to_graph(sampledata->sampletime - prev_sampledata->sampletime),
+ pbi * (arg_scale_y * 5));
+
+ /* labels around highest value */
+ if (i == max_here) {
+ svg(" <text class=\"sec\" x=\"%.03f\" y=\"%.03f\">%0.2fmb/sec</text>\n",
+ time_to_graph(sampledata->sampletime - graph_start) + 5,
+ ((arg_scale_y * 5) - (pbi * (arg_scale_y * 5))) + 15,
+ max / 1024.0 / (interval / 1000000000.0));
+ }
+ i++;
+ prev_sampledata = sampledata;
+ }
}
-static void svg_io_bo_bar(void)
-{
- double max = 0.0;
- double range;
- int max_here = 0;
- int i;
-
- svg("<!-- IO utilization graph - out -->\n");
-
- svg("<text class=\"t2\" x=\"5\" y=\"-15\">IO utilization - write</text>\n");
-
- /*
- * calculate rounding range
- *
- * We need to round IO data since IO block data is not updated on
- * each poll. Applying a smoothing function loses some burst data,
- * so keep the smoothing range short.
- */
- range = 0.25 / (1.0 / hz);
- if (range < 2.0)
- range = 2.0; /* no smoothing */
-
- /* surrounding box */
- svg_graph_box(5);
-
- /* find the max IO first */
- for (i = 1; i < samples; i++) {
- int start;
- int stop;
- double tot;
-
- start = max(i - ((range / 2) - 1), 0);
- stop = min(i + (range / 2), samples - 1);
-
- tot = (double)(blockstat[stop].bi - blockstat[start].bi)
- / (stop - start);
- if (tot > max)
- max = tot;
- tot = (double)(blockstat[stop].bo - blockstat[start].bo)
- / (stop - start);
- if (tot > max) {
- max = tot;
- max_here = i;
- }
- }
-
- /* plot bo */
- for (i = 1; i < samples; i++) {
- int start;
- int stop;
- double tot;
- double pbo;
-
- start = max(i - ((range / 2) - 1), 0);
- stop = min(i + (range / 2), samples);
-
- tot = (double)(blockstat[stop].bo - blockstat[start].bo)
- / (stop - start);
- pbo = tot / max;
-
- if (pbo > 0.001)
- svg("<rect class=\"bo\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
- time_to_graph(sampletime[i - 1] - graph_start),
- (scale_y * 5) - (pbo * (scale_y * 5)),
- time_to_graph(sampletime[i] - sampletime[i - 1]),
- pbo * (scale_y * 5));
-
- /* labels around highest bo value */
- if (i == max_here) {
- svg(" <text class=\"sec\" x=\"%.03f\" y=\"%.03f\">%0.2fmb/sec</text>\n",
- time_to_graph(sampletime[i] - graph_start) + 5,
- ((scale_y * 5) - (pbo * (scale_y * 5))),
- max / 1024.0 / (interval / 1000000000.0));
- }
- }
+static void svg_io_bo_bar(void) {
+ double max = 0.0;
+ double range;
+ int max_here = 0;
+ int i;
+ int k;
+ struct list_sample_data *start_sampledata = sampledata;
+ struct list_sample_data *stop_sampledata = sampledata;
+
+ svg("<!-- IO utilization graph - out -->\n");
+
+ svg("<text class=\"t2\" x=\"5\" y=\"-15\">IO utilization - write</text>\n");
+
+ /*
+ * calculate rounding range
+ *
+ * We need to round IO data since IO block data is not updated on
+ * each poll. Applying a smoothing function loses some burst data,
+ * so keep the smoothing range short.
+ */
+ range = 0.25 / (1.0 / arg_hz);
+ if (range < 2.0)
+ range = 2.0; /* no smoothing */
+
+ /* surrounding box */
+ svg_graph_box(5);
+
+ /* find the max IO first */
+ i = 0;
+ LIST_FOREACH_BEFORE(link, sampledata, head) {
+ int start;
+ int stop;
+ int diff;
+ double tot;
+
+ start = MAX(i - ((range / 2) - 1), 0);
+ stop = MIN(i + (range / 2), samples - 1);
+ diff = (stop - start);
+
+ start_sampledata = sampledata;
+ stop_sampledata = sampledata;
+
+ for (k=0;(k<((range/2)-1))&&(start_sampledata->link_next);k++)
+ start_sampledata = start_sampledata->link_next;
+ for (k=0;(k<(range/2))&&(stop_sampledata->link_prev);k++)
+ stop_sampledata = stop_sampledata->link_prev;
+
+ tot = (double)(stop_sampledata->blockstat.bi - start_sampledata->blockstat.bi)
+ / diff;
+ if (tot > max)
+ max = tot;
+ tot = (double)(stop_sampledata->blockstat.bo - start_sampledata->blockstat.bo)
+ / diff;
+ if (tot > max) {
+ max = tot;
+ max_here = i;
+ }
+ i++;
+ }
+
+ /* plot bo */
+ prev_sampledata = head;
+ i=1;
+ LIST_FOREACH_BEFORE(link, sampledata, head) {
+ int start;
+ int stop;
+ int diff;
+ double tot;
+ double pbo;
+
+ tot = 0;
+ pbo = 0;
+
+ start = MAX(i - ((range / 2) - 1), 0);
+ stop = MIN(i + (range / 2), samples);
+ diff = (stop - start);
+
+ start_sampledata = sampledata;
+ stop_sampledata = sampledata;
+
+ for (k=0;(k<((range/2)-1))&&(start_sampledata->link_next);k++)
+ start_sampledata = start_sampledata->link_next;
+ for (k=0;(k<(range/2))&&(stop_sampledata->link_prev);k++)
+ stop_sampledata = stop_sampledata->link_prev;
+
+ tot = (double)(stop_sampledata->blockstat.bo - start_sampledata->blockstat.bo)
+ / diff;
+
+ if (max > 0)
+ pbo = tot / max;
+
+ if (pbo > 0.001)
+ svg("<rect class=\"bo\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
+ time_to_graph(prev_sampledata->sampletime - graph_start),
+ (arg_scale_y * 5) - (pbo * (arg_scale_y * 5)),
+ time_to_graph(sampledata->sampletime - prev_sampledata->sampletime),
+ pbo * (arg_scale_y * 5));
+
+ /* labels around highest bo value */
+ if (i == max_here) {
+ svg(" <text class=\"sec\" x=\"%.03f\" y=\"%.03f\">%0.2fmb/sec</text>\n",
+ time_to_graph(sampledata->sampletime - graph_start) + 5,
+ ((arg_scale_y * 5) - (pbo * (arg_scale_y * 5))),
+ max / 1024.0 / (interval / 1000000000.0));
+ }
+ i++;
+ prev_sampledata = sampledata;
+ }
}
+static void svg_cpu_bar(void) {
-static void svg_cpu_bar(void)
-{
- int i;
-
- svg("<!-- CPU utilization graph -->\n");
+ svg("<!-- CPU utilization graph -->\n");
- svg("<text class=\"t2\" x=\"5\" y=\"-15\">CPU utilization</text>\n");
- /* surrounding box */
- svg_graph_box(5);
+ svg("<text class=\"t2\" x=\"5\" y=\"-15\">CPU utilization</text>\n");
+ /* surrounding box */
+ svg_graph_box(5);
- /* bars for each sample, proportional to the CPU util. */
- for (i = 1; i < samples; i++) {
- int c;
- double trt;
- double ptrt;
+ /* bars for each sample, proportional to the CPU util. */
+ prev_sampledata = head;
+ LIST_FOREACH_BEFORE(link, sampledata, head) {
+ int c;
+ double trt;
+ double ptrt;
- ptrt = trt = 0.0;
+ ptrt = trt = 0.0;
- for (c = 0; c < cpus; c++)
- trt += cpustat[c].sample[i].runtime - cpustat[c].sample[i - 1].runtime;
+ for (c = 0; c < cpus; c++)
+ trt += sampledata->runtime[c] - prev_sampledata->runtime[c];
- trt = trt / 1000000000.0;
+ trt = trt / 1000000000.0;
- trt = trt / (double)cpus;
+ trt = trt / (double)cpus;
- if (trt > 0.0)
- ptrt = trt / (sampletime[i] - sampletime[i - 1]);
+ if (trt > 0.0)
+ ptrt = trt / (sampledata->sampletime - prev_sampledata->sampletime);
- if (ptrt > 1.0)
- ptrt = 1.0;
+ if (ptrt > 1.0)
+ ptrt = 1.0;
- if (ptrt > 0.001) {
- svg("<rect class=\"cpu\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
- time_to_graph(sampletime[i - 1] - graph_start),
- (scale_y * 5) - (ptrt * (scale_y * 5)),
- time_to_graph(sampletime[i] - sampletime[i - 1]),
- ptrt * (scale_y * 5));
- }
- }
+ if (ptrt > 0.001) {
+ svg("<rect class=\"cpu\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
+ time_to_graph(prev_sampledata->sampletime - graph_start),
+ (arg_scale_y * 5) - (ptrt * (arg_scale_y * 5)),
+ time_to_graph(sampledata->sampletime - prev_sampledata->sampletime),
+ ptrt * (arg_scale_y * 5));
+ }
+ prev_sampledata = sampledata;
+ }
}
-static void svg_wait_bar(void)
-{
- int i;
+static void svg_wait_bar(void) {
- svg("<!-- Wait time aggregation box -->\n");
+ svg("<!-- Wait time aggregation box -->\n");
- svg("<text class=\"t2\" x=\"5\" y=\"-15\">CPU wait</text>\n");
+ svg("<text class=\"t2\" x=\"5\" y=\"-15\">CPU wait</text>\n");
- /* surrounding box */
- svg_graph_box(5);
+ /* surrounding box */
+ svg_graph_box(5);
- /* bars for each sample, proportional to the CPU util. */
- for (i = 1; i < samples; i++) {
- int c;
- double twt;
- double ptwt;
+ /* bars for each sample, proportional to the CPU util. */
+ prev_sampledata = head;
+ LIST_FOREACH_BEFORE(link, sampledata, head) {
+ int c;
+ double twt;
+ double ptwt;
- ptwt = twt = 0.0;
+ ptwt = twt = 0.0;
- for (c = 0; c < cpus; c++)
- twt += cpustat[c].sample[i].waittime - cpustat[c].sample[i - 1].waittime;
+ for (c = 0; c < cpus; c++)
+ twt += sampledata->waittime[c] - prev_sampledata->waittime[c];
- twt = twt / 1000000000.0;
+ twt = twt / 1000000000.0;
- twt = twt / (double)cpus;
+ twt = twt / (double)cpus;
- if (twt > 0.0)
- ptwt = twt / (sampletime[i] - sampletime[i - 1]);
+ if (twt > 0.0)
+ ptwt = twt / (sampledata->sampletime - prev_sampledata->sampletime);
- if (ptwt > 1.0)
- ptwt = 1.0;
+ if (ptwt > 1.0)
+ ptwt = 1.0;
- if (ptwt > 0.001) {
- svg("<rect class=\"wait\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
- time_to_graph(sampletime[i - 1] - graph_start),
- ((scale_y * 5) - (ptwt * (scale_y * 5))),
- time_to_graph(sampletime[i] - sampletime[i - 1]),
- ptwt * (scale_y * 5));
- }
- }
+ if (ptwt > 0.001) {
+ svg("<rect class=\"wait\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
+ time_to_graph(prev_sampledata->sampletime - graph_start),
+ ((arg_scale_y * 5) - (ptwt * (arg_scale_y * 5))),
+ time_to_graph(sampledata->sampletime - prev_sampledata->sampletime),
+ ptwt * (arg_scale_y * 5));
+ }
+ prev_sampledata = sampledata;
+ }
}
-static void svg_entropy_bar(void)
-{
- int i;
+static void svg_entropy_bar(void) {
- svg("<!-- entropy pool graph -->\n");
+ svg("<!-- entropy pool graph -->\n");
- svg("<text class=\"t2\" x=\"5\" y=\"-15\">Entropy pool size</text>\n");
- /* surrounding box */
- svg_graph_box(5);
+ svg("<text class=\"t2\" x=\"5\" y=\"-15\">Entropy pool size</text>\n");
+ /* surrounding box */
+ svg_graph_box(5);
- /* bars for each sample, scale 0-4096 */
- for (i = 1; i < samples; i++) {
- /* svg("<!-- entropy %.03f %i -->\n", sampletime[i], entropy_avail[i]); */
- svg("<rect class=\"cpu\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
- time_to_graph(sampletime[i - 1] - graph_start),
- ((scale_y * 5) - ((entropy_avail[i] / 4096.) * (scale_y * 5))),
- time_to_graph(sampletime[i] - sampletime[i - 1]),
- (entropy_avail[i] / 4096.) * (scale_y * 5));
- }
+ /* bars for each sample, scale 0-4096 */
+ prev_sampledata = head;
+ LIST_FOREACH_BEFORE(link, sampledata, head) {
+ /* svg("<!-- entropy %.03f %i -->\n", sampletime[i], entropy_avail[i]); */
+ svg("<rect class=\"cpu\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
+ time_to_graph(prev_sampledata->sampletime - graph_start),
+ ((arg_scale_y * 5) - ((sampledata->entropy_avail / 4096.) * (arg_scale_y * 5))),
+ time_to_graph(sampledata->sampletime - prev_sampledata->sampletime),
+ (sampledata->entropy_avail / 4096.) * (arg_scale_y * 5));
+ prev_sampledata = sampledata;
+ }
}
-
-static struct ps_struct *get_next_ps(struct ps_struct *ps)
-{
- /*
- * walk the list of processes and return the next one to be
- * painted
- */
- if (ps == ps_first)
- return ps->next_ps;
-
- /* go deep */
- if (ps->children)
- return ps->children;
-
- /* find siblings */
- if (ps->next)
- return ps->next;
-
- /* go back for parent siblings */
- while (1) {
- if (ps->parent)
- if (ps->parent->next)
- return ps->parent->next;
- ps = ps->parent;
- if (!ps)
- return ps;
- }
-
- return NULL;
+static struct ps_struct *get_next_ps(struct ps_struct *ps) {
+ /*
+ * walk the list of processes and return the next one to be
+ * painted
+ */
+ if (ps == ps_first)
+ return ps->next_ps;
+
+ /* go deep */
+ if (ps->children)
+ return ps->children;
+
+ /* find siblings */
+ if (ps->next)
+ return ps->next;
+
+ /* go back for parent siblings */
+ while (1) {
+ if (ps->parent)
+ if (ps->parent->next)
+ return ps->parent->next;
+ ps = ps->parent;
+ if (!ps)
+ return ps;
+ }
+
+ return NULL;
}
+static int ps_filter(struct ps_struct *ps) {
+ if (!arg_filter)
+ return 0;
-static int ps_filter(struct ps_struct *ps)
-{
- if (!filter)
- return 0;
-
- /* can't draw data when there is only 1 sample (need start + stop) */
- if (ps->first == ps->last)
- return -1;
+ /* can't draw data when there is only 1 sample (need start + stop) */
+ if (ps->first == ps->last)
+ return -1;
- /* don't filter kthreadd */
- if (ps->pid == 2)
- return 0;
+ /* don't filter kthreadd */
+ if (ps->pid == 2)
+ return 0;
- /* drop stuff that doesn't use any real CPU time */
- if (ps->total <= 0.001)
- return -1;
+ /* drop stuff that doesn't use any real CPU time */
+ if (ps->total <= 0.001)
+ return -1;
- return 0;
+ return 0;
}
-
-static void svg_do_initcall(int count_only)
-{
- FILE *f;
- double t;
- char func[256];
- int ret;
- int usecs;
-
- /* can't plot initcall when disabled or in relative mode */
- if (!initcall || relative) {
- kcount = 0;
- return;
- }
-
- if (!count_only) {
- svg("<!-- initcall -->\n");
-
- svg("<text class=\"t2\" x=\"5\" y=\"-15\">Kernel init threads</text>\n");
- /* surrounding box */
- svg_graph_box(kcount);
- }
-
- kcount = 0;
-
- /*
- * Initcall graphing - parses dmesg buffer and displays kernel threads
- * This somewhat uses the same methods and scaling to show processes
- * but looks a lot simpler. It's overlaid entirely onto the PS graph
- * when appropriate.
- */
-
- f = popen("dmesg", "r");
- if (!f)
- return;
-
- while (!feof(f)) {
- int c;
- int z = 0;
- char l[256];
-
- if (fgets(l, sizeof(l) - 1, f) == NULL)
- continue;
-
- c = sscanf(l, "[%lf] initcall %s %*s %d %*s %d %*s",
- &t, func, &ret, &usecs);
- if (c != 4) {
- /* also parse initcalls done by module loading */
- c = sscanf(l, "[%lf] initcall %s %*s %*s %d %*s %d %*s",
- &t, func, &ret, &usecs);
- if (c != 4)
- continue;
- }
-
- /* chop the +0xXX/0xXX stuff */
- while(func[z] != '+')
- z++;
- func[z] = 0;
-
- if (count_only) {
- /* filter out irrelevant stuff */
- if (usecs >= 1000)
- kcount++;
- continue;
- }
-
- svg("<!-- thread=\"%s\" time=\"%.3f\" elapsed=\"%d\" result=\"%d\" -->\n",
- func, t, usecs, ret);
-
- if (usecs < 1000)
- continue;
-
- /* rect */
- svg(" <rect class=\"krnl\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
- time_to_graph(t - (usecs / 1000000.0)),
- ps_to_graph(kcount),
- time_to_graph(usecs / 1000000.0),
- ps_to_graph(1));
-
- /* label */
- svg(" <text x=\"%.03f\" y=\"%.03f\">%s <tspan class=\"run\">%.03fs</tspan></text>\n",
- time_to_graph(t - (usecs / 1000000.0)) + 5,
- ps_to_graph(kcount) + 15,
- func,
- usecs / 1000000.0);
-
- kcount++;
- }
-
- fclose(f);
+static void svg_do_initcall(int count_only) {
+ _cleanup_pclose_ FILE *f = NULL;
+ double t;
+ char func[256];
+ int ret;
+ int usecs;
+
+ /* can't plot initcall when disabled or in relative mode */
+ if (!initcall || arg_relative) {
+ kcount = 0;
+ return;
+ }
+
+ if (!count_only) {
+ svg("<!-- initcall -->\n");
+
+ svg("<text class=\"t2\" x=\"5\" y=\"-15\">Kernel init threads</text>\n");
+ /* surrounding box */
+ svg_graph_box(kcount);
+ }
+
+ kcount = 0;
+
+ /*
+ * Initcall graphing - parses dmesg buffer and displays kernel threads
+ * This somewhat uses the same methods and scaling to show processes
+ * but looks a lot simpler. It's overlaid entirely onto the PS graph
+ * when appropriate.
+ */
+
+ f = popen("dmesg", "r");
+ if (!f)
+ return;
+
+ while (!feof(f)) {
+ int c;
+ int z = 0;
+ char l[256];
+
+ if (fgets(l, sizeof(l) - 1, f) == NULL)
+ continue;
+
+ c = sscanf(l, "[%lf] initcall %s %*s %d %*s %d %*s",
+ &t, func, &ret, &usecs);
+ if (c != 4) {
+ /* also parse initcalls done by module loading */
+ c = sscanf(l, "[%lf] initcall %s %*s %*s %d %*s %d %*s",
+ &t, func, &ret, &usecs);
+ if (c != 4)
+ continue;
+ }
+
+ /* chop the +0xXX/0xXX stuff */
+ while(func[z] != '+')
+ z++;
+ func[z] = 0;
+
+ if (count_only) {
+ /* filter out irrelevant stuff */
+ if (usecs >= 1000)
+ kcount++;
+ continue;
+ }
+
+ svg("<!-- thread=\"%s\" time=\"%.3f\" elapsed=\"%d\" result=\"%d\" -->\n",
+ func, t, usecs, ret);
+
+ if (usecs < 1000)
+ continue;
+
+ /* rect */
+ svg(" <rect class=\"krnl\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
+ time_to_graph(t - (usecs / 1000000.0)),
+ ps_to_graph(kcount),
+ time_to_graph(usecs / 1000000.0),
+ ps_to_graph(1));
+
+ /* label */
+ svg(" <text x=\"%.03f\" y=\"%.03f\">%s <tspan class=\"run\">%.03fs</tspan></text>\n",
+ time_to_graph(t - (usecs / 1000000.0)) + 5,
+ ps_to_graph(kcount) + 15,
+ func,
+ usecs / 1000000.0);
+
+ kcount++;
+ }
}
-
-static void svg_ps_bars(void)
-{
- struct ps_struct *ps;
- int i = 0;
- int j = 0;
- int w;
- int pid;
-
- svg("<!-- Process graph -->\n");
-
- svg("<text class=\"t2\" x=\"5\" y=\"-15\">Processes</text>\n");
-
- /* surrounding box */
- svg_graph_box(pcount);
-
- /* pass 2 - ps boxes */
- ps = ps_first;
- while ((ps = get_next_ps(ps))) {
- double starttime;
- int t;
-
- if (!ps)
- continue;
-
- /* leave some trace of what we actually filtered etc. */
- svg("<!-- %s [%i] ppid=%i runtime=%.03fs -->\n", ps->name, ps->pid,
- ps->ppid, ps->total);
-
- /* it would be nice if we could use exec_start from /proc/pid/sched,
- * but it's unreliable and gives bogus numbers */
- starttime = sampletime[ps->first];
-
- if (!ps_filter(ps)) {
- /* remember where _to_ our children need to draw a line */
- ps->pos_x = time_to_graph(starttime - graph_start);
- ps->pos_y = ps_to_graph(j+1); /* bottom left corner */
- } else {
- /* hook children to our parent coords instead */
- ps->pos_x = ps->parent->pos_x;
- ps->pos_y = ps->parent->pos_y;
-
- /* if this is the last child, we might still need to draw a connecting line */
- if ((!ps->next) && (ps->parent))
- svg(" <line class=\"dot\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
- ps->parent->pos_x,
- ps_to_graph(j-1) + 10.0, /* whee, use the last value here */
- ps->parent->pos_x,
- ps->parent->pos_y);
- continue;
- }
-
- svg(" <rect class=\"ps\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
- time_to_graph(starttime - graph_start),
- ps_to_graph(j),
- time_to_graph(sampletime[ps->last] - starttime),
- ps_to_graph(1));
-
- /* paint cpu load over these */
- for (t = ps->first + 1; t < ps->last; t++) {
- double rt, prt;
- double wt, wrt;
-
- /* calculate over interval */
- rt = ps->sample[t].runtime - ps->sample[t-1].runtime;
- wt = ps->sample[t].waittime - ps->sample[t-1].waittime;
-
- prt = (rt / 1000000000) / (sampletime[t] - sampletime[t-1]);
- wrt = (wt / 1000000000) / (sampletime[t] - sampletime[t-1]);
-
- /* this can happen if timekeeping isn't accurate enough */
- if (prt > 1.0)
- prt = 1.0;
- if (wrt > 1.0)
- wrt = 1.0;
-
- if ((prt < 0.1) && (wrt < 0.1)) /* =~ 26 (color threshold) */
- continue;
-
- svg(" <rect class=\"wait\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
- time_to_graph(sampletime[t - 1] - graph_start),
- ps_to_graph(j),
- time_to_graph(sampletime[t] - sampletime[t - 1]),
- ps_to_graph(wrt));
-
- /* draw cpu over wait - TODO figure out how/why run + wait > interval */
- svg(" <rect class=\"cpu\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
- time_to_graph(sampletime[t - 1] - graph_start),
- ps_to_graph(j + (1.0 - prt)),
- time_to_graph(sampletime[t] - sampletime[t - 1]),
- ps_to_graph(prt));
- }
-
- /* determine where to display the process name */
- if (sampletime[ps->last] - sampletime[ps->first] < 1.5)
- /* too small to fit label inside the box */
- w = ps->last;
- else
- w = ps->first;
-
- /* text label of process name */
- svg(" <text x=\"%.03f\" y=\"%.03f\">%s [%i] <tspan class=\"run\">%.03fs</tspan></text>\n",
- time_to_graph(sampletime[w] - graph_start) + 5.0,
- ps_to_graph(j) + 14.0,
- ps->name,
- ps->pid,
- (ps->sample[ps->last].runtime - ps->sample[ps->first].runtime) / 1000000000.0);
- /* paint lines to the parent process */
- if (ps->parent) {
- /* horizontal part */
- svg(" <line class=\"dot\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
- time_to_graph(starttime - graph_start),
- ps_to_graph(j) + 10.0,
- ps->parent->pos_x,
- ps_to_graph(j) + 10.0);
-
- /* one vertical line connecting all the horizontal ones up */
- if (!ps->next)
- svg(" <line class=\"dot\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
- ps->parent->pos_x,
- ps_to_graph(j) + 10.0,
- ps->parent->pos_x,
- ps->parent->pos_y);
- }
-
- j++; /* count boxes */
-
- svg("\n");
- }
-
- /* last pass - determine when idle */
- pid = getpid();
- /* make sure we start counting from the point where we actually have
- * data: assume that bootchart's first sample is when data started
- */
- ps = ps_first;
- while (ps->next_ps) {
- ps = ps->next_ps;
- if (ps->pid == pid)
- break;
- }
-
- for (i = ps->first; i < samples - (hz / 2); i++) {
- double crt;
- double brt;
- int c;
-
- /* subtract bootchart cpu utilization from total */
- crt = 0.0;
- for (c = 0; c < cpus; c++)
- crt += cpustat[c].sample[i + ((int)hz / 2)].runtime - cpustat[c].sample[i].runtime;
- brt = ps->sample[i + ((int)hz / 2)].runtime - ps->sample[i].runtime;
-
- /*
- * our definition of "idle":
- *
- * if for (hz / 2) we've used less CPU than (interval / 2) ...
- * defaults to 4.0%, which experimentally, is where atom idles
- */
- if ((crt - brt) < (interval / 2.0)) {
- idletime = sampletime[i] - graph_start;
- svg("\n<!-- idle detected at %.03f seconds -->\n",
- idletime);
- svg("<line class=\"idle\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
- time_to_graph(idletime),
- -scale_y,
- time_to_graph(idletime),
- ps_to_graph(pcount) + scale_y);
- svg("<text class=\"idle\" x=\"%.03f\" y=\"%.03f\">%.01fs</text>\n",
- time_to_graph(idletime) + 5.0,
- ps_to_graph(pcount) + scale_y,
- idletime);
- break;
- }
- }
+static void svg_ps_bars(void) {
+ struct ps_struct *ps;
+ int i = 0;
+ int j = 0;
+ int pid;
+ double w = 0.0;
+
+ svg("<!-- Process graph -->\n");
+
+ svg("<text class=\"t2\" x=\"5\" y=\"-15\">Processes</text>\n");
+
+ /* surrounding box */
+ svg_graph_box(pcount);
+
+ /* pass 2 - ps boxes */
+ ps = ps_first;
+ while ((ps = get_next_ps(ps))) {
+ _cleanup_free_ char *enc_name = NULL;
+ double endtime;
+ double starttime;
+ int t;
+
+ enc_name = xml_comment_encode(ps->name);
+ if(!enc_name)
+ continue;
+
+ /* leave some trace of what we actually filtered etc. */
+ svg("<!-- %s [%i] ppid=%i runtime=%.03fs -->\n", enc_name, ps->pid,
+ ps->ppid, ps->total);
+
+ starttime = ps->first->sampledata->sampletime;
+
+ if (!ps_filter(ps)) {
+ /* remember where _to_ our children need to draw a line */
+ ps->pos_x = time_to_graph(starttime - graph_start);
+ ps->pos_y = ps_to_graph(j+1); /* bottom left corner */
+ } else if (ps->parent){
+ /* hook children to our parent coords instead */
+ ps->pos_x = ps->parent->pos_x;
+ ps->pos_y = ps->parent->pos_y;
+
+ /* if this is the last child, we might still need to draw a connecting line */
+ if ((!ps->next) && (ps->parent))
+ svg(" <line class=\"dot\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
+ ps->parent->pos_x,
+ ps_to_graph(j-1) + 10.0, /* whee, use the last value here */
+ ps->parent->pos_x,
+ ps->parent->pos_y);
+ continue;
+ }
+
+ endtime = ps->last->sampledata->sampletime;
+ svg(" <rect class=\"ps\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
+ time_to_graph(starttime - graph_start),
+ ps_to_graph(j),
+ time_to_graph(ps->last->sampledata->sampletime - starttime),
+ ps_to_graph(1));
+
+ /* paint cpu load over these */
+ ps->sample = ps->first;
+ t = 1;
+ while (ps->sample->next) {
+ double rt, prt;
+ double wt, wrt;
+ struct ps_sched_struct *prev;
+
+ prev = ps->sample;
+ ps->sample = ps->sample->next;
+
+ /* calculate over interval */
+ rt = ps->sample->runtime - prev->runtime;
+ wt = ps->sample->waittime - prev->waittime;
+
+ prt = (rt / 1000000000) / (ps->sample->sampledata->sampletime - prev->sampledata->sampletime);
+ wrt = (wt / 1000000000) / (ps->sample->sampledata->sampletime - prev->sampledata->sampletime);
+
+ /* this can happen if timekeeping isn't accurate enough */
+ if (prt > 1.0)
+ prt = 1.0;
+ if (wrt > 1.0)
+ wrt = 1.0;
+
+ if ((prt < 0.1) && (wrt < 0.1)) /* =~ 26 (color threshold) */
+ continue;
+
+ svg(" <rect class=\"wait\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
+ time_to_graph(prev->sampledata->sampletime - graph_start),
+ ps_to_graph(j),
+ time_to_graph(ps->sample->sampledata->sampletime - prev->sampledata->sampletime),
+ ps_to_graph(wrt));
+
+ /* draw cpu over wait - TODO figure out how/why run + wait > interval */
+ svg(" <rect class=\"cpu\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
+ time_to_graph(prev->sampledata->sampletime - graph_start),
+ ps_to_graph(j + (1.0 - prt)),
+ time_to_graph(ps->sample->sampledata->sampletime - prev->sampledata->sampletime),
+ ps_to_graph(prt));
+ t++;
+ }
+
+ /* determine where to display the process name */
+ if ((endtime - starttime) < 1.5)
+ /* too small to fit label inside the box */
+ w = endtime;
+ else
+ w = starttime;
+
+ /* text label of process name */
+ svg(" <text x=\"%.03f\" y=\"%.03f\"><![CDATA[%s]]> [%i]<tspan class=\"run\">%.03fs</tspan></text>\n",
+ time_to_graph(w - graph_start) + 5.0,
+ ps_to_graph(j) + 14.0,
+ ps->name,
+ ps->pid,
+ (ps->last->runtime - ps->first->runtime) / 1000000000.0);
+ /* paint lines to the parent process */
+ if (ps->parent) {
+ /* horizontal part */
+ svg(" <line class=\"dot\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
+ time_to_graph(starttime - graph_start),
+ ps_to_graph(j) + 10.0,
+ ps->parent->pos_x,
+ ps_to_graph(j) + 10.0);
+
+ /* one vertical line connecting all the horizontal ones up */
+ if (!ps->next)
+ svg(" <line class=\"dot\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
+ ps->parent->pos_x,
+ ps_to_graph(j) + 10.0,
+ ps->parent->pos_x,
+ ps->parent->pos_y);
+ }
+
+ j++; /* count boxes */
+
+ svg("\n");
+ }
+
+ /* last pass - determine when idle */
+ pid = getpid();
+ /* make sure we start counting from the point where we actually have
+ * data: assume that bootchart's first sample is when data started
+ */
+
+ ps = ps_first;
+ while (ps->next_ps) {
+ ps = ps->next_ps;
+ if (ps->pid == pid)
+ break;
+ }
+
+ /* need to know last node first */
+ ps->sample = ps->first;
+ i = ps->sample->next->sampledata->counter;
+
+ while (ps->sample->next && i<(samples-(arg_hz/2))) {
+ double crt;
+ double brt;
+ int c;
+ int ii;
+ struct ps_sched_struct *sample_hz;
+
+ ps->sample = ps->sample->next;
+ sample_hz = ps->sample;
+ for (ii=0;((ii<(int)arg_hz/2)&&(ps->sample->next));ii++)
+ sample_hz = sample_hz->next;
+
+ /* subtract bootchart cpu utilization from total */
+ crt = 0.0;
+ for (c = 0; c < cpus; c++)
+ crt += sample_hz->sampledata->runtime[c] - ps->sample->sampledata->runtime[c];
+ brt = sample_hz->runtime - ps->sample->runtime;
+ /*
+ * our definition of "idle":
+ *
+ * if for (hz / 2) we've used less CPU than (interval / 2) ...
+ * defaults to 4.0%, which experimentally, is where atom idles
+ */
+ if ((crt - brt) < (interval / 2.0)) {
+ idletime = ps->sample->sampledata->sampletime - graph_start;
+ svg("\n<!-- idle detected at %.03f seconds -->\n",
+ idletime);
+ svg("<line class=\"idle\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
+ time_to_graph(idletime),
+ -arg_scale_y,
+ time_to_graph(idletime),
+ ps_to_graph(pcount) + arg_scale_y);
+ svg("<text class=\"idle\" x=\"%.03f\" y=\"%.03f\">%.01fs</text>\n",
+ time_to_graph(idletime) + 5.0,
+ ps_to_graph(pcount) + arg_scale_y,
+ idletime);
+ break;
+ }
+ i++;
+ }
}
-
-static void svg_top_ten_cpu(void)
-{
- struct ps_struct *top[10];
- struct ps_struct emptyps;
- struct ps_struct *ps;
- int n, m;
-
- memset(&emptyps, 0, sizeof(struct ps_struct));
- for (n=0; n < 10; n++)
- top[n] = &emptyps;
-
- /* walk all ps's and setup ptrs */
- ps = ps_first;
- while ((ps = get_next_ps(ps))) {
- for (n = 0; n < 10; n++) {
- if (ps->total <= top[n]->total)
- continue;
- /* cascade insert */
- for (m = 9; m > n; m--)
- top[m] = top[m-1];
- top[n] = ps;
- break;
- }
- }
-
- svg("<text class=\"t2\" x=\"20\" y=\"0\">Top CPU consumers:</text>\n");
- for (n = 0; n < 10; n++)
- svg("<text class=\"t3\" x=\"20\" y=\"%d\">%3.03fs - %s[%d]</text>\n",
- 20 + (n * 13),
- top[n]->total,
- top[n]->name,
- top[n]->pid);
+static void svg_top_ten_cpu(void) {
+ struct ps_struct *top[10];
+ struct ps_struct emptyps = {};
+ struct ps_struct *ps;
+ int n, m;
+
+ for (n = 0; n < (int) ELEMENTSOF(top); n++)
+ top[n] = &emptyps;
+
+ /* walk all ps's and setup ptrs */
+ ps = ps_first;
+ while ((ps = get_next_ps(ps))) {
+ for (n = 0; n < 10; n++) {
+ if (ps->total <= top[n]->total)
+ continue;
+ /* cascade insert */
+ for (m = 9; m > n; m--)
+ top[m] = top[m-1];
+ top[n] = ps;
+ break;
+ }
+ }
+
+ svg("<text class=\"t2\" x=\"20\" y=\"0\">Top CPU consumers:</text>\n");
+ for (n = 0; n < 10; n++)
+ svg("<text class=\"t3\" x=\"20\" y=\"%d\">%3.03fs - <![CDATA[%s]]> [%d]</text>\n",
+ 20 + (n * 13),
+ top[n]->total,
+ top[n]->name,
+ top[n]->pid);
}
-
-static void svg_top_ten_pss(void)
-{
- struct ps_struct *top[10];
- struct ps_struct emptyps;
- struct ps_struct *ps;
- int n, m;
-
- memset(&emptyps, 0, sizeof(struct ps_struct));
- for (n=0; n < 10; n++)
- top[n] = &emptyps;
-
- /* walk all ps's and setup ptrs */
- ps = ps_first;
- while ((ps = get_next_ps(ps))) {
- for (n = 0; n < 10; n++) {
- if (ps->pss_max <= top[n]->pss_max)
- continue;
- /* cascade insert */
- for (m = 9; m > n; m--)
- top[m] = top[m-1];
- top[n] = ps;
- break;
- }
- }
-
- svg("<text class=\"t2\" x=\"20\" y=\"0\">Top PSS consumers:</text>\n");
- for (n = 0; n < 10; n++)
- svg("<text class=\"t3\" x=\"20\" y=\"%d\">%dK - %s[%d]</text>\n",
- 20 + (n * 13),
- top[n]->pss_max,
- top[n]->name,
- top[n]->pid);
+static void svg_top_ten_pss(void) {
+ struct ps_struct *top[10];
+ struct ps_struct emptyps = {};
+ struct ps_struct *ps;
+ int n, m;
+
+ for (n = 0; n < (int) ELEMENTSOF(top); n++)
+ top[n] = &emptyps;
+
+ /* walk all ps's and setup ptrs */
+ ps = ps_first;
+ while ((ps = get_next_ps(ps))) {
+ for (n = 0; n < 10; n++) {
+ if (ps->pss_max <= top[n]->pss_max)
+ continue;
+ /* cascade insert */
+ for (m = 9; m > n; m--)
+ top[m] = top[m-1];
+ top[n] = ps;
+ break;
+ }
+ }
+
+ svg("<text class=\"t2\" x=\"20\" y=\"0\">Top PSS consumers:</text>\n");
+ for (n = 0; n < 10; n++)
+ svg("<text class=\"t3\" x=\"20\" y=\"%d\">%dK - <![CDATA[%s]]> [%d]</text>\n",
+ 20 + (n * 13),
+ top[n]->pss_max,
+ top[n]->name,
+ top[n]->pid);
}
+void svg_do(const char *build) {
+ struct ps_struct *ps;
-void svg_do(void)
-{
- struct ps_struct *ps;
-
- memset(&str, 0, sizeof(str));
+ memset(&str, 0, sizeof(str));
- ps = ps_first;
+ ps = ps_first;
- /* count initcall thread count first */
- svg_do_initcall(1);
- ksize = (kcount ? ps_to_graph(kcount) + (scale_y * 2) : 0);
+ /* count initcall thread count first */
+ svg_do_initcall(1);
+ ksize = (kcount ? ps_to_graph(kcount) + (arg_scale_y * 2) : 0);
- /* then count processes */
- while ((ps = get_next_ps(ps))) {
- if (!ps_filter(ps))
- pcount++;
- else
- pfiltered++;
- }
- psize = ps_to_graph(pcount) + (scale_y * 2);
+ /* then count processes */
+ while ((ps = get_next_ps(ps))) {
+ if (!ps_filter(ps))
+ pcount++;
+ else
+ pfiltered++;
+ }
+ psize = ps_to_graph(pcount) + (arg_scale_y * 2);
- esize = (entropy ? scale_y * 7 : 0);
+ esize = (arg_entropy ? arg_scale_y * 7 : 0);
- /* after this, we can draw the header with proper sizing */
- svg_header();
+ /* after this, we can draw the header with proper sizing */
+ svg_header();
- svg("<g transform=\"translate(10,400)\">\n");
- svg_io_bi_bar();
- svg("</g>\n\n");
+ svg("<g transform=\"translate(10,400)\">\n");
+ svg_io_bi_bar();
+ svg("</g>\n\n");
- svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (scale_y * 7.0));
- svg_io_bo_bar();
- svg("</g>\n\n");
+ svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 7.0));
+ svg_io_bo_bar();
+ svg("</g>\n\n");
- svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (scale_y * 14.0));
- svg_cpu_bar();
- svg("</g>\n\n");
+ svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 14.0));
+ svg_cpu_bar();
+ svg("</g>\n\n");
- svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (scale_y * 21.0));
- svg_wait_bar();
- svg("</g>\n\n");
+ svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 21.0));
+ svg_wait_bar();
+ svg("</g>\n\n");
- if (kcount) {
- svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (scale_y * 28.0));
- svg_do_initcall(0);
- svg("</g>\n\n");
- }
+ if (kcount) {
+ svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 28.0));
+ svg_do_initcall(0);
+ svg("</g>\n\n");
+ }
- svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (scale_y * 28.0) + ksize);
- svg_ps_bars();
- svg("</g>\n\n");
+ svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 28.0) + ksize);
+ svg_ps_bars();
+ svg("</g>\n\n");
- svg("<g transform=\"translate(10, 0)\">\n");
- svg_title();
- svg("</g>\n\n");
+ svg("<g transform=\"translate(10, 0)\">\n");
+ svg_title(build);
+ svg("</g>\n\n");
- svg("<g transform=\"translate(10,200)\">\n");
- svg_top_ten_cpu();
- svg("</g>\n\n");
+ svg("<g transform=\"translate(10,200)\">\n");
+ svg_top_ten_cpu();
+ svg("</g>\n\n");
- if (entropy) {
- svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (scale_y * 28.0) + ksize + psize);
- svg_entropy_bar();
- svg("</g>\n\n");
- }
+ if (arg_entropy) {
+ svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 28.0) + ksize + psize);
+ svg_entropy_bar();
+ svg("</g>\n\n");
+ }
- if (pss) {
- svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (scale_y * 28.0) + ksize + psize + esize);
- svg_pss_graph();
- svg("</g>\n\n");
+ if (arg_pss) {
+ svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 28.0) + ksize + psize + esize);
+ svg_pss_graph();
+ svg("</g>\n\n");
- svg("<g transform=\"translate(410,200)\">\n");
- svg_top_ten_pss();
- svg("</g>\n\n");
- }
+ svg("<g transform=\"translate(410,200)\">\n");
+ svg_top_ten_pss();
+ svg("</g>\n\n");
+ }
- /* svg footer */
- svg("\n</svg>\n");
+ /* svg footer */
+ svg("\n</svg>\n");
}
diff --git a/src/bootchart/svg.h b/src/bootchart/svg.h
new file mode 100644
index 0000000000..e7369f5111
--- /dev/null
+++ b/src/bootchart/svg.h
@@ -0,0 +1,27 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright (C) 2009-2013 Intel Coproration
+
+ Authors:
+ Auke Kok <auke-jan.h.kok@intel.com>
+
+ 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 <http://www.gnu.org/licenses/>.
+***/
+
+void svg_do(const char *build);
diff --git a/src/cgls/cgls.c b/src/cgls/cgls.c
index cfb728bb80..ef3e5672ab 100644
--- a/src/cgls/cgls.c
+++ b/src/cgls/cgls.c
@@ -33,10 +33,13 @@
#include "util.h"
#include "pager.h"
#include "build.h"
+#include "output-mode.h"
static bool arg_no_pager = false;
static bool arg_kernel_threads = false;
static bool arg_all = false;
+static int arg_full = -1;
+static char* arg_machine = NULL;
static void help(void) {
@@ -46,7 +49,9 @@ static void help(void) {
" --version Show package version\n"
" --no-pager Do not pipe output into a pager\n"
" -a --all Show all groups, including empty\n"
- " -k Include kernel threads in output\n",
+ " --full Do not ellipsize output\n"
+ " -k Include kernel threads in output\n"
+ " -M --machine Show container\n",
program_invocation_short_name);
}
@@ -54,7 +59,8 @@ static int parse_argv(int argc, char *argv[]) {
enum {
ARG_NO_PAGER = 0x100,
- ARG_VERSION
+ ARG_VERSION,
+ ARG_FULL,
};
static const struct option options[] = {
@@ -62,6 +68,8 @@ static int parse_argv(int argc, char *argv[]) {
{ "version", no_argument, NULL, ARG_VERSION },
{ "no-pager", no_argument, NULL, ARG_NO_PAGER },
{ "all", no_argument, NULL, 'a' },
+ { "full", no_argument, NULL, ARG_FULL },
+ { "machine", required_argument, NULL, 'M' },
{ NULL, 0, NULL, 0 }
};
@@ -70,7 +78,7 @@ static int parse_argv(int argc, char *argv[]) {
assert(argc >= 1);
assert(argv);
- while ((c = getopt_long(argc, argv, "hka", options, NULL)) >= 0) {
+ while ((c = getopt_long(argc, argv, "hkaM:", options, NULL)) >= 0) {
switch (c) {
@@ -91,10 +99,18 @@ static int parse_argv(int argc, char *argv[]) {
arg_all = true;
break;
+ case ARG_FULL:
+ arg_full = true;
+ break;
+
case 'k':
arg_kernel_threads = true;
break;
+ case 'M':
+ arg_machine = optarg;
+ break;
+
case '?':
return -EINVAL;
@@ -109,6 +125,8 @@ static int parse_argv(int argc, char *argv[]) {
int main(int argc, char *argv[]) {
int r = 0, retval = EXIT_FAILURE;
+ int output_flags;
+ char _cleanup_free_ *root = NULL;
log_parse_environment();
log_open();
@@ -121,23 +139,40 @@ int main(int argc, char *argv[]) {
goto finish;
}
- if (!arg_no_pager)
- pager_open();
+ if (!arg_no_pager) {
+ r = pager_open(false);
+ if (r > 0) {
+ if (arg_full == -1)
+ arg_full = true;
+ }
+ }
+
+ output_flags =
+ arg_all * OUTPUT_SHOW_ALL |
+ (arg_full > 0) * OUTPUT_FULL_WIDTH;
if (optind < argc) {
- unsigned i;
+ int i;
- for (i = (unsigned) optind; i < (unsigned) argc; i++) {
+ for (i = optind; i < argc; i++) {
int q;
printf("%s:\n", argv[i]);
- q = show_cgroup_by_path(argv[i], NULL, 0, arg_kernel_threads, arg_all);
+ if (arg_machine)
+ root = strjoin("machine/", arg_machine, "/", argv[i], NULL);
+ else
+ root = strdup(argv[i]);
+ if (!root)
+ return log_oom();
+
+ q = show_cgroup_by_path(root, NULL, 0,
+ arg_kernel_threads, output_flags);
if (q < 0)
r = q;
}
} else {
- char _cleanup_free_ *p;
+ _cleanup_free_ char *p;
p = get_current_dir_name();
if (!p) {
@@ -145,31 +180,31 @@ int main(int argc, char *argv[]) {
goto finish;
}
- if (path_startswith(p, "/sys/fs/cgroup")) {
+ if (path_startswith(p, "/sys/fs/cgroup") && !arg_machine) {
printf("Working Directory %s:\n", p);
- r = show_cgroup_by_path(p, NULL, 0, arg_kernel_threads, arg_all);
+ r = show_cgroup_by_path(p, NULL, 0,
+ arg_kernel_threads, output_flags);
} else {
- char _cleanup_free_ *root = NULL;
- const char *t = NULL;
-
- r = cg_get_by_pid(SYSTEMD_CGROUP_CONTROLLER, 1, &root);
- if (r < 0)
- t = "/";
- else {
- if (endswith(root, "/system"))
- root[strlen(root)-7] = 0;
-
- t = root[0] ? root : "/";
+ if (arg_machine)
+ r = cg_get_machine_path(arg_machine, &root);
+ else
+ r = cg_get_root_path(&root);
+ if (r < 0) {
+ log_error("Failed to get %s path: %s",
+ arg_machine ? "machine" : "root", strerror(-r));
+ goto finish;
}
- r = show_cgroup(SYSTEMD_CGROUP_CONTROLLER, t, NULL, 0, arg_kernel_threads, arg_all);
+ r = show_cgroup(SYSTEMD_CGROUP_CONTROLLER, root, NULL, 0,
+ arg_kernel_threads, output_flags);
}
}
- if (r < 0)
- log_error("Failed to list cgroup tree: %s", strerror(-r));
-
- retval = EXIT_SUCCESS;
+ if (r < 0) {
+ log_error("Failed to list cgroup tree %s: %s", root, strerror(-r));
+ retval = EXIT_FAILURE;
+ } else
+ retval = EXIT_SUCCESS;
finish:
pager_close();
diff --git a/src/cgtop/cgtop.c b/src/cgtop/cgtop.c
index f2e62761f1..1e21b0074d 100644
--- a/src/cgtop/cgtop.c
+++ b/src/cgtop/cgtop.c
@@ -19,9 +19,11 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#define __STDC_FORMAT_MACROS
#include <errno.h>
#include <string.h>
#include <stdlib.h>
+#include <stdint.h>
#include <unistd.h>
#include <alloca.h>
#include <getopt.h>
@@ -31,6 +33,7 @@
#include "hashmap.h"
#include "cgroup-util.h"
#include "build.h"
+#include "fileio.h"
typedef struct Group {
char *path;
@@ -68,6 +71,11 @@ static enum {
ORDER_IO
} arg_order = ORDER_CPU;
+static enum {
+ CPU_PERCENT,
+ CPU_TIME,
+} arg_cpu_type = CPU_PERCENT;
+
static void group_free(Group *g) {
assert(g);
@@ -371,16 +379,22 @@ static int group_compare(const void*a, const void *b) {
return 1;
if (arg_order == ORDER_CPU) {
- if (x->cpu_valid && y->cpu_valid) {
-
- if (x->cpu_fraction > y->cpu_fraction)
+ if (arg_cpu_type == CPU_PERCENT) {
+ if (x->cpu_valid && y->cpu_valid) {
+ if (x->cpu_fraction > y->cpu_fraction)
+ return -1;
+ else if (x->cpu_fraction < y->cpu_fraction)
+ return 1;
+ } else if (x->cpu_valid)
return -1;
- else if (x->cpu_fraction < y->cpu_fraction)
+ else if (y->cpu_valid)
return 1;
- } else if (x->cpu_valid)
- return -1;
- else if (y->cpu_valid)
- return 1;
+ } else {
+ if (x->cpu_usage > y->cpu_usage)
+ return -1;
+ else if (x->cpu_usage < y->cpu_usage)
+ return 1;
+ }
}
if (arg_order == ORDER_TASKS) {
@@ -423,17 +437,23 @@ static int group_compare(const void*a, const void *b) {
return strcmp(x->path, y->path);
}
+#define ON ANSI_HIGHLIGHT_ON
+#define OFF ANSI_HIGHLIGHT_OFF
+
static int display(Hashmap *a) {
Iterator i;
Group *g;
Group **array;
- unsigned rows, path_columns, n = 0, j;
+ signed path_columns;
+ unsigned rows, n = 0, j, maxtcpu = 0, maxtpath = 0;
+ char buffer[MAX3(21, FORMAT_BYTES_MAX, FORMAT_TIMESPAN_MAX)];
assert(a);
/* Set cursor to top left corner and clear screen */
- fputs("\033[H"
- "\033[2J", stdout);
+ if (on_tty())
+ fputs("\033[H"
+ "\033[2J", stdout);
array = alloca(sizeof(Group*) * hashmap_size(a));
@@ -443,33 +463,51 @@ static int display(Hashmap *a) {
qsort(array, n, sizeof(Group*), group_compare);
+ /* Find the longest names in one run */
+ for (j = 0; j < n; j++) {
+ unsigned cputlen, pathtlen;
+
+ format_timespan(buffer, sizeof(buffer), (nsec_t) (array[j]->cpu_usage / NSEC_PER_USEC), 0);
+ cputlen = strlen(buffer);
+ maxtcpu = MAX(maxtcpu, cputlen);
+ pathtlen = strlen(array[j]->path);
+ maxtpath = MAX(maxtpath, pathtlen);
+ }
+
+ if (arg_cpu_type == CPU_PERCENT)
+ snprintf(buffer, sizeof(buffer), "%6s", "%CPU");
+ else
+ snprintf(buffer, sizeof(buffer), "%*s", maxtcpu, "CPU Time");
+
rows = lines();
if (rows <= 10)
rows = 10;
- path_columns = columns() - 42;
- if (path_columns < 10)
- path_columns = 10;
-
- printf("%s%-*s%s %s%7s%s %s%6s%s %s%8s%s %s%8s%s %s%8s%s\n\n",
- arg_order == ORDER_PATH ? ANSI_HIGHLIGHT_ON : "", path_columns, "Path",
- arg_order == ORDER_PATH ? ANSI_HIGHLIGHT_OFF : "",
- arg_order == ORDER_TASKS ? ANSI_HIGHLIGHT_ON : "", "Tasks",
- arg_order == ORDER_TASKS ? ANSI_HIGHLIGHT_OFF : "",
- arg_order == ORDER_CPU ? ANSI_HIGHLIGHT_ON : "", "%CPU",
- arg_order == ORDER_CPU ? ANSI_HIGHLIGHT_OFF : "",
- arg_order == ORDER_MEMORY ? ANSI_HIGHLIGHT_ON : "", "Memory",
- arg_order == ORDER_MEMORY ? ANSI_HIGHLIGHT_OFF : "",
- arg_order == ORDER_IO ? ANSI_HIGHLIGHT_ON : "", "Input/s",
- arg_order == ORDER_IO ? ANSI_HIGHLIGHT_OFF : "",
- arg_order == ORDER_IO ? ANSI_HIGHLIGHT_ON : "", "Output/s",
- arg_order == ORDER_IO ? ANSI_HIGHLIGHT_OFF : "");
+ if (on_tty()) {
+ path_columns = columns() - 36 - strlen(buffer);
+ if (path_columns < 10)
+ path_columns = 10;
+
+ printf("%s%-*s%s %s%7s%s %s%s%s %s%8s%s %s%8s%s %s%8s%s\n\n",
+ arg_order == ORDER_PATH ? ON : "", path_columns, "Path",
+ arg_order == ORDER_PATH ? OFF : "",
+ arg_order == ORDER_TASKS ? ON : "", "Tasks",
+ arg_order == ORDER_TASKS ? OFF : "",
+ arg_order == ORDER_CPU ? ON : "", buffer,
+ arg_order == ORDER_CPU ? OFF : "",
+ arg_order == ORDER_MEMORY ? ON : "", "Memory",
+ arg_order == ORDER_MEMORY ? OFF : "",
+ arg_order == ORDER_IO ? ON : "", "Input/s",
+ arg_order == ORDER_IO ? OFF : "",
+ arg_order == ORDER_IO ? ON : "", "Output/s",
+ arg_order == ORDER_IO ? OFF : "");
+ } else
+ path_columns = maxtpath;
for (j = 0; j < n; j++) {
char *p;
- char m[FORMAT_BYTES_MAX];
- if (j + 5 > rows)
+ if (on_tty() && j + 5 > rows)
break;
g = array[j];
@@ -483,21 +521,24 @@ static int display(Hashmap *a) {
else
fputs(" -", stdout);
- if (g->cpu_valid)
- printf(" %6.1f", g->cpu_fraction*100);
- else
- fputs(" -", stdout);
+ if (arg_cpu_type == CPU_PERCENT) {
+ if (g->cpu_valid)
+ printf(" %6.1f", g->cpu_fraction*100);
+ else
+ fputs(" -", stdout);
+ } else
+ printf(" %*s", maxtcpu, format_timespan(buffer, sizeof(buffer), (nsec_t) (g->cpu_usage / NSEC_PER_USEC), 0));
if (g->memory_valid)
- printf(" %8s", format_bytes(m, sizeof(m), g->memory));
+ printf(" %8s", format_bytes(buffer, sizeof(buffer), g->memory));
else
fputs(" -", stdout);
if (g->io_valid) {
printf(" %8s",
- format_bytes(m, sizeof(m), g->io_input_bps));
+ format_bytes(buffer, sizeof(buffer), g->io_input_bps));
printf(" %8s",
- format_bytes(m, sizeof(m), g->io_output_bps));
+ format_bytes(buffer, sizeof(buffer), g->io_output_bps));
} else
fputs(" - -", stdout);
@@ -518,11 +559,12 @@ static void help(void) {
" -c Order by CPU load\n"
" -m Order by memory load\n"
" -i Order by IO load\n"
- " -d --delay=DELAY Specify delay\n"
+ " --cpu[=TYPE] Show CPU usage as time or percentage (default)\n"
+ " -d --delay=DELAY Delay between updates\n"
" -n --iterations=N Run for N iterations before exiting\n"
" -b --batch Run in batch mode, accepting no input\n"
- " --depth=DEPTH Maximum traversal depth (default: 2)\n",
- program_invocation_short_name);
+ " --depth=DEPTH Maximum traversal depth (default: %d)\n",
+ program_invocation_short_name, arg_depth);
}
static void version(void) {
@@ -534,6 +576,7 @@ static int parse_argv(int argc, char *argv[]) {
enum {
ARG_VERSION = 0x100,
ARG_DEPTH,
+ ARG_CPU_TYPE
};
static const struct option options[] = {
@@ -543,6 +586,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "iterations", required_argument, NULL, 'n' },
{ "batch", no_argument, NULL, 'b' },
{ "depth", required_argument, NULL, ARG_DEPTH },
+ { "cpu", optional_argument, NULL, ARG_CPU_TYPE},
{ NULL, 0, NULL, 0 }
};
@@ -564,6 +608,17 @@ static int parse_argv(int argc, char *argv[]) {
version();
return 0;
+ case ARG_CPU_TYPE:
+ if (optarg) {
+ if (strcmp(optarg, "time") == 0)
+ arg_cpu_type = CPU_TIME;
+ else if (strcmp(optarg, "percentage") == 0)
+ arg_cpu_type = CPU_PERCENT;
+ else
+ return -EINVAL;
+ }
+ break;
+
case ARG_DEPTH:
r = safe_atou(optarg, &arg_depth);
if (r < 0) {
@@ -574,7 +629,7 @@ static int parse_argv(int argc, char *argv[]) {
break;
case 'd':
- r = parse_usec(optarg, &arg_delay);
+ r = parse_sec(optarg, &arg_delay);
if (r < 0 || arg_delay <= 0) {
log_error("Failed to parse delay parameter.");
return -EINVAL;
@@ -655,6 +710,9 @@ int main(int argc, char *argv[]) {
signal(SIGWINCH, columns_lines_cache_reset);
+ if (!on_tty())
+ arg_iterations = 1;
+
while (!quit) {
Hashmap *c;
usec_t t;
@@ -735,13 +793,17 @@ int main(int argc, char *argv[]) {
arg_order = ORDER_IO;
break;
+ case '%':
+ arg_cpu_type = arg_cpu_type == CPU_TIME ? CPU_PERCENT : CPU_TIME;
+ break;
+
case '+':
if (arg_delay < USEC_PER_SEC)
arg_delay += USEC_PER_MSEC*250;
else
arg_delay += USEC_PER_SEC;
- fprintf(stdout, "\nIncreased delay to %s.", format_timespan(h, sizeof(h), arg_delay));
+ fprintf(stdout, "\nIncreased delay to %s.", format_timespan(h, sizeof(h), arg_delay, 0));
fflush(stdout);
sleep(1);
break;
@@ -754,7 +816,7 @@ int main(int argc, char *argv[]) {
else
arg_delay -= USEC_PER_SEC;
- fprintf(stdout, "\nDecreased delay to %s.", format_timespan(h, sizeof(h), arg_delay));
+ fprintf(stdout, "\nDecreased delay to %s.", format_timespan(h, sizeof(h), arg_delay, 0));
fflush(stdout);
sleep(1);
break;
@@ -762,8 +824,9 @@ int main(int argc, char *argv[]) {
case '?':
case 'h':
fprintf(stdout,
- "\t<" ANSI_HIGHLIGHT_ON "P" ANSI_HIGHLIGHT_OFF "> By path; <" ANSI_HIGHLIGHT_ON "T" ANSI_HIGHLIGHT_OFF "> By tasks; <" ANSI_HIGHLIGHT_ON "C" ANSI_HIGHLIGHT_OFF "> By CPU; <" ANSI_HIGHLIGHT_ON "M" ANSI_HIGHLIGHT_OFF "> By memory; <" ANSI_HIGHLIGHT_ON "I" ANSI_HIGHLIGHT_OFF "> By I/O\n"
- "\t<" ANSI_HIGHLIGHT_ON "Q" ANSI_HIGHLIGHT_OFF "> Quit; <" ANSI_HIGHLIGHT_ON "+" ANSI_HIGHLIGHT_OFF "> Increase delay; <" ANSI_HIGHLIGHT_ON "-" ANSI_HIGHLIGHT_OFF "> Decrease delay; <" ANSI_HIGHLIGHT_ON "SPACE" ANSI_HIGHLIGHT_OFF "> Refresh");
+ "\t<" ON "P" OFF "> By path; <" ON "T" OFF "> By tasks; <" ON "C" OFF "> By CPU; <" ON "M" OFF "> By memory; <" ON "I" OFF "> By I/O\n"
+ "\t<" ON "+" OFF "> Increase delay; <" ON "-" OFF "> Decrease delay; <" ON "%%" OFF "> Toggle time\n"
+ "\t<" ON "Q" OFF "> Quit; <" ON "SPACE" OFF "> Refresh");
fflush(stdout);
sleep(3);
break;
@@ -776,8 +839,6 @@ int main(int argc, char *argv[]) {
}
}
- log_info("Exiting.");
-
r = 0;
finish:
diff --git a/src/core/automount.c b/src/core/automount.c
index 4a98540d82..a20d5340f2 100644
--- a/src/core/automount.c
+++ b/src/core/automount.c
@@ -109,7 +109,6 @@ static void automount_done(Unit *u) {
assert(a);
unmount_autofs(a);
- unit_ref_unset(&a->mount);
free(a->where);
a->where = NULL;
@@ -200,8 +199,8 @@ static int automount_verify(Automount *a) {
}
static int automount_load(Unit *u) {
- int r;
Automount *a = AUTOMOUNT(u);
+ int r;
assert(u);
assert(u->load_state == UNIT_STUB);
@@ -222,17 +221,15 @@ static int automount_load(Unit *u) {
path_kill_slashes(a->where);
- r = automount_add_mount_links(a);
+ r = unit_load_related_unit(u, ".mount", &x);
if (r < 0)
return r;
- r = unit_load_related_unit(u, ".mount", &x);
+ r = unit_add_two_dependencies(u, UNIT_BEFORE, UNIT_TRIGGERS, x, true);
if (r < 0)
return r;
- unit_ref_set(&a->mount, x);
-
- r = unit_add_two_dependencies(u, UNIT_BEFORE, UNIT_TRIGGERS, UNIT_DEREF(a->mount), true);
+ r = automount_add_mount_links(a);
if (r < 0)
return r;
@@ -586,18 +583,17 @@ fail:
}
static void automount_enter_runnning(Automount *a) {
- int r;
- struct stat st;
_cleanup_dbus_error_free_ DBusError error;
+ struct stat st;
+ int r;
assert(a);
- assert(UNIT_DEREF(a->mount));
dbus_error_init(&error);
/* We don't take mount requests anymore if we are supposed to
* shut down anyway */
- if (unit_pending_inactive(UNIT(a))) {
+ if (unit_stop_pending(UNIT(a))) {
log_debug_unit(UNIT(a)->id,
"Suppressing automount request on %s since unit stop is scheduled.", UNIT(a)->id);
automount_send_ready(a, -EHOSTDOWN);
@@ -616,11 +612,15 @@ static void automount_enter_runnning(Automount *a) {
if (!S_ISDIR(st.st_mode) || st.st_dev != a->dev_id)
log_info_unit(UNIT(a)->id,
"%s's automount point already active?", UNIT(a)->id);
- else if ((r = manager_add_job(UNIT(a)->manager, JOB_START, UNIT_DEREF(a->mount), JOB_REPLACE, true, &error, NULL)) < 0) {
- log_warning_unit(UNIT(a)->id,
- "%s failed to queue mount startup job: %s",
- UNIT(a)->id, bus_error(&error, r));
- goto fail;
+ else {
+ r = manager_add_job(UNIT(a)->manager, JOB_START, UNIT_TRIGGER(UNIT(a)),
+ JOB_REPLACE, true, &error, NULL);
+ if (r < 0) {
+ log_warning_unit(UNIT(a)->id,
+ "%s failed to queue mount startup job: %s",
+ UNIT(a)->id, bus_error(&error, r));
+ goto fail;
+ }
}
automount_set_state(a, AUTOMOUNT_RUNNING);
@@ -643,7 +643,7 @@ static int automount_start(Unit *u) {
return -EEXIST;
}
- if (UNIT_DEREF(a->mount)->load_state != UNIT_LOADED)
+ if (UNIT_TRIGGER(u)->load_state != UNIT_LOADED)
return -ENOENT;
a->result = AUTOMOUNT_SUCCESS;
@@ -765,14 +765,12 @@ static const char *automount_sub_state_to_string(Unit *u) {
}
static bool automount_check_gc(Unit *u) {
- Automount *a = AUTOMOUNT(u);
-
- assert(a);
+ assert(u);
- if (!UNIT_DEREF(a->mount))
+ if (!UNIT_TRIGGER(u))
return false;
- return UNIT_VTABLE(UNIT_DEREF(a->mount))->check_gc(UNIT_DEREF(a->mount));
+ return UNIT_VTABLE(UNIT_TRIGGER(u))->check_gc(UNIT_TRIGGER(u));
}
static void automount_fd_event(Unit *u, int fd, uint32_t events, Watch *w) {
diff --git a/src/core/automount.h b/src/core/automount.h
index 3d5736d1cb..0c6b8a72e9 100644
--- a/src/core/automount.h
+++ b/src/core/automount.h
@@ -48,8 +48,6 @@ struct Automount {
char *where;
- UnitRef mount;
-
int pipe_fd;
mode_t directory_mode;
Watch pipe_watch;
@@ -66,8 +64,8 @@ int automount_send_ready(Automount *a, int status);
int automount_add_one_mount_link(Automount *a, Mount *m);
-const char* automount_state_to_string(AutomountState i);
-AutomountState automount_state_from_string(const char *s);
+const char* automount_state_to_string(AutomountState i) _const_;
+AutomountState automount_state_from_string(const char *s) _pure_;
-const char* automount_result_to_string(AutomountResult i);
-AutomountResult automount_result_from_string(const char *s);
+const char* automount_result_to_string(AutomountResult i) _const_;
+AutomountResult automount_result_from_string(const char *s) _pure_;
diff --git a/src/core/bus-errors.h b/src/core/bus-errors.h
index 04c1b2849d..7a4084ea15 100644
--- a/src/core/bus-errors.h
+++ b/src/core/bus-errors.h
@@ -21,9 +21,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <string.h>
-#include <dbus/dbus.h>
-
#define BUS_ERROR_NO_SUCH_UNIT "org.freedesktop.systemd1.NoSuchUnit"
#define BUS_ERROR_NO_SUCH_JOB "org.freedesktop.systemd1.NoSuchJob"
#define BUS_ERROR_NOT_SUBSCRIBED "org.freedesktop.systemd1.NotSubscribed"
@@ -43,13 +40,3 @@
#define BUS_ERROR_TRANSACTION_ORDER_IS_CYCLIC "org.freedesktop.systemd1.TransactionOrderIsCyclic"
#define BUS_ERROR_SHUTTING_DOWN "org.freedesktop.systemd1.ShuttingDown"
#define BUS_ERROR_NO_SUCH_PROCESS "org.freedesktop.systemd1.NoSuchProcess"
-
-static inline const char *bus_error(const DBusError *e, int r) {
- if (e && e->message)
- return e->message;
-
- if (r >= 0)
- return strerror(r);
-
- return strerror(-r);
-}
diff --git a/src/core/cgroup-attr.c b/src/core/cgroup-attr.c
index 71af09cf87..7e3e08eabb 100644
--- a/src/core/cgroup-attr.c
+++ b/src/core/cgroup-attr.c
@@ -22,11 +22,11 @@
#include "cgroup-attr.h"
#include "cgroup-util.h"
#include "list.h"
+#include "fileio.h"
int cgroup_attribute_apply(CGroupAttribute *a, CGroupBonding *b) {
+ _cleanup_free_ char *path = NULL, *v = NULL;
int r;
- char *path = NULL;
- char *v = NULL;
assert(a);
@@ -34,25 +34,20 @@ int cgroup_attribute_apply(CGroupAttribute *a, CGroupBonding *b) {
if (!b)
return 0;
- if (a->map_callback) {
- r = a->map_callback(a->controller, a->name, a->value, &v);
+ if (a->semantics && a->semantics->map_write) {
+ r = a->semantics->map_write(a->semantics, a->value, &v);
if (r < 0)
return r;
}
r = cg_get_path(a->controller, b->path, a->name, &path);
- if (r < 0) {
- free(v);
+ if (r < 0)
return r;
- }
- r = write_one_line_file(path, v ? v : a->value);
+ r = write_string_file(path, v ? v : a->value);
if (r < 0)
log_warning("Failed to write '%s' to %s: %s", v ? v : a->value, path, strerror(-r));
- free(path);
- free(v);
-
return r;
}
@@ -71,23 +66,50 @@ int cgroup_attribute_apply_list(CGroupAttribute *first, CGroupBonding *b) {
return r;
}
-CGroupAttribute *cgroup_attribute_find_list(CGroupAttribute *first, const char *controller, const char *name) {
+bool cgroup_attribute_matches(CGroupAttribute *a, const char *controller, const char *name) {
+ assert(a);
+
+ if (controller) {
+ if (streq(a->controller, controller) && (!name || streq(a->name, name)))
+ return true;
+
+ } else if (!name)
+ return true;
+ else if (streq(a->name, name)) {
+ size_t x, y;
+ x = strlen(a->controller);
+ y = strlen(name);
+
+ if (y > x &&
+ memcmp(a->controller, name, x) == 0 &&
+ name[x] == '.')
+ return true;
+ }
+
+ return false;
+}
+
+CGroupAttribute *cgroup_attribute_find_list(
+ CGroupAttribute *first,
+ const char *controller,
+ const char *name) {
CGroupAttribute *a;
- assert(controller);
assert(name);
LIST_FOREACH(by_unit, a, first)
- if (streq(a->controller, controller) &&
- streq(a->name, name))
+ if (cgroup_attribute_matches(a, controller, name))
return a;
return NULL;
}
-static void cgroup_attribute_free(CGroupAttribute *a) {
+void cgroup_attribute_free(CGroupAttribute *a) {
assert(a);
+ if (a->unit)
+ LIST_REMOVE(CGroupAttribute, by_unit, a->unit->cgroup_attributes, a);
+
free(a->controller);
free(a->name);
free(a->value);
@@ -100,3 +122,11 @@ void cgroup_attribute_free_list(CGroupAttribute *first) {
LIST_FOREACH_SAFE(by_unit, a, n, first)
cgroup_attribute_free(a);
}
+
+void cgroup_attribute_free_some(CGroupAttribute *first, const char *controller, const char *name) {
+ CGroupAttribute *a, *n;
+
+ LIST_FOREACH_SAFE(by_unit, a, n, first)
+ if (cgroup_attribute_matches(a, controller, name))
+ cgroup_attribute_free(a);
+}
diff --git a/src/core/cgroup-attr.h b/src/core/cgroup-attr.h
index 2b754eac40..3a13b7c92d 100644
--- a/src/core/cgroup-attr.h
+++ b/src/core/cgroup-attr.h
@@ -23,17 +23,18 @@
typedef struct CGroupAttribute CGroupAttribute;
-typedef int (*CGroupAttributeMapCallback)(const char *controller, const char*name, const char *value, char **ret);
-
#include "unit.h"
#include "cgroup.h"
+#include "cgroup-semantics.h"
struct CGroupAttribute {
char *controller;
char *name;
char *value;
- CGroupAttributeMapCallback map_callback;
+ Unit *unit;
+
+ const CGroupSemantics *semantics;
LIST_FIELDS(CGroupAttribute, by_unit);
};
@@ -41,6 +42,9 @@ struct CGroupAttribute {
int cgroup_attribute_apply(CGroupAttribute *a, CGroupBonding *b);
int cgroup_attribute_apply_list(CGroupAttribute *first, CGroupBonding *b);
-CGroupAttribute *cgroup_attribute_find_list(CGroupAttribute *first, const char *controller, const char *name);
+bool cgroup_attribute_matches(CGroupAttribute *a, const char *controller, const char *name) _pure_;
+CGroupAttribute *cgroup_attribute_find_list(CGroupAttribute *first, const char *controller, const char *name) _pure_;
+void cgroup_attribute_free(CGroupAttribute *a);
void cgroup_attribute_free_list(CGroupAttribute *first);
+void cgroup_attribute_free_some(CGroupAttribute *first, const char *controller, const char *name);
diff --git a/src/core/cgroup-semantics.c b/src/core/cgroup-semantics.c
new file mode 100644
index 0000000000..82b02bbd78
--- /dev/null
+++ b/src/core/cgroup-semantics.c
@@ -0,0 +1,333 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ 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 <http://www.gnu.org/licenses/>.
+***/
+
+#include "util.h"
+#include "strv.h"
+#include "path-util.h"
+#include "cgroup-util.h"
+
+#include "cgroup-semantics.h"
+
+static int parse_cpu_shares(const CGroupSemantics *s, const char *value, char **ret) {
+ unsigned long ul;
+
+ assert(s);
+ assert(value);
+ assert(ret);
+
+ if (safe_atolu(value, &ul) < 0 || ul < 1)
+ return -EINVAL;
+
+ if (asprintf(ret, "%lu", ul) < 0)
+ return -ENOMEM;
+
+ return 1;
+}
+
+static int parse_memory_limit(const CGroupSemantics *s, const char *value, char **ret) {
+ off_t sz;
+
+ assert(s);
+ assert(value);
+ assert(ret);
+
+ if (parse_bytes(value, &sz) < 0 || sz <= 0)
+ return -EINVAL;
+
+ if (asprintf(ret, "%llu", (unsigned long long) sz) < 0)
+ return -ENOMEM;
+
+ return 1;
+}
+
+static int parse_device(const CGroupSemantics *s, const char *value, char **ret) {
+ _cleanup_strv_free_ char **l = NULL;
+ char *x;
+ unsigned k;
+
+ assert(s);
+ assert(value);
+ assert(ret);
+
+ l = strv_split_quoted(value);
+ if (!l)
+ return -ENOMEM;
+
+ k = strv_length(l);
+ if (k < 1 || k > 2)
+ return -EINVAL;
+
+ if (!streq(l[0], "*") && !path_startswith(l[0], "/dev"))
+ return -EINVAL;
+
+ if (!isempty(l[1]) && !in_charset(l[1], "rwm"))
+ return -EINVAL;
+
+ x = strdup(value);
+ if (!x)
+ return -ENOMEM;
+
+ *ret = x;
+ return 1;
+}
+
+static int parse_blkio_weight(const CGroupSemantics *s, const char *value, char **ret) {
+ _cleanup_strv_free_ char **l = NULL;
+ unsigned long ul;
+
+ assert(s);
+ assert(value);
+ assert(ret);
+
+ l = strv_split_quoted(value);
+ if (!l)
+ return -ENOMEM;
+
+ if (strv_length(l) != 1)
+ return 0; /* Returning 0 will cause parse_blkio_weight_device() be tried instead */
+
+ if (safe_atolu(l[0], &ul) < 0 || ul < 10 || ul > 1000)
+ return -EINVAL;
+
+ if (asprintf(ret, "%lu", ul) < 0)
+ return -ENOMEM;
+
+ return 1;
+}
+
+static int parse_blkio_weight_device(const CGroupSemantics *s, const char *value, char **ret) {
+ _cleanup_strv_free_ char **l = NULL;
+ unsigned long ul;
+
+ assert(s);
+ assert(value);
+ assert(ret);
+
+ l = strv_split_quoted(value);
+ if (!l)
+ return -ENOMEM;
+
+ if (strv_length(l) != 2)
+ return -EINVAL;
+
+ if (!path_startswith(l[0], "/dev"))
+ return -EINVAL;
+
+ if (safe_atolu(l[1], &ul) < 0 || ul < 10 || ul > 1000)
+ return -EINVAL;
+
+ if (asprintf(ret, "%s %lu", l[0], ul) < 0)
+ return -ENOMEM;
+
+ return 1;
+}
+
+static int parse_blkio_bandwidth(const CGroupSemantics *s, const char *value, char **ret) {
+ _cleanup_strv_free_ char **l = NULL;
+ off_t bytes;
+
+ assert(s);
+ assert(value);
+ assert(ret);
+
+ l = strv_split_quoted(value);
+ if (!l)
+ return -ENOMEM;
+
+ if (strv_length(l) != 2)
+ return -EINVAL;
+
+ if (!path_startswith(l[0], "/dev")) {
+ return -EINVAL;
+ }
+
+ if (parse_bytes(l[1], &bytes) < 0 || bytes <= 0)
+ return -EINVAL;
+
+ if (asprintf(ret, "%s %llu", l[0], (unsigned long long) bytes) < 0)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static int map_device(const CGroupSemantics *s, const char *value, char **ret) {
+ _cleanup_strv_free_ char **l = NULL;
+ unsigned k;
+
+ assert(s);
+ assert(value);
+ assert(ret);
+
+ l = strv_split_quoted(value);
+ if (!l)
+ return -ENOMEM;
+
+ k = strv_length(l);
+ if (k < 1 || k > 2)
+ return -EINVAL;
+
+ if (streq(l[0], "*")) {
+
+ if (asprintf(ret, "a *:*%s%s",
+ isempty(l[1]) ? "" : " ", strempty(l[1])) < 0)
+ return -ENOMEM;
+ } else {
+ struct stat st;
+
+ if (stat(l[0], &st) < 0) {
+ log_warning("Couldn't stat device %s", l[0]);
+ return -errno;
+ }
+
+ if (!S_ISCHR(st.st_mode) && !S_ISBLK(st.st_mode)) {
+ log_warning("%s is not a device.", l[0]);
+ return -ENODEV;
+ }
+
+ if (asprintf(ret, "%c %u:%u%s%s",
+ S_ISCHR(st.st_mode) ? 'c' : 'b',
+ major(st.st_rdev), minor(st.st_rdev),
+ isempty(l[1]) ? "" : " ", strempty(l[1])) < 0)
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static int map_blkio(const CGroupSemantics *s, const char *value, char **ret) {
+ _cleanup_strv_free_ char **l = NULL;
+ struct stat st;
+ dev_t d;
+
+ assert(s);
+ assert(value);
+ assert(ret);
+
+ l = strv_split_quoted(value);
+ if (!l)
+ return log_oom();
+
+ if (strv_length(l) != 2)
+ return -EINVAL;
+
+ if (stat(l[0], &st) < 0) {
+ log_warning("Couldn't stat device %s", l[0]);
+ return -errno;
+ }
+
+ if (S_ISBLK(st.st_mode))
+ d = st.st_rdev;
+ else if (major(st.st_dev) != 0) {
+ /* If this is not a device node then find the block
+ * device this file is stored on */
+ d = st.st_dev;
+
+ /* If this is a partition, try to get the originating
+ * block device */
+ block_get_whole_disk(d, &d);
+ } else {
+ log_warning("%s is not a block device and file system block device cannot be determined or is not local.", l[0]);
+ return -ENODEV;
+ }
+
+ if (asprintf(ret, "%u:%u %s", major(d), minor(d), l[1]) < 0)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static const CGroupSemantics semantics[] = {
+ { "cpu", "cpu.shares", "CPUShare", false, parse_cpu_shares, NULL, NULL },
+ { "memory", "memory.soft_limit_in_bytes", "MemorySoftLimit", false, parse_memory_limit, NULL, NULL },
+ { "memory", "memory.limit_in_bytes", "MemoryLimit", false, parse_memory_limit, NULL, NULL },
+ { "devices", "devices.allow", "DeviceAllow", true, parse_device, map_device, NULL },
+ { "devices", "devices.deny", "DeviceDeny", true, parse_device, map_device, NULL },
+ { "blkio", "blkio.weight", "BlockIOWeight", false, parse_blkio_weight, NULL, NULL },
+ { "blkio", "blkio.weight_device", "BlockIOWeight", true, parse_blkio_weight_device, map_blkio, NULL },
+ { "blkio", "blkio.read_bps_device", "BlockIOReadBandwidth", true, parse_blkio_bandwidth, map_blkio, NULL },
+ { "blkio", "blkio.write_bps_device", "BlockIOWriteBandwidth", true, parse_blkio_bandwidth, map_blkio, NULL }
+};
+
+int cgroup_semantics_find(
+ const char *controller,
+ const char *name,
+ const char *value,
+ char **ret,
+ const CGroupSemantics **_s) {
+
+ _cleanup_free_ char *c = NULL;
+ unsigned i;
+ int r;
+
+ assert(name);
+ assert(_s);
+ assert(!value == !ret);
+
+ if (!controller) {
+ r = cg_controller_from_attr(name, &c);
+ if (r < 0)
+ return r;
+
+ controller = c;
+ }
+
+ for (i = 0; i < ELEMENTSOF(semantics); i++) {
+ const CGroupSemantics *s = semantics + i;
+ bool matches_name, matches_pretty;
+
+ if (controller && s->controller && !streq(s->controller, controller))
+ continue;
+
+ matches_name = s->name && streq(s->name, name);
+ matches_pretty = s->pretty && streq(s->pretty, name);
+
+ if (!matches_name && !matches_pretty)
+ continue;
+
+ if (value) {
+ if (matches_pretty && s->map_pretty) {
+
+ r = s->map_pretty(s, value, ret);
+ if (r < 0)
+ return r;
+
+ if (r == 0)
+ continue;
+
+ } else {
+ char *x;
+
+ x = strdup(value);
+ if (!x)
+ return -ENOMEM;
+
+ *ret = x;
+ }
+ }
+
+ *_s = s;
+ return 1;
+ }
+
+ *ret = NULL;
+ *_s = NULL;
+ return 0;
+}
diff --git a/src/core/cgroup-semantics.h b/src/core/cgroup-semantics.h
new file mode 100644
index 0000000000..4f848f4bb7
--- /dev/null
+++ b/src/core/cgroup-semantics.h
@@ -0,0 +1,43 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#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 <http://www.gnu.org/licenses/>.
+***/
+
+typedef struct CGroupSemantics CGroupSemantics;
+
+struct CGroupSemantics {
+ const char *controller;
+ const char *name;
+ const char *pretty;
+
+ bool multiple;
+
+ /* This call is used for parsing the pretty value to the actual attribute value */
+ int (*map_pretty)(const CGroupSemantics *semantics, const char *value, char **ret);
+
+ /* Right before writing this attribute the attribute value is converted to a low-level value */
+ int (*map_write)(const CGroupSemantics *semantics, const char *value, char **ret);
+
+ /* If this attribute takes a list, this call can be used to reset the list to empty */
+ int (*reset)(const CGroupSemantics *semantics, const char *group);
+};
+
+int cgroup_semantics_find(const char *controller, const char *name, const char *value, char **ret, const CGroupSemantics **semantics);
diff --git a/src/core/cgroup.c b/src/core/cgroup.c
index 8fc1731485..83df0f3c9a 100644
--- a/src/core/cgroup.c
+++ b/src/core/cgroup.c
@@ -40,7 +40,7 @@ int cgroup_bonding_realize(CGroupBonding *b) {
assert(b->path);
assert(b->controller);
- r = cg_create(b->controller, b->path);
+ r = cg_create(b->controller, b->path, NULL);
if (r < 0) {
log_warning("Failed to create cgroup %s:%s: %s", b->controller, b->path, strerror(-r));
return r;
@@ -110,9 +110,8 @@ void cgroup_bonding_trim_list(CGroupBonding *first, bool delete_root) {
cgroup_bonding_trim(b, delete_root);
}
-
int cgroup_bonding_install(CGroupBonding *b, pid_t pid, const char *cgroup_suffix) {
- char *p = NULL;
+ _cleanup_free_ char *p = NULL;
const char *path;
int r;
@@ -129,8 +128,6 @@ int cgroup_bonding_install(CGroupBonding *b, pid_t pid, const char *cgroup_suffi
path = b->path;
r = cg_create_and_attach(b->controller, path, pid);
- free(p);
-
if (r < 0)
return r;
@@ -151,6 +148,34 @@ int cgroup_bonding_install_list(CGroupBonding *first, pid_t pid, const char *cgr
return 0;
}
+int cgroup_bonding_migrate(CGroupBonding *b, CGroupBonding *list) {
+ CGroupBonding *q;
+ int ret = 0;
+
+ LIST_FOREACH(by_unit, q, list) {
+ int r;
+
+ if (q == b)
+ continue;
+
+ if (!q->ours)
+ continue;
+
+ r = cg_migrate_recursive(q->controller, q->path, b->controller, b->path, true, false);
+ if (r < 0 && ret == 0)
+ ret = r;
+ }
+
+ return ret;
+}
+
+int cgroup_bonding_migrate_to(CGroupBonding *b, const char *target, bool rem) {
+ assert(b);
+ assert(target);
+
+ return cg_migrate_recursive(b->controller, b->path, b->controller, target, true, rem);
+}
+
int cgroup_bonding_set_group_access(CGroupBonding *b, mode_t mode, uid_t uid, gid_t gid) {
assert(b);
@@ -294,9 +319,10 @@ int cgroup_bonding_is_empty_list(CGroupBonding *first) {
}
int manager_setup_cgroup(Manager *m) {
- char *current = NULL, *path = NULL;
+ _cleanup_free_ char *current = NULL, *path = NULL;
+ char suffix_buffer[sizeof("/systemd-") + DECIMAL_STR_MAX(pid_t)];
+ const char *suffix;
int r;
- char suffix[32];
assert(m);
@@ -307,17 +333,17 @@ int manager_setup_cgroup(Manager *m) {
}
/* 1. Determine hierarchy */
- r = cg_get_by_pid(SYSTEMD_CGROUP_CONTROLLER, 0, &current);
+ r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, 0, &current);
if (r < 0) {
log_error("Cannot determine cgroup we are running in: %s", strerror(-r));
- goto finish;
+ return r;
}
if (m->running_as == SYSTEMD_SYSTEM)
- strcpy(suffix, "/system");
+ suffix = "/system";
else {
- snprintf(suffix, sizeof(suffix), "/systemd-%lu", (unsigned long) getpid());
- char_array_0(suffix);
+ sprintf(suffix_buffer, "/systemd-%lu", (unsigned long) getpid());
+ suffix = suffix_buffer;
}
free(m->cgroup_hierarchy);
@@ -325,39 +351,42 @@ int manager_setup_cgroup(Manager *m) {
/* We probably got reexecuted and can continue to use our root cgroup */
m->cgroup_hierarchy = current;
current = NULL;
-
} else {
/* We need a new root cgroup */
- m->cgroup_hierarchy = NULL;
- if (asprintf(&m->cgroup_hierarchy, "%s%s", streq(current, "/") ? "" : current, suffix) < 0) {
- r = log_oom();
- goto finish;
- }
+ if (streq(current, "/"))
+ m->cgroup_hierarchy = strdup(suffix);
+ else
+ m->cgroup_hierarchy = strappend(current, suffix);
+
+ if (!m->cgroup_hierarchy)
+ return log_oom();
}
/* 2. Show data */
r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_hierarchy, NULL, &path);
if (r < 0) {
log_error("Cannot find cgroup mount point: %s", strerror(-r));
- goto finish;
+ return r;
}
log_debug("Using cgroup controller " SYSTEMD_CGROUP_CONTROLLER ". File system hierarchy is at %s.", path);
/* 3. Install agent */
- r = cg_install_release_agent(SYSTEMD_CGROUP_CONTROLLER, SYSTEMD_CGROUP_AGENT_PATH);
- if (r < 0)
- log_warning("Failed to install release agent, ignoring: %s", strerror(-r));
- else if (r > 0)
- log_debug("Installed release agent.");
- else
- log_debug("Release agent already installed.");
+ if (m->running_as == SYSTEMD_SYSTEM) {
+ r = cg_install_release_agent(SYSTEMD_CGROUP_CONTROLLER, SYSTEMD_CGROUP_AGENT_PATH);
+ if (r < 0)
+ log_warning("Failed to install release agent, ignoring: %s", strerror(-r));
+ else if (r > 0)
+ log_debug("Installed release agent.");
+ else
+ log_debug("Release agent already installed.");
+ }
/* 4. Realize the group */
r = cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_hierarchy, 0);
if (r < 0) {
log_error("Failed to create root cgroup hierarchy: %s", strerror(-r));
- goto finish;
+ return r;
}
/* 5. And pin it, so that it cannot be unmounted */
@@ -367,19 +396,21 @@ int manager_setup_cgroup(Manager *m) {
m->pin_cgroupfs_fd = open(path, O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_NOCTTY|O_NONBLOCK);
if (r < 0) {
log_error("Failed to open pin file: %m");
- r = -errno;
- goto finish;
+ return -errno;
}
- log_debug("Created root group.");
-
+ /* 6. Remove non-existing controllers from the default controllers list */
cg_shorten_controllers(m->default_controllers);
-finish:
- free(current);
- free(path);
+ /* 7. Let's create the user and machine hierarchies
+ * right-away, so that people can inotify on them, if they
+ * wish, without this being racy. */
+ if (m->running_as == SYSTEMD_SYSTEM) {
+ cg_create(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_hierarchy, "../user");
+ cg_create(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_hierarchy, "../machine");
+ }
- return r;
+ return 0;
}
void manager_shutdown_cgroup(Manager *m, bool delete) {
@@ -411,7 +442,7 @@ int cgroup_bonding_get(Manager *m, const char *cgroup, CGroupBonding **bonding)
return 1;
}
- p = strdup(cgroup);
+ p = strdupa(cgroup);
if (!p)
return -ENOMEM;
@@ -419,8 +450,7 @@ int cgroup_bonding_get(Manager *m, const char *cgroup, CGroupBonding **bonding)
char *e;
e = strrchr(p, '/');
- if (!e || e == p) {
- free(p);
+ if (e == p || !e) {
*bonding = NULL;
return 0;
}
@@ -429,7 +459,6 @@ int cgroup_bonding_get(Manager *m, const char *cgroup, CGroupBonding **bonding)
b = hashmap_get(m->cgroup_bondings, p);
if (b) {
- free(p);
*bonding = b;
return 1;
}
@@ -484,7 +513,7 @@ Unit* cgroup_unit_by_pid(Manager *m, pid_t pid) {
if (pid <= 1)
return NULL;
- if (cg_get_by_pid(SYSTEMD_CGROUP_CONTROLLER, pid, &group) < 0)
+ if (cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, pid, &group) < 0)
return NULL;
l = hashmap_get(m->cgroup_bondings, group);
@@ -520,7 +549,8 @@ Unit* cgroup_unit_by_pid(Manager *m, pid_t pid) {
CGroupBonding *cgroup_bonding_find_list(CGroupBonding *first, const char *controller) {
CGroupBonding *b;
- assert(controller);
+ if (!controller)
+ controller = SYSTEMD_CGROUP_CONTROLLER;
LIST_FOREACH(by_unit, b, first)
if (streq(b->controller, controller))
diff --git a/src/core/cgroup.h b/src/core/cgroup.h
index 229da52ba4..6555d89e37 100644
--- a/src/core/cgroup.h
+++ b/src/core/cgroup.h
@@ -58,6 +58,9 @@ void cgroup_bonding_free_list(CGroupBonding *first, bool trim);
int cgroup_bonding_install(CGroupBonding *b, pid_t pid, const char *suffix);
int cgroup_bonding_install_list(CGroupBonding *first, pid_t pid, const char *suffix);
+int cgroup_bonding_migrate(CGroupBonding *b, CGroupBonding *list);
+int cgroup_bonding_migrate_to(CGroupBonding *b, const char *target, bool rem);
+
int cgroup_bonding_set_group_access(CGroupBonding *b, mode_t mode, uid_t uid, gid_t gid);
int cgroup_bonding_set_group_access_list(CGroupBonding *b, mode_t mode, uid_t uid, gid_t gid);
@@ -73,7 +76,7 @@ void cgroup_bonding_trim_list(CGroupBonding *first, bool delete_root);
int cgroup_bonding_is_empty(CGroupBonding *b);
int cgroup_bonding_is_empty_list(CGroupBonding *first);
-CGroupBonding *cgroup_bonding_find_list(CGroupBonding *first, const char *controller);
+CGroupBonding *cgroup_bonding_find_list(CGroupBonding *first, const char *controller) _pure_;
char *cgroup_bonding_to_string(CGroupBonding *b);
diff --git a/src/core/condition.c b/src/core/condition.c
index b3184922b8..16cae6d23b 100644
--- a/src/core/condition.c
+++ b/src/core/condition.c
@@ -36,6 +36,7 @@
#include "condition.h"
#include "virt.h"
#include "path-util.h"
+#include "fileio.h"
Condition* condition_new(ConditionType type, const char *parameter, bool trigger, bool negate) {
Condition *c;
@@ -161,6 +162,10 @@ static bool test_security(const char *parameter) {
if (streq(parameter, "selinux"))
return is_selinux_enabled() > 0;
#endif
+ if (streq(parameter, "apparmor"))
+ return access("/sys/kernel/security/apparmor/", F_OK) == 0;
+ if (streq(parameter, "smack"))
+ return access("/sys/fs/smackfs", F_OK) == 0;
return false;
}
diff --git a/src/core/condition.h b/src/core/condition.h
index 1797385d26..50ed955af9 100644
--- a/src/core/condition.h
+++ b/src/core/condition.h
@@ -66,5 +66,5 @@ bool condition_test_list(Condition *c);
void condition_dump(Condition *c, FILE *f, const char *prefix);
void condition_dump_list(Condition *c, FILE *f, const char *prefix);
-const char* condition_type_to_string(ConditionType t);
-int condition_type_from_string(const char *s);
+const char* condition_type_to_string(ConditionType t) _const_;
+int condition_type_from_string(const char *s) _pure_;
diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c
index e815cb58e4..2a8a0e1ac5 100644
--- a/src/core/dbus-execute.c
+++ b/src/core/dbus-execute.c
@@ -29,6 +29,7 @@
#include "strv.h"
#include "dbus-common.h"
#include "syscall-list.h"
+#include "fileio.h"
DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_execute_append_input, exec_input, ExecInput);
DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_execute_append_output, exec_output, ExecOutput);
@@ -159,12 +160,12 @@ int bus_execute_append_cpu_sched_priority(DBusMessageIter *i, const char *proper
if (c->cpu_sched_set)
n = c->cpu_sched_priority;
else {
- struct sched_param p;
- n = 0;
+ struct sched_param p = {};
- zero(p);
if (sched_getparam(0, &p) >= 0)
n = p.sched_priority;
+ else
+ n = 0;
}
if (!dbus_message_iter_append_basic(i, DBUS_TYPE_INT32, &n))
@@ -278,9 +279,8 @@ int bus_execute_append_rlimits(DBusMessageIter *i, const char *property, void *d
if (c->rlimit[r])
u = (uint64_t) c->rlimit[r]->rlim_max;
else {
- struct rlimit rl;
+ struct rlimit rl = {};
- zero(rl);
getrlimit(r, &rl);
u = (uint64_t) rl.rlim_max;
diff --git a/src/core/dbus-execute.h b/src/core/dbus-execute.h
index eaa1b73e69..91d70e535f 100644
--- a/src/core/dbus-execute.h
+++ b/src/core/dbus-execute.h
@@ -37,6 +37,7 @@
#define BUS_EXEC_CONTEXT_INTERFACE \
" <property name=\"Environment\" type=\"as\" access=\"read\"/>\n" \
+ " <property name=\"EnvironmentFiles\" type=\"a(sb)\" access=\"read\"/>\n" \
" <property name=\"UMask\" type=\"u\" access=\"read\"/>\n" \
" <property name=\"LimitCPU\" type=\"t\" access=\"read\"/>\n" \
" <property name=\"LimitFSIZE\" type=\"t\" access=\"read\"/>\n" \
@@ -88,13 +89,11 @@
" <property name=\"InaccessibleDirectories\" type=\"as\" access=\"read\"/>\n" \
" <property name=\"MountFlags\" type=\"t\" access=\"read\"/>\n" \
" <property name=\"PrivateTmp\" type=\"b\" access=\"read\"/>\n" \
+ " <property name=\"PrivateNetwork\" type=\"b\" access=\"read\"/>\n" \
" <property name=\"SameProcessGroup\" type=\"b\" access=\"read\"/>\n" \
- " <property name=\"KillMode\" type=\"s\" access=\"read\"/>\n" \
- " <property name=\"KillSignal\" type=\"i\" access=\"read\"/>\n" \
" <property name=\"UtmpIdentifier\" type=\"s\" access=\"read\"/>\n" \
" <property name=\"ControlGroupModify\" type=\"b\" access=\"read\"/>\n" \
" <property name=\"ControlGroupPersistent\" type=\"b\" access=\"read\"/>\n" \
- " <property name=\"PrivateNetwork\" type=\"b\" access=\"read\"/>\n" \
" <property name=\"IgnoreSIGPIPE\" type=\"b\" access=\"read\"/>\n" \
" <property name=\"NoNewPrivileges\" type=\"b\" access=\"read\"/>\n" \
" <property name=\"SystemCallFilter\" type=\"au\" access=\"read\"/>\n"
diff --git a/src/core/dbus-job.c b/src/core/dbus-job.c
index fdc1dce177..98ccfa62ec 100644
--- a/src/core/dbus-job.c
+++ b/src/core/dbus-job.c
@@ -101,12 +101,11 @@ static DBusHandlerResult bus_job_message_dispatch(Job *j, DBusConnection *connec
if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Job", "Cancel")) {
SELINUX_UNIT_ACCESS_CHECK(j->unit, connection, message, "stop");
+ job_finish_and_invalidate(j, JOB_CANCELED, true);
reply = dbus_message_new_method_return(message);
if (!reply)
return DBUS_HANDLER_RESULT_NEED_MEMORY;
-
- job_finish_and_invalidate(j, JOB_CANCELED, true);
} else {
const BusBoundProperties bps[] = {
{ "org.freedesktop.systemd1.Job", bus_job_properties, j },
@@ -114,11 +113,10 @@ static DBusHandlerResult bus_job_message_dispatch(Job *j, DBusConnection *connec
};
SELINUX_UNIT_ACCESS_CHECK(j->unit, connection, message, "status");
-
return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, bps);
}
- if (!dbus_connection_send(connection, reply, NULL))
+ if (!bus_maybe_send_reply(connection, message, reply))
return DBUS_HANDLER_RESULT_NEED_MEMORY;
return DBUS_HANDLER_RESULT_HANDLED;
@@ -187,7 +185,7 @@ static DBusHandlerResult bus_job_message_handler(DBusConnection *connection, DBu
free(introspection);
- if (!dbus_connection_send(connection, reply, NULL))
+ if (!bus_maybe_send_reply(connection, message, reply))
goto oom;
return DBUS_HANDLER_RESULT_HANDLED;
@@ -220,7 +218,7 @@ const DBusObjectPathVTable bus_job_vtable = {
};
static int job_send_message(Job *j, DBusMessage* (*new_message)(Job *j)) {
- DBusMessage *m = NULL;
+ _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
int r;
assert(j);
@@ -229,9 +227,9 @@ static int job_send_message(Job *j, DBusMessage* (*new_message)(Job *j)) {
if (bus_has_subscriber(j->manager) || j->forgot_bus_clients) {
m = new_message(j);
if (!m)
- goto oom;
+ return -ENOMEM;
+
r = bus_broadcast(j->manager, m);
- dbus_message_unref(m);
if (r < 0)
return r;
@@ -240,18 +238,19 @@ static int job_send_message(Job *j, DBusMessage* (*new_message)(Job *j)) {
* to the client(s) which created the job */
JobBusClient *cl;
assert(j->bus_client_list);
+
LIST_FOREACH(client, cl, j->bus_client_list) {
assert(cl->bus);
m = new_message(j);
if (!m)
- goto oom;
+ return -ENOMEM;
if (!dbus_message_set_destination(m, cl->name))
- goto oom;
+ return -ENOMEM;
if (!dbus_connection_send(cl->bus, m, NULL))
- goto oom;
+ return -ENOMEM;
dbus_message_unref(m);
m = NULL;
@@ -259,10 +258,6 @@ static int job_send_message(Job *j, DBusMessage* (*new_message)(Job *j)) {
}
return 0;
-oom:
- if (m)
- dbus_message_unref(m);
- return -ENOMEM;
}
static DBusMessage* new_change_signal_message(Job *j) {
diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c
index 859fa1a906..56b02a1cf5 100644
--- a/src/core/dbus-manager.c
+++ b/src/core/dbus-manager.c
@@ -36,6 +36,7 @@
#include "path-util.h"
#include "dbus-unit.h"
#include "virt.h"
+#include "env-util.h"
#define BUS_MANAGER_INTERFACE_BEGIN \
" <interface name=\"org.freedesktop.systemd1.Manager\">\n"
@@ -102,10 +103,39 @@
" <method name=\"ResetFailedUnit\">\n" \
" <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
" </method>\n" \
+ " <method name=\"SetUnitControlGroup\">\n" \
+ " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
+ " <arg name=\"group\" type=\"s\" direction=\"in\"/>\n" \
+ " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \
+ " </method>\n" \
+ " <method name=\"UnsetUnitControlGroup\">\n" \
+ " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
+ " <arg name=\"group\" type=\"s\" direction=\"in\"/>\n" \
+ " <arg name=\"mode\" type=\"s\" direction=\"in\"\n/>" \
+ " </method>\n" \
+ " <method name=\"GetUnitControlGroupAttribute\">\n" \
+ " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
+ " <arg name=\"attribute\" type=\"s\" direction=\"in\"/>\n" \
+ " <arg name=\"values\" type=\"as\" direction=\"out\"/>\n" \
+ " </method>\n" \
+ " <method name=\"SetUnitControlGroupAttribute\">\n" \
+ " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
+ " <arg name=\"attribute\" type=\"s\" direction=\"in\"/>\n" \
+ " <arg name=\"values\" type=\"as\" direction=\"in\"/>\n" \
+ " <arg name=\"mode\" type=\"s\" direction=\"in\"\n/>" \
+ " </method>\n" \
+ " <method name=\"UnsetUnitControlGroupAttributes\">\n" \
+ " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
+ " <arg name=\"attribute\" type=\"s\" direction=\"in\"/>\n" \
+ " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \
+ " </method>\n" \
" <method name=\"GetJob\">\n" \
" <arg name=\"id\" type=\"u\" direction=\"in\"/>\n" \
" <arg name=\"job\" type=\"o\" direction=\"out\"/>\n" \
" </method>\n" \
+ " <method name=\"CancelJob\">\n" \
+ " <arg name=\"id\" type=\"u\" direction=\"in\"/>\n" \
+ " </method>\n" \
" <method name=\"ClearJobs\"/>\n" \
" <method name=\"ResetFailed\"/>\n" \
" <method name=\"ListUnits\">\n" \
@@ -124,6 +154,9 @@
" <arg name=\"cleanup\" type=\"b\" direction=\"in\"/>\n" \
" <arg name=\"unit\" type=\"o\" direction=\"out\"/>\n" \
" </method>\n" \
+ " <method name=\"RemoveSnapshot\">\n" \
+ " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
+ " </method>\n" \
" <method name=\"Reload\"/>\n" \
" <method name=\"Reexecute\"/>\n" \
" <method name=\"Exit\"/>\n" \
@@ -257,8 +290,8 @@
" <property name=\"DefaultControllers\" type=\"as\" access=\"read\"/>\n" \
" <property name=\"DefaultStandardOutput\" type=\"s\" access=\"read\"/>\n" \
" <property name=\"DefaultStandardError\" type=\"s\" access=\"read\"/>\n" \
- " <property name=\"RuntimeWatchdogUSec\" type=\"s\" access=\"readwrite\"/>\n" \
- " <property name=\"ShutdownWatchdogUSec\" type=\"s\" access=\"readwrite\"/>\n" \
+ " <property name=\"RuntimeWatchdogUSec\" type=\"t\" access=\"readwrite\"/>\n" \
+ " <property name=\"ShutdownWatchdogUSec\" type=\"t\" access=\"readwrite\"/>\n" \
" <property name=\"Virtualization\" type=\"s\" access=\"read\"/>\n"
#define BUS_MANAGER_INTERFACE_END \
@@ -774,10 +807,33 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
DBUS_TYPE_INVALID))
goto oom;
+ } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "CancelJob")) {
+ uint32_t id;
+ Job *j;
+
+ if (!dbus_message_get_args(
+ message,
+ &error,
+ DBUS_TYPE_UINT32, &id,
+ DBUS_TYPE_INVALID))
+ return bus_send_error_reply(connection, message, &error, -EINVAL);
+
+ j = manager_get_job(m, id);
+ if (!j) {
+ dbus_set_error(&error, BUS_ERROR_NO_SUCH_JOB, "Job %u does not exist.", (unsigned) id);
+ return bus_send_error_reply(connection, message, &error, -ENOENT);
+ }
+
+ SELINUX_UNIT_ACCESS_CHECK(j->unit, connection, message, "stop");
+ job_finish_and_invalidate(j, JOB_CANCELED, true);
+
+ reply = dbus_message_new_method_return(message);
+ if (!reply)
+ goto oom;
+
} else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ClearJobs")) {
SELINUX_ACCESS_CHECK(connection, message, "reboot");
-
manager_clear_jobs(m);
reply = dbus_message_new_method_return(message);
@@ -819,6 +875,151 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
if (!reply)
goto oom;
+ } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "SetUnitControlGroup")) {
+ const char *name;
+ Unit *u;
+ DBusMessageIter iter;
+
+ if (!dbus_message_iter_init(message, &iter))
+ goto oom;
+
+ r = bus_iter_get_basic_and_next(&iter, DBUS_TYPE_STRING, &name, true);
+ if (r < 0)
+ return bus_send_error_reply(connection, message, NULL, r);
+
+ u = manager_get_unit(m, name);
+ if (!u) {
+ dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s is not loaded.", name);
+ return bus_send_error_reply(connection, message, &error, -ENOENT);
+ }
+
+ SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "start");
+
+ r = bus_unit_cgroup_set(u, &iter);
+ if (r < 0)
+ return bus_send_error_reply(connection, message, NULL, r);
+
+ reply = dbus_message_new_method_return(message);
+ if (!reply)
+ goto oom;
+
+ } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "UnsetUnitControlGroup")) {
+ const char *name;
+ Unit *u;
+ DBusMessageIter iter;
+
+ if (!dbus_message_iter_init(message, &iter))
+ goto oom;
+
+ r = bus_iter_get_basic_and_next(&iter, DBUS_TYPE_STRING, &name, true);
+ if (r < 0)
+ return bus_send_error_reply(connection, message, NULL, r);
+
+ u = manager_get_unit(m, name);
+ if (!u) {
+ dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s is not loaded.", name);
+ return bus_send_error_reply(connection, message, &error, -ENOENT);
+ }
+
+ SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "stop");
+
+ r = bus_unit_cgroup_unset(u, &iter);
+ if (r < 0)
+ return bus_send_error_reply(connection, message, NULL, r);
+
+ reply = dbus_message_new_method_return(message);
+ if (!reply)
+ goto oom;
+
+ } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "SetUnitControlGroupAttribute")) {
+ const char *name;
+ Unit *u;
+ DBusMessageIter iter;
+
+ if (!dbus_message_iter_init(message, &iter))
+ goto oom;
+
+ r = bus_iter_get_basic_and_next(&iter, DBUS_TYPE_STRING, &name, true);
+ if (r < 0)
+ return bus_send_error_reply(connection, message, NULL, r);
+
+ u = manager_get_unit(m, name);
+ if (!u) {
+ dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s is not loaded.", name);
+ return bus_send_error_reply(connection, message, &error, -ENOENT);
+ }
+
+ SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "start");
+
+ r = bus_unit_cgroup_attribute_set(u, &iter);
+ if (r < 0)
+ return bus_send_error_reply(connection, message, NULL, r);
+
+ reply = dbus_message_new_method_return(message);
+ if (!reply)
+ goto oom;
+
+ } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "UnsetUnitControlGroupAttribute")) {
+ const char *name;
+ Unit *u;
+ DBusMessageIter iter;
+
+ if (!dbus_message_iter_init(message, &iter))
+ goto oom;
+
+ r = bus_iter_get_basic_and_next(&iter, DBUS_TYPE_STRING, &name, true);
+ if (r < 0)
+ return bus_send_error_reply(connection, message, NULL, r);
+
+ u = manager_get_unit(m, name);
+ if (!u) {
+ dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s is not loaded.", name);
+ return bus_send_error_reply(connection, message, &error, -ENOENT);
+ }
+
+ SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "stop");
+
+ r = bus_unit_cgroup_attribute_unset(u, &iter);
+ if (r < 0)
+ return bus_send_error_reply(connection, message, NULL, r);
+
+ reply = dbus_message_new_method_return(message);
+ if (!reply)
+ goto oom;
+
+ } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "GetUnitControlGroupAttribute")) {
+ const char *name;
+ Unit *u;
+ DBusMessageIter iter;
+ _cleanup_strv_free_ char **list = NULL;
+
+ if (!dbus_message_iter_init(message, &iter))
+ goto oom;
+
+ r = bus_iter_get_basic_and_next(&iter, DBUS_TYPE_STRING, &name, true);
+ if (r < 0)
+ return bus_send_error_reply(connection, message, NULL, r);
+
+ u = manager_get_unit(m, name);
+ if (!u) {
+ dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s is not loaded.", name);
+ return bus_send_error_reply(connection, message, &error, -ENOENT);
+ }
+
+ SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "status");
+
+ r = bus_unit_cgroup_attribute_get(u, &iter, &list);
+ if (r < 0)
+ return bus_send_error_reply(connection, message, NULL, r);
+
+ reply = dbus_message_new_method_return(message);
+ if (!reply)
+ goto oom;
+
+ dbus_message_iter_init_append(reply, &iter);
+ if (bus_append_strv_iter(&iter, list) < 0)
+ goto oom;
+
} else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ListUnits")) {
DBusMessageIter iter, sub;
Iterator i;
@@ -985,11 +1186,9 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
if (!client)
goto oom;
- r = set_put(s, client);
- if (r < 0) {
- free(client);
+ r = set_consume(s, client);
+ if (r < 0)
return bus_send_error_reply(connection, message, NULL, r);
- }
reply = dbus_message_new_method_return(message);
if (!reply)
@@ -1080,6 +1279,35 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
DBUS_TYPE_INVALID))
goto oom;
+ } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "RemoveSnapshot")) {
+ const char *name;
+ Unit *u;
+
+ if (!dbus_message_get_args(
+ message,
+ &error,
+ DBUS_TYPE_STRING, &name,
+ DBUS_TYPE_INVALID))
+ return bus_send_error_reply(connection, message, &error, -EINVAL);
+
+ u = manager_get_unit(m, name);
+ if (!u) {
+ dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s does not exist.", name);
+ return bus_send_error_reply(connection, message, &error, -ENOENT);
+ }
+
+ if (u->type != UNIT_SNAPSHOT) {
+ dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s is not a snapshot.", name);
+ return bus_send_error_reply(connection, message, &error, -ENOENT);
+ }
+
+ SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "stop");
+ snapshot_remove(SNAPSHOT(u));
+
+ reply = dbus_message_new_method_return(message);
+ if (!reply)
+ goto oom;
+
} else if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect")) {
char *introspection = NULL;
FILE *f;
@@ -1251,7 +1479,7 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
} else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "SwitchRoot")) {
const char *switch_root, *switch_root_init;
char *u, *v;
- int k;
+ bool good;
SELINUX_ACCESS_CHECK(connection, message, "reboot");
@@ -1275,20 +1503,24 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
}
/* Safety check */
- if (isempty(switch_root_init))
- k = access(switch_root, F_OK);
+ if (isempty(switch_root_init)) {
+ good = path_is_os_tree(switch_root);
+ if (!good)
+ log_error("Not switching root: %s does not seem to be an OS tree. /etc/os-release is missing.", switch_root);
+ }
else {
- char *p;
+ _cleanup_free_ char *p = NULL;
p = strjoin(switch_root, "/", switch_root_init, NULL);
if (!p)
goto oom;
- k = access(p, X_OK);
- free(p);
+ good = access(p, X_OK) >= 0;
+ if (!good)
+ log_error("Not switching root: cannot execute new init %s", p);
}
- if (k < 0)
- return bus_send_error_reply(connection, message, NULL, -errno);
+ if (!good)
+ return bus_send_error_reply(connection, message, NULL, -EINVAL);
u = strdup(switch_root);
if (!u)
@@ -1315,7 +1547,8 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
m->exit_code = MANAGER_SWITCH_ROOT;
} else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "SetEnvironment")) {
- char **l = NULL, **e = NULL;
+ _cleanup_strv_free_ char **l = NULL;
+ char **e = NULL;
SELINUX_ACCESS_CHECK(connection, message, "reboot");
@@ -1324,9 +1557,10 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
goto oom;
if (r < 0)
return bus_send_error_reply(connection, message, NULL, r);
+ if (!strv_env_is_valid(l))
+ return bus_send_error_reply(connection, message, NULL, -EINVAL);
e = strv_env_merge(2, m->environment, l);
- strv_free(l);
if (!e)
goto oom;
@@ -1340,7 +1574,8 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
m->environment = e;
} else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "UnsetEnvironment")) {
- char **l = NULL, **e = NULL;
+ _cleanup_strv_free_ char **l = NULL;
+ char **e = NULL;
SELINUX_ACCESS_CHECK(connection, message, "reboot");
@@ -1349,10 +1584,10 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
goto oom;
if (r < 0)
return bus_send_error_reply(connection, message, NULL, r);
+ if (!strv_env_name_or_assignment_is_valid(l))
+ return bus_send_error_reply(connection, message, NULL, -EINVAL);
e = strv_env_delete(m->environment, 1, l);
- strv_free(l);
-
if (!e)
goto oom;
@@ -1366,7 +1601,8 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
m->environment = e;
} else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "UnsetAndSetEnvironment")) {
- char **l_set = NULL, **l_unset = NULL, **e = NULL, **f = NULL;
+ _cleanup_strv_free_ char **l_set = NULL, **l_unset = NULL, **e = NULL;
+ char **f = NULL;
DBusMessageIter iter;
SELINUX_ACCESS_CHECK(connection, message, "reboot");
@@ -1379,33 +1615,25 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
goto oom;
if (r < 0)
return bus_send_error_reply(connection, message, NULL, r);
+ if (!strv_env_name_or_assignment_is_valid(l_unset))
+ return bus_send_error_reply(connection, message, NULL, -EINVAL);
- if (!dbus_message_iter_next(&iter)) {
- strv_free(l_unset);
+ if (!dbus_message_iter_next(&iter))
return bus_send_error_reply(connection, message, NULL, -EINVAL);
- }
r = bus_parse_strv_iter(&iter, &l_set);
- if (r < 0) {
- strv_free(l_unset);
- if (r == -ENOMEM)
- goto oom;
-
+ if (r == -ENOMEM)
+ goto oom;
+ if (r < 0)
return bus_send_error_reply(connection, message, NULL, r);
- }
+ if (!strv_env_is_valid(l_set))
+ return bus_send_error_reply(connection, message, NULL, -EINVAL);
e = strv_env_delete(m->environment, 1, l_unset);
- strv_free(l_unset);
-
- if (!e) {
- strv_free(l_set);
+ if (!e)
goto oom;
- }
f = strv_env_merge(2, e, l_set);
- strv_free(l_set);
- strv_free(e);
-
if (!f)
goto oom;
@@ -1668,7 +1896,7 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
}
if (reply)
- if (!dbus_connection_send(connection, reply, NULL))
+ if (!bus_maybe_send_reply(connection, message, reply))
goto oom;
return DBUS_HANDLER_RESULT_HANDLED;
diff --git a/src/core/dbus-mount.c b/src/core/dbus-mount.c
index d81edeb807..0fcceb500d 100644
--- a/src/core/dbus-mount.c
+++ b/src/core/dbus-mount.c
@@ -40,6 +40,7 @@
BUS_EXEC_COMMAND_INTERFACE("ExecRemount") \
BUS_EXEC_CONTEXT_INTERFACE \
BUS_KILL_CONTEXT_INTERFACE \
+ BUS_UNIT_CGROUP_INTERFACE \
" <property name=\"ControlPID\" type=\"u\" access=\"read\"/>\n" \
" <property name=\"DirectoryMode\" type=\"u\" access=\"read\"/>\n" \
" <property name=\"Result\" type=\"s\" access=\"read\"/>\n" \
@@ -159,6 +160,7 @@ DBusHandlerResult bus_mount_message_handler(Unit *u, DBusConnection *c, DBusMess
{ "org.freedesktop.systemd1.Mount", bus_mount_properties, m },
{ "org.freedesktop.systemd1.Mount", bus_exec_context_properties, &m->exec_context },
{ "org.freedesktop.systemd1.Mount", bus_kill_context_properties, &m->kill_context },
+ { "org.freedesktop.systemd1.Mount", bus_unit_cgroup_properties, u },
{ NULL, }
};
diff --git a/src/core/dbus-path.c b/src/core/dbus-path.c
index f7fed1754d..1e62083d9b 100644
--- a/src/core/dbus-path.c
+++ b/src/core/dbus-path.c
@@ -84,15 +84,15 @@ static int bus_path_append_paths(DBusMessageIter *i, const char *property, void
}
static int bus_path_append_unit(DBusMessageIter *i, const char *property, void *data) {
- Unit *u = data;
- Path *p = PATH(u);
+ Unit *u = data, *trigger;
const char *t;
assert(i);
assert(property);
assert(u);
- t = UNIT_DEREF(p->unit) ? UNIT_DEREF(p->unit)->id : "";
+ trigger = UNIT_TRIGGER(u);
+ t = trigger ? trigger->id : "";
return dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &t) ? 0 : -ENOMEM;
}
diff --git a/src/core/dbus-service.c b/src/core/dbus-service.c
index d99058dd46..e06a5dce97 100644
--- a/src/core/dbus-service.c
+++ b/src/core/dbus-service.c
@@ -50,6 +50,7 @@
BUS_EXEC_COMMAND_INTERFACE("ExecStopPost") \
BUS_EXEC_CONTEXT_INTERFACE \
BUS_KILL_CONTEXT_INTERFACE \
+ BUS_UNIT_CGROUP_INTERFACE \
" <property name=\"PermissionsStartOnly\" type=\"b\" access=\"read\"/>\n" \
" <property name=\"RootDirectoryStartOnly\" type=\"b\" access=\"read\"/>\n" \
" <property name=\"RemainAfterExit\" type=\"b\" access=\"read\"/>\n" \
@@ -152,6 +153,7 @@ DBusHandlerResult bus_service_message_handler(Unit *u, DBusConnection *connectio
{ "org.freedesktop.systemd1.Service", bus_exec_context_properties, &s->exec_context },
{ "org.freedesktop.systemd1.Service", bus_kill_context_properties, &s->kill_context },
{ "org.freedesktop.systemd1.Service", bus_exec_main_status_properties, &s->main_exec_status },
+ { "org.freedesktop.systemd1.Service", bus_unit_cgroup_properties, u },
{ NULL, }
};
diff --git a/src/core/dbus-snapshot.c b/src/core/dbus-snapshot.c
index 435c6df39c..2ae8574f59 100644
--- a/src/core/dbus-snapshot.c
+++ b/src/core/dbus-snapshot.c
@@ -54,19 +54,16 @@ static const BusProperty bus_snapshot_properties[] = {
DBusHandlerResult bus_snapshot_message_handler(Unit *u, DBusConnection *c, DBusMessage *message) {
Snapshot *s = SNAPSHOT(u);
_cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
- DBusError error;
-
- dbus_error_init(&error);
if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Snapshot", "Remove")) {
SELINUX_UNIT_ACCESS_CHECK(u, c, message, "stop");
- snapshot_remove(SNAPSHOT(u));
-
reply = dbus_message_new_method_return(message);
if (!reply)
- goto oom;
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
+
+ snapshot_remove(SNAPSHOT(u));
} else {
const BusBoundProperties bps[] = {
@@ -80,15 +77,8 @@ DBusHandlerResult bus_snapshot_message_handler(Unit *u, DBusConnection *c, DBusM
return bus_default_message_handler(c, message, INTROSPECTION, INTERFACES_LIST, bps);
}
- if (reply) {
- if (!dbus_connection_send(c, reply, NULL))
- goto oom;
- }
+ if (!bus_maybe_send_reply(c, message, reply))
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
return DBUS_HANDLER_RESULT_HANDLED;
-
-oom:
- dbus_error_free(&error);
-
- return DBUS_HANDLER_RESULT_NEED_MEMORY;
}
diff --git a/src/core/dbus-socket.c b/src/core/dbus-socket.c
index 095a031612..77d98ea0fd 100644
--- a/src/core/dbus-socket.c
+++ b/src/core/dbus-socket.c
@@ -39,6 +39,7 @@
BUS_EXEC_COMMAND_INTERFACE("ExecStopPost") \
BUS_EXEC_CONTEXT_INTERFACE \
BUS_KILL_CONTEXT_INTERFACE \
+ BUS_UNIT_CGROUP_INTERFACE \
" <property name=\"ControlPID\" type=\"u\" access=\"read\"/>\n" \
" <property name=\"BindToDevice\" type=\"s\" access=\"read\"/>\n" \
" <property name=\"DirectoryMode\" type=\"u\" access=\"read\"/>\n" \
@@ -62,6 +63,7 @@
" <property name=\"NConnections\" type=\"u\" access=\"read\"/>\n" \
" <property name=\"MessageQueueMaxMessages\" type=\"x\" access=\"read\"/>\n" \
" <property name=\"MessageQueueMessageSize\" type=\"x\" access=\"read\"/>\n" \
+ " <property name=\"Listen\" type=\"a(ss)\" access=\"read\"/>\n" \
" <property name=\"Result\" type=\"s\" access=\"read\"/>\n" \
" <property name=\"SmackLabel\" type=\"s\" access=\"read\"/>\n" \
" <property name=\"SmackLabelIPIn\" type=\"s\" access=\"read\"/>\n" \
@@ -97,6 +99,66 @@ const char bus_socket_invalidating_properties[] =
static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_socket_append_bind_ipv6_only, socket_address_bind_ipv6_only, SocketAddressBindIPv6Only);
static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_socket_append_socket_result, socket_result, SocketResult);
+static int bus_socket_append_listen(DBusMessageIter *i, const char *property, void *data) {
+
+ Socket *s = SOCKET(data);
+ SocketPort *p;
+ DBusMessageIter array, stru;
+
+ assert(data);
+ assert(property);
+ assert(s);
+
+ if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "(ss)", &array))
+ return log_oom();
+
+ LIST_FOREACH(port, p, s->ports) {
+ const char *type = socket_port_type_to_string(p);
+ _cleanup_free_ char *address = NULL;
+ const char *a;
+
+ if (!dbus_message_iter_open_container(&array, DBUS_TYPE_STRUCT, NULL, &stru))
+ return log_oom();
+
+ if (!dbus_message_iter_append_basic(&stru, DBUS_TYPE_STRING, &type))
+ return log_oom();
+
+ switch (p->type) {
+ case SOCKET_SOCKET: {
+ int r;
+
+ r = socket_address_print(&p->address, &address);
+ if (r) {
+ log_error("socket_address_print failed: %s", strerror(-r));
+ return r;
+ }
+ a = address;
+ break;
+ }
+
+ case SOCKET_SPECIAL:
+ case SOCKET_MQUEUE:
+ case SOCKET_FIFO:
+ a = p->path;
+ break;
+
+ default:
+ a = type;
+ }
+
+ if (!dbus_message_iter_append_basic(&stru, DBUS_TYPE_STRING, &a))
+ return -ENOMEM;
+
+ if (!dbus_message_iter_close_container(&array, &stru))
+ return -ENOMEM;
+ }
+
+ if (!dbus_message_iter_close_container(i, &array))
+ return -ENOMEM;
+
+ return 0;
+}
+
static const BusProperty bus_socket_properties[] = {
{ "BindIPv6Only", bus_socket_append_bind_ipv6_only, "s", offsetof(Socket, bind_ipv6_only) },
{ "Backlog", bus_property_append_unsigned, "u", offsetof(Socket, backlog) },
@@ -122,6 +184,7 @@ static const BusProperty bus_socket_properties[] = {
{ "Broadcast", bus_property_append_bool, "b", offsetof(Socket, broadcast) },
{ "PassCredentials",bus_property_append_bool, "b", offsetof(Socket, pass_cred) },
{ "PassSecurity", bus_property_append_bool, "b", offsetof(Socket, pass_sec) },
+ { "Listen", bus_socket_append_listen, "a(ss)", 0, },
{ "Mark", bus_property_append_int, "i", offsetof(Socket, mark) },
{ "MaxConnections", bus_property_append_unsigned, "u", offsetof(Socket, max_connections) },
{ "NConnections", bus_property_append_unsigned, "u", offsetof(Socket, n_connections) },
@@ -142,6 +205,7 @@ DBusHandlerResult bus_socket_message_handler(Unit *u, DBusConnection *c, DBusMes
{ "org.freedesktop.systemd1.Socket", bus_socket_properties, s },
{ "org.freedesktop.systemd1.Socket", bus_exec_context_properties, &s->exec_context },
{ "org.freedesktop.systemd1.Socket", bus_kill_context_properties, &s->kill_context },
+ { "org.freedesktop.systemd1.Socket", bus_unit_properties, u },
{ NULL, }
};
diff --git a/src/core/dbus-swap.c b/src/core/dbus-swap.c
index 67ea0f24fe..2e99fba7db 100644
--- a/src/core/dbus-swap.c
+++ b/src/core/dbus-swap.c
@@ -38,6 +38,7 @@
BUS_EXEC_COMMAND_INTERFACE("ExecDeactivate") \
BUS_EXEC_CONTEXT_INTERFACE \
BUS_KILL_CONTEXT_INTERFACE \
+ BUS_UNIT_CGROUP_INTERFACE \
" <property name=\"ControlPID\" type=\"u\" access=\"read\"/>\n" \
" <property name=\"Result\" type=\"s\" access=\"read\"/>\n" \
" </interface>\n"
@@ -106,6 +107,7 @@ DBusHandlerResult bus_swap_message_handler(Unit *u, DBusConnection *c, DBusMessa
{ "org.freedesktop.systemd1.Swap", bus_swap_properties, s },
{ "org.freedesktop.systemd1.Swap", bus_exec_context_properties, &s->exec_context },
{ "org.freedesktop.systemd1.Swap", bus_kill_context_properties, &s->kill_context },
+ { "org.freedesktop.systemd1.Swap", bus_unit_cgroup_properties, u },
{ NULL, }
};
diff --git a/src/core/dbus-timer.c b/src/core/dbus-timer.c
index 11d18cbd83..4082f7f9b9 100644
--- a/src/core/dbus-timer.c
+++ b/src/core/dbus-timer.c
@@ -30,8 +30,10 @@
#define BUS_TIMER_INTERFACE \
" <interface name=\"org.freedesktop.systemd1.Timer\">\n" \
" <property name=\"Unit\" type=\"s\" access=\"read\"/>\n" \
- " <property name=\"Timers\" type=\"a(stt)\" access=\"read\"/>\n" \
- " <property name=\"NextElapseUSec\" type=\"t\" access=\"read\"/>\n" \
+ " <property name=\"TimersMonotonic\" type=\"a(stt)\" access=\"read\"/>\n" \
+ " <property name=\"TimersCalendar\" type=\"a(sst)\" access=\"read\"/>\n" \
+ " <property name=\"NextElapseUSecRealtime\" type=\"t\" access=\"read\"/>\n" \
+ " <property name=\"NextElapseUSecMonotonic\" type=\"t\" access=\"read\"/>\n" \
" <property name=\"Result\" type=\"s\" access=\"read\"/>\n" \
" </interface>\n"
@@ -52,11 +54,13 @@
const char bus_timer_interface[] _introspect_("Timer") = BUS_TIMER_INTERFACE;
const char bus_timer_invalidating_properties[] =
- "Timers\0"
- "NextElapseUSec\0"
+ "TimersMonotonic\0"
+ "TimersRealtime\0"
+ "NextElapseUSecRealtime\0"
+ "NextElapseUSecMonotonic\0"
"Result\0";
-static int bus_timer_append_timers(DBusMessageIter *i, const char *property, void *data) {
+static int bus_timer_append_monotonic_timers(DBusMessageIter *i, const char *property, void *data) {
Timer *p = data;
DBusMessageIter sub, sub2;
TimerValue *k;
@@ -69,11 +73,14 @@ static int bus_timer_append_timers(DBusMessageIter *i, const char *property, voi
return -ENOMEM;
LIST_FOREACH(value, k, p->values) {
- char *buf;
+ _cleanup_free_ char *buf = NULL;
const char *t;
size_t l;
bool b;
+ if (k->base == TIMER_CALENDAR)
+ continue;
+
t = timer_base_to_string(k->base);
assert(endswith(t, "Sec"));
@@ -92,7 +99,48 @@ static int bus_timer_append_timers(DBusMessageIter *i, const char *property, voi
dbus_message_iter_append_basic(&sub2, DBUS_TYPE_UINT64, &k->next_elapse) &&
dbus_message_iter_close_container(&sub, &sub2);
- free(buf);
+ if (!b)
+ return -ENOMEM;
+ }
+
+ if (!dbus_message_iter_close_container(i, &sub))
+ return -ENOMEM;
+
+ return 0;
+}
+
+static int bus_timer_append_calendar_timers(DBusMessageIter *i, const char *property, void *data) {
+ Timer *p = data;
+ DBusMessageIter sub, sub2;
+ TimerValue *k;
+
+ assert(i);
+ assert(property);
+ assert(p);
+
+ if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "(sst)", &sub))
+ return -ENOMEM;
+
+ LIST_FOREACH(value, k, p->values) {
+ _cleanup_free_ char *buf = NULL;
+ const char *t;
+ bool b;
+ int j;
+
+ if (k->base != TIMER_CALENDAR)
+ continue;
+
+ t = timer_base_to_string(k->base);
+ j = calendar_spec_to_string(k->calendar_spec, &buf);
+ if (j < 0)
+ return j;
+
+ b = dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) &&
+ dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &t) &&
+ dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &buf) &&
+ dbus_message_iter_append_basic(&sub2, DBUS_TYPE_UINT64, &k->next_elapse) &&
+ dbus_message_iter_close_container(&sub, &sub2);
+
if (!b)
return -ENOMEM;
}
@@ -104,15 +152,15 @@ static int bus_timer_append_timers(DBusMessageIter *i, const char *property, voi
}
static int bus_timer_append_unit(DBusMessageIter *i, const char *property, void *data) {
- Unit *u = data;
- Timer *timer = TIMER(u);
+ Unit *u = data, *trigger;
const char *t;
assert(i);
assert(property);
assert(u);
- t = UNIT_DEREF(timer->unit) ? UNIT_DEREF(timer->unit)->id : "";
+ trigger = UNIT_TRIGGER(u);
+ t = trigger ? trigger->id : "";
return dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &t) ? 0 : -ENOMEM;
}
@@ -120,11 +168,12 @@ static int bus_timer_append_unit(DBusMessageIter *i, const char *property, void
static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_timer_append_timer_result, timer_result, TimerResult);
static const BusProperty bus_timer_properties[] = {
- { "Unit", bus_timer_append_unit, "s", 0 },
- { "Timers", bus_timer_append_timers, "a(stt)", 0 },
- { "NextElapseUSec", bus_property_append_usec, "t", offsetof(Timer, next_elapse_monotonic) },
- { "NextElapseUSecRealtime", bus_property_append_usec, "t", offsetof(Timer, next_elapse_realtime) },
- { "Result", bus_timer_append_timer_result,"s", offsetof(Timer, result) },
+ { "Unit", bus_timer_append_unit, "s", 0 },
+ { "TimersMonotonic", bus_timer_append_monotonic_timers, "a(stt)", 0 },
+ { "TimersCalendar", bus_timer_append_calendar_timers, "a(sst)", 0 },
+ { "NextElapseUSecMonotonic", bus_property_append_usec, "t", offsetof(Timer, next_elapse_monotonic) },
+ { "NextElapseUSecRealtime", bus_property_append_usec, "t", offsetof(Timer, next_elapse_realtime) },
+ { "Result", bus_timer_append_timer_result, "s", offsetof(Timer, result) },
{ NULL, }
};
diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c
index 8433a720b2..575f8eb36a 100644
--- a/src/core/dbus-unit.c
+++ b/src/core/dbus-unit.c
@@ -27,6 +27,10 @@
#include "bus-errors.h"
#include "dbus-common.h"
#include "selinux-access.h"
+#include "cgroup-util.h"
+#include "strv.h"
+#include "path-util.h"
+#include "fileio.h"
const char bus_unit_interface[] _introspect_("Unit") = BUS_UNIT_INTERFACE;
@@ -310,7 +314,7 @@ static int bus_unit_append_cgroups(DBusMessageIter *i, const char *property, voi
return -ENOMEM;
LIST_FOREACH(by_unit, cgb, u->cgroup_bondings) {
- char _cleanup_free_ *t = NULL;
+ _cleanup_free_ char *t = NULL;
bool success;
t = cgroup_bonding_to_string(cgb);
@@ -337,11 +341,11 @@ static int bus_unit_append_cgroup_attrs(DBusMessageIter *i, const char *property
return -ENOMEM;
LIST_FOREACH(by_unit, a, u->cgroup_attributes) {
- char _cleanup_free_ *v = NULL;
+ _cleanup_free_ char *v = NULL;
bool success;
- if (a->map_callback)
- a->map_callback(a->controller, a->name, a->value, &v);
+ if (a->semantics && a->semantics->map_write)
+ a->semantics->map_write(a->semantics, a->value, &v);
success =
dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) &&
@@ -468,6 +472,90 @@ static DBusHandlerResult bus_unit_message_dispatch(Unit *u, DBusConnection *conn
if (!reply)
goto oom;
+ } else if (streq_ptr(dbus_message_get_member(message), "SetControlGroup")) {
+ DBusMessageIter iter;
+
+ SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "start");
+
+ if (!dbus_message_iter_init(message, &iter))
+ goto oom;
+
+ r = bus_unit_cgroup_set(u, &iter);
+ if (r < 0)
+ return bus_send_error_reply(connection, message, NULL, r);
+
+ reply = dbus_message_new_method_return(message);
+ if (!reply)
+ goto oom;
+
+ } else if (streq_ptr(dbus_message_get_member(message), "UnsetControlGroup")) {
+ DBusMessageIter iter;
+
+ SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "stop");
+
+ if (!dbus_message_iter_init(message, &iter))
+ goto oom;
+
+ r = bus_unit_cgroup_unset(u, &iter);
+ if (r < 0)
+ return bus_send_error_reply(connection, message, NULL, r);
+
+ reply = dbus_message_new_method_return(message);
+ if (!reply)
+ goto oom;
+ } else if (streq_ptr(dbus_message_get_member(message), "GetControlGroupAttribute")) {
+ DBusMessageIter iter;
+ _cleanup_strv_free_ char **list = NULL;
+
+ SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "status");
+
+ if (!dbus_message_iter_init(message, &iter))
+ goto oom;
+
+ r = bus_unit_cgroup_attribute_get(u, &iter, &list);
+ if (r < 0)
+ return bus_send_error_reply(connection, message, NULL, r);
+
+ reply = dbus_message_new_method_return(message);
+ if (!reply)
+ goto oom;
+
+ dbus_message_iter_init_append(reply, &iter);
+ if (bus_append_strv_iter(&iter, list) < 0)
+ goto oom;
+
+ } else if (streq_ptr(dbus_message_get_member(message), "SetControlGroupAttribute")) {
+ DBusMessageIter iter;
+
+ SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "start");
+
+ if (!dbus_message_iter_init(message, &iter))
+ goto oom;
+
+ r = bus_unit_cgroup_attribute_set(u, &iter);
+ if (r < 0)
+ return bus_send_error_reply(connection, message, NULL, r);
+
+ reply = dbus_message_new_method_return(message);
+ if (!reply)
+ goto oom;
+
+ } else if (streq_ptr(dbus_message_get_member(message), "UnsetControlGroupAttribute")) {
+ DBusMessageIter iter;
+
+ SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "stop");
+
+ if (!dbus_message_iter_init(message, &iter))
+ goto oom;
+
+ r = bus_unit_cgroup_attribute_unset(u, &iter);
+ if (r < 0)
+ return bus_send_error_reply(connection, message, NULL, r);
+
+ reply = dbus_message_new_method_return(message);
+ if (!reply)
+ goto oom;
+
} else if (UNIT_VTABLE(u)->bus_message_handler)
return UNIT_VTABLE(u)->bus_message_handler(u, connection, message);
else
@@ -494,7 +582,7 @@ static DBusHandlerResult bus_unit_message_dispatch(Unit *u, DBusConnection *conn
}
if (reply)
- if (!dbus_connection_send(connection, reply, NULL))
+ if (!bus_maybe_send_reply(connection, message, reply))
goto oom;
return DBUS_HANDLER_RESULT_HANDLED;
@@ -585,7 +673,7 @@ static DBusHandlerResult bus_unit_message_handler(DBusConnection *connection, DB
free(introspection);
- if (!dbus_connection_send(connection, reply, NULL))
+ if (!bus_maybe_send_reply(connection, message, reply))
goto oom;
return DBUS_HANDLER_RESULT_HANDLED;
@@ -798,7 +886,7 @@ DBusHandlerResult bus_unit_queue_job(
DBUS_TYPE_INVALID))
goto oom;
- if (!dbus_connection_send(connection, reply, NULL))
+ if (!bus_maybe_send_reply(connection, message, reply))
goto oom;
return DBUS_HANDLER_RESULT_HANDLED;
@@ -809,6 +897,360 @@ oom:
return DBUS_HANDLER_RESULT_NEED_MEMORY;
}
+static int parse_mode(DBusMessageIter *iter, bool *runtime, bool next) {
+ const char *mode;
+ int r;
+
+ assert(iter);
+ assert(runtime);
+
+ r = bus_iter_get_basic_and_next(iter, DBUS_TYPE_STRING, &mode, next);
+ if (r < 0)
+ return r;
+
+ if (streq(mode, "runtime"))
+ *runtime = true;
+ else if (streq(mode, "persistent"))
+ *runtime = false;
+ else
+ return -EINVAL;
+
+ return 0;
+}
+
+int bus_unit_cgroup_set(Unit *u, DBusMessageIter *iter) {
+ _cleanup_free_ char *controller = NULL, *old_path = NULL, *new_path = NULL, *contents = NULL;
+ const char *name;
+ CGroupBonding *b;
+ bool runtime;
+ int r;
+
+ assert(u);
+ assert(iter);
+
+ if (!unit_get_exec_context(u))
+ return -EINVAL;
+
+ r = bus_iter_get_basic_and_next(iter, DBUS_TYPE_STRING, &name, true);
+ if (r < 0)
+ return r;
+
+ r = parse_mode(iter, &runtime, false);
+ if (r < 0)
+ return r;
+
+ r = cg_split_spec(name, &controller, &new_path);
+ if (r < 0)
+ return r;
+
+ if (!new_path) {
+ new_path = unit_default_cgroup_path(u);
+ if (!new_path)
+ return -ENOMEM;
+ }
+
+ if (!controller || streq(controller, SYSTEMD_CGROUP_CONTROLLER))
+ return -EINVAL;
+
+ b = cgroup_bonding_find_list(u->cgroup_bondings, controller);
+ if (b) {
+ if (streq(b->path, new_path))
+ return 0;
+
+ if (b->essential)
+ return -EINVAL;
+
+ old_path = strdup(b->path);
+ if (!old_path)
+ return -ENOMEM;
+ }
+
+ r = unit_add_cgroup_from_text(u, name, true, &b);
+ if (r < 0)
+ return r;
+ if (r > 0) {
+ CGroupAttribute *a;
+
+ /* Try to move things to the new place, and clean up the old place */
+ cgroup_bonding_realize(b);
+ cgroup_bonding_migrate(b, u->cgroup_bondings);
+
+ if (old_path)
+ cg_trim(controller, old_path, true);
+
+ /* Apply the attributes to the new group */
+ LIST_FOREACH(by_unit, a, u->cgroup_attributes)
+ if (streq(a->controller, controller))
+ cgroup_attribute_apply(a, b);
+ }
+
+ contents = strjoin("[", UNIT_VTABLE(u)->exec_section, "]\n"
+ "ControlGroup=", name, "\n", NULL);
+ if (!contents)
+ return -ENOMEM;
+
+ return unit_write_drop_in(u, runtime, controller, contents);
+}
+
+int bus_unit_cgroup_unset(Unit *u, DBusMessageIter *iter) {
+ _cleanup_free_ char *controller = NULL, *path = NULL, *target = NULL;
+ const char *name;
+ CGroupAttribute *a, *n;
+ CGroupBonding *b;
+ bool runtime;
+ int r;
+
+ assert(u);
+ assert(iter);
+
+ if (!unit_get_exec_context(u))
+ return -EINVAL;
+
+ r = bus_iter_get_basic_and_next(iter, DBUS_TYPE_STRING, &name, true);
+ if (r < 0)
+ return r;
+
+ r = parse_mode(iter, &runtime, false);
+ if (r < 0)
+ return r;
+
+ r = cg_split_spec(name, &controller, &path);
+ if (r < 0)
+ return r;
+
+ if (!controller || streq(controller, SYSTEMD_CGROUP_CONTROLLER))
+ return -EINVAL;
+
+ b = cgroup_bonding_find_list(u->cgroup_bondings, controller);
+ if (!b)
+ return -ENOENT;
+
+ if (path && !path_equal(path, b->path))
+ return -ENOENT;
+
+ if (b->essential)
+ return -EINVAL;
+
+ unit_remove_drop_in(u, runtime, controller);
+
+ /* Try to migrate the old group away */
+ if (cg_pid_get_path(controller, 0, &target) >= 0)
+ cgroup_bonding_migrate_to(u->cgroup_bondings, target, false);
+
+ cgroup_bonding_free(b, true);
+
+ /* Drop all attributes of this controller */
+ LIST_FOREACH_SAFE(by_unit, a, n, u->cgroup_attributes) {
+ if (!streq(a->controller, controller))
+ continue;
+
+ unit_remove_drop_in(u, runtime, a->name);
+ cgroup_attribute_free(a);
+ }
+
+ return 0;
+}
+
+int bus_unit_cgroup_attribute_get(Unit *u, DBusMessageIter *iter, char ***_result) {
+ _cleanup_free_ char *controller = NULL;
+ CGroupAttribute *a;
+ CGroupBonding *b;
+ const char *name;
+ char **l = NULL;
+ int r;
+
+ assert(u);
+ assert(iter);
+ assert(_result);
+
+ if (!unit_get_exec_context(u))
+ return -EINVAL;
+
+ r = bus_iter_get_basic_and_next(iter, DBUS_TYPE_STRING, &name, false);
+ if (r < 0)
+ return r;
+
+ r = cg_controller_from_attr(name, &controller);
+ if (r < 0)
+ return r;
+
+ /* First attempt, read the value from the kernel */
+ b = cgroup_bonding_find_list(u->cgroup_bondings, controller);
+ if (b) {
+ _cleanup_free_ char *p = NULL, *v = NULL;
+
+ r = cg_get_path(b->controller, b->path, name, &p);
+ if (r < 0)
+ return r;
+
+ r = read_full_file(p, &v, NULL);
+ if (r >= 0) {
+ /* Split on new lines */
+ l = strv_split_newlines(v);
+ if (!l)
+ return -ENOMEM;
+
+ *_result = l;
+ return 0;
+
+ }
+ }
+
+ /* If that didn't work, read our cached value */
+ LIST_FOREACH(by_unit, a, u->cgroup_attributes) {
+
+ if (!cgroup_attribute_matches(a, controller, name))
+ continue;
+
+ r = strv_extend(&l, a->value);
+ if (r < 0) {
+ strv_free(l);
+ return r;
+ }
+ }
+
+ if (!l)
+ return -ENOENT;
+
+ *_result = l;
+ return 0;
+}
+
+static int update_attribute_drop_in(Unit *u, bool runtime, const char *name) {
+ _cleanup_free_ char *buf = NULL;
+ CGroupAttribute *a;
+
+ assert(u);
+ assert(name);
+
+ LIST_FOREACH(by_unit, a, u->cgroup_attributes) {
+ if (!cgroup_attribute_matches(a, NULL, name))
+ continue;
+
+ if (!buf) {
+ buf = strjoin("[", UNIT_VTABLE(u)->exec_section, "]\n"
+ "ControlGroupAttribute=", a->name, " ", a->value, "\n", NULL);
+
+ if (!buf)
+ return -ENOMEM;
+ } else {
+ char *b;
+
+ b = strjoin(buf,
+ "ControlGroupAttribute=", a->name, " ", a->value, "\n", NULL);
+
+ if (!b)
+ return -ENOMEM;
+
+ free(buf);
+ buf = b;
+ }
+ }
+
+ if (buf)
+ return unit_write_drop_in(u, runtime, name, buf);
+ else
+ return unit_remove_drop_in(u, runtime, name);
+}
+
+int bus_unit_cgroup_attribute_set(Unit *u, DBusMessageIter *iter) {
+ _cleanup_strv_free_ char **l = NULL;
+ int r;
+ bool runtime = false;
+ char **value;
+ const char *name;
+
+ assert(u);
+ assert(iter);
+
+ if (!unit_get_exec_context(u))
+ return -EINVAL;
+
+ r = bus_iter_get_basic_and_next(iter, DBUS_TYPE_STRING, &name, true);
+ if (r < 0)
+ return r;
+
+ r = bus_parse_strv_iter(iter, &l);
+ if (r < 0)
+ return r;
+
+ if (!dbus_message_iter_next(iter))
+ return -EINVAL;
+
+ r = parse_mode(iter, &runtime, false);
+ if (r < 0)
+ return r;
+
+ STRV_FOREACH(value, l) {
+ _cleanup_free_ char *v = NULL;
+ CGroupAttribute *a;
+ const CGroupSemantics *s;
+
+ r = cgroup_semantics_find(NULL, name, *value, &v, &s);
+ if (r < 0)
+ return r;
+
+ if (s && !s->multiple && l[1])
+ return -EINVAL;
+
+ r = unit_add_cgroup_attribute(u, s, NULL, name, v ? v : *value, &a);
+ if (r < 0)
+ return r;
+
+ if (r > 0) {
+ CGroupBonding *b;
+
+ b = cgroup_bonding_find_list(u->cgroup_bondings, a->controller);
+ if (!b) {
+ /* Doesn't exist yet? Then let's add it */
+ r = unit_add_cgroup_from_text(u, a->controller, false, &b);
+ if (r < 0)
+ return r;
+
+ if (r > 0) {
+ cgroup_bonding_realize(b);
+ cgroup_bonding_migrate(b, u->cgroup_bondings);
+ }
+ }
+
+ /* Make it count */
+ cgroup_attribute_apply(a, u->cgroup_bondings);
+ }
+
+ }
+
+ r = update_attribute_drop_in(u, runtime, name);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+int bus_unit_cgroup_attribute_unset(Unit *u, DBusMessageIter *iter) {
+ const char *name;
+ bool runtime;
+ int r;
+
+ assert(u);
+ assert(iter);
+
+ if (!unit_get_exec_context(u))
+ return -EINVAL;
+
+ r = bus_iter_get_basic_and_next(iter, DBUS_TYPE_STRING, &name, true);
+ if (r < 0)
+ return r;
+
+ r = parse_mode(iter, &runtime, false);
+ if (r < 0)
+ return r;
+
+ cgroup_attribute_free_some(u->cgroup_attributes, NULL, name);
+ update_attribute_drop_in(u, runtime, name);
+
+ return 0;
+}
+
const BusProperty bus_unit_properties[] = {
{ "Id", bus_property_append_string, "s", offsetof(Unit, id), true },
{ "Names", bus_unit_append_names, "as", 0 },
@@ -842,6 +1284,7 @@ const BusProperty bus_unit_properties[] = {
{ "SubState", bus_unit_append_sub_state, "s", 0 },
{ "FragmentPath", bus_property_append_string, "s", offsetof(Unit, fragment_path), true },
{ "SourcePath", bus_property_append_string, "s", offsetof(Unit, source_path), true },
+ { "DropInPaths", bus_property_append_strv, "as", offsetof(Unit, dropin_paths), true },
{ "UnitFileState", bus_unit_append_file_state, "s", 0 },
{ "InactiveExitTimestamp",bus_property_append_usec, "t", offsetof(Unit, inactive_exit_timestamp.realtime) },
{ "InactiveExitTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, inactive_exit_timestamp.monotonic) },
@@ -864,9 +1307,6 @@ const BusProperty bus_unit_properties[] = {
{ "OnFailureIsolate", bus_property_append_bool, "b", offsetof(Unit, on_failure_isolate) },
{ "IgnoreOnIsolate", bus_property_append_bool, "b", offsetof(Unit, ignore_on_isolate) },
{ "IgnoreOnSnapshot", bus_property_append_bool, "b", offsetof(Unit, ignore_on_snapshot) },
- { "DefaultControlGroup", bus_unit_append_default_cgroup, "s", 0 },
- { "ControlGroup", bus_unit_append_cgroups, "as", 0 },
- { "ControlGroupAttributes", bus_unit_append_cgroup_attrs,"a(sss)", 0 },
{ "NeedDaemonReload", bus_unit_append_need_daemon_reload, "b", 0 },
{ "JobTimeoutUSec", bus_property_append_usec, "t", offsetof(Unit, job_timeout) },
{ "ConditionTimestamp", bus_property_append_usec, "t", offsetof(Unit, condition_timestamp.realtime) },
@@ -875,3 +1315,10 @@ const BusProperty bus_unit_properties[] = {
{ "LoadError", bus_unit_append_load_error, "(ss)", 0 },
{ NULL, }
};
+
+const BusProperty bus_unit_cgroup_properties[] = {
+ { "DefaultControlGroup", bus_unit_append_default_cgroup, "s", 0 },
+ { "ControlGroups", bus_unit_append_cgroups, "as", 0 },
+ { "ControlGroupAttributes", bus_unit_append_cgroup_attrs, "a(sss)", 0 },
+ { NULL, }
+};
diff --git a/src/core/dbus-unit.h b/src/core/dbus-unit.h
index ac6785a949..acd1ddbe78 100644
--- a/src/core/dbus-unit.h
+++ b/src/core/dbus-unit.h
@@ -69,11 +69,13 @@
" <property name=\"Requisite\" type=\"as\" access=\"read\"/>\n" \
" <property name=\"RequisiteOverridable\" type=\"as\" access=\"read\"/>\n" \
" <property name=\"Wants\" type=\"as\" access=\"read\"/>\n" \
- " <property name=\"BindsTo\" type=\"as\" access=\"read\"/>\n" \
+ " <property name=\"BindsTo\" type=\"as\" access=\"read\"/>\n" \
+ " <property name=\"PartOf\" type=\"as\" access=\"read\"/>\n" \
" <property name=\"RequiredBy\" type=\"as\" access=\"read\"/>\n" \
" <property name=\"RequiredByOverridable\" type=\"as\" access=\"read\"/>\n" \
" <property name=\"WantedBy\" type=\"as\" access=\"read\"/>\n" \
" <property name=\"BoundBy\" type=\"as\" access=\"read\"/>\n" \
+ " <property name=\"ConsistsOf\" type=\"as\" access=\"read\"/>\n" \
" <property name=\"Conflicts\" type=\"as\" access=\"read\"/>\n" \
" <property name=\"ConflictedBy\" type=\"as\" access=\"read\"/>\n" \
" <property name=\"Before\" type=\"as\" access=\"read\"/>\n" \
@@ -86,6 +88,7 @@
" <property name=\"RequiresMountsFor\" type=\"as\" access=\"read\"/>\n" \
" <property name=\"Description\" type=\"s\" access=\"read\"/>\n" \
" <property name=\"SourcePath\" type=\"s\" access=\"read\"/>\n" \
+ " <property name=\"DropInPaths\" type=\"as\" access=\"read\"/>\n" \
" <property name=\"Documentation\" type=\"as\" access=\"read\"/>\n" \
" <property name=\"LoadState\" type=\"s\" access=\"read\"/>\n" \
" <property name=\"ActiveState\" type=\"s\" access=\"read\"/>\n" \
@@ -113,9 +116,6 @@
" <property name=\"OnFailureIsolate\" type=\"b\" access=\"read\"/>\n" \
" <property name=\"IgnoreOnIsolate\" type=\"b\" access=\"read\"/>\n" \
" <property name=\"IgnoreOnSnapshot\" type=\"b\" access=\"read\"/>\n" \
- " <property name=\"DefaultControlGroup\" type=\"s\" access=\"read\"/>\n" \
- " <property name=\"ControlGroup\" type=\"as\" access=\"read\"/>\n" \
- " <property name=\"ControlGroupAttributes\" type=\"a(sss)\" access=\"read\"/>\n" \
" <property name=\"NeedDaemonReload\" type=\"b\" access=\"read\"/>\n" \
" <property name=\"JobTimeoutUSec\" type=\"t\" access=\"read\"/>\n" \
" <property name=\"ConditionTimestamp\" type=\"t\" access=\"read\"/>\n" \
@@ -124,16 +124,42 @@
" <property name=\"LoadError\" type=\"(ss)\" access=\"read\"/>\n" \
" </interface>\n"
+#define BUS_UNIT_CGROUP_INTERFACE \
+ " <property name=\"DefaultControlGroup\" type=\"s\" access=\"read\"/>\n" \
+ " <property name=\"ControlGroups\" type=\"as\" access=\"read\"/>\n" \
+ " <property name=\"ControlGroupAttributes\" type=\"a(sss)\" access=\"read\"/>\n" \
+ " <method name=\"SetControlGroup\">\n" \
+ " <arg name=\"group\" type=\"s\" direction=\"in\"/>\n" \
+ " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \
+ " </method>\n" \
+ " <method name=\"UnsetControlGroup\">\n" \
+ " <arg name=\"group\" type=\"s\" direction=\"in\"/>\n" \
+ " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \
+ " </method>\n" \
+ " <method name=\"GetControlGroupAttribute\">\n" \
+ " <arg name=\"attribute\" type=\"s\" direction=\"in\"/>\n" \
+ " <arg name=\"values\" type=\"as\" direction=\"out\"/>\n" \
+ " </method>\n" \
+ " <method name=\"SetControlGroupAttribute\">\n" \
+ " <arg name=\"attribute\" type=\"s\" direction=\"in\"/>\n" \
+ " <arg name=\"values\" type=\"as\" direction=\"in\"/>\n" \
+ " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \
+ " </method>\n" \
+ " <method name=\"UnsetControlGroupAttribute\">\n" \
+ " <arg name=\"attribute\" type=\"s\" direction=\"in\"/>\n" \
+ " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \
+ " </method>\n"
+
#define BUS_UNIT_INTERFACES_LIST \
BUS_GENERIC_INTERFACES_LIST \
"org.freedesktop.systemd1.Unit\0"
extern const BusProperty bus_unit_properties[];
+extern const BusProperty bus_unit_cgroup_properties[];
void bus_unit_send_change_signal(Unit *u);
void bus_unit_send_removed_signal(Unit *u);
-
DBusHandlerResult bus_unit_queue_job(
DBusConnection *connection,
DBusMessage *message,
@@ -142,6 +168,12 @@ DBusHandlerResult bus_unit_queue_job(
JobMode mode,
bool reload_if_possible);
+int bus_unit_cgroup_set(Unit *u, DBusMessageIter *iter);
+int bus_unit_cgroup_unset(Unit *u, DBusMessageIter *iter);
+int bus_unit_cgroup_attribute_get(Unit *u, DBusMessageIter *iter, char ***_result);
+int bus_unit_cgroup_attribute_set(Unit *u, DBusMessageIter *iter);
+int bus_unit_cgroup_attribute_unset(Unit *u, DBusMessageIter *iter);
+
extern const DBusObjectPathVTable bus_unit_vtable;
extern const char bus_unit_interface[];
diff --git a/src/core/dbus.c b/src/core/dbus.c
index 2a1c66054a..1272c938cf 100644
--- a/src/core/dbus.c
+++ b/src/core/dbus.c
@@ -48,7 +48,7 @@
#include "special.h"
#include "dbus-common.h"
-#define CONNECTIONS_MAX 52
+#define CONNECTIONS_MAX 512
/* Well-known address (http://dbus.freedesktop.org/doc/dbus-specification.html#message-bus-types) */
#define DBUS_SYSTEM_BUS_DEFAULT_ADDRESS "unix:path=/var/run/dbus/system_bus_socket"
@@ -203,13 +203,11 @@ static void bus_toggle_watch(DBusWatch *bus_watch, void *data) {
}
static int bus_timeout_arm(Manager *m, Watch *w) {
- struct itimerspec its;
+ struct itimerspec its = {};
assert(m);
assert(w);
- zero(its);
-
if (dbus_timeout_get_enabled(w->data.bus_timeout)) {
timespec_store(&its.it_value, dbus_timeout_get_interval(w->data.bus_timeout) * USEC_PER_MSEC);
its.it_interval = its.it_value;
@@ -365,8 +363,8 @@ static DBusHandlerResult api_bus_message_filter(DBusConnection *connection, DBus
log_debug("Got D-Bus activation request for %s", name);
- if (manager_unit_pending_inactive(m, SPECIAL_DBUS_SERVICE) ||
- manager_unit_pending_inactive(m, SPECIAL_DBUS_SOCKET)) {
+ if (manager_unit_inactive_or_pending(m, SPECIAL_DBUS_SERVICE) ||
+ manager_unit_inactive_or_pending(m, SPECIAL_DBUS_SOCKET)) {
r = -EADDRNOTAVAIL;
dbus_set_error(&error, BUS_ERROR_SHUTTING_DOWN, "Refusing activation, D-Bus is shutting down.");
} else {
@@ -406,7 +404,7 @@ static DBusHandlerResult api_bus_message_filter(DBusConnection *connection, DBus
dbus_error_free(&error);
if (reply) {
- if (!dbus_connection_send(connection, reply, NULL))
+ if (!bus_maybe_send_reply(connection, message, reply))
goto oom;
dbus_message_unref(reply);
@@ -978,9 +976,8 @@ static DBusConnection* manager_bus_connect_private(Manager *m, DBusBusType type)
}
return connection;
+
fail:
- if (connection)
- dbus_connection_close(connection);
dbus_error_free(&error);
return NULL;
}
@@ -1054,7 +1051,7 @@ fail:
static int bus_init_private(Manager *m) {
DBusError error;
int r;
- const char *const external_only[] = {
+ static const char *const external_only[] = {
"EXTERNAL",
NULL
};
@@ -1077,18 +1074,33 @@ static int bus_init_private(Manager *m) {
} else {
const char *e;
char *p;
+ char *escaped;
e = secure_getenv("XDG_RUNTIME_DIR");
if (!e)
return 0;
- if (asprintf(&p, "unix:path=%s/systemd/private", e) < 0) {
+ if (asprintf(&p, "%s/systemd/private", e) < 0) {
+ r = log_oom();
+ goto fail;
+ }
+
+ mkdir_parents_label(p, 0755);
+ unlink(p);
+ free(p);
+
+ escaped = dbus_address_escape_value(e);
+ if (!escaped) {
+ r = log_oom();
+ goto fail;
+ }
+ if (asprintf(&p, "unix:path=%s/systemd/private", escaped) < 0) {
+ dbus_free(escaped);
r = log_oom();
goto fail;
}
+ dbus_free(escaped);
- mkdir_parents_label(p+10, 0755);
- unlink(p+10);
m->private_bus = dbus_server_listen(p, &error);
free(p);
}
diff --git a/src/core/device.c b/src/core/device.c
index 0b01718ad4..9fca82ab16 100644
--- a/src/core/device.c
+++ b/src/core/device.c
@@ -124,13 +124,13 @@ static void device_dump(Unit *u, FILE *f, const char *prefix) {
prefix, strna(d->sysfs));
}
-static UnitActiveState device_active_state(Unit *u) {
+_pure_ static UnitActiveState device_active_state(Unit *u) {
assert(u);
return state_translation_table[DEVICE(u)->state];
}
-static const char *device_sub_state_to_string(Unit *u) {
+_pure_ static const char *device_sub_state_to_string(Unit *u) {
assert(u);
return device_state_to_string(DEVICE(u)->state);
@@ -281,16 +281,22 @@ static int device_update_unit(Manager *m, struct udev_device *dev, const char *p
size_t l;
FOREACH_WORD_QUOTED(w, l, wants, state) {
- char *e;
+ char *e, *n;
e = strndup(w, l);
if (!e) {
r = -ENOMEM;
goto fail;
}
-
- r = unit_add_dependency_by_name(u, UNIT_WANTS, e, NULL, true);
+ n = unit_name_mangle(e);
+ if (!n) {
+ r = -ENOMEM;
+ goto fail;
+ }
free(e);
+
+ r = unit_add_dependency_by_name(u, UNIT_WANTS, n, NULL, true);
+ free(n);
if (r < 0)
goto fail;
}
@@ -312,6 +318,7 @@ fail:
static int device_process_new_device(Manager *m, struct udev_device *dev, bool update_state) {
const char *sysfs, *dn;
struct udev_list_entry *item = NULL, *first = NULL;
+ int r;
assert(m);
@@ -319,7 +326,9 @@ static int device_process_new_device(Manager *m, struct udev_device *dev, bool u
return -ENOMEM;
/* Add the main unit named after the sysfs path */
- device_update_unit(m, dev, sysfs, true);
+ r = device_update_unit(m, dev, sysfs, true);
+ if (r < 0)
+ return r;
/* Add an additional unit for the device node */
if ((dn = udev_device_get_devnode(dev)))
@@ -477,7 +486,6 @@ static void device_shutdown(Manager *m) {
}
static int device_enumerate(Manager *m) {
- struct epoll_event ev;
int r;
struct udev_enumerate *e = NULL;
struct udev_list_entry *item = NULL, *first = NULL;
@@ -485,6 +493,8 @@ static int device_enumerate(Manager *m) {
assert(m);
if (!m->udev) {
+ struct epoll_event ev;
+
if (!(m->udev = udev_new()))
return -ENOMEM;
diff --git a/src/core/device.h b/src/core/device.h
index 3c4604f60e..41e8de9703 100644
--- a/src/core/device.h
+++ b/src/core/device.h
@@ -52,5 +52,5 @@ extern const UnitVTable device_vtable;
void device_fd_event(Manager *m, int events);
-const char* device_state_to_string(DeviceState i);
-DeviceState device_state_from_string(const char *s);
+const char* device_state_to_string(DeviceState i) _const_;
+DeviceState device_state_from_string(const char *s) _pure_;
diff --git a/src/core/execute.c b/src/core/execute.c
index 7dc15044b4..3959ef9623 100644
--- a/src/core/execute.c
+++ b/src/core/execute.c
@@ -40,6 +40,7 @@
#include <sys/poll.h>
#include <linux/seccomp-bpf.h>
#include <glob.h>
+#include <libgen.h>
#ifdef HAVE_PAM
#include <security/pam_appl.h>
@@ -64,6 +65,8 @@
#include "loopback-setup.h"
#include "path-util.h"
#include "syscall-list.h"
+#include "env-util.h"
+#include "fileio.h"
#define IDLE_TIMEOUT_USEC (5*USEC_PER_SEC)
@@ -141,7 +144,7 @@ static int flags_fds(const int fds[], unsigned n_fds, bool nonblock) {
return 0;
}
-static const char *tty_path(const ExecContext *context) {
+_pure_ static const char *tty_path(const ExecContext *context) {
assert(context);
if (context->tty_path)
@@ -163,6 +166,26 @@ void exec_context_tty_reset(const ExecContext *context) {
vt_disallocate(context->tty_path);
}
+static bool is_terminal_output(ExecOutput o) {
+ return
+ o == EXEC_OUTPUT_TTY ||
+ o == EXEC_OUTPUT_SYSLOG_AND_CONSOLE ||
+ o == EXEC_OUTPUT_KMSG_AND_CONSOLE ||
+ o == EXEC_OUTPUT_JOURNAL_AND_CONSOLE;
+}
+
+void exec_context_serialize(const ExecContext *context, Unit *u, FILE *f) {
+ assert(context);
+ assert(u);
+ assert(f);
+
+ if (context->tmp_dir)
+ unit_serialize_item(u, f, "tmp-dir", context->tmp_dir);
+
+ if (context->var_tmp_dir)
+ unit_serialize_item(u, f, "var-tmp-dir", context->var_tmp_dir);
+}
+
static int open_null_as(int flags, int nfd) {
int fd, r;
@@ -182,7 +205,10 @@ static int open_null_as(int flags, int nfd) {
static int connect_logger_as(const ExecContext *context, ExecOutput output, const char *ident, const char *unit_id, int nfd) {
int fd, r;
- union sockaddr_union sa;
+ union sockaddr_union sa = {
+ .un.sun_family = AF_UNIX,
+ .un.sun_path = "/run/systemd/journal/stdout",
+ };
assert(context);
assert(output < _EXEC_OUTPUT_MAX);
@@ -193,10 +219,6 @@ static int connect_logger_as(const ExecContext *context, ExecOutput output, cons
if (fd < 0)
return -errno;
- zero(sa);
- sa.un.sun_family = AF_UNIX;
- strncpy(sa.un.sun_path, "/run/systemd/journal/stdout", sizeof(sa.un.sun_path));
-
r = connect(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path));
if (r < 0) {
close_nointr_nofail(fd);
@@ -222,7 +244,7 @@ static int connect_logger_as(const ExecContext *context, ExecOutput output, cons
!!context->syslog_level_prefix,
output == EXEC_OUTPUT_SYSLOG || output == EXEC_OUTPUT_SYSLOG_AND_CONSOLE,
output == EXEC_OUTPUT_KMSG || output == EXEC_OUTPUT_KMSG_AND_CONSOLE,
- output == EXEC_OUTPUT_SYSLOG_AND_CONSOLE || output == EXEC_OUTPUT_KMSG_AND_CONSOLE || output == EXEC_OUTPUT_JOURNAL_AND_CONSOLE);
+ is_terminal_output(output));
if (fd != nfd) {
r = dup2(fd, nfd) < 0 ? -errno : nfd;
@@ -318,9 +340,10 @@ static int setup_input(const ExecContext *context, int socket_fd, bool apply_tty
}
}
-static int setup_output(const ExecContext *context, int socket_fd, const char *ident, const char *unit_id, bool apply_tty_stdin) {
+static int setup_output(const ExecContext *context, int fileno, int socket_fd, const char *ident, const char *unit_id, bool apply_tty_stdin) {
ExecOutput o;
ExecInput i;
+ int r;
assert(context);
assert(ident);
@@ -328,91 +351,55 @@ static int setup_output(const ExecContext *context, int socket_fd, const char *i
i = fixup_input(context->std_input, socket_fd, apply_tty_stdin);
o = fixup_output(context->std_output, socket_fd);
- /* This expects the input is already set up */
+ if (fileno == STDERR_FILENO) {
+ ExecOutput e;
+ e = fixup_output(context->std_error, socket_fd);
- switch (o) {
+ /* This expects the input and output are already set up */
+
+ /* Don't change the stderr file descriptor if we inherit all
+ * the way and are not on a tty */
+ if (e == EXEC_OUTPUT_INHERIT &&
+ o == EXEC_OUTPUT_INHERIT &&
+ i == EXEC_INPUT_NULL &&
+ !is_terminal_input(context->std_input) &&
+ getppid () != 1)
+ return fileno;
- case EXEC_OUTPUT_INHERIT:
+ /* Duplicate from stdout if possible */
+ if (e == o || e == EXEC_OUTPUT_INHERIT)
+ return dup2(STDOUT_FILENO, fileno) < 0 ? -errno : fileno;
+ o = e;
+
+ } else if (o == EXEC_OUTPUT_INHERIT) {
/* If input got downgraded, inherit the original value */
if (i == EXEC_INPUT_NULL && is_terminal_input(context->std_input))
- return open_terminal_as(tty_path(context), O_WRONLY, STDOUT_FILENO);
+ return open_terminal_as(tty_path(context), O_WRONLY, fileno);
/* If the input is connected to anything that's not a /dev/null, inherit that... */
if (i != EXEC_INPUT_NULL)
- return dup2(STDIN_FILENO, STDOUT_FILENO) < 0 ? -errno : STDOUT_FILENO;
+ return dup2(STDIN_FILENO, fileno) < 0 ? -errno : fileno;
/* If we are not started from PID 1 we just inherit STDOUT from our parent process. */
if (getppid() != 1)
- return STDOUT_FILENO;
-
- /* We need to open /dev/null here anew, to get the
- * right access mode. So we fall through */
-
- case EXEC_OUTPUT_NULL:
- return open_null_as(O_WRONLY, STDOUT_FILENO);
-
- case EXEC_OUTPUT_TTY:
- if (is_terminal_input(i))
- return dup2(STDIN_FILENO, STDOUT_FILENO) < 0 ? -errno : STDOUT_FILENO;
+ return fileno;
- /* We don't reset the terminal if this is just about output */
- return open_terminal_as(tty_path(context), O_WRONLY, STDOUT_FILENO);
-
- case EXEC_OUTPUT_SYSLOG:
- case EXEC_OUTPUT_SYSLOG_AND_CONSOLE:
- case EXEC_OUTPUT_KMSG:
- case EXEC_OUTPUT_KMSG_AND_CONSOLE:
- case EXEC_OUTPUT_JOURNAL:
- case EXEC_OUTPUT_JOURNAL_AND_CONSOLE:
- return connect_logger_as(context, o, ident, unit_id, STDOUT_FILENO);
-
- case EXEC_OUTPUT_SOCKET:
- assert(socket_fd >= 0);
- return dup2(socket_fd, STDOUT_FILENO) < 0 ? -errno : STDOUT_FILENO;
-
- default:
- assert_not_reached("Unknown output type");
+ /* We need to open /dev/null here anew, to get the right access mode. */
+ return open_null_as(O_WRONLY, fileno);
}
-}
-
-static int setup_error(const ExecContext *context, int socket_fd, const char *ident, const char *unit_id, bool apply_tty_stdin) {
- ExecOutput o, e;
- ExecInput i;
-
- assert(context);
- assert(ident);
-
- i = fixup_input(context->std_input, socket_fd, apply_tty_stdin);
- o = fixup_output(context->std_output, socket_fd);
- e = fixup_output(context->std_error, socket_fd);
-
- /* This expects the input and output are already set up */
- /* Don't change the stderr file descriptor if we inherit all
- * the way and are not on a tty */
- if (e == EXEC_OUTPUT_INHERIT &&
- o == EXEC_OUTPUT_INHERIT &&
- i == EXEC_INPUT_NULL &&
- !is_terminal_input(context->std_input) &&
- getppid () != 1)
- return STDERR_FILENO;
-
- /* Duplicate from stdout if possible */
- if (e == o || e == EXEC_OUTPUT_INHERIT)
- return dup2(STDOUT_FILENO, STDERR_FILENO) < 0 ? -errno : STDERR_FILENO;
-
- switch (e) {
+ switch (o) {
case EXEC_OUTPUT_NULL:
- return open_null_as(O_WRONLY, STDERR_FILENO);
+ return open_null_as(O_WRONLY, fileno);
case EXEC_OUTPUT_TTY:
if (is_terminal_input(i))
- return dup2(STDIN_FILENO, STDERR_FILENO) < 0 ? -errno : STDERR_FILENO;
+ return dup2(STDIN_FILENO, fileno) < 0 ? -errno : fileno;
/* We don't reset the terminal if this is just about output */
- return open_terminal_as(tty_path(context), O_WRONLY, STDERR_FILENO);
+ return open_terminal_as(tty_path(context), O_WRONLY, fileno);
case EXEC_OUTPUT_SYSLOG:
case EXEC_OUTPUT_SYSLOG_AND_CONSOLE:
@@ -420,11 +407,21 @@ static int setup_error(const ExecContext *context, int socket_fd, const char *id
case EXEC_OUTPUT_KMSG_AND_CONSOLE:
case EXEC_OUTPUT_JOURNAL:
case EXEC_OUTPUT_JOURNAL_AND_CONSOLE:
- return connect_logger_as(context, e, ident, unit_id, STDERR_FILENO);
+ r = connect_logger_as(context, o, ident, unit_id, fileno);
+ if (r < 0) {
+ log_struct_unit(LOG_CRIT, unit_id,
+ "MESSAGE=Failed to connect std%s of %s to the journal socket: %s",
+ fileno == STDOUT_FILENO ? "out" : "err",
+ unit_id, strerror(-r),
+ "ERRNO=%d", -r,
+ NULL);
+ r = open_null_as(O_WRONLY, fileno);
+ }
+ return r;
case EXEC_OUTPUT_SOCKET:
assert(socket_fd >= 0);
- return dup2(socket_fd, STDERR_FILENO) < 0 ? -errno : STDERR_FILENO;
+ return dup2(socket_fd, fileno) < 0 ? -errno : fileno;
default:
assert_not_reached("Unknown error type");
@@ -512,7 +509,7 @@ fail:
return r;
}
-static int write_confirm_message(const char *format, ...) {
+_printf_attr_(1, 2) static int write_confirm_message(const char *format, ...) {
int fd;
va_list ap;
@@ -673,9 +670,9 @@ static int enforce_user(const ExecContext *context, uid_t uid) {
/* First step: If we need to keep capabilities but
* drop privileges we need to make sure we keep our
- * caps, whiel we drop privileges. */
+ * caps, while we drop privileges. */
if (uid != 0) {
- int sb = context->secure_bits|SECURE_KEEP_CAPS;
+ int sb = context->secure_bits | 1<<SECURE_KEEP_CAPS;
if (prctl(PR_GET_SECUREBITS) != sb)
if (prctl(PR_SET_SECUREBITS, sb) < 0)
@@ -940,7 +937,7 @@ static int apply_seccomp(uint32_t *syscall_filter) {
int i;
unsigned n;
struct sock_filter *f;
- struct sock_fprog prog;
+ struct sock_fprog prog = {};
assert(syscall_filter);
@@ -957,7 +954,7 @@ static int apply_seccomp(uint32_t *syscall_filter) {
for (i = 0, n = 0; i < syscall_max(); i++)
if (syscall_filter[i >> 4] & (1 << (i & 31))) {
struct sock_filter item[] = {
- BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, i, 0, 1),
+ BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, INDEX_TO_SYSCALL(i), 0, 1),
BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW)
};
@@ -972,7 +969,6 @@ static int apply_seccomp(uint32_t *syscall_filter) {
memcpy(f + (ELEMENTSOF(header) + 2*n), footer, sizeof(footer));
/* Third: install the filter */
- zero(prog);
prog.len = ELEMENTSOF(header) + ELEMENTSOF(footer) + 2*n;
prog.filter = f;
if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog) < 0)
@@ -983,7 +979,7 @@ static int apply_seccomp(uint32_t *syscall_filter) {
int exec_spawn(ExecCommand *command,
char **argv,
- const ExecContext *context,
+ ExecContext *context,
int fds[], unsigned n_fds,
char **environment,
bool apply_permissions,
@@ -1001,7 +997,7 @@ int exec_spawn(ExecCommand *command,
int r;
char *line;
int socket_fd;
- char _cleanup_strv_free_ **files_env = NULL;
+ _cleanup_strv_free_ char **files_env = NULL;
assert(command);
assert(context);
@@ -1024,8 +1020,8 @@ int exec_spawn(ExecCommand *command,
r = exec_context_load_environment(context, &files_env);
if (r < 0) {
- log_struct(LOG_ERR,
- "UNIT=%s", unit_id,
+ log_struct_unit(LOG_ERR,
+ unit_id,
"MESSAGE=Failed to load environment files: %s", strerror(-r),
"ERRNO=%d", -r,
NULL);
@@ -1039,18 +1035,30 @@ int exec_spawn(ExecCommand *command,
if (!line)
return log_oom();
- log_struct(LOG_DEBUG,
- "UNIT=%s", unit_id,
- "MESSAGE=About to execute %s", line,
- NULL);
+ log_struct_unit(LOG_DEBUG,
+ unit_id,
+ "EXECUTABLE=%s", command->path,
+ "MESSAGE=About to execute: %s", line,
+ NULL);
free(line);
r = cgroup_bonding_realize_list(cgroup_bondings);
if (r < 0)
return r;
+ /* We must initialize the attributes in the parent, before we
+ fork, because we really need them initialized before making
+ the process a member of the group (which we do in both the
+ child and the parent), and we cannot really apply them twice
+ (due to 'append' style attributes) */
cgroup_attribute_apply_list(cgroup_attributes, cgroup_bondings);
+ if (context->private_tmp && !context->tmp_dir && !context->var_tmp_dir) {
+ r = setup_tmpdirs(&context->tmp_dir, &context->var_tmp_dir);
+ if (r < 0)
+ return r;
+ }
+
pid = fork();
if (pid < 0)
return -errno;
@@ -1061,7 +1069,7 @@ int exec_spawn(ExecCommand *command,
const char *username = NULL, *home = NULL;
uid_t uid = (uid_t) -1;
gid_t gid = (gid_t) -1;
- char _cleanup_strv_free_ **our_env = NULL, **pam_env = NULL,
+ _cleanup_strv_free_ char **our_env = NULL, **pam_env = NULL,
**final_env = NULL, **final_argv = NULL;
unsigned n_env = 0;
bool set_access = false;
@@ -1165,13 +1173,13 @@ int exec_spawn(ExecCommand *command,
goto fail_child;
}
- err = setup_output(context, socket_fd, path_get_file_name(command->path), unit_id, apply_tty_stdin);
+ err = setup_output(context, STDOUT_FILENO, socket_fd, path_get_file_name(command->path), unit_id, apply_tty_stdin);
if (err < 0) {
r = EXIT_STDOUT;
goto fail_child;
}
- err = setup_error(context, socket_fd, path_get_file_name(command->path), unit_id, apply_tty_stdin);
+ err = setup_output(context, STDERR_FILENO, socket_fd, path_get_file_name(command->path), unit_id, apply_tty_stdin);
if (err < 0) {
r = EXIT_STDERR;
goto fail_child;
@@ -1191,7 +1199,7 @@ int exec_spawn(ExecCommand *command,
snprintf(t, sizeof(t), "%i", context->oom_score_adjust);
char_array_0(t);
- if (write_one_line_file("/proc/self/oom_score_adj", t) < 0) {
+ if (write_string_file("/proc/self/oom_score_adj", t) < 0) {
err = -errno;
r = EXIT_OOM_ADJUST;
goto fail_child;
@@ -1206,13 +1214,16 @@ int exec_spawn(ExecCommand *command,
}
if (context->cpu_sched_set) {
- struct sched_param param;
-
- zero(param);
- param.sched_priority = context->cpu_sched_priority;
+ struct sched_param param = {
+ .sched_priority = context->cpu_sched_priority,
+ };
- if (sched_setscheduler(0, context->cpu_sched_policy |
- (context->cpu_sched_reset_on_fork ? SCHED_RESET_ON_FORK : 0), &param) < 0) {
+ r = sched_setscheduler(0,
+ context->cpu_sched_policy |
+ (context->cpu_sched_reset_on_fork ?
+ SCHED_RESET_ON_FORK : 0),
+ &param);
+ if (r < 0) {
err = -errno;
r = EXIT_SETSCHEDULER;
goto fail_child;
@@ -1262,7 +1273,12 @@ int exec_spawn(ExecCommand *command,
if (cgroup_bondings && context->control_group_modify) {
err = cgroup_bonding_set_group_access_list(cgroup_bondings, 0755, uid, gid);
if (err >= 0)
- err = cgroup_bonding_set_task_access_list(cgroup_bondings, 0644, uid, gid, context->control_group_persistent);
+ err = cgroup_bonding_set_task_access_list(
+ cgroup_bondings,
+ 0644,
+ uid,
+ gid,
+ context->control_group_persistent);
if (err < 0) {
r = EXIT_CGROUP;
goto fail_child;
@@ -1273,7 +1289,12 @@ int exec_spawn(ExecCommand *command,
}
if (cgroup_bondings && !set_access && context->control_group_persistent >= 0) {
- err = cgroup_bonding_set_task_access_list(cgroup_bondings, (mode_t) -1, (uid_t) -1, (uid_t) -1, context->control_group_persistent);
+ err = cgroup_bonding_set_task_access_list(
+ cgroup_bondings,
+ (mode_t) -1,
+ (uid_t) -1,
+ (uid_t) -1,
+ context->control_group_persistent);
if (err < 0) {
r = EXIT_CGROUP;
goto fail_child;
@@ -1317,6 +1338,8 @@ int exec_spawn(ExecCommand *command,
err = setup_namespace(context->read_write_dirs,
context->read_only_dirs,
context->inaccessible_dirs,
+ context->tmp_dir,
+ context->var_tmp_dir,
context->private_tmp,
context->mount_flags);
if (err < 0) {
@@ -1339,7 +1362,7 @@ int exec_spawn(ExecCommand *command,
goto fail_child;
}
} else {
- char _cleanup_free_ *d = NULL;
+ _cleanup_free_ char *d = NULL;
if (asprintf(&d, "%s/%s",
context->root_directory ? context->root_directory : "",
@@ -1431,7 +1454,8 @@ int exec_spawn(ExecCommand *command,
}
}
- if (!(our_env = new0(char*, 7))) {
+ our_env = new0(char*, 7);
+ if (!our_env) {
err = -ENOMEM;
r = EXIT_MEMORY;
goto fail_child;
@@ -1471,20 +1495,21 @@ int exec_spawn(ExecCommand *command,
assert(n_env <= 7);
- if (!(final_env = strv_env_merge(
- 5,
- environment,
- our_env,
- context->environment,
- files_env,
- pam_env,
- NULL))) {
+ final_env = strv_env_merge(5,
+ environment,
+ our_env,
+ context->environment,
+ files_env,
+ pam_env,
+ NULL);
+ if (!final_env) {
err = -ENOMEM;
r = EXIT_MEMORY;
goto fail_child;
}
- if (!(final_argv = replace_env_argv(argv, final_env))) {
+ final_argv = replace_env_argv(argv, final_env);
+ if (!final_argv) {
err = -ENOMEM;
r = EXIT_MEMORY;
goto fail_child;
@@ -1492,6 +1517,20 @@ int exec_spawn(ExecCommand *command,
final_env = strv_env_clean(final_env);
+ if (_unlikely_(log_get_max_level() >= LOG_PRI(LOG_DEBUG))) {
+ line = exec_command_line(final_argv);
+ if (line) {
+ log_open();
+ log_struct_unit(LOG_DEBUG,
+ unit_id,
+ "EXECUTABLE=%s", command->path,
+ "MESSAGE=Executing: %s", line,
+ NULL);
+ log_close();
+ free(line);
+ line = NULL;
+ }
+ }
execve(command->path, final_argv, final_env);
err = -errno;
r = EXIT_EXEC;
@@ -1512,19 +1551,18 @@ int exec_spawn(ExecCommand *command,
_exit(r);
}
- log_struct(LOG_DEBUG,
- "UNIT=%s", unit_id,
- "MESSAGE=Forked %s as %lu",
- command->path, (unsigned long) pid,
- NULL);
+ log_struct_unit(LOG_DEBUG,
+ unit_id,
+ "MESSAGE=Forked %s as %lu",
+ command->path, (unsigned long) pid,
+ NULL);
/* We add the new process to the cgroup both in the child (so
* that we can be sure that no user code is ever executed
* outside of the cgroup) and in the parent (so that we can be
* sure that when we kill the cgroup the process will be
* killed too). */
- if (cgroup_bondings)
- cgroup_bonding_install_list(cgroup_bondings, pid, cgroup_suffix);
+ cgroup_bonding_install_list(cgroup_bondings, pid, cgroup_suffix);
exec_status_start(&command->exec_status, pid);
@@ -1545,7 +1583,35 @@ void exec_context_init(ExecContext *c) {
c->timer_slack_nsec = (nsec_t) -1;
}
-void exec_context_done(ExecContext *c) {
+void exec_context_tmp_dirs_done(ExecContext *c) {
+ char* dirs[] = {c->tmp_dir ? c->tmp_dir : c->var_tmp_dir,
+ c->tmp_dir ? c->var_tmp_dir : NULL,
+ NULL};
+ char **dirp;
+
+ for(dirp = dirs; *dirp; dirp++) {
+ char *dir;
+ int r;
+
+ r = rm_rf_dangerous(*dirp, false, true, false);
+ dir = dirname(*dirp);
+ if (r < 0)
+ log_warning("Failed to remove content of temporary directory %s: %s",
+ dir, strerror(-r));
+ else {
+ r = rmdir(dir);
+ if (r < 0)
+ log_warning("Failed to remove temporary directory %s: %s",
+ dir, strerror(-r));
+ }
+
+ free(*dirp);
+ }
+
+ c->tmp_dir = c->var_tmp_dir = NULL;
+}
+
+void exec_context_done(ExecContext *c, bool reloading_or_reexecuting) {
unsigned l;
assert(c);
@@ -1609,6 +1675,9 @@ void exec_context_done(ExecContext *c) {
free(c->syscall_filter);
c->syscall_filter = NULL;
+
+ if (!reloading_or_reexecuting)
+ exec_context_tmp_dirs_done(c);
}
void exec_command_done(ExecCommand *c) {
@@ -1658,7 +1727,7 @@ int exec_context_load_environment(const ExecContext *c, char ***l) {
int k;
bool ignore = false;
char **p;
- glob_t pglob;
+ _cleanup_globfree_ glob_t pglob = {};
int count, n;
fn = *i;
@@ -1669,7 +1738,6 @@ int exec_context_load_environment(const ExecContext *c, char ***l) {
}
if (!path_is_absolute(fn)) {
-
if (ignore)
continue;
@@ -1678,10 +1746,8 @@ int exec_context_load_environment(const ExecContext *c, char ***l) {
}
/* Filename supports globbing, take all matching files */
- zero(pglob);
errno = 0;
if (glob(fn, 0, NULL, &pglob) != 0) {
- globfree(&pglob);
if (ignore)
continue;
@@ -1690,7 +1756,6 @@ int exec_context_load_environment(const ExecContext *c, char ***l) {
}
count = pglob.gl_pathc;
if (count == 0) {
- globfree(&pglob);
if (ignore)
continue;
@@ -1698,15 +1763,17 @@ int exec_context_load_environment(const ExecContext *c, char ***l) {
return -EINVAL;
}
for (n = 0; n < count; n++) {
- k = load_env_file(pglob.gl_pathv[n], &p);
+ k = load_env_file(pglob.gl_pathv[n], NULL, &p);
if (k < 0) {
if (ignore)
continue;
strv_free(r);
- globfree(&pglob);
return k;
}
+ /* Log invalid environment variables with filename */
+ if (p)
+ p = strv_env_clean_log(p, pglob.gl_pathv[n]);
if (r == NULL)
r = p;
@@ -1716,16 +1783,12 @@ int exec_context_load_environment(const ExecContext *c, char ***l) {
m = strv_env_merge(2, r, p);
strv_free(r);
strv_free(p);
-
- if (!m) {
- globfree(&pglob);
+ if (!m)
return -ENOMEM;
- }
r = m;
}
}
- globfree(&pglob);
}
*l = r;
@@ -1733,6 +1796,37 @@ int exec_context_load_environment(const ExecContext *c, char ***l) {
return 0;
}
+static bool tty_may_match_dev_console(const char *tty) {
+ char *active = NULL, *console;
+ bool b;
+
+ if (startswith(tty, "/dev/"))
+ tty += 5;
+
+ /* trivial identity? */
+ if (streq(tty, "console"))
+ return true;
+
+ console = resolve_dev_console(&active);
+ /* if we could not resolve, assume it may */
+ if (!console)
+ return true;
+
+ /* "tty0" means the active VC, so it may be the same sometimes */
+ b = streq(console, tty) || (streq(console, "tty0") && tty_is_vc(tty));
+ free(active);
+
+ return b;
+}
+
+bool exec_context_may_touch_console(ExecContext *ec) {
+ return (ec->tty_reset || ec->tty_vhangup || ec->tty_vt_disallocate ||
+ is_terminal_input(ec->std_input) ||
+ is_terminal_output(ec->std_output) ||
+ is_terminal_output(ec->std_error)) &&
+ tty_may_match_dev_console(tty_path(ec));
+}
+
static void strv_fprintf(FILE *f, char **l) {
char **g;
@@ -1827,7 +1921,7 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) {
prefix, c->cpu_sched_priority,
prefix, yes_no(c->cpu_sched_reset_on_fork));
free(policy_str);
- }
+ }
if (c->cpuset) {
fprintf(f, "%sCPUAffinity:", prefix);
@@ -1895,12 +1989,12 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) {
if (c->secure_bits)
fprintf(f, "%sSecure Bits:%s%s%s%s%s%s\n",
prefix,
- (c->secure_bits & SECURE_KEEP_CAPS) ? " keep-caps" : "",
- (c->secure_bits & SECURE_KEEP_CAPS_LOCKED) ? " keep-caps-locked" : "",
- (c->secure_bits & SECURE_NO_SETUID_FIXUP) ? " no-setuid-fixup" : "",
- (c->secure_bits & SECURE_NO_SETUID_FIXUP_LOCKED) ? " no-setuid-fixup-locked" : "",
- (c->secure_bits & SECURE_NOROOT) ? " noroot" : "",
- (c->secure_bits & SECURE_NOROOT_LOCKED) ? "noroot-locked" : "");
+ (c->secure_bits & 1<<SECURE_KEEP_CAPS) ? " keep-caps" : "",
+ (c->secure_bits & 1<<SECURE_KEEP_CAPS_LOCKED) ? " keep-caps-locked" : "",
+ (c->secure_bits & 1<<SECURE_NO_SETUID_FIXUP) ? " no-setuid-fixup" : "",
+ (c->secure_bits & 1<<SECURE_NO_SETUID_FIXUP_LOCKED) ? " no-setuid-fixup-locked" : "",
+ (c->secure_bits & 1<<SECURE_NOROOT) ? " noroot" : "",
+ (c->secure_bits & 1<<SECURE_NOROOT_LOCKED) ? "noroot-locked" : "");
if (c->capability_bounding_set_drop) {
unsigned long l;
diff --git a/src/core/execute.h b/src/core/execute.h
index 2bcd2e1e6c..15574dc97e 100644
--- a/src/core/execute.h
+++ b/src/core/execute.h
@@ -36,6 +36,8 @@ typedef struct ExecContext ExecContext;
struct CGroupBonding;
struct CGroupAttribute;
+typedef struct Unit Unit;
+
#include "list.h"
#include "util.h"
@@ -141,6 +143,8 @@ struct ExecContext {
bool non_blocking;
bool private_tmp;
bool private_network;
+ char *tmp_dir;
+ char *var_tmp_dir;
bool no_new_privileges;
@@ -164,7 +168,7 @@ struct ExecContext {
int exec_spawn(ExecCommand *command,
char **argv,
- const ExecContext *context,
+ ExecContext *context,
int fds[], unsigned n_fds,
char **environment,
bool apply_permissions,
@@ -192,18 +196,22 @@ void exec_command_append_list(ExecCommand **l, ExecCommand *e);
int exec_command_set(ExecCommand *c, const char *path, ...);
void exec_context_init(ExecContext *c);
-void exec_context_done(ExecContext *c);
+void exec_context_done(ExecContext *c, bool reloading_or_reexecuting);
+void exec_context_tmp_dirs_done(ExecContext *c);
void exec_context_dump(ExecContext *c, FILE* f, const char *prefix);
void exec_context_tty_reset(const ExecContext *context);
int exec_context_load_environment(const ExecContext *c, char ***l);
+bool exec_context_may_touch_console(ExecContext *c);
+void exec_context_serialize(const ExecContext *c, Unit *u, FILE *f);
+
void exec_status_start(ExecStatus *s, pid_t pid);
void exec_status_exit(ExecStatus *s, ExecContext *context, pid_t pid, int code, int status);
void exec_status_dump(ExecStatus *s, FILE *f, const char *prefix);
-const char* exec_output_to_string(ExecOutput i);
-ExecOutput exec_output_from_string(const char *s);
+const char* exec_output_to_string(ExecOutput i) _const_;
+ExecOutput exec_output_from_string(const char *s) _pure_;
-const char* exec_input_to_string(ExecInput i);
-ExecInput exec_input_from_string(const char *s);
+const char* exec_input_to_string(ExecInput i) _const_;
+ExecInput exec_input_from_string(const char *s) _pure_;
diff --git a/src/core/hostname-setup.c b/src/core/hostname-setup.c
index 7894f8a5f2..8aa1cff1d3 100644
--- a/src/core/hostname-setup.c
+++ b/src/core/hostname-setup.c
@@ -29,6 +29,7 @@
#include "macro.h"
#include "util.h"
#include "log.h"
+#include "fileio.h"
static int read_and_strip_hostname(const char *path, char **hn) {
char *s;
@@ -41,7 +42,7 @@ static int read_and_strip_hostname(const char *path, char **hn) {
if (r < 0)
return r;
- hostname_cleanup(s);
+ hostname_cleanup(s, false);
if (isempty(s)) {
free(s);
diff --git a/src/core/ima-setup.c b/src/core/ima-setup.c
index e8cc1ba8b6..7f8ec23d58 100644
--- a/src/core/ima-setup.c
+++ b/src/core/ima-setup.c
@@ -50,11 +50,6 @@ int ima_setup(void) {
int policyfd = -1, imafd = -1;
int result = 0;
-#ifndef HAVE_SELINUX
- /* Mount the securityfs filesystem */
- mount_setup_early();
-#endif
-
if (stat(IMA_POLICY_PATH, &st) < 0)
return 0;
diff --git a/src/core/job.c b/src/core/job.c
index 5ff95f5c90..d304a16d06 100644
--- a/src/core/job.c
+++ b/src/core/job.c
@@ -34,6 +34,9 @@
#include "load-dropin.h"
#include "log.h"
#include "dbus-job.h"
+#include "special.h"
+#include "sync.h"
+#include "virt.h"
JobBusClient* job_bus_client_new(DBusConnection *connection, const char *name) {
JobBusClient *cl;
@@ -166,6 +169,8 @@ static void job_merge_into_installed(Job *j, Job *other) {
assert(other->type == JOB_NOP);
j->override = j->override || other->override;
+ j->irreversible = j->irreversible || other->irreversible;
+ j->ignore_order = j->ignore_order || other->ignore_order;
}
Job* job_install(Job *j) {
@@ -201,6 +206,7 @@ Job* job_install(Job *j) {
"Merged into running job, re-running: %s/%s as %u",
uj->unit->id, job_type_to_string(uj->type), (unsigned) uj->id);
uj->state = JOB_WAITING;
+ uj->manager->n_running_jobs--;
return uj;
}
}
@@ -290,11 +296,13 @@ void job_dump(Job *j, FILE*f, const char *prefix) {
"%s-> Job %u:\n"
"%s\tAction: %s -> %s\n"
"%s\tState: %s\n"
- "%s\tForced: %s\n",
+ "%s\tForced: %s\n"
+ "%s\tIrreversible: %s\n",
prefix, j->id,
prefix, j->unit->id, job_type_to_string(j->type),
prefix, job_state_to_string(j->state),
- prefix, yes_no(j->override));
+ prefix, yes_no(j->override),
+ prefix, yes_no(j->irreversible));
}
/*
@@ -476,7 +484,7 @@ static void job_change_type(Job *j, JobType newtype) {
int job_run_and_invalidate(Job *j) {
int r;
uint32_t id;
- Manager *m;
+ Manager *m = j->manager;
assert(j);
assert(j->installed);
@@ -493,6 +501,7 @@ int job_run_and_invalidate(Job *j) {
return -EAGAIN;
j->state = JOB_RUNNING;
+ m->n_running_jobs++;
job_add_to_dbus_queue(j);
/* While we execute this operation the job might go away (for
@@ -501,7 +510,6 @@ int job_run_and_invalidate(Job *j) {
* store the id here, so that we can verify the job is still
* valid. */
id = j->id;
- m = j->manager;
switch (j->type) {
@@ -551,16 +559,17 @@ int job_run_and_invalidate(Job *j) {
r = job_finish_and_invalidate(j, JOB_DONE, true);
else if (r == -ENOEXEC)
r = job_finish_and_invalidate(j, JOB_SKIPPED, true);
- else if (r == -EAGAIN)
+ else if (r == -EAGAIN) {
j->state = JOB_WAITING;
- else if (r < 0)
+ m->n_running_jobs--;
+ } else if (r < 0)
r = job_finish_and_invalidate(j, JOB_FAILED, true);
}
return r;
}
-static const char *job_get_status_message_format(Unit *u, JobType t, JobResult result) {
+_pure_ static const char *job_get_status_message_format(Unit *u, JobType t, JobResult result) {
const UnitStatusMessageFormats *format_table;
assert(u);
@@ -579,7 +588,7 @@ static const char *job_get_status_message_format(Unit *u, JobType t, JobResult r
return NULL;
}
-static const char *job_get_status_message_format_try_harder(Unit *u, JobType t, JobResult result) {
+_pure_ static const char *job_get_status_message_format_try_harder(Unit *u, JobType t, JobResult result) {
const char *format;
assert(u);
@@ -635,20 +644,20 @@ static void job_print_status_message(Unit *u, JobType t, JobResult result) {
case JOB_DONE:
if (u->condition_result)
- unit_status_printf(u, ANSI_HIGHLIGHT_GREEN_ON " OK " ANSI_HIGHLIGHT_OFF, format, unit_description(u));
+ unit_status_printf(u, ANSI_GREEN_ON " OK " ANSI_HIGHLIGHT_OFF, format);
break;
case JOB_FAILED:
- unit_status_printf(u, ANSI_HIGHLIGHT_RED_ON "FAILED" ANSI_HIGHLIGHT_OFF, format, unit_description(u));
- unit_status_printf(u, NULL, "See 'systemctl status %s' for details.", u->id);
+ unit_status_printf(u, ANSI_HIGHLIGHT_RED_ON "FAILED" ANSI_HIGHLIGHT_OFF, format);
+ manager_status_printf(u->manager, false, NULL, "See 'systemctl status %s' for details.", u->id);
break;
case JOB_DEPENDENCY:
- unit_status_printf(u, ANSI_HIGHLIGHT_YELLOW_ON "DEPEND" ANSI_HIGHLIGHT_OFF, format, unit_description(u));
+ unit_status_printf(u, ANSI_HIGHLIGHT_YELLOW_ON "DEPEND" ANSI_HIGHLIGHT_OFF, format);
break;
case JOB_TIMEOUT:
- unit_status_printf(u, ANSI_HIGHLIGHT_RED_ON " TIME " ANSI_HIGHLIGHT_OFF, format, unit_description(u));
+ unit_status_printf(u, ANSI_HIGHLIGHT_RED_ON " TIME " ANSI_HIGHLIGHT_OFF, format);
break;
default:
@@ -664,12 +673,12 @@ static void job_print_status_message(Unit *u, JobType t, JobResult result) {
switch (result) {
case JOB_TIMEOUT:
- unit_status_printf(u, ANSI_HIGHLIGHT_RED_ON " TIME " ANSI_HIGHLIGHT_OFF, format, unit_description(u));
+ unit_status_printf(u, ANSI_HIGHLIGHT_RED_ON " TIME " ANSI_HIGHLIGHT_OFF, format);
break;
case JOB_DONE:
case JOB_FAILED:
- unit_status_printf(u, ANSI_HIGHLIGHT_GREEN_ON " OK " ANSI_HIGHLIGHT_OFF, format, unit_description(u));
+ unit_status_printf(u, ANSI_GREEN_ON " OK " ANSI_HIGHLIGHT_OFF, format);
break;
default:
@@ -682,7 +691,7 @@ static void job_print_status_message(Unit *u, JobType t, JobResult result) {
* Most likely a DEPEND warning from a requisiting unit will
* occur next and it's nice to see what was requisited. */
if (result == JOB_SKIPPED)
- unit_status_printf(u, ANSI_HIGHLIGHT_ON " INFO " ANSI_HIGHLIGHT_OFF, "%s is not active.", unit_description(u));
+ unit_status_printf(u, ANSI_HIGHLIGHT_ON " INFO " ANSI_HIGHLIGHT_OFF, "%s is not active.");
}
}
@@ -713,25 +722,25 @@ static void job_log_status_message(Unit *u, JobType t, JobResult result) {
sd_id128_t mid;
mid = result == JOB_DONE ? SD_MESSAGE_UNIT_STARTED : SD_MESSAGE_UNIT_FAILED;
- log_struct(result == JOB_DONE ? LOG_INFO : LOG_ERR,
+ log_struct_unit(result == JOB_DONE ? LOG_INFO : LOG_ERR,
+ u->id,
MESSAGE_ID(mid),
- "UNIT=%s", u->id,
"RESULT=%s", job_result_to_string(result),
"MESSAGE=%s", buf,
NULL);
} else if (t == JOB_STOP)
- log_struct(result == JOB_DONE ? LOG_INFO : LOG_ERR,
+ log_struct_unit(result == JOB_DONE ? LOG_INFO : LOG_ERR,
+ u->id,
MESSAGE_ID(SD_MESSAGE_UNIT_STOPPED),
- "UNIT=%s", u->id,
"RESULT=%s", job_result_to_string(result),
"MESSAGE=%s", buf,
NULL);
else if (t == JOB_RELOAD)
- log_struct(result == JOB_DONE ? LOG_INFO : LOG_ERR,
+ log_struct_unit(result == JOB_DONE ? LOG_INFO : LOG_ERR,
+ u->id,
MESSAGE_ID(SD_MESSAGE_UNIT_RELOADED),
- "UNIT=%s", u->id,
"RESULT=%s", job_result_to_string(result),
"MESSAGE=%s", buf,
NULL);
@@ -753,6 +762,9 @@ int job_finish_and_invalidate(Job *j, JobResult result, bool recursive) {
j->result = result;
+ if (j->state == JOB_RUNNING)
+ j->manager->n_running_jobs--;
+
log_debug_unit(u->id, "Job %s/%s finished, result=%s",
u->id, job_type_to_string(t), job_result_to_string(result));
@@ -818,19 +830,21 @@ int job_finish_and_invalidate(Job *j, JobResult result, bool recursive) {
* this context. And JOB_FAILURE is already handled by the
* unit itself. */
if (result == JOB_TIMEOUT || result == JOB_DEPENDENCY) {
- log_struct(LOG_NOTICE,
- "UNIT=%s", u->id,
+ log_struct_unit(LOG_NOTICE,
+ u->id,
"JOB_TYPE=%s", job_type_to_string(t),
- "JOB_RESULT=%s", job_result_to_string(t),
+ "JOB_RESULT=%s", job_result_to_string(result),
"Job %s/%s failed with result '%s'.",
u->id,
job_type_to_string(t),
job_result_to_string(result),
NULL);
- unit_trigger_on_failure(u);
+ unit_start_on_failure(u);
}
+ unit_trigger_notify(u);
+
finish:
/* Try to start the next jobs that can be started */
SET_FOREACH(other, u->dependencies[UNIT_AFTER], i)
@@ -846,10 +860,12 @@ finish:
}
int job_start_timer(Job *j) {
- struct itimerspec its;
- struct epoll_event ev;
+ struct itimerspec its = {};
+ struct epoll_event ev = {
+ .data.ptr = &j->timer_watch,
+ .events = EPOLLIN,
+ };
int fd, r;
- assert(j);
if (j->unit->job_timeout <= 0 ||
j->timer_watch.type == WATCH_JOB_TIMER)
@@ -862,7 +878,6 @@ int job_start_timer(Job *j) {
goto fail;
}
- zero(its);
timespec_store(&its.it_value, j->unit->job_timeout);
if (timerfd_settime(fd, 0, &its, NULL) < 0) {
@@ -870,10 +885,6 @@ int job_start_timer(Job *j) {
goto fail;
}
- zero(ev);
- ev.data.ptr = &j->timer_watch;
- ev.events = EPOLLIN;
-
if (epoll_ctl(j->manager->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0) {
r = -errno;
goto fail;
@@ -943,6 +954,7 @@ int job_serialize(Job *j, FILE *f, FDSet *fds) {
fprintf(f, "job-type=%s\n", job_type_to_string(j->type));
fprintf(f, "job-state=%s\n", job_state_to_string(j->state));
fprintf(f, "job-override=%s\n", yes_no(j->override));
+ fprintf(f, "job-irreversible=%s\n", yes_no(j->irreversible));
fprintf(f, "job-sent-dbus-new-signal=%s\n", yes_no(j->sent_dbus_new_signal));
fprintf(f, "job-ignore-order=%s\n", yes_no(j->ignore_order));
/* Cannot save bus clients. Just note the fact that we're losing
@@ -1010,6 +1022,12 @@ int job_deserialize(Job *j, FILE *f, FDSet *fds) {
log_debug("Failed to parse job override flag %s", v);
else
j->override = j->override || b;
+ } else if (streq(l, "job-irreversible")) {
+ int b = parse_boolean(v);
+ if (b < 0)
+ log_debug("Failed to parse job irreversible flag %s", v);
+ else
+ j->irreversible = j->irreversible || b;
} else if (streq(l, "job-sent-dbus-new-signal")) {
int b = parse_boolean(v);
if (b < 0)
@@ -1045,21 +1063,43 @@ int job_deserialize(Job *j, FILE *f, FDSet *fds) {
}
int job_coldplug(Job *j) {
- struct epoll_event ev;
+ struct epoll_event ev = {
+ .data.ptr = &j->timer_watch,
+ .events = EPOLLIN,
+ };
if (j->timer_watch.type != WATCH_JOB_TIMER)
return 0;
- zero(ev);
- ev.data.ptr = &j->timer_watch;
- ev.events = EPOLLIN;
-
if (epoll_ctl(j->manager->epoll_fd, EPOLL_CTL_ADD, j->timer_watch.fd, &ev) < 0)
return -errno;
return 0;
}
+void job_shutdown_magic(Job *j) {
+ assert(j);
+
+ /* The shutdown target gets some special treatment here: we
+ * tell the kernel to begin with flushing its disk caches, to
+ * optimize shutdown time a bit. Ideally we wouldn't hardcode
+ * this magic into PID 1. However all other processes aren't
+ * options either since they'd exit much sooner than PID 1 and
+ * asynchronous sync() would cause their exit to be
+ * delayed. */
+
+ if (!unit_has_name(j->unit, SPECIAL_SHUTDOWN_TARGET))
+ return;
+
+ if (j->type != JOB_START)
+ return;
+
+ if (detect_container(NULL) > 0)
+ return;
+
+ asynchronous_sync();
+}
+
static const char* const job_state_table[_JOB_STATE_MAX] = {
[JOB_WAITING] = "waiting",
[JOB_RUNNING] = "running"
@@ -1083,6 +1123,7 @@ DEFINE_STRING_TABLE_LOOKUP(job_type, JobType);
static const char* const job_mode_table[_JOB_MODE_MAX] = {
[JOB_FAIL] = "fail",
[JOB_REPLACE] = "replace",
+ [JOB_REPLACE_IRREVERSIBLY] = "replace-irreversibly",
[JOB_ISOLATE] = "isolate",
[JOB_IGNORE_DEPENDENCIES] = "ignore-dependencies",
[JOB_IGNORE_REQUIREMENTS] = "ignore-requirements"
diff --git a/src/core/job.h b/src/core/job.h
index 3aa49d4b46..d90bc96b76 100644
--- a/src/core/job.h
+++ b/src/core/job.h
@@ -83,6 +83,7 @@ enum JobState {
enum JobMode {
JOB_FAIL, /* Fail if a conflicting job is already queued */
JOB_REPLACE, /* Replace an existing conflicting job */
+ JOB_REPLACE_IRREVERSIBLY, /* Like JOB_REPLACE + produce irreversible jobs */
JOB_ISOLATE, /* Start a unit, and stop all others */
JOB_IGNORE_DEPENDENCIES, /* Ignore both requirement and ordering dependencies */
JOB_IGNORE_REQUIREMENTS, /* Ignore requirement dependencies */
@@ -161,6 +162,7 @@ struct Job {
bool sent_dbus_new_signal:1;
bool ignore_order:1;
bool forgot_bus_clients:1;
+ bool irreversible:1;
};
JobBusClient* job_bus_client_new(DBusConnection *connection, const char *name);
@@ -181,22 +183,22 @@ void job_dependency_free(JobDependency *l);
int job_merge(Job *j, Job *other);
-JobType job_type_lookup_merge(JobType a, JobType b);
+JobType job_type_lookup_merge(JobType a, JobType b) _pure_;
-static inline bool job_type_is_mergeable(JobType a, JobType b) {
+_pure_ static inline bool job_type_is_mergeable(JobType a, JobType b) {
return job_type_lookup_merge(a, b) >= 0;
}
-static inline bool job_type_is_conflicting(JobType a, JobType b) {
+_pure_ static inline bool job_type_is_conflicting(JobType a, JobType b) {
return !job_type_is_mergeable(a, b);
}
-static inline bool job_type_is_superset(JobType a, JobType b) {
+_pure_ static inline bool job_type_is_superset(JobType a, JobType b) {
/* Checks whether operation a is a "superset" of b in its actions */
return a == job_type_lookup_merge(a, b);
}
-bool job_type_is_redundant(JobType a, UnitActiveState b);
+bool job_type_is_redundant(JobType a, UnitActiveState b) _pure_;
/* Collapses a state-dependent job type into a simpler type by observing
* the state of the unit which it is going to be applied to. */
@@ -217,14 +219,16 @@ int job_finish_and_invalidate(Job *j, JobResult result, bool recursive);
char *job_dbus_path(Job *j);
-const char* job_type_to_string(JobType t);
-JobType job_type_from_string(const char *s);
+void job_shutdown_magic(Job *j);
-const char* job_state_to_string(JobState t);
-JobState job_state_from_string(const char *s);
+const char* job_type_to_string(JobType t) _const_;
+JobType job_type_from_string(const char *s) _pure_;
-const char* job_mode_to_string(JobMode t);
-JobMode job_mode_from_string(const char *s);
+const char* job_state_to_string(JobState t) _const_;
+JobState job_state_from_string(const char *s) _pure_;
-const char* job_result_to_string(JobResult t);
-JobResult job_result_from_string(const char *s);
+const char* job_mode_to_string(JobMode t) _const_;
+JobMode job_mode_from_string(const char *s) _pure_;
+
+const char* job_result_to_string(JobResult t) _const_;
+JobResult job_result_from_string(const char *s) _pure_;
diff --git a/src/core/kill.h b/src/core/kill.h
index 3c9b0ab8db..71a0513e84 100644
--- a/src/core/kill.h
+++ b/src/core/kill.h
@@ -26,6 +26,8 @@ typedef struct KillContext KillContext;
#include <stdbool.h>
#include <stdio.h>
+#include "macro.h"
+
typedef enum KillMode {
/* The kill mode is a property of a unit. */
KILL_CONTROL_GROUP = 0,
@@ -53,8 +55,8 @@ typedef enum KillWho {
void kill_context_init(KillContext *c);
void kill_context_dump(KillContext *c, FILE *f, const char *prefix);
-const char *kill_mode_to_string(KillMode k);
-KillMode kill_mode_from_string(const char *s);
+const char *kill_mode_to_string(KillMode k) _const_;
+KillMode kill_mode_from_string(const char *s) _pure_;
-const char *kill_who_to_string(KillWho k);
-KillWho kill_who_from_string(const char *s);
+const char *kill_who_to_string(KillWho k) _const_;
+KillWho kill_who_from_string(const char *s) _pure_;
diff --git a/src/core/killall.c b/src/core/killall.c
index 55200ffa48..a0f57455fb 100644
--- a/src/core/killall.c
+++ b/src/core/killall.c
@@ -22,16 +22,17 @@
#include <sys/wait.h>
#include <signal.h>
#include <errno.h>
+#include <unistd.h>
#include "util.h"
#include "def.h"
#include "killall.h"
+#include "set.h"
-#define TIMEOUT_USEC (5 * USEC_PER_SEC)
+#define TIMEOUT_USEC (10 * USEC_PER_SEC)
static bool ignore_proc(pid_t pid) {
- char buf[PATH_MAX];
- FILE *f;
+ _cleanup_fclose_ FILE *f = NULL;
char c;
size_t count;
uid_t uid;
@@ -49,15 +50,11 @@ static bool ignore_proc(pid_t pid) {
if (uid != 0)
return false;
- snprintf(buf, sizeof(buf), "/proc/%lu/cmdline", (unsigned long) pid);
- char_array_0(buf);
-
- f = fopen(buf, "re");
+ f = fopen(procfs_file_alloca(pid, "cmdline"), "re");
if (!f)
return true; /* not really, but has the desired effect */
count = fread(&c, 1, 1, f);
- fclose(f);
/* Kernel threads have an empty cmdline */
if (count <= 0)
@@ -73,38 +70,68 @@ static bool ignore_proc(pid_t pid) {
return false;
}
-static void wait_for_children(int n_processes, sigset_t *mask) {
+static void wait_for_children(Set *pids, sigset_t *mask) {
usec_t until;
assert(mask);
+ if (set_isempty(pids))
+ return;
+
until = now(CLOCK_MONOTONIC) + TIMEOUT_USEC;
for (;;) {
struct timespec ts;
int k;
usec_t n;
+ void *p;
+ Iterator i;
+ /* First, let the kernel inform us about killed
+ * children. Most processes will probably be our
+ * children, but some are not (might be our
+ * grandchildren instead...). */
for (;;) {
- pid_t pid = waitpid(-1, NULL, WNOHANG);
+ pid_t pid;
+ pid = waitpid(-1, NULL, WNOHANG);
if (pid == 0)
break;
+ if (pid < 0) {
+ if (errno == ECHILD)
+ break;
- if (pid < 0 && errno == ECHILD)
+ log_error("waitpid() failed: %m");
return;
+ }
+
+ set_remove(pids, ULONG_TO_PTR(pid));
+ }
- if (n_processes > 0)
- if (--n_processes == 0)
- return;
+ /* Now explicitly check who might be remaining, who
+ * might not be our child. */
+ SET_FOREACH(p, pids, i) {
+
+ /* We misuse getpgid as a check whether a
+ * process still exists. */
+ if (getpgid((pid_t) PTR_TO_ULONG(p)) >= 0)
+ continue;
+
+ if (errno != ESRCH)
+ continue;
+
+ set_remove(pids, p);
}
+ if (set_isempty(pids))
+ return;
+
n = now(CLOCK_MONOTONIC);
if (n >= until)
return;
timespec_store(&ts, until - n);
-
- if ((k = sigtimedwait(mask, NULL, &ts)) != SIGCHLD) {
+ k = sigtimedwait(mask, NULL, &ts);
+ if (k != SIGCHLD) {
if (k < 0 && errno != EAGAIN) {
log_error("sigtimedwait() failed: %m");
@@ -117,10 +144,9 @@ static void wait_for_children(int n_processes, sigset_t *mask) {
}
}
-static int killall(int sig) {
- DIR *dir;
+static int killall(int sig, Set *pids) {
+ _cleanup_closedir_ DIR *dir = NULL;
struct dirent *d;
- unsigned int n_processes = 0;
dir = opendir("/proc");
if (!dir)
@@ -139,20 +165,29 @@ static int killall(int sig) {
if (ignore_proc(pid))
continue;
- if (kill(pid, sig) >= 0)
- n_processes++;
- else if (errno != ENOENT)
+ if (sig == SIGKILL) {
+ _cleanup_free_ char *s;
+
+ get_process_comm(pid, &s);
+ log_notice("Sending SIGKILL to PID %lu (%s).", (unsigned long) pid, strna(s));
+ }
+
+ if (kill(pid, sig) >= 0) {
+ if (pids)
+ set_put(pids, ULONG_TO_PTR((unsigned long) pid));
+ } else if (errno != ENOENT)
log_warning("Could not kill %d: %m", pid);
}
- closedir(dir);
-
- return n_processes;
+ return set_size(pids);
}
void broadcast_signal(int sig, bool wait_for_exit) {
sigset_t mask, oldmask;
- int n_processes;
+ Set *pids = NULL;
+
+ if (wait_for_exit)
+ pids = set_new(trivial_hash_func, trivial_compare_func);
assert_se(sigemptyset(&mask) == 0);
assert_se(sigaddset(&mask, SIGCHLD) == 0);
@@ -161,17 +196,15 @@ void broadcast_signal(int sig, bool wait_for_exit) {
if (kill(-1, SIGSTOP) < 0 && errno != ESRCH)
log_warning("kill(-1, SIGSTOP) failed: %m");
- n_processes = killall(sig);
+ killall(sig, pids);
if (kill(-1, SIGCONT) < 0 && errno != ESRCH)
log_warning("kill(-1, SIGCONT) failed: %m");
- if (n_processes <= 0)
- goto finish;
-
if (wait_for_exit)
- wait_for_children(n_processes, &mask);
+ wait_for_children(pids, &mask);
+
+ assert_se(sigprocmask(SIG_SETMASK, &oldmask, NULL) == 0);
-finish:
- sigprocmask(SIG_SETMASK, &oldmask, NULL);
+ set_free(pids);
}
diff --git a/src/core/kmod-setup.c b/src/core/kmod-setup.c
index 20ab232374..e4885fb212 100644
--- a/src/core/kmod-setup.c
+++ b/src/core/kmod-setup.c
@@ -30,66 +30,72 @@
#include "kmod-setup.h"
-typedef struct Kmodule {
- const char *name;
- const char *directory;
- bool (*condition_fn)(void);
-} KModule;
-
-static const KModule kmod_table[] = {
- { "autofs4", "/sys/class/misc/autofs", NULL } ,
- { "ipv6", "/sys/module/ipv6", NULL },
- { "unix", "/proc/net/unix", NULL } ,
-};
-
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat-nonliteral"
-static void systemd_kmod_log(void *data, int priority, const char *file, int line,
- const char *fn, const char *format, va_list args)
-{
+
+static void systemd_kmod_log(
+ void *data,
+ int priority,
+ const char *file, int line,
+ const char *fn,
+ const char *format,
+ va_list args) {
+
/* library logging is enabled at debug only */
log_metav(LOG_DEBUG, file, line, fn, format, args);
}
+
#pragma GCC diagnostic pop
int kmod_setup(void) {
- unsigned i;
+
+ static const char kmod_table[] =
+ /* This one we need to load explicitly, since
+ * auto-loading on use doesn't work before udev
+ * created the ghost device nodes, and we need it
+ * earlier than that. */
+ "autofs4\0" "/sys/class/misc/autofs\0"
+
+ /* This one we need to load explicitly, since
+ * auto-loading of IPv6 is not done when we try to
+ * configure ::1 on the loopback device. */
+ "ipv6\0" "/sys/module/ipv6\0"
+
+ "unix\0" "/proc/net/unix\0";
+
struct kmod_ctx *ctx = NULL;
- struct kmod_module *mod;
- int err;
+ const char *name, *path;
+ int r;
- for (i = 0; i < ELEMENTSOF(kmod_table); i += 2) {
- if (kmod_table[i].condition_fn && !kmod_table[i].condition_fn())
- continue;
+ NULSTR_FOREACH_PAIR(name, path, kmod_table) {
+ struct kmod_module *mod;
- if (access(kmod_table[i].directory, F_OK) >= 0)
+ if (access(path, F_OK) >= 0)
continue;
log_debug("Your kernel apparently lacks built-in %s support. Might be a good idea to compile it in. "
"We'll now try to work around this by loading the module...",
- kmod_table[i].name);
+ name);
if (!ctx) {
ctx = kmod_new(NULL, NULL);
- if (!ctx) {
- log_error("Failed to allocate memory for kmod");
- return -ENOMEM;
- }
+ if (!ctx)
+ return log_oom();
kmod_set_log_fn(ctx, systemd_kmod_log, NULL);
kmod_load_resources(ctx);
}
- err = kmod_module_new_from_name(ctx, kmod_table[i].name, &mod);
- if (err < 0) {
- log_error("Failed to lookup module '%s'", kmod_table[i].name);
+ r = kmod_module_new_from_name(ctx, name, &mod);
+ if (r < 0) {
+ log_error("Failed to lookup module '%s'", name);
continue;
}
- err = kmod_module_probe_insert_module(mod, KMOD_PROBE_APPLY_BLACKLIST, NULL, NULL, NULL, NULL);
- if (err == 0)
+ r = kmod_module_probe_insert_module(mod, KMOD_PROBE_APPLY_BLACKLIST, NULL, NULL, NULL, NULL);
+ if (r == 0)
log_info("Inserted module '%s'", kmod_module_get_name(mod));
- else if (err == KMOD_PROBE_APPLY_BLACKLIST)
+ else if (r == KMOD_PROBE_APPLY_BLACKLIST)
log_info("Module '%s' is blacklisted", kmod_module_get_name(mod));
else
log_error("Failed to insert module '%s'", kmod_module_get_name(mod));
diff --git a/src/core/load-dropin.c b/src/core/load-dropin.c
index 86f81c7484..a877e66098 100644
--- a/src/core/load-dropin.c
+++ b/src/core/load-dropin.c
@@ -27,51 +27,78 @@
#include "log.h"
#include "strv.h"
#include "unit-name.h"
+#include "conf-parser.h"
+#include "load-fragment.h"
+#include "conf-files.h"
-static int iterate_dir(Unit *u, const char *path, UnitDependency dependency) {
- DIR *d;
- struct dirent *de;
+static int iterate_dir(
+ Unit *u,
+ const char *path,
+ UnitDependency dependency,
+ char ***strv) {
+
+ _cleanup_closedir_ DIR *d = NULL;
int r;
assert(u);
assert(path);
+ /* The config directories are special, since the order of the
+ * drop-ins matters */
+ if (dependency < 0) {
+ r = strv_extend(strv, path);
+ if (r < 0)
+ return log_oom();
+
+ return 0;
+ }
+
d = opendir(path);
if (!d) {
-
if (errno == ENOENT)
return 0;
return -errno;
}
- while ((de = readdir(d))) {
- char *f;
+ for (;;) {
+ struct dirent *de;
+ union dirent_storage buf;
+ _cleanup_free_ char *f = NULL;
+ int k;
+
+ k = readdir_r(d, &buf.de, &de);
+ if (k != 0) {
+ log_error("Failed to read directory %s: %s", path, strerror(k));
+ return -k;
+ }
+
+ if (!de)
+ break;
if (ignore_file(de->d_name))
continue;
f = strjoin(path, "/", de->d_name, NULL);
- if (!f) {
- r = -ENOMEM;
- goto finish;
- }
+ if (!f)
+ return log_oom();
r = unit_add_dependency_by_name(u, dependency, de->d_name, f, true);
- free(f);
-
if (r < 0)
log_error("Cannot add dependency %s to %s, ignoring: %s", de->d_name, u->id, strerror(-r));
}
- r = 0;
-
-finish:
- closedir(d);
- return r;
+ return 0;
}
-static int process_dir(Unit *u, const char *unit_path, const char *name, const char *suffix, UnitDependency dependency) {
+static int process_dir(
+ Unit *u,
+ const char *unit_path,
+ const char *name,
+ const char *suffix,
+ UnitDependency dependency,
+ char ***strv) {
+
int r;
char *path;
@@ -82,13 +109,13 @@ static int process_dir(Unit *u, const char *unit_path, const char *name, const c
path = strjoin(unit_path, "/", name, suffix, NULL);
if (!path)
- return -ENOMEM;
+ return log_oom();
if (u->manager->unit_path_cache &&
!set_get(u->manager->unit_path_cache, path))
r = 0;
else
- r = iterate_dir(u, path, dependency);
+ r = iterate_dir(u, path, dependency, strv);
free(path);
if (r < 0)
@@ -100,19 +127,19 @@ static int process_dir(Unit *u, const char *unit_path, const char *name, const c
template = unit_name_template(name);
if (!template)
- return -ENOMEM;
+ return log_oom();
path = strjoin(unit_path, "/", template, suffix, NULL);
free(template);
if (!path)
- return -ENOMEM;
+ return log_oom();
if (u->manager->unit_path_cache &&
!set_get(u->manager->unit_path_cache, path))
r = 0;
else
- r = iterate_dir(u, path, dependency);
+ r = iterate_dir(u, path, dependency, strv);
free(path);
if (r < 0)
@@ -122,9 +149,43 @@ static int process_dir(Unit *u, const char *unit_path, const char *name, const c
return 0;
}
-int unit_load_dropin(Unit *u) {
+char **unit_find_dropin_paths(Unit *u) {
+ _cleanup_strv_free_ char **strv = NULL;
+ char **configs = NULL;
Iterator i;
char *t;
+ int r;
+
+ assert(u);
+
+ SET_FOREACH(t, u->names, i) {
+ char **p;
+
+ STRV_FOREACH(p, u->manager->lookup_paths.unit_path) {
+ /* This loads the drop-in config snippets */
+ r = process_dir(u, *p, t, ".d", _UNIT_DEPENDENCY_INVALID, &strv);
+ if (r < 0)
+ return NULL;
+ }
+ }
+
+ if (strv_isempty(strv))
+ return NULL;
+
+ r = conf_files_list_strv(&configs, ".conf", NULL, (const char**) strv);
+ if (r < 0) {
+ log_error("Failed to get list of configuration files: %s", strerror(-r));
+ strv_free(configs);
+ return NULL;
+ }
+
+ return configs;
+}
+
+int unit_load_dropin(Unit *u) {
+ Iterator i;
+ char *t, **f;
+ int r;
assert(u);
@@ -134,17 +195,29 @@ int unit_load_dropin(Unit *u) {
char **p;
STRV_FOREACH(p, u->manager->lookup_paths.unit_path) {
- int r;
-
- r = process_dir(u, *p, t, ".wants", UNIT_WANTS);
+ r = process_dir(u, *p, t, ".wants", UNIT_WANTS, NULL);
if (r < 0)
return r;
- r = process_dir(u, *p, t, ".requires", UNIT_REQUIRES);
+ r = process_dir(u, *p, t, ".requires", UNIT_REQUIRES, NULL);
if (r < 0)
return r;
}
}
+ u->dropin_paths = unit_find_dropin_paths(u);
+ if (! u->dropin_paths)
+ return 0;
+
+ STRV_FOREACH(f, u->dropin_paths) {
+ r = config_parse(u->id, *f, NULL,
+ UNIT_VTABLE(u)->sections, config_item_perf_lookup,
+ (void*) load_fragment_gperf_lookup, false, false, u);
+ if (r < 0)
+ return r;
+ }
+
+ u->dropin_mtime = now(CLOCK_REALTIME);
+
return 0;
}
diff --git a/src/core/load-dropin.h b/src/core/load-dropin.h
index 1d2fafeee6..fd551179e2 100644
--- a/src/core/load-dropin.h
+++ b/src/core/load-dropin.h
@@ -25,4 +25,5 @@
/* Read service data supplementary drop-in directories */
+char **unit_find_dropin_paths(Unit *u);
int unit_load_dropin(Unit *u);
diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4
index 7fba0cfb77..4e1454ee6c 100644
--- a/src/core/load-fragment-gperf.gperf.m4
+++ b/src/core/load-fragment-gperf.gperf.m4
@@ -31,7 +31,7 @@ $1.CPUSchedulingPriority, config_parse_exec_cpu_sched_prio, 0,
$1.CPUSchedulingResetOnFork, config_parse_bool, 0, offsetof($1, exec_context.cpu_sched_reset_on_fork)
$1.CPUAffinity, config_parse_exec_cpu_affinity, 0, offsetof($1, exec_context)
$1.UMask, config_parse_mode, 0, offsetof($1, exec_context.umask)
-$1.Environment, config_parse_unit_strv_printf, 0, offsetof($1, exec_context.environment)
+$1.Environment, config_parse_environ, 0, offsetof($1, exec_context.environment)
$1.EnvironmentFile, config_parse_unit_env_file, 0, offsetof($1, exec_context.environment_files)
$1.StandardInput, config_parse_input, 0, offsetof($1, exec_context.std_input)
$1.StandardOutput, config_parse_output, 0, offsetof($1, exec_context.std_output)
@@ -48,7 +48,7 @@ $1.Capabilities, config_parse_exec_capabilities, 0,
$1.SecureBits, config_parse_exec_secure_bits, 0, offsetof($1, exec_context)
$1.CapabilityBoundingSet, config_parse_bounding_set, 0, offsetof($1, exec_context.capability_bounding_set_drop)
$1.TimerSlackNSec, config_parse_nsec, 0, offsetof($1, exec_context.timer_slack_nsec)
-$1.NoNewPrivileges config_parse_bool, 0, offsetof($1, exec_context.no_new_privileges)
+$1.NoNewPrivileges, config_parse_bool, 0, offsetof($1, exec_context.no_new_privileges)
$1.SystemCallFilter, config_parse_syscall_filter, 0, offsetof($1, exec_context)
$1.LimitCPU, config_parse_limit, RLIMIT_CPU, offsetof($1, exec_context.rlimit)
$1.LimitFSIZE, config_parse_limit, RLIMIT_FSIZE, offsetof($1, exec_context.rlimit)
@@ -68,14 +68,14 @@ $1.LimitRTPRIO, config_parse_limit, RLIMIT_RTPR
$1.LimitRTTIME, config_parse_limit, RLIMIT_RTTIME, offsetof($1, exec_context.rlimit)
$1.ControlGroup, config_parse_unit_cgroup, 0, 0
$1.ControlGroupAttribute, config_parse_unit_cgroup_attr, 0, 0
-$1.CPUShares, config_parse_unit_cpu_shares, 0, 0
-$1.MemoryLimit, config_parse_unit_memory_limit, 0, 0
-$1.MemorySoftLimit, config_parse_unit_memory_limit, 0, 0
-$1.DeviceAllow, config_parse_unit_device_allow, 0, 0
-$1.DeviceDeny, config_parse_unit_device_allow, 0, 0
-$1.BlockIOWeight, config_parse_unit_blkio_weight, 0, 0
-$1.BlockIOReadBandwidth, config_parse_unit_blkio_bandwidth, 0, 0
-$1.BlockIOWriteBandwidth, config_parse_unit_blkio_bandwidth, 0, 0
+$1.CPUShares, config_parse_unit_cgroup_attr_pretty, 0, 0
+$1.MemoryLimit, config_parse_unit_cgroup_attr_pretty, 0, 0
+$1.MemorySoftLimit, config_parse_unit_cgroup_attr_pretty, 0, 0
+$1.DeviceAllow, config_parse_unit_cgroup_attr_pretty, 0, 0
+$1.DeviceDeny, config_parse_unit_cgroup_attr_pretty, 0, 0
+$1.BlockIOWeight, config_parse_unit_cgroup_attr_pretty, 0, 0
+$1.BlockIOReadBandwidth, config_parse_unit_cgroup_attr_pretty, 0, 0
+$1.BlockIOWriteBandwidth, config_parse_unit_cgroup_attr_pretty, 0, 0
$1.ReadWriteDirectories, config_parse_path_strv, 0, offsetof($1, exec_context.read_write_dirs)
$1.ReadOnlyDirectories, config_parse_path_strv, 0, offsetof($1, exec_context.read_only_dirs)
$1.InaccessibleDirectories, config_parse_path_strv, 0, offsetof($1, exec_context.inaccessible_dirs)
@@ -90,7 +90,7 @@ $1.ControlGroupModify, config_parse_bool, 0,
$1.ControlGroupPersistent, config_parse_tristate, 0, offsetof($1, exec_context.control_group_persistent)'
)m4_dnl
m4_define(`KILL_CONTEXT_CONFIG_ITEMS',
-`$1.SendSIGKILL, config_parse_bool, 0, offsetof($1, kill_context.send_sigkill)
+`$1.SendSIGKILL, config_parse_bool, 0, offsetof($1, kill_context.send_sigkill)
$1.KillMode, config_parse_kill_mode, 0, offsetof($1, kill_context.kill_mode)
$1.KillSignal, config_parse_kill_signal, 0, offsetof($1, kill_context.kill_signal)'
)m4_dnl
@@ -122,7 +122,7 @@ Unit.DefaultDependencies, config_parse_bool, 0,
Unit.OnFailureIsolate, config_parse_bool, 0, offsetof(Unit, on_failure_isolate)
Unit.IgnoreOnIsolate, config_parse_bool, 0, offsetof(Unit, ignore_on_isolate)
Unit.IgnoreOnSnapshot, config_parse_bool, 0, offsetof(Unit, ignore_on_snapshot)
-Unit.JobTimeoutSec, config_parse_usec, 0, offsetof(Unit, job_timeout)
+Unit.JobTimeoutSec, config_parse_sec, 0, offsetof(Unit, job_timeout)
Unit.ConditionPathExists, config_parse_unit_condition_path, CONDITION_PATH_EXISTS, 0
Unit.ConditionPathExistsGlob, config_parse_unit_condition_path, CONDITION_PATH_EXISTS_GLOB, 0
Unit.ConditionPathIsDirectory, config_parse_unit_condition_path, CONDITION_PATH_IS_DIRECTORY, 0
@@ -147,12 +147,12 @@ Service.ExecStartPost, config_parse_exec, SERVICE_EXE
Service.ExecReload, config_parse_exec, SERVICE_EXEC_RELOAD, offsetof(Service, exec_command)
Service.ExecStop, config_parse_exec, SERVICE_EXEC_STOP, offsetof(Service, exec_command)
Service.ExecStopPost, config_parse_exec, SERVICE_EXEC_STOP_POST, offsetof(Service, exec_command)
-Service.RestartSec, config_parse_usec, 0, offsetof(Service, restart_usec)
+Service.RestartSec, config_parse_sec, 0, offsetof(Service, restart_usec)
Service.TimeoutSec, config_parse_service_timeout, 0, offsetof(Service, timeout_start_usec)
Service.TimeoutStartSec, config_parse_service_timeout, 0, offsetof(Service, timeout_start_usec)
Service.TimeoutStopSec, config_parse_service_timeout, 0, offsetof(Service, timeout_stop_usec)
-Service.WatchdogSec, config_parse_usec, 0, offsetof(Service, watchdog_usec)
-Service.StartLimitInterval, config_parse_usec, 0, offsetof(Service, start_limit.interval)
+Service.WatchdogSec, config_parse_sec, 0, offsetof(Service, watchdog_usec)
+Service.StartLimitInterval, config_parse_sec, 0, offsetof(Service, start_limit.interval)
Service.StartLimitBurst, config_parse_unsigned, 0, offsetof(Service, start_limit.burst)
Service.StartLimitAction, config_parse_start_limit_action, 0, offsetof(Service, start_limit_action)
Service.Type, config_parse_service_type, 0, offsetof(Service, type)
@@ -174,13 +174,13 @@ Service.FsckPassNo, config_parse_fsck_passno, 0,
EXEC_CONTEXT_CONFIG_ITEMS(Service)m4_dnl
KILL_CONTEXT_CONFIG_ITEMS(Service)m4_dnl
m4_dnl
-Socket.ListenStream, config_parse_socket_listen, 0, 0
-Socket.ListenDatagram, config_parse_socket_listen, 0, 0
-Socket.ListenSequentialPacket, config_parse_socket_listen, 0, 0
-Socket.ListenFIFO, config_parse_socket_listen, 0, 0
-Socket.ListenNetlink, config_parse_socket_listen, 0, 0
-Socket.ListenSpecial, config_parse_socket_listen, 0, 0
-Socket.ListenMessageQueue, config_parse_socket_listen, 0, 0
+Socket.ListenStream, config_parse_socket_listen, SOCKET_SOCKET, 0
+Socket.ListenDatagram, config_parse_socket_listen, SOCKET_SOCKET, 0
+Socket.ListenSequentialPacket, config_parse_socket_listen, SOCKET_SOCKET, 0
+Socket.ListenFIFO, config_parse_socket_listen, SOCKET_FIFO, 0
+Socket.ListenNetlink, config_parse_socket_listen, SOCKET_SOCKET, 0
+Socket.ListenSpecial, config_parse_socket_listen, SOCKET_SPECIAL, 0
+Socket.ListenMessageQueue, config_parse_socket_listen, SOCKET_MQUEUE, 0
Socket.BindIPv6Only, config_parse_socket_bind, 0, 0,
Socket.Backlog, config_parse_unsigned, 0, offsetof(Socket, backlog)
Socket.BindToDevice, config_parse_socket_bindtodevice, 0, 0
@@ -188,7 +188,7 @@ Socket.ExecStartPre, config_parse_exec, SOCKET_EXEC
Socket.ExecStartPost, config_parse_exec, SOCKET_EXEC_START_POST, offsetof(Socket, exec_command)
Socket.ExecStopPre, config_parse_exec, SOCKET_EXEC_STOP_PRE, offsetof(Socket, exec_command)
Socket.ExecStopPost, config_parse_exec, SOCKET_EXEC_STOP_POST, offsetof(Socket, exec_command)
-Socket.TimeoutSec, config_parse_usec, 0, offsetof(Socket, timeout_usec)
+Socket.TimeoutSec, config_parse_sec, 0, offsetof(Socket, timeout_usec)
Socket.DirectoryMode, config_parse_mode, 0, offsetof(Socket, directory_mode)
Socket.SocketMode, config_parse_mode, 0, offsetof(Socket, socket_mode)
Socket.Accept, config_parse_bool, 0, offsetof(Socket, accept)
@@ -221,7 +221,7 @@ Mount.Where, config_parse_path, 0,
Mount.Options, config_parse_string, 0, offsetof(Mount, parameters_fragment.options)
Mount.Type, config_parse_string, 0, offsetof(Mount, parameters_fragment.fstype)
Mount.FsckPassNo, config_parse_fsck_passno, 0, offsetof(Mount, parameters_fragment.passno)
-Mount.TimeoutSec, config_parse_usec, 0, offsetof(Mount, timeout_usec)
+Mount.TimeoutSec, config_parse_sec, 0, offsetof(Mount, timeout_usec)
Mount.DirectoryMode, config_parse_mode, 0, offsetof(Mount, directory_mode)
EXEC_CONTEXT_CONFIG_ITEMS(Mount)m4_dnl
KILL_CONTEXT_CONFIG_ITEMS(Mount)m4_dnl
@@ -231,7 +231,7 @@ Automount.DirectoryMode, config_parse_mode, 0,
m4_dnl
Swap.What, config_parse_path, 0, offsetof(Swap, parameters_fragment.what)
Swap.Priority, config_parse_int, 0, offsetof(Swap, parameters_fragment.priority)
-Swap.TimeoutSec, config_parse_usec, 0, offsetof(Swap, timeout_usec)
+Swap.TimeoutSec, config_parse_sec, 0, offsetof(Swap, timeout_usec)
EXEC_CONTEXT_CONFIG_ITEMS(Swap)m4_dnl
KILL_CONTEXT_CONFIG_ITEMS(Swap)m4_dnl
m4_dnl
@@ -241,14 +241,14 @@ Timer.OnBootSec, config_parse_timer, 0,
Timer.OnStartupSec, config_parse_timer, 0, 0
Timer.OnUnitActiveSec, config_parse_timer, 0, 0
Timer.OnUnitInactiveSec, config_parse_timer, 0, 0
-Timer.Unit, config_parse_timer_unit, 0, 0
+Timer.Unit, config_parse_trigger_unit, 0, 0
m4_dnl
Path.PathExists, config_parse_path_spec, 0, 0
Path.PathExistsGlob, config_parse_path_spec, 0, 0
Path.PathChanged, config_parse_path_spec, 0, 0
Path.PathModified, config_parse_path_spec, 0, 0
Path.DirectoryNotEmpty, config_parse_path_spec, 0, 0
-Path.Unit, config_parse_path_unit, 0, 0
+Path.Unit, config_parse_trigger_unit, 0, 0
Path.MakeDirectory, config_parse_bool, 0, offsetof(Path, make_directory)
Path.DirectoryMode, config_parse_mode, 0, offsetof(Path, directory_mode)
m4_dnl The [Install] section is ignored here.
diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c
index e35fdbc5ec..e2015ed58f 100644
--- a/src/core/load-fragment.c
+++ b/src/core/load-fragment.c
@@ -34,6 +34,8 @@
#include <sys/time.h>
#include <sys/resource.h>
+#include <systemd/sd-messages.h>
+
#include "unit.h"
#include "strv.h"
#include "conf-parser.h"
@@ -44,36 +46,39 @@
#include "missing.h"
#include "unit-name.h"
#include "unit-printf.h"
-#include "bus-errors.h"
+#include "dbus-common.h"
#include "utf8.h"
#include "path-util.h"
#include "syscall-list.h"
+#include "env-util.h"
#ifndef HAVE_SYSV_COMPAT
-int config_parse_warn_compat(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- log_debug("[%s:%u] Support for option %s= has been disabled at compile time and is ignored", filename, line, lvalue);
+int config_parse_warn_compat(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ log_syntax(unit, LOG_DEBUG, filename, line, EINVAL,
+ "Support for option %s= has been disabled at compile time and is ignored",
+ lvalue);
return 0;
}
#endif
-int config_parse_unit_deps(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
+int config_parse_unit_deps(const char* unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
UnitDependency d = ltype;
Unit *u = userdata;
@@ -86,39 +91,38 @@ int config_parse_unit_deps(
assert(rvalue);
FOREACH_WORD_QUOTED(w, l, rvalue, state) {
- char _cleanup_free_ *t = NULL, *k = NULL;
+ _cleanup_free_ char *t = NULL, *k = NULL;
int r;
t = strndup(w, l);
if (!t)
- return -ENOMEM;
+ return log_oom();
k = unit_name_printf(u, t);
if (!k)
- return -ENOMEM;
+ return log_oom();
r = unit_add_dependency_by_name(u, d, k, NULL, true);
if (r < 0)
- log_error("[%s:%u] Failed to add dependency on %s, ignoring: %s",
- filename, line, k, strerror(-r));
+ log_syntax(unit, LOG_ERR, filename, line, -r,
+ "Failed to add dependency on %s, ignoring: %s", k, strerror(-r));
}
return 0;
}
-int config_parse_unit_string_printf(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
+int config_parse_unit_string_printf(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
Unit *u = userdata;
- char *k;
- int r;
+ _cleanup_free_ char *k = NULL;
assert(filename);
assert(lvalue);
@@ -127,27 +131,25 @@ int config_parse_unit_string_printf(
k = unit_full_printf(u, rvalue);
if (!k)
- return -ENOMEM;
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ "Failed to resolve unit specifiers on %s. Ignoring.", rvalue);
- r = config_parse_string(filename, line, section, lvalue, ltype, k, data, userdata);
- free (k);
-
- return r;
+ return config_parse_string(unit, filename, line, section, lvalue, ltype,
+ k ? k : rvalue, data, userdata);
}
-int config_parse_unit_strv_printf(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
+int config_parse_unit_strv_printf(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
Unit *u = userdata;
- char *k;
- int r;
+ _cleanup_free_ char *k = NULL;
assert(filename);
assert(lvalue);
@@ -156,27 +158,25 @@ int config_parse_unit_strv_printf(
k = unit_full_printf(u, rvalue);
if (!k)
- return -ENOMEM;
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ "Failed to resolve unit specifiers on %s. Ignoring.", rvalue);
- r = config_parse_strv(filename, line, section, lvalue, ltype, k, data, userdata);
- free(k);
-
- return r;
+ return config_parse_strv(unit, filename, line, section, lvalue, ltype,
+ k ? k : rvalue, data, userdata);
}
-int config_parse_unit_path_printf(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
+int config_parse_unit_path_printf(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
Unit *u = userdata;
- char *k;
- int r;
+ _cleanup_free_ char *k = NULL;
assert(filename);
assert(lvalue);
@@ -185,23 +185,22 @@ int config_parse_unit_path_printf(
k = unit_full_printf(u, rvalue);
if (!k)
- return log_oom();
-
- r = config_parse_path(filename, line, section, lvalue, ltype, k, data, userdata);
- free(k);
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ "Failed to resolve unit specifiers on %s. Ignoring.", rvalue);
- return r;
+ return config_parse_path(unit, filename, line, section, lvalue, ltype,
+ k ? k : rvalue, data, userdata);
}
-int config_parse_socket_listen(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
+int config_parse_socket_listen(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
SocketPort *p, *tail;
Socket *s;
@@ -213,67 +212,64 @@ int config_parse_socket_listen(
s = SOCKET(data);
+ if (isempty(rvalue)) {
+ /* An empty assignment removes all ports */
+ socket_free_ports(s);
+ return 0;
+ }
+
p = new0(SocketPort, 1);
if (!p)
- return -ENOMEM;
-
- if (streq(lvalue, "ListenFIFO")) {
- p->type = SOCKET_FIFO;
-
- if (!(p->path = unit_full_printf(UNIT(s), rvalue))) {
- free(p);
- return -ENOMEM;
- }
-
- path_kill_slashes(p->path);
-
- } else if (streq(lvalue, "ListenSpecial")) {
- p->type = SOCKET_SPECIAL;
+ return log_oom();
- if (!(p->path = unit_full_printf(UNIT(s), rvalue))) {
- free(p);
- return -ENOMEM;
- }
-
- path_kill_slashes(p->path);
-
- } else if (streq(lvalue, "ListenMessageQueue")) {
-
- p->type = SOCKET_MQUEUE;
-
- if (!(p->path = unit_full_printf(UNIT(s), rvalue))) {
- free(p);
- return -ENOMEM;
+ if (ltype != SOCKET_SOCKET) {
+
+ p->type = ltype;
+ p->path = unit_full_printf(UNIT(s), rvalue);
+ if (!p->path) {
+ p->path = strdup(rvalue);
+ if (!p->path) {
+ free(p);
+ return log_oom();
+ } else
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ "Failed to resolve unit specifiers on %s. Ignoring.", rvalue);
}
path_kill_slashes(p->path);
} else if (streq(lvalue, "ListenNetlink")) {
- char *k;
+ _cleanup_free_ char *k = NULL;
int r;
p->type = SOCKET_SOCKET;
k = unit_full_printf(UNIT(s), rvalue);
- r = socket_address_parse_netlink(&p->address, k);
- free(k);
+ if (!k)
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ "Failed to resolve unit specifiers on %s. Ignoring.", rvalue);
+ r = socket_address_parse_netlink(&p->address, k ? k : rvalue);
if (r < 0) {
- log_error("[%s:%u] Failed to parse address value, ignoring: %s", filename, line, rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ "Failed to parse address value, ignoring: %s", rvalue);
free(p);
return 0;
}
} else {
- char *k;
+ _cleanup_free_ char *k = NULL;
int r;
p->type = SOCKET_SOCKET;
k = unit_full_printf(UNIT(s), rvalue);
- r = socket_address_parse(&p->address, k);
- free(k);
+ if (!k)
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ "Failed to resolve unit specifiers on %s. Ignoring.", rvalue);
+ r = socket_address_parse(&p->address, k ? k : rvalue);
if (r < 0) {
- log_error("[%s:%u] Failed to parse address value, ignoring: %s", filename, line, rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ "Failed to parse address value, ignoring: %s", rvalue);
free(p);
return 0;
}
@@ -288,7 +284,8 @@ int config_parse_socket_listen(
}
if (socket_address_family(&p->address) != AF_LOCAL && p->address.type == SOCK_SEQPACKET) {
- log_error("[%s:%u] Address family not supported, ignoring: %s", filename, line, rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, ENOTSUP,
+ "Address family not supported, ignoring: %s", rvalue);
free(p);
return 0;
}
@@ -305,15 +302,15 @@ int config_parse_socket_listen(
return 0;
}
-int config_parse_socket_bind(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
+int config_parse_socket_bind(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
Socket *s;
SocketAddressBindIPv6Only b;
@@ -331,7 +328,8 @@ int config_parse_socket_bind(
r = parse_boolean(rvalue);
if (r < 0) {
- log_error("[%s:%u] Failed to parse bind IPv6 only value, ignoring: %s", filename, line, rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ "Failed to parse bind IPv6 only value, ignoring: %s", rvalue);
return 0;
}
@@ -342,31 +340,34 @@ int config_parse_socket_bind(
return 0;
}
-int config_parse_exec_nice(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
+int config_parse_exec_nice(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
ExecContext *c = data;
- int priority;
+ int priority, r;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
- if (safe_atoi(rvalue, &priority) < 0) {
- log_error("[%s:%u] Failed to parse nice priority, ignoring: %s. ", filename, line, rvalue);
+ r = safe_atoi(rvalue, &priority);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, -r,
+ "Failed to parse nice priority, ignoring: %s. ", rvalue);
return 0;
}
if (priority < PRIO_MIN || priority >= PRIO_MAX) {
- log_error("[%s:%u] Nice priority out of range, ignoring: %s", filename, line, rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, ERANGE,
+ "Nice priority out of range, ignoring: %s", rvalue);
return 0;
}
@@ -376,31 +377,34 @@ int config_parse_exec_nice(
return 0;
}
-int config_parse_exec_oom_score_adjust(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
+int config_parse_exec_oom_score_adjust(const char* unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
ExecContext *c = data;
- int oa;
+ int oa, r;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
- if (safe_atoi(rvalue, &oa) < 0) {
- log_error("[%s:%u] Failed to parse the OOM score adjust value, ignoring: %s", filename, line, rvalue);
+ r = safe_atoi(rvalue, &oa);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, -r,
+ "Failed to parse the OOM score adjust value, ignoring: %s", rvalue);
return 0;
}
if (oa < OOM_SCORE_ADJ_MIN || oa > OOM_SCORE_ADJ_MAX) {
- log_error("[%s:%u] OOM score adjust value out of range, ignoring: %s", filename, line, rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, ERANGE,
+ "OOM score adjust value out of range, ignoring: %s", rvalue);
return 0;
}
@@ -410,15 +414,15 @@ int config_parse_exec_oom_score_adjust(
return 0;
}
-int config_parse_exec(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
+int config_parse_exec(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
ExecCommand **e = data, *nce;
char *path, **n;
@@ -430,12 +434,18 @@ int config_parse_exec(
assert(rvalue);
assert(e);
+ e += ltype;
+
+ if (isempty(rvalue)) {
+ /* An empty assignment resets the list */
+ exec_command_free_list(*e);
+ *e = NULL;
+ return 0;
+ }
+
/* We accept an absolute path as first argument, or
* alternatively an absolute prefixed with @ to allow
* overriding of argv[0]. */
-
- e += ltype;
-
for (;;) {
int i;
char *w;
@@ -465,14 +475,14 @@ int config_parse_exec(
}
if (*rvalue != '/') {
- log_error("[%s:%u] Executable path is not absolute, ignoring: %s",
- filename, line, rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ "Executable path is not absolute, ignoring: %s", rvalue);
return 0;
}
k = 0;
FOREACH_WORD_QUOTED(w, l, rvalue, state) {
- if (strncmp(w, ";", MAX(l, 1U)) == 0)
+ if (strneq(w, ";", MAX(l, 1U)))
break;
k++;
@@ -480,13 +490,13 @@ int config_parse_exec(
n = new(char*, k + !honour_argv0);
if (!n)
- return -ENOMEM;
+ return log_oom();
k = 0;
FOREACH_WORD_QUOTED(w, l, rvalue, state) {
- if (strncmp(w, ";", MAX(l, 1U)) == 0)
+ if (strneq(w, ";", MAX(l, 1U)))
break;
- else if (strncmp(w, "\\;", MAX(l, 1U)) == 0)
+ else if (strneq(w, "\\;", MAX(l, 1U)))
w ++;
if (honour_argv0 && w == rvalue) {
@@ -494,12 +504,14 @@ int config_parse_exec(
path = strndup(w, l);
if (!path) {
- r = -ENOMEM;
+ r = log_oom();
goto fail;
}
if (!utf8_is_valid(path)) {
- log_error("[%s:%u] Path is not UTF-8 clean, ignoring assignment: %s", filename, line, rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ "Path is not UTF-8 clean, ignoring assignment: %s",
+ rvalue);
r = 0;
goto fail;
}
@@ -509,12 +521,14 @@ int config_parse_exec(
c = n[k++] = cunescape_length(w, l);
if (!c) {
- r = -ENOMEM;
+ r = log_oom();
goto fail;
}
if (!utf8_is_valid(c)) {
- log_error("[%s:%u] Path is not UTF-8 clean, ignoring assignment: %s", filename, line, rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ "Path is not UTF-8 clean, ignoring assignment: %s",
+ rvalue);
r = 0;
goto fail;
}
@@ -524,7 +538,8 @@ int config_parse_exec(
n[k] = NULL;
if (!n[0]) {
- log_error("[%s:%u] Invalid command line, ignoring: %s", filename, line, rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ "Invalid command line, ignoring: %s", rvalue);
r = 0;
goto fail;
}
@@ -532,7 +547,7 @@ int config_parse_exec(
if (!path) {
path = strdup(n[0]);
if (!path) {
- r = -ENOMEM;
+ r = log_oom();
goto fail;
}
}
@@ -541,7 +556,7 @@ int config_parse_exec(
nce = new0(ExecCommand, 1);
if (!nce) {
- r = -ENOMEM;
+ r = log_oom();
goto fail;
}
@@ -570,15 +585,15 @@ fail:
DEFINE_CONFIG_PARSE_ENUM(config_parse_service_type, service_type, ServiceType, "Failed to parse service type");
DEFINE_CONFIG_PARSE_ENUM(config_parse_service_restart, service_restart, ServiceRestart, "Failed to parse service restart specifier");
-int config_parse_socket_bindtodevice(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
+int config_parse_socket_bindtodevice(const char* unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
Socket *s = data;
char *n;
@@ -589,8 +604,9 @@ int config_parse_socket_bindtodevice(
assert(data);
if (rvalue[0] && !streq(rvalue, "*")) {
- if (!(n = strdup(rvalue)))
- return -ENOMEM;
+ n = strdup(rvalue);
+ if (!n)
+ return log_oom();
} else
n = NULL;
@@ -603,15 +619,15 @@ int config_parse_socket_bindtodevice(
DEFINE_CONFIG_PARSE_ENUM(config_parse_output, exec_output, ExecOutput, "Failed to parse output specifier");
DEFINE_CONFIG_PARSE_ENUM(config_parse_input, exec_input, ExecInput, "Failed to parse input specifier");
-int config_parse_exec_io_class(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
+int config_parse_exec_io_class(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
ExecContext *c = data;
int x;
@@ -623,7 +639,8 @@ int config_parse_exec_io_class(
x = ioprio_class_from_string(rvalue);
if (x < 0) {
- log_error("[%s:%u] Failed to parse IO scheduling class, ignoring: %s", filename, line, rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ "Failed to parse IO scheduling class, ignoring: %s", rvalue);
return 0;
}
@@ -633,26 +650,28 @@ int config_parse_exec_io_class(
return 0;
}
-int config_parse_exec_io_priority(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
+int config_parse_exec_io_priority(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
ExecContext *c = data;
- int i;
+ int i, r;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
- if (safe_atoi(rvalue, &i) < 0 || i < 0 || i >= IOPRIO_BE_NR) {
- log_error("[%s:%u] Failed to parse io priority, ignoring: %s", filename, line, rvalue);
+ r = safe_atoi(rvalue, &i);
+ if (r < 0 || i < 0 || i >= IOPRIO_BE_NR) {
+ log_syntax(unit, LOG_ERR, filename, line, -r,
+ "Failed to parse IO priority, ignoring: %s", rvalue);
return 0;
}
@@ -662,15 +681,15 @@ int config_parse_exec_io_priority(
return 0;
}
-int config_parse_exec_cpu_sched_policy(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
+int config_parse_exec_cpu_sched_policy(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
ExecContext *c = data;
@@ -683,7 +702,8 @@ int config_parse_exec_cpu_sched_policy(
x = sched_policy_from_string(rvalue);
if (x < 0) {
- log_error("[%s:%u] Failed to parse CPU scheduling policy, ignoring: %s", filename, line, rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, -x,
+ "Failed to parse CPU scheduling policy, ignoring: %s", rvalue);
return 0;
}
@@ -695,36 +715,38 @@ int config_parse_exec_cpu_sched_policy(
return 0;
}
-int config_parse_exec_cpu_sched_prio(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
+int config_parse_exec_cpu_sched_prio(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
ExecContext *c = data;
- int i, min, max;
+ int i, min, max, r;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
- if (safe_atoi(rvalue, &i) < 0) {
- log_error("[%s:%u] Failed to parse CPU scheduling priority, ignoring: %s", filename, line, rvalue);
+ r = safe_atoi(rvalue, &i);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, -r,
+ "Failed to parse CPU scheduling policy, ignoring: %s", rvalue);
return 0;
}
-
/* On Linux RR/FIFO range from 1 to 99 and OTHER/BATCH may only be 0 */
min = sched_get_priority_min(c->cpu_sched_policy);
max = sched_get_priority_max(c->cpu_sched_policy);
if (i < min || i > max) {
- log_error("[%s:%u] CPU scheduling priority is out of range, ignoring: %s", filename, line, rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, ERANGE,
+ "CPU scheduling priority is out of range, ignoring: %s", rvalue);
return 0;
}
@@ -734,15 +756,15 @@ int config_parse_exec_cpu_sched_prio(
return 0;
}
-int config_parse_exec_cpu_affinity(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
+int config_parse_exec_cpu_affinity(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
ExecContext *c = data;
char *w;
@@ -754,26 +776,34 @@ int config_parse_exec_cpu_affinity(
assert(rvalue);
assert(data);
+ if (isempty(rvalue)) {
+ /* An empty assignment resets the CPU list */
+ if (c->cpuset)
+ CPU_FREE(c->cpuset);
+ c->cpuset = NULL;
+ return 0;
+ }
+
FOREACH_WORD_QUOTED(w, l, rvalue, state) {
- char _cleanup_free_ *t = NULL;
+ _cleanup_free_ char *t = NULL;
int r;
unsigned cpu;
t = strndup(w, l);
if (!t)
- return -ENOMEM;
+ return log_oom();
r = safe_atou(t, &cpu);
if (!c->cpuset) {
c->cpuset = cpu_set_malloc(&c->cpuset_ncpus);
if (!c->cpuset)
- return -ENOMEM;
+ return log_oom();
}
if (r < 0 || cpu >= c->cpuset_ncpus) {
- log_error("[%s:%u] Failed to parse CPU affinity %s, ignoring: %s",
- filename, line, t, rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, ERANGE,
+ "Failed to parse CPU affinity '%s', ignoring: %s", t, rvalue);
return 0;
}
@@ -783,15 +813,15 @@ int config_parse_exec_cpu_affinity(
return 0;
}
-int config_parse_exec_capabilities(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
+int config_parse_exec_capabilities(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
ExecContext *c = data;
cap_t cap;
@@ -801,11 +831,10 @@ int config_parse_exec_capabilities(
assert(rvalue);
assert(data);
- if (!(cap = cap_from_text(rvalue))) {
- if (errno == ENOMEM)
- return -ENOMEM;
-
- log_error("[%s:%u] Failed to parse capabilities, ignoring: %s", filename, line, rvalue);
+ cap = cap_from_text(rvalue);
+ if (!cap) {
+ log_syntax(unit, LOG_ERR, filename, line, errno,
+ "Failed to parse capabilities, ignoring: %s", rvalue);
return 0;
}
@@ -816,15 +845,15 @@ int config_parse_exec_capabilities(
return 0;
}
-int config_parse_exec_secure_bits(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
+int config_parse_exec_secure_bits(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
ExecContext *c = data;
char *w;
@@ -836,22 +865,28 @@ int config_parse_exec_secure_bits(
assert(rvalue);
assert(data);
+ if (isempty(rvalue)) {
+ /* An empty assignment resets the field */
+ c->secure_bits = 0;
+ return 0;
+ }
+
FOREACH_WORD_QUOTED(w, l, rvalue, state) {
if (first_word(w, "keep-caps"))
- c->secure_bits |= SECURE_KEEP_CAPS;
+ c->secure_bits |= 1<<SECURE_KEEP_CAPS;
else if (first_word(w, "keep-caps-locked"))
- c->secure_bits |= SECURE_KEEP_CAPS_LOCKED;
+ c->secure_bits |= 1<<SECURE_KEEP_CAPS_LOCKED;
else if (first_word(w, "no-setuid-fixup"))
- c->secure_bits |= SECURE_NO_SETUID_FIXUP;
+ c->secure_bits |= 1<<SECURE_NO_SETUID_FIXUP;
else if (first_word(w, "no-setuid-fixup-locked"))
- c->secure_bits |= SECURE_NO_SETUID_FIXUP_LOCKED;
+ c->secure_bits |= 1<<SECURE_NO_SETUID_FIXUP_LOCKED;
else if (first_word(w, "noroot"))
- c->secure_bits |= SECURE_NOROOT;
+ c->secure_bits |= 1<<SECURE_NOROOT;
else if (first_word(w, "noroot-locked"))
- c->secure_bits |= SECURE_NOROOT_LOCKED;
+ c->secure_bits |= 1<<SECURE_NOROOT_LOCKED;
else {
- log_error("[%s:%u] Failed to parse secure bits, ignoring: %s",
- filename, line, rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ "Failed to parse secure bits, ignoring: %s", rvalue);
return 0;
}
}
@@ -859,15 +894,15 @@ int config_parse_exec_secure_bits(
return 0;
}
-int config_parse_bounding_set(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
+int config_parse_bounding_set(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
uint64_t *capability_bounding_set_drop = data;
char *w;
@@ -892,18 +927,18 @@ int config_parse_bounding_set(
* interface. */
FOREACH_WORD_QUOTED(w, l, rvalue, state) {
- char _cleanup_free_ *t = NULL;
+ _cleanup_free_ char *t = NULL;
int r;
cap_value_t cap;
t = strndup(w, l);
if (!t)
- return -ENOMEM;
+ return log_oom();
r = cap_from_name(t, &cap);
if (r < 0) {
- log_error("[%s:%u] Failed to parse capability in bounding set, ignoring: %s",
- filename, line, t);
+ log_syntax(unit, LOG_ERR, filename, line, errno,
+ "Failed to parse capability in bounding set, ignoring: %s", t);
continue;
}
@@ -918,15 +953,15 @@ int config_parse_bounding_set(
return 0;
}
-int config_parse_limit(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
+int config_parse_limit(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
struct rlimit **rl = data;
unsigned long long u;
@@ -940,54 +975,72 @@ int config_parse_limit(
if (streq(rvalue, "infinity"))
u = (unsigned long long) RLIM_INFINITY;
- else if (safe_atollu(rvalue, &u) < 0) {
- log_error("[%s:%u] Failed to parse resource value, ignoring: %s", filename, line, rvalue);
- return 0;
+ else {
+ int r;
+
+ r = safe_atollu(rvalue, &u);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, -r,
+ "Failed to parse resource value, ignoring: %s", rvalue);
+ return 0;
+ }
}
- if (!*rl)
- if (!(*rl = new(struct rlimit, 1)))
- return -ENOMEM;
+ if (!*rl) {
+ *rl = new(struct rlimit, 1);
+ if (!*rl)
+ return log_oom();
+ }
(*rl)->rlim_cur = (*rl)->rlim_max = (rlim_t) u;
return 0;
}
-int config_parse_unit_cgroup(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
+int config_parse_unit_cgroup(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
Unit *u = userdata;
char *w;
size_t l;
char *state;
+ if (isempty(rvalue)) {
+ /* An empty assignment resets the list */
+ cgroup_bonding_free_list(u->cgroup_bondings, false);
+ u->cgroup_bondings = NULL;
+ return 0;
+ }
+
FOREACH_WORD_QUOTED(w, l, rvalue, state) {
- char _cleanup_free_ *t = NULL, *k = NULL, *ku = NULL;
+ _cleanup_free_ char *t = NULL, *k = NULL, *ku = NULL;
int r;
t = strndup(w, l);
if (!t)
- return -ENOMEM;
+ return log_oom();
k = unit_full_printf(u, t);
if (!k)
- return -ENOMEM;
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ "Failed to resolve unit specifiers on %s. Ignoring.",
+ t);
- ku = cunescape(k);
+ ku = cunescape(k ? k : t);
if (!ku)
- return -ENOMEM;
+ return log_oom();
- r = unit_add_cgroup_from_text(u, ku);
+ r = unit_add_cgroup_from_text(u, ku, true, NULL);
if (r < 0) {
- log_error("[%s:%u] Failed to parse cgroup value %s, ignoring: %s",
- filename, line, k, rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, -r,
+ "Failed to parse cgroup value %s, ignoring: %s",
+ k, rvalue);
return 0;
}
}
@@ -996,26 +1049,28 @@ int config_parse_unit_cgroup(
}
#ifdef HAVE_SYSV_COMPAT
-int config_parse_sysv_priority(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
+int config_parse_sysv_priority(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
int *priority = data;
- int i;
+ int i, r;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
- if (safe_atoi(rvalue, &i) < 0 || i < 0) {
- log_error("[%s:%u] Failed to parse SysV start priority, ignoring: %s", filename, line, rvalue);
+ r = safe_atoi(rvalue, &i);
+ if (r < 0 || i < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, -r,
+ "Failed to parse SysV start priority, ignoring: %s", rvalue);
return 0;
}
@@ -1024,26 +1079,28 @@ int config_parse_sysv_priority(
}
#endif
-int config_parse_fsck_passno(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
+int config_parse_fsck_passno(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
int *passno = data;
- int i;
+ int i, r;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
- if (safe_atoi(rvalue, &i) || i < 0) {
- log_error("[%s:%u] Failed to parse fsck pass number, ignoring: %s", filename, line, rvalue);
+ r = safe_atoi(rvalue, &i);
+ if (r || i < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, -r,
+ "Failed to parse fsck pass number, ignoring: %s", rvalue);
return 0;
}
@@ -1053,15 +1110,15 @@ int config_parse_fsck_passno(
DEFINE_CONFIG_PARSE_ENUM(config_parse_kill_mode, kill_mode, KillMode, "Failed to parse kill mode");
-int config_parse_kill_signal(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
+int config_parse_kill_signal(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
int *sig = data;
int r;
@@ -1071,8 +1128,10 @@ int config_parse_kill_signal(
assert(rvalue);
assert(sig);
- if ((r = signal_from_string_try_harder(rvalue)) <= 0) {
- log_error("[%s:%u] Failed to parse kill signal, ignoring: %s", filename, line, rvalue);
+ r = signal_from_string_try_harder(rvalue);
+ if (r <= 0) {
+ log_syntax(unit, LOG_ERR, filename, line, -r,
+ "Failed to parse kill signal, ignoring: %s", rvalue);
return 0;
}
@@ -1080,15 +1139,15 @@ int config_parse_kill_signal(
return 0;
}
-int config_parse_exec_mount_flags(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
+int config_parse_exec_mount_flags(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
ExecContext *c = data;
char *w;
@@ -1102,11 +1161,11 @@ int config_parse_exec_mount_flags(
assert(data);
FOREACH_WORD_SEPARATOR(w, l, rvalue, ", ", state) {
- char _cleanup_free_ *t;
+ _cleanup_free_ char *t;
t = strndup(w, l);
if (!t)
- return -ENOMEM;
+ return log_oom();
if (streq(t, "shared"))
flags |= MS_SHARED;
@@ -1115,8 +1174,9 @@ int config_parse_exec_mount_flags(
else if (streq(w, "private"))
flags |= MS_PRIVATE;
else {
- log_error("[%s:%u] Failed to parse mount flag %s, ignoring: %s",
- filename, line, t, rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ "Failed to parse mount flag %s, ignoring: %s",
+ t, rvalue);
return 0;
}
}
@@ -1125,15 +1185,15 @@ int config_parse_exec_mount_flags(
return 0;
}
-int config_parse_timer(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
+int config_parse_timer(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
Timer *t = data;
usec_t u = 0;
@@ -1147,22 +1207,33 @@ int config_parse_timer(
assert(rvalue);
assert(data);
+ if (isempty(rvalue)) {
+ /* Empty assignment resets list */
+ timer_free_values(t);
+ return 0;
+ }
+
b = timer_base_from_string(lvalue);
if (b < 0) {
- log_error("[%s:%u] Failed to parse timer base, ignoring: %s", filename, line, lvalue);
+ log_syntax(unit, LOG_ERR, filename, line, -b,
+ "Failed to parse timer base, ignoring: %s", lvalue);
return 0;
}
if (b == TIMER_CALENDAR) {
if (calendar_spec_from_string(rvalue, &c) < 0) {
- log_error("[%s:%u] Failed to parse calendar specification, ignoring: %s", filename, line, rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ "Failed to parse calendar specification, ignoring: %s",
+ rvalue);
return 0;
}
id = CLOCK_REALTIME;
} else {
- if (parse_usec(rvalue, &u) < 0) {
- log_error("[%s:%u] Failed to parse timer value, ignoring: %s", filename, line, rvalue);
+ if (parse_sec(rvalue, &u) < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ "Failed to parse timer value, ignoring: %s",
+ rvalue);
return 0;
}
@@ -1171,7 +1242,7 @@ int config_parse_timer(
v = new0(TimerValue, 1);
if (!v)
- return -ENOMEM;
+ return log_oom();
v->base = b;
v->clock_id = id;
@@ -1183,7 +1254,8 @@ int config_parse_timer(
return 0;
}
-int config_parse_timer_unit(
+int config_parse_trigger_unit(
+ const char *unit,
const char *filename,
unsigned line,
const char *section,
@@ -1193,78 +1265,105 @@ int config_parse_timer_unit(
void *data,
void *userdata) {
- Timer *t = data;
+ _cleanup_free_ char *p = NULL;
+ Unit *u = data;
+ UnitType type;
int r;
- DBusError error;
- Unit *u;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
- dbus_error_init(&error);
+ if (!set_isempty(u->dependencies[UNIT_TRIGGERS])) {
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ "Multiple units to trigger specified, ignoring: %s", rvalue);
+ return 0;
+ }
+
+ p = unit_name_printf(u, rvalue);
+ if (!p)
+ return log_oom();
- if (endswith(rvalue, ".timer")) {
- log_error("[%s:%u] Unit cannot be of type timer, ignoring: %s", filename, line, rvalue);
+ type = unit_name_to_type(p);
+ if (type < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ "Unit type not valid, ignoring: %s", rvalue);
return 0;
}
- r = manager_load_unit(UNIT(t)->manager, rvalue, NULL, NULL, &u);
- if (r < 0) {
- log_error("[%s:%u] Failed to load unit %s, ignoring: %s", filename, line, rvalue, bus_error(&error, r));
- dbus_error_free(&error);
+ if (type == u->type) {
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ "Trigger cannot be of same type, ignoring: %s", rvalue);
return 0;
}
- unit_ref_set(&t->unit, u);
+ r = unit_add_two_dependencies_by_name(u, UNIT_BEFORE, UNIT_TRIGGERS, p, NULL, true);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, -r,
+ "Failed to add trigger on %s, ignoring: %s", p, strerror(-r));
+ return 0;
+ }
return 0;
}
-int config_parse_path_spec(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
+int config_parse_path_spec(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
Path *p = data;
PathSpec *s;
PathType b;
- char *k;
+ _cleanup_free_ char *k = NULL;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
+ if (isempty(rvalue)) {
+ /* Empty assignment clears list */
+ path_free_specs(p);
+ return 0;
+ }
+
b = path_type_from_string(lvalue);
if (b < 0) {
- log_error("[%s:%u] Failed to parse path type, ignoring: %s", filename, line, lvalue);
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ "Failed to parse path type, ignoring: %s", lvalue);
return 0;
}
k = unit_full_printf(UNIT(p), rvalue);
- if (!k)
- return log_oom();
+ if (!k) {
+ k = strdup(rvalue);
+ if (!k)
+ return log_oom();
+ else
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ "Failed to resolve unit specifiers on %s. Ignoring.",
+ rvalue);
+ }
if (!path_is_absolute(k)) {
- log_error("[%s:%u] Path is not absolute, ignoring: %s", filename, line, k);
- free(k);
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ "Path is not absolute, ignoring: %s", k);
return 0;
}
s = new0(PathSpec, 1);
- if (!s) {
- free(k);
+ if (!s)
return log_oom();
- }
s->path = path_kill_slashes(k);
+ k = NULL;
s->type = b;
s->inotify_fd = -1;
@@ -1273,58 +1372,21 @@ int config_parse_path_spec(
return 0;
}
-int config_parse_path_unit(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- Path *t = data;
- int r;
- DBusError error;
- Unit *u;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- dbus_error_init(&error);
-
- if (endswith(rvalue, ".path")) {
- log_error("[%s:%u] Unit cannot be of type path, ignoring: %s", filename, line, rvalue);
- return 0;
- }
-
- if ((r = manager_load_unit(UNIT(t)->manager, rvalue, NULL, &error, &u)) < 0) {
- log_error("[%s:%u] Failed to load unit %s, ignoring: %s", filename, line, rvalue, bus_error(&error, r));
- dbus_error_free(&error);
- return 0;
- }
-
- unit_ref_set(&t->unit, u);
-
- return 0;
-}
-
-int config_parse_socket_service(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
+int config_parse_socket_service(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
Socket *s = data;
int r;
DBusError error;
Unit *x;
+ _cleanup_free_ char *p = NULL;
assert(filename);
assert(lvalue);
@@ -1333,14 +1395,21 @@ int config_parse_socket_service(
dbus_error_init(&error);
- if (!endswith(rvalue, ".service")) {
- log_error("[%s:%u] Unit must be of type service, ignoring: %s", filename, line, rvalue);
+ p = unit_name_printf(UNIT(s), rvalue);
+ if (!p)
+ return log_oom();
+
+ if (!endswith(p, ".service")) {
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ "Unit must be of type service, ignoring: %s", rvalue);
return 0;
}
- r = manager_load_unit(UNIT(s)->manager, rvalue, NULL, &error, &x);
+ r = manager_load_unit(UNIT(s)->manager, p, NULL, &error, &x);
if (r < 0) {
- log_error("[%s:%u] Failed to load unit %s, ignoring: %s", filename, line, rvalue, bus_error(&error, r));
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ "Failed to load unit %s, ignoring: %s",
+ rvalue, bus_error(&error, r));
dbus_error_free(&error);
return 0;
}
@@ -1350,15 +1419,15 @@ int config_parse_socket_service(
return 0;
}
-int config_parse_service_sockets(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
+int config_parse_service_sockets(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
Service *s = data;
int r;
@@ -1371,26 +1440,27 @@ int config_parse_service_sockets(
assert(data);
FOREACH_WORD_QUOTED(w, l, rvalue, state) {
- char _cleanup_free_ *t = NULL, *k = NULL;
+ _cleanup_free_ char *t = NULL, *k = NULL;
t = strndup(w, l);
if (!t)
- return -ENOMEM;
+ return log_oom();
k = unit_name_printf(UNIT(s), t);
if (!k)
- return -ENOMEM;
+ return log_oom();
if (!endswith(k, ".socket")) {
- log_error("[%s:%u] Unit must be of type socket, ignoring: %s",
- filename, line, k);
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ "Unit must be of type socket, ignoring: %s", k);
continue;
}
r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_WANTS, UNIT_AFTER, k, NULL, true);
if (r < 0)
- log_error("[%s:%u] Failed to add dependency on %s, ignoring: %s",
- filename, line, k, strerror(-r));
+ log_syntax(unit, LOG_ERR, filename, line, -r,
+ "Failed to add dependency on %s, ignoring: %s",
+ k, strerror(-r));
r = unit_add_dependency_by_name(UNIT(s), UNIT_TRIGGERED_BY, k, NULL, true);
if (r < 0)
@@ -1400,15 +1470,15 @@ int config_parse_service_sockets(
return 0;
}
-int config_parse_service_timeout(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
+int config_parse_service_timeout(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
Service *s = userdata;
int r;
@@ -1418,9 +1488,9 @@ int config_parse_service_timeout(
assert(rvalue);
assert(s);
- r = config_parse_usec(filename, line, section, lvalue, ltype, rvalue, data, userdata);
-
- if (r)
+ r = config_parse_sec(unit, filename, line, section, lvalue, ltype,
+ rvalue, data, userdata);
+ if (r < 0)
return r;
if (streq(lvalue, "TimeoutSec")) {
@@ -1432,55 +1502,115 @@ int config_parse_service_timeout(
return 0;
}
-int config_parse_unit_env_file(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- char ***env = data, **k;
+int config_parse_unit_env_file(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ char ***env = data;
Unit *u = userdata;
- char *s;
+ _cleanup_free_ char *s = NULL;
+ int r;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
+ if (isempty(rvalue)) {
+ /* Empty assignment frees the list */
+ strv_free(*env);
+ *env = NULL;
+ return 0;
+ }
+
s = unit_full_printf(u, rvalue);
if (!s)
- return -ENOMEM;
+ return log_oom();
if (!path_is_absolute(s[0] == '-' ? s + 1 : s)) {
- log_error("[%s:%u] Path '%s' is not absolute, ignoring.", filename, line, s);
- free(s);
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ "Path '%s' is not absolute, ignoring.", s);
+ return 0;
+ }
+
+ r = strv_extend(env, s);
+ if (r < 0)
+ return log_oom();
+
+ return 0;
+}
+
+int config_parse_environ(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ Unit *u = userdata;
+ char*** env = data, *w, *state;
+ size_t l;
+ _cleanup_free_ char *k = NULL;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(u);
+
+ if (isempty(rvalue)) {
+ /* Empty assignment resets the list */
+ strv_free(*env);
+ *env = NULL;
return 0;
}
- k = strv_append(*env, s);
- free(s);
+ k = unit_full_printf(u, rvalue);
if (!k)
- return -ENOMEM;
+ return log_oom();
- strv_free(*env);
- *env = k;
+ FOREACH_WORD_QUOTED(w, l, k, state) {
+ _cleanup_free_ char *n;
+ char **x;
+
+ n = cunescape_length(w, l);
+ if (!n)
+ return log_oom();
+
+ if (!env_assignment_is_valid(n)) {
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ "Invalid environment assignment, ignoring: %s", rvalue);
+ continue;
+ }
+
+ x = strv_env_set(*env, n);
+ if (!x)
+ return log_oom();
+
+ strv_free(*env);
+ *env = x;
+ }
return 0;
}
-int config_parse_ip_tos(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
+int config_parse_ip_tos(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
int *ip_tos = data, x;
@@ -1491,7 +1621,8 @@ int config_parse_ip_tos(
x = ip_tos_from_string(rvalue);
if (x < 0) {
- log_error("[%s:%u] Failed to parse IP TOS value, ignoring: %s", filename, line, rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ "Failed to parse IP TOS value, ignoring: %s", rvalue);
return 0;
}
@@ -1499,15 +1630,15 @@ int config_parse_ip_tos(
return 0;
}
-int config_parse_unit_condition_path(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
+int config_parse_unit_condition_path(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
ConditionType cond = ltype;
Unit *u = data;
@@ -1520,6 +1651,13 @@ int config_parse_unit_condition_path(
assert(rvalue);
assert(data);
+ if (isempty(rvalue)) {
+ /* Empty assignment resets the list */
+ condition_free_list(u->conditions);
+ u->conditions = NULL;
+ return 0;
+ }
+
trigger = rvalue[0] == '|';
if (trigger)
rvalue++;
@@ -1530,30 +1668,31 @@ int config_parse_unit_condition_path(
p = unit_full_printf(u, rvalue);
if (!p)
- return -ENOMEM;
+ return log_oom();
if (!path_is_absolute(p)) {
- log_error("[%s:%u] Path in condition not absolute, ignoring: %s", filename, line, p);
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ "Path in condition not absolute, ignoring: %s", p);
return 0;
}
c = condition_new(cond, p, trigger, negate);
if (!c)
- return -ENOMEM;
+ return log_oom();
LIST_PREPEND(Condition, conditions, u->conditions, c);
return 0;
}
-int config_parse_unit_condition_string(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
+int config_parse_unit_condition_string(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
ConditionType cond = ltype;
Unit *u = data;
@@ -1566,6 +1705,13 @@ int config_parse_unit_condition_string(
assert(rvalue);
assert(data);
+ if (isempty(rvalue)) {
+ /* Empty assignment resets the list */
+ condition_free_list(u->conditions);
+ u->conditions = NULL;
+ return 0;
+ }
+
trigger = rvalue[0] == '|';
if (trigger)
rvalue++;
@@ -1576,7 +1722,7 @@ int config_parse_unit_condition_string(
s = unit_full_printf(u, rvalue);
if (!s)
- return -ENOMEM;
+ return log_oom();
c = condition_new(cond, s, trigger, negate);
if (!c)
@@ -1586,15 +1732,15 @@ int config_parse_unit_condition_string(
return 0;
}
-int config_parse_unit_condition_null(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
+int config_parse_unit_condition_null(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
Unit *u = data;
Condition *c;
@@ -1606,22 +1752,35 @@ int config_parse_unit_condition_null(
assert(rvalue);
assert(data);
- if ((trigger = rvalue[0] == '|'))
+ if (isempty(rvalue)) {
+ /* Empty assignment resets the list */
+ condition_free_list(u->conditions);
+ u->conditions = NULL;
+ return 0;
+ }
+
+ trigger = rvalue[0] == '|';
+ if (trigger)
rvalue++;
- if ((negate = rvalue[0] == '!'))
+ negate = rvalue[0] == '!';
+ if (negate)
rvalue++;
- if ((b = parse_boolean(rvalue)) < 0) {
- log_error("[%s:%u] Failed to parse boolean value in condition, ignoring: %s", filename, line, rvalue);
+ b = parse_boolean(rvalue);
+ if (b < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, -b,
+ "Failed to parse boolean value in condition, ignoring: %s",
+ rvalue);
return 0;
}
if (!b)
negate = !negate;
- if (!(c = condition_new(CONDITION_NULL, NULL, trigger, negate)))
- return -ENOMEM;
+ c = condition_new(CONDITION_NULL, NULL, trigger, negate);
+ if (!c)
+ return log_oom();
LIST_PREPEND(Condition, conditions, u->conditions, c);
return 0;
@@ -1630,383 +1789,117 @@ int config_parse_unit_condition_null(
DEFINE_CONFIG_PARSE_ENUM(config_parse_notify_access, notify_access, NotifyAccess, "Failed to parse notify access specifier");
DEFINE_CONFIG_PARSE_ENUM(config_parse_start_limit_action, start_limit_action, StartLimitAction, "Failed to parse start limit action specifier");
-int config_parse_unit_cgroup_attr(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- Unit *u = data;
- char **l;
- int r;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- l = strv_split_quoted(rvalue);
- if (!l)
- return -ENOMEM;
-
- if (strv_length(l) != 2) {
- log_error("[%s:%u] Failed to parse cgroup attribute value, ignoring: %s", filename, line, rvalue);
- strv_free(l);
- return 0;
- }
-
- r = unit_add_cgroup_attribute(u, NULL, l[0], l[1], NULL);
- strv_free(l);
-
- if (r < 0) {
- log_error("[%s:%u] Failed to add cgroup attribute value, ignoring: %s", filename, line, rvalue);
- return 0;
- }
-
- return 0;
-}
+int config_parse_unit_cgroup_attr(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
-int config_parse_unit_cpu_shares(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata) {
Unit *u = data;
+ size_t a, b;
+ _cleanup_free_ char *n = NULL, *v = NULL;
+ const CGroupSemantics *s;
int r;
- unsigned long ul;
- char *t;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
- if (safe_atolu(rvalue, &ul) < 0 || ul < 1) {
- log_error("[%s:%u] Failed to parse CPU shares value, ignoring: %s", filename, line, rvalue);
- return 0;
- }
-
- if (asprintf(&t, "%lu", ul) < 0)
- return -ENOMEM;
-
- r = unit_add_cgroup_attribute(u, "cpu", "cpu.shares", t, NULL);
- free(t);
-
- if (r < 0) {
- log_error("[%s:%u] Failed to add cgroup attribute value, ignoring: %s", filename, line, rvalue);
+ if (isempty(rvalue)) {
+ /* Empty assignment clears the list */
+ cgroup_attribute_free_list(u->cgroup_attributes);
+ u->cgroup_attributes = NULL;
return 0;
}
- return 0;
-}
-
-int config_parse_unit_memory_limit(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata) {
- Unit *u = data;
- int r;
- off_t sz;
- char *t;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- if (parse_bytes(rvalue, &sz) < 0 || sz <= 0) {
- log_error("[%s:%u] Failed to parse memory limit value, ignoring: %s", filename, line, rvalue);
+ a = strcspn(rvalue, WHITESPACE);
+ b = strspn(rvalue + a, WHITESPACE);
+ if (a <= 0 || b <= 0) {
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ "Failed to parse cgroup attribute value, ignoring: %s",
+ rvalue);
return 0;
}
- if (asprintf(&t, "%llu", (unsigned long long) sz) < 0)
- return -ENOMEM;
-
- r = unit_add_cgroup_attribute(u,
- "memory",
- streq(lvalue, "MemorySoftLimit") ? "memory.soft_limit_in_bytes" : "memory.limit_in_bytes",
- t, NULL);
- free(t);
+ n = strndup(rvalue, a);
+ if (!n)
+ return log_oom();
+ r = cgroup_semantics_find(NULL, n, rvalue + a + b, &v, &s);
if (r < 0) {
- log_error("[%s:%u] Failed to add cgroup attribute value, ignoring: %s", filename, line, rvalue);
- return 0;
- }
-
- return 0;
-}
-
-static int device_map(const char *controller, const char *name, const char *value, char **ret) {
- char **l;
-
- assert(controller);
- assert(name);
- assert(value);
- assert(ret);
-
- l = strv_split_quoted(value);
- if (!l)
- return -ENOMEM;
-
- assert(strv_length(l) >= 1);
-
- if (streq(l[0], "*")) {
-
- if (asprintf(ret, "a *:*%s%s",
- isempty(l[1]) ? "" : " ", strempty(l[1])) < 0) {
- strv_free(l);
- return -ENOMEM;
- }
-
- } else {
- struct stat st;
-
- if (stat(l[0], &st) < 0) {
- log_warning("Couldn't stat device %s", l[0]);
- strv_free(l);
- return -errno;
- }
-
- if (!S_ISCHR(st.st_mode) && !S_ISBLK(st.st_mode)) {
- log_warning("%s is not a device.", l[0]);
- strv_free(l);
- return -ENODEV;
- }
-
- if (asprintf(ret, "%c %u:%u%s%s",
- S_ISCHR(st.st_mode) ? 'c' : 'b',
- major(st.st_rdev), minor(st.st_rdev),
- isempty(l[1]) ? "" : " ", strempty(l[1])) < 0) {
-
- strv_free(l);
- return -ENOMEM;
- }
- }
-
- strv_free(l);
- return 0;
-}
-
-int config_parse_unit_device_allow(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata) {
- Unit *u = data;
- char **l;
- int r;
- unsigned k;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- l = strv_split_quoted(rvalue);
- if (!l)
- return -ENOMEM;
-
- k = strv_length(l);
- if (k < 1 || k > 2) {
- log_error("[%s:%u] Failed to parse device value, ignoring: %s", filename, line, rvalue);
- strv_free(l);
- return 0;
- }
-
- if (!streq(l[0], "*") && !path_startswith(l[0], "/dev")) {
- log_error("[%s:%u] Device node path not absolute, ignoring: %s", filename, line, rvalue);
- strv_free(l);
+ log_syntax(unit, LOG_ERR, filename, line, -r,
+ "Failed to parse cgroup attribute value, ignoring: %s",
+ rvalue);
return 0;
}
- if (!isempty(l[1]) && !in_charset(l[1], "rwm")) {
- log_error("[%s:%u] Device access string invalid, ignoring: %s", filename, line, rvalue);
- strv_free(l);
- return 0;
- }
- strv_free(l);
-
- r = unit_add_cgroup_attribute(u, "devices",
- streq(lvalue, "DeviceAllow") ? "devices.allow" : "devices.deny",
- rvalue, device_map);
-
+ r = unit_add_cgroup_attribute(u, s, NULL, n, v ? v : rvalue + a + b, NULL);
if (r < 0) {
- log_error("[%s:%u] Failed to add cgroup attribute value, ignoring: %s", filename, line, rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, -r,
+ "Failed to add cgroup attribute value, ignoring: %s", rvalue);
return 0;
}
return 0;
}
-static int blkio_map(const char *controller, const char *name, const char *value, char **ret) {
- struct stat st;
- char **l;
- dev_t d;
-
- assert(controller);
- assert(name);
- assert(value);
- assert(ret);
-
- l = strv_split_quoted(value);
- if (!l)
- return -ENOMEM;
-
- assert(strv_length(l) == 2);
-
- if (stat(l[0], &st) < 0) {
- log_warning("Couldn't stat device %s", l[0]);
- strv_free(l);
- return -errno;
- }
-
- if (S_ISBLK(st.st_mode))
- d = st.st_rdev;
- else if (major(st.st_dev) != 0) {
- /* If this is not a device node then find the block
- * device this file is stored on */
- d = st.st_dev;
-
- /* If this is a partition, try to get the originating
- * block device */
- block_get_whole_disk(d, &d);
- } else {
- log_warning("%s is not a block device and file system block device cannot be determined or is not local.", l[0]);
- strv_free(l);
- return -ENODEV;
- }
-
- if (asprintf(ret, "%u:%u %s", major(d), minor(d), l[1]) < 0) {
- strv_free(l);
- return -ENOMEM;
- }
+int config_parse_unit_cgroup_attr_pretty(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
- strv_free(l);
- return 0;
-}
-
-int config_parse_unit_blkio_weight(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata) {
Unit *u = data;
+ _cleanup_free_ char *v = NULL;
+ const CGroupSemantics *s;
int r;
- unsigned long ul;
- const char *device = NULL, *weight;
- unsigned k;
- char *t, **l;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
- l = strv_split_quoted(rvalue);
- if (!l)
- return -ENOMEM;
-
- k = strv_length(l);
- if (k < 1 || k > 2) {
- log_error("[%s:%u] Failed to parse weight value, ignoring: %s", filename, line, rvalue);
- strv_free(l);
- return 0;
- }
-
- if (k == 1)
- weight = l[0];
- else {
- device = l[0];
- weight = l[1];
- }
-
- if (device && !path_is_absolute(device)) {
- log_error("[%s:%u] Failed to parse block device node value, ignoring: %s", filename, line, rvalue);
- strv_free(l);
- return 0;
- }
-
- if (safe_atolu(weight, &ul) < 0 || ul < 10 || ul > 1000) {
- log_error("[%s:%u] Failed to parse block IO weight value, ignoring: %s", filename, line, rvalue);
- strv_free(l);
- return 0;
- }
-
- if (device)
- r = asprintf(&t, "%s %lu", device, ul);
- else
- r = asprintf(&t, "%lu", ul);
- strv_free(l);
-
- if (r < 0)
- return -ENOMEM;
-
- if (device)
- r = unit_add_cgroup_attribute(u, "blkio", "blkio.weight_device", t, blkio_map);
- else
- r = unit_add_cgroup_attribute(u, "blkio", "blkio.weight", t, NULL);
- free(t);
-
+ r = cgroup_semantics_find(NULL, lvalue, rvalue, &v, &s);
if (r < 0) {
- log_error("[%s:%u] Failed to add cgroup attribute value, ignoring: %s", filename, line, rvalue);
- return 0;
- }
-
- return 0;
-}
-
-int config_parse_unit_blkio_bandwidth(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata) {
- Unit *u = data;
- int r;
- off_t bytes;
- unsigned k;
- char *t, **l;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- l = strv_split_quoted(rvalue);
- if (!l)
- return -ENOMEM;
-
- k = strv_length(l);
- if (k != 2) {
- log_error("[%s:%u] Failed to parse bandwidth value, ignoring: %s", filename, line, rvalue);
- strv_free(l);
+ log_syntax(unit, LOG_ERR, filename, line, -r,
+ "Failed to parse cgroup attribute value, ignoring: %s",
+ rvalue);
return 0;
- }
-
- if (!path_is_absolute(l[0])) {
- log_error("[%s:%u] Failed to parse block device node value, ignoring: %s", filename, line, rvalue);
- strv_free(l);
+ } else if (r == 0) {
+ log_syntax(unit, LOG_ERR, filename, line, ENOTSUP,
+ "Unknown or unsupported cgroup attribute %s, ignoring: %s",
+ lvalue, rvalue);
return 0;
}
- if (parse_bytes(l[1], &bytes) < 0 || bytes <= 0) {
- log_error("[%s:%u] Failed to parse block IO bandwidth value, ignoring: %s", filename, line, rvalue);
- strv_free(l);
- return 0;
- }
-
- r = asprintf(&t, "%s %llu", l[0], (unsigned long long) bytes);
- strv_free(l);
-
- if (r < 0)
- return -ENOMEM;
-
- r = unit_add_cgroup_attribute(u, "blkio",
- streq(lvalue, "BlockIOReadBandwidth") ? "blkio.read_bps_device" : "blkio.write_bps_device",
- t, blkio_map);
- free(t);
-
+ r = unit_add_cgroup_attribute(u, s, NULL, NULL, v, NULL);
if (r < 0) {
- log_error("[%s:%u] Failed to add cgroup attribute value, ignoring: %s", filename, line, rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, -r,
+ "Failed to add cgroup attribute value, ignoring: %s", rvalue);
return 0;
}
return 0;
}
-int config_parse_unit_requires_mounts_for(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
+int config_parse_unit_requires_mounts_for(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
Unit *u = userdata;
int r;
@@ -2019,7 +1912,8 @@ int config_parse_unit_requires_mounts_for(
empty_before = !u->requires_mounts_for;
- r = config_parse_path_strv(filename, line, section, lvalue, ltype, rvalue, data, userdata);
+ r = config_parse_path_strv(unit, filename, line, section, lvalue, ltype,
+ rvalue, data, userdata);
/* Make it easy to find units with requires_mounts set */
if (empty_before && u->requires_mounts_for)
@@ -2028,15 +1922,15 @@ int config_parse_unit_requires_mounts_for(
return r;
}
-int config_parse_documentation(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
+int config_parse_documentation(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
Unit *u = userdata;
int r;
@@ -2047,7 +1941,15 @@ int config_parse_documentation(
assert(rvalue);
assert(u);
- r = config_parse_unit_strv_printf(filename, line, section, lvalue, ltype, rvalue, data, userdata);
+ if (isempty(rvalue)) {
+ /* Empty assignment resets the list */
+ strv_free(u->documentation);
+ u->documentation = NULL;
+ return 0;
+ }
+
+ r = config_parse_unit_strv_printf(unit, filename, line, section, lvalue, ltype,
+ rvalue, data, userdata);
if (r < 0)
return r;
@@ -2056,7 +1958,8 @@ int config_parse_documentation(
if (is_valid_documentation_url(*a))
*(b++) = *a;
else {
- log_error("[%s:%u] Invalid URL, ignoring: %s", filename, line, *a);
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ "Invalid URL, ignoring: %s", *a);
free(*a);
}
}
@@ -2066,22 +1969,24 @@ int config_parse_documentation(
}
static void syscall_set(uint32_t *p, int nr) {
+ nr = SYSCALL_TO_INDEX(nr);
p[nr >> 4] |= 1 << (nr & 31);
}
static void syscall_unset(uint32_t *p, int nr) {
+ nr = SYSCALL_TO_INDEX(nr);
p[nr >> 4] &= ~(1 << (nr & 31));
}
-int config_parse_syscall_filter(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
+int config_parse_syscall_filter(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
ExecContext *c = data;
Unit *u = userdata;
@@ -2095,6 +2000,13 @@ int config_parse_syscall_filter(
assert(rvalue);
assert(u);
+ if (isempty(rvalue)) {
+ /* Empty assignment resets the list */
+ free(c->syscall_filter);
+ c->syscall_filter = NULL;
+ return 0;
+ }
+
if (rvalue[0] == '~') {
invert = true;
rvalue++;
@@ -2106,7 +2018,7 @@ int config_parse_syscall_filter(
n = (syscall_max() + 31) >> 4;
c->syscall_filter = new(uint32_t, n);
if (!c->syscall_filter)
- return -ENOMEM;
+ return log_oom();
memset(c->syscall_filter, invert ? 0xFF : 0, n * sizeof(uint32_t));
@@ -2122,17 +2034,16 @@ int config_parse_syscall_filter(
FOREACH_WORD_QUOTED(w, l, rvalue, state) {
int id;
- char _cleanup_free_ *t = NULL;
+ _cleanup_free_ char *t = NULL;
t = strndup(w, l);
if (!t)
- return -ENOMEM;
+ return log_oom();
id = syscall_from_name(t);
-
if (id < 0) {
- log_error("[%s:%u] Failed to parse syscall, ignoring: %s",
- filename, line, t);
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ "Failed to parse syscall, ignoring: %s", t);
continue;
}
@@ -2184,11 +2095,9 @@ static int open_follow(char **filename, FILE **_f, Set *names, char **_final) {
if (!id)
return -ENOMEM;
- r = set_put(names, id);
- if (r < 0) {
- free(id);
+ r = set_consume(names, id);
+ if (r < 0)
return r;
- }
}
}
@@ -2359,7 +2268,9 @@ static int load_from_path(Unit *u, const char *path) {
u->load_state = UNIT_MASKED;
else {
/* Now, parse the file contents */
- r = config_parse(filename, f, UNIT_VTABLE(u)->sections, config_item_perf_lookup, (void*) load_fragment_gperf_lookup, false, u);
+ r = config_parse(u->id, filename, f, UNIT_VTABLE(u)->sections,
+ config_item_perf_lookup,
+ (void*) load_fragment_gperf_lookup, false, true, u);
if (r < 0)
goto finish;
@@ -2521,16 +2432,15 @@ void unit_dump_config_items(FILE *f) {
{ config_parse_socket_listen, "SOCKET [...]" },
{ config_parse_socket_bind, "SOCKETBIND" },
{ config_parse_socket_bindtodevice, "NETWORKINTERFACE" },
- { config_parse_usec, "SECONDS" },
+ { config_parse_sec, "SECONDS" },
{ config_parse_nsec, "NANOSECONDS" },
{ config_parse_path_strv, "PATH [...]" },
{ config_parse_unit_requires_mounts_for, "PATH [...]" },
{ config_parse_exec_mount_flags, "MOUNTFLAG [...]" },
{ config_parse_unit_string_printf, "STRING" },
+ { config_parse_trigger_unit, "UNIT" },
{ config_parse_timer, "TIMER" },
- { config_parse_timer_unit, "NAME" },
{ config_parse_path_spec, "PATH" },
- { config_parse_path_unit, "UNIT" },
{ config_parse_notify_access, "ACCESS" },
{ config_parse_ip_tos, "TOS" },
{ config_parse_unit_condition_path, "CONDITION" },
@@ -2557,7 +2467,7 @@ void unit_dump_config_items(FILE *f) {
prefix_len = dot-i;
if (dot)
- if (!prev || strncmp(prev, i, prefix_len+1) != 0) {
+ if (!prev || !strneq(prev, i, prefix_len+1)) {
if (prev)
fputc('\n', f);
diff --git a/src/core/load-fragment.h b/src/core/load-fragment.h
index 24f738464c..ff7f22a6f0 100644
--- a/src/core/load-fragment.h
+++ b/src/core/load-fragment.h
@@ -29,59 +29,55 @@ int unit_load_fragment(Unit *u);
void unit_dump_config_items(FILE *f);
-int config_parse_warn_compat(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_unit_deps(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_unit_string_printf(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_unit_strv_printf(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_unit_path_printf(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_documentation(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_socket_listen(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_socket_bind(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_exec_nice(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_exec_oom_score_adjust(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_exec(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_service_timeout(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_service_type(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_service_restart(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_socket_bindtodevice(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_output(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_input(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_exec_io_class(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_exec_io_priority(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_exec_cpu_sched_policy(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_exec_cpu_sched_prio(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_exec_cpu_affinity(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_exec_capabilities(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_exec_secure_bits(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_bounding_set(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_limit(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_unit_cgroup(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_sysv_priority(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_fsck_passno(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_kill_signal(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_exec_mount_flags(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_timer(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_timer_unit(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_path_spec(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_path_unit(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_socket_service(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_service_sockets(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_unit_env_file(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_ip_tos(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_unit_condition_path(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_unit_condition_string(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_unit_condition_null(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_kill_mode(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_notify_access(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_start_limit_action(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_unit_cgroup_attr(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_unit_cpu_shares(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_unit_memory_limit(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_unit_device_allow(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_unit_blkio_weight(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_unit_blkio_bandwidth(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_unit_requires_mounts_for(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_syscall_filter(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_warn_compat(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_unit_deps(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_unit_string_printf(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_unit_strv_printf(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_unit_path_printf(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_documentation(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_socket_listen(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_socket_bind(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_exec_nice(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_exec_oom_score_adjust(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_exec(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_service_timeout(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_service_type(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_service_restart(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_socket_bindtodevice(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_output(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_input(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_exec_io_class(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_exec_io_priority(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_exec_cpu_sched_policy(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_exec_cpu_sched_prio(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_exec_cpu_affinity(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_exec_capabilities(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_exec_secure_bits(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_bounding_set(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_limit(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_unit_cgroup(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_sysv_priority(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_fsck_passno(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_kill_signal(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_exec_mount_flags(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_timer(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_trigger_unit(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_path_spec(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_socket_service(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_service_sockets(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_unit_env_file(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_ip_tos(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_unit_condition_path(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_unit_condition_string(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_unit_condition_null(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_kill_mode(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_notify_access(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_start_limit_action(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_unit_cgroup_attr(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_unit_cgroup_attr_pretty(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_unit_requires_mounts_for(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_syscall_filter(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_environ(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
/* gperf prototypes */
const struct ConfigPerfItem* load_fragment_gperf_lookup(const char *key, unsigned length);
diff --git a/src/core/locale-setup.c b/src/core/locale-setup.c
index 48b59bf448..d7113b9795 100644
--- a/src/core/locale-setup.c
+++ b/src/core/locale-setup.c
@@ -27,6 +27,7 @@
#include "util.h"
#include "macro.h"
#include "virt.h"
+#include "fileio.h"
enum {
/* We don't list LC_ALL here on purpose. People should be
@@ -67,11 +68,9 @@ static const char * const variable_names[_VARIABLE_MAX] = {
};
int locale_setup(void) {
- char *variables[_VARIABLE_MAX];
+ char *variables[_VARIABLE_MAX] = {};
int r = 0, i;
- zero(variables);
-
if (detect_container(NULL) <= 0) {
r = parse_env_file("/proc/cmdline", WHITESPACE,
"locale.LANG", &variables[VARIABLE_LANG],
diff --git a/src/core/loopback-setup.c b/src/core/loopback-setup.c
index 065b75a6e3..aff24fa642 100644
--- a/src/core/loopback-setup.c
+++ b/src/core/loopback-setup.c
@@ -88,25 +88,26 @@ static int add_adresses(int fd, int if_loopback, unsigned *requests) {
union {
struct sockaddr sa;
struct sockaddr_nl nl;
- } sa;
+ } sa = {
+ .nl.nl_family = AF_NETLINK,
+ };
+
union {
struct nlmsghdr header;
uint8_t buf[NLMSG_ALIGN(sizeof(struct nlmsghdr)) +
NLMSG_ALIGN(sizeof(struct ifaddrmsg)) +
RTA_LENGTH(sizeof(struct in6_addr))];
- } request;
+ } request = {
+ .header.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg)),
+ .header.nlmsg_type = RTM_NEWADDR,
+ .header.nlmsg_flags = NLM_F_REQUEST|NLM_F_CREATE|NLM_F_ACK,
+ .header.nlmsg_seq = *requests + 1,
+ };
struct ifaddrmsg *ifaddrmsg;
uint32_t ipv4_address = htonl(INADDR_LOOPBACK);
int r;
- zero(request);
-
- request.header.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
- request.header.nlmsg_type = RTM_NEWADDR;
- request.header.nlmsg_flags = NLM_F_REQUEST|NLM_F_CREATE|NLM_F_ACK;
- request.header.nlmsg_seq = *requests + 1;
-
ifaddrmsg = NLMSG_DATA(&request.header);
ifaddrmsg->ifa_family = AF_INET;
ifaddrmsg->ifa_prefixlen = 8;
@@ -114,13 +115,11 @@ static int add_adresses(int fd, int if_loopback, unsigned *requests) {
ifaddrmsg->ifa_scope = RT_SCOPE_HOST;
ifaddrmsg->ifa_index = if_loopback;
- r = add_rtattr(&request.header, sizeof(request), IFA_LOCAL, &ipv4_address, sizeof(ipv4_address));
+ r = add_rtattr(&request.header, sizeof(request), IFA_LOCAL,
+ &ipv4_address, sizeof(ipv4_address));
if (r < 0)
return r;
- zero(sa);
- sa.nl.nl_family = AF_NETLINK;
-
if (sendto_loop(fd, &request, request.header.nlmsg_len, 0, &sa.sa, sizeof(sa)) < 0)
return -errno;
(*requests)++;
@@ -134,7 +133,8 @@ static int add_adresses(int fd, int if_loopback, unsigned *requests) {
ifaddrmsg->ifa_family = AF_INET6;
ifaddrmsg->ifa_prefixlen = 128;
- r = add_rtattr(&request.header, sizeof(request), IFA_LOCAL, &in6addr_loopback, sizeof(in6addr_loopback));
+ r = add_rtattr(&request.header, sizeof(request), IFA_LOCAL,
+ &in6addr_loopback, sizeof(in6addr_loopback));
if (r < 0)
return r;
@@ -149,31 +149,29 @@ static int start_interface(int fd, int if_loopback, unsigned *requests) {
union {
struct sockaddr sa;
struct sockaddr_nl nl;
- } sa;
+ } sa = {
+ .nl.nl_family = AF_NETLINK,
+ };
+
union {
struct nlmsghdr header;
uint8_t buf[NLMSG_ALIGN(sizeof(struct nlmsghdr)) +
NLMSG_ALIGN(sizeof(struct ifinfomsg))];
- } request;
+ } request = {
+ .header.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)),
+ .header.nlmsg_type = RTM_NEWLINK,
+ .header.nlmsg_flags = NLM_F_REQUEST|NLM_F_ACK,
+ .header.nlmsg_seq = *requests + 1,
+ };
struct ifinfomsg *ifinfomsg;
- zero(request);
-
- request.header.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
- request.header.nlmsg_type = RTM_NEWLINK;
- request.header.nlmsg_flags = NLM_F_REQUEST|NLM_F_ACK;
- request.header.nlmsg_seq = *requests + 1;
-
ifinfomsg = NLMSG_DATA(&request.header);
ifinfomsg->ifi_family = AF_UNSPEC;
ifinfomsg->ifi_index = if_loopback;
ifinfomsg->ifi_flags = IFF_UP;
ifinfomsg->ifi_change = IFF_UP;
- zero(sa);
- sa.nl.nl_family = AF_NETLINK;
-
if (sendto_loop(fd, &request, request.header.nlmsg_len, 0, &sa.sa, sizeof(sa)) < 0)
return -errno;
@@ -229,11 +227,15 @@ static int read_response(int fd, unsigned requests_max) {
}
static int check_loopback(void) {
- int r, fd;
+ int r;
+ _cleanup_close_ int fd;
union {
struct sockaddr sa;
struct sockaddr_in in;
- } sa;
+ } sa = {
+ .in.sin_family = AF_INET,
+ .in.sin_addr.s_addr = INADDR_LOOPBACK,
+ };
/* If we failed to set up the loop back device, check whether
* it might already be set up */
@@ -242,17 +244,11 @@ static int check_loopback(void) {
if (fd < 0)
return -errno;
- zero(sa);
- sa.in.sin_family = AF_INET;
- sa.in.sin_addr.s_addr = INADDR_LOOPBACK;
-
if (bind(fd, &sa.sa, sizeof(sa.in)) >= 0)
r = 1;
else
r = errno == EADDRNOTAVAIL ? 0 : -errno;
- close_nointr_nofail(fd);
-
return r;
}
@@ -261,9 +257,11 @@ int loopback_setup(void) {
union {
struct sockaddr sa;
struct sockaddr_nl nl;
- } sa;
+ } sa = {
+ .nl.nl_family = AF_NETLINK,
+ };
unsigned requests = 0, i;
- int fd;
+ _cleanup_close_ int fd = -1;
bool eperm = false;
errno = 0;
@@ -275,20 +273,18 @@ int loopback_setup(void) {
if (fd < 0)
return -errno;
- zero(sa);
- sa.nl.nl_family = AF_NETLINK;
if (bind(fd, &sa.sa, sizeof(sa)) < 0) {
r = -errno;
- goto finish;
+ goto error;
}
r = add_adresses(fd, if_loopback, &requests);
if (r < 0)
- goto finish;
+ goto error;
r = start_interface(fd, if_loopback, &requests);
if (r < 0)
- goto finish;
+ goto error;
for (i = 0; i < requests; i++) {
r = read_response(fd, requests);
@@ -296,22 +292,17 @@ int loopback_setup(void) {
if (r == -EPERM)
eperm = true;
else if (r < 0)
- goto finish;
+ goto error;
}
if (eperm && check_loopback() < 0) {
r = -EPERM;
- goto finish;
+ goto error;
}
- r = 0;
-
-finish:
- if (r < 0)
- log_warning("Failed to configure loopback device: %s", strerror(-r));
-
- if (fd >= 0)
- close_nointr_nofail(fd);
+ return 0;
+error:
+ log_warning("Failed to configure loopback device: %s", strerror(-r));
return r;
}
diff --git a/src/core/machine-id-setup.c b/src/core/machine-id-setup.c
index 7f4c23b130..18e015fe7f 100644
--- a/src/core/machine-id-setup.c
+++ b/src/core/machine-id-setup.c
@@ -35,6 +35,7 @@
#include "mkdir.h"
#include "log.h"
#include "virt.h"
+#include "fileio.h"
static int shorten_uuid(char destination[36], const char *source) {
unsigned i, j;
@@ -71,16 +72,19 @@ static int generate(char id[34]) {
/* First, try reading the D-Bus machine id, unless it is a symlink */
fd = open("/var/lib/dbus/machine-id", O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
if (fd >= 0) {
-
- k = loop_read(fd, id, 32, false);
+ k = loop_read(fd, id, 33, false);
close_nointr_nofail(fd);
- if (k >= 32) {
- id[32] = '\n';
- id[33] = 0;
+ if (k == 33 && id[32] == '\n') {
- log_info("Initializing machine ID from D-Bus machine ID.");
- return 0;
+ id[32] = 0;
+ if (id128_is_valid(id)) {
+ id[32] = '\n';
+ id[33] = 0;
+
+ log_info("Initializing machine ID from D-Bus machine ID.");
+ return 0;
+ }
}
}
@@ -112,7 +116,7 @@ static int generate(char id[34]) {
* $container_uuid the way libvirt/LXC does it */
r = detect_container(NULL);
if (r > 0) {
- char *e;
+ _cleanup_free_ char *e = NULL;
r = getenv_for_pid(1, "container_uuid", &e);
if (r > 0) {
@@ -120,12 +124,9 @@ static int generate(char id[34]) {
r = shorten_uuid(id, e);
if (r >= 0) {
log_info("Initializing machine ID from container UUID.");
- free(e);
return 0;
}
}
-
- free(e);
}
}
@@ -150,62 +151,57 @@ static int generate(char id[34]) {
}
int machine_id_setup(void) {
- int fd, r;
+ _cleanup_close_ int fd = -1;
+ int r;
bool writable;
struct stat st;
char id[34]; /* 32 + \n + \0 */
- mode_t m;
-
- m = umask(0000);
-
- /* We create this 0444, to indicate that this isn't really
- * something you should ever modify. Of course, since the file
- * will be owned by root it doesn't matter much, but maybe
- * people look. */
-
- fd = open("/etc/machine-id", O_RDWR|O_CREAT|O_CLOEXEC|O_NOCTTY, 0444);
- if (fd >= 0)
- writable = true;
- else {
- fd = open("/etc/machine-id", O_RDONLY|O_CLOEXEC|O_NOCTTY);
- if (fd < 0) {
- umask(m);
- log_error("Cannot open /etc/machine-id: %m");
- return -errno;
- }
- writable = false;
- }
+ RUN_WITH_UMASK(0000) {
+ /* We create this 0444, to indicate that this isn't really
+ * something you should ever modify. Of course, since the file
+ * will be owned by root it doesn't matter much, but maybe
+ * people look. */
+
+ fd = open("/etc/machine-id", O_RDWR|O_CREAT|O_CLOEXEC|O_NOCTTY, 0444);
+ if (fd >= 0)
+ writable = true;
+ else {
+ fd = open("/etc/machine-id", O_RDONLY|O_CLOEXEC|O_NOCTTY);
+ if (fd < 0) {
+ log_error("Cannot open /etc/machine-id: %m");
+ return -errno;
+ }
- umask(m);
+ writable = false;
+ }
+ }
if (fstat(fd, &st) < 0) {
log_error("fstat() failed: %m");
- r = -errno;
- goto finish;
+ return -errno;
}
- if (S_ISREG(st.st_mode)) {
- if (loop_read(fd, id, 32, false) >= 32) {
- r = 0;
- goto finish;
+ if (S_ISREG(st.st_mode))
+ if (loop_read(fd, id, 33, false) == 33 && id[32] == '\n') {
+ id[32] = 0;
+
+ if (id128_is_valid(id))
+ return 0;
}
- }
/* Hmm, so, the id currently stored is not useful, then let's
* generate one */
r = generate(id);
if (r < 0)
- goto finish;
+ return r;
if (S_ISREG(st.st_mode) && writable) {
lseek(fd, 0, SEEK_SET);
- if (loop_write(fd, id, 33, false) == 33) {
- r = 0;
- goto finish;
- }
+ if (loop_write(fd, id, 33, false) == 33)
+ return 0;
}
close_nointr_nofail(fd);
@@ -214,33 +210,28 @@ int machine_id_setup(void) {
/* Hmm, we couldn't write it? So let's write it to
* /run/machine-id as a replacement */
- m = umask(0022);
- r = write_one_line_file("/run/machine-id", id);
- umask(m);
-
+ RUN_WITH_UMASK(0022) {
+ r = write_string_file("/run/machine-id", id);
+ }
if (r < 0) {
log_error("Cannot write /run/machine-id: %s", strerror(-r));
-
unlink("/run/machine-id");
- goto finish;
+ return r;
}
/* And now, let's mount it over */
- r = mount("/run/machine-id", "/etc/machine-id", NULL, MS_BIND, NULL) < 0 ? -errno : 0;
+ r = mount("/run/machine-id", "/etc/machine-id", NULL, MS_BIND, NULL);
if (r < 0) {
- unlink("/run/machine-id");
- log_error("Failed to mount /etc/machine-id: %s", strerror(-r));
- } else {
- log_info("Installed transient /etc/machine-id file.");
-
- /* Mark the mount read-only */
- mount(NULL, "/etc/machine-id", NULL, MS_BIND|MS_RDONLY|MS_REMOUNT, NULL);
+ log_error("Failed to mount /etc/machine-id: %m");
+ unlink_noerrno("/run/machine-id");
+ return -errno;
}
-finish:
+ log_info("Installed transient /etc/machine-id file.");
- if (fd >= 0)
- close_nointr_nofail(fd);
+ /* Mark the mount read-only */
+ if (mount(NULL, "/etc/machine-id", NULL, MS_BIND|MS_RDONLY|MS_REMOUNT, NULL) < 0)
+ log_warning("Failed to make transient /etc/machine-id read-only: %m");
- return r;
+ return 0;
}
diff --git a/src/core/macros.systemd.in b/src/core/macros.systemd.in
index 647cce6913..f77082c2db 100644
--- a/src/core/macros.systemd.in
+++ b/src/core/macros.systemd.in
@@ -61,11 +61,11 @@ fi \
%{nil}
%udev_hwdb_update() \
-@bindir@/udevadm hwdb --update >/dev/null 2>&1 || : \
+@rootbindir@/udevadm hwdb --update >/dev/null 2>&1 || : \
%{nil}
%udev_rules_update() \
-@bindir@/udevadm control --reload >/dev/null 2>&1 || : \
+@rootbindir@/udevadm control --reload >/dev/null 2>&1 || : \
%{nil}
%journal_catalog_update() \
diff --git a/src/core/main.c b/src/core/main.c
index 1ee3c9c0e8..7fc06bea05 100644
--- a/src/core/main.c
+++ b/src/core/main.c
@@ -40,7 +40,7 @@
#include "fdset.h"
#include "special.h"
#include "conf-parser.h"
-#include "bus-errors.h"
+#include "dbus-common.h"
#include "missing.h"
#include "label.h"
#include "build.h"
@@ -52,6 +52,10 @@
#include "switch-root.h"
#include "capability.h"
#include "killall.h"
+#include "env-util.h"
+#include "hwclock.h"
+#include "sd-daemon.h"
+#include "sd-messages.h"
#include "mount-setup.h"
#include "loopback-setup.h"
@@ -61,10 +65,11 @@
#include "hostname-setup.h"
#include "machine-id-setup.h"
#include "locale-setup.h"
-#include "hwclock.h"
#include "selinux-setup.h"
#include "ima-setup.h"
-#include "sd-daemon.h"
+#include "fileio.h"
+#include "smack-setup.h"
+#include "efivars.h"
static enum {
ACTION_RUN,
@@ -104,20 +109,21 @@ _noreturn_ static void crash(int sig) {
if (!arg_dump_core)
log_error("Caught <%s>, not dumping core.", signal_to_string(sig));
else {
- struct sigaction sa;
+ struct sigaction sa = {
+ .sa_handler = nop_handler,
+ .sa_flags = SA_NOCLDSTOP|SA_RESTART,
+ };
pid_t pid;
/* We want to wait for the core process, hence let's enable SIGCHLD */
- zero(sa);
- sa.sa_handler = nop_handler;
- sa.sa_flags = SA_NOCLDSTOP|SA_RESTART;
assert_se(sigaction(SIGCHLD, &sa, NULL) == 0);
- if ((pid = fork()) < 0)
+ pid = fork();
+ if (pid < 0)
log_error("Caught <%s>, cannot fork for core dump: %s", signal_to_string(sig), strerror(errno));
else if (pid == 0) {
- struct rlimit rl;
+ struct rlimit rl = {};
/* Enable default signal handler for core dump */
zero(sa);
@@ -125,7 +131,6 @@ _noreturn_ static void crash(int sig) {
assert_se(sigaction(sig, &sa, NULL) == 0);
/* Don't limit the core dump size */
- zero(rl);
rl.rlim_cur = RLIM_INFINITY;
rl.rlim_max = RLIM_INFINITY;
setrlimit(RLIMIT_CORE, &rl);
@@ -144,7 +149,8 @@ _noreturn_ static void crash(int sig) {
int r;
/* Order things nicely. */
- if ((r = wait_for_terminate(pid, &status)) < 0)
+ r = wait_for_terminate(pid, &status);
+ if (r < 0)
log_error("Caught <%s>, waitpid() failed: %s", signal_to_string(sig), strerror(-r));
else if (status.si_code != CLD_DUMPED)
log_error("Caught <%s>, core dump failed.", signal_to_string(sig));
@@ -157,16 +163,16 @@ _noreturn_ static void crash(int sig) {
chvt(arg_crash_chvt);
if (arg_crash_shell) {
- struct sigaction sa;
+ struct sigaction sa = {
+ .sa_handler = SIG_IGN,
+ .sa_flags = SA_NOCLDSTOP|SA_NOCLDWAIT|SA_RESTART,
+ };
pid_t pid;
log_info("Executing crash shell in 10s...");
sleep(10);
/* Let the kernel reap children for us */
- zero(sa);
- sa.sa_handler = SIG_IGN;
- sa.sa_flags = SA_NOCLDSTOP|SA_NOCLDWAIT|SA_RESTART;
assert_se(sigaction(SIGCHLD, &sa, NULL) == 0);
pid = fork();
@@ -188,12 +194,10 @@ _noreturn_ static void crash(int sig) {
}
static void install_crash_handler(void) {
- struct sigaction sa;
-
- zero(sa);
-
- sa.sa_handler = crash;
- sa.sa_flags = SA_NODEFER;
+ struct sigaction sa = {
+ .sa_handler = crash,
+ .sa_flags = SA_NODEFER,
+ };
sigaction_many(&sa, SIGNALS_CRASH_HANDLER, -1);
}
@@ -342,7 +346,8 @@ static int parse_proc_cmdline_word(const char *word) {
else
arg_default_std_error = r;
} else if (startswith(word, "systemd.setenv=")) {
- char *cenv, *eq;
+ _cleanup_free_ char *cenv = NULL;
+ char *eq;
int r;
cenv = strdup(word + 15);
@@ -351,40 +356,58 @@ static int parse_proc_cmdline_word(const char *word) {
eq = strchr(cenv, '=');
if (!eq) {
- r = unsetenv(cenv);
- if (r < 0)
- log_warning("unsetenv failed %m. Ignoring.");
+ if (!env_name_is_valid(cenv))
+ log_warning("Environment variable name '%s' is not valid. Ignoring.", cenv);
+ else {
+ r = unsetenv(cenv);
+ if (r < 0)
+ log_warning("Unsetting environment variable '%s' failed, ignoring: %m", cenv);
+ }
} else {
- *eq = 0;
- r = setenv(cenv, eq + 1, 1);
- if (r < 0)
- log_warning("setenv failed %m. Ignoring.");
+ if (!env_assignment_is_valid(cenv))
+ log_warning("Environment variable assignment '%s' is not valid. Ignoring.", cenv);
+ else {
+ *eq = 0;
+ r = setenv(cenv, eq + 1, 1);
+ if (r < 0)
+ log_warning("Setting environment variable '%s=%s' failed, ignoring: %m", cenv, eq + 1);
+ }
}
- free(cenv);
} else if (startswith(word, "systemd.") ||
(in_initrd() && startswith(word, "rd.systemd."))) {
- log_warning("Unknown kernel switch %s. Ignoring.", word);
-
- log_info("Supported kernel switches:\n"
- "systemd.unit=UNIT Default unit to start\n"
- "rd.systemd.unit=UNIT Default unit to start when run in initrd\n"
- "systemd.dump_core=0|1 Dump core on crash\n"
- "systemd.crash_shell=0|1 Run shell on crash\n"
- "systemd.crash_chvt=N Change to VT #N on crash\n"
- "systemd.confirm_spawn=0|1 Confirm every process spawn\n"
- "systemd.show_status=0|1 Show status updates on the console during bootup\n"
- "systemd.log_target=console|kmsg|journal|journal-or-kmsg|syslog|syslog-or-kmsg|null\n"
- " Log target\n"
- "systemd.log_level=LEVEL Log level\n"
- "systemd.log_color=0|1 Highlight important log messages\n"
- "systemd.log_location=0|1 Include code location in log messages\n"
- "systemd.default_standard_output=null|tty|syslog|syslog+console|kmsg|kmsg+console|journal|journal+console\n"
- " Set default log output for services\n"
- "systemd.default_standard_error=null|tty|syslog|syslog+console|kmsg|kmsg+console|journal|journal+console\n"
- " Set default log error output for services\n"
- "systemd.setenv=ASSIGNMENT Set an environment variable for all spawned processes\n");
+ const char *c;
+
+ /* Ignore systemd.journald.xyz and friends */
+ c = word;
+ if (startswith(c, "rd."))
+ c += 3;
+ if (startswith(c, "systemd."))
+ c += 8;
+ if (c[strcspn(c, ".=")] != '.') {
+
+ log_warning("Unknown kernel switch %s. Ignoring.", word);
+
+ log_info("Supported kernel switches:\n"
+ "systemd.unit=UNIT Default unit to start\n"
+ "rd.systemd.unit=UNIT Default unit to start when run in initrd\n"
+ "systemd.dump_core=0|1 Dump core on crash\n"
+ "systemd.crash_shell=0|1 Run shell on crash\n"
+ "systemd.crash_chvt=N Change to VT #N on crash\n"
+ "systemd.confirm_spawn=0|1 Confirm every process spawn\n"
+ "systemd.show_status=0|1 Show status updates on the console during bootup\n"
+ "systemd.log_target=console|kmsg|journal|journal-or-kmsg|syslog|syslog-or-kmsg|null\n"
+ " Log target\n"
+ "systemd.log_level=LEVEL Log level\n"
+ "systemd.log_color=0|1 Highlight important log messages\n"
+ "systemd.log_location=0|1 Include code location in log messages\n"
+ "systemd.default_standard_output=null|tty|syslog|syslog+console|kmsg|kmsg+console|journal|journal+console\n"
+ " Set default log output for services\n"
+ "systemd.default_standard_error=null|tty|syslog|syslog+console|kmsg|kmsg+console|journal|journal+console\n"
+ " Set default log error output for services\n"
+ "systemd.setenv=ASSIGNMENT Set an environment variable for all spawned processes\n");
+ }
} else if (streq(word, "quiet"))
arg_show_status = false;
@@ -400,87 +423,47 @@ static int parse_proc_cmdline_word(const char *word) {
return 0;
}
-static int config_parse_level2(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
-
- log_set_max_level_from_string(rvalue);
- return 0;
-}
-
-static int config_parse_target(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
-
- log_set_target_from_string(rvalue);
- return 0;
-}
-
-static int config_parse_color(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
+#define DEFINE_SETTER(name, func, descr) \
+ static int name(const char *unit, \
+ const char *filename, \
+ unsigned line, \
+ const char *section, \
+ const char *lvalue, \
+ int ltype, \
+ const char *rvalue, \
+ void *data, \
+ void *userdata) { \
+ \
+ int r; \
+ \
+ assert(filename); \
+ assert(lvalue); \
+ assert(rvalue); \
+ \
+ r = func(rvalue); \
+ if (r < 0) \
+ log_syntax(unit, LOG_ERR, filename, line, -r, \
+ "Invalid " descr "'%s': %s", \
+ rvalue, strerror(-r)); \
+ \
+ return 0; \
+ }
- assert(filename);
- assert(lvalue);
- assert(rvalue);
+DEFINE_SETTER(config_parse_level2, log_set_max_level_from_string, "log level")
+DEFINE_SETTER(config_parse_target, log_set_target_from_string, "target")
+DEFINE_SETTER(config_parse_color, log_show_color_from_string, "color" )
+DEFINE_SETTER(config_parse_location, log_show_location_from_string, "location")
- log_show_color_from_string(rvalue);
- return 0;
-}
-static int config_parse_location(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
-
- log_show_location_from_string(rvalue);
- return 0;
-}
-
-static int config_parse_cpu_affinity2(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
+static int config_parse_cpu_affinity2(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
char *w;
size_t l;
@@ -508,7 +491,8 @@ static int config_parse_cpu_affinity2(
return log_oom();
if (r < 0 || cpu >= ncpus) {
- log_error("[%s:%u] Failed to parse CPU affinity: %s", filename, line, rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, -r,
+ "Failed to parse CPU affinity '%s'", rvalue);
CPU_FREE(c);
return -EBADMSG;
}
@@ -518,7 +502,7 @@ static int config_parse_cpu_affinity2(
if (c) {
if (sched_setaffinity(0, CPU_ALLOC_SIZE(ncpus), c) < 0)
- log_warning("Failed to set CPU affinity: %m");
+ log_warning_unit(unit, "Failed to set CPU affinity: %m");
CPU_FREE(c);
}
@@ -539,22 +523,19 @@ static void strv_free_free(char ***l) {
}
static void free_join_controllers(void) {
- if (!arg_join_controllers)
- return;
-
strv_free_free(arg_join_controllers);
arg_join_controllers = NULL;
}
-static int config_parse_join_controllers(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
+static int config_parse_join_controllers(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
unsigned n = 0;
char *state, *w;
@@ -660,8 +641,8 @@ static int parse_config_file(void) {
{ "Manager", "DefaultStandardOutput", config_parse_output, 0, &arg_default_std_output },
{ "Manager", "DefaultStandardError", config_parse_output, 0, &arg_default_std_error },
{ "Manager", "JoinControllers", config_parse_join_controllers, 0, &arg_join_controllers },
- { "Manager", "RuntimeWatchdogSec", config_parse_usec, 0, &arg_runtime_watchdog },
- { "Manager", "ShutdownWatchdogSec", config_parse_usec, 0, &arg_shutdown_watchdog },
+ { "Manager", "RuntimeWatchdogSec", config_parse_sec, 0, &arg_runtime_watchdog },
+ { "Manager", "ShutdownWatchdogSec", config_parse_sec, 0, &arg_shutdown_watchdog },
{ "Manager", "CapabilityBoundingSet", config_parse_bounding_set, 0, &arg_capability_bounding_set_drop },
{ "Manager", "TimerSlackNSec", config_parse_nsec, 0, &arg_timer_slack_nsec },
{ "Manager", "DefaultLimitCPU", config_parse_limit, 0, &arg_default_rlimit[RLIMIT_CPU]},
@@ -683,11 +664,11 @@ static int parse_config_file(void) {
{ NULL, NULL, NULL, 0, NULL }
};
- FILE *f;
+ _cleanup_fclose_ FILE *f;
const char *fn;
int r;
- fn = arg_running_as == SYSTEMD_SYSTEM ? SYSTEM_CONFIG_FILE : USER_CONFIG_FILE;
+ fn = arg_running_as == SYSTEMD_SYSTEM ? PKGSYSCONFDIR "/system.conf" : PKGSYSCONFDIR "/user.conf";
f = fopen(fn, "re");
if (!f) {
if (errno == ENOENT)
@@ -697,17 +678,16 @@ static int parse_config_file(void) {
return 0;
}
- r = config_parse(fn, f, "Manager\0", config_item_table_lookup, (void*) items, false, NULL);
+ r = config_parse(NULL, fn, f, "Manager\0", config_item_table_lookup, (void*) items, false, false, NULL);
if (r < 0)
log_warning("Failed to parse configuration file: %s", strerror(-r));
- fclose(f);
-
return 0;
}
static int parse_proc_cmdline(void) {
- char *line, *w, *state;
+ _cleanup_free_ char *line = NULL;
+ char *w, *state;
int r;
size_t l;
@@ -716,34 +696,27 @@ static int parse_proc_cmdline(void) {
if (detect_container(NULL) > 0)
return 0;
- if ((r = read_one_line_file("/proc/cmdline", &line)) < 0) {
+ r = read_one_line_file("/proc/cmdline", &line);
+ if (r < 0) {
log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
return 0;
}
FOREACH_WORD_QUOTED(w, l, line, state) {
- char *word;
+ _cleanup_free_ char *word;
- if (!(word = strndup(w, l))) {
- r = -ENOMEM;
- goto finish;
- }
+ word = strndup(w, l);
+ if (!word)
+ return log_oom();
r = parse_proc_cmdline_word(word);
if (r < 0) {
log_error("Failed on cmdline argument %s: %s", word, strerror(-r));
- free(word);
- goto finish;
+ return r;
}
-
- free(word);
}
- r = 0;
-
-finish:
- free(line);
- return r;
+ return 0;
}
static int parse_argv(int argc, char *argv[]) {
@@ -1069,7 +1042,7 @@ static int version(void) {
return 0;
}
-static int prepare_reexecute(Manager *m, FILE **_f, FDSet **_fds, bool serialize_jobs) {
+static int prepare_reexecute(Manager *m, FILE **_f, FDSet **_fds, bool switching_root) {
FILE *f = NULL;
FDSet *fds = NULL;
int r;
@@ -1094,7 +1067,7 @@ static int prepare_reexecute(Manager *m, FILE **_f, FDSet **_fds, bool serialize
goto fail;
}
- r = manager_serialize(m, f, fds, serialize_jobs);
+ r = manager_serialize(m, f, fds, switching_root);
if (r < 0) {
log_error("Failed to serialize state: %s", strerror(-r));
goto fail;
@@ -1244,14 +1217,14 @@ static int initialize_join_controllers(void) {
return -ENOMEM;
arg_join_controllers[0] = strv_new("cpu", "cpuacct", NULL);
- if (!arg_join_controllers[0])
- return -ENOMEM;
-
arg_join_controllers[1] = strv_new("net_cls", "net_prio", NULL);
- if (!arg_join_controllers[1])
+ arg_join_controllers[2] = NULL;
+
+ if (!arg_join_controllers[0] || !arg_join_controllers[1]) {
+ free_join_controllers();
return -ENOMEM;
+ }
- arg_join_controllers[2] = NULL;
return 0;
}
@@ -1264,6 +1237,10 @@ int main(int argc, char *argv[]) {
bool reexecute = false;
const char *shutdown_verb = NULL;
dual_timestamp initrd_timestamp = { 0ULL, 0ULL };
+ dual_timestamp userspace_timestamp = { 0ULL, 0ULL };
+ dual_timestamp kernel_timestamp = { 0ULL, 0ULL };
+ dual_timestamp firmware_timestamp = { 0ULL, 0ULL };
+ dual_timestamp loader_timestamp = { 0ULL, 0ULL };
static char systemd[] = "systemd";
bool skip_setup = false;
int j;
@@ -1285,22 +1262,19 @@ int main(int argc, char *argv[]) {
}
#endif
+ dual_timestamp_from_monotonic(&kernel_timestamp, 0);
+ dual_timestamp_get(&userspace_timestamp);
+
/* Determine if this is a reexecution or normal bootup. We do
* the full command line parsing much later, so let's just
* have a quick peek here. */
- for (j = 1; j < argc; j++)
- if (streq(argv[j], "--deserialize")) {
- skip_setup = true;
- break;
- }
+ if (strv_find(argv+1, "--deserialize"))
+ skip_setup = true;
/* If we have switched root, do all the special setup
* things */
- for (j = 1; j < argc; j++)
- if (streq(argv[j], "--switched-root")) {
- skip_setup = false;
- break;
- }
+ if (strv_find(argv+1, "--switched-root"))
+ skip_setup = false;
/* If we get started via the /sbin/init symlink then we are
called 'init'. After a subsequent reexecution we are then
@@ -1315,7 +1289,9 @@ int main(int argc, char *argv[]) {
log_show_color(isatty(STDERR_FILENO) > 0);
if (getpid() == 1 && detect_container(NULL) <= 0) {
-
+#ifdef ENABLE_EFI
+ efi_get_boot_timestamps(&userspace_timestamp, &firmware_timestamp, &loader_timestamp);
+#endif
/* Running outside of a container as PID 1 */
arg_running_as = SYSTEMD_SYSTEM;
make_null_stdio();
@@ -1325,7 +1301,7 @@ int main(int argc, char *argv[]) {
if (in_initrd()) {
char *rd_timestamp = NULL;
- dual_timestamp_get(&initrd_timestamp);
+ initrd_timestamp = userspace_timestamp;
asprintf(&rd_timestamp, "%llu %llu",
(unsigned long long) initrd_timestamp.realtime,
(unsigned long long) initrd_timestamp.monotonic);
@@ -1336,10 +1312,13 @@ int main(int argc, char *argv[]) {
}
if (!skip_setup) {
+ mount_setup_early();
if (selinux_setup(&loaded_policy) < 0)
goto finish;
if (ima_setup() < 0)
goto finish;
+ if (smack_setup() < 0)
+ goto finish;
}
if (label_init(NULL) < 0)
@@ -1382,7 +1361,6 @@ int main(int argc, char *argv[]) {
log_set_target(LOG_TARGET_JOURNAL_OR_KMSG);
} else if (getpid() == 1) {
-
/* Running inside a container, as PID 1 */
arg_running_as = SYSTEMD_SYSTEM;
log_set_target(LOG_TARGET_CONSOLE);
@@ -1391,12 +1369,21 @@ int main(int argc, char *argv[]) {
/* For the later on, see above... */
log_set_target(LOG_TARGET_JOURNAL);
- } else {
+ /* clear the kernel timestamp,
+ * because we are in a container */
+ kernel_timestamp.monotonic = 0ULL;
+ kernel_timestamp.realtime = 0ULL;
+ } else {
/* Running as user instance */
arg_running_as = SYSTEMD_USER;
log_set_target(LOG_TARGET_AUTO);
log_open();
+
+ /* clear the kernel timestamp,
+ * because we are not PID 1 */
+ kernel_timestamp.monotonic = 0ULL;
+ kernel_timestamp.realtime = 0ULL;
}
/* Initialize default unit */
@@ -1412,7 +1399,7 @@ int main(int argc, char *argv[]) {
/* Mount /proc, /sys and friends, so that /proc/cmdline and
* /proc/$PID/fd is available. */
- if (geteuid() == 0 && !getenv("SYSTEMD_SKIP_API_MOUNTS")) {
+ if (getpid() == 1) {
r = mount_setup(loaded_policy);
if (r < 0)
goto finish;
@@ -1554,10 +1541,9 @@ int main(int argc, char *argv[]) {
/* Make sure we leave a core dump without panicing the
* kernel. */
- if (getpid() == 1)
+ if (getpid() == 1) {
install_crash_handler();
- if (geteuid() == 0 && !getenv("SYSTEMD_SKIP_API_MOUNTS")) {
r = mount_cgroup_controllers(arg_join_controllers);
if (r < 0)
goto finish;
@@ -1639,12 +1625,14 @@ int main(int argc, char *argv[]) {
m->default_std_error = arg_default_std_error;
m->runtime_watchdog = arg_runtime_watchdog;
m->shutdown_watchdog = arg_shutdown_watchdog;
+ m->userspace_timestamp = userspace_timestamp;
+ m->kernel_timestamp = kernel_timestamp;
+ m->firmware_timestamp = firmware_timestamp;
+ m->loader_timestamp = loader_timestamp;
+ m->initrd_timestamp = initrd_timestamp;
manager_set_default_rlimits(m, arg_default_rlimit);
- if (dual_timestamp_is_set(&initrd_timestamp))
- m->initrd_timestamp = initrd_timestamp;
-
if (arg_default_controllers)
manager_set_default_controllers(m, arg_default_controllers);
@@ -1710,18 +1698,29 @@ int main(int argc, char *argv[]) {
manager_dump_units(m, stdout, "\t");
}
- r = manager_add_job(m, JOB_START, target, JOB_REPLACE, false, &error, &default_unit_job);
- if (r < 0) {
- log_error("Failed to start default target: %s", bus_error(&error, r));
+ r = manager_add_job(m, JOB_START, target, JOB_ISOLATE, false, &error, &default_unit_job);
+ if (r == -EPERM) {
+ log_debug("Default target could not be isolated, starting instead: %s", bus_error(&error, r));
+ dbus_error_free(&error);
+
+ r = manager_add_job(m, JOB_START, target, JOB_REPLACE, false, &error, &default_unit_job);
+ if (r < 0) {
+ log_error("Failed to start default target: %s", bus_error(&error, r));
+ dbus_error_free(&error);
+ goto finish;
+ }
+ } else if (r < 0) {
+ log_error("Failed to isolate default target: %s", bus_error(&error, r));
dbus_error_free(&error);
goto finish;
}
+
m->default_unit_job_id = default_unit_job->id;
after_startup = now(CLOCK_MONOTONIC);
log_full(arg_action == ACTION_TEST ? LOG_INFO : LOG_DEBUG,
"Loaded units and determined initial transaction in %s.",
- format_timespan(timespan, sizeof(timespan), after_startup - before_startup));
+ format_timespan(timespan, sizeof(timespan), after_startup - before_startup, 0));
if (arg_action == ACTION_TEST) {
printf("-> By jobs:\n");
@@ -1754,7 +1753,7 @@ int main(int argc, char *argv[]) {
case MANAGER_REEXECUTE:
- if (prepare_reexecute(m, &serialization, &fds, true) < 0)
+ if (prepare_reexecute(m, &serialization, &fds, false) < 0)
goto finish;
reexecute = true;
@@ -1768,7 +1767,7 @@ int main(int argc, char *argv[]) {
m->switch_root = m->switch_root_init = NULL;
if (!switch_root_init)
- if (prepare_reexecute(m, &serialization, &fds, false) < 0)
+ if (prepare_reexecute(m, &serialization, &fds, true) < 0)
goto finish;
reexecute = true;
diff --git a/src/core/manager.c b/src/core/manager.c
index df0fd63e87..c7f8f20a70 100644
--- a/src/core/manager.c
+++ b/src/core/manager.c
@@ -70,6 +70,7 @@
#include "cgroup-util.h"
#include "path-util.h"
#include "audit-fd.h"
+#include "env-util.h"
/* As soon as 16 units are in our GC queue, make sure to run a gc sweep */
#define GC_QUEUE_ENTRIES_MAX 16
@@ -77,18 +78,28 @@
/* As soon as 5s passed since a unit was added to our GC queue, make sure to run a gc sweep */
#define GC_QUEUE_USEC_MAX (10*USEC_PER_SEC)
+/* Initial delay and the interval for printing status messages about running jobs */
+#define JOBS_IN_PROGRESS_WAIT_SEC 5
+#define JOBS_IN_PROGRESS_PERIOD_SEC 1
+#define JOBS_IN_PROGRESS_PERIOD_DIVISOR 3
+
/* Where clients shall send notification messages to */
#define NOTIFY_SOCKET "@/org/freedesktop/systemd1/notify"
+#define TIME_T_MAX (time_t)((1UL << ((sizeof(time_t) << 3) - 1)) - 1)
+
static int manager_setup_notify(Manager *m) {
union {
struct sockaddr sa;
struct sockaddr_un un;
- } sa;
- struct epoll_event ev;
- int one = 1;
-
- assert(m);
+ } sa = {
+ .sa.sa_family = AF_UNIX,
+ };
+ struct epoll_event ev = {
+ .events = EPOLLIN,
+ .data.ptr = &m->notify_watch,
+ };
+ int one = 1, r;
m->notify_watch.type = WATCH_NOTIFY;
m->notify_watch.fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
@@ -97,9 +108,6 @@ static int manager_setup_notify(Manager *m) {
return -errno;
}
- zero(sa);
- sa.sa.sa_family = AF_UNIX;
-
if (getpid() != 1 || detect_container(NULL) > 0)
snprintf(sa.un.sun_path, sizeof(sa.un.sun_path), NOTIFY_SOCKET "/%llu", random_ull());
else
@@ -107,22 +115,22 @@ static int manager_setup_notify(Manager *m) {
sa.un.sun_path[0] = 0;
- if (bind(m->notify_watch.fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + 1 + strlen(sa.un.sun_path+1)) < 0) {
+ r = bind(m->notify_watch.fd, &sa.sa,
+ offsetof(struct sockaddr_un, sun_path) + 1 + strlen(sa.un.sun_path+1));
+ if (r < 0) {
log_error("bind() failed: %m");
return -errno;
}
- if (setsockopt(m->notify_watch.fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)) < 0) {
+ r = setsockopt(m->notify_watch.fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one));
+ if (r < 0) {
log_error("SO_PASSCRED failed: %m");
return -errno;
}
- zero(ev);
- ev.events = EPOLLIN;
- ev.data.ptr = &m->notify_watch;
-
- if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->notify_watch.fd, &ev) < 0) {
- log_error("Failed to add timer change fd to epoll: %m");
+ r = epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->notify_watch.fd, &ev);
+ if (r < 0) {
+ log_error("Failed to add notification socket fd to epoll: %m");
return -errno;
}
@@ -136,11 +144,151 @@ static int manager_setup_notify(Manager *m) {
return 0;
}
+static int manager_jobs_in_progress_mod_timer(Manager *m) {
+ struct itimerspec its = {
+ .it_value.tv_sec = JOBS_IN_PROGRESS_WAIT_SEC,
+ .it_interval.tv_sec = JOBS_IN_PROGRESS_PERIOD_SEC,
+ };
+
+ if (m->jobs_in_progress_watch.type != WATCH_JOBS_IN_PROGRESS)
+ return 0;
+
+ if (timerfd_settime(m->jobs_in_progress_watch.fd, 0, &its, NULL) < 0)
+ return -errno;
+
+ return 0;
+}
+
+static int manager_watch_jobs_in_progress(Manager *m) {
+ struct epoll_event ev = {
+ .events = EPOLLIN,
+ .data.ptr = &m->jobs_in_progress_watch,
+ };
+ int r;
+
+ if (m->jobs_in_progress_watch.type != WATCH_INVALID)
+ return 0;
+
+ m->jobs_in_progress_watch.type = WATCH_JOBS_IN_PROGRESS;
+ m->jobs_in_progress_watch.fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK|TFD_CLOEXEC);
+ if (m->jobs_in_progress_watch.fd < 0) {
+ log_error("Failed to create timerfd: %m");
+ r = -errno;
+ goto err;
+ }
+
+ r = manager_jobs_in_progress_mod_timer(m);
+ if (r < 0) {
+ log_error("Failed to set up timer for jobs progress watch: %s", strerror(-r));
+ goto err;
+ }
+
+ if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->jobs_in_progress_watch.fd, &ev) < 0) {
+ log_error("Failed to add jobs progress timer fd to epoll: %m");
+ r = -errno;
+ goto err;
+ }
+
+ log_debug("Set up jobs progress timerfd.");
+
+ return 0;
+
+err:
+ if (m->jobs_in_progress_watch.fd >= 0)
+ close_nointr_nofail(m->jobs_in_progress_watch.fd);
+ watch_init(&m->jobs_in_progress_watch);
+ return r;
+}
+
+static void manager_unwatch_jobs_in_progress(Manager *m) {
+ if (m->jobs_in_progress_watch.type != WATCH_JOBS_IN_PROGRESS)
+ return;
+
+ assert_se(epoll_ctl(m->epoll_fd, EPOLL_CTL_DEL, m->jobs_in_progress_watch.fd, NULL) >= 0);
+ close_nointr_nofail(m->jobs_in_progress_watch.fd);
+ watch_init(&m->jobs_in_progress_watch);
+ m->jobs_in_progress_iteration = 0;
+
+ log_debug("Closed jobs progress timerfd.");
+}
+
+#define CYLON_BUFFER_EXTRA (2*strlen(ANSI_RED_ON) + strlen(ANSI_HIGHLIGHT_RED_ON) + 2*strlen(ANSI_HIGHLIGHT_OFF))
+static void draw_cylon(char buffer[], size_t buflen, unsigned width, unsigned pos) {
+ char *p = buffer;
+
+ assert(buflen >= CYLON_BUFFER_EXTRA + width + 1);
+ assert(pos <= width+1); /* 0 or width+1 mean that the center light is behind the corner */
+
+ if (pos > 1) {
+ if (pos > 2)
+ p = mempset(p, ' ', pos-2);
+ p = stpcpy(p, ANSI_RED_ON);
+ *p++ = '*';
+ }
+
+ if (pos > 0 && pos <= width) {
+ p = stpcpy(p, ANSI_HIGHLIGHT_RED_ON);
+ *p++ = '*';
+ }
+
+ p = stpcpy(p, ANSI_HIGHLIGHT_OFF);
+
+ if (pos < width) {
+ p = stpcpy(p, ANSI_RED_ON);
+ *p++ = '*';
+ if (pos < width-1)
+ p = mempset(p, ' ', width-1-pos);
+ p = stpcpy(p, ANSI_HIGHLIGHT_OFF);
+ }
+}
+
+static void manager_print_jobs_in_progress(Manager *m) {
+ Iterator i;
+ Job *j;
+ char *job_of_n = NULL;
+ unsigned counter = 0, print_nr;
+ char cylon[6 + CYLON_BUFFER_EXTRA + 1];
+ unsigned cylon_pos;
+
+ print_nr = (m->jobs_in_progress_iteration / JOBS_IN_PROGRESS_PERIOD_DIVISOR) % m->n_running_jobs;
+
+ HASHMAP_FOREACH(j, m->jobs, i)
+ if (j->state == JOB_RUNNING && counter++ == print_nr)
+ break;
+
+ /* m->n_running_jobs must be consistent with the contents of m->jobs,
+ * so the above loop must have succeeded in finding j. */
+ assert(counter == print_nr + 1);
+
+ cylon_pos = m->jobs_in_progress_iteration % 14;
+ if (cylon_pos >= 8)
+ cylon_pos = 14 - cylon_pos;
+ draw_cylon(cylon, sizeof(cylon), 6, cylon_pos);
+
+ if (m->n_running_jobs > 1)
+ if (asprintf(&job_of_n, "(%u of %u) ", counter, m->n_running_jobs) < 0)
+ job_of_n = NULL;
+
+ manager_status_printf(m, true, cylon, "%sA %s job is running for %s",
+ strempty(job_of_n), job_type_to_string(j->type), unit_description(j->unit));
+ free(job_of_n);
+
+ m->jobs_in_progress_iteration++;
+}
+
static int manager_setup_time_change(Manager *m) {
- struct epoll_event ev;
- struct itimerspec its;
+ struct epoll_event ev = {
+ .events = EPOLLIN,
+ .data.ptr = &m->time_change_watch,
+ };
+
+ /* We only care for the cancellation event, hence we set the
+ * timeout to the latest possible value. */
+ struct itimerspec its = {
+ .it_value.tv_sec = TIME_T_MAX,
+ };
+ assert_cc(sizeof(time_t) == sizeof(TIME_T_MAX));
- assert(m);
assert(m->time_change_watch.type == WATCH_INVALID);
/* Uses TFD_TIMER_CANCEL_ON_SET to get notifications whenever
@@ -153,13 +301,6 @@ static int manager_setup_time_change(Manager *m) {
return -errno;
}
- zero(its);
-
- /* We only care for the cancellation event, hence we set the
- * timeout to the latest possible value. */
- assert_cc(sizeof(time_t) == sizeof(long));
- its.it_value.tv_sec = LONG_MAX;
-
if (timerfd_settime(m->time_change_watch.fd, TFD_TIMER_ABSTIME|TFD_TIMER_CANCEL_ON_SET, &its, NULL) < 0) {
log_debug("Failed to set up TFD_TIMER_CANCEL_ON_SET, ignoring: %m");
close_nointr_nofail(m->time_change_watch.fd);
@@ -167,10 +308,6 @@ static int manager_setup_time_change(Manager *m) {
return 0;
}
- zero(ev);
- ev.events = EPOLLIN;
- ev.data.ptr = &m->time_change_watch;
-
if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->time_change_watch.fd, &ev) < 0) {
log_error("Failed to add timer change fd to epoll: %m");
return -errno;
@@ -210,15 +347,18 @@ static int enable_special_signals(Manager *m) {
static int manager_setup_signals(Manager *m) {
sigset_t mask;
- struct epoll_event ev;
- struct sigaction sa;
+ struct epoll_event ev = {
+ .events = EPOLLIN,
+ .data.ptr = &m->signal_watch,
+ };
+ struct sigaction sa = {
+ .sa_handler = SIG_DFL,
+ .sa_flags = SA_NOCLDSTOP|SA_RESTART,
+ };
assert(m);
/* We are not interested in SIGSTOP and friends. */
- zero(sa);
- sa.sa_handler = SIG_DFL;
- sa.sa_flags = SA_NOCLDSTOP|SA_RESTART;
assert_se(sigaction(SIGCHLD, &sa, NULL) == 0);
assert_se(sigemptyset(&mask) == 0);
@@ -260,10 +400,6 @@ static int manager_setup_signals(Manager *m) {
if (m->signal_watch.fd < 0)
return -errno;
- zero(ev);
- ev.events = EPOLLIN;
- ev.data.ptr = &m->signal_watch;
-
if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->signal_watch.fd, &ev) < 0)
return -errno;
@@ -286,6 +422,9 @@ static void manager_strip_environment(Manager *m) {
* the initrd interface:
* http://www.freedesktop.org/wiki/Software/systemd/InitrdInterface */
strv_remove_prefix(m->environment, "RD_");
+
+ /* Drop invalid entries */
+ strv_env_clean(m->environment);
}
int manager_new(SystemdRunningAs running_as, Manager **_m) {
@@ -300,9 +439,6 @@ int manager_new(SystemdRunningAs running_as, Manager **_m) {
if (!m)
return -ENOMEM;
- dual_timestamp_get(&m->userspace_timestamp);
- dual_timestamp_from_monotonic(&m->kernel_timestamp, 0);
-
m->running_as = running_as;
m->name_data_slot = m->conn_data_slot = m->subscribed_data_slot = -1;
m->exit_code = _MANAGER_EXIT_CODE_INVALID;
@@ -314,6 +450,7 @@ int manager_new(SystemdRunningAs running_as, Manager **_m) {
watch_init(&m->swap_watch);
watch_init(&m->udev_watch);
watch_init(&m->time_change_watch);
+ watch_init(&m->jobs_in_progress_watch);
m->epoll_fd = m->dev_autofs_fd = -1;
m->current_job_id = 1; /* start as id #1, so that we can leave #0 around as "null-like" value */
@@ -519,6 +656,9 @@ static void manager_clear_jobs_and_units(Manager *m) {
assert(hashmap_isempty(m->jobs));
assert(hashmap_isempty(m->units));
+
+ m->n_on_console = 0;
+ m->n_running_jobs = 0;
}
void manager_free(Manager *m) {
@@ -554,6 +694,8 @@ void manager_free(Manager *m) {
close_nointr_nofail(m->notify_watch.fd);
if (m->time_change_watch.fd >= 0)
close_nointr_nofail(m->time_change_watch.fd);
+ if (m->jobs_in_progress_watch.fd >= 0)
+ close_nointr_nofail(m->jobs_in_progress_watch.fd);
free(m->notify_socket);
@@ -617,14 +759,15 @@ int manager_coldplug(Manager *m) {
static void manager_build_unit_path_cache(Manager *m) {
char **i;
- DIR *d = NULL;
+ _cleanup_free_ DIR *d = NULL;
int r;
assert(m);
set_free_free(m->unit_path_cache);
- if (!(m->unit_path_cache = set_new(string_hash_func, string_compare_func))) {
+ m->unit_path_cache = set_new(string_hash_func, string_compare_func);
+ if (!m->unit_path_cache) {
log_error("Failed to allocate unit path cache.");
return;
}
@@ -637,7 +780,8 @@ static void manager_build_unit_path_cache(Manager *m) {
d = opendir(*i);
if (!d) {
- log_error("Failed to open directory: %m");
+ if (errno != ENOENT)
+ log_error("Failed to open directory %s: %m", *i);
continue;
}
@@ -653,10 +797,9 @@ static void manager_build_unit_path_cache(Manager *m) {
goto fail;
}
- if ((r = set_put(m->unit_path_cache, p)) < 0) {
- free(p);
+ r = set_consume(m->unit_path_cache, p);
+ if (r < 0)
goto fail;
- }
}
closedir(d);
@@ -670,9 +813,6 @@ fail:
set_free_free(m->unit_path_cache);
m->unit_path_cache = NULL;
-
- if (d)
- closedir(d);
}
int manager_startup(Manager *m, FILE *serialization, FDSet *fds) {
@@ -756,7 +896,7 @@ int manager_add_job(Manager *m, JobType type, Unit *unit, JobMode mode, bool ove
job_type_collapse(&type, unit);
- tr = transaction_new();
+ tr = transaction_new(mode == JOB_REPLACE_IRREVERSIBLY);
if (!tr)
return -ENOMEM;
@@ -978,6 +1118,10 @@ unsigned manager_dispatch_run_queue(Manager *m) {
}
m->dispatching_run_queue = false;
+
+ if (m->n_running_jobs > 0)
+ manager_watch_jobs_in_progress(m);
+
return n;
}
@@ -1018,30 +1162,29 @@ static int manager_process_notify_fd(Manager *m) {
for (;;) {
char buf[4096];
- struct msghdr msghdr;
- struct iovec iovec;
- struct ucred *ucred;
+ struct iovec iovec = {
+ .iov_base = buf,
+ .iov_len = sizeof(buf)-1,
+ };
+
union {
struct cmsghdr cmsghdr;
uint8_t buf[CMSG_SPACE(sizeof(struct ucred))];
- } control;
+ } control = {};
+
+ struct msghdr msghdr = {
+ .msg_iov = &iovec,
+ .msg_iovlen = 1,
+ .msg_control = &control,
+ .msg_controllen = sizeof(control),
+ };
+ struct ucred *ucred;
Unit *u;
- char **tags;
-
- zero(iovec);
- iovec.iov_base = buf;
- iovec.iov_len = sizeof(buf)-1;
-
- zero(control);
- zero(msghdr);
- msghdr.msg_iov = &iovec;
- msghdr.msg_iovlen = 1;
- msghdr.msg_control = &control;
- msghdr.msg_controllen = sizeof(control);
+ _cleanup_strv_free_ char **tags = NULL;
n = recvmsg(m->notify_watch.fd, &msghdr, MSG_DONTWAIT);
if (n <= 0) {
- if (n >= 0)
+ if (n == 0)
return -EIO;
if (errno == EAGAIN || errno == EINTR)
@@ -1079,8 +1222,6 @@ static int manager_process_notify_fd(Manager *m) {
if (UNIT_VTABLE(u)->notify_message)
UNIT_VTABLE(u)->notify_message(u, ucred->pid, tags);
-
- strv_free(tags);
}
return 0;
@@ -1090,12 +1231,10 @@ static int manager_dispatch_sigchld(Manager *m) {
assert(m);
for (;;) {
- siginfo_t si;
+ siginfo_t si = {};
Unit *u;
int r;
- zero(si);
-
/* First we call waitd() for a PID and do not reap the
* zombie. That way we can still access /proc/$PID for
* it while it is a zombie. */
@@ -1114,7 +1253,7 @@ static int manager_dispatch_sigchld(Manager *m) {
break;
if (si.si_code == CLD_EXITED || si.si_code == CLD_KILLED || si.si_code == CLD_DUMPED) {
- char _cleanup_free_ *name = NULL;
+ _cleanup_free_ char *name = NULL;
get_process_comm(si.si_pid, &name);
log_debug("Got SIGCHLD for process %lu (%s)", (unsigned long) si.si_pid, strna(name));
@@ -1505,6 +1644,8 @@ static int process_event(Manager *m, struct epoll_event *ev) {
NULL);
/* Restart the watch */
+ epoll_ctl(m->epoll_fd, EPOLL_CTL_DEL, m->time_change_watch.fd,
+ NULL);
close_nointr_nofail(m->time_change_watch.fd);
watch_init(&m->time_change_watch);
manager_setup_time_change(m);
@@ -1517,6 +1658,16 @@ static int process_event(Manager *m, struct epoll_event *ev) {
break;
}
+ case WATCH_JOBS_IN_PROGRESS: {
+ uint64_t v;
+
+ /* not interested in the data */
+ read(w->fd, &v, sizeof(v));
+
+ manager_print_jobs_in_progress(m);
+ break;
+ }
+
default:
log_error("event type=%i", w->type);
assert_not_reached("Unknown epoll event type.");
@@ -1724,7 +1875,8 @@ void manager_send_unit_plymouth(Manager *m, Unit *u) {
/* We set SOCK_NONBLOCK here so that we rather drop the
* message then wait for plymouth */
- if ((fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0)) < 0) {
+ fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
+ if (fd < 0) {
log_error("socket() failed: %m");
return;
}
@@ -1807,7 +1959,6 @@ void manager_dispatch_bus_query_pid_done(
int manager_open_serialization(Manager *m, FILE **_f) {
char *path = NULL;
- mode_t saved_umask;
int fd;
FILE *f;
@@ -1821,9 +1972,9 @@ int manager_open_serialization(Manager *m, FILE **_f) {
if (!path)
return -ENOMEM;
- saved_umask = umask(0077);
- fd = mkostemp(path, O_RDWR|O_CLOEXEC);
- umask(saved_umask);
+ RUN_WITH_UMASK(0077) {
+ fd = mkostemp(path, O_RDWR|O_CLOEXEC);
+ }
if (fd < 0) {
free(path);
@@ -1844,10 +1995,11 @@ int manager_open_serialization(Manager *m, FILE **_f) {
return 0;
}
-int manager_serialize(Manager *m, FILE *f, FDSet *fds, bool serialize_jobs) {
+int manager_serialize(Manager *m, FILE *f, FDSet *fds, bool switching_root) {
Iterator i;
Unit *u;
const char *t;
+ char **e;
int r;
assert(m);
@@ -1871,6 +2023,16 @@ int manager_serialize(Manager *m, FILE *f, FDSet *fds, bool serialize_jobs) {
dual_timestamp_serialize(f, "finish-timestamp", &m->finish_timestamp);
}
+ if (!switching_root) {
+ STRV_FOREACH(e, m->environment) {
+ _cleanup_free_ char *ce;
+
+ ce = cescape(*e);
+ if (ce)
+ fprintf(f, "env=%s\n", *e);
+ }
+ }
+
fputc('\n', f);
HASHMAP_FOREACH_KEY(u, t, m->units, i) {
@@ -1884,7 +2046,7 @@ int manager_serialize(Manager *m, FILE *f, FDSet *fds, bool serialize_jobs) {
fputs(u->id, f);
fputc('\n', f);
- if ((r = unit_serialize(u, f, fds, serialize_jobs)) < 0) {
+ if ((r = unit_serialize(u, f, fds, !switching_root)) < 0) {
m->n_reloading --;
return r;
}
@@ -1971,7 +2133,25 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) {
dual_timestamp_deserialize(l+20, &m->userspace_timestamp);
else if (startswith(l, "finish-timestamp="))
dual_timestamp_deserialize(l+17, &m->finish_timestamp);
- else
+ else if (startswith(l, "env=")) {
+ _cleanup_free_ char *uce = NULL;
+ char **e;
+
+ uce = cunescape(l+4);
+ if (!uce) {
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ e = strv_env_set(m->environment, uce);
+ if (!e) {
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ strv_free(m->environment);
+ m->environment = e;
+ } else
log_debug("Unknown serialization item '%s'", l);
}
@@ -2054,7 +2234,7 @@ int manager_reload(Manager *m) {
goto finish;
}
- r = manager_serialize(m, f, fds, true);
+ r = manager_serialize(m, f, fds, false);
if (r < 0) {
m->n_reloading --;
goto finish;
@@ -2115,7 +2295,7 @@ finish:
return r;
}
-bool manager_is_booting_or_shutting_down(Manager *m) {
+static bool manager_is_booting_or_shutting_down(Manager *m) {
Unit *u;
assert(m);
@@ -2132,6 +2312,12 @@ bool manager_is_booting_or_shutting_down(Manager *m) {
return false;
}
+bool manager_is_reloading_or_reexecuting(Manager *m) {
+ assert(m);
+
+ return m->n_reloading != 0;
+}
+
void manager_reset_failed(Manager *m) {
Unit *u;
Iterator i;
@@ -2142,7 +2328,7 @@ void manager_reset_failed(Manager *m) {
unit_reset_failed(u);
}
-bool manager_unit_pending_inactive(Manager *m, const char *name) {
+bool manager_unit_inactive_or_pending(Manager *m, const char *name) {
Unit *u;
assert(m);
@@ -2153,7 +2339,7 @@ bool manager_unit_pending_inactive(Manager *m, const char *name) {
if (!u)
return true;
- return unit_pending_inactive(u);
+ return unit_inactive_or_pending(u);
}
void manager_check_finished(Manager *m) {
@@ -2162,8 +2348,13 @@ void manager_check_finished(Manager *m) {
assert(m);
- if (hashmap_size(m->jobs) > 0)
+ if (m->n_running_jobs == 0)
+ manager_unwatch_jobs_in_progress(m);
+
+ if (hashmap_size(m->jobs) > 0) {
+ manager_jobs_in_progress_mod_timer(m);
return;
+ }
/* Notify Type=idle units that we are done now */
close_pipe(m->idle_pipe);
@@ -2200,10 +2391,10 @@ void manager_check_finished(Manager *m) {
"INITRD_USEC=%llu", (unsigned long long) initrd_usec,
"USERSPACE_USEC=%llu", (unsigned long long) userspace_usec,
"MESSAGE=Startup finished in %s (kernel) + %s (initrd) + %s (userspace) = %s.",
- format_timespan(kernel, sizeof(kernel), kernel_usec),
- format_timespan(initrd, sizeof(initrd), initrd_usec),
- format_timespan(userspace, sizeof(userspace), userspace_usec),
- format_timespan(sum, sizeof(sum), total_usec),
+ format_timespan(kernel, sizeof(kernel), kernel_usec, USEC_PER_MSEC),
+ format_timespan(initrd, sizeof(initrd), initrd_usec, USEC_PER_MSEC),
+ format_timespan(userspace, sizeof(userspace), userspace_usec, USEC_PER_MSEC),
+ format_timespan(sum, sizeof(sum), total_usec, USEC_PER_MSEC),
NULL);
} else {
kernel_usec = m->userspace_timestamp.monotonic - m->kernel_timestamp.monotonic;
@@ -2215,9 +2406,9 @@ void manager_check_finished(Manager *m) {
"KERNEL_USEC=%llu", (unsigned long long) kernel_usec,
"USERSPACE_USEC=%llu", (unsigned long long) userspace_usec,
"MESSAGE=Startup finished in %s (kernel) + %s (userspace) = %s.",
- format_timespan(kernel, sizeof(kernel), kernel_usec),
- format_timespan(userspace, sizeof(userspace), userspace_usec),
- format_timespan(sum, sizeof(sum), total_usec),
+ format_timespan(kernel, sizeof(kernel), kernel_usec, USEC_PER_MSEC),
+ format_timespan(userspace, sizeof(userspace), userspace_usec, USEC_PER_MSEC),
+ format_timespan(sum, sizeof(sum), total_usec, USEC_PER_MSEC),
NULL);
}
} else {
@@ -2229,7 +2420,7 @@ void manager_check_finished(Manager *m) {
MESSAGE_ID(SD_MESSAGE_STARTUP_FINISHED),
"USERSPACE_USEC=%llu", (unsigned long long) userspace_usec,
"MESSAGE=Startup finished in %s.",
- format_timespan(sum, sizeof(sum), total_usec),
+ format_timespan(sum, sizeof(sum), total_usec, USEC_PER_MSEC),
NULL);
}
@@ -2237,7 +2428,7 @@ void manager_check_finished(Manager *m) {
sd_notifyf(false,
"READY=1\nSTATUS=Startup finished in %s.",
- format_timespan(sum, sizeof(sum), total_usec));
+ format_timespan(sum, sizeof(sum), total_usec, USEC_PER_MSEC));
}
static int create_generator_dir(Manager *m, char **generator, const char *name) {
@@ -2259,7 +2450,8 @@ static int create_generator_dir(Manager *m, char **generator, const char *name)
r = mkdir_p_label(p, 0755);
if (r < 0) {
- log_error("Failed to create generator directory: %s", strerror(-r));
+ log_error("Failed to create generator directory %s: %s",
+ p, strerror(-r));
free(p);
return r;
}
@@ -2269,8 +2461,9 @@ static int create_generator_dir(Manager *m, char **generator, const char *name)
return log_oom();
if (!mkdtemp(p)) {
+ log_error("Failed to create generator directory %s: %m",
+ p);
free(p);
- log_error("Failed to create generator directory: %m");
return -errno;
}
}
@@ -2298,7 +2491,6 @@ void manager_run_generators(Manager *m) {
DIR *d = NULL;
const char *generator_path;
const char *argv[5];
- mode_t u;
int r;
assert(m);
@@ -2309,7 +2501,8 @@ void manager_run_generators(Manager *m) {
if (errno == ENOENT)
return;
- log_error("Failed to enumerate generator directory: %m");
+ log_error("Failed to enumerate generator directory %s: %m",
+ generator_path);
return;
}
@@ -2331,9 +2524,9 @@ void manager_run_generators(Manager *m) {
argv[3] = m->generator_unit_path_late;
argv[4] = NULL;
- u = umask(0022);
- execute_directory(generator_path, d, (char**) argv);
- umask(u);
+ RUN_WITH_UMASK(0022) {
+ execute_directory(generator_path, d, (char**) argv);
+ }
trim_generator_dir(m, &m->generator_unit_path);
trim_generator_dir(m, &m->generator_unit_path_early);
@@ -2439,7 +2632,7 @@ void manager_set_show_status(Manager *m, bool b) {
unlink("/run/systemd/show-status");
}
-bool manager_get_show_status(Manager *m) {
+static bool manager_get_show_status(Manager *m) {
assert(m);
if (m->running_as != SYSTEMD_SYSTEM)
@@ -2454,6 +2647,25 @@ bool manager_get_show_status(Manager *m) {
return plymouth_running();
}
+void manager_status_printf(Manager *m, bool ephemeral, const char *status, const char *format, ...) {
+ va_list ap;
+
+ if (!manager_get_show_status(m))
+ return;
+
+ /* XXX We should totally drop the check for ephemeral here
+ * and thus effectively make 'Type=idle' pointless. */
+ if (ephemeral && m->n_on_console > 0)
+ return;
+
+ if (!manager_is_booting_or_shutting_down(m))
+ return;
+
+ va_start(ap, format);
+ status_vprintf(status, true, ephemeral, format, ap);
+ va_end(ap);
+}
+
void watch_init(Watch *w) {
assert(w);
diff --git a/src/core/manager.h b/src/core/manager.h
index cc4edf8f1e..bf833540ae 100644
--- a/src/core/manager.h
+++ b/src/core/manager.h
@@ -61,7 +61,8 @@ enum WatchType {
WATCH_UDEV,
WATCH_DBUS_WATCH,
WATCH_DBUS_TIMEOUT,
- WATCH_TIME_CHANGE
+ WATCH_TIME_CHANGE,
+ WATCH_JOBS_IN_PROGRESS
};
struct Watch {
@@ -84,6 +85,7 @@ struct Watch {
#include "set.h"
#include "dbus.h"
#include "path-lookup.h"
+#include "execute.h"
struct Manager {
/* Note that the set of units we know of is allowed to be
@@ -127,6 +129,7 @@ struct Manager {
Watch notify_watch;
Watch signal_watch;
Watch time_change_watch;
+ Watch jobs_in_progress_watch;
int epoll_fd;
@@ -225,6 +228,11 @@ struct Manager {
unsigned n_installed_jobs;
unsigned n_failed_jobs;
+ /* Jobs in progress watching */
+ unsigned n_running_jobs;
+ unsigned n_on_console;
+ unsigned jobs_in_progress_iteration;
+
/* Type=idle pipes */
int idle_pipe[2];
@@ -270,20 +278,20 @@ void manager_dispatch_bus_query_pid_done(Manager *m, const char *name, pid_t pid
int manager_open_serialization(Manager *m, FILE **_f);
-int manager_serialize(Manager *m, FILE *f, FDSet *fds, bool serialize_jobs);
+int manager_serialize(Manager *m, FILE *f, FDSet *fds, bool switching_root);
int manager_deserialize(Manager *m, FILE *f, FDSet *fds);
int manager_distribute_fds(Manager *m, FDSet *fds);
int manager_reload(Manager *m);
-bool manager_is_booting_or_shutting_down(Manager *m);
+bool manager_is_reloading_or_reexecuting(Manager *m) _pure_;
void manager_reset_failed(Manager *m);
void manager_send_unit_audit(Manager *m, Unit *u, int type, bool success);
void manager_send_unit_plymouth(Manager *m, Unit *u);
-bool manager_unit_pending_inactive(Manager *m, const char *name);
+bool manager_unit_inactive_or_pending(Manager *m, const char *name);
void manager_check_finished(Manager *m);
@@ -293,6 +301,6 @@ void manager_undo_generators(Manager *m);
void manager_recheck_journal(Manager *m);
void manager_set_show_status(Manager *m, bool b);
-bool manager_get_show_status(Manager *m);
+void manager_status_printf(Manager *m, bool ephemeral, const char *status, const char *format, ...) _printf_attr_(4,5);
void watch_init(Watch *w);
diff --git a/src/core/mount-setup.c b/src/core/mount-setup.c
index 98614d0c3e..4629808a7a 100644
--- a/src/core/mount-setup.c
+++ b/src/core/mount-setup.c
@@ -41,6 +41,7 @@
#include "path-util.h"
#include "missing.h"
#include "virt.h"
+#include "efivars.h"
#ifndef TTY_GID
#define TTY_GID 5
@@ -65,7 +66,7 @@ typedef struct MountPoint {
/* The first three entries we might need before SELinux is up. The
* fourth (securityfs) is needed by IMA to load a custom policy. The
* other ones we can delay until SELinux and IMA are loaded. */
-#define N_EARLY_MOUNT 4
+#define N_EARLY_MOUNT 5
static const MountPoint mount_table[] = {
{ "proc", "/proc", "proc", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV,
@@ -76,8 +77,8 @@ static const MountPoint mount_table[] = {
NULL, MNT_FATAL|MNT_IN_CONTAINER },
{ "securityfs", "/sys/kernel/security", "securityfs", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV,
NULL, MNT_NONE },
- { "efivarfs", "/sys/firmware/efi/efivars", "efivarfs", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV,
- is_efiboot, MNT_NONE },
+ { "smackfs", "/sys/fs/smackfs", "smackfs", "smackfsdef=*", MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME,
+ NULL, MNT_NONE },
{ "tmpfs", "/dev/shm", "tmpfs", "mode=1777", MS_NOSUID|MS_NODEV|MS_STRICTATIME,
NULL, MNT_FATAL|MNT_IN_CONTAINER },
{ "devpts", "/dev/pts", "devpts", "mode=620,gid=" STRINGIFY(TTY_GID), MS_NOSUID|MS_NOEXEC,
@@ -86,8 +87,18 @@ static const MountPoint mount_table[] = {
NULL, MNT_FATAL|MNT_IN_CONTAINER },
{ "tmpfs", "/sys/fs/cgroup", "tmpfs", "mode=755", MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME,
NULL, MNT_IN_CONTAINER },
+#ifdef HAVE_XATTR
+ { "cgroup", "/sys/fs/cgroup/systemd", "cgroup", "none,name=systemd,xattr", MS_NOSUID|MS_NOEXEC|MS_NODEV,
+ NULL, MNT_IN_CONTAINER },
+#endif
{ "cgroup", "/sys/fs/cgroup/systemd", "cgroup", "none,name=systemd", MS_NOSUID|MS_NOEXEC|MS_NODEV,
NULL, MNT_IN_CONTAINER },
+ { "pstore", "/sys/fs/pstore", "pstore", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV,
+ NULL, MNT_NONE },
+#ifdef ENABLE_EFI
+ { "efivarfs", "/sys/firmware/efi/efivars", "efivarfs", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV,
+ is_efi_boot, MNT_NONE },
+#endif
};
/* These are API file systems that might be mounted by other software,
@@ -200,9 +211,9 @@ int mount_setup_early(void) {
int mount_cgroup_controllers(char ***join_controllers) {
int r;
- FILE *f;
char buf[LINE_MAX];
- Set *controllers;
+ _cleanup_set_free_free_ Set *controllers = NULL;
+ _cleanup_fclose_ FILE *f;
/* Mount all available cgroup controllers that are built into the kernel. */
@@ -213,10 +224,8 @@ int mount_cgroup_controllers(char ***join_controllers) {
}
controllers = set_new(string_hash_func, string_compare_func);
- if (!controllers) {
- r = log_oom();
- goto finish;
- }
+ if (!controllers)
+ return log_oom();
/* Ignore the header line */
(void) fgets(buf, sizeof(buf), f);
@@ -231,8 +240,7 @@ int mount_cgroup_controllers(char ***join_controllers) {
break;
log_error("Failed to parse /proc/cgroups.");
- r = -EIO;
- goto finish;
+ return -EIO;
}
if (!enabled) {
@@ -240,18 +248,22 @@ int mount_cgroup_controllers(char ***join_controllers) {
continue;
}
- r = set_put(controllers, controller);
+ r = set_consume(controllers, controller);
if (r < 0) {
log_error("Failed to add controller to set.");
- free(controller);
- goto finish;
+ return r;
}
}
for (;;) {
- MountPoint p;
- char *controller, *where, *options;
+ MountPoint p = {
+ .what = "cgroup",
+ .type = "cgroup",
+ .flags = MS_NOSUID|MS_NOEXEC|MS_NODEV,
+ .mode = MNT_IN_CONTAINER,
+ };
char ***k = NULL;
+ _cleanup_free_ char *options = NULL, *controller;
controller = set_steal_first(controllers);
if (!controller)
@@ -268,14 +280,13 @@ int mount_cgroup_controllers(char ***join_controllers) {
for (i = *k, j = *k; *i; i++) {
if (!streq(*i, controller)) {
- char *t;
+ char _cleanup_free_ *t;
t = set_remove(controllers, *i);
if (!t) {
free(*i);
continue;
}
- free(t);
}
*(j++) = *i;
@@ -284,76 +295,36 @@ int mount_cgroup_controllers(char ***join_controllers) {
*j = NULL;
options = strv_join(*k, ",");
- if (!options) {
- free(controller);
- r = log_oom();
- goto finish;
- }
-
+ if (!options)
+ return log_oom();
} else {
options = controller;
controller = NULL;
}
- where = strappend("/sys/fs/cgroup/", options);
- if (!where) {
- free(options);
- r = log_oom();
- goto finish;
- }
-
- zero(p);
- p.what = "cgroup";
- p.where = where;
- p.type = "cgroup";
+ p.where = strappenda("/sys/fs/cgroup/", options);
p.options = options;
- p.flags = MS_NOSUID|MS_NOEXEC|MS_NODEV;
r = mount_one(&p, true);
- free(controller);
- free(where);
-
- if (r < 0) {
- free(options);
- goto finish;
- }
+ if (r < 0)
+ return r;
if (r > 0 && k && *k) {
char **i;
for (i = *k; *i; i++) {
- char *t;
-
- t = strappend("/sys/fs/cgroup/", *i);
- if (!t) {
- r = log_oom();
- free(options);
- goto finish;
- }
+ char *t = strappenda("/sys/fs/cgroup/", *i);
r = symlink(options, t);
- free(t);
-
if (r < 0 && errno != EEXIST) {
- log_error("Failed to create symlink: %m");
- r = -errno;
- free(options);
- goto finish;
+ log_error("Failed to create symlink %s: %m", t);
+ return -errno;
}
}
}
-
- free(options);
}
- r = 0;
-
-finish:
- set_free_free(controllers);
-
- fclose(f);
-
- return r;
+ return 0;
}
static int nftw_cb(
@@ -415,7 +386,7 @@ int mount_setup(bool loaded_policy) {
after_relabel = now(CLOCK_MONOTONIC);
log_info("Relabelled /dev and /run in %s.",
- format_timespan(timespan, sizeof(timespan), after_relabel - before_relabel));
+ format_timespan(timespan, sizeof(timespan), after_relabel - before_relabel, 0));
}
/* Create a few default symlinks, which are normally created
@@ -433,9 +404,14 @@ int mount_setup(bool loaded_policy) {
if (mount(NULL, "/", NULL, MS_REC|MS_SHARED, NULL) < 0)
log_warning("Failed to set up the root directory for shared mount propagation: %m");
- /* Create a few directories we always want around */
+ /* Create a few directories we always want around, Note that
+ * sd_booted() checks for /run/systemd/system, so this mkdir
+ * really needs to stay for good, otherwise software that
+ * copied sd-daemon.c into their sources will misdetect
+ * systemd. */
mkdir_label("/run/systemd", 0755);
mkdir_label("/run/systemd/system", 0755);
+ mkdir_label("/run/systemd/inaccessible", 0000);
return 0;
}
diff --git a/src/core/mount.c b/src/core/mount.c
index 5d2b010013..10073b50be 100644
--- a/src/core/mount.c
+++ b/src/core/mount.c
@@ -25,6 +25,7 @@
#include <sys/epoll.h>
#include <signal.h>
+#include "manager.h"
#include "unit.h"
#include "mount.h"
#include "load-fragment.h"
@@ -126,7 +127,7 @@ static void mount_done(Unit *u) {
mount_parameters_done(&m->parameters_proc_self_mountinfo);
mount_parameters_done(&m->parameters_fragment);
- exec_context_done(&m->exec_context);
+ exec_context_done(&m->exec_context, manager_is_reloading_or_reexecuting(u->manager));
exec_command_done_array(m->exec_command, _MOUNT_EXEC_COMMAND_MAX);
m->control_command = NULL;
@@ -135,7 +136,7 @@ static void mount_done(Unit *u) {
unit_unwatch_timer(u, &m->timer_watch);
}
-static MountParameters* get_mount_parameters_fragment(Mount *m) {
+_pure_ static MountParameters* get_mount_parameters_fragment(Mount *m) {
assert(m);
if (m->from_fragment)
@@ -144,7 +145,7 @@ static MountParameters* get_mount_parameters_fragment(Mount *m) {
return NULL;
}
-static MountParameters* get_mount_parameters(Mount *m) {
+_pure_ static MountParameters* get_mount_parameters(Mount *m) {
assert(m);
if (m->from_proc_self_mountinfo)
@@ -292,7 +293,7 @@ static int mount_add_requires_mounts_links(Mount *m) {
}
static char* mount_test_option(const char *haystack, const char *needle) {
- struct mntent me;
+ struct mntent me = { .mnt_opts = (char*) haystack };
assert(needle);
@@ -302,9 +303,6 @@ static char* mount_test_option(const char *haystack, const char *needle) {
if (!haystack)
return NULL;
- zero(me);
- me.mnt_opts = (char*) haystack;
-
return hasmntopt(&me, needle);
}
@@ -329,6 +327,12 @@ static bool mount_is_bind(MountParameters *p) {
if (p->fstype && streq(p->fstype, "bind"))
return true;
+ if (mount_test_option(p->options, "rbind"))
+ return true;
+
+ if (p->fstype && streq(p->fstype, "rbind"))
+ return true;
+
return false;
}
@@ -432,31 +436,49 @@ static int mount_add_quota_links(Mount *m) {
}
static int mount_add_default_dependencies(Mount *m) {
- int r;
+ const char *after, *after2, *online;
MountParameters *p;
- const char *after;
+ int r;
assert(m);
if (UNIT(m)->manager->running_as != SYSTEMD_SYSTEM)
return 0;
- p = get_mount_parameters_fragment(m);
+ p = get_mount_parameters(m);
+
if (!p)
return 0;
if (path_equal(m->where, "/"))
return 0;
- if (mount_is_network(p))
+ if (mount_is_network(p)) {
after = SPECIAL_REMOTE_FS_PRE_TARGET;
- else
+ after2 = SPECIAL_NETWORK_TARGET;
+ online = SPECIAL_NETWORK_ONLINE_TARGET;
+ } else {
after = SPECIAL_LOCAL_FS_PRE_TARGET;
+ after2 = NULL;
+ online = NULL;
+ }
- r = unit_add_two_dependencies_by_name(UNIT(m), UNIT_WANTS, UNIT_AFTER, after, NULL, true);
+ r = unit_add_dependency_by_name(UNIT(m), UNIT_AFTER, after, NULL, true);
if (r < 0)
return r;
+ if (after2) {
+ r = unit_add_dependency_by_name(UNIT(m), UNIT_AFTER, after2, NULL, true);
+ if (r < 0)
+ return r;
+ }
+
+ if (online) {
+ r = unit_add_two_dependencies_by_name(UNIT(m), UNIT_WANTS, UNIT_AFTER, online, NULL, true);
+ if (r < 0)
+ return r;
+ }
+
r = unit_add_two_dependencies_by_name(UNIT(m), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_UMOUNT_TARGET, NULL, true);
if (r < 0)
return r;
@@ -495,7 +517,7 @@ static int mount_fix_timeouts(Mount *m) {
if (!t)
return -ENOMEM;
- r = parse_usec(t, &u);
+ r = parse_sec(t, &u);
free(t);
if (r < 0) {
@@ -717,9 +739,9 @@ static void mount_set_state(Mount *m, MountState state) {
state == MOUNT_UNMOUNTING_SIGTERM ||
state == MOUNT_UNMOUNTING_SIGKILL ||
state == MOUNT_FAILED) {
- if (state != old_state)
- mount_notify_automount(m, -ENODEV);
- }
+ if (state != old_state)
+ mount_notify_automount(m, -ENODEV);
+ }
if (state != old_state)
log_debug_unit(UNIT(m)->id,
@@ -863,6 +885,7 @@ static void mount_enter_dead(Mount *m, MountResult f) {
if (f != MOUNT_SUCCESS)
m->result = f;
+ exec_context_tmp_dirs_done(&m->exec_context);
mount_set_state(m, m->result != MOUNT_SUCCESS ? MOUNT_FAILED : MOUNT_DEAD);
}
@@ -877,56 +900,23 @@ static void mount_enter_mounted(Mount *m, MountResult f) {
static void mount_enter_signal(Mount *m, MountState state, MountResult f) {
int r;
- Set *pid_set = NULL;
- bool wait_for_exit = false;
assert(m);
if (f != MOUNT_SUCCESS)
m->result = f;
- if (m->kill_context.kill_mode != KILL_NONE) {
- int sig = (state == MOUNT_MOUNTING_SIGTERM ||
- state == MOUNT_UNMOUNTING_SIGTERM ||
- state == MOUNT_REMOUNTING_SIGTERM) ? m->kill_context.kill_signal : SIGKILL;
-
- if (m->control_pid > 0) {
- if (kill_and_sigcont(m->control_pid, sig) < 0 && errno != ESRCH)
-
- log_warning_unit(UNIT(m)->id,
- "Failed to kill control process %li: %m",
- (long) m->control_pid);
- else
- wait_for_exit = true;
- }
-
- if (m->kill_context.kill_mode == KILL_CONTROL_GROUP) {
-
- if (!(pid_set = set_new(trivial_hash_func, trivial_compare_func))) {
- r = -ENOMEM;
- goto fail;
- }
-
- /* Exclude the control pid from being killed via the cgroup */
- if (m->control_pid > 0)
- if ((r = set_put(pid_set, LONG_TO_PTR(m->control_pid))) < 0)
- goto fail;
-
- r = cgroup_bonding_kill_list(UNIT(m)->cgroup_bondings, sig, true, false, pid_set, NULL);
- if (r < 0) {
- if (r != -EAGAIN && r != -ESRCH && r != -ENOENT)
- log_warning_unit(UNIT(m)->id,
- "Failed to kill control group: %s",
- strerror(-r));
- } else if (r > 0)
- wait_for_exit = true;
-
- set_free(pid_set);
- pid_set = NULL;
- }
- }
+ r = unit_kill_context(
+ UNIT(m),
+ &m->kill_context,
+ state != MOUNT_MOUNTING_SIGTERM && state != MOUNT_UNMOUNTING_SIGTERM && state != MOUNT_REMOUNTING_SIGTERM,
+ -1,
+ m->control_pid,
+ false);
+ if (r < 0)
+ goto fail;
- if (wait_for_exit) {
+ if (r > 0) {
r = unit_watch_timer(UNIT(m), CLOCK_MONOTONIC, true, m->timeout_usec, &m->timer_watch);
if (r < 0)
goto fail;
@@ -947,19 +937,20 @@ fail:
mount_enter_mounted(m, MOUNT_FAILURE_RESOURCES);
else
mount_enter_dead(m, MOUNT_FAILURE_RESOURCES);
-
- if (pid_set)
- set_free(pid_set);
}
void warn_if_dir_nonempty(const char *unit, const char* where) {
+ assert(unit);
+ assert(where);
+
if (dir_is_empty(where) > 0)
return;
- log_struct(LOG_NOTICE,
+
+ log_struct_unit(LOG_NOTICE,
+ unit,
"MESSAGE=%s: Directory %s to mount over is not empty, mounting anyway.",
unit, where,
"WHERE=%s", where,
- "_SYSTEMD_UNIT=%s", unit,
MESSAGE_ID(SD_MESSAGE_OVERMOUNTING),
NULL);
}
@@ -1188,6 +1179,8 @@ static int mount_serialize(Unit *u, FILE *f, FDSet *fds) {
if (m->control_command_id >= 0)
unit_serialize_item(u, f, "control-command", mount_exec_command_to_string(m->control_command_id));
+ exec_context_serialize(&m->exec_context, UNIT(m), f);
+
return 0;
}
@@ -1244,7 +1237,22 @@ static int mount_deserialize_item(Unit *u, const char *key, const char *value, F
m->control_command_id = id;
m->control_command = m->exec_command + id;
}
+ } else if (streq(key, "tmp-dir")) {
+ char *t;
+
+ t = strdup(value);
+ if (!t)
+ return log_oom();
+
+ m->exec_context.tmp_dir = t;
+ } else if (streq(key, "var-tmp-dir")) {
+ char *t;
+ t = strdup(value);
+ if (!t)
+ return log_oom();
+
+ m->exec_context.var_tmp_dir = t;
} else
log_debug_unit(UNIT(m)->id,
"Unknown serialization key '%s'", key);
@@ -1252,19 +1260,19 @@ static int mount_deserialize_item(Unit *u, const char *key, const char *value, F
return 0;
}
-static UnitActiveState mount_active_state(Unit *u) {
+_pure_ static UnitActiveState mount_active_state(Unit *u) {
assert(u);
return state_translation_table[MOUNT(u)->state];
}
-static const char *mount_sub_state_to_string(Unit *u) {
+_pure_ static const char *mount_sub_state_to_string(Unit *u) {
assert(u);
return mount_state_to_string(MOUNT(u)->state);
}
-static bool mount_check_gc(Unit *u) {
+_pure_ static bool mount_check_gc(Unit *u) {
Mount *m = MOUNT(u);
assert(m);
@@ -1520,6 +1528,20 @@ static int mount_add_one(
goto fail;
}
+ u->source_path = strdup("/proc/self/mountinfo");
+ if (!u->source_path) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ r = unit_add_dependency_by_name(u, UNIT_BEFORE, SPECIAL_LOCAL_FS_TARGET, NULL, true);
+ if (r < 0)
+ goto fail;
+
+ r = unit_add_dependency_by_name(u, UNIT_CONFLICTS, SPECIAL_UMOUNT_TARGET, NULL, true);
+ if (r < 0)
+ goto fail;
+
unit_add_to_load_queue(u);
} else {
delete = false;
@@ -1681,25 +1703,27 @@ static void mount_shutdown(Manager *m) {
static int mount_enumerate(Manager *m) {
int r;
- struct epoll_event ev;
assert(m);
if (!m->proc_self_mountinfo) {
- if (!(m->proc_self_mountinfo = fopen("/proc/self/mountinfo", "re")))
+ struct epoll_event ev = {
+ .events = EPOLLPRI,
+ .data.ptr = &m->mount_watch,
+ };
+
+ m->proc_self_mountinfo = fopen("/proc/self/mountinfo", "re");
+ if (!m->proc_self_mountinfo)
return -errno;
m->mount_watch.type = WATCH_MOUNT;
m->mount_watch.fd = fileno(m->proc_self_mountinfo);
- zero(ev);
- ev.events = EPOLLPRI;
- ev.data.ptr = &m->mount_watch;
-
if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->mount_watch.fd, &ev) < 0)
return -errno;
}
- if ((r = mount_load_proc_self_mountinfo(m, false)) < 0)
+ r = mount_load_proc_self_mountinfo(m, false);
+ if (r < 0)
goto fail;
return 0;
@@ -1800,53 +1824,7 @@ static void mount_reset_failed(Unit *u) {
}
static int mount_kill(Unit *u, KillWho who, int signo, DBusError *error) {
- Mount *m = MOUNT(u);
- int r = 0;
- Set *pid_set = NULL;
-
- assert(m);
-
- if (who == KILL_MAIN) {
- dbus_set_error(error, BUS_ERROR_NO_SUCH_PROCESS, "Mount units have no main processes");
- return -ESRCH;
- }
-
- if (m->control_pid <= 0 && who == KILL_CONTROL) {
- dbus_set_error(error, BUS_ERROR_NO_SUCH_PROCESS, "No control process to kill");
- return -ESRCH;
- }
-
- if (who == KILL_CONTROL || who == KILL_ALL)
- if (m->control_pid > 0)
- if (kill(m->control_pid, signo) < 0)
- r = -errno;
-
- if (who == KILL_ALL) {
- int q;
-
- pid_set = set_new(trivial_hash_func, trivial_compare_func);
- if (!pid_set)
- return -ENOMEM;
-
- /* Exclude the control pid from being killed via the cgroup */
- if (m->control_pid > 0) {
- q = set_put(pid_set, LONG_TO_PTR(m->control_pid));
- if (q < 0) {
- r = q;
- goto finish;
- }
- }
-
- q = cgroup_bonding_kill_list(UNIT(m)->cgroup_bondings, signo, false, false, pid_set, NULL);
- if (q < 0 && q != -EAGAIN && q != -ESRCH && q != -ENOENT)
- r = q;
- }
-
-finish:
- if (pid_set)
- set_free(pid_set);
-
- return r;
+ return unit_kill_common(u, who, signo, -1, MOUNT(u)->control_pid, error);
}
static const char* const mount_state_table[_MOUNT_STATE_MAX] = {
@@ -1888,13 +1866,15 @@ DEFINE_STRING_TABLE_LOOKUP(mount_result, MountResult);
const UnitVTable mount_vtable = {
.object_size = sizeof(Mount),
- .exec_context_offset = offsetof(Mount, exec_context),
.sections =
"Unit\0"
"Mount\0"
"Install\0",
+ .exec_context_offset = offsetof(Mount, exec_context),
+ .exec_section = "Mount",
+
.no_alias = true,
.no_instances = true,
diff --git a/src/core/mount.h b/src/core/mount.h
index 30c6d9b249..bcc10ee0d4 100644
--- a/src/core/mount.h
+++ b/src/core/mount.h
@@ -111,13 +111,13 @@ extern const UnitVTable mount_vtable;
void mount_fd_event(Manager *m, int events);
-const char* mount_state_to_string(MountState i);
-MountState mount_state_from_string(const char *s);
+const char* mount_state_to_string(MountState i) _const_;
+MountState mount_state_from_string(const char *s) _pure_;
-const char* mount_exec_command_to_string(MountExecCommand i);
-MountExecCommand mount_exec_command_from_string(const char *s);
+const char* mount_exec_command_to_string(MountExecCommand i) _const_;
+MountExecCommand mount_exec_command_from_string(const char *s) _pure_;
-const char* mount_result_to_string(MountResult i);
-MountResult mount_result_from_string(const char *s);
+const char* mount_result_to_string(MountResult i) _const_;
+MountResult mount_result_from_string(const char *s) _pure_;
void warn_if_dir_nonempty(const char *unit, const char* where);
diff --git a/src/core/namespace.c b/src/core/namespace.c
index ba18ddc5b0..7e33d84156 100644
--- a/src/core/namespace.c
+++ b/src/core/namespace.c
@@ -36,23 +36,24 @@
#include "path-util.h"
#include "namespace.h"
#include "missing.h"
+#include "execute.h"
-typedef enum PathMode {
+typedef enum MountMode {
/* This is ordered by priority! */
INACCESSIBLE,
READONLY,
PRIVATE_TMP,
PRIVATE_VAR_TMP,
READWRITE
-} PathMode;
+} MountMode;
-typedef struct Path {
+typedef struct BindMount {
const char *path;
- PathMode mode;
+ MountMode mode;
bool done;
-} Path;
+} BindMount;
-static int append_paths(Path **p, char **strv, PathMode mode) {
+static int append_mounts(BindMount **p, char **strv, MountMode mode) {
char **i;
STRV_FOREACH(i, strv) {
@@ -68,8 +69,8 @@ static int append_paths(Path **p, char **strv, PathMode mode) {
return 0;
}
-static int path_compare(const void *a, const void *b) {
- const Path *p = a, *q = b;
+static int mount_path_compare(const void *a, const void *b) {
+ const BindMount *p = a, *q = b;
if (path_equal(p->path, q->path)) {
@@ -93,14 +94,13 @@ static int path_compare(const void *a, const void *b) {
return 0;
}
-static void drop_duplicates(Path *p, unsigned *n, bool *need_inaccessible) {
- Path *f, *t, *previous;
+static void drop_duplicates(BindMount *m, unsigned *n) {
+ BindMount *f, *t, *previous;
- assert(p);
+ assert(m);
assert(n);
- assert(need_inaccessible);
- for (f = p, t = p, previous = NULL; f < p+*n; f++) {
+ for (f = m, t = m, previous = NULL; f < m+*n; f++) {
/* The first one wins */
if (previous && path_equal(f->path, previous->path))
@@ -109,37 +109,33 @@ static void drop_duplicates(Path *p, unsigned *n, bool *need_inaccessible) {
t->path = f->path;
t->mode = f->mode;
- if (t->mode == INACCESSIBLE)
- *need_inaccessible = true;
-
previous = t;
t++;
}
- *n = t - p;
+ *n = t - m;
}
static int apply_mount(
- Path *p,
+ BindMount *m,
const char *tmp_dir,
- const char *var_tmp_dir,
- const char *inaccessible_dir) {
+ const char *var_tmp_dir) {
const char *what;
int r;
- assert(p);
+ assert(m);
- switch (p->mode) {
+ switch (m->mode) {
case INACCESSIBLE:
- what = inaccessible_dir;
+ what = "/run/systemd/inaccessible";
break;
case READONLY:
case READWRITE:
- what = p->path;
+ what = m->path;
break;
case PRIVATE_TMP:
@@ -156,155 +152,115 @@ static int apply_mount(
assert(what);
- r = mount(what, p->path, NULL, MS_BIND|MS_REC, NULL);
+ r = mount(what, m->path, NULL, MS_BIND|MS_REC, NULL);
if (r >= 0)
- log_debug("Successfully mounted %s to %s", what, p->path);
+ log_debug("Successfully mounted %s to %s", what, m->path);
return r;
}
-static int make_read_only(Path *p) {
+static int make_read_only(BindMount *m) {
int r;
- assert(p);
+ assert(m);
- if (p->mode != INACCESSIBLE && p->mode != READONLY)
+ if (m->mode != INACCESSIBLE && m->mode != READONLY)
return 0;
- r = mount(NULL, p->path, NULL, MS_BIND|MS_REMOUNT|MS_RDONLY|MS_REC, NULL);
+ r = mount(NULL, m->path, NULL, MS_BIND|MS_REMOUNT|MS_RDONLY|MS_REC, NULL);
if (r < 0)
return -errno;
return 0;
}
-int setup_namespace(
- char **writable,
- char **readable,
- char **inaccessible,
- bool private_tmp,
- unsigned long flags) {
-
- char
- tmp_dir[] = "/tmp/systemd-private-XXXXXX",
- var_tmp_dir[] = "/var/tmp/systemd-private-XXXXXX",
- inaccessible_dir[] = "/tmp/systemd-inaccessible-XXXXXX";
-
- Path *paths, *p;
- unsigned n;
- bool need_inaccessible = false;
- bool remove_tmp = false, remove_var_tmp = false, remove_inaccessible = false;
- int r;
-
- if (!flags)
- flags = MS_SHARED;
-
- n =
- strv_length(writable) +
- strv_length(readable) +
- strv_length(inaccessible) +
- (private_tmp ? 2 : 0);
+int setup_tmpdirs(char **tmp_dir,
+ char **var_tmp_dir) {
+ int r = 0;
+ char tmp_dir_template[] = "/tmp/systemd-private-XXXXXX",
+ var_tmp_dir_template[] = "/var/tmp/systemd-private-XXXXXX";
- p = paths = alloca(sizeof(Path) * n);
- if ((r = append_paths(&p, writable, READWRITE)) < 0 ||
- (r = append_paths(&p, readable, READONLY)) < 0 ||
- (r = append_paths(&p, inaccessible, INACCESSIBLE)) < 0)
- goto fail;
-
- if (private_tmp) {
- p->path = "/tmp";
- p->mode = PRIVATE_TMP;
- p++;
-
- p->path = "/var/tmp";
- p->mode = PRIVATE_VAR_TMP;
- p++;
- }
+ assert(tmp_dir);
+ assert(var_tmp_dir);
- assert(paths + n == p);
+ r = create_tmp_dir(tmp_dir_template, tmp_dir);
+ if (r < 0)
+ return r;
- qsort(paths, n, sizeof(Path), path_compare);
- drop_duplicates(paths, &n, &need_inaccessible);
+ r = create_tmp_dir(var_tmp_dir_template, var_tmp_dir);
+ if (r == 0)
+ return 0;
- if (need_inaccessible) {
- mode_t u;
- char *d;
+ /* failure */
+ rmdir(*tmp_dir);
+ rmdir(tmp_dir_template);
+ free(*tmp_dir);
+ *tmp_dir = NULL;
- u = umask(0777);
- d = mkdtemp(inaccessible_dir);
- umask(u);
+ return r;
+}
- if (!d) {
- r = -errno;
- goto fail;
- }
+int setup_namespace(char** read_write_dirs,
+ char** read_only_dirs,
+ char** inaccessible_dirs,
+ char* tmp_dir,
+ char* var_tmp_dir,
+ bool private_tmp,
+ unsigned mount_flags) {
+
+ unsigned n = strv_length(read_write_dirs) +
+ strv_length(read_only_dirs) +
+ strv_length(inaccessible_dirs) +
+ (private_tmp ? 2 : 0);
+ BindMount *m, *mounts;
+ int r = 0;
+
+ if (!mount_flags)
+ mount_flags = MS_SHARED;
+
+ if (unshare(CLONE_NEWNS) < 0)
+ return -errno;
- remove_inaccessible = true;
- }
+ m = mounts = (BindMount *) alloca(n * sizeof(BindMount));
+ if ((r = append_mounts(&m, read_write_dirs, READWRITE)) < 0 ||
+ (r = append_mounts(&m, read_only_dirs, READONLY)) < 0 ||
+ (r = append_mounts(&m, inaccessible_dirs, INACCESSIBLE)) < 0)
+ return r;
if (private_tmp) {
- mode_t u;
- char *d;
-
- u = umask(0000);
- d = mkdtemp(tmp_dir);
- umask(u);
-
- if (!d) {
- r = -errno;
- goto fail;
- }
+ m->path = "/tmp";
+ m->mode = PRIVATE_TMP;
+ m++;
- remove_tmp = true;
-
- u = umask(0000);
- d = mkdtemp(var_tmp_dir);
- umask(u);
-
- if (!d) {
- r = -errno;
- goto fail;
- }
-
- remove_var_tmp = true;
-
- if (chmod(tmp_dir, 0777 + S_ISVTX) < 0) {
- r = -errno;
- goto fail;
- }
-
- if (chmod(var_tmp_dir, 0777 + S_ISVTX) < 0) {
- r = -errno;
- goto fail;
- }
+ m->path = "/var/tmp";
+ m->mode = PRIVATE_VAR_TMP;
+ m++;
}
- if (unshare(CLONE_NEWNS) < 0) {
- r = -errno;
- goto fail;
- }
+ assert(mounts + n == m);
+
+ qsort(mounts, n, sizeof(BindMount), mount_path_compare);
+ drop_duplicates(mounts, &n);
/* Remount / as SLAVE so that nothing now mounted in the namespace
shows up in the parent */
- if (mount(NULL, "/", NULL, MS_SLAVE|MS_REC, NULL) < 0) {
- r = -errno;
- goto fail;
- }
+ if (mount(NULL, "/", NULL, MS_SLAVE|MS_REC, NULL) < 0)
+ return -errno;
- for (p = paths; p < paths + n; p++) {
- r = apply_mount(p, tmp_dir, var_tmp_dir, inaccessible_dir);
+ for (m = mounts; m < mounts + n; ++m) {
+ r = apply_mount(m, tmp_dir, var_tmp_dir);
if (r < 0)
goto undo_mounts;
}
- for (p = paths; p < paths + n; p++) {
- r = make_read_only(p);
+ for (m = mounts; m < mounts + n; ++m) {
+ r = make_read_only(m);
if (r < 0)
goto undo_mounts;
}
/* Remount / as the desired mode */
- if (mount(NULL, "/", NULL, flags|MS_REC, NULL) < 0) {
+ if (mount(NULL, "/", NULL, mount_flags | MS_REC, NULL) < 0) {
r = -errno;
goto undo_mounts;
}
@@ -312,19 +268,10 @@ int setup_namespace(
return 0;
undo_mounts:
- for (p = paths; p < paths + n; p++)
- if (p->done)
- umount2(p->path, MNT_DETACH);
-
-fail:
- if (remove_inaccessible)
- rmdir(inaccessible_dir);
-
- if (remove_tmp)
- rmdir(tmp_dir);
-
- if (remove_var_tmp)
- rmdir(var_tmp_dir);
+ for (m = mounts; m < mounts + n; ++m) {
+ if (m->done)
+ umount2(m->path, MNT_DETACH);
+ }
return r;
}
diff --git a/src/core/namespace.h b/src/core/namespace.h
index 5d72ed91fb..ddb579468d 100644
--- a/src/core/namespace.h
+++ b/src/core/namespace.h
@@ -23,9 +23,11 @@
#include <stdbool.h>
-int setup_namespace(
- char **writable,
- char **readable,
- char **inaccessible,
- bool private_tmp,
- unsigned long flags);
+int setup_tmpdirs(char **tmp_dir, char **var_tmp_dir);
+int setup_namespace(char **read_write_dirs,
+ char **read_only_dirs,
+ char **inaccessible_dirs,
+ char *tmp_dir,
+ char *var_tmp_dir,
+ bool private_tmp,
+ unsigned mount_flags);
diff --git a/src/core/path.c b/src/core/path.c
index 767620ba75..8a09deb4ff 100644
--- a/src/core/path.c
+++ b/src/core/path.c
@@ -31,8 +31,9 @@
#include "mkdir.h"
#include "dbus-path.h"
#include "special.h"
-#include "bus-errors.h"
+#include "dbus-common.h"
#include "path-util.h"
+#include "macro.h"
static const UnitActiveState state_translation_table[_PATH_STATE_MAX] = {
[PATH_DEAD] = UNIT_INACTIVE,
@@ -52,7 +53,7 @@ int path_spec_watch(PathSpec *s, Unit *u) {
};
bool exists = false;
- char *k, *slash;
+ char *slash, *oldslash = NULL;
int r;
assert(u);
@@ -60,47 +61,85 @@ int path_spec_watch(PathSpec *s, Unit *u) {
path_spec_unwatch(s, u);
- if (!(k = strdup(s->path)))
- return -ENOMEM;
-
- if ((s->inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC)) < 0) {
+ s->inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
+ if (s->inotify_fd < 0) {
r = -errno;
goto fail;
}
- if (unit_watch_fd(u, s->inotify_fd, EPOLLIN, &s->watch) < 0) {
- r = -errno;
+ r = unit_watch_fd(u, s->inotify_fd, EPOLLIN, &s->watch);
+ if (r < 0)
goto fail;
- }
- s->primary_wd = inotify_add_watch(s->inotify_fd, k, flags_table[s->type]);
- if (s->primary_wd >= 0)
- exists = true;
+ /* This assumes the path was passed through path_kill_slashes()! */
- do {
+ for (slash = strchr(s->path, '/'); ; slash = strchr(slash+1, '/')) {
+ char *cut = NULL;
int flags;
+ char tmp;
+
+ if (slash) {
+ cut = slash + (slash == s->path);
+ tmp = *cut;
+ *cut = '\0';
+
+ flags = IN_MOVE_SELF | IN_DELETE_SELF | IN_ATTRIB | IN_CREATE | IN_MOVED_TO;
+ } else
+ flags = flags_table[s->type];
+
+ r = inotify_add_watch(s->inotify_fd, s->path, flags);
+ if (r < 0) {
+ if (errno == EACCES || errno == ENOENT) {
+ if (cut)
+ *cut = tmp;
+ break;
+ }
+
+ log_warning("Failed to add watch on %s: %m", s->path);
+ r = -errno;
+ if (cut)
+ *cut = tmp;
+ goto fail;
+ } else {
+ exists = true;
- /* This assumes the path was passed through path_kill_slashes()! */
- slash = strrchr(k, '/');
- if (!slash)
- break;
+ /* Path exists, we don't need to watch parent
+ too closely. */
+ if (oldslash) {
+ char *cut2 = oldslash + (oldslash == s->path);
+ char tmp2 = *cut2;
+ *cut2 = '\0';
+
+ inotify_add_watch(s->inotify_fd, s->path, IN_MOVE_SELF);
+ /* Error is ignored, the worst can happen is
+ we get spurious events. */
- /* Trim the path at the last slash. Keep the slash if it's the root dir. */
- slash[slash == k] = 0;
+ *cut2 = tmp2;
+ }
+ }
- flags = IN_MOVE_SELF;
- if (!exists)
- flags |= IN_DELETE_SELF | IN_ATTRIB | IN_CREATE | IN_MOVED_TO;
+ if (cut)
+ *cut = tmp;
- if (inotify_add_watch(s->inotify_fd, k, flags) >= 0)
- exists = true;
- } while (slash != k);
+ if (slash)
+ oldslash = slash;
+ else {
+ /* whole path has been iterated over */
+ s->primary_wd = r;
+ break;
+ }
+ }
+
+ if (!exists) {
+ log_error("Failed to add watch on any of the components of %s: %m",
+ s->path);
+ r = -errno; /* either EACCESS or ENOENT */
+ goto fail;
+ }
return 0;
fail:
- free(k);
-
path_spec_unwatch(s, u);
return r;
}
@@ -117,7 +156,7 @@ void path_spec_unwatch(PathSpec *s, Unit *u) {
}
int path_spec_fd_event(PathSpec *s, uint32_t events) {
- uint8_t *buf = NULL;
+ _cleanup_free_ uint8_t *buf = NULL;
struct inotify_event *e;
ssize_t k;
int l;
@@ -125,30 +164,24 @@ int path_spec_fd_event(PathSpec *s, uint32_t events) {
if (events != EPOLLIN) {
log_error("Got invalid poll event on inotify.");
- r = -EINVAL;
- goto out;
+ return -EINVAL;
}
if (ioctl(s->inotify_fd, FIONREAD, &l) < 0) {
log_error("FIONREAD failed: %m");
- r = -errno;
- goto out;
+ return -errno;
}
assert(l > 0);
buf = malloc(l);
- if (!buf) {
- log_error("Failed to allocate buffer: %m");
- r = -errno;
- goto out;
- }
+ if (!buf)
+ return log_oom();
k = read(s->inotify_fd, buf, l);
if (k < 0) {
log_error("Failed to read inotify event: %m");
- r = -errno;
- goto out;
+ return -errno;
}
e = (struct inotify_event*) buf;
@@ -166,8 +199,7 @@ int path_spec_fd_event(PathSpec *s, uint32_t events) {
e = (struct inotify_event*) ((uint8_t*) e + step);
k -= step;
}
-out:
- free(buf);
+
return r;
}
@@ -248,22 +280,27 @@ static void path_init(Unit *u) {
p->directory_mode = 0755;
}
-static void path_done(Unit *u) {
- Path *p = PATH(u);
+void path_free_specs(Path *p) {
PathSpec *s;
assert(p);
- unit_ref_unset(&p->unit);
-
while ((s = p->specs)) {
- path_spec_unwatch(s, u);
+ path_spec_unwatch(s, UNIT(p));
LIST_REMOVE(PathSpec, spec, p->specs, s);
path_spec_done(s);
free(s);
}
}
+static void path_done(Unit *u) {
+ Path *p = PATH(u);
+
+ assert(p);
+
+ path_free_specs(p);
+}
+
int path_add_one_mount_link(Path *p, Mount *m) {
PathSpec *s;
int r;
@@ -276,11 +313,12 @@ int path_add_one_mount_link(Path *p, Mount *m) {
return 0;
LIST_FOREACH(spec, s, p->specs) {
-
if (!path_spec_startswith(s, m->where))
continue;
- if ((r = unit_add_two_dependencies(UNIT(p), UNIT_AFTER, UNIT_REQUIRES, UNIT(m), true)) < 0)
+ r = unit_add_two_dependencies(UNIT(p), UNIT_AFTER, UNIT_REQUIRES,
+ UNIT(m), true);
+ if (r < 0)
return r;
}
@@ -293,9 +331,11 @@ static int path_add_mount_links(Path *p) {
assert(p);
- LIST_FOREACH(units_by_type, other, UNIT(p)->manager->units_by_type[UNIT_MOUNT])
- if ((r = path_add_one_mount_link(p, MOUNT(other))) < 0)
+ LIST_FOREACH(units_by_type, other, UNIT(p)->manager->units_by_type[UNIT_MOUNT]) {
+ r = path_add_one_mount_link(p, MOUNT(other));
+ if (r < 0)
return r;
+ }
return 0;
}
@@ -320,15 +360,20 @@ static int path_add_default_dependencies(Path *p) {
assert(p);
- if (UNIT(p)->manager->running_as == SYSTEMD_SYSTEM) {
- if ((r = unit_add_dependency_by_name(UNIT(p), UNIT_BEFORE, SPECIAL_BASIC_TARGET, NULL, true)) < 0)
- return r;
+ r = unit_add_dependency_by_name(UNIT(p), UNIT_BEFORE,
+ SPECIAL_PATHS_TARGET, NULL, true);
+ if (r < 0)
+ return r;
- if ((r = unit_add_two_dependencies_by_name(UNIT(p), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, NULL, true)) < 0)
+ if (UNIT(p)->manager->running_as == SYSTEMD_SYSTEM) {
+ r = unit_add_two_dependencies_by_name(UNIT(p), UNIT_AFTER, UNIT_REQUIRES,
+ SPECIAL_SYSINIT_TARGET, NULL, true);
+ if (r < 0)
return r;
}
- return unit_add_two_dependencies_by_name(UNIT(p), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, NULL, true);
+ return unit_add_two_dependencies_by_name(UNIT(p), UNIT_BEFORE, UNIT_CONFLICTS,
+ SPECIAL_SHUTDOWN_TARGET, NULL, true);
}
static int path_load(Unit *u) {
@@ -338,31 +383,33 @@ static int path_load(Unit *u) {
assert(u);
assert(u->load_state == UNIT_STUB);
- if ((r = unit_load_fragment_and_dropin(u)) < 0)
+ r = unit_load_fragment_and_dropin(u);
+ if (r < 0)
return r;
if (u->load_state == UNIT_LOADED) {
- if (!UNIT_DEREF(p->unit)) {
+ if (set_isempty(u->dependencies[UNIT_TRIGGERS])) {
Unit *x;
r = unit_load_related_unit(u, ".service", &x);
if (r < 0)
return r;
- unit_ref_set(&p->unit, x);
+ r = unit_add_two_dependencies(u, UNIT_BEFORE, UNIT_TRIGGERS, x, true);
+ if (r < 0)
+ return r;
}
- r = unit_add_two_dependencies(u, UNIT_BEFORE, UNIT_TRIGGERS, UNIT_DEREF(p->unit), true);
+ r = path_add_mount_links(p);
if (r < 0)
return r;
- if ((r = path_add_mount_links(p)) < 0)
- return r;
-
- if (UNIT(p)->default_dependencies)
- if ((r = path_add_default_dependencies(p)) < 0)
+ if (UNIT(p)->default_dependencies) {
+ r = path_add_default_dependencies(p);
+ if (r < 0)
return r;
+ }
}
return path_verify(p);
@@ -370,11 +417,14 @@ static int path_load(Unit *u) {
static void path_dump(Unit *u, FILE *f, const char *prefix) {
Path *p = PATH(u);
+ Unit *trigger;
PathSpec *s;
assert(p);
assert(f);
+ trigger = UNIT_TRIGGER(u);
+
fprintf(f,
"%sPath State: %s\n"
"%sResult: %s\n"
@@ -383,7 +433,7 @@ static void path_dump(Unit *u, FILE *f, const char *prefix) {
"%sDirectoryMode: %04o\n",
prefix, path_state_to_string(p->state),
prefix, path_result_to_string(p->result),
- prefix, UNIT_DEREF(p->unit)->id,
+ prefix, trigger ? trigger->id : "n/a",
prefix, yes_no(p->make_directory),
prefix, p->directory_mode);
@@ -406,9 +456,11 @@ static int path_watch(Path *p) {
assert(p);
- LIST_FOREACH(spec, s, p->specs)
- if ((r = path_spec_watch(s, UNIT(p))) < 0)
+ LIST_FOREACH(spec, s, p->specs) {
+ r = path_spec_watch(s, UNIT(p));
+ if (r < 0)
return r;
+ }
return 0;
}
@@ -463,32 +515,35 @@ static void path_enter_dead(Path *p, PathResult f) {
}
static void path_enter_running(Path *p) {
+ _cleanup_dbus_error_free_ DBusError error;
int r;
- DBusError error;
assert(p);
+
dbus_error_init(&error);
/* Don't start job if we are supposed to go down */
- if (UNIT(p)->job && UNIT(p)->job->type == JOB_STOP)
+ if (unit_stop_pending(UNIT(p)))
return;
- if ((r = manager_add_job(UNIT(p)->manager, JOB_START, UNIT_DEREF(p->unit), JOB_REPLACE, true, &error, NULL)) < 0)
+ r = manager_add_job(UNIT(p)->manager, JOB_START, UNIT_TRIGGER(UNIT(p)),
+ JOB_REPLACE, true, &error, NULL);
+ if (r < 0)
goto fail;
p->inotify_triggered = false;
- if ((r = path_watch(p)) < 0)
+ r = path_watch(p);
+ if (r < 0)
goto fail;
path_set_state(p, PATH_RUNNING);
return;
fail:
- log_warning("%s failed to queue unit startup job: %s", UNIT(p)->id, bus_error(&error, r));
+ log_warning("%s failed to queue unit startup job: %s",
+ UNIT(p)->id, bus_error(&error, r));
path_enter_dead(p, PATH_FAILURE_RESOURCES);
-
- dbus_error_free(&error);
}
static bool path_check_good(Path *p, bool initial) {
@@ -517,7 +572,8 @@ static void path_enter_waiting(Path *p, bool initial, bool recheck) {
return;
}
- if ((r = path_watch(p)) < 0)
+ r = path_watch(p);
+ if (r < 0)
goto fail;
/* Hmm, so now we have created inotify watches, but the file
@@ -535,7 +591,8 @@ static void path_enter_waiting(Path *p, bool initial, bool recheck) {
return;
fail:
- log_warning("%s failed to enter waiting state: %s", UNIT(p)->id, strerror(-r));
+ log_warning("%s failed to enter waiting state: %s",
+ UNIT(p)->id, strerror(-r));
path_enter_dead(p, PATH_FAILURE_RESOURCES);
}
@@ -557,7 +614,7 @@ static int path_start(Unit *u) {
assert(p);
assert(p->state == PATH_DEAD || p->state == PATH_FAILED);
- if (UNIT_DEREF(p->unit)->load_state != UNIT_LOADED)
+ if (UNIT_TRIGGER(u)->load_state != UNIT_LOADED)
return -ENOENT;
path_mkdir(p);
@@ -602,7 +659,8 @@ static int path_deserialize_item(Unit *u, const char *key, const char *value, FD
if (streq(key, "state")) {
PathState state;
- if ((state = path_state_from_string(value)) < 0)
+ state = path_state_from_string(value);
+ if (state < 0)
log_debug("Failed to parse state value %s", value);
else
p->deserialized_state = state;
@@ -622,13 +680,13 @@ static int path_deserialize_item(Unit *u, const char *key, const char *value, FD
return 0;
}
-static UnitActiveState path_active_state(Unit *u) {
+_pure_ static UnitActiveState path_active_state(Unit *u) {
assert(u);
return state_translation_table[PATH(u)->state];
}
-static const char *path_sub_state_to_string(Unit *u) {
+_pure_ static const char *path_sub_state_to_string(Unit *u) {
assert(u);
return path_state_to_string(PATH(u)->state);
@@ -677,32 +735,28 @@ fail:
path_enter_dead(p, PATH_FAILURE_RESOURCES);
}
-void path_unit_notify(Unit *u, UnitActiveState new_state) {
- Iterator i;
- Unit *k;
-
- if (u->type == UNIT_PATH)
- return;
-
- SET_FOREACH(k, u->dependencies[UNIT_TRIGGERED_BY], i) {
- Path *p;
+static void path_trigger_notify(Unit *u, Unit *other) {
+ Path *p = PATH(u);
- if (k->type != UNIT_PATH)
- continue;
+ assert(u);
+ assert(other);
- if (k->load_state != UNIT_LOADED)
- continue;
+ /* Invoked whenever the unit we trigger changes state or gains
+ * or loses a job */
- p = PATH(k);
+ if (other->load_state != UNIT_LOADED)
+ return;
- if (p->state == PATH_RUNNING && new_state == UNIT_INACTIVE) {
- log_debug("%s got notified about unit deactivation.", UNIT(p)->id);
+ if (p->state == PATH_RUNNING &&
+ UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(other))) {
+ log_debug_unit(UNIT(p)->id,
+ "%s got notified about unit deactivation.",
+ UNIT(p)->id);
- /* Hmm, so inotify was triggered since the
- * last activation, so I guess we need to
- * recheck what is going on. */
- path_enter_waiting(p, false, p->inotify_triggered);
- }
+ /* Hmm, so inotify was triggered since the
+ * last activation, so I guess we need to
+ * recheck what is going on. */
+ path_enter_waiting(p, false, p->inotify_triggered);
}
}
@@ -769,6 +823,8 @@ const UnitVTable path_vtable = {
.fd_event = path_fd_event,
+ .trigger_notify = path_trigger_notify,
+
.reset_failed = path_reset_failed,
.bus_interface = "org.freedesktop.systemd1.Path",
diff --git a/src/core/path.h b/src/core/path.h
index 77926888ae..6adab5897d 100644
--- a/src/core/path.h
+++ b/src/core/path.h
@@ -80,8 +80,6 @@ struct Path {
LIST_HEAD(PathSpec, specs);
- UnitRef unit;
-
PathState state, deserialized_state;
bool inotify_triggered;
@@ -92,19 +90,19 @@ struct Path {
PathResult result;
};
-void path_unit_notify(Unit *u, UnitActiveState new_state);
-
/* Called from the mount code figure out if a mount is a dependency of
* any of the paths of this path object */
int path_add_one_mount_link(Path *p, Mount *m);
+void path_free_specs(Path *p);
+
extern const UnitVTable path_vtable;
-const char* path_state_to_string(PathState i);
-PathState path_state_from_string(const char *s);
+const char* path_state_to_string(PathState i) _const_;
+PathState path_state_from_string(const char *s) _pure_;
-const char* path_type_to_string(PathType i);
-PathType path_type_from_string(const char *s);
+const char* path_type_to_string(PathType i) _const_;
+PathType path_type_from_string(const char *s) _pure_;
-const char* path_result_to_string(PathResult i);
-PathResult path_result_from_string(const char *s);
+const char* path_result_to_string(PathResult i) _const_;
+PathResult path_result_from_string(const char *s) _pure_;
diff --git a/src/core/selinux-access.c b/src/core/selinux-access.c
index 6dfe8b45f3..426aed07d2 100644
--- a/src/core/selinux-access.c
+++ b/src/core/selinux-access.c
@@ -104,8 +104,6 @@ static int bus_get_selinux_security_context(
*scon = b;
- log_debug("GetConnectionSELinuxSecurityContext %s (pid %ld)", *scon, (long) bus_get_unix_process_id(connection, name, error));
-
return 0;
}
@@ -134,7 +132,7 @@ static int bus_get_audit_data(
if (r < 0)
return r;
- r = get_process_cmdline(pid, LINE_MAX, true, &audit->cmdline);
+ r = get_process_cmdline(pid, 0, true, &audit->cmdline);
if (r < 0)
return r;
@@ -176,20 +174,25 @@ static int audit_callback(
user_avc's into the /var/log/audit/audit.log, otherwise they will be
sent to syslog.
*/
-static int log_callback(int type, const char *fmt, ...) {
+_printf_attr_(2, 3) static int log_callback(int type, const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
#ifdef HAVE_AUDIT
if (get_audit_fd() >= 0) {
- char buf[LINE_MAX];
+ _cleanup_free_ char *buf = NULL;
+ int r;
- vsnprintf(buf, sizeof(buf), fmt, ap);
- audit_log_user_avc_message(get_audit_fd(), AUDIT_USER_AVC, buf, NULL, NULL, NULL, 0);
+ r = vasprintf(&buf, fmt, ap);
va_end(ap);
- return 0;
+ if (r >= 0) {
+ audit_log_user_avc_message(get_audit_fd(), AUDIT_USER_AVC, buf, NULL, NULL, NULL, 0);
+ return 0;
+ }
+
+ va_start(ap, fmt);
}
#endif
log_metav(LOG_USER | LOG_INFO, __FILE__, __LINE__, __FUNCTION__, fmt, ap);
@@ -258,7 +261,7 @@ static int get_audit_data(
const char *sender;
int r, fd;
struct ucred ucred;
- socklen_t len;
+ socklen_t len = sizeof(ucred);
sender = dbus_message_get_sender(message);
if (sender)
@@ -280,7 +283,7 @@ static int get_audit_data(
if (r < 0)
return r;
- r = get_process_cmdline(ucred.pid, LINE_MAX, true, &audit->cmdline);
+ r = get_process_cmdline(ucred.pid, 0, true, &audit->cmdline);
if (r < 0)
return r;
@@ -308,8 +311,6 @@ static int get_calling_context(
*/
sender = dbus_message_get_sender(message);
if (sender) {
- log_error("SELinux Got Sender %s", sender);
-
r = bus_get_selinux_security_context(connection, sender, scon, error);
if (r >= 0)
return r;
@@ -318,7 +319,6 @@ static int get_calling_context(
return r;
}
- log_debug("SELinux No Sender");
if (!dbus_connection_get_unix_fd(connection, &fd)) {
log_error("bus_connection_get_unix_fd failed %m");
return -EINVAL;
@@ -363,8 +363,6 @@ int selinux_access_check(
if (r < 0)
return r;
- log_debug("SELinux access check for path=%s permission=%s", strna(path), permission);
-
audit.uid = audit.loginuid = (uid_t) -1;
audit.gid = (gid_t) -1;
audit.cmdline = NULL;
diff --git a/src/core/selinux-setup.c b/src/core/selinux-setup.c
index e9c0de92f1..7a32ed59a0 100644
--- a/src/core/selinux-setup.c
+++ b/src/core/selinux-setup.c
@@ -58,9 +58,6 @@ int selinux_setup(bool *loaded_policy) {
cb.func_log = null_log;
selinux_set_callback(SELINUX_CB_LOG, cb);
- /* Make sure getcon() works, which needs /proc and /sys */
- mount_setup_early();
-
/* Already initialized by somebody else? */
r = getcon_raw(&con);
if (r == 0) {
@@ -104,7 +101,7 @@ int selinux_setup(bool *loaded_policy) {
after_load = now(CLOCK_MONOTONIC);
log_info("Successfully loaded SELinux policy in %s.",
- format_timespan(timespan, sizeof(timespan), after_load - before_load));
+ format_timespan(timespan, sizeof(timespan), after_load - before_load, 0));
*loaded_policy = true;
diff --git a/src/core/service.c b/src/core/service.c
index 2a4e691e78..3617c24711 100644
--- a/src/core/service.c
+++ b/src/core/service.c
@@ -36,12 +36,14 @@
#include "unit-printf.h"
#include "dbus-service.h"
#include "special.h"
-#include "bus-errors.h"
+#include "dbus-common.h"
#include "exit-status.h"
#include "def.h"
#include "path-util.h"
#include "util.h"
#include "utf8.h"
+#include "env-util.h"
+#include "fileio.h"
#ifdef HAVE_SYSV_COMPAT
@@ -281,7 +283,7 @@ static void service_done(Unit *u) {
free(s->status_text);
s->status_text = NULL;
- exec_context_done(&s->exec_context);
+ exec_context_done(&s->exec_context, manager_is_reloading_or_reexecuting(u->manager));
exec_command_free_array(s->exec_command, _SERVICE_EXEC_COMMAND_MAX);
s->control_command = NULL;
s->main_command = NULL;
@@ -322,17 +324,15 @@ static void service_done(Unit *u) {
static char *sysv_translate_name(const char *name) {
char *r;
- if (!(r = new(char, strlen(name) + sizeof(".service"))))
+ r = new(char, strlen(name) + sizeof(".service"));
+ if (!r)
return NULL;
if (endswith(name, ".sh"))
- /* Drop Debian-style .sh suffix */
+ /* Drop .sh suffix */
strcpy(stpcpy(r, name) - 3, ".service");
- if (startswith(name, "rc."))
- /* Drop Frugalware-style rc. prefix */
- strcpy(stpcpy(r, name + 3), ".service");
else
- /* Normal init scripts */
+ /* Normal init script name */
strcpy(stpcpy(r, name), ".service");
return r;
@@ -348,24 +348,13 @@ static int sysv_translate_facility(const char *name, const char *filename, char
static const char * const table[] = {
/* LSB defined facilities */
- "local_fs", SPECIAL_LOCAL_FS_TARGET,
- /* Due to unfortunate name selection in Mandriva,
- * $network is provided by network-up which is ordered
- * after network which actually starts interfaces.
- * To break the loop, just ignore it */
+ "local_fs", NULL,
"network", SPECIAL_NETWORK_TARGET,
"named", SPECIAL_NSS_LOOKUP_TARGET,
"portmap", SPECIAL_RPCBIND_TARGET,
"remote_fs", SPECIAL_REMOTE_FS_TARGET,
- "syslog", SPECIAL_SYSLOG_TARGET,
+ "syslog", NULL,
"time", SPECIAL_TIME_SYNC_TARGET,
-
- /* common extensions */
- "mail-transfer-agent", SPECIAL_MAIL_TRANSFER_AGENT_TARGET,
- "x-display-manager", SPECIAL_DISPLAY_MANAGER_SERVICE,
- "null", NULL,
- "mail-transport-agent", SPECIAL_MAIL_TRANSFER_AGENT_TARGET,
- "smtp", SPECIAL_MAIL_TRANSFER_AGENT_TARGET,
};
unsigned i;
@@ -385,8 +374,9 @@ static int sysv_translate_facility(const char *name, const char *filename, char
if (!table[i+1])
return 0;
- if (!(r = strdup(table[i+1])))
- return -ENOMEM;
+ r = strdup(table[i+1]);
+ if (!r)
+ return log_oom();
goto finish;
}
@@ -772,7 +762,7 @@ static int service_load_sysv_path(Service *s, const char *path) {
continue;
if (unit_name_to_type(m) == UNIT_SERVICE)
- r = unit_add_name(u, m);
+ r = unit_merge_by_name(u, m);
else
/* NB: SysV targets
* which are provided
@@ -815,7 +805,6 @@ static int service_load_sysv_path(Service *s, const char *path) {
}
r = sysv_translate_facility(n, path_get_file_name(path), &m);
-
if (r < 0) {
log_error_unit(u->id,
"[%s:%u] Failed to translate LSB dependency %s, ignoring: %s",
@@ -992,10 +981,8 @@ static int service_load_sysv_name(Service *s, const char *name) {
assert(s);
assert(name);
- /* For SysV services we strip the rc.* and *.sh
- * prefixes/suffixes. */
- if (startswith(name, "rc.") ||
- endswith(name, ".sh.service"))
+ /* For SysV services we strip the *.sh suffixes. */
+ if (endswith(name, ".sh.service"))
return -ENOENT;
STRV_FOREACH(p, UNIT(s)->manager->lookup_paths.sysvinit_path) {
@@ -1012,25 +999,12 @@ static int service_load_sysv_name(Service *s, const char *name) {
r = service_load_sysv_path(s, path);
if (r >= 0 && UNIT(s)->load_state == UNIT_STUB) {
- /* Try Debian style *.sh source'able init scripts */
+ /* Try *.sh source'able init scripts */
strcat(path, ".sh");
r = service_load_sysv_path(s, path);
}
free(path);
- if (r >= 0 && UNIT(s)->load_state == UNIT_STUB) {
- /* Try Frugalware style rc.* init scripts */
-
- path = strjoin(*p, "/rc.", name, NULL);
- if (!path)
- return -ENOMEM;
-
- /* Drop .service suffix */
- path[strlen(path)-8] = 0;
- r = service_load_sysv_path(s, path);
- free(path);
- }
-
if (r < 0)
return r;
@@ -1108,7 +1082,8 @@ static int fsck_fix_order(Service *s) {
else
continue;
- if ((r = unit_add_dependency(UNIT(s), d, UNIT(t), true)) < 0)
+ r = unit_add_dependency(UNIT(s), d, UNIT(t), true);
+ if (r < 0)
return r;
}
@@ -1163,18 +1138,32 @@ static int service_add_default_dependencies(Service *s) {
/* First, pull in base system */
if (UNIT(s)->manager->running_as == SYSTEMD_SYSTEM) {
-
- if ((r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_BASIC_TARGET, NULL, true)) < 0)
+ r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_AFTER, UNIT_REQUIRES,
+ SPECIAL_BASIC_TARGET, NULL, true);
+ if (r < 0)
return r;
} else if (UNIT(s)->manager->running_as == SYSTEMD_USER) {
+ r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_AFTER, UNIT_REQUIRES,
+ SPECIAL_SOCKETS_TARGET, NULL, true);
+ if (r < 0)
+ return r;
- if ((r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SOCKETS_TARGET, NULL, true)) < 0)
+ r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_AFTER, UNIT_REQUIRES,
+ SPECIAL_TIMERS_TARGET, NULL, true);
+ if (r < 0)
+ return r;
+
+ r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_AFTER, UNIT_REQUIRES,
+ SPECIAL_PATHS_TARGET, NULL, true);
+ if (r < 0)
return r;
}
/* Second, activate normal shutdown */
- return unit_add_two_dependencies_by_name(UNIT(s), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, NULL, true);
+ r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_BEFORE, UNIT_CONFLICTS,
+ SPECIAL_SHUTDOWN_TARGET, NULL, true);
+ return r;
}
static void service_fix_output(Service *s) {
@@ -1232,18 +1221,22 @@ static int service_load(Unit *u) {
service_fix_output(s);
- if ((r = unit_add_exec_dependencies(u, &s->exec_context)) < 0)
+ r = unit_add_exec_dependencies(u, &s->exec_context);
+ if (r < 0)
return r;
- if ((r = unit_add_default_cgroups(u)) < 0)
+ r = unit_add_default_cgroups(u);
+ if (r < 0)
return r;
#ifdef HAVE_SYSV_COMPAT
- if ((r = sysv_fix_order(s)) < 0)
+ r = sysv_fix_order(s);
+ if (r < 0)
return r;
#endif
- if ((r = fsck_fix_order(s)) < 0)
+ r = fsck_fix_order(s);
+ if (r < 0)
return r;
if (s->bus_name)
@@ -1256,13 +1249,18 @@ static int service_load(Unit *u) {
if (s->watchdog_usec > 0 && s->notify_access == NOTIFY_NONE)
s->notify_access = NOTIFY_MAIN;
- if (s->type == SERVICE_DBUS || s->bus_name)
- if ((r = unit_add_two_dependencies_by_name(u, UNIT_AFTER, UNIT_REQUIRES, SPECIAL_DBUS_SOCKET, NULL, true)) < 0)
+ if (s->type == SERVICE_DBUS || s->bus_name) {
+ r = unit_add_two_dependencies_by_name(u, UNIT_AFTER, UNIT_REQUIRES,
+ SPECIAL_DBUS_SOCKET, NULL, true);
+ if (r < 0)
return r;
+ }
- if (UNIT(s)->default_dependencies)
- if ((r = service_add_default_dependencies(s)) < 0)
+ if (UNIT(s)->default_dependencies) {
+ r = service_add_default_dependencies(s);
+ if (r < 0)
return r;
+ }
r = unit_exec_context_defaults(u, &s->exec_context);
if (r < 0)
@@ -1277,7 +1275,7 @@ static void service_dump(Unit *u, FILE *f, const char *prefix) {
ServiceExecCommand c;
Service *s = SERVICE(u);
const char *prefix2;
- char *p2;
+ _cleanup_free_ char *p2 = NULL;
assert(s);
@@ -1372,12 +1370,10 @@ static void service_dump(Unit *u, FILE *f, const char *prefix) {
if (s->status_text)
fprintf(f, "%sStatus Text: %s\n",
prefix, s->status_text);
-
- free(p2);
}
static int service_load_pid_file(Service *s, bool may_warn) {
- char *k;
+ _cleanup_free_ char *k = NULL;
int r;
pid_t pid;
@@ -1386,7 +1382,8 @@ static int service_load_pid_file(Service *s, bool may_warn) {
if (!s->pid_file)
return -ENOENT;
- if ((r = read_one_line_file(s->pid_file, &k)) < 0) {
+ r = read_one_line_file(s->pid_file, &k);
+ if (r < 0) {
if (may_warn)
log_info_unit(UNIT(s)->id,
"PID file %s not readable (yet?) after %s.",
@@ -1395,10 +1392,13 @@ static int service_load_pid_file(Service *s, bool may_warn) {
}
r = parse_pid(k, &pid);
- free(k);
-
- if (r < 0)
+ if (r < 0) {
+ if (may_warn)
+ log_info_unit(UNIT(s)->id,
+ "Failed to read PID from file %s: %s",
+ s->pid_file, strerror(-r));
return r;
+ }
if (kill(pid, 0) < 0 && errno != EPERM) {
if (may_warn)
@@ -1421,12 +1421,18 @@ static int service_load_pid_file(Service *s, bool may_warn) {
log_debug_unit(UNIT(s)->id,
"Main PID loaded: %lu", (unsigned long) pid);
- if ((r = service_set_main_pid(s, pid)) < 0)
+ r = service_set_main_pid(s, pid);
+ if (r < 0)
return r;
- if ((r = unit_watch_pid(UNIT(s), pid)) < 0)
+ r = unit_watch_pid(UNIT(s), pid);
+ if (r < 0) {
/* FIXME: we need to do something here */
+ log_warning_unit(UNIT(s)->id,
+ "Failed to watch PID %lu from service %s",
+ (unsigned long) pid, UNIT(s)->id);
return r;
+ }
return 0;
}
@@ -1447,16 +1453,22 @@ static int service_search_main_pid(Service *s) {
assert(s->main_pid <= 0);
- if ((pid = cgroup_bonding_search_main_pid_list(UNIT(s)->cgroup_bondings)) <= 0)
+ pid = cgroup_bonding_search_main_pid_list(UNIT(s)->cgroup_bondings);
+ if (pid <= 0)
return -ENOENT;
log_debug_unit(UNIT(s)->id,
"Main PID guessed: %lu", (unsigned long) pid);
- if ((r = service_set_main_pid(s, pid)) < 0)
+ r = service_set_main_pid(s, pid);
+ if (r < 0)
return r;
- if ((r = unit_watch_pid(UNIT(s), pid)) < 0)
+ r = unit_watch_pid(UNIT(s), pid);
+ if (r < 0)
/* FIXME: we need to do something here */
+ log_warning_unit(UNIT(s)->id,
+ "Failed to watch PID %lu from service %s",
+ (unsigned long) pid, UNIT(s)->id);
return r;
return 0;
@@ -1531,6 +1543,9 @@ static void service_set_state(Service *s, ServiceState state) {
s->control_command_id = _SERVICE_EXEC_COMMAND_INVALID;
}
+ if (state == SERVICE_FAILED)
+ service_notify_sockets_dead(s, s->result == SERVICE_FAILURE_START_LIMIT);
+
if (state == SERVICE_DEAD ||
state == SERVICE_STOP ||
state == SERVICE_STOP_SIGTERM ||
@@ -1538,7 +1553,6 @@ static void service_set_state(Service *s, ServiceState state) {
state == SERVICE_STOP_POST ||
state == SERVICE_FINAL_SIGTERM ||
state == SERVICE_FINAL_SIGKILL ||
- state == SERVICE_FAILED ||
state == SERVICE_AUTO_RESTART)
service_notify_sockets_dead(s, false);
@@ -1558,7 +1572,7 @@ static void service_set_state(Service *s, ServiceState state) {
service_connection_unref(s);
}
- if (state == SERVICE_STOP)
+ if (state == SERVICE_STOP || state == SERVICE_STOP_SIGTERM)
service_stop_watchdog(s);
/* For the inactive states unit_notify() will trim the cgroup,
@@ -1618,9 +1632,11 @@ static int service_coldplug(Unit *u) {
s->deserialized_state == SERVICE_STOP ||
s->deserialized_state == SERVICE_STOP_SIGTERM ||
s->deserialized_state == SERVICE_STOP_SIGKILL)
- if (s->main_pid > 0)
- if ((r = unit_watch_pid(UNIT(s), s->main_pid)) < 0)
+ if (s->main_pid > 0) {
+ r = unit_watch_pid(UNIT(s), s->main_pid);
+ if (r < 0)
return r;
+ }
if (s->deserialized_state == SERVICE_START_PRE ||
s->deserialized_state == SERVICE_START ||
@@ -1632,9 +1648,11 @@ static int service_coldplug(Unit *u) {
s->deserialized_state == SERVICE_STOP_POST ||
s->deserialized_state == SERVICE_FINAL_SIGTERM ||
s->deserialized_state == SERVICE_FINAL_SIGKILL)
- if (s->control_pid > 0)
- if ((r = unit_watch_pid(UNIT(s), s->control_pid)) < 0)
+ if (s->control_pid > 0) {
+ r = unit_watch_pid(UNIT(s), s->control_pid);
+ if (r < 0)
return r;
+ }
if (s->deserialized_state == SERVICE_START_POST ||
s->deserialized_state == SERVICE_RUNNING)
@@ -1669,7 +1687,8 @@ static int service_collect_fds(Service *s, int **fds, unsigned *n_fds) {
sock = SOCKET(u);
- if ((r = socket_collect_fds(sock, &cfds, &cn_fds)) < 0)
+ r = socket_collect_fds(sock, &cfds, &cn_fds);
+ if (r < 0)
goto fail;
if (!cfds)
@@ -1681,7 +1700,8 @@ static int service_collect_fds(Service *s, int **fds, unsigned *n_fds) {
} else {
int *t;
- if (!(t = new(int, rn_fds+cn_fds))) {
+ t = new(int, rn_fds+cn_fds);
+ if (!t) {
free(cfds);
r = -ENOMEM;
goto fail;
@@ -1722,9 +1742,11 @@ static int service_spawn(
pid_t pid;
int r;
- int *fds = NULL, *fdsbuf = NULL;
+ int *fds = NULL;
+ _cleanup_free_ int *fdsbuf = NULL;
unsigned n_fds = 0, n_env = 0;
- char **argv = NULL, **final_env = NULL, **our_env = NULL;
+ _cleanup_strv_free_ char
+ **argv = NULL, **final_env = NULL, **our_env = NULL;
assert(s);
assert(c);
@@ -1739,7 +1761,8 @@ static int service_spawn(
fds = &s->socket_fd;
n_fds = 1;
} else {
- if ((r = service_collect_fds(s, &fdsbuf, &n_fds)) < 0)
+ r = service_collect_fds(s, &fdsbuf, &n_fds);
+ if (r < 0)
goto fail;
fds = fdsbuf;
@@ -1747,13 +1770,15 @@ static int service_spawn(
}
if (timeout && s->timeout_start_usec) {
- r = unit_watch_timer(UNIT(s), CLOCK_MONOTONIC, true, s->timeout_start_usec, &s->timer_watch);
+ r = unit_watch_timer(UNIT(s), CLOCK_MONOTONIC, true,
+ s->timeout_start_usec, &s->timer_watch);
if (r < 0)
goto fail;
} else
unit_unwatch_timer(UNIT(s), &s->timer_watch);
- if (!(argv = unit_full_printf_strv(UNIT(s), c->argv))) {
+ argv = unit_full_printf_strv(UNIT(s), c->argv);
+ if (!argv) {
r = -ENOMEM;
goto fail;
}
@@ -1809,29 +1834,19 @@ static int service_spawn(
UNIT(s)->id,
s->type == SERVICE_IDLE ? UNIT(s)->manager->idle_pipe : NULL,
&pid);
-
if (r < 0)
goto fail;
- if ((r = unit_watch_pid(UNIT(s), pid)) < 0)
+ r = unit_watch_pid(UNIT(s), pid);
+ if (r < 0)
/* FIXME: we need to do something here */
goto fail;
- free(fdsbuf);
- strv_free(argv);
- strv_free(our_env);
- strv_free(final_env);
-
*_pid = pid;
return 0;
fail:
- free(fdsbuf);
- strv_free(argv);
- strv_free(our_env);
- strv_free(final_env);
-
if (timeout)
unit_unwatch_timer(UNIT(s), &s->timer_watch);
@@ -1863,7 +1878,7 @@ static int main_pid_good(Service *s) {
return -EAGAIN;
}
-static int control_pid_good(Service *s) {
+_pure_ static int control_pid_good(Service *s) {
assert(s);
return s->control_pid > 0;
@@ -1874,7 +1889,8 @@ static int cgroup_good(Service *s) {
assert(s);
- if ((r = cgroup_bonding_is_empty_list(UNIT(s)->cgroup_bondings)) < 0)
+ r = cgroup_bonding_is_empty_list(UNIT(s)->cgroup_bondings);
+ if (r < 0)
return r;
return !r;
@@ -1911,6 +1927,9 @@ static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart)
s->forbid_restart = false;
+ /* we want fresh tmpdirs in case service is started again immediately */
+ exec_context_tmp_dirs_done(&s->exec_context);
+
return;
fail:
@@ -1931,7 +1950,8 @@ static void service_enter_stop_post(Service *s, ServiceResult f) {
service_unwatch_control_pid(s);
- if ((s->control_command = s->exec_command[SERVICE_EXEC_STOP_POST])) {
+ s->control_command = s->exec_command[SERVICE_EXEC_STOP_POST];
+ if (s->control_command) {
s->control_command_id = SERVICE_EXEC_STOP_POST;
r = service_spawn(s,
@@ -1963,66 +1983,26 @@ fail:
static void service_enter_signal(Service *s, ServiceState state, ServiceResult f) {
int r;
- Set *pid_set = NULL;
- bool wait_for_exit = false;
assert(s);
if (f != SERVICE_SUCCESS)
s->result = f;
- if (s->kill_context.kill_mode != KILL_NONE) {
- int sig = (state == SERVICE_STOP_SIGTERM || state == SERVICE_FINAL_SIGTERM) ? s->kill_context.kill_signal : SIGKILL;
-
- if (s->main_pid > 0) {
- if (kill_and_sigcont(s->main_pid, sig) < 0 && errno != ESRCH)
- log_warning_unit(UNIT(s)->id,
- "Failed to kill main process %li: %m", (long) s->main_pid);
- else
- wait_for_exit = !s->main_pid_alien;
- }
-
- if (s->control_pid > 0) {
- if (kill_and_sigcont(s->control_pid, sig) < 0 && errno != ESRCH)
- log_warning_unit(UNIT(s)->id,
- "Failed to kill control process %li: %m", (long) s->control_pid);
- else
- wait_for_exit = true;
- }
-
- if (s->kill_context.kill_mode == KILL_CONTROL_GROUP) {
-
- pid_set = set_new(trivial_hash_func, trivial_compare_func);
- if (!pid_set) {
- r = -ENOMEM;
- goto fail;
- }
-
- /* Exclude the main/control pids from being killed via the cgroup */
- if (s->main_pid > 0)
- if ((r = set_put(pid_set, LONG_TO_PTR(s->main_pid))) < 0)
- goto fail;
-
- if (s->control_pid > 0)
- if ((r = set_put(pid_set, LONG_TO_PTR(s->control_pid))) < 0)
- goto fail;
-
- r = cgroup_bonding_kill_list(UNIT(s)->cgroup_bondings, sig, true, false, pid_set, NULL);
- if (r < 0) {
- if (r != -EAGAIN && r != -ESRCH && r != -ENOENT)
- log_warning_unit(UNIT(s)->id,
- "Failed to kill control group: %s", strerror(-r));
- } else if (r > 0)
- wait_for_exit = true;
-
- set_free(pid_set);
- pid_set = NULL;
- }
- }
+ r = unit_kill_context(
+ UNIT(s),
+ &s->kill_context,
+ state != SERVICE_STOP_SIGTERM && state != SERVICE_FINAL_SIGTERM,
+ s->main_pid,
+ s->control_pid,
+ s->main_pid_alien);
+ if (r < 0)
+ goto fail;
- if (wait_for_exit) {
+ if (r > 0) {
if (s->timeout_stop_usec > 0) {
- r = unit_watch_timer(UNIT(s), CLOCK_MONOTONIC, true, s->timeout_stop_usec, &s->timer_watch);
+ r = unit_watch_timer(UNIT(s), CLOCK_MONOTONIC, true,
+ s->timeout_stop_usec, &s->timer_watch);
if (r < 0)
goto fail;
}
@@ -2043,9 +2023,6 @@ fail:
service_enter_stop_post(s, SERVICE_FAILURE_RESOURCES);
else
service_enter_dead(s, SERVICE_FAILURE_RESOURCES, true);
-
- if (pid_set)
- set_free(pid_set);
}
static void service_enter_stop(Service *s, ServiceResult f) {
@@ -2058,7 +2035,8 @@ static void service_enter_stop(Service *s, ServiceResult f) {
service_unwatch_control_pid(s);
- if ((s->control_command = s->exec_command[SERVICE_EXEC_STOP])) {
+ s->control_command = s->exec_command[SERVICE_EXEC_STOP];
+ if (s->control_command) {
s->control_command_id = SERVICE_EXEC_STOP;
r = service_spawn(s,
@@ -2114,7 +2092,8 @@ static void service_enter_start_post(Service *s) {
if (s->watchdog_usec > 0)
service_reset_watchdog(s);
- if ((s->control_command = s->exec_command[SERVICE_EXEC_START_POST])) {
+ s->control_command = s->exec_command[SERVICE_EXEC_START_POST];
+ if (s->control_command) {
s->control_command_id = SERVICE_EXEC_START_POST;
r = service_spawn(s,
@@ -2176,7 +2155,8 @@ static void service_enter_start(Service *s) {
r = service_spawn(s,
c,
- s->type == SERVICE_FORKING || s->type == SERVICE_DBUS || s->type == SERVICE_NOTIFY || s->type == SERVICE_ONESHOT,
+ s->type == SERVICE_FORKING || s->type == SERVICE_DBUS ||
+ s->type == SERVICE_NOTIFY || s->type == SERVICE_ONESHOT,
true,
true,
true,
@@ -2233,11 +2213,13 @@ static void service_enter_start_pre(Service *s) {
service_unwatch_control_pid(s);
- if ((s->control_command = s->exec_command[SERVICE_EXEC_START_PRE])) {
+ s->control_command = s->exec_command[SERVICE_EXEC_START_PRE];
+ if (s->control_command) {
/* Before we start anything, let's clear up what might
* be left from previous runs. */
- cgroup_bonding_kill_list(UNIT(s)->cgroup_bondings, SIGKILL, true, true, NULL, "control");
+ cgroup_bonding_kill_list(UNIT(s)->cgroup_bondings, SIGKILL,
+ true,true, NULL, "control");
s->control_command_id = SERVICE_EXEC_START_PRE;
@@ -2317,7 +2299,8 @@ static void service_enter_reload(Service *s) {
service_unwatch_control_pid(s);
- if ((s->control_command = s->exec_command[SERVICE_EXEC_RELOAD])) {
+ s->control_command = s->exec_command[SERVICE_EXEC_RELOAD];
+ if (s->control_command) {
s->control_command_id = SERVICE_EXEC_RELOAD;
r = service_spawn(s,
@@ -2451,7 +2434,9 @@ static int service_start_limit_test(Service *s) {
log_warning_unit(UNIT(s)->id,
"%s start request repeated too quickly, rebooting.", UNIT(s)->id);
- r = manager_add_job_by_name(UNIT(s)->manager, JOB_START, SPECIAL_REBOOT_TARGET, JOB_REPLACE, true, &error, NULL);
+ r = manager_add_job_by_name(UNIT(s)->manager, JOB_START,
+ SPECIAL_REBOOT_TARGET, JOB_REPLACE,
+ true, &error, NULL);
if (r < 0) {
log_error_unit(UNIT(s)->id,
"Failed to reboot: %s.", bus_error(&error, r));
@@ -2585,7 +2570,7 @@ static int service_reload(Unit *u) {
return 0;
}
-static bool service_can_reload(Unit *u) {
+_pure_ static bool service_can_reload(Unit *u) {
Service *s = SERVICE(u);
assert(s);
@@ -2605,7 +2590,8 @@ static int service_serialize(Unit *u, FILE *f, FDSet *fds) {
unit_serialize_item(u, f, "reload-result", service_result_to_string(s->reload_result));
if (s->control_pid > 0)
- unit_serialize_item_format(u, f, "control-pid", "%lu", (unsigned long) s->control_pid);
+ unit_serialize_item_format(u, f, "control-pid", "%lu",
+ (unsigned long) s->control_pid);
if (s->main_pid_known && s->main_pid > 0)
unit_serialize_item_format(u, f, "main-pid", "%lu", (unsigned long) s->main_pid);
@@ -2619,7 +2605,8 @@ static int service_serialize(Unit *u, FILE *f, FDSet *fds) {
* multiple commands attached here, we will start from the
* first one again */
if (s->control_command_id >= 0)
- unit_serialize_item(u, f, "control-command", service_exec_command_to_string(s->control_command_id));
+ unit_serialize_item(u, f, "control-command",
+ service_exec_command_to_string(s->control_command_id));
if (s->socket_fd >= 0) {
int copy;
@@ -2631,17 +2618,29 @@ static int service_serialize(Unit *u, FILE *f, FDSet *fds) {
}
if (s->main_exec_status.pid > 0) {
- unit_serialize_item_format(u, f, "main-exec-status-pid", "%lu", (unsigned long) s->main_exec_status.pid);
- dual_timestamp_serialize(f, "main-exec-status-start", &s->main_exec_status.start_timestamp);
- dual_timestamp_serialize(f, "main-exec-status-exit", &s->main_exec_status.exit_timestamp);
+ unit_serialize_item_format(u, f, "main-exec-status-pid", "%lu",
+ (unsigned long) s->main_exec_status.pid);
+ dual_timestamp_serialize(f, "main-exec-status-start",
+ &s->main_exec_status.start_timestamp);
+ dual_timestamp_serialize(f, "main-exec-status-exit",
+ &s->main_exec_status.exit_timestamp);
if (dual_timestamp_is_set(&s->main_exec_status.exit_timestamp)) {
- unit_serialize_item_format(u, f, "main-exec-status-code", "%i", s->main_exec_status.code);
- unit_serialize_item_format(u, f, "main-exec-status-status", "%i", s->main_exec_status.status);
+ unit_serialize_item_format(u, f, "main-exec-status-code", "%i",
+ s->main_exec_status.code);
+ unit_serialize_item_format(u, f, "main-exec-status-status", "%i",
+ s->main_exec_status.status);
}
}
if (dual_timestamp_is_set(&s->watchdog_timestamp))
- dual_timestamp_serialize(f, "watchdog-timestamp", &s->watchdog_timestamp);
+ dual_timestamp_serialize(f, "watchdog-timestamp",
+ &s->watchdog_timestamp);
+
+ if (s->exec_context.tmp_dir)
+ unit_serialize_item(u, f, "tmp-dir", s->exec_context.tmp_dir);
+
+ if (s->exec_context.var_tmp_dir)
+ unit_serialize_item(u, f, "var-tmp-dir", s->exec_context.var_tmp_dir);
return 0;
}
@@ -2657,7 +2656,8 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value,
if (streq(key, "state")) {
ServiceState state;
- if ((state = service_state_from_string(value)) < 0)
+ state = service_state_from_string(value);
+ if (state < 0)
log_debug_unit(u->id, "Failed to parse state value %s", value);
else
s->deserialized_state = state;
@@ -2696,14 +2696,18 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value,
} else if (streq(key, "main-pid-known")) {
int b;
- if ((b = parse_boolean(value)) < 0)
+ b = parse_boolean(value);
+ if (b < 0)
log_debug_unit(u->id, "Failed to parse main-pid-known value %s", value);
else
s->main_pid_known = b;
} else if (streq(key, "status-text")) {
char *t;
- if ((t = strdup(value))) {
+ t = strdup(value);
+ if (!t)
+ log_oom();
+ else {
free(s->status_text);
s->status_text = t;
}
@@ -2711,7 +2715,8 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value,
} else if (streq(key, "control-command")) {
ServiceExecCommand id;
- if ((id = service_exec_command_from_string(value)) < 0)
+ id = service_exec_command_from_string(value);
+ if (id < 0)
log_debug_unit(u->id, "Failed to parse exec-command value %s", value);
else {
s->control_command_id = id;
@@ -2755,13 +2760,29 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value,
dual_timestamp_deserialize(value, &s->main_exec_status.exit_timestamp);
else if (streq(key, "watchdog-timestamp"))
dual_timestamp_deserialize(value, &s->watchdog_timestamp);
- else
+ else if (streq(key, "tmp-dir")) {
+ char *t;
+
+ t = strdup(value);
+ if (!t)
+ return log_oom();
+
+ s->exec_context.tmp_dir = t;
+ } else if (streq(key, "var-tmp-dir")) {
+ char *t;
+
+ t = strdup(value);
+ if (!t)
+ return log_oom();
+
+ s->exec_context.var_tmp_dir = t;
+ } else
log_debug_unit(u->id, "Unknown serialization key '%s'", key);
return 0;
}
-static UnitActiveState service_active_state(Unit *u) {
+_pure_ static UnitActiveState service_active_state(Unit *u) {
const UnitActiveState *table;
assert(u);
@@ -2797,7 +2818,7 @@ static bool service_check_gc(Unit *u) {
return false;
}
-static bool service_check_snapshot(Unit *u) {
+_pure_ static bool service_check_snapshot(Unit *u) {
Service *s = SERVICE(u);
assert(s);
@@ -2832,6 +2853,9 @@ static int service_watch_pid_file(Service *s) {
goto fail;
/* the pidfile might have appeared just before we set the watch */
+ log_debug_unit(UNIT(s)->id,
+ "Trying to read %s's PID file %s in case it changed",
+ UNIT(s)->id, s->pid_file_pathspec->path);
service_retry_pid_file(s);
return 0;
@@ -2926,24 +2950,34 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
s->main_pid = 0;
exec_status_exit(&s->main_exec_status, &s->exec_context, pid, code, status);
- /* If this is not a forking service than the main
- * process got started and hence we copy the exit
- * status so that it is recorded both as main and as
- * control process exit status */
if (s->main_command) {
+ /* If this is not a forking service than the
+ * main process got started and hence we copy
+ * the exit status so that it is recorded both
+ * as main and as control process exit
+ * status */
+
s->main_command->exec_status = s->main_exec_status;
if (s->main_command->ignore)
f = SERVICE_SUCCESS;
+ } else if (s->exec_command[SERVICE_EXEC_START]) {
+
+ /* If this is a forked process, then we should
+ * ignore the return value if this was
+ * configured for the starter process */
+
+ if (s->exec_command[SERVICE_EXEC_START]->ignore)
+ f = SERVICE_SUCCESS;
}
- log_struct(f == SERVICE_SUCCESS ? LOG_DEBUG : LOG_NOTICE,
+ log_struct_unit(f == SERVICE_SUCCESS ? LOG_DEBUG : LOG_NOTICE,
+ u->id,
"MESSAGE=%s: main process exited, code=%s, status=%i/%s",
u->id, sigchld_code_to_string(code), status,
strna(code == CLD_EXITED
? exit_status_to_string(status, EXIT_STATUS_FULL)
: signal_to_string(status)),
- "UNIT=%s", u->id,
"EXIT_CODE=%s", sigchld_code_to_string(code),
"EXIT_STATUS=%i", status,
NULL);
@@ -3013,7 +3047,8 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
s->control_pid = 0;
if (s->control_command) {
- exec_status_exit(&s->control_command->exec_status, &s->exec_context, pid, code, status);
+ exec_status_exit(&s->control_command->exec_status,
+ &s->exec_context, pid, code, status);
if (s->control_command->ignore)
f = SERVICE_SUCCESS;
@@ -3029,7 +3064,8 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
/* Immediately get rid of the cgroup, so that the
* kernel doesn't delay the cgroup empty messages for
* the service cgroup any longer than necessary */
- cgroup_bonding_kill_list(UNIT(s)->cgroup_bondings, SIGKILL, true, true, NULL, "control");
+ cgroup_bonding_kill_list(UNIT(s)->cgroup_bondings, SIGKILL,
+ true, true, NULL, "control");
if (s->control_command &&
s->control_command->command_next &&
@@ -3398,7 +3434,8 @@ static void service_notify_message(Unit *u, pid_t pid, char **tags) {
if (strv_find(tags, "WATCHDOG=1")) {
log_debug_unit(u->id,
"%s: got WATCHDOG=1", u->id);
- service_reset_watchdog(s);
+ if (dual_timestamp_is_set(&s->watchdog_timestamp))
+ service_reset_watchdog(s);
}
/* Notify clients about changed status or main pid */
@@ -3410,9 +3447,10 @@ static void service_notify_message(Unit *u, pid_t pid, char **tags) {
static int service_enumerate(Manager *m) {
char **p;
unsigned i;
- DIR *d = NULL;
- char *path = NULL, *fpath = NULL, *name = NULL;
- Set *runlevel_services[ELEMENTSOF(rcnd_table)], *shutdown_services = NULL;
+ _cleanup_closedir_ DIR *d = NULL;
+ _cleanup_free_ char *path = NULL, *fpath = NULL, *name = NULL;
+ Set *runlevel_services[ELEMENTSOF(rcnd_table)] = {};
+ _cleanup_set_free_ Set *shutdown_services = NULL;
Unit *service;
Iterator j;
int r;
@@ -3422,8 +3460,6 @@ static int service_enumerate(Manager *m) {
if (m->running_as != SYSTEMD_SYSTEM)
return 0;
- zero(runlevel_services);
-
STRV_FOREACH(p, m->lookup_paths.sysvrcnd_path)
for (i = 0; i < ELEMENTSOF(rcnd_table); i ++) {
struct dirent *de;
@@ -3438,9 +3474,10 @@ static int service_enumerate(Manager *m) {
if (d)
closedir(d);
- if (!(d = opendir(path))) {
+ d = opendir(path);
+ if (!d) {
if (errno != ENOENT)
- log_warning("opendir() failed on %s: %s", path, strerror(errno));
+ log_warning("opendir(%s) failed: %s", path, strerror(errno));
continue;
}
@@ -3479,8 +3516,9 @@ static int service_enumerate(Manager *m) {
}
free(name);
- if (!(name = sysv_translate_name(de->d_name + 3))) {
- r = -ENOMEM;
+ name = sysv_translate_name(de->d_name + 3);
+ if (!name) {
+ r = log_oom();
goto finish;
}
@@ -3499,19 +3537,25 @@ static int service_enumerate(Manager *m) {
SERVICE(service)->sysv_enabled = true;
}
- if ((r = set_ensure_allocated(&runlevel_services[i], trivial_hash_func, trivial_compare_func)) < 0)
+ r = set_ensure_allocated(&runlevel_services[i],
+ trivial_hash_func, trivial_compare_func);
+ if (r < 0)
goto finish;
- if ((r = set_put(runlevel_services[i], service)) < 0)
+ r = set_put(runlevel_services[i], service);
+ if (r < 0)
goto finish;
} else if (de->d_name[0] == 'K' &&
(rcnd_table[i].type == RUNLEVEL_DOWN)) {
- if ((r = set_ensure_allocated(&shutdown_services, trivial_hash_func, trivial_compare_func)) < 0)
+ r = set_ensure_allocated(&shutdown_services,
+ trivial_hash_func, trivial_compare_func);
+ if (r < 0)
goto finish;
- if ((r = set_put(shutdown_services, service)) < 0)
+ r = set_put(shutdown_services, service);
+ if (r < 0)
goto finish;
}
}
@@ -3532,7 +3576,10 @@ static int service_enumerate(Manager *m) {
if (service->fragment_path)
continue;
- if ((r = unit_add_two_dependencies_by_name_inverse(service, UNIT_AFTER, UNIT_WANTS, rcnd_table[i].target, NULL, true)) < 0)
+ r = unit_add_two_dependencies_by_name_inverse(
+ service, UNIT_AFTER, UNIT_WANTS,
+ rcnd_table[i].target, NULL, true);
+ if (r < 0)
goto finish;
}
@@ -3547,23 +3594,19 @@ static int service_enumerate(Manager *m) {
if (service->fragment_path)
continue;
- if ((r = unit_add_two_dependencies_by_name(service, UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, NULL, true)) < 0)
+ r = unit_add_two_dependencies_by_name(
+ service, UNIT_BEFORE, UNIT_CONFLICTS,
+ SPECIAL_SHUTDOWN_TARGET, NULL, true);
+ if (r < 0)
goto finish;
}
r = 0;
finish:
- free(path);
- free(fpath);
- free(name);
for (i = 0; i < ELEMENTSOF(rcnd_table); i++)
set_free(runlevel_services[i]);
- set_free(shutdown_services);
-
- if (d)
- closedir(d);
return r;
}
@@ -3686,65 +3729,7 @@ static void service_reset_failed(Unit *u) {
static int service_kill(Unit *u, KillWho who, int signo, DBusError *error) {
Service *s = SERVICE(u);
- int r = 0;
- Set *pid_set = NULL;
-
- assert(s);
-
- if (s->main_pid <= 0 && who == KILL_MAIN) {
- dbus_set_error(error, BUS_ERROR_NO_SUCH_PROCESS, "No main process to kill");
- return -ESRCH;
- }
-
- if (s->control_pid <= 0 && who == KILL_CONTROL) {
- dbus_set_error(error, BUS_ERROR_NO_SUCH_PROCESS, "No control process to kill");
- return -ESRCH;
- }
-
- if (who == KILL_CONTROL || who == KILL_ALL)
- if (s->control_pid > 0)
- if (kill(s->control_pid, signo) < 0)
- r = -errno;
-
- if (who == KILL_MAIN || who == KILL_ALL)
- if (s->main_pid > 0)
- if (kill(s->main_pid, signo) < 0)
- r = -errno;
-
- if (who == KILL_ALL) {
- int q;
-
- pid_set = set_new(trivial_hash_func, trivial_compare_func);
- if (!pid_set)
- return -ENOMEM;
-
- /* Exclude the control/main pid from being killed via the cgroup */
- if (s->control_pid > 0) {
- q = set_put(pid_set, LONG_TO_PTR(s->control_pid));
- if (q < 0) {
- r = q;
- goto finish;
- }
- }
-
- if (s->main_pid > 0) {
- q = set_put(pid_set, LONG_TO_PTR(s->main_pid));
- if (q < 0) {
- r = q;
- goto finish;
- }
- }
-
- q = cgroup_bonding_kill_list(UNIT(s)->cgroup_bondings, signo, false, false, pid_set, NULL);
- if (q < 0 && q != -EAGAIN && q != -ESRCH && q != -ENOENT)
- r = q;
- }
-
-finish:
- if (pid_set)
- set_free(pid_set);
-
- return r;
+ return unit_kill_common(u, who, signo, s->main_pid, s->control_pid, error);
}
static const char* const service_state_table[_SERVICE_STATE_MAX] = {
@@ -3830,13 +3815,15 @@ DEFINE_STRING_TABLE_LOOKUP(start_limit_action, StartLimitAction);
const UnitVTable service_vtable = {
.object_size = sizeof(Service),
- .exec_context_offset = offsetof(Service, exec_context),
.sections =
"Unit\0"
"Service\0"
"Install\0",
+ .exec_context_offset = offsetof(Service, exec_context),
+ .exec_section = "Service",
+
.init = service_init,
.done = service_done,
.load = service_load,
diff --git a/src/core/service.h b/src/core/service.h
index d1e53bf727..703d3faa45 100644
--- a/src/core/service.h
+++ b/src/core/service.h
@@ -203,23 +203,23 @@ struct Socket;
int service_set_socket_fd(Service *s, int fd, struct Socket *socket);
-const char* service_state_to_string(ServiceState i);
-ServiceState service_state_from_string(const char *s);
+const char* service_state_to_string(ServiceState i) _const_;
+ServiceState service_state_from_string(const char *s) _pure_;
-const char* service_restart_to_string(ServiceRestart i);
-ServiceRestart service_restart_from_string(const char *s);
+const char* service_restart_to_string(ServiceRestart i) _const_;
+ServiceRestart service_restart_from_string(const char *s) _pure_;
-const char* service_type_to_string(ServiceType i);
-ServiceType service_type_from_string(const char *s);
+const char* service_type_to_string(ServiceType i) _const_;
+ServiceType service_type_from_string(const char *s) _pure_;
-const char* service_exec_command_to_string(ServiceExecCommand i);
-ServiceExecCommand service_exec_command_from_string(const char *s);
+const char* service_exec_command_to_string(ServiceExecCommand i) _const_;
+ServiceExecCommand service_exec_command_from_string(const char *s) _pure_;
-const char* notify_access_to_string(NotifyAccess i);
-NotifyAccess notify_access_from_string(const char *s);
+const char* notify_access_to_string(NotifyAccess i) _const_;
+NotifyAccess notify_access_from_string(const char *s) _pure_;
-const char* service_result_to_string(ServiceResult i);
-ServiceResult service_result_from_string(const char *s);
+const char* service_result_to_string(ServiceResult i) _const_;
+ServiceResult service_result_from_string(const char *s) _pure_;
-const char* start_limit_action_to_string(StartLimitAction i);
-StartLimitAction start_limit_action_from_string(const char *s);
+const char* start_limit_action_to_string(StartLimitAction i) _const_;
+StartLimitAction start_limit_action_from_string(const char *s) _pure_;
diff --git a/src/core/shutdown.c b/src/core/shutdown.c
index 0b0e0c3d47..2db761de36 100644
--- a/src/core/shutdown.c
+++ b/src/core/shutdown.c
@@ -261,6 +261,8 @@ int main(int argc, char *argv[]) {
if (retries >= FINALIZE_ATTEMPTS)
log_error("Too many iterations, giving up.");
+ else
+ log_info("Storage is finalized.");
arguments[0] = NULL;
arguments[1] = argv[1];
@@ -272,6 +274,9 @@ int main(int argc, char *argv[]) {
if (prepare_new_root() >= 0 &&
pivot_to_new_root() >= 0) {
+
+ log_info("Returning to initrd...");
+
execv("/shutdown", argv);
log_error("Failed to execute shutdown binary: %m");
}
diff --git a/src/core/smack-setup.c b/src/core/smack-setup.c
new file mode 100644
index 0000000000..73eeb04190
--- /dev/null
+++ b/src/core/smack-setup.c
@@ -0,0 +1,151 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright (C) 2013 Intel Corporation
+ Authors:
+ Nathaniel Chen <nathaniel.chen@intel.com>
+
+ 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 <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/vfs.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <sys/mount.h>
+#include <stdint.h>
+
+#include "macro.h"
+#include "smack-setup.h"
+#include "util.h"
+#include "log.h"
+#include "label.h"
+
+#define SMACK_CONFIG "/etc/smack/accesses.d/"
+#define CIPSO_CONFIG "/etc/smack/cipso/"
+
+static int write_rules(const char* dstpath, const char* srcdir) {
+ _cleanup_fclose_ FILE *dst = NULL;
+ _cleanup_closedir_ DIR *dir = NULL;
+ struct dirent *entry;
+ char buf[NAME_MAX];
+ int dfd = -1;
+ int r = 0;
+
+ dst = fopen(dstpath, "we");
+ if (!dst) {
+ if (errno != ENOENT)
+ log_warning("Failed to open %s: %m", dstpath);
+ return -errno; /* negative error */
+ }
+
+ /* write rules to dst from every file in the directory */
+ dir = opendir(srcdir);
+ if (!dir) {
+ if (errno != ENOENT)
+ log_warning("Failed to opendir %s: %m", srcdir);
+ return errno; /* positive on purpose */
+ }
+
+ dfd = dirfd(dir);
+ assert(dfd >= 0);
+
+ FOREACH_DIRENT(entry, dir, return 0) {
+ int fd;
+ _cleanup_fclose_ FILE *policy = NULL;
+
+ fd = openat(dfd, entry->d_name, O_RDONLY|O_CLOEXEC);
+ if (fd < 0) {
+ if (r == 0)
+ r = -errno;
+ log_warning("Failed to open %s: %m", entry->d_name);
+ continue;
+ }
+
+ policy = fdopen(fd, "re");
+ if (!policy) {
+ if (r == 0)
+ r = -errno;
+ close_nointr_nofail(fd);
+ log_error("Failed to open %s: %m", entry->d_name);
+ continue;
+ }
+
+ /* load2 write rules in the kernel require a line buffered stream */
+ FOREACH_LINE(buf, policy,
+ log_error("Failed to read line from %s: %m",
+ entry->d_name)) {
+ if (!fputs(buf, dst)) {
+ if (r == 0)
+ r = -EINVAL;
+ log_error("Failed to write line to %s", dstpath);
+ break;
+ }
+ if (fflush(dst)) {
+ if (r == 0)
+ r = -errno;
+ log_error("Failed to flush writes to %s: %m", dstpath);
+ break;
+ }
+ }
+ }
+
+ return r;
+}
+
+
+int smack_setup(void) {
+ int r;
+
+ r = write_rules("/sys/fs/smackfs/load2", SMACK_CONFIG);
+ switch(r) {
+ case -ENOENT:
+ log_debug("Smack is not enabled in the kernel.");
+ return 0;
+ case ENOENT:
+ log_debug("Smack access rules directory " SMACK_CONFIG " not found");
+ return 0;
+ case 0:
+ log_info("Successfully loaded Smack policies.");
+ break;
+ default:
+ log_warning("Failed to load Smack access rules: %s, ignoring.",
+ strerror(abs(r)));
+ return 0;
+ }
+
+ r = write_rules("/sys/fs/smackfs/cipso2", CIPSO_CONFIG);
+ switch(r) {
+ case -ENOENT:
+ log_debug("Smack/CIPSO is not enabled in the kernel.");
+ return 0;
+ case ENOENT:
+ log_debug("Smack/CIPSO access rules directory " CIPSO_CONFIG " not found");
+ return 0;
+ case 0:
+ log_info("Successfully loaded Smack/CIPSO policies.");
+ return 0;
+ default:
+ log_warning("Failed to load Smack/CIPSO access rules: %s, ignoring.",
+ strerror(abs(r)));
+ return 0;
+ }
+}
diff --git a/src/core/smack-setup.h b/src/core/smack-setup.h
new file mode 100644
index 0000000000..ffe91843c3
--- /dev/null
+++ b/src/core/smack-setup.h
@@ -0,0 +1,26 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright (C) 2013 Intel Corporation
+ Authors:
+ Nathaniel Chen <nathaniel.chen@intel.com>
+
+ 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 <http://www.gnu.org/licenses/>.
+***/
+
+int smack_setup(void);
diff --git a/src/core/snapshot.c b/src/core/snapshot.c
index 5c2a319cb6..a63eccd8de 100644
--- a/src/core/snapshot.c
+++ b/src/core/snapshot.c
@@ -173,13 +173,13 @@ static int snapshot_deserialize_item(Unit *u, const char *key, const char *value
return 0;
}
-static UnitActiveState snapshot_active_state(Unit *u) {
+_pure_ static UnitActiveState snapshot_active_state(Unit *u) {
assert(u);
return state_translation_table[SNAPSHOT(u)->state];
}
-static const char *snapshot_sub_state_to_string(Unit *u) {
+_pure_ static const char *snapshot_sub_state_to_string(Unit *u) {
assert(u);
return snapshot_state_to_string(SNAPSHOT(u)->state);
@@ -256,6 +256,7 @@ int snapshot_create(Manager *m, const char *name, bool cleanup, DBusError *e, Sn
}
SNAPSHOT(u)->cleanup = cleanup;
+ u->allow_isolate = true;
*_s = SNAPSHOT(u);
return 0;
diff --git a/src/core/snapshot.h b/src/core/snapshot.h
index 9662d93164..56f87cff4d 100644
--- a/src/core/snapshot.h
+++ b/src/core/snapshot.h
@@ -46,5 +46,5 @@ extern const UnitVTable snapshot_vtable;
int snapshot_create(Manager *m, const char *name, bool cleanup, DBusError *e, Snapshot **s);
void snapshot_remove(Snapshot *s);
-const char* snapshot_state_to_string(SnapshotState i);
-SnapshotState snapshot_state_from_string(const char *s);
+const char* snapshot_state_to_string(SnapshotState i) _const_;
+SnapshotState snapshot_state_from_string(const char *s) _pure_;
diff --git a/src/core/socket.c b/src/core/socket.c
index fcbcdbe192..1b08f0a5fd 100644
--- a/src/core/socket.c
+++ b/src/core/socket.c
@@ -28,7 +28,7 @@
#include <signal.h>
#include <arpa/inet.h>
#include <mqueue.h>
-#ifdef HAVE_ATTR_XATTR_H
+#ifdef HAVE_XATTR
#include <attr/xattr.h>
#endif
@@ -46,7 +46,7 @@
#include "dbus-socket.h"
#include "missing.h"
#include "special.h"
-#include "bus-errors.h"
+#include "dbus-common.h"
#include "label.h"
#include "exit-status.h"
#include "def.h"
@@ -102,8 +102,7 @@ static void socket_unwatch_control_pid(Socket *s) {
s->control_pid = 0;
}
-static void socket_done(Unit *u) {
- Socket *s = SOCKET(u);
+void socket_free_ports(Socket *s) {
SocketPort *p;
assert(s);
@@ -119,8 +118,16 @@ static void socket_done(Unit *u) {
free(p->path);
free(p);
}
+}
+
+static void socket_done(Unit *u) {
+ Socket *s = SOCKET(u);
+
+ assert(s);
+
+ socket_free_ports(s);
- exec_context_done(&s->exec_context);
+ exec_context_done(&s->exec_context, manager_is_reloading_or_reexecuting(u->manager));
exec_command_free_array(s->exec_command, _SOCKET_EXEC_COMMAND_MAX);
s->control_command = NULL;
@@ -308,7 +315,7 @@ static int socket_add_device_link(Socket *s) {
assert(s);
- if (!s->bind_to_device)
+ if (!s->bind_to_device || streq(s->bind_to_device, "lo"))
return 0;
if (asprintf(&t, "/sys/subsystem/net/devices/%s", s->bind_to_device) < 0)
@@ -324,18 +331,20 @@ static int socket_add_default_dependencies(Socket *s) {
int r;
assert(s);
- if (UNIT(s)->manager->running_as == SYSTEMD_SYSTEM) {
- if ((r = unit_add_dependency_by_name(UNIT(s), UNIT_BEFORE, SPECIAL_SOCKETS_TARGET, NULL, true)) < 0)
- return r;
+ r = unit_add_dependency_by_name(UNIT(s), UNIT_BEFORE, SPECIAL_SOCKETS_TARGET, NULL, true);
+ if (r < 0)
+ return r;
- if ((r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, NULL, true)) < 0)
+ if (UNIT(s)->manager->running_as == SYSTEMD_SYSTEM) {
+ r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, NULL, true);
+ if (r < 0)
return r;
}
return unit_add_two_dependencies_by_name(UNIT(s), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, NULL, true);
}
-static bool socket_has_exec(Socket *s) {
+_pure_ static bool socket_has_exec(Socket *s) {
unsigned i;
assert(s);
@@ -401,7 +410,7 @@ static int socket_load(Unit *u) {
return socket_verify(s);
}
-static const char* listen_lookup(int family, int type) {
+_const_ static const char* listen_lookup(int family, int type) {
if (family == AF_NETLINK)
return "ListenNetlink";
@@ -779,7 +788,7 @@ static void socket_apply_socket_options(Socket *s, int fd) {
if (setsockopt(fd, SOL_TCP, TCP_CONGESTION, s->tcp_congestion, strlen(s->tcp_congestion)+1) < 0)
log_warning_unit(UNIT(s)->id, "TCP_CONGESTION failed: %m");
-#ifdef HAVE_ATTR_XATTR_H
+#ifdef HAVE_XATTR
if (s->smack_ip_in)
if (fsetxattr(fd, "security.SMACK64IPIN", s->smack_ip_in, strlen(s->smack_ip_in), 0) < 0)
log_error_unit(UNIT(s)->id,
@@ -801,7 +810,7 @@ static void socket_apply_fifo_options(Socket *s, int fd) {
log_warning_unit(UNIT(s)->id,
"F_SETPIPE_SZ: %m");
-#ifdef HAVE_ATTR_XATTR_H
+#ifdef HAVE_XATTR
if (s->smack)
if (fsetxattr(fd, "security.SMACK64", s->smack, strlen(s->smack), 0) < 0)
log_error_unit(UNIT(s)->id,
@@ -1246,6 +1255,7 @@ static void socket_enter_dead(Socket *s, SocketResult f) {
if (f != SOCKET_SUCCESS)
s->result = f;
+ exec_context_tmp_dirs_done(&s->exec_context);
socket_set_state(s, s->result != SOCKET_SUCCESS ? SOCKET_FAILED : SOCKET_DEAD);
}
@@ -1281,54 +1291,23 @@ fail:
static void socket_enter_signal(Socket *s, SocketState state, SocketResult f) {
int r;
- Set *pid_set = NULL;
- bool wait_for_exit = false;
assert(s);
if (f != SOCKET_SUCCESS)
s->result = f;
- if (s->kill_context.kill_mode != KILL_NONE) {
- int sig = (state == SOCKET_STOP_PRE_SIGTERM || state == SOCKET_FINAL_SIGTERM) ? s->kill_context.kill_signal : SIGKILL;
-
- if (s->control_pid > 0) {
- if (kill_and_sigcont(s->control_pid, sig) < 0 && errno != ESRCH)
-
- log_warning_unit(UNIT(s)->id,
- "Failed to kill control process %li: %m",
- (long) s->control_pid);
- else
- wait_for_exit = true;
- }
-
- if (s->kill_context.kill_mode == KILL_CONTROL_GROUP) {
-
- if (!(pid_set = set_new(trivial_hash_func, trivial_compare_func))) {
- r = -ENOMEM;
- goto fail;
- }
-
- /* Exclude the control pid from being killed via the cgroup */
- if (s->control_pid > 0)
- if ((r = set_put(pid_set, LONG_TO_PTR(s->control_pid))) < 0)
- goto fail;
-
- r = cgroup_bonding_kill_list(UNIT(s)->cgroup_bondings, sig, true, false, pid_set, NULL);
- if (r < 0) {
- if (r != -EAGAIN && r != -ESRCH && r != -ENOENT)
- log_warning_unit(UNIT(s)->id,
- "Failed to kill control group: %s",
- strerror(-r));
- } else if (r > 0)
- wait_for_exit = true;
-
- set_free(pid_set);
- pid_set = NULL;
- }
- }
+ r = unit_kill_context(
+ UNIT(s),
+ &s->kill_context,
+ state != SOCKET_STOP_PRE_SIGTERM && state != SOCKET_FINAL_SIGTERM,
+ -1,
+ s->control_pid,
+ false);
+ if (r < 0)
+ goto fail;
- if (wait_for_exit) {
+ if (r > 0) {
r = unit_watch_timer(UNIT(s), CLOCK_MONOTONIC, true, s->timeout_usec, &s->timer_watch);
if (r < 0)
goto fail;
@@ -1350,9 +1329,6 @@ fail:
socket_enter_stop_post(s, SOCKET_FAILURE_RESOURCES);
else
socket_enter_dead(s, SOCKET_FAILURE_RESOURCES);
-
- if (pid_set)
- set_free(pid_set);
}
static void socket_enter_stop_pre(Socket *s, SocketResult f) {
@@ -1471,7 +1447,7 @@ static void socket_enter_running(Socket *s, int cfd) {
/* We don't take connections anymore if we are supposed to
* shut down anyway */
- if (unit_pending_inactive(UNIT(s))) {
+ if (unit_stop_pending(UNIT(s))) {
log_debug_unit(UNIT(s)->id,
"Suppressing connection request on %s since unit stop is scheduled.",
UNIT(s)->id);
@@ -1502,7 +1478,7 @@ static void socket_enter_running(Socket *s, int cfd) {
/* If there's already a start pending don't bother to
* do anything */
SET_FOREACH(u, UNIT(s)->dependencies[UNIT_TRIGGERS], i)
- if (unit_pending_active(u)) {
+ if (unit_active_or_pending(u)) {
pending = true;
break;
}
@@ -1658,7 +1634,7 @@ static int socket_start(Unit *u) {
service = SERVICE(UNIT_DEREF(s->service));
if (UNIT(service)->load_state != UNIT_LOADED) {
- log_error_unit(UNIT(service)->id,
+ log_error_unit(u->id,
"Socket service %s not loaded, refusing.",
UNIT(service)->id);
return -ENOENT;
@@ -1669,7 +1645,7 @@ static int socket_start(Unit *u) {
if (service->state != SERVICE_DEAD &&
service->state != SERVICE_FAILED &&
service->state != SERVICE_AUTO_RESTART) {
- log_error_unit(UNIT(service)->id,
+ log_error_unit(u->id,
"Socket service %s already active, refusing.",
UNIT(service)->id);
return -EBUSY;
@@ -1677,7 +1653,7 @@ static int socket_start(Unit *u) {
#ifdef HAVE_SYSV_COMPAT
if (service->is_sysv) {
- log_error_unit(UNIT(s)->id,
+ log_error_unit(u->id,
"Using SysV services for socket activation is not supported. Refusing.");
return -ENOENT;
}
@@ -1769,6 +1745,8 @@ static int socket_serialize(Unit *u, FILE *f, FDSet *fds) {
}
}
+ exec_context_serialize(&s->exec_context, UNIT(s), f);
+
return 0;
}
@@ -1928,7 +1906,22 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value,
p->fd = fdset_remove(fds, fd);
}
}
+ } else if (streq(key, "tmp-dir")) {
+ char *t;
+
+ t = strdup(value);
+ if (!t)
+ return log_oom();
+
+ s->exec_context.tmp_dir = t;
+ } else if (streq(key, "var-tmp-dir")) {
+ char *t;
+ t = strdup(value);
+ if (!t)
+ return log_oom();
+
+ s->exec_context.var_tmp_dir = t;
} else
log_debug_unit(UNIT(s)->id,
"Unknown serialization key '%s'", key);
@@ -1964,19 +1957,41 @@ static int socket_distribute_fds(Unit *u, FDSet *fds) {
return 0;
}
-static UnitActiveState socket_active_state(Unit *u) {
+_pure_ static UnitActiveState socket_active_state(Unit *u) {
assert(u);
return state_translation_table[SOCKET(u)->state];
}
-static const char *socket_sub_state_to_string(Unit *u) {
+_pure_ static const char *socket_sub_state_to_string(Unit *u) {
assert(u);
return socket_state_to_string(SOCKET(u)->state);
}
-static bool socket_check_gc(Unit *u) {
+const char* socket_port_type_to_string(SocketPort *p) {
+
+ assert(p);
+
+ switch (p->type) {
+ case SOCKET_SOCKET:
+ switch (p->address.type) {
+ case SOCK_STREAM: return "Stream";
+ case SOCK_DGRAM: return "Datagram";
+ case SOCK_SEQPACKET: return "SequentialPacket";
+ case SOCK_RAW:
+ if (socket_address_family(&p->address) == AF_NETLINK)
+ return "Netlink";
+ default: return "Invalid";
+ }
+ case SOCKET_SPECIAL: return "Special";
+ case SOCKET_MQUEUE: return "MessageQueue";
+ case SOCKET_FIFO: return "FIFO";
+ default: return NULL;
+ }
+}
+
+_pure_ static bool socket_check_gc(Unit *u) {
Socket *s = SOCKET(u);
assert(u);
@@ -2292,53 +2307,7 @@ static void socket_reset_failed(Unit *u) {
}
static int socket_kill(Unit *u, KillWho who, int signo, DBusError *error) {
- Socket *s = SOCKET(u);
- int r = 0;
- Set *pid_set = NULL;
-
- assert(s);
-
- if (who == KILL_MAIN) {
- dbus_set_error(error, BUS_ERROR_NO_SUCH_PROCESS, "Socket units have no main processes");
- return -ESRCH;
- }
-
- if (s->control_pid <= 0 && who == KILL_CONTROL) {
- dbus_set_error(error, BUS_ERROR_NO_SUCH_PROCESS, "No control process to kill");
- return -ESRCH;
- }
-
- if (who == KILL_CONTROL || who == KILL_ALL)
- if (s->control_pid > 0)
- if (kill(s->control_pid, signo) < 0)
- r = -errno;
-
- if (who == KILL_ALL) {
- int q;
-
- pid_set = set_new(trivial_hash_func, trivial_compare_func);
- if (!pid_set)
- return -ENOMEM;
-
- /* Exclude the control pid from being killed via the cgroup */
- if (s->control_pid > 0) {
- q = set_put(pid_set, LONG_TO_PTR(s->control_pid));
- if (q < 0) {
- r = q;
- goto finish;
- }
- }
-
- q = cgroup_bonding_kill_list(UNIT(s)->cgroup_bondings, signo, false, false, pid_set, NULL);
- if (q < 0 && q != -EAGAIN && q != -ESRCH && q != -ENOENT)
- r = q;
- }
-
-finish:
- if (pid_set)
- set_free(pid_set);
-
- return r;
+ return unit_kill_common(u, who, signo, -1, SOCKET(u)->control_pid, error);
}
static const char* const socket_state_table[_SOCKET_STATE_MAX] = {
@@ -2381,13 +2350,15 @@ DEFINE_STRING_TABLE_LOOKUP(socket_result, SocketResult);
const UnitVTable socket_vtable = {
.object_size = sizeof(Socket),
- .exec_context_offset = offsetof(Socket, exec_context),
.sections =
"Unit\0"
"Socket\0"
"Install\0",
+ .exec_context_offset = offsetof(Socket, exec_context),
+ .exec_section = "Socket",
+
.init = socket_init,
.done = socket_done,
.load = socket_load,
diff --git a/src/core/socket.h b/src/core/socket.h
index f099520dce..9d48cde0a6 100644
--- a/src/core/socket.h
+++ b/src/core/socket.h
@@ -130,6 +130,10 @@ struct Socket {
bool broadcast;
bool pass_cred;
bool pass_sec;
+
+ /* Only for INET6 sockets: issue IPV6_V6ONLY sockopt */
+ SocketAddressBindIPv6Only bind_ipv6_only;
+
int priority;
int mark;
size_t receive_buffer;
@@ -142,9 +146,6 @@ struct Socket {
long mq_maxmsg;
long mq_msgsize;
- /* Only for INET6 sockets: issue IPV6_V6ONLY sockopt */
- SocketAddressBindIPv6Only bind_ipv6_only;
-
char *smack;
char *smack_ip_in;
char *smack_ip_out;
@@ -163,13 +164,17 @@ int socket_add_one_mount_link(Socket *s, Mount *m);
/* Called from the service code when a per-connection service ended */
void socket_connection_unref(Socket *s);
+void socket_free_ports(Socket *s);
+
extern const UnitVTable socket_vtable;
-const char* socket_state_to_string(SocketState i);
-SocketState socket_state_from_string(const char *s);
+const char* socket_state_to_string(SocketState i) _const_;
+SocketState socket_state_from_string(const char *s) _pure_;
+
+const char* socket_exec_command_to_string(SocketExecCommand i) _const_;
+SocketExecCommand socket_exec_command_from_string(const char *s) _pure_;
-const char* socket_exec_command_to_string(SocketExecCommand i);
-SocketExecCommand socket_exec_command_from_string(const char *s);
+const char* socket_result_to_string(SocketResult i) _const_;
+SocketResult socket_result_from_string(const char *s) _pure_;
-const char* socket_result_to_string(SocketResult i);
-SocketResult socket_result_from_string(const char *s);
+const char* socket_port_type_to_string(SocketPort *p) _pure_;
diff --git a/src/core/special.h b/src/core/special.h
index ef72260ecd..a9b50bce05 100644
--- a/src/core/special.h
+++ b/src/core/special.h
@@ -46,21 +46,23 @@
/* Early boot targets */
#define SPECIAL_SYSINIT_TARGET "sysinit.target"
#define SPECIAL_SOCKETS_TARGET "sockets.target"
-#define SPECIAL_LOCAL_FS_TARGET "local-fs.target" /* LSB's $local_fs */
+#define SPECIAL_TIMERS_TARGET "timers.target"
+#define SPECIAL_PATHS_TARGET "paths.target"
+#define SPECIAL_LOCAL_FS_TARGET "local-fs.target"
#define SPECIAL_LOCAL_FS_PRE_TARGET "local-fs-pre.target"
+#define SPECIAL_INITRD_FS_TARGET "initrd-fs.target"
+#define SPECIAL_INITRD_ROOT_FS_TARGET "initrd-root-fs.target"
#define SPECIAL_REMOTE_FS_TARGET "remote-fs.target" /* LSB's $remote_fs */
#define SPECIAL_REMOTE_FS_PRE_TARGET "remote-fs-pre.target"
#define SPECIAL_SWAP_TARGET "swap.target"
+#define SPECIAL_NETWORK_ONLINE_TARGET "network-online.target"
#define SPECIAL_BASIC_TARGET "basic.target"
/* LSB compatibility */
#define SPECIAL_NETWORK_TARGET "network.target" /* LSB's $network */
#define SPECIAL_NSS_LOOKUP_TARGET "nss-lookup.target" /* LSB's $named */
#define SPECIAL_RPCBIND_TARGET "rpcbind.target" /* LSB's $portmap */
-#define SPECIAL_SYSLOG_TARGET "syslog.target" /* LSB's $syslog */
#define SPECIAL_TIME_SYNC_TARGET "time-sync.target" /* LSB's $time */
-#define SPECIAL_DISPLAY_MANAGER_SERVICE "display-manager.service" /* Common extension of LSB */
-#define SPECIAL_MAIL_TRANSFER_AGENT_TARGET "mail-transfer-agent.target" /* Common extension of LSB */
/*
* Rules regarding adding further high level targets like the above:
diff --git a/src/core/swap.c b/src/core/swap.c
index c8e25d0665..d503fe20df 100644
--- a/src/core/swap.c
+++ b/src/core/swap.c
@@ -125,7 +125,7 @@ static void swap_done(Unit *u) {
free(s->parameters_fragment.what);
s->parameters_fragment.what = NULL;
- exec_context_done(&s->exec_context);
+ exec_context_done(&s->exec_context, manager_is_reloading_or_reexecuting(u->manager));
exec_command_done_array(s->exec_command, _SWAP_EXEC_COMMAND_MAX);
s->control_command = NULL;
@@ -214,7 +214,7 @@ static int swap_add_default_dependencies(Swap *s) {
static int swap_verify(Swap *s) {
bool b;
- char _cleanup_free_ *e = NULL;
+ _cleanup_free_ char *e = NULL;
if (UNIT(s)->load_state != UNIT_LOADED)
return 0;
@@ -315,7 +315,7 @@ static int swap_add_one(
bool set_flags) {
Unit *u = NULL;
- char _cleanup_free_ *e = NULL;
+ _cleanup_free_ char *e = NULL;
char *wp = NULL;
bool delete = false;
int r;
@@ -632,6 +632,7 @@ static void swap_enter_dead(Swap *s, SwapResult f) {
if (f != SWAP_SUCCESS)
s->result = f;
+ exec_context_tmp_dirs_done(&s->exec_context);
swap_set_state(s, s->result != SWAP_SUCCESS ? SWAP_FAILED : SWAP_DEAD);
}
@@ -646,57 +647,23 @@ static void swap_enter_active(Swap *s, SwapResult f) {
static void swap_enter_signal(Swap *s, SwapState state, SwapResult f) {
int r;
- Set *pid_set = NULL;
- bool wait_for_exit = false;
assert(s);
if (f != SWAP_SUCCESS)
s->result = f;
- if (s->kill_context.kill_mode != KILL_NONE) {
- int sig = (state == SWAP_ACTIVATING_SIGTERM ||
- state == SWAP_DEACTIVATING_SIGTERM) ? s->kill_context.kill_signal : SIGKILL;
-
- if (s->control_pid > 0) {
- if (kill_and_sigcont(s->control_pid, sig) < 0 && errno != ESRCH)
-
- log_warning_unit(UNIT(s)->id,
- "Failed to kill control process %li: %m",
- (long) s->control_pid);
- else
- wait_for_exit = true;
- }
-
- if (s->kill_context.kill_mode == KILL_CONTROL_GROUP) {
-
- pid_set = set_new(trivial_hash_func, trivial_compare_func);
- if (!pid_set) {
- r = log_oom();
- goto fail;
- }
-
- /* Exclude the control pid from being killed via the cgroup */
- if (s->control_pid > 0) {
- r = set_put(pid_set, LONG_TO_PTR(s->control_pid));
- if (r < 0)
- goto fail;
- }
-
- r = cgroup_bonding_kill_list(UNIT(s)->cgroup_bondings, sig, true, false, pid_set, NULL);
- if (r < 0) {
- if (r != -EAGAIN && r != -ESRCH && r != -ENOENT)
- log_warning_unit(UNIT(s)->id,
- "Failed to kill control group: %s", strerror(-r));
- } else if (r > 0)
- wait_for_exit = true;
-
- set_free(pid_set);
- pid_set = NULL;
- }
- }
+ r = unit_kill_context(
+ UNIT(s),
+ &s->kill_context,
+ state != SWAP_ACTIVATING_SIGTERM && state != SWAP_DEACTIVATING_SIGTERM,
+ -1,
+ s->control_pid,
+ false);
+ if (r < 0)
+ goto fail;
- if (wait_for_exit) {
+ if (r > 0) {
r = unit_watch_timer(UNIT(s), CLOCK_MONOTONIC, true, s->timeout_usec, &s->timer_watch);
if (r < 0)
goto fail;
@@ -712,9 +679,6 @@ fail:
"%s failed to kill processes: %s", UNIT(s)->id, strerror(-r));
swap_enter_dead(s, SWAP_FAILURE_RESOURCES);
-
- if (pid_set)
- set_free(pid_set);
}
static void swap_enter_activating(Swap *s) {
@@ -868,6 +832,8 @@ static int swap_serialize(Unit *u, FILE *f, FDSet *fds) {
if (s->control_command_id >= 0)
unit_serialize_item(u, f, "control-command", swap_exec_command_to_string(s->control_command_id));
+ exec_context_serialize(&s->exec_context, UNIT(s), f);
+
return 0;
}
@@ -911,26 +877,41 @@ static int swap_deserialize_item(Unit *u, const char *key, const char *value, FD
s->control_command_id = id;
s->control_command = s->exec_command + id;
}
+ } else if (streq(key, "tmp-dir")) {
+ char *t;
+ t = strdup(value);
+ if (!t)
+ return log_oom();
+
+ s->exec_context.tmp_dir = t;
+ } else if (streq(key, "var-tmp-dir")) {
+ char *t;
+
+ t = strdup(value);
+ if (!t)
+ return log_oom();
+
+ s->exec_context.var_tmp_dir = t;
} else
log_debug_unit(u->id, "Unknown serialization key '%s'", key);
return 0;
}
-static UnitActiveState swap_active_state(Unit *u) {
+_pure_ static UnitActiveState swap_active_state(Unit *u) {
assert(u);
return state_translation_table[SWAP(u)->state];
}
-static const char *swap_sub_state_to_string(Unit *u) {
+_pure_ static const char *swap_sub_state_to_string(Unit *u) {
assert(u);
return swap_state_to_string(SWAP(u)->state);
}
-static bool swap_check_gc(Unit *u) {
+_pure_ static bool swap_check_gc(Unit *u) {
Swap *s = SWAP(u);
assert(s);
@@ -1261,10 +1242,14 @@ static void swap_shutdown(Manager *m) {
static int swap_enumerate(Manager *m) {
int r;
- struct epoll_event ev;
assert(m);
if (!m->proc_swaps) {
+ struct epoll_event ev = {
+ .events = EPOLLPRI,
+ .data.ptr = &m->swap_watch,
+ };
+
m->proc_swaps = fopen("/proc/swaps", "re");
if (!m->proc_swaps)
return (errno == ENOENT) ? 0 : -errno;
@@ -1272,10 +1257,6 @@ static int swap_enumerate(Manager *m) {
m->swap_watch.type = WATCH_SWAP;
m->swap_watch.fd = fileno(m->proc_swaps);
- zero(ev);
- ev.events = EPOLLPRI;
- ev.data.ptr = &m->swap_watch;
-
if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->swap_watch.fd, &ev) < 0)
return -errno;
}
@@ -1299,53 +1280,7 @@ static void swap_reset_failed(Unit *u) {
}
static int swap_kill(Unit *u, KillWho who, int signo, DBusError *error) {
- Swap *s = SWAP(u);
- int r = 0;
- Set *pid_set = NULL;
-
- assert(s);
-
- if (who == KILL_MAIN) {
- dbus_set_error(error, BUS_ERROR_NO_SUCH_PROCESS, "Swap units have no main processes");
- return -ESRCH;
- }
-
- if (s->control_pid <= 0 && who == KILL_CONTROL) {
- dbus_set_error(error, BUS_ERROR_NO_SUCH_PROCESS, "No control process to kill");
- return -ESRCH;
- }
-
- if (who == KILL_CONTROL || who == KILL_ALL)
- if (s->control_pid > 0)
- if (kill(s->control_pid, signo) < 0)
- r = -errno;
-
- if (who == KILL_ALL) {
- int q;
-
- pid_set = set_new(trivial_hash_func, trivial_compare_func);
- if (!pid_set)
- return -ENOMEM;
-
- /* Exclude the control pid from being killed via the cgroup */
- if (s->control_pid > 0) {
- q = set_put(pid_set, LONG_TO_PTR(s->control_pid));
- if (q < 0) {
- r = q;
- goto finish;
- }
- }
-
- q = cgroup_bonding_kill_list(UNIT(s)->cgroup_bondings, signo, false, false, pid_set, NULL);
- if (q < 0 && q != -EAGAIN && q != -ESRCH && q != -ENOENT)
- r = q;
- }
-
-finish:
- if (pid_set)
- set_free(pid_set);
-
- return r;
+ return unit_kill_common(u, who, signo, -1, SWAP(u)->control_pid, error);
}
static const char* const swap_state_table[_SWAP_STATE_MAX] = {
@@ -1382,13 +1317,15 @@ DEFINE_STRING_TABLE_LOOKUP(swap_result, SwapResult);
const UnitVTable swap_vtable = {
.object_size = sizeof(Swap),
- .exec_context_offset = offsetof(Swap, exec_context),
.sections =
"Unit\0"
"Swap\0"
"Install\0",
+ .exec_context_offset = offsetof(Swap, exec_context),
+ .exec_section = "Swap",
+
.no_alias = true,
.no_instances = true,
diff --git a/src/core/swap.h b/src/core/swap.h
index 35d47fd46f..121889d1d5 100644
--- a/src/core/swap.h
+++ b/src/core/swap.h
@@ -111,11 +111,11 @@ int swap_add_one_mount_link(Swap *s, Mount *m);
int swap_dispatch_reload(Manager *m);
int swap_fd_event(Manager *m, int events);
-const char* swap_state_to_string(SwapState i);
-SwapState swap_state_from_string(const char *s);
+const char* swap_state_to_string(SwapState i) _const_;
+SwapState swap_state_from_string(const char *s) _pure_;
-const char* swap_exec_command_to_string(SwapExecCommand i);
-SwapExecCommand swap_exec_command_from_string(const char *s);
+const char* swap_exec_command_to_string(SwapExecCommand i) _const_;
+SwapExecCommand swap_exec_command_from_string(const char *s) _pure_;
-const char* swap_result_to_string(SwapResult i);
-SwapResult swap_result_from_string(const char *s);
+const char* swap_result_to_string(SwapResult i) _const_;
+SwapResult swap_result_from_string(const char *s) _pure_;
diff --git a/src/core/sync.c b/src/core/sync.c
new file mode 100644
index 0000000000..7e74b63071
--- /dev/null
+++ b/src/core/sync.c
@@ -0,0 +1,65 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ 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 <http://www.gnu.org/licenses/>.
+***/
+
+#include <pthread.h>
+#include <unistd.h>
+
+#include "sync.h"
+
+static void *sync_thread(void *p) {
+ sync();
+ return NULL;
+}
+
+int asynchronous_sync(void) {
+ pthread_attr_t a;
+ pthread_t t;
+ int r;
+
+ /* It kinda sucks that we have to resort to threads to
+ * implement an asynchronous sync(), but well, such is
+ * life.
+ *
+ * Note that issuing this command right before exiting a
+ * process will cause the process to wait for the sync() to
+ * complete. This function hence is nicely asynchronous really
+ * only in long running processes. */
+
+ r = pthread_attr_init(&a);
+ if (r != 0)
+ return -r;
+
+ r = pthread_attr_setdetachstate(&a, PTHREAD_CREATE_DETACHED);
+ if (r != 0) {
+ r = -r;
+ goto finish;
+ }
+
+ r = pthread_create(&t, &a, sync_thread, NULL);
+ if (r != 0) {
+ r = -r;
+ goto finish;
+ }
+
+finish:
+ pthread_attr_destroy(&a);
+ return r;
+}
diff --git a/src/core/sync.h b/src/core/sync.h
new file mode 100644
index 0000000000..eb26c88deb
--- /dev/null
+++ b/src/core/sync.h
@@ -0,0 +1,24 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ 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 <http://www.gnu.org/licenses/>.
+***/
+
+int asynchronous_sync(void);
diff --git a/src/core/syscall-list.c b/src/core/syscall-list.c
index 05fad3e158..35216b2a88 100644
--- a/src/core/syscall-list.c
+++ b/src/core/syscall-list.c
@@ -26,12 +26,14 @@
#include "syscall-list.h"
-const struct syscall_name *lookup_syscall(register const char *str, register unsigned int len);
+static const struct syscall_name* lookup_syscall(register const char *str,
+ register unsigned int len);
#include "syscall-to-name.h"
#include "syscall-from-name.h"
const char *syscall_to_name(int id) {
+ id = SYSCALL_TO_INDEX(id);
if (id < 0 || id >= (int) ELEMENTSOF(syscall_names))
return NULL;
diff --git a/src/core/syscall-list.h b/src/core/syscall-list.h
index 0fc6859605..6bb4d91f91 100644
--- a/src/core/syscall-list.h
+++ b/src/core/syscall-list.h
@@ -22,6 +22,20 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#if defined __x86_64__ && defined __ILP32__
+/* The x32 ABI defines all of its syscalls with bit 30 set, which causes
+ issues when attempting to use syscalls as simple indices into an array.
+ Instead, use the syscall id & ~SYSCALL_MASK as the index, and | the
+ internal id with the syscall mask as needed.
+*/
+#include <asm/unistd.h>
+#define SYSCALL_TO_INDEX(x) ((x) & ~__X32_SYSCALL_BIT)
+#define INDEX_TO_SYSCALL(x) ((x) | __X32_SYSCALL_BIT)
+#else
+#define SYSCALL_TO_INDEX(x) (x)
+#define INDEX_TO_SYSCALL(x) (x)
+#endif
+
const char *syscall_to_name(int id);
int syscall_from_name(const char *name);
diff --git a/src/core/system.conf b/src/core/system.conf
index 68076d9735..508e0f5fa4 100644
--- a/src/core/system.conf
+++ b/src/core/system.conf
@@ -5,7 +5,7 @@
# the Free Software Foundation; either version 2.1 of the License, or
# (at your option) any later version.
#
-# See systemd.conf(5) for details
+# See systemd-system.conf(5) for details
[Manager]
#LogLevel=info
diff --git a/src/core/target.c b/src/core/target.c
index 981424132b..3fffa0d2f5 100644
--- a/src/core/target.c
+++ b/src/core/target.c
@@ -184,13 +184,13 @@ static int target_deserialize_item(Unit *u, const char *key, const char *value,
return 0;
}
-static UnitActiveState target_active_state(Unit *u) {
+_pure_ static UnitActiveState target_active_state(Unit *u) {
assert(u);
return state_translation_table[TARGET(u)->state];
}
-static const char *target_sub_state_to_string(Unit *u) {
+_pure_ static const char *target_sub_state_to_string(Unit *u) {
assert(u);
return target_state_to_string(TARGET(u)->state);
diff --git a/src/core/target.h b/src/core/target.h
index 1676553add..a5398d9e98 100644
--- a/src/core/target.h
+++ b/src/core/target.h
@@ -40,5 +40,5 @@ struct Target {
extern const UnitVTable target_vtable;
-const char* target_state_to_string(TargetState i);
-TargetState target_state_from_string(const char *s);
+const char* target_state_to_string(TargetState i) _const_;
+TargetState target_state_from_string(const char *s) _pure_;
diff --git a/src/core/timer.c b/src/core/timer.c
index 31ef176e7e..9166c1e2fc 100644
--- a/src/core/timer.c
+++ b/src/core/timer.c
@@ -26,7 +26,7 @@
#include "timer.h"
#include "dbus-timer.h"
#include "special.h"
-#include "bus-errors.h"
+#include "dbus-common.h"
static const UnitActiveState state_translation_table[_TIMER_STATE_MAX] = {
[TIMER_DEAD] = UNIT_INACTIVE,
@@ -48,8 +48,7 @@ static void timer_init(Unit *u) {
watch_init(&t->realtime_watch);
}
-static void timer_done(Unit *u) {
- Timer *t = TIMER(u);
+void timer_free_values(Timer *t) {
TimerValue *v;
assert(t);
@@ -62,11 +61,17 @@ static void timer_done(Unit *u) {
free(v);
}
+}
+
+static void timer_done(Unit *u) {
+ Timer *t = TIMER(u);
+
+ assert(t);
+
+ timer_free_values(t);
unit_unwatch_timer(u, &t->monotonic_watch);
unit_unwatch_timer(u, &t->realtime_watch);
-
- unit_ref_unset(&t->unit);
}
static int timer_verify(Timer *t) {
@@ -89,11 +94,11 @@ static int timer_add_default_dependencies(Timer *t) {
assert(t);
- if (UNIT(t)->manager->running_as == SYSTEMD_SYSTEM) {
- r = unit_add_dependency_by_name(UNIT(t), UNIT_BEFORE, SPECIAL_BASIC_TARGET, NULL, true);
- if (r < 0)
- return r;
+ r = unit_add_dependency_by_name(UNIT(t), UNIT_BEFORE, SPECIAL_TIMERS_TARGET, NULL, true);
+ if (r < 0)
+ return r;
+ if (UNIT(t)->manager->running_as == SYSTEMD_SYSTEM) {
r = unit_add_two_dependencies_by_name(UNIT(t), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, NULL, true);
if (r < 0)
return r;
@@ -115,20 +120,18 @@ static int timer_load(Unit *u) {
if (u->load_state == UNIT_LOADED) {
- if (!UNIT_DEREF(t->unit)) {
+ if (set_isempty(u->dependencies[UNIT_TRIGGERS])) {
Unit *x;
r = unit_load_related_unit(u, ".service", &x);
if (r < 0)
return r;
- unit_ref_set(&t->unit, x);
+ r = unit_add_two_dependencies(u, UNIT_BEFORE, UNIT_TRIGGERS, x, true);
+ if (r < 0)
+ return r;
}
- r = unit_add_two_dependencies(u, UNIT_BEFORE, UNIT_TRIGGERS, UNIT_DEREF(t->unit), true);
- if (r < 0)
- return r;
-
if (UNIT(t)->default_dependencies) {
r = timer_add_default_dependencies(t);
if (r < 0)
@@ -141,15 +144,18 @@ static int timer_load(Unit *u) {
static void timer_dump(Unit *u, FILE *f, const char *prefix) {
Timer *t = TIMER(u);
+ Unit *trigger;
TimerValue *v;
+ trigger = UNIT_TRIGGER(u);
+
fprintf(f,
"%sTimer State: %s\n"
"%sResult: %s\n"
"%sUnit: %s\n",
prefix, timer_state_to_string(t->state),
prefix, timer_result_to_string(t->result),
- prefix, UNIT_DEREF(t->unit)->id);
+ prefix, trigger ? trigger->id : "n/a");
LIST_FOREACH(value, v, t->values) {
@@ -170,7 +176,7 @@ static void timer_dump(Unit *u, FILE *f, const char *prefix) {
"%s%s: %s\n",
prefix,
timer_base_to_string(v->base),
- strna(format_timespan(timespan1, sizeof(timespan1), v->value)));
+ strna(format_timespan(timespan1, sizeof(timespan1), v->value, 0)));
}
}
}
@@ -245,11 +251,6 @@ static void timer_enter_waiting(Timer *t, bool initial) {
if (r < 0)
continue;
- if (!initial && v->next_elapse < ts.realtime) {
- v->disabled = true;
- continue;
- }
-
if (!found_realtime)
t->next_elapse_realtime = v->next_elapse;
else
@@ -278,18 +279,26 @@ static void timer_enter_waiting(Timer *t, bool initial) {
case TIMER_UNIT_ACTIVE:
- if (UNIT_DEREF(t->unit)->inactive_exit_timestamp.monotonic <= 0)
+ base = UNIT_TRIGGER(UNIT(t))->inactive_exit_timestamp.monotonic;
+
+ if (base <= 0)
+ base = t->last_trigger_monotonic;
+
+ if (base <= 0)
continue;
- base = UNIT_DEREF(t->unit)->inactive_exit_timestamp.monotonic;
break;
case TIMER_UNIT_INACTIVE:
- if (UNIT_DEREF(t->unit)->inactive_enter_timestamp.monotonic <= 0)
+ base = UNIT_TRIGGER(UNIT(t))->inactive_enter_timestamp.monotonic;
+
+ if (base <= 0)
+ base = t->last_trigger_monotonic;
+
+ if (base <= 0)
continue;
- base = UNIT_DEREF(t->unit)->inactive_enter_timestamp.monotonic;
break;
default:
@@ -298,7 +307,10 @@ static void timer_enter_waiting(Timer *t, bool initial) {
v->next_elapse = base + v->value;
- if (!initial && v->next_elapse < ts.monotonic) {
+ if (!initial &&
+ v->next_elapse < ts.monotonic &&
+ (v->base == TIMER_ACTIVE || v->base == TIMER_BOOT || v->base == TIMER_STARTUP)) {
+ /* This is a one time trigger, disable it now */
v->disabled = true;
continue;
}
@@ -321,9 +333,9 @@ static void timer_enter_waiting(Timer *t, bool initial) {
if (found_monotonic) {
char buf[FORMAT_TIMESPAN_MAX];
log_debug_unit(UNIT(t)->id,
- "%s: Monotonic timer elapses in %s the next time.",
+ "%s: Monotonic timer elapses in %s.",
UNIT(t)->id,
- format_timespan(buf, sizeof(buf), t->next_elapse_monotonic - ts.monotonic));
+ format_timespan(buf, sizeof(buf), t->next_elapse_monotonic > ts.monotonic ? t->next_elapse_monotonic - ts.monotonic : 0, 0));
r = unit_watch_timer(UNIT(t), CLOCK_MONOTONIC, false, t->next_elapse_monotonic, &t->monotonic_watch);
if (r < 0)
@@ -334,7 +346,7 @@ static void timer_enter_waiting(Timer *t, bool initial) {
if (found_realtime) {
char buf[FORMAT_TIMESTAMP_MAX];
log_debug_unit(UNIT(t)->id,
- "%s: Realtime timer elapses at %s the next time.",
+ "%s: Realtime timer elapses at %s.",
UNIT(t)->id,
format_timestamp(buf, sizeof(buf), t->next_elapse_realtime));
@@ -362,13 +374,16 @@ static void timer_enter_running(Timer *t) {
dbus_error_init(&error);
/* Don't start job if we are supposed to go down */
- if (UNIT(t)->job && UNIT(t)->job->type == JOB_STOP)
+ if (unit_stop_pending(UNIT(t)))
return;
- r = manager_add_job(UNIT(t)->manager, JOB_START, UNIT_DEREF(t->unit), JOB_REPLACE, true, &error, NULL);
+ r = manager_add_job(UNIT(t)->manager, JOB_START, UNIT_TRIGGER(UNIT(t)),
+ JOB_REPLACE, true, &error, NULL);
if (r < 0)
goto fail;
+ t->last_trigger_monotonic = now(CLOCK_MONOTONIC);
+
timer_set_state(t, TIMER_RUNNING);
return;
@@ -387,7 +402,7 @@ static int timer_start(Unit *u) {
assert(t);
assert(t->state == TIMER_DEAD || t->state == TIMER_FAILED);
- if (UNIT_DEREF(t->unit)->load_state != UNIT_LOADED)
+ if (UNIT_TRIGGER(u)->load_state != UNIT_LOADED)
return -ENOENT;
t->result = TIMER_SUCCESS;
@@ -449,13 +464,13 @@ static int timer_deserialize_item(Unit *u, const char *key, const char *value, F
return 0;
}
-static UnitActiveState timer_active_state(Unit *u) {
+_pure_ static UnitActiveState timer_active_state(Unit *u) {
assert(u);
return state_translation_table[TIMER(u)->state];
}
-static const char *timer_sub_state_to_string(Unit *u) {
+_pure_ static const char *timer_sub_state_to_string(Unit *u) {
assert(u);
return timer_state_to_string(TIMER(u)->state);
@@ -474,58 +489,47 @@ static void timer_timer_event(Unit *u, uint64_t elapsed, Watch *w) {
timer_enter_running(t);
}
-void timer_unit_notify(Unit *u, UnitActiveState new_state) {
- Iterator i;
- Unit *k;
-
- if (u->type == UNIT_TIMER)
- return;
+static void timer_trigger_notify(Unit *u, Unit *other) {
+ Timer *t = TIMER(u);
+ TimerValue *v;
- SET_FOREACH(k, u->dependencies[UNIT_TRIGGERED_BY], i) {
- Timer *t;
- TimerValue *v;
+ assert(u);
+ assert(other);
- if (k->type != UNIT_TIMER)
- continue;
+ if (other->load_state != UNIT_LOADED)
+ return;
- if (k->load_state != UNIT_LOADED)
- continue;
+ /* Reenable all timers that depend on unit state */
+ LIST_FOREACH(value, v, t->values)
+ if (v->base == TIMER_UNIT_ACTIVE ||
+ v->base == TIMER_UNIT_INACTIVE)
+ v->disabled = false;
- t = TIMER(k);
+ switch (t->state) {
- /* Reenable all timers that depend on unit state */
- LIST_FOREACH(value, v, t->values)
- if (v->base == TIMER_UNIT_ACTIVE ||
- v->base == TIMER_UNIT_INACTIVE)
- v->disabled = false;
+ case TIMER_WAITING:
+ case TIMER_ELAPSED:
- switch (t->state) {
+ /* Recalculate sleep time */
+ timer_enter_waiting(t, false);
+ break;
- case TIMER_WAITING:
- case TIMER_ELAPSED:
+ case TIMER_RUNNING:
- /* Recalculate sleep time */
+ if (UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(other))) {
+ log_debug_unit(UNIT(t)->id,
+ "%s got notified about unit deactivation.",
+ UNIT(t)->id);
timer_enter_waiting(t, false);
- break;
-
- case TIMER_RUNNING:
-
- if (UNIT_IS_INACTIVE_OR_FAILED(new_state)) {
- log_debug_unit(UNIT(t)->id,
- "%s got notified about unit deactivation.",
- UNIT(t)->id);
- timer_enter_waiting(t, false);
- }
-
- break;
+ }
+ break;
- case TIMER_DEAD:
- case TIMER_FAILED:
- break;
+ case TIMER_DEAD:
+ case TIMER_FAILED:
+ break;
- default:
- assert_not_reached("Unknown timer state");
- }
+ default:
+ assert_not_reached("Unknown timer state");
}
}
@@ -548,8 +552,8 @@ static void timer_time_change(Unit *u) {
if (t->state != TIMER_WAITING)
return;
- log_info_unit(u->id,
- "%s: time change, recalculating next elapse.", u->id);
+ log_debug_unit(u->id,
+ "%s: time change, recalculating next elapse.", u->id);
timer_enter_waiting(t, false);
}
@@ -607,6 +611,8 @@ const UnitVTable timer_vtable = {
.timer_event = timer_timer_event,
+ .trigger_notify = timer_trigger_notify,
+
.reset_failed = timer_reset_failed,
.time_change = timer_time_change,
diff --git a/src/core/timer.h b/src/core/timer.h
index 57a514a68c..4168553e9d 100644
--- a/src/core/timer.h
+++ b/src/core/timer.h
@@ -52,8 +52,8 @@ typedef struct TimerValue {
bool disabled;
clockid_t clock_id;
- usec_t value;
- CalendarSpec *calendar_spec;
+ usec_t value; /* only for monotonic events */
+ CalendarSpec *calendar_spec; /* only for calendar events */
usec_t next_elapse;
LIST_FIELDS(struct TimerValue, value);
@@ -74,23 +74,24 @@ struct Timer {
usec_t next_elapse_realtime;
TimerState state, deserialized_state;
- UnitRef unit;
Watch monotonic_watch;
Watch realtime_watch;
TimerResult result;
+
+ usec_t last_trigger_monotonic;
};
-void timer_unit_notify(Unit *u, UnitActiveState new_state);
+void timer_free_values(Timer *t);
extern const UnitVTable timer_vtable;
-const char *timer_state_to_string(TimerState i);
-TimerState timer_state_from_string(const char *s);
+const char *timer_state_to_string(TimerState i) _const_;
+TimerState timer_state_from_string(const char *s) _pure_;
-const char *timer_base_to_string(TimerBase i);
-TimerBase timer_base_from_string(const char *s);
+const char *timer_base_to_string(TimerBase i) _const_;
+TimerBase timer_base_from_string(const char *s) _pure_;
-const char* timer_result_to_string(TimerResult i);
-TimerResult timer_result_from_string(const char *s);
+const char* timer_result_to_string(TimerResult i) _const_;
+TimerResult timer_result_from_string(const char *s) _pure_;
diff --git a/src/core/transaction.c b/src/core/transaction.c
index 1854047afd..fa97b69755 100644
--- a/src/core/transaction.c
+++ b/src/core/transaction.c
@@ -24,6 +24,7 @@
#include "transaction.h"
#include "bus-errors.h"
+#include "dbus-common.h"
static void transaction_unlink_job(Transaction *tr, Job *j, bool delete_dependencies);
@@ -97,6 +98,7 @@ static void transaction_merge_and_delete_job(Transaction *tr, Job *j, Job *other
j->type = t;
j->state = JOB_WAITING;
j->override = j->override || other->override;
+ j->irreversible = j->irreversible || other->irreversible;
j->matters_to_anchor = j->matters_to_anchor || other->matters_to_anchor;
@@ -138,7 +140,7 @@ static void transaction_merge_and_delete_job(Transaction *tr, Job *j, Job *other
transaction_delete_job(tr, other, true);
}
-static bool job_is_conflicted_by(Job *j) {
+_pure_ static bool job_is_conflicted_by(Job *j) {
JobDependency *l;
assert(j);
@@ -318,7 +320,7 @@ rescan:
}
}
-static bool unit_matters_to_anchor(Unit *u, Job *j) {
+_pure_ static bool unit_matters_to_anchor(Unit *u, Job *j) {
assert(u);
assert(!j->transaction_prev);
@@ -395,8 +397,8 @@ static int transaction_verify_order_one(Transaction *tr, Job *j, Job *from, unsi
"Job %s/%s deleted to break ordering cycle starting with %s/%s",
delete->unit->id, job_type_to_string(delete->type),
j->unit->id, job_type_to_string(j->type));
- status_printf(ANSI_HIGHLIGHT_RED_ON " SKIP " ANSI_HIGHLIGHT_OFF, true,
- "Ordering cycle found, skipping %s", unit_description(delete->unit));
+ unit_status_printf(delete->unit, ANSI_HIGHLIGHT_RED_ON " SKIP " ANSI_HIGHLIGHT_OFF,
+ "Ordering cycle found, skipping %s");
transaction_delete_unit(tr, delete->unit);
return -EAGAIN;
}
@@ -488,7 +490,7 @@ rescan:
}
}
-static int transaction_is_destructive(Transaction *tr, DBusError *e) {
+static int transaction_is_destructive(Transaction *tr, JobMode mode, DBusError *e) {
Iterator i;
Job *j;
@@ -503,7 +505,7 @@ static int transaction_is_destructive(Transaction *tr, DBusError *e) {
assert(!j->transaction_prev);
assert(!j->transaction_next);
- if (j->unit->job &&
+ if (j->unit->job && (mode == JOB_FAIL || j->unit->job->irreversible) &&
!job_type_is_superset(j->type, j->unit->job->type)) {
dbus_set_error(e, BUS_ERROR_TRANSACTION_IS_DESTRUCTIVE, "Transaction is destructive.");
@@ -620,6 +622,7 @@ static int transaction_apply(Transaction *tr, Manager *m, JobMode mode) {
job_add_to_run_queue(j);
job_add_to_dbus_queue(j);
job_start_timer(j);
+ job_shutdown_magic(j);
}
return 0;
@@ -708,12 +711,10 @@ int transaction_activate(Transaction *tr, Manager *m, JobMode mode, DBusError *e
transaction_drop_redundant(tr);
/* Ninth step: check whether we can actually apply this */
- if (mode == JOB_FAIL) {
- r = transaction_is_destructive(tr, e);
- if (r < 0) {
- log_notice("Requested transaction contradicts existing jobs: %s", bus_error(e, r));
- return r;
- }
+ r = transaction_is_destructive(tr, mode, e);
+ if (r < 0) {
+ log_notice("Requested transaction contradicts existing jobs: %s", bus_error(e, r));
+ return r;
}
/* Tenth step: apply changes */
@@ -769,6 +770,7 @@ static Job* transaction_add_one_job(Transaction *tr, JobType type, Unit *unit, b
j->marker = NULL;
j->matters_to_anchor = false;
j->override = override;
+ j->irreversible = tr->irreversible;
LIST_PREPEND(Job, transaction, f, j);
@@ -1105,7 +1107,7 @@ int transaction_add_isolate_jobs(Transaction *tr, Manager *m) {
return 0;
}
-Transaction *transaction_new(void) {
+Transaction *transaction_new(bool irreversible) {
Transaction *tr;
tr = new0(Transaction, 1);
@@ -1118,6 +1120,8 @@ Transaction *transaction_new(void) {
return NULL;
}
+ tr->irreversible = irreversible;
+
return tr;
}
diff --git a/src/core/transaction.h b/src/core/transaction.h
index 67ace4da0b..12f9194927 100644
--- a/src/core/transaction.h
+++ b/src/core/transaction.h
@@ -33,9 +33,10 @@ struct Transaction {
/* Jobs to be added */
Hashmap *jobs; /* Unit object => Job object list 1:1 */
Job *anchor_job; /* the job the user asked for */
+ bool irreversible;
};
-Transaction *transaction_new(void);
+Transaction *transaction_new(bool irreversible);
void transaction_free(Transaction *tr);
int transaction_add_job_and_dependencies(
diff --git a/src/core/umount.c b/src/core/umount.c
index 96232d38db..1e95ad70dd 100644
--- a/src/core/umount.c
+++ b/src/core/umount.c
@@ -219,7 +219,8 @@ static int loopback_list_get(MountPoint **head) {
}
if (udev_enumerate_add_match_subsystem(e, "block") < 0 ||
- udev_enumerate_add_match_sysname(e, "loop*") < 0) {
+ udev_enumerate_add_match_sysname(e, "loop*") < 0 ||
+ udev_enumerate_add_match_sysattr(e, "loop/backing_file", NULL) < 0) {
r = -EIO;
goto finish;
}
@@ -378,25 +379,23 @@ static int delete_loopback(const char *device) {
}
static int delete_dm(dev_t devnum) {
- int fd, r;
- struct dm_ioctl dm;
+ _cleanup_close_ int fd = -1;
+ int r;
+ struct dm_ioctl dm = {
+ .version = {DM_VERSION_MAJOR,
+ DM_VERSION_MINOR,
+ DM_VERSION_PATCHLEVEL},
+ .data_size = sizeof(dm),
+ .dev = devnum,
+ };
assert(major(devnum) != 0);
- if ((fd = open("/dev/mapper/control", O_RDWR|O_CLOEXEC)) < 0)
+ fd = open("/dev/mapper/control", O_RDWR|O_CLOEXEC);
+ if (fd < 0)
return -errno;
- zero(dm);
- dm.version[0] = DM_VERSION_MAJOR;
- dm.version[1] = DM_VERSION_MINOR;
- dm.version[2] = DM_VERSION_PATCHLEVEL;
-
- dm.data_size = sizeof(dm);
- dm.dev = devnum;
-
r = ioctl(fd, DM_DEV_REMOVE, &dm);
- close_nointr_nofail(fd);
-
return r >= 0 ? 0 : -errno;
}
@@ -419,7 +418,7 @@ static int mount_points_list_umount(MountPoint **head, bool *changed, bool log_e
*
* Mount points can be stacked. If a mount
* point is stacked below / or /usr, we
- * cannnot umount or remount it directly,
+ * cannot umount or remount it directly,
* since there is no way to refer to the
* underlying mount. There's nothing we can do
* about it for the general case, but we can
@@ -442,9 +441,11 @@ static int mount_points_list_umount(MountPoint **head, bool *changed, bool log_e
)
continue;
- /* Trying to umount. Forcing to umount if busy (only for NFS mounts) */
+ /* Trying to umount. We don't force here since we rely
+ * on busy NFS and FUSE file systems to return EBUSY
+ * until we closed everything on top of them. */
log_info("Unmounting %s.", m->path);
- if (umount2(m->path, MNT_FORCE) == 0) {
+ if (umount2(m->path, 0) == 0) {
if (changed)
*changed = true;
diff --git a/src/core/unit-printf.c b/src/core/unit-printf.c
index a58c96c238..85a05b872a 100644
--- a/src/core/unit-printf.c
+++ b/src/core/unit-printf.c
@@ -26,6 +26,7 @@
#include "strv.h"
#include "unit-name.h"
#include "unit-printf.h"
+#include "macro.h"
static char *specifier_prefix_and_instance(char specifier, void *data, void *userdata) {
Unit *u = userdata;
@@ -123,6 +124,7 @@ static char *specifier_user_name(char specifier, void *data, void *userdata) {
ExecContext *c;
int r;
const char *username;
+ _cleanup_free_ char *tmp = NULL;
uid_t uid;
char *printed = NULL;
@@ -130,12 +132,13 @@ static char *specifier_user_name(char specifier, void *data, void *userdata) {
c = unit_get_exec_context(u);
- /* get USER env from our own env if set */
- if (!c || !c->user)
- return getusername_malloc();
+ if (c && c->user)
+ username = c->user;
+ else
+ /* get USER env from env or our own uid */
+ username = tmp = getusername_malloc();
/* fish username from passwd */
- username = c->user;
r = get_user_creds(&username, &uid, NULL, NULL, NULL);
if (r < 0)
return NULL;
@@ -187,64 +190,37 @@ static char *specifier_user_shell(char specifier, void *data, void *userdata) {
ExecContext *c;
int r;
const char *username, *shell;
+ char *ret;
assert(u);
c = unit_get_exec_context(u);
- /* return HOME if set, otherwise from passwd */
- if (!c || !c->user) {
- char *sh;
-
- r = get_shell(&sh);
- if (r < 0)
- return strdup("/bin/sh");
-
- return sh;
- }
+ if (c && c->user)
+ username = c->user;
+ else
+ username = "root";
- username = c->user;
+ /* return /bin/sh for root, otherwise the value from passwd */
r = get_user_creds(&username, NULL, NULL, NULL, &shell);
- if (r < 0)
- return strdup("/bin/sh");
-
- return strdup(shell);
-}
-
-static char *specifier_machine_id(char specifier, void *data, void *userdata) {
- sd_id128_t id;
- char *buf;
- int r;
-
- r = sd_id128_get_machine(&id);
- if (r < 0)
- return NULL;
-
- buf = new(char, 33);
- if (!buf)
- return NULL;
-
- return sd_id128_to_string(id, buf);
-}
-
-static char *specifier_boot_id(char specifier, void *data, void *userdata) {
- sd_id128_t id;
- char *buf;
- int r;
-
- r = sd_id128_get_boot(&id);
- if (r < 0)
+ if (r < 0) {
+ log_warning_unit(u->id,
+ "Failed to determine shell: %s",
+ strerror(-r));
return NULL;
+ }
- buf = new(char, 33);
- if (!buf)
- return NULL;
+ if (!path_is_absolute(shell)) {
+ log_warning_unit(u->id,
+ "Shell %s is not absolute, ignoring.",
+ shell);
+ }
- return sd_id128_to_string(id, buf);
-}
+ ret = strdup(shell);
+ if (!ret)
+ log_oom();
-static char *specifier_host_name(char specifier, void *data, void *userdata) {
- return gethostname_malloc();
+ return ret;
}
char *unit_name_printf(Unit *u, const char* format) {
@@ -283,12 +259,13 @@ char *unit_full_printf(Unit *u, const char *format) {
* %r root cgroup path of this systemd instance (e.g. "/user/lennart/shared/systemd-4711")
* %R parent of root cgroup path (e.g. "/usr/lennart/shared")
* %t the runtime directory to place sockets in (e.g. "/run" or $XDG_RUNTIME_DIR)
+ * %U the UID of the configured user or running user
* %u the username of the configured user or running user
* %h the homedir of the configured user or running user
* %s the shell of the configured user or running user
* %m the machine ID of the running system
- * %b the boot ID of the running system
* %H the host name of the running system
+ * %b the boot ID of the running system
*/
const Specifier table[] = {
@@ -315,7 +292,6 @@ char *unit_full_printf(Unit *u, const char *format) {
{ 0, NULL, NULL }
};
- assert(u);
assert(format);
return specifier_printf(format, table, u);
diff --git a/src/core/unit.c b/src/core/unit.c
index 45453dce64..9b36b225fa 100644
--- a/src/core/unit.c
+++ b/src/core/unit.c
@@ -45,6 +45,10 @@
#include "cgroup-util.h"
#include "missing.h"
#include "cgroup-attr.h"
+#include "mkdir.h"
+#include "label.h"
+#include "fileio-label.h"
+#include "bus-errors.h"
const UnitVTable * const unit_vtable[_UNIT_TYPE_MAX] = {
[UNIT_SERVICE] = &service_vtable,
@@ -404,6 +408,7 @@ void unit_free(Unit *u) {
strv_free(u->documentation);
free(u->fragment_path);
free(u->source_path);
+ strv_free(u->dropin_paths);
free(u->instance);
set_free_free(u->names);
@@ -613,9 +618,11 @@ int unit_add_exec_dependencies(Unit *u, ExecContext *c) {
/* If syslog or kernel logging is requested, make sure our own
* logging daemon is run first. */
- if (u->manager->running_as == SYSTEMD_SYSTEM)
- if ((r = unit_add_two_dependencies_by_name(u, UNIT_REQUIRES, UNIT_AFTER, SPECIAL_JOURNALD_SOCKET, NULL, true)) < 0)
+ if (u->manager->running_as == SYSTEMD_SYSTEM) {
+ r = unit_add_dependency_by_name(u, UNIT_AFTER, SPECIAL_JOURNALD_SOCKET, NULL, true);
+ if (r < 0)
return r;
+ }
return 0;
}
@@ -690,8 +697,11 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
if (u->source_path)
fprintf(f, "%s\tSource Path: %s\n", prefix, u->source_path);
+ STRV_FOREACH(j, u->dropin_paths)
+ fprintf(f, "%s\tDropIn Path: %s\n", prefix, *j);
+
if (u->job_timeout > 0)
- fprintf(f, "%s\tJob Timeout: %s\n", prefix, format_timespan(timespan, sizeof(timespan), u->job_timeout));
+ fprintf(f, "%s\tJob Timeout: %s\n", prefix, format_timespan(timespan, sizeof(timespan), u->job_timeout, 0));
condition_dump_list(u->conditions, f, prefix);
@@ -744,15 +754,13 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
prefix, b->controller, b->path);
LIST_FOREACH(by_unit, a, u->cgroup_attributes) {
- char *v = NULL;
+ _cleanup_free_ char *v = NULL;
- if (a->map_callback)
- a->map_callback(a->controller, a->name, a->value, &v);
+ if (a->semantics && a->semantics->map_write)
+ a->semantics->map_write(a->semantics, a->value, &v);
fprintf(f, "%s\tControlGroupAttribute: %s %s \"%s\"\n",
prefix, a->controller, a->name, v ? v : a->value);
-
- free(v);
}
if (UNIT_VTABLE(u)->dump)
@@ -906,8 +914,8 @@ int unit_load(Unit *u) {
if (u->on_failure_isolate &&
set_size(u->dependencies[UNIT_ON_FAILURE]) > 1) {
- log_error("More than one OnFailure= dependencies specified for %s but OnFailureIsolate= enabled. Refusing.",
- u->id);
+ log_error_unit(u->id,
+ "More than one OnFailure= dependencies specified for %s but OnFailureIsolate= enabled. Refusing.", u->id);
r = -EINVAL;
goto fail;
@@ -926,7 +934,8 @@ fail:
unit_add_to_dbus_queue(u);
unit_add_to_gc_queue(u);
- log_debug("Failed to load configuration for %s: %s", u->id, strerror(-r));
+ log_debug_unit(u->id, "Failed to load configuration for %s: %s",
+ u->id, strerror(-r));
return r;
}
@@ -940,7 +949,7 @@ bool unit_condition_test(Unit *u) {
return u->condition_result;
}
-static const char* unit_get_status_message_format(Unit *u, JobType t) {
+_pure_ static const char* unit_get_status_message_format(Unit *u, JobType t) {
const UnitStatusMessageFormats *format_table;
assert(u);
@@ -957,7 +966,7 @@ static const char* unit_get_status_message_format(Unit *u, JobType t) {
return format_table->starting_stopping[t == JOB_STOP];
}
-static const char *unit_get_status_message_format_try_harder(Unit *u, JobType t) {
+_pure_ static const char *unit_get_status_message_format_try_harder(Unit *u, JobType t) {
const char *format;
assert(u);
@@ -991,7 +1000,7 @@ static void unit_status_print_starting_stopping(Unit *u, JobType t) {
if (!format)
return;
- unit_status_printf(u, "", format, unit_description(u));
+ unit_status_printf(u, "", format);
}
#pragma GCC diagnostic push
@@ -1022,11 +1031,11 @@ static void unit_status_log_starting_stopping_reloading(Unit *u, JobType t) {
t == JOB_STOP ? SD_MESSAGE_UNIT_STOPPING :
SD_MESSAGE_UNIT_RELOADING;
- log_struct(LOG_INFO,
- MESSAGE_ID(mid),
- "UNIT=%s", u->id,
- "MESSAGE=%s", buf,
- NULL);
+ log_struct_unit(LOG_INFO,
+ u->id,
+ MESSAGE_ID(mid),
+ "MESSAGE=%s", buf,
+ NULL);
}
#pragma GCC diagnostic pop
@@ -1059,13 +1068,14 @@ int unit_start(Unit *u) {
* but we don't want to recheck the condition in that case. */
if (state != UNIT_ACTIVATING &&
!unit_condition_test(u)) {
- log_debug("Starting of %s requested but condition failed. Ignoring.", u->id);
+ log_debug_unit(u->id, "Starting of %s requested but condition failed. Ignoring.", u->id);
return -EALREADY;
}
/* Forward to the main object, if we aren't it. */
if ((following = unit_following(u))) {
- log_debug("Redirecting start request from %s to %s.", u->id, following->id);
+ log_debug_unit(u->id, "Redirecting start request from %s to %s.",
+ u->id, following->id);
return unit_start(following);
}
@@ -1116,7 +1126,8 @@ int unit_stop(Unit *u) {
return -EALREADY;
if ((following = unit_following(u))) {
- log_debug("Redirecting stop request from %s to %s.", u->id, following->id);
+ log_debug_unit(u->id, "Redirecting stop request from %s to %s.",
+ u->id, following->id);
return unit_stop(following);
}
@@ -1156,7 +1167,8 @@ int unit_reload(Unit *u) {
return -ENOEXEC;
if ((following = unit_following(u))) {
- log_debug("Redirecting reload request from %s to %s.", u->id, following->id);
+ log_debug_unit(u->id, "Redirecting reload request from %s to %s.",
+ u->id, following->id);
return unit_reload(following);
}
@@ -1194,22 +1206,22 @@ static void unit_check_unneeded(Unit *u) {
return;
SET_FOREACH(other, u->dependencies[UNIT_REQUIRED_BY], i)
- if (unit_pending_active(other))
+ if (unit_active_or_pending(other))
return;
SET_FOREACH(other, u->dependencies[UNIT_REQUIRED_BY_OVERRIDABLE], i)
- if (unit_pending_active(other))
+ if (unit_active_or_pending(other))
return;
SET_FOREACH(other, u->dependencies[UNIT_WANTED_BY], i)
- if (unit_pending_active(other))
+ if (unit_active_or_pending(other))
return;
SET_FOREACH(other, u->dependencies[UNIT_BOUND_BY], i)
- if (unit_pending_active(other))
+ if (unit_active_or_pending(other))
return;
- log_info("Service %s is not needed anymore. Stopping.", u->id);
+ log_info_unit(u->id, "Service %s is not needed anymore. Stopping.", u->id);
/* Ok, nobody needs us anymore. Sniff. Then let's commit suicide */
manager_add_job(u->manager, JOB_STOP, u, JOB_FAIL, true, NULL, NULL);
@@ -1237,11 +1249,6 @@ static void retroactively_start_dependencies(Unit *u) {
!UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other)))
manager_add_job(u->manager, JOB_START, other, JOB_FAIL, false, NULL, NULL);
- SET_FOREACH(other, u->dependencies[UNIT_REQUISITE], i)
- if (!set_get(u->dependencies[UNIT_AFTER], other) &&
- !UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other)))
- manager_add_job(u->manager, JOB_START, other, JOB_REPLACE, true, NULL, NULL);
-
SET_FOREACH(other, u->dependencies[UNIT_WANTS], i)
if (!set_get(u->dependencies[UNIT_AFTER], other) &&
!UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other)))
@@ -1297,7 +1304,7 @@ static void check_unneeded_dependencies(Unit *u) {
unit_check_unneeded(other);
}
-void unit_trigger_on_failure(Unit *u) {
+void unit_start_on_failure(Unit *u) {
Unit *other;
Iterator i;
@@ -1306,17 +1313,30 @@ void unit_trigger_on_failure(Unit *u) {
if (set_size(u->dependencies[UNIT_ON_FAILURE]) <= 0)
return;
- log_info("Triggering OnFailure= dependencies of %s.", u->id);
+ log_info_unit(u->id, "Triggering OnFailure= dependencies of %s.", u->id);
SET_FOREACH(other, u->dependencies[UNIT_ON_FAILURE], i) {
int r;
- if ((r = manager_add_job(u->manager, JOB_START, other, u->on_failure_isolate ? JOB_ISOLATE : JOB_REPLACE, true, NULL, NULL)) < 0)
- log_error("Failed to enqueue OnFailure= job: %s", strerror(-r));
+ r = manager_add_job(u->manager, JOB_START, other, u->on_failure_isolate ? JOB_ISOLATE : JOB_REPLACE, true, NULL, NULL);
+ if (r < 0)
+ log_error_unit(u->id, "Failed to enqueue OnFailure= job: %s", strerror(-r));
}
}
+void unit_trigger_notify(Unit *u) {
+ Unit *other;
+ Iterator i;
+
+ assert(u);
+
+ SET_FOREACH(other, u->dependencies[UNIT_TRIGGERED_BY], i)
+ if (UNIT_VTABLE(other)->trigger_notify)
+ UNIT_VTABLE(other)->trigger_notify(other, u);
+}
+
void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_success) {
+ Manager *m;
bool unexpected;
assert(u);
@@ -1329,7 +1349,9 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su
* behavior here. For example: if a mount point is remounted
* this function will be called too! */
- if (u->manager->n_reloading <= 0) {
+ m = u->manager;
+
+ if (m->n_reloading <= 0) {
dual_timestamp ts;
dual_timestamp_get(&ts);
@@ -1343,14 +1365,21 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su
u->active_enter_timestamp = ts;
else if (UNIT_IS_ACTIVE_OR_RELOADING(os) && !UNIT_IS_ACTIVE_OR_RELOADING(ns))
u->active_exit_timestamp = ts;
-
- timer_unit_notify(u, ns);
- path_unit_notify(u, ns);
}
if (UNIT_IS_INACTIVE_OR_FAILED(ns))
cgroup_bonding_trim_list(u->cgroup_bondings, true);
+ if (UNIT_IS_INACTIVE_OR_FAILED(os) != UNIT_IS_INACTIVE_OR_FAILED(ns)) {
+ ExecContext *ec = unit_get_exec_context(u);
+ if (ec && exec_context_may_touch_console(ec)) {
+ if (UNIT_IS_INACTIVE_OR_FAILED(ns))
+ m->n_on_console--;
+ else
+ m->n_on_console++;
+ }
+ }
+
if (u->job) {
unexpected = false;
@@ -1417,7 +1446,7 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su
} else
unexpected = true;
- if (u->manager->n_reloading <= 0) {
+ if (m->n_reloading <= 0) {
/* If this state change happened without being
* requested by a job, then let's retroactively start
@@ -1438,11 +1467,9 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su
check_unneeded_dependencies(u);
if (ns != os && ns == UNIT_FAILED) {
- log_struct(LOG_NOTICE,
- "MESSAGE=Unit %s entered failed state", u->id,
- "UNIT=%s", u->id,
- NULL);
- unit_trigger_on_failure(u);
+ log_notice_unit(u->id,
+ "Unit %s entered failed state.", u->id);
+ unit_start_on_failure(u);
}
}
@@ -1453,18 +1480,18 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su
/* The bus just might have become available,
* hence try to connect to it, if we aren't
* yet connected. */
- bus_init(u->manager, true);
+ bus_init(m, true);
if (u->type == UNIT_SERVICE &&
!UNIT_IS_ACTIVE_OR_RELOADING(os) &&
- u->manager->n_reloading <= 0) {
+ m->n_reloading <= 0) {
/* Write audit record if we have just finished starting up */
- manager_send_unit_audit(u->manager, u, AUDIT_SERVICE_START, true);
+ manager_send_unit_audit(m, u, AUDIT_SERVICE_START, true);
u->in_audit = true;
}
if (!UNIT_IS_ACTIVE_OR_RELOADING(os))
- manager_send_unit_plymouth(u->manager, u);
+ manager_send_unit_plymouth(m, u);
} else {
@@ -1474,46 +1501,47 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su
if (u->type == UNIT_SERVICE &&
UNIT_IS_INACTIVE_OR_FAILED(ns) &&
!UNIT_IS_INACTIVE_OR_FAILED(os) &&
- u->manager->n_reloading <= 0) {
+ m->n_reloading <= 0) {
/* Hmm, if there was no start record written
* write it now, so that we always have a nice
* pair */
if (!u->in_audit) {
- manager_send_unit_audit(u->manager, u, AUDIT_SERVICE_START, ns == UNIT_INACTIVE);
+ manager_send_unit_audit(m, u, AUDIT_SERVICE_START, ns == UNIT_INACTIVE);
if (ns == UNIT_INACTIVE)
- manager_send_unit_audit(u->manager, u, AUDIT_SERVICE_STOP, true);
+ manager_send_unit_audit(m, u, AUDIT_SERVICE_STOP, true);
} else
/* Write audit record if we have just finished shutting down */
- manager_send_unit_audit(u->manager, u, AUDIT_SERVICE_STOP, ns == UNIT_INACTIVE);
+ manager_send_unit_audit(m, u, AUDIT_SERVICE_STOP, ns == UNIT_INACTIVE);
u->in_audit = false;
}
}
- manager_recheck_journal(u->manager);
+ manager_recheck_journal(m);
+ unit_trigger_notify(u);
/* Maybe we finished startup and are now ready for being
* stopped because unneeded? */
- unit_check_unneeded(u);
+ if (u->manager->n_reloading <= 0)
+ unit_check_unneeded(u);
unit_add_to_dbus_queue(u);
unit_add_to_gc_queue(u);
}
int unit_watch_fd(Unit *u, int fd, uint32_t events, Watch *w) {
- struct epoll_event ev;
+ struct epoll_event ev = {
+ .data.ptr = w,
+ .events = events,
+ };
assert(u);
assert(fd >= 0);
assert(w);
assert(w->type == WATCH_INVALID || (w->type == WATCH_FD && w->fd == fd && w->data.unit == u));
- zero(ev);
- ev.data.ptr = w;
- ev.events = events;
-
if (epoll_ctl(u->manager->epoll_fd,
w->type == WATCH_INVALID ? EPOLL_CTL_ADD : EPOLL_CTL_MOD,
fd,
@@ -1561,7 +1589,7 @@ void unit_unwatch_pid(Unit *u, pid_t pid) {
}
int unit_watch_timer(Unit *u, clockid_t clock_id, bool relative, usec_t usec, Watch *w) {
- struct itimerspec its;
+ struct itimerspec its = {};
int flags, fd;
bool ours;
@@ -1586,8 +1614,6 @@ int unit_watch_timer(Unit *u, clockid_t clock_id, bool relative, usec_t usec, Wa
} else
assert_not_reached("Invalid watch type");
- zero(its);
-
if (usec <= 0) {
/* Set absolute time in the past, but not 0, since we
* don't want to disarm the timer */
@@ -1605,11 +1631,10 @@ int unit_watch_timer(Unit *u, clockid_t clock_id, bool relative, usec_t usec, Wa
goto fail;
if (w->type == WATCH_INVALID) {
- struct epoll_event ev;
-
- zero(ev);
- ev.data.ptr = w;
- ev.events = EPOLLIN;
+ struct epoll_event ev = {
+ .data.ptr = w,
+ .events = EPOLLIN,
+ };
if (epoll_ctl(u->manager->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0)
goto fail;
@@ -1781,6 +1806,7 @@ static const char *resolve_template(Unit *u, const char *name, const char*path,
assert(u);
assert(name || path);
+ assert(p);
if (!name)
name = path_get_file_name(path);
@@ -1793,13 +1819,13 @@ static const char *resolve_template(Unit *u, const char *name, const char*path,
if (u->instance)
s = unit_name_replace_instance(name, u->instance);
else {
- char *i;
+ _cleanup_free_ char *i = NULL;
- if (!(i = unit_name_to_prefix(u->id)))
+ i = unit_name_to_prefix(u->id);
+ if (!i)
return NULL;
s = unit_name_replace_instance(name, i);
- free(i);
}
if (!s)
@@ -1812,22 +1838,20 @@ static const char *resolve_template(Unit *u, const char *name, const char*path,
int unit_add_dependency_by_name(Unit *u, UnitDependency d, const char *name, const char *path, bool add_reference) {
Unit *other;
int r;
- char *s;
+ _cleanup_free_ char *s = NULL;
assert(u);
assert(name || path);
- if (!(name = resolve_template(u, name, path, &s)))
+ name = resolve_template(u, name, path, &s);
+ if (!name)
return -ENOMEM;
- if ((r = manager_load_unit(u->manager, name, path, NULL, &other)) < 0)
- goto finish;
-
- r = unit_add_dependency(u, d, other, add_reference);
+ r = manager_load_unit(u->manager, name, path, NULL, &other);
+ if (r < 0)
+ return r;
-finish:
- free(s);
- return r;
+ return unit_add_dependency(u, d, other, add_reference);
}
int unit_add_two_dependencies_by_name(Unit *u, UnitDependency d, UnitDependency e, const char *name, const char *path, bool add_reference) {
@@ -1895,30 +1919,12 @@ finish:
}
int set_unit_path(const char *p) {
- char *cwd, *c;
- int r;
+ _cleanup_free_ char *c = NULL;
/* This is mostly for debug purposes */
-
- if (path_is_absolute(p)) {
- if (!(c = strdup(p)))
- return -ENOMEM;
- } else {
- if (!(cwd = get_current_dir_name()))
- return -errno;
-
- r = asprintf(&c, "%s/%s", cwd, p);
- free(cwd);
-
- if (r < 0)
- return -ENOMEM;
- }
-
- if (setenv("SYSTEMD_UNIT_PATH", c, 0) < 0) {
- r = -errno;
- free(c);
- return r;
- }
+ c = path_make_absolute_cwd(p);
+ if (setenv("SYSTEMD_UNIT_PATH", c, 0) < 0)
+ return -errno;
return 0;
}
@@ -1932,7 +1938,7 @@ char *unit_dbus_path(Unit *u) {
return unit_dbus_path_from_name(u->id);
}
-int unit_add_cgroup(Unit *u, CGroupBonding *b) {
+static int unit_add_cgroup(Unit *u, CGroupBonding *b) {
int r;
assert(u);
@@ -1941,8 +1947,9 @@ int unit_add_cgroup(Unit *u, CGroupBonding *b) {
assert(b->path);
if (!b->controller) {
- if (!(b->controller = strdup(SYSTEMD_CGROUP_CONTROLLER)))
- return -ENOMEM;
+ b->controller = strdup(SYSTEMD_CGROUP_CONTROLLER);
+ if (!b->controller)
+ return log_oom();
b->ours = true;
}
@@ -1956,7 +1963,8 @@ int unit_add_cgroup(Unit *u, CGroupBonding *b) {
l = hashmap_get(u->manager->cgroup_bondings, b->path);
LIST_PREPEND(CGroupBonding, by_path, l, b);
- if ((r = hashmap_replace(u->manager->cgroup_bondings, b->path, l)) < 0) {
+ r = hashmap_replace(u->manager->cgroup_bondings, b->path, l);
+ if (r < 0) {
LIST_REMOVE(CGroupBonding, by_path, l, b);
return r;
}
@@ -1969,26 +1977,31 @@ int unit_add_cgroup(Unit *u, CGroupBonding *b) {
}
char *unit_default_cgroup_path(Unit *u) {
- char *p;
+ _cleanup_free_ char *escaped_instance = NULL;
assert(u);
+ escaped_instance = cg_escape(u->id);
+ if (!escaped_instance)
+ return NULL;
+
if (u->instance) {
- char *t;
+ _cleanup_free_ char *t = NULL, *escaped_template = NULL;
t = unit_name_template(u->id);
if (!t)
return NULL;
- p = strjoin(u->manager->cgroup_hierarchy, "/", t, "/", u->instance, NULL);
- free(t);
- } else
- p = strjoin(u->manager->cgroup_hierarchy, "/", u->id, NULL);
+ escaped_template = cg_escape(t);
+ if (!escaped_template)
+ return NULL;
- return p;
+ return strjoin(u->manager->cgroup_hierarchy, "/", escaped_template, "/", escaped_instance, NULL);
+ } else
+ return strjoin(u->manager->cgroup_hierarchy, "/", escaped_instance, NULL);
}
-int unit_add_cgroup_from_text(Unit *u, const char *name) {
+int unit_add_cgroup_from_text(Unit *u, const char *name, bool overwrite, CGroupBonding **ret) {
char *controller = NULL, *path = NULL;
CGroupBonding *b = NULL;
bool ours = false;
@@ -1997,7 +2010,8 @@ int unit_add_cgroup_from_text(Unit *u, const char *name) {
assert(u);
assert(name);
- if ((r = cg_split_spec(name, &controller, &path)) < 0)
+ r = cg_split_spec(name, &controller, &path);
+ if (r < 0)
return r;
if (!path) {
@@ -2006,23 +2020,59 @@ int unit_add_cgroup_from_text(Unit *u, const char *name) {
}
if (!controller) {
- controller = strdup(SYSTEMD_CGROUP_CONTROLLER);
+ controller = strdup("systemd");
ours = true;
}
if (!path || !controller) {
free(path);
free(controller);
+ return log_oom();
+ }
- return -ENOMEM;
+ if (streq(controller, "systemd")) {
+ /* Within the systemd unit hierarchy we do not allow changes. */
+ if (path_startswith(path, "/system")) {
+ log_warning_unit(u->id, "Manipulating the systemd:/system cgroup hierarchy is not permitted.");
+ free(path);
+ free(controller);
+ return -EPERM;
+ }
}
- if (cgroup_bonding_find_list(u->cgroup_bondings, controller)) {
+ b = cgroup_bonding_find_list(u->cgroup_bondings, controller);
+ if (b) {
+ if (streq(path, b->path)) {
+ free(path);
+ free(controller);
+
+ if (ret)
+ *ret = b;
+ return 0;
+ }
+
+ if (overwrite && !b->essential) {
+ free(controller);
+
+ free(b->path);
+ b->path = path;
+
+ b->ours = ours;
+ b->realized = false;
+
+ if (ret)
+ *ret = b;
+
+ return 1;
+ }
+
r = -EEXIST;
+ b = NULL;
goto fail;
}
- if (!(b = new0(CGroupBonding, 1))) {
+ b = new0(CGroupBonding, 1);
+ if (!b) {
r = -ENOMEM;
goto fail;
}
@@ -2032,10 +2082,14 @@ int unit_add_cgroup_from_text(Unit *u, const char *name) {
b->ours = ours;
b->essential = streq(controller, SYSTEMD_CGROUP_CONTROLLER);
- if ((r = unit_add_cgroup(u, b)) < 0)
+ r = unit_add_cgroup(u, b);
+ if (r < 0)
goto fail;
- return 0;
+ if (ret)
+ *ret = b;
+
+ return 1;
fail:
free(path);
@@ -2051,16 +2105,21 @@ static int unit_add_one_default_cgroup(Unit *u, const char *controller) {
assert(u);
+ if (controller && !cg_controller_is_valid(controller, true))
+ return -EINVAL;
+
if (!controller)
controller = SYSTEMD_CGROUP_CONTROLLER;
if (cgroup_bonding_find_list(u->cgroup_bondings, controller))
return 0;
- if (!(b = new0(CGroupBonding, 1)))
+ b = new0(CGroupBonding, 1);
+ if (!b)
return -ENOMEM;
- if (!(b->controller = strdup(controller)))
+ b->controller = strdup(controller);
+ if (!b->controller)
goto fail;
b->path = unit_default_cgroup_path(u);
@@ -2070,10 +2129,11 @@ static int unit_add_one_default_cgroup(Unit *u, const char *controller) {
b->ours = true;
b->essential = streq(controller, SYSTEMD_CGROUP_CONTROLLER);
- if ((r = unit_add_cgroup(u, b)) < 0)
+ r = unit_add_cgroup(u, b);
+ if (r < 0)
goto fail;
- return 0;
+ return 1;
fail:
free(b->path);
@@ -2096,7 +2156,8 @@ int unit_add_default_cgroups(Unit *u) {
if (!u->manager->cgroup_hierarchy)
return 0;
- if ((r = unit_add_one_default_cgroup(u, NULL)) < 0)
+ r = unit_add_one_default_cgroup(u, NULL);
+ if (r < 0)
return r;
STRV_FOREACH(c, u->manager->default_controllers)
@@ -2111,42 +2172,90 @@ int unit_add_default_cgroups(Unit *u) {
CGroupBonding* unit_get_default_cgroup(Unit *u) {
assert(u);
- return cgroup_bonding_find_list(u->cgroup_bondings, SYSTEMD_CGROUP_CONTROLLER);
+ return cgroup_bonding_find_list(u->cgroup_bondings, NULL);
}
-int unit_add_cgroup_attribute(Unit *u, const char *controller, const char *name, const char *value, CGroupAttributeMapCallback map_callback) {
- int r;
- char *c = NULL;
+int unit_add_cgroup_attribute(
+ Unit *u,
+ const CGroupSemantics *semantics,
+ const char *controller,
+ const char *name,
+ const char *value,
+ CGroupAttribute **ret) {
+
+ _cleanup_free_ char *c = NULL;
CGroupAttribute *a;
+ int r;
assert(u);
- assert(name);
assert(value);
- if (!controller) {
- const char *dot;
+ if (semantics) {
+ /* Semantics always take precedence */
+ if (semantics->name)
+ name = semantics->name;
- dot = strchr(name, '.');
- if (!dot)
- return -EINVAL;
+ if (semantics->controller)
+ controller = semantics->controller;
+ }
- c = strndup(name, dot - name);
- if (!c)
- return -ENOMEM;
+ if (!name)
+ return -EINVAL;
+
+ if (!controller) {
+ r = cg_controller_from_attr(name, &c);
+ if (r < 0)
+ return -EINVAL;
controller = c;
}
- if (streq(controller, SYSTEMD_CGROUP_CONTROLLER)) {
- r = -EINVAL;
- goto finish;
+ if (!controller ||
+ streq(controller, SYSTEMD_CGROUP_CONTROLLER) ||
+ streq(controller, "systemd"))
+ return -EINVAL;
+
+ if (!filename_is_safe(name))
+ return -EINVAL;
+
+ if (!cg_controller_is_valid(controller, false))
+ return -EINVAL;
+
+ /* Check if this attribute already exists. Note that we will
+ * explicitly check for the value here too, as there are
+ * attributes which accept multiple values. */
+ a = cgroup_attribute_find_list(u->cgroup_attributes, controller, name);
+ if (a) {
+ if (streq(value, a->value)) {
+ /* Exactly the same value is always OK, let's ignore this */
+ if (ret)
+ *ret = a;
+
+ return 0;
+ }
+
+ if (semantics && !semantics->multiple) {
+ char *v;
+
+ /* If this is a single-item entry, we can
+ * simply patch the existing attribute */
+
+ v = strdup(value);
+ if (!v)
+ return -ENOMEM;
+
+ free(a->value);
+ a->value = v;
+
+ if (ret)
+ *ret = a;
+ return 1;
+ }
}
a = new0(CGroupAttribute, 1);
- if (!a) {
- r = -ENOMEM;
- goto finish;
- }
+ if (!a)
+ return -ENOMEM;
if (c) {
a->controller = c;
@@ -2162,58 +2271,54 @@ int unit_add_cgroup_attribute(Unit *u, const char *controller, const char *name,
free(a->name);
free(a->value);
free(a);
-
return -ENOMEM;
}
- a->map_callback = map_callback;
+ a->semantics = semantics;
+ a->unit = u;
LIST_PREPEND(CGroupAttribute, by_unit, u->cgroup_attributes, a);
- r = 0;
+ if (ret)
+ *ret = a;
-finish:
- free(c);
- return r;
+ return 1;
}
int unit_load_related_unit(Unit *u, const char *type, Unit **_found) {
- char *t;
+ _cleanup_free_ char *t = NULL;
int r;
assert(u);
assert(type);
assert(_found);
- if (!(t = unit_name_change_suffix(u->id, type)))
+ t = unit_name_change_suffix(u->id, type);
+ if (!t)
return -ENOMEM;
assert(!unit_has_name(u, t));
r = manager_load_unit(u->manager, t, NULL, NULL, _found);
- free(t);
-
assert(r < 0 || *_found != u);
-
return r;
}
int unit_get_related_unit(Unit *u, const char *type, Unit **_found) {
+ _cleanup_free_ char *t = NULL;
Unit *found;
- char *t;
assert(u);
assert(type);
assert(_found);
- if (!(t = unit_name_change_suffix(u->id, type)))
+ t = unit_name_change_suffix(u->id, type);
+ if (!t)
return -ENOMEM;
assert(!unit_has_name(u, t));
found = manager_get_unit(u->manager, t);
- free(t);
-
if (!found)
return -ENOENT;
@@ -2371,6 +2476,9 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) {
job_free(j);
return r;
}
+
+ if (j->state == JOB_RUNNING)
+ u->manager->n_running_jobs++;
} else {
/* legacy */
JobType type = job_type_from_string(v);
@@ -2473,25 +2581,18 @@ int unit_coldplug(Unit *u) {
return 0;
}
-void unit_status_printf(Unit *u, const char *status, const char *format, ...) {
- va_list ap;
-
- assert(u);
- assert(format);
-
- if (!manager_get_show_status(u->manager))
- return;
-
- if (!manager_is_booting_or_shutting_down(u->manager))
- return;
-
- va_start(ap, format);
- status_vprintf(status, true, format, ap);
- va_end(ap);
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wformat-nonliteral"
+void unit_status_printf(Unit *u, const char *status, const char *unit_status_msg_format) {
+ manager_status_printf(u->manager, false, status, unit_status_msg_format, unit_description(u));
}
+#pragma GCC diagnostic pop
bool unit_need_daemon_reload(Unit *u) {
+ _cleanup_strv_free_ char **t = NULL;
+ char **path;
struct stat st;
+ unsigned loaded_cnt, current_cnt;
assert(u);
@@ -2516,7 +2617,30 @@ bool unit_need_daemon_reload(Unit *u) {
return true;
}
- return false;
+ t = unit_find_dropin_paths(u);
+ loaded_cnt = strv_length(t);
+ current_cnt = strv_length(u->dropin_paths);
+
+ if (loaded_cnt == current_cnt) {
+ if (loaded_cnt == 0)
+ return false;
+
+ if (strv_overlap(u->dropin_paths, t)) {
+ STRV_FOREACH(path, u->dropin_paths) {
+ zero(st);
+ if (stat(*path, &st) < 0)
+ return true;
+
+ if (u->dropin_mtime > 0 &&
+ timespec_load(&st.st_mtim) > u->dropin_mtime)
+ return true;
+ }
+
+ return false;
+ } else
+ return true;
+ } else
+ return true;
}
void unit_reset_failed(Unit *u) {
@@ -2535,7 +2659,19 @@ Unit *unit_following(Unit *u) {
return NULL;
}
-bool unit_pending_inactive(Unit *u) {
+bool unit_stop_pending(Unit *u) {
+ assert(u);
+
+ /* This call does check the current state of the unit. It's
+ * hence useful to be called from state change calls of the
+ * unit itself, where the state isn't updated yet. This is
+ * different from unit_inactive_or_pending() which checks both
+ * the current state and for a queued job. */
+
+ return u->job && u->job->type == JOB_STOP;
+}
+
+bool unit_inactive_or_pending(Unit *u) {
assert(u);
/* Returns true if the unit is inactive or going down */
@@ -2543,13 +2679,13 @@ bool unit_pending_inactive(Unit *u) {
if (UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(u)))
return true;
- if (u->job && u->job->type == JOB_STOP)
+ if (unit_stop_pending(u))
return true;
return false;
}
-bool unit_pending_active(Unit *u) {
+bool unit_active_or_pending(Unit *u) {
assert(u);
/* Returns true if the unit is active or going up */
@@ -2578,6 +2714,71 @@ int unit_kill(Unit *u, KillWho w, int signo, DBusError *error) {
return UNIT_VTABLE(u)->kill(u, w, signo, error);
}
+int unit_kill_common(
+ Unit *u,
+ KillWho who,
+ int signo,
+ pid_t main_pid,
+ pid_t control_pid,
+ DBusError *error) {
+
+ int r = 0;
+
+ if (who == KILL_MAIN && main_pid <= 0) {
+ if (main_pid < 0)
+ dbus_set_error(error, BUS_ERROR_NO_SUCH_PROCESS, "%s units have no main processes", unit_type_to_string(u->type));
+ else
+ dbus_set_error(error, BUS_ERROR_NO_SUCH_PROCESS, "No main process to kill");
+ return -ESRCH;
+ }
+
+ if (who == KILL_CONTROL && control_pid <= 0) {
+ if (control_pid < 0)
+ dbus_set_error(error, BUS_ERROR_NO_SUCH_PROCESS, "%s units have no control processes", unit_type_to_string(u->type));
+ else
+ dbus_set_error(error, BUS_ERROR_NO_SUCH_PROCESS, "No control process to kill");
+ return -ESRCH;
+ }
+
+ if (who == KILL_CONTROL || who == KILL_ALL)
+ if (control_pid > 0)
+ if (kill(control_pid, signo) < 0)
+ r = -errno;
+
+ if (who == KILL_MAIN || who == KILL_ALL)
+ if (main_pid > 0)
+ if (kill(main_pid, signo) < 0)
+ r = -errno;
+
+ if (who == KILL_ALL) {
+ _cleanup_set_free_ Set *pid_set = NULL;
+ int q;
+
+ pid_set = set_new(trivial_hash_func, trivial_compare_func);
+ if (!pid_set)
+ return -ENOMEM;
+
+ /* Exclude the control/main pid from being killed via the cgroup */
+ if (control_pid > 0) {
+ q = set_put(pid_set, LONG_TO_PTR(control_pid));
+ if (q < 0)
+ return q;
+ }
+
+ if (main_pid > 0) {
+ q = set_put(pid_set, LONG_TO_PTR(main_pid));
+ if (q < 0)
+ return q;
+ }
+
+ q = cgroup_bonding_kill_list(u->cgroup_bondings, signo, false, false, pid_set, NULL);
+ if (q < 0 && q != -EAGAIN && q != -ESRCH && q != -ENOENT)
+ r = q;
+ }
+
+ return r;
+}
+
int unit_following_set(Unit *u, Set **s) {
assert(u);
assert(s);
@@ -2699,6 +2900,155 @@ ExecContext *unit_get_exec_context(Unit *u) {
return (ExecContext*) ((uint8_t*) u + offset);
}
+static int drop_in_file(Unit *u, bool runtime, const char *name, char **_p, char **_q) {
+ char *p, *q;
+ int r;
+
+ assert(u);
+ assert(name);
+ assert(_p);
+ assert(_q);
+
+ if (u->manager->running_as == SYSTEMD_USER && runtime)
+ return -ENOTSUP;
+
+ if (!filename_is_safe(name))
+ return -EINVAL;
+
+ if (u->manager->running_as == SYSTEMD_USER) {
+ _cleanup_free_ char *c = NULL;
+
+ r = user_config_home(&c);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -ENOENT;
+
+ p = strjoin(c, "/", u->id, ".d", NULL);
+ } else if (runtime)
+ p = strjoin("/run/systemd/system/", u->id, ".d", NULL);
+ else
+ p = strjoin("/etc/systemd/system/", u->id, ".d", NULL);
+ if (!p)
+ return -ENOMEM;
+
+ q = strjoin(p, "/50-", name, ".conf", NULL);
+ if (!q) {
+ free(p);
+ return -ENOMEM;
+ }
+
+ *_p = p;
+ *_q = q;
+ return 0;
+}
+
+int unit_write_drop_in(Unit *u, bool runtime, const char *name, const char *data) {
+ _cleanup_free_ char *p = NULL, *q = NULL;
+ int r;
+
+ assert(u);
+
+ r = drop_in_file(u, runtime, name, &p, &q);
+ if (r < 0)
+ return r;
+
+ mkdir_p(p, 0755);
+ return write_string_file_atomic_label(q, data);
+}
+
+int unit_remove_drop_in(Unit *u, bool runtime, const char *name) {
+ _cleanup_free_ char *p = NULL, *q = NULL;
+ int r;
+
+ assert(u);
+
+ r = drop_in_file(u, runtime, name, &p, &q);
+ if (unlink(q) < 0)
+ r = -errno;
+ else
+ r = 0;
+
+ rmdir(p);
+ return r;
+}
+
+int unit_kill_context(
+ Unit *u,
+ KillContext *c,
+ bool sigkill,
+ pid_t main_pid,
+ pid_t control_pid,
+ bool main_pid_alien) {
+
+ int sig, wait_for_exit = 0, r;
+
+ assert(u);
+ assert(c);
+
+ if (c->kill_mode == KILL_NONE)
+ return 0;
+
+ sig = sigkill ? SIGKILL : c->kill_signal;
+
+ if (main_pid > 0) {
+ r = kill_and_sigcont(main_pid, sig);
+
+ if (r < 0 && r != -ESRCH) {
+ _cleanup_free_ char *comm = NULL;
+ get_process_comm(main_pid, &comm);
+
+ log_warning_unit(u->id, "Failed to kill main process %li (%s): %s",
+ (long) main_pid, strna(comm), strerror(-r));
+ } else
+ wait_for_exit = !main_pid_alien;
+ }
+
+ if (control_pid > 0) {
+ r = kill_and_sigcont(control_pid, sig);
+
+ if (r < 0 && r != -ESRCH) {
+ _cleanup_free_ char *comm = NULL;
+ get_process_comm(control_pid, &comm);
+
+ log_warning_unit(u->id,
+ "Failed to kill control process %li (%s): %s",
+ (long) control_pid, strna(comm), strerror(-r));
+ } else
+ wait_for_exit = true;
+ }
+
+ if (c->kill_mode == KILL_CONTROL_GROUP) {
+ _cleanup_set_free_ Set *pid_set = NULL;
+
+ pid_set = set_new(trivial_hash_func, trivial_compare_func);
+ if (!pid_set)
+ return -ENOMEM;
+
+ /* Exclude the main/control pids from being killed via the cgroup */
+ if (main_pid > 0) {
+ r = set_put(pid_set, LONG_TO_PTR(main_pid));
+ if (r < 0)
+ return r;
+ }
+
+ if (control_pid > 0) {
+ r = set_put(pid_set, LONG_TO_PTR(control_pid));
+ if (r < 0)
+ return r;
+ }
+
+ r = cgroup_bonding_kill_list(u->cgroup_bondings, sig, true, false, pid_set, NULL);
+ if (r < 0) {
+ if (r != -EAGAIN && r != -ESRCH && r != -ENOENT)
+ log_warning_unit(u->id, "Failed to kill control group: %s", strerror(-r));
+ } else if (r > 0)
+ wait_for_exit = true;
+ }
+
+ return wait_for_exit;
+}
+
static const char* const unit_active_state_table[_UNIT_ACTIVE_STATE_MAX] = {
[UNIT_ACTIVE] = "active",
[UNIT_RELOADING] = "reloading",
diff --git a/src/core/unit.h b/src/core/unit.h
index 702bfeece6..b04475e4fb 100644
--- a/src/core/unit.h
+++ b/src/core/unit.h
@@ -23,6 +23,7 @@
#include <stdbool.h>
#include <stdlib.h>
+#include <unistd.h>
typedef struct Unit Unit;
typedef struct UnitVTable UnitVTable;
@@ -39,6 +40,7 @@ typedef struct UnitStatusMessageFormats UnitStatusMessageFormats;
#include "condition.h"
#include "install.h"
#include "unit-name.h"
+#include "cgroup-semantics.h"
enum UnitActiveState {
UNIT_ACTIVE,
@@ -136,8 +138,10 @@ struct Unit {
char *fragment_path; /* if loaded from a config file this is the primary path to it */
char *source_path; /* if converted, the source file */
+ char **dropin_paths;
usec_t fragment_mtime;
usec_t source_mtime;
+ usec_t dropin_mtime;
/* If there is something to do with this unit, then this is the installed job for it */
Job *job;
@@ -270,6 +274,9 @@ struct UnitVTable {
* ExecContext is found, if the unit type has that */
size_t exec_context_offset;
+ /* The name of the section with the exec settings of ExecContext */
+ const char *exec_section;
+
/* Config file sections this unit type understands, separated
* by NUL chars */
const char *sections;
@@ -345,7 +352,7 @@ struct UnitVTable {
/* Called whenever a process of this unit sends us a message */
void (*notify_message)(Unit *u, pid_t pid, char **tags);
- /* Called whenever a name thus Unit registered for comes or
+ /* Called whenever a name this Unit registered for comes or
* goes away. */
void (*bus_name_owner_change)(Unit *u, const char *name, const char *old_owner, const char *new_owner);
@@ -361,6 +368,10 @@ struct UnitVTable {
/* Return the set of units that are following each other */
int (*following_set)(Unit *u, Set **s);
+ /* Invoked each time a unit this unit is triggering changes
+ * state or gains/loses a job */
+ void (*trigger_notify)(Unit *u, Unit *trigger);
+
/* Called whenever CLOCK_REALTIME made a jump */
void (*time_change)(Unit *u);
@@ -410,6 +421,8 @@ extern const UnitVTable * const unit_vtable[_UNIT_TYPE_MAX];
/* For casting the various unit types into a unit */
#define UNIT(u) (&(u)->meta)
+#define UNIT_TRIGGER(u) ((Unit*) set_first((u)->dependencies[UNIT_TRIGGERS]))
+
DEFINE_CAST(SOCKET, Socket);
DEFINE_CAST(TIMER, Timer);
DEFINE_CAST(SERVICE, Service);
@@ -437,11 +450,10 @@ int unit_add_two_dependencies_by_name_inverse(Unit *u, UnitDependency d, UnitDep
int unit_add_exec_dependencies(Unit *u, ExecContext *c);
-int unit_add_cgroup(Unit *u, CGroupBonding *b);
-int unit_add_cgroup_from_text(Unit *u, const char *name);
+int unit_add_cgroup_from_text(Unit *u, const char *name, bool overwrite, CGroupBonding **ret);
int unit_add_default_cgroups(Unit *u);
CGroupBonding* unit_get_default_cgroup(Unit *u);
-int unit_add_cgroup_attribute(Unit *u, const char *controller, const char *name, const char *value, CGroupAttributeMapCallback map_callback);
+int unit_add_cgroup_attribute(Unit *u, const CGroupSemantics *semantics, const char *controller, const char *name, const char *value, CGroupAttribute **ret);
int unit_choose_id(Unit *u, const char *name);
int unit_set_description(Unit *u, const char *description);
@@ -456,13 +468,13 @@ void unit_add_to_gc_queue(Unit *u);
int unit_merge(Unit *u, Unit *other);
int unit_merge_by_name(Unit *u, const char *other);
-Unit *unit_follow_merge(Unit *u);
+Unit *unit_follow_merge(Unit *u) _pure_;
int unit_load_fragment_and_dropin(Unit *u);
int unit_load_fragment_and_dropin_optional(Unit *u);
int unit_load(Unit *unit);
-const char *unit_description(Unit *u);
+const char *unit_description(Unit *u) _pure_;
bool unit_has_name(Unit *u, const char *name);
@@ -472,15 +484,16 @@ const char* unit_sub_state_to_string(Unit *u);
void unit_dump(Unit *u, FILE *f, const char *prefix);
-bool unit_can_reload(Unit *u);
-bool unit_can_start(Unit *u);
-bool unit_can_isolate(Unit *u);
+bool unit_can_reload(Unit *u) _pure_;
+bool unit_can_start(Unit *u) _pure_;
+bool unit_can_isolate(Unit *u) _pure_;
int unit_start(Unit *u);
int unit_stop(Unit *u);
int unit_reload(Unit *u);
int unit_kill(Unit *u, KillWho w, int signo, DBusError *error);
+int unit_kill_common(Unit *u, KillWho who, int signo, pid_t main_pid, pid_t control_pid, DBusError *error);
void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_success);
@@ -505,7 +518,7 @@ char *unit_dbus_path(Unit *u);
int unit_load_related_unit(Unit *u, const char *type, Unit **_found);
int unit_get_related_unit(Unit *u, const char *type, Unit **_found);
-bool unit_can_serialize(Unit *u);
+bool unit_can_serialize(Unit *u) _pure_;
int unit_serialize(Unit *u, FILE *f, FDSet *fds, bool serialize_jobs);
void unit_serialize_item_format(Unit *u, FILE *f, const char *key, const char *value, ...) _printf_attr_(4,5);
void unit_serialize_item(Unit *u, FILE *f, const char *key, const char *value);
@@ -515,7 +528,7 @@ int unit_add_node_link(Unit *u, const char *what, bool wants);
int unit_coldplug(Unit *u);
-void unit_status_printf(Unit *u, const char *status, const char *format, ...);
+void unit_status_printf(Unit *u, const char *status, const char *unit_status_msg_format) _printf_attr_(3, 0);
bool unit_need_daemon_reload(Unit *u);
@@ -523,8 +536,9 @@ void unit_reset_failed(Unit *u);
Unit *unit_following(Unit *u);
-bool unit_pending_inactive(Unit *u);
-bool unit_pending_active(Unit *u);
+bool unit_stop_pending(Unit *u) _pure_;
+bool unit_inactive_or_pending(Unit *u) _pure_;
+bool unit_active_or_pending(Unit *u);
int unit_add_default_target_dependency(Unit *u, Unit *target);
@@ -532,7 +546,8 @@ char *unit_default_cgroup_path(Unit *u);
int unit_following_set(Unit *u, Set **s);
-void unit_trigger_on_failure(Unit *u);
+void unit_start_on_failure(Unit *u);
+void unit_trigger_notify(Unit *u);
bool unit_condition_test(Unit *u);
@@ -548,17 +563,26 @@ int unit_add_mount_links(Unit *u);
int unit_exec_context_defaults(Unit *u, ExecContext *c);
-ExecContext *unit_get_exec_context(Unit *u);
+ExecContext *unit_get_exec_context(Unit *u) _pure_;
+
+int unit_write_drop_in(Unit *u, bool runtime, const char *name, const char *data);
+int unit_remove_drop_in(Unit *u, bool runtime, const char *name);
-const char *unit_active_state_to_string(UnitActiveState i);
-UnitActiveState unit_active_state_from_string(const char *s);
+int unit_kill_context(Unit *u, KillContext *c, bool sigkill, pid_t main_pid, pid_t control_pid, bool main_pid_alien);
-const char *unit_dependency_to_string(UnitDependency i);
-UnitDependency unit_dependency_from_string(const char *s);
+const char *unit_active_state_to_string(UnitActiveState i) _const_;
+UnitActiveState unit_active_state_from_string(const char *s) _pure_;
-#define log_full_unit(level, unit, ...) log_meta_object(level, __FILE__, __LINE__, __func__, "UNIT=", unit, __VA_ARGS__)
+const char *unit_dependency_to_string(UnitDependency i) _const_;
+UnitDependency unit_dependency_from_string(const char *s) _pure_;
+
+/* Macros which append UNIT= or USER_UNIT= to the message */
+
+#define log_full_unit(level, unit, ...) log_meta_object(level, __FILE__, __LINE__, __func__, getpid() == 1 ? "UNIT=" : "USER_UNIT=", unit, __VA_ARGS__)
#define log_debug_unit(unit, ...) log_full_unit(LOG_DEBUG, unit, __VA_ARGS__)
#define log_info_unit(unit, ...) log_full_unit(LOG_INFO, unit, __VA_ARGS__)
#define log_notice_unit(unit, ...) log_full_unit(LOG_NOTICE, unit, __VA_ARGS__)
#define log_warning_unit(unit, ...) log_full_unit(LOG_WARNING, unit, __VA_ARGS__)
#define log_error_unit(unit, ...) log_full_unit(LOG_ERR, unit, __VA_ARGS__)
+
+#define log_struct_unit(level, unit, ...) log_struct(level, getpid() == 1 ? "UNIT=%s" : "USER_UNIT=%s", unit, __VA_ARGS__)
diff --git a/src/core/user.conf b/src/core/user.conf
index e02b46bc3a..4252451eb7 100644
--- a/src/core/user.conf
+++ b/src/core/user.conf
@@ -5,7 +5,7 @@
# the Free Software Foundation; either version 2.1 of the License, or
# (at your option) any later version.
#
-# See systemd.conf(5) for details
+# See systemd-user.conf(5) for details
[Manager]
#LogLevel=info
diff --git a/src/cryptsetup/cryptsetup-generator.c b/src/cryptsetup/cryptsetup-generator.c
index 6e7b7070e2..81b770890a 100644
--- a/src/cryptsetup/cryptsetup-generator.c
+++ b/src/cryptsetup/cryptsetup-generator.c
@@ -29,11 +29,11 @@
#include "mkdir.h"
#include "virt.h"
#include "strv.h"
+#include "fileio.h"
static const char *arg_dest = "/tmp";
static bool arg_enabled = true;
static bool arg_read_crypttab = true;
-static char **arg_proc_cmdline_disks = NULL;
static bool has_option(const char *haystack, const char *needle) {
const char *f = haystack;
@@ -70,9 +70,8 @@ static int create_disk(
const char *password,
const char *options) {
- char *p = NULL, *n = NULL, *d = NULL, *u = NULL, *from = NULL, *to = NULL, *e = NULL;
- int r;
- FILE *f = NULL;
+ _cleanup_free_ char *p = NULL, *n = NULL, *d = NULL, *u = NULL, *from = NULL, *to = NULL, *e = NULL;
+ _cleanup_fclose_ FILE *f = NULL;
bool noauto, nofail;
assert(name);
@@ -82,59 +81,65 @@ static int create_disk(
nofail = has_option(options, "nofail");
n = unit_name_from_path_instance("systemd-cryptsetup", name, ".service");
- if (!n) {
- r = log_oom();
- goto fail;
- }
+ if (!n)
+ return log_oom();
p = strjoin(arg_dest, "/", n, NULL);
- if (!p) {
- r = log_oom();
- goto fail;
- }
+ if (!p)
+ return log_oom();
u = fstab_node_to_udev_node(device);
- if (!u) {
- r = log_oom();
- goto fail;
- }
+ if (!u)
+ return log_oom();
d = unit_name_from_path(u, ".device");
- if (!d) {
- r = log_oom();
- goto fail;
- }
+ if (!d)
+ return log_oom();
f = fopen(p, "wxe");
if (!f) {
- r = -errno;
log_error("Failed to create unit file %s: %m", p);
- goto fail;
+ return -errno;
}
- fprintf(f,
+ fputs(
"# Automatically generated by systemd-cryptsetup-generator\n\n"
"[Unit]\n"
- "Description=Cryptography Setup for %%I\n"
+ "Description=Cryptography Setup for %I\n"
"Documentation=man:systemd-cryptsetup@.service(8) man:crypttab(5)\n"
"SourcePath=/etc/crypttab\n"
"Conflicts=umount.target\n"
"DefaultDependencies=no\n"
- "BindsTo=%s dev-mapper-%%i.device\n"
- "After=systemd-readahead-collect.service systemd-readahead-replay.service %s\n"
- "Before=umount.target\n",
- d, d);
+ "BindsTo=dev-mapper-%i.device\n"
+ "After=systemd-readahead-collect.service systemd-readahead-replay.service\n",
+ f);
if (!nofail)
fprintf(f,
"Before=cryptsetup.target\n");
- if (password && (streq(password, "/dev/urandom") ||
- streq(password, "/dev/random") ||
- streq(password, "/dev/hw_random")))
- fputs("After=systemd-random-seed-load.service\n", f);
+ if (password) {
+ if (streq(password, "/dev/urandom") ||
+ streq(password, "/dev/random") ||
+ streq(password, "/dev/hw_random"))
+ fputs("After=systemd-random-seed-load.service\n", f);
+ else if (!streq(password, "-") &&
+ !streq(password, "none"))
+ fprintf(f,
+ "RequiresMountsFor=%s\n",
+ password);
+ }
+
+ if (is_device_path(u))
+ fprintf(f,
+ "BindsTo=%s\n"
+ "After=%s\n"
+ "Before=umount.target\n",
+ d, d);
else
- fputs("Before=local-fs.target\n", f);
+ fprintf(f,
+ "RequiresMountsFor=%s\n",
+ u);
fprintf(f,
"\n[Service]\n"
@@ -159,86 +164,78 @@ static int create_disk(
fflush(f);
if (ferror(f)) {
- r = -errno;
log_error("Failed to write file %s: %m", p);
- goto fail;
+ return -errno;
}
- if (asprintf(&from, "../%s", n) < 0) {
- r = log_oom();
- goto fail;
- }
+ if (asprintf(&from, "../%s", n) < 0)
+ return log_oom();
if (!noauto) {
to = strjoin(arg_dest, "/", d, ".wants/", n, NULL);
- if (!to) {
- r = log_oom();
- goto fail;
- }
+ if (!to)
+ return log_oom();
mkdir_parents_label(to, 0755);
if (symlink(from, to) < 0) {
- log_error("Failed to create symlink '%s' to '%s': %m", from, to);
- r = -errno;
- goto fail;
+ log_error("Failed to create symlink %s: %m", to);
+ return -errno;
}
free(to);
-
if (!nofail)
to = strjoin(arg_dest, "/cryptsetup.target.requires/", n, NULL);
else
to = strjoin(arg_dest, "/cryptsetup.target.wants/", n, NULL);
- if (!to) {
- r = log_oom();
- goto fail;
- }
+ if (!to)
+ return log_oom();
mkdir_parents_label(to, 0755);
if (symlink(from, to) < 0) {
- log_error("Failed to create symlink '%s' to '%s': %m", from, to);
- r = -errno;
- goto fail;
+ log_error("Failed to create symlink %s: %m", to);
+ return -errno;
}
-
- free(to);
- to = NULL;
}
e = unit_name_escape(name);
+ if (!e)
+ return log_oom();
+
+ free(to);
to = strjoin(arg_dest, "/dev-mapper-", e, ".device.requires/", n, NULL);
- if (!to) {
- r = log_oom();
- goto fail;
- }
+ if (!to)
+ return log_oom();
mkdir_parents_label(to, 0755);
if (symlink(from, to) < 0) {
- log_error("Failed to create symlink '%s' to '%s': %m", from, to);
- r = -errno;
- goto fail;
+ log_error("Failed to create symlink %s: %m", to);
+ return -errno;
}
- r = 0;
-
-fail:
- free(p);
- free(n);
- free(d);
- free(e);
-
- free(from);
- free(to);
-
- if (f)
- fclose(f);
+ if (!noauto && !nofail) {
+ int r;
+ free(p);
+ p = strjoin(arg_dest, "/dev-mapper-", e, ".device.d/50-job-timeout-sec-0.conf", NULL);
+ if (!p)
+ return log_oom();
+
+ mkdir_parents_label(p, 0755);
+
+ r = write_string_file(p,
+ "# Automatically generated by systemd-cryptsetup-generator\n\n"
+ "[Unit]\n"
+ "JobTimeoutSec=0\n"); /* the binary handles timeouts anyway */
+ if (r)
+ return r;
+ }
- return r;
+ return 0;
}
-static int parse_proc_cmdline(void) {
- char *line, *w, *state;
+static int parse_proc_cmdline(char ***arg_proc_cmdline_disks, char **arg_proc_cmdline_keyfile) {
+ _cleanup_free_ char *line = NULL;
+ char *w = NULL, *state = NULL;
int r;
size_t l;
@@ -252,13 +249,11 @@ static int parse_proc_cmdline(void) {
}
FOREACH_WORD_QUOTED(w, l, line, state) {
- char *word;
+ _cleanup_free_ char *word = NULL;
word = strndup(w, l);
- if (!word) {
- r = log_oom();
- goto finish;
- }
+ if (!word)
+ return log_oom();
if (startswith(word, "luks=")) {
r = parse_boolean(word + 5);
@@ -295,28 +290,29 @@ static int parse_proc_cmdline(void) {
}
} else if (startswith(word, "luks.uuid=")) {
- char **t;
-
- t = strv_append(arg_proc_cmdline_disks, word + 10);
- if (!t) {
- r = log_oom();
- goto finish;
- }
- strv_free(arg_proc_cmdline_disks);
- arg_proc_cmdline_disks = t;
+ if (strv_extend(arg_proc_cmdline_disks, word + 10) < 0)
+ return log_oom();
} else if (startswith(word, "rd.luks.uuid=")) {
if (in_initrd()) {
- char **t;
+ if (strv_extend(arg_proc_cmdline_disks, word + 13) < 0)
+ return log_oom();
+ }
- t = strv_append(arg_proc_cmdline_disks, word + 13);
- if (!t) {
- r = log_oom();
- goto finish;
- }
- strv_free(arg_proc_cmdline_disks);
- arg_proc_cmdline_disks = t;
+ } else if (startswith(word, "luks.key=")) {
+ *arg_proc_cmdline_keyfile = strdup(word + 9);
+ if (!*arg_proc_cmdline_keyfile)
+ return log_oom();
+
+ } else if (startswith(word, "rd.luks.key=")) {
+
+ if (in_initrd()) {
+ if (*arg_proc_cmdline_keyfile)
+ free(*arg_proc_cmdline_keyfile);
+ *arg_proc_cmdline_keyfile = strdup(word + 12);
+ if (!*arg_proc_cmdline_keyfile)
+ return log_oom();
}
} else if (startswith(word, "luks.") ||
@@ -324,21 +320,20 @@ static int parse_proc_cmdline(void) {
log_warning("Unknown kernel switch %s. Ignoring.", word);
}
-
- free(word);
}
- r = 0;
+ strv_uniq(*arg_proc_cmdline_disks);
-finish:
- free(line);
- return r;
+ return 0;
}
int main(int argc, char *argv[]) {
- FILE *f = NULL;
- int r = EXIT_SUCCESS;
+ _cleanup_strv_free_ char **arg_proc_cmdline_disks_done = NULL;
+ _cleanup_strv_free_ char **arg_proc_cmdline_disks = NULL;
+ _cleanup_free_ char *arg_proc_cmdline_keyfile = NULL;
+ _cleanup_fclose_ FILE *f = NULL;
unsigned n = 0;
+ int r = EXIT_SUCCESS;
char **i;
if (argc > 1 && argc != 4) {
@@ -355,91 +350,119 @@ int main(int argc, char *argv[]) {
umask(0022);
- if (parse_proc_cmdline() < 0)
+ if (parse_proc_cmdline(&arg_proc_cmdline_disks, &arg_proc_cmdline_keyfile) < 0)
return EXIT_FAILURE;
- if (!arg_enabled) {
- r = EXIT_SUCCESS;
- goto finish;
- }
+ if (!arg_enabled)
+ return EXIT_SUCCESS;
- STRV_FOREACH(i, arg_proc_cmdline_disks) {
- char *name, *device;
- const char *p = *i;
+ if (arg_read_crypttab) {
+ struct stat st;
- if (startswith(p, "luks-"))
- p += 5;
+ f = fopen("/etc/crypttab", "re");
+ if (!f) {
+ if (errno == ENOENT)
+ r = EXIT_SUCCESS;
+ else {
+ r = EXIT_FAILURE;
+ log_error("Failed to open /etc/crypttab: %m");
+ }
- name = strappend("luks-", p);
- device = strappend("UUID=", p);
+ goto next;
+ }
- if (!name || !device) {
- log_oom();
+ if (fstat(fileno(f), &st) < 0) {
+ log_error("Failed to stat /etc/crypttab: %m");
r = EXIT_FAILURE;
- free(name);
- free(device);
- goto finish;
+ goto next;
}
- if (create_disk(name, device, NULL, NULL) < 0)
- r = EXIT_FAILURE;
+ /* If we readd support for specifying passphrases
+ * directly in crypttabe we should upgrade the warning
+ * below, though possibly only if a passphrase is
+ * specified directly. */
+ if (st.st_mode & 0005)
+ log_debug("/etc/crypttab is world-readable. This is usually not a good idea.");
- free(name);
- free(device);
- }
+ for (;;) {
+ char line[LINE_MAX], *l;
+ _cleanup_free_ char *name = NULL, *device = NULL, *password = NULL, *options = NULL;
+ int k;
- if (!arg_read_crypttab)
- return r;
+ if (!fgets(line, sizeof(line), f))
+ break;
- f = fopen("/etc/crypttab", "re");
- if (!f) {
+ n++;
- if (errno == ENOENT)
- r = EXIT_SUCCESS;
- else {
- r = EXIT_FAILURE;
- log_error("Failed to open /etc/crypttab: %m");
- }
+ l = strstrip(line);
+ if (*l == '#' || *l == 0)
+ continue;
- goto finish;
- }
+ k = sscanf(l, "%ms %ms %ms %ms", &name, &device, &password, &options);
+ if (k < 2 || k > 4) {
+ log_error("Failed to parse /etc/crypttab:%u, ignoring.", n);
+ r = EXIT_FAILURE;
+ continue;
+ }
- for (;;) {
- char line[LINE_MAX], *l;
- char *name = NULL, *device = NULL, *password = NULL, *options = NULL;
- int k;
+ if (arg_proc_cmdline_disks) {
+ /*
+ If luks UUIDs are specified on the kernel command line, use them as a filter
+ for /etc/crypttab and only generate units for those.
+ */
+ STRV_FOREACH(i, arg_proc_cmdline_disks) {
+ _cleanup_free_ char *proc_device = NULL, *proc_name = NULL;
+ const char *p = *i;
- if (!fgets(line, sizeof(line), f))
- break;
+ if (startswith(p, "luks-"))
+ p += 5;
- n++;
+ proc_name = strappend("luks-", p);
+ proc_device = strappend("UUID=", p);
- l = strstrip(line);
- if (*l == '#' || *l == 0)
- continue;
+ if (!proc_name || !proc_device)
+ return log_oom();
- k = sscanf(l, "%ms %ms %ms %ms", &name, &device, &password, &options);
- if (k < 2 || k > 4) {
- log_error("Failed to parse /etc/crypttab:%u, ignoring.", n);
- r = EXIT_FAILURE;
- goto next;
+ if (streq(proc_device, device) || streq(proc_name, name)) {
+ if (create_disk(name, device, password, options) < 0)
+ r = EXIT_FAILURE;
+
+ if (strv_extend(&arg_proc_cmdline_disks_done, p) < 0)
+ return log_oom();
+ }
+ }
+ } else {
+ if (create_disk(name, device, password, options) < 0)
+ r = EXIT_FAILURE;
+ }
}
+ }
- if (create_disk(name, device, password, options) < 0)
- r = EXIT_FAILURE;
+next:
+ STRV_FOREACH(i, arg_proc_cmdline_disks) {
+ /*
+ Generate units for those UUIDs, which were specified
+ on the kernel command line and not yet written.
+ */
- next:
- free(name);
- free(device);
- free(password);
- free(options);
- }
+ _cleanup_free_ char *name = NULL, *device = NULL;
+ const char *p = *i;
-finish:
- if (f)
- fclose(f);
+ if (startswith(p, "luks-"))
+ p += 5;
+
+ if (strv_contains(arg_proc_cmdline_disks_done, p))
+ continue;
- strv_free(arg_proc_cmdline_disks);
+ name = strappend("luks-", p);
+ device = strappend("UUID=", p);
+
+ if (!name || !device)
+ return log_oom();
+
+ if (create_disk(name, device, arg_proc_cmdline_keyfile, "timeout=0") < 0)
+ r = EXIT_FAILURE;
+ }
return r;
}
diff --git a/src/cryptsetup/cryptsetup.c b/src/cryptsetup/cryptsetup.c
index f33284370c..347394db8e 100644
--- a/src/cryptsetup/cryptsetup.c
+++ b/src/cryptsetup/cryptsetup.c
@@ -44,7 +44,7 @@ static unsigned opt_tries = 0;
static bool opt_readonly = false;
static bool opt_verify = false;
static bool opt_discards = false;
-static usec_t opt_timeout = DEFAULT_TIMEOUT_USEC;
+static usec_t opt_timeout = 0;
/* Options Debian's crypttab knows we don't:
@@ -68,7 +68,8 @@ static int parse_one_option(const char *option) {
if (startswith(option, "cipher=")) {
char *t;
- if (!(t = strdup(option+7)))
+ t = strdup(option+7);
+ if (!t)
return -ENOMEM;
free(opt_cipher);
@@ -98,7 +99,8 @@ static int parse_one_option(const char *option) {
} else if (startswith(option, "hash=")) {
char *t;
- if (!(t = strdup(option+5)))
+ t = strdup(option+5);
+ if (!t)
return -ENOMEM;
free(opt_hash);
@@ -111,7 +113,7 @@ static int parse_one_option(const char *option) {
return 0;
}
- } else if (streq(option, "readonly"))
+ } else if (streq(option, "readonly") || streq(option, "read-only"))
opt_readonly = true;
else if (streq(option, "verify"))
opt_verify = true;
@@ -125,7 +127,7 @@ static int parse_one_option(const char *option) {
opt_type = CRYPT_PLAIN;
else if (startswith(option, "timeout=")) {
- if (parse_usec(option+8, &opt_timeout) < 0) {
+ if (parse_sec(option+8, &opt_timeout) < 0) {
log_error("timeout= parse failure, ignoring.");
return 0;
}
@@ -137,22 +139,19 @@ static int parse_one_option(const char *option) {
}
static int parse_options(const char *options) {
- char *state;
- char *w;
+ char *state, *w;
size_t l;
+ int r;
assert(options);
FOREACH_WORD_SEPARATOR(w, l, options, ",", state) {
- char *o;
- int r;
+ _cleanup_free_ char *o;
- if (!(o = strndup(w, l)))
+ o = strndup(w, l);
+ if (!o)
return -ENOMEM;
-
r = parse_one_option(o);
- free(o);
-
if (r < 0)
return r;
}
@@ -165,11 +164,19 @@ static void log_glue(int level, const char *msg, void *usrptr) {
}
static char *disk_description(const char *path) {
+
+ static const char name_fields[] = {
+ "ID_PART_ENTRY_NAME\0"
+ "DM_NAME\0"
+ "ID_MODEL_FROM_DATABASE\0"
+ "ID_MODEL\0"
+ };
+
struct udev *udev = NULL;
struct udev_device *device = NULL;
struct stat st;
char *description = NULL;
- const char *model;
+ const char *i;
assert(path);
@@ -179,16 +186,23 @@ static char *disk_description(const char *path) {
if (!S_ISBLK(st.st_mode))
return NULL;
- if (!(udev = udev_new()))
+ udev = udev_new();
+ if (!udev)
return NULL;
- if (!(device = udev_device_new_from_devnum(udev, 'b', st.st_rdev)))
+ device = udev_device_new_from_devnum(udev, 'b', st.st_rdev);
+ if (!device)
goto finish;
- if ((model = udev_device_get_property_value(device, "ID_MODEL_FROM_DATABASE")) ||
- (model = udev_device_get_property_value(device, "ID_MODEL")) ||
- (model = udev_device_get_property_value(device, "DM_NAME")))
- description = strdup(model);
+ NULSTR_FOREACH(i, name_fields) {
+ const char *name;
+
+ name = udev_device_get_property_value(device, i);
+ if (!isempty(name)) {
+ description = strdup(name);
+ break;
+ }
+ }
finish:
if (device)
@@ -289,8 +303,10 @@ int main(int argc, char *argv[]) {
key_file = argv[4];
}
- if (argc >= 6 && argv[5][0] && !streq(argv[5], "-"))
- parse_options(argv[5]);
+ if (argc >= 6 && argv[5][0] && !streq(argv[5], "-")) {
+ if (parse_options(argv[5]) < 0)
+ goto finish;
+ }
/* A delicious drop of snake oil */
mlockall(MCL_FUTURE);
@@ -315,7 +331,8 @@ int main(int argc, char *argv[]) {
name = name_buffer ? name_buffer : argv[2];
- if ((k = crypt_init(&cd, argv[3]))) {
+ k = crypt_init(&cd, argv[3]);
+ if (k) {
log_error("crypt_init() failed: %s", strerror(-k));
goto finish;
}
@@ -353,8 +370,9 @@ int main(int argc, char *argv[]) {
size_t l;
l = strcspn(opt_cipher, "-");
+ truncated_cipher = strndup(opt_cipher, l);
- if (!(truncated_cipher = strndup(opt_cipher, l))) {
+ if (!truncated_cipher) {
log_oom();
goto finish;
}
@@ -373,8 +391,7 @@ int main(int argc, char *argv[]) {
passwords = NULL;
if (!key_file) {
- char *text;
- char **p;
+ char *text, **p;
if (asprintf(&text, "Please enter passphrase for disk %s!", name) < 0) {
log_oom();
@@ -444,10 +461,7 @@ int main(int argc, char *argv[]) {
k = crypt_load(cd, CRYPT_LUKS1, NULL);
if ((!opt_type && k < 0) || streq_ptr(opt_type, CRYPT_PLAIN)) {
- struct crypt_params_plain params;
-
- zero(params);
- params.hash = hash;
+ struct crypt_params_plain params = { .hash = hash };
/* for CRYPT_PLAIN limit reads
* from keyfile to key length, and
@@ -483,10 +497,25 @@ int main(int argc, char *argv[]) {
crypt_get_volume_key_size(cd)*8,
argv[3]);
- if (key_file)
- k = crypt_activate_by_keyfile_offset(cd, argv[2], CRYPT_ANY_SLOT, key_file, opt_keyfile_size,
- opt_keyfile_offset, flags);
- else {
+ if (key_file) {
+ struct stat st;
+
+ /* Ideally we'd do this on the open
+ * fd, but since this is just a
+ * warning it's OK to do this in two
+ * steps */
+ if (stat(key_file, &st) >= 0 && (st.st_mode & 0005))
+ log_warning("Key file %s is world-readable. That's certainly not a good idea.", key_file);
+
+ k = crypt_activate_by_keyfile_offset(
+ cd, argv[2], CRYPT_ANY_SLOT, key_file, opt_keyfile_size,
+ opt_keyfile_offset, flags);
+ if (k < 0) {
+ log_error("Failed to activate with key file '%s': %s", key_file, strerror(-k));
+ key_file = NULL;
+ continue;
+ }
+ } else {
char **p;
STRV_FOREACH(p, passwords) {
@@ -521,14 +550,16 @@ int main(int argc, char *argv[]) {
} else if (streq(argv[1], "detach")) {
int k;
- if ((k = crypt_init_by_name(&cd, argv[2]))) {
+ k = crypt_init_by_name(&cd, argv[2]);
+ if (k) {
log_error("crypt_init() failed: %s", strerror(-k));
goto finish;
}
crypt_set_log_callback(cd, log_glue, NULL);
- if ((k = crypt_deactivate(cd, argv[2])) < 0) {
+ k = crypt_deactivate(cd, argv[2]);
+ if (k < 0) {
log_error("Failed to deactivate: %s", strerror(-k));
goto finish;
}
diff --git a/src/delta/delta.c b/src/delta/delta.c
index 9f20938516..aec3dc8995 100644
--- a/src/delta/delta.c
+++ b/src/delta/delta.c
@@ -312,17 +312,17 @@ static int parse_flags(const char *flag_str, int flags) {
size_t l;
FOREACH_WORD(w, l, flag_str, state) {
- if (strncmp("masked", w, l) == 0)
+ if (strneq("masked", w, l))
flags |= SHOW_MASKED;
- else if (strncmp ("equivalent", w, l) == 0)
+ else if (strneq ("equivalent", w, l))
flags |= SHOW_EQUIVALENT;
- else if (strncmp("redirected", w, l) == 0)
+ else if (strneq("redirected", w, l))
flags |= SHOW_REDIRECTED;
- else if (strncmp("overridden", w, l) == 0)
+ else if (strneq("overridden", w, l))
flags |= SHOW_OVERRIDDEN;
- else if (strncmp("unchanged", w, l) == 0)
+ else if (strneq("unchanged", w, l))
flags |= SHOW_UNCHANGED;
- else if (strncmp("default", w, l) == 0)
+ else if (strneq("default", w, l))
flags |= SHOW_DEFAULTS;
else
return -EINVAL;
@@ -454,7 +454,7 @@ int main(int argc, char *argv[]) {
arg_flags |= SHOW_OVERRIDDEN;
if (!arg_no_pager)
- pager_open();
+ pager_open(false);
if (optind < argc) {
int i;
diff --git a/src/efi-boot-generator/Makefile b/src/efi-boot-generator/Makefile
new file mode 120000
index 0000000000..d0b0e8e008
--- /dev/null
+++ b/src/efi-boot-generator/Makefile
@@ -0,0 +1 @@
+../Makefile \ No newline at end of file
diff --git a/src/efi-boot-generator/efi-boot-generator.c b/src/efi-boot-generator/efi-boot-generator.c
new file mode 100644
index 0000000000..4367c536b0
--- /dev/null
+++ b/src/efi-boot-generator/efi-boot-generator.c
@@ -0,0 +1,123 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ 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 <http://www.gnu.org/licenses/>.
+***/
+
+#include <unistd.h>
+#include <stdlib.h>
+
+#include "efivars.h"
+#include "path-util.h"
+#include "util.h"
+#include "mkdir.h"
+
+static const char *arg_dest = "/tmp";
+
+int main(int argc, char *argv[]) {
+ int r = EXIT_SUCCESS;
+ sd_id128_t id;
+ _cleanup_free_ char *name = NULL;
+ _cleanup_fclose_ FILE *f = NULL;
+
+ if (argc > 1 && argc != 4) {
+ log_error("This program takes three or no arguments.");
+ return EXIT_FAILURE;
+ }
+
+ if (argc > 1)
+ arg_dest = argv[3];
+
+ log_set_target(LOG_TARGET_SAFE);
+ log_parse_environment();
+ log_open();
+
+ umask(0022);
+
+ if (!is_efi_boot())
+ return EXIT_SUCCESS;
+
+ if (dir_is_empty("/boot") <= 0)
+ return EXIT_SUCCESS;
+
+ r = efi_get_loader_device_part_uuid(&id);
+ if (r == -ENOENT)
+ return EXIT_SUCCESS;
+ if (r < 0) {
+ log_error("Failed to read ESP partition UUID: %s", strerror(-r));
+ return EXIT_FAILURE;
+ }
+
+ name = strjoin(arg_dest, "/boot.mount", NULL);
+ if (!name) {
+ log_oom();
+ return EXIT_FAILURE;
+ }
+
+ f = fopen(name, "wxe");
+ if (!f) {
+ log_error("Failed to create mount unit file %s: %m", name);
+ return EXIT_FAILURE;
+ }
+
+ fprintf(f,
+ "# Automatially generated by systemd-efi-boot-generator\n\n"
+ "[Unit]\n"
+ "Description=EFI System Partition\n\n"
+ "[Mount]\n"
+ "Where=/boot\n"
+ "What=/dev/disk/by-partuuid/%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n"
+ "Options=umask=0077\n",
+ SD_ID128_FORMAT_VAL(id));
+
+ free(name);
+ name = strjoin(arg_dest, "/boot.automount", NULL);
+ if (!name) {
+ log_oom();
+ return EXIT_FAILURE;
+ }
+
+ fclose(f);
+ f = fopen(name, "wxe");
+ if (!f) {
+ log_error("Failed to create automount unit file %s: %m", name);
+ return EXIT_FAILURE;
+ }
+
+ fputs("# Automatially generated by systemd-efi-boot-generator\n\n"
+ "[Unit]\n"
+ "Description=EFI System Partition Automount\n\n"
+ "[Automount]\n"
+ "Where=/boot\n", f);
+
+ free(name);
+ name = strjoin(arg_dest, "/local-fs.target.wants/boot.automount", NULL);
+ if (!name) {
+ log_oom();
+ return EXIT_FAILURE;
+ }
+
+ mkdir_parents(name, 0755);
+
+ if (symlink("../boot.automount", name) < 0) {
+ log_error("Failed to create symlink %s: %m", name);
+ return EXIT_FAILURE;
+ }
+
+ return 0;
+}
diff --git a/src/fsck/fsck.c b/src/fsck/fsck.c
index f5d38ad261..f298cf7b9a 100644
--- a/src/fsck/fsck.c
+++ b/src/fsck/fsck.c
@@ -35,15 +35,16 @@
#include "special.h"
#include "bus-errors.h"
#include "virt.h"
+#include "fileio.h"
static bool arg_skip = false;
static bool arg_force = false;
static bool arg_show_progress = false;
-static void start_target(const char *target, bool isolate) {
+static void start_target(const char *target) {
DBusMessage *m = NULL, *reply = NULL;
DBusError error;
- const char *mode, *basic_target = "basic.target";
+ const char *mode = "replace", *basic_target = "basic.target";
DBusConnection *bus = NULL;
assert(target);
@@ -55,11 +56,6 @@ static void start_target(const char *target, bool isolate) {
goto finish;
}
- if (isolate)
- mode = "isolate";
- else
- mode = "replace";
-
log_info("Running request %s/start/%s", target, mode);
if (!(m = dbus_message_new_method_call("org.freedesktop.systemd1", "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "StartUnitReplace"))) {
@@ -388,10 +384,10 @@ int main(int argc, char *argv[]) {
if (status.si_code == CLD_EXITED && (status.si_status & 2) && root_directory)
/* System should be rebooted. */
- start_target(SPECIAL_REBOOT_TARGET, false);
+ start_target(SPECIAL_REBOOT_TARGET);
else if (status.si_code == CLD_EXITED && (status.si_status & 6))
/* Some other problem */
- start_target(SPECIAL_EMERGENCY_TARGET, true);
+ start_target(SPECIAL_EMERGENCY_TARGET);
else {
r = EXIT_SUCCESS;
log_warning("Ignoring error.");
diff --git a/src/fstab-generator/fstab-generator.c b/src/fstab-generator/fstab-generator.c
index f3ecc24be6..c17299f267 100644
--- a/src/fstab-generator/fstab-generator.c
+++ b/src/fstab-generator/fstab-generator.c
@@ -33,6 +33,7 @@
#include "special.h"
#include "mkdir.h"
#include "virt.h"
+#include "fileio.h"
static const char *arg_dest = "/tmp";
static bool arg_enabled = true;
@@ -68,7 +69,7 @@ static int mount_find_pri(struct mntent *me, int *ret) {
errno = 0;
r = strtoul(pri, &end, 10);
- if (errno != 0)
+ if (errno > 0)
return -errno;
if (end == pri || (*end != ',' && *end != 0))
@@ -79,8 +80,8 @@ static int mount_find_pri(struct mntent *me, int *ret) {
}
static int add_swap(const char *what, struct mntent *me) {
- char _cleanup_free_ *name = NULL, *unit = NULL, *lnk = NULL, *device = NULL;
- FILE _cleanup_fclose_ *f = NULL;
+ _cleanup_free_ char *name = NULL, *unit = NULL, *lnk = NULL, *device = NULL;
+ _cleanup_fclose_ FILE *f = NULL;
bool noauto, nofail;
int r, pri = -1;
@@ -177,7 +178,9 @@ static bool mount_is_bind(struct mntent *me) {
return
hasmntopt(me, "bind") ||
- streq(me->mnt_type, "bind");
+ streq(me->mnt_type, "bind") ||
+ hasmntopt(me, "rbind") ||
+ streq(me->mnt_type, "rbind");
}
static bool mount_is_network(struct mntent *me) {
@@ -188,15 +191,34 @@ static bool mount_is_network(struct mntent *me) {
fstype_is_network(me->mnt_type);
}
-static int add_mount(const char *what, const char *where, const char *type, const char *opts,
- int passno, bool wait, bool noauto, bool nofail, bool automount, bool isbind, bool isnetwork,
- const char *source) {
- char _cleanup_free_
+static bool mount_in_initrd(struct mntent *me) {
+ assert(me);
+
+ return
+ hasmntopt(me, "x-initrd.mount") ||
+ streq(me->mnt_dir, "/usr");
+}
+
+static int add_mount(
+ const char *what,
+ const char *where,
+ const char *type,
+ const char *opts,
+ int passno,
+ bool noauto,
+ bool nofail,
+ bool automount,
+ bool isbind,
+ const char *pre,
+ const char *pre2,
+ const char *online,
+ const char *post,
+ const char *source) {
+ _cleanup_free_ char
*name = NULL, *unit = NULL, *lnk = NULL, *device = NULL,
*automount_name = NULL, *automount_unit = NULL;
- FILE _cleanup_fclose_ *f = NULL;
+ _cleanup_fclose_ FILE *f = NULL;
int r;
- const char *post, *pre;
assert(what);
assert(where);
@@ -216,14 +238,6 @@ static int add_mount(const char *what, const char *where, const char *type, cons
mount_point_ignore(where))
return 0;
- if (isnetwork) {
- post = SPECIAL_REMOTE_FS_TARGET;
- pre = SPECIAL_REMOTE_FS_PRE_TARGET;
- } else {
- post = SPECIAL_LOCAL_FS_TARGET;
- pre = SPECIAL_LOCAL_FS_PRE_TARGET;
- }
-
name = unit_name_from_path(where, ".mount");
if (!name)
return log_oom();
@@ -248,17 +262,30 @@ static int add_mount(const char *what, const char *where, const char *type, cons
"DefaultDependencies=no\n",
source);
- if (!path_equal(where, "/"))
+ if (!path_equal(where, "/")) {
+ if (pre)
+ fprintf(f,
+ "After=%s\n",
+ pre);
+
+ if (pre2)
+ fprintf(f,
+ "After=%s\n",
+ pre2);
+
+ if (online)
+ fprintf(f,
+ "After=%s\n"
+ "Wants=%s\n",
+ online,
+ online);
+
fprintf(f,
- "After=%s\n"
- "Wants=%s\n"
"Conflicts=" SPECIAL_UMOUNT_TARGET "\n"
- "Before=" SPECIAL_UMOUNT_TARGET "\n",
- pre,
- pre);
-
+ "Before=" SPECIAL_UMOUNT_TARGET "\n");
+ }
- if (!noauto && !nofail && !automount)
+ if (post && !noauto && !nofail && !automount)
fprintf(f,
"Before=%s\n",
post);
@@ -281,10 +308,6 @@ static int add_mount(const char *what, const char *where, const char *type, cons
"Options=%s\n",
opts);
- if (wait)
- fprintf(f,
- "TimeoutSec=0\n");
-
fflush(f);
if (ferror(f)) {
log_error("Failed to write unit file %s: %m", unit);
@@ -292,14 +315,16 @@ static int add_mount(const char *what, const char *where, const char *type, cons
}
if (!noauto) {
- lnk = strjoin(arg_dest, "/", post, nofail || automount ? ".wants/" : ".requires/", name, NULL);
- if (!lnk)
- return log_oom();
+ if (post) {
+ lnk = strjoin(arg_dest, "/", post, nofail || automount ? ".wants/" : ".requires/", name, NULL);
+ if (!lnk)
+ return log_oom();
- mkdir_parents_label(lnk, 0755);
- if (symlink(unit, lnk) < 0) {
- log_error("Failed to create symlink %s: %m", lnk);
- return -errno;
+ mkdir_parents_label(lnk, 0755);
+ if (symlink(unit, lnk) < 0) {
+ log_error("Failed to create symlink %s: %m", lnk);
+ return -errno;
+ }
}
if (!isbind &&
@@ -343,14 +368,20 @@ static int add_mount(const char *what, const char *where, const char *type, cons
fprintf(f,
"# Automatically generated by systemd-fstab-generator\n\n"
"[Unit]\n"
- "SourcePath=/etc/fstab\n"
+ "SourcePath=%s\n"
"DefaultDependencies=no\n"
"Conflicts=" SPECIAL_UMOUNT_TARGET "\n"
- "Before=" SPECIAL_UMOUNT_TARGET " %s\n"
- "\n"
+ "Before=" SPECIAL_UMOUNT_TARGET "\n",
+ source);
+
+ if (post)
+ fprintf(f,
+ "Before= %s\n",
+ post);
+
+ fprintf(f,
"[Automount]\n"
"Where=%s\n",
- post,
where);
fflush(f);
@@ -374,27 +405,34 @@ static int add_mount(const char *what, const char *where, const char *type, cons
return 0;
}
-static int parse_fstab(void) {
+static int parse_fstab(const char *prefix, bool initrd) {
+ _cleanup_free_ char *fstab_path = NULL;
FILE *f;
int r = 0;
struct mntent *me;
- errno = 0;
- f = setmntent("/etc/fstab", "r");
+ fstab_path = strjoin(strempty(prefix), "/etc/fstab", NULL);
+ if (!fstab_path)
+ return log_oom();
+
+ f = setmntent(fstab_path, "r");
if (!f) {
if (errno == ENOENT)
return 0;
- log_error("Failed to open /etc/fstab: %m");
+ log_error("Failed to open %s/etc/fstab: %m", strempty(prefix));
return -errno;
}
while ((me = getmntent(f))) {
- char _cleanup_free_ *where = NULL, *what = NULL;
+ _cleanup_free_ char *where = NULL, *what = NULL;
int k;
+ if (initrd && !mount_in_initrd(me))
+ continue;
+
what = fstab_node_to_udev_node(me->mnt_fsname);
- where = strdup(me->mnt_dir);
+ where = strjoin(strempty(prefix), me->mnt_dir, NULL);
if (!what || !where) {
r = log_oom();
goto finish;
@@ -408,7 +446,8 @@ static int parse_fstab(void) {
if (streq(me->mnt_type, "swap"))
k = add_swap(what, me);
else {
- bool noauto, nofail, automount, isbind, isnetwork;
+ bool noauto, nofail, automount, isbind;
+ const char *pre, *pre2, *post, *online;
noauto = !!hasmntopt(me, "noauto");
nofail = !!hasmntopt(me, "nofail");
@@ -416,12 +455,27 @@ static int parse_fstab(void) {
hasmntopt(me, "comment=systemd.automount") ||
hasmntopt(me, "x-systemd.automount");
isbind = mount_is_bind(me);
- isnetwork = mount_is_network(me);
+
+ if (initrd) {
+ pre = pre2 = online = NULL;
+ post = SPECIAL_INITRD_FS_TARGET;
+ } else if (mount_in_initrd(me)) {
+ pre = pre2 = online = NULL;
+ post = SPECIAL_INITRD_ROOT_FS_TARGET;
+ } else if (mount_is_network(me)) {
+ pre = SPECIAL_REMOTE_FS_PRE_TARGET;
+ pre2 = SPECIAL_NETWORK_TARGET;
+ online = SPECIAL_NETWORK_ONLINE_TARGET;
+ post = SPECIAL_REMOTE_FS_TARGET;
+ } else {
+ pre = SPECIAL_LOCAL_FS_PRE_TARGET;
+ pre2 = online = NULL;
+ post = SPECIAL_LOCAL_FS_TARGET;
+ }
k = add_mount(what, where, me->mnt_type, me->mnt_opts,
- me->mnt_passno, false, noauto, nofail,
- automount, isbind, isnetwork,
- "/etc/fstab");
+ me->mnt_passno, noauto, nofail, automount,
+ isbind, pre, pre2, online, post, fstab_path);
}
if (k < 0)
@@ -434,11 +488,10 @@ finish:
}
static int parse_new_root_from_proc_cmdline(void) {
- char *w, *state;
_cleanup_free_ char *what = NULL, *type = NULL, *opts = NULL, *line = NULL;
+ char *w, *state;
int r;
size_t l;
- bool wait = false;
r = read_one_line_file("/proc/cmdline", &line);
if (r < 0) {
@@ -446,15 +499,15 @@ static int parse_new_root_from_proc_cmdline(void) {
return 0;
}
- opts = strdup("defaults");
+ opts = strdup("ro");
type = strdup("auto");
if (!opts || !type)
return log_oom();
- /* root= and roofstype= may occur more than once, the last instance should take precedence.
- * In the case of multiple rootflags= the arguments should be concatenated */
+ /* root= and roofstype= may occur more than once, the last instance should take precedence.
+ * In the case of multiple rootflags= the arguments should be concatenated */
FOREACH_WORD_QUOTED(w, l, line, state) {
- char *word, *tmp_word;
+ _cleanup_free_ char *word;
word = strndup(w, l);
if (!word)
@@ -473,41 +526,46 @@ static int parse_new_root_from_proc_cmdline(void) {
return log_oom();
} else if (startswith(word, "rootflags=")) {
- tmp_word = opts;
- opts = strjoin(opts, ",", word + 10, NULL);
- free(tmp_word);
- if (!opts)
+ char *o;
+
+ o = strjoin(opts, ",", word + 10, NULL);
+ if (!o)
return log_oom();
+ free(opts);
+ opts = o;
+
} else if (streq(word, "ro") || streq(word, "rw")) {
- tmp_word = opts;
- opts = strjoin(opts, ",", word, NULL);
- free(tmp_word);
- if (!opts)
- return log_oom();
+ char *o;
- } else if (streq(word, "rootwait"))
- wait = true;
+ o = strjoin(opts, ",", word, NULL);
+ if (!o)
+ return log_oom();
- free(word);
+ free(opts);
+ opts = o;
+ }
}
- if (what) {
+ if (!what) {
+ log_debug("Could not find a root= entry on the kernel commandline.");
+ return 0;
+ }
- log_debug("Found entry what=%s where=/new_root type=%s", what, type);
- r = add_mount(what, "/new_root", type, opts, 0, wait, false, false,
- false, false, false, "/proc/cmdline");
+ if (what[0] != '/') {
+ log_debug("Skipping entry what=%s where=/sysroot type=%s", what, type);
+ return 0;
+ }
- if (r < 0)
- return r;
- } else
- log_error("Could not find a root= entry on the kernel commandline.");
+ log_debug("Found entry what=%s where=/sysroot type=%s", what, type);
+ r = add_mount(what, "/sysroot", type, opts, 0, false, false, false,
+ false, NULL, NULL, NULL, SPECIAL_INITRD_ROOT_FS_TARGET, "/proc/cmdline");
- return 0;
+ return (r < 0) ? r : 0;
}
static int parse_proc_cmdline(void) {
- char _cleanup_free_ *line = NULL;
+ _cleanup_free_ char *line = NULL;
char *w, *state;
int r;
size_t l;
@@ -522,7 +580,7 @@ static int parse_proc_cmdline(void) {
}
FOREACH_WORD_QUOTED(w, l, line, state) {
- char _cleanup_free_ *word = NULL;
+ _cleanup_free_ char *word = NULL;
word = strndup(w, l);
if (!word)
@@ -556,7 +614,7 @@ static int parse_proc_cmdline(void) {
}
int main(int argc, char *argv[]) {
- int r, k = 0;
+ int r = 0, k, l = 0;
if (argc > 1 && argc != 4) {
log_error("This program takes three or no arguments.");
@@ -576,12 +634,15 @@ int main(int argc, char *argv[]) {
return EXIT_FAILURE;
if (in_initrd())
- k = parse_new_root_from_proc_cmdline();
+ r = parse_new_root_from_proc_cmdline();
if (!arg_enabled)
- return EXIT_SUCCESS;
+ return (r < 0) ? EXIT_FAILURE : EXIT_SUCCESS;
+
+ k = parse_fstab(NULL, false);
- r = parse_fstab();
+ if (in_initrd())
+ l = parse_fstab("/sysroot", true);
- return (r < 0) || (k < 0) ? EXIT_FAILURE : EXIT_SUCCESS;
+ return (r < 0) || (k < 0) || (l < 0) ? EXIT_FAILURE : EXIT_SUCCESS;
}
diff --git a/src/getty-generator/getty-generator.c b/src/getty-generator/getty-generator.c
index cb38f21052..4b7a60a4ec 100644
--- a/src/getty-generator/getty-generator.c
+++ b/src/getty-generator/getty-generator.c
@@ -28,6 +28,7 @@
#include "mkdir.h"
#include "unit-name.h"
#include "virt.h"
+#include "fileio.h"
static const char *arg_dest = "/tmp";
@@ -54,7 +55,7 @@ static int add_symlink(const char *fservice, const char *tservice) {
/* In case console=hvc0 is passed this will very likely result in EEXIST */
r = 0;
else {
- log_error("Failed to create symlink from %s to %s: %m", from, to);
+ log_error("Failed to create symlink %s: %m", to);
r = -errno;
}
}
diff --git a/src/hostname/hostnamectl.c b/src/hostname/hostnamectl.c
index ff1f091776..064581a31c 100644
--- a/src/hostname/hostnamectl.c
+++ b/src/hostname/hostnamectl.c
@@ -36,6 +36,7 @@
#include "strv.h"
#include "sd-id128.h"
#include "virt.h"
+#include "fileio.h"
static enum transport {
TRANSPORT_NORMAL,
@@ -78,14 +79,18 @@ static void print_status_info(StatusInfo *i) {
printf(" Static hostname: %s\n",
strna(i->static_hostname));
- if (!streq_ptr(i->hostname, i->static_hostname))
+ if (!isempty(i->pretty_hostname) &&
+ !streq_ptr(i->pretty_hostname, i->static_hostname))
+ printf(" Pretty hostname: %s\n",
+ strna(i->pretty_hostname));
+
+ if (!isempty(i->hostname) &&
+ !streq_ptr(i->hostname, i->static_hostname))
printf("Transient hostname: %s\n",
strna(i->hostname));
- printf(" Pretty hostname: %s\n"
- " Icon name: %s\n"
+ printf(" Icon name: %s\n"
" Chassis: %s\n",
- strna(i->pretty_hostname),
strna(i->icon_name),
strna(i->chassis));
@@ -151,7 +156,7 @@ static int show_status(DBusConnection *bus, char **args, unsigned n) {
const char *interface = "";
int r;
DBusMessageIter iter, sub, sub2, sub3;
- StatusInfo info;
+ StatusInfo info = {};
assert(args);
@@ -175,7 +180,6 @@ static int show_status(DBusConnection *bus, char **args, unsigned n) {
return -EIO;
}
- zero(info);
dbus_message_iter_recurse(&iter, &sub);
while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
@@ -213,26 +217,6 @@ static int show_status(DBusConnection *bus, char **args, unsigned n) {
return 0;
}
-static char* hostname_simplify(char *s) {
- char *p, *d;
-
- for (p = s, d = s; *p; p++) {
- if ((*p >= 'a' && *p <= 'z') ||
- (*p >= '0' && *p <= '9') ||
- *p == '-' || *p == '_')
- *(d++) = *p;
- else if (*p >= 'A' && *p <= 'Z')
- *(d++) = *p - 'A' + 'a';
- else if (*p == ' ')
- *(d++) = '-';
- }
-
- *d = 0;
-
- strshorten(s, HOST_NAME_MAX);
- return s;
-}
-
static int set_hostname(DBusConnection *bus, char **args, unsigned n) {
_cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
dbus_bool_t interactive = true;
@@ -246,6 +230,27 @@ static int set_hostname(DBusConnection *bus, char **args, unsigned n) {
polkit_agent_open_if_enabled();
if (arg_set_pretty) {
+ const char *p;
+
+ /* If the passed hostname is already valid, then
+ * assume the user doesn't know anything about pretty
+ * hostnames, so let's unset the pretty hostname, and
+ * just set the passed hostname as static/dynamic
+ * hostname. */
+
+ h = strdup(hostname);
+ if (!h)
+ return log_oom();
+
+ hostname_cleanup(h, true);
+
+ if (arg_set_static && streq(h, hostname))
+ p = "";
+ else {
+ p = hostname;
+ hostname = h;
+ }
+
r = bus_method_call_with_reply(
bus,
"org.freedesktop.hostname1",
@@ -254,17 +259,14 @@ static int set_hostname(DBusConnection *bus, char **args, unsigned n) {
"SetPrettyHostname",
&reply,
NULL,
- DBUS_TYPE_STRING, &hostname,
+ DBUS_TYPE_STRING, &p,
DBUS_TYPE_BOOLEAN, &interactive,
DBUS_TYPE_INVALID);
if (r < 0)
return r;
- h = strdup(hostname);
- if (!h)
- return log_oom();
-
- hostname = hostname_simplify(h);
+ dbus_message_unref(reply);
+ reply = NULL;
}
if (arg_set_static) {
@@ -282,6 +284,9 @@ static int set_hostname(DBusConnection *bus, char **args, unsigned n) {
if (r < 0)
return r;
+
+ dbus_message_unref(reply);
+ reply = NULL;
}
if (arg_set_transient) {
diff --git a/src/hostname/hostnamed.c b/src/hostname/hostnamed.c
index 92b150bfc9..0437e33a66 100644
--- a/src/hostname/hostnamed.c
+++ b/src/hostname/hostnamed.c
@@ -32,6 +32,9 @@
#include "polkit.h"
#include "def.h"
#include "virt.h"
+#include "env-util.h"
+#include "fileio-label.h"
+#include "label.h"
#define INTERFACE \
" <interface name=\"org.freedesktop.hostname1\">\n" \
@@ -286,8 +289,7 @@ static int write_data_static_hostname(void) {
return 0;
}
-
- return write_one_line_file_atomic("/etc/hostname", data[PROP_STATIC_HOSTNAME]);
+ return write_string_file_atomic_label("/etc/hostname", data[PROP_STATIC_HOSTNAME]);
}
static int write_data_other(void) {
@@ -301,7 +303,7 @@ static int write_data_other(void) {
char **l = NULL;
int r, p;
- r = load_env_file("/etc/machine-info", &l);
+ r = load_env_file("/etc/machine-info", NULL, &l);
if (r < 0 && r != -ENOENT)
return r;
@@ -337,7 +339,7 @@ static int write_data_other(void) {
return 0;
}
- r = write_env_file("/etc/machine-info", l);
+ r = write_env_file_label("/etc/machine-info", l);
strv_free(l);
return r;
@@ -442,7 +444,7 @@ static DBusHandlerResult hostname_message_handler(
return bus_send_error_reply(connection, message, NULL, r);
}
- log_info("Changed host name to '%s'", strempty(data[PROP_HOSTNAME]));
+ log_info("Changed host name to '%s'", strna(data[PROP_HOSTNAME]));
changed = bus_properties_changed_new(
"/org/freedesktop/hostname1",
@@ -496,7 +498,7 @@ static DBusHandlerResult hostname_message_handler(
return bus_send_error_reply(connection, message, NULL, r);
}
- log_info("Changed static host name to '%s'", strempty(data[PROP_STATIC_HOSTNAME]));
+ log_info("Changed static host name to '%s'", strna(data[PROP_STATIC_HOSTNAME]));
changed = bus_properties_changed_new(
"/org/freedesktop/hostname1",
@@ -551,7 +553,7 @@ static DBusHandlerResult hostname_message_handler(
* safe than sorry */
if (k == PROP_ICON_NAME && !filename_is_safe(name))
return bus_send_error_reply(connection, message, NULL, -EINVAL);
- if (k == PROP_PRETTY_HOSTNAME && !string_is_safe(name))
+ if (k == PROP_PRETTY_HOSTNAME && string_has_cc(name))
return bus_send_error_reply(connection, message, NULL, -EINVAL);
if (k == PROP_CHASSIS && !valid_chassis(name))
return bus_send_error_reply(connection, message, NULL, -EINVAL);
@@ -572,7 +574,7 @@ static DBusHandlerResult hostname_message_handler(
log_info("Changed %s to '%s'",
k == PROP_PRETTY_HOSTNAME ? "pretty host name" :
- k == PROP_CHASSIS ? "chassis" : "icon name", strempty(data[k]));
+ k == PROP_CHASSIS ? "chassis" : "icon name", strna(data[k]));
changed = bus_properties_changed_new(
"/org/freedesktop/hostname1",
@@ -590,7 +592,7 @@ static DBusHandlerResult hostname_message_handler(
if (!reply)
goto oom;
- if (!dbus_connection_send(connection, reply, NULL))
+ if (!bus_maybe_send_reply(connection, message, reply))
goto oom;
dbus_message_unref(reply);
@@ -682,6 +684,7 @@ int main(int argc, char *argv[]) {
log_open();
umask(0022);
+ label_init("/etc");
if (argc == 2 && streq(argv[1], "--introspect")) {
fputs(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
diff --git a/src/initctl/initctl.c b/src/initctl/initctl.c
index 0eb008d9e6..735f1e1450 100644
--- a/src/initctl/initctl.c
+++ b/src/initctl/initctl.c
@@ -290,7 +290,8 @@ static int server_init(Server *s, unsigned n_sockets) {
zero(*s);
- if ((s->epoll_fd = epoll_create1(EPOLL_CLOEXEC)) < 0) {
+ s->epoll_fd = epoll_create1(EPOLL_CLOEXEC);
+ if (s->epoll_fd < 0) {
r = -errno;
log_error("Failed to create epoll object: %s", strerror(errno));
goto fail;
@@ -303,8 +304,10 @@ static int server_init(Server *s, unsigned n_sockets) {
fd = SD_LISTEN_FDS_START+i;
- if ((r = sd_is_fifo(fd, NULL)) < 0) {
- log_error("Failed to determine file descriptor type: %s", strerror(-r));
+ r = sd_is_fifo(fd, NULL);
+ if (r < 0) {
+ log_error("Failed to determine file descriptor type: %s",
+ strerror(-r));
goto fail;
}
@@ -314,9 +317,11 @@ static int server_init(Server *s, unsigned n_sockets) {
goto fail;
}
- if (!(f = new0(Fifo, 1))) {
+ f = new0(Fifo, 1);
+ if (!f) {
r = -ENOMEM;
- log_error("Failed to create fifo object: %s", strerror(errno));
+ log_error("Failed to create fifo object: %s",
+ strerror(errno));
goto fail;
}
@@ -328,7 +333,8 @@ static int server_init(Server *s, unsigned n_sockets) {
if (epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0) {
r = -errno;
fifo_free(f);
- log_error("Failed to add fifo fd to epoll object: %s", strerror(errno));
+ log_error("Failed to add fifo fd to epoll object: %s",
+ strerror(errno));
goto fail;
}
@@ -339,7 +345,9 @@ static int server_init(Server *s, unsigned n_sockets) {
}
if (bus_connect(DBUS_BUS_SYSTEM, &s->bus, NULL, &error) < 0) {
- log_error("Failed to get D-Bus connection: %s", bus_error_message(&error));
+ log_error("Failed to get D-Bus connection: %s",
+ bus_error_message(&error));
+ r = -EIO;
goto fail;
}
diff --git a/src/journal/cat.c b/src/journal/cat.c
index a95392ccb0..ea61578353 100644
--- a/src/journal/cat.c
+++ b/src/journal/cat.c
@@ -25,7 +25,7 @@
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
-#include <sys/fcntl.h>
+#include <fcntl.h>
#include <systemd/sd-journal.h>
diff --git a/src/journal/catalog.c b/src/journal/catalog.c
index 3735ad9213..7738d243a5 100644
--- a/src/journal/catalog.c
+++ b/src/journal/catalog.c
@@ -26,6 +26,7 @@
#include <string.h>
#include <sys/mman.h>
#include <locale.h>
+#include <libgen.h>
#include "util.h"
#include "log.h"
@@ -34,11 +35,12 @@
#include "hashmap.h"
#include "strv.h"
#include "strbuf.h"
+#include "strxcpyx.h"
#include "conf-files.h"
#include "mkdir.h"
#include "catalog.h"
-static const char * const conf_file_dirs[] = {
+const char * const catalog_file_dirs[] = {
"/usr/local/lib/systemd/catalog/",
"/usr/lib/systemd/catalog/",
NULL
@@ -61,7 +63,7 @@ typedef struct CatalogItem {
le64_t offset;
} CatalogItem;
-static unsigned catalog_hash_func(const void *p) {
+unsigned catalog_hash_func(const void *p) {
const CatalogItem *i = p;
assert_cc(sizeof(unsigned) == sizeof(uint8_t)*4);
@@ -85,7 +87,7 @@ static unsigned catalog_hash_func(const void *p) {
string_hash_func(i->language);
}
-static int catalog_compare_func(const void *a, const void *b) {
+int catalog_compare_func(const void *a, const void *b) {
const CatalogItem *i = a, *j = b;
unsigned k;
@@ -96,7 +98,7 @@ static int catalog_compare_func(const void *a, const void *b) {
return 1;
}
- return strncmp(i->language, j->language, sizeof(i->language));
+ return strcmp(i->language, j->language);
}
static int finish_item(
@@ -123,12 +125,13 @@ static int finish_item(
return log_oom();
i->id = id;
- strncpy(i->language, language, sizeof(i->language));
+ strscpy(i->language, sizeof(i->language), language);
i->offset = htole64((uint64_t) offset);
r = hashmap_put(h, i, i);
if (r == EEXIST) {
- log_warning("Duplicate entry for " SD_ID128_FORMAT_STR ".%s, ignoring.", SD_ID128_FORMAT_VAL(id), language ? language : "C");
+ log_warning("Duplicate entry for " SD_ID128_FORMAT_STR ".%s, ignoring.",
+ SD_ID128_FORMAT_VAL(id), language ? language : "C");
free(i);
return 0;
}
@@ -136,7 +139,7 @@ static int finish_item(
return 0;
}
-static int import_file(Hashmap *h, struct strbuf *sb, const char *path) {
+int catalog_import_file(Hashmap *h, struct strbuf *sb, const char *path) {
_cleanup_fclose_ FILE *f = NULL;
_cleanup_free_ char *payload = NULL;
unsigned n = 0;
@@ -177,7 +180,7 @@ static int import_file(Hashmap *h, struct strbuf *sb, const char *path) {
continue;
}
- if (strchr(COMMENTS, line[0]))
+ if (strchr(COMMENTS "\n", line[0]))
continue;
if (empty_line &&
@@ -185,15 +188,15 @@ static int import_file(Hashmap *h, struct strbuf *sb, const char *path) {
line[0] == '-' &&
line[1] == '-' &&
line[2] == ' ' &&
- (line[2+1+32] == ' ' || line[2+1+32] == 0)) {
+ (line[2+1+32] == ' ' || line[2+1+32] == '\0')) {
bool with_language;
sd_id128_t jd;
/* New entry */
- with_language = line[2+1+32] != 0;
- line[2+1+32] = 0;
+ with_language = line[2+1+32] != '\0';
+ line[2+1+32] = '\0';
if (sd_id128_from_string(line + 2 + 1, &jd) >= 0) {
@@ -211,21 +214,21 @@ static int import_file(Hashmap *h, struct strbuf *sb, const char *path) {
log_error("[%s:%u] Language too short.", path, n);
return -EINVAL;
}
- if (c > sizeof(language)) {
+ if (c > sizeof(language) - 1) {
log_error("[%s:%u] language too long.", path, n);
return -EINVAL;
}
- strncpy(language, t, sizeof(language));
+ strscpy(language, sizeof(language), t);
} else
- zero(language);
+ language[0] = '\0';
got_id = true;
empty_line = false;
id = jd;
if (payload)
- payload[0] = 0;
+ payload[0] = '\0';
continue;
}
@@ -269,34 +272,99 @@ static int import_file(Hashmap *h, struct strbuf *sb, const char *path) {
return 0;
}
-#define CATALOG_DATABASE CATALOG_PATH "/database"
+static long write_catalog(const char *database, Hashmap *h, struct strbuf *sb,
+ CatalogItem *items, size_t n) {
+ CatalogHeader header;
+ _cleanup_fclose_ FILE *w = NULL;
+ int r;
+ _cleanup_free_ char *d, *p = NULL;
+ size_t k;
+
+ d = dirname_malloc(database);
+ if (!d)
+ return log_oom();
+
+ r = mkdir_p(d, 0775);
+ if (r < 0) {
+ log_error("Recursive mkdir %s: %s", d, strerror(-r));
+ return r;
+ }
+
+ r = fopen_temporary(database, &w, &p);
+ if (r < 0) {
+ log_error("Failed to open database for writing: %s: %s",
+ database, strerror(-r));
+ return r;
+ }
+
+ zero(header);
+ memcpy(header.signature, CATALOG_SIGNATURE, sizeof(header.signature));
+ header.header_size = htole64(ALIGN_TO(sizeof(CatalogHeader), 8));
+ header.catalog_item_size = htole64(sizeof(CatalogItem));
+ header.n_items = htole64(hashmap_size(h));
+
+ r = -EIO;
-int catalog_update(void) {
+ k = fwrite(&header, 1, sizeof(header), w);
+ if (k != sizeof(header)) {
+ log_error("%s: failed to write header.", p);
+ goto error;
+ }
+
+ k = fwrite(items, 1, n * sizeof(CatalogItem), w);
+ if (k != n * sizeof(CatalogItem)) {
+ log_error("%s: failed to write database.", p);
+ goto error;
+ }
+
+ k = fwrite(sb->buf, 1, sb->len, w);
+ if (k != sb->len) {
+ log_error("%s: failed to write strings.", p);
+ goto error;
+ }
+
+ fflush(w);
+
+ if (ferror(w)) {
+ log_error("%s: failed to write database.", p);
+ goto error;
+ }
+
+ fchmod(fileno(w), 0644);
+
+ if (rename(p, database) < 0) {
+ log_error("rename (%s -> %s) failed: %m", p, database);
+ r = -errno;
+ goto error;
+ }
+
+ return ftell(w);
+
+error:
+ unlink(p);
+ return r;
+}
+
+int catalog_update(const char* database, const char* root, const char* const* dirs) {
_cleanup_strv_free_ char **files = NULL;
- _cleanup_fclose_ FILE *w = NULL;
- _cleanup_free_ char *p = NULL;
char **f;
Hashmap *h;
struct strbuf *sb = NULL;
_cleanup_free_ CatalogItem *items = NULL;
CatalogItem *i;
- CatalogHeader header;
- size_t k;
Iterator j;
unsigned n;
- int r;
+ long r;
h = hashmap_new(catalog_hash_func, catalog_compare_func);
- if (!h)
- return -ENOMEM;
-
sb = strbuf_new();
- if (!sb) {
+
+ if (!h || !sb) {
r = log_oom();
goto finish;
}
- r = conf_files_list_strv(&files, ".catalog", (const char **) conf_file_dirs);
+ r = conf_files_list_strv(&files, ".catalog", root, dirs);
if (r < 0) {
log_error("Failed to get catalog files: %s", strerror(-r));
goto finish;
@@ -304,7 +372,7 @@ int catalog_update(void) {
STRV_FOREACH(f, files) {
log_debug("reading file '%s'", *f);
- import_file(h, sb, *f);
+ catalog_import_file(h, sb, *f);
}
if (hashmap_size(h) <= 0) {
@@ -324,86 +392,34 @@ int catalog_update(void) {
n = 0;
HASHMAP_FOREACH(i, h, j) {
- log_debug("Found " SD_ID128_FORMAT_STR ", language %s", SD_ID128_FORMAT_VAL(i->id), isempty(i->language) ? "C" : i->language);
+ log_debug("Found " SD_ID128_FORMAT_STR ", language %s",
+ SD_ID128_FORMAT_VAL(i->id),
+ isempty(i->language) ? "C" : i->language);
items[n++] = *i;
}
assert(n == hashmap_size(h));
qsort(items, n, sizeof(CatalogItem), catalog_compare_func);
- r = mkdir_p(CATALOG_PATH, 0775);
- if (r < 0) {
- log_error("Recursive mkdir %s: %s", CATALOG_PATH, strerror(-r));
- goto finish;
- }
-
- r = fopen_temporary(CATALOG_DATABASE, &w, &p);
- if (r < 0) {
- log_error("Failed to open database for writing: %s: %s",
- CATALOG_DATABASE, strerror(-r));
- goto finish;
- }
-
- zero(header);
- memcpy(header.signature, CATALOG_SIGNATURE, sizeof(header.signature));
- header.header_size = htole64(ALIGN_TO(sizeof(CatalogHeader), 8));
- header.catalog_item_size = htole64(sizeof(CatalogItem));
- header.n_items = htole64(hashmap_size(h));
-
- k = fwrite(&header, 1, sizeof(header), w);
- if (k != sizeof(header)) {
- log_error("%s: failed to write header.", p);
- goto finish;
- }
-
- k = fwrite(items, 1, n * sizeof(CatalogItem), w);
- if (k != n * sizeof(CatalogItem)) {
- log_error("%s: failed to write database.", p);
- goto finish;
- }
-
- k = fwrite(sb->buf, 1, sb->len, w);
- if (k != sb->len) {
- log_error("%s: failed to write strings.", p);
- goto finish;
- }
-
- fflush(w);
-
- if (ferror(w)) {
- log_error("%s: failed to write database.", p);
- goto finish;
- }
-
- fchmod(fileno(w), 0644);
-
- if (rename(p, CATALOG_DATABASE) < 0) {
- log_error("rename (%s -> %s) failed: %m", p, CATALOG_DATABASE);
- r = -errno;
- goto finish;
- }
-
- log_debug("%s: wrote %u items, with %zu bytes of strings, %ld total size.",
- CATALOG_DATABASE, n, sb->len, ftell(w));
-
- free(p);
- p = NULL;
+ r = write_catalog(database, h, sb, items, n);
+ if (r < 0)
+ log_error("Failed to write %s: %s", database, strerror(-r));
+ else
+ log_debug("%s: wrote %u items, with %zu bytes of strings, %ld total size.",
+ database, n, sb->len, r);
r = 0;
finish:
- hashmap_free_free(h);
-
+ if (h)
+ hashmap_free_free(h);
if (sb)
strbuf_cleanup(sb);
- if (p)
- unlink(p);
-
- return r;
+ return r < 0 ? r : 0;
}
-static int open_mmap(int *_fd, struct stat *_st, void **_p) {
+static int open_mmap(const char *database, int *_fd, struct stat *_st, void **_p) {
const CatalogHeader *h;
int fd;
void *p;
@@ -413,7 +429,7 @@ static int open_mmap(int *_fd, struct stat *_st, void **_p) {
assert(_st);
assert(_p);
- fd = open(CATALOG_DATABASE, O_RDONLY|O_CLOEXEC);
+ fd = open(database, O_RDONLY|O_CLOEXEC);
if (fd < 0)
return -errno;
@@ -491,7 +507,7 @@ static const char *find_id(void *p, sd_id128_t id) {
le64toh(f->offset);
}
-int catalog_get(sd_id128_t id, char **_text) {
+int catalog_get(const char* database, sd_id128_t id, char **_text) {
_cleanup_close_ int fd = -1;
void *p = NULL;
struct stat st;
@@ -501,7 +517,7 @@ int catalog_get(sd_id128_t id, char **_text) {
assert(_text);
- r = open_mmap(&fd, &st, &p);
+ r = open_mmap(database, &fd, &st, &p);
if (r < 0)
return r;
@@ -551,7 +567,23 @@ static char *find_header(const char *s, const char *header) {
}
}
-int catalog_list(FILE *f) {
+static void dump_catalog_entry(FILE *f, sd_id128_t id, const char *s, bool oneline) {
+ if (oneline) {
+ _cleanup_free_ char *subject = NULL, *defined_by = NULL;
+
+ subject = find_header(s, "Subject:");
+ defined_by = find_header(s, "Defined-By:");
+
+ fprintf(f, SD_ID128_FORMAT_STR " %s: %s\n",
+ SD_ID128_FORMAT_VAL(id),
+ strna(defined_by), strna(subject));
+ } else
+ fprintf(f, "-- " SD_ID128_FORMAT_STR "\n%s\n",
+ SD_ID128_FORMAT_VAL(id), s);
+}
+
+
+int catalog_list(FILE *f, const char *database, bool oneline) {
_cleanup_close_ int fd = -1;
void *p = NULL;
struct stat st;
@@ -562,7 +594,7 @@ int catalog_list(FILE *f) {
sd_id128_t last_id;
bool last_id_set = false;
- r = open_mmap(&fd, &st, &p);
+ r = open_mmap(database, &fd, &st, &p);
if (r < 0)
return r;
@@ -571,17 +603,13 @@ int catalog_list(FILE *f) {
for (n = 0; n < le64toh(h->n_items); n++) {
const char *s;
- _cleanup_free_ char *subject = NULL, *defined_by = NULL;
if (last_id_set && sd_id128_equal(last_id, items[n].id))
continue;
assert_se(s = find_id(p, items[n].id));
- subject = find_header(s, "Subject:");
- defined_by = find_header(s, "Defined-By:");
-
- fprintf(f, SD_ID128_FORMAT_STR " %s: %s\n", SD_ID128_FORMAT_VAL(items[n].id), strna(defined_by), strna(subject));
+ dump_catalog_entry(f, items[n].id, s, oneline);
last_id_set = true;
last_id = items[n].id;
@@ -591,3 +619,37 @@ int catalog_list(FILE *f) {
return 0;
}
+
+int catalog_list_items(FILE *f, const char *database, bool oneline, char **items) {
+ char **item;
+ int r = 0;
+
+ STRV_FOREACH(item, items) {
+ sd_id128_t id;
+ int k;
+ _cleanup_free_ char *msg = NULL;
+
+ k = sd_id128_from_string(*item, &id);
+ if (k < 0) {
+ log_error("Failed to parse id128 '%s': %s",
+ *item, strerror(-k));
+ if (r == 0)
+ r = k;
+ continue;
+ }
+
+ k = catalog_get(database, id, &msg);
+ if (k < 0) {
+ log_full(k == -ENOENT ? LOG_NOTICE : LOG_ERR,
+ "Failed to retrieve catalog entry for '%s': %s",
+ *item, strerror(-k));
+ if (r == 0)
+ r = k;
+ continue;
+ }
+
+ dump_catalog_entry(f, id, msg, oneline);
+ }
+
+ return r;
+}
diff --git a/src/journal/catalog.h b/src/journal/catalog.h
index 9add773c95..24a2d0b553 100644
--- a/src/journal/catalog.h
+++ b/src/journal/catalog.h
@@ -21,8 +21,17 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "sd-id128.h"
+#include <stdbool.h>
-int catalog_update(void);
-int catalog_get(sd_id128_t id, char **data);
-int catalog_list(FILE *f);
+#include "sd-id128.h"
+#include "hashmap.h"
+#include "strbuf.h"
+
+int catalog_import_file(Hashmap *h, struct strbuf *sb, const char *path);
+unsigned catalog_hash_func(const void *p);
+int catalog_compare_func(const void *a, const void *b) _pure_;
+int catalog_update(const char* database, const char* root, const char* const* dirs);
+int catalog_get(const char* database, sd_id128_t id, char **data);
+int catalog_list(FILE *f, const char* database, bool oneline);
+int catalog_list_items(FILE *f, const char* database, bool oneline, char **items);
+extern const char * const catalog_file_dirs[];
diff --git a/src/journal/coredump.c b/src/journal/coredump.c
index a507fc65f8..fd03e389bb 100644
--- a/src/journal/coredump.c
+++ b/src/journal/coredump.c
@@ -32,11 +32,16 @@
#include "log.h"
#include "util.h"
+#include "macro.h"
#include "mkdir.h"
#include "special.h"
#include "cgroup-util.h"
-#define COREDUMP_MAX (24*1024*1024)
+/* Few programs have less than 3MiB resident */
+#define COREDUMP_MIN_START (3*1024*1024)
+/* Make sure to not make this larger than the maximum journal entry
+ * size. See ENTRY_SIZE_MAX in journald-native.c. */
+#define COREDUMP_MAX (768*1024*1024)
enum {
ARG_PID = 1,
@@ -49,8 +54,7 @@ enum {
};
static int divert_coredump(void) {
- FILE *f;
- int r;
+ _cleanup_fclose_ FILE *f = NULL;
log_info("Detected coredump of the journal daemon itself, diverting coredump to /var/lib/systemd/coredump/.");
@@ -70,19 +74,16 @@ static int divert_coredump(void) {
if (l <= 0) {
if (ferror(f)) {
log_error("Failed to read coredump: %m");
- r = -errno;
- goto finish;
+ return -errno;
}
- r = 0;
break;
}
q = fwrite(buffer, 1, l, f);
if (q != l) {
log_error("Failed to write coredump: %m");
- r = -errno;
- goto finish;
+ return -errno;
}
}
@@ -90,25 +91,24 @@ static int divert_coredump(void) {
if (ferror(f)) {
log_error("Failed to write coredump: %m");
- r = -errno;
+ return -errno;
}
-finish:
- fclose(f);
- return r;
+ return 0;
}
int main(int argc, char* argv[]) {
int r, j = 0;
- char *p = NULL;
+ char *t;
ssize_t n;
pid_t pid;
uid_t uid;
gid_t gid;
struct iovec iovec[14];
- char *core_pid = NULL, *core_uid = NULL, *core_gid = NULL, *core_signal = NULL,
+ size_t coredump_bufsize, coredump_size;
+ _cleanup_free_ char *core_pid = NULL, *core_uid = NULL, *core_gid = NULL, *core_signal = NULL,
*core_timestamp = NULL, *core_comm = NULL, *core_exe = NULL, *core_unit = NULL,
- *core_session = NULL, *core_message = NULL, *core_cmdline = NULL, *t;
+ *core_session = NULL, *core_message = NULL, *core_cmdline = NULL, *coredump_data = NULL;
prctl(PR_SET_DUMPABLE, 0);
@@ -143,11 +143,11 @@ int main(int argc, char* argv[]) {
}
core_unit = strappend("COREDUMP_UNIT=", t);
- free(t);
+ } else if (cg_pid_get_user_unit(pid, &t) >= 0)
+ core_unit = strappend("COREDUMP_USER_UNIT=", t);
- if (core_unit)
- IOVEC_SET_STRING(iovec[j++], core_unit);
- }
+ if (core_unit)
+ IOVEC_SET_STRING(iovec[j++], core_unit);
/* OK, now we know it's not the journal, hence make use of
* it */
@@ -205,7 +205,7 @@ int main(int argc, char* argv[]) {
IOVEC_SET_STRING(iovec[j++], core_exe);
}
- if (get_process_cmdline(pid, LINE_MAX, false, &t) >= 0) {
+ if (get_process_cmdline(pid, 0, false, &t) >= 0) {
core_cmdline = strappend("COREDUMP_CMDLINE=", t);
free(t);
@@ -237,23 +237,35 @@ int main(int argc, char* argv[]) {
goto finish;
}
- p = malloc(9 + COREDUMP_MAX);
- if (!p) {
+ coredump_bufsize = COREDUMP_MIN_START;
+ coredump_data = malloc(coredump_bufsize);
+ if (!coredump_data) {
r = log_oom();
goto finish;
}
- memcpy(p, "COREDUMP=", 9);
+ memcpy(coredump_data, "COREDUMP=", 9);
+ coredump_size = 9;
- n = loop_read(STDIN_FILENO, p + 9, COREDUMP_MAX, false);
- if (n < 0) {
- log_error("Failed to read core dump data: %s", strerror(-n));
- r = (int) n;
- goto finish;
+ for (;;) {
+ n = loop_read(STDIN_FILENO, coredump_data + coredump_size,
+ coredump_bufsize - coredump_size, false);
+ if (n < 0) {
+ log_error("Failed to read core dump data: %s", strerror(-n));
+ r = (int) n;
+ goto finish;
+ } else if (n == 0)
+ break;
+
+ coredump_size += n;
+ if (!GREEDY_REALLOC(coredump_data, coredump_bufsize, coredump_size + 1)) {
+ r = log_oom();
+ goto finish;
+ }
}
- iovec[j].iov_base = p;
- iovec[j].iov_len = 9 + n;
+ iovec[j].iov_base = coredump_data;
+ iovec[j].iov_len = coredump_size;
j++;
r = sd_journal_sendv(iovec, j);
@@ -261,18 +273,5 @@ int main(int argc, char* argv[]) {
log_error("Failed to send coredump: %s", strerror(-r));
finish:
- free(p);
- free(core_pid);
- free(core_uid);
- free(core_gid);
- free(core_signal);
- free(core_timestamp);
- free(core_comm);
- free(core_exe);
- free(core_cmdline);
- free(core_unit);
- free(core_session);
- free(core_message);
-
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}
diff --git a/src/journal/coredumpctl.c b/src/journal/coredumpctl.c
index b6e558186d..5652c2f91a 100644
--- a/src/journal/coredumpctl.c
+++ b/src/journal/coredumpctl.c
@@ -34,6 +34,8 @@
#include "log.h"
#include "path-util.h"
#include "pager.h"
+#include "macro.h"
+#include "journal-internal.h"
static enum {
ACTION_NONE,
@@ -42,7 +44,6 @@ static enum {
ACTION_GDB,
} arg_action = ACTION_LIST;
-static Set *matches = NULL;
static FILE* output = NULL;
static char* field = NULL;
@@ -67,10 +68,9 @@ static Set *new_matches(void) {
return NULL;
}
- r = set_put(set, tmp);
+ r = set_consume(set, tmp);
if (r < 0) {
log_error("failed to add to set: %s", strerror(-r));
- free(tmp);
set_free(set);
return NULL;
}
@@ -103,7 +103,7 @@ static int add_match(Set *set, const char *match) {
unsigned pid;
const char* prefix;
char *pattern = NULL;
- char _cleanup_free_ *p = NULL;
+ _cleanup_free_ char *p = NULL;
if (strchr(match, '='))
prefix = "";
@@ -124,22 +124,21 @@ static int add_match(Set *set, const char *match) {
if (!pattern)
goto fail;
- r = set_put(set, pattern);
+ log_debug("Adding pattern: %s", pattern);
+ r = set_consume(set, pattern);
if (r < 0) {
- log_error("failed to add pattern '%s': %s",
+ log_error("Failed to add pattern '%s': %s",
pattern, strerror(-r));
goto fail;
}
- log_debug("Added pattern: %s", pattern);
return 0;
fail:
- free(pattern);
- log_error("failed to add match: %s", strerror(-r));
+ log_error("Failed to add match: %s", strerror(-r));
return r;
}
-static int parse_argv(int argc, char *argv[]) {
+static int parse_argv(int argc, char *argv[], Set *matches) {
enum {
ARG_VERSION = 0x100,
ARG_NO_PAGER,
@@ -268,7 +267,7 @@ static int retrieve(const void *data,
}
static void print_field(FILE* file, sd_journal *j) {
- const char _cleanup_free_ *value = NULL;
+ _cleanup_free_ const char *value = NULL;
const void *d;
size_t l;
@@ -281,7 +280,7 @@ static void print_field(FILE* file, sd_journal *j) {
}
static int print_entry(FILE* file, sd_journal *j, int had_legend) {
- const char _cleanup_free_
+ _cleanup_free_ const char
*pid = NULL, *uid = NULL, *gid = NULL,
*sgnl = NULL, *exe = NULL;
const void *d;
@@ -519,10 +518,11 @@ finish:
}
int main(int argc, char *argv[]) {
- sd_journal *j = NULL;
+ _cleanup_journal_close_ sd_journal*j = NULL;
const char* match;
Iterator it;
int r = 0;
+ _cleanup_set_free_free_ Set *matches = NULL;
setlocale(LC_ALL, "");
log_parse_environment();
@@ -534,7 +534,7 @@ int main(int argc, char *argv[]) {
goto end;
}
- r = parse_argv(argc, argv);
+ r = parse_argv(argc, argv, matches);
if (r < 0)
goto end;
@@ -560,7 +560,7 @@ int main(int argc, char *argv[]) {
case ACTION_LIST:
if (!arg_no_pager)
- pager_open();
+ pager_open(false);
r = dump_list(j);
break;
@@ -578,11 +578,6 @@ int main(int argc, char *argv[]) {
}
end:
- if (j)
- sd_journal_close(j);
-
- set_free_free(matches);
-
pager_close();
if (output)
diff --git a/src/journal/fsprg.c b/src/journal/fsprg.c
index 2190b7c796..6817a629c8 100644
--- a/src/journal/fsprg.c
+++ b/src/journal/fsprg.c
@@ -74,7 +74,7 @@ static void uint64_export(void *buf, size_t buflen, uint64_t x) {
((uint8_t*) buf)[7] = (x >> 0) & 0xff;
}
-static uint64_t uint64_import(const void *buf, size_t buflen) {
+_pure_ static uint64_t uint64_import(const void *buf, size_t buflen) {
assert(buflen == 8);
return
(uint64_t)(((uint8_t*) buf)[0]) << 56 |
diff --git a/src/journal/fsprg.h b/src/journal/fsprg.h
index 306ef18d73..150d034828 100644
--- a/src/journal/fsprg.h
+++ b/src/journal/fsprg.h
@@ -28,6 +28,8 @@
#include <sys/types.h>
#include <inttypes.h>
+#include "macro.h"
+
#ifdef __cplusplus
extern "C" {
#endif
@@ -35,9 +37,9 @@ extern "C" {
#define FSPRG_RECOMMENDED_SECPAR 1536
#define FSPRG_RECOMMENDED_SEEDLEN (96/8)
-size_t FSPRG_mskinbytes(unsigned secpar);
-size_t FSPRG_mpkinbytes(unsigned secpar);
-size_t FSPRG_stateinbytes(unsigned secpar);
+size_t FSPRG_mskinbytes(unsigned secpar) _const_;
+size_t FSPRG_mpkinbytes(unsigned secpar) _const_;
+size_t FSPRG_stateinbytes(unsigned secpar) _const_;
/* Setup msk and mpk. Providing seed != NULL makes this algorithm deterministic. */
void FSPRG_GenMK(void *msk, void *mpk, const void *seed, size_t seedlen, unsigned secpar);
@@ -50,7 +52,7 @@ void FSPRG_GenState0(void *state, const void *mpk, const void *seed, size_t seed
void FSPRG_Evolve(void *state);
-uint64_t FSPRG_GetEpoch(const void *state);
+uint64_t FSPRG_GetEpoch(const void *state) _pure_;
/* Seek to any arbitrary state (by providing msk together with seed from GenState0). */
void FSPRG_Seek(void *state, uint64_t epoch, const void *msk, const void *seed, size_t seedlen);
diff --git a/src/journal/journal-file.c b/src/journal/journal-file.c
index 13fc8edea9..38499a6881 100644
--- a/src/journal/journal-file.c
+++ b/src/journal/journal-file.c
@@ -44,7 +44,7 @@
#define COMPRESSION_SIZE_THRESHOLD (512ULL)
/* This is the minimum journal file size */
-#define JOURNAL_FILE_SIZE_MIN (64ULL*1024ULL) /* 64 KiB */
+#define JOURNAL_FILE_SIZE_MIN (4ULL*1024ULL*1024ULL) /* 4 MiB */
/* These are the lower and upper bounds if we deduce the max_use value
* from the file system size */
@@ -68,6 +68,50 @@
/* How many entries to keep in the entry array chain cache at max */
#define CHAIN_CACHE_MAX 20
+int journal_file_set_online(JournalFile *f) {
+ assert(f);
+
+ if (!f->writable)
+ return -EPERM;
+
+ if (!(f->fd >= 0 && f->header))
+ return -EINVAL;
+
+ switch(f->header->state) {
+ case STATE_ONLINE:
+ return 0;
+
+ case STATE_OFFLINE:
+ f->header->state = STATE_ONLINE;
+ fsync(f->fd);
+ return 0;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+int journal_file_set_offline(JournalFile *f) {
+ assert(f);
+
+ if (!f->writable)
+ return -EPERM;
+
+ if (!(f->fd >= 0 && f->header))
+ return -EINVAL;
+
+ if (f->header->state != STATE_ONLINE)
+ return 0;
+
+ fsync(f->fd);
+
+ f->header->state = STATE_OFFLINE;
+
+ fsync(f->fd);
+
+ return 0;
+}
+
void journal_file_close(JournalFile *f) {
assert(f);
@@ -81,16 +125,10 @@ void journal_file_close(JournalFile *f) {
if (f->mmap && f->fd >= 0)
mmap_cache_close_fd(f->mmap, f->fd);
- if (f->writable && f->fd >= 0)
- fdatasync(f->fd);
-
- if (f->header) {
- /* Mark the file offline. Don't override the archived state if it already is set */
- if (f->writable && f->header->state == STATE_ONLINE)
- f->header->state = STATE_OFFLINE;
+ journal_file_set_offline(f);
+ if (f->header)
munmap(f->header, PAGE_ALIGN(sizeof(Header)));
- }
if (f->fd >= 0)
close_nointr_nofail(f->fd);
@@ -177,7 +215,7 @@ static int journal_file_refresh_header(JournalFile *f) {
f->header->boot_id = boot_id;
- f->header->state = STATE_ONLINE;
+ journal_file_set_online(f);
/* Sync the online state to disk */
msync(f->header, PAGE_ALIGN(sizeof(Header)), MS_SYNC);
@@ -457,6 +495,10 @@ int journal_file_append_object(JournalFile *f, int type, uint64_t size, Object *
assert(offset);
assert(ret);
+ r = journal_file_set_online(f);
+ if (r < 0)
+ return r;
+
p = le64toh(f->header->tail_object_offset);
if (p == 0)
p = le64toh(f->header->header_size);
@@ -1267,9 +1309,6 @@ int journal_file_append_entry(JournalFile *f, const dual_timestamp *ts, const st
assert(f);
assert(iovec || n_iovec == 0);
- if (!f->writable)
- return -EPERM;
-
if (!ts) {
dual_timestamp_get(&_ts);
ts = &_ts;
@@ -1286,7 +1325,7 @@ int journal_file_append_entry(JournalFile *f, const dual_timestamp *ts, const st
#endif
/* alloca() can't take 0, hence let's allocate at least one */
- items = alloca(sizeof(EntryItem) * MAX(1, n_iovec));
+ items = alloca(sizeof(EntryItem) * MAX(1u, n_iovec));
for (i = 0; i < n_iovec; i++) {
uint64_t p;
@@ -1673,7 +1712,7 @@ found:
return 1;
}
-static int test_object_offset(JournalFile *f, uint64_t p, uint64_t needle) {
+_pure_ static int test_object_offset(JournalFile *f, uint64_t p, uint64_t needle) {
assert(f);
assert(p > 0);
@@ -1791,6 +1830,17 @@ static int test_object_monotonic(JournalFile *f, uint64_t p, uint64_t needle) {
return TEST_RIGHT;
}
+static inline int find_data_object_by_boot_id(
+ JournalFile *f,
+ sd_id128_t boot_id,
+ Object **o,
+ uint64_t *b) {
+ char t[sizeof("_BOOT_ID=")-1 + 32 + 1] = "_BOOT_ID=";
+
+ sd_id128_to_string(boot_id, t + 9);
+ return journal_file_find_data_object(f, t, sizeof(t) - 1, o, b);
+}
+
int journal_file_move_to_entry_by_monotonic(
JournalFile *f,
sd_id128_t boot_id,
@@ -1799,14 +1849,12 @@ int journal_file_move_to_entry_by_monotonic(
Object **ret,
uint64_t *offset) {
- char t[9+32+1] = "_BOOT_ID=";
Object *o;
int r;
assert(f);
- sd_id128_to_string(boot_id, t + 9);
- r = journal_file_find_data_object(f, t, strlen(t), &o, NULL);
+ r = find_data_object_by_boot_id(f, boot_id, &o, NULL);
if (r < 0)
return r;
if (r == 0)
@@ -2020,7 +2068,6 @@ int journal_file_move_to_entry_by_monotonic_for_data(
direction_t direction,
Object **ret, uint64_t *offset) {
- char t[9+32+1] = "_BOOT_ID=";
Object *o, *d;
int r;
uint64_t b, z;
@@ -2028,8 +2075,7 @@ int journal_file_move_to_entry_by_monotonic_for_data(
assert(f);
/* First, seek by time */
- sd_id128_to_string(boot_id, t + 9);
- r = journal_file_find_data_object(f, t, strlen(t), &o, &b);
+ r = find_data_object_by_boot_id(f, boot_id, &o, &b);
if (r < 0)
return r;
if (r == 0)
@@ -2730,7 +2776,7 @@ void journal_default_metrics(JournalMetrics *m, int fd) {
if (m->keep_free == (uint64_t) -1) {
if (fs_size > 0) {
- m->keep_free = PAGE_ALIGN(fs_size / 20); /* 5% of file system size */
+ m->keep_free = PAGE_ALIGN(fs_size * 3 / 20); /* 15% of file system size */
if (m->keep_free > DEFAULT_KEEP_FREE_UPPER)
m->keep_free = DEFAULT_KEEP_FREE_UPPER;
@@ -2768,7 +2814,6 @@ int journal_file_get_cutoff_realtime_usec(JournalFile *f, usec_t *from, usec_t *
}
int journal_file_get_cutoff_monotonic_usec(JournalFile *f, sd_id128_t boot_id, usec_t *from, usec_t *to) {
- char t[9+32+1] = "_BOOT_ID=";
Object *o;
uint64_t p;
int r;
@@ -2776,9 +2821,7 @@ int journal_file_get_cutoff_monotonic_usec(JournalFile *f, sd_id128_t boot_id, u
assert(f);
assert(from || to);
- sd_id128_to_string(boot_id, t + 9);
-
- r = journal_file_find_data_object(f, t, strlen(t), &o, &p);
+ r = find_data_object_by_boot_id(f, boot_id, &o, &p);
if (r <= 0)
return r;
diff --git a/src/journal/journal-file.h b/src/journal/journal-file.h
index cdbc8e41f6..7b1cd42854 100644
--- a/src/journal/journal-file.h
+++ b/src/journal/journal-file.h
@@ -106,6 +106,8 @@ int journal_file_open(
JournalFile *template,
JournalFile **ret);
+int journal_file_set_offline(JournalFile *f);
+int journal_file_set_online(JournalFile *f);
void journal_file_close(JournalFile *j);
int journal_file_open_reliably(
@@ -148,9 +150,9 @@ static inline bool VALID_EPOCH(uint64_t u) {
int journal_file_move_to_object(JournalFile *f, int type, uint64_t offset, Object **ret);
-uint64_t journal_file_entry_n_items(Object *o);
-uint64_t journal_file_entry_array_n_items(Object *o);
-uint64_t journal_file_hash_table_n_items(Object *o);
+uint64_t journal_file_entry_n_items(Object *o) _pure_;
+uint64_t journal_file_entry_array_n_items(Object *o) _pure_;
+uint64_t journal_file_hash_table_n_items(Object *o) _pure_;
int journal_file_append_object(JournalFile *f, int type, uint64_t size, Object **ret, uint64_t *offset);
int journal_file_append_entry(JournalFile *f, const dual_timestamp *ts, const struct iovec iovec[], unsigned n_iovec, uint64_t *seqno, Object **ret, uint64_t *offset);
diff --git a/src/journal/journal-gatewayd.c b/src/journal/journal-gatewayd.c
index 63d9744776..745f45f932 100644
--- a/src/journal/journal-gatewayd.c
+++ b/src/journal/journal-gatewayd.c
@@ -23,6 +23,7 @@
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
+#include <getopt.h>
#include <microhttpd.h>
@@ -30,8 +31,13 @@
#include "util.h"
#include "sd-journal.h"
#include "sd-daemon.h"
+#include "sd-bus.h"
+#include "bus-message.h"
+#include "bus-internal.h"
#include "logs-show.h"
-#include "virt.h"
+#include "microhttpd-util.h"
+#include "build.h"
+#include "fileio.h"
typedef struct RequestMeta {
sd_journal *journal;
@@ -106,8 +112,7 @@ static int open_journal(RequestMeta *m) {
return sd_journal_open(&m->journal, SD_JOURNAL_LOCAL_ONLY|SD_JOURNAL_SYSTEM_ONLY);
}
-
-static int respond_oom(struct MHD_Connection *connection) {
+static int respond_oom_internal(struct MHD_Connection *connection) {
struct MHD_Response *response;
const char m[] = "Out of memory.\n";
int ret;
@@ -125,6 +130,8 @@ static int respond_oom(struct MHD_Connection *connection) {
return ret;
}
+#define respond_oom(connection) log_oom(), respond_oom_internal(connection)
+
static int respond_error(
struct MHD_Connection *connection,
unsigned code,
@@ -328,7 +335,7 @@ static int request_parse_range(
colon2 = strchr(colon + 1, ':');
if (colon2) {
- char _cleanup_free_ *t;
+ _cleanup_free_ char *t;
t = strndup(colon + 1, colon2 - colon - 1);
if (!t)
@@ -477,18 +484,14 @@ static int request_parse_arguments(
static int request_handler_entries(
struct MHD_Connection *connection,
- void **connection_cls) {
+ void *connection_cls) {
struct MHD_Response *response;
- RequestMeta *m;
+ RequestMeta *m = connection_cls;
int r;
assert(connection);
- assert(connection_cls);
-
- m = request_meta(connection_cls);
- if (!m)
- return respond_oom(connection);
+ assert(m);
r = open_journal(m);
if (r < 0)
@@ -646,15 +649,11 @@ static int request_handler_fields(
void *connection_cls) {
struct MHD_Response *response;
- RequestMeta *m;
+ RequestMeta *m = connection_cls;
int r;
assert(connection);
- assert(connection_cls);
-
- m = request_meta(connection_cls);
- if (!m)
- return respond_oom(connection);
+ assert(m);
r = open_journal(m);
if (r < 0)
@@ -743,24 +742,63 @@ static int request_handler_file(
return ret;
}
+static int get_virtualization(char **v) {
+ _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+ _cleanup_bus_unref_ sd_bus *bus = NULL;
+ const char *t;
+ char *b;
+ int r;
+
+ r = sd_bus_open_system(&bus);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_call_method(
+ bus,
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.DBus.Properties",
+ "Get",
+ NULL,
+ &reply,
+ "ss",
+ "org.freedesktop.systemd1.Manager",
+ "Virtualization");
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_read(reply, "v", "s", &t);
+ if (r < 0)
+ return r;
+
+ if (isempty(t)) {
+ *v = NULL;
+ return 0;
+ }
+
+ b = strdup(t);
+ if (!b)
+ return -ENOMEM;
+
+ *v = b;
+ return 1;
+}
+
static int request_handler_machine(
struct MHD_Connection *connection,
- void **connection_cls) {
+ void *connection_cls) {
struct MHD_Response *response;
- RequestMeta *m;
+ RequestMeta *m = connection_cls;
int r;
_cleanup_free_ char* hostname = NULL, *os_name = NULL;
uint64_t cutoff_from, cutoff_to, usage;
char *json;
sd_id128_t mid, bid;
- const char *v = "bare";
+ _cleanup_free_ char *v = NULL;
assert(connection);
-
- m = request_meta(connection_cls);
- if (!m)
- return respond_oom(connection);
+ assert(m);
r = open_journal(m);
if (r < 0)
@@ -788,7 +826,7 @@ static int request_handler_machine(
parse_env_file("/etc/os-release", NEWLINE, "PRETTY_NAME", &os_name, NULL);
- detect_virtualization(&v);
+ get_virtualization(&v);
r = asprintf(&json,
"{ \"machine_id\" : \"" SD_ID128_FORMAT_STR "\","
@@ -801,9 +839,9 @@ static int request_handler_machine(
"\"cutoff_to_realtime\" : \"%llu\" }\n",
SD_ID128_FORMAT_VAL(mid),
SD_ID128_FORMAT_VAL(bid),
- hostname_cleanup(hostname),
+ hostname_cleanup(hostname, false),
os_name ? os_name : "Linux",
- v,
+ v ? v : "bare",
(unsigned long long) usage,
(unsigned long long) cutoff_from,
(unsigned long long) cutoff_to);
@@ -835,43 +873,146 @@ static int request_handler(
void **connection_cls) {
assert(connection);
+ assert(connection_cls);
assert(url);
assert(method);
if (!streq(method, "GET"))
- return MHD_NO;
+ return respond_error(connection, MHD_HTTP_METHOD_NOT_ACCEPTABLE,
+ "Unsupported method.\n");
+
+
+ if (!*connection_cls) {
+ if (!request_meta(connection_cls))
+ return respond_oom(connection);
+ return MHD_YES;
+ }
if (streq(url, "/"))
return request_handler_redirect(connection, "/browse");
if (streq(url, "/entries"))
- return request_handler_entries(connection, connection_cls);
+ return request_handler_entries(connection, *connection_cls);
if (startswith(url, "/fields/"))
- return request_handler_fields(connection, url + 8, connection_cls);
+ return request_handler_fields(connection, url + 8, *connection_cls);
if (streq(url, "/browse"))
return request_handler_file(connection, DOCUMENT_ROOT "/browse.html", "text/html");
if (streq(url, "/machine"))
- return request_handler_machine(connection, connection_cls);
+ return request_handler_machine(connection, *connection_cls);
return respond_error(connection, MHD_HTTP_NOT_FOUND, "Not found.\n");
}
-int main(int argc, char *argv[]) {
- struct MHD_Daemon *d = NULL;
- int r = EXIT_FAILURE, n;
+static int help(void) {
+
+ printf("%s [OPTIONS...] ...\n\n"
+ "HTTP server for journal events.\n\n"
+ " -h --help Show this help\n"
+ " --version Show package version\n"
+ " --cert=CERT.PEM Specify server certificate in PEM format\n"
+ " --key=KEY.PEM Specify server key in PEM format\n",
+ program_invocation_short_name);
+
+ return 0;
+}
- if (argc > 1) {
+static char *key_pem = NULL;
+static char *cert_pem = NULL;
+
+static int parse_argv(int argc, char *argv[]) {
+ enum {
+ ARG_VERSION = 0x100,
+ ARG_KEY,
+ ARG_CERT,
+ };
+
+ int r, c;
+
+ static const struct option options[] = {
+ { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, ARG_VERSION },
+ { "key", required_argument, NULL, ARG_KEY },
+ { "cert", required_argument, NULL, ARG_CERT },
+ { NULL, 0, NULL, 0 }
+ };
+
+ assert(argc >= 0);
+ assert(argv);
+
+ while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
+ switch(c) {
+ case ARG_VERSION:
+ puts(PACKAGE_STRING);
+ puts(SYSTEMD_FEATURES);
+ return 0;
+
+ case 'h':
+ return help();
+
+ case ARG_KEY:
+ if (key_pem) {
+ log_error("Key file specified twice");
+ return -EINVAL;
+ }
+ r = read_full_file(optarg, &key_pem, NULL);
+ if (r < 0) {
+ log_error("Failed to read key file: %s", strerror(-r));
+ return r;
+ }
+ assert(key_pem);
+ break;
+
+ case ARG_CERT:
+ if (cert_pem) {
+ log_error("Certificate file specified twice");
+ return -EINVAL;
+ }
+ r = read_full_file(optarg, &cert_pem, NULL);
+ if (r < 0) {
+ log_error("Failed to read certificate file: %s", strerror(-r));
+ return r;
+ }
+ assert(cert_pem);
+ break;
+
+ case '?':
+ return -EINVAL;
+
+ default:
+ log_error("Unknown option code %c", c);
+ return -EINVAL;
+ }
+
+ if (optind < argc) {
log_error("This program does not take arguments.");
- goto finish;
+ return -EINVAL;
+ }
+
+ if (!!key_pem != !!cert_pem) {
+ log_error("Certificate and key files must be specified together");
+ return -EINVAL;
}
+ return 1;
+}
+
+int main(int argc, char *argv[]) {
+ struct MHD_Daemon *d = NULL;
+ int r, n;
+
log_set_target(LOG_TARGET_AUTO);
log_parse_environment();
log_open();
+ r = parse_argv(argc, argv);
+ if (r < 0)
+ return EXIT_FAILURE;
+ if (r == 0)
+ return EXIT_SUCCESS;
+
n = sd_listen_fds(1);
if (n < 0) {
log_error("Failed to determine passed sockets: %s", strerror(-n));
@@ -879,23 +1020,36 @@ int main(int argc, char *argv[]) {
} else if (n > 1) {
log_error("Can't listen on more than one socket.");
goto finish;
- } else if (n > 0) {
- d = MHD_start_daemon(
- MHD_USE_THREAD_PER_CONNECTION|MHD_USE_POLL|MHD_USE_DEBUG,
- 19531,
- NULL, NULL,
- request_handler, NULL,
- MHD_OPTION_LISTEN_SOCKET, SD_LISTEN_FDS_START,
- MHD_OPTION_NOTIFY_COMPLETED, request_meta_free, NULL,
- MHD_OPTION_END);
} else {
- d = MHD_start_daemon(
- MHD_USE_DEBUG|MHD_USE_THREAD_PER_CONNECTION|MHD_USE_POLL,
- 19531,
- NULL, NULL,
- request_handler, NULL,
- MHD_OPTION_NOTIFY_COMPLETED, request_meta_free, NULL,
- MHD_OPTION_END);
+ struct MHD_OptionItem opts[] = {
+ { MHD_OPTION_NOTIFY_COMPLETED,
+ (intptr_t) request_meta_free, NULL },
+ { MHD_OPTION_EXTERNAL_LOGGER,
+ (intptr_t) microhttpd_logger, NULL },
+ { MHD_OPTION_END, 0, NULL },
+ { MHD_OPTION_END, 0, NULL },
+ { MHD_OPTION_END, 0, NULL },
+ { MHD_OPTION_END, 0, NULL }};
+ int opts_pos = 2;
+ int flags = MHD_USE_THREAD_PER_CONNECTION|MHD_USE_POLL|MHD_USE_DEBUG;
+
+ if (n > 0)
+ opts[opts_pos++] = (struct MHD_OptionItem)
+ {MHD_OPTION_LISTEN_SOCKET, SD_LISTEN_FDS_START};
+ if (key_pem) {
+ assert(cert_pem);
+ opts[opts_pos++] = (struct MHD_OptionItem)
+ {MHD_OPTION_HTTPS_MEM_KEY, 0, key_pem};
+ opts[opts_pos++] = (struct MHD_OptionItem)
+ {MHD_OPTION_HTTPS_MEM_CERT, 0, cert_pem};
+ flags |= MHD_USE_SSL;
+ }
+
+ d = MHD_start_daemon(flags, 19531,
+ NULL, NULL,
+ request_handler, NULL,
+ MHD_OPTION_ARRAY, opts,
+ MHD_OPTION_END);
}
if (!d) {
diff --git a/src/journal/journal-internal.h b/src/journal/journal-internal.h
index 97de0e75ff..c7e585d810 100644
--- a/src/journal/journal-internal.h
+++ b/src/journal/journal-internal.h
@@ -30,6 +30,7 @@
#include "journal-def.h"
#include "list.h"
#include "hashmap.h"
+#include "set.h"
#include "journal-file.h"
typedef struct Match Match;
@@ -73,19 +74,20 @@ typedef enum LocationType {
struct Location {
LocationType type;
+ bool seqnum_set;
+ bool realtime_set;
+ bool monotonic_set;
+ bool xor_hash_set;
+
uint64_t seqnum;
sd_id128_t seqnum_id;
- bool seqnum_set;
uint64_t realtime;
- bool realtime_set;
uint64_t monotonic;
sd_id128_t boot_id;
- bool monotonic_set;
uint64_t xor_hash;
- bool xor_hash_set;
};
struct Directory {
@@ -112,7 +114,7 @@ struct sd_journal {
int inotify_fd;
- Match *level0, *level1;
+ Match *level0, *level1, *level2;
unsigned current_invalidate_counter, last_invalidate_counter;
@@ -123,7 +125,17 @@ struct sd_journal {
bool on_network;
size_t data_threshold;
+
+ Set *errors;
+
+ usec_t last_process_usec;
};
char *journal_make_match_string(sd_journal *j);
void journal_print_header(sd_journal *j);
+
+static inline void journal_closep(sd_journal **j) {
+ sd_journal_close(*j);
+}
+
+#define _cleanup_journal_close_ _cleanup_(journal_closep)
diff --git a/src/journal/journal-send.c b/src/journal/journal-send.c
index d5ec73e371..14c437da78 100644
--- a/src/journal/journal-send.c
+++ b/src/journal/journal-send.c
@@ -111,13 +111,12 @@ _public_ int sd_journal_printv(int priority, const char *format, va_list ap) {
return sd_journal_sendv(iov, 2);
}
-static int fill_iovec_sprintf(const char *format, va_list ap, int extra, struct iovec **_iov) {
+_printf_attr_(1, 0) static int fill_iovec_sprintf(const char *format, va_list ap, int extra, struct iovec **_iov) {
+ PROTECT_ERRNO;
int r, n = 0, i = 0, j;
struct iovec *iov = NULL;
- int saved_errno;
assert(_iov);
- saved_errno = errno;
if (extra > 0) {
n = MAX(extra * 2, extra + 4);
@@ -163,7 +162,6 @@ static int fill_iovec_sprintf(const char *format, va_list ap, int extra, struct
*_iov = iov;
- errno = saved_errno;
return i;
fail:
@@ -172,7 +170,6 @@ fail:
free(iov);
- errno = saved_errno;
return r;
}
@@ -202,14 +199,14 @@ finish:
}
_public_ int sd_journal_sendv(const struct iovec *iov, int n) {
+ PROTECT_ERRNO;
int fd, buffer_fd;
struct iovec *w;
uint64_t *l;
- int r, i, j = 0;
+ int i, j = 0;
struct msghdr mh;
struct sockaddr_un sa;
ssize_t k;
- int saved_errno;
union {
struct cmsghdr cmsghdr;
uint8_t buf[CMSG_SPACE(sizeof(int))];
@@ -227,24 +224,18 @@ _public_ int sd_journal_sendv(const struct iovec *iov, int n) {
if (_unlikely_(n <= 0))
return -EINVAL;
- saved_errno = errno;
-
w = alloca(sizeof(struct iovec) * n * 5 + 3);
l = alloca(sizeof(uint64_t) * n);
for (i = 0; i < n; i++) {
char *c, *nl;
- if (_unlikely_(!iov[i].iov_base || iov[i].iov_len <= 1)) {
- r = -EINVAL;
- goto finish;
- }
+ if (_unlikely_(!iov[i].iov_base || iov[i].iov_len <= 1))
+ return -EINVAL;
c = memchr(iov[i].iov_base, '=', iov[i].iov_len);
- if (_unlikely_(!c || c == iov[i].iov_base)) {
- r = -EINVAL;
- goto finish;
- }
+ if (_unlikely_(!c || c == iov[i].iov_base))
+ return -EINVAL;
have_syslog_identifier = have_syslog_identifier ||
(c == (char *) iov[i].iov_base + 17 &&
@@ -252,10 +243,8 @@ _public_ int sd_journal_sendv(const struct iovec *iov, int n) {
nl = memchr(iov[i].iov_base, '\n', iov[i].iov_len);
if (nl) {
- if (_unlikely_(nl < c)) {
- r = -EINVAL;
- goto finish;
- }
+ if (_unlikely_(nl < c))
+ return -EINVAL;
/* Already includes a newline? Bummer, then
* let's write the variable name, then a
@@ -300,10 +289,8 @@ _public_ int sd_journal_sendv(const struct iovec *iov, int n) {
}
fd = journal_fd();
- if (_unlikely_(fd < 0)) {
- r = fd;
- goto finish;
- }
+ if (_unlikely_(fd < 0))
+ return fd;
zero(sa);
sa.sun_family = AF_UNIX;
@@ -316,37 +303,29 @@ _public_ int sd_journal_sendv(const struct iovec *iov, int n) {
mh.msg_iovlen = j;
k = sendmsg(fd, &mh, MSG_NOSIGNAL);
- if (k >= 0) {
- r = 0;
- goto finish;
- }
+ if (k >= 0)
+ return 0;
- if (errno != EMSGSIZE && errno != ENOBUFS) {
- r = -errno;
- goto finish;
- }
+ if (errno != EMSGSIZE && errno != ENOBUFS)
+ return -errno;
/* Message doesn't fit... Let's dump the data in a temporary
* file and just pass a file descriptor of it to the other
* side */
buffer_fd = mkostemp(path, O_CLOEXEC|O_RDWR);
- if (buffer_fd < 0) {
- r = -errno;
- goto finish;
- }
+ if (buffer_fd < 0)
+ return -errno;
if (unlink(path) < 0) {
close_nointr_nofail(buffer_fd);
- r = -errno;
- goto finish;
+ return -errno;
}
n = writev(buffer_fd, w, j);
if (n < 0) {
close_nointr_nofail(buffer_fd);
- r = -errno;
- goto finish;
+ return -errno;
}
mh.msg_iov = NULL;
@@ -367,24 +346,15 @@ _public_ int sd_journal_sendv(const struct iovec *iov, int n) {
k = sendmsg(fd, &mh, MSG_NOSIGNAL);
close_nointr_nofail(buffer_fd);
- if (k < 0) {
- r = -errno;
- goto finish;
- }
-
- r = 0;
-
-finish:
- errno = saved_errno;
+ if (k < 0)
+ return -errno;
- return r;
+ return 0;
}
static int fill_iovec_perror_and_send(const char *message, int skip, struct iovec iov[]) {
- size_t n, k, r;
- int saved_errno;
-
- saved_errno = errno;
+ PROTECT_ERRNO;
+ size_t n, k;
k = isempty(message) ? 0 : strlen(message) + 2;
n = 8 + k + 256 + 1;
@@ -394,7 +364,7 @@ static int fill_iovec_perror_and_send(const char *message, int skip, struct iove
char* j;
errno = 0;
- j = strerror_r(saved_errno, buffer + 8 + k, n - 8 - k);
+ j = strerror_r(_saved_errno_, buffer + 8 + k, n - 8 - k);
if (errno == 0) {
char error[6 + 10 + 1]; /* for a 32bit value */
@@ -408,24 +378,18 @@ static int fill_iovec_perror_and_send(const char *message, int skip, struct iove
memcpy(buffer + 8 + k - 2, ": ", 2);
}
- snprintf(error, sizeof(error), "ERRNO=%u", saved_errno);
+ snprintf(error, sizeof(error), "ERRNO=%u", _saved_errno_);
char_array_0(error);
IOVEC_SET_STRING(iov[skip+0], "PRIORITY=3");
IOVEC_SET_STRING(iov[skip+1], buffer);
IOVEC_SET_STRING(iov[skip+2], error);
- r = sd_journal_sendv(iov, skip + 3);
-
- errno = saved_errno;
- return r;
+ return sd_journal_sendv(iov, skip + 3);
}
- if (errno != ERANGE) {
- r = -errno;
- errno = saved_errno;
- return r;
- }
+ if (errno != ERANGE)
+ return -errno;
n *= 2;
}
diff --git a/src/journal/journal-vacuum.c b/src/journal/journal-vacuum.c
index 731f6c770f..4a3a5a9e63 100644
--- a/src/journal/journal-vacuum.c
+++ b/src/journal/journal-vacuum.c
@@ -36,7 +36,7 @@
#include "util.h"
struct vacuum_info {
- off_t usage;
+ uint64_t usage;
char *filename;
uint64_t realtime;
@@ -293,7 +293,7 @@ int journal_directory_vacuum(
if (unlinkat(dirfd(d), list[i].filename, 0) >= 0) {
log_debug("Deleted archived journal %s/%s.", directory, list[i].filename);
- if ((uint64_t) list[i].usage > sum)
+ if (list[i].usage < sum)
sum -= list[i].usage;
else
sum = 0;
diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c
index a74d43be7f..409f082276 100644
--- a/src/journal/journalctl.c
+++ b/src/journal/journalctl.c
@@ -27,7 +27,6 @@
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
-#include <sys/poll.h>
#include <time.h>
#include <getopt.h>
#include <signal.h>
@@ -35,9 +34,15 @@
#include <sys/ioctl.h>
#include <linux/fs.h>
+#ifdef HAVE_ACL
+#include <sys/acl.h>
+#include "acl-util.h"
+#endif
+
#include <systemd/sd-journal.h>
#include "log.h"
+#include "logs-show.h"
#include "util.h"
#include "path-util.h"
#include "build.h"
@@ -56,11 +61,12 @@
#define DEFAULT_FSS_INTERVAL_USEC (15*USEC_PER_MINUTE)
static OutputMode arg_output = OUTPUT_SHORT;
+static bool arg_pager_end = false;
static bool arg_follow = false;
static bool arg_full = false;
static bool arg_all = false;
static bool arg_no_pager = false;
-static unsigned arg_lines = 0;
+static int arg_lines = -1;
static bool arg_no_tail = false;
static bool arg_quiet = false;
static bool arg_merge = false;
@@ -74,9 +80,12 @@ static usec_t arg_interval = DEFAULT_FSS_INTERVAL_USEC;
#endif
static usec_t arg_since, arg_until;
static bool arg_since_set = false, arg_until_set = false;
-static const char *arg_unit = NULL;
+static char **arg_system_units = NULL;
+static char **arg_user_units = NULL;
static const char *arg_field = NULL;
static bool arg_catalog = false;
+static bool arg_reverse = false;
+static const char *arg_root = NULL;
static enum {
ACTION_SHOW,
@@ -86,6 +95,7 @@ static enum {
ACTION_VERIFY,
ACTION_DISK_USAGE,
ACTION_LIST_CATALOG,
+ ACTION_DUMP_CATALOG,
ACTION_UPDATE_CATALOG
} arg_action = ACTION_SHOW;
@@ -99,10 +109,13 @@ static int help(void) {
" -c --cursor=CURSOR Start showing entries from specified cursor\n"
" -b --this-boot Show data only from current boot\n"
" -u --unit=UNIT Show data only from the specified unit\n"
+ " --user-unit=UNIT Show data only from the specified user session unit\n"
" -p --priority=RANGE Show only messages within the specified priority range\n"
+ " -e --pager-end Immediately jump to end of the journal in the pager\n"
" -f --follow Follow journal\n"
" -n --lines[=INTEGER] Number of journal entries to show\n"
" --no-tail Show all lines, even in follow mode\n"
+ " -r --reverse Show the newest entries first\n"
" -o --output=STRING Change journal output mode (short, short-monotonic,\n"
" verbose, export, json, json-pretty, json-sse, cat)\n"
" -x --catalog Add message explanations where available\n"
@@ -112,6 +125,7 @@ static int help(void) {
" --no-pager Do not pipe output into a pager\n"
" -m --merge Show entries from all available journals\n"
" -D --directory=PATH Show journal files from directory\n"
+ " --root=ROOT Operate on catalog files underneath the root ROOT\n"
#ifdef HAVE_GCRYPT
" --interval=TIME Time interval for changing the FSS sealing key\n"
" --verify-key=KEY Specify FSS verification key\n"
@@ -124,6 +138,7 @@ static int help(void) {
" --disk-usage Show total disk usage\n"
" -F --field=FIELD List all values a certain field takes\n"
" --list-catalog Show message IDs of all entries in the message catalog\n"
+ " --dump-catalog Show entries in the message catalog\n"
" --update-catalog Update the message catalog database\n"
#ifdef HAVE_GCRYPT
" --setup-keys Generate new FSS key pair\n"
@@ -141,6 +156,7 @@ static int parse_argv(int argc, char *argv[]) {
ARG_NO_PAGER,
ARG_NO_TAIL,
ARG_NEW_ID128,
+ ARG_ROOT,
ARG_HEADER,
ARG_FULL,
ARG_SETUP_KEYS,
@@ -150,7 +166,9 @@ static int parse_argv(int argc, char *argv[]) {
ARG_DISK_USAGE,
ARG_SINCE,
ARG_UNTIL,
+ ARG_USER_UNIT,
ARG_LIST_CATALOG,
+ ARG_DUMP_CATALOG,
ARG_UPDATE_CATALOG
};
@@ -158,6 +176,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "help", no_argument, NULL, 'h' },
{ "version" , no_argument, NULL, ARG_VERSION },
{ "no-pager", no_argument, NULL, ARG_NO_PAGER },
+ { "pager-end", no_argument, NULL, 'e' },
{ "follow", no_argument, NULL, 'f' },
{ "output", required_argument, NULL, 'o' },
{ "all", no_argument, NULL, 'a' },
@@ -169,6 +188,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "merge", no_argument, NULL, 'm' },
{ "this-boot", no_argument, NULL, 'b' },
{ "directory", required_argument, NULL, 'D' },
+ { "root", required_argument, NULL, ARG_ROOT },
{ "header", no_argument, NULL, ARG_HEADER },
{ "priority", required_argument, NULL, 'p' },
{ "setup-keys", no_argument, NULL, ARG_SETUP_KEYS },
@@ -180,10 +200,13 @@ static int parse_argv(int argc, char *argv[]) {
{ "since", required_argument, NULL, ARG_SINCE },
{ "until", required_argument, NULL, ARG_UNTIL },
{ "unit", required_argument, NULL, 'u' },
+ { "user-unit", required_argument, NULL, ARG_USER_UNIT },
{ "field", required_argument, NULL, 'F' },
{ "catalog", no_argument, NULL, 'x' },
{ "list-catalog", no_argument, NULL, ARG_LIST_CATALOG },
+ { "dump-catalog", no_argument, NULL, ARG_DUMP_CATALOG },
{ "update-catalog",no_argument, NULL, ARG_UPDATE_CATALOG },
+ { "reverse", no_argument, NULL, 'r' },
{ NULL, 0, NULL, 0 }
};
@@ -192,7 +215,7 @@ static int parse_argv(int argc, char *argv[]) {
assert(argc >= 0);
assert(argv);
- while ((c = getopt_long(argc, argv, "hfo:an::qmbD:p:c:u:F:x", options, NULL)) >= 0) {
+ while ((c = getopt_long(argc, argv, "hefo:an::qmbD:p:c:u:F:xr", options, NULL)) >= 0) {
switch (c) {
@@ -209,6 +232,14 @@ static int parse_argv(int argc, char *argv[]) {
arg_no_pager = true;
break;
+ case 'e':
+ arg_pager_end = true;
+
+ if (arg_lines < 0)
+ arg_lines = 1000;
+
+ break;
+
case 'f':
arg_follow = true;
break;
@@ -239,13 +270,30 @@ static int parse_argv(int argc, char *argv[]) {
case 'n':
if (optarg) {
- r = safe_atou(optarg, &arg_lines);
- if (r < 0 || arg_lines <= 0) {
+ r = safe_atoi(optarg, &arg_lines);
+ if (r < 0 || arg_lines < 0) {
log_error("Failed to parse lines '%s'", optarg);
return -EINVAL;
}
- } else
- arg_lines = 10;
+ } else {
+ int n;
+
+ /* Hmm, no argument? Maybe the next
+ * word on the command line is
+ * supposed to be the argument? Let's
+ * see if there is one, and is
+ * parsable as a positive
+ * integer... */
+
+ if (optind < argc &&
+ safe_atoi(argv[optind], &n) >= 0 &&
+ n >= 0) {
+
+ arg_lines = n;
+ optind++;
+ } else
+ arg_lines = 10;
+ }
break;
@@ -273,6 +321,10 @@ static int parse_argv(int argc, char *argv[]) {
arg_directory = optarg;
break;
+ case ARG_ROOT:
+ arg_root = optarg;
+ break;
+
case 'c':
arg_cursor = optarg;
break;
@@ -302,7 +354,7 @@ static int parse_argv(int argc, char *argv[]) {
break;
case ARG_INTERVAL:
- r = parse_usec(optarg, &arg_interval);
+ r = parse_sec(optarg, &arg_interval);
if (r < 0 || arg_interval <= 0) {
log_error("Failed to parse sealing key change interval: %s", optarg);
return -EINVAL;
@@ -385,7 +437,15 @@ static int parse_argv(int argc, char *argv[]) {
break;
case 'u':
- arg_unit = optarg;
+ r = strv_extend(&arg_system_units, optarg);
+ if (r < 0)
+ return log_oom();
+ break;
+
+ case ARG_USER_UNIT:
+ r = strv_extend(&arg_user_units, optarg);
+ if (r < 0)
+ return log_oom();
break;
case '?':
@@ -403,20 +463,28 @@ static int parse_argv(int argc, char *argv[]) {
arg_action = ACTION_LIST_CATALOG;
break;
+ case ARG_DUMP_CATALOG:
+ arg_action = ACTION_DUMP_CATALOG;
+ break;
+
case ARG_UPDATE_CATALOG:
arg_action = ACTION_UPDATE_CATALOG;
break;
+ case 'r':
+ arg_reverse = true;
+ break;
+
default:
log_error("Unknown option code %c", c);
return -EINVAL;
}
}
- if (arg_follow && !arg_no_tail && arg_lines <= 0)
+ if (arg_follow && !arg_no_tail && arg_lines < 0)
arg_lines = 10;
- if (arg_since_set && arg_until_set && arg_since_set > arg_until_set) {
+ if (arg_since_set && arg_until_set && arg_since > arg_until) {
log_error("--since= must be before --until=.");
return -EINVAL;
}
@@ -426,6 +494,11 @@ static int parse_argv(int argc, char *argv[]) {
return -EINVAL;
}
+ if (arg_follow && arg_reverse) {
+ log_error("Please specify either --reverse= or --follow=, not both.");
+ return -EINVAL;
+ }
+
return 1;
}
@@ -445,30 +518,33 @@ static int generate_new_id128(void) {
"As UUID:\n"
"%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n\n"
"As macro:\n"
- "#define MESSAGE_XYZ SD_ID128_MAKE(",
+ "#define MESSAGE_XYZ SD_ID128_MAKE(",
SD_ID128_FORMAT_VAL(id),
SD_ID128_FORMAT_VAL(id));
-
for (i = 0; i < 16; i++)
printf("%02x%s", id.bytes[i], i != 15 ? "," : "");
+ fputs(")\n\n", stdout);
- fputs(")\n", stdout);
+ printf("As Python constant:\n"
+ ">>> import uuid\n"
+ ">>> MESSAGE_XYZ = uuid.UUID('" SD_ID128_FORMAT_STR "')\n",
+ SD_ID128_FORMAT_VAL(id));
return 0;
}
static int add_matches(sd_journal *j, char **args) {
char **i;
- int r;
assert(j);
STRV_FOREACH(i, args) {
+ int r;
if (streq(*i, "+"))
r = sd_journal_add_disjunction(j);
else if (path_is_absolute(*i)) {
- char *p, *t = NULL;
+ _cleanup_free_ char *p, *t = NULL;
const char *path;
struct stat st;
@@ -476,7 +552,6 @@ static int add_matches(sd_journal *j, char **args) {
path = p ? p : *i;
if (stat(path, &st) < 0) {
- free(p);
log_error("Couldn't stat file: %m");
return -errno;
}
@@ -488,18 +563,14 @@ static int add_matches(sd_journal *j, char **args) {
else if (S_ISBLK(st.st_mode))
asprintf(&t, "_KERNEL_DEVICE=b%u:%u", major(st.st_rdev), minor(st.st_rdev));
else {
- free(p);
- log_error("File is not a device node, regular file or is not executable: %s", *i);
+ log_error("File is neither a device node, nor regular file, nor executable: %s", *i);
return -EINVAL;
}
- free(p);
-
if (!t)
return log_oom();
r = sd_journal_add_match(j, t, 0);
- free(t);
} else
r = sd_journal_add_match(j, *i, 0);
@@ -535,39 +606,57 @@ static int add_this_boot(sd_journal *j) {
return r;
}
+ r = sd_journal_add_conjunction(j);
+ if (r < 0)
+ return r;
+
return 0;
}
-static int add_unit(sd_journal *j) {
- _cleanup_free_ char *m = NULL, *u = NULL;
+static int add_units(sd_journal *j) {
+ _cleanup_free_ char *u = NULL;
int r;
+ char **i;
assert(j);
- if (isempty(arg_unit))
- return 0;
+ STRV_FOREACH(i, arg_system_units) {
+ u = unit_name_mangle(*i);
+ if (!u)
+ return log_oom();
+ r = add_matches_for_unit(j, u);
+ if (r < 0)
+ return r;
+ r = sd_journal_add_disjunction(j);
+ if (r < 0)
+ return r;
+ }
- u = unit_name_mangle(arg_unit);
- if (!u)
- return log_oom();
+ STRV_FOREACH(i, arg_user_units) {
+ u = unit_name_mangle(*i);
+ if (!u)
+ return log_oom();
- m = strappend("_SYSTEMD_UNIT=", u);
- if (!m)
- return log_oom();
+ r = add_matches_for_user_unit(j, u, getuid());
+ if (r < 0)
+ return r;
+
+ r = sd_journal_add_disjunction(j);
+ if (r < 0)
+ return r;
- r = sd_journal_add_match(j, m, strlen(m));
- if (r < 0) {
- log_error("Failed to add match: %s", strerror(-r));
- return r;
}
+ r = sd_journal_add_conjunction(j);
+ if (r < 0)
+ return r;
+
return 0;
}
static int add_priorities(sd_journal *j) {
char match[] = "PRIORITY=0";
int i, r;
-
assert(j);
if (arg_priorities == 0xFF)
@@ -584,6 +673,10 @@ static int add_priorities(sd_journal *j) {
}
}
+ r = sd_journal_add_conjunction(j);
+ if (r < 0)
+ return r;
+
return 0;
}
@@ -737,12 +830,12 @@ static int setup_keys(void) {
fprintf(stderr,
ANSI_HIGHLIGHT_OFF "\n"
"The sealing key is automatically changed every %s.\n",
- format_timespan(tsb, sizeof(tsb), arg_interval));
+ format_timespan(tsb, sizeof(tsb), arg_interval, 0));
hn = gethostname_malloc();
if (hn) {
- hostname_cleanup(hn);
+ hostname_cleanup(hn, false);
fprintf(stderr, "\nThe keys have been generated for host %s/" SD_ID128_FORMAT_STR ".\n", hn, SD_ID128_FORMAT_VAL(machine));
} else
fprintf(stderr, "\nThe keys have been generated for host " SD_ID128_FORMAT_STR ".\n", SD_ID128_FORMAT_VAL(machine));
@@ -811,10 +904,10 @@ static int verify(sd_journal *j) {
log_info("=> Validated from %s to %s, final %s entries not sealed.",
format_timestamp(a, sizeof(a), first),
format_timestamp(b, sizeof(b), validated),
- format_timespan(c, sizeof(c), last > validated ? last - validated : 0));
+ format_timespan(c, sizeof(c), last > validated ? last - validated : 0, 0));
} else if (last > 0)
log_info("=> No sealing yet, %s of entries not sealed.",
- format_timespan(c, sizeof(c), last - first));
+ format_timespan(c, sizeof(c), last - first, 0));
else
log_info("=> No sealing yet, no entries in file.");
}
@@ -824,33 +917,122 @@ static int verify(sd_journal *j) {
return r;
}
-static int access_check(void) {
-
#ifdef HAVE_ACL
- if (access("/var/log/journal", F_OK) < 0 && geteuid() != 0 && in_group("adm") <= 0) {
- log_error("Unprivileged users can't see messages unless persistent log storage is enabled. Users in the group 'adm' can always see messages.");
- return -EACCES;
+static int access_check_var_log_journal(sd_journal *j) {
+ _cleanup_strv_free_ char **g = NULL;
+ bool have_access;
+ int r;
+
+ assert(j);
+
+ have_access = in_group("systemd-journal") > 0;
+
+ if (!have_access) {
+ /* Let's enumerate all groups from the default ACL of
+ * the directory, which generally should allow access
+ * to most journal files too */
+ r = search_acl_groups(&g, "/var/log/journal/", &have_access);
+ if (r < 0)
+ return r;
}
- if (!arg_quiet && geteuid() != 0 && in_group("adm") <= 0)
- log_warning("Showing user generated messages only. Users in the group 'adm' can see all messages. Pass -q to turn this notice off.");
-#else
- if (geteuid() != 0 && in_group("adm") <= 0) {
- log_error("No access to messages. Only users in the group 'adm' can see messages.");
- return -EACCES;
+ if (!have_access) {
+
+ if (strv_isempty(g))
+ log_notice("Hint: You are currently not seeing messages from other users and the system.\n"
+ " Users in the 'systemd-journal' group can see all messages. Pass -q to\n"
+ " turn off this notice.");
+ else {
+ _cleanup_free_ char *s = NULL;
+
+ r = strv_extend(&g, "systemd-journal");
+ if (r < 0)
+ return log_oom();
+
+ strv_sort(g);
+ strv_uniq(g);
+
+ s = strv_join(g, "', '");
+ if (!s)
+ return log_oom();
+
+ log_notice("Hint: You are currently not seeing messages from other users and the system.\n"
+ " Users in the groups '%s' can see all messages.\n"
+ " Pass -q to turn off this notice.", s);
+ }
}
-#endif
return 0;
}
+#endif
+
+static int access_check(sd_journal *j) {
+ Iterator it;
+ void *code;
+ int r = 0;
+
+ assert(j);
+
+ if (set_isempty(j->errors)) {
+ if (hashmap_isempty(j->files))
+ log_notice("No journal files were found.");
+ return 0;
+ }
+
+ if (set_contains(j->errors, INT_TO_PTR(-EACCES))) {
+#ifdef HAVE_ACL
+ /* If /var/log/journal doesn't even exist,
+ * unprivileged users have no access at all */
+ if (access("/var/log/journal", F_OK) < 0 &&
+ geteuid() != 0 &&
+ in_group("systemd-journal") <= 0) {
+ log_error("Unprivileged users cannot access messages, unless persistent log storage is\n"
+ "enabled. Users in the 'systemd-journal' group may always access messages.");
+ return -EACCES;
+ }
+
+ /* If /var/log/journal exists, try to pring a nice
+ notice if the user lacks access to it */
+ if (!arg_quiet && geteuid() != 0) {
+ r = access_check_var_log_journal(j);
+ if (r < 0)
+ return r;
+ }
+#else
+ if (geteuid() != 0 && in_group("systemd-journal") <= 0) {
+ log_error("Unprivileged users cannot access messages. Users in the 'systemd-journal' group\n"
+ "group may access messages.");
+ return -EACCES;
+ }
+#endif
+
+ if (hashmap_isempty(j->files)) {
+ log_error("No journal files were opened due to insufficient permissions.");
+ r = -EACCES;
+ }
+ }
+
+ SET_FOREACH(code, j->errors, it) {
+ int err;
+
+ err = -PTR_TO_INT(code);
+ assert(err > 0);
+
+ if (err != EACCES)
+ log_warning("Error was encountered while opening journal files: %s",
+ strerror(err));
+ }
+
+ return r;
+}
int main(int argc, char *argv[]) {
int r;
- sd_journal *j = NULL;
+ _cleanup_journal_close_ sd_journal*j = NULL;
bool need_seek = false;
sd_id128_t previous_boot_id;
- bool previous_boot_id_valid = false;
- unsigned n_shown = 0;
+ bool previous_boot_id_valid = false, first_line = true;
+ int n_shown = 0;
setlocale(LC_ALL, "");
log_parse_environment();
@@ -872,21 +1054,40 @@ int main(int argc, char *argv[]) {
goto finish;
}
- if (arg_action == ACTION_LIST_CATALOG) {
- r = catalog_list(stdout);
- if (r < 0)
- log_error("Failed to list catalog: %s", strerror(-r));
- goto finish;
- }
+ if (arg_action == ACTION_UPDATE_CATALOG ||
+ arg_action == ACTION_LIST_CATALOG ||
+ arg_action == ACTION_DUMP_CATALOG) {
- if (arg_action == ACTION_UPDATE_CATALOG) {
- r = catalog_update();
- goto finish;
- }
+ const char* database = CATALOG_DATABASE;
+ _cleanup_free_ char *copy = NULL;
+ if (arg_root) {
+ copy = strjoin(arg_root, "/", CATALOG_DATABASE, NULL);
+ if (!copy) {
+ r = log_oom();
+ goto finish;
+ }
+ path_kill_slashes(copy);
+ database = copy;
+ }
+
+ if (arg_action == ACTION_UPDATE_CATALOG) {
+ r = catalog_update(database, arg_root, catalog_file_dirs);
+ if (r < 0)
+ log_error("Failed to list catalog: %s", strerror(-r));
+ } else {
+ bool oneline = arg_action == ACTION_LIST_CATALOG;
+
+ if (optind < argc)
+ r = catalog_list_items(stdout, database,
+ oneline, argv + optind);
+ else
+ r = catalog_list(stdout, database, oneline);
+ if (r < 0)
+ log_error("Failed to list catalog: %s", strerror(-r));
+ }
- r = access_check();
- if (r < 0)
goto finish;
+ }
if (arg_directory)
r = sd_journal_open_directory(&j, arg_directory, 0);
@@ -894,9 +1095,13 @@ int main(int argc, char *argv[]) {
r = sd_journal_open(&j, arg_merge ? 0 : SD_JOURNAL_LOCAL_ONLY);
if (r < 0) {
log_error("Failed to open journal: %s", strerror(-r));
- goto finish;
+ return EXIT_FAILURE;
}
+ r = access_check(j);
+ if (r < 0)
+ return EXIT_FAILURE;
+
if (arg_action == ACTION_VERIFY) {
r = verify(j);
goto finish;
@@ -904,8 +1109,7 @@ int main(int argc, char *argv[]) {
if (arg_action == ACTION_PRINT_HEADER) {
journal_print_header(j);
- r = 0;
- goto finish;
+ return EXIT_SUCCESS;
}
if (arg_action == ACTION_DISK_USAGE) {
@@ -914,43 +1118,57 @@ int main(int argc, char *argv[]) {
r = sd_journal_get_usage(j, &bytes);
if (r < 0)
- goto finish;
+ return EXIT_FAILURE;
- printf("Journals take up %s on disk.\n", format_bytes(sbytes, sizeof(sbytes), bytes));
- r = 0;
- goto finish;
+ printf("Journals take up %s on disk.\n",
+ format_bytes(sbytes, sizeof(sbytes), bytes));
+ return EXIT_SUCCESS;
}
r = add_this_boot(j);
if (r < 0)
- goto finish;
+ return EXIT_FAILURE;
+
+ r = add_units(j);
+ strv_free(arg_system_units);
+ strv_free(arg_user_units);
- r = add_unit(j);
if (r < 0)
- goto finish;
+ return EXIT_FAILURE;
+
+ r = add_priorities(j);
+ if (r < 0)
+ return EXIT_FAILURE;
r = add_matches(j, argv + optind);
if (r < 0)
- goto finish;
+ return EXIT_FAILURE;
- r = add_priorities(j);
+ /* Opening the fd now means the first sd_journal_wait() will actually wait */
+ r = sd_journal_get_fd(j);
if (r < 0)
- goto finish;
+ return EXIT_FAILURE;
if (arg_field) {
const void *data;
size_t size;
+ r = sd_journal_set_data_threshold(j, 0);
+ if (r < 0) {
+ log_error("Failed to unset data size threshold");
+ return EXIT_FAILURE;
+ }
+
r = sd_journal_query_unique(j, arg_field);
if (r < 0) {
log_error("Failed to query unique data objects: %s", strerror(-r));
- goto finish;
+ return EXIT_FAILURE;
}
SD_JOURNAL_FOREACH_UNIQUE(j, data, size) {
const void *eq;
- if (arg_lines > 0 && n_shown >= arg_lines)
+ if (arg_lines >= 0 && n_shown >= arg_lines)
break;
eq = memchr(data, '=', size);
@@ -962,41 +1180,59 @@ int main(int argc, char *argv[]) {
n_shown ++;
}
- r = 0;
- goto finish;
+ return EXIT_SUCCESS;
}
if (arg_cursor) {
r = sd_journal_seek_cursor(j, arg_cursor);
if (r < 0) {
log_error("Failed to seek to cursor: %s", strerror(-r));
- goto finish;
+ return EXIT_FAILURE;
}
+ if (!arg_reverse)
+ r = sd_journal_next(j);
+ else
+ r = sd_journal_previous(j);
- r = sd_journal_next(j);
-
- } else if (arg_since_set) {
+ } else if (arg_since_set && !arg_reverse) {
r = sd_journal_seek_realtime_usec(j, arg_since);
if (r < 0) {
log_error("Failed to seek to date: %s", strerror(-r));
- goto finish;
+ return EXIT_FAILURE;
}
r = sd_journal_next(j);
- } else if (arg_lines > 0) {
+ } else if (arg_until_set && arg_reverse) {
+ r = sd_journal_seek_realtime_usec(j, arg_until);
+ if (r < 0) {
+ log_error("Failed to seek to date: %s", strerror(-r));
+ return EXIT_FAILURE;
+ }
+ r = sd_journal_previous(j);
+
+ } else if (arg_lines >= 0) {
r = sd_journal_seek_tail(j);
if (r < 0) {
log_error("Failed to seek to tail: %s", strerror(-r));
- goto finish;
+ return EXIT_FAILURE;
}
r = sd_journal_previous_skip(j, arg_lines);
+ } else if (arg_reverse) {
+ r = sd_journal_seek_tail(j);
+ if (r < 0) {
+ log_error("Failed to seek to tail: %s", strerror(-r));
+ return EXIT_FAILURE;
+ }
+
+ r = sd_journal_previous(j);
+
} else {
r = sd_journal_seek_head(j);
if (r < 0) {
log_error("Failed to seek to head: %s", strerror(-r));
- goto finish;
+ return EXIT_FAILURE;
}
r = sd_journal_next(j);
@@ -1004,11 +1240,11 @@ int main(int argc, char *argv[]) {
if (r < 0) {
log_error("Failed to iterate through journal: %s", strerror(-r));
- goto finish;
+ return EXIT_FAILURE;
}
if (!arg_no_pager && !arg_follow)
- pager_open();
+ pager_open(arg_pager_end);
if (!arg_quiet) {
usec_t start, end;
@@ -1032,11 +1268,14 @@ int main(int argc, char *argv[]) {
}
for (;;) {
- while (arg_lines == 0 || arg_follow || n_shown < arg_lines) {
+ while (arg_lines < 0 || n_shown < arg_lines || (arg_follow && !first_line)) {
int flags;
if (need_seek) {
- r = sd_journal_next(j);
+ if (!arg_reverse)
+ r = sd_journal_next(j);
+ else
+ r = sd_journal_previous(j);
if (r < 0) {
log_error("Failed to iterate through journal: %s", strerror(-r));
goto finish;
@@ -1046,7 +1285,7 @@ int main(int argc, char *argv[]) {
if (r == 0)
break;
- if (arg_until_set) {
+ if (arg_until_set && !arg_reverse) {
usec_t usec;
r = sd_journal_get_realtime_usec(j, &usec);
@@ -1054,6 +1293,20 @@ int main(int argc, char *argv[]) {
log_error("Failed to determine timestamp: %s", strerror(-r));
goto finish;
}
+ if (usec > arg_until)
+ goto finish;
+ }
+
+ if (arg_since_set && arg_reverse) {
+ usec_t usec;
+
+ r = sd_journal_get_realtime_usec(j, &usec);
+ if (r < 0) {
+ log_error("Failed to determine timestamp: %s", strerror(-r));
+ goto finish;
+ }
+ if (usec < arg_since)
+ goto finish;
}
if (!arg_merge) {
@@ -1077,7 +1330,7 @@ int main(int argc, char *argv[]) {
arg_catalog * OUTPUT_CATALOG;
r = output_journal(stdout, j, arg_output, 0, flags);
- if (r < 0)
+ if (r < 0 || ferror(stdout))
goto finish;
need_seek = true;
@@ -1092,12 +1345,11 @@ int main(int argc, char *argv[]) {
log_error("Couldn't wait for journal event: %s", strerror(-r));
goto finish;
}
+
+ first_line = false;
}
finish:
- if (j)
- sd_journal_close(j);
-
pager_close();
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
diff --git a/src/journal/journald-gperf.gperf b/src/journal/journald-gperf.gperf
index 1baef1411c..2ecba3bd0e 100644
--- a/src/journal/journald-gperf.gperf
+++ b/src/journal/journald-gperf.gperf
@@ -18,7 +18,8 @@ struct ConfigPerfItem;
Journal.Storage, config_parse_storage, 0, offsetof(Server, storage)
Journal.Compress, config_parse_bool, 0, offsetof(Server, compress)
Journal.Seal, config_parse_bool, 0, offsetof(Server, seal)
-Journal.RateLimitInterval, config_parse_usec, 0, offsetof(Server, rate_limit_interval)
+Journal.SyncIntervalSec, config_parse_sec, 0, offsetof(Server, sync_interval_usec)
+Journal.RateLimitInterval, config_parse_sec, 0, offsetof(Server, rate_limit_interval)
Journal.RateLimitBurst, config_parse_unsigned, 0, offsetof(Server, rate_limit_burst)
Journal.SystemMaxUse, config_parse_bytes_off, 0, offsetof(Server, system_metrics.max_use)
Journal.SystemMaxFileSize, config_parse_bytes_off, 0, offsetof(Server, system_metrics.max_size)
@@ -26,8 +27,8 @@ Journal.SystemKeepFree, config_parse_bytes_off, 0, offsetof(Server, system_m
Journal.RuntimeMaxUse, config_parse_bytes_off, 0, offsetof(Server, runtime_metrics.max_use)
Journal.RuntimeMaxFileSize, config_parse_bytes_off, 0, offsetof(Server, runtime_metrics.max_size)
Journal.RuntimeKeepFree, config_parse_bytes_off, 0, offsetof(Server, runtime_metrics.keep_free)
-Journal.MaxRetentionSec, config_parse_usec, 0, offsetof(Server, max_retention_usec)
-Journal.MaxFileSec, config_parse_usec, 0, offsetof(Server, max_file_usec)
+Journal.MaxRetentionSec, config_parse_sec, 0, offsetof(Server, max_retention_usec)
+Journal.MaxFileSec, config_parse_sec, 0, offsetof(Server, max_file_usec)
Journal.ForwardToSyslog, config_parse_bool, 0, offsetof(Server, forward_to_syslog)
Journal.ForwardToKMsg, config_parse_bool, 0, offsetof(Server, forward_to_kmsg)
Journal.ForwardToConsole, config_parse_bool, 0, offsetof(Server, forward_to_console)
diff --git a/src/journal/journald-kmsg.c b/src/journal/journald-kmsg.c
index b8198760d6..2f536320f8 100644
--- a/src/journal/journald-kmsg.c
+++ b/src/journal/journald-kmsg.c
@@ -250,10 +250,12 @@ static void dev_kmsg_record(Server *s, char *p, size_t l) {
break;
g = udev_list_entry_get_name(ll);
- b = strappend("_UDEV_DEVLINK=", g);
if (g) {
- IOVEC_SET_STRING(iovec[n++], b);
- z++;
+ b = strappend("_UDEV_DEVLINK=", g);
+ if (b) {
+ IOVEC_SET_STRING(iovec[n++], b);
+ z++;
+ }
}
j++;
diff --git a/src/journal/journald-native.c b/src/journal/journald-native.c
index 069114778b..f878dfc911 100644
--- a/src/journal/journald-native.c
+++ b/src/journal/journald-native.c
@@ -31,8 +31,10 @@
#include "journald-console.h"
#include "journald-syslog.h"
-#define ENTRY_SIZE_MAX (1024*1024*64)
-#define DATA_SIZE_MAX (1024*1024*64)
+/* Make sure not to make this smaller than the maximum coredump
+ * size. See COREDUMP_MAX in coredump.c */
+#define ENTRY_SIZE_MAX (1024*1024*768)
+#define DATA_SIZE_MAX (1024*1024*768)
static bool valid_user_field(const char *p, size_t l) {
const char *a;
@@ -121,11 +123,12 @@ void server_process_native_message(
/* A property follows */
- if (n+N_IOVEC_META_FIELDS >= m) {
+ /* n received properties, +1 for _TRANSPORT */
+ if (n + 1 + N_IOVEC_META_FIELDS >= m) {
struct iovec *c;
unsigned u;
- u = MAX((n+N_IOVEC_META_FIELDS+1) * 2U, 4U);
+ u = MAX((n + 1 + N_IOVEC_META_FIELDS) * 2U, 4U);
c = realloc(iovec, u * sizeof(struct iovec));
if (!c) {
log_oom();
diff --git a/src/journal/journald-rate-limit.c b/src/journal/journald-rate-limit.c
index 8bd68476a3..32e35a926d 100644
--- a/src/journal/journald-rate-limit.c
+++ b/src/journal/journald-rate-limit.c
@@ -115,7 +115,7 @@ void journal_rate_limit_free(JournalRateLimit *r) {
free(r);
}
-static bool journal_rate_limit_group_expired(JournalRateLimitGroup *g, usec_t ts) {
+_pure_ static bool journal_rate_limit_group_expired(JournalRateLimitGroup *g, usec_t ts) {
unsigned i;
assert(g);
@@ -170,21 +170,6 @@ fail:
return NULL;
}
-static uint64_t u64log2(uint64_t n) {
- unsigned r;
-
- if (n <= 1)
- return 0;
-
- r = 0;
- for (;;) {
- n = n >> 1;
- if (!n)
- return r;
- r++;
- }
-}
-
static unsigned burst_modulate(unsigned burst, uint64_t available) {
unsigned k;
diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c
index 43ffe75560..cc52b8a5c9 100644
--- a/src/journal/journald-server.c
+++ b/src/journal/journald-server.c
@@ -24,16 +24,14 @@
#include <linux/sockios.h>
#include <sys/statvfs.h>
#include <sys/mman.h>
+#include <sys/timerfd.h>
#include <libudev.h>
#include <systemd/sd-journal.h>
#include <systemd/sd-messages.h>
#include <systemd/sd-daemon.h>
-#ifdef HAVE_LOGIND
-#include <systemd/sd-login.h>
-#endif
-
+#include "fileio.h"
#include "mkdir.h"
#include "hashmap.h"
#include "journal-file.h"
@@ -66,6 +64,7 @@
#define USER_JOURNALS_MAX 1024
+#define DEFAULT_SYNC_INTERVAL_USEC (5*USEC_PER_MINUTE)
#define DEFAULT_RATE_LIMIT_INTERVAL (10*USEC_PER_SEC)
#define DEFAULT_RATE_LIMIT_BURST 200
@@ -91,13 +90,14 @@ DEFINE_STRING_TABLE_LOOKUP(split_mode, SplitMode);
DEFINE_CONFIG_PARSE_ENUM(config_parse_split_mode, split_mode, SplitMode, "Failed to parse split mode setting");
static uint64_t available_space(Server *s) {
- char ids[33], *p;
+ char ids[33];
+ _cleanup_free_ char *p = NULL;
const char *f;
sd_id128_t machine;
struct statvfs ss;
uint64_t sum = 0, avail = 0, ss_avail = 0;
int r;
- DIR *d;
+ _cleanup_closedir_ DIR *d = NULL;
usec_t ts;
JournalMetrics *m;
@@ -125,13 +125,11 @@ static uint64_t available_space(Server *s) {
return 0;
d = opendir(p);
- free(p);
-
if (!d)
return 0;
if (fstatvfs(dirfd(d), &ss) < 0)
- goto finish;
+ return 0;
for (;;) {
struct stat st;
@@ -170,14 +168,11 @@ static uint64_t available_space(Server *s) {
s->cached_available_space = avail;
s->cached_available_space_timestamp = ts;
-finish:
- closedir(d);
-
return avail;
}
static void server_read_file_gid(Server *s) {
- const char *adm = "adm";
+ const char *g = "systemd-journal";
int r;
assert(s);
@@ -185,9 +180,9 @@ static void server_read_file_gid(Server *s) {
if (s->file_gid_valid)
return;
- r = get_group_creds(&adm, &s->file_gid);
+ r = get_group_creds(&g, &s->file_gid);
if (r < 0)
- log_warning("Failed to resolve 'adm' group: %s", strerror(-r));
+ log_warning("Failed to resolve '%s' group: %s", g, strerror(-r));
/* if we couldn't read the gid, then it will be 0, but that's
* fine and we shouldn't try to resolve the group again, so
@@ -232,9 +227,9 @@ void server_fix_perms(Server *s, JournalFile *f, uid_t uid) {
}
}
+ /* We do not recalculate the mask here, so that the fchmod() mask above stays intact. */
if (acl_get_permset(entry, &permset) < 0 ||
- acl_add_perm(permset, ACL_READ) < 0 ||
- acl_calc_mask(&acl) < 0) {
+ acl_add_perm(permset, ACL_READ) < 0) {
log_warning("Failed to patch ACL on %s, ignoring: %m", f->path);
goto finish;
}
@@ -347,6 +342,33 @@ void server_rotate(Server *s) {
}
}
+void server_sync(Server *s) {
+ JournalFile *f;
+ void *k;
+ Iterator i;
+ int r;
+
+ static const struct itimerspec sync_timer_disable = {};
+
+ if (s->system_journal) {
+ r = journal_file_set_offline(s->system_journal);
+ if (r < 0)
+ log_error("Failed to sync system journal: %s", strerror(-r));
+ }
+
+ HASHMAP_FOREACH_KEY(f, k, s->user_journals, i) {
+ r = journal_file_set_offline(f);
+ if (r < 0)
+ log_error("Failed to sync user journal: %s", strerror(-r));
+ }
+
+ r = timerfd_settime(s->sync_timer_fd, 0, &sync_timer_disable, NULL);
+ if (r < 0)
+ log_error("Failed to disable max timer: %m");
+
+ s->sync_scheduled = false;
+}
+
void server_vacuum(Server *s) {
char *p;
char ids[33];
@@ -394,48 +416,6 @@ void server_vacuum(Server *s) {
s->cached_available_space_timestamp = 0;
}
-static char *shortened_cgroup_path(pid_t pid) {
- int r;
- char *process_path, *init_path, *path;
-
- assert(pid > 0);
-
- r = cg_get_by_pid(SYSTEMD_CGROUP_CONTROLLER, pid, &process_path);
- if (r < 0)
- return NULL;
-
- r = cg_get_by_pid(SYSTEMD_CGROUP_CONTROLLER, 1, &init_path);
- if (r < 0) {
- free(process_path);
- return NULL;
- }
-
- if (endswith(init_path, "/system"))
- init_path[strlen(init_path) - 7] = 0;
- else if (streq(init_path, "/"))
- init_path[0] = 0;
-
- if (startswith(process_path, init_path)) {
- char *p;
-
- p = strdup(process_path + strlen(init_path));
- if (!p) {
- free(process_path);
- free(init_path);
- return NULL;
- }
- path = p;
- } else {
- path = process_path;
- process_path = NULL;
- }
-
- free(process_path);
- free(init_path);
-
- return path;
-}
-
bool shall_try_append_again(JournalFile *f, int r) {
/* -E2BIG Hit configured limit
@@ -490,8 +470,10 @@ static void write_to_journal(Server *s, uid_t uid, struct iovec *iovec, unsigned
}
r = journal_file_append_entry(f, NULL, iovec, n, &s->seqnum, NULL, NULL);
- if (r >= 0)
+ if (r >= 0) {
+ server_schedule_sync(s);
return;
+ }
if (vacuumed || !shall_try_append_again(f, r)) {
log_error("Failed to write entry, ignoring: %s", strerror(-r));
@@ -519,18 +501,26 @@ static void dispatch_message_real(
const char *label, size_t label_len,
const char *unit_id) {
- char *pid = NULL, *uid = NULL, *gid = NULL,
- *source_time = NULL, *boot_id = NULL, *machine_id = NULL,
- *comm = NULL, *cmdline = NULL, *hostname = NULL,
- *audit_session = NULL, *audit_loginuid = NULL,
- *exe = NULL, *cgroup = NULL, *session = NULL,
- *owner_uid = NULL, *unit = NULL, *selinux_context = NULL;
-
- char idbuf[33];
+ char pid[sizeof("_PID=") + DECIMAL_STR_MAX(pid_t)],
+ uid[sizeof("_UID=") + DECIMAL_STR_MAX(uid_t)],
+ gid[sizeof("_GID=") + DECIMAL_STR_MAX(gid_t)],
+ owner_uid[sizeof("_SYSTEMD_OWNER_UID=") + DECIMAL_STR_MAX(uid_t)],
+ source_time[sizeof("_SOURCE_REALTIME_TIMESTAMP=") + DECIMAL_STR_MAX(usec_t)],
+ boot_id[sizeof("_BOOT_ID=") + 32] = "_BOOT_ID=",
+ machine_id[sizeof("_MACHINE_ID=") + 32] = "_MACHINE_ID=";
+ char *comm, *exe, *cmdline, *cgroup, *session, *unit, *hostname;
sd_id128_t id;
int r;
- char *t;
- uid_t loginuid = 0, realuid = 0;
+ char *t, *c;
+ uid_t realuid = 0, owner = 0, journal_uid;
+ bool owner_valid = false;
+#ifdef HAVE_AUDIT
+ char audit_session[sizeof("_AUDIT_SESSION=") + DECIMAL_STR_MAX(uint32_t)],
+ audit_loginuid[sizeof("_AUDIT_LOGINUID=") + DECIMAL_STR_MAX(uid_t)];
+
+ uint32_t audit;
+ uid_t loginuid;
+#endif
assert(s);
assert(iovec);
@@ -538,165 +528,154 @@ static void dispatch_message_real(
assert(n + N_IOVEC_META_FIELDS <= m);
if (ucred) {
- uint32_t audit;
-#ifdef HAVE_LOGIND
- uid_t owner;
-#endif
-
realuid = ucred->uid;
- if (asprintf(&pid, "_PID=%lu", (unsigned long) ucred->pid) >= 0)
- IOVEC_SET_STRING(iovec[n++], pid);
+ sprintf(pid, "_PID=%lu", (unsigned long) ucred->pid);
+ IOVEC_SET_STRING(iovec[n++], pid);
- if (asprintf(&uid, "_UID=%lu", (unsigned long) ucred->uid) >= 0)
- IOVEC_SET_STRING(iovec[n++], uid);
+ sprintf(uid, "_UID=%lu", (unsigned long) ucred->uid);
+ IOVEC_SET_STRING(iovec[n++], uid);
- if (asprintf(&gid, "_GID=%lu", (unsigned long) ucred->gid) >= 0)
- IOVEC_SET_STRING(iovec[n++], gid);
+ sprintf(gid, "_GID=%lu", (unsigned long) ucred->gid);
+ IOVEC_SET_STRING(iovec[n++], gid);
r = get_process_comm(ucred->pid, &t);
if (r >= 0) {
- comm = strappend("_COMM=", t);
+ comm = strappenda("_COMM=", t);
free(t);
-
- if (comm)
- IOVEC_SET_STRING(iovec[n++], comm);
+ IOVEC_SET_STRING(iovec[n++], comm);
}
r = get_process_exe(ucred->pid, &t);
if (r >= 0) {
- exe = strappend("_EXE=", t);
+ exe = strappenda("_EXE=", t);
free(t);
-
- if (exe)
- IOVEC_SET_STRING(iovec[n++], exe);
+ IOVEC_SET_STRING(iovec[n++], exe);
}
- r = get_process_cmdline(ucred->pid, LINE_MAX, false, &t);
+ r = get_process_cmdline(ucred->pid, 0, false, &t);
if (r >= 0) {
- cmdline = strappend("_CMDLINE=", t);
+ cmdline = strappenda("_CMDLINE=", t);
free(t);
-
- if (cmdline)
- IOVEC_SET_STRING(iovec[n++], cmdline);
+ IOVEC_SET_STRING(iovec[n++], cmdline);
}
+#ifdef HAVE_AUDIT
r = audit_session_from_pid(ucred->pid, &audit);
- if (r >= 0)
- if (asprintf(&audit_session, "_AUDIT_SESSION=%lu", (unsigned long) audit) >= 0)
- IOVEC_SET_STRING(iovec[n++], audit_session);
+ if (r >= 0) {
+ sprintf(audit_session, "_AUDIT_SESSION=%lu", (unsigned long) audit);
+ IOVEC_SET_STRING(iovec[n++], audit_session);
+ }
r = audit_loginuid_from_pid(ucred->pid, &loginuid);
- if (r >= 0)
- if (asprintf(&audit_loginuid, "_AUDIT_LOGINUID=%lu", (unsigned long) loginuid) >= 0)
- IOVEC_SET_STRING(iovec[n++], audit_loginuid);
-
- t = shortened_cgroup_path(ucred->pid);
- if (t) {
- cgroup = strappend("_SYSTEMD_CGROUP=", t);
- free(t);
-
- if (cgroup)
- IOVEC_SET_STRING(iovec[n++], cgroup);
+ if (r >= 0) {
+ sprintf(audit_loginuid, "_AUDIT_LOGINUID=%lu", (unsigned long) loginuid);
+ IOVEC_SET_STRING(iovec[n++], audit_loginuid);
}
+#endif
-#ifdef HAVE_LOGIND
- if (sd_pid_get_session(ucred->pid, &t) >= 0) {
- session = strappend("_SYSTEMD_SESSION=", t);
- free(t);
+ r = cg_pid_get_path_shifted(ucred->pid, NULL, &c);
+ if (r >= 0) {
+ cgroup = strappenda("_SYSTEMD_CGROUP=", c);
+ IOVEC_SET_STRING(iovec[n++], cgroup);
- if (session)
+ r = cg_path_get_session(c, &t);
+ if (r >= 0) {
+ session = strappenda("_SYSTEMD_SESSION=", t);
+ free(t);
IOVEC_SET_STRING(iovec[n++], session);
- }
+ }
- if (sd_pid_get_owner_uid(ucred->uid, &owner) >= 0)
- if (asprintf(&owner_uid, "_SYSTEMD_OWNER_UID=%lu", (unsigned long) owner) >= 0)
- IOVEC_SET_STRING(iovec[n++], owner_uid);
-#endif
+ if (cg_path_get_owner_uid(c, &owner) >= 0) {
+ owner_valid = true;
- if (cg_pid_get_unit(ucred->pid, &t) >= 0) {
- unit = strappend("_SYSTEMD_UNIT=", t);
- free(t);
- } else if (unit_id)
- unit = strappend("_SYSTEMD_UNIT=", unit_id);
+ sprintf(owner_uid, "_SYSTEMD_OWNER_UID=%lu", (unsigned long) owner);
+ IOVEC_SET_STRING(iovec[n++], owner_uid);
+ }
- if (unit)
- IOVEC_SET_STRING(iovec[n++], unit);
+ if (cg_path_get_unit(c, &t) >= 0) {
+ unit = strappenda("_SYSTEMD_UNIT=", t);
+ free(t);
+ } else if (cg_path_get_user_unit(c, &t) >= 0) {
+ unit = strappenda("_SYSTEMD_USER_UNIT=", t);
+ free(t);
+ } else if (unit_id) {
+ if (session)
+ unit = strappenda("_SYSTEMD_USER_UNIT=", unit_id);
+ else
+ unit = strappenda("_SYSTEMD_UNIT=", unit_id);
+ } else
+ unit = NULL;
+
+ if (unit)
+ IOVEC_SET_STRING(iovec[n++], unit);
+
+ free(c);
+ }
#ifdef HAVE_SELINUX
if (label) {
- selinux_context = malloc(sizeof("_SELINUX_CONTEXT=") + label_len);
- if (selinux_context) {
- memcpy(selinux_context, "_SELINUX_CONTEXT=", sizeof("_SELINUX_CONTEXT=")-1);
- memcpy(selinux_context+sizeof("_SELINUX_CONTEXT=")-1, label, label_len);
- selinux_context[sizeof("_SELINUX_CONTEXT=")-1+label_len] = 0;
- IOVEC_SET_STRING(iovec[n++], selinux_context);
- }
+ char *selinux_context = alloca(sizeof("_SELINUX_CONTEXT=") + label_len);
+
+ *((char*) mempcpy(stpcpy(selinux_context, "_SELINUX_CONTEXT="), label, label_len)) = 0;
+ IOVEC_SET_STRING(iovec[n++], selinux_context);
} else {
security_context_t con;
if (getpidcon(ucred->pid, &con) >= 0) {
- selinux_context = strappend("_SELINUX_CONTEXT=", con);
- if (selinux_context)
- IOVEC_SET_STRING(iovec[n++], selinux_context);
+ char *selinux_context = strappenda("_SELINUX_CONTEXT=", con);
freecon(con);
+ IOVEC_SET_STRING(iovec[n++], selinux_context);
}
}
#endif
}
if (tv) {
- if (asprintf(&source_time, "_SOURCE_REALTIME_TIMESTAMP=%llu",
- (unsigned long long) timeval_load(tv)) >= 0)
- IOVEC_SET_STRING(iovec[n++], source_time);
+ sprintf(source_time, "_SOURCE_REALTIME_TIMESTAMP=%llu", (unsigned long long) timeval_load(tv));
+ IOVEC_SET_STRING(iovec[n++], source_time);
}
/* Note that strictly speaking storing the boot id here is
* redundant since the entry includes this in-line
* anyway. However, we need this indexed, too. */
r = sd_id128_get_boot(&id);
- if (r >= 0)
- if (asprintf(&boot_id, "_BOOT_ID=%s", sd_id128_to_string(id, idbuf)) >= 0)
- IOVEC_SET_STRING(iovec[n++], boot_id);
+ if (r >= 0) {
+ sd_id128_to_string(id, boot_id + sizeof("_BOOT_ID=") - 1);
+ IOVEC_SET_STRING(iovec[n++], boot_id);
+ }
r = sd_id128_get_machine(&id);
- if (r >= 0)
- if (asprintf(&machine_id, "_MACHINE_ID=%s", sd_id128_to_string(id, idbuf)) >= 0)
- IOVEC_SET_STRING(iovec[n++], machine_id);
+ if (r >= 0) {
+ sd_id128_to_string(id, machine_id + sizeof("_MACHINE_ID=") - 1);
+ IOVEC_SET_STRING(iovec[n++], machine_id);
+ }
t = gethostname_malloc();
if (t) {
- hostname = strappend("_HOSTNAME=", t);
+ hostname = strappenda("_HOSTNAME=", t);
free(t);
- if (hostname)
- IOVEC_SET_STRING(iovec[n++], hostname);
+ IOVEC_SET_STRING(iovec[n++], hostname);
}
assert(n <= m);
- write_to_journal(s,
- s->split_mode == SPLIT_NONE ? 0 :
- (s->split_mode == SPLIT_UID ? realuid :
- (realuid == 0 ? 0 : loginuid)), iovec, n);
-
- free(pid);
- free(uid);
- free(gid);
- free(comm);
- free(exe);
- free(cmdline);
- free(source_time);
- free(boot_id);
- free(machine_id);
- free(hostname);
- free(audit_session);
- free(audit_loginuid);
- free(cgroup);
- free(session);
- free(owner_uid);
- free(unit);
- free(selinux_context);
+ if (s->split_mode == SPLIT_UID && realuid > 0)
+ /* Split up strictly by any UID */
+ journal_uid = realuid;
+ else if (s->split_mode == SPLIT_LOGIN && realuid > 0 && owner_valid && owner > 0)
+ /* Split up by login UIDs, this avoids creation of
+ * individual journals for system UIDs. We do this
+ * only if the realuid is not root, in order not to
+ * accidentally leak privileged information to the
+ * user that is logged by a privileged process that is
+ * part of an unprivileged session.*/
+ journal_uid = owner;
+ else
+ journal_uid = 0;
+
+ write_to_journal(s, journal_uid, iovec, n);
}
void server_driver_message(Server *s, sd_id128_t message_id, const char *format, ...) {
@@ -705,7 +684,7 @@ void server_driver_message(Server *s, sd_id128_t message_id, const char *format,
struct iovec iovec[N_IOVEC_META_FIELDS + 4];
int n = 0;
va_list ap;
- struct ucred ucred;
+ struct ucred ucred = {};
assert(s);
assert(format);
@@ -726,7 +705,6 @@ void server_driver_message(Server *s, sd_id128_t message_id, const char *format,
IOVEC_SET_STRING(iovec[n++], mid);
}
- zero(ucred);
ucred.pid = getpid();
ucred.uid = getuid();
ucred.gid = getgid();
@@ -743,8 +721,9 @@ void server_dispatch_message(
const char *unit_id,
int priority) {
- int rl;
- char *path = NULL, *c;
+ int rl, r;
+ _cleanup_free_ char *path = NULL;
+ char *c;
assert(s);
assert(iovec || n == 0);
@@ -758,8 +737,8 @@ void server_dispatch_message(
if (!ucred)
goto finish;
- path = shortened_cgroup_path(ucred->pid);
- if (!path)
+ r = cg_pid_get_path_shifted(ucred->pid, NULL, &path);
+ if (r < 0)
goto finish;
/* example: /user/lennart/3/foobar
@@ -778,18 +757,16 @@ void server_dispatch_message(
}
}
- rl = journal_rate_limit_test(s->rate_limit, path, priority & LOG_PRIMASK, available_space(s));
+ rl = journal_rate_limit_test(s->rate_limit, path,
+ priority & LOG_PRIMASK, available_space(s));
- if (rl == 0) {
- free(path);
+ if (rl == 0)
return;
- }
/* Write a suppression message if we suppressed something */
if (rl > 1)
- server_driver_message(s, SD_MESSAGE_JOURNAL_DROPPED, "Suppressed %u messages from %s", rl - 1, path);
-
- free(path);
+ server_driver_message(s, SD_MESSAGE_JOURNAL_DROPPED,
+ "Suppressed %u messages from %s", rl - 1, path);
finish:
dispatch_message_real(s, iovec, n, m, ucred, tv, label, label_len, unit_id);
@@ -961,6 +938,12 @@ int server_flush_to_var(Server *s) {
server_rotate(s);
server_vacuum(s);
+ if (!s->system_journal) {
+ log_notice("Didn't flush runtime journal since rotation of system journal wasn't successful.");
+ r = -EIO;
+ goto finish;
+ }
+
log_debug("Retrying write.");
r = journal_file_copy_entry(f, s->system_journal, o, f->current_offset, NULL, NULL, NULL);
if (r < 0) {
@@ -978,8 +961,7 @@ finish:
if (r >= 0)
rm_rf("/run/log/journal", false, true, false);
- if (j)
- sd_journal_close(j);
+ sd_journal_close(j);
return r;
}
@@ -1009,11 +991,10 @@ int process_event(Server *s, struct epoll_event *ev) {
return -errno;
}
- log_info("Received SIG%s", signal_to_string(sfsi.ssi_signo));
-
if (sfsi.ssi_signo == SIGUSR1) {
touch("/run/systemd/journal/flushed");
server_flush_to_var(s);
+ server_sync(s);
return 1;
}
@@ -1023,8 +1004,23 @@ int process_event(Server *s, struct epoll_event *ev) {
return 1;
}
+ log_info("Received SIG%s", signal_to_string(sfsi.ssi_signo));
+
return 0;
+ } else if (ev->data.fd == s->sync_timer_fd) {
+ int r;
+ uint64_t t;
+
+ log_debug("Got sync request from epoll.");
+
+ r = read(ev->data.fd, (void *)&t, sizeof(t));
+ if (r < 0)
+ return 0;
+
+ server_sync(s);
+ return 1;
+
} else if (ev->data.fd == s->dev_kmsg_fd) {
int r;
@@ -1234,7 +1230,8 @@ static int open_signalfd(Server *s) {
}
static int server_parse_proc_cmdline(Server *s) {
- char *line, *w, *state;
+ _cleanup_free_ char *line = NULL;
+ char *w, *state;
int r;
size_t l;
@@ -1248,13 +1245,11 @@ static int server_parse_proc_cmdline(Server *s) {
}
FOREACH_WORD_QUOTED(w, l, line, state) {
- char *word;
+ _cleanup_free_ char *word;
word = strndup(w, l);
- if (!word) {
- r = -ENOMEM;
- goto finish;
- }
+ if (!word)
+ return -ENOMEM;
if (startswith(word, "systemd.journald.forward_to_syslog=")) {
r = parse_boolean(word + 35);
@@ -1276,25 +1271,18 @@ static int server_parse_proc_cmdline(Server *s) {
s->forward_to_console = r;
} else if (startswith(word, "systemd.journald"))
log_warning("Invalid systemd.journald parameter. Ignoring.");
-
- free(word);
}
- r = 0;
-
-finish:
- free(line);
- return r;
+ return 0;
}
static int server_parse_config_file(Server *s) {
- FILE *f;
- const char *fn;
+ static const char fn[] = "/etc/systemd/journald.conf";
+ _cleanup_fclose_ FILE *f = NULL;
int r;
assert(s);
- fn = "/etc/systemd/journald.conf";
f = fopen(fn, "re");
if (!f) {
if (errno == ENOENT)
@@ -1304,25 +1292,75 @@ static int server_parse_config_file(Server *s) {
return -errno;
}
- r = config_parse(fn, f, "Journal\0", config_item_perf_lookup, (void*) journald_gperf_lookup, false, s);
+ r = config_parse(NULL, fn, f, "Journal\0", config_item_perf_lookup,
+ (void*) journald_gperf_lookup, false, false, s);
if (r < 0)
log_warning("Failed to parse configuration file: %s", strerror(-r));
- fclose(f);
-
return r;
}
+static int server_open_sync_timer(Server *s) {
+ int r;
+ struct epoll_event ev;
+
+ assert(s);
+
+ s->sync_timer_fd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC);
+ if (s->sync_timer_fd < 0)
+ return -errno;
+
+ zero(ev);
+ ev.events = EPOLLIN;
+ ev.data.fd = s->sync_timer_fd;
+
+ r = epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, s->sync_timer_fd, &ev);
+ if (r < 0) {
+ log_error("Failed to add idle timer fd to epoll object: %m");
+ return -errno;
+ }
+
+ return 0;
+}
+
+int server_schedule_sync(Server *s) {
+ int r;
+
+ assert(s);
+
+ if (s->sync_scheduled)
+ return 0;
+
+ if (s->sync_interval_usec) {
+ struct itimerspec sync_timer_enable = {
+ .it_value.tv_sec = s->sync_interval_usec / USEC_PER_SEC,
+ .it_value.tv_nsec = s->sync_interval_usec % MSEC_PER_SEC,
+ };
+
+ r = timerfd_settime(s->sync_timer_fd, 0, &sync_timer_enable, NULL);
+ if (r < 0)
+ return -errno;
+ }
+
+ s->sync_scheduled = true;
+
+ return 0;
+}
+
int server_init(Server *s) {
int n, r, fd;
assert(s);
zero(*s);
- s->syslog_fd = s->native_fd = s->stdout_fd = s->signal_fd = s->epoll_fd = s->dev_kmsg_fd = -1;
+ s->sync_timer_fd = s->syslog_fd = s->native_fd = s->stdout_fd =
+ s->signal_fd = s->epoll_fd = s->dev_kmsg_fd = -1;
s->compress = true;
s->seal = true;
+ s->sync_interval_usec = DEFAULT_SYNC_INTERVAL_USEC;
+ s->sync_scheduled = false;
+
s->rate_limit_interval = DEFAULT_RATE_LIMIT_INTERVAL;
s->rate_limit_burst = DEFAULT_RATE_LIMIT_BURST;
@@ -1338,6 +1376,12 @@ int server_init(Server *s) {
server_parse_config_file(s);
server_parse_proc_cmdline(s);
+ if (!!s->rate_limit_interval ^ !!s->rate_limit_burst) {
+ log_debug("Setting both rate limit interval and burst from %llu,%u to 0,0",
+ (long long unsigned) s->rate_limit_interval,
+ s->rate_limit_burst);
+ s->rate_limit_interval = s->rate_limit_burst = 0;
+ }
mkdir_p("/run/systemd/journal", 0755);
@@ -1416,6 +1460,10 @@ int server_init(Server *s) {
if (r < 0)
return r;
+ r = server_open_sync_timer(s);
+ if (r < 0)
+ return r;
+
r = open_signalfd(s);
if (r < 0)
return r;
@@ -1424,7 +1472,8 @@ int server_init(Server *s) {
if (!s->udev)
return -ENOMEM;
- s->rate_limit = journal_rate_limit_new(s->rate_limit_interval, s->rate_limit_burst);
+ s->rate_limit = journal_rate_limit_new(s->rate_limit_interval,
+ s->rate_limit_burst);
if (!s->rate_limit)
return -ENOMEM;
@@ -1487,6 +1536,9 @@ void server_done(Server *s) {
if (s->dev_kmsg_fd >= 0)
close_nointr_nofail(s->dev_kmsg_fd);
+ if (s->sync_timer_fd >= 0)
+ close_nointr_nofail(s->sync_timer_fd);
+
if (s->rate_limit)
journal_rate_limit_free(s->rate_limit);
diff --git a/src/journal/journald-server.h b/src/journal/journald-server.h
index 9f50a29e50..129f7e8ab4 100644
--- a/src/journal/journald-server.h
+++ b/src/journal/journald-server.h
@@ -71,6 +71,7 @@ typedef struct Server {
size_t buffer_size;
JournalRateLimit *rate_limit;
+ usec_t sync_interval_usec;
usec_t rate_limit_interval;
unsigned rate_limit_burst;
@@ -119,6 +120,9 @@ typedef struct Server {
uint64_t *kernel_seqnum;
struct udev *udev;
+
+ int sync_timer_fd;
+ bool sync_scheduled;
} Server;
#define N_IOVEC_META_FIELDS 17
@@ -126,27 +130,29 @@ typedef struct Server {
#define N_IOVEC_UDEV_FIELDS 32
void server_dispatch_message(Server *s, struct iovec *iovec, unsigned n, unsigned m, struct ucred *ucred, struct timeval *tv, const char *label, size_t label_len, const char *unit_id, int priority);
-void server_driver_message(Server *s, sd_id128_t message_id, const char *format, ...);
+void server_driver_message(Server *s, sd_id128_t message_id, const char *format, ...) _printf_attr_(3,4);
/* gperf lookup function */
const struct ConfigPerfItem* journald_gperf_lookup(const char *key, unsigned length);
-int config_parse_storage(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_storage(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-const char *storage_to_string(Storage s);
-Storage storage_from_string(const char *s);
+const char *storage_to_string(Storage s) _const_;
+Storage storage_from_string(const char *s) _pure_;
-int config_parse_split_mode(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_split_mode(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-const char *split_mode_to_string(SplitMode s);
-SplitMode split_mode_from_string(const char *s);
+const char *split_mode_to_string(SplitMode s) _const_;
+SplitMode split_mode_from_string(const char *s) _pure_;
void server_fix_perms(Server *s, JournalFile *f, uid_t uid);
bool shall_try_append_again(JournalFile *f, int r);
int server_init(Server *s);
void server_done(Server *s);
+void server_sync(Server *s);
void server_vacuum(Server *s);
void server_rotate(Server *s);
+int server_schedule_sync(Server *s);
int server_flush_to_var(Server *s);
int process_event(Server *s, struct epoll_event *ev);
void server_maybe_append_tags(Server *s);
diff --git a/src/journal/journald-stream.c b/src/journal/journald-stream.c
index 7b88f747db..6d51c29083 100644
--- a/src/journal/journald-stream.c
+++ b/src/journal/journald-stream.c
@@ -175,7 +175,7 @@ static int stdout_stream_line(StdoutStream *s, char *p) {
case STDOUT_STREAM_PRIORITY:
r = safe_atoi(p, &s->priority);
- if (r < 0 || s->priority <= 0 || s->priority >= 999) {
+ if (r < 0 || s->priority < 0 || s->priority > 999) {
log_warning("Failed to parse log priority line.");
return -EINVAL;
}
@@ -412,13 +412,16 @@ fail:
}
int server_open_stdout_socket(Server *s) {
- union sockaddr_union sa;
int r;
struct epoll_event ev;
assert(s);
if (s->stdout_fd < 0) {
+ union sockaddr_union sa = {
+ .un.sun_family = AF_UNIX,
+ .un.sun_path = "/run/systemd/journal/stdout",
+ };
s->stdout_fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
if (s->stdout_fd < 0) {
@@ -426,10 +429,6 @@ int server_open_stdout_socket(Server *s) {
return -errno;
}
- zero(sa);
- sa.un.sun_family = AF_UNIX;
- strncpy(sa.un.sun_path, "/run/systemd/journal/stdout", sizeof(sa.un.sun_path));
-
unlink(sa.un.sun_path);
r = bind(s->stdout_fd, &sa.sa, offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path));
diff --git a/src/journal/journald-syslog.c b/src/journal/journald-syslog.c
index afddca3630..000f5acc10 100644
--- a/src/journal/journald-syslog.c
+++ b/src/journal/journald-syslog.c
@@ -34,28 +34,28 @@
#define WARN_FORWARD_SYSLOG_MISSED_USEC (30 * USEC_PER_SEC)
static void forward_syslog_iovec(Server *s, const struct iovec *iovec, unsigned n_iovec, struct ucred *ucred, struct timeval *tv) {
- struct msghdr msghdr;
+
+ union sockaddr_union sa = {
+ .un.sun_family = AF_UNIX,
+ .un.sun_path = "/run/systemd/journal/syslog",
+ };
+ struct msghdr msghdr = {
+ .msg_iov = (struct iovec *) iovec,
+ .msg_iovlen = n_iovec,
+ .msg_name = &sa,
+ .msg_namelen = offsetof(union sockaddr_union, un.sun_path)
+ + sizeof("/run/systemd/journal/syslog") - 1,
+ };
struct cmsghdr *cmsg;
union {
struct cmsghdr cmsghdr;
uint8_t buf[CMSG_SPACE(sizeof(struct ucred))];
} control;
- union sockaddr_union sa;
assert(s);
assert(iovec);
assert(n_iovec > 0);
- zero(msghdr);
- msghdr.msg_iov = (struct iovec*) iovec;
- msghdr.msg_iovlen = n_iovec;
-
- zero(sa);
- sa.un.sun_family = AF_UNIX;
- strncpy(sa.un.sun_path, "/run/systemd/journal/syslog", sizeof(sa.un.sun_path));
- msghdr.msg_name = &sa;
- msghdr.msg_namelen = offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path);
-
if (ucred) {
zero(control);
msghdr.msg_control = &control;
@@ -412,13 +412,16 @@ void server_process_syslog_message(
}
int server_open_syslog_socket(Server *s) {
- union sockaddr_union sa;
int one, r;
struct epoll_event ev;
assert(s);
if (s->syslog_fd < 0) {
+ union sockaddr_union sa = {
+ .un.sun_family = AF_UNIX,
+ .un.sun_path = "/dev/log",
+ };
s->syslog_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
if (s->syslog_fd < 0) {
@@ -426,10 +429,6 @@ int server_open_syslog_socket(Server *s) {
return -errno;
}
- zero(sa);
- sa.un.sun_family = AF_UNIX;
- strncpy(sa.un.sun_path, "/dev/log", sizeof(sa.un.sun_path));
-
unlink(sa.un.sun_path);
r = bind(s->syslog_fd, &sa.sa, offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path));
diff --git a/src/journal/journald-syslog.h b/src/journal/journald-syslog.h
index 7ff215b524..324b70eef0 100644
--- a/src/journal/journald-syslog.h
+++ b/src/journal/journald-syslog.h
@@ -23,7 +23,7 @@
#include "journald-server.h"
-int syslog_fixup_facility(int priority);
+int syslog_fixup_facility(int priority) _const_;
void syslog_parse_priority(char **p, int *priority);
size_t syslog_parse_identifier(const char **buf, char **identifier, char **pid);
diff --git a/src/journal/journald.c b/src/journal/journald.c
index d6b9be5974..5fb10b1d3f 100644
--- a/src/journal/journald.c
+++ b/src/journal/journald.c
@@ -88,7 +88,6 @@ int main(int argc, char *argv[]) {
/* Calculate when to rotate the next time */
t = (int) ((server.oldest_file_usec + server.max_retention_usec - n + USEC_PER_MSEC - 1) / USEC_PER_MSEC);
- log_info("Sleeping for %i ms", t);
}
#ifdef HAVE_GCRYPT
diff --git a/src/journal/journald.conf b/src/journal/journald.conf
index 948318bc62..5410477201 100644
--- a/src/journal/journald.conf
+++ b/src/journal/journald.conf
@@ -12,6 +12,7 @@
#Compress=yes
#Seal=yes
#SplitMode=login
+#SyncIntervalSec=5m
#RateLimitInterval=10s
#RateLimitBurst=200
#SystemMaxUse=
diff --git a/src/journal/libsystemd-journal.sym b/src/journal/libsystemd-journal.sym
index 7b602f59cb..449f37c4da 100644
--- a/src/journal/libsystemd-journal.sym
+++ b/src/journal/libsystemd-journal.sym
@@ -83,9 +83,24 @@ global:
LIBSYSTEMD_JOURNAL_196 {
global:
- sd_journal_fd_reliable;
sd_journal_get_catalog;
sd_journal_get_catalog_for_message_id;
sd_journal_set_data_threshold;
sd_journal_get_data_threshold;
} LIBSYSTEMD_JOURNAL_195;
+
+LIBSYSTEMD_JOURNAL_198 {
+global:
+ sd_journal_reliable_fd;
+} LIBSYSTEMD_JOURNAL_196;
+
+LIBSYSTEMD_JOURNAL_201 {
+global:
+ sd_journal_get_events;
+ sd_journal_get_timeout;
+} LIBSYSTEMD_JOURNAL_198;
+
+LIBSYSTEMD_JOURNAL_202 {
+global:
+ sd_journal_add_conjunction;
+} LIBSYSTEMD_JOURNAL_201;
diff --git a/src/journal/lookup3.h b/src/journal/lookup3.h
index 502b42c209..3224473a6a 100644
--- a/src/journal/lookup3.h
+++ b/src/journal/lookup3.h
@@ -5,13 +5,15 @@
#include <inttypes.h>
#include <sys/types.h>
-uint32_t jenkins_hashword(const uint32_t *k, size_t length, uint32_t initval);
+#include "macro.h"
+
+uint32_t jenkins_hashword(const uint32_t *k, size_t length, uint32_t initval) _pure_;
void jenkins_hashword2(const uint32_t *k, size_t length, uint32_t *pc, uint32_t *pb);
-uint32_t jenkins_hashlittle(const void *key, size_t length, uint32_t initval);
+uint32_t jenkins_hashlittle(const void *key, size_t length, uint32_t initval) _pure_;
void jenkins_hashlittle2(const void *key, size_t length, uint32_t *pc, uint32_t *pb);
-uint32_t jenkins_hashbig(const void *key, size_t length, uint32_t initval);
+uint32_t jenkins_hashbig(const void *key, size_t length, uint32_t initval) _pure_;
static inline uint64_t hash64(const void *data, size_t length) {
uint32_t a = 0, b = 0;
diff --git a/src/journal/microhttpd-util.c b/src/journal/microhttpd-util.c
new file mode 100644
index 0000000000..382087c790
--- /dev/null
+++ b/src/journal/microhttpd-util.c
@@ -0,0 +1,37 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2012 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 <http://www.gnu.org/licenses/>.
+***/
+
+#include <stddef.h>
+#include <stdio.h>
+
+#include "microhttpd-util.h"
+#include "log.h"
+#include "macro.h"
+#include "util.h"
+
+void microhttpd_logger(void *arg, const char *fmt, va_list ap) {
+ _cleanup_free_ char *f;
+ if (asprintf(&f, "microhttpd: %s", fmt) <= 0) {
+ log_oom();
+ return;
+ }
+ log_metav(LOG_INFO, NULL, 0, NULL, f, ap);
+}
diff --git a/src/journal/microhttpd-util.h b/src/journal/microhttpd-util.h
new file mode 100644
index 0000000000..20ad76990c
--- /dev/null
+++ b/src/journal/microhttpd-util.h
@@ -0,0 +1,28 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2012 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 <http://www.gnu.org/licenses/>.
+***/
+
+#pragma once
+
+#include <stdarg.h>
+
+#include "macro.h"
+
+void microhttpd_logger(void *arg, const char *fmt, va_list ap) _printf_attr_(2, 0);
diff --git a/src/journal/mmap-cache.c b/src/journal/mmap-cache.c
index 251aefe121..767f555526 100644
--- a/src/journal/mmap-cache.c
+++ b/src/journal/mmap-cache.c
@@ -41,9 +41,9 @@ struct Window {
bool keep_always;
bool in_unused;
+ int prot;
void *ptr;
uint64_t offset;
- int prot;
size_t size;
FileDescriptor *fd;
@@ -70,12 +70,11 @@ struct FileDescriptor {
struct MMapCache {
int n_ref;
+ unsigned n_windows;
Hashmap *fds;
Hashmap *contexts;
- unsigned n_windows;
-
LIST_HEAD(Window, unused);
Window *last_unused;
};
@@ -134,7 +133,7 @@ static void window_free(Window *w) {
free(w);
}
-static bool window_matches(Window *w, int fd, int prot, uint64_t offset, size_t size) {
+_pure_ static bool window_matches(Window *w, int fd, int prot, uint64_t offset, size_t size) {
assert(w);
assert(fd >= 0);
assert(size > 0);
diff --git a/src/journal/sd-journal.c b/src/journal/sd-journal.c
index 095fbb249c..c21712b7c4 100644
--- a/src/journal/sd-journal.c
+++ b/src/journal/sd-journal.c
@@ -49,6 +49,21 @@
#define DEFAULT_DATA_THRESHOLD (64*1024)
+/* We return an error here only if we didn't manage to
+ memorize the real error. */
+static int set_put_error(sd_journal *j, int r) {
+ int k;
+
+ if (r >= 0)
+ return r;
+
+ k = set_ensure_allocated(&j->errors, trivial_hash_func, trivial_compare_func);
+ if (k < 0)
+ return k;
+
+ return set_put(j->errors, INT_TO_PTR(r));
+}
+
static void detach_location(sd_journal *j) {
Iterator i;
JournalFile *f;
@@ -94,6 +109,9 @@ static void set_location(sd_journal *j, LocationType type, JournalFile *f, Objec
init_location(&j->current_location, type, f, o);
+ if (j->current_file)
+ j->current_file->current_offset = 0;
+
j->current_file = f;
j->current_field = 0;
@@ -188,7 +206,7 @@ static void match_free_if_empty(Match *m) {
}
_public_ int sd_journal_add_match(sd_journal *j, const void *data, size_t size) {
- Match *l2, *l3, *add_here = NULL, *m;
+ Match *l3, *l4, *add_here = NULL, *m;
le64_t le_hash;
if (!j)
@@ -203,44 +221,52 @@ _public_ int sd_journal_add_match(sd_journal *j, const void *data, size_t size)
if (!match_is_valid(data, size))
return -EINVAL;
- /* level 0: OR term
- * level 1: AND terms
- * level 2: OR terms
- * level 3: concrete matches */
+ /* level 0: AND term
+ * level 1: OR terms
+ * level 2: AND terms
+ * level 3: OR terms
+ * level 4: concrete matches */
if (!j->level0) {
- j->level0 = match_new(NULL, MATCH_OR_TERM);
+ j->level0 = match_new(NULL, MATCH_AND_TERM);
if (!j->level0)
return -ENOMEM;
}
if (!j->level1) {
- j->level1 = match_new(j->level0, MATCH_AND_TERM);
+ j->level1 = match_new(j->level0, MATCH_OR_TERM);
if (!j->level1)
return -ENOMEM;
}
- assert(j->level0->type == MATCH_OR_TERM);
- assert(j->level1->type == MATCH_AND_TERM);
+ if (!j->level2) {
+ j->level2 = match_new(j->level1, MATCH_AND_TERM);
+ if (!j->level2)
+ return -ENOMEM;
+ }
+
+ assert(j->level0->type == MATCH_AND_TERM);
+ assert(j->level1->type == MATCH_OR_TERM);
+ assert(j->level2->type == MATCH_AND_TERM);
le_hash = htole64(hash64(data, size));
- LIST_FOREACH(matches, l2, j->level1->matches) {
- assert(l2->type == MATCH_OR_TERM);
+ LIST_FOREACH(matches, l3, j->level2->matches) {
+ assert(l3->type == MATCH_OR_TERM);
- LIST_FOREACH(matches, l3, l2->matches) {
- assert(l3->type == MATCH_DISCRETE);
+ LIST_FOREACH(matches, l4, l3->matches) {
+ assert(l4->type == MATCH_DISCRETE);
/* Exactly the same match already? Then ignore
* this addition */
- if (l3->le_hash == le_hash &&
- l3->size == size &&
- memcmp(l3->data, data, size) == 0)
+ if (l4->le_hash == le_hash &&
+ l4->size == size &&
+ memcmp(l4->data, data, size) == 0)
return 0;
/* Same field? Then let's add this to this OR term */
- if (same_field(data, size, l3->data, l3->size)) {
- add_here = l2;
+ if (same_field(data, size, l4->data, l4->size)) {
+ add_here = l3;
break;
}
}
@@ -250,7 +276,7 @@ _public_ int sd_journal_add_match(sd_journal *j, const void *data, size_t size)
}
if (!add_here) {
- add_here = match_new(j->level1, MATCH_OR_TERM);
+ add_here = match_new(j->level2, MATCH_OR_TERM);
if (!add_here)
goto fail;
}
@@ -273,6 +299,9 @@ fail:
if (add_here)
match_free_if_empty(add_here);
+ if (j->level2)
+ match_free_if_empty(j->level2);
+
if (j->level1)
match_free_if_empty(j->level1);
@@ -282,9 +311,7 @@ fail:
return -ENOMEM;
}
-_public_ int sd_journal_add_disjunction(sd_journal *j) {
- Match *m;
-
+_public_ int sd_journal_add_conjunction(sd_journal *j) {
assert(j);
if (!j->level0)
@@ -296,11 +323,28 @@ _public_ int sd_journal_add_disjunction(sd_journal *j) {
if (!j->level1->matches)
return 0;
- m = match_new(j->level0, MATCH_AND_TERM);
- if (!m)
- return -ENOMEM;
+ j->level1 = NULL;
+ j->level2 = NULL;
+
+ return 0;
+}
+
+_public_ int sd_journal_add_disjunction(sd_journal *j) {
+ assert(j);
+
+ if (!j->level0)
+ return 0;
+
+ if (!j->level1)
+ return 0;
+
+ if (!j->level2)
+ return 0;
- j->level1 = m;
+ if (!j->level2->matches)
+ return 0;
+
+ j->level2 = NULL;
return 0;
}
@@ -365,13 +409,13 @@ _public_ void sd_journal_flush_matches(sd_journal *j) {
if (j->level0)
match_free(j->level0);
- j->level0 = j->level1 = NULL;
+ j->level0 = j->level1 = j->level2 = NULL;
detach_location(j);
}
static int compare_entry_order(JournalFile *af, Object *_ao,
- JournalFile *bf, uint64_t bp) {
+ JournalFile *bf, uint64_t bp) {
uint64_t a, b;
Object *ao, *bo;
@@ -454,7 +498,7 @@ static int compare_entry_order(JournalFile *af, Object *_ao,
return 0;
}
-static int compare_with_location(JournalFile *af, Object *ao, Location *l) {
+_pure_ static int compare_with_location(JournalFile *af, Object *ao, Location *l) {
uint64_t a;
assert(af);
@@ -587,7 +631,7 @@ static int next_for_match(
return r;
if ((direction == DIRECTION_DOWN ? cp >= after_offset : cp <= after_offset) &&
- (np == 0 || (direction == DIRECTION_DOWN ? cp > np : np < cp))) {
+ (np == 0 || (direction == DIRECTION_DOWN ? cp > np : cp < np))) {
np = cp;
continue_looking = true;
}
@@ -958,7 +1002,7 @@ _public_ int sd_journal_get_cursor(sd_journal *j, char **cursor) {
(unsigned long long) le64toh(o->entry.xor_hash)) < 0)
return -ENOMEM;
- return 1;
+ return 0;
}
_public_ int sd_journal_seek_cursor(sd_journal *j, const char *cursor) {
@@ -1207,15 +1251,15 @@ static void check_network(sd_journal *j, int fd) {
return;
j->on_network =
- (long)sfs.f_type == (long)CIFS_MAGIC_NUMBER ||
- sfs.f_type == CODA_SUPER_MAGIC ||
- sfs.f_type == NCP_SUPER_MAGIC ||
- sfs.f_type == NFS_SUPER_MAGIC ||
- sfs.f_type == SMB_SUPER_MAGIC;
+ F_TYPE_CMP(sfs.f_type, CIFS_MAGIC_NUMBER) ||
+ F_TYPE_CMP(sfs.f_type, CODA_SUPER_MAGIC) ||
+ F_TYPE_CMP(sfs.f_type, NCP_SUPER_MAGIC) ||
+ F_TYPE_CMP(sfs.f_type, NFS_SUPER_MAGIC) ||
+ F_TYPE_CMP(sfs.f_type, SMB_SUPER_MAGIC);
}
static int add_file(sd_journal *j, const char *prefix, const char *filename) {
- char *path;
+ _cleanup_free_ char *path = NULL;
int r;
JournalFile *f;
@@ -1234,20 +1278,15 @@ static int add_file(sd_journal *j, const char *prefix, const char *filename) {
if (!path)
return -ENOMEM;
- if (hashmap_get(j->files, path)) {
- free(path);
+ if (hashmap_get(j->files, path))
return 0;
- }
if (hashmap_size(j->files) >= JOURNAL_FILES_MAX) {
log_debug("Too many open journal files, not adding %s, ignoring.", path);
- free(path);
- return 0;
+ return set_put_error(j, -ETOOMANYREFS);
}
r = journal_file_open(path, O_RDONLY, 0, false, false, NULL, j->mmap, NULL, &f);
- free(path);
-
if (r < 0) {
if (errno == ENOENT)
return 0;
@@ -1263,12 +1302,12 @@ static int add_file(sd_journal *j, const char *prefix, const char *filename) {
return r;
}
+ log_debug("File %s got added.", f->path);
+
check_network(j, f->fd);
j->current_invalidate_counter ++;
- log_debug("File %s got added.", f->path);
-
return 0;
}
@@ -1311,9 +1350,9 @@ static int remove_file(sd_journal *j, const char *prefix, const char *filename)
}
static int add_directory(sd_journal *j, const char *prefix, const char *dirname) {
- char *path;
+ _cleanup_free_ char *path = NULL;
int r;
- DIR *d;
+ _cleanup_closedir_ DIR *d = NULL;
sd_id128_t id, mid;
Directory *m;
@@ -1321,10 +1360,12 @@ static int add_directory(sd_journal *j, const char *prefix, const char *dirname)
assert(prefix);
assert(dirname);
+ log_debug("Considering %s/%s.", prefix, dirname);
+
if ((j->flags & SD_JOURNAL_LOCAL_ONLY) &&
(sd_id128_from_string(dirname, &id) < 0 ||
sd_id128_get_machine(&mid) < 0 ||
- !sd_id128_equal(id, mid)))
+ !(sd_id128_equal(id, mid) || path_startswith(prefix, "/run"))))
return 0;
path = strjoin(prefix, "/", dirname, NULL);
@@ -1334,8 +1375,6 @@ static int add_directory(sd_journal *j, const char *prefix, const char *dirname)
d = opendir(path);
if (!d) {
log_debug("Failed to open %s: %m", path);
- free(path);
-
if (errno == ENOENT)
return 0;
return -errno;
@@ -1344,32 +1383,24 @@ static int add_directory(sd_journal *j, const char *prefix, const char *dirname)
m = hashmap_get(j->directories_by_path, path);
if (!m) {
m = new0(Directory, 1);
- if (!m) {
- closedir(d);
- free(path);
+ if (!m)
return -ENOMEM;
- }
m->is_root = false;
m->path = path;
if (hashmap_put(j->directories_by_path, m->path, m) < 0) {
- closedir(d);
- free(m->path);
free(m);
return -ENOMEM;
}
+ path = NULL; /* avoid freeing in cleanup */
j->current_invalidate_counter ++;
log_debug("Directory %s got added.", m->path);
- } else if (m->is_root) {
- free (path);
- closedir(d);
+ } else if (m->is_root)
return 0;
- } else
- free(path);
if (m->wd <= 0 && j->inotify_fd >= 0) {
@@ -1393,20 +1424,23 @@ static int add_directory(sd_journal *j, const char *prefix, const char *dirname)
if (dirent_is_file_with_suffix(de, ".journal") ||
dirent_is_file_with_suffix(de, ".journal~")) {
r = add_file(j, m->path, de->d_name);
- if (r < 0)
- log_debug("Failed to add file %s/%s: %s", m->path, de->d_name, strerror(-r));
+ if (r < 0) {
+ log_debug("Failed to add file %s/%s: %s",
+ m->path, de->d_name, strerror(-r));
+ r = set_put_error(j, r);
+ if (r < 0)
+ return r;
+ }
}
}
check_network(j, dirfd(d));
- closedir(d);
-
return 0;
}
static int add_root_directory(sd_journal *j, const char *p) {
- DIR *d;
+ _cleanup_closedir_ DIR *d = NULL;
Directory *m;
int r;
@@ -1424,21 +1458,17 @@ static int add_root_directory(sd_journal *j, const char *p) {
m = hashmap_get(j->directories_by_path, p);
if (!m) {
m = new0(Directory, 1);
- if (!m) {
- closedir(d);
+ if (!m)
return -ENOMEM;
- }
m->is_root = true;
m->path = strdup(p);
if (!m->path) {
- closedir(d);
free(m);
return -ENOMEM;
}
if (hashmap_put(j->directories_by_path, m->path, m) < 0) {
- closedir(d);
free(m->path);
free(m);
return -ENOMEM;
@@ -1448,10 +1478,8 @@ static int add_root_directory(sd_journal *j, const char *p) {
log_debug("Root directory %s got added.", m->path);
- } else if (!m->is_root) {
- closedir(d);
+ } else if (!m->is_root)
return 0;
- }
if (m->wd <= 0 && j->inotify_fd >= 0) {
@@ -1475,9 +1503,13 @@ static int add_root_directory(sd_journal *j, const char *p) {
if (dirent_is_file_with_suffix(de, ".journal") ||
dirent_is_file_with_suffix(de, ".journal~")) {
r = add_file(j, m->path, de->d_name);
- if (r < 0)
- log_debug("Failed to add file %s/%s: %s", m->path, de->d_name, strerror(-r));
-
+ if (r < 0) {
+ log_debug("Failed to add file %s/%s: %s",
+ m->path, de->d_name, strerror(-r));
+ r = set_put_error(j, r);
+ if (r < 0)
+ return r;
+ }
} else if ((de->d_type == DT_DIR || de->d_type == DT_LNK || de->d_type == DT_UNKNOWN) &&
sd_id128_from_string(de->d_name, &id) >= 0) {
@@ -1489,8 +1521,6 @@ static int add_root_directory(sd_journal *j, const char *p) {
check_network(j, dirfd(d));
- closedir(d);
-
return 0;
}
@@ -1518,7 +1548,7 @@ static int remove_directory(sd_journal *j, Directory *d) {
}
static int add_search_paths(sd_journal *j) {
-
+ int r;
const char search_paths[] =
"/run/log/journal\0"
"/var/log/journal\0";
@@ -1529,8 +1559,14 @@ static int add_search_paths(sd_journal *j) {
/* We ignore most errors here, since the idea is to only open
* what's actually accessible, and ignore the rest. */
- NULSTR_FOREACH(p, search_paths)
- add_root_directory(j, p);
+ NULSTR_FOREACH(p, search_paths) {
+ r = add_root_directory(j, p);
+ if (r < 0 && r != -ENOENT) {
+ r = set_put_error(j, r);
+ if (r < 0)
+ return r;
+ }
+ }
return 0;
}
@@ -1566,37 +1602,21 @@ static sd_journal *journal_new(int flags, const char *path) {
if (path) {
j->path = strdup(path);
- if (!j->path) {
- free(j);
- return NULL;
- }
+ if (!j->path)
+ goto fail;
}
j->files = hashmap_new(string_hash_func, string_compare_func);
- if (!j->files) {
- free(j->path);
- free(j);
- return NULL;
- }
-
j->directories_by_path = hashmap_new(string_hash_func, string_compare_func);
- if (!j->directories_by_path) {
- hashmap_free(j->files);
- free(j->path);
- free(j);
- return NULL;
- }
-
j->mmap = mmap_cache_new();
- if (!j->mmap) {
- hashmap_free(j->files);
- hashmap_free(j->directories_by_path);
- free(j->path);
- free(j);
- return NULL;
- }
+ if (!j->files || !j->directories_by_path || !j->mmap)
+ goto fail;
return j;
+
+fail:
+ sd_journal_close(j);
+ return NULL;
}
_public_ int sd_journal_open(sd_journal **ret, int flags) {
@@ -1635,7 +1655,7 @@ _public_ int sd_journal_open_directory(sd_journal **ret, const char *path, int f
if (!ret)
return -EINVAL;
- if (!path || !path_is_absolute(path))
+ if (!path)
return -EINVAL;
if (flags != 0)
@@ -1646,8 +1666,10 @@ _public_ int sd_journal_open_directory(sd_journal **ret, const char *path, int f
return -ENOMEM;
r = add_root_directory(j, path);
- if (r < 0)
+ if (r < 0) {
+ set_put_error(j, r);
goto fail;
+ }
*ret = j;
return 0;
@@ -1665,6 +1687,8 @@ _public_ void sd_journal_close(sd_journal *j) {
if (!j)
return;
+ sd_journal_flush_matches(j);
+
while ((f = hashmap_steal_first(j->files)))
journal_file_close(f);
@@ -1682,13 +1706,12 @@ _public_ void sd_journal_close(sd_journal *j) {
if (j->inotify_fd >= 0)
close_nointr_nofail(j->inotify_fd);
- sd_journal_flush_matches(j);
-
if (j->mmap)
mmap_cache_unref(j->mmap);
free(j->path);
free(j->unique_field);
+ set_free(j->errors);
free(j);
}
@@ -1866,7 +1889,7 @@ _public_ int sd_journal_get_data(sd_journal *j, const char *field, const void **
*data = o->data.payload;
*size = t;
- return 1;
+ return 0;
}
r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
@@ -1987,6 +2010,43 @@ _public_ int sd_journal_get_fd(sd_journal *j) {
return j->inotify_fd;
}
+_public_ int sd_journal_get_events(sd_journal *j) {
+ int fd;
+
+ if (!j)
+ return -EINVAL;
+
+ fd = sd_journal_get_fd(j);
+ if (fd < 0)
+ return fd;
+
+ return POLLIN;
+}
+
+_public_ int sd_journal_get_timeout(sd_journal *j, uint64_t *timeout_usec) {
+ int fd;
+
+ if (!j)
+ return -EINVAL;
+ if (!timeout_usec)
+ return -EINVAL;
+
+ fd = sd_journal_get_fd(j);
+ if (fd < 0)
+ return fd;
+
+ if (!j->on_network) {
+ *timeout_usec = (uint64_t) -1;
+ return 0;
+ }
+
+ /* If we are on the network we need to regularly check for
+ * changes manually */
+
+ *timeout_usec = j->last_process_usec + JOURNAL_FILES_RECHECK_USEC;
+ return 1;
+}
+
static void process_inotify_event(sd_journal *j, struct inotify_event *e) {
Directory *d;
int r;
@@ -2007,8 +2067,11 @@ static void process_inotify_event(sd_journal *j, struct inotify_event *e) {
if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
r = add_file(j, d->path, e->name);
- if (r < 0)
- log_debug("Failed to add file %s/%s: %s", d->path, e->name, strerror(-r));
+ if (r < 0) {
+ log_debug("Failed to add file %s/%s: %s",
+ d->path, e->name, strerror(-r));
+ set_put_error(j, r);
+ }
} else if (e->mask & (IN_DELETE|IN_MOVED_FROM|IN_UNMOUNT)) {
@@ -2066,6 +2129,8 @@ _public_ int sd_journal_process(sd_journal *j) {
if (!j)
return -EINVAL;
+ j->last_process_usec = now(CLOCK_MONOTONIC);
+
for (;;) {
struct inotify_event *e;
ssize_t l;
@@ -2099,6 +2164,7 @@ _public_ int sd_journal_process(sd_journal *j) {
_public_ int sd_journal_wait(sd_journal *j, uint64_t timeout_usec) {
int r;
+ uint64_t t;
assert(j);
@@ -2117,12 +2183,18 @@ _public_ int sd_journal_wait(sd_journal *j, uint64_t timeout_usec) {
return determine_change(j);
}
- if (j->on_network) {
- /* If we are on the network we need to regularly check
- * for changes manually */
+ r = sd_journal_get_timeout(j, &t);
+ if (r < 0)
+ return r;
+
+ if (t != (uint64_t) -1) {
+ usec_t n;
+
+ n = now(CLOCK_MONOTONIC);
+ t = t > n ? t - n : 0;
- if (timeout_usec == (uint64_t) -1 || timeout_usec > JOURNAL_FILES_RECHECK_USEC)
- timeout_usec = JOURNAL_FILES_RECHECK_USEC;
+ if (timeout_usec == (uint64_t) -1 || timeout_usec > t)
+ timeout_usec = t;
}
do {
@@ -2442,7 +2514,7 @@ _public_ int sd_journal_get_catalog(sd_journal *j, char **ret) {
if (r < 0)
return r;
- r = catalog_get(id, &text);
+ r = catalog_get(CATALOG_DATABASE, id, &text);
if (r < 0)
return r;
@@ -2458,7 +2530,7 @@ _public_ int sd_journal_get_catalog_for_message_id(sd_id128_t id, char **ret) {
if (!ret)
return -EINVAL;
- return catalog_get(id, ret);
+ return catalog_get(CATALOG_DATABASE, id, ret);
}
_public_ int sd_journal_set_data_threshold(sd_journal *j, size_t sz) {
diff --git a/src/journal/test-catalog.c b/src/journal/test-catalog.c
index cec8a11c43..987867f0c8 100644
--- a/src/journal/test-catalog.c
+++ b/src/journal/test-catalog.c
@@ -4,6 +4,7 @@
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
@@ -20,29 +21,114 @@
***/
#include <locale.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
#include "util.h"
#include "log.h"
-#include "catalog.h"
+#include "macro.h"
#include "sd-messages.h"
+#include "catalog.h"
-int main(int argc, char *argv[]) {
+static void test_import(Hashmap *h, struct strbuf *sb,
+ const char* contents, ssize_t size, int code) {
+ int r;
+ char name[] = "/tmp/test-catalog.XXXXXX";
+ _cleanup_close_ int fd = mkstemp(name);
+ assert(fd >= 0);
+ assert_se(write(fd, contents, size) == size);
+
+ r = catalog_import_file(h, sb, name);
+ assert(r == code);
+
+ unlink(name);
+}
+
+static void test_catalog_importing(void) {
+ Hashmap *h;
+ struct strbuf *sb;
+
+ assert_se(h = hashmap_new(catalog_hash_func, catalog_compare_func));
+ assert_se(sb = strbuf_new());
+
+#define BUF "xxx"
+ test_import(h, sb, BUF, sizeof(BUF), -EINVAL);
+#undef BUF
+ assert(hashmap_isempty(h));
+ log_debug("----------------------------------------");
+
+#define BUF \
+"-- 0027229ca0644181a76c4e92458afaff dededededededededededededededede\n" \
+"Subject: message\n" \
+"\n" \
+"payload\n"
+ test_import(h, sb, BUF, sizeof(BUF), -EINVAL);
+#undef BUF
+
+ log_debug("----------------------------------------");
+
+#define BUF \
+"-- 0027229ca0644181a76c4e92458afaff dededededededededededededededed\n" \
+"Subject: message\n" \
+"\n" \
+"payload\n"
+ test_import(h, sb, BUF, sizeof(BUF), 0);
+#undef BUF
+
+ assert(hashmap_size(h) == 1);
+ log_debug("----------------------------------------");
+
+ hashmap_free_free(h);
+ strbuf_cleanup(sb);
+}
+
+
+static const char* database = NULL;
+
+static void test_catalog_update(void) {
+ int r;
+
+ static char name[] = "/tmp/test-catalog.XXXXXX";
+ r = mkstemp(name);
+ assert(r >= 0);
+
+ database = name;
+
+ /* Test what happens if there are no files. */
+ r = catalog_update(database, NULL, NULL);
+ assert(r >= 0);
+
+ /* Note: this might actually not find anything, if systemd was
+ * not installed before. That should be fine too. */
+ r = catalog_update(database, NULL, catalog_file_dirs);
+ assert(r >= 0);
+}
+
+int main(int argc, char *argv[]) {
_cleanup_free_ char *text = NULL;
+ int r;
setlocale(LC_ALL, "de_DE.UTF-8");
log_set_max_level(LOG_DEBUG);
- assert_se(catalog_update() >= 0);
+ test_catalog_importing();
+
+ test_catalog_update();
- assert_se(catalog_list(stdout) >= 0);
+ r = catalog_list(stdout, database, true);
+ assert_se(r >= 0);
- assert_se(catalog_get(SD_MESSAGE_COREDUMP, &text) >= 0);
+ r = catalog_list(stdout, database, false);
+ assert_se(r >= 0);
+ assert_se(catalog_get(database, SD_MESSAGE_COREDUMP, &text) >= 0);
printf(">>>%s<<<\n", text);
- fflush(stdout);
+ if (database)
+ unlink(database);
return 0;
}
diff --git a/src/journal/test-journal-enum.c b/src/journal/test-journal-enum.c
index 8a843ecdda..980244e016 100644
--- a/src/journal/test-journal-enum.c
+++ b/src/journal/test-journal-enum.c
@@ -23,10 +23,13 @@
#include "log.h"
#include "sd-journal.h"
+#include "macro.h"
+#include "util.h"
+#include "journal-internal.h"
int main(int argc, char *argv[]) {
unsigned n = 0;
- sd_journal *j;
+ _cleanup_journal_close_ sd_journal*j = NULL;
log_set_max_level(LOG_DEBUG);
@@ -48,6 +51,5 @@ int main(int argc, char *argv[]) {
break;
}
- sd_journal_close(j);
return 0;
}
diff --git a/src/journal/test-journal-match.c b/src/journal/test-journal-match.c
index fa228144f5..37bffc1883 100644
--- a/src/journal/test-journal-match.c
+++ b/src/journal/test-journal-match.c
@@ -28,8 +28,8 @@
#include "log.h"
int main(int argc, char *argv[]) {
- sd_journal *j;
- char *t;
+ _cleanup_journal_close_ sd_journal*j;
+ _cleanup_free_ char *t;
log_set_max_level(LOG_DEBUG);
@@ -54,14 +54,23 @@ int main(int argc, char *argv[]) {
assert_se(sd_journal_add_match(j, "ONE=two", 0) >= 0);
assert_se(sd_journal_add_match(j, "TWO=two", 0) >= 0);
- assert_se(t = journal_make_match_string(j));
+ assert_se(sd_journal_add_conjunction(j) >= 0);
+
+ assert_se(sd_journal_add_match(j, "L4_1=yes", 0) >= 0);
+ assert_se(sd_journal_add_match(j, "L4_1=ok", 0) >= 0);
+ assert_se(sd_journal_add_match(j, "L4_2=yes", 0) >= 0);
+ assert_se(sd_journal_add_match(j, "L4_2=ok", 0) >= 0);
+
+ assert_se(sd_journal_add_disjunction(j) >= 0);
+
+ assert_se(sd_journal_add_match(j, "L3=yes", 0) >= 0);
+ assert_se(sd_journal_add_match(j, "L3=ok", 0) >= 0);
- assert_se(streq(t, "((TWO=two AND (ONE=two OR ONE=one)) OR (PIFF=paff AND (QUUX=yyyyy OR QUUX=xxxxx OR QUUX=mmmm) AND (HALLO= OR HALLO=WALDO)))"));
+ assert_se(t = journal_make_match_string(j));
printf("resulting match expression is: %s\n", t);
- free(t);
- sd_journal_close(j);
+ assert_se(streq(t, "(((L3=ok OR L3=yes) OR ((L4_2=ok OR L4_2=yes) AND (L4_1=ok OR L4_1=yes))) AND ((TWO=two AND (ONE=two OR ONE=one)) OR (PIFF=paff AND (QUUX=yyyyy OR QUUX=xxxxx OR QUUX=mmmm) AND (HALLO= OR HALLO=WALDO))))"));
return 0;
}
diff --git a/src/journal/test-journal-stream.c b/src/journal/test-journal-stream.c
index b3e816db70..4aba7febc7 100644
--- a/src/journal/test-journal-stream.c
+++ b/src/journal/test-journal-stream.c
@@ -75,7 +75,7 @@ int main(int argc, char *argv[]) {
JournalFile *one, *two, *three;
char t[] = "/tmp/journal-stream-XXXXXX";
unsigned i;
- sd_journal *j;
+ _cleanup_journal_close_ sd_journal*j = NULL;
char *z;
const void *data;
size_t l;
@@ -126,25 +126,23 @@ int main(int argc, char *argv[]) {
assert_se(sd_journal_add_match(j, "MAGIC=quux", 0) >= 0);
SD_JOURNAL_FOREACH_BACKWARDS(j) {
- char *c;
+ _cleanup_free_ char *c;
assert_se(sd_journal_get_data(j, "NUMBER", &data, &l) >= 0);
printf("\t%.*s\n", (int) l, (const char*) data);
assert_se(sd_journal_get_cursor(j, &c) >= 0);
assert_se(sd_journal_test_cursor(j, c) > 0);
- free(c);
}
SD_JOURNAL_FOREACH(j) {
- char *c;
+ _cleanup_free_ char *c;
assert_se(sd_journal_get_data(j, "NUMBER", &data, &l) >= 0);
printf("\t%.*s\n", (int) l, (const char*) data);
assert_se(sd_journal_get_cursor(j, &c) >= 0);
assert_se(sd_journal_test_cursor(j, c) > 0);
- free(c);
}
sd_journal_flush_matches(j);
@@ -177,8 +175,6 @@ int main(int argc, char *argv[]) {
SD_JOURNAL_FOREACH_UNIQUE(j, data, l)
printf("%.*s\n", (int) l, (const char*) data);
- sd_journal_close(j);
-
assert_se(rm_rf_dangerous(t, false, true, false) >= 0);
return 0;
diff --git a/src/journal/test-journal-syslog.c b/src/journal/test-journal-syslog.c
index 3ae8633f22..b9419372ad 100644
--- a/src/journal/test-journal-syslog.c
+++ b/src/journal/test-journal-syslog.c
@@ -25,14 +25,14 @@
static void test_syslog_parse_identifier(const char* str,
const char *ident, const char*pid, int ret) {
const char *buf = str;
- char *ident2 = NULL, *pid2 = NULL;
+ _cleanup_free_ char *ident2 = NULL, *pid2 = NULL;
int ret2;
ret2 = syslog_parse_identifier(&buf, &ident2, &pid2);
assert(ret == ret2);
- assert(ident==ident2 || !strcmp(ident, ident2));
- assert(pid==pid2 || !strcmp(pid, pid2));
+ assert(ident==ident2 || streq(ident, ident2));
+ assert(pid==pid2 || streq(pid, pid2));
}
int main(void) {
diff --git a/src/journal/test-journal-verify.c b/src/journal/test-journal-verify.c
index b6677215c0..ad2e2d4c3b 100644
--- a/src/journal/test-journal-verify.c
+++ b/src/journal/test-journal-verify.c
@@ -117,7 +117,7 @@ int main(int argc, char *argv[]) {
log_info("=> Validated from %s to %s, %s missing",
format_timestamp(a, sizeof(a), from),
format_timestamp(b, sizeof(b), to),
- format_timespan(c, sizeof(c), total > to ? total - to : 0));
+ format_timespan(c, sizeof(c), total > to ? total - to : 0, 0));
}
journal_file_close(f);
diff --git a/src/kernel-install/50-depmod.install b/src/kernel-install/50-depmod.install
new file mode 100644
index 0000000000..68c24bed7a
--- /dev/null
+++ b/src/kernel-install/50-depmod.install
@@ -0,0 +1,8 @@
+#!/bin/bash
+# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
+# ex: ts=8 sw=4 sts=4 et filetype=sh
+
+[[ $1 == "add" ]] || exit 0
+[[ $2 ]] || exit 1
+
+exec depmod -a "$2"
diff --git a/src/kernel-install/90-loaderentry.install b/src/kernel-install/90-loaderentry.install
new file mode 100644
index 0000000000..55b4d24672
--- /dev/null
+++ b/src/kernel-install/90-loaderentry.install
@@ -0,0 +1,77 @@
+#!/bin/bash
+# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
+# ex: ts=8 sw=4 sts=4 et filetype=sh
+
+COMMAND="$1"
+KERNEL_VERSION="$2"
+BOOT_DIR_ABS="$3"
+KERNEL_IMAGE="$4"
+
+if [[ -f /etc/machine-id ]]; then
+ read MACHINE_ID < /etc/machine-id
+fi
+
+if ! [[ $MACHINE_ID ]]; then
+ exit 1
+fi
+
+BOOT_DIR="/$MACHINE_ID/$KERNEL_VERSION"
+LOADER_ENTRY="/boot/loader/entries/$MACHINE_ID-$KERNEL_VERSION.conf"
+
+if [[ $COMMAND == remove ]]; then
+ exec rm -f "$LOADER_ENTRY"
+fi
+
+if ! [[ $COMMAND == add ]]; then
+ exit 1
+fi
+
+if ! [[ $KERNEL_IMAGE ]]; then
+ exit 1
+fi
+
+if [[ -f /etc/os-release ]]; then
+ . /etc/os-release
+fi
+
+if ! [[ $PRETTY_NAME ]]; then
+ PRETTY_NAME="Linux $KERNEL_VERSION"
+fi
+
+if [[ -f /etc/kernel/cmdline ]]; then
+ readarray -t BOOT_OPTIONS < /etc/kernel/cmdline
+fi
+
+if ! [[ ${BOOT_OPTIONS[*]} ]]; then
+ readarray -t BOOT_OPTIONS < /proc/cmdline
+fi
+
+if ! [[ $BOOT_OPTIONS ]]; then
+ echo "Could not determine the kernel command line parameters." >&2
+ echo "Please specify the kernel command line in /etc/kernel/cmdline!" >&2
+ exit 1
+fi
+
+cp --preserve "$KERNEL_IMAGE" "$BOOT_DIR_ABS/linux" || {
+ echo "Could not copy '$KERNEL_IMAGE to '$BOOT_DIR_ABS/linux'." >&2
+ exit 1
+}
+
+mkdir -p "${LOADER_ENTRY%/*}" || {
+ echo "Could not create loader entry directory '${LOADER_ENTRY%/*}'." >&2
+ exit 1
+}
+
+{
+ echo "title $PRETTY_NAME"
+ echo "version $KERNEL_VERSION"
+ echo "machine-id $MACHINE_ID"
+ echo "options ${BOOT_OPTIONS[*]}"
+ echo "linux $BOOT_DIR/linux"
+ [[ -f $BOOT_DIR_ABS/initrd ]] && \
+ echo "initrd $BOOT_DIR/initrd"
+} > "$LOADER_ENTRY" || {
+ echo "Could not create loader entry '$LOADER_ENTRY'." >&2
+ exit 1
+}
+exit 0
diff --git a/src/kernel-install/kernel-install b/src/kernel-install/kernel-install
new file mode 100644
index 0000000000..fb2ee57b5b
--- /dev/null
+++ b/src/kernel-install/kernel-install
@@ -0,0 +1,123 @@
+#!/bin/bash
+# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
+# ex: ts=8 sw=4 sts=4 et filetype=sh
+#
+# This file is part of systemd.
+#
+# Copyright 2013 Harald Hoyer
+#
+# 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 <http://www.gnu.org/licenses/>.
+
+usage()
+{
+ echo "Usage:" >&2
+ echo " $0 add <kernel-version> <kernel-image>" >&2
+ echo " $0 remove <kernel-version> <kernel-image>" >&2
+}
+
+dropindirs_sort()
+{
+ local suffix=$1; shift
+ local -a files
+ local f d i
+
+ readarray -t files < <(
+ for d in "$@"; do
+ for i in "$d/"*"$suffix"; do
+ if [[ -e "$i" ]]; then
+ echo "${i##*/}"
+ fi
+ done
+ done | sort -Vu
+ )
+
+ for f in "${files[@]}"; do
+ for d in "$@"; do
+ if [[ -e "$d/$f" ]]; then
+ echo "$d/$f"
+ continue 2
+ fi
+ done
+ done
+}
+
+export LC_COLLATE=C
+
+COMMAND="$1"
+KERNEL_VERSION="$2"
+KERNEL_IMAGE="$3"
+
+if [[ -f /etc/machine-id ]]; then
+ read MACHINE_ID < /etc/machine-id
+fi
+
+if ! [[ $MACHINE_ID ]]; then
+ echo "Could not determine your machine ID from /etc/machine-id." >&2
+ echo "Please run 'systemd-machine-id-setup' as root. See man:machine-id(5)" >&2
+ exit 1
+fi
+
+if [[ ! $COMMAND ]] || [[ ! $KERNEL_VERSION ]]; then
+ usage
+ exit 1
+fi
+
+BOOT_DIR_ABS="/boot/$MACHINE_ID/$KERNEL_VERSION"
+ret=0
+
+readarray -t PLUGINS < <(
+ dropindirs_sort ".install" \
+ "/etc/kernel/install.d" \
+ "/usr/lib/kernel/install.d"
+)
+
+case $COMMAND in
+ add)
+ if [[ ! $KERNEL_IMAGE ]]; then
+ usage
+ exit 1
+ fi
+
+ mkdir -p "$BOOT_DIR_ABS" || {
+ echo "Could not create boot directory '$BOOT_DIR_ABS'." >&2
+ exit 1
+ }
+
+ for f in "${PLUGINS[@]}"; do
+ if [[ -x $f ]]; then
+ "$f" add "$KERNEL_VERSION" "$BOOT_DIR_ABS" "$KERNEL_IMAGE"
+ ((ret+=$?))
+ fi
+ done
+ ;;
+
+ remove)
+ for f in "${PLUGINS[@]}"; do
+ if [[ -x $f ]]; then
+ "$f" remove "$KERNEL_VERSION" "$BOOT_DIR_ABS"
+ ((ret+=$?))
+ fi
+ done
+
+ rm -rf "$BOOT_DIR_ABS"
+ ((ret+=$?))
+ ;;
+
+ *)
+ usage
+ exit 1
+ ;;
+esac
+
+exit $ret
diff --git a/src/libsystemd-bus/Makefile b/src/libsystemd-bus/Makefile
new file mode 120000
index 0000000000..d0b0e8e008
--- /dev/null
+++ b/src/libsystemd-bus/Makefile
@@ -0,0 +1 @@
+../Makefile \ No newline at end of file
diff --git a/src/libsystemd-bus/bus-bloom.c b/src/libsystemd-bus/bus-bloom.c
new file mode 100644
index 0000000000..cb65e47b4c
--- /dev/null
+++ b/src/libsystemd-bus/bus-bloom.c
@@ -0,0 +1,93 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ 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 <http://www.gnu.org/licenses/>.
+***/
+
+#include "util.h"
+#include "MurmurHash3.h"
+
+#include "bus-bloom.h"
+
+static inline void set_bit(uint64_t filter[], unsigned b) {
+ filter[b >> 6] |= 1ULL << (b & 63);
+}
+
+void bloom_add_data(uint64_t filter[BLOOM_SIZE/8], const void *data, size_t n) {
+ uint16_t hash[8];
+ unsigned k = 0;
+
+ /*
+ * Our bloom filter has the following parameters:
+ *
+ * m=512 (bits in the filter)
+ * k=8 (hash functions)
+ *
+ * We calculate a single 128bit MurmurHash value of which we
+ * use 8 parts of 9 bits as individual hash functions.
+ *
+ */
+
+ MurmurHash3_x64_128(data, n, 0, hash);
+
+ assert_cc(BLOOM_SIZE*8 == 512);
+
+ for (k = 0; k < ELEMENTSOF(hash); k++)
+ set_bit(filter, hash[k] & 511);
+}
+
+void bloom_add_pair(uint64_t filter[BLOOM_SIZE/8], const char *a, const char *b) {
+ size_t n;
+ char *c;
+
+ assert(filter);
+ assert(a);
+ assert(b);
+
+ n = strlen(a) + 1 + strlen(b);
+ c = alloca(n + 1);
+ strcpy(stpcpy(stpcpy(c, a), ":"), b);
+
+ bloom_add_data(filter, c, n);
+}
+
+void bloom_add_prefixes(uint64_t filter[BLOOM_SIZE/8], const char *a, const char *b, char sep) {
+ size_t n;
+ char *c, *p;
+
+ assert(filter);
+ assert(a);
+ assert(b);
+
+ n = strlen(a) + 1 + strlen(b);
+ c = alloca(n + 1);
+
+ p = stpcpy(stpcpy(c, a), ":");
+ strcpy(p, b);
+
+ for (;;) {
+ char *e;
+
+ e = strrchr(p, sep);
+ if (!e || e == p)
+ break;
+
+ *e = 0;
+ bloom_add_data(filter, c, e - c);
+ }
+}
diff --git a/src/libsystemd-bus/bus-bloom.h b/src/libsystemd-bus/bus-bloom.h
new file mode 100644
index 0000000000..1bf14a3331
--- /dev/null
+++ b/src/libsystemd-bus/bus-bloom.h
@@ -0,0 +1,30 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ 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 <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/types.h>
+
+#define BLOOM_SIZE 64
+
+void bloom_add_data(uint64_t filter[BLOOM_SIZE/8], const void *data, size_t n);
+void bloom_add_pair(uint64_t filter[BLOOM_SIZE/8], const char *a, const char *b);
+void bloom_add_prefixes(uint64_t filter[BLOOM_SIZE/8], const char *a, const char *b, char sep);
diff --git a/src/libsystemd-bus/bus-control.c b/src/libsystemd-bus/bus-control.c
new file mode 100644
index 0000000000..a4dc9bf511
--- /dev/null
+++ b/src/libsystemd-bus/bus-control.c
@@ -0,0 +1,378 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ 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 <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_VALGRIND_MEMCHECK_H
+#include <valgrind/memcheck.h>
+#endif
+
+#include <stddef.h>
+#include <errno.h>
+
+#include "strv.h"
+
+#include "sd-bus.h"
+#include "bus-internal.h"
+#include "bus-message.h"
+#include "bus-control.h"
+
+int sd_bus_get_unique_name(sd_bus *bus, const char **unique) {
+ int r;
+
+ if (!bus)
+ return -EINVAL;
+ if (!unique)
+ return -EINVAL;
+
+ r = bus_ensure_running(bus);
+ if (r < 0)
+ return r;
+
+ *unique = bus->unique_name;
+ return 0;
+}
+
+int sd_bus_request_name(sd_bus *bus, const char *name, int flags) {
+ _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+ uint32_t ret;
+ int r;
+
+ if (!bus)
+ return -EINVAL;
+ if (!name)
+ return -EINVAL;
+ if (!bus->bus_client)
+ return -EINVAL;
+
+ if (bus->is_kernel) {
+ struct kdbus_cmd_name *n;
+ size_t l;
+
+ l = strlen(name);
+ n = alloca0(offsetof(struct kdbus_cmd_name, name) + l + 1);
+ n->size = offsetof(struct kdbus_cmd_name, name) + l + 1;
+ n->name_flags = flags;
+ memcpy(n->name, name, l+1);
+
+#ifdef HAVE_VALGRIND_MEMCHECK_H
+ VALGRIND_MAKE_MEM_DEFINED(n, n->size);
+#endif
+
+ r = ioctl(bus->input_fd, KDBUS_CMD_NAME_ACQUIRE, n);
+ if (r < 0)
+ return -errno;
+
+ return n->name_flags;
+ } else {
+ r = sd_bus_call_method(
+ bus,
+ "org.freedesktop.DBus",
+ "/",
+ "org.freedesktop.DBus",
+ "RequestName",
+ NULL,
+ &reply,
+ "su",
+ name,
+ flags);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_read(reply, "u", &ret);
+ if (r < 0)
+ return r;
+
+ return ret;
+ }
+}
+
+int sd_bus_release_name(sd_bus *bus, const char *name) {
+ _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+ uint32_t ret;
+ int r;
+
+ if (!bus)
+ return -EINVAL;
+ if (!name)
+ return -EINVAL;
+ if (!bus->bus_client)
+ return -EINVAL;
+
+ if (bus->is_kernel) {
+ struct kdbus_cmd_name *n;
+ size_t l;
+
+ l = strlen(name);
+ n = alloca0(offsetof(struct kdbus_cmd_name, name) + l + 1);
+ n->size = offsetof(struct kdbus_cmd_name, name) + l + 1;
+ memcpy(n->name, name, l+1);
+
+#ifdef HAVE_VALGRIND_MEMCHECK_H
+ VALGRIND_MAKE_MEM_DEFINED(n, n->size);
+#endif
+ r = ioctl(bus->input_fd, KDBUS_CMD_NAME_RELEASE, n);
+ if (r < 0)
+ return -errno;
+
+ return n->name_flags;
+ } else {
+ r = sd_bus_call_method(
+ bus,
+ "org.freedesktop.DBus",
+ "/",
+ "org.freedesktop.DBus",
+ "ReleaseName",
+ NULL,
+ &reply,
+ "s",
+ name);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_read(reply, "u", &ret);
+ if (r < 0)
+ return r;
+ }
+
+ return ret;
+}
+
+int sd_bus_list_names(sd_bus *bus, char ***l) {
+ _cleanup_bus_message_unref_ sd_bus_message *reply1 = NULL, *reply2 = NULL;
+ char **x = NULL;
+ int r;
+
+ if (!bus)
+ return -EINVAL;
+ if (!l)
+ return -EINVAL;
+
+ r = sd_bus_call_method(
+ bus,
+ "org.freedesktop.DBus",
+ "/",
+ "org.freedesktop.DBus",
+ "ListNames",
+ NULL,
+ &reply1,
+ NULL);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_call_method(
+ bus,
+ "org.freedesktop.DBus",
+ "/",
+ "org.freedesktop.DBus",
+ "ListActivatableNames",
+ NULL,
+ &reply2,
+ NULL);
+ if (r < 0)
+ return r;
+
+ r = bus_message_read_strv_extend(reply1, &x);
+ if (r < 0) {
+ strv_free(x);
+ return r;
+ }
+
+ r = bus_message_read_strv_extend(reply2, &x);
+ if (r < 0) {
+ strv_free(x);
+ return r;
+ }
+
+ *l = strv_uniq(x);
+ return 0;
+}
+
+int sd_bus_get_owner(sd_bus *bus, const char *name, char **owner) {
+ _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+ const char *found;
+ int r;
+
+ if (!bus)
+ return -EINVAL;
+ if (!name)
+ return -EINVAL;
+
+ r = sd_bus_call_method(
+ bus,
+ "org.freedesktop.DBus",
+ "/",
+ "org.freedesktop.DBus",
+ "GetNameOwner",
+ NULL,
+ &reply,
+ "s",
+ name);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_read(reply, "s", &found);
+ if (r < 0)
+ return r;
+
+ if (owner) {
+ char *t;
+
+ t = strdup(found);
+ if (!t)
+ return -ENOMEM;
+
+ *owner = t;
+ }
+
+ return 0;
+}
+
+int sd_bus_get_owner_uid(sd_bus *bus, const char *name, uid_t *uid) {
+ _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+ uint32_t u;
+ int r;
+
+ if (!bus)
+ return -EINVAL;
+ if (!name)
+ return -EINVAL;
+ if (!uid)
+ return -EINVAL;
+
+ r = sd_bus_call_method(
+ bus,
+ "org.freedesktop.DBus",
+ "/",
+ "org.freedesktop.DBus",
+ "GetConnectionUnixUser",
+ NULL,
+ &reply,
+ "s",
+ name);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_read(reply, "u", &u);
+ if (r < 0)
+ return r;
+
+ *uid = (uid_t) u;
+ return 0;
+}
+
+int sd_bus_get_owner_pid(sd_bus *bus, const char *name, pid_t *pid) {
+ _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+ uint32_t u;
+ int r;
+
+ if (!bus)
+ return -EINVAL;
+ if (!name)
+ return -EINVAL;
+ if (!pid)
+ return -EINVAL;
+
+ r = sd_bus_call_method(
+ bus,
+ "org.freedesktop.DBus",
+ "/",
+ "org.freedesktop.DBus",
+ "GetConnectionUnixProcessID",
+ NULL,
+ &reply,
+ "s",
+ name);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_read(reply, "u", &u);
+ if (r < 0)
+ return r;
+
+ if (u == 0)
+ return -EIO;
+
+ *pid = (uid_t) u;
+ return 0;
+}
+
+int bus_add_match_internal(sd_bus *bus, const char *match) {
+ assert(bus);
+ assert(match);
+
+ return sd_bus_call_method(
+ bus,
+ "org.freedesktop.DBus",
+ "/",
+ "org.freedesktop.DBus",
+ "AddMatch",
+ NULL,
+ NULL,
+ "s",
+ match);
+}
+
+int bus_remove_match_internal(sd_bus *bus, const char *match) {
+ assert(bus);
+ assert(match);
+
+ return sd_bus_call_method(
+ bus,
+ "org.freedesktop.DBus",
+ "/",
+ "org.freedesktop.DBus",
+ "RemoveMatch",
+ NULL,
+ NULL,
+ "s",
+ match);
+}
+
+int sd_bus_get_owner_machine_id(sd_bus *bus, const char *name, sd_id128_t *machine) {
+ _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+ const char *mid;
+ int r;
+
+ if (!bus)
+ return -EINVAL;
+ if (!name)
+ return -EINVAL;
+
+ if (streq_ptr(name, bus->unique_name))
+ return sd_id128_get_machine(machine);
+
+ r = sd_bus_call_method(bus,
+ name,
+ "/",
+ "org.freedesktop.DBus.Peer",
+ "GetMachineId",
+ NULL,
+ &reply,
+ NULL);
+
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_read(reply, "s", &mid);
+ if (r < 0)
+ return r;
+
+ return sd_id128_from_string(mid, machine);
+}
diff --git a/src/libsystemd-bus/bus-control.h b/src/libsystemd-bus/bus-control.h
new file mode 100644
index 0000000000..34ecb260c3
--- /dev/null
+++ b/src/libsystemd-bus/bus-control.h
@@ -0,0 +1,27 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ 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 <http://www.gnu.org/licenses/>.
+***/
+
+#include "sd-bus.h"
+
+int bus_add_match_internal(sd_bus *bus, const char *match);
+int bus_remove_match_internal(sd_bus *bus, const char *match);
diff --git a/src/libsystemd-bus/bus-error.c b/src/libsystemd-bus/bus-error.c
new file mode 100644
index 0000000000..5faa17384e
--- /dev/null
+++ b/src/libsystemd-bus/bus-error.c
@@ -0,0 +1,177 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ 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 <http://www.gnu.org/licenses/>.
+***/
+
+#include <errno.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "util.h"
+
+#include "sd-bus.h"
+#include "bus-error.h"
+
+bool bus_error_is_dirty(sd_bus_error *e) {
+ if (!e)
+ return 0;
+
+ return e->name || e->message || e->need_free;
+}
+
+void sd_bus_error_free(sd_bus_error *e) {
+ if (!e)
+ return;
+
+ if (e->need_free) {
+ free((void*) e->name);
+ free((void*) e->message);
+ }
+
+ e->name = e->message = NULL;
+ e->need_free = false;
+}
+
+int sd_bus_error_set(sd_bus_error *e, const char *name, const char *format, ...) {
+ char *n, *m = NULL;
+ va_list ap;
+ int r;
+
+ if (!e)
+ return 0;
+ if (bus_error_is_dirty(e))
+ return -EINVAL;
+ if (!name)
+ return -EINVAL;
+
+ n = strdup(name);
+ if (!n)
+ return -ENOMEM;
+
+ if (format) {
+ va_start(ap, format);
+ r = vasprintf(&m, format, ap);
+ va_end(ap);
+
+ if (r < 0) {
+ free(n);
+ return -ENOMEM;
+ }
+ }
+
+ e->name = n;
+ e->message = m;
+ e->need_free = true;
+
+ return 0;
+}
+
+int sd_bus_error_copy(sd_bus_error *dest, const sd_bus_error *e) {
+ char *x, *y = NULL;
+
+ if (!dest)
+ return 0;
+ if (bus_error_is_dirty(dest))
+ return -EINVAL;
+ if (!sd_bus_error_is_set(e))
+ return 0;
+
+ x = strdup(e->name);
+ if (!x)
+ return -ENOMEM;
+
+ if (e->message) {
+ y = strdup(e->message);
+ if (!y) {
+ free(x);
+ return -ENOMEM;
+ }
+ }
+
+ dest->name = x;
+ dest->message = y;
+ dest->need_free = true;
+ return 0;
+}
+
+void sd_bus_error_set_const(sd_bus_error *e, const char *name, const char *message) {
+ if (!e)
+ return;
+ if (bus_error_is_dirty(e))
+ return;
+
+ e->name = name;
+ e->message = message;
+ e->need_free = false;
+}
+
+int sd_bus_error_is_set(const sd_bus_error *e) {
+ if (!e)
+ return 0;
+
+ return !!e->name;
+}
+
+int sd_bus_error_has_name(const sd_bus_error *e, const char *name) {
+ if (!e)
+ return 0;
+
+ return streq_ptr(e->name, name);
+}
+
+int bus_error_to_errno(const sd_bus_error* e) {
+
+ /* Better replce this with a gperf table */
+
+ if (!e->name)
+ return -EIO;
+
+ if (streq(e->name, "org.freedesktop.DBus.Error.NoMemory"))
+ return -ENOMEM;
+
+ if (streq(e->name, "org.freedesktop.DBus.Error.AuthFailed") ||
+ streq(e->name, "org.freedesktop.DBus.Error.AccessDenied"))
+ return -EPERM;
+
+ return -EIO;
+}
+
+int bus_error_from_errno(sd_bus_error *e, int error) {
+ if (!e)
+ return error;
+
+ if (error == -ENOMEM)
+ sd_bus_error_set_const(e, "org.freedesktop.DBus.Error.NoMemory", strerror(-error));
+ else if (error == -EPERM || error == -EACCES)
+ sd_bus_error_set_const(e, "org.freedesktop.DBus.Error.AccessDenied", strerror(-error));
+ else
+ sd_bus_error_set_const(e, "org.freedesktop.DBus.Error.Failed", "Operation failed");
+
+ return error;
+}
+
+const char *bus_error_message(const sd_bus_error *e, int error) {
+ if (e && e->message)
+ return e->message;
+
+ return strerror(error);
+}
diff --git a/src/libsystemd-bus/bus-error.h b/src/libsystemd-bus/bus-error.h
new file mode 100644
index 0000000000..ceca7d678f
--- /dev/null
+++ b/src/libsystemd-bus/bus-error.h
@@ -0,0 +1,31 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ 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 <http://www.gnu.org/licenses/>.
+***/
+
+#include "sd-bus.h"
+
+int bus_error_to_errno(const sd_bus_error *e);
+int bus_error_from_errno(sd_bus_error *e, int error);
+
+bool bus_error_is_dirty(sd_bus_error *e);
+
+const char *bus_error_message(const sd_bus_error *e, int error);
diff --git a/src/libsystemd-bus/bus-internal.c b/src/libsystemd-bus/bus-internal.c
new file mode 100644
index 0000000000..0e66f3d355
--- /dev/null
+++ b/src/libsystemd-bus/bus-internal.c
@@ -0,0 +1,256 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ 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 <http://www.gnu.org/licenses/>.
+***/
+
+#include "bus-internal.h"
+
+bool object_path_is_valid(const char *p) {
+ const char *q;
+ bool slash;
+
+ if (!p)
+ return false;
+
+ if (p[0] != '/')
+ return false;
+
+ if (p[1] == 0)
+ return true;
+
+ for (slash = true, q = p+1; *q; q++)
+ if (*q == '/') {
+ if (slash)
+ return false;
+
+ slash = true;
+ } else {
+ bool good;
+
+ good =
+ (*q >= 'a' && *q <= 'z') ||
+ (*q >= 'A' && *q <= 'Z') ||
+ (*q >= '0' && *q <= '9') ||
+ *q == '_';
+
+ if (!good)
+ return false;
+
+ slash = false;
+ }
+
+ if (slash)
+ return false;
+
+ return true;
+}
+
+bool interface_name_is_valid(const char *p) {
+ const char *q;
+ bool dot, found_dot;
+
+ if (isempty(p))
+ return false;
+
+ for (dot = true, q = p; *q; q++)
+ if (*q == '.') {
+ if (dot)
+ return false;
+
+ found_dot = dot = true;
+ } else {
+ bool good;
+
+ good =
+ (*q >= 'a' && *q <= 'z') ||
+ (*q >= 'A' && *q <= 'Z') ||
+ (!dot && *q >= '0' && *q <= '9') ||
+ *q == '_';
+
+ if (!good)
+ return false;
+
+ dot = false;
+ }
+
+ if (q - p > 255)
+ return false;
+
+ if (dot)
+ return false;
+
+ if (!found_dot)
+ return false;
+
+ return true;
+}
+
+bool service_name_is_valid(const char *p) {
+ const char *q;
+ bool dot, found_dot, unique;
+
+ if (isempty(p))
+ return false;
+
+ unique = p[0] == ':';
+
+ for (dot = true, q = unique ? p+1 : p; *q; q++)
+ if (*q == '.') {
+ if (dot)
+ return false;
+
+ found_dot = dot = true;
+ } else {
+ bool good;
+
+ good =
+ (*q >= 'a' && *q <= 'z') ||
+ (*q >= 'A' && *q <= 'Z') ||
+ ((!dot || unique) && *q >= '0' && *q <= '9') ||
+ *q == '_' || *q == '-';
+
+ if (!good)
+ return false;
+
+ dot = false;
+ }
+
+ if (q - p > 255)
+ return false;
+
+ if (dot)
+ return false;
+
+ if (!found_dot)
+ return false;
+
+ return true;
+}
+
+bool member_name_is_valid(const char *p) {
+ const char *q;
+
+ if (isempty(p))
+ return false;
+
+ for (q = p; *q; q++) {
+ bool good;
+
+ good =
+ (*q >= 'a' && *q <= 'z') ||
+ (*q >= 'A' && *q <= 'Z') ||
+ (*q >= '0' && *q <= '9') ||
+ *q == '_';
+
+ if (!good)
+ return false;
+ }
+
+ if (q - p > 255)
+ return false;
+
+ return true;
+}
+
+static bool complex_pattern_check(char c, const char *a, const char *b) {
+ bool separator = false;
+
+ if (!a && !b)
+ return true;
+
+ if (!a || !b)
+ return false;
+
+ for (;;) {
+ if (*a != *b)
+ return (separator && (*a == 0 || *b == 0)) ||
+ (*a == 0 && *b == c && b[1] == 0) ||
+ (*b == 0 && *a == c && a[1] == 0);
+
+ if (*a == 0)
+ return true;
+
+ separator = *a == c;
+
+ a++, b++;
+ }
+}
+
+bool namespace_complex_pattern(const char *pattern, const char *value) {
+ return complex_pattern_check('.', pattern, value);
+}
+
+bool path_complex_pattern(const char *pattern, const char *value) {
+ return complex_pattern_check('/', pattern, value);
+}
+
+static bool simple_pattern_check(char c, const char *a, const char *b) {
+
+ if (!a && !b)
+ return true;
+
+ if (!a || !b)
+ return false;
+
+ for (;;) {
+ if (*a != *b)
+ return *a == 0 && *b == c;
+
+ if (*a == 0)
+ return true;
+
+ a++, b++;
+ }
+}
+
+bool namespace_simple_pattern(const char *pattern, const char *value) {
+ return simple_pattern_check('.', pattern, value);
+}
+
+bool path_simple_pattern(const char *pattern, const char *value) {
+ return simple_pattern_check('/', pattern, value);
+}
+
+int bus_message_type_from_string(const char *s, uint8_t *u) {
+ if (streq(s, "signal"))
+ *u = SD_BUS_MESSAGE_TYPE_SIGNAL;
+ else if (streq(s, "method_call"))
+ *u = SD_BUS_MESSAGE_TYPE_METHOD_CALL;
+ else if (streq(s, "error"))
+ *u = SD_BUS_MESSAGE_TYPE_METHOD_ERROR;
+ else if (streq(s, "method_return"))
+ *u = SD_BUS_MESSAGE_TYPE_METHOD_RETURN;
+ else
+ return -EINVAL;
+
+ return 0;
+}
+
+const char *bus_message_type_to_string(uint8_t u) {
+ if (u == SD_BUS_MESSAGE_TYPE_SIGNAL)
+ return "signal";
+ else if (u == SD_BUS_MESSAGE_TYPE_METHOD_CALL)
+ return "method_call";
+ else if (u == SD_BUS_MESSAGE_TYPE_METHOD_ERROR)
+ return "error";
+ else if (u == SD_BUS_MESSAGE_TYPE_METHOD_RETURN)
+ return "method_return";
+ else
+ return NULL;
+}
diff --git a/src/libsystemd-bus/bus-internal.h b/src/libsystemd-bus/bus-internal.h
new file mode 100644
index 0000000000..4babfac86d
--- /dev/null
+++ b/src/libsystemd-bus/bus-internal.h
@@ -0,0 +1,198 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ 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 <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+
+#include "hashmap.h"
+#include "prioq.h"
+#include "list.h"
+#include "util.h"
+
+#include "sd-bus.h"
+#include "bus-error.h"
+#include "bus-match.h"
+
+struct reply_callback {
+ sd_bus_message_handler_t callback;
+ void *userdata;
+ usec_t timeout;
+ uint64_t serial;
+ unsigned prioq_idx;
+};
+
+struct filter_callback {
+ sd_bus_message_handler_t callback;
+ void *userdata;
+
+ unsigned last_iteration;
+
+ LIST_FIELDS(struct filter_callback, callbacks);
+};
+
+struct object_callback {
+ sd_bus_message_handler_t callback;
+ void *userdata;
+
+ char *path;
+ bool is_fallback;
+
+ unsigned last_iteration;
+};
+
+enum bus_state {
+ BUS_UNSET,
+ BUS_OPENING,
+ BUS_AUTHENTICATING,
+ BUS_HELLO,
+ BUS_RUNNING
+};
+
+enum bus_auth {
+ _BUS_AUTH_INVALID,
+ BUS_AUTH_EXTERNAL,
+ BUS_AUTH_ANONYMOUS
+};
+
+struct sd_bus {
+ unsigned n_ref;
+ enum bus_state state;
+ int input_fd, output_fd;
+ int message_version;
+
+ bool is_kernel:1;
+ bool negotiate_fds:1;
+ bool can_fds:1;
+ bool bus_client:1;
+ bool ucred_valid:1;
+ bool is_server:1;
+ bool anonymous_auth:1;
+ bool prefer_readv:1;
+ bool prefer_writev:1;
+ bool processing:1;
+ bool match_callbacks_modified:1;
+ bool filter_callbacks_modified:1;
+ bool object_callbacks_modified:1;
+
+ void *rbuffer;
+ size_t rbuffer_size;
+
+ sd_bus_message **rqueue;
+ unsigned rqueue_size;
+
+ sd_bus_message **wqueue;
+ unsigned wqueue_size;
+ size_t windex;
+
+ uint64_t serial;
+
+ char *unique_name;
+
+ struct bus_match_node match_callbacks;
+ Prioq *reply_callbacks_prioq;
+ Hashmap *reply_callbacks;
+ LIST_HEAD(struct filter_callback, filter_callbacks);
+ Hashmap *object_callbacks;
+
+ union {
+ struct sockaddr sa;
+ struct sockaddr_un un;
+ struct sockaddr_in in;
+ struct sockaddr_in6 in6;
+ } sockaddr;
+ socklen_t sockaddr_size;
+
+ char *kernel;
+
+ sd_id128_t server_id;
+
+ char *address;
+ unsigned address_index;
+
+ int last_connect_error;
+
+ enum bus_auth auth;
+ size_t auth_rbegin;
+ struct iovec auth_iovec[3];
+ unsigned auth_index;
+ char *auth_buffer;
+ usec_t auth_timeout;
+
+ struct ucred ucred;
+ char label[NAME_MAX];
+
+ int *fds;
+ unsigned n_fds;
+
+ char *exec_path;
+ char **exec_argv;
+
+ uint64_t hello_serial;
+ unsigned iteration_counter;
+};
+
+static inline void bus_unrefp(sd_bus **b) {
+ sd_bus_unref(*b);
+}
+
+#define _cleanup_bus_unref_ __attribute__((cleanup(bus_unrefp)))
+#define _cleanup_bus_error_free_ __attribute__((cleanup(sd_bus_error_free)))
+
+#define BUS_DEFAULT_TIMEOUT ((usec_t) (25 * USEC_PER_SEC))
+
+#define BUS_WQUEUE_MAX 128
+#define BUS_RQUEUE_MAX 128
+
+#define BUS_MESSAGE_SIZE_MAX (64*1024*1024)
+#define BUS_AUTH_SIZE_MAX (64*1024)
+
+#define BUS_CONTAINER_DEPTH 128
+
+/* Defined by the specification as maximum size of an array in
+ * bytes */
+#define BUS_ARRAY_MAX_SIZE 67108864
+
+#define BUS_FDS_MAX 1024
+
+#define BUS_EXEC_ARGV_MAX 256
+
+bool object_path_is_valid(const char *p);
+bool interface_name_is_valid(const char *p);
+bool service_name_is_valid(const char *p);
+bool member_name_is_valid(const char *p);
+
+bool namespace_complex_pattern(const char *pattern, const char *value);
+bool path_complex_pattern(const char *pattern, const char *value);
+
+bool namespace_simple_pattern(const char *pattern, const char *value);
+bool path_simple_pattern(const char *pattern, const char *value);
+
+int bus_message_type_from_string(const char *s, uint8_t *u);
+const char *bus_message_type_to_string(uint8_t u);
+
+#define error_name_is_valid interface_name_is_valid
+
+int bus_ensure_running(sd_bus *bus);
+int bus_start_running(sd_bus *bus);
+int bus_next_address(sd_bus *bus);
diff --git a/src/libsystemd-bus/bus-kernel.c b/src/libsystemd-bus/bus-kernel.c
new file mode 100644
index 0000000000..0762b7836f
--- /dev/null
+++ b/src/libsystemd-bus/bus-kernel.c
@@ -0,0 +1,618 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ 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 <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_VALGRIND_MEMCHECK_H
+#include <valgrind/memcheck.h>
+#endif
+
+#include <fcntl.h>
+#include <malloc.h>
+
+#include "util.h"
+
+#include "bus-internal.h"
+#include "bus-message.h"
+#include "bus-kernel.h"
+#include "bus-bloom.h"
+
+#define KDBUS_ITEM_NEXT(item) \
+ (typeof(item))(((uint8_t *)item) + ALIGN8((item)->size))
+
+#define KDBUS_ITEM_FOREACH(item, head) \
+ for (item = (head)->items; \
+ (uint8_t *)(item) < (uint8_t *)(head) + (head)->size; \
+ item = KDBUS_ITEM_NEXT(item))
+
+#define KDBUS_ITEM_HEADER_SIZE offsetof(struct kdbus_item, data)
+#define KDBUS_ITEM_SIZE(s) ALIGN8((s) + KDBUS_ITEM_HEADER_SIZE)
+
+static int parse_unique_name(const char *s, uint64_t *id) {
+ int r;
+
+ assert(s);
+ assert(id);
+
+ if (!startswith(s, ":1."))
+ return 0;
+
+ r = safe_atou64(s + 3, id);
+ if (r < 0)
+ return r;
+
+ return 1;
+}
+
+static void append_payload_vec(struct kdbus_item **d, const void *p, size_t sz) {
+ assert(d);
+ assert(p);
+ assert(sz > 0);
+
+ *d = ALIGN8_PTR(*d);
+
+ (*d)->size = offsetof(struct kdbus_item, vec) + sizeof(struct kdbus_vec);
+ (*d)->type = KDBUS_MSG_PAYLOAD_VEC;
+ (*d)->vec.address = (intptr_t) p;
+ (*d)->vec.size = sz;
+
+ *d = (struct kdbus_item *) ((uint8_t*) *d + (*d)->size);
+}
+
+static void append_destination(struct kdbus_item **d, const char *s, size_t length) {
+ assert(d);
+ assert(s);
+
+ *d = ALIGN8_PTR(*d);
+
+ (*d)->size = offsetof(struct kdbus_item, str) + length + 1;
+ (*d)->type = KDBUS_MSG_DST_NAME;
+ memcpy((*d)->str, s, length + 1);
+
+ *d = (struct kdbus_item *) ((uint8_t*) *d + (*d)->size);
+}
+
+static void* append_bloom(struct kdbus_item **d, size_t length) {
+ void *r;
+
+ assert(d);
+
+ *d = ALIGN8_PTR(*d);
+
+ (*d)->size = offsetof(struct kdbus_item, data) + length;
+ (*d)->type = KDBUS_MSG_BLOOM;
+ r = (*d)->data;
+
+ *d = (struct kdbus_item *) ((uint8_t*) *d + (*d)->size);
+
+ return r;
+}
+
+static void append_fds(struct kdbus_item **d, const int fds[], unsigned n_fds) {
+ assert(d);
+ assert(fds);
+ assert(n_fds > 0);
+
+ *d = ALIGN8_PTR(*d);
+ (*d)->size = offsetof(struct kdbus_item, fds) + sizeof(int) * n_fds;
+ (*d)->type = KDBUS_MSG_UNIX_FDS;
+ memcpy((*d)->fds, fds, sizeof(int) * n_fds);
+
+ *d = (struct kdbus_item *) ((uint8_t*) *d + (*d)->size);
+}
+
+static int bus_message_setup_bloom(sd_bus_message *m, void *bloom) {
+ unsigned i;
+ int r;
+
+ assert(m);
+ assert(bloom);
+
+ memset(bloom, 0, BLOOM_SIZE);
+
+ bloom_add_pair(bloom, "message-type", bus_message_type_to_string(m->header->type));
+
+ if (m->interface)
+ bloom_add_pair(bloom, "interface", m->interface);
+ if (m->member)
+ bloom_add_pair(bloom, "member", m->member);
+ if (m->path) {
+ bloom_add_pair(bloom, "path", m->path);
+ bloom_add_prefixes(bloom, "path-slash-prefix", m->path, '/');
+ }
+
+ r = sd_bus_message_rewind(m, true);
+ if (r < 0)
+ return r;
+
+ for (i = 0; i < 64; i++) {
+ char type;
+ const char *t;
+ char buf[sizeof("arg")-1 + 2 + sizeof("-slash-prefix")];
+ char *e;
+
+ r = sd_bus_message_peek_type(m, &type, NULL);
+ if (r < 0)
+ return r;
+
+ if (type != SD_BUS_TYPE_STRING &&
+ type != SD_BUS_TYPE_OBJECT_PATH &&
+ type != SD_BUS_TYPE_SIGNATURE)
+ break;
+
+ r = sd_bus_message_read_basic(m, type, &t);
+ if (r < 0)
+ return r;
+
+ e = stpcpy(buf, "arg");
+ if (i < 10)
+ *(e++) = '0' + i;
+ else {
+ *(e++) = '0' + (i / 10);
+ *(e++) = '0' + (i % 10);
+ }
+
+ *e = 0;
+ bloom_add_pair(bloom, buf, t);
+
+ strcpy(e, "-dot-prefix");
+ bloom_add_prefixes(bloom, buf, t, '.');
+ strcpy(e, "-slash-prefix");
+ bloom_add_prefixes(bloom, buf, t, '/');
+ }
+
+ return 0;
+}
+
+static int bus_message_setup_kmsg(sd_bus *b, sd_bus_message *m) {
+ struct kdbus_item *d;
+ bool well_known;
+ uint64_t unique;
+ size_t sz, dl;
+ int r;
+
+ assert(b);
+ assert(m);
+ assert(m->sealed);
+
+ if (m->kdbus)
+ return 0;
+
+ if (m->destination) {
+ r = parse_unique_name(m->destination, &unique);
+ if (r < 0)
+ return r;
+
+ well_known = r == 0;
+ } else
+ well_known = false;
+
+ sz = offsetof(struct kdbus_msg, items);
+
+ /* Add in fixed header, fields header and payload */
+ sz += 3 * ALIGN8(offsetof(struct kdbus_item, vec) + sizeof(struct kdbus_vec));
+
+ /* Add space for bloom filter */
+ sz += ALIGN8(offsetof(struct kdbus_item, data) + BLOOM_SIZE);
+
+ /* Add in well-known destination header */
+ if (well_known) {
+ dl = strlen(m->destination);
+ sz += ALIGN8(offsetof(struct kdbus_item, str) + dl + 1);
+ }
+
+ /* Add space for unix fds */
+ if (m->n_fds > 0)
+ sz += ALIGN8(offsetof(struct kdbus_item, fds) + sizeof(int)*m->n_fds);
+
+ m->kdbus = memalign(8, sz);
+ if (!m->kdbus)
+ return -ENOMEM;
+
+ memset(m->kdbus, 0, sz);
+
+ m->kdbus->flags =
+ ((m->header->flags & SD_BUS_MESSAGE_NO_REPLY_EXPECTED) ? 0 : KDBUS_MSG_FLAGS_EXPECT_REPLY) |
+ ((m->header->flags & SD_BUS_MESSAGE_NO_AUTO_START) ? KDBUS_MSG_FLAGS_NO_AUTO_START : 0);
+ m->kdbus->dst_id =
+ well_known ? 0 :
+ m->destination ? unique : KDBUS_DST_ID_BROADCAST;
+ m->kdbus->payload_type = KDBUS_PAYLOAD_DBUS1;
+ m->kdbus->cookie = m->header->serial;
+
+ m->kdbus->timeout_ns = m->timeout * NSEC_PER_USEC;
+
+ d = m->kdbus->items;
+
+ if (well_known)
+ append_destination(&d, m->destination, dl);
+
+ append_payload_vec(&d, m->header, sizeof(*m->header));
+
+ if (m->fields)
+ append_payload_vec(&d, m->fields, ALIGN8(m->header->fields_size));
+
+ if (m->body)
+ append_payload_vec(&d, m->body, m->header->body_size);
+
+ if (m->kdbus->dst_id == KDBUS_DST_ID_BROADCAST) {
+ void *p;
+
+ p = append_bloom(&d, BLOOM_SIZE);
+ r = bus_message_setup_bloom(m, p);
+ if (r < 0) {
+ free(m->kdbus);
+ m->kdbus = NULL;
+ return -r;
+ }
+ }
+
+ if (m->n_fds > 0)
+ append_fds(&d, m->fds, m->n_fds);
+
+ m->kdbus->size = (uint8_t*) d - (uint8_t*) m->kdbus;
+ assert(m->kdbus->size <= sz);
+
+ m->free_kdbus = true;
+
+ return 0;
+}
+
+int bus_kernel_take_fd(sd_bus *b) {
+ struct kdbus_cmd_hello hello = {
+ .conn_flags =
+ KDBUS_HELLO_ACCEPT_FD|
+ KDBUS_HELLO_ATTACH_COMM|
+ KDBUS_HELLO_ATTACH_EXE|
+ KDBUS_HELLO_ATTACH_CMDLINE|
+ KDBUS_HELLO_ATTACH_CGROUP|
+ KDBUS_HELLO_ATTACH_CAPS|
+ KDBUS_HELLO_ATTACH_SECLABEL|
+ KDBUS_HELLO_ATTACH_AUDIT
+ };
+ int r;
+
+ assert(b);
+
+ if (b->is_server)
+ return -EINVAL;
+
+ r = ioctl(b->input_fd, KDBUS_CMD_HELLO, &hello);
+ if (r < 0)
+ return -errno;
+
+ /* The higher 32bit of both flags fields are considered
+ * 'incompatible flags'. Refuse them all for now. */
+ if (hello.bus_flags > 0xFFFFFFFFULL ||
+ hello.conn_flags > 0xFFFFFFFFULL)
+ return -ENOTSUP;
+
+ if (hello.bloom_size != BLOOM_SIZE)
+ return -ENOTSUP;
+
+ if (asprintf(&b->unique_name, ":1.%llu", (unsigned long long) hello.id) < 0)
+ return -ENOMEM;
+
+ b->is_kernel = true;
+ b->bus_client = true;
+ b->can_fds = true;
+
+ r = bus_start_running(b);
+ if (r < 0)
+ return r;
+
+ return 1;
+}
+
+int bus_kernel_connect(sd_bus *b) {
+ assert(b);
+ assert(b->input_fd < 0);
+ assert(b->output_fd < 0);
+ assert(b->kernel);
+
+ if (b->is_server)
+ return -EINVAL;
+
+ b->input_fd = open(b->kernel, O_RDWR|O_NOCTTY|O_CLOEXEC);
+ if (b->input_fd < 0)
+ return -errno;
+
+ b->output_fd = b->input_fd;
+
+ return bus_kernel_take_fd(b);
+}
+
+int bus_kernel_write_message(sd_bus *bus, sd_bus_message *m) {
+ int r;
+
+ assert(bus);
+ assert(m);
+ assert(bus->state == BUS_RUNNING);
+
+ r = bus_message_setup_kmsg(bus, m);
+ if (r < 0)
+ return r;
+
+ r = ioctl(bus->output_fd, KDBUS_CMD_MSG_SEND, m->kdbus);
+ if (r < 0)
+ return errno == EAGAIN ? 0 : -errno;
+
+ return 1;
+}
+
+static void close_kdbus_msg(struct kdbus_msg *k) {
+ struct kdbus_item *d;
+
+ KDBUS_ITEM_FOREACH(d, k) {
+
+ if (d->type != KDBUS_MSG_UNIX_FDS)
+ continue;
+
+ close_many(d->fds, (d->size - offsetof(struct kdbus_item, fds)) / sizeof(int));
+ }
+}
+
+static int bus_kernel_make_message(sd_bus *bus, struct kdbus_msg *k, sd_bus_message **ret) {
+ sd_bus_message *m = NULL;
+ struct kdbus_item *d;
+ unsigned n_payload = 0, n_fds = 0;
+ _cleanup_free_ int *fds = NULL;
+ struct bus_header *h = NULL;
+ size_t total, n_bytes = 0, idx = 0;
+ const char *destination = NULL, *seclabel = NULL;
+ int r;
+
+ assert(bus);
+ assert(k);
+ assert(ret);
+
+ if (k->payload_type != KDBUS_PAYLOAD_DBUS1)
+ return 0;
+
+ KDBUS_ITEM_FOREACH(d, k) {
+ size_t l;
+
+ l = d->size - offsetof(struct kdbus_item, data);
+
+ if (d->type == KDBUS_MSG_PAYLOAD) {
+
+ if (!h) {
+ if (l < sizeof(struct bus_header))
+ return -EBADMSG;
+
+ h = (struct bus_header*) d->data;
+ }
+
+ n_payload++;
+ n_bytes += l;
+
+ } else if (d->type == KDBUS_MSG_UNIX_FDS) {
+ int *f;
+ unsigned j;
+
+ j = l / sizeof(int);
+ f = realloc(fds, sizeof(int) * (n_fds + j));
+ if (!f)
+ return -ENOMEM;
+
+ fds = f;
+ memcpy(fds + n_fds, d->fds, sizeof(int) * j);
+ n_fds += j;
+
+ } else if (d->type == KDBUS_MSG_DST_NAME)
+ destination = d->str;
+ else if (d->type == KDBUS_MSG_SRC_SECLABEL)
+ seclabel = d->str;
+ }
+
+ if (!h)
+ return -EBADMSG;
+
+ r = bus_header_size(h, &total);
+ if (r < 0)
+ return r;
+
+ if (n_bytes != total)
+ return -EBADMSG;
+
+ r = bus_message_from_header(h, sizeof(struct bus_header), fds, n_fds, NULL, seclabel, 0, &m);
+ if (r < 0)
+ return r;
+
+ KDBUS_ITEM_FOREACH(d, k) {
+ size_t l;
+
+ l = d->size - offsetof(struct kdbus_item, data);
+
+ if (d->type == KDBUS_MSG_PAYLOAD) {
+
+ if (idx == sizeof(struct bus_header) &&
+ l == ALIGN8(BUS_MESSAGE_FIELDS_SIZE(m)))
+ m->fields = d->data;
+ else if (idx == sizeof(struct bus_header) + ALIGN8(BUS_MESSAGE_FIELDS_SIZE(m)) &&
+ l == BUS_MESSAGE_BODY_SIZE(m))
+ m->body = d->data;
+ else if (!(idx == 0 && l == sizeof(struct bus_header))) {
+ sd_bus_message_unref(m);
+ return -EBADMSG;
+ }
+
+ idx += l;
+ } else if (d->type == KDBUS_MSG_SRC_CREDS) {
+ m->pid_starttime = d->creds.starttime / NSEC_PER_USEC;
+ m->uid = d->creds.uid;
+ m->gid = d->creds.gid;
+ m->pid = d->creds.pid;
+ m->tid = d->creds.tid;
+ m->uid_valid = m->gid_valid = true;
+ } else if (d->type == KDBUS_MSG_TIMESTAMP) {
+ m->realtime = d->timestamp.realtime_ns / NSEC_PER_USEC;
+ m->monotonic = d->timestamp.monotonic_ns / NSEC_PER_USEC;
+ } else if (d->type == KDBUS_MSG_SRC_PID_COMM)
+ m->comm = d->str;
+ else if (d->type == KDBUS_MSG_SRC_TID_COMM)
+ m->tid_comm = d->str;
+ else if (d->type == KDBUS_MSG_SRC_EXE)
+ m->exe = d->str;
+ else if (d->type == KDBUS_MSG_SRC_CMDLINE) {
+ m->cmdline = d->str;
+ m->cmdline_length = l;
+ } else if (d->type == KDBUS_MSG_SRC_CGROUP)
+ m->cgroup = d->str;
+ else if (d->type == KDBUS_MSG_SRC_AUDIT)
+ m->audit = &d->audit;
+ else if (d->type == KDBUS_MSG_SRC_CAPS) {
+ m->capability = d->data;
+ m->capability_size = l;
+ } else
+ log_debug("Got unknown field from kernel %llu", d->type);
+ }
+
+ r = bus_message_parse_fields(m);
+ if (r < 0) {
+ sd_bus_message_unref(m);
+ return r;
+ }
+
+ if (k->src_id == KDBUS_SRC_ID_KERNEL)
+ m->sender = "org.freedesktop.DBus";
+ else {
+ snprintf(m->sender_buffer, sizeof(m->sender_buffer), ":1.%llu", (unsigned long long) k->src_id);
+ m->sender = m->sender_buffer;
+ }
+
+ if (!m->destination) {
+ if (destination)
+ m->destination = destination;
+ else if (k->dst_id != KDBUS_DST_ID_WELL_KNOWN_NAME &&
+ k->dst_id != KDBUS_DST_ID_BROADCAST) {
+ snprintf(m->destination_buffer, sizeof(m->destination_buffer), ":1.%llu", (unsigned long long) k->dst_id);
+ m->destination = m->destination_buffer;
+ }
+ }
+
+ /* We take possession of the kmsg struct now */
+ m->kdbus = k;
+ m->free_kdbus = true;
+ m->free_fds = true;
+
+ fds = NULL;
+
+ *ret = m;
+ return 1;
+}
+
+int bus_kernel_read_message(sd_bus *bus, sd_bus_message **m) {
+ struct kdbus_msg *k;
+ size_t sz = 1024;
+ int r;
+
+ assert(bus);
+ assert(m);
+
+ for (;;) {
+ void *q;
+
+ q = memalign(8, sz);
+ if (!q)
+ return -errno;
+
+ free(bus->rbuffer);
+ k = bus->rbuffer = q;
+ k->size = sz;
+
+ /* Let's tell valgrind that there's really no need to
+ * initialize this fully. This should be removed again
+ * when valgrind learned the kdbus ioctls natively. */
+#ifdef HAVE_VALGRIND_MEMCHECK_H
+ VALGRIND_MAKE_MEM_DEFINED(k, sz);
+#endif
+
+ r = ioctl(bus->input_fd, KDBUS_CMD_MSG_RECV, bus->rbuffer);
+ if (r >= 0)
+ break;
+
+ if (errno == EAGAIN)
+ return 0;
+
+ if (errno != ENOBUFS)
+ return -errno;
+
+ sz *= 2;
+ }
+
+ r = bus_kernel_make_message(bus, k, m);
+ if (r > 0)
+ bus->rbuffer = NULL;
+ else
+ close_kdbus_msg(k);
+
+ return r < 0 ? r : 1;
+}
+
+int bus_kernel_create(const char *name, char **s) {
+ struct kdbus_cmd_bus_make *make;
+ struct kdbus_item *n, *cg;
+ size_t l;
+ int fd;
+ char *p;
+
+ assert(name);
+ assert(s);
+
+ fd = open("/dev/kdbus/control", O_RDWR|O_NOCTTY|O_CLOEXEC);
+ if (fd < 0)
+ return -errno;
+
+ l = strlen(name);
+ make = alloca0(offsetof(struct kdbus_cmd_bus_make, items) +
+ KDBUS_ITEM_HEADER_SIZE + sizeof(uint64_t) +
+ KDBUS_ITEM_HEADER_SIZE + DECIMAL_STR_MAX(uid_t) + 1 + l + 1);
+
+ cg = make->items;
+ cg->type = KDBUS_MAKE_CGROUP;
+ cg->data64[0] = 1;
+ cg->size = KDBUS_ITEM_HEADER_SIZE + sizeof(uint64_t);
+
+ n = KDBUS_ITEM_NEXT(cg);
+ n->type = KDBUS_MAKE_NAME;
+ sprintf(n->str, "%lu-%s", (unsigned long) getuid(), name);
+ n->size = KDBUS_ITEM_HEADER_SIZE + strlen(n->str) + 1;
+
+ make->size = offsetof(struct kdbus_cmd_bus_make, items) + cg->size + n->size;
+ make->flags = KDBUS_MAKE_ACCESS_WORLD | KDBUS_MAKE_POLICY_OPEN;
+ make->bus_flags = 0;
+ make->bloom_size = BLOOM_SIZE;
+ assert_cc(BLOOM_SIZE % 8 == 0);
+
+ p = strjoin("/dev/kdbus/", n->str, "/bus", NULL);
+ if (!p)
+ return -ENOMEM;
+
+ if (ioctl(fd, KDBUS_CMD_BUS_MAKE, make) < 0) {
+ close_nointr_nofail(fd);
+ free(p);
+ return -errno;
+ }
+
+ if (s)
+ *s = p;
+
+ return fd;
+}
diff --git a/src/libsystemd-bus/bus-kernel.h b/src/libsystemd-bus/bus-kernel.h
new file mode 100644
index 0000000000..ac746afe03
--- /dev/null
+++ b/src/libsystemd-bus/bus-kernel.h
@@ -0,0 +1,32 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ 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 <http://www.gnu.org/licenses/>.
+***/
+
+#include "sd-bus.h"
+
+int bus_kernel_connect(sd_bus *b);
+int bus_kernel_take_fd(sd_bus *b);
+
+int bus_kernel_write_message(sd_bus *bus, sd_bus_message *m);
+int bus_kernel_read_message(sd_bus *bus, sd_bus_message **m);
+
+int bus_kernel_create(const char *name, char **s);
diff --git a/src/libsystemd-bus/bus-match.c b/src/libsystemd-bus/bus-match.c
new file mode 100644
index 0000000000..501a38df70
--- /dev/null
+++ b/src/libsystemd-bus/bus-match.c
@@ -0,0 +1,994 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ 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 <http://www.gnu.org/licenses/>.
+***/
+
+#include "bus-internal.h"
+#include "bus-message.h"
+#include "bus-match.h"
+
+/* Example:
+ *
+ * A: type=signal,sender=foo,interface=bar
+ * B: type=signal,sender=quux,interface=fips
+ * C: type=signal,sender=quux,interface=waldo
+ * D: type=signal,member=test
+ * E: sender=miau
+ * F: type=signal
+ * G: type=signal
+ *
+ * results in this tree:
+ *
+ * BUS_MATCH_ROOT
+ * + BUS_MATCH_MESSAGE_TYPE
+ * | ` BUS_MATCH_VALUE: value == signal
+ * | + DBUS_MATCH_SENDER
+ * | | + BUS_MATCH_VALUE: value == foo
+ * | | | ` DBUS_MATCH_INTERFACE
+ * | | | ` BUS_MATCH_VALUE: value == bar
+ * | | | ` BUS_MATCH_LEAF: A
+ * | | ` BUS_MATCH_VALUE: value == quux
+ * | | ` DBUS_MATCH_INTERFACE
+ * | | | BUS_MATCH_VALUE: value == fips
+ * | | | ` BUS_MATCH_LEAF: B
+ * | | ` BUS_MATCH_VALUE: value == waldo
+ * | | ` BUS_MATCH_LEAF: C
+ * | + DBUS_MATCH_MEMBER
+ * | | ` BUS_MATCH_VALUE: value == test
+ * | | ` BUS_MATCH_LEAF: D
+ * | + BUS_MATCH_LEAF: F
+ * | ` BUS_MATCH_LEAF: G
+ * ` BUS_MATCH_SENDER
+ * ` BUS_MATCH_VALUE: value == miau
+ * ` BUS_MATCH_LEAF: E
+ */
+
+static inline bool BUS_MATCH_IS_COMPARE(enum bus_match_node_type t) {
+ return t >= BUS_MATCH_MESSAGE_TYPE && t <= BUS_MATCH_ARG_NAMESPACE_LAST;
+}
+
+static inline bool BUS_MATCH_CAN_HASH(enum bus_match_node_type t) {
+ return (t >= BUS_MATCH_MESSAGE_TYPE && t <= BUS_MATCH_PATH) ||
+ (t >= BUS_MATCH_ARG && t <= BUS_MATCH_ARG_LAST);
+}
+
+static void bus_match_node_free(struct bus_match_node *node) {
+ assert(node);
+ assert(node->parent);
+ assert(!node->child);
+ assert(node->type != BUS_MATCH_ROOT);
+ assert(node->type < _BUS_MATCH_NODE_TYPE_MAX);
+
+ if (node->parent->child) {
+ /* We are apparently linked into the parent's child
+ * list. Let's remove us from there. */
+ if (node->prev) {
+ assert(node->prev->next == node);
+ node->prev->next = node->next;
+ } else {
+ assert(node->parent->child == node);
+ node->parent->child = node->next;
+ }
+
+ if (node->next)
+ node->next->prev = node->prev;
+ }
+
+ if (node->type == BUS_MATCH_VALUE) {
+ /* We might be in the parent's hash table, so clean
+ * this up */
+
+ if (node->parent->type == BUS_MATCH_MESSAGE_TYPE)
+ hashmap_remove(node->parent->compare.children, UINT_TO_PTR(node->value.u8));
+ else if (BUS_MATCH_CAN_HASH(node->parent->type) && node->value.str)
+ hashmap_remove(node->parent->compare.children, node->value.str);
+
+ free(node->value.str);
+ }
+
+ if (BUS_MATCH_IS_COMPARE(node->type)) {
+ assert(hashmap_isempty(node->compare.children));
+ hashmap_free(node->compare.children);
+ }
+
+ free(node);
+}
+
+static bool bus_match_node_maybe_free(struct bus_match_node *node) {
+ assert(node);
+
+ if (node->child)
+ return false;
+
+ if (BUS_MATCH_IS_COMPARE(node->type) && !hashmap_isempty(node->compare.children))
+ return true;
+
+ bus_match_node_free(node);
+ return true;
+}
+
+static bool value_node_test(
+ struct bus_match_node *node,
+ enum bus_match_node_type parent_type,
+ uint8_t value_u8,
+ const char *value_str) {
+
+ assert(node);
+ assert(node->type == BUS_MATCH_VALUE);
+
+ /* Tests parameters against this value node, doing prefix
+ * magic and stuff. */
+
+ switch (parent_type) {
+
+ case BUS_MATCH_MESSAGE_TYPE:
+ return node->value.u8 == value_u8;
+
+ case BUS_MATCH_SENDER:
+ case BUS_MATCH_DESTINATION:
+ case BUS_MATCH_INTERFACE:
+ case BUS_MATCH_MEMBER:
+ case BUS_MATCH_PATH:
+ case BUS_MATCH_ARG ... BUS_MATCH_ARG_LAST:
+ return streq_ptr(node->value.str, value_str);
+
+ case BUS_MATCH_ARG_NAMESPACE ... BUS_MATCH_ARG_NAMESPACE_LAST:
+ return namespace_simple_pattern(node->value.str, value_str);
+
+ case BUS_MATCH_PATH_NAMESPACE:
+ return path_simple_pattern(node->value.str, value_str);
+
+ case BUS_MATCH_ARG_PATH ... BUS_MATCH_ARG_PATH_LAST:
+ return path_complex_pattern(node->value.str, value_str);
+
+ default:
+ assert_not_reached("Invalid node type");
+ }
+}
+
+static bool value_node_same(
+ struct bus_match_node *node,
+ enum bus_match_node_type parent_type,
+ uint8_t value_u8,
+ const char *value_str) {
+
+ /* Tests parameters against this value node, not doing prefix
+ * magic and stuff, i.e. this one actually compares the match
+ * itself.*/
+
+ assert(node);
+ assert(node->type == BUS_MATCH_VALUE);
+
+ switch (parent_type) {
+
+ case BUS_MATCH_MESSAGE_TYPE:
+ return node->value.u8 == value_u8;
+
+ case BUS_MATCH_SENDER:
+ case BUS_MATCH_DESTINATION:
+ case BUS_MATCH_INTERFACE:
+ case BUS_MATCH_MEMBER:
+ case BUS_MATCH_PATH:
+ case BUS_MATCH_ARG ... BUS_MATCH_ARG_LAST:
+ case BUS_MATCH_ARG_NAMESPACE ... BUS_MATCH_ARG_NAMESPACE_LAST:
+ case BUS_MATCH_PATH_NAMESPACE:
+ case BUS_MATCH_ARG_PATH ... BUS_MATCH_ARG_PATH_LAST:
+ return streq(node->value.str, value_str);
+
+ default:
+ assert_not_reached("Invalid node type");
+ }
+}
+
+int bus_match_run(
+ sd_bus *bus,
+ struct bus_match_node *node,
+ int ret,
+ sd_bus_message *m) {
+
+
+ const char *test_str = NULL;
+ uint8_t test_u8 = 0;
+ int r;
+
+ assert(m);
+
+ if (!node)
+ return 0;
+
+ if (bus && bus->match_callbacks_modified)
+ return 0;
+
+ /* Not these special semantics: when traversing the tree we
+ * usually let bus_match_run() when called for a node
+ * recursively invoke bus_match_run(). There's are two
+ * exceptions here though, which are BUS_NODE_ROOT (which
+ * cannot have a sibling), and BUS_NODE_VALUE (whose siblings
+ * are invoked anyway by its parent. */
+
+ switch (node->type) {
+
+ case BUS_MATCH_ROOT:
+
+ /* Run all children. Since we cannot have any siblings
+ * we won't call any. The children of the root node
+ * are compares or leaves, they will automatically
+ * call their siblings. */
+ return bus_match_run(bus, node->child, ret, m);
+
+ case BUS_MATCH_VALUE:
+
+ /* Run all children. We don't execute any siblings, we
+ * assume our caller does that. The children of value
+ * nodes are compares or leaves, they will
+ * automatically call their siblings */
+
+ assert(node->child);
+ return bus_match_run(bus, node->child, ret, m);
+
+ case BUS_MATCH_LEAF:
+
+ if (bus) {
+ if (node->leaf.last_iteration == bus->iteration_counter)
+ return 0;
+
+ node->leaf.last_iteration = bus->iteration_counter;
+ }
+
+ r = sd_bus_message_rewind(m, true);
+ if (r < 0)
+ return r;
+
+ /* Run the callback. And then invoke siblings. */
+ assert(node->leaf.callback);
+ r = node->leaf.callback(bus, ret, m, node->leaf.userdata);
+ if (r != 0)
+ return r;
+
+ return bus_match_run(bus, node->next, ret, m);
+
+ case BUS_MATCH_MESSAGE_TYPE:
+ test_u8 = m->header->type;
+ break;
+
+ case BUS_MATCH_SENDER:
+ test_str = m->sender;
+ /* FIXME: resolve test_str from a well-known to a unique name first */
+ break;
+
+ case BUS_MATCH_DESTINATION:
+ test_str = m->destination;
+ break;
+
+ case BUS_MATCH_INTERFACE:
+ test_str = m->interface;
+ break;
+
+ case BUS_MATCH_MEMBER:
+ test_str = m->member;
+ break;
+
+ case BUS_MATCH_PATH:
+ case BUS_MATCH_PATH_NAMESPACE:
+ test_str = m->path;
+ break;
+
+ case BUS_MATCH_ARG ... BUS_MATCH_ARG_LAST:
+ test_str = bus_message_get_arg(m, node->type - BUS_MATCH_ARG);
+ break;
+
+ case BUS_MATCH_ARG_PATH ... BUS_MATCH_ARG_PATH_LAST:
+ test_str = bus_message_get_arg(m, node->type - BUS_MATCH_ARG_PATH);
+ break;
+
+ case BUS_MATCH_ARG_NAMESPACE ... BUS_MATCH_ARG_NAMESPACE_LAST:
+ test_str = bus_message_get_arg(m, node->type - BUS_MATCH_ARG_NAMESPACE);
+ break;
+
+ default:
+ assert_not_reached("Unknown match type.");
+ }
+
+ if (BUS_MATCH_CAN_HASH(node->type)) {
+ struct bus_match_node *found;
+
+ /* Lookup via hash table, nice! So let's jump directly. */
+
+ if (test_str)
+ found = hashmap_get(node->compare.children, test_str);
+ else if (node->type == BUS_MATCH_MESSAGE_TYPE)
+ found = hashmap_get(node->compare.children, UINT_TO_PTR(test_u8));
+ else
+ found = NULL;
+
+ if (found) {
+ r = bus_match_run(bus, found, ret, m);
+ if (r != 0)
+ return r;
+ }
+ } else {
+ struct bus_match_node *c;
+
+ /* No hash table, so let's iterate manually... */
+
+ for (c = node->child; c; c = c->next) {
+ if (!value_node_test(c, node->type, test_u8, test_str))
+ continue;
+
+ r = bus_match_run(bus, c, ret, m);
+ if (r != 0)
+ return r;
+ }
+ }
+
+ if (bus && bus->match_callbacks_modified)
+ return 0;
+
+ /* And now, let's invoke our siblings */
+ return bus_match_run(bus, node->next, ret, m);
+}
+
+static int bus_match_add_compare_value(
+ struct bus_match_node *where,
+ enum bus_match_node_type t,
+ uint8_t value_u8,
+ const char *value_str,
+ struct bus_match_node **ret) {
+
+ struct bus_match_node *c = NULL, *n = NULL;
+ int r;
+
+ assert(where);
+ assert(where->type == BUS_MATCH_ROOT || where->type == BUS_MATCH_VALUE);
+ assert(BUS_MATCH_IS_COMPARE(t));
+ assert(ret);
+
+ for (c = where->child; c && c->type != t; c = c->next)
+ ;
+
+ if (c) {
+ /* Comparison node already exists? Then let's see if
+ * the value node exists too. */
+
+ if (t == BUS_MATCH_MESSAGE_TYPE)
+ n = hashmap_get(c->compare.children, UINT_TO_PTR(value_u8));
+ else if (BUS_MATCH_CAN_HASH(t))
+ n = hashmap_get(c->compare.children, value_str);
+ else {
+ for (n = c->child; n && !value_node_same(n, t, value_u8, value_str); n = n->next)
+ ;
+ }
+
+ if (n) {
+ *ret = n;
+ return 0;
+ }
+ } else {
+ /* Comparison node, doesn't exist yet? Then let's
+ * create it. */
+
+ c = new0(struct bus_match_node, 1);
+ if (!c) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ c->type = t;
+ c->parent = where;
+ c->next = where->child;
+ if (c->next)
+ c->next->prev = c;
+ where->child = c;
+
+ if (t == BUS_MATCH_MESSAGE_TYPE) {
+ c->compare.children = hashmap_new(trivial_hash_func, trivial_compare_func);
+ if (!c->compare.children) {
+ r = -ENOMEM;
+ goto fail;
+ }
+ } else if (BUS_MATCH_CAN_HASH(t)) {
+ c->compare.children = hashmap_new(string_hash_func, string_compare_func);
+ if (!c->compare.children) {
+ r = -ENOMEM;
+ goto fail;
+ }
+ }
+ }
+
+ n = new0(struct bus_match_node, 1);
+ if (!n) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ n->type = BUS_MATCH_VALUE;
+ n->value.u8 = value_u8;
+ if (value_str) {
+ n->value.str = strdup(value_str);
+ if (!n->value.str) {
+ r = -ENOMEM;
+ goto fail;
+ }
+ }
+
+ n->parent = c;
+ if (c->compare.children) {
+
+ if (t == BUS_MATCH_MESSAGE_TYPE)
+ r = hashmap_put(c->compare.children, UINT_TO_PTR(value_u8), n);
+ else
+ r = hashmap_put(c->compare.children, n->value.str, n);
+
+ if (r < 0)
+ goto fail;
+ } else {
+ n->next = c->child;
+ if (n->next)
+ n->next->prev = n;
+ c->child = n;
+ }
+
+ *ret = n;
+ return 1;
+
+fail:
+ if (c)
+ bus_match_node_maybe_free(c);
+
+ if (n) {
+ free(n->value.str);
+ free(n);
+ }
+
+ return r;
+}
+
+static int bus_match_find_compare_value(
+ struct bus_match_node *where,
+ enum bus_match_node_type t,
+ uint8_t value_u8,
+ const char *value_str,
+ struct bus_match_node **ret) {
+
+ struct bus_match_node *c, *n;
+
+ assert(where);
+ assert(where->type == BUS_MATCH_ROOT || where->type == BUS_MATCH_VALUE);
+ assert(BUS_MATCH_IS_COMPARE(t));
+ assert(ret);
+
+ for (c = where->child; c && c->type != t; c = c->next)
+ ;
+
+ if (!c)
+ return 0;
+
+ if (t == BUS_MATCH_MESSAGE_TYPE)
+ n = hashmap_get(c->compare.children, UINT_TO_PTR(value_u8));
+ else if (BUS_MATCH_CAN_HASH(t))
+ n = hashmap_get(c->compare.children, value_str);
+ else {
+ for (n = c->child; !value_node_same(n, t, value_u8, value_str); n = n->next)
+ ;
+ }
+
+ if (n) {
+ *ret = n;
+ return 1;
+ }
+
+ return 0;
+}
+
+static int bus_match_add_leaf(
+ struct bus_match_node *where,
+ sd_bus_message_handler_t callback,
+ void *userdata,
+ struct bus_match_node **ret) {
+
+ struct bus_match_node *n;
+
+ assert(where);
+ assert(where->type == BUS_MATCH_ROOT || where->type == BUS_MATCH_VALUE);
+ assert(ret);
+
+ n = new0(struct bus_match_node, 1);
+ if (!n)
+ return -ENOMEM;
+
+ n->type = BUS_MATCH_LEAF;
+ n->parent = where;
+ n->next = where->child;
+ if (n->next)
+ n->next->prev = n;
+ n->leaf.callback = callback;
+ n->leaf.userdata = userdata;
+
+ where->child = n;
+
+ *ret = n;
+ return 1;
+}
+
+static int bus_match_find_leaf(
+ struct bus_match_node *where,
+ sd_bus_message_handler_t callback,
+ void *userdata,
+ struct bus_match_node **ret) {
+
+ struct bus_match_node *c;
+
+ assert(where);
+ assert(where->type == BUS_MATCH_ROOT || where->type == BUS_MATCH_VALUE);
+ assert(ret);
+
+ for (c = where->child; c; c = c->next) {
+ if (c->type == BUS_MATCH_LEAF &&
+ c->leaf.callback == callback &&
+ c->leaf.userdata == userdata) {
+ *ret = c;
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+enum bus_match_node_type bus_match_node_type_from_string(const char *k, size_t n) {
+ assert(k);
+
+ if (n == 4 && memcmp(k, "type", 4) == 0)
+ return BUS_MATCH_MESSAGE_TYPE;
+ if (n == 6 && memcmp(k, "sender", 6) == 0)
+ return BUS_MATCH_SENDER;
+ if (n == 11 && memcmp(k, "destination", 11) == 0)
+ return BUS_MATCH_DESTINATION;
+ if (n == 9 && memcmp(k, "interface", 9) == 0)
+ return BUS_MATCH_INTERFACE;
+ if (n == 6 && memcmp(k, "member", 6) == 0)
+ return BUS_MATCH_MEMBER;
+ if (n == 4 && memcmp(k, "path", 4) == 0)
+ return BUS_MATCH_PATH;
+ if (n == 14 && memcmp(k, "path_namespace", 14) == 0)
+ return BUS_MATCH_PATH_NAMESPACE;
+
+ if (n == 4 && memcmp(k, "arg", 3) == 0) {
+ int j;
+
+ j = undecchar(k[3]);
+ if (j < 0)
+ return -EINVAL;
+
+ return BUS_MATCH_ARG + j;
+ }
+
+ if (n == 5 && memcmp(k, "arg", 3) == 0) {
+ int a, b;
+ enum bus_match_node_type t;
+
+ a = undecchar(k[3]);
+ b = undecchar(k[4]);
+ if (a <= 0 || b < 0)
+ return -EINVAL;
+
+ t = BUS_MATCH_ARG + a * 10 + b;
+ if (t > BUS_MATCH_ARG_LAST)
+ return -EINVAL;
+
+ return t;
+ }
+
+ if (n == 8 && memcmp(k, "arg", 3) == 0 && memcmp(k + 4, "path", 4) == 0) {
+ int j;
+
+ j = undecchar(k[3]);
+ if (j < 0)
+ return -EINVAL;
+
+ return BUS_MATCH_ARG_PATH + j;
+ }
+
+ if (n == 9 && memcmp(k, "arg", 3) == 0 && memcmp(k + 5, "path", 4) == 0) {
+ enum bus_match_node_type t;
+ int a, b;
+
+ a = undecchar(k[3]);
+ b = undecchar(k[4]);
+ if (a <= 0 || b < 0)
+ return -EINVAL;
+
+ t = BUS_MATCH_ARG_PATH + a * 10 + b;
+ if (t > BUS_MATCH_ARG_PATH_LAST)
+ return -EINVAL;
+
+ return t;
+ }
+
+ if (n == 13 && memcmp(k, "arg", 3) == 0 && memcmp(k + 4, "namespace", 9) == 0) {
+ int j;
+
+ j = undecchar(k[3]);
+ if (j < 0)
+ return -EINVAL;
+
+ return BUS_MATCH_ARG_NAMESPACE + j;
+ }
+
+ if (n == 14 && memcmp(k, "arg", 3) == 0 && memcmp(k + 5, "namespace", 9) == 0) {
+ enum bus_match_node_type t;
+ int a, b;
+
+ a = undecchar(k[3]);
+ b = undecchar(k[4]);
+ if (a <= 0 || b < 0)
+ return -EINVAL;
+
+ t = BUS_MATCH_ARG_NAMESPACE + a * 10 + b;
+ if (t > BUS_MATCH_ARG_NAMESPACE_LAST)
+ return -EINVAL;
+
+ return t;
+ }
+
+ return -EINVAL;
+}
+
+struct match_component {
+ enum bus_match_node_type type;
+ uint8_t value_u8;
+ char *value_str;
+};
+
+static int match_component_compare(const void *a, const void *b) {
+ const struct match_component *x = a, *y = b;
+
+ if (x->type < y->type)
+ return -1;
+ if (x->type > y->type)
+ return 1;
+
+ return 0;
+}
+
+static void free_components(struct match_component *components, unsigned n_components) {
+ unsigned i;
+
+ for (i = 0; i < n_components; i++)
+ free(components[i].value_str);
+
+ free(components);
+}
+
+static int parse_match(
+ const char *match,
+ struct match_component **_components,
+ unsigned *_n_components) {
+
+ const char *p = match;
+ struct match_component *components = NULL;
+ size_t components_allocated = 0;
+ unsigned n_components = 0, i;
+ _cleanup_free_ char *value = NULL;
+ int r;
+
+ assert(match);
+ assert(_components);
+ assert(_n_components);
+
+ while (*p != 0) {
+ const char *eq, *q;
+ enum bus_match_node_type t;
+ unsigned j = 0;
+ size_t value_allocated = 0;
+ bool escaped = false;
+ uint8_t u;
+
+ eq = strchr(p, '=');
+ if (!eq)
+ return -EINVAL;
+
+ if (eq[1] != '\'')
+ return -EINVAL;
+
+ t = bus_match_node_type_from_string(p, eq - p);
+ if (t < 0)
+ return -EINVAL;
+
+ for (q = eq + 2;; q++) {
+
+ if (*q == 0) {
+ r = -EINVAL;
+ goto fail;
+ }
+
+ if (!escaped) {
+ if (*q == '\\') {
+ escaped = true;
+ continue;
+ }
+ if (*q == '\'') {
+ if (value)
+ value[j] = 0;
+ break;
+ }
+ }
+
+ if (!GREEDY_REALLOC(value, value_allocated, j + 2)) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ value[j++] = *q;
+ escaped = false;
+ }
+
+ if (t == BUS_MATCH_MESSAGE_TYPE) {
+ r = bus_message_type_from_string(value, &u);
+ if (r < 0)
+ goto fail;
+
+ free(value);
+ value = NULL;
+ } else
+ u = 0;
+
+ if (!GREEDY_REALLOC(components, components_allocated, n_components + 1)) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ components[n_components].type = t;
+ components[n_components].value_str = value;
+ components[n_components].value_u8 = u;
+ n_components++;
+
+ value = NULL;
+
+ if (q[1] == 0)
+ break;
+
+ if (q[1] != ',') {
+ r = -EINVAL;
+ goto fail;
+ }
+
+ p = q + 2;
+ }
+
+ /* Order the whole thing, so that we always generate the same tree */
+ qsort(components, n_components, sizeof(struct match_component), match_component_compare);
+
+ /* Check for duplicates */
+ for (i = 0; i+1 < n_components; i++)
+ if (components[i].type == components[i+1].type) {
+ r = -EINVAL;
+ goto fail;
+ }
+
+ *_components = components;
+ *_n_components = n_components;
+
+ return 0;
+
+fail:
+ free_components(components, n_components);
+ return r;
+}
+
+int bus_match_add(
+ struct bus_match_node *root,
+ const char *match,
+ sd_bus_message_handler_t callback,
+ void *userdata,
+ struct bus_match_node **ret) {
+
+ struct match_component *components = NULL;
+ unsigned n_components = 0, i;
+ struct bus_match_node *n;
+ int r;
+
+ assert(root);
+ assert(match);
+ assert(callback);
+
+ r = parse_match(match, &components, &n_components);
+ if (r < 0)
+ return r;
+
+ n = root;
+ for (i = 0; i < n_components; i++) {
+ r = bus_match_add_compare_value(
+ n, components[i].type,
+ components[i].value_u8, components[i].value_str, &n);
+ if (r < 0)
+ goto finish;
+ }
+
+ r = bus_match_add_leaf(n, callback, userdata, &n);
+ if (r < 0)
+ goto finish;
+
+ if (ret)
+ *ret = n;
+
+finish:
+ free_components(components, n_components);
+ return r;
+}
+
+int bus_match_remove(
+ struct bus_match_node *root,
+ const char *match,
+ sd_bus_message_handler_t callback,
+ void *userdata) {
+
+ struct match_component *components = NULL;
+ unsigned n_components = 0, i;
+ struct bus_match_node *n, **gc;
+ int r;
+
+ assert(root);
+ assert(match);
+
+ r = parse_match(match, &components, &n_components);
+ if (r < 0)
+ return r;
+
+ gc = newa(struct bus_match_node*, n_components);
+
+ n = root;
+ for (i = 0; i < n_components; i++) {
+ r = bus_match_find_compare_value(
+ n, components[i].type,
+ components[i].value_u8, components[i].value_str,
+ &n);
+ if (r <= 0)
+ goto finish;
+
+ gc[i] = n;
+ }
+
+ r = bus_match_find_leaf(n, callback, userdata, &n);
+ if (r <= 0)
+ goto finish;
+
+ /* Free the leaf */
+ bus_match_node_free(n);
+
+ /* Prune the tree above */
+ for (i = n_components; i > 0; i --) {
+ struct bus_match_node *p = gc[i-1]->parent;
+
+ if (!bus_match_node_maybe_free(gc[i-1]))
+ break;
+
+ if (!bus_match_node_maybe_free(p))
+ break;
+ }
+
+finish:
+ free_components(components, n_components);
+ return r;
+}
+
+void bus_match_free(struct bus_match_node *node) {
+ struct bus_match_node *c;
+
+ if (!node)
+ return;
+
+ if (BUS_MATCH_CAN_HASH(node->type)) {
+ Iterator i;
+
+ HASHMAP_FOREACH(c, node->compare.children, i)
+ bus_match_free(c);
+
+ assert(hashmap_isempty(node->compare.children));
+ }
+
+ while ((c = node->child))
+ bus_match_free(c);
+
+ if (node->type != BUS_MATCH_ROOT)
+ bus_match_node_free(node);
+}
+
+const char* bus_match_node_type_to_string(enum bus_match_node_type t, char buf[], size_t l) {
+ switch (t) {
+
+ case BUS_MATCH_ROOT:
+ return "root";
+
+ case BUS_MATCH_VALUE:
+ return "value";
+
+ case BUS_MATCH_LEAF:
+ return "leaf";
+
+ case BUS_MATCH_MESSAGE_TYPE:
+ return "type";
+
+ case BUS_MATCH_SENDER:
+ return "sender";
+
+ case BUS_MATCH_DESTINATION:
+ return "destination";
+
+ case BUS_MATCH_INTERFACE:
+ return "interface";
+
+ case BUS_MATCH_MEMBER:
+ return "member";
+
+ case BUS_MATCH_PATH:
+ return "path";
+
+ case BUS_MATCH_PATH_NAMESPACE:
+ return "path_namespace";
+
+ case BUS_MATCH_ARG ... BUS_MATCH_ARG_LAST:
+ snprintf(buf, l, "arg%i", t - BUS_MATCH_ARG);
+ return buf;
+
+ case BUS_MATCH_ARG_PATH ... BUS_MATCH_ARG_PATH_LAST:
+ snprintf(buf, l, "arg%ipath", t - BUS_MATCH_ARG_PATH);
+ return buf;
+
+ case BUS_MATCH_ARG_NAMESPACE ... BUS_MATCH_ARG_NAMESPACE_LAST:
+ snprintf(buf, l, "arg%inamespace", t - BUS_MATCH_ARG_NAMESPACE);
+ return buf;
+
+ default:
+ return NULL;
+ }
+}
+
+void bus_match_dump(struct bus_match_node *node, unsigned level) {
+ struct bus_match_node *c;
+ _cleanup_free_ char *pfx = NULL;
+ char buf[32];
+
+ if (!node)
+ return;
+
+ pfx = strrep(" ", level);
+ printf("%s[%s]", strempty(pfx), bus_match_node_type_to_string(node->type, buf, sizeof(buf)));
+
+ if (node->type == BUS_MATCH_VALUE) {
+ if (node->parent->type == BUS_MATCH_MESSAGE_TYPE)
+ printf(" <%u>\n", node->value.u8);
+ else
+ printf(" <%s>\n", node->value.str);
+ } else if (node->type == BUS_MATCH_ROOT)
+ puts(" root");
+ else if (node->type == BUS_MATCH_LEAF)
+ printf(" %p/%p\n", node->leaf.callback, node->leaf.userdata);
+ else
+ putchar('\n');
+
+ if (BUS_MATCH_CAN_HASH(node->type)) {
+ Iterator i;
+
+ HASHMAP_FOREACH(c, node->compare.children, i)
+ bus_match_dump(c, level + 1);
+ }
+
+ for (c = node->child; c; c = c->next)
+ bus_match_dump(c, level + 1);
+}
diff --git a/src/libsystemd-bus/bus-match.h b/src/libsystemd-bus/bus-match.h
new file mode 100644
index 0000000000..075f1a9e3a
--- /dev/null
+++ b/src/libsystemd-bus/bus-match.h
@@ -0,0 +1,82 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ 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 <http://www.gnu.org/licenses/>.
+***/
+
+#include "hashmap.h"
+
+#include "sd-bus.h"
+
+enum bus_match_node_type {
+ BUS_MATCH_ROOT,
+ BUS_MATCH_VALUE,
+ BUS_MATCH_LEAF,
+
+ /* The following are all different kinds of compare nodes */
+ BUS_MATCH_MESSAGE_TYPE,
+ BUS_MATCH_SENDER,
+ BUS_MATCH_DESTINATION,
+ BUS_MATCH_INTERFACE,
+ BUS_MATCH_MEMBER,
+ BUS_MATCH_PATH,
+ BUS_MATCH_PATH_NAMESPACE,
+ BUS_MATCH_ARG,
+ BUS_MATCH_ARG_LAST = BUS_MATCH_ARG + 63,
+ BUS_MATCH_ARG_PATH,
+ BUS_MATCH_ARG_PATH_LAST = BUS_MATCH_ARG_PATH + 63,
+ BUS_MATCH_ARG_NAMESPACE,
+ BUS_MATCH_ARG_NAMESPACE_LAST = BUS_MATCH_ARG_NAMESPACE + 63,
+ _BUS_MATCH_NODE_TYPE_MAX,
+ _BUS_MATCH_NODE_TYPE_INVALID = -1
+};
+
+struct bus_match_node {
+ enum bus_match_node_type type;
+ struct bus_match_node *parent, *next, *prev, *child;
+
+ union {
+ struct {
+ char *str;
+ uint8_t u8;
+ } value;
+ struct {
+ sd_bus_message_handler_t callback;
+ void *userdata;
+ unsigned last_iteration;
+ } leaf;
+ struct {
+ /* If this is set, then the child is NULL */
+ Hashmap *children;
+ } compare;
+ };
+};
+
+int bus_match_run(sd_bus *bus, struct bus_match_node *root, int ret, sd_bus_message *m);
+
+int bus_match_add(struct bus_match_node *root, const char *match, sd_bus_message_handler_t callback, void *userdata, struct bus_match_node **ret);
+int bus_match_remove(struct bus_match_node *root, const char *match, sd_bus_message_handler_t callback, void *userdata);
+
+void bus_match_free(struct bus_match_node *node);
+
+void bus_match_dump(struct bus_match_node *node, unsigned level);
+
+const char* bus_match_node_type_to_string(enum bus_match_node_type t, char buf[], size_t l);
+enum bus_match_node_type bus_match_node_type_from_string(const char *k, size_t n);
diff --git a/src/libsystemd-bus/bus-message.c b/src/libsystemd-bus/bus-message.c
new file mode 100644
index 0000000000..835a9f9a44
--- /dev/null
+++ b/src/libsystemd-bus/bus-message.c
@@ -0,0 +1,3494 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ 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 <http://www.gnu.org/licenses/>.
+***/
+
+#include <errno.h>
+#include <fcntl.h>
+
+#include "util.h"
+#include "utf8.h"
+#include "strv.h"
+#include "time-util.h"
+#include "cgroup-util.h"
+
+#include "sd-bus.h"
+#include "bus-message.h"
+#include "bus-internal.h"
+#include "bus-type.h"
+#include "bus-signature.h"
+
+static int message_append_basic(sd_bus_message *m, char type, const void *p, const void **stored);
+
+static void reset_containers(sd_bus_message *m) {
+ unsigned i;
+
+ assert(m);
+
+ for (i = 0; i < m->n_containers; i++)
+ free(m->containers[i].signature);
+
+ free(m->containers);
+ m->containers = NULL;
+
+ m->n_containers = 0;
+ m->root_container.index = 0;
+}
+
+static void message_free(sd_bus_message *m) {
+ assert(m);
+
+ if (m->free_header)
+ free(m->header);
+
+ if (m->free_fields)
+ free(m->fields);
+
+ if (m->free_body)
+ free(m->body);
+
+ if (m->free_kdbus)
+ free(m->kdbus);
+
+ if (m->free_fds) {
+ close_many(m->fds, m->n_fds);
+ free(m->fds);
+ }
+
+ free(m->cmdline_array);
+
+ reset_containers(m);
+ free(m->root_container.signature);
+
+ free(m->peeked_signature);
+
+ free(m->unit);
+ free(m->user_unit);
+ free(m->session);
+ free(m);
+}
+
+static void* buffer_extend(void **p, uint32_t *sz, size_t align, size_t extend) {
+ size_t start, n;
+ void *k;
+
+ assert(p);
+ assert(sz);
+ assert(align > 0);
+
+ start = ALIGN_TO((size_t) *sz, align);
+ n = start + extend;
+
+ if (n == *sz)
+ return (uint8_t*) *p + start;
+
+ if (n > (size_t) ((uint32_t) -1))
+ return NULL;
+
+ k = realloc(*p, n);
+ if (!k)
+ return NULL;
+
+ /* Zero out padding */
+ if (start > *sz)
+ memset((uint8_t*) k + *sz, 0, start - *sz);
+
+ *p = k;
+ *sz = n;
+
+ return (uint8_t*) k + start;
+}
+
+static void *message_extend_fields(sd_bus_message *m, size_t align, size_t sz) {
+ void *p, *o;
+
+ assert(m);
+
+ o = m->fields;
+ p = buffer_extend(&m->fields, &m->header->fields_size, align, sz);
+ if (!p)
+ return NULL;
+
+ if (o != m->fields) {
+ /* Adjust quick access pointers */
+
+ if (m->path)
+ m->path = (const char*) m->fields + (m->path - (const char*) o);
+ if (m->interface)
+ m->interface = (const char*) m->fields + (m->interface - (const char*) o);
+ if (m->member)
+ m->member = (const char*) m->fields + (m->member - (const char*) o);
+ if (m->destination)
+ m->destination = (const char*) m->fields + (m->destination - (const char*) o);
+ if (m->sender)
+ m->sender = (const char*) m->fields + (m->sender - (const char*) o);
+ if (m->error.name)
+ m->error.name = (const char*) m->fields + (m->error.name - (const char*) o);
+ }
+
+ m->free_fields = true;
+
+ return p;
+}
+
+static int message_append_field_string(
+ sd_bus_message *m,
+ uint8_t h,
+ char type,
+ const char *s,
+ const char **ret) {
+
+ size_t l;
+ uint8_t *p;
+
+ assert(m);
+
+ l = strlen(s);
+ if (l > (size_t) (uint32_t) -1)
+ return -EINVAL;
+
+ /* field id byte + signature length + signature 's' + NUL + string length + string + NUL */
+ p = message_extend_fields(m, 8, 4 + 4 + l + 1);
+ if (!p)
+ return -ENOMEM;
+
+ p[0] = h;
+ p[1] = 1;
+ p[2] = type;
+ p[3] = 0;
+
+ ((uint32_t*) p)[1] = l;
+ memcpy(p + 8, s, l + 1);
+
+ if (ret)
+ *ret = (const char*) p + 8;
+
+ return 0;
+}
+
+static int message_append_field_signature(
+ sd_bus_message *m,
+ uint8_t h,
+ const char *s,
+ const char **ret) {
+
+ size_t l;
+ uint8_t *p;
+
+ assert(m);
+
+ l = strlen(s);
+ if (l > 255)
+ return -EINVAL;
+
+ /* field id byte + signature length + signature 'g' + NUL + string length + string + NUL */
+ p = message_extend_fields(m, 8, 4 + 1 + l + 1);
+ if (!p)
+ return -ENOMEM;
+
+ p[0] = h;
+ p[1] = 1;
+ p[2] = SD_BUS_TYPE_SIGNATURE;
+ p[3] = 0;
+ p[4] = l;
+ memcpy(p + 5, s, l + 1);
+
+ if (ret)
+ *ret = (const char*) p + 5;
+
+ return 0;
+}
+
+static int message_append_field_uint32(sd_bus_message *m, uint8_t h, uint32_t x) {
+ uint8_t *p;
+
+ assert(m);
+
+ /* field id byte + signature length + signature 'u' + NUL + value */
+ p = message_extend_fields(m, 8, 4 + 4);
+ if (!p)
+ return -ENOMEM;
+
+ p[0] = h;
+ p[1] = 1;
+ p[2] = SD_BUS_TYPE_UINT32;
+ p[3] = 0;
+
+ ((uint32_t*) p)[1] = x;
+
+ return 0;
+}
+
+int bus_message_from_header(
+ void *buffer,
+ size_t length,
+ int *fds,
+ unsigned n_fds,
+ const struct ucred *ucred,
+ const char *label,
+ size_t extra,
+ sd_bus_message **ret) {
+
+ sd_bus_message *m;
+ struct bus_header *h;
+ size_t a, label_sz;
+
+ assert(buffer || length <= 0);
+ assert(fds || n_fds <= 0);
+ assert(ret);
+
+ if (length < sizeof(struct bus_header))
+ return -EBADMSG;
+
+ h = buffer;
+ if (h->version != 1)
+ return -EBADMSG;
+
+ if (h->serial == 0)
+ return -EBADMSG;
+
+ if (h->type == _SD_BUS_MESSAGE_TYPE_INVALID)
+ return -EBADMSG;
+
+ if (h->endian != SD_BUS_LITTLE_ENDIAN &&
+ h->endian != SD_BUS_BIG_ENDIAN)
+ return -EBADMSG;
+
+ a = ALIGN(sizeof(sd_bus_message)) + ALIGN(extra);
+
+ if (label) {
+ label_sz = strlen(label);
+ a += label_sz + 1;
+ }
+
+ m = malloc0(a);
+ if (!m)
+ return -ENOMEM;
+
+ m->n_ref = 1;
+ m->sealed = true;
+ m->header = h;
+ m->fds = fds;
+ m->n_fds = n_fds;
+
+ if (ucred) {
+ m->uid = ucred->uid;
+ m->pid = ucred->pid;
+ m->gid = ucred->gid;
+ m->uid_valid = m->gid_valid = true;
+ }
+
+ if (label) {
+ m->label = (char*) m + ALIGN(sizeof(sd_bus_message)) + ALIGN(extra);
+ memcpy(m->label, label, label_sz + 1);
+ }
+
+ *ret = m;
+ return 0;
+}
+
+int bus_message_from_malloc(
+ void *buffer,
+ size_t length,
+ int *fds,
+ unsigned n_fds,
+ const struct ucred *ucred,
+ const char *label,
+ sd_bus_message **ret) {
+
+ sd_bus_message *m;
+ int r;
+
+ r = bus_message_from_header(buffer, length, fds, n_fds, ucred, label, 0, &m);
+ if (r < 0)
+ return r;
+
+ if (length != BUS_MESSAGE_SIZE(m)) {
+ r = -EBADMSG;
+ goto fail;
+ }
+
+ m->fields = (uint8_t*) buffer + sizeof(struct bus_header);
+ m->body = (uint8_t*) buffer + sizeof(struct bus_header) + ALIGN8(BUS_MESSAGE_FIELDS_SIZE(m));
+
+ m->n_iovec = 1;
+ m->iovec[0].iov_base = buffer;
+ m->iovec[0].iov_len = length;
+
+ r = bus_message_parse_fields(m);
+ if (r < 0)
+ goto fail;
+
+ /* We take possession of the memory and fds now */
+ m->free_header = true;
+ m->free_fds = true;
+
+ *ret = m;
+ return 0;
+
+fail:
+ message_free(m);
+ return r;
+}
+
+static sd_bus_message *message_new(sd_bus *bus, uint8_t type) {
+ sd_bus_message *m;
+
+ m = malloc0(ALIGN(sizeof(sd_bus_message)) + sizeof(struct bus_header));
+ if (!m)
+ return NULL;
+
+ m->n_ref = 1;
+ m->header = (struct bus_header*) ((uint8_t*) m + ALIGN(sizeof(struct sd_bus_message)));
+ m->header->endian = SD_BUS_NATIVE_ENDIAN;
+ m->header->type = type;
+ m->header->version = bus ? bus->message_version : 1;
+ m->allow_fds = !bus || bus->can_fds || (bus->state != BUS_HELLO && bus->state != BUS_RUNNING);
+
+ return m;
+}
+
+int sd_bus_message_new_signal(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *member,
+ sd_bus_message **m) {
+
+ sd_bus_message *t;
+ int r;
+
+ if (!path)
+ return -EINVAL;
+ if (!interface)
+ return -EINVAL;
+ if (!member)
+ return -EINVAL;
+ if (!m)
+ return -EINVAL;
+ if (bus && bus->state == BUS_UNSET)
+ return -ENOTCONN;
+
+ t = message_new(bus, SD_BUS_MESSAGE_TYPE_SIGNAL);
+ if (!t)
+ return -ENOMEM;
+
+ t->header->flags |= SD_BUS_MESSAGE_NO_REPLY_EXPECTED;
+
+ r = message_append_field_string(t, SD_BUS_MESSAGE_HEADER_PATH, SD_BUS_TYPE_OBJECT_PATH, path, &t->path);
+ if (r < 0)
+ goto fail;
+ r = message_append_field_string(t, SD_BUS_MESSAGE_HEADER_INTERFACE, SD_BUS_TYPE_STRING, interface, &t->interface);
+ if (r < 0)
+ goto fail;
+ r = message_append_field_string(t, SD_BUS_MESSAGE_HEADER_MEMBER, SD_BUS_TYPE_STRING, member, &t->member);
+ if (r < 0)
+ goto fail;
+
+ *m = t;
+ return 0;
+
+fail:
+ sd_bus_message_unref(t);
+ return r;
+}
+
+int sd_bus_message_new_method_call(
+ sd_bus *bus,
+ const char *destination,
+ const char *path,
+ const char *interface,
+ const char *member,
+ sd_bus_message **m) {
+
+ sd_bus_message *t;
+ int r;
+
+ if (!path)
+ return -EINVAL;
+ if (!member)
+ return -EINVAL;
+ if (!m)
+ return -EINVAL;
+ if (bus && bus->state == BUS_UNSET)
+ return -ENOTCONN;
+
+ t = message_new(bus, SD_BUS_MESSAGE_TYPE_METHOD_CALL);
+ if (!t)
+ return -ENOMEM;
+
+ r = message_append_field_string(t, SD_BUS_MESSAGE_HEADER_PATH, SD_BUS_TYPE_OBJECT_PATH, path, &t->path);
+ if (r < 0)
+ goto fail;
+ r = message_append_field_string(t, SD_BUS_MESSAGE_HEADER_MEMBER, SD_BUS_TYPE_STRING, member, &t->member);
+ if (r < 0)
+ goto fail;
+
+ if (interface) {
+ r = message_append_field_string(t, SD_BUS_MESSAGE_HEADER_INTERFACE, SD_BUS_TYPE_STRING, interface, &t->interface);
+ if (r < 0)
+ goto fail;
+ }
+
+ if (destination) {
+ r = message_append_field_string(t, SD_BUS_MESSAGE_HEADER_DESTINATION, SD_BUS_TYPE_STRING, destination, &t->destination);
+ if (r < 0)
+ goto fail;
+ }
+
+ *m = t;
+ return 0;
+
+fail:
+ message_free(t);
+ return r;
+}
+
+static int message_new_reply(
+ sd_bus *bus,
+ sd_bus_message *call,
+ uint8_t type,
+ sd_bus_message **m) {
+
+ sd_bus_message *t;
+ int r;
+
+ if (!call)
+ return -EINVAL;
+ if (!call->sealed)
+ return -EPERM;
+ if (call->header->type != SD_BUS_MESSAGE_TYPE_METHOD_CALL)
+ return -EINVAL;
+ if (!m)
+ return -EINVAL;
+ if (bus && bus->state == BUS_UNSET)
+ return -ENOTCONN;
+
+ t = message_new(bus, type);
+ if (!t)
+ return -ENOMEM;
+
+ t->header->flags |= SD_BUS_MESSAGE_NO_REPLY_EXPECTED;
+ t->reply_serial = BUS_MESSAGE_SERIAL(call);
+
+ r = message_append_field_uint32(t, SD_BUS_MESSAGE_HEADER_REPLY_SERIAL, t->reply_serial);
+ if (r < 0)
+ goto fail;
+
+ if (call->sender) {
+ r = message_append_field_string(t, SD_BUS_MESSAGE_HEADER_DESTINATION, SD_BUS_TYPE_STRING, call->sender, &t->sender);
+ if (r < 0)
+ goto fail;
+ }
+
+ t->dont_send = !!(call->header->flags & SD_BUS_MESSAGE_NO_REPLY_EXPECTED);
+
+ *m = t;
+ return 0;
+
+fail:
+ message_free(t);
+ return r;
+}
+
+int sd_bus_message_new_method_return(
+ sd_bus *bus,
+ sd_bus_message *call,
+ sd_bus_message **m) {
+
+ return message_new_reply(bus, call, SD_BUS_MESSAGE_TYPE_METHOD_RETURN, m);
+}
+
+int sd_bus_message_new_method_error(
+ sd_bus *bus,
+ sd_bus_message *call,
+ const sd_bus_error *e,
+ sd_bus_message **m) {
+
+ sd_bus_message *t;
+ int r;
+
+ if (!sd_bus_error_is_set(e))
+ return -EINVAL;
+ if (!m)
+ return -EINVAL;
+
+ r = message_new_reply(bus, call, SD_BUS_MESSAGE_TYPE_METHOD_ERROR, &t);
+ if (r < 0)
+ return r;
+
+ r = message_append_field_string(t, SD_BUS_MESSAGE_HEADER_ERROR_NAME, SD_BUS_TYPE_STRING, e->name, &t->error.name);
+ if (r < 0)
+ goto fail;
+
+ if (e->message) {
+ r = message_append_basic(t, SD_BUS_TYPE_STRING, e->message, (const void**) &t->error.message);
+ if (r < 0)
+ goto fail;
+ }
+
+ *m = t;
+ return 0;
+
+fail:
+ message_free(t);
+ return r;
+}
+
+sd_bus_message* sd_bus_message_ref(sd_bus_message *m) {
+ if (!m)
+ return NULL;
+
+ assert(m->n_ref > 0);
+ m->n_ref++;
+
+ return m;
+}
+
+sd_bus_message* sd_bus_message_unref(sd_bus_message *m) {
+ if (!m)
+ return NULL;
+
+ assert(m->n_ref > 0);
+ m->n_ref--;
+
+ if (m->n_ref <= 0)
+ message_free(m);
+
+ return NULL;
+}
+
+int sd_bus_message_get_type(sd_bus_message *m, uint8_t *type) {
+ if (!m)
+ return -EINVAL;
+ if (!type)
+ return -EINVAL;
+
+ *type = m->header->type;
+ return 0;
+}
+
+int sd_bus_message_get_serial(sd_bus_message *m, uint64_t *serial) {
+ if (!m)
+ return -EINVAL;
+ if (!serial)
+ return -EINVAL;
+ if (m->header->serial == 0)
+ return -ENOENT;
+
+ *serial = BUS_MESSAGE_SERIAL(m);
+ return 0;
+}
+
+int sd_bus_message_get_reply_serial(sd_bus_message *m, uint64_t *serial) {
+ if (!m)
+ return -EINVAL;
+ if (!serial)
+ return -EINVAL;
+ if (m->reply_serial == 0)
+ return -ENOENT;
+
+ *serial = m->reply_serial;
+ return 0;
+}
+
+int sd_bus_message_get_no_reply(sd_bus_message *m) {
+ if (!m)
+ return -EINVAL;
+
+ return m->header->type == SD_BUS_MESSAGE_TYPE_METHOD_CALL ? !!(m->header->flags & SD_BUS_MESSAGE_NO_REPLY_EXPECTED) : 0;
+}
+
+const char *sd_bus_message_get_path(sd_bus_message *m) {
+ if (!m)
+ return NULL;
+
+ return m->path;
+}
+
+const char *sd_bus_message_get_interface(sd_bus_message *m) {
+ if (!m)
+ return NULL;
+
+ return m->interface;
+}
+
+const char *sd_bus_message_get_member(sd_bus_message *m) {
+ if (!m)
+ return NULL;
+
+ return m->member;
+}
+const char *sd_bus_message_get_destination(sd_bus_message *m) {
+ if (!m)
+ return NULL;
+
+ return m->destination;
+}
+
+const char *sd_bus_message_get_sender(sd_bus_message *m) {
+ if (!m)
+ return NULL;
+
+ return m->sender;
+}
+
+const sd_bus_error *sd_bus_message_get_error(sd_bus_message *m) {
+ if (!m)
+ return NULL;
+
+ if (!sd_bus_error_is_set(&m->error))
+ return NULL;
+
+ return &m->error;
+}
+
+int sd_bus_message_get_uid(sd_bus_message *m, uid_t *uid) {
+ if (!m)
+ return -EINVAL;
+ if (!uid)
+ return -EINVAL;
+ if (!m->uid_valid)
+ return -ESRCH;
+
+ *uid = m->uid;
+ return 0;
+}
+
+int sd_bus_message_get_gid(sd_bus_message *m, gid_t *gid) {
+ if (!m)
+ return -EINVAL;
+ if (!gid)
+ return -EINVAL;
+ if (!m->gid_valid)
+ return -ESRCH;
+
+ *gid = m->gid;
+ return 0;
+}
+
+int sd_bus_message_get_pid(sd_bus_message *m, pid_t *pid) {
+ if (!m)
+ return -EINVAL;
+ if (!pid)
+ return -EINVAL;
+ if (m->pid <= 0)
+ return -ESRCH;
+
+ *pid = m->pid;
+ return 0;
+}
+
+int sd_bus_message_get_tid(sd_bus_message *m, pid_t *tid) {
+ if (!m)
+ return -EINVAL;
+ if (!tid)
+ return -EINVAL;
+ if (m->tid <= 0)
+ return -ESRCH;
+
+ *tid = m->tid;
+ return 0;
+}
+
+int sd_bus_message_get_pid_starttime(sd_bus_message *m, uint64_t *usec) {
+ if (!m)
+ return -EINVAL;
+ if (!usec)
+ return -EINVAL;
+ if (m->pid_starttime <= 0)
+ return -ESRCH;
+
+ *usec = m->pid_starttime;
+ return 0;
+}
+
+int sd_bus_message_get_selinux_context(sd_bus_message *m, const char **ret) {
+ if (!m)
+ return -EINVAL;
+ if (!m->label)
+ return -ESRCH;
+
+ *ret = m->label;
+ return 0;
+}
+
+int sd_bus_message_get_monotonic_timestamp(sd_bus_message *m, uint64_t *usec) {
+ if (!m)
+ return -EINVAL;
+ if (!usec)
+ return -EINVAL;
+ if (m->monotonic <= 0)
+ return -ESRCH;
+
+ *usec = m->monotonic;
+ return 0;
+}
+
+int sd_bus_message_get_realtime_timestamp(sd_bus_message *m, uint64_t *usec) {
+ if (!m)
+ return -EINVAL;
+ if (!usec)
+ return -EINVAL;
+ if (m->realtime <= 0)
+ return -ESRCH;
+
+ *usec = m->realtime;
+ return 0;
+}
+
+int sd_bus_message_get_comm(sd_bus_message *m, const char **ret) {
+ if (!m)
+ return -EINVAL;
+ if (!ret)
+ return -EINVAL;
+ if (!m->comm)
+ return -ESRCH;
+
+ *ret = m->comm;
+ return 0;
+}
+
+int sd_bus_message_get_tid_comm(sd_bus_message *m, const char **ret) {
+ if (!m)
+ return -EINVAL;
+ if (!ret)
+ return -EINVAL;
+ if (!m->tid_comm)
+ return -ESRCH;
+
+ *ret = m->tid_comm;
+ return 0;
+}
+
+int sd_bus_message_get_exe(sd_bus_message *m, const char **ret) {
+ if (!m)
+ return -EINVAL;
+ if (!ret)
+ return -EINVAL;
+ if (!m->exe)
+ return -ESRCH;
+
+ *ret = m->exe;
+ return 0;
+}
+
+int sd_bus_message_get_cgroup(sd_bus_message *m, const char **ret) {
+ if (!m)
+ return -EINVAL;
+ if (!ret)
+ return -EINVAL;
+ if (!m->cgroup)
+ return -ESRCH;
+
+ *ret = m->cgroup;
+ return 0;
+}
+
+int sd_bus_message_get_unit(sd_bus_message *m, const char **ret) {
+ int r;
+
+ if (!m)
+ return -EINVAL;
+ if (!ret)
+ return -EINVAL;
+ if (!m->cgroup)
+ return -ESRCH;
+
+ if (!m->unit) {
+ r = cg_path_get_unit(m->cgroup, &m->unit);
+ if (r < 0)
+ return r;
+ }
+
+ *ret = m->unit;
+ return 0;
+}
+
+int sd_bus_message_get_user_unit(sd_bus_message *m, const char **ret) {
+ int r;
+
+ if (!m)
+ return -EINVAL;
+ if (!ret)
+ return -EINVAL;
+ if (!m->cgroup)
+ return -ESRCH;
+
+ if (!m->user_unit) {
+ r = cg_path_get_user_unit(m->cgroup, &m->user_unit);
+ if (r < 0)
+ return r;
+ }
+
+ *ret = m->user_unit;
+ return 0;
+}
+
+int sd_bus_message_get_session(sd_bus_message *m, const char **ret) {
+ int r;
+
+ if (!m)
+ return -EINVAL;
+ if (!ret)
+ return -EINVAL;
+ if (!m->cgroup)
+ return -ESRCH;
+
+ if (!m->session) {
+ r = cg_path_get_session(m->cgroup, &m->session);
+ if (r < 0)
+ return r;
+ }
+
+ *ret = m->session;
+ return 0;
+}
+
+int sd_bus_message_get_owner_uid(sd_bus_message *m, uid_t *uid) {
+ if (!m)
+ return -EINVAL;
+ if (!uid)
+ return -EINVAL;
+ if (!m->cgroup)
+ return -ESRCH;
+
+ return cg_path_get_owner_uid(m->cgroup, uid);
+}
+
+int sd_bus_message_get_cmdline(sd_bus_message *m, char ***cmdline) {
+ size_t n, i;
+ const char *p;
+ bool first;
+
+ if (!m)
+ return -EINVAL;
+
+ if (!m->cmdline)
+ return -ENOENT;
+
+ for (p = m->cmdline, n = 0; p < m->cmdline + m->cmdline_length; p++)
+ if (*p == 0)
+ n++;
+
+ m->cmdline_array = new(char*, n + 1);
+ if (!m->cmdline_array)
+ return -ENOMEM;
+
+ for (p = m->cmdline, i = 0, first = true; p < m->cmdline + m->cmdline_length; p++) {
+ if (first)
+ m->cmdline_array[i++] = (char*) p;
+
+ first = *p == 0;
+ }
+
+ m->cmdline_array[i] = NULL;
+ *cmdline = m->cmdline_array;
+
+ return 0;
+}
+
+int sd_bus_message_get_audit_sessionid(sd_bus_message *m, uint32_t *sessionid) {
+ if (!m)
+ return -EINVAL;
+ if (!sessionid)
+ return -EINVAL;
+ if (!m->audit)
+ return -ESRCH;
+
+ *sessionid = m->audit->sessionid;
+ return 0;
+}
+
+int sd_bus_message_get_audit_loginuid(sd_bus_message *m, uid_t *uid) {
+ if (!m)
+ return -EINVAL;
+ if (!uid)
+ return -EINVAL;
+ if (!m->audit)
+ return -ESRCH;
+
+ *uid = m->audit->loginuid;
+ return 0;
+}
+
+int sd_bus_message_has_effective_cap(sd_bus_message *m, int capability) {
+ unsigned sz;
+
+ if (!m)
+ return -EINVAL;
+ if (capability < 0)
+ return -EINVAL;
+ if (!m->capability)
+ return -ESRCH;
+
+ sz = m->capability_size / 4;
+ if ((unsigned) capability >= sz*8)
+ return 0;
+
+ return !!(m->capability[2 * sz + (capability / 8)] & (1 << (capability % 8)));
+}
+
+int sd_bus_message_is_signal(sd_bus_message *m, const char *interface, const char *member) {
+ if (!m)
+ return -EINVAL;
+
+ if (m->header->type != SD_BUS_MESSAGE_TYPE_SIGNAL)
+ return 0;
+
+ if (interface && (!m->interface || !streq(m->interface, interface)))
+ return 0;
+
+ if (member && (!m->member || !streq(m->member, member)))
+ return 0;
+
+ return 1;
+}
+
+int sd_bus_message_is_method_call(sd_bus_message *m, const char *interface, const char *member) {
+ if (!m)
+ return -EINVAL;
+
+ if (m->header->type != SD_BUS_MESSAGE_TYPE_METHOD_CALL)
+ return 0;
+
+ if (interface && (!m->interface || !streq(m->interface, interface)))
+ return 0;
+
+ if (member && (!m->member || !streq(m->member, member)))
+ return 0;
+
+ return 1;
+}
+
+int sd_bus_message_is_method_error(sd_bus_message *m, const char *name) {
+ if (!m)
+ return -EINVAL;
+
+ if (m->header->type != SD_BUS_MESSAGE_TYPE_METHOD_ERROR)
+ return 0;
+
+ if (name && (!m->error.name || !streq(m->error.name, name)))
+ return 0;
+
+ return 1;
+}
+
+int sd_bus_message_set_no_reply(sd_bus_message *m, int b) {
+ if (!m)
+ return -EINVAL;
+ if (m->sealed)
+ return -EPERM;
+ if (m->header->type != SD_BUS_MESSAGE_TYPE_METHOD_CALL)
+ return -EPERM;
+
+ if (b)
+ m->header->flags |= SD_BUS_MESSAGE_NO_REPLY_EXPECTED;
+ else
+ m->header->flags &= ~SD_BUS_MESSAGE_NO_REPLY_EXPECTED;
+
+ return 0;
+}
+
+static struct bus_container *message_get_container(sd_bus_message *m) {
+ assert(m);
+
+ if (m->n_containers == 0)
+ return &m->root_container;
+
+ assert(m->containers);
+ return m->containers + m->n_containers - 1;
+}
+
+static void *message_extend_body(sd_bus_message *m, size_t align, size_t sz) {
+ void *p, *o;
+ size_t added;
+ struct bus_container *c;
+
+ assert(m);
+ assert(align > 0);
+
+ o = m->body;
+ added = m->header->body_size;
+
+ p = buffer_extend(&m->body, &m->header->body_size, align, sz);
+ if (!p)
+ return NULL;
+
+ added = m->header->body_size - added;
+
+ for (c = m->containers; c < m->containers + m->n_containers; c++)
+ if (c->array_size) {
+ c->array_size = (uint32_t*) ((uint8_t*) m->body + ((uint8_t*) c->array_size - (uint8_t*) o));
+ *c->array_size += added;
+ }
+
+ if (o != m->body) {
+ if (m->error.message)
+ m->error.message = (const char*) m->body + (m->error.message - (const char*) o);
+ }
+
+ m->free_body = true;
+
+ return p;
+}
+
+int message_append_basic(sd_bus_message *m, char type, const void *p, const void **stored) {
+ struct bus_container *c;
+ ssize_t align, sz;
+ uint32_t k;
+ void *a;
+ char *e = NULL;
+ int fd = -1;
+ uint32_t fdi = 0;
+ int r;
+
+ if (!m)
+ return -EINVAL;
+ if (!p)
+ return -EINVAL;
+ if (m->sealed)
+ return -EPERM;
+ if (!bus_type_is_basic(type))
+ return -EINVAL;
+
+ c = message_get_container(m);
+
+ if (c->signature && c->signature[c->index]) {
+ /* Container signature is already set */
+
+ if (c->signature[c->index] != type)
+ return -ENXIO;
+ } else {
+ /* Maybe we can append to the signature? But only if this is the top-level container*/
+ if (c->enclosing != 0)
+ return -ENXIO;
+
+ e = strextend(&c->signature, CHAR_TO_STR(type), NULL);
+ if (!e)
+ return -ENOMEM;
+ }
+
+ switch (type) {
+
+ case SD_BUS_TYPE_STRING:
+ case SD_BUS_TYPE_OBJECT_PATH:
+
+ align = 4;
+ sz = 4 + strlen(p) + 1;
+ break;
+
+ case SD_BUS_TYPE_SIGNATURE:
+
+ align = 1;
+ sz = 1 + strlen(p) + 1;
+ break;
+
+ case SD_BUS_TYPE_BOOLEAN:
+ align = sz = 4;
+
+ assert_cc(sizeof(int) == sizeof(uint32_t));
+ memcpy(&k, p, 4);
+ k = !!k;
+ p = &k;
+ break;
+
+ case SD_BUS_TYPE_UNIX_FD: {
+ int z, *f;
+
+ if (!m->allow_fds) {
+ r = -ENOTSUP;
+ goto fail;
+ }
+
+ align = sz = 4;
+
+ z = *(int*) p;
+ if (z < 0) {
+ r = -EINVAL;
+ goto fail;
+ }
+
+ fd = fcntl(z, F_DUPFD_CLOEXEC, 3);
+ if (fd < 0) {
+ r = -errno;
+ goto fail;
+ }
+
+ f = realloc(m->fds, sizeof(int) * (m->n_fds + 1));
+ if (!f) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ fdi = m->n_fds;
+ f[fdi] = fd;
+ m->fds = f;
+ m->free_fds = true;
+ break;
+ }
+
+ default:
+ align = bus_type_get_alignment(type);
+ sz = bus_type_get_size(type);
+ break;
+ }
+
+ assert(align > 0);
+ assert(sz > 0);
+
+ a = message_extend_body(m, align, sz);
+ if (!a) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ if (type == SD_BUS_TYPE_STRING || type == SD_BUS_TYPE_OBJECT_PATH) {
+ *(uint32_t*) a = sz - 5;
+ memcpy((uint8_t*) a + 4, p, sz - 4);
+
+ if (stored)
+ *stored = (const uint8_t*) a + 4;
+
+ } else if (type == SD_BUS_TYPE_SIGNATURE) {
+ *(uint8_t*) a = sz - 1;
+ memcpy((uint8_t*) a + 1, p, sz - 1);
+
+ if (stored)
+ *stored = (const uint8_t*) a + 1;
+ } else if (type == SD_BUS_TYPE_UNIX_FD) {
+ *(uint32_t*) a = fdi;
+
+ if (stored)
+ *stored = a;
+
+ m->n_fds ++;
+
+ } else {
+ memcpy(a, p, sz);
+
+ if (stored)
+ *stored = a;
+ }
+
+ if (c->enclosing != SD_BUS_TYPE_ARRAY)
+ c->index++;
+
+ return 0;
+
+fail:
+ /* Truncate extended signature again */
+ if (e)
+ c->signature[c->index] = 0;
+
+ if (fd >= 0)
+ close_nointr_nofail(fd);
+
+ return r;
+}
+
+int sd_bus_message_append_basic(sd_bus_message *m, char type, const void *p) {
+ return message_append_basic(m, type, p, NULL);
+}
+
+static int bus_message_open_array(
+ sd_bus_message *m,
+ struct bus_container *c,
+ const char *contents,
+ uint32_t **array_size) {
+
+ unsigned nindex;
+ char *e = NULL;
+ void *a, *b;
+ int alignment;
+ size_t saved;
+
+ assert(m);
+ assert(c);
+ assert(contents);
+ assert(array_size);
+
+ if (!signature_is_single(contents))
+ return -EINVAL;
+
+ alignment = bus_type_get_alignment(contents[0]);
+ if (alignment < 0)
+ return alignment;
+
+ if (c->signature && c->signature[c->index]) {
+
+ /* Verify the existing signature */
+
+ if (c->signature[c->index] != SD_BUS_TYPE_ARRAY)
+ return -ENXIO;
+
+ if (!startswith(c->signature + c->index + 1, contents))
+ return -ENXIO;
+
+ nindex = c->index + 1 + strlen(contents);
+ } else {
+ if (c->enclosing != 0)
+ return -ENXIO;
+
+ /* Extend the existing signature */
+
+ e = strextend(&c->signature, CHAR_TO_STR(SD_BUS_TYPE_ARRAY), contents, NULL);
+ if (!e)
+ return -ENOMEM;
+
+ nindex = e - c->signature;
+ }
+
+ saved = m->header->body_size;
+ a = message_extend_body(m, 4, 4);
+ if (!a) {
+ /* Truncate extended signature again */
+ if (e)
+ c->signature[c->index] = 0;
+
+ return -ENOMEM;
+ }
+ b = m->body;
+
+ if (!message_extend_body(m, alignment, 0)) {
+ /* Add alignment between size and first element */
+ if (e)
+ c->signature[c->index] = 0;
+
+ m->header->body_size = saved;
+ return -ENOMEM;
+ }
+
+ if (c->enclosing != SD_BUS_TYPE_ARRAY)
+ c->index = nindex;
+
+ /* m->body might have changed so let's readjust a */
+ a = (uint8_t*) m->body + ((uint8_t*) a - (uint8_t*) b);
+ *(uint32_t*) a = 0;
+
+ *array_size = a;
+ return 0;
+}
+
+static int bus_message_open_variant(
+ sd_bus_message *m,
+ struct bus_container *c,
+ const char *contents) {
+
+ char *e = NULL;
+ size_t l;
+ void *a;
+
+ assert(m);
+ assert(c);
+ assert(contents);
+
+ if (!signature_is_single(contents))
+ return -EINVAL;
+
+ if (*contents == SD_BUS_TYPE_DICT_ENTRY_BEGIN)
+ return -EINVAL;
+
+ if (c->signature && c->signature[c->index]) {
+
+ if (c->signature[c->index] != SD_BUS_TYPE_VARIANT)
+ return -ENXIO;
+
+ } else {
+ if (c->enclosing != 0)
+ return -ENXIO;
+
+ e = strextend(&c->signature, CHAR_TO_STR(SD_BUS_TYPE_VARIANT), NULL);
+ if (!e)
+ return -ENOMEM;
+ }
+
+ l = strlen(contents);
+ a = message_extend_body(m, 1, 1 + l + 1);
+ if (!a) {
+ /* Truncate extended signature again */
+ if (e)
+ c->signature[c->index] = 0;
+
+ return -ENOMEM;
+ }
+
+ *(uint8_t*) a = l;
+ memcpy((uint8_t*) a + 1, contents, l + 1);
+
+ if (c->enclosing != SD_BUS_TYPE_ARRAY)
+ c->index++;
+
+ return 0;
+}
+
+static int bus_message_open_struct(
+ sd_bus_message *m,
+ struct bus_container *c,
+ const char *contents) {
+
+ size_t nindex;
+ char *e = NULL;
+
+ assert(m);
+ assert(c);
+ assert(contents);
+
+ if (!signature_is_valid(contents, false))
+ return -EINVAL;
+
+ if (c->signature && c->signature[c->index]) {
+ size_t l;
+
+ l = strlen(contents);
+
+ if (c->signature[c->index] != SD_BUS_TYPE_STRUCT_BEGIN ||
+ !startswith(c->signature + c->index + 1, contents) ||
+ c->signature[c->index + 1 + l] != SD_BUS_TYPE_STRUCT_END)
+ return -ENXIO;
+
+ nindex = c->index + 1 + l + 1;
+ } else {
+ if (c->enclosing != 0)
+ return -ENXIO;
+
+ e = strextend(&c->signature, CHAR_TO_STR(SD_BUS_TYPE_STRUCT_BEGIN), contents, CHAR_TO_STR(SD_BUS_TYPE_STRUCT_END), NULL);
+ if (!e)
+ return -ENOMEM;
+
+ nindex = e - c->signature;
+ }
+
+ /* Align contents to 8 byte boundary */
+ if (!message_extend_body(m, 8, 0)) {
+ if (e)
+ c->signature[c->index] = 0;
+
+ return -ENOMEM;
+ }
+
+ if (c->enclosing != SD_BUS_TYPE_ARRAY)
+ c->index = nindex;
+
+ return 0;
+}
+
+static int bus_message_open_dict_entry(
+ sd_bus_message *m,
+ struct bus_container *c,
+ const char *contents) {
+
+ size_t nindex;
+
+ assert(m);
+ assert(c);
+ assert(contents);
+
+ if (!signature_is_pair(contents))
+ return -EINVAL;
+
+ if (c->enclosing != SD_BUS_TYPE_ARRAY)
+ return -ENXIO;
+
+ if (c->signature && c->signature[c->index]) {
+ size_t l;
+
+ l = strlen(contents);
+
+ if (c->signature[c->index] != SD_BUS_TYPE_DICT_ENTRY_BEGIN ||
+ !startswith(c->signature + c->index + 1, contents) ||
+ c->signature[c->index + 1 + l] != SD_BUS_TYPE_DICT_ENTRY_END)
+ return -ENXIO;
+
+ nindex = c->index + 1 + l + 1;
+ } else
+ return -ENXIO;
+
+ /* Align contents to 8 byte boundary */
+ if (!message_extend_body(m, 8, 0))
+ return -ENOMEM;
+
+ if (c->enclosing != SD_BUS_TYPE_ARRAY)
+ c->index = nindex;
+
+ return 0;
+}
+
+int sd_bus_message_open_container(
+ sd_bus_message *m,
+ char type,
+ const char *contents) {
+
+ struct bus_container *c, *w;
+ uint32_t *array_size = NULL;
+ char *signature;
+ int r;
+
+ if (!m)
+ return -EINVAL;
+ if (m->sealed)
+ return -EPERM;
+ if (!contents)
+ return -EINVAL;
+
+ /* Make sure we have space for one more container */
+ w = realloc(m->containers, sizeof(struct bus_container) * (m->n_containers + 1));
+ if (!w)
+ return -ENOMEM;
+ m->containers = w;
+
+ c = message_get_container(m);
+
+ signature = strdup(contents);
+ if (!signature)
+ return -ENOMEM;
+
+ if (type == SD_BUS_TYPE_ARRAY)
+ r = bus_message_open_array(m, c, contents, &array_size);
+ else if (type == SD_BUS_TYPE_VARIANT)
+ r = bus_message_open_variant(m, c, contents);
+ else if (type == SD_BUS_TYPE_STRUCT)
+ r = bus_message_open_struct(m, c, contents);
+ else if (type == SD_BUS_TYPE_DICT_ENTRY)
+ r = bus_message_open_dict_entry(m, c, contents);
+ else
+ r = -EINVAL;
+
+ if (r < 0) {
+ free(signature);
+ return r;
+ }
+
+ /* OK, let's fill it in */
+ w += m->n_containers++;
+ w->enclosing = type;
+ w->signature = signature;
+ w->index = 0;
+ w->array_size = array_size;
+ w->begin = 0;
+
+ return 0;
+}
+
+int sd_bus_message_close_container(sd_bus_message *m) {
+ struct bus_container *c;
+
+ if (!m)
+ return -EINVAL;
+ if (m->sealed)
+ return -EPERM;
+ if (m->n_containers <= 0)
+ return -EINVAL;
+
+ c = message_get_container(m);
+ if (c->enclosing != SD_BUS_TYPE_ARRAY)
+ if (c->signature && c->signature[c->index] != 0)
+ return -EINVAL;
+
+ free(c->signature);
+ m->n_containers--;
+
+ return 0;
+}
+
+
+typedef struct {
+ const char *types;
+ unsigned n_struct;
+ unsigned n_array;
+} TypeStack;
+
+static int type_stack_push(TypeStack *stack, unsigned max, unsigned *i, const char *types, unsigned n_struct, unsigned n_array) {
+ assert(stack);
+ assert(max > 0);
+
+ if (*i >= max)
+ return -EINVAL;
+
+ stack[*i].types = types;
+ stack[*i].n_struct = n_struct;
+ stack[*i].n_array = n_array;
+ (*i)++;
+
+ return 0;
+}
+
+static int type_stack_pop(TypeStack *stack, unsigned max, unsigned *i, const char **types, unsigned *n_struct, unsigned *n_array) {
+ assert(stack);
+ assert(max > 0);
+ assert(types);
+ assert(n_struct);
+ assert(n_array);
+
+ if (*i <= 0)
+ return 0;
+
+ (*i)--;
+ *types = stack[*i].types;
+ *n_struct = stack[*i].n_struct;
+ *n_array = stack[*i].n_array;
+
+ return 1;
+}
+
+int bus_message_append_ap(
+ sd_bus_message *m,
+ const char *types,
+ va_list ap) {
+
+ unsigned n_array, n_struct;
+ TypeStack stack[BUS_CONTAINER_DEPTH];
+ unsigned stack_ptr = 0;
+ int r;
+
+ assert(m);
+
+ if (!types)
+ return 0;
+
+ n_array = (unsigned) -1;
+ n_struct = strlen(types);
+
+ for (;;) {
+ const char *t;
+
+ if (n_array == 0 || (n_array == (unsigned) -1 && n_struct == 0)) {
+ r = type_stack_pop(stack, ELEMENTSOF(stack), &stack_ptr, &types, &n_struct, &n_array);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ break;
+
+ r = sd_bus_message_close_container(m);
+ if (r < 0)
+ return r;
+
+ continue;
+ }
+
+ t = types;
+ if (n_array != (unsigned) -1)
+ n_array --;
+ else {
+ types ++;
+ n_struct--;
+ }
+
+ switch (*t) {
+
+ case SD_BUS_TYPE_BYTE: {
+ uint8_t x;
+
+ x = (uint8_t) va_arg(ap, int);
+ r = sd_bus_message_append_basic(m, *t, &x);
+ break;
+ }
+
+ case SD_BUS_TYPE_BOOLEAN:
+ case SD_BUS_TYPE_INT32:
+ case SD_BUS_TYPE_UINT32:
+ case SD_BUS_TYPE_UNIX_FD: {
+ uint32_t x;
+
+ /* We assume a boolean is the same as int32_t */
+ assert_cc(sizeof(int32_t) == sizeof(int));
+
+ x = va_arg(ap, uint32_t);
+ r = sd_bus_message_append_basic(m, *t, &x);
+ break;
+ }
+
+ case SD_BUS_TYPE_INT16:
+ case SD_BUS_TYPE_UINT16: {
+ uint16_t x;
+
+ x = (uint16_t) va_arg(ap, int);
+ r = sd_bus_message_append_basic(m, *t, &x);
+ break;
+ }
+
+ case SD_BUS_TYPE_INT64:
+ case SD_BUS_TYPE_UINT64:
+ case SD_BUS_TYPE_DOUBLE: {
+ uint64_t x;
+
+ x = va_arg(ap, uint64_t);
+ r = sd_bus_message_append_basic(m, *t, &x);
+ break;
+ }
+
+ case SD_BUS_TYPE_STRING:
+ case SD_BUS_TYPE_OBJECT_PATH:
+ case SD_BUS_TYPE_SIGNATURE: {
+ const char *x;
+
+ x = va_arg(ap, const char*);
+ r = sd_bus_message_append_basic(m, *t, x);
+ break;
+ }
+
+ case SD_BUS_TYPE_ARRAY: {
+ size_t k;
+
+ r = signature_element_length(t + 1, &k);
+ if (r < 0)
+ return r;
+
+ {
+ char s[k + 1];
+ memcpy(s, t + 1, k);
+ s[k] = 0;
+
+ r = sd_bus_message_open_container(m, SD_BUS_TYPE_ARRAY, s);
+ if (r < 0)
+ return r;
+ }
+
+ if (n_array == (unsigned) -1) {
+ types += k;
+ n_struct -= k;
+ }
+
+ r = type_stack_push(stack, ELEMENTSOF(stack), &stack_ptr, types, n_struct, n_array);
+ if (r < 0)
+ return r;
+
+ types = t + 1;
+ n_struct = k;
+ n_array = va_arg(ap, unsigned);
+
+ break;
+ }
+
+ case SD_BUS_TYPE_VARIANT: {
+ const char *s;
+
+ s = va_arg(ap, const char*);
+ if (!s)
+ return -EINVAL;
+
+ r = sd_bus_message_open_container(m, SD_BUS_TYPE_VARIANT, s);
+ if (r < 0)
+ return r;
+
+ r = type_stack_push(stack, ELEMENTSOF(stack), &stack_ptr, types, n_struct, n_array);
+ if (r < 0)
+ return r;
+
+ types = s;
+ n_struct = strlen(s);
+ n_array = (unsigned) -1;
+
+ break;
+ }
+
+ case SD_BUS_TYPE_STRUCT_BEGIN:
+ case SD_BUS_TYPE_DICT_ENTRY_BEGIN: {
+ size_t k;
+
+ r = signature_element_length(t, &k);
+ if (r < 0)
+ return r;
+
+ {
+ char s[k - 1];
+
+ memcpy(s, t + 1, k - 2);
+ s[k - 2] = 0;
+
+ r = sd_bus_message_open_container(m, *t == SD_BUS_TYPE_STRUCT_BEGIN ? SD_BUS_TYPE_STRUCT : SD_BUS_TYPE_DICT_ENTRY, s);
+ if (r < 0)
+ return r;
+ }
+
+ if (n_array == (unsigned) -1) {
+ types += k - 1;
+ n_struct -= k - 1;
+ }
+
+ r = type_stack_push(stack, ELEMENTSOF(stack), &stack_ptr, types, n_struct, n_array);
+ if (r < 0)
+ return r;
+
+ types = t + 1;
+ n_struct = k - 2;
+ n_array = (unsigned) -1;
+
+ break;
+ }
+
+ default:
+ r = -EINVAL;
+ }
+
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+int sd_bus_message_append(sd_bus_message *m, const char *types, ...) {
+ va_list ap;
+ int r;
+
+ if (!m)
+ return -EINVAL;
+ if (m->sealed)
+ return -EPERM;
+ if (!types)
+ return 0;
+
+ va_start(ap, types);
+ r = bus_message_append_ap(m, types, ap);
+ va_end(ap);
+
+ return r;
+}
+
+static int buffer_peek(const void *p, uint32_t sz, size_t *rindex, size_t align, size_t nbytes, void **r) {
+ size_t k, start, n;
+
+ assert(rindex);
+ assert(align > 0);
+
+ start = ALIGN_TO((size_t) *rindex, align);
+ n = start + nbytes;
+
+ if (n > sz)
+ return -EBADMSG;
+
+ /* Verify that padding is 0 */
+ for (k = *rindex; k < start; k++)
+ if (((const uint8_t*) p)[k] != 0)
+ return -EBADMSG;
+
+ if (r)
+ *r = (uint8_t*) p + start;
+
+ *rindex = n;
+
+ return 1;
+}
+
+static bool message_end_of_array(sd_bus_message *m, size_t index) {
+ struct bus_container *c;
+
+ assert(m);
+
+ c = message_get_container(m);
+ if (!c->array_size)
+ return false;
+
+ return index >= c->begin + BUS_MESSAGE_BSWAP32(m, *c->array_size);
+}
+
+static int message_peek_body(sd_bus_message *m, size_t *rindex, size_t align, size_t nbytes, void **ret) {
+ assert(m);
+ assert(rindex);
+ assert(align > 0);
+
+ if (message_end_of_array(m, *rindex))
+ return 0;
+
+ return buffer_peek(m->body, BUS_MESSAGE_BODY_SIZE(m), rindex, align, nbytes, ret);
+}
+
+static bool validate_nul(const char *s, size_t l) {
+
+ /* Check for NUL chars in the string */
+ if (memchr(s, 0, l))
+ return false;
+
+ /* Check for NUL termination */
+ if (s[l] != 0)
+ return false;
+
+ return true;
+}
+
+static bool validate_string(const char *s, size_t l) {
+
+ if (!validate_nul(s, l))
+ return false;
+
+ /* Check if valid UTF8 */
+ if (!utf8_is_valid(s))
+ return false;
+
+ return true;
+}
+
+static bool validate_signature(const char *s, size_t l) {
+
+ if (!validate_nul(s, l))
+ return false;
+
+ /* Check if valid signature */
+ if (!signature_is_valid(s, true))
+ return false;
+
+ return true;
+}
+
+static bool validate_object_path(const char *s, size_t l) {
+
+ if (!validate_nul(s, l))
+ return false;
+
+ if (!object_path_is_valid(s))
+ return false;
+
+ return true;
+}
+
+int sd_bus_message_read_basic(sd_bus_message *m, char type, void *p) {
+ struct bus_container *c;
+ int r;
+ void *q;
+
+ if (!m)
+ return -EINVAL;
+ if (!m->sealed)
+ return -EPERM;
+ if (!bus_type_is_basic(type))
+ return -EINVAL;
+ if (!p)
+ return -EINVAL;
+
+ c = message_get_container(m);
+
+ if (!c->signature || c->signature[c->index] == 0)
+ return 0;
+
+ if (c->signature[c->index] != type)
+ return -ENXIO;
+
+ switch (type) {
+
+ case SD_BUS_TYPE_STRING:
+ case SD_BUS_TYPE_OBJECT_PATH: {
+ uint32_t l;
+ size_t rindex;
+
+ rindex = m->rindex;
+ r = message_peek_body(m, &rindex, 4, 4, &q);
+ if (r <= 0)
+ return r;
+
+ l = BUS_MESSAGE_BSWAP32(m, *(uint32_t*) q);
+ r = message_peek_body(m, &rindex, 1, l+1, &q);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -EBADMSG;
+
+ if (type == SD_BUS_TYPE_OBJECT_PATH) {
+ if (!validate_object_path(q, l))
+ return -EBADMSG;
+ } else {
+ if (!validate_string(q, l))
+ return -EBADMSG;
+ }
+
+ m->rindex = rindex;
+ *(const char**) p = q;
+ break;
+ }
+
+ case SD_BUS_TYPE_SIGNATURE: {
+ uint8_t l;
+ size_t rindex;
+
+ rindex = m->rindex;
+ r = message_peek_body(m, &rindex, 1, 1, &q);
+ if (r <= 0)
+ return r;
+
+ l = *(uint8_t*) q;
+ r = message_peek_body(m, &rindex, 1, l+1, &q);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -EBADMSG;
+
+ if (!validate_signature(q, l))
+ return -EBADMSG;
+
+ m->rindex = rindex;
+ *(const char**) p = q;
+ break;
+ }
+
+ default: {
+ ssize_t sz, align;
+ size_t rindex;
+
+ align = bus_type_get_alignment(type);
+ sz = bus_type_get_size(type);
+ assert(align > 0 && sz > 0);
+
+ rindex = m->rindex;
+ r = message_peek_body(m, &rindex, align, sz, &q);
+ if (r <= 0)
+ return r;
+
+ switch (type) {
+
+ case SD_BUS_TYPE_BYTE:
+ *(uint8_t*) p = *(uint8_t*) q;
+ break;
+
+ case SD_BUS_TYPE_BOOLEAN:
+ *(int*) p = !!*(uint32_t*) q;
+ break;
+
+ case SD_BUS_TYPE_INT16:
+ case SD_BUS_TYPE_UINT16:
+ *(uint16_t*) p = BUS_MESSAGE_BSWAP16(m, *(uint16_t*) q);
+ break;
+
+ case SD_BUS_TYPE_INT32:
+ case SD_BUS_TYPE_UINT32:
+ *(uint32_t*) p = BUS_MESSAGE_BSWAP32(m, *(uint32_t*) q);
+ break;
+
+ case SD_BUS_TYPE_INT64:
+ case SD_BUS_TYPE_UINT64:
+ case SD_BUS_TYPE_DOUBLE:
+ *(uint64_t*) p = BUS_MESSAGE_BSWAP64(m, *(uint64_t*) q);
+ break;
+
+ case SD_BUS_TYPE_UNIX_FD: {
+ uint32_t j;
+
+ j = BUS_MESSAGE_BSWAP32(m, *(uint32_t*) q);
+ if (j >= m->n_fds)
+ return -EBADMSG;
+
+ *(int*) p = m->fds[j];
+ break;
+ }
+
+ default:
+ assert_not_reached("Unknown basic type...");
+ }
+
+ m->rindex = rindex;
+
+ break;
+ }
+ }
+
+ if (c->enclosing != SD_BUS_TYPE_ARRAY)
+ c->index++;
+
+ return 1;
+}
+
+static int bus_message_enter_array(
+ sd_bus_message *m,
+ struct bus_container *c,
+ const char *contents,
+ uint32_t **array_size) {
+
+ size_t rindex;
+ void *q;
+ int r, alignment;
+
+ assert(m);
+ assert(c);
+ assert(contents);
+ assert(array_size);
+
+ if (!signature_is_single(contents))
+ return -EINVAL;
+
+ alignment = bus_type_get_alignment(contents[0]);
+ if (alignment < 0)
+ return alignment;
+
+ if (!c->signature || c->signature[c->index] == 0)
+ return 0;
+
+ if (c->signature[c->index] != SD_BUS_TYPE_ARRAY)
+ return -ENXIO;
+
+ if (!startswith(c->signature + c->index + 1, contents))
+ return -ENXIO;
+
+ rindex = m->rindex;
+ r = message_peek_body(m, &rindex, 4, 4, &q);
+ if (r <= 0)
+ return r;
+
+ if (BUS_MESSAGE_BSWAP32(m, *(uint32_t*) q) > BUS_ARRAY_MAX_SIZE)
+ return -EBADMSG;
+
+ r = message_peek_body(m, &rindex, alignment, 0, NULL);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -EBADMSG;
+
+ if (c->enclosing != SD_BUS_TYPE_ARRAY)
+ c->index += 1 + strlen(contents);
+
+ m->rindex = rindex;
+
+ *array_size = (uint32_t*) q;
+
+ return 1;
+}
+
+static int bus_message_enter_variant(
+ sd_bus_message *m,
+ struct bus_container *c,
+ const char *contents) {
+
+ size_t rindex;
+ uint8_t l;
+ void *q;
+ int r;
+
+ assert(m);
+ assert(c);
+ assert(contents);
+
+ if (!signature_is_single(contents))
+ return -EINVAL;
+
+ if (*contents == SD_BUS_TYPE_DICT_ENTRY_BEGIN)
+ return -EINVAL;
+
+ if (!c->signature || c->signature[c->index] == 0)
+ return 0;
+
+ if (c->signature[c->index] != SD_BUS_TYPE_VARIANT)
+ return -ENXIO;
+
+ rindex = m->rindex;
+ r = message_peek_body(m, &rindex, 1, 1, &q);
+ if (r <= 0)
+ return r;
+
+ l = *(uint8_t*) q;
+ r = message_peek_body(m, &rindex, 1, l+1, &q);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -EBADMSG;
+
+ if (!validate_signature(q, l))
+ return -EBADMSG;
+
+ if (!streq(q, contents))
+ return -ENXIO;
+
+ if (c->enclosing != SD_BUS_TYPE_ARRAY)
+ c->index++;
+
+ m->rindex = rindex;
+
+ return 1;
+}
+
+static int bus_message_enter_struct(
+ sd_bus_message *m,
+ struct bus_container *c,
+ const char *contents) {
+
+ size_t l;
+ int r;
+
+ assert(m);
+ assert(c);
+ assert(contents);
+
+ if (!signature_is_valid(contents, false))
+ return -EINVAL;
+
+ if (!c->signature || c->signature[c->index] == 0)
+ return 0;
+
+ l = strlen(contents);
+
+ if (c->signature[c->index] != SD_BUS_TYPE_STRUCT_BEGIN ||
+ !startswith(c->signature + c->index + 1, contents) ||
+ c->signature[c->index + 1 + l] != SD_BUS_TYPE_STRUCT_END)
+ return -ENXIO;
+
+ r = message_peek_body(m, &m->rindex, 8, 0, NULL);
+ if (r <= 0)
+ return r;
+
+ if (c->enclosing != SD_BUS_TYPE_ARRAY)
+ c->index += 1 + l + 1;
+
+ return 1;
+}
+
+static int bus_message_enter_dict_entry(
+ sd_bus_message *m,
+ struct bus_container *c,
+ const char *contents) {
+
+ size_t l;
+ int r;
+
+ assert(m);
+ assert(c);
+ assert(contents);
+
+ if (!signature_is_pair(contents))
+ return -EINVAL;
+
+ if (c->enclosing != SD_BUS_TYPE_ARRAY)
+ return -ENXIO;
+
+ if (!c->signature || c->signature[c->index] == 0)
+ return 0;
+
+ l = strlen(contents);
+
+ if (c->signature[c->index] != SD_BUS_TYPE_DICT_ENTRY_BEGIN ||
+ !startswith(c->signature + c->index + 1, contents) ||
+ c->signature[c->index + 1 + l] != SD_BUS_TYPE_DICT_ENTRY_END)
+ return -ENXIO;
+
+ r = message_peek_body(m, &m->rindex, 8, 0, NULL);
+ if (r <= 0)
+ return r;
+
+ if (c->enclosing != SD_BUS_TYPE_ARRAY)
+ c->index += 1 + l + 1;
+
+ return 1;
+}
+
+int sd_bus_message_enter_container(sd_bus_message *m, char type, const char *contents) {
+ struct bus_container *c, *w;
+ uint32_t *array_size = NULL;
+ char *signature;
+ int r;
+
+ if (!m)
+ return -EINVAL;
+ if (!m->sealed)
+ return -EPERM;
+ if (!contents)
+ return -EINVAL;
+
+ /*
+ * We enforce a global limit on container depth, that is much
+ * higher than the 32 structs and 32 arrays the specification
+ * mandates. This is simpler to implement for us, and we need
+ * this only to ensure our container array doesn't grow
+ * without bounds. We are happy to return any data from a
+ * message as long as the data itself is valid, even if the
+ * overall message might be not.
+ *
+ * Note that the message signature is validated when
+ * parsing the headers, and that validation does check the
+ * 32/32 limit.
+ *
+ * Note that the specification defines no limits on the depth
+ * of stacked variants, but we do.
+ */
+ if (m->n_containers >= BUS_CONTAINER_DEPTH)
+ return -EBADMSG;
+
+ w = realloc(m->containers, sizeof(struct bus_container) * (m->n_containers + 1));
+ if (!w)
+ return -ENOMEM;
+ m->containers = w;
+
+ c = message_get_container(m);
+
+ if (!c->signature || c->signature[c->index] == 0)
+ return 0;
+
+ signature = strdup(contents);
+ if (!signature)
+ return -ENOMEM;
+
+ if (type == SD_BUS_TYPE_ARRAY)
+ r = bus_message_enter_array(m, c, contents, &array_size);
+ else if (type == SD_BUS_TYPE_VARIANT)
+ r = bus_message_enter_variant(m, c, contents);
+ else if (type == SD_BUS_TYPE_STRUCT)
+ r = bus_message_enter_struct(m, c, contents);
+ else if (type == SD_BUS_TYPE_DICT_ENTRY)
+ r = bus_message_enter_dict_entry(m, c, contents);
+ else
+ r = -EINVAL;
+
+ if (r <= 0) {
+ free(signature);
+ return r;
+ }
+
+ /* OK, let's fill it in */
+ w += m->n_containers++;
+ w->enclosing = type;
+ w->signature = signature;
+ w->index = 0;
+ w->array_size = array_size;
+ w->begin = m->rindex;
+
+ return 1;
+}
+
+int sd_bus_message_exit_container(sd_bus_message *m) {
+ struct bus_container *c;
+
+ if (!m)
+ return -EINVAL;
+ if (!m->sealed)
+ return -EPERM;
+ if (m->n_containers <= 0)
+ return -EINVAL;
+
+ c = message_get_container(m);
+ if (c->enclosing == SD_BUS_TYPE_ARRAY) {
+ uint32_t l;
+
+ l = BUS_MESSAGE_BSWAP32(m, *c->array_size);
+ if (c->begin + l != m->rindex)
+ return -EBUSY;
+
+ } else {
+ if (c->signature && c->signature[c->index] != 0)
+ return -EINVAL;
+ }
+
+ free(c->signature);
+ m->n_containers--;
+
+ return 1;
+}
+
+int sd_bus_message_peek_type(sd_bus_message *m, char *type, const char **contents) {
+ struct bus_container *c;
+ int r;
+
+ if (!m)
+ return -EINVAL;
+ if (!m->sealed)
+ return -EPERM;
+
+ c = message_get_container(m);
+
+ if (!c->signature || c->signature[c->index] == 0)
+ goto eof;
+
+ if (message_end_of_array(m, m->rindex))
+ goto eof;
+
+ if (bus_type_is_basic(c->signature[c->index])) {
+ if (contents)
+ *contents = NULL;
+ if (type)
+ *type = c->signature[c->index];
+ return 1;
+ }
+
+ if (c->signature[c->index] == SD_BUS_TYPE_ARRAY) {
+
+ if (contents) {
+ size_t l;
+ char *sig;
+
+ r = signature_element_length(c->signature+c->index+1, &l);
+ if (r < 0)
+ return r;
+
+ assert(l >= 1);
+
+ sig = strndup(c->signature + c->index + 1, l);
+ if (!sig)
+ return -ENOMEM;
+
+ free(m->peeked_signature);
+ m->peeked_signature = sig;
+
+ *contents = sig;
+ }
+
+ if (type)
+ *type = SD_BUS_TYPE_ARRAY;
+
+ return 1;
+ }
+
+ if (c->signature[c->index] == SD_BUS_TYPE_STRUCT_BEGIN ||
+ c->signature[c->index] == SD_BUS_TYPE_DICT_ENTRY_BEGIN) {
+
+ if (contents) {
+ size_t l;
+ char *sig;
+
+ r = signature_element_length(c->signature+c->index, &l);
+ if (r < 0)
+ return r;
+
+ assert(l >= 2);
+ sig = strndup(c->signature + c->index + 1, l - 2);
+ if (!sig)
+ return -ENOMEM;
+
+ free(m->peeked_signature);
+ m->peeked_signature = sig;
+
+ *contents = sig;
+ }
+
+ if (type)
+ *type = c->signature[c->index] == SD_BUS_TYPE_STRUCT_BEGIN ? SD_BUS_TYPE_STRUCT : SD_BUS_TYPE_DICT_ENTRY;
+
+ return 1;
+ }
+
+ if (c->signature[c->index] == SD_BUS_TYPE_VARIANT) {
+ if (contents) {
+ size_t rindex, l;
+ void *q;
+
+ rindex = m->rindex;
+ r = message_peek_body(m, &rindex, 1, 1, &q);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ goto eof;
+
+ l = *(uint8_t*) q;
+ r = message_peek_body(m, &rindex, 1, l+1, &q);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -EBADMSG;
+
+ if (!validate_signature(q, l))
+ return -EBADMSG;
+
+ *contents = q;
+ }
+
+ if (type)
+ *type = SD_BUS_TYPE_VARIANT;
+
+ return 1;
+ }
+
+ return -EINVAL;
+
+eof:
+ if (type)
+ *type = c->enclosing;
+ if (contents)
+ *contents = NULL;
+ return 0;
+}
+
+int sd_bus_message_rewind(sd_bus_message *m, int complete) {
+ struct bus_container *c;
+
+ if (!m)
+ return -EINVAL;
+ if (!m->sealed)
+ return -EPERM;
+
+ if (complete) {
+ reset_containers(m);
+ m->rindex = 0;
+ m->root_container.index = 0;
+
+ c = message_get_container(m);
+ } else {
+ c = message_get_container(m);
+
+ c->index = 0;
+ m->rindex = c->begin;
+ }
+
+ return !isempty(c->signature);
+}
+static int message_read_ap(
+ sd_bus_message *m,
+ const char *types,
+ va_list ap) {
+
+ unsigned n_array, n_struct;
+ TypeStack stack[BUS_CONTAINER_DEPTH];
+ unsigned stack_ptr = 0;
+ int r;
+
+ assert(m);
+
+ if (!types)
+ return 0;
+
+ /* Ideally, we'd just call ourselves recursively on every
+ * complex type. However, the state of a va_list that is
+ * passed to a function is undefined after that function
+ * returns. This means we need to docode the va_list linearly
+ * in a single stackframe. We hence implement our own
+ * home-grown stack in an array. */
+
+ n_array = (unsigned) -1;
+ n_struct = strlen(types);
+
+ for (;;) {
+ const char *t;
+
+ if (n_array == 0 || (n_array == (unsigned) -1 && n_struct == 0)) {
+ r = type_stack_pop(stack, ELEMENTSOF(stack), &stack_ptr, &types, &n_struct, &n_array);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ break;
+
+ r = sd_bus_message_exit_container(m);
+ if (r < 0)
+ return r;
+
+ continue;
+ }
+
+ t = types;
+ if (n_array != (unsigned) -1)
+ n_array --;
+ else {
+ types ++;
+ n_struct--;
+ }
+
+ switch (*t) {
+
+ case SD_BUS_TYPE_BYTE:
+ case SD_BUS_TYPE_BOOLEAN:
+ case SD_BUS_TYPE_INT16:
+ case SD_BUS_TYPE_UINT16:
+ case SD_BUS_TYPE_INT32:
+ case SD_BUS_TYPE_UINT32:
+ case SD_BUS_TYPE_INT64:
+ case SD_BUS_TYPE_UINT64:
+ case SD_BUS_TYPE_DOUBLE:
+ case SD_BUS_TYPE_STRING:
+ case SD_BUS_TYPE_OBJECT_PATH:
+ case SD_BUS_TYPE_SIGNATURE:
+ case SD_BUS_TYPE_UNIX_FD: {
+ void *p;
+
+ p = va_arg(ap, void*);
+ r = sd_bus_message_read_basic(m, *t, p);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -ENXIO;
+
+ break;
+ }
+
+ case SD_BUS_TYPE_ARRAY: {
+ size_t k;
+
+ r = signature_element_length(t + 1, &k);
+ if (r < 0)
+ return r;
+
+ {
+ char s[k + 1];
+ memcpy(s, t + 1, k);
+ s[k] = 0;
+
+ r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, s);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -ENXIO;
+ }
+
+ if (n_array == (unsigned) -1) {
+ types += k;
+ n_struct -= k;
+ }
+
+ r = type_stack_push(stack, ELEMENTSOF(stack), &stack_ptr, types, n_struct, n_array);
+ if (r < 0)
+ return r;
+
+ types = t + 1;
+ n_struct = k;
+ n_array = va_arg(ap, unsigned);
+
+ break;
+ }
+
+ case SD_BUS_TYPE_VARIANT: {
+ const char *s;
+
+ s = va_arg(ap, const char *);
+ if (!s)
+ return -EINVAL;
+
+ r = sd_bus_message_enter_container(m, SD_BUS_TYPE_VARIANT, s);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -ENXIO;
+
+ r = type_stack_push(stack, ELEMENTSOF(stack), &stack_ptr, types, n_struct, n_array);
+ if (r < 0)
+ return r;
+
+ types = s;
+ n_struct = strlen(s);
+ n_array = (unsigned) -1;
+
+ break;
+ }
+
+ case SD_BUS_TYPE_STRUCT_BEGIN:
+ case SD_BUS_TYPE_DICT_ENTRY_BEGIN: {
+ size_t k;
+
+ r = signature_element_length(t, &k);
+ if (r < 0)
+ return r;
+
+ {
+ char s[k - 1];
+ memcpy(s, t + 1, k - 2);
+ s[k - 2] = 0;
+
+ r = sd_bus_message_enter_container(m, *t == SD_BUS_TYPE_STRUCT_BEGIN ? SD_BUS_TYPE_STRUCT : SD_BUS_TYPE_DICT_ENTRY, s);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -ENXIO;
+ }
+
+ if (n_array == (unsigned) -1) {
+ types += k - 1;
+ n_struct -= k - 1;
+ }
+
+ r = type_stack_push(stack, ELEMENTSOF(stack), &stack_ptr, types, n_struct, n_array);
+ if (r < 0)
+ return r;
+
+ types = t + 1;
+ n_struct = k - 2;
+ n_array = (unsigned) -1;
+
+ break;
+ }
+
+ default:
+ return -EINVAL;
+ }
+ }
+
+ return 1;
+}
+
+int sd_bus_message_read(sd_bus_message *m, const char *types, ...) {
+ va_list ap;
+ int r;
+
+ if (!m)
+ return -EINVAL;
+ if (!m->sealed)
+ return -EPERM;
+ if (!types)
+ return -EINVAL;
+
+ va_start(ap, types);
+ r = message_read_ap(m, types, ap);
+ va_end(ap);
+
+ return r;
+}
+
+static int message_peek_fields(
+ sd_bus_message *m,
+ size_t *rindex,
+ size_t align,
+ size_t nbytes,
+ void **ret) {
+
+ assert(m);
+ assert(rindex);
+ assert(align > 0);
+
+ return buffer_peek(m->fields, BUS_MESSAGE_FIELDS_SIZE(m), rindex, align, nbytes, ret);
+}
+
+static int message_peek_field_uint32(
+ sd_bus_message *m,
+ size_t *ri,
+ uint32_t *ret) {
+
+ int r;
+ void *q;
+
+ assert(m);
+ assert(ri);
+
+ r = message_peek_fields(m, ri, 4, 4, &q);
+ if (r < 0)
+ return r;
+
+ if (ret)
+ *ret = BUS_MESSAGE_BSWAP32(m, *(uint32_t*) q);
+
+ return 0;
+}
+
+static int message_peek_field_string(
+ sd_bus_message *m,
+ bool (*validate)(const char *p),
+ size_t *ri,
+ const char **ret) {
+
+ uint32_t l;
+ int r;
+ void *q;
+
+ assert(m);
+ assert(ri);
+
+ r = message_peek_field_uint32(m, ri, &l);
+ if (r < 0)
+ return r;
+
+ r = message_peek_fields(m, ri, 1, l+1, &q);
+ if (r < 0)
+ return r;
+
+ if (validate) {
+ if (!validate_nul(q, l))
+ return -EBADMSG;
+
+ if (!validate(q))
+ return -EBADMSG;
+ } else {
+ if (!validate_string(q, l))
+ return -EBADMSG;
+ }
+
+ if (ret)
+ *ret = q;
+
+ return 0;
+}
+
+static int message_peek_field_signature(
+ sd_bus_message *m,
+ size_t *ri,
+ const char **ret) {
+
+ size_t l;
+ int r;
+ void *q;
+
+ assert(m);
+ assert(ri);
+
+ r = message_peek_fields(m, ri, 1, 1, &q);
+ if (r < 0)
+ return r;
+
+ l = *(uint8_t*) q;
+ r = message_peek_fields(m, ri, 1, l+1, &q);
+ if (r < 0)
+ return r;
+
+ if (!validate_signature(q, l))
+ return -EBADMSG;
+
+ if (ret)
+ *ret = q;
+
+ return 0;
+}
+
+static int message_skip_fields(
+ sd_bus_message *m,
+ size_t *ri,
+ uint32_t array_size,
+ const char **signature) {
+
+ size_t original_index;
+ int r;
+
+ assert(m);
+ assert(ri);
+ assert(signature);
+
+ original_index = *ri;
+
+ for (;;) {
+ char t;
+ size_t l;
+
+ if (array_size != (uint32_t) -1 &&
+ array_size <= *ri - original_index)
+ return 0;
+
+ t = **signature;
+ if (!t)
+ return 0;
+
+ if (t == SD_BUS_TYPE_STRING) {
+
+ r = message_peek_field_string(m, NULL, ri, NULL);
+ if (r < 0)
+ return r;
+
+ (*signature)++;
+
+ } else if (t == SD_BUS_TYPE_OBJECT_PATH) {
+
+ r = message_peek_field_string(m, object_path_is_valid, ri, NULL);
+ if (r < 0)
+ return r;
+
+ (*signature)++;
+
+ } else if (t == SD_BUS_TYPE_SIGNATURE) {
+
+ r = message_peek_field_signature(m, ri, NULL);
+ if (r < 0)
+ return r;
+
+ (*signature)++;
+
+ } else if (bus_type_is_basic(t)) {
+ ssize_t align, k;
+
+ align = bus_type_get_alignment(t);
+ k = bus_type_get_size(t);
+ assert(align > 0 && k > 0);
+
+ r = message_peek_fields(m, ri, align, k, NULL);
+ if (r < 0)
+ return r;
+
+ (*signature)++;
+
+ } else if (t == SD_BUS_TYPE_ARRAY) {
+
+ r = signature_element_length(*signature+1, &l);
+ if (r < 0)
+ return r;
+
+ assert(l >= 1);
+ {
+ char sig[l-1], *s;
+ uint32_t nas;
+ int alignment;
+
+ strncpy(sig, *signature + 1, l-1);
+ s = sig;
+
+ alignment = bus_type_get_alignment(sig[0]);
+ if (alignment < 0)
+ return alignment;
+
+ r = message_peek_field_uint32(m, ri, &nas);
+ if (r < 0)
+ return r;
+ if (nas > BUS_ARRAY_MAX_SIZE)
+ return -EBADMSG;
+
+ r = message_peek_fields(m, ri, alignment, 0, NULL);
+ if (r < 0)
+ return r;
+
+ r = message_skip_fields(m, ri, nas, (const char**) &s);
+ if (r < 0)
+ return r;
+ }
+
+ (*signature) += 1 + l;
+
+ } else if (t == SD_BUS_TYPE_VARIANT) {
+ const char *s;
+
+ r = message_peek_field_signature(m, ri, &s);
+ if (r < 0)
+ return r;
+
+ r = message_skip_fields(m, ri, (uint32_t) -1, (const char**) &s);
+ if (r < 0)
+ return r;
+
+ (*signature)++;
+
+ } else if (t == SD_BUS_TYPE_STRUCT ||
+ t == SD_BUS_TYPE_DICT_ENTRY) {
+
+ r = signature_element_length(*signature, &l);
+ if (r < 0)
+ return r;
+
+ assert(l >= 2);
+ {
+ char sig[l-1], *s;
+ strncpy(sig, *signature + 1, l-1);
+ s = sig;
+
+ r = message_skip_fields(m, ri, (uint32_t) -1, (const char**) &s);
+ if (r < 0)
+ return r;
+ }
+
+ *signature += l;
+ } else
+ return -EINVAL;
+ }
+}
+
+int bus_message_parse_fields(sd_bus_message *m) {
+ size_t ri;
+ int r;
+ uint32_t unix_fds = 0;
+
+ assert(m);
+
+ for (ri = 0; ri < BUS_MESSAGE_FIELDS_SIZE(m); ) {
+ const char *signature;
+ uint8_t *header;
+
+ r = message_peek_fields(m, &ri, 8, 1, (void**) &header);
+ if (r < 0)
+ return r;
+
+ r = message_peek_field_signature(m, &ri, &signature);
+ if (r < 0)
+ return r;
+
+ switch (*header) {
+ case _SD_BUS_MESSAGE_HEADER_INVALID:
+ return -EBADMSG;
+
+ case SD_BUS_MESSAGE_HEADER_PATH:
+
+ if (m->path)
+ return -EBADMSG;
+
+ if (!streq(signature, "o"))
+ return -EBADMSG;
+
+ r = message_peek_field_string(m, object_path_is_valid, &ri, &m->path);
+ break;
+
+ case SD_BUS_MESSAGE_HEADER_INTERFACE:
+
+ if (m->interface)
+ return -EBADMSG;
+
+ if (!streq(signature, "s"))
+ return -EBADMSG;
+
+ r = message_peek_field_string(m, interface_name_is_valid, &ri, &m->interface);
+ break;
+
+ case SD_BUS_MESSAGE_HEADER_MEMBER:
+
+ if (m->member)
+ return -EBADMSG;
+
+ if (!streq(signature, "s"))
+ return -EBADMSG;
+
+ r = message_peek_field_string(m, member_name_is_valid, &ri, &m->member);
+ break;
+
+ case SD_BUS_MESSAGE_HEADER_ERROR_NAME:
+
+ if (m->error.name)
+ return -EBADMSG;
+
+ if (!streq(signature, "s"))
+ return -EBADMSG;
+
+ r = message_peek_field_string(m, error_name_is_valid, &ri, &m->error.name);
+ break;
+
+ case SD_BUS_MESSAGE_HEADER_DESTINATION:
+
+ if (m->destination)
+ return -EBADMSG;
+
+ if (!streq(signature, "s"))
+ return -EBADMSG;
+
+ r = message_peek_field_string(m, service_name_is_valid, &ri, &m->destination);
+ break;
+
+ case SD_BUS_MESSAGE_HEADER_SENDER:
+
+ if (m->sender)
+ return -EBADMSG;
+
+ if (!streq(signature, "s"))
+ return -EBADMSG;
+
+ r = message_peek_field_string(m, service_name_is_valid, &ri, &m->sender);
+ break;
+
+
+ case SD_BUS_MESSAGE_HEADER_SIGNATURE: {
+ const char *s;
+ char *c;
+
+ if (m->root_container.signature)
+ return -EBADMSG;
+
+ if (!streq(signature, "g"))
+ return -EBADMSG;
+
+ r = message_peek_field_signature(m, &ri, &s);
+ if (r < 0)
+ return r;
+
+ c = strdup(s);
+ if (!c)
+ return -ENOMEM;
+
+ free(m->root_container.signature);
+ m->root_container.signature = c;
+ break;
+ }
+
+ case SD_BUS_MESSAGE_HEADER_REPLY_SERIAL:
+ if (m->reply_serial != 0)
+ return -EBADMSG;
+
+ if (!streq(signature, "u"))
+ return -EBADMSG;
+
+ r = message_peek_field_uint32(m, &ri, &m->reply_serial);
+ if (r < 0)
+ return r;
+
+ if (m->reply_serial == 0)
+ return -EBADMSG;
+
+ break;
+
+ case SD_BUS_MESSAGE_HEADER_UNIX_FDS:
+ if (unix_fds != 0)
+ return -EBADMSG;
+
+ if (!streq(signature, "u"))
+ return -EBADMSG;
+
+ r = message_peek_field_uint32(m, &ri, &unix_fds);
+ if (r < 0)
+ return -EBADMSG;
+
+ if (unix_fds == 0)
+ return -EBADMSG;
+
+ break;
+
+ default:
+ r = message_skip_fields(m, &ri, (uint32_t) -1, (const char **) &signature);
+ }
+
+ if (r < 0)
+ return r;
+ }
+
+ if (m->n_fds != unix_fds)
+ return -EBADMSG;
+
+ if (isempty(m->root_container.signature) != (BUS_MESSAGE_BODY_SIZE(m) == 0))
+ return -EBADMSG;
+
+ switch (m->header->type) {
+
+ case SD_BUS_MESSAGE_TYPE_SIGNAL:
+ if (!m->path || !m->interface || !m->member)
+ return -EBADMSG;
+ break;
+
+ case SD_BUS_MESSAGE_TYPE_METHOD_CALL:
+
+ if (!m->path || !m->member)
+ return -EBADMSG;
+
+ break;
+
+ case SD_BUS_MESSAGE_TYPE_METHOD_RETURN:
+
+ if (m->reply_serial == 0)
+ return -EBADMSG;
+ break;
+
+ case SD_BUS_MESSAGE_TYPE_METHOD_ERROR:
+
+ if (m->reply_serial == 0 || !m->error.name)
+ return -EBADMSG;
+ break;
+ }
+
+ /* Try to read the error message, but if we can't it's a non-issue */
+ if (m->header->type == SD_BUS_MESSAGE_TYPE_METHOD_ERROR)
+ sd_bus_message_read(m, "s", &m->error.message);
+
+ return 0;
+}
+
+int bus_message_seal(sd_bus_message *m, uint64_t serial) {
+ int r;
+ size_t l, a;
+
+ assert(m);
+
+ if (m->sealed)
+ return -EPERM;
+
+ if (m->n_containers > 0)
+ return -EBADMSG;
+
+ /* If there's a non-trivial signature set, then add it in here */
+ if (!isempty(m->root_container.signature)) {
+ r = message_append_field_signature(m, SD_BUS_MESSAGE_HEADER_SIGNATURE, m->root_container.signature, NULL);
+ if (r < 0)
+ return r;
+ }
+
+ if (m->n_fds > 0) {
+ r = message_append_field_uint32(m, SD_BUS_MESSAGE_HEADER_UNIX_FDS, m->n_fds);
+ if (r < 0)
+ return r;
+ }
+
+ l = BUS_MESSAGE_FIELDS_SIZE(m);
+ a = ALIGN8(l) - l;
+
+ if (a > 0) {
+ /* Add padding at the end, since we know the body
+ * needs to start at an 8 byte alignment. */
+ void *p;
+
+ p = message_extend_fields(m, 1, a);
+ if (!p)
+ return -ENOMEM;
+
+ memset(p, 0, a);
+ m->header->fields_size -= a;
+ }
+
+ m->header->serial = serial;
+ m->sealed = true;
+
+ return 0;
+}
+
+int sd_bus_message_set_destination(sd_bus_message *m, const char *destination) {
+ if (!m)
+ return -EINVAL;
+ if (!destination)
+ return -EINVAL;
+ if (m->sealed)
+ return -EPERM;
+ if (m->destination)
+ return -EEXIST;
+
+ return message_append_field_string(m, SD_BUS_MESSAGE_HEADER_DESTINATION, SD_BUS_TYPE_STRING, destination, &m->destination);
+}
+
+int bus_message_dump(sd_bus_message *m) {
+ const char *u = NULL, *uu = NULL, *s = NULL;
+ char **cmdline = NULL;
+ unsigned level = 1;
+ int r;
+ uid_t owner, audit_loginuid;
+ uint32_t audit_sessionid;
+
+ assert(m);
+
+ printf("Message %p\n"
+ "\tn_ref=%u\n"
+ "\tendian=%c\n"
+ "\ttype=%i\n"
+ "\tflags=%u\n"
+ "\tversion=%u\n"
+ "\tserial=%u\n"
+ "\tfields_size=%u\n"
+ "\tbody_size=%u\n"
+ "\tpath=%s\n"
+ "\tinterface=%s\n"
+ "\tmember=%s\n"
+ "\tdestination=%s\n"
+ "\tsender=%s\n"
+ "\tsignature=%s\n"
+ "\treply_serial=%u\n"
+ "\terror.name=%s\n"
+ "\terror.message=%s\n"
+ "\tsealed=%s\n",
+ m,
+ m->n_ref,
+ m->header->endian,
+ m->header->type,
+ m->header->flags,
+ m->header->version,
+ BUS_MESSAGE_SERIAL(m),
+ BUS_MESSAGE_FIELDS_SIZE(m),
+ BUS_MESSAGE_BODY_SIZE(m),
+ strna(m->path),
+ strna(m->interface),
+ strna(m->member),
+ strna(m->destination),
+ strna(m->sender),
+ strna(m->root_container.signature),
+ m->reply_serial,
+ strna(m->error.name),
+ strna(m->error.message),
+ yes_no(m->sealed));
+
+ if (m->pid != 0)
+ printf("\tpid=%lu\n", (unsigned long) m->pid);
+ if (m->tid != 0)
+ printf("\ttid=%lu\n", (unsigned long) m->tid);
+ if (m->uid_valid)
+ printf("\tuid=%lu\n", (unsigned long) m->uid);
+ if (m->gid_valid)
+ printf("\tgid=%lu\n", (unsigned long) m->gid);
+ if (m->pid_starttime != 0)
+ printf("\tpid_starttime=%llu\n", (unsigned long long) m->pid_starttime);
+ if (m->monotonic != 0)
+ printf("\tmonotonic=%llu\n", (unsigned long long) m->monotonic);
+ if (m->realtime != 0)
+ printf("\trealtime=%llu\n", (unsigned long long) m->realtime);
+ if (m->exe)
+ printf("\texe=[%s]\n", m->exe);
+ if (m->comm)
+ printf("\tcomm=[%s]\n", m->comm);
+ if (m->tid_comm)
+ printf("\ttid_comm=[%s]\n", m->tid_comm);
+ if (m->label)
+ printf("\tlabel=[%s]\n", m->label);
+ if (m->cgroup)
+ printf("\tcgroup=[%s]\n", m->cgroup);
+
+ sd_bus_message_get_unit(m, &u);
+ if (u)
+ printf("\tunit=[%s]\n", u);
+ sd_bus_message_get_user_unit(m, &uu);
+ if (uu)
+ printf("\tuser_unit=[%s]\n", uu);
+ sd_bus_message_get_session(m, &s);
+ if (s)
+ printf("\tsession=[%s]\n", s);
+ if (sd_bus_message_get_owner_uid(m, &owner) >= 0)
+ printf("\towner_uid=%lu\n", (unsigned long) owner);
+ if (sd_bus_message_get_audit_loginuid(m, &audit_loginuid) >= 0)
+ printf("\taudit_loginuid=%lu\n", (unsigned long) audit_loginuid);
+ if (sd_bus_message_get_audit_sessionid(m, &audit_sessionid) >= 0)
+ printf("\taudit_sessionid=%lu\n", (unsigned long) audit_sessionid);
+
+ printf("\tCAP_KILL=%i\n", sd_bus_message_has_effective_cap(m, 5));
+
+ if (sd_bus_message_get_cmdline(m, &cmdline) >= 0) {
+ char **c;
+
+ fputs("\tcmdline=[", stdout);
+ STRV_FOREACH(c, cmdline) {
+ if (c != cmdline)
+ putchar(' ');
+
+ fputs(*c, stdout);
+ }
+
+ fputs("]\n", stdout);
+ }
+
+ r = sd_bus_message_rewind(m, true);
+ if (r < 0) {
+ log_error("Failed to rewind: %s", strerror(-r));
+ return r;
+ }
+
+ printf("BEGIN_MESSAGE \"%s\" {\n", strempty(m->root_container.signature));
+
+ for(;;) {
+ _cleanup_free_ char *prefix = NULL;
+ const char *contents = NULL;
+ char type;
+ union {
+ uint8_t u8;
+ uint16_t u16;
+ int16_t s16;
+ uint32_t u32;
+ int32_t s32;
+ uint64_t u64;
+ int64_t s64;
+ double d64;
+ const char *string;
+ int i;
+ } basic;
+
+ r = sd_bus_message_peek_type(m, &type, &contents);
+ if (r < 0) {
+ log_error("Failed to peek type: %s", strerror(-r));
+ return r;
+ }
+ if (r == 0) {
+ if (level <= 1)
+ break;
+
+ r = sd_bus_message_exit_container(m);
+ if (r < 0) {
+ log_error("Failed to exit container: %s", strerror(-r));
+ return r;
+ }
+
+ level--;
+
+ prefix = strrep("\t", level);
+ if (!prefix)
+ return log_oom();
+
+ if (type == SD_BUS_TYPE_ARRAY)
+ printf("%s} END_ARRAY \n", prefix);
+ else if (type == SD_BUS_TYPE_VARIANT)
+ printf("%s} END_VARIANT\n", prefix);
+ else if (type == SD_BUS_TYPE_STRUCT)
+ printf("%s} END_STRUCT\n", prefix);
+ else if (type == SD_BUS_TYPE_DICT_ENTRY)
+ printf("%s} END_DICT_ENTRY\n", prefix);
+
+ continue;
+ }
+
+ prefix = strrep("\t", level);
+ if (!prefix)
+ return log_oom();
+
+ if (bus_type_is_container(type) > 0) {
+ r = sd_bus_message_enter_container(m, type, contents);
+ if (r < 0) {
+ log_error("Failed to enter container: %s", strerror(-r));
+ return r;
+ }
+
+ if (type == SD_BUS_TYPE_ARRAY)
+ printf("%sBEGIN_ARRAY \"%s\" {\n", prefix, contents);
+ else if (type == SD_BUS_TYPE_VARIANT)
+ printf("%sBEGIN_VARIANT \"%s\" {\n", prefix, contents);
+ else if (type == SD_BUS_TYPE_STRUCT)
+ printf("%sBEGIN_STRUCT \"%s\" {\n", prefix, contents);
+ else if (type == SD_BUS_TYPE_DICT_ENTRY)
+ printf("%sBEGIN_DICT_ENTRY \"%s\" {\n", prefix, contents);
+
+ level ++;
+
+ continue;
+ }
+
+ r = sd_bus_message_read_basic(m, type, &basic);
+ if (r < 0) {
+ log_error("Failed to get basic: %s", strerror(-r));
+ return r;
+ }
+
+ switch (type) {
+
+ case SD_BUS_TYPE_BYTE:
+ printf("%sBYTE: %u\n", prefix, basic.u8);
+ break;
+
+ case SD_BUS_TYPE_BOOLEAN:
+ printf("%sBOOLEAN: %s\n", prefix, yes_no(basic.i));
+ break;
+
+ case SD_BUS_TYPE_INT16:
+ printf("%sINT16: %i\n", prefix, basic.s16);
+ break;
+
+ case SD_BUS_TYPE_UINT16:
+ printf("%sUINT16: %u\n", prefix, basic.u16);
+ break;
+
+ case SD_BUS_TYPE_INT32:
+ printf("%sINT32: %i\n", prefix, basic.s32);
+ break;
+
+ case SD_BUS_TYPE_UINT32:
+ printf("%sUINT32: %u\n", prefix, basic.u32);
+ break;
+
+ case SD_BUS_TYPE_INT64:
+ printf("%sINT64: %lli\n", prefix, (long long) basic.s64);
+ break;
+
+ case SD_BUS_TYPE_UINT64:
+ printf("%sUINT64: %llu\n", prefix, (unsigned long long) basic.u64);
+ break;
+
+ case SD_BUS_TYPE_DOUBLE:
+ printf("%sDOUBLE: %g\n", prefix, basic.d64);
+ break;
+
+ case SD_BUS_TYPE_STRING:
+ printf("%sSTRING: \"%s\"\n", prefix, basic.string);
+ break;
+
+ case SD_BUS_TYPE_OBJECT_PATH:
+ printf("%sOBJECT_PATH: \"%s\"\n", prefix, basic.string);
+ break;
+
+ case SD_BUS_TYPE_SIGNATURE:
+ printf("%sSIGNATURE: \"%s\"\n", prefix, basic.string);
+ break;
+
+ case SD_BUS_TYPE_UNIX_FD:
+ printf("%sUNIX_FD: %i\n", prefix, basic.i);
+ break;
+
+ default:
+ assert_not_reached("Unknown basic type.");
+ }
+ }
+
+ printf("} END_MESSAGE\n");
+ return 0;
+}
+
+int bus_message_get_blob(sd_bus_message *m, void **buffer, size_t *sz) {
+ size_t total;
+ void *p, *e;
+
+ assert(m);
+ assert(buffer);
+ assert(sz);
+
+ total = BUS_MESSAGE_SIZE(m);
+
+ p = malloc(total);
+ if (!p)
+ return -ENOMEM;
+
+ e = mempcpy(p, m->header, sizeof(*m->header));
+
+ if (m->fields) {
+ e = mempcpy(e, m->fields, m->header->fields_size);
+
+ if (m->header->fields_size % 8 != 0)
+ e = mempset(e, 0, 8 - (m->header->fields_size % 8));
+ }
+
+ if (m->body)
+ e = mempcpy(e, m->body, m->header->body_size);
+
+ assert(total == (size_t) ((uint8_t*) e - (uint8_t*) p));
+
+ *buffer = p;
+ *sz = total;
+
+ return 0;
+}
+
+int bus_message_read_strv_extend(sd_bus_message *m, char ***l) {
+ int r;
+
+ assert(m);
+ assert(l);
+
+ r = sd_bus_message_enter_container(m, 'a', "s");
+ if (r < 0)
+ return r;
+
+ for (;;) {
+ const char *s;
+
+ r = sd_bus_message_read_basic(m, 's', &s);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ break;
+
+ r = strv_extend(l, s);
+ if (r < 0)
+ return r;
+ }
+
+ r = sd_bus_message_exit_container(m);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+const char* bus_message_get_arg(sd_bus_message *m, unsigned i) {
+ int r;
+ const char *t = NULL;
+ unsigned j;
+
+ assert(m);
+
+ r = sd_bus_message_rewind(m, true);
+ if (r < 0)
+ return NULL;
+
+ for (j = 0; j <= i; j++) {
+ char type;
+
+ r = sd_bus_message_peek_type(m, &type, NULL);
+ if (r < 0)
+ return NULL;
+
+ if (type != SD_BUS_TYPE_STRING &&
+ type != SD_BUS_TYPE_OBJECT_PATH &&
+ type != SD_BUS_TYPE_SIGNATURE)
+ return NULL;
+
+ r = sd_bus_message_read_basic(m, type, &t);
+ if (r < 0)
+ return NULL;
+ }
+
+ return t;
+}
+
+int bus_header_size(struct bus_header *h, size_t *sum) {
+ size_t fs, bs;
+
+ assert(h);
+ assert(sum);
+
+ if (h->endian == SD_BUS_NATIVE_ENDIAN) {
+ fs = h->fields_size;
+ bs = h->body_size;
+ } else if (h->endian == SD_BUS_REVERSE_ENDIAN) {
+ fs = bswap_32(h->fields_size);
+ bs = bswap_32(h->body_size);
+ } else
+ return -EBADMSG;
+
+ *sum = sizeof(struct bus_header) + ALIGN8(fs) + bs;
+ return 0;
+}
diff --git a/src/libsystemd-bus/bus-message.h b/src/libsystemd-bus/bus-message.h
new file mode 100644
index 0000000000..9c0829c7fa
--- /dev/null
+++ b/src/libsystemd-bus/bus-message.h
@@ -0,0 +1,198 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ 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 <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdbool.h>
+#include <byteswap.h>
+#include <sys/socket.h>
+
+#include "macro.h"
+#include "sd-bus.h"
+#include "kdbus.h"
+#include "time-util.h"
+
+struct bus_container {
+ char enclosing;
+
+ char *signature;
+ unsigned index;
+
+ uint32_t *array_size;
+ size_t begin;
+};
+
+struct bus_header {
+ uint8_t endian;
+ uint8_t type;
+ uint8_t flags;
+ uint8_t version;
+ uint32_t body_size;
+ uint32_t serial;
+ uint32_t fields_size;
+} _packed_;
+
+struct sd_bus_message {
+ unsigned n_ref;
+
+ uint32_t reply_serial;
+
+ const char *path;
+ const char *interface;
+ const char *member;
+ const char *destination;
+ const char *sender;
+
+ sd_bus_error error;
+
+ uid_t uid;
+ gid_t gid;
+ pid_t pid;
+ pid_t tid;
+ usec_t pid_starttime;
+ usec_t monotonic;
+ usec_t realtime;
+
+ bool sealed:1;
+ bool dont_send:1;
+ bool allow_fds:1;
+ bool uid_valid:1;
+ bool gid_valid:1;
+ bool free_header:1;
+ bool free_fields:1;
+ bool free_body:1;
+ bool free_kdbus:1;
+ bool free_fds:1;
+
+ struct bus_header *header;
+ void *fields;
+ void *body;
+ struct kdbus_msg *kdbus;
+
+ char *label;
+
+ size_t rindex;
+
+ uint32_t n_fds;
+ int *fds;
+
+ struct bus_container root_container, *containers;
+ unsigned n_containers;
+
+ struct iovec iovec[3];
+ unsigned n_iovec;
+
+ char *peeked_signature;
+
+ usec_t timeout;
+
+ char sender_buffer[3 + DECIMAL_STR_MAX(uint64_t) + 1];
+ char destination_buffer[3 + DECIMAL_STR_MAX(uint64_t) + 1];
+
+ const char *exe;
+ const char *comm;
+ const char *tid_comm;
+ const char *cgroup;
+
+ const char *cmdline;
+ size_t cmdline_length;
+ char **cmdline_array;
+
+ char *session;
+ char *unit;
+ char *user_unit;
+
+ struct kdbus_audit *audit;
+
+ uint8_t *capability;
+ size_t capability_size;
+};
+
+#define BUS_MESSAGE_NEED_BSWAP(m) ((m)->header->endian != SD_BUS_NATIVE_ENDIAN)
+
+static inline uint16_t BUS_MESSAGE_BSWAP16(sd_bus_message *m, uint16_t u) {
+ return BUS_MESSAGE_NEED_BSWAP(m) ? bswap_16(u) : u;
+}
+
+static inline uint32_t BUS_MESSAGE_BSWAP32(sd_bus_message *m, uint32_t u) {
+ return BUS_MESSAGE_NEED_BSWAP(m) ? bswap_32(u) : u;
+}
+
+static inline uint64_t BUS_MESSAGE_BSWAP64(sd_bus_message *m, uint64_t u) {
+ return BUS_MESSAGE_NEED_BSWAP(m) ? bswap_64(u) : u;
+}
+
+static inline uint32_t BUS_MESSAGE_SERIAL(sd_bus_message *m) {
+ return BUS_MESSAGE_BSWAP32(m, m->header->serial);
+}
+
+static inline uint32_t BUS_MESSAGE_BODY_SIZE(sd_bus_message *m) {
+ return BUS_MESSAGE_BSWAP32(m, m->header->body_size);
+}
+
+static inline uint32_t BUS_MESSAGE_FIELDS_SIZE(sd_bus_message *m) {
+ return BUS_MESSAGE_BSWAP32(m, m->header->fields_size);
+}
+
+static inline uint32_t BUS_MESSAGE_SIZE(sd_bus_message *m) {
+ return
+ sizeof(struct bus_header) +
+ ALIGN8(BUS_MESSAGE_FIELDS_SIZE(m)) +
+ BUS_MESSAGE_BODY_SIZE(m);
+}
+
+static inline void bus_message_unrefp(sd_bus_message **m) {
+ sd_bus_message_unref(*m);
+}
+
+#define _cleanup_bus_message_unref_ __attribute__((cleanup(bus_message_unrefp)))
+
+int bus_message_seal(sd_bus_message *m, uint64_t serial);
+int bus_message_dump(sd_bus_message *m);
+int bus_message_get_blob(sd_bus_message *m, void **buffer, size_t *sz);
+int bus_message_read_strv_extend(sd_bus_message *m, char ***l);
+
+int bus_message_from_header(
+ void *header,
+ size_t length,
+ int *fds,
+ unsigned n_fds,
+ const struct ucred *ucred,
+ const char *label,
+ size_t extra,
+ sd_bus_message **ret);
+
+int bus_message_from_malloc(
+ void *buffer,
+ size_t length,
+ int *fds,
+ unsigned n_fds,
+ const struct ucred *ucred,
+ const char *label,
+ sd_bus_message **ret);
+
+const char* bus_message_get_arg(sd_bus_message *m, unsigned i);
+
+int bus_message_append_ap(sd_bus_message *m, const char *types, va_list ap);
+
+int bus_message_parse_fields(sd_bus_message *m);
+
+int bus_header_size(struct bus_header *h, size_t *sum);
diff --git a/src/libsystemd-bus/bus-signature.c b/src/libsystemd-bus/bus-signature.c
new file mode 100644
index 0000000000..a92b7124c3
--- /dev/null
+++ b/src/libsystemd-bus/bus-signature.c
@@ -0,0 +1,153 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ 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 <http://www.gnu.org/licenses/>.
+***/
+
+#include <util.h>
+
+#include "bus-signature.h"
+#include "bus-type.h"
+
+static int signature_element_length_internal(
+ const char *s,
+ bool allow_dict_entry,
+ unsigned array_depth,
+ unsigned struct_depth,
+ size_t *l) {
+
+ int r;
+
+ assert(s);
+
+ if (bus_type_is_basic(*s) || *s == SD_BUS_TYPE_VARIANT) {
+ *l = 1;
+ return 0;
+ }
+
+ if (*s == SD_BUS_TYPE_ARRAY) {
+ size_t t;
+
+ if (array_depth >= 32)
+ return -EINVAL;
+
+ r = signature_element_length_internal(s + 1, true, array_depth+1, struct_depth, &t);
+ if (r < 0)
+ return r;
+
+ *l = t + 1;
+ return 0;
+ }
+
+ if (*s == SD_BUS_TYPE_STRUCT_BEGIN) {
+ const char *p = s + 1;
+
+ if (struct_depth >= 32)
+ return -EINVAL;
+
+ while (*p != SD_BUS_TYPE_STRUCT_END) {
+ size_t t;
+
+ r = signature_element_length_internal(p, false, array_depth, struct_depth+1, &t);
+ if (r < 0)
+ return r;
+
+ p += t;
+ }
+
+ *l = p - s + 1;
+ return 0;
+ }
+
+ if (*s == SD_BUS_TYPE_DICT_ENTRY_BEGIN && allow_dict_entry) {
+ const char *p = s + 1;
+ unsigned n = 0;
+
+ if (struct_depth >= 32)
+ return -EINVAL;
+
+ while (*p != SD_BUS_TYPE_DICT_ENTRY_END) {
+ size_t t;
+
+ if (n == 0 && !bus_type_is_basic(*p))
+ return -EINVAL;
+
+ r = signature_element_length_internal(p, false, array_depth, struct_depth+1, &t);
+ if (r < 0)
+ return r;
+
+ p += t;
+ n++;
+ }
+
+ if (n != 2)
+ return -EINVAL;
+
+ *l = p - s + 1;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+
+int signature_element_length(const char *s, size_t *l) {
+ return signature_element_length_internal(s, true, 0, 0, l);
+}
+
+bool signature_is_single(const char *s) {
+ int r;
+ size_t t;
+
+ assert(s);
+
+ r = signature_element_length(s, &t);
+ if (r < 0)
+ return false;
+
+ return s[t] == 0;
+}
+
+bool signature_is_pair(const char *s) {
+ assert(s);
+
+ if (!bus_type_is_basic(*s))
+ return false;
+
+ return signature_is_single(s + 1);
+}
+
+bool signature_is_valid(const char *s, bool allow_dict_entry) {
+ const char *p;
+ int r;
+
+ assert(s);
+
+ p = s;
+ while (*p) {
+ size_t t;
+
+ r = signature_element_length_internal(p, allow_dict_entry, 0, 0, &t);
+ if (r < 0)
+ return false;
+
+ p += t;
+ }
+
+ return p - s <= 255;
+}
diff --git a/src/libsystemd-bus/bus-signature.h b/src/libsystemd-bus/bus-signature.h
new file mode 100644
index 0000000000..4000e0612a
--- /dev/null
+++ b/src/libsystemd-bus/bus-signature.h
@@ -0,0 +1,31 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ 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 <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdbool.h>
+#include <sys/types.h>
+
+bool signature_is_single(const char *s);
+bool signature_is_pair(const char *s);
+bool signature_is_valid(const char *s, bool allow_dict_entry);
+
+int signature_element_length(const char *s, size_t *l);
diff --git a/src/libsystemd-bus/bus-socket.c b/src/libsystemd-bus/bus-socket.c
new file mode 100644
index 0000000000..8a86b02c68
--- /dev/null
+++ b/src/libsystemd-bus/bus-socket.c
@@ -0,0 +1,1059 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ 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 <http://www.gnu.org/licenses/>.
+***/
+
+#include <endian.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/poll.h>
+#include <byteswap.h>
+
+#include "util.h"
+#include "macro.h"
+#include "missing.h"
+#include "strv.h"
+#include "utf8.h"
+#include "sd-daemon.h"
+
+#include "sd-bus.h"
+#include "bus-socket.h"
+#include "bus-internal.h"
+#include "bus-message.h"
+
+static void iovec_advance(struct iovec iov[], unsigned *idx, size_t size) {
+
+ while (size > 0) {
+ struct iovec *i = iov + *idx;
+
+ if (i->iov_len > size) {
+ i->iov_base = (uint8_t*) i->iov_base + size;
+ i->iov_len -= size;
+ return;
+ }
+
+ size -= i->iov_len;
+
+ i->iov_base = NULL;
+ i->iov_len = 0;
+
+ (*idx) ++;
+ }
+}
+
+static void append_iovec(sd_bus_message *m, const void *p, size_t sz) {
+ assert(m);
+ assert(p);
+ assert(sz > 0);
+
+ m->iovec[m->n_iovec].iov_base = (void*) p;
+ m->iovec[m->n_iovec].iov_len = sz;
+ m->n_iovec++;
+}
+
+static void bus_message_setup_iovec(sd_bus_message *m) {
+ assert(m);
+ assert(m->sealed);
+
+ if (m->n_iovec > 0)
+ return;
+
+ append_iovec(m, m->header, sizeof(*m->header));
+
+ if (m->fields)
+ append_iovec(m, m->fields, ALIGN8(m->header->fields_size));
+
+ if (m->body)
+ append_iovec(m, m->body, m->header->body_size);
+}
+
+bool bus_socket_auth_needs_write(sd_bus *b) {
+
+ unsigned i;
+
+ if (b->auth_index >= ELEMENTSOF(b->auth_iovec))
+ return false;
+
+ for (i = b->auth_index; i < ELEMENTSOF(b->auth_iovec); i++) {
+ struct iovec *j = b->auth_iovec + i;
+
+ if (j->iov_len > 0)
+ return true;
+ }
+
+ return false;
+}
+
+static int bus_socket_write_auth(sd_bus *b) {
+ ssize_t k;
+
+ assert(b);
+ assert(b->state == BUS_AUTHENTICATING);
+
+ if (!bus_socket_auth_needs_write(b))
+ return 0;
+
+ if (b->prefer_writev)
+ k = writev(b->output_fd, b->auth_iovec + b->auth_index, ELEMENTSOF(b->auth_iovec) - b->auth_index);
+ else {
+ struct msghdr mh;
+ zero(mh);
+
+ mh.msg_iov = b->auth_iovec + b->auth_index;
+ mh.msg_iovlen = ELEMENTSOF(b->auth_iovec) - b->auth_index;
+
+ k = sendmsg(b->output_fd, &mh, MSG_DONTWAIT|MSG_NOSIGNAL);
+ if (k < 0 && errno == ENOTSOCK) {
+ b->prefer_writev = true;
+ k = writev(b->output_fd, b->auth_iovec + b->auth_index, ELEMENTSOF(b->auth_iovec) - b->auth_index);
+ }
+ }
+
+ if (k < 0)
+ return errno == EAGAIN ? 0 : -errno;
+
+ iovec_advance(b->auth_iovec, &b->auth_index, (size_t) k);
+ return 1;
+}
+
+static int bus_socket_auth_verify_client(sd_bus *b) {
+ char *e, *f, *start;
+ sd_id128_t peer;
+ unsigned i;
+ int r;
+
+ assert(b);
+
+ /* We expect two response lines: "OK" and possibly
+ * "AGREE_UNIX_FD" */
+
+ e = memmem(b->rbuffer, b->rbuffer_size, "\r\n", 2);
+ if (!e)
+ return 0;
+
+ if (b->negotiate_fds) {
+ f = memmem(e + 2, b->rbuffer_size - (e - (char*) b->rbuffer) - 2, "\r\n", 2);
+ if (!f)
+ return 0;
+
+ start = f + 2;
+ } else {
+ f = NULL;
+ start = e + 2;
+ }
+
+ /* Nice! We got all the lines we need. First check the OK
+ * line */
+
+ if (e - (char*) b->rbuffer != 3 + 32)
+ return -EPERM;
+
+ if (memcmp(b->rbuffer, "OK ", 3))
+ return -EPERM;
+
+ b->auth = b->anonymous_auth ? BUS_AUTH_ANONYMOUS : BUS_AUTH_EXTERNAL;
+
+ for (i = 0; i < 32; i += 2) {
+ int x, y;
+
+ x = unhexchar(((char*) b->rbuffer)[3 + i]);
+ y = unhexchar(((char*) b->rbuffer)[3 + i + 1]);
+
+ if (x < 0 || y < 0)
+ return -EINVAL;
+
+ peer.bytes[i/2] = ((uint8_t) x << 4 | (uint8_t) y);
+ }
+
+ if (!sd_id128_equal(b->server_id, SD_ID128_NULL) &&
+ !sd_id128_equal(b->server_id, peer))
+ return -EPERM;
+
+ b->server_id = peer;
+
+ /* And possibly check the second line, too */
+
+ if (f)
+ b->can_fds =
+ (f - e == sizeof("\r\nAGREE_UNIX_FD") - 1) &&
+ memcmp(e + 2, "AGREE_UNIX_FD", sizeof("AGREE_UNIX_FD") - 1) == 0;
+
+ b->rbuffer_size -= (start - (char*) b->rbuffer);
+ memmove(b->rbuffer, start, b->rbuffer_size);
+
+ r = bus_start_running(b);
+ if (r < 0)
+ return r;
+
+ return 1;
+}
+
+static bool line_equals(const char *s, size_t m, const char *line) {
+ size_t l;
+
+ l = strlen(line);
+ if (l != m)
+ return false;
+
+ return memcmp(s, line, l) == 0;
+}
+
+static bool line_begins(const char *s, size_t m, const char *word) {
+ size_t l;
+
+ l = strlen(word);
+ if (m < l)
+ return false;
+
+ if (memcmp(s, word, l) != 0)
+ return false;
+
+ return m == l || (m > l && s[l] == ' ');
+}
+
+static int verify_anonymous_token(sd_bus *b, const char *p, size_t l) {
+ _cleanup_free_ char *token = NULL;
+
+ if (!b->anonymous_auth)
+ return 0;
+
+ if (l <= 0)
+ return 1;
+
+ assert(p[0] == ' ');
+ p++; l--;
+
+ if (l % 2 != 0)
+ return 0;
+ token = unhexmem(p, l);
+ if (!token)
+ return -ENOMEM;
+
+ if (memchr(token, 0, l/2))
+ return 0;
+
+ return !!utf8_is_valid(token);
+}
+
+static int verify_external_token(sd_bus *b, const char *p, size_t l) {
+ _cleanup_free_ char *token = NULL;
+ uid_t u;
+ int r;
+
+ /* We don't do any real authentication here. Instead, we if
+ * the owner of this bus wanted authentication he should have
+ * checked SO_PEERCRED before even creating the bus object. */
+
+ if (!b->anonymous_auth && !b->ucred_valid)
+ return 0;
+
+ if (l <= 0)
+ return 1;
+
+ assert(p[0] == ' ');
+ p++; l--;
+
+ if (l % 2 != 0)
+ return 0;
+
+ token = unhexmem(p, l);
+ if (!token)
+ return -ENOMEM;
+
+ if (memchr(token, 0, l/2))
+ return 0;
+
+ r = parse_uid(token, &u);
+ if (r < 0)
+ return 0;
+
+ /* We ignore the passed value if anonymous authentication is
+ * on anyway. */
+ if (!b->anonymous_auth && u != b->ucred.uid)
+ return 0;
+
+ return 1;
+}
+
+static int bus_socket_auth_write(sd_bus *b, const char *t) {
+ char *p;
+ size_t l;
+
+ assert(b);
+ assert(t);
+
+ /* We only make use of the first iovec */
+ assert(b->auth_index == 0 || b->auth_index == 1);
+
+ l = strlen(t);
+ p = malloc(b->auth_iovec[0].iov_len + l);
+ if (!p)
+ return -ENOMEM;
+
+ memcpy(p, b->auth_iovec[0].iov_base, b->auth_iovec[0].iov_len);
+ memcpy(p + b->auth_iovec[0].iov_len, t, l);
+
+ b->auth_iovec[0].iov_base = p;
+ b->auth_iovec[0].iov_len += l;
+
+ free(b->auth_buffer);
+ b->auth_buffer = p;
+ b->auth_index = 0;
+ return 0;
+}
+
+static int bus_socket_auth_write_ok(sd_bus *b) {
+ char t[3 + 32 + 2 + 1];
+
+ assert(b);
+
+ snprintf(t, sizeof(t), "OK " SD_ID128_FORMAT_STR "\r\n", SD_ID128_FORMAT_VAL(b->server_id));
+ char_array_0(t);
+
+ return bus_socket_auth_write(b, t);
+}
+
+static int bus_socket_auth_verify_server(sd_bus *b) {
+ char *e;
+ const char *line;
+ size_t l;
+ bool processed = false;
+ int r;
+
+ assert(b);
+
+ if (b->rbuffer_size < 1)
+ return 0;
+
+ /* First char must be a NUL byte */
+ if (*(char*) b->rbuffer != 0)
+ return -EIO;
+
+ if (b->rbuffer_size < 3)
+ return 0;
+
+ /* Begin with the first line */
+ if (b->auth_rbegin <= 0)
+ b->auth_rbegin = 1;
+
+ for (;;) {
+ /* Check if line is complete */
+ line = (char*) b->rbuffer + b->auth_rbegin;
+ e = memmem(line, b->rbuffer_size - b->auth_rbegin, "\r\n", 2);
+ if (!e)
+ return processed;
+
+ l = e - line;
+
+ if (line_begins(line, l, "AUTH ANONYMOUS")) {
+
+ r = verify_anonymous_token(b, line + 14, l - 14);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ r = bus_socket_auth_write(b, "REJECTED\r\n");
+ else {
+ b->auth = BUS_AUTH_ANONYMOUS;
+ r = bus_socket_auth_write_ok(b);
+ }
+
+ } else if (line_begins(line, l, "AUTH EXTERNAL")) {
+
+ r = verify_external_token(b, line + 13, l - 13);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ r = bus_socket_auth_write(b, "REJECTED\r\n");
+ else {
+ b->auth = BUS_AUTH_EXTERNAL;
+ r = bus_socket_auth_write_ok(b);
+ }
+
+ } else if (line_begins(line, l, "AUTH"))
+ r = bus_socket_auth_write(b, "REJECTED EXTERNAL ANONYMOUS\r\n");
+ else if (line_equals(line, l, "CANCEL") ||
+ line_begins(line, l, "ERROR")) {
+
+ b->auth = _BUS_AUTH_INVALID;
+ r = bus_socket_auth_write(b, "REJECTED\r\n");
+
+ } else if (line_equals(line, l, "BEGIN")) {
+
+ if (b->auth == _BUS_AUTH_INVALID)
+ r = bus_socket_auth_write(b, "ERROR\r\n");
+ else {
+ /* We can't leave from the auth phase
+ * before we haven't written
+ * everything queued, so let's check
+ * that */
+
+ if (bus_socket_auth_needs_write(b))
+ return 1;
+
+ b->rbuffer_size -= (e + 2 - (char*) b->rbuffer);
+ memmove(b->rbuffer, e + 2, b->rbuffer_size);
+ return bus_start_running(b);
+ }
+
+ } else if (line_begins(line, l, "DATA")) {
+
+ if (b->auth == _BUS_AUTH_INVALID)
+ r = bus_socket_auth_write(b, "ERROR\r\n");
+ else {
+ if (b->auth == BUS_AUTH_ANONYMOUS)
+ r = verify_anonymous_token(b, line + 4, l - 4);
+ else
+ r = verify_external_token(b, line + 4, l - 4);
+
+ if (r < 0)
+ return r;
+ if (r == 0) {
+ b->auth = _BUS_AUTH_INVALID;
+ r = bus_socket_auth_write(b, "REJECTED\r\n");
+ } else
+ r = bus_socket_auth_write_ok(b);
+ }
+ } else if (line_equals(line, l, "NEGOTIATE_UNIX_FD")) {
+ if (b->auth == _BUS_AUTH_INVALID || !b->negotiate_fds)
+ r = bus_socket_auth_write(b, "ERROR\r\n");
+ else {
+ b->can_fds = true;
+ r = bus_socket_auth_write(b, "AGREE_UNIX_FD\r\n");
+ }
+ } else
+ r = bus_socket_auth_write(b, "ERROR\r\n");
+
+ if (r < 0)
+ return r;
+
+ b->auth_rbegin = e + 2 - (char*) b->rbuffer;
+
+ processed = true;
+ }
+}
+
+static int bus_socket_auth_verify(sd_bus *b) {
+ assert(b);
+
+ if (b->is_server)
+ return bus_socket_auth_verify_server(b);
+ else
+ return bus_socket_auth_verify_client(b);
+}
+
+static int bus_socket_read_auth(sd_bus *b) {
+ struct msghdr mh;
+ struct iovec iov;
+ size_t n;
+ ssize_t k;
+ int r;
+ void *p;
+ union {
+ struct cmsghdr cmsghdr;
+ uint8_t buf[CMSG_SPACE(sizeof(int) * BUS_FDS_MAX) +
+ CMSG_SPACE(sizeof(struct ucred)) +
+ CMSG_SPACE(NAME_MAX)]; /*selinux label */
+ } control;
+ struct cmsghdr *cmsg;
+ bool handle_cmsg = false;
+
+ assert(b);
+ assert(b->state == BUS_AUTHENTICATING);
+
+ r = bus_socket_auth_verify(b);
+ if (r != 0)
+ return r;
+
+ n = MAX(256u, b->rbuffer_size * 2);
+
+ if (n > BUS_AUTH_SIZE_MAX)
+ n = BUS_AUTH_SIZE_MAX;
+
+ if (b->rbuffer_size >= n)
+ return -ENOBUFS;
+
+ p = realloc(b->rbuffer, n);
+ if (!p)
+ return -ENOMEM;
+
+ b->rbuffer = p;
+
+ zero(iov);
+ iov.iov_base = (uint8_t*) b->rbuffer + b->rbuffer_size;
+ iov.iov_len = n - b->rbuffer_size;
+
+ if (b->prefer_readv)
+ k = readv(b->input_fd, &iov, 1);
+ else {
+ zero(mh);
+ mh.msg_iov = &iov;
+ mh.msg_iovlen = 1;
+ mh.msg_control = &control;
+ mh.msg_controllen = sizeof(control);
+
+ k = recvmsg(b->input_fd, &mh, MSG_DONTWAIT|MSG_NOSIGNAL|MSG_CMSG_CLOEXEC);
+ if (k < 0 && errno == ENOTSOCK) {
+ b->prefer_readv = true;
+ k = readv(b->input_fd, &iov, 1);
+ } else
+ handle_cmsg = true;
+ }
+ if (k < 0)
+ return errno == EAGAIN ? 0 : -errno;
+ if (k == 0)
+ return -ECONNRESET;
+
+ b->rbuffer_size += k;
+
+ if (handle_cmsg) {
+ for (cmsg = CMSG_FIRSTHDR(&mh); cmsg; cmsg = CMSG_NXTHDR(&mh, cmsg)) {
+ if (cmsg->cmsg_level == SOL_SOCKET &&
+ cmsg->cmsg_type == SCM_RIGHTS) {
+ int j;
+
+ /* Whut? We received fds during the auth
+ * protocol? Somebody is playing games with
+ * us. Close them all, and fail */
+ j = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
+ close_many((int*) CMSG_DATA(cmsg), j);
+ return -EIO;
+
+ } else if (cmsg->cmsg_level == SOL_SOCKET &&
+ cmsg->cmsg_type == SCM_CREDENTIALS &&
+ cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred))) {
+
+ /* Ignore bogus data, which we might
+ * get on socketpair() sockets */
+ if (((struct ucred*) CMSG_DATA(cmsg))->pid != 0) {
+ memcpy(&b->ucred, CMSG_DATA(cmsg), sizeof(struct ucred));
+ b->ucred_valid = true;
+ }
+
+ } else if (cmsg->cmsg_level == SOL_SOCKET &&
+ cmsg->cmsg_type == SCM_SECURITY) {
+
+ size_t l;
+
+ l = cmsg->cmsg_len - CMSG_LEN(0);
+ if (l > 0) {
+ memcpy(&b->label, CMSG_DATA(cmsg), l);
+ b->label[l] = 0;
+ }
+ }
+ }
+ }
+
+ r = bus_socket_auth_verify(b);
+ if (r != 0)
+ return r;
+
+ return 1;
+}
+
+static int bus_socket_setup(sd_bus *b) {
+ int enable;
+ socklen_t l;
+
+ assert(b);
+
+ /* Enable SO_PASSCRED + SO_PASSEC. We try this on any
+ * socket, just in case. */
+ enable = !b->bus_client;
+ setsockopt(b->input_fd, SOL_SOCKET, SO_PASSCRED, &enable, sizeof(enable));
+ setsockopt(b->input_fd, SOL_SOCKET, SO_PASSSEC, &enable, sizeof(enable));
+
+ /* Increase the buffers to a MB */
+ fd_inc_rcvbuf(b->input_fd, 1024*1024);
+ fd_inc_sndbuf(b->output_fd, 1024*1024);
+
+ /* Get the peer for socketpair() sockets */
+ l = sizeof(b->ucred);
+ if (getsockopt(b->input_fd, SOL_SOCKET, SO_PEERCRED, &b->ucred, &l) >= 0 && l >= sizeof(b->ucred))
+ b->ucred_valid = b->ucred.pid > 0;
+
+ return 0;
+}
+
+static int bus_socket_start_auth_client(sd_bus *b) {
+ size_t l;
+ const char *auth_suffix, *auth_prefix;
+
+ assert(b);
+
+ if (b->anonymous_auth) {
+ auth_prefix = "\0AUTH ANONYMOUS ";
+
+ /* For ANONYMOUS auth we send some arbitrary "trace" string */
+ l = 9;
+ b->auth_buffer = hexmem("anonymous", l);
+ } else {
+ char text[20 + 1]; /* enough space for a 64bit integer plus NUL */
+
+ auth_prefix = "\0AUTH EXTERNAL ";
+
+ snprintf(text, sizeof(text), "%lu", (unsigned long) geteuid());
+ char_array_0(text);
+
+ l = strlen(text);
+ b->auth_buffer = hexmem(text, l);
+ }
+
+ if (!b->auth_buffer)
+ return -ENOMEM;
+
+ if (b->negotiate_fds)
+ auth_suffix = "\r\nNEGOTIATE_UNIX_FD\r\nBEGIN\r\n";
+ else
+ auth_suffix = "\r\nBEGIN\r\n";
+
+ b->auth_iovec[0].iov_base = (void*) auth_prefix;
+ b->auth_iovec[0].iov_len = 1 + strlen(auth_prefix + 1);
+ b->auth_iovec[1].iov_base = (void*) b->auth_buffer;
+ b->auth_iovec[1].iov_len = l * 2;
+ b->auth_iovec[2].iov_base = (void*) auth_suffix;
+ b->auth_iovec[2].iov_len = strlen(auth_suffix);
+
+ return bus_socket_write_auth(b);
+}
+
+static int bus_socket_start_auth(sd_bus *b) {
+ assert(b);
+
+ b->state = BUS_AUTHENTICATING;
+ b->auth_timeout = now(CLOCK_MONOTONIC) + BUS_DEFAULT_TIMEOUT;
+
+ if (sd_is_socket(b->input_fd, AF_UNIX, 0, 0) <= 0)
+ b->negotiate_fds = false;
+
+ if (b->output_fd != b->input_fd)
+ if (sd_is_socket(b->output_fd, AF_UNIX, 0, 0) <= 0)
+ b->negotiate_fds = false;
+
+ if (b->is_server)
+ return bus_socket_read_auth(b);
+ else
+ return bus_socket_start_auth_client(b);
+}
+
+int bus_socket_connect(sd_bus *b) {
+ int r;
+
+ assert(b);
+ assert(b->input_fd < 0);
+ assert(b->output_fd < 0);
+ assert(b->sockaddr.sa.sa_family != AF_UNSPEC);
+
+ b->input_fd = socket(b->sockaddr.sa.sa_family, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
+ if (b->input_fd < 0)
+ return -errno;
+
+ b->output_fd = b->input_fd;
+
+ r = bus_socket_setup(b);
+ if (r < 0)
+ return r;
+
+ r = connect(b->input_fd, &b->sockaddr.sa, b->sockaddr_size);
+ if (r < 0) {
+ if (errno == EINPROGRESS)
+ return 1;
+
+ return -errno;
+ }
+
+ return bus_socket_start_auth(b);
+}
+
+int bus_socket_exec(sd_bus *b) {
+ int s[2], r;
+ pid_t pid;
+
+ assert(b);
+ assert(b->input_fd < 0);
+ assert(b->output_fd < 0);
+ assert(b->exec_path);
+
+ r = socketpair(AF_UNIX, SOCK_STREAM|SOCK_NONBLOCK|SOCK_CLOEXEC, 0, s);
+ if (r < 0)
+ return -errno;
+
+ pid = fork();
+ if (pid < 0) {
+ close_pipe(s);
+ return -errno;
+ }
+ if (pid == 0) {
+ /* Child */
+
+ reset_all_signal_handlers();
+
+ close_all_fds(s+1, 1);
+
+ assert_se(dup3(s[1], STDIN_FILENO, 0) == STDIN_FILENO);
+ assert_se(dup3(s[1], STDOUT_FILENO, 0) == STDOUT_FILENO);
+
+ if (s[1] != STDIN_FILENO && s[1] != STDOUT_FILENO)
+ close_nointr_nofail(s[1]);
+
+ fd_cloexec(STDIN_FILENO, false);
+ fd_cloexec(STDOUT_FILENO, false);
+ fd_nonblock(STDIN_FILENO, false);
+ fd_nonblock(STDOUT_FILENO, false);
+
+ if (b->exec_argv)
+ execvp(b->exec_path, b->exec_argv);
+ else {
+ const char *argv[] = { b->exec_path, NULL };
+ execvp(b->exec_path, (char**) argv);
+ }
+
+ _exit(EXIT_FAILURE);
+ }
+
+ close_nointr_nofail(s[1]);
+ b->output_fd = b->input_fd = s[0];
+
+ return bus_socket_start_auth(b);
+}
+
+int bus_socket_take_fd(sd_bus *b) {
+ int r;
+ assert(b);
+
+ r = bus_socket_setup(b);
+ if (r < 0)
+ return r;
+
+ return bus_socket_start_auth(b);
+}
+
+int bus_socket_write_message(sd_bus *bus, sd_bus_message *m, size_t *idx) {
+ struct iovec *iov;
+ ssize_t k;
+ size_t n;
+ unsigned j;
+
+ assert(bus);
+ assert(m);
+ assert(idx);
+ assert(bus->state == BUS_RUNNING || bus->state == BUS_HELLO);
+
+ if (*idx >= BUS_MESSAGE_SIZE(m))
+ return 0;
+
+ bus_message_setup_iovec(m);
+
+ n = m->n_iovec * sizeof(struct iovec);
+ iov = alloca(n);
+ memcpy(iov, m->iovec, n);
+
+ j = 0;
+ iovec_advance(iov, &j, *idx);
+
+ if (bus->prefer_writev)
+ k = writev(bus->output_fd, iov, m->n_iovec);
+ else {
+ struct msghdr mh;
+ zero(mh);
+
+ if (m->n_fds > 0) {
+ struct cmsghdr *control;
+ control = alloca(CMSG_SPACE(sizeof(int) * m->n_fds));
+
+ mh.msg_control = control;
+ control->cmsg_level = SOL_SOCKET;
+ control->cmsg_type = SCM_RIGHTS;
+ mh.msg_controllen = control->cmsg_len = CMSG_LEN(sizeof(int) * m->n_fds);
+ memcpy(CMSG_DATA(control), m->fds, sizeof(int) * m->n_fds);
+ }
+
+ mh.msg_iov = iov;
+ mh.msg_iovlen = m->n_iovec;
+
+ k = sendmsg(bus->output_fd, &mh, MSG_DONTWAIT|MSG_NOSIGNAL);
+ if (k < 0 && errno == ENOTSOCK) {
+ bus->prefer_writev = true;
+ k = writev(bus->output_fd, iov, m->n_iovec);
+ }
+ }
+
+ if (k < 0)
+ return errno == EAGAIN ? 0 : -errno;
+
+ *idx += (size_t) k;
+ return 1;
+}
+
+static int bus_socket_read_message_need(sd_bus *bus, size_t *need) {
+ uint32_t a, b;
+ uint8_t e;
+ uint64_t sum;
+
+ assert(bus);
+ assert(need);
+ assert(bus->state == BUS_RUNNING || bus->state == BUS_HELLO);
+
+ if (bus->rbuffer_size < sizeof(struct bus_header)) {
+ *need = sizeof(struct bus_header) + 8;
+
+ /* Minimum message size:
+ *
+ * Header +
+ *
+ * Method Call: +2 string headers
+ * Signal: +3 string headers
+ * Method Error: +1 string headers
+ * +1 uint32 headers
+ * Method Reply: +1 uint32 headers
+ *
+ * A string header is at least 9 bytes
+ * A uint32 header is at least 8 bytes
+ *
+ * Hence the minimum message size of a valid message
+ * is header + 8 bytes */
+
+ return 0;
+ }
+
+ a = ((const uint32_t*) bus->rbuffer)[1];
+ b = ((const uint32_t*) bus->rbuffer)[3];
+
+ e = ((const uint8_t*) bus->rbuffer)[0];
+ if (e == SD_BUS_LITTLE_ENDIAN) {
+ a = le32toh(a);
+ b = le32toh(b);
+ } else if (e == SD_BUS_BIG_ENDIAN) {
+ a = be32toh(a);
+ b = be32toh(b);
+ } else
+ return -EBADMSG;
+
+ sum = (uint64_t) sizeof(struct bus_header) + (uint64_t) ALIGN_TO(b, 8) + (uint64_t) a;
+ if (sum >= BUS_MESSAGE_SIZE_MAX)
+ return -ENOBUFS;
+
+ *need = (size_t) sum;
+ return 0;
+}
+
+static int bus_socket_make_message(sd_bus *bus, size_t size, sd_bus_message **m) {
+ sd_bus_message *t;
+ void *b;
+ int r;
+
+ assert(bus);
+ assert(m);
+ assert(bus->rbuffer_size >= size);
+ assert(bus->state == BUS_RUNNING || bus->state == BUS_HELLO);
+
+ if (bus->rbuffer_size > size) {
+ b = memdup((const uint8_t*) bus->rbuffer + size,
+ bus->rbuffer_size - size);
+ if (!b)
+ return -ENOMEM;
+ } else
+ b = NULL;
+
+ r = bus_message_from_malloc(bus->rbuffer, size,
+ bus->fds, bus->n_fds,
+ bus->ucred_valid ? &bus->ucred : NULL,
+ bus->label[0] ? bus->label : NULL,
+ &t);
+ if (r < 0) {
+ free(b);
+ return r;
+ }
+
+ bus->rbuffer = b;
+ bus->rbuffer_size -= size;
+
+ bus->fds = NULL;
+ bus->n_fds = 0;
+
+ *m = t;
+ return 1;
+}
+
+int bus_socket_read_message(sd_bus *bus, sd_bus_message **m) {
+ struct msghdr mh;
+ struct iovec iov;
+ ssize_t k;
+ size_t need;
+ int r;
+ void *b;
+ union {
+ struct cmsghdr cmsghdr;
+ uint8_t buf[CMSG_SPACE(sizeof(int) * BUS_FDS_MAX) +
+ CMSG_SPACE(sizeof(struct ucred)) +
+ CMSG_SPACE(NAME_MAX)]; /*selinux label */
+ } control;
+ struct cmsghdr *cmsg;
+ bool handle_cmsg = false;
+
+ assert(bus);
+ assert(m);
+ assert(bus->state == BUS_RUNNING || bus->state == BUS_HELLO);
+
+ r = bus_socket_read_message_need(bus, &need);
+ if (r < 0)
+ return r;
+
+ if (bus->rbuffer_size >= need)
+ return bus_socket_make_message(bus, need, m);
+
+ b = realloc(bus->rbuffer, need);
+ if (!b)
+ return -ENOMEM;
+
+ bus->rbuffer = b;
+
+ zero(iov);
+ iov.iov_base = (uint8_t*) bus->rbuffer + bus->rbuffer_size;
+ iov.iov_len = need - bus->rbuffer_size;
+
+ if (bus->prefer_readv)
+ k = readv(bus->input_fd, &iov, 1);
+ else {
+ zero(mh);
+ mh.msg_iov = &iov;
+ mh.msg_iovlen = 1;
+ mh.msg_control = &control;
+ mh.msg_controllen = sizeof(control);
+
+ k = recvmsg(bus->input_fd, &mh, MSG_DONTWAIT|MSG_NOSIGNAL|MSG_CMSG_CLOEXEC);
+ if (k < 0 && errno == ENOTSOCK) {
+ bus->prefer_readv = true;
+ k = readv(bus->input_fd, &iov, 1);
+ } else
+ handle_cmsg = true;
+ }
+ if (k < 0)
+ return errno == EAGAIN ? 0 : -errno;
+ if (k == 0)
+ return -ECONNRESET;
+
+ bus->rbuffer_size += k;
+
+ if (handle_cmsg) {
+ for (cmsg = CMSG_FIRSTHDR(&mh); cmsg; cmsg = CMSG_NXTHDR(&mh, cmsg)) {
+ if (cmsg->cmsg_level == SOL_SOCKET &&
+ cmsg->cmsg_type == SCM_RIGHTS) {
+ int n, *f;
+
+ n = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
+
+ if (!bus->can_fds) {
+ /* Whut? We received fds but this
+ * isn't actually enabled? Close them,
+ * and fail */
+
+ close_many((int*) CMSG_DATA(cmsg), n);
+ return -EIO;
+ }
+
+ f = realloc(bus->fds, sizeof(int) + (bus->n_fds + n));
+ if (!f) {
+ close_many((int*) CMSG_DATA(cmsg), n);
+ return -ENOMEM;
+ }
+
+ memcpy(f + bus->n_fds, CMSG_DATA(cmsg), n * sizeof(int));
+ bus->fds = f;
+ bus->n_fds += n;
+ } else if (cmsg->cmsg_level == SOL_SOCKET &&
+ cmsg->cmsg_type == SCM_CREDENTIALS &&
+ cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred))) {
+
+ /* Ignore bogus data, which we might
+ * get on socketpair() sockets */
+ if (((struct ucred*) CMSG_DATA(cmsg))->pid != 0) {
+ memcpy(&bus->ucred, CMSG_DATA(cmsg), sizeof(struct ucred));
+ bus->ucred_valid = true;
+ }
+
+ } else if (cmsg->cmsg_level == SOL_SOCKET &&
+ cmsg->cmsg_type == SCM_SECURITY) {
+
+ size_t l;
+ l = cmsg->cmsg_len - CMSG_LEN(0);
+ if (l > 0) {
+ memcpy(&bus->label, CMSG_DATA(cmsg), l);
+ bus->label[l] = 0;
+ }
+ }
+ }
+ }
+
+ r = bus_socket_read_message_need(bus, &need);
+ if (r < 0)
+ return r;
+
+ if (bus->rbuffer_size >= need)
+ return bus_socket_make_message(bus, need, m);
+
+ return 1;
+}
+
+int bus_socket_process_opening(sd_bus *b) {
+ int error = 0;
+ socklen_t slen = sizeof(error);
+ struct pollfd p = {
+ .fd = b->output_fd,
+ .events = POLLOUT,
+ };
+ int r;
+
+ assert(b->state == BUS_OPENING);
+
+ r = poll(&p, 1, 0);
+ if (r < 0)
+ return -errno;
+
+ if (!(p.revents & (POLLOUT|POLLERR|POLLHUP)))
+ return 0;
+
+ r = getsockopt(b->output_fd, SOL_SOCKET, SO_ERROR, &error, &slen);
+ if (r < 0)
+ b->last_connect_error = errno;
+ else if (error != 0)
+ b->last_connect_error = error;
+ else if (p.revents & (POLLERR|POLLHUP))
+ b->last_connect_error = ECONNREFUSED;
+ else
+ return bus_socket_start_auth(b);
+
+ return bus_next_address(b);
+}
+
+int bus_socket_process_authenticating(sd_bus *b) {
+ int r;
+
+ assert(b);
+ assert(b->state == BUS_AUTHENTICATING);
+
+ if (now(CLOCK_MONOTONIC) >= b->auth_timeout)
+ return -ETIMEDOUT;
+
+ r = bus_socket_write_auth(b);
+ if (r != 0)
+ return r;
+
+ return bus_socket_read_auth(b);
+}
diff --git a/src/libsystemd-bus/bus-socket.h b/src/libsystemd-bus/bus-socket.h
new file mode 100644
index 0000000000..a9c43f82df
--- /dev/null
+++ b/src/libsystemd-bus/bus-socket.h
@@ -0,0 +1,36 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ 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 <http://www.gnu.org/licenses/>.
+***/
+
+#include "sd-bus.h"
+
+int bus_socket_connect(sd_bus *b);
+int bus_socket_exec(sd_bus *b);
+int bus_socket_take_fd(sd_bus *b);
+
+int bus_socket_write_message(sd_bus *bus, sd_bus_message *m, size_t *idx);
+int bus_socket_read_message(sd_bus *bus, sd_bus_message **m);
+
+int bus_socket_process_opening(sd_bus *b);
+int bus_socket_process_authenticating(sd_bus *b);
+
+bool bus_socket_auth_needs_write(sd_bus *b);
diff --git a/src/libsystemd-bus/bus-type.c b/src/libsystemd-bus/bus-type.c
new file mode 100644
index 0000000000..0557328085
--- /dev/null
+++ b/src/libsystemd-bus/bus-type.c
@@ -0,0 +1,163 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ 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 <http://www.gnu.org/licenses/>.
+***/
+
+#include "util.h"
+#include "bus-type.h"
+
+bool bus_type_is_valid(char c) {
+ static const char valid[] = {
+ SD_BUS_TYPE_BYTE,
+ SD_BUS_TYPE_BOOLEAN,
+ SD_BUS_TYPE_INT16,
+ SD_BUS_TYPE_UINT16,
+ SD_BUS_TYPE_INT32,
+ SD_BUS_TYPE_UINT32,
+ SD_BUS_TYPE_INT64,
+ SD_BUS_TYPE_UINT64,
+ SD_BUS_TYPE_DOUBLE,
+ SD_BUS_TYPE_STRING,
+ SD_BUS_TYPE_OBJECT_PATH,
+ SD_BUS_TYPE_SIGNATURE,
+ SD_BUS_TYPE_ARRAY,
+ SD_BUS_TYPE_VARIANT,
+ SD_BUS_TYPE_STRUCT,
+ SD_BUS_TYPE_DICT_ENTRY,
+ SD_BUS_TYPE_UNIX_FD
+ };
+
+ return !!memchr(valid, c, sizeof(valid));
+}
+
+bool bus_type_is_valid_in_signature(char c) {
+ static const char valid[] = {
+ SD_BUS_TYPE_BYTE,
+ SD_BUS_TYPE_BOOLEAN,
+ SD_BUS_TYPE_INT16,
+ SD_BUS_TYPE_UINT16,
+ SD_BUS_TYPE_INT32,
+ SD_BUS_TYPE_UINT32,
+ SD_BUS_TYPE_INT64,
+ SD_BUS_TYPE_UINT64,
+ SD_BUS_TYPE_DOUBLE,
+ SD_BUS_TYPE_STRING,
+ SD_BUS_TYPE_OBJECT_PATH,
+ SD_BUS_TYPE_SIGNATURE,
+ SD_BUS_TYPE_ARRAY,
+ SD_BUS_TYPE_VARIANT,
+ SD_BUS_TYPE_STRUCT_BEGIN,
+ SD_BUS_TYPE_STRUCT_END,
+ SD_BUS_TYPE_DICT_ENTRY_BEGIN,
+ SD_BUS_TYPE_DICT_ENTRY_END,
+ SD_BUS_TYPE_UNIX_FD
+ };
+
+ return !!memchr(valid, c, sizeof(valid));
+}
+
+bool bus_type_is_basic(char c) {
+ static const char valid[] = {
+ SD_BUS_TYPE_BYTE,
+ SD_BUS_TYPE_BOOLEAN,
+ SD_BUS_TYPE_INT16,
+ SD_BUS_TYPE_UINT16,
+ SD_BUS_TYPE_INT32,
+ SD_BUS_TYPE_UINT32,
+ SD_BUS_TYPE_INT64,
+ SD_BUS_TYPE_UINT64,
+ SD_BUS_TYPE_DOUBLE,
+ SD_BUS_TYPE_STRING,
+ SD_BUS_TYPE_OBJECT_PATH,
+ SD_BUS_TYPE_SIGNATURE,
+ SD_BUS_TYPE_UNIX_FD
+ };
+
+ return !!memchr(valid, c, sizeof(valid));
+}
+
+bool bus_type_is_container(char c) {
+ static const char valid[] = {
+ SD_BUS_TYPE_ARRAY,
+ SD_BUS_TYPE_VARIANT,
+ SD_BUS_TYPE_STRUCT,
+ SD_BUS_TYPE_DICT_ENTRY
+ };
+
+ return !!memchr(valid, c, sizeof(valid));
+}
+
+int bus_type_get_alignment(char c) {
+
+ switch (c) {
+ case SD_BUS_TYPE_BYTE:
+ case SD_BUS_TYPE_SIGNATURE:
+ case SD_BUS_TYPE_VARIANT:
+ return 1;
+
+ case SD_BUS_TYPE_INT16:
+ case SD_BUS_TYPE_UINT16:
+ return 2;
+
+ case SD_BUS_TYPE_BOOLEAN:
+ case SD_BUS_TYPE_INT32:
+ case SD_BUS_TYPE_UINT32:
+ case SD_BUS_TYPE_STRING:
+ case SD_BUS_TYPE_OBJECT_PATH:
+ case SD_BUS_TYPE_ARRAY:
+ case SD_BUS_TYPE_UNIX_FD:
+ return 4;
+
+ case SD_BUS_TYPE_INT64:
+ case SD_BUS_TYPE_UINT64:
+ case SD_BUS_TYPE_DOUBLE:
+ case SD_BUS_TYPE_STRUCT:
+ case SD_BUS_TYPE_STRUCT_BEGIN:
+ case SD_BUS_TYPE_DICT_ENTRY:
+ case SD_BUS_TYPE_DICT_ENTRY_BEGIN:
+ return 8;
+ }
+
+ return -EINVAL;
+}
+
+int bus_type_get_size(char c) {
+
+ switch (c) {
+ case SD_BUS_TYPE_BYTE:
+ return 1;
+
+ case SD_BUS_TYPE_INT16:
+ case SD_BUS_TYPE_UINT16:
+ return 2;
+
+ case SD_BUS_TYPE_BOOLEAN:
+ case SD_BUS_TYPE_INT32:
+ case SD_BUS_TYPE_UINT32:
+ case SD_BUS_TYPE_UNIX_FD:
+ return 4;
+
+ case SD_BUS_TYPE_INT64:
+ case SD_BUS_TYPE_UINT64:
+ case SD_BUS_TYPE_DOUBLE:
+ return 8;
+ }
+
+ return -EINVAL;
+}
diff --git a/src/libsystemd-bus/bus-type.h b/src/libsystemd-bus/bus-type.h
new file mode 100644
index 0000000000..e261136084
--- /dev/null
+++ b/src/libsystemd-bus/bus-type.h
@@ -0,0 +1,34 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ 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 <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdbool.h>
+
+#include "sd-bus.h"
+#include "sd-bus-protocol.h"
+
+bool bus_type_is_valid(char c);
+bool bus_type_is_valid_in_signature(char c);
+bool bus_type_is_basic(char c);
+bool bus_type_is_container(char c);
+int bus_type_get_alignment(char c);
+int bus_type_get_size(char c);
diff --git a/src/libsystemd-bus/busctl.c b/src/libsystemd-bus/busctl.c
new file mode 100644
index 0000000000..220c1eb34e
--- /dev/null
+++ b/src/libsystemd-bus/busctl.c
@@ -0,0 +1,340 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ 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 <http://www.gnu.org/licenses/>.
+***/
+
+#include <getopt.h>
+
+#include "strv.h"
+#include "util.h"
+#include "log.h"
+#include "build.h"
+#include "pager.h"
+
+#include "sd-bus.h"
+#include "bus-message.h"
+#include "bus-internal.h"
+
+static bool arg_no_pager = false;
+static char *arg_address = NULL;
+static bool arg_user = false;
+static bool arg_no_unique = false;
+static char **arg_matches = NULL;
+
+static void pager_open_if_enabled(void) {
+
+ /* Cache result before we open the pager */
+ if (arg_no_pager)
+ return;
+
+ pager_open(false);
+}
+
+static int list_bus_names(sd_bus *bus, char **argv) {
+ _cleanup_strv_free_ char **l = NULL;
+ char **i;
+ int r;
+ size_t max_i = 0;
+
+ assert(bus);
+
+ r = sd_bus_list_names(bus, &l);
+ if (r < 0) {
+ log_error("Failed to list names: %s", strerror(-r));
+ return r;
+ }
+
+ pager_open_if_enabled();
+
+ strv_sort(l);
+
+ STRV_FOREACH(i, l)
+ max_i = MAX(max_i, strlen(*i));
+
+ printf("%-*s %*s %-*s %-*s CONNECTION\n",
+ (int) max_i, "NAME", 10, "PID", 15, "PROCESS", 16, "USER");
+
+ STRV_FOREACH(i, l) {
+ _cleanup_free_ char *owner = NULL;
+ pid_t pid;
+ uid_t uid;
+
+ if (arg_no_unique && (*i)[0] == ':')
+ continue;
+
+ printf("%-*s", (int) max_i, *i);
+
+ r = sd_bus_get_owner_pid(bus, *i, &pid);
+ if (r >= 0) {
+ _cleanup_free_ char *comm = NULL;
+
+ printf(" %10lu", (unsigned long) pid);
+
+ get_process_comm(pid, &comm);
+ printf(" %-15s", strna(comm));
+ } else
+ printf(" - - ");
+
+ r = sd_bus_get_owner_uid(bus, *i, &uid);
+ if (r >= 0) {
+ _cleanup_free_ char *u = NULL;
+
+ u = uid_to_name(uid);
+ if (!u)
+ return log_oom();
+
+ if (strlen(u) > 16)
+ u[16] = 0;
+
+ printf(" %-16s", u);
+ } else
+ printf(" - ");
+
+ r = sd_bus_get_owner(bus, *i, &owner);
+ if (r >= 0)
+ printf(" %s\n", owner);
+ else
+ printf(" -\n");
+ }
+
+ return 0;
+}
+
+static int monitor(sd_bus *bus, char *argv[]) {
+ char **i;
+ int r;
+
+ STRV_FOREACH(i, argv+1) {
+ _cleanup_free_ char *m = NULL;
+
+ if (!service_name_is_valid(*i)) {
+ log_error("Invalid service name '%s'", *i);
+ return -EINVAL;
+ }
+
+ m = strjoin("sender='", *i, "'", NULL);
+ if (!m)
+ return log_oom();
+
+ r = sd_bus_add_match(bus, m, NULL, NULL);
+ if (r < 0) {
+ log_error("Failed to add match: %s", strerror(-r));
+ return r;
+ }
+ }
+
+ STRV_FOREACH(i, arg_matches) {
+ r = sd_bus_add_match(bus, *i, NULL, NULL);
+ if (r < 0) {
+ log_error("Failed to add match: %s", strerror(-r));
+ return r;
+ }
+ }
+
+ for (;;) {
+ _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+
+ r = sd_bus_process(bus, &m);
+ if (r < 0) {
+ log_error("Failed to process bus: %s", strerror(-r));
+ return r;
+ }
+
+ if (m) {
+ bus_message_dump(m);
+ continue;
+ }
+
+ if (r > 0)
+ continue;
+
+ r = sd_bus_wait(bus, (uint64_t) -1);
+ if (r < 0) {
+ log_error("Failed to wait for bus: %s", strerror(-r));
+ return r;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int help(void) {
+
+ printf("%s [OPTIONS...] {COMMAND} ...\n\n"
+ "Introspect the bus.\n\n"
+ " -h --help Show this help\n"
+ " --version Show package version\n"
+ " --system Connect to system bus\n"
+ " --user Connect to user bus\n"
+ " --address=ADDRESS Connect to bus specified by address\n"
+ " --no-unique Only show well-known names\n"
+ " --match=MATCH Only show matching messages\n"
+ " --no-pager Do not pipe output into a pager\n\n"
+ "Commands:\n"
+ " list List bus names\n"
+ " monitor [SERVICE...] Show bus traffic\n",
+ program_invocation_short_name);
+
+ return 0;
+}
+
+static int parse_argv(int argc, char *argv[]) {
+
+ enum {
+ ARG_VERSION = 0x100,
+ ARG_NO_PAGER,
+ ARG_SYSTEM,
+ ARG_USER,
+ ARG_ADDRESS,
+ ARG_MATCH,
+ ARG_NO_UNIQUE
+ };
+
+ static const struct option options[] = {
+ { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, ARG_VERSION },
+ { "no-pager", no_argument, NULL, ARG_NO_PAGER },
+ { "system", no_argument, NULL, ARG_SYSTEM },
+ { "user", no_argument, NULL, ARG_USER },
+ { "address", required_argument, NULL, ARG_ADDRESS },
+ { "no-unique", no_argument, NULL, ARG_NO_UNIQUE },
+ { "match", required_argument, NULL, ARG_MATCH },
+ { NULL, 0, NULL, 0 },
+ };
+
+ int c;
+
+ assert(argc >= 0);
+ assert(argv);
+
+ while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
+
+ switch (c) {
+
+ case 'h':
+ return help();
+
+ case ARG_VERSION:
+ puts(PACKAGE_STRING);
+ puts(SYSTEMD_FEATURES);
+ return 0;
+
+ case ARG_NO_PAGER:
+ arg_no_pager = true;
+ break;
+
+ case ARG_USER:
+ arg_user = true;
+ break;
+
+ case ARG_SYSTEM:
+ arg_user = false;
+ break;
+
+ case ARG_ADDRESS:
+ arg_address = optarg;
+ break;
+
+ case ARG_NO_UNIQUE:
+ arg_no_unique = true;
+ break;
+
+ case ARG_MATCH:
+ if (strv_extend(&arg_matches, optarg) < 0)
+ return log_oom();
+ break;
+
+ case '?':
+ return -EINVAL;
+
+ default:
+ log_error("Unknown option code %c", c);
+ return -EINVAL;
+ }
+ }
+
+ return 1;
+}
+
+static int busctl_main(sd_bus *bus, int argc, char *argv[]) {
+ assert(bus);
+
+ if (optind >= argc ||
+ streq(argv[optind], "list"))
+ return list_bus_names(bus, argv + optind);
+
+ if (streq(argv[optind], "monitor"))
+ return monitor(bus, argv + optind);
+
+ if (streq(argv[optind], "help"))
+ return help();
+
+ log_error("Unknown command '%s'", argv[optind]);
+ return -EINVAL;
+}
+
+int main(int argc, char *argv[]) {
+ _cleanup_bus_unref_ sd_bus *bus = NULL;
+ int r;
+
+ log_parse_environment();
+ log_open();
+
+ r = parse_argv(argc, argv);
+ if (r <= 0)
+ goto finish;
+
+ if (arg_address) {
+ r = sd_bus_new(&bus);
+ if (r < 0) {
+ log_error("Failed to allocate bus: %s", strerror(-r));
+ goto finish;
+ }
+
+ r = sd_bus_set_address(bus, arg_address);
+ if (r < 0) {
+ log_error("Failed to set address: %s", strerror(-r));
+ goto finish;
+ }
+
+ r = sd_bus_set_bus_client(bus, true);
+ if (r < 0) {
+ log_error("Failed to set bus client: %s", strerror(-r));
+ goto finish;
+ }
+
+ r = sd_bus_start(bus);
+ } else if (arg_user)
+ r = sd_bus_open_user(&bus);
+ else
+ r = sd_bus_open_system(&bus);
+
+ if (r < 0) {
+ log_error("Failed to connect to bus: %s", strerror(-r));
+ goto finish;
+ }
+
+ r = busctl_main(bus, argc, argv);
+
+finish:
+ pager_close();
+ strv_free(arg_matches);
+
+ return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+}
diff --git a/src/libsystemd-bus/kdbus.h b/src/libsystemd-bus/kdbus.h
new file mode 100644
index 0000000000..db5e243c17
--- /dev/null
+++ b/src/libsystemd-bus/kdbus.h
@@ -0,0 +1,418 @@
+/*
+ * Copyright (C) 2013 Kay Sievers
+ * Copyright (C) 2013 Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+ * Copyright (C) 2013 Linux Foundation
+ * Copyright (C) 2013 Lennart Poettering
+ * Copyright (C) 2013 Daniel Mack <daniel@zonque.org>
+ *
+ * kdbus 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.
+ */
+
+#ifndef _KDBUS_H_
+#define _KDBUS_H_
+
+#ifndef __KERNEL__
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <linux/types.h>
+#endif
+
+#define KDBUS_IOC_MAGIC 0x95
+
+/* Message sent from kernel to userspace, when the owner or starter of
+ * a well-known name changes */
+struct kdbus_manager_msg_name_change {
+ __u64 old_id;
+ __u64 new_id;
+ __u64 flags; /* 0 or (possibly?) KDBUS_NAME_IN_QUEUE */
+ char name[0];
+};
+
+struct kdbus_manager_msg_id_change {
+ __u64 id;
+ __u64 flags; /* The kernel flags field from KDBUS_HELLO */
+};
+
+struct kdbus_creds {
+ __u64 uid;
+ __u64 gid;
+ __u64 pid;
+ __u64 tid;
+
+ /* The starttime of the process PID. This is useful to detect
+ PID overruns from the client side. i.e. if you use the PID to
+ look something up in /proc/$PID/ you can afterwards check the
+ starttime field of it to ensure you didn't run into a PID
+ ovretun. */
+ __u64 starttime;
+};
+
+struct kdbus_audit {
+ __u64 sessionid;
+ __u64 loginuid;
+};
+
+struct kdbus_timestamp {
+ __u64 monotonic_ns;
+ __u64 realtime_ns;
+};
+
+#define KDBUS_SRC_ID_KERNEL (0)
+#define KDBUS_DST_ID_WELL_KNOWN_NAME (0)
+#define KDBUS_MATCH_SRC_ID_ANY (~0ULL)
+#define KDBUS_DST_ID_BROADCAST (~0ULL)
+
+/* Message Item Types */
+enum {
+ KDBUS_MSG_NULL,
+
+ /* Filled in by userspace */
+ KDBUS_MSG_PAYLOAD, /* .data, inline memory */
+ KDBUS_MSG_PAYLOAD_VEC, /* .data_vec, reference to memory area */
+ KDBUS_MSG_UNIX_FDS, /* .data_fds of file descriptors */
+ KDBUS_MSG_BLOOM, /* for broadcasts, carries bloom filter blob in .data */
+ KDBUS_MSG_DST_NAME, /* destination's well-known name, in .str */
+
+ /* Filled in by kernelspace */
+ KDBUS_MSG_SRC_NAMES = 0x200,/* NUL separated string list with well-known names of source */
+ KDBUS_MSG_TIMESTAMP, /* .timestamp */
+ KDBUS_MSG_SRC_CREDS, /* .creds */
+ KDBUS_MSG_SRC_PID_COMM, /* optional, in .str */
+ KDBUS_MSG_SRC_TID_COMM, /* optional, in .str */
+ KDBUS_MSG_SRC_EXE, /* optional, in .str */
+ KDBUS_MSG_SRC_CMDLINE, /* optional, in .str (a chain of NUL str) */
+ KDBUS_MSG_SRC_CGROUP, /* optional, in .str */
+ KDBUS_MSG_SRC_CAPS, /* caps data blob, in .data */
+ KDBUS_MSG_SRC_SECLABEL, /* NUL terminated string, in .str */
+ KDBUS_MSG_SRC_AUDIT, /* .audit */
+
+ /* Special messages from kernel, consisting of one and only one of these data blocks */
+ KDBUS_MSG_NAME_ADD = 0x400,/* .name_change */
+ KDBUS_MSG_NAME_REMOVE, /* .name_change */
+ KDBUS_MSG_NAME_CHANGE, /* .name_change */
+ KDBUS_MSG_ID_ADD, /* .id_change */
+ KDBUS_MSG_ID_REMOVE, /* .id_change */
+ KDBUS_MSG_REPLY_TIMEOUT, /* empty, but .reply_cookie in .kdbus_msg is filled in */
+ KDBUS_MSG_REPLY_DEAD, /* dito */
+};
+
+enum {
+ KDBUS_VEC_ALIGNED = 1 << 0,
+};
+
+struct kdbus_vec {
+ __u64 address;
+ __u64 size;
+ __u64 flags;
+};
+
+/**
+ * struct kdbus_item - chain of data blocks
+ *
+ * size: overall data record size
+ * type: kdbus_item type of data
+ */
+struct kdbus_item {
+ __u64 size;
+ __u64 type;
+ union {
+ /* inline data */
+ __u8 data[0];
+ __u32 data32[0];
+ __u64 data64[0];
+ char str[0];
+
+ /* connection */
+ __u64 id;
+
+ /* data vector */
+ struct kdbus_vec vec;
+
+ /* process credentials and properties*/
+ struct kdbus_creds creds;
+ struct kdbus_audit audit;
+ struct kdbus_timestamp timestamp;
+
+ /* specific fields */
+ int fds[0];
+ struct kdbus_manager_msg_name_change name_change;
+ struct kdbus_manager_msg_id_change id_change;
+ };
+};
+
+enum {
+ KDBUS_MSG_FLAGS_EXPECT_REPLY = 1 << 0,
+ KDBUS_MSG_FLAGS_NO_AUTO_START = 1 << 1,
+};
+
+enum {
+ KDBUS_PAYLOAD_NULL,
+ KDBUS_PAYLOAD_DBUS1 = 0x4442757356657231ULL, /* 'DBusVer1' */
+ KDBUS_PAYLOAD_GVARIANT = 0x4756617269616e74ULL, /* 'GVariant' */
+};
+
+/**
+ * struct kdbus_msg
+ *
+ * set by userspace:
+ * dst_id: destination id
+ * flags: KDBUS_MSG_FLAGS_*
+ * items: data records
+ *
+ * set by kernel:
+ * src_id: who sent the message
+ */
+struct kdbus_msg {
+ __u64 size;
+ __u64 flags;
+ __u64 dst_id; /* connection, 0 == name in data, ~0 broadcast */
+ __u64 src_id; /* connection, 0 == kernel */
+ __u64 payload_type; /* 'DBusVer1', 'GVariant', ... */
+ __u64 cookie; /* userspace-supplied cookie */
+ union {
+ __u64 cookie_reply; /* cookie we reply to */
+ __u64 timeout_ns; /* timespan to wait for reply */
+ };
+ struct kdbus_item items[0];
+};
+
+enum {
+ KDBUS_POLICY_NULL,
+ KDBUS_POLICY_NAME,
+ KDBUS_POLICY_ACCESS,
+};
+
+enum {
+ KDBUS_POLICY_ACCESS_NULL,
+ KDBUS_POLICY_ACCESS_USER,
+ KDBUS_POLICY_ACCESS_GROUP,
+ KDBUS_POLICY_ACCESS_WORLD,
+};
+
+enum {
+ KDBUS_POLICY_RECV = 1 << 2,
+ KDBUS_POLICY_SEND = 1 << 1,
+ KDBUS_POLICY_OWN = 1 << 0,
+};
+
+struct kdbus_policy {
+ __u64 size;
+ __u64 type; /* NAME or ACCESS */
+ union {
+ char name[0];
+ struct {
+ __u32 type; /* USER, GROUP, WORLD */
+ __u32 bits; /* RECV, SEND, OWN */
+ __u64 id; /* uid, gid, 0 */
+ } access;
+ };
+};
+
+struct kdbus_cmd_policy {
+ __u64 size;
+ __u8 buffer[0]; /* a series of KDBUS_POLICY_NAME plus one or
+ * more KDBUS_POLICY_ACCESS each. */
+};
+
+/* Flags for struct kdbus_cmd_hello */
+enum {
+ KDBUS_HELLO_STARTER = 1 << 0,
+ KDBUS_HELLO_ACCEPT_FD = 1 << 1,
+
+ /* The following have an effect on directed messages only --
+ * not for broadcasts */
+ KDBUS_HELLO_ATTACH_COMM = 1 << 10,
+ KDBUS_HELLO_ATTACH_EXE = 1 << 11,
+ KDBUS_HELLO_ATTACH_CMDLINE = 1 << 12,
+ KDBUS_HELLO_ATTACH_CGROUP = 1 << 13,
+ KDBUS_HELLO_ATTACH_CAPS = 1 << 14,
+ KDBUS_HELLO_ATTACH_SECLABEL = 1 << 15,
+ KDBUS_HELLO_ATTACH_AUDIT = 1 << 16,
+};
+
+/* Items to append to struct kdbus_cmd_hello */
+enum {
+ KDBUS_HELLO_NULL,
+};
+
+struct kdbus_cmd_hello {
+ __u64 size;
+
+ /* userspace → kernel, kernel → userspace */
+ __u64 conn_flags; /* userspace specifies its
+ * capabilities and more, kernel
+ * returns its capabilites and
+ * more. Kernel might refuse client's
+ * capabilities by returning an error
+ * from KDBUS_HELLO */
+
+ /* kernel → userspace */
+ __u64 bus_flags; /* this is .flags copied verbatim from
+ * from original KDBUS_CMD_BUS_MAKE
+ * ioctl. It's intended to be useful
+ * to do negotiation of features of
+ * the payload that is transfreted. */
+ __u64 id; /* id assigned to this connection */
+ __u64 bloom_size; /* The bloom filter size chosen by the
+ * bus owner */
+ struct kdbus_item items[0];
+};
+
+/* Flags for kdbus_cmd_{bus,ep,ns}_make */
+enum {
+ KDBUS_MAKE_ACCESS_GROUP = 1 << 0,
+ KDBUS_MAKE_ACCESS_WORLD = 1 << 1,
+ KDBUS_MAKE_POLICY_OPEN = 1 << 2,
+};
+
+/* Items to append to kdbus_cmd_{bus,ep,ns}_make */
+enum {
+ KDBUS_MAKE_NULL,
+ KDBUS_MAKE_NAME,
+ KDBUS_MAKE_CGROUP, /* the cgroup hierarchy ID for which to attach
+ * cgroup membership paths * to messages. */
+ KDBUS_MAKE_CRED, /* allow translator services which connect
+ * to the bus on behalf of somebody else,
+ * allow specifying the credentials of the
+ * client to connect on behalf on. Needs
+ * privileges */
+};
+
+struct kdbus_cmd_bus_make {
+ __u64 size;
+ __u64 flags; /* userspace → kernel, kernel → userspace
+ * When creating a bus feature
+ * kernel negotiation. */
+ __u64 bus_flags; /* userspace → kernel
+ * When a bus is created this value is
+ * copied verbatim into the bus
+ * structure and returned from
+ * KDBUS_CMD_HELLO, later */
+ __u64 bloom_size; /* size of the bloom filter for this bus */
+ struct kdbus_item items[0];
+
+};
+
+struct kdbus_cmd_ep_make {
+ __u64 size;
+ __u64 flags; /* userspace → kernel, kernel → userspace
+ * When creating an entry point
+ * feature kernel negotiation done the
+ * same way as for
+ * KDBUS_CMD_BUS_MAKE. Unused for
+ * now. */
+ struct kdbus_item items[0];
+};
+
+struct kdbus_cmd_ns_make {
+ __u64 size;
+ __u64 flags; /* userspace → kernel, kernel → userspace
+ * When creating an entry point
+ * feature kernel negotiation done the
+ * same way as for
+ * KDBUS_CMD_BUS_MAKE. Unused for
+ * now. */
+ struct kdbus_item items[0];
+};
+
+enum {
+ /* userspace → kernel */
+ KDBUS_NAME_REPLACE_EXISTING = 1 << 0,
+ KDBUS_NAME_QUEUE = 1 << 1,
+ KDBUS_NAME_ALLOW_REPLACEMENT = 1 << 2,
+
+ /* kernel → userspace */
+ KDBUS_NAME_IN_QUEUE = 1 << 16,
+};
+
+struct kdbus_cmd_name {
+ __u64 size;
+ __u64 name_flags;
+ __u64 id; /* We allow registration/deregestration of names of other peers */
+ __u64 conn_flags;
+ char name[0];
+};
+
+struct kdbus_cmd_names {
+ __u64 size;
+ struct kdbus_cmd_name names[0];
+};
+
+enum {
+ KDBUS_NAME_INFO_ITEM_NULL,
+ KDBUS_NAME_INFO_ITEM_NAME, /* userspace → kernel */
+ KDBUS_NAME_INFO_ITEM_SECLABEL, /* kernel → userspace */
+ KDBUS_NAME_INFO_ITEM_AUDIT, /* kernel → userspace */
+};
+
+struct kdbus_cmd_name_info {
+ __u64 size; /* overall size of info */
+ __u64 flags;
+ __u64 id; /* either ID, or 0 and _ITEM_NAME follows */
+ struct kdbus_creds creds;
+ struct kdbus_item items[0]; /* list of item records */
+};
+
+enum {
+ KDBUS_MATCH_NULL,
+ KDBUS_MATCH_BLOOM, /* Matches a mask blob against KDBUS_MSG_BLOOM */
+ KDBUS_MATCH_SRC_NAME, /* Matches a name string against KDBUS_MSG_SRC_NAMES */
+ KDBUS_MATCH_NAME_ADD, /* Matches a name string against KDBUS_MSG_NAME_ADD */
+ KDBUS_MATCH_NAME_REMOVE, /* Matches a name string against KDBUS_MSG_NAME_REMOVE */
+ KDBUS_MATCH_NAME_CHANGE, /* Matches a name string against KDBUS_MSG_NAME_CHANGE */
+ KDBUS_MATCH_ID_ADD, /* Matches an ID against KDBUS_MSG_ID_ADD */
+ KDBUS_MATCH_ID_REMOVE, /* Matches an ID against KDBUS_MSG_ID_REMOVE */
+};
+
+struct kdbus_cmd_match {
+ __u64 size;
+ __u64 id; /* We allow registration/deregestration of matches for other peers */
+ __u64 cookie; /* userspace supplied cookie; when removing; kernel deletes everything with same cookie */
+ __u64 src_id; /* ~0: any. other: exact unique match */
+ struct kdbus_item items[0];
+};
+
+struct kdbus_cmd_monitor {
+ __u64 id; /* We allow setting the monitor flag of other peers */
+ unsigned int enabled; /* A boolean to enable/disable monitoring */
+};
+
+/* FD states:
+ * control nodes: unset
+ * bus owner (via KDBUS_CMD_BUS_MAKE)
+ * ns owner (via KDBUS_CMD_NS_MAKE)
+ *
+ * ep nodes: unset
+ * connected (via KDBUS_CMD_HELLO)
+ * starter (via KDBUS_CMD_HELLO with KDBUS_CMD_HELLO_STARTER)
+ * ep owner (via KDBUS_CMD_EP_MAKE)
+ */
+enum kdbus_cmd {
+ /* kdbus control node commands: require unset state */
+ KDBUS_CMD_BUS_MAKE = _IOWR(KDBUS_IOC_MAGIC, 0x00, struct kdbus_cmd_bus_make),
+ KDBUS_CMD_NS_MAKE = _IOWR(KDBUS_IOC_MAGIC, 0x10, struct kdbus_cmd_ns_make),
+
+ /* kdbus ep node commands: require unset state */
+ KDBUS_CMD_EP_MAKE = _IOWR(KDBUS_IOC_MAGIC, 0x20, struct kdbus_cmd_ep_make),
+ KDBUS_CMD_HELLO = _IOWR(KDBUS_IOC_MAGIC, 0x30, struct kdbus_cmd_hello),
+
+ /* kdbus ep node commands: require connected state */
+ KDBUS_CMD_MSG_SEND = _IOWR(KDBUS_IOC_MAGIC, 0x40, struct kdbus_msg),
+ KDBUS_CMD_MSG_RECV = _IOWR(KDBUS_IOC_MAGIC, 0x41, struct kdbus_msg),
+
+ KDBUS_CMD_NAME_ACQUIRE = _IOWR(KDBUS_IOC_MAGIC, 0x50, struct kdbus_cmd_name),
+ KDBUS_CMD_NAME_RELEASE = _IOWR(KDBUS_IOC_MAGIC, 0x51, struct kdbus_cmd_name),
+ KDBUS_CMD_NAME_LIST = _IOWR(KDBUS_IOC_MAGIC, 0x52, struct kdbus_cmd_names),
+ KDBUS_CMD_NAME_QUERY = _IOWR(KDBUS_IOC_MAGIC, 0x53, struct kdbus_cmd_name_info),
+
+ KDBUS_CMD_MATCH_ADD = _IOWR(KDBUS_IOC_MAGIC, 0x60, struct kdbus_cmd_match),
+ KDBUS_CMD_MATCH_REMOVE = _IOWR(KDBUS_IOC_MAGIC, 0x61, struct kdbus_cmd_match),
+ KDBUS_CMD_MONITOR = _IOWR(KDBUS_IOC_MAGIC, 0x62, struct kdbus_cmd_monitor),
+
+ /* kdbus ep node commands: require ep owner state */
+ KDBUS_CMD_EP_POLICY_SET = _IOWR(KDBUS_IOC_MAGIC, 0x70, struct kdbus_cmd_policy),
+};
+#endif
diff --git a/src/libsystemd-bus/sd-bus.c b/src/libsystemd-bus/sd-bus.c
new file mode 100644
index 0000000000..7d6d848ec5
--- /dev/null
+++ b/src/libsystemd-bus/sd-bus.c
@@ -0,0 +1,2425 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ 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 <http://www.gnu.org/licenses/>.
+***/
+
+#include <endian.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <netdb.h>
+#include <sys/poll.h>
+#include <byteswap.h>
+
+#include "util.h"
+#include "macro.h"
+#include "strv.h"
+#include "set.h"
+#include "missing.h"
+
+#include "sd-bus.h"
+#include "bus-internal.h"
+#include "bus-message.h"
+#include "bus-type.h"
+#include "bus-socket.h"
+#include "bus-kernel.h"
+#include "bus-control.h"
+
+static int bus_poll(sd_bus *bus, bool need_more, uint64_t timeout_usec);
+
+static void bus_free(sd_bus *b) {
+ struct filter_callback *f;
+ struct object_callback *c;
+ unsigned i;
+
+ assert(b);
+
+ sd_bus_close(b);
+
+ free(b->rbuffer);
+ free(b->unique_name);
+ free(b->auth_buffer);
+ free(b->address);
+ free(b->kernel);
+
+ free(b->exec_path);
+ strv_free(b->exec_argv);
+
+ close_many(b->fds, b->n_fds);
+ free(b->fds);
+
+ for (i = 0; i < b->rqueue_size; i++)
+ sd_bus_message_unref(b->rqueue[i]);
+ free(b->rqueue);
+
+ for (i = 0; i < b->wqueue_size; i++)
+ sd_bus_message_unref(b->wqueue[i]);
+ free(b->wqueue);
+
+ hashmap_free_free(b->reply_callbacks);
+ prioq_free(b->reply_callbacks_prioq);
+
+ while ((f = b->filter_callbacks)) {
+ LIST_REMOVE(struct filter_callback, callbacks, b->filter_callbacks, f);
+ free(f);
+ }
+
+ while ((c = hashmap_steal_first(b->object_callbacks))) {
+ free(c->path);
+ free(c);
+ }
+
+ hashmap_free(b->object_callbacks);
+
+ bus_match_free(&b->match_callbacks);
+
+ free(b);
+}
+
+int sd_bus_new(sd_bus **ret) {
+ sd_bus *r;
+
+ if (!ret)
+ return -EINVAL;
+
+ r = new0(sd_bus, 1);
+ if (!r)
+ return -ENOMEM;
+
+ r->n_ref = 1;
+ r->input_fd = r->output_fd = -1;
+ r->message_version = 1;
+ r->negotiate_fds = true;
+
+ /* We guarantee that wqueue always has space for at least one
+ * entry */
+ r->wqueue = new(sd_bus_message*, 1);
+ if (!r->wqueue) {
+ free(r);
+ return -ENOMEM;
+ }
+
+ *ret = r;
+ return 0;
+}
+
+int sd_bus_set_address(sd_bus *bus, const char *address) {
+ char *a;
+
+ if (!bus)
+ return -EINVAL;
+ if (bus->state != BUS_UNSET)
+ return -EPERM;
+ if (!address)
+ return -EINVAL;
+
+ a = strdup(address);
+ if (!a)
+ return -ENOMEM;
+
+ free(bus->address);
+ bus->address = a;
+
+ return 0;
+}
+
+int sd_bus_set_fd(sd_bus *bus, int input_fd, int output_fd) {
+ if (!bus)
+ return -EINVAL;
+ if (bus->state != BUS_UNSET)
+ return -EPERM;
+ if (input_fd < 0)
+ return -EINVAL;
+ if (output_fd < 0)
+ return -EINVAL;
+
+ bus->input_fd = input_fd;
+ bus->output_fd = output_fd;
+ return 0;
+}
+
+int sd_bus_set_exec(sd_bus *bus, const char *path, char *const argv[]) {
+ char *p, **a;
+
+ if (!bus)
+ return -EINVAL;
+ if (bus->state != BUS_UNSET)
+ return -EPERM;
+ if (!path)
+ return -EINVAL;
+ if (strv_isempty(argv))
+ return -EINVAL;
+
+ p = strdup(path);
+ if (!p)
+ return -ENOMEM;
+
+ a = strv_copy(argv);
+ if (!a) {
+ free(p);
+ return -ENOMEM;
+ }
+
+ free(bus->exec_path);
+ strv_free(bus->exec_argv);
+
+ bus->exec_path = p;
+ bus->exec_argv = a;
+
+ return 0;
+}
+
+int sd_bus_set_bus_client(sd_bus *bus, int b) {
+ if (!bus)
+ return -EINVAL;
+ if (bus->state != BUS_UNSET)
+ return -EPERM;
+
+ bus->bus_client = !!b;
+ return 0;
+}
+
+int sd_bus_set_negotiate_fds(sd_bus *bus, int b) {
+ if (!bus)
+ return -EINVAL;
+ if (bus->state != BUS_UNSET)
+ return -EPERM;
+
+ bus->negotiate_fds = !!b;
+ return 0;
+}
+
+int sd_bus_set_server(sd_bus *bus, int b, sd_id128_t server_id) {
+ if (!bus)
+ return -EINVAL;
+ if (!b && !sd_id128_equal(server_id, SD_ID128_NULL))
+ return -EINVAL;
+ if (bus->state != BUS_UNSET)
+ return -EPERM;
+
+ bus->is_server = !!b;
+ bus->server_id = server_id;
+ return 0;
+}
+
+int sd_bus_set_anonymous(sd_bus *bus, int b) {
+ if (!bus)
+ return -EINVAL;
+ if (bus->state != BUS_UNSET)
+ return -EPERM;
+
+ bus->anonymous_auth = !!b;
+ return 0;
+}
+
+static int hello_callback(sd_bus *bus, int error, sd_bus_message *reply, void *userdata) {
+ const char *s;
+ int r;
+
+ assert(bus);
+ assert(bus->state == BUS_HELLO);
+
+ if (error != 0)
+ return -error;
+
+ assert(reply);
+
+ r = sd_bus_message_read(reply, "s", &s);
+ if (r < 0)
+ return r;
+
+ if (!service_name_is_valid(s) || s[0] != ':')
+ return -EBADMSG;
+
+ bus->unique_name = strdup(s);
+ if (!bus->unique_name)
+ return -ENOMEM;
+
+ bus->state = BUS_RUNNING;
+
+ return 1;
+}
+
+static int bus_send_hello(sd_bus *bus) {
+ _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ int r;
+
+ assert(bus);
+
+ if (!bus->bus_client || bus->is_kernel)
+ return 0;
+
+ r = sd_bus_message_new_method_call(
+ bus,
+ "org.freedesktop.DBus",
+ "/",
+ "org.freedesktop.DBus",
+ "Hello",
+ &m);
+ if (r < 0)
+ return r;
+
+ return sd_bus_send_with_reply(bus, m, hello_callback, NULL, 0, &bus->hello_serial);
+}
+
+int bus_start_running(sd_bus *bus) {
+ assert(bus);
+
+ if (bus->bus_client && !bus->is_kernel) {
+ bus->state = BUS_HELLO;
+ return 1;
+ }
+
+ bus->state = BUS_RUNNING;
+ return 1;
+}
+
+static int parse_address_key(const char **p, const char *key, char **value) {
+ size_t l, n = 0;
+ const char *a;
+ char *r = NULL;
+
+ assert(p);
+ assert(*p);
+ assert(value);
+
+ if (key) {
+ l = strlen(key);
+ if (strncmp(*p, key, l) != 0)
+ return 0;
+
+ if ((*p)[l] != '=')
+ return 0;
+
+ if (*value)
+ return -EINVAL;
+
+ a = *p + l + 1;
+ } else
+ a = *p;
+
+ while (*a != ';' && *a != ',' && *a != 0) {
+ char c, *t;
+
+ if (*a == '%') {
+ int x, y;
+
+ x = unhexchar(a[1]);
+ if (x < 0) {
+ free(r);
+ return x;
+ }
+
+ y = unhexchar(a[2]);
+ if (y < 0) {
+ free(r);
+ return y;
+ }
+
+ c = (char) ((x << 4) | y);
+ a += 3;
+ } else {
+ c = *a;
+ a++;
+ }
+
+ t = realloc(r, n + 2);
+ if (!t) {
+ free(r);
+ return -ENOMEM;
+ }
+
+ r = t;
+ r[n++] = c;
+ }
+
+ if (!r) {
+ r = strdup("");
+ if (!r)
+ return -ENOMEM;
+ } else
+ r[n] = 0;
+
+ if (*a == ',')
+ a++;
+
+ *p = a;
+
+ free(*value);
+ *value = r;
+
+ return 1;
+}
+
+static void skip_address_key(const char **p) {
+ assert(p);
+ assert(*p);
+
+ *p += strcspn(*p, ",");
+
+ if (**p == ',')
+ (*p) ++;
+}
+
+static int parse_unix_address(sd_bus *b, const char **p, char **guid) {
+ _cleanup_free_ char *path = NULL, *abstract = NULL;
+ size_t l;
+ int r;
+
+ assert(b);
+ assert(p);
+ assert(*p);
+ assert(guid);
+
+ while (**p != 0 && **p != ';') {
+ r = parse_address_key(p, "guid", guid);
+ if (r < 0)
+ return r;
+ else if (r > 0)
+ continue;
+
+ r = parse_address_key(p, "path", &path);
+ if (r < 0)
+ return r;
+ else if (r > 0)
+ continue;
+
+ r = parse_address_key(p, "abstract", &abstract);
+ if (r < 0)
+ return r;
+ else if (r > 0)
+ continue;
+
+ skip_address_key(p);
+ }
+
+ if (!path && !abstract)
+ return -EINVAL;
+
+ if (path && abstract)
+ return -EINVAL;
+
+ if (path) {
+ l = strlen(path);
+ if (l > sizeof(b->sockaddr.un.sun_path))
+ return -E2BIG;
+
+ b->sockaddr.un.sun_family = AF_UNIX;
+ strncpy(b->sockaddr.un.sun_path, path, sizeof(b->sockaddr.un.sun_path));
+ b->sockaddr_size = offsetof(struct sockaddr_un, sun_path) + l;
+ } else if (abstract) {
+ l = strlen(abstract);
+ if (l > sizeof(b->sockaddr.un.sun_path) - 1)
+ return -E2BIG;
+
+ b->sockaddr.un.sun_family = AF_UNIX;
+ b->sockaddr.un.sun_path[0] = 0;
+ strncpy(b->sockaddr.un.sun_path+1, abstract, sizeof(b->sockaddr.un.sun_path)-1);
+ b->sockaddr_size = offsetof(struct sockaddr_un, sun_path) + 1 + l;
+ }
+
+ return 0;
+}
+
+static int parse_tcp_address(sd_bus *b, const char **p, char **guid) {
+ _cleanup_free_ char *host = NULL, *port = NULL, *family = NULL;
+ int r;
+ struct addrinfo *result, hints = {
+ .ai_socktype = SOCK_STREAM,
+ .ai_flags = AI_ADDRCONFIG,
+ };
+
+ assert(b);
+ assert(p);
+ assert(*p);
+ assert(guid);
+
+ while (**p != 0 && **p != ';') {
+ r = parse_address_key(p, "guid", guid);
+ if (r < 0)
+ return r;
+ else if (r > 0)
+ continue;
+
+ r = parse_address_key(p, "host", &host);
+ if (r < 0)
+ return r;
+ else if (r > 0)
+ continue;
+
+ r = parse_address_key(p, "port", &port);
+ if (r < 0)
+ return r;
+ else if (r > 0)
+ continue;
+
+ r = parse_address_key(p, "family", &family);
+ if (r < 0)
+ return r;
+ else if (r > 0)
+ continue;
+
+ skip_address_key(p);
+ }
+
+ if (!host || !port)
+ return -EINVAL;
+
+ if (family) {
+ if (streq(family, "ipv4"))
+ hints.ai_family = AF_INET;
+ else if (streq(family, "ipv6"))
+ hints.ai_family = AF_INET6;
+ else
+ return -EINVAL;
+ }
+
+ r = getaddrinfo(host, port, &hints, &result);
+ if (r == EAI_SYSTEM)
+ return -errno;
+ else if (r != 0)
+ return -EADDRNOTAVAIL;
+
+ memcpy(&b->sockaddr, result->ai_addr, result->ai_addrlen);
+ b->sockaddr_size = result->ai_addrlen;
+
+ freeaddrinfo(result);
+
+ return 0;
+}
+
+static int parse_exec_address(sd_bus *b, const char **p, char **guid) {
+ char *path = NULL;
+ unsigned n_argv = 0, j;
+ char **argv = NULL;
+ int r;
+
+ assert(b);
+ assert(p);
+ assert(*p);
+ assert(guid);
+
+ while (**p != 0 && **p != ';') {
+ r = parse_address_key(p, "guid", guid);
+ if (r < 0)
+ goto fail;
+ else if (r > 0)
+ continue;
+
+ r = parse_address_key(p, "path", &path);
+ if (r < 0)
+ goto fail;
+ else if (r > 0)
+ continue;
+
+ if (startswith(*p, "argv")) {
+ unsigned ul;
+
+ errno = 0;
+ ul = strtoul(*p + 4, (char**) p, 10);
+ if (errno > 0 || **p != '=' || ul > 256) {
+ r = -EINVAL;
+ goto fail;
+ }
+
+ (*p) ++;
+
+ if (ul >= n_argv) {
+ char **x;
+
+ x = realloc(argv, sizeof(char*) * (ul + 2));
+ if (!x) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ memset(x + n_argv, 0, sizeof(char*) * (ul - n_argv + 2));
+
+ argv = x;
+ n_argv = ul + 1;
+ }
+
+ r = parse_address_key(p, NULL, argv + ul);
+ if (r < 0)
+ goto fail;
+
+ continue;
+ }
+
+ skip_address_key(p);
+ }
+
+ if (!path) {
+ r = -EINVAL;
+ goto fail;
+ }
+
+ /* Make sure there are no holes in the array, with the
+ * exception of argv[0] */
+ for (j = 1; j < n_argv; j++)
+ if (!argv[j]) {
+ r = -EINVAL;
+ goto fail;
+ }
+
+ if (argv && argv[0] == NULL) {
+ argv[0] = strdup(path);
+ if (!argv[0]) {
+ r = -ENOMEM;
+ goto fail;
+ }
+ }
+
+ b->exec_path = path;
+ b->exec_argv = argv;
+ return 0;
+
+fail:
+ for (j = 0; j < n_argv; j++)
+ free(argv[j]);
+
+ free(argv);
+ free(path);
+ return r;
+}
+
+static int parse_kernel_address(sd_bus *b, const char **p, char **guid) {
+ _cleanup_free_ char *path = NULL;
+ int r;
+
+ assert(b);
+ assert(p);
+ assert(*p);
+ assert(guid);
+
+ while (**p != 0 && **p != ';') {
+ r = parse_address_key(p, "guid", guid);
+ if (r < 0)
+ return r;
+ else if (r > 0)
+ continue;
+
+ r = parse_address_key(p, "path", &path);
+ if (r < 0)
+ return r;
+ else if (r > 0)
+ continue;
+
+ skip_address_key(p);
+ }
+
+ if (!path)
+ return -EINVAL;
+
+ free(b->kernel);
+ b->kernel = path;
+ path = NULL;
+
+ return 0;
+}
+
+static void bus_reset_parsed_address(sd_bus *b) {
+ assert(b);
+
+ zero(b->sockaddr);
+ b->sockaddr_size = 0;
+ strv_free(b->exec_argv);
+ free(b->exec_path);
+ b->exec_path = NULL;
+ b->exec_argv = NULL;
+ b->server_id = SD_ID128_NULL;
+ free(b->kernel);
+ b->kernel = NULL;
+}
+
+static int bus_parse_next_address(sd_bus *b) {
+ _cleanup_free_ char *guid = NULL;
+ const char *a;
+ int r;
+
+ assert(b);
+
+ if (!b->address)
+ return 0;
+ if (b->address[b->address_index] == 0)
+ return 0;
+
+ bus_reset_parsed_address(b);
+
+ a = b->address + b->address_index;
+
+ while (*a != 0) {
+
+ if (*a == ';') {
+ a++;
+ continue;
+ }
+
+ if (startswith(a, "unix:")) {
+ a += 5;
+
+ r = parse_unix_address(b, &a, &guid);
+ if (r < 0)
+ return r;
+ break;
+
+ } else if (startswith(a, "tcp:")) {
+
+ a += 4;
+ r = parse_tcp_address(b, &a, &guid);
+ if (r < 0)
+ return r;
+
+ break;
+
+ } else if (startswith(a, "unixexec:")) {
+
+ a += 9;
+ r = parse_exec_address(b, &a, &guid);
+ if (r < 0)
+ return r;
+
+ break;
+
+ } else if (startswith(a, "kernel:")) {
+
+ a += 7;
+ r = parse_kernel_address(b, &a, &guid);
+ if (r < 0)
+ return r;
+
+ break;
+ }
+
+ a = strchr(a, ';');
+ if (!a)
+ return 0;
+ }
+
+ if (guid) {
+ r = sd_id128_from_string(guid, &b->server_id);
+ if (r < 0)
+ return r;
+ }
+
+ b->address_index = a - b->address;
+ return 1;
+}
+
+static int bus_start_address(sd_bus *b) {
+ int r;
+
+ assert(b);
+
+ for (;;) {
+ sd_bus_close(b);
+
+ if (b->sockaddr.sa.sa_family != AF_UNSPEC) {
+
+ r = bus_socket_connect(b);
+ if (r >= 0)
+ return r;
+
+ b->last_connect_error = -r;
+
+ } else if (b->exec_path) {
+
+ r = bus_socket_exec(b);
+ if (r >= 0)
+ return r;
+
+ b->last_connect_error = -r;
+ } else if (b->kernel) {
+
+ r = bus_kernel_connect(b);
+ if (r >= 0)
+ return r;
+
+ b->last_connect_error = -r;
+ }
+
+ r = bus_parse_next_address(b);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return b->last_connect_error ? -b->last_connect_error : -ECONNREFUSED;
+ }
+}
+
+int bus_next_address(sd_bus *b) {
+ assert(b);
+
+ bus_reset_parsed_address(b);
+ return bus_start_address(b);
+}
+
+static int bus_start_fd(sd_bus *b) {
+ struct stat st;
+ int r;
+
+ assert(b);
+ assert(b->input_fd >= 0);
+ assert(b->output_fd >= 0);
+
+ r = fd_nonblock(b->input_fd, true);
+ if (r < 0)
+ return r;
+
+ r = fd_cloexec(b->input_fd, true);
+ if (r < 0)
+ return r;
+
+ if (b->input_fd != b->output_fd) {
+ r = fd_nonblock(b->output_fd, true);
+ if (r < 0)
+ return r;
+
+ r = fd_cloexec(b->output_fd, true);
+ if (r < 0)
+ return r;
+ }
+
+ if (fstat(b->input_fd, &st) < 0)
+ return -errno;
+
+ if (S_ISCHR(b->input_fd))
+ return bus_kernel_take_fd(b);
+ else
+ return bus_socket_take_fd(b);
+}
+
+int sd_bus_start(sd_bus *bus) {
+ int r;
+
+ if (!bus)
+ return -EINVAL;
+ if (bus->state != BUS_UNSET)
+ return -EPERM;
+
+ bus->state = BUS_OPENING;
+
+ if (bus->is_server && bus->bus_client)
+ return -EINVAL;
+
+ if (bus->input_fd >= 0)
+ r = bus_start_fd(bus);
+ else if (bus->address || bus->sockaddr.sa.sa_family != AF_UNSPEC || bus->exec_path || bus->kernel)
+ r = bus_start_address(bus);
+ else
+ return -EINVAL;
+
+ if (r < 0)
+ return r;
+
+ return bus_send_hello(bus);
+}
+
+int sd_bus_open_system(sd_bus **ret) {
+ const char *e;
+ sd_bus *b;
+ int r;
+
+ if (!ret)
+ return -EINVAL;
+
+ r = sd_bus_new(&b);
+ if (r < 0)
+ return r;
+
+ e = secure_getenv("DBUS_SYSTEM_BUS_ADDRESS");
+ if (e) {
+ r = sd_bus_set_address(b, e);
+ if (r < 0)
+ goto fail;
+ } else {
+ b->sockaddr.un.sun_family = AF_UNIX;
+ strncpy(b->sockaddr.un.sun_path, "/run/dbus/system_bus_socket", sizeof(b->sockaddr.un.sun_path));
+ b->sockaddr_size = offsetof(struct sockaddr_un, sun_path) + sizeof("/run/dbus/system_bus_socket") - 1;
+ }
+
+ b->bus_client = true;
+
+ r = sd_bus_start(b);
+ if (r < 0)
+ goto fail;
+
+ *ret = b;
+ return 0;
+
+fail:
+ bus_free(b);
+ return r;
+}
+
+int sd_bus_open_user(sd_bus **ret) {
+ const char *e;
+ sd_bus *b;
+ size_t l;
+ int r;
+
+ if (!ret)
+ return -EINVAL;
+
+ r = sd_bus_new(&b);
+ if (r < 0)
+ return r;
+
+ e = secure_getenv("DBUS_SESSION_BUS_ADDRESS");
+ if (e) {
+ r = sd_bus_set_address(b, e);
+ if (r < 0)
+ goto fail;
+ } else {
+ e = secure_getenv("XDG_RUNTIME_DIR");
+ if (!e) {
+ r = -ENOENT;
+ goto fail;
+ }
+
+ l = strlen(e);
+ if (l + 4 > sizeof(b->sockaddr.un.sun_path)) {
+ r = -E2BIG;
+ goto fail;
+ }
+
+ b->sockaddr.un.sun_family = AF_UNIX;
+ memcpy(mempcpy(b->sockaddr.un.sun_path, e, l), "/bus", 4);
+ b->sockaddr_size = offsetof(struct sockaddr_un, sun_path) + l + 4;
+ }
+
+ b->bus_client = true;
+
+ r = sd_bus_start(b);
+ if (r < 0)
+ goto fail;
+
+ *ret = b;
+ return 0;
+
+fail:
+ bus_free(b);
+ return r;
+}
+
+void sd_bus_close(sd_bus *bus) {
+ if (!bus)
+ return;
+
+ if (bus->input_fd >= 0)
+ close_nointr_nofail(bus->input_fd);
+ if (bus->output_fd >= 0 && bus->output_fd != bus->input_fd)
+ close_nointr_nofail(bus->output_fd);
+
+ bus->input_fd = bus->output_fd = -1;
+}
+
+sd_bus *sd_bus_ref(sd_bus *bus) {
+ if (!bus)
+ return NULL;
+
+ assert(bus->n_ref > 0);
+
+ bus->n_ref++;
+ return bus;
+}
+
+sd_bus *sd_bus_unref(sd_bus *bus) {
+ if (!bus)
+ return NULL;
+
+ assert(bus->n_ref > 0);
+ bus->n_ref--;
+
+ if (bus->n_ref <= 0)
+ bus_free(bus);
+
+ return NULL;
+}
+
+int sd_bus_is_open(sd_bus *bus) {
+ if (!bus)
+ return -EINVAL;
+
+ return bus->state != BUS_UNSET && bus->input_fd >= 0;
+}
+
+int sd_bus_can_send(sd_bus *bus, char type) {
+ int r;
+
+ if (!bus)
+ return -EINVAL;
+ if (bus->output_fd < 0)
+ return -ENOTCONN;
+
+ if (type == SD_BUS_TYPE_UNIX_FD) {
+ if (!bus->negotiate_fds)
+ return 0;
+
+ r = bus_ensure_running(bus);
+ if (r < 0)
+ return r;
+
+ return bus->can_fds;
+ }
+
+ return bus_type_is_valid(type);
+}
+
+int sd_bus_get_server_id(sd_bus *bus, sd_id128_t *server_id) {
+ int r;
+
+ if (!bus)
+ return -EINVAL;
+ if (!server_id)
+ return -EINVAL;
+
+ r = bus_ensure_running(bus);
+ if (r < 0)
+ return r;
+
+ *server_id = bus->server_id;
+ return 0;
+}
+
+static int bus_seal_message(sd_bus *b, sd_bus_message *m) {
+ assert(m);
+
+ if (m->header->version > b->message_version)
+ return -EPERM;
+
+ if (m->sealed)
+ return 0;
+
+ return bus_message_seal(m, ++b->serial);
+}
+
+static int dispatch_wqueue(sd_bus *bus) {
+ int r, ret = 0;
+
+ assert(bus);
+ assert(bus->state == BUS_RUNNING || bus->state == BUS_HELLO);
+
+ if (bus->output_fd < 0)
+ return -ENOTCONN;
+
+ while (bus->wqueue_size > 0) {
+
+ if (bus->is_kernel)
+ r = bus_kernel_write_message(bus, bus->wqueue[0]);
+ else
+ r = bus_socket_write_message(bus, bus->wqueue[0], &bus->windex);
+
+ if (r < 0) {
+ sd_bus_close(bus);
+ return r;
+ } else if (r == 0)
+ /* Didn't do anything this time */
+ return ret;
+ else if (bus->is_kernel || bus->windex >= BUS_MESSAGE_SIZE(bus->wqueue[0])) {
+ /* Fully written. Let's drop the entry from
+ * the queue.
+ *
+ * This isn't particularly optimized, but
+ * well, this is supposed to be our worst-case
+ * buffer only, and the socket buffer is
+ * supposed to be our primary buffer, and if
+ * it got full, then all bets are off
+ * anyway. */
+
+ sd_bus_message_unref(bus->wqueue[0]);
+ bus->wqueue_size --;
+ memmove(bus->wqueue, bus->wqueue + 1, sizeof(sd_bus_message*) * bus->wqueue_size);
+ bus->windex = 0;
+
+ ret = 1;
+ }
+ }
+
+ return ret;
+}
+
+static int dispatch_rqueue(sd_bus *bus, sd_bus_message **m) {
+ sd_bus_message *z = NULL;
+ int r, ret = 0;
+
+ assert(bus);
+ assert(m);
+ assert(bus->state == BUS_RUNNING || bus->state == BUS_HELLO);
+
+ if (bus->input_fd < 0)
+ return -ENOTCONN;
+
+ if (bus->rqueue_size > 0) {
+ /* Dispatch a queued message */
+
+ *m = bus->rqueue[0];
+ bus->rqueue_size --;
+ memmove(bus->rqueue, bus->rqueue + 1, sizeof(sd_bus_message*) * bus->rqueue_size);
+ return 1;
+ }
+
+ /* Try to read a new message */
+ do {
+ if (bus->is_kernel)
+ r = bus_kernel_read_message(bus, &z);
+ else
+ r = bus_socket_read_message(bus, &z);
+
+ if (r < 0) {
+ sd_bus_close(bus);
+ return r;
+ }
+ if (r == 0)
+ return ret;
+
+ r = 1;
+ } while (!z);
+
+ *m = z;
+ return 1;
+}
+
+int sd_bus_send(sd_bus *bus, sd_bus_message *m, uint64_t *serial) {
+ int r;
+
+ if (!bus)
+ return -EINVAL;
+ if (bus->state == BUS_UNSET)
+ return -ENOTCONN;
+ if (bus->output_fd < 0)
+ return -ENOTCONN;
+ if (!m)
+ return -EINVAL;
+
+ if (m->n_fds > 0) {
+ r = sd_bus_can_send(bus, SD_BUS_TYPE_UNIX_FD);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -ENOTSUP;
+ }
+
+ /* If the serial number isn't kept, then we know that no reply
+ * is expected */
+ if (!serial && !m->sealed)
+ m->header->flags |= SD_BUS_MESSAGE_NO_REPLY_EXPECTED;
+
+ r = bus_seal_message(bus, m);
+ if (r < 0)
+ return r;
+
+ /* If this is a reply and no reply was requested, then let's
+ * suppress this, if we can */
+ if (m->dont_send && !serial)
+ return 0;
+
+ if ((bus->state == BUS_RUNNING || bus->state == BUS_HELLO) && bus->wqueue_size <= 0) {
+ size_t idx = 0;
+
+ if (bus->is_kernel)
+ r = bus_kernel_write_message(bus, m);
+ else
+ r = bus_socket_write_message(bus, m, &idx);
+
+ if (r < 0) {
+ sd_bus_close(bus);
+ return r;
+ } else if (!bus->is_kernel && idx < BUS_MESSAGE_SIZE(m)) {
+ /* Wasn't fully written. So let's remember how
+ * much was written. Note that the first entry
+ * of the wqueue array is always allocated so
+ * that we always can remember how much was
+ * written. */
+ bus->wqueue[0] = sd_bus_message_ref(m);
+ bus->wqueue_size = 1;
+ bus->windex = idx;
+ }
+ } else {
+ sd_bus_message **q;
+
+ /* Just append it to the queue. */
+
+ if (bus->wqueue_size >= BUS_WQUEUE_MAX)
+ return -ENOBUFS;
+
+ q = realloc(bus->wqueue, sizeof(sd_bus_message*) * (bus->wqueue_size + 1));
+ if (!q)
+ return -ENOMEM;
+
+ bus->wqueue = q;
+ q[bus->wqueue_size ++] = sd_bus_message_ref(m);
+ }
+
+ if (serial)
+ *serial = BUS_MESSAGE_SERIAL(m);
+
+ return 0;
+}
+
+static usec_t calc_elapse(uint64_t usec) {
+ if (usec == (uint64_t) -1)
+ return 0;
+
+ if (usec == 0)
+ usec = BUS_DEFAULT_TIMEOUT;
+
+ return now(CLOCK_MONOTONIC) + usec;
+}
+
+static int timeout_compare(const void *a, const void *b) {
+ const struct reply_callback *x = a, *y = b;
+
+ if (x->timeout != 0 && y->timeout == 0)
+ return -1;
+
+ if (x->timeout == 0 && y->timeout != 0)
+ return 1;
+
+ if (x->timeout < y->timeout)
+ return -1;
+
+ if (x->timeout > y->timeout)
+ return 1;
+
+ return 0;
+}
+
+int sd_bus_send_with_reply(
+ sd_bus *bus,
+ sd_bus_message *m,
+ sd_bus_message_handler_t callback,
+ void *userdata,
+ uint64_t usec,
+ uint64_t *serial) {
+
+ struct reply_callback *c;
+ int r;
+
+ if (!bus)
+ return -EINVAL;
+ if (bus->state == BUS_UNSET)
+ return -ENOTCONN;
+ if (bus->output_fd < 0)
+ return -ENOTCONN;
+ if (!m)
+ return -EINVAL;
+ if (!callback)
+ return -EINVAL;
+ if (m->header->type != SD_BUS_MESSAGE_TYPE_METHOD_CALL)
+ return -EINVAL;
+ if (m->header->flags & SD_BUS_MESSAGE_NO_REPLY_EXPECTED)
+ return -EINVAL;
+
+ r = hashmap_ensure_allocated(&bus->reply_callbacks, uint64_hash_func, uint64_compare_func);
+ if (r < 0)
+ return r;
+
+ if (usec != (uint64_t) -1) {
+ r = prioq_ensure_allocated(&bus->reply_callbacks_prioq, timeout_compare);
+ if (r < 0)
+ return r;
+ }
+
+ r = bus_seal_message(bus, m);
+ if (r < 0)
+ return r;
+
+ c = new0(struct reply_callback, 1);
+ if (!c)
+ return -ENOMEM;
+
+ c->callback = callback;
+ c->userdata = userdata;
+ c->serial = BUS_MESSAGE_SERIAL(m);
+ c->timeout = calc_elapse(usec);
+
+ r = hashmap_put(bus->reply_callbacks, &c->serial, c);
+ if (r < 0) {
+ free(c);
+ return r;
+ }
+
+ if (c->timeout != 0) {
+ r = prioq_put(bus->reply_callbacks_prioq, c, &c->prioq_idx);
+ if (r < 0) {
+ c->timeout = 0;
+ sd_bus_send_with_reply_cancel(bus, c->serial);
+ return r;
+ }
+ }
+
+ r = sd_bus_send(bus, m, serial);
+ if (r < 0) {
+ sd_bus_send_with_reply_cancel(bus, c->serial);
+ return r;
+ }
+
+ return r;
+}
+
+int sd_bus_send_with_reply_cancel(sd_bus *bus, uint64_t serial) {
+ struct reply_callback *c;
+
+ if (!bus)
+ return -EINVAL;
+ if (serial == 0)
+ return -EINVAL;
+
+ c = hashmap_remove(bus->reply_callbacks, &serial);
+ if (!c)
+ return 0;
+
+ if (c->timeout != 0)
+ prioq_remove(bus->reply_callbacks_prioq, c, &c->prioq_idx);
+
+ free(c);
+ return 1;
+}
+
+int bus_ensure_running(sd_bus *bus) {
+ int r;
+
+ assert(bus);
+
+ if (bus->input_fd < 0)
+ return -ENOTCONN;
+ if (bus->state == BUS_UNSET)
+ return -ENOTCONN;
+
+ if (bus->state == BUS_RUNNING)
+ return 1;
+
+ for (;;) {
+ r = sd_bus_process(bus, NULL);
+ if (r < 0)
+ return r;
+ if (bus->state == BUS_RUNNING)
+ return 1;
+ if (r > 0)
+ continue;
+
+ r = sd_bus_wait(bus, (uint64_t) -1);
+ if (r < 0)
+ return r;
+ }
+}
+
+int sd_bus_send_with_reply_and_block(
+ sd_bus *bus,
+ sd_bus_message *m,
+ uint64_t usec,
+ sd_bus_error *error,
+ sd_bus_message **reply) {
+
+ int r;
+ usec_t timeout;
+ uint64_t serial;
+ bool room = false;
+
+ if (!bus)
+ return -EINVAL;
+ if (bus->output_fd < 0)
+ return -ENOTCONN;
+ if (bus->state == BUS_UNSET)
+ return -ENOTCONN;
+ if (!m)
+ return -EINVAL;
+ if (m->header->type != SD_BUS_MESSAGE_TYPE_METHOD_CALL)
+ return -EINVAL;
+ if (m->header->flags & SD_BUS_MESSAGE_NO_REPLY_EXPECTED)
+ return -EINVAL;
+ if (bus_error_is_dirty(error))
+ return -EINVAL;
+
+ r = bus_ensure_running(bus);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_send(bus, m, &serial);
+ if (r < 0)
+ return r;
+
+ timeout = calc_elapse(usec);
+
+ for (;;) {
+ usec_t left;
+ sd_bus_message *incoming = NULL;
+
+ if (!room) {
+ sd_bus_message **q;
+
+ if (bus->rqueue_size >= BUS_RQUEUE_MAX)
+ return -ENOBUFS;
+
+ /* Make sure there's room for queuing this
+ * locally, before we read the message */
+
+ q = realloc(bus->rqueue, (bus->rqueue_size + 1) * sizeof(sd_bus_message*));
+ if (!q)
+ return -ENOMEM;
+
+ bus->rqueue = q;
+ room = true;
+ }
+
+ if (bus->is_kernel)
+ r = bus_kernel_read_message(bus, &incoming);
+ else
+ r = bus_socket_read_message(bus, &incoming);
+ if (r < 0)
+ return r;
+ if (incoming) {
+
+ if (incoming->reply_serial == serial) {
+ /* Found a match! */
+
+ if (incoming->header->type == SD_BUS_MESSAGE_TYPE_METHOD_RETURN) {
+
+ if (reply)
+ *reply = incoming;
+ else
+ sd_bus_message_unref(incoming);
+
+ return 0;
+ }
+
+ if (incoming->header->type == SD_BUS_MESSAGE_TYPE_METHOD_ERROR) {
+ int k;
+
+ r = sd_bus_error_copy(error, &incoming->error);
+ if (r < 0) {
+ sd_bus_message_unref(incoming);
+ return r;
+ }
+
+ k = bus_error_to_errno(&incoming->error);
+ sd_bus_message_unref(incoming);
+ return k;
+ }
+
+ sd_bus_message_unref(incoming);
+ return -EIO;
+ }
+
+ /* There's already guaranteed to be room for
+ * this, so need to resize things here */
+ bus->rqueue[bus->rqueue_size ++] = incoming;
+ room = false;
+
+ /* Try to read more, right-away */
+ continue;
+ }
+ if (r != 0)
+ continue;
+
+ if (timeout > 0) {
+ usec_t n;
+
+ n = now(CLOCK_MONOTONIC);
+ if (n >= timeout)
+ return -ETIMEDOUT;
+
+ left = timeout - n;
+ } else
+ left = (uint64_t) -1;
+
+ r = bus_poll(bus, true, left);
+ if (r < 0)
+ return r;
+
+ r = dispatch_wqueue(bus);
+ if (r < 0)
+ return r;
+ }
+}
+
+int sd_bus_get_fd(sd_bus *bus) {
+ if (!bus)
+ return -EINVAL;
+ if (bus->input_fd < 0)
+ return -ENOTCONN;
+ if (bus->input_fd != bus->output_fd)
+ return -EPERM;
+
+ return bus->input_fd;
+}
+
+int sd_bus_get_events(sd_bus *bus) {
+ int flags = 0;
+
+ if (!bus)
+ return -EINVAL;
+ if (bus->state == BUS_UNSET)
+ return -ENOTCONN;
+ if (bus->input_fd < 0)
+ return -ENOTCONN;
+
+ if (bus->state == BUS_OPENING)
+ flags |= POLLOUT;
+ else if (bus->state == BUS_AUTHENTICATING) {
+
+ if (bus_socket_auth_needs_write(bus))
+ flags |= POLLOUT;
+
+ flags |= POLLIN;
+
+ } else if (bus->state == BUS_RUNNING || bus->state == BUS_HELLO) {
+ if (bus->rqueue_size <= 0)
+ flags |= POLLIN;
+ if (bus->wqueue_size > 0)
+ flags |= POLLOUT;
+ }
+
+ return flags;
+}
+
+int sd_bus_get_timeout(sd_bus *bus, uint64_t *timeout_usec) {
+ struct reply_callback *c;
+
+ if (!bus)
+ return -EINVAL;
+ if (!timeout_usec)
+ return -EINVAL;
+ if (bus->state == BUS_UNSET)
+ return -ENOTCONN;
+ if (bus->input_fd < 0)
+ return -ENOTCONN;
+
+ if (bus->state == BUS_AUTHENTICATING) {
+ *timeout_usec = bus->auth_timeout;
+ return 1;
+ }
+
+ if (bus->state != BUS_RUNNING && bus->state != BUS_HELLO) {
+ *timeout_usec = (uint64_t) -1;
+ return 0;
+ }
+
+ c = prioq_peek(bus->reply_callbacks_prioq);
+ if (!c) {
+ *timeout_usec = (uint64_t) -1;
+ return 0;
+ }
+
+ *timeout_usec = c->timeout;
+ return 1;
+}
+
+static int process_timeout(sd_bus *bus) {
+ struct reply_callback *c;
+ usec_t n;
+ int r;
+
+ assert(bus);
+
+ c = prioq_peek(bus->reply_callbacks_prioq);
+ if (!c)
+ return 0;
+
+ n = now(CLOCK_MONOTONIC);
+ if (c->timeout > n)
+ return 0;
+
+ assert_se(prioq_pop(bus->reply_callbacks_prioq) == c);
+ hashmap_remove(bus->reply_callbacks, &c->serial);
+
+ r = c->callback(bus, ETIMEDOUT, NULL, c->userdata);
+ free(c);
+
+ return r < 0 ? r : 1;
+}
+
+static int process_hello(sd_bus *bus, sd_bus_message *m) {
+ assert(bus);
+ assert(m);
+
+ if (bus->state != BUS_HELLO)
+ return 0;
+
+ /* Let's make sure the first message on the bus is the HELLO
+ * reply. But note that we don't actually parse the message
+ * here (we leave that to the usual handling), we just verify
+ * we don't let any earlier msg through. */
+
+ if (m->header->type != SD_BUS_MESSAGE_TYPE_METHOD_RETURN &&
+ m->header->type != SD_BUS_MESSAGE_TYPE_METHOD_ERROR)
+ return -EIO;
+
+ if (m->reply_serial != bus->hello_serial)
+ return -EIO;
+
+ return 0;
+}
+
+static int process_reply(sd_bus *bus, sd_bus_message *m) {
+ struct reply_callback *c;
+ int r;
+
+ assert(bus);
+ assert(m);
+
+ if (m->header->type != SD_BUS_MESSAGE_TYPE_METHOD_RETURN &&
+ m->header->type != SD_BUS_MESSAGE_TYPE_METHOD_ERROR)
+ return 0;
+
+ c = hashmap_remove(bus->reply_callbacks, &m->reply_serial);
+ if (!c)
+ return 0;
+
+ if (c->timeout != 0)
+ prioq_remove(bus->reply_callbacks_prioq, c, &c->prioq_idx);
+
+ r = sd_bus_message_rewind(m, true);
+ if (r < 0)
+ return r;
+
+ r = c->callback(bus, 0, m, c->userdata);
+ free(c);
+
+ return r;
+}
+
+static int process_filter(sd_bus *bus, sd_bus_message *m) {
+ struct filter_callback *l;
+ int r;
+
+ assert(bus);
+ assert(m);
+
+ do {
+ bus->filter_callbacks_modified = false;
+
+ LIST_FOREACH(callbacks, l, bus->filter_callbacks) {
+
+ if (bus->filter_callbacks_modified)
+ break;
+
+ /* Don't run this more than once per iteration */
+ if (l->last_iteration == bus->iteration_counter)
+ continue;
+
+ l->last_iteration = bus->iteration_counter;
+
+ r = sd_bus_message_rewind(m, true);
+ if (r < 0)
+ return r;
+
+ r = l->callback(bus, 0, m, l->userdata);
+ if (r != 0)
+ return r;
+
+ }
+
+ } while (bus->filter_callbacks_modified);
+
+ return 0;
+}
+
+static int process_match(sd_bus *bus, sd_bus_message *m) {
+ int r;
+
+ assert(bus);
+ assert(m);
+
+ do {
+ bus->match_callbacks_modified = false;
+
+ r = bus_match_run(bus, &bus->match_callbacks, 0, m);
+ if (r != 0)
+ return r;
+
+ } while (bus->match_callbacks_modified);
+
+ return 0;
+}
+
+static int process_builtin(sd_bus *bus, sd_bus_message *m) {
+ _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+ int r;
+
+ assert(bus);
+ assert(m);
+
+ if (m->header->type != SD_BUS_MESSAGE_TYPE_METHOD_CALL)
+ return 0;
+
+ if (!streq_ptr(m->interface, "org.freedesktop.DBus.Peer"))
+ return 0;
+
+ if (m->header->flags & SD_BUS_MESSAGE_NO_REPLY_EXPECTED)
+ return 1;
+
+ if (streq_ptr(m->member, "Ping"))
+ r = sd_bus_message_new_method_return(bus, m, &reply);
+ else if (streq_ptr(m->member, "GetMachineId")) {
+ sd_id128_t id;
+ char sid[33];
+
+ r = sd_id128_get_machine(&id);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_new_method_return(bus, m, &reply);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_append(reply, "s", sd_id128_to_string(id, sid));
+ } else {
+ _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+
+ sd_bus_error_set(&error,
+ "org.freedesktop.DBus.Error.UnknownMethod",
+ "Unknown method '%s' on interface '%s'.", m->member, m->interface);
+
+ r = sd_bus_message_new_method_error(bus, m, &error, &reply);
+ }
+
+ if (r < 0)
+ return r;
+
+ r = sd_bus_send(bus, reply, NULL);
+ if (r < 0)
+ return r;
+
+ return 1;
+}
+
+static int process_object(sd_bus *bus, sd_bus_message *m) {
+ _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+ struct object_callback *c;
+ int r;
+ bool found = false;
+ size_t pl;
+
+ assert(bus);
+ assert(m);
+
+ if (m->header->type != SD_BUS_MESSAGE_TYPE_METHOD_CALL)
+ return 0;
+
+ if (hashmap_isempty(bus->object_callbacks))
+ return 0;
+
+ pl = strlen(m->path);
+
+ do {
+ char p[pl+1];
+
+ bus->object_callbacks_modified = false;
+
+ c = hashmap_get(bus->object_callbacks, m->path);
+ if (c && c->last_iteration != bus->iteration_counter) {
+
+ c->last_iteration = bus->iteration_counter;
+
+ r = sd_bus_message_rewind(m, true);
+ if (r < 0)
+ return r;
+
+ r = c->callback(bus, 0, m, c->userdata);
+ if (r != 0)
+ return r;
+
+ found = true;
+ }
+
+ /* Look for fallback prefixes */
+ strcpy(p, m->path);
+ for (;;) {
+ char *e;
+
+ if (bus->object_callbacks_modified)
+ break;
+
+ e = strrchr(p, '/');
+ if (e == p || !e)
+ break;
+
+ *e = 0;
+
+ c = hashmap_get(bus->object_callbacks, p);
+ if (c && c->last_iteration != bus->iteration_counter && c->is_fallback) {
+
+ c->last_iteration = bus->iteration_counter;
+
+ r = sd_bus_message_rewind(m, true);
+ if (r < 0)
+ return r;
+
+ r = c->callback(bus, 0, m, c->userdata);
+ if (r != 0)
+ return r;
+
+ found = true;
+ }
+ }
+
+ } while (bus->object_callbacks_modified);
+
+ /* We found some handlers but none wanted to take this, then
+ * return this -- with one exception, we can handle
+ * introspection minimally ourselves */
+ if (!found || sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect"))
+ return 0;
+
+ sd_bus_error_set(&error,
+ "org.freedesktop.DBus.Error.UnknownMethod",
+ "Unknown method '%s' or interface '%s'.", m->member, m->interface);
+
+ r = sd_bus_message_new_method_error(bus, m, &error, &reply);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_send(bus, reply, NULL);
+ if (r < 0)
+ return r;
+
+ return 1;
+}
+
+static int process_introspect(sd_bus *bus, sd_bus_message *m) {
+ _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+ _cleanup_free_ char *introspection = NULL;
+ _cleanup_set_free_free_ Set *s = NULL;
+ _cleanup_fclose_ FILE *f = NULL;
+ struct object_callback *c;
+ Iterator i;
+ size_t size = 0;
+ char *node;
+ int r;
+
+ assert(bus);
+ assert(m);
+
+ if (!sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect"))
+ return 0;
+
+ if (!m->path)
+ return 0;
+
+ s = set_new(string_hash_func, string_compare_func);
+ if (!s)
+ return -ENOMEM;
+
+ HASHMAP_FOREACH(c, bus->object_callbacks, i) {
+ const char *e;
+ char *a, *p;
+
+ if (streq(c->path, "/"))
+ continue;
+
+ if (streq(m->path, "/"))
+ e = c->path;
+ else {
+ e = startswith(c->path, m->path);
+ if (!e || *e != '/')
+ continue;
+ }
+
+ a = strdup(e+1);
+ if (!a)
+ return -ENOMEM;
+
+ p = strchr(a, '/');
+ if (p)
+ *p = 0;
+
+ r = set_consume(s, a);
+ if (r < 0 && r != -EEXIST)
+ return r;
+ }
+
+ f = open_memstream(&introspection, &size);
+ if (!f)
+ return -ENOMEM;
+
+ fputs(SD_BUS_INTROSPECT_DOCTYPE, f);
+ fputs("<node>\n", f);
+ fputs(SD_BUS_INTROSPECT_INTERFACE_PEER, f);
+ fputs(SD_BUS_INTROSPECT_INTERFACE_INTROSPECTABLE, f);
+
+ while ((node = set_steal_first(s))) {
+ fprintf(f, " <node name=\"%s\"/>\n", node);
+ free(node);
+ }
+
+ fputs("</node>\n", f);
+
+ fflush(f);
+
+ if (ferror(f))
+ return -ENOMEM;
+
+ r = sd_bus_message_new_method_return(bus, m, &reply);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_append(reply, "s", introspection);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_send(bus, reply, NULL);
+ if (r < 0)
+ return r;
+
+ return 1;
+}
+
+static int process_message(sd_bus *bus, sd_bus_message *m) {
+ int r;
+
+ assert(bus);
+ assert(m);
+
+ bus->iteration_counter++;
+
+ r = process_hello(bus, m);
+ if (r != 0)
+ return r;
+
+ r = process_reply(bus, m);
+ if (r != 0)
+ return r;
+
+ r = process_filter(bus, m);
+ if (r != 0)
+ return r;
+
+ r = process_match(bus, m);
+ if (r != 0)
+ return r;
+
+ r = process_builtin(bus, m);
+ if (r != 0)
+ return r;
+
+ r = process_object(bus, m);
+ if (r != 0)
+ return r;
+
+ return process_introspect(bus, m);
+}
+
+static int process_running(sd_bus *bus, sd_bus_message **ret) {
+ _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ int r;
+
+ assert(bus);
+ assert(bus->state == BUS_RUNNING || bus->state == BUS_HELLO);
+
+ r = process_timeout(bus);
+ if (r != 0)
+ goto null_message;
+
+ r = dispatch_wqueue(bus);
+ if (r != 0)
+ goto null_message;
+
+ r = dispatch_rqueue(bus, &m);
+ if (r < 0)
+ return r;
+ if (!m)
+ goto null_message;
+
+ r = process_message(bus, m);
+ if (r != 0)
+ goto null_message;
+
+ if (ret) {
+ r = sd_bus_message_rewind(m, true);
+ if (r < 0)
+ return r;
+
+ *ret = m;
+ m = NULL;
+ return 1;
+ }
+
+ if (m->header->type == SD_BUS_MESSAGE_TYPE_METHOD_CALL) {
+ _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+ _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+
+ sd_bus_error_set(&error, "org.freedesktop.DBus.Error.UnknownObject", "Unknown object '%s'.", m->path);
+
+ r = sd_bus_message_new_method_error(bus, m, &error, &reply);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_send(bus, reply, NULL);
+ if (r < 0)
+ return r;
+ }
+
+ return 1;
+
+null_message:
+ if (r >= 0 && ret)
+ *ret = NULL;
+
+ return r;
+}
+
+int sd_bus_process(sd_bus *bus, sd_bus_message **ret) {
+ int r;
+
+ /* Returns 0 when we didn't do anything. This should cause the
+ * caller to invoke sd_bus_wait() before returning the next
+ * time. Returns > 0 when we did something, which possibly
+ * means *ret is filled in with an unprocessed message. */
+
+ if (!bus)
+ return -EINVAL;
+ if (bus->input_fd < 0)
+ return -ENOTCONN;
+
+ /* We don't allow recursively invoking sd_bus_process(). */
+ if (bus->processing)
+ return -EBUSY;
+
+ switch (bus->state) {
+
+ case BUS_UNSET:
+ return -ENOTCONN;
+
+ case BUS_OPENING:
+ r = bus_socket_process_opening(bus);
+ if (r < 0)
+ return r;
+ if (ret)
+ *ret = NULL;
+ return r;
+
+ case BUS_AUTHENTICATING:
+
+ r = bus_socket_process_authenticating(bus);
+ if (r < 0)
+ return r;
+ if (ret)
+ *ret = NULL;
+ return r;
+
+ case BUS_RUNNING:
+ case BUS_HELLO:
+
+ bus->processing = true;
+ r = process_running(bus, ret);
+ bus->processing = false;
+
+ return r;
+ }
+
+ assert_not_reached("Unknown state");
+}
+
+static int bus_poll(sd_bus *bus, bool need_more, uint64_t timeout_usec) {
+ struct pollfd p[2] = {};
+ int r, e, n;
+ struct timespec ts;
+ usec_t until, m;
+
+ assert(bus);
+
+ if (bus->input_fd < 0)
+ return -ENOTCONN;
+
+ e = sd_bus_get_events(bus);
+ if (e < 0)
+ return e;
+
+ if (need_more)
+ e |= POLLIN;
+
+ r = sd_bus_get_timeout(bus, &until);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ m = (uint64_t) -1;
+ else {
+ usec_t nw;
+ nw = now(CLOCK_MONOTONIC);
+ m = until > nw ? until - nw : 0;
+ }
+
+ if (timeout_usec != (uint64_t) -1 && (m == (uint64_t) -1 || timeout_usec < m))
+ m = timeout_usec;
+
+ p[0].fd = bus->input_fd;
+ if (bus->output_fd == bus->input_fd) {
+ p[0].events = e;
+ n = 1;
+ } else {
+ p[0].events = e & POLLIN;
+ p[1].fd = bus->output_fd;
+ p[1].events = e & POLLOUT;
+ n = 2;
+ }
+
+ r = ppoll(p, n, m == (uint64_t) -1 ? NULL : timespec_store(&ts, m), NULL);
+ if (r < 0)
+ return -errno;
+
+ return r > 0 ? 1 : 0;
+}
+
+int sd_bus_wait(sd_bus *bus, uint64_t timeout_usec) {
+
+ if (!bus)
+ return -EINVAL;
+ if (bus->state == BUS_UNSET)
+ return -ENOTCONN;
+ if (bus->input_fd < 0)
+ return -ENOTCONN;
+ if (bus->rqueue_size > 0)
+ return 0;
+
+ return bus_poll(bus, false, timeout_usec);
+}
+
+int sd_bus_flush(sd_bus *bus) {
+ int r;
+
+ if (!bus)
+ return -EINVAL;
+ if (bus->state == BUS_UNSET)
+ return -ENOTCONN;
+ if (bus->output_fd < 0)
+ return -ENOTCONN;
+
+ r = bus_ensure_running(bus);
+ if (r < 0)
+ return r;
+
+ if (bus->wqueue_size <= 0)
+ return 0;
+
+ for (;;) {
+ r = dispatch_wqueue(bus);
+ if (r < 0)
+ return r;
+
+ if (bus->wqueue_size <= 0)
+ return 0;
+
+ r = bus_poll(bus, false, (uint64_t) -1);
+ if (r < 0)
+ return r;
+ }
+}
+
+int sd_bus_add_filter(sd_bus *bus, sd_bus_message_handler_t callback, void *userdata) {
+ struct filter_callback *f;
+
+ if (!bus)
+ return -EINVAL;
+ if (!callback)
+ return -EINVAL;
+
+ f = new0(struct filter_callback, 1);
+ if (!f)
+ return -ENOMEM;
+ f->callback = callback;
+ f->userdata = userdata;
+
+ bus->filter_callbacks_modified = true;
+ LIST_PREPEND(struct filter_callback, callbacks, bus->filter_callbacks, f);
+ return 0;
+}
+
+int sd_bus_remove_filter(sd_bus *bus, sd_bus_message_handler_t callback, void *userdata) {
+ struct filter_callback *f;
+
+ if (!bus)
+ return -EINVAL;
+ if (!callback)
+ return -EINVAL;
+
+ LIST_FOREACH(callbacks, f, bus->filter_callbacks) {
+ if (f->callback == callback && f->userdata == userdata) {
+ bus->filter_callbacks_modified = true;
+ LIST_REMOVE(struct filter_callback, callbacks, bus->filter_callbacks, f);
+ free(f);
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static int bus_add_object(
+ sd_bus *bus,
+ bool fallback,
+ const char *path,
+ sd_bus_message_handler_t callback,
+ void *userdata) {
+
+ struct object_callback *c;
+ int r;
+
+ if (!bus)
+ return -EINVAL;
+ if (!path)
+ return -EINVAL;
+ if (!callback)
+ return -EINVAL;
+
+ r = hashmap_ensure_allocated(&bus->object_callbacks, string_hash_func, string_compare_func);
+ if (r < 0)
+ return r;
+
+ c = new0(struct object_callback, 1);
+ if (!c)
+ return -ENOMEM;
+
+ c->path = strdup(path);
+ if (!c->path) {
+ free(c);
+ return -ENOMEM;
+ }
+
+ c->callback = callback;
+ c->userdata = userdata;
+ c->is_fallback = fallback;
+
+ bus->object_callbacks_modified = true;
+ r = hashmap_put(bus->object_callbacks, c->path, c);
+ if (r < 0) {
+ free(c->path);
+ free(c);
+ return r;
+ }
+
+ return 0;
+}
+
+static int bus_remove_object(
+ sd_bus *bus,
+ bool fallback,
+ const char *path,
+ sd_bus_message_handler_t callback,
+ void *userdata) {
+
+ struct object_callback *c;
+
+ if (!bus)
+ return -EINVAL;
+ if (!path)
+ return -EINVAL;
+ if (!callback)
+ return -EINVAL;
+
+ c = hashmap_get(bus->object_callbacks, path);
+ if (!c)
+ return 0;
+
+ if (c->callback != callback || c->userdata != userdata || c->is_fallback != fallback)
+ return 0;
+
+ bus->object_callbacks_modified = true;
+ assert_se(c == hashmap_remove(bus->object_callbacks, c->path));
+
+ free(c->path);
+ free(c);
+
+ return 1;
+}
+
+int sd_bus_add_object(sd_bus *bus, const char *path, sd_bus_message_handler_t callback, void *userdata) {
+ return bus_add_object(bus, false, path, callback, userdata);
+}
+
+int sd_bus_remove_object(sd_bus *bus, const char *path, sd_bus_message_handler_t callback, void *userdata) {
+ return bus_remove_object(bus, false, path, callback, userdata);
+}
+
+int sd_bus_add_fallback(sd_bus *bus, const char *prefix, sd_bus_message_handler_t callback, void *userdata) {
+ return bus_add_object(bus, true, prefix, callback, userdata);
+}
+
+int sd_bus_remove_fallback(sd_bus *bus, const char *prefix, sd_bus_message_handler_t callback, void *userdata) {
+ return bus_remove_object(bus, true, prefix, callback, userdata);
+}
+
+int sd_bus_add_match(sd_bus *bus, const char *match, sd_bus_message_handler_t callback, void *userdata) {
+ int r = 0;
+
+ if (!bus)
+ return -EINVAL;
+ if (!match)
+ return -EINVAL;
+
+ if (bus->bus_client) {
+ r = bus_add_match_internal(bus, match);
+ if (r < 0)
+ return r;
+ }
+
+ if (callback) {
+ bus->match_callbacks_modified = true;
+ r = bus_match_add(&bus->match_callbacks, match, callback, userdata, NULL);
+ if (r < 0) {
+
+ if (bus->bus_client)
+ bus_remove_match_internal(bus, match);
+ }
+ }
+
+ return r;
+}
+
+int sd_bus_remove_match(sd_bus *bus, const char *match, sd_bus_message_handler_t callback, void *userdata) {
+ int r = 0, q = 0;
+
+ if (!bus)
+ return -EINVAL;
+ if (!match)
+ return -EINVAL;
+
+ if (bus->bus_client)
+ r = bus_remove_match_internal(bus, match);
+
+ if (callback) {
+ bus->match_callbacks_modified = true;
+ q = bus_match_remove(&bus->match_callbacks, match, callback, userdata);
+ }
+
+ if (r < 0)
+ return r;
+ return q;
+}
+
+int sd_bus_emit_signal(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *member,
+ const char *types, ...) {
+
+ _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ va_list ap;
+ int r;
+
+ if (!bus)
+ return -EINVAL;
+
+ r = sd_bus_message_new_signal(bus, path, interface, member, &m);
+ if (r < 0)
+ return r;
+
+ va_start(ap, types);
+ r = bus_message_append_ap(m, types, ap);
+ va_end(ap);
+ if (r < 0)
+ return r;
+
+ return sd_bus_send(bus, m, NULL);
+}
+
+int sd_bus_call_method(
+ sd_bus *bus,
+ const char *destination,
+ const char *path,
+ const char *interface,
+ const char *member,
+ sd_bus_error *error,
+ sd_bus_message **reply,
+ const char *types, ...) {
+
+ _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ va_list ap;
+ int r;
+
+ if (!bus)
+ return -EINVAL;
+
+ r = sd_bus_message_new_method_call(bus, destination, path, interface, member, &m);
+ if (r < 0)
+ return r;
+
+ va_start(ap, types);
+ r = bus_message_append_ap(m, types, ap);
+ va_end(ap);
+ if (r < 0)
+ return r;
+
+ return sd_bus_send_with_reply_and_block(bus, m, 0, error, reply);
+}
+
+int sd_bus_reply_method_return(
+ sd_bus *bus,
+ sd_bus_message *call,
+ const char *types, ...) {
+
+ _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ va_list ap;
+ int r;
+
+ if (!bus)
+ return -EINVAL;
+ if (!call)
+ return -EINVAL;
+ if (!call->sealed)
+ return -EPERM;
+ if (call->header->type != SD_BUS_MESSAGE_TYPE_METHOD_CALL)
+ return -EINVAL;
+
+ if (call->header->flags & SD_BUS_MESSAGE_NO_REPLY_EXPECTED)
+ return 0;
+
+ r = sd_bus_message_new_method_return(bus, call, &m);
+ if (r < 0)
+ return r;
+
+ va_start(ap, types);
+ r = bus_message_append_ap(m, types, ap);
+ va_end(ap);
+ if (r < 0)
+ return r;
+
+ return sd_bus_send(bus, m, NULL);
+}
+
+int sd_bus_reply_method_error(
+ sd_bus *bus,
+ sd_bus_message *call,
+ const sd_bus_error *e) {
+
+ _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ int r;
+
+ if (!bus)
+ return -EINVAL;
+ if (!call)
+ return -EINVAL;
+ if (!call->sealed)
+ return -EPERM;
+ if (call->header->type != SD_BUS_MESSAGE_TYPE_METHOD_CALL)
+ return -EINVAL;
+ if (!sd_bus_error_is_set(e))
+ return -EINVAL;
+
+ if (call->header->flags & SD_BUS_MESSAGE_NO_REPLY_EXPECTED)
+ return 0;
+
+ r = sd_bus_message_new_method_error(bus, call, e, &m);
+ if (r < 0)
+ return r;
+
+ return sd_bus_send(bus, m, NULL);
+}
diff --git a/src/libsystemd-bus/test-bus-chat.c b/src/libsystemd-bus/test-bus-chat.c
new file mode 100644
index 0000000000..f457c8f88a
--- /dev/null
+++ b/src/libsystemd-bus/test-bus-chat.c
@@ -0,0 +1,576 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ 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 <http://www.gnu.org/licenses/>.
+***/
+
+#include <assert.h>
+#include <stdlib.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "log.h"
+#include "util.h"
+#include "macro.h"
+
+#include "sd-bus.h"
+#include "bus-message.h"
+#include "bus-error.h"
+#include "bus-match.h"
+#include "bus-internal.h"
+
+static int match_callback(sd_bus *bus, int error, sd_bus_message *m, void *userdata) {
+ log_info("Match triggered! interface=%s member=%s", strna(sd_bus_message_get_interface(m)), strna(sd_bus_message_get_member(m)));
+ return 0;
+}
+
+static int object_callback(sd_bus *bus, int error, sd_bus_message *m, void *userdata) {
+ int r;
+
+ assert(bus);
+
+ if (error != 0)
+ return 0;
+
+ if (sd_bus_message_is_method_call(m, "org.object.test", "Foobar")) {
+ log_info("Invoked Foobar() on %s", sd_bus_message_get_path(m));
+
+ r = sd_bus_reply_method_return(bus, m, NULL);
+ if (r < 0) {
+ log_error("Failed to send reply: %s", strerror(-r));
+ return r;
+ }
+
+ return 1;
+ }
+
+ return 0;
+}
+
+static int server_init(sd_bus **_bus) {
+ sd_bus *bus = NULL;
+ sd_id128_t id;
+ int r;
+ const char *unique;
+
+ assert(_bus);
+
+ r = sd_bus_open_user(&bus);
+ if (r < 0) {
+ log_error("Failed to connect to user bus: %s", strerror(-r));
+ goto fail;
+ }
+
+ r = sd_bus_get_server_id(bus, &id);
+ if (r < 0) {
+ log_error("Failed to get server ID: %s", strerror(-r));
+ goto fail;
+ }
+
+ r = sd_bus_get_unique_name(bus, &unique);
+ if (r < 0) {
+ log_error("Failed to get unique name: %s", strerror(-r));
+ goto fail;
+ }
+
+ log_info("Peer ID is " SD_ID128_FORMAT_STR ".", SD_ID128_FORMAT_VAL(id));
+ log_info("Unique ID: %s", unique);
+ log_info("Can send file handles: %i", sd_bus_can_send(bus, 'h'));
+
+ r = sd_bus_request_name(bus, "org.freedesktop.systemd.test", 0);
+ if (r < 0) {
+ log_error("Failed to acquire name: %s", strerror(-r));
+ goto fail;
+ }
+
+ r = sd_bus_add_fallback(bus, "/foo/bar", object_callback, NULL);
+ if (r < 0) {
+ log_error("Failed to add object: %s", strerror(-r));
+ goto fail;
+ }
+
+ r = sd_bus_add_match(bus, "type='signal',interface='foo.bar',member='Notify'", match_callback, NULL);
+ if (r < 0) {
+ log_error("Failed to add match: %s", strerror(-r));
+ goto fail;
+ }
+
+ r = sd_bus_add_match(bus, "type='signal',interface='org.freedesktop.DBus',member='NameOwnerChanged'", match_callback, NULL);
+ if (r < 0) {
+ log_error("Failed to add match: %s", strerror(-r));
+ goto fail;
+ }
+
+ bus_match_dump(&bus->match_callbacks, 0);
+
+ *_bus = bus;
+ return 0;
+
+fail:
+ if (bus)
+ sd_bus_unref(bus);
+
+ return r;
+}
+
+static int server(sd_bus *bus) {
+ int r;
+ bool client1_gone = false, client2_gone = false;
+
+ while (!client1_gone || !client2_gone) {
+ _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ pid_t pid = 0;
+ const char *label = NULL;
+
+ r = sd_bus_process(bus, &m);
+ if (r < 0) {
+ log_error("Failed to process requests: %s", strerror(-r));
+ goto fail;
+ }
+
+ if (r == 0) {
+ r = sd_bus_wait(bus, (uint64_t) -1);
+ if (r < 0) {
+ log_error("Failed to wait: %s", strerror(-r));
+ goto fail;
+ }
+
+ continue;
+ }
+
+ if (!m)
+ continue;
+
+ sd_bus_message_get_pid(m, &pid);
+ sd_bus_message_get_selinux_context(m, &label);
+ log_info("Got message! member=%s pid=%lu label=%s",
+ strna(sd_bus_message_get_member(m)),
+ (unsigned long) pid,
+ strna(label));
+ /* bus_message_dump(m); */
+ /* sd_bus_message_rewind(m, true); */
+
+ if (sd_bus_message_is_method_call(m, "org.freedesktop.systemd.test", "LowerCase")) {
+ const char *hello;
+ _cleanup_free_ char *lowercase = NULL;
+
+ r = sd_bus_message_read(m, "s", &hello);
+ if (r < 0) {
+ log_error("Failed to get parameter: %s", strerror(-r));
+ goto fail;
+ }
+
+ lowercase = strdup(hello);
+ if (!lowercase) {
+ r = log_oom();
+ goto fail;
+ }
+
+ ascii_strlower(lowercase);
+
+ r = sd_bus_reply_method_return(bus, m, "s", lowercase);
+ if (r < 0) {
+ log_error("Failed to send reply: %s", strerror(-r));
+ goto fail;
+ }
+ } else if (sd_bus_message_is_method_call(m, "org.freedesktop.systemd.test", "ExitClient1")) {
+
+ r = sd_bus_reply_method_return(bus, m, NULL);
+ if (r < 0) {
+ log_error("Failed to send reply: %s", strerror(-r));
+ goto fail;
+ }
+
+ client1_gone = true;
+ } else if (sd_bus_message_is_method_call(m, "org.freedesktop.systemd.test", "ExitClient2")) {
+
+ r = sd_bus_reply_method_return(bus, m, NULL);
+ if (r < 0) {
+ log_error("Failed to send reply: %s", strerror(-r));
+ goto fail;
+ }
+
+ client2_gone = true;
+ } else if (sd_bus_message_is_method_call(m, "org.freedesktop.systemd.test", "Slow")) {
+
+ sleep(1);
+
+ r = sd_bus_reply_method_return(bus, m, NULL);
+ if (r < 0) {
+ log_error("Failed to send reply: %s", strerror(-r));
+ goto fail;
+ }
+
+ } else if (sd_bus_message_is_method_call(m, "org.freedesktop.systemd.test", "FileDescriptor")) {
+ int fd;
+ static const char x = 'X';
+
+ r = sd_bus_message_read(m, "h", &fd);
+ if (r < 0) {
+ log_error("Failed to get parameter: %s", strerror(-r));
+ goto fail;
+ }
+
+ if (write(fd, &x, 1) < 0) {
+ log_error("Failed to write to fd: %m");
+ close_nointr_nofail(fd);
+ goto fail;
+ }
+
+ r = sd_bus_reply_method_return(bus, m, NULL);
+ if (r < 0) {
+ log_error("Failed to send reply: %s", strerror(-r));
+ goto fail;
+ }
+
+ } else if (sd_bus_message_is_method_call(m, NULL, NULL)) {
+
+ r = sd_bus_reply_method_error(
+ bus, m,
+ &SD_BUS_ERROR_MAKE("org.freedesktop.DBus.Error.UnknownMethod", "Unknown method."));
+ if (r < 0) {
+ log_error("Failed to send reply: %s", strerror(-r));
+ goto fail;
+ }
+ }
+ }
+
+ r = 0;
+
+fail:
+ if (bus) {
+ sd_bus_flush(bus);
+ sd_bus_unref(bus);
+ }
+
+ return r;
+}
+
+static void* client1(void*p) {
+ _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+ sd_bus *bus = NULL;
+ sd_bus_error error = SD_BUS_ERROR_NULL;
+ const char *hello;
+ int r;
+ int pp[2] = { -1, -1 };
+ char x;
+
+ r = sd_bus_open_user(&bus);
+ if (r < 0) {
+ log_error("Failed to connect to user bus: %s", strerror(-r));
+ goto finish;
+ }
+
+ r = sd_bus_call_method(
+ bus,
+ "org.freedesktop.systemd.test",
+ "/",
+ "org.freedesktop.systemd.test",
+ "LowerCase",
+ &error,
+ &reply,
+ "s",
+ "HELLO");
+ if (r < 0) {
+ log_error("Failed to issue method call: %s", strerror(-r));
+ goto finish;
+ }
+
+ r = sd_bus_message_read(reply, "s", &hello);
+ if (r < 0) {
+ log_error("Failed to get string: %s", strerror(-r));
+ goto finish;
+ }
+
+ assert(streq(hello, "hello"));
+
+ if (pipe2(pp, O_CLOEXEC|O_NONBLOCK) < 0) {
+ log_error("Failed to allocate pipe: %m");
+ r = -errno;
+ goto finish;
+ }
+
+ r = sd_bus_call_method(
+ bus,
+ "org.freedesktop.systemd.test",
+ "/",
+ "org.freedesktop.systemd.test",
+ "FileDescriptor",
+ &error,
+ NULL,
+ "h",
+ pp[1]);
+ if (r < 0) {
+ log_error("Failed to issue method call: %s", strerror(-r));
+ goto finish;
+ }
+
+ errno = 0;
+ if (read(pp[0], &x, 1) <= 0) {
+ log_error("Failed to read from pipe: %s", errno ? strerror(errno) : "early read");
+ goto finish;
+ }
+
+ r = 0;
+
+finish:
+ if (bus) {
+ _cleanup_bus_message_unref_ sd_bus_message *q;
+
+ r = sd_bus_message_new_method_call(
+ bus,
+ "org.freedesktop.systemd.test",
+ "/",
+ "org.freedesktop.systemd.test",
+ "ExitClient1",
+ &q);
+ if (r < 0)
+ log_error("Failed to allocate method call: %s", strerror(-r));
+ else
+ sd_bus_send(bus, q, NULL);
+
+ sd_bus_flush(bus);
+ sd_bus_unref(bus);
+ }
+
+ sd_bus_error_free(&error);
+
+ close_pipe(pp);
+
+ return INT_TO_PTR(r);
+}
+
+static int quit_callback(sd_bus *b, int ret, sd_bus_message *m, void *userdata) {
+ bool *x = userdata;
+
+ log_error("Quit callback: %s", strerror(ret));
+
+ *x = 1;
+ return 1;
+}
+
+static void* client2(void*p) {
+ _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
+ sd_bus *bus = NULL;
+ sd_bus_error error = SD_BUS_ERROR_NULL;
+ bool quit = false;
+ const char *mid;
+ int r;
+
+ r = sd_bus_open_user(&bus);
+ if (r < 0) {
+ log_error("Failed to connect to user bus: %s", strerror(-r));
+ goto finish;
+ }
+
+ r = sd_bus_message_new_method_call(
+ bus,
+ "org.freedesktop.systemd.test",
+ "/foo/bar/waldo/piep",
+ "org.object.test",
+ "Foobar",
+ &m);
+ if (r < 0) {
+ log_error("Failed to allocate method call: %s", strerror(-r));
+ goto finish;
+ }
+
+ r = sd_bus_send(bus, m, NULL);
+ if (r < 0) {
+ log_error("Failed to issue method call: %s", bus_error_message(&error, -r));
+ goto finish;
+ }
+
+ sd_bus_message_unref(m);
+ m = NULL;
+
+ r = sd_bus_message_new_signal(
+ bus,
+ "/foobar",
+ "foo.bar",
+ "Notify",
+ &m);
+ if (r < 0) {
+ log_error("Failed to allocate signal: %s", strerror(-r));
+ goto finish;
+ }
+
+ r = sd_bus_send(bus, m, NULL);
+ if (r < 0) {
+ log_error("Failed to issue signal: %s", bus_error_message(&error, -r));
+ goto finish;
+ }
+
+ sd_bus_message_unref(m);
+ m = NULL;
+
+ r = sd_bus_message_new_method_call(
+ bus,
+ "org.freedesktop.systemd.test",
+ "/",
+ "org.freedesktop.DBus.Peer",
+ "GetMachineId",
+ &m);
+ if (r < 0) {
+ log_error("Failed to allocate method call: %s", strerror(-r));
+ goto finish;
+ }
+
+ r = sd_bus_send_with_reply_and_block(bus, m, 0, &error, &reply);
+ if (r < 0) {
+ log_error("Failed to issue method call: %s", bus_error_message(&error, -r));
+ goto finish;
+ }
+
+ r = sd_bus_message_read(reply, "s", &mid);
+ if (r < 0) {
+ log_error("Failed to parse machine ID: %s", strerror(-r));
+ goto finish;
+ }
+
+ log_info("Machine ID is %s.", mid);
+
+ sd_bus_message_unref(m);
+ m = NULL;
+
+ r = sd_bus_message_new_method_call(
+ bus,
+ "org.freedesktop.systemd.test",
+ "/",
+ "org.freedesktop.systemd.test",
+ "Slow",
+ &m);
+ if (r < 0) {
+ log_error("Failed to allocate method call: %s", strerror(-r));
+ goto finish;
+ }
+
+ sd_bus_message_unref(reply);
+ reply = NULL;
+
+ r = sd_bus_send_with_reply_and_block(bus, m, 200 * USEC_PER_MSEC, &error, &reply);
+ if (r < 0)
+ log_info("Failed to issue method call: %s", bus_error_message(&error, -r));
+ else
+ log_info("Slow call succeed.");
+
+ sd_bus_message_unref(m);
+ m = NULL;
+
+ r = sd_bus_message_new_method_call(
+ bus,
+ "org.freedesktop.systemd.test",
+ "/",
+ "org.freedesktop.systemd.test",
+ "Slow",
+ &m);
+ if (r < 0) {
+ log_error("Failed to allocate method call: %s", strerror(-r));
+ goto finish;
+ }
+
+ r = sd_bus_send_with_reply(bus, m, quit_callback, &quit, 200 * USEC_PER_MSEC, NULL);
+ if (r < 0) {
+ log_info("Failed to issue method call: %s", bus_error_message(&error, -r));
+ goto finish;
+ }
+
+ while (!quit) {
+ r = sd_bus_process(bus, NULL);
+ if (r < 0) {
+ log_error("Failed to process requests: %s", strerror(-r));
+ goto finish;
+ }
+ if (r == 0) {
+ r = sd_bus_wait(bus, (uint64_t) -1);
+ if (r < 0) {
+ log_error("Failed to wait: %s", strerror(-r));
+ goto finish;
+ }
+ }
+ }
+
+ r = 0;
+
+finish:
+ if (bus) {
+ _cleanup_bus_message_unref_ sd_bus_message *q;
+
+ r = sd_bus_message_new_method_call(
+ bus,
+ "org.freedesktop.systemd.test",
+ "/",
+ "org.freedesktop.systemd.test",
+ "ExitClient2",
+ &q);
+ if (r < 0) {
+ log_error("Failed to allocate method call: %s", strerror(-r));
+ goto finish;
+ }
+
+ sd_bus_send(bus, q, NULL);
+ sd_bus_flush(bus);
+ sd_bus_unref(bus);
+ }
+
+ sd_bus_error_free(&error);
+ return INT_TO_PTR(r);
+}
+
+int main(int argc, char *argv[]) {
+ pthread_t c1, c2;
+ sd_bus *bus;
+ void *p;
+ int q, r;
+
+ r = server_init(&bus);
+ if (r < 0) {
+ log_info("Failed to connect to bus, skipping tests.");
+ return EXIT_TEST_SKIP;
+ }
+
+ log_info("Initialized...");
+
+ r = pthread_create(&c1, NULL, client1, bus);
+ if (r != 0)
+ return EXIT_FAILURE;
+
+ r = pthread_create(&c2, NULL, client2, bus);
+ if (r != 0)
+ return EXIT_FAILURE;
+
+ r = server(bus);
+
+ q = pthread_join(c1, &p);
+ if (q != 0)
+ return EXIT_FAILURE;
+ if (PTR_TO_INT(p) < 0)
+ return EXIT_FAILURE;
+
+ q = pthread_join(c2, &p);
+ if (q != 0)
+ return EXIT_FAILURE;
+ if (PTR_TO_INT(p) < 0)
+ return EXIT_FAILURE;
+
+ if (r < 0)
+ return EXIT_FAILURE;
+
+ return EXIT_SUCCESS;
+}
diff --git a/src/libsystemd-bus/test-bus-kernel.c b/src/libsystemd-bus/test-bus-kernel.c
new file mode 100644
index 0000000000..1095e57e42
--- /dev/null
+++ b/src/libsystemd-bus/test-bus-kernel.c
@@ -0,0 +1,162 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ 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 <http://www.gnu.org/licenses/>.
+***/
+
+#include <fcntl.h>
+
+#include "util.h"
+
+#include "sd-bus.h"
+#include "bus-message.h"
+#include "bus-error.h"
+#include "bus-kernel.h"
+
+int main(int argc, char *argv[]) {
+ _cleanup_close_ int bus_ref = -1;
+ _cleanup_free_ char *bus_name = NULL, *address = NULL;
+ _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ const char *ua = NULL, *ub = NULL, *the_string = NULL;
+ sd_bus *a, *b;
+ int r, pipe_fds[2];
+
+ bus_ref = bus_kernel_create("deine-mutter", &bus_name);
+ if (bus_ref == -ENOENT)
+ return EXIT_TEST_SKIP;
+
+ assert_se(bus_ref >= 0);
+
+ address = strappend("kernel:path=", bus_name);
+ assert_se(address);
+
+ r = sd_bus_new(&a);
+ assert_se(r >= 0);
+
+ r = sd_bus_new(&b);
+ assert_se(r >= 0);
+
+ r = sd_bus_set_address(a, address);
+ assert_se(r >= 0);
+
+ r = sd_bus_set_address(b, address);
+ assert_se(r >= 0);
+
+ r = sd_bus_start(a);
+ assert_se(r >= 0);
+
+ r = sd_bus_start(b);
+ assert_se(r >= 0);
+
+ r = sd_bus_get_unique_name(a, &ua);
+ assert_se(r >= 0);
+
+ printf("unique a: %s\n", ua);
+
+ r = sd_bus_get_unique_name(b, &ub);
+ assert_se(r >= 0);
+
+ printf("unique b: %s\n", ub);
+
+ {
+ //FIXME:
+ struct kdbus_cmd_match cmd_match;
+
+ cmd_match.size = sizeof(cmd_match);
+ cmd_match.src_id = KDBUS_MATCH_SRC_ID_ANY;
+
+ r = ioctl(sd_bus_get_fd(a), KDBUS_CMD_MATCH_ADD, &cmd_match);
+ assert_se(r >= 0);
+
+ r = ioctl(sd_bus_get_fd(b), KDBUS_CMD_MATCH_ADD, &cmd_match);
+ assert_se(r >= 0);
+ }
+
+ r = sd_bus_emit_signal(a, "/foo/bar/waldo", "waldo.com", "Piep", "sss", "I am a string", "/this/is/a/path", "and.this.a.domain.name");
+ assert_se(r >= 0);
+
+ r = sd_bus_process(b, &m);
+ assert_se(r > 0);
+ assert_se(m);
+
+ bus_message_dump(m);
+ assert_se(sd_bus_message_rewind(m, true) >= 0);
+
+ r = sd_bus_message_read(m, "s", &the_string);
+ assert_se(r >= 0);
+ assert_se(streq(the_string, "I am a string"));
+
+ sd_bus_message_unref(m);
+ m = NULL;
+
+ r = sd_bus_request_name(a, "net.x0pointer.foobar", 0);
+ assert_se(r >= 0);
+
+ r = sd_bus_message_new_method_call(b, "net.x0pointer.foobar", "/a/path", "an.inter.face", "AMethod", &m);
+ assert_se(r >= 0);
+
+ assert_se(pipe2(pipe_fds, O_CLOEXEC) >= 0);
+
+ assert_se(write(pipe_fds[1], "x", 1) == 1);
+
+ close_nointr_nofail(pipe_fds[1]);
+ pipe_fds[1] = -1;
+
+ r = sd_bus_message_append(m, "h", pipe_fds[0]);
+ assert_se(r >= 0);
+
+ close_nointr_nofail(pipe_fds[0]);
+ pipe_fds[0] = -1;
+
+ r = sd_bus_send(b, m, NULL);
+ assert_se(r >= 0);
+
+ for (;;) {
+ sd_bus_message_unref(m);
+ m = NULL;
+ r = sd_bus_process(a, &m);
+ assert_se(r > 0);
+ assert_se(m);
+
+ bus_message_dump(m);
+ assert_se(sd_bus_message_rewind(m, true) >= 0);
+
+ if (sd_bus_message_is_method_call(m, "an.inter.face", "AMethod")) {
+ int fd;
+ char x;
+
+ r = sd_bus_message_read(m, "h", &fd);
+ assert_se(r >= 0);
+
+ assert_se(read(fd, &x, 1) == 1);
+ assert_se(x == 'x');
+ break;
+ }
+ }
+
+ r = sd_bus_release_name(a, "net.x0pointer.foobar");
+ assert_se(r >= 0);
+
+ r = sd_bus_release_name(a, "net.x0pointer.foobar");
+ assert_se(r == -ESRCH);
+
+ sd_bus_unref(a);
+ sd_bus_unref(b);
+
+ return 0;
+}
diff --git a/src/libsystemd-bus/test-bus-marshal.c b/src/libsystemd-bus/test-bus-marshal.c
new file mode 100644
index 0000000000..20ae723fbe
--- /dev/null
+++ b/src/libsystemd-bus/test-bus-marshal.c
@@ -0,0 +1,175 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ 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 <http://www.gnu.org/licenses/>.
+***/
+
+#include <assert.h>
+#include <stdlib.h>
+#include <byteswap.h>
+
+#ifdef HAVE_GLIB
+#include <gio/gio.h>
+#endif
+
+#include <dbus.h>
+
+#include "log.h"
+#include "util.h"
+
+#include "sd-bus.h"
+#include "bus-message.h"
+
+int main(int argc, char *argv[]) {
+ _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ int r, boolean;
+ const char *x, *y, *z, *a, *b, *c, *d;
+ uint8_t u, v;
+ void *buffer = NULL;
+ size_t sz;
+ char *h;
+
+ r = sd_bus_message_new_method_call(NULL, "foobar.waldo", "/", "foobar.waldo", "Piep", &m);
+ assert_se(r >= 0);
+
+ r = sd_bus_message_append(m, "s", "a string");
+ assert_se(r >= 0);
+
+ r = sd_bus_message_append(m, "s", NULL);
+ assert_se(r < 0);
+
+ r = sd_bus_message_append(m, "as", 2, "string #1", "string #2");
+ assert_se(r >= 0);
+
+ r = sd_bus_message_append(m, "sass", "foobar", 5, "foo", "bar", "waldo", "piep", "pap", "after");
+ assert_se(r >= 0);
+
+ r = sd_bus_message_append(m, "a{yv}", 2, 3, "s", "foo", 5, "s", "waldo");
+ assert_se(r >= 0);
+
+ r = sd_bus_message_append(m, "ba(ss)", 255, 3, "aaa", "1", "bbb", "2", "ccc", "3");
+ assert_se(r >= 0);
+
+ r = sd_bus_message_open_container(m, 'a', "s");
+ assert_se(r >= 0);
+
+ r = sd_bus_message_append_basic(m, 's', "foobar");
+ assert_se(r >= 0);
+
+ r = sd_bus_message_append_basic(m, 's', "waldo");
+ assert_se(r >= 0);
+
+ r = sd_bus_message_close_container(m);
+ assert_se(r >= 0);
+
+ r = bus_message_seal(m, 4711);
+ assert_se(r >= 0);
+
+ bus_message_dump(m);
+
+ r = bus_message_get_blob(m, &buffer, &sz);
+ assert_se(r >= 0);
+
+ h = hexmem(buffer, sz);
+ assert_se(h);
+
+ log_info("message size = %lu, contents =\n%s", (unsigned long) sz, h);
+ free(h);
+
+#ifdef HAVE_GLIB
+ {
+ GDBusMessage *g;
+ char *p;
+
+#if !defined(GLIB_VERSION_2_36)
+ g_type_init();
+#endif
+
+ g = g_dbus_message_new_from_blob(buffer, sz, 0, NULL);
+ p = g_dbus_message_print(g, 0);
+ log_info("%s", p);
+ g_free(p);
+ g_object_unref(g);
+ }
+#endif
+
+ {
+ DBusMessage *w;
+ DBusError error;
+
+ dbus_error_init(&error);
+
+ w = dbus_message_demarshal(buffer, sz, &error);
+ if (!w) {
+ log_error("%s", error.message);
+ } else
+ dbus_message_unref(w);
+ }
+
+ m = sd_bus_message_unref(m);
+
+ r = bus_message_from_malloc(buffer, sz, NULL, 0, NULL, NULL, &m);
+ assert_se(r >= 0);
+
+ bus_message_dump(m);
+
+ assert_se(sd_bus_message_rewind(m, true) >= 0);
+
+ r = sd_bus_message_read(m, "sas", &x, 2, &y, &z);
+ assert_se(r > 0);
+ assert_se(streq(x, "a string"));
+ assert_se(streq(y, "string #1"));
+ assert_se(streq(z, "string #2"));
+
+ r = sd_bus_message_read(m, "sass", &x, 5, &y, &z, &a, &b, &c, &d);
+ assert_se(r > 0);
+ assert_se(streq(x, "foobar"));
+ assert_se(streq(y, "foo"));
+ assert_se(streq(z, "bar"));
+ assert_se(streq(a, "waldo"));
+ assert_se(streq(b, "piep"));
+ assert_se(streq(c, "pap"));
+ assert_se(streq(d, "after"));
+
+ r = sd_bus_message_read(m, "a{yv}", 2, &u, "s", &x, &v, "s", &y);
+ assert_se(r > 0);
+ assert_se(u == 3);
+ assert_se(streq(x, "foo"));
+ assert_se(v == 5);
+ assert_se(streq(y, "waldo"));
+
+ r = sd_bus_message_read(m, "ba(ss)", &boolean, 3, &x, &y, &a, &b, &c, &d);
+ assert_se(r > 0);
+ assert_se(boolean);
+ assert_se(streq(x, "aaa"));
+ assert_se(streq(y, "1"));
+ assert_se(streq(a, "bbb"));
+ assert_se(streq(b, "2"));
+ assert_se(streq(c, "ccc"));
+ assert_se(streq(d, "3"));
+
+ r = sd_bus_message_read(m, "as", 2, &x, &y);
+ assert_se(r > 0);
+ assert_se(streq(x, "foobar"));
+ assert_se(streq(y, "waldo"));
+
+ r = sd_bus_message_peek_type(m, NULL, NULL);
+ assert_se(r == 0);
+
+ return 0;
+}
diff --git a/src/libsystemd-bus/test-bus-match.c b/src/libsystemd-bus/test-bus-match.c
new file mode 100644
index 0000000000..9cf994009d
--- /dev/null
+++ b/src/libsystemd-bus/test-bus-match.c
@@ -0,0 +1,114 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ 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 <http://www.gnu.org/licenses/>.
+***/
+
+#include <assert.h>
+
+#include "log.h"
+#include "util.h"
+#include "macro.h"
+
+#include "bus-match.h"
+#include "bus-message.h"
+
+static bool mask[32];
+
+static int filter(sd_bus *b, int ret, sd_bus_message *m, void *userdata) {
+ log_info("Ran %i", PTR_TO_INT(userdata));
+ mask[PTR_TO_INT(userdata)] = true;
+ return 0;
+}
+
+static bool mask_contains(unsigned a[], unsigned n) {
+ unsigned i, j;
+
+ for (i = 0; i < ELEMENTSOF(mask); i++) {
+ bool found = false;
+
+ for (j = 0; j < n; j++)
+ if (a[j] == i) {
+ found = true;
+ break;
+ }
+
+ if (found != mask[i])
+ return false;
+ }
+
+ return true;
+}
+
+int main(int argc, char *argv[]) {
+ struct bus_match_node root;
+ _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ enum bus_match_node_type i;
+
+ zero(root);
+ root.type = BUS_MATCH_ROOT;
+
+ assert_se(bus_match_add(&root, "arg2='wal\\'do',sender='foo',type='signal',interface='bar',", filter, INT_TO_PTR(1), NULL) >= 0);
+ assert_se(bus_match_add(&root, "arg2='wal\\'do2',sender='foo',type='signal',interface='bar',", filter, INT_TO_PTR(2), NULL) >= 0);
+ assert_se(bus_match_add(&root, "arg3='test',sender='foo',type='signal',interface='bar',", filter, INT_TO_PTR(3), NULL) >= 0);
+ assert_se(bus_match_add(&root, "arg3='test',sender='foo',type='method_call',interface='bar',", filter, INT_TO_PTR(4), NULL) >= 0);
+ assert_se(bus_match_add(&root, "", filter, INT_TO_PTR(5), NULL) >= 0);
+ assert_se(bus_match_add(&root, "interface='quux'", filter, INT_TO_PTR(6), NULL) >= 0);
+ assert_se(bus_match_add(&root, "interface='bar'", filter, INT_TO_PTR(7), NULL) >= 0);
+ assert_se(bus_match_add(&root, "member='waldo',path='/foo/bar'", filter, INT_TO_PTR(8), NULL) >= 0);
+ assert_se(bus_match_add(&root, "path='/foo/bar'", filter, INT_TO_PTR(9), NULL) >= 0);
+ assert_se(bus_match_add(&root, "path_namespace='/foo'", filter, INT_TO_PTR(10), NULL) >= 0);
+ assert_se(bus_match_add(&root, "path_namespace='/foo/quux'", filter, INT_TO_PTR(11), NULL) >= 0);
+ assert_se(bus_match_add(&root, "arg1='two'", filter, INT_TO_PTR(12), NULL) >= 0);
+ assert_se(bus_match_add(&root, "member='waldo',arg2path='/prefix/'", filter, INT_TO_PTR(13), NULL) >= 0);
+ assert_se(bus_match_add(&root, "member='waldo',path='/foo/bar',arg3namespace='prefix'", filter, INT_TO_PTR(14), NULL) >= 0);
+
+ bus_match_dump(&root, 0);
+
+ assert_se(sd_bus_message_new_signal(NULL, "/foo/bar", "bar", "waldo", &m) >= 0);
+ assert_se(sd_bus_message_append(m, "ssss", "one", "two", "/prefix/three", "prefix.four") >= 0);
+ assert_se(bus_message_seal(m, 1) >= 0);
+
+ zero(mask);
+ assert_se(bus_match_run(NULL, &root, 0, m) == 0);
+ assert_se(mask_contains((unsigned[]) { 9, 8, 7, 5, 10, 12, 13, 14 }, 8));
+
+ assert_se(bus_match_remove(&root, "member='waldo',path='/foo/bar'", filter, INT_TO_PTR(8)) > 0);
+ assert_se(bus_match_remove(&root, "arg2path='/prefix/',member='waldo'", filter, INT_TO_PTR(13)) > 0);
+ assert_se(bus_match_remove(&root, "interface='barxx'", filter, INT_TO_PTR(7)) == 0);
+
+ bus_match_dump(&root, 0);
+
+ zero(mask);
+ assert_se(bus_match_run(NULL, &root, 0, m) == 0);
+ assert_se(mask_contains((unsigned[]) { 9, 5, 10, 12, 14, 7 }, 6));
+
+ for (i = 0; i < _BUS_MATCH_NODE_TYPE_MAX; i++) {
+ char buf[32];
+ const char *x;
+
+ assert_se(x = bus_match_node_type_to_string(i, buf, sizeof(buf)));
+
+ if (i >= BUS_MATCH_MESSAGE_TYPE)
+ assert_se(bus_match_node_type_from_string(x, strlen(x)) == i);
+ }
+
+ bus_match_free(&root);
+
+ return 0;
+}
diff --git a/src/libsystemd-bus/test-bus-server.c b/src/libsystemd-bus/test-bus-server.c
new file mode 100644
index 0000000000..a9772624f2
--- /dev/null
+++ b/src/libsystemd-bus/test-bus-server.c
@@ -0,0 +1,223 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ 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 <http://www.gnu.org/licenses/>.
+***/
+
+#include <assert.h>
+#include <stdlib.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "log.h"
+#include "util.h"
+#include "macro.h"
+
+#include "sd-bus.h"
+#include "bus-internal.h"
+#include "bus-message.h"
+
+struct context {
+ int fds[2];
+
+ bool client_negotiate_unix_fds;
+ bool server_negotiate_unix_fds;
+
+ bool client_anonymous_auth;
+ bool server_anonymous_auth;
+};
+
+static void *server(void *p) {
+ struct context *c = p;
+ sd_bus *bus = NULL;
+ sd_id128_t id;
+ bool quit = false;
+ int r;
+
+ assert_se(sd_id128_randomize(&id) >= 0);
+
+ assert_se(sd_bus_new(&bus) >= 0);
+ assert_se(sd_bus_set_fd(bus, c->fds[0], c->fds[0]) >= 0);
+ assert_se(sd_bus_set_server(bus, 1, id) >= 0);
+ assert_se(sd_bus_set_negotiate_fds(bus, c->server_negotiate_unix_fds) >= 0);
+ assert_se(sd_bus_set_anonymous(bus, c->server_anonymous_auth) >= 0);
+ assert_se(sd_bus_start(bus) >= 0);
+
+ while (!quit) {
+ _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
+
+ r = sd_bus_process(bus, &m);
+ if (r < 0) {
+ log_error("Failed to process requests: %s", strerror(-r));
+ goto fail;
+ }
+
+ if (r == 0) {
+ r = sd_bus_wait(bus, (uint64_t) -1);
+ if (r < 0) {
+ log_error("Failed to wait: %s", strerror(-r));
+ goto fail;
+ }
+
+ continue;
+ }
+
+ if (!m)
+ continue;
+
+ log_info("Got message! member=%s", strna(sd_bus_message_get_member(m)));
+
+ if (sd_bus_message_is_method_call(m, "org.freedesktop.systemd.test", "Exit")) {
+
+ assert_se((sd_bus_can_send(bus, 'h') >= 1) == (c->server_negotiate_unix_fds && c->client_negotiate_unix_fds));
+
+ r = sd_bus_message_new_method_return(bus, m, &reply);
+ if (r < 0) {
+ log_error("Failed to allocate return: %s", strerror(-r));
+ goto fail;
+ }
+
+ quit = true;
+
+ } else if (sd_bus_message_is_method_call(m, NULL, NULL)) {
+ r = sd_bus_message_new_method_error(
+ bus, m,
+ &SD_BUS_ERROR_MAKE("org.freedesktop.DBus.Error.UnknownMethod", "Unknown method."),
+ &reply);
+ if (r < 0) {
+ log_error("Failed to allocate return: %s", strerror(-r));
+ goto fail;
+ }
+ }
+
+ if (reply) {
+ r = sd_bus_send(bus, reply, NULL);
+ if (r < 0) {
+ log_error("Failed to send reply: %s", strerror(-r));
+ goto fail;
+ }
+ }
+ }
+
+ r = 0;
+
+fail:
+ if (bus) {
+ sd_bus_flush(bus);
+ sd_bus_unref(bus);
+ }
+
+ return INT_TO_PTR(r);
+}
+
+static int client(struct context *c) {
+ _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
+ _cleanup_bus_unref_ sd_bus *bus = NULL;
+ sd_bus_error error = SD_BUS_ERROR_NULL;
+ int r;
+
+ assert_se(sd_bus_new(&bus) >= 0);
+ assert_se(sd_bus_set_fd(bus, c->fds[1], c->fds[1]) >= 0);
+ assert_se(sd_bus_set_negotiate_fds(bus, c->client_negotiate_unix_fds) >= 0);
+ assert_se(sd_bus_set_anonymous(bus, c->client_anonymous_auth) >= 0);
+ assert_se(sd_bus_start(bus) >= 0);
+
+ r = sd_bus_message_new_method_call(
+ bus,
+ "org.freedesktop.systemd.test",
+ "/",
+ "org.freedesktop.systemd.test",
+ "Exit",
+ &m);
+ if (r < 0) {
+ log_error("Failed to allocate method call: %s", strerror(-r));
+ return r;
+ }
+
+ r = sd_bus_send_with_reply_and_block(bus, m, 0, &error, &reply);
+ if (r < 0) {
+ log_error("Failed to issue method call: %s", bus_error_message(&error, -r));
+ return r;
+ }
+
+ return 0;
+}
+
+static int test_one(bool client_negotiate_unix_fds, bool server_negotiate_unix_fds,
+ bool client_anonymous_auth, bool server_anonymous_auth) {
+
+ struct context c;
+ pthread_t s;
+ void *p;
+ int r, q;
+
+ zero(c);
+
+ assert_se(socketpair(AF_UNIX, SOCK_STREAM, 0, c.fds) >= 0);
+
+ c.client_negotiate_unix_fds = client_negotiate_unix_fds;
+ c.server_negotiate_unix_fds = server_negotiate_unix_fds;
+ c.client_anonymous_auth = client_anonymous_auth;
+ c.server_anonymous_auth = server_anonymous_auth;
+
+ r = pthread_create(&s, NULL, server, &c);
+ if (r != 0)
+ return -r;
+
+ r = client(&c);
+
+ q = pthread_join(s, &p);
+ if (q != 0)
+ return -q;
+
+ if (r < 0)
+ return r;
+
+ if (PTR_TO_INT(p) < 0)
+ return PTR_TO_INT(p);
+
+ return 0;
+}
+
+int main(int argc, char *argv[]) {
+ int r;
+
+ r = test_one(true, true, false, false);
+ assert_se(r >= 0);
+
+ r = test_one(true, false, false, false);
+ assert_se(r >= 0);
+
+ r = test_one(false, true, false, false);
+ assert_se(r >= 0);
+
+ r = test_one(false, false, false, false);
+ assert_se(r >= 0);
+
+ r = test_one(true, true, true, true);
+ assert_se(r >= 0);
+
+ r = test_one(true, true, false, true);
+ assert_se(r >= 0);
+
+ r = test_one(true, true, true, false);
+ assert_se(r == -EPERM);
+
+ return EXIT_SUCCESS;
+}
diff --git a/src/libsystemd-bus/test-bus-signature.c b/src/libsystemd-bus/test-bus-signature.c
new file mode 100644
index 0000000000..cab0daa77e
--- /dev/null
+++ b/src/libsystemd-bus/test-bus-signature.c
@@ -0,0 +1,116 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ 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 <http://www.gnu.org/licenses/>.
+***/
+
+#include <assert.h>
+#include <stdlib.h>
+
+#include "log.h"
+#include "bus-signature.h"
+#include "bus-internal.h"
+
+int main(int argc, char *argv[]) {
+
+ assert_se(signature_is_single("y"));
+ assert_se(signature_is_single("u"));
+ assert_se(signature_is_single("v"));
+ assert_se(signature_is_single("as"));
+ assert_se(signature_is_single("(ss)"));
+ assert_se(signature_is_single("()"));
+ assert_se(signature_is_single("(()()()()())"));
+ assert_se(signature_is_single("(((())))"));
+ assert_se(signature_is_single("((((s))))"));
+ assert_se(signature_is_single("{ss}"));
+ assert_se(signature_is_single("a{ss}"));
+ assert_se(!signature_is_single("uu"));
+ assert_se(!signature_is_single(""));
+ assert_se(!signature_is_single("("));
+ assert_se(!signature_is_single(")"));
+ assert_se(!signature_is_single("())"));
+ assert_se(!signature_is_single("((())"));
+ assert_se(!signature_is_single("{)"));
+ assert_se(!signature_is_single("{}"));
+ assert_se(!signature_is_single("{sss}"));
+ assert_se(!signature_is_single("{s}"));
+ assert_se(!signature_is_single("{ass}"));
+ assert_se(!signature_is_single("a}"));
+
+ assert_se(signature_is_pair("yy"));
+ assert_se(signature_is_pair("ss"));
+ assert_se(signature_is_pair("sas"));
+ assert_se(signature_is_pair("sv"));
+ assert_se(signature_is_pair("sa(vs)"));
+ assert_se(!signature_is_pair(""));
+ assert_se(!signature_is_pair("va"));
+ assert_se(!signature_is_pair("sss"));
+ assert_se(!signature_is_pair("{s}ss"));
+
+ assert_se(signature_is_valid("ssa{ss}sssub", true));
+ assert_se(signature_is_valid("ssa{ss}sssub", false));
+ assert_se(signature_is_valid("{ss}", true));
+ assert_se(!signature_is_valid("{ss}", false));
+ assert_se(signature_is_valid("", true));
+ assert_se(signature_is_valid("", false));
+
+ assert_se(signature_is_valid("sssusa(uuubbba(uu)uuuu)a{u(uuuvas)}", false));
+
+ assert_se(!signature_is_valid("a", false));
+ assert_se(signature_is_valid("as", false));
+ assert_se(signature_is_valid("aas", false));
+ assert_se(signature_is_valid("aaas", false));
+ assert_se(signature_is_valid("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaad", false));
+ assert_se(signature_is_valid("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaas", false));
+ assert_se(!signature_is_valid("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaau", false));
+
+ assert_se(signature_is_valid("(((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))", false));
+ assert_se(!signature_is_valid("((((((((((((((((((((((((((((((((()))))))))))))))))))))))))))))))))", false));
+
+ assert_se(namespace_complex_pattern("", ""));
+ assert_se(namespace_complex_pattern("foobar", "foobar"));
+ assert_se(namespace_complex_pattern("foobar.waldo", "foobar.waldo"));
+ assert_se(namespace_complex_pattern("foobar.", "foobar.waldo"));
+ assert_se(namespace_complex_pattern("foobar.waldo", "foobar."));
+ assert_se(!namespace_complex_pattern("foobar.waldo", "foobar"));
+ assert_se(!namespace_complex_pattern("foobar", "foobar.waldo"));
+ assert_se(!namespace_complex_pattern("", "foo"));
+ assert_se(!namespace_complex_pattern("foo", ""));
+ assert_se(!namespace_complex_pattern("foo.", ""));
+
+ assert_se(path_complex_pattern("", ""));
+ assert_se(path_complex_pattern("", "/"));
+ assert_se(path_complex_pattern("/", ""));
+ assert_se(path_complex_pattern("/", "/"));
+ assert_se(path_complex_pattern("/foobar/", "/"));
+ assert_se(path_complex_pattern("/foobar/", "/foobar"));
+ assert_se(path_complex_pattern("/foobar", "/foobar"));
+ assert_se(path_complex_pattern("/foobar", "/foobar/"));
+ assert_se(!path_complex_pattern("/foobar", "/foobar/waldo"));
+ assert_se(path_complex_pattern("/foobar/", "/foobar/waldo"));
+
+ assert_se(namespace_simple_pattern("", ""));
+ assert_se(namespace_simple_pattern("foobar", "foobar"));
+ assert_se(namespace_simple_pattern("foobar.waldo", "foobar.waldo"));
+ assert_se(namespace_simple_pattern("foobar", "foobar.waldo"));
+ assert_se(!namespace_simple_pattern("foobar.waldo", "foobar"));
+ assert_se(!namespace_simple_pattern("", "foo"));
+ assert_se(!namespace_simple_pattern("foo", ""));
+
+ return 0;
+}
diff --git a/src/libsystemd-daemon/sd-daemon.c b/src/libsystemd-daemon/sd-daemon.c
index 80aadf7adf..485b301023 100644
--- a/src/libsystemd-daemon/sd-daemon.c
+++ b/src/libsystemd-daemon/sd-daemon.c
@@ -25,18 +25,14 @@
***/
#ifndef _GNU_SOURCE
-#define _GNU_SOURCE
+# define _GNU_SOURCE
#endif
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/un.h>
-#ifdef __BIONIC__
-#include <linux/fcntl.h>
-#else
-#include <sys/fcntl.h>
-#endif
+#include <fcntl.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <errno.h>
@@ -47,22 +43,22 @@
#include <stddef.h>
#include <limits.h>
-#if defined(__linux__)
-#include <mqueue.h>
+#if defined(__linux__) && !defined(SD_DAEMON_DISABLE_MQ)
+# include <mqueue.h>
#endif
#include "sd-daemon.h"
#if (__GNUC__ >= 4)
-#ifdef SD_EXPORT_SYMBOLS
+# ifdef SD_EXPORT_SYMBOLS
/* Export symbols */
-#define _sd_export_ __attribute__ ((visibility("default")))
-#else
+# define _sd_export_ __attribute__ ((visibility("default")))
+# else
/* Don't export the symbols */
-#define _sd_export_ __attribute__ ((visibility("hidden")))
-#endif
+# define _sd_export_ __attribute__ ((visibility("hidden")))
+# endif
#else
-#define _sd_export_
+# define _sd_export_
#endif
_sd_export_ int sd_listen_fds(int unset_environment) {
@@ -84,7 +80,7 @@ _sd_export_ int sd_listen_fds(int unset_environment) {
errno = 0;
l = strtoul(e, &p, 10);
- if (errno != 0) {
+ if (errno > 0) {
r = -errno;
goto finish;
}
@@ -109,7 +105,7 @@ _sd_export_ int sd_listen_fds(int unset_environment) {
errno = 0;
l = strtoul(e, &p, 10);
- if (errno != 0) {
+ if (errno > 0) {
r = -errno;
goto finish;
}
@@ -278,11 +274,8 @@ _sd_export_ int sd_is_socket(int fd, int family, int type, int listening) {
return r;
if (family > 0) {
- union sockaddr_union sockaddr;
- socklen_t l;
-
- memset(&sockaddr, 0, sizeof(sockaddr));
- l = sizeof(sockaddr);
+ union sockaddr_union sockaddr = {};
+ socklen_t l = sizeof(sockaddr);
if (getsockname(fd, &sockaddr.sa, &l) < 0)
return -errno;
@@ -297,8 +290,8 @@ _sd_export_ int sd_is_socket(int fd, int family, int type, int listening) {
}
_sd_export_ int sd_is_socket_inet(int fd, int family, int type, int listening, uint16_t port) {
- union sockaddr_union sockaddr;
- socklen_t l;
+ union sockaddr_union sockaddr = {};
+ socklen_t l = sizeof(sockaddr);
int r;
if (family != 0 && family != AF_INET && family != AF_INET6)
@@ -308,9 +301,6 @@ _sd_export_ int sd_is_socket_inet(int fd, int family, int type, int listening, u
if (r <= 0)
return r;
- memset(&sockaddr, 0, sizeof(sockaddr));
- l = sizeof(sockaddr);
-
if (getsockname(fd, &sockaddr.sa, &l) < 0)
return -errno;
@@ -343,17 +333,14 @@ _sd_export_ int sd_is_socket_inet(int fd, int family, int type, int listening, u
}
_sd_export_ int sd_is_socket_unix(int fd, int type, int listening, const char *path, size_t length) {
- union sockaddr_union sockaddr;
- socklen_t l;
+ union sockaddr_union sockaddr = {};
+ socklen_t l = sizeof(sockaddr);
int r;
r = sd_is_socket_internal(fd, type, listening);
if (r <= 0)
return r;
- memset(&sockaddr, 0, sizeof(sockaddr));
- l = sizeof(sockaddr);
-
if (getsockname(fd, &sockaddr.sa, &l) < 0)
return -errno;
@@ -387,7 +374,7 @@ _sd_export_ int sd_is_socket_unix(int fd, int type, int listening, const char *p
}
_sd_export_ int sd_is_mq(int fd, const char *path) {
-#if !defined(__linux__)
+#if !defined(__linux__) || defined(SD_DAEMON_DISABLE_MQ)
return 0;
#else
struct mq_attr attr;
@@ -519,18 +506,15 @@ _sd_export_ int sd_booted(void) {
#if defined(DISABLE_SYSTEMD) || !defined(__linux__)
return 0;
#else
+ struct stat st;
- struct stat a, b;
-
- /* We simply test whether the systemd cgroup hierarchy is
- * mounted */
-
- if (lstat("/sys/fs/cgroup", &a) < 0)
- return 0;
+ /* We test whether the runtime unit file directory has been
+ * created. This takes place in mount-setup.c, so is
+ * guaranteed to happen very early during boot. */
- if (lstat("/sys/fs/cgroup/systemd", &b) < 0)
+ if (lstat("/run/systemd/system/", &st) < 0)
return 0;
- return a.st_dev != b.st_dev;
+ return !!S_ISDIR(st.st_mode);
#endif
}
diff --git a/src/libsystemd-id128/sd-id128.c b/src/libsystemd-id128/sd-id128.c
index 4286ae7d14..64ddd09236 100644
--- a/src/libsystemd-id128/sd-id128.c
+++ b/src/libsystemd-id128/sd-id128.c
@@ -44,30 +44,50 @@ _public_ char *sd_id128_to_string(sd_id128_t id, char s[33]) {
return s;
}
-_public_ int sd_id128_from_string(const char s[33], sd_id128_t *ret) {
- unsigned n;
+_public_ int sd_id128_from_string(const char s[], sd_id128_t *ret) {
+ unsigned n, i;
sd_id128_t t;
+ bool is_guid = false;
if (!s)
return -EINVAL;
if (!ret)
return -EINVAL;
- for (n = 0; n < 16; n++) {
+ for (n = 0, i = 0; n < 16;) {
int a, b;
- a = unhexchar(s[n*2]);
+ if (s[i] == '-') {
+ /* Is this a GUID? Then be nice, and skip over
+ * the dashes */
+
+ if (i == 8)
+ is_guid = true;
+ else if (i == 13 || i == 18 || i == 23) {
+ if (!is_guid)
+ return -EINVAL;
+ } else
+ return -EINVAL;
+
+ i++;
+ continue;
+ }
+
+ a = unhexchar(s[i++]);
if (a < 0)
return -EINVAL;
- b = unhexchar(s[n*2+1]);
+ b = unhexchar(s[i++]);
if (b < 0)
return -EINVAL;
- t.bytes[n] = (a << 4) | b;
+ t.bytes[n++] = (a << 4) | b;
}
- if (s[32] != 0)
+ if (i != (is_guid ? 36 : 32))
+ return -EINVAL;
+
+ if (s[i] != 0)
return -EINVAL;
*ret = t;
@@ -90,8 +110,8 @@ static sd_id128_t make_v4_uuid(sd_id128_t id) {
_public_ int sd_id128_get_machine(sd_id128_t *ret) {
static __thread sd_id128_t saved_machine_id;
static __thread bool saved_machine_id_valid = false;
- int fd;
- char buf[32];
+ _cleanup_close_ int fd = -1;
+ char buf[33];
ssize_t k;
unsigned j;
sd_id128_t t;
@@ -108,13 +128,14 @@ _public_ int sd_id128_get_machine(sd_id128_t *ret) {
if (fd < 0)
return -errno;
- k = loop_read(fd, buf, 32, false);
- close_nointr_nofail(fd);
-
+ k = loop_read(fd, buf, 33, false);
if (k < 0)
return (int) k;
- if (k < 32)
+ if (k != 33)
+ return -EIO;
+
+ if (buf[32] !='\n')
return -EIO;
for (j = 0; j < 16; j++) {
@@ -139,7 +160,7 @@ _public_ int sd_id128_get_machine(sd_id128_t *ret) {
_public_ int sd_id128_get_boot(sd_id128_t *ret) {
static __thread sd_id128_t saved_boot_id;
static __thread bool saved_boot_id_valid = false;
- int fd;
+ _cleanup_close_ int fd = -1;
char buf[36];
ssize_t k;
unsigned j;
@@ -159,17 +180,18 @@ _public_ int sd_id128_get_boot(sd_id128_t *ret) {
return -errno;
k = loop_read(fd, buf, 36, false);
- close_nointr_nofail(fd);
-
if (k < 0)
return (int) k;
- if (k < 36)
+ if (k != 36)
return -EIO;
for (j = 0, p = buf; j < 16; j++) {
int a, b;
+ if (p >= buf + k)
+ return -EIO;
+
if (*p == '-')
p++;
@@ -192,9 +214,9 @@ _public_ int sd_id128_get_boot(sd_id128_t *ret) {
}
_public_ int sd_id128_randomize(sd_id128_t *ret) {
- int fd;
- ssize_t k;
+ _cleanup_close_ int fd = -1;
sd_id128_t t;
+ ssize_t k;
if (!ret)
return -EINVAL;
@@ -204,12 +226,10 @@ _public_ int sd_id128_randomize(sd_id128_t *ret) {
return -errno;
k = loop_read(fd, &t, 16, false);
- close_nointr_nofail(fd);
-
if (k < 0)
return (int) k;
- if (k < 16)
+ if (k != 16)
return -EIO;
/* Turn this into a valid v4 UUID, to be nice. Note that we
diff --git a/src/libudev/libudev-device-private.c b/src/libudev/libudev-device-private.c
index c123057907..cba08d2f65 100644
--- a/src/libudev/libudev-device-private.c
+++ b/src/libudev/libudev-device-private.c
@@ -38,7 +38,7 @@ static void udev_device_tag(struct udev_device *dev, const char *tag, bool add)
id = udev_device_get_id_filename(dev);
if (id == NULL)
return;
- util_strscpyl(filename, sizeof(filename), "/run/udev/tags/", tag, "/", id, NULL);
+ strscpyl(filename, sizeof(filename), "/run/udev/tags/", tag, "/", id, NULL);
if (add) {
int fd;
@@ -116,7 +116,7 @@ int udev_device_update_db(struct udev_device *udev_device)
return -1;
has_info = device_has_info(udev_device);
- util_strscpyl(filename, sizeof(filename), "/run/udev/data/", id, NULL);
+ strscpyl(filename, sizeof(filename), "/run/udev/data/", id, NULL);
/* do not store anything for otherwise empty devices */
if (!has_info &&
@@ -127,7 +127,7 @@ int udev_device_update_db(struct udev_device *udev_device)
}
/* write a database file */
- util_strscpyl(filename_tmp, sizeof(filename_tmp), filename, ".tmp", NULL);
+ strscpyl(filename_tmp, sizeof(filename_tmp), filename, ".tmp", NULL);
mkdir_parents(filename_tmp, 0755);
f = fopen(filename_tmp, "we");
if (f == NULL) {
@@ -186,7 +186,7 @@ int udev_device_delete_db(struct udev_device *udev_device)
id = udev_device_get_id_filename(udev_device);
if (id == NULL)
return -1;
- util_strscpyl(filename, sizeof(filename), "/run/udev/data/", id, NULL);
+ strscpyl(filename, sizeof(filename), "/run/udev/data/", id, NULL);
unlink(filename);
return 0;
}
diff --git a/src/libudev/libudev-device.c b/src/libudev/libudev-device.c
index f218b0278a..6bb2e41510 100644
--- a/src/libudev/libudev-device.c
+++ b/src/libudev/libudev-device.c
@@ -379,7 +379,7 @@ static struct udev_list_entry *udev_device_add_property_from_string(struct udev_
char name[UTIL_LINE_SIZE];
char *val;
- util_strscpy(name, sizeof(name), property);
+ strscpy(name, sizeof(name), property);
val = strchr(name, '=');
if (val == NULL)
return NULL;
@@ -404,7 +404,7 @@ void udev_device_add_property_from_string_parse(struct udev_device *udev_device,
if (startswith(property, "DEVPATH=")) {
char path[UTIL_PATH_SIZE];
- util_strscpyl(path, sizeof(path), "/sys", &property[8], NULL);
+ strscpyl(path, sizeof(path), "/sys", &property[8], NULL);
udev_device_set_syspath(udev_device, path);
} else if (startswith(property, "SUBSYSTEM=")) {
udev_device_set_subsystem(udev_device, &property[10]);
@@ -417,7 +417,7 @@ void udev_device_add_property_from_string_parse(struct udev_device *udev_device,
char *slink;
char *next;
- util_strscpy(devlinks, sizeof(devlinks), &property[9]);
+ strscpy(devlinks, sizeof(devlinks), &property[9]);
slink = devlinks;
next = strchr(slink, ' ');
while (next != NULL) {
@@ -432,7 +432,7 @@ void udev_device_add_property_from_string_parse(struct udev_device *udev_device,
char tags[UTIL_PATH_SIZE];
char *next;
- util_strscpy(tags, sizeof(tags), &property[5]);
+ strscpy(tags, sizeof(tags), &property[5]);
next = strchr(tags, ':');
if (next != NULL) {
next++;
@@ -527,7 +527,7 @@ int udev_device_read_db(struct udev_device *udev_device, const char *dbfile)
id = udev_device_get_id_filename(udev_device);
if (id == NULL)
return -1;
- util_strscpyl(filename, sizeof(filename), "/run/udev/data/", id, NULL);
+ strscpyl(filename, sizeof(filename), "/run/udev/data/", id, NULL);
dbfile = filename;
}
@@ -550,7 +550,7 @@ int udev_device_read_db(struct udev_device *udev_device, const char *dbfile)
val = &line[2];
switch(line[0]) {
case 'S':
- util_strscpyl(filename, sizeof(filename), "/dev/", val, NULL);
+ strscpyl(filename, sizeof(filename), "/dev/", val, NULL);
udev_device_add_devlink(udev_device, filename);
break;
case 'L':
@@ -588,7 +588,7 @@ int udev_device_read_uevent_file(struct udev_device *udev_device)
if (udev_device->uevent_loaded)
return 0;
- util_strscpyl(filename, sizeof(filename), udev_device->syspath, "/uevent", NULL);
+ strscpyl(filename, sizeof(filename), udev_device->syspath, "/uevent", NULL);
f = fopen(filename, "re");
if (f == NULL)
return -1;
@@ -702,14 +702,14 @@ _public_ struct udev_device *udev_device_new_from_syspath(struct udev *udev, con
return NULL;
/* resolve possible symlink to real path */
- util_strscpy(path, sizeof(path), syspath);
+ strscpy(path, sizeof(path), syspath);
util_resolve_sys_link(udev, path, sizeof(path));
if (startswith(path + strlen("/sys"), "/devices/")) {
char file[UTIL_PATH_SIZE];
/* all "devices" require a "uevent" file */
- util_strscpyl(file, sizeof(file), path, "/uevent", NULL);
+ strscpyl(file, sizeof(file), path, "/uevent", NULL);
if (stat(file, &statbuf) != 0)
return NULL;
} else {
@@ -823,7 +823,7 @@ _public_ struct udev_device *udev_device_new_from_device_id(struct udev *udev, c
return NULL;
}
case '+':
- util_strscpy(subsys, sizeof(subsys), &id[1]);
+ strscpy(subsys, sizeof(subsys), &id[1]);
sysname = strchr(subsys, ':');
if (sysname == NULL)
return NULL;
@@ -856,22 +856,22 @@ _public_ struct udev_device *udev_device_new_from_subsystem_sysname(struct udev
struct stat statbuf;
if (streq(subsystem, "subsystem")) {
- util_strscpyl(path, sizeof(path), "/sys/subsystem/", sysname, NULL);
+ strscpyl(path, sizeof(path), "/sys/subsystem/", sysname, NULL);
if (stat(path, &statbuf) == 0)
goto found;
- util_strscpyl(path, sizeof(path), "/sys/bus/", sysname, NULL);
+ strscpyl(path, sizeof(path), "/sys/bus/", sysname, NULL);
if (stat(path, &statbuf) == 0)
goto found;
- util_strscpyl(path, sizeof(path), "/sys/class/", sysname, NULL);
+ strscpyl(path, sizeof(path), "/sys/class/", sysname, NULL);
if (stat(path, &statbuf) == 0)
goto found;
goto out;
}
if (streq(subsystem, "module")) {
- util_strscpyl(path, sizeof(path), "/sys/module/", sysname, NULL);
+ strscpyl(path, sizeof(path), "/sys/module/", sysname, NULL);
if (stat(path, &statbuf) == 0)
goto found;
goto out;
@@ -881,32 +881,32 @@ _public_ struct udev_device *udev_device_new_from_subsystem_sysname(struct udev
char subsys[UTIL_NAME_SIZE];
char *driver;
- util_strscpy(subsys, sizeof(subsys), sysname);
+ strscpy(subsys, sizeof(subsys), sysname);
driver = strchr(subsys, ':');
if (driver != NULL) {
driver[0] = '\0';
driver = &driver[1];
- util_strscpyl(path, sizeof(path), "/sys/subsystem/", subsys, "/drivers/", driver, NULL);
+ strscpyl(path, sizeof(path), "/sys/subsystem/", subsys, "/drivers/", driver, NULL);
if (stat(path, &statbuf) == 0)
goto found;
- util_strscpyl(path, sizeof(path), "/sys/bus/", subsys, "/drivers/", driver, NULL);
+ strscpyl(path, sizeof(path), "/sys/bus/", subsys, "/drivers/", driver, NULL);
if (stat(path, &statbuf) == 0)
goto found;
}
goto out;
}
- util_strscpyl(path, sizeof(path), "/sys/subsystem/", subsystem, "/devices/", sysname, NULL);
+ strscpyl(path, sizeof(path), "/sys/subsystem/", subsystem, "/devices/", sysname, NULL);
if (stat(path, &statbuf) == 0)
goto found;
- util_strscpyl(path, sizeof(path), "/sys/bus/", subsystem, "/devices/", sysname, NULL);
+ strscpyl(path, sizeof(path), "/sys/bus/", subsystem, "/devices/", sysname, NULL);
if (stat(path, &statbuf) == 0)
goto found;
- util_strscpyl(path, sizeof(path), "/sys/class/", subsystem, "/", sysname, NULL);
+ strscpyl(path, sizeof(path), "/sys/class/", subsystem, "/", sysname, NULL);
if (stat(path, &statbuf) == 0)
goto found;
out:
@@ -957,7 +957,7 @@ static struct udev_device *device_new_from_parent(struct udev_device *udev_devic
char path[UTIL_PATH_SIZE];
const char *subdir;
- util_strscpy(path, sizeof(path), udev_device->syspath);
+ strscpy(path, sizeof(path), udev_device->syspath);
subdir = path + strlen("/sys/");
for (;;) {
char *pos;
@@ -1260,9 +1260,9 @@ _public_ struct udev_list_entry *udev_device_get_properties_list_entry(struct ud
size_t l;
s = symlinks;
- l = util_strpcpyl(&s, sizeof(symlinks), udev_list_entry_get_name(list_entry), NULL);
+ l = strpcpyl(&s, sizeof(symlinks), udev_list_entry_get_name(list_entry), NULL);
udev_list_entry_foreach(list_entry, udev_list_entry_get_next(list_entry))
- l = util_strpcpyl(&s, l, " ", udev_list_entry_get_name(list_entry), NULL);
+ l = strpcpyl(&s, l, " ", udev_list_entry_get_name(list_entry), NULL);
udev_device_add_property(udev_device, "DEVLINKS", symlinks);
}
}
@@ -1275,9 +1275,9 @@ _public_ struct udev_list_entry *udev_device_get_properties_list_entry(struct ud
size_t l;
s = tags;
- l = util_strpcpyl(&s, sizeof(tags), ":", NULL);
+ l = strpcpyl(&s, sizeof(tags), ":", NULL);
udev_list_entry_foreach(list_entry, udev_device_get_tags_list_entry(udev_device))
- l = util_strpcpyl(&s, l, udev_list_entry_get_name(list_entry), ":", NULL);
+ l = strpcpyl(&s, l, udev_list_entry_get_name(list_entry), ":", NULL);
udev_device_add_property(udev_device, "TAGS", tags);
}
}
@@ -1374,15 +1374,13 @@ _public_ const char *udev_device_get_sysattr_value(struct udev_device *udev_devi
if (list_entry != NULL)
return udev_list_entry_get_value(list_entry);
- util_strscpyl(path, sizeof(path), udev_device_get_syspath(udev_device), "/", sysattr, NULL);
+ strscpyl(path, sizeof(path), udev_device_get_syspath(udev_device), "/", sysattr, NULL);
if (lstat(path, &statbuf) != 0) {
udev_list_entry_add(&udev_device->sysattr_value_list, sysattr, NULL);
goto out;
}
if (S_ISLNK(statbuf.st_mode)) {
- struct udev_device *dev;
-
/*
* Some core links return only the last element of the target path,
* these are just values, the paths should not be exposed.
@@ -1398,17 +1396,6 @@ _public_ const char *udev_device_get_sysattr_value(struct udev_device *udev_devi
goto out;
}
- /* resolve custom link to a device and return its syspath */
- if (!streq(sysattr, "device")) {
- util_strscpyl(path, sizeof(path), udev_device->syspath, "/", sysattr, NULL);
- dev = udev_device_new_from_syspath(udev_device->udev, path);
- if (dev != NULL) {
- list_entry = udev_list_entry_add(&udev_device->sysattr_value_list, sysattr,
- udev_device_get_syspath(dev));
- val = udev_list_entry_get_value(list_entry);
- udev_device_unref(dev);
- }
- }
goto out;
}
@@ -1440,6 +1427,91 @@ out:
return val;
}
+/**
+ * udev_device_set_sysattr_value:
+ * @udev_device: udev device
+ * @sysattr: attribute name
+ * @value: new value to be set
+ *
+ * Update the contents of the sys attribute and the cached value of the device.
+ *
+ * Returns: Negative error code on failure or 0 on success.
+ **/
+_public_ int udev_device_set_sysattr_value(struct udev_device *udev_device, const char *sysattr, char *value)
+{
+ struct udev_device *dev;
+ char path[UTIL_PATH_SIZE];
+ struct stat statbuf;
+ int fd;
+ ssize_t size, value_len;
+ int ret = 0;
+
+ if (udev_device == NULL)
+ return -EINVAL;
+ dev = udev_device;
+ if (sysattr == NULL)
+ return -EINVAL;
+ if (value == NULL)
+ value_len = 0;
+ else
+ value_len = strlen(value);
+
+ strscpyl(path, sizeof(path), udev_device_get_syspath(dev), "/", sysattr, NULL);
+ if (lstat(path, &statbuf) != 0) {
+ udev_list_entry_add(&dev->sysattr_value_list, sysattr, NULL);
+ ret = -ENXIO;
+ goto out;
+ }
+
+ if (S_ISLNK(statbuf.st_mode)) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /* skip directories */
+ if (S_ISDIR(statbuf.st_mode)) {
+ ret = -EISDIR;
+ goto out;
+ }
+
+ /* skip non-readable files */
+ if ((statbuf.st_mode & S_IRUSR) == 0) {
+ ret = -EACCES;
+ goto out;
+ }
+
+ /* Value is limited to 4k */
+ if (value_len > 4096) {
+ ret = -EINVAL;
+ goto out;
+ }
+ util_remove_trailing_chars(value, '\n');
+
+ /* write attribute value */
+ fd = open(path, O_WRONLY|O_CLOEXEC);
+ if (fd < 0) {
+ ret = -errno;
+ goto out;
+ }
+ size = write(fd, value, value_len);
+ close(fd);
+ if (size < 0) {
+ ret = -errno;
+ goto out;
+ }
+ if (size < value_len) {
+ ret = -EIO;
+ goto out;
+ }
+
+ /* wrote a valid value, store it in cache and return it */
+ udev_list_entry_add(&dev->sysattr_value_list, sysattr, value);
+out:
+ if (dev != udev_device)
+ udev_device_unref(dev);
+ return ret;
+}
+
static int udev_device_sysattr_list_read(struct udev_device *udev_device)
{
struct dirent *dent;
@@ -1463,7 +1535,7 @@ static int udev_device_sysattr_list_read(struct udev_device *udev_device)
if (dent->d_type != DT_LNK && dent->d_type != DT_REG)
continue;
- util_strscpyl(path, sizeof(path), udev_device_get_syspath(udev_device), "/", dent->d_name, NULL);
+ strscpyl(path, sizeof(path), udev_device_get_syspath(udev_device), "/", dent->d_name, NULL);
if (lstat(path, &statbuf) != 0)
continue;
if ((statbuf.st_mode & S_IRUSR) == 0)
@@ -1722,10 +1794,10 @@ static int update_envp_monitor_buf(struct udev_device *udev_device)
return -EINVAL;
/* add property string to monitor buffer */
- l = util_strpcpyl(&s, l, key, "=", udev_list_entry_get_value(list_entry), NULL);
+ l = strpcpyl(&s, l, key, "=", udev_list_entry_get_value(list_entry), NULL);
if (l == 0)
return -EINVAL;
- /* advance past the trailing '\0' that util_strpcpyl() guarantees */
+ /* advance past the trailing '\0' that strpcpyl() guarantees */
s++;
l--;
}
diff --git a/src/libudev/libudev-enumerate.c b/src/libudev/libudev-enumerate.c
index 6a5f4e025e..5ccaabdc6c 100644
--- a/src/libudev/libudev-enumerate.c
+++ b/src/libudev/libudev-enumerate.c
@@ -309,7 +309,7 @@ _public_ struct udev_list_entry *udev_enumerate_get_list_entry(struct udev_enume
}
if (move_later &&
- strncmp(entry->syspath, move_later->syspath, move_later_prefix) != 0) {
+ !strneq(entry->syspath, move_later->syspath, move_later_prefix)) {
udev_list_entry_add(&udev_enumerate->devices_list, move_later->syspath, NULL);
move_later = NULL;
@@ -659,11 +659,11 @@ static int scan_dir_and_add_devices(struct udev_enumerate *udev_enumerate,
struct dirent *dent;
s = path;
- l = util_strpcpyl(&s, sizeof(path), "/sys/", basedir, NULL);
+ l = strpcpyl(&s, sizeof(path), "/sys/", basedir, NULL);
if (subdir1 != NULL)
- l = util_strpcpyl(&s, l, "/", subdir1, NULL);
+ l = strpcpyl(&s, l, "/", subdir1, NULL);
if (subdir2 != NULL)
- util_strpcpyl(&s, l, "/", subdir2, NULL);
+ strpcpyl(&s, l, "/", subdir2, NULL);
dir = opendir(path);
if (dir == NULL)
return -ENOENT;
@@ -677,7 +677,7 @@ static int scan_dir_and_add_devices(struct udev_enumerate *udev_enumerate,
if (!match_sysname(udev_enumerate, dent->d_name))
continue;
- util_strscpyl(syspath, sizeof(syspath), path, "/", dent->d_name, NULL);
+ strscpyl(syspath, sizeof(syspath), path, "/", dent->d_name, NULL);
dev = udev_device_new_from_syspath(udev_enumerate->udev, syspath);
if (dev == NULL)
continue;
@@ -738,7 +738,7 @@ static int scan_dir(struct udev_enumerate *udev_enumerate, const char *basedir,
DIR *dir;
struct dirent *dent;
- util_strscpyl(path, sizeof(path), "/sys/", basedir, NULL);
+ strscpyl(path, sizeof(path), "/sys/", basedir, NULL);
dir = opendir(path);
if (dir == NULL)
return -1;
@@ -789,7 +789,7 @@ static int scan_devices_tags(struct udev_enumerate *udev_enumerate)
struct dirent *dent;
char path[UTIL_PATH_SIZE];
- util_strscpyl(path, sizeof(path), "/run/udev/tags/", udev_list_entry_get_name(list_entry), NULL);
+ strscpyl(path, sizeof(path), "/run/udev/tags/", udev_list_entry_get_name(list_entry), NULL);
dir = opendir(path);
if (dir == NULL)
continue;
diff --git a/src/libudev/libudev-hwdb.c b/src/libudev/libudev-hwdb.c
index 09096c4d71..42ab6d9a6b 100644
--- a/src/libudev/libudev-hwdb.c
+++ b/src/libudev/libudev-hwdb.c
@@ -271,30 +271,30 @@ _public_ struct udev_hwdb *udev_hwdb_new(struct udev *udev) {
hwdb->refcount = 1;
udev_list_init(udev, &hwdb->properties_list, true);
- hwdb->f = fopen(HWDB_BIN, "re");
+ hwdb->f = fopen("/etc/udev/hwdb.bin", "re");
if (!hwdb->f) {
- log_debug("error reading %s: %m", HWDB_BIN);
+ log_debug("error reading /etc/udev/hwdb.bin: %m");
udev_hwdb_unref(hwdb);
return NULL;
}
if (fstat(fileno(hwdb->f), &hwdb->st) < 0 ||
(size_t)hwdb->st.st_size < offsetof(struct trie_header_f, strings_len) + 8) {
- log_debug("error reading %s: %m", HWDB_BIN);
+ log_debug("error reading /etc/udev/hwdb.bin: %m");
udev_hwdb_unref(hwdb);
return NULL;
}
hwdb->map = mmap(0, hwdb->st.st_size, PROT_READ, MAP_SHARED, fileno(hwdb->f), 0);
if (hwdb->map == MAP_FAILED) {
- log_debug("error mapping %s: %m", HWDB_BIN);
+ log_debug("error mapping /etc/udev/hwdb.bin: %m");
udev_hwdb_unref(hwdb);
return NULL;
}
if (memcmp(hwdb->map, sig, sizeof(hwdb->head->signature)) != 0 ||
(size_t)hwdb->st.st_size != le64toh(hwdb->head->file_size)) {
- log_debug("error recognizing the format of %s", HWDB_BIN);
+ log_debug("error recognizing the format of /etc/udev/hwdb.bin");
udev_hwdb_unref(hwdb);
return NULL;
}
diff --git a/src/libudev/libudev-monitor.c b/src/libudev/libudev-monitor.c
index b02ea8808c..0212792552 100644
--- a/src/libudev/libudev-monitor.c
+++ b/src/libudev/libudev-monitor.c
@@ -115,9 +115,9 @@ struct udev_monitor *udev_monitor_new_from_netlink_fd(struct udev *udev, const c
if (name == NULL)
group = UDEV_MONITOR_NONE;
- else if (strcmp(name, "udev") == 0)
+ else if (streq(name, "udev"))
group = UDEV_MONITOR_UDEV;
- else if (strcmp(name, "kernel") == 0)
+ else if (streq(name, "kernel"))
group = UDEV_MONITOR_KERNEL;
else
return NULL;
@@ -467,7 +467,7 @@ static int passes_filter(struct udev_monitor *udev_monitor, struct udev_device *
const char *devtype;
const char *ddevtype;
- if (strcmp(dsubsys, subsys) != 0)
+ if (!streq(dsubsys, subsys))
continue;
devtype = udev_list_entry_get_value(list_entry);
@@ -476,7 +476,7 @@ static int passes_filter(struct udev_monitor *udev_monitor, struct udev_device *
ddevtype = udev_device_get_devtype(udev_device);
if (ddevtype == NULL)
continue;
- if (strcmp(ddevtype, devtype) == 0)
+ if (streq(ddevtype, devtype))
goto tag;
}
return 0;
diff --git a/src/libudev/libudev-private.h b/src/libudev/libudev-private.h
index 1b86384703..54c51acc23 100644
--- a/src/libudev/libudev-private.h
+++ b/src/libudev/libudev-private.h
@@ -29,6 +29,7 @@
#include "macro.h"
#include "util.h"
#include "mkdir.h"
+#include "strxcpyx.h"
#define READ_END 0
#define WRITE_END 1
@@ -164,11 +165,6 @@ int util_resolve_sys_link(struct udev *udev, char *syspath, size_t size);
int util_log_priority(const char *priority);
size_t util_path_encode(const char *src, char *dest, size_t size);
void util_remove_trailing_chars(char *path, char c);
-size_t util_strpcpy(char **dest, size_t size, const char *src);
-size_t util_strpcpyf(char **dest, size_t size, const char *src, ...) __attribute__((format(printf, 3, 4)));
-size_t util_strpcpyl(char **dest, size_t size, const char *src, ...) __attribute__((sentinel));
-size_t util_strscpy(char *dest, size_t size, const char *src);
-size_t util_strscpyl(char *dest, size_t size, const char *src, ...) __attribute__((sentinel));
int util_replace_whitespace(const char *str, char *to, size_t len);
int util_replace_chars(char *str, const char *white);
unsigned int util_string_hash32(const char *key);
diff --git a/src/libudev/libudev-queue.c b/src/libudev/libudev-queue.c
index 08d52ab1f1..0dd20313d9 100644
--- a/src/libudev/libudev-queue.c
+++ b/src/libudev/libudev-queue.c
@@ -458,7 +458,7 @@ _public_ struct udev_list_entry *udev_queue_get_queued_list_entry(struct udev_qu
snprintf(seqnum_str, sizeof(seqnum_str), "%llu", seqnum);
s = syspath;
- l = util_strpcpy(&s, sizeof(syspath), "/sys");
+ l = strpcpy(&s, sizeof(syspath), "/sys");
len = udev_queue_read_devpath(queue_file, s, l);
if (len < 0)
break;
@@ -467,7 +467,7 @@ _public_ struct udev_list_entry *udev_queue_get_queued_list_entry(struct udev_qu
udev_list_entry_add(&udev_queue->queue_list, syspath, seqnum_str);
} else {
udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_queue->queue_list)) {
- if (strcmp(seqnum_str, udev_list_entry_get_value(list_entry)) == 0) {
+ if (streq(seqnum_str, udev_list_entry_get_value(list_entry))) {
udev_list_entry_delete(list_entry);
break;
}
diff --git a/src/libudev/libudev-util.c b/src/libudev/libudev-util.c
index b55bf75bfc..714dc50ae9 100644
--- a/src/libudev/libudev-util.c
+++ b/src/libudev/libudev-util.c
@@ -51,7 +51,7 @@ int util_delete_path(struct udev *udev, const char *path)
if (path[0] == '/')
while(path[1] == '/')
path++;
- util_strscpy(p, sizeof(p), path);
+ strscpy(p, sizeof(p), path);
pos = strrchr(p, '/');
if (pos == p || pos == NULL)
return 0;
@@ -83,7 +83,7 @@ uid_t util_lookup_user(struct udev *udev, const char *user)
size_t buflen = sysconf(_SC_GETPW_R_SIZE_MAX);
char *buf = alloca(buflen);
- if (strcmp(user, "root") == 0)
+ if (streq(user, "root"))
return 0;
uid = strtoul(user, &endptr, 10);
if (endptr[0] == '\0')
@@ -108,7 +108,7 @@ gid_t util_lookup_group(struct udev *udev, const char *group)
size_t buflen = sysconf(_SC_GETPW_R_SIZE_MAX);
char *buf = NULL;
- if (strcmp(group, "root") == 0)
+ if (streq(group, "root"))
return 0;
gid = strtoul(group, &endptr, 10);
if (endptr[0] == '\0')
@@ -151,7 +151,7 @@ int util_resolve_subsys_kernel(struct udev *udev, const char *string,
if (string[0] != '[')
return -1;
- util_strscpy(temp, sizeof(temp), string);
+ strscpy(temp, sizeof(temp), string);
subsys = &temp[1];
@@ -183,7 +183,7 @@ int util_resolve_subsys_kernel(struct udev *udev, const char *string,
val = udev_device_get_sysattr_value(dev, attr);
if (val != NULL)
- util_strscpy(result, maxsize, val);
+ strscpy(result, maxsize, val);
else
result[0] = '\0';
udev_dbg(udev, "value '[%s/%s]%s' is '%s'\n", subsys, sysname, attr, result);
@@ -192,9 +192,9 @@ int util_resolve_subsys_kernel(struct udev *udev, const char *string,
char *s;
s = result;
- l = util_strpcpyl(&s, maxsize, udev_device_get_syspath(dev), NULL);
+ l = strpcpyl(&s, maxsize, udev_device_get_syspath(dev), NULL);
if (attr != NULL)
- util_strpcpyl(&s, l, "/", attr, NULL);
+ strpcpyl(&s, l, "/", attr, NULL);
udev_dbg(udev, "path '[%s/%s]%s' is '%s'\n", subsys, sysname, attr, result);
}
udev_device_unref(dev);
@@ -207,7 +207,7 @@ ssize_t util_get_sys_core_link_value(struct udev *udev, const char *slink, const
ssize_t len;
const char *pos;
- util_strscpyl(path, sizeof(path), syspath, "/", slink, NULL);
+ strscpyl(path, sizeof(path), syspath, "/", slink, NULL);
len = readlink(path, target, sizeof(target));
if (len <= 0 || len == (ssize_t)sizeof(target))
return -1;
@@ -216,7 +216,7 @@ ssize_t util_get_sys_core_link_value(struct udev *udev, const char *slink, const
if (pos == NULL)
return -1;
pos = &pos[1];
- return util_strscpy(value, size, pos);
+ return strscpy(value, size, pos);
}
int util_resolve_sys_link(struct udev *udev, char *syspath, size_t size)
@@ -241,9 +241,8 @@ int util_resolve_sys_link(struct udev *udev, char *syspath, size_t size)
return -EINVAL;
base[0] = '\0';
}
- if (base == NULL)
- return -EINVAL;
- util_strscpyl(base, size - (base - syspath), "/", &link_target[back * 3], NULL);
+
+ strscpyl(base, size - (base - syspath), "/", &link_target[back * 3], NULL);
return 0;
}
@@ -307,89 +306,6 @@ void util_remove_trailing_chars(char *path, char c)
path[--len] = '\0';
}
-/*
- * Concatenates strings. In any case, terminates in _all_ cases with '\0'
- * and moves the @dest pointer forward to the added '\0'. Returns the
- * remaining size, and 0 if the string was truncated.
- */
-size_t util_strpcpy(char **dest, size_t size, const char *src)
-{
- size_t len;
-
- len = strlen(src);
- if (len >= size) {
- if (size > 1)
- *dest = mempcpy(*dest, src, size-1);
- size = 0;
- } else {
- if (len > 0) {
- *dest = mempcpy(*dest, src, len);
- size -= len;
- }
- }
- *dest[0] = '\0';
- return size;
-}
-
-size_t util_strpcpyf(char **dest, size_t size, const char *src, ...)
-{
- va_list va;
- int i;
-
- va_start(va, src);
- i = vsnprintf(*dest, size, src, va);
- if (i < (int)size) {
- *dest += i;
- size -= i;
- } else {
- *dest += size;
- size = 0;
- }
- va_end(va);
- *dest[0] = '\0';
- return size;
-}
-
-/* concatenates list of strings, moves dest forward */
-size_t util_strpcpyl(char **dest, size_t size, const char *src, ...)
-{
- va_list va;
-
- va_start(va, src);
- do {
- size = util_strpcpy(dest, size, src);
- src = va_arg(va, char *);
- } while (src != NULL);
- va_end(va);
- return size;
-}
-
-/* copies string */
-size_t util_strscpy(char *dest, size_t size, const char *src)
-{
- char *s;
-
- s = dest;
- return util_strpcpy(&s, size, src);
-}
-
-/* concatenates list of strings */
-size_t util_strscpyl(char *dest, size_t size, const char *src, ...)
-{
- va_list va;
- char *s;
-
- va_start(va, src);
- s = dest;
- do {
- size = util_strpcpy(&s, size, src);
- src = va_arg(va, char *);
- } while (src != NULL);
- va_end(va);
-
- return size;
-}
-
/* count of characters used to encode one unicode char */
static int utf8_encoded_expected_len(const char *str)
{
@@ -651,7 +567,7 @@ err:
* Murmurhash is under the MIT license.
*
*/
-static unsigned int murmur_hash2(const char *key, int len, unsigned int seed)
+static unsigned int murmur_hash2(const char *key, size_t len, unsigned int seed)
{
/*
* 'm' and 'r' are mixing constants generated offline.
@@ -666,17 +582,18 @@ static unsigned int murmur_hash2(const char *key, int len, unsigned int seed)
/* mix 4 bytes at a time into the hash */
const unsigned char * data = (const unsigned char *)key;
- while(len >= 4) {
- unsigned int k = *(unsigned int *)data;
+ while(len >= sizeof(unsigned int)) {
+ unsigned int k;
+ memcpy(&k, data, sizeof(k));
k *= m;
k ^= k >> r;
k *= m;
h *= m;
h ^= k;
- data += 4;
- len -= 4;
+ data += sizeof(k);
+ len -= sizeof(k);
}
/* handle the last few bytes of the input array */
diff --git a/src/libudev/libudev.c b/src/libudev/libudev.c
index d860ebc080..208039a1b2 100644
--- a/src/libudev/libudev.c
+++ b/src/libudev/libudev.c
@@ -191,7 +191,7 @@ _public_ struct udev *udev_new(void)
val++;
}
- if (strcmp(key, "udev_log") == 0) {
+ if (streq(key, "udev_log")) {
udev_set_log_priority(udev, util_log_priority(val));
continue;
}
diff --git a/src/libudev/libudev.h b/src/libudev/libudev.h
index bb41532a21..61567b1d67 100644
--- a/src/libudev/libudev.h
+++ b/src/libudev/libudev.h
@@ -107,6 +107,7 @@ const char *udev_device_get_action(struct udev_device *udev_device);
unsigned long long int udev_device_get_seqnum(struct udev_device *udev_device);
unsigned long long int udev_device_get_usec_since_initialized(struct udev_device *udev_device);
const char *udev_device_get_sysattr_value(struct udev_device *udev_device, const char *sysattr);
+int udev_device_set_sysattr_value(struct udev_device *udev_device, const char *sysattr, char *value);
int udev_device_has_tag(struct udev_device *udev_device, const char *tag);
/*
diff --git a/src/libudev/libudev.pc.in b/src/libudev/libudev.pc.in
index dad7139c85..2b183e9dc7 100644
--- a/src/libudev/libudev.pc.in
+++ b/src/libudev/libudev.pc.in
@@ -13,6 +13,6 @@ includedir=@includedir@
Name: libudev
Description: Library to access udev device information
Version: @VERSION@
-Libs: -L${libdir} -ludev -lrt
-Libs.private:
+Libs: -L${libdir} -ludev
+Libs.private: -lrt
Cflags: -I${includedir}
diff --git a/src/libudev/libudev.sym b/src/libudev/libudev.sym
index df6a1aeddf..8e09430aec 100644
--- a/src/libudev/libudev.sym
+++ b/src/libudev/libudev.sym
@@ -69,7 +69,6 @@ global:
udev_monitor_get_fd;
udev_monitor_get_udev;
udev_monitor_new_from_netlink;
- udev_monitor_new_from_socket;
udev_monitor_receive_device;
udev_monitor_ref;
udev_monitor_set_receive_buffer_size;
@@ -108,3 +107,7 @@ global:
udev_hwdb_unref;
udev_hwdb_get_properties_list_entry;
} LIBUDEV_189;
+
+LIBUDEV_199 {
+ udev_device_set_sysattr_value;
+} LIBUDEV_196;
diff --git a/src/locale/kbd-model-map b/src/locale/kbd-model-map
index b0860abe80..1fe9bca6ce 100644
--- a/src/locale/kbd-model-map
+++ b/src/locale/kbd-model-map
@@ -53,6 +53,7 @@ de-latin1-nodeadkeys de pc105 nodeadkeys terminate:ctrl_alt_bksp
no no pc105 - terminate:ctrl_alt_bksp
bg_bds-utf8 bg,us pc105 - terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll
dvorak us pc105 dvorak terminate:ctrl_alt_bksp
+dvorak us pc105 dvorak-alt-intl terminate:ctrl_alt_bksp
ru ru,us pc105 - terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll
cz-lat2 cz pc105 qwerty terminate:ctrl_alt_bksp
pl2 pl pc105 - terminate:ctrl_alt_bksp
diff --git a/src/locale/localectl.c b/src/locale/localectl.c
index 290edcc103..50250c4b47 100644
--- a/src/locale/localectl.c
+++ b/src/locale/localectl.c
@@ -37,6 +37,7 @@
#include "pager.h"
#include "set.h"
#include "path-util.h"
+#include "utf8.h"
static bool arg_no_pager = false;
static enum transport {
@@ -53,7 +54,7 @@ static void pager_open_if_enabled(void) {
if (arg_no_pager)
return;
- pager_open();
+ pager_open(false);
}
static void polkit_agent_open_if_enabled(void) {
@@ -158,7 +159,7 @@ static int show_status(DBusConnection *bus, char **args, unsigned n) {
const char *interface = "";
int r;
DBusMessageIter iter, sub, sub2, sub3;
- StatusInfo info;
+ StatusInfo info = {};
assert(args);
@@ -182,7 +183,6 @@ static int show_status(DBusConnection *bus, char **args, unsigned n) {
return -EIO;
}
- zero(info);
dbus_message_iter_recurse(&iter, &sub);
while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
@@ -360,15 +360,17 @@ static int add_locales_from_archive(Set *locales) {
if (e[i].locrec_offset == 0)
continue;
+ if (!utf8_is_valid((char*) p + e[i].name_offset))
+ continue;
+
z = strdup((char*) p + e[i].name_offset);
if (!z) {
r = log_oom();
goto finish;
}
- r = set_put(locales, z);
+ r = set_consume(locales, z);
if (r < 0) {
- free(z);
log_error("Failed to add locale: %s", strerror(-r));
goto finish;
}
@@ -384,7 +386,7 @@ static int add_locales_from_archive(Set *locales) {
}
static int add_locales_from_libdir (Set *locales) {
- DIR _cleanup_closedir_ *dir;
+ _cleanup_closedir_ DIR *dir;
struct dirent *entry;
int r;
@@ -408,20 +410,16 @@ static int add_locales_from_libdir (Set *locales) {
if (!z)
return log_oom();
- r = set_put(locales, z);
- if (r < 0) {
- free(z);
-
- if (r != -EEXIST) {
- log_error("Failed to add locale: %s", strerror(-r));
- return r;
- }
+ r = set_consume(locales, z);
+ if (r < 0 && r != -EEXIST) {
+ log_error("Failed to add locale: %s", strerror(-r));
+ return r;
}
errno = 0;
}
- if (errno != 0) {
+ if (errno > 0) {
log_error("Failed to read locale directory: %m");
return -errno;
}
@@ -432,7 +430,6 @@ static int add_locales_from_libdir (Set *locales) {
static int list_locales(DBusConnection *bus, char **args, unsigned n) {
_cleanup_set_free_ Set *locales;
_cleanup_strv_free_ char **l = NULL;
- char **j;
int r;
locales = set_new(string_hash_func, string_compare_func);
@@ -455,8 +452,7 @@ static int list_locales(DBusConnection *bus, char **args, unsigned n) {
pager_open_if_enabled();
- STRV_FOREACH(j, l)
- puts(*j);
+ strv_print(l);
return 0;
}
@@ -525,12 +521,9 @@ static int nftw_cb(
if (e)
*e = 0;
- r = set_put(keymaps, p);
- if (r == -EEXIST)
- free(p);
- else if (r < 0) {
+ r = set_consume(keymaps, p);
+ if (r < 0 && r != -EEXIST) {
log_error("Can't add keymap: %s", strerror(-r));
- free(p);
return r;
}
@@ -538,8 +531,7 @@ static int nftw_cb(
}
static int list_vconsole_keymaps(DBusConnection *bus, char **args, unsigned n) {
- char _cleanup_strv_free_ **l = NULL;
- char **i;
+ _cleanup_strv_free_ char **l = NULL;
keymaps = set_new(string_hash_func, string_compare_func);
if (!keymaps)
@@ -566,9 +558,7 @@ static int list_vconsole_keymaps(DBusConnection *bus, char **args, unsigned n) {
pager_open_if_enabled();
- STRV_FOREACH(i, l)
- puts(*i);
-
+ strv_print(l);
return 0;
}
@@ -611,24 +601,132 @@ static int set_x11_keymap(DBusConnection *bus, char **args, unsigned n) {
DBUS_TYPE_INVALID);
}
+static int list_x11_keymaps(DBusConnection *bus, char **args, unsigned n) {
+ _cleanup_fclose_ FILE *f = NULL;
+ _cleanup_strv_free_ char **list = NULL;
+ char line[LINE_MAX];
+ enum {
+ NONE,
+ MODELS,
+ LAYOUTS,
+ VARIANTS,
+ OPTIONS
+ } state = NONE, look_for;
+ int r;
+
+ if (n > 2) {
+ log_error("Too many arguments.");
+ return -EINVAL;
+ }
+
+ f = fopen("/usr/share/X11/xkb/rules/base.lst", "re");
+ if (!f) {
+ log_error("Failed to open keyboard mapping list. %m");
+ return -errno;
+ }
+
+ if (streq(args[0], "list-x11-keymap-models"))
+ look_for = MODELS;
+ else if (streq(args[0], "list-x11-keymap-layouts"))
+ look_for = LAYOUTS;
+ else if (streq(args[0], "list-x11-keymap-variants"))
+ look_for = VARIANTS;
+ else if (streq(args[0], "list-x11-keymap-options"))
+ look_for = OPTIONS;
+ else
+ assert_not_reached("Wrong parameter");
+
+ FOREACH_LINE(line, f, break) {
+ char *l, *w;
+
+ l = strstrip(line);
+
+ if (isempty(l))
+ continue;
+
+ if (l[0] == '!') {
+ if (startswith(l, "! model"))
+ state = MODELS;
+ else if (startswith(l, "! layout"))
+ state = LAYOUTS;
+ else if (startswith(l, "! variant"))
+ state = VARIANTS;
+ else if (startswith(l, "! option"))
+ state = OPTIONS;
+ else
+ state = NONE;
+
+ continue;
+ }
+
+ if (state != look_for)
+ continue;
+
+ w = l + strcspn(l, WHITESPACE);
+
+ if (n > 1) {
+ char *e;
+
+ if (*w == 0)
+ continue;
+
+ *w = 0;
+ w++;
+ w += strspn(w, WHITESPACE);
+
+ e = strchr(w, ':');
+ if (!e)
+ continue;
+
+ *e = 0;
+
+ if (!streq(w, args[1]))
+ continue;
+ } else
+ *w = 0;
+
+ r = strv_extend(&list, l);
+ if (r < 0)
+ return log_oom();
+ }
+
+ if (strv_isempty(list)) {
+ log_error("Couldn't find any entries.");
+ return -ENOENT;
+ }
+
+ strv_sort(list);
+ strv_uniq(list);
+
+ pager_open_if_enabled();
+
+ strv_print(list);
+ return 0;
+}
+
static int help(void) {
printf("%s [OPTIONS...] COMMAND ...\n\n"
- "Query or change system time and date settings.\n\n"
- " -h --help Show this help\n"
- " --version Show package version\n"
- " --no-convert Don't convert keyboard mappings\n"
- " --no-pager Do not pipe output into a pager\n"
- " --no-ask-password Do not prompt for password\n"
- " -H --host=[USER@]HOST Operate on remote host\n\n"
+ "Query or change system locale and keyboard settings.\n\n"
+ " -h --help Show this help\n"
+ " --version Show package version\n"
+ " --no-convert Don't convert keyboard mappings\n"
+ " --no-pager Do not pipe output into a pager\n"
+ " --no-ask-password Do not prompt for password\n"
+ " -H --host=[USER@]HOST Operate on remote host\n\n"
"Commands:\n"
- " status Show current locale settings\n"
- " set-locale LOCALE... Set system locale\n"
- " list-locales Show known locales\n"
- " set-keymap MAP [MAP] Set virtual console keyboard mapping\n"
- " list-keymaps Show known virtual console keyboard mappings\n"
+ " status Show current locale settings\n"
+ " set-locale LOCALE... Set system locale\n"
+ " list-locales Show known locales\n"
+ " set-keymap MAP [MAP] Set virtual console keyboard mapping\n"
+ " list-keymaps Show known virtual console keyboard mappings\n"
" set-x11-keymap LAYOUT [MODEL] [VARIANT] [OPTIONS]\n"
- " Set X11 keyboard mapping\n",
+ " Set X11 keyboard mapping\n"
+ " list-x11-keymap-models Show known X11 keyboard mapping models\n"
+ " list-x11-keymap-layouts Show known X11 keyboard mapping layouts\n"
+ " list-x11-keymap-variants [LAYOUT]\n"
+ " Show known X11 keyboard mapping variants\n"
+ " list-x11-keymap-options Show known X11 keyboard mapping options\n",
program_invocation_short_name);
return 0;
@@ -713,12 +811,16 @@ static int localectl_main(DBusConnection *bus, int argc, char *argv[], DBusError
const int argc;
int (* const dispatch)(DBusConnection *bus, char **args, unsigned n);
} verbs[] = {
- { "status", LESS, 1, show_status },
- { "set-locale", MORE, 2, set_locale },
- { "list-locales", EQUAL, 1, list_locales },
- { "set-keymap", MORE, 2, set_vconsole_keymap },
- { "list-keymaps", EQUAL, 1, list_vconsole_keymaps },
- { "set-x11-keymap", MORE, 2, set_x11_keymap },
+ { "status", LESS, 1, show_status },
+ { "set-locale", MORE, 2, set_locale },
+ { "list-locales", EQUAL, 1, list_locales },
+ { "set-keymap", MORE, 2, set_vconsole_keymap },
+ { "list-keymaps", EQUAL, 1, list_vconsole_keymaps },
+ { "set-x11-keymap", MORE, 2, set_x11_keymap },
+ { "list-x11-keymap-models", EQUAL, 1, list_x11_keymaps },
+ { "list-x11-keymap-layouts", EQUAL, 1, list_x11_keymaps },
+ { "list-x11-keymap-variants", LESS, 2, list_x11_keymaps },
+ { "list-x11-keymap-options", EQUAL, 1, list_x11_keymaps },
};
int left;
diff --git a/src/locale/localed.c b/src/locale/localed.c
index bb2a3a2e54..e160c046a4 100644
--- a/src/locale/localed.c
+++ b/src/locale/localed.c
@@ -31,6 +31,10 @@
#include "dbus-common.h"
#include "polkit.h"
#include "def.h"
+#include "env-util.h"
+#include "fileio.h"
+#include "fileio-label.h"
+#include "label.h"
#define INTERFACE \
" <interface name=\"org.freedesktop.locale1\">\n" \
@@ -114,21 +118,7 @@ static const char * const names[_PROP_MAX] = {
[PROP_LC_IDENTIFICATION] = "LC_IDENTIFICATION"
};
-static char *data[_PROP_MAX] = {
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL
-};
+static char *data[_PROP_MAX] = {};
typedef struct State {
char *x11_layout, *x11_model, *x11_variant, *x11_options;
@@ -351,7 +341,7 @@ static int write_data_locale(void) {
int r, p;
char **l = NULL;
- r = load_env_file("/etc/locale.conf", &l);
+ r = load_env_file("/etc/locale.conf", NULL, &l);
if (r < 0 && r != -ENOENT)
return r;
@@ -389,7 +379,7 @@ static int write_data_locale(void) {
return 0;
}
- r = write_env_file("/etc/locale.conf", l);
+ r = write_env_file_label("/etc/locale.conf", l);
strv_free(l);
return r;
@@ -490,7 +480,7 @@ static int write_data_vconsole(void) {
int r;
char **l = NULL;
- r = load_env_file("/etc/vconsole.conf", &l);
+ r = load_env_file("/etc/vconsole.conf", NULL, &l);
if (r < 0 && r != -ENOENT)
return r;
@@ -545,7 +535,7 @@ static int write_data_vconsole(void) {
return 0;
}
- r = write_env_file("/etc/vconsole.conf", l);
+ r = write_env_file_label("/etc/vconsole.conf", l);
strv_free(l);
return r;
@@ -853,7 +843,7 @@ static int convert_x11_to_vconsole(DBusConnection *connection) {
* layout stripped off. */
if (x > 0 &&
strlen(a[1]) == x &&
- strncmp(state.x11_layout, a[1], x) == 0)
+ strneq(state.x11_layout, a[1], x))
matching = 5;
else {
size_t w;
@@ -1007,7 +997,7 @@ static DBusHandlerResult locale_message_handler(
dbus_bool_t interactive;
DBusMessageIter iter;
bool modified = false;
- bool passed[_PROP_MAX];
+ bool passed[_PROP_MAX] = {};
int p;
if (!dbus_message_iter_init(message, &iter))
@@ -1029,8 +1019,6 @@ static DBusHandlerResult locale_message_handler(
dbus_message_iter_get_basic(&iter, &interactive);
- zero(passed);
-
/* Check whether a variable changed and if so valid */
STRV_FOREACH(i, l) {
bool valid = false;
@@ -1273,7 +1261,7 @@ static DBusHandlerResult locale_message_handler(
if (!(reply = dbus_message_new_method_return(message)))
goto oom;
- if (!dbus_connection_send(connection, reply, NULL))
+ if (!bus_maybe_send_reply(connection, message, reply))
goto oom;
dbus_message_unref(reply);
@@ -1363,7 +1351,7 @@ int main(int argc, char *argv[]) {
log_set_target(LOG_TARGET_AUTO);
log_parse_environment();
log_open();
-
+ label_init("/etc");
umask(0022);
if (argc == 2 && streq(argv[1], "--introspect")) {
diff --git a/src/login/70-uaccess.rules b/src/login/70-uaccess.rules
index d1275f2ca9..a118f8e887 100644
--- a/src/login/70-uaccess.rules
+++ b/src/login/70-uaccess.rules
@@ -9,7 +9,6 @@ ACTION=="remove", GOTO="uaccess_end"
ENV{MAJOR}=="", GOTO="uaccess_end"
# PTP/MTP protocol devices, cameras, portable media players
-SUBSYSTEM=="usb", ENV{ID_USB_INTERFACES}=="", ENV{DEVTYPE}=="usb_device", IMPORT{builtin}="usb_id"
SUBSYSTEM=="usb", ENV{ID_USB_INTERFACES}=="*:060101:*", TAG+="uaccess"
# Digicams with proprietary protocol
diff --git a/src/login/71-seat.rules.in b/src/login/71-seat.rules.in
index 4f1a9a59a1..ad26acbbb3 100644
--- a/src/login/71-seat.rules.in
+++ b/src/login/71-seat.rules.in
@@ -10,7 +10,7 @@ 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+="seat-master"
+SUBSYSTEM=="graphics", KERNEL=="fb[0-9]*", TAG+="seat", TAG+="master-of-seat"
SUBSYSTEM=="usb", ATTR{bDeviceClass}=="09", TAG+="seat"
# 'Plugable' USB hub, sound, network, graphics adapter
@@ -38,7 +38,7 @@ SUBSYSTEM=="usb", ATTR{idVendor}=="17e9", ATTR{idProduct}=="401a", ATTR{product}
SUBSYSTEM=="usb", ATTR{idVendor}=="17e9", ATTR{idProduct}=="401a", ATTR{product}=="mimo inc", \
ATTR{../idVendor}=="058f", ATTR{../idProduct}=="6254", \
ENV{ID_AVOID_LOOP}=="", \
- RUN+="@bindir@/udevadm trigger --parent-match=%p/.."
+ 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}"
diff --git a/src/login/inhibit.c b/src/login/inhibit.c
index 9b6613340f..29e50c1447 100644
--- a/src/login/inhibit.c
+++ b/src/login/inhibit.c
@@ -42,7 +42,7 @@ static enum {
} arg_action = ACTION_INHIBIT;
static int inhibit(DBusConnection *bus, DBusError *error) {
- DBusMessage *reply = NULL;
+ _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
int r;
r = bus_method_call_with_reply(
@@ -64,15 +64,13 @@ static int inhibit(DBusConnection *bus, DBusError *error) {
if (!dbus_message_get_args(reply, error,
DBUS_TYPE_UNIX_FD, &r,
DBUS_TYPE_INVALID))
- r = -EIO;
-
- dbus_message_unref(reply);
+ return -EIO;
return r;
}
static int print_inhibitors(DBusConnection *bus, DBusError *error) {
- DBusMessage *reply = NULL;
+ _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
unsigned n = 0;
DBusMessageIter iter, sub, sub2;
int r;
@@ -87,36 +85,22 @@ static int print_inhibitors(DBusConnection *bus, DBusError *error) {
NULL,
DBUS_TYPE_INVALID);
if (r < 0)
- goto finish;
-
- if (!dbus_message_iter_init(reply, &iter)) {
- r = -ENOMEM;
- goto finish;
- }
+ return r;
- if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
- r = -EIO;
- goto finish;
- }
+ if (!dbus_message_iter_init(reply, &iter))
+ return -ENOMEM;
- printf("%-21s %-20s %-20s %-5s %6s %6s\n",
- "WHAT",
- "WHO",
- "WHY",
- "MODE",
- "UID",
- "PID");
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY)
+ return -EIO;
dbus_message_iter_recurse(&iter, &sub);
while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
const char *what, *who, *why, *mode;
- char *ewho, *ewhy;
+ _cleanup_free_ char *comm = NULL, *u = NULL;
dbus_uint32_t uid, pid;
- if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
- r = -EIO;
- goto finish;
- }
+ if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT)
+ return -EIO;
dbus_message_iter_recurse(&sub, &sub2);
@@ -125,33 +109,28 @@ static int print_inhibitors(DBusConnection *bus, DBusError *error) {
bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &why, true) < 0 ||
bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &mode, true) < 0 ||
bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT32, &uid, true) < 0 ||
- bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT32, &pid, false) < 0) {
- r = -EIO;
- goto finish;
- }
+ bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT32, &pid, false) < 0)
+ return -EIO;
- ewho = ellipsize(who, 20, 66);
- ewhy = ellipsize(why, 20, 66);
+ get_process_comm(pid, &comm);
+ u = uid_to_name(uid);
- printf("%-21s %-20s %-20s %-5s %6lu %6lu\n",
- what, ewho ? ewho : who, ewhy ? ewhy : why, mode, (unsigned long) uid, (unsigned long) pid);
-
- free(ewho);
- free(ewhy);
+ printf(" Who: %s (UID %lu/%s, PID %lu/%s)\n"
+ " What: %s\n"
+ " Why: %s\n"
+ " Mode: %s\n\n",
+ who, (unsigned long) uid, strna(u), (unsigned long) pid, strna(comm),
+ what,
+ why,
+ mode);
dbus_message_iter_next(&sub);
n++;
}
- printf("\n%u inhibitors listed.\n", n);
- r = 0;
-
-finish:
- if (reply)
- dbus_message_unref(reply);
-
- return r;
+ printf("%u inhibitors listed.\n", n);
+ return 0;
}
static int help(void) {
@@ -239,7 +218,10 @@ static int parse_argv(int argc, char *argv[]) {
}
}
- if (arg_action == ACTION_INHIBIT && optind >= argc) {
+ if (arg_action == ACTION_INHIBIT && argc == 1)
+ arg_action = ACTION_LIST;
+
+ else if (arg_action == ACTION_INHIBIT && optind >= argc) {
log_error("Missing command line to execute.");
return -EINVAL;
}
@@ -251,7 +233,7 @@ int main(int argc, char *argv[]) {
int r, exit_code = 0;
DBusConnection *bus = NULL;
DBusError error;
- int fd = -1;
+ _cleanup_close_ int fd = -1;
dbus_error_init(&error);
@@ -273,7 +255,7 @@ int main(int argc, char *argv[]) {
r = print_inhibitors(bus, &error);
if (r < 0) {
- log_error("Failed to list inhibitors: %s", bus_error_message_or_strerror(&error, -r));
+ log_error("Failed to list inhibitors: %s", bus_error(&error, r));
goto finish;
}
@@ -288,7 +270,7 @@ int main(int argc, char *argv[]) {
free(w);
if (fd < 0) {
- log_error("Failed to inhibit: %s", bus_error_message_or_strerror(&error, -r));
+ log_error("Failed to inhibit: %s", bus_error(&error, r));
r = fd;
goto finish;
}
@@ -324,8 +306,5 @@ finish:
dbus_error_free(&error);
- if (fd >= 0)
- close_nointr_nofail(fd);
-
return r < 0 ? EXIT_FAILURE : exit_code;
}
diff --git a/src/login/libsystemd-login.sym b/src/login/libsystemd-login.sym
index ff51be729b..925fb91095 100644
--- a/src/login/libsystemd-login.sym
+++ b/src/login/libsystemd-login.sym
@@ -53,3 +53,25 @@ global:
sd_seat_can_tty;
sd_seat_can_graphical;
} LIBSYSTEMD_LOGIN_43;
+
+LIBSYSTEMD_LOGIN_198 {
+global:
+ sd_session_get_tty;
+} LIBSYSTEMD_LOGIN_186;
+
+LIBSYSTEMD_LOGIN_201 {
+global:
+ sd_login_monitor_get_events;
+ sd_login_monitor_get_timeout;
+} LIBSYSTEMD_LOGIN_198;
+
+LIBSYSTEMD_LOGIN_202 {
+global:
+ sd_pid_get_user_unit;
+ sd_pid_get_machine_name;
+} LIBSYSTEMD_LOGIN_201;
+
+LIBSYSTEMD_LOGIN_203 {
+global:
+ sd_get_machine_names;
+} LIBSYSTEMD_LOGIN_202;
diff --git a/src/login/loginctl.c b/src/login/loginctl.c
index e2b33a626c..caaea8dfaa 100644
--- a/src/login/loginctl.c
+++ b/src/login/loginctl.c
@@ -40,6 +40,7 @@
static char **arg_property = NULL;
static bool arg_all = false;
+static bool arg_full = false;
static bool arg_no_pager = false;
static const char *arg_kill_who = NULL;
static int arg_signal = SIGTERM;
@@ -57,7 +58,7 @@ static void pager_open_if_enabled(void) {
if (arg_no_pager)
return;
- pager_open();
+ pager_open(false);
}
static void polkit_agent_open_if_enabled(void) {
@@ -71,7 +72,7 @@ static void polkit_agent_open_if_enabled(void) {
}
static int list_sessions(DBusConnection *bus, char **args, unsigned n) {
- DBusMessage *reply = NULL;
+ _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
int r;
DBusMessageIter iter, sub, sub2;
unsigned k = 0;
@@ -88,14 +89,13 @@ static int list_sessions(DBusConnection *bus, char **args, unsigned n) {
NULL,
DBUS_TYPE_INVALID);
if (r)
- goto finish;
+ return r;
if (!dbus_message_iter_init(reply, &iter) ||
dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) {
log_error("Failed to parse reply.");
- r = -EIO;
- goto finish;
+ return -EIO;
}
dbus_message_iter_recurse(&iter, &sub);
@@ -109,8 +109,7 @@ static int list_sessions(DBusConnection *bus, char **args, unsigned n) {
if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
log_error("Failed to parse reply.");
- r = -EIO;
- goto finish;
+ return -EIO;
}
dbus_message_iter_recurse(&sub, &sub2);
@@ -121,8 +120,7 @@ static int list_sessions(DBusConnection *bus, char **args, unsigned n) {
bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &seat, true) < 0 ||
bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &object, false) < 0) {
log_error("Failed to parse reply.");
- r = -EIO;
- goto finish;
+ return -EIO;
}
printf("%10s %10u %-16s %-16s\n", id, (unsigned) uid, user, seat);
@@ -135,17 +133,11 @@ static int list_sessions(DBusConnection *bus, char **args, unsigned n) {
if (on_tty())
printf("\n%u sessions listed.\n", k);
- r = 0;
-
-finish:
- if (reply)
- dbus_message_unref(reply);
-
- return r;
+ return 0;
}
static int list_users(DBusConnection *bus, char **args, unsigned n) {
- DBusMessage *reply = NULL;
+ _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
int r;
DBusMessageIter iter, sub, sub2;
unsigned k = 0;
@@ -162,14 +154,13 @@ static int list_users(DBusConnection *bus, char **args, unsigned n) {
NULL,
DBUS_TYPE_INVALID);
if (r)
- goto finish;
+ return r;
if (!dbus_message_iter_init(reply, &iter) ||
dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) {
log_error("Failed to parse reply.");
- r = -EIO;
- goto finish;
+ return -EIO;
}
dbus_message_iter_recurse(&iter, &sub);
@@ -183,8 +174,7 @@ static int list_users(DBusConnection *bus, char **args, unsigned n) {
if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
log_error("Failed to parse reply.");
- r = -EIO;
- goto finish;
+ return -EIO;
}
dbus_message_iter_recurse(&sub, &sub2);
@@ -193,8 +183,7 @@ static int list_users(DBusConnection *bus, char **args, unsigned n) {
bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &user, true) < 0 ||
bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &object, false) < 0) {
log_error("Failed to parse reply.");
- r = -EIO;
- goto finish;
+ return -EIO;
}
printf("%10u %-16s\n", (unsigned) uid, user);
@@ -207,17 +196,11 @@ static int list_users(DBusConnection *bus, char **args, unsigned n) {
if (on_tty())
printf("\n%u users listed.\n", k);
- r = 0;
-
-finish:
- if (reply)
- dbus_message_unref(reply);
-
- return r;
+ return 0;
}
static int list_seats(DBusConnection *bus, char **args, unsigned n) {
- DBusMessage *reply = NULL;
+ _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
int r;
DBusMessageIter iter, sub, sub2;
unsigned k = 0;
@@ -234,14 +217,13 @@ static int list_seats(DBusConnection *bus, char **args, unsigned n) {
NULL,
DBUS_TYPE_INVALID);
if (r)
- goto finish;
+ return r;
if (!dbus_message_iter_init(reply, &iter) ||
dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) {
log_error("Failed to parse reply.");
- r = -EIO;
- goto finish;
+ return -EIO;
}
dbus_message_iter_recurse(&iter, &sub);
@@ -254,8 +236,7 @@ static int list_seats(DBusConnection *bus, char **args, unsigned n) {
if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
log_error("Failed to parse reply.");
- r = -EIO;
- goto finish;
+ return -EIO;
}
dbus_message_iter_recurse(&sub, &sub2);
@@ -263,8 +244,7 @@ static int list_seats(DBusConnection *bus, char **args, unsigned n) {
if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &seat, true) < 0 ||
bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &object, false) < 0) {
log_error("Failed to parse reply.");
- r = -EIO;
- goto finish;
+ return -EIO;
}
printf("%-16s\n", seat);
@@ -277,13 +257,7 @@ static int list_seats(DBusConnection *bus, char **args, unsigned n) {
if (on_tty())
printf("\n%u seats listed.\n", k);
- r = 0;
-
-finish:
- if (reply)
- dbus_message_unref(reply);
-
- return r;
+ return 0;
}
typedef struct SessionStatusInfo {
@@ -402,6 +376,9 @@ static void print_session_status_info(SessionStatusInfo *i) {
if (i->default_control_group) {
unsigned c;
+ int output_flags =
+ arg_all * OUTPUT_SHOW_ALL |
+ arg_full * OUTPUT_FULL_WIDTH;
printf("\t CGroup: %s\n", i->default_control_group);
@@ -412,7 +389,10 @@ static void print_session_status_info(SessionStatusInfo *i) {
else
c = 0;
- show_cgroup_and_extra_by_spec(i->default_control_group, "\t\t ", c, false, arg_all, &i->leader, i->leader > 0 ? 1 : 0);
+ show_cgroup_and_extra_by_spec(i->default_control_group,
+ "\t\t ", c, false, &i->leader,
+ i->leader > 0 ? 1 : 0,
+ output_flags);
}
}
}
@@ -454,6 +434,9 @@ static void print_user_status_info(UserStatusInfo *i) {
if (i->default_control_group) {
unsigned c;
+ int output_flags =
+ arg_all * OUTPUT_SHOW_ALL |
+ arg_full * OUTPUT_FULL_WIDTH;
printf("\t CGroup: %s\n", i->default_control_group);
@@ -464,7 +447,8 @@ static void print_user_status_info(UserStatusInfo *i) {
else
c = 0;
- show_cgroup_by_path(i->default_control_group, "\t\t ", c, false, arg_all);
+ show_cgroup_by_path(i->default_control_group, "\t\t ",
+ c, false, output_flags);
}
}
}
@@ -850,17 +834,13 @@ static int show_one(const char *verb, DBusConnection *bus, const char *path, boo
const char *interface = "";
int r;
DBusMessageIter iter, sub, sub2, sub3;
- SessionStatusInfo session_info;
- UserStatusInfo user_info;
- SeatStatusInfo seat_info;
+ SessionStatusInfo session_info = {};
+ UserStatusInfo user_info = {};
+ SeatStatusInfo seat_info = {};
assert(path);
assert(new_line);
- zero(session_info);
- zero(user_info);
- zero(seat_info);
-
r = bus_method_call_with_reply(
bus,
"org.freedesktop.login1",
@@ -950,7 +930,7 @@ finish:
}
static int show(DBusConnection *bus, char **args, unsigned n) {
- DBusMessage *reply = NULL;
+ _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
int r, ret = 0;
DBusError error;
unsigned i;
@@ -1037,15 +1017,9 @@ static int show(DBusConnection *bus, char **args, unsigned n) {
r = show_one(args[0], bus, path, show_properties, &new_line);
if (r != 0)
ret = r;
-
- dbus_message_unref(reply);
- reply = NULL;
}
finish:
- if (reply)
- dbus_message_unref(reply);
-
dbus_error_free(&error);
return ret;
@@ -1081,7 +1055,6 @@ finish:
}
static int kill_session(DBusConnection *bus, char **args, unsigned n) {
- int ret = 0;
unsigned i;
assert(args);
@@ -1090,28 +1063,28 @@ static int kill_session(DBusConnection *bus, char **args, unsigned n) {
arg_kill_who = "all";
for (i = 1; i < n; i++) {
- ret = bus_method_call_with_reply (
- bus,
- "org.freedesktop.login1",
- "/org/freedesktop/login1",
- "org.freedesktop.login1.Manager",
- "KillSession",
- NULL,
- NULL,
- DBUS_TYPE_STRING, &args[i],
- DBUS_TYPE_STRING, &arg_kill_who,
- DBUS_TYPE_INT32, &arg_signal,
- DBUS_TYPE_INVALID);
- if (ret)
- goto finish;
+ int r;
+
+ r = bus_method_call_with_reply (
+ bus,
+ "org.freedesktop.login1",
+ "/org/freedesktop/login1",
+ "org.freedesktop.login1.Manager",
+ "KillSession",
+ NULL,
+ NULL,
+ DBUS_TYPE_STRING, &args[i],
+ DBUS_TYPE_STRING, &arg_kill_who,
+ DBUS_TYPE_INT32, &arg_signal,
+ DBUS_TYPE_INVALID);
+ if (r)
+ return r;
}
-finish:
- return ret;
+ return 0;
}
static int enable_linger(DBusConnection *bus, char **args, unsigned n) {
- int ret = 0;
unsigned i;
dbus_bool_t b, interactive = true;
@@ -1124,36 +1097,35 @@ static int enable_linger(DBusConnection *bus, char **args, unsigned n) {
for (i = 1; i < n; i++) {
uint32_t u;
uid_t uid;
+ int r;
- ret = get_user_creds((const char**) (args+i), &uid, NULL, NULL, NULL);
- if (ret < 0) {
- log_error("Failed to resolve user %s: %s", args[i], strerror(-ret));
- goto finish;
+ r = get_user_creds((const char**) (args+i), &uid, NULL, NULL, NULL);
+ if (r < 0) {
+ log_error("Failed to resolve user %s: %s", args[i], strerror(-r));
+ return r;
}
u = (uint32_t) uid;
- ret = bus_method_call_with_reply (
- bus,
- "org.freedesktop.login1",
- "/org/freedesktop/login1",
- "org.freedesktop.login1.Manager",
- "SetUserLinger",
- NULL,
- NULL,
- DBUS_TYPE_UINT32, &u,
- DBUS_TYPE_BOOLEAN, &b,
- DBUS_TYPE_BOOLEAN, &interactive,
- DBUS_TYPE_INVALID);
- if (ret)
- goto finish;
+ r = bus_method_call_with_reply (
+ bus,
+ "org.freedesktop.login1",
+ "/org/freedesktop/login1",
+ "org.freedesktop.login1.Manager",
+ "SetUserLinger",
+ NULL,
+ NULL,
+ DBUS_TYPE_UINT32, &u,
+ DBUS_TYPE_BOOLEAN, &b,
+ DBUS_TYPE_BOOLEAN, &interactive,
+ DBUS_TYPE_INVALID);
+ if (r)
+ return r;
}
-finish:
- return ret;
+ return 0;
}
static int terminate_user(DBusConnection *bus, char **args, unsigned n) {
- int ret = 0;
unsigned i;
assert(args);
@@ -1161,34 +1133,33 @@ static int terminate_user(DBusConnection *bus, char **args, unsigned n) {
for (i = 1; i < n; i++) {
uint32_t u;
uid_t uid;
+ int r;
- ret = get_user_creds((const char**) (args+i), &uid, NULL, NULL, NULL);
- if (ret < 0) {
- log_error("Failed to look up user %s: %s", args[i], strerror(-ret));
- goto finish;
+ r = get_user_creds((const char**) (args+i), &uid, NULL, NULL, NULL);
+ if (r < 0) {
+ log_error("Failed to look up user %s: %s", args[i], strerror(-r));
+ return r;
}
u = (uint32_t) uid;
- ret = bus_method_call_with_reply (
- bus,
- "org.freedesktop.login1",
- "/org/freedesktop/login1",
- "org.freedesktop.login1.Manager",
- "TerminateUser",
- NULL,
- NULL,
- DBUS_TYPE_UINT32, &u,
- DBUS_TYPE_INVALID);
- if (ret)
- goto finish;
+ r = bus_method_call_with_reply (
+ bus,
+ "org.freedesktop.login1",
+ "/org/freedesktop/login1",
+ "org.freedesktop.login1.Manager",
+ "TerminateUser",
+ NULL,
+ NULL,
+ DBUS_TYPE_UINT32, &u,
+ DBUS_TYPE_INVALID);
+ if (r)
+ return r;
}
-finish:
- return ret;
+ return 0;
}
static int kill_user(DBusConnection *bus, char **args, unsigned n) {
- int ret = 0;
unsigned i;
assert(args);
@@ -1199,35 +1170,34 @@ static int kill_user(DBusConnection *bus, char **args, unsigned n) {
for (i = 1; i < n; i++) {
uid_t uid;
uint32_t u;
+ int r;
- ret = get_user_creds((const char**) (args+i), &uid, NULL, NULL, NULL);
- if (ret < 0) {
- log_error("Failed to look up user %s: %s", args[i], strerror(-ret));
- goto finish;
+ r = get_user_creds((const char**) (args+i), &uid, NULL, NULL, NULL);
+ if (r < 0) {
+ log_error("Failed to look up user %s: %s", args[i], strerror(-r));
+ return r;
}
u = (uint32_t) uid;
- ret = bus_method_call_with_reply (
- bus,
- "org.freedesktop.login1",
- "/org/freedesktop/login1",
- "org.freedesktop.login1.Manager",
- "KillUser",
- NULL,
- NULL,
- DBUS_TYPE_UINT32, &u,
- DBUS_TYPE_INT32, &arg_signal,
- DBUS_TYPE_INVALID);
- if (ret)
- goto finish;
+ r = bus_method_call_with_reply (
+ bus,
+ "org.freedesktop.login1",
+ "/org/freedesktop/login1",
+ "org.freedesktop.login1.Manager",
+ "KillUser",
+ NULL,
+ NULL,
+ DBUS_TYPE_UINT32, &u,
+ DBUS_TYPE_INT32, &arg_signal,
+ DBUS_TYPE_INVALID);
+ if (r)
+ return r;
}
-finish:
- return ret;
+ return 0;
}
static int attach(DBusConnection *bus, char **args, unsigned n) {
- int ret = 0;
unsigned i;
dbus_bool_t interactive = true;
@@ -1236,24 +1206,25 @@ static int attach(DBusConnection *bus, char **args, unsigned n) {
polkit_agent_open_if_enabled();
for (i = 2; i < n; i++) {
- ret = bus_method_call_with_reply (
- bus,
- "org.freedesktop.login1",
- "/org/freedesktop/login1",
- "org.freedesktop.login1.Manager",
- "AttachDevice",
- NULL,
- NULL,
- DBUS_TYPE_STRING, &args[1],
- DBUS_TYPE_STRING, &args[i],
- DBUS_TYPE_BOOLEAN, &interactive,
- DBUS_TYPE_INVALID);
- if (ret)
- goto finish;
+ int r;
+
+ r = bus_method_call_with_reply (
+ bus,
+ "org.freedesktop.login1",
+ "/org/freedesktop/login1",
+ "org.freedesktop.login1.Manager",
+ "AttachDevice",
+ NULL,
+ NULL,
+ DBUS_TYPE_STRING, &args[1],
+ DBUS_TYPE_STRING, &args[i],
+ DBUS_TYPE_BOOLEAN, &interactive,
+ DBUS_TYPE_INVALID);
+ if (r)
+ return r;
}
-finish:
- return ret;
+ return 0;
}
static int flush_devices(DBusConnection *bus, char **args, unsigned n) {
@@ -1276,6 +1247,8 @@ static int flush_devices(DBusConnection *bus, char **args, unsigned n) {
}
static int lock_sessions(DBusConnection *bus, char **args, unsigned n) {
+ assert(args);
+
polkit_agent_open_if_enabled();
return bus_method_call_with_reply (
@@ -1283,35 +1256,35 @@ static int lock_sessions(DBusConnection *bus, char **args, unsigned n) {
"org.freedesktop.login1",
"/org/freedesktop/login1",
"org.freedesktop.login1.Manager",
- "LockSessions",
+ streq(args[0], "lock-sessions") ? "LockSessions" : "UnlockSessions",
NULL,
NULL,
DBUS_TYPE_INVALID);
}
static int terminate_seat(DBusConnection *bus, char **args, unsigned n) {
- int ret = 0;
unsigned i;
assert(args);
for (i = 1; i < n; i++) {
- ret = bus_method_call_with_reply (
- bus,
- "org.freedesktop.login1",
- "/org/freedesktop/login1",
- "org.freedesktop.login1.Manager",
- "TerminateSeat",
- NULL,
- NULL,
- DBUS_TYPE_STRING, &args[i],
- DBUS_TYPE_INVALID);
- if (ret)
- goto finish;
+ int r;
+
+ r = bus_method_call_with_reply (
+ bus,
+ "org.freedesktop.login1",
+ "/org/freedesktop/login1",
+ "org.freedesktop.login1.Manager",
+ "TerminateSeat",
+ NULL,
+ NULL,
+ DBUS_TYPE_STRING, &args[i],
+ DBUS_TYPE_INVALID);
+ if (r)
+ return r;
}
-finish:
- return ret;
+ return 0;
}
static int help(void) {
@@ -1323,6 +1296,7 @@ static int help(void) {
" -p --property=NAME Show only properties by this name\n"
" -a --all Show all properties, including empty ones\n"
" --kill-who=WHO Who to send signal to\n"
+ " --full Do not ellipsize output\n"
" -s --signal=SIGNAL Which signal to send\n"
" --no-ask-password Don't prompt for password\n"
" -H --host=[USER@]HOST Show information for remote host\n"
@@ -1336,6 +1310,7 @@ static int help(void) {
" lock-session [ID...] Screen lock one or more sessions\n"
" unlock-session [ID...] Screen unlock one or more sessions\n"
" lock-sessions Screen lock all current sessions\n"
+ " unlock-sessions Screen unlock all current sessions\n"
" terminate-session [ID...] Terminate one or more sessions\n"
" kill-session [ID...] Send signal to processes of a session\n"
" list-users List users\n"
@@ -1362,7 +1337,8 @@ static int parse_argv(int argc, char *argv[]) {
ARG_VERSION = 0x100,
ARG_NO_PAGER,
ARG_KILL_WHO,
- ARG_NO_ASK_PASSWORD
+ ARG_NO_ASK_PASSWORD,
+ ARG_FULL,
};
static const struct option options[] = {
@@ -1376,6 +1352,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "host", required_argument, NULL, 'H' },
{ "privileged", no_argument, NULL, 'P' },
{ "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
+ { "full", no_argument, NULL, ARG_FULL },
{ NULL, 0, NULL, 0 }
};
@@ -1447,6 +1424,10 @@ static int parse_argv(int argc, char *argv[]) {
arg_host = optarg;
break;
+ case ARG_FULL:
+ arg_full = true;
+ break;
+
case '?':
return -EINVAL;
@@ -1478,6 +1459,7 @@ static int loginctl_main(DBusConnection *bus, int argc, char *argv[], DBusError
{ "lock-session", MORE, 2, activate },
{ "unlock-session", MORE, 2, activate },
{ "lock-sessions", EQUAL, 1, lock_sessions },
+ { "unlock-sessions", EQUAL, 1, lock_sessions },
{ "terminate-session", MORE, 2, activate },
{ "kill-session", MORE, 2, kill_session },
{ "list-users", EQUAL, 1, list_users },
diff --git a/src/login/logind-action.c b/src/login/logind-action.c
index e1517d6ac2..c930591023 100644
--- a/src/login/logind-action.c
+++ b/src/login/logind-action.c
@@ -21,10 +21,13 @@
#include <unistd.h>
+#include <systemd/sd-messages.h>
+
#include "conf-parser.h"
#include "special.h"
#include "dbus-common.h"
#include "logind-action.h"
+#include "sleep-config.h"
int manager_handle_action(
Manager *m,
@@ -56,10 +59,15 @@ int manager_handle_action(
DBusError error;
int r;
InhibitWhat inhibit_operation;
- bool supported = true;
+ bool supported;
assert(m);
+ if (m->action_what) {
+ log_debug("Action already in progress, ignoring.");
+ return -EALREADY;
+ }
+
/* If the key handling is turned off, don't do anything */
if (handle == HANDLE_IGNORE) {
log_debug("Refusing operation, as it is turned off.");
@@ -67,13 +75,15 @@ int manager_handle_action(
}
if (handle == HANDLE_SUSPEND)
- supported = can_sleep("mem") > 0;
+ supported = can_sleep("suspend") > 0;
else if (handle == HANDLE_HIBERNATE)
- supported = can_sleep("disk") > 0;
+ supported = can_sleep("hibernate") > 0;
else if (handle == HANDLE_HYBRID_SLEEP)
- supported = can_sleep("disk") > 0 && can_sleep_disk("suspend") > 0;
+ supported = can_sleep("hybrid-sleep") > 0;
else if (handle == HANDLE_KEXEC)
supported = access("/sbin/kexec", X_OK) >= 0;
+ else
+ supported = true;
if (!supported) {
log_warning("Requested operation not supported, ignoring.");
diff --git a/src/login/logind-action.h b/src/login/logind-action.h
index 7ab44644f2..552713637d 100644
--- a/src/login/logind-action.h
+++ b/src/login/logind-action.h
@@ -46,9 +46,9 @@ int manager_handle_action(
bool ignore_inhibited,
bool is_edge);
-const char* handle_action_to_string(HandleAction h);
-HandleAction handle_action_from_string(const char *s);
+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 *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_handle_action(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
#endif
diff --git a/src/login/logind-button.c b/src/login/logind-button.c
index dbf3d3c446..ea45c28eef 100644
--- a/src/login/logind-button.c
+++ b/src/login/logind-button.c
@@ -33,6 +33,7 @@
#include "logind-button.h"
#include "special.h"
#include "dbus-common.h"
+#include "sd-messages.h"
Button* button_new(Manager *m, const char *name) {
Button *b;
@@ -70,7 +71,11 @@ void button_free(Button *b) {
if (b->fd >= 0) {
hashmap_remove(b->manager->button_fds, INT_TO_PTR(b->fd + 1));
assert_se(epoll_ctl(b->manager->epoll_fd, EPOLL_CTL_DEL, b->fd, NULL) == 0);
- close_nointr_nofail(b->fd);
+
+ /* If the device has been unplugged close() returns
+ * ENODEV, let's ignore this, hence we don't use
+ * close_nointr_nofail() */
+ close(b->fd);
}
free(b->name);
@@ -102,7 +107,7 @@ int button_open(Button *b) {
assert(b);
if (b->fd >= 0) {
- close_nointr_nofail(b->fd);
+ close(b->fd);
b->fd = -1;
}
@@ -145,7 +150,7 @@ int button_open(Button *b) {
return 0;
fail:
- close_nointr_nofail(b->fd);
+ close(b->fd);
b->fd = -1;
return r;
}
@@ -188,7 +193,10 @@ int button_process(Button *b) {
case KEY_POWER:
case KEY_POWER2:
- log_info("Power key pressed.");
+ log_struct(LOG_INFO,
+ "MESSAGE=Power key pressed.",
+ MESSAGE_ID(SD_MESSAGE_POWER_KEY),
+ NULL);
return button_handle(b, INHIBIT_HANDLE_POWER_KEY, b->manager->handle_power_key, b->manager->power_key_ignore_inhibited, true);
/* The kernel is a bit confused here:
@@ -198,11 +206,17 @@ int button_process(Button *b) {
*/
case KEY_SLEEP:
- log_info("Suspend key pressed.");
+ log_struct(LOG_INFO,
+ "MESSAGE=Suspend key pressed.",
+ MESSAGE_ID(SD_MESSAGE_SUSPEND_KEY),
+ NULL);
return button_handle(b, INHIBIT_HANDLE_SUSPEND_KEY, b->manager->handle_suspend_key, b->manager->suspend_key_ignore_inhibited, true);
case KEY_SUSPEND:
- log_info("Hibernate key pressed.");
+ log_struct(LOG_INFO,
+ "MESSAGE=Hibernate key pressed.",
+ MESSAGE_ID(SD_MESSAGE_HIBERNATE_KEY),
+ NULL);
return button_handle(b, INHIBIT_HANDLE_HIBERNATE_KEY, b->manager->handle_hibernate_key, b->manager->hibernate_key_ignore_inhibited, true);
}
@@ -211,7 +225,10 @@ int button_process(Button *b) {
switch (ev.code) {
case SW_LID:
- log_info("Lid closed.");
+ log_struct(LOG_INFO,
+ "MESSAGE=Lid closed.",
+ MESSAGE_ID(SD_MESSAGE_LID_CLOSED),
+ NULL);
b->lid_close_queued = true;
return button_handle(b, INHIBIT_HANDLE_LID_SWITCH, b->manager->handle_lid_switch, b->manager->lid_switch_ignore_inhibited, true);
@@ -222,7 +239,10 @@ int button_process(Button *b) {
switch (ev.code) {
case SW_LID:
- log_info("Lid opened.");
+ log_struct(LOG_INFO,
+ "MESSAGE=Lid opened.",
+ MESSAGE_ID(SD_MESSAGE_LID_OPENED),
+ NULL);
b->lid_close_queued = false;
break;
}
diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c
index 77a06f2ce4..4a84b860f1 100644
--- a/src/login/logind-dbus.c
+++ b/src/login/logind-dbus.c
@@ -31,8 +31,11 @@
#include "path-util.h"
#include "polkit.h"
#include "special.h"
+#include "sleep-config.h"
#include "systemd/sd-id128.h"
#include "systemd/sd-messages.h"
+#include "fileio-label.h"
+#include "label.h"
#define BUS_MANAGER_INTERFACE \
" <interface name=\"org.freedesktop.login1.Manager\">\n" \
@@ -64,7 +67,7 @@
" <method name=\"CreateSession\">\n" \
" <arg name=\"uid\" type=\"u\" direction=\"in\"/>\n" \
" <arg name=\"leader\" type=\"u\" direction=\"in\"/>\n" \
- " <arg name=\"sevice\" type=\"s\" direction=\"in\"/>\n" \
+ " <arg name=\"service\" type=\"s\" direction=\"in\"/>\n" \
" <arg name=\"type\" type=\"s\" direction=\"in\"/>\n" \
" <arg name=\"class\" type=\"s\" direction=\"in\"/>\n" \
" <arg name=\"seat\" type=\"s\" direction=\"in\"/>\n" \
@@ -102,6 +105,7 @@
" <arg name=\"id\" type=\"s\" direction=\"in\"/>\n" \
" </method>\n" \
" <method name=\"LockSessions\"/>\n" \
+ " <method name=\"UnlockSessions\"/>\n" \
" <method name=\"KillSession\">\n" \
" <arg name=\"id\" type=\"s\" direction=\"in\"/>\n" \
" <arg name=\"who\" type=\"s\" direction=\"in\"/>\n" \
@@ -296,30 +300,30 @@ static int bus_manager_append_preparing(DBusMessageIter *i, const char *property
assert(property);
if (streq(property, "PreparingForShutdown"))
- b = !!(m->delayed_what & INHIBIT_SHUTDOWN);
+ b = !!(m->action_what & INHIBIT_SHUTDOWN);
else
- b = !!(m->delayed_what & INHIBIT_SLEEP);
+ b = !!(m->action_what & INHIBIT_SLEEP);
dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b);
return 0;
}
static int bus_manager_create_session(Manager *m, DBusMessage *message, DBusMessage **_reply) {
- Session *session = NULL;
- User *user = NULL;
- const char *type, *class, *seat, *tty, *display, *remote_user, *remote_host, *service;
+ const char *type, *class, *cseat, *tty, *display, *remote_user, *remote_host, *service;
uint32_t uid, leader, audit_id = 0;
dbus_bool_t remote, kill_processes, exists;
- char **controllers = NULL, **reset_controllers = NULL;
+ _cleanup_strv_free_ char **controllers = NULL, **reset_controllers = NULL;
+ _cleanup_free_ char *cgroup = NULL, *id = NULL, *p = NULL;
SessionType t;
SessionClass c;
- Seat *s;
DBusMessageIter iter;
int r;
- char *id = NULL, *p;
uint32_t vtnr = 0;
- int fifo_fd = -1;
- DBusMessage *reply = NULL;
+ _cleanup_close_ int fifo_fd = -1;
+ _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
+ Session *session = NULL;
+ User *user = NULL;
+ Seat *seat = NULL;
bool b;
assert(m);
@@ -350,31 +354,38 @@ static int bus_manager_create_session(Manager *m, DBusMessage *message, DBusMess
return -EINVAL;
dbus_message_iter_get_basic(&iter, &type);
- t = session_type_from_string(type);
+ if (isempty(type))
+ t = _SESSION_TYPE_INVALID;
+ else {
+ t = session_type_from_string(type);
+ if (t < 0)
+ return -EINVAL;
+ }
- if (t < 0 ||
- !dbus_message_iter_next(&iter) ||
+ if (!dbus_message_iter_next(&iter) ||
dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
return -EINVAL;
dbus_message_iter_get_basic(&iter, &class);
if (isempty(class))
- c = SESSION_USER;
- else
+ c = _SESSION_CLASS_INVALID;
+ else {
c = session_class_from_string(class);
+ if (c < 0)
+ return -EINVAL;
+ }
- if (c < 0 ||
- !dbus_message_iter_next(&iter) ||
+ if (!dbus_message_iter_next(&iter) ||
dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
return -EINVAL;
- dbus_message_iter_get_basic(&iter, &seat);
+ dbus_message_iter_get_basic(&iter, &cseat);
- if (isempty(seat))
- s = NULL;
+ if (isempty(cseat))
+ seat = NULL;
else {
- s = hashmap_get(m->seats, seat);
- if (!s)
+ seat = hashmap_get(m->seats, cseat);
+ if (!seat)
return -ENOENT;
}
@@ -393,9 +404,9 @@ static int bus_manager_create_session(Manager *m, DBusMessage *message, DBusMess
if (tty_is_vc(tty)) {
int v;
- if (!s)
- s = m->vtconsole;
- else if (s != m->vtconsole)
+ if (!seat)
+ seat = m->vtconsole;
+ else if (seat != m->vtconsole)
return -EINVAL;
v = vtnr_from_tty(tty);
@@ -409,18 +420,17 @@ static int bus_manager_create_session(Manager *m, DBusMessage *message, DBusMess
return -EINVAL;
} else if (tty_is_console(tty)) {
- if (!s)
- s = m->vtconsole;
- else if (s != m->vtconsole)
+ if (!seat)
+ seat = m->vtconsole;
+ else if (seat != m->vtconsole)
return -EINVAL;
if (vtnr != 0)
return -EINVAL;
-
}
- if (s) {
- if (seat_can_multi_session(s)) {
+ if (seat) {
+ if (seat_can_multi_session(seat)) {
if (vtnr > 63)
return -EINVAL;
} else {
@@ -439,6 +449,22 @@ static int bus_manager_create_session(Manager *m, DBusMessage *message, DBusMess
dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_BOOLEAN)
return -EINVAL;
+ 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 (!isempty(display) || !isempty(tty))
+ c = SESSION_USER;
+ else
+ c = SESSION_BACKGROUND;
+ }
+
dbus_message_iter_get_basic(&iter, &remote);
if (!dbus_message_iter_next(&iter) ||
@@ -462,8 +488,7 @@ static int bus_manager_create_session(Manager *m, DBusMessage *message, DBusMess
if (r < 0)
return -EINVAL;
- if (strv_contains(controllers, "systemd") ||
- !dbus_message_iter_next(&iter) ||
+ if (!dbus_message_iter_next(&iter) ||
dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRING) {
r = -EINVAL;
@@ -474,8 +499,7 @@ static int bus_manager_create_session(Manager *m, DBusMessage *message, DBusMess
if (r < 0)
goto fail;
- if (strv_contains(reset_controllers, "systemd") ||
- !dbus_message_iter_next(&iter) ||
+ if (!dbus_message_iter_next(&iter) ||
dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_BOOLEAN) {
r = -EINVAL;
goto fail;
@@ -483,78 +507,84 @@ static int bus_manager_create_session(Manager *m, DBusMessage *message, DBusMess
dbus_message_iter_get_basic(&iter, &kill_processes);
- r = manager_add_user_by_uid(m, uid, &user);
+ r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, leader, &cgroup);
if (r < 0)
goto fail;
- audit_session_from_pid(leader, &audit_id);
-
- if (audit_id > 0) {
- asprintf(&id, "%lu", (unsigned long) audit_id);
+ r = manager_get_session_by_cgroup(m, cgroup, &session);
+ if (r < 0)
+ goto fail;
- if (!id) {
- r = -ENOMEM;
+ if (session) {
+ fifo_fd = session_create_fifo(session);
+ if (fifo_fd < 0) {
+ r = fifo_fd;
goto fail;
}
- session = hashmap_get(m->sessions, id);
+ /* Session already exists, client is probably
+ * something like "su" which changes uid but
+ * is still the same audit session */
- if (session) {
- free(id);
+ reply = dbus_message_new_method_return(message);
+ if (!reply) {
+ r = -ENOMEM;
+ goto fail;
+ }
- fifo_fd = session_create_fifo(session);
- if (fifo_fd < 0) {
- r = fifo_fd;
- goto fail;
- }
+ p = session_bus_path(session);
+ if (!p) {
+ r = -ENOMEM;
+ goto fail;
+ }
- /* Session already exists, client is probably
- * something like "su" which changes uid but
- * is still the same audit session */
+ cseat = session->seat ? session->seat->id : "";
+ vtnr = session->vtnr;
+ exists = true;
- reply = dbus_message_new_method_return(message);
- if (!reply) {
- r = -ENOMEM;
- goto fail;
- }
+ b = dbus_message_append_args(
+ reply,
+ DBUS_TYPE_STRING, &session->id,
+ DBUS_TYPE_OBJECT_PATH, &p,
+ DBUS_TYPE_STRING, &session->user->runtime_path,
+ DBUS_TYPE_UNIX_FD, &fifo_fd,
+ DBUS_TYPE_STRING, &cseat,
+ DBUS_TYPE_UINT32, &vtnr,
+ DBUS_TYPE_BOOLEAN, &exists,
+ DBUS_TYPE_INVALID);
+ if (!b) {
+ r = -ENOMEM;
+ goto fail;
+ }
- p = session_bus_path(session);
- if (!p) {
- r = -ENOMEM;
- goto fail;
- }
+ *_reply = reply;
+ reply = NULL;
- seat = session->seat ? session->seat->id : "";
- vtnr = session->vtnr;
- exists = true;
-
- b = dbus_message_append_args(
- reply,
- DBUS_TYPE_STRING, &session->id,
- DBUS_TYPE_OBJECT_PATH, &p,
- DBUS_TYPE_STRING, &session->user->runtime_path,
- DBUS_TYPE_UNIX_FD, &fifo_fd,
- DBUS_TYPE_STRING, &seat,
- DBUS_TYPE_UINT32, &vtnr,
- DBUS_TYPE_BOOLEAN, &exists,
- DBUS_TYPE_INVALID);
- free(p);
+ return 0;
+ }
- if (!b) {
- r = -ENOMEM;
- goto fail;
- }
+ audit_session_from_pid(leader, &audit_id);
+ if (audit_id > 0) {
+ /* Keep our session IDs and the audit session IDs in sync */
- close_nointr_nofail(fifo_fd);
- *_reply = reply;
+ if (asprintf(&id, "%lu", (unsigned long) audit_id) < 0) {
+ r = -ENOMEM;
+ goto fail;
+ }
- strv_free(controllers);
- strv_free(reset_controllers);
+ /* 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)) {
+ audit_id = 0;
- return 0;
+ free(id);
+ id = NULL;
}
+ }
- } else {
+ if (!id) {
do {
free(id);
id = NULL;
@@ -567,8 +597,11 @@ static int bus_manager_create_session(Manager *m, DBusMessage *message, DBusMess
} 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, user, id, &session);
- free(id);
if (r < 0)
goto fail;
@@ -577,11 +610,11 @@ static int bus_manager_create_session(Manager *m, DBusMessage *message, DBusMess
session->type = t;
session->class = c;
session->remote = remote;
- session->controllers = controllers;
- session->reset_controllers = reset_controllers;
session->kill_processes = kill_processes;
session->vtnr = vtnr;
+ session->controllers = cg_shorten_controllers(controllers);
+ session->reset_controllers = cg_shorten_controllers(reset_controllers);
controllers = reset_controllers = NULL;
if (!isempty(tty)) {
@@ -630,8 +663,8 @@ static int bus_manager_create_session(Manager *m, DBusMessage *message, DBusMess
goto fail;
}
- if (s) {
- r = seat_attach_session(s, session);
+ if (seat) {
+ r = seat_attach_session(seat, session);
if (r < 0)
goto fail;
}
@@ -652,7 +685,7 @@ static int bus_manager_create_session(Manager *m, DBusMessage *message, DBusMess
goto fail;
}
- seat = s ? s->id : "";
+ cseat = seat ? seat->id : "";
exists = false;
b = dbus_message_append_args(
reply,
@@ -660,42 +693,38 @@ static int bus_manager_create_session(Manager *m, DBusMessage *message, DBusMess
DBUS_TYPE_OBJECT_PATH, &p,
DBUS_TYPE_STRING, &session->user->runtime_path,
DBUS_TYPE_UNIX_FD, &fifo_fd,
- DBUS_TYPE_STRING, &seat,
+ DBUS_TYPE_STRING, &cseat,
DBUS_TYPE_UINT32, &vtnr,
DBUS_TYPE_BOOLEAN, &exists,
DBUS_TYPE_INVALID);
- free(p);
if (!b) {
r = -ENOMEM;
goto fail;
}
- close_nointr_nofail(fifo_fd);
*_reply = reply;
+ reply = NULL;
return 0;
fail:
- strv_free(controllers);
- strv_free(reset_controllers);
-
if (session)
session_add_to_gc_queue(session);
if (user)
user_add_to_gc_queue(user);
- if (fifo_fd >= 0)
- close_nointr_nofail(fifo_fd);
-
- if (reply)
- dbus_message_unref(reply);
-
return r;
}
-static int bus_manager_inhibit(Manager *m, DBusConnection *connection, DBusMessage *message, DBusError *error, DBusMessage **_reply) {
+static int bus_manager_inhibit(
+ Manager *m,
+ DBusConnection *connection,
+ DBusMessage *message,
+ DBusError *error,
+ DBusMessage **_reply) {
+
Inhibitor *i = NULL;
char *id = NULL;
const char *who, *why, *what, *mode;
@@ -704,7 +733,7 @@ static int bus_manager_inhibit(Manager *m, DBusConnection *connection, DBusMessa
InhibitMode mm;
unsigned long ul;
int r, fifo_fd = -1;
- DBusMessage *reply = NULL;
+ _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
assert(m);
assert(connection);
@@ -742,6 +771,15 @@ static int bus_manager_inhibit(Manager *m, DBusConnection *connection, DBusMessa
goto fail;
}
+ /* 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) {
+ r = -EALREADY;
+ goto fail;
+ }
+
r = verify_polkit(connection, message,
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") :
@@ -816,6 +854,7 @@ static int bus_manager_inhibit(Manager *m, DBusConnection *connection, DBusMessa
close_nointr_nofail(fifo_fd);
*_reply = reply;
+ reply = NULL;
inhibitor_start(i);
@@ -828,9 +867,6 @@ fail:
if (fifo_fd >= 0)
close_nointr_nofail(fifo_fd);
- if (reply)
- dbus_message_unref(reply);
-
return r;
}
@@ -872,7 +908,7 @@ static int trigger_device(Manager *m, struct udev_device *d) {
goto finish;
}
- write_one_line_file(t, "change");
+ write_string_file(t, "change");
free(t);
}
@@ -887,7 +923,7 @@ finish:
static int attach_device(Manager *m, const char *seat, const char *sysfs) {
struct udev_device *d;
- char *rule = NULL, *file = NULL;
+ _cleanup_free_ char *rule = NULL, *file = NULL;
const char *id_for_seat;
int r;
@@ -921,16 +957,14 @@ static int attach_device(Manager *m, const char *seat, const char *sysfs) {
}
mkdir_p_label("/etc/udev/rules.d", 0755);
- r = write_one_line_file_atomic(file, rule);
+ label_init("/etc");
+ r = write_string_file_atomic_label(file, rule);
if (r < 0)
goto finish;
r = trigger_device(m, d);
finish:
- free(rule);
- free(file);
-
if (d)
udev_device_unref(d);
@@ -938,7 +972,7 @@ finish:
}
static int flush_devices(Manager *m) {
- DIR *d;
+ _cleanup_closedir_ DIR *d;
assert(m);
@@ -963,8 +997,6 @@ static int flush_devices(Manager *m) {
if (unlinkat(dirfd(d), de->d_name, 0) < 0)
log_warning("Failed to unlink %s: %m", de->d_name);
}
-
- closedir(d);
}
return trigger_device(m, NULL);
@@ -979,75 +1011,115 @@ static int have_multiple_sessions(
assert(m);
- /* Check for other users' sessions. Greeter sessions do not count. */
+ /* 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)
+ if (session->class == SESSION_USER &&
+ session->user->uid != uid)
return true;
return false;
}
-static int send_start_unit(DBusConnection *connection, const char *unit_name, DBusError *error) {
- const char *mode = "replace";
+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;
+ }
+
+ return log_struct(LOG_NOTICE, MESSAGE_ID(SD_MESSAGE_SHUTDOWN),
+ p,
+ q, NULL);
+}
+
+static int execute_shutdown_or_sleep(
+ Manager *m,
+ InhibitWhat w,
+ const char *unit_name,
+ DBusError *error) {
+
+ _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
+ const char *mode = "replace-irreversibly", *p;
+ int r;
+ char *c;
+ assert(m);
+ assert(w >= 0);
+ assert(w < _INHIBIT_WHAT_MAX);
assert(unit_name);
- return bus_method_call_with_reply (
- connection,
+ bus_manager_log_shutdown(m, w, unit_name);
+
+ r = bus_method_call_with_reply(
+ m->bus,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
"StartUnit",
- NULL,
- NULL,
+ &reply,
+ error,
DBUS_TYPE_STRING, &unit_name,
DBUS_TYPE_STRING, &mode,
DBUS_TYPE_INVALID);
-}
-
-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"
- };
-
- dbus_bool_t active = _active;
- DBusMessage *message;
- int r = 0;
+ if (r < 0)
+ return r;
- assert(m);
- assert(w >= 0);
- assert(w < _INHIBIT_WHAT_MAX);
- assert(signal_name[w]);
+ if (!dbus_message_get_args(
+ reply,
+ error,
+ DBUS_TYPE_OBJECT_PATH, &p,
+ DBUS_TYPE_INVALID))
+ return -EINVAL;
- message = dbus_message_new_signal("/org/freedesktop/login1", "org.freedesktop.login1.Manager", signal_name[w]);
- if (!message)
+ c = strdup(p);
+ if (!c)
return -ENOMEM;
- if (!dbus_message_append_args(message, DBUS_TYPE_BOOLEAN, &active, DBUS_TYPE_INVALID) ||
- !dbus_connection_send(m->bus, message, NULL))
- r = -ENOMEM;
+ m->action_unit = unit_name;
+ free(m->action_job);
+ m->action_job = c;
+ m->action_what = w;
- dbus_message_unref(message);
- return r;
+ return 0;
}
-static int delay_shutdown_or_sleep(Manager *m, InhibitWhat w, const char *unit_name) {
+static int delay_shutdown_or_sleep(
+ Manager *m,
+ InhibitWhat w,
+ const char *unit_name) {
+
assert(m);
assert(w >= 0);
assert(w < _INHIBIT_WHAT_MAX);
+ assert(unit_name);
- /* Tell everybody to prepare for shutdown/sleep */
- send_prepare_for(m, w, true);
-
- /* Update timestamp for timeout */
- if (!m->delayed_unit)
- m->delayed_timestamp = now(CLOCK_MONOTONIC);
-
- /* Remember what we want to do, possibly overriding what kind
- * of unit we previously queued. */
- m->delayed_unit = unit_name;
- m->delayed_what = w;
+ m->action_timestamp = now(CLOCK_MONOTONIC);
+ m->action_unit = unit_name;
+ m->action_what = w;
return 0;
}
@@ -1060,14 +1132,13 @@ static int bus_manager_can_shutdown_or_sleep(
const char *action,
const char *action_multiple_sessions,
const char *action_ignore_inhibit,
- const char *sleep_type,
- const char *sleep_disk_type,
+ const char *sleep_verb,
DBusError *error,
DBusMessage **_reply) {
bool multiple_sessions, challenge, blocked, b;
- const char *result;
- DBusMessage *reply = NULL;
+ const char *result = NULL;
+ _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
int r;
unsigned long ul;
@@ -1082,22 +1153,10 @@ static int bus_manager_can_shutdown_or_sleep(
assert(error);
assert(_reply);
- if (sleep_type) {
- r = can_sleep(sleep_type);
- if (r < 0)
- return r;
-
- if (r == 0) {
- result = "na";
- goto finish;
- }
- }
-
- if (sleep_disk_type) {
- r = can_sleep_disk(sleep_disk_type);
+ if (sleep_verb) {
+ r = can_sleep(sleep_verb);
if (r < 0)
return r;
-
if (r == 0) {
result = "na";
goto finish;
@@ -1166,48 +1225,37 @@ finish:
reply,
DBUS_TYPE_STRING, &result,
DBUS_TYPE_INVALID);
- if (!b) {
- dbus_message_unref(reply);
+ if (!b)
return -ENOMEM;
- }
*_reply = reply;
+ reply = NULL;
return 0;
}
-static int bus_manager_log_shutdown(
- Manager *m,
- InhibitWhat w,
- const char *unit_name) {
+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"
+ };
- const char *p, *q;
+ dbus_bool_t active = _active;
+ _cleanup_dbus_message_unref_ DBusMessage *message = NULL;
assert(m);
- assert(unit_name);
+ assert(w >= 0);
+ assert(w < _INHIBIT_WHAT_MAX);
+ assert(signal_name[w]);
- if (w != INHIBIT_SHUTDOWN)
- return 0;
+ message = dbus_message_new_signal("/org/freedesktop/login1", "org.freedesktop.login1.Manager", signal_name[w]);
+ if (!message)
+ return -ENOMEM;
- 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 (!dbus_message_append_args(message, DBUS_TYPE_BOOLEAN, &active, DBUS_TYPE_INVALID) ||
+ !dbus_connection_send(m->bus, message, NULL))
+ return -ENOMEM;
- return log_struct(LOG_NOTICE, MESSAGE_ID(SD_MESSAGE_SHUTDOWN),
- p,
- q, NULL);
+ return 0;
}
int bus_manager_shutdown_or_sleep_now_or_later(
@@ -1223,6 +1271,10 @@ int bus_manager_shutdown_or_sleep_now_or_later(
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 &&
@@ -1232,13 +1284,10 @@ int bus_manager_shutdown_or_sleep_now_or_later(
/* 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 {
- bus_manager_log_shutdown(m, w, unit_name);
-
+ else
/* Shutdown is not delayed, execute it
* immediately */
- r = send_start_unit(m->bus, unit_name, error);
- }
+ r = execute_shutdown_or_sleep(m, w, unit_name, error);
return r;
}
@@ -1252,8 +1301,7 @@ static int bus_manager_do_shutdown_or_sleep(
const char *action,
const char *action_multiple_sessions,
const char *action_ignore_inhibit,
- const char *sleep_type,
- const char *sleep_disk_type,
+ const char *sleep_verb,
DBusError *error,
DBusMessage **_reply) {
@@ -1275,6 +1323,10 @@ static int bus_manager_do_shutdown_or_sleep(
assert(error);
assert(_reply);
+ /* Don't allow multiple jobs being executed at the same time */
+ if (m->action_what)
+ return -EALREADY;
+
if (!dbus_message_get_args(
message,
error,
@@ -1282,17 +1334,8 @@ static int bus_manager_do_shutdown_or_sleep(
DBUS_TYPE_INVALID))
return -EINVAL;
- if (sleep_type) {
- r = can_sleep(sleep_type);
- if (r < 0)
- return r;
-
- if (r == 0)
- return -ENOTSUP;
- }
-
- if (sleep_disk_type) {
- r = can_sleep_disk(sleep_disk_type);
+ if (sleep_verb) {
+ r = can_sleep(sleep_verb);
if (r < 0)
return r;
@@ -1376,7 +1419,7 @@ static DBusHandlerResult manager_message_handler(
Manager *m = userdata;
DBusError error;
- DBusMessage *reply = NULL;
+ _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
int r;
assert(connection);
@@ -1824,8 +1867,10 @@ static DBusHandlerResult manager_message_handler(
if (!reply)
goto oom;
- } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "LockSessions")) {
- r = session_send_lock_all(m, true);
+ } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "LockSessions") ||
+ dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "UnlockSessions")) {
+
+ r = session_send_lock_all(m, streq(dbus_message_get_member(message), "LockSessions"));
if (r < 0)
bus_send_error_reply(connection, message, NULL, r);
@@ -2093,7 +2138,7 @@ static DBusHandlerResult manager_message_handler(
"org.freedesktop.login1.power-off",
"org.freedesktop.login1.power-off-multiple-sessions",
"org.freedesktop.login1.power-off-ignore-inhibit",
- NULL, NULL,
+ NULL,
&error, &reply);
if (r < 0)
return bus_send_error_reply(connection, message, &error, r);
@@ -2105,7 +2150,7 @@ static DBusHandlerResult manager_message_handler(
"org.freedesktop.login1.reboot",
"org.freedesktop.login1.reboot-multiple-sessions",
"org.freedesktop.login1.reboot-ignore-inhibit",
- NULL, NULL,
+ NULL,
&error, &reply);
if (r < 0)
return bus_send_error_reply(connection, message, &error, r);
@@ -2118,7 +2163,7 @@ static DBusHandlerResult manager_message_handler(
"org.freedesktop.login1.suspend",
"org.freedesktop.login1.suspend-multiple-sessions",
"org.freedesktop.login1.suspend-ignore-inhibit",
- "mem", NULL,
+ "suspend",
&error, &reply);
if (r < 0)
return bus_send_error_reply(connection, message, &error, r);
@@ -2130,7 +2175,7 @@ static DBusHandlerResult manager_message_handler(
"org.freedesktop.login1.hibernate",
"org.freedesktop.login1.hibernate-multiple-sessions",
"org.freedesktop.login1.hibernate-ignore-inhibit",
- "disk", NULL,
+ "hibernate",
&error, &reply);
if (r < 0)
return bus_send_error_reply(connection, message, &error, r);
@@ -2143,7 +2188,7 @@ static DBusHandlerResult manager_message_handler(
"org.freedesktop.login1.hibernate",
"org.freedesktop.login1.hibernate-multiple-sessions",
"org.freedesktop.login1.hibernate-ignore-inhibit",
- "disk", "suspend",
+ "hybrid-sleep",
&error, &reply);
if (r < 0)
return bus_send_error_reply(connection, message, &error, r);
@@ -2156,7 +2201,7 @@ static DBusHandlerResult manager_message_handler(
"org.freedesktop.login1.power-off",
"org.freedesktop.login1.power-off-multiple-sessions",
"org.freedesktop.login1.power-off-ignore-inhibit",
- NULL, NULL,
+ NULL,
&error, &reply);
if (r < 0)
return bus_send_error_reply(connection, message, &error, r);
@@ -2167,7 +2212,7 @@ static DBusHandlerResult manager_message_handler(
"org.freedesktop.login1.reboot",
"org.freedesktop.login1.reboot-multiple-sessions",
"org.freedesktop.login1.reboot-ignore-inhibit",
- NULL, NULL,
+ NULL,
&error, &reply);
if (r < 0)
return bus_send_error_reply(connection, message, &error, r);
@@ -2179,7 +2224,7 @@ static DBusHandlerResult manager_message_handler(
"org.freedesktop.login1.suspend",
"org.freedesktop.login1.suspend-multiple-sessions",
"org.freedesktop.login1.suspend-ignore-inhibit",
- "mem", NULL,
+ "suspend",
&error, &reply);
if (r < 0)
return bus_send_error_reply(connection, message, &error, r);
@@ -2191,7 +2236,7 @@ static DBusHandlerResult manager_message_handler(
"org.freedesktop.login1.hibernate",
"org.freedesktop.login1.hibernate-multiple-sessions",
"org.freedesktop.login1.hibernate-ignore-inhibit",
- "disk", NULL,
+ "hibernate",
&error, &reply);
if (r < 0)
return bus_send_error_reply(connection, message, &error, r);
@@ -2203,7 +2248,7 @@ static DBusHandlerResult manager_message_handler(
"org.freedesktop.login1.hibernate",
"org.freedesktop.login1.hibernate-multiple-sessions",
"org.freedesktop.login1.hibernate-ignore-inhibit",
- "disk", "suspend",
+ "hybrid-sleep",
&error, &reply);
if (r < 0)
return bus_send_error_reply(connection, message, &error, r);
@@ -2282,16 +2327,11 @@ static DBusHandlerResult manager_message_handler(
if (reply) {
if (!bus_maybe_send_reply(connection, message, reply))
goto oom;
-
- dbus_message_unref(reply);
}
return DBUS_HANDLER_RESULT_HANDLED;
oom:
- if (reply)
- dbus_message_unref(reply);
-
dbus_error_free(&error);
return DBUS_HANDLER_RESULT_NEED_MEMORY;
@@ -2324,6 +2364,30 @@ DBusHandlerResult bus_message_filter(
log_error("Failed to parse Released message: %s", bus_error_message(&error));
else
manager_cgroup_notify_empty(m, cgroup);
+
+ } else if (dbus_message_is_signal(message, "org.freedesktop.systemd1.Manager", "JobRemoved")) {
+ uint32_t id;
+ const char *path, *result, *unit;
+
+ if (!dbus_message_get_args(message, &error,
+ DBUS_TYPE_UINT32, &id,
+ DBUS_TYPE_OBJECT_PATH, &path,
+ DBUS_TYPE_STRING, &unit,
+ DBUS_TYPE_STRING, &result,
+ DBUS_TYPE_INVALID))
+ log_error("Failed to parse JobRemoved message: %s", bus_error_message(&error));
+
+ else if (m->action_job && streq(m->action_job, path)) {
+ log_info("Operation finished.");
+
+ /* Tell people that they now may take a lock again */
+ send_prepare_for(m, m->action_what, false);
+
+ free(m->action_job);
+ m->action_job = NULL;
+ m->action_unit = NULL;
+ m->action_what = 0;
+ }
}
dbus_error_free(&error);
@@ -2332,62 +2396,51 @@ DBusHandlerResult bus_message_filter(
}
int manager_send_changed(Manager *manager, const char *properties) {
- DBusMessage *m;
- int r = -ENOMEM;
+ _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
assert(manager);
- m = bus_properties_changed_new("/org/freedesktop/login1", "org.freedesktop.login1.Manager", properties);
+ m = bus_properties_changed_new("/org/freedesktop/login1",
+ "org.freedesktop.login1.Manager",
+ properties);
if (!m)
- goto finish;
+ return -ENOMEM;
if (!dbus_connection_send(manager->bus, m, NULL))
- goto finish;
-
- r = 0;
-
-finish:
- if (m)
- dbus_message_unref(m);
+ return -ENOMEM;
- return r;
+ return 0;
}
int manager_dispatch_delayed(Manager *manager) {
- const char *unit_name;
DBusError error;
- bool delayed;
int r;
assert(manager);
- if (!manager->delayed_unit)
+ if (manager->action_what == 0 || manager->action_job)
return 0;
/* Continue delay? */
- delayed =
- manager->delayed_timestamp + manager->inhibit_delay_max > now(CLOCK_MONOTONIC) &&
- manager_is_inhibited(manager, manager->delayed_what, INHIBIT_DELAY, NULL, false, false, 0);
- if (delayed)
- return 0;
+ if (manager_is_inhibited(manager, manager->action_what, INHIBIT_DELAY, NULL, false, false, 0)) {
- bus_manager_log_shutdown(manager, manager->delayed_what, manager->delayed_unit);
+ if (manager->action_timestamp + manager->inhibit_delay_max > now(CLOCK_MONOTONIC))
+ return 0;
- /* Reset delay data */
- unit_name = manager->delayed_unit;
- manager->delayed_unit = NULL;
+ log_info("Delay lock is active but inhibitor timeout is reached.");
+ }
- /* Actually do the shutdown */
+ /* Actually do the operation */
dbus_error_init(&error);
- r = send_start_unit(manager->bus, unit_name, &error);
+ 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_or_strerror(&error, -r));
+ log_warning("Failed to send delayed message: %s", bus_error(&error, r));
dbus_error_free(&error);
+
+ manager->action_unit = NULL;
+ manager->action_what = 0;
return r;
}
- /* Tell people about it */
- send_prepare_for(manager, manager->delayed_what, false);
-
return 1;
}
diff --git a/src/login/logind-gperf.gperf b/src/login/logind-gperf.gperf
index 076d116161..735d2dbc9c 100644
--- a/src/login/logind-gperf.gperf
+++ b/src/login/logind-gperf.gperf
@@ -21,7 +21,7 @@ Login.KillOnlyUsers, config_parse_strv, 0, offsetof(Manag
Login.KillExcludeUsers, config_parse_strv, 0, offsetof(Manager, kill_exclude_users)
Login.Controllers, config_parse_strv, 0, offsetof(Manager, controllers)
Login.ResetControllers, config_parse_strv, 0, offsetof(Manager, reset_controllers)
-Login.InhibitDelayMaxSec, config_parse_usec, 0, offsetof(Manager, inhibit_delay_max)
+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)
@@ -31,4 +31,4 @@ Login.SuspendKeyIgnoreInhibited, config_parse_bool, 0, offsetof(Manag
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.IdleAction, config_parse_handle_action, 0, offsetof(Manager, idle_action)
-Login.IdleActionSec, config_parse_usec, 0, offsetof(Manager, idle_action_usec)
+Login.IdleActionSec, config_parse_sec, 0, offsetof(Manager, idle_action_usec)
diff --git a/src/login/logind-inhibit.c b/src/login/logind-inhibit.c
index f1b9cca834..e77088364a 100644
--- a/src/login/logind-inhibit.c
+++ b/src/login/logind-inhibit.c
@@ -30,6 +30,7 @@
#include "mkdir.h"
#include "path-util.h"
#include "logind-inhibit.h"
+#include "fileio.h"
Inhibitor* inhibitor_new(Manager *m, const char* id) {
Inhibitor *i;
@@ -291,7 +292,7 @@ int inhibitor_create_fifo(Inhibitor *i) {
/* Open reading side */
if (i->fifo_fd < 0) {
- struct epoll_event ev;
+ struct epoll_event ev = {};
i->fifo_fd = open(i->fifo_path, O_RDONLY|O_CLOEXEC|O_NDELAY);
if (i->fifo_fd < 0)
@@ -301,7 +302,6 @@ int inhibitor_create_fifo(Inhibitor *i) {
if (r < 0)
return r;
- zero(ev);
ev.events = 0;
ev.data.u32 = FD_OTHER_BASE + i->fifo_fd;
@@ -353,9 +353,14 @@ static int pid_is_active(Manager *m, pid_t pid) {
int r;
r = manager_get_session_by_pid(m, pid, &s);
- if (r <= 0)
+ 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);
}
@@ -439,19 +444,19 @@ InhibitWhat inhibit_what_from_string(const char *s) {
size_t l;
FOREACH_WORD_SEPARATOR(w, l, s, ":", state) {
- if (l == 8 && strncmp(w, "shutdown", l) == 0)
+ if (l == 8 && strneq(w, "shutdown", l))
what |= INHIBIT_SHUTDOWN;
- else if (l == 5 && strncmp(w, "sleep", l) == 0)
+ else if (l == 5 && strneq(w, "sleep", l))
what |= INHIBIT_SLEEP;
- else if (l == 4 && strncmp(w, "idle", l) == 0)
+ else if (l == 4 && strneq(w, "idle", l))
what |= INHIBIT_IDLE;
- else if (l == 16 && strncmp(w, "handle-power-key", l) == 0)
+ else if (l == 16 && strneq(w, "handle-power-key", l))
what |= INHIBIT_HANDLE_POWER_KEY;
- else if (l == 18 && strncmp(w, "handle-suspend-key", l) == 0)
+ else if (l == 18 && strneq(w, "handle-suspend-key", l))
what |= INHIBIT_HANDLE_SUSPEND_KEY;
- else if (l == 20 && strncmp(w, "handle-hibernate-key", l) == 0)
+ else if (l == 20 && strneq(w, "handle-hibernate-key", l))
what |= INHIBIT_HANDLE_HIBERNATE_KEY;
- else if (l == 17 && strncmp(w, "handle-lid-switch", l) == 0)
+ else if (l == 17 && strneq(w, "handle-lid-switch", l))
what |= INHIBIT_HANDLE_LID_SWITCH;
else
return _INHIBIT_WHAT_INVALID;
diff --git a/src/login/logind-seat-dbus.c b/src/login/logind-seat-dbus.c
index 7833d70a03..5c535ba0ec 100644
--- a/src/login/logind-seat-dbus.c
+++ b/src/login/logind-seat-dbus.c
@@ -61,7 +61,7 @@ static int bus_seat_append_active(DBusMessageIter *i, const char *property, void
DBusMessageIter sub;
Seat *s = data;
const char *id, *path;
- char *p = NULL;
+ _cleanup_free_ char *p = NULL;
assert(i);
assert(property);
@@ -82,12 +82,8 @@ static int bus_seat_append_active(DBusMessageIter *i, const char *property, void
}
if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &id) ||
- !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &path)) {
- free(p);
+ !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &path))
return -ENOMEM;
- }
-
- free(p);
if (!dbus_message_iter_close_container(i, &sub))
return -ENOMEM;
@@ -108,7 +104,7 @@ static int bus_seat_append_sessions(DBusMessageIter *i, const char *property, vo
return -ENOMEM;
LIST_FOREACH(sessions_by_seat, session, s->sessions) {
- char *p;
+ _cleanup_free_ char *p = NULL;
if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2))
return -ENOMEM;
@@ -118,12 +114,8 @@ static int bus_seat_append_sessions(DBusMessageIter *i, const char *property, vo
return -ENOMEM;
if (!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &session->id) ||
- !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &p)) {
- free(p);
+ !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &p))
return -ENOMEM;
- }
-
- free(p);
if (!dbus_message_iter_close_container(&sub, &sub2))
return -ENOMEM;
@@ -260,7 +252,7 @@ static DBusHandlerResult seat_message_dispatch(
DBusMessage *message) {
DBusError error;
- DBusMessage *reply = NULL;
+ _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
int r;
assert(s);
@@ -310,18 +302,13 @@ static DBusHandlerResult seat_message_dispatch(
}
if (reply) {
- if (!dbus_connection_send(connection, reply, NULL))
+ if (!bus_maybe_send_reply(connection, message, reply))
goto oom;
-
- dbus_message_unref(reply);
}
return DBUS_HANDLER_RESULT_HANDLED;
oom:
- if (reply)
- dbus_message_unref(reply);
-
dbus_error_free(&error);
return DBUS_HANDLER_RESULT_NEED_MEMORY;
@@ -361,7 +348,7 @@ const DBusObjectPathVTable bus_seat_vtable = {
};
char *seat_bus_path(Seat *s) {
- char *t, *r;
+ _cleanup_free_ char *t;
assert(s);
@@ -369,53 +356,41 @@ char *seat_bus_path(Seat *s) {
if (!t)
return NULL;
- r = strappend("/org/freedesktop/login1/seat/", t);
- free(t);
-
- return r;
+ return strappend("/org/freedesktop/login1/seat/", t);
}
int seat_send_signal(Seat *s, bool new_seat) {
- DBusMessage *m;
- int r = -ENOMEM;
- char *p = NULL;
+ _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
+ _cleanup_free_ char *p = NULL;
assert(s);
m = dbus_message_new_signal("/org/freedesktop/login1",
"org.freedesktop.login1.Manager",
new_seat ? "SeatNew" : "SeatRemoved");
-
if (!m)
return -ENOMEM;
p = seat_bus_path(s);
if (!p)
- goto finish;
+ return -ENOMEM;
if (!dbus_message_append_args(
m,
DBUS_TYPE_STRING, &s->id,
DBUS_TYPE_OBJECT_PATH, &p,
DBUS_TYPE_INVALID))
- goto finish;
+ return -ENOMEM;
if (!dbus_connection_send(s->manager->bus, m, NULL))
- goto finish;
-
- r = 0;
-
-finish:
- dbus_message_unref(m);
- free(p);
+ return -ENOMEM;
- return r;
+ return 0;
}
int seat_send_changed(Seat *s, const char *properties) {
- DBusMessage *m;
- int r = -ENOMEM;
- char *p = NULL;
+ _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
+ _cleanup_free_ char *p = NULL;
assert(s);
@@ -428,17 +403,10 @@ int seat_send_changed(Seat *s, const char *properties) {
m = bus_properties_changed_new(p, "org.freedesktop.login1.Seat", properties);
if (!m)
- goto finish;
+ return -ENOMEM;
if (!dbus_connection_send(s->manager->bus, m, NULL))
- goto finish;
-
- r = 0;
-
-finish:
- if (m)
- dbus_message_unref(m);
- free(p);
+ return -ENOMEM;
- return r;
+ return 0;
}
diff --git a/src/login/logind-session-dbus.c b/src/login/logind-session-dbus.c
index ef73cd434a..ec823af547 100644
--- a/src/login/logind-session-dbus.c
+++ b/src/login/logind-session-dbus.c
@@ -108,12 +108,8 @@ static int bus_session_append_seat(DBusMessageIter *i, const char *property, voi
}
if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &id) ||
- !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &path)) {
- free(p);
+ !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &path))
return -ENOMEM;
- }
-
- free(p);
if (!dbus_message_iter_close_container(i, &sub))
return -ENOMEM;
@@ -124,7 +120,7 @@ static int bus_session_append_seat(DBusMessageIter *i, const char *property, voi
static int bus_session_append_user(DBusMessageIter *i, const char *property, void *data) {
DBusMessageIter sub;
User *u = data;
- char *p = NULL;
+ _cleanup_free_ char *p = NULL;
assert(i);
assert(property);
@@ -138,12 +134,8 @@ static int bus_session_append_user(DBusMessageIter *i, const char *property, voi
return -ENOMEM;
if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT32, &u->uid) ||
- !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &p)) {
- free(p);
+ !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &p))
return -ENOMEM;
- }
-
- free(p);
if (!dbus_message_iter_close_container(i, &sub))
return -ENOMEM;
@@ -205,7 +197,7 @@ static int bus_session_append_idle_hint_since(DBusMessageIter *i, const char *pr
static int bus_session_append_default_cgroup(DBusMessageIter *i, const char *property, void *data) {
Session *s = data;
- char *t;
+ _cleanup_free_ char *t = NULL;
int r;
bool success;
@@ -218,8 +210,6 @@ static int bus_session_append_default_cgroup(DBusMessageIter *i, const char *pro
return r;
success = dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &t);
- free(t);
-
return success ? 0 : -ENOMEM;
}
@@ -307,7 +297,7 @@ static DBusHandlerResult session_message_dispatch(
DBusMessage *message) {
DBusError error;
- DBusMessage *reply = NULL;
+ _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
int r;
assert(s);
@@ -412,18 +402,13 @@ static DBusHandlerResult session_message_dispatch(
}
if (reply) {
- if (!dbus_connection_send(connection, reply, NULL))
+ if (!bus_maybe_send_reply(connection, message, reply))
goto oom;
-
- dbus_message_unref(reply);
}
return DBUS_HANDLER_RESULT_HANDLED;
oom:
- if (reply)
- dbus_message_unref(reply);
-
dbus_error_free(&error);
return DBUS_HANDLER_RESULT_NEED_MEMORY;
@@ -463,7 +448,7 @@ const DBusObjectPathVTable bus_session_vtable = {
};
char *session_bus_path(Session *s) {
- char *t, *r;
+ _cleanup_free_ char *t;
assert(s);
@@ -471,16 +456,12 @@ char *session_bus_path(Session *s) {
if (!t)
return NULL;
- r = strappend("/org/freedesktop/login1/session/", t);
- free(t);
-
- return r;
+ return strappend("/org/freedesktop/login1/session/", t);
}
int session_send_signal(Session *s, bool new_session) {
- DBusMessage *m;
- int r = -ENOMEM;
- char *p = NULL;
+ _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
+ _cleanup_free_ char *p = NULL;
assert(s);
@@ -493,31 +474,24 @@ int session_send_signal(Session *s, bool new_session) {
p = session_bus_path(s);
if (!p)
- goto finish;
+ return -ENOMEM;
if (!dbus_message_append_args(
m,
DBUS_TYPE_STRING, &s->id,
DBUS_TYPE_OBJECT_PATH, &p,
DBUS_TYPE_INVALID))
- goto finish;
+ return -ENOMEM;
if (!dbus_connection_send(s->manager->bus, m, NULL))
- goto finish;
-
- r = 0;
-
-finish:
- dbus_message_unref(m);
- free(p);
+ return -ENOMEM;
- return r;
+ return 0;
}
int session_send_changed(Session *s, const char *properties) {
- DBusMessage *m;
- int r = -ENOMEM;
- char *p = NULL;
+ _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
+ _cleanup_free_ char *p = NULL;
assert(s);
@@ -530,25 +504,18 @@ int session_send_changed(Session *s, const char *properties) {
m = bus_properties_changed_new(p, "org.freedesktop.login1.Session", properties);
if (!m)
- goto finish;
+ return -ENOMEM;
if (!dbus_connection_send(s->manager->bus, m, NULL))
- goto finish;
-
- r = 0;
-
-finish:
- if (m)
- dbus_message_unref(m);
- free(p);
+ return -ENOMEM;
- return r;
+ return 0;
}
int session_send_lock(Session *s, bool lock) {
- DBusMessage *m;
+ _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
bool b;
- char *p;
+ _cleanup_free_ char *p = NULL;
assert(s);
@@ -557,14 +524,11 @@ int session_send_lock(Session *s, bool lock) {
return -ENOMEM;
m = dbus_message_new_signal(p, "org.freedesktop.login1.Session", lock ? "Lock" : "Unlock");
- free(p);
if (!m)
return -ENOMEM;
b = dbus_connection_send(s->manager->bus, m, NULL);
- dbus_message_unref(m);
-
if (!b)
return -ENOMEM;
diff --git a/src/login/logind-session.c b/src/login/logind-session.c
index b64a5d302d..662273b07f 100644
--- a/src/login/logind-session.c
+++ b/src/login/logind-session.c
@@ -33,6 +33,7 @@
#include "path-util.h"
#include "cgroup-util.h"
#include "logind-session.h"
+#include "fileio.h"
Session* session_new(Manager *m, User *u, const char *id) {
Session *s;
@@ -435,15 +436,14 @@ static int session_create_one_group(Session *s, const char *controller, const ch
int r;
assert(s);
- assert(controller);
assert(path);
if (s->leader > 0) {
r = cg_create_and_attach(controller, path, s->leader);
if (r < 0)
- r = cg_create(controller, path);
+ r = cg_create(controller, path, NULL);
} else
- r = cg_create(controller, path);
+ r = cg_create(controller, path, NULL);
if (r < 0)
return r;
@@ -465,7 +465,18 @@ static int session_create_cgroup(Session *s) {
assert(s->user->cgroup_path);
if (!s->cgroup_path) {
- if (asprintf(&p, "%s/%s", s->user->cgroup_path, s->id) < 0)
+ _cleanup_free_ char *name = NULL, *escaped = NULL;
+
+ name = strappend(s->id, ".session");
+ if (!name)
+ return log_oom();
+
+ escaped = cg_escape(name);
+ if (!escaped)
+ return log_oom();
+
+ p = strjoin(s->user->cgroup_path, "/", escaped, NULL);
+ if (!p)
return log_oom();
} else
p = s->cgroup_path;
@@ -709,6 +720,8 @@ int session_stop(Session *s) {
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);
@@ -720,8 +733,6 @@ int session_stop(Session *s) {
user_send_changed(s->user, "Sessions\0");
user_save(s->user);
- s->started = false;
-
return r;
}
@@ -772,7 +783,6 @@ static int get_process_ctty_atime(pid_t pid, usec_t *atime) {
}
int session_get_idle_hint(Session *s, dual_timestamp *t) {
- _cleanup_free_ char *p = NULL;
usec_t atime = 0, n;
int r;
@@ -786,7 +796,7 @@ int session_get_idle_hint(Session *s, dual_timestamp *t) {
return s->idle_hint;
}
- /* Graphical sessions really should really implement a real
+ /* Graphical sessions should really implement a real
* idle hint logic */
if (s->display)
goto dont_know;
@@ -898,7 +908,7 @@ int session_create_fifo(Session *s) {
/* Open reading side */
if (s->fifo_fd < 0) {
- struct epoll_event ev;
+ struct epoll_event ev = {};
s->fifo_fd = open(s->fifo_path, O_RDONLY|O_CLOEXEC|O_NDELAY);
if (s->fifo_fd < 0)
@@ -908,7 +918,6 @@ int session_create_fifo(Session *s) {
if (r < 0)
return r;
- zero(ev);
ev.events = 0;
ev.data.u32 = FD_OTHER_BASE + s->fifo_fd;
@@ -1057,7 +1066,8 @@ 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_LOCK_SCREEN] = "lock-screen",
+ [SESSION_BACKGROUND] = "background"
};
DEFINE_STRING_TABLE_LOOKUP(session_class, SessionClass);
diff --git a/src/login/logind-session.h b/src/login/logind-session.h
index 7598afa618..a73df3a3bc 100644
--- a/src/login/logind-session.h
+++ b/src/login/logind-session.h
@@ -37,22 +37,23 @@ typedef enum SessionState {
_SESSION_STATE_INVALID = -1
} SessionState;
-typedef enum SessionType {
- SESSION_UNSPECIFIED,
- SESSION_TTY,
- SESSION_X11,
- _SESSION_TYPE_MAX,
- _SESSION_TYPE_INVALID = -1
-} SessionType;
-
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_TYPE_MAX,
+ _SESSION_TYPE_INVALID = -1
+} SessionType;
+
typedef enum KillWho {
KILL_LEADER,
KILL_ALL,
@@ -134,14 +135,14 @@ int session_send_changed(Session *s, const char *properties);
int session_send_lock(Session *s, bool lock);
int session_send_lock_all(Manager *m, bool lock);
-const char* session_state_to_string(SessionState t);
-SessionState session_state_from_string(const char *s);
+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);
-SessionType session_type_from_string(const char *s);
+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);
-SessionClass session_class_from_string(const char *s);
+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);
-KillWho kill_who_from_string(const char *s);
+const char *kill_who_to_string(KillWho k) _const_;
+KillWho kill_who_from_string(const char *s) _pure_;
diff --git a/src/login/logind-user-dbus.c b/src/login/logind-user-dbus.c
index ddf9d9d5cf..3ec3ff8e61 100644
--- a/src/login/logind-user-dbus.c
+++ b/src/login/logind-user-dbus.c
@@ -65,7 +65,7 @@ static int bus_user_append_display(DBusMessageIter *i, const char *property, voi
DBusMessageIter sub;
User *u = data;
const char *id, *path;
- char *p = NULL;
+ _cleanup_free_ char *p = NULL;
assert(i);
assert(property);
@@ -86,12 +86,8 @@ static int bus_user_append_display(DBusMessageIter *i, const char *property, voi
}
if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &id) ||
- !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &path)) {
- free(p);
+ !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &path))
return -ENOMEM;
- }
-
- free(p);
if (!dbus_message_iter_close_container(i, &sub))
return -ENOMEM;
@@ -191,7 +187,7 @@ static int bus_user_append_idle_hint_since(DBusMessageIter *i, const char *prope
static int bus_user_append_default_cgroup(DBusMessageIter *i, const char *property, void *data) {
User *u = data;
- char *t;
+ _cleanup_free_ char *t = NULL;
int r;
bool success;
@@ -204,8 +200,6 @@ static int bus_user_append_default_cgroup(DBusMessageIter *i, const char *proper
return r;
success = dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &t);
- free(t);
-
return success ? 0 : -ENOMEM;
}
@@ -257,7 +251,7 @@ static DBusHandlerResult user_message_dispatch(
DBusMessage *message) {
DBusError error;
- DBusMessage *reply = NULL;
+ _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
int r;
assert(u);
@@ -304,18 +298,13 @@ static DBusHandlerResult user_message_dispatch(
}
if (reply) {
- if (!dbus_connection_send(connection, reply, NULL))
+ if (!bus_maybe_send_reply(connection, message, reply))
goto oom;
-
- dbus_message_unref(reply);
}
return DBUS_HANDLER_RESULT_HANDLED;
oom:
- if (reply)
- dbus_message_unref(reply);
-
dbus_error_free(&error);
return DBUS_HANDLER_RESULT_NEED_MEMORY;
@@ -366,9 +355,8 @@ char *user_bus_path(User *u) {
}
int user_send_signal(User *u, bool new_user) {
- DBusMessage *m;
- int r = -ENOMEM;
- char *p = NULL;
+ _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
+ _cleanup_free_ char *p = NULL;
uint32_t uid;
assert(u);
@@ -382,7 +370,7 @@ int user_send_signal(User *u, bool new_user) {
p = user_bus_path(u);
if (!p)
- goto finish;
+ return -ENOMEM;
uid = u->uid;
@@ -391,24 +379,17 @@ int user_send_signal(User *u, bool new_user) {
DBUS_TYPE_UINT32, &uid,
DBUS_TYPE_OBJECT_PATH, &p,
DBUS_TYPE_INVALID))
- goto finish;
+ return -ENOMEM;
if (!dbus_connection_send(u->manager->bus, m, NULL))
- goto finish;
-
- r = 0;
-
-finish:
- dbus_message_unref(m);
- free(p);
+ return -ENOMEM;
- return r;
+ return 0;
}
int user_send_changed(User *u, const char *properties) {
- DBusMessage *m;
- int r = -ENOMEM;
- char *p = NULL;
+ _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
+ _cleanup_free_ char *p = NULL;
assert(u);
@@ -421,17 +402,10 @@ int user_send_changed(User *u, const char *properties) {
m = bus_properties_changed_new(p, "org.freedesktop.login1.User", properties);
if (!m)
- goto finish;
+ return -ENOMEM;
if (!dbus_connection_send(u->manager->bus, m, NULL))
- goto finish;
-
- r = 0;
-
-finish:
- if (m)
- dbus_message_unref(m);
- free(p);
+ return -ENOMEM;
- return r;
+ return 0;
}
diff --git a/src/login/logind-user.c b/src/login/logind-user.c
index b692b533e2..9e2cbf646b 100644
--- a/src/login/logind-user.c
+++ b/src/login/logind-user.c
@@ -29,6 +29,7 @@
#include "cgroup-util.h"
#include "hashmap.h"
#include "strv.h"
+#include "fileio.h"
User* user_new(Manager *m, uid_t uid, gid_t gid, const char *name) {
User *u;
@@ -314,12 +315,22 @@ static int user_create_cgroup(User *u) {
assert(u);
if (!u->cgroup_path) {
- if (asprintf(&p, "%s/%s", u->manager->cgroup_path, u->name) < 0)
+ _cleanup_free_ char *name = NULL, *escaped = NULL;
+
+ if (asprintf(&name, "%lu.user", (unsigned long) u->uid) < 0)
+ return log_oom();
+
+ escaped = cg_escape(name);
+ if (!escaped)
+ return log_oom();
+
+ p = strjoin(u->manager->cgroup_path, "/", escaped, NULL);
+ if (!p)
return log_oom();
} else
p = u->cgroup_path;
- r = cg_create(SYSTEMD_CGROUP_CONTROLLER, p);
+ r = cg_create(SYSTEMD_CGROUP_CONTROLLER, p, NULL);
if (r < 0) {
log_error("Failed to create cgroup "SYSTEMD_CGROUP_CONTROLLER":%s: %s", p, strerror(-r));
free(p);
@@ -334,7 +345,7 @@ static int user_create_cgroup(User *u) {
if (strv_contains(u->manager->reset_controllers, *k))
continue;
- r = cg_create(*k, p);
+ r = cg_create(*k, p, NULL);
if (r < 0)
log_warning("Failed to create cgroup %s:%s: %s", *k, p, strerror(-r));
}
diff --git a/src/login/logind-user.h b/src/login/logind-user.h
index a679d43a30..080354da74 100644
--- a/src/login/logind-user.h
+++ b/src/login/logind-user.h
@@ -80,5 +80,5 @@ extern const DBusObjectPathVTable bus_user_vtable;
int user_send_signal(User *u, bool new_user);
int user_send_changed(User *u, const char *properties);
-const char* user_state_to_string(UserState s);
-UserState user_state_from_string(const char *s);
+const char* user_state_to_string(UserState s) _const_;
+UserState user_state_from_string(const char *s) _pure_;
diff --git a/src/login/logind.c b/src/login/logind.c
index 6776229ee7..5a394401dc 100644
--- a/src/login/logind.c
+++ b/src/login/logind.c
@@ -37,6 +37,7 @@
#include "dbus-loop.h"
#include "strv.h"
#include "conf-parser.h"
+#include "mkdir.h"
Manager *manager_new(void) {
Manager *m;
@@ -187,6 +188,8 @@ void manager_free(Manager *m) {
strv_free(m->kill_only_users);
strv_free(m->kill_exclude_users);
+ free(m->action_job);
+
free(m->cgroup_path);
free(m);
}
@@ -459,7 +462,7 @@ int manager_enumerate_devices(Manager *m) {
goto finish;
}
- r = udev_enumerate_add_match_tag(e, "seat-master");
+ r = udev_enumerate_add_match_tag(e, "master-of-seat");
if (r < 0)
goto finish;
@@ -594,9 +597,9 @@ int manager_enumerate_seats(Manager *m) {
}
static int manager_enumerate_users_from_cgroup(Manager *m) {
+ _cleanup_closedir_ DIR *d = NULL;
int r = 0, k;
char *name;
- DIR *d;
r = cg_enumerate_subgroups(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_path, &d);
if (r < 0) {
@@ -609,31 +612,37 @@ static int manager_enumerate_users_from_cgroup(Manager *m) {
while ((k = cg_read_subgroup(d, &name)) > 0) {
User *user;
+ char *e;
- k = manager_add_user_by_name(m, name, &user);
- if (k < 0) {
- free(name);
- r = k;
- continue;
- }
-
- user_add_to_gc_queue(user);
+ e = endswith(name, ".user");
+ if (e) {
+ *e = 0;
- if (!user->cgroup_path)
- if (asprintf(&user->cgroup_path, "%s/%s", m->cgroup_path, name) < 0) {
- r = -ENOMEM;
+ k = manager_add_user_by_name(m, name, &user);
+ if (k < 0) {
free(name);
- break;
+ r = k;
+ continue;
}
+ user_add_to_gc_queue(user);
+
+ if (!user->cgroup_path) {
+ user->cgroup_path = strjoin(m->cgroup_path, "/", name, NULL);
+ if (!user->cgroup_path) {
+ k = log_oom();
+ free(name);
+ break;
+ }
+ }
+ }
+
free(name);
}
- if (r >= 0 && k < 0)
+ if (k < 0)
r = k;
- closedir(d);
-
return r;
}
@@ -729,7 +738,7 @@ static int manager_enumerate_sessions_from_cgroup(Manager *m) {
int r = 0;
HASHMAP_FOREACH(u, m->users, i) {
- DIR *d;
+ _cleanup_closedir_ DIR *d = NULL;
char *name;
int k;
@@ -748,30 +757,34 @@ static int manager_enumerate_sessions_from_cgroup(Manager *m) {
while ((k = cg_read_subgroup(d, &name)) > 0) {
Session *session;
+ char *e;
- if (streq(name, "shared"))
- continue;
+ e = endswith(name, ".session");
+ if (e) {
+ *e = 0;
- k = manager_add_session(m, u, name, &session);
- if (k < 0) {
- free(name);
- break;
- }
+ k = manager_add_session(m, u, name, &session);
+ if (k < 0) {
+ free(name);
+ r = k;
+ continue;
+ }
- session_add_to_gc_queue(session);
+ session_add_to_gc_queue(session);
- if (!session->cgroup_path)
- if (asprintf(&session->cgroup_path, "%s/%s", u->cgroup_path, name) < 0) {
- k = -ENOMEM;
- free(name);
- break;
+ if (!session->cgroup_path) {
+ session->cgroup_path = strjoin(m->cgroup_path, "/", name, NULL);
+ if (!session->cgroup_path) {
+ k = log_oom();
+ free(name);
+ break;
+ }
}
+ }
free(name);
}
- closedir(d);
-
if (k < 0)
r = k;
}
@@ -1038,16 +1051,13 @@ int manager_get_session_by_cgroup(Manager *m, const char *cgroup, Session **sess
return 1;
}
- p = strdup(cgroup);
- if (!p)
- return log_oom();
+ p = strdupa(cgroup);
for (;;) {
char *e;
e = strrchr(p, '/');
if (!e || e == p) {
- free(p);
*session = NULL;
return 0;
}
@@ -1056,7 +1066,6 @@ int manager_get_session_by_cgroup(Manager *m, const char *cgroup, Session **sess
s = hashmap_get(m->session_cgroups, p);
if (s) {
- free(p);
*session = s;
return 1;
}
@@ -1077,7 +1086,7 @@ int manager_get_user_by_cgroup(Manager *m, const char *cgroup, User **user) {
return 1;
}
- p = strdup(cgroup);
+ p = strdupa(cgroup);
if (!p)
return log_oom();
@@ -1086,7 +1095,6 @@ int manager_get_user_by_cgroup(Manager *m, const char *cgroup, User **user) {
e = strrchr(p, '/');
if (!e || e == p) {
- free(p);
*user = NULL;
return 0;
}
@@ -1095,7 +1103,6 @@ int manager_get_user_by_cgroup(Manager *m, const char *cgroup, User **user) {
u = hashmap_get(m->user_cgroups, p);
if (u) {
- free(p);
*user = u;
return 1;
}
@@ -1103,21 +1110,18 @@ int manager_get_user_by_cgroup(Manager *m, const char *cgroup, User **user) {
}
int manager_get_session_by_pid(Manager *m, pid_t pid, Session **session) {
- char *p;
+ _cleanup_free_ char *p = NULL;
int r;
assert(m);
assert(pid >= 1);
assert(session);
- r = cg_get_by_pid(SYSTEMD_CGROUP_CONTROLLER, pid, &p);
+ r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, pid, &p);
if (r < 0)
return r;
- r = manager_get_session_by_cgroup(m, p, session);
- free(p);
-
- return r;
+ return manager_get_session_by_cgroup(m, p, session);
}
void manager_cgroup_notify_empty(Manager *m, const char *cgroup) {
@@ -1171,7 +1175,10 @@ static void manager_dispatch_other(Manager *m, int fd) {
static int manager_connect_bus(Manager *m) {
DBusError error;
int r;
- struct epoll_event ev;
+ struct epoll_event ev = {
+ .events = EPOLLIN,
+ .data.u32 = FD_BUS,
+ };
assert(m);
assert(!m->bus);
@@ -1227,10 +1234,6 @@ static int manager_connect_bus(Manager *m) {
goto fail;
}
- zero(ev);
- ev.events = EPOLLIN;
- ev.data.u32 = FD_BUS;
-
if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->bus_fd, &ev) < 0)
goto fail;
@@ -1243,7 +1246,10 @@ fail:
}
static int manager_connect_console(Manager *m) {
- struct epoll_event ev;
+ struct epoll_event ev = {
+ .events = 0,
+ .data.u32 = FD_CONSOLE,
+ };
assert(m);
assert(m->console_active_fd < 0);
@@ -1268,10 +1274,6 @@ static int manager_connect_console(Manager *m) {
return -errno;
}
- zero(ev);
- ev.events = 0;
- ev.data.u32 = FD_CONSOLE;
-
if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->console_active_fd, &ev) < 0)
return -errno;
@@ -1279,8 +1281,11 @@ static int manager_connect_console(Manager *m) {
}
static int manager_connect_udev(Manager *m) {
- struct epoll_event ev;
int r;
+ struct epoll_event ev = {
+ .events = EPOLLIN,
+ .data.u32 = FD_SEAT_UDEV,
+ };
assert(m);
assert(!m->udev_seat_monitor);
@@ -1291,7 +1296,7 @@ static int manager_connect_udev(Manager *m) {
if (!m->udev_seat_monitor)
return -ENOMEM;
- r = udev_monitor_filter_add_match_tag(m->udev_seat_monitor, "seat-master");
+ r = udev_monitor_filter_add_match_tag(m->udev_seat_monitor, "master-of-seat");
if (r < 0)
return r;
@@ -1301,9 +1306,6 @@ static int manager_connect_udev(Manager *m) {
m->udev_seat_fd = udev_monitor_get_fd(m->udev_seat_monitor);
- zero(ev);
- ev.events = EPOLLIN;
- ev.data.u32 = FD_SEAT_UDEV;
if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->udev_seat_fd, &ev) < 0)
return -errno;
@@ -1444,7 +1446,7 @@ int manager_get_idle_hint(Manager *m, dual_timestamp *t) {
int manager_dispatch_idle_action(Manager *m) {
struct dual_timestamp since;
- struct itimerspec its;
+ struct itimerspec its = {};
int r;
usec_t n;
@@ -1456,12 +1458,11 @@ int manager_dispatch_idle_action(Manager *m) {
goto finish;
}
- zero(its);
n = now(CLOCK_MONOTONIC);
r = manager_get_idle_hint(m, &since);
if (r <= 0)
- /* Not idle. Let's check if after a timeout it it might be idle then. */
+ /* Not idle. Let's check if after a timeout it might be idle then. */
timespec_store(&its.it_value, n + m->idle_action_usec);
else {
/* Idle! Let's see if it's time to do something, or if
@@ -1479,7 +1480,10 @@ int manager_dispatch_idle_action(Manager *m) {
}
if (m->idle_action_fd < 0) {
- struct epoll_event ev;
+ struct epoll_event ev = {
+ .events = EPOLLIN,
+ .data.u32 = FD_IDLE_ACTION,
+ };
m->idle_action_fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK|TFD_CLOEXEC);
if (m->idle_action_fd < 0) {
@@ -1488,10 +1492,6 @@ int manager_dispatch_idle_action(Manager *m) {
goto finish;
}
- zero(ev);
- ev.events = EPOLLIN;
- ev.data.u32 = FD_IDLE_ACTION;
-
if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->idle_action_fd, &ev) < 0) {
log_error("Failed to add idle action timer to epoll: %m");
r = -errno;
@@ -1626,11 +1626,11 @@ int manager_run(Manager *m) {
manager_gc(m, true);
- if (m->delayed_unit) {
+ if (m->action_what != 0 && !m->action_job) {
usec_t x, y;
x = now(CLOCK_MONOTONIC);
- y = m->delayed_timestamp + m->inhibit_delay_max;
+ y = m->action_timestamp + m->inhibit_delay_max;
msec = x >= y ? 0 : (int) ((y - x) / USEC_PER_MSEC);
}
@@ -1683,13 +1683,12 @@ int manager_run(Manager *m) {
}
static int manager_parse_config_file(Manager *m) {
- FILE *f;
- const char *fn;
+ static const char fn[] = "/etc/systemd/logind.conf";
+ _cleanup_fclose_ FILE *f = NULL;
int r;
assert(m);
- fn = "/etc/systemd/logind.conf";
f = fopen(fn, "re");
if (!f) {
if (errno == ENOENT)
@@ -1699,12 +1698,11 @@ static int manager_parse_config_file(Manager *m) {
return -errno;
}
- r = config_parse(fn, f, "Login\0", config_item_perf_lookup, (void*) logind_gperf_lookup, false, m);
+ r = config_parse(NULL, fn, f, "Login\0", config_item_perf_lookup,
+ (void*) logind_gperf_lookup, false, false, m);
if (r < 0)
log_warning("Failed to parse configuration file: %s", strerror(-r));
- fclose(f);
-
return r;
}
@@ -1725,6 +1723,15 @@ int main(int argc, char *argv[]) {
goto finish;
}
+ /* Always create the directories people can create inotify
+ * watches in. Note that some applications might check for the
+ * existence of /run/systemd/seats/ to determine whether
+ * logind is available, so please always make sure this check
+ * stays in. */
+ mkdir_label("/run/systemd/seats", 0755);
+ mkdir_label("/run/systemd/users", 0755);
+ mkdir_label("/run/systemd/sessions", 0755);
+
m = manager_new();
if (!m) {
r = log_oom();
diff --git a/src/login/logind.h b/src/login/logind.h
index 816635dcfc..904dc20467 100644
--- a/src/login/logind.h
+++ b/src/login/logind.h
@@ -91,16 +91,23 @@ struct Manager {
Hashmap *inhibitor_fds;
Hashmap *button_fds;
- /* If a shutdown was delayed due to a inhibitor this contains
- the unit name we are supposed to start after the delay is
- over */
- const char *delayed_unit;
- InhibitWhat delayed_what;
- usec_t delayed_timestamp;
-
usec_t inhibit_delay_max;
- int idle_action_fd;
+ /* 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;
+ usec_t action_timestamp;
+
+ int idle_action_fd; /* the timer_fd */
usec_t idle_action_usec;
usec_t idle_action_not_before_usec;
HandleAction idle_action;
diff --git a/src/login/org.freedesktop.login1.policy.in b/src/login/org.freedesktop.login1.policy.in
index b5f5db4f6a..0c551d4f9b 100644
--- a/src/login/org.freedesktop.login1.policy.in
+++ b/src/login/org.freedesktop.login1.policy.in
@@ -158,7 +158,7 @@
<defaults>
<allow_any>auth_admin_keep</allow_any>
<allow_inactive>auth_admin_keep</allow_inactive>
- <allow_active>auth_admin_keep</allow_active>
+ <allow_active>yes</allow_active>
</defaults>
<annotate key="org.freedesktop.policykit.imply">org.freedesktop.login1.power-off</annotate>
</action>
diff --git a/src/login/pam-module.c b/src/login/pam-module.c
index 88b0ef9e45..13290fd8ea 100644
--- a/src/login/pam-module.c
+++ b/src/login/pam-module.c
@@ -32,8 +32,6 @@
#include <security/pam_ext.h>
#include <security/pam_misc.h>
-#include <systemd/sd-daemon.h>
-
#include "util.h"
#include "audit.h"
#include "macro.h"
@@ -41,6 +39,7 @@
#include "dbus-common.h"
#include "def.h"
#include "socket-util.h"
+#include "fileio.h"
static int parse_argv(pam_handle_t *handle,
int argc, const char **argv,
@@ -257,13 +256,15 @@ static bool check_user_lists(
}
static int get_seat_from_display(const char *display, const char **seat, uint32_t *vtnr) {
- char *p = NULL;
+ _cleanup_free_ char *p = NULL;
int r;
- int fd;
- union sockaddr_union sa;
+ _cleanup_close_ int fd = -1;
+ union sockaddr_union sa = {
+ .un.sun_family = AF_UNIX,
+ };
struct ucred ucred;
socklen_t l;
- char *tty;
+ _cleanup_free_ char *tty = NULL;
int v;
assert(display);
@@ -278,27 +279,17 @@ static int get_seat_from_display(const char *display, const char **seat, uint32_
r = socket_from_display(display, &p);
if (r < 0)
return r;
+ strncpy(sa.un.sun_path, p, sizeof(sa.un.sun_path)-1);
fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0);
- if (fd < 0) {
- free(p);
+ if (fd < 0)
return -errno;
- }
- zero(sa);
- sa.un.sun_family = AF_UNIX;
- strncpy(sa.un.sun_path, p, sizeof(sa.un.sun_path)-1);
- free(p);
-
- if (connect(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path)) < 0) {
- close_nointr_nofail(fd);
+ if (connect(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path)) < 0)
return -errno;
- }
l = sizeof(ucred);
r = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &l);
- close_nointr_nofail(fd);
-
if (r < 0)
return -errno;
@@ -307,8 +298,6 @@ static int get_seat_from_display(const char *display, const char **seat, uint32_
return r;
v = vtnr_from_tty(tty);
- free(tty);
-
if (v < 0)
return v;
else if (v == 0)
@@ -347,8 +336,8 @@ _public_ PAM_EXTERN int pam_sm_open_session(
/* pam_syslog(handle, LOG_INFO, "pam-systemd initializing"); */
- /* Make this a NOP on non-systemd systems */
- if (sd_booted() <= 0)
+ /* Make this a NOP on non-logind systems */
+ if (!logind_running())
return PAM_SUCCESS;
if (parse_argv(handle,
@@ -451,9 +440,10 @@ _public_ PAM_EXTERN int pam_sm_open_session(
seat = strempty(seat);
if (strchr(tty, ':')) {
- /* A tty with a colon is usually an X11 display, place
- * there to show up in utmp. We rearrange things and
- * don't pretend that an X display was a tty */
+ /* A tty with a colon is usually an X11 display,
+ * placed there to show up in utmp. We rearrange
+ * things and don't pretend that an X display was a
+ * tty. */
if (isempty(display))
display = tty;
@@ -493,7 +483,7 @@ _public_ PAM_EXTERN int pam_sm_open_session(
if (isempty(class))
class = class_pam;
if (isempty(class))
- class = "user";
+ class = streq(type, "unspecified") ? "background" : "user";
remote = !isempty(remote_host) &&
!streq(remote_host, "localhost") &&
diff --git a/src/login/sd-login.c b/src/login/sd-login.c
index 45e3bb8dcd..d0dc42f685 100644
--- a/src/login/sd-login.c
+++ b/src/login/sd-login.c
@@ -23,114 +23,64 @@
#include <string.h>
#include <errno.h>
#include <sys/inotify.h>
+#include <sys/poll.h>
#include "util.h"
#include "cgroup-util.h"
#include "macro.h"
#include "sd-login.h"
#include "strv.h"
+#include "fileio.h"
_public_ int sd_pid_get_session(pid_t pid, char **session) {
- int r;
- char *cgroup, *p;
-
if (pid < 0)
return -EINVAL;
if (!session)
return -EINVAL;
- r = cg_pid_get_cgroup(pid, NULL, &cgroup);
- if (r < 0)
- return r;
-
- if (!startswith(cgroup, "/user/")) {
- free(cgroup);
- return -ENOENT;
- }
-
- p = strchr(cgroup + 6, '/');
- if (!p) {
- free(cgroup);
- return -ENOENT;
- }
-
- p++;
- if (startswith(p, "shared/") || streq(p, "shared")) {
- free(cgroup);
- return -ENOENT;
- }
-
- p = strndup(p, strcspn(p, "/"));
- free(cgroup);
-
- if (!p)
- return -ENOMEM;
-
- *session = p;
- return 0;
+ return cg_pid_get_session(pid, session);
}
_public_ int sd_pid_get_unit(pid_t pid, char **unit) {
if (pid < 0)
return -EINVAL;
-
if (!unit)
return -EINVAL;
return cg_pid_get_unit(pid, unit);
}
-_public_ int sd_pid_get_owner_uid(pid_t pid, uid_t *uid) {
- int r;
- char *root, *cgroup, *p, *cc;
- struct stat st;
+_public_ int sd_pid_get_user_unit(pid_t pid, char **unit) {
if (pid < 0)
return -EINVAL;
-
- if (!uid)
+ if (!unit)
return -EINVAL;
- r = cg_pid_get_cgroup(pid, &root, &cgroup);
- if (r < 0)
- return r;
-
- if (!startswith(cgroup, "/user/")) {
- free(cgroup);
- free(root);
- return -ENOENT;
- }
-
- p = strchr(cgroup + 6, '/');
- if (!p) {
- free(cgroup);
- return -ENOENT;
- }
+ return cg_pid_get_user_unit(pid, unit);
+}
- p++;
- p += strcspn(p, "/");
- *p = 0;
+_public_ int sd_pid_get_machine_name(pid_t pid, char **name) {
- r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, root, cgroup, &cc);
- free(root);
- free(cgroup);
+ if (pid < 0)
+ return -EINVAL;
+ if (!name)
+ return -EINVAL;
- if (r < 0)
- return -ENOMEM;
+ return cg_pid_get_machine_name(pid, name);
+}
- r = lstat(cc, &st);
- free(cc);
+_public_ int sd_pid_get_owner_uid(pid_t pid, uid_t *uid) {
- if (r < 0)
- return -errno;
+ if (pid < 0)
+ return -EINVAL;
- if (!S_ISDIR(st.st_mode))
- return -ENOTDIR;
+ if (!uid)
+ return -EINVAL;
- *uid = st.st_uid;
- return 0;
+ return cg_pid_get_owner_uid(pid, uid);
}
_public_ int sd_uid_get_state(uid_t uid, char**state) {
@@ -165,7 +115,8 @@ _public_ int sd_uid_get_state(uid_t uid, char**state) {
}
_public_ int sd_uid_is_on_seat(uid_t uid, int require_active, const char *seat) {
- char *p, *w, *t, *state, *s = NULL;
+ char *w, *state;
+ _cleanup_free_ char *t = NULL, *s = NULL, *p = NULL;
size_t l;
int r;
const char *variable;
@@ -180,38 +131,26 @@ _public_ int sd_uid_is_on_seat(uid_t uid, int require_active, const char *seat)
return -ENOMEM;
r = parse_env_file(p, NEWLINE, variable, &s, NULL);
- free(p);
- if (r < 0) {
- free(s);
+ if (r < 0)
return r;
- }
if (!s)
return -EIO;
- if (asprintf(&t, "%lu", (unsigned long) uid) < 0) {
- free(s);
+ if (asprintf(&t, "%lu", (unsigned long) uid) < 0)
return -ENOMEM;
- }
FOREACH_WORD(w, l, s, state) {
- if (strncmp(t, w, l) == 0) {
- free(s);
- free(t);
-
+ if (strneq(t, w, l))
return 1;
- }
}
- free(s);
- free(t);
-
return 0;
}
static int uid_get_array(uid_t uid, const char *variable, char ***array) {
- char *p, *s = NULL;
+ _cleanup_free_ char *p = NULL, *s = NULL;
char **a;
int r;
@@ -221,11 +160,7 @@ static int uid_get_array(uid_t uid, const char *variable, char ***array) {
r = parse_env_file(p, NEWLINE,
variable, &s,
NULL);
- free(p);
-
if (r < 0) {
- free(s);
-
if (r == -ENOENT) {
if (array)
*array = NULL;
@@ -242,7 +177,6 @@ static int uid_get_array(uid_t uid, const char *variable, char ***array) {
}
a = strv_split(s, " ");
- free(s);
if (!a)
return -ENOMEM;
@@ -304,31 +238,27 @@ static int file_of_session(const char *session, char **_p) {
_public_ int sd_session_is_active(const char *session) {
int r;
- char *p, *s = NULL;
+ _cleanup_free_ char *p = NULL, *s = NULL;
r = file_of_session(session, &p);
if (r < 0)
return r;
r = parse_env_file(p, NEWLINE, "ACTIVE", &s, NULL);
- free(p);
- if (r < 0) {
- free(s);
+ if (r < 0)
return r;
- }
if (!s)
return -EIO;
r = parse_boolean(s);
- free(s);
return r;
}
_public_ int sd_session_get_state(const char *session, char **state) {
- char *p, *s = NULL;
+ _cleanup_free_ char *p = NULL, *s = NULL;
int r;
if (!state)
@@ -339,21 +269,21 @@ _public_ int sd_session_get_state(const char *session, char **state) {
return r;
r = parse_env_file(p, NEWLINE, "STATE", &s, NULL);
- free(p);
- if (r < 0) {
- free(s);
+ if (r < 0)
return r;
- } else if (!s)
+ else if (!s)
return -EIO;
*state = s;
+ s = NULL;
+
return 0;
}
_public_ int sd_session_get_uid(const char *session, uid_t *uid) {
int r;
- char *p, *s = NULL;
+ _cleanup_free_ char *p = NULL, *s = NULL;
if (!uid)
return -EINVAL;
@@ -363,24 +293,20 @@ _public_ int sd_session_get_uid(const char *session, uid_t *uid) {
return r;
r = parse_env_file(p, NEWLINE, "UID", &s, NULL);
- free(p);
- if (r < 0) {
- free(s);
+ if (r < 0)
return r;
- }
if (!s)
return -EIO;
r = parse_uid(s, uid);
- free(s);
return r;
}
static int session_get_string(const char *session, const char *field, char **value) {
- char *p, *s = NULL;
+ _cleanup_free_ char *p = NULL, *s = NULL;
int r;
if (!value)
@@ -391,17 +317,15 @@ static int session_get_string(const char *session, const char *field, char **val
return r;
r = parse_env_file(p, NEWLINE, field, &s, NULL);
- free(p);
- if (r < 0) {
- free(s);
+ if (r < 0)
return r;
- }
if (isempty(s))
return -ENOENT;
*value = s;
+ s = NULL;
return 0;
}
@@ -409,6 +333,10 @@ _public_ int sd_session_get_seat(const char *session, char **seat) {
return session_get_string(session, "SEAT", seat);
}
+_public_ int sd_session_get_tty(const char *session, char **tty) {
+ return session_get_string(session, "TTY", tty);
+}
+
_public_ int sd_session_get_service(const char *session, char **service) {
return session_get_string(session, "SERVICE", service);
}
@@ -434,25 +362,25 @@ static int file_of_seat(const char *seat, char **_p) {
if (seat)
p = strappend("/run/systemd/seats/", seat);
else {
- char *buf;
+ _cleanup_free_ char *buf = NULL;
r = sd_session_get_seat(NULL, &buf);
if (r < 0)
return r;
p = strappend("/run/systemd/seats/", buf);
- free(buf);
}
if (!p)
return -ENOMEM;
*_p = p;
+ p = NULL;
return 0;
}
_public_ int sd_seat_get_active(const char *seat, char **session, uid_t *uid) {
- char *p, *s = NULL, *t = NULL;
+ _cleanup_free_ char *p = NULL, *s = NULL, *t = NULL;
int r;
if (!session && !uid)
@@ -466,46 +394,33 @@ _public_ int sd_seat_get_active(const char *seat, char **session, uid_t *uid) {
"ACTIVE", &s,
"ACTIVE_UID", &t,
NULL);
- free(p);
-
- if (r < 0) {
- free(s);
- free(t);
+ if (r < 0)
return r;
- }
- if (session && !s) {
- free(t);
+ if (session && !s)
return -ENOENT;
- }
- if (uid && !t) {
- free(s);
+ if (uid && !t)
return -ENOENT;
- }
if (uid && t) {
r = parse_uid(t, uid);
- if (r < 0) {
- free(t);
- free(s);
+ if (r < 0)
return r;
- }
}
- free(t);
-
- if (session && s)
+ if (session && s) {
*session = s;
- else
- free(s);
+ s = NULL;
+ }
return 0;
}
_public_ int sd_seat_get_sessions(const char *seat, char ***sessions, uid_t **uids, unsigned *n_uids) {
- char *p, *s = NULL, *t = NULL, **a = NULL;
- uid_t *b = NULL;
+ _cleanup_free_ char *p = NULL, *s = NULL, *t = NULL;
+ _cleanup_strv_free_ char **a = NULL;
+ _cleanup_free_ uid_t *b = NULL;
unsigned n = 0;
int r;
@@ -517,25 +432,16 @@ _public_ int sd_seat_get_sessions(const char *seat, char ***sessions, uid_t **ui
"SESSIONS", &s,
"ACTIVE_SESSIONS", &t,
NULL);
- free(p);
- if (r < 0) {
- free(s);
- free(t);
+ if (r < 0)
return r;
- }
if (s) {
a = strv_split(s, " ");
- if (!a) {
- free(s);
- free(t);
+ if (!a)
return -ENOMEM;
- }
}
- free(s);
-
if (uids && t) {
char *w, *state;
size_t l;
@@ -543,30 +449,22 @@ _public_ int sd_seat_get_sessions(const char *seat, char ***sessions, uid_t **ui
FOREACH_WORD(w, l, t, state)
n++;
- if (n == 0)
- b = NULL;
- else {
+ if (n > 0) {
unsigned i = 0;
b = new(uid_t, n);
- if (!b) {
- strv_free(a);
+ if (!b)
return -ENOMEM;
- }
FOREACH_WORD(w, l, t, state) {
- char *k;
+ _cleanup_free_ char *k = NULL;
k = strndup(w, l);
- if (!k) {
- free(t);
- free(b);
- strv_free(a);
+ if (!k)
return -ENOMEM;
- }
r = parse_uid(k, b + i);
- free(k);
+
if (r < 0)
continue;
@@ -575,17 +473,17 @@ _public_ int sd_seat_get_sessions(const char *seat, char ***sessions, uid_t **ui
}
}
- free(t);
-
r = strv_length(a);
- if (sessions)
+ if (sessions) {
*sessions = a;
- else
- strv_free(a);
+ a = NULL;
+ }
- if (uids)
+ if (uids) {
*uids = b;
+ b = NULL;
+ }
if (n_uids)
*n_uids = n;
@@ -594,7 +492,7 @@ _public_ int sd_seat_get_sessions(const char *seat, char ***sessions, uid_t **ui
}
static int seat_get_can(const char *seat, const char *variable) {
- char *p, *s = NULL;
+ _cleanup_free_ char *p = NULL, *s = NULL;
int r;
r = file_of_seat(seat, &p);
@@ -604,17 +502,12 @@ static int seat_get_can(const char *seat, const char *variable) {
r = parse_env_file(p, NEWLINE,
variable, &s,
NULL);
- free(p);
-
- if (r < 0) {
- free(s);
+ if (r < 0)
return r;
- }
- if (s) {
+ if (s)
r = parse_boolean(s);
- free(s);
- } else
+ else
r = 0;
return r;
@@ -641,10 +534,10 @@ _public_ int sd_get_sessions(char ***sessions) {
}
_public_ int sd_get_uids(uid_t **users) {
- DIR *d;
+ _cleanup_closedir_ DIR *d;
int r = 0;
unsigned n = 0;
- uid_t *l = NULL;
+ _cleanup_free_ uid_t *l = NULL;
d = opendir("/run/systemd/users/");
if (!d)
@@ -657,10 +550,8 @@ _public_ int sd_get_uids(uid_t **users) {
uid_t uid;
k = readdir_r(d, &buf.de, &de);
- if (k != 0) {
- r = -k;
- goto finish;
- }
+ if (k != 0)
+ return -k;
if (!de)
break;
@@ -680,10 +571,8 @@ _public_ int sd_get_uids(uid_t **users) {
n = MAX(16, 2*r);
t = realloc(l, sizeof(uid_t) * n);
- if (!t) {
- r = -ENOMEM;
- goto finish;
- }
+ if (!t)
+ return -ENOMEM;
l = t;
}
@@ -694,19 +583,51 @@ _public_ int sd_get_uids(uid_t **users) {
r++;
}
-finish:
- if (d)
- closedir(d);
-
- if (r >= 0) {
- if (users)
- *users = l;
- } else
- free(l);
+ if (users) {
+ *users = l;
+ l = NULL;
+ }
return r;
}
+_public_ int sd_get_machine_names(char ***machines) {
+ _cleanup_closedir_ DIR *d = NULL;
+ _cleanup_strv_free_ char **l = NULL;
+ _cleanup_free_ char *md = NULL;
+ char *n;
+ int c = 0, r;
+
+ r = cg_get_machine_path(NULL, &md);
+ if (r < 0)
+ return r;
+
+ r = cg_enumerate_subgroups(SYSTEMD_CGROUP_CONTROLLER, md, &d);
+ if (r < 0)
+ return r;
+
+ while ((r = cg_read_subgroup(d, &n)) > 0) {
+
+ r = strv_push(&l, n);
+ if (r < 0) {
+ free(n);
+ return -ENOMEM;
+ }
+
+ c++;
+ }
+
+ if (r < 0)
+ return r;
+
+ if (machines) {
+ *machines = l;
+ l = NULL;
+ }
+
+ return c;
+}
+
static inline int MONITOR_TO_FD(sd_login_monitor *m) {
return (int) (unsigned long) m - 1;
}
@@ -724,7 +645,7 @@ _public_ int sd_login_monitor_new(const char *category, sd_login_monitor **m) {
fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
if (fd < 0)
- return errno;
+ return -errno;
if (!category || streq(category, "seat")) {
k = inotify_add_watch(fd, "/run/systemd/seats/", IN_MOVED_TO|IN_DELETE);
@@ -756,6 +677,27 @@ _public_ int sd_login_monitor_new(const char *category, sd_login_monitor **m) {
good = true;
}
+ if (!category || streq(category, "machine")) {
+ _cleanup_free_ char *md = NULL, *p = NULL;
+ int r;
+
+ r = cg_get_machine_path(NULL, &md);
+ if (r < 0)
+ return r;
+
+ r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, md, NULL, &p);
+ if (r < 0)
+ return r;
+
+ k = inotify_add_watch(fd, p, IN_MOVED_TO|IN_CREATE|IN_DELETE);
+ if (k < 0) {
+ close_nointr_nofail(fd);
+ return -errno;
+ }
+
+ good = true;
+ }
+
if (!good) {
close_nointr(fd);
return -EINVAL;
@@ -792,3 +734,29 @@ _public_ int sd_login_monitor_get_fd(sd_login_monitor *m) {
return MONITOR_TO_FD(m);
}
+
+_public_ int sd_login_monitor_get_events(sd_login_monitor *m) {
+
+ if (!m)
+ return -EINVAL;
+
+ /* For now we will only return POLLIN here, since we don't
+ * need anything else ever for inotify. However, let's have
+ * this API to keep our options open should we later on need
+ * it. */
+ return POLLIN;
+}
+
+_public_ int sd_login_monitor_get_timeout(sd_login_monitor *m, uint64_t *timeout_usec) {
+
+ if (!m)
+ return -EINVAL;
+ if (!timeout_usec)
+ return -EINVAL;
+
+ /* For now we will only return (uint64_t) -1, since we don't
+ * need any timeout. However, let's have this API to keep our
+ * options open should we later on need it. */
+ *timeout_usec = (uint64_t) -1;
+ return 0;
+}
diff --git a/src/login/sysfs-show.c b/src/login/sysfs-show.c
index d113ec3e43..3c03bd1f2e 100644
--- a/src/login/sysfs-show.c
+++ b/src/login/sysfs-show.c
@@ -45,6 +45,7 @@ static int show_sysfs_one(
struct udev_device *d;
const char *sn, *name, *sysfs, *subsystem, *sysname;
char *l, *k;
+ bool is_master;
sysfs = udev_list_entry_get_name(*item);
if (!path_startswith(sysfs, sub))
@@ -60,13 +61,15 @@ static int show_sysfs_one(
if (isempty(sn))
sn = "seat0";
- /* fixme, also check for tag 'seat' here */
+ /* Explicitly also check for tag 'seat' here */
if (!streq(seat, sn) || !udev_device_has_tag(d, "seat")) {
udev_device_unref(d);
*item = udev_list_entry_get_next(*item);
continue;
}
+ is_master = udev_device_has_tag(d, "master-of-seat");
+
name = udev_device_get_sysattr_value(d, "name");
if (!name)
name = udev_device_get_sysattr_value(d, "id");
@@ -110,7 +113,8 @@ static int show_sysfs_one(
free(k);
if (asprintf(&l,
- "(%s:%s)%s%s%s",
+ "%s%s:%s%s%s%s",
+ is_master ? "[MASTER] " : "",
subsystem, sysname,
name ? " \"" : "", name ? name : "", name ? "\"" : "") < 0) {
udev_device_unref(d);
diff --git a/src/login/test-login.c b/src/login/test-login.c
index 159ff3efc5..945cb38be9 100644
--- a/src/login/test-login.c
+++ b/src/login/test-login.c
@@ -35,7 +35,7 @@ int main(int argc, char* argv[]) {
char *state;
char *session2;
char *t;
- char **seats, **sessions;
+ char **seats, **sessions, **machines;
uid_t *uids;
unsigned n;
struct pollfd pollfd;
@@ -180,15 +180,34 @@ int main(int argc, char* argv[]) {
printf("n_uids = %i\n", r);
assert_se(sd_get_uids(NULL) == r);
- r = sd_login_monitor_new("session", &m);
+ r = sd_get_machine_names(&machines);
assert_se(r >= 0);
+ assert_se(r == (int) strv_length(machines));
+ assert_se(t = strv_join(machines, ", "));
+ strv_free(machines);
+ printf("n_machines = %i\n", r);
+ printf("machines = %s\n", t);
+ free(t);
- zero(pollfd);
- pollfd.fd = sd_login_monitor_get_fd(m);
- pollfd.events = POLLIN;
+ r = sd_login_monitor_new("session", &m);
+ assert_se(r >= 0);
for (n = 0; n < 5; n++) {
- r = poll(&pollfd, 1, -1);
+ usec_t timeout, nw;
+
+ zero(pollfd);
+ assert_se((pollfd.fd = sd_login_monitor_get_fd(m)) >= 0);
+ assert_se((pollfd.events = sd_login_monitor_get_events(m)) >= 0);
+
+ assert_se(sd_login_monitor_get_timeout(m, &timeout) >= 0);
+
+ nw = now(CLOCK_MONOTONIC);
+
+ r = poll(&pollfd, 1,
+ timeout == (uint64_t) -1 ? -1 :
+ timeout > nw ? (int) ((timeout - nw) / 1000) :
+ 0);
+
assert_se(r >= 0);
sd_login_monitor_flush(m);
diff --git a/src/login/uaccess.c b/src/login/uaccess.c
deleted file mode 100644
index 2c530c8f39..0000000000
--- a/src/login/uaccess.c
+++ /dev/null
@@ -1,91 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2011 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU 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 <http://www.gnu.org/licenses/>.
-***/
-
-#include <errno.h>
-#include <string.h>
-
-#include <systemd/sd-daemon.h>
-#include <systemd/sd-login.h>
-
-#include "logind-acl.h"
-#include "util.h"
-#include "log.h"
-
-int main(int argc, char *argv[]) {
- int r;
- const char *path = NULL, *seat;
- bool changed_acl = false;
- uid_t uid;
-
- log_set_target(LOG_TARGET_AUTO);
- log_parse_environment();
- log_open();
-
- umask(0022);
-
- if (argc < 2 || argc > 3) {
- log_error("This program expects one or two arguments.");
- r = -EINVAL;
- goto finish;
- }
-
- /* Make sure we don't muck around with ACLs the system is not
- * running systemd. */
- if (!sd_booted())
- return 0;
-
- path = argv[1];
- seat = argc < 3 || isempty(argv[2]) ? "seat0" : argv[2];
-
- r = sd_seat_get_active(seat, NULL, &uid);
- if (r == -ENOENT) {
- /* No active session on this seat */
- r = 0;
- goto finish;
- } else if (r < 0) {
- log_error("Failed to determine active user on seat %s.", seat);
- goto finish;
- }
-
- r = devnode_acl(path, true, false, 0, true, uid);
- if (r < 0) {
- log_error("Failed to apply ACL on %s: %s", path, strerror(-r));
- goto finish;
- }
-
- changed_acl = true;
- r = 0;
-
-finish:
- if (path && !changed_acl) {
- int k;
- /* Better be safe that sorry and reset ACL */
-
- k = devnode_acl(path, true, false, 0, false, 0);
- if (k < 0) {
- log_error("Failed to apply ACL on %s: %s", path, strerror(-k));
- if (r >= 0)
- r = k;
- }
- }
-
- return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
-}
diff --git a/src/login/user-sessions.c b/src/login/user-sessions.c
index 91531e8f38..41d32044e9 100644
--- a/src/login/user-sessions.c
+++ b/src/login/user-sessions.c
@@ -26,6 +26,7 @@
#include "log.h"
#include "util.h"
#include "cgroup-util.h"
+#include "fileio.h"
int main(int argc, char*argv[]) {
int ret = EXIT_FAILURE;
@@ -69,7 +70,7 @@ int main(int argc, char*argv[]) {
int r, q;
char *cgroup_user_tree = NULL;
- r = write_one_line_file_atomic("/run/nologin", "System is going down.");
+ r = write_string_file_atomic("/run/nologin", "System is going down.");
if (r < 0)
log_error("Failed to create /run/nologin: %s", strerror(-r));
diff --git a/src/modules-load/modules-load.c b/src/modules-load/modules-load.c
index f6279e1975..7b19ee02ef 100644
--- a/src/modules-load/modules-load.c
+++ b/src/modules-load/modules-load.c
@@ -26,6 +26,7 @@
#include <sys/stat.h>
#include <limits.h>
#include <dirent.h>
+#include <getopt.h>
#include <libkmod.h>
#include "log.h"
@@ -33,9 +34,20 @@
#include "strv.h"
#include "conf-files.h"
#include "virt.h"
+#include "fileio.h"
static char **arg_proc_cmdline_modules = NULL;
+static const char conf_file_dirs[] =
+ "/etc/modules-load.d\0"
+ "/run/modules-load.d\0"
+ "/usr/local/lib/modules-load.d\0"
+ "/usr/lib/modules-load.d\0"
+#ifdef HAVE_SPLIT_USR
+ "/lib/modules-load.d\0"
+#endif
+ ;
+
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat-nonliteral"
static void systemd_kmod_log(void *data, int priority, const char *file, int line,
@@ -46,14 +58,14 @@ static void systemd_kmod_log(void *data, int priority, const char *file, int lin
#pragma GCC diagnostic pop
static int add_modules(const char *p) {
- char **t, **k;
+ char **t;
+ _cleanup_strv_free_ char **k = NULL;
k = strv_split(p, ",");
if (!k)
return log_oom();
t = strv_merge(arg_proc_cmdline_modules, k);
- strv_free(k);
if (!t)
return log_oom();
@@ -64,7 +76,7 @@ static int add_modules(const char *p) {
}
static int parse_proc_cmdline(void) {
- char _cleanup_free_ *line = NULL;
+ _cleanup_free_ char *line = NULL;
char *w, *state;
int r;
size_t l;
@@ -79,7 +91,7 @@ static int parse_proc_cmdline(void) {
}
FOREACH_WORD_QUOTED(w, l, line, state) {
- char _cleanup_free_ *word;
+ _cleanup_free_ char *word;
word = strndup(w, l);
if (!word)
@@ -136,7 +148,7 @@ static int load_module(struct kmod_ctx *ctx, const char *m) {
break;
case KMOD_MODULE_LIVE:
- log_info("Module '%s' is already loaded", kmod_module_get_name(mod));
+ log_debug("Module '%s' is already loaded", kmod_module_get_name(mod));
break;
default:
@@ -162,15 +174,98 @@ static int load_module(struct kmod_ctx *ctx, const char *m) {
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", conf_file_dirs, &f);
+ if (r < 0) {
+ if (ignore_enoent && r == -ENOENT)
+ return 0;
+
+ log_error("Failed to open %s, ignoring: %s", path, strerror(-r));
+ return r;
+ }
+
+ log_debug("apply: %s\n", path);
+ for (;;) {
+ char line[LINE_MAX], *l;
+ int k;
+
+ if (!fgets(line, sizeof(line), f)) {
+ if (feof(f))
+ break;
+
+ log_error("Failed to read file '%s', ignoring: %m", path);
+ return -errno;
+ }
+
+ 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 int help(void) {
+
+ printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
+ "Loads statically configured kernel modules.\n\n"
+ " -h --help Show this help\n",
+ program_invocation_short_name);
+
+ return 0;
+}
+
+static int parse_argv(int argc, char *argv[]) {
+
+ static const struct option options[] = {
+ { "help", no_argument, NULL, 'h' },
+ { NULL, 0, NULL, 0 }
+ };
+
+ 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 '?':
+ return -EINVAL;
+
+ default:
+ log_error("Unknown option code %c", c);
+ return -EINVAL;
+ }
+ }
+
+ return 1;
+}
+
int main(int argc, char *argv[]) {
- int r = EXIT_FAILURE, k;
- char **files = NULL, **fn, **i;
+ int r, k;
struct kmod_ctx *ctx;
- if (argc > 1) {
- log_error("This program takes no argument.");
- return EXIT_FAILURE;
- }
+ r = parse_argv(argc, argv);
+ if (r <= 0)
+ return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
log_set_target(LOG_TARGET_AUTO);
log_parse_environment();
@@ -190,70 +285,43 @@ int main(int argc, char *argv[]) {
kmod_load_resources(ctx);
kmod_set_log_fn(ctx, systemd_kmod_log, NULL);
- r = EXIT_SUCCESS;
+ r = 0;
- STRV_FOREACH(i, arg_proc_cmdline_modules) {
- k = load_module(ctx, *i);
- if (k < 0)
- r = EXIT_FAILURE;
- }
-
- k = conf_files_list(&files, ".conf",
- "/etc/modules-load.d",
- "/run/modules-load.d",
- "/usr/local/lib/modules-load.d",
- "/usr/lib/modules-load.d",
-#ifdef HAVE_SPLIT_USR
- "/lib/modules-load.d",
-#endif
- NULL);
- if (k < 0) {
- log_error("Failed to enumerate modules-load.d files: %s", strerror(-k));
- r = EXIT_FAILURE;
- goto finish;
- }
-
- STRV_FOREACH(fn, files) {
- FILE *f;
+ if (argc > optind) {
+ int i;
- f = fopen(*fn, "re");
- if (!f) {
- if (errno == ENOENT)
- continue;
-
- log_error("Failed to open %s: %m", *fn);
- r = EXIT_FAILURE;
- continue;
+ for (i = optind; i < argc; i++) {
+ k = apply_file(ctx, argv[i], false);
+ if (k < 0 && r == 0)
+ r = k;
}
- log_debug("apply: %s\n", *fn);
- for (;;) {
- char line[LINE_MAX], *l;
-
- if (!fgets(line, sizeof(line), f))
- break;
+ } else {
+ _cleanup_free_ char **files = NULL;
+ char **fn, **i;
- l = strstrip(line);
- if (*l == '#' || *l == 0)
- continue;
-
- k = load_module(ctx, l);
+ STRV_FOREACH(i, arg_proc_cmdline_modules) {
+ k = load_module(ctx, *i);
if (k < 0)
r = EXIT_FAILURE;
}
- if (ferror(f)) {
- log_error("Failed to read from file: %m");
- r = EXIT_FAILURE;
+ r = conf_files_list_nulstr(&files, ".conf", NULL, conf_file_dirs);
+ if (r < 0) {
+ log_error("Failed to enumerate modules-load.d files: %s", strerror(-r));
+ goto finish;
}
- fclose(f);
+ STRV_FOREACH(fn, files) {
+ k = apply_file(ctx, *fn, true);
+ if (k < 0 && r == 0)
+ r = k;
+ }
}
finish:
- strv_free(files);
kmod_unref(ctx);
strv_free(arg_proc_cmdline_modules);
- return r;
+ return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}
diff --git a/src/notify/notify.c b/src/notify/notify.c
index f521f56659..1e9766f862 100644
--- a/src/notify/notify.c
+++ b/src/notify/notify.c
@@ -34,6 +34,7 @@
#include "log.h"
#include "sd-readahead.h"
#include "build.h"
+#include "env-util.h"
static bool arg_ready = false;
static pid_t arg_pid = 0;
diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c
index 1f3bda5b4a..09153c87ce 100644
--- a/src/nspawn/nspawn.c
+++ b/src/nspawn/nspawn.c
@@ -33,6 +33,7 @@
#include <sys/prctl.h>
#include <sys/capability.h>
#include <getopt.h>
+#include <sys/poll.h>
#include <sys/epoll.h>
#include <termios.h>
#include <sys/signalfd.h>
@@ -41,6 +42,10 @@
#include <sys/un.h>
#include <sys/socket.h>
+#ifdef HAVE_XATTR
+#include <attr/xattr.h>
+#endif
+
#include <systemd/sd-daemon.h>
#include "log.h"
@@ -56,6 +61,12 @@
#include "sd-id128.h"
#include "dev-setup.h"
#include "fdset.h"
+#include "build.h"
+#include "fileio.h"
+
+#ifndef TTY_GID
+#define TTY_GID 5
+#endif
typedef enum LinkJournal {
LINK_NO,
@@ -68,6 +79,7 @@ static char *arg_directory = NULL;
static char *arg_user = NULL;
static char **arg_controllers = NULL;
static char *arg_uuid = NULL;
+static char *arg_machine = NULL;
static bool arg_private_network = false;
static bool arg_read_only = false;
static bool arg_boot = false;
@@ -95,23 +107,34 @@ static uint64_t arg_retain =
(1ULL << CAP_SYS_PTRACE) |
(1ULL << CAP_SYS_TTY_CONFIG) |
(1ULL << CAP_SYS_RESOURCE) |
- (1ULL << CAP_SYS_BOOT);
+ (1ULL << CAP_SYS_BOOT) |
+ (1ULL << CAP_AUDIT_WRITE) |
+ (1ULL << CAP_AUDIT_CONTROL);
+static char **arg_bind = NULL;
+static char **arg_bind_ro = NULL;
static int help(void) {
printf("%s [OPTIONS...] [PATH] [ARGUMENTS...]\n\n"
"Spawn a minimal namespace container for debugging, testing and building.\n\n"
- " -h --help Show this help\n"
- " -D --directory=NAME Root directory for the container\n"
- " -b --boot Boot up full system (i.e. invoke init)\n"
- " -u --user=USER Run the command under specified user or uid\n"
- " -C --controllers=LIST Put the container in specified comma-separated cgroup hierarchies\n"
- " --uuid=UUID Set a specific machine UUID for the container\n"
- " --private-network Disable network in container\n"
- " --read-only Mount the root directory read-only\n"
- " --capability=CAP In addition to the default, retain specified capability\n"
- " --link-journal=MODE Link up guest journal, one of no, auto, guest, host\n"
- " -j Equivalent to --link-journal=host\n",
+ " -h --help Show this help\n"
+ " --version Print version string\n"
+ " -D --directory=NAME Root directory for the container\n"
+ " -b --boot Boot up full system (i.e. invoke init)\n"
+ " -u --user=USER Run the command under specified user or uid\n"
+ " -C --controllers=LIST Put the container in specified comma-separated\n"
+ " cgroup hierarchies\n"
+ " --uuid=UUID Set a specific machine UUID for the container\n"
+ " -M --machine=NAME Set the machine name for the container\n"
+ " --private-network Disable network in container\n"
+ " --read-only Mount the root directory read-only\n"
+ " --capability=CAP In addition to the default, retain specified\n"
+ " capability\n"
+ " --link-journal=MODE Link up guest journal, one of no, auto, guest, host\n"
+ " -j Equivalent to --link-journal=host\n"
+ " --bind=PATH[:PATH] Bind mount a file or directory from the host into\n"
+ " the container\n"
+ " --bind-ro=PATH[:PATH] Similar, but creates a read-only bind mount\n",
program_invocation_short_name);
return 0;
@@ -120,15 +143,19 @@ static int help(void) {
static int parse_argv(int argc, char *argv[]) {
enum {
- ARG_PRIVATE_NETWORK = 0x100,
+ ARG_VERSION = 0x100,
+ ARG_PRIVATE_NETWORK,
ARG_UUID,
ARG_READ_ONLY,
ARG_CAPABILITY,
- ARG_LINK_JOURNAL
+ ARG_LINK_JOURNAL,
+ ARG_BIND,
+ ARG_BIND_RO
};
static const struct option options[] = {
{ "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, ARG_VERSION },
{ "directory", required_argument, NULL, 'D' },
{ "user", required_argument, NULL, 'u' },
{ "controllers", required_argument, NULL, 'C' },
@@ -138,6 +165,9 @@ static int parse_argv(int argc, char *argv[]) {
{ "read-only", no_argument, NULL, ARG_READ_ONLY },
{ "capability", required_argument, NULL, ARG_CAPABILITY },
{ "link-journal", required_argument, NULL, ARG_LINK_JOURNAL },
+ { "bind", required_argument, NULL, ARG_BIND },
+ { "bind-ro", required_argument, NULL, ARG_BIND_RO },
+ { "machine", required_argument, NULL, 'M' },
{ NULL, 0, NULL, 0 }
};
@@ -146,7 +176,7 @@ static int parse_argv(int argc, char *argv[]) {
assert(argc >= 0);
assert(argv);
- while ((c = getopt_long(argc, argv, "+hD:u:C:bj", options, NULL)) >= 0) {
+ while ((c = getopt_long(argc, argv, "+hD:u:C:bM:j", options, NULL)) >= 0) {
switch (c) {
@@ -154,6 +184,11 @@ static int parse_argv(int argc, char *argv[]) {
help();
return 0;
+ case ARG_VERSION:
+ puts(PACKAGE_STRING);
+ puts(SYSTEMD_FEATURES);
+ return 0;
+
case 'D':
free(arg_directory);
arg_directory = canonicalize_file_name(optarg);
@@ -166,22 +201,19 @@ static int parse_argv(int argc, char *argv[]) {
case 'u':
free(arg_user);
- if (!(arg_user = strdup(optarg))) {
- log_error("Failed to duplicate user name.");
- return -ENOMEM;
- }
+ arg_user = strdup(optarg);
+ if (!arg_user)
+ return log_oom();
break;
case 'C':
strv_free(arg_controllers);
arg_controllers = strv_split(optarg, ",");
- if (!arg_controllers) {
- log_error("Failed to split controllers list.");
- return -ENOMEM;
- }
- strv_uniq(arg_controllers);
+ if (!arg_controllers)
+ return log_oom();
+ cg_shorten_controllers(arg_controllers);
break;
case ARG_PRIVATE_NETWORK:
@@ -193,9 +225,27 @@ static int parse_argv(int argc, char *argv[]) {
break;
case ARG_UUID:
+ if (!id128_is_valid(optarg)) {
+ log_error("Invalid UUID: %s", optarg);
+ return -EINVAL;
+ }
+
arg_uuid = optarg;
break;
+ case 'M':
+ if (!hostname_is_valid(optarg)) {
+ log_error("Invalid machine name: %s", optarg);
+ return -EINVAL;
+ }
+
+ free(arg_machine);
+ arg_machine = strdup(optarg);
+ if (!arg_machine)
+ return log_oom();
+
+ break;
+
case ARG_READ_ONLY:
arg_read_only = true;
break;
@@ -245,6 +295,43 @@ static int parse_argv(int argc, char *argv[]) {
break;
+ case ARG_BIND:
+ case ARG_BIND_RO: {
+ _cleanup_free_ char *a = NULL, *b = NULL;
+ char *e;
+ char ***x;
+ int r;
+
+ x = c == ARG_BIND ? &arg_bind : &arg_bind_ro;
+
+ e = strchr(optarg, ':');
+ if (e) {
+ a = strndup(optarg, e - optarg);
+ b = strdup(e + 1);
+ } else {
+ a = strdup(optarg);
+ b = strdup(optarg);
+ }
+
+ if (!a || !b)
+ return log_oom();
+
+ if (!path_is_absolute(a) || !path_is_absolute(b)) {
+ log_error("Invalid bind mount specification: %s", optarg);
+ return -EINVAL;
+ }
+
+ r = strv_extend(x, a);
+ if (r < 0)
+ return r;
+
+ r = strv_extend(x, b);
+ if (r < 0)
+ return r;
+
+ break;
+ }
+
case '?':
return -EINVAL;
@@ -274,7 +361,7 @@ static int mount_all(const char *dest) {
{ NULL, "/proc/sys", NULL, NULL, MS_BIND|MS_RDONLY|MS_REMOUNT, true }, /* Then, make it r/o */
{ "sysfs", "/sys", "sysfs", NULL, MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV, true },
{ "tmpfs", "/dev", "tmpfs", "mode=755", MS_NOSUID|MS_STRICTATIME, true },
- { "/dev/pts", "/dev/pts", NULL, NULL, MS_BIND, true },
+ { "devpts", "/dev/pts", "devpts","newinstance,ptmxmode=0666,mode=620,gid=" STRINGIFY(TTY_GID), MS_NOSUID|MS_NOEXEC, true },
{ "tmpfs", "/dev/shm", "tmpfs", "mode=1777", MS_NOSUID|MS_NODEV|MS_STRICTATIME, true },
{ "tmpfs", "/run", "tmpfs", "mode=755", MS_NOSUID|MS_NODEV|MS_STRICTATIME, true },
#ifdef HAVE_SELINUX
@@ -287,17 +374,12 @@ static int mount_all(const char *dest) {
int r = 0;
for (k = 0; k < ELEMENTSOF(mount_table); k++) {
- char _cleanup_free_ *where = NULL;
+ _cleanup_free_ char *where = NULL;
int t;
- if (asprintf(&where, "%s/%s", dest, mount_table[k].where) < 0) {
- log_oom();
-
- if (r == 0)
- r = -ENOMEM;
-
- break;
- }
+ where = strjoin(dest, "/", mount_table[k].where, NULL);
+ if (!where)
+ return log_oom();
t = path_is_mount_point(where, true);
if (t < 0) {
@@ -313,7 +395,7 @@ static int mount_all(const char *dest) {
if (mount_table[k].what && t > 0)
continue;
- mkdir_p_label(where, 0755);
+ mkdir_p(where, 0755);
if (mount(mount_table[k].what,
where,
@@ -332,6 +414,32 @@ static int mount_all(const char *dest) {
return r;
}
+static int mount_binds(const char *dest, char **l, unsigned long flags) {
+ char **x, **y;
+
+ STRV_FOREACH_PAIR(x, y, l) {
+ _cleanup_free_ char *where = NULL;
+
+ where = strjoin(dest, "/", *y, NULL);
+ if (!where)
+ return log_oom();
+
+ mkdir_p_label(where, 0755);
+
+ if (mount(*x, where, "bind", MS_BIND, NULL) < 0) {
+ log_error("mount(%s) failed: %m", where);
+ return -errno;
+ }
+
+ if (flags && mount(NULL, where, NULL, MS_REMOUNT|MS_BIND|flags, NULL) < 0) {
+ log_error("mount(%s) failed: %m", where);
+ return -errno;
+ }
+ }
+
+ return 0;
+}
+
static int setup_timezone(const char *dest) {
_cleanup_free_ char *where = NULL, *p = NULL, *q = NULL, *check = NULL, *what = NULL;
char *z, *y;
@@ -393,7 +501,8 @@ static int setup_timezone(const char *dest) {
}
static int setup_resolv_conf(const char *dest) {
- char *where;
+ char _cleanup_free_ *where = NULL;
+ _cleanup_close_ int fd = -1;
assert(dest);
@@ -405,18 +514,24 @@ static int setup_resolv_conf(const char *dest) {
if (!where)
return log_oom();
+ fd = open(where, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0644);
+
/* We don't really care for the results of this really. If it
* fails, it fails, but meh... */
- if (mount("/etc/resolv.conf", where, "bind", MS_BIND, NULL) >= 0)
- mount("/etc/resolv.conf", where, "bind", MS_BIND|MS_REMOUNT|MS_RDONLY, NULL);
-
- free(where);
+ if (mount("/etc/resolv.conf", where, "bind", MS_BIND, NULL) < 0)
+ log_warning("Failed to bind mount /etc/resolv.conf: %m");
+ else
+ if (mount("/etc/resolv.conf", where, "bind",
+ MS_BIND|MS_REMOUNT|MS_RDONLY, NULL) < 0) {
+ log_error("Failed to remount /etc/resolv.conf readonly: %m");
+ return -errno;
+ }
return 0;
}
static int setup_boot_id(const char *dest) {
- char _cleanup_free_ *from = NULL, *to = NULL;
+ _cleanup_free_ char *from = NULL, *to = NULL;
sd_id128_t rnd;
char as_uuid[37];
int r;
@@ -442,7 +557,7 @@ static int setup_boot_id(const char *dest) {
SD_ID128_FORMAT_VAL(rnd));
char_array_0(as_uuid);
- r = write_one_line_file(from, as_uuid);
+ r = write_string_file(from, as_uuid);
if (r < 0) {
log_error("Failed to write boot id: %s", strerror(-r));
return r;
@@ -451,8 +566,8 @@ static int setup_boot_id(const char *dest) {
if (mount(from, to, "bind", MS_BIND, NULL) < 0) {
log_error("Failed to bind mount boot id: %m");
r = -errno;
- } else
- mount(from, to, "bind", MS_BIND|MS_REMOUNT|MS_RDONLY, NULL);
+ } else if (mount(from, to, "bind", MS_BIND|MS_REMOUNT|MS_RDONLY, NULL))
+ log_warning("Failed to make boot id read-only: %m");
unlink(from);
return r;
@@ -466,12 +581,11 @@ static int copy_devnodes(const char *dest) {
"full\0"
"random\0"
"urandom\0"
- "tty\0"
- "ptmx\0";
+ "tty\0";
const char *d;
int r = 0;
- mode_t _cleanup_umask_ u;
+ _cleanup_umask_ mode_t u;
assert(dest);
@@ -479,7 +593,7 @@ static int copy_devnodes(const char *dest) {
NULSTR_FOREACH(d, devnodes) {
struct stat st;
- char _cleanup_free_ *from = NULL, *to = NULL;
+ _cleanup_free_ char *from = NULL, *to = NULL;
asprintf(&from, "/dev/%s", d);
asprintf(&to, "%s/dev/%s", dest, d);
@@ -518,11 +632,26 @@ static int copy_devnodes(const char *dest) {
return r;
}
+static int setup_ptmx(const char *dest) {
+ _cleanup_free_ char *p = NULL;
+
+ p = strappend(dest, "/dev/ptmx");
+ if (!p)
+ return log_oom();
+
+ if (symlink("pts/ptmx", p) < 0) {
+ log_error("Failed to create /dev/ptmx symlink: %m");
+ return -errno;
+ }
+
+ return 0;
+}
+
static int setup_dev_console(const char *dest, const char *console) {
struct stat st;
- char _cleanup_free_ *to = NULL;
+ _cleanup_free_ char *to = NULL;
int r;
- mode_t _cleanup_umask_ u;
+ _cleanup_umask_ mode_t u;
assert(dest);
assert(console);
@@ -568,14 +697,17 @@ static int setup_dev_console(const char *dest, const char *console) {
}
static int setup_kmsg(const char *dest, int kmsg_socket) {
- char _cleanup_free_ *from = NULL, *to = NULL;
+ _cleanup_free_ char *from = NULL, *to = NULL;
int r, fd, k;
- mode_t _cleanup_umask_ u;
+ _cleanup_umask_ mode_t u;
union {
struct cmsghdr cmsghdr;
uint8_t buf[CMSG_SPACE(sizeof(int))];
- } control;
- struct msghdr mh;
+ } control = {};
+ struct msghdr mh = {
+ .msg_control = &control,
+ .msg_controllen = sizeof(control),
+ };
struct cmsghdr *cmsg;
assert(dest);
@@ -616,12 +748,6 @@ static int setup_kmsg(const char *dest, int kmsg_socket) {
return -errno;
}
- zero(mh);
- zero(control);
-
- mh.msg_control = &control;
- mh.msg_controllen = sizeof(control);
-
cmsg = CMSG_FIRSTHDR(&mh);
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
@@ -646,30 +772,16 @@ static int setup_kmsg(const char *dest, int kmsg_socket) {
}
static int setup_hostname(void) {
- char *hn;
- int r = 0;
-
- hn = path_get_file_name(arg_directory);
- if (hn) {
- hn = strdup(hn);
- if (!hn)
- return -ENOMEM;
-
- hostname_cleanup(hn);
-
- if (!isempty(hn))
- if (sethostname(hn, strlen(hn)) < 0)
- r = -errno;
- free(hn);
- }
+ if (sethostname(arg_machine, strlen(arg_machine)) < 0)
+ return -errno;
- return r;
+ return 0;
}
static int setup_journal(const char *directory) {
sd_id128_t machine_id;
- char _cleanup_free_ *p = NULL, *b = NULL, *q = NULL, *d = NULL;
+ _cleanup_free_ char *p = NULL, *b = NULL, *q = NULL, *d = NULL;
char *id;
int r;
@@ -799,22 +911,70 @@ static int setup_journal(const char *directory) {
return 0;
}
-static int drop_capabilities(void) {
- return capability_bounding_set_drop(~arg_retain, false);
+static int setup_cgroup(const char *path) {
+ char **c;
+ int r;
+
+ r = cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, path, 1);
+ if (r < 0) {
+ log_error("Failed to create cgroup: %s", strerror(-r));
+ return r;
+ }
+
+ STRV_FOREACH(c, arg_controllers) {
+ r = cg_create_and_attach(*c, path, 1);
+ if (r < 0)
+ log_warning("Failed to create cgroup in controller %s: %s", *c, strerror(-r));
+ }
+
+ return 0;
}
-static int is_os_tree(const char *path) {
- int r;
- char *p;
- /* We use /bin/sh as flag file if something is an OS */
+static int save_attributes(const char *cgroup, pid_t pid, const char *uuid, const char *directory) {
+#ifdef HAVE_XATTR
+ _cleanup_free_ char *path = NULL;
+ char buf[DECIMAL_STR_MAX(pid_t)];
+ int r = 0, k;
- if (asprintf(&p, "%s/bin/sh", path) < 0)
- return -ENOMEM;
+ assert(cgroup);
+ assert(pid >= 0);
+ assert(arg_directory);
- r = access(p, F_OK);
- free(p);
+ assert_se(snprintf(buf, sizeof(buf), "%lu", (unsigned long) pid) < (int) sizeof(buf));
+
+ r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, cgroup, NULL, &path);
+ if (r < 0) {
+ log_error("Failed to get path: %s", strerror(-r));
+ return r;
+ }
- return r < 0 ? 0 : 1;
+ r = setxattr(path, "trusted.init_pid", buf, strlen(buf), XATTR_CREATE);
+ if (r < 0)
+ log_warning("Failed to set %s attribute on %s: %m", "trusted.init_pid", path);
+
+ if (uuid) {
+ k = setxattr(path, "trusted.machine_id", uuid, strlen(uuid), XATTR_CREATE);
+ if (k < 0) {
+ log_warning("Failed to set %s attribute on %s: %m", "trusted.machine_id", path);
+ if (r == 0)
+ r = k;
+ }
+ }
+
+ k = setxattr(path, "trusted.root_directory", directory, strlen(directory), XATTR_CREATE);
+ if (k < 0) {
+ log_warning("Failed to set %s attribute on %s: %m", "trusted.root_directory", path);
+ if (r == 0)
+ r = k;
+ }
+ return r;
+#else
+ return 0;
+#endif
+}
+
+static int drop_capabilities(void) {
+ return capability_bounding_set_drop(~arg_retain, false);
}
static int process_pty(int master, pid_t pid, sigset_t *mask) {
@@ -874,8 +1034,17 @@ static int process_pty(int master, pid_t pid, sigset_t *mask) {
signal_ev.events = EPOLLIN;
signal_ev.data.fd = signal_fd;
- if (epoll_ctl(ep, EPOLL_CTL_ADD, STDOUT_FILENO, &stdout_ev) < 0 ||
- epoll_ctl(ep, EPOLL_CTL_ADD, master, &master_ev) < 0 ||
+ if (epoll_ctl(ep, EPOLL_CTL_ADD, STDOUT_FILENO, &stdout_ev) < 0) {
+ if (errno != EPERM) {
+ log_error("Failed to register stdout in epoll: %m");
+ r = -errno;
+ goto finish;
+ }
+ /* stdout without epoll support. Likely redirected to regular file. */
+ stdout_writable = true;
+ }
+
+ if (epoll_ctl(ep, EPOLL_CTL_ADD, master, &master_ev) < 0 ||
epoll_ctl(ep, EPOLL_CTL_ADD, signal_fd, &signal_ev) < 0) {
log_error("Failed to register fds in epoll: %m");
r = -errno;
@@ -1053,9 +1222,9 @@ finish:
int main(int argc, char *argv[]) {
pid_t pid = 0;
int r = EXIT_FAILURE, k;
- char *oldcg = NULL, *newcg = NULL;
- char **controller = NULL;
- int master = -1, n_fd_passed;
+ _cleanup_free_ char *newcg = NULL;
+ _cleanup_close_ int master = -1;
+ int n_fd_passed;
const char *console = NULL;
struct termios saved_attr, raw_attr;
sigset_t mask;
@@ -1067,9 +1236,13 @@ int main(int argc, char *argv[]) {
log_parse_environment();
log_open();
- r = parse_argv(argc, argv);
- if (r <= 0)
+ k = parse_argv(argc, argv);
+ if (k < 0)
+ goto finish;
+ else if (k == 0) {
+ r = EXIT_SUCCESS;
goto finish;
+ }
if (arg_directory) {
char *p;
@@ -1081,12 +1254,26 @@ int main(int argc, char *argv[]) {
arg_directory = get_current_dir_name();
if (!arg_directory) {
- log_error("Failed to determine path");
+ log_error("Failed to determine path, please use -D.");
goto finish;
}
path_kill_slashes(arg_directory);
+ if (!arg_machine) {
+ arg_machine = strdup(path_get_file_name(arg_directory));
+ if (!arg_machine) {
+ log_oom();
+ goto finish;
+ }
+
+ hostname_cleanup(arg_machine, false);
+ if (isempty(arg_machine)) {
+ log_error("Failed to determine machine name automatically, please use -M.");
+ goto finish;
+ }
+ }
+
if (geteuid() != 0) {
log_error("Need to be root.");
goto finish;
@@ -1102,8 +1289,8 @@ int main(int argc, char *argv[]) {
goto finish;
}
- if (is_os_tree(arg_directory) <= 0) {
- log_error("Directory %s doesn't look like an OS root directory. Refusing.", arg_directory);
+ if (path_is_os_tree(arg_directory) <= 0) {
+ log_error("Directory %s doesn't look like an OS root directory (/etc/os-release is missing). Refusing.", arg_directory);
goto finish;
}
@@ -1119,27 +1306,20 @@ int main(int argc, char *argv[]) {
fdset_close_others(fds);
log_open();
- k = cg_get_by_pid(SYSTEMD_CGROUP_CONTROLLER, 0, &oldcg);
+ k = cg_get_machine_path(arg_machine, &newcg);
if (k < 0) {
- log_error("Failed to determine current cgroup: %s", strerror(-k));
+ log_error("Failed to determine machine cgroup path: %s", strerror(-k));
goto finish;
}
- if (asprintf(&newcg, "%s/nspawn-%lu", oldcg, (unsigned long) getpid()) < 0) {
- log_error("Failed to allocate cgroup path.");
- goto finish;
- }
+ k = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, newcg, true);
+ if (k <= 0 && k != -ENOENT) {
+ log_error("Container already running.");
- k = cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, newcg, 0);
- if (k < 0) {
- log_error("Failed to create cgroup: %s", strerror(-k));
- goto finish;
- }
+ free(newcg);
+ newcg = NULL;
- STRV_FOREACH(controller, arg_controllers) {
- k = cg_create_and_attach(*controller, newcg, 0);
- if (k < 0)
- log_warning("Failed to create cgroup in controller %s: %s", *controller, strerror(-k));
+ goto finish;
}
master = posix_openpt(O_RDWR|O_NOCTTY|O_CLOEXEC|O_NDELAY);
@@ -1173,22 +1353,29 @@ int main(int argc, char *argv[]) {
}
if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_NONBLOCK|SOCK_CLOEXEC, 0, kmsg_socket_pair) < 0) {
- log_error("Failed to create kmsg socket pair");
+ log_error("Failed to create kmsg socket pair.");
goto finish;
}
+ sd_notify(0, "READY=1");
+
assert_se(sigemptyset(&mask) == 0);
sigset_add_many(&mask, SIGCHLD, SIGWINCH, SIGTERM, SIGINT, -1);
assert_se(sigprocmask(SIG_BLOCK, &mask, NULL) == 0);
for (;;) {
siginfo_t status;
+ int pipefd[2], pipefd2[2];
- if (saved_attr_valid) {
- if (tcsetattr(STDIN_FILENO, TCSANOW, &raw_attr) < 0) {
- log_error("Failed to set terminal attributes: %m");
- goto finish;
- }
+ if (pipe2(pipefd, O_NONBLOCK|O_CLOEXEC) < 0) {
+ log_error("pipe2(): %m");
+ goto finish;
+ }
+
+ if (pipe2(pipefd2, O_NONBLOCK|O_CLOEXEC) < 0) {
+ log_error("pipe2(): %m");
+ close_pipe(pipefd);
+ goto finish;
}
pid = syscall(__NR_clone, SIGCHLD|CLONE_NEWIPC|CLONE_NEWNS|CLONE_NEWPID|CLONE_NEWUTS|(arg_private_network ? CLONE_NEWNET : 0), NULL);
@@ -1203,11 +1390,10 @@ int main(int argc, char *argv[]) {
if (pid == 0) {
/* child */
-
const char *home = NULL;
uid_t uid = (uid_t) -1;
gid_t gid = (gid_t) -1;
- unsigned n_env = 0;
+ unsigned n_env = 2;
const char *envp[] = {
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"container=systemd-nspawn", /* LXC sets container=lxc, so follow the scheme here */
@@ -1221,12 +1407,25 @@ int main(int argc, char *argv[]) {
NULL
};
- envp[2] = strv_find_prefix(environ, "TERM=");
- n_env = 3;
+ envp[n_env] = strv_find_prefix(environ, "TERM=");
+ if (envp[n_env])
+ n_env ++;
+
+ /* Wait for the parent process to log our PID */
+ close_nointr_nofail(pipefd[1]);
+ fd_wait_for_event(pipefd[0], POLLHUP, -1);
+ close_nointr_nofail(pipefd[0]);
close_nointr_nofail(master);
master = -1;
+ if (saved_attr_valid) {
+ if (tcsetattr(STDIN_FILENO, TCSANOW, &raw_attr) < 0) {
+ log_error("Failed to set terminal attributes: %m");
+ goto child_fail;
+ }
+ }
+
close_nointr(STDIN_FILENO);
close_nointr(STDOUT_FILENO);
close_nointr(STDERR_FILENO);
@@ -1266,6 +1465,11 @@ int main(int argc, char *argv[]) {
goto child_fail;
}
+ if (setup_cgroup(newcg) < 0)
+ goto child_fail;
+
+ close_pipe(pipefd2);
+
/* Mark everything as slave, so that we still
* receive mounts from the real root, but don't
* propagate mounts to the real root. */
@@ -1292,6 +1496,9 @@ int main(int argc, char *argv[]) {
if (copy_devnodes(arg_directory) < 0)
goto child_fail;
+ if (setup_ptmx(arg_directory) < 0)
+ goto child_fail;
+
dev_setup(arg_directory);
if (setup_dev_console(arg_directory, console) < 0)
@@ -1315,6 +1522,12 @@ int main(int argc, char *argv[]) {
if (setup_journal(arg_directory) < 0)
goto child_fail;
+ if (mount_binds(arg_directory, arg_bind, 0) < 0)
+ goto child_fail;
+
+ if (mount_binds(arg_directory, arg_bind_ro, MS_RDONLY) < 0)
+ goto child_fail;
+
if (chdir(arg_directory) < 0) {
log_error("chdir(%s) failed: %m", arg_directory);
goto child_fail;
@@ -1422,7 +1635,7 @@ int main(int argc, char *argv[]) {
}
if ((asprintf((char **)(envp + n_env++), "LISTEN_FDS=%u", n_fd_passed) < 0) ||
- (asprintf((char **)(envp + n_env++), "LISTEN_PID=%lu", (unsigned long) getpid()) < 0)) {
+ (asprintf((char **)(envp + n_env++), "LISTEN_PID=%lu", (unsigned long) 1) < 0)) {
log_oom();
goto child_fail;
}
@@ -1461,6 +1674,17 @@ int main(int argc, char *argv[]) {
_exit(EXIT_FAILURE);
}
+ log_info("Init process in the container running as PID %lu.", (unsigned long) pid);
+ close_nointr_nofail(pipefd[0]);
+ close_nointr_nofail(pipefd[1]);
+
+ /* Wait for the child process to establish cgroup hierarchy */
+ close_nointr_nofail(pipefd2[1]);
+ fd_wait_for_event(pipefd2[0], POLLHUP, -1);
+ close_nointr_nofail(pipefd2[0]);
+
+ save_attributes(newcg, pid, arg_uuid, arg_directory);
+
fdset_free(fds);
fds = NULL;
@@ -1470,16 +1694,16 @@ int main(int argc, char *argv[]) {
if (saved_attr_valid)
tcsetattr(STDIN_FILENO, TCSANOW, &saved_attr);
- r = wait_for_terminate(pid, &status);
- if (r < 0) {
+ k = wait_for_terminate(pid, &status);
+ if (k < 0) {
r = EXIT_FAILURE;
break;
}
if (status.si_code == CLD_EXITED) {
+ r = status.si_status;
if (status.si_status != 0) {
log_error("Container failed with error code %i.", status.si_status);
- r = status.si_status;
break;
}
@@ -1511,21 +1735,14 @@ finish:
if (saved_attr_valid)
tcsetattr(STDIN_FILENO, TCSANOW, &saved_attr);
- if (master >= 0)
- close_nointr_nofail(master);
-
close_pipe(kmsg_socket_pair);
- if (oldcg)
- cg_attach(SYSTEMD_CGROUP_CONTROLLER, oldcg, 0);
-
if (newcg)
cg_kill_recursive_and_wait(SYSTEMD_CGROUP_CONTROLLER, newcg, true);
free(arg_directory);
+ free(arg_machine);
strv_free(arg_controllers);
- free(oldcg);
- free(newcg);
fdset_free(fds);
diff --git a/src/nss-myhostname/Makefile b/src/nss-myhostname/Makefile
new file mode 120000
index 0000000000..d0b0e8e008
--- /dev/null
+++ b/src/nss-myhostname/Makefile
@@ -0,0 +1 @@
+../Makefile \ No newline at end of file
diff --git a/src/nss-myhostname/netlink.c b/src/nss-myhostname/netlink.c
index 53c3b501be..b1ef912c8a 100644
--- a/src/nss-myhostname/netlink.c
+++ b/src/nss-myhostname/netlink.c
@@ -36,173 +36,171 @@
#include "ifconf.h"
-int ifconf_acquire_addresses(struct address **_list, unsigned *_n_list) {
-
+#define SEQ 4711
+
+static int read_reply(int fd, struct address **list, unsigned *n_list) {
+ ssize_t bytes;
+ struct cmsghdr *cmsg;
+ struct ucred *ucred;
+ struct nlmsghdr *p;
+ uint8_t cred_buffer[CMSG_SPACE(sizeof(struct ucred))];
struct {
struct nlmsghdr hdr;
- struct rtgenmsg gen;
- } req;
- struct rtgenmsg *gen;
- int fd, r, on = 1;
- uint32_t seq = 4711;
- struct address *list = NULL;
- unsigned n_list = 0;
-
- fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
- if (fd < 0)
+ struct ifaddrmsg ifaddrmsg;
+ uint8_t payload[16*1024];
+ } resp;
+ struct iovec iov = {
+ .iov_base = &resp,
+ .iov_len = sizeof(resp),
+ };
+ struct msghdr msg = {
+ .msg_name = NULL,
+ .msg_namelen = 0,
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ .msg_control = cred_buffer,
+ .msg_controllen = sizeof(cred_buffer),
+ .msg_flags = 0,
+ };
+
+ assert(fd >= 0);
+ assert(list);
+
+ bytes = recvmsg(fd, &msg, 0);
+ if (bytes < 0)
return -errno;
- if (setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)) < 0) {
- r = -errno;
- goto finish;
- }
+ cmsg = CMSG_FIRSTHDR(&msg);
+ if (!cmsg || cmsg->cmsg_type != SCM_CREDENTIALS)
+ return -EIO;
- memset(&req, 0, sizeof(req));
- req.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg));
- req.hdr.nlmsg_type = RTM_GETADDR;
- req.hdr.nlmsg_flags = NLM_F_REQUEST|NLM_F_DUMP|NLM_F_ACK;
- req.hdr.nlmsg_seq = seq;
- req.hdr.nlmsg_pid = 0;
+ ucred = (struct ucred*) CMSG_DATA(cmsg);
+ if (ucred->uid != 0 || ucred->pid != 0)
+ return 0;
- gen = NLMSG_DATA(&req.hdr);
- gen->rtgen_family = AF_UNSPEC;
+ for (p = &resp.hdr; bytes > 0; p = NLMSG_NEXT(p, bytes)) {
+ struct ifaddrmsg *ifaddrmsg;
+ struct rtattr *a;
+ size_t l;
+ void *local = NULL, *address = NULL;
- if (send(fd, &req, req.hdr.nlmsg_len, 0) < 0) {
- r = -errno;
- goto finish;
- }
+ if (!NLMSG_OK(p, (size_t) bytes))
+ return -EIO;
- for (;;) {
- ssize_t bytes;
- struct msghdr msg;
- struct cmsghdr *cmsg;
- struct ucred *ucred;
- struct iovec iov;
- struct nlmsghdr *p;
- uint8_t cred_buffer[CMSG_SPACE(sizeof(struct ucred))];
- struct {
- struct nlmsghdr hdr;
- struct ifaddrmsg ifaddrmsg;
- uint8_t payload[16*1024];
- } resp;
-
- memset(&iov, 0, sizeof(iov));
- iov.iov_base = &resp;
- iov.iov_len = sizeof(resp);
-
- memset(&msg, 0, sizeof(msg));
- msg.msg_name = NULL;
- msg.msg_namelen = 0;
- msg.msg_iov = &iov;
- msg.msg_iovlen = 1;
- msg.msg_control = cred_buffer;
- msg.msg_controllen = sizeof(cred_buffer);
- msg.msg_flags = 0;
-
- bytes = recvmsg(fd, &msg, 0);
- if (bytes < 0) {
- r = -errno;
- goto finish;
- }
+ if (p->nlmsg_seq != SEQ)
+ continue;
+
+ if (p->nlmsg_type == NLMSG_DONE)
+ return 1;
- cmsg = CMSG_FIRSTHDR(&msg);
- if (!cmsg || cmsg->cmsg_type != SCM_CREDENTIALS) {
- r = -EIO;
- goto finish;
+ if (p->nlmsg_type == NLMSG_ERROR) {
+ struct nlmsgerr *nlmsgerr;
+
+ nlmsgerr = NLMSG_DATA(p);
+ return -nlmsgerr->error;
}
- ucred = (struct ucred*) CMSG_DATA(cmsg);
- if (ucred->uid != 0 || ucred->pid != 0)
+ if (p->nlmsg_type != RTM_NEWADDR)
continue;
- for (p = &resp.hdr; bytes > 0; p = NLMSG_NEXT(p, bytes)) {
- struct ifaddrmsg *ifaddrmsg;
- struct rtattr *a;
- size_t l;
- void *local = NULL, *address = NULL;
+ ifaddrmsg = NLMSG_DATA(p);
- if (!NLMSG_OK(p, (size_t) bytes)) {
- r = -EIO;
- goto finish;
- }
+ if (ifaddrmsg->ifa_family != AF_INET &&
+ ifaddrmsg->ifa_family != AF_INET6)
+ continue;
- if (p->nlmsg_seq != seq)
- continue;
+ if (ifaddrmsg->ifa_scope == RT_SCOPE_HOST ||
+ ifaddrmsg->ifa_scope == RT_SCOPE_NOWHERE)
+ continue;
- if (p->nlmsg_type == NLMSG_DONE) {
- r = 0;
- goto finish;
- }
+ if (ifaddrmsg->ifa_flags & IFA_F_DEPRECATED)
+ continue;
- if (p->nlmsg_type == NLMSG_ERROR) {
- struct nlmsgerr *nlmsgerr;
+ l = NLMSG_PAYLOAD(p, sizeof(struct ifaddrmsg));
+ a = IFA_RTA(ifaddrmsg);
- nlmsgerr = NLMSG_DATA(p);
- r = -nlmsgerr->error;
- goto finish;
- }
+ while (RTA_OK(a, l)) {
- if (p->nlmsg_type != RTM_NEWADDR)
- continue;
+ if (a->rta_type == IFA_ADDRESS)
+ address = RTA_DATA(a);
+ else if (a->rta_type == IFA_LOCAL)
+ local = RTA_DATA(a);
- ifaddrmsg = NLMSG_DATA(p);
+ a = RTA_NEXT(a, l);
+ }
- if (ifaddrmsg->ifa_family != AF_INET &&
- ifaddrmsg->ifa_family != AF_INET6)
- continue;
+ if (local)
+ address = local;
- if (ifaddrmsg->ifa_scope == RT_SCOPE_HOST ||
- ifaddrmsg->ifa_scope == RT_SCOPE_NOWHERE)
- continue;
+ if (!address)
+ continue;
- if (ifaddrmsg->ifa_flags & IFA_F_DEPRECATED)
- continue;
+ *list = realloc(*list, (*n_list+1) * sizeof(struct address));
+ if (!*list)
+ return -ENOMEM;
- l = NLMSG_PAYLOAD(p, sizeof(struct ifaddrmsg));
- a = IFA_RTA(ifaddrmsg);
+ (*list)[*n_list].family = ifaddrmsg->ifa_family;
+ (*list)[*n_list].scope = ifaddrmsg->ifa_scope;
+ memcpy((*list)[*n_list].address,
+ address, ifaddrmsg->ifa_family == AF_INET ? 4 : 16);
+ (*list)[*n_list].ifindex = ifaddrmsg->ifa_index;
- while (RTA_OK(a, l)) {
+ (*n_list)++;
+ }
- if (a->rta_type == IFA_ADDRESS)
- address = RTA_DATA(a);
- else if (a->rta_type == IFA_LOCAL)
- local = RTA_DATA(a);
+ return 0;
+}
- a = RTA_NEXT(a, l);
- }
- if (local)
- address = local;
+int ifconf_acquire_addresses(struct address **_list, unsigned *_n_list) {
- if (!address)
- continue;
+ struct {
+ struct nlmsghdr hdr;
+ struct rtgenmsg gen;
+ } req = { {
+ .nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg)),
+ .nlmsg_type = RTM_GETADDR,
+ .nlmsg_flags = NLM_F_REQUEST|NLM_F_DUMP|NLM_F_ACK,
+ .nlmsg_seq = SEQ,
+ .nlmsg_pid = 0,
+ }, {
+ .rtgen_family = AF_UNSPEC,
+ }
+ };
+ int r, on = 1;
+ struct address *list = NULL;
+ unsigned n_list = 0;
+ int fd;
- list = realloc(list, (n_list+1) * sizeof(struct address));
- if (!list) {
- r = -ENOMEM;
- goto finish;
- }
+ fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
+ if (fd < 0)
+ return -errno;
- list[n_list].family = ifaddrmsg->ifa_family;
- list[n_list].scope = ifaddrmsg->ifa_scope;
- memcpy(list[n_list].address, address, ifaddrmsg->ifa_family == AF_INET ? 4 : 16);
- list[n_list].ifindex = ifaddrmsg->ifa_index;
+ if (setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)) < 0) {
+ r = -errno;
+ goto finish;
+ }
- n_list++;
- }
+ if (send(fd, &req, req.hdr.nlmsg_len, 0) < 0) {
+ r = -errno;
+ goto finish;
}
+ while((r = read_reply(fd, &list, &n_list)) == 0)
+ ;
+
finish:
close(fd);
- if (r < 0)
+ if (r < 0) {
free(list);
- else {
- qsort(list, n_list, sizeof(struct address), address_compare);
-
- *_list = list;
- *_n_list = n_list;
+ return r;
}
- return r;
+ qsort(list, n_list, sizeof(struct address), address_compare);
+
+ *_list = list;
+ *_n_list = n_list;
+
+ return 0;
}
diff --git a/src/nss-myhostname/nss-myhostname.c b/src/nss-myhostname/nss-myhostname.c
index 834a80690f..60e256d54f 100644
--- a/src/nss-myhostname/nss-myhostname.c
+++ b/src/nss-myhostname/nss-myhostname.c
@@ -25,13 +25,18 @@
#include <netdb.h>
#include <errno.h>
#include <string.h>
-#include <assert.h>
#include <unistd.h>
#include <net/if.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include "ifconf.h"
+#include "macro.h"
+
+/* Ensure that glibc's assert is used. We cannot use assert from macro.h, as
+ * libnss_myhostname will be linked into arbitrary programs which will, in turn
+ * attempt to write to the journal via log_dispatch() */
+#include <assert.h>
/* We use 127.0.0.2 as IPv4 address. This has the advantage over
* 127.0.0.1 that it can be translated back to the local hostname. For
@@ -42,8 +47,6 @@
#define LOCALADDRESS_IPV6 &in6addr_loopback
#define LOOPBACK_INTERFACE "lo"
-#define ALIGN(a) (((a+sizeof(void*)-1)/sizeof(void*))*sizeof(void*))
-
enum nss_status _nss_myhostname_gethostbyname4_r(
const char *name,
struct gaih_addrtuple **pat,
@@ -96,33 +99,47 @@ enum nss_status _nss_myhostname_gethostbyname4_r(
int32_t *ttlp) {
unsigned lo_ifi;
- char hn[HOST_NAME_MAX+1];
+ char hn[HOST_NAME_MAX+1] = {};
+ const char *canonical = NULL;
size_t l, idx, ms;
char *r_name;
struct gaih_addrtuple *r_tuple, *r_tuple_prev = NULL;
struct address *addresses = NULL, *a;
unsigned n_addresses = 0, n;
+ uint32_t local_address_ipv4;
- memset(hn, 0, sizeof(hn));
- if (gethostname(hn, sizeof(hn)-1) < 0) {
- *errnop = errno;
- *h_errnop = NO_RECOVERY;
- return NSS_STATUS_UNAVAIL;
- }
+ if (strcasecmp(name, "localhost") == 0) {
+ /* We respond to 'localhost', so that /etc/hosts
+ * is optional */
- if (strcasecmp(name, hn) != 0) {
- *errnop = ENOENT;
- *h_errnop = HOST_NOT_FOUND;
- return NSS_STATUS_NOTFOUND;
- }
+ canonical = "localhost";
+ local_address_ipv4 = htonl(INADDR_LOOPBACK);
+ } else {
+ /* We respond to our local host name */
- /* If this fails, n_addresses is 0. Which is fine */
- ifconf_acquire_addresses(&addresses, &n_addresses);
+ if (gethostname(hn, sizeof(hn)-1) < 0) {
+ *errnop = errno;
+ *h_errnop = NO_RECOVERY;
+ return NSS_STATUS_UNAVAIL;
+ }
+
+ if (strcasecmp(name, hn) != 0) {
+ *errnop = ENOENT;
+ *h_errnop = HOST_NOT_FOUND;
+ return NSS_STATUS_NOTFOUND;
+ }
+
+ /* If this fails, n_addresses is 0. Which is fine */
+ ifconf_acquire_addresses(&addresses, &n_addresses);
+
+ canonical = hn;
+ local_address_ipv4 = LOCALADDRESS_IPV4;
+ }
/* If this call fails we fill in 0 as scope. Which is fine */
- lo_ifi = if_nametoindex(LOOPBACK_INTERFACE);
+ lo_ifi = n_addresses <= 0 ? if_nametoindex(LOOPBACK_INTERFACE) : 0;
- l = strlen(hn);
+ l = strlen(canonical);
ms = ALIGN(l+1)+ALIGN(sizeof(struct gaih_addrtuple))*(n_addresses > 0 ? n_addresses : 2);
if (buflen < ms) {
*errnop = ENOMEM;
@@ -133,7 +150,7 @@ enum nss_status _nss_myhostname_gethostbyname4_r(
/* First, fill in hostname */
r_name = buffer;
- memcpy(r_name, hn, l+1);
+ memcpy(r_name, canonical, l+1);
idx = ALIGN(l+1);
if (n_addresses <= 0) {
@@ -153,7 +170,7 @@ enum nss_status _nss_myhostname_gethostbyname4_r(
r_tuple->next = r_tuple_prev;
r_tuple->name = r_name;
r_tuple->family = AF_INET;
- *(uint32_t*) r_tuple->addr = LOCALADDRESS_IPV4;
+ *(uint32_t*) r_tuple->addr = local_address_ipv4;
r_tuple->scopeid = (uint32_t) lo_ifi;
idx += ALIGN(sizeof(struct gaih_addrtuple));
@@ -176,7 +193,11 @@ enum nss_status _nss_myhostname_gethostbyname4_r(
/* Verify the size matches */
assert(idx == ms);
- *pat = r_tuple_prev;
+ /* Nscd expects us to store the first record in **pat. */
+ if (*pat)
+ **pat = *r_tuple_prev;
+ else
+ *pat = r_tuple_prev;
if (ttlp)
*ttlp = 0;
@@ -187,31 +208,34 @@ enum nss_status _nss_myhostname_gethostbyname4_r(
}
static enum nss_status fill_in_hostent(
- const char *hn,
+ const char *canonical, const char *additional,
int af,
+ struct address *addresses, unsigned n_addresses,
+ uint32_t local_address_ipv4,
struct hostent *result,
char *buffer, size_t buflen,
int *errnop, int *h_errnop,
int32_t *ttlp,
char **canonp) {
- size_t l, idx, ms;
- char *r_addr, *r_name, *r_aliases, *r_addr_list;
+ size_t l_canonical, l_additional, idx, ms;
+ char *r_addr, *r_name, *r_aliases, *r_alias = NULL, *r_addr_list;
size_t alen;
- struct address *addresses = NULL, *a;
- unsigned n_addresses = 0, n, c;
+ struct address *a;
+ unsigned n, c;
alen = PROTO_ADDRESS_SIZE(af);
- ifconf_acquire_addresses(&addresses, &n_addresses);
-
for (a = addresses, n = 0, c = 0; n < n_addresses; a++, n++)
if (af == a->family)
c++;
- l = strlen(hn);
- ms = ALIGN(l+1)+
+ l_canonical = strlen(canonical);
+ l_additional = additional ? strlen(additional) : 0;
+ ms = ALIGN(l_canonical+1)+
+ (additional ? ALIGN(l_additional+1) : 0) +
sizeof(char*)+
+ (additional ? sizeof(char*) : 0) +
(c > 0 ? c : 1)*ALIGN(alen)+
(c > 0 ? c+1 : 2)*sizeof(char*);
@@ -222,15 +246,27 @@ static enum nss_status fill_in_hostent(
return NSS_STATUS_TRYAGAIN;
}
- /* First, fill in hostname */
+ /* First, fill in hostnames */
r_name = buffer;
- memcpy(r_name, hn, l+1);
- idx = ALIGN(l+1);
+ memcpy(r_name, canonical, l_canonical+1);
+ idx = ALIGN(l_canonical+1);
+
+ if (additional) {
+ r_alias = buffer + idx;
+ memcpy(r_alias, additional, l_additional+1);
+ idx += ALIGN(l_additional+1);
+ }
- /* Second, create (empty) aliases array */
+ /* Second, create aliases array */
r_aliases = buffer + idx;
- *(char**) r_aliases = NULL;
- idx += sizeof(char*);
+ if (additional) {
+ ((char**) r_aliases)[0] = r_alias;
+ ((char**) r_aliases)[1] = NULL;
+ idx += 2*sizeof(char*);
+ } else {
+ ((char**) r_aliases)[0] = NULL;
+ idx += sizeof(char*);
+ }
/* Third, add addresses */
r_addr = buffer + idx;
@@ -249,7 +285,7 @@ static enum nss_status fill_in_hostent(
idx += c*ALIGN(alen);
} else {
if (af == AF_INET)
- *(uint32_t*) r_addr = LOCALADDRESS_IPV4;
+ *(uint32_t*) r_addr = local_address_ipv4;
else
memcpy(r_addr, LOCALADDRESS_IPV6, 16);
@@ -308,7 +344,11 @@ enum nss_status _nss_myhostname_gethostbyname3_r(
int32_t *ttlp,
char **canonp) {
- char hn[HOST_NAME_MAX+1];
+ char hn[HOST_NAME_MAX+1] = {};
+ struct address *addresses = NULL;
+ unsigned n_addresses = 0;
+ const char *canonical, *additional = NULL;
+ uint32_t local_address_ipv4;
if (af == AF_UNSPEC)
af = AF_INET;
@@ -319,20 +359,39 @@ enum nss_status _nss_myhostname_gethostbyname3_r(
return NSS_STATUS_UNAVAIL;
}
- memset(hn, 0, sizeof(hn));
- if (gethostname(hn, sizeof(hn)-1) < 0) {
- *errnop = errno;
- *h_errnop = NO_RECOVERY;
- return NSS_STATUS_UNAVAIL;
- }
+ if (strcasecmp(name, "localhost") == 0) {
+ canonical = "localhost";
+ local_address_ipv4 = htonl(INADDR_LOOPBACK);
+ } else {
+ if (gethostname(hn, sizeof(hn)-1) < 0) {
+ *errnop = errno;
+ *h_errnop = NO_RECOVERY;
+ return NSS_STATUS_UNAVAIL;
+ }
+
+ if (strcasecmp(name, hn) != 0) {
+ *errnop = ENOENT;
+ *h_errnop = HOST_NOT_FOUND;
+ return NSS_STATUS_NOTFOUND;
+ }
+
+ ifconf_acquire_addresses(&addresses, &n_addresses);
- if (strcasecmp(name, hn) != 0) {
- *errnop = ENOENT;
- *h_errnop = HOST_NOT_FOUND;
- return NSS_STATUS_NOTFOUND;
+ canonical = hn;
+ additional = n_addresses <= 0 && af == AF_INET6 ? "localhost" : NULL;
+ local_address_ipv4 = LOCALADDRESS_IPV4;
}
- return fill_in_hostent(hn, af, host, buffer, buflen, errnop, h_errnop, ttlp, canonp);
+ return fill_in_hostent(
+ canonical, additional,
+ af,
+ addresses, n_addresses,
+ local_address_ipv4,
+ host,
+ buffer, buflen,
+ errnop, h_errnop,
+ ttlp,
+ canonp);
}
enum nss_status _nss_myhostname_gethostbyname2_r(
@@ -376,9 +435,12 @@ enum nss_status _nss_myhostname_gethostbyaddr2_r(
int *errnop, int *h_errnop,
int32_t *ttlp) {
- char hn[HOST_NAME_MAX+1];
- struct address *addresses = NULL, *a;
+ char hn[HOST_NAME_MAX+1] = {};
+ struct address *addresses = NULL;
+ struct address *a;
unsigned n_addresses = 0, n;
+ uint32_t local_address_ipv4 = LOCALADDRESS_IPV4;
+ const char *canonical = NULL, *additional = NULL;
if (len != PROTO_ADDRESS_SIZE(af)) {
*errnop = EINVAL;
@@ -391,10 +453,18 @@ enum nss_status _nss_myhostname_gethostbyaddr2_r(
if ((*(uint32_t*) addr) == LOCALADDRESS_IPV4)
goto found;
+ if ((*(uint32_t*) addr) == htonl(INADDR_LOOPBACK)) {
+ canonical = "localhost";
+ local_address_ipv4 = htonl(INADDR_LOOPBACK);
+ goto found;
+ }
+
} else if (af == AF_INET6) {
- if (memcmp(addr, LOCALADDRESS_IPV6, 16) == 0)
+ if (memcmp(addr, LOCALADDRESS_IPV6, 16) == 0) {
+ additional = "localhost";
goto found;
+ }
} else {
*errnop = EAFNOSUPPORT;
@@ -416,20 +486,33 @@ enum nss_status _nss_myhostname_gethostbyaddr2_r(
*h_errnop = HOST_NOT_FOUND;
free(addresses);
+
return NSS_STATUS_NOTFOUND;
found:
- free(addresses);
+ if (!canonical) {
+ if (gethostname(hn, sizeof(hn)-1) < 0) {
+ *errnop = errno;
+ *h_errnop = NO_RECOVERY;
- memset(hn, 0, sizeof(hn));
- if (gethostname(hn, sizeof(hn)-1) < 0) {
- *errnop = errno;
- *h_errnop = NO_RECOVERY;
+ free(addresses);
- return NSS_STATUS_UNAVAIL;
+ return NSS_STATUS_UNAVAIL;
+ }
+
+ canonical = hn;
}
- return fill_in_hostent(hn, af, host, buffer, buflen, errnop, h_errnop, ttlp, NULL);
+ return fill_in_hostent(
+ canonical, additional,
+ af,
+ addresses, n_addresses,
+ local_address_ipv4,
+ host,
+ buffer, buflen,
+ errnop, h_errnop,
+ ttlp,
+ NULL);
}
diff --git a/src/python-systemd/.gitignore b/src/python-systemd/.gitignore
new file mode 100644
index 0000000000..4124b7affd
--- /dev/null
+++ b/src/python-systemd/.gitignore
@@ -0,0 +1,2 @@
+/id128-constants.h
+*.py[oc]
diff --git a/src/python-systemd/_daemon.c b/src/python-systemd/_daemon.c
new file mode 100644
index 0000000000..d3b4807368
--- /dev/null
+++ b/src/python-systemd/_daemon.c
@@ -0,0 +1,325 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Zbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
+
+ 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 <http://www.gnu.org/licenses/>.
+***/
+
+#define PY_SSIZE_T_CLEAN
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wredundant-decls"
+#include <Python.h>
+#pragma GCC diagnostic pop
+
+#include <stdbool.h>
+#include <assert.h>
+#include <sys/socket.h>
+
+#include <systemd/sd-daemon.h>
+#include "pyutil.h"
+
+PyDoc_STRVAR(module__doc__,
+ "Python interface to the libsystemd-daemon library.\n\n"
+ "Provides _listen_fds, notify, booted, and is_* functions\n"
+ "which wrap sd_listen_fds, sd_notify, sd_booted, sd_is_* and\n"
+ "useful for socket activation and checking if the system is\n"
+ "running under systemd."
+);
+
+static PyObject* set_error(int r, const char* invalid_message) {
+ assert (r < 0);
+
+ if (r == -EINVAL && invalid_message)
+ PyErr_SetString(PyExc_ValueError, invalid_message);
+ else if (r == -ENOMEM)
+ PyErr_SetString(PyExc_MemoryError, "Not enough memory");
+ else {
+ errno = -r;
+ PyErr_SetFromErrno(PyExc_OSError);
+ }
+
+ return NULL;
+}
+
+
+#if PY_MAJOR_VERSION >=3 && PY_MINOR_VERSION >= 1
+static int Unicode_FSConverter(PyObject* obj, void *_result) {
+ PyObject **result = _result;
+
+ assert(result);
+
+ if (!obj)
+ /* cleanup: we don't return Py_CLEANUP_SUPPORTED, so
+ * we can assume that it was PyUnicode_FSConverter. */
+ return PyUnicode_FSConverter(obj, result);
+
+ if (obj == Py_None) {
+ *result = NULL;
+ return 1;
+ }
+
+ return PyUnicode_FSConverter(obj, result);
+}
+#endif
+
+
+PyDoc_STRVAR(booted__doc__,
+ "booted() -> bool\n\n"
+ "Return True iff this system is running under systemd.\n"
+ "Wraps sd_daemon_booted(3)."
+);
+
+static PyObject* booted(PyObject *self, PyObject *args) {
+ int r;
+ assert(args == NULL);
+
+ r = sd_booted();
+ if (r < 0)
+ return set_error(r, NULL);
+
+ return PyBool_FromLong(r);
+}
+
+
+PyDoc_STRVAR(listen_fds__doc__,
+ "_listen_fds(unset_environment=True) -> int\n\n"
+ "Return the number of descriptors passed to this process by the init system\n"
+ "as part of the socket-based activation logic.\n"
+ "Wraps sd_listen_fds(3)."
+);
+
+static PyObject* listen_fds(PyObject *self, PyObject *args) {
+ int r;
+ int unset = true;
+
+#if PY_MAJOR_VERSION >=3 && PY_MINOR_VERSION >= 3
+ if (!PyArg_ParseTuple(args, "|p:_listen_fds", &unset))
+ return NULL;
+#else
+ PyObject *obj = NULL;
+ if (!PyArg_ParseTuple(args, "|O:_listen_fds", &obj))
+ return NULL;
+ if (obj != NULL)
+ unset = PyObject_IsTrue(obj);
+ if (unset < 0)
+ return NULL;
+#endif
+
+ r = sd_listen_fds(unset);
+ if (r < 0)
+ return set_error(r, NULL);
+
+ return long_FromLong(r);
+}
+
+PyDoc_STRVAR(is_fifo__doc__,
+ "_is_fifo(fd, path) -> bool\n\n"
+ "Returns True iff the descriptor refers to a FIFO or a pipe.\n"
+ "Wraps sd_is_fifo(3)."
+);
+
+
+static PyObject* is_fifo(PyObject *self, PyObject *args) {
+ int r;
+ int fd;
+ const char *path = NULL;
+
+#if PY_MAJOR_VERSION >=3 && PY_MINOR_VERSION >= 1
+ if (!PyArg_ParseTuple(args, "i|O&:_is_fifo",
+ &fd, Unicode_FSConverter, &path))
+ return NULL;
+#else
+ if (!PyArg_ParseTuple(args, "i|z:_is_fifo", &fd, &path))
+ return NULL;
+#endif
+
+ r = sd_is_fifo(fd, path);
+ if (r < 0)
+ return set_error(r, NULL);
+
+ return PyBool_FromLong(r);
+}
+
+
+PyDoc_STRVAR(is_mq__doc__,
+ "_is_mq(fd, path) -> bool\n\n"
+ "Returns True iff the descriptor refers to a POSIX message queue.\n"
+ "Wraps sd_is_mq(3)."
+);
+
+static PyObject* is_mq(PyObject *self, PyObject *args) {
+ int r;
+ int fd;
+ const char *path = NULL;
+
+#if PY_MAJOR_VERSION >=3 && PY_MINOR_VERSION >= 1
+ if (!PyArg_ParseTuple(args, "i|O&:_is_mq",
+ &fd, Unicode_FSConverter, &path))
+ return NULL;
+#else
+ if (!PyArg_ParseTuple(args, "i|z:_is_mq", &fd, &path))
+ return NULL;
+#endif
+
+ r = sd_is_mq(fd, path);
+ if (r < 0)
+ return set_error(r, NULL);
+
+ return PyBool_FromLong(r);
+}
+
+
+
+PyDoc_STRVAR(is_socket__doc__,
+ "_is_socket(fd, family=AF_UNSPEC, type=0, listening=-1) -> bool\n\n"
+ "Returns True iff the descriptor refers to a socket.\n"
+ "Wraps sd_is_socket(3).\n\n"
+ "Constants for `family` are defined in the socket module."
+);
+
+static PyObject* is_socket(PyObject *self, PyObject *args) {
+ int r;
+ int fd, family = AF_UNSPEC, type = 0, listening = -1;
+
+ if (!PyArg_ParseTuple(args, "i|iii:_is_socket",
+ &fd, &family, &type, &listening))
+ return NULL;
+
+ r = sd_is_socket(fd, family, type, listening);
+ if (r < 0)
+ return set_error(r, NULL);
+
+ return PyBool_FromLong(r);
+}
+
+
+PyDoc_STRVAR(is_socket_inet__doc__,
+ "_is_socket_inet(fd, family=AF_UNSPEC, type=0, listening=-1, port=0) -> bool\n\n"
+ "Wraps sd_is_socket_inet(3).\n\n"
+ "Constants for `family` are defined in the socket module."
+);
+
+static PyObject* is_socket_inet(PyObject *self, PyObject *args) {
+ int r;
+ int fd, family = AF_UNSPEC, type = 0, listening = -1, port = 0;
+
+ if (!PyArg_ParseTuple(args, "i|iiii:_is_socket_inet",
+ &fd, &family, &type, &listening, &port))
+ return NULL;
+
+ if (port < 0 || port > INT16_MAX)
+ return set_error(-EINVAL, "port must fit into uint16_t");
+
+ r = sd_is_socket_inet(fd, family, type, listening, (uint16_t) port);
+ if (r < 0)
+ return set_error(r, NULL);
+
+ return PyBool_FromLong(r);
+}
+
+
+PyDoc_STRVAR(is_socket_unix__doc__,
+ "_is_socket_unix(fd, type, listening, path) -> bool\n\n"
+ "Wraps sd_is_socket_unix(3)."
+);
+
+static PyObject* is_socket_unix(PyObject *self, PyObject *args) {
+ int r;
+ int fd, type = 0, listening = -1;
+ char* path = NULL;
+ Py_ssize_t length = 0;
+
+#if PY_MAJOR_VERSION >=3 && PY_MINOR_VERSION >= 1
+ _cleanup_Py_DECREF_ PyObject *_path = NULL;
+ if (!PyArg_ParseTuple(args, "i|iiO&:_is_socket_unix",
+ &fd, &type, &listening, Unicode_FSConverter, &_path))
+ return NULL;
+ if (_path) {
+ assert(PyBytes_Check(_path));
+ if (PyBytes_AsStringAndSize(_path, &path, &length))
+ return NULL;
+ }
+#else
+ if (!PyArg_ParseTuple(args, "i|iiz#:_is_socket_unix",
+ &fd, &type, &listening, &path, &length))
+ return NULL;
+#endif
+
+ r = sd_is_socket_unix(fd, type, listening, path, length);
+ if (r < 0)
+ return set_error(r, NULL);
+
+ return PyBool_FromLong(r);
+}
+
+
+static PyMethodDef methods[] = {
+ { "booted", booted, METH_NOARGS, booted__doc__},
+ { "_listen_fds", listen_fds, METH_VARARGS, listen_fds__doc__},
+ { "_is_fifo", is_fifo, METH_VARARGS, is_fifo__doc__},
+ { "_is_mq", is_mq, METH_VARARGS, is_mq__doc__},
+ { "_is_socket", is_socket, METH_VARARGS, is_socket__doc__},
+ { "_is_socket_inet", is_socket_inet, METH_VARARGS, is_socket_inet__doc__},
+ { "_is_socket_unix", is_socket_unix, METH_VARARGS, is_socket_unix__doc__},
+ { NULL, NULL, 0, NULL } /* Sentinel */
+};
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wmissing-prototypes"
+
+#if PY_MAJOR_VERSION < 3
+
+PyMODINIT_FUNC init_daemon(void) {
+ PyObject *m;
+
+ m = Py_InitModule3("_daemon", methods, module__doc__);
+ if (m == NULL)
+ return;
+
+ PyModule_AddIntConstant(m, "LISTEN_FDS_START", SD_LISTEN_FDS_START);
+ PyModule_AddStringConstant(m, "__version__", PACKAGE_VERSION);
+}
+
+#else
+
+static struct PyModuleDef module = {
+ PyModuleDef_HEAD_INIT,
+ "_daemon", /* name of module */
+ module__doc__, /* module documentation, may be NULL */
+ 0, /* size of per-interpreter state of the module */
+ methods
+};
+
+PyMODINIT_FUNC PyInit__daemon(void) {
+ PyObject *m;
+
+ m = PyModule_Create(&module);
+ if (m == NULL)
+ return NULL;
+
+ if (PyModule_AddIntConstant(m, "LISTEN_FDS_START", SD_LISTEN_FDS_START) ||
+ PyModule_AddStringConstant(m, "__version__", PACKAGE_VERSION)) {
+ Py_DECREF(m);
+ return NULL;
+ }
+
+ return m;
+}
+
+#endif
+
+#pragma GCC diagnostic pop
diff --git a/src/python-systemd/_journal.c b/src/python-systemd/_journal.c
index 0bdf709aea..f8e0b4f7f2 100644
--- a/src/python-systemd/_journal.c
+++ b/src/python-systemd/_journal.c
@@ -3,7 +3,7 @@
/***
This file is part of systemd.
- Copyright 2012 David Strauss
+ Copyright 2012 David Strauss <david@davidstrauss.net>
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
@@ -119,7 +119,13 @@ static PyMethodDef methods[] = {
#if PY_MAJOR_VERSION < 3
PyMODINIT_FUNC init_journal(void) {
- (void) Py_InitModule("_journal", methods);
+ PyObject *m;
+
+ m = Py_InitModule("_journal", methods);
+ if (m == NULL)
+ return;
+
+ PyModule_AddStringConstant(m, "__version__", PACKAGE_VERSION);
}
#else
@@ -128,12 +134,23 @@ static struct PyModuleDef module = {
PyModuleDef_HEAD_INIT,
"_journal", /* name of module */
NULL, /* module documentation, may be NULL */
- 0, /* size of per-interpreter state of the module */
+ -1, /* size of per-interpreter state of the module */
methods
};
PyMODINIT_FUNC PyInit__journal(void) {
- return PyModule_Create(&module);
+ PyObject *m;
+
+ m = PyModule_Create(&module);
+ if (m == NULL)
+ return NULL;
+
+ if (PyModule_AddStringConstant(m, "__version__", PACKAGE_VERSION)) {
+ Py_DECREF(m);
+ return NULL;
+ }
+
+ return m;
}
#endif
diff --git a/src/python-systemd/_reader.c b/src/python-systemd/_reader.c
new file mode 100644
index 0000000000..d20c58d2a8
--- /dev/null
+++ b/src/python-systemd/_reader.c
@@ -0,0 +1,1131 @@
+/*-*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Steven Hiscocks, 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 <http://www.gnu.org/licenses/>.
+***/
+
+#include <Python.h>
+#include <structmember.h>
+#include <datetime.h>
+#include <time.h>
+#include <stdio.h>
+
+#include <systemd/sd-journal.h>
+
+#include "pyutil.h"
+#include "macro.h"
+#include "util.h"
+#include "build.h"
+
+typedef struct {
+ PyObject_HEAD
+ sd_journal *j;
+} Reader;
+static PyTypeObject ReaderType;
+
+static int set_error(int r, const char* path, const char* invalid_message) {
+ if (r >= 0)
+ return r;
+ if (r == -EINVAL && invalid_message)
+ PyErr_SetString(PyExc_ValueError, invalid_message);
+ else if (r == -ENOMEM)
+ PyErr_SetString(PyExc_MemoryError, "Not enough memory");
+ else {
+ errno = -r;
+ PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
+ }
+ return -1;
+}
+
+
+PyDoc_STRVAR(module__doc__,
+ "Class to reads the systemd journal similar to journalctl.");
+
+
+#if PY_MAJOR_VERSION >= 3
+static PyTypeObject MonotonicType;
+
+PyDoc_STRVAR(MonotonicType__doc__,
+ "A tuple of (timestamp, bootid) for holding monotonic timestamps");
+
+static PyStructSequence_Field MonotonicType_fields[] = {
+ {(char*) "timestamp", (char*) "Time"},
+ {(char*) "bootid", (char*) "Unique identifier of the boot"},
+ {} /* Sentinel */
+};
+
+static PyStructSequence_Desc Monotonic_desc = {
+ (char*) "journal.Monotonic",
+ MonotonicType__doc__,
+ MonotonicType_fields,
+ 2,
+};
+#endif
+
+
+static void Reader_dealloc(Reader* self)
+{
+ sd_journal_close(self->j);
+ Py_TYPE(self)->tp_free((PyObject*)self);
+}
+
+PyDoc_STRVAR(Reader__doc__,
+ "_Reader([flags | path]) -> ...\n\n"
+ "_Reader allows filtering and retrieval of Journal entries.\n"
+ "Note: this is a low-level interface, and probably not what you\n"
+ "want, use systemd.journal.Reader instead.\n\n"
+ "Argument `flags` sets open flags of the journal, which can be one\n"
+ "of, or ORed combination of constants: LOCAL_ONLY (default) opens\n"
+ "journal on local machine only; RUNTIME_ONLY opens only\n"
+ "volatile journal files; and SYSTEM_ONLY opens only\n"
+ "journal files of system services and the kernel.\n\n"
+ "Argument `path` is the directory of journal files. Note that\n"
+ "`flags` and `path` are exclusive.\n\n"
+ "_Reader implements the context manager protocol: the journal\n"
+ "will be closed when exiting the block.");
+static int Reader_init(Reader *self, PyObject *args, PyObject *keywds)
+{
+ int flags = 0, r;
+ char *path = NULL;
+
+ static const char* const kwlist[] = {"flags", "path", NULL};
+ if (!PyArg_ParseTupleAndKeywords(args, keywds, "|iz", (char**) kwlist,
+ &flags, &path))
+ return -1;
+
+ if (!flags)
+ flags = SD_JOURNAL_LOCAL_ONLY;
+ else
+ if (path) {
+ PyErr_SetString(PyExc_ValueError, "cannot use both flags and path");
+ return -1;
+ }
+
+ Py_BEGIN_ALLOW_THREADS
+ if (path)
+ r = sd_journal_open_directory(&self->j, path, 0);
+ else
+ r = sd_journal_open(&self->j, flags);
+ Py_END_ALLOW_THREADS
+
+ return set_error(r, path, "Invalid flags or path");
+}
+
+
+PyDoc_STRVAR(Reader_fileno__doc__,
+ "fileno() -> int\n\n"
+ "Get a file descriptor to poll for changes in the journal.\n"
+ "This method invokes sd_journal_get_fd().\n"
+ "See man:sd_journal_get_fd(3).");
+static PyObject* Reader_fileno(Reader *self, PyObject *args)
+{
+ int fd = sd_journal_get_fd(self->j);
+ set_error(fd, NULL, NULL);
+ if (fd < 0)
+ return NULL;
+ return long_FromLong(fd);
+}
+
+
+PyDoc_STRVAR(Reader_reliable_fd__doc__,
+ "reliable_fd() -> bool\n\n"
+ "Returns True iff the journal can be polled reliably.\n"
+ "This method invokes sd_journal_reliable_fd().\n"
+ "See man:sd_journal_reliable_fd(3).");
+static PyObject* Reader_reliable_fd(Reader *self, PyObject *args)
+{
+ int r = sd_journal_reliable_fd(self->j);
+ set_error(r, NULL, NULL);
+ if (r < 0)
+ return NULL;
+ return PyBool_FromLong(r);
+}
+
+
+PyDoc_STRVAR(Reader_get_events__doc__,
+ "get_events() -> int\n\n"
+ "Returns a mask of poll() events to wait for on the file\n"
+ "descriptor returned by .fileno().\n\n"
+ "See man:sd_journal_get_events(3) for further discussion.");
+static PyObject* Reader_get_events(Reader *self, PyObject *args)
+{
+ int r = sd_journal_get_events(self->j);
+ set_error(r, NULL, NULL);
+ if (r < 0)
+ return NULL;
+ return long_FromLong(r);
+}
+
+
+PyDoc_STRVAR(Reader_get_timeout__doc__,
+ "get_timeout() -> int or None\n\n"
+ "Returns a timeout value for usage in poll(), the time since the\n"
+ "epoch of clock_gettime(2) in microseconds, or None if no timeout\n"
+ "is necessary.\n\n"
+ "The return value must be converted to a relative timeout in \n"
+ "milliseconds if it is to be used as an argument for poll().\n"
+ "See man:sd_journal_get_timeout(3) for further discussion.");
+static PyObject* Reader_get_timeout(Reader *self, PyObject *args)
+{
+ int r;
+ uint64_t t;
+
+ r = sd_journal_get_timeout(self->j, &t);
+ set_error(r, NULL, NULL);
+ if (r < 0)
+ return NULL;
+
+ if (t == (uint64_t) -1)
+ Py_RETURN_NONE;
+
+ assert_cc(sizeof(unsigned long long) == sizeof(t));
+ return PyLong_FromUnsignedLongLong(t);
+}
+
+
+PyDoc_STRVAR(Reader_get_timeout_ms__doc__,
+ "get_timeout_ms() -> int\n\n"
+ "Returns a timeout value suitable for usage in poll(), the value\n"
+ "returned by .get_timeout() converted to relative ms, or -1 if\n"
+ "no timeout is necessary.");
+static PyObject* Reader_get_timeout_ms(Reader *self, PyObject *args)
+{
+ int r;
+ uint64_t t;
+
+ r = sd_journal_get_timeout(self->j, &t);
+ set_error(r, NULL, NULL);
+ if (r < 0)
+ return NULL;
+
+ return absolute_timeout(t);
+}
+
+
+PyDoc_STRVAR(Reader_close__doc__,
+ "close() -> None\n\n"
+ "Free resources allocated by this Reader object.\n"
+ "This method invokes sd_journal_close().\n"
+ "See man:sd_journal_close(3).");
+static PyObject* Reader_close(Reader *self, PyObject *args)
+{
+ assert(self);
+ assert(!args);
+
+ sd_journal_close(self->j);
+ self->j = NULL;
+ Py_RETURN_NONE;
+}
+
+
+PyDoc_STRVAR(Reader_get_usage__doc__,
+ "get_usage() -> int\n\n"
+ "Returns the total disk space currently used by journal\n"
+ "files (in bytes). If `SD_JOURNAL_LOCAL_ONLY` was\n"
+ "passed when opening the journal this value will only reflect\n"
+ "the size of journal files of the local host, otherwise\n"
+ "of all hosts.\n\n"
+ "This method invokes sd_journal_get_usage().\n"
+ "See man:sd_journal_get_usage(3).");
+static PyObject* Reader_get_usage(Reader *self, PyObject *args)
+{
+ int r;
+ uint64_t bytes;
+
+ r = sd_journal_get_usage(self->j, &bytes);
+ if (set_error(r, NULL, NULL))
+ return NULL;
+
+ assert_cc(sizeof(unsigned long long) == sizeof(bytes));
+ return PyLong_FromUnsignedLongLong(bytes);
+}
+
+
+PyDoc_STRVAR(Reader___enter____doc__,
+ "__enter__() -> self\n\n"
+ "Part of the context manager protocol.\n"
+ "Returns self.\n");
+static PyObject* Reader___enter__(PyObject *self, PyObject *args)
+{
+ assert(self);
+ assert(!args);
+
+ Py_INCREF(self);
+ return self;
+}
+
+PyDoc_STRVAR(Reader___exit____doc__,
+ "__exit__(type, value, traceback) -> None\n\n"
+ "Part of the context manager protocol.\n"
+ "Closes the journal.\n");
+static PyObject* Reader___exit__(Reader *self, PyObject *args)
+{
+ assert(self);
+
+ sd_journal_close(self->j);
+ self->j = NULL;
+ Py_RETURN_NONE;
+}
+
+
+PyDoc_STRVAR(Reader_next__doc__,
+ "next([skip]) -> bool\n\n"
+ "Go to the next log entry. Optional skip value means to go to\n"
+ "the `skip`\\-th log entry.\n"
+ "Returns False if at end of file, True otherwise.");
+static PyObject* Reader_next(Reader *self, PyObject *args)
+{
+ int64_t skip = 1LL;
+ int r;
+
+ if (!PyArg_ParseTuple(args, "|L:next", &skip))
+ return NULL;
+
+ if (skip == 0LL) {
+ PyErr_SetString(PyExc_ValueError, "skip must be nonzero");
+ return NULL;
+ }
+
+ Py_BEGIN_ALLOW_THREADS
+ if (skip == 1LL)
+ r = sd_journal_next(self->j);
+ else if (skip == -1LL)
+ r = sd_journal_previous(self->j);
+ else if (skip > 1LL)
+ r = sd_journal_next_skip(self->j, skip);
+ else if (skip < -1LL)
+ r = sd_journal_previous_skip(self->j, -skip);
+ else
+ assert_not_reached("should not be here");
+ Py_END_ALLOW_THREADS
+
+ set_error(r, NULL, NULL);
+ if (r < 0)
+ return NULL;
+ return PyBool_FromLong(r);
+}
+
+PyDoc_STRVAR(Reader_previous__doc__,
+ "previous([skip]) -> bool\n\n"
+ "Go to the previous log entry. Optional skip value means to \n"
+ "go to the `skip`\\-th previous log entry.\n"
+ "Returns False if at start of file, True otherwise.");
+static PyObject* Reader_previous(Reader *self, PyObject *args)
+{
+ int64_t skip = 1LL;
+ if (!PyArg_ParseTuple(args, "|L:previous", &skip))
+ return NULL;
+
+ return PyObject_CallMethod((PyObject *)self, (char*) "_next",
+ (char*) "L", -skip);
+}
+
+
+static int extract(const char* msg, size_t msg_len,
+ PyObject **key, PyObject **value) {
+ PyObject *k = NULL, *v;
+ const char *delim_ptr;
+
+ delim_ptr = memchr(msg, '=', msg_len);
+ if (!delim_ptr) {
+ PyErr_SetString(PyExc_OSError,
+ "journal gave us a field without '='");
+ return -1;
+ }
+
+ if (key) {
+ k = unicode_FromStringAndSize(msg, delim_ptr - (const char*) msg);
+ if (!k)
+ return -1;
+ }
+
+ if (value) {
+ v = PyBytes_FromStringAndSize(delim_ptr + 1,
+ (const char*) msg + msg_len - (delim_ptr + 1));
+ if (!v) {
+ Py_XDECREF(k);
+ return -1;
+ }
+
+ *value = v;
+ }
+
+ if (key)
+ *key = k;
+
+ return 0;
+}
+
+PyDoc_STRVAR(Reader_get__doc__,
+ "get(str) -> str\n\n"
+ "Return data associated with this key in current log entry.\n"
+ "Throws KeyError is the data is not available.");
+static PyObject* Reader_get(Reader *self, PyObject *args)
+{
+ const char* field;
+ const void* msg;
+ size_t msg_len;
+ PyObject *value;
+ int r;
+
+ assert(self);
+ assert(args);
+
+ if (!PyArg_ParseTuple(args, "s:get", &field))
+ return NULL;
+
+ r = sd_journal_get_data(self->j, field, &msg, &msg_len);
+ if (r == -ENOENT) {
+ PyErr_SetString(PyExc_KeyError, field);
+ return NULL;
+ } else if (set_error(r, NULL, "field name is not valid"))
+ return NULL;
+
+ r = extract(msg, msg_len, NULL, &value);
+ if (r < 0)
+ return NULL;
+ return value;
+}
+
+
+PyDoc_STRVAR(Reader_get_all__doc__,
+ "_get_all() -> dict\n\n"
+ "Return dictionary of the current log entry.");
+static PyObject* Reader_get_all(Reader *self, PyObject *args)
+{
+ PyObject *dict;
+ const void *msg;
+ size_t msg_len;
+ int r;
+
+ dict = PyDict_New();
+ if (!dict)
+ return NULL;
+
+ SD_JOURNAL_FOREACH_DATA(self->j, msg, msg_len) {
+ _cleanup_Py_DECREF_ PyObject *key = NULL, *value = NULL;
+
+ r = extract(msg, msg_len, &key, &value);
+ if (r < 0)
+ goto error;
+
+ if (PyDict_Contains(dict, key)) {
+ PyObject *cur_value = PyDict_GetItem(dict, key);
+
+ if (PyList_CheckExact(cur_value)) {
+ r = PyList_Append(cur_value, value);
+ if (r < 0)
+ goto error;
+ } else {
+ _cleanup_Py_DECREF_ PyObject *tmp_list = PyList_New(0);
+ if (!tmp_list)
+ goto error;
+
+ r = PyList_Append(tmp_list, cur_value);
+ if (r < 0)
+ goto error;
+
+ r = PyList_Append(tmp_list, value);
+ if (r < 0)
+ goto error;
+
+ r = PyDict_SetItem(dict, key, tmp_list);
+ if (r < 0)
+ goto error;
+ }
+ } else {
+ r = PyDict_SetItem(dict, key, value);
+ if (r < 0)
+ goto error;
+ }
+ }
+
+ return dict;
+
+error:
+ Py_DECREF(dict);
+ return NULL;
+}
+
+
+PyDoc_STRVAR(Reader_get_realtime__doc__,
+ "get_realtime() -> int\n\n"
+ "Return the realtime timestamp for the current journal entry\n"
+ "in microseconds.\n\n"
+ "Wraps sd_journal_get_realtime_usec().\n"
+ "See man:sd_journal_get_realtime_usec(3).");
+static PyObject* Reader_get_realtime(Reader *self, PyObject *args)
+{
+ uint64_t timestamp;
+ int r;
+
+ assert(self);
+ assert(!args);
+
+ r = sd_journal_get_realtime_usec(self->j, &timestamp);
+ if (set_error(r, NULL, NULL))
+ return NULL;
+
+ assert_cc(sizeof(unsigned long long) == sizeof(timestamp));
+ return PyLong_FromUnsignedLongLong(timestamp);
+}
+
+
+PyDoc_STRVAR(Reader_get_monotonic__doc__,
+ "get_monotonic() -> (timestamp, bootid)\n\n"
+ "Return the monotonic timestamp for the current journal entry\n"
+ "as a tuple of time in microseconds and bootid.\n\n"
+ "Wraps sd_journal_get_monotonic_usec().\n"
+ "See man:sd_journal_get_monotonic_usec(3).");
+static PyObject* Reader_get_monotonic(Reader *self, PyObject *args)
+{
+ uint64_t timestamp;
+ sd_id128_t id;
+ PyObject *monotonic, *bootid, *tuple;
+ int r;
+
+ assert(self);
+ assert(!args);
+
+ r = sd_journal_get_monotonic_usec(self->j, &timestamp, &id);
+ if (set_error(r, NULL, NULL))
+ return NULL;
+
+ assert_cc(sizeof(unsigned long long) == sizeof(timestamp));
+ monotonic = PyLong_FromUnsignedLongLong(timestamp);
+ bootid = PyBytes_FromStringAndSize((const char*) &id.bytes, sizeof(id.bytes));
+#if PY_MAJOR_VERSION >= 3
+ tuple = PyStructSequence_New(&MonotonicType);
+#else
+ tuple = PyTuple_New(2);
+#endif
+ if (!monotonic || !bootid || !tuple) {
+ Py_XDECREF(monotonic);
+ Py_XDECREF(bootid);
+ Py_XDECREF(tuple);
+ return NULL;
+ }
+
+#if PY_MAJOR_VERSION >= 3
+ PyStructSequence_SET_ITEM(tuple, 0, monotonic);
+ PyStructSequence_SET_ITEM(tuple, 1, bootid);
+#else
+ PyTuple_SET_ITEM(tuple, 0, monotonic);
+ PyTuple_SET_ITEM(tuple, 1, bootid);
+#endif
+
+ return tuple;
+}
+
+PyDoc_STRVAR(Reader_add_match__doc__,
+ "add_match(match) -> None\n\n"
+ "Add a match to filter journal log entries. All matches of different\n"
+ "fields are combined with logical AND, and matches of the same field\n"
+ "are automatically combined with logical OR.\n"
+ "Match is a string of the form \"FIELD=value\".");
+static PyObject* Reader_add_match(Reader *self, PyObject *args, PyObject *keywds)
+{
+ char *match;
+ int match_len, r;
+ if (!PyArg_ParseTuple(args, "s#:add_match", &match, &match_len))
+ return NULL;
+
+ r = sd_journal_add_match(self->j, match, match_len);
+ set_error(r, NULL, "Invalid match");
+ if (r < 0)
+ return NULL;
+
+ Py_RETURN_NONE;
+}
+
+
+PyDoc_STRVAR(Reader_add_disjunction__doc__,
+ "add_disjunction() -> None\n\n"
+ "Inserts a logical OR between matches added since previous\n"
+ "add_disjunction() or add_conjunction() and the next\n"
+ "add_disjunction() or add_conjunction().\n\n"
+ "See man:sd_journal_add_disjunction(3) for explanation.");
+static PyObject* Reader_add_disjunction(Reader *self, PyObject *args)
+{
+ int r;
+ r = sd_journal_add_disjunction(self->j);
+ set_error(r, NULL, NULL);
+ if (r < 0)
+ return NULL;
+ Py_RETURN_NONE;
+}
+
+
+PyDoc_STRVAR(Reader_add_conjunction__doc__,
+ "add_conjunction() -> None\n\n"
+ "Inserts a logical AND between matches added since previous\n"
+ "add_disjunction() or add_conjunction() and the next\n"
+ "add_disjunction() or add_conjunction().\n\n"
+ "See man:sd_journal_add_disjunction(3) for explanation.");
+static PyObject* Reader_add_conjunction(Reader *self, PyObject *args)
+{
+ int r;
+ r = sd_journal_add_conjunction(self->j);
+ set_error(r, NULL, NULL);
+ if (r < 0)
+ return NULL;
+ Py_RETURN_NONE;
+}
+
+
+PyDoc_STRVAR(Reader_flush_matches__doc__,
+ "flush_matches() -> None\n\n"
+ "Clear all current match filters.");
+static PyObject* Reader_flush_matches(Reader *self, PyObject *args)
+{
+ sd_journal_flush_matches(self->j);
+ Py_RETURN_NONE;
+}
+
+
+PyDoc_STRVAR(Reader_seek_head__doc__,
+ "seek_head() -> None\n\n"
+ "Jump to the beginning of the journal.\n"
+ "This method invokes sd_journal_seek_head().\n"
+ "See man:sd_journal_seek_head(3).");
+static PyObject* Reader_seek_head(Reader *self, PyObject *args)
+{
+ int r;
+ Py_BEGIN_ALLOW_THREADS
+ r = sd_journal_seek_head(self->j);
+ Py_END_ALLOW_THREADS
+ if (set_error(r, NULL, NULL))
+ return NULL;
+ Py_RETURN_NONE;
+}
+
+
+PyDoc_STRVAR(Reader_seek_tail__doc__,
+ "seek_tail() -> None\n\n"
+ "Jump to the end of the journal.\n"
+ "This method invokes sd_journal_seek_tail().\n"
+ "See man:sd_journal_seek_tail(3).");
+static PyObject* Reader_seek_tail(Reader *self, PyObject *args)
+{
+ int r;
+ Py_BEGIN_ALLOW_THREADS
+ r = sd_journal_seek_tail(self->j);
+ Py_END_ALLOW_THREADS
+ if (set_error(r, NULL, NULL))
+ return NULL;
+ Py_RETURN_NONE;
+}
+
+
+PyDoc_STRVAR(Reader_seek_realtime__doc__,
+ "seek_realtime(realtime) -> None\n\n"
+ "Seek to nearest matching journal entry to `realtime`. Argument\n"
+ "`realtime` in specified in seconds.");
+static PyObject* Reader_seek_realtime(Reader *self, PyObject *args)
+{
+ uint64_t timestamp;
+ int r;
+
+ if (!PyArg_ParseTuple(args, "K:seek_realtime", &timestamp))
+ return NULL;
+
+ Py_BEGIN_ALLOW_THREADS
+ r = sd_journal_seek_realtime_usec(self->j, timestamp);
+ Py_END_ALLOW_THREADS
+ if (set_error(r, NULL, NULL))
+ return NULL;
+ Py_RETURN_NONE;
+}
+
+
+PyDoc_STRVAR(Reader_seek_monotonic__doc__,
+ "seek_monotonic(monotonic[, bootid]) -> None\n\n"
+ "Seek to nearest matching journal entry to `monotonic`. Argument\n"
+ "`monotonic` is an timestamp from boot in microseconds.\n"
+ "Argument `bootid` is a string representing which boot the\n"
+ "monotonic time is reference to. Defaults to current bootid.");
+static PyObject* Reader_seek_monotonic(Reader *self, PyObject *args)
+{
+ char *bootid = NULL;
+ uint64_t timestamp;
+ sd_id128_t id;
+ int r;
+
+ if (!PyArg_ParseTuple(args, "K|z:seek_monotonic", &timestamp, &bootid))
+ return NULL;
+
+ if (bootid) {
+ r = sd_id128_from_string(bootid, &id);
+ if (set_error(r, NULL, "Invalid bootid"))
+ return NULL;
+ } else {
+ Py_BEGIN_ALLOW_THREADS
+ r = sd_id128_get_boot(&id);
+ Py_END_ALLOW_THREADS
+ if (set_error(r, NULL, NULL))
+ return NULL;
+ }
+
+ Py_BEGIN_ALLOW_THREADS
+ r = sd_journal_seek_monotonic_usec(self->j, id, timestamp);
+ Py_END_ALLOW_THREADS
+ if (set_error(r, NULL, NULL))
+ return NULL;
+
+ Py_RETURN_NONE;
+}
+
+
+PyDoc_STRVAR(Reader_process__doc__,
+ "process() -> state change (integer)\n\n"
+ "Process events and reset the readable state of the file\n"
+ "descriptor returned by .fileno().\n\n"
+ "Will return constants: NOP if no change; APPEND if new\n"
+ "entries have been added to the end of the journal; and\n"
+ "INVALIDATE if journal files have been added or removed.\n\n"
+ "See man:sd_journal_process(3) for further discussion.");
+static PyObject* Reader_process(Reader *self, PyObject *args)
+{
+ int r;
+
+ assert(!args);
+
+ Py_BEGIN_ALLOW_THREADS
+ r = sd_journal_process(self->j);
+ Py_END_ALLOW_THREADS
+ if (set_error(r, NULL, NULL) < 0)
+ return NULL;
+
+ return long_FromLong(r);
+}
+
+
+PyDoc_STRVAR(Reader_wait__doc__,
+ "wait([timeout]) -> state change (integer)\n\n"
+ "Wait for a change in the journal. Argument `timeout` specifies\n"
+ "the maximum number of microseconds to wait before returning\n"
+ "regardless of wheter the journal has changed. If `timeout` is -1,\n"
+ "then block forever.\n\n"
+ "Will return constants: NOP if no change; APPEND if new\n"
+ "entries have been added to the end of the journal; and\n"
+ "INVALIDATE if journal files have been added or removed.\n\n"
+ "See man:sd_journal_wait(3) for further discussion.");
+static PyObject* Reader_wait(Reader *self, PyObject *args)
+{
+ int r;
+ int64_t timeout;
+
+ if (!PyArg_ParseTuple(args, "|L:wait", &timeout))
+ return NULL;
+
+ Py_BEGIN_ALLOW_THREADS
+ r = sd_journal_wait(self->j, timeout);
+ Py_END_ALLOW_THREADS
+ if (set_error(r, NULL, NULL) < 0)
+ return NULL;
+
+ return long_FromLong(r);
+}
+
+
+PyDoc_STRVAR(Reader_seek_cursor__doc__,
+ "seek_cursor(cursor) -> None\n\n"
+ "Seek to journal entry by given unique reference `cursor`.");
+static PyObject* Reader_seek_cursor(Reader *self, PyObject *args)
+{
+ const char *cursor;
+ int r;
+
+ if (!PyArg_ParseTuple(args, "s:seek_cursor", &cursor))
+ return NULL;
+
+ Py_BEGIN_ALLOW_THREADS
+ r = sd_journal_seek_cursor(self->j, cursor);
+ Py_END_ALLOW_THREADS
+ if (set_error(r, NULL, "Invalid cursor"))
+ return NULL;
+ Py_RETURN_NONE;
+}
+
+
+PyDoc_STRVAR(Reader_get_cursor__doc__,
+ "get_cursor() -> str\n\n"
+ "Return a cursor string for the current journal entry.\n\n"
+ "Wraps sd_journal_get_cursor(). See man:sd_journal_get_cursor(3).");
+static PyObject* Reader_get_cursor(Reader *self, PyObject *args)
+{
+ _cleanup_free_ char *cursor = NULL;
+ int r;
+
+ assert(self);
+ assert(!args);
+
+ r = sd_journal_get_cursor(self->j, &cursor);
+ if (set_error(r, NULL, NULL))
+ return NULL;
+
+ return unicode_FromString(cursor);
+}
+
+
+PyDoc_STRVAR(Reader_test_cursor__doc__,
+ "test_cursor(str) -> bool\n\n"
+ "Test whether the cursor string matches current journal entry.\n\n"
+ "Wraps sd_journal_test_cursor(). See man:sd_journal_test_cursor(3).");
+static PyObject* Reader_test_cursor(Reader *self, PyObject *args)
+{
+ const char *cursor;
+ int r;
+
+ assert(self);
+ assert(args);
+
+ if (!PyArg_ParseTuple(args, "s:test_cursor", &cursor))
+ return NULL;
+
+ r = sd_journal_test_cursor(self->j, cursor);
+ set_error(r, NULL, NULL);
+ if (r < 0)
+ return NULL;
+
+ return PyBool_FromLong(r);
+}
+
+PyDoc_STRVAR(Reader_query_unique__doc__,
+ "query_unique(field) -> a set of values\n\n"
+ "Return a set of unique values appearing in journal for the\n"
+ "given `field`. Note this does not respect any journal matches.");
+static PyObject* Reader_query_unique(Reader *self, PyObject *args)
+{
+ char *query;
+ int r;
+ const void *uniq;
+ size_t uniq_len;
+ PyObject *value_set, *key, *value;
+
+ if (!PyArg_ParseTuple(args, "s:query_unique", &query))
+ return NULL;
+
+ Py_BEGIN_ALLOW_THREADS
+ r = sd_journal_query_unique(self->j, query);
+ Py_END_ALLOW_THREADS
+ if (set_error(r, NULL, "Invalid field name"))
+ return NULL;
+
+ value_set = PySet_New(0);
+ key = unicode_FromString(query);
+
+ SD_JOURNAL_FOREACH_UNIQUE(self->j, uniq, uniq_len) {
+ const char *delim_ptr;
+
+ delim_ptr = memchr(uniq, '=', uniq_len);
+ value = PyBytes_FromStringAndSize(
+ delim_ptr + 1,
+ (const char*) uniq + uniq_len - (delim_ptr + 1));
+ PySet_Add(value_set, value);
+ Py_DECREF(value);
+ }
+ Py_DECREF(key);
+ return value_set;
+}
+
+
+PyDoc_STRVAR(Reader_get_catalog__doc__,
+ "get_catalog() -> str\n\n"
+ "Retrieve a message catalog entry for the current journal entry.\n"
+ "Will throw IndexError if the entry has no MESSAGE_ID\n"
+ "and KeyError is the id is specified, but hasn't been found\n"
+ "in the catalog.\n\n"
+ "Wraps man:sd_journal_get_catalog(3).");
+static PyObject* Reader_get_catalog(Reader *self, PyObject *args)
+{
+ int r;
+ _cleanup_free_ char *msg = NULL;
+
+ assert(self);
+ assert(!args);
+
+ Py_BEGIN_ALLOW_THREADS
+ r = sd_journal_get_catalog(self->j, &msg);
+ Py_END_ALLOW_THREADS
+ if (r == -ENOENT) {
+ const void* mid;
+ size_t mid_len;
+
+ r = sd_journal_get_data(self->j, "MESSAGE_ID", &mid, &mid_len);
+ if (r == 0) {
+ const int l = sizeof("MESSAGE_ID");
+ assert(mid_len > l);
+ PyErr_Format(PyExc_KeyError, "%.*s", (int) mid_len - l,
+ (const char*) mid + l);
+ } else if (r == -ENOENT)
+ PyErr_SetString(PyExc_IndexError, "no MESSAGE_ID field");
+ else
+ set_error(r, NULL, NULL);
+ return NULL;
+ } else if (set_error(r, NULL, NULL))
+ return NULL;
+
+ return unicode_FromString(msg);
+}
+
+
+PyDoc_STRVAR(get_catalog__doc__,
+ "get_catalog(id128) -> str\n\n"
+ "Retrieve a message catalog entry for the given id.\n"
+ "Wraps man:sd_journal_get_catalog_for_message_id(3).");
+static PyObject* get_catalog(PyObject *self, PyObject *args)
+{
+ int r;
+ char *id_ = NULL;
+ sd_id128_t id;
+ _cleanup_free_ char *msg = NULL;
+
+ assert(!self);
+ assert(args);
+
+ if (!PyArg_ParseTuple(args, "z:get_catalog", &id_))
+ return NULL;
+
+ r = sd_id128_from_string(id_, &id);
+ if (set_error(r, NULL, "Invalid id128"))
+ return NULL;
+
+ Py_BEGIN_ALLOW_THREADS
+ r = sd_journal_get_catalog_for_message_id(id, &msg);
+ Py_END_ALLOW_THREADS
+ if (set_error(r, NULL, NULL))
+ return NULL;
+
+ return unicode_FromString(msg);
+}
+
+
+PyDoc_STRVAR(data_threshold__doc__,
+ "Threshold for field size truncation in bytes.\n\n"
+ "Fields longer than this will be truncated to the threshold size.\n"
+ "Defaults to 64Kb.");
+
+static PyObject* Reader_get_data_threshold(Reader *self, void *closure)
+{
+ size_t cvalue;
+ int r;
+
+ r = sd_journal_get_data_threshold(self->j, &cvalue);
+ if (set_error(r, NULL, NULL))
+ return NULL;
+
+ return long_FromSize_t(cvalue);
+}
+
+static int Reader_set_data_threshold(Reader *self, PyObject *value, void *closure)
+{
+ int r;
+ if (value == NULL) {
+ PyErr_SetString(PyExc_AttributeError, "Cannot delete data threshold");
+ return -1;
+ }
+ if (!long_Check(value)){
+ PyErr_SetString(PyExc_TypeError, "Data threshold must be an int");
+ return -1;
+ }
+ r = sd_journal_set_data_threshold(self->j, (size_t) long_AsLong(value));
+ return set_error(r, NULL, NULL);
+}
+
+
+PyDoc_STRVAR(closed__doc__,
+ "True iff journal is closed");
+static PyObject* Reader_get_closed(Reader *self, void *closure)
+{
+ return PyBool_FromLong(self->j == NULL);
+}
+
+
+static PyGetSetDef Reader_getsetters[] = {
+ {(char*) "data_threshold",
+ (getter) Reader_get_data_threshold,
+ (setter) Reader_set_data_threshold,
+ (char*) data_threshold__doc__,
+ NULL},
+ {(char*) "closed",
+ (getter) Reader_get_closed,
+ NULL,
+ (char*) closed__doc__,
+ NULL},
+ {} /* Sentinel */
+};
+
+static PyMethodDef Reader_methods[] = {
+ {"fileno", (PyCFunction) Reader_fileno, METH_NOARGS, Reader_fileno__doc__},
+ {"reliable_fd", (PyCFunction) Reader_reliable_fd, METH_NOARGS, Reader_reliable_fd__doc__},
+ {"get_events", (PyCFunction) Reader_get_events, METH_NOARGS, Reader_get_events__doc__},
+ {"get_timeout", (PyCFunction) Reader_get_timeout, METH_NOARGS, Reader_get_timeout__doc__},
+ {"get_timeout_ms", (PyCFunction) Reader_get_timeout_ms, METH_NOARGS, Reader_get_timeout_ms__doc__},
+ {"close", (PyCFunction) Reader_close, METH_NOARGS, Reader_close__doc__},
+ {"get_usage", (PyCFunction) Reader_get_usage, METH_NOARGS, Reader_get_usage__doc__},
+ {"__enter__", (PyCFunction) Reader___enter__, METH_NOARGS, Reader___enter____doc__},
+ {"__exit__", (PyCFunction) Reader___exit__, METH_VARARGS, Reader___exit____doc__},
+ {"_next", (PyCFunction) Reader_next, METH_VARARGS, Reader_next__doc__},
+ {"_previous", (PyCFunction) Reader_previous, METH_VARARGS, Reader_previous__doc__},
+ {"_get", (PyCFunction) Reader_get, METH_VARARGS, Reader_get__doc__},
+ {"_get_all", (PyCFunction) Reader_get_all, METH_NOARGS, Reader_get_all__doc__},
+ {"_get_realtime", (PyCFunction) Reader_get_realtime, METH_NOARGS, Reader_get_realtime__doc__},
+ {"_get_monotonic", (PyCFunction) Reader_get_monotonic, METH_NOARGS, Reader_get_monotonic__doc__},
+ {"add_match", (PyCFunction) Reader_add_match, METH_VARARGS|METH_KEYWORDS, Reader_add_match__doc__},
+ {"add_disjunction", (PyCFunction) Reader_add_disjunction, METH_NOARGS, Reader_add_disjunction__doc__},
+ {"add_conjunction", (PyCFunction) Reader_add_conjunction, METH_NOARGS, Reader_add_conjunction__doc__},
+ {"flush_matches", (PyCFunction) Reader_flush_matches, METH_NOARGS, Reader_flush_matches__doc__},
+ {"seek_head", (PyCFunction) Reader_seek_head, METH_NOARGS, Reader_seek_head__doc__},
+ {"seek_tail", (PyCFunction) Reader_seek_tail, METH_NOARGS, Reader_seek_tail__doc__},
+ {"seek_realtime", (PyCFunction) Reader_seek_realtime, METH_VARARGS, Reader_seek_realtime__doc__},
+ {"seek_monotonic", (PyCFunction) Reader_seek_monotonic, METH_VARARGS, Reader_seek_monotonic__doc__},
+ {"process", (PyCFunction) Reader_process, METH_NOARGS, Reader_process__doc__},
+ {"wait", (PyCFunction) Reader_wait, METH_VARARGS, Reader_wait__doc__},
+ {"seek_cursor", (PyCFunction) Reader_seek_cursor, METH_VARARGS, Reader_seek_cursor__doc__},
+ {"_get_cursor", (PyCFunction) Reader_get_cursor, METH_NOARGS, Reader_get_cursor__doc__},
+ {"test_cursor", (PyCFunction) Reader_test_cursor, METH_VARARGS, Reader_test_cursor__doc__},
+ {"query_unique", (PyCFunction) Reader_query_unique, METH_VARARGS, Reader_query_unique__doc__},
+ {"get_catalog", (PyCFunction) Reader_get_catalog, METH_NOARGS, Reader_get_catalog__doc__},
+ {} /* Sentinel */
+};
+
+static PyTypeObject ReaderType = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "_reader._Reader", /*tp_name*/
+ sizeof(Reader), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ (destructor)Reader_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ Reader__doc__, /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ Reader_methods, /* tp_methods */
+ 0, /* tp_members */
+ Reader_getsetters, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ (initproc) Reader_init, /* tp_init */
+ 0, /* tp_alloc */
+ PyType_GenericNew, /* tp_new */
+};
+
+static PyMethodDef methods[] = {
+ { "_get_catalog", get_catalog, METH_VARARGS, get_catalog__doc__},
+ { NULL, NULL, 0, NULL } /* Sentinel */
+};
+
+#if PY_MAJOR_VERSION >= 3
+static PyModuleDef module = {
+ PyModuleDef_HEAD_INIT,
+ "_reader",
+ module__doc__,
+ -1,
+ methods,
+ NULL, NULL, NULL, NULL
+};
+#endif
+
+#if PY_MAJOR_VERSION >= 3
+static bool initialized = false;
+#endif
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wmissing-prototypes"
+
+PyMODINIT_FUNC
+#if PY_MAJOR_VERSION >= 3
+PyInit__reader(void)
+#else
+init_reader(void)
+#endif
+{
+ PyObject* m;
+
+ PyDateTime_IMPORT;
+
+ if (PyType_Ready(&ReaderType) < 0)
+#if PY_MAJOR_VERSION >= 3
+ return NULL;
+#else
+ return;
+#endif
+
+#if PY_MAJOR_VERSION >= 3
+ m = PyModule_Create(&module);
+ if (m == NULL)
+ return NULL;
+
+ if (!initialized) {
+ PyStructSequence_InitType(&MonotonicType, &Monotonic_desc);
+ initialized = true;
+ }
+#else
+ m = Py_InitModule3("_reader", methods, module__doc__);
+ if (m == NULL)
+ return;
+#endif
+
+ Py_INCREF(&ReaderType);
+#if PY_MAJOR_VERSION >= 3
+ Py_INCREF(&MonotonicType);
+#endif
+ if (PyModule_AddObject(m, "_Reader", (PyObject *) &ReaderType) ||
+#if PY_MAJOR_VERSION >= 3
+ PyModule_AddObject(m, "Monotonic", (PyObject*) &MonotonicType) ||
+#endif
+ PyModule_AddIntConstant(m, "NOP", SD_JOURNAL_NOP) ||
+ PyModule_AddIntConstant(m, "APPEND", SD_JOURNAL_APPEND) ||
+ PyModule_AddIntConstant(m, "INVALIDATE", SD_JOURNAL_INVALIDATE) ||
+ PyModule_AddIntConstant(m, "LOCAL_ONLY", SD_JOURNAL_LOCAL_ONLY) ||
+ PyModule_AddIntConstant(m, "RUNTIME_ONLY", SD_JOURNAL_RUNTIME_ONLY) ||
+ PyModule_AddIntConstant(m, "SYSTEM_ONLY", SD_JOURNAL_SYSTEM_ONLY) ||
+ PyModule_AddStringConstant(m, "__version__", PACKAGE_VERSION)) {
+#if PY_MAJOR_VERSION >= 3
+ Py_DECREF(m);
+ return NULL;
+#endif
+ }
+
+#if PY_MAJOR_VERSION >= 3
+ return m;
+#endif
+}
+
+#pragma GCC diagnostic pop
diff --git a/src/python-systemd/daemon.py b/src/python-systemd/daemon.py
new file mode 100644
index 0000000000..e2829d1671
--- /dev/null
+++ b/src/python-systemd/daemon.py
@@ -0,0 +1,54 @@
+from ._daemon import (__version__,
+ booted,
+ _listen_fds,
+ _is_fifo,
+ _is_socket,
+ _is_socket_inet,
+ _is_socket_unix,
+ _is_mq,
+ LISTEN_FDS_START)
+from socket import AF_UNSPEC as _AF_UNSPEC
+
+def _convert_fileobj(fileobj):
+ try:
+ return fileobj.fileno()
+ except AttributeError:
+ return fileobj
+
+def is_fifo(fileobj, path=None):
+ fd = _convert_fileobj(fileobj)
+ return _is_fifo(fd, path)
+
+def is_socket(fileobj, family=_AF_UNSPEC, type=0, listening=-1):
+ fd = _convert_fileobj(fileobj)
+ return _is_socket(fd, family, type, listening)
+
+def is_socket_inet(fileobj, family=_AF_UNSPEC, type=0, listening=-1, port=0):
+ fd = _convert_fileobj(fileobj)
+ return _is_socket_inet(fd, family, type, listening)
+
+def is_socket_unix(fileobj, type=0, listening=-1, path=None):
+ fd = _convert_fileobj(fileobj)
+ return _is_socket_unix(fd, type, listening, path)
+
+def is_mq(fileobj, path=None):
+ fd = _convert_fileobj(fileobj)
+ return _is_mq(fd, path)
+
+def listen_fds(unset_environment=True):
+ """Return a list of socket activated descriptors
+
+ Example::
+
+ (in primary window)
+ $ systemd-activate -l 2000 python3 -c \\
+ 'from systemd.daemon import listen_fds; print(listen_fds())'
+ (in another window)
+ $ telnet localhost 2000
+ (in primary window)
+ ...
+ Execing python3 (...)
+ [3]
+ """
+ num = _listen_fds(unset_environment)
+ return list(range(LISTEN_FDS_START, LISTEN_FDS_START + num))
diff --git a/src/python-systemd/docs/.gitignore b/src/python-systemd/docs/.gitignore
new file mode 100644
index 0000000000..b06a965e6a
--- /dev/null
+++ b/src/python-systemd/docs/.gitignore
@@ -0,0 +1 @@
+!layout.html
diff --git a/src/python-systemd/docs/conf.py b/src/python-systemd/docs/conf.py
new file mode 100644
index 0000000000..1919170bb1
--- /dev/null
+++ b/src/python-systemd/docs/conf.py
@@ -0,0 +1,279 @@
+# -*- coding: utf-8 -*-
+#
+# python-systemd documentation build configuration file, created by
+# sphinx-quickstart on Sat Feb 9 13:49:42 2013.
+#
+# This file is execfile()d with the current directory set to its containing dir.
+#
+# Note that not all possible configuration values are present in this
+# autogenerated file.
+#
+# All configuration values have a default; values that are commented out
+# serve to show the default.
+
+import sys, os
+
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+#sys.path.insert(0, os.path.abspath('.'))
+
+# -- General configuration -----------------------------------------------------
+
+# If your documentation needs a minimal Sphinx version, state it here.
+#needs_sphinx = '1.0'
+
+# Add any Sphinx extension module names here, as strings. They can be extensions
+# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
+extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.intersphinx', 'sphinx.ext.coverage', 'sphinx.ext.viewcode']
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['.']
+
+# The suffix of source filenames.
+source_suffix = '.rst'
+
+# The encoding of source files.
+#source_encoding = 'utf-8-sig'
+
+# The master toctree document.
+master_doc = 'index'
+
+# General information about the project.
+project = u'python-systemd'
+
+# The language for content autogenerated by Sphinx. Refer to documentation
+# for a list of supported languages.
+#language = None
+
+# There are two options for replacing |today|: either, you set today to some
+# non-false value, then it is used:
+#today = ''
+# Else, today_fmt is used as the format for a strftime call.
+#today_fmt = '%B %d, %Y'
+
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+exclude_patterns = []
+
+# The reST default role (used for this markup: `text`) to use for all documents.
+#default_role = None
+
+# If true, '()' will be appended to :func: etc. cross-reference text.
+#add_function_parentheses = True
+
+# If true, the current module name will be prepended to all description
+# unit titles (such as .. function::).
+#add_module_names = True
+
+# If true, sectionauthor and moduleauthor directives will be shown in the
+# output. They are ignored by default.
+#show_authors = False
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = 'sphinx'
+
+# A list of ignored prefixes for module index sorting.
+#modindex_common_prefix = []
+
+
+# -- Options for HTML output ---------------------------------------------------
+
+# The theme to use for HTML and HTML Help pages. See the documentation for
+# a list of builtin themes.
+html_theme = 'default'
+
+# Theme options are theme-specific and customize the look and feel of a theme
+# further. For a list of options available for each theme, see the
+# documentation.
+#html_theme_options = {}
+
+# Add any paths that contain custom themes here, relative to this directory.
+#html_theme_path = []
+
+# The name for this set of Sphinx documents. If None, it defaults to
+# "<project> v<release> documentation".
+#html_title = None
+
+# A shorter title for the navigation bar. Default is the same as html_title.
+#html_short_title = None
+
+# The name of an image file (relative to this directory) to place at the top
+# of the sidebar.
+#html_logo = None
+
+# The name of an image file (within the static path) to use as favicon of the
+# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
+# pixels large.
+#html_favicon = None
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_static_path = ['.']
+
+# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
+# using the given strftime format.
+#html_last_updated_fmt = '%b %d, %Y'
+
+# If true, SmartyPants will be used to convert quotes and dashes to
+# typographically correct entities.
+#html_use_smartypants = True
+
+# Custom sidebar templates, maps document names to template names.
+#html_sidebars = {}
+
+# Additional templates that should be rendered to pages, maps page names to
+# template names.
+#html_additional_pages = {}
+
+# If false, no module index is generated.
+#html_domain_indices = True
+
+# If false, no index is generated.
+#html_use_index = True
+
+# If true, the index is split into individual pages for each letter.
+#html_split_index = False
+
+# If true, links to the reST sources are added to the pages.
+html_show_sourcelink = False
+
+# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
+#html_show_sphinx = True
+
+# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
+#html_show_copyright = True
+
+# If true, an OpenSearch description file will be output, and all pages will
+# contain a <link> tag referring to it. The value of this option must be the
+# base URL from which the finished HTML is served.
+#html_use_opensearch = ''
+
+# This is the file name suffix for HTML files (e.g. ".xhtml").
+#html_file_suffix = None
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = 'python-systemddoc'
+
+
+# -- Options for LaTeX output --------------------------------------------------
+
+latex_elements = {
+# The paper size ('letterpaper' or 'a4paper').
+#'papersize': 'letterpaper',
+
+# The font size ('10pt', '11pt' or '12pt').
+#'pointsize': '10pt',
+
+# Additional stuff for the LaTeX preamble.
+#'preamble': '',
+}
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title, author, documentclass [howto/manual]).
+latex_documents = [
+ ('index', 'python-systemd.tex', u'python-systemd Documentation',
+ None, 'manual'),
+]
+
+# The name of an image file (relative to this directory) to place at the top of
+# the title page.
+#latex_logo = None
+
+# For "manual" documents, if this is true, then toplevel headings are parts,
+# not chapters.
+#latex_use_parts = False
+
+# If true, show page references after internal links.
+#latex_show_pagerefs = False
+
+# If true, show URL addresses after external links.
+#latex_show_urls = False
+
+# Documents to append as an appendix to all manuals.
+#latex_appendices = []
+
+# If false, no module index is generated.
+#latex_domain_indices = True
+
+
+# -- Options for manual page output --------------------------------------------
+
+# One entry per manual page. List of tuples
+# (source start file, name, description, authors, manual section).
+man_pages = [
+ ('index', 'python-systemd', u'python-systemd Documentation',
+ [], 1)
+]
+
+# If true, show URL addresses after external links.
+#man_show_urls = False
+
+
+# -- Options for Texinfo output ------------------------------------------------
+
+# Grouping the document tree into Texinfo files. List of tuples
+# (source start file, target name, title, author,
+# dir menu entry, description, category)
+texinfo_documents = [
+ ('index', 'python-systemd', u'python-systemd Documentation',
+ u'David Strauss, Zbigniew Jędrzejewski-Szmek, Marti Raudsepp, Steven Hiscocks', 'python-systemd', 'One line description of project.',
+ 'Miscellaneous'),
+]
+
+# Documents to append as an appendix to all manuals.
+#texinfo_appendices = []
+
+# If false, no module index is generated.
+#texinfo_domain_indices = True
+
+# How to display URL addresses: 'footnote', 'no', or 'inline'.
+#texinfo_show_urls = 'footnote'
+
+
+# -- Options for Epub output ---------------------------------------------------
+
+# Bibliographic Dublin Core info.
+epub_title = u'python-systemd'
+epub_author = u'David Strauss, Zbigniew Jędrzejewski-Szmek, Marti Raudsepp, Steven Hiscocks'
+epub_publisher = u'David Strauss, Zbigniew Jędrzejewski-Szmek, Marti Raudsepp, Steven Hiscocks'
+epub_copyright = u'2013, David Strauss, Zbigniew Jędrzejewski-Szmek, Marti Raudsepp, Steven Hiscocks'
+
+# The language of the text. It defaults to the language option
+# or en if the language is not set.
+#epub_language = ''
+
+# The scheme of the identifier. Typical schemes are ISBN or URL.
+#epub_scheme = ''
+
+# The unique identifier of the text. This can be a ISBN number
+# or the project homepage.
+#epub_identifier = ''
+
+# A unique identification for the text.
+#epub_uid = ''
+
+# A tuple containing the cover image and cover page html template filenames.
+#epub_cover = ()
+
+# HTML files that should be inserted before the pages created by sphinx.
+# The format is a list of tuples containing the path and title.
+#epub_pre_files = []
+
+# HTML files shat should be inserted after the pages created by sphinx.
+# The format is a list of tuples containing the path and title.
+#epub_post_files = []
+
+# A list of files that should not be packed into the epub file.
+#epub_exclude_files = []
+
+# The depth of the table of contents in toc.ncx.
+#epub_tocdepth = 3
+
+# Allow duplicate toc entries.
+#epub_tocdup = True
+
+
+# Example configuration for intersphinx: refer to the Python standard library.
+intersphinx_mapping = {'http://docs.python.org/': None}
diff --git a/src/python-systemd/docs/daemon.rst b/src/python-systemd/docs/daemon.rst
new file mode 100644
index 0000000000..72280ca2f0
--- /dev/null
+++ b/src/python-systemd/docs/daemon.rst
@@ -0,0 +1,16 @@
+`systemd.daemon` module
+=======================
+
+.. automodule:: systemd.daemon
+ :members:
+ :undoc-members:
+ :inherited-members:
+
+ .. autoattribute:: systemd.daemon.LISTEN_FDS_START
+
+ .. autofunction:: _listen_fds
+ .. autofunction:: _is_fifo
+ .. autofunction:: _is_socket
+ .. autofunction:: _is_socket_unix
+ .. autofunction:: _is_socket_inet
+ .. autofunction:: _is_mq
diff --git a/src/python-systemd/docs/default.css b/src/python-systemd/docs/default.css
new file mode 100644
index 0000000000..7c097d64a2
--- /dev/null
+++ b/src/python-systemd/docs/default.css
@@ -0,0 +1,196 @@
+@import url("basic.css");
+
+/* -- page layout ----------------------------------------------------------- */
+
+div.documentwrapper {
+ float: left;
+ width: 100%;
+}
+
+div.bodywrapper {
+ margin: 0 0 0 230px;
+}
+
+div.body {
+ background-color: #ffffff;
+ color: #000000;
+ padding: 0 20px 30px 20px;
+}
+
+div.footer {
+ color: #ffffff;
+ width: 100%;
+ padding: 9px 0 9px 0;
+ text-align: center;
+ font-size: 75%;
+}
+
+div.footer a {
+ color: #ffffff;
+ text-decoration: underline;
+}
+
+div.related {
+ background-color: #133f52;
+ line-height: 30px;
+ color: #ffffff;
+}
+
+div.related a {
+ color: #ffffff;
+}
+
+div.sphinxsidebar {
+ background-color: #dddddd;
+}
+
+div.sphinxsidebar p.topless {
+ margin: 5px 10px 10px 10px;
+}
+
+div.sphinxsidebar ul {
+ margin: 10px;
+ padding: 0;
+}
+
+div.sphinxsidebar input {
+ border: 1px solid #000000;
+ font-family: sans-serif;
+ font-size: 1em;
+}
+
+
+
+/* -- hyperlink styles ------------------------------------------------------ */
+
+a {
+ text-decoration: none;
+}
+
+a:hover {
+ text-decoration: underline;
+}
+
+
+
+/* -- body styles ----------------------------------------------------------- */
+
+div.body h1,
+div.body h2,
+div.body h3,
+div.body h4,
+div.body h5,
+div.body h6 {
+ font-family: 'Trebuchet MS', sans-serif;
+ background-color: #f2f2f2;
+ font-weight: normal;
+ color: #20435c;
+ border-bottom: 1px solid #ccc;
+ margin: 20px -20px 10px -20px;
+ padding: 3px 0 3px 10px;
+}
+
+div.body h1 { margin-top: 0; font-size: 200%; }
+div.body h2 { font-size: 160%; }
+div.body h3 { font-size: 140%; }
+div.body h4 { font-size: 120%; }
+div.body h5 { font-size: 110%; }
+div.body h6 { font-size: 100%; }
+
+a.headerlink {
+ color: #c60f0f;
+ font-size: 0.8em;
+ padding: 0 4px 0 4px;
+ text-decoration: none;
+}
+
+a.headerlink:hover {
+ background-color: #c60f0f;
+ color: white;
+}
+
+div.body p, div.body dd, div.body li {
+ text-align: justify;
+ line-height: 130%;
+}
+
+div.admonition p.admonition-title + p {
+ display: inline;
+}
+
+div.admonition p {
+ margin-bottom: 5px;
+}
+
+div.admonition pre {
+ margin-bottom: 5px;
+}
+
+div.admonition ul, div.admonition ol {
+ margin-bottom: 5px;
+}
+
+div.note {
+ background-color: #eee;
+ border: 1px solid #ccc;
+}
+
+div.seealso {
+ background-color: #ffc;
+ border: 1px solid #ff6;
+}
+
+div.topic {
+ background-color: #eee;
+}
+
+div.warning {
+ background-color: #ffe4e4;
+ border: 1px solid #f66;
+}
+
+p.admonition-title {
+ display: inline;
+}
+
+p.admonition-title:after {
+ content: ":";
+}
+
+pre {
+ padding: 5px;
+ background-color: #eeffcc;
+ color: #333333;
+ line-height: 120%;
+ border: 1px solid #ac9;
+ border-left: none;
+ border-right: none;
+}
+
+tt {
+ background-color: #ecf0f3;
+ padding: 0 1px 0 1px;
+ font-size: 0.95em;
+}
+
+th {
+ background-color: #ede;
+}
+
+.warning tt {
+ background: #efc2c2;
+}
+
+.note tt {
+ background: #d6d6d6;
+}
+
+.viewcode-back {
+ font-family: sans-serif;
+}
+
+div.viewcode-block:target {
+ background-color: #f4debf;
+ border-top: 1px solid #ac9;
+ border-bottom: 1px solid #ac9;
+}
diff --git a/src/python-systemd/docs/id128.rst b/src/python-systemd/docs/id128.rst
new file mode 100644
index 0000000000..89c37f3470
--- /dev/null
+++ b/src/python-systemd/docs/id128.rst
@@ -0,0 +1,40 @@
+`systemd.id128` module
+======================
+
+.. automodule:: systemd.id128
+ :members:
+ :undoc-members:
+ :inherited-members:
+
+ .. autoattribute:: systemd.id128.SD_MESSAGE_COREDUMP
+ .. autoattribute:: systemd.id128.SD_MESSAGE_FORWARD_SYSLOG_MISSED
+ .. autoattribute:: systemd.id128.SD_MESSAGE_HIBERNATE_KEY
+ .. autoattribute:: systemd.id128.SD_MESSAGE_JOURNAL_DROPPED
+ .. autoattribute:: systemd.id128.SD_MESSAGE_JOURNAL_MISSED
+ .. autoattribute:: systemd.id128.SD_MESSAGE_JOURNAL_START
+ .. autoattribute:: systemd.id128.SD_MESSAGE_JOURNAL_STOP
+ .. autoattribute:: systemd.id128.SD_MESSAGE_LID_CLOSED
+ .. autoattribute:: systemd.id128.SD_MESSAGE_LID_OPENED
+ .. autoattribute:: systemd.id128.SD_MESSAGE_OVERMOUNTING
+ .. autoattribute:: systemd.id128.SD_MESSAGE_POWER_KEY
+ .. autoattribute:: systemd.id128.SD_MESSAGE_SEAT_START
+ .. autoattribute:: systemd.id128.SD_MESSAGE_SEAT_STOP
+ .. autoattribute:: systemd.id128.SD_MESSAGE_SESSION_START
+ .. autoattribute:: systemd.id128.SD_MESSAGE_SESSION_STOP
+ .. autoattribute:: systemd.id128.SD_MESSAGE_SHUTDOWN
+ .. autoattribute:: systemd.id128.SD_MESSAGE_SLEEP_START
+ .. autoattribute:: systemd.id128.SD_MESSAGE_SLEEP_STOP
+ .. autoattribute:: systemd.id128.SD_MESSAGE_SPAWN_FAILED
+ .. autoattribute:: systemd.id128.SD_MESSAGE_STARTUP_FINISHED
+ .. autoattribute:: systemd.id128.SD_MESSAGE_SUSPEND_KEY
+ .. autoattribute:: systemd.id128.SD_MESSAGE_TIMEZONE_CHANGE
+ .. autoattribute:: systemd.id128.SD_MESSAGE_TIME_CHANGE
+ .. autoattribute:: systemd.id128.SD_MESSAGE_UNIT_FAILED
+ .. autoattribute:: systemd.id128.SD_MESSAGE_UNIT_RELOADED
+ .. autoattribute:: systemd.id128.SD_MESSAGE_UNIT_RELOADING
+ .. autoattribute:: systemd.id128.SD_MESSAGE_UNIT_STARTED
+ .. autoattribute:: systemd.id128.SD_MESSAGE_UNIT_STARTING
+ .. autoattribute:: systemd.id128.SD_MESSAGE_UNIT_STOPPED
+ .. autoattribute:: systemd.id128.SD_MESSAGE_UNIT_STOPPING
+ .. autoattribute:: systemd.id128.SD_MESSAGE_CONFIG_ERROR
+ .. autoattribute:: systemd.id128.SD_MESSAGE_BOOTCHART
diff --git a/src/python-systemd/docs/index.rst b/src/python-systemd/docs/index.rst
new file mode 100644
index 0000000000..e78d966274
--- /dev/null
+++ b/src/python-systemd/docs/index.rst
@@ -0,0 +1,24 @@
+.. python-systemd documentation master file, created by
+ sphinx-quickstart on Sat Feb 9 13:49:42 2013.
+ You can adapt this file completely to your liking, but it should at least
+ contain the root `toctree` directive.
+
+Welcome to python-systemd's documentation!
+==========================================
+
+Contents:
+
+.. toctree::
+ :maxdepth: 2
+
+ journal
+ id128
+ daemon
+ login
+
+Indices and tables
+==================
+
+* :ref:`genindex`
+* :ref:`modindex`
+* :ref:`search`
diff --git a/src/python-systemd/docs/journal.rst b/src/python-systemd/docs/journal.rst
new file mode 100644
index 0000000000..08756b99be
--- /dev/null
+++ b/src/python-systemd/docs/journal.rst
@@ -0,0 +1,63 @@
+`systemd.journal` module
+========================
+
+.. automodule:: systemd.journal
+ :members: send, sendv, stream, stream_fd
+ :undoc-members:
+
+`JournalHandler` class
+----------------------
+
+.. autoclass:: JournalHandler
+
+Accessing the Journal
+---------------------
+
+.. autoclass:: _Reader
+ :undoc-members:
+ :inherited-members:
+
+.. autoclass:: Reader
+ :undoc-members:
+ :inherited-members:
+
+ .. automethod:: __init__
+
+.. autofunction:: _get_catalog
+.. autofunction:: get_catalog
+
+.. autoclass:: Monotonic
+
+.. autoattribute:: systemd.journal.DEFAULT_CONVERTERS
+
+Example: polling for journal events
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This example shows that journal events can be waited for (using
+e.g. `poll`). This makes it easy to integrate Reader in an external
+event loop:
+
+ >>> import select
+ >>> from systemd import journal
+ >>> j = journal.Reader()
+ >>> j.seek_tail()
+ >>> p = select.poll()
+ >>> p.register(j, select.POLLIN)
+ >>> p.poll()
+ [(3, 1)]
+ >>> j.get_next()
+
+
+Journal access types
+~~~~~~~~~~~~~~~~~~~~
+
+.. autoattribute:: systemd.journal.LOCAL_ONLY
+.. autoattribute:: systemd.journal.RUNTIME_ONLY
+.. autoattribute:: systemd.journal.SYSTEM_ONLY
+
+Journal event types
+~~~~~~~~~~~~~~~~~~~
+
+.. autoattribute:: systemd.journal.NOP
+.. autoattribute:: systemd.journal.APPEND
+.. autoattribute:: systemd.journal.INVALIDATE
diff --git a/src/python-systemd/docs/layout.html b/src/python-systemd/docs/layout.html
new file mode 100644
index 0000000000..be5ff980ef
--- /dev/null
+++ b/src/python-systemd/docs/layout.html
@@ -0,0 +1,17 @@
+{% extends "!layout.html" %}
+
+{% block relbar1 %}
+ <a href="../man/systemd.index.html">Index </a>·
+ <a href="../man/systemd.directives.html">Directives </a>·
+ <a href="index.html">Python </a>·
+ <a href="../libudev/index.html">libudev </a>·
+ <a href="../libudev/index.html">gudev </a>
+ <span style="float:right">systemd {{release}}</span>
+ <hr />
+{% endblock %}
+
+{# remove the lower relbar #}
+{% block relbar2 %} {% endblock %}
+
+{# remove the footer #}
+{% block footer %} {% endblock %}
diff --git a/src/python-systemd/docs/login.rst b/src/python-systemd/docs/login.rst
new file mode 100644
index 0000000000..2cd9d8cbee
--- /dev/null
+++ b/src/python-systemd/docs/login.rst
@@ -0,0 +1,5 @@
+`systemd.login` module
+=======================
+
+.. automodule:: systemd.login
+ :members:
diff --git a/src/python-systemd/id128.c b/src/python-systemd/id128.c
new file mode 100644
index 0000000000..ec1d9fb4a2
--- /dev/null
+++ b/src/python-systemd/id128.c
@@ -0,0 +1,161 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Zbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
+
+ 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 <http://www.gnu.org/licenses/>.
+***/
+
+#include <Python.h>
+
+#include <systemd/sd-messages.h>
+
+#include "pyutil.h"
+
+PyDoc_STRVAR(module__doc__,
+ "Python interface to the libsystemd-id128 library.\n\n"
+ "Provides SD_MESSAGE_* constants and functions to query and generate\n"
+ "128-bit unique identifiers."
+);
+
+PyDoc_STRVAR(randomize__doc__,
+ "randomize() -> UUID\n\n"
+ "Return a new random 128-bit unique identifier.\n"
+ "Wraps sd_id128_randomize(3)."
+);
+
+PyDoc_STRVAR(get_machine__doc__,
+ "get_machine() -> UUID\n\n"
+ "Return a 128-bit unique identifier for this machine.\n"
+ "Wraps sd_id128_get_machine(3)."
+);
+
+PyDoc_STRVAR(get_boot__doc__,
+ "get_boot() -> UUID\n\n"
+ "Return a 128-bit unique identifier for this boot.\n"
+ "Wraps sd_id128_get_boot(3)."
+);
+
+static PyObject* make_uuid(sd_id128_t id) {
+ _cleanup_Py_DECREF_ PyObject
+ *uuid = NULL, *UUID = NULL, *bytes = NULL,
+ *args = NULL, *kwargs = NULL;
+
+ uuid = PyImport_ImportModule("uuid");
+ if (!uuid)
+ return NULL;
+
+ UUID = PyObject_GetAttrString(uuid, "UUID");
+ bytes = PyBytes_FromStringAndSize((const char*) &id.bytes, sizeof(id.bytes));
+ args = Py_BuildValue("()");
+ kwargs = PyDict_New();
+ if (!UUID || !bytes || !args || !kwargs)
+ return NULL;
+
+ if (PyDict_SetItemString(kwargs, "bytes", bytes) < 0)
+ return NULL;
+
+ return PyObject_Call(UUID, args, kwargs);
+}
+
+#define helper(name) \
+ static PyObject *name(PyObject *self, PyObject *args) { \
+ sd_id128_t id; \
+ int r; \
+ \
+ assert(args == NULL); \
+ \
+ r = sd_id128_##name(&id); \
+ if (r < 0) { \
+ errno = -r; \
+ return PyErr_SetFromErrno(PyExc_IOError); \
+ } \
+ \
+ return make_uuid(id); \
+ }
+
+helper(randomize)
+helper(get_machine)
+helper(get_boot)
+
+static PyMethodDef methods[] = {
+ { "randomize", randomize, METH_NOARGS, randomize__doc__},
+ { "get_machine", get_machine, METH_NOARGS, get_machine__doc__},
+ { "get_boot", get_boot, METH_NOARGS, get_boot__doc__},
+ { NULL, NULL, 0, NULL } /* Sentinel */
+};
+
+static int add_id(PyObject *module, const char* name, sd_id128_t id) {
+ PyObject *obj;
+
+ obj = make_uuid(id);
+ if (!obj)
+ return -1;
+
+ return PyModule_AddObject(module, name, obj);
+}
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wmissing-prototypes"
+
+#if PY_MAJOR_VERSION < 3
+
+PyMODINIT_FUNC initid128(void) {
+ PyObject *m;
+
+ m = Py_InitModule3("id128", methods, module__doc__);
+ if (m == NULL)
+ return;
+
+ /* a series of lines like 'add_id() ;' follow */
+#define JOINER ;
+#include "id128-constants.h"
+#undef JOINER
+ PyModule_AddStringConstant(m, "__version__", PACKAGE_VERSION);
+}
+
+#else
+
+static struct PyModuleDef module = {
+ PyModuleDef_HEAD_INIT,
+ "id128", /* name of module */
+ module__doc__, /* module documentation, may be NULL */
+ -1, /* size of per-interpreter state of the module */
+ methods
+};
+
+PyMODINIT_FUNC PyInit_id128(void) {
+ PyObject *m;
+
+ m = PyModule_Create(&module);
+ if (m == NULL)
+ return NULL;
+
+ if ( /* a series of lines like 'add_id() ||' follow */
+#define JOINER ||
+#include "id128-constants.h"
+#undef JOINER
+ PyModule_AddStringConstant(m, "__version__", PACKAGE_VERSION)) {
+ Py_DECREF(m);
+ return NULL;
+ }
+
+ return m;
+}
+
+#endif
+
+#pragma GCC diagnostic pop
diff --git a/src/python-systemd/journal.py b/src/python-systemd/journal.py
index 516ca1ab56..9ef1ede229 100644
--- a/src/python-systemd/journal.py
+++ b/src/python-systemd/journal.py
@@ -1,8 +1,10 @@
-# -*- Mode: python; indent-tabs-mode: nil -*- */
+# -*- Mode: python; coding:utf-8; indent-tabs-mode: nil -*- */
#
# This file is part of systemd.
#
-# Copyright 2012 David Strauss
+# Copyright 2012 David Strauss <david@davidstrauss.net>
+# Copyright 2012 Zbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
+# Copyright 2012 Marti Raudsepp <marti@juffo.org>
#
# 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
@@ -17,12 +19,330 @@
# You should have received a copy of the GNU Lesser General Public License
# along with systemd; If not, see <http://www.gnu.org/licenses/>.
+from __future__ import division
+
+import sys as _sys
+import datetime as _datetime
+import uuid as _uuid
import traceback as _traceback
import os as _os
import logging as _logging
+if _sys.version_info >= (3,3):
+ from collections import ChainMap as _ChainMap
from syslog import (LOG_EMERG, LOG_ALERT, LOG_CRIT, LOG_ERR,
LOG_WARNING, LOG_NOTICE, LOG_INFO, LOG_DEBUG)
-from ._journal import sendv, stream_fd
+from ._journal import __version__, sendv, stream_fd
+from ._reader import (_Reader, NOP, APPEND, INVALIDATE,
+ LOCAL_ONLY, RUNTIME_ONLY, SYSTEM_ONLY,
+ _get_catalog)
+from . import id128 as _id128
+
+if _sys.version_info >= (3,):
+ from ._reader import Monotonic
+else:
+ Monotonic = tuple
+
+def _convert_monotonic(m):
+ return Monotonic((_datetime.timedelta(microseconds=m[0]),
+ _uuid.UUID(bytes=m[1])))
+
+def _convert_source_monotonic(s):
+ return _datetime.timedelta(microseconds=int(s))
+
+def _convert_realtime(t):
+ return _datetime.datetime.fromtimestamp(t / 1000000)
+
+def _convert_timestamp(s):
+ return _datetime.datetime.fromtimestamp(int(s) / 1000000)
+
+if _sys.version_info >= (3,):
+ def _convert_uuid(s):
+ return _uuid.UUID(s.decode())
+else:
+ _convert_uuid = _uuid.UUID
+
+DEFAULT_CONVERTERS = {
+ 'MESSAGE_ID': _convert_uuid,
+ '_MACHINE_ID': _convert_uuid,
+ '_BOOT_ID': _convert_uuid,
+ 'PRIORITY': int,
+ 'LEADER': int,
+ 'SESSION_ID': int,
+ 'USERSPACE_USEC': int,
+ 'INITRD_USEC': int,
+ 'KERNEL_USEC': int,
+ '_UID': int,
+ '_GID': int,
+ '_PID': int,
+ 'SYSLOG_FACILITY': int,
+ 'SYSLOG_PID': int,
+ '_AUDIT_SESSION': int,
+ '_AUDIT_LOGINUID': int,
+ '_SYSTEMD_SESSION': int,
+ '_SYSTEMD_OWNER_UID': int,
+ 'CODE_LINE': int,
+ 'ERRNO': int,
+ 'EXIT_STATUS': int,
+ '_SOURCE_REALTIME_TIMESTAMP': _convert_timestamp,
+ '__REALTIME_TIMESTAMP': _convert_realtime,
+ '_SOURCE_MONOTONIC_TIMESTAMP': _convert_source_monotonic,
+ '__MONOTONIC_TIMESTAMP': _convert_monotonic,
+ 'COREDUMP': bytes,
+ 'COREDUMP_PID': int,
+ 'COREDUMP_UID': int,
+ 'COREDUMP_GID': int,
+ 'COREDUMP_SESSION': int,
+ 'COREDUMP_SIGNAL': int,
+ 'COREDUMP_TIMESTAMP': _convert_timestamp,
+}
+
+_IDENT_LETTER = set('ABCDEFGHIJKLMNOPQRTSUVWXYZ_')
+
+def _valid_field_name(s):
+ return not (set(s) - _IDENT_LETTER)
+
+class Reader(_Reader):
+ """Reader allows the access and filtering of systemd journal
+ entries. Note that in order to access the system journal, a
+ non-root user must be in the `systemd-journal` group.
+
+ Example usage to print out all informational or higher level
+ messages for systemd-udevd for this boot:
+
+ >>> j = journal.Reader()
+ >>> j.this_boot()
+ >>> j.log_level(journal.LOG_INFO)
+ >>> j.add_match(_SYSTEMD_UNIT="systemd-udevd.service")
+ >>> for entry in j:
+ ... print(entry['MESSAGE'])
+
+ See systemd.journal-fields(7) for more info on typical fields
+ found in the journal.
+ """
+ def __init__(self, flags=0, path=None, converters=None):
+ """Create an instance of Reader, which allows filtering and
+ return of journal entries.
+
+ Argument `flags` sets open flags of the journal, which can be one
+ of, or ORed combination of constants: LOCAL_ONLY (default) opens
+ journal on local machine only; RUNTIME_ONLY opens only
+ volatile journal files; and SYSTEM_ONLY opens only
+ journal files of system services and the kernel.
+
+ Argument `path` is the directory of journal files. Note that
+ `flags` and `path` are exclusive.
+
+ Argument `converters` is a dictionary which updates the
+ DEFAULT_CONVERTERS to convert journal field values. Field
+ names are used as keys into this dictionary. The values must
+ be single argument functions, which take a `bytes` object and
+ return a converted value. When there's no entry for a field
+ name, then the default UTF-8 decoding will be attempted. If
+ the conversion fails with a ValueError, unconverted bytes
+ object will be returned. (Note that ValueEror is a superclass
+ of UnicodeDecodeError).
+
+ Reader implements the context manager protocol: the journal
+ will be closed when exiting the block.
+ """
+ super(Reader, self).__init__(flags, path)
+ if _sys.version_info >= (3,3):
+ self.converters = _ChainMap()
+ if converters is not None:
+ self.converters.maps.append(converters)
+ self.converters.maps.append(DEFAULT_CONVERTERS)
+ else:
+ self.converters = DEFAULT_CONVERTERS.copy()
+ if converters is not None:
+ self.converters.update(converters)
+
+ def _convert_field(self, key, value):
+ """Convert value using self.converters[key]
+
+ If `key` is not present in self.converters, a standard unicode
+ decoding will be attempted. If the conversion (either
+ key-specific or the default one) fails with a ValueError, the
+ original bytes object will be returned.
+ """
+ convert = self.converters.get(key, bytes.decode)
+ try:
+ return convert(value)
+ except ValueError:
+ # Leave in default bytes
+ return value
+
+ def _convert_entry(self, entry):
+ """Convert entire journal entry utilising _covert_field"""
+ result = {}
+ for key, value in entry.items():
+ if isinstance(value, list):
+ result[key] = [self._convert_field(key, val) for val in value]
+ else:
+ result[key] = self._convert_field(key, value)
+ return result
+
+ def __iter__(self):
+ """Part of iterator protocol.
+ Returns self.
+ """
+ return self
+
+ if _sys.version_info >= (3,):
+ def __next__(self):
+ """Part of iterator protocol.
+ Returns self.get_next().
+ """
+ return self.get_next()
+ else:
+ def next(self):
+ """Part of iterator protocol.
+ Returns self.get_next().
+ """
+ return self.get_next()
+
+ def add_match(self, *args, **kwargs):
+ """Add one or more matches to the filter journal log entries.
+ All matches of different field are combined in a logical AND,
+ and matches of the same field are automatically combined in a
+ logical OR.
+ Matches can be passed as strings of form "FIELD=value", or
+ keyword arguments FIELD="value".
+ """
+ args = list(args)
+ args.extend(_make_line(key, val) for key, val in kwargs.items())
+ for arg in args:
+ super(Reader, self).add_match(arg)
+
+ def get_next(self, skip=1):
+ """Return the next log entry as a mapping type, currently
+ a standard dictionary of fields.
+
+ Optional skip value will return the `skip`\-th log entry.
+
+ Entries will be processed with converters specified during
+ Reader creation.
+ """
+ if super(Reader, self)._next(skip):
+ entry = super(Reader, self)._get_all()
+ if entry:
+ entry['__REALTIME_TIMESTAMP'] = self._get_realtime()
+ entry['__MONOTONIC_TIMESTAMP'] = self._get_monotonic()
+ entry['__CURSOR'] = self._get_cursor()
+ return self._convert_entry(entry)
+ return dict()
+
+ def get_previous(self, skip=1):
+ """Return the previous log entry as a mapping type,
+ currently a standard dictionary of fields.
+
+ Optional skip value will return the -`skip`\-th log entry.
+
+ Entries will be processed with converters specified during
+ Reader creation.
+
+ Equivalent to get_next(-skip).
+ """
+ return self.get_next(-skip)
+
+ def query_unique(self, field):
+ """Return unique values appearing in the journal for given `field`.
+
+ Note this does not respect any journal matches.
+
+ Entries will be processed with converters specified during
+ Reader creation.
+ """
+ return set(self._convert_field(field, value)
+ for value in super(Reader, self).query_unique(field))
+
+ def wait(self, timeout=None):
+ """Wait for a change in the journal. `timeout` is the maximum
+ time in seconds to wait, or None, to wait forever.
+
+ Returns one of NOP (no change), APPEND (new entries have been
+ added to the end of the journal), or INVALIDATE (journal files
+ have been added or removed).
+ """
+ us = -1 if timeout is None else int(timeout * 1000000)
+ return super(Reader, self).wait(us)
+
+ def seek_realtime(self, realtime):
+ """Seek to a matching journal entry nearest to `realtime` time.
+
+ Argument `realtime` must be either an integer unix timestamp
+ or datetime.datetime instance.
+ """
+ if isinstance(realtime, _datetime.datetime):
+ realtime = float(realtime.strftime("%s.%f")) * 1000000
+ return super(Reader, self).seek_realtime(int(realtime))
+
+ def seek_monotonic(self, monotonic, bootid=None):
+ """Seek to a matching journal entry nearest to `monotonic` time.
+
+ Argument `monotonic` is a timestamp from boot in either
+ seconds or a datetime.timedelta instance. Argument `bootid`
+ is a string or UUID representing which boot the monotonic time
+ is reference to. Defaults to current bootid.
+ """
+ if isinstance(monotonic, _datetime.timedelta):
+ monotonic = monotonic.totalseconds()
+ monotonic = int(monotonic * 1000000)
+ if isinstance(bootid, _uuid.UUID):
+ bootid = bootid.get_hex()
+ return super(Reader, self).seek_monotonic(monotonic, bootid)
+
+ def log_level(self, level):
+ """Set maximum log `level` by setting matches for PRIORITY.
+ """
+ if 0 <= level <= 7:
+ for i in range(level+1):
+ self.add_match(PRIORITY="%d" % i)
+ else:
+ raise ValueError("Log level must be 0 <= level <= 7")
+
+ def messageid_match(self, messageid):
+ """Add match for log entries with specified `messageid`.
+
+ `messageid` can be string of hexadicimal digits or a UUID
+ instance. Standard message IDs can be found in systemd.id128.
+
+ Equivalent to add_match(MESSAGE_ID=`messageid`).
+ """
+ if isinstance(messageid, _uuid.UUID):
+ messageid = messageid.get_hex()
+ self.add_match(MESSAGE_ID=messageid)
+
+ def this_boot(self, bootid=None):
+ """Add match for _BOOT_ID equal to current boot ID or the specified boot ID.
+
+ If specified, bootid should be either a UUID or a 32 digit hex number.
+
+ Equivalent to add_match(_BOOT_ID='bootid').
+ """
+ if bootid is None:
+ bootid = _id128.get_boot().hex
+ else:
+ bootid = getattr(bootid, 'hex', bootid)
+ self.add_match(_BOOT_ID=bootid)
+
+ def this_machine(self, machineid=None):
+ """Add match for _MACHINE_ID equal to the ID of this machine.
+
+ If specified, machineid should be either a UUID or a 32 digit hex number.
+
+ Equivalent to add_match(_MACHINE_ID='machineid').
+ """
+ if machineid is None:
+ machineid = _id128.get_machine().hex
+ else:
+ machineid = getattr(machineid, 'hex', machineid)
+ self.add_match(_MACHINE_ID=machineid)
+
+
+def get_catalog(mid):
+ if isinstance(mid, _uuid.UUID):
+ mid = mid.get_hex()
+ return _get_catalog(mid)
def _make_line(field, value):
if isinstance(value, bytes):
@@ -33,24 +353,18 @@ def _make_line(field, value):
def send(MESSAGE, MESSAGE_ID=None,
CODE_FILE=None, CODE_LINE=None, CODE_FUNC=None,
**kwargs):
- r"""Send a message to journald.
+ r"""Send a message to the journal.
>>> journal.send('Hello world')
>>> journal.send('Hello, again, world', FIELD2='Greetings!')
>>> journal.send('Binary message', BINARY=b'\xde\xad\xbe\xef')
Value of the MESSAGE argument will be used for the MESSAGE=
- field.
+ field. MESSAGE must be a string and will be sent as UTF-8 to
+ the journal.
MESSAGE_ID can be given to uniquely identify the type of
- message.
-
- Other parts of the message can be specified as keyword
- arguments.
-
- Both MESSAGE and MESSAGE_ID, if present, must be strings, and
- will be sent as UTF-8 to journal. Other arguments can be
- bytes, in which case they will be sent as-is to journal.
+ message. It must be a string or a uuid.UUID object.
CODE_LINE, CODE_FILE, and CODE_FUNC can be specified to
identify the caller. Unless at least on of the three is given,
@@ -58,6 +372,11 @@ def send(MESSAGE, MESSAGE_ID=None,
send(). CODE_FILE and CODE_FUNC must be strings, CODE_LINE
must be an integer.
+ Additional fields for the journal entry can only be specified
+ as keyword arguments. The payload can be either a string or
+ bytes. A string will be sent as UTF-8, and bytes will be sent
+ as-is to the journal.
+
Other useful fields include PRIORITY, SYSLOG_FACILITY,
SYSLOG_IDENTIFIER, SYSLOG_PID.
"""
@@ -65,7 +384,8 @@ def send(MESSAGE, MESSAGE_ID=None,
args = ['MESSAGE=' + MESSAGE]
if MESSAGE_ID is not None:
- args.append('MESSAGE_ID=' + MESSAGE_ID)
+ id = getattr(MESSAGE_ID, 'hex', MESSAGE_ID)
+ args.append('MESSAGE_ID=' + id)
if CODE_LINE == CODE_FILE == CODE_FUNC == None:
CODE_FILE, CODE_LINE, CODE_FUNC = \
@@ -94,19 +414,20 @@ def stream(identifier, priority=LOG_DEBUG, level_prefix=False):
<open file '<fdopen>', mode 'w' at 0x...>
>>> stream.write('message...\n')
- will produce the following message in the journal:
+ will produce the following message in the journal::
- PRIORITY=7
- SYSLOG_IDENTIFIER=myapp
- MESSAGE=message...
+ PRIORITY=7
+ SYSLOG_IDENTIFIER=myapp
+ MESSAGE=message...
Using the interface with print might be more convinient:
>>> from __future__ import print_function
>>> print('message...', file=stream)
- priority is the syslog priority, one of LOG_EMERG, LOG_ALERT,
- LOG_CRIT, LOG_ERR, LOG_WARNING, LOG_NOTICE, LOG_INFO, LOG_DEBUG.
+ priority is the syslog priority, one of `LOG_EMERG`,
+ `LOG_ALERT`, `LOG_CRIT`, `LOG_ERR`, `LOG_WARNING`,
+ `LOG_NOTICE`, `LOG_INFO`, `LOG_DEBUG`.
level_prefix is a boolean. If true, kernel-style log priority
level prefixes (such as '<1>') are interpreted. See
@@ -120,7 +441,7 @@ class JournalHandler(_logging.Handler):
"""Journal handler class for the Python logging framework.
Please see the Python logging module documentation for an
- overview: http://docs.python.org/library/logging.html
+ overview: http://docs.python.org/library/logging.html.
To create a custom logger whose messages go only to journal:
@@ -129,33 +450,51 @@ class JournalHandler(_logging.Handler):
>>> log.addHandler(journal.JournalHandler())
>>> log.warn("Some message: %s", detail)
- Note that by default, message levels INFO and DEBUG are ignored
- by the logging framework. To enable those log levels:
+ Note that by default, message levels `INFO` and `DEBUG` are
+ ignored by the logging framework. To enable those log levels:
>>> log.setLevel(logging.DEBUG)
- To attach journal MESSAGE_ID, an extra field is supported:
-
- >>> log.warn("Message with ID",
- >>> extra={'MESSAGE_ID': '22bb01335f724c959ac4799627d1cb61'})
-
To redirect all logging messages to journal regardless of where
they come from, attach it to the root logger:
>>> logging.root.addHandler(journal.JournalHandler())
- For more complex configurations when using dictConfig or
- fileConfig, specify 'systemd.journal.JournalHandler' as the
+ For more complex configurations when using `dictConfig` or
+ `fileConfig`, specify `systemd.journal.JournalHandler` as the
handler class. Only standard handler configuration options
- are supported: level, formatter, filters.
+ are supported: `level`, `formatter`, `filters`.
- The following journal fields will be sent:
+ To attach journal MESSAGE_ID, an extra field is supported:
+
+ >>> import uuid
+ >>> mid = uuid.UUID('0123456789ABCDEF0123456789ABCDEF')
+ >>> log.warn("Message with ID", extra={'MESSAGE_ID': mid})
+
+ Fields to be attached to all messages sent through this
+ handler can be specified as keyword arguments. This probably
+ makes sense only for SYSLOG_IDENTIFIER and similar fields
+ which are constant for the whole program:
+
+ >>> journal.JournalHandler(SYSLOG_IDENTIFIER='my-cool-app')
- MESSAGE, PRIORITY, THREAD_NAME, CODE_FILE, CODE_LINE,
- CODE_FUNC, LOGGER (name as supplied to getLogger call),
- MESSAGE_ID (optional, see above).
+ The following journal fields will be sent:
+ `MESSAGE`, `PRIORITY`, `THREAD_NAME`, `CODE_FILE`, `CODE_LINE`,
+ `CODE_FUNC`, `LOGGER` (name as supplied to getLogger call),
+ `MESSAGE_ID` (optional, see above), `SYSLOG_IDENTIFIER` (defaults
+ to sys.argv[0]).
"""
+ def __init__(self, level=_logging.NOTSET, **kwargs):
+ super(JournalHandler, self).__init__(level)
+
+ for name in kwargs:
+ if not _valid_field_name(name):
+ raise ValueError('Invalid field name: ' + name)
+ if 'SYSLOG_IDENTIFIER' not in kwargs:
+ kwargs['SYSLOG_IDENTIFIER'] = _sys.argv[0]
+ self._extra = kwargs
+
def emit(self, record):
"""Write record as journal event.
@@ -176,7 +515,8 @@ class JournalHandler(_logging.Handler):
THREAD_NAME=record.threadName,
CODE_FILE=record.pathname,
CODE_LINE=record.lineno,
- CODE_FUNC=record.funcName)
+ CODE_FUNC=record.funcName,
+ **self._extra)
except Exception:
self.handleError(record)
diff --git a/src/python-systemd/login.c b/src/python-systemd/login.c
new file mode 100644
index 0000000000..1dbe5ac5bf
--- /dev/null
+++ b/src/python-systemd/login.c
@@ -0,0 +1,176 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Zbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
+
+ 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 <http://www.gnu.org/licenses/>.
+***/
+
+#define PY_SSIZE_T_CLEAN
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wredundant-decls"
+#include <Python.h>
+#pragma GCC diagnostic pop
+
+#include "systemd/sd-login.h"
+#include "pyutil.h"
+#include "util.h"
+#include "strv.h"
+
+PyDoc_STRVAR(module__doc__,
+ "Python interface to the libsystemd-login library."
+);
+
+#define helper(name) \
+static PyObject* name(PyObject *self, PyObject *args) { \
+ _cleanup_strv_free_ char **list = NULL; \
+ int r; \
+ PyObject *ans; \
+ \
+ assert(args == NULL); \
+ \
+ r = sd_get_##name(&list); \
+ if (r < 0) { \
+ errno = -r; \
+ return PyErr_SetFromErrno(PyExc_IOError); \
+ } \
+ \
+ ans = PyList_New(r); \
+ if (!ans) \
+ return NULL; \
+ \
+ for (r--; r >= 0; r--) { \
+ PyObject *s = unicode_FromString(list[r]); \
+ if (!s) { \
+ Py_DECREF(ans); \
+ return NULL; \
+ } \
+ \
+ PyList_SetItem(ans, r, s); \
+ } \
+ \
+ return ans; \
+}
+
+helper(seats)
+helper(sessions)
+helper(machine_names)
+#undef helper
+
+static PyObject* uids(PyObject *self, PyObject *args) {
+ _cleanup_free_ uid_t *list = NULL;
+ int r;
+ PyObject *ans;
+
+ assert(args == NULL);
+
+ r = sd_get_uids(&list);
+ if (r < 0) {
+ errno = -r;
+ return PyErr_SetFromErrno(PyExc_IOError);
+ }
+
+ ans = PyList_New(r);
+ if (!ans)
+ return NULL;
+
+ for (r--; r >= 0; r--) {
+ PyObject *s = long_FromLong(list[r]);
+ if (!s) {
+ Py_DECREF(ans);
+ return NULL;
+ }
+
+ PyList_SetItem(ans, r, s);
+ }
+
+ return ans;
+}
+
+PyDoc_STRVAR(seats__doc__,
+ "seats() -> list\n\n"
+ "Returns a list of currently available local seats.\n"
+ "Wraps sd_get_seats(3)."
+);
+
+PyDoc_STRVAR(sessions__doc__,
+ "sessions() -> list\n\n"
+ "Returns a list of current login sessions.\n"
+ "Wraps sd_get_sessions(3)."
+);
+
+PyDoc_STRVAR(machine_names__doc__,
+ "machine_names() -> list\n\n"
+ "Returns a list of currently running virtual machines\n"
+ "and containers on the system.\n"
+ "Wraps sd_get_machine_names(3)."
+);
+
+PyDoc_STRVAR(uids__doc__,
+ "uids() -> list\n\n"
+ "Returns a list of uids of users who currently have login sessions.\n"
+ "Wraps sd_get_uids(3)."
+);
+
+static PyMethodDef methods[] = {
+ { "seats", seats, METH_NOARGS, seats__doc__},
+ { "sessions", sessions, METH_NOARGS, sessions__doc__},
+ { "machine_names", machine_names, METH_NOARGS, machine_names__doc__},
+ { "uids", uids, METH_NOARGS, uids__doc__},
+ {} /* Sentinel */
+};
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wmissing-prototypes"
+
+#if PY_MAJOR_VERSION < 3
+
+PyMODINIT_FUNC initlogin(void) {
+ PyObject *m;
+
+ m = Py_InitModule3("login", methods, module__doc__);
+ if (m == NULL)
+ return;
+ PyModule_AddStringConstant(m, "__version__", PACKAGE_VERSION);
+}
+#else
+
+static struct PyModuleDef module = {
+ PyModuleDef_HEAD_INIT,
+ "login", /* name of module */
+ module__doc__, /* module documentation, may be NULL */
+ -1, /* size of per-interpreter state of the module */
+ methods
+};
+
+PyMODINIT_FUNC PyInit_login(void) {
+ PyObject *m;
+
+ m = PyModule_Create(&module);
+ if (m == NULL)
+ return NULL;
+
+ if (PyModule_AddStringConstant(m, "__version__", PACKAGE_VERSION)) {
+ Py_DECREF(m);
+ return NULL;
+ }
+
+ return m;
+}
+
+#endif
+
+#pragma GCC diagnostic pop
diff --git a/src/python-systemd/pyutil.c b/src/python-systemd/pyutil.c
new file mode 100644
index 0000000000..9510acdddb
--- /dev/null
+++ b/src/python-systemd/pyutil.c
@@ -0,0 +1,46 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Zbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
+
+ 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 <http://www.gnu.org/licenses/>.
+***/
+
+#include <Python.h>
+#include "pyutil.h"
+
+void cleanup_Py_DECREFp(PyObject **p) {
+ if (!*p)
+ return;
+
+ Py_DECREF(*p);
+}
+
+PyObject* absolute_timeout(uint64_t t) {
+ if (t == (uint64_t) -1)
+ return PyLong_FromLong(-1);
+ else {
+ struct timespec ts;
+ uint64_t n;
+ int msec;
+
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+ n = (uint64_t) ts.tv_sec * 1000000 + ts.tv_nsec / 1000;
+ msec = t > n ? (int) ((t - n + 999) / 1000) : 0;
+
+ return PyLong_FromLong(msec);
+ }
+}
diff --git a/src/python-systemd/pyutil.h b/src/python-systemd/pyutil.h
new file mode 100644
index 0000000000..5c7ea37cdb
--- /dev/null
+++ b/src/python-systemd/pyutil.h
@@ -0,0 +1,49 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Zbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
+
+ 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 <http://www.gnu.org/licenses/>.
+***/
+
+#ifndef Py_TYPE
+/* avoid duplication warnings from errors in Python 2.7 headers */
+# include <Python.h>
+#endif
+
+void cleanup_Py_DECREFp(PyObject **p);
+PyObject* absolute_timeout(uint64_t t);
+
+#define _cleanup_Py_DECREF_ __attribute__((cleanup(cleanup_Py_DECREFp)))
+
+#if PY_MAJOR_VERSION >=3
+# define unicode_FromStringAndSize PyUnicode_FromStringAndSize
+# define unicode_FromString PyUnicode_FromString
+# define long_FromLong PyLong_FromLong
+# define long_FromSize_t PyLong_FromSize_t
+# define long_Check PyLong_Check
+# define long_AsLong PyLong_AsLong
+#else
+/* Python 3 type naming convention is used */
+# define unicode_FromStringAndSize PyString_FromStringAndSize
+# define unicode_FromString PyString_FromString
+# define long_FromLong PyInt_FromLong
+# define long_FromSize_t PyInt_FromSize_t
+# define long_Check PyInt_Check
+# define long_AsLong PyInt_AsLong
+#endif
diff --git a/src/quotacheck/quotacheck.c b/src/quotacheck/quotacheck.c
index e7a4405027..1f63785041 100644
--- a/src/quotacheck/quotacheck.c
+++ b/src/quotacheck/quotacheck.c
@@ -27,19 +27,22 @@
#include "util.h"
#include "virt.h"
+#include "fileio.h"
static bool arg_skip = false;
static bool arg_force = false;
static int parse_proc_cmdline(void) {
- char *line, *w, *state;
- int r;
+ _cleanup_free_ char *line = NULL;
+ char *w, *state;
size_t l;
+ int r;
if (detect_container(NULL) > 0)
return 0;
- if ((r = read_one_line_file("/proc/cmdline", &line)) < 0) {
+ r = read_one_line_file("/proc/cmdline", &line);
+ if (r < 0) {
log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
return 0;
}
@@ -61,8 +64,6 @@ static int parse_proc_cmdline(void) {
}
#endif
}
-
- free(line);
return 0;
}
@@ -76,13 +77,13 @@ static void test_files(void) {
}
int main(int argc, char *argv[]) {
+
static const char * const cmdline[] = {
- "/sbin/quotacheck",
+ QUOTACHECK,
"-anug",
NULL
};
- int r = EXIT_FAILURE;
pid_t pid;
if (argc > 1) {
@@ -101,23 +102,21 @@ int main(int argc, char *argv[]) {
if (!arg_force) {
if (arg_skip)
- return 0;
+ return EXIT_SUCCESS;
if (access("/run/systemd/quotacheck", F_OK) < 0)
- return 0;
+ return EXIT_SUCCESS;
}
- if ((pid = fork()) < 0) {
+ pid = fork();
+ if (pid < 0) {
log_error("fork(): %m");
- goto finish;
+ return EXIT_FAILURE;
} else if (pid == 0) {
/* Child */
execv(cmdline[0], (char**) cmdline);
_exit(1); /* Operational error */
}
- r = wait_for_terminate_and_warn("quotacheck", pid) == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
-
-finish:
- return r;
+ return wait_for_terminate_and_warn("quotacheck", pid) >= 0 ? EXIT_SUCCESS : EXIT_FAILURE;
}
diff --git a/src/rc-local-generator/rc-local-generator.c b/src/rc-local-generator/rc-local-generator.c
index 448980ba2d..9265501250 100644
--- a/src/rc-local-generator/rc-local-generator.c
+++ b/src/rc-local-generator/rc-local-generator.c
@@ -59,7 +59,7 @@ static int add_symlink(const char *service, const char *where) {
if (errno == EEXIST)
r = 0;
else {
- log_error("Failed to create symlink from %s to %s: %m", from, to);
+ log_error("Failed to create symlink %s: %m", to);
r = -errno;
}
}
diff --git a/src/readahead/readahead-analyze.c b/src/readahead/readahead-analyze.c
index 9a929c0937..76db3cb7e4 100644
--- a/src/readahead/readahead-analyze.c
+++ b/src/readahead/readahead-analyze.c
@@ -34,10 +34,10 @@
int main_analyze(const char *pack_path) {
char line[LINE_MAX];
- FILE *pack;
+ _cleanup_fclose_ FILE *pack = NULL;
int a;
int missing = 0;
- off_t tsize = 0;
+ size_t tsize = 0;
if (!pack_path)
pack_path = "/.readahead";
@@ -114,11 +114,11 @@ int main_analyze(const char *pack_path) {
tsize += size;
- printf(" %4d%% (%2d) %12ld: %s\n",
- sections ? (int) (size * 100 / st.st_size) : 100,
- sections ? sections : 1,
- (unsigned long)size,
- path);
+ printf(" %4jd%% (%2d) %12jd: %s\n",
+ (intmax_t) (sections && st.st_size ? size * 100 / st.st_size : 100),
+ sections ? sections : 1,
+ (intmax_t) size,
+ path);
} else {
printf(" %4dp (%2d) %12s: %s (MISSING)\n",
sections ? pages : -1,
@@ -130,21 +130,17 @@ int main_analyze(const char *pack_path) {
}
- fclose(pack);
-
printf("\nHOST: %s"
"TYPE: %c\n"
"MISSING: %d\n"
- "TOTAL: %llu\n",
+ "TOTAL: %zu\n",
line,
a,
missing,
- (unsigned long long) tsize);
+ tsize);
return EXIT_SUCCESS;
fail:
- if(pack)
- fclose(pack);
return EXIT_FAILURE;
}
diff --git a/src/readahead/readahead-collect.c b/src/readahead/readahead-collect.c
index 5d07f4704a..ccd8408c1b 100644
--- a/src/readahead/readahead-collect.c
+++ b/src/readahead/readahead-collect.c
@@ -42,6 +42,7 @@
#include <sys/vfs.h>
#include <getopt.h>
#include <sys/inotify.h>
+#include <math.h>
#ifdef HAVE_FANOTIFY_INIT
#include <sys/fanotify.h>
@@ -67,16 +68,14 @@
*/
static ReadaheadShared *shared = NULL;
+static usec_t starttime;
/* Avoid collisions with the NULL pointer */
#define SECTOR_TO_PTR(s) ULONG_TO_PTR((s)+1)
#define PTR_TO_SECTOR(p) (PTR_TO_ULONG(p)-1)
static int btrfs_defrag(int fd) {
- struct btrfs_ioctl_vol_args data;
-
- zero(data);
- data.fd = fd;
+ struct btrfs_ioctl_vol_args data = { .fd = fd };
return ioctl(fd, BTRFS_IOC_DEFRAG, &data);
}
@@ -184,11 +183,10 @@ static unsigned long fd_first_block(int fd) {
struct {
struct fiemap fiemap;
struct fiemap_extent extent;
- } data;
-
- zero(data);
- data.fiemap.fm_length = ~0ULL;
- data.fiemap.fm_extent_count = 1;
+ } data = {
+ .fiemap.fm_length = ~0ULL,
+ .fiemap.fm_extent_count = 1,
+ };
if (ioctl(fd, FS_IOC_FIEMAP, &data) < 0)
return 0;
@@ -205,6 +203,7 @@ static unsigned long fd_first_block(int fd) {
struct item {
const char *path;
unsigned long block;
+ unsigned long bin;
};
static int qsort_compare(const void *a, const void *b) {
@@ -213,6 +212,13 @@ static int qsort_compare(const void *a, const void *b) {
i = a;
j = b;
+ /* sort by bin first */
+ if (i->bin < j->bin)
+ return -1;
+ if (i->bin > j->bin)
+ return 1;
+
+ /* then sort by sector */
if (i->block < j->block)
return -1;
if (i->block > j->block)
@@ -228,7 +234,7 @@ static int collect(const char *root) {
FD_INOTIFY, /* We get notifications to quit early via this fd */
_FD_MAX
};
- struct pollfd pollfd[_FD_MAX];
+ struct pollfd pollfd[_FD_MAX] = {};
int fanotify_fd = -1, signal_fd = -1, inotify_fd = -1, r = 0;
pid_t my_pid;
Hashmap *files = NULL;
@@ -250,6 +256,8 @@ static int collect(const char *root) {
goto finish;
}
+ starttime = now(CLOCK_MONOTONIC);
+
/* If there's no pack file yet we lower the kernel readahead
* so that mincore() is accurate. If there is a pack file
* already we assume it is accurate enough so that kernel
@@ -272,13 +280,15 @@ static int collect(const char *root) {
goto finish;
}
- if (!(files = hashmap_new(string_hash_func, string_compare_func))) {
+ files = hashmap_new(string_hash_func, string_compare_func);
+ if (!files) {
log_error("Failed to allocate set.");
r = -ENOMEM;
goto finish;
}
- if ((fanotify_fd = fanotify_init(FAN_CLOEXEC|FAN_NONBLOCK, O_RDONLY|O_LARGEFILE|O_CLOEXEC|O_NOATIME)) < 0) {
+ fanotify_fd = fanotify_init(FAN_CLOEXEC|FAN_NONBLOCK, O_RDONLY|O_LARGEFILE|O_CLOEXEC|O_NOATIME);
+ if (fanotify_fd < 0) {
log_error("Failed to create fanotify object: %m");
r = -errno;
goto finish;
@@ -290,7 +300,8 @@ static int collect(const char *root) {
goto finish;
}
- if ((inotify_fd = open_inotify()) < 0) {
+ inotify_fd = open_inotify();
+ if (inotify_fd < 0) {
r = inotify_fd;
goto finish;
}
@@ -299,7 +310,6 @@ static int collect(const char *root) {
my_pid = getpid();
- zero(pollfd);
pollfd[FD_FANOTIFY].fd = fanotify_fd;
pollfd[FD_FANOTIFY].events = POLLIN;
pollfd[FD_SIGNAL].fd = signal_fd;
@@ -447,11 +457,31 @@ static int collect(const char *root) {
free(p);
else {
unsigned long ul;
+ usec_t entrytime;
+ struct item *entry;
+
+ entry = new0(struct item, 1);
+ if (!entry) {
+ r = log_oom();
+ goto finish;
+ }
ul = fd_first_block(m->fd);
- if ((k = hashmap_put(files, p, SECTOR_TO_PTR(ul))) < 0) {
- log_warning("set_put() failed: %s", strerror(-k));
+ entrytime = now(CLOCK_MONOTONIC);
+
+ entry->block = ul;
+ entry->path = strdup(p);
+ if (!entry->path) {
+ free(entry);
+ r = log_oom();
+ goto finish;
+ }
+ entry->bin = (entrytime - starttime) / 2000000;
+
+ k = hashmap_put(files, p, entry);
+ if (k < 0) {
+ log_warning("hashmap_put() failed: %s", strerror(-k));
free(p);
}
}
@@ -476,7 +506,7 @@ done:
on_ssd = fs_on_ssd(root) > 0;
log_debug("On SSD: %s", yes_no(on_ssd));
- on_btrfs = statfs(root, &sfs) >= 0 && (long) sfs.f_type == (long) BTRFS_SUPER_MAGIC;
+ on_btrfs = statfs(root, &sfs) >= 0 && F_TYPE_CMP(sfs.f_type, BTRFS_SUPER_MAGIC);
log_debug("On btrfs: %s", yes_no(on_btrfs));
if (asprintf(&pack_fn_new, "%s/.readahead.new", root) < 0) {
@@ -518,8 +548,7 @@ done:
j = ordered;
HASHMAP_FOREACH_KEY(q, p, files, i) {
- j->path = p;
- j->block = PTR_TO_SECTOR(q);
+ memcpy(j, q, sizeof(struct item));
j++;
}
diff --git a/src/readahead/readahead-common.c b/src/readahead/readahead-common.c
index 41aaff0a3d..a234a89954 100644
--- a/src/readahead/readahead-common.c
+++ b/src/readahead/readahead-common.c
@@ -33,6 +33,7 @@
#include "readahead-common.h"
#include "util.h"
#include "missing.h"
+#include "fileio.h"
int file_verify(int fd, const char *fn, off_t file_size_max, struct stat *st) {
assert(fd >= 0);
@@ -215,16 +216,23 @@ bool enough_ram(void) {
return si.totalram > 127 * 1024*1024 / si.mem_unit;
}
+static void mkdirs(void) {
+ if (mkdir("/run/systemd", 0755) && errno != EEXIST)
+ log_warning("Failed to create /run/systemd: %m");
+ if (mkdir("/run/systemd/readahead", 0755) && errno != EEXIST)
+ log_warning("Failed to create /run/systemd: %m");
+}
+
int open_inotify(void) {
int fd;
- if ((fd = inotify_init1(IN_CLOEXEC|IN_NONBLOCK)) < 0) {
+ fd = inotify_init1(IN_CLOEXEC|IN_NONBLOCK);
+ if (fd < 0) {
log_error("Failed to create inotify handle: %m");
return -errno;
}
- mkdir("/run/systemd", 0755);
- mkdir("/run/systemd/readahead", 0755);
+ mkdirs();
if (inotify_add_watch(fd, "/run/systemd/readahead", IN_CREATE) < 0) {
log_error("Failed to watch /run/systemd/readahead: %m");
@@ -236,32 +244,28 @@ int open_inotify(void) {
}
ReadaheadShared *shared_get(void) {
- int fd;
+ int _cleanup_close_ fd = -1;
ReadaheadShared *m = NULL;
- mkdir("/run/systemd", 0755);
- mkdir("/run/systemd/readahead", 0755);
+ mkdirs();
- if ((fd = open("/run/systemd/readahead/shared", O_CREAT|O_RDWR|O_CLOEXEC, 0644)) < 0) {
+ fd = open("/run/systemd/readahead/shared", O_CREAT|O_RDWR|O_CLOEXEC, 0644);
+ if (fd < 0) {
log_error("Failed to create shared memory segment: %m");
- goto finish;
+ return NULL;
}
if (ftruncate(fd, sizeof(ReadaheadShared)) < 0) {
log_error("Failed to truncate shared memory segment: %m");
- goto finish;
+ return NULL;
}
- if ((m = mmap(NULL, sizeof(ReadaheadShared), PROT_WRITE|PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED) {
+ m = mmap(NULL, sizeof(ReadaheadShared), PROT_WRITE|PROT_READ, MAP_SHARED, fd, 0);
+ if (m == MAP_FAILED) {
log_error("Failed to mmap shared memory segment: %m");
- m = NULL;
- goto finish;
+ return NULL;
}
-finish:
- if (fd >= 0)
- close_nointr_nofail(fd);
-
return m;
}
@@ -315,7 +319,7 @@ int block_bump_request_nr(const char *p) {
goto finish;
}
- r = write_one_line_file(ap, line);
+ r = write_string_file(ap, line);
if (r < 0)
goto finish;
@@ -398,7 +402,7 @@ int block_set_readahead(const char *p, uint64_t bytes) {
goto finish;
}
- r = write_one_line_file(ap, line);
+ r = write_string_file(ap, line);
if (r < 0)
goto finish;
diff --git a/src/readahead/readahead.c b/src/readahead/readahead.c
index abeecc7634..29255c9f07 100644
--- a/src/readahead/readahead.c
+++ b/src/readahead/readahead.c
@@ -108,7 +108,7 @@ static int parse_argv(int argc, char *argv[]) {
}
case ARG_TIMEOUT:
- if (parse_usec(optarg, &arg_timeout) < 0 || arg_timeout <= 0) {
+ if (parse_sec(optarg, &arg_timeout) < 0 || arg_timeout <= 0) {
log_error("Failed to parse timeout %s.", optarg);
return -EINVAL;
}
diff --git a/src/readahead/sd-readahead.c b/src/readahead/sd-readahead.c
index d48cd76807..675d82cdd1 100644
--- a/src/readahead/sd-readahead.c
+++ b/src/readahead/sd-readahead.c
@@ -25,7 +25,7 @@
***/
#ifndef _GNU_SOURCE
-#define _GNU_SOURCE
+# define _GNU_SOURCE
#endif
#include <unistd.h>
@@ -38,15 +38,15 @@
#include "sd-readahead.h"
#if (__GNUC__ >= 4)
-#ifdef SD_EXPORT_SYMBOLS
+# ifdef SD_EXPORT_SYMBOLS
/* Export symbols */
-#define _sd_export_ __attribute__ ((visibility("default")))
-#else
+# define _sd_export_ __attribute__ ((visibility("default")))
+# else
/* Don't export the symbols */
-#define _sd_export_ __attribute__ ((visibility("hidden")))
-#endif
+# define _sd_export_ __attribute__ ((visibility("hidden")))
+# endif
#else
-#define _sd_export_
+# define _sd_export_
#endif
static int touch(const char *path) {
@@ -65,7 +65,7 @@ static int touch(const char *path) {
if (close(fd) >= 0)
break;
- if (errno != -EINTR)
+ if (errno != EINTR)
return -errno;
}
diff --git a/src/remount-fs/remount-fs.c b/src/remount-fs/remount-fs.c
index b436203b16..75eaed2f9e 100644
--- a/src/remount-fs/remount-fs.c
+++ b/src/remount-fs/remount-fs.c
@@ -131,10 +131,9 @@ int main(int argc, char *argv[]) {
}
while (!hashmap_isempty(pids)) {
- siginfo_t si;
+ siginfo_t si = {};
char *s;
- zero(si);
if (waitid(P_ALL, 0, &si, WEXITED) < 0) {
if (errno == EINTR)
diff --git a/src/reply-password/reply-password.c b/src/reply-password/reply-password.c
index a935d0f084..2f168985b4 100644
--- a/src/reply-password/reply-password.c
+++ b/src/reply-password/reply-password.c
@@ -41,14 +41,14 @@ static int send_on_socket(int fd, const char *socket_name, const void *packet, s
union {
struct sockaddr sa;
struct sockaddr_un un;
- } sa;
+ } sa = {
+ .un.sun_family = AF_UNIX,
+ };
assert(fd >= 0);
assert(socket_name);
assert(packet);
- zero(sa);
- sa.un.sun_family = AF_UNIX;
strncpy(sa.un.sun_path, socket_name, sizeof(sa.un.sun_path));
if (sendto(fd, packet, size, MSG_NOSIGNAL, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(socket_name)) < 0) {
diff --git a/src/shared/MurmurHash3.c b/src/shared/MurmurHash3.c
new file mode 100644
index 0000000000..aa1387373f
--- /dev/null
+++ b/src/shared/MurmurHash3.c
@@ -0,0 +1,349 @@
+//-----------------------------------------------------------------------------
+// MurmurHash3 was written by Austin Appleby, and is placed in the public
+// domain. The author hereby disclaims copyright to this source code.
+
+// Note - The x86 and x64 versions do _not_ produce the same results, as the
+// algorithms are optimized for their respective platforms. You can still
+// compile and run any of them on any platform, but your performance with the
+// non-native version will be less than optimal.
+
+#include "MurmurHash3.h"
+
+//-----------------------------------------------------------------------------
+// Platform-specific functions and macros
+
+// Microsoft Visual Studio
+
+#if defined(_MSC_VER)
+
+#define FORCE_INLINE __forceinline
+
+#include <stdlib.h>
+
+#define ROTL32(x,y) _rotl(x,y)
+#define ROTL64(x,y) _rotl64(x,y)
+
+#define BIG_CONSTANT(x) (x)
+
+// Other compilers
+
+#else // defined(_MSC_VER)
+
+#define FORCE_INLINE inline __attribute__((always_inline))
+
+static inline uint32_t rotl32 ( uint32_t x, int8_t r )
+{
+ return (x << r) | (x >> (32 - r));
+}
+
+static inline uint64_t rotl64 ( uint64_t x, int8_t r )
+{
+ return (x << r) | (x >> (64 - r));
+}
+
+#define ROTL32(x,y) rotl32(x,y)
+#define ROTL64(x,y) rotl64(x,y)
+
+#define BIG_CONSTANT(x) (x##LLU)
+
+#endif // !defined(_MSC_VER)
+
+//-----------------------------------------------------------------------------
+// Block read - if your platform needs to do endian-swapping or can only
+// handle aligned reads, do the conversion here
+
+static FORCE_INLINE uint32_t getblock32 ( const uint32_t * p, int i )
+{
+ return p[i];
+}
+
+static FORCE_INLINE uint64_t getblock64 ( const uint64_t * p, int i )
+{
+ return p[i];
+}
+
+//-----------------------------------------------------------------------------
+// Finalization mix - force all bits of a hash block to avalanche
+
+static FORCE_INLINE uint32_t fmix32 ( uint32_t h )
+{
+ h ^= h >> 16;
+ h *= 0x85ebca6b;
+ h ^= h >> 13;
+ h *= 0xc2b2ae35;
+ h ^= h >> 16;
+
+ return h;
+}
+
+//----------
+
+static FORCE_INLINE uint64_t fmix64 ( uint64_t k )
+{
+ k ^= k >> 33;
+ k *= BIG_CONSTANT(0xff51afd7ed558ccd);
+ k ^= k >> 33;
+ k *= BIG_CONSTANT(0xc4ceb9fe1a85ec53);
+ k ^= k >> 33;
+
+ return k;
+}
+
+//-----------------------------------------------------------------------------
+
+void MurmurHash3_x86_32 ( const void * key, size_t len,
+ uint32_t seed, void * out )
+{
+ const uint8_t * data = (const uint8_t*)key;
+ const int nblocks = len / 4;
+
+ uint32_t h1 = seed;
+
+ const uint32_t c1 = 0xcc9e2d51;
+ const uint32_t c2 = 0x1b873593;
+
+ const uint8_t * tail;
+ uint32_t k1;
+
+ //----------
+ // body
+
+ const uint32_t * blocks = (const uint32_t *)(data + nblocks*4);
+
+ for(int i = -nblocks; i; i++)
+ {
+ k1 = getblock32(blocks,i);
+
+ k1 *= c1;
+ k1 = ROTL32(k1,15);
+ k1 *= c2;
+
+ h1 ^= k1;
+ h1 = ROTL32(h1,13);
+ h1 = h1*5+0xe6546b64;
+ }
+
+ //----------
+ // tail
+
+ tail = (const uint8_t*)(data + nblocks*4);
+
+ k1 = 0;
+
+ switch(len & 3)
+ {
+ case 3: k1 ^= tail[2] << 16;
+ case 2: k1 ^= tail[1] << 8;
+ case 1: k1 ^= tail[0];
+ k1 *= c1; k1 = ROTL32(k1,15); k1 *= c2; h1 ^= k1;
+ };
+
+ //----------
+ // finalization
+
+ h1 ^= len;
+
+ h1 = fmix32(h1);
+
+ *(uint32_t*)out = h1;
+}
+
+//-----------------------------------------------------------------------------
+
+void MurmurHash3_x86_128 ( const void * key, const size_t len,
+ uint32_t seed, void * out )
+{
+ const uint8_t * data = (const uint8_t*)key;
+ const int nblocks = len / 16;
+
+ uint32_t h1 = seed;
+ uint32_t h2 = seed;
+ uint32_t h3 = seed;
+ uint32_t h4 = seed;
+
+ const uint32_t c1 = 0x239b961b;
+ const uint32_t c2 = 0xab0e9789;
+ const uint32_t c3 = 0x38b34ae5;
+ const uint32_t c4 = 0xa1e38b93;
+
+ const uint8_t * tail;
+
+ uint32_t k1;
+ uint32_t k2;
+ uint32_t k3;
+ uint32_t k4;
+
+ //----------
+ // body
+
+ const uint32_t * blocks = (const uint32_t *)(data + nblocks*16);
+
+ for(int i = -nblocks; i; i++)
+ {
+ k1 = getblock32(blocks,i*4+0);
+ k2 = getblock32(blocks,i*4+1);
+ k3 = getblock32(blocks,i*4+2);
+ k4 = getblock32(blocks,i*4+3);
+
+ k1 *= c1; k1 = ROTL32(k1,15); k1 *= c2; h1 ^= k1;
+
+ h1 = ROTL32(h1,19); h1 += h2; h1 = h1*5+0x561ccd1b;
+
+ k2 *= c2; k2 = ROTL32(k2,16); k2 *= c3; h2 ^= k2;
+
+ h2 = ROTL32(h2,17); h2 += h3; h2 = h2*5+0x0bcaa747;
+
+ k3 *= c3; k3 = ROTL32(k3,17); k3 *= c4; h3 ^= k3;
+
+ h3 = ROTL32(h3,15); h3 += h4; h3 = h3*5+0x96cd1c35;
+
+ k4 *= c4; k4 = ROTL32(k4,18); k4 *= c1; h4 ^= k4;
+
+ h4 = ROTL32(h4,13); h4 += h1; h4 = h4*5+0x32ac3b17;
+ }
+
+ //----------
+ // tail
+
+ tail = (const uint8_t*)(data + nblocks*16);
+
+ k1 = 0;
+ k2 = 0;
+ k3 = 0;
+ k4 = 0;
+
+ switch(len & 15)
+ {
+ case 15: k4 ^= tail[14] << 16;
+ case 14: k4 ^= tail[13] << 8;
+ case 13: k4 ^= tail[12] << 0;
+ k4 *= c4; k4 = ROTL32(k4,18); k4 *= c1; h4 ^= k4;
+
+ case 12: k3 ^= tail[11] << 24;
+ case 11: k3 ^= tail[10] << 16;
+ case 10: k3 ^= tail[ 9] << 8;
+ case 9: k3 ^= tail[ 8] << 0;
+ k3 *= c3; k3 = ROTL32(k3,17); k3 *= c4; h3 ^= k3;
+
+ case 8: k2 ^= tail[ 7] << 24;
+ case 7: k2 ^= tail[ 6] << 16;
+ case 6: k2 ^= tail[ 5] << 8;
+ case 5: k2 ^= tail[ 4] << 0;
+ k2 *= c2; k2 = ROTL32(k2,16); k2 *= c3; h2 ^= k2;
+
+ case 4: k1 ^= tail[ 3] << 24;
+ case 3: k1 ^= tail[ 2] << 16;
+ case 2: k1 ^= tail[ 1] << 8;
+ case 1: k1 ^= tail[ 0] << 0;
+ k1 *= c1; k1 = ROTL32(k1,15); k1 *= c2; h1 ^= k1;
+ };
+
+ //----------
+ // finalization
+
+ h1 ^= len; h2 ^= len; h3 ^= len; h4 ^= len;
+
+ h1 += h2; h1 += h3; h1 += h4;
+ h2 += h1; h3 += h1; h4 += h1;
+
+ h1 = fmix32(h1);
+ h2 = fmix32(h2);
+ h3 = fmix32(h3);
+ h4 = fmix32(h4);
+
+ h1 += h2; h1 += h3; h1 += h4;
+ h2 += h1; h3 += h1; h4 += h1;
+
+ ((uint32_t*)out)[0] = h1;
+ ((uint32_t*)out)[1] = h2;
+ ((uint32_t*)out)[2] = h3;
+ ((uint32_t*)out)[3] = h4;
+}
+
+//-----------------------------------------------------------------------------
+
+void MurmurHash3_x64_128 ( const void * key, const size_t len,
+ const uint32_t seed, void * out )
+{
+ const uint8_t * data = (const uint8_t*)key;
+ const int nblocks = len / 16;
+
+ uint64_t h1 = seed;
+ uint64_t h2 = seed;
+
+ const uint64_t c1 = BIG_CONSTANT(0x87c37b91114253d5);
+ const uint64_t c2 = BIG_CONSTANT(0x4cf5ad432745937f);
+
+ const uint8_t *tail;
+
+ uint64_t k1;
+ uint64_t k2;
+
+ //----------
+ // body
+
+ const uint64_t * blocks = (const uint64_t *)(data);
+
+ for(int i = 0; i < nblocks; i++)
+ {
+ k1 = getblock64(blocks,i*2+0);
+ k2 = getblock64(blocks,i*2+1);
+
+ k1 *= c1; k1 = ROTL64(k1,31); k1 *= c2; h1 ^= k1;
+
+ h1 = ROTL64(h1,27); h1 += h2; h1 = h1*5+0x52dce729;
+
+ k2 *= c2; k2 = ROTL64(k2,33); k2 *= c1; h2 ^= k2;
+
+ h2 = ROTL64(h2,31); h2 += h1; h2 = h2*5+0x38495ab5;
+ }
+
+ //----------
+ // tail
+
+ tail = (const uint8_t*)(data + nblocks*16);
+
+ k1 = 0;
+ k2 = 0;
+
+ switch(len & 15)
+ {
+ case 15: k2 ^= (uint64_t)(tail[14]) << 48;
+ case 14: k2 ^= (uint64_t)(tail[13]) << 40;
+ case 13: k2 ^= (uint64_t)(tail[12]) << 32;
+ case 12: k2 ^= (uint64_t)(tail[11]) << 24;
+ case 11: k2 ^= (uint64_t)(tail[10]) << 16;
+ case 10: k2 ^= (uint64_t)(tail[ 9]) << 8;
+ case 9: k2 ^= (uint64_t)(tail[ 8]) << 0;
+ k2 *= c2; k2 = ROTL64(k2,33); k2 *= c1; h2 ^= k2;
+
+ case 8: k1 ^= (uint64_t)(tail[ 7]) << 56;
+ case 7: k1 ^= (uint64_t)(tail[ 6]) << 48;
+ case 6: k1 ^= (uint64_t)(tail[ 5]) << 40;
+ case 5: k1 ^= (uint64_t)(tail[ 4]) << 32;
+ case 4: k1 ^= (uint64_t)(tail[ 3]) << 24;
+ case 3: k1 ^= (uint64_t)(tail[ 2]) << 16;
+ case 2: k1 ^= (uint64_t)(tail[ 1]) << 8;
+ case 1: k1 ^= (uint64_t)(tail[ 0]) << 0;
+ k1 *= c1; k1 = ROTL64(k1,31); k1 *= c2; h1 ^= k1;
+ };
+
+ //----------
+ // finalization
+
+ h1 ^= len; h2 ^= len;
+
+ h1 += h2;
+ h2 += h1;
+
+ h1 = fmix64(h1);
+ h2 = fmix64(h2);
+
+ h1 += h2;
+ h2 += h1;
+
+ ((uint64_t*)out)[0] = h1;
+ ((uint64_t*)out)[1] = h2;
+}
+
+//-----------------------------------------------------------------------------
diff --git a/src/shared/MurmurHash3.h b/src/shared/MurmurHash3.h
new file mode 100644
index 0000000000..07c474eba4
--- /dev/null
+++ b/src/shared/MurmurHash3.h
@@ -0,0 +1,38 @@
+//-----------------------------------------------------------------------------
+// MurmurHash3 was written by Austin Appleby, and is placed in the public
+// domain. The author hereby disclaims copyright to this source code.
+
+#ifndef _MURMURHASH3_H_
+#define _MURMURHASH3_H_
+
+//-----------------------------------------------------------------------------
+// Platform-specific functions and macros
+
+// Microsoft Visual Studio
+
+#if defined(_MSC_VER)
+
+typedef unsigned char uint8_t;
+typedef unsigned long uint32_t;
+typedef unsigned __int64 uint64_t;
+
+// Other compilers
+
+#else // defined(_MSC_VER)
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#endif // !defined(_MSC_VER)
+
+//-----------------------------------------------------------------------------
+
+void MurmurHash3_x86_32 ( const void * key, size_t len, uint32_t seed, void * out );
+
+void MurmurHash3_x86_128 ( const void * key, size_t len, uint32_t seed, void * out );
+
+void MurmurHash3_x64_128 ( const void * key, size_t len, uint32_t seed, void * out );
+
+//-----------------------------------------------------------------------------
+
+#endif // _MURMURHASH3_H_
diff --git a/src/shared/acl-util.c b/src/shared/acl-util.c
index d1eb6f2268..48bb12f46b 100644
--- a/src/shared/acl-util.c
+++ b/src/shared/acl-util.c
@@ -3,7 +3,7 @@
/***
This file is part of systemd.
- Copyright 2011 Lennart Poettering
+ Copyright 2011,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
@@ -26,6 +26,8 @@
#include <stdbool.h>
#include "acl-util.h"
+#include "util.h"
+#include "strv.h"
int acl_find_uid(acl_t acl, uid_t uid, acl_entry_t *entry) {
acl_entry_t i;
@@ -66,3 +68,59 @@ int acl_find_uid(acl_t acl, uid_t uid, acl_entry_t *entry) {
return 0;
}
+
+int search_acl_groups(char*** dst, const char* path, bool* belong) {
+ acl_t acl;
+
+ assert(path);
+ assert(belong);
+
+ acl = acl_get_file(path, ACL_TYPE_DEFAULT);
+ if (acl) {
+ acl_entry_t entry;
+ int r;
+
+ r = acl_get_entry(acl, ACL_FIRST_ENTRY, &entry);
+ while (r > 0) {
+ acl_tag_t tag;
+ gid_t *gid;
+ char *name;
+
+ r = acl_get_tag_type(entry, &tag);
+ if (r < 0)
+ break;
+
+ if (tag != ACL_GROUP)
+ goto next;
+
+ gid = acl_get_qualifier(entry);
+ if (!gid)
+ break;
+
+ if (in_gid(*gid) > 0) {
+ *belong = true;
+ break;
+ }
+
+ name = gid_to_name(*gid);
+ if (!name) {
+ acl_free(acl);
+ return log_oom();
+ }
+
+ r = strv_push(dst, name);
+ if (r < 0) {
+ free(name);
+ acl_free(acl);
+ return log_oom();
+ }
+
+ next:
+ r = acl_get_entry(acl, ACL_NEXT_ENTRY, &entry);
+ }
+
+ acl_free(acl);
+ }
+
+ return 0;
+}
diff --git a/src/shared/acl-util.h b/src/shared/acl-util.h
index 31fbbcd510..23090d9984 100644
--- a/src/shared/acl-util.h
+++ b/src/shared/acl-util.h
@@ -21,4 +21,7 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <stdbool.h>
+
int acl_find_uid(acl_t acl, uid_t uid, acl_entry_t *entry);
+int search_acl_groups(char*** dst, const char* path, bool* belong);
diff --git a/src/shared/ask-password-api.c b/src/shared/ask-password-api.c
index 8a0fb89a84..4557155d45 100644
--- a/src/shared/ask-password-api.c
+++ b/src/shared/ask-password-api.c
@@ -109,7 +109,6 @@ int ask_password_tty(
}
zero(pollfd);
-
pollfd[POLL_TTY].fd = ttyfd >= 0 ? ttyfd : STDIN_FILENO;
pollfd[POLL_TTY].events = POLLIN;
pollfd[POLL_INOTIFY].fd = notify;
@@ -248,25 +247,25 @@ static int create_socket(char **name) {
union {
struct sockaddr sa;
struct sockaddr_un un;
- } sa;
+ } sa = {
+ .un.sun_family = AF_UNIX,
+ };
int one = 1, r;
char *c;
- mode_t u;
assert(name);
- if ((fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0)) < 0) {
+ fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
+ if (fd < 0) {
log_error("socket() failed: %m");
return -errno;
}
- zero(sa);
- sa.un.sun_family = AF_UNIX;
snprintf(sa.un.sun_path, sizeof(sa.un.sun_path)-1, "/run/systemd/ask-password/sck.%llu", random_ull());
- u = umask(0177);
- r = bind(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path));
- umask(u);
+ RUN_WITH_UMASK(0177) {
+ r = bind(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path));
+ }
if (r < 0) {
r = -errno;
@@ -280,7 +279,8 @@ static int create_socket(char **name) {
goto fail;
}
- if (!(c = strdup(sa.un.sun_path))) {
+ c = strdup(sa.un.sun_path);
+ if (!c) {
r = log_oom();
goto fail;
}
@@ -315,7 +315,6 @@ int ask_password_agent(
int socket_fd = -1, signal_fd = -1;
sigset_t mask, oldmask;
struct pollfd pollfd[_FD_MAX];
- mode_t u;
assert(_passphrases);
@@ -325,9 +324,9 @@ int ask_password_agent(
mkdir_p_label("/run/systemd/ask-password", 0755);
- u = umask(0022);
- fd = mkostemp(temp, O_CLOEXEC|O_CREAT|O_WRONLY);
- umask(u);
+ RUN_WITH_UMASK(0022) {
+ fd = mkostemp(temp, O_CLOEXEC|O_CREAT|O_WRONLY);
+ }
if (fd < 0) {
log_error("Failed to create password file: %m");
diff --git a/src/shared/audit.c b/src/shared/audit.c
index e5c483ab08..97560cc9a3 100644
--- a/src/shared/audit.c
+++ b/src/shared/audit.c
@@ -33,6 +33,8 @@
#include "audit.h"
#include "util.h"
#include "log.h"
+#include "fileio.h"
+#include "virt.h"
int audit_session_from_pid(pid_t pid, uint32_t *id) {
char *s;
@@ -44,6 +46,10 @@ int audit_session_from_pid(pid_t pid, uint32_t *id) {
if (have_effective_cap(CAP_AUDIT_CONTROL) <= 0)
return -ENOENT;
+ /* Audit doesn't support containers right now */
+ if (detect_container(NULL) > 0)
+ return -ENOTSUP;
+
if (pid == 0)
r = read_one_line_file("/proc/self/sessionid", &s);
else {
@@ -89,6 +95,10 @@ int audit_loginuid_from_pid(pid_t pid, uid_t *uid) {
if (have_effective_cap(CAP_AUDIT_CONTROL) <= 0)
return -ENOENT;
+ /* Audit doesn't support containers right now */
+ if (detect_container(NULL) > 0)
+ return -ENOTSUP;
+
if (pid == 0)
r = read_one_line_file("/proc/self/loginuid", &s);
else {
diff --git a/src/shared/calendarspec.c b/src/shared/calendarspec.c
index c2eae3f139..7979e2384f 100644
--- a/src/shared/calendarspec.c
+++ b/src/shared/calendarspec.c
@@ -133,7 +133,7 @@ int calendar_spec_normalize(CalendarSpec *c) {
return 0;
}
-static bool chain_valid(CalendarComponent *c, int from, int to) {
+_pure_ static bool chain_valid(CalendarComponent *c, int from, int to) {
if (!c)
return true;
@@ -149,7 +149,7 @@ static bool chain_valid(CalendarComponent *c, int from, int to) {
return true;
}
-bool calendar_spec_valid(CalendarSpec *c) {
+_pure_ bool calendar_spec_valid(CalendarSpec *c) {
assert(c);
if (c->weekdays_bits > 127)
@@ -391,7 +391,7 @@ static int prepend_component(const char **p, CalendarComponent **c) {
errno = 0;
value = strtoul(*p, &e, 10);
- if (errno != 0)
+ if (errno > 0)
return -errno;
if (e == *p)
return -EINVAL;
@@ -400,7 +400,7 @@ static int prepend_component(const char **p, CalendarComponent **c) {
if (*e == '/') {
repeat = strtoul(e+1, &ee, 10);
- if (errno != 0)
+ if (errno > 0)
return -errno;
if (ee == e+1)
return -EINVAL;
@@ -653,7 +653,7 @@ int calendar_spec_from_string(const char *p, CalendarSpec **spec) {
if (!c)
return -ENOMEM;
- if (strcasecmp(p, "hourly") == 0) {
+ if (strcaseeq(p, "hourly")) {
r = const_chain(0, &c->minute);
if (r < 0)
goto fail;
@@ -661,7 +661,7 @@ int calendar_spec_from_string(const char *p, CalendarSpec **spec) {
if (r < 0)
goto fail;
- } else if (strcasecmp(p, "daily") == 0) {
+ } else if (strcaseeq(p, "daily")) {
r = const_chain(0, &c->hour);
if (r < 0)
goto fail;
@@ -672,7 +672,7 @@ int calendar_spec_from_string(const char *p, CalendarSpec **spec) {
if (r < 0)
goto fail;
- } else if (strcasecmp(p, "monthly") == 0) {
+ } else if (strcaseeq(p, "monthly")) {
r = const_chain(1, &c->day);
if (r < 0)
goto fail;
@@ -686,7 +686,7 @@ int calendar_spec_from_string(const char *p, CalendarSpec **spec) {
if (r < 0)
goto fail;
- } else if (strcasecmp(p, "weekly") == 0) {
+ } else if (strcaseeq(p, "weekly")) {
c->weekdays_bits = 1;
diff --git a/src/shared/capability.c b/src/shared/capability.c
index 9b743e86d0..321952067d 100644
--- a/src/shared/capability.c
+++ b/src/shared/capability.c
@@ -34,6 +34,7 @@
#include "capability.h"
#include "util.h"
#include "log.h"
+#include "fileio.h"
int have_effective_cap(int value) {
cap_t cap;
@@ -203,7 +204,7 @@ static int drop_from_file(const char *fn, uint64_t drop) {
if (asprintf(&p, "%u %u", lo, hi) < 0)
return -ENOMEM;
- r = write_one_line_file(fn, p);
+ r = write_string_file(fn, p);
free(p);
return r;
diff --git a/src/shared/cgroup-label.c b/src/shared/cgroup-label.c
index beeeec5830..5b5163c250 100644
--- a/src/shared/cgroup-label.c
+++ b/src/shared/cgroup-label.c
@@ -36,47 +36,42 @@
#include "util.h"
#include "mkdir.h"
-int cg_create(const char *controller, const char *path) {
- char *fs;
+int cg_create(const char *controller, const char *path, const char *suffix) {
+ _cleanup_free_ char *fs = NULL;
int r;
- assert(controller);
- assert(path);
-
- r = cg_get_path_and_check(controller, path, NULL, &fs);
+ r = cg_get_path_and_check(controller, path, suffix, &fs);
if (r < 0)
return r;
r = mkdir_parents_label(fs, 0755);
+ if (r < 0)
+ return r;
- if (r >= 0) {
- if (mkdir(fs, 0755) >= 0)
- r = 1;
- else if (errno == EEXIST)
- r = 0;
- else
- r = -errno;
- }
+ if (mkdir(fs, 0755) < 0) {
- free(fs);
+ if (errno == EEXIST)
+ return 0;
- return r;
+ return -errno;
+ }
+
+ return 1;
}
int cg_create_and_attach(const char *controller, const char *path, pid_t pid) {
int r, q;
- assert(controller);
- assert(path);
assert(pid >= 0);
- if ((r = cg_create(controller, path)) < 0)
+ r = cg_create(controller, path, NULL);
+ if (r < 0)
return r;
- if ((q = cg_attach(controller, path, pid)) < 0)
+ q = cg_attach(controller, path, pid);
+ if (q < 0)
return q;
/* This does not remove the cgroup on failure */
-
return r;
}
diff --git a/src/shared/cgroup-show.c b/src/shared/cgroup-show.c
index 2b79f370f9..83cc0731b8 100644
--- a/src/shared/cgroup-show.c
+++ b/src/shared/cgroup-show.c
@@ -40,18 +40,7 @@ static int compare(const void *a, const void *b) {
return 0;
}
-static unsigned ilog10(unsigned long ul) {
- int n = 0;
-
- while (ul > 0) {
- n++;
- ul /= 10;
- }
-
- return n;
-}
-
-static void show_pid_array(int pids[], unsigned n_pids, const char *prefix, unsigned n_columns, bool extra, bool more, bool kernel_threads) {
+static void show_pid_array(int pids[], unsigned n_pids, const char *prefix, unsigned n_columns, bool extra, bool more, bool kernel_threads, OutputFlags flags) {
unsigned i, m, pid_width;
pid_t biggest = 0;
@@ -71,16 +60,19 @@ static void show_pid_array(int pids[], unsigned n_pids, const char *prefix, unsi
pids[m++] = pids[i];
}
n_pids = m;
- pid_width = ilog10(biggest);
+ pid_width = DECIMAL_STR_WIDTH(biggest);
/* And sort */
qsort(pids, n_pids, sizeof(pid_t), compare);
- if (n_columns > pid_width+2)
- n_columns -= pid_width+2;
- else
- n_columns = 20;
-
+ if(flags & OUTPUT_FULL_WIDTH)
+ n_columns = 0;
+ else {
+ if (n_columns > pid_width+2)
+ n_columns -= pid_width+2;
+ else
+ n_columns = 20;
+ }
for (i = 0; i < n_pids; i++) {
char *t = NULL;
@@ -99,22 +91,22 @@ static void show_pid_array(int pids[], unsigned n_pids, const char *prefix, unsi
}
-static int show_cgroup_one_by_path(const char *path, const char *prefix, unsigned n_columns, bool more, bool kernel_threads) {
+static int show_cgroup_one_by_path(const char *path, const char *prefix, unsigned n_columns, bool more, bool kernel_threads, OutputFlags flags) {
char *fn;
- FILE *f;
+ _cleanup_fclose_ FILE *f = NULL;
size_t n = 0, n_allocated = 0;
- pid_t *pids = NULL;
- char *p;
+ _cleanup_free_ pid_t *pids = NULL;
+ char *p = NULL;
pid_t pid;
int r;
- r = cg_fix_path(path, &p);
+ r = cg_mangle_path(path, &p);
if (r < 0)
return r;
- r = asprintf(&fn, "%s/cgroup.procs", p);
+ fn = strappend(p, "/cgroup.procs");
free(p);
- if (r < 0)
+ if (!fn)
return -ENOMEM;
f = fopen(fn, "re");
@@ -133,10 +125,8 @@ static int show_cgroup_one_by_path(const char *path, const char *prefix, unsigne
n_allocated = MAX(16U, n*2U);
npids = realloc(pids, sizeof(pid_t) * n_allocated);
- if (!npids) {
- r = -ENOMEM;
- goto finish;
- }
+ if (!npids)
+ return -ENOMEM;
pids = npids;
}
@@ -146,26 +136,18 @@ static int show_cgroup_one_by_path(const char *path, const char *prefix, unsigne
}
if (r < 0)
- goto finish;
+ return r;
if (n > 0)
- show_pid_array(pids, n, prefix, n_columns, false, more, kernel_threads);
-
- r = 0;
-
-finish:
- free(pids);
-
- if (f)
- fclose(f);
+ show_pid_array(pids, n, prefix, n_columns, false, more, kernel_threads, flags);
- return r;
+ return 0;
}
-int show_cgroup_by_path(const char *path, const char *prefix, unsigned n_columns, bool kernel_threads, bool all) {
- DIR *d;
- char *last = NULL;
- char *p1 = NULL, *p2 = NULL, *fn = NULL, *gn = NULL;
+int show_cgroup_by_path(const char *path, const char *prefix, unsigned n_columns, bool kernel_threads, OutputFlags flags) {
+ _cleanup_free_ char *fn = NULL, *p1 = NULL, *last = NULL, *p2 = NULL;
+ _cleanup_closedir_ DIR *d = NULL;
+ char *gn = NULL;
bool shown_pids = false;
int r;
@@ -177,33 +159,27 @@ int show_cgroup_by_path(const char *path, const char *prefix, unsigned n_columns
if (!prefix)
prefix = "";
- r = cg_fix_path(path, &fn);
+ r = cg_mangle_path(path, &fn);
if (r < 0)
return r;
d = opendir(fn);
- if (!d) {
- free(fn);
+ if (!d)
return -errno;
- }
while ((r = cg_read_subgroup(d, &gn)) > 0) {
- char *k;
+ _cleanup_free_ char *k = NULL;
- r = asprintf(&k, "%s/%s", fn, gn);
+ k = strjoin(fn, "/", gn, NULL);
free(gn);
- if (r < 0) {
- r = -ENOMEM;
- goto finish;
- }
+ if (!k)
+ return -ENOMEM;
- if (!all && cg_is_empty_recursive(NULL, k, false) > 0) {
- free(k);
+ if (!(flags & OUTPUT_SHOW_ALL) && cg_is_empty_recursive(NULL, k, false) > 0)
continue;
- }
if (!shown_pids) {
- show_cgroup_one_by_path(path, prefix, n_columns, true, kernel_threads);
+ show_cgroup_one_by_path(path, prefix, n_columns, true, kernel_threads, flags);
shown_pids = true;
}
@@ -213,25 +189,23 @@ int show_cgroup_by_path(const char *path, const char *prefix, unsigned n_columns
if (!p1) {
p1 = strappend(prefix, draw_special_char(DRAW_TREE_VERT));
- if (!p1) {
- free(k);
- r = -ENOMEM;
- goto finish;
- }
+ if (!p1)
+ return -ENOMEM;
}
- show_cgroup_by_path(last, p1, n_columns-2, kernel_threads, all);
+ show_cgroup_by_path(last, p1, n_columns-2, kernel_threads, flags);
free(last);
}
last = k;
+ k = NULL;
}
if (r < 0)
- goto finish;
+ return r;
if (!shown_pids)
- show_cgroup_one_by_path(path, prefix, n_columns, !!last, kernel_threads);
+ show_cgroup_one_by_path(path, prefix, n_columns, !!last, kernel_threads, flags);
if (last) {
printf("%s%s%s\n", prefix, draw_special_char(DRAW_TREE_RIGHT),
@@ -239,47 +213,31 @@ int show_cgroup_by_path(const char *path, const char *prefix, unsigned n_columns
if (!p2) {
p2 = strappend(prefix, " ");
- if (!p2) {
- r = -ENOMEM;
- goto finish;
- }
+ if (!p2)
+ return -ENOMEM;
}
- show_cgroup_by_path(last, p2, n_columns-2, kernel_threads, all);
+ show_cgroup_by_path(last, p2, n_columns-2, kernel_threads, flags);
}
- r = 0;
-
-finish:
- free(p1);
- free(p2);
- free(last);
- free(fn);
-
- closedir(d);
-
- return r;
+ return 0;
}
-int show_cgroup(const char *controller, const char *path, const char *prefix, unsigned n_columns, bool kernel_threads, bool all) {
- char *p;
+int show_cgroup(const char *controller, const char *path, const char *prefix, unsigned n_columns, bool kernel_threads, OutputFlags flags) {
+ _cleanup_free_ char *p = NULL;
int r;
- assert(controller);
assert(path);
r = cg_get_path(controller, path, NULL, &p);
if (r < 0)
return r;
- r = show_cgroup_by_path(p, prefix, n_columns, kernel_threads, all);
- free(p);
-
- return r;
+ return show_cgroup_by_path(p, prefix, n_columns, kernel_threads, flags);
}
-static int show_extra_pids(const char *controller, const char *path, const char *prefix, unsigned n_columns, const pid_t pids[], unsigned n_pids) {
- pid_t *copy;
+static int show_extra_pids(const char *controller, const char *path, const char *prefix, unsigned n_columns, const pid_t pids[], unsigned n_pids, OutputFlags flags) {
+ _cleanup_free_ pid_t *copy = NULL;
unsigned i, j;
int r;
@@ -292,21 +250,18 @@ static int show_extra_pids(const char *controller, const char *path, const char
if (n_columns <= 0)
n_columns = columns();
- if (!prefix)
- prefix = "";
+ prefix = strempty(prefix);
copy = new(pid_t, n_pids);
if (!copy)
return -ENOMEM;
for (i = 0, j = 0; i < n_pids; i++) {
- char *k;
+ _cleanup_free_ char *k = NULL;
- r = cg_get_by_pid(controller, pids[i], &k);
- if (r < 0) {
- free(copy);
+ r = cg_pid_get_path(controller, pids[i], &k);
+ if (r < 0)
return r;
- }
if (path_startswith(k, path))
continue;
@@ -314,28 +269,26 @@ static int show_extra_pids(const char *controller, const char *path, const char
copy[j++] = pids[i];
}
- show_pid_array(copy, j, prefix, n_columns, true, false, false);
+ show_pid_array(copy, j, prefix, n_columns, true, false, false, flags);
- free(copy);
return 0;
}
-int show_cgroup_and_extra(const char *controller, const char *path, const char *prefix, unsigned n_columns, bool kernel_threads, bool all, const pid_t extra_pids[], unsigned n_extra_pids) {
+int show_cgroup_and_extra(const char *controller, const char *path, const char *prefix, unsigned n_columns, bool kernel_threads, const pid_t extra_pids[], unsigned n_extra_pids, OutputFlags flags) {
int r;
- assert(controller);
assert(path);
- r = show_cgroup(controller, path, prefix, n_columns, kernel_threads, all);
+ r = show_cgroup(controller, path, prefix, n_columns, kernel_threads, flags);
if (r < 0)
return r;
- return show_extra_pids(controller, path, prefix, n_columns, extra_pids, n_extra_pids);
+ return show_extra_pids(controller, path, prefix, n_columns, extra_pids, n_extra_pids, flags);
}
-int show_cgroup_and_extra_by_spec(const char *spec, const char *prefix, unsigned n_columns, bool kernel_threads, bool all, const pid_t extra_pids[], unsigned n_extra_pids) {
- int r;
+int show_cgroup_and_extra_by_spec(const char *spec, const char *prefix, unsigned n_columns, bool kernel_threads, const pid_t extra_pids[], unsigned n_extra_pids, OutputFlags flags) {
_cleanup_free_ char *controller = NULL, *path = NULL;
+ int r;
assert(spec);
@@ -343,5 +296,5 @@ int show_cgroup_and_extra_by_spec(const char *spec, const char *prefix, unsigned
if (r < 0)
return r;
- return show_cgroup_and_extra(controller, path, prefix, n_columns, kernel_threads, all, extra_pids, n_extra_pids);
+ return show_cgroup_and_extra(controller, path, prefix, n_columns, kernel_threads, extra_pids, n_extra_pids, flags);
}
diff --git a/src/shared/cgroup-show.h b/src/shared/cgroup-show.h
index dba900a153..72bc8a650f 100644
--- a/src/shared/cgroup-show.h
+++ b/src/shared/cgroup-show.h
@@ -24,11 +24,13 @@
#include <stdbool.h>
#include <sys/types.h>
+#include "util.h"
+#include "logs-show.h"
-int show_cgroup_by_path(const char *path, const char *prefix, unsigned columns, bool kernel_threads, bool all);
-int show_cgroup(const char *controller, const char *path, const char *prefix, unsigned columns, bool kernel_threads, bool all);
+int show_cgroup_by_path(const char *path, const char *prefix, unsigned columns, bool kernel_threads, OutputFlags flags);
+int show_cgroup(const char *controller, const char *path, const char *prefix, unsigned columns, bool kernel_threads, OutputFlags flags);
-int show_cgroup_and_extra_by_spec(const char *spec, const char *prefix, unsigned n_columns, bool kernel_threads, bool all, const pid_t extra_pids[], unsigned n_extra_pids);
-int show_cgroup_and_extra(const char *controller, const char *path, const char *prefix, unsigned n_columns, bool kernel_threads, bool all, const pid_t extra_pids[], unsigned n_extra_pids);
+int show_cgroup_and_extra_by_spec(const char *spec, const char *prefix, unsigned n_columns, bool kernel_threads, const pid_t extra_pids[], unsigned n_extra_pids, OutputFlags flags);
+int show_cgroup_and_extra(const char *controller, const char *path, const char *prefix, unsigned n_columns, bool kernel_threads, const pid_t extra_pids[], unsigned n_extra_pids, OutputFlags flags);
#endif
diff --git a/src/shared/cgroup-util.c b/src/shared/cgroup-util.c
index 18cbf0412a..43c415d760 100644
--- a/src/shared/cgroup-util.c
+++ b/src/shared/cgroup-util.c
@@ -36,13 +36,14 @@
#include "util.h"
#include "path-util.h"
#include "strv.h"
+#include "unit-name.h"
+#include "fileio.h"
int cg_enumerate_processes(const char *controller, const char *path, FILE **_f) {
- char *fs;
- int r;
+ _cleanup_free_ char *fs = NULL;
FILE *f;
+ int r;
- assert(path);
assert(_f);
r = cg_get_path(controller, path, "cgroup.procs", &fs);
@@ -50,8 +51,6 @@ int cg_enumerate_processes(const char *controller, const char *path, FILE **_f)
return r;
f = fopen(fs, "re");
- free(fs);
-
if (!f)
return -errno;
@@ -60,11 +59,10 @@ int cg_enumerate_processes(const char *controller, const char *path, FILE **_f)
}
int cg_enumerate_tasks(const char *controller, const char *path, FILE **_f) {
- char *fs;
- int r;
+ _cleanup_free_ char *fs = NULL;
FILE *f;
+ int r;
- assert(path);
assert(_f);
r = cg_get_path(controller, path, "tasks", &fs);
@@ -72,8 +70,6 @@ int cg_enumerate_tasks(const char *controller, const char *path, FILE **_f) {
return r;
f = fopen(fs, "re");
- free(fs);
-
if (!f)
return -errno;
@@ -87,6 +83,9 @@ int cg_read_pid(FILE *f, pid_t *_pid) {
/* Note that the cgroup.procs might contain duplicates! See
* cgroups.txt for details. */
+ assert(f);
+ assert(_pid);
+
errno = 0;
if (fscanf(f, "%lu", &ul) != 1) {
@@ -104,11 +103,10 @@ int cg_read_pid(FILE *f, pid_t *_pid) {
}
int cg_enumerate_subgroups(const char *controller, const char *path, DIR **_d) {
- char *fs;
+ _cleanup_free_ char *fs = NULL;
int r;
DIR *d;
- assert(path);
assert(_d);
/* This is not recursive! */
@@ -118,8 +116,6 @@ int cg_enumerate_subgroups(const char *controller, const char *path, DIR **_d) {
return r;
d = opendir(fs);
- free(fs);
-
if (!d)
return -errno;
@@ -131,9 +127,9 @@ int cg_read_subgroup(DIR *d, char **fn) {
struct dirent *de;
assert(d);
+ assert(fn);
- errno = 0;
- while ((de = readdir(d))) {
+ FOREACH_DIRENT(de, d, return -errno) {
char *b;
if (de->d_type != DT_DIR)
@@ -143,21 +139,19 @@ int cg_read_subgroup(DIR *d, char **fn) {
streq(de->d_name, ".."))
continue;
- if (!(b = strdup(de->d_name)))
+ b = strdup(de->d_name);
+ if (!b)
return -ENOMEM;
*fn = b;
return 1;
}
- if (errno)
- return -errno;
-
return 0;
}
int cg_rmdir(const char *controller, const char *path, bool honour_sticky) {
- char *p;
+ _cleanup_free_ char *p = NULL;
int r;
r = cg_get_path(controller, path, NULL, &p);
@@ -170,61 +164,59 @@ int cg_rmdir(const char *controller, const char *path, bool honour_sticky) {
/* If the sticky bit is set don't remove the directory */
tasks = strappend(p, "/tasks");
- if (!tasks) {
- free(p);
+ if (!tasks)
return -ENOMEM;
- }
r = file_is_priv_sticky(tasks);
free(tasks);
- if (r > 0) {
- free(p);
+ if (r > 0)
return 0;
- }
}
r = rmdir(p);
- free(p);
+ if (r < 0 && errno != ENOENT)
+ return -errno;
- return (r < 0 && errno != ENOENT) ? -errno : 0;
+ return 0;
}
int cg_kill(const char *controller, const char *path, int sig, bool sigcont, bool ignore_self, Set *s) {
+ _cleanup_set_free_ Set *allocated_set = NULL;
bool done = false;
int r, ret = 0;
pid_t my_pid;
- FILE *f = NULL;
- Set *allocated_set = NULL;
- assert(controller);
- assert(path);
assert(sig >= 0);
/* This goes through the tasks list and kills them all. This
* is repeated until no further processes are added to the
* tasks list, to properly handle forking processes */
- if (!s)
- if (!(s = allocated_set = set_new(trivial_hash_func, trivial_compare_func)))
+ if (!s) {
+ s = allocated_set = set_new(trivial_hash_func, trivial_compare_func);
+ if (!s)
return -ENOMEM;
+ }
my_pid = getpid();
do {
+ _cleanup_fclose_ FILE *f = NULL;
pid_t pid = 0;
done = true;
- if ((r = cg_enumerate_processes(controller, path, &f)) < 0) {
+ r = cg_enumerate_processes(controller, path, &f);
+ if (r < 0) {
if (ret >= 0 && r != -ENOENT)
- ret = r;
+ return r;
- goto finish;
+ return ret;
}
while ((r = cg_read_pid(f, &pid)) > 0) {
- if (pid == my_pid && ignore_self)
+ if (ignore_self && pid == my_pid)
continue;
if (set_get(s, LONG_TO_PTR(pid)) == LONG_TO_PTR(pid))
@@ -245,100 +237,77 @@ int cg_kill(const char *controller, const char *path, int sig, bool sigcont, boo
done = false;
- if ((r = set_put(s, LONG_TO_PTR(pid))) < 0) {
+ r = set_put(s, LONG_TO_PTR(pid));
+ if (r < 0) {
if (ret >= 0)
- ret = r;
+ return r;
- goto finish;
+ return ret;
}
}
if (r < 0) {
if (ret >= 0)
- ret = r;
+ return r;
- goto finish;
+ return ret;
}
- fclose(f);
- f = NULL;
-
/* To avoid racing against processes which fork
* quicker than we can kill them we repeat this until
* no new pids need to be killed. */
} while (!done);
-finish:
- if (allocated_set)
- set_free(allocated_set);
-
- if (f)
- fclose(f);
-
return ret;
}
int cg_kill_recursive(const char *controller, const char *path, int sig, bool sigcont, bool ignore_self, bool rem, Set *s) {
+ _cleanup_set_free_ Set *allocated_set = NULL;
+ _cleanup_closedir_ DIR *d = NULL;
int r, ret = 0;
- DIR *d = NULL;
char *fn;
- Set *allocated_set = NULL;
assert(path);
- assert(controller);
assert(sig >= 0);
- if (!s)
- if (!(s = allocated_set = set_new(trivial_hash_func, trivial_compare_func)))
+ if (!s) {
+ s = allocated_set = set_new(trivial_hash_func, trivial_compare_func);
+ if (!s)
return -ENOMEM;
+ }
ret = cg_kill(controller, path, sig, sigcont, ignore_self, s);
- if ((r = cg_enumerate_subgroups(controller, path, &d)) < 0) {
+ r = cg_enumerate_subgroups(controller, path, &d);
+ if (r < 0) {
if (ret >= 0 && r != -ENOENT)
- ret = r;
+ return r;
- goto finish;
+ return ret;
}
while ((r = cg_read_subgroup(d, &fn)) > 0) {
- char *p = NULL;
+ _cleanup_free_ char *p = NULL;
- r = asprintf(&p, "%s/%s", path, fn);
+ p = strjoin(path, "/", fn, NULL);
free(fn);
-
- if (r < 0) {
- if (ret >= 0)
- ret = -ENOMEM;
-
- goto finish;
- }
+ if (!p)
+ return -ENOMEM;
r = cg_kill_recursive(controller, p, sig, sigcont, ignore_self, rem, s);
- free(p);
-
- if (r != 0 && ret >= 0)
+ if (ret >= 0 && r != 0)
ret = r;
}
- if (r < 0 && ret >= 0)
+ if (ret >= 0 && r < 0)
ret = r;
- if (rem)
- if ((r = cg_rmdir(controller, path, true)) < 0) {
- if (ret >= 0 &&
- r != -ENOENT &&
- r != -EBUSY)
- ret = r;
- }
-
-finish:
- if (d)
- closedir(d);
-
- if (allocated_set)
- set_free(allocated_set);
+ if (rem) {
+ r = cg_rmdir(controller, path, true);
+ if (r < 0 && ret >= 0 && r != -ENOENT && r != -EBUSY)
+ return r;
+ }
return ret;
}
@@ -347,7 +316,6 @@ int cg_kill_recursive_and_wait(const char *controller, const char *path, bool re
unsigned i;
assert(path);
- assert(controller);
/* This safely kills all processes; first it sends a SIGTERM,
* then checks 8 times after 200ms whether the group is now
@@ -365,7 +333,8 @@ int cg_kill_recursive_and_wait(const char *controller, const char *path, bool re
else
sig = 0;
- if ((r = cg_kill_recursive(controller, path, sig, true, true, rem, NULL)) <= 0)
+ r = cg_kill_recursive(controller, path, sig, true, true, rem, NULL);
+ if (r <= 0)
return r;
usleep(200 * USEC_PER_MSEC);
@@ -374,31 +343,34 @@ int cg_kill_recursive_and_wait(const char *controller, const char *path, bool re
return 0;
}
-int cg_migrate(const char *controller, const char *from, const char *to, bool ignore_self) {
+int cg_migrate(const char *cfrom, const char *pfrom, const char *cto, const char *pto, bool ignore_self) {
bool done = false;
- Set *s;
+ _cleanup_set_free_ Set *s = NULL;
int r, ret = 0;
pid_t my_pid;
- FILE *f = NULL;
- assert(controller);
- assert(from);
- assert(to);
+ assert(cfrom);
+ assert(pfrom);
+ assert(cto);
+ assert(pto);
- if (!(s = set_new(trivial_hash_func, trivial_compare_func)))
+ s = set_new(trivial_hash_func, trivial_compare_func);
+ if (!s)
return -ENOMEM;
my_pid = getpid();
do {
+ _cleanup_fclose_ FILE *f = NULL;
pid_t pid = 0;
done = true;
- if ((r = cg_enumerate_tasks(controller, from, &f)) < 0) {
+ r = cg_enumerate_tasks(cfrom, pfrom, &f);
+ if (r < 0) {
if (ret >= 0 && r != -ENOENT)
- ret = r;
+ return r;
- goto finish;
+ return ret;
}
while ((r = cg_read_pid(f, &pid)) > 0) {
@@ -406,13 +378,14 @@ int cg_migrate(const char *controller, const char *from, const char *to, bool ig
/* This might do weird stuff if we aren't a
* single-threaded program. However, we
* luckily know we are not */
- if (pid == my_pid && ignore_self)
+ if (ignore_self && pid == my_pid)
continue;
if (set_get(s, LONG_TO_PTR(pid)) == LONG_TO_PTR(pid))
continue;
- if ((r = cg_attach(controller, to, pid)) < 0) {
+ r = cg_attach(cto, pto, pid);
+ if (r < 0) {
if (ret >= 0 && r != -ESRCH)
ret = r;
} else if (ret == 0)
@@ -420,68 +393,59 @@ int cg_migrate(const char *controller, const char *from, const char *to, bool ig
done = false;
- if ((r = set_put(s, LONG_TO_PTR(pid))) < 0) {
+ r = set_put(s, LONG_TO_PTR(pid));
+ if (r < 0) {
if (ret >= 0)
- ret = r;
+ return r;
- goto finish;
+ return ret;
}
}
if (r < 0) {
if (ret >= 0)
- ret = r;
+ return r;
- goto finish;
+ return ret;
}
-
- fclose(f);
- f = NULL;
-
} while (!done);
-finish:
- set_free(s);
-
- if (f)
- fclose(f);
-
return ret;
}
-int cg_migrate_recursive(const char *controller, const char *from, const char *to, bool ignore_self, bool rem) {
+int cg_migrate_recursive(const char *cfrom, const char *pfrom, const char *cto, const char *pto, bool ignore_self, bool rem) {
+ _cleanup_closedir_ DIR *d = NULL;
int r, ret = 0;
- DIR *d = NULL;
char *fn;
- assert(controller);
- assert(from);
- assert(to);
+ assert(cfrom);
+ assert(pfrom);
+ assert(cto);
+ assert(pto);
- ret = cg_migrate(controller, from, to, ignore_self);
+ ret = cg_migrate(cfrom, pfrom, cto, pto, ignore_self);
- if ((r = cg_enumerate_subgroups(controller, from, &d)) < 0) {
+ r = cg_enumerate_subgroups(cfrom, pfrom, &d);
+ if (r < 0) {
if (ret >= 0 && r != -ENOENT)
- ret = r;
- goto finish;
+ return r;
+
+ return ret;
}
while ((r = cg_read_subgroup(d, &fn)) > 0) {
- char *p = NULL;
+ _cleanup_free_ char *p = NULL;
- r = asprintf(&p, "%s/%s", from, fn);
+ p = strjoin(pfrom, "/", fn, NULL);
free(fn);
-
- if (r < 0) {
+ if (!p) {
if (ret >= 0)
- ret = -ENOMEM;
+ return -ENOMEM;
- goto finish;
+ return ret;
}
- r = cg_migrate_recursive(controller, p, to, ignore_self, rem);
- free(p);
-
+ r = cg_migrate_recursive(cfrom, p, cto, pto, ignore_self, rem);
if (r != 0 && ret >= 0)
ret = r;
}
@@ -489,23 +453,19 @@ int cg_migrate_recursive(const char *controller, const char *from, const char *t
if (r < 0 && ret >= 0)
ret = r;
- if (rem)
- if ((r = cg_rmdir(controller, from, true)) < 0) {
- if (ret >= 0 &&
- r != -ENOENT &&
- r != -EBUSY)
- ret = r;
- }
-
-finish:
- if (d)
- closedir(d);
+ if (rem) {
+ r = cg_rmdir(cfrom, pfrom, true);
+ if (r < 0 && ret >= 0 && r != -ENOENT && r != -EBUSY)
+ return r;
+ }
return ret;
}
static const char *normalize_controller(const char *controller) {
+ assert(controller);
+
if (streq(controller, SYSTEMD_CGROUP_CONTROLLER))
return "systemd";
else if (startswith(controller, "name="))
@@ -517,9 +477,6 @@ static const char *normalize_controller(const char *controller) {
static int join_path(const char *controller, const char *path, const char *suffix, char **fs) {
char *t = NULL;
- if (!(controller || path))
- return -EINVAL;
-
if (controller) {
if (path && suffix)
t = strjoin("/sys/fs/cgroup/", controller, "/", path, "/", suffix, NULL);
@@ -528,12 +485,14 @@ static int join_path(const char *controller, const char *path, const char *suffi
else if (suffix)
t = strjoin("/sys/fs/cgroup/", controller, "/", suffix, NULL);
else
- t = strjoin("/sys/fs/cgroup/", controller, NULL);
+ t = strappend("/sys/fs/cgroup/", controller);
} else {
if (path && suffix)
t = strjoin(path, "/", suffix, NULL);
else if (path)
t = strdup(path);
+ else
+ return -EINVAL;
}
if (!t)
@@ -551,6 +510,9 @@ int cg_get_path(const char *controller, const char *path, const char *suffix, ch
assert(fs);
+ if (controller && !cg_controller_is_valid(controller, true))
+ return -EINVAL;
+
if (_unlikely_(!good)) {
int r;
@@ -563,10 +525,11 @@ int cg_get_path(const char *controller, const char *path, const char *suffix, ch
}
p = controller ? normalize_controller(controller) : NULL;
+
return join_path(p, path, suffix, fs);
}
-static int check(const char *p) {
+static int check_hierarchy(const char *p) {
char *cc;
assert(p);
@@ -584,17 +547,16 @@ int cg_get_path_and_check(const char *controller, const char *path, const char *
const char *p;
int r;
- assert(controller);
assert(fs);
- if (isempty(controller))
+ if (!cg_controller_is_valid(controller, true))
return -EINVAL;
/* Normalize the controller syntax */
p = normalize_controller(controller);
/* Check if this controller actually really exists */
- r = check(p);
+ r = check_hierarchy(p);
if (r < 0)
return r;
@@ -628,10 +590,9 @@ static int trim_cb(const char *path, const struct stat *sb, int typeflag, struct
}
int cg_trim(const char *controller, const char *path, bool delete_root) {
- char *fs;
+ _cleanup_free_ char *fs = NULL;
int r = 0;
- assert(controller);
assert(path);
r = cg_get_path(controller, path, NULL, &fs);
@@ -639,7 +600,7 @@ int cg_trim(const char *controller, const char *path, bool delete_root) {
return r;
errno = 0;
- if (nftw(fs, trim_cb, 64, FTW_DEPTH|FTW_MOUNT|FTW_PHYS) < 0)
+ if (nftw(fs, trim_cb, 64, FTW_DEPTH|FTW_MOUNT|FTW_PHYS) != 0)
r = errno ? -errno : -EIO;
if (delete_root) {
@@ -647,48 +608,39 @@ int cg_trim(const char *controller, const char *path, bool delete_root) {
char *p;
p = strappend(fs, "/tasks");
- if (!p) {
- free(fs);
+ if (!p)
return -ENOMEM;
- }
is_sticky = file_is_priv_sticky(p) > 0;
free(p);
if (!is_sticky)
- if (rmdir(fs) < 0 && errno != ENOENT) {
- if (r == 0)
- r = -errno;
- }
+ if (rmdir(fs) < 0 && errno != ENOENT && r == 0)
+ return -errno;
}
- free(fs);
-
return r;
}
int cg_delete(const char *controller, const char *path) {
- char *parent;
+ _cleanup_free_ char *parent = NULL;
int r;
- assert(controller);
assert(path);
- if ((r = path_get_parent(path, &parent)) < 0)
+ r = path_get_parent(path, &parent);
+ if (r < 0)
return r;
- r = cg_migrate_recursive(controller, path, parent, false, true);
- free(parent);
-
+ r = cg_migrate_recursive(controller, path, controller, parent, false, true);
return r == -ENOENT ? 0 : r;
}
int cg_attach(const char *controller, const char *path, pid_t pid) {
- char *fs;
+ _cleanup_free_ char *fs = NULL;
+ char c[DECIMAL_STR_MAX(pid_t) + 2];
int r;
- char c[32];
- assert(controller);
assert(path);
assert(pid >= 0);
@@ -700,19 +652,20 @@ int cg_attach(const char *controller, const char *path, pid_t pid) {
pid = getpid();
snprintf(c, sizeof(c), "%lu\n", (unsigned long) pid);
- char_array_0(c);
-
- r = write_one_line_file(fs, c);
- free(fs);
- return r;
+ return write_string_file(fs, c);
}
-int cg_set_group_access(const char *controller, const char *path, mode_t mode, uid_t uid, gid_t gid) {
- char *fs;
+int cg_set_group_access(
+ const char *controller,
+ const char *path,
+ mode_t mode,
+ uid_t uid,
+ gid_t gid) {
+
+ _cleanup_free_ char *fs = NULL;
int r;
- assert(controller);
assert(path);
if (mode != (mode_t) -1)
@@ -722,17 +675,20 @@ int cg_set_group_access(const char *controller, const char *path, mode_t mode, u
if (r < 0)
return r;
- r = chmod_and_chown(fs, mode, uid, gid);
- free(fs);
-
- return r;
+ return chmod_and_chown(fs, mode, uid, gid);
}
-int cg_set_task_access(const char *controller, const char *path, mode_t mode, uid_t uid, gid_t gid, int sticky) {
- char *fs;
+int cg_set_task_access(
+ const char *controller,
+ const char *path,
+ mode_t mode,
+ uid_t uid,
+ gid_t gid,
+ int sticky) {
+
+ _cleanup_free_ char *fs = NULL, *procs = NULL;
int r;
- assert(controller);
assert(path);
if (mode == (mode_t) -1 && uid == (uid_t) -1 && gid == (gid_t) -1 && sticky < 0)
@@ -756,10 +712,8 @@ int cg_set_task_access(const char *controller, const char *path, mode_t mode, ui
* mode from the file itself */
r = lstat(fs, &st);
- if (r < 0) {
- free(fs);
+ if (r < 0)
return -errno;
- }
if (mode == (mode_t) -1)
/* No mode set, we just shall set the sticky bit */
@@ -770,144 +724,148 @@ int cg_set_task_access(const char *controller, const char *path, mode_t mode, ui
}
r = chmod_and_chown(fs, mode, uid, gid);
- free(fs);
+ if (r < 0)
+ return r;
- return r;
+ /* Always keep values for "cgroup.procs" in sync with "tasks" */
+ r = cg_get_path(controller, path, "cgroup.procs", &procs);
+ if (r < 0)
+ return r;
+
+ return chmod_and_chown(procs, mode, uid, gid);
}
-int cg_get_by_pid(const char *controller, pid_t pid, char **path) {
- int r;
- char *p = NULL;
- FILE *f;
- char *fs;
+int cg_pid_get_path(const char *controller, pid_t pid, char **path) {
+ _cleanup_fclose_ FILE *f = NULL;
+ char line[LINE_MAX];
+ const char *fs;
size_t cs;
- assert(controller);
assert(path);
assert(pid >= 0);
- if (pid == 0)
- pid = getpid();
+ if (controller) {
+ if (!cg_controller_is_valid(controller, true))
+ return -EINVAL;
- if (asprintf(&fs, "/proc/%lu/cgroup", (unsigned long) pid) < 0)
- return -ENOMEM;
+ controller = normalize_controller(controller);
+ } else
+ controller = SYSTEMD_CGROUP_CONTROLLER;
- f = fopen(fs, "re");
- free(fs);
+ if (pid == 0)
+ fs = "/proc/self/cgroup";
+ else
+ fs = procfs_file_alloca(pid, "cgroup");
+ f = fopen(fs, "re");
if (!f)
return errno == ENOENT ? -ESRCH : -errno;
cs = strlen(controller);
- while (!feof(f)) {
- char line[LINE_MAX];
- char *l;
-
- errno = 0;
- if (!(fgets(line, sizeof(line), f))) {
- if (feof(f))
- break;
-
- r = errno ? -errno : -EIO;
- goto finish;
- }
+ FOREACH_LINE(line, f, return -errno) {
+ char *l, *p, *w, *e;
+ size_t k;
+ char *state;
+ bool found = false;
truncate_nl(line);
- if (!(l = strchr(line, ':')))
+ l = strchr(line, ':');
+ if (!l)
continue;
l++;
- if (strncmp(l, controller, cs) != 0)
+ e = strchr(l, ':');
+ if (!e)
continue;
- if (l[cs] != ':')
- continue;
+ *e = 0;
- if (!(p = strdup(l + cs + 1))) {
- r = -ENOMEM;
- goto finish;
+ FOREACH_WORD_SEPARATOR(w, k, l, ",", state) {
+
+ if (k == cs && memcmp(w, controller, cs) == 0) {
+ found = true;
+ break;
+ }
+
+ if (k == 5 + cs &&
+ memcmp(w, "name=", 5) == 0 &&
+ memcmp(w+5, controller, cs) == 0) {
+ found = true;
+ break;
+ }
}
- *path = p;
- r = 0;
- goto finish;
- }
+ if (!found)
+ continue;
- r = -ENOENT;
+ p = strdup(e + 1);
+ if (!p)
+ return -ENOMEM;
-finish:
- fclose(f);
+ *path = p;
+ return 0;
+ }
- return r;
+ return -ENOENT;
}
int cg_install_release_agent(const char *controller, const char *agent) {
- char *fs = NULL, *contents = NULL, *line = NULL, *sc;
+ _cleanup_free_ char *fs = NULL, *contents = NULL;
+ char *sc;
int r;
- assert(controller);
assert(agent);
- if ((r = cg_get_path(controller, NULL, "release_agent", &fs)) < 0)
+ r = cg_get_path(controller, NULL, "release_agent", &fs);
+ if (r < 0)
return r;
- if ((r = read_one_line_file(fs, &contents)) < 0)
- goto finish;
+ r = read_one_line_file(fs, &contents);
+ if (r < 0)
+ return r;
sc = strstrip(contents);
if (sc[0] == 0) {
-
- if (asprintf(&line, "%s\n", agent) < 0) {
- r = -ENOMEM;
- goto finish;
- }
-
- if ((r = write_one_line_file(fs, line)) < 0)
- goto finish;
-
- } else if (!streq(sc, agent)) {
- r = -EEXIST;
- goto finish;
- }
+ r = write_string_file(fs, agent);
+ if (r < 0)
+ return r;
+ } else if (!streq(sc, agent))
+ return -EEXIST;
free(fs);
fs = NULL;
- if ((r = cg_get_path(controller, NULL, "notify_on_release", &fs)) < 0)
- goto finish;
+ r = cg_get_path(controller, NULL, "notify_on_release", &fs);
+ if (r < 0)
+ return r;
free(contents);
contents = NULL;
- if ((r = read_one_line_file(fs, &contents)) < 0)
- goto finish;
+ r = read_one_line_file(fs, &contents);
+ if (r < 0)
+ return r;
sc = strstrip(contents);
-
if (streq(sc, "0")) {
- if ((r = write_one_line_file(fs, "1\n")) < 0)
- goto finish;
+ r = write_string_file(fs, "1");
+ if (r < 0)
+ return r;
- r = 1;
- } else if (!streq(sc, "1")) {
- r = -EIO;
- goto finish;
- } else
- r = 0;
+ return 1;
+ }
-finish:
- free(fs);
- free(contents);
- free(line);
+ if (!streq(sc, "1"))
+ return -EIO;
- return r;
+ return 0;
}
int cg_is_empty(const char *controller, const char *path, bool ignore_self) {
+ _cleanup_fclose_ FILE *f = NULL;
pid_t pid = 0, self_pid;
- int r;
- FILE *f = NULL;
bool found = false;
+ int r;
assert(path);
@@ -926,8 +884,6 @@ int cg_is_empty(const char *controller, const char *path, bool ignore_self) {
break;
}
- fclose(f);
-
if (r < 0)
return r;
@@ -935,8 +891,8 @@ int cg_is_empty(const char *controller, const char *path, bool ignore_self) {
}
int cg_is_empty_by_spec(const char *spec, bool ignore_self) {
- int r;
_cleanup_free_ char *controller = NULL, *path = NULL;
+ int r;
assert(spec);
@@ -947,11 +903,10 @@ int cg_is_empty_by_spec(const char *spec, bool ignore_self) {
return cg_is_empty(controller, path, ignore_self);
}
-
int cg_is_empty_recursive(const char *controller, const char *path, bool ignore_self) {
- int r;
- DIR *d = NULL;
+ _cleanup_closedir_ DIR *d = NULL;
char *fn;
+ int r;
assert(path);
@@ -964,47 +919,41 @@ int cg_is_empty_recursive(const char *controller, const char *path, bool ignore_
return r == -ENOENT ? 1 : r;
while ((r = cg_read_subgroup(d, &fn)) > 0) {
- char *p = NULL;
+ _cleanup_free_ char *p = NULL;
- r = asprintf(&p, "%s/%s", path, fn);
+ p = strjoin(path, "/", fn, NULL);
free(fn);
-
- if (r < 0) {
- r = -ENOMEM;
- goto finish;
- }
+ if (!p)
+ return -ENOMEM;
r = cg_is_empty_recursive(controller, p, ignore_self);
- free(p);
-
if (r <= 0)
- goto finish;
+ return r;
}
- if (r >= 0)
- r = 1;
-
-finish:
-
- if (d)
- closedir(d);
+ if (r < 0)
+ return r;
- return r;
+ return 1;
}
int cg_split_spec(const char *spec, char **controller, char **path) {
const char *e;
char *t = NULL, *u = NULL;
+ _cleanup_free_ char *v = NULL;
assert(spec);
- assert(controller || path);
if (*spec == '/') {
+ if (!path_is_safe(spec))
+ return -EINVAL;
if (path) {
- if (!(t = strdup(spec)))
+ t = strdup(spec);
+ if (!t)
return -ENOMEM;
+ path_kill_slashes(t);
*path = t;
}
@@ -1014,13 +963,14 @@ int cg_split_spec(const char *spec, char **controller, char **path) {
return 0;
}
- if (!(e = strchr(spec, ':'))) {
-
- if (strchr(spec, '/') || spec[0] == 0)
+ e = strchr(spec, ':');
+ if (!e) {
+ if (!cg_controller_is_valid(spec, true))
return -EINVAL;
if (controller) {
- if (!(t = strdup(spec)))
+ t = strdup(normalize_controller(spec));
+ if (!t)
return -ENOMEM;
*controller = t;
@@ -1032,61 +982,87 @@ int cg_split_spec(const char *spec, char **controller, char **path) {
return 0;
}
- if (e[1] != '/' ||
- e == spec ||
- memchr(spec, '/', e-spec))
+ v = strndup(spec, e-spec);
+ if (!v)
+ return -ENOMEM;
+ t = strdup(normalize_controller(v));
+ if (!t)
+ return -ENOMEM;
+ if (!cg_controller_is_valid(t, true)) {
+ free(t);
return -EINVAL;
+ }
- if (controller)
- if (!(t = strndup(spec, e-spec)))
- return -ENOMEM;
+ u = strdup(e+1);
+ if (!u) {
+ free(t);
+ return -ENOMEM;
+ }
+ if (!path_is_safe(u) ||
+ !path_is_absolute(u)) {
+ free(t);
+ free(u);
+ return -EINVAL;
+ }
- if (path)
- if (!(u = strdup(e+1))) {
- free(t);
- return -ENOMEM;
- }
+ path_kill_slashes(u);
if (controller)
*controller = t;
+ else
+ free(t);
if (path)
*path = u;
+ else
+ free(u);
return 0;
}
int cg_join_spec(const char *controller, const char *path, char **spec) {
- assert(controller);
+ char *s;
+
assert(path);
- if (!path_is_absolute(path) ||
- controller[0] == 0 ||
- strchr(controller, ':') ||
- strchr(controller, '/'))
+ if (!controller)
+ controller = "systemd";
+ else {
+ if (!cg_controller_is_valid(controller, true))
+ return -EINVAL;
+
+ controller = normalize_controller(controller);
+ }
+
+ if (!path_is_absolute(path))
return -EINVAL;
- if (asprintf(spec, "%s:%s", controller, path) < 0)
+ s = strjoin(controller, ":", path, NULL);
+ if (!s)
return -ENOMEM;
+ path_kill_slashes(s + strlen(controller) + 1);
+
+ *spec = s;
return 0;
}
-int cg_fix_path(const char *path, char **result) {
- char *t, *c, *p;
+int cg_mangle_path(const char *path, char **result) {
+ _cleanup_free_ char *c = NULL, *p = NULL;
+ char *t;
int r;
assert(path);
assert(result);
/* First check if it already is a filesystem path */
- if (path_startswith(path, "/sys/fs/cgroup") &&
- access(path, F_OK) >= 0) {
+ if (path_startswith(path, "/sys/fs/cgroup")) {
t = strdup(path);
if (!t)
return -ENOMEM;
+ path_kill_slashes(t);
*result = t;
return 0;
}
@@ -1096,15 +1072,61 @@ int cg_fix_path(const char *path, char **result) {
if (r < 0)
return r;
- r = cg_get_path(c ? c : SYSTEMD_CGROUP_CONTROLLER, p ? p : "/", NULL, result);
- free(c);
- free(p);
+ return cg_get_path(c ? c : SYSTEMD_CGROUP_CONTROLLER, p ? p : "/", NULL, result);
+}
- return r;
+int cg_get_system_path(char **path) {
+ char *p;
+ int r;
+
+ assert(path);
+
+ r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, 1, &p);
+ if (r < 0) {
+ p = strdup("/system");
+ if (!p)
+ return -ENOMEM;
+ }
+
+ if (endswith(p, "/system"))
+ *path = p;
+ else {
+ char *q;
+
+ q = strappend(p, "/system");
+ free(p);
+ if (!q)
+ return -ENOMEM;
+
+ *path = q;
+ }
+
+ return 0;
+}
+
+int cg_get_root_path(char **path) {
+ char *root, *e;
+ int r;
+
+ assert(path);
+
+ r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, 1, &root);
+ if (r < 0)
+ return r;
+
+ e = endswith(root, "/system");
+ if (e == root)
+ e[1] = 0;
+ else if (e)
+ *e = 0;
+
+ *path = root;
+ return 0;
}
int cg_get_user_path(char **path) {
- char *root, *p;
+ _cleanup_free_ char *root = NULL;
+ char *p;
assert(path);
@@ -1112,18 +1134,34 @@ int cg_get_user_path(char **path) {
* same as PID 1 has but with the "/system" suffix replaced by
* "/user" */
- if (cg_get_by_pid(SYSTEMD_CGROUP_CONTROLLER, 1, &root) < 0)
+ if (cg_get_root_path(&root) < 0 || streq(root, "/"))
p = strdup("/user");
- else {
- if (endswith(root, "/system"))
- root[strlen(root) - 7] = 0;
- else if (streq(root, "/"))
- root[0] = 0;
-
+ else
p = strappend(root, "/user");
- free(root);
+
+ if (!p)
+ return -ENOMEM;
+
+ *path = p;
+ return 0;
+}
+
+int cg_get_machine_path(const char *machine, char **path) {
+ _cleanup_free_ char *root = NULL, *escaped = NULL;
+ char *p;
+
+ assert(path);
+
+ if (machine) {
+ const char *name = strappenda(machine, ".nspawn");
+
+ escaped = cg_escape(name);
+ if (!escaped)
+ return -ENOMEM;
}
+ p = strjoin(cg_get_root_path(&root) >= 0 && !streq(root, "/") ? root : "",
+ "/machine", machine ? "/" : "", machine ? escaped : "", NULL);
if (!p)
return -ENOMEM;
@@ -1134,25 +1172,29 @@ int cg_get_user_path(char **path) {
char **cg_shorten_controllers(char **controllers) {
char **f, **t;
- controllers = strv_uniq(controllers);
-
if (!controllers)
return controllers;
for (f = controllers, t = controllers; *f; f++) {
- int r;
const char *p;
+ int r;
+
+ p = normalize_controller(*f);
- if (streq(*f, "systemd") || streq(*f, SYSTEMD_CGROUP_CONTROLLER)) {
+ if (streq(p, "systemd")) {
free(*f);
continue;
}
- p = normalize_controller(*f);
+ if (!cg_controller_is_valid(p, true)) {
+ log_warning("Controller %s is not valid, removing from controllers list.", p);
+ free(*f);
+ continue;
+ }
- r = check(p);
+ r = check_hierarchy(p);
if (r < 0) {
- log_debug("Controller %s is not available, removing from controllers list.", *f);
+ log_debug("Controller %s is not available, removing from controllers list.", p);
free(*f);
continue;
}
@@ -1161,40 +1203,28 @@ char **cg_shorten_controllers(char **controllers) {
}
*t = NULL;
- return controllers;
+ return strv_uniq(controllers);
}
-int cg_pid_get_cgroup(pid_t pid, char **root, char **cgroup) {
- char *cg_process, *cg_init, *p;
+int cg_pid_get_path_shifted(pid_t pid, char **root, char **cgroup) {
+ _cleanup_free_ char *cg_root = NULL;
+ char *cg_process, *p;
int r;
- assert(pid >= 0);
-
- if (pid == 0)
- pid = getpid();
-
- r = cg_get_by_pid(SYSTEMD_CGROUP_CONTROLLER, pid, &cg_process);
+ r = cg_get_root_path(&cg_root);
if (r < 0)
return r;
- r = cg_get_by_pid(SYSTEMD_CGROUP_CONTROLLER, 1, &cg_init);
- if (r < 0) {
- free(cg_process);
+ r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, pid, &cg_process);
+ if (r < 0)
return r;
- }
- if (endswith(cg_init, "/system"))
- cg_init[strlen(cg_init)-7] = 0;
- else if (streq(cg_init, "/"))
- cg_init[0] = 0;
-
- if (startswith(cg_process, cg_init))
- p = cg_process + strlen(cg_init);
+ p = path_startswith(cg_process, cg_root);
+ if (p)
+ p--;
else
p = cg_process;
- free(cg_init);
-
if (cgroup) {
char* c;
@@ -1216,55 +1246,358 @@ int cg_pid_get_cgroup(pid_t pid, char **root, char **cgroup) {
return 0;
}
+int cg_path_decode_unit(const char *cgroup, char **unit){
+ char *p, *e, *c, *s, *k;
+
+ assert(cgroup);
+ assert(unit);
+
+ e = strchrnul(cgroup, '/');
+ c = strndupa(cgroup, e - cgroup);
+ c = cg_unescape(c);
+
+ /* Could this be a valid unit name? */
+ if (!unit_name_is_valid(c, true))
+ return -EINVAL;
+
+ if (!unit_name_is_template(c))
+ s = strdup(c);
+ else {
+ if (*e != '/')
+ return -EINVAL;
+
+ e += strspn(e, "/");
+
+ p = strchrnul(e, '/');
+ k = strndupa(e, p - e);
+ k = cg_unescape(k);
+
+ if (!unit_name_is_valid(k, false))
+ return -EINVAL;
+
+ s = strdup(k);
+ }
+
+ if (!s)
+ return -ENOMEM;
+
+ *unit = s;
+ return 0;
+}
+
+int cg_path_get_unit(const char *path, char **unit) {
+ const char *e;
+
+ assert(path);
+ assert(unit);
+
+ e = path_startswith(path, "/system/");
+ if (!e)
+ return -ENOENT;
+
+ return cg_path_decode_unit(e, unit);
+}
+
int cg_pid_get_unit(pid_t pid, char **unit) {
+ _cleanup_free_ char *cgroup = NULL;
int r;
- char *cgroup, *p, *at, *b;
- size_t k;
- assert(pid >= 0);
assert(unit);
- r = cg_pid_get_cgroup(pid, NULL, &cgroup);
+ r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
if (r < 0)
return r;
- if (!startswith(cgroup, "/system/")) {
- free(cgroup);
+ return cg_path_get_unit(cgroup, unit);
+}
+
+_pure_ static const char *skip_label(const char *e) {
+ assert(e);
+
+ e = strchr(e, '/');
+ if (!e)
+ return NULL;
+
+ e += strspn(e, "/");
+ return e;
+}
+
+int cg_path_get_user_unit(const char *path, char **unit) {
+ const char *e;
+
+ assert(path);
+ assert(unit);
+
+ /* We always have to parse the path from the beginning as unit
+ * cgroups might have arbitrary child cgroups and we shouldn't get
+ * confused by those */
+
+ e = path_startswith(path, "/user/");
+ if (!e)
return -ENOENT;
- }
- p = cgroup + 8;
- k = strcspn(p, "/");
+ /* Skip the user name */
+ e = skip_label(e);
+ if (!e)
+ return -ENOENT;
- at = memchr(p, '@', k);
- if (at && at[1] == '.') {
- size_t j;
+ /* Skip the session ID */
+ e = skip_label(e);
+ if (!e)
+ return -ENOENT;
- /* This is a templated service */
- if (p[k] != '/') {
- free(cgroup);
- return -EIO;
- }
+ /* Skip the systemd cgroup */
+ e = skip_label(e);
+ if (!e)
+ return -ENOENT;
- j = strcspn(p+k+1, "/");
+ return cg_path_decode_unit(e, unit);
+}
- b = malloc(k + j + 1);
+int cg_pid_get_user_unit(pid_t pid, char **unit) {
+ _cleanup_free_ char *cgroup = NULL;
+ int r;
- if (b) {
- memcpy(b, p, at - p + 1);
- memcpy(b + (at - p) + 1, p + k + 1, j);
- memcpy(b + (at - p) + 1 + j, at + 1, k - (at - p) - 1);
- b[k+j] = 0;
- }
- } else
- b = strndup(p, k);
+ assert(unit);
- free(cgroup);
+ r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
+ if (r < 0)
+ return r;
+
+ return cg_path_get_user_unit(cgroup, unit);
+}
+
+int cg_path_get_machine_name(const char *path, char **machine) {
+ const char *e, *n;
+ char *s, *r;
+
+ assert(path);
+ assert(machine);
+
+ e = path_startswith(path, "/machine/");
+ if (!e)
+ return -ENOENT;
+
+ n = strchrnul(e, '/');
+ if (e == n)
+ return -ENOENT;
- if (!b)
+ s = strndupa(e, n - e);
+
+ r = strdup(cg_unescape(s));
+ if (!r)
return -ENOMEM;
- *unit = b;
+ *machine = r;
return 0;
+}
+
+int cg_pid_get_machine_name(pid_t pid, char **machine) {
+ _cleanup_free_ char *cgroup = NULL;
+ int r;
+
+ assert(machine);
+
+ r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
+ if (r < 0)
+ return r;
+
+ return cg_path_get_machine_name(cgroup, machine);
+}
+
+int cg_path_get_session(const char *path, char **session) {
+ const char *e, *n;
+ char *s;
+
+ assert(path);
+ assert(session);
+
+ e = path_startswith(path, "/user/");
+ if (!e)
+ return -ENOENT;
+
+ /* Skip the user name */
+ e = skip_label(e);
+ if (!e)
+ return -ENOENT;
+
+ n = strchrnul(e, '/');
+ if (n - e < 8)
+ return -ENOENT;
+ if (memcmp(n - 8, ".session", 8) != 0)
+ return -ENOENT;
+
+ s = strndup(e, n - e - 8);
+ if (!s)
+ return -ENOMEM;
+
+ *session = s;
+ return 0;
+}
+
+int cg_pid_get_session(pid_t pid, char **session) {
+ _cleanup_free_ char *cgroup = NULL;
+ int r;
+
+ assert(session);
+
+ r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
+ if (r < 0)
+ return r;
+
+ return cg_path_get_session(cgroup, session);
+}
+
+int cg_path_get_owner_uid(const char *path, uid_t *uid) {
+ const char *e, *n;
+ char *s;
+
+ assert(path);
+ assert(uid);
+
+ e = path_startswith(path, "/user/");
+ if (!e)
+ return -ENOENT;
+
+ n = strchrnul(e, '/');
+ if (n - e < 5)
+ return -ENOENT;
+ if (memcmp(n - 5, ".user", 5) != 0)
+ return -ENOENT;
+
+ s = strndupa(e, n - e - 5);
+ if (!s)
+ return -ENOMEM;
+
+ return parse_uid(s, uid);
+}
+
+int cg_pid_get_owner_uid(pid_t pid, uid_t *uid) {
+ _cleanup_free_ char *cgroup = NULL;
+ int r;
+
+ assert(uid);
+
+ r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
+ if (r < 0)
+ return r;
+
+ return cg_path_get_owner_uid(cgroup, uid);
+}
+
+int cg_controller_from_attr(const char *attr, char **controller) {
+ const char *dot;
+ char *c;
+
+ assert(attr);
+ assert(controller);
+
+ if (!filename_is_safe(attr))
+ return -EINVAL;
+
+ dot = strchr(attr, '.');
+ if (!dot) {
+ *controller = NULL;
+ return 0;
+ }
+
+ c = strndup(attr, dot - attr);
+ if (!c)
+ return -ENOMEM;
+
+ if (!cg_controller_is_valid(c, false)) {
+ free(c);
+ return -EINVAL;
+ }
+
+ *controller = c;
+ return 1;
+}
+
+char *cg_escape(const char *p) {
+ bool need_prefix = false;
+
+ /* This implements very minimal escaping for names to be used
+ * as file names in the cgroup tree: any name which might
+ * conflict with a kernel name or is prefixed with '_' is
+ * prefixed with a '_'. That way, when reading cgroup names it
+ * is sufficient to remove a single prefixing underscore if
+ * there is one. */
+
+ /* The return value of this function (unlike cg_unescape())
+ * needs free()! */
+
+ if (p[0] == 0 ||
+ p[0] == '_' ||
+ p[0] == '.' ||
+ streq(p, "notify_on_release") ||
+ streq(p, "release_agent") ||
+ streq(p, "tasks"))
+ need_prefix = true;
+ else {
+ const char *dot;
+
+ dot = strrchr(p, '.');
+ if (dot) {
+
+ if (dot - p == 6 && memcmp(p, "cgroup", 6) == 0)
+ need_prefix = true;
+ else {
+ char *n;
+
+ n = strndupa(p, dot - p);
+
+ if (check_hierarchy(n) >= 0)
+ need_prefix = true;
+ }
+ }
+ }
+
+ if (need_prefix)
+ return strappend("_", p);
+ else
+ return strdup(p);
+}
+
+char *cg_unescape(const char *p) {
+ assert(p);
+
+ /* The return value of this function (unlike cg_escape())
+ * doesn't need free()! */
+
+ if (p[0] == '_')
+ return (char*) p+1;
+
+ return (char*) p;
+}
+
+#define CONTROLLER_VALID \
+ "0123456789" \
+ "abcdefghijklmnopqrstuvwxyz" \
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
+ "_"
+
+bool cg_controller_is_valid(const char *p, bool allow_named) {
+ const char *t, *s;
+
+ if (!p)
+ return false;
+
+ if (allow_named) {
+ s = startswith(p, "name=");
+ if (s)
+ p = s;
+ }
+
+ if (*p == 0 || *p == '_')
+ return false;
+
+ for (t = p; *t; t++)
+ if (!strchr(CONTROLLER_VALID, *t))
+ return false;
+
+ if (t - p > FILENAME_MAX)
+ return false;
+ return true;
}
diff --git a/src/shared/cgroup-util.h b/src/shared/cgroup-util.h
index af2efc39b4..25dd277ba5 100644
--- a/src/shared/cgroup-util.h
+++ b/src/shared/cgroup-util.h
@@ -28,6 +28,21 @@
#include "set.h"
#include "def.h"
+/*
+ * General rules:
+ *
+ * We accept named hierarchies in the syntax "foo" and "name=foo".
+ *
+ * We expect that named hierarchies do not conflict in name with a
+ * kernel hierarchy, modulo the "name=" prefix.
+ *
+ * We always generate "normalized" controller names, i.e. without the
+ * "name=" prefix.
+ *
+ * We require absolute cgroup paths. When returning, we will always
+ * generate paths with multiple adjacent / removed.
+ */
+
int cg_enumerate_processes(const char *controller, const char *path, FILE **_f);
int cg_enumerate_tasks(const char *controller, const char *path, FILE **_f);
int cg_read_pid(FILE *f, pid_t *_pid);
@@ -39,23 +54,24 @@ int cg_kill(const char *controller, const char *path, int sig, bool sigcont, boo
int cg_kill_recursive(const char *controller, const char *path, int sig, bool sigcont, bool ignore_self, bool remove, Set *s);
int cg_kill_recursive_and_wait(const char *controller, const char *path, bool remove);
-int cg_migrate(const char *controller, const char *from, const char *to, bool ignore_self);
-int cg_migrate_recursive(const char *controller, const char *from, const char *to, bool ignore_self, bool remove);
+int cg_migrate(const char *cfrom, const char *pfrom, const char *cto, const char *pto, bool ignore_self);
+int cg_migrate_recursive(const char *cfrom, const char *pfrom, const char *cto, const char *pto, bool ignore_self, bool remove);
int cg_split_spec(const char *spec, char **controller, char **path);
int cg_join_spec(const char *controller, const char *path, char **spec);
-int cg_fix_path(const char *path, char **result);
+int cg_mangle_path(const char *path, char **result);
int cg_get_path(const char *controller, const char *path, const char *suffix, char **fs);
int cg_get_path_and_check(const char *controller, const char *path, const char *suffix, char **fs);
-int cg_get_by_pid(const char *controller, pid_t pid, char **path);
+
+int cg_pid_get_path(const char *controller, pid_t pid, char **path);
int cg_trim(const char *controller, const char *path, bool delete_root);
int cg_rmdir(const char *controller, const char *path, bool honour_sticky);
int cg_delete(const char *controller, const char *path);
-int cg_create(const char *controller, const char *path);
+int cg_create(const char *controller, const char *path, const char *suffix);
int cg_attach(const char *controller, const char *path, pid_t pid);
int cg_create_and_attach(const char *controller, const char *path, pid_t pid);
@@ -68,8 +84,32 @@ int cg_is_empty(const char *controller, const char *path, bool ignore_self);
int cg_is_empty_by_spec(const char *spec, bool ignore_self);
int cg_is_empty_recursive(const char *controller, const char *path, bool ignore_self);
+int cg_get_root_path(char **path);
+int cg_get_system_path(char **path);
int cg_get_user_path(char **path);
-int cg_pid_get_cgroup(pid_t pid, char **root, char **cgroup);
+int cg_get_machine_path(const char *machine, char **path);
+
+int cg_path_get_session(const char *path, char **session);
+int cg_path_get_owner_uid(const char *path, uid_t *uid);
+int cg_path_get_unit(const char *path, char **unit);
+int cg_path_get_user_unit(const char *path, char **unit);
+int cg_path_get_machine_name(const char *path, char **machine);
+
+int cg_pid_get_path_shifted(pid_t pid, char **root, char **cgroup);
+
+int cg_pid_get_session(pid_t pid, char **session);
+int cg_pid_get_owner_uid(pid_t pid, uid_t *uid);
int cg_pid_get_unit(pid_t pid, char **unit);
+int cg_pid_get_user_unit(pid_t pid, char **unit);
+int cg_pid_get_machine_name(pid_t pid, char **machine);
+
+int cg_path_decode_unit(const char *cgroup, char **unit);
char **cg_shorten_controllers(char **controllers);
+
+int cg_controller_from_attr(const char *attr, char **controller);
+
+char *cg_escape(const char *p);
+char *cg_unescape(const char *p) _pure_;
+
+bool cg_controller_is_valid(const char *p, bool allow_named);
diff --git a/src/shared/conf-files.c b/src/shared/conf-files.c
index 34b86293d3..6d99739353 100644
--- a/src/shared/conf-files.c
+++ b/src/shared/conf-files.c
@@ -37,11 +37,14 @@
#include "hashmap.h"
#include "conf-files.h"
-static int files_add(Hashmap *h, const char *path, const char *suffix) {
- DIR *dir;
- int r = 0;
+static int files_add(Hashmap *h, const char *root, const char *path, const char *suffix) {
+ _cleanup_closedir_ DIR *dir = NULL;
+ _cleanup_free_ char *dirpath = NULL;
- dir = opendir(path);
+ if (asprintf(&dirpath, "%s%s", root ? root : "", path) < 0)
+ return -ENOMEM;
+
+ dir = opendir(dirpath);
if (!dir) {
if (errno == ENOENT)
return 0;
@@ -51,14 +54,12 @@ static int files_add(Hashmap *h, const char *path, const char *suffix) {
for (;;) {
struct dirent *de;
union dirent_storage buf;
- int k;
char *p;
+ int r;
- k = readdir_r(dir, &buf.de, &de);
- if (k != 0) {
- r = -k;
- goto finish;
- }
+ r = readdir_r(dir, &buf.de, &de);
+ if (r != 0)
+ return -r;
if (!de)
break;
@@ -66,20 +67,24 @@ static int files_add(Hashmap *h, const char *path, const char *suffix) {
if (!dirent_is_file_with_suffix(de, suffix))
continue;
- if (asprintf(&p, "%s/%s", path, de->d_name) < 0) {
- r = -ENOMEM;
- goto finish;
- }
+ p = strjoin(dirpath, "/", de->d_name, NULL);
+ if (!p)
+ return -ENOMEM;
- if (hashmap_put(h, path_get_file_name(p), p) <= 0) {
- log_debug("Skip overridden file: %s.", p);
+ r = hashmap_put(h, path_get_file_name(p), p);
+ if (r == -EEXIST) {
+ log_debug("Skipping overridden file: %s.", p);
+ free(p);
+ } else if (r < 0) {
+ free(p);
+ return r;
+ } else if (r == 0) {
+ log_debug("Duplicate file %s", p);
free(p);
}
}
-finish:
- closedir(dir);
- return r;
+ return 0;
}
static int base_cmp(const void *a, const void *b) {
@@ -90,64 +95,84 @@ static int base_cmp(const void *a, const void *b) {
return strcmp(path_get_file_name(s1), path_get_file_name(s2));
}
-int conf_files_list_strv(char ***strv, const char *suffix, const char **dirs) {
- Hashmap *fh = NULL;
- char **files = NULL;
- const char **p;
+static int conf_files_list_strv_internal(char ***strv, const char *suffix, const char *root, char **dirs) {
+ Hashmap *fh;
+ char **files, **p;
int r;
- assert(dirs);
+ assert(strv);
+ assert(suffix);
+
+ /* This alters the dirs string array */
+ if (!path_strv_canonicalize_uniq(dirs))
+ return -ENOMEM;
fh = hashmap_new(string_hash_func, string_compare_func);
- if (!fh) {
- r = -ENOMEM;
- goto finish;
- }
+ if (!fh)
+ return -ENOMEM;
STRV_FOREACH(p, dirs) {
- r = files_add(fh, *p, suffix);
- if (r < 0)
- log_warning("Failed to search for files in %s: %s",
- *p, strerror(-r));
+ r = files_add(fh, root, *p, suffix);
+ if (r == -ENOMEM) {
+ hashmap_free_free(fh);
+ return r;
+ } else if (r < 0)
+ log_debug("Failed to search for files in %s: %s",
+ *p, strerror(-r));
}
files = hashmap_get_strv(fh);
if (files == NULL) {
- log_error("Failed to compose list of files.");
- r = -ENOMEM;
- goto finish;
+ hashmap_free_free(fh);
+ return -ENOMEM;
}
+
qsort(files, hashmap_size(fh), sizeof(char *), base_cmp);
- r = 0;
+ *strv = files;
-finish:
hashmap_free(fh);
- *strv = files;
- return r;
+ return 0;
}
-int conf_files_list(char ***strv, const char *suffix, const char *dir, ...) {
- char **dirs = NULL;
+int conf_files_list_strv(char ***strv, const char *suffix, const char *root, const char* const* dirs) {
+ _cleanup_strv_free_ char **copy = NULL;
+
+ assert(strv);
+ assert(suffix);
+
+ copy = strv_copy((char**) dirs);
+ if (!copy)
+ return -ENOMEM;
+
+ return conf_files_list_strv_internal(strv, suffix, root, copy);
+}
+
+int conf_files_list(char ***strv, const char *suffix, const char *root, const char *dir, ...) {
+ _cleanup_strv_free_ char **dirs = NULL;
va_list ap;
- int r;
+
+ assert(strv);
+ assert(suffix);
va_start(ap, dir);
dirs = strv_new_ap(dir, ap);
va_end(ap);
- if (!dirs) {
- r = -ENOMEM;
- goto finish;
- }
- if (!path_strv_canonicalize(dirs)) {
- r = -ENOMEM;
- goto finish;
- }
- strv_uniq(dirs);
+ if (!dirs)
+ return -ENOMEM;
+
+ return conf_files_list_strv_internal(strv, suffix, root, dirs);
+}
+
+int conf_files_list_nulstr(char ***strv, const char *suffix, const char *root, const char *d) {
+ _cleanup_strv_free_ char **dirs = NULL;
+
+ assert(strv);
+ assert(suffix);
- r = conf_files_list_strv(strv, suffix, (const char **)dirs);
+ dirs = strv_split_nulstr(d);
+ if (!dirs)
+ return -ENOMEM;
-finish:
- strv_free(dirs);
- return r;
+ return conf_files_list_strv_internal(strv, suffix, root, dirs);
}
diff --git a/src/shared/conf-files.h b/src/shared/conf-files.h
index f37ee1f3db..3bd3d2f3d4 100644
--- a/src/shared/conf-files.h
+++ b/src/shared/conf-files.h
@@ -25,7 +25,8 @@
#include "macro.h"
-int conf_files_list(char ***strv, const char *suffix, const char *dir, ...);
-int conf_files_list_strv(char ***strv, const char *suffix, const char **dirs);
+int conf_files_list(char ***strv, const char *suffix, const char *root, const char *dir, ...);
+int conf_files_list_strv(char ***strv, const char *suffix, const char *root, const char* const* dirs);
+int conf_files_list_nulstr(char ***strv, const char *suffix, const char *root, const char *dirs);
#endif
diff --git a/src/shared/conf-parser.c b/src/shared/conf-parser.c
index 9f5c07c761..2303d9a50b 100644
--- a/src/shared/conf-parser.c
+++ b/src/shared/conf-parser.c
@@ -34,6 +34,45 @@
#include "path-util.h"
#include "set.h"
#include "exit-status.h"
+#include "sd-messages.h"
+
+int log_syntax_internal(const char *unit, int level,
+ const char *file, unsigned line, const char *func,
+ const char *config_file, unsigned config_line,
+ int error, const char *format, ...) {
+
+ _cleanup_free_ char *msg = NULL;
+ int r;
+ va_list ap;
+
+ va_start(ap, format);
+ r = vasprintf(&msg, format, ap);
+ va_end(ap);
+ if (r < 0)
+ return log_oom();
+
+ if (unit)
+ r = log_struct_internal(level,
+ file, line, func,
+ getpid() == 1 ? "UNIT=%s" : "USER_UNIT=%s", unit,
+ MESSAGE_ID(SD_MESSAGE_CONFIG_ERROR),
+ "CONFIG_FILE=%s", config_file,
+ "CONFIG_LINE=%u", config_line,
+ "ERRNO=%d", error > 0 ? error : EINVAL,
+ "MESSAGE=[%s:%u] %s", config_file, config_line, msg,
+ NULL);
+ else
+ r = log_struct_internal(level,
+ file, line, func,
+ MESSAGE_ID(SD_MESSAGE_CONFIG_ERROR),
+ "CONFIG_FILE=%s", config_file,
+ "CONFIG_LINE=%u", config_line,
+ "ERRNO=%d", error > 0 ? error : EINVAL,
+ "MESSAGE=[%s:%u] %s", config_file, config_line, msg,
+ NULL);
+
+ return r;
+}
int config_item_table_lookup(
void *table,
@@ -110,16 +149,16 @@ int config_item_perf_lookup(
}
/* Run the user supplied parser for an assignment */
-static int next_assignment(
- const char *filename,
- unsigned line,
- ConfigItemLookup lookup,
- void *table,
- const char *section,
- const char *lvalue,
- const char *rvalue,
- bool relaxed,
- void *userdata) {
+static int next_assignment(const char *unit,
+ const char *filename,
+ unsigned line,
+ ConfigItemLookup lookup,
+ void *table,
+ const char *section,
+ const char *lvalue,
+ const char *rvalue,
+ bool relaxed,
+ void *userdata) {
ConfigParserCallback func = NULL;
int ltype = 0;
@@ -138,29 +177,32 @@ static int next_assignment(
if (r > 0) {
if (func)
- return func(filename, line, section, lvalue, ltype, rvalue, data, userdata);
+ return func(unit, filename, line, section, lvalue, ltype,
+ rvalue, data, userdata);
return 0;
}
/* Warn about unknown non-extension fields. */
if (!relaxed && !startswith(lvalue, "X-"))
- log_info("[%s:%u] Unknown lvalue '%s' in section '%s'. Ignoring.", filename, line, lvalue, section);
+ log_syntax(unit, LOG_WARNING, filename, line, EINVAL,
+ "Unknown lvalue '%s' in section '%s'", lvalue, section);
return 0;
}
/* Parse a variable assignment line */
-static int parse_line(
- const char *filename,
- unsigned line,
- const char *sections,
- ConfigItemLookup lookup,
- void *table,
- bool relaxed,
- char **section,
- char *l,
- void *userdata) {
+static int parse_line(const char* unit,
+ const char *filename,
+ unsigned line,
+ const char *sections,
+ ConfigItemLookup lookup,
+ void *table,
+ bool relaxed,
+ bool allow_include,
+ char **section,
+ char *l,
+ void *userdata) {
char *e;
@@ -174,21 +216,23 @@ static int parse_line(
if (!*l)
return 0;
- if (strchr(COMMENTS, *l))
+ if (strchr(COMMENTS "\n", *l))
return 0;
if (startswith(l, ".include ")) {
- char *fn;
- int r;
+ _cleanup_free_ char *fn = NULL;
+
+ if (!allow_include) {
+ log_syntax(unit, LOG_ERR, filename, line, EBADMSG,
+ ".include not allowed here. Ignoring.");
+ return 0;
+ }
fn = file_in_same_dir(filename, strstrip(l+9));
if (!fn)
return -ENOMEM;
- r = config_parse(fn, NULL, sections, lookup, table, relaxed, userdata);
- free(fn);
-
- return r;
+ return config_parse(unit, fn, NULL, sections, lookup, table, relaxed, false, userdata);
}
if (*l == '[') {
@@ -199,7 +243,8 @@ static int parse_line(
assert(k > 0);
if (l[k-1] != ']') {
- log_error("[%s:%u] Invalid section header.", filename, line);
+ log_syntax(unit, LOG_ERR, filename, line, EBADMSG,
+ "Invalid section header '%s'", l);
return -EBADMSG;
}
@@ -210,7 +255,8 @@ static int parse_line(
if (sections && !nulstr_contains(sections, n)) {
if (!relaxed)
- log_info("[%s:%u] Unknown section '%s'. Ignoring.", filename, line, n);
+ log_syntax(unit, LOG_WARNING, filename, line, EINVAL,
+ "Unknown section '%s'. Ignoring.", n);
free(n);
*section = NULL;
@@ -225,60 +271,58 @@ static int parse_line(
if (sections && !*section) {
if (!relaxed)
- log_info("[%s:%u] Assignment outside of section. Ignoring.", filename, line);
+ log_syntax(unit, LOG_WARNING, filename, line, EINVAL,
+ "Assignment outside of section. Ignoring.");
return 0;
}
e = strchr(l, '=');
if (!e) {
- log_error("[%s:%u] Missing '='.", filename, line);
+ log_syntax(unit, LOG_WARNING, filename, line, EINVAL, "Missing '='.");
return -EBADMSG;
}
*e = 0;
e++;
- return next_assignment(
- filename,
- line,
- lookup,
- table,
- *section,
- strstrip(l),
- strstrip(e),
- relaxed,
- userdata);
+ return next_assignment(unit,
+ filename,
+ line,
+ lookup,
+ table,
+ *section,
+ strstrip(l),
+ strstrip(e),
+ relaxed,
+ userdata);
}
/* Go through the file and parse each line */
-int config_parse(
- const char *filename,
- FILE *f,
- const char *sections,
- ConfigItemLookup lookup,
- void *table,
- bool relaxed,
- void *userdata) {
-
+int config_parse(const char *unit,
+ const char *filename,
+ FILE *f,
+ const char *sections,
+ ConfigItemLookup lookup,
+ void *table,
+ bool relaxed,
+ bool allow_include,
+ void *userdata) {
+
+ _cleanup_free_ char *section = NULL, *continuation = NULL;
+ _cleanup_fclose_ FILE *ours = NULL;
unsigned line = 0;
- char *section = NULL;
int r;
- bool ours = false;
- char *continuation = NULL;
assert(filename);
assert(lookup);
if (!f) {
- f = fopen(filename, "re");
+ f = ours = fopen(filename, "re");
if (!f) {
- r = -errno;
- log_error("Failed to open configuration file '%s': %s", filename, strerror(-r));
- goto finish;
+ log_error("Failed to open configuration file '%s': %m", filename);
+ return -errno;
}
-
- ours = true;
}
while (!feof(f)) {
@@ -289,19 +333,16 @@ int config_parse(
if (feof(f))
break;
- r = -errno;
- log_error("Failed to read configuration file '%s': %s", filename, strerror(-r));
- goto finish;
+ log_error("Failed to read configuration file '%s': %m", filename);
+ return -errno;
}
truncate_nl(l);
if (continuation) {
c = strappend(continuation, l);
- if (!c) {
- r = -ENOMEM;
- goto finish;
- }
+ if (!c)
+ return -ENOMEM;
free(continuation);
continuation = NULL;
@@ -323,166 +364,93 @@ int config_parse(
continuation = c;
else {
continuation = strdup(l);
- if (!continuation) {
- r = -ENOMEM;
- goto finish;
- }
+ if (!continuation)
+ return -ENOMEM;
}
continue;
}
- r = parse_line(filename,
- ++line,
- sections,
- lookup,
- table,
- relaxed,
- &section,
- p,
- userdata);
+ r = parse_line(unit,
+ filename,
+ ++line,
+ sections,
+ lookup,
+ table,
+ relaxed,
+ allow_include,
+ &section,
+ p,
+ userdata);
free(c);
if (r < 0)
- goto finish;
- }
-
- r = 0;
-
-finish:
- free(section);
- free(continuation);
-
- if (f && ours)
- fclose(f);
-
- return r;
-}
-
-int config_parse_int(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- int *i = data;
- int r;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- if ((r = safe_atoi(rvalue, i)) < 0) {
- log_error("[%s:%u] Failed to parse numeric value, ingoring: %s", filename, line, rvalue);
- return 0;
+ return r;
}
return 0;
}
-int config_parse_long(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- long *i = data;
- int r;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- if ((r = safe_atoli(rvalue, i)) < 0) {
- log_error("[%s:%u] Failed to parse numeric value, ignoring: %s", filename, line, rvalue);
- return 0;
- }
-
- return 0;
-}
-
-int config_parse_uint64(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- uint64_t *u = data;
- int r;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- if ((r = safe_atou64(rvalue, u)) < 0) {
- log_error("[%s:%u] Failed to parse numeric value, ignoring: %s", filename, line, rvalue);
- return 0;
- }
-
- return 0;
-}
-
-int config_parse_unsigned(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- unsigned *u = data;
- int r;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- if ((r = safe_atou(rvalue, u)) < 0) {
- log_error("[%s:%u] Failed to parse numeric value: %s", filename, line, rvalue);
- return r;
+#define DEFINE_PARSER(type, vartype, conv_func) \
+ int config_parse_##type(const char *unit, \
+ const char *filename, \
+ unsigned line, \
+ const char *section, \
+ const char *lvalue, \
+ int ltype, \
+ const char *rvalue, \
+ void *data, \
+ void *userdata) { \
+ \
+ vartype *i = data; \
+ int r; \
+ \
+ assert(filename); \
+ assert(lvalue); \
+ assert(rvalue); \
+ assert(data); \
+ \
+ r = conv_func(rvalue, i); \
+ if (r < 0) \
+ log_syntax(unit, LOG_ERR, filename, line, -r, \
+ "Failed to parse %s value, ignoring: %s", \
+ #vartype, rvalue); \
+ \
+ return 0; \
}
- return 0;
-}
-
-int config_parse_bytes_size(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
+DEFINE_PARSER(int, int, safe_atoi)
+DEFINE_PARSER(long, long, safe_atoli)
+DEFINE_PARSER(uint64, uint64_t, safe_atou64)
+DEFINE_PARSER(unsigned, unsigned, safe_atou)
+DEFINE_PARSER(double, double, safe_atod)
+DEFINE_PARSER(nsec, nsec_t, parse_nsec)
+DEFINE_PARSER(sec, usec_t, parse_sec)
+
+
+int config_parse_bytes_size(const char* unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
size_t *sz = data;
off_t o;
+ int r;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
- if (parse_bytes(rvalue, &o) < 0 || (off_t) (size_t) o != o) {
- log_error("[%s:%u] Failed to parse byte value, ignoring: %s", filename, line, rvalue);
+ r = parse_bytes(rvalue, &o);
+ if (r < 0 || (off_t) (size_t) o != o) {
+ log_syntax(unit, LOG_ERR, filename, line, -r,
+ "Failed to parse byte value, ignoring: %s", rvalue);
return 0;
}
@@ -491,17 +459,18 @@ int config_parse_bytes_size(
}
-int config_parse_bytes_off(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
+int config_parse_bytes_off(const char* unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
off_t *bytes = data;
+ int r;
assert(filename);
assert(lvalue);
@@ -510,23 +479,23 @@ int config_parse_bytes_off(
assert_cc(sizeof(off_t) == sizeof(uint64_t));
- if (parse_bytes(rvalue, bytes) < 0) {
- log_error("[%s:%u] Failed to parse bytes value, ignoring: %s", filename, line, rvalue);
- return 0;
- }
+ r = parse_bytes(rvalue, bytes);
+ if (r < 0)
+ log_syntax(unit, LOG_ERR, filename, line, -r,
+ "Failed to parse bytes value, ignoring: %s", rvalue);
return 0;
}
-int config_parse_bool(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
+int config_parse_bool(const char* unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
int k;
bool *b = data;
@@ -536,8 +505,10 @@ int config_parse_bool(
assert(rvalue);
assert(data);
- if ((k = parse_boolean(rvalue)) < 0) {
- log_error("[%s:%u] Failed to parse boolean value, ignoring: %s", filename, line, rvalue);
+ k = parse_boolean(rvalue);
+ if (k < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, -k,
+ "Failed to parse boolean value, ignoring: %s", rvalue);
return 0;
}
@@ -545,15 +516,15 @@ int config_parse_bool(
return 0;
}
-int config_parse_tristate(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
+int config_parse_tristate(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
int k;
int *b = data;
@@ -567,7 +538,8 @@ int config_parse_tristate(
k = parse_boolean(rvalue);
if (k < 0) {
- log_error("[%s:%u] Failed to parse boolean value, ignoring: %s", filename, line, rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, -k,
+ "Failed to parse boolean value, ignoring: %s", rvalue);
return 0;
}
@@ -575,15 +547,15 @@ int config_parse_tristate(
return 0;
}
-int config_parse_string(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
+int config_parse_string(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
char **s = data;
char *n;
@@ -595,10 +567,11 @@ int config_parse_string(
n = strdup(rvalue);
if (!n)
- return -ENOMEM;
+ return log_oom();
if (!utf8_is_valid(n)) {
- log_error("[%s:%u] String is not UTF-8 clean, ignoring assignment: %s", filename, line, rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ "String is not UTF-8 clean, ignoring assignment: %s", rvalue);
free(n);
return 0;
}
@@ -614,15 +587,15 @@ int config_parse_string(
return 0;
}
-int config_parse_path(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
+int config_parse_path(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
char **s = data;
char *n;
@@ -633,18 +606,20 @@ int config_parse_path(
assert(data);
if (!utf8_is_valid(rvalue)) {
- log_error("[%s:%u] Path is not UTF-8 clean, ignoring assignment: %s", filename, line, rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ "Path is not UTF-8 clean, ignoring assignment: %s", rvalue);
return 0;
}
if (!path_is_absolute(rvalue)) {
- log_error("[%s:%u] Not an absolute path, ignoring: %s", filename, line, rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ "Not an absolute path, ignoring: %s", rvalue);
return 0;
}
n = strdup(rvalue);
if (!n)
- return -ENOMEM;
+ return log_oom();
path_kill_slashes(n);
@@ -654,22 +629,18 @@ int config_parse_path(
return 0;
}
-int config_parse_strv(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- char*** sv = data;
- char **n;
- char *w;
- unsigned k;
+int config_parse_strv(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ char *** sv = data, *w, *state;
size_t l;
- char *state;
int r;
assert(filename);
@@ -677,66 +648,55 @@ int config_parse_strv(
assert(rvalue);
assert(data);
- k = strv_length(*sv);
- FOREACH_WORD_QUOTED(w, l, rvalue, state)
- k++;
+ if (isempty(rvalue)) {
+ char **empty;
- n = new(char*, k+1);
- if (!n)
- return -ENOMEM;
+ /* Empty assignment resets the list. As a special rule
+ * we actually fill in a real empty array here rather
+ * than NULL, since some code wants to know if
+ * something was set at all... */
+ empty = strv_new(NULL, NULL);
+ if (!empty)
+ return log_oom();
- if (*sv)
- for (k = 0; (*sv)[k]; k++)
- n[k] = (*sv)[k];
- else
- k = 0;
+ strv_free(*sv);
+ *sv = empty;
+ return 0;
+ }
FOREACH_WORD_QUOTED(w, l, rvalue, state) {
- n[k] = cunescape_length(w, l);
- if (!n[k]) {
- r = -ENOMEM;
- goto fail;
- }
+ _cleanup_free_ char *n;
- if (!utf8_is_valid(n[k])) {
- log_error("[%s:%u] String is not UTF-8 clean, ignoring assignment: %s", filename, line, rvalue);
- free(n[k]);
+ n = cunescape_length(w, l);
+ if (!n)
+ return log_oom();
+
+ if (!utf8_is_valid(n)) {
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ "String is not UTF-8 clean, ignoring: %s", rvalue);
continue;
}
- k++;
+ r = strv_extend(sv, n);
+ if (r < 0)
+ return log_oom();
}
- n[k] = NULL;
- free(*sv);
- *sv = n;
-
return 0;
-
-fail:
- for (; k > 0; k--)
- free(n[k-1]);
- free(n);
-
- return r;
}
-int config_parse_path_strv(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- char*** sv = data;
- char **n;
- char *w;
- unsigned k;
+int config_parse_path_strv(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ char*** sv = data, *w, *state;
size_t l;
- char *state;
int r;
assert(filename);
@@ -744,115 +704,50 @@ int config_parse_path_strv(
assert(rvalue);
assert(data);
- k = strv_length(*sv);
- FOREACH_WORD_QUOTED(w, l, rvalue, state)
- k++;
-
- n = new(char*, k+1);
- if (!n)
- return -ENOMEM;
-
- k = 0;
- if (*sv)
- for (; (*sv)[k]; k++)
- n[k] = (*sv)[k];
+ if (isempty(rvalue)) {
+ /* Empty assignment resets the list */
+ strv_free(*sv);
+ *sv = NULL;
+ return 0;
+ }
FOREACH_WORD_QUOTED(w, l, rvalue, state) {
- n[k] = strndup(w, l);
- if (!n[k]) {
- r = -ENOMEM;
- goto fail;
- }
+ _cleanup_free_ char *n;
- if (!utf8_is_valid(n[k])) {
- log_error("[%s:%u] Path is not UTF-8 clean, ignoring assignment: %s", filename, line, rvalue);
- free(n[k]);
+ n = strndup(w, l);
+ if (!n)
+ return log_oom();
+
+ if (!utf8_is_valid(n)) {
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ "Path is not UTF-8 clean, ignoring assignment: %s", rvalue);
continue;
}
- if (!path_is_absolute(n[k])) {
- log_error("[%s:%u] Not an absolute path, ignoring: %s", filename, line, rvalue);
- free(n[k]);
+ if (!path_is_absolute(n)) {
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ "Not an absolute path, ignoring: %s", rvalue);
continue;
}
- path_kill_slashes(n[k]);
- k++;
- }
-
- n[k] = NULL;
- free(*sv);
- *sv = n;
-
- return 0;
-
-fail:
- for (; k > 0; k--)
- free(n[k-1]);
- free(n);
-
- return r;
-}
-
-int config_parse_usec(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- usec_t *usec = data;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- if (parse_usec(rvalue, usec) < 0) {
- log_error("[%s:%u] Failed to parse time value, ignoring: %s", filename, line, rvalue);
- return 0;
- }
-
- return 0;
-}
-
-int config_parse_nsec(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- nsec_t *nsec = data;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- if (parse_nsec(rvalue, nsec) < 0) {
- log_error("[%s:%u] Failed to parse time value, ignoring: %s", filename, line, rvalue);
- return 0;
+ path_kill_slashes(n);
+ r = strv_extend(sv, n);
+ if (r < 0)
+ return log_oom();
}
return 0;
}
-int config_parse_mode(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
+int config_parse_mode(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
mode_t *m = data;
long l;
@@ -866,12 +761,14 @@ int config_parse_mode(
errno = 0;
l = strtol(rvalue, &x, 8);
if (!x || x == rvalue || *x || errno) {
- log_error("[%s:%u] Failed to parse mode value, ignoring: %s", filename, line, rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, errno,
+ "Failed to parse mode value, ignoring: %s", rvalue);
return 0;
}
if (l < 0000 || l > 07777) {
- log_error("[%s:%u] mode value out of range, ignoring: %s", filename, line, rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, ERANGE,
+ "Mode value out of range, ignoring: %s", rvalue);
return 0;
}
@@ -879,15 +776,15 @@ int config_parse_mode(
return 0;
}
-int config_parse_facility(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
+int config_parse_facility(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
int *o = data, x;
@@ -899,7 +796,8 @@ int config_parse_facility(
x = log_facility_unshifted_from_string(rvalue);
if (x < 0) {
- log_error("[%s:%u] Failed to parse log facility, ignoring: %s", filename, line, rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ "Failed to parse log facility, ignoring: %s", rvalue);
return 0;
}
@@ -908,15 +806,15 @@ int config_parse_facility(
return 0;
}
-int config_parse_level(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
+int config_parse_level(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
int *o = data, x;
@@ -928,7 +826,8 @@ int config_parse_level(
x = log_level_from_string(rvalue);
if (x < 0) {
- log_error("[%s:%u] Failed to parse log level, ignoring: %s", filename, line, rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ "Failed to parse log level, ignoring: %s", rvalue);
return 0;
}
@@ -936,15 +835,15 @@ int config_parse_level(
return 0;
}
-int config_parse_set_status(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
+int config_parse_set_status(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
char *w;
size_t l;
@@ -957,6 +856,16 @@ int config_parse_set_status(
assert(rvalue);
assert(data);
+ if (isempty(rvalue)) {
+ /* Empty assignment resets the list */
+
+ set_free(status_set->signal);
+ set_free(status_set->code);
+
+ status_set->signal = status_set->code = NULL;
+ return 0;
+ }
+
FOREACH_WORD(w, l, rvalue, state) {
int val;
char *temp;
@@ -977,18 +886,21 @@ int config_parse_set_status(
r = set_put(status_set->signal, INT_TO_PTR(val));
if (r < 0) {
- log_error("[%s:%u] Unable to store: %s", filename, line, w);
+ log_syntax(unit, LOG_ERR, filename, line, -r,
+ "Unable to store: %s", w);
return r;
}
} else {
- log_error("[%s:%u] Failed to parse value, ignoring: %s", filename, line, w);
+ log_syntax(unit, LOG_ERR, filename, line, -val,
+ "Failed to parse value, ignoring: %s", w);
return 0;
}
} else {
free(temp);
if (val < 0 || val > 255)
- log_warning("[%s:%u] Value %d is outside range 0-255, ignoring", filename, line, val);
+ log_syntax(unit, LOG_ERR, filename, line, ERANGE,
+ "Value %d is outside range 0-255, ignoring", val);
else {
r = set_ensure_allocated(&status_set->code, trivial_hash_func, trivial_compare_func);
if (r < 0)
@@ -996,7 +908,8 @@ int config_parse_set_status(
r = set_put(status_set->code, INT_TO_PTR(val));
if (r < 0) {
- log_error("[%s:%u] Unable to store: %s", filename, line, w);
+ log_syntax(unit, LOG_ERR, filename, line, -r,
+ "Unable to store: %s", w);
return r;
}
}
diff --git a/src/shared/conf-parser.h b/src/shared/conf-parser.h
index 56ffc2f8a8..08428a514a 100644
--- a/src/shared/conf-parser.h
+++ b/src/shared/conf-parser.h
@@ -24,19 +24,21 @@
#include <stdio.h>
#include <stdbool.h>
+#include "macro.h"
+
/* An abstract parser for simple, line based, shallow configuration
* files consisting of variable assignments only. */
/* Prototype for a parser for a specific configuration setting */
-typedef int (*ConfigParserCallback)(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata);
+typedef int (*ConfigParserCallback)(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata);
/* Wraps information for parsing a specific configuration variable, to
* be stored in a simple array */
@@ -78,45 +80,58 @@ int config_item_table_lookup(void *table, const char *section, const char *lvalu
* ConfigPerfItem tables */
int config_item_perf_lookup(void *table, const char *section, const char *lvalue, ConfigParserCallback *func, int *ltype, void **data, void *userdata);
-int config_parse(
- const char *filename,
- FILE *f,
- const char *sections, /* nulstr */
- ConfigItemLookup lookup,
- void *table,
- bool relaxed,
- void *userdata);
+int config_parse(const char *unit,
+ const char *filename,
+ FILE *f,
+ const char *sections, /* nulstr */
+ ConfigItemLookup lookup,
+ void *table,
+ bool relaxed,
+ bool allow_include,
+ void *userdata);
/* Generic parsers */
-int config_parse_int(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_unsigned(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_long(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_uint64(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_bytes_size(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_bytes_off(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_bool(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_tristate(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_string(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_path(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_strv(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_path_strv(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_usec(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_nsec(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_mode(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_facility(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_level(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_set_status(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_int(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_unsigned(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_long(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_uint64(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_double(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_bytes_size(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_bytes_off(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_bool(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_tristate(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_string(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_path(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_strv(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_path_strv(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_sec(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_nsec(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_mode(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_facility(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_level(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_set_status(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+
+int log_syntax_internal(const char *unit, int level,
+ const char *file, unsigned line, const char *func,
+ const char *config_file, unsigned config_line,
+ int error, const char *format, ...) _printf_attr_(9, 10);
+
+#define log_syntax(unit, level, config_file, config_line, error, ...) \
+ log_syntax_internal(unit, level, \
+ __FILE__, __LINE__, __func__, \
+ config_file, config_line, \
+ error, __VA_ARGS__)
#define DEFINE_CONFIG_PARSE_ENUM(function,name,type,msg) \
- int function( \
- const char *filename, \
- unsigned line, \
- const char *section, \
- const char *lvalue, \
- int ltype, \
- const char *rvalue, \
- void *data, \
- void *userdata) { \
+ int function(const char *unit, \
+ const char *filename, \
+ unsigned line, \
+ const char *section, \
+ const char *lvalue, \
+ int ltype, \
+ const char *rvalue, \
+ void *data, \
+ void *userdata) { \
\
type *i = data, x; \
\
@@ -126,11 +141,11 @@ int config_parse_set_status(const char *filename, unsigned line, const char *sec
assert(data); \
\
if ((x = name##_from_string(rvalue)) < 0) { \
- log_error("[%s:%u] " msg ", ignoring: %s", filename, line, rvalue); \
+ log_syntax(unit, LOG_ERR, filename, line, -x, \
+ msg ", ignoring: %s", rvalue); \
return 0; \
} \
\
*i = x; \
- \
return 0; \
}
diff --git a/src/shared/dbus-common.c b/src/shared/dbus-common.c
index e9a78c299e..b8c15cb9fc 100644
--- a/src/shared/dbus-common.c
+++ b/src/shared/dbus-common.c
@@ -258,12 +258,11 @@ const char *bus_error_message(const DBusError *error) {
return error->message;
}
-const char *bus_error_message_or_strerror(const DBusError *error, int err) {
-
+const char *bus_error(const DBusError *error, int err) {
if (error && dbus_error_is_set(error))
return bus_error_message(error);
- return strerror(err);
+ return strerror(err < 0 ? -err : err);
}
DBusHandlerResult bus_default_message_handler(
@@ -717,9 +716,14 @@ dbus_bool_t bus_maybe_send_reply (DBusConnection *c,
DBusMessage *message,
DBusMessage *reply)
{
- if (dbus_message_get_no_reply (message))
+ /* Some parts of systemd "reply" to signals, which of course
+ * have the no-reply flag set. We will be defensive here and
+ * still send out a reply if we're passed a signal.
+ */
+ if (dbus_message_get_type(message) == DBUS_MESSAGE_TYPE_METHOD_CALL &&
+ dbus_message_get_no_reply(message))
return TRUE;
- return dbus_connection_send (c, reply, NULL);
+ return dbus_connection_send(c, reply, NULL);
}
DBusHandlerResult bus_send_error_reply(DBusConnection *c, DBusMessage *message, DBusError *berror, int error) {
@@ -927,6 +931,95 @@ int bus_parse_strv_iter(DBusMessageIter *iter, char ***_l) {
return 0;
}
+int bus_parse_strv_pairs_iter(DBusMessageIter *iter, char ***_l) {
+ DBusMessageIter sub, sub2;
+ unsigned n = 0, i = 0;
+ char **l;
+
+ assert(iter);
+ assert(_l);
+
+ if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY ||
+ dbus_message_iter_get_element_type(iter) != DBUS_TYPE_STRUCT)
+ return -EINVAL;
+
+ dbus_message_iter_recurse(iter, &sub);
+
+ while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
+ n++;
+ dbus_message_iter_next(&sub);
+ }
+
+ l = new(char*, n*2+1);
+ if (!l)
+ return -ENOMEM;
+
+ dbus_message_iter_recurse(iter, &sub);
+
+ while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
+ const char *a, *b;
+
+ assert_se(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT);
+
+ dbus_message_iter_recurse(&sub, &sub2);
+
+ if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &a, true) < 0 ||
+ bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &b, false) < 0)
+ return -EINVAL;
+
+ l[i] = strdup(a);
+ if (!l[i]) {
+ strv_free(l);
+ return -ENOMEM;
+ }
+
+ l[++i] = strdup(b);
+ if (!l[i]) {
+ strv_free(l);
+ return -ENOMEM;
+ }
+
+ i++;
+ dbus_message_iter_next(&sub);
+ }
+
+ assert(i == n*2);
+ l[i] = NULL;
+
+ if (_l)
+ *_l = l;
+
+ return 0;
+}
+
+int bus_parse_unit_info(DBusMessageIter *iter, struct unit_info *u) {
+ DBusMessageIter sub;
+
+ assert(iter);
+ assert(u);
+
+ if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRUCT)
+ return -EINVAL;
+
+ dbus_message_iter_recurse(iter, &sub);
+
+ if (bus_iter_get_basic_and_next(&sub, DBUS_TYPE_STRING, &u->id, true) < 0 ||
+ bus_iter_get_basic_and_next(&sub, DBUS_TYPE_STRING, &u->description, true) < 0 ||
+ bus_iter_get_basic_and_next(&sub, DBUS_TYPE_STRING, &u->load_state, true) < 0 ||
+ bus_iter_get_basic_and_next(&sub, DBUS_TYPE_STRING, &u->active_state, true) < 0 ||
+ bus_iter_get_basic_and_next(&sub, DBUS_TYPE_STRING, &u->sub_state, true) < 0 ||
+ bus_iter_get_basic_and_next(&sub, DBUS_TYPE_STRING, &u->following, true) < 0 ||
+ bus_iter_get_basic_and_next(&sub, DBUS_TYPE_OBJECT_PATH, &u->unit_path, true) < 0 ||
+ bus_iter_get_basic_and_next(&sub, DBUS_TYPE_UINT32, &u->job_id, true) < 0 ||
+ bus_iter_get_basic_and_next(&sub, DBUS_TYPE_STRING, &u->job_type, true) < 0 ||
+ bus_iter_get_basic_and_next(&sub, DBUS_TYPE_OBJECT_PATH, &u->job_path, false) < 0) {
+ log_error("Failed to parse reply.");
+ return -EIO;
+ }
+
+ return 0;
+}
+
int bus_append_strv_iter(DBusMessageIter *iter, char **l) {
DBusMessageIter sub;
@@ -1003,7 +1096,7 @@ int generic_print_property(const char *name, DBusMessageIter *iter, bool all) {
} else if (strstr(name, "USec")) {
char timespan[FORMAT_TIMESPAN_MAX];
- printf("%s=%s\n", name, format_timespan(timespan, sizeof(timespan), u));
+ printf("%s=%s\n", name, format_timespan(timespan, sizeof(timespan), u, 0));
} else
printf("%s=%llu\n", name, (unsigned long long) u);
diff --git a/src/shared/dbus-common.h b/src/shared/dbus-common.h
index bcbf18ffab..9752f08c05 100644
--- a/src/shared/dbus-common.h
+++ b/src/shared/dbus-common.h
@@ -25,6 +25,8 @@
#include <inttypes.h>
#include <sys/types.h>
+#include "macro.h"
+
#ifndef DBUS_ERROR_UNKNOWN_OBJECT
#define DBUS_ERROR_UNKNOWN_OBJECT "org.freedesktop.DBus.Error.UnknownObject"
#endif
@@ -92,7 +94,7 @@ int bus_connect_system_ssh(const char *user, const char *host, DBusConnection **
int bus_connect_system_polkit(DBusConnection **_bus, DBusError *error);
const char *bus_error_message(const DBusError *error);
-const char *bus_error_message_or_strerror(const DBusError *error, int err);
+const char *bus_error(const DBusError *e, int r);
typedef int (*BusPropertyCallback)(DBusMessageIter *iter, const char *property, void *data);
typedef int (*BusPropertySetCallback)(DBusMessageIter *iter, const char *property, void *data);
@@ -187,16 +189,32 @@ int bus_property_set_uint64(DBusMessageIter *i, const char *property, void *data
return 0; \
}
-const char *bus_errno_to_dbus(int error);
+const char *bus_errno_to_dbus(int error) _const_;
DBusMessage* bus_properties_changed_new(const char *path, const char *interface, const char *properties);
DBusMessage* bus_properties_changed_one_new(const char *path, const char *interface, const char *property);
-uint32_t bus_flags_to_events(DBusWatch *bus_watch);
-unsigned bus_events_to_flags(uint32_t events);
+uint32_t bus_flags_to_events(DBusWatch *bus_watch) _pure_;
+unsigned bus_events_to_flags(uint32_t events) _const_;
int bus_parse_strv(DBusMessage *m, char ***_l);
int bus_parse_strv_iter(DBusMessageIter *iter, char ***_l);
+int bus_parse_strv_pairs_iter(DBusMessageIter *iter, char ***_l);
+
+struct unit_info {
+ const char *id;
+ const char *description;
+ const char *load_state;
+ const char *active_state;
+ const char *sub_state;
+ const char *following;
+ const char *unit_path;
+ uint32_t job_id;
+ const char *job_type;
+ const char *job_path;
+};
+
+int bus_parse_unit_info(DBusMessageIter *iter, struct unit_info *u);
int bus_append_strv_iter(DBusMessageIter *iter, char **l);
diff --git a/src/shared/dbus-loop.c b/src/shared/dbus-loop.c
index da0a00443a..c533242220 100644
--- a/src/shared/dbus-loop.c
+++ b/src/shared/dbus-loop.c
@@ -44,8 +44,8 @@ typedef struct EpollData {
} EpollData;
static dbus_bool_t add_watch(DBusWatch *watch, void *data) {
- EpollData *e;
- struct epoll_event ev;
+ _cleanup_free_ EpollData *e = NULL;
+ struct epoll_event ev = {};
assert(watch);
@@ -57,16 +57,13 @@ static dbus_bool_t add_watch(DBusWatch *watch, void *data) {
e->object = watch;
e->is_timeout = false;
- zero(ev);
ev.events = bus_flags_to_events(watch);
ev.data.ptr = e;
if (epoll_ctl(PTR_TO_INT(data), EPOLL_CTL_ADD, e->fd, &ev) < 0) {
- if (errno != EEXIST) {
- free(e);
+ if (errno != EEXIST)
return FALSE;
- }
/* Hmm, bloody D-Bus creates multiple watches on the
* same fd. epoll() does not like that. As a dirty
@@ -74,14 +71,11 @@ static dbus_bool_t add_watch(DBusWatch *watch, void *data) {
* one we can safely add to the epoll(). */
e->fd = dup(e->fd);
- if (e->fd < 0) {
- free(e);
+ if (e->fd < 0)
return FALSE;
- }
if (epoll_ctl(PTR_TO_INT(data), EPOLL_CTL_ADD, e->fd, &ev) < 0) {
close_nointr_nofail(e->fd);
- free(e);
return FALSE;
}
@@ -89,12 +83,13 @@ static dbus_bool_t add_watch(DBusWatch *watch, void *data) {
}
dbus_watch_set_data(watch, e, NULL);
+ e = NULL; /* prevent freeing */
return TRUE;
}
static void remove_watch(DBusWatch *watch, void *data) {
- EpollData *e;
+ _cleanup_free_ EpollData *e = NULL;
assert(watch);
@@ -106,13 +101,11 @@ static void remove_watch(DBusWatch *watch, void *data) {
if (e->fd_is_dupped)
close_nointr_nofail(e->fd);
-
- free(e);
}
static void toggle_watch(DBusWatch *watch, void *data) {
EpollData *e;
- struct epoll_event ev;
+ struct epoll_event ev = {};
assert(watch);
@@ -120,21 +113,18 @@ static void toggle_watch(DBusWatch *watch, void *data) {
if (!e)
return;
- zero(ev);
- ev.events = bus_flags_to_events(watch);
ev.data.ptr = e;
+ ev.events = bus_flags_to_events(watch);
assert_se(epoll_ctl(PTR_TO_INT(data), EPOLL_CTL_MOD, e->fd, &ev) == 0);
}
static int timeout_arm(EpollData *e) {
- struct itimerspec its;
+ struct itimerspec its = {};
assert(e);
assert(e->is_timeout);
- zero(its);
-
if (dbus_timeout_get_enabled(e->object)) {
timespec_store(&its.it_value, dbus_timeout_get_interval(e->object) * USEC_PER_MSEC);
its.it_interval = its.it_value;
@@ -148,7 +138,7 @@ static int timeout_arm(EpollData *e) {
static dbus_bool_t add_timeout(DBusTimeout *timeout, void *data) {
EpollData *e;
- struct epoll_event ev;
+ struct epoll_event ev = {};
assert(timeout);
@@ -166,7 +156,6 @@ static dbus_bool_t add_timeout(DBusTimeout *timeout, void *data) {
if (timeout_arm(e) < 0)
goto fail;
- zero(ev);
ev.events = EPOLLIN;
ev.data.ptr = e;
@@ -186,7 +175,7 @@ fail:
}
static void remove_timeout(DBusTimeout *timeout, void *data) {
- EpollData *e;
+ _cleanup_free_ EpollData *e = NULL;
assert(timeout);
@@ -196,7 +185,6 @@ static void remove_timeout(DBusTimeout *timeout, void *data) {
assert_se(epoll_ctl(PTR_TO_INT(data), EPOLL_CTL_DEL, e->fd, NULL) >= 0);
close_nointr_nofail(e->fd);
- free(e);
}
static void toggle_timeout(DBusTimeout *timeout, void *data) {
@@ -234,13 +222,11 @@ int bus_loop_open(DBusConnection *c) {
int bus_loop_dispatch(int fd) {
int n;
- struct epoll_event event;
+ struct epoll_event event = {};
EpollData *d;
assert(fd >= 0);
- zero(event);
-
n = epoll_wait(fd, &event, 1, 0);
if (n < 0)
return errno == EAGAIN || errno == EINTR ? 0 : -errno;
diff --git a/src/shared/efivars.c b/src/shared/efivars.c
new file mode 100644
index 0000000000..8d004bad33
--- /dev/null
+++ b/src/shared/efivars.c
@@ -0,0 +1,503 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ 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 <http://www.gnu.org/licenses/>.
+***/
+
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <ctype.h>
+
+#include "util.h"
+#include "utf8.h"
+#include "efivars.h"
+
+#ifdef ENABLE_EFI
+
+bool is_efi_boot(void) {
+ return access("/sys/firmware/efi", F_OK) >= 0;
+}
+
+static int read_flag(const char *varname) {
+ int r;
+ void *v;
+ size_t s;
+ uint8_t b;
+
+ r = efi_get_variable(EFI_VENDOR_GLOBAL, varname, NULL, &v, &s);
+ if (r < 0)
+ return r;
+
+ if (s != 1) {
+ r = -EINVAL;
+ goto finish;
+ }
+
+ b = *(uint8_t *)v;
+ r = b > 0;
+finish:
+ free(v);
+ return r;
+}
+
+int is_efi_secure_boot(void) {
+ return read_flag("SecureBoot");
+}
+
+int is_efi_secure_boot_setup_mode(void) {
+ return read_flag("SetupMode");
+}
+
+int efi_get_variable(
+ sd_id128_t vendor,
+ const char *name,
+ uint32_t *attribute,
+ void **value,
+ size_t *size) {
+
+ _cleanup_close_ int fd = -1;
+ _cleanup_free_ char *p = NULL;
+ uint32_t a;
+ ssize_t n;
+ struct stat st;
+ void *r;
+
+ assert(name);
+ assert(value);
+ assert(size);
+
+ if (asprintf(&p,
+ "/sys/firmware/efi/efivars/%s-%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
+ name, SD_ID128_FORMAT_VAL(vendor)) < 0)
+ return -ENOMEM;
+
+ fd = open(p, O_RDONLY|O_NOCTTY|O_CLOEXEC);
+ if (fd < 0)
+ return -errno;
+
+ if (fstat(fd, &st) < 0)
+ return -errno;
+ if (st.st_size < 4)
+ return -EIO;
+ if (st.st_size > 4*1024*1024 + 4)
+ return -E2BIG;
+
+ n = read(fd, &a, sizeof(a));
+ if (n < 0)
+ return -errno;
+ if (n != sizeof(a))
+ return -EIO;
+
+ r = malloc(st.st_size - 4 + 2);
+ if (!r)
+ return -ENOMEM;
+
+ n = read(fd, r, (size_t) st.st_size - 4);
+ if (n < 0) {
+ free(r);
+ return -errno;
+ }
+ if (n != (ssize_t) st.st_size - 4) {
+ free(r);
+ return -EIO;
+ }
+
+ /* Always NUL terminate (2 bytes, to protect UTF-16) */
+ ((char*) r)[st.st_size - 4] = 0;
+ ((char*) r)[st.st_size - 4 + 1] = 0;
+
+ *value = r;
+ *size = (size_t) st.st_size - 4;
+
+ if (attribute)
+ *attribute = a;
+
+ return 0;
+}
+
+int efi_get_variable_string(sd_id128_t vendor, const char *name, char **p) {
+ _cleanup_free_ void *s = NULL;
+ size_t ss;
+ int r;
+ char *x;
+
+ r = efi_get_variable(vendor, name, NULL, &s, &ss);
+ if (r < 0)
+ return r;
+
+ x = utf16_to_utf8(s, ss);
+ if (!x)
+ return -ENOMEM;
+
+ *p = x;
+ return 0;
+}
+
+static size_t utf16_size(const uint16_t *s) {
+ size_t l = 0;
+
+ while (s[l] > 0)
+ l++;
+
+ return (l+1) * sizeof(uint16_t);
+}
+
+static void efi_guid_to_id128(const void *guid, sd_id128_t *id128) {
+ struct uuid {
+ uint32_t u1;
+ uint16_t u2;
+ uint16_t u3;
+ uint8_t u4[8];
+ } _packed_;
+ const struct uuid *uuid = guid;
+
+ id128->bytes[0] = (uuid->u1 >> 24) & 0xff;
+ id128->bytes[1] = (uuid->u1 >> 16) & 0xff;
+ id128->bytes[2] = (uuid->u1 >> 8) & 0xff;
+ id128->bytes[3] = (uuid->u1) & 0xff;
+ id128->bytes[4] = (uuid->u2 >> 8) & 0xff;
+ id128->bytes[5] = (uuid->u2) & 0xff;
+ id128->bytes[6] = (uuid->u3 >> 8) & 0xff;
+ id128->bytes[7] = (uuid->u3) & 0xff;
+ memcpy(&id128->bytes[8], uuid->u4, sizeof(uuid->u4));
+}
+
+int efi_get_boot_option(
+ uint16_t id,
+ char **title,
+ sd_id128_t *part_uuid,
+ char **path) {
+
+ struct boot_option {
+ uint32_t attr;
+ uint16_t path_len;
+ uint16_t title[];
+ } _packed_;
+
+ struct drive_path {
+ uint32_t part_nr;
+ uint64_t part_start;
+ uint64_t part_size;
+ char signature[16];
+ uint8_t mbr_type;
+ uint8_t signature_type;
+ } _packed_;
+
+ struct device_path {
+ uint8_t type;
+ uint8_t sub_type;
+ uint16_t length;
+ union {
+ uint16_t path[0];
+ struct drive_path drive;
+ };
+ } _packed_;
+
+ char boot_id[9];
+ _cleanup_free_ uint8_t *buf = NULL;
+ size_t l;
+ struct boot_option *header;
+ size_t title_size;
+ char *s = NULL;
+ char *p = NULL;
+ sd_id128_t p_uuid = SD_ID128_NULL;
+ int err;
+
+ snprintf(boot_id, sizeof(boot_id), "Boot%04X", id);
+ err = efi_get_variable(EFI_VENDOR_GLOBAL, boot_id, NULL, (void **)&buf, &l);
+ if (err < 0)
+ return err;
+ if (l < sizeof(struct boot_option))
+ return -ENOENT;
+
+ header = (struct boot_option *)buf;
+ title_size = utf16_size(header->title);
+ if (title_size > l - offsetof(struct boot_option, title))
+ return -EINVAL;
+
+ if (title) {
+ s = utf16_to_utf8(header->title, title_size);
+ if (!s) {
+ err = -ENOMEM;
+ goto err;
+ }
+ }
+
+ if (header->path_len > 0) {
+ uint8_t *dbuf;
+ size_t dnext;
+
+ dbuf = buf + offsetof(struct boot_option, title) + title_size;
+ dnext = 0;
+ while (dnext < header->path_len) {
+ struct device_path *dpath;
+
+ dpath = (struct device_path *)(dbuf + dnext);
+ if (dpath->length < 4)
+ break;
+
+ /* Type 0x7F – End of Hardware Device Path, Sub-Type 0xFF – End Entire Device Path */
+ if (dpath->type == 0x7f && dpath->sub_type == 0xff)
+ break;
+
+ dnext += dpath->length;
+
+ /* Type 0x04 – Media Device Path */
+ if (dpath->type != 0x04)
+ continue;
+
+ /* Sub-Type 1 – Hard Drive */
+ if (dpath->sub_type == 0x01) {
+ /* 0x02 – GUID Partition Table */
+ if (dpath->drive.mbr_type != 0x02)
+ continue;
+
+ /* 0x02 – GUID signature */
+ if (dpath->drive.signature_type != 0x02)
+ continue;
+
+ if (part_uuid)
+ efi_guid_to_id128(dpath->drive.signature, &p_uuid);
+ continue;
+ }
+
+ /* Sub-Type 4 – File Path */
+ if (dpath->sub_type == 0x04 && !p && path) {
+ p = utf16_to_utf8(dpath->path, dpath->length-4);
+ continue;
+ }
+ }
+ }
+
+ if (title)
+ *title = s;
+ if (part_uuid)
+ *part_uuid = p_uuid;
+ if (path)
+ *path = p;
+
+ return 0;
+err:
+ free(s);
+ free(p);
+ return err;
+}
+
+int efi_get_boot_order(uint16_t **order) {
+ void *buf;
+ size_t l;
+ int r;
+
+ r = efi_get_variable(EFI_VENDOR_GLOBAL, "BootOrder", NULL, &buf, &l);
+ if (r < 0)
+ return r;
+
+ if (l <= 0) {
+ free(buf);
+ return -ENOENT;
+ }
+
+ if ((l % sizeof(uint16_t) > 0) ||
+ (l / sizeof(uint16_t) > INT_MAX)) {
+ free(buf);
+ return -EINVAL;
+ }
+
+ *order = buf;
+ return (int) (l / sizeof(uint16_t));
+}
+
+static int boot_id_hex(const char s[4]) {
+ int i;
+ int id = 0;
+
+ for (i = 0; i < 4; i++)
+ if (s[i] >= '0' && s[i] <= '9')
+ id |= (s[i] - '0') << (3 - i) * 4;
+ else if (s[i] >= 'A' && s[i] <= 'F')
+ id |= (s[i] - 'A' + 10) << (3 - i) * 4;
+ else
+ return -1;
+
+ return id;
+}
+
+static int cmp_uint16(const void *_a, const void *_b) {
+ const uint16_t *a = _a, *b = _b;
+
+ return (int)*a - (int)*b;
+}
+
+int efi_get_boot_options(uint16_t **options) {
+ _cleanup_closedir_ DIR *dir = NULL;
+ struct dirent *de;
+ uint16_t *list = NULL;
+ int count = 0, r;
+
+ assert(options);
+
+ dir = opendir("/sys/firmware/efi/efivars/");
+ if (!dir)
+ return -errno;
+
+ FOREACH_DIRENT(de, dir, r = -errno; goto fail) {
+ int id;
+ uint16_t *t;
+
+ if (strncmp(de->d_name, "Boot", 4) != 0)
+ continue;
+
+ if (strlen(de->d_name) != 45)
+ continue;
+
+ if (strcmp(de->d_name + 8, "-8be4df61-93ca-11d2-aa0d-00e098032b8c") != 0)
+ continue;
+
+ id = boot_id_hex(de->d_name + 4);
+ if (id < 0)
+ continue;
+
+ t = realloc(list, (count + 1) * sizeof(uint16_t));
+ if (!t) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ list = t;
+ list[count ++] = id;
+ }
+
+ qsort(list, count, sizeof(uint16_t), cmp_uint16);
+
+ *options = list;
+ return count;
+
+fail:
+ free(list);
+ return r;
+}
+
+static int read_usec(sd_id128_t vendor, const char *name, usec_t *u) {
+ _cleanup_free_ char *j = NULL;
+ int r;
+ uint64_t x;
+
+ assert(name);
+ assert(u);
+
+ r = efi_get_variable_string(EFI_VENDOR_LOADER, name, &j);
+ if (r < 0)
+ return r;
+
+ r = safe_atou64(j, &x);
+ if (r < 0)
+ return r;
+
+ *u = x;
+ return 0;
+}
+
+static int get_boot_usec(usec_t *firmware, usec_t *loader) {
+ uint64_t x, y;
+ int r;
+
+ assert(firmware);
+ assert(loader);
+
+ r = read_usec(EFI_VENDOR_LOADER, "LoaderTimeInitUSec", &x);
+ if (r < 0)
+ return r;
+
+ r = read_usec(EFI_VENDOR_LOADER, "LoaderTimeExecUSec", &y);
+ if (r < 0)
+ return r;
+
+ if (y == 0 || y < x)
+ return -EIO;
+
+ if (y > USEC_PER_HOUR)
+ return -EIO;
+
+ *firmware = x;
+ *loader = y;
+
+ return 0;
+}
+
+int efi_get_boot_timestamps(const dual_timestamp *n, dual_timestamp *firmware, dual_timestamp *loader) {
+ usec_t x, y, a;
+ int r;
+ dual_timestamp _n;
+
+ assert(firmware);
+ assert(loader);
+
+ if (!n) {
+ dual_timestamp_get(&_n);
+ n = &_n;
+ }
+
+ r = get_boot_usec(&x, &y);
+ if (r < 0)
+ return r;
+
+ /* Let's convert this to timestamps where the firmware
+ * began/loader began working. To make this more confusing:
+ * since usec_t is unsigned and the kernel's monotonic clock
+ * begins at kernel initialization we'll actually initialize
+ * the monotonic timestamps here as negative of the actual
+ * value. */
+
+ firmware->monotonic = y;
+ loader->monotonic = y - x;
+
+ a = n->monotonic + firmware->monotonic;
+ firmware->realtime = n->realtime > a ? n->realtime - a : 0;
+
+ a = n->monotonic + loader->monotonic;
+ loader->realtime = n->realtime > a ? n->realtime - a : 0;
+
+ return 0;
+}
+
+int efi_get_loader_device_part_uuid(sd_id128_t *u) {
+ _cleanup_free_ char *p = NULL;
+ int r, parsed[16];
+ unsigned i;
+
+ assert(u);
+
+ r = efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderDevicePartUUID", &p);
+ if (r < 0)
+ return r;
+
+ if (sscanf(p, "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
+ &parsed[0], &parsed[1], &parsed[2], &parsed[3],
+ &parsed[4], &parsed[5], &parsed[6], &parsed[7],
+ &parsed[8], &parsed[9], &parsed[10], &parsed[11],
+ &parsed[12], &parsed[13], &parsed[14], &parsed[15]) != 16)
+ return -EIO;
+
+ for (i = 0; i < ELEMENTSOF(parsed); i++)
+ u->bytes[i] = parsed[i];
+
+ return 0;
+}
+
+#endif
diff --git a/src/shared/efivars.h b/src/shared/efivars.h
new file mode 100644
index 0000000000..2b88c6075c
--- /dev/null
+++ b/src/shared/efivars.h
@@ -0,0 +1,47 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ 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 <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/types.h>
+#include <inttypes.h>
+#include <stdbool.h>
+
+#include "sd-id128.h"
+#include "time-util.h"
+
+#define EFI_VENDOR_LOADER SD_ID128_MAKE(4a,67,b0,82,0a,4c,41,cf,b6,c7,44,0b,29,bb,8c,4f)
+#define EFI_VENDOR_GLOBAL SD_ID128_MAKE(8b,e4,df,61,93,ca,11,d2,aa,0d,00,e0,98,03,2b,8c)
+
+bool is_efi_boot(void);
+int is_efi_secure_boot(void);
+int is_efi_secure_boot_setup_mode(void);
+
+int efi_get_variable(sd_id128_t vendor, const char *name, uint32_t *attribute, void **value, size_t *size);
+int efi_get_variable_string(sd_id128_t vendor, const char *name, char **p);
+
+int efi_get_boot_option(uint16_t nr, char **title, sd_id128_t *partuuid, char **path);
+int efi_get_boot_order(uint16_t **order);
+int efi_get_boot_options(uint16_t **options);
+
+int efi_get_boot_timestamps(const dual_timestamp *n, dual_timestamp *firmware, dual_timestamp *loader);
+
+int efi_get_loader_device_part_uuid(sd_id128_t *u);
diff --git a/src/shared/env-util.c b/src/shared/env-util.c
new file mode 100644
index 0000000000..6a52fb960d
--- /dev/null
+++ b/src/shared/env-util.c
@@ -0,0 +1,415 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2012 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU 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 <http://www.gnu.org/licenses/>.
+***/
+
+#include <limits.h>
+#include <sys/param.h>
+#include <unistd.h>
+
+#include "strv.h"
+#include "utf8.h"
+#include "util.h"
+#include "env-util.h"
+
+#define VALID_CHARS_ENV_NAME \
+ "0123456789" \
+ "abcdefghijklmnopqrstuvwxyz" \
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
+ "_"
+
+#ifndef ARG_MAX
+#define ARG_MAX ((size_t) sysconf(_SC_ARG_MAX))
+#endif
+
+static bool env_name_is_valid_n(const char *e, size_t n) {
+ const char *p;
+
+ if (!e)
+ return false;
+
+ if (n <= 0)
+ return false;
+
+ if (e[0] >= '0' && e[0] <= '9')
+ return false;
+
+ /* POSIX says the overall size of the environment block cannot
+ * be > ARG_MAX, an individual assignment hence cannot be
+ * either. Discounting the equal sign and trailing NUL this
+ * hence leaves ARG_MAX-2 as longest possible variable
+ * name. */
+ if (n > ARG_MAX - 2)
+ return false;
+
+ for (p = e; p < e + n; p++)
+ if (!strchr(VALID_CHARS_ENV_NAME, *p))
+ return false;
+
+ return true;
+}
+
+bool env_name_is_valid(const char *e) {
+ if (!e)
+ return false;
+
+ return env_name_is_valid_n(e, strlen(e));
+}
+
+bool env_value_is_valid(const char *e) {
+ if (!e)
+ return false;
+
+ if (!utf8_is_valid(e))
+ return false;
+
+ if (string_has_cc(e))
+ return false;
+
+ /* POSIX says the overall size of the environment block cannot
+ * be > ARG_MAX, an individual assignment hence cannot be
+ * either. Discounting the shortest possible variable name of
+ * length 1, the equal sign and trailing NUL this hence leaves
+ * ARG_MAX-3 as longest possible variable value. */
+ if (strlen(e) > ARG_MAX - 3)
+ return false;
+
+ return true;
+}
+
+bool env_assignment_is_valid(const char *e) {
+ const char *eq;
+
+ eq = strchr(e, '=');
+ if (!eq)
+ return false;
+
+ if (!env_name_is_valid_n(e, eq - e))
+ return false;
+
+ if (!env_value_is_valid(eq + 1))
+ return false;
+
+ /* POSIX says the overall size of the environment block cannot
+ * be > ARG_MAX, hence the individual variable assignments
+ * cannot be either, but let's leave room for one trailing NUL
+ * byte. */
+ if (strlen(e) > ARG_MAX - 1)
+ return false;
+
+ return true;
+}
+
+bool strv_env_is_valid(char **e) {
+ char **p, **q;
+
+ STRV_FOREACH(p, e) {
+ size_t k;
+
+ if (!env_assignment_is_valid(*p))
+ return false;
+
+ /* Check if there are duplicate assginments */
+ k = strcspn(*p, "=");
+ STRV_FOREACH(q, p + 1)
+ if (strneq(*p, *q, k) && (*q)[k] == '=')
+ return false;
+ }
+
+ return true;
+}
+
+bool strv_env_name_or_assignment_is_valid(char **l) {
+ char **p, **q;
+
+ STRV_FOREACH(p, l) {
+ if (!env_assignment_is_valid(*p) && !env_name_is_valid(*p))
+ return false;
+
+ STRV_FOREACH(q, p + 1)
+ if (streq(*p, *q))
+ return false;
+ }
+
+ return true;
+}
+
+static int env_append(char **r, char ***k, char **a) {
+ assert(r);
+ assert(k);
+
+ if (!a)
+ return 0;
+
+ /* Add the entries of a to *k unless they already exist in *r
+ * in which case they are overridden instead. This assumes
+ * there is enough space in the r array. */
+
+ for (; *a; a++) {
+ char **j;
+ size_t n;
+
+ n = strcspn(*a, "=");
+
+ if ((*a)[n] == '=')
+ n++;
+
+ for (j = r; j < *k; j++)
+ if (strneq(*j, *a, n))
+ break;
+
+ if (j >= *k)
+ (*k)++;
+ else
+ free(*j);
+
+ *j = strdup(*a);
+ if (!*j)
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+char **strv_env_merge(unsigned n_lists, ...) {
+ size_t n = 0;
+ char **l, **k, **r;
+ va_list ap;
+ unsigned i;
+
+ /* Merges an arbitrary number of environment sets */
+
+ va_start(ap, n_lists);
+ for (i = 0; i < n_lists; i++) {
+ l = va_arg(ap, char**);
+ n += strv_length(l);
+ }
+ va_end(ap);
+
+ r = new(char*, n+1);
+ if (!r)
+ return NULL;
+
+ k = r;
+
+ va_start(ap, n_lists);
+ for (i = 0; i < n_lists; i++) {
+ l = va_arg(ap, char**);
+ if (env_append(r, &k, l) < 0)
+ goto fail;
+ }
+ va_end(ap);
+
+ *k = NULL;
+
+ return r;
+
+fail:
+ va_end(ap);
+ strv_free(r);
+
+ return NULL;
+}
+
+_pure_ static bool env_match(const char *t, const char *pattern) {
+ assert(t);
+ assert(pattern);
+
+ /* pattern a matches string a
+ * a matches a=
+ * a matches a=b
+ * a= matches a=
+ * a=b matches a=b
+ * a= does not match a
+ * a=b does not match a=
+ * a=b does not match a
+ * a=b does not match a=c */
+
+ if (streq(t, pattern))
+ return true;
+
+ if (!strchr(pattern, '=')) {
+ size_t l = strlen(pattern);
+
+ return strneq(t, pattern, l) && t[l] == '=';
+ }
+
+ return false;
+}
+
+char **strv_env_delete(char **x, unsigned n_lists, ...) {
+ size_t n, i = 0;
+ char **k, **r;
+ va_list ap;
+
+ /* Deletes every entry from x that is mentioned in the other
+ * string lists */
+
+ n = strv_length(x);
+
+ r = new(char*, n+1);
+ if (!r)
+ return NULL;
+
+ STRV_FOREACH(k, x) {
+ unsigned v;
+
+ va_start(ap, n_lists);
+ for (v = 0; v < n_lists; v++) {
+ char **l, **j;
+
+ l = va_arg(ap, char**);
+ STRV_FOREACH(j, l)
+ if (env_match(*k, *j))
+ goto skip;
+ }
+ va_end(ap);
+
+ r[i] = strdup(*k);
+ if (!r[i]) {
+ strv_free(r);
+ return NULL;
+ }
+
+ i++;
+ continue;
+
+ skip:
+ va_end(ap);
+ }
+
+ r[i] = NULL;
+
+ assert(i <= n);
+
+ return r;
+}
+
+char **strv_env_unset(char **l, const char *p) {
+
+ char **f, **t;
+
+ if (!l)
+ return NULL;
+
+ assert(p);
+
+ /* Drops every occurrence of the env var setting p in the
+ * string list. edits in-place. */
+
+ for (f = t = l; *f; f++) {
+
+ if (env_match(*f, p)) {
+ free(*f);
+ continue;
+ }
+
+ *(t++) = *f;
+ }
+
+ *t = NULL;
+ return l;
+}
+
+char **strv_env_set(char **x, const char *p) {
+
+ char **k, **r;
+ char* m[2] = { (char*) p, NULL };
+
+ /* Overrides the env var setting of p, returns a new copy */
+
+ r = new(char*, strv_length(x)+2);
+ if (!r)
+ return NULL;
+
+ k = r;
+ if (env_append(r, &k, x) < 0)
+ goto fail;
+
+ if (env_append(r, &k, m) < 0)
+ goto fail;
+
+ *k = NULL;
+
+ return r;
+
+fail:
+ strv_free(r);
+ return NULL;
+}
+
+char *strv_env_get_n(char **l, const char *name, size_t k) {
+ char **i;
+
+ assert(name);
+
+ if (k <= 0)
+ return NULL;
+
+ STRV_FOREACH(i, l)
+ if (strneq(*i, name, k) &&
+ (*i)[k] == '=')
+ return *i + k + 1;
+
+ return NULL;
+}
+
+char *strv_env_get(char **l, const char *name) {
+ assert(name);
+
+ return strv_env_get_n(l, name, strlen(name));
+}
+
+char **strv_env_clean_log(char **e, const char *message) {
+ char **p, **q;
+ int k = 0;
+
+ STRV_FOREACH(p, e) {
+ size_t n;
+ bool duplicate = false;
+
+ if (!env_assignment_is_valid(*p)) {
+ if (message)
+ log_error("Ignoring invalid environment '%s': %s", *p, message);
+ free(*p);
+ continue;
+ }
+
+ n = strcspn(*p, "=");
+ STRV_FOREACH(q, p + 1)
+ if (strneq(*p, *q, n) && (*q)[n] == '=') {
+ duplicate = true;
+ break;
+ }
+
+ if (duplicate) {
+ free(*p);
+ continue;
+ }
+
+ e[k++] = *p;
+ }
+
+ e[k] = NULL;
+ return e;
+}
+
+char **strv_env_clean(char **e) {
+ return strv_env_clean_log(e, NULL);
+}
diff --git a/src/shared/env-util.h b/src/shared/env-util.h
new file mode 100644
index 0000000000..8d2114b64c
--- /dev/null
+++ b/src/shared/env-util.h
@@ -0,0 +1,44 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ 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 <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdbool.h>
+#include <sys/types.h>
+
+bool env_name_is_valid(const char *e);
+bool env_value_is_valid(const char *e);
+bool env_assignment_is_valid(const char *e);
+
+bool strv_env_is_valid(char **e);
+char **strv_env_clean(char **l);
+char **strv_env_clean_log(char **e, const char *message);
+
+bool strv_env_name_or_assignment_is_valid(char **l);
+
+char **strv_env_merge(unsigned n_lists, ...);
+char **strv_env_delete(char **x, unsigned n_lists, ...); /* New copy */
+
+char **strv_env_set(char **x, const char *p); /* New copy ... */
+char **strv_env_unset(char **l, const char *p); /* In place ... */
+
+char *strv_env_get_n(char **l, const char *name, size_t k) _pure_;
+char *strv_env_get(char **x, const char *n) _pure_;
diff --git a/src/shared/exit-status.h b/src/shared/exit-status.h
index d3b548fc96..1f035a3007 100644
--- a/src/shared/exit-status.h
+++ b/src/shared/exit-status.h
@@ -82,7 +82,7 @@ typedef struct ExitStatusSet {
Set *signal;
} ExitStatusSet;
-const char* exit_status_to_string(ExitStatus status, ExitStatusLevel level);
+const char* exit_status_to_string(ExitStatus status, ExitStatusLevel level) _const_;
bool is_clean_exit(int code, int status, ExitStatusSet *success_status);
bool is_clean_exit_lsb(int code, int status, ExitStatusSet *success_status);
diff --git a/src/shared/fileio-label.c b/src/shared/fileio-label.c
new file mode 100644
index 0000000000..0711826e85
--- /dev/null
+++ b/src/shared/fileio-label.c
@@ -0,0 +1,55 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 Lennart Poettering
+ Copyright 2010 Harald Hoyer
+
+ 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 <http://www.gnu.org/licenses/>.
+***/
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "fileio-label.h"
+#include "label.h"
+
+int write_string_file_atomic_label(const char *fn, const char *line) {
+ int r;
+
+ r = label_context_set(fn, S_IFREG);
+ if (r < 0)
+ return r;
+
+ write_string_file_atomic(fn, line);
+
+ label_context_clear();
+
+ return r;
+}
+
+int write_env_file_label(const char *fname, char **l) {
+ int r;
+
+ r = label_context_set(fname, S_IFREG);
+ if (r < 0)
+ return r;
+
+ write_env_file(fname, l);
+
+ label_context_clear();
+
+ return r;
+}
diff --git a/src/shared/fileio-label.h b/src/shared/fileio-label.h
new file mode 100644
index 0000000000..fce4fe0d73
--- /dev/null
+++ b/src/shared/fileio-label.h
@@ -0,0 +1,29 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 Lennart Poettering
+ Copyright 2010 Harald Hoyer
+
+ 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 <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdio.h>
+#include "fileio.h"
+
+int write_string_file_atomic_label(const char *fn, const char *line);
+int write_env_file_label(const char *fname, char **l);
diff --git a/src/shared/fileio.c b/src/shared/fileio.c
new file mode 100644
index 0000000000..ad068bf30d
--- /dev/null
+++ b/src/shared/fileio.c
@@ -0,0 +1,596 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU 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 <http://www.gnu.org/licenses/>.
+***/
+
+#include <unistd.h>
+#include "fileio.h"
+#include "util.h"
+#include "strv.h"
+
+
+int write_string_to_file(FILE *f, const char *line) {
+ errno = 0;
+ fputs(line, f);
+ if (!endswith(line, "\n"))
+ fputc('\n', f);
+
+ fflush(f);
+
+ if (ferror(f))
+ return errno ? -errno : -EIO;
+
+ return 0;
+}
+
+int write_string_file(const char *fn, const char *line) {
+ _cleanup_fclose_ FILE *f = NULL;
+
+ assert(fn);
+ assert(line);
+
+ f = fopen(fn, "we");
+ if (!f)
+ return -errno;
+
+ return write_string_to_file(f, line);
+}
+
+int write_string_file_atomic(const char *fn, const char *line) {
+ _cleanup_fclose_ FILE *f = NULL;
+ _cleanup_free_ char *p = NULL;
+ int r;
+
+ assert(fn);
+ assert(line);
+
+ r = fopen_temporary(fn, &f, &p);
+ if (r < 0)
+ return r;
+
+ fchmod_umask(fileno(f), 0644);
+
+ errno = 0;
+ fputs(line, f);
+ if (!endswith(line, "\n"))
+ fputc('\n', f);
+
+ fflush(f);
+
+ if (ferror(f))
+ r = errno ? -errno : -EIO;
+ else {
+ if (rename(p, fn) < 0)
+ r = -errno;
+ else
+ r = 0;
+ }
+
+ if (r < 0)
+ unlink(p);
+
+ return r;
+}
+
+int read_one_line_file(const char *fn, char **line) {
+ _cleanup_fclose_ FILE *f = NULL;
+ char t[LINE_MAX], *c;
+
+ assert(fn);
+ assert(line);
+
+ f = fopen(fn, "re");
+ if (!f)
+ return -errno;
+
+ if (!fgets(t, sizeof(t), f)) {
+
+ if (ferror(f))
+ return errno ? -errno : -EIO;
+
+ t[0] = 0;
+ }
+
+ c = strdup(t);
+ if (!c)
+ return -ENOMEM;
+ truncate_nl(c);
+
+ *line = c;
+ return 0;
+}
+
+int read_full_file(const char *fn, char **contents, size_t *size) {
+ _cleanup_fclose_ FILE *f = NULL;
+ size_t n, l;
+ _cleanup_free_ char *buf = NULL;
+ struct stat st;
+
+ assert(fn);
+ assert(contents);
+
+ f = fopen(fn, "re");
+ if (!f)
+ return -errno;
+
+ if (fstat(fileno(f), &st) < 0)
+ return -errno;
+
+ /* Safety check */
+ if (st.st_size > 4*1024*1024)
+ return -E2BIG;
+
+ n = st.st_size > 0 ? st.st_size : LINE_MAX;
+ l = 0;
+
+ for (;;) {
+ char *t;
+ size_t k;
+
+ t = realloc(buf, n+1);
+ if (!t)
+ return -ENOMEM;
+
+ buf = t;
+ k = fread(buf + l, 1, n - l, f);
+
+ if (k <= 0) {
+ if (ferror(f))
+ return -errno;
+
+ break;
+ }
+
+ l += k;
+ n *= 2;
+
+ /* Safety check */
+ if (n > 4*1024*1024)
+ return -E2BIG;
+ }
+
+ buf[l] = 0;
+ *contents = buf;
+ buf = NULL;
+
+ if (size)
+ *size = l;
+
+ return 0;
+}
+
+static int parse_env_file_internal(
+ const char *fname,
+ const char *newline,
+ int (*push) (const char *key, char *value, void *userdata),
+ void *userdata) {
+
+ _cleanup_free_ char *contents = NULL, *key = NULL;
+ size_t key_alloc = 0, n_key = 0, value_alloc = 0, n_value = 0, last_value_whitespace = (size_t) -1, last_key_whitespace = (size_t) -1;
+ char *p, *value = NULL;
+ int r;
+
+ enum {
+ PRE_KEY,
+ KEY,
+ PRE_VALUE,
+ VALUE,
+ VALUE_ESCAPE,
+ SINGLE_QUOTE_VALUE,
+ SINGLE_QUOTE_VALUE_ESCAPE,
+ DOUBLE_QUOTE_VALUE,
+ DOUBLE_QUOTE_VALUE_ESCAPE,
+ COMMENT,
+ COMMENT_ESCAPE
+ } state = PRE_KEY;
+
+ assert(fname);
+ assert(newline);
+
+ r = read_full_file(fname, &contents, NULL);
+ if (r < 0)
+ return r;
+
+ for (p = contents; *p; p++) {
+ char c = *p;
+
+ switch (state) {
+
+ case PRE_KEY:
+ if (strchr(COMMENTS, c))
+ state = COMMENT;
+ else if (!strchr(WHITESPACE, c)) {
+ state = KEY;
+ last_key_whitespace = (size_t) -1;
+
+ if (!greedy_realloc((void**) &key, &key_alloc, n_key+2)) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ key[n_key++] = c;
+ }
+ break;
+
+ case KEY:
+ if (strchr(newline, c)) {
+ state = PRE_KEY;
+ n_key = 0;
+ } else if (c == '=') {
+ state = PRE_VALUE;
+ last_value_whitespace = (size_t) -1;
+ } else {
+ if (!strchr(WHITESPACE, c))
+ last_key_whitespace = (size_t) -1;
+ else if (last_key_whitespace == (size_t) -1)
+ last_key_whitespace = n_key;
+
+ if (!greedy_realloc((void**) &key, &key_alloc, n_key+2)) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ key[n_key++] = c;
+ }
+
+ break;
+
+ case PRE_VALUE:
+ if (strchr(newline, c)) {
+ state = PRE_KEY;
+ key[n_key] = 0;
+
+ if (value)
+ value[n_value] = 0;
+
+ /* strip trailing whitespace from key */
+ if (last_key_whitespace != (size_t) -1)
+ key[last_key_whitespace] = 0;
+
+ r = push(key, value, userdata);
+ if (r < 0)
+ goto fail;
+
+ n_key = 0;
+ value = NULL;
+ value_alloc = n_value = 0;
+
+ } else if (c == '\'')
+ state = SINGLE_QUOTE_VALUE;
+ else if (c == '\"')
+ state = DOUBLE_QUOTE_VALUE;
+ else if (c == '\\')
+ state = VALUE_ESCAPE;
+ else if (!strchr(WHITESPACE, c)) {
+ state = VALUE;
+
+ if (!greedy_realloc((void**) &value, &value_alloc, n_value+2)) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ value[n_value++] = c;
+ }
+
+ break;
+
+ case VALUE:
+ if (strchr(newline, c)) {
+ state = PRE_KEY;
+
+ key[n_key] = 0;
+
+ if (value)
+ value[n_value] = 0;
+
+ /* Chomp off trailing whitespace from value */
+ if (last_value_whitespace != (size_t) -1)
+ value[last_value_whitespace] = 0;
+
+ /* strip trailing whitespace from key */
+ if (last_key_whitespace != (size_t) -1)
+ key[last_key_whitespace] = 0;
+
+ r = push(key, value, userdata);
+ if (r < 0)
+ goto fail;
+
+ n_key = 0;
+ value = NULL;
+ value_alloc = n_value = 0;
+
+ } else if (c == '\\') {
+ state = VALUE_ESCAPE;
+ last_value_whitespace = (size_t) -1;
+ } else {
+ if (!strchr(WHITESPACE, c))
+ last_value_whitespace = (size_t) -1;
+ else if (last_value_whitespace == (size_t) -1)
+ last_value_whitespace = n_value;
+
+ if (!greedy_realloc((void**) &value, &value_alloc, n_value+2)) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ value[n_value++] = c;
+ }
+
+ break;
+
+ case VALUE_ESCAPE:
+ state = VALUE;
+
+ if (!strchr(newline, c)) {
+ /* Escaped newlines we eat up entirely */
+ if (!greedy_realloc((void**) &value, &value_alloc, n_value+2)) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ value[n_value++] = c;
+ }
+ break;
+
+ case SINGLE_QUOTE_VALUE:
+ if (c == '\'')
+ state = PRE_VALUE;
+ else if (c == '\\')
+ state = SINGLE_QUOTE_VALUE_ESCAPE;
+ else {
+ if (!greedy_realloc((void**) &value, &value_alloc, n_value+2)) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ value[n_value++] = c;
+ }
+
+ break;
+
+ case SINGLE_QUOTE_VALUE_ESCAPE:
+ state = SINGLE_QUOTE_VALUE;
+
+ if (!strchr(newline, c)) {
+ if (!greedy_realloc((void**) &value, &value_alloc, n_value+2)) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ value[n_value++] = c;
+ }
+ break;
+
+ case DOUBLE_QUOTE_VALUE:
+ if (c == '\"')
+ state = PRE_VALUE;
+ else if (c == '\\')
+ state = DOUBLE_QUOTE_VALUE_ESCAPE;
+ else {
+ if (!greedy_realloc((void**) &value, &value_alloc, n_value+2)) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ value[n_value++] = c;
+ }
+
+ break;
+
+ case DOUBLE_QUOTE_VALUE_ESCAPE:
+ state = DOUBLE_QUOTE_VALUE;
+
+ if (!strchr(newline, c)) {
+ if (!greedy_realloc((void**) &value, &value_alloc, n_value+2)) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ value[n_value++] = c;
+ }
+ break;
+
+ case COMMENT:
+ if (c == '\\')
+ state = COMMENT_ESCAPE;
+ else if (strchr(newline, c))
+ state = PRE_KEY;
+ break;
+
+ case COMMENT_ESCAPE:
+ state = COMMENT;
+ break;
+ }
+ }
+
+ if (state == PRE_VALUE ||
+ state == VALUE ||
+ state == VALUE_ESCAPE ||
+ state == SINGLE_QUOTE_VALUE ||
+ state == SINGLE_QUOTE_VALUE_ESCAPE ||
+ state == DOUBLE_QUOTE_VALUE ||
+ state == DOUBLE_QUOTE_VALUE_ESCAPE) {
+
+ key[n_key] = 0;
+
+ if (value)
+ value[n_value] = 0;
+
+ if (state == VALUE)
+ if (last_value_whitespace != (size_t) -1)
+ value[last_value_whitespace] = 0;
+
+ /* strip trailing whitespace from key */
+ if (last_key_whitespace != (size_t) -1)
+ key[last_key_whitespace] = 0;
+
+ r = push(key, value, userdata);
+ if (r < 0)
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ free(value);
+ return r;
+}
+
+static int parse_env_file_push(const char *key, char *value, void *userdata) {
+ const char *k;
+ va_list* ap = (va_list*) userdata;
+ va_list aq;
+
+ va_copy(aq, *ap);
+
+ while ((k = va_arg(aq, const char *))) {
+ char **v;
+
+ v = va_arg(aq, char **);
+
+ if (streq(key, k)) {
+ va_end(aq);
+ free(*v);
+ *v = value;
+ return 1;
+ }
+ }
+
+ va_end(aq);
+
+ free(value);
+ return 0;
+}
+
+int parse_env_file(
+ const char *fname,
+ const char *newline, ...) {
+
+ va_list ap;
+ int r;
+
+ if (!newline)
+ newline = NEWLINE;
+
+ va_start(ap, newline);
+ r = parse_env_file_internal(fname, newline, parse_env_file_push, &ap);
+ va_end(ap);
+
+ return r;
+}
+
+static int load_env_file_push(const char *key, char *value, void *userdata) {
+ char ***m = userdata;
+ char *p;
+ int r;
+
+ p = strjoin(key, "=", strempty(value), NULL);
+ if (!p)
+ return -ENOMEM;
+
+ r = strv_push(m, p);
+ if (r < 0) {
+ free(p);
+ return r;
+ }
+
+ free(value);
+ return 0;
+}
+
+int load_env_file(const char *fname, const char *newline, char ***rl) {
+ char **m = NULL;
+ int r;
+
+ if (!newline)
+ newline = NEWLINE;
+
+ r = parse_env_file_internal(fname, newline, load_env_file_push, &m);
+ if (r < 0) {
+ strv_free(m);
+ return r;
+ }
+
+ *rl = m;
+ return 0;
+}
+
+static void write_env_var(FILE *f, const char *v) {
+ const char *p;
+
+ p = strchr(v, '=');
+ if (!p) {
+ /* Fallback */
+ fputs(v, f);
+ fputc('\n', f);
+ return;
+ }
+
+ p++;
+ fwrite(v, 1, p-v, f);
+
+ if (string_has_cc(p) || chars_intersect(p, WHITESPACE "\'\"\\`$")) {
+ fputc('\"', f);
+
+ for (; *p; p++) {
+ if (strchr("\'\"\\`$", *p))
+ fputc('\\', f);
+
+ fputc(*p, f);
+ }
+
+ fputc('\"', f);
+ } else
+ fputs(p, f);
+
+ fputc('\n', f);
+}
+
+int write_env_file(const char *fname, char **l) {
+ char **i;
+ _cleanup_free_ char *p = NULL;
+ _cleanup_fclose_ FILE *f = NULL;
+ int r;
+
+ r = fopen_temporary(fname, &f, &p);
+ if (r < 0)
+ return r;
+
+ fchmod_umask(fileno(f), 0644);
+
+ errno = 0;
+ STRV_FOREACH(i, l)
+ write_env_var(f, *i);
+
+ fflush(f);
+
+ if (ferror(f))
+ r = errno ? -errno : -EIO;
+ else {
+ if (rename(p, fname) < 0)
+ r = -errno;
+ else
+ r = 0;
+ }
+
+ if (r < 0)
+ unlink(p);
+
+ return r;
+}
diff --git a/src/shared/fileio.h b/src/shared/fileio.h
new file mode 100644
index 0000000000..0ca6878ea4
--- /dev/null
+++ b/src/shared/fileio.h
@@ -0,0 +1,37 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ 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 <http://www.gnu.org/licenses/>.
+***/
+#include <stddef.h>
+#include <stdio.h>
+
+#include "macro.h"
+
+int write_string_to_file(FILE *f, const char *line);
+int write_string_file(const char *fn, const char *line);
+int write_string_file_atomic(const char *fn, const char *line);
+
+int read_one_line_file(const char *fn, char **line);
+int read_full_file(const char *fn, char **contents, size_t *size);
+
+int parse_env_file(const char *fname, const char *separator, ...) _sentinel_;
+int load_env_file(const char *fname, const char *separator, char ***l);
+int write_env_file(const char *fname, char **l);
diff --git a/src/shared/hashmap.c b/src/shared/hashmap.c
index a2c728d642..9f7db34397 100644
--- a/src/shared/hashmap.c
+++ b/src/shared/hashmap.c
@@ -309,6 +309,17 @@ void hashmap_free_free(Hashmap *h) {
hashmap_free(h);
}
+void hashmap_free_free_free(Hashmap *h) {
+
+ /* Free the hashmap and all data and key objects in it */
+
+ if (!h)
+ return;
+
+ hashmap_clear_free_free(h);
+ hashmap_free(h);
+}
+
void hashmap_clear(Hashmap *h) {
if (!h)
return;
@@ -327,6 +338,22 @@ void hashmap_clear_free(Hashmap *h) {
free(p);
}
+void hashmap_clear_free_free(Hashmap *h) {
+ if (!h)
+ return;
+
+ while (h->iterate_list_head) {
+ void *a, *b;
+
+ a = h->iterate_list_head->value;
+ b = (void*) h->iterate_list_head->key;
+ remove_entry(h, h->iterate_list_head);
+ free(a);
+ free(b);
+ }
+}
+
+
static struct hashmap_entry *hash_scan(Hashmap *h, unsigned hash, const void *key) {
struct hashmap_entry *e;
assert(h);
diff --git a/src/shared/hashmap.h b/src/shared/hashmap.h
index 6fd71cf519..15b7e27585 100644
--- a/src/shared/hashmap.h
+++ b/src/shared/hashmap.h
@@ -23,6 +23,8 @@
#include <stdbool.h>
+#include "macro.h"
+
/* Pretty straightforward hash table implementation. As a minor
* optimization a NULL hashmap object will be treated as empty hashmap
* for all read operations. That way it is not necessary to
@@ -38,29 +40,33 @@ typedef _IteratorStruct* Iterator;
typedef unsigned (*hash_func_t)(const void *p);
typedef int (*compare_func_t)(const void *a, const void *b);
-unsigned string_hash_func(const void *p);
-int string_compare_func(const void *a, const void *b);
+unsigned string_hash_func(const void *p) _pure_;
+int string_compare_func(const void *a, const void *b) _pure_;
-unsigned trivial_hash_func(const void *p);
-int trivial_compare_func(const void *a, const void *b);
+/* This will compare the passed pointers directly, and will not
+ * dereference them. This is hence not useful for strings or
+ * suchlike. */
+unsigned trivial_hash_func(const void *p) _const_;
+int trivial_compare_func(const void *a, const void *b) _const_;
-unsigned uint64_hash_func(const void *p);
-int uint64_compare_func(const void *a, const void *b);
+unsigned uint64_hash_func(const void *p) _pure_;
+int uint64_compare_func(const void *a, const void *b) _pure_;
Hashmap *hashmap_new(hash_func_t hash_func, compare_func_t compare_func);
void hashmap_free(Hashmap *h);
void hashmap_free_free(Hashmap *h);
+void hashmap_free_free_free(Hashmap *h);
Hashmap *hashmap_copy(Hashmap *h);
int hashmap_ensure_allocated(Hashmap **h, hash_func_t hash_func, compare_func_t compare_func);
int hashmap_put(Hashmap *h, const void *key, void *value);
int hashmap_update(Hashmap *h, const void *key, void *value);
int hashmap_replace(Hashmap *h, const void *key, void *value);
-void* hashmap_get(Hashmap *h, const void *key);
-void* hashmap_get2(Hashmap *h, const void *key, void **rkey);
+void *hashmap_get(Hashmap *h, const void *key);
+void *hashmap_get2(Hashmap *h, const void *key, void **rkey);
bool hashmap_contains(Hashmap *h, const void *key);
-void* hashmap_remove(Hashmap *h, const void *key);
-void* hashmap_remove_value(Hashmap *h, const void *key, void *value);
+void *hashmap_remove(Hashmap *h, const void *key);
+void *hashmap_remove_value(Hashmap *h, const void *key, void *value);
int hashmap_remove_and_put(Hashmap *h, const void *old_key, const void *new_key, void *value);
int hashmap_remove_and_replace(Hashmap *h, const void *old_key, const void *new_key, void *value);
@@ -68,8 +74,8 @@ int hashmap_merge(Hashmap *h, Hashmap *other);
void hashmap_move(Hashmap *h, Hashmap *other);
int hashmap_move_one(Hashmap *h, Hashmap *other, const void *key);
-unsigned hashmap_size(Hashmap *h);
-bool hashmap_isempty(Hashmap *h);
+unsigned hashmap_size(Hashmap *h) _pure_;
+bool hashmap_isempty(Hashmap *h) _pure_;
void *hashmap_iterate(Hashmap *h, Iterator *i, const void **key);
void *hashmap_iterate_backwards(Hashmap *h, Iterator *i, const void **key);
@@ -77,12 +83,13 @@ void *hashmap_iterate_skip(Hashmap *h, const void *key, Iterator *i);
void hashmap_clear(Hashmap *h);
void hashmap_clear_free(Hashmap *h);
+void hashmap_clear_free_free(Hashmap *h);
void *hashmap_steal_first(Hashmap *h);
void *hashmap_steal_first_key(Hashmap *h);
-void* hashmap_first(Hashmap *h);
-void* hashmap_first_key(Hashmap *h);
-void* hashmap_last(Hashmap *h);
+void *hashmap_first(Hashmap *h) _pure_;
+void *hashmap_first_key(Hashmap *h) _pure_;
+void *hashmap_last(Hashmap *h) _pure_;
void *hashmap_next(Hashmap *h, const void *key);
diff --git a/src/shared/hwclock.c b/src/shared/hwclock.c
index f9adf0369e..cc11faa6c3 100644
--- a/src/shared/hwclock.c
+++ b/src/shared/hwclock.c
@@ -41,83 +41,7 @@
#include "log.h"
#include "strv.h"
#include "hwclock.h"
-
-static int rtc_open(int flags) {
- int fd;
- DIR *d;
-
- /* First, we try to make use of the /dev/rtc symlink. If that
- * doesn't exist, we open the first RTC which has hctosys=1
- * set. If we don't find any we just take the first RTC that
- * exists at all. */
-
- fd = open("/dev/rtc", flags);
- if (fd >= 0)
- return fd;
-
- d = opendir("/sys/class/rtc");
- if (!d)
- goto fallback;
-
- for (;;) {
- char *p, *v;
- struct dirent *de;
- union dirent_storage buf;
- int r;
-
- r = readdir_r(d, &buf.de, &de);
- if (r != 0)
- goto fallback;
-
- if (!de)
- goto fallback;
-
- if (ignore_file(de->d_name))
- continue;
-
- p = strjoin("/sys/class/rtc/", de->d_name, "/hctosys", NULL);
- if (!p) {
- closedir(d);
- return -ENOMEM;
- }
-
- r = read_one_line_file(p, &v);
- free(p);
-
- if (r < 0)
- continue;
-
- r = parse_boolean(v);
- free(v);
-
- if (r <= 0)
- continue;
-
- p = strappend("/dev/", de->d_name);
- if (!p) {
- closedir(d);
- return -ENOMEM;
- }
-
- fd = open(p, flags);
- free(p);
-
- if (fd >= 0) {
- closedir(d);
- return fd;
- }
- }
-
-fallback:
- if (d)
- closedir(d);
-
- fd = open("/dev/rtc0", flags);
- if (fd < 0)
- return -errno;
-
- return fd;
-}
+#include "fileio.h"
int hwclock_get_time(struct tm *tm) {
int fd;
@@ -125,7 +49,7 @@ int hwclock_get_time(struct tm *tm) {
assert(tm);
- fd = rtc_open(O_RDONLY|O_CLOEXEC);
+ fd = open("/dev/rtc", O_RDONLY|O_CLOEXEC);
if (fd < 0)
return -errno;
@@ -149,7 +73,7 @@ int hwclock_set_time(const struct tm *tm) {
assert(tm);
- fd = rtc_open(O_RDONLY|O_CLOEXEC);
+ fd = open("/dev/rtc", O_RDONLY|O_CLOEXEC);
if (fd < 0)
return -errno;
@@ -162,8 +86,7 @@ int hwclock_set_time(const struct tm *tm) {
}
int hwclock_is_localtime(void) {
- FILE *f;
- bool local = false;
+ _cleanup_fclose_ FILE *f;
/*
* The third line of adjtime is "UTC" or "LOCAL" or nothing.
@@ -180,19 +103,16 @@ int hwclock_is_localtime(void) {
b = fgets(line, sizeof(line), f) &&
fgets(line, sizeof(line), f) &&
fgets(line, sizeof(line), f);
-
- fclose(f);
-
if (!b)
return -EIO;
truncate_nl(line);
- local = streq(line, "LOCAL");
+ return streq(line, "LOCAL");
- } else if (errno != -ENOENT)
+ } else if (errno != ENOENT)
return -errno;
- return local;
+ return 0;
}
int hwclock_set_timezone(int *min) {
diff --git a/src/shared/install-printf.c b/src/shared/install-printf.c
new file mode 100644
index 0000000000..c44459b4e0
--- /dev/null
+++ b/src/shared/install-printf.c
@@ -0,0 +1,132 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ 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 <http://www.gnu.org/licenses/>.
+***/
+
+#include <assert.h>
+#include <stdlib.h>
+
+#include "specifier.h"
+#include "unit-name.h"
+#include "util.h"
+#include "install-printf.h"
+
+static char *specifier_prefix_and_instance(char specifier, void *data, void *userdata) {
+ InstallInfo *i = userdata;
+ assert(i);
+
+ return unit_name_to_prefix_and_instance(i->name);
+}
+
+static char *specifier_prefix(char specifier, void *data, void *userdata) {
+ InstallInfo *i = userdata;
+ assert(i);
+
+ return unit_name_to_prefix(i->name);
+}
+
+static char *specifier_instance(char specifier, void *data, void *userdata) {
+ InstallInfo *i = userdata;
+ char *instance;
+ int r;
+
+ assert(i);
+
+ r = unit_name_to_instance(i->name, &instance);
+ if (r < 0)
+ return NULL;
+ if (instance != NULL)
+ return instance;
+ else
+ return strdup("");
+}
+
+static char *specifier_user_name(char specifier, void *data, void *userdata) {
+ InstallInfo *i = userdata;
+ const char *username;
+ _cleanup_free_ char *tmp = NULL;
+ char *printed = NULL;
+
+ assert(i);
+
+ if (i->user)
+ username = i->user;
+ else
+ /* get USER env from env or our own uid */
+ username = tmp = getusername_malloc();
+
+ switch (specifier) {
+ case 'u':
+ printed = strdup(username);
+ break;
+ case 'U': {
+ /* fish username from passwd */
+ uid_t uid;
+ int r;
+
+ r = get_user_creds(&username, &uid, NULL, NULL, NULL);
+ if (r < 0)
+ return NULL;
+
+ if (asprintf(&printed, "%d", uid) < 0)
+ return NULL;
+ break;
+ }}
+
+ return printed;
+}
+
+
+char *install_full_printf(InstallInfo *i, const char *format) {
+
+ /* This is similar to unit_full_printf() but does not support
+ * anything path-related.
+ *
+ * %n: the full id of the unit (foo@bar.waldo)
+ * %N: the id of the unit without the suffix (foo@bar)
+ * %p: the prefix (foo)
+ * %i: the instance (bar)
+
+ * %U the UID of the configured user or running user
+ * %u the username of the configured user or running user
+ * %m the machine ID of the running system
+ * %H the host name of the running system
+ * %b the boot ID of the running system
+ */
+
+ const Specifier table[] = {
+ { 'n', specifier_string, i->name },
+ { 'N', specifier_prefix_and_instance, NULL },
+ { 'p', specifier_prefix, NULL },
+ { 'i', specifier_instance, NULL },
+
+ { 'U', specifier_user_name, NULL },
+ { 'u', specifier_user_name, NULL },
+
+ { 'm', specifier_machine_id, NULL },
+ { 'H', specifier_host_name, NULL },
+ { 'b', specifier_boot_id, NULL },
+ { 0, NULL, NULL }
+ };
+
+ assert(i);
+ assert(format);
+
+ return specifier_printf(format, table, i);
+}
diff --git a/src/shared/install-printf.h b/src/shared/install-printf.h
new file mode 100644
index 0000000000..46f5294d21
--- /dev/null
+++ b/src/shared/install-printf.h
@@ -0,0 +1,25 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ 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 <http://www.gnu.org/licenses/>.
+***/
+
+#pragma once
+
+#include "install.h"
+char *install_full_printf(InstallInfo *i, const char *format);
diff --git a/src/shared/install.c b/src/shared/install.c
index a9d75f3b7c..edf4d2a9fe 100644
--- a/src/shared/install.c
+++ b/src/shared/install.c
@@ -36,21 +36,19 @@
#include "install.h"
#include "conf-parser.h"
#include "conf-files.h"
-
-typedef struct {
- char *name;
- char *path;
-
- char **aliases;
- char **wanted_by;
- char **required_by;
-} InstallInfo;
+#include "specifier.h"
+#include "install-printf.h"
typedef struct {
Hashmap *will_install;
Hashmap *have_installed;
} InstallContext;
+#define _cleanup_lookup_paths_free_ \
+ __attribute__((cleanup(lookup_paths_free)))
+#define _cleanup_install_context_done_ \
+ __attribute__((cleanup(install_context_done)))
+
static int lookup_paths_init_from_scope(LookupPaths *paths, UnitFileScope scope) {
assert(paths);
assert(scope >= 0);
@@ -180,11 +178,9 @@ static int mark_symlink_for_removal(
path_kill_slashes(n);
- r = set_put(*remove_symlinks_to, n);
- if (r < 0) {
- free(n);
+ r = set_consume(*remove_symlinks_to, n);
+ if (r < 0)
return r == -EEXIST ? 0 : r;
- }
return 0;
}
@@ -200,7 +196,7 @@ static int remove_marked_symlinks_fd(
char** files) {
int r = 0;
- DIR *d;
+ _cleanup_closedir_ DIR *d = NULL;
assert(remove_symlinks_to);
assert(fd >= 0);
@@ -237,7 +233,7 @@ static int remove_marked_symlinks_fd(
if (de->d_type == DT_DIR) {
int nfd, q;
- char *p;
+ _cleanup_free_ char *p = NULL;
nfd = openat(fd, de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
if (nfd < 0) {
@@ -252,32 +248,26 @@ static int remove_marked_symlinks_fd(
p = path_make_absolute(de->d_name, path);
if (!p) {
close_nointr_nofail(nfd);
- r = -ENOMEM;
- break;
+ return -ENOMEM;
}
/* This will close nfd, regardless whether it succeeds or not */
q = remove_marked_symlinks_fd(remove_symlinks_to, nfd, p, config_path, deleted, changes, n_changes, files);
- free(p);
if (r == 0)
r = q;
} else if (de->d_type == DT_LNK) {
- char *p, *dest;
+ _cleanup_free_ char *p = NULL, *dest = NULL;
int q;
bool found;
p = path_make_absolute(de->d_name, path);
- if (!p) {
- r = -ENOMEM;
- break;
- }
+ if (!p)
+ return -ENOMEM;
q = readlink_and_canonicalize(p, &dest);
if (q < 0) {
- free(p);
-
if (q == -ENOENT)
continue;
@@ -316,14 +306,9 @@ static int remove_marked_symlinks_fd(
}
}
}
-
- free(p);
- free(dest);
}
}
- closedir(d);
-
return r;
}
@@ -375,7 +360,7 @@ static int find_symlinks_fd(
bool *same_name_link) {
int r = 0;
- DIR *d;
+ _cleanup_closedir_ DIR *d = NULL;
assert(name);
assert(fd >= 0);
@@ -395,13 +380,11 @@ static int find_symlinks_fd(
union dirent_storage buf;
k = readdir_r(d, &buf.de, &de);
- if (k != 0) {
- r = -errno;
- break;
- }
+ if (k != 0)
+ return -errno;
if (!de)
- break;
+ return r;
if (ignore_file(de->d_name))
continue;
@@ -410,7 +393,7 @@ static int find_symlinks_fd(
if (de->d_type == DT_DIR) {
int nfd, q;
- char *p;
+ _cleanup_free_ char *p = NULL;
nfd = openat(fd, de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
if (nfd < 0) {
@@ -425,39 +408,31 @@ static int find_symlinks_fd(
p = path_make_absolute(de->d_name, path);
if (!p) {
close_nointr_nofail(nfd);
- r = -ENOMEM;
- break;
+ return -ENOMEM;
}
/* This will close nfd, regardless whether it succeeds or not */
q = find_symlinks_fd(name, nfd, p, config_path, same_name_link);
- free(p);
- if (q > 0) {
- r = 1;
- break;
- }
+ if (q > 0)
+ return 1;
if (r == 0)
r = q;
} else if (de->d_type == DT_LNK) {
- char *p, *dest;
+ _cleanup_free_ char *p = NULL, *dest = NULL;
bool found_path, found_dest, b = false;
int q;
/* Acquire symlink name */
p = path_make_absolute(de->d_name, path);
- if (!p) {
- r = -ENOMEM;
- break;
- }
+ if (!p)
+ return -ENOMEM;
/* Acquire symlink destination */
q = readlink_and_canonicalize(p, &dest);
if (q < 0) {
- free(p);
-
if (q == -ENOENT)
continue;
@@ -480,37 +455,25 @@ static int find_symlinks_fd(
else
found_dest = streq(path_get_file_name(dest), name);
- free(dest);
-
if (found_path && found_dest) {
- char *t;
+ _cleanup_free_ char *t = NULL;
/* Filter out same name links in the main
* config path */
t = path_make_absolute(name, config_path);
- if (!t) {
- free(p);
- r = -ENOMEM;
- break;
- }
+ if (!t)
+ return -ENOMEM;
b = path_equal(t, p);
- free(t);
}
- free(p);
-
if (b)
*same_name_link = true;
- else if (found_path || found_dest) {
- r = 1;
- break;
- }
+ else if (found_path || found_dest)
+ return 1;
}
}
- closedir(d);
-
return r;
}
@@ -543,7 +506,7 @@ static int find_symlinks_in_scope(
UnitFileState *state) {
int r;
- char _cleanup_free_ *path = NULL;
+ _cleanup_free_ char *path = NULL;
bool same_name_link_runtime = false, same_name_link = false;
assert(scope >= 0);
@@ -602,7 +565,7 @@ int unit_file_mask(
unsigned *n_changes) {
char **i;
- char _cleanup_free_ *prefix;
+ _cleanup_free_ char *prefix;
int r;
assert(scope >= 0);
@@ -613,7 +576,7 @@ int unit_file_mask(
return r;
STRV_FOREACH(i, files) {
- char _cleanup_free_ *path = NULL;
+ _cleanup_free_ char *path = NULL;
if (!unit_name_is_valid(*i, true)) {
if (r == 0)
@@ -735,25 +698,25 @@ int unit_file_link(
UnitFileChange **changes,
unsigned *n_changes) {
- LookupPaths paths;
- char **i, *config_path = NULL;
+ _cleanup_lookup_paths_free_ LookupPaths paths = {};
+ char **i;
+ _cleanup_free_ char *config_path = NULL;
int r, q;
assert(scope >= 0);
assert(scope < _UNIT_FILE_SCOPE_MAX);
- zero(paths);
-
r = lookup_paths_init_from_scope(&paths, scope);
if (r < 0)
return r;
r = get_config_path(scope, runtime, root_dir, &config_path);
if (r < 0)
- goto finish;
+ return r;
STRV_FOREACH(i, files) {
- char *path, *fn;
+ _cleanup_free_ char *path = NULL;
+ char *fn;
struct stat st;
fn = path_get_file_name(*i);
@@ -777,48 +740,34 @@ int unit_file_link(
}
q = in_search_path(*i, paths.unit_path);
- if (q < 0) {
- r = q;
- break;
- }
+ if (q < 0)
+ return q;
if (q > 0)
continue;
path = path_make_absolute(fn, config_path);
- if (!path) {
- r = -ENOMEM;
- break;
- }
+ if (!path)
+ return -ENOMEM;
if (symlink(*i, path) >= 0) {
add_file_change(changes, n_changes, UNIT_FILE_SYMLINK, path, *i);
-
- free(path);
continue;
}
if (errno == EEXIST) {
- char *dest = NULL;
+ _cleanup_free_ char *dest = NULL;
q = readlink_and_make_absolute(path, &dest);
if (q < 0 && errno != ENOENT) {
- free(path);
-
if (r == 0)
r = q;
-
continue;
}
- if (q >= 0 && path_equal(dest, *i)) {
- free(dest);
- free(path);
+ if (q >= 0 && path_equal(dest, *i))
continue;
- }
-
- free(dest);
if (force) {
unlink(path);
@@ -828,7 +777,6 @@ int unit_file_link(
add_file_change(changes, n_changes, UNIT_FILE_UNLINK, path, NULL);
add_file_change(changes, n_changes, UNIT_FILE_SYMLINK, path, *i);
- free(path);
continue;
}
}
@@ -839,14 +787,8 @@ int unit_file_link(
if (r == 0)
r = -errno;
}
-
- free(path);
}
- finish:
- lookup_paths_free(&paths);
- free(config_path);
-
return r;
}
@@ -977,15 +919,15 @@ static int install_info_add_auto(
return install_info_add(c, name_or_path, NULL);
}
-static int config_parse_also(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
+static int config_parse_also(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
char *w;
size_t l;
@@ -997,7 +939,7 @@ static int config_parse_also(
assert(rvalue);
FOREACH_WORD_QUOTED(w, l, rvalue, state) {
- char *n;
+ _cleanup_free_ char *n;
int r;
n = strndup(w, l);
@@ -1005,17 +947,40 @@ static int config_parse_also(
return -ENOMEM;
r = install_info_add(c, n, NULL);
- if (r < 0) {
- free(n);
+ if (r < 0)
return r;
- }
-
- free(n);
}
return 0;
}
+static int config_parse_user(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ InstallInfo *i = data;
+ char* printed;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+
+ printed = install_full_printf(i, rvalue);
+ if (!printed)
+ return -ENOMEM;
+
+ free(i->user);
+ i->user = printed;
+
+ return 0;
+}
+
static int unit_file_load(
InstallContext *c,
InstallInfo *info,
@@ -1027,11 +992,12 @@ static int unit_file_load(
{ "Install", "WantedBy", config_parse_strv, 0, &info->wanted_by },
{ "Install", "RequiredBy", config_parse_strv, 0, &info->required_by },
{ "Install", "Also", config_parse_also, 0, c },
+ { "Exec", "User", config_parse_user, 0, info },
{ NULL, NULL, NULL, 0, NULL }
};
int fd;
- FILE *f;
+ _cleanup_fclose_ FILE *f = NULL;
int r;
assert(c);
@@ -1048,8 +1014,8 @@ static int unit_file_load(
return -ENOMEM;
}
- r = config_parse(path, f, NULL, config_item_table_lookup, (void*) items, true, info);
- fclose(f);
+ r = config_parse(NULL, path, f, NULL,
+ config_item_table_lookup, (void*) items, true, true, info);
if (r < 0)
return r;
@@ -1149,15 +1115,13 @@ static int unit_file_can_install(
const char *name,
bool allow_symlink) {
- InstallContext c;
+ _cleanup_install_context_done_ InstallContext c = {};
InstallInfo *i;
int r;
assert(paths);
assert(name);
- zero(c);
-
r = install_info_add_auto(&c, name);
if (r < 0)
return r;
@@ -1172,8 +1136,6 @@ static int unit_file_can_install(
strv_length(i->wanted_by) +
strv_length(i->required_by);
- install_context_done(&c);
-
return r;
}
@@ -1184,7 +1146,7 @@ static int create_symlink(
UnitFileChange **changes,
unsigned *n_changes) {
- char *dest;
+ _cleanup_free_ char *dest = NULL;
int r;
assert(old_path);
@@ -1204,12 +1166,8 @@ static int create_symlink(
if (r < 0)
return r;
- if (path_equal(dest, old_path)) {
- free(dest);
+ if (path_equal(dest, old_path))
return 0;
- }
-
- free(dest);
if (!force)
return -EEXIST;
@@ -1239,16 +1197,17 @@ static int install_info_symlink_alias(
assert(config_path);
STRV_FOREACH(s, i->aliases) {
- char *alias_path;
+ _cleanup_free_ char *alias_path = NULL, *dst = NULL;
- alias_path = path_make_absolute(*s, config_path);
+ dst = install_full_printf(i, *s);
+ if (!dst)
+ return -ENOMEM;
+ alias_path = path_make_absolute(dst, config_path);
if (!alias_path)
return -ENOMEM;
q = create_symlink(i->path, alias_path, force, changes, n_changes);
- free(alias_path);
-
if (r == 0)
r = q;
}
@@ -1270,18 +1229,21 @@ static int install_info_symlink_wants(
assert(config_path);
STRV_FOREACH(s, i->wanted_by) {
- char *path;
+ _cleanup_free_ char *path = NULL, *dst = NULL;
+
+ dst = install_full_printf(i, *s);
+ if (!dst)
+ return -ENOMEM;
- if (!unit_name_is_valid(*s, true)) {
+ if (!unit_name_is_valid(dst, true)) {
r = -EINVAL;
continue;
}
- if (asprintf(&path, "%s/%s.wants/%s", config_path, *s, i->name) < 0)
+ if (asprintf(&path, "%s/%s.wants/%s", config_path, dst, i->name) < 0)
return -ENOMEM;
q = create_symlink(i->path, path, force, changes, n_changes);
- free(path);
if (r == 0)
r = q;
@@ -1304,18 +1266,21 @@ static int install_info_symlink_requires(
assert(config_path);
STRV_FOREACH(s, i->required_by) {
- char *path;
+ _cleanup_free_ char *path = NULL, *dst = NULL;
- if (!unit_name_is_valid(*s, true)) {
+ dst = install_full_printf(i, *s);
+ if (!dst)
+ return -ENOMEM;
+
+ if (!unit_name_is_valid(dst, true)) {
r = -EINVAL;
continue;
}
- if (asprintf(&path, "%s/%s.requires/%s", config_path, *s, i->name) < 0)
+ if (asprintf(&path, "%s/%s.requires/%s", config_path, dst, i->name) < 0)
return -ENOMEM;
q = create_symlink(i->path, path, force, changes, n_changes);
- free(path);
if (r == 0)
r = q;
@@ -1333,7 +1298,7 @@ static int install_info_symlink_link(
unsigned *n_changes) {
int r;
- char *path;
+ _cleanup_free_ char *path = NULL;
assert(i);
assert(paths);
@@ -1348,8 +1313,6 @@ static int install_info_symlink_link(
return -ENOMEM;
r = create_symlink(i->path, path, force, changes, n_changes);
- free(path);
-
return r;
}
@@ -1488,29 +1451,27 @@ int unit_file_enable(
UnitFileChange **changes,
unsigned *n_changes) {
- LookupPaths paths;
- InstallContext c;
- char **i, *config_path = NULL;
+ _cleanup_lookup_paths_free_ LookupPaths paths = {};
+ _cleanup_install_context_done_ InstallContext c = {};
+ char **i;
+ _cleanup_free_ char *config_path = NULL;
int r;
assert(scope >= 0);
assert(scope < _UNIT_FILE_SCOPE_MAX);
- zero(paths);
- zero(c);
-
r = lookup_paths_init_from_scope(&paths, scope);
if (r < 0)
return r;
r = get_config_path(scope, runtime, root_dir, &config_path);
if (r < 0)
- goto finish;
+ return r;
STRV_FOREACH(i, files) {
r = install_info_add_auto(&c, *i);
if (r < 0)
- goto finish;
+ return r;
}
/* This will return the number of symlink rules that were
@@ -1518,12 +1479,6 @@ int unit_file_enable(
useful to determine whether the passed files had any
installation data at all. */
r = install_context_apply(&c, &paths, config_path, root_dir, force, changes, n_changes);
-
-finish:
- install_context_done(&c);
- lookup_paths_free(&paths);
- free(config_path);
-
return r;
}
@@ -1535,30 +1490,28 @@ int unit_file_disable(
UnitFileChange **changes,
unsigned *n_changes) {
- LookupPaths paths;
- InstallContext c;
- char **i, *config_path = NULL;
- Set *remove_symlinks_to = NULL;
+ _cleanup_lookup_paths_free_ LookupPaths paths = {};
+ _cleanup_install_context_done_ InstallContext c = {};
+ char **i;
+ _cleanup_free_ char *config_path = NULL;
+ _cleanup_set_free_free_ Set *remove_symlinks_to = NULL;
int r, q;
assert(scope >= 0);
assert(scope < _UNIT_FILE_SCOPE_MAX);
- zero(paths);
- zero(c);
-
r = lookup_paths_init_from_scope(&paths, scope);
if (r < 0)
return r;
r = get_config_path(scope, runtime, root_dir, &config_path);
if (r < 0)
- goto finish;
+ return r;
STRV_FOREACH(i, files) {
r = install_info_add_auto(&c, *i);
if (r < 0)
- goto finish;
+ return r;
}
r = install_context_mark_for_removal(&c, &paths, &remove_symlinks_to, config_path, root_dir);
@@ -1567,12 +1520,6 @@ int unit_file_disable(
if (r == 0)
r = q;
-finish:
- install_context_done(&c);
- lookup_paths_free(&paths);
- set_free_free(remove_symlinks_to);
- free(config_path);
-
return r;
}
@@ -1585,34 +1532,32 @@ int unit_file_reenable(
UnitFileChange **changes,
unsigned *n_changes) {
- LookupPaths paths;
- InstallContext c;
- char **i, *config_path = NULL;
- Set *remove_symlinks_to = NULL;
+ _cleanup_lookup_paths_free_ LookupPaths paths = {};
+ _cleanup_install_context_done_ InstallContext c = {};
+ char **i;
+ _cleanup_free_ char *config_path = NULL;
+ _cleanup_set_free_free_ Set *remove_symlinks_to = NULL;
int r, q;
assert(scope >= 0);
assert(scope < _UNIT_FILE_SCOPE_MAX);
- zero(paths);
- zero(c);
-
r = lookup_paths_init_from_scope(&paths, scope);
if (r < 0)
return r;
r = get_config_path(scope, runtime, root_dir, &config_path);
if (r < 0)
- goto finish;
+ return r;
STRV_FOREACH(i, files) {
r = mark_symlink_for_removal(&remove_symlinks_to, *i);
if (r < 0)
- goto finish;
+ return r;
r = install_info_add_auto(&c, *i);
if (r < 0)
- goto finish;
+ return r;
}
r = remove_marked_symlinks(remove_symlinks_to, config_path, changes, n_changes, files);
@@ -1622,12 +1567,6 @@ int unit_file_reenable(
if (r == 0)
r = q;
-finish:
- lookup_paths_free(&paths);
- install_context_done(&c);
- set_free_free(remove_symlinks_to);
- free(config_path);
-
return r;
}
@@ -1636,17 +1575,16 @@ UnitFileState unit_file_get_state(
const char *root_dir,
const char *name) {
- LookupPaths paths;
+ _cleanup_lookup_paths_free_ LookupPaths paths = {};
UnitFileState state = _UNIT_FILE_STATE_INVALID;
- char **i, *path = NULL;
+ char **i;
+ _cleanup_free_ char *path = NULL;
int r;
assert(scope >= 0);
assert(scope < _UNIT_FILE_SCOPE_MAX);
assert(name);
- zero(paths);
-
if (root_dir && scope != UNIT_FILE_SYSTEM)
return -EINVAL;
@@ -1668,65 +1606,50 @@ UnitFileState unit_file_get_state(
else
asprintf(&path, "%s/%s", *i, name);
- if (!path) {
- r = -ENOMEM;
- goto finish;
- }
+ if (!path)
+ return -ENOMEM;
if (lstat(path, &st) < 0) {
r = -errno;
if (errno == ENOENT)
continue;
- goto finish;
+ return -errno;
}
- if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) {
- r = -ENOENT;
- goto finish;
- }
+ if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode))
+ return -ENOENT;
r = null_or_empty_path(path);
if (r < 0 && r != -ENOENT)
- goto finish;
+ return r;
else if (r > 0) {
state = path_startswith(*i, "/run") ?
UNIT_FILE_MASKED_RUNTIME : UNIT_FILE_MASKED;
- r = 0;
- goto finish;
+ return state;
}
r = find_symlinks_in_scope(scope, root_dir, name, &state);
- if (r < 0) {
- goto finish;
- } else if (r > 0) {
- r = 0;
- goto finish;
- }
+ if (r < 0)
+ return r;
+ else if (r > 0)
+ return state;
r = unit_file_can_install(&paths, root_dir, path, true);
- if (r < 0 && errno != -ENOENT)
- goto finish;
- else if (r > 0) {
- state = UNIT_FILE_DISABLED;
- r = 0;
- goto finish;
- } else if (r == 0) {
- state = UNIT_FILE_STATIC;
- r = 0;
- goto finish;
- }
+ if (r < 0 && errno != ENOENT)
+ return r;
+ else if (r > 0)
+ return UNIT_FILE_DISABLED;
+ else if (r == 0)
+ return UNIT_FILE_STATIC;
}
-finish:
- lookup_paths_free(&paths);
- free(path);
-
return r < 0 ? r : state;
}
int unit_file_query_preset(UnitFileScope scope, const char *name) {
- char **files, **i;
+ _cleanup_strv_free_ char **files = NULL;
+ char **i;
int r;
assert(scope >= 0);
@@ -1734,7 +1657,7 @@ int unit_file_query_preset(UnitFileScope scope, const char *name) {
assert(name);
if (scope == UNIT_FILE_SYSTEM)
- r = conf_files_list(&files, ".preset",
+ r = conf_files_list(&files, ".preset", NULL,
"/etc/systemd/system-preset",
"/usr/local/lib/systemd/system-preset",
"/usr/lib/systemd/system-preset",
@@ -1743,7 +1666,7 @@ int unit_file_query_preset(UnitFileScope scope, const char *name) {
#endif
NULL);
else if (scope == UNIT_FILE_GLOBAL)
- r = conf_files_list(&files, ".preset",
+ r = conf_files_list(&files, ".preset", NULL,
"/etc/systemd/user-preset",
"/usr/local/lib/systemd/user-preset",
"/usr/lib/systemd/user-preset",
@@ -1755,15 +1678,14 @@ int unit_file_query_preset(UnitFileScope scope, const char *name) {
return r;
STRV_FOREACH(i, files) {
- FILE *f;
+ _cleanup_fclose_ FILE *f;
f = fopen(*i, "re");
if (!f) {
if (errno == ENOENT)
continue;
- r = -errno;
- goto finish;
+ return -errno;
}
for (;;) {
@@ -1776,41 +1698,30 @@ int unit_file_query_preset(UnitFileScope scope, const char *name) {
if (!*l)
continue;
- if (strchr(COMMENTS, *l))
+ if (strchr(COMMENTS "\n", *l))
continue;
if (first_word(l, "enable")) {
l += 6;
l += strspn(l, WHITESPACE);
- if (fnmatch(l, name, FNM_NOESCAPE) == 0) {
- r = 1;
- fclose(f);
- goto finish;
- }
+ if (fnmatch(l, name, FNM_NOESCAPE) == 0)
+ return 1;
+
} else if (first_word(l, "disable")) {
l += 7;
l += strspn(l, WHITESPACE);
- if (fnmatch(l, name, FNM_NOESCAPE) == 0) {
- r = 0;
- fclose(f);
- goto finish;
- }
+ if (fnmatch(l, name, FNM_NOESCAPE) == 0)
+ return 0;
+
} else
log_debug("Couldn't parse line '%s'", l);
}
-
- fclose(f);
}
/* Default is "enable" */
- r = 1;
-
-finish:
- strv_free(files);
-
- return r;
+ return 1;
}
int unit_file_preset(
@@ -1822,37 +1733,32 @@ int unit_file_preset(
UnitFileChange **changes,
unsigned *n_changes) {
- LookupPaths paths;
- InstallContext plus, minus;
- char **i, *config_path = NULL;
- Set *remove_symlinks_to = NULL;
+ _cleanup_lookup_paths_free_ LookupPaths paths = {};
+ _cleanup_install_context_done_ InstallContext plus = {}, minus = {};
+ char **i;
+ _cleanup_free_ char *config_path = NULL;
+ _cleanup_set_free_free_ Set *remove_symlinks_to = NULL;
int r, q;
assert(scope >= 0);
assert(scope < _UNIT_FILE_SCOPE_MAX);
- zero(paths);
- zero(plus);
- zero(minus);
-
r = lookup_paths_init_from_scope(&paths, scope);
if (r < 0)
return r;
r = get_config_path(scope, runtime, root_dir, &config_path);
if (r < 0)
- goto finish;
+ return r;
STRV_FOREACH(i, files) {
- if (!unit_name_is_valid(*i, true)) {
- r = -EINVAL;
- goto finish;
- }
+ if (!unit_name_is_valid(*i, true))
+ return -EINVAL;
r = unit_file_query_preset(scope, *i);
if (r < 0)
- goto finish;
+ return r;
if (r)
r = install_info_add_auto(&plus, *i);
@@ -1860,46 +1766,49 @@ int unit_file_preset(
r = install_info_add_auto(&minus, *i);
if (r < 0)
- goto finish;
+ return r;
}
- r = install_context_mark_for_removal(&minus, &paths, &remove_symlinks_to, config_path, root_dir);
+ r = install_context_mark_for_removal(&minus, &paths, &remove_symlinks_to,
+ config_path, root_dir);
- q = remove_marked_symlinks(remove_symlinks_to, config_path, changes, n_changes, files);
+ q = remove_marked_symlinks(remove_symlinks_to, config_path,
+ changes, n_changes, files);
if (r == 0)
r = q;
/* Returns number of symlinks that where supposed to be installed. */
- q = install_context_apply(&plus, &paths, config_path, root_dir, force, changes, n_changes);
+ q = install_context_apply(&plus, &paths, config_path, root_dir, force,
+ changes, n_changes);
if (r == 0)
r = q;
-finish:
- lookup_paths_free(&paths);
- install_context_done(&plus);
- install_context_done(&minus);
- set_free_free(remove_symlinks_to);
- free(config_path);
-
return r;
}
+static void unitfilelist_free(UnitFileList **f) {
+ if (!*f)
+ return;
+
+ free((*f)->path);
+ free(*f);
+}
+
int unit_file_get_list(
UnitFileScope scope,
const char *root_dir,
Hashmap *h) {
- LookupPaths paths;
- char **i, *buf = NULL;
- DIR *d = NULL;
+ _cleanup_lookup_paths_free_ LookupPaths paths = {};
+ char **i;
+ _cleanup_free_ char *buf = NULL;
+ _cleanup_closedir_ DIR *d = NULL;
int r;
assert(scope >= 0);
assert(scope < _UNIT_FILE_SCOPE_MAX);
assert(h);
- zero(paths);
-
if (root_dir && scope != UNIT_FILE_SYSTEM)
return -EINVAL;
@@ -1914,10 +1823,9 @@ int unit_file_get_list(
buf = NULL;
if (root_dir) {
- if (asprintf(&buf, "%s/%s", root_dir, *i) < 0) {
- r = -ENOMEM;
- goto finish;
- }
+ if (asprintf(&buf, "%s/%s", root_dir, *i) < 0)
+ return -ENOMEM;
+
units_dir = buf;
} else
units_dir = *i;
@@ -1930,20 +1838,18 @@ int unit_file_get_list(
if (errno == ENOENT)
continue;
- r = -errno;
- goto finish;
+ return -errno;
}
for (;;) {
struct dirent *de;
union dirent_storage buffer;
- UnitFileList *f;
+ UnitFileList __attribute__((cleanup(unitfilelist_free)))
+ *f = NULL;
r = readdir_r(d, &buffer.de, &de);
- if (r != 0) {
- r = -r;
- goto finish;
- }
+ if (r != 0)
+ return -r;
if (!de)
break;
@@ -1962,31 +1868,24 @@ int unit_file_get_list(
if (r == -ENOENT)
continue;
- goto finish;
+ return r;
}
if (de->d_type != DT_LNK && de->d_type != DT_REG)
continue;
f = new0(UnitFileList, 1);
- if (!f) {
- r = -ENOMEM;
- goto finish;
- }
+ if (!f)
+ return -ENOMEM;
f->path = path_make_absolute(de->d_name, units_dir);
- if (!f->path) {
- free(f);
- r = -ENOMEM;
- goto finish;
- }
+ if (!f->path)
+ return -ENOMEM;
r = null_or_empty_path(f->path);
- if (r < 0 && r != -ENOENT) {
- free(f->path);
- free(f);
- goto finish;
- } else if (r > 0) {
+ if (r < 0 && r != -ENOENT)
+ return r;
+ else if (r > 0) {
f->state =
path_startswith(*i, "/run") ?
UNIT_FILE_MASKED_RUNTIME : UNIT_FILE_MASKED;
@@ -1994,11 +1893,9 @@ int unit_file_get_list(
}
r = find_symlinks_in_scope(scope, root_dir, de->d_name, &f->state);
- if (r < 0) {
- free(f->path);
- free(f);
- goto finish;
- } else if (r > 0) {
+ if (r < 0)
+ return r;
+ else if (r > 0) {
f->state = UNIT_FILE_ENABLED;
goto found;
}
@@ -2008,32 +1905,21 @@ int unit_file_get_list(
r == -EBADMSG || /* Invalid format? */
r == -ENOENT /* Included file not found? */)
f->state = UNIT_FILE_INVALID;
- else if (r < 0) {
- free(f->path);
- free(f);
- goto finish;
- } else if (r > 0)
+ else if (r < 0)
+ return r;
+ else if (r > 0)
f->state = UNIT_FILE_DISABLED;
else
f->state = UNIT_FILE_STATIC;
found:
r = hashmap_put(h, path_get_file_name(f->path), f);
- if (r < 0) {
- free(f->path);
- free(f);
- goto finish;
- }
+ if (r < 0)
+ return r;
+ f = NULL; /* prevent cleanup */
}
}
-finish:
- lookup_paths_free(&paths);
- free(buf);
-
- if (d)
- closedir(d);
-
return r;
}
diff --git a/src/shared/install.h b/src/shared/install.h
index 55249914b1..94516c9d05 100644
--- a/src/shared/install.h
+++ b/src/shared/install.h
@@ -63,6 +63,16 @@ typedef struct UnitFileList {
UnitFileState state;
} UnitFileList;
+typedef struct {
+ char *name;
+ char *path;
+ char *user;
+
+ char **aliases;
+ char **wanted_by;
+ char **required_by;
+} InstallInfo;
+
int unit_file_enable(UnitFileScope scope, bool runtime, const char *root_dir, char *files[], bool force, UnitFileChange **changes, unsigned *n_changes);
int unit_file_disable(UnitFileScope scope, bool runtime, const char *root_dir, char *files[], UnitFileChange **changes, unsigned *n_changes);
int unit_file_reenable(UnitFileScope scope, bool runtime, const char *root_dir, char *files[], bool force, UnitFileChange **changes, unsigned *n_changes);
@@ -80,8 +90,8 @@ void unit_file_changes_free(UnitFileChange *changes, unsigned n_changes);
int unit_file_query_preset(UnitFileScope scope, const char *name);
-const char *unit_file_state_to_string(UnitFileState s);
-UnitFileState unit_file_state_from_string(const char *s);
+const char *unit_file_state_to_string(UnitFileState s) _const_;
+UnitFileState unit_file_state_from_string(const char *s) _pure_;
-const char *unit_file_change_type_to_string(UnitFileChangeType s);
-UnitFileChangeType unit_file_change_type_from_string(const char *s);
+const char *unit_file_change_type_to_string(UnitFileChangeType s) _const_;
+UnitFileChangeType unit_file_change_type_from_string(const char *s) _pure_;
diff --git a/src/shared/label.c b/src/shared/label.c
index d353da57ec..1fe4574633 100644
--- a/src/shared/label.c
+++ b/src/shared/label.c
@@ -25,8 +25,12 @@
#include <malloc.h>
#include <sys/socket.h>
#include <sys/un.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
#include "label.h"
+#include "strv.h"
#include "util.h"
#include "path-util.h"
@@ -78,7 +82,7 @@ int label_init(const char *prefix) {
l = after_mallinfo.uordblks > before_mallinfo.uordblks ? after_mallinfo.uordblks - before_mallinfo.uordblks : 0;
log_debug("Successfully loaded SELinux database in %s, size on heap is %iK.",
- format_timespan(timespan, sizeof(timespan), after_timestamp - before_timestamp),
+ format_timespan(timespan, sizeof(timespan), after_timestamp - before_timestamp, 0),
(l+1023)/1024);
}
#endif
diff --git a/src/shared/label.h b/src/shared/label.h
index 1220b18965..dda4d1c024 100644
--- a/src/shared/label.h
+++ b/src/shared/label.h
@@ -45,3 +45,7 @@ int label_mkdir(const char *path, mode_t mode, bool apply);
void label_retest_selinux(void);
int label_bind(int fd, const struct sockaddr *addr, socklen_t addrlen);
+
+int label_write_one_line_file_atomic(const char *fn, const char *line);
+int label_write_env_file(const char *fname, char **l);
+int label_fopen_temporary(const char *path, FILE **_f, char **_temp_path);
diff --git a/src/shared/log.c b/src/shared/log.c
index 8d3458e731..27317f7ed3 100644
--- a/src/shared/log.c
+++ b/src/shared/log.c
@@ -129,16 +129,15 @@ static int create_log_socket(int type) {
}
static int log_open_syslog(void) {
- union sockaddr_union sa;
int r;
+ union sockaddr_union sa = {
+ .un.sun_family = AF_UNIX,
+ .un.sun_path = "/dev/log",
+ };
if (syslog_fd >= 0)
return 0;
- zero(sa);
- sa.un.sun_family = AF_UNIX;
- strncpy(sa.un.sun_path, "/dev/log", sizeof(sa.un.sun_path));
-
syslog_fd = create_log_socket(SOCK_DGRAM);
if (syslog_fd < 0) {
r = syslog_fd;
@@ -183,7 +182,10 @@ void log_close_journal(void) {
}
static int log_open_journal(void) {
- union sockaddr_union sa;
+ union sockaddr_union sa = {
+ .un.sun_family = AF_UNIX,
+ .un.sun_path = "/run/systemd/journal/socket",
+ };
int r;
if (journal_fd >= 0)
@@ -195,10 +197,6 @@ static int log_open_journal(void) {
goto fail;
}
- zero(sa);
- sa.un.sun_family = AF_UNIX;
- strncpy(sa.un.sun_path, "/run/systemd/journal/socket", sizeof(sa.un.sun_path));
-
if (connect(journal_fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path)) < 0) {
r = -errno;
goto fail;
@@ -313,7 +311,7 @@ static int write_to_console(
const char *buffer) {
char location[64];
- struct iovec iovec[5];
+ struct iovec iovec[5] = {};
unsigned n = 0;
bool highlight;
@@ -322,8 +320,6 @@ static int write_to_console(
highlight = LOG_PRI(level) <= LOG_ERR && show_color;
- zero(iovec);
-
if (show_location) {
snprintf(location, sizeof(location), "(%s:%u) ", file, line);
char_array_0(location);
@@ -353,8 +349,11 @@ static int write_to_syslog(
const char *buffer) {
char header_priority[16], header_time[64], header_pid[16];
- struct iovec iovec[5];
- struct msghdr msghdr;
+ struct iovec iovec[5] = {};
+ struct msghdr msghdr = {
+ .msg_iov = iovec,
+ .msg_iovlen = ELEMENTSOF(iovec),
+ };
time_t t;
struct tm *tm;
@@ -375,7 +374,6 @@ static int write_to_syslog(
snprintf(header_pid, sizeof(header_pid), "[%lu]: ", (unsigned long) getpid());
char_array_0(header_pid);
- zero(iovec);
IOVEC_SET_STRING(iovec[0], header_priority);
IOVEC_SET_STRING(iovec[1], header_time);
IOVEC_SET_STRING(iovec[2], program_invocation_short_name);
@@ -386,10 +384,6 @@ static int write_to_syslog(
if (syslog_is_stream)
iovec[4].iov_len++;
- zero(msghdr);
- msghdr.msg_iov = iovec;
- msghdr.msg_iovlen = ELEMENTSOF(iovec);
-
for (;;) {
ssize_t n;
@@ -417,7 +411,7 @@ static int write_to_kmsg(
const char *buffer) {
char header_priority[16], header_pid[16];
- struct iovec iovec[5];
+ struct iovec iovec[5] = {};
if (kmsg_fd < 0)
return 0;
@@ -428,7 +422,6 @@ static int write_to_kmsg(
snprintf(header_pid, sizeof(header_pid), "[%lu]: ", (unsigned long) getpid());
char_array_0(header_pid);
- zero(iovec);
IOVEC_SET_STRING(iovec[0], header_priority);
IOVEC_SET_STRING(iovec[1], program_invocation_short_name);
IOVEC_SET_STRING(iovec[2], header_pid);
@@ -441,6 +434,37 @@ static int write_to_kmsg(
return 1;
}
+static int log_do_header(char *header, size_t size,
+ int level,
+ const char *file, int line, const char *func,
+ const char *object_name, const char *object) {
+ snprintf(header, size,
+ "PRIORITY=%i\n"
+ "SYSLOG_FACILITY=%i\n"
+ "%s%.*s%s"
+ "%s%.*i%s"
+ "%s%.*s%s"
+ "%s%.*s%s"
+ "SYSLOG_IDENTIFIER=%s\n",
+ LOG_PRI(level),
+ LOG_FAC(level),
+ file ? "CODE_FILE=" : "",
+ file ? LINE_MAX : 0, file, /* %.0s means no output */
+ file ? "\n" : "",
+ line ? "CODE_LINE=" : "",
+ line ? 1 : 0, line, /* %.0d means no output too, special case for 0 */
+ line ? "\n" : "",
+ func ? "CODE_FUNCTION=" : "",
+ func ? LINE_MAX : 0, func,
+ func ? "\n" : "",
+ object ? object_name : "",
+ object ? LINE_MAX : 0, object, /* %.0s means no output */
+ object ? "\n" : "",
+ program_invocation_short_name);
+ header[size - 1] = '\0';
+ return 0;
+}
+
static int write_to_journal(
int level,
const char*file,
@@ -451,39 +475,20 @@ static int write_to_journal(
const char *buffer) {
char header[LINE_MAX];
- struct iovec iovec[3];
- struct msghdr mh;
+ struct iovec iovec[4] = {};
+ struct msghdr mh = {};
if (journal_fd < 0)
return 0;
- snprintf(header, sizeof(header),
- "PRIORITY=%i\n"
- "SYSLOG_FACILITY=%i\n"
- "CODE_FILE=%s\n"
- "CODE_LINE=%i\n"
- "CODE_FUNCTION=%s\n"
- "%s%.*s%s"
- "SYSLOG_IDENTIFIER=%s\n"
- "MESSAGE=",
- LOG_PRI(level),
- LOG_FAC(level),
- file,
- line,
- func,
- object ? object_name : "",
- object ? LINE_MAX : 0, object, /* %.0s means no output */
- object ? "\n" : "",
- program_invocation_short_name);
+ log_do_header(header, sizeof(header), level,
+ file, line, func, object_name, object);
- char_array_0(header);
-
- zero(iovec);
IOVEC_SET_STRING(iovec[0], header);
- IOVEC_SET_STRING(iovec[1], buffer);
- IOVEC_SET_STRING(iovec[2], "\n");
+ IOVEC_SET_STRING(iovec[1], "MESSAGE=");
+ IOVEC_SET_STRING(iovec[2], buffer);
+ IOVEC_SET_STRING(iovec[3], "\n");
- zero(mh);
mh.msg_iov = iovec;
mh.msg_iovlen = ELEMENTSOF(iovec);
@@ -586,18 +591,14 @@ int log_dump_internal(
const char *func,
char *buffer) {
- int saved_errno, r;
+ PROTECT_ERRNO;
/* This modifies the buffer... */
if (_likely_(LOG_PRI(level) > log_max_level))
return 0;
- saved_errno = errno;
- r = log_dispatch(level, file, line, func, NULL, NULL, buffer);
- errno = saved_errno;
-
- return r;
+ return log_dispatch(level, file, line, func, NULL, NULL, buffer);
}
int log_metav(
@@ -608,20 +609,16 @@ int log_metav(
const char *format,
va_list ap) {
+ PROTECT_ERRNO;
char buffer[LINE_MAX];
- int saved_errno, r;
if (_likely_(LOG_PRI(level) > log_max_level))
return 0;
- saved_errno = errno;
vsnprintf(buffer, sizeof(buffer), format, ap);
char_array_0(buffer);
- r = log_dispatch(level, file, line, func, NULL, NULL, buffer);
- errno = saved_errno;
-
- return r;
+ return log_dispatch(level, file, line, func, NULL, NULL, buffer);
}
int log_meta(
@@ -651,21 +648,17 @@ int log_metav_object(
const char *format,
va_list ap) {
+ PROTECT_ERRNO;
char buffer[LINE_MAX];
- int saved_errno, r;
if (_likely_(LOG_PRI(level) > log_max_level))
return 0;
- saved_errno = errno;
vsnprintf(buffer, sizeof(buffer), format, ap);
char_array_0(buffer);
- r = log_dispatch(level, file, line, func,
- object_name, object, buffer);
- errno = saved_errno;
-
- return r;
+ return log_dispatch(level, file, line, func,
+ object_name, object, buffer);
}
int log_meta_object(
@@ -723,7 +716,7 @@ int log_struct_internal(
const char *func,
const char *format, ...) {
- int saved_errno;
+ PROTECT_ERRNO;
va_list ap;
int r;
@@ -736,37 +729,22 @@ int log_struct_internal(
if ((level & LOG_FACMASK) == 0)
level = log_facility | LOG_PRI(level);
- saved_errno = errno;
-
if ((log_target == LOG_TARGET_AUTO ||
log_target == LOG_TARGET_JOURNAL_OR_KMSG ||
log_target == LOG_TARGET_JOURNAL) &&
journal_fd >= 0) {
char header[LINE_MAX];
- struct iovec iovec[17];
+ struct iovec iovec[17] = {};
unsigned n = 0, i;
- struct msghdr mh;
- const char nl = '\n';
+ struct msghdr mh = {
+ .msg_iov = iovec,
+ };
+ static const char nl = '\n';
/* If the journal is available do structured logging */
-
- snprintf(header, sizeof(header),
- "PRIORITY=%i\n"
- "SYSLOG_FACILITY=%i\n"
- "CODE_FILE=%s\n"
- "CODE_LINE=%i\n"
- "CODE_FUNCTION=%s\n"
- "SYSLOG_IDENTIFIER=%s\n",
- LOG_PRI(level),
- LOG_FAC(level),
- file,
- line,
- func,
- program_invocation_short_name);
- char_array_0(header);
-
- zero(iovec);
+ log_do_header(header, sizeof(header), level,
+ file, line, func, NULL, NULL);
IOVEC_SET_STRING(iovec[n++], header);
va_start(ap, format);
@@ -799,8 +777,6 @@ int log_struct_internal(
format = va_arg(ap, char *);
}
- zero(mh);
- mh.msg_iov = iovec;
mh.msg_iovlen = n;
if (sendmsg(journal_fd, &mh, MSG_NOSIGNAL) < 0)
@@ -846,7 +822,6 @@ int log_struct_internal(
r = -EINVAL;
}
- errno = saved_errno;
return r;
}
diff --git a/src/shared/log.h b/src/shared/log.h
index 9aafcb4100..979f833d57 100644
--- a/src/shared/log.h
+++ b/src/shared/log.h
@@ -56,8 +56,8 @@ void log_show_location(bool b);
int log_show_color_from_string(const char *e);
int log_show_location_from_string(const char *e);
-LogTarget log_get_target(void);
-int log_get_max_level(void);
+LogTarget log_get_target(void) _pure_;
+int log_get_max_level(void) _pure_;
int log_open(void);
void log_close(void);
@@ -83,7 +83,7 @@ int log_metav(
int line,
const char *func,
const char *format,
- va_list ap);
+ va_list ap) _printf_attr_(5,0);
int log_meta_object(
int level,
@@ -102,14 +102,14 @@ int log_metav_object(
const char *object_name,
const char *object,
const char *format,
- va_list ap);
+ va_list ap) _printf_attr_(7,0);
int log_struct_internal(
int level,
const char *file,
int line,
const char *func,
- const char *format, ...) _sentinel_;
+ const char *format, ...) _printf_attr_(5,0) _sentinel_;
int log_oom_internal(
const char *file,
@@ -151,9 +151,9 @@ _noreturn_ void log_assert_failed_unreachable(
/* This modifies the buffer passed! */
#define log_dump(level, buffer) log_dump_internal(level, __FILE__, __LINE__, __func__, buffer)
-bool log_on_console(void);
+bool log_on_console(void) _pure_;
-const char *log_target_to_string(LogTarget target);
-LogTarget log_target_from_string(const char *s);
+const char *log_target_to_string(LogTarget target) _const_;
+LogTarget log_target_from_string(const char *s) _pure_;
#define MESSAGE_ID(x) "MESSAGE_ID=" SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(x)
diff --git a/src/shared/logs-show.c b/src/shared/logs-show.c
index 04450a5504..116dc8a36c 100644
--- a/src/shared/logs-show.c
+++ b/src/shared/logs-show.c
@@ -30,6 +30,7 @@
#include "util.h"
#include "utf8.h"
#include "hashmap.h"
+#include "journal-internal.h"
#define PRINT_THRESHOLD 128
#define JSON_THRESHOLD 4096
@@ -277,7 +278,7 @@ static int output_short(
} else if ((flags & OUTPUT_FULL_WIDTH) || (message_len + n + 1 < n_columns))
fprintf(f, ": %s%.*s%s\n", color_on, (int) message_len, message, color_off);
else if (n < n_columns && n_columns - n - 2 >= 3) {
- char *e;
+ _cleanup_free_ char *e;
e = ellipsize_mem(message, message_len, n_columns - n - 2, 90);
@@ -285,8 +286,6 @@ static int output_short(
fprintf(f, ": %s%.*s%s\n", color_on, (int) message_len, message, color_off);
else
fprintf(f, ": %s%s%s\n", color_on, e, color_off);
-
- free(e);
} else
fputs("\n", f);
@@ -305,7 +304,7 @@ static int output_verbose(
const void *data;
size_t length;
- char *cursor;
+ _cleanup_free_ char *cursor = NULL;
uint64_t realtime;
char ts[FORMAT_TIMESTAMP_MAX];
int r;
@@ -331,8 +330,6 @@ static int output_verbose(
format_timestamp(ts, sizeof(ts), realtime),
cursor);
- free(cursor);
-
SD_JOURNAL_FOREACH_DATA(j, data, length) {
if (!shall_print(data, length, flags)) {
const char *c;
@@ -369,7 +366,7 @@ static int output_export(
char sid[33];
int r;
usec_t realtime, monotonic;
- char *cursor;
+ _cleanup_free_ char *cursor = NULL;
const void *data;
size_t length;
@@ -405,8 +402,6 @@ static int output_export(
(unsigned long long) monotonic,
sd_id128_to_string(boot_id, sid));
- free(cursor);
-
SD_JOURNAL_FOREACH_DATA(j, data, length) {
/* We already printed the boot id, from the data in
@@ -500,11 +495,11 @@ static int output_json(
OutputFlags flags) {
uint64_t realtime, monotonic;
- char *cursor, *k;
+ _cleanup_free_ char *cursor = NULL;
const void *data;
size_t length;
sd_id128_t boot_id;
- char sid[33];
+ char sid[33], *k;
int r;
Hashmap *h = NULL;
bool done, separator;
@@ -556,7 +551,6 @@ static int output_json(
(unsigned long long) monotonic,
sd_id128_to_string(boot_id, sid));
}
- free(cursor);
h = hashmap_new(string_hash_func, string_compare_func);
if (!h)
@@ -781,72 +775,22 @@ int output_journal(
return ret;
}
-int show_journal_by_unit(
- FILE *f,
- const char *unit,
- OutputMode mode,
- unsigned n_columns,
- usec_t not_before,
- unsigned how_many,
- OutputFlags flags) {
+static int show_journal(FILE *f,
+ sd_journal *j,
+ OutputMode mode,
+ unsigned n_columns,
+ usec_t not_before,
+ unsigned how_many,
+ OutputFlags flags) {
- _cleanup_free_ char *m1 = NULL, *m2 = NULL, *m3 = NULL;
- sd_journal *j = NULL;
int r;
unsigned line = 0;
bool need_seek = false;
int warn_cutoff = flags & OUTPUT_WARN_CUTOFF;
+ assert(j);
assert(mode >= 0);
assert(mode < _OUTPUT_MODE_MAX);
- assert(unit);
-
- if (!endswith(unit, ".service") &&
- !endswith(unit, ".socket") &&
- !endswith(unit, ".mount") &&
- !endswith(unit, ".swap"))
- return 0;
-
- if (how_many <= 0)
- return 0;
-
- if (asprintf(&m1, "_SYSTEMD_UNIT=%s", unit) < 0 ||
- asprintf(&m2, "COREDUMP_UNIT=%s", unit) < 0 ||
- asprintf(&m3, "UNIT=%s", unit) < 0) {
- r = -ENOMEM;
- goto finish;
- }
-
- r = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY|SD_JOURNAL_SYSTEM_ONLY);
- if (r < 0)
- goto finish;
-
- /* Look for messages from the service itself */
- r = sd_journal_add_match(j, m1, 0);
- if (r < 0)
- goto finish;
-
- /* Look for coredumps of the service */
- r = sd_journal_add_disjunction(j);
- if (r < 0)
- goto finish;
- r = sd_journal_add_match(j, "MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1", 0);
- if (r < 0)
- goto finish;
- r = sd_journal_add_match(j, m2, 0);
- if (r < 0)
- goto finish;
-
- /* Look for messages from PID 1 about this service */
- r = sd_journal_add_disjunction(j);
- if (r < 0)
- goto finish;
- r = sd_journal_add_match(j, "_PID=1", 0);
- if (r < 0)
- goto finish;
- r = sd_journal_add_match(j, m3, 0);
- if (r < 0)
- goto finish;
/* Seek to end */
r = sd_journal_seek_tail(j);
@@ -923,12 +867,110 @@ int show_journal_by_unit(
}
finish:
- if (j)
- sd_journal_close(j);
+ return r;
+}
+int add_matches_for_unit(sd_journal *j, const char *unit) {
+ int r;
+ _cleanup_free_ char *m1 = NULL, *m2 = NULL, *m3 = NULL;
+
+ assert(j);
+ assert(unit);
+
+ if (asprintf(&m1, "_SYSTEMD_UNIT=%s", unit) < 0 ||
+ asprintf(&m2, "COREDUMP_UNIT=%s", unit) < 0 ||
+ asprintf(&m3, "UNIT=%s", unit) < 0)
+ return -ENOMEM;
+
+ (void)(
+ /* Look for messages from the service itself */
+ (r = sd_journal_add_match(j, m1, 0)) ||
+
+ /* Look for coredumps of the service */
+ (r = sd_journal_add_disjunction(j)) ||
+ (r = sd_journal_add_match(j,
+ "MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1", 0)) ||
+ (r = sd_journal_add_match(j, m2, 0)) ||
+
+ /* Look for messages from PID 1 about this service */
+ (r = sd_journal_add_disjunction(j)) ||
+ (r = sd_journal_add_match(j, "_PID=1", 0)) ||
+ (r = sd_journal_add_match(j, m3, 0))
+ );
+ return r;
+}
+
+int add_matches_for_user_unit(sd_journal *j, const char *unit, uid_t uid) {
+ int r;
+ _cleanup_free_ char *m1 = NULL, *m2 = NULL, *m3 = NULL, *m4 = NULL;
+
+ assert(j);
+ assert(unit);
+
+ if (asprintf(&m1, "_SYSTEMD_USER_UNIT=%s", unit) < 0 ||
+ asprintf(&m2, "USER_UNIT=%s", unit) < 0 ||
+ asprintf(&m3, "COREDUMP_USER_UNIT=%s", unit) < 0 ||
+ asprintf(&m4, "_UID=%d", uid) < 0)
+ return -ENOMEM;
+
+ (void) (
+ /* Look for messages from the user service itself */
+ (r = sd_journal_add_match(j, m1, 0)) ||
+ (r = sd_journal_add_match(j, m4, 0)) ||
+
+ /* Look for messages from systemd about this service */
+ (r = sd_journal_add_disjunction(j)) ||
+ (r = sd_journal_add_match(j, m2, 0)) ||
+ (r = sd_journal_add_match(j, m4, 0)) ||
+
+ /* Look for coredumps of the service */
+ (r = sd_journal_add_disjunction(j)) ||
+ (r = sd_journal_add_match(j, m3, 0)) ||
+ (r = sd_journal_add_match(j, m4, 0))
+ );
return r;
}
+int show_journal_by_unit(
+ FILE *f,
+ const char *unit,
+ OutputMode mode,
+ unsigned n_columns,
+ usec_t not_before,
+ unsigned how_many,
+ uid_t uid,
+ OutputFlags flags,
+ bool system) {
+
+ _cleanup_journal_close_ sd_journal*j = NULL;
+ int r;
+ int jflags = SD_JOURNAL_LOCAL_ONLY | system * SD_JOURNAL_SYSTEM_ONLY;
+
+ assert(mode >= 0);
+ assert(mode < _OUTPUT_MODE_MAX);
+ assert(unit);
+
+ if (how_many <= 0)
+ return 0;
+
+ r = sd_journal_open(&j, jflags);
+ if (r < 0)
+ return r;
+
+ if (system)
+ r = add_matches_for_unit(j, unit);
+ else
+ r = add_matches_for_user_unit(j, unit, uid);
+ if (r < 0)
+ return r;
+
+ r = show_journal(f, j, mode, n_columns, not_before, how_many, flags);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
static const char *const output_mode_table[_OUTPUT_MODE_MAX] = {
[OUTPUT_SHORT] = "short",
[OUTPUT_SHORT_MONOTONIC] = "short-monotonic",
diff --git a/src/shared/logs-show.h b/src/shared/logs-show.h
index 11cb41aab3..b0f93a661a 100644
--- a/src/shared/logs-show.h
+++ b/src/shared/logs-show.h
@@ -22,32 +22,13 @@
***/
#include <stdbool.h>
+#include <unistd.h>
+#include <sys/types.h>
#include <systemd/sd-journal.h>
#include "util.h"
-
-typedef enum OutputMode {
- OUTPUT_SHORT,
- OUTPUT_SHORT_MONOTONIC,
- OUTPUT_VERBOSE,
- OUTPUT_EXPORT,
- OUTPUT_JSON,
- OUTPUT_JSON_PRETTY,
- OUTPUT_JSON_SSE,
- OUTPUT_CAT,
- _OUTPUT_MODE_MAX,
- _OUTPUT_MODE_INVALID = -1
-} OutputMode;
-
-typedef enum OutputFlags {
- OUTPUT_SHOW_ALL = 1 << 0,
- OUTPUT_FOLLOW = 1 << 1,
- OUTPUT_WARN_CUTOFF = 1 << 2,
- OUTPUT_FULL_WIDTH = 1 << 3,
- OUTPUT_COLOR = 1 << 4,
- OUTPUT_CATALOG = 1 << 5
-} OutputFlags;
+#include "output-mode.h"
int output_journal(
FILE *f,
@@ -56,6 +37,15 @@ int output_journal(
unsigned n_columns,
OutputFlags flags);
+int add_matches_for_unit(
+ sd_journal *j,
+ const char *unit);
+
+int add_matches_for_user_unit(
+ sd_journal *j,
+ const char *unit,
+ uid_t uid);
+
int show_journal_by_unit(
FILE *f,
const char *unit,
@@ -63,7 +53,9 @@ int show_journal_by_unit(
unsigned n_columns,
usec_t not_before,
unsigned how_many,
- OutputFlags flags);
+ uid_t uid,
+ OutputFlags flags,
+ bool system);
void json_escape(
FILE *f,
@@ -71,5 +63,5 @@ void json_escape(
size_t l,
OutputFlags flags);
-const char* output_mode_to_string(OutputMode m);
-OutputMode output_mode_from_string(const char *s);
+const char* output_mode_to_string(OutputMode m) _const_;
+OutputMode output_mode_from_string(const char *s) _pure_;
diff --git a/src/shared/macro.h b/src/shared/macro.h
index 29d91392f8..0874102ece 100644
--- a/src/shared/macro.h
+++ b/src/shared/macro.h
@@ -28,6 +28,7 @@
#include <inttypes.h>
#define _printf_attr_(a,b) __attribute__ ((format (printf, a, b)))
+#define _alloc_(...) __attribute__ ((alloc_size(__VA_ARGS__)))
#define _sentinel_ __attribute__ ((sentinel))
#define _noreturn_ __attribute__((noreturn))
#define _unused_ __attribute__ ((unused))
@@ -45,16 +46,37 @@
#define _weakref_(x) __attribute__((weakref(#x)))
#define _introspect_(x) __attribute__((section("introspect." x)))
#define _alignas_(x) __attribute__((aligned(__alignof(x))))
+#define _cleanup_(x) __attribute__((cleanup(x)))
+
+/* automake test harness */
+#define EXIT_TEST_SKIP 77
#define XSTRINGIFY(x) #x
#define STRINGIFY(x) XSTRINGIFY(x)
/* Rounds up */
-#define ALIGN(l) ALIGN_TO((l), sizeof(void*))
+
+#define ALIGN4(l) (((l) + 3) & ~3)
+#define ALIGN8(l) (((l) + 7) & ~7)
+
+#if __SIZEOF_POINTER__ == 8
+#define ALIGN(l) ALIGN8(l)
+#elif __SIZEOF_POINTER__ == 4
+#define ALIGN(l) ALIGN4(l)
+#else
+#error "Wut? Pointers are neither 4 nor 8 bytes long?"
+#endif
+
+#define ALIGN_PTR(p) ((void*) ALIGN((unsigned long) p))
+#define ALIGN4_PTR(p) ((void*) ALIGN4((unsigned long) p))
+#define ALIGN8_PTR(p) ((void*) ALIGN8((unsigned long) p))
+
static inline size_t ALIGN_TO(size_t l, size_t ali) {
return ((l + ali - 1) & ~(ali - 1));
}
+#define ALIGN_TO_PTR(p, ali) ((void*) ALIGN_TO((unsigned long) p))
+
#define ELEMENTSOF(x) (sizeof(x)/sizeof((x)[0]))
/*
@@ -64,34 +86,35 @@ static inline size_t ALIGN_TO(size_t l, size_t ali) {
* @member: the name of the member within the struct.
*
*/
-#define container_of(ptr, type, member) ({ \
- const typeof( ((type *)0)->member ) *__mptr = (ptr); \
- (type *)( (char *)__mptr - offsetof(type,member) );})
+#define container_of(ptr, type, member) \
+ __extension__ ({ \
+ const typeof( ((type *)0)->member ) *__mptr = (ptr); \
+ (type *)( (char *)__mptr - offsetof(type,member) ); \
+ })
-#ifndef MAX
-#define MAX(a,b) \
- __extension__ ({ \
- typeof(a) _a = (a); \
- typeof(b) _b = (b); \
- _a > _b ? _a : _b; \
+#undef MAX
+#define MAX(a,b) \
+ __extension__ ({ \
+ typeof(a) _a = (a); \
+ typeof(b) _b = (b); \
+ _a > _b ? _a : _b; \
})
-#endif
-#define MAX3(a,b,c) \
- MAX(MAX(a,b),c)
+#define MAX3(x,y,z) \
+ __extension__ ({ \
+ typeof(x) _c = MAX(x,y); \
+ MAX(_c, z); \
+ })
-#ifndef MIN
+#undef MIN
#define MIN(a,b) \
__extension__ ({ \
typeof(a) _a = (a); \
typeof(b) _b = (b); \
_a < _b ? _a : _b; \
})
-#endif
-
-#define MIN3(a,b,c) \
- MIN(MIN(a,b),c)
+#ifndef CLAMP
#define CLAMP(x, low, high) \
__extension__ ({ \
typeof(x) _x = (x); \
@@ -99,6 +122,7 @@ static inline size_t ALIGN_TO(size_t l, size_t ali) {
typeof(high) _high = (high); \
((_x > _high) ? _high : ((_x < _low) ? _low : _x)); \
})
+#endif
#define assert_se(expr) \
do { \
@@ -156,6 +180,8 @@ static inline size_t ALIGN_TO(size_t l, size_t ali) {
#define memzero(x,l) (memset((x), 0, (l)))
#define zero(x) (memzero(&(x), sizeof(x)))
+#define CHAR_TO_STR(x) ((char[2]) { x, 0 })
+
#define char_array_0(x) x[sizeof(x)-1] = 0;
#define IOVEC_SET_STRING(i, s) \
@@ -194,14 +220,6 @@ static inline size_t IOVEC_INCREMENT(struct iovec *i, unsigned n, size_t k) {
return k;
}
-#define _cleanup_free_ __attribute__((cleanup(freep)))
-#define _cleanup_fclose_ __attribute__((cleanup(fclosep)))
-#define _cleanup_close_ __attribute__((cleanup(closep)))
-#define _cleanup_closedir_ __attribute__((cleanup(closedirp)))
-#define _cleanup_umask_ __attribute__((cleanup(umaskp)))
-#define _cleanup_set_free_ __attribute__((cleanup(set_freep)))
-#define _cleanup_strv_free_ __attribute__((cleanup(strv_freep)))
-
#define VA_FORMAT_ADVANCE(format, ap) \
do { \
int _argtypes[128]; \
@@ -247,4 +265,21 @@ do { \
} \
} while(false)
+ /* Because statfs.t_type can be int on some architecures, we have to cast
+ * the const magic to the type, otherwise the compiler warns about
+ * signed/unsigned comparison, because the magic can be 32 bit unsigned.
+ */
+#define F_TYPE_CMP(a, b) (a == (typeof(a)) b)
+
+
+/* Returns the number of chars needed to format variables of the
+ * specified type as a decimal string. Adds in extra space for a
+ * negative '-' prefix. */
+
+#define DECIMAL_STR_MAX(type) \
+ (1+(sizeof(type) <= 1 ? 3 : \
+ sizeof(type) <= 2 ? 5 : \
+ sizeof(type) <= 4 ? 10 : \
+ sizeof(type) <= 8 ? 20 : sizeof(int[-2*(sizeof(type) > 8)])))
+
#include "log.h"
diff --git a/src/shared/missing.h b/src/shared/missing.h
index c53579ff7f..d4ba0d3dcf 100644
--- a/src/shared/missing.h
+++ b/src/shared/missing.h
@@ -196,6 +196,14 @@ static inline pid_t gettid(void) {
#define MS_STRICTATIME (1<<24)
#endif
+#ifndef MS_REC
+#define MS_REC 16384
+#endif
+
+#ifndef MS_SHARED
+#define MS_SHARED (1<<20)
+#endif
+
#ifndef PR_SET_NO_NEW_PRIVS
#define PR_SET_NO_NEW_PRIVS 38
#endif
diff --git a/src/shared/output-mode.h b/src/shared/output-mode.h
new file mode 100644
index 0000000000..0efd430c5d
--- /dev/null
+++ b/src/shared/output-mode.h
@@ -0,0 +1,44 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ 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 <http://www.gnu.org/licenses/>.
+***/
+
+typedef enum OutputMode {
+ OUTPUT_SHORT,
+ OUTPUT_SHORT_MONOTONIC,
+ OUTPUT_VERBOSE,
+ OUTPUT_EXPORT,
+ OUTPUT_JSON,
+ OUTPUT_JSON_PRETTY,
+ OUTPUT_JSON_SSE,
+ OUTPUT_CAT,
+ _OUTPUT_MODE_MAX,
+ _OUTPUT_MODE_INVALID = -1
+} OutputMode;
+
+typedef enum OutputFlags {
+ OUTPUT_SHOW_ALL = 1 << 0,
+ OUTPUT_FOLLOW = 1 << 1,
+ OUTPUT_WARN_CUTOFF = 1 << 2,
+ OUTPUT_FULL_WIDTH = 1 << 3,
+ OUTPUT_COLOR = 1 << 4,
+ OUTPUT_CATALOG = 1 << 5
+} OutputFlags;
diff --git a/src/shared/pager.c b/src/shared/pager.c
index 488a12c763..8dddf24f4c 100644
--- a/src/shared/pager.c
+++ b/src/shared/pager.c
@@ -44,7 +44,7 @@ _noreturn_ static void pager_fallback(void) {
_exit(EXIT_SUCCESS);
}
-int pager_open(void) {
+int pager_open(bool jump_to_end) {
int fd[2];
const char *pager;
pid_t parent_pid;
@@ -85,7 +85,10 @@ int pager_open(void) {
dup2(fd[0], STDIN_FILENO);
close_pipe(fd);
- setenv("LESS", "FRSX", 0);
+ if (jump_to_end)
+ setenv("LESS", "FRSXMK+G", 1);
+ else
+ setenv("LESS", "FRSXMK", 1);
/* Make sure the pager goes away when the parent dies */
if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0)
diff --git a/src/shared/pager.h b/src/shared/pager.h
index 5e7b5ab91e..03dca8bc03 100644
--- a/src/shared/pager.h
+++ b/src/shared/pager.h
@@ -23,6 +23,8 @@
#include <stdbool.h>
-int pager_open(void);
+#include "macro.h"
+
+int pager_open(bool jump_to_end);
void pager_close(void);
-bool pager_have(void);
+bool pager_have(void) _pure_;
diff --git a/src/shared/path-lookup.c b/src/shared/path-lookup.c
index 6e5529e0c7..1a47ea9ce7 100644
--- a/src/shared/path-lookup.c
+++ b/src/shared/path-lookup.c
@@ -41,21 +41,26 @@ DEFINE_STRING_TABLE_LOOKUP(systemd_running_as, SystemdRunningAs);
int user_config_home(char **config_home) {
const char *e;
+ char *r;
e = getenv("XDG_CONFIG_HOME");
if (e) {
- if (asprintf(config_home, "%s/systemd/user", e) < 0)
+ r = strappend(e, "/systemd/user");
+ if (!r)
return -ENOMEM;
+ *config_home = r;
return 1;
} else {
const char *home;
home = getenv("HOME");
if (home) {
- if (asprintf(config_home, "%s/.config/systemd/user", home) < 0)
+ r = strappend(home, "/.config/systemd/user");
+ if (!r)
return -ENOMEM;
+ *config_home = r;
return 1;
}
}
@@ -239,7 +244,6 @@ int lookup_paths_init(
const char *generator_late) {
const char *e;
- char *t;
assert(p);
@@ -316,15 +320,12 @@ int lookup_paths_init(
return -ENOMEM;
strv_uniq(p->unit_path);
- path_strv_remove_empty(p->unit_path);
if (!strv_isempty(p->unit_path)) {
-
- t = strv_join(p->unit_path, "\n\t");
+ _cleanup_free_ char *t = strv_join(p->unit_path, "\n\t");
if (!t)
return -ENOMEM;
- log_debug("Looking for unit files in:\n\t%s", t);
- free(t);
+ log_debug("Looking for unit files in (higher priority first):\n\t%s", t);
} else {
log_debug("Ignoring unit files.");
strv_free(p->unit_path);
@@ -379,16 +380,12 @@ int lookup_paths_init(
strv_uniq(p->sysvinit_path);
strv_uniq(p->sysvrcnd_path);
- path_strv_remove_empty(p->sysvinit_path);
- path_strv_remove_empty(p->sysvrcnd_path);
if (!strv_isempty(p->sysvinit_path)) {
-
- t = strv_join(p->sysvinit_path, "\n\t");
+ _cleanup_free_ char *t = strv_join(p->sysvinit_path, "\n\t");
if (!t)
return -ENOMEM;
log_debug("Looking for SysV init scripts in:\n\t%s", t);
- free(t);
} else {
log_debug("Ignoring SysV init scripts.");
strv_free(p->sysvinit_path);
@@ -396,20 +393,19 @@ int lookup_paths_init(
}
if (!strv_isempty(p->sysvrcnd_path)) {
-
- t = strv_join(p->sysvrcnd_path, "\n\t");
+ _cleanup_free_ char *t =
+ strv_join(p->sysvrcnd_path, "\n\t");
if (!t)
return -ENOMEM;
log_debug("Looking for SysV rcN.d links in:\n\t%s", t);
- free(t);
} else {
log_debug("Ignoring SysV rcN.d links.");
strv_free(p->sysvrcnd_path);
p->sysvrcnd_path = NULL;
}
#else
- log_debug("Disabled SysV init scripts and rcN.d links support");
+ log_debug("SysV init scripts and rcN.d links support disabled");
#endif
}
diff --git a/src/shared/path-lookup.h b/src/shared/path-lookup.h
index baef62228a..9dee85f967 100644
--- a/src/shared/path-lookup.h
+++ b/src/shared/path-lookup.h
@@ -36,8 +36,8 @@ typedef enum SystemdRunningAs {
_SYSTEMD_RUNNING_AS_INVALID = -1
} SystemdRunningAs;
-const char* systemd_running_as_to_string(SystemdRunningAs i);
-SystemdRunningAs systemd_running_as_from_string(const char *s);
+const char* systemd_running_as_to_string(SystemdRunningAs i) _const_;
+SystemdRunningAs systemd_running_as_from_string(const char *s) _pure_;
int user_config_home(char **config_home);
diff --git a/src/shared/path-util.c b/src/shared/path-util.c
index dd12d3d634..0c1b6a0ab0 100644
--- a/src/shared/path-util.c
+++ b/src/shared/path-util.c
@@ -50,7 +50,8 @@ char *path_get_file_name(const char *p) {
assert(p);
- if ((r = strrchr(p, '/')))
+ r = strrchr(p, '/');
+ if (r)
return r + 1;
return (char*) p;
@@ -135,7 +136,8 @@ char *path_make_absolute_cwd(const char *p) {
if (path_is_absolute(p))
return strdup(p);
- if (!(cwd = get_current_dir_name()))
+ cwd = get_current_dir_name();
+ if (!cwd)
return NULL;
r = path_make_absolute(p, cwd);
@@ -190,14 +192,18 @@ char **path_strv_canonicalize(char **l) {
errno = 0;
u = canonicalize_file_name(t);
- free(t);
-
if (!u) {
- if (errno == ENOMEM || !errno)
- enomem = true;
-
- continue;
- }
+ if (errno == ENOENT)
+ u = t;
+ else {
+ free(t);
+ if (errno == ENOMEM || !errno)
+ enomem = true;
+
+ continue;
+ }
+ } else
+ free(t);
l[k++] = u;
}
@@ -210,24 +216,14 @@ char **path_strv_canonicalize(char **l) {
return l;
}
-char **path_strv_remove_empty(char **l) {
- char **f, **t;
+char **path_strv_canonicalize_uniq(char **l) {
+ if (strv_isempty(l))
+ return l;
- if (!l)
+ if (!path_strv_canonicalize(l))
return NULL;
- for (f = t = l; *f; f++) {
-
- if (dir_is_empty(*f) > 0) {
- free(*f);
- continue;
- }
-
- *(t++) = *f;
- }
-
- *t = NULL;
- return l;
+ return strv_uniq(l);
}
char *path_kill_slashes(char *path) {
@@ -418,3 +414,15 @@ int path_is_read_only_fs(const char *path) {
return !!(st.f_flag & ST_RDONLY);
}
+
+int path_is_os_tree(const char *path) {
+ char *p;
+ int r;
+
+ /* We use /etc/os-release as flag file if something is an OS */
+
+ p = strappenda(path, "/etc/os-release");
+ r = access(p, F_OK);
+
+ return r < 0 ? 0 : 1;
+}
diff --git a/src/shared/path-util.h b/src/shared/path-util.h
index e81821a28f..d187743769 100644
--- a/src/shared/path-util.h
+++ b/src/shared/path-util.h
@@ -1,7 +1,6 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-#ifndef foopathutilhfoo
-#define foopathutilhfoo
+#pragma once
/***
This file is part of systemd.
@@ -22,24 +21,25 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "stdbool.h"
+#include <stdbool.h>
-bool is_path(const char *p);
-char **path_split_and_make_absolute(const char *p);
-char *path_get_file_name(const char *p);
+#include "macro.h"
+
+bool is_path(const char *p) _pure_;
+char** path_split_and_make_absolute(const char *p);
+char* path_get_file_name(const char *p) _pure_;
int path_get_parent(const char *path, char **parent);
-bool path_is_absolute(const char *p);
-char *path_make_absolute(const char *p, const char *prefix);
-char *path_make_absolute_cwd(const char *p);
-char *path_kill_slashes(char *path);
-char *path_startswith(const char *path, const char *prefix);
-bool path_equal(const char *a, const char *b);
+bool path_is_absolute(const char *p) _pure_;
+char* path_make_absolute(const char *p, const char *prefix);
+char* path_make_absolute_cwd(const char *p);
+char* path_kill_slashes(char *path);
+char* path_startswith(const char *path, const char *prefix) _pure_;
+bool path_equal(const char *a, const char *b) _pure_;
-char **path_strv_make_absolute_cwd(char **l);
-char **path_strv_canonicalize(char **l);
-char **path_strv_remove_empty(char **l);
+char** path_strv_make_absolute_cwd(char **l);
+char** path_strv_canonicalize(char **l);
+char** path_strv_canonicalize_uniq(char **l);
int path_is_mount_point(const char *path, bool allow_symlink);
int path_is_read_only_fs(const char *path);
-
-#endif
+int path_is_os_tree(const char *path);
diff --git a/src/shared/polkit.c b/src/shared/polkit.c
index 826944585c..cea7074ad3 100644
--- a/src/shared/polkit.c
+++ b/src/shared/polkit.c
@@ -35,9 +35,10 @@ int verify_polkit(
bool *_challenge,
DBusError *error) {
+
+#ifdef ENABLE_POLKIT
DBusMessage *m = NULL, *reply = NULL;
const char *unix_process = "unix-process", *pid = "pid", *starttime = "start-time", *cancel_id = "";
- const char *sender;
uint32_t flags = interactive ? 1 : 0;
pid_t pid_raw;
uint32_t pid_u32;
@@ -46,6 +47,8 @@ int verify_polkit(
DBusMessageIter iter_msg, iter_struct, iter_array, iter_dict, iter_variant;
int r;
dbus_bool_t authorized = FALSE, challenge = FALSE;
+#endif
+ const char *sender;
unsigned long ul;
assert(c);
@@ -63,6 +66,8 @@ int verify_polkit(
if (ul == 0)
return 1;
+#ifdef ENABLE_POLKIT
+
pid_raw = bus_get_unix_process_id(c, sender, error);
if (pid_raw == 0)
return -EINVAL;
@@ -163,4 +168,7 @@ finish:
dbus_message_unref(reply);
return r;
+#else
+ return -EPERM;
+#endif
}
diff --git a/src/shared/prioq.c b/src/shared/prioq.c
new file mode 100644
index 0000000000..2d166360aa
--- /dev/null
+++ b/src/shared/prioq.c
@@ -0,0 +1,305 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ 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 <http://www.gnu.org/licenses/>.
+***/
+
+#include "util.h"
+#include "prioq.h"
+
+struct prioq_item {
+ void *data;
+ unsigned *idx;
+};
+
+struct Prioq {
+ compare_func_t compare_func;
+ unsigned n_items, n_allocated;
+
+ struct prioq_item *items;
+};
+
+Prioq *prioq_new(compare_func_t compare_func) {
+ Prioq *q;
+
+ q = new0(Prioq, 1);
+ if (!q)
+ return q;
+
+ q->compare_func = compare_func;
+ return q;
+}
+
+void prioq_free(Prioq *q) {
+ if (!q)
+ return;
+
+ free(q->items);
+ free(q);
+}
+
+int prioq_ensure_allocated(Prioq **q, compare_func_t compare_func) {
+ assert(q);
+
+ if (*q)
+ return 0;
+
+ *q = prioq_new(compare_func);
+ if (!*q)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static void swap(Prioq *q, unsigned j, unsigned k) {
+ void *saved_data;
+ unsigned *saved_idx;
+
+ assert(q);
+ assert(j < q->n_items);
+ assert(k < q->n_items);
+
+ assert(!q->items[j].idx || *(q->items[j].idx) == j);
+ assert(!q->items[k].idx || *(q->items[k].idx) == k);
+
+ saved_data = q->items[j].data;
+ saved_idx = q->items[j].idx;
+ q->items[j].data = q->items[k].data;
+ q->items[j].idx = q->items[k].idx;
+ q->items[k].data = saved_data;
+ q->items[k].idx = saved_idx;
+
+ if (q->items[j].idx)
+ *q->items[j].idx = j;
+
+ if (q->items[k].idx)
+ *q->items[k].idx = k;
+}
+
+static unsigned shuffle_up(Prioq *q, unsigned idx) {
+ assert(q);
+
+ while (idx > 0) {
+ unsigned k;
+
+ k = (idx-1)/2;
+
+ if (q->compare_func(q->items[k].data, q->items[idx].data) < 0)
+ break;
+
+ swap(q, idx, k);
+ idx = k;
+ }
+
+ return idx;
+}
+
+static unsigned shuffle_down(Prioq *q, unsigned idx) {
+ assert(q);
+
+ for (;;) {
+ unsigned j, k, s;
+
+ k = (idx+1)*2; /* right child */
+ j = k-1; /* left child */
+
+ if (j >= q->n_items)
+ break;
+
+ if (q->compare_func(q->items[j].data, q->items[idx].data) < 0)
+
+ /* So our left child is smaller than we are, let's
+ * remember this fact */
+ s = j;
+ else
+ s = idx;
+
+ if (k < q->n_items &&
+ q->compare_func(q->items[k].data, q->items[s].data) < 0)
+
+ /* So our right child is smaller than we are, let's
+ * remember this fact */
+ s = k;
+
+ /* s now points to the smallest of the three items */
+
+ if (s == idx)
+ /* No swap necessary, we're done */
+ break;
+
+ swap(q, idx, s);
+ idx = s;
+ }
+
+ return idx;
+}
+
+int prioq_put(Prioq *q, void *data, unsigned *idx) {
+ struct prioq_item *i;
+ unsigned k;
+
+ assert(q);
+
+ if (q->n_items >= q->n_allocated) {
+ unsigned n;
+ struct prioq_item *j;
+
+ n = MAX((q->n_items+1) * 2, 16u);
+ j = realloc(q->items, sizeof(struct prioq_item) * n);
+ if (!j)
+ return -ENOMEM;
+
+ q->items = j;
+ q->n_allocated = n;
+ }
+
+ k = q->n_items++;
+ i = q->items + k;
+ i->data = data;
+ i->idx = idx;
+
+ if (idx)
+ *idx = k;
+
+ shuffle_up(q, k);
+
+ return 0;
+}
+
+static void remove_item(Prioq *q, struct prioq_item *i) {
+ struct prioq_item *l;
+
+ assert(q);
+ assert(i);
+
+ l = q->items + q->n_items - 1;
+
+ if (i == l)
+ /* Last entry, let's just remove it */
+ q->n_items--;
+ else {
+ unsigned k;
+
+ /* Not last entry, let's replace the last entry with
+ * this one, and reshuffle */
+
+ k = i - q->items;
+
+ i->data = l->data;
+ i->idx = l->idx;
+ if (i->idx)
+ *i->idx = k;
+ q->n_items--;
+
+ k = shuffle_down(q, k);
+ shuffle_up(q, k);
+ }
+}
+
+_pure_ static struct prioq_item* find_item(Prioq *q, void *data, unsigned *idx) {
+ struct prioq_item *i;
+
+ assert(q);
+
+ if (idx) {
+ if (*idx > q->n_items)
+ return NULL;
+
+ i = q->items + *idx;
+ if (i->data != data)
+ return NULL;
+
+ return i;
+ } else {
+ for (i = q->items; i < q->items + q->n_items; i++)
+ if (i->data == data)
+ return i;
+ return NULL;
+ }
+}
+
+int prioq_remove(Prioq *q, void *data, unsigned *idx) {
+ struct prioq_item *i;
+
+ if (!q)
+ return 0;
+
+ i = find_item(q, data, idx);
+ if (!i)
+ return 0;
+
+ remove_item(q, i);
+ return 1;
+}
+
+int prioq_reshuffle(Prioq *q, void *data, unsigned *idx) {
+ struct prioq_item *i;
+ unsigned k;
+
+ assert(q);
+
+ i = find_item(q, data, idx);
+ if (!i)
+ return 0;
+
+ k = i - q->items;
+ k = shuffle_down(q, k);
+ shuffle_up(q, k);
+ return 1;
+}
+
+void *prioq_peek(Prioq *q) {
+
+ if (!q)
+ return NULL;
+
+ if (q->n_items <= 0)
+ return NULL;
+
+ return q->items[0].data;
+}
+
+void *prioq_pop(Prioq *q) {
+ void *data;
+
+ if (!q)
+ return NULL;
+
+ if (q->n_items <= 0)
+ return NULL;
+
+ data = q->items[0].data;
+ remove_item(q, q->items);
+ return data;
+}
+
+unsigned prioq_size(Prioq *q) {
+
+ if (!q)
+ return 0;
+
+ return q->n_items;
+
+}
+bool prioq_isempty(Prioq *q) {
+
+ if (!q)
+ return true;
+
+ return q->n_items <= 0;
+}
diff --git a/src/shared/prioq.h b/src/shared/prioq.h
new file mode 100644
index 0000000000..4a206a3e59
--- /dev/null
+++ b/src/shared/prioq.h
@@ -0,0 +1,40 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ 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 <http://www.gnu.org/licenses/>.
+***/
+
+#include "hashmap.h"
+
+typedef struct Prioq Prioq;
+
+Prioq *prioq_new(compare_func_t compare);
+void prioq_free(Prioq *q);
+int prioq_ensure_allocated(Prioq **q, compare_func_t compare_func);
+
+int prioq_put(Prioq *q, void *data, unsigned *idx);
+int prioq_remove(Prioq *q, void *data, unsigned *idx);
+int prioq_reshuffle(Prioq *q, void *data, unsigned *idx);
+
+void *prioq_peek(Prioq *q) _pure_;
+void *prioq_pop(Prioq *q);
+
+unsigned prioq_size(Prioq *q) _pure_;
+bool prioq_isempty(Prioq *q) _pure_;
diff --git a/src/shared/ratelimit.c b/src/shared/ratelimit.c
index 1054d52f97..01b62b7b38 100644
--- a/src/shared/ratelimit.c
+++ b/src/shared/ratelimit.c
@@ -46,7 +46,7 @@ bool ratelimit_test(RateLimit *r) {
goto good;
}
- if (r->num <= r->burst)
+ if (r->num < r->burst)
goto good;
return false;
diff --git a/src/shared/set.c b/src/shared/set.c
index 53399b655b..c338dc3a44 100644
--- a/src/shared/set.c
+++ b/src/shared/set.c
@@ -37,14 +37,6 @@ void set_free(Set* s) {
hashmap_free(MAKE_HASHMAP(s));
}
-void set_freep(Set **s) {
- if (!s)
- return;
-
- set_free(*s);
- *s = NULL;
-}
-
void set_free_free(Set *s) {
hashmap_free_free(MAKE_HASHMAP(s));
}
@@ -57,6 +49,13 @@ int set_put(Set *s, void *value) {
return hashmap_put(MAKE_HASHMAP(s), value, value);
}
+int set_consume(Set *s, void *value) {
+ int r = set_put(s, value);
+ if (r < 0)
+ free(value);
+ return r;
+}
+
int set_replace(Set *s, void *value) {
return hashmap_replace(MAKE_HASHMAP(s), value, value);
}
diff --git a/src/shared/set.h b/src/shared/set.h
index ed5b5a44d6..e5d46e9a8f 100644
--- a/src/shared/set.h
+++ b/src/shared/set.h
@@ -33,12 +33,20 @@ typedef struct Set Set;
Set *set_new(hash_func_t hash_func, compare_func_t compare_func);
void set_free(Set* s);
-void set_freep(Set **s);
+static inline void set_freep(Set **s) {
+ set_free(*s);
+}
+
void set_free_free(Set *s);
+static inline void set_free_freep(Set **s) {
+ set_free_free(*s);
+}
+
Set* set_copy(Set *s);
int set_ensure_allocated(Set **s, hash_func_t hash_func, compare_func_t compare_func);
int set_put(Set *s, void *value);
+int set_consume(Set *s, void *value);
int set_replace(Set *s, void *value);
void *set_get(Set *s, void *value);
bool set_contains(Set *s, void *value);
@@ -70,3 +78,6 @@ char **set_get_strv(Set *s);
#define SET_FOREACH_BACKWARDS(e, s, i) \
for ((i) = ITERATOR_LAST, (e) = set_iterate_backwards((s), &(i)); (e); (e) = set_iterate_backwards((s), &(i)))
+
+#define _cleanup_set_free_ _cleanup_(set_freep)
+#define _cleanup_set_free_free_ _cleanup_(set_free_freep)
diff --git a/src/shared/sleep-config.c b/src/shared/sleep-config.c
new file mode 100644
index 0000000000..cd3238b405
--- /dev/null
+++ b/src/shared/sleep-config.c
@@ -0,0 +1,179 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ 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 <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdio.h>
+
+#include "conf-parser.h"
+#include "sleep-config.h"
+#include "fileio.h"
+#include "log.h"
+#include "strv.h"
+#include "util.h"
+
+int parse_sleep_config(const char *verb, char ***modes, char ***states) {
+ _cleanup_strv_free_ char
+ **suspend_mode = NULL, **suspend_state = NULL,
+ **hibernate_mode = NULL, **hibernate_state = NULL,
+ **hybrid_mode = NULL, **hybrid_state = NULL;
+
+ const ConfigTableItem items[] = {
+ { "Sleep", "SuspendMode", config_parse_strv, 0, &suspend_mode },
+ { "Sleep", "SuspendState", config_parse_strv, 0, &suspend_state },
+ { "Sleep", "HibernateMode", config_parse_strv, 0, &hibernate_mode },
+ { "Sleep", "HibernateState", config_parse_strv, 0, &hibernate_state },
+ { "Sleep", "HybridSleepMode", config_parse_strv, 0, &hybrid_mode },
+ { "Sleep", "HybridSleepState", config_parse_strv, 0, &hybrid_state },
+ {}};
+
+ int r;
+ FILE _cleanup_fclose_ *f;
+
+ f = fopen(PKGSYSCONFDIR "/sleep.conf", "re");
+ if (!f)
+ log_full(errno == ENOENT ? LOG_DEBUG: LOG_WARNING,
+ "Failed to open configuration file " PKGSYSCONFDIR "/sleep.conf: %m");
+ else {
+ r = config_parse(NULL, PKGSYSCONFDIR "/sleep.conf", f, "Sleep\0",
+ config_item_table_lookup, (void*) items, false, false, NULL);
+ if (r < 0)
+ log_warning("Failed to parse configuration file: %s", strerror(-r));
+ }
+
+ if (streq(verb, "suspend")) {
+ /* empty by default */
+ *modes = suspend_mode;
+
+ if (suspend_state)
+ *states = suspend_state;
+ else
+ *states = strv_split_nulstr("mem\0standby\0freeze\0");
+
+ suspend_mode = suspend_state = NULL;
+ } else if (streq(verb, "hibernate")) {
+ if (hibernate_mode)
+ *modes = hibernate_mode;
+ else
+ *modes = strv_split_nulstr("platform\0shutdown\0");
+
+ if (hibernate_state)
+ *states = hibernate_state;
+ else
+ *states = strv_split_nulstr("disk\0");
+
+ hibernate_mode = hibernate_state = NULL;
+ } else if (streq(verb, "hybrid-sleep")) {
+ if (hybrid_mode)
+ *modes = hybrid_mode;
+ else
+ *modes = strv_split_nulstr("suspend\0platform\0shutdown\0");
+
+ if (hybrid_state)
+ *states = hybrid_state;
+ else
+ *states = strv_split_nulstr("disk\0");
+
+ hybrid_mode = hybrid_state = NULL;
+ } else
+ assert_not_reached("what verb");
+
+ if (!modes || !states) {
+ strv_free(*modes);
+ strv_free(*states);
+ return log_oom();
+ }
+
+ return 0;
+}
+
+int can_sleep_state(char **types) {
+ char *w, *state, **type;
+ int r;
+ _cleanup_free_ char *p = NULL;
+
+ if (strv_isempty(types))
+ return true;
+
+ /* If /sys is read-only we cannot sleep */
+ if (access("/sys/power/state", W_OK) < 0)
+ return false;
+
+ r = read_one_line_file("/sys/power/state", &p);
+ if (r < 0)
+ return false;
+
+ STRV_FOREACH(type, types) {
+ size_t l, k;
+
+ k = strlen(*type);
+ FOREACH_WORD_SEPARATOR(w, l, p, WHITESPACE, state)
+ if (l == k && memcmp(w, *type, l) == 0)
+ return true;
+ }
+
+ return false;
+}
+
+int can_sleep_disk(char **types) {
+ char *w, *state, **type;
+ int r;
+ _cleanup_free_ char *p = NULL;
+
+ if (strv_isempty(types))
+ return true;
+
+ /* If /sys is read-only we cannot sleep */
+ if (access("/sys/power/disk", W_OK) < 0)
+ return false;
+
+ r = read_one_line_file("/sys/power/disk", &p);
+ if (r < 0)
+ return false;
+
+ STRV_FOREACH(type, types) {
+ size_t l, k;
+
+ k = strlen(*type);
+ FOREACH_WORD_SEPARATOR(w, l, p, WHITESPACE, state) {
+ if (l == k && memcmp(w, *type, l) == 0)
+ return true;
+
+ if (l == k + 2 && w[0] == '[' && memcmp(w + 1, *type, l - 2) == 0 && w[l-1] == ']')
+ return true;
+ }
+ }
+
+ return false;
+}
+
+int can_sleep(const char *verb) {
+ _cleanup_strv_free_ char **modes = NULL, **states = NULL;
+ int r;
+
+ assert(streq(verb, "suspend") ||
+ streq(verb, "hibernate") ||
+ streq(verb, "hybrid-sleep"));
+
+ r = parse_sleep_config(verb, &modes, &states);
+ if (r < 0)
+ return false;
+
+ return can_sleep_state(states) && can_sleep_disk(modes);
+}
diff --git a/src/shared/sleep-config.h b/src/shared/sleep-config.h
new file mode 100644
index 0000000000..51d2dec7b4
--- /dev/null
+++ b/src/shared/sleep-config.h
@@ -0,0 +1,26 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ 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 <http://www.gnu.org/licenses/>.
+***/
+
+int parse_sleep_config(const char *verb, char ***modes, char ***states);
+
+int can_sleep(const char *verb);
+int can_sleep_disk(char **types);
+int can_sleep_state(char **types);
diff --git a/src/shared/socket-util.c b/src/shared/socket-util.c
index 56ec99f442..c583d3dfea 100644
--- a/src/shared/socket-util.c
+++ b/src/shared/socket-util.c
@@ -38,6 +38,7 @@
#include "path-util.h"
#include "socket-util.h"
#include "missing.h"
+#include "fileio.h"
int socket_address_parse(SocketAddress *a, const char *s) {
int r;
@@ -67,7 +68,7 @@ int socket_address_parse(SocketAddress *a, const char *s) {
errno = 0;
if (inet_pton(AF_INET6, n, &a->sockaddr.in6.sin6_addr) <= 0) {
free(n);
- return errno != 0 ? -errno : -EINVAL;
+ return errno > 0 ? -errno : -EINVAL;
}
free(n);
@@ -203,7 +204,7 @@ int socket_address_parse_netlink(SocketAddress *a, const char *s) {
errno = 0;
if (sscanf(s, "%ms %u", &sfamily, &group) < 1)
- return errno ? -errno : -EINVAL;
+ return errno > 0 ? -errno : -EINVAL;
family = netlink_family_from_string(sfamily);
if (family < 0)
@@ -363,7 +364,7 @@ int socket_address_print(const SocketAddress *a, char **p) {
}
case AF_NETLINK: {
- char _cleanup_free_ *sfamily = NULL;
+ _cleanup_free_ char *sfamily = NULL;
r = netlink_family_to_string_alloc(a->protocol, &sfamily);
if (r < 0)
@@ -432,7 +433,7 @@ bool socket_address_equal(const SocketAddress *a, const SocketAddress *b) {
return false;
if (a->sockaddr.un.sun_path[0]) {
- if (strncmp(a->sockaddr.un.sun_path, b->sockaddr.un.sun_path, sizeof(a->sockaddr.un.sun_path)) != 0)
+ if (!strneq(a->sockaddr.un.sun_path, b->sockaddr.un.sun_path, sizeof(a->sockaddr.un.sun_path)))
return false;
} else {
if (memcmp(a->sockaddr.un.sun_path, b->sockaddr.un.sun_path, a->size) != 0)
@@ -564,6 +565,45 @@ bool socket_address_matches_fd(const SocketAddress *a, int fd) {
return false;
}
+int make_socket_fd(const char* address, int flags) {
+ SocketAddress a;
+ int fd, r;
+ _cleanup_free_ char *p = NULL;
+
+ r = socket_address_parse(&a, address);
+ if (r < 0) {
+ log_error("failed to parse socket: %s", strerror(-r));
+ return r;
+ }
+
+ fd = socket(socket_address_family(&a), flags, 0);
+ if (fd < 0) {
+ log_error("socket(): %m");
+ return -errno;
+ }
+
+ r = socket_address_print(&a, &p);
+ if (r < 0) {
+ log_error("socket_address_print(): %s", strerror(-r));
+ return r;
+ }
+ log_info("Listening on %s", p);
+
+ r = bind(fd, &a.sockaddr.sa, a.size);
+ if (r < 0) {
+ log_error("bind to %s: %m", address);
+ return -errno;
+ }
+
+ r = listen(fd, SOMAXCONN);
+ if (r < 0) {
+ log_error("listen on %s: %m", address);
+ return -errno;
+ }
+
+ return fd;
+}
+
static const char* const netlink_family_table[] = {
[NETLINK_ROUTE] = "route",
[NETLINK_FIREWALL] = "firewall",
diff --git a/src/shared/socket-util.h b/src/shared/socket-util.h
index 771765d323..7829a337fc 100644
--- a/src/shared/socket-util.h
+++ b/src/shared/socket-util.h
@@ -67,9 +67,9 @@ typedef enum SocketAddressBindIPv6Only {
int socket_address_parse(SocketAddress *a, const char *s);
int socket_address_parse_netlink(SocketAddress *a, const char *s);
int socket_address_print(const SocketAddress *a, char **p);
-int socket_address_verify(const SocketAddress *a);
+int socket_address_verify(const SocketAddress *a) _pure_;
-bool socket_address_can_accept(const SocketAddress *a);
+bool socket_address_can_accept(const SocketAddress *a) _pure_;
int socket_address_listen(
const SocketAddress *a,
@@ -88,12 +88,14 @@ bool socket_address_is_netlink(const SocketAddress *a, const char *s);
bool socket_address_matches_fd(const SocketAddress *a, int fd);
-bool socket_address_equal(const SocketAddress *a, const SocketAddress *b);
+int make_socket_fd(const char* address, int flags);
+
+bool socket_address_equal(const SocketAddress *a, const SocketAddress *b) _pure_;
bool socket_address_needs_mount(const SocketAddress *a, const char *prefix);
-const char* socket_address_bind_ipv6_only_to_string(SocketAddressBindIPv6Only b);
-SocketAddressBindIPv6Only socket_address_bind_ipv6_only_from_string(const char *s);
+const char* socket_address_bind_ipv6_only_to_string(SocketAddressBindIPv6Only b) _const_;
+SocketAddressBindIPv6Only socket_address_bind_ipv6_only_from_string(const char *s) _pure_;
int netlink_family_to_string_alloc(int b, char **s);
int netlink_family_from_string(const char *s);
diff --git a/src/shared/spawn-polkit-agent.c b/src/shared/spawn-polkit-agent.c
index fcb3722ddf..f9e52cdcbd 100644
--- a/src/shared/spawn-polkit-agent.c
+++ b/src/shared/spawn-polkit-agent.c
@@ -33,6 +33,7 @@
#include "util.h"
#include "spawn-polkit-agent.h"
+#ifdef ENABLE_POLKIT
static pid_t agent_pid = 0;
int polkit_agent_open(void) {
@@ -84,3 +85,14 @@ void polkit_agent_close(void) {
wait_for_terminate(agent_pid, NULL);
agent_pid = 0;
}
+
+#else
+
+int polkit_agent_open(void) {
+ return 0;
+}
+
+void polkit_agent_close(void) {
+}
+
+#endif
diff --git a/src/shared/specifier.c b/src/shared/specifier.c
index 599027cd47..7577c91052 100644
--- a/src/shared/specifier.c
+++ b/src/shared/specifier.c
@@ -109,3 +109,39 @@ char *specifier_printf(const char *text, const Specifier table[], void *userdata
char* specifier_string(char specifier, void *data, void *userdata) {
return strdup(strempty(data));
}
+
+char *specifier_machine_id(char specifier, void *data, void *userdata) {
+ sd_id128_t id;
+ char *buf;
+ int r;
+
+ r = sd_id128_get_machine(&id);
+ if (r < 0)
+ return NULL;
+
+ buf = new(char, 33);
+ if (!buf)
+ return NULL;
+
+ return sd_id128_to_string(id, buf);
+}
+
+char *specifier_boot_id(char specifier, void *data, void *userdata) {
+ sd_id128_t id;
+ char *buf;
+ int r;
+
+ r = sd_id128_get_boot(&id);
+ if (r < 0)
+ return NULL;
+
+ buf = new(char, 33);
+ if (!buf)
+ return NULL;
+
+ return sd_id128_to_string(id, buf);
+}
+
+char *specifier_host_name(char specifier, void *data, void *userdata) {
+ return gethostname_malloc();
+}
diff --git a/src/shared/specifier.h b/src/shared/specifier.h
index 25a27a423f..0440dcac48 100644
--- a/src/shared/specifier.h
+++ b/src/shared/specifier.h
@@ -31,4 +31,8 @@ typedef struct Specifier {
char *specifier_printf(const char *text, const Specifier table[], void *userdata);
-char* specifier_string(char specifier, void *data, void *userdata);
+char *specifier_string(char specifier, void *data, void *userdata);
+
+char *specifier_machine_id(char specifier, void *data, void *userdata);
+char *specifier_boot_id(char specifier, void *data, void *userdata);
+char *specifier_host_name(char specifier, void *data, void *userdata);
diff --git a/src/shared/strbuf.c b/src/shared/strbuf.c
index 915cd3ac99..01a076c2ba 100644
--- a/src/shared/strbuf.c
+++ b/src/shared/strbuf.c
@@ -26,7 +26,7 @@
#include "strbuf.h"
/*
- * Strbuf stores given strings in a single continous allocated memory
+ * Strbuf stores given strings in a single continuous allocated memory
* area. Identical strings are de-duplicated and return the same offset
* as the first string stored. If the tail of a string already exists
* in the buffer, the tail is returned.
@@ -95,13 +95,36 @@ void strbuf_cleanup(struct strbuf *str) {
free(str);
}
-static int strbuf_children_cmp(const void *v1, const void *v2) {
- const struct strbuf_child_entry *n1 = v1;
- const struct strbuf_child_entry *n2 = v2;
-
+static int strbuf_children_cmp(const struct strbuf_child_entry *n1,
+ const struct strbuf_child_entry *n2) {
return n1->c - n2->c;
}
+static void bubbleinsert(struct strbuf_node *node,
+ uint8_t c,
+ struct strbuf_node *node_child) {
+
+ struct strbuf_child_entry new = {
+ .c = c,
+ .child = node_child,
+ };
+ int left = 0, right = node->children_count;
+
+ while (right > left) {
+ int middle = (right + left) / 2 ;
+ if (strbuf_children_cmp(&node->children[middle], &new) <= 0)
+ left = middle + 1;
+ else
+ right = middle;
+ }
+
+ memmove(node->children + left + 1, node->children + left,
+ sizeof(struct strbuf_child_entry) * (node->children_count - left));
+ node->children[left] = new;
+
+ node->children_count ++;
+}
+
/* add string, return the index/offset into the buffer */
ssize_t strbuf_add_string(struct strbuf *str, const char *s, size_t len) {
uint8_t c;
@@ -137,8 +160,9 @@ ssize_t strbuf_add_string(struct strbuf *str, const char *s, size_t len) {
/* lookup child node */
c = s[len - 1 - depth];
search.c = c;
- child = bsearch(&search, node->children, node->children_count, sizeof(struct strbuf_child_entry),
- strbuf_children_cmp);
+ child = bsearch(&search, node->children, node->children_count,
+ sizeof(struct strbuf_child_entry),
+ (__compar_fn_t) strbuf_children_cmp);
if (!child)
break;
node = child->child;
@@ -158,19 +182,20 @@ ssize_t strbuf_add_string(struct strbuf *str, const char *s, size_t len) {
node_child = new0(struct strbuf_node, 1);
if (!node_child)
return -ENOMEM;
- str->nodes_count++;
node_child->value_off = off;
node_child->value_len = len;
/* extend array, add new entry, sort for bisection */
child = realloc(node->children, (node->children_count + 1) * sizeof(struct strbuf_child_entry));
- if (!child)
+ if (!child) {
+ free(node_child);
return -ENOMEM;
+ }
+
+ str->nodes_count++;
+
node->children = child;
- node->children[node->children_count].c = c;
- node->children[node->children_count].child = node_child;
- node->children_count++;
- qsort(node->children, node->children_count, sizeof(struct strbuf_child_entry), strbuf_children_cmp);
+ bubbleinsert(node, c, node_child);
return off;
}
diff --git a/src/shared/strv.c b/src/shared/strv.c
index 6b76d0eaef..a5ce7e9593 100644
--- a/src/shared/strv.c
+++ b/src/shared/strv.c
@@ -64,15 +64,7 @@ void strv_free(char **l) {
free(l);
}
-void strv_freep(char ***l) {
- if (!l)
- return;
-
- strv_free(*l);
- *l = NULL;
-}
-
-char **strv_copy(char **l) {
+char **strv_copy(char * const *l) {
char **r, **k;
k = r = new(char*, strv_length(l) + 1);
@@ -92,7 +84,7 @@ char **strv_copy(char **l) {
return r;
}
-unsigned strv_length(char **l) {
+unsigned strv_length(char * const *l) {
unsigned n = 0;
if (!l)
@@ -305,6 +297,31 @@ char **strv_split_quoted(const char *s) {
return r;
}
+char **strv_split_newlines(const char *s) {
+ char **l;
+ unsigned n;
+
+ assert(s);
+
+ /* Special version of strv_split() that splits on newlines and
+ * suppresses an empty string at the end */
+
+ l = strv_split(s, NEWLINE);
+ if (!l)
+ return NULL;
+
+ n = strv_length(l);
+ if (n <= 0)
+ return l;
+
+ if (isempty(l[n-1])) {
+ free(l[n-1]);
+ l[n-1] = NULL;
+ }
+
+ return l;
+}
+
char *strv_join(char **l, const char *separator) {
char *r, *e;
char **s;
@@ -370,6 +387,43 @@ fail:
return NULL;
}
+int strv_push(char ***l, char *value) {
+ char **c;
+ unsigned n;
+
+ if (!value)
+ return 0;
+
+ n = strv_length(*l);
+ c = realloc(*l, sizeof(char*) * (n + 2));
+ if (!c)
+ return -ENOMEM;
+
+ c[n] = value;
+ c[n+1] = NULL;
+
+ *l = c;
+ return 0;
+}
+
+int strv_extend(char ***l, const char *value) {
+ char *v;
+ int r;
+
+ if (!value)
+ return 0;
+
+ v = strdup(value);
+ if (!v)
+ return -ENOMEM;
+
+ r = strv_push(l, v);
+ if (r < 0)
+ free(v);
+
+ return r;
+}
+
char **strv_uniq(char **l) {
char **i;
@@ -432,249 +486,6 @@ char **strv_remove_prefix(char **l, const char *s) {
return l;
}
-static int env_append(char **r, char ***k, char **a) {
- assert(r);
- assert(k);
-
- if (!a)
- return 0;
-
- /* Add the entries of a to *k unless they already exist in *r
- * in which case they are overridden instead. This assumes
- * there is enough space in the r array. */
-
- for (; *a; a++) {
- char **j;
- size_t n;
-
- n = strcspn(*a, "=");
-
- if ((*a)[n] == '=')
- n++;
-
- for (j = r; j < *k; j++)
- if (strncmp(*j, *a, n) == 0)
- break;
-
- if (j >= *k)
- (*k)++;
- else
- free(*j);
-
- *j = strdup(*a);
- if (!*j)
- return -ENOMEM;
- }
-
- return 0;
-}
-
-char **strv_env_merge(unsigned n_lists, ...) {
- size_t n = 0;
- char **l, **k, **r;
- va_list ap;
- unsigned i;
-
- /* Merges an arbitrary number of environment sets */
-
- va_start(ap, n_lists);
- for (i = 0; i < n_lists; i++) {
- l = va_arg(ap, char**);
- n += strv_length(l);
- }
- va_end(ap);
-
- r = new(char*, n+1);
- if (!r)
- return NULL;
-
- k = r;
-
- va_start(ap, n_lists);
- for (i = 0; i < n_lists; i++) {
- l = va_arg(ap, char**);
- if (env_append(r, &k, l) < 0)
- goto fail;
- }
- va_end(ap);
-
- *k = NULL;
-
- return r;
-
-fail:
- va_end(ap);
- strv_free(r);
-
- return NULL;
-}
-
-static bool env_match(const char *t, const char *pattern) {
- assert(t);
- assert(pattern);
-
- /* pattern a matches string a
- * a matches a=
- * a matches a=b
- * a= matches a=
- * a=b matches a=b
- * a= does not match a
- * a=b does not match a=
- * a=b does not match a
- * a=b does not match a=c */
-
- if (streq(t, pattern))
- return true;
-
- if (!strchr(pattern, '=')) {
- size_t l = strlen(pattern);
-
- return strncmp(t, pattern, l) == 0 && t[l] == '=';
- }
-
- return false;
-}
-
-char **strv_env_delete(char **x, unsigned n_lists, ...) {
- size_t n, i = 0;
- char **k, **r;
- va_list ap;
-
- /* Deletes every entry from x that is mentioned in the other
- * string lists */
-
- n = strv_length(x);
-
- r = new(char*, n+1);
- if (!r)
- return NULL;
-
- STRV_FOREACH(k, x) {
- unsigned v;
-
- va_start(ap, n_lists);
- for (v = 0; v < n_lists; v++) {
- char **l, **j;
-
- l = va_arg(ap, char**);
- STRV_FOREACH(j, l)
- if (env_match(*k, *j))
- goto skip;
- }
- va_end(ap);
-
- r[i] = strdup(*k);
- if (!r[i]) {
- strv_free(r);
- return NULL;
- }
-
- i++;
- continue;
-
- skip:
- va_end(ap);
- }
-
- r[i] = NULL;
-
- assert(i <= n);
-
- return r;
-}
-
-char **strv_env_unset(char **l, const char *p) {
-
- char **f, **t;
-
- if (!l)
- return NULL;
-
- assert(p);
-
- /* Drops every occurrence of the env var setting p in the
- * string list. edits in-place. */
-
- for (f = t = l; *f; f++) {
-
- if (env_match(*f, p)) {
- free(*f);
- continue;
- }
-
- *(t++) = *f;
- }
-
- *t = NULL;
- return l;
-}
-
-char **strv_env_set(char **x, const char *p) {
-
- char **k, **r;
- char* m[2] = { (char*) p, NULL };
-
- /* Overrides the env var setting of p, returns a new copy */
-
- r = new(char*, strv_length(x)+2);
- if (!r)
- return NULL;
-
- k = r;
- if (env_append(r, &k, x) < 0)
- goto fail;
-
- if (env_append(r, &k, m) < 0)
- goto fail;
-
- *k = NULL;
-
- return r;
-
-fail:
- strv_free(r);
- return NULL;
-
-}
-
-char *strv_env_get_with_length(char **l, const char *name, size_t k) {
- char **i;
-
- assert(name);
-
- STRV_FOREACH(i, l)
- if (strncmp(*i, name, k) == 0 &&
- (*i)[k] == '=')
- return *i + k + 1;
-
- return NULL;
-}
-
-char *strv_env_get(char **l, const char *name) {
- return strv_env_get_with_length(l, name, strlen(name));
-}
-
-char **strv_env_clean(char **l) {
- char **r, **ret;
-
- for (r = ret = l; *l; l++) {
- const char *equal;
-
- equal = strchr(*l, '=');
-
- if (equal && equal[1] == 0) {
- free(*l);
- continue;
- }
-
- *(r++) = *l;
- }
-
- *r = NULL;
-
- return ret;
-}
-
char **strv_parse_nulstr(const char *s, size_t l) {
const char *p;
unsigned c = 0, i = 0;
@@ -721,6 +532,22 @@ char **strv_parse_nulstr(const char *s, size_t l) {
return v;
}
+char **strv_split_nulstr(const char *s) {
+ const char *i;
+ char **r = NULL;
+
+ NULSTR_FOREACH(i, s)
+ if (strv_extend(&r, i) < 0) {
+ strv_free(r);
+ return NULL;
+ }
+
+ if (!r)
+ return strv_new(NULL, NULL);
+
+ return r;
+}
+
bool strv_overlap(char **a, char **b) {
char **i, **j;
@@ -748,3 +575,13 @@ char **strv_sort(char **l) {
qsort(l, strv_length(l), sizeof(char*), str_compare);
return l;
}
+
+void strv_print(char **l) {
+ char **s;
+
+ if (!l)
+ return;
+
+ STRV_FOREACH(s, l)
+ puts(*s);
+}
diff --git a/src/shared/strv.h b/src/shared/strv.h
index 45558d8960..e35118752f 100644
--- a/src/shared/strv.h
+++ b/src/shared/strv.h
@@ -26,17 +26,24 @@
#include "macro.h"
-char *strv_find(char **l, const char *name);
-char *strv_find_prefix(char **l, const char *name);
+char *strv_find(char **l, const char *name) _pure_;
+char *strv_find_prefix(char **l, const char *name) _pure_;
void strv_free(char **l);
-void strv_freep(char ***l);
-char **strv_copy(char **l) _malloc_;
-unsigned strv_length(char **l);
+static inline void strv_freep(char ***l) {
+ strv_free(*l);
+}
+
+#define _cleanup_strv_free_ _cleanup_(strv_freep)
+
+char **strv_copy(char * const *l);
+unsigned strv_length(char * const *l) _pure_;
char **strv_merge(char **a, char **b);
char **strv_merge_concat(char **a, char **b, const char *suffix);
char **strv_append(char **l, const char *s);
+int strv_extend(char ***l, const char *value);
+int strv_push(char ***l, char *value);
char **strv_remove(char **l, const char *s);
char **strv_remove_prefix(char **l, const char *s);
@@ -44,36 +51,27 @@ char **strv_uniq(char **l);
#define strv_contains(l, s) (!!strv_find((l), (s)))
-char **strv_new(const char *x, ...) _sentinel_ _malloc_;
-char **strv_new_ap(const char *x, va_list ap) _malloc_;
+char **strv_new(const char *x, ...) _sentinel_;
+char **strv_new_ap(const char *x, va_list ap);
static inline const char* STRV_IFNOTNULL(const char *x) {
return x ? x : (const char *) -1;
}
-static inline bool strv_isempty(char **l) {
+static inline bool strv_isempty(char * const *l) {
return !l || !*l;
}
-char **strv_split(const char *s, const char *separator) _malloc_;
-char **strv_split_quoted(const char *s) _malloc_;
-
-char *strv_join(char **l, const char *separator) _malloc_;
-
-char **strv_env_merge(unsigned n_lists, ...);
-char **strv_env_delete(char **x, unsigned n_lists, ...);
+char **strv_split(const char *s, const char *separator);
+char **strv_split_quoted(const char *s);
+char **strv_split_newlines(const char *s);
-char **strv_env_set(char **x, const char *p);
-char **strv_env_unset(char **l, const char *p);
-
-char *strv_env_get_with_length(char **l, const char *name, size_t k);
-char *strv_env_get(char **x, const char *n);
-
-char **strv_env_clean(char **l);
+char *strv_join(char **l, const char *separator);
char **strv_parse_nulstr(const char *s, size_t l);
+char **strv_split_nulstr(const char *s);
-bool strv_overlap(char **a, char **b);
+bool strv_overlap(char **a, char **b) _pure_;
#define STRV_FOREACH(s, l) \
for ((s) = (l); (s) && *(s); (s)++)
@@ -81,4 +79,9 @@ bool strv_overlap(char **a, char **b);
#define STRV_FOREACH_BACKWARDS(s, l) \
for (; (l) && ((s) >= (l)); (s)--)
+#define STRV_FOREACH_PAIR(x, y, l) \
+ for ((x) = (l), (y) = (x+1); (x) && *(x) && *(y); (x) += 2, (y) = (x + 1))
+
+
char **strv_sort(char **l);
+void strv_print(char **l);
diff --git a/src/shared/strxcpyx.c b/src/shared/strxcpyx.c
new file mode 100644
index 0000000000..3cb04b25cd
--- /dev/null
+++ b/src/shared/strxcpyx.c
@@ -0,0 +1,104 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 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 <http://www.gnu.org/licenses/>.
+***/
+
+/*
+ * Concatenates/copies strings. In any case, terminates in all cases
+ * with '\0' * and moves the @dest pointer forward to the added '\0'.
+ * Returns the * remaining size, and 0 if the string was truncated.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include "strxcpyx.h"
+
+size_t strpcpy(char **dest, size_t size, const char *src)
+{
+ size_t len;
+
+ len = strlen(src);
+ if (len >= size) {
+ if (size > 1)
+ *dest = mempcpy(*dest, src, size-1);
+ size = 0;
+ } else {
+ if (len > 0) {
+ *dest = mempcpy(*dest, src, len);
+ size -= len;
+ }
+ }
+ *dest[0] = '\0';
+ return size;
+}
+
+size_t strpcpyf(char **dest, size_t size, const char *src, ...)
+{
+ va_list va;
+ int i;
+
+ va_start(va, src);
+ i = vsnprintf(*dest, size, src, va);
+ if (i < (int)size) {
+ *dest += i;
+ size -= i;
+ } else {
+ *dest += size;
+ size = 0;
+ }
+ va_end(va);
+ *dest[0] = '\0';
+ return size;
+}
+
+size_t strpcpyl(char **dest, size_t size, const char *src, ...)
+{
+ va_list va;
+
+ va_start(va, src);
+ do {
+ size = strpcpy(dest, size, src);
+ src = va_arg(va, char *);
+ } while (src != NULL);
+ va_end(va);
+ return size;
+}
+
+size_t strscpy(char *dest, size_t size, const char *src)
+{
+ char *s;
+
+ s = dest;
+ return strpcpy(&s, size, src);
+}
+
+size_t strscpyl(char *dest, size_t size, const char *src, ...) {
+ va_list va;
+ char *s;
+
+ va_start(va, src);
+ s = dest;
+ do {
+ size = strpcpy(&s, size, src);
+ src = va_arg(va, char *);
+ } while (src != NULL);
+ va_end(va);
+
+ return size;
+}
diff --git a/src/shared/strxcpyx.h b/src/shared/strxcpyx.h
new file mode 100644
index 0000000000..1229a4821d
--- /dev/null
+++ b/src/shared/strxcpyx.h
@@ -0,0 +1,31 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 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 <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdarg.h>
+#include <stdbool.h>
+
+size_t strpcpy(char **dest, size_t size, const char *src);
+size_t strpcpyf(char **dest, size_t size, const char *src, ...) __attribute__((format(printf, 3, 4)));
+size_t strpcpyl(char **dest, size_t size, const char *src, ...) __attribute__((sentinel));
+size_t strscpy(char *dest, size_t size, const char *src);
+size_t strscpyl(char *dest, size_t size, const char *src, ...) __attribute__((sentinel));
diff --git a/src/shared/time-dst.c b/src/shared/time-dst.c
index afc893cccc..3950a882bf 100644
--- a/src/shared/time-dst.c
+++ b/src/shared/time-dst.c
@@ -38,6 +38,7 @@
#include <sys/stat.h>
#include "time-dst.h"
+#include "util.h"
/*
* If tzh_version is '2' or greater, the above is followed by a second instance
@@ -84,15 +85,12 @@ static inline int64_t decode64(const void *ptr) {
int time_get_dst(time_t date, const char *tzfile,
time_t *switch_cur, char **zone_cur, bool *dst_cur,
time_t *switch_next, int *delta_next, char **zone_next, bool *dst_next) {
- time_t *transitions = NULL;
- size_t num_transitions = 0;
unsigned char *type_idxs = 0;
size_t num_types = 0;
struct ttinfo *types = NULL;
char *zone_names = NULL;
struct stat st;
size_t num_isstd, num_isgmt;
- FILE *f;
struct tzhead tzhead;
size_t chars;
size_t i;
@@ -102,22 +100,21 @@ int time_get_dst(time_t date, const char *tzfile,
size_t tzspec_len;
size_t num_leaps;
size_t lo, hi;
- int err = -EINVAL;
+ size_t num_transitions = 0;
+ _cleanup_free_ time_t *transitions = NULL;
+ _cleanup_fclose_ FILE *f;
f = fopen(tzfile, "re");
if (f == NULL)
return -errno;
- if (fstat(fileno(f), &st) < 0) {
- err = -errno;
- fclose(f);
- return err;
- }
+ if (fstat(fileno(f), &st) < 0)
+ return -errno;
read_again:
if (fread((void *)&tzhead, sizeof(tzhead), 1, f) != 1 ||
memcmp(tzhead.tzh_magic, TZ_MAGIC, sizeof(tzhead.tzh_magic)) != 0)
- goto lose;
+ return -EINVAL;
num_transitions = (size_t)decode(tzhead.tzh_timecnt);
num_types = (size_t)decode(tzhead.tzh_typecnt);
@@ -139,31 +136,31 @@ read_again:
+ chars
+ num_leaps * 8 + num_isstd + num_isgmt);
if (fseek(f, to_skip, SEEK_CUR) != 0)
- goto lose;
+ return -EINVAL;
goto read_again;
}
if (num_transitions > ((SIZE_MAX - (__alignof__(struct ttinfo) - 1)) / (sizeof(time_t) + 1)))
- goto lose;
+ return -EINVAL;
total_size = num_transitions * (sizeof(time_t) + 1);
total_size = ((total_size + __alignof__(struct ttinfo) - 1) & ~(__alignof__(struct ttinfo) - 1));
types_idx = total_size;
if (num_leaps > (SIZE_MAX - total_size) / sizeof(struct ttinfo))
- goto lose;
+ return -EINVAL;
total_size += num_types * sizeof(struct ttinfo);
if (chars > SIZE_MAX - total_size)
- goto lose;
+ return -EINVAL;
total_size += chars;
if (__alignof__(struct leap) - 1 > SIZE_MAX - total_size)
- goto lose;
+ return -EINVAL;
total_size = ((total_size + __alignof__(struct leap) - 1) & ~(__alignof__(struct leap) - 1));
if (num_leaps > (SIZE_MAX - total_size) / sizeof(struct leap))
- goto lose;
+ return -EINVAL;
total_size += num_leaps * sizeof(struct leap);
tzspec_len = 0;
@@ -171,24 +168,24 @@ read_again:
off_t rem = st.st_size - ftello(f);
if (rem < 0 || (size_t) rem < (num_transitions * (8 + 1) + num_types * 6 + chars))
- goto lose;
+ return -EINVAL;
tzspec_len = (size_t) rem - (num_transitions * (8 + 1) + num_types * 6 + chars);
if (num_leaps > SIZE_MAX / 12 || tzspec_len < num_leaps * 12)
- goto lose;
+ return -EINVAL;
tzspec_len -= num_leaps * 12;
if (tzspec_len < num_isstd)
- goto lose;
+ return -EINVAL;
tzspec_len -= num_isstd;
if (tzspec_len == 0 || tzspec_len - 1 < num_isgmt)
- goto lose;
+ return -EINVAL;
tzspec_len -= num_isgmt + 1;
if (SIZE_MAX - total_size < tzspec_len)
- goto lose;
+ return -EINVAL;
}
transitions = (time_t *)calloc(total_size + tzspec_len, 1);
if (transitions == NULL)
- goto lose;
+ return -EINVAL;
type_idxs = (unsigned char *)transitions + (num_transitions
* sizeof(time_t));
@@ -197,18 +194,18 @@ read_again:
if (sizeof(time_t) == 4 || trans_width == 8) {
if (fread(transitions, trans_width + 1, num_transitions, f) != num_transitions)
- goto lose;
+ return -EINVAL;
} else {
if (fread(transitions, 4, num_transitions, f) != num_transitions ||
fread(type_idxs, 1, num_transitions, f) != num_transitions)
- goto lose;
+ return -EINVAL;
}
/* Check for bogus indices in the data file, so we can hereafter
safely use type_idxs[T] as indices into `types' and never crash. */
for (i = 0; i < num_transitions; ++i)
if (type_idxs[i] >= num_types)
- goto lose;
+ return -EINVAL;
if ((BYTE_ORDER != BIG_ENDIAN && (sizeof(time_t) == 4 || trans_width == 4)) ||
(BYTE_ORDER == BIG_ENDIAN && sizeof(time_t) == 8 && trans_width == 4)) {
@@ -231,26 +228,26 @@ read_again:
int c;
if (fread(x, 1, sizeof(x), f) != sizeof(x))
- goto lose;
+ return -EINVAL;
c = getc(f);
if ((unsigned int)c > 1u)
- goto lose;
+ return -EINVAL;
types[i].isdst = c;
c = getc(f);
if ((size_t) c > chars)
/* Bogus index in data file. */
- goto lose;
+ return -EINVAL;
types[i].idx = c;
types[i].offset = (long int)decode(x);
}
if (fread(zone_names, 1, chars, f) != chars)
- goto lose;
+ return -EINVAL;
for (i = 0; i < num_isstd; ++i) {
int c = getc(f);
if (c == EOF)
- goto lose;
+ return -EINVAL;
types[i].isstd = c != 0;
}
@@ -260,7 +257,7 @@ read_again:
for (i = 0; i < num_isgmt; ++i) {
int c = getc(f);
if (c == EOF)
- goto lose;
+ return -EINVAL;
types[i].isgmt = c != 0;
}
@@ -268,10 +265,10 @@ read_again:
types[i++].isgmt = 0;
if (num_transitions == 0)
- goto lose;
+ return -EINVAL;
if (date < transitions[0] || date >= transitions[num_transitions - 1])
- goto lose;
+ return -EINVAL;
/* Find the first transition after TIMER, and
then pick the type of the transition before it. */
@@ -331,11 +328,5 @@ found:
if (dst_next)
*dst_next = types[type_idxs[i]].isdst;
- free(transitions);
- fclose(f);
return 0;
-lose:
- free(transitions);
- fclose(f);
- return err;
}
diff --git a/src/shared/time-util.c b/src/shared/time-util.c
index 4fd8f08984..9ee711a49e 100644
--- a/src/shared/time-util.c
+++ b/src/shared/time-util.c
@@ -225,11 +225,13 @@ char *format_timestamp_relative(char *buf, size_t l, usec_t t) {
return buf;
}
-char *format_timespan(char *buf, size_t l, usec_t t) {
+char *format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy) {
static const struct {
const char *suffix;
usec_t usec;
} table[] = {
+ { "y", USEC_PER_YEAR },
+ { "month", USEC_PER_MONTH },
{ "w", USEC_PER_WEEK },
{ "d", USEC_PER_DAY },
{ "h", USEC_PER_HOUR },
@@ -241,6 +243,7 @@ char *format_timespan(char *buf, size_t l, usec_t t) {
unsigned i;
char *p = buf;
+ bool something = false;
assert(buf);
assert(l > 0);
@@ -248,17 +251,25 @@ char *format_timespan(char *buf, size_t l, usec_t t) {
if (t == (usec_t) -1)
return NULL;
- if (t == 0) {
+ if (t <= 0) {
snprintf(p, l, "0");
p[l-1] = 0;
return p;
}
- /* The result of this function can be parsed with parse_usec */
+ /* The result of this function can be parsed with parse_sec */
for (i = 0; i < ELEMENTSOF(table); i++) {
int k;
size_t n;
+ bool done = false;
+ usec_t a, b;
+
+ if (t <= 0)
+ break;
+
+ if (t < accuracy && something)
+ break;
if (t < table[i].usec)
continue;
@@ -266,13 +277,54 @@ char *format_timespan(char *buf, size_t l, usec_t t) {
if (l <= 1)
break;
- k = snprintf(p, l, "%s%llu%s", p > buf ? " " : "", (unsigned long long) (t / table[i].usec), table[i].suffix);
+ a = t / table[i].usec;
+ b = t % table[i].usec;
+
+ /* Let's see if we should shows this in dot notation */
+ if (t < USEC_PER_MINUTE && b > 0) {
+ usec_t cc;
+ int j;
+
+ j = 0;
+ for (cc = table[i].usec; cc > 1; cc /= 10)
+ j++;
+
+ for (cc = accuracy; cc > 1; cc /= 10) {
+ b /= 10;
+ j--;
+ }
+
+ if (j > 0) {
+ k = snprintf(p, l,
+ "%s%llu.%0*llu%s",
+ p > buf ? " " : "",
+ (unsigned long long) a,
+ j,
+ (unsigned long long) b,
+ table[i].suffix);
+
+ t = 0;
+ done = true;
+ }
+ }
+
+ /* No? Then let's show it normally */
+ if (!done) {
+ k = snprintf(p, l,
+ "%s%llu%s",
+ p > buf ? " " : "",
+ (unsigned long long) a,
+ table[i].suffix);
+
+ t = b;
+ }
+
n = MIN((size_t) k, l);
l -= n;
p += n;
- t %= table[i].usec;
+ something = true;
}
*p = 0;
@@ -380,14 +432,14 @@ int parse_timestamp(const char *t, usec_t *usec) {
} else if (t[0] == '+') {
- r = parse_usec(t+1, &plus);
+ r = parse_sec(t+1, &plus);
if (r < 0)
return r;
goto finish;
} else if (t[0] == '-') {
- r = parse_usec(t+1, &minus);
+ r = parse_sec(t+1, &minus);
if (r < 0)
return r;
@@ -400,7 +452,7 @@ int parse_timestamp(const char *t, usec_t *usec) {
if (!z)
return -ENOMEM;
- r = parse_usec(z, &minus);
+ r = parse_sec(z, &minus);
if (r < 0)
return r;
@@ -495,7 +547,7 @@ finish:
return 0;
}
-int parse_usec(const char *t, usec_t *usec) {
+int parse_sec(const char *t, usec_t *usec) {
static const struct {
const char *suffix;
usec_t usec;
@@ -532,41 +584,74 @@ int parse_usec(const char *t, usec_t *usec) {
const char *p;
usec_t r = 0;
+ bool something = false;
assert(t);
assert(usec);
p = t;
- do {
- long long l;
+ for (;;) {
+ long long l, z = 0;
char *e;
- unsigned i;
+ unsigned i, n = 0;
+
+ p += strspn(p, WHITESPACE);
+
+ if (*p == 0) {
+ if (!something)
+ return -EINVAL;
+
+ break;
+ }
errno = 0;
l = strtoll(p, &e, 10);
- if (errno != 0)
+ if (errno > 0)
return -errno;
if (l < 0)
return -ERANGE;
- if (e == p)
+ if (*e == '.') {
+ char *b = e + 1;
+
+ errno = 0;
+ z = strtoll(b, &e, 10);
+ if (errno > 0)
+ return -errno;
+
+ if (z < 0)
+ return -ERANGE;
+
+ if (e == b)
+ return -EINVAL;
+
+ n = e - b;
+
+ } else if (e == p)
return -EINVAL;
e += strspn(e, WHITESPACE);
for (i = 0; i < ELEMENTSOF(table); i++)
if (startswith(e, table[i].suffix)) {
- r += (usec_t) l * table[i].usec;
+ usec_t k = (usec_t) z * table[i].usec;
+
+ for (; n > 0; n--)
+ k /= 10;
+
+ r += (usec_t) l * table[i].usec + k;
p = e + strlen(table[i].suffix);
+
+ something = true;
break;
}
if (i >= ELEMENTSOF(table))
return -EINVAL;
- } while (*p != 0);
+ }
*usec = r;
@@ -612,41 +697,74 @@ int parse_nsec(const char *t, nsec_t *nsec) {
const char *p;
nsec_t r = 0;
+ bool something = false;
assert(t);
assert(nsec);
p = t;
- do {
- long long l;
+ for (;;) {
+ long long l, z = 0;
char *e;
- unsigned i;
+ unsigned i, n = 0;
+
+ p += strspn(p, WHITESPACE);
+
+ if (*p == 0) {
+ if (!something)
+ return -EINVAL;
+
+ break;
+ }
errno = 0;
l = strtoll(p, &e, 10);
- if (errno != 0)
+ if (errno > 0)
return -errno;
if (l < 0)
return -ERANGE;
- if (e == p)
+ if (*e == '.') {
+ char *b = e + 1;
+
+ errno = 0;
+ z = strtoll(b, &e, 10);
+ if (errno > 0)
+ return -errno;
+
+ if (z < 0)
+ return -ERANGE;
+
+ if (e == b)
+ return -EINVAL;
+
+ n = e - b;
+
+ } else if (e == p)
return -EINVAL;
e += strspn(e, WHITESPACE);
for (i = 0; i < ELEMENTSOF(table); i++)
if (startswith(e, table[i].suffix)) {
- r += (nsec_t) l * table[i].nsec;
+ nsec_t k = (nsec_t) z * table[i].nsec;
+
+ for (; n > 0; n--)
+ k /= 10;
+
+ r += (nsec_t) l * table[i].nsec + k;
p = e + strlen(table[i].suffix);
+
+ something = true;
break;
}
if (i >= ELEMENTSOF(table))
return -EINVAL;
- } while (*p != 0);
+ }
*nsec = r;
diff --git a/src/shared/time-util.h b/src/shared/time-util.h
index ec263c1a19..f27a006891 100644
--- a/src/shared/time-util.h
+++ b/src/shared/time-util.h
@@ -22,6 +22,7 @@
***/
#include <stdio.h>
+#include <inttypes.h>
typedef uint64_t usec_t;
typedef uint64_t nsec_t;
@@ -65,20 +66,20 @@ dual_timestamp* dual_timestamp_from_monotonic(dual_timestamp *ts, usec_t u);
#define dual_timestamp_is_set(ts) ((ts)->realtime > 0)
-usec_t timespec_load(const struct timespec *ts);
+usec_t timespec_load(const struct timespec *ts) _pure_;
struct timespec *timespec_store(struct timespec *ts, usec_t u);
-usec_t timeval_load(const struct timeval *tv);
+usec_t timeval_load(const struct timeval *tv) _pure_;
struct timeval *timeval_store(struct timeval *tv, usec_t u);
char *format_timestamp(char *buf, size_t l, usec_t t);
char *format_timestamp_relative(char *buf, size_t l, usec_t t);
-char *format_timespan(char *buf, size_t l, usec_t t);
+char *format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy);
void dual_timestamp_serialize(FILE *f, const char *name, dual_timestamp *t);
void dual_timestamp_deserialize(const char *value, dual_timestamp *t);
int parse_timestamp(const char *t, usec_t *usec);
-int parse_usec(const char *t, usec_t *usec);
+int parse_sec(const char *t, usec_t *usec);
int parse_nsec(const char *t, nsec_t *nsec);
diff --git a/src/shared/unit-name.c b/src/shared/unit-name.c
index 88ca0b8f2c..a809713595 100644
--- a/src/shared/unit-name.c
+++ b/src/shared/unit-name.c
@@ -33,7 +33,7 @@
"ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
":-_.\\"
-const char* const unit_type_table[_UNIT_TYPE_MAX] = {
+static const char* const unit_type_table[_UNIT_TYPE_MAX] = {
[UNIT_SERVICE] = "service",
[UNIT_SOCKET] = "socket",
[UNIT_TARGET] = "target",
@@ -48,7 +48,7 @@ const char* const unit_type_table[_UNIT_TYPE_MAX] = {
DEFINE_STRING_TABLE_LOOKUP(unit_type, UnitType);
-const char* const unit_load_state_table[_UNIT_LOAD_STATE_MAX] = {
+static const char* const unit_load_state_table[_UNIT_LOAD_STATE_MAX] = {
[UNIT_STUB] = "stub",
[UNIT_LOADED] = "loaded",
[UNIT_ERROR] = "error",
@@ -506,6 +506,36 @@ char *unit_name_mangle(const char *name) {
return r;
}
+char *snapshot_name_mangle(const char *name) {
+ char *r, *t;
+ const char *f;
+
+ assert(name);
+
+ /* Similar to unit_name_mangle(), but is called when we know
+ * that this is about snapshot units. */
+
+ r = new(char, strlen(name) * 4 + 1 + sizeof(".snapshot")-1);
+ if (!r)
+ return NULL;
+
+ for (f = name, t = r; *f; f++) {
+ if (*f == '/')
+ *(t++) = '-';
+ else if (!strchr(VALID_CHARS, *f))
+ t = do_escape_char(*f, t);
+ else
+ *(t++) = *f;
+ }
+
+ if (!endswith(name, ".snapshot"))
+ strcpy(t, ".snapshot");
+ else
+ *t = 0;
+
+ return r;
+}
+
UnitType unit_name_to_type(const char *n) {
const char *e;
diff --git a/src/shared/unit-name.h b/src/shared/unit-name.h
index d7528a3ac7..9eca8eb3c1 100644
--- a/src/shared/unit-name.h
+++ b/src/shared/unit-name.h
@@ -23,6 +23,8 @@
#include <stdbool.h>
+#include "macro.h"
+
#define UNIT_NAME_MAX 256
typedef enum UnitType UnitType;
@@ -53,23 +55,21 @@ enum UnitLoadState {
_UNIT_LOAD_STATE_INVALID = -1
};
-extern const char* const unit_type_table[];
-const char *unit_type_to_string(UnitType i);
-UnitType unit_type_from_string(const char *s);
+const char *unit_type_to_string(UnitType i) _const_;
+UnitType unit_type_from_string(const char *s) _pure_;
-extern const char* const unit_load_state_table[];
-const char *unit_load_state_to_string(UnitLoadState i);
-UnitLoadState unit_load_state_from_string(const char *s);
+const char *unit_load_state_to_string(UnitLoadState i) _const_;
+UnitLoadState unit_load_state_from_string(const char *s) _pure_;
int unit_name_to_instance(const char *n, char **instance);
char* unit_name_to_prefix(const char *n);
char* unit_name_to_prefix_and_instance(const char *n);
-bool unit_name_is_valid(const char *n, bool template_ok);
-bool unit_prefix_is_valid(const char *p);
-bool unit_instance_is_valid(const char *i);
+bool unit_name_is_valid(const char *n, bool template_ok) _pure_;
+bool unit_prefix_is_valid(const char *p) _pure_;
+bool unit_instance_is_valid(const char *i) _pure_;
-UnitType unit_name_to_type(const char *n);
+UnitType unit_name_to_type(const char *n) _pure_;
char *unit_name_change_suffix(const char *n, const char *suffix);
@@ -80,8 +80,8 @@ char *unit_name_unescape(const char *f);
char *unit_name_path_escape(const char *f);
char *unit_name_path_unescape(const char *f);
-bool unit_name_is_template(const char *n);
-bool unit_name_is_instance(const char *n);
+bool unit_name_is_template(const char *n) _pure_;
+bool unit_name_is_instance(const char *n) _pure_;
char *unit_name_replace_instance(const char *f, const char *i);
@@ -94,3 +94,4 @@ char *unit_name_to_path(const char *name);
char *unit_dbus_path_from_name(const char *name);
char *unit_name_mangle(const char *name);
+char *snapshot_name_mangle(const char *name);
diff --git a/src/shared/utf8.c b/src/shared/utf8.c
index 62e2803919..3964e8b1ce 100644
--- a/src/shared/utf8.c
+++ b/src/shared/utf8.c
@@ -49,6 +49,7 @@
#include <stdbool.h>
#include "utf8.h"
+#include "util.h"
#define FILTER_CHAR '_'
@@ -283,3 +284,39 @@ char *ascii_filter(const char *str) {
return r;
}
+
+char *utf16_to_utf8(const void *s, size_t length) {
+ char *r;
+ const uint8_t *f;
+ uint8_t *t;
+
+ r = new(char, (length*3+1)/2 + 1);
+ if (!r)
+ return NULL;
+
+ t = (uint8_t*) r;
+
+ for (f = s; f < (const uint8_t*) s + length; f += 2) {
+ uint16_t c;
+
+ c = (f[1] << 8) | f[0];
+
+ if (c == 0) {
+ *t = 0;
+ return r;
+ } else if (c < 0x80) {
+ *(t++) = (uint8_t) c;
+ } else if (c < 0x800) {
+ *(t++) = (uint8_t) (0xc0 | (c >> 6));
+ *(t++) = (uint8_t) (0x80 | (c & 0x3f));
+ } else {
+ *(t++) = (uint8_t) (0xe0 | (c >> 12));
+ *(t++) = (uint8_t) (0x80 | ((c >> 6) & 0x3f));
+ *(t++) = (uint8_t) (0x80 | (c & 0x3f));
+ }
+ }
+
+ *t = 0;
+
+ return r;
+}
diff --git a/src/shared/utf8.h b/src/shared/utf8.h
index 13d2f6a980..794ae15ab9 100644
--- a/src/shared/utf8.h
+++ b/src/shared/utf8.h
@@ -30,3 +30,5 @@ char *utf8_is_printable_n(const char* str, size_t length) _pure_;
char *utf8_filter(const char *s);
char *ascii_filter(const char *s);
+
+char *utf16_to_utf8(const void *s, size_t length);
diff --git a/src/shared/util.c b/src/shared/util.c
index 49b58444c7..673e0da6b6 100644
--- a/src/shared/util.c
+++ b/src/shared/util.c
@@ -59,6 +59,7 @@
#include <limits.h>
#include <langinfo.h>
#include <locale.h>
+#include <libgen.h>
#include "macro.h"
#include "util.h"
@@ -70,6 +71,8 @@
#include "path-util.h"
#include "exit-status.h"
#include "hashmap.h"
+#include "env-util.h"
+#include "fileio.h"
int saved_argc = 0;
char **saved_argv = NULL;
@@ -77,10 +80,6 @@ char **saved_argv = NULL;
static volatile unsigned cached_columns = 0;
static volatile unsigned cached_lines = 0;
-bool is_efiboot(void) {
- return access("/sys/firmware/efi", F_OK) >= 0;
-}
-
size_t page_size(void) {
static __thread size_t pgsz = 0;
long r;
@@ -186,44 +185,62 @@ bool first_word(const char *s, const char *word) {
}
int close_nointr(int fd) {
- assert(fd >= 0);
-
- for (;;) {
- int r;
+ int r;
- r = close(fd);
- if (r >= 0)
- return r;
+ assert(fd >= 0);
+ r = close(fd);
- if (errno != EINTR)
- return -errno;
- }
+ /* Just ignore EINTR; a retry loop is the wrong
+ * thing to do on Linux.
+ *
+ * http://lkml.indiana.edu/hypermail/linux/kernel/0509.1/0877.html
+ * https://bugzilla.gnome.org/show_bug.cgi?id=682819
+ * http://utcc.utoronto.ca/~cks/space/blog/unix/CloseEINTR
+ * https://sites.google.com/site/michaelsafyan/software-engineering/checkforeintrwheninvokingclosethinkagain
+ */
+ if (_unlikely_(r < 0 && errno == EINTR))
+ return 0;
+ else if (r >= 0)
+ return r;
+ else
+ return -errno;
}
void close_nointr_nofail(int fd) {
- int saved_errno = errno;
+ PROTECT_ERRNO;
/* like close_nointr() but cannot fail, and guarantees errno
* is unchanged */
assert_se(close_nointr(fd) == 0);
-
- errno = saved_errno;
}
void close_many(const int fds[], unsigned n_fd) {
unsigned i;
+ assert(fds || n_fd <= 0);
+
for (i = 0; i < n_fd; i++)
close_nointr_nofail(fds[i]);
}
+int unlink_noerrno(const char *path) {
+ PROTECT_ERRNO;
+ int r;
+
+ r = unlink(path);
+ if (r < 0)
+ return -errno;
+
+ return 0;
+}
+
int parse_boolean(const char *v) {
assert(v);
- if (streq(v, "1") || v[0] == 'y' || v[0] == 'Y' || v[0] == 't' || v[0] == 'T' || !strcasecmp(v, "on"))
+ if (streq(v, "1") || v[0] == 'y' || v[0] == 'Y' || v[0] == 't' || v[0] == 'T' || strcaseeq(v, "on"))
return 1;
- else if (streq(v, "0") || v[0] == 'n' || v[0] == 'N' || v[0] == 'f' || v[0] == 'F' || !strcasecmp(v, "off"))
+ else if (streq(v, "0") || v[0] == 'n' || v[0] == 'N' || v[0] == 'f' || v[0] == 'F' || strcaseeq(v, "off"))
return 0;
return -EINVAL;
@@ -285,7 +302,7 @@ int safe_atou(const char *s, unsigned *ret_u) {
l = strtoul(s, &x, 0);
if (!x || x == s || *x || errno)
- return errno ? -errno : -EINVAL;
+ return errno > 0 ? -errno : -EINVAL;
if ((unsigned long) (unsigned) l != l)
return -ERANGE;
@@ -305,7 +322,7 @@ int safe_atoi(const char *s, int *ret_i) {
l = strtol(s, &x, 0);
if (!x || x == s || *x || errno)
- return errno ? -errno : -EINVAL;
+ return errno > 0 ? -errno : -EINVAL;
if ((long) (int) l != l)
return -ERANGE;
@@ -348,6 +365,25 @@ int safe_atolli(const char *s, long long int *ret_lli) {
return 0;
}
+int safe_atod(const char *s, double *ret_d) {
+ char *x = NULL;
+ double d;
+
+ assert(s);
+ assert(ret_d);
+
+ RUN_WITH_LOCALE(LC_NUMERIC_MASK, "C") {
+ errno = 0;
+ d = strtod(s, &x);
+ }
+
+ if (!x || x == s || *x || errno)
+ return errno ? -errno : -EINVAL;
+
+ *ret_d = (double) d;
+ return 0;
+}
+
/* Split a string into words. */
char *split(const char *c, size_t *l, const char *separator, char **state) {
char *current;
@@ -424,22 +460,25 @@ char *split_quoted(const char *c, size_t *l, char **state) {
int get_parent_of_pid(pid_t pid, pid_t *_ppid) {
int r;
_cleanup_fclose_ FILE *f = NULL;
- char fn[PATH_MAX], line[LINE_MAX], *p;
+ char line[LINE_MAX];
long unsigned ppid;
+ const char *p;
- assert(pid > 0);
+ assert(pid >= 0);
assert(_ppid);
- assert_se(snprintf(fn, sizeof(fn)-1, "/proc/%lu/stat", (unsigned long) pid) < (int) (sizeof(fn)-1));
- char_array_0(fn);
+ if (pid == 0) {
+ *_ppid = getppid();
+ return 0;
+ }
- f = fopen(fn, "re");
+ p = procfs_file_alloca(pid, "stat");
+ f = fopen(p, "re");
if (!f)
return -errno;
if (!fgets(line, sizeof(line), f)) {
r = feof(f) ? -EIO : -errno;
- fclose(f);
return r;
}
@@ -469,15 +508,18 @@ int get_parent_of_pid(pid_t pid, pid_t *_ppid) {
int get_starttime_of_pid(pid_t pid, unsigned long long *st) {
_cleanup_fclose_ FILE *f = NULL;
- char fn[PATH_MAX], line[LINE_MAX], *p;
+ char line[LINE_MAX];
+ const char *p;
- assert(pid > 0);
+ assert(pid >= 0);
assert(st);
- assert_se(snprintf(fn, sizeof(fn)-1, "/proc/%lu/stat", (unsigned long) pid) < (int) (sizeof(fn)-1));
- char_array_0(fn);
+ if (pid == 0)
+ p = "/proc/self/stat";
+ else
+ p = procfs_file_alloca(pid, "stat");
- f = fopen(fn, "re");
+ f = fopen(p, "re");
if (!f)
return -errno;
@@ -525,31 +567,6 @@ int get_starttime_of_pid(pid_t pid, unsigned long long *st) {
return 0;
}
-int write_one_line_file(const char *fn, const char *line) {
- _cleanup_fclose_ FILE *f = NULL;
-
- assert(fn);
- assert(line);
-
- f = fopen(fn, "we");
- if (!f)
- return -errno;
-
- errno = 0;
- if (fputs(line, f) < 0)
- return errno ? -errno : -EIO;
-
- if (!endswith(line, "\n"))
- fputc('\n', f);
-
- fflush(f);
-
- if (ferror(f))
- return errno ? -errno : -EIO;
-
- return 0;
-}
-
int fchmod_umask(int fd, mode_t m) {
mode_t u;
int r;
@@ -561,413 +578,102 @@ int fchmod_umask(int fd, mode_t m) {
return r;
}
-int write_one_line_file_atomic(const char *fn, const char *line) {
- FILE *f;
- int r;
- char *p;
-
- assert(fn);
- assert(line);
-
- r = fopen_temporary(fn, &f, &p);
- if (r < 0)
- return r;
-
- fchmod_umask(fileno(f), 0644);
-
- errno = 0;
- if (fputs(line, f) < 0) {
- r = -errno;
- goto finish;
- }
-
- if (!endswith(line, "\n"))
- fputc('\n', f);
+char *truncate_nl(char *s) {
+ assert(s);
- fflush(f);
+ s[strcspn(s, NEWLINE)] = 0;
+ return s;
+}
- if (ferror(f)) {
- if (errno != 0)
- r = -errno;
- else
- r = -EIO;
- } else {
- if (rename(p, fn) < 0)
- r = -errno;
- else
- r = 0;
- }
+int get_process_comm(pid_t pid, char **name) {
+ const char *p;
-finish:
- if (r < 0)
- unlink(p);
+ assert(name);
+ assert(pid >= 0);
- fclose(f);
- free(p);
+ if (pid == 0)
+ p = "/proc/self/comm";
+ else
+ p = procfs_file_alloca(pid, "comm");
- return r;
+ return read_one_line_file(p, name);
}
-int read_one_line_file(const char *fn, char **line) {
+int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char **line) {
_cleanup_fclose_ FILE *f = NULL;
- char t[LINE_MAX], *c;
+ char *r = NULL, *k;
+ const char *p;
+ int c;
- assert(fn);
assert(line);
+ assert(pid >= 0);
- f = fopen(fn, "re");
- if (!f)
- return -errno;
-
- if (!fgets(t, sizeof(t), f)) {
-
- if (ferror(f))
- return errno ? -errno : -EIO;
-
- t[0] = 0;
- }
-
- c = strdup(t);
- if (!c)
- return -ENOMEM;
- truncate_nl(c);
-
- *line = c;
- return 0;
-}
-
-int read_full_file(const char *fn, char **contents, size_t *size) {
- _cleanup_fclose_ FILE *f = NULL;
- size_t n, l;
- _cleanup_free_ char *buf = NULL;
- struct stat st;
+ if (pid == 0)
+ p = "/proc/self/cmdline";
+ else
+ p = procfs_file_alloca(pid, "cmdline");
- f = fopen(fn, "re");
+ f = fopen(p, "re");
if (!f)
return -errno;
- if (fstat(fileno(f), &st) < 0)
- return -errno;
-
- /* Safety check */
- if (st.st_size > 4*1024*1024)
- return -E2BIG;
-
- n = st.st_size > 0 ? st.st_size : LINE_MAX;
- l = 0;
-
- for (;;) {
- char *t;
- size_t k;
-
- t = realloc(buf, n+1);
- if (!t)
- return -ENOMEM;
-
- buf = t;
- k = fread(buf + l, 1, n - l, f);
-
- if (k <= 0) {
- if (ferror(f))
- return -errno;
-
- break;
- }
-
- l += k;
- n *= 2;
-
- /* Safety check */
- if (n > 4*1024*1024)
- return -E2BIG;
- }
-
- buf[l] = 0;
- *contents = buf;
- buf = NULL;
-
- if (size)
- *size = l;
-
- return 0;
-}
-
-int parse_env_file(
- const char *fname,
- const char *separator, ...) {
-
- int r = 0;
- char *contents = NULL, *p;
-
- assert(fname);
- assert(separator);
-
- if ((r = read_full_file(fname, &contents, NULL)) < 0)
- return r;
-
- p = contents;
- for (;;) {
- const char *key = NULL;
-
- p += strspn(p, separator);
- p += strspn(p, WHITESPACE);
-
- if (!*p)
- break;
-
- if (!strchr(COMMENTS, *p)) {
- va_list ap;
- char **value;
-
- va_start(ap, separator);
- while ((key = va_arg(ap, char *))) {
- size_t n;
- char *v;
-
- value = va_arg(ap, char **);
-
- n = strlen(key);
- if (strncmp(p, key, n) != 0 ||
- p[n] != '=')
- continue;
-
- p += n + 1;
- n = strcspn(p, separator);
-
- if (n >= 2 &&
- strchr(QUOTES, p[0]) &&
- p[n-1] == p[0])
- v = strndup(p+1, n-2);
- else
- v = strndup(p, n);
-
- if (!v) {
- r = -ENOMEM;
- va_end(ap);
- goto fail;
- }
-
- if (v[0] == '\0') {
- /* return empty value strings as NULL */
- free(v);
- v = NULL;
- }
-
- free(*value);
- *value = v;
+ if (max_length == 0) {
+ size_t len = 0, allocated = 0;
- p += n;
+ while ((c = getc(f)) != EOF) {
- r ++;
- break;
+ if (!GREEDY_REALLOC(r, allocated, len+2)) {
+ free(r);
+ return -ENOMEM;
}
- va_end(ap);
- }
-
- if (!key)
- p += strcspn(p, separator);
- }
-
-fail:
- free(contents);
- return r;
-}
-
-int load_env_file(
- const char *fname,
- char ***rl) {
-
- FILE *f;
- char **m = NULL;
- int r;
-
- assert(fname);
- assert(rl);
-
- if (!(f = fopen(fname, "re")))
- return -errno;
-
- while (!feof(f)) {
- char l[LINE_MAX], *p, *u;
- char **t;
-
- if (!fgets(l, sizeof(l), f)) {
- if (feof(f))
- break;
-
- r = -errno;
- goto finish;
- }
-
- p = strstrip(l);
-
- if (!*p)
- continue;
-
- if (strchr(COMMENTS, *p))
- continue;
-
- if (!(u = normalize_env_assignment(p))) {
- r = log_oom();
- goto finish;
- }
-
- t = strv_append(m, u);
- free(u);
- if (!t) {
- r = log_oom();
- goto finish;
+ r[len++] = isprint(c) ? c : ' ';
}
- strv_free(m);
- m = t;
- }
-
- r = 0;
-
- *rl = m;
- m = NULL;
-
-finish:
- if (f)
- fclose(f);
-
- strv_free(m);
-
- return r;
-}
-
-int write_env_file(const char *fname, char **l) {
- char **i, *p;
- FILE *f;
- int r;
-
- r = fopen_temporary(fname, &f, &p);
- if (r < 0)
- return r;
-
- fchmod_umask(fileno(f), 0644);
-
- errno = 0;
- STRV_FOREACH(i, l) {
- fputs(*i, f);
- fputc('\n', f);
- }
-
- fflush(f);
+ if (len > 0)
+ r[len-1] = 0;
- if (ferror(f)) {
- if (errno != 0)
- r = -errno;
- else
- r = -EIO;
} else {
- if (rename(p, fname) < 0)
- r = -errno;
- else
- r = 0;
- }
-
- if (r < 0)
- unlink(p);
-
- fclose(f);
- free(p);
-
- return r;
-}
-
-char *truncate_nl(char *s) {
- assert(s);
-
- s[strcspn(s, NEWLINE)] = 0;
- return s;
-}
-
-int get_process_comm(pid_t pid, char **name) {
- int r;
+ bool space = false;
+ size_t left;
- assert(name);
-
- if (pid == 0)
- r = read_one_line_file("/proc/self/comm", name);
- else {
- char *p;
- if (asprintf(&p, "/proc/%lu/comm", (unsigned long) pid) < 0)
- return -ENOMEM;
-
- r = read_one_line_file(p, name);
- free(p);
- }
-
- return r;
-}
-
-int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char **line) {
- char *r, *k;
- int c;
- bool space = false;
- size_t left;
- FILE *f;
-
- assert(max_length > 0);
- assert(line);
-
- if (pid == 0)
- f = fopen("/proc/self/cmdline", "re");
- else {
- char *p;
- if (asprintf(&p, "/proc/%lu/cmdline", (unsigned long) pid) < 0)
+ r = new(char, max_length);
+ if (!r)
return -ENOMEM;
- f = fopen(p, "re");
- free(p);
- }
-
- if (!f)
- return -errno;
+ k = r;
+ left = max_length;
+ while ((c = getc(f)) != EOF) {
- r = new(char, max_length);
- if (!r) {
- fclose(f);
- return -ENOMEM;
- }
+ if (isprint(c)) {
+ if (space) {
+ if (left <= 4)
+ break;
- k = r;
- left = max_length;
- while ((c = getc(f)) != EOF) {
+ *(k++) = ' ';
+ left--;
+ space = false;
+ }
- if (isprint(c)) {
- if (space) {
if (left <= 4)
break;
- *(k++) = ' ';
+ *(k++) = (char) c;
left--;
- space = false;
- }
-
- if (left <= 4)
- break;
+ } else
+ space = true;
+ }
- *(k++) = (char) c;
- left--;
- } else
- space = true;
+ if (left <= 4) {
+ size_t n = MIN(left-1, 3U);
+ memcpy(k, "...", n);
+ k[n] = 0;
+ } else
+ *k = 0;
}
- if (left <= 4) {
- size_t n = MIN(left-1, 3U);
- memcpy(k, "...", n);
- k[n] = 0;
- } else
- *k = 0;
-
- fclose(f);
-
/* Kernel threads have no argv[] */
- if (r[0] == 0) {
+ if (r == NULL || r[0] == 0) {
char *t;
int h;
@@ -992,7 +698,7 @@ int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char *
}
int is_kernel_thread(pid_t pid) {
- char *p;
+ const char *p;
size_t count;
char c;
bool eof;
@@ -1001,12 +707,10 @@ int is_kernel_thread(pid_t pid) {
if (pid == 0)
return 0;
- if (asprintf(&p, "/proc/%lu/cmdline", (unsigned long) pid) < 0)
- return -ENOMEM;
+ assert(pid > 0);
+ p = procfs_file_alloca(pid, "cmdline");
f = fopen(p, "re");
- free(p);
-
if (!f)
return -errno;
@@ -1022,54 +726,39 @@ int is_kernel_thread(pid_t pid) {
return 0;
}
+
int get_process_exe(pid_t pid, char **name) {
- int r;
+ const char *p;
+ assert(pid >= 0);
assert(name);
if (pid == 0)
- r = readlink_malloc("/proc/self/exe", name);
- else {
- char *p;
- if (asprintf(&p, "/proc/%lu/exe", (unsigned long) pid) < 0)
- return -ENOMEM;
-
- r = readlink_malloc(p, name);
- free(p);
- }
+ p = "/proc/self/exe";
+ else
+ p = procfs_file_alloca(pid, "exe");
- return r;
+ return readlink_malloc(p, name);
}
static int get_process_id(pid_t pid, const char *field, uid_t *uid) {
- char *p;
- FILE *f;
- int r;
+ _cleanup_fclose_ FILE *f = NULL;
+ char line[LINE_MAX];
+ const char *p;
+ assert(field);
assert(uid);
if (pid == 0)
return getuid();
- if (asprintf(&p, "/proc/%lu/status", (unsigned long) pid) < 0)
- return -ENOMEM;
-
+ p = procfs_file_alloca(pid, "status");
f = fopen(p, "re");
- free(p);
-
if (!f)
return -errno;
- while (!feof(f)) {
- char line[LINE_MAX], *l;
-
- if (!fgets(line, sizeof(line), f)) {
- if (feof(f))
- break;
-
- r = -errno;
- goto finish;
- }
+ FOREACH_LINE(line, f, return -errno) {
+ char *l;
l = strstrip(line);
@@ -1079,17 +768,11 @@ static int get_process_id(pid_t pid, const char *field, uid_t *uid) {
l[strcspn(l, WHITESPACE)] = 0;
- r = parse_uid(l, uid);
- goto finish;
+ return parse_uid(l, uid);
}
}
- r = -EIO;
-
-finish:
- fclose(f);
-
- return r;
+ return -EIO;
}
int get_process_uid(pid_t pid, uid_t *uid) {
@@ -1097,6 +780,7 @@ int get_process_uid(pid_t pid, uid_t *uid) {
}
int get_process_gid(pid_t pid, gid_t *gid) {
+ assert_cc(sizeof(uid_t) == sizeof(gid_t));
return get_process_id(pid, "Gid:", gid);
}
@@ -1212,15 +896,14 @@ int reset_all_signal_handlers(void) {
int sig;
for (sig = 1; sig < _NSIG; sig++) {
- struct sigaction sa;
+ struct sigaction sa = {
+ .sa_handler = SIG_DFL,
+ .sa_flags = SA_RESTART,
+ };
if (sig == SIGKILL || sig == SIGSTOP)
continue;
- zero(sa);
- sa.sa_handler = SIG_DFL;
- sa.sa_flags = SA_RESTART;
-
/* On Linux the first two RT signals are reserved by
* glibc, and sigaction() will return EINVAL for them. */
if ((sigaction(sig, &sa, NULL) < 0))
@@ -1351,7 +1034,6 @@ int rmdir_parents(const char *path, const char *stop) {
return 0;
}
-
char hexchar(int x) {
static const char table[16] = "0123456789abcdef";
@@ -1372,6 +1054,49 @@ int unhexchar(char c) {
return -1;
}
+char *hexmem(const void *p, size_t l) {
+ char *r, *z;
+ const uint8_t *x;
+
+ z = r = malloc(l * 2 + 1);
+ if (!r)
+ return NULL;
+
+ for (x = p; x < (const uint8_t*) p + l; x++) {
+ *(z++) = hexchar(*x >> 4);
+ *(z++) = hexchar(*x & 15);
+ }
+
+ *z = 0;
+ return r;
+}
+
+void *unhexmem(const char *p, size_t l) {
+ uint8_t *r, *z;
+ const char *x;
+
+ assert(p);
+
+ z = r = malloc((l + 1) / 2 + 1);
+ if (!r)
+ return NULL;
+
+ for (x = p; x < p + l; x += 2) {
+ int a, b;
+
+ a = unhexchar(x[0]);
+ if (x+1 < p + l)
+ b = unhexchar(x[1]);
+ else
+ b = 0;
+
+ *(z++) = (uint8_t) a << 4 | (uint8_t) b;
+ }
+
+ *z = 0;
+ return r;
+}
+
char octchar(int x) {
return '0' + (x & 7);
}
@@ -1647,16 +1372,24 @@ char *bus_path_escape(const char *s) {
assert(s);
/* Escapes all chars that D-Bus' object path cannot deal
- * with. Can be reverse with bus_path_unescape() */
+ * with. Can be reverse with bus_path_unescape(). We special
+ * case the empty string. */
- if (!(r = new(char, strlen(s)*3+1)))
+ if (*s == 0)
+ return strdup("_");
+
+ r = new(char, strlen(s)*3 + 1);
+ if (!r)
return NULL;
for (f = s, t = r; *f; f++) {
+ /* Escape everything that is not a-zA-Z0-9. We also
+ * escape 0-9 if it's the first character */
+
if (!(*f >= 'A' && *f <= 'Z') &&
!(*f >= 'a' && *f <= 'z') &&
- !(*f >= '0' && *f <= '9')) {
+ !(f > s && *f >= '0' && *f <= '9')) {
*(t++) = '_';
*(t++) = hexchar(*f >> 4);
*(t++) = hexchar(*f);
@@ -1674,7 +1407,12 @@ char *bus_path_unescape(const char *f) {
assert(f);
- if (!(r = strdup(f)))
+ /* Special case for the empty string */
+ if (streq(f, "_"))
+ return strdup("");
+
+ r = new(char, strlen(f) + 1);
+ if (!r)
return NULL;
for (t = r; *f; f++) {
@@ -1711,7 +1449,7 @@ char *ascii_strlower(char *t) {
return t;
}
-static bool ignore_file_allow_backup(const char *filename) {
+_pure_ static bool ignore_file_allow_backup(const char *filename) {
assert(filename);
return
@@ -1774,7 +1512,7 @@ int fd_cloexec(int fd, bool cloexec) {
return 0;
}
-static bool fd_in_set(int fd, const int fdset[], unsigned n_fdset) {
+_pure_ static bool fd_in_set(int fd, const int fdset[], unsigned n_fdset) {
unsigned i;
assert(n_fdset == 0 || fdset);
@@ -2126,29 +1864,28 @@ int open_terminal(const char *name, int mode) {
}
int flush_fd(int fd) {
- struct pollfd pollfd;
-
- zero(pollfd);
- pollfd.fd = fd;
- pollfd.events = POLLIN;
+ struct pollfd pollfd = {
+ .fd = fd,
+ .events = POLLIN,
+ };
for (;;) {
char buf[LINE_MAX];
ssize_t l;
int r;
- if ((r = poll(&pollfd, 1, 0)) < 0) {
-
+ r = poll(&pollfd, 1, 0);
+ if (r < 0) {
if (errno == EINTR)
continue;
return -errno;
- }
- if (r == 0)
+ } else if (r == 0)
return 0;
- if ((l = read(fd, buf, sizeof(buf))) < 0) {
+ l = read(fd, buf, sizeof(buf));
+ if (l < 0) {
if (errno == EINTR)
continue;
@@ -2157,9 +1894,7 @@ int flush_fd(int fd) {
return 0;
return -errno;
- }
-
- if (l <= 0)
+ } else if (l == 0)
return 0;
}
}
@@ -2173,7 +1908,6 @@ int acquire_terminal(
int fd = -1, notify = -1, r = 0, wd = -1;
usec_t ts = 0;
- struct sigaction sa_old, sa_new;
assert(name);
@@ -2208,6 +1942,11 @@ int acquire_terminal(
}
for (;;) {
+ struct sigaction sa_old, sa_new = {
+ .sa_handler = SIG_IGN,
+ .sa_flags = SA_RESTART,
+ };
+
if (notify >= 0) {
r = flush_fd(notify);
if (r < 0)
@@ -2223,9 +1962,6 @@ int acquire_terminal(
/* Temporarily ignore SIGHUP, so that we don't get SIGHUP'ed
* if we already own the tty. */
- zero(sa_new);
- sa_new.sa_handler = SIG_IGN;
- sa_new.sa_flags = SA_RESTART;
assert_se(sigaction(SIGHUP, &sa_new, &sa_old) == 0);
/* First, try to get the tty */
@@ -2332,18 +2068,19 @@ fail:
}
int release_terminal(void) {
- int r = 0, fd;
- struct sigaction sa_old, sa_new;
+ int r = 0;
+ struct sigaction sa_old, sa_new = {
+ .sa_handler = SIG_IGN,
+ .sa_flags = SA_RESTART,
+ };
+ _cleanup_close_ int fd;
- if ((fd = open("/dev/tty", O_RDWR|O_NOCTTY|O_NDELAY|O_CLOEXEC)) < 0)
+ fd = open("/dev/tty", O_RDWR|O_NOCTTY|O_NDELAY|O_CLOEXEC);
+ if (fd < 0)
return -errno;
/* Temporarily ignore SIGHUP, so that we don't get SIGHUP'ed
* by our own TIOCNOTTY */
-
- zero(sa_new);
- sa_new.sa_handler = SIG_IGN;
- sa_new.sa_flags = SA_RESTART;
assert_se(sigaction(SIGHUP, &sa_new, &sa_old) == 0);
if (ioctl(fd, TIOCNOTTY) < 0)
@@ -2351,7 +2088,6 @@ int release_terminal(void) {
assert_se(sigaction(SIGHUP, &sa_old, NULL) == 0);
- close_nointr_nofail(fd);
return r;
}
@@ -2369,13 +2105,13 @@ int sigaction_many(const struct sigaction *sa, ...) {
}
int ignore_signals(int sig, ...) {
- struct sigaction sa;
+ struct sigaction sa = {
+ .sa_handler = SIG_IGN,
+ .sa_flags = SA_RESTART,
+ };
va_list ap;
int r = 0;
- zero(sa);
- sa.sa_handler = SIG_IGN;
- sa.sa_flags = SA_RESTART;
if (sigaction(sig, &sa, NULL) < 0)
r = -errno;
@@ -2390,14 +2126,13 @@ int ignore_signals(int sig, ...) {
}
int default_signals(int sig, ...) {
- struct sigaction sa;
+ struct sigaction sa = {
+ .sa_handler = SIG_DFL,
+ .sa_flags = SA_RESTART,
+ };
va_list ap;
int r = 0;
- zero(sa);
- sa.sa_handler = SIG_DFL;
- sa.sa_flags = SA_RESTART;
-
if (sigaction(sig, &sa, NULL) < 0)
r = -errno;
@@ -2446,11 +2181,10 @@ ssize_t loop_read(int fd, void *buf, size_t nbytes, bool do_poll) {
continue;
if (k < 0 && errno == EAGAIN && do_poll) {
- struct pollfd pollfd;
-
- zero(pollfd);
- pollfd.fd = fd;
- pollfd.events = POLLIN;
+ struct pollfd pollfd = {
+ .fd = fd,
+ .events = POLLIN,
+ };
if (poll(&pollfd, 1, -1) < 0) {
if (errno == EINTR)
@@ -2495,11 +2229,10 @@ ssize_t loop_write(int fd, const void *buf, size_t nbytes, bool do_poll) {
continue;
if (k < 0 && errno == EAGAIN && do_poll) {
- struct pollfd pollfd;
-
- zero(pollfd);
- pollfd.fd = fd;
- pollfd.events = POLLOUT;
+ struct pollfd pollfd = {
+ .fd = fd,
+ .events = POLLOUT,
+ };
if (poll(&pollfd, 1, -1) < 0) {
if (errno == EINTR)
@@ -2555,7 +2288,7 @@ int parse_bytes(const char *t, off_t *bytes) {
errno = 0;
l = strtoll(p, &e, 10);
- if (errno != 0)
+ if (errno > 0)
return -errno;
if (l < 0)
@@ -2647,6 +2380,24 @@ int dir_is_empty(const char *path) {
}
}
+char* dirname_malloc(const char *path) {
+ char *d, *dir, *dir2;
+
+ d = strdup(path);
+ if (!d)
+ return NULL;
+ dir = dirname(d);
+ assert(dir);
+
+ if (dir != d) {
+ dir2 = strdup(dir);
+ free(d);
+ return dir2;
+ }
+
+ return dir;
+}
+
unsigned long long random_ull(void) {
_cleanup_close_ int fd;
uint64_t ull;
@@ -2815,27 +2566,29 @@ int getttyname_harder(int fd, char **r) {
}
int get_ctty_devnr(pid_t pid, dev_t *d) {
- int k;
- char line[LINE_MAX], *p, *fn;
+ _cleanup_fclose_ FILE *f = NULL;
+ char line[LINE_MAX], *p;
unsigned long ttynr;
- FILE *f;
+ const char *fn;
+ int k;
- if (asprintf(&fn, "/proc/%lu/stat", (unsigned long) (pid <= 0 ? getpid() : pid)) < 0)
- return -ENOMEM;
+ assert(pid >= 0);
+ assert(d);
+
+ if (pid == 0)
+ fn = "/proc/self/stat";
+ else
+ fn = procfs_file_alloca(pid, "stat");
f = fopen(fn, "re");
- free(fn);
if (!f)
return -errno;
if (!fgets(line, sizeof(line), f)) {
k = feof(f) ? -EIO : -errno;
- fclose(f);
return k;
}
- fclose(f);
-
p = strrchr(line, ')');
if (!p)
return -EIO;
@@ -2860,7 +2613,7 @@ int get_ctty_devnr(pid_t pid, dev_t *d) {
int get_ctty(pid_t pid, dev_t *_devnr, char **r) {
int k;
- char fn[PATH_MAX], *s, *b, *p;
+ char fn[sizeof("/dev/char/")-1 + 2*DECIMAL_STR_MAX(unsigned) + 1 + 1], *s, *b, *p;
dev_t devnr;
assert(r);
@@ -2870,7 +2623,6 @@ int get_ctty(pid_t pid, dev_t *_devnr, char **r) {
return k;
snprintf(fn, sizeof(fn), "/dev/char/%u:%u", major(devnr), minor(devnr));
- char_array_0(fn);
k = readlink_malloc(fn, &s);
if (k < 0) {
@@ -3018,10 +2770,11 @@ int rm_rf_children_dangerous(int fd, bool only_dirs, bool honour_sticky, struct
return ret;
}
-static int is_temporary_fs(struct statfs *s) {
+_pure_ static int is_temporary_fs(struct statfs *s) {
assert(s);
- return s->f_type == TMPFS_MAGIC ||
- (long)s->f_type == (long)RAMFS_MAGIC;
+ return
+ F_TYPE_CMP(s->f_type, TMPFS_MAGIC) ||
+ F_TYPE_CMP(s->f_type, RAMFS_MAGIC);
}
int rm_rf_children(int fd, bool only_dirs, bool honour_sticky, struct stat *root_dev) {
@@ -3181,12 +2934,13 @@ cpu_set_t* cpu_set_malloc(unsigned *ncpus) {
}
}
-int status_vprintf(const char *status, bool ellipse, const char *format, va_list ap) {
+int status_vprintf(const char *status, bool ellipse, bool ephemeral, const char *format, va_list ap) {
static const char status_indent[] = " "; /* "[" STATUS "] " */
_cleanup_free_ char *s = NULL;
_cleanup_close_ int fd = -1;
- struct iovec iovec[5];
+ struct iovec iovec[6] = {};
int n = 0;
+ static bool prev_ephemeral;
assert(format);
@@ -3222,7 +2976,9 @@ int status_vprintf(const char *status, bool ellipse, const char *format, va_list
}
}
- zero(iovec);
+ if (prev_ephemeral)
+ IOVEC_SET_STRING(iovec[n++], "\r" ANSI_ERASE_TO_END_OF_LINE);
+ prev_ephemeral = ephemeral;
if (status) {
if (!isempty(status)) {
@@ -3234,7 +2990,8 @@ int status_vprintf(const char *status, bool ellipse, const char *format, va_list
}
IOVEC_SET_STRING(iovec[n++], s);
- IOVEC_SET_STRING(iovec[n++], "\n");
+ if (!ephemeral)
+ IOVEC_SET_STRING(iovec[n++], "\n");
if (writev(fd, iovec, n) < 0)
return -errno;
@@ -3242,14 +2999,14 @@ int status_vprintf(const char *status, bool ellipse, const char *format, va_list
return 0;
}
-int status_printf(const char *status, bool ellipse, const char *format, ...) {
+int status_printf(const char *status, bool ellipse, bool ephemeral, const char *format, ...) {
va_list ap;
int r;
assert(format);
va_start(ap, format);
- r = status_vprintf(status, ellipse, format, ap);
+ r = status_vprintf(status, ellipse, ephemeral, format, ap);
va_end(ap);
return r;
@@ -3266,7 +3023,7 @@ int status_welcome(void) {
if (r < 0 && r != -ENOENT)
log_warning("Failed to read /etc/os-release: %s", strerror(-r));
- return status_printf(NULL, false,
+ return status_printf(NULL, false, false,
"\nWelcome to \x1B[%sm%s\x1B[0m!\n",
isempty(ansi_color) ? "1" : ansi_color,
isempty(pretty_name) ? "Linux" : pretty_name);
@@ -3321,10 +3078,10 @@ char *replace_env(const char *format, char **env) {
if (*e == '}') {
const char *t;
- if (!(t = strv_env_get_with_length(env, word+2, e-word-2)))
- t = "";
+ t = strempty(strv_env_get_n(env, word+2, e-word-2));
- if (!(k = strappend(r, t)))
+ k = strappend(r, t);
+ if (!k)
goto fail;
free(r);
@@ -3365,7 +3122,8 @@ char **replace_env_argv(char **argv, char **env) {
char **w, **m;
unsigned q;
- if ((e = strv_env_get(env, *i+1))) {
+ e = strv_env_get(env, *i+1);
+ if (e) {
if (!(m = strv_split_quoted(e))) {
r[k] = NULL;
@@ -3407,8 +3165,7 @@ char **replace_env_argv(char **argv, char **env) {
}
int fd_columns(int fd) {
- struct winsize ws;
- zero(ws);
+ struct winsize ws = {};
if (ioctl(fd, TIOCGWINSZ, &ws) < 0)
return -errno;
@@ -3442,8 +3199,7 @@ unsigned columns(void) {
}
int fd_lines(int fd) {
- struct winsize ws;
- zero(ws);
+ struct winsize ws = {};
if (ioctl(fd, TIOCGWINSZ, &ws) < 0)
return -errno;
@@ -3492,13 +3248,9 @@ bool on_tty(void) {
}
int running_in_chroot(void) {
- struct stat a, b;
-
- zero(a);
- zero(b);
+ struct stat a = {}, b = {};
/* Only works as root */
-
if (stat("/proc/1/root", &a) < 0)
return -errno;
@@ -3824,6 +3576,29 @@ int vtnr_from_tty(const char *tty) {
return i;
}
+char *resolve_dev_console(char **active) {
+ char *tty;
+
+ /* Resolve where /dev/console is pointing to, if /sys is actually ours
+ * (i.e. not read-only-mounted which is a sign for container setups) */
+
+ if (path_is_read_only_fs("/sys") > 0)
+ return NULL;
+
+ if (read_one_line_file("/sys/class/tty/console/active", active) < 0)
+ return NULL;
+
+ /* If multiple log outputs are configured the last one is what
+ * /dev/console points to */
+ tty = strrchr(*active, ' ');
+ if (tty)
+ tty++;
+ else
+ tty = *active;
+
+ return tty;
+}
+
bool tty_is_vc_resolve(const char *tty) {
char *active = NULL;
bool b;
@@ -3833,19 +3608,11 @@ bool tty_is_vc_resolve(const char *tty) {
if (startswith(tty, "/dev/"))
tty += 5;
- /* Resolve where /dev/console is pointing to, if /sys is
- * actually ours (i.e. not read-only-mounted which is a sign
- * for container setups) */
- if (streq(tty, "console") && path_is_read_only_fs("/sys") <= 0)
- if (read_one_line_file("/sys/class/tty/console/active", &active) >= 0) {
- /* If multiple log outputs are configured the
- * last one is what /dev/console points to */
- tty = strrchr(active, ' ');
- if (tty)
- tty++;
- else
- tty = active;
- }
+ if (streq(tty, "console")) {
+ tty = resolve_dev_console(&active);
+ if (!tty)
+ return false;
+ }
b = tty_is_vc(tty);
free(active);
@@ -3894,8 +3661,8 @@ void execute_directory(const char *directory, DIR *d, char *argv[]) {
assert(directory);
- /* Executes all binaries in a directory in parallel and waits
- * until all they all finished. */
+ /* Executes all binaries in a directory in parallel and
+ * waits for them to finish. */
if (!d) {
if (!(_d = opendir(directory))) {
@@ -3961,10 +3728,9 @@ void execute_directory(const char *directory, DIR *d, char *argv[]) {
while (!hashmap_isempty(pids)) {
pid_t pid = PTR_TO_UINT(hashmap_first_key(pids));
- siginfo_t si;
+ siginfo_t si = {};
char *path;
- zero(si);
if (waitid(P_PID, pid, &si, WEXITED) < 0) {
if (errno == EINTR)
@@ -4044,13 +3810,27 @@ static bool hostname_valid_char(char c) {
bool hostname_is_valid(const char *s) {
const char *p;
+ bool dot;
if (isempty(s))
return false;
- for (p = s; *p; p++)
- if (!hostname_valid_char(*p))
- return false;
+ for (p = s, dot = true; *p; p++) {
+ if (*p == '.') {
+ if (dot)
+ return false;
+
+ dot = true;
+ } else {
+ if (!hostname_valid_char(*p))
+ return false;
+
+ dot = false;
+ }
+ }
+
+ if (dot)
+ return false;
if (p-s > HOST_NAME_MAX)
return false;
@@ -4058,31 +3838,40 @@ bool hostname_is_valid(const char *s) {
return true;
}
-char* hostname_cleanup(char *s) {
+char* hostname_cleanup(char *s, bool lowercase) {
char *p, *d;
+ bool dot;
+
+ for (p = s, d = s, dot = true; *p; p++) {
+ if (*p == '.') {
+ if (dot)
+ continue;
+
+ *(d++) = '.';
+ dot = true;
+ } else if (hostname_valid_char(*p)) {
+ *(d++) = lowercase ? tolower(*p) : *p;
+ dot = false;
+ }
- for (p = s, d = s; *p; p++)
- if ((*p >= 'a' && *p <= 'z') ||
- (*p >= 'A' && *p <= 'Z') ||
- (*p >= '0' && *p <= '9') ||
- *p == '-' ||
- *p == '_' ||
- *p == '.')
- *(d++) = *p;
+ }
- *d = 0;
+ if (dot && d > s)
+ d[-1] = 0;
+ else
+ *d = 0;
strshorten(s, HOST_NAME_MAX);
+
return s;
}
int pipe_eof(int fd) {
- struct pollfd pollfd;
int r;
-
- zero(pollfd);
- pollfd.fd = fd;
- pollfd.events = POLLIN|POLLHUP;
+ struct pollfd pollfd = {
+ .fd = fd,
+ .events = POLLIN|POLLHUP,
+ };
r = poll(&pollfd, 1, 0);
if (r < 0)
@@ -4095,12 +3884,11 @@ int pipe_eof(int fd) {
}
int fd_wait_for_event(int fd, int event, usec_t t) {
- struct pollfd pollfd;
int r;
-
- zero(pollfd);
- pollfd.fd = fd;
- pollfd.events = event;
+ struct pollfd pollfd = {
+ .fd = fd,
+ .events = event,
+ };
r = poll(&pollfd, 1, t == (usec_t) -1 ? -1 : (int) (t / USEC_PER_MSEC));
if (r < 0)
@@ -4427,7 +4215,7 @@ int get_user_creds(
}
if (!p)
- return errno != 0 ? -errno : -ESRCH;
+ return errno > 0 ? -errno : -ESRCH;
if (uid)
*uid = p->pw_uid;
@@ -4444,6 +4232,40 @@ int get_user_creds(
return 0;
}
+char* uid_to_name(uid_t uid) {
+ struct passwd *p;
+ char *r;
+
+ if (uid == 0)
+ return strdup("root");
+
+ p = getpwuid(uid);
+ if (p)
+ return strdup(p->pw_name);
+
+ if (asprintf(&r, "%lu", (unsigned long) uid) < 0)
+ return NULL;
+
+ return r;
+}
+
+char* gid_to_name(gid_t gid) {
+ struct group *p;
+ char *r;
+
+ if (gid == 0)
+ return strdup("root");
+
+ p = getgrgid(gid);
+ if (p)
+ return strdup(p->gr_name);
+
+ if (asprintf(&r, "%lu", (unsigned long) gid) < 0)
+ return NULL;
+
+ return r;
+}
+
int get_group_creds(const char **groupname, gid_t *gid) {
struct group *g;
gid_t id;
@@ -4474,7 +4296,7 @@ int get_group_creds(const char **groupname, gid_t *gid) {
}
if (!g)
- return errno != 0 ? -errno : -ESRCH;
+ return errno > 0 ? -errno : -ESRCH;
if (gid)
*gid = g->gr_gid;
@@ -4482,14 +4304,10 @@ int get_group_creds(const char **groupname, gid_t *gid) {
return 0;
}
-int in_group(const char *name) {
- gid_t gid, *gids;
+int in_gid(gid_t gid) {
+ gid_t *gids;
int ngroups_max, r, i;
- r = get_group_creds(&name, &gid);
- if (r < 0)
- return r;
-
if (getgid() == gid)
return 1;
@@ -4512,13 +4330,23 @@ int in_group(const char *name) {
return 0;
}
+int in_group(const char *name) {
+ int r;
+ gid_t gid;
+
+ r = get_group_creds(&name, &gid);
+ if (r < 0)
+ return r;
+
+ return in_gid(gid);
+}
+
int glob_exists(const char *path) {
- glob_t g;
+ _cleanup_globfree_ glob_t g = {};
int r, k;
assert(path);
- zero(g);
errno = 0;
k = glob(path, GLOB_NOSORT|GLOB_BRACE, NULL, &g);
@@ -4531,8 +4359,6 @@ int glob_exists(const char *path) {
else
r = errno ? -errno : -EIO;
- globfree(&g);
-
return r;
}
@@ -4934,7 +4760,7 @@ static const char *const __signal_table[] = {
DEFINE_PRIVATE_STRING_TABLE_LOOKUP(__signal, int);
const char *signal_to_string(int signo) {
- static __thread char buf[12];
+ static __thread char buf[sizeof("RTMIN+")-1 + DECIMAL_STR_MAX(int) + 1];
const char *name;
name = __signal_to_string(signo);
@@ -4942,10 +4768,10 @@ const char *signal_to_string(int signo) {
return name;
if (signo >= SIGRTMIN && signo <= SIGRTMAX)
- snprintf(buf, sizeof(buf) - 1, "RTMIN+%d", signo - SIGRTMIN);
+ snprintf(buf, sizeof(buf), "RTMIN+%d", signo - SIGRTMIN);
else
- snprintf(buf, sizeof(buf) - 1, "%d", signo);
- char_array_0(buf);
+ snprintf(buf, sizeof(buf), "%d", signo);
+
return buf;
}
@@ -5213,20 +5039,21 @@ int setrlimit_closest(int resource, const struct rlimit *rlim) {
}
int getenv_for_pid(pid_t pid, const char *field, char **_value) {
- char path[sizeof("/proc/")-1+10+sizeof("/environ")], *value = NULL;
+ _cleanup_fclose_ FILE *f = NULL;
+ char *value = NULL;
int r;
- FILE *f;
bool done = false;
size_t l;
+ const char *path;
+ assert(pid >= 0);
assert(field);
assert(_value);
if (pid == 0)
- pid = getpid();
-
- snprintf(path, sizeof(path), "/proc/%lu/environ", (unsigned long) pid);
- char_array_0(path);
+ path = "/proc/self/environ";
+ else
+ path = procfs_file_alloca(pid, "environ");
f = fopen(path, "re");
if (!f)
@@ -5255,10 +5082,8 @@ int getenv_for_pid(pid_t pid, const char *field, char **_value) {
if (memcmp(line, field, l) == 0 && line[l] == '=') {
value = strdup(line + l + 1);
- if (!value) {
- r = -ENOMEM;
- break;
- }
+ if (!value)
+ return -ENOMEM;
r = 1;
break;
@@ -5266,67 +5091,10 @@ int getenv_for_pid(pid_t pid, const char *field, char **_value) {
} while (!done);
- fclose(f);
-
- if (r >= 0)
- *_value = value;
-
+ *_value = value;
return r;
}
-int can_sleep(const char *type) {
- char *w, *state;
- size_t l, k;
- int r;
- _cleanup_free_ char *p = NULL;
-
- assert(type);
-
- /* If /sys is read-only we cannot sleep */
- if (access("/sys/power/state", W_OK) < 0)
- return false;
-
- r = read_one_line_file("/sys/power/state", &p);
- if (r < 0)
- return false;
-
- k = strlen(type);
- FOREACH_WORD_SEPARATOR(w, l, p, WHITESPACE, state)
- if (l == k && memcmp(w, type, l) == 0)
- return true;
-
- return false;
-}
-
-int can_sleep_disk(const char *type) {
- char *w, *state;
- size_t l, k;
- int r;
- _cleanup_free_ char *p = NULL;
-
- assert(type);
-
- /* If /sys is read-only we cannot sleep */
- if (access("/sys/power/state", W_OK) < 0 ||
- access("/sys/power/disk", W_OK) < 0)
- return false;
-
- r = read_one_line_file("/sys/power/disk", &p);
- if (r < 0)
- return false;
-
- k = strlen(type);
- FOREACH_WORD_SEPARATOR(w, l, p, WHITESPACE, state) {
- if (l == k && memcmp(w, type, l) == 0)
- return true;
-
- if (l == k + 2 && w[0] == '[' && memcmp(w + 1, type, l - 2) == 0 && w[l-1] == ']')
- return true;
- }
-
- return false;
-}
-
bool is_valid_documentation_url(const char *url) {
assert(url);
@@ -5447,7 +5215,7 @@ int get_home_dir(char **_h) {
errno = 0;
p = getpwuid(u);
if (!p)
- return errno ? -errno : -ESRCH;
+ return errno > 0 ? -errno : -ESRCH;
if (!path_is_absolute(p->pw_dir))
return -EINVAL;
@@ -5460,76 +5228,6 @@ int get_home_dir(char **_h) {
return 0;
}
-int get_shell(char **_sh) {
- char *sh;
- const char *e;
- uid_t u;
- struct passwd *p;
-
- assert(_sh);
-
- /* Take the user specified one */
- e = getenv("SHELL");
- if (e) {
- sh = strdup(e);
- if (!sh)
- return -ENOMEM;
-
- *_sh = sh;
- return 0;
- }
-
- /* Hardcode home directory for root to avoid NSS */
- u = getuid();
- if (u == 0) {
- sh = strdup("/bin/sh");
- if (!sh)
- return -ENOMEM;
-
- *_sh = sh;
- return 0;
- }
-
- /* Check the database... */
- errno = 0;
- p = getpwuid(u);
- if (!p)
- return errno ? -errno : -ESRCH;
-
- if (!path_is_absolute(p->pw_shell))
- return -EINVAL;
-
- sh = strdup(p->pw_shell);
- if (!sh)
- return -ENOMEM;
-
- *_sh = sh;
- return 0;
-}
-
-void freep(void *p) {
- free(*(void**) p);
-}
-
-void fclosep(FILE **f) {
- if (*f)
- fclose(*f);
-}
-
-void closep(int *fd) {
- if (*fd >= 0)
- close_nointr_nofail(*fd);
-}
-
-void closedirp(DIR **d) {
- if (*d)
- closedir(*d);
-}
-
-void umaskp(mode_t *u) {
- umask(*u);
-}
-
bool filename_is_safe(const char *p) {
if (isempty(p))
@@ -5566,6 +5264,39 @@ bool string_is_safe(const char *p) {
return true;
}
+bool string_has_cc(const char *p) {
+ const char *t;
+
+ assert(p);
+
+ for (t = p; *t; t++)
+ if (*t > 0 && *t < ' ')
+ return true;
+
+ return false;
+}
+
+bool path_is_safe(const char *p) {
+
+ if (isempty(p))
+ return false;
+
+ if (streq(p, "..") || startswith(p, "../") || endswith(p, "/..") || strstr(p, "/../"))
+ return false;
+
+ if (strlen(p) > PATH_MAX)
+ return false;
+
+ /* The following two checks are not really dangerous, but hey, they still are confusing */
+ if (streq(p, ".") || startswith(p, "./") || endswith(p, "/.") || strstr(p, "/./"))
+ return false;
+
+ if (strstr(p, "//"))
+ return false;
+
+ return true;
+}
+
/* hey glibc, APIs with callbacks without a user pointer are so useless */
void *xbsearch_r(const void *key, const void *base, size_t nmemb, size_t size,
int (*compar) (const void *, const void *, void *), void *arg) {
@@ -5607,7 +5338,23 @@ bool is_locale_utf8(void) {
goto out;
}
- cached_answer = streq(set, "UTF-8");
+ if(streq(set, "UTF-8")) {
+ cached_answer = true;
+ goto out;
+ }
+
+ /* For LC_CTYPE=="C" return true,
+ * because CTYPE is effectly unset and
+ * everything defaults to UTF-8 nowadays. */
+
+ set = setlocale(LC_CTYPE, NULL);
+ if (!set) {
+ cached_answer = true;
+ goto out;
+ }
+
+ cached_answer = streq(set, "C");
+
out:
return (bool)cached_answer;
}
@@ -5618,12 +5365,14 @@ const char *draw_special_char(DrawSpecialChar ch) {
[DRAW_TREE_VERT] = "\342\224\202 ", /* │ */
[DRAW_TREE_BRANCH] = "\342\224\234\342\224\200", /* ├─ */
[DRAW_TREE_RIGHT] = "\342\224\224\342\224\200", /* └─ */
+ [DRAW_TREE_SPACE] = " ", /* */
[DRAW_TRIANGULAR_BULLET] = "\342\200\243 ", /* ‣ */
},
/* ASCII fallback */ {
[DRAW_TREE_VERT] = "| ",
[DRAW_TREE_BRANCH] = "|-",
[DRAW_TREE_RIGHT] = "`-",
+ [DRAW_TREE_SPACE] = " ",
[DRAW_TRIANGULAR_BULLET] = "> ",
}
};
@@ -5775,7 +5524,6 @@ int on_ac_power(void) {
for (;;) {
struct dirent *de;
union dirent_storage buf;
- _cleanup_free_ char *p = NULL;
_cleanup_close_ int fd = -1, device = -1;
char contents[6];
ssize_t n;
@@ -5841,3 +5589,261 @@ int on_ac_power(void) {
return found_online || !found_offline;
}
+
+static int search_and_fopen_internal(const char *path, const char *mode, char **search, FILE **_f) {
+ char **i;
+
+ assert(path);
+ assert(mode);
+ assert(_f);
+
+ if (!path_strv_canonicalize_uniq(search))
+ return -ENOMEM;
+
+ STRV_FOREACH(i, search) {
+ _cleanup_free_ char *p = NULL;
+ FILE *f;
+
+ p = strjoin(*i, "/", path, NULL);
+ if (!p)
+ return -ENOMEM;
+
+ f = fopen(p, mode);
+ if (f) {
+ *_f = f;
+ return 0;
+ }
+
+ if (errno != ENOENT)
+ return -errno;
+ }
+
+ return -ENOENT;
+}
+
+int search_and_fopen(const char *path, const char *mode, const char **search, FILE **_f) {
+ _cleanup_strv_free_ char **copy = NULL;
+
+ assert(path);
+ assert(mode);
+ assert(_f);
+
+ if (path_is_absolute(path)) {
+ FILE *f;
+
+ f = fopen(path, mode);
+ if (f) {
+ *_f = f;
+ return 0;
+ }
+
+ return -errno;
+ }
+
+ copy = strv_copy((char**) search);
+ if (!copy)
+ return -ENOMEM;
+
+ return search_and_fopen_internal(path, mode, copy, _f);
+}
+
+int search_and_fopen_nulstr(const char *path, const char *mode, const char *search, FILE **_f) {
+ _cleanup_strv_free_ char **s = NULL;
+
+ if (path_is_absolute(path)) {
+ FILE *f;
+
+ f = fopen(path, mode);
+ if (f) {
+ *_f = f;
+ return 0;
+ }
+
+ return -errno;
+ }
+
+ s = strv_split_nulstr(search);
+ if (!s)
+ return -ENOMEM;
+
+ return search_and_fopen_internal(path, mode, s, _f);
+}
+
+int create_tmp_dir(char template[], char** dir_name) {
+ int r = 0;
+ char *d, *dt;
+
+ assert(dir_name);
+
+ RUN_WITH_UMASK(0077) {
+ d = mkdtemp(template);
+ }
+ if (!d) {
+ log_error("Can't create directory %s: %m", template);
+ return -errno;
+ }
+
+ dt = strjoin(d, "/tmp", NULL);
+ if (!dt) {
+ r = log_oom();
+ goto fail3;
+ }
+
+ RUN_WITH_UMASK(0000) {
+ r = mkdir(dt, 0777);
+ }
+ if (r < 0) {
+ log_error("Can't create directory %s: %m", dt);
+ r = -errno;
+ goto fail2;
+ }
+ log_debug("Created temporary directory %s", dt);
+
+ r = chmod(dt, 0777 | S_ISVTX);
+ if (r < 0) {
+ log_error("Failed to chmod %s: %m", dt);
+ r = -errno;
+ goto fail1;
+ }
+ log_debug("Set sticky bit on %s", dt);
+
+ *dir_name = dt;
+
+ return 0;
+fail1:
+ rmdir(dt);
+fail2:
+ free(dt);
+fail3:
+ rmdir(template);
+ return r;
+}
+
+char *strextend(char **x, ...) {
+ va_list ap;
+ size_t f, l;
+ char *r, *p;
+
+ assert(x);
+
+ l = f = *x ? strlen(*x) : 0;
+
+ va_start(ap, x);
+ for (;;) {
+ const char *t;
+ size_t n;
+
+ t = va_arg(ap, const char *);
+ if (!t)
+ break;
+
+ n = strlen(t);
+ if (n > ((size_t) -1) - l) {
+ va_end(ap);
+ return NULL;
+ }
+
+ l += n;
+ }
+ va_end(ap);
+
+ r = realloc(*x, l+1);
+ if (!r)
+ return NULL;
+
+ p = r + f;
+
+ va_start(ap, x);
+ for (;;) {
+ const char *t;
+
+ t = va_arg(ap, const char *);
+ if (!t)
+ break;
+
+ p = stpcpy(p, t);
+ }
+ va_end(ap);
+
+ *p = 0;
+ *x = r;
+
+ return r + l;
+}
+
+char *strrep(const char *s, unsigned n) {
+ size_t l;
+ char *r, *p;
+ unsigned i;
+
+ assert(s);
+
+ l = strlen(s);
+ p = r = malloc(l * n + 1);
+ if (!r)
+ return NULL;
+
+ for (i = 0; i < n; i++)
+ p = stpcpy(p, s);
+
+ *p = 0;
+ return r;
+}
+
+void* greedy_realloc(void **p, size_t *allocated, size_t need) {
+ size_t a;
+ void *q;
+
+ if (*allocated >= need)
+ return *p;
+
+ a = MAX(64u, need * 2);
+ q = realloc(*p, a);
+ if (!q)
+ return NULL;
+
+ *p = q;
+ *allocated = a;
+ return q;
+}
+
+bool id128_is_valid(const char *s) {
+ size_t i, l;
+
+ l = strlen(s);
+ if (l == 32) {
+
+ /* Simple formatted 128bit hex string */
+
+ for (i = 0; i < l; i++) {
+ char c = s[i];
+
+ if (!(c >= '0' && c <= '9') &&
+ !(c >= 'a' && c <= 'z') &&
+ !(c >= 'A' && c <= 'Z'))
+ return false;
+ }
+
+ } else if (l == 36) {
+
+ /* Formatted UUID */
+
+ for (i = 0; i < l; i++) {
+ char c = s[i];
+
+ if ((i == 8 || i == 13 || i == 18 || i == 23)) {
+ if (c != '-')
+ return false;
+ } else {
+ if (!(c >= '0' && c <= '9') &&
+ !(c >= 'a' && c <= 'z') &&
+ !(c >= 'A' && c <= 'Z'))
+ return false;
+ }
+ }
+
+ } else
+ return false;
+
+ return true;
+}
diff --git a/src/shared/util.h b/src/shared/util.h
index bb6602fb24..64e63b8c07 100644
--- a/src/shared/util.h
+++ b/src/shared/util.h
@@ -21,6 +21,7 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <alloca.h>
#include <inttypes.h>
#include <time.h>
#include <sys/time.h>
@@ -36,6 +37,8 @@
#include <dirent.h>
#include <sys/resource.h>
#include <stddef.h>
+#include <unistd.h>
+#include <locale.h>
#include "macro.h"
#include "time-util.h"
@@ -50,25 +53,28 @@ union dirent_storage {
#define WHITESPACE " \t\n\r"
#define NEWLINE "\n\r"
#define QUOTES "\"\'"
-#define COMMENTS "#;\n"
+#define COMMENTS "#;"
#define FORMAT_BYTES_MAX 8
#define ANSI_HIGHLIGHT_ON "\x1B[1;39m"
+#define ANSI_RED_ON "\x1B[31m"
#define ANSI_HIGHLIGHT_RED_ON "\x1B[1;31m"
+#define ANSI_GREEN_ON "\x1B[32m"
#define ANSI_HIGHLIGHT_GREEN_ON "\x1B[1;32m"
#define ANSI_HIGHLIGHT_YELLOW_ON "\x1B[1;33m"
#define ANSI_HIGHLIGHT_OFF "\x1B[0m"
-
-bool is_efiboot(void);
+#define ANSI_ERASE_TO_END_OF_LINE "\x1B[K"
size_t page_size(void);
#define PAGE_ALIGN(l) ALIGN_TO((l), page_size())
#define streq(a,b) (strcmp((a),(b)) == 0)
#define strneq(a, b, n) (strncmp((a), (b), (n)) == 0)
+#define strcaseeq(a,b) (strcasecmp((a),(b)) == 0)
+#define strncaseeq(a, b, n) (strncasecmp((a), (b), (n)) == 0)
-bool streq_ptr(const char *a, const char *b);
+bool streq_ptr(const char *a, const char *b) _pure_;
#define new(t, n) ((t*) malloc_multiply(sizeof(t), (n)))
@@ -100,17 +106,17 @@ static inline bool isempty(const char *p) {
return !p || !p[0];
}
-char *endswith(const char *s, const char *postfix);
-char *startswith(const char *s, const char *prefix);
-char *startswith_no_case(const char *s, const char *prefix);
+char *endswith(const char *s, const char *postfix) _pure_;
+char *startswith(const char *s, const char *prefix) _pure_;
+char *startswith_no_case(const char *s, const char *prefix) _pure_;
-bool first_word(const char *s, const char *word);
+bool first_word(const char *s, const char *word) _pure_;
int close_nointr(int fd);
void close_nointr_nofail(int fd);
void close_many(const int fds[], unsigned n_fd);
-int parse_boolean(const char *v);
+int parse_boolean(const char *v) _pure_;
int parse_bytes(const char *t, off_t *bytes);
int parse_pid(const char *s, pid_t* ret_pid);
int parse_uid(const char *s, uid_t* ret_uid);
@@ -122,6 +128,8 @@ int safe_atoi(const char *s, int *ret_i);
int safe_atollu(const char *s, unsigned long long *ret_u);
int safe_atolli(const char *s, long long int *ret_i);
+int safe_atod(const char *s, double *ret_d);
+
#if __WORDSIZE == 32
static inline int safe_atolu(const char *s, unsigned long *ret_u) {
assert_cc(sizeof(unsigned long) == sizeof(unsigned));
@@ -177,15 +185,6 @@ char *split_quoted(const char *c, size_t *l, char **state);
pid_t get_parent_of_pid(pid_t pid, pid_t *ppid);
int get_starttime_of_pid(pid_t pid, unsigned long long *st);
-int write_one_line_file(const char *fn, const char *line);
-int write_one_line_file_atomic(const char *fn, const char *line);
-int read_one_line_file(const char *fn, char **line);
-int read_full_file(const char *fn, char **contents, size_t *size);
-
-int parse_env_file(const char *fname, const char *separator, ...) _sentinel_;
-int load_env_file(const char *fname, char ***l);
-int write_env_file(const char *fname, char **l);
-
char *strappend(const char *s, const char *suffix);
char *strnappend(const char *s, const char *suffix, size_t length);
@@ -212,12 +211,12 @@ int get_process_exe(pid_t pid, char **name);
int get_process_uid(pid_t pid, uid_t *uid);
int get_process_gid(pid_t pid, gid_t *gid);
-char hexchar(int x);
-int unhexchar(char c);
-char octchar(int x);
-int unoctchar(char c);
-char decchar(int x);
-int undecchar(char c);
+char hexchar(int x) _const_;
+int unhexchar(char c) _const_;
+char octchar(int x) _const_;
+int unoctchar(char c) _const_;
+char decchar(int x) _const_;
+int undecchar(char c) _const_;
char *cescape(const char *s);
char *cunescape(const char *s);
@@ -231,12 +230,12 @@ char *bus_path_unescape(const char *s);
char *ascii_strlower(char *path);
-bool dirent_is_file(const struct dirent *de);
-bool dirent_is_file_with_suffix(const struct dirent *de, const char *suffix);
+bool dirent_is_file(const struct dirent *de) _pure_;
+bool dirent_is_file_with_suffix(const struct dirent *de, const char *suffix) _pure_;
-bool ignore_file(const char *filename);
+bool ignore_file(const char *filename) _pure_;
-bool chars_intersect(const char *a, const char *b);
+bool chars_intersect(const char *a, const char *b) _pure_;
int make_stdio(int fd);
int make_null_stdio(void);
@@ -308,7 +307,7 @@ bool fstype_is_network(const char *fstype);
int chvt(int vt);
int read_one_char(FILE *f, char *ret, usec_t timeout, bool *need_nl);
-int ask(char *ret, const char *replies, const char *text, ...);
+int ask(char *ret, const char *replies, const char *text, ...) _printf_attr_(3, 4);
int reset_terminal_fd(int fd, bool switch_to_text);
int reset_terminal(const char *name);
@@ -332,6 +331,7 @@ ssize_t loop_write(int fd, const void *buf, size_t nbytes, bool do_poll);
bool is_device_path(const char *path);
int dir_is_empty(const char *path);
+char* dirname_malloc(const char *path);
void rename_process(const char name[8]);
@@ -361,8 +361,8 @@ int pipe_eof(int fd);
cpu_set_t* cpu_set_malloc(unsigned *ncpus);
-int status_vprintf(const char *status, bool ellipse, const char *format, va_list ap);
-int status_printf(const char *status, bool ellipse, const char *format, ...);
+int status_vprintf(const char *status, bool ellipse, bool ephemeral, const char *format, va_list ap) _printf_attr_(4,0);
+int status_printf(const char *status, bool ellipse, bool ephemeral, const char *format, ...) _printf_attr_(4,5);
int status_welcome(void);
int fd_columns(int fd);
@@ -388,16 +388,17 @@ int wait_for_terminate_and_warn(const char *name, pid_t pid);
_noreturn_ void freeze(void);
-bool null_or_empty(struct stat *st);
+bool null_or_empty(struct stat *st) _pure_;
int null_or_empty_path(const char *fn);
DIR *xopendirat(int dirfd, const char *name, int flags);
char *fstab_node_to_udev_node(const char *p);
+char *resolve_dev_console(char **active);
bool tty_is_vc(const char *tty);
bool tty_is_vc_resolve(const char *tty);
-bool tty_is_console(const char *tty);
+bool tty_is_console(const char *tty) _pure_;
int vtnr_from_tty(const char *tty);
const char *default_term_for_tty(const char *tty);
@@ -409,8 +410,8 @@ bool nulstr_contains(const char*nulstr, const char *needle);
bool plymouth_running(void);
-bool hostname_is_valid(const char *s);
-char* hostname_cleanup(char *s);
+bool hostname_is_valid(const char *s) _pure_;
+char* hostname_cleanup(char *s, bool lowercase);
char* strshorten(char *s, size_t l);
@@ -425,14 +426,18 @@ int symlink_atomic(const char *from, const char *to);
int fchmod_umask(int fd, mode_t mode);
-bool display_is_local(const char *display);
+bool display_is_local(const char *display) _pure_;
int socket_from_display(const char *display, char **path);
int get_user_creds(const char **username, uid_t *uid, gid_t *gid, const char **home, const char **shell);
int get_group_creds(const char **groupname, gid_t *gid);
+int in_gid(gid_t gid);
int in_group(const char *name);
+char* uid_to_name(uid_t uid);
+char* gid_to_name(gid_t gid);
+
int glob_exists(const char *path);
int dirent_ensure_type(DIR *d, struct dirent *de);
@@ -444,7 +449,7 @@ char *strjoin(const char *x, ...) _sentinel_;
bool is_main_thread(void);
-bool in_charset(const char *s, const char* charset);
+bool in_charset(const char *s, const char* charset) _pure_;
int block_get_whole_disk(dev_t d, dev_t *ret);
@@ -461,8 +466,8 @@ int strdup_or_null(const char *a, char **b);
int ioprio_class_to_string_alloc(int i, char **s);
int ioprio_class_from_string(const char *s);
-const char *sigchld_code_to_string(int i);
-int sigchld_code_from_string(const char *s);
+const char *sigchld_code_to_string(int i) _const_;
+int sigchld_code_from_string(const char *s) _pure_;
int log_facility_unshifted_to_string_alloc(int i, char **s);
int log_facility_unshifted_from_string(const char *s);
@@ -473,14 +478,14 @@ int log_level_from_string(const char *s);
int sched_policy_to_string_alloc(int i, char **s);
int sched_policy_from_string(const char *s);
-const char *rlimit_to_string(int i);
-int rlimit_from_string(const char *s);
+const char *rlimit_to_string(int i) _const_;
+int rlimit_from_string(const char *s) _pure_;
int ip_tos_to_string_alloc(int i, char **s);
int ip_tos_from_string(const char *s);
-const char *signal_to_string(int i);
-int signal_from_string(const char *s);
+const char *signal_to_string(int i) _const_;
+int signal_from_string(const char *s) _pure_;
int signal_from_string_try_harder(const char *s);
@@ -489,13 +494,13 @@ extern char **saved_argv;
bool kexec_loaded(void);
-int prot_from_flags(int flags);
+int prot_from_flags(int flags) _const_;
char *format_bytes(char *buf, size_t l, off_t t);
int fd_wait_for_event(int fd, int event, usec_t timeout);
-void* memdup(const void *p, size_t l) _malloc_;
+void* memdup(const void *p, size_t l) _alloc_(2);
int is_kernel_thread(pid_t pid);
@@ -508,40 +513,68 @@ int setrlimit_closest(int resource, const struct rlimit *rlim);
int getenv_for_pid(pid_t pid, const char *field, char **_value);
-int can_sleep(const char *type);
-int can_sleep_disk(const char *type);
-
-bool is_valid_documentation_url(const char *url);
+bool is_valid_documentation_url(const char *url) _pure_;
bool in_initrd(void);
void warn_melody(void);
-int get_shell(char **ret);
int get_home_dir(char **ret);
-void freep(void *p);
-void fclosep(FILE **f);
-void closep(int *fd);
-void closedirp(DIR **d);
-void umaskp(mode_t *u);
+static inline void freep(void *p) {
+ free(*(void**) p);
+}
+
+static inline void fclosep(FILE **f) {
+ if (*f)
+ fclose(*f);
+}
+
+static inline void pclosep(FILE **f) {
+ if (*f)
+ pclose(*f);
+}
+
+static inline void closep(int *fd) {
+ if (*fd >= 0)
+ close_nointr_nofail(*fd);
+}
+
+static inline void closedirp(DIR **d) {
+ if (*d)
+ closedir(*d);
+}
-_malloc_ static inline void *malloc_multiply(size_t a, size_t b) {
+static inline void umaskp(mode_t *u) {
+ umask(*u);
+}
+
+#define _cleanup_free_ _cleanup_(freep)
+#define _cleanup_fclose_ _cleanup_(fclosep)
+#define _cleanup_pclose_ _cleanup_(pclosep)
+#define _cleanup_close_ _cleanup_(closep)
+#define _cleanup_closedir_ _cleanup_(closedirp)
+#define _cleanup_umask_ _cleanup_(umaskp)
+#define _cleanup_globfree_ _cleanup_(globfree)
+
+_malloc_ _alloc_(1, 2) static inline void *malloc_multiply(size_t a, size_t b) {
if (_unlikely_(b == 0 || a > ((size_t) -1) / b))
return NULL;
return malloc(a * b);
}
-_malloc_ static inline void *memdup_multiply(const void *p, size_t a, size_t b) {
+_alloc_(2, 3) static inline void *memdup_multiply(const void *p, size_t a, size_t b) {
if (_unlikely_(b == 0 || a > ((size_t) -1) / b))
return NULL;
return memdup(p, a * b);
}
-bool filename_is_safe(const char *p);
-bool string_is_safe(const char *p);
+bool filename_is_safe(const char *p) _pure_;
+bool path_is_safe(const char *p) _pure_;
+bool string_is_safe(const char *p) _pure_;
+bool string_has_cc(const char *p) _pure_;
void *xbsearch_r(const void *key, const void *base, size_t nmemb, size_t size,
int (*compar) (const void *, const void *, void *),
@@ -553,6 +586,7 @@ typedef enum DrawSpecialChar {
DRAW_TREE_VERT,
DRAW_TREE_BRANCH,
DRAW_TREE_RIGHT,
+ DRAW_TREE_SPACE,
DRAW_TRIANGULAR_BULLET,
_DRAW_SPECIAL_CHAR_MAX
} DrawSpecialChar;
@@ -563,3 +597,138 @@ char *strreplace(const char *text, const char *old_string, const char *new_strin
char *strip_tab_ansi(char **p, size_t *l);
int on_ac_power(void);
+
+int search_and_fopen(const char *path, const char *mode, const char **search, FILE **_f);
+int search_and_fopen_nulstr(const char *path, const char *mode, const char *search, FILE **_f);
+int create_tmp_dir(char template[], char** dir_name);
+
+#define FOREACH_LINE(line, f, on_error) \
+ for (;;) \
+ if (!fgets(line, sizeof(line), f)) { \
+ if (ferror(f)) { \
+ on_error; \
+ } \
+ break; \
+ } else
+
+#define FOREACH_DIRENT(de, d, on_error) \
+ for (errno = 0, de = readdir(d);; errno = 0, de = readdir(d)) \
+ if (!de) { \
+ if (errno > 0) { \
+ on_error; \
+ } \
+ break; \
+ } else if (ignore_file((de)->d_name)) \
+ continue; \
+ else
+
+static inline void *mempset(void *s, int c, size_t n) {
+ memset(s, c, n);
+ return (uint8_t*)s + n;
+}
+
+char *hexmem(const void *p, size_t l);
+void *unhexmem(const char *p, size_t l);
+
+char *strextend(char **x, ...) _sentinel_;
+char *strrep(const char *s, unsigned n);
+
+void* greedy_realloc(void **p, size_t *allocated, size_t need);
+#define GREEDY_REALLOC(array, allocated, need) \
+ greedy_realloc((void**) &(array), &(allocated), sizeof((array)[0]) * (need))
+
+static inline void _reset_errno_(int *saved_errno) {
+ errno = *saved_errno;
+}
+
+#define PROTECT_ERRNO _cleanup_(_reset_errno_) __attribute__((unused)) int _saved_errno_ = errno
+
+struct _umask_struct_ {
+ mode_t mask;
+ bool quit;
+};
+
+static inline void _reset_umask_(struct _umask_struct_ *s) {
+ umask(s->mask);
+};
+
+#define RUN_WITH_UMASK(mask) \
+ for (_cleanup_(_reset_umask_) struct _umask_struct_ _saved_umask_ = { umask(mask), false }; \
+ !_saved_umask_.quit ; \
+ _saved_umask_.quit = true)
+
+static inline unsigned u64log2(uint64_t n) {
+ return (n > 1) ? __builtin_clzll(n) ^ 63U : 0;
+}
+
+static inline bool logind_running(void) {
+ return access("/run/systemd/seats/", F_OK) >= 0;
+}
+
+#define DECIMAL_STR_WIDTH(x) \
+ ({ \
+ typeof(x) _x_ = (x); \
+ unsigned ans = 1; \
+ while (_x_ /= 10) \
+ ans++; \
+ ans; \
+ })
+
+int unlink_noerrno(const char *path);
+
+#define alloca0(n) \
+ ({ \
+ char *_new_; \
+ size_t _len_ = n; \
+ _new_ = alloca(_len_); \
+ (void *) memset(_new_, 0, _len_); \
+ })
+
+#define strappenda(a, b) \
+ ({ \
+ const char *_a_ = (a), *_b_ = (b); \
+ char *_c_; \
+ size_t _x_, _y_; \
+ _x_ = strlen(_a_); \
+ _y_ = strlen(_b_); \
+ _c_ = alloca(_x_ + _y_ + 1); \
+ strcpy(stpcpy(_c_, _a_), _b_); \
+ _c_; \
+ })
+
+#define procfs_file_alloca(pid, field) \
+ ({ \
+ pid_t _pid_ = (pid); \
+ char *_r_; \
+ _r_ = alloca(sizeof("/proc/") -1 + DECIMAL_STR_MAX(pid_t) + 1 + sizeof(field)); \
+ sprintf(_r_, "/proc/%lu/" field, (unsigned long) _pid_); \
+ _r_; \
+ })
+
+struct _locale_struct_ {
+ locale_t saved_locale;
+ locale_t new_locale;
+ bool quit;
+};
+
+static inline void _reset_locale_(struct _locale_struct_ *s) {
+ PROTECT_ERRNO;
+ if (s->saved_locale != (locale_t) 0)
+ uselocale(s->saved_locale);
+ if (s->new_locale != (locale_t) 0)
+ freelocale(s->new_locale);
+}
+
+#define RUN_WITH_LOCALE(mask, loc) \
+ for (_cleanup_(_reset_locale_) struct _locale_struct_ _saved_locale_ = { (locale_t) 0, (locale_t) 0, false }; \
+ ({ \
+ if (!_saved_locale_.quit) { \
+ PROTECT_ERRNO; \
+ _saved_locale_.new_locale = newlocale((mask), (loc), (locale_t) 0); \
+ if (_saved_locale_.new_locale != (locale_t) 0) \
+ _saved_locale_.saved_locale = uselocale(_saved_locale_.new_locale); \
+ } \
+ !_saved_locale_.quit; }) ; \
+ _saved_locale_.quit = true)
+
+bool id128_is_valid(const char *s) _pure_;
diff --git a/src/shared/utmp-wtmp.c b/src/shared/utmp-wtmp.c
index 046fb584fb..5d88405e13 100644
--- a/src/shared/utmp-wtmp.c
+++ b/src/shared/utmp-wtmp.c
@@ -33,7 +33,7 @@
#include "utmp-wtmp.h"
int utmp_get_runlevel(int *runlevel, int *previous) {
- struct utmpx lookup, *found;
+ struct utmpx *found, lookup = { .ut_type = RUN_LVL };
int r;
const char *e;
@@ -66,9 +66,6 @@ int utmp_get_runlevel(int *runlevel, int *previous) {
setutxent();
- zero(lookup);
- lookup.ut_type = RUN_LVL;
-
if (!(found = getutxid(&lookup)))
r = -errno;
else {
@@ -77,15 +74,11 @@ int utmp_get_runlevel(int *runlevel, int *previous) {
a = found->ut_pid & 0xFF;
b = (found->ut_pid >> 8) & 0xFF;
- if (a < 0 || b < 0)
- r = -EIO;
- else {
- *runlevel = a;
+ *runlevel = a;
+ if (previous)
+ *previous = b;
- if (previous)
- *previous = b;
- r = 0;
- }
+ r = 0;
}
endutxent();
@@ -106,14 +99,12 @@ static void init_timestamp(struct utmpx *store, usec_t t) {
}
static void init_entry(struct utmpx *store, usec_t t) {
- struct utsname uts;
+ struct utsname uts = {};
assert(store);
init_timestamp(store, t);
- zero(uts);
-
if (uname(&uts) >= 0)
strncpy(store->ut_host, uts.release, sizeof(store->ut_host));
@@ -199,7 +190,7 @@ int utmp_put_reboot(usec_t t) {
return write_entry_both(&store);
}
-static const char *sanitize_id(const char *id) {
+_pure_ static const char *sanitize_id(const char *id) {
size_t l;
assert(id);
@@ -296,7 +287,7 @@ int utmp_put_runlevel(int runlevel, int previous) {
#define TIMEOUT_MSEC 50
static int write_to_terminal(const char *tty, const char *message) {
- int fd, r;
+ _cleanup_close_ int fd = -1;
const char *p;
size_t left;
usec_t end;
@@ -304,14 +295,10 @@ static int write_to_terminal(const char *tty, const char *message) {
assert(tty);
assert(message);
- if ((fd = open(tty, O_WRONLY|O_NDELAY|O_NOCTTY|O_CLOEXEC)) < 0)
+ fd = open(tty, O_WRONLY|O_NDELAY|O_NOCTTY|O_CLOEXEC);
+ if (fd < 0 || !isatty(fd))
return -errno;
- if (!isatty(fd)) {
- r = -errno;
- goto finish;
- }
-
p = message;
left = strlen(message);
@@ -319,36 +306,31 @@ static int write_to_terminal(const char *tty, const char *message) {
while (left > 0) {
ssize_t n;
- struct pollfd pollfd;
+ struct pollfd pollfd = {
+ .fd = fd,
+ .events = POLLOUT,
+ };
usec_t t;
int k;
t = now(CLOCK_MONOTONIC);
- if (t >= end) {
- r = -ETIME;
- goto finish;
- }
-
- zero(pollfd);
- pollfd.fd = fd;
- pollfd.events = POLLOUT;
+ if (t >= end)
+ return -ETIME;
- if ((k = poll(&pollfd, 1, (end - t) / USEC_PER_MSEC)) < 0)
+ k = poll(&pollfd, 1, (end - t) / USEC_PER_MSEC);
+ if (k < 0)
return -errno;
- if (k <= 0) {
- r = -ETIME;
- goto finish;
- }
-
- if ((n = write(fd, p, left)) < 0) {
+ if (k == 0)
+ return -ETIME;
+ n = write(fd, p, left);
+ if (n < 0) {
if (errno == EAGAIN)
continue;
- r = -errno;
- goto finish;
+ return -errno;
}
assert((size_t) n <= left);
@@ -357,12 +339,7 @@ static int write_to_terminal(const char *tty, const char *message) {
left -= n;
}
- r = 0;
-
-finish:
- close_nointr_nofail(fd);
-
- return r;
+ return 0;
}
int utmp_wall(const char *message, bool (*match_tty)(const char *tty)) {
@@ -403,10 +380,12 @@ int utmp_wall(const char *message, bool (*match_tty)(const char *tty)) {
if (u->ut_type != USER_PROCESS || u->ut_user[0] == 0)
continue;
+ /* this access is fine, because strlen("/dev/") << 32 (UT_LINESIZE) */
if (path_startswith(u->ut_line, "/dev/"))
path = u->ut_line;
else {
- if (asprintf(&buf, "/dev/%s", u->ut_line) < 0) {
+ if (asprintf(&buf, "/dev/%.*s",
+ (int) sizeof(u->ut_line), u->ut_line) < 0) {
r = -ENOMEM;
goto finish;
}
diff --git a/src/shared/virt.c b/src/shared/virt.c
index fc62c72328..1c86a3dd1e 100644
--- a/src/shared/virt.c
+++ b/src/shared/virt.c
@@ -25,6 +25,7 @@
#include "util.h"
#include "virt.h"
+#include "fileio.h"
/* Returns a short identifier for the various VM implementations */
int detect_vm(const char **id) {
@@ -61,13 +62,28 @@ int detect_vm(const char **id) {
union {
uint32_t sig32[3];
char text[13];
- } sig;
+ } sig = {};
unsigned i;
const char *j, *k;
bool hypervisor;
+ _cleanup_free_ char *hvtype = NULL;
+ int r;
+
+ /* Try high-level hypervisor sysfs file first:
+ *
+ * https://bugs.freedesktop.org/show_bug.cgi?id=61491 */
+ r = read_one_line_file("/sys/hypervisor/type", &hvtype);
+ if (r >= 0) {
+ if (streq(hvtype, "xen")) {
+ if (id)
+ *id = "xen";
+
+ return 1;
+ }
+ } else if (r != -ENOENT)
+ return r;
/* http://lwn.net/Articles/301888/ */
- zero(sig);
#if defined (__i386__)
#define REG_a "eax"
@@ -117,11 +133,11 @@ int detect_vm(const char **id) {
}
for (i = 0; i < ELEMENTSOF(dmi_vendors); i++) {
- char *s;
- int r;
+ _cleanup_free_ char *s = NULL;
const char *found = NULL;
- if ((r = read_one_line_file(dmi_vendors[i], &s)) < 0) {
+ r = read_one_line_file(dmi_vendors[i], &s);
+ if (r < 0) {
if (r != -ENOENT)
return r;
@@ -131,7 +147,6 @@ int detect_vm(const char **id) {
NULSTR_FOREACH_PAIR(j, k, dmi_vendor_table)
if (startswith(s, j))
found = k;
- free(s);
if (found) {
if (id)
@@ -141,7 +156,7 @@ int detect_vm(const char **id) {
}
}
- if (hypervisor) {
+ if (hypervisor || hvtype) {
if (id)
*id = "other";
@@ -153,7 +168,7 @@ int detect_vm(const char **id) {
}
int detect_container(const char **id) {
- char *e = NULL;
+ _cleanup_free_ char *e = NULL;
int r;
/* Unfortunately many of these operations require root access
@@ -201,8 +216,6 @@ int detect_container(const char **id) {
*id = "other";
}
- free(e);
-
return r;
}
diff --git a/src/shared/watchdog.c b/src/shared/watchdog.c
index 13265e7692..ddbe7afd3c 100644
--- a/src/shared/watchdog.c
+++ b/src/shared/watchdog.c
@@ -60,7 +60,7 @@ static int update_timeout(void) {
}
watchdog_timeout = (usec_t) sec * USEC_PER_SEC;
- log_info("Set hardware watchdog to %s.", format_timespan(buf, sizeof(buf), watchdog_timeout));
+ log_info("Set hardware watchdog to %s.", format_timespan(buf, sizeof(buf), watchdog_timeout, 0));
flags = WDIOS_ENABLECARD;
r = ioctl(watchdog_fd, WDIOC_SETOPTIONS, &flags);
diff --git a/src/shutdownd/shutdownd.c b/src/shutdownd/shutdownd.c
index c0747415fd..461a7261f4 100644
--- a/src/shutdownd/shutdownd.c
+++ b/src/shutdownd/shutdownd.c
@@ -38,6 +38,7 @@
#include "util.h"
#include "utmp-wtmp.h"
#include "mkdir.h"
+#include "fileio.h"
union shutdown_buffer {
struct sd_shutdown_command command;
@@ -45,30 +46,29 @@ union shutdown_buffer {
};
static int read_packet(int fd, union shutdown_buffer *_b) {
- struct msghdr msghdr;
- struct iovec iovec;
struct ucred *ucred;
+ ssize_t n;
+
+ union shutdown_buffer b; /* We maintain our own copy here, in
+ * order not to corrupt the last message */
+ struct iovec iovec = {
+ iovec.iov_base = &b,
+ iovec.iov_len = sizeof(b) - 1,
+ };
union {
struct cmsghdr cmsghdr;
uint8_t buf[CMSG_SPACE(sizeof(struct ucred))];
- } control;
- ssize_t n;
- union shutdown_buffer b; /* We maintain our own copy here, in order not to corrupt the last message */
+ } control = {};
+ struct msghdr msghdr = {
+ .msg_iov = &iovec,
+ msghdr.msg_iovlen = 1,
+ msghdr.msg_control = &control,
+ msghdr.msg_controllen = sizeof(control),
+ };
assert(fd >= 0);
assert(_b);
- zero(iovec);
- iovec.iov_base = &b;
- iovec.iov_len = sizeof(b) - 1;
-
- zero(control);
- zero(msghdr);
- msghdr.msg_iov = &iovec;
- msghdr.msg_iovlen = 1;
- msghdr.msg_control = &control;
- msghdr.msg_controllen = sizeof(control);
-
n = recvmsg(fd, &msghdr, MSG_DONTWAIT);
if (n <= 0) {
if (n == 0) {
@@ -150,35 +150,32 @@ static void warn_wall(usec_t n, struct sd_shutdown_command *c) {
}
}
-static usec_t when_wall(usec_t n, usec_t elapse) {
+_const_ static usec_t when_wall(usec_t n, usec_t elapse) {
static const struct {
usec_t delay;
usec_t interval;
} table[] = {
- { 10 * USEC_PER_MINUTE, USEC_PER_MINUTE },
- { USEC_PER_HOUR, 15 * USEC_PER_MINUTE },
- { 3 * USEC_PER_HOUR, 30 * USEC_PER_MINUTE }
+ { 0, USEC_PER_MINUTE },
+ { 10 * USEC_PER_MINUTE, 15 * USEC_PER_MINUTE },
+ { USEC_PER_HOUR, 30 * USEC_PER_MINUTE },
+ { 3 * USEC_PER_HOUR, USEC_PER_HOUR },
};
usec_t left, sub;
- unsigned i;
+ unsigned i = ELEMENTSOF(table) - 1;
/* If the time is already passed, then don't announce */
if (n >= elapse)
return 0;
left = elapse - n;
- for (i = 0; i < ELEMENTSOF(table); i++)
- if (n + table[i].delay >= elapse) {
- sub = ((left / table[i].interval) * table[i].interval);
- break;
- }
-
- if (i >= ELEMENTSOF(table))
- sub = ((left / USEC_PER_HOUR) * USEC_PER_HOUR);
+ while (left < table[i].delay)
+ i--;
+ sub = (left / table[i].interval) * table[i].interval;
- return elapse > sub ? elapse - sub : 1;
+ assert(sub < elapse);
+ return elapse - sub;
}
static usec_t when_nologin(usec_t elapse) {
@@ -272,8 +269,8 @@ int main(int argc, char *argv[]) {
};
int r = EXIT_FAILURE, n_fds;
- union shutdown_buffer b;
- struct pollfd pollfd[_FD_MAX];
+ union shutdown_buffer b = {};
+ struct pollfd pollfd[_FD_MAX] = {};
bool exec_shutdown = false, unlink_nologin = false;
unsigned i;
@@ -304,9 +301,6 @@ int main(int argc, char *argv[]) {
return EXIT_FAILURE;
}
- zero(b);
- zero(pollfd);
-
pollfd[FD_SOCKET].fd = SD_LISTEN_FDS_START;
pollfd[FD_SOCKET].events = POLLIN;
@@ -404,13 +398,12 @@ int main(int argc, char *argv[]) {
}
if (pollfd[FD_WALL_TIMER].revents) {
- struct itimerspec its;
+ struct itimerspec its = {};
warn_wall(n, &b.command);
flush_fd(pollfd[FD_WALL_TIMER].fd);
/* Restart timer */
- zero(its);
timespec_store(&its.it_value, when_wall(n, b.command.usec));
if (timerfd_settime(pollfd[FD_WALL_TIMER].fd, TFD_TIMER_ABSTIME, &its, NULL) < 0) {
log_error("timerfd_settime(): %m");
@@ -423,7 +416,7 @@ int main(int argc, char *argv[]) {
log_info("Creating /run/nologin, blocking further logins...");
- e = write_one_line_file_atomic("/run/nologin", "System is going down.");
+ e = write_string_file_atomic("/run/nologin", "System is going down.");
if (e < 0)
log_error("Failed to create /run/nologin: %s", strerror(-e));
else
diff --git a/src/sleep/sleep.c b/src/sleep/sleep.c
index 218de3a567..a56ab89e54 100644
--- a/src/sleep/sleep.c
+++ b/src/sleep/sleep.c
@@ -4,6 +4,7 @@
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
@@ -22,106 +23,200 @@
#include <stdio.h>
#include <errno.h>
#include <string.h>
+#include <getopt.h>
-#include "log.h"
-#include "util.h"
#include "systemd/sd-id128.h"
#include "systemd/sd-messages.h"
+#include "log.h"
+#include "util.h"
+#include "strv.h"
+#include "fileio.h"
+#include "build.h"
+#include "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 = write_string_file("/sys/power/disk", *mode);
+ if (k == 0)
+ return 0;
+ log_debug("Failed to write '%s' to /sys/power/disk: %s",
+ *mode, strerror(-k));
+ if (r == 0)
+ r = k;
+ }
-int main(int argc, char *argv[]) {
- const char *verb;
- char* arguments[4];
- int r;
- FILE *f;
+ if (r < 0)
+ log_error("Failed to write mode to /sys/power/disk: %s",
+ strerror(-r));
- log_set_target(LOG_TARGET_AUTO);
- log_parse_environment();
- log_open();
+ return r;
+}
- if (argc != 2) {
- log_error("Invalid number of arguments.");
- r = -EINVAL;
- goto finish;
+static int write_state(FILE *f0, char **states) {
+ FILE _cleanup_fclose_ *f = f0;
+ char **state;
+ int r = 0;
+
+ STRV_FOREACH(state, states) {
+ int k;
+
+ k = write_string_to_file(f, *state);
+ if (k == 0)
+ return 0;
+ log_debug("Failed to write '%s' to /sys/power/state: %s",
+ *state, strerror(-k));
+ if (r == 0)
+ r = k;
+
+ fclose(f);
+ f = fopen("/sys/power/state", "we");
+ if (!f) {
+ log_error("Failed to open /sys/power/state: %m");
+ return -errno;
+ }
}
- if (streq(argv[1], "suspend"))
- verb = "mem";
- else if (streq(argv[1], "hibernate") || streq(argv[1], "hybrid-sleep"))
- verb = "disk";
- else {
- log_error("Unknown action '%s'.", argv[1]);
- r = -EINVAL;
- goto finish;
- }
+ return r;
+}
- /* Configure the hibernation mode */
- if (streq(argv[1], "hibernate")) {
- if (write_one_line_file("/sys/power/disk", "platform") < 0)
- write_one_line_file("/sys/power/disk", "shutdown");
- } else if (streq(argv[1], "hybrid-sleep")) {
- if (write_one_line_file("/sys/power/disk", "suspend") < 0)
- if (write_one_line_file("/sys/power/disk", "platform") < 0)
- write_one_line_file("/sys/power/disk", "shutdown");
- }
+static int execute(char **modes, char **states) {
+ char* arguments[4];
+ int r;
+ FILE *f;
+ const char* note = strappenda("SLEEP=", arg_verb);
+ /* This file is opened first, so that if we hit an error,
+ * we can abort before modyfing any state. */
f = fopen("/sys/power/state", "we");
if (!f) {
log_error("Failed to open /sys/power/state: %m");
- r = -errno;
- goto finish;
+ return -errno;
}
+ /* Configure the hibernation mode */
+ r = write_mode(modes);
+ if (r < 0)
+ return r;
+
arguments[0] = NULL;
arguments[1] = (char*) "pre";
- arguments[2] = argv[1];
+ arguments[2] = arg_verb;
arguments[3] = NULL;
execute_directory(SYSTEM_SLEEP_PATH, NULL, arguments);
- if (streq(argv[1], "suspend"))
- log_struct(LOG_INFO,
- MESSAGE_ID(SD_MESSAGE_SLEEP_START),
- "MESSAGE=Suspending system...",
- "SLEEP=suspend",
- NULL);
- else if (streq(argv[1], "hibernate"))
- log_struct(LOG_INFO,
- MESSAGE_ID(SD_MESSAGE_SLEEP_START),
- "MESSAGE=Hibernating system...",
- "SLEEP=hibernate",
- NULL);
- else
- log_struct(LOG_INFO,
- MESSAGE_ID(SD_MESSAGE_SLEEP_START),
- "MESSAGE=Hibernating and suspending system...",
- "SLEEP=hybrid-sleep",
- NULL);
-
- fputs(verb, f);
- fputc('\n', f);
- fflush(f);
-
- r = ferror(f) ? -errno : 0;
-
- if (streq(argv[1], "suspend"))
- log_struct(LOG_INFO,
- MESSAGE_ID(SD_MESSAGE_SLEEP_STOP),
- "MESSAGE=System resumed.",
- "SLEEP=suspend",
- NULL);
- else
- log_struct(LOG_INFO,
- MESSAGE_ID(SD_MESSAGE_SLEEP_STOP),
- "MESSAGE=System thawed.",
- "SLEEP=hibernate",
- NULL);
+ log_struct(LOG_INFO,
+ MESSAGE_ID(SD_MESSAGE_SLEEP_START),
+ "MESSAGE=Suspending system...",
+ note,
+ NULL);
+
+ r = write_state(f, states);
+ if (r < 0)
+ return r;
+
+ log_struct(LOG_INFO,
+ MESSAGE_ID(SD_MESSAGE_SLEEP_STOP),
+ "MESSAGE=System resumed.",
+ note,
+ NULL);
arguments[1] = (char*) "post";
execute_directory(SYSTEM_SLEEP_PATH, NULL, arguments);
- fclose(f);
+ return r;
+}
-finish:
+static int 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
+ );
+
+ return 0;
+}
- return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+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 },
+ { NULL, 0, NULL, 0 }
+ };
+
+ 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:
+ puts(PACKAGE_STRING);
+ puts(SYSTEMD_FEATURES);
+ return 0 /* done */;
+
+ case '?':
+ return -EINVAL;
+
+ default:
+ log_error("Unknown option code %c", c);
+ return -EINVAL;
+ }
+
+ 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/stdio-bridge/stdio-bridge.c b/src/stdio-bridge/stdio-bridge.c
index f926fe5538..a5bdb03416 100644
--- a/src/stdio-bridge/stdio-bridge.c
+++ b/src/stdio-bridge/stdio-bridge.c
@@ -26,121 +26,22 @@
#include <unistd.h>
#include <string.h>
#include <errno.h>
-#include <sys/epoll.h>
+#include <sys/poll.h>
#include <stddef.h>
#include "log.h"
#include "util.h"
#include "socket-util.h"
-
-#define BUFFER_SIZE (64*1024)
-#define EXTRA_SIZE 16
-
-static bool initial_nul = false;
-static bool auth_over = false;
-
-static void format_uid(char *buf, size_t l) {
- char text[20 + 1]; /* enough space for a 64bit integer plus NUL */
- unsigned j;
-
- assert(l > 0);
-
- snprintf(text, sizeof(text)-1, "%llu", (unsigned long long) geteuid());
- text[sizeof(text)-1] = 0;
-
- memset(buf, 0, l);
-
- for (j = 0; text[j] && j*2+2 < l; j++) {
- buf[j*2] = hexchar(text[j] >> 4);
- buf[j*2+1] = hexchar(text[j] & 0xF);
- }
-
- buf[j*2] = 0;
-}
-
-static size_t patch_in_line(char *line, size_t l, size_t left) {
- size_t r;
-
- if (line[0] == 0 && !initial_nul) {
- initial_nul = true;
- line += 1;
- l -= 1;
- r = 1;
- } else
- r = 0;
-
- if (l == 5 && strncmp(line, "BEGIN", 5) == 0) {
- r += l;
- auth_over = true;
-
- } else if (l == 17 && strncmp(line, "NEGOTIATE_UNIX_FD", 17) == 0) {
- memmove(line + 13, line + 17, left);
- memcpy(line, "NEGOTIATE_NOP", 13);
- r += 13;
-
- } else if (l >= 14 && strncmp(line, "AUTH EXTERNAL ", 14) == 0) {
- char uid[20*2 + 1];
- size_t len;
-
- format_uid(uid, sizeof(uid));
- len = strlen(uid);
- assert(len <= EXTRA_SIZE);
-
- memmove(line + 14 + len, line + l, left);
- memcpy(line + 14, uid, len);
-
- r += 14 + len;
- } else
- r += l;
-
- return r;
-}
-
-static size_t patch_in_buffer(char* in_buffer, size_t *in_buffer_full) {
- size_t i, good = 0;
-
- if (*in_buffer_full <= 0)
- return *in_buffer_full;
-
- /* If authentication is done, we don't touch anything anymore */
- if (auth_over)
- return *in_buffer_full;
-
- if (*in_buffer_full < 2)
- return 0;
-
- for (i = 0; i <= *in_buffer_full - 2; i ++) {
-
- /* Fully lines can be send on */
- if (in_buffer[i] == '\r' && in_buffer[i+1] == '\n') {
- if (i > good) {
- size_t old_length, new_length;
-
- old_length = i - good;
- new_length = patch_in_line(in_buffer+good, old_length, *in_buffer_full - i);
- *in_buffer_full = *in_buffer_full + new_length - old_length;
-
- good += new_length + 2;
-
- } else
- good = i+2;
- }
-
- if (auth_over)
- break;
- }
-
- return good;
-}
+#include "sd-daemon.h"
+#include "sd-bus.h"
+#include "bus-internal.h"
+#include "bus-message.h"
int main(int argc, char *argv[]) {
- int r = EXIT_FAILURE, fd = -1, ep = -1;
- union sockaddr_union sa;
- char in_buffer[BUFFER_SIZE+EXTRA_SIZE], out_buffer[BUFFER_SIZE+EXTRA_SIZE];
- size_t in_buffer_full = 0, out_buffer_full = 0;
- struct epoll_event stdin_ev, stdout_ev, fd_ev;
- bool stdin_readable = false, stdout_writable = false, fd_readable = false, fd_writable = false;
- bool stdin_rhup = false, stdout_whup = false, fd_rhup = false, fd_whup = false;
+ _cleanup_bus_unref_ sd_bus *a = NULL, *b = NULL;
+ sd_id128_t server_id;
+ bool is_unix;
+ int r;
if (argc > 1) {
log_error("This program takes no argument.");
@@ -151,217 +52,180 @@ int main(int argc, char *argv[]) {
log_parse_environment();
log_open();
- if ((fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0)) < 0) {
- log_error("Failed to create socket: %s", strerror(errno));
+ is_unix =
+ sd_is_socket(STDIN_FILENO, AF_UNIX, 0, 0) > 0 &&
+ sd_is_socket(STDOUT_FILENO, AF_UNIX, 0, 0) > 0;
+
+ r = sd_bus_new(&a);
+ if (r < 0) {
+ log_error("Failed to allocate bus: %s", strerror(-r));
goto finish;
}
- zero(sa);
- sa.un.sun_family = AF_UNIX;
- strncpy(sa.un.sun_path, "/run/dbus/system_bus_socket", sizeof(sa.un.sun_path));
+ r = sd_bus_set_address(a, "unix:path=/run/dbus/system_bus_socket");
+ if (r < 0) {
+ log_error("Failed to set address to connect to: %s", strerror(-r));
+ goto finish;
+ }
- if (connect(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path)) < 0) {
- log_error("Failed to connect: %m");
+ r = sd_bus_set_negotiate_fds(a, is_unix);
+ if (r < 0) {
+ log_error("Failed to set FD negotiation: %s", strerror(-r));
goto finish;
}
- fd_nonblock(STDIN_FILENO, 1);
- fd_nonblock(STDOUT_FILENO, 1);
+ r = sd_bus_start(a);
+ if (r < 0) {
+ log_error("Failed to start bus client: %s", strerror(-r));
+ goto finish;
+ }
- if ((ep = epoll_create1(EPOLL_CLOEXEC)) < 0) {
- log_error("Failed to create epoll: %m");
+ r = sd_bus_get_server_id(a, &server_id);
+ if (r < 0) {
+ log_error("Failed to get server ID: %s", strerror(-r));
goto finish;
}
- zero(stdin_ev);
- stdin_ev.events = EPOLLIN|EPOLLET;
- stdin_ev.data.fd = STDIN_FILENO;
+ r = sd_bus_new(&b);
+ if (r < 0) {
+ log_error("Failed to allocate bus: %s", strerror(-r));
+ goto finish;
+ }
- zero(stdout_ev);
- stdout_ev.events = EPOLLOUT|EPOLLET;
- stdout_ev.data.fd = STDOUT_FILENO;
+ r = sd_bus_set_fd(b, STDIN_FILENO, STDOUT_FILENO);
+ if (r < 0) {
+ log_error("Failed to set fds: %s", strerror(-r));
+ goto finish;
+ }
- zero(fd_ev);
- fd_ev.events = EPOLLIN|EPOLLOUT|EPOLLET;
- fd_ev.data.fd = fd;
+ r = sd_bus_set_server(b, 1, server_id);
+ if (r < 0) {
+ log_error("Failed to set server mode: %s", strerror(-r));
+ goto finish;
+ }
- if (epoll_ctl(ep, EPOLL_CTL_ADD, STDIN_FILENO, &stdin_ev) < 0 ||
- epoll_ctl(ep, EPOLL_CTL_ADD, STDOUT_FILENO, &stdout_ev) < 0 ||
- epoll_ctl(ep, EPOLL_CTL_ADD, fd, &fd_ev) < 0) {
- log_error("Failed to regiser fds in epoll: %m");
+ r = sd_bus_set_negotiate_fds(b, is_unix);
+ if (r < 0) {
+ log_error("Failed to set FD negotiation: %s", strerror(-r));
goto finish;
}
- do {
- struct epoll_event ev[16];
- ssize_t k;
- int i, nfds;
+ r = sd_bus_set_anonymous(b, true);
+ if (r < 0) {
+ log_error("Failed to set anonymous authentication: %s", strerror(-r));
+ goto finish;
+ }
- if ((nfds = epoll_wait(ep, ev, ELEMENTSOF(ev), -1)) < 0) {
+ r = sd_bus_start(b);
+ if (r < 0) {
+ log_error("Failed to start bus client: %s", strerror(-r));
+ goto finish;
+ }
- if (errno == EINTR || errno == EAGAIN)
- continue;
+ for (;;) {
+ _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ int events_a, events_b, fd;
+ uint64_t timeout_a, timeout_b, t;
+ struct timespec _ts, *ts;
- log_error("epoll_wait(): %m");
+ r = sd_bus_process(a, &m);
+ if (r < 0) {
+ log_error("Failed to process bus: %s", strerror(-r));
goto finish;
}
- assert(nfds >= 1);
-
- for (i = 0; i < nfds; i++) {
- if (ev[i].data.fd == STDIN_FILENO) {
-
- if (!stdin_rhup && (ev[i].events & (EPOLLHUP|EPOLLIN)))
- stdin_readable = true;
-
- } else if (ev[i].data.fd == STDOUT_FILENO) {
-
- if (ev[i].events & EPOLLHUP) {
- stdout_writable = false;
- stdout_whup = true;
- }
+ if (m) {
+ r = sd_bus_send(b, m, NULL);
+ if (r < 0) {
+ log_error("Failed to send message: %s", strerror(-r));
+ goto finish;
+ }
+ }
- if (!stdout_whup && (ev[i].events & EPOLLOUT))
- stdout_writable = true;
+ if (r > 0)
+ continue;
- } else if (ev[i].data.fd == fd) {
+ r = sd_bus_process(b, &m);
+ if (r < 0) {
+ log_error("Failed to process bus: %s", strerror(-r));
+ goto finish;
+ }
- if (ev[i].events & EPOLLHUP) {
- fd_writable = false;
- fd_whup = true;
- }
+ if (m) {
+ r = sd_bus_send(a, m, NULL);
+ if (r < 0) {
+ log_error("Failed to send message: %s", strerror(-r));
+ goto finish;
+ }
+ }
- if (!fd_rhup && (ev[i].events & (EPOLLHUP|EPOLLIN)))
- fd_readable = true;
+ if (r > 0)
+ continue;
- if (!fd_whup && (ev[i].events & EPOLLOUT))
- fd_writable = true;
- }
+ fd = sd_bus_get_fd(a);
+ if (fd < 0) {
+ log_error("Failed to get fd: %s", strerror(-r));
+ goto finish;
}
- while ((stdin_readable && in_buffer_full <= 0) ||
- (fd_writable && patch_in_buffer(in_buffer, &in_buffer_full) > 0) ||
- (fd_readable && out_buffer_full <= 0) ||
- (stdout_writable && out_buffer_full > 0)) {
-
- size_t in_buffer_good = 0;
-
- if (stdin_readable && in_buffer_full < BUFFER_SIZE) {
-
- if ((k = read(STDIN_FILENO, in_buffer + in_buffer_full, BUFFER_SIZE - in_buffer_full)) < 0) {
-
- if (errno == EAGAIN)
- stdin_readable = false;
- else if (errno == EPIPE || errno == ECONNRESET)
- k = 0;
- else {
- log_error("read(): %m");
- goto finish;
- }
- } else
- in_buffer_full += (size_t) k;
-
- if (k == 0) {
- stdin_rhup = true;
- stdin_readable = false;
- shutdown(STDIN_FILENO, SHUT_RD);
- close_nointr_nofail(STDIN_FILENO);
- }
- }
+ events_a = sd_bus_get_events(a);
+ if (events_a < 0) {
+ log_error("Failed to get events mask: %s", strerror(-r));
+ goto finish;
+ }
- in_buffer_good = patch_in_buffer(in_buffer, &in_buffer_full);
+ r = sd_bus_get_timeout(a, &timeout_a);
+ if (r < 0) {
+ log_error("Failed to get timeout: %s", strerror(-r));
+ goto finish;
+ }
- if (fd_writable && in_buffer_good > 0) {
+ events_b = sd_bus_get_events(b);
+ if (events_b < 0) {
+ log_error("Failed to get events mask: %s", strerror(-r));
+ goto finish;
+ }
- if ((k = write(fd, in_buffer, in_buffer_good)) < 0) {
+ r = sd_bus_get_timeout(b, &timeout_b);
+ if (r < 0) {
+ log_error("Failed to get timeout: %s", strerror(-r));
+ goto finish;
+ }
- if (errno == EAGAIN)
- fd_writable = false;
- else if (errno == EPIPE || errno == ECONNRESET) {
- fd_whup = true;
- fd_writable = false;
- shutdown(fd, SHUT_WR);
- } else {
- log_error("write(): %m");
- goto finish;
- }
+ t = timeout_a;
+ if (t == (uint64_t) -1 || (timeout_b != (uint64_t) -1 && timeout_b < timeout_a))
+ t = timeout_b;
- } else {
- assert(in_buffer_full >= (size_t) k);
- memmove(in_buffer, in_buffer + k, in_buffer_full - k);
- in_buffer_full -= k;
- }
- }
+ if (t == (uint64_t) -1)
+ ts = NULL;
+ else {
+ usec_t nw;
- if (fd_readable && out_buffer_full < BUFFER_SIZE) {
-
- if ((k = read(fd, out_buffer + out_buffer_full, BUFFER_SIZE - out_buffer_full)) < 0) {
-
- if (errno == EAGAIN)
- fd_readable = false;
- else if (errno == EPIPE || errno == ECONNRESET)
- k = 0;
- else {
- log_error("read(): %m");
- goto finish;
- }
- } else
- out_buffer_full += (size_t) k;
-
- if (k == 0) {
- fd_rhup = true;
- fd_readable = false;
- shutdown(fd, SHUT_RD);
- }
- }
+ nw = now(CLOCK_MONOTONIC);
+ if (t > nw)
+ t -= nw;
+ else
+ t = 0;
- if (stdout_writable && out_buffer_full > 0) {
-
- if ((k = write(STDOUT_FILENO, out_buffer, out_buffer_full)) < 0) {
-
- if (errno == EAGAIN)
- stdout_writable = false;
- else if (errno == EPIPE || errno == ECONNRESET) {
- stdout_whup = true;
- stdout_writable = false;
- shutdown(STDOUT_FILENO, SHUT_WR);
- close_nointr(STDOUT_FILENO);
- } else {
- log_error("write(): %m");
- goto finish;
- }
-
- } else {
- assert(out_buffer_full >= (size_t) k);
- memmove(out_buffer, out_buffer + k, out_buffer_full - k);
- out_buffer_full -= k;
- }
- }
+ ts = timespec_store(&_ts, t);
}
- if (stdin_rhup && in_buffer_full <= 0 && !fd_whup) {
- fd_whup = true;
- fd_writable = false;
- shutdown(fd, SHUT_WR);
- }
+ {
+ struct pollfd p[3] = {
+ {.fd = fd, .events = events_a, },
+ {.fd = STDIN_FILENO, .events = events_b & POLLIN, },
+ {.fd = STDOUT_FILENO, .events = events_b & POLLOUT, }};
- if (fd_rhup && out_buffer_full <= 0 && !stdout_whup) {
- stdout_whup = true;
- stdout_writable = false;
- shutdown(STDOUT_FILENO, SHUT_WR);
- close_nointr(STDOUT_FILENO);
+ r = ppoll(p, ELEMENTSOF(p), ts, NULL);
}
+ if (r < 0) {
+ log_error("ppoll() failed: %m");
+ goto finish;
+ }
+ }
- } while (!stdout_whup || !fd_whup);
-
- r = EXIT_SUCCESS;
+ r = 0;
finish:
- if (fd >= 0)
- close_nointr_nofail(fd);
-
- if (ep >= 0)
- close_nointr_nofail(ep);
-
- return r;
+ return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}
diff --git a/src/sysctl/sysctl.c b/src/sysctl/sysctl.c
index 035e0ec321..db18dd9f6e 100644
--- a/src/sysctl/sysctl.c
+++ b/src/sysctl/sysctl.c
@@ -34,29 +34,44 @@
#include "hashmap.h"
#include "path-util.h"
#include "conf-files.h"
+#include "fileio.h"
-#define PROC_SYS_PREFIX "/proc/sys/"
+static char **arg_prefixes = NULL;
-static char **arg_prefixes;
-static Hashmap *sysctl_options;
+static const char conf_file_dirs[] =
+ "/etc/sysctl.d\0"
+ "/run/sysctl.d\0"
+ "/usr/local/lib/sysctl.d\0"
+ "/usr/lib/sysctl.d\0"
+#ifdef HAVE_SPLIT_USR
+ "/lib/sysctl.d\0"
+#endif
+ ;
+
+static char *normalize_sysctl(char *s) {
+ char *n;
+
+ for (n = s; *n; n++)
+ if (*n == '.')
+ *n = '/';
+
+ return s;
+}
static int apply_sysctl(const char *property, const char *value) {
- char *p, *n;
+ _cleanup_free_ char *p = NULL;
+ char *n;
int r = 0, k;
log_debug("Setting '%s' to '%s'", property, value);
- p = new(char, sizeof(PROC_SYS_PREFIX) + strlen(property));
+ p = new(char, sizeof("/proc/sys/") + strlen(property));
if (!p)
return log_oom();
- n = stpcpy(p, PROC_SYS_PREFIX);
+ n = stpcpy(p, "/proc/sys/");
strcpy(n, property);
- for (; *n; n++)
- if (*n == '.')
- *n = '/';
-
if (!strv_isempty(arg_prefixes)) {
char **i;
bool good = false;
@@ -69,14 +84,12 @@ static int apply_sysctl(const char *property, const char *value) {
if (!good) {
log_debug("Skipping %s", p);
- free(p);
return 0;
}
}
- k = write_one_line_file(p, value);
+ k = write_string_file(p, value);
if (k < 0) {
-
log_full(k == -ENOENT ? LOG_DEBUG : LOG_WARNING,
"Failed to write '%s' to '%s': %s", value, p, strerror(-k));
@@ -84,16 +97,16 @@ static int apply_sysctl(const char *property, const char *value) {
r = k;
}
- free(p);
-
return r;
}
-static int apply_all(void) {
+static int apply_all(Hashmap *sysctl_options) {
int r = 0;
char *property, *value;
Iterator i;
+ assert(sysctl_options);
+
HASHMAP_FOREACH_KEY(value, property, sysctl_options, i) {
int k;
@@ -104,40 +117,39 @@ static int apply_all(void) {
return r;
}
-static int parse_file(const char *path, bool ignore_enoent) {
- FILE *f;
- int r = 0;
+static int parse_file(Hashmap *sysctl_options, const char *path, bool ignore_enoent) {
+ _cleanup_fclose_ FILE *f = NULL;
+ int r;
assert(path);
- f = fopen(path, "re");
- if (!f) {
- if (ignore_enoent && errno == ENOENT)
+ r = search_and_fopen_nulstr(path, "re", conf_file_dirs, &f);
+ if (r < 0) {
+ if (ignore_enoent && r == -ENOENT)
return 0;
- log_error("Failed to open file '%s', ignoring: %m", path);
- return -errno;
+ log_error("Failed to open file '%s', ignoring: %s", path, strerror(-r));
+ return r;
}
log_debug("parse: %s\n", path);
while (!feof(f)) {
- char l[LINE_MAX], *p, *value, *new_value, *property;
+ char l[LINE_MAX], *p, *value, *new_value, *property, *existing;
+ int k;
if (!fgets(l, sizeof(l), f)) {
if (feof(f))
break;
log_error("Failed to read file '%s', ignoring: %m", path);
- r = -errno;
- goto finish;
+ return -errno;
}
p = strstrip(l);
-
if (!*p)
continue;
- if (strchr(COMMENTS, *p))
+ if (strchr(COMMENTS "\n", *p))
continue;
value = strchr(p, '=');
@@ -152,40 +164,37 @@ static int parse_file(const char *path, bool ignore_enoent) {
*value = 0;
value++;
- property = strdup(strstrip(p));
- if (!property) {
- r = log_oom();
- goto finish;
+ p = normalize_sysctl(strstrip(p));
+ value = strstrip(value);
+
+ existing = hashmap_get(sysctl_options, p);
+ if (existing) {
+ if (!streq(value, existing))
+ log_warning("Duplicate assignment of %s in file '%s', ignoring.",
+ p, path);
+
+ continue;
}
- new_value = strdup(strstrip(value));
+ property = strdup(p);
+ if (!property)
+ return log_oom();
+
+ new_value = strdup(value);
if (!new_value) {
free(property);
- r = log_oom();
- goto finish;
+ return log_oom();
}
- r = hashmap_put(sysctl_options, property, new_value);
- if (r < 0) {
- if (r == -EEXIST) {
- /* ignore this "error" to avoid returning it
- * for the function when this is the last key
- * in the file being parsed. */
- r = 0;
- log_debug("Skipping previously assigned sysctl variable %s", property);
- } else
- log_error("Failed to add sysctl variable %s to hashmap: %s", property, strerror(-r));
-
+ k = hashmap_put(sysctl_options, property, new_value);
+ if (k < 0) {
+ log_error("Failed to add sysctl variable %s to hashmap: %s", property, strerror(-r));
free(property);
free(new_value);
- if (r != 0)
- goto finish;
+ return k;
}
}
-finish:
- fclose(f);
-
return r;
}
@@ -257,8 +266,7 @@ static int parse_argv(int argc, char *argv[]) {
int main(int argc, char *argv[]) {
int r = 0, k;
- char *property, *value;
- Iterator it;
+ Hashmap *sysctl_options;
r = parse_argv(argc, argv);
if (r <= 0)
@@ -282,54 +290,35 @@ int main(int argc, char *argv[]) {
int i;
for (i = optind; i < argc; i++) {
- k = parse_file(argv[i], false);
- if (k < 0)
+ k = parse_file(sysctl_options, argv[i], false);
+ if (k < 0 && r == 0)
r = k;
}
} else {
- char **files, **f;
+ _cleanup_strv_free_ char **files = NULL;
+ char **f;
- r = conf_files_list(&files, ".conf",
- "/etc/sysctl.d",
- "/run/sysctl.d",
- "/usr/local/lib/sysctl.d",
- "/usr/lib/sysctl.d",
-#ifdef HAVE_SPLIT_USR
- "/lib/sysctl.d",
-#endif
- NULL);
+ r = conf_files_list_nulstr(&files, ".conf", NULL, conf_file_dirs);
if (r < 0) {
log_error("Failed to enumerate sysctl.d files: %s", strerror(-r));
goto finish;
}
- /* We parse the files in decreasing order of precedence.
- * parse_file() will skip keys that were already assigned. */
+ r = parse_file(sysctl_options, "/etc/sysctl.conf", true);
- r = parse_file("/etc/sysctl.conf", true);
-
- f = files + strv_length(files) - 1;
- STRV_FOREACH_BACKWARDS(f, files) {
- k = parse_file(*f, true);
- if (k < 0)
+ STRV_FOREACH(f, files) {
+ k = parse_file(sysctl_options, *f, true);
+ if (k < 0 && r == 0)
r = k;
}
-
- strv_free(files);
}
- k = apply_all();
- if (k < 0)
+ k = apply_all(sysctl_options);
+ if (k < 0 && r == 0)
r = k;
finish:
- HASHMAP_FOREACH_KEY(value, property, sysctl_options, it) {
- hashmap_remove(sysctl_options, property);
- free(property);
- free(value);
- }
- hashmap_free(sysctl_options);
-
+ hashmap_free_free_free(sysctl_options);
strv_free(arg_prefixes);
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
diff --git a/src/system-update-generator/system-update-generator.c b/src/system-update-generator/system-update-generator.c
index 6660192f5e..13b8a0c426 100644
--- a/src/system-update-generator/system-update-generator.c
+++ b/src/system-update-generator/system-update-generator.c
@@ -36,7 +36,7 @@ static const char *arg_dest = "/tmp";
static int generate_symlink(void) {
struct stat st;
- char *p;
+ char _cleanup_free_ *p = NULL;
if (lstat("/system-update", &st) < 0) {
if (errno == ENOENT)
@@ -51,13 +51,10 @@ static int generate_symlink(void) {
return log_oom();
if (symlink(SYSTEM_DATA_UNIT_PATH "/system-update.target", p) < 0) {
- free(p);
- log_error("Failed to create symlink: %m");
+ log_error("Failed to create symlink %s: %m", p);
return -errno;
}
- free(p);
-
return 0;
}
diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c
index 2ebfff8daf..3cca861cf6 100644
--- a/src/systemctl/systemctl.c
+++ b/src/systemctl/systemctl.c
@@ -38,6 +38,7 @@
#include <systemd/sd-daemon.h>
#include <systemd/sd-shutdown.h>
+#include <systemd/sd-login.h>
#include "log.h"
#include "util.h"
@@ -65,11 +66,18 @@
#include "logs-show.h"
#include "path-util.h"
#include "socket-util.h"
+#include "fileio.h"
-static const char *arg_type = NULL;
-static const char *arg_load_state = NULL;
-static char **arg_property = NULL;
+static char **arg_types = NULL;
+static char **arg_load_states = NULL;
+static char **arg_properties = NULL;
static bool arg_all = false;
+static enum dependency {
+ DEPENDENCY_FORWARD,
+ DEPENDENCY_REVERSE,
+ DEPENDENCY_AFTER,
+ DEPENDENCY_BEFORE,
+} arg_dependency = DEPENDENCY_FORWARD;
static const char *arg_job_mode = "replace";
static UnitFileScope arg_scope = UNIT_FILE_SYSTEM;
static bool arg_no_block = false;
@@ -78,6 +86,8 @@ static bool arg_no_pager = false;
static bool arg_no_wtmp = false;
static bool arg_no_wall = false;
static bool arg_no_reload = false;
+static bool arg_show_types = false;
+static bool arg_ignore_inhibitors = false;
static bool arg_dry = false;
static bool arg_quiet = false;
static bool arg_full = false;
@@ -114,11 +124,6 @@ static enum action {
ACTION_CANCEL_SHUTDOWN,
_ACTION_MAX
} arg_action = ACTION_SYSTEMCTL;
-static enum dot {
- DOT_ALL,
- DOT_ORDER,
- DOT_REQUIRE
-} arg_dot = DOT_ALL;
static enum transport {
TRANSPORT_NORMAL,
TRANSPORT_SSH,
@@ -127,6 +132,7 @@ static enum transport {
static const char *arg_host = NULL;
static unsigned arg_lines = 10;
static OutputMode arg_output = OUTPUT_SHORT;
+static bool arg_plain = false;
static bool private_bus = false;
@@ -138,7 +144,7 @@ static void pager_open_if_enabled(void) {
if (arg_no_pager)
return;
- pager_open();
+ pager_open(false);
}
static void ask_password_agent_open_if_enabled(void) {
@@ -236,21 +242,18 @@ static void warn_wall(enum action a) {
return;
if (arg_wall) {
- char *p;
+ _cleanup_free_ char *p;
p = strv_join(arg_wall, " ");
if (!p) {
- log_error("Failed to join strings.");
+ log_oom();
return;
}
if (*p) {
utmp_wall(p, NULL);
- free(p);
return;
}
-
- free(p);
}
if (!table[a])
@@ -276,19 +279,6 @@ static bool avoid_bus(void) {
return false;
}
-struct unit_info {
- const char *id;
- const char *description;
- const char *load_state;
- const char *active_state;
- const char *sub_state;
- const char *following;
- const char *unit_path;
- uint32_t job_id;
- const char *job_type;
- const char *job_path;
-};
-
static int compare_unit_info(const void *a, const void *b) {
const char *d1, *d2;
const struct unit_info *u = a, *v = b;
@@ -299,7 +289,8 @@ static int compare_unit_info(const void *a, const void *b) {
if (d1 && d2) {
int r;
- if ((r = strcasecmp(d1, d2)) != 0)
+ r = strcasecmp(d1, d2);
+ if (r != 0)
return r;
}
@@ -312,9 +303,9 @@ static bool output_show_unit(const struct unit_info *u) {
if (arg_failed)
return streq(u->active_state, "failed");
- return (!arg_type || ((dot = strrchr(u->id, '.')) &&
- streq(dot+1, arg_type))) &&
- (!arg_load_state || streq(u->load_state, arg_load_state)) &&
+ return (!arg_types || ((dot = strrchr(u->id, '.')) &&
+ strv_find(arg_types, dot+1))) &&
+ (!arg_load_states || strv_find(arg_load_states, u->load_state)) &&
(arg_all || !(streq(u->active_state, "inactive")
|| u->following[0]) || u->job_id > 0);
}
@@ -345,7 +336,7 @@ static void output_units_list(const struct unit_info *unit_infos, unsigned c) {
if (!arg_full) {
unsigned basic_len;
- id_len = MIN(max_id_len, 25);
+ id_len = MIN(max_id_len, 25u);
basic_len = 5 + id_len + 5 + active_len + sub_len;
if (job_count)
basic_len += job_len + 1;
@@ -354,7 +345,7 @@ static void output_units_list(const struct unit_info *unit_infos, unsigned c) {
extra_len = columns() - basic_len;
/* Either UNIT already got 25, or is fully satisfied.
* Grant up to 25 to DESC now. */
- incr = MIN(extra_len, 25);
+ incr = MIN(extra_len, 25u);
desc_len += incr;
extra_len -= incr;
/* split the remaining space between UNIT and DESC,
@@ -369,9 +360,9 @@ static void output_units_list(const struct unit_info *unit_infos, unsigned c) {
id_len = max_id_len;
for (u = unit_infos; u < unit_infos + c; u++) {
- char *e;
- const char *on_loaded, *off_loaded;
- const char *on_active, *off_active;
+ _cleanup_free_ char *e = NULL;
+ const char *on_loaded, *off_loaded, *on = "";
+ const char *on_active, *off_active, *off = "";
if (!output_show_unit(u))
continue;
@@ -390,21 +381,21 @@ static void output_units_list(const struct unit_info *unit_infos, unsigned c) {
n_shown++;
if (streq(u->load_state, "error")) {
- on_loaded = ansi_highlight_red(true);
- off_loaded = ansi_highlight_red(false);
+ on_loaded = on = ansi_highlight_red(true);
+ off_loaded = off = ansi_highlight_red(false);
} else
on_loaded = off_loaded = "";
if (streq(u->active_state, "failed")) {
- on_active = ansi_highlight_red(true);
- off_active = ansi_highlight_red(false);
+ on_active = on = ansi_highlight_red(true);
+ off_active = off = ansi_highlight_red(false);
} else
on_active = off_active = "";
e = arg_full ? NULL : ellipsize(u->id, id_len, 33);
- printf("%-*s %s%-6s%s %s%-*s %-*s%s %-*s",
- id_len, e ? e : u->id,
+ printf("%s%-*s%s %s%-6s%s %s%-*s %-*s%s %-*s",
+ on, id_len, e ? e : u->id, off,
on_loaded, u->load_state, off_loaded,
on_active, active_len, u->active_state,
sub_len, u->sub_state, off_active,
@@ -413,8 +404,6 @@ static void output_units_list(const struct unit_info *unit_infos, unsigned c) {
printf("%.*s\n", desc_len, u->description);
else
printf("%s\n", u->description);
-
- free(e);
}
if (!arg_no_legend) {
@@ -445,96 +434,334 @@ static void output_units_list(const struct unit_info *unit_infos, unsigned c) {
}
}
-static int list_units(DBusConnection *bus, char **args) {
- DBusMessage *reply = NULL;
+static int get_unit_list(DBusConnection *bus, DBusMessage **reply,
+ struct unit_info **unit_infos, unsigned *c) {
+ DBusMessageIter iter, sub;
+ size_t size = 0;
int r;
- DBusMessageIter iter, sub, sub2;
- unsigned c = 0, n_units = 0;
- struct unit_info *unit_infos = NULL;
- pager_open_if_enabled();
+ assert(bus);
+ assert(unit_infos);
+ assert(c);
- r = bus_method_call_with_reply (
+ r = bus_method_call_with_reply(
bus,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
"ListUnits",
- &reply,
+ reply,
NULL,
DBUS_TYPE_INVALID);
- if (r)
- goto finish;
+ if (r < 0)
+ return r;
- if (!dbus_message_iter_init(reply, &iter) ||
+ if (!dbus_message_iter_init(*reply, &iter) ||
dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) {
log_error("Failed to parse reply.");
- r = -EIO;
- goto finish;
+ return -EIO;
}
dbus_message_iter_recurse(&iter, &sub);
while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
- struct unit_info *u;
+ if (!GREEDY_REALLOC(*unit_infos, size, *c + 1))
+ return log_oom();
- if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
+ bus_parse_unit_info(&sub, *unit_infos + *c);
+ (*c)++;
+
+ dbus_message_iter_next(&sub);
+ }
+
+ return 0;
+}
+
+static int list_units(DBusConnection *bus, char **args) {
+ _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
+ _cleanup_free_ struct unit_info *unit_infos = NULL;
+ unsigned c = 0;
+ int r;
+
+ pager_open_if_enabled();
+
+ r = get_unit_list(bus, &reply, &unit_infos, &c);
+ if (r < 0)
+ return r;
+
+ qsort(unit_infos, c, sizeof(struct unit_info), compare_unit_info);
+
+ output_units_list(unit_infos, c);
+
+ return 0;
+}
+
+static int get_triggered_units(DBusConnection *bus, const char* unit_path,
+ char*** triggered)
+{
+ const char *interface = "org.freedesktop.systemd1.Unit",
+ *triggers_property = "Triggers";
+ _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
+ DBusMessageIter iter, sub;
+ int r;
+
+ r = bus_method_call_with_reply(bus,
+ "org.freedesktop.systemd1",
+ unit_path,
+ "org.freedesktop.DBus.Properties",
+ "Get",
+ &reply,
+ NULL,
+ DBUS_TYPE_STRING, &interface,
+ DBUS_TYPE_STRING, &triggers_property,
+ DBUS_TYPE_INVALID);
+ if (r < 0)
+ return r;
+
+ if (!dbus_message_iter_init(reply, &iter) ||
+ dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
+ log_error("Failed to parse reply.");
+ return -EBADMSG;
+ }
+
+ dbus_message_iter_recurse(&iter, &sub);
+ dbus_message_iter_recurse(&sub, &iter);
+ sub = iter;
+
+ while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
+ const char *unit;
+
+ if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING) {
log_error("Failed to parse reply.");
- r = -EIO;
- goto finish;
+ return -EBADMSG;
}
- if (c >= n_units) {
- struct unit_info *w;
+ dbus_message_iter_get_basic(&sub, &unit);
+ r = strv_extend(triggered, unit);
+ if (r < 0)
+ return r;
- n_units = MAX(2*c, 16);
- w = realloc(unit_infos, sizeof(struct unit_info) * n_units);
+ dbus_message_iter_next(&sub);
+ }
- if (!w) {
- log_error("Failed to allocate unit array.");
- r = -ENOMEM;
- goto finish;
- }
+ return 0;
+}
- unit_infos = w;
- }
+static int get_listening(DBusConnection *bus, const char* unit_path,
+ char*** listen, unsigned *c)
+{
+ const char *interface = "org.freedesktop.systemd1.Socket",
+ *listen_property = "Listen";
+ _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
+ DBusMessageIter iter, sub;
+ int r;
- u = unit_infos+c;
+ r = bus_method_call_with_reply(bus,
+ "org.freedesktop.systemd1",
+ unit_path,
+ "org.freedesktop.DBus.Properties",
+ "Get",
+ &reply,
+ NULL,
+ DBUS_TYPE_STRING, &interface,
+ DBUS_TYPE_STRING, &listen_property,
+ DBUS_TYPE_INVALID);
+ if (r < 0)
+ return r;
- dbus_message_iter_recurse(&sub, &sub2);
+ if (!dbus_message_iter_init(reply, &iter) ||
+ dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
+ log_error("Failed to parse reply.");
+ return -EBADMSG;
+ }
+
+ dbus_message_iter_recurse(&iter, &sub);
+ dbus_message_iter_recurse(&sub, &iter);
+ sub = iter;
+
+ while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
+ DBusMessageIter sub2;
+ const char *type, *path;
- if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &u->id, true) < 0 ||
- bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &u->description, true) < 0 ||
- bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &u->load_state, true) < 0 ||
- bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &u->active_state, true) < 0 ||
- bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &u->sub_state, true) < 0 ||
- bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &u->following, true) < 0 ||
- bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &u->unit_path, true) < 0 ||
- bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT32, &u->job_id, true) < 0 ||
- bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &u->job_type, true) < 0 ||
- bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &u->job_path, false) < 0) {
+ if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
log_error("Failed to parse reply.");
- r = -EIO;
- goto finish;
+ return -EBADMSG;
+ }
+
+ dbus_message_iter_recurse(&sub, &sub2);
+
+ if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &type, true) >= 0 &&
+ bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &path, false) >= 0) {
+ r = strv_extend(listen, type);
+ if (r < 0)
+ return r;
+
+ r = strv_extend(listen, path);
+ if (r < 0)
+ return r;
+
+ (*c) ++;
}
dbus_message_iter_next(&sub);
- c++;
}
- if (c > 0) {
- qsort(unit_infos, c, sizeof(struct unit_info), compare_unit_info);
- output_units_list(unit_infos, c);
+ return 0;
+}
+
+struct socket_info {
+ const char* id;
+
+ char* type;
+ char* path;
+
+ /* Note: triggered is a list here, although it almost certainly
+ * will always be one unit. Nevertheless, dbus API allows for multiple
+ * values, so let's follow that.*/
+ char** triggered;
+
+ /* The strv above is shared. free is set only in the first one. */
+ bool own_triggered;
+};
+
+static int socket_info_compare(struct socket_info *a, struct socket_info *b) {
+ int o = strcmp(a->path, b->path);
+ if (o == 0)
+ o = strcmp(a->type, b->type);
+ return o;
+}
+
+static int output_sockets_list(struct socket_info *socket_infos, unsigned cs) {
+ struct socket_info *s;
+ unsigned pathlen = sizeof("LISTEN") - 1,
+ typelen = (sizeof("TYPE") - 1) * arg_show_types,
+ socklen = sizeof("UNIT") - 1,
+ servlen = sizeof("ACTIVATES") - 1;
+ const char *on, *off;
+
+ for (s = socket_infos; s < socket_infos + cs; s++) {
+ char **a;
+ unsigned tmp = 0;
+
+ socklen = MAX(socklen, strlen(s->id));
+ if (arg_show_types)
+ typelen = MAX(typelen, strlen(s->type));
+ pathlen = MAX(pathlen, strlen(s->path));
+
+ STRV_FOREACH(a, s->triggered)
+ tmp += strlen(*a) + 2*(a != s->triggered);
+ servlen = MAX(servlen, tmp);
+ }
+
+ if (cs) {
+ printf("%-*s %-*.*s%-*s %s\n",
+ pathlen, "LISTEN",
+ typelen + arg_show_types, typelen + arg_show_types, "TYPE ",
+ socklen, "UNIT",
+ "ACTIVATES");
+
+ for (s = socket_infos; s < socket_infos + cs; s++) {
+ char **a;
+
+ if (arg_show_types)
+ printf("%-*s %-*s %-*s",
+ pathlen, s->path, typelen, s->type, socklen, s->id);
+ else
+ printf("%-*s %-*s",
+ pathlen, s->path, socklen, s->id);
+ STRV_FOREACH(a, s->triggered)
+ printf("%s %s",
+ a == s->triggered ? "" : ",", *a);
+ printf("\n");
+ }
+
+ on = ansi_highlight(true);
+ off = ansi_highlight(false);
+ printf("\n");
+ } else {
+ on = ansi_highlight_red(true);
+ off = ansi_highlight_red(false);
}
-finish:
- if (reply)
- dbus_message_unref(reply);
+ printf("%s%u sockets listed.%s\n", on, cs, off);
+ if (!arg_all)
+ printf("Pass --all to see loaded but inactive sockets, too.\n");
- free(unit_infos);
+ return 0;
+}
- return r;
+static int list_sockets(DBusConnection *bus, char **args) {
+ _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
+ _cleanup_free_ struct unit_info *unit_infos = NULL;
+ struct socket_info *socket_infos = NULL;
+ const struct unit_info *u;
+ struct socket_info *s;
+ unsigned cu = 0, cs = 0;
+ size_t size = 0;
+ int r;
+
+ pager_open_if_enabled();
+
+ r = get_unit_list(bus, &reply, &unit_infos, &cu);
+ if (r < 0)
+ return r;
+
+ for (u = unit_infos; u < unit_infos + cu; u++) {
+ const char *dot;
+ _cleanup_strv_free_ char **listen = NULL, **triggered = NULL;
+ unsigned c = 0, i;
+
+ if (!output_show_unit(u))
+ continue;
+
+ if ((dot = strrchr(u->id, '.')) && !streq(dot+1, "socket"))
+ continue;
+
+ r = get_triggered_units(bus, u->unit_path, &triggered);
+ if (r < 0)
+ goto cleanup;
+
+ r = get_listening(bus, u->unit_path, &listen, &c);
+ if (r < 0)
+ goto cleanup;
+
+ if (!GREEDY_REALLOC(socket_infos, size, cs + c)) {
+ r = log_oom();
+ goto cleanup;
+ }
+
+ for (i = 0; i < c; i++)
+ socket_infos[cs + i] = (struct socket_info) {
+ .id = u->id,
+ .type = listen[i*2],
+ .path = listen[i*2 + 1],
+ .triggered = triggered,
+ .own_triggered = i==0,
+ };
+
+ /* from this point on we will cleanup those socket_infos */
+ cs += c;
+ free(listen);
+ listen = triggered = NULL; /* avoid cleanup */
+ }
+
+ qsort(socket_infos, cs, sizeof(struct socket_info),
+ (__compar_fn_t) socket_info_compare);
+
+ output_sockets_list(socket_infos, cs);
+
+ cleanup:
+ assert(cs == 0 || socket_infos);
+ for (s = socket_infos; s < socket_infos + cs; s++) {
+ free(s->type);
+ free(s->path);
+ if (s->own_triggered)
+ strv_free(s->triggered);
+ }
+ free(socket_infos);
+
+ return 0;
}
static int compare_unit_file_list(const void *a, const void *b) {
@@ -558,7 +785,7 @@ static int compare_unit_file_list(const void *a, const void *b) {
static bool output_show_unit_file(const UnitFileList *u) {
const char *dot;
- return !arg_type || ((dot = strrchr(u->path, '.')) && streq(dot+1, arg_type));
+ return !arg_types || ((dot = strrchr(u->path, '.')) && strv_find(arg_types, dot+1));
}
static void output_unit_file_list(const UnitFileList *units, unsigned c) {
@@ -577,7 +804,7 @@ static void output_unit_file_list(const UnitFileList *units, unsigned c) {
if (!arg_full) {
unsigned basic_cols;
- id_cols = MIN(max_id_len, 25);
+ id_cols = MIN(max_id_len, 25u);
basic_cols = 1 + id_cols + state_cols;
if (basic_cols < (unsigned) columns())
id_cols += MIN(columns() - basic_cols, max_id_len - id_cols);
@@ -588,7 +815,7 @@ static void output_unit_file_list(const UnitFileList *units, unsigned c) {
printf("%-*s %-*s\n", id_cols, "UNIT FILE", state_cols, "STATE");
for (u = units; u < units + c; u++) {
- char *e;
+ _cleanup_free_ char *e = NULL;
const char *on, *off;
const char *id;
@@ -616,8 +843,6 @@ static void output_unit_file_list(const UnitFileList *units, unsigned c) {
printf("%-*s %s%-*s%s\n",
id_cols, e ? e : id,
on, state_cols, unit_file_state_to_string(u->state), off);
-
- free(e);
}
if (!arg_no_legend)
@@ -625,11 +850,11 @@ static void output_unit_file_list(const UnitFileList *units, unsigned c) {
}
static int list_unit_files(DBusConnection *bus, char **args) {
- DBusMessage *reply = NULL;
- int r;
+ _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
+ _cleanup_free_ UnitFileList *units = NULL;
DBusMessageIter iter, sub, sub2;
unsigned c = 0, n_units = 0;
- UnitFileList *units = NULL;
+ int r;
pager_open_if_enabled();
@@ -663,7 +888,7 @@ static int list_unit_files(DBusConnection *bus, char **args) {
hashmap_free(h);
} else {
- r = bus_method_call_with_reply (
+ r = bus_method_call_with_reply(
bus,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
@@ -672,15 +897,14 @@ static int list_unit_files(DBusConnection *bus, char **args) {
&reply,
NULL,
DBUS_TYPE_INVALID);
- if (r)
- goto finish;
+ if (r < 0)
+ return r;
if (!dbus_message_iter_init(reply, &iter) ||
dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) {
log_error("Failed to parse reply.");
- r = -EIO;
- goto finish;
+ return -EIO;
}
dbus_message_iter_recurse(&iter, &sub);
@@ -689,36 +913,27 @@ static int list_unit_files(DBusConnection *bus, char **args) {
UnitFileList *u;
const char *state;
- if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
- log_error("Failed to parse reply.");
- r = -EIO;
- goto finish;
- }
+ assert(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT);
if (c >= n_units) {
UnitFileList *w;
- n_units = MAX(2*c, 16);
+ n_units = MAX(2*c, 16u);
w = realloc(units, sizeof(struct UnitFileList) * n_units);
-
- if (!w) {
- log_error("Failed to allocate unit array.");
- r = -ENOMEM;
- goto finish;
- }
+ if (!w)
+ return log_oom();
units = w;
}
- u = units+c;
+ u = units + c;
dbus_message_iter_recurse(&sub, &sub2);
if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &u->path, true) < 0 ||
bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &state, false) < 0) {
log_error("Failed to parse reply.");
- r = -EIO;
- goto finish;
+ return -EIO;
}
u->state = unit_file_state_from_string(state);
@@ -733,99 +948,95 @@ static int list_unit_files(DBusConnection *bus, char **args) {
output_unit_file_list(units, c);
}
- r = 0;
-
-finish:
- if (reply)
- dbus_message_unref(reply);
-
- free(units);
-
- return r;
+ return 0;
}
-static int dot_one_property(const char *name, const char *prop, DBusMessageIter *iter) {
- static const char * const colors[] = {
- "Requires", "[color=\"black\"]",
- "RequiresOverridable", "[color=\"black\"]",
- "Requisite", "[color=\"darkblue\"]",
- "RequisiteOverridable", "[color=\"darkblue\"]",
- "Wants", "[color=\"grey66\"]",
- "Conflicts", "[color=\"red\"]",
- "ConflictedBy", "[color=\"red\"]",
- "After", "[color=\"green\"]"
- };
-
- const char *c = NULL;
- unsigned i;
-
- assert(name);
- assert(prop);
- assert(iter);
-
- for (i = 0; i < ELEMENTSOF(colors); i += 2)
- if (streq(colors[i], prop)) {
- c = colors[i+1];
- break;
+static int list_dependencies_print(const char *name, int level, unsigned int branches, bool last) {
+ int i;
+ _cleanup_free_ char *n = NULL;
+ size_t len = 0;
+ size_t max_len = MAX(columns(),20u);
+
+ if (!arg_plain) {
+ for (i = level - 1; i >= 0; i--) {
+ len += 2;
+ if(len > max_len - 3 && !arg_full) {
+ printf("%s...\n",max_len % 2 ? "" : " ");
+ return 0;
+ }
+ printf("%s", draw_special_char(branches & (1 << i) ? DRAW_TREE_VERT : DRAW_TREE_SPACE));
}
+ len += 2;
+ if(len > max_len - 3 && !arg_full) {
+ printf("%s...\n",max_len % 2 ? "" : " ");
+ return 0;
+ }
+ printf("%s", draw_special_char(last ? DRAW_TREE_RIGHT : DRAW_TREE_BRANCH));
+ }
- if (!c)
+ if(arg_full){
+ printf("%s\n", name);
return 0;
+ }
- if (arg_dot != DOT_ALL)
- if ((arg_dot == DOT_ORDER) != streq(prop, "After"))
- return 0;
-
- switch (dbus_message_iter_get_arg_type(iter)) {
+ n = ellipsize(name, max_len-len, 100);
+ if(!n)
+ return log_oom();
- case DBUS_TYPE_ARRAY:
+ printf("%s\n", n);
+ return 0;
+}
- if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRING) {
- DBusMessageIter sub;
+static int list_dependencies_get_dependencies(DBusConnection *bus, const char *name, char ***deps) {
+ static const char *dependencies[] = {
+ [DEPENDENCY_FORWARD] = "Requires\0"
+ "RequiresOverridable\0"
+ "Requisite\0"
+ "RequisiteOverridable\0"
+ "Wants\0",
+ [DEPENDENCY_REVERSE] = "RequiredBy\0"
+ "RequiredByOverridable\0"
+ "WantedBy\0"
+ "PartOf\0",
+ [DEPENDENCY_AFTER] = "After\0",
+ [DEPENDENCY_BEFORE] = "Before\0",
+ };
- dbus_message_iter_recurse(iter, &sub);
+ _cleanup_free_ char *path;
+ const char *interface = "org.freedesktop.systemd1.Unit";
- while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
- const char *s;
+ _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
+ DBusMessageIter iter, sub, sub2, sub3;
- assert(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING);
- dbus_message_iter_get_basic(&sub, &s);
- printf("\t\"%s\"->\"%s\" %s;\n", name, s, c);
+ int r = 0;
+ char **ret = NULL;
- dbus_message_iter_next(&sub);
- }
+ assert(bus);
+ assert(name);
+ assert(deps);
- return 0;
- }
+ path = unit_dbus_path_from_name(name);
+ if (path == NULL) {
+ r = -EINVAL;
+ goto finish;
}
- return 0;
-}
-
-static int dot_one(DBusConnection *bus, const char *name, const char *path) {
- DBusMessage *reply = NULL;
- const char *interface = "org.freedesktop.systemd1.Unit";
- int r;
- DBusMessageIter iter, sub, sub2, sub3;
-
- assert(path);
-
- r = bus_method_call_with_reply (
- bus,
- "org.freedesktop.systemd1",
- path,
- "org.freedesktop.DBus.Properties",
- "GetAll",
- &reply,
- NULL,
- DBUS_TYPE_STRING, &interface,
- DBUS_TYPE_INVALID);
- if (r)
+ r = bus_method_call_with_reply(
+ bus,
+ "org.freedesktop.systemd1",
+ path,
+ "org.freedesktop.DBus.Properties",
+ "GetAll",
+ &reply,
+ NULL,
+ DBUS_TYPE_STRING, &interface,
+ DBUS_TYPE_INVALID);
+ if (r < 0)
goto finish;
if (!dbus_message_iter_init(reply, &iter) ||
- dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
- dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_DICT_ENTRY) {
+ dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
+ dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_DICT_ENTRY) {
log_error("Failed to parse reply.");
r = -EIO;
goto finish;
@@ -836,12 +1047,7 @@ static int dot_one(DBusConnection *bus, const char *name, const char *path) {
while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
const char *prop;
- if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_DICT_ENTRY) {
- log_error("Failed to parse reply.");
- r = -EIO;
- goto finish;
- }
-
+ assert(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_DICT_ENTRY);
dbus_message_iter_recurse(&sub, &sub2);
if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &prop, true) < 0) {
@@ -850,118 +1056,203 @@ static int dot_one(DBusConnection *bus, const char *name, const char *path) {
goto finish;
}
- if (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_VARIANT) {
+ if (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_VARIANT) {
log_error("Failed to parse reply.");
r = -EIO;
goto finish;
}
dbus_message_iter_recurse(&sub2, &sub3);
+ dbus_message_iter_next(&sub);
- if (dot_one_property(name, prop, &sub3)) {
- log_error("Failed to parse reply.");
- r = -EIO;
- goto finish;
- }
+ assert(arg_dependency < ELEMENTSOF(dependencies));
+ if (!nulstr_contains(dependencies[arg_dependency], prop))
+ continue;
- dbus_message_iter_next(&sub);
- }
+ if (dbus_message_iter_get_arg_type(&sub3) == DBUS_TYPE_ARRAY) {
+ if (dbus_message_iter_get_element_type(&sub3) == DBUS_TYPE_STRING) {
+ DBusMessageIter sub4;
+ dbus_message_iter_recurse(&sub3, &sub4);
-finish:
- if (reply)
- dbus_message_unref(reply);
+ while (dbus_message_iter_get_arg_type(&sub4) != DBUS_TYPE_INVALID) {
+ const char *s;
+
+ assert(dbus_message_iter_get_arg_type(&sub4) == DBUS_TYPE_STRING);
+ dbus_message_iter_get_basic(&sub4, &s);
+
+ r = strv_extend(&ret, s);
+ if (r < 0) {
+ log_oom();
+ goto finish;
+ }
+ dbus_message_iter_next(&sub4);
+ }
+ }
+ }
+ }
+finish:
+ if (r < 0)
+ strv_free(ret);
+ else
+ *deps = ret;
return r;
}
-static int dot(DBusConnection *bus, char **args) {
- DBusMessage *reply = NULL;
- int r;
- DBusMessageIter iter, sub, sub2;
+static int list_dependencies_compare(const void *_a, const void *_b) {
+ const char **a = (const char**) _a, **b = (const char**) _b;
+ if (unit_name_to_type(*a) == UNIT_TARGET && unit_name_to_type(*b) != UNIT_TARGET)
+ return 1;
+ if (unit_name_to_type(*a) != UNIT_TARGET && unit_name_to_type(*b) == UNIT_TARGET)
+ return -1;
+ return strcasecmp(*a, *b);
+}
- r = bus_method_call_with_reply (
- bus,
- "org.freedesktop.systemd1",
- "/org/freedesktop/systemd1",
- "org.freedesktop.systemd1.Manager",
- "ListUnits",
- &reply,
- NULL,
- DBUS_TYPE_INVALID);
- if (r)
- goto finish;
+static int list_dependencies_one(DBusConnection *bus, const char *name, int level, char ***units, unsigned int branches) {
+ _cleanup_strv_free_ char **deps = NULL, **u;
+ char **c;
+ int r = 0;
- if (!dbus_message_iter_init(reply, &iter) ||
- dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
- dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) {
- log_error("Failed to parse reply.");
- r = -EIO;
- goto finish;
- }
+ u = strv_append(*units, name);
+ if (!u)
+ return log_oom();
- printf("digraph systemd {\n");
+ r = list_dependencies_get_dependencies(bus, name, &deps);
+ if (r < 0)
+ return r;
- dbus_message_iter_recurse(&iter, &sub);
- while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
- const char *id, *description, *load_state, *active_state, *sub_state, *following, *unit_path;
+ qsort(deps, strv_length(deps), sizeof (char*), list_dependencies_compare);
- if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
- log_error("Failed to parse reply.");
- r = -EIO;
- goto finish;
+ STRV_FOREACH(c, deps) {
+ if (strv_contains(u, *c)) {
+ if (!arg_plain) {
+ r = list_dependencies_print("...", level + 1, (branches << 1) | (c[1] == NULL ? 0 : 1), 1);
+ if (r < 0)
+ return r;
+ }
+ continue;
}
- dbus_message_iter_recurse(&sub, &sub2);
+ r = list_dependencies_print(*c, level, branches, c[1] == NULL);
+ if (r < 0)
+ return r;
- if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &id, true) < 0 ||
- bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &description, true) < 0 ||
- bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &load_state, true) < 0 ||
- bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &active_state, true) < 0 ||
- bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &sub_state, true) < 0 ||
- bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &following, true) < 0 ||
- bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &unit_path, true) < 0) {
- log_error("Failed to parse reply.");
- r = -EIO;
- goto finish;
+ if (arg_all || unit_name_to_type(*c) == UNIT_TARGET) {
+ r = list_dependencies_one(bus, *c, level + 1, &u, (branches << 1) | (c[1] == NULL ? 0 : 1));
+ if(r < 0)
+ return r;
}
+ }
+ if (arg_plain) {
+ strv_free(*units);
+ *units = u;
+ u = NULL;
+ }
+ return 0;
+}
- if ((r = dot_one(bus, id, unit_path)) < 0)
- goto finish;
+static int list_dependencies(DBusConnection *bus, char **args) {
+ _cleanup_free_ char *unit = NULL;
+ _cleanup_strv_free_ char **units = NULL;
+ const char *u;
- /* printf("\t\"%s\";\n", id); */
- dbus_message_iter_next(&sub);
+ assert(bus);
+
+ if (args[1]) {
+ unit = unit_name_mangle(args[1]);
+ if (!unit)
+ return log_oom();
+ u = unit;
+ } else
+ u = SPECIAL_DEFAULT_TARGET;
+
+ pager_open_if_enabled();
+
+ puts(u);
+
+ return list_dependencies_one(bus, u, 0, &units, 0);
+}
+
+struct job_info {
+ uint32_t id;
+ char *name, *type, *state;
+};
+
+static void list_jobs_print(struct job_info* jobs, size_t n) {
+ size_t i;
+ struct job_info *j;
+ const char *on, *off;
+ bool shorten = false;
+
+ assert(n == 0 || jobs);
+
+ if (n == 0) {
+ on = ansi_highlight_green(true);
+ off = ansi_highlight_green(false);
+
+ printf("%sNo jobs running.%s\n", on, off);
+ return;
}
- printf("}\n");
+ pager_open_if_enabled();
- log_info(" Color legend: black = Requires\n"
- " dark blue = Requisite\n"
- " dark grey = Wants\n"
- " red = Conflicts\n"
- " green = After\n");
+ {
+ /* JOB UNIT TYPE STATE */
+ unsigned l0 = 3, l1 = 4, l2 = 4, l3 = 5;
- if (on_tty())
- log_notice("-- You probably want to process this output with graphviz' dot tool.\n"
- "-- Try a shell pipeline like 'systemctl dot | dot -Tsvg > systemd.svg'!\n");
+ for (i = 0, j = jobs; i < n; i++, j++) {
+ assert(j->name && j->type && j->state);
+ l0 = MAX(l0, DECIMAL_STR_WIDTH(j->id));
+ l1 = MAX(l1, strlen(j->name));
+ l2 = MAX(l2, strlen(j->type));
+ l3 = MAX(l3, strlen(j->state));
+ }
- r = 0;
+ if (!arg_full && l0 + 1 + l1 + l2 + 1 + l3 > columns()) {
+ l1 = MAX(33u, columns() - l0 - l2 - l3 - 3);
+ shorten = true;
+ }
-finish:
- if (reply)
- dbus_message_unref(reply);
+ if (on_tty())
+ printf("%*s %-*s %-*s %-*s\n",
+ l0, "JOB",
+ l1, "UNIT",
+ l2, "TYPE",
+ l3, "STATE");
- return r;
+ for (i = 0, j = jobs; i < n; i++, j++) {
+ _cleanup_free_ char *e = NULL;
+
+ if (streq(j->state, "running")) {
+ on = ansi_highlight(true);
+ off = ansi_highlight(false);
+ } else
+ on = off = "";
+
+ e = shorten ? ellipsize(j->name, l1, 33) : NULL;
+ printf("%*u %s%-*s%s %-*s %s%-*s%s\n",
+ l0, j->id,
+ on, l1, e ? e : j->name, off,
+ l2, j->type,
+ on, l3, j->state, off);
+ }
+ }
+
+ on = ansi_highlight(true);
+ off = ansi_highlight(false);
+
+ if (on_tty())
+ printf("\n%s%zu jobs listed%s.\n", on, n, off);
}
static int list_jobs(DBusConnection *bus, char **args) {
- DBusMessage *reply = NULL;
- int r;
+ _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
DBusMessageIter iter, sub, sub2;
- unsigned k = 0;
-
- pager_open_if_enabled();
+ int r;
+ struct job_info *jobs = NULL;
+ size_t size = 0, used = 0;
- r = bus_method_call_with_reply (
+ r = bus_method_call_with_reply(
bus,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
@@ -970,31 +1261,25 @@ static int list_jobs(DBusConnection *bus, char **args) {
&reply,
NULL,
DBUS_TYPE_INVALID);
- if (r)
- goto finish;
+ if (r < 0)
+ return r;
if (!dbus_message_iter_init(reply, &iter) ||
dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) {
log_error("Failed to parse reply.");
- r = -EIO;
- goto finish;
+ return -EIO;
}
dbus_message_iter_recurse(&iter, &sub);
- if (on_tty())
- printf("%4s %-25s %-15s %-7s\n", "JOB", "UNIT", "TYPE", "STATE");
-
while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
const char *name, *type, *state, *job_path, *unit_path;
uint32_t id;
- char *e;
if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
log_error("Failed to parse reply.");
- r = -EIO;
- goto finish;
+ return -EIO;
}
dbus_message_iter_recurse(&sub, &sub2);
@@ -1010,36 +1295,50 @@ static int list_jobs(DBusConnection *bus, char **args) {
goto finish;
}
- e = arg_full ? NULL : ellipsize(name, 25, 33);
- printf("%4u %-25s %-15s %-7s\n", id, e ? e : name, type, state);
- free(e);
+ if (!GREEDY_REALLOC(jobs, size, used + 1)) {
+ r = log_oom();
+ goto finish;
+ }
- k++;
+ jobs[used++] = (struct job_info) { id,
+ strdup(name),
+ strdup(type),
+ strdup(state) };
+ if (!jobs[used-1].name || !jobs[used-1].type || !jobs[used-1].state) {
+ r = log_oom();
+ goto finish;
+ }
dbus_message_iter_next(&sub);
}
- if (on_tty())
- printf("\n%u jobs listed.\n", k);
+ list_jobs_print(jobs, used);
- r = 0;
-
-finish:
- if (reply)
- dbus_message_unref(reply);
+ finish:
+ while (used--) {
+ free(jobs[used].name);
+ free(jobs[used].type);
+ free(jobs[used].state);
+ }
+ free(jobs);
- return r;
+ return 0;
}
static int load_unit(DBusConnection *bus, char **args) {
- int r = 0;
- char **name, *n;
+ char **name;
assert(args);
STRV_FOREACH(name, args+1) {
+ _cleanup_free_ char *n = NULL;
+ int r;
+
n = unit_name_mangle(*name);
- r = bus_method_call_with_reply (
+ if (!n)
+ return log_oom();
+
+ r = bus_method_call_with_reply(
bus,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
@@ -1047,20 +1346,16 @@ static int load_unit(DBusConnection *bus, char **args) {
"LoadUnit",
NULL,
NULL,
- DBUS_TYPE_STRING, n ? &n : name,
+ DBUS_TYPE_STRING, &n,
DBUS_TYPE_INVALID);
- free(n);
- if (r)
- goto finish;
+ if (r < 0)
+ return r;
}
-finish:
- return r;
+ return 0;
}
static int cancel_job(DBusConnection *bus, char **args) {
- DBusMessage *reply = NULL;
- int r = 0;
char **name;
assert(args);
@@ -1069,71 +1364,50 @@ static int cancel_job(DBusConnection *bus, char **args) {
return daemon_reload(bus, args);
STRV_FOREACH(name, args+1) {
- unsigned id;
- const char *path;
+ uint32_t id;
+ int r;
- r = safe_atou(*name, &id);
+ r = safe_atou32(*name, &id);
if (r < 0) {
log_error("Failed to parse job id: %s", strerror(-r));
- goto finish;
+ return r;
}
- assert_cc(sizeof(uint32_t) == sizeof(id));
- r = bus_method_call_with_reply (
+ r = bus_method_call_with_reply(
bus,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
- "GetJob",
- &reply,
- NULL,
- DBUS_TYPE_UINT32, &id,
- DBUS_TYPE_INVALID);
- if (r)
- goto finish;
-
- if (!dbus_message_get_args(reply, NULL,
- DBUS_TYPE_OBJECT_PATH, &path,
- DBUS_TYPE_INVALID)) {
- log_error("Failed to parse reply");
- dbus_message_unref(reply);
- r = -EIO;
- goto finish;
- }
- dbus_message_unref(reply);
-
- r = bus_method_call_with_reply (
- bus,
- "org.freedesktop.systemd1",
- path,
- "org.freedesktop.systemd1.Job",
- "Cancel",
+ "CancelJob",
NULL,
NULL,
+ DBUS_TYPE_UINT32, &id,
DBUS_TYPE_INVALID);
- if (r)
- goto finish;
+ if (r < 0)
+ return r;
}
-finish:
- return r;
+ return 0;
}
static bool need_daemon_reload(DBusConnection *bus, const char *unit) {
- DBusMessage *reply = NULL;
+ _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
dbus_bool_t b = FALSE;
DBusMessageIter iter, sub;
const char
*interface = "org.freedesktop.systemd1.Unit",
*property = "NeedDaemonReload",
*path;
- char *n;
+ _cleanup_free_ char *n = NULL;
int r;
/* We ignore all errors here, since this is used to show a warning only */
n = unit_name_mangle(unit);
- r = bus_method_call_with_reply (
+ if (!n)
+ return log_oom();
+
+ r = bus_method_call_with_reply(
bus,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
@@ -1141,19 +1415,20 @@ static bool need_daemon_reload(DBusConnection *bus, const char *unit) {
"GetUnit",
&reply,
NULL,
- DBUS_TYPE_STRING, n ? (const char**) &n : &unit,
+ DBUS_TYPE_STRING, &n,
DBUS_TYPE_INVALID);
- free(n);
- if (r)
- goto finish;
+ if (r < 0)
+ return r;
if (!dbus_message_get_args(reply, NULL,
DBUS_TYPE_OBJECT_PATH, &path,
DBUS_TYPE_INVALID))
- goto finish;
+ return -EIO;
dbus_message_unref(reply);
- r = bus_method_call_with_reply (
+ reply = NULL;
+
+ r = bus_method_call_with_reply(
bus,
"org.freedesktop.systemd1",
path,
@@ -1164,24 +1439,18 @@ static bool need_daemon_reload(DBusConnection *bus, const char *unit) {
DBUS_TYPE_STRING, &interface,
DBUS_TYPE_STRING, &property,
DBUS_TYPE_INVALID);
- if (r)
- goto finish;
+ if (r < 0)
+ return r;
if (!dbus_message_iter_init(reply, &iter) ||
dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
- goto finish;
+ return -EIO;
dbus_message_iter_recurse(&iter, &sub);
-
if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_BOOLEAN)
- goto finish;
+ return -EIO;
dbus_message_iter_get_basic(&sub, &b);
-
-finish:
- if (reply)
- dbus_message_unref(reply);
-
return b;
}
@@ -1193,15 +1462,15 @@ typedef struct WaitData {
} WaitData;
static DBusHandlerResult wait_filter(DBusConnection *connection, DBusMessage *message, void *data) {
- DBusError error;
+ _cleanup_dbus_error_free_ DBusError error;
WaitData *d = data;
+ dbus_error_init(&error);
+
assert(connection);
assert(message);
assert(d);
- dbus_error_init(&error);
-
log_debug("Got D-Bus request: %s.%s() on %s",
dbus_message_get_interface(message),
dbus_message_get_member(message),
@@ -1214,7 +1483,6 @@ static DBusHandlerResult wait_filter(DBusConnection *connection, DBusMessage *me
} else if (dbus_message_is_signal(message, "org.freedesktop.systemd1.Manager", "JobRemoved")) {
uint32_t id;
const char *path, *result, *unit;
- dbus_bool_t success = true;
if (dbus_message_get_args(message, &error,
DBUS_TYPE_UINT32, &id,
@@ -1222,10 +1490,8 @@ static DBusHandlerResult wait_filter(DBusConnection *connection, DBusMessage *me
DBUS_TYPE_STRING, &unit,
DBUS_TYPE_STRING, &result,
DBUS_TYPE_INVALID)) {
- char *p;
- p = set_remove(d->set, (char*) path);
- free(p);
+ free(set_remove(d->set, (char*) path));
if (!isempty(result))
d->result = strdup(result);
@@ -1233,7 +1499,7 @@ static DBusHandlerResult wait_filter(DBusConnection *connection, DBusMessage *me
if (!isempty(unit))
d->name = strdup(unit);
- goto finish;
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
#ifndef LEGACY
dbus_error_free(&error);
@@ -1242,47 +1508,21 @@ static DBusHandlerResult wait_filter(DBusConnection *connection, DBusMessage *me
DBUS_TYPE_OBJECT_PATH, &path,
DBUS_TYPE_STRING, &result,
DBUS_TYPE_INVALID)) {
- char *p;
-
/* Compatibility with older systemd versions <
* 183 during upgrades. This should be dropped
* one day. */
- p = set_remove(d->set, (char*) path);
- free(p);
+ free(set_remove(d->set, (char*) path));
if (*result)
d->result = strdup(result);
- goto finish;
- }
-
- dbus_error_free(&error);
- if (dbus_message_get_args(message, &error,
- DBUS_TYPE_UINT32, &id,
- DBUS_TYPE_OBJECT_PATH, &path,
- DBUS_TYPE_BOOLEAN, &success,
- DBUS_TYPE_INVALID)) {
- char *p;
-
- /* Compatibility with older systemd versions <
- * 19 during upgrades. This should be dropped
- * one day */
-
- p = set_remove(d->set, (char*) path);
- free(p);
-
- if (!success)
- d->result = strdup("failed");
-
- goto finish;
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
#endif
log_error("Failed to parse message: %s", bus_error_message(&error));
}
-finish:
- dbus_error_free(&error);
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
@@ -1315,14 +1555,11 @@ static int enable_wait_for_jobs(DBusConnection *bus) {
static int wait_for_jobs(DBusConnection *bus, Set *s) {
int r = 0;
- WaitData d;
+ WaitData d = { .set = s };
assert(bus);
assert(s);
- zero(d);
- d.set = s;
-
if (!dbus_connection_add_filter(bus, wait_filter, &d, NULL))
return log_oom();
@@ -1366,20 +1603,25 @@ static int wait_for_jobs(DBusConnection *bus, Set *s) {
return r;
}
-static int check_one_unit(DBusConnection *bus, char *name, char **check_states, bool quiet) {
- DBusMessage *reply = NULL;
+static int check_one_unit(DBusConnection *bus, const char *name, char **check_states, bool quiet) {
+ _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
+ _cleanup_free_ char *n = NULL;
DBusMessageIter iter, sub;
const char
*interface = "org.freedesktop.systemd1.Unit",
*property = "ActiveState";
- const char *path = NULL;
- const char *state;
+ const char *state, *path;
+ DBusError error;
int r;
- char *n;
assert(name);
+ dbus_error_init(&error);
+
n = unit_name_mangle(name);
+ if (!n)
+ return log_oom();
+
r = bus_method_call_with_reply (
bus,
"org.freedesktop.systemd1",
@@ -1387,26 +1629,28 @@ static int check_one_unit(DBusConnection *bus, char *name, char **check_states,
"org.freedesktop.systemd1.Manager",
"GetUnit",
&reply,
- NULL,
- DBUS_TYPE_STRING, n ? &n : &name,
+ &error,
+ DBUS_TYPE_STRING, &n,
DBUS_TYPE_INVALID);
- free(n);
- if (r) {
- if ((r != -ENOMEM) && (!quiet))
+ if (r < 0) {
+ dbus_error_free(&error);
+
+ if (!quiet)
puts("unknown");
- goto finish;
+ return 0;
}
if (!dbus_message_get_args(reply, NULL,
DBUS_TYPE_OBJECT_PATH, &path,
DBUS_TYPE_INVALID)) {
log_error("Failed to parse reply.");
- r = -EIO;
- goto finish;
+ return -EIO;
}
dbus_message_unref(reply);
- r = bus_method_call_with_reply (
+ reply = NULL;
+
+ r = bus_method_call_with_reply(
bus,
"org.freedesktop.systemd1",
path,
@@ -1417,22 +1661,23 @@ static int check_one_unit(DBusConnection *bus, char *name, char **check_states,
DBUS_TYPE_STRING, &interface,
DBUS_TYPE_STRING, &property,
DBUS_TYPE_INVALID);
- if (r)
- goto finish;
+ if (r < 0) {
+ if (!quiet)
+ puts("unknown");
+ return 0;
+ }
if (!dbus_message_iter_init(reply, &iter) ||
dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
log_error("Failed to parse reply.");
- r = -EIO;
- goto finish;
+ return r;
}
dbus_message_iter_recurse(&iter, &sub);
if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING) {
log_error("Failed to parse reply.");
- r = -EIO;
- goto finish;
+ return r;
}
dbus_message_iter_get_basic(&sub, &state);
@@ -1440,16 +1685,7 @@ static int check_one_unit(DBusConnection *bus, char *name, char **check_states,
if (!quiet)
puts(state);
- if (strv_find(check_states, state))
- r = 0;
- else
- r = 3; /* According to LSB: "program is not running" */
-
-finish:
- if (reply)
- dbus_message_unref(reply);
-
- return r;
+ return strv_find(check_states, state) ? 1 : 0;
}
static void check_triggering_units(
@@ -1458,11 +1694,11 @@ static void check_triggering_units(
_cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
DBusMessageIter iter, sub;
- char *service_trigger = NULL;
const char *interface = "org.freedesktop.systemd1.Unit",
- *triggered_by_property = "TriggeredBy";
-
- char _cleanup_free_ *unit_path = NULL, *n = NULL;
+ *load_state_property = "LoadState",
+ *triggered_by_property = "TriggeredBy",
+ *state;
+ _cleanup_free_ char *unit_path = NULL, *n = NULL;
bool print_warning_label = true;
int r;
@@ -1478,7 +1714,42 @@ static void check_triggering_units(
return;
}
- r = bus_method_call_with_reply (
+ r = bus_method_call_with_reply(
+ bus,
+ "org.freedesktop.systemd1",
+ unit_path,
+ "org.freedesktop.DBus.Properties",
+ "Get",
+ &reply,
+ NULL,
+ DBUS_TYPE_STRING, &interface,
+ DBUS_TYPE_STRING, &load_state_property,
+ DBUS_TYPE_INVALID);
+ if (r < 0)
+ return;
+
+ if (!dbus_message_iter_init(reply, &iter) ||
+ dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
+ log_error("Failed to parse reply.");
+ return;
+ }
+
+ dbus_message_iter_recurse(&iter, &sub);
+
+ if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING) {
+ log_error("Failed to parse reply.");
+ return;
+ }
+
+ dbus_message_iter_get_basic(&sub, &state);
+
+ if (streq(state, "masked"))
+ return;
+
+ dbus_message_unref(reply);
+ reply = NULL;
+
+ r = bus_method_call_with_reply(
bus,
"org.freedesktop.systemd1",
unit_path,
@@ -1489,7 +1760,7 @@ static void check_triggering_units(
DBUS_TYPE_STRING, &interface,
DBUS_TYPE_STRING, &triggered_by_property,
DBUS_TYPE_INVALID);
- if (r)
+ if (r < 0)
return;
if (!dbus_message_iter_init(reply, &iter) ||
@@ -1503,7 +1774,12 @@ static void check_triggering_units(
sub = iter;
while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
- char **check_states = NULL;
+ const char * const check_states[] = {
+ "active",
+ "reloading",
+ NULL
+ };
+ const char *service_trigger;
if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING) {
log_error("Failed to parse reply.");
@@ -1512,16 +1788,15 @@ static void check_triggering_units(
dbus_message_iter_get_basic(&sub, &service_trigger);
- check_states = strv_new("active", "reloading", NULL);
- r = check_one_unit(bus, service_trigger, check_states, true);
- strv_free(check_states);
+ r = check_one_unit(bus, service_trigger, (char**) check_states, true);
if (r < 0)
return;
- if (r == 0) {
+ if (r > 0) {
if (print_warning_label) {
log_warning("Warning: Stopping %s, but it can still be activated by:", unit_name);
print_warning_label = false;
}
+
log_warning(" %s", service_trigger);
}
@@ -1538,9 +1813,9 @@ static int start_unit_one(
Set *s) {
_cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
+ _cleanup_free_ char *n;
const char *path;
int r;
- _cleanup_free_ char *n, *p = NULL;
assert(method);
assert(name);
@@ -1585,73 +1860,59 @@ static int start_unit_one(
n, arg_scope == UNIT_FILE_SYSTEM ? "--system" : "--user");
if (s) {
+ char *p;
+
p = strdup(path);
if (!p)
return log_oom();
- r = set_put(s, p);
+ r = set_consume(s, p);
if (r < 0) {
log_error("Failed to add path to set.");
return r;
}
-
- p = NULL;
}
return 0;
}
+static const struct {
+ const char *target;
+ const char *verb;
+ const char *mode;
+} action_table[_ACTION_MAX] = {
+ [ACTION_HALT] = { SPECIAL_HALT_TARGET, "halt", "replace-irreversibly" },
+ [ACTION_POWEROFF] = { SPECIAL_POWEROFF_TARGET, "poweroff", "replace-irreversibly" },
+ [ACTION_REBOOT] = { SPECIAL_REBOOT_TARGET, "reboot", "replace-irreversibly" },
+ [ACTION_KEXEC] = { SPECIAL_KEXEC_TARGET, "kexec", "replace-irreversibly" },
+ [ACTION_RUNLEVEL2] = { SPECIAL_RUNLEVEL2_TARGET, NULL, "isolate" },
+ [ACTION_RUNLEVEL3] = { SPECIAL_RUNLEVEL3_TARGET, NULL, "isolate" },
+ [ACTION_RUNLEVEL4] = { SPECIAL_RUNLEVEL4_TARGET, NULL, "isolate" },
+ [ACTION_RUNLEVEL5] = { SPECIAL_RUNLEVEL5_TARGET, NULL, "isolate" },
+ [ACTION_RESCUE] = { SPECIAL_RESCUE_TARGET, "rescue", "isolate" },
+ [ACTION_EMERGENCY] = { SPECIAL_EMERGENCY_TARGET, "emergency", "isolate" },
+ [ACTION_DEFAULT] = { SPECIAL_DEFAULT_TARGET, "default", "isolate" },
+ [ACTION_EXIT] = { SPECIAL_EXIT_TARGET, "exit", "replace-irreversibly" },
+ [ACTION_SUSPEND] = { SPECIAL_SUSPEND_TARGET, "suspend", "replace-irreversibly" },
+ [ACTION_HIBERNATE] = { SPECIAL_HIBERNATE_TARGET, "hibernate", "replace-irreversibly" },
+ [ACTION_HYBRID_SLEEP] = { SPECIAL_HYBRID_SLEEP_TARGET, "hybrid-sleep", "replace-irreversibly" },
+};
+
static enum action verb_to_action(const char *verb) {
- if (streq(verb, "halt"))
- return ACTION_HALT;
- else if (streq(verb, "poweroff"))
- return ACTION_POWEROFF;
- else if (streq(verb, "reboot"))
- return ACTION_REBOOT;
- else if (streq(verb, "kexec"))
- return ACTION_KEXEC;
- else if (streq(verb, "rescue"))
- return ACTION_RESCUE;
- else if (streq(verb, "emergency"))
- return ACTION_EMERGENCY;
- else if (streq(verb, "default"))
- return ACTION_DEFAULT;
- else if (streq(verb, "exit"))
- return ACTION_EXIT;
- else if (streq(verb, "suspend"))
- return ACTION_SUSPEND;
- else if (streq(verb, "hibernate"))
- return ACTION_HIBERNATE;
- else if (streq(verb, "hybrid-sleep"))
- return ACTION_HYBRID_SLEEP;
- else
- return ACTION_INVALID;
+ enum action i;
+
+ for (i = ACTION_INVALID; i < _ACTION_MAX; i++)
+ if (action_table[i].verb && streq(verb, action_table[i].verb))
+ return i;
+ return ACTION_INVALID;
}
static int start_unit(DBusConnection *bus, char **args) {
- static const char * const table[_ACTION_MAX] = {
- [ACTION_HALT] = SPECIAL_HALT_TARGET,
- [ACTION_POWEROFF] = SPECIAL_POWEROFF_TARGET,
- [ACTION_REBOOT] = SPECIAL_REBOOT_TARGET,
- [ACTION_KEXEC] = SPECIAL_KEXEC_TARGET,
- [ACTION_RUNLEVEL2] = SPECIAL_RUNLEVEL2_TARGET,
- [ACTION_RUNLEVEL3] = SPECIAL_RUNLEVEL3_TARGET,
- [ACTION_RUNLEVEL4] = SPECIAL_RUNLEVEL4_TARGET,
- [ACTION_RUNLEVEL5] = SPECIAL_RUNLEVEL5_TARGET,
- [ACTION_RESCUE] = SPECIAL_RESCUE_TARGET,
- [ACTION_EMERGENCY] = SPECIAL_EMERGENCY_TARGET,
- [ACTION_DEFAULT] = SPECIAL_DEFAULT_TARGET,
- [ACTION_EXIT] = SPECIAL_EXIT_TARGET,
- [ACTION_SUSPEND] = SPECIAL_SUSPEND_TARGET,
- [ACTION_HIBERNATE] = SPECIAL_HIBERNATE_TARGET,
- [ACTION_HYBRID_SLEEP] = SPECIAL_HYBRID_SLEEP_TARGET
- };
-
int r, ret = 0;
const char *method, *mode, *one_name;
- Set *s = NULL;
- DBusError error;
+ _cleanup_set_free_free_ Set *s = NULL;
+ _cleanup_dbus_error_free_ DBusError error;
char **name;
dbus_error_init(&error);
@@ -1661,6 +1922,7 @@ static int start_unit(DBusConnection *bus, char **args) {
ask_password_agent_open_if_enabled();
if (arg_action == ACTION_SYSTEMCTL) {
+ enum action action;
method =
streq(args[0], "stop") ||
streq(args[0], "condstop") ? "StopUnit" :
@@ -1677,42 +1939,33 @@ static int start_unit(DBusConnection *bus, char **args) {
streq(args[0], "force-reload") ? "ReloadOrTryRestartUnit" :
"StartUnit";
+ action = verb_to_action(args[0]);
- mode =
- (streq(args[0], "isolate") ||
- streq(args[0], "rescue") ||
- streq(args[0], "emergency")) ? "isolate" : arg_job_mode;
+ mode = streq(args[0], "isolate") ? "isolate" :
+ action_table[action].mode ?: arg_job_mode;
- one_name = table[verb_to_action(args[0])];
+ one_name = action_table[action].target;
} else {
- assert(arg_action < ELEMENTSOF(table));
- assert(table[arg_action]);
+ assert(arg_action < ELEMENTSOF(action_table));
+ assert(action_table[arg_action].target);
method = "StartUnit";
- mode = (arg_action == ACTION_EMERGENCY ||
- arg_action == ACTION_RESCUE ||
- arg_action == ACTION_RUNLEVEL2 ||
- arg_action == ACTION_RUNLEVEL3 ||
- arg_action == ACTION_RUNLEVEL4 ||
- arg_action == ACTION_RUNLEVEL5) ? "isolate" : "replace";
-
- one_name = table[arg_action];
+ mode = action_table[arg_action].mode;
+ one_name = action_table[arg_action].target;
}
if (!arg_no_block) {
ret = enable_wait_for_jobs(bus);
if (ret < 0) {
log_error("Could not watch jobs: %s", strerror(-ret));
- goto finish;
+ return ret;
}
s = set_new(string_hash_func, string_compare_func);
- if (!s) {
- ret = log_oom();
- goto finish;
- }
+ if (!s)
+ return log_oom();
}
if (one_name) {
@@ -1731,10 +1984,8 @@ static int start_unit(DBusConnection *bus, char **args) {
if (!arg_no_block) {
r = wait_for_jobs(bus, s);
- if (r < 0) {
- ret = r;
- goto finish;
- }
+ if (r < 0)
+ return r;
/* When stopping units, warn if they can still be triggered by
* another active unit (socket, path, timer) */
@@ -1747,10 +1998,6 @@ static int start_unit(DBusConnection *bus, char **args) {
}
}
-finish:
- set_free_free(s);
- dbus_error_free(&error);
-
return ret;
}
@@ -1761,6 +2008,9 @@ static int reboot_with_logind(DBusConnection *bus, enum action a) {
const char *method;
dbus_bool_t interactive = true;
+ if (!bus)
+ return -EIO;
+
polkit_agent_open_if_enabled();
switch (a) {
@@ -1789,7 +2039,7 @@ static int reboot_with_logind(DBusConnection *bus, enum action a) {
return -EINVAL;
}
- return bus_method_call_with_reply (
+ return bus_method_call_with_reply(
bus,
"org.freedesktop.login1",
"/org/freedesktop/login1",
@@ -1804,6 +2054,136 @@ static int reboot_with_logind(DBusConnection *bus, enum action a) {
#endif
}
+static int check_inhibitors(DBusConnection *bus, enum action a) {
+#ifdef HAVE_LOGIND
+ _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
+ DBusMessageIter iter, sub, sub2;
+ int r;
+ unsigned c = 0;
+ _cleanup_strv_free_ char **sessions = NULL;
+ char **s;
+
+ if (!bus)
+ return 0;
+
+ if (arg_ignore_inhibitors || arg_force > 0)
+ return 0;
+
+ if (arg_when > 0)
+ return 0;
+
+ if (geteuid() == 0)
+ return 0;
+
+ if (!on_tty())
+ return 0;
+
+ r = bus_method_call_with_reply(
+ bus,
+ "org.freedesktop.login1",
+ "/org/freedesktop/login1",
+ "org.freedesktop.login1.Manager",
+ "ListInhibitors",
+ &reply,
+ NULL,
+ DBUS_TYPE_INVALID);
+ if (r < 0)
+ /* If logind is not around, then there are no inhibitors... */
+ return 0;
+
+ if (!dbus_message_iter_init(reply, &iter) ||
+ dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
+ dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) {
+ log_error("Failed to parse reply.");
+ return -EIO;
+ }
+
+ dbus_message_iter_recurse(&iter, &sub);
+ while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
+ const char *what, *who, *why, *mode;
+ uint32_t uid, pid;
+ _cleanup_strv_free_ char **sv = NULL;
+ _cleanup_free_ char *comm = NULL, *user = NULL;
+
+ if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
+ log_error("Failed to parse reply.");
+ return -EIO;
+ }
+
+ dbus_message_iter_recurse(&sub, &sub2);
+
+ if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &what, true) < 0 ||
+ bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &who, true) < 0 ||
+ bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &why, true) < 0 ||
+ bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &mode, true) < 0 ||
+ bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT32, &uid, true) < 0 ||
+ bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT32, &pid, false) < 0) {
+ log_error("Failed to parse reply.");
+ return -EIO;
+ }
+
+ if (!streq(mode, "block"))
+ goto next;
+
+ sv = strv_split(what, ":");
+ if (!sv)
+ return log_oom();
+
+ if (!strv_contains(sv,
+ a == ACTION_HALT ||
+ a == ACTION_POWEROFF ||
+ a == ACTION_REBOOT ||
+ a == ACTION_KEXEC ? "shutdown" : "sleep"))
+ goto next;
+
+ get_process_comm(pid, &comm);
+ user = uid_to_name(uid);
+ log_warning("Operation inhibited by \"%s\" (PID %lu \"%s\", user %s), reason is \"%s\".",
+ who, (unsigned long) pid, strna(comm), strna(user), why);
+ c++;
+
+ next:
+ dbus_message_iter_next(&sub);
+ }
+
+ dbus_message_iter_recurse(&iter, &sub);
+
+ /* Check for current sessions */
+ sd_get_sessions(&sessions);
+ STRV_FOREACH(s, sessions) {
+ uid_t uid;
+ _cleanup_free_ char *type = NULL, *tty = NULL, *seat = NULL, *user = NULL, *service = NULL, *class = NULL;
+
+ if (sd_session_get_uid(*s, &uid) < 0 || uid == getuid())
+ continue;
+
+ if (sd_session_get_class(*s, &class) < 0 || !streq(class, "user"))
+ continue;
+
+ if (sd_session_get_type(*s, &type) < 0 || (!streq(type, "x11") && !streq(type, "tty")))
+ continue;
+
+ sd_session_get_tty(*s, &tty);
+ sd_session_get_seat(*s, &seat);
+ sd_session_get_service(*s, &service);
+ user = uid_to_name(uid);
+
+ log_warning("User %s is logged in on %s.", strna(user), isempty(tty) ? (isempty(seat) ? strna(service) : seat) : tty);
+ c++;
+ }
+
+ if (c <= 0)
+ return 0;
+
+ log_error("Please retry operation after closing inhibitors and logging out other users.\nAlternatively, ignore inhibitors and users with 'systemctl %s -i'.",
+ action_table[a].verb);
+
+ return -EPERM;
+#else
+ return 0;
+#endif
+}
+
static int start_special(DBusConnection *bus, char **args) {
enum action a;
int r;
@@ -1812,6 +2192,10 @@ static int start_special(DBusConnection *bus, char **args) {
a = verb_to_action(args[0]);
+ r = check_inhibitors(bus, a);
+ if (r < 0)
+ return r;
+
if (arg_force >= 2 && geteuid() != 0) {
log_error("Must be root.");
return -EPERM;
@@ -1844,13 +2228,19 @@ static int start_special(DBusConnection *bus, char **args) {
}
r = start_unit(bus, args);
- if (r >= 0)
+ if (r == EXIT_SUCCESS)
warn_wall(a);
return r;
}
static int check_unit_active(DBusConnection *bus, char **args) {
+ const char * const check_states[] = {
+ "active",
+ "reloading",
+ NULL
+ };
+
char **name;
int r = 3; /* According to LSB: "program is not running" */
@@ -1858,12 +2248,12 @@ static int check_unit_active(DBusConnection *bus, char **args) {
assert(args);
STRV_FOREACH(name, args+1) {
- char **check_states = strv_new("active", "reloading", NULL);
- int state = check_one_unit(bus, *name, check_states, arg_quiet);
- strv_free(check_states);
+ int state;
+
+ state = check_one_unit(bus, *name, (char**) check_states, arg_quiet);
if (state < 0)
return state;
- if (state == 0)
+ if (state > 0)
r = 0;
}
@@ -1871,6 +2261,11 @@ static int check_unit_active(DBusConnection *bus, char **args) {
}
static int check_unit_failed(DBusConnection *bus, char **args) {
+ const char * const check_states[] = {
+ "failed",
+ NULL
+ };
+
char **name;
int r = 1;
@@ -1878,12 +2273,12 @@ static int check_unit_failed(DBusConnection *bus, char **args) {
assert(args);
STRV_FOREACH(name, args+1) {
- char **check_states = strv_new("failed", NULL);
- int state = check_one_unit(bus, *name, check_states, arg_quiet);
- strv_free(check_states);
+ int state;
+
+ state = check_one_unit(bus, *name, (char**) check_states, arg_quiet);
if (state < 0)
return state;
- if (state == 0)
+ if (state > 0)
r = 0;
}
@@ -1891,17 +2286,23 @@ static int check_unit_failed(DBusConnection *bus, char **args) {
}
static int kill_unit(DBusConnection *bus, char **args) {
+ char **name;
int r = 0;
- char **name, *n;
+ assert(bus);
assert(args);
if (!arg_kill_who)
arg_kill_who = "all";
STRV_FOREACH(name, args+1) {
+ _cleanup_free_ char *n = NULL;
+
n = unit_name_mangle(*name);
- r = bus_method_call_with_reply (
+ if (!n)
+ return log_oom();
+
+ r = bus_method_call_with_reply(
bus,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
@@ -1909,14 +2310,157 @@ static int kill_unit(DBusConnection *bus, char **args) {
"KillUnit",
NULL,
NULL,
- DBUS_TYPE_STRING, n ? &n : name,
+ DBUS_TYPE_STRING, &n,
DBUS_TYPE_STRING, &arg_kill_who,
DBUS_TYPE_INT32, &arg_signal,
DBUS_TYPE_INVALID);
- free(n);
- if (r)
+ if (r < 0)
+ return r;
+ }
+ return 0;
+}
+
+static int set_cgroup(DBusConnection *bus, char **args) {
+ _cleanup_free_ char *n = NULL;
+ const char *method, *runtime;
+ char **argument;
+ int r;
+
+ assert(bus);
+ assert(args);
+
+ method =
+ streq(args[0], "set-cgroup") ? "SetUnitControlGroup" :
+ streq(args[0], "unset-cgroup") ? "UnsetUnitControlGroup"
+ : "UnsetUnitControlGroupAttribute";
+
+ runtime = arg_runtime ? "runtime" : "persistent";
+
+ n = unit_name_mangle(args[1]);
+ if (!n)
+ return log_oom();
+
+ STRV_FOREACH(argument, args + 2) {
+
+ r = bus_method_call_with_reply(
+ bus,
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ method,
+ NULL,
+ NULL,
+ DBUS_TYPE_STRING, &n,
+ DBUS_TYPE_STRING, argument,
+ DBUS_TYPE_STRING, &runtime,
+ DBUS_TYPE_INVALID);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+static int set_cgroup_attr(DBusConnection *bus, char **args) {
+ _cleanup_dbus_message_unref_ DBusMessage *m = NULL, *reply = NULL;
+ DBusError error;
+ DBusMessageIter iter;
+ _cleanup_free_ char *n = NULL;
+ const char *runtime;
+ int r;
+
+ assert(bus);
+ assert(args);
+
+ dbus_error_init(&error);
+
+ runtime = arg_runtime ? "runtime" : "persistent";
+
+ n = unit_name_mangle(args[1]);
+ if (!n)
+ return log_oom();
+
+ m = dbus_message_new_method_call(
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "SetUnitControlGroupAttribute");
+ if (!m)
+ return log_oom();
+
+ dbus_message_iter_init_append(m, &iter);
+ if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &n) ||
+ !dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &args[2]))
+ return log_oom();
+
+ r = bus_append_strv_iter(&iter, args + 3);
+ if (r < 0)
+ return log_oom();
+
+ if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &runtime))
+ return log_oom();
+
+ reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
+ if (!reply) {
+ log_error("Failed to issue method call: %s", bus_error_message(&error));
+ dbus_error_free(&error);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int get_cgroup_attr(DBusConnection *bus, char **args) {
+ _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
+ _cleanup_free_ char *n = NULL;
+ char **argument;
+ int r;
+
+ assert(bus);
+ assert(args);
+
+ n = unit_name_mangle(args[1]);
+ if (!n)
+ return log_oom();
+
+ STRV_FOREACH(argument, args + 2) {
+ _cleanup_strv_free_ char **list = NULL;
+ DBusMessageIter iter;
+ char **a;
+
+ r = bus_method_call_with_reply(
+ bus,
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "GetUnitControlGroupAttribute",
+ &reply,
+ NULL,
+ DBUS_TYPE_STRING, &n,
+ DBUS_TYPE_STRING, argument,
+ DBUS_TYPE_INVALID);
+ if (r < 0)
+ return r;
+
+ if (!dbus_message_iter_init(reply, &iter)) {
+ log_error("Failed to initialize iterator.");
+ return -EIO;
+ }
+
+ r = bus_parse_strv_iter(&iter, &list);
+ if (r < 0) {
+ log_error("Failed to parse value list.");
return r;
+ }
+
+ STRV_FOREACH(a, list) {
+ if (endswith(*a, "\n"))
+ fputs(*a, stdout);
+ else
+ puts(*a);
+ }
}
+
return 0;
}
@@ -1966,7 +2510,8 @@ static int exec_status_info_deserialize(DBusMessageIter *sub, ExecStatusInfo *i)
if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &path, true) < 0)
return -EIO;
- if (!(i->path = strdup(path)))
+ i->path = strdup(path);
+ if (!i->path)
return -ENOMEM;
if (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_ARRAY ||
@@ -1981,8 +2526,8 @@ static int exec_status_info_deserialize(DBusMessageIter *sub, ExecStatusInfo *i)
n++;
}
-
- if (!(i->argv = new0(char*, n+1)))
+ i->argv = new0(char*, n+1);
+ if (!i->argv)
return -ENOMEM;
n = 0;
@@ -1994,8 +2539,11 @@ static int exec_status_info_deserialize(DBusMessageIter *sub, ExecStatusInfo *i)
dbus_message_iter_get_basic(&sub3, &s);
dbus_message_iter_next(&sub3);
- if (!(i->argv[n++] = strdup(s)))
+ i->argv[n] = strdup(s);
+ if (!i->argv[n])
return -ENOMEM;
+
+ n++;
}
if (!dbus_message_iter_next(&sub2) ||
@@ -2035,6 +2583,8 @@ typedef struct UnitStatusInfo {
const char *source_path;
const char *default_control_group;
+ char **dropin_paths;
+
const char *load_error;
const char *result;
@@ -2065,6 +2615,9 @@ typedef struct UnitStatusInfo {
unsigned n_connections;
bool accept;
+ /* Pairs of type, path */
+ char **listen;
+
/* Device */
const char *sysfs_path;
@@ -2084,9 +2637,26 @@ static void print_status_info(UnitStatusInfo *i) {
char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
char since2[FORMAT_TIMESTAMP_MAX], *s2;
const char *path;
+ int flags =
+ arg_all * OUTPUT_SHOW_ALL |
+ (!on_tty() || pager_have()) * OUTPUT_FULL_WIDTH |
+ on_tty() * OUTPUT_COLOR |
+ !arg_quiet * OUTPUT_WARN_CUTOFF |
+ arg_full * OUTPUT_FULL_WIDTH;
+ int maxlen = 8; /* a value that'll suffice most of the time */
+ char **t, **t2;
assert(i);
+ STRV_FOREACH_PAIR(t, t2, i->listen)
+ maxlen = MAX(maxlen, (int)(sizeof("Listen") - 1 + strlen(*t)));
+ if (i->accept)
+ maxlen = MAX(maxlen, (int)sizeof("Accept") - 1);
+ if (i->main_pid > 0)
+ maxlen = MAX(maxlen, (int)sizeof("Main PID") - 1);
+ else if (i->control_pid > 0)
+ maxlen = MAX(maxlen, (int)sizeof("Control") - 1);
+
/* This shows pretty information about a unit. See
* print_property() for a low-level property printer */
@@ -2098,7 +2668,7 @@ static void print_status_info(UnitStatusInfo *i) {
printf("\n");
if (i->following)
- printf("\t Follow: unit currently follows state of %s\n", i->following);
+ printf(" %*s: unit currently follows state of %s\n", maxlen, "Follow", i->following);
if (streq_ptr(i->load_state, "error")) {
on = ansi_highlight_red(true);
@@ -2109,13 +2679,45 @@ static void print_status_info(UnitStatusInfo *i) {
path = i->source_path ? i->source_path : i->fragment_path;
if (i->load_error)
- printf("\t Loaded: %s%s%s (Reason: %s)\n", on, strna(i->load_state), off, i->load_error);
+ printf(" %*s: %s%s%s (Reason: %s)\n",
+ maxlen, "Loaded", on, strna(i->load_state), off, i->load_error);
else if (path && i->unit_file_state)
- printf("\t Loaded: %s%s%s (%s; %s)\n", on, strna(i->load_state), off, path, i->unit_file_state);
+ printf(" %*s: %s%s%s (%s; %s)\n",
+ maxlen, "Loaded", on, strna(i->load_state), off, path, i->unit_file_state);
else if (path)
- printf("\t Loaded: %s%s%s (%s)\n", on, strna(i->load_state), off, path);
+ printf(" %*s: %s%s%s (%s)\n",
+ maxlen, "Loaded", on, strna(i->load_state), off, path);
else
- printf("\t Loaded: %s%s%s\n", on, strna(i->load_state), off);
+ printf(" %*s: %s%s%s\n",
+ maxlen, "Loaded", on, strna(i->load_state), off);
+
+ if (!strv_isempty(i->dropin_paths)) {
+ char ** dropin;
+ char * dir = NULL;
+ bool last = false;
+
+ STRV_FOREACH(dropin, i->dropin_paths) {
+ if (! dir || last) {
+ printf(" %*s ", maxlen, dir ? "" : "Drop-In:");
+
+ free(dir);
+
+ if (path_get_parent(*dropin, &dir) < 0) {
+ log_oom();
+ return;
+ }
+
+ printf("%s\n %*s %s", dir, maxlen, "",
+ draw_special_char(DRAW_TREE_RIGHT));
+ }
+
+ last = ! (*(dropin + 1) && startswith(*(dropin + 1), dir));
+
+ printf("%s%s", path_get_file_name(*dropin), last ? "\n" : ", ");
+ }
+
+ free(dir);
+ }
ss = streq_ptr(i->active_state, i->sub_state) ? NULL : i->sub_state;
@@ -2129,16 +2731,11 @@ static void print_status_info(UnitStatusInfo *i) {
on = off = "";
if (ss)
- printf("\t Active: %s%s (%s)%s",
- on,
- strna(i->active_state),
- ss,
- off);
+ printf(" %*s: %s%s (%s)%s",
+ maxlen, "Active", on, strna(i->active_state), ss, off);
else
- printf("\t Active: %s%s%s",
- on,
- strna(i->active_state),
- off);
+ printf(" %*s: %s%s%s",
+ maxlen, "Active", on, strna(i->active_state), off);
if (!isempty(i->result) && !streq(i->result, "success"))
printf(" (Result: %s)", i->result);
@@ -2165,45 +2762,37 @@ static void print_status_info(UnitStatusInfo *i) {
s2 = format_timestamp(since2, sizeof(since2), i->condition_timestamp);
if (s1)
- printf("\t start condition failed at %s; %s\n", s2, s1);
+ printf(" %*s start condition failed at %s; %s\n", maxlen, "", s2, s1);
else if (s2)
- printf("\t start condition failed at %s\n", s2);
+ printf(" %*s start condition failed at %s\n", maxlen, "", s2);
}
if (i->sysfs_path)
- printf("\t Device: %s\n", i->sysfs_path);
+ printf(" %*s: %s\n", maxlen, "Device", i->sysfs_path);
if (i->where)
- printf("\t Where: %s\n", i->where);
+ printf(" %*s: %s\n", maxlen, "Where", i->where);
if (i->what)
- printf("\t What: %s\n", i->what);
+ printf(" %*s: %s\n", maxlen, "What", i->what);
- if (!strv_isempty(i->documentation)) {
- char **t;
- bool first = true;
+ STRV_FOREACH(t, i->documentation)
+ printf(" %*s %s\n", maxlen+1, t == i->documentation ? "Docs:" : "", *t);
- STRV_FOREACH(t, i->documentation) {
- if (first) {
- printf("\t Docs: %s\n", *t);
- first = false;
- } else
- printf("\t %s\n", *t);
- }
- }
+ STRV_FOREACH_PAIR(t, t2, i->listen)
+ printf(" %*s %s (%s)\n", maxlen+1, t == i->listen ? "Listen:" : "", *t2, *t);
if (i->accept)
- printf("\tAccepted: %u; Connected: %u\n", i->n_accepted, i->n_connections);
+ printf(" %*s: %u; Connected: %u\n", maxlen, "Accepted", i->n_accepted, i->n_connections);
LIST_FOREACH(exec, p, i->exec) {
- char *t;
+ _cleanup_free_ char *argv = NULL;
bool good;
/* Only show exited processes here */
if (p->code == 0)
continue;
- t = strv_join(p->argv, " ");
- printf("\t Process: %u %s=%s ", p->pid, p->name, strna(t));
- free(t);
+ argv = strv_join(p->argv, " ");
+ printf(" %*s: %u %s=%s ", maxlen, "Process", p->pid, p->name, strna(argv));
good = is_clean_exit_lsb(p->code, p->status, NULL);
if (!good) {
@@ -2239,18 +2828,14 @@ static void print_status_info(UnitStatusInfo *i) {
}
if (i->main_pid > 0 || i->control_pid > 0) {
- printf("\t");
-
if (i->main_pid > 0) {
- printf("Main PID: %u", (unsigned) i->main_pid);
+ printf(" %*s: %u", maxlen, "Main PID", (unsigned) i->main_pid);
if (i->running) {
- char *t = NULL;
- get_process_comm(i->main_pid, &t);
- if (t) {
- printf(" (%s)", t);
- free(t);
- }
+ _cleanup_free_ char *comm = NULL;
+ get_process_comm(i->main_pid, &comm);
+ if (comm)
+ printf(" (%s)", comm);
} else if (i->exit_code > 0) {
printf(" (code=%s, ", sigchld_code_to_string(i->exit_code));
@@ -2267,42 +2852,43 @@ static void print_status_info(UnitStatusInfo *i) {
printf("signal=%s", signal_to_string(i->exit_status));
printf(")");
}
- }
- if (i->main_pid > 0 && i->control_pid > 0)
- printf(";");
+ if (i->control_pid > 0)
+ printf(";");
+ }
if (i->control_pid > 0) {
- char *t = NULL;
+ _cleanup_free_ char *c = NULL;
- printf(" Control: %u", (unsigned) i->control_pid);
+ printf(" %*s: %u", i->main_pid ? 0 : maxlen, "Control", (unsigned) i->control_pid);
- get_process_comm(i->control_pid, &t);
- if (t) {
- printf(" (%s)", t);
- free(t);
- }
+ get_process_comm(i->control_pid, &c);
+ if (c)
+ printf(" (%s)", c);
}
printf("\n");
}
if (i->status_text)
- printf("\t Status: \"%s\"\n", i->status_text);
+ printf(" %*s: \"%s\"\n", maxlen, "Status", i->status_text);
if (i->default_control_group &&
(i->main_pid > 0 || i->control_pid > 0 || cg_is_empty_by_spec(i->default_control_group, false) == 0)) {
unsigned c;
- printf("\t CGroup: %s\n", i->default_control_group);
+ printf(" %*s: %s\n", maxlen, "CGroup", i->default_control_group);
if (arg_transport != TRANSPORT_SSH) {
unsigned k = 0;
pid_t extra[2];
+ char prefix[maxlen + 4];
+ memset(prefix, ' ', sizeof(prefix) - 1);
+ prefix[sizeof(prefix) - 1] = '\0';
c = columns();
- if (c > 18)
- c -= 18;
+ if (c > sizeof(prefix) - 1)
+ c -= sizeof(prefix) - 1;
else
c = 0;
@@ -2312,17 +2898,12 @@ static void print_status_info(UnitStatusInfo *i) {
if (i->control_pid > 0)
extra[k++] = i->control_pid;
- show_cgroup_and_extra_by_spec(i->default_control_group, "\t\t ", c, false, arg_all, extra, k);
+ show_cgroup_and_extra_by_spec(i->default_control_group, prefix,
+ c, false, extra, k, flags);
}
}
if (i->id && arg_transport != TRANSPORT_SSH) {
- int flags =
- arg_all * OUTPUT_SHOW_ALL |
- (!on_tty() || pager_have()) * OUTPUT_FULL_WIDTH |
- on_tty() * OUTPUT_COLOR |
- !arg_quiet * OUTPUT_WARN_CUTOFF;
-
printf("\n");
show_journal_by_unit(stdout,
i->id,
@@ -2330,7 +2911,9 @@ static void print_status_info(UnitStatusInfo *i) {
0,
i->inactive_exit_timestamp_monotonic,
arg_lines,
- flags);
+ getuid(),
+ flags,
+ arg_scope == UNIT_FILE_SYSTEM);
}
if (i->need_daemon_reload)
@@ -2355,7 +2938,7 @@ static void show_unit_help(UnitStatusInfo *i) {
if (startswith(*p, "man:")) {
size_t k;
char *e = NULL;
- char *page = NULL, *section = NULL;
+ _cleanup_free_ char *page = NULL, *section = NULL;
const char *args[4] = { "man", NULL, NULL, NULL };
pid_t pid;
@@ -2366,14 +2949,8 @@ static void show_unit_help(UnitStatusInfo *i) {
if (e) {
page = strndup((*p) + 4, e - *p - 4);
- if (!page) {
- log_oom();
- return;
- }
-
section = strndup(e + 1, *p + k - e - 2);
- if (!section) {
- free(page);
+ if (!page || !section) {
log_oom();
return;
}
@@ -2386,8 +2963,6 @@ static void show_unit_help(UnitStatusInfo *i) {
pid = fork();
if (pid < 0) {
log_error("Failed to fork: %m");
- free(page);
- free(section);
continue;
}
@@ -2398,9 +2973,6 @@ static void show_unit_help(UnitStatusInfo *i) {
_exit(EXIT_FAILURE);
}
- free(page);
- free(section);
-
wait_for_terminate(pid, NULL);
} else
log_info("Can't show: %s", *p);
@@ -2560,6 +3132,38 @@ static int status_property(const char *name, DBusMessageIter *iter, UnitStatusIn
dbus_message_iter_next(&sub);
}
+
+ } else if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "Listen")) {
+ DBusMessageIter sub, sub2;
+
+ dbus_message_iter_recurse(iter, &sub);
+ while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
+ const char *type, *path;
+
+ dbus_message_iter_recurse(&sub, &sub2);
+
+ if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &type, true) >= 0 &&
+ bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &path, false) >= 0) {
+ int r;
+
+ r = strv_extend(&i->listen, type);
+ if (r < 0)
+ return r;
+ r = strv_extend(&i->listen, path);
+ if (r < 0)
+ return r;
+ }
+
+ dbus_message_iter_next(&sub);
+ }
+
+ return 0;
+
+ } else if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRING && streq(name, "DropInPaths")) {
+ int r = bus_parse_strv_iter(iter, &i->dropin_paths);
+ if (r < 0)
+ return r;
+
} else if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRING &&
streq(name, "Documentation")) {
@@ -2568,16 +3172,13 @@ static int status_property(const char *name, DBusMessageIter *iter, UnitStatusIn
dbus_message_iter_recurse(iter, &sub);
while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING) {
const char *s;
- char **l;
+ int r;
dbus_message_iter_get_basic(&sub, &s);
- l = strv_append(i->documentation, s);
- if (!l)
- return -ENOMEM;
-
- strv_free(i->documentation);
- i->documentation = l;
+ r = strv_extend(&i->documentation, s);
+ if (r < 0)
+ return r;
dbus_message_iter_next(&sub);
}
@@ -2621,7 +3222,7 @@ static int print_property(const char *name, DBusMessageIter *iter) {
/* This is a low-level property printer, see
* print_status_info() for the nicer output */
- if (arg_property && !strv_find(arg_property, name))
+ if (arg_properties && !strv_find(arg_properties, name))
return 0;
switch (dbus_message_iter_get_arg_type(iter)) {
@@ -2690,6 +3291,7 @@ static int print_property(const char *name, DBusMessageIter *iter) {
DBusMessageIter sub, sub2;
dbus_message_iter_recurse(iter, &sub);
+
while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
const char *type, *path;
@@ -2704,6 +3306,24 @@ static int print_property(const char *name, DBusMessageIter *iter) {
return 0;
+ } else if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "Listen")) {
+ DBusMessageIter sub, sub2;
+
+ dbus_message_iter_recurse(iter, &sub);
+ while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
+ const char *type, *path;
+
+ dbus_message_iter_recurse(&sub, &sub2);
+
+ if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &type, true) >= 0 &&
+ bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &path, false) >= 0)
+ printf("Listen%s=%s\n", type, path);
+
+ dbus_message_iter_next(&sub);
+ }
+
+ return 0;
+
} else if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "Timers")) {
DBusMessageIter sub, sub2;
@@ -2721,8 +3341,8 @@ static int print_property(const char *name, DBusMessageIter *iter) {
printf("%s={ value=%s ; next_elapse=%s }\n",
base,
- format_timespan(timespan1, sizeof(timespan1), value),
- format_timespan(timespan2, sizeof(timespan2), next_elapse));
+ format_timespan(timespan1, sizeof(timespan1), value, 0),
+ format_timespan(timespan2, sizeof(timespan2), next_elapse, 0));
}
dbus_message_iter_next(&sub);
@@ -2743,7 +3363,7 @@ static int print_property(const char *name, DBusMessageIter *iter) {
bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &attr, true) >= 0 &&
bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &value, false) >= 0) {
- printf("ControlGroupAttribute={ controller=%s ; attribute=%s ; value=\"%s\" }\n",
+ printf("ControlGroupAttributes={ controller=%s ; attribute=%s ; value=\"%s\" }\n",
controller,
attr,
value);
@@ -2759,12 +3379,11 @@ static int print_property(const char *name, DBusMessageIter *iter) {
dbus_message_iter_recurse(iter, &sub);
while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
- ExecStatusInfo info;
+ ExecStatusInfo info = {};
- zero(info);
if (exec_status_info_deserialize(&sub, &info) >= 0) {
char timestamp1[FORMAT_TIMESTAMP_MAX], timestamp2[FORMAT_TIMESTAMP_MAX];
- char *t;
+ _cleanup_free_ char *t;
t = strv_join(info.argv, " ");
@@ -2780,8 +3399,6 @@ static int print_property(const char *name, DBusMessageIter *iter) {
info.status,
info.code == CLD_EXITED ? "" : "/",
strempty(info.code == CLD_EXITED ? NULL : signal_to_string(info.status)));
-
- free(t);
}
free(info.path);
@@ -2806,19 +3423,17 @@ static int print_property(const char *name, DBusMessageIter *iter) {
}
static int show_one(const char *verb, DBusConnection *bus, const char *path, bool show_properties, bool *new_line) {
- DBusMessage *reply = NULL;
+ _cleanup_free_ DBusMessage *reply = NULL;
const char *interface = "";
int r;
DBusMessageIter iter, sub, sub2, sub3;
- UnitStatusInfo info;
+ UnitStatusInfo info = {};
ExecStatusInfo *p;
assert(path);
assert(new_line);
- zero(info);
-
- r = bus_method_call_with_reply (
+ r = bus_method_call_with_reply(
bus,
"org.freedesktop.systemd1",
path,
@@ -2828,15 +3443,14 @@ static int show_one(const char *verb, DBusConnection *bus, const char *path, boo
NULL,
DBUS_TYPE_STRING, &interface,
DBUS_TYPE_INVALID);
- if (r)
- goto finish;
+ if (r < 0)
+ return r;
if (!dbus_message_iter_init(reply, &iter) ||
dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_DICT_ENTRY) {
log_error("Failed to parse reply.");
- r = -EIO;
- goto finish;
+ return -EIO;
}
dbus_message_iter_recurse(&iter, &sub);
@@ -2849,24 +3463,13 @@ static int show_one(const char *verb, DBusConnection *bus, const char *path, boo
while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
const char *name;
- if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_DICT_ENTRY) {
- log_error("Failed to parse reply.");
- r = -EIO;
- goto finish;
- }
-
+ assert(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_DICT_ENTRY);
dbus_message_iter_recurse(&sub, &sub2);
- if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &name, true) < 0) {
+ if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &name, true) < 0 ||
+ dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_VARIANT) {
log_error("Failed to parse reply.");
- r = -EIO;
- goto finish;
- }
-
- if (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_VARIANT) {
- log_error("Failed to parse reply.");
- r = -EIO;
- goto finish;
+ return -EIO;
}
dbus_message_iter_recurse(&sub2, &sub3);
@@ -2875,11 +3478,9 @@ static int show_one(const char *verb, DBusConnection *bus, const char *path, boo
r = print_property(name, &sub3);
else
r = status_property(name, &sub3, &info);
-
if (r < 0) {
log_error("Failed to parse reply.");
- r = -EIO;
- goto finish;
+ return -EIO;
}
dbus_message_iter_next(&sub);
@@ -2895,6 +3496,8 @@ static int show_one(const char *verb, DBusConnection *bus, const char *path, boo
}
strv_free(info.documentation);
+ strv_free(info.dropin_paths);
+ strv_free(info.listen);
if (!streq_ptr(info.active_state, "active") &&
!streq_ptr(info.active_state, "reloading") &&
@@ -2907,22 +3510,18 @@ static int show_one(const char *verb, DBusConnection *bus, const char *path, boo
exec_status_info_free(p);
}
-finish:
- if (reply)
- dbus_message_unref(reply);
-
return r;
}
static int show_one_by_pid(const char *verb, DBusConnection *bus, uint32_t pid, bool *new_line) {
- DBusMessage *reply = NULL;
+ _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
const char *path = NULL;
- DBusError error;
+ _cleanup_dbus_error_free_ DBusError error;
int r;
dbus_error_init(&error);
- r = bus_method_call_with_reply (
+ r = bus_method_call_with_reply(
bus,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
@@ -2932,85 +3531,107 @@ static int show_one_by_pid(const char *verb, DBusConnection *bus, uint32_t pid,
NULL,
DBUS_TYPE_UINT32, &pid,
DBUS_TYPE_INVALID);
- if (r)
- goto finish;
+ if (r < 0)
+ return r;
if (!dbus_message_get_args(reply, &error,
DBUS_TYPE_OBJECT_PATH, &path,
DBUS_TYPE_INVALID)) {
log_error("Failed to parse reply: %s", bus_error_message(&error));
- r = -EIO;
- goto finish;
+ return -EIO;
}
r = show_one(verb, bus, path, false, new_line);
+ return r;
+}
-finish:
- if (reply)
- dbus_message_unref(reply);
+static int show_all(const char* verb, DBusConnection *bus, bool show_properties, bool *new_line) {
+ _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
+ _cleanup_free_ struct unit_info *unit_infos = NULL;
+ unsigned c = 0;
+ const struct unit_info *u;
+ int r;
- dbus_error_free(&error);
+ r = get_unit_list(bus, &reply, &unit_infos, &c);
+ if (r < 0)
+ return r;
- return r;
+ qsort(unit_infos, c, sizeof(struct unit_info), compare_unit_info);
+
+ for (u = unit_infos; u < unit_infos + c; u++) {
+ _cleanup_free_ char *p = NULL;
+
+ if (!output_show_unit(u))
+ continue;
+
+ p = unit_dbus_path_from_name(u->id);
+ if (!p)
+ return log_oom();
+
+ printf("%s -> '%s'\n", u->id, p);
+
+ r = show_one(verb, bus, p, show_properties, new_line);
+ if (r != 0)
+ return r;
+ }
+
+ return 0;
}
static int show(DBusConnection *bus, char **args) {
int r, ret = 0;
- bool show_properties, new_line = false;
+ bool show_properties, show_status, new_line = false;
char **name;
assert(bus);
assert(args);
show_properties = streq(args[0], "show");
+ show_status = streq(args[0], "status");
if (show_properties)
pager_open_if_enabled();
- if (show_properties && strv_length(args) <= 1) {
- /* If not argument is specified inspect the manager
- * itself */
+ /* If no argument is specified inspect the manager itself */
+ if (show_properties && strv_length(args) <= 1)
return show_one(args[0], bus, "/org/freedesktop/systemd1", show_properties, &new_line);
- }
+
+ if (show_status && strv_length(args) <= 1)
+ return show_all(args[0], bus, false, &new_line);
STRV_FOREACH(name, args+1) {
uint32_t id;
if (safe_atou32(*name, &id) < 0) {
- char *p, *n;
+ _cleanup_free_ char *p = NULL, *n = NULL;
/* Interpret as unit name */
n = unit_name_mangle(*name);
- p = unit_dbus_path_from_name(n ? n : *name);
- free(n);
+ if (!n)
+ return log_oom();
+
+ p = unit_dbus_path_from_name(n);
if (!p)
return log_oom();
r = show_one(args[0], bus, p, show_properties, &new_line);
- free(p);
-
if (r != 0)
ret = r;
} else if (show_properties) {
+ _cleanup_free_ char *p = NULL;
/* Interpret as job id */
-
- char *p;
if (asprintf(&p, "/org/freedesktop/systemd1/job/%u", id) < 0)
return log_oom();
r = show_one(args[0], bus, p, show_properties, &new_line);
- free(p);
-
if (r != 0)
ret = r;
} else {
-
/* Interpret as PID */
-
r = show_one_by_pid(args[0], bus, id, &new_line);
if (r != 0)
ret = r;
@@ -3021,7 +3642,7 @@ static int show(DBusConnection *bus, char **args) {
}
static int dump(DBusConnection *bus, char **args) {
- DBusMessage *reply = NULL;
+ _cleanup_free_ DBusMessage *reply = NULL;
DBusError error;
int r;
const char *text;
@@ -3030,7 +3651,7 @@ static int dump(DBusConnection *bus, char **args) {
pager_open_if_enabled();
- r = bus_method_call_with_reply (
+ r = bus_method_call_with_reply(
bus,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
@@ -3039,46 +3660,42 @@ static int dump(DBusConnection *bus, char **args) {
&reply,
NULL,
DBUS_TYPE_INVALID);
- if (r)
- goto finish;
+ if (r < 0)
+ return r;
if (!dbus_message_get_args(reply, &error,
DBUS_TYPE_STRING, &text,
DBUS_TYPE_INVALID)) {
log_error("Failed to parse reply: %s", bus_error_message(&error));
- r = -EIO;
- goto finish;
+ dbus_error_free(&error);
+ return -EIO;
}
fputs(text, stdout);
-
-finish:
- if (reply)
- dbus_message_unref(reply);
-
- dbus_error_free(&error);
-
- return r;
+ return 0;
}
static int snapshot(DBusConnection *bus, char **args) {
- DBusMessage *reply = NULL;
+ _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
DBusError error;
int r;
dbus_bool_t cleanup = FALSE;
DBusMessageIter iter, sub;
const char
- *name = "", *path, *id,
+ *path, *id,
*interface = "org.freedesktop.systemd1.Unit",
*property = "Id";
- char *n;
+ _cleanup_free_ char *n = NULL;
dbus_error_init(&error);
if (strv_length(args) > 1)
- name = args[1];
+ n = snapshot_name_mangle(args[1]);
+ else
+ n = strdup("");
+ if (!n)
+ return log_oom();
- n = unit_name_mangle(name);
r = bus_method_call_with_reply (
bus,
"org.freedesktop.systemd1",
@@ -3087,22 +3704,23 @@ static int snapshot(DBusConnection *bus, char **args) {
"CreateSnapshot",
&reply,
NULL,
- DBUS_TYPE_STRING, n ? (const char**) &n : &name,
+ DBUS_TYPE_STRING, &n,
DBUS_TYPE_BOOLEAN, &cleanup,
DBUS_TYPE_INVALID);
- free(n);
- if (r)
- goto finish;
+ if (r < 0)
+ return r;
if (!dbus_message_get_args(reply, &error,
DBUS_TYPE_OBJECT_PATH, &path,
DBUS_TYPE_INVALID)) {
log_error("Failed to parse reply: %s", bus_error_message(&error));
- r = -EIO;
- goto finish;
+ dbus_error_free(&error);
+ return -EIO;
}
dbus_message_unref(reply);
+ reply = NULL;
+
r = bus_method_call_with_reply (
bus,
"org.freedesktop.systemd1",
@@ -3114,22 +3732,20 @@ static int snapshot(DBusConnection *bus, char **args) {
DBUS_TYPE_STRING, &interface,
DBUS_TYPE_STRING, &property,
DBUS_TYPE_INVALID);
- if (r)
- goto finish;
+ if (r < 0)
+ return r;
if (!dbus_message_iter_init(reply, &iter) ||
dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
log_error("Failed to parse reply.");
- r = -EIO;
- goto finish;
+ return -EIO;
}
dbus_message_iter_recurse(&iter, &sub);
if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING) {
log_error("Failed to parse reply.");
- r = -EIO;
- goto finish;
+ return -EIO;
}
dbus_message_iter_get_basic(&sub, &id);
@@ -3137,70 +3753,37 @@ static int snapshot(DBusConnection *bus, char **args) {
if (!arg_quiet)
puts(id);
-finish:
- if (reply)
- dbus_message_unref(reply);
-
- dbus_error_free(&error);
-
- return r;
+ return 0;
}
static int delete_snapshot(DBusConnection *bus, char **args) {
- DBusMessage *reply = NULL;
- int r = 0;
- DBusError error;
char **name;
assert(args);
- dbus_error_init(&error);
-
STRV_FOREACH(name, args+1) {
- const char *path = NULL;
- char *n;
+ _cleanup_free_ char *n = NULL;
+ int r;
- n = unit_name_mangle(*name);
- r = bus_method_call_with_reply (
+ n = snapshot_name_mangle(*name);
+ if (!n)
+ return log_oom();
+
+ r = bus_method_call_with_reply(
bus,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
- "GetUnit",
- &reply,
- NULL,
- DBUS_TYPE_STRING, n ? &n : name,
- DBUS_TYPE_INVALID);
- free(n);
- if (r)
- goto finish;
-
- if (!dbus_message_get_args(reply, &error,
- DBUS_TYPE_OBJECT_PATH, &path,
- DBUS_TYPE_INVALID)) {
- log_error("Failed to parse reply: %s", bus_error_message(&error));
- r = -EIO;
- dbus_message_unref(reply);
- dbus_error_free(&error);
- goto finish;
- }
- dbus_message_unref(reply);
-
- r = bus_method_call_with_reply (
- bus,
- "org.freedesktop.systemd1",
- path,
- "org.freedesktop.systemd1.Snapshot",
- "Remove",
+ "RemoveSnapshot",
NULL,
NULL,
+ DBUS_TYPE_STRING, &n,
DBUS_TYPE_INVALID);
- if (r)
- goto finish;
+ if (r < 0)
+ return r;
}
-finish:
- return r;
+ return 0;
}
static int daemon_reload(DBusConnection *bus, char **args) {
@@ -3228,7 +3811,7 @@ static int daemon_reload(DBusConnection *bus, char **args) {
/* "daemon-reload" */ "Reload";
}
- r = bus_method_call_with_reply (
+ r = bus_method_call_with_reply(
bus,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
@@ -3246,23 +3829,28 @@ static int daemon_reload(DBusConnection *bus, char **args) {
/* On reexecution, we expect a disconnect, not
* a reply */
r = 0;
- else if (r)
+ else if (r < 0)
log_error("Failed to issue method call: %s", bus_error_message(&error));
- dbus_error_free(&error);
+ dbus_error_free(&error);
return r;
}
static int reset_failed(DBusConnection *bus, char **args) {
int r = 0;
- char **name, *n;
+ char **name;
if (strv_length(args) <= 1)
return daemon_reload(bus, args);
STRV_FOREACH(name, args+1) {
+ _cleanup_free_ char *n;
+
n = unit_name_mangle(*name);
- r = bus_method_call_with_reply (
+ if (!n)
+ return log_oom();
+
+ r = bus_method_call_with_reply(
bus,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
@@ -3270,19 +3858,17 @@ static int reset_failed(DBusConnection *bus, char **args) {
"ResetFailedUnit",
NULL,
NULL,
- DBUS_TYPE_STRING, n ? &n : name,
+ DBUS_TYPE_STRING, &n,
DBUS_TYPE_INVALID);
- free(n);
- if (r)
- goto finish;
+ if (r < 0)
+ return r;
}
-finish:
- return r;
+ return 0;
}
static int show_enviroment(DBusConnection *bus, char **args) {
- DBusMessage *reply = NULL;
+ _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
DBusMessageIter iter, sub, sub2;
int r;
const char
@@ -3291,7 +3877,7 @@ static int show_enviroment(DBusConnection *bus, char **args) {
pager_open_if_enabled();
- r = bus_method_call_with_reply (
+ r = bus_method_call_with_reply(
bus,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
@@ -3302,14 +3888,13 @@ static int show_enviroment(DBusConnection *bus, char **args) {
DBUS_TYPE_STRING, &interface,
DBUS_TYPE_STRING, &property,
DBUS_TYPE_INVALID);
- if (r)
- goto finish;
+ if (r < 0)
+ return r;
if (!dbus_message_iter_init(reply, &iter) ||
dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
log_error("Failed to parse reply.");
- r = -EIO;
- goto finish;
+ return -EIO;
}
dbus_message_iter_recurse(&iter, &sub);
@@ -3317,8 +3902,7 @@ static int show_enviroment(DBusConnection *bus, char **args) {
if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_ARRAY ||
dbus_message_iter_get_element_type(&sub) != DBUS_TYPE_STRING) {
log_error("Failed to parse reply.");
- r = -EIO;
- goto finish;
+ return -EIO;
}
dbus_message_iter_recurse(&sub, &sub2);
@@ -3328,23 +3912,16 @@ static int show_enviroment(DBusConnection *bus, char **args) {
if (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_STRING) {
log_error("Failed to parse reply.");
- r = -EIO;
- goto finish;
+ return -EIO;
}
dbus_message_iter_get_basic(&sub2, &text);
- printf("%s\n", text);
+ puts(text);
dbus_message_iter_next(&sub2);
}
- r = 0;
-
-finish:
- if (reply)
- dbus_message_unref(reply);
-
- return r;
+ return 0;
}
static int switch_root(DBusConnection *bus, char **args) {
@@ -3369,15 +3946,13 @@ static int switch_root(DBusConnection *bus, char **args) {
if (!init)
init = strdup("");
-
- if (!init)
- return log_oom();
-
}
+ if (!init)
+ return log_oom();
log_debug("switching root - root: %s; init: %s", root, init);
- return bus_method_call_with_reply (
+ return bus_method_call_with_reply(
bus,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
@@ -3398,6 +3973,7 @@ static int set_environment(DBusConnection *bus, char **args) {
int r;
assert(bus);
+ assert(args);
dbus_error_init(&error);
@@ -3422,15 +3998,11 @@ static int set_environment(DBusConnection *bus, char **args) {
reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
if (!reply) {
log_error("Failed to issue method call: %s", bus_error_message(&error));
- r = -EIO;
- goto finish;
+ dbus_error_free(&error);
+ return -EIO;
}
- r = 0;
-
-finish:
- dbus_error_free(&error);
- return r;
+ return 0;
}
static int enable_sysv_units(char **args) {
@@ -3439,7 +4011,7 @@ static int enable_sysv_units(char **args) {
#if defined(HAVE_SYSV_COMPAT) && defined(HAVE_CHKCONFIG)
const char *verb = args[0];
unsigned f = 1, t = 1;
- LookupPaths paths;
+ LookupPaths paths = {};
if (arg_scope != UNIT_FILE_SYSTEM)
return 0;
@@ -3452,7 +4024,6 @@ static int enable_sysv_units(char **args) {
/* Processes all SysV units, and reshuffles the array so that
* afterwards only the native units remain */
- zero(paths);
r = lookup_paths_init(&paths, SYSTEMD_SYSTEM, false, NULL, NULL, NULL);
if (r < 0)
return r;
@@ -3460,11 +4031,11 @@ static int enable_sysv_units(char **args) {
r = 0;
for (f = 1; args[f]; f++) {
const char *name;
- char *p;
+ _cleanup_free_ char *p = NULL, *q = NULL;
bool found_native = false, found_sysv;
unsigned c = 1;
const char *argv[6] = { "/sbin/chkconfig", NULL, NULL, NULL, NULL };
- char **k, *l, *q = NULL;
+ char **k, *l;
int j;
pid_t pid;
siginfo_t status;
@@ -3478,8 +4049,6 @@ static int enable_sysv_units(char **args) {
continue;
STRV_FOREACH(k, paths.unit_path) {
- p = NULL;
-
if (!isempty(arg_root))
asprintf(&p, "%s/%s/%s", arg_root, *k, name);
else
@@ -3492,6 +4061,7 @@ static int enable_sysv_units(char **args) {
found_native = access(p, F_OK) >= 0;
free(p);
+ p = NULL;
if (found_native)
break;
@@ -3500,7 +4070,6 @@ static int enable_sysv_units(char **args) {
if (found_native)
continue;
- p = NULL;
if (!isempty(arg_root))
asprintf(&p, "%s/" SYSTEM_SYSVINIT_PATH "/%s", arg_root, name);
else
@@ -3513,10 +4082,8 @@ static int enable_sysv_units(char **args) {
p[strlen(p) - sizeof(".service") + 1] = 0;
found_sysv = access(p, F_OK) >= 0;
- if (!found_sysv) {
- free(p);
+ if (!found_sysv)
continue;
- }
/* Mark this entry, so that we don't try enabling it as native unit */
args[f] = (char*) "";
@@ -3534,8 +4101,6 @@ static int enable_sysv_units(char **args) {
l = strv_join((char**)argv, " ");
if (!l) {
- free(q);
- free(p);
r = log_oom();
goto finish;
}
@@ -3546,8 +4111,6 @@ static int enable_sysv_units(char **args) {
pid = fork();
if (pid < 0) {
log_error("Failed to fork: %m");
- free(p);
- free(q);
r = -errno;
goto finish;
} else if (pid == 0) {
@@ -3557,9 +4120,6 @@ static int enable_sysv_units(char **args) {
_exit(EXIT_FAILURE);
}
- free(p);
- free(q);
-
j = wait_for_terminate(pid, &status);
if (j < 0) {
log_error("Failed to wait for child: %s", strerror(-r));
@@ -3643,10 +4203,12 @@ static int enable_unit(DBusConnection *bus, char **args) {
UnitFileChange *changes = NULL;
unsigned n_changes = 0, i;
int carries_install_info = -1;
- DBusMessage *m = NULL, *reply = NULL;
+ _cleanup_dbus_message_unref_ DBusMessage *m = NULL, *reply = NULL;
int r;
- DBusError error;
- char **mangled_names = NULL;
+ _cleanup_dbus_error_free_ DBusError error;
+ _cleanup_strv_free_ char **mangled_names = NULL;
+
+ dbus_error_init(&error);
r = enable_sysv_units(args);
if (r < 0)
@@ -3655,8 +4217,6 @@ static int enable_unit(DBusConnection *bus, char **args) {
if (!args[1])
return 0;
- dbus_error_init(&error);
-
if (!bus || avoid_bus()) {
if (streq(verb, "enable")) {
r = unit_file_enable(arg_scope, arg_runtime, arg_root, args+1, arg_force, &changes, &n_changes);
@@ -3825,39 +4385,29 @@ static int enable_unit(DBusConnection *bus, char **args) {
}
if (carries_install_info == 0)
- log_warning(
-"The unit files have no [Install] section. They are not meant to be enabled\n"
-"using systemctl.\n"
-"Possible reasons for having this kind of units are:\n"
-"1) A unit may be statically enabled by being symlinked from another unit's\n"
-" .wants/ or .requires/ directory.\n"
-"2) A unit's purpose may be to act as a helper for some other unit which has\n"
-" a requirement dependency on it.\n"
-"3) A unit may be started when needed via activation (socket, path, timer,\n"
-" D-Bus, udev, scripted systemctl call, ...).\n");
+ log_warning("The unit files have no [Install] section. They are not meant to be enabled\n"
+ "using systemctl.\n"
+ "Possible reasons for having this kind of units are:\n"
+ "1) A unit may be statically enabled by being symlinked from another unit's\n"
+ " .wants/ or .requires/ directory.\n"
+ "2) A unit's purpose may be to act as a helper for some other unit which has\n"
+ " a requirement dependency on it.\n"
+ "3) A unit may be started when needed via activation (socket, path, timer,\n"
+ " D-Bus, udev, scripted systemctl call, ...).\n");
finish:
- if (m)
- dbus_message_unref(m);
-
- if (reply)
- dbus_message_unref(reply);
-
unit_file_changes_free(changes, n_changes);
- dbus_error_free(&error);
-
- strv_free(mangled_names);
-
return r;
}
static int unit_is_enabled(DBusConnection *bus, char **args) {
- DBusError error;
+ _cleanup_dbus_error_free_ DBusError error;
int r;
- DBusMessage *reply = NULL;
+ _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
bool enabled;
char **name;
+ char *n;
dbus_error_init(&error);
@@ -3872,11 +4422,16 @@ static int unit_is_enabled(DBusConnection *bus, char **args) {
STRV_FOREACH(name, args+1) {
UnitFileState state;
- state = unit_file_get_state(arg_scope, arg_root, *name);
- if (state < 0) {
- r = state;
- goto finish;
- }
+ n = unit_name_mangle(*name);
+ if (!n)
+ return log_oom();
+
+ state = unit_file_get_state(arg_scope, arg_root, n);
+
+ free(n);
+
+ if (state < 0)
+ return state;
if (state == UNIT_FILE_ENABLED ||
state == UNIT_FILE_ENABLED_RUNTIME ||
@@ -3891,6 +4446,10 @@ static int unit_is_enabled(DBusConnection *bus, char **args) {
STRV_FOREACH(name, args+1) {
const char *s;
+ n = unit_name_mangle(*name);
+ if (!n)
+ return log_oom();
+
r = bus_method_call_with_reply (
bus,
"org.freedesktop.systemd1",
@@ -3899,17 +4458,19 @@ static int unit_is_enabled(DBusConnection *bus, char **args) {
"GetUnitFileState",
&reply,
NULL,
- DBUS_TYPE_STRING, name,
+ DBUS_TYPE_STRING, &n,
DBUS_TYPE_INVALID);
+
+ free(n);
+
if (r)
- goto finish;
+ return r;
if (!dbus_message_get_args(reply, &error,
DBUS_TYPE_STRING, &s,
DBUS_TYPE_INVALID)) {
log_error("Failed to parse reply: %s", bus_error_message(&error));
- r = -EIO;
- goto finish;
+ return -EIO;
}
dbus_message_unref(reply);
@@ -3925,14 +4486,7 @@ static int unit_is_enabled(DBusConnection *bus, char **args) {
}
}
- r = enabled ? 0 : 1;
-
-finish:
- if (reply)
- dbus_message_unref(reply);
-
- dbus_error_free(&error);
- return r;
+ return enabled ? 0 : 1;
}
static int systemctl_help(void) {
@@ -3945,13 +4499,20 @@ static int systemctl_help(void) {
" --version Show package version\n"
" -t --type=TYPE List only units of a particular type\n"
" -p --property=NAME Show only properties by this name\n"
- " -a --all Show all units/properties, including dead/empty ones\n"
+ " -a --all Show all loaded units/properties, including dead/empty\n"
+ " ones. To list all units installed on the system, use\n"
+ " the 'list-unit-files' command instead.\n"
+ " --reverse Show reverse dependencies with 'list-dependencies'\n"
" --failed Show only failed units\n"
" --full Don't ellipsize unit names on output\n"
" --fail When queueing a new job, fail if conflicting jobs are\n"
" pending\n"
+ " --irreversible Create jobs which cannot be implicitly cancelled\n"
+ " --show-types When showing sockets, explicitly show their type\n"
" --ignore-dependencies\n"
" When queueing a new job, ignore all its dependencies\n"
+ " -i --ignore-inhibitors\n"
+ " When shutting down or sleeping, ignore inhibitors\n"
" --kill-who=WHO Who to send signal to\n"
" -s --signal=SIGNAL Which signal to send\n"
" -H --host=[USER@]HOST\n"
@@ -3966,8 +4527,6 @@ static int systemctl_help(void) {
" --no-pager Do not pipe output into a pager\n"
" --no-ask-password\n"
" Do not ask for system passwords\n"
- " --order When generating graph for dot, show only order\n"
- " --require When generating graph for dot, show only requirement\n"
" --system Connect to system manager\n"
" --user Connect to user service manager\n"
" --global Enable/disable unit files globally\n"
@@ -3996,10 +4555,21 @@ static int systemctl_help(void) {
" status [NAME...|PID...] Show runtime status of one or more units\n"
" show [NAME...|JOB...] Show properties of one or more\n"
" units/jobs or the manager\n"
- " help [NAME...|PID...] Show manual for one or more units\n"
+ " help [NAME...|PID...] Show manual for one or more units\n"
" reset-failed [NAME...] Reset failed state for all, one, or more\n"
" units\n"
- " load [NAME...] Load one or more units\n\n"
+ " get-cgroup-attr [NAME] [ATTR] ...\n"
+ " Get control group attrubute\n"
+ " set-cgroup-attr [NAME] [ATTR] [VALUE] ...\n"
+ " Set control group attribute\n"
+ " unset-cgroup-attr [NAME] [ATTR...]\n"
+ " Unset control group attribute\n"
+ " set-cgroup [NAME] [CGROUP...] Add unit to a control group\n"
+ " unset-cgroup [NAME] [CGROUP...] Remove unit from a control group\n"
+ " load [NAME...] Load one or more units\n"
+ " list-dependencies [NAME] Recursively show units which are required\n"
+ " or wanted by this unit or by which this\n"
+ " unit is required or wanted\n\n"
"Unit File Commands:\n"
" list-unit-files List installed unit files\n"
" enable [NAME...] Enable one or more unit files\n"
@@ -4017,7 +4587,6 @@ static int systemctl_help(void) {
" cancel [JOB...] Cancel all, one, or more jobs\n\n"
"Status Commands:\n"
" dump Dump server status\n"
- " dot Dump dependency graph for dot(1)\n\n"
"Snapshot Commands:\n"
" snapshot [NAME] Create a snapshot\n"
" delete [NAME...] Remove one or more snapshots\n\n"
@@ -4113,16 +4682,21 @@ static int runlevel_help(void) {
static int help_types(void) {
int i;
+ const char *t;
puts("Available unit types:");
- for(i = UNIT_SERVICE; i < _UNIT_TYPE_MAX; i++)
- if (unit_type_table[i])
- puts(unit_type_table[i]);
+ for(i = 0; i < _UNIT_TYPE_MAX; i++) {
+ t = unit_type_to_string(i);
+ if (t)
+ puts(t);
+ }
puts("\nAvailable unit load states: ");
- for(i = UNIT_STUB; i < _UNIT_LOAD_STATE_MAX; i++)
- if (unit_type_table[i])
- puts(unit_load_state_table[i]);
+ for(i = 0; i < _UNIT_LOAD_STATE_MAX; i++) {
+ t = unit_load_state_to_string(i);
+ if (t)
+ puts(t);
+ }
return 0;
}
@@ -4131,6 +4705,11 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
enum {
ARG_FAIL = 0x100,
+ ARG_REVERSE,
+ ARG_AFTER,
+ ARG_BEFORE,
+ ARG_SHOW_TYPES,
+ ARG_IRREVERSIBLE,
ARG_IGNORE_DEPENDENCIES,
ARG_VERSION,
ARG_USER,
@@ -4140,8 +4719,6 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
ARG_NO_LEGEND,
ARG_NO_PAGER,
ARG_NO_WALL,
- ARG_ORDER,
- ARG_REQUIRE,
ARG_ROOT,
ARG_FULL,
ARG_NO_RELOAD,
@@ -4149,7 +4726,8 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
ARG_NO_ASK_PASSWORD,
ARG_FAILED,
ARG_RUNTIME,
- ARG_FORCE
+ ARG_FORCE,
+ ARG_PLAIN
};
static const struct option options[] = {
@@ -4158,10 +4736,16 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
{ "type", required_argument, NULL, 't' },
{ "property", required_argument, NULL, 'p' },
{ "all", no_argument, NULL, 'a' },
+ { "reverse", no_argument, NULL, ARG_REVERSE },
+ { "after", no_argument, NULL, ARG_AFTER },
+ { "before", no_argument, NULL, ARG_BEFORE },
+ { "show-types", no_argument, NULL, ARG_SHOW_TYPES },
{ "failed", no_argument, NULL, ARG_FAILED },
{ "full", no_argument, NULL, ARG_FULL },
{ "fail", no_argument, NULL, ARG_FAIL },
+ { "irreversible", no_argument, NULL, ARG_IRREVERSIBLE },
{ "ignore-dependencies", no_argument, NULL, ARG_IGNORE_DEPENDENCIES },
+ { "ignore-inhibitors", no_argument, NULL, 'i' },
{ "user", no_argument, NULL, ARG_USER },
{ "system", no_argument, NULL, ARG_SYSTEM },
{ "global", no_argument, NULL, ARG_GLOBAL },
@@ -4170,8 +4754,6 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
{ "no-pager", no_argument, NULL, ARG_NO_PAGER },
{ "no-wall", no_argument, NULL, ARG_NO_WALL },
{ "quiet", no_argument, NULL, 'q' },
- { "order", no_argument, NULL, ARG_ORDER },
- { "require", no_argument, NULL, ARG_REQUIRE },
{ "root", required_argument, NULL, ARG_ROOT },
{ "force", no_argument, NULL, ARG_FORCE },
{ "no-reload", no_argument, NULL, ARG_NO_RELOAD },
@@ -4183,6 +4765,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
{ "runtime", no_argument, NULL, ARG_RUNTIME },
{ "lines", required_argument, NULL, 'n' },
{ "output", required_argument, NULL, 'o' },
+ { "plain", no_argument, NULL, ARG_PLAIN },
{ NULL, 0, NULL, 0 }
};
@@ -4191,7 +4774,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
assert(argc >= 0);
assert(argv);
- while ((c = getopt_long(argc, argv, "ht:p:aqfs:H:Pn:o:", options, NULL)) >= 0) {
+ while ((c = getopt_long(argc, argv, "ht:p:aqfs:H:Pn:o:i", options, NULL)) >= 0) {
switch (c) {
@@ -4204,37 +4787,74 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
puts(SYSTEMD_FEATURES);
return 0;
- case 't':
- if (streq(optarg, "help")) {
- help_types();
- return 0;
- }
+ case 't': {
+ char *word, *state;
+ size_t size;
- if (unit_type_from_string(optarg) >= 0) {
- arg_type = optarg;
- break;
- }
- if (unit_load_state_from_string(optarg) >= 0) {
- arg_load_state = optarg;
- break;
+ FOREACH_WORD_SEPARATOR(word, size, optarg, ",", state) {
+ _cleanup_free_ char *type;
+
+ type = strndup(word, size);
+ if (!type)
+ return -ENOMEM;
+
+ if (streq(type, "help")) {
+ help_types();
+ return 0;
+ }
+
+ if (unit_type_from_string(type) >= 0) {
+ if (strv_push(&arg_types, type))
+ return log_oom();
+ type = NULL;
+ continue;
+ }
+
+ if (unit_load_state_from_string(optarg) >= 0) {
+ if (strv_push(&arg_load_states, type))
+ return log_oom();
+ type = NULL;
+ continue;
+ }
+
+ log_error("Unknown unit type or load state '%s'.", type);
+ log_info("Use -t help to see a list of allowed values.");
+ return -EINVAL;
}
- log_error("Unkown unit type or load state '%s'.",
- optarg);
- log_info("Use -t help to see a list of allowed values.");
- return -EINVAL;
+
+ break;
+ }
+
case 'p': {
- char **l;
+ /* Make sure that if the empty property list
+ was specified, we won't show any properties. */
+ if (isempty(optarg) && !arg_properties) {
+ arg_properties = strv_new(NULL, NULL);
+ if (!arg_properties)
+ return log_oom();
+ } else {
+ char *word, *state;
+ size_t size;
- if (!(l = strv_append(arg_property, optarg)))
- return -ENOMEM;
+ FOREACH_WORD_SEPARATOR(word, size, optarg, ",", state) {
+ char *prop;
- strv_free(arg_property);
- arg_property = l;
+ prop = strndup(word, size);
+ if (!prop)
+ return log_oom();
+
+ if (strv_push(&arg_properties, prop)) {
+ free(prop);
+ return log_oom();
+ }
+ }
+ }
/* If the user asked for a particular
* property, show it to him, even if it is
* empty. */
arg_all = true;
+
break;
}
@@ -4242,10 +4862,30 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
arg_all = true;
break;
+ case ARG_REVERSE:
+ arg_dependency = DEPENDENCY_REVERSE;
+ break;
+
+ case ARG_AFTER:
+ arg_dependency = DEPENDENCY_AFTER;
+ break;
+
+ case ARG_BEFORE:
+ arg_dependency = DEPENDENCY_BEFORE;
+ break;
+
+ case ARG_SHOW_TYPES:
+ arg_show_types = true;
+ break;
+
case ARG_FAIL:
arg_job_mode = "fail";
break;
+ case ARG_IRREVERSIBLE:
+ arg_job_mode = "replace-irreversibly";
+ break;
+
case ARG_IGNORE_DEPENDENCIES:
arg_job_mode = "ignore-dependencies";
break;
@@ -4278,14 +4918,6 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
arg_no_wall = true;
break;
- case ARG_ORDER:
- arg_dot = DOT_ORDER;
- break;
-
- case ARG_REQUIRE:
- arg_dot = DOT_REQUIRE;
- break;
-
case ARG_ROOT:
arg_root = optarg;
break;
@@ -4357,6 +4989,14 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
}
break;
+ case 'i':
+ arg_ignore_inhibitors = true;
+ break;
+
+ case ARG_PLAIN:
+ arg_plain = true;
+ break;
+
case '?':
return -EINVAL;
@@ -4479,23 +5119,22 @@ static int parse_time_spec(const char *t, usec_t *_u) {
} else {
char *e = NULL;
long hour, minute;
- struct tm tm;
+ struct tm tm = {};
time_t s;
usec_t n;
errno = 0;
hour = strtol(t, &e, 10);
- if (errno != 0 || *e != ':' || hour < 0 || hour > 23)
+ if (errno > 0 || *e != ':' || hour < 0 || hour > 23)
return -EINVAL;
minute = strtol(e+1, &e, 10);
- if (errno != 0 || *e != 0 || minute < 0 || minute > 59)
+ if (errno > 0 || *e != 0 || minute < 0 || minute > 59)
return -EINVAL;
n = now(CLOCK_REALTIME);
s = (time_t) (n / USEC_PER_SEC);
- zero(tm);
assert_se(localtime_r(&s, &tm));
tm.tm_hour = (int) hour;
@@ -4774,7 +5413,7 @@ static int parse_argv(int argc, char *argv[]) {
* request to it. For now we simply
* guess that it is Upstart. */
- execv("/lib/upstart/telinit", argv);
+ execv(TELINIT, argv);
log_error("Couldn't find an alternative telinit implementation to spawn.");
return -EIO;
@@ -4790,7 +5429,7 @@ static int parse_argv(int argc, char *argv[]) {
return systemctl_parse_argv(argc, argv);
}
-static int action_to_runlevel(void) {
+_pure_ static int action_to_runlevel(void) {
static const char table[_ACTION_MAX] = {
[ACTION_HALT] = '0',
@@ -4809,8 +5448,8 @@ static int action_to_runlevel(void) {
}
static int talk_upstart(void) {
- DBusMessage *m = NULL, *reply = NULL;
- DBusError error;
+ _cleanup_dbus_message_unref_ DBusMessage *m = NULL, *reply = NULL;
+ _cleanup_dbus_error_free_ DBusError error;
int previous, rl, r;
char
env1_buf[] = "RUNLEVEL=X",
@@ -4887,39 +5526,32 @@ static int talk_upstart(void) {
r = 1;
finish:
- if (m)
- dbus_message_unref(m);
-
- if (reply)
- dbus_message_unref(reply);
-
if (bus) {
dbus_connection_flush(bus);
dbus_connection_close(bus);
dbus_connection_unref(bus);
}
- dbus_error_free(&error);
-
return r;
}
static int talk_initctl(void) {
- struct init_request request;
- int r, fd;
+ struct init_request request = {};
+ int r;
+ _cleanup_close_ int fd = -1;
char rl;
- if (!(rl = action_to_runlevel()))
+ rl = action_to_runlevel();
+ if (!rl)
return 0;
- zero(request);
request.magic = INIT_MAGIC;
request.sleeptime = 0;
request.cmd = INIT_CMD_RUNLVL;
request.runlevel = rl;
- if ((fd = open(INIT_FIFO, O_WRONLY|O_NDELAY|O_CLOEXEC|O_NOCTTY)) < 0) {
-
+ fd = open(INIT_FIFO, O_WRONLY|O_NDELAY|O_CLOEXEC|O_NOCTTY);
+ if (fd < 0) {
if (errno == ENOENT)
return 0;
@@ -4929,11 +5561,9 @@ static int talk_initctl(void) {
errno = 0;
r = loop_write(fd, &request, sizeof(request), false) != sizeof(request);
- close_nointr_nofail(fd);
-
- if (r < 0) {
+ if (r) {
log_error("Failed to write to "INIT_FIFO": %m");
- return errno ? -errno : -EIO;
+ return errno > 0 ? -errno : -EIO;
}
return 1;
@@ -4953,6 +5583,7 @@ static int systemctl_main(DBusConnection *bus, int argc, char *argv[], DBusError
} verbs[] = {
{ "list-units", LESS, 1, list_units },
{ "list-unit-files", EQUAL, 1, list_unit_files },
+ { "list-sockets", LESS, 1, list_sockets },
{ "list-jobs", EQUAL, 1, list_jobs },
{ "clear-jobs", EQUAL, 1, daemon_reload },
{ "load", MORE, 2, load_unit },
@@ -4969,15 +5600,19 @@ static int systemctl_main(DBusConnection *bus, int argc, char *argv[], DBusError
{ "condreload", MORE, 2, start_unit }, /* For compatibility with ALTLinux */
{ "condrestart", MORE, 2, start_unit }, /* For compatibility with RH */
{ "isolate", EQUAL, 2, start_unit },
+ { "set-cgroup", MORE, 3, set_cgroup },
+ { "unset-cgroup", MORE, 3, set_cgroup },
+ { "get-cgroup-attr", MORE, 3, get_cgroup_attr },
+ { "set-cgroup-attr", MORE, 4, set_cgroup_attr },
+ { "unset-cgroup-attr", MORE, 3, set_cgroup },
{ "kill", MORE, 2, kill_unit },
{ "is-active", MORE, 2, check_unit_active },
{ "check", MORE, 2, check_unit_active },
{ "is-failed", MORE, 2, check_unit_failed },
{ "show", MORE, 1, show },
- { "status", MORE, 2, show },
+ { "status", MORE, 1, show },
{ "help", MORE, 2, show },
{ "dump", EQUAL, 1, dump },
- { "dot", EQUAL, 1, dot },
{ "snapshot", LESS, 2, snapshot },
{ "delete", MORE, 2, delete_snapshot },
{ "daemon-reload", EQUAL, 1, daemon_reload },
@@ -5006,6 +5641,7 @@ static int systemctl_main(DBusConnection *bus, int argc, char *argv[], DBusError
{ "unmask", MORE, 2, enable_unit },
{ "link", MORE, 2, enable_unit },
{ "switch-root", MORE, 2, switch_root },
+ { "list-dependencies", LESS, 2, list_dependencies },
};
int left;
@@ -5105,49 +5741,43 @@ static int systemctl_main(DBusConnection *bus, int argc, char *argv[], DBusError
}
static int send_shutdownd(usec_t t, char mode, bool dry_run, bool warn, const char *message) {
- int fd;
- struct msghdr msghdr;
- struct iovec iovec[2];
- union sockaddr_union sockaddr;
- struct sd_shutdown_command c;
+ _cleanup_close_ int fd;
+ struct sd_shutdown_command c = {
+ .usec = t,
+ .mode = mode,
+ .dry_run = dry_run,
+ .warn_wall = warn,
+ };
+ union sockaddr_union sockaddr = {
+ .un.sun_family = AF_UNIX,
+ .un.sun_path = "/run/systemd/shutdownd",
+ };
+ struct iovec iovec[2] = {
+ {.iov_base = (char*) &c,
+ .iov_len = offsetof(struct sd_shutdown_command, wall_message),
+ }
+ };
+ struct msghdr msghdr = {
+ .msg_name = &sockaddr,
+ .msg_namelen = offsetof(struct sockaddr_un, sun_path)
+ + sizeof("/run/systemd/shutdownd") - 1,
+ .msg_iov = iovec,
+ .msg_iovlen = 1,
+ };
fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0);
if (fd < 0)
return -errno;
- zero(c);
- c.usec = t;
- c.mode = mode;
- c.dry_run = dry_run;
- c.warn_wall = warn;
-
- zero(sockaddr);
- sockaddr.sa.sa_family = AF_UNIX;
- strncpy(sockaddr.un.sun_path, "/run/systemd/shutdownd", sizeof(sockaddr.un.sun_path));
-
- zero(msghdr);
- msghdr.msg_name = &sockaddr;
- msghdr.msg_namelen = offsetof(struct sockaddr_un, sun_path) + sizeof("/run/systemd/shutdownd") - 1;
-
- zero(iovec);
- iovec[0].iov_base = (char*) &c;
- iovec[0].iov_len = offsetof(struct sd_shutdown_command, wall_message);
-
- if (isempty(message))
- msghdr.msg_iovlen = 1;
- else {
+ if (!isempty(message)) {
iovec[1].iov_base = (char*) message;
iovec[1].iov_len = strlen(message);
- msghdr.msg_iovlen = 2;
+ msghdr.msg_iovlen++;
}
- msghdr.msg_iov = iovec;
- if (sendmsg(fd, &msghdr, MSG_NOSIGNAL) < 0) {
- close_nointr_nofail(fd);
+ if (sendmsg(fd, &msghdr, MSG_NOSIGNAL) < 0)
return -errno;
- }
- close_nointr_nofail(fd);
return 0;
}
@@ -5229,6 +5859,10 @@ static _noreturn_ void halt_now(enum action a) {
static int halt_main(DBusConnection *bus) {
int r;
+ r = check_inhibitors(bus, arg_action);
+ if (r < 0)
+ return r;
+
if (geteuid() != 0) {
/* Try logind if we are a normal user and no special
* mode applies. Maybe PolicyKit allows us to shutdown
@@ -5236,7 +5870,7 @@ static int halt_main(DBusConnection *bus) {
if (arg_when <= 0 &&
!arg_dry &&
- !arg_force &&
+ arg_force <= 0 &&
(arg_action == ACTION_POWEROFF ||
arg_action == ACTION_REBOOT)) {
r = reboot_with_logind(bus, arg_action);
@@ -5249,7 +5883,7 @@ static int halt_main(DBusConnection *bus) {
}
if (arg_when > 0) {
- char *m;
+ _cleanup_free_ char *m;
m = strv_join(arg_wall, " ");
r = send_shutdownd(arg_when,
@@ -5260,7 +5894,6 @@ static int halt_main(DBusConnection *bus) {
arg_dry,
!arg_no_wall,
m);
- free(m);
if (r < 0)
log_warning("Failed to talk to shutdownd, proceeding with immediate shutdown: %s", strerror(-r));
@@ -5313,7 +5946,7 @@ static int runlevel_main(void) {
int main(int argc, char*argv[]) {
int r, retval = EXIT_FAILURE;
DBusConnection *bus = NULL;
- DBusError error;
+ _cleanup_dbus_error_free_ DBusError error;
dbus_error_init(&error);
@@ -5416,11 +6049,11 @@ finish:
dbus_connection_unref(bus);
}
- dbus_error_free(&error);
-
dbus_shutdown();
- strv_free(arg_property);
+ strv_free(arg_types);
+ strv_free(arg_load_states);
+ strv_free(arg_properties);
pager_close();
ask_password_agent_close();
diff --git a/src/systemd/sd-bus-protocol.h b/src/systemd/sd-bus-protocol.h
new file mode 100644
index 0000000000..a26d27c31a
--- /dev/null
+++ b/src/systemd/sd-bus-protocol.h
@@ -0,0 +1,153 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef foosdbusprotocolhfoo
+#define foosdbusprotocolhfoo
+
+/***
+ 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 <http://www.gnu.org/licenses/>.
+***/
+
+#include <endian.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Types of message */
+
+enum {
+ _SD_BUS_MESSAGE_TYPE_INVALID = 0,
+ SD_BUS_MESSAGE_TYPE_METHOD_CALL,
+ SD_BUS_MESSAGE_TYPE_METHOD_RETURN,
+ SD_BUS_MESSAGE_TYPE_METHOD_ERROR,
+ SD_BUS_MESSAGE_TYPE_SIGNAL,
+ _SD_BUS_MESSAGE_TYPE_MAX
+};
+
+/* Primitive types */
+
+enum {
+ _SD_BUS_TYPE_INVALID = 0,
+ SD_BUS_TYPE_BYTE = 'y',
+ SD_BUS_TYPE_BOOLEAN = 'b',
+ SD_BUS_TYPE_INT16 = 'n',
+ SD_BUS_TYPE_UINT16 = 'q',
+ SD_BUS_TYPE_INT32 = 'i',
+ SD_BUS_TYPE_UINT32 = 'u',
+ SD_BUS_TYPE_INT64 = 'x',
+ SD_BUS_TYPE_UINT64 = 't',
+ SD_BUS_TYPE_DOUBLE = 'd',
+ SD_BUS_TYPE_STRING = 's',
+ SD_BUS_TYPE_OBJECT_PATH = 'o',
+ SD_BUS_TYPE_SIGNATURE = 'g',
+ SD_BUS_TYPE_UNIX_FD = 'h',
+ SD_BUS_TYPE_ARRAY = 'a',
+ SD_BUS_TYPE_VARIANT = 'v',
+ SD_BUS_TYPE_STRUCT = 'r', /* not actually used in signatures */
+ SD_BUS_TYPE_STRUCT_BEGIN = '(',
+ SD_BUS_TYPE_STRUCT_END = ')',
+ SD_BUS_TYPE_DICT_ENTRY = 'e', /* not actually used in signatures */
+ SD_BUS_TYPE_DICT_ENTRY_BEGIN = '{',
+ SD_BUS_TYPE_DICT_ENTRY_END = '}',
+};
+
+/* Endianness */
+
+enum {
+ _SD_BUS_INVALID_ENDIAN = 0,
+ SD_BUS_LITTLE_ENDIAN = 'l',
+ SD_BUS_BIG_ENDIAN = 'B',
+#if __BYTE_ORDER == __BIG_ENDIAN
+ SD_BUS_NATIVE_ENDIAN = SD_BUS_BIG_ENDIAN,
+ SD_BUS_REVERSE_ENDIAN = SD_BUS_LITTLE_ENDIAN
+#else
+ SD_BUS_NATIVE_ENDIAN = SD_BUS_LITTLE_ENDIAN,
+ SD_BUS_REVERSE_ENDIAN = SD_BUS_BIG_ENDIAN
+#endif
+};
+
+/* Flags */
+
+enum {
+ SD_BUS_MESSAGE_NO_REPLY_EXPECTED = 1,
+ SD_BUS_MESSAGE_NO_AUTO_START = 2
+};
+
+/* Header fields */
+
+enum {
+ _SD_BUS_MESSAGE_HEADER_INVALID = 0,
+ SD_BUS_MESSAGE_HEADER_PATH,
+ SD_BUS_MESSAGE_HEADER_INTERFACE,
+ SD_BUS_MESSAGE_HEADER_MEMBER,
+ SD_BUS_MESSAGE_HEADER_ERROR_NAME,
+ SD_BUS_MESSAGE_HEADER_REPLY_SERIAL,
+ SD_BUS_MESSAGE_HEADER_DESTINATION,
+ SD_BUS_MESSAGE_HEADER_SENDER,
+ SD_BUS_MESSAGE_HEADER_SIGNATURE,
+ SD_BUS_MESSAGE_HEADER_UNIX_FDS,
+ _SD_BUS_MESSAGE_HEADER_MAX
+};
+
+#define SD_BUS_INTROSPECT_DOCTYPE \
+ "<!DOCTYPE node PUBLIC \"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\"\n" \
+ "\"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\">\n"
+
+#define SD_BUS_INTROSPECT_INTERFACE_PROPERTIES \
+ " <interface name=\"org.freedesktop.DBus.Properties\">\n" \
+ " <method name=\"Get\">\n" \
+ " <arg name=\"interface\" direction=\"in\" type=\"s\"/>\n" \
+ " <arg name=\"property\" direction=\"in\" type=\"s\"/>\n" \
+ " <arg name=\"value\" direction=\"out\" type=\"v\"/>\n" \
+ " </method>\n" \
+ " <method name=\"GetAll\">\n" \
+ " <arg name=\"interface\" direction=\"in\" type=\"s\"/>\n" \
+ " <arg name=\"properties\" direction=\"out\" type=\"a{sv}\"/>\n" \
+ " </method>\n" \
+ " <method name=\"Set\">\n" \
+ " <arg name=\"interface\" direction=\"in\" type=\"s\"/>\n" \
+ " <arg name=\"property\" direction=\"in\" type=\"s\"/>\n" \
+ " <arg name=\"value\" direction=\"in\" type=\"v\"/>\n" \
+ " </method>\n" \
+ " <signal name=\"PropertiesChanged\">\n" \
+ " <arg type=\"s\" name=\"interface\"/>\n" \
+ " <arg type=\"a{sv}\" name=\"changed_properties\"/>\n" \
+ " <arg type=\"as\" name=\"invalidated_properties\"/>\n" \
+ " </signal>\n" \
+ " </interface>\n"
+
+#define SD_BUS_INTROSPECT_INTERFACE_INTROSPECTABLE \
+ " <interface name=\"org.freedesktop.DBus.Introspectable\">\n" \
+ " <method name=\"Introspect\">\n" \
+ " <arg name=\"data\" type=\"s\" direction=\"out\"/>\n" \
+ " </method>\n" \
+ " </interface>\n"
+
+#define SD_BUS_INTROSPECT_INTERFACE_PEER \
+ "<interface name=\"org.freedesktop.DBus.Peer\">\n" \
+ " <method name=\"Ping\"/>\n" \
+ " <method name=\"GetMachineId\">\n" \
+ " <arg type=\"s\" name=\"machine_uuid\" direction=\"out\"/>\n" \
+ " </method>\n" \
+ "</interface>\n"
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/systemd/sd-bus.h b/src/systemd/sd-bus.h
new file mode 100644
index 0000000000..c1ec50871f
--- /dev/null
+++ b/src/systemd/sd-bus.h
@@ -0,0 +1,210 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef foosdbushfoo
+#define foosdbushfoo
+
+/***
+ 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 <http://www.gnu.org/licenses/>.
+***/
+
+#include <inttypes.h>
+#include <sys/types.h>
+
+#include <sd-id128.h>
+#include "sd-bus-protocol.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef _sd_printf_attr_
+# if __GNUC__ >= 4
+# define _sd_printf_attr_(a,b) __attribute__ ((format (printf, a, b)))
+# else
+# define _sd_printf_attr_(a,b)
+# endif
+#endif
+
+/* TODO:
+ * - add page donation logic
+ * - api for appending/reading fixed arrays
+ * - merge busctl into systemctl or so?
+ * - default policy (allow uid == 0 and our own uid)
+ *
+ * - enforce alignment of pointers passed in
+ * - negotiation for attach attributes
+ *
+ * - for kernel and unix transports allow setting the unix user/access mode for the node
+ */
+
+typedef struct sd_bus sd_bus;
+typedef struct sd_bus_message sd_bus_message;
+
+typedef struct {
+ const char *name;
+ const char *message;
+ int need_free;
+} sd_bus_error;
+
+typedef int (*sd_bus_message_handler_t)(sd_bus *bus, int ret, sd_bus_message *m, void *userdata);
+
+/* Connections */
+
+int sd_bus_open_system(sd_bus **ret);
+int sd_bus_open_user(sd_bus **ret);
+
+int sd_bus_new(sd_bus **ret);
+int sd_bus_set_address(sd_bus *bus, const char *address);
+int sd_bus_set_fd(sd_bus *bus, int input_fd, int output_fd);
+int sd_bus_set_exec(sd_bus *bus, const char *path, char *const argv[]);
+int sd_bus_set_bus_client(sd_bus *bus, int b);
+int sd_bus_set_server(sd_bus *bus, int b, sd_id128_t server_id);
+int sd_bus_set_anonymous(sd_bus *bus, int b);
+int sd_bus_set_negotiate_fds(sd_bus *bus, int b);
+int sd_bus_start(sd_bus *ret);
+
+void sd_bus_close(sd_bus *bus);
+
+sd_bus *sd_bus_ref(sd_bus *bus);
+sd_bus *sd_bus_unref(sd_bus *bus);
+
+int sd_bus_is_open(sd_bus *bus);
+int sd_bus_can_send(sd_bus *bus, char type);
+int sd_bus_get_server_id(sd_bus *bus, sd_id128_t *peer);
+
+int sd_bus_send(sd_bus *bus, sd_bus_message *m, uint64_t *serial);
+int sd_bus_send_with_reply(sd_bus *bus, sd_bus_message *m, sd_bus_message_handler_t callback, void *userdata, uint64_t usec, uint64_t *serial);
+int sd_bus_send_with_reply_cancel(sd_bus *bus, uint64_t serial);
+int sd_bus_send_with_reply_and_block(sd_bus *bus, sd_bus_message *m, uint64_t usec, sd_bus_error *error, sd_bus_message **r);
+
+int sd_bus_get_fd(sd_bus *bus);
+int sd_bus_get_events(sd_bus *bus);
+int sd_bus_get_timeout(sd_bus *bus, uint64_t *timeout_usec);
+int sd_bus_process(sd_bus *bus, sd_bus_message **r);
+int sd_bus_wait(sd_bus *bus, uint64_t timeout_usec);
+int sd_bus_flush(sd_bus *bus);
+
+int sd_bus_add_filter(sd_bus *bus, sd_bus_message_handler_t callback, void *userdata);
+int sd_bus_remove_filter(sd_bus *bus, sd_bus_message_handler_t callback, void *userdata);
+
+int sd_bus_add_object(sd_bus *bus, const char *path, sd_bus_message_handler_t callback, void *userdata);
+int sd_bus_remove_object(sd_bus *bus, const char *path, sd_bus_message_handler_t callback, void *userdata);
+
+int sd_bus_add_fallback(sd_bus *bus, const char *prefix, sd_bus_message_handler_t callback, void *userdata);
+int sd_bus_remove_fallback(sd_bus *bus, const char *prefix, sd_bus_message_handler_t callback, void *userdata);
+
+int sd_bus_add_match(sd_bus *bus, const char *match, sd_bus_message_handler_t callback, void *userdata);
+int sd_bus_remove_match(sd_bus *bus, const char *match, sd_bus_message_handler_t callback, void *userdata);
+
+/* Message object */
+
+int sd_bus_message_new_signal(sd_bus *bus, const char *path, const char *interface, const char *member, sd_bus_message **m);
+int sd_bus_message_new_method_call(sd_bus *bus, const char *destination, const char *path, const char *interface, const char *member, sd_bus_message **m);
+int sd_bus_message_new_method_return(sd_bus *bus, sd_bus_message *call, sd_bus_message **m);
+int sd_bus_message_new_method_error(sd_bus *bus, sd_bus_message *call, const sd_bus_error *e, sd_bus_message **m);
+
+sd_bus_message* sd_bus_message_ref(sd_bus_message *m);
+sd_bus_message* sd_bus_message_unref(sd_bus_message *m);
+
+int sd_bus_message_get_type(sd_bus_message *m, uint8_t *type);
+int sd_bus_message_get_serial(sd_bus_message *m, uint64_t *serial);
+int sd_bus_message_get_reply_serial(sd_bus_message *m, uint64_t *serial);
+int sd_bus_message_get_no_reply(sd_bus_message *m);
+
+const char *sd_bus_message_get_path(sd_bus_message *m);
+const char *sd_bus_message_get_interface(sd_bus_message *m);
+const char *sd_bus_message_get_member(sd_bus_message *m);
+const char *sd_bus_message_get_destination(sd_bus_message *m);
+const char *sd_bus_message_get_sender(sd_bus_message *m);
+const sd_bus_error *sd_bus_message_get_error(sd_bus_message *m);
+
+int sd_bus_message_get_monotonic_timestamp(sd_bus_message *m, uint64_t *usec);
+int sd_bus_message_get_realtime_timestamp(sd_bus_message *m, uint64_t *usec);
+int sd_bus_message_get_uid(sd_bus_message *m, uid_t *uid);
+int sd_bus_message_get_gid(sd_bus_message *m, gid_t *gid);
+int sd_bus_message_get_pid(sd_bus_message *m, pid_t *pid);
+int sd_bus_message_get_tid(sd_bus_message *m, pid_t *tid);
+int sd_bus_message_get_pid_starttime(sd_bus_message *m, uint64_t *usec);
+int sd_bus_message_get_selinux_context(sd_bus_message *m, const char **r);
+int sd_bus_message_get_comm(sd_bus_message *m, const char **r);
+int sd_bus_message_get_tid_comm(sd_bus_message *m, const char **r);
+int sd_bus_message_get_exe(sd_bus_message *m, const char **r);
+int sd_bus_message_get_cgroup(sd_bus_message *m, const char **r);
+int sd_bus_message_get_cmdline(sd_bus_message *m, char ***cmdline);
+int sd_bus_message_get_unit(sd_bus_message *m, const char **unit);
+int sd_bus_message_get_user_unit(sd_bus_message *m, const char **unit);
+int sd_bus_message_get_session(sd_bus_message *m, const char **session);
+int sd_bus_message_get_owner_uid(sd_bus_message *m, uid_t *uid);
+int sd_bus_message_get_audit_sessionid(sd_bus_message *m, uint32_t *sessionid);
+int sd_bus_message_get_audit_loginuid(sd_bus_message *m, uid_t *loginuid);
+int sd_bus_message_has_effective_cap(sd_bus_message *m, int capability);
+
+int sd_bus_message_is_signal(sd_bus_message *m, const char *interface, const char *member);
+int sd_bus_message_is_method_call(sd_bus_message *m, const char *interface, const char *member);
+int sd_bus_message_is_method_error(sd_bus_message *m, const char *name);
+
+int sd_bus_message_set_no_reply(sd_bus_message *m, int b);
+int sd_bus_message_set_destination(sd_bus_message *m, const char *destination);
+
+int sd_bus_message_append(sd_bus_message *m, const char *types, ...);
+int sd_bus_message_append_basic(sd_bus_message *m, char type, const void *p);
+int sd_bus_message_open_container(sd_bus_message *m, char type, const char *contents);
+int sd_bus_message_close_container(sd_bus_message *m);
+
+int sd_bus_message_read(sd_bus_message *m, const char *types, ...);
+int sd_bus_message_read_basic(sd_bus_message *m, char type, void *p);
+int sd_bus_message_enter_container(sd_bus_message *m, char type, const char *contents);
+int sd_bus_message_exit_container(sd_bus_message *m);
+int sd_bus_message_peek_type(sd_bus_message *m, char *type, const char **contents);
+int sd_bus_message_rewind(sd_bus_message *m, int complete);
+
+/* Convenience calls */
+
+int sd_bus_emit_signal(sd_bus *bus, const char *path, const char *interface, const char *member, const char *types, ...);
+int sd_bus_call_method(sd_bus *bus, const char *destination, const char *path, const char *interface, const char *member, sd_bus_error *error, sd_bus_message **reply, const char *types, ...);
+int sd_bus_reply_method_return(sd_bus *bus, sd_bus_message *call, const char *types, ...);
+int sd_bus_reply_method_error(sd_bus *bus, sd_bus_message *call, const sd_bus_error *e);
+
+/* Bus management */
+
+int sd_bus_get_unique_name(sd_bus *bus, const char **unique);
+int sd_bus_request_name(sd_bus *bus, const char *name, int flags);
+int sd_bus_release_name(sd_bus *bus, const char *name);
+int sd_bus_list_names(sd_bus *bus, char ***l);
+int sd_bus_get_owner(sd_bus *bus, const char *name, char **owner);
+int sd_bus_get_owner_uid(sd_bus *bus, const char *name, uid_t *uid);
+int sd_bus_get_owner_pid(sd_bus *bus, const char *name, pid_t *pid);
+int sd_bus_get_owner_machine_id(sd_bus *bus, const char *name, sd_id128_t *machine);
+
+/* Error structures */
+
+#define SD_BUS_ERROR_NULL ((sd_bus_error) {NULL, NULL, 0})
+#define SD_BUS_ERROR_MAKE(name, message) ((sd_bus_error) {(name), (message), 0})
+
+void sd_bus_error_free(sd_bus_error *e);
+int sd_bus_error_set(sd_bus_error *e, const char *name, const char *format, ...) _sd_printf_attr_(3, 0);
+void sd_bus_error_set_const(sd_bus_error *e, const char *name, const char *message);
+int sd_bus_error_copy(sd_bus_error *dest, const sd_bus_error *e);
+int sd_bus_error_is_set(const sd_bus_error *e);
+int sd_bus_error_has_name(const sd_bus_error *e, const char *name);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/systemd/sd-daemon.h b/src/systemd/sd-daemon.h
index fb7456d50f..daa3f4c857 100644
--- a/src/systemd/sd-daemon.h
+++ b/src/systemd/sd-daemon.h
@@ -68,11 +68,11 @@ extern "C" {
*/
#ifndef _sd_printf_attr_
-#if __GNUC__ >= 4
-#define _sd_printf_attr_(a,b) __attribute__ ((format (printf, a, b)))
-#else
-#define _sd_printf_attr_(a,b)
-#endif
+# if __GNUC__ >= 4
+# define _sd_printf_attr_(a,b) __attribute__ ((format (printf, a, b)))
+# else
+# define _sd_printf_attr_(a,b)
+# endif
#endif
/*
diff --git a/src/systemd/sd-id128.h b/src/systemd/sd-id128.h
index 79bb8b3c99..3a83932093 100644
--- a/src/systemd/sd-id128.h
+++ b/src/systemd/sd-id128.h
@@ -40,7 +40,7 @@ union sd_id128 {
char *sd_id128_to_string(sd_id128_t id, char s[33]);
-int sd_id128_from_string(const char s[33], sd_id128_t *ret);
+int sd_id128_from_string(const char *s, sd_id128_t *ret);
int sd_id128_randomize(sd_id128_t *ret);
diff --git a/src/systemd/sd-journal.h b/src/systemd/sd-journal.h
index 2e8d2d882f..5375d49963 100644
--- a/src/systemd/sd-journal.h
+++ b/src/systemd/sd-journal.h
@@ -34,19 +34,31 @@
extern "C" {
#endif
+#ifndef _sd_printf_attr_
+# if __GNUC__ >= 4
+# define _sd_printf_attr_(a,b) __attribute__ ((format (printf, a, b)))
+# else
+# define _sd_printf_attr_(a,b)
+# endif
+#endif
+
+#ifndef _sd_sentinel_attr_
+# define _sd_sentinel_attr_ __attribute__((sentinel))
+#endif
+
/* Journal APIs. See sd-journal(3) for more information. */
/* Write to daemon */
-int sd_journal_print(int priority, const char *format, ...) __attribute__ ((format (printf, 2, 3)));
-int sd_journal_printv(int priority, const char *format, va_list ap);
-int sd_journal_send(const char *format, ...) __attribute__((sentinel));
+int sd_journal_print(int priority, const char *format, ...) _sd_printf_attr_(2, 3);
+int sd_journal_printv(int priority, const char *format, va_list ap) _sd_printf_attr_(2, 0);
+int sd_journal_send(const char *format, ...) _sd_printf_attr_(1, 0) _sd_sentinel_attr_;
int sd_journal_sendv(const struct iovec *iov, int n);
int sd_journal_perror(const char *message);
/* Used by the macros below. Don't call this directly. */
-int sd_journal_print_with_location(int priority, const char *file, const char *line, const char *func, const char *format, ...) __attribute__ ((format (printf, 5, 6)));
-int sd_journal_printv_with_location(int priority, const char *file, const char *line, const char *func, const char *format, va_list ap);
-int sd_journal_send_with_location(const char *file, const char *line, const char *func, const char *format, ...) __attribute__((sentinel));
+int sd_journal_print_with_location(int priority, const char *file, const char *line, const char *func, const char *format, ...) _sd_printf_attr_(5, 6);
+int sd_journal_printv_with_location(int priority, const char *file, const char *line, const char *func, const char *format, va_list ap) _sd_printf_attr_(5, 0);
+int sd_journal_send_with_location(const char *file, const char *line, const char *func, const char *format, ...) _sd_printf_attr_(4, 0) _sd_sentinel_attr_;
int sd_journal_sendv_with_location(const char *file, const char *line, const char *func, const struct iovec *iov, int n);
int sd_journal_perror_with_location(const char *file, const char *line, const char *func, const char *message);
@@ -106,6 +118,7 @@ void sd_journal_restart_data(sd_journal *j);
int sd_journal_add_match(sd_journal *j, const void *data, size_t size);
int sd_journal_add_disjunction(sd_journal *j);
+int sd_journal_add_conjunction(sd_journal *j);
void sd_journal_flush_matches(sd_journal *j);
int sd_journal_seek_head(sd_journal *j);
@@ -127,9 +140,11 @@ int sd_journal_enumerate_unique(sd_journal *j, const void **data, size_t *l);
void sd_journal_restart_unique(sd_journal *j);
int sd_journal_get_fd(sd_journal *j);
-int sd_journal_reliable_fd(sd_journal *j);
+int sd_journal_get_events(sd_journal *j);
+int sd_journal_get_timeout(sd_journal *j, uint64_t *timeout_usec);
int sd_journal_process(sd_journal *j);
int sd_journal_wait(sd_journal *j, uint64_t timeout_usec);
+int sd_journal_reliable_fd(sd_journal *j);
int sd_journal_get_catalog(sd_journal *j, char **text);
int sd_journal_get_catalog_for_message_id(sd_id128_t id, char **ret);
diff --git a/src/systemd/sd-login.h b/src/systemd/sd-login.h
index 6bd1f2da4a..4855e327a1 100644
--- a/src/systemd/sd-login.h
+++ b/src/systemd/sd-login.h
@@ -23,6 +23,7 @@
***/
#include <sys/types.h>
+#include <inttypes.h>
#ifdef __cplusplus
extern "C" {
@@ -61,9 +62,18 @@ int sd_pid_get_session(pid_t pid, char **session);
* return an error for system processes. */
int sd_pid_get_owner_uid(pid_t pid, uid_t *uid);
-/* Get systemd unit (i.e. service) name from PID. This will return an
- * error for non-service processes. */
-int sd_pid_get_unit(pid_t, char **unit);
+/* Get systemd unit (i.e. service) name from PID, for system
+ * services. This will return an error for non-service processes. */
+int sd_pid_get_unit(pid_t pid, char **unit);
+
+/* Get systemd unit (i.e. service) name from PID, for user
+ * services. This will return an error for non-user-service
+ * processes. */
+int sd_pid_get_user_unit(pid_t pid, char **unit);
+
+/* Get machine name from PID, for processes assigned to VM or
+ * container. This will return an error for non-service processes. */
+int sd_pid_get_machine_name(pid_t pid, char **name);
/* Get state from uid. Possible states: offline, lingering, online, active, closing */
int sd_uid_get_state(uid_t uid, char**state);
@@ -108,6 +118,9 @@ int sd_session_get_class(const char *session, char **clazz);
/* Determine the X11 display of this session. */
int sd_session_get_display(const char *session, char **display);
+/* Determine the TTY of this session. */
+int sd_session_get_tty(const char *session, char **display);
+
/* Return active session and user of seat */
int sd_seat_get_active(const char *seat, char **session, uid_t *uid);
@@ -137,11 +150,15 @@ int sd_get_sessions(char ***sessions);
* users. If users is NULL only returns the number of users. */
int sd_get_uids(uid_t **users);
+/* Get all running virtual machines/containers */
+int sd_get_machine_names(char ***machines);
+
/* Monitor object */
typedef struct sd_login_monitor sd_login_monitor;
/* Create a new monitor. Category must be NULL, "seat", "session",
- * "uid" to get monitor events for the specific category (or all). */
+ * "uid", "machine" to get monitor events for the specific category
+ * (or all). */
int sd_login_monitor_new(const char *category, sd_login_monitor** ret);
/* Destroys the passed monitor. Returns NULL. */
@@ -153,6 +170,12 @@ int sd_login_monitor_flush(sd_login_monitor *m);
/* Get FD from monitor */
int sd_login_monitor_get_fd(sd_login_monitor *m);
+/* Get poll() mask to monitor */
+int sd_login_monitor_get_events(sd_login_monitor *m);
+
+/* Get timeout for poll(), as usec value relative to CLOCK_MONOTONIC's epoch */
+int sd_login_monitor_get_timeout(sd_login_monitor *m, uint64_t *timeout_usec);
+
#ifdef __cplusplus
}
#endif
diff --git a/src/systemd/sd-messages.h b/src/systemd/sd-messages.h
index bc560947d4..c8de331691 100644
--- a/src/systemd/sd-messages.h
+++ b/src/systemd/sd-messages.h
@@ -67,6 +67,16 @@ extern "C" {
#define SD_MESSAGE_OVERMOUNTING SD_ID128_MAKE(1d,ee,03,69,c7,fc,47,36,b7,09,9b,38,ec,b4,6e,e7)
+#define SD_MESSAGE_LID_OPENED SD_ID128_MAKE(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,6f)
+#define SD_MESSAGE_LID_CLOSED SD_ID128_MAKE(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,70)
+#define SD_MESSAGE_POWER_KEY SD_ID128_MAKE(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,71)
+#define SD_MESSAGE_SUSPEND_KEY SD_ID128_MAKE(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,72)
+#define SD_MESSAGE_HIBERNATE_KEY SD_ID128_MAKE(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,73)
+
+#define SD_MESSAGE_CONFIG_ERROR SD_ID128_MAKE(c7,72,d2,4e,9a,88,4c,be,b9,ea,12,62,5c,30,6c,01)
+
+#define SD_MESSAGE_BOOTCHART SD_ID128_MAKE(9f,26,aa,56,2c,f4,40,c2,b1,6c,77,3d,04,79,b5,18)
+
#ifdef __cplusplus
}
#endif
diff --git a/src/systemd/sd-shutdown.h b/src/systemd/sd-shutdown.h
index cee4350173..b8f6a487ec 100644
--- a/src/systemd/sd-shutdown.h
+++ b/src/systemd/sd-shutdown.h
@@ -37,7 +37,7 @@ typedef enum sd_shutdown_mode {
/* Calculate the size of the message as "offsetof(struct
* sd_shutdown_command, wall_message) +
* strlen(command.wall_message)" */
-__attribute__((packed)) struct sd_shutdown_command {
+struct sd_shutdown_command {
/* Microseconds after the epoch 1970 UTC */
uint64_t usec;
@@ -55,7 +55,7 @@ __attribute__((packed)) struct sd_shutdown_command {
/* The wall message to send around. Leave empty for the
* default wall message */
char wall_message[];
-};
+} __attribute__((packed));
/* The scheme is very simple:
*
diff --git a/src/test/test-cgroup-util.c b/src/test/test-cgroup-util.c
new file mode 100644
index 0000000000..c9634d42b0
--- /dev/null
+++ b/src/test/test-cgroup-util.c
@@ -0,0 +1,183 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ 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 <http://www.gnu.org/licenses/>.
+***/
+
+#include <assert.h>
+
+#include "util.h"
+#include "cgroup-util.h"
+
+static void check_p_d_u(const char *path, int code, const char *result) {
+ _cleanup_free_ char *unit = NULL;
+
+ assert_se(cg_path_decode_unit(path, &unit) == code);
+ assert_se(streq_ptr(unit, result));
+}
+
+static void test_path_decode_unit(void) {
+ check_p_d_u("getty@.service/getty@tty2.service", 0, "getty@tty2.service");
+ check_p_d_u("getty@.service/getty@tty2.service/xxx", 0, "getty@tty2.service");
+ check_p_d_u("getty@.service/", -EINVAL, NULL);
+ check_p_d_u("getty@.service", -EINVAL, NULL);
+ check_p_d_u("getty.service", 0, "getty.service");
+ check_p_d_u("getty", -EINVAL, NULL);
+}
+
+static void check_p_g_u(const char *path, int code, const char *result) {
+ _cleanup_free_ char *unit = NULL;
+
+ assert_se(cg_path_get_unit(path, &unit) == code);
+ assert_se(streq_ptr(unit, result));
+}
+
+static void check_p_g_u_u(const char *path, int code, const char *result) {
+ _cleanup_free_ char *unit = NULL;
+
+ assert_se(cg_path_get_user_unit(path, &unit) == code);
+ assert_se(streq_ptr(unit, result));
+}
+
+static void test_path_get_unit(void) {
+ check_p_g_u("/system/foobar.service/sdfdsaf", 0, "foobar.service");
+ check_p_g_u("/system/getty@.service/getty@tty5.service", 0, "getty@tty5.service");
+ check_p_g_u("/system/getty@.service/getty@tty5.service/aaa/bbb", 0, "getty@tty5.service");
+ check_p_g_u("/system/getty@.service/getty@tty5.service/", 0, "getty@tty5.service");
+ check_p_g_u("/system/getty@tty6.service/tty5", 0, "getty@tty6.service");
+ check_p_g_u("sadfdsafsda", -ENOENT, NULL);
+ check_p_g_u("/system/getty####@tty6.service/tty5", -EINVAL, NULL);
+}
+
+static void test_path_get_user_unit(void) {
+ check_p_g_u_u("/user/lennart/2/systemd-21548/foobar.service", 0, "foobar.service");
+ check_p_g_u_u("/user/lennart/2/systemd-21548/foobar.service/waldo", 0, "foobar.service");
+ check_p_g_u_u("/user/lennart/2/systemd-21548/foobar.service/waldo/uuuux", 0, "foobar.service");
+ check_p_g_u_u("/user/lennart/2/systemd-21548/waldo/waldo/uuuux", -EINVAL, NULL);
+ check_p_g_u_u("/user/lennart/2/foobar.service", -ENOENT, NULL);
+ check_p_g_u_u("/user/lennart/2/systemd-21548/foobar@.service/foobar@pie.service/pa/po", 0, "foobar@pie.service");
+}
+
+static void test_get_paths(void) {
+ _cleanup_free_ char *a = NULL, *b = NULL, *c = NULL, *d = NULL;
+
+ assert_se(cg_get_root_path(&a) >= 0);
+ log_info("Root = %s", a);
+
+ assert_se(cg_get_system_path(&b) >= 0);
+ log_info("System = %s", b);
+
+ assert_se(cg_get_user_path(&c) >= 0);
+ log_info("User = %s", c);
+
+ assert_se(cg_get_machine_path("harley", &d) >= 0);
+ log_info("Machine = %s", d);
+}
+
+static void test_proc(void) {
+ _cleanup_closedir_ DIR *d = NULL;
+ struct dirent *de;
+ int r;
+
+ d = opendir("/proc");
+ assert_se(d);
+
+ FOREACH_DIRENT(de, d, break) {
+ _cleanup_free_ char *path = NULL, *path_shifted = NULL, *session = NULL, *unit = NULL, *user_unit = NULL, *machine = NULL, *prefix = NULL;
+ pid_t pid;
+ uid_t uid = (uid_t) -1;
+
+ if (de->d_type != DT_DIR &&
+ de->d_type != DT_UNKNOWN)
+ continue;
+
+ r = parse_pid(de->d_name, &pid);
+ if (r < 0)
+ continue;
+
+ if (is_kernel_thread(pid))
+ continue;
+
+ cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, pid, &path);
+ cg_pid_get_path_shifted(pid, &prefix, &path_shifted);
+ cg_pid_get_owner_uid(pid, &uid);
+ cg_pid_get_session(pid, &session);
+ cg_pid_get_unit(pid, &unit);
+ cg_pid_get_user_unit(pid, &user_unit);
+ cg_pid_get_machine_name(pid, &machine);
+
+ printf("%lu\t%s\t%s\t%s\t%lu\t%s\t%s\t%s\t%s\n",
+ (unsigned long) pid,
+ path,
+ prefix,
+ path_shifted,
+ (unsigned long) uid,
+ session,
+ unit,
+ user_unit,
+ machine);
+ }
+}
+
+static void test_escape_one(const char *s, const char *r) {
+ _cleanup_free_ char *b;
+
+ b = cg_escape(s);
+ assert_se(b);
+ assert_se(streq(b, r));
+
+ assert_se(streq(cg_unescape(b), s));
+}
+
+static void test_escape(void) {
+ test_escape_one("foobar", "foobar");
+ test_escape_one(".foobar", "_.foobar");
+ test_escape_one("foobar.service", "foobar.service");
+ test_escape_one("cgroup.service", "_cgroup.service");
+ test_escape_one("cpu.service", "_cpu.service");
+ test_escape_one("tasks", "_tasks");
+ test_escape_one("_foobar", "__foobar");
+ test_escape_one("", "_");
+ test_escape_one("_", "__");
+ test_escape_one(".", "_.");
+}
+
+static void test_controller_is_valid(void) {
+ assert_se(cg_controller_is_valid("foobar", false));
+ assert_se(cg_controller_is_valid("foo_bar", false));
+ assert_se(cg_controller_is_valid("name=foo", true));
+ assert_se(!cg_controller_is_valid("", false));
+ assert_se(!cg_controller_is_valid("name=", true));
+ assert_se(!cg_controller_is_valid("=", false));
+ assert_se(!cg_controller_is_valid("cpu,cpuacct", false));
+ assert_se(!cg_controller_is_valid("_", false));
+ assert_se(!cg_controller_is_valid("_foobar", false));
+ assert_se(!cg_controller_is_valid("tatü", false));
+}
+
+int main(void) {
+ test_path_decode_unit();
+ test_path_get_unit();
+ test_path_get_user_unit();
+ test_get_paths();
+ test_proc();
+ test_escape();
+ test_controller_is_valid();
+
+ return 0;
+}
diff --git a/src/test/test-cgroup.c b/src/test/test-cgroup.c
index 6d64a4e47f..3a3489d6a2 100644
--- a/src/test/test-cgroup.c
+++ b/src/test/test-cgroup.c
@@ -31,25 +31,25 @@ int main(int argc, char*argv[]) {
char *path;
char *c, *p;
- assert_se(cg_create(SYSTEMD_CGROUP_CONTROLLER, "/test-a") == 0);
- assert_se(cg_create(SYSTEMD_CGROUP_CONTROLLER, "/test-a") == 0);
- assert_se(cg_create(SYSTEMD_CGROUP_CONTROLLER, "/test-b") == 0);
- assert_se(cg_create(SYSTEMD_CGROUP_CONTROLLER, "/test-b/test-c") == 0);
+ assert_se(cg_create(SYSTEMD_CGROUP_CONTROLLER, "/test-a", NULL) == 0);
+ assert_se(cg_create(SYSTEMD_CGROUP_CONTROLLER, "/test-a", NULL) == 0);
+ assert_se(cg_create(SYSTEMD_CGROUP_CONTROLLER, "/test-b", NULL) == 0);
+ assert_se(cg_create(SYSTEMD_CGROUP_CONTROLLER, "/test-b/test-c", NULL) == 0);
assert_se(cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, "/test-b", 0) == 0);
- assert_se(cg_get_by_pid(SYSTEMD_CGROUP_CONTROLLER, getpid(), &path) == 0);
+ assert_se(cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, getpid(), &path) == 0);
assert_se(streq(path, "/test-b"));
free(path);
assert_se(cg_attach(SYSTEMD_CGROUP_CONTROLLER, "/test-a", 0) == 0);
- assert_se(cg_get_by_pid(SYSTEMD_CGROUP_CONTROLLER, getpid(), &path) == 0);
+ assert_se(cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, getpid(), &path) == 0);
assert_se(path_equal(path, "/test-a"));
free(path);
assert_se(cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, "/test-b/test-d", 0) == 0);
- assert_se(cg_get_by_pid(SYSTEMD_CGROUP_CONTROLLER, getpid(), &path) == 0);
+ assert_se(cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, getpid(), &path) == 0);
assert_se(path_equal(path, "/test-b/test-d"));
free(path);
@@ -65,7 +65,7 @@ int main(int argc, char*argv[]) {
assert_se(cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-a", 0, false, false, false, NULL) == 0);
assert_se(cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-b", 0, false, false, false, NULL) > 0);
- assert_se(cg_migrate_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-b", "/test-a", false, false) > 0);
+ assert_se(cg_migrate_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-b", SYSTEMD_CGROUP_CONTROLLER, "/test-a", false, false) > 0);
assert_se(cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-a", false) == 0);
assert_se(cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-b", false) > 0);
diff --git a/src/test/test-efivars.c b/src/test/test-efivars.c
new file mode 100644
index 0000000000..43ea5917b6
--- /dev/null
+++ b/src/test/test-efivars.c
@@ -0,0 +1,47 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ 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 <http://www.gnu.org/licenses/>.
+***/
+
+#include "util.h"
+#include "log.h"
+#include "efivars.h"
+
+int main(int argc, char* argv[]) {
+ char s[MAX(FORMAT_TIMESPAN_MAX, FORMAT_TIMESTAMP_MAX)];
+ int r;
+ dual_timestamp fw, l, k;
+
+ dual_timestamp_from_monotonic(&k, 0);
+
+ r = efi_get_boot_timestamps(NULL, &fw, &l);
+ if (r < 0) {
+ log_error("Failed to read variables: %s", strerror(-r));
+ return 1;
+ }
+
+ log_info("Firmware began %s before kernel.", format_timespan(s, sizeof(s), fw.monotonic, 0));
+ log_info("Loader began %s before kernel.", format_timespan(s, sizeof(s), l.monotonic, 0));
+
+ log_info("Firmware began %s.", format_timestamp(s, sizeof(s), fw.realtime));
+ log_info("Loader began %s.", format_timestamp(s, sizeof(s), l.realtime));
+ log_info("Kernel began %s.", format_timestamp(s, sizeof(s), k.realtime));
+
+ return 0;
+}
diff --git a/src/test/test-env-replace.c b/src/test/test-env-replace.c
index cd596a6e16..0274e97618 100644
--- a/src/test/test-env-replace.c
+++ b/src/test/test-env-replace.c
@@ -23,17 +23,92 @@
#include <string.h>
#include "util.h"
-#include "log.h"
#include "strv.h"
+#include "env-util.h"
-int main(int argc, char *argv[]) {
+static void test_strv_env_delete(void) {
+ _cleanup_strv_free_ char **a = NULL, **b = NULL, **c = NULL, **d = NULL;
+
+ a = strv_new("FOO=BAR", "WALDO=WALDO", "WALDO=", "PIEP", "SCHLUMPF=SMURF", NULL);
+ assert_se(a);
+
+ b = strv_new("PIEP", "FOO", NULL);
+ assert_se(b);
+
+ c = strv_new("SCHLUMPF", NULL);
+ assert_se(c);
+
+ d = strv_env_delete(a, 2, b, c);
+ assert_se(d);
+
+ assert_se(streq(d[0], "WALDO=WALDO"));
+ assert_se(streq(d[1], "WALDO="));
+ assert_se(strv_length(d) == 2);
+}
+
+static void test_strv_env_unset(void) {
+ _cleanup_strv_free_ char **l = NULL;
+
+ l = strv_new("PIEP", "SCHLUMPF=SMURFF", "NANANANA=YES", NULL);
+ assert_se(l);
+
+ assert_se(strv_env_unset(l, "SCHLUMPF") == l);
+
+ assert_se(streq(l[0], "PIEP"));
+ assert_se(streq(l[1], "NANANANA=YES"));
+ assert_se(strv_length(l) == 2);
+}
+
+static void test_strv_env_set(void) {
+ _cleanup_strv_free_ char **l = NULL, **r = NULL;
+ l = strv_new("PIEP", "SCHLUMPF=SMURFF", "NANANANA=YES", NULL);
+ assert_se(l);
+
+ r = strv_env_set(l, "WALDO=WALDO");
+ assert_se(r);
+
+ assert_se(streq(r[0], "PIEP"));
+ assert_se(streq(r[1], "SCHLUMPF=SMURFF"));
+ assert_se(streq(r[2], "NANANANA=YES"));
+ assert_se(streq(r[3], "WALDO=WALDO"));
+ assert_se(strv_length(r) == 4);
+}
+
+static void test_strv_env_merge(void) {
+ _cleanup_strv_free_ char **a = NULL, **b = NULL, **r = NULL;
+
+ a = strv_new("FOO=BAR", "WALDO=WALDO", "WALDO=", "PIEP", "SCHLUMPF=SMURF", NULL);
+ assert_se(a);
+
+ b = strv_new("FOO=KKK", "FOO=", "PIEP=", "SCHLUMPF=SMURFF", "NANANANA=YES", NULL);
+ assert_se(b);
+
+ r = strv_env_merge(2, a, b);
+ assert_se(r);
+ assert_se(streq(r[0], "FOO="));
+ assert_se(streq(r[1], "WALDO="));
+ assert_se(streq(r[2], "PIEP"));
+ assert_se(streq(r[3], "SCHLUMPF=SMURFF"));
+ assert_se(streq(r[4], "PIEP="));
+ assert_se(streq(r[5], "NANANANA=YES"));
+ assert_se(strv_length(r) == 6);
+
+ assert_se(strv_env_clean(r) == r);
+ assert_se(streq(r[0], "FOO="));
+ assert_se(streq(r[1], "WALDO="));
+ assert_se(streq(r[2], "SCHLUMPF=SMURFF"));
+ assert_se(streq(r[3], "PIEP="));
+ assert_se(streq(r[4], "NANANANA=YES"));
+ assert_se(strv_length(r) == 5);
+}
+
+static void test_replace_env_arg(void) {
const char *env[] = {
"FOO=BAR BAR",
"BAR=waldo",
NULL
};
-
const char *line[] = {
"FOO$FOO",
"FOO$FOOFOO",
@@ -46,98 +121,98 @@ int main(int argc, char *argv[]) {
"${FOO",
NULL
};
-
- char **i, **r, *t, **a, **b;
- const char nulstr[] = "fuck\0fuck2\0fuck3\0\0fuck5\0\0xxx";
-
- a = strv_parse_nulstr(nulstr, sizeof(nulstr)-1);
-
- STRV_FOREACH(i, a)
- printf("nulstr--%s\n", *i);
-
- strv_free(a);
+ _cleanup_strv_free_ char **r = NULL;
r = replace_env_argv((char**) line, (char**) env);
+ assert_se(r);
+ assert_se(streq(r[0], "FOO$FOO"));
+ assert_se(streq(r[1], "FOO$FOOFOO"));
+ assert_se(streq(r[2], "FOOBAR BAR$FOO"));
+ assert_se(streq(r[3], "FOOBAR BAR"));
+ assert_se(streq(r[4], "BAR BAR"));
+ assert_se(streq(r[5], "BAR"));
+ assert_se(streq(r[6], "BAR"));
+ assert_se(streq(r[7], "BAR BARwaldo"));
+ assert_se(streq(r[8], "${FOO"));
+ assert_se(strv_length(r) == 9);
+}
- STRV_FOREACH(i, r)
- printf("%s\n", *i);
-
- strv_free(r);
-
- t = normalize_env_assignment("foo=bar");
- printf("%s\n", t);
- free(t);
-
- t = normalize_env_assignment("=bar");
- printf("%s\n", t);
- free(t);
-
- t = normalize_env_assignment("foo=");
- printf("%s\n", t);
- free(t);
-
- t = normalize_env_assignment("=");
- printf("%s\n", t);
- free(t);
-
- t = normalize_env_assignment("");
- printf("%s\n", t);
- free(t);
-
- t = normalize_env_assignment("a=\"waldo\"");
- printf("%s\n", t);
- free(t);
-
- t = normalize_env_assignment("a=\"waldo");
- printf("%s\n", t);
- free(t);
-
- t = normalize_env_assignment("a=waldo\"");
- printf("%s\n", t);
- free(t);
-
- t = normalize_env_assignment("a=\'");
- printf("%s\n", t);
- free(t);
-
- t = normalize_env_assignment("a=\'\'");
- printf("%s\n", t);
- free(t);
-
- t = normalize_env_assignment(" xyz ");
- printf("<%s>\n", t);
- free(t);
-
- t = normalize_env_assignment(" xyz = bar ");
- printf("<%s>\n", t);
- free(t);
-
- t = normalize_env_assignment(" xyz = 'bar ' ");
- printf("<%s>\n", t);
- free(t);
-
- t = normalize_env_assignment(" ' xyz' = 'bar ' ");
- printf("<%s>\n", t);
- free(t);
-
- a = strv_new("FOO=BAR", "WALDO=WALDO", "WALDO=", "PIEP", "SCHLUMPF=SMURF", NULL);
- b = strv_new("FOO=KKK", "FOO=", "PIEP=", "SCHLUMPF=SMURFF", "NANANANA=YES", NULL);
+static void test_one_normalize(const char *input, const char *output) {
+ _cleanup_free_ char *t;
- r = strv_env_merge(2, a, b);
- strv_free(a);
- strv_free(b);
+ t = normalize_env_assignment(input);
+ assert_se(t);
+ assert_se(streq(t, output));
+}
- STRV_FOREACH(i, r)
- printf("%s\n", *i);
+static void test_normalize_env_assignment(void) {
+ test_one_normalize("foo=bar", "foo=bar");
+ test_one_normalize("=bar", "=bar");
+ test_one_normalize("foo=", "foo=");
+ test_one_normalize("=", "=");
+ test_one_normalize("", "");
+ test_one_normalize("a=\"waldo\"", "a=waldo");
+ test_one_normalize("a=\"waldo", "a=\"waldo");
+ test_one_normalize("a=waldo\"", "a=waldo\"");
+ test_one_normalize("a=\'", "a='");
+ test_one_normalize("a=\'\'", "a=");
+ test_one_normalize(" xyz ", "xyz");
+ test_one_normalize(" xyz = bar ", "xyz=bar");
+ test_one_normalize(" xyz = 'bar ' ", "xyz=bar ");
+ test_one_normalize(" ' xyz' = 'bar ' ", "' xyz'=bar ");
+}
- printf("CLEANED UP:\n");
+static void test_env_clean(void) {
+ _cleanup_strv_free_ char **e;
+
+ e = strv_new("FOOBAR=WALDO",
+ "FOOBAR=WALDO",
+ "FOOBAR",
+ "F",
+ "X=",
+ "F=F",
+ "=",
+ "=F",
+ "",
+ "0000=000",
+ "äöüß=abcd",
+ "abcd=äöüß",
+ "xyz\n=xyz",
+ "xyz=xyz\n",
+ "another=one",
+ "another=final one",
+ NULL);
+ assert_se(e);
+ assert_se(!strv_env_is_valid(e));
+ assert_se(strv_env_clean(e) == e);
+ assert_se(strv_env_is_valid(e));
+
+ assert_se(streq(e[0], "FOOBAR=WALDO"));
+ assert_se(streq(e[1], "X="));
+ assert_se(streq(e[2], "F=F"));
+ assert_se(streq(e[3], "abcd=äöüß"));
+ assert_se(streq(e[4], "another=final one"));
+ assert_se(e[5] == NULL);
+}
- r = strv_env_clean(r);
+static void test_env_name_is_valid(void) {
+ assert_se(env_name_is_valid("test"));
- STRV_FOREACH(i, r)
- printf("%s\n", *i);
+ assert_se(!env_name_is_valid(NULL));
+ assert_se(!env_name_is_valid(""));
+ assert_se(!env_name_is_valid("5_starting_with_a_number_is_wrong"));
+ assert_se(!env_name_is_valid("#¤%&?_only_numbers_letters_and_underscore_allowed"));
+}
- strv_free(r);
+int main(int argc, char *argv[]) {
+ test_strv_env_delete();
+ test_strv_env_unset();
+ test_strv_env_set();
+ test_strv_env_merge();
+ test_replace_env_arg();
+ test_normalize_env_assignment();
+ test_env_clean();
+ test_env_name_is_valid();
return 0;
}
diff --git a/src/test/test-fileio.c b/src/test/test-fileio.c
new file mode 100644
index 0000000000..d56f7cc856
--- /dev/null
+++ b/src/test/test-fileio.c
@@ -0,0 +1,145 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ 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 <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "util.h"
+#include "fileio.h"
+#include "strv.h"
+#include "env-util.h"
+
+static void test_parse_env_file(void) {
+ char t[] = "/tmp/test-parse-env-file-XXXXXX";
+ int fd, r;
+ FILE *f;
+ _cleanup_free_ char *one = NULL, *two = NULL, *three = NULL, *four = NULL, *five = NULL,
+ *six = NULL, *seven = NULL, *eight = NULL, *nine = NULL, *ten = NULL;
+ _cleanup_strv_free_ char **a = NULL, **b = NULL;
+ char **i;
+ unsigned k;
+
+ fd = mkostemp(t, O_CLOEXEC);
+ assert_se(fd >= 0);
+
+ f = fdopen(fd, "w");
+ assert_se(f);
+
+ fputs("one=BAR \n"
+ "# comment\n"
+ " # comment \n"
+ " ; comment \n"
+ " two = bar \n"
+ "invalid line\n"
+ "invalid line #comment\n"
+ "three = \"333\n"
+ "xxxx\"\n"
+ "four = \'44\\\"44\'\n"
+ "five = \'55\\\'55\' \"FIVE\" cinco \n"
+ "six = seis sechs\\\n"
+ " sis\n"
+ "seven=\"sevenval\" #nocomment\n"
+ "eight=eightval #nocomment\n"
+ "export nine=nineval\n"
+ "ten=", f);
+
+ fflush(f);
+ fclose(f);
+
+ r = load_env_file(t, NULL, &a);
+ assert_se(r >= 0);
+
+ STRV_FOREACH(i, a)
+ log_info("Got: <%s>", *i);
+
+ assert_se(streq(a[0], "one=BAR"));
+ assert_se(streq(a[1], "two=bar"));
+ assert_se(streq(a[2], "three=333\nxxxx"));
+ assert_se(streq(a[3], "four=44\"44"));
+ assert_se(streq(a[4], "five=55\'55FIVEcinco"));
+ assert_se(streq(a[5], "six=seis sechs sis"));
+ assert_se(streq(a[6], "seven=sevenval#nocomment"));
+ assert_se(streq(a[7], "eight=eightval #nocomment"));
+ assert_se(streq(a[8], "export nine=nineval"));
+ assert_se(streq(a[9], "ten="));
+ assert_se(a[10] == NULL);
+
+ strv_env_clean_log(a, "/tmp/test-fileio");
+
+ k = 0;
+ STRV_FOREACH(i, b) {
+ log_info("Got2: <%s>", *i);
+ assert_se(streq(*i, a[k++]));
+ }
+
+ r = parse_env_file(
+ t, NULL,
+ "one", &one,
+ "two", &two,
+ "three", &three,
+ "four", &four,
+ "five", &five,
+ "six", &six,
+ "seven", &seven,
+ "eight", &eight,
+ "export nine", &nine,
+ "ten", &ten,
+ NULL);
+
+ assert_se(r >= 0);
+
+ log_info("one=[%s]", strna(one));
+ log_info("two=[%s]", strna(two));
+ log_info("three=[%s]", strna(three));
+ log_info("four=[%s]", strna(four));
+ log_info("five=[%s]", strna(five));
+ log_info("six=[%s]", strna(six));
+ log_info("seven=[%s]", strna(seven));
+ log_info("eight=[%s]", strna(eight));
+ log_info("export nine=[%s]", strna(nine));
+ log_info("ten=[%s]", strna(nine));
+
+ assert_se(streq(one, "BAR"));
+ assert_se(streq(two, "bar"));
+ assert_se(streq(three, "333\nxxxx"));
+ assert_se(streq(four, "44\"44"));
+ assert_se(streq(five, "55\'55FIVEcinco"));
+ assert_se(streq(six, "seis sechs sis"));
+ assert_se(streq(seven, "sevenval#nocomment"));
+ assert_se(streq(eight, "eightval #nocomment"));
+ assert_se(streq(nine, "nineval"));
+ assert_se(ten == NULL);
+
+ r = write_env_file("/tmp/test-fileio", a);
+ assert_se(r >= 0);
+
+ r = load_env_file("/tmp/test-fileio", NULL, &b);
+ assert_se(r >= 0);
+
+ unlink(t);
+ unlink("/tmp/test-fileio");
+}
+
+int main(int argc, char *argv[]) {
+ test_parse_env_file();
+ return 0;
+}
diff --git a/src/test/test-hashmap.c b/src/test/test-hashmap.c
new file mode 100644
index 0000000000..2aead79bb1
--- /dev/null
+++ b/src/test/test-hashmap.c
@@ -0,0 +1,508 @@
+/***
+ This file is part of systemd
+
+ Copyright 2013 Daniel Buch
+
+ 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 <http://www.gnu.org/licenses/>.
+***/
+
+#include <inttypes.h>
+#include "strv.h"
+#include "util.h"
+#include "hashmap.h"
+
+static void test_hashmap_replace(void) {
+ Hashmap *m;
+ char *val1, *val2, *val3, *val4, *val5, *r;
+
+ m = hashmap_new(string_hash_func, string_compare_func);
+
+ val1 = strdup("val1");
+ assert_se(val1);
+ val2 = strdup("val2");
+ assert_se(val2);
+ val3 = strdup("val3");
+ assert_se(val3);
+ val4 = strdup("val4");
+ assert_se(val4);
+ val5 = strdup("val5");
+ assert_se(val5);
+
+ hashmap_put(m, "key 1", val1);
+ hashmap_put(m, "key 2", val2);
+ hashmap_put(m, "key 3", val3);
+ hashmap_put(m, "key 4", val4);
+
+ hashmap_replace(m, "key 3", val1);
+ r = hashmap_get(m, "key 3");
+ assert_se(streq(r, "val1"));
+
+ hashmap_replace(m, "key 5", val5);
+ r = hashmap_get(m, "key 5");
+ assert_se(streq(r, "val5"));
+
+ free(val1);
+ free(val2);
+ free(val3);
+ free(val4);
+ free(val5);
+ hashmap_free(m);
+}
+
+static void test_hashmap_copy(void) {
+ Hashmap *m, *copy;
+ char *val1, *val2, *val3, *val4, *r;
+
+ val1 = strdup("val1");
+ assert_se(val1);
+ val2 = strdup("val2");
+ assert_se(val2);
+ val3 = strdup("val3");
+ assert_se(val3);
+ val4 = strdup("val4");
+ assert_se(val4);
+
+ m = hashmap_new(string_hash_func, string_compare_func);
+
+ hashmap_put(m, "key 1", val1);
+ hashmap_put(m, "key 2", val2);
+ hashmap_put(m, "key 3", val3);
+ hashmap_put(m, "key 4", val4);
+
+ copy = hashmap_copy(m);
+
+ r = hashmap_get(copy, "key 1");
+ assert_se(streq(r, "val1"));
+ r = hashmap_get(copy, "key 2");
+ assert_se(streq(r, "val2"));
+ r = hashmap_get(copy, "key 3");
+ assert_se(streq(r, "val3"));
+ r = hashmap_get(copy, "key 4");
+ assert_se(streq(r, "val4"));
+
+ hashmap_free_free(copy);
+ hashmap_free(m);
+}
+
+static void test_hashmap_get_strv(void) {
+ Hashmap *m;
+ char **strv;
+ char *val1, *val2, *val3, *val4;
+
+ val1 = strdup("val1");
+ assert_se(val1);
+ val2 = strdup("val2");
+ assert_se(val2);
+ val3 = strdup("val3");
+ assert_se(val3);
+ val4 = strdup("val4");
+ assert_se(val4);
+
+ m = hashmap_new(string_hash_func, string_compare_func);
+
+ hashmap_put(m, "key 1", val1);
+ hashmap_put(m, "key 2", val2);
+ hashmap_put(m, "key 3", val3);
+ hashmap_put(m, "key 4", val4);
+
+ strv = hashmap_get_strv(m);
+
+ assert_se(streq(strv[0], "val1"));
+ assert_se(streq(strv[1], "val2"));
+ assert_se(streq(strv[2], "val3"));
+ assert_se(streq(strv[3], "val4"));
+
+ strv_free(strv);
+
+ hashmap_free(m);
+}
+
+static void test_hashmap_move_one(void) {
+ Hashmap *m, *n;
+ char *val1, *val2, *val3, *val4, *r;
+
+ val1 = strdup("val1");
+ assert_se(val1);
+ val2 = strdup("val2");
+ assert_se(val2);
+ val3 = strdup("val3");
+ assert_se(val3);
+ val4 = strdup("val4");
+ assert_se(val4);
+
+ m = hashmap_new(string_hash_func, string_compare_func);
+ n = hashmap_new(string_hash_func, string_compare_func);
+
+ hashmap_put(m, "key 1", val1);
+ hashmap_put(m, "key 2", val2);
+ hashmap_put(m, "key 3", val3);
+ hashmap_put(m, "key 4", val4);
+
+ hashmap_move_one(n, m, "key 3");
+ hashmap_move_one(n, m, "key 4");
+
+ r = hashmap_get(n, "key 3");
+ assert_se(r && streq(r, "val3"));
+ r = hashmap_get(n, "key 4");
+ assert_se(r && streq(r, "val4"));
+ r = hashmap_get(m, "key 3");
+ assert_se(!r);
+
+
+ hashmap_free_free(m);
+ hashmap_free_free(n);
+}
+
+static void test_hashmap_next(void) {
+ Hashmap *m;
+ char *val1, *val2, *val3, *val4, *r;
+
+ m = hashmap_new(string_hash_func, string_compare_func);
+ val1 = strdup("val1");
+ assert_se(val1);
+ val2 = strdup("val2");
+ assert_se(val2);
+ val3 = strdup("val3");
+ assert_se(val3);
+ val4 = strdup("val4");
+ assert_se(val4);
+
+ hashmap_put(m, "key 1", val1);
+ hashmap_put(m, "key 2", val2);
+ hashmap_put(m, "key 3", val3);
+ hashmap_put(m, "key 4", val4);
+
+ r = hashmap_next(m, "key 1");
+ assert_se(streq(r, val2));
+ r = hashmap_next(m, "key 2");
+ assert_se(streq(r, val3));
+ r = hashmap_next(m, "key 3");
+ assert_se(streq(r, val4));
+ r = hashmap_next(m, "key 4");
+ assert_se(!r);
+
+ hashmap_free_free(m);
+}
+
+static void test_hashmap_update(void) {
+ Hashmap *m;
+ char *val1, *val2, *r;
+
+ m = hashmap_new(string_hash_func, string_compare_func);
+ val1 = strdup("old_value");
+ assert_se(val1);
+ val2 = strdup("new_value");
+ assert_se(val2);
+
+ hashmap_put(m, "key 1", val1);
+ r = hashmap_get(m, "key 1");
+ assert_se(streq(r, "old_value"));
+
+ hashmap_update(m, "key 1", val2);
+ r = hashmap_get(m, "key 1");
+ assert_se(streq(r, "new_value"));
+
+ free(val1);
+ free(val2);
+ hashmap_free(m);
+}
+
+static void test_hashmap_put(void) {
+ Hashmap *m;
+ int valid_hashmap_put;
+
+ m = hashmap_new(string_hash_func, string_compare_func);
+
+ valid_hashmap_put = hashmap_put(m, "key 1", (void*) (const char *) "val 1");
+ assert_se(valid_hashmap_put == 1);
+
+ assert_se(m);
+ hashmap_free(m);
+}
+
+static void test_hashmap_ensure_allocated(void) {
+ Hashmap *m;
+ int valid_hashmap;
+
+ m = hashmap_new(string_hash_func, string_compare_func);
+
+ valid_hashmap = hashmap_ensure_allocated(&m, string_hash_func, string_compare_func);
+ assert_se(valid_hashmap == 0);
+
+ assert_se(m);
+ hashmap_free(m);
+}
+
+static void test_hashmap_foreach_key(void) {
+ Hashmap *m;
+ Iterator i;
+ bool key_found[] = { false, false, false, false };
+ const char *s;
+ const char *key;
+ static const char key_table[] =
+ "key 1\0"
+ "key 2\0"
+ "key 3\0"
+ "key 4\0";
+
+ m = hashmap_new(string_hash_func, string_compare_func);
+
+ NULSTR_FOREACH(key, key_table)
+ hashmap_put(m, key, (void*) (const char*) "my dummy val");
+
+ HASHMAP_FOREACH_KEY(s, key, m, i) {
+ if (!key_found[0] && streq(key, "key 1"))
+ key_found[0] = true;
+ else if (!key_found[1] && streq(key, "key 2"))
+ key_found[1] = true;
+ else if (!key_found[2] && streq(key, "key 3"))
+ key_found[2] = true;
+ else if (!key_found[3] && streq(key, "fail"))
+ key_found[3] = true;
+ }
+
+ assert_se(m);
+ assert_se(key_found[0] && key_found[1] && key_found[2] && !key_found[3]);
+
+ hashmap_free(m);
+}
+
+static void test_hashmap_foreach(void) {
+ Hashmap *m;
+ Iterator i;
+ bool value_found[] = { false, false, false, false };
+ char *val1, *val2, *val3, *val4, *s;
+
+ val1 = strdup("my val1");
+ assert_se(val1);
+ val2 = strdup("my val2");
+ assert_se(val2);
+ val3 = strdup("my val3");
+ assert_se(val3);
+ val4 = strdup("my val4");
+ assert_se(val4);
+
+ m = hashmap_new(string_hash_func, string_compare_func);
+
+ hashmap_put(m, "Key 1", val1);
+ hashmap_put(m, "Key 2", val2);
+ hashmap_put(m, "Key 3", val3);
+ hashmap_put(m, "Key 4", val4);
+
+ HASHMAP_FOREACH(s, m, i) {
+ if (!value_found[0] && streq(s, val1))
+ value_found[0] = true;
+ else if (!value_found[1] && streq(s, val2))
+ value_found[1] = true;
+ else if (!value_found[2] && streq(s, val3))
+ value_found[2] = true;
+ else if (!value_found[3] && streq(s, val4))
+ value_found[3] = true;
+ }
+
+ assert_se(m);
+ assert_se(value_found[0] && value_found[1] && value_found[2] && value_found[3]);
+
+ hashmap_free_free(m);
+}
+
+static void test_hashmap_foreach_backwards(void) {
+ Hashmap *m;
+ Iterator i;
+ char *val1, *val2, *val3, *val4, *s;
+ bool value_found[] = { false, false, false, false };
+
+ val1 = strdup("my val1");
+ assert_se(val1);
+ val2 = strdup("my val2");
+ assert_se(val2);
+ val3 = strdup("my val3");
+ assert_se(val3);
+ val4 = strdup("my val4");
+ assert_se(val4);
+
+ m = hashmap_new(string_hash_func, string_compare_func);
+ hashmap_put(m, "Key 1", val1);
+ hashmap_put(m, "Key 2", val2);
+ hashmap_put(m, "Key 3", val3);
+ hashmap_put(m, "Key 4", val4);
+
+ HASHMAP_FOREACH_BACKWARDS(s, m, i) {
+ if (!value_found[0] && streq(s, val1))
+ value_found[0] = true;
+ else if (!value_found[1] && streq(s, val2))
+ value_found[1] = true;
+ else if (!value_found[2] && streq(s, val3))
+ value_found[2] = true;
+ else if (!value_found[3] && streq(s, val4))
+ value_found[3] = true;
+ }
+
+ assert_se(m);
+ assert_se(value_found[0] && value_found[1] && value_found[2] && value_found[3]);
+
+ hashmap_free_free(m);
+}
+
+static void test_hashmap_merge(void) {
+ Hashmap *m;
+ Hashmap *n;
+ char *val1, *val2, *val3, *val4, *r;
+
+ val1 = strdup("my val1");
+ assert_se(val1);
+ val2 = strdup("my val2");
+ assert_se(val2);
+ val3 = strdup("my val3");
+ assert_se(val3);
+ val4 = strdup("my val4");
+ assert_se(val4);
+
+ n = hashmap_new(string_hash_func, string_compare_func);
+ m = hashmap_new(string_hash_func, string_compare_func);
+
+ hashmap_put(m, "Key 1", val1);
+ hashmap_put(m, "Key 2", val2);
+ hashmap_put(n, "Key 3", val3);
+ hashmap_put(n, "Key 4", val4);
+
+ assert_se(hashmap_merge(m, n) == 0);
+ r = hashmap_get(m, "Key 3");
+ assert_se(r && streq(r, "my val3"));
+ r = hashmap_get(m, "Key 4");
+ assert_se(r && streq(r, "my val4"));
+
+ assert_se(n);
+ assert_se(m);
+ hashmap_free(n);
+ hashmap_free_free(m);
+}
+
+static void test_hashmap_contains(void) {
+ Hashmap *m;
+ char *val1;
+
+ val1 = strdup("my val");
+ assert_se(val1);
+
+ m = hashmap_new(string_hash_func, string_compare_func);
+
+ assert_se(!hashmap_contains(m, "Key 1"));
+ hashmap_put(m, "Key 1", val1);
+ assert_se(hashmap_contains(m, "Key 1"));
+
+ assert_se(m);
+ hashmap_free_free(m);
+}
+
+static void test_hashmap_isempty(void) {
+ Hashmap *m;
+ char *val1;
+
+ val1 = strdup("my val");
+ assert_se(val1);
+
+ m = hashmap_new(string_hash_func, string_compare_func);
+
+ assert_se(hashmap_isempty(m));
+ hashmap_put(m, "Key 1", val1);
+ assert_se(!hashmap_isempty(m));
+
+ assert_se(m);
+ hashmap_free_free(m);
+}
+
+static void test_hashmap_size(void) {
+ Hashmap *m;
+ char *val1, *val2, *val3, *val4;
+
+ val1 = strdup("my val");
+ assert_se(val1);
+ val2 = strdup("my val");
+ assert_se(val2);
+ val3 = strdup("my val");
+ assert_se(val3);
+ val4 = strdup("my val");
+ assert_se(val4);
+
+ m = hashmap_new(string_hash_func, string_compare_func);
+
+ hashmap_put(m, "Key 1", val1);
+ hashmap_put(m, "Key 2", val2);
+ hashmap_put(m, "Key 3", val3);
+ hashmap_put(m, "Key 4", val4);
+
+ assert_se(m);
+ assert_se(hashmap_size(m) == 4);
+ hashmap_free_free(m);
+}
+
+static void test_hashmap_get(void) {
+ Hashmap *m;
+ char *r;
+ char *val;
+
+ val = strdup("my val");
+ assert_se(val);
+
+ m = hashmap_new(string_hash_func, string_compare_func);
+
+ hashmap_put(m, "Key 1", val);
+
+ r = hashmap_get(m, "Key 1");
+ assert_se(streq(r, val));
+
+ assert_se(m);
+ hashmap_free_free(m);
+}
+
+static void test_uint64_compare_func(void) {
+ assert_se(uint64_compare_func("a", "a") == 0);
+ assert_se(uint64_compare_func("a", "b") == -1);
+ assert_se(uint64_compare_func("b", "a") == 1);
+}
+
+static void test_trivial_compare_func(void) {
+ assert_se(trivial_compare_func(INT_TO_PTR('a'), INT_TO_PTR('a')) == 0);
+ assert_se(trivial_compare_func(INT_TO_PTR('a'), INT_TO_PTR('b')) == -1);
+ assert_se(trivial_compare_func(INT_TO_PTR('b'), INT_TO_PTR('a')) == 1);
+}
+
+static void test_string_compare_func(void) {
+ assert_se(!string_compare_func("fred", "wilma") == 0);
+ assert_se(string_compare_func("fred", "fred") == 0);
+}
+
+int main(int argc, const char *argv[])
+{
+ test_hashmap_copy();
+ test_hashmap_get_strv();
+ test_hashmap_move_one();
+ test_hashmap_next();
+ test_hashmap_replace();
+ test_hashmap_update();
+ test_hashmap_put();
+ test_hashmap_ensure_allocated();
+ test_hashmap_foreach();
+ test_hashmap_foreach_backwards();
+ test_hashmap_foreach_key();
+ test_hashmap_contains();
+ test_hashmap_merge();
+ test_hashmap_isempty();
+ test_hashmap_get();
+ test_hashmap_size();
+ test_uint64_compare_func();
+ test_trivial_compare_func();
+ test_string_compare_func();
+}
diff --git a/src/test/test-id128.c b/src/test/test-id128.c
index bfd743eca3..2ed8e292e6 100644
--- a/src/test/test-id128.c
+++ b/src/test/test-id128.c
@@ -27,10 +27,13 @@
#include "macro.h"
#define ID128_WALDI SD_ID128_MAKE(01, 02, 03, 04, 05, 06, 07, 08, 09, 0a, 0b, 0c, 0d, 0e, 0f, 10)
+#define STR_WALDI "0102030405060708090a0b0c0d0e0f10"
+#define UUID_WALDI "01020304-0506-0708-090a-0b0c0d0e0f10"
int main(int argc, char *argv[]) {
sd_id128_t id, id2;
char t[33];
+ _cleanup_free_ char *b = NULL;
assert_se(sd_id128_randomize(&id) == 0);
printf("random: %s\n", sd_id128_to_string(id, t));
@@ -45,8 +48,28 @@ int main(int argc, char *argv[]) {
printf("boot: %s\n", sd_id128_to_string(id, t));
printf("waldi: %s\n", sd_id128_to_string(ID128_WALDI, t));
-
- printf("waldi2: " SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(ID128_WALDI));
+ assert_se(streq(t, STR_WALDI));
+
+ assert_se(asprintf(&b, SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(ID128_WALDI)) == 32);
+ printf("waldi2: %s\n", b);
+ assert_se(streq(t, b));
+
+ assert_se(sd_id128_from_string(UUID_WALDI, &id) >= 0);
+ assert_se(sd_id128_equal(id, ID128_WALDI));
+
+ assert_se(sd_id128_from_string("", &id) < 0);
+ assert_se(sd_id128_from_string("01020304-0506-0708-090a-0b0c0d0e0f101", &id) < 0);
+ assert_se(sd_id128_from_string("01020304-0506-0708-090a-0b0c0d0e0f10-", &id) < 0);
+ assert_se(sd_id128_from_string("01020304-0506-0708-090a0b0c0d0e0f10", &id) < 0);
+ assert_se(sd_id128_from_string("010203040506-0708-090a-0b0c0d0e0f10", &id) < 0);
+
+ assert_se(id128_is_valid(STR_WALDI));
+ assert_se(id128_is_valid(UUID_WALDI));
+ assert_se(!id128_is_valid(""));
+ assert_se(!id128_is_valid("01020304-0506-0708-090a-0b0c0d0e0f101"));
+ assert_se(!id128_is_valid("01020304-0506-0708-090a-0b0c0d0e0f10-"));
+ assert_se(!id128_is_valid("01020304-0506-0708-090a0b0c0d0e0f10"));
+ assert_se(!id128_is_valid("010203040506-0708-090a-0b0c0d0e0f10"));
return 0;
}
diff --git a/src/test/test-ns.c b/src/test/test-ns.c
index b1c759fc20..ad0d0419c4 100644
--- a/src/test/test-ns.c
+++ b/src/test/test-ns.c
@@ -26,6 +26,7 @@
#include <linux/fs.h>
#include "namespace.h"
+#include "execute.h"
#include "log.h"
int main(int argc, char *argv[]) {
@@ -47,8 +48,19 @@ int main(int argc, char *argv[]) {
};
int r;
+ char tmp_dir[] = "/tmp/systemd-private-XXXXXX",
+ var_tmp_dir[] = "/var/tmp/systemd-private-XXXXXX";
- r = setup_namespace((char**) writable, (char**) readonly, (char**) inaccessible, true, 0);
+ assert_se(mkdtemp(tmp_dir));
+ assert_se(mkdtemp(var_tmp_dir));
+
+ r = setup_namespace((char **) writable,
+ (char **) readonly,
+ (char **) inaccessible,
+ tmp_dir,
+ var_tmp_dir,
+ true,
+ 0);
if (r < 0) {
log_error("Failed to setup namespace: %s", strerror(-r));
return 1;
diff --git a/src/test/test-path-util.c b/src/test/test-path-util.c
new file mode 100644
index 0000000000..127e17803f
--- /dev/null
+++ b/src/test/test-path-util.c
@@ -0,0 +1,89 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ 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 <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdio.h>
+
+#include "path-util.h"
+#include "util.h"
+#include "macro.h"
+
+
+static void test_path(void) {
+ assert_se(path_equal("/goo", "/goo"));
+ assert_se(path_equal("//goo", "/goo"));
+ assert_se(path_equal("//goo/////", "/goo"));
+ assert_se(path_equal("goo/////", "goo"));
+
+ assert_se(path_equal("/goo/boo", "/goo//boo"));
+ assert_se(path_equal("//goo/boo", "/goo/boo//"));
+
+ assert_se(path_equal("/", "///"));
+
+ assert_se(!path_equal("/x", "x/"));
+ assert_se(!path_equal("x/", "/"));
+
+ assert_se(!path_equal("/x/./y", "x/y"));
+ assert_se(!path_equal("x/.y", "x/y"));
+
+ assert_se(path_is_absolute("/"));
+ assert_se(!path_is_absolute("./"));
+
+ assert_se(is_path("/dir"));
+ assert_se(is_path("a/b"));
+ assert_se(!is_path("."));
+
+ assert_se(streq(path_get_file_name("./aa/bb/../file.da."), "file.da."));
+ assert_se(streq(path_get_file_name("/aa///.file"), ".file"));
+ assert_se(streq(path_get_file_name("/aa///file..."), "file..."));
+ assert_se(streq(path_get_file_name("file.../"), ""));
+
+#define test_parent(x, y) { \
+ char *z; \
+ int r = path_get_parent(x, &z); \
+ printf("expected: %s\n", y ? y : "error"); \
+ printf("actual: %s\n", r<0 ? "error" : z); \
+ assert_se((y==NULL) ^ (r==0)); \
+ assert_se(y==NULL || path_equal(z, y)); \
+ }
+
+ test_parent("./aa/bb/../file.da.", "./aa/bb/..");
+ test_parent("/aa///.file", "/aa///");
+ test_parent("/aa///file...", "/aa///");
+ test_parent("file.../", NULL);
+
+ assert_se(path_is_mount_point("/", true));
+ assert_se(path_is_mount_point("/", false));
+
+ {
+ char p1[] = "aaa/bbb////ccc";
+ char p2[] = "//aaa/.////ccc";
+ char p3[] = "/./";
+
+ assert(path_equal(path_kill_slashes(p1), "aaa/bbb/ccc"));
+ assert(path_equal(path_kill_slashes(p2), "/aaa/./ccc"));
+ assert(path_equal(path_kill_slashes(p3), "/./"));
+ }
+}
+
+int main(void) {
+ test_path();
+ return 0;
+}
diff --git a/src/test/test-prioq.c b/src/test/test-prioq.c
new file mode 100644
index 0000000000..aeac73973b
--- /dev/null
+++ b/src/test/test-prioq.c
@@ -0,0 +1,166 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ 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 <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdlib.h>
+
+#include "util.h"
+#include "set.h"
+#include "prioq.h"
+
+#define SET_SIZE 1024*4
+
+static int unsigned_compare(const void *a, const void *b) {
+ const unsigned *x = a, *y = b;
+
+ if (*x < *y)
+ return -1;
+
+ if (*x > *y)
+ return 1;
+
+ return 0;
+}
+
+static void test_unsigned(void) {
+ unsigned buffer[SET_SIZE], i;
+ Prioq *q;
+
+ srand(0);
+
+ q = prioq_new(trivial_compare_func);
+ assert_se(q);
+
+ for (i = 0; i < ELEMENTSOF(buffer); i++) {
+ unsigned u;
+
+ u = (unsigned) rand();
+ buffer[i] = u;
+ assert_se(prioq_put(q, UINT_TO_PTR(u), NULL) >= 0);
+ }
+
+ qsort(buffer, ELEMENTSOF(buffer), sizeof(buffer[0]), unsigned_compare);
+
+ for (i = 0; i < ELEMENTSOF(buffer); i++) {
+ unsigned u;
+
+ assert_se(prioq_size(q) == ELEMENTSOF(buffer) - i);
+
+ u = PTR_TO_UINT(prioq_pop(q));
+ assert_se(buffer[i] == u);
+ }
+
+ assert_se(prioq_isempty(q));
+ prioq_free(q);
+}
+
+struct test {
+ unsigned value;
+ unsigned idx;
+};
+
+static int test_compare(const void *a, const void *b) {
+ const struct test *x = a, *y = b;
+
+ if (x->value < y->value)
+ return -1;
+
+ if (x->value > y->value)
+ return 1;
+
+ return 0;
+}
+
+static unsigned test_hash(const void *a) {
+ const struct test *x = a;
+
+ return x->value;
+}
+
+static void test_struct(void) {
+ Prioq *q;
+ Set *s;
+ unsigned previous = 0, i;
+ int r;
+
+ srand(0);
+
+ q = prioq_new(test_compare);
+ assert_se(q);
+
+ s = set_new(test_hash, test_compare);
+ assert_se(s);
+
+ for (i = 0; i < SET_SIZE; i++) {
+ struct test *t;
+
+ t = new0(struct test, 1);
+ assert_se(t);
+ t->value = (unsigned) rand();
+
+ r = prioq_put(q, t, &t->idx);
+ assert_se(r >= 0);
+
+ if (i % 4 == 0) {
+ r = set_consume(s, t);
+ assert_se(r >= 0);
+ }
+ }
+
+ for (;;) {
+ struct test *t;
+
+ t = set_steal_first(s);
+ if (!t)
+ break;
+
+ r = prioq_remove(q, t, &t->idx);
+ assert_se(r > 0);
+
+ free(t);
+ }
+
+ for (i = 0; i < SET_SIZE * 3 / 4; i++) {
+ struct test *t;
+
+ assert_se(prioq_size(q) == (SET_SIZE * 3 / 4) - i);
+
+ t = prioq_pop(q);
+ assert_se(t);
+
+ assert_se(previous <= t->value);
+ previous = t->value;
+ free(t);
+ }
+
+ assert_se(prioq_isempty(q));
+ prioq_free(q);
+
+ assert_se(set_isempty(s));
+ set_free(s);
+}
+
+int main(int argc, char* argv[]) {
+
+ test_unsigned();
+ test_struct();
+
+ return 0;
+}
diff --git a/src/test/test-sched-prio.c b/src/test/test-sched-prio.c
index 29235e8347..ba0aacf79d 100644
--- a/src/test/test-sched-prio.c
+++ b/src/test/test-sched-prio.c
@@ -22,7 +22,7 @@
#include <sched.h>
#include "manager.h"
-
+#include "macro.h"
int main(int argc, char *argv[]) {
Manager *m;
@@ -30,10 +30,16 @@ int main(int argc, char *argv[]) {
Service *ser;
FILE *serial = NULL;
FDSet *fdset = NULL;
+ int r;
/* prepare the test */
assert_se(set_unit_path(TEST_DIR) >= 0);
- assert_se(manager_new(SYSTEMD_SYSTEM, &m) >= 0);
+ r = manager_new(SYSTEMD_USER, &m);
+ if (r == -EPERM) {
+ puts("manager_new: Permission denied. Skipping test.");
+ return EXIT_TEST_SKIP;
+ }
+ assert(r >= 0);
assert_se(manager_startup(m, serial, fdset) >= 0);
/* load idle ok */
@@ -82,5 +88,5 @@ int main(int argc, char *argv[]) {
assert_se(ser->exec_context.cpu_sched_policy == SCHED_RR);
assert_se(ser->exec_context.cpu_sched_priority == 99);
- return 0;
+ return EXIT_SUCCESS;
}
diff --git a/src/test/test-sleep.c b/src/test/test-sleep.c
index 5a98ecda2f..c3cb9c531d 100644
--- a/src/test/test-sleep.c
+++ b/src/test/test-sleep.c
@@ -26,14 +26,32 @@
#include "util.h"
#include "log.h"
+#include "sleep-config.h"
+#include "strv.h"
int main(int argc, char* argv[]) {
- log_info("Can Suspend: %s", yes_no(can_sleep("mem") > 0));
- log_info("Can Hibernate: %s", yes_no(can_sleep("disk") > 0));
- log_info("Can Hibernate+Suspend (Hybrid-Sleep): %s", yes_no(can_sleep_disk("suspend") > 0));
- log_info("Can Hibernate+Reboot: %s", yes_no(can_sleep_disk("reboot") > 0));
- log_info("Can Hibernate+Platform: %s", yes_no(can_sleep_disk("platform") > 0));
- log_info("Can Hibernate+Shutdown: %s", yes_no(can_sleep_disk("shutdown") > 0));
+ _cleanup_strv_free_ char
+ **standby = strv_new("standby", NULL),
+ **mem = strv_new("mem", NULL),
+ **disk = strv_new("disk", NULL),
+ **suspend = strv_new("suspend", NULL),
+ **reboot = strv_new("reboot", NULL),
+ **platform = strv_new("platform", NULL),
+ **shutdown = strv_new("shutdown", NULL),
+ **freez = strv_new("freeze", NULL);
+
+ log_info("Can Standby: %s", yes_no(can_sleep_state(standby) > 0));
+ log_info("Can Suspend: %s", yes_no(can_sleep_state(mem) > 0));
+ log_info("Can Hibernate: %s", yes_no(can_sleep_state(disk) > 0));
+ log_info("Can Hibernate+Suspend (Hybrid-Sleep): %s", yes_no(can_sleep_disk(suspend) > 0));
+ log_info("Can Hibernate+Reboot: %s", yes_no(can_sleep_disk(reboot) > 0));
+ log_info("Can Hibernate+Platform: %s", yes_no(can_sleep_disk(platform) > 0));
+ log_info("Can Hibernate+Shutdown: %s", yes_no(can_sleep_disk(shutdown) > 0));
+ log_info("Can Freeze: %s", yes_no(can_sleep_disk(freez) > 0));
+
+ log_info("Suspend configured and possible: %s", yes_no(can_sleep("suspend") > 0));
+ log_info("Hibernation configured and possible: %s", yes_no(can_sleep("hibernate") > 0));
+ log_info("Hybrid-sleep configured and possible: %s", yes_no(can_sleep("hybrid-sleep") > 0));
return 0;
}
diff --git a/src/test/test-strbuf.c b/src/test/test-strbuf.c
new file mode 100644
index 0000000000..e9b6c033fd
--- /dev/null
+++ b/src/test/test-strbuf.c
@@ -0,0 +1,93 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 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
+ 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 <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "strbuf.h"
+#include "strv.h"
+#include "util.h"
+
+static ssize_t add_string(struct strbuf *sb, const char *s) {
+ return strbuf_add_string(sb, s, strlen(s));
+}
+
+static void test_strbuf(void) {
+ struct strbuf *sb;
+ _cleanup_strv_free_ char **l;
+ ssize_t a, b, c, d, e, f, g;
+
+ sb = strbuf_new();
+
+ a = add_string(sb, "waldo");
+ b = add_string(sb, "foo");
+ c = add_string(sb, "bar");
+ d = add_string(sb, "waldo"); /* duplicate */
+ e = add_string(sb, "aldo"); /* duplicate */
+ f = add_string(sb, "do"); /* duplicate */
+ g = add_string(sb, "waldorf"); /* not a duplicate: matches from tail */
+
+ /* check the content of the buffer directly */
+ l = strv_parse_nulstr(sb->buf, sb->len);
+
+ assert(streq(l[0], "")); /* root*/
+ assert(streq(l[1], "waldo"));
+ assert(streq(l[2], "foo"));
+ assert(streq(l[3], "bar"));
+ assert(streq(l[4], "waldorf"));
+
+ assert(sb->nodes_count == 5); /* root + 4 non-duplicates */
+ assert(sb->dedup_count == 3);
+ assert(sb->in_count == 7);
+
+ assert(sb->in_len == 29); /* length of all strings added */
+ assert(sb->dedup_len == 11); /* length of all strings duplicated */
+ assert(sb->len == 23); /* buffer length: in - dedup + \0 for each node */
+
+ /* check the returned offsets and the respective content in the buffer */
+ assert(a == 1);
+ assert(b == 7);
+ assert(c == 11);
+ assert(d == 1);
+ assert(e == 2);
+ assert(f == 4);
+ assert(g == 15);
+
+ assert(streq(sb->buf + a, "waldo"));
+ assert(streq(sb->buf + b, "foo"));
+ assert(streq(sb->buf + c, "bar"));
+ assert(streq(sb->buf + d, "waldo"));
+ assert(streq(sb->buf + e, "aldo"));
+ assert(streq(sb->buf + f, "do"));
+ assert(streq(sb->buf + g, "waldorf"));
+
+ strbuf_complete(sb);
+ assert(sb->root == NULL);
+
+ strbuf_cleanup(sb);
+}
+
+int main(int argc, const char *argv[])
+{
+ test_strbuf();
+
+ return 0;
+}
diff --git a/src/test/test-strv.c b/src/test/test-strv.c
index 5ee4447669..074e1bb3d4 100644
--- a/src/test/test-strv.c
+++ b/src/test/test-strv.c
@@ -4,6 +4,7 @@
This file is part of systemd.
Copyright 2010 Lennart Poettering
+ Copyright 2013 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
@@ -23,44 +24,242 @@
#include "util.h"
#include "specifier.h"
+#include "strv.h"
+
+static void test_specifier_printf(void) {
+ _cleanup_free_ char *w = NULL;
-int main(int argc, char *argv[]) {
const Specifier table[] = {
{ 'a', specifier_string, (char*) "AAAA" },
{ 'b', specifier_string, (char*) "BBBB" },
{ 0, NULL, NULL }
};
- char *w, *state;
- size_t l;
- const char test[] = "test a b c 'd' e '' '' hhh '' ''";
+ w = specifier_printf("xxx a=%a b=%b yyy", table, NULL);
+ puts(w);
+
+ assert_se(w);
+ assert_se(streq(w, "xxx a=AAAA b=BBBB yyy"));
+}
- printf("<%s>\n", test);
+static void test_strv_find(void) {
+ const char * const input_table[] = {
+ "one",
+ "two",
+ "three",
+ NULL
+ };
- FOREACH_WORD_QUOTED(w, l, test, state) {
- char *t;
+ assert_se(strv_find((char **)input_table, "three"));
+ assert_se(!strv_find((char **)input_table, "four"));
+}
- assert_se(t = strndup(w, l));
- printf("<%s>\n", t);
- free(t);
- }
+static void test_strv_find_prefix(void) {
+ const char * const input_table[] = {
+ "one",
+ "two",
+ "three",
+ NULL
+ };
+
+ assert_se(strv_find_prefix((char **)input_table, "o"));
+ assert_se(strv_find_prefix((char **)input_table, "one"));
+ assert_se(strv_find_prefix((char **)input_table, ""));
+ assert_se(!strv_find_prefix((char **)input_table, "xxx"));
+ assert_se(!strv_find_prefix((char **)input_table, "onee"));
+}
- printf("%s\n", default_term_for_tty("/dev/tty23"));
- printf("%s\n", default_term_for_tty("/dev/ttyS23"));
- printf("%s\n", default_term_for_tty("/dev/tty0"));
- printf("%s\n", default_term_for_tty("/dev/pty0"));
- printf("%s\n", default_term_for_tty("/dev/pts/0"));
- printf("%s\n", default_term_for_tty("/dev/console"));
- printf("%s\n", default_term_for_tty("tty23"));
- printf("%s\n", default_term_for_tty("ttyS23"));
- printf("%s\n", default_term_for_tty("tty0"));
- printf("%s\n", default_term_for_tty("pty0"));
- printf("%s\n", default_term_for_tty("pts/0"));
- printf("%s\n", default_term_for_tty("console"));
+static void test_strv_join(void) {
+ _cleanup_free_ char *p = NULL, *q = NULL, *r = NULL, *s = NULL, *t = NULL;
- w = specifier_printf("xxx a=%a b=%b yyy", table, NULL);
- printf("<%s>\n", w);
- free(w);
+ const char * const input_table_multiple[] = {
+ "one",
+ "two",
+ "three",
+ NULL
+ };
+ const char * const input_table_one[] = {
+ "one",
+ NULL
+ };
+ const char * const input_table_none[] = {
+ NULL
+ };
+
+ p = strv_join((char **)input_table_multiple, ", ");
+ assert_se(p);
+ assert_se(streq(p, "one, two, three"));
+
+ q = strv_join((char **)input_table_multiple, ";");
+ assert_se(q);
+ assert_se(streq(q, "one;two;three"));
+
+ r = strv_join((char **)input_table_multiple, NULL);
+ assert_se(r);
+ assert_se(streq(r, "one two three"));
+
+ s = strv_join((char **)input_table_one, ", ");
+ assert_se(s);
+ assert_se(streq(s, "one"));
+
+ t = strv_join((char **)input_table_none, ", ");
+ assert_se(t);
+ assert_se(streq(t, ""));
+}
+
+static void test_strv_split_nulstr(void) {
+ _cleanup_strv_free_ char **l = NULL;
+ const char nulstr[] = "str0\0str1\0str2\0str3\0";
+
+ l = strv_split_nulstr (nulstr);
+ assert_se(l);
+
+ assert_se(streq(l[0], "str0"));
+ assert_se(streq(l[1], "str1"));
+ assert_se(streq(l[2], "str2"));
+ assert_se(streq(l[3], "str3"));
+}
+
+static void test_strv_parse_nulstr(void) {
+ _cleanup_strv_free_ char **l = NULL;
+ const char nulstr[] = "fuck\0fuck2\0fuck3\0\0fuck5\0\0xxx";
+
+ l = strv_parse_nulstr(nulstr, sizeof(nulstr)-1);
+ assert_se(l);
+ puts("Parse nulstr:");
+ strv_print(l);
+
+ assert_se(streq(l[0], "fuck"));
+ assert_se(streq(l[1], "fuck2"));
+ assert_se(streq(l[2], "fuck3"));
+ assert_se(streq(l[3], ""));
+ assert_se(streq(l[4], "fuck5"));
+ assert_se(streq(l[5], ""));
+ assert_se(streq(l[6], "xxx"));
+}
+
+static void test_strv_overlap(void) {
+ const char * const input_table[] = {
+ "one",
+ "two",
+ "three",
+ NULL
+ };
+ const char * const input_table_overlap[] = {
+ "two",
+ NULL
+ };
+ const char * const input_table_unique[] = {
+ "four",
+ "five",
+ "six",
+ NULL
+ };
+
+ assert_se(strv_overlap((char **)input_table, (char**)input_table_overlap));
+ assert_se(!strv_overlap((char **)input_table, (char**)input_table_unique));
+}
+
+static void test_strv_sort(void) {
+ const char* input_table[] = {
+ "durian",
+ "apple",
+ "citrus",
+ "CAPITAL LETTERS FIRST",
+ "banana",
+ NULL
+ };
+
+ strv_sort((char **)input_table);
+
+ assert_se(streq(input_table[0], "CAPITAL LETTERS FIRST"));
+ assert_se(streq(input_table[1], "apple"));
+ assert_se(streq(input_table[2], "banana"));
+ assert_se(streq(input_table[3], "citrus"));
+ assert_se(streq(input_table[4], "durian"));
+}
+
+static void test_strv_merge_concat(void) {
+ _cleanup_strv_free_ char **a = NULL, **b = NULL, **c = NULL;
+
+ a = strv_new("without", "suffix", NULL);
+ b = strv_new("with", "suffix", NULL);
+ assert_se(a);
+ assert_se(b);
+
+ c = strv_merge_concat(a, b, "_suffix");
+ assert_se(c);
+
+ assert_se(streq(c[0], "without"));
+ assert_se(streq(c[1], "suffix"));
+ assert_se(streq(c[2], "with_suffix"));
+ assert_se(streq(c[3], "suffix_suffix"));
+}
+
+static void test_strv_merge(void) {
+ _cleanup_strv_free_ char **a = NULL, **b = NULL, **c = NULL;
+
+ a = strv_new("abc", "def", "ghi", NULL);
+ b = strv_new("jkl", "mno", "pqr", NULL);
+ assert_se(a);
+ assert_se(b);
+
+ c = strv_merge(a, b);
+ assert_se(c);
+
+ assert_se(streq(c[0], "abc"));
+ assert_se(streq(c[1], "def"));
+ assert_se(streq(c[2], "ghi"));
+ assert_se(streq(c[3], "jkl"));
+ assert_se(streq(c[4], "mno"));
+ assert_se(streq(c[5], "pqr"));
+
+ assert_se(strv_length(c) == 6);
+}
+
+static void test_strv_append(void) {
+ _cleanup_strv_free_ char **a = NULL, **b = NULL, **c = NULL;
+
+ a = strv_new("test", "test1", NULL);
+ assert_se(a);
+ b = strv_append(a, "test2");
+ c = strv_append(NULL, "test3");
+ assert_se(b);
+ assert_se(c);
+
+ assert_se(streq(b[0], "test"));
+ assert_se(streq(b[1], "test1"));
+ assert_se(streq(b[2], "test2"));
+ assert_se(streq(c[0], "test3"));
+}
+
+static void test_strv_foreach_pair(void) {
+ _cleanup_strv_free_ char **a = NULL;
+ char **x, **y;
+
+ a = strv_new("pair_one", "pair_one",
+ "pair_two", "pair_two",
+ "pair_three", "pair_three",
+ NULL);
+
+ STRV_FOREACH_PAIR(x, y, a) {
+ assert_se(streq(*x, *y));
+ }
+}
+
+int main(int argc, char *argv[]) {
+ test_specifier_printf();
+ test_strv_foreach_pair();
+ test_strv_find();
+ test_strv_find_prefix();
+ test_strv_join();
+ test_strv_split_nulstr();
+ test_strv_parse_nulstr();
+ test_strv_overlap();
+ test_strv_sort();
+ test_strv_merge();
+ test_strv_merge_concat();
+ test_strv_append();
return 0;
}
diff --git a/src/test/test-strxcpyx.c b/src/test/test-strxcpyx.c
new file mode 100644
index 0000000000..b7b70d4c15
--- /dev/null
+++ b/src/test/test-strxcpyx.c
@@ -0,0 +1,101 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 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
+ 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 <http://www.gnu.org/licenses/>.
+***/
+
+#include <string.h>
+
+#include "util.h"
+#include "strv.h"
+#include "strxcpyx.h"
+
+static void test_strpcpy(void) {
+ char target[25];
+ char *s = target;
+ size_t space_left;
+
+ space_left = sizeof(target);
+ space_left = strpcpy(&s, space_left, "12345");
+ space_left = strpcpy(&s, space_left, "hey hey hey");
+ space_left = strpcpy(&s, space_left, "waldo");
+ space_left = strpcpy(&s, space_left, "ba");
+ space_left = strpcpy(&s, space_left, "r");
+ space_left = strpcpy(&s, space_left, "foo");
+
+ assert(streq(target, "12345hey hey heywaldobar"));
+ assert(space_left == 0);
+}
+
+static void test_strpcpyf(void) {
+ char target[25];
+ char *s = target;
+ size_t space_left;
+
+ space_left = sizeof(target);
+ space_left = strpcpyf(&s, space_left, "space left: %zd. ", space_left);
+ space_left = strpcpyf(&s, space_left, "foo%s", "bar");
+
+ assert(streq(target, "space left: 25. foobar"));
+ assert(space_left == 3);
+}
+
+static void test_strpcpyl(void) {
+ char target[25];
+ char *s = target;
+ size_t space_left;
+
+ space_left = sizeof(target);
+ space_left = strpcpyl(&s, space_left, "waldo", " test", " waldo. ", NULL);
+ space_left = strpcpyl(&s, space_left, "Banana", NULL);
+
+ assert(streq(target, "waldo test waldo. Banana"));
+ assert(space_left == 1);
+}
+
+static void test_strscpy(void) {
+ char target[25];
+ size_t space_left;
+
+ space_left = sizeof(target);
+ space_left = strscpy(target, space_left, "12345");
+
+ assert(streq(target, "12345"));
+ assert(space_left == 20);
+}
+
+static void test_strscpyl(void) {
+ char target[25];
+ size_t space_left;
+
+ space_left = sizeof(target);
+ space_left = strscpyl(target, space_left, "12345", "waldo", "waldo", NULL);
+
+ assert(streq(target, "12345waldowaldo"));
+ assert(space_left == 10);
+}
+
+int main(int argc, char *argv[]) {
+ test_strpcpy();
+ test_strpcpyf();
+ test_strpcpyl();
+ test_strscpy();
+ test_strscpyl();
+
+ return 0;
+}
diff --git a/src/test/test-time.c b/src/test/test-time.c
new file mode 100644
index 0000000000..36a33046a2
--- /dev/null
+++ b/src/test/test-time.c
@@ -0,0 +1,136 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ 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 <http://www.gnu.org/licenses/>.
+***/
+
+#include "time-util.h"
+
+static void test_parse_sec(void) {
+ usec_t u;
+
+ assert_se(parse_sec("5s", &u) >= 0);
+ assert_se(u == 5 * USEC_PER_SEC);
+ assert_se(parse_sec("5s500ms", &u) >= 0);
+ assert_se(u == 5 * USEC_PER_SEC + 500 * USEC_PER_MSEC);
+ assert_se(parse_sec(" 5s 500ms ", &u) >= 0);
+ assert_se(u == 5 * USEC_PER_SEC + 500 * USEC_PER_MSEC);
+ assert_se(parse_sec(" 5.5s ", &u) >= 0);
+ assert_se(u == 5 * USEC_PER_SEC + 500 * USEC_PER_MSEC);
+ assert_se(parse_sec(" 5.5s 0.5ms ", &u) >= 0);
+ assert_se(u == 5 * USEC_PER_SEC + 500 * USEC_PER_MSEC + 500);
+ assert_se(parse_sec(" .22s ", &u) >= 0);
+ assert_se(u == 220 * USEC_PER_MSEC);
+ assert_se(parse_sec(" .50y ", &u) >= 0);
+ assert_se(u == USEC_PER_YEAR / 2);
+ assert_se(parse_sec("2.5", &u) >= 0);
+ assert_se(u == 2500 * USEC_PER_MSEC);
+ assert_se(parse_sec(".7", &u) >= 0);
+ assert_se(u == 700 * USEC_PER_MSEC);
+
+ assert_se(parse_sec(" xyz ", &u) < 0);
+ assert_se(parse_sec("", &u) < 0);
+ assert_se(parse_sec(" . ", &u) < 0);
+ assert_se(parse_sec(" 5. ", &u) < 0);
+ assert_se(parse_sec(".s ", &u) < 0);
+}
+
+static void test_parse_nsec(void) {
+ nsec_t u;
+
+ assert_se(parse_nsec("5s", &u) >= 0);
+ assert_se(u == 5 * NSEC_PER_SEC);
+ assert_se(parse_nsec("5s500ms", &u) >= 0);
+ assert_se(u == 5 * NSEC_PER_SEC + 500 * NSEC_PER_MSEC);
+ assert_se(parse_nsec(" 5s 500ms ", &u) >= 0);
+ assert_se(u == 5 * NSEC_PER_SEC + 500 * NSEC_PER_MSEC);
+ assert_se(parse_nsec(" 5.5s ", &u) >= 0);
+ assert_se(u == 5 * NSEC_PER_SEC + 500 * NSEC_PER_MSEC);
+ assert_se(parse_nsec(" 5.5s 0.5ms ", &u) >= 0);
+ assert_se(u == 5 * NSEC_PER_SEC + 500 * NSEC_PER_MSEC + 500 * NSEC_PER_USEC);
+ assert_se(parse_nsec(" .22s ", &u) >= 0);
+ assert_se(u == 220 * NSEC_PER_MSEC);
+ assert_se(parse_nsec(" .50y ", &u) >= 0);
+ assert_se(u == NSEC_PER_YEAR / 2);
+ assert_se(parse_nsec("2.5", &u) >= 0);
+ assert_se(u == 2);
+ assert_se(parse_nsec(".7", &u) >= 0);
+ assert_se(u == 0);
+
+ assert_se(parse_nsec(" xyz ", &u) < 0);
+ assert_se(parse_nsec("", &u) < 0);
+ assert_se(parse_nsec(" . ", &u) < 0);
+ assert_se(parse_nsec(" 5. ", &u) < 0);
+ assert_se(parse_nsec(".s ", &u) < 0);
+}
+
+static void test_format_timespan_one(usec_t x, usec_t accuracy) {
+ char *r;
+ char l[FORMAT_TIMESPAN_MAX];
+ usec_t y;
+
+ log_info("%llu (at accuracy %llu)", (unsigned long long) x, (unsigned long long) accuracy);
+
+ r = format_timespan(l, sizeof(l), x, accuracy);
+ assert_se(r);
+
+ log_info(" = <%s>", l);
+
+ assert_se(parse_sec(l, &y) >= 0);
+
+ log_info(" = %llu", (unsigned long long) y);
+
+ if (accuracy <= 0)
+ accuracy = 1;
+
+ assert_se(x / accuracy == y / accuracy);
+}
+
+static void test_format_timespan(usec_t accuracy) {
+ test_format_timespan_one(0, accuracy);
+ test_format_timespan_one(1, accuracy);
+ test_format_timespan_one(1*USEC_PER_SEC, accuracy);
+ test_format_timespan_one(999*USEC_PER_MSEC, accuracy);
+ test_format_timespan_one(1234567, accuracy);
+ test_format_timespan_one(12, accuracy);
+ test_format_timespan_one(123, accuracy);
+ test_format_timespan_one(1234, accuracy);
+ test_format_timespan_one(12345, accuracy);
+ test_format_timespan_one(123456, accuracy);
+ test_format_timespan_one(1234567, accuracy);
+ test_format_timespan_one(12345678, accuracy);
+ test_format_timespan_one(1200000, accuracy);
+ test_format_timespan_one(1230000, accuracy);
+ test_format_timespan_one(1230000, accuracy);
+ test_format_timespan_one(1234000, accuracy);
+ test_format_timespan_one(1234500, accuracy);
+ test_format_timespan_one(1234560, accuracy);
+ test_format_timespan_one(1234567, accuracy);
+ test_format_timespan_one(986087, accuracy);
+ test_format_timespan_one(500 * USEC_PER_MSEC, accuracy);
+ test_format_timespan_one(9*USEC_PER_YEAR/5 - 23, accuracy);
+}
+
+int main(int argc, char *argv[]) {
+ test_parse_sec();
+ test_parse_nsec();
+ test_format_timespan(1);
+ test_format_timespan(USEC_PER_MSEC);
+ test_format_timespan(USEC_PER_SEC);
+ return 0;
+}
diff --git a/src/test/test-udev.c b/src/test/test-udev.c
index db9d36124e..52b61b4206 100644
--- a/src/test/test-udev.c
+++ b/src/test/test-udev.c
@@ -32,6 +32,7 @@
#include <sys/mount.h>
#include <sys/signalfd.h>
+#include "missing.h"
#include "udev.h"
void udev_main_log(struct udev *udev, int priority,
@@ -117,7 +118,7 @@ int main(int argc, char *argv[])
rules = udev_rules_new(udev, 1);
- util_strscpyl(syspath, sizeof(syspath), "/sys", devpath, NULL);
+ strscpyl(syspath, sizeof(syspath), "/sys", devpath, NULL);
dev = udev_device_new_from_syspath(udev, syspath);
if (dev == NULL) {
log_debug("unknown device '%s'\n", devpath);
@@ -139,12 +140,12 @@ int main(int argc, char *argv[])
if (udev_device_get_devnode(dev) != NULL) {
mode_t mode = 0600;
- if (strcmp(udev_device_get_subsystem(dev), "block") == 0)
+ if (streq(udev_device_get_subsystem(dev), "block"))
mode |= S_IFBLK;
else
mode |= S_IFCHR;
- if (strcmp(action, "remove") != 0) {
+ if (!streq(action, "remove")) {
mkdir_parents_label(udev_device_get_devnode(dev), 0755);
mknod(udev_device_get_devnode(dev), mode, udev_device_get_devnum(dev));
} else {
diff --git a/src/test/test-unit-file.c b/src/test/test-unit-file.c
index 6636b949ea..a7fe77af24 100644
--- a/src/test/test-unit-file.c
+++ b/src/test/test-unit-file.c
@@ -4,6 +4,7 @@
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
@@ -23,12 +24,17 @@
#include <stdio.h>
#include <stddef.h>
#include <string.h>
+#include <unistd.h>
#include "install.h"
+#include "install-printf.h"
+#include "specifier.h"
#include "util.h"
#include "macro.h"
#include "hashmap.h"
#include "load-fragment.h"
+#include "strv.h"
+#include "fileio.h"
static void test_unit_file_get_set(void) {
int r;
@@ -79,13 +85,13 @@ static void test_config_parse_exec(void) {
ExecCommand *c = NULL, *c1;
/* basic test */
- r = config_parse_exec("fake", 1, "section",
+ r = config_parse_exec(NULL, "fake", 1, "section",
"LValue", 0, "/RValue r1",
&c, NULL);
assert_se(r >= 0);
check_execcommand(c, "/RValue", "/RValue", "r1", false);
- r = config_parse_exec("fake", 2, "section",
+ r = config_parse_exec(NULL, "fake", 2, "section",
"LValue", 0, "/RValue///slashes/// r1",
&c, NULL);
/* test slashes */
@@ -95,7 +101,7 @@ static void test_config_parse_exec(void) {
"r1", false);
/* honour_argv0 */
- r = config_parse_exec("fake", 3, "section",
+ r = config_parse_exec(NULL, "fake", 3, "section",
"LValue", 0, "@/RValue///slashes2/// argv0 r1",
&c, NULL);
assert_se(r >= 0);
@@ -103,7 +109,7 @@ static void test_config_parse_exec(void) {
check_execcommand(c1, "/RValue/slashes2", "argv0", "r1", false);
/* ignore && honour_argv0 */
- r = config_parse_exec("fake", 4, "section",
+ r = config_parse_exec(NULL, "fake", 4, "section",
"LValue", 0, "-@/RValue///slashes3/// argv0a r1",
&c, NULL);
assert_se(r >= 0);
@@ -112,7 +118,7 @@ static void test_config_parse_exec(void) {
"/RValue/slashes3", "argv0a", "r1", true);
/* ignore && honour_argv0 */
- r = config_parse_exec("fake", 4, "section",
+ r = config_parse_exec(NULL, "fake", 4, "section",
"LValue", 0, "@-/RValue///slashes4/// argv0b r1",
&c, NULL);
assert_se(r >= 0);
@@ -121,21 +127,21 @@ static void test_config_parse_exec(void) {
"/RValue/slashes4", "argv0b", "r1", true);
/* ignore && ignore */
- r = config_parse_exec("fake", 4, "section",
+ r = config_parse_exec(NULL, "fake", 4, "section",
"LValue", 0, "--/RValue argv0 r1",
&c, NULL);
assert_se(r == 0);
assert_se(c1->command_next == NULL);
/* ignore && ignore */
- r = config_parse_exec("fake", 4, "section",
+ r = config_parse_exec(NULL, "fake", 4, "section",
"LValue", 0, "-@-/RValue argv0 r1",
&c, NULL);
assert_se(r == 0);
assert_se(c1->command_next == NULL);
/* semicolon */
- r = config_parse_exec("fake", 5, "section",
+ r = config_parse_exec(NULL, "fake", 5, "section",
"LValue", 0,
"-@/RValue argv0 r1 ; "
"/goo/goo boo",
@@ -150,7 +156,7 @@ static void test_config_parse_exec(void) {
"/goo/goo", "/goo/goo", "boo", false);
/* trailing semicolon */
- r = config_parse_exec("fake", 5, "section",
+ r = config_parse_exec(NULL, "fake", 5, "section",
"LValue", 0,
"-@/RValue argv0 r1 ; ",
&c, NULL);
@@ -162,7 +168,7 @@ static void test_config_parse_exec(void) {
assert_se(c1->command_next == NULL);
/* escaped semicolon */
- r = config_parse_exec("fake", 5, "section",
+ r = config_parse_exec(NULL, "fake", 5, "section",
"LValue", 0,
"/usr/bin/find \\;",
&c, NULL);
@@ -174,10 +180,188 @@ static void test_config_parse_exec(void) {
exec_command_free_list(c);
}
+#define env_file_1 \
+ "a=a\n" \
+ "b=b\\\n" \
+ "c\n" \
+ "d=d\\\n" \
+ "e\\\n" \
+ "f\n" \
+ "g=g\\ \n" \
+ "h=h\n" \
+ "i=i\\"
+
+#define env_file_2 \
+ "a=a\\\n"
+
+#define env_file_3 \
+ "#SPAMD_ARGS=\"-d --socketpath=/var/lib/bulwark/spamd \\\n" \
+ "#--nouser-config \\\n" \
+ "normal=line"
+
+#define env_file_4 \
+ "# Generated\n" \
+ "\n" \
+ "HWMON_MODULES=\"coretemp f71882fg\"\n" \
+ "\n" \
+ "# For compatibility reasons\n" \
+ "\n" \
+ "MODULE_0=coretemp\n" \
+ "MODULE_1=f71882fg"
+
+
+static void test_load_env_file_1(void) {
+ _cleanup_strv_free_ char **data = NULL;
+ int r;
+
+ char name[] = "/tmp/test-load-env-file.XXXXXX";
+ _cleanup_close_ int fd = mkstemp(name);
+ assert(fd >= 0);
+ assert_se(write(fd, env_file_1, sizeof(env_file_1)) == sizeof(env_file_1));
+
+ r = load_env_file(name, NULL, &data);
+ assert(r == 0);
+ assert(streq(data[0], "a=a"));
+ assert(streq(data[1], "b=bc"));
+ assert(streq(data[2], "d=def"));
+ assert(streq(data[3], "g=g "));
+ assert(streq(data[4], "h=h"));
+ assert(streq(data[5], "i=i"));
+ assert(data[6] == NULL);
+ unlink(name);
+}
+
+static void test_load_env_file_2(void) {
+ _cleanup_strv_free_ char **data = NULL;
+ int r;
+
+ char name[] = "/tmp/test-load-env-file.XXXXXX";
+ _cleanup_close_ int fd = mkstemp(name);
+ assert(fd >= 0);
+ assert_se(write(fd, env_file_2, sizeof(env_file_2)) == sizeof(env_file_2));
+
+ r = load_env_file(name, NULL, &data);
+ assert(r == 0);
+ assert(streq(data[0], "a=a"));
+ assert(data[1] == NULL);
+ unlink(name);
+}
+
+static void test_load_env_file_3(void) {
+ _cleanup_strv_free_ char **data = NULL;
+ int r;
+
+ char name[] = "/tmp/test-load-env-file.XXXXXX";
+ _cleanup_close_ int fd = mkstemp(name);
+ assert(fd >= 0);
+ assert_se(write(fd, env_file_3, sizeof(env_file_3)) == sizeof(env_file_3));
+
+ r = load_env_file(name, NULL, &data);
+ assert(r == 0);
+ assert(data == NULL);
+ unlink(name);
+}
+
+static void test_load_env_file_4(void) {
+ _cleanup_strv_free_ char **data = NULL;
+ int r;
+
+ char name[] = "/tmp/test-load-env-file.XXXXXX";
+ _cleanup_close_ int fd = mkstemp(name);
+ assert(fd >= 0);
+ assert_se(write(fd, env_file_4, sizeof(env_file_4)) == sizeof(env_file_4));
+
+ r = load_env_file(name, NULL, &data);
+ assert(r == 0);
+ assert(streq(data[0], "HWMON_MODULES=coretemp f71882fg"));
+ assert(streq(data[1], "MODULE_0=coretemp"));
+ assert(streq(data[2], "MODULE_1=f71882fg"));
+ assert(data[3] == NULL);
+ unlink(name);
+}
+
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wnonnull"
+
+static void test_install_printf(void) {
+ char name[] = "name.service",
+ path[] = "/run/systemd/system/name.service",
+ user[] = "xxxx-no-such-user";
+ InstallInfo i = {name, path, user};
+ InstallInfo i2 = {name, path, NULL};
+ char name3[] = "name@inst.service",
+ path3[] = "/run/systemd/system/name.service";
+ InstallInfo i3 = {name3, path3, user};
+ InstallInfo i4 = {name3, path3, NULL};
+
+ _cleanup_free_ char *mid, *bid, *host;
+
+ assert_se((mid = specifier_machine_id('m', NULL, NULL)));
+ assert_se((bid = specifier_boot_id('b', NULL, NULL)));
+ assert_se((host = gethostname_malloc()));
+
+#define expect(src, pattern, result) \
+ do { \
+ _cleanup_free_ char *t = install_full_printf(&src, pattern); \
+ _cleanup_free_ char \
+ *d1 = strdup(i.name), \
+ *d2 = strdup(i.path), \
+ *d3 = strdup(i.user); \
+ memzero(i.name, strlen(i.name)); \
+ memzero(i.path, strlen(i.path)); \
+ memzero(i.user, strlen(i.user)); \
+ assert(d1 && d2 && d3); \
+ if (result) { \
+ printf("%s\n", t); \
+ assert(streq(t, result)); \
+ } else assert(t == NULL); \
+ strcpy(i.name, d1); \
+ strcpy(i.path, d2); \
+ strcpy(i.user, d3); \
+ } while(false)
+
+ assert_se(setenv("USER", "root", 1) == 0);
+
+ expect(i, "%n", "name.service");
+ expect(i, "%N", "name");
+ expect(i, "%p", "name");
+ expect(i, "%i", "");
+ expect(i, "%u", "xxxx-no-such-user");
+ expect(i, "%U", NULL);
+ expect(i, "%m", mid);
+ expect(i, "%b", bid);
+ expect(i, "%H", host);
+
+ expect(i2, "%u", "root");
+ expect(i2, "%U", "0");
+
+ expect(i3, "%n", "name@inst.service");
+ expect(i3, "%N", "name@inst");
+ expect(i3, "%p", "name");
+ expect(i3, "%u", "xxxx-no-such-user");
+ expect(i3, "%U", NULL);
+ expect(i3, "%m", mid);
+ expect(i3, "%b", bid);
+ expect(i3, "%H", host);
+
+ expect(i4, "%u", "root");
+ expect(i4, "%U", "0");
+}
+#pragma GCC diagnostic pop
+
int main(int argc, char *argv[]) {
+ log_parse_environment();
+ log_open();
+
test_unit_file_get_set();
test_config_parse_exec();
+ test_load_env_file_1();
+ test_load_env_file_2();
+ test_load_env_file_3();
+ test_load_env_file_4();
+ test_install_printf();
return 0;
}
diff --git a/src/test/test-unit-name.c b/src/test/test-unit-name.c
index 50187e1129..86cb2b8da6 100644
--- a/src/test/test-unit-name.c
+++ b/src/test/test-unit-name.c
@@ -4,6 +4,7 @@
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
@@ -22,156 +23,177 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <sys/types.h>
+#include <pwd.h>
+#include "manager.h"
+#include "unit.h"
#include "unit-name.h"
+#include "unit-printf.h"
+#include "install.h"
+#include "specifier.h"
#include "util.h"
+#include "macro.h"
+
+static void test_replacements(void) {
+#define expect(pattern, repl, expected) \
+ { \
+ _cleanup_free_ char *t = \
+ unit_name_replace_instance(pattern, repl); \
+ puts(t); \
+ assert(streq(t, expected)); \
+ }
+
+ expect("foo@.service", "waldo", "foo@waldo.service");
+ expect("foo@xyz.service", "waldo", "foo@waldo.service");
+ expect("xyz", "waldo", "xyz");
+ expect("", "waldo", "");
+ expect("foo.service", "waldo", "foo.service");
+ expect(".service", "waldo", ".service");
+ expect("foo@", "waldo", "foo@waldo");
+ expect("@bar", "waldo", "@waldo");
+
+ puts("-------------------------------------------------");
+#undef expect
+#define expect(path, suffix, expected) \
+ { \
+ _cleanup_free_ char *k, *t = \
+ unit_name_from_path(path, suffix); \
+ puts(t); \
+ k = unit_name_to_path(t); \
+ puts(k); \
+ assert(streq(k, expected ? expected : path)); \
+ }
+
+ expect("/waldo", ".mount", NULL);
+ expect("/waldo/quuix", ".mount", NULL);
+ expect("/waldo/quuix/", ".mount", "/waldo/quuix");
+ expect("/", ".mount", NULL);
+ expect("///", ".mount", "/");
+
+ puts("-------------------------------------------------");
+#undef expect
+#define expect(pattern, path, suffix, expected) \
+ { \
+ _cleanup_free_ char *t = \
+ unit_name_from_path_instance(pattern, path, suffix); \
+ puts(t); \
+ assert(streq(t, expected)); \
+ }
+
+ expect("waldo", "/waldo", ".mount", "waldo@waldo.mount");
+ expect("waldo", "/waldo////quuix////", ".mount", "waldo@waldo-quuix.mount");
+ expect("waldo", "/", ".mount", "waldo@-.mount");
+ expect("wa--ldo", "/--", ".mount", "wa--ldo@\\x2d\\x2d.mount");
+
+ puts("-------------------------------------------------");
+#undef expect
+#define expect(pattern) \
+ { \
+ _cleanup_free_ char *k, *t; \
+ assert_se(t = unit_name_mangle(pattern)); \
+ assert_se(k = unit_name_mangle(t)); \
+ puts(t); \
+ assert_se(streq(t, k)); \
+ }
+
+ expect("/home");
+ expect("/dev/sda");
+ expect("üxknürz.service");
+ expect("foobar-meh...waldi.service");
+ expect("_____####----.....service");
+ expect("_____##@;;;,,,##----.....service");
+ expect("xxx@@@@/////\\\\\\\\\\yyy.service");
+
+#undef expect
+}
-int main(int argc, char* argv[]) {
- char *t, *k;
-
- t = unit_name_replace_instance("foo@.service", "waldo");
- puts(t);
- free(t);
-
- t = unit_name_replace_instance("foo@xyz.service", "waldo");
- puts(t);
- free(t);
-
- t = unit_name_replace_instance("xyz", "waldo");
- puts(t);
- free(t);
-
- t = unit_name_replace_instance("", "waldo");
- puts(t);
- free(t);
-
- t = unit_name_replace_instance("", "");
- puts(t);
- free(t);
-
- t = unit_name_replace_instance("foo.service", "waldo");
- puts(t);
- free(t);
-
- t = unit_name_replace_instance(".service", "waldo");
- puts(t);
- free(t);
-
- t = unit_name_replace_instance("foo@bar", "waldo");
- puts(t);
- free(t);
-
- t = unit_name_replace_instance("foo@", "waldo");
- puts(t);
- free(t);
-
- t = unit_name_replace_instance("@", "waldo");
- puts(t);
- free(t);
-
- t = unit_name_replace_instance("@bar", "waldo");
- puts(t);
- free(t);
-
- t = unit_name_from_path("/waldo", ".mount");
- puts(t);
- k = unit_name_to_path(t);
- puts(k);
- free(k);
- free(t);
-
- t = unit_name_from_path("/waldo/quuix", ".mount");
- puts(t);
- k = unit_name_to_path(t);
- puts(k);
- free(k);
- free(t);
-
- t = unit_name_from_path("/waldo/quuix/", ".mount");
- puts(t);
- k = unit_name_to_path(t);
- puts(k);
- free(k);
- free(t);
-
- t = unit_name_from_path("/", ".mount");
- puts(t);
- k = unit_name_to_path(t);
- puts(k);
- free(k);
- free(t);
-
- t = unit_name_from_path("///", ".mount");
- puts(t);
- k = unit_name_to_path(t);
- puts(k);
- free(k);
- free(t);
-
- t = unit_name_from_path_instance("waldo", "/waldo", ".mount");
- puts(t);
- free(t);
-
- t = unit_name_from_path_instance("waldo", "/waldo////quuix////", ".mount");
- puts(t);
- free(t);
-
- t = unit_name_from_path_instance("waldo", "/", ".mount");
- puts(t);
- free(t);
-
- t = unit_name_from_path_instance("wa--ldo", "/--", ".mount");
- puts(t);
- free(t);
-
- assert_se(t = unit_name_mangle("/home"));
- assert_se(k = unit_name_mangle(t));
- puts(t);
- assert_se(streq(t, k));
- free(t);
- free(k);
-
- assert_se(t = unit_name_mangle("/dev/sda"));
- assert_se(k = unit_name_mangle(t));
- puts(t);
- assert_se(streq(t, k));
- free(t);
- free(k);
-
- assert_se(t = unit_name_mangle("üxknürz.service"));
- assert_se(k = unit_name_mangle(t));
- puts(t);
- assert_se(streq(t, k));
- free(t);
- free(k);
-
- assert_se(t = unit_name_mangle("foobar-meh...waldi.service"));
- assert_se(k = unit_name_mangle(t));
- puts(t);
- assert_se(streq(t, k));
- free(t);
- free(k);
-
- assert_se(t = unit_name_mangle("_____####----.....service"));
- assert_se(k = unit_name_mangle(t));
- puts(t);
- assert_se(streq(t, k));
- free(t);
- free(k);
-
- assert_se(t = unit_name_mangle("_____##@;;;,,,##----.....service"));
- assert_se(k = unit_name_mangle(t));
- puts(t);
- assert_se(streq(t, k));
- free(t);
- free(k);
-
- assert_se(t = unit_name_mangle("xxx@@@@/////\\\\\\\\\\yyy.service"));
- assert_se(k = unit_name_mangle(t));
- puts(t);
- assert_se(streq(t, k));
- free(t);
- free(k);
+static int test_unit_printf(void) {
+ Manager *m;
+ Unit *u, *u2;
+ int r;
+
+ _cleanup_free_ char *mid, *bid, *host, *root_uid;
+ struct passwd *root;
+
+ assert_se((mid = specifier_machine_id('m', NULL, NULL)));
+ assert_se((bid = specifier_boot_id('b', NULL, NULL)));
+ assert_se((host = gethostname_malloc()));
+
+ assert_se((root = getpwnam("root")));
+ assert_se(asprintf(&root_uid, "%d", (int) root->pw_uid) > 0);
+
+ r = manager_new(SYSTEMD_USER, &m);
+ if (r == -EPERM) {
+ puts("manager_new: Permission denied. Skipping test.");
+ return EXIT_TEST_SKIP;
+ }
+ assert(r == 0);
+
+#define expect(unit, pattern, expected) \
+ { \
+ char *e; \
+ _cleanup_free_ char *t = \
+ unit_full_printf(unit, pattern); \
+ printf("result: %s\nexpect: %s\n", t, expected); \
+ if ((e = endswith(expected, "*"))) \
+ assert(strncmp(t, e, e-expected)); \
+ else \
+ assert(streq(t, expected)); \
+ }
+
+ assert_se(setenv("USER", "root", 1) == 0);
+ assert_se(setenv("HOME", "/root", 1) == 0);
+
+ assert_se(u = unit_new(m, sizeof(Service)));
+ assert_se(unit_add_name(u, "blah.service") == 0);
+ assert_se(unit_add_name(u, "blah.service") == 0);
+
+ /* general tests */
+ expect(u, "%%", "%");
+ expect(u, "%%s", "%s");
+ expect(u, "%", ""); // REALLY?
+
+ /* normal unit */
+ expect(u, "%n", "blah.service");
+ expect(u, "%N", "blah");
+ expect(u, "%p", "blah");
+ expect(u, "%P", "blah");
+ expect(u, "%i", "");
+ expect(u, "%I", "");
+ expect(u, "%u", root->pw_name);
+ expect(u, "%U", root_uid);
+ expect(u, "%h", root->pw_dir);
+ expect(u, "%s", "/bin/sh");
+ expect(u, "%m", mid);
+ expect(u, "%b", bid);
+ expect(u, "%H", host);
+ expect(u, "%t", "/run/user/*");
+
+ /* templated */
+ assert_se(u2 = unit_new(m, sizeof(Service)));
+ assert_se(unit_add_name(u2, "blah@foo-foo.service") == 0);
+ assert_se(unit_add_name(u2, "blah@foo-foo.service") == 0);
+
+ expect(u2, "%n", "blah@foo-foo.service");
+ expect(u2, "%N", "blah@foo-foo");
+ expect(u2, "%p", "blah");
+ expect(u2, "%P", "blah");
+ expect(u2, "%i", "foo-foo");
+ expect(u2, "%I", "foo/foo");
+ expect(u2, "%u", root->pw_name);
+ expect(u2, "%U", root_uid);
+ expect(u2, "%h", root->pw_dir);
+ expect(u2, "%s", "/bin/sh");
+ expect(u2, "%m", mid);
+ expect(u2, "%b", bid);
+ expect(u2, "%H", host);
+ expect(u2, "%t", "/run/user/*");
return 0;
}
+
+int main(int argc, char* argv[]) {
+ test_replacements();
+ return test_unit_printf();
+}
diff --git a/src/test/test-util.c b/src/test/test-util.c
new file mode 100644
index 0000000000..4c3a8a6b88
--- /dev/null
+++ b/src/test/test-util.c
@@ -0,0 +1,472 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 Lennart Poettering
+ Copyright 2013 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
+ 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 <http://www.gnu.org/licenses/>.
+***/
+
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <locale.h>
+#include <errno.h>
+
+#include "util.h"
+
+static void test_streq_ptr(void) {
+ assert_se(streq_ptr(NULL, NULL));
+ assert_se(!streq_ptr("abc", "cdef"));
+}
+
+static void test_first_word(void) {
+ assert_se(first_word("Hello", ""));
+ assert_se(first_word("Hello", "Hello"));
+ assert_se(first_word("Hello world", "Hello"));
+ assert_se(first_word("Hello\tworld", "Hello"));
+ assert_se(first_word("Hello\nworld", "Hello"));
+ assert_se(first_word("Hello\rworld", "Hello"));
+ assert_se(first_word("Hello ", "Hello"));
+
+ assert_se(!first_word("Hello", "Hellooo"));
+ assert_se(!first_word("Hello", "xxxxx"));
+ assert_se(!first_word("Hellooo", "Hello"));
+}
+
+static void test_close_many(void) {
+ int fds[3];
+ char name0[] = "/tmp/test-close-many.XXXXXX";
+ char name1[] = "/tmp/test-close-many.XXXXXX";
+ char name2[] = "/tmp/test-close-many.XXXXXX";
+
+ fds[0] = mkstemp(name0);
+ fds[1] = mkstemp(name1);
+ fds[2] = mkstemp(name2);
+
+ close_many(fds, 2);
+
+ assert_se(fcntl(fds[0], F_GETFD) == -1);
+ assert_se(fcntl(fds[1], F_GETFD) == -1);
+ assert_se(fcntl(fds[2], F_GETFD) >= 0);
+
+ close_nointr_nofail(fds[2]);
+
+ unlink(name0);
+ unlink(name1);
+ unlink(name2);
+}
+
+static void test_parse_boolean(void) {
+ assert_se(parse_boolean("1") == 1);
+ assert_se(parse_boolean("y") == 1);
+ assert_se(parse_boolean("Y") == 1);
+ assert_se(parse_boolean("yes") == 1);
+ assert_se(parse_boolean("YES") == 1);
+ assert_se(parse_boolean("true") == 1);
+ assert_se(parse_boolean("TRUE") == 1);
+ assert_se(parse_boolean("on") == 1);
+ assert_se(parse_boolean("ON") == 1);
+
+ assert_se(parse_boolean("0") == 0);
+ assert_se(parse_boolean("n") == 0);
+ assert_se(parse_boolean("N") == 0);
+ assert_se(parse_boolean("no") == 0);
+ assert_se(parse_boolean("NO") == 0);
+ assert_se(parse_boolean("false") == 0);
+ assert_se(parse_boolean("FALSE") == 0);
+ assert_se(parse_boolean("off") == 0);
+ assert_se(parse_boolean("OFF") == 0);
+
+ assert_se(parse_boolean("garbage") < 0);
+ assert_se(parse_boolean("") < 0);
+}
+
+static void test_parse_pid(void) {
+ int r;
+ pid_t pid;
+
+ r = parse_pid("100", &pid);
+ assert_se(r == 0);
+ assert_se(pid == 100);
+
+ r = parse_pid("0x7FFFFFFF", &pid);
+ assert_se(r == 0);
+ assert_se(pid == 2147483647);
+
+ pid = 65; /* pid is left unchanged on ERANGE. Set to known arbitrary value. */
+ r = parse_pid("0", &pid);
+ assert_se(r == -ERANGE);
+ assert_se(pid == 65);
+
+ pid = 65; /* pid is left unchanged on ERANGE. Set to known arbitrary value. */
+ r = parse_pid("-100", &pid);
+ assert_se(r == -ERANGE);
+ assert_se(pid == 65);
+
+ pid = 65; /* pid is left unchanged on ERANGE. Set to known arbitrary value. */
+ r = parse_pid("0xFFFFFFFFFFFFFFFFF", &pid);
+ assert(r == -ERANGE);
+ assert_se(pid == 65);
+}
+
+static void test_parse_uid(void) {
+ int r;
+ uid_t uid;
+
+ r = parse_uid("100", &uid);
+ assert_se(r == 0);
+ assert_se(uid == 100);
+}
+
+static void test_safe_atolli(void) {
+ int r;
+ long long l;
+
+ r = safe_atolli("12345", &l);
+ assert_se(r == 0);
+ assert_se(l == 12345);
+
+ r = safe_atolli("junk", &l);
+ assert_se(r == -EINVAL);
+}
+
+static void test_safe_atod(void) {
+ int r;
+ double d;
+ char *e;
+
+ r = safe_atod("junk", &d);
+ assert_se(r == -EINVAL);
+
+ r = safe_atod("0.2244", &d);
+ assert_se(r == 0);
+ assert_se(abs(d - 0.2244) < 0.000001);
+
+ r = safe_atod("0,5", &d);
+ assert_se(r == -EINVAL);
+
+ errno = 0;
+ strtod("0,5", &e);
+ assert_se(*e == ',');
+
+ /* Check if this really is locale independent */
+ setlocale(LC_NUMERIC, "de_DE.utf8");
+
+ r = safe_atod("0.2244", &d);
+ assert_se(r == 0);
+ assert_se(abs(d - 0.2244) < 0.000001);
+
+ r = safe_atod("0,5", &d);
+ assert_se(r == -EINVAL);
+
+ errno = 0;
+ assert_se(abs(strtod("0,5", &e) - 0.5) < 0.00001);
+
+ /* And check again, reset */
+ setlocale(LC_NUMERIC, "C");
+
+ r = safe_atod("0.2244", &d);
+ assert_se(r == 0);
+ assert_se(abs(d - 0.2244) < 0.000001);
+
+ r = safe_atod("0,5", &d);
+ assert_se(r == -EINVAL);
+
+ errno = 0;
+ strtod("0,5", &e);
+ assert_se(*e == ',');
+}
+
+static void test_strappend(void) {
+ _cleanup_free_ char *t1, *t2, *t3, *t4;
+
+ t1 = strappend(NULL, NULL);
+ assert_se(streq(t1, ""));
+
+ t2 = strappend(NULL, "suf");
+ assert_se(streq(t2, "suf"));
+
+ t3 = strappend("pre", NULL);
+ assert_se(streq(t3, "pre"));
+
+ t4 = strappend("pre", "suf");
+ assert_se(streq(t4, "presuf"));
+}
+
+static void test_strstrip(void) {
+ char *r;
+ char input[] = " hello, waldo. ";
+
+ r = strstrip(input);
+ assert_se(streq(r, "hello, waldo."));
+
+}
+
+static void test_delete_chars(void) {
+ char *r;
+ char input[] = " hello, waldo. abc";
+
+ r = delete_chars(input, WHITESPACE);
+ assert_se(streq(r, "hello,waldo.abc"));
+}
+
+static void test_in_charset(void) {
+ assert_se(in_charset("dddaaabbbcccc", "abcd"));
+ assert_se(!in_charset("dddaaabbbcccc", "abc f"));
+}
+
+static void test_hexchar(void) {
+ assert_se(hexchar(0xa) == 'a');
+ assert_se(hexchar(0x0) == '0');
+}
+
+static void test_unhexchar(void) {
+ assert_se(unhexchar('a') == 0xA);
+ assert_se(unhexchar('A') == 0xA);
+ assert_se(unhexchar('0') == 0x0);
+}
+
+static void test_octchar(void) {
+ assert_se(octchar(00) == '0');
+ assert_se(octchar(07) == '7');
+}
+
+static void test_unoctchar(void) {
+ assert_se(unoctchar('0') == 00);
+ assert_se(unoctchar('7') == 07);
+}
+
+static void test_decchar(void) {
+ assert_se(decchar(0) == '0');
+ assert_se(decchar(9) == '9');
+}
+
+static void test_undecchar(void) {
+ assert_se(undecchar('0') == 0);
+ assert_se(undecchar('9') == 9);
+}
+
+static void test_foreach_word(void) {
+ char *w, *state;
+ size_t l;
+ int i = 0;
+ const char test[] = "test abc d\te f ";
+ const char * const expected[] = {
+ "test",
+ "abc",
+ "d",
+ "e",
+ "f",
+ "",
+ NULL
+ };
+
+ FOREACH_WORD(w, l, test, state) {
+ assert_se(strneq(expected[i++], w, l));
+ }
+}
+
+static void test_foreach_word_quoted(void) {
+ char *w, *state;
+ size_t l;
+ int i = 0;
+ const char test[] = "test a b c 'd' e '' '' hhh '' '' \"a b c\"";
+ const char * const expected[] = {
+ "test",
+ "a",
+ "b",
+ "c",
+ "d",
+ "e",
+ "",
+ "",
+ "hhh",
+ "",
+ "",
+ "a b c",
+ NULL
+ };
+
+ printf("<%s>\n", test);
+ FOREACH_WORD_QUOTED(w, l, test, state) {
+ _cleanup_free_ char *t = NULL;
+
+ assert_se(t = strndup(w, l));
+ assert_se(strneq(expected[i++], w, l));
+ printf("<%s>\n", t);
+ }
+}
+
+static void test_default_term_for_tty(void) {
+ puts(default_term_for_tty("/dev/tty23"));
+ puts(default_term_for_tty("/dev/ttyS23"));
+ puts(default_term_for_tty("/dev/tty0"));
+ puts(default_term_for_tty("/dev/pty0"));
+ puts(default_term_for_tty("/dev/pts/0"));
+ puts(default_term_for_tty("/dev/console"));
+ puts(default_term_for_tty("tty23"));
+ puts(default_term_for_tty("ttyS23"));
+ puts(default_term_for_tty("tty0"));
+ puts(default_term_for_tty("pty0"));
+ puts(default_term_for_tty("pts/0"));
+ puts(default_term_for_tty("console"));
+}
+
+static void test_memdup_multiply(void) {
+ int org[] = {1, 2, 3};
+ int *dup;
+
+ dup = (int*)memdup_multiply(org, sizeof(int), 3);
+
+ assert_se(dup);
+ assert_se(dup[0] == 1);
+ assert_se(dup[1] == 2);
+ assert_se(dup[2] == 3);
+ free(dup);
+}
+
+static void test_bus_path_escape_one(const char *a, const char *b) {
+ _cleanup_free_ char *t = NULL, *x = NULL, *y = NULL;
+
+ assert_se(t = bus_path_escape(a));
+ assert_se(streq(t, b));
+
+ assert_se(x = bus_path_unescape(t));
+ assert_se(streq(a, x));
+
+ assert_se(y = bus_path_unescape(b));
+ assert_se(streq(a, y));
+}
+
+static void test_bus_path_escape(void) {
+ test_bus_path_escape_one("foo123bar", "foo123bar");
+ test_bus_path_escape_one("foo.bar", "foo_2ebar");
+ test_bus_path_escape_one("foo_2ebar", "foo_5f2ebar");
+ test_bus_path_escape_one("", "_");
+ test_bus_path_escape_one("_", "_5f");
+ test_bus_path_escape_one("1", "_31");
+ test_bus_path_escape_one(":1", "_3a1");
+}
+
+static void test_hostname_is_valid(void) {
+ assert(hostname_is_valid("foobar"));
+ assert(hostname_is_valid("foobar.com"));
+ assert(!hostname_is_valid("fööbar"));
+ assert(!hostname_is_valid(""));
+ assert(!hostname_is_valid("."));
+ assert(!hostname_is_valid(".."));
+ assert(!hostname_is_valid("foobar."));
+ assert(!hostname_is_valid(".foobar"));
+ assert(!hostname_is_valid("foo..bar"));
+ assert(!hostname_is_valid("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"));
+}
+
+static void test_u64log2(void) {
+ assert(u64log2(0) == 0);
+ assert(u64log2(8) == 3);
+ assert(u64log2(9) == 3);
+ assert(u64log2(15) == 3);
+ assert(u64log2(16) == 4);
+ assert(u64log2(1024*1024) == 20);
+ assert(u64log2(1024*1024+5) == 20);
+}
+
+static void test_get_process_comm(void) {
+ _cleanup_free_ char *a = NULL, *c = NULL, *d = NULL, *f = NULL, *i = NULL;
+ unsigned long long b;
+ pid_t e;
+ uid_t u;
+ gid_t g;
+ dev_t h;
+ int r;
+
+ assert_se(get_process_comm(1, &a) >= 0);
+ log_info("pid1 comm: '%s'", a);
+
+ assert_se(get_starttime_of_pid(1, &b) >= 0);
+ log_info("pid1 starttime: '%llu'", b);
+
+ assert_se(get_process_cmdline(1, 0, true, &c) >= 0);
+ log_info("pid1 cmdline: '%s'", c);
+
+ assert_se(get_process_cmdline(1, 8, false, &d) >= 0);
+ log_info("pid1 cmdline truncated: '%s'", d);
+
+ assert_se(get_parent_of_pid(1, &e) >= 0);
+ log_info("pid1 ppid: '%llu'", (unsigned long long) e);
+ assert_se(e == 0);
+
+ assert_se(is_kernel_thread(1) == 0);
+
+ r = get_process_exe(1, &f);
+ assert_se(r >= 0 || r == -EACCES);
+ log_info("pid1 exe: '%s'", strna(f));
+
+ assert_se(get_process_uid(1, &u) == 0);
+ log_info("pid1 uid: '%llu'", (unsigned long long) u);
+ assert_se(u == 0);
+
+ assert_se(get_process_gid(1, &g) == 0);
+ log_info("pid1 gid: '%llu'", (unsigned long long) g);
+ assert_se(g == 0);
+
+ assert(get_ctty_devnr(1, &h) == -ENOENT);
+
+ getenv_for_pid(1, "PATH", &i);
+ log_info("pid1 $PATH: '%s'", strna(i));
+}
+
+static void test_protect_errno(void) {
+ errno = 12;
+ {
+ PROTECT_ERRNO;
+ errno = 11;
+ }
+ assert(errno == 12);
+}
+
+int main(int argc, char *argv[]) {
+ test_streq_ptr();
+ test_first_word();
+ test_close_many();
+ test_parse_boolean();
+ test_parse_pid();
+ test_parse_uid();
+ test_safe_atolli();
+ test_safe_atod();
+ test_strappend();
+ test_strstrip();
+ test_delete_chars();
+ test_in_charset();
+ test_hexchar();
+ test_unhexchar();
+ test_octchar();
+ test_unoctchar();
+ test_decchar();
+ test_undecchar();
+ test_foreach_word();
+ test_foreach_word_quoted();
+ test_default_term_for_tty();
+ test_memdup_multiply();
+ test_bus_path_escape();
+ test_hostname_is_valid();
+ test_u64log2();
+ test_get_process_comm();
+ test_protect_errno();
+
+ return 0;
+}
diff --git a/src/timedate/timedatectl.c b/src/timedate/timedatectl.c
index 281c0524da..8d4e560b93 100644
--- a/src/timedate/timedatectl.c
+++ b/src/timedate/timedatectl.c
@@ -51,7 +51,7 @@ static void pager_open_if_enabled(void) {
if (arg_no_pager)
return;
- pager_open();
+ pager_open(false);
}
static void polkit_agent_open_if_enabled(void) {
@@ -68,12 +68,12 @@ typedef struct StatusInfo {
const char *timezone;
bool local_rtc;
bool ntp;
+ bool can_ntp;
} StatusInfo;
static bool ntp_synced(void) {
- struct timex txc;
+ struct timex txc = {};
- zero(txc);
if (adjtimex(&txc) < 0)
return false;
@@ -153,7 +153,7 @@ static void print_status_info(StatusInfo *i) {
" RTC in local TZ: %s\n",
strna(i->timezone),
a,
- yes_no(i->ntp),
+ i->can_ntp ? yes_no(i->ntp) : "n/a",
yes_no(ntp_synced()),
yes_no(i->local_rtc));
@@ -228,6 +228,8 @@ static int status_property(const char *name, DBusMessageIter *iter, StatusInfo *
i->local_rtc = b;
else if (streq(name, "NTP"))
i->ntp = b;
+ else if (streq(name, "CanNTP"))
+ i->can_ntp = b;
}
}
@@ -239,7 +241,7 @@ static int show_status(DBusConnection *bus, char **args, unsigned n) {
const char *interface = "";
int r;
DBusMessageIter iter, sub, sub2, sub3;
- StatusInfo info;
+ StatusInfo info = {};
assert(args);
@@ -263,7 +265,6 @@ static int show_status(DBusConnection *bus, char **args, unsigned n) {
return -EIO;
}
- zero(info);
dbus_message_iter_recurse(&iter, &sub);
while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
@@ -425,7 +426,6 @@ static int list_timezones(DBusConnection *bus, char **args, unsigned n) {
_cleanup_fclose_ FILE *f = NULL;
_cleanup_strv_free_ char **zones = NULL;
size_t n_zones = 0;
- char **i;
assert(args);
assert(n == 1);
@@ -487,8 +487,7 @@ static int list_timezones(DBusConnection *bus, char **args, unsigned n) {
pager_open_if_enabled();
strv_sort(zones);
- STRV_FOREACH(i, zones)
- puts(*i);
+ strv_print(zones);
return 0;
}
diff --git a/src/timedate/timedated.c b/src/timedate/timedated.c
index fdb4335464..cdb6e5b16c 100644
--- a/src/timedate/timedated.c
+++ b/src/timedate/timedated.c
@@ -35,6 +35,8 @@
#include "hwclock.h"
#include "conf-files.h"
#include "path-util.h"
+#include "fileio-label.h"
+#include "label.h"
#define NULL_ADJTIME_UTC "0.0 0 0\n0\nUTC\n"
#define NULL_ADJTIME_LOCAL "0.0 0 0\n0\nLOCAL\n"
@@ -43,6 +45,7 @@
" <interface name=\"org.freedesktop.timedate1\">\n" \
" <property name=\"Timezone\" type=\"s\" access=\"read\"/>\n" \
" <property name=\"LocalRTC\" type=\"b\" access=\"read\"/>\n" \
+ " <property name=\"CanNTP\" type=\"b\" access=\"read\"/>\n" \
" <property name=\"NTP\" type=\"b\" access=\"read\"/>\n" \
" <method name=\"SetTime\">\n" \
" <arg name=\"usec_utc\" type=\"x\" direction=\"in\"/>\n" \
@@ -82,10 +85,14 @@ const char timedate_interface[] _introspect_("timedate1") = INTERFACE;
typedef struct TZ {
char *zone;
bool local_rtc;
+ int can_ntp;
int use_ntp;
} TZ;
static TZ tz = {
+ .zone = NULL,
+ .local_rtc = false,
+ .can_ntp = -1,
.use_ntp = -1,
};
@@ -176,14 +183,6 @@ static int read_data(void) {
}
}
-#ifdef HAVE_DEBIAN
- r = read_one_line_file("/etc/timezone", &tz.zone);
- if (r < 0) {
- if (r != -ENOENT)
- log_warning("Failed to read /etc/timezone: %s", strerror(-r));
- }
-#endif
-
have_timezone:
if (isempty(tz.zone)) {
free(tz.zone);
@@ -219,7 +218,7 @@ static int write_data_timezone(void) {
static int write_data_local_rtc(void) {
int r;
- char *s, *w;
+ _cleanup_free_ char *s = NULL, *w = NULL;
r = read_full_file("/etc/adjtime", &s, NULL);
if (r < 0) {
@@ -237,58 +236,45 @@ static int write_data_local_rtc(void) {
size_t a, b;
p = strchr(s, '\n');
- if (!p) {
- free(s);
+ if (!p)
return -EIO;
- }
p = strchr(p+1, '\n');
- if (!p) {
- free(s);
+ if (!p)
return -EIO;
- }
p++;
e = strchr(p, '\n');
- if (!e) {
- free(s);
+ if (!e)
return -EIO;
- }
a = p - s;
b = strlen(e);
w = new(char, a + (tz.local_rtc ? 5 : 3) + b + 1);
- if (!w) {
- free(s);
+ if (!w)
return -ENOMEM;
- }
*(char*) mempcpy(stpcpy(mempcpy(w, s, a), tz.local_rtc ? "LOCAL" : "UTC"), e, b) = 0;
if (streq(w, NULL_ADJTIME_UTC)) {
- free(w);
-
- if (unlink("/etc/adjtime") < 0) {
+ if (unlink("/etc/adjtime") < 0)
if (errno != ENOENT)
return -errno;
- }
return 0;
}
}
-
- r = write_one_line_file_atomic("/etc/adjtime", w);
- free(w);
-
- return r;
+ label_init("/etc");
+ return write_string_file_atomic_label("/etc/adjtime", w);
}
static char** get_ntp_services(void) {
- char **r = NULL, **files, **i;
+ _cleanup_strv_free_ char **r = NULL, **files;
+ char **i;
int k;
- k = conf_files_list(&files, ".list",
+ k = conf_files_list(&files, ".list", NULL,
"/etc/systemd/ntp-units.d",
"/run/systemd/ntp-units.d",
"/usr/local/lib/systemd/ntp-units.d",
@@ -298,14 +284,14 @@ static char** get_ntp_services(void) {
return NULL;
STRV_FOREACH(i, files) {
- FILE *f;
+ _cleanup_fclose_ FILE *f;
f = fopen(*i, "re");
if (!f)
continue;
for (;;) {
- char line[PATH_MAX], *l, **q;
+ char line[PATH_MAX], *l;
if (!fgets(line, sizeof(line), f)) {
@@ -319,22 +305,17 @@ static char** get_ntp_services(void) {
if (l[0] == 0 || l[0] == '#')
continue;
- q = strv_append(r, l);
- if (!q) {
+ if (strv_extend(&r, l) < 0) {
log_oom();
- break;
+ return NULL;
}
-
- strv_free(r);
- r = q;
}
-
- fclose(f);
}
- strv_free(files);
+ i = r;
+ r = NULL; /* avoid cleanup */
- return strv_uniq(r);
+ return strv_uniq(i);
}
static int read_ntp(DBusConnection *bus) {
@@ -370,8 +351,6 @@ static int read_ntp(DBusConnection *bus) {
goto finish;
}
- if (reply)
- dbus_message_unref(reply);
reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
if (!reply) {
if (streq(error.name, "org.freedesktop.DBus.Error.FileNotFound")) {
@@ -393,6 +372,7 @@ static int read_ntp(DBusConnection *bus) {
goto finish;
}
+ tz.can_ntp = 1;
tz.use_ntp =
streq(s, "enabled") ||
streq(s, "enabled-runtime");
@@ -401,6 +381,7 @@ static int read_ntp(DBusConnection *bus) {
}
/* NTP is not installed. */
+ tz.can_ntp = 0;
tz.use_ntp = 0;
r = 0;
@@ -451,8 +432,6 @@ static int start_ntp(DBusConnection *bus, DBusError *error) {
goto finish;
}
- if (reply)
- dbus_message_unref(reply);
reply = dbus_connection_send_with_reply_and_block(bus, m, -1, error);
if (!reply) {
if (streq(error->name, "org.freedesktop.DBus.Error.FileNotFound") ||
@@ -541,8 +520,6 @@ static int enable_ntp(DBusConnection *bus, DBusError *error) {
}
}
- if (reply)
- dbus_message_unref(reply);
reply = dbus_connection_send_with_reply_and_block(bus, m, -1, error);
if (!reply) {
if (streq(error->name, "org.freedesktop.DBus.Error.FileNotFound")) {
@@ -594,6 +571,20 @@ finish:
return r;
}
+static int property_append_can_ntp(DBusMessageIter *i, const char *property, void *data) {
+ dbus_bool_t db;
+
+ assert(i);
+ assert(property);
+
+ db = tz.can_ntp > 0;
+
+ if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &db))
+ return -ENOMEM;
+
+ return 0;
+}
+
static int property_append_ntp(DBusMessageIter *i, const char *property, void *data) {
dbus_bool_t db;
@@ -611,6 +602,7 @@ static int property_append_ntp(DBusMessageIter *i, const char *property, void *d
static const BusProperty bus_timedate_properties[] = {
{ "Timezone", bus_property_append_string, "s", offsetof(TZ, zone), true },
{ "LocalRTC", bus_property_append_bool, "b", offsetof(TZ, local_rtc) },
+ { "CanNTP", property_append_can_ntp, "b", offsetof(TZ, can_ntp) },
{ "NTP", property_append_ntp, "b", offsetof(TZ, use_ntp) },
{ NULL, }
};
@@ -802,15 +794,24 @@ static DBusHandlerResult timedate_message_handler(
struct timespec ts;
struct tm* tm;
+ if (relative) {
+ usec_t n, x;
+
+ n = now(CLOCK_REALTIME);
+ x = n + utc;
+
+ if ((utc > 0 && x < n) ||
+ (utc < 0 && x > n))
+ return bus_send_error_reply(connection, message, NULL, -EOVERFLOW);
+
+ timespec_store(&ts, x);
+ } else
+ timespec_store(&ts, (usec_t) utc);
+
r = verify_polkit(connection, message, "org.freedesktop.timedate1.set-time", interactive, NULL, &error);
if (r < 0)
return bus_send_error_reply(connection, message, &error, r);
- if (relative)
- timespec_store(&ts, now(CLOCK_REALTIME) + utc);
- else
- timespec_store(&ts, utc);
-
/* Set system clock */
if (clock_settime(CLOCK_REALTIME, &ts) < 0) {
log_error("Failed to set local time: %m");
@@ -875,7 +876,7 @@ static DBusHandlerResult timedate_message_handler(
if (!(reply = dbus_message_new_method_return(message)))
goto oom;
- if (!dbus_connection_send(connection, reply, NULL))
+ if (!bus_maybe_send_reply(connection, message, reply))
goto oom;
dbus_message_unref(reply);
diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c
index d8fb07e59a..f4885ec942 100644
--- a/src/tmpfiles/tmpfiles.c
+++ b/src/tmpfiles/tmpfiles.c
@@ -43,6 +43,7 @@
#include "log.h"
#include "util.h"
#include "macro.h"
+#include "missing.h"
#include "mkdir.h"
#include "path-util.h"
#include "strv.h"
@@ -70,6 +71,7 @@ typedef enum ItemType {
/* These ones take globs */
IGNORE_PATH = 'x',
+ IGNORE_DIRECTORY_PATH = 'X',
REMOVE_PATH = 'r',
RECURSIVE_REMOVE_PATH = 'R',
RELABEL_PATH = 'z',
@@ -105,21 +107,20 @@ static bool arg_remove = false;
static const char *arg_prefix = NULL;
-static const char * const conf_file_dirs[] = {
- "/etc/tmpfiles.d",
- "/run/tmpfiles.d",
- "/usr/local/lib/tmpfiles.d",
- "/usr/lib/tmpfiles.d",
+static const char conf_file_dirs[] =
+ "/etc/tmpfiles.d\0"
+ "/run/tmpfiles.d\0"
+ "/usr/local/lib/tmpfiles.d\0"
+ "/usr/lib/tmpfiles.d\0"
#ifdef HAVE_SPLIT_USR
- "/lib/tmpfiles.d",
+ "/lib/tmpfiles.d\0"
#endif
- NULL
-};
+ ;
#define MAX_DEPTH 256
static bool needs_glob(ItemType t) {
- return t == IGNORE_PATH || t == REMOVE_PATH || t == RECURSIVE_REMOVE_PATH || t == RELABEL_PATH || t == RECURSIVE_RELABEL_PATH;
+ return t == IGNORE_PATH || t == IGNORE_DIRECTORY_PATH || t == REMOVE_PATH || t == RECURSIVE_REMOVE_PATH || t == RELABEL_PATH || t == RECURSIVE_RELABEL_PATH;
}
static struct Item* find_glob(Hashmap *h, const char *match) {
@@ -134,7 +135,7 @@ static struct Item* find_glob(Hashmap *h, const char *match) {
}
static void load_unix_sockets(void) {
- FILE *f = NULL;
+ _cleanup_fclose_ FILE *f = NULL;
char line[LINE_MAX];
if (unix_sockets)
@@ -185,24 +186,16 @@ static void load_unix_sockets(void) {
path_kill_slashes(s);
- k = set_put(unix_sockets, s);
- if (k < 0) {
- free(s);
-
- if (k != -EEXIST)
- goto fail;
- }
+ k = set_consume(unix_sockets, s);
+ if (k < 0 && k != -EEXIST)
+ goto fail;
}
- fclose(f);
return;
fail:
set_free_free(unix_sockets);
unix_sockets = NULL;
-
- if (f)
- fclose(f);
}
static bool unix_socket_alive(const char *fn) {
@@ -217,7 +210,44 @@ static bool unix_socket_alive(const char *fn) {
return true;
}
+static int dir_is_mount_point(DIR *d, const char *subdir) {
+ struct file_handle *h;
+ int mount_id_parent, mount_id;
+ int r_p, r;
+
+ h = alloca(MAX_HANDLE_SZ);
+
+ h->handle_bytes = MAX_HANDLE_SZ;
+ r_p = name_to_handle_at(dirfd(d), ".", h, &mount_id_parent, 0);
+ if (r_p < 0)
+ r_p = -errno;
+
+ h->handle_bytes = MAX_HANDLE_SZ;
+ r = name_to_handle_at(dirfd(d), subdir, h, &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 == -ENOTSUP || r == -ENOSYS || r == -ENOTSUP)
+ return true;
+
+ /* return error */
+ if (r_p < 0)
+ return r_p;
+ return r;
+}
+
static int dir_cleanup(
+ Item *i,
const char *p,
DIR *d,
const struct stat *ds,
@@ -230,12 +260,12 @@ static int dir_cleanup(
struct dirent *dent;
struct timespec times[2];
bool deleted = false;
- char *sub_path = NULL;
int r = 0;
while ((dent = readdir(d))) {
struct stat s;
usec_t age;
+ _cleanup_free_ char *sub_path = NULL;
if (streq(dent->d_name, ".") ||
streq(dent->d_name, ".."))
@@ -255,13 +285,16 @@ static int dir_cleanup(
if (s.st_dev != rootdev)
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)
+ continue;
+
/* Do not delete read-only files owned by root */
if (s.st_uid == 0 && !(s.st_mode & S_IWUSR))
continue;
- free(sub_path);
- sub_path = NULL;
-
if (asprintf(&sub_path, "%s/%s", p, dent->d_name) < 0) {
r = log_oom();
goto finish;
@@ -284,7 +317,7 @@ static int dir_cleanup(
if (maxdepth <= 0)
log_warning("Reached max depth on %s.", sub_path);
else {
- DIR *sub_dir;
+ _cleanup_closedir_ DIR *sub_dir;
int q;
sub_dir = xopendirat(dirfd(d), dent->d_name, O_NOFOLLOW|O_NOATIME);
@@ -297,8 +330,7 @@ static int dir_cleanup(
continue;
}
- q = dir_cleanup(sub_path, sub_dir, &s, cutoff, rootdev, false, maxdepth-1, false);
- closedir(sub_dir);
+ q = dir_cleanup(i, sub_path, sub_dir, &s, cutoff, rootdev, false, maxdepth-1, false);
if (q < 0)
r = q;
@@ -320,12 +352,14 @@ static int dir_cleanup(
if (age >= cutoff)
continue;
- log_debug("rmdir '%s'\n", sub_path);
+ if (i->type != IGNORE_DIRECTORY_PATH || !streq(dent->d_name, p)) {
+ log_debug("rmdir '%s'\n", sub_path);
- if (unlinkat(dirfd(d), dent->d_name, AT_REMOVEDIR) < 0) {
- if (errno != ENOENT && errno != ENOTEMPTY) {
- log_error("rmdir(%s): %m", sub_path);
- r = -errno;
+ if (unlinkat(dirfd(d), dent->d_name, AT_REMOVEDIR) < 0) {
+ if (errno != ENOENT && errno != ENOTEMPTY) {
+ log_error("rmdir(%s): %m", sub_path);
+ r = -errno;
+ }
}
}
@@ -390,70 +424,6 @@ finish:
log_error("utimensat(%s): %m", p);
}
- free(sub_path);
-
- return r;
-}
-
-static int clean_item(Item *i) {
- DIR *d;
- struct stat s, ps;
- bool mountpoint;
- int r;
- usec_t cutoff, n;
-
- assert(i);
-
- if (i->type != CREATE_DIRECTORY &&
- i->type != TRUNCATE_DIRECTORY &&
- i->type != IGNORE_PATH)
- return 0;
-
- if (!i->age_set)
- return 0;
-
- n = now(CLOCK_REALTIME);
- if (n < i->age)
- return 0;
-
- cutoff = n - i->age;
-
- d = opendir(i->path);
- if (!d) {
- if (errno == ENOENT)
- return 0;
-
- log_error("Failed to open directory %s: %m", i->path);
- return -errno;
- }
-
- if (fstat(dirfd(d), &s) < 0) {
- log_error("stat(%s) failed: %m", i->path);
- r = -errno;
- goto finish;
- }
-
- if (!S_ISDIR(s.st_mode)) {
- log_error("%s is not a directory.", i->path);
- r = -ENOTDIR;
- goto finish;
- }
-
- if (fstatat(dirfd(d), "..", &ps, AT_SYMLINK_NOFOLLOW) != 0) {
- log_error("stat(%s/..) failed: %m", i->path);
- r = -errno;
- goto finish;
- }
-
- mountpoint = s.st_dev != ps.st_dev ||
- (s.st_dev == ps.st_dev && s.st_ino == ps.st_ino);
-
- r = dir_cleanup(i->path, d, &s, cutoff, s.st_dev, mountpoint, MAX_DEPTH, i->keep_first_level);
-
-finish:
- if (d)
- closedir(d);
-
return r;
}
@@ -480,18 +450,17 @@ static int item_set_perms(Item *i, const char *path) {
static int write_one_file(Item *i, const char *path) {
int r, e, fd, flags;
struct stat st;
- mode_t u;
flags = i->type == CREATE_FILE ? O_CREAT|O_APPEND :
i->type == TRUNCATE_FILE ? O_CREAT|O_TRUNC : 0;
- u = umask(0);
- label_context_set(path, S_IFREG);
- fd = open(path, flags|O_NDELAY|O_CLOEXEC|O_WRONLY|O_NOCTTY|O_NOFOLLOW, i->mode);
- e = errno;
- label_context_clear();
- umask(u);
- errno = e;
+ RUN_WITH_UMASK(0) {
+ label_context_set(path, S_IFREG);
+ fd = open(path, flags|O_NDELAY|O_CLOEXEC|O_WRONLY|O_NOCTTY|O_NOFOLLOW, i->mode);
+ e = errno;
+ label_context_clear();
+ errno = e;
+ }
if (fd < 0) {
if (i->type == WRITE_FILE && errno == ENOENT)
@@ -542,7 +511,7 @@ static int write_one_file(Item *i, const char *path) {
}
static int recursive_relabel_children(Item *i, const char *path) {
- DIR *d;
+ _cleanup_closedir_ DIR *d;
int ret = 0;
/* This returns the first error we run into, but nevertheless
@@ -557,7 +526,7 @@ static int recursive_relabel_children(Item *i, const char *path) {
union dirent_storage buf;
bool is_dir;
int r;
- char *entry_path;
+ _cleanup_free_ char *entry_path = NULL;
r = readdir_r(d, &buf.de, &de);
if (r != 0) {
@@ -584,7 +553,6 @@ static int recursive_relabel_children(Item *i, const char *path) {
if (lstat(entry_path, &st) < 0) {
if (ret == 0 && errno != ENOENT)
ret = -errno;
- free(entry_path);
continue;
}
@@ -597,7 +565,6 @@ static int recursive_relabel_children(Item *i, const char *path) {
if (r < 0) {
if (ret == 0 && r != -ENOENT)
ret = r;
- free(entry_path);
continue;
}
@@ -606,12 +573,8 @@ static int recursive_relabel_children(Item *i, const char *path) {
if (r < 0 && ret == 0)
ret = r;
}
-
- free(entry_path);
}
- closedir(d);
-
return ret;
}
@@ -634,34 +597,31 @@ static int recursive_relabel(Item *i, const char *path) {
static int glob_item(Item *i, int (*action)(Item *, const char *)) {
int r = 0, k;
- glob_t g;
+ _cleanup_globfree_ glob_t g = {};
char **fn;
- zero(g);
-
errno = 0;
- if ((k = glob(i->path, GLOB_NOSORT|GLOB_BRACE, NULL, &g)) != 0) {
-
+ k = glob(i->path, GLOB_NOSORT|GLOB_BRACE, NULL, &g);
+ if (k != 0)
if (k != GLOB_NOMATCH) {
- if (errno != 0)
+ if (errno > 0)
errno = EIO;
log_error("glob(%s) failed: %m", i->path);
return -errno;
}
- }
- STRV_FOREACH(fn, g.gl_pathv)
- if ((k = action(i, *fn)) < 0)
+ STRV_FOREACH(fn, g.gl_pathv) {
+ k = action(i, *fn);
+ if (k < 0)
r = k;
+ }
- globfree(&g);
return r;
}
static int create_item(Item *i) {
int r, e;
- mode_t u;
struct stat st;
assert(i);
@@ -669,6 +629,7 @@ static int create_item(Item *i) {
switch (i->type) {
case IGNORE_PATH:
+ case IGNORE_DIRECTORY_PATH:
case REMOVE_PATH:
case RECURSIVE_REMOVE_PATH:
return 0;
@@ -689,10 +650,10 @@ static int create_item(Item *i) {
case TRUNCATE_DIRECTORY:
case CREATE_DIRECTORY:
- u = umask(0);
- mkdir_parents_label(i->path, 0755);
- r = mkdir(i->path, i->mode);
- umask(u);
+ RUN_WITH_UMASK(0000) {
+ mkdir_parents_label(i->path, 0755);
+ r = mkdir(i->path, i->mode);
+ }
if (r < 0 && errno != EEXIST) {
log_error("Failed to create directory %s: %m", i->path);
@@ -717,9 +678,9 @@ static int create_item(Item *i) {
case CREATE_FIFO:
- u = umask(0);
- r = mkfifo(i->path, i->mode);
- umask(u);
+ RUN_WITH_UMASK(0000) {
+ r = mkfifo(i->path, i->mode);
+ }
if (r < 0 && errno != EEXIST) {
log_error("Failed to create fifo %s: %m", i->path);
@@ -778,7 +739,7 @@ static int create_item(Item *i) {
if (have_effective_cap(CAP_MKNOD) == 0) {
/* In a container we lack CAP_MKNOD. We
- shouldnt attempt to create the device node in
+ shouldn't attempt to create the device node in
that case to avoid noise, and we don't support
virtualized devices in containers anyway. */
@@ -788,13 +749,13 @@ static int create_item(Item *i) {
file_type = (i->type == CREATE_BLOCK_DEVICE ? S_IFBLK : S_IFCHR);
- u = umask(0);
- label_context_set(i->path, file_type);
- r = mknod(i->path, i->mode | file_type, i->major_minor);
- e = errno;
- label_context_clear();
- umask(u);
- errno = e;
+ RUN_WITH_UMASK(0000) {
+ label_context_set(i->path, file_type);
+ r = mknod(i->path, i->mode | file_type, i->major_minor);
+ e = errno;
+ label_context_clear();
+ errno = e;
+ }
if (r < 0 && errno != EEXIST) {
log_error("Failed to create device node %s: %m", i->path);
@@ -852,6 +813,7 @@ static int remove_item_instance(Item *i, const char *instance) {
case CREATE_BLOCK_DEVICE:
case CREATE_CHAR_DEVICE:
case IGNORE_PATH:
+ case IGNORE_DIRECTORY_PATH:
case RELABEL_PATH:
case RECURSIVE_RELABEL_PATH:
case WRITE_FILE:
@@ -896,6 +858,7 @@ static int remove_item(Item *i) {
case CREATE_CHAR_DEVICE:
case CREATE_BLOCK_DEVICE:
case IGNORE_PATH:
+ case IGNORE_DIRECTORY_PATH:
case RELABEL_PATH:
case RECURSIVE_RELABEL_PATH:
case WRITE_FILE:
@@ -911,6 +874,77 @@ static int remove_item(Item *i) {
return r;
}
+static int clean_item_instance(Item *i, const char* instance) {
+ _cleanup_closedir_ DIR *d = NULL;
+ struct stat s, ps;
+ bool mountpoint;
+ int r;
+ usec_t cutoff, n;
+
+ assert(i);
+
+ if (!i->age_set)
+ return 0;
+
+ n = now(CLOCK_REALTIME);
+ if (n < i->age)
+ return 0;
+
+ cutoff = n - i->age;
+
+ d = opendir(instance);
+ if (!d) {
+ if (errno == ENOENT || errno == ENOTDIR)
+ return 0;
+
+ log_error("Failed to open directory %s: %m", i->path);
+ return -errno;
+ }
+
+ if (fstat(dirfd(d), &s) < 0) {
+ log_error("stat(%s) failed: %m", i->path);
+ return -errno;
+ }
+
+ 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) {
+ log_error("stat(%s/..) failed: %m", i->path);
+ return -errno;
+ }
+
+ mountpoint = s.st_dev != ps.st_dev ||
+ (s.st_dev == ps.st_dev && s.st_ino == ps.st_ino);
+
+ r = dir_cleanup(i, instance, d, &s, cutoff, s.st_dev, mountpoint,
+ MAX_DEPTH, i->keep_first_level);
+ return r;
+}
+
+static int clean_item(Item *i) {
+ int r = 0;
+
+ assert(i);
+
+ switch (i->type) {
+ case CREATE_DIRECTORY:
+ case TRUNCATE_DIRECTORY:
+ case IGNORE_PATH:
+ clean_item_instance(i, i->path);
+ break;
+ case IGNORE_DIRECTORY_PATH:
+ r = glob_item(i, clean_item_instance);
+ break;
+ default:
+ break;
+ }
+
+ return r;
+}
+
static int process_item(Item *i) {
int r, q, p;
@@ -979,8 +1013,10 @@ static bool item_equal(Item *a, Item *b) {
}
static int parse_line(const char *fname, unsigned line, const char *buffer) {
- Item *i, *existing;
- char *mode = NULL, *user = NULL, *group = NULL, *age = NULL;
+ _cleanup_free_ Item *i = NULL;
+ Item *existing;
+ _cleanup_free_ char
+ *mode = NULL, *user = NULL, *group = NULL, *age = NULL;
char type;
Hashmap *h;
int r, n = -1;
@@ -993,24 +1029,18 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
if (!i)
return log_oom();
- if (sscanf(buffer,
- "%c "
- "%ms "
- "%ms "
- "%ms "
- "%ms "
- "%ms "
- "%n",
+ r = sscanf(buffer,
+ "%c %ms %ms %ms %ms %ms %n",
&type,
&i->path,
&mode,
&user,
&group,
&age,
- &n) < 2) {
+ &n);
+ if (r < 2) {
log_error("[%s:%u] Syntax error.", fname, line);
- r = -EIO;
- goto finish;
+ return -EIO;
}
if (n >= 0) {
@@ -1030,6 +1060,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
case TRUNCATE_DIRECTORY:
case CREATE_FIFO:
case IGNORE_PATH:
+ case IGNORE_DIRECTORY_PATH:
case REMOVE_PATH:
case RECURSIVE_REMOVE_PATH:
case RELABEL_PATH:
@@ -1039,16 +1070,14 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
case CREATE_SYMLINK:
if (!i->argument) {
log_error("[%s:%u] Symlink file requires argument.", fname, line);
- r = -EBADMSG;
- goto finish;
+ return -EBADMSG;
}
break;
case WRITE_FILE:
if (!i->argument) {
log_error("[%s:%u] Write file requires argument.", fname, line);
- r = -EBADMSG;
- goto finish;
+ return -EBADMSG;
}
break;
@@ -1058,14 +1087,12 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
if (!i->argument) {
log_error("[%s:%u] Device file requires argument.", fname, line);
- r = -EBADMSG;
- goto finish;
+ 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);
- r = -EBADMSG;
- goto finish;
+ return -EBADMSG;
}
i->major_minor = makedev(major, minor);
@@ -1074,24 +1101,20 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
default:
log_error("[%s:%u] Unknown file type '%c'.", fname, line, type);
- r = -EBADMSG;
- goto finish;
+ return -EBADMSG;
}
i->type = type;
if (!path_is_absolute(i->path)) {
log_error("[%s:%u] Path '%s' not absolute.", fname, line, i->path);
- r = -EBADMSG;
- goto finish;
+ return -EBADMSG;
}
path_kill_slashes(i->path);
- if (arg_prefix && !path_startswith(i->path, arg_prefix)) {
- r = 0;
- goto finish;
- }
+ if (arg_prefix && !path_startswith(i->path, arg_prefix))
+ return 0;
if (user && !streq(user, "-")) {
const char *u = user;
@@ -1099,7 +1122,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
r = get_user_creds(&u, &i->uid, NULL, NULL, NULL);
if (r < 0) {
log_error("[%s:%u] Unknown user '%s'.", fname, line, user);
- goto finish;
+ return r;
}
i->uid_set = true;
@@ -1111,7 +1134,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
r = get_group_creds(&g, &i->gid);
if (r < 0) {
log_error("[%s:%u] Unknown group '%s'.", fname, line, group);
- goto finish;
+ return r;
}
i->gid_set = true;
@@ -1122,8 +1145,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
if (sscanf(mode, "%o", &m) != 1) {
log_error("[%s:%u] Invalid mode '%s'.", fname, line, mode);
- r = -ENOENT;
- goto finish;
+ return -ENOENT;
}
i->mode = m;
@@ -1141,10 +1163,9 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
a++;
}
- if (parse_usec(a, &i->age) < 0) {
+ if (parse_sec(a, &i->age) < 0) {
log_error("[%s:%u] Invalid age '%s'.", fname, line, age);
- r = -EBADMSG;
- goto finish;
+ return -EBADMSG;
}
i->age_set = true;
@@ -1159,29 +1180,18 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
if (!item_equal(existing, i))
log_warning("Two or more conflicting lines for %s configured, ignoring.", i->path);
- r = 0;
- goto finish;
+ return 0;
}
r = hashmap_put(h, i->path, i);
if (r < 0) {
log_error("Failed to insert item %s: %s", i->path, strerror(-r));
- goto finish;
+ return r;
}
- i = NULL;
- r = 0;
-
-finish:
- free(user);
- free(group);
- free(mode);
- free(age);
-
- if (i)
- item_free(i);
+ i = NULL; /* avoid cleanup */
- return r;
+ return 0;
}
static int help(void) {
@@ -1265,18 +1275,19 @@ static int parse_argv(int argc, char *argv[]) {
static int read_config_file(const char *fn, bool ignore_enoent) {
FILE *f;
unsigned v = 0;
- int r = 0;
+ int r;
+ Iterator iterator;
+ Item *i;
assert(fn);
- f = fopen(fn, "re");
- if (!f) {
-
- if (ignore_enoent && errno == ENOENT)
+ r = search_and_fopen_nulstr(fn, "re", conf_file_dirs, &f);
+ if (r < 0) {
+ if (ignore_enoent && r == -ENOENT)
return 0;
- log_error("Failed to open %s: %m", fn);
- return -errno;
+ log_error("Failed to open '%s', ignoring: %s", fn, strerror(-r));
+ return r;
}
log_debug("apply: %s\n", fn);
@@ -1298,43 +1309,47 @@ static int read_config_file(const char *fn, bool ignore_enoent) {
r = k;
}
- if (ferror(f)) {
- log_error("Failed to read from file %s: %m", fn);
- if (r == 0)
- r = -EIO;
- }
-
- fclose(f);
+ /* we have to determine age parameter for each entry of type X */
+ HASHMAP_FOREACH(i, globs, iterator) {
+ Iterator iter;
+ Item *j, *candidate_item = NULL;
- return r;
-}
+ if (i->type != IGNORE_DIRECTORY_PATH)
+ continue;
-static char *resolve_fragment(const char *fragment, const char **search_paths) {
- const char **p;
- char *resolved_path;
+ HASHMAP_FOREACH(j, items, iter) {
+ if (j->type != CREATE_DIRECTORY && j->type != TRUNCATE_DIRECTORY)
+ continue;
- if (is_path(fragment))
- return strdup(fragment);
+ if (path_equal(j->path, i->path)) {
+ candidate_item = j;
+ break;
+ }
- STRV_FOREACH(p, search_paths) {
- resolved_path = strjoin(*p, "/", fragment, NULL);
- if (resolved_path == NULL) {
- log_oom();
- return NULL;
+ 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 (access(resolved_path, F_OK) == 0)
- return resolved_path;
+ if (candidate_item) {
+ i->age = candidate_item->age;
+ i->age_set = true;
+ }
+ }
- free(resolved_path);
+ if (ferror(f)) {
+ log_error("Failed to read from file %s: %m", fn);
+ if (r == 0)
+ r = -EIO;
}
- errno = ENOENT;
- return NULL;
+ fclose(f);
+
+ return r;
}
int main(int argc, char *argv[]) {
- int r;
+ int r, k;
Item *i;
Iterator iterator;
@@ -1354,46 +1369,36 @@ int main(int argc, char *argv[]) {
globs = hashmap_new(string_hash_func, string_compare_func);
if (!items || !globs) {
- log_oom();
- r = EXIT_FAILURE;
+ r = log_oom();
goto finish;
}
- r = EXIT_SUCCESS;
+ r = 0;
if (optind < argc) {
int j;
for (j = optind; j < argc; j++) {
- char *fragment;
-
- fragment = resolve_fragment(argv[j], (const char **)conf_file_dirs);
- if (!fragment) {
- log_error("Failed to find a %s file: %m", argv[j]);
- r = EXIT_FAILURE;
- goto finish;
- }
- if (read_config_file(fragment, false) < 0)
- r = EXIT_FAILURE;
- free(fragment);
+ k = read_config_file(argv[j], false);
+ if (k < 0 && r == 0)
+ r = k;
}
} else {
- char **files, **f;
+ _cleanup_strv_free_ char **files = NULL;
+ char **f;
- r = conf_files_list_strv(&files, ".conf", (const char **)conf_file_dirs);
+ r = conf_files_list_nulstr(&files, ".conf", NULL, conf_file_dirs);
if (r < 0) {
log_error("Failed to enumerate tmpfiles.d files: %s", strerror(-r));
- r = EXIT_FAILURE;
goto finish;
}
STRV_FOREACH(f, files) {
- if (read_config_file(*f, true) < 0)
- r = EXIT_FAILURE;
+ k = read_config_file(*f, true);
+ if (k < 0 && r == 0)
+ r = k;
}
-
- strv_free(files);
}
HASHMAP_FOREACH(i, globs, iterator)
@@ -1416,5 +1421,5 @@ finish:
label_finish();
- return r;
+ return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}
diff --git a/src/tty-ask-password-agent/tty-ask-password-agent.c b/src/tty-ask-password-agent/tty-ask-password-agent.c
index 99a626c6c5..f463662d6b 100644
--- a/src/tty-ask-password-agent/tty-ask-password-agent.c
+++ b/src/tty-ask-password-agent/tty-ask-password-agent.c
@@ -60,11 +60,11 @@ static int ask_password_plymouth(
char ***_passphrases) {
int fd = -1, notify = -1;
- union sockaddr_union sa;
+ union sockaddr_union sa = {};
char *packet = NULL;
ssize_t k;
int r, n;
- struct pollfd pollfd[2];
+ struct pollfd pollfd[2] = {};
char buffer[LINE_MAX];
size_t p = 0;
enum {
@@ -91,7 +91,6 @@ static int ask_password_plymouth(
goto finish;
}
- zero(sa);
sa.sa.sa_family = AF_UNIX;
strncpy(sa.un.sun_path+1, "/org/freedesktop/plymouthd", sizeof(sa.un.sun_path)-1);
if (connect(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + 1 + strlen(sa.un.sun_path+1)) < 0) {
@@ -116,7 +115,6 @@ static int ask_password_plymouth(
goto finish;
}
- zero(pollfd);
pollfd[POLL_SOCKET].fd = fd;
pollfd[POLL_SOCKET].events = POLLIN;
pollfd[POLL_INOTIFY].fd = notify;
@@ -277,7 +275,7 @@ static int parse_password(const char *filename, char **wall) {
return -errno;
}
- r = config_parse(filename, f, NULL, config_item_table_lookup, (void*) items, true, NULL);
+ r = config_parse(NULL, filename, f, NULL, config_item_table_lookup, (void*) items, true, false, NULL);
if (r < 0) {
log_error("Failed to parse password file %s: %s", filename, strerror(-r));
goto finish;
@@ -325,7 +323,7 @@ static int parse_password(const char *filename, char **wall) {
union {
struct sockaddr sa;
struct sockaddr_un un;
- } sa;
+ } sa = {};
size_t packet_length = 0;
assert(arg_action == ACTION_QUERY ||
@@ -341,7 +339,7 @@ static int parse_password(const char *filename, char **wall) {
}
if (arg_plymouth) {
- char **passwords = NULL;
+ _cleanup_strv_free_ char **passwords = NULL;
if ((r = ask_password_plymouth(message, not_after, filename, accept_cached, &passwords)) >= 0) {
char **p;
@@ -410,7 +408,6 @@ static int parse_password(const char *filename, char **wall) {
goto finish;
}
- zero(sa);
sa.un.sun_family = AF_UNIX;
strncpy(sa.un.sun_path, socket_name, sizeof(sa.un.sun_path));
@@ -563,7 +560,7 @@ static int watch_passwords(void) {
};
int notify = -1, signal_fd = -1, tty_block_fd = -1;
- struct pollfd pollfd[_FD_MAX];
+ struct pollfd pollfd[_FD_MAX] = {};
sigset_t mask;
int r;
@@ -591,7 +588,6 @@ static int watch_passwords(void) {
goto finish;
}
- zero(pollfd);
pollfd[FD_INOTIFY].fd = notify;
pollfd[FD_INOTIFY].events = POLLIN;
pollfd[FD_SIGNAL].fd = signal_fd;
diff --git a/src/udev/accelerometer/accelerometer.c b/src/udev/accelerometer/accelerometer.c
index 21f51936ee..e611b48a42 100644
--- a/src/udev/accelerometer/accelerometer.c
+++ b/src/udev/accelerometer/accelerometer.c
@@ -46,6 +46,7 @@
#include <stdio.h>
#include <string.h>
+#include <stdbool.h>
#include <math.h>
#include <sys/types.h>
#include <sys/stat.h>
@@ -122,7 +123,7 @@ string_to_orientation (const char *orientation)
if (orientation == NULL)
return ORIENTATION_UNDEFINED;
for (i = 0; orientations[i] != NULL; i++) {
- if (strcmp (orientation, orientations[i]) == 0)
+ if (streq (orientation, orientations[i]))
return i;
}
return ORIENTATION_UNDEFINED;
@@ -178,7 +179,7 @@ get_prev_orientation(struct udev_device *dev)
return string_to_orientation(value);
}
-#define SET_AXIS(axis, code_) if (ev[i].code == code_) { if (got_##axis == 0) { axis = ev[i].value; got_##axis = 1; } }
+#define SET_AXIS(axis, code_) if (ev[i].code == code_) { if (got_##axis == 0) { axis = ev[i].value; got_##axis = true; } }
/* accelerometers */
static void test_orientation(struct udev *udev,
@@ -186,54 +187,46 @@ static void test_orientation(struct udev *udev,
const char *devpath)
{
OrientationUp old, new;
- int fd, r;
+ _cleanup_close_ int fd = -1;
struct input_event ev[64];
- int got_syn = 0;
- int got_x, got_y, got_z;
+ bool got_syn = false;
+ bool got_x = false, got_y = false, got_z = false;
int x = 0, y = 0, z = 0;
char text[64];
old = get_prev_orientation(dev);
- if ((fd = open(devpath, O_RDONLY)) < 0)
+ fd = open(devpath, O_RDONLY);
+ if (fd < 0)
return;
- got_x = got_y = got_z = 0;
-
while (1) {
- int i;
+ int i, r;
r = read(fd, ev, sizeof(struct input_event) * 64);
- if (r < (int) sizeof(struct input_event)) {
- close(fd);
+ if (r < (int) sizeof(struct input_event))
return;
- }
for (i = 0; i < r / (int) sizeof(struct input_event); i++) {
- if (got_syn == 1) {
+ if (got_syn) {
if (ev[i].type == EV_ABS) {
SET_AXIS(x, ABS_X);
SET_AXIS(y, ABS_Y);
SET_AXIS(z, ABS_Z);
}
}
- if (ev[i].type == EV_SYN && ev[i].code == SYN_REPORT) {
- got_syn = 1;
- }
+ if (ev[i].type == EV_SYN && ev[i].code == SYN_REPORT)
+ got_syn = true;
if (got_x && got_y && got_z)
goto read_dev;
}
}
read_dev:
- close(fd);
-
- if (!got_x || !got_y || !got_z)
- return;
-
new = orientation_calc(old, x, y, z);
- snprintf(text, sizeof(text), "ID_INPUT_ACCELEROMETER_ORIENTATION=%s", orientation_to_string(new));
+ snprintf(text, sizeof(text),
+ "ID_INPUT_ACCELEROMETER_ORIENTATION=%s", orientation_to_string(new));
puts(text);
}
@@ -257,7 +250,6 @@ int main (int argc, char** argv)
char devpath[PATH_MAX];
char *devnode;
- const char *id_path;
struct udev_enumerate *enumerate;
struct udev_list_entry *list_entry;
@@ -303,18 +295,10 @@ int main (int argc, char** argv)
return 1;
}
- id_path = udev_device_get_property_value(dev, "ID_PATH");
- if (id_path == NULL) {
- fprintf (stderr, "unable to get property ID_PATH for '%s'", devpath);
- return 0;
- }
-
/* Get the children devices and find the devnode */
- /* FIXME: use udev_enumerate_add_match_parent() instead */
devnode = NULL;
enumerate = udev_enumerate_new(udev);
- udev_enumerate_add_match_property(enumerate, "ID_PATH", id_path);
- udev_enumerate_add_match_subsystem(enumerate, "input");
+ udev_enumerate_add_match_parent(enumerate, dev);
udev_enumerate_scan_devices(enumerate);
udev_list_entry_foreach(list_entry, udev_enumerate_get_list_entry(enumerate)) {
struct udev_device *device;
diff --git a/src/udev/ata_id/ata_id.c b/src/udev/ata_id/ata_id.c
index 488fed4ac4..68a06b93b8 100644
--- a/src/udev/ata_id/ata_id.c
+++ b/src/udev/ata_id/ata_id.c
@@ -51,65 +51,58 @@ static int disk_scsi_inquiry_command(int fd,
void *buf,
size_t buf_len)
{
- struct sg_io_v4 io_v4;
- uint8_t cdb[6];
- uint8_t sense[32];
+ uint8_t cdb[6] = {
+ /*
+ * INQUIRY, see SPC-4 section 6.4
+ */
+ [0] = 0x12, /* OPERATION CODE: INQUIRY */
+ [3] = (buf_len >> 8), /* ALLOCATION LENGTH */
+ [4] = (buf_len & 0xff),
+ };
+ uint8_t sense[32] = {};
+ struct sg_io_v4 io_v4 = {
+ .guard = 'Q',
+ .protocol = BSG_PROTOCOL_SCSI,
+ .subprotocol = BSG_SUB_PROTOCOL_SCSI_CMD,
+ .request_len = sizeof(cdb),
+ .request = (uintptr_t) cdb,
+ .max_response_len = sizeof(sense),
+ .response = (uintptr_t) sense,
+ .din_xfer_len = buf_len,
+ .din_xferp = (uintptr_t) buf,
+ .timeout = COMMAND_TIMEOUT_MSEC,
+ };
int ret;
- /*
- * INQUIRY, see SPC-4 section 6.4
- */
- memset(cdb, 0, sizeof(cdb));
- cdb[0] = 0x12; /* OPERATION CODE: INQUIRY */
- cdb[3] = (buf_len >> 8); /* ALLOCATION LENGTH */
- cdb[4] = (buf_len & 0xff);
-
- memset(sense, 0, sizeof(sense));
-
- memset(&io_v4, 0, sizeof(struct sg_io_v4));
- io_v4.guard = 'Q';
- io_v4.protocol = BSG_PROTOCOL_SCSI;
- io_v4.subprotocol = BSG_SUB_PROTOCOL_SCSI_CMD;
- io_v4.request_len = sizeof (cdb);
- io_v4.request = (uintptr_t) cdb;
- io_v4.max_response_len = sizeof (sense);
- io_v4.response = (uintptr_t) sense;
- io_v4.din_xfer_len = buf_len;
- io_v4.din_xferp = (uintptr_t) buf;
- io_v4.timeout = COMMAND_TIMEOUT_MSEC;
-
ret = ioctl(fd, SG_IO, &io_v4);
if (ret != 0) {
/* could be that the driver doesn't do version 4, try version 3 */
if (errno == EINVAL) {
- struct sg_io_hdr io_hdr;
-
- memset(&io_hdr, 0, sizeof(struct sg_io_hdr));
- io_hdr.interface_id = 'S';
- io_hdr.cmdp = (unsigned char*) cdb;
- io_hdr.cmd_len = sizeof (cdb);
- io_hdr.dxferp = buf;
- io_hdr.dxfer_len = buf_len;
- io_hdr.sbp = sense;
- io_hdr.mx_sb_len = sizeof (sense);
- io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
- io_hdr.timeout = COMMAND_TIMEOUT_MSEC;
+ struct sg_io_hdr io_hdr = {
+ .interface_id = 'S',
+ .cmdp = (unsigned char*) cdb,
+ .cmd_len = sizeof (cdb),
+ .dxferp = buf,
+ .dxfer_len = buf_len,
+ .sbp = sense,
+ .mx_sb_len = sizeof(sense),
+ .dxfer_direction = SG_DXFER_FROM_DEV,
+ .timeout = COMMAND_TIMEOUT_MSEC,
+ };
ret = ioctl(fd, SG_IO, &io_hdr);
if (ret != 0)
- goto out;
+ return ret;
/* even if the ioctl succeeds, we need to check the return value */
if (!(io_hdr.status == 0 &&
io_hdr.host_status == 0 &&
io_hdr.driver_status == 0)) {
errno = EIO;
- ret = -1;
- goto out;
+ return -1;
}
- } else {
- goto out;
- }
+ } else
+ return ret;
}
/* even if the ioctl succeeds, we need to check the return value */
@@ -117,172 +110,156 @@ static int disk_scsi_inquiry_command(int fd,
io_v4.transport_status == 0 &&
io_v4.driver_status == 0)) {
errno = EIO;
- ret = -1;
- goto out;
+ return -1;
}
- out:
- return ret;
+ return 0;
}
static int disk_identify_command(int fd,
void *buf,
size_t buf_len)
{
- struct sg_io_v4 io_v4;
- uint8_t cdb[12];
- uint8_t sense[32];
- uint8_t *desc = sense+8;
+ uint8_t cdb[12] = {
+ /*
+ * ATA Pass-Through 12 byte command, as described in
+ *
+ * T10 04-262r8 ATA Command Pass-Through
+ *
+ * from http://www.t10.org/ftp/t10/document.04/04-262r8.pdf
+ */
+ [0] = 0xa1, /* OPERATION CODE: 12 byte pass through */
+ [1] = 4 << 1, /* PROTOCOL: PIO Data-in */
+ [2] = 0x2e, /* OFF_LINE=0, CK_COND=1, T_DIR=1, BYT_BLOK=1, T_LENGTH=2 */
+ [3] = 0, /* FEATURES */
+ [4] = 1, /* SECTORS */
+ [5] = 0, /* LBA LOW */
+ [6] = 0, /* LBA MID */
+ [7] = 0, /* LBA HIGH */
+ [8] = 0 & 0x4F, /* SELECT */
+ [9] = 0xEC, /* Command: ATA IDENTIFY DEVICE */
+ };
+ uint8_t sense[32] = {};
+ uint8_t *desc = sense + 8;
+ struct sg_io_v4 io_v4 = {
+ .guard = 'Q',
+ .protocol = BSG_PROTOCOL_SCSI,
+ .subprotocol = BSG_SUB_PROTOCOL_SCSI_CMD,
+ .request_len = sizeof(cdb),
+ .request = (uintptr_t) cdb,
+ .max_response_len = sizeof(sense),
+ .response = (uintptr_t) sense,
+ .din_xfer_len = buf_len,
+ .din_xferp = (uintptr_t) buf,
+ .timeout = COMMAND_TIMEOUT_MSEC,
+ };
int ret;
- /*
- * ATA Pass-Through 12 byte command, as described in
- *
- * T10 04-262r8 ATA Command Pass-Through
- *
- * from http://www.t10.org/ftp/t10/document.04/04-262r8.pdf
- */
- memset(cdb, 0, sizeof(cdb));
- cdb[0] = 0xa1; /* OPERATION CODE: 12 byte pass through */
- cdb[1] = 4 << 1; /* PROTOCOL: PIO Data-in */
- cdb[2] = 0x2e; /* OFF_LINE=0, CK_COND=1, T_DIR=1, BYT_BLOK=1, T_LENGTH=2 */
- cdb[3] = 0; /* FEATURES */
- cdb[4] = 1; /* SECTORS */
- cdb[5] = 0; /* LBA LOW */
- cdb[6] = 0; /* LBA MID */
- cdb[7] = 0; /* LBA HIGH */
- cdb[8] = 0 & 0x4F; /* SELECT */
- cdb[9] = 0xEC; /* Command: ATA IDENTIFY DEVICE */;
- memset(sense, 0, sizeof(sense));
-
- memset(&io_v4, 0, sizeof(struct sg_io_v4));
- io_v4.guard = 'Q';
- io_v4.protocol = BSG_PROTOCOL_SCSI;
- io_v4.subprotocol = BSG_SUB_PROTOCOL_SCSI_CMD;
- io_v4.request_len = sizeof (cdb);
- io_v4.request = (uintptr_t) cdb;
- io_v4.max_response_len = sizeof (sense);
- io_v4.response = (uintptr_t) sense;
- io_v4.din_xfer_len = buf_len;
- io_v4.din_xferp = (uintptr_t) buf;
- io_v4.timeout = COMMAND_TIMEOUT_MSEC;
-
ret = ioctl(fd, SG_IO, &io_v4);
if (ret != 0) {
/* could be that the driver doesn't do version 4, try version 3 */
if (errno == EINVAL) {
- struct sg_io_hdr io_hdr;
-
- memset(&io_hdr, 0, sizeof(struct sg_io_hdr));
- io_hdr.interface_id = 'S';
- io_hdr.cmdp = (unsigned char*) cdb;
- io_hdr.cmd_len = sizeof (cdb);
- io_hdr.dxferp = buf;
- io_hdr.dxfer_len = buf_len;
- io_hdr.sbp = sense;
- io_hdr.mx_sb_len = sizeof (sense);
- io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
- io_hdr.timeout = COMMAND_TIMEOUT_MSEC;
+ struct sg_io_hdr io_hdr = {
+ .interface_id = 'S',
+ .cmdp = (unsigned char*) cdb,
+ .cmd_len = sizeof (cdb),
+ .dxferp = buf,
+ .dxfer_len = buf_len,
+ .sbp = sense,
+ .mx_sb_len = sizeof (sense),
+ .dxfer_direction = SG_DXFER_FROM_DEV,
+ .timeout = COMMAND_TIMEOUT_MSEC,
+ };
ret = ioctl(fd, SG_IO, &io_hdr);
if (ret != 0)
- goto out;
- } else {
- goto out;
- }
+ return ret;
+ } else
+ return ret;
}
if (!(sense[0] == 0x72 && desc[0] == 0x9 && desc[1] == 0x0c)) {
errno = EIO;
- ret = -1;
- goto out;
+ return -1;
}
- out:
- return ret;
+ return 0;
}
static int disk_identify_packet_device_command(int fd,
void *buf,
size_t buf_len)
{
- struct sg_io_v4 io_v4;
- uint8_t cdb[16];
- uint8_t sense[32];
- uint8_t *desc = sense+8;
+ uint8_t cdb[16] = {
+ /*
+ * ATA Pass-Through 16 byte command, as described in
+ *
+ * T10 04-262r8 ATA Command Pass-Through
+ *
+ * from http://www.t10.org/ftp/t10/document.04/04-262r8.pdf
+ */
+ [0] = 0x85, /* OPERATION CODE: 16 byte pass through */
+ [1] = 4 << 1, /* PROTOCOL: PIO Data-in */
+ [2] = 0x2e, /* OFF_LINE=0, CK_COND=1, T_DIR=1, BYT_BLOK=1, T_LENGTH=2 */
+ [3] = 0, /* FEATURES */
+ [4] = 0, /* FEATURES */
+ [5] = 0, /* SECTORS */
+ [6] = 1, /* SECTORS */
+ [7] = 0, /* LBA LOW */
+ [8] = 0, /* LBA LOW */
+ [9] = 0, /* LBA MID */
+ [10] = 0, /* LBA MID */
+ [11] = 0, /* LBA HIGH */
+ [12] = 0, /* LBA HIGH */
+ [13] = 0, /* DEVICE */
+ [14] = 0xA1, /* Command: ATA IDENTIFY PACKET DEVICE */
+ [15] = 0, /* CONTROL */
+ };
+ uint8_t sense[32] = {};
+ uint8_t *desc = sense + 8;
+ struct sg_io_v4 io_v4 = {
+ .guard = 'Q',
+ .protocol = BSG_PROTOCOL_SCSI,
+ .subprotocol = BSG_SUB_PROTOCOL_SCSI_CMD,
+ .request_len = sizeof (cdb),
+ .request = (uintptr_t) cdb,
+ .max_response_len = sizeof (sense),
+ .response = (uintptr_t) sense,
+ .din_xfer_len = buf_len,
+ .din_xferp = (uintptr_t) buf,
+ .timeout = COMMAND_TIMEOUT_MSEC,
+ };
int ret;
- /*
- * ATA Pass-Through 16 byte command, as described in
- *
- * T10 04-262r8 ATA Command Pass-Through
- *
- * from http://www.t10.org/ftp/t10/document.04/04-262r8.pdf
- */
- memset(cdb, 0, sizeof(cdb));
- cdb[0] = 0x85; /* OPERATION CODE: 16 byte pass through */
- cdb[1] = 4 << 1; /* PROTOCOL: PIO Data-in */
- cdb[2] = 0x2e; /* OFF_LINE=0, CK_COND=1, T_DIR=1, BYT_BLOK=1, T_LENGTH=2 */
- cdb[3] = 0; /* FEATURES */
- cdb[4] = 0; /* FEATURES */
- cdb[5] = 0; /* SECTORS */
- cdb[6] = 1; /* SECTORS */
- cdb[7] = 0; /* LBA LOW */
- cdb[8] = 0; /* LBA LOW */
- cdb[9] = 0; /* LBA MID */
- cdb[10] = 0; /* LBA MID */
- cdb[11] = 0; /* LBA HIGH */
- cdb[12] = 0; /* LBA HIGH */
- cdb[13] = 0; /* DEVICE */
- cdb[14] = 0xA1; /* Command: ATA IDENTIFY PACKET DEVICE */;
- cdb[15] = 0; /* CONTROL */
- memset(sense, 0, sizeof(sense));
-
- memset(&io_v4, 0, sizeof(struct sg_io_v4));
- io_v4.guard = 'Q';
- io_v4.protocol = BSG_PROTOCOL_SCSI;
- io_v4.subprotocol = BSG_SUB_PROTOCOL_SCSI_CMD;
- io_v4.request_len = sizeof (cdb);
- io_v4.request = (uintptr_t) cdb;
- io_v4.max_response_len = sizeof (sense);
- io_v4.response = (uintptr_t) sense;
- io_v4.din_xfer_len = buf_len;
- io_v4.din_xferp = (uintptr_t) buf;
- io_v4.timeout = COMMAND_TIMEOUT_MSEC;
-
ret = ioctl(fd, SG_IO, &io_v4);
if (ret != 0) {
/* could be that the driver doesn't do version 4, try version 3 */
if (errno == EINVAL) {
- struct sg_io_hdr io_hdr;
-
- memset(&io_hdr, 0, sizeof(struct sg_io_hdr));
- io_hdr.interface_id = 'S';
- io_hdr.cmdp = (unsigned char*) cdb;
- io_hdr.cmd_len = sizeof (cdb);
- io_hdr.dxferp = buf;
- io_hdr.dxfer_len = buf_len;
- io_hdr.sbp = sense;
- io_hdr.mx_sb_len = sizeof (sense);
- io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
- io_hdr.timeout = COMMAND_TIMEOUT_MSEC;
+ struct sg_io_hdr io_hdr = {
+ .interface_id = 'S',
+ .cmdp = (unsigned char*) cdb,
+ .cmd_len = sizeof (cdb),
+ .dxferp = buf,
+ .dxfer_len = buf_len,
+ .sbp = sense,
+ .mx_sb_len = sizeof (sense),
+ .dxfer_direction = SG_DXFER_FROM_DEV,
+ .timeout = COMMAND_TIMEOUT_MSEC,
+ };
ret = ioctl(fd, SG_IO, &io_hdr);
if (ret != 0)
- goto out;
- } else {
- goto out;
- }
+ return ret;
+ } else
+ return ret;
}
if (!(sense[0] == 0x72 && desc[0] == 0x9 && desc[1] == 0x0c)) {
errno = EIO;
- ret = -1;
- goto out;
+ return -1;
}
- out:
- return ret;
+ return 0;
}
/**
@@ -348,20 +325,19 @@ static void disk_identify_fixup_uint16 (uint8_t identify[512], unsigned int offs
* non-zero with errno set.
*/
static int disk_identify(struct udev *udev,
- int fd,
- uint8_t out_identify[512],
- int *out_is_packet_device)
+ int fd,
+ uint8_t out_identify[512],
+ int *out_is_packet_device)
{
int ret;
uint8_t inquiry_buf[36];
int peripheral_device_type;
int all_nul_bytes;
int n;
- int is_packet_device;
+ int is_packet_device = 0;
/* init results */
- memset(out_identify, '\0', 512);
- is_packet_device = 0;
+ memzero(out_identify, 512);
/* If we were to use ATA PASS_THROUGH (12) on an ATAPI device
* we could accidentally blank media. This is because MMC's BLANK
@@ -425,7 +401,7 @@ static int disk_identify(struct udev *udev,
out:
if (out_is_packet_device != NULL)
- *out_is_packet_device = is_packet_device;
+ *out_is_packet_device = is_packet_device;
return ret;
}
diff --git a/src/udev/cdrom_id/cdrom_id.c b/src/udev/cdrom_id/cdrom_id.c
index 1056536b7d..1ad0459236 100644
--- a/src/udev/cdrom_id/cdrom_id.c
+++ b/src/udev/cdrom_id/cdrom_id.c
@@ -17,10 +17,6 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef _GNU_SOURCE
-#define _GNU_SOURCE 1
-#endif
-
#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
@@ -513,6 +509,8 @@ static int cd_profiles_old_mmc(struct udev *udev, int fd)
if (cd_media == 1) {
log_debug("no current profile, but disc is present; assuming CD-ROM\n");
cd_media_cd_rom = 1;
+ cd_media_track_count = 1;
+ cd_media_track_count_data = 1;
return 0;
} else {
log_debug("no current profile, assuming no media\n");
diff --git a/src/udev/collect/collect.c b/src/udev/collect/collect.c
index 3c46e40de1..f95ee23b75 100644
--- a/src/udev/collect/collect.c
+++ b/src/udev/collect/collect.c
@@ -35,8 +35,8 @@
#include "libudev-private.h"
#include "macro.h"
-#define BUFSIZE 16
-#define UDEV_ALARM_TIMEOUT 180
+#define BUFSIZE 16
+#define UDEV_ALARM_TIMEOUT 180
enum collect_state {
STATE_NONE,
@@ -139,12 +139,12 @@ static int checkout(int fd)
restart:
len = bufsize >> 1;
- buf = calloc(1,bufsize + 1);
- if (!buf) {
- fprintf(stderr, "Out of memory.\n");
+ buf = malloc(bufsize + 1);
+ if (!buf)
return log_oom();
- }
memset(buf, ' ', bufsize);
+ buf[bufsize] = '\0';
+
ptr = buf + len;
while ((read(fd, buf + len, len)) > 0) {
while (ptr && *ptr) {
@@ -213,7 +213,7 @@ static void invite(char *us)
udev_list_node_foreach(him_node, &bunch) {
struct _mate *him = node_to_mate(him_node);
- if (!strcmp(him->name, us)) {
+ if (streq(him->name, us)) {
him->state = STATE_CONFIRMED;
who = him;
}
@@ -241,7 +241,7 @@ static void reject(char *us)
udev_list_node_foreach(him_node, &bunch) {
struct _mate *him = node_to_mate(him_node);
- if (!strcmp(him->name, us)) {
+ if (streq(him->name, us)) {
him->state = STATE_NONE;
who = him;
}
@@ -414,7 +414,7 @@ int main(int argc, char **argv)
if (debug)
fprintf(stderr, "Using checkpoint '%s'\n", checkpoint);
- util_strscpyl(tmpdir, sizeof(tmpdir), "/run/udev/collect", NULL);
+ strscpyl(tmpdir, sizeof(tmpdir), "/run/udev/collect", NULL);
fd = prepare(tmpdir, checkpoint);
if (fd < 0) {
ret = 3;
@@ -434,7 +434,7 @@ int main(int argc, char **argv)
udev_list_node_foreach(him_node, &bunch) {
struct _mate *him = node_to_mate(him_node);
- if (!strcmp(him->name, argv[i]))
+ if (streq(him->name, argv[i]))
who = him;
}
if (!who) {
diff --git a/src/udev/keymap/95-keyboard-force-release.rules b/src/udev/keymap/95-keyboard-force-release.rules
index f97a022d8d..3e33e85535 100644
--- a/src/udev/keymap/95-keyboard-force-release.rules
+++ b/src/udev/keymap/95-keyboard-force-release.rules
@@ -42,7 +42,7 @@ ENV{DMI_VENDOR}=="Viooo Corporation", ATTR{[dmi/id]product_name}=="PT17", RUN+="
# These are all the HP laptops that setup a touchpad toggle key
ENV{DMI_VENDOR}=="Hewlett-Packard*", ATTR{[dmi/id]product_name}=="*[pP][aA][vV][iI][lL][iI][oO][nN]*", RUN+="keyboard-force-release.sh $devpath hp-other"
ENV{DMI_VENDOR}=="Hewlett-Packard*", ATTR{[dmi/id]product_name}=="*[tT][xX]2*", RUN+="keyboard-force-release.sh $devpath hp-other"
-ENV{DMI_VENDOR}=="Hewlett-Packard*", ATTR{[dmi/id]product_name}=="*2510p*|*2530p*|HP G60 Notebook PC", RUN+="keyboard-force-release.sh $devpath hp-other"
+ENV{DMI_VENDOR}=="Hewlett-Packard*", ATTR{[dmi/id]product_name}=="*2510p*|*2530p*|HP G60 Notebook PC|HDX9494NR", RUN+="keyboard-force-release.sh $devpath hp-other"
ENV{DMI_VENDOR}=="Zepto", ATTR{[dmi/id]product_name}=="Znote 6615WD", RUN+="keyboard-force-release.sh $devpath common-volume-keys"
diff --git a/src/udev/keymap/95-keymap.rules b/src/udev/keymap/95-keymap.rules
index 347b5a1dda..7956092030 100644
--- a/src/udev/keymap/95-keymap.rules
+++ b/src/udev/keymap/95-keymap.rules
@@ -86,6 +86,8 @@ ENV{DMI_VENDOR}=="LENOVO*", ATTR{[dmi/id]product_version}=="*IdeaPad*", RUN+="ke
ENV{DMI_VENDOR}=="LENOVO*", ATTR{[dmi/id]product_name}=="S10-*", RUN+="keymap $name lenovo-ideapad"
ENV{DMI_VENDOR}=="LENOVO", ATTR{[dmi/id]product_version}=="*IdeaPad Y550*", RUN+="keymap $name 0x95 media 0xA3 play"
ENV{DMI_VENDOR}=="LENOVO", ATTR{[dmi/id]product_version}=="*Lenovo V480*", RUN+="keymap $name 0xf1 f21"
+# 0xf1 is touchpad toggle, 0xCE is microphone mute in Lenovo U300s
+ENV{DMI_VENDOR}=="LENOVO", ATTR{[dmi/id]product_version}=="*IdeaPad U300s*", RUN+="keymap $name 0xf1 f21 0xCE f20"
ENV{DMI_VENDOR}=="Hewlett-Packard*", RUN+="keymap $name hewlett-packard"
ENV{DMI_VENDOR}=="Hewlett-Packard*", ATTR{[dmi/id]product_name}=="*[tT][aA][bB][lL][eE][tT]*", RUN+="keymap $name hewlett-packard-tablet"
@@ -96,9 +98,11 @@ ENV{DMI_VENDOR}=="Hewlett-Packard*", ATTR{[dmi/id]product_name}=="*2510p*|*2530p
ENV{DMI_VENDOR}=="Hewlett-Packard*", ATTR{[dmi/id]product_name}=="*[tT][xX]2*", RUN+="keymap $name hewlett-packard-tx2"
ENV{DMI_VENDOR}=="Hewlett-Packard", ATTR{[dmi/id]product_name}=="Presario 2100*", RUN+="keymap $name hewlett-packard-presario-2100"
ENV{DMI_VENDOR}=="Hewlett-Packard", ATTR{[dmi/id]product_name}=="HP G62 Notebook PC", RUN+="keymap $name 0xB2 www"
-ENV{DMI_VENDOR}=="Hewlett-Packard", ATTR{[dmi/id]product_name}=="HP ProBook*", RUN+="keymap $name 0xF8 rfkill"
+ENV{DMI_VENDOR}=="Hewlett-Packard", ATTR{[dmi/id]product_name}=="HP ProBook*", RUN+="keymap $name 0xF8 rfkill 0xB2 www"
ENV{DMI_VENDOR}=="Hewlett-Packard", ATTR{[dmi/id]product_name}=="HP EliteBook 8440p", RUN+="keymap $name hewlett-packard_elitebook-8440p"
-# HP Pavillion dv6315ea has empty DMI_VENDOR
+ENV{DMI_VENDOR}=="Hewlett-Packard", ATTR{[dmi/id]product_name}=="HP EliteBook 8460p", RUN+="keymap $name hewlett-packard_elitebook-8460p"
+ENV{DMI_VENDOR}=="Hewlett-Packard", ATTR{[dmi/id]product_name}=="HDX9494NR", RUN+="keymap $name hewlett-packard-hdx9494nr"
+# HP Pavilion dv6315ea has empty DMI_VENDOR
ATTR{[dmi/id]board_vendor}=="Quanta", ATTR{[dmi/id]board_name}=="30B7", ATTR{[dmi/id]board_version}=="65.2B", RUN+="keymap $name 0x88 media" # "quick play
# Gateway clone of Acer Aspire One AOA110/AOA150
@@ -140,6 +144,9 @@ ENV{DMI_VENDOR}=="MICRO-STAR*|Micro-Star*", RUN+="keymap $name micro-star"
# brightness; so ignore those atkbd ones, to avoid loops
ENV{DMI_VENDOR}=="MICRO-STAR*", ATTR{[dmi/id]product_name}=="*U-100*|*U100*|*N033", RUN+="keymap $name 0xF7 reserved 0xF8 reserved"
+# MSI Wind U90/U100 generates separate touchpad on/off keycodes so ignore touchpad toggle keycode
+ENV{DMI_VENDOR}=="MICRO-STAR*", ATTR{[dmi/id]product_name}=="U90/U100", RUN+="keymap $name 0xE4 reserved"
+
ENV{DMI_VENDOR}=="INVENTEC", ATTR{[dmi/id]product_name}=="SYMPHONY 6.0/7.0", RUN+="keymap $name inventec-symphony_6.0_7.0"
ENV{DMI_VENDOR}=="MAXDATA", ATTR{[dmi/id]product_name}=="Pro 7000*", RUN+="keymap $name maxdata-pro_7000"
diff --git a/src/udev/keymap/findkeyboards b/src/udev/keymap/findkeyboards
index 9ce27429b2..c6b50d12d0 100755
--- a/src/udev/keymap/findkeyboards
+++ b/src/udev/keymap/findkeyboards
@@ -33,12 +33,12 @@ str_line_starts() {
keyboard_devices() {
# standard AT keyboard
for dev in `udevadm trigger --dry-run --verbose --property-match=ID_INPUT_KEYBOARD=1`; do
- walk=`udevadm info --attribute-walk --path=$dev`
env=`udevadm info --query=env --path=$dev`
# filter out non-event devices, such as the parent input devices which have no devnode
if ! echo "$env" | str_line_starts 'DEVNAME='; then
continue
fi
+ walk=`udevadm info --attribute-walk --path=$dev`
if strstr "$walk" 'DRIVERS=="atkbd"'; then
echo -n 'AT keyboard: '
elif echo "$env" | str_line_starts 'ID_USB_DRIVER=usbhid'; then
diff --git a/src/udev/keymap/keymap.c b/src/udev/keymap/keymap.c
index 27aaf6b670..ae0a19d3a3 100644
--- a/src/udev/keymap/keymap.c
+++ b/src/udev/keymap/keymap.c
@@ -36,7 +36,7 @@
#include <linux/limits.h>
#include <linux/input.h>
-const struct key* lookup_key (const char *str, unsigned int len);
+static const struct key* lookup_key (const char *str, unsigned int len);
#include "keys-from-name.h"
#include "keys-to-name.h"
diff --git a/src/udev/scsi_id/scsi_id.c b/src/udev/scsi_id/scsi_id.c
index c90b6aa581..ab7c54c3f2 100644
--- a/src/udev/scsi_id/scsi_id.c
+++ b/src/udev/scsi_id/scsi_id.c
@@ -108,7 +108,7 @@ static void set_type(const char *from, char *to, size_t len)
break;
}
}
- util_strscpy(to, len, type);
+ strscpy(to, len, type);
}
/*
@@ -227,7 +227,7 @@ static int get_file_options(struct udev *udev,
continue;
str1 = strsep(&buf, "=");
- if (str1 && strcasecmp(str1, "VENDOR") == 0) {
+ if (str1 && strcaseeq(str1, "VENDOR")) {
str1 = get_value(&buf);
if (!str1) {
retval = log_oom();
@@ -236,7 +236,7 @@ static int get_file_options(struct udev *udev,
vendor_in = str1;
str1 = strsep(&buf, "=");
- if (str1 && strcasecmp(str1, "MODEL") == 0) {
+ if (str1 && strcaseeq(str1, "MODEL")) {
str1 = get_value(&buf);
if (!str1) {
retval = log_oom();
@@ -247,7 +247,7 @@ static int get_file_options(struct udev *udev,
}
}
- if (str1 && strcasecmp(str1, "OPTIONS") == 0) {
+ if (str1 && strcaseeq(str1, "OPTIONS")) {
str1 = get_value(&buf);
if (!str1) {
retval = log_oom();
@@ -267,10 +267,10 @@ static int get_file_options(struct udev *udev,
if (vendor == NULL) {
if (vendor_in == NULL)
break;
- } else if ((vendor_in && strncmp(vendor, vendor_in,
- strlen(vendor_in)) == 0) &&
- (!model_in || (strncmp(model, model_in,
- strlen(model_in)) == 0))) {
+ } else if ((vendor_in && strneq(vendor, vendor_in,
+ strlen(vendor_in))) &&
+ (!model_in || (strneq(model, model_in,
+ strlen(model_in))))) {
/*
* Matched vendor and optionally model.
*
@@ -341,7 +341,7 @@ static int set_options(struct udev *udev,
case 'd':
dev_specified = 1;
- util_strscpy(maj_min_dev, MAX_PATH_LEN, optarg);
+ strscpy(maj_min_dev, MAX_PATH_LEN, optarg);
break;
case 'e':
@@ -349,7 +349,7 @@ static int set_options(struct udev *udev,
break;
case 'f':
- util_strscpy(config_file, MAX_PATH_LEN, optarg);
+ strscpy(config_file, MAX_PATH_LEN, optarg);
break;
case 'g':
@@ -372,11 +372,11 @@ static int set_options(struct udev *udev,
exit(0);
case 'p':
- if (strcmp(optarg, "0x80") == 0) {
+ if (streq(optarg, "0x80")) {
default_page_code = PAGE_80;
- } else if (strcmp(optarg, "0x83") == 0) {
+ } else if (streq(optarg, "0x83")) {
default_page_code = PAGE_83;
- } else if (strcmp(optarg, "pre-spc3-83") == 0) {
+ } else if (streq(optarg, "pre-spc3-83")) {
default_page_code = PAGE_83_PRE_SPC3;
} else {
log_error("Unknown page code '%s'\n", optarg);
@@ -415,7 +415,7 @@ static int set_options(struct udev *udev,
}
if (optind < argc && !dev_specified) {
dev_specified = 1;
- util_strscpy(maj_min_dev, MAX_PATH_LEN, argv[optind]);
+ strscpy(maj_min_dev, MAX_PATH_LEN, argv[optind]);
}
return 0;
}
@@ -449,11 +449,11 @@ static int per_dev_options(struct udev *udev,
break;
case 'p':
- if (strcmp(optarg, "0x80") == 0) {
+ if (streq(optarg, "0x80")) {
*page_code = PAGE_80;
- } else if (strcmp(optarg, "0x83") == 0) {
+ } else if (streq(optarg, "0x83")) {
*page_code = PAGE_83;
- } else if (strcmp(optarg, "pre-spc3-83") == 0) {
+ } else if (streq(optarg, "pre-spc3-83")) {
*page_code = PAGE_83_PRE_SPC3;
} else {
log_error("Unknown page code '%s'\n", optarg);
diff --git a/src/udev/scsi_id/scsi_id.h b/src/udev/scsi_id/scsi_id.h
index 828a98305f..103e443d07 100644
--- a/src/udev/scsi_id/scsi_id.h
+++ b/src/udev/scsi_id/scsi_id.h
@@ -15,25 +15,25 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#define MAX_PATH_LEN 512
+#define MAX_PATH_LEN 512
/*
* MAX_ATTR_LEN: maximum length of the result of reading a sysfs
* attribute.
*/
-#define MAX_ATTR_LEN 256
+#define MAX_ATTR_LEN 256
/*
* MAX_SERIAL_LEN: the maximum length of the serial number, including
* added prefixes such as vendor and product (model) strings.
*/
-#define MAX_SERIAL_LEN 256
+#define MAX_SERIAL_LEN 256
/*
* MAX_BUFFER_LEN: maximum buffer size and line length used while reading
* the config file.
*/
-#define MAX_BUFFER_LEN 256
+#define MAX_BUFFER_LEN 256
struct scsi_id_device {
char vendor[9];
@@ -58,9 +58,9 @@ struct scsi_id_device {
char tgpt_group[8];
};
-extern int scsi_std_inquiry(struct udev *udev, struct scsi_id_device *dev_scsi, const char *devname);
-extern int scsi_get_serial (struct udev *udev, struct scsi_id_device *dev_scsi, const char *devname,
- int page_code, int len);
+int scsi_std_inquiry(struct udev *udev, struct scsi_id_device *dev_scsi, const char *devname);
+int scsi_get_serial(struct udev *udev, struct scsi_id_device *dev_scsi, const char *devname,
+ int page_code, int len);
/*
* Page code values.
@@ -68,6 +68,6 @@ extern int scsi_get_serial (struct udev *udev, struct scsi_id_device *dev_scsi,
enum page_code {
PAGE_83_PRE_SPC3 = -0x83,
PAGE_UNSPECIFIED = 0x00,
- PAGE_80 = 0x80,
- PAGE_83 = 0x83,
+ PAGE_80 = 0x80,
+ PAGE_83 = 0x83,
};
diff --git a/src/udev/scsi_id/scsi_serial.c b/src/udev/scsi_id/scsi_serial.c
index 3c52dee62d..d522a23a08 100644
--- a/src/udev/scsi_id/scsi_serial.c
+++ b/src/udev/scsi_id/scsi_serial.c
@@ -436,7 +436,7 @@ static int do_scsi_page0_inquiry(struct udev *udev,
* If the vendor id appears in the page assume the page is
* invalid.
*/
- if (!strncmp((char *)&buffer[VENDOR_LENGTH], dev_scsi->vendor, VENDOR_LENGTH)) {
+ if (strneq((char *)&buffer[VENDOR_LENGTH], dev_scsi->vendor, VENDOR_LENGTH)) {
log_debug("%s: invalid page0 data\n", dev_scsi->kernel);
return 1;
}
@@ -799,7 +799,7 @@ static int do_scsi_page80_inquiry(struct udev *udev,
ser_ind = prepend_vendor_model(udev, dev_scsi, &serial[1]);
if (ser_ind < 0)
return 1;
- ser_ind++; /* for the leading 'S' */
+ ser_ind++; /* for the leading 'S' */
for (i = 4; i < len + 4; i++, ser_ind++)
serial[ser_ind] = buf[i];
}
diff --git a/src/udev/udev-builtin-blkid.c b/src/udev/udev-builtin-blkid.c
index 4293103046..bae429344f 100644
--- a/src/udev/udev-builtin-blkid.c
+++ b/src/udev/udev-builtin-blkid.c
@@ -76,8 +76,24 @@ static void print_property(struct udev_device *dev, bool test, const char *name,
udev_builtin_add_property(dev, test, "ID_PART_ENTRY_TYPE", s);
} else if (startswith(name, "PART_ENTRY_")) {
- util_strscpyl(s, sizeof(s), "ID_", name, NULL);
+ strscpyl(s, sizeof(s), "ID_", name, NULL);
udev_builtin_add_property(dev, test, s, value);
+
+ } else if (streq(name, "SYSTEM_ID")) {
+ blkid_encode_string(value, s, sizeof(s));
+ udev_builtin_add_property(dev, test, "ID_FS_SYSTEM_ID", s);
+
+ } else if (streq(name, "PUBLISHER_ID")) {
+ blkid_encode_string(value, s, sizeof(s));
+ udev_builtin_add_property(dev, test, "ID_FS_PUBLISHER_ID", s);
+
+ } else if (streq(name, "APPLICATION_ID")) {
+ blkid_encode_string(value, s, sizeof(s));
+ udev_builtin_add_property(dev, test, "ID_FS_APPLICATION_ID", s);
+
+ } else if (streq(name, "BOOT_SYSTEM_ID")) {
+ blkid_encode_string(value, s, sizeof(s));
+ udev_builtin_add_property(dev, test, "ID_FS_BOOT_SYSTEM_ID", s);
}
}
diff --git a/src/udev/udev-builtin-btrfs.c b/src/udev/udev-builtin-btrfs.c
index dfb05525ad..f7bea69b26 100644
--- a/src/udev/udev-builtin-btrfs.c
+++ b/src/udev/udev-builtin-btrfs.c
@@ -48,7 +48,7 @@ static int builtin_btrfs(struct udev_device *dev, int argc, char *argv[], bool t
if (fd < 0)
return EXIT_FAILURE;
- util_strscpy(args.name, sizeof(args.name), argv[2]);
+ strscpy(args.name, sizeof(args.name), argv[2]);
err = ioctl(fd, BTRFS_IOC_DEVICES_READY, &args);
close(fd);
if (err < 0)
diff --git a/src/udev/udev-builtin-firmware.c b/src/udev/udev-builtin-firmware.c
index 4a91d33575..b80940b6ef 100644
--- a/src/udev/udev-builtin-firmware.c
+++ b/src/udev/udev-builtin-firmware.c
@@ -78,8 +78,6 @@ static int builtin_firmware(struct udev_device *dev, int argc, char *argv[], boo
{
struct udev *udev = udev_device_get_udev(dev);
static const char *searchpath[] = { FIRMWARE_PATH };
- char fwencpath[UTIL_PATH_SIZE];
- char misspath[UTIL_PATH_SIZE];
char loadpath[UTIL_PATH_SIZE];
char datapath[UTIL_PATH_SIZE];
char fwpath[UTIL_PATH_SIZE];
@@ -100,34 +98,21 @@ static int builtin_firmware(struct udev_device *dev, int argc, char *argv[], boo
/* lookup firmware file */
uname(&kernel);
for (i = 0; i < ELEMENTSOF(searchpath); i++) {
- util_strscpyl(fwpath, sizeof(fwpath), searchpath[i], kernel.release, "/", firmware, NULL);
+ strscpyl(fwpath, sizeof(fwpath), searchpath[i], kernel.release, "/", firmware, NULL);
fwfile = fopen(fwpath, "re");
if (fwfile != NULL)
break;
- util_strscpyl(fwpath, sizeof(fwpath), searchpath[i], firmware, NULL);
+ strscpyl(fwpath, sizeof(fwpath), searchpath[i], firmware, NULL);
fwfile = fopen(fwpath, "re");
if (fwfile != NULL)
break;
}
- util_path_encode(firmware, fwencpath, sizeof(fwencpath));
- util_strscpyl(misspath, sizeof(misspath), "/run/udev/firmware-missing/", fwencpath, NULL);
- util_strscpyl(loadpath, sizeof(loadpath), udev_device_get_syspath(dev), "/loading", NULL);
+ strscpyl(loadpath, sizeof(loadpath), udev_device_get_syspath(dev), "/loading", NULL);
if (fwfile == NULL) {
- int err;
-
- /* This link indicates the missing firmware file and the associated device */
log_debug("did not find firmware file '%s'\n", firmware);
- do {
- err = mkdir_parents(misspath, 0755);
- if (err != 0 && err != -ENOENT)
- break;
- err = symlink(udev_device_get_devpath(dev), misspath);
- if (err != 0)
- err = -errno;
- } while (err == -ENOENT);
rc = EXIT_FAILURE;
/*
* Do not cancel the request in the initrd, the real root might have
@@ -146,13 +131,10 @@ static int builtin_firmware(struct udev_device *dev, int argc, char *argv[], boo
goto exit;
}
- if (unlink(misspath) == 0)
- util_delete_path(udev, misspath);
-
if (!set_loading(udev, loadpath, "1"))
goto exit;
- util_strscpyl(datapath, sizeof(datapath), udev_device_get_syspath(dev), "/data", NULL);
+ strscpyl(datapath, sizeof(datapath), udev_device_get_syspath(dev), "/data", NULL);
if (!copy_firmware(udev, fwpath, datapath, statbuf.st_size)) {
log_error("error sending firmware '%s' to device\n", firmware);
set_loading(udev, loadpath, "-1");
diff --git a/src/udev/udev-builtin-kmod.c b/src/udev/udev-builtin-kmod.c
index 17aca2944d..fc28121267 100644
--- a/src/udev/udev-builtin-kmod.c
+++ b/src/udev/udev-builtin-kmod.c
@@ -78,7 +78,7 @@ static int builtin_kmod(struct udev_device *dev, int argc, char *argv[], bool te
if (!ctx)
return 0;
- if (argc < 3 || strcmp(argv[1], "load")) {
+ if (argc < 3 || !streq(argv[1], "load")) {
log_error("expect: %s load <module>\n", argv[0]);
return EXIT_FAILURE;
}
diff --git a/src/udev/udev-builtin-net_id.c b/src/udev/udev-builtin-net_id.c
index d5db762e80..5719021e93 100644
--- a/src/udev/udev-builtin-net_id.c
+++ b/src/udev/udev-builtin-net_id.c
@@ -24,6 +24,8 @@
* - physical/geographical location of the hardware
* - the interface's MAC address
*
+ * http://www.freedesktop.org/wiki/Software/systemd/PredictableNetworkInterfaceNames
+ *
* Two character prefixes based on the type of interface:
* en -- ethernet
* wl -- wlan
@@ -89,11 +91,13 @@
#include <linux/pci_regs.h>
#include "udev.h"
+#include "fileio.h"
enum netname_type{
NET_UNDEF,
NET_PCI,
NET_USB,
+ NET_BCMA,
};
struct netnames {
@@ -108,8 +112,9 @@ struct netnames {
char pci_onboard[IFNAMSIZ];
const char *pci_onboard_label;
- struct udev_device *usbdev;
char usb_ports[IFNAMSIZ];
+
+ char bcma_core[IFNAMSIZ];
};
/* retrieve on-board index number and label from firmware */
@@ -136,7 +141,7 @@ static int dev_pci_onboard(struct udev_device *dev, struct netnames *names) {
/* read the 256 bytes PCI configuration space to check the multi-function bit */
static bool is_pci_multifunction(struct udev_device *dev) {
char filename[256];
- FILE *f;
+ FILE *f = NULL;
char config[64];
bool multi = false;
@@ -151,7 +156,8 @@ static bool is_pci_multifunction(struct udev_device *dev) {
if ((config[PCI_HEADER_TYPE] & 0x80) != 0)
multi = true;
out:
- fclose(f);
+ if(f)
+ fclose(f);
return multi;
}
@@ -182,11 +188,11 @@ static int dev_pci_slot(struct udev_device *dev, struct netnames *names) {
/* compose a name based on the raw kernel's PCI bus, slot numbers */
s = names->pci_path;
- l = util_strpcpyf(&s, sizeof(names->pci_path), "p%ds%d", bus, slot);
+ l = strpcpyf(&s, sizeof(names->pci_path), "p%ds%d", bus, slot);
if (func > 0 || is_pci_multifunction(names->pcidev))
- l = util_strpcpyf(&s, l, "f%d", func);
+ l = strpcpyf(&s, l, "f%d", func);
if (dev_id > 0)
- l = util_strpcpyf(&s, l, "d%d", dev_id);
+ l = strpcpyf(&s, l, "d%d", dev_id);
if (l == 0)
names->pci_path[0] = '\0';
@@ -218,7 +224,7 @@ static int dev_pci_slot(struct udev_device *dev, struct netnames *names) {
snprintf(str, sizeof(str), "%s/%s/address", slots, dent->d_name);
if (read_one_line_file(str, &address) >= 0) {
/* match slot address with device by stripping the function */
- if (strncmp(address, udev_device_get_sysname(names->pcidev), strlen(address)) == 0)
+ if (strneq(address, udev_device_get_sysname(names->pcidev), strlen(address)))
hotplug_slot = i;
free(address);
}
@@ -230,11 +236,11 @@ static int dev_pci_slot(struct udev_device *dev, struct netnames *names) {
if (hotplug_slot > 0) {
s = names->pci_slot;
- l = util_strpcpyf(&s, sizeof(names->pci_slot), "s%d", hotplug_slot);
+ l = strpcpyf(&s, sizeof(names->pci_slot), "s%d", hotplug_slot);
if (func > 0 || is_pci_multifunction(names->pcidev))
- l = util_strpcpyf(&s, l, "f%d", func);
+ l = strpcpyf(&s, l, "f%d", func);
if (dev_id > 0)
- l = util_strpcpyf(&s, l, "d%d", dev_id);
+ l = strpcpyf(&s, l, "d%d", dev_id);
if (l == 0)
names->pci_path[0] = '\0';
}
@@ -250,7 +256,7 @@ static int names_pci(struct udev_device *dev, struct netnames *names) {
if (!parent)
return -ENOENT;
/* check if our direct parent is a PCI device with no other bus in-between */
- if (streq("pci", udev_device_get_subsystem(parent))) {
+ if (streq_ptr("pci", udev_device_get_subsystem(parent))) {
names->type = NET_PCI;
names->pcidev = parent;
} else {
@@ -264,6 +270,7 @@ static int names_pci(struct udev_device *dev, struct netnames *names) {
}
static int names_usb(struct udev_device *dev, struct netnames *names) {
+ struct udev_device *usbdev;
char name[256];
char *ports;
char *config;
@@ -271,12 +278,12 @@ static int names_usb(struct udev_device *dev, struct netnames *names) {
size_t l;
char *s;
- names->usbdev = udev_device_get_parent_with_subsystem_devtype(dev, "usb", "usb_interface");
- if (!names->usbdev)
+ usbdev = udev_device_get_parent_with_subsystem_devtype(dev, "usb", "usb_interface");
+ if (!usbdev)
return -ENOENT;
/* get USB port number chain, configuration, interface */
- util_strscpy(name, sizeof(name), udev_device_get_sysname(names->usbdev));
+ strscpy(name, sizeof(name), udev_device_get_sysname(usbdev));
s = strchr(name, '-');
if (!s)
return -EINVAL;
@@ -299,15 +306,15 @@ static int names_usb(struct udev_device *dev, struct netnames *names) {
while ((s = strchr(s, '.')))
s[0] = 'u';
s = names->usb_ports;
- l = util_strpcpyl(&s, sizeof(names->usb_ports), "u", ports, NULL);
+ l = strpcpyl(&s, sizeof(names->usb_ports), "u", ports, NULL);
/* append USB config number, suppress the common config == 1 */
if (!streq(config, "1"))
- l = util_strpcpyl(&s, sizeof(names->usb_ports), "c", config, NULL);
+ l = strpcpyl(&s, sizeof(names->usb_ports), "c", config, NULL);
/* append USB interface number, suppress the interface == 0 */
if (!streq(interf, "0"))
- l = util_strpcpyl(&s, sizeof(names->usb_ports), "i", interf, NULL);
+ l = strpcpyl(&s, sizeof(names->usb_ports), "i", interf, NULL);
if (l == 0)
return -ENAMETOOLONG;
@@ -315,6 +322,25 @@ static int names_usb(struct udev_device *dev, struct netnames *names) {
return 0;
}
+static int names_bcma(struct udev_device *dev, struct netnames *names) {
+ struct udev_device *bcmadev;
+ unsigned int core;
+
+ bcmadev = udev_device_get_parent_with_subsystem_devtype(dev, "bcma", NULL);
+ if (!bcmadev)
+ return -ENOENT;
+
+ /* bus num:core num */
+ if (sscanf(udev_device_get_sysname(bcmadev), "bcma%*d:%d", &core) != 1)
+ return -EINVAL;
+ /* suppress the common core == 0 */
+ if (core > 0)
+ snprintf(names->bcma_core, sizeof(names->bcma_core), "b%d", core);
+
+ names->type = NET_BCMA;
+ return 0;
+}
+
static int names_mac(struct udev_device *dev, struct netnames *names) {
const char *s;
unsigned int i;
@@ -366,10 +392,11 @@ static int ieee_oui(struct udev_device *dev, struct netnames *names, bool test)
static int builtin_net_id(struct udev_device *dev, int argc, char *argv[], bool test) {
const char *s;
+ const char *p;
unsigned int i;
const char *devtype;
const char *prefix = "en";
- struct netnames names;
+ struct netnames names = {};
int err;
/* handle only ARPHRD_ETHER devices */
@@ -380,6 +407,16 @@ static int builtin_net_id(struct udev_device *dev, int argc, char *argv[], bool
if (i != 1)
return 0;
+ /* skip stacked devices, like VLANs, ... */
+ s = udev_device_get_sysattr_value(dev, "ifindex");
+ if (!s)
+ return EXIT_FAILURE;
+ p = udev_device_get_sysattr_value(dev, "iflink");
+ if (!p)
+ return EXIT_FAILURE;
+ if (!streq(s, p))
+ return 0;
+
devtype = udev_device_get_devtype(dev);
if (devtype) {
if (streq("wlan", devtype))
@@ -388,7 +425,6 @@ static int builtin_net_id(struct udev_device *dev, int argc, char *argv[], bool
prefix = "ww";
}
- zero(names);
err = names_mac(dev, &names);
if (err >= 0 && names.mac_valid) {
char str[IFNAMSIZ];
@@ -440,7 +476,24 @@ static int builtin_net_id(struct udev_device *dev, int argc, char *argv[], bool
if (names.pci_slot[0])
if (snprintf(str, sizeof(str), "%s%s%s", prefix, names.pci_slot, names.usb_ports) < (int)sizeof(str))
udev_builtin_add_property(dev, test, "ID_NET_NAME_SLOT", str);
+ goto out;
+ }
+
+ /* Broadcom bus */
+ err = names_bcma(dev, &names);
+ if (err >= 0 && names.type == NET_BCMA) {
+ char str[IFNAMSIZ];
+
+ if (names.pci_path[0])
+ if (snprintf(str, sizeof(str), "%s%s%s", prefix, names.pci_path, names.bcma_core) < (int)sizeof(str))
+ udev_builtin_add_property(dev, test, "ID_NET_NAME_PATH", str);
+
+ if (names.pci_slot[0])
+ if (snprintf(str, sizeof(str), "%s%s%s", prefix, names.pci_slot, names.bcma_core) < (int)sizeof(str))
+ udev_builtin_add_property(dev, test, "ID_NET_NAME_SLOT", str);
+ goto out;
}
+
out:
return EXIT_SUCCESS;
}
diff --git a/src/udev/udev-builtin-path_id.c b/src/udev/udev-builtin-path_id.c
index c2c9161c94..da0273197b 100644
--- a/src/udev/udev-builtin-path_id.c
+++ b/src/udev/udev-builtin-path_id.c
@@ -83,7 +83,7 @@ static struct udev_device *skip_subsystem(struct udev_device *dev, const char *s
const char *subsystem;
subsystem = udev_device_get_subsystem(parent);
- if (subsystem == NULL || strcmp(subsystem, subsys) != 0)
+ if (subsystem == NULL || !streq(subsystem, subsys))
break;
dev = parent;
parent = udev_device_get_parent(parent);
@@ -345,7 +345,7 @@ static struct udev_device *handle_scsi(struct udev_device *parent, char **path)
const char *id;
devtype = udev_device_get_devtype(parent);
- if (devtype == NULL || strcmp(devtype, "scsi_device") != 0)
+ if (devtype == NULL || !streq(devtype, "scsi_device"))
return parent;
/* firewire */
@@ -356,7 +356,7 @@ static struct udev_device *handle_scsi(struct udev_device *parent, char **path)
goto out;
}
- /* lousy scsi sysfs does not have a "subsystem" for the transport */
+ /* scsi sysfs does not have a "subsystem" for the transport */
name = udev_device_get_syspath(parent);
if (strstr(name, "/rport-") != NULL) {
@@ -375,12 +375,15 @@ static struct udev_device *handle_scsi(struct udev_device *parent, char **path)
}
/*
- * We do not support the ATA transport class, it creates duplicated link
- * names as the fake SCSI host adapters are all separated, they are all
- * re-based as host == 0. ATA should just stop faking two duplicated
- * hierarchies for a single topology and leave the SCSI stuff alone;
- * until that happens, there are no by-path/ links for ATA devices behind
- * an ATA transport class.
+ * We do not support the ATA transport class, it uses global counters
+ * to name the ata devices which numbers spread across multiple
+ * controllers.
+ *
+ * The real link numbers are not exported. Also, possible chains of ports
+ * behind port multipliers cannot be composed that way.
+ *
+ * Until all that is solved at the kernel level, there are no by-path/
+ * links for ATA devices.
*/
if (strstr(name, "/ata") != NULL) {
parent = NULL;
@@ -435,7 +438,7 @@ static struct udev_device *handle_usb(struct udev_device *parent, char **path)
devtype = udev_device_get_devtype(parent);
if (devtype == NULL)
return parent;
- if (strcmp(devtype, "usb_interface") != 0 && strcmp(devtype, "usb_device") != 0)
+ if (!streq(devtype, "usb_interface") && !streq(devtype, "usb_device"))
return parent;
str = udev_device_get_sysname(parent);
@@ -495,37 +498,37 @@ static int builtin_path_id(struct udev_device *dev, int argc, char *argv[], bool
subsys = udev_device_get_subsystem(parent);
if (subsys == NULL) {
;
- } else if (strcmp(subsys, "scsi_tape") == 0) {
+ } else if (streq(subsys, "scsi_tape")) {
handle_scsi_tape(parent, &path);
- } else if (strcmp(subsys, "scsi") == 0) {
+ } else if (streq(subsys, "scsi")) {
parent = handle_scsi(parent, &path);
some_transport = true;
- } else if (strcmp(subsys, "cciss") == 0) {
+ } else if (streq(subsys, "cciss")) {
parent = handle_cciss(parent, &path);
some_transport = true;
- } else if (strcmp(subsys, "usb") == 0) {
+ } else if (streq(subsys, "usb")) {
parent = handle_usb(parent, &path);
some_transport = true;
- } else if (strcmp(subsys, "serio") == 0) {
+ } else if (streq(subsys, "serio")) {
path_prepend(&path, "serio-%s", udev_device_get_sysnum(parent));
parent = skip_subsystem(parent, "serio");
- } else if (strcmp(subsys, "pci") == 0) {
+ } else if (streq(subsys, "pci")) {
path_prepend(&path, "pci-%s", udev_device_get_sysname(parent));
parent = skip_subsystem(parent, "pci");
- } else if (strcmp(subsys, "platform") == 0) {
+ } else if (streq(subsys, "platform")) {
path_prepend(&path, "platform-%s", udev_device_get_sysname(parent));
parent = skip_subsystem(parent, "platform");
some_transport = true;
- } else if (strcmp(subsys, "acpi") == 0) {
+ } else if (streq(subsys, "acpi")) {
path_prepend(&path, "acpi-%s", udev_device_get_sysname(parent));
parent = skip_subsystem(parent, "acpi");
- } else if (strcmp(subsys, "xen") == 0) {
+ } else if (streq(subsys, "xen")) {
path_prepend(&path, "xen-%s", udev_device_get_sysname(parent));
parent = skip_subsystem(parent, "xen");
- } else if (strcmp(subsys, "virtio") == 0) {
+ } else if (streq(subsys, "virtio")) {
path_prepend(&path, "virtio-pci-%s", udev_device_get_sysname(parent));
parent = skip_subsystem(parent, "virtio");
- } else if (strcmp(subsys, "scm") == 0) {
+ } else if (streq(subsys, "scm")) {
path_prepend(&path, "scm-%s", udev_device_get_sysname(parent));
parent = skip_subsystem(parent, "scm");
}
diff --git a/src/udev/udev-builtin-uaccess.c b/src/udev/udev-builtin-uaccess.c
index 662bac9e0b..354ee08f28 100644
--- a/src/udev/udev-builtin-uaccess.c
+++ b/src/udev/udev-builtin-uaccess.c
@@ -29,7 +29,6 @@
#include <dirent.h>
#include <getopt.h>
-#include <systemd/sd-daemon.h>
#include <systemd/sd-login.h>
#include "logind-acl.h"
#include "udev.h"
@@ -49,7 +48,7 @@ static int builtin_uaccess(struct udev_device *dev, int argc, char *argv[], bool
umask(0022);
/* don't muck around with ACLs when the system is not running systemd */
- if (!sd_booted())
+ if (!logind_running())
return 0;
path = udev_device_get_devnode(dev);
diff --git a/src/udev/udev-builtin-usb_id.c b/src/udev/udev-builtin-usb_id.c
index 13d1226393..e3bbd05e4b 100644
--- a/src/udev/udev-builtin-usb_id.c
+++ b/src/udev/udev-builtin-usb_id.c
@@ -111,7 +111,7 @@ static int set_usb_mass_storage_ifsubtype(char *to, const char *from, size_t len
break;
}
}
- util_strscpy(to, len, type);
+ strscpy(to, len, type);
return type_num;
}
@@ -143,7 +143,7 @@ static void set_scsi_type(char *to, const char *from, size_t len)
break;
}
}
- util_strscpy(to, len, type);
+ strscpy(to, len, type);
}
#define USB_DT_DEVICE 0x01
@@ -151,11 +151,12 @@ static void set_scsi_type(char *to, const char *from, size_t len)
static int dev_if_packed_info(struct udev_device *dev, char *ifs_str, size_t len)
{
- char *filename = NULL;
- int fd;
+ _cleanup_free_ char *filename = NULL;
+ _cleanup_close_ int fd = -1;
ssize_t size;
unsigned char buf[18 + 65535];
- unsigned int pos, strpos;
+ int pos = 0;
+ unsigned strpos = 0;
struct usb_interface_descriptor {
u_int8_t bLength;
u_int8_t bDescriptorType;
@@ -167,29 +168,22 @@ static int dev_if_packed_info(struct udev_device *dev, char *ifs_str, size_t len
u_int8_t bInterfaceProtocol;
u_int8_t iInterface;
} __attribute__((packed));
- int err = 0;
- if (asprintf(&filename, "%s/descriptors", udev_device_get_syspath(dev)) < 0) {
- err = -1;
- goto out;
- }
+ if (asprintf(&filename, "%s/descriptors", udev_device_get_syspath(dev)) < 0)
+ return log_oom();
+
fd = open(filename, O_RDONLY|O_CLOEXEC);
if (fd < 0) {
fprintf(stderr, "error opening USB device 'descriptors' file\n");
- err = -1;
- goto out;
+ return -errno;
}
+
size = read(fd, buf, sizeof(buf));
- close(fd);
- if (size < 18 || size == sizeof(buf)) {
- err = -1;
- goto out;
- }
+ if (size < 18 || size == sizeof(buf))
+ return -EIO;
- pos = 0;
- strpos = 0;
ifs_str[0] = '\0';
- while (pos < sizeof(buf) && strpos+7 < len-2) {
+ while (pos < size && strpos+7 < len-2) {
struct usb_interface_descriptor *desc;
char if_str[8];
@@ -213,13 +207,13 @@ static int dev_if_packed_info(struct udev_device *dev, char *ifs_str, size_t len
memcpy(&ifs_str[strpos], if_str, 8),
strpos += 7;
}
+
if (strpos > 0) {
ifs_str[strpos++] = ':';
ifs_str[strpos++] = '\0';
}
-out:
- free(filename);
- return err;
+
+ return 0;
}
/*
@@ -273,7 +267,7 @@ static int builtin_usb_id(struct udev_device *dev, int argc, char *argv[], bool
instance_str[0] = '\0';
/* shortcut, if we are called directly for a "usb_device" type */
- if (udev_device_get_devtype(dev) != NULL && strcmp(udev_device_get_devtype(dev), "usb_device") == 0) {
+ if (udev_device_get_devtype(dev) != NULL && streq(udev_device_get_devtype(dev), "usb_device")) {
dev_if_packed_info(dev, packed_if_str, sizeof(packed_if_str));
dev_usb = dev;
goto fallback;
@@ -432,18 +426,29 @@ fallback:
usb_serial = udev_device_get_sysattr_value(dev_usb, "serial");
if (usb_serial) {
+ const unsigned char *p;
+
+ /* http://msdn.microsoft.com/en-us/library/windows/hardware/gg487321.aspx */
+ for (p = (unsigned char *)usb_serial; *p != '\0'; p++)
+ if (*p < 0x20 || *p > 0x7f || *p == ',') {
+ usb_serial = NULL;
+ break;
+ }
+ }
+
+ if (usb_serial) {
util_replace_whitespace(usb_serial, serial_str, sizeof(serial_str)-1);
util_replace_chars(serial_str, NULL);
}
}
s = serial;
- l = util_strpcpyl(&s, sizeof(serial), vendor_str, "_", model_str, NULL);
+ l = strpcpyl(&s, sizeof(serial), vendor_str, "_", model_str, NULL);
if (serial_str[0] != '\0')
- l = util_strpcpyl(&s, l, "_", serial_str, NULL);
+ l = strpcpyl(&s, l, "_", serial_str, NULL);
if (instance_str[0] != '\0')
- util_strpcpyl(&s, l, "-", instance_str, NULL);
+ strpcpyl(&s, l, "-", instance_str, NULL);
udev_builtin_add_property(dev, test, "ID_VENDOR", vendor_str);
udev_builtin_add_property(dev, test, "ID_VENDOR_ENC", vendor_str_enc);
diff --git a/src/udev/udev-builtin.c b/src/udev/udev-builtin.c
index 32e6e1e90c..c7d431988d 100644
--- a/src/udev/udev-builtin.c
+++ b/src/udev/udev-builtin.c
@@ -34,7 +34,9 @@ static const struct udev_builtin *builtins[] = {
[UDEV_BUILTIN_BLKID] = &udev_builtin_blkid,
#endif
[UDEV_BUILTIN_BTRFS] = &udev_builtin_btrfs,
+#ifdef HAVE_FIRMWARE
[UDEV_BUILTIN_FIRMWARE] = &udev_builtin_firmware,
+#endif
[UDEV_BUILTIN_HWDB] = &udev_builtin_hwdb,
[UDEV_BUILTIN_INPUT_ID] = &udev_builtin_input_id,
#ifdef HAVE_KMOD
@@ -110,12 +112,12 @@ enum udev_builtin_cmd udev_builtin_lookup(const char *command)
enum udev_builtin_cmd i;
char *pos;
- util_strscpy(name, sizeof(name), command);
+ strscpy(name, sizeof(name), command);
pos = strchr(name, ' ');
if (pos)
pos[0] = '\0';
for (i = 0; i < ELEMENTSOF(builtins); i++)
- if (strcmp(builtins[i]->name, name) == 0)
+ if (streq(builtins[i]->name, name))
return i;
return UDEV_BUILTIN_MAX;
}
@@ -128,7 +130,7 @@ int udev_builtin_run(struct udev_device *dev, enum udev_builtin_cmd cmd, const c
/* we need '0' here to reset the internal state */
optind = 0;
- util_strscpy(arg, sizeof(arg), command);
+ strscpy(arg, sizeof(arg), command);
udev_build_argv(udev_device_get_udev(dev), arg, &argc, argv);
return builtins[cmd]->cmd(dev, argc, argv, test);
}
diff --git a/src/udev/udev-ctrl.c b/src/udev/udev-ctrl.c
index a235912ffb..e60da906c5 100644
--- a/src/udev/udev-ctrl.c
+++ b/src/udev/udev-ctrl.c
@@ -73,6 +73,7 @@ struct udev_ctrl_connection {
struct udev_ctrl *udev_ctrl_new_from_fd(struct udev *udev, int fd)
{
struct udev_ctrl *uctrl;
+ const int on = 1;
uctrl = calloc(1, sizeof(struct udev_ctrl));
if (uctrl == NULL)
@@ -91,9 +92,10 @@ struct udev_ctrl *udev_ctrl_new_from_fd(struct udev *udev, int fd)
uctrl->bound = true;
uctrl->sock = fd;
}
+ setsockopt(uctrl->sock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on));
uctrl->saddr.sun_family = AF_LOCAL;
- util_strscpy(uctrl->saddr.sun_path, sizeof(uctrl->saddr.sun_path), "/run/udev/control");
+ strscpy(uctrl->saddr.sun_path, sizeof(uctrl->saddr.sun_path), "/run/udev/control");
uctrl->addrlen = offsetof(struct sockaddr_un, sun_path) + strlen(uctrl->saddr.sun_path);
return uctrl;
}
@@ -250,7 +252,7 @@ static int ctrl_send(struct udev_ctrl *uctrl, enum udev_ctrl_msg_type type, int
ctrl_msg_wire.type = type;
if (buf != NULL)
- util_strscpy(ctrl_msg_wire.buf, sizeof(ctrl_msg_wire.buf), buf);
+ strscpy(ctrl_msg_wire.buf, sizeof(ctrl_msg_wire.buf), buf);
else
ctrl_msg_wire.intval = intval;
diff --git a/src/udev/udev-event.c b/src/udev/udev-event.c
index 5eedf4f0df..3db2cb7165 100644
--- a/src/udev/udev-event.c
+++ b/src/udev/udev-event.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2010 Kay Sievers <kay@vrfy.org>
+ * Copyright (C) 2003-2013 Kay Sievers <kay@vrfy.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -191,20 +191,20 @@ subst:
switch (type) {
case SUBST_DEVPATH:
- l = util_strpcpy(&s, l, udev_device_get_devpath(dev));
+ l = strpcpy(&s, l, udev_device_get_devpath(dev));
break;
case SUBST_KERNEL:
- l = util_strpcpy(&s, l, udev_device_get_sysname(dev));
+ l = strpcpy(&s, l, udev_device_get_sysname(dev));
break;
case SUBST_KERNEL_NUMBER:
if (udev_device_get_sysnum(dev) == NULL)
break;
- l = util_strpcpy(&s, l, udev_device_get_sysnum(dev));
+ l = strpcpy(&s, l, udev_device_get_sysnum(dev));
break;
case SUBST_ID:
if (event->dev_parent == NULL)
break;
- l = util_strpcpy(&s, l, udev_device_get_sysname(event->dev_parent));
+ l = strpcpy(&s, l, udev_device_get_sysname(event->dev_parent));
break;
case SUBST_DRIVER: {
const char *driver;
@@ -215,21 +215,21 @@ subst:
driver = udev_device_get_driver(event->dev_parent);
if (driver == NULL)
break;
- l = util_strpcpy(&s, l, driver);
+ l = strpcpy(&s, l, driver);
break;
}
case SUBST_MAJOR: {
char num[UTIL_PATH_SIZE];
sprintf(num, "%d", major(udev_device_get_devnum(dev)));
- l = util_strpcpy(&s, l, num);
+ l = strpcpy(&s, l, num);
break;
}
case SUBST_MINOR: {
char num[UTIL_PATH_SIZE];
sprintf(num, "%d", minor(udev_device_get_devnum(dev)));
- l = util_strpcpy(&s, l, num);
+ l = strpcpy(&s, l, num);
break;
}
case SUBST_RESULT: {
@@ -247,7 +247,7 @@ subst:
char tmp[UTIL_PATH_SIZE];
char *cpos;
- util_strscpy(result, sizeof(result), event->program_result);
+ strscpy(result, sizeof(result), event->program_result);
cpos = result;
while (--i) {
while (cpos[0] != '\0' && !isspace(cpos[0]))
@@ -259,16 +259,16 @@ subst:
log_error("requested part of result string not found\n");
break;
}
- util_strscpy(tmp, sizeof(tmp), cpos);
+ strscpy(tmp, sizeof(tmp), cpos);
/* %{2+}c copies the whole string from the second part on */
if (rest[0] != '+') {
cpos = strchr(tmp, ' ');
if (cpos)
cpos[0] = '\0';
}
- l = util_strpcpy(&s, l, tmp);
+ l = strpcpy(&s, l, tmp);
} else {
- l = util_strpcpy(&s, l, event->program_result);
+ l = strpcpy(&s, l, event->program_result);
}
break;
}
@@ -300,14 +300,14 @@ subst:
/* strip trailing whitespace, and replace unwanted characters */
if (value != vbuf)
- util_strscpy(vbuf, sizeof(vbuf), value);
+ strscpy(vbuf, sizeof(vbuf), value);
len = strlen(vbuf);
while (len > 0 && isspace(vbuf[--len]))
vbuf[len] = '\0';
count = util_replace_chars(vbuf, UDEV_ALLOWED_CHARS_INPUT);
if (count > 0)
log_debug("%i character(s) replaced\n" , count);
- l = util_strpcpy(&s, l, vbuf);
+ l = strpcpy(&s, l, vbuf);
break;
}
case SUBST_PARENT: {
@@ -319,20 +319,20 @@ subst:
break;
devnode = udev_device_get_devnode(dev_parent);
if (devnode != NULL)
- l = util_strpcpy(&s, l, devnode + strlen("/dev/"));
+ l = strpcpy(&s, l, devnode + strlen("/dev/"));
break;
}
case SUBST_DEVNODE:
if (udev_device_get_devnode(dev) != NULL)
- l = util_strpcpy(&s, l, udev_device_get_devnode(dev));
+ l = strpcpy(&s, l, udev_device_get_devnode(dev));
break;
case SUBST_NAME:
if (event->name != NULL)
- l = util_strpcpy(&s, l, event->name);
+ l = strpcpy(&s, l, event->name);
else if (udev_device_get_devnode(dev) != NULL)
- l = util_strpcpy(&s, l, udev_device_get_devnode(dev) + strlen("/dev/"));
+ l = strpcpy(&s, l, udev_device_get_devnode(dev) + strlen("/dev/"));
else
- l = util_strpcpy(&s, l, udev_device_get_sysname(dev));
+ l = strpcpy(&s, l, udev_device_get_sysname(dev));
break;
case SUBST_LINKS: {
struct udev_list_entry *list_entry;
@@ -340,16 +340,16 @@ subst:
list_entry = udev_device_get_devlinks_list_entry(dev);
if (list_entry == NULL)
break;
- l = util_strpcpy(&s, l, udev_list_entry_get_name(list_entry) + strlen("/dev/"));
+ l = strpcpy(&s, l, udev_list_entry_get_name(list_entry) + strlen("/dev/"));
udev_list_entry_foreach(list_entry, udev_list_entry_get_next(list_entry))
- l = util_strpcpyl(&s, l, " ", udev_list_entry_get_name(list_entry) + strlen("/dev/"), NULL);
+ l = strpcpyl(&s, l, " ", udev_list_entry_get_name(list_entry) + strlen("/dev/"), NULL);
break;
}
case SUBST_ROOT:
- l = util_strpcpy(&s, l, "/dev");
+ l = strpcpy(&s, l, "/dev");
break;
case SUBST_SYS:
- l = util_strpcpy(&s, l, "/sys");
+ l = strpcpy(&s, l, "/sys");
break;
case SUBST_ENV:
if (attr == NULL) {
@@ -360,7 +360,7 @@ subst:
value = udev_device_get_property_value(event->dev, attr);
if (value == NULL)
break;
- l = util_strpcpy(&s, l, value);
+ l = strpcpy(&s, l, value);
break;
}
default:
@@ -667,7 +667,7 @@ int udev_event_spawn(struct udev_event *event,
char program[UTIL_PATH_SIZE];
int err = 0;
- util_strscpy(arg, sizeof(arg), cmd);
+ strscpy(arg, sizeof(arg), cmd);
udev_build_argv(event->udev, arg, NULL, argv);
/* pipes from child to parent */
@@ -688,7 +688,7 @@ int udev_event_spawn(struct udev_event *event,
/* allow programs in /usr/lib/udev/ to be called without the path */
if (argv[0][0] != '/') {
- util_strscpyl(program, sizeof(program), UDEVLIBEXECDIR "/", argv[0], NULL);
+ strscpyl(program, sizeof(program), UDEVLIBEXECDIR "/", argv[0], NULL);
argv[0] = program;
}
@@ -763,8 +763,8 @@ static int rename_netif(struct udev_event *event)
}
memset(&ifr, 0x00, sizeof(struct ifreq));
- util_strscpy(ifr.ifr_name, IFNAMSIZ, udev_device_get_sysname(dev));
- util_strscpy(ifr.ifr_newname, IFNAMSIZ, event->name);
+ strscpy(ifr.ifr_name, IFNAMSIZ, udev_device_get_sysname(dev));
+ strscpy(ifr.ifr_newname, IFNAMSIZ, event->name);
err = ioctl(sk, SIOCSIFNAME, &ifr);
if (err >= 0) {
print_kmsg("renamed network interface %s to %s\n", ifr.ifr_name, ifr.ifr_newname);
@@ -784,7 +784,7 @@ int udev_event_execute_rules(struct udev_event *event, struct udev_rules *rules,
if (udev_device_get_subsystem(dev) == NULL)
return -1;
- if (strcmp(udev_device_get_action(dev), "remove") == 0) {
+ if (streq(udev_device_get_action(dev), "remove")) {
udev_device_read_db(dev, NULL);
udev_device_delete_db(dev);
udev_device_tag_index(dev, NULL, false);
@@ -812,8 +812,8 @@ int udev_event_execute_rules(struct udev_event *event, struct udev_rules *rules,
udev_rules_apply_to_event(rules, event, sigmask);
/* rename a new network interface, if needed */
- if (udev_device_get_ifindex(dev) > 0 && strcmp(udev_device_get_action(dev), "add") == 0 &&
- event->name != NULL && strcmp(event->name, udev_device_get_sysname(dev)) != 0) {
+ if (udev_device_get_ifindex(dev) > 0 && streq(udev_device_get_action(dev), "add") &&
+ event->name != NULL && !streq(event->name, udev_device_get_sysname(dev))) {
char syspath[UTIL_PATH_SIZE];
char *pos;
@@ -825,11 +825,11 @@ int udev_event_execute_rules(struct udev_event *event, struct udev_rules *rules,
udev_device_add_property(dev, "INTERFACE_OLD", udev_device_get_sysname(dev));
/* now change the devpath, because the kernel device name has changed */
- util_strscpy(syspath, sizeof(syspath), udev_device_get_syspath(dev));
+ strscpy(syspath, sizeof(syspath), udev_device_get_syspath(dev));
pos = strrchr(syspath, '/');
if (pos != NULL) {
pos++;
- util_strscpy(pos, sizeof(syspath) - (pos - syspath), event->name);
+ strscpy(pos, sizeof(syspath) - (pos - syspath), event->name);
udev_device_set_syspath(event->dev, syspath);
udev_device_add_property(dev, "INTERFACE", udev_device_get_sysname(dev));
log_debug("changed devpath to '%s'\n", udev_device_get_devpath(dev));
@@ -838,6 +838,8 @@ int udev_event_execute_rules(struct udev_event *event, struct udev_rules *rules,
}
if (major(udev_device_get_devnum(dev)) > 0) {
+ bool apply;
+
/* remove/update possible left-over symlinks from old database entry */
if (event->dev_db != NULL)
udev_node_update_old_links(dev, event->dev_db);
@@ -861,7 +863,8 @@ int udev_event_execute_rules(struct udev_event *event, struct udev_rules *rules,
}
}
- udev_node_add(dev, event->mode, event->uid, event->gid);
+ apply = streq(udev_device_get_action(dev), "add") || event->owner_set || event->group_set || event->mode_set;
+ udev_node_add(dev, apply, event->mode, event->uid, event->gid);
}
/* preserve old, or get new initialization timestamp */
diff --git a/src/udev/udev-node.c b/src/udev/udev-node.c
index 1e378adf29..1148a1529c 100644
--- a/src/udev/udev-node.c
+++ b/src/udev/udev-node.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2010 Kay Sievers <kay@vrfy.org>
+ * Copyright (C) 2003-2013 Kay Sievers <kay@vrfy.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -31,15 +31,13 @@
#include "udev.h"
-#define TMP_FILE_EXT ".udev-tmp"
-
-static int node_symlink(struct udev *udev, const char *node, const char *slink)
+static int node_symlink(struct udev_device *dev, const char *node, const char *slink)
{
struct stat stats;
char target[UTIL_PATH_SIZE];
char *s;
size_t l;
- char slink_tmp[UTIL_PATH_SIZE + sizeof(TMP_FILE_EXT)];
+ char slink_tmp[UTIL_PATH_SIZE + 32];
int i = 0;
int tail = 0;
int err = 0;
@@ -55,10 +53,10 @@ static int node_symlink(struct udev *udev, const char *node, const char *slink)
l = sizeof(target);
while (slink[i] != '\0') {
if (slink[i] == '/')
- l = util_strpcpy(&s, l, "../");
+ l = strpcpy(&s, l, "../");
i++;
}
- l = util_strscpy(s, l, &node[tail]);
+ l = strscpy(s, l, &node[tail]);
if (l == 0) {
err = -EINVAL;
goto exit;
@@ -76,7 +74,7 @@ static int node_symlink(struct udev *udev, const char *node, const char *slink)
len = readlink(slink, buf, sizeof(buf));
if (len > 0 && len < (int)sizeof(buf)) {
buf[len] = '\0';
- if (strcmp(target, buf) == 0) {
+ if (streq(target, buf)) {
log_debug("preserve already existing symlink '%s' to '%s'\n", slink, target);
label_fix(slink, true, false);
utimensat(AT_FDCWD, slink, NULL, AT_SYMLINK_NOFOLLOW);
@@ -101,7 +99,7 @@ static int node_symlink(struct udev *udev, const char *node, const char *slink)
}
log_debug("atomically replace '%s'\n", slink);
- util_strscpyl(slink_tmp, sizeof(slink_tmp), slink, TMP_FILE_EXT, NULL);
+ strscpyl(slink_tmp, sizeof(slink_tmp), slink, ".tmp-", udev_device_get_id_filename(dev), NULL);
unlink(slink_tmp);
do {
err = mkdir_parents_label(slink_tmp, 0755);
@@ -136,7 +134,7 @@ static const char *link_find_prioritized(struct udev_device *dev, bool add, cons
if (add) {
priority = udev_device_get_devlink_priority(dev);
- util_strscpy(buf, bufsize, udev_device_get_devnode(dev));
+ strscpy(buf, bufsize, udev_device_get_devnode(dev));
target = buf;
}
@@ -156,7 +154,7 @@ static const char *link_find_prioritized(struct udev_device *dev, bool add, cons
log_debug("found '%s' claiming '%s'\n", dent->d_name, stackdir);
/* did we find ourself? */
- if (strcmp(dent->d_name, udev_device_get_id_filename(dev)) == 0)
+ if (streq(dent->d_name, udev_device_get_id_filename(dev)))
continue;
dev_db = udev_device_new_from_device_id(udev, dent->d_name);
@@ -169,7 +167,7 @@ static const char *link_find_prioritized(struct udev_device *dev, bool add, cons
log_debug("'%s' claims priority %i for '%s'\n",
udev_device_get_syspath(dev_db), udev_device_get_devlink_priority(dev_db), stackdir);
priority = udev_device_get_devlink_priority(dev_db);
- util_strscpy(buf, bufsize, devnode);
+ strscpy(buf, bufsize, devnode);
target = buf;
}
}
@@ -191,8 +189,8 @@ static void link_update(struct udev_device *dev, const char *slink, bool add)
char buf[UTIL_PATH_SIZE];
util_path_encode(slink + strlen("/dev"), name_enc, sizeof(name_enc));
- util_strscpyl(dirname, sizeof(dirname), "/run/udev/links/", name_enc, NULL);
- util_strscpyl(filename, sizeof(filename), dirname, "/", udev_device_get_id_filename(dev), NULL);
+ strscpyl(dirname, sizeof(dirname), "/run/udev/links/", name_enc, NULL);
+ strscpyl(filename, sizeof(filename), dirname, "/", udev_device_get_id_filename(dev), NULL);
if (!add && unlink(filename) == 0)
rmdir(dirname);
@@ -204,7 +202,7 @@ static void link_update(struct udev_device *dev, const char *slink, bool add)
util_delete_path(udev, slink);
} else {
log_debug("creating link '%s' to '%s'\n", slink, target);
- node_symlink(udev, target, slink);
+ node_symlink(dev, target, slink);
}
if (add) {
@@ -240,7 +238,7 @@ void udev_node_update_old_links(struct udev_device *dev, struct udev_device *dev
udev_list_entry_foreach(list_entry_current, udev_device_get_devlinks_list_entry(dev)) {
const char *name_current = udev_list_entry_get_name(list_entry_current);
- if (strcmp(name, name_current) == 0) {
+ if (streq(name, name_current)) {
found = 1;
break;
}
@@ -254,14 +252,14 @@ void udev_node_update_old_links(struct udev_device *dev, struct udev_device *dev
}
}
-static int node_fixup(struct udev_device *dev, mode_t mode, uid_t uid, gid_t gid)
+static int node_permissions_apply(struct udev_device *dev, bool apply, mode_t mode, uid_t uid, gid_t gid)
{
const char *devnode = udev_device_get_devnode(dev);
dev_t devnum = udev_device_get_devnum(dev);
struct stat stats;
int err = 0;
- if (strcmp(udev_device_get_subsystem(dev), "block") == 0)
+ if (streq(udev_device_get_subsystem(dev), "block"))
mode |= S_IFBLK;
else
mode |= S_IFCHR;
@@ -279,13 +277,7 @@ static int node_fixup(struct udev_device *dev, mode_t mode, uid_t uid, gid_t gid
goto out;
}
- /*
- * Set permissions and selinux file context only on add events. We always
- * set it on bootup (coldplug) with "trigger --action=add" for all devices
- * and for any newly added devices (hotplug). We don't want to change it
- * later, in case something else has applied custom settings in the meantime.
- */
- if (strcmp(udev_device_get_action(dev), "add") == 0) {
+ if (apply) {
if ((stats.st_mode & 0777) != (mode & 0777) || stats.st_uid != uid || stats.st_gid != gid) {
log_debug("set permissions %s, %#o, uid=%u, gid=%u\n", devnode, mode, uid, gid);
chmod(devnode, mode);
@@ -293,7 +285,6 @@ static int node_fixup(struct udev_device *dev, mode_t mode, uid_t uid, gid_t gid
} else {
log_debug("preserve permissions %s, %#o, uid=%u, gid=%u\n", devnode, mode, uid, gid);
}
-
label_fix(devnode, true, false);
}
@@ -303,23 +294,22 @@ out:
return err;
}
-void udev_node_add(struct udev_device *dev, mode_t mode, uid_t uid, gid_t gid)
+void udev_node_add(struct udev_device *dev, bool apply, mode_t mode, uid_t uid, gid_t gid)
{
- struct udev *udev = udev_device_get_udev(dev);
char filename[UTIL_PATH_SIZE];
struct udev_list_entry *list_entry;
log_debug("handling device node '%s', devnum=%s, mode=%#o, uid=%d, gid=%d\n",
udev_device_get_devnode(dev), udev_device_get_id_filename(dev), mode, uid, gid);
- if (node_fixup(dev, mode, uid, gid) < 0)
+ if (node_permissions_apply(dev, apply, mode, uid, gid) < 0)
return;
/* always add /dev/{block,char}/$major:$minor */
snprintf(filename, sizeof(filename), "/dev/%s/%u:%u",
- strcmp(udev_device_get_subsystem(dev), "block") == 0 ? "block" : "char",
+ streq(udev_device_get_subsystem(dev), "block") ? "block" : "char",
major(udev_device_get_devnum(dev)), minor(udev_device_get_devnum(dev)));
- node_symlink(udev, udev_device_get_devnode(dev), filename);
+ node_symlink(dev, udev_device_get_devnode(dev), filename);
/* create/update symlinks, add symlinks to name index */
udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(dev))
@@ -337,7 +327,7 @@ void udev_node_remove(struct udev_device *dev)
/* remove /dev/{block,char}/$major:$minor */
snprintf(filename, sizeof(filename), "/dev/%s/%u:%u",
- strcmp(udev_device_get_subsystem(dev), "block") == 0 ? "block" : "char",
+ streq(udev_device_get_subsystem(dev), "block") ? "block" : "char",
major(udev_device_get_devnum(dev)), minor(udev_device_get_devnum(dev)));
unlink(filename);
}
diff --git a/src/udev/udev-rules.c b/src/udev/udev-rules.c
index 9743243a37..7a4fb70258 100644
--- a/src/udev/udev-rules.c
+++ b/src/udev/udev-rules.c
@@ -55,7 +55,7 @@ struct udev_rules {
unsigned int token_cur;
unsigned int token_max;
- /* all key strings are copied and de-duplicated in a single continous string buffer */
+ /* all key strings are copied and de-duplicated in a single continuous string buffer */
struct strbuf *strbuf;
/* during rule parsing, uid/gid lookup results are cached */
@@ -600,7 +600,7 @@ static int import_property_from_string(struct udev_device *dev, char *line)
log_debug("updating devpath from '%s' to '%s'\n",
udev_device_get_devpath(dev), val);
- util_strscpyl(syspath, sizeof(syspath), "/sys", val, NULL);
+ strscpyl(syspath, sizeof(syspath), "/sys", val, NULL);
udev_device_set_syspath(dev, syspath);
} else {
struct udev_list_entry *entry;
@@ -691,8 +691,8 @@ static int wait_for_file(struct udev_device *dev, const char *file, int timeout)
/* a relative path is a device attribute */
devicepath[0] = '\0';
if (file[0] != '/') {
- util_strscpyl(devicepath, sizeof(devicepath), udev_device_get_syspath(dev), NULL);
- util_strscpyl(filepath, sizeof(filepath), devicepath, "/", file, NULL);
+ strscpyl(devicepath, sizeof(devicepath), udev_device_get_syspath(dev), NULL);
+ strscpyl(filepath, sizeof(filepath), devicepath, "/", file, NULL);
file = filepath;
}
@@ -726,7 +726,7 @@ static int attr_subst_subdir(char *attr, size_t len)
const char *tail;
DIR *dir;
- util_strscpy(dirname, sizeof(dirname), attr);
+ strscpy(dirname, sizeof(dirname), attr);
pos = strstr(dirname, "/*/");
if (pos == NULL)
return -1;
@@ -741,7 +741,7 @@ static int attr_subst_subdir(char *attr, size_t len)
if (dent->d_name[0] == '.')
continue;
- util_strscpyl(attr, len, dirname, "/", dent->d_name, tail, NULL);
+ strscpyl(attr, len, dirname, "/", dent->d_name, tail, NULL);
if (stat(attr, &stats) == 0) {
found = true;
break;
@@ -1619,7 +1619,7 @@ struct udev_rules *udev_rules_new(struct udev *udev, int resolve_names)
return udev_rules_unref(rules);
udev_rules_check_timestamp(rules);
- r = conf_files_list_strv(&files, ".rules", (const char **)rules->dirs);
+ r = conf_files_list_strv(&files, ".rules", NULL, (const char **)rules->dirs);
if (r < 0) {
log_error("failed to enumerate rules files: %s\n", strerror(-r));
return udev_rules_unref(rules);
@@ -1737,7 +1737,7 @@ static int match_key(struct udev_rules *rules, struct token *token, const char *
if (next != NULL) {
size_t matchlen = (size_t)(next - s);
- match = (matchlen == len && strncmp(s, val, matchlen) == 0);
+ match = (matchlen == len && strneq(s, val, matchlen));
if (match)
break;
} else {
@@ -1752,7 +1752,7 @@ static int match_key(struct udev_rules *rules, struct token *token, const char *
{
char value[UTIL_PATH_SIZE];
- util_strscpy(value, sizeof(value), rules_str(rules, token->key.value_off));
+ strscpy(value, sizeof(value), rules_str(rules, token->key.value_off));
key_value = value;
while (key_value != NULL) {
pos = strchr(key_value, '|');
@@ -1819,7 +1819,7 @@ static int match_attr(struct udev_rules *rules, struct udev_device *dev, struct
klen = strlen(key_value);
if (klen > 0 && !isspace(key_value[klen-1])) {
if (value != vbuf) {
- util_strscpy(vbuf, sizeof(vbuf), value);
+ strscpy(vbuf, sizeof(vbuf), value);
value = vbuf;
}
while (len > 0 && isspace(vbuf[--len]))
@@ -2015,8 +2015,8 @@ int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event
if (filename[0] != '/') {
char tmp[UTIL_PATH_SIZE];
- util_strscpy(tmp, sizeof(tmp), filename);
- util_strscpyl(filename, sizeof(filename),
+ strscpy(tmp, sizeof(tmp), filename);
+ strscpyl(filename, sizeof(filename),
udev_device_get_syspath(event->dev), "/", tmp, NULL);
}
}
@@ -2328,7 +2328,7 @@ int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event
/* append value separated by space */
udev_event_apply_format(event, value, temp, sizeof(temp));
- util_strscpyl(value_new, sizeof(value_new), value_old, " ", temp, NULL);
+ strscpyl(value_new, sizeof(value_new), value_old, " ", temp, NULL);
} else
udev_event_apply_format(event, value, value_new, sizeof(value_new));
@@ -2419,7 +2419,7 @@ int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event
next[0] = '\0';
log_debug("LINK '%s' %s:%u\n", pos,
rules_str(rules, rule->rule.filename_off), rule->rule.filename_line);
- util_strscpyl(filename, sizeof(filename), "/dev/", pos, NULL);
+ strscpyl(filename, sizeof(filename), "/dev/", pos, NULL);
udev_device_add_devlink(event->dev, filename);
while (isspace(next[1]))
next++;
@@ -2429,7 +2429,7 @@ int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event
if (pos[0] != '\0') {
log_debug("LINK '%s' %s:%u\n", pos,
rules_str(rules, rule->rule.filename_off), rule->rule.filename_line);
- util_strscpyl(filename, sizeof(filename), "/dev/", pos, NULL);
+ strscpyl(filename, sizeof(filename), "/dev/", pos, NULL);
udev_device_add_devlink(event->dev, filename);
}
break;
@@ -2441,7 +2441,7 @@ int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event
FILE *f;
if (util_resolve_subsys_kernel(event->udev, key_name, attr, sizeof(attr), 0) != 0)
- util_strscpyl(attr, sizeof(attr), udev_device_get_syspath(event->dev), "/", key_name, NULL);
+ strscpyl(attr, sizeof(attr), udev_device_get_syspath(event->dev), "/", key_name, NULL);
attr_subst_subdir(attr, sizeof(attr));
udev_event_apply_format(event, rules_str(rules, cur->key.value_off), value, sizeof(value));
@@ -2539,7 +2539,7 @@ void udev_rules_apply_static_dev_perms(struct udev_rules *rules)
/* we assure, that the permissions tokens are sorted before the static token */
if (mode == 0 && uid == 0 && gid == 0)
goto next;
- util_strscpyl(filename, sizeof(filename), "/dev/", rules_str(rules, cur->key.value_off), NULL);
+ strscpyl(filename, sizeof(filename), "/dev/", rules_str(rules, cur->key.value_off), NULL);
if (stat(filename, &stats) != 0)
goto next;
if (!S_ISBLK(stats.st_mode) && !S_ISCHR(stats.st_mode))
diff --git a/src/udev/udev-watch.c b/src/udev/udev-watch.c
index 311f5bdf23..9b694c6666 100644
--- a/src/udev/udev-watch.c
+++ b/src/udev/udev-watch.c
@@ -116,7 +116,7 @@ void udev_watch_begin(struct udev *udev, struct udev_device *dev)
unlink(filename);
r = symlink(udev_device_get_id_filename(dev), filename);
if (r < 0)
- log_error("Failed to create symlink: %m");
+ log_error("Failed to create symlink %s: %m", filename);
udev_device_set_watch_handle(dev, wd);
}
diff --git a/src/udev/udev.h b/src/udev/udev.h
index 72a7623e34..caec5f0a5d 100644
--- a/src/udev/udev.h
+++ b/src/udev/udev.h
@@ -16,13 +16,13 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef _UDEV_H_
-#define _UDEV_H_
+#pragma once
#include <sys/types.h>
#include <sys/param.h>
#include <signal.h>
+#include "macro.h"
#include "libudev.h"
#include "libudev-private.h"
#include "util.h"
@@ -95,7 +95,7 @@ void udev_watch_end(struct udev *udev, struct udev_device *dev);
struct udev_device *udev_watch_lookup(struct udev *udev, int wd);
/* udev-node.c */
-void udev_node_add(struct udev_device *dev, mode_t mode, uid_t uid, gid_t gid);
+void udev_node_add(struct udev_device *dev, bool apply, mode_t mode, uid_t uid, gid_t gid);
void udev_node_remove(struct udev_device *dev);
void udev_node_update_old_links(struct udev_device *dev, struct udev_device *dev_old);
@@ -140,7 +140,9 @@ enum udev_builtin_cmd {
UDEV_BUILTIN_BLKID,
#endif
UDEV_BUILTIN_BTRFS,
+#ifdef HAVE_FIRMWARE
UDEV_BUILTIN_FIRMWARE,
+#endif
UDEV_BUILTIN_HWDB,
UDEV_BUILTIN_INPUT_ID,
#ifdef HAVE_KMOD
@@ -167,7 +169,9 @@ struct udev_builtin {
extern const struct udev_builtin udev_builtin_blkid;
#endif
extern const struct udev_builtin udev_builtin_btrfs;
+#ifdef HAVE_FIRMWARE
extern const struct udev_builtin udev_builtin_firmware;
+#endif
extern const struct udev_builtin udev_builtin_hwdb;
extern const struct udev_builtin udev_builtin_input_id;
#ifdef HAVE_KMOD
@@ -191,7 +195,7 @@ int udev_builtin_hwdb_lookup(struct udev_device *dev, const char *modalias, bool
/* udev logging */
void udev_main_log(struct udev *udev, int priority,
const char *file, int line, const char *fn,
- const char *format, va_list args);
+ const char *format, va_list args) _printf_attr_(6, 0);
/* udevadm commands */
struct udevadm_cmd {
@@ -208,4 +212,3 @@ extern const struct udevadm_cmd udevadm_monitor;
extern const struct udevadm_cmd udevadm_hwdb;
extern const struct udevadm_cmd udevadm_test;
extern const struct udevadm_cmd udevadm_test_builtin;
-#endif
diff --git a/src/udev/udevadm-hwdb.c b/src/udev/udevadm-hwdb.c
index 00c0d3d4c4..3e849aaed6 100644
--- a/src/udev/udevadm-hwdb.c
+++ b/src/udev/udevadm-hwdb.c
@@ -84,14 +84,12 @@ static int trie_children_cmp(const void *v1, const void *v2) {
static int node_add_child(struct trie *trie, struct trie_node *node, struct trie_node *node_child, uint8_t c) {
struct trie_child_entry *child;
- int err = 0;
/* extend array, add new entry, sort for bisection */
child = realloc(node->children, (node->children_count + 1) * sizeof(struct trie_child_entry));
- if (!child) {
- err = -ENOMEM;
- goto out;
- }
+ if (!child)
+ return -ENOMEM;
+
node->children = child;
trie->children_count++;
node->children[node->children_count].c = c;
@@ -99,8 +97,8 @@ static int node_add_child(struct trie *trie, struct trie_node *node, struct trie
node->children_count++;
qsort(node->children, node->children_count, sizeof(struct trie_child_entry), trie_children_cmp);
trie->nodes_count++;
-out:
- return err;
+
+ return 0;
}
static struct trie_node *node_lookup(const struct trie_node *node, uint8_t c) {
@@ -183,46 +181,44 @@ static int trie_insert(struct trie *trie, struct trie_node *node, const char *se
struct trie_node *child;
for (p = 0; (c = trie->strings->buf[node->prefix_off + p]); p++) {
- char *s;
+ _cleanup_free_ char *s = NULL;
ssize_t off;
+ _cleanup_free_ struct trie_node *new_child = NULL;
if (c == search[i + p])
continue;
/* split node */
- child = calloc(sizeof(struct trie_node), 1);
- if (!child) {
- err = -ENOMEM;
- goto out;
- }
+ new_child = calloc(sizeof(struct trie_node), 1);
+ if (!new_child)
+ return -ENOMEM;
/* move values from parent to child */
- child->prefix_off = node->prefix_off + p+1;
- child->children = node->children;
- child->children_count = node->children_count;
- child->values = node->values;
- child->values_count = node->values_count;
+ new_child->prefix_off = node->prefix_off + p+1;
+ new_child->children = node->children;
+ new_child->children_count = node->children_count;
+ new_child->values = node->values;
+ new_child->values_count = node->values_count;
/* update parent; use strdup() because the source gets realloc()d */
s = strndup(trie->strings->buf + node->prefix_off, p);
- if (!s) {
- err = -ENOMEM;
- goto out;
- }
+ if (!s)
+ return -ENOMEM;
+
off = strbuf_add_string(trie->strings, s, p);
- free(s);
- if (off < 0) {
- err = off;
- goto out;
- }
+ if (off < 0)
+ return off;
+
node->prefix_off = off;
node->children = NULL;
node->children_count = 0;
node->values = NULL;
node->values_count = 0;
- err = node_add_child(trie, node, child, c);
+ err = node_add_child(trie, node, new_child, c);
if (err)
- goto out;
+ return err;
+
+ new_child = NULL; /* avoid cleanup */
break;
}
i += p;
@@ -237,27 +233,28 @@ static int trie_insert(struct trie *trie, struct trie_node *node, const char *se
/* new child */
child = calloc(sizeof(struct trie_node), 1);
- if (!child) {
- err = -ENOMEM;
- goto out;
- }
+ if (!child)
+ return -ENOMEM;
+
off = strbuf_add_string(trie->strings, search + i+1, strlen(search + i+1));
if (off < 0) {
- err = off;
- goto out;
+ free(child);
+ return off;
}
+
child->prefix_off = off;
err = node_add_child(trie, node, child, c);
- if (err)
- goto out;
+ if (err) {
+ free(child);
+ return err;
+ }
+
return trie_node_add_value(trie, child, key, value);
}
node = child;
i++;
}
-out:
- return err;
}
struct trie_f {
@@ -471,18 +468,21 @@ static int import_file(struct trie *trie, const char *filename) {
static void help(void) {
printf("Usage: udevadm hwdb OPTIONS\n"
" --update update the hardware database\n"
- " --test <modalias> query database and print result\n"
+ " --test=<modalias> query database and print result\n"
+ " --root=<path> alternative root path in the filesystem\n"
" --help\n\n");
}
static int adm_hwdb(struct udev *udev, int argc, char *argv[]) {
static const struct option options[] = {
{ "update", no_argument, NULL, 'u' },
+ { "root", required_argument, NULL, 'r' },
{ "test", required_argument, NULL, 't' },
{ "help", no_argument, NULL, 'h' },
{}
};
const char *test = NULL;
+ const char *root = "";
bool update = false;
struct trie *trie = NULL;
int err;
@@ -491,7 +491,7 @@ static int adm_hwdb(struct udev *udev, int argc, char *argv[]) {
for (;;) {
int option;
- option = getopt_long(argc, argv, "ut:h", options, NULL);
+ option = getopt_long(argc, argv, "ut:r:h", options, NULL);
if (option == -1)
break;
@@ -502,6 +502,9 @@ static int adm_hwdb(struct udev *udev, int argc, char *argv[]) {
case 't':
test = optarg;
break;
+ case 'r':
+ root = optarg;
+ break;
case 'h':
help();
return EXIT_SUCCESS;
@@ -515,6 +518,7 @@ static int adm_hwdb(struct udev *udev, int argc, char *argv[]) {
if (update) {
char **files, **f;
+ _cleanup_free_ char *hwdb_bin = NULL;
trie = calloc(sizeof(struct trie), 1);
if (!trie) {
@@ -537,7 +541,7 @@ static int adm_hwdb(struct udev *udev, int argc, char *argv[]) {
}
trie->nodes_count++;
- err = conf_files_list_strv(&files, ".hwdb", (const char **)conf_file_dirs);
+ err = conf_files_list_strv(&files, ".hwdb", root, conf_file_dirs);
if (err < 0) {
log_error("failed to enumerate hwdb files: %s\n", strerror(-err));
rc = EXIT_FAILURE;
@@ -565,11 +569,14 @@ static int adm_hwdb(struct udev *udev, int argc, char *argv[]) {
log_debug("strings dedup'ed: %8zu bytes (%8zu)\n",
trie->strings->dedup_len, trie->strings->dedup_count);
- mkdir_parents(HWDB_BIN, 0755);
- err = trie_store(trie, HWDB_BIN);
+ if (asprintf(&hwdb_bin, "%s/etc/udev/hwdb.bin", root) < 0) {
+ rc = EXIT_FAILURE;
+ goto out;
+ }
+ mkdir_parents(hwdb_bin, 0755);
+ err = trie_store(trie, hwdb_bin);
if (err < 0) {
- log_error("Failure writing hardware database '%s': %s",
- HWDB_BIN, strerror(-err));
+ log_error("Failure writing database %s: %s", hwdb_bin, strerror(-err));
rc = EXIT_FAILURE;
}
}
diff --git a/src/udev/udevadm-info.c b/src/udev/udevadm-info.c
index 95f077ca95..002876594f 100644
--- a/src/udev/udevadm-info.c
+++ b/src/udev/udevadm-info.c
@@ -33,7 +33,7 @@
static bool skip_attribute(const char *name)
{
- static const char const *skip[] = {
+ static const char* const skip[] = {
"uevent",
"dev",
"modalias",
@@ -45,7 +45,7 @@ static bool skip_attribute(const char *name)
unsigned int i;
for (i = 0; i < ELEMENTSOF(skip); i++)
- if (strcmp(name, skip[i]) == 0)
+ if (streq(name, skip[i]))
return true;
return false;
}
@@ -256,12 +256,6 @@ static void cleanup_db(struct udev *udev)
cleanup_dir(dir, 0, 1);
closedir(dir);
}
-
- dir = opendir("/run/udev/firmware-missing");
- if (dir != NULL) {
- cleanup_dir(dir, 0, 1);
- closedir(dir);
- }
}
static struct udev_device *find_device(struct udev *udev, const char *id, const char *prefix)
@@ -269,7 +263,7 @@ static struct udev_device *find_device(struct udev *udev, const char *id, const
char name[UTIL_PATH_SIZE];
if (prefix && !startswith(id, prefix)) {
- util_strscpyl(name, sizeof(name), prefix, id, NULL);
+ strscpyl(name, sizeof(name), prefix, id, NULL);
id = name;
}
@@ -393,15 +387,15 @@ static int uinfo(struct udev *udev, int argc, char *argv[])
break;
case 'q':
action = ACTION_QUERY;
- if (strcmp(optarg, "property") == 0 || strcmp(optarg, "env") == 0) {
+ if (streq(optarg, "property") || streq(optarg, "env")) {
query = QUERY_PROPERTY;
- } else if (strcmp(optarg, "name") == 0) {
+ } else if (streq(optarg, "name")) {
query = QUERY_NAME;
- } else if (strcmp(optarg, "symlink") == 0) {
+ } else if (streq(optarg, "symlink")) {
query = QUERY_SYMLINK;
- } else if (strcmp(optarg, "path") == 0) {
+ } else if (streq(optarg, "path")) {
query = QUERY_PATH;
- } else if (strcmp(optarg, "all") == 0) {
+ } else if (streq(optarg, "all")) {
query = QUERY_ALL;
} else {
fprintf(stderr, "unknown query type\n");
@@ -414,7 +408,7 @@ static int uinfo(struct udev *udev, int argc, char *argv[])
break;
case 'd':
action = ACTION_DEVICE_ID_FILE;
- util_strscpy(name, sizeof(name), optarg);
+ strscpy(name, sizeof(name), optarg);
break;
case 'a':
action = ACTION_ATTRIBUTE_WALK;
diff --git a/src/udev/udevadm-monitor.c b/src/udev/udevadm-monitor.c
index ffa70d8300..a390ee6c34 100644
--- a/src/udev/udevadm-monitor.c
+++ b/src/udev/udevadm-monitor.c
@@ -116,7 +116,7 @@ static int adm_monitor(struct udev *udev, int argc, char *argv[])
char subsys[UTIL_NAME_SIZE];
char *devtype;
- util_strscpy(subsys, sizeof(subsys), optarg);
+ strscpy(subsys, sizeof(subsys), optarg);
devtype = strchr(subsys, '/');
if (devtype != NULL) {
devtype[0] = '\0';
diff --git a/src/udev/udevadm-test-builtin.c b/src/udev/udevadm-test-builtin.c
index 9853d83b49..f4aa21ee72 100644
--- a/src/udev/udevadm-test-builtin.c
+++ b/src/udev/udevadm-test-builtin.c
@@ -96,9 +96,9 @@ static int adm_builtin(struct udev *udev, int argc, char *argv[])
/* add /sys if needed */
if (!startswith(syspath, "/sys"))
- util_strscpyl(filename, sizeof(filename), "/sys", syspath, NULL);
+ strscpyl(filename, sizeof(filename), "/sys", syspath, NULL);
else
- util_strscpy(filename, sizeof(filename), syspath);
+ strscpy(filename, sizeof(filename), syspath);
util_remove_trailing_chars(filename, '/');
dev = udev_device_new_from_syspath(udev, filename);
diff --git a/src/udev/udevadm-test.c b/src/udev/udevadm-test.c
index 2d8aa7913e..df1409bff6 100644
--- a/src/udev/udevadm-test.c
+++ b/src/udev/udevadm-test.c
@@ -66,11 +66,11 @@ static int adm_test(struct udev *udev, int argc, char *argv[])
action = optarg;
break;
case 'N':
- if (strcmp (optarg, "early") == 0) {
+ if (streq (optarg, "early")) {
resolve_names = 1;
- } else if (strcmp (optarg, "late") == 0) {
+ } else if (streq (optarg, "late")) {
resolve_names = 0;
- } else if (strcmp (optarg, "never") == 0) {
+ } else if (streq (optarg, "never")) {
resolve_names = -1;
} else {
fprintf(stderr, "resolve-names must be early, late or never\n");
@@ -113,9 +113,9 @@ static int adm_test(struct udev *udev, int argc, char *argv[])
/* add /sys if needed */
if (!startswith(syspath, "/sys"))
- util_strscpyl(filename, sizeof(filename), "/sys", syspath, NULL);
+ strscpyl(filename, sizeof(filename), "/sys", syspath, NULL);
else
- util_strscpy(filename, sizeof(filename), syspath);
+ strscpy(filename, sizeof(filename), syspath);
util_remove_trailing_chars(filename, '/');
dev = udev_device_new_from_syspath(udev, filename);
diff --git a/src/udev/udevadm-trigger.c b/src/udev/udevadm-trigger.c
index d52ae461fe..f472996965 100644
--- a/src/udev/udevadm-trigger.c
+++ b/src/udev/udevadm-trigger.c
@@ -48,7 +48,7 @@ static void exec_list(struct udev_enumerate *udev_enumerate, const char *action)
printf("%s\n", udev_list_entry_get_name(entry));
if (dry_run)
continue;
- util_strscpyl(filename, sizeof(filename), udev_list_entry_get_name(entry), "/uevent", NULL);
+ strscpyl(filename, sizeof(filename), udev_list_entry_get_name(entry), "/uevent", NULL);
fd = open(filename, O_WRONLY);
if (fd < 0)
continue;
@@ -62,7 +62,7 @@ static const char *keyval(const char *str, const char **val, char *buf, size_t s
{
char *pos;
- util_strscpy(buf, size,str);
+ strscpy(buf, size,str);
pos = strchr(buf, '=');
if (pos != NULL) {
pos[0] = 0;
@@ -122,9 +122,9 @@ static int adm_trigger(struct udev *udev, int argc, char *argv[])
dry_run = 1;
break;
case 't':
- if (strcmp(optarg, "devices") == 0) {
+ if (streq(optarg, "devices")) {
device_type = TYPE_DEVICES;
- } else if (strcmp(optarg, "subsystems") == 0) {
+ } else if (streq(optarg, "subsystems")) {
device_type = TYPE_SUBSYSTEMS;
} else {
log_error("unknown type --type=%s\n", optarg);
@@ -165,9 +165,9 @@ static int adm_trigger(struct udev *udev, int argc, char *argv[])
/* add sys dir if needed */
if (!startswith(optarg, "/sys"))
- util_strscpyl(path, sizeof(path), "/sys", optarg, NULL);
+ strscpyl(path, sizeof(path), "/sys", optarg, NULL);
else
- util_strscpy(path, sizeof(path), optarg);
+ strscpy(path, sizeof(path), optarg);
util_remove_trailing_chars(path, '/');
dev = udev_device_new_from_syspath(udev, path);
if (dev == NULL) {
@@ -214,7 +214,7 @@ static int adm_trigger(struct udev *udev, int argc, char *argv[])
exec_list(udev_enumerate, action);
goto exit;
default:
- goto exit;
+ assert_not_reached("device_type");
}
exit:
udev_enumerate_unref(udev_enumerate);
diff --git a/src/udev/udevadm.c b/src/udev/udevadm.c
index 53419ffa62..e14b3ca27c 100644
--- a/src/udev/udevadm.c
+++ b/src/udev/udevadm.c
@@ -131,7 +131,7 @@ int main(int argc, char *argv[])
if (command != NULL)
for (i = 0; i < ELEMENTSOF(udevadm_cmds); i++) {
- if (strcmp(udevadm_cmds[i]->name, command) == 0) {
+ if (streq(udevadm_cmds[i]->name, command)) {
argc -= optind;
argv += optind;
/* we need '0' here to reset the internal state */
diff --git a/src/udev/udevd.c b/src/udev/udevd.c
index ffc48a03bd..7d13b4f532 100644
--- a/src/udev/udevd.c
+++ b/src/udev/udevd.c
@@ -48,6 +48,7 @@
#include "sd-daemon.h"
#include "cgroup-util.h"
#include "dev-setup.h"
+#include "fileio.h"
static bool debug;
@@ -97,7 +98,9 @@ struct event {
dev_t devnum;
int ifindex;
bool is_block;
+#ifdef HAVE_FIRMWARE
bool nodelay;
+#endif
};
static inline struct event *node_to_event(struct udev_list_node *node)
@@ -264,7 +267,7 @@ static void worker_new(struct event *event)
prctl(PR_SET_PDEATHSIG, SIGTERM);
/* reset OOM score, we only protect the main daemon */
- write_one_line_file("/proc/self/oom_score_adj", "0");
+ write_string_file("/proc/self/oom_score_adj", "0");
for (;;) {
struct udev_event *udev_event;
@@ -441,8 +444,10 @@ static int event_queue_insert(struct udev_device *dev)
event->devnum = udev_device_get_devnum(dev);
event->is_block = streq("block", udev_device_get_subsystem(dev));
event->ifindex = udev_device_get_ifindex(dev);
+#ifdef HAVE_FIRMWARE
if (streq(udev_device_get_subsystem(dev), "firmware"))
event->nodelay = true;
+#endif
udev_queue_export_device_queued(udev_queue_export, dev);
log_debug("seq %llu queued, '%s' '%s'\n", udev_device_get_seqnum(dev),
@@ -499,7 +504,7 @@ static bool is_devpath_busy(struct event *event)
return true;
/* check our old name */
- if (event->devpath_old != NULL && strcmp(loop_event->devpath, event->devpath_old) == 0) {
+ if (event->devpath_old != NULL && streq(loop_event->devpath, event->devpath_old)) {
event->delaying_seqnum = loop_event->seqnum;
return true;
}
@@ -522,9 +527,11 @@ static bool is_devpath_busy(struct event *event)
return true;
}
+#ifdef HAVE_FIRMWARE
/* allow to bypass the dependency tracking */
if (event->nodelay)
continue;
+#endif
/* parent device event found */
if (event->devpath[common] == '/') {
@@ -726,7 +733,7 @@ static int handle_inotify(struct udev *udev)
int fd;
log_debug("device %s closed, synthesising 'change'\n", udev_device_get_devnode(dev));
- util_strscpyl(filename, sizeof(filename), udev_device_get_syspath(dev), "/uevent", NULL);
+ strscpyl(filename, sizeof(filename), udev_device_get_syspath(dev), "/uevent", NULL);
fd = open(filename, O_WRONLY);
if (fd >= 0) {
if (write(fd, "change", 6) < 0)
@@ -812,8 +819,12 @@ static void static_dev_create_from_modules(struct udev *udev)
char buf[4096];
FILE *f;
- uname(&kernel);
- util_strscpyl(modules, sizeof(modules), ROOTPREFIX "/lib/modules/", kernel.release, "/modules.devname", NULL);
+ if (uname(&kernel) < 0) {
+ log_error("uname failed: %m");
+ return;
+ }
+
+ strscpyl(modules, sizeof(modules), ROOTPREFIX "/lib/modules/", kernel.release, "/modules.devname", NULL);
f = fopen(modules, "re");
if (f == NULL)
return;
@@ -860,7 +871,7 @@ static void static_dev_create_from_modules(struct udev *udev)
else
continue;
- util_strscpyl(filename, sizeof(filename), "/dev/", devname, NULL);
+ strscpyl(filename, sizeof(filename), "/dev/", devname, NULL);
mkdir_parents_label(filename, 0755);
label_context_set(filename, mode);
log_debug("mknod '%s' %c%u:%u\n", filename, type, maj, min);
@@ -872,113 +883,6 @@ static void static_dev_create_from_modules(struct udev *udev)
fclose(f);
}
-static int mem_size_mb(void)
-{
- FILE *f;
- char buf[4096];
- long int memsize = -1;
-
- f = fopen("/proc/meminfo", "re");
- if (f == NULL)
- return -1;
-
- while (fgets(buf, sizeof(buf), f) != NULL) {
- long int value;
-
- if (sscanf(buf, "MemTotal: %ld kB", &value) == 1) {
- memsize = value / 1024;
- break;
- }
- }
-
- fclose(f);
- return memsize;
-}
-
-static int convert_db(struct udev *udev)
-{
- char filename[UTIL_PATH_SIZE];
- struct udev_enumerate *udev_enumerate;
- struct udev_list_entry *list_entry;
-
- /* current database */
- if (access("/run/udev/data", F_OK) >= 0)
- return 0;
-
- /* make sure we do not get here again */
- mkdir_p("/run/udev/data", 0755);
-
- /* old database */
- util_strscpyl(filename, sizeof(filename), "/dev/.udev/db", NULL);
- if (access(filename, F_OK) < 0)
- return 0;
-
- print_kmsg("converting old udev database\n");
-
- udev_enumerate = udev_enumerate_new(udev);
- if (udev_enumerate == NULL)
- return -1;
- udev_enumerate_scan_devices(udev_enumerate);
- udev_list_entry_foreach(list_entry, udev_enumerate_get_list_entry(udev_enumerate)) {
- struct udev_device *device;
-
- device = udev_device_new_from_syspath(udev, udev_list_entry_get_name(list_entry));
- if (device == NULL)
- continue;
-
- /* try to find the old database for devices without a current one */
- if (udev_device_read_db(device, NULL) < 0) {
- bool have_db;
- const char *id;
- struct stat stats;
- char devpath[UTIL_PATH_SIZE];
- char from[UTIL_PATH_SIZE];
-
- have_db = false;
-
- /* find database in old location */
- id = udev_device_get_id_filename(device);
- util_strscpyl(from, sizeof(from), "/dev/.udev/db/", id, NULL);
- if (lstat(from, &stats) == 0) {
- if (!have_db) {
- udev_device_read_db(device, from);
- have_db = true;
- }
- unlink(from);
- }
-
- /* find old database with $subsys:$sysname name */
- util_strscpyl(from, sizeof(from), "/dev/.udev/db/",
- udev_device_get_subsystem(device), ":", udev_device_get_sysname(device), NULL);
- if (lstat(from, &stats) == 0) {
- if (!have_db) {
- udev_device_read_db(device, from);
- have_db = true;
- }
- unlink(from);
- }
-
- /* find old database with the encoded devpath name */
- util_path_encode(udev_device_get_devpath(device), devpath, sizeof(devpath));
- util_strscpyl(from, sizeof(from), "/dev/.udev/db/", devpath, NULL);
- if (lstat(from, &stats) == 0) {
- if (!have_db) {
- udev_device_read_db(device, from);
- have_db = true;
- }
- unlink(from);
- }
-
- /* write out new database */
- if (have_db)
- udev_device_update_db(device);
- }
- udev_device_unref(device);
- }
- udev_enumerate_unref(udev_enumerate);
- return 0;
-}
-
static int systemd_fds(struct udev *udev, int *rctrl, int *rnetlink)
{
int ctrl = -1, netlink = -1;
@@ -1117,11 +1021,11 @@ int main(int argc, char *argv[])
udev_set_log_priority(udev, LOG_DEBUG);
break;
case 'N':
- if (strcmp (optarg, "early") == 0) {
+ if (streq(optarg, "early")) {
resolve_names = 1;
- } else if (strcmp (optarg, "late") == 0) {
+ } else if (streq(optarg, "late")) {
resolve_names = 0;
- } else if (strcmp (optarg, "never") == 0) {
+ } else if (streq(optarg, "never")) {
resolve_names = -1;
} else {
fprintf(stderr, "resolve-names must be early, late or never\n");
@@ -1200,7 +1104,7 @@ int main(int argc, char *argv[])
}
/* get our own cgroup, we regularly kill everything udev has left behind */
- if (cg_get_by_pid(SYSTEMD_CGROUP_CONTROLLER, 0, &udev_cgroup) < 0)
+ if (cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, 0, &udev_cgroup) < 0)
udev_cgroup = NULL;
} else {
/* open control and netlink socket */
@@ -1264,7 +1168,7 @@ int main(int argc, char *argv[])
setsid();
- write_one_line_file("/proc/self/oom_score_adj", "-1000");
+ write_string_file("/proc/self/oom_score_adj", "-1000");
} else {
sd_notify(1, "READY=1");
}
@@ -1354,17 +1258,14 @@ int main(int argc, char *argv[])
goto exit;
}
- /* if needed, convert old database from earlier udev version */
- convert_db(udev);
-
if (children_max <= 0) {
- int memsize = mem_size_mb();
+ cpu_set_t cpu_set;
- /* set value depending on the amount of RAM */
- if (memsize > 0)
- children_max = 16 + (memsize / 8);
- else
- children_max = 16;
+ children_max = 8;
+
+ if (sched_getaffinity(0, sizeof (cpu_set), &cpu_set) == 0) {
+ children_max += CPU_COUNT(&cpu_set) * 2;
+ }
}
log_debug("set children_max to %u\n", children_max);
diff --git a/src/update-utmp/update-utmp.c b/src/update-utmp/update-utmp.c
index a311280c27..9184025554 100644
--- a/src/update-utmp/update-utmp.c
+++ b/src/update-utmp/update-utmp.c
@@ -313,11 +313,10 @@ static int on_runlevel(Context *c) {
int main(int argc, char *argv[]) {
int r;
DBusError error;
- Context c;
+ Context c = {};
dbus_error_init(&error);
- zero(c);
#ifdef HAVE_AUDIT
c.audit_fd = -1;
#endif
diff --git a/src/vconsole/vconsole-setup.c b/src/vconsole/vconsole-setup.c
index b9d8681a85..1bbf737c36 100644
--- a/src/vconsole/vconsole-setup.c
+++ b/src/vconsole/vconsole-setup.c
@@ -33,11 +33,13 @@
#include <sys/wait.h>
#include <linux/tiocl.h>
#include <linux/kd.h>
+#include <linux/vt.h>
#include "util.h"
#include "log.h"
#include "macro.h"
#include "virt.h"
+#include "fileio.h"
static bool is_vconsole(int fd) {
unsigned char data[1];
@@ -55,7 +57,7 @@ static int disable_utf8(int fd) {
if (loop_write(fd, "\033%@", 3, false) < 0)
r = -errno;
- k = write_one_line_file("/sys/module/vt/parameters/default_utf8", "0");
+ k = write_string_file("/sys/module/vt/parameters/default_utf8", "0");
if (k < 0)
r = k;
@@ -67,14 +69,27 @@ static int disable_utf8(int fd) {
static int enable_utf8(int fd) {
int r = 0, k;
-
- if (ioctl(fd, KDSKBMODE, K_UNICODE) < 0)
- r = -errno;
+ long current = 0;
+
+ if (ioctl(fd, KDGKBMODE, &current) < 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;
+ }
if (loop_write(fd, "\033%G", 3, false) < 0)
r = -errno;
- k = write_one_line_file("/sys/module/vt/parameters/default_utf8", "1");
+ k = write_string_file("/sys/module/vt/parameters/default_utf8", "1");
if (k < 0)
r = k;
@@ -84,7 +99,7 @@ static int enable_utf8(int fd) {
return r;
}
-static int load_keymap(const char *vc, const char *map, const char *map_toggle, bool utf8, pid_t *_pid) {
+static int keymap_load(const char *vc, const char *map, const char *map_toggle, bool utf8, pid_t *_pid) {
const char *args[8];
int i = 0;
pid_t pid;
@@ -119,7 +134,7 @@ static int load_keymap(const char *vc, const char *map, const char *map_toggle,
return 0;
}
-static int load_font(const char *vc, const char *font, const char *map, const char *unimap, pid_t *_pid) {
+static int font_load(const char *vc, const char *font, const char *map, const char *unimap, pid_t *_pid) {
const char *args[9];
int i = 0;
pid_t pid;
@@ -157,6 +172,46 @@ static int load_font(const char *vc, const char *font, const char *map, const ch
return 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 = {};
+ int i, r;
+
+ /* get active, and 16 bit mask of used VT numbers */
+ r = ioctl(fd, VT_GETSTATE, &vcs);
+ if (r < 0)
+ return;
+
+ for (i = 1; i <= 15; i++) {
+ char vcname[16];
+ _cleanup_close_ int vcfd = -1;
+ struct console_font_op cfo = {};
+
+ if (i == vcs.v_active)
+ continue;
+
+ /* skip non-allocated ttys */
+ snprintf(vcname, sizeof(vcname), "/dev/vcs%i", i);
+ if (access(vcname, F_OK) < 0)
+ continue;
+
+ snprintf(vcname, sizeof(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 */
+ ioctl(vcfd, KDFONTOP, &cfo);
+ }
+}
+
int main(int argc, char **argv) {
const char *vc;
char *vc_keymap = NULL;
@@ -166,8 +221,9 @@ int main(int argc, char **argv) {
char *vc_font_unimap = NULL;
int fd = -1;
bool utf8;
- int r = EXIT_FAILURE;
pid_t font_pid = 0, keymap_pid = 0;
+ bool font_copy = false;
+ int r = EXIT_FAILURE;
log_set_target(LOG_TARGET_AUTO);
log_parse_environment();
@@ -177,8 +233,10 @@ int main(int argc, char **argv) {
if (argv[1])
vc = argv[1];
- else
+ else {
vc = "/dev/tty0";
+ font_copy = true;
+ }
fd = open_terminal(vc, O_RDWR|O_CLOEXEC);
if (fd < 0) {
@@ -193,8 +251,18 @@ int main(int argc, char **argv) {
utf8 = is_locale_utf8();
- r = 0;
+ 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("Failed to read /etc/vconsole.conf: %s", strerror(-r));
+ /* Let the kernel command line override /etc/vconsole.conf */
if (detect_container(NULL) <= 0) {
r = parse_env_file("/proc/cmdline", WHITESPACE,
"vconsole.keymap", &vc_keymap,
@@ -208,42 +276,25 @@ int main(int argc, char **argv) {
log_warning("Failed to read /proc/cmdline: %s", strerror(-r));
}
- /* Hmm, nothing set on the kernel cmd line? Then let's
- * try /etc/vconsole.conf */
- if (r <= 0) {
- 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("Failed to read /etc/vconsole.conf: %s", strerror(-r));
- }
-
- if (r <= 0) {
- }
-
- r = EXIT_FAILURE;
-
if (utf8)
enable_utf8(fd);
else
disable_utf8(fd);
-
- if (load_keymap(vc, vc_keymap, vc_keymap_toggle, utf8, &keymap_pid) >= 0 &&
- load_font(vc, vc_font, vc_font_map, vc_font_unimap, &font_pid) >= 0)
+ r = EXIT_FAILURE;
+ if (keymap_load(vc, vc_keymap, vc_keymap_toggle, utf8, &keymap_pid) >= 0 &&
+ font_load(vc, vc_font, vc_font_map, vc_font_unimap, &font_pid) >= 0)
r = EXIT_SUCCESS;
finish:
if (keymap_pid > 0)
wait_for_terminate_and_warn(KBD_LOADKEYS, keymap_pid);
- if (font_pid > 0)
+ if (font_pid > 0) {
wait_for_terminate_and_warn(KBD_SETFONT, font_pid);
+ if (font_copy)
+ font_copy_to_all_vcs(fd);
+ }
free(vc_keymap);
free(vc_font);