summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/activate/activate.c1
-rw-r--r--src/analyze/analyze-verify.c38
-rw-r--r--src/analyze/analyze-verify.h2
-rw-r--r--src/analyze/analyze.c6
-rw-r--r--src/ask-password/ask-password.c12
-rw-r--r--src/binfmt/binfmt.c2
-rw-r--r--src/boot/boot-efi.c190
-rw-r--r--src/boot/boot-loader.c132
-rw-r--r--src/boot/boot.h64
-rw-r--r--src/boot/bootctl.c1203
-rw-r--r--src/boot/efi/.gitignore2
-rw-r--r--src/boot/efi/boot.c1834
-rw-r--r--src/boot/efi/console.c141
-rw-r--r--src/boot/efi/console.h34
-rw-r--r--src/boot/efi/graphics.c91
-rw-r--r--src/boot/efi/graphics.h24
-rw-r--r--src/boot/efi/linux.c130
-rw-r--r--src/boot/efi/linux.h24
-rw-r--r--src/boot/efi/pefile.c172
-rw-r--r--src/boot/efi/pefile.h22
-rw-r--r--src/boot/efi/splash.c323
-rw-r--r--src/boot/efi/splash.h22
-rw-r--r--src/boot/efi/stub.c113
-rw-r--r--src/boot/efi/util.c347
-rw-r--r--src/boot/efi/util.h50
-rw-r--r--src/bootchart/bootchart.c237
-rw-r--r--src/bootchart/bootchart.conf7
-rw-r--r--src/bootchart/bootchart.h20
-rw-r--r--src/bootchart/store.c219
-rw-r--r--src/bootchart/store.h10
-rw-r--r--src/bootchart/svg.c855
-rw-r--r--src/bootchart/svg.h12
-rw-r--r--src/bus-proxyd/bus-proxyd.c17
-rw-r--r--src/bus-proxyd/bus-xml-policy.c2
-rw-r--r--src/bus-proxyd/bus-xml-policy.h2
-rw-r--r--src/bus-proxyd/driver.c103
-rw-r--r--src/bus-proxyd/proxy.c27
-rw-r--r--src/bus-proxyd/proxy.h3
-rw-r--r--src/bus-proxyd/stdio-bridge.c15
-rw-r--r--src/bus-proxyd/synthesize.c10
-rw-r--r--src/bus-proxyd/synthesize.h2
-rw-r--r--src/bus-proxyd/test-bus-xml-policy.c13
-rw-r--r--src/cgls/cgls.c1
-rw-r--r--src/cgtop/cgtop.c3
-rw-r--r--src/console/consoled-display.c1
-rw-r--r--src/console/consoled-manager.c16
-rw-r--r--src/console/consoled-session.c4
-rw-r--r--src/console/consoled-terminal.c3
-rw-r--r--src/console/consoled-workspace.c1
-rw-r--r--src/console/consoled.c7
-rw-r--r--src/console/consoled.h4
-rw-r--r--src/core/automount.c360
-rw-r--r--src/core/automount.h6
-rw-r--r--src/core/bus-policy.c7
-rw-r--r--src/core/busname.c115
-rw-r--r--src/core/busname.h1
-rw-r--r--src/core/cgroup.c103
-rw-r--r--src/core/cgroup.h5
-rw-r--r--src/core/dbus-automount.c3
-rw-r--r--src/core/dbus-automount.h1
-rw-r--r--src/core/dbus-busname.c1
-rw-r--r--src/core/dbus-busname.h2
-rw-r--r--src/core/dbus-device.c1
-rw-r--r--src/core/dbus-device.h1
-rw-r--r--src/core/dbus-job.c34
-rw-r--r--src/core/dbus-job.h2
-rw-r--r--src/core/dbus-kill.c4
-rw-r--r--src/core/dbus-manager.c500
-rw-r--r--src/core/dbus-manager.h2
-rw-r--r--src/core/dbus-mount.c1
-rw-r--r--src/core/dbus-path.c1
-rw-r--r--src/core/dbus-path.h1
-rw-r--r--src/core/dbus-scope.c25
-rw-r--r--src/core/dbus-service.c1
-rw-r--r--src/core/dbus-slice.c1
-rw-r--r--src/core/dbus-snapshot.c13
-rw-r--r--src/core/dbus-snapshot.h2
-rw-r--r--src/core/dbus-socket.c2
-rw-r--r--src/core/dbus-swap.c2
-rw-r--r--src/core/dbus-target.c2
-rw-r--r--src/core/dbus-target.h1
-rw-r--r--src/core/dbus-timer.c2
-rw-r--r--src/core/dbus-unit.c167
-rw-r--r--src/core/dbus-unit.h10
-rw-r--r--src/core/dbus.c127
-rw-r--r--src/core/dbus.h5
-rw-r--r--src/core/device.c454
-rw-r--r--src/core/device.h18
-rw-r--r--src/core/execute.c204
-rw-r--r--src/core/execute.h17
-rw-r--r--src/core/failure-action.c4
-rw-r--r--src/core/hostname-setup.c29
-rw-r--r--src/core/ima-setup.c18
-rw-r--r--src/core/job.c178
-rw-r--r--src/core/job.h6
-rw-r--r--src/core/kill.c5
-rw-r--r--src/core/killall.c4
-rw-r--r--src/core/kmod-setup.c23
-rw-r--r--src/core/load-dropin.c3
-rw-r--r--src/core/load-fragment-gperf.gperf.m41
-rw-r--r--src/core/load-fragment.c114
-rw-r--r--src/core/locale-setup.c2
-rw-r--r--src/core/loopback-setup.c8
-rw-r--r--src/core/machine-id-setup.c295
-rw-r--r--src/core/main.c73
-rw-r--r--src/core/manager.c236
-rw-r--r--src/core/manager.h22
-rw-r--r--src/core/mount-setup.c11
-rw-r--r--src/core/mount.c339
-rw-r--r--src/core/mount.h10
-rw-r--r--src/core/namespace.c113
-rw-r--r--src/core/namespace.h3
-rw-r--r--src/core/org.freedesktop.systemd1.policy.in.in14
-rw-r--r--src/core/path.c42
-rw-r--r--src/core/path.h1
-rw-r--r--src/core/scope.c36
-rw-r--r--src/core/scope.h1
-rw-r--r--src/core/selinux-access.c43
-rw-r--r--src/core/selinux-access.h1
-rw-r--r--src/core/selinux-setup.c4
-rw-r--r--src/core/service.c502
-rw-r--r--src/core/service.h6
-rw-r--r--src/core/shutdown.c10
-rw-r--r--src/core/slice.c33
-rw-r--r--src/core/slice.h1
-rw-r--r--src/core/smack-setup.c6
-rw-r--r--src/core/snapshot.c20
-rw-r--r--src/core/snapshot.h1
-rw-r--r--src/core/socket.c245
-rw-r--r--src/core/socket.h4
-rw-r--r--src/core/swap.c204
-rw-r--r--src/core/swap.h7
-rw-r--r--src/core/system.conf7
-rw-r--r--src/core/systemd.pc.in1
-rw-r--r--src/core/target.c4
-rw-r--r--src/core/target.h1
-rw-r--r--src/core/timer.c67
-rw-r--r--src/core/timer.h1
-rw-r--r--src/core/transaction.c173
-rw-r--r--src/core/umount.c39
-rw-r--r--src/core/unit-printf.c116
-rw-r--r--src/core/unit.c624
-rw-r--r--src/core/unit.h76
-rw-r--r--src/cryptsetup/cryptsetup-generator.c36
-rw-r--r--src/cryptsetup/cryptsetup.c52
-rw-r--r--src/dbus1-generator/dbus1-generator.c6
-rw-r--r--src/debug-generator/debug-generator.c12
-rw-r--r--src/delta/delta.c3
-rw-r--r--src/detect-virt/detect-virt.c1
-rw-r--r--src/efi-boot-generator/efi-boot-generator.c26
-rw-r--r--src/escape/escape.c40
-rw-r--r--src/firstboot/firstboot.c27
-rw-r--r--src/fsck/fsck.c298
-rw-r--r--src/fstab-generator/fstab-generator.c172
-rw-r--r--src/getty-generator/getty-generator.c24
-rw-r--r--src/gpt-auto-generator/gpt-auto-generator.c105
-rw-r--r--src/gudev/.gitignore7
-rwxr-xr-xsrc/gudev/gjs-example.js75
-rw-r--r--src/gudev/gudev-1.0.pc.in11
-rw-r--r--src/gudev/gudev.h32
-rw-r--r--src/gudev/gudevclient.c532
-rw-r--r--src/gudev/gudevclient.h99
-rw-r--r--src/gudev/gudevdevice.c1026
-rw-r--r--src/gudev/gudevdevice.h130
-rw-r--r--src/gudev/gudevenumerator.c429
-rw-r--r--src/gudev/gudevenumerator.h106
-rw-r--r--src/gudev/gudevenums.h48
-rw-r--r--src/gudev/gudevenumtypes.c.template39
-rw-r--r--src/gudev/gudevenumtypes.h.template24
-rw-r--r--src/gudev/gudevmarshal.list1
-rw-r--r--src/gudev/gudevprivate.h40
-rw-r--r--src/gudev/gudevtypes.h50
-rw-r--r--src/gudev/libgudev-1.0.sym68
-rwxr-xr-xsrc/gudev/seed-example-enum.js38
-rwxr-xr-xsrc/gudev/seed-example.js72
-rw-r--r--src/hibernate-resume/hibernate-resume-generator.c8
-rw-r--r--src/hibernate-resume/hibernate-resume.c2
-rw-r--r--src/hostname/hostnamectl.c11
-rw-r--r--src/hostname/hostnamed.c84
-rw-r--r--src/hwdb/hwdb.c2
-rw-r--r--src/import/aufs-util.c5
-rw-r--r--src/import/export-raw.c345
-rw-r--r--src/import/export-raw.h37
-rw-r--r--src/import/export-tar.c329
-rw-r--r--src/import/export-tar.h37
-rw-r--r--src/import/export.c320
-rw-r--r--src/import/import-common.c401
-rw-r--r--src/import/import-common.h17
-rw-r--r--src/import/import-compress.c470
-rw-r--r--src/import/import-compress.h64
-rw-r--r--src/import/import-job.h119
-rw-r--r--src/import/import-raw.c496
-rw-r--r--src/import/import-raw.h4
-rw-r--r--src/import/import-tar.c415
-rw-r--r--src/import/import-tar.h2
-rw-r--r--src/import/import.c337
-rw-r--r--src/import/importd.c340
-rw-r--r--src/import/org.freedesktop.import1.policy.in20
-rw-r--r--src/import/pull-common.c425
-rw-r--r--src/import/pull-common.h36
-rw-r--r--src/import/pull-dkr.c (renamed from src/import/import-dkr.c)710
-rw-r--r--src/import/pull-dkr.h37
-rw-r--r--src/import/pull-job.c (renamed from src/import/import-job.c)260
-rw-r--r--src/import/pull-job.h117
-rw-r--r--src/import/pull-raw.c517
-rw-r--r--src/import/pull-raw.h (renamed from src/import/import-dkr.h)19
-rw-r--r--src/import/pull-tar.c417
-rw-r--r--src/import/pull-tar.h37
-rw-r--r--src/import/pull.c81
-rw-r--r--src/import/qcow2-util.c8
-rw-r--r--src/initctl/initctl.c27
-rw-r--r--src/journal-remote/journal-gatewayd.c56
-rw-r--r--src/journal-remote/journal-remote-parse.c59
-rw-r--r--src/journal-remote/journal-remote-parse.h6
-rw-r--r--src/journal-remote/journal-remote-write.c2
-rw-r--r--src/journal-remote/journal-remote-write.h1
-rw-r--r--src/journal-remote/journal-remote.c150
-rw-r--r--src/journal-remote/journal-remote.h1
-rw-r--r--src/journal-remote/journal-upload.c2
-rw-r--r--src/journal-remote/microhttpd-util.c53
-rw-r--r--src/journal-remote/microhttpd-util.h16
-rw-r--r--src/journal/.gitignore2
-rw-r--r--src/journal/audit-type.c (renamed from src/shared/time-dst.h)18
-rw-r--r--src/journal/audit-type.h39
-rw-r--r--src/journal/cat.c1
-rw-r--r--src/journal/catalog.c3
-rw-r--r--src/journal/compress.c26
-rw-r--r--src/journal/compress.h2
-rw-r--r--src/journal/coredump-vacuum.c5
-rw-r--r--src/journal/coredump.c7
-rw-r--r--src/journal/coredump.conf7
-rw-r--r--src/journal/coredumpctl.c6
-rw-r--r--src/journal/fsprg.c1
-rw-r--r--src/journal/journal-authenticate.c4
-rw-r--r--src/journal/journal-authenticate.h1
-rw-r--r--src/journal/journal-def.h2
-rw-r--r--src/journal/journal-file.c84
-rw-r--r--src/journal/journal-file.h2
-rw-r--r--src/journal/journal-qrcode.h2
-rw-r--r--src/journal/journal-vacuum.c5
-rw-r--r--src/journal/journal-vacuum.h1
-rw-r--r--src/journal/journal-verify.c6
-rw-r--r--src/journal/journalctl.c581
-rw-r--r--src/journal/journald-audit.c25
-rw-r--r--src/journal/journald-console.c4
-rw-r--r--src/journal/journald-kmsg.c8
-rw-r--r--src/journal/journald-native.c2
-rw-r--r--src/journal/journald-rate-limit.c1
-rw-r--r--src/journal/journald-rate-limit.h1
-rw-r--r--src/journal/journald-server.c21
-rw-r--r--src/journal/journald-server.h4
-rw-r--r--src/journal/journald-stream.c3
-rw-r--r--src/journal/journald-syslog.c4
-rw-r--r--src/journal/journald-wall.c4
-rw-r--r--src/journal/journald.c5
-rw-r--r--src/journal/journald.conf7
-rw-r--r--src/journal/mmap-cache.c1
-rw-r--r--src/journal/mmap-cache.h1
-rw-r--r--src/journal/sd-journal.c27
-rw-r--r--src/journal/stacktrace.c1
-rw-r--r--src/journal/test-audit-type.c44
-rw-r--r--src/journal/test-catalog.c1
-rw-r--r--src/journal/test-compress.c1
-rw-r--r--src/journal/test-journal-enum.c1
-rw-r--r--src/journal/test-journal-flush.c4
-rw-r--r--src/journal/test-journal-init.c3
-rw-r--r--src/journal/test-journal-interleaving.c9
-rw-r--r--src/journal/test-journal-stream.c12
-rw-r--r--src/journal/test-journal-verify.c5
-rw-r--r--src/journal/test-journal.c7
-rw-r--r--src/journal/test-mmap-cache.c1
-rw-r--r--src/kernel-install/90-loaderentry.install6
-rw-r--r--src/libsystemd-network/dhcp-identifier.c11
-rw-r--r--src/libsystemd-network/dhcp-identifier.h4
-rw-r--r--src/libsystemd-network/dhcp-network.c2
-rw-r--r--src/libsystemd-network/dhcp-packet.c8
-rw-r--r--src/libsystemd-network/lldp-internal.c18
-rw-r--r--src/libsystemd-network/lldp-network.c1
-rw-r--r--src/libsystemd-network/lldp-tlv.c8
-rw-r--r--src/libsystemd-network/network-internal.c1
-rw-r--r--src/libsystemd-network/network-internal.h2
-rw-r--r--src/libsystemd-network/sd-dhcp-client.c6
-rw-r--r--src/libsystemd-network/sd-dhcp-lease.c11
-rw-r--r--src/libsystemd-network/sd-dhcp-server.c3
-rw-r--r--src/libsystemd-network/sd-dhcp6-client.c20
-rw-r--r--src/libsystemd-network/sd-ipv4ll.c1
-rw-r--r--src/libsystemd-network/sd-lldp.c5
-rw-r--r--src/libsystemd-network/sd-pppoe.c4
-rw-r--r--src/libsystemd-network/test-dhcp-client.c4
-rw-r--r--src/libsystemd-network/test-dhcp-option.c34
-rw-r--r--src/libsystemd-network/test-dhcp-server.c2
-rw-r--r--src/libsystemd-network/test-dhcp6-client.c2
-rw-r--r--src/libsystemd-network/test-ipv4ll.c3
-rw-r--r--src/libsystemd-network/test-lldp.c5
-rw-r--r--src/libsystemd-network/test-pppoe.c7
-rw-r--r--src/libsystemd-terminal/evcat.c16
-rw-r--r--src/libsystemd-terminal/grdev-drm.c20
-rw-r--r--src/libsystemd-terminal/grdev-internal.h6
-rw-r--r--src/libsystemd-terminal/grdev.c11
-rw-r--r--src/libsystemd-terminal/grdev.h6
-rw-r--r--src/libsystemd-terminal/idev-evdev.c15
-rw-r--r--src/libsystemd-terminal/idev-internal.h6
-rw-r--r--src/libsystemd-terminal/idev-keyboard.c29
-rw-r--r--src/libsystemd-terminal/idev.c19
-rw-r--r--src/libsystemd-terminal/idev.h7
-rw-r--r--src/libsystemd-terminal/modeset.c17
-rw-r--r--src/libsystemd-terminal/subterm.c8
-rw-r--r--src/libsystemd-terminal/sysview-internal.h8
-rw-r--r--src/libsystemd-terminal/sysview.c94
-rw-r--r--src/libsystemd-terminal/sysview.h10
-rw-r--r--src/libsystemd-terminal/term-charset.c3
-rw-r--r--src/libsystemd-terminal/term-page.c5
-rw-r--r--src/libsystemd-terminal/term-wcwidth.c1
-rw-r--r--src/libsystemd-terminal/test-term-page.c4
-rw-r--r--src/libsystemd-terminal/test-term-parser.c4
-rw-r--r--src/libsystemd-terminal/test-unifont.c3
-rw-r--r--src/libsystemd-terminal/unifont.c3
-rw-r--r--src/libsystemd-terminal/unifont.h3
-rw-r--r--src/libsystemd/libsystemd.sym.m480
-rw-r--r--src/libsystemd/sd-bus/bus-bloom.h1
-rw-r--r--src/libsystemd/sd-bus/bus-common-errors.c10
-rw-r--r--src/libsystemd/sd-bus/bus-common-errors.h1
-rw-r--r--src/libsystemd/sd-bus/bus-container.c2
-rw-r--r--src/libsystemd/sd-bus/bus-control.c81
-rw-r--r--src/libsystemd/sd-bus/bus-convenience.c28
-rw-r--r--src/libsystemd/sd-bus/bus-creds.c360
-rw-r--r--src/libsystemd/sd-bus/bus-creds.h7
-rw-r--r--src/libsystemd/sd-bus/bus-dump.c79
-rw-r--r--src/libsystemd/sd-bus/bus-error.c4
-rw-r--r--src/libsystemd/sd-bus/bus-gvariant.c1
-rw-r--r--src/libsystemd/sd-bus/bus-internal.h3
-rw-r--r--src/libsystemd/sd-bus/bus-introspect.c1
-rw-r--r--src/libsystemd/sd-bus/bus-introspect.h1
-rw-r--r--src/libsystemd/sd-bus/bus-kernel.c102
-rw-r--r--src/libsystemd/sd-bus/bus-kernel.h2
-rw-r--r--src/libsystemd/sd-bus/bus-match.c3
-rw-r--r--src/libsystemd/sd-bus/bus-message.c40
-rw-r--r--src/libsystemd/sd-bus/bus-message.h1
-rw-r--r--src/libsystemd/sd-bus/bus-objects.c4
-rw-r--r--src/libsystemd/sd-bus/bus-signature.h1
-rw-r--r--src/libsystemd/sd-bus/bus-socket.c37
-rw-r--r--src/libsystemd/sd-bus/bus-track.c7
-rw-r--r--src/libsystemd/sd-bus/bus-type.c1
-rw-r--r--src/libsystemd/sd-bus/bus-type.h1
-rw-r--r--src/libsystemd/sd-bus/bus-util.c393
-rw-r--r--src/libsystemd/sd-bus/bus-util.h31
-rw-r--r--src/libsystemd/sd-bus/busctl-introspect.h1
-rw-r--r--src/libsystemd/sd-bus/busctl.c3
-rw-r--r--src/libsystemd/sd-bus/kdbus.h68
-rw-r--r--src/libsystemd/sd-bus/sd-bus.c94
-rw-r--r--src/libsystemd/sd-bus/test-bus-benchmark.c (renamed from src/libsystemd/sd-bus/test-bus-kernel-benchmark.c)152
-rw-r--r--src/libsystemd/sd-bus/test-bus-chat.c46
-rw-r--r--src/libsystemd/sd-bus/test-bus-creds.c1
-rw-r--r--src/libsystemd/sd-bus/test-bus-error.c4
-rw-r--r--src/libsystemd/sd-bus/test-bus-introspect.c1
-rw-r--r--src/libsystemd/sd-bus/test-bus-kernel-bloom.c2
-rw-r--r--src/libsystemd/sd-bus/test-bus-kernel.c6
-rw-r--r--src/libsystemd/sd-bus/test-bus-marshal.c13
-rw-r--r--src/libsystemd/sd-bus/test-bus-match.c3
-rw-r--r--src/libsystemd/sd-bus/test-bus-objects.c31
-rw-r--r--src/libsystemd/sd-bus/test-bus-server.c4
-rw-r--r--src/libsystemd/sd-bus/test-bus-signature.c2
-rw-r--r--src/libsystemd/sd-bus/test-bus-zero-copy.c8
-rw-r--r--src/libsystemd/sd-daemon/sd-daemon.c67
-rw-r--r--src/libsystemd/sd-device/device-enumerator-private.h36
-rw-r--r--src/libsystemd/sd-device/device-enumerator.c983
-rw-r--r--src/libsystemd/sd-device/device-internal.h126
-rw-r--r--src/libsystemd/sd-device/device-private.c1117
-rw-r--r--src/libsystemd/sd-device/device-private.h64
-rw-r--r--src/libsystemd/sd-device/device-util.h60
-rw-r--r--src/libsystemd/sd-device/sd-device.c1839
-rw-r--r--src/libsystemd/sd-event/sd-event.c53
-rw-r--r--src/libsystemd/sd-event/test-event.c1
-rw-r--r--src/libsystemd/sd-hwdb/sd-hwdb.c4
-rw-r--r--src/libsystemd/sd-id128/sd-id128.c28
-rw-r--r--src/libsystemd/sd-login/sd-login.c31
-rw-r--r--src/libsystemd/sd-login/test-login.c1
-rw-r--r--src/libsystemd/sd-network/network-util.h1
-rw-r--r--src/libsystemd/sd-network/sd-network.c11
-rw-r--r--src/libsystemd/sd-path/sd-path.c4
-rw-r--r--src/libsystemd/sd-resolve/sd-resolve.c15
-rw-r--r--src/libsystemd/sd-resolve/test-resolve.c5
-rw-r--r--src/libsystemd/sd-rtnl/local-addresses.h4
-rw-r--r--src/libsystemd/sd-rtnl/rtnl-internal.h1
-rw-r--r--src/libsystemd/sd-rtnl/rtnl-message.c20
-rw-r--r--src/libsystemd/sd-rtnl/rtnl-types.c61
-rw-r--r--src/libsystemd/sd-rtnl/rtnl-types.h23
-rw-r--r--src/libsystemd/sd-rtnl/rtnl-util.c1
-rw-r--r--src/libsystemd/sd-rtnl/rtnl-util.h1
-rw-r--r--src/libsystemd/sd-rtnl/sd-rtnl.c73
-rw-r--r--src/libsystemd/sd-rtnl/test-rtnl.c1
-rw-r--r--src/libudev/libudev-device-internal.h57
-rw-r--r--src/libudev/libudev-device-private.c519
-rw-r--r--src/libudev/libudev-device.c1728
-rw-r--r--src/libudev/libudev-enumerate.c814
-rw-r--r--src/libudev/libudev-list.c3
-rw-r--r--src/libudev/libudev-monitor.c96
-rw-r--r--src/libudev/libudev-private.h14
-rw-r--r--src/libudev/libudev-queue.c5
-rw-r--r--src/libudev/libudev-util.c10
-rw-r--r--src/libudev/libudev.c3
-rw-r--r--src/libudev/libudev.h1
-rw-r--r--src/locale/localectl.c10
-rw-r--r--src/locale/localed.c66
-rw-r--r--src/login/inhibit.c5
-rw-r--r--src/login/loginctl.c20
-rw-r--r--src/login/logind-acl.c5
-rw-r--r--src/login/logind-action.c6
-rw-r--r--src/login/logind-action.h1
-rw-r--r--src/login/logind-button.c3
-rw-r--r--src/login/logind-button.h2
-rw-r--r--src/login/logind-core.c4
-rw-r--r--src/login/logind-dbus.c822
-rw-r--r--src/login/logind-device.c1
-rw-r--r--src/login/logind-device.h2
-rw-r--r--src/login/logind-gperf.gperf1
-rw-r--r--src/login/logind-inhibit.c14
-rw-r--r--src/login/logind-inhibit.h3
-rw-r--r--src/login/logind-seat-dbus.c30
-rw-r--r--src/login/logind-seat.c7
-rw-r--r--src/login/logind-seat.h5
-rw-r--r--src/login/logind-session-dbus.c81
-rw-r--r--src/login/logind-session-device.c4
-rw-r--r--src/login/logind-session-device.h4
-rw-r--r--src/login/logind-session.c32
-rw-r--r--src/login/logind-session.h11
-rw-r--r--src/login/logind-user-dbus.c37
-rw-r--r--src/login/logind-user.c17
-rw-r--r--src/login/logind-user.h5
-rw-r--r--src/login/logind-utmp.c182
-rw-r--r--src/login/logind.c48
-rw-r--r--src/login/logind.conf8
-rw-r--r--src/login/logind.h45
-rw-r--r--src/login/org.freedesktop.login1.conf8
-rw-r--r--src/login/org.freedesktop.login1.policy.in30
-rw-r--r--src/login/pam_systemd.c5
-rw-r--r--src/login/sysfs-show.c1
-rw-r--r--src/login/test-inhibit.c1
-rw-r--r--src/machine-id-commit/machine-id-commit.c10
-rw-r--r--src/machine-id-setup/machine-id-setup-main.c1
-rw-r--r--src/machine/image-dbus.c112
-rw-r--r--src/machine/image-dbus.h9
-rw-r--r--src/machine/machine-dbus.c534
-rw-r--r--src/machine/machine-dbus.h15
-rw-r--r--src/machine/machine.c51
-rw-r--r--src/machine/machine.h20
-rw-r--r--src/machine/machinectl.c919
-rw-r--r--src/machine/machined-dbus.c397
-rw-r--r--src/machine/machined.c13
-rw-r--r--src/machine/machined.h10
-rw-r--r--src/machine/org.freedesktop.machine1.conf84
-rw-r--r--src/machine/org.freedesktop.machine1.policy.in22
-rw-r--r--src/modules-load/modules-load.c6
-rw-r--r--src/network/networkctl.c116
-rw-r--r--src/network/networkd-address-pool.c1
-rw-r--r--src/network/networkd-address.c27
-rw-r--r--src/network/networkd-dhcp4.c105
-rw-r--r--src/network/networkd-dhcp6.c103
-rw-r--r--src/network/networkd-fdb.c84
-rw-r--r--src/network/networkd-link.c925
-rw-r--r--src/network/networkd-link.h25
-rw-r--r--src/network/networkd-manager.c16
-rw-r--r--src/network/networkd-netdev-bond.c319
-rw-r--r--src/network/networkd-netdev-bond.h85
-rw-r--r--src/network/networkd-netdev-bridge.c4
-rw-r--r--src/network/networkd-netdev-dummy.c5
-rw-r--r--src/network/networkd-netdev-gperf.gperf108
-rw-r--r--src/network/networkd-netdev-ipvlan.c3
-rw-r--r--src/network/networkd-netdev-macvlan.c2
-rw-r--r--src/network/networkd-netdev-tunnel.c287
-rw-r--r--src/network/networkd-netdev-tunnel.h1
-rw-r--r--src/network/networkd-netdev-tuntap.c14
-rw-r--r--src/network/networkd-netdev-veth.c2
-rw-r--r--src/network/networkd-netdev-vlan.c2
-rw-r--r--src/network/networkd-netdev-vxlan.c29
-rw-r--r--src/network/networkd-netdev-vxlan.h3
-rw-r--r--src/network/networkd-netdev.c197
-rw-r--r--src/network/networkd-netdev.h36
-rw-r--r--src/network/networkd-network-bus.c2
-rw-r--r--src/network/networkd-network-gperf.gperf3
-rw-r--r--src/network/networkd-network.c54
-rw-r--r--src/network/networkd-route.c3
-rw-r--r--src/network/networkd-wait-online-link.c2
-rw-r--r--src/network/networkd-wait-online-manager.c1
-rw-r--r--src/network/networkd-wait-online.c5
-rw-r--r--src/network/networkd-wait-online.h1
-rw-r--r--src/network/networkd.c15
-rw-r--r--src/network/networkd.h18
-rw-r--r--src/network/test-network-tables.c1
-rw-r--r--src/notify/notify.c4
-rw-r--r--src/nspawn/nspawn.c2187
-rw-r--r--src/nss-myhostname/nss-myhostname.c5
-rw-r--r--src/nss-resolve/nss-resolve.c5
-rw-r--r--src/path/path.c4
-rw-r--r--src/python-systemd/_daemon.c2
-rw-r--r--src/python-systemd/daemon.py2
-rw-r--r--src/python-systemd/docs/layout.html2
-rw-r--r--src/quotacheck/quotacheck.c3
-rw-r--r--src/random-seed/random-seed.c60
-rw-r--r--src/remount-fs/remount-fs.c13
-rw-r--r--src/reply-password/reply-password.c8
-rw-r--r--src/resolve-host/resolve-host.c2
-rw-r--r--src/resolve/resolved-bus.c27
-rw-r--r--src/resolve/resolved-dns-answer.c2
-rw-r--r--src/resolve/resolved-dns-cache.c7
-rw-r--r--src/resolve/resolved-dns-cache.h1
-rw-r--r--src/resolve/resolved-dns-domain.h1
-rw-r--r--src/resolve/resolved-dns-packet.h1
-rw-r--r--src/resolve/resolved-dns-query.c6
-rw-r--r--src/resolve/resolved-dns-query.h8
-rw-r--r--src/resolve/resolved-dns-rr.h2
-rw-r--r--src/resolve/resolved-dns-scope.c10
-rw-r--r--src/resolve/resolved-dns-scope.h3
-rw-r--r--src/resolve/resolved-dns-server.c15
-rw-r--r--src/resolve/resolved-dns-server.h1
-rw-r--r--src/resolve/resolved-dns-stream.h1
-rw-r--r--src/resolve/resolved-dns-transaction.c3
-rw-r--r--src/resolve/resolved-dns-transaction.h2
-rw-r--r--src/resolve/resolved-link.c6
-rw-r--r--src/resolve/resolved-link.h2
-rw-r--r--src/resolve/resolved-manager.c50
-rw-r--r--src/resolve/resolved-manager.h4
-rw-r--r--src/resolve/resolved.c2
-rw-r--r--src/resolve/resolved.conf.in5
-rw-r--r--src/resolve/test-dns-domain.c3
-rw-r--r--src/run/run.c135
-rw-r--r--src/shared/acl-util.c182
-rw-r--r--src/shared/acl-util.h6
-rw-r--r--src/shared/acpi-fpdt.c2
-rw-r--r--src/shared/apparmor-util.c1
-rw-r--r--src/shared/architecture.c10
-rw-r--r--src/shared/architecture.h2
-rw-r--r--src/shared/arphrd-list.c1
-rw-r--r--src/shared/ask-password-api.c7
-rw-r--r--src/shared/ask-password-api.h1
-rw-r--r--src/shared/audit.c10
-rw-r--r--src/shared/audit.h4
-rw-r--r--src/shared/barrier.c7
-rw-r--r--src/shared/barrier.h7
-rw-r--r--src/shared/base-filesystem.c23
-rw-r--r--src/shared/base-filesystem.h4
-rw-r--r--src/shared/boot-timestamps.c3
-rw-r--r--src/shared/btrfs-ctree.h6
-rw-r--r--src/shared/btrfs-util.c703
-rw-r--r--src/shared/btrfs-util.h31
-rw-r--r--src/shared/bus-label.c24
-rw-r--r--src/shared/bus-label.h9
-rw-r--r--src/shared/cap-list.c1
-rw-r--r--src/shared/capability.c7
-rw-r--r--src/shared/capability.h3
-rw-r--r--src/shared/cgroup-show.c3
-rw-r--r--src/shared/cgroup-show.h1
-rw-r--r--src/shared/cgroup-util.c309
-rw-r--r--src/shared/cgroup-util.h2
-rw-r--r--src/shared/clean-ipc.c2
-rw-r--r--src/shared/clock-util.c13
-rw-r--r--src/shared/clock-util.h1
-rw-r--r--src/shared/condition.c11
-rw-r--r--src/shared/conf-files.c9
-rw-r--r--src/shared/conf-files.h1
-rw-r--r--src/shared/conf-parser.c85
-rw-r--r--src/shared/conf-parser.h17
-rw-r--r--src/shared/copy.c24
-rw-r--r--src/shared/dev-setup.c34
-rw-r--r--src/shared/dev-setup.h4
-rw-r--r--src/shared/device-nodes.c24
-rw-r--r--src/shared/dropin.c15
-rw-r--r--src/shared/efivars.c406
-rw-r--r--src/shared/efivars.h94
-rw-r--r--src/shared/env-util.c145
-rw-r--r--src/shared/env-util.h4
-rw-r--r--src/shared/errno-list.c1
-rw-r--r--src/shared/exit-status.c17
-rw-r--r--src/shared/exit-status.h1
-rw-r--r--src/shared/fdset.c3
-rw-r--r--src/shared/fdset.h1
-rw-r--r--src/shared/fileio-label.c4
-rw-r--r--src/shared/formats-util.h63
-rw-r--r--src/shared/fstab-util.c30
-rw-r--r--src/shared/fstab-util.h2
-rw-r--r--src/shared/fw-util.c8
-rw-r--r--src/shared/fw-util.h4
-rw-r--r--src/shared/generator.c97
-rw-r--r--src/shared/generator.h17
-rw-r--r--src/shared/hashmap.c68
-rw-r--r--src/shared/hashmap.h26
-rw-r--r--src/shared/hostname-util.c193
-rw-r--r--src/shared/hostname-util.h39
-rw-r--r--src/shared/import-util.c21
-rw-r--r--src/shared/import-util.h2
-rw-r--r--src/shared/install-printf.c28
-rw-r--r--src/shared/install-printf.h3
-rw-r--r--src/shared/install.c279
-rw-r--r--src/shared/install.h48
-rw-r--r--src/shared/json.c442
-rw-r--r--src/shared/json.h40
-rw-r--r--src/shared/lockfile-util.c154
-rw-r--r--src/shared/lockfile-util.h39
-rw-r--r--src/shared/log.c79
-rw-r--r--src/shared/log.h37
-rw-r--r--src/shared/login-shared.c6
-rw-r--r--src/shared/logs-show.c68
-rw-r--r--src/shared/logs-show.h1
-rw-r--r--src/shared/machine-image.c57
-rw-r--r--src/shared/machine-image.h5
-rw-r--r--src/shared/machine-pool.c379
-rw-r--r--src/shared/machine-pool.h30
-rw-r--r--src/shared/macro.h9
-rw-r--r--src/shared/memfd-util.c2
-rw-r--r--src/shared/memfd-util.h5
-rw-r--r--src/shared/missing.h240
-rw-r--r--src/shared/mkdir-label.c6
-rw-r--r--src/shared/mkdir.c11
-rw-r--r--src/shared/mkdir.h1
-rw-r--r--src/shared/ordered-set.h59
-rw-r--r--src/shared/pager.c3
-rw-r--r--src/shared/path-lookup.c16
-rw-r--r--src/shared/path-lookup.h22
-rw-r--r--src/shared/path-util.c274
-rw-r--r--src/shared/path-util.h32
-rw-r--r--src/shared/prioq.c6
-rw-r--r--src/shared/prioq.h2
-rw-r--r--src/shared/process-util.c539
-rw-r--r--src/shared/process-util.h65
-rw-r--r--src/shared/pty.c9
-rw-r--r--src/shared/pty.h5
-rw-r--r--src/shared/ptyfwd.c78
-rw-r--r--src/shared/ptyfwd.h5
-rw-r--r--src/shared/random-util.c129
-rw-r--r--src/shared/random-util.h38
-rw-r--r--src/shared/ratelimit.c2
-rw-r--r--src/shared/ring.c1
-rw-r--r--src/shared/ring.h5
-rw-r--r--src/shared/rm-rf.c224
-rw-r--r--src/shared/rm-rf.h34
-rw-r--r--src/shared/selinux-util.c4
-rw-r--r--src/shared/selinux-util.h1
-rw-r--r--src/shared/set.h2
-rw-r--r--src/shared/signal-util.c228
-rw-r--r--src/shared/signal-util.h41
-rw-r--r--src/shared/smack-util.c3
-rw-r--r--src/shared/socket-label.c11
-rw-r--r--src/shared/socket-util.c159
-rw-r--r--src/shared/socket-util.h6
-rw-r--r--src/shared/spawn-ask-password-agent.c5
-rw-r--r--src/shared/spawn-polkit-agent.c5
-rw-r--r--src/shared/special.h9
-rw-r--r--src/shared/specifier.c1
-rw-r--r--src/shared/strbuf.h2
-rw-r--r--src/shared/strv.c8
-rw-r--r--src/shared/strv.h4
-rw-r--r--src/shared/strxcpyx.h2
-rw-r--r--src/shared/switch-root.c7
-rw-r--r--src/shared/sysctl-util.c80
-rw-r--r--src/shared/sysctl-util.h (renamed from src/boot/boot-loader.h)8
-rw-r--r--src/shared/terminal-util.c1072
-rw-r--r--src/shared/terminal-util.h109
-rw-r--r--src/shared/time-dst.c335
-rw-r--r--src/shared/time-util.c17
-rw-r--r--src/shared/time-util.h2
-rw-r--r--src/shared/udev-util.h4
-rw-r--r--src/shared/unit-name.c678
-rw-r--r--src/shared/unit-name.h86
-rw-r--r--src/shared/utf8.c13
-rw-r--r--src/shared/utf8.h2
-rw-r--r--src/shared/util.c3234
-rw-r--r--src/shared/util.h315
-rw-r--r--src/shared/utmp-wtmp.c22
-rw-r--r--src/shared/utmp-wtmp.h15
-rw-r--r--src/shared/virt.c25
-rw-r--r--src/shutdownd/shutdownd.c459
-rw-r--r--src/sleep/sleep.c2
-rw-r--r--src/socket-proxy/socket-proxyd.c2
-rw-r--r--src/sysctl/sysctl.c78
-rw-r--r--src/system-update-generator/system-update-generator.c2
-rw-r--r--src/systemctl/systemctl.c744
-rwxr-xr-xsrc/systemctl/systemd-sysv-install.SKELETON47
-rw-r--r--src/systemd/sd-bus.h79
-rw-r--r--src/systemd/sd-daemon.h2
-rw-r--r--src/systemd/sd-device.h99
-rw-r--r--src/systemd/sd-event.h4
-rw-r--r--src/systemd/sd-id128.h4
-rw-r--r--src/systemd/sd-login.h25
-rw-r--r--src/systemd/sd-messages.h2
-rw-r--r--src/systemd/sd-network.h6
-rw-r--r--src/systemd/sd-shutdown.h119
-rw-r--r--src/sysusers/sysusers.c44
-rw-r--r--src/sysv-generator/sysv-generator.c315
-rw-r--r--src/test/test-barrier.c3
-rw-r--r--src/test/test-btrfs.c67
-rw-r--r--src/test/test-cap-list.c1
-rw-r--r--src/test/test-capability.c1
-rw-r--r--src/test/test-cgroup-mask.c7
-rw-r--r--src/test/test-cgroup-util.c79
-rw-r--r--src/test/test-cgroup.c1
-rw-r--r--src/test/test-condition.c1
-rw-r--r--src/test/test-conf-files.c4
-rw-r--r--src/test/test-conf-parser.c234
-rw-r--r--src/test/test-copy.c9
-rw-r--r--src/test/test-ellipsize.c2
-rw-r--r--src/test/test-engine.c9
-rw-r--r--src/test/test-env-replace.c27
-rw-r--r--src/test/test-execute.c8
-rw-r--r--src/test/test-fdset.c53
-rw-r--r--src/test/test-fileio.c1
-rw-r--r--src/test/test-hashmap-plain.c2
-rw-r--r--src/test/test-hashmap.c4
-rw-r--r--src/test/test-hostname.c5
-rw-r--r--src/test/test-install.c4
-rw-r--r--src/test/test-job-type.c3
-rw-r--r--src/test/test-json.c97
-rw-r--r--src/test/test-libudev.c12
-rw-r--r--src/test/test-locale-util.c1
-rw-r--r--src/test/test-log.c1
-rw-r--r--src/test/test-loopback.c3
-rw-r--r--src/test/test-namespace.c1
-rw-r--r--src/test/test-ns.c28
-rw-r--r--src/test/test-path-lookup.c22
-rw-r--r--src/test/test-path-util.c137
-rw-r--r--src/test/test-path.c18
-rw-r--r--src/test/test-process-util.c138
-rw-r--r--src/test/test-pty.c5
-rw-r--r--src/test/test-ring.c5
-rw-r--r--src/test/test-rtnl-manual.c5
-rw-r--r--src/test/test-sched-prio.c2
-rw-r--r--src/test/test-set.c18
-rw-r--r--src/test/test-sleep.c5
-rw-r--r--src/test/test-socket-util.c19
-rw-r--r--src/test/test-strip-tab-ansi.c1
-rw-r--r--src/test/test-strv.c6
-rw-r--r--src/test/test-strxcpyx.c1
-rw-r--r--src/test/test-tables.c2
-rw-r--r--src/test/test-terminal-util.c84
-rw-r--r--src/test/test-time.c6
-rw-r--r--src/test/test-tmpfiles.c3
-rw-r--r--src/test/test-udev.c28
-rw-r--r--src/test/test-unit-file.c65
-rw-r--r--src/test/test-unit-name.c361
-rw-r--r--src/test/test-utf8.c2
-rw-r--r--src/test/test-util.c347
-rw-r--r--src/test/test-watchdog.c1
-rw-r--r--src/timedate/timedatectl.c97
-rw-r--r--src/timedate/timedated.c145
-rw-r--r--src/timesync/timesyncd-conf.c1
-rw-r--r--src/timesync/timesyncd-manager.c26
-rw-r--r--src/timesync/timesyncd-manager.h2
-rw-r--r--src/timesync/timesyncd-server.h2
-rw-r--r--src/timesync/timesyncd.c2
-rw-r--r--src/timesync/timesyncd.conf.in7
-rw-r--r--src/tmpfiles/tmpfiles.c639
-rw-r--r--src/tty-ask-password-agent/tty-ask-password-agent.c7
-rw-r--r--src/udev/.gitignore2
-rw-r--r--src/udev/accelerometer/accelerometer.c6
-rw-r--r--src/udev/ata_id/ata_id.c2
-rw-r--r--src/udev/cdrom_id/cdrom_id.c28
-rw-r--r--src/udev/collect/collect.c8
l---------src/udev/mtd_probe/Makefile (renamed from src/gudev/Makefile)0
-rw-r--r--src/udev/mtd_probe/mtd_probe.c4
-rw-r--r--src/udev/mtd_probe/mtd_probe.h6
-rw-r--r--src/udev/net/ethtool-util.c3
-rw-r--r--src/udev/net/link-config.c30
-rw-r--r--src/udev/net/link-config.h5
-rw-r--r--src/udev/scsi_id/scsi_serial.c1
-rw-r--r--src/udev/udev-builtin-blkid.c4
-rw-r--r--src/udev/udev-builtin-btrfs.c4
-rw-r--r--src/udev/udev-builtin-hwdb.c6
-rw-r--r--src/udev/udev-builtin-input_id.c175
-rw-r--r--src/udev/udev-builtin-keyboard.c251
-rw-r--r--src/udev/udev-builtin-kmod.c5
-rw-r--r--src/udev/udev-builtin-net_id.c58
-rw-r--r--src/udev/udev-builtin-path_id.c30
-rw-r--r--src/udev/udev-builtin-uaccess.c7
-rw-r--r--src/udev/udev-builtin-usb_id.c68
-rw-r--r--src/udev/udev-builtin.c11
-rw-r--r--src/udev/udev-ctrl.c75
-rw-r--r--src/udev/udev-event.c266
-rw-r--r--src/udev/udev-node.c5
-rw-r--r--src/udev/udev-rules.c111
-rw-r--r--src/udev/udev-watch.c4
-rw-r--r--src/udev/udev.h13
-rw-r--r--src/udev/udevadm-control.c5
-rw-r--r--src/udev/udevadm-hwdb.c1
-rw-r--r--src/udev/udevadm-info.c17
-rw-r--r--src/udev/udevadm-monitor.c8
-rw-r--r--src/udev/udevadm-settle.c15
-rw-r--r--src/udev/udevadm-test-builtin.c10
-rw-r--r--src/udev/udevadm-test.c20
-rw-r--r--src/udev/udevadm-trigger.c7
-rw-r--r--src/udev/udevadm.c3
-rw-r--r--src/udev/udevd.c1623
-rw-r--r--src/udev/v4l_id/v4l_id.c19
-rw-r--r--src/update-done/update-done.c24
-rw-r--r--src/update-utmp/update-utmp.c11
l---------src/user-sessions/Makefile (renamed from src/shutdownd/Makefile)0
-rw-r--r--src/user-sessions/user-sessions.c (renamed from src/login/user-sessions.c)1
-rw-r--r--src/vconsole/90-vconsole.rules.in7
-rw-r--r--src/vconsole/vconsole-setup.c90
797 files changed, 40308 insertions, 23640 deletions
diff --git a/src/activate/activate.c b/src/activate/activate.c
index 2689934c40..d345e28567 100644
--- a/src/activate/activate.c
+++ b/src/activate/activate.c
@@ -20,7 +20,6 @@
***/
#include <unistd.h>
-#include <fcntl.h>
#include <sys/epoll.h>
#include <sys/prctl.h>
#include <sys/socket.h>
diff --git a/src/analyze/analyze-verify.c b/src/analyze/analyze-verify.c
index f569109268..f4255f979e 100644
--- a/src/analyze/analyze-verify.c
+++ b/src/analyze/analyze-verify.c
@@ -20,13 +20,11 @@
***/
#include <stdlib.h>
-#include <getopt.h>
#include "manager.h"
#include "bus-util.h"
#include "log.h"
#include "strv.h"
-#include "build.h"
#include "pager.h"
#include "analyze-verify.h"
@@ -74,8 +72,7 @@ static int verify_socket(Unit *u) {
/* This makes sure instance is created if necessary. */
r = socket_instantiate_service(SOCKET(u));
if (r < 0) {
- log_unit_error(u->id, "Socket %s cannot be started, failed to create instance.",
- u->id);
+ log_unit_error_errno(u, r, "Socket cannot be started, failed to create instance: %m");
return r;
}
@@ -84,11 +81,10 @@ static int verify_socket(Unit *u) {
Service *service;
service = SERVICE(UNIT_DEREF(SOCKET(u)->service));
- log_unit_debug(u->id, "%s uses %s", u->id, UNIT(service)->id);
+ log_unit_debug(u, "Using %s", UNIT(service)->id);
if (UNIT(service)->load_state != UNIT_LOADED) {
- log_unit_error(u->id, "Service %s not loaded, %s cannot be started.",
- UNIT(service)->id, u->id);
+ log_unit_error(u, "Service %s not loaded, %s cannot be started.", UNIT(service)->id, u->id);
return -ENOENT;
}
}
@@ -100,11 +96,8 @@ static int verify_executable(Unit *u, ExecCommand *exec) {
if (exec == NULL)
return 0;
- if (access(exec->path, X_OK) < 0) {
- log_unit_error(u->id, "%s: command %s is not executable: %m",
- u->id, exec->path);
- return -errno;
- }
+ if (access(exec->path, X_OK) < 0)
+ return log_unit_error_errno(u, errno, "Command %s is not executable: %m", exec->path);
return 0;
}
@@ -145,16 +138,15 @@ static int verify_documentation(Unit *u, bool check_man) {
int r = 0, k;
STRV_FOREACH(p, u->documentation) {
- log_unit_debug(u->id, "%s: found documentation item %s.", u->id, *p);
+ log_unit_debug(u, "Found documentation item: %s", *p);
+
if (check_man && startswith(*p, "man:")) {
k = show_man_page(*p + 4, true);
if (k != 0) {
if (k < 0)
- log_unit_error(u->id, "%s: can't show %s: %s",
- u->id, *p, strerror(-r));
+ log_unit_error_errno(u, r, "Can't show %s: %m", *p);
else {
- log_unit_error(u->id, "%s: man %s command failed with code %d",
- u->id, *p + 4, k);
+ log_unit_error_errno(u, r, "man %s command failed with code %d", *p + 4, k);
k = -ENOEXEC;
}
if (r == 0)
@@ -178,14 +170,12 @@ static int verify_unit(Unit *u, bool check_man) {
if (log_get_max_level() >= LOG_DEBUG)
unit_dump(u, stdout, "\t");
- log_unit_debug(u->id, "Creating %s/start job", u->id);
+ log_unit_debug(u, "Creating %s/start job", u->id);
r = manager_add_job(u->manager, JOB_START, u, JOB_REPLACE, false, &err, &j);
if (sd_bus_error_is_set(&err))
- log_unit_error(u->id, "Error: %s: %s",
- err.name, err.message);
+ log_unit_error(u, "Error: %s: %s", err.name, err.message);
if (r < 0)
- log_unit_error(u->id, "Failed to create %s/start: %s",
- u->id, strerror(-r));
+ log_unit_error_errno(u, r, "Failed to create %s/start: %m", u->id);
k = verify_socket(u);
if (k < 0 && r == 0)
@@ -202,7 +192,7 @@ static int verify_unit(Unit *u, bool check_man) {
return r;
}
-int verify_units(char **filenames, SystemdRunningAs running_as, bool check_man) {
+int verify_units(char **filenames, ManagerRunningAs running_as, bool check_man) {
_cleanup_bus_error_free_ sd_bus_error err = SD_BUS_ERROR_NULL;
Manager *m = NULL;
FILE *serial = NULL;
@@ -228,7 +218,7 @@ int verify_units(char **filenames, SystemdRunningAs running_as, bool check_man)
r = manager_new(running_as, true, &m);
if (r < 0)
- return log_error_errno(r, "Failed to initalize manager: %m");
+ return log_error_errno(r, "Failed to initialize manager: %m");
log_debug("Starting manager...");
diff --git a/src/analyze/analyze-verify.h b/src/analyze/analyze-verify.h
index f10c34c4ae..d2d4a7f190 100644
--- a/src/analyze/analyze-verify.h
+++ b/src/analyze/analyze-verify.h
@@ -25,4 +25,4 @@
#include "path-lookup.h"
-int verify_units(char **filenames, SystemdRunningAs running_as, bool check_man);
+int verify_units(char **filenames, ManagerRunningAs running_as, bool check_man);
diff --git a/src/analyze/analyze.c b/src/analyze/analyze.c
index 591b4ab14e..9583458f72 100644
--- a/src/analyze/analyze.c
+++ b/src/analyze/analyze.c
@@ -24,23 +24,21 @@
#include <stdlib.h>
#include <getopt.h>
#include <locale.h>
-#include <sys/utsname.h>
#include "sd-bus.h"
#include "bus-util.h"
#include "bus-error.h"
-#include "install.h"
#include "log.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"
#include "pager.h"
#include "analyze-verify.h"
+#include "terminal-util.h"
#define SCALE_X (0.1 / 1000.0) /* pixels per us */
#define SCALE_Y (20.0)
@@ -1330,7 +1328,7 @@ int main(int argc, char *argv[]) {
if (streq_ptr(argv[optind], "verify"))
r = verify_units(argv+optind+1,
- arg_user ? SYSTEMD_USER : SYSTEMD_SYSTEM,
+ arg_user ? MANAGER_USER : MANAGER_SYSTEM,
arg_man);
else {
_cleanup_bus_close_unref_ sd_bus *bus = NULL;
diff --git a/src/ask-password/ask-password.c b/src/ask-password/ask-password.c
index ad8ad659d2..2cbed293ba 100644
--- a/src/ask-password/ask-password.c
+++ b/src/ask-password/ask-password.c
@@ -19,25 +19,13 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <sys/socket.h>
-#include <poll.h>
-#include <sys/types.h>
-#include <assert.h>
-#include <string.h>
#include <errno.h>
#include <unistd.h>
-#include <fcntl.h>
-#include <sys/un.h>
-#include <sys/stat.h>
-#include <sys/signalfd.h>
#include <getopt.h>
-#include <termios.h>
-#include <limits.h>
#include <stddef.h>
#include "log.h"
#include "macro.h"
-#include "util.h"
#include "strv.h"
#include "ask-password-api.h"
#include "def.h"
diff --git a/src/binfmt/binfmt.c b/src/binfmt/binfmt.c
index 089b7754a4..6028ed68c0 100644
--- a/src/binfmt/binfmt.c
+++ b/src/binfmt/binfmt.c
@@ -25,11 +25,9 @@
#include <string.h>
#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"
diff --git a/src/boot/boot-efi.c b/src/boot/boot-efi.c
deleted file mode 100644
index bd0c59bd5a..0000000000
--- a/src/boot/boot-efi.c
+++ /dev/null
@@ -1,190 +0,0 @@
-/*-*- 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 = NULL;
- 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];
- memzero(e, 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;
-
- 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_loader_get_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
deleted file mode 100644
index d44fdb3aef..0000000000
--- a/src/boot/boot-loader.c
+++ /dev/null
@@ -1,132 +0,0 @@
-/*-*- 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.h b/src/boot/boot.h
deleted file mode 100644
index bd8dc69d3d..0000000000
--- a/src/boot/boot.h
+++ /dev/null
@@ -1,64 +0,0 @@
-/*-*- 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
index 51b51c4254..1e65597acf 100644
--- a/src/boot/bootctl.c
+++ b/src/boot/bootctl.c
@@ -3,7 +3,8 @@
/***
This file is part of systemd.
- Copyright 2013 Kay Sievers
+ Copyright 2013-2015 Kay Sievers
+ 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
@@ -19,249 +20,1115 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdlib.h>
-#include <stdbool.h>
-#include <unistd.h>
+#include <stdio.h>
#include <getopt.h>
-#include <locale.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <sys/statfs.h>
+#include <sys/stat.h>
+#include <errno.h>
#include <string.h>
-#include <sys/timex.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <dirent.h>
+#include <ctype.h>
+#include <limits.h>
+#include <ftw.h>
+#include <stdbool.h>
+#include <blkid/blkid.h>
-#include "boot.h"
+#include "efivars.h"
#include "build.h"
#include "util.h"
-#include "utf8.h"
+#include "rm-rf.h"
+#include "blkid-util.h"
-static void help(void) {
- printf("%s [OPTIONS...] COMMAND ...\n\n"
- "Query or change firmware and boot manager 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);
+static int verify_esp(const char *p, uint32_t *part, uint64_t *pstart, uint64_t *psize, sd_id128_t *uuid) {
+ struct statfs sfs;
+ struct stat st, st2;
+ _cleanup_free_ char *t = NULL;
+ _cleanup_blkid_free_probe_ blkid_probe b = NULL;
+ int r;
+ const char *v, *t2;
+
+ if (statfs(p, &sfs) < 0)
+ return log_error_errno(errno, "Failed to check file system type of \"%s\": %m", p);
+
+ if (sfs.f_type != 0x4d44) {
+ log_error("File system \"%s\" is not a FAT EFI System Partition (ESP) file system.", p);
+ return -ENODEV;
+ }
+
+ if (stat(p, &st) < 0)
+ return log_error_errno(errno, "Failed to determine block device node of \"%s\": %m", p);
+
+ if (major(st.st_dev) == 0) {
+ log_error("Block device node of %p is invalid.", p);
+ return -ENODEV;
+ }
+
+ t2 = strjoina(p, "/..");
+ r = stat(t2, &st2);
+ if (r < 0)
+ return log_error_errno(errno, "Failed to determine block device node of parent of \"%s\": %m", p);
+
+ if (st.st_dev == st2.st_dev) {
+ log_error("Directory \"%s\" is not the root of the EFI System Partition (ESP) file system.", p);
+ return -ENODEV;
+ }
+
+ r = asprintf(&t, "/dev/block/%u:%u", major(st.st_dev), minor(st.st_dev));
+ if (r < 0)
+ return log_oom();
+
+ errno = 0;
+ b = blkid_new_probe_from_filename(t);
+ if (!b) {
+ if (errno == 0)
+ return log_oom();
+
+ return log_error_errno(errno, "Failed to open file system \"%s\": %m", p);
+ }
+
+ blkid_probe_enable_superblocks(b, 1);
+ blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE);
+ blkid_probe_enable_partitions(b, 1);
+ blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS);
+
+ errno = 0;
+ r = blkid_do_safeprobe(b);
+ if (r == -2) {
+ log_error("File system \"%s\" is ambigious.", p);
+ return -ENODEV;
+ } else if (r == 1) {
+ log_error("File system \"%s\" does not contain a label.", p);
+ return -ENODEV;
+ } else if (r != 0) {
+ r = errno ? -errno : -EIO;
+ return log_error_errno(r, "Failed to probe file system \"%s\": %m", p);
+ }
+
+ errno = 0;
+ r = blkid_probe_lookup_value(b, "TYPE", &v, NULL);
+ if (r != 0) {
+ r = errno ? -errno : -EIO;
+ return log_error_errno(r, "Failed to probe file system type \"%s\": %m", p);
+ }
+
+ if (!streq(v, "vfat")) {
+ log_error("File system \"%s\" is not FAT.", p);
+ return -ENODEV;
+ }
+
+ errno = 0;
+ r = blkid_probe_lookup_value(b, "PART_ENTRY_SCHEME", &v, NULL);
+ if (r != 0) {
+ r = errno ? -errno : -EIO;
+ return log_error_errno(r, "Failed to probe partition scheme \"%s\": %m", p);
+ }
+
+ if (!streq(v, "gpt")) {
+ log_error("File system \"%s\" is not on a GPT partition table.", p);
+ return -ENODEV;
+ }
+
+ errno = 0;
+ r = blkid_probe_lookup_value(b, "PART_ENTRY_TYPE", &v, NULL);
+ if (r != 0) {
+ r = errno ? -errno : -EIO;
+ return log_error_errno(r, "Failed to probe partition type UUID \"%s\": %m", p);
+ }
+
+ if (!streq(v, "c12a7328-f81f-11d2-ba4b-00a0c93ec93b")) {
+ log_error("File system \"%s\" has wrong type for an EFI System Partition (ESP).", p);
+ return -ENODEV;
+ }
+
+ errno = 0;
+ r = blkid_probe_lookup_value(b, "PART_ENTRY_UUID", &v, NULL);
+ if (r != 0) {
+ r = errno ? -errno : -EIO;
+ return log_error_errno(r, "Failed to probe partition entry UUID \"%s\": %m", p);
+ }
+
+ r = sd_id128_from_string(v, uuid);
+ if (r < 0) {
+ log_error("Partition \"%s\" has invalid UUID \"%s\".", p, v);
+ return -EIO;
+ }
+
+ errno = 0;
+ r = blkid_probe_lookup_value(b, "PART_ENTRY_NUMBER", &v, NULL);
+ if (r != 0) {
+ r = errno ? -errno : -EIO;
+ return log_error_errno(r, "Failed to probe partition number \"%s\": m", p);
+ }
+ *part = strtoul(v, NULL, 10);
+
+ errno = 0;
+ r = blkid_probe_lookup_value(b, "PART_ENTRY_OFFSET", &v, NULL);
+ if (r != 0) {
+ r = errno ? -errno : -EIO;
+ return log_error_errno(r, "Failed to probe partition offset \"%s\": %m", p);
+ }
+ *pstart = strtoul(v, NULL, 10);
+
+ errno = 0;
+ r = blkid_probe_lookup_value(b, "PART_ENTRY_SIZE", &v, NULL);
+ if (r != 0) {
+ r = errno ? -errno : -EIO;
+ return log_error_errno(r, "Failed to probe partition size \"%s\": %m", p);
+ }
+ *psize = strtoul(v, NULL, 10);
+
+ return 0;
}
-static int parse_argv(int argc, char *argv[]) {
- enum {
- ARG_VERSION = 0x100,
- };
+/* search for "#### LoaderInfo: systemd-boot 218 ####" string inside the binary */
+static int get_file_version(int fd, char **v) {
+ struct stat st;
+ char *buf;
+ const char *s, *e;
+ char *x = NULL;
+ int r = 0;
- static const struct option options[] = {
- { "help", no_argument, NULL, 'h' },
- { "version", no_argument, NULL, ARG_VERSION },
- {}
- };
+ assert(fd >= 0);
+ assert(v);
- int c;
+ if (fstat(fd, &st) < 0)
+ return -errno;
- assert(argc >= 0);
- assert(argv);
+ if (st.st_size < 27)
+ return 0;
- while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
+ buf = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+ if (buf == MAP_FAILED)
+ return -errno;
- switch (c) {
+ s = memmem(buf, st.st_size - 8, "#### LoaderInfo: ", 17);
+ if (!s)
+ goto finish;
+ s += 17;
- case 'h':
- help();
+ e = memmem(s, st.st_size - (s - buf), " ####", 5);
+ if (!e || e - s < 3) {
+ log_error("Malformed version string.");
+ r = -EINVAL;
+ goto finish;
+ }
+
+ x = strndup(s, e - s);
+ if (!x) {
+ r = log_oom();
+ goto finish;
+ }
+ r = 1;
+
+finish:
+ munmap(buf, st.st_size);
+ *v = x;
+ return r;
+}
+
+static int enumerate_binaries(const char *esp_path, const char *path, const char *prefix) {
+ char *p;
+ _cleanup_closedir_ DIR *d = NULL;
+ struct dirent *de;
+ int r = 0, c = 0;
+
+ p = strjoina(esp_path, "/", path);
+ d = opendir(p);
+ if (!d) {
+ if (errno == ENOENT)
return 0;
- case ARG_VERSION:
- puts(PACKAGE_STRING);
- puts(SYSTEMD_FEATURES);
+ return log_error_errno(errno, "Failed to read \"%s\": %m", p);
+ }
+
+ while ((de = readdir(d))) {
+ _cleanup_close_ int fd = -1;
+ _cleanup_free_ char *v = NULL;
+
+ if (de->d_name[0] == '.')
+ continue;
+
+ if (!endswith_no_case(de->d_name, ".efi"))
+ continue;
+
+ if (prefix && !startswith_no_case(de->d_name, prefix))
+ continue;
+
+ fd = openat(dirfd(d), de->d_name, O_RDONLY|O_CLOEXEC);
+ if (fd < 0)
+ return log_error_errno(errno, "Failed to open \"%s/%s\" for reading: %m", p, de->d_name);
+
+ r = get_file_version(fd, &v);
+ if (r < 0)
+ return r;
+ if (r > 0)
+ printf(" File: └─/%s/%s (%s)\n", path, de->d_name, v);
+ else
+ printf(" File: └─/%s/%s\n", path, de->d_name);
+ c++;
+ }
+
+ return c;
+}
+
+static int status_binaries(const char *esp_path, sd_id128_t partition) {
+ int r;
+
+ printf("Boot Loader Binaries:\n");
+
+ printf(" ESP: /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(partition));
+
+ r = enumerate_binaries(esp_path, "EFI/systemd", NULL);
+ if (r == 0)
+ log_error("systemd-boot not installed in ESP.");
+ else if (r < 0)
+ return r;
+
+ r = enumerate_binaries(esp_path, "EFI/Boot", "boot");
+ if (r == 0)
+ log_error("No default/fallback boot loader installed in ESP.");
+ else if (r < 0)
+ return r;
+
+ return 0;
+}
+
+static int print_efi_option(uint16_t id, bool in_order) {
+ _cleanup_free_ char *title = NULL;
+ _cleanup_free_ char *path = NULL;
+ sd_id128_t partition;
+ bool active;
+ int r = 0;
+
+ r = efi_get_boot_option(id, &title, &partition, &path, &active);
+ if (r < 0)
+ return r;
+
+ /* print only configured entries with partition information */
+ if (!path || sd_id128_equal(partition, SD_ID128_NULL))
+ return 0;
+
+ efi_tilt_backslashes(path);
+
+ printf(" Title: %s\n", strna(title));
+ printf(" ID: 0x%04X\n", id);
+ printf(" Status: %sactive%s\n", active ? "" : "in", in_order ? ", boot-order" : "");
+ 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(partition));
+ printf(" File: └─%s\n", path);
+ printf("\n");
+
+ return 0;
+}
+
+static int status_variables(void) {
+ int n_options, n_order;
+ _cleanup_free_ uint16_t *options = NULL, *order = NULL;
+ int i;
+
+ if (!is_efi_boot()) {
+ log_notice("Not booted with EFI, not showing EFI variables.");
+ return 0;
+ }
+
+ n_options = efi_get_boot_options(&options);
+ if (n_options == -ENOENT)
+ return log_error_errno(ENOENT, "Failed to access EFI variables, efivarfs"
+ " needs to be available at /sys/firmware/efi/efivars/.");
+ else if (n_options < 0)
+ return log_error_errno(n_options, "Failed to read EFI boot entries: %m");
+
+ n_order = efi_get_boot_order(&order);
+ if (n_order == -ENOENT)
+ n_order = 0;
+ else if (n_order < 0)
+ return log_error_errno(n_order, "Failed to read EFI boot order.");
+
+ /* print entries in BootOrder first */
+ printf("Boot Loader Entries in EFI Variables:\n");
+ for (i = 0; i < n_order; i++)
+ print_efi_option(order[i], true);
+
+ /* print remaining entries */
+ for (i = 0; i < n_options; i++) {
+ int j;
+
+ for (j = 0; j < n_order; j++)
+ if (options[i] == order[j])
+ goto next;
+
+ print_efi_option(options[i], false);
+ next:
+ continue;
+ }
+
+ return 0;
+}
+
+static int compare_product(const char *a, const char *b) {
+ size_t x, y;
+
+ assert(a);
+ assert(b);
+
+ x = strcspn(a, " ");
+ y = strcspn(b, " ");
+ if (x != y)
+ return x < y ? -1 : x > y ? 1 : 0;
+
+ return strncmp(a, b, x);
+}
+
+static int compare_version(const char *a, const char *b) {
+ assert(a);
+ assert(b);
+
+ a += strcspn(a, " ");
+ a += strspn(a, " ");
+ b += strcspn(b, " ");
+ b += strspn(b, " ");
+
+ return strverscmp(a, b);
+}
+
+static int version_check(int fd, const char *from, const char *to) {
+ _cleanup_free_ char *a = NULL, *b = NULL;
+ _cleanup_close_ int fd2 = -1;
+ int r;
+
+ assert(fd >= 0);
+ assert(from);
+ assert(to);
+
+ r = get_file_version(fd, &a);
+ if (r < 0)
+ return r;
+ if (r == 0) {
+ log_error("Source file \"%s\" does not carry version information!", from);
+ return -EINVAL;
+ }
+
+ fd2 = open(to, O_RDONLY|O_CLOEXEC);
+ if (fd2 < 0) {
+ if (errno == ENOENT)
return 0;
- case '?':
- return -EINVAL;
+ return log_error_errno(errno, "Failed to open \"%s\" for reading: %m", to);
+ }
- default:
- assert_not_reached("Unhandled option");
+ r = get_file_version(fd2, &b);
+ if (r < 0)
+ return r;
+ if (r == 0 || compare_product(a, b) != 0) {
+ log_notice("Skipping \"%s\", since it's owned by another boot loader.", to);
+ return -EEXIST;
+ }
+
+ if (compare_version(a, b) < 0) {
+ log_warning("Skipping \"%s\", since a newer boot loader version exists already.", to);
+ return -ESTALE;
+ }
+
+ return 0;
+}
+
+static int copy_file(const char *from, const char *to, bool force) {
+ _cleanup_fclose_ FILE *f = NULL, *g = NULL;
+ char *p;
+ int r;
+ struct timespec t[2];
+ struct stat st;
+
+ assert(from);
+ assert(to);
+
+ f = fopen(from, "re");
+ if (!f)
+ return log_error_errno(errno, "Failed to open \"%s\" for reading: %m", from);
+
+ if (!force) {
+ /* If this is an update, then let's compare versions first */
+ r = version_check(fileno(f), from, to);
+ if (r < 0)
+ return r;
+ }
+
+ p = strjoina(to, "~");
+ g = fopen(p, "wxe");
+ if (!g) {
+ /* Directory doesn't exist yet? Then let's skip this... */
+ if (!force && errno == ENOENT)
+ return 0;
+
+ return log_error_errno(errno, "Failed to open \"%s\" for writing: %m", to);
+ }
+
+ rewind(f);
+ do {
+ size_t k;
+ uint8_t buf[32*1024];
+
+ k = fread(buf, 1, sizeof(buf), f);
+ if (ferror(f)) {
+ r = log_error_errno(EIO, "Failed to read \"%s\": %m", from);
+ goto error;
}
- return 1;
+ if (k == 0)
+ break;
+
+ fwrite(buf, 1, k, g);
+ if (ferror(g)) {
+ r = log_error_errno(EIO, "Failed to write \"%s\": %m", to);
+ goto error;
+ }
+ } while (!feof(f));
+
+ fflush(g);
+ if (ferror(g)) {
+ r = log_error_errno(EIO, "Failed to write \"%s\": %m", to);
+ goto error;
+ }
+
+ r = fstat(fileno(f), &st);
+ if (r < 0) {
+ r = log_error_errno(errno, "Failed to get file timestamps of \"%s\": %m", from);
+ goto error;
+ }
+
+ t[0] = st.st_atim;
+ t[1] = st.st_mtim;
+
+ r = futimens(fileno(g), t);
+ if (r < 0) {
+ r = log_error_errno(errno, "Failed to set file timestamps on \"%s\": %m", p);
+ goto error;
+ }
+
+ if (rename(p, to) < 0) {
+ r = log_error_errno(errno, "Failed to rename \"%s\" to \"%s\": %m", p, to);
+ goto error;
+ }
+
+ log_info("Copied \"%s\" to \"%s\".", from, to);
+ return 0;
+
+error:
+ unlink(p);
+ return r;
}
-static int boot_info_new(struct boot_info **info) {
- struct boot_info *in;
- int err;
+static char* strupper(char *s) {
+ char *p;
- in = new0(struct boot_info, 1);
- if (!in)
- return -ENOMEM;
+ for (p = s; *p; p++)
+ *p = toupper(*p);
- err = sd_id128_get_machine(&in->machine_id);
- if (err < 0)
- goto err;
+ return s;
+}
- err = sd_id128_get_boot(&in->boot_id);
- if (err < 0)
- goto err;
+static int mkdir_one(const char *prefix, const char *suffix) {
+ char *p;
- in->fw_entry_active = -1;
- in->loader_entry_active = -1;
+ p = strjoina(prefix, "/", suffix);
+ if (mkdir(p, 0700) < 0) {
+ if (errno != EEXIST)
+ return log_error_errno(errno, "Failed to create \"%s\": %m", p);
+ } else
+ log_info("Created \"%s\".", p);
- *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;
+static const char *efi_subdirs[] = {
+ "EFI",
+ "EFI/systemd",
+ "EFI/Boot",
+ "loader",
+ "loader/entries"
+};
- for (i = 0; i < n; i++) {
- free(entries[i].title);
- free(entries[i].path);
+static int create_dirs(const char *esp_path) {
+ int r;
+ unsigned i;
+
+ for (i = 0; i < ELEMENTSOF(efi_subdirs); i++) {
+ r = mkdir_one(esp_path, efi_subdirs[i]);
+ if (r < 0)
+ return r;
}
- free(entries);
+
+ return 0;
}
-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 copy_one_file(const char *esp_path, const char *name, bool force) {
+ char *p, *q;
+ int r;
+
+ p = strjoina(BOOTLIBDIR "/", name);
+ q = strjoina(esp_path, "/EFI/systemd/", name);
+ r = copy_file(p, q, force);
+
+ if (startswith(name, "systemd-boot")) {
+ int k;
+ char *v;
+
+ /* Create the EFI default boot loader name (specified for removable devices) */
+ v = strjoina(esp_path, "/EFI/Boot/BOOT", name + strlen("systemd-boot"));
+ strupper(strrchr(v, '/') + 1);
+
+ k = copy_file(p, v, force);
+ if (k < 0 && r == 0)
+ r = k;
+ }
+
+ return r;
+}
+
+static int install_binaries(const char *esp_path, bool force) {
+ struct dirent *de;
+ _cleanup_closedir_ DIR *d = NULL;
+ int r = 0;
+
+ if (force) {
+ /* Don't create any of these directories when we are
+ * just updating. When we update we'll drop-in our
+ * files (unless there are newer ones already), but we
+ * won't create the directories for them in the first
+ * place. */
+ r = create_dirs(esp_path);
+ if (r < 0)
+ return r;
+ }
+
+ d = opendir(BOOTLIBDIR);
+ if (!d)
+ return log_error_errno(errno, "Failed to open \""BOOTLIBDIR"\": %m");
+
+ while ((de = readdir(d))) {
+ int k;
+
+ if (de->d_name[0] == '.')
+ continue;
+
+ if (!endswith_no_case(de->d_name, ".efi"))
+ continue;
+
+ k = copy_one_file(esp_path, de->d_name, force);
+ if (k < 0 && r == 0)
+ r = k;
+ }
+
+ return r;
+}
+
+static bool same_entry(uint16_t id, const sd_id128_t uuid, const char *path) {
+ _cleanup_free_ char *opath = NULL;
+ sd_id128_t ouuid;
+ int r;
+
+ r = efi_get_boot_option(id, NULL, &ouuid, &opath, NULL);
+ if (r < 0)
+ return false;
+ if (!sd_id128_equal(uuid, ouuid))
+ return false;
+ if (!streq_ptr(path, opath))
+ return false;
+
+ return true;
+}
+
+static int find_slot(sd_id128_t uuid, const char *path, uint16_t *id) {
+ _cleanup_free_ uint16_t *options = NULL;
+ int n, i;
+
+ n = efi_get_boot_options(&options);
+ if (n < 0)
+ return n;
+
+ /* find already existing systemd-boot entry */
+ for (i = 0; i < n; i++)
+ if (same_entry(options[i], uuid, path)) {
+ *id = options[i];
+ return 1;
+ }
+
+ /* find free slot in the sorted BootXXXX variable list */
+ for (i = 0; i < n; i++)
+ if (i != options[i]) {
+ *id = i;
+ return 1;
+ }
+
+ /* use the next one */
+ if (i == 0xffff)
+ return -ENOSPC;
+ *id = i;
+ return 0;
}
-static int show_status(char **args, unsigned n) {
- char buf[64];
- struct boot_info *info;
- int err;
+static int insert_into_order(uint16_t slot, bool first) {
+ _cleanup_free_ uint16_t *order = NULL;
+ uint16_t *t;
+ int n, i;
+
+ n = efi_get_boot_order(&order);
+ if (n <= 0)
+ /* no entry, add us */
+ return efi_set_boot_order(&slot, 1);
+
+ /* are we the first and only one? */
+ if (n == 1 && order[0] == slot)
+ return 0;
+
+ /* are we already in the boot order? */
+ for (i = 0; i < n; i++) {
+ if (order[i] != slot)
+ continue;
+
+ /* we do not require to be the first one, all is fine */
+ if (!first)
+ return 0;
- err = boot_info_new(&info);
- if (err < 0)
+ /* move us to the first slot */
+ memmove(order + 1, order, i * sizeof(uint16_t));
+ order[0] = slot;
+ return efi_set_boot_order(order, n);
+ }
+
+ /* extend array */
+ t = realloc(order, (n + 1) * sizeof(uint16_t));
+ if (!t)
return -ENOMEM;
+ order = t;
- 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");
+ /* add us to the top or end of the list */
+ if (first) {
+ memmove(order + 1, order, n * sizeof(uint16_t));
+ order[0] = slot;
+ } else
+ order[n] = slot;
+
+ return efi_set_boot_order(order, n + 1);
+}
+
+static int remove_from_order(uint16_t slot) {
+ _cleanup_free_ uint16_t *order = NULL;
+ int n, i;
+
+ n = efi_get_boot_order(&order);
+ if (n <= 0)
+ return 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));
+ for (i = 0; i < n; i++) {
+ if (order[i] != slot)
+ continue;
+
+ if (i + 1 < n)
+ memmove(order + i, order + i+1, (n - i) * sizeof(uint16_t));
+ return efi_set_boot_order(order, n - 1);
+ }
+
+ return 0;
+}
+
+static int install_variables(const char *esp_path,
+ uint32_t part, uint64_t pstart, uint64_t psize,
+ sd_id128_t uuid, const char *path,
+ bool first) {
+ char *p;
+ uint16_t slot;
+ int r;
+
+ if (!is_efi_boot()) {
+ log_warning("Not booted with EFI, skipping EFI variable setup.");
+ return 0;
+ }
+
+ p = strjoina(esp_path, path);
+ if (access(p, F_OK) < 0) {
+ if (errno == ENOENT)
+ return 0;
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);
+ return log_error_errno(errno, "Cannot access \"%s\": %m", p);
}
- 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);
+ r = find_slot(uuid, path, &slot);
+ if (r < 0)
+ return log_error_errno(r,
+ r == -ENOENT ?
+ "Failed to access EFI variables. Is the \"efivarfs\" filesystem mounted?" :
+ "Failed to determine current boot order: %m");
+
+ if (first || r == false) {
+ r = efi_add_boot_option(slot, "Linux Boot Manager",
+ part, pstart, psize,
+ uuid, path);
+ if (r < 0)
+ return log_error_errno(r, "Failed to create EFI Boot variable entry: %m");
+
+ log_info("Created EFI boot entry \"Linux Boot Manager\".");
+ }
+
+ return insert_into_order(slot, first);
+}
+
+static int remove_boot_efi(const char *esp_path) {
+ char *p;
+ _cleanup_closedir_ DIR *d = NULL;
+ struct dirent *de;
+ int r, c = 0;
+
+ p = strjoina(esp_path, "/EFI/Boot");
+ d = opendir(p);
+ if (!d) {
+ if (errno == ENOENT)
+ return 0;
+
+ return log_error_errno(errno, "Failed to open directory \"%s\": %m", p);
+ }
+
+ while ((de = readdir(d))) {
+ _cleanup_close_ int fd = -1;
+ _cleanup_free_ char *v = NULL;
+
+ if (de->d_name[0] == '.')
+ continue;
+
+ if (!endswith_no_case(de->d_name, ".efi"))
+ continue;
+
+ if (!startswith_no_case(de->d_name, "Boot"))
+ continue;
+
+ fd = openat(dirfd(d), de->d_name, O_RDONLY|O_CLOEXEC);
+ if (fd < 0)
+ return log_error_errno(errno, "Failed to open \"%s/%s\" for reading: %m", p, de->d_name);
+
+ r = get_file_version(fd, &v);
+ if (r < 0)
+ return r;
+ if (r > 0 && startswith(v, "systemd-boot ")) {
+ r = unlinkat(dirfd(d), de->d_name, 0);
+ if (r < 0)
+ return log_error_errno(errno, "Failed to remove \"%s/%s\": %m", p, de->d_name);
+
+ log_info("Removed \"%s/\%s\".", p, de->d_name);
}
+
+ c++;
+ }
+
+ return c;
+}
+
+static int rmdir_one(const char *prefix, const char *suffix) {
+ char *p;
+
+ p = strjoina(prefix, "/", suffix);
+ if (rmdir(p) < 0) {
+ if (!IN_SET(errno, ENOENT, ENOTEMPTY))
+ return log_error_errno(errno, "Failed to remove \"%s\": %m", p);
} 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");
+ log_info("Removed \"%s\".", p);
- boot_info_free(info);
- return err;
+ return 0;
}
-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 },
+static int remove_binaries(const char *esp_path) {
+ char *p;
+ int r, q;
+ unsigned i;
+
+ p = strjoina(esp_path, "/EFI/systemd");
+ r = rm_rf(p, REMOVE_ROOT|REMOVE_PHYSICAL);
+
+ q = remove_boot_efi(esp_path);
+ if (q < 0 && r == 0)
+ r = q;
+
+ for (i = ELEMENTSOF(efi_subdirs); i > 0; i--) {
+ q = rmdir_one(esp_path, efi_subdirs[i-1]);
+ if (q < 0 && r == 0)
+ r = q;
+ }
+
+ return r;
+}
+
+static int remove_variables(sd_id128_t uuid, const char *path, bool in_order) {
+ uint16_t slot;
+ int r;
+
+ if (!is_efi_boot())
+ return 0;
+
+ r = find_slot(uuid, path, &slot);
+ if (r != 1)
+ return 0;
+
+ r = efi_remove_boot_option(slot);
+ if (r < 0)
+ return r;
+
+ if (in_order)
+ return remove_from_order(slot);
+ else
+ return 0;
+}
+
+static int install_loader_config(const char *esp_path) {
+ char *p;
+ char line[64];
+ char *machine = NULL;
+ _cleanup_fclose_ FILE *f = NULL, *g = NULL;
+
+ f = fopen("/etc/machine-id", "re");
+ if (!f)
+ return -errno;
+
+ if (fgets(line, sizeof(line), f) != NULL) {
+ char *s;
+
+ s = strchr(line, '\n');
+ if (s)
+ s[0] = '\0';
+ if (strlen(line) == 32)
+ machine = line;
+ }
+
+ if (!machine)
+ return -ESRCH;
+
+ p = strjoina(esp_path, "/loader/loader.conf");
+ g = fopen(p, "wxe");
+ if (g) {
+ fprintf(g, "#timeout 3\n");
+ fprintf(g, "default %s-*\n", machine);
+ if (ferror(g))
+ return log_error_errno(EIO, "Failed to write \"%s\": %m", p);
+ }
+
+ return 0;
+}
+
+static int help(void) {
+ printf("%s [COMMAND] [OPTIONS...]\n"
+ "\n"
+ "Install, update or remove the sdboot EFI boot manager.\n\n"
+ " -h --help Show this help\n"
+ " --version Print version\n"
+ " --path=PATH Path to the EFI System Partition (ESP)\n"
+ " --no-variables Don't touch EFI variables\n"
+ "\n"
+ "Commands:\n"
+ " status Show status of installed systemd-boot and EFI variables\n"
+ " install Install systemd-boot to the ESP and EFI variables\n"
+ " update Update systemd-boot in the ESP and EFI variables\n"
+ " remove Remove systemd-boot from the ESP and EFI variables\n",
+ program_invocation_short_name);
+
+ return 0;
+}
+
+static const char *arg_path = "/boot";
+static bool arg_touch_variables = true;
+
+static int parse_argv(int argc, char *argv[]) {
+ enum {
+ ARG_PATH = 0x100,
+ ARG_VERSION,
+ ARG_NO_VARIABLES,
};
- int left;
- unsigned i;
+ static const struct option options[] = {
+ { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, ARG_VERSION },
+ { "path", required_argument, NULL, ARG_PATH },
+ { "no-variables", no_argument, NULL, ARG_NO_VARIABLES },
+ { NULL, 0, NULL, 0 }
+ };
+
+ int c;
assert(argc >= 0);
assert(argv);
- left = argc - optind;
+ while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
+ switch (c) {
- if (left <= 0)
- /* Special rule: no arguments means "status" */
- i = 0;
- else {
- if (streq(argv[optind], "help")) {
+ case 'h':
help();
return 0;
+
+ case ARG_VERSION:
+ printf(VERSION "\n");
+ return 0;
+
+ case ARG_PATH:
+ arg_path = optarg;
+ break;
+
+ case ARG_NO_VARIABLES:
+ arg_touch_variables = false;
+ break;
+
+ case '?':
+ return -EINVAL;
+
+ default:
+ assert_not_reached("Unknown option");
}
- for (i = 0; i < ELEMENTSOF(verbs); i++)
- if (streq(argv[optind], verbs[i].verb))
- break;
+ return 1;
+}
+
+static void read_loader_efi_var(const char *name, char **var) {
+ int r;
+
+ r = efi_get_variable_string(EFI_VENDOR_LOADER, name, var);
+ if (r < 0 && r != -ENOENT)
+ log_warning_errno(r, "Failed to read EFI variable %s: %m", name);
+}
+static int bootctl_main(int argc, char*argv[]) {
+ enum action {
+ ACTION_STATUS,
+ ACTION_INSTALL,
+ ACTION_UPDATE,
+ ACTION_REMOVE
+ } arg_action = ACTION_STATUS;
+ static const struct {
+ const char* verb;
+ enum action action;
+ } verbs[] = {
+ { "status", ACTION_STATUS },
+ { "install", ACTION_INSTALL },
+ { "update", ACTION_UPDATE },
+ { "remove", ACTION_REMOVE },
+ };
+
+ sd_id128_t uuid = {};
+ uint32_t part = 0;
+ uint64_t pstart = 0, psize = 0;
+ int r, q;
+
+ if (argv[optind]) {
+ unsigned i;
+
+ for (i = 0; i < ELEMENTSOF(verbs); i++) {
+ if (!streq(argv[optind], verbs[i].verb))
+ continue;
+ arg_action = verbs[i].action;
+ break;
+ }
if (i >= ELEMENTSOF(verbs)) {
- log_error("Unknown operation %s", argv[optind]);
+ log_error("Unknown operation \"%s\"", argv[optind]);
return -EINVAL;
}
}
- switch (verbs[i].argc_cmp) {
+ if (geteuid() != 0)
+ return log_error_errno(EPERM, "Need to be root.");
- case EQUAL:
- if (left != verbs[i].argc) {
- log_error("Invalid number of arguments.");
- return -EINVAL;
- }
+ r = verify_esp(arg_path, &part, &pstart, &psize, &uuid);
+ if (r == -ENODEV && !arg_path)
+ log_notice("You might want to use --path= to indicate the path to your ESP, in case it is not mounted on /boot.");
+ if (r < 0)
+ return r;
+
+ switch (arg_action) {
+ case ACTION_STATUS: {
+ _cleanup_free_ char *fw_type = NULL;
+ _cleanup_free_ char *fw_info = NULL;
+ _cleanup_free_ char *loader = NULL;
+ _cleanup_free_ char *loader_path = NULL;
+ sd_id128_t loader_part_uuid = {};
+
+ if (is_efi_boot()) {
+ read_loader_efi_var("LoaderFirmwareType", &fw_type);
+ read_loader_efi_var("LoaderFirmwareInfo", &fw_info);
+ read_loader_efi_var("LoaderInfo", &loader);
+ read_loader_efi_var("LoaderImageIdentifier", &loader_path);
+ if (loader_path)
+ efi_tilt_backslashes(loader_path);
+ r = efi_loader_get_device_part_uuid(&loader_part_uuid);
+ if (r < 0 && r == -ENOENT)
+ log_warning_errno(r, "Failed to read EFI variable LoaderDevicePartUUID: %m");
+
+ printf("System:\n");
+ printf(" Firmware: %s (%s)\n", strna(fw_type), strna(fw_info));
+
+ r = is_efi_secure_boot();
+ if (r < 0)
+ log_warning_errno(r, "Failed to query secure boot status: %m");
+ else
+ printf(" Secure Boot: %s\n", r ? "enabled" : "disabled");
+
+ r = is_efi_secure_boot_setup_mode();
+ if (r < 0)
+ log_warning_errno(r, "Failed to query secure boot mode: %m");
+ else
+ printf(" Setup Mode: %s\n", r ? "setup" : "user");
+ printf("\n");
+
+ printf("Loader:\n");
+ printf(" Product: %s\n", strna(loader));
+ if (!sd_id128_equal(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(loader_part_uuid));
+ else
+ printf(" Partition: n/a\n");
+ printf(" File: %s%s\n", draw_special_char(DRAW_TREE_RIGHT), strna(loader_path));
+ printf("\n");
+ } else
+ printf("System:\n Not booted with EFI\n");
+
+ r = status_binaries(arg_path, uuid);
+ if (r < 0)
+ return r;
+
+ if (arg_touch_variables)
+ r = status_variables();
break;
+ }
- case MORE:
- if (left < verbs[i].argc) {
- log_error("Too few arguments.");
- return -EINVAL;
+ case ACTION_INSTALL:
+ case ACTION_UPDATE:
+ umask(0002);
+
+ r = install_binaries(arg_path, arg_action == ACTION_INSTALL);
+ if (r < 0)
+ return r;
+
+ if (arg_action == ACTION_INSTALL) {
+ r = install_loader_config(arg_path);
+ if (r < 0)
+ return r;
}
+
+ if (arg_touch_variables)
+ r = install_variables(arg_path,
+ part, pstart, psize, uuid,
+ "/EFI/systemd/systemd-boot" EFI_MACHINE_TYPE_NAME ".efi",
+ arg_action == ACTION_INSTALL);
break;
- case LESS:
- if (left > verbs[i].argc) {
- log_error("Too many arguments.");
- return -EINVAL;
+ case ACTION_REMOVE:
+ r = remove_binaries(arg_path);
+
+ if (arg_touch_variables) {
+ q = remove_variables(uuid, "/EFI/systemd/systemd-boot" EFI_MACHINE_TYPE_NAME ".efi", true);
+ if (q < 0 && r == 0)
+ r = q;
}
break;
-
- default:
- assert_not_reached("Unknown comparison operator.");
}
- return verbs[i].dispatch(argv + optind, left);
+ return r;
}
int main(int argc, char *argv[]) {
diff --git a/src/boot/efi/.gitignore b/src/boot/efi/.gitignore
new file mode 100644
index 0000000000..e193acbe12
--- /dev/null
+++ b/src/boot/efi/.gitignore
@@ -0,0 +1,2 @@
+/systemd_boot.so
+/stub.so
diff --git a/src/boot/efi/boot.c b/src/boot/efi/boot.c
new file mode 100644
index 0000000000..eb1a4e3b66
--- /dev/null
+++ b/src/boot/efi/boot.c
@@ -0,0 +1,1834 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that 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.
+ *
+ * Copyright (C) 2012-2015 Kay Sievers <kay@vrfy.org>
+ * Copyright (C) 2012-2015 Harald Hoyer <harald@redhat.com>
+ */
+
+#include <efi.h>
+#include <efilib.h>
+
+#include "util.h"
+#include "console.h"
+#include "graphics.h"
+#include "pefile.h"
+#include "linux.h"
+
+#ifndef EFI_OS_INDICATIONS_BOOT_TO_FW_UI
+#define EFI_OS_INDICATIONS_BOOT_TO_FW_UI 0x0000000000000001ULL
+#endif
+
+/* magic string to find in the binary image */
+static const char __attribute__((used)) magic[] = "#### LoaderInfo: systemd-boot " VERSION " ####";
+
+static const EFI_GUID global_guid = EFI_GLOBAL_VARIABLE;
+
+enum loader_type {
+ LOADER_UNDEFINED,
+ LOADER_EFI,
+ LOADER_LINUX
+};
+
+typedef struct {
+ CHAR16 *file;
+ CHAR16 *title_show;
+ CHAR16 *title;
+ CHAR16 *version;
+ CHAR16 *machine_id;
+ EFI_HANDLE *device;
+ enum loader_type type;
+ CHAR16 *loader;
+ CHAR16 *options;
+ CHAR16 key;
+ EFI_STATUS (*call)(VOID);
+ BOOLEAN no_autoselect;
+ BOOLEAN non_unique;
+} ConfigEntry;
+
+typedef struct {
+ ConfigEntry **entries;
+ UINTN entry_count;
+ INTN idx_default;
+ INTN idx_default_efivar;
+ UINTN timeout_sec;
+ UINTN timeout_sec_config;
+ INTN timeout_sec_efivar;
+ CHAR16 *entry_default_pattern;
+ CHAR16 *entry_oneshot;
+ CHAR16 *options_edit;
+ BOOLEAN no_editor;
+} Config;
+
+static VOID cursor_left(UINTN *cursor, UINTN *first)
+{
+ if ((*cursor) > 0)
+ (*cursor)--;
+ else if ((*first) > 0)
+ (*first)--;
+}
+
+static VOID cursor_right(UINTN *cursor, UINTN *first, UINTN x_max, UINTN len)
+{
+ if ((*cursor)+1 < x_max)
+ (*cursor)++;
+ else if ((*first) + (*cursor) < len)
+ (*first)++;
+}
+
+static BOOLEAN line_edit(CHAR16 *line_in, CHAR16 **line_out, UINTN x_max, UINTN y_pos) {
+ CHAR16 *line;
+ UINTN size;
+ UINTN len;
+ UINTN first;
+ CHAR16 *print;
+ UINTN cursor;
+ UINTN clear;
+ BOOLEAN exit;
+ BOOLEAN enter;
+
+ if (!line_in)
+ line_in = L"";
+ size = StrLen(line_in) + 1024;
+ line = AllocatePool(size * sizeof(CHAR16));
+ StrCpy(line, line_in);
+ len = StrLen(line);
+ print = AllocatePool((x_max+1) * sizeof(CHAR16));
+
+ uefi_call_wrapper(ST->ConOut->EnableCursor, 2, ST->ConOut, TRUE);
+
+ first = 0;
+ cursor = 0;
+ clear = 0;
+ enter = FALSE;
+ exit = FALSE;
+ while (!exit) {
+ EFI_STATUS err;
+ UINT64 key;
+ UINTN i;
+
+ i = len - first;
+ if (i >= x_max-1)
+ i = x_max-1;
+ CopyMem(print, line + first, i * sizeof(CHAR16));
+ while (clear > 0 && i < x_max-1) {
+ clear--;
+ print[i++] = ' ';
+ }
+ print[i] = '\0';
+
+ uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_pos);
+ uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, print);
+ uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, cursor, y_pos);
+
+ err = console_key_read(&key, TRUE);
+ if (EFI_ERROR(err))
+ continue;
+
+ switch (key) {
+ case KEYPRESS(0, SCAN_ESC, 0):
+ case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'c'):
+ case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'g'):
+ case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('c')):
+ case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('g')):
+ exit = TRUE;
+ break;
+
+ case KEYPRESS(0, SCAN_HOME, 0):
+ case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'a'):
+ case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('a')):
+ /* beginning-of-line */
+ cursor = 0;
+ first = 0;
+ continue;
+
+ case KEYPRESS(0, SCAN_END, 0):
+ case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'e'):
+ case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('e')):
+ /* end-of-line */
+ cursor = len - first;
+ if (cursor+1 >= x_max) {
+ cursor = x_max-1;
+ first = len - (x_max-1);
+ }
+ continue;
+
+ case KEYPRESS(0, SCAN_DOWN, 0):
+ case KEYPRESS(EFI_ALT_PRESSED, 0, 'f'):
+ case KEYPRESS(EFI_CONTROL_PRESSED, SCAN_RIGHT, 0):
+ /* forward-word */
+ while (line[first + cursor] && line[first + cursor] == ' ')
+ cursor_right(&cursor, &first, x_max, len);
+ while (line[first + cursor] && line[first + cursor] != ' ')
+ cursor_right(&cursor, &first, x_max, len);
+ uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, cursor, y_pos);
+ continue;
+
+ case KEYPRESS(0, SCAN_UP, 0):
+ case KEYPRESS(EFI_ALT_PRESSED, 0, 'b'):
+ case KEYPRESS(EFI_CONTROL_PRESSED, SCAN_LEFT, 0):
+ /* backward-word */
+ if ((first + cursor) > 0 && line[first + cursor-1] == ' ') {
+ cursor_left(&cursor, &first);
+ while ((first + cursor) > 0 && line[first + cursor] == ' ')
+ cursor_left(&cursor, &first);
+ }
+ while ((first + cursor) > 0 && line[first + cursor-1] != ' ')
+ cursor_left(&cursor, &first);
+ uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, cursor, y_pos);
+ continue;
+
+ case KEYPRESS(0, SCAN_RIGHT, 0):
+ case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'f'):
+ case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('f')):
+ /* forward-char */
+ if (first + cursor == len)
+ continue;
+ cursor_right(&cursor, &first, x_max, len);
+ uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, cursor, y_pos);
+ continue;
+
+ case KEYPRESS(0, SCAN_LEFT, 0):
+ case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'b'):
+ case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('b')):
+ /* backward-char */
+ cursor_left(&cursor, &first);
+ uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, cursor, y_pos);
+ continue;
+
+ case KEYPRESS(EFI_ALT_PRESSED, 0, 'd'):
+ /* kill-word */
+ clear = 0;
+ for (i = first + cursor; i < len && line[i] == ' '; i++)
+ clear++;
+ for (; i < len && line[i] != ' '; i++)
+ clear++;
+
+ for (i = first + cursor; i + clear < len; i++)
+ line[i] = line[i + clear];
+ len -= clear;
+ line[len] = '\0';
+ continue;
+
+ case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'w'):
+ case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('w')):
+ case KEYPRESS(EFI_ALT_PRESSED, 0, CHAR_BACKSPACE):
+ /* backward-kill-word */
+ clear = 0;
+ if ((first + cursor) > 0 && line[first + cursor-1] == ' ') {
+ cursor_left(&cursor, &first);
+ clear++;
+ while ((first + cursor) > 0 && line[first + cursor] == ' ') {
+ cursor_left(&cursor, &first);
+ clear++;
+ }
+ }
+ while ((first + cursor) > 0 && line[first + cursor-1] != ' ') {
+ cursor_left(&cursor, &first);
+ clear++;
+ }
+ uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, cursor, y_pos);
+
+ for (i = first + cursor; i + clear < len; i++)
+ line[i] = line[i + clear];
+ len -= clear;
+ line[len] = '\0';
+ continue;
+
+ case KEYPRESS(0, SCAN_DELETE, 0):
+ case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'd'):
+ case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('d')):
+ if (len == 0)
+ continue;
+ if (first + cursor == len)
+ continue;
+ for (i = first + cursor; i < len; i++)
+ line[i] = line[i+1];
+ clear = 1;
+ len--;
+ continue;
+
+ case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'k'):
+ case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('k')):
+ /* kill-line */
+ line[first + cursor] = '\0';
+ clear = len - (first + cursor);
+ len = first + cursor;
+ continue;
+
+ case KEYPRESS(0, 0, CHAR_LINEFEED):
+ case KEYPRESS(0, 0, CHAR_CARRIAGE_RETURN):
+ if (StrCmp(line, line_in) != 0) {
+ *line_out = line;
+ line = NULL;
+ }
+ enter = TRUE;
+ exit = TRUE;
+ break;
+
+ case KEYPRESS(0, 0, CHAR_BACKSPACE):
+ if (len == 0)
+ continue;
+ if (first == 0 && cursor == 0)
+ continue;
+ for (i = first + cursor-1; i < len; i++)
+ line[i] = line[i+1];
+ clear = 1;
+ len--;
+ if (cursor > 0)
+ cursor--;
+ if (cursor > 0 || first == 0)
+ continue;
+ /* show full line if it fits */
+ if (len < x_max) {
+ cursor = first;
+ first = 0;
+ continue;
+ }
+ /* jump left to see what we delete */
+ if (first > 10) {
+ first -= 10;
+ cursor = 10;
+ } else {
+ cursor = first;
+ first = 0;
+ }
+ continue;
+
+ case KEYPRESS(0, 0, ' ') ... KEYPRESS(0, 0, '~'):
+ case KEYPRESS(0, 0, 0x80) ... KEYPRESS(0, 0, 0xffff):
+ if (len+1 == size)
+ continue;
+ for (i = len; i > first + cursor; i--)
+ line[i] = line[i-1];
+ line[first + cursor] = KEYCHAR(key);
+ len++;
+ line[len] = '\0';
+ if (cursor+1 < x_max)
+ cursor++;
+ else if (first + cursor < len)
+ first++;
+ continue;
+ }
+ }
+
+ uefi_call_wrapper(ST->ConOut->EnableCursor, 2, ST->ConOut, FALSE);
+ FreePool(print);
+ FreePool(line);
+ return enter;
+}
+
+static UINTN entry_lookup_key(Config *config, UINTN start, CHAR16 key) {
+ UINTN i;
+
+ if (key == 0)
+ return -1;
+
+ /* select entry by number key */
+ if (key >= '1' && key <= '9') {
+ i = key - '0';
+ if (i > config->entry_count)
+ i = config->entry_count;
+ return i-1;
+ }
+
+ /* find matching key in config entries */
+ for (i = start; i < config->entry_count; i++)
+ if (config->entries[i]->key == key)
+ return i;
+
+ for (i = 0; i < start; i++)
+ if (config->entries[i]->key == key)
+ return i;
+
+ return -1;
+}
+
+static VOID print_status(Config *config, CHAR16 *loaded_image_path) {
+ UINT64 key;
+ UINTN i;
+ CHAR16 *s;
+ CHAR8 *b;
+ UINTN x;
+ UINTN y;
+ UINTN size;
+
+ uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK);
+ uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
+
+ Print(L"systemd-boot version: " VERSION "\n");
+ Print(L"architecture: " EFI_MACHINE_TYPE_NAME "\n");
+ Print(L"loaded image: %s\n", loaded_image_path);
+ Print(L"UEFI specification: %d.%02d\n", ST->Hdr.Revision >> 16, ST->Hdr.Revision & 0xffff);
+ Print(L"firmware vendor: %s\n", ST->FirmwareVendor);
+ Print(L"firmware version: %d.%02d\n", ST->FirmwareRevision >> 16, ST->FirmwareRevision & 0xffff);
+
+ if (uefi_call_wrapper(ST->ConOut->QueryMode, 4, ST->ConOut, ST->ConOut->Mode->Mode, &x, &y) == EFI_SUCCESS)
+ Print(L"console size: %d x %d\n", x, y);
+
+ if (efivar_get_raw(&global_guid, L"SecureBoot", &b, &size) == EFI_SUCCESS) {
+ Print(L"SecureBoot: %s\n", yes_no(*b > 0));
+ FreePool(b);
+ }
+
+ if (efivar_get_raw(&global_guid, L"SetupMode", &b, &size) == EFI_SUCCESS) {
+ Print(L"SetupMode: %s\n", *b > 0 ? L"setup" : L"user");
+ FreePool(b);
+ }
+
+ if (efivar_get_raw(&global_guid, L"OsIndicationsSupported", &b, &size) == EFI_SUCCESS) {
+ Print(L"OsIndicationsSupported: %d\n", (UINT64)*b);
+ FreePool(b);
+ }
+ Print(L"\n");
+
+ Print(L"timeout: %d\n", config->timeout_sec);
+ if (config->timeout_sec_efivar >= 0)
+ Print(L"timeout (EFI var): %d\n", config->timeout_sec_efivar);
+ Print(L"timeout (config): %d\n", config->timeout_sec_config);
+ if (config->entry_default_pattern)
+ Print(L"default pattern: '%s'\n", config->entry_default_pattern);
+ Print(L"editor: %s\n", yes_no(!config->no_editor));
+ Print(L"\n");
+
+ Print(L"config entry count: %d\n", config->entry_count);
+ Print(L"entry selected idx: %d\n", config->idx_default);
+ if (config->idx_default_efivar >= 0)
+ Print(L"entry EFI var idx: %d\n", config->idx_default_efivar);
+ Print(L"\n");
+
+ if (efivar_get_int(L"LoaderConfigTimeout", &i) == EFI_SUCCESS)
+ Print(L"LoaderConfigTimeout: %d\n", i);
+ if (config->entry_oneshot)
+ Print(L"LoaderEntryOneShot: %s\n", config->entry_oneshot);
+ if (efivar_get(L"LoaderDevicePartUUID", &s) == EFI_SUCCESS) {
+ Print(L"LoaderDevicePartUUID: %s\n", s);
+ FreePool(s);
+ }
+ if (efivar_get(L"LoaderEntryDefault", &s) == EFI_SUCCESS) {
+ Print(L"LoaderEntryDefault: %s\n", s);
+ FreePool(s);
+ }
+
+ Print(L"\n--- press key ---\n\n");
+ console_key_read(&key, TRUE);
+
+ for (i = 0; i < config->entry_count; i++) {
+ ConfigEntry *entry;
+
+ if (key == KEYPRESS(0, SCAN_ESC, 0) || key == KEYPRESS(0, 0, 'q'))
+ break;
+
+ entry = config->entries[i];
+ Print(L"config entry: %d/%d\n", i+1, config->entry_count);
+ if (entry->file)
+ Print(L"file '%s'\n", entry->file);
+ Print(L"title show '%s'\n", entry->title_show);
+ if (entry->title)
+ Print(L"title '%s'\n", entry->title);
+ if (entry->version)
+ Print(L"version '%s'\n", entry->version);
+ if (entry->machine_id)
+ Print(L"machine-id '%s'\n", entry->machine_id);
+ if (entry->device) {
+ EFI_DEVICE_PATH *device_path;
+ CHAR16 *str;
+
+ device_path = DevicePathFromHandle(entry->device);
+ if (device_path) {
+ str = DevicePathToStr(device_path);
+ Print(L"device handle '%s'\n", str);
+ FreePool(str);
+ }
+ }
+ if (entry->loader)
+ Print(L"loader '%s'\n", entry->loader);
+ if (entry->options)
+ Print(L"options '%s'\n", entry->options);
+ Print(L"auto-select %s\n", yes_no(!entry->no_autoselect));
+ if (entry->call)
+ Print(L"internal call yes\n");
+
+ Print(L"\n--- press key ---\n\n");
+ console_key_read(&key, TRUE);
+ }
+
+ uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
+}
+
+static BOOLEAN menu_run(Config *config, ConfigEntry **chosen_entry, CHAR16 *loaded_image_path) {
+ EFI_STATUS err;
+ UINTN visible_max;
+ UINTN idx_highlight;
+ UINTN idx_highlight_prev;
+ UINTN idx_first;
+ UINTN idx_last;
+ BOOLEAN refresh;
+ BOOLEAN highlight;
+ UINTN i;
+ UINTN line_width;
+ CHAR16 **lines;
+ UINTN x_start;
+ UINTN y_start;
+ UINTN x_max;
+ UINTN y_max;
+ CHAR16 *status;
+ CHAR16 *clearline;
+ INTN timeout_remain;
+ INT16 idx;
+ BOOLEAN exit = FALSE;
+ BOOLEAN run = TRUE;
+ BOOLEAN wait = FALSE;
+
+ graphics_mode(FALSE);
+ uefi_call_wrapper(ST->ConIn->Reset, 2, ST->ConIn, FALSE);
+ uefi_call_wrapper(ST->ConOut->EnableCursor, 2, ST->ConOut, FALSE);
+ uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK);
+
+ /* draw a single character to make ClearScreen work on some firmware */
+ uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, L" ");
+ uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
+
+ err = uefi_call_wrapper(ST->ConOut->QueryMode, 4, ST->ConOut, ST->ConOut->Mode->Mode, &x_max, &y_max);
+ if (EFI_ERROR(err)) {
+ x_max = 80;
+ y_max = 25;
+ }
+
+ /* we check 10 times per second for a keystroke */
+ if (config->timeout_sec > 0)
+ timeout_remain = config->timeout_sec * 10;
+ else
+ timeout_remain = -1;
+
+ idx_highlight = config->idx_default;
+ idx_highlight_prev = 0;
+
+ visible_max = y_max - 2;
+
+ if ((UINTN)config->idx_default >= visible_max)
+ idx_first = config->idx_default-1;
+ else
+ idx_first = 0;
+
+ idx_last = idx_first + visible_max-1;
+
+ refresh = TRUE;
+ highlight = FALSE;
+
+ /* length of the longest entry */
+ line_width = 5;
+ for (i = 0; i < config->entry_count; i++) {
+ UINTN entry_len;
+
+ entry_len = StrLen(config->entries[i]->title_show);
+ if (line_width < entry_len)
+ line_width = entry_len;
+ }
+ if (line_width > x_max-6)
+ line_width = x_max-6;
+
+ /* offsets to center the entries on the screen */
+ x_start = (x_max - (line_width)) / 2;
+ if (config->entry_count < visible_max)
+ y_start = ((visible_max - config->entry_count) / 2) + 1;
+ else
+ y_start = 0;
+
+ /* menu entries title lines */
+ lines = AllocatePool(sizeof(CHAR16 *) * config->entry_count);
+ for (i = 0; i < config->entry_count; i++) {
+ UINTN j, k;
+
+ lines[i] = AllocatePool(((x_max+1) * sizeof(CHAR16)));
+ for (j = 0; j < x_start; j++)
+ lines[i][j] = ' ';
+
+ for (k = 0; config->entries[i]->title_show[k] != '\0' && j < x_max; j++, k++)
+ lines[i][j] = config->entries[i]->title_show[k];
+
+ for (; j < x_max; j++)
+ lines[i][j] = ' ';
+ lines[i][x_max] = '\0';
+ }
+
+ status = NULL;
+ clearline = AllocatePool((x_max+1) * sizeof(CHAR16));
+ for (i = 0; i < x_max; i++)
+ clearline[i] = ' ';
+ clearline[i] = 0;
+
+ while (!exit) {
+ UINT64 key;
+
+ if (refresh) {
+ for (i = 0; i < config->entry_count; i++) {
+ if (i < idx_first || i > idx_last)
+ continue;
+ uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_start + i - idx_first);
+ if (i == idx_highlight)
+ uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut,
+ EFI_BLACK|EFI_BACKGROUND_LIGHTGRAY);
+ else
+ uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut,
+ EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK);
+ uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, lines[i]);
+ if ((INTN)i == config->idx_default_efivar) {
+ uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, x_start-3, y_start + i - idx_first);
+ uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, L"=>");
+ }
+ }
+ refresh = FALSE;
+ } else if (highlight) {
+ uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_start + idx_highlight_prev - idx_first);
+ uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK);
+ uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, lines[idx_highlight_prev]);
+ if ((INTN)idx_highlight_prev == config->idx_default_efivar) {
+ uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, x_start-3, y_start + idx_highlight_prev - idx_first);
+ uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, L"=>");
+ }
+
+ uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_start + idx_highlight - idx_first);
+ uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_BLACK|EFI_BACKGROUND_LIGHTGRAY);
+ uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, lines[idx_highlight]);
+ if ((INTN)idx_highlight == config->idx_default_efivar) {
+ uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, x_start-3, y_start + idx_highlight - idx_first);
+ uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, L"=>");
+ }
+ highlight = FALSE;
+ }
+
+ if (timeout_remain > 0) {
+ FreePool(status);
+ status = PoolPrint(L"Boot in %d sec.", (timeout_remain + 5) / 10);
+ }
+
+ /* print status at last line of screen */
+ if (status) {
+ UINTN len;
+ UINTN x;
+
+ /* center line */
+ len = StrLen(status);
+ if (len < x_max)
+ x = (x_max - len) / 2;
+ else
+ x = 0;
+ uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK);
+ uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_max-1);
+ uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, clearline + (x_max - x));
+ uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, status);
+ uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, clearline+1 + x + len);
+ }
+
+ err = console_key_read(&key, wait);
+ if (EFI_ERROR(err)) {
+ /* timeout reached */
+ if (timeout_remain == 0) {
+ exit = TRUE;
+ break;
+ }
+
+ /* sleep and update status */
+ if (timeout_remain > 0) {
+ uefi_call_wrapper(BS->Stall, 1, 100 * 1000);
+ timeout_remain--;
+ continue;
+ }
+
+ /* timeout disabled, wait for next key */
+ wait = TRUE;
+ continue;
+ }
+
+ timeout_remain = -1;
+
+ /* clear status after keystroke */
+ if (status) {
+ FreePool(status);
+ status = NULL;
+ uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK);
+ uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_max-1);
+ uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, clearline+1);
+ }
+
+ idx_highlight_prev = idx_highlight;
+
+ switch (key) {
+ case KEYPRESS(0, SCAN_UP, 0):
+ case KEYPRESS(0, 0, 'k'):
+ if (idx_highlight > 0)
+ idx_highlight--;
+ break;
+
+ case KEYPRESS(0, SCAN_DOWN, 0):
+ case KEYPRESS(0, 0, 'j'):
+ if (idx_highlight < config->entry_count-1)
+ idx_highlight++;
+ break;
+
+ case KEYPRESS(0, SCAN_HOME, 0):
+ case KEYPRESS(EFI_ALT_PRESSED, 0, '<'):
+ if (idx_highlight > 0) {
+ refresh = TRUE;
+ idx_highlight = 0;
+ }
+ break;
+
+ case KEYPRESS(0, SCAN_END, 0):
+ case KEYPRESS(EFI_ALT_PRESSED, 0, '>'):
+ if (idx_highlight < config->entry_count-1) {
+ refresh = TRUE;
+ idx_highlight = config->entry_count-1;
+ }
+ break;
+
+ case KEYPRESS(0, SCAN_PAGE_UP, 0):
+ if (idx_highlight > visible_max)
+ idx_highlight -= visible_max;
+ else
+ idx_highlight = 0;
+ break;
+
+ case KEYPRESS(0, SCAN_PAGE_DOWN, 0):
+ idx_highlight += visible_max;
+ if (idx_highlight > config->entry_count-1)
+ idx_highlight = config->entry_count-1;
+ break;
+
+ case KEYPRESS(0, 0, CHAR_LINEFEED):
+ case KEYPRESS(0, 0, CHAR_CARRIAGE_RETURN):
+ exit = TRUE;
+ break;
+
+ case KEYPRESS(0, SCAN_F1, 0):
+ case KEYPRESS(0, 0, 'h'):
+ case KEYPRESS(0, 0, '?'):
+ status = StrDuplicate(L"(d)efault, (t/T)timeout, (e)dit, (v)ersion (Q)uit (P)rint (h)elp");
+ break;
+
+ case KEYPRESS(0, 0, 'Q'):
+ exit = TRUE;
+ run = FALSE;
+ break;
+
+ case KEYPRESS(0, 0, 'd'):
+ if (config->idx_default_efivar != (INTN)idx_highlight) {
+ /* store the selected entry in a persistent EFI variable */
+ efivar_set(L"LoaderEntryDefault", config->entries[idx_highlight]->file, TRUE);
+ config->idx_default_efivar = idx_highlight;
+ status = StrDuplicate(L"Default boot entry selected.");
+ } else {
+ /* clear the default entry EFI variable */
+ efivar_set(L"LoaderEntryDefault", NULL, TRUE);
+ config->idx_default_efivar = -1;
+ status = StrDuplicate(L"Default boot entry cleared.");
+ }
+ refresh = TRUE;
+ break;
+
+ case KEYPRESS(0, 0, '-'):
+ case KEYPRESS(0, 0, 'T'):
+ if (config->timeout_sec_efivar > 0) {
+ config->timeout_sec_efivar--;
+ efivar_set_int(L"LoaderConfigTimeout", config->timeout_sec_efivar, TRUE);
+ if (config->timeout_sec_efivar > 0)
+ status = PoolPrint(L"Menu timeout set to %d sec.", config->timeout_sec_efivar);
+ else
+ status = StrDuplicate(L"Menu disabled. Hold down key at bootup to show menu.");
+ } else if (config->timeout_sec_efivar <= 0){
+ config->timeout_sec_efivar = -1;
+ efivar_set(L"LoaderConfigTimeout", NULL, TRUE);
+ if (config->timeout_sec_config > 0)
+ status = PoolPrint(L"Menu timeout of %d sec is defined by configuration file.",
+ config->timeout_sec_config);
+ else
+ status = StrDuplicate(L"Menu disabled. Hold down key at bootup to show menu.");
+ }
+ break;
+
+ case KEYPRESS(0, 0, '+'):
+ case KEYPRESS(0, 0, 't'):
+ if (config->timeout_sec_efivar == -1 && config->timeout_sec_config == 0)
+ config->timeout_sec_efivar++;
+ config->timeout_sec_efivar++;
+ efivar_set_int(L"LoaderConfigTimeout", config->timeout_sec_efivar, TRUE);
+ if (config->timeout_sec_efivar > 0)
+ status = PoolPrint(L"Menu timeout set to %d sec.",
+ config->timeout_sec_efivar);
+ else
+ status = StrDuplicate(L"Menu disabled. Hold down key at bootup to show menu.");
+ break;
+
+ case KEYPRESS(0, 0, 'e'):
+ /* only the options of configured entries can be edited */
+ if (config->no_editor || config->entries[idx_highlight]->type == LOADER_UNDEFINED)
+ break;
+ uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK);
+ uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_max-1);
+ uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, clearline+1);
+ if (line_edit(config->entries[idx_highlight]->options, &config->options_edit, x_max-1, y_max-1))
+ exit = TRUE;
+ uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_max-1);
+ uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, clearline+1);
+ break;
+
+ case KEYPRESS(0, 0, 'v'):
+ status = PoolPrint(L"systemd-boot " VERSION " (" EFI_MACHINE_TYPE_NAME "), UEFI Specification %d.%02d, Vendor %s %d.%02d",
+ ST->Hdr.Revision >> 16, ST->Hdr.Revision & 0xffff,
+ ST->FirmwareVendor, ST->FirmwareRevision >> 16, ST->FirmwareRevision & 0xffff);
+ break;
+
+ case KEYPRESS(0, 0, 'P'):
+ print_status(config, loaded_image_path);
+ refresh = TRUE;
+ break;
+
+ case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'l'):
+ case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('l')):
+ refresh = TRUE;
+ break;
+
+ default:
+ /* jump with a hotkey directly to a matching entry */
+ idx = entry_lookup_key(config, idx_highlight+1, KEYCHAR(key));
+ if (idx < 0)
+ break;
+ idx_highlight = idx;
+ refresh = TRUE;
+ }
+
+ if (idx_highlight > idx_last) {
+ idx_last = idx_highlight;
+ idx_first = 1 + idx_highlight - visible_max;
+ refresh = TRUE;
+ } else if (idx_highlight < idx_first) {
+ idx_first = idx_highlight;
+ idx_last = idx_highlight + visible_max-1;
+ refresh = TRUE;
+ }
+
+ if (!refresh && idx_highlight != idx_highlight_prev)
+ highlight = TRUE;
+ }
+
+ *chosen_entry = config->entries[idx_highlight];
+
+ for (i = 0; i < config->entry_count; i++)
+ FreePool(lines[i]);
+ FreePool(lines);
+ FreePool(clearline);
+
+ uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_WHITE|EFI_BACKGROUND_BLACK);
+ uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
+ return run;
+}
+
+static VOID config_add_entry(Config *config, ConfigEntry *entry) {
+ if ((config->entry_count & 15) == 0) {
+ UINTN i;
+
+ i = config->entry_count + 16;
+ if (config->entry_count == 0)
+ config->entries = AllocatePool(sizeof(VOID *) * i);
+ else
+ config->entries = ReallocatePool(config->entries,
+ sizeof(VOID *) * config->entry_count, sizeof(VOID *) * i);
+ }
+ config->entries[config->entry_count++] = entry;
+}
+
+static VOID config_entry_free(ConfigEntry *entry) {
+ FreePool(entry->title_show);
+ FreePool(entry->title);
+ FreePool(entry->machine_id);
+ FreePool(entry->loader);
+ FreePool(entry->options);
+}
+
+static BOOLEAN is_digit(CHAR16 c)
+{
+ return (c >= '0') && (c <= '9');
+}
+
+static UINTN c_order(CHAR16 c)
+{
+ if (c == '\0')
+ return 0;
+ if (is_digit(c))
+ return 0;
+ else if ((c >= 'a') && (c <= 'z'))
+ return c;
+ else
+ return c + 0x10000;
+}
+
+static INTN str_verscmp(CHAR16 *s1, CHAR16 *s2)
+{
+ CHAR16 *os1 = s1;
+ CHAR16 *os2 = s2;
+
+ while (*s1 || *s2) {
+ INTN first;
+
+ while ((*s1 && !is_digit(*s1)) || (*s2 && !is_digit(*s2))) {
+ INTN order;
+
+ order = c_order(*s1) - c_order(*s2);
+ if (order)
+ return order;
+ s1++;
+ s2++;
+ }
+
+ while (*s1 == '0')
+ s1++;
+ while (*s2 == '0')
+ s2++;
+
+ first = 0;
+ while (is_digit(*s1) && is_digit(*s2)) {
+ if (first == 0)
+ first = *s1 - *s2;
+ s1++;
+ s2++;
+ }
+
+ if (is_digit(*s1))
+ return 1;
+ if (is_digit(*s2))
+ return -1;
+
+ if (first)
+ return first;
+ }
+
+ return StrCmp(os1, os2);
+}
+
+static CHAR8 *line_get_key_value(CHAR8 *content, CHAR8 *sep, UINTN *pos, CHAR8 **key_ret, CHAR8 **value_ret) {
+ CHAR8 *line;
+ UINTN linelen;
+ CHAR8 *value;
+
+skip:
+ line = content + *pos;
+ if (*line == '\0')
+ return NULL;
+
+ linelen = 0;
+ while (line[linelen] && !strchra((CHAR8 *)"\n\r", line[linelen]))
+ linelen++;
+
+ /* move pos to next line */
+ *pos += linelen;
+ if (content[*pos])
+ (*pos)++;
+
+ /* empty line */
+ if (linelen == 0)
+ goto skip;
+
+ /* terminate line */
+ line[linelen] = '\0';
+
+ /* remove leading whitespace */
+ while (strchra((CHAR8 *)" \t", *line)) {
+ line++;
+ linelen--;
+ }
+
+ /* remove trailing whitespace */
+ while (linelen > 0 && strchra(sep, line[linelen-1]))
+ linelen--;
+ line[linelen] = '\0';
+
+ if (*line == '#')
+ goto skip;
+
+ /* split key/value */
+ value = line;
+ while (*value && !strchra(sep, *value))
+ value++;
+ if (*value == '\0')
+ goto skip;
+ *value = '\0';
+ value++;
+ while (*value && strchra(sep, *value))
+ value++;
+
+ /* unquote */
+ if (value[0] == '\"' && line[linelen-1] == '\"') {
+ value++;
+ line[linelen-1] = '\0';
+ }
+
+ *key_ret = line;
+ *value_ret = value;
+ return line;
+}
+
+static VOID config_defaults_load_from_file(Config *config, CHAR8 *content) {
+ CHAR8 *line;
+ UINTN pos = 0;
+ CHAR8 *key, *value;
+
+ line = content;
+ while ((line = line_get_key_value(content, (CHAR8 *)" \t", &pos, &key, &value))) {
+ if (strcmpa((CHAR8 *)"timeout", key) == 0) {
+ CHAR16 *s;
+
+ s = stra_to_str(value);
+ config->timeout_sec_config = Atoi(s);
+ config->timeout_sec = config->timeout_sec_config;
+ FreePool(s);
+ continue;
+ }
+
+ if (strcmpa((CHAR8 *)"default", key) == 0) {
+ FreePool(config->entry_default_pattern);
+ config->entry_default_pattern = stra_to_str(value);
+ StrLwr(config->entry_default_pattern);
+ continue;
+ }
+
+ if (strcmpa((CHAR8 *)"editor", key) == 0) {
+ BOOLEAN on;
+
+ if (EFI_ERROR(parse_boolean(value, &on)))
+ continue;
+ config->no_editor = !on;
+ }
+ }
+}
+
+static VOID config_entry_add_from_file(Config *config, EFI_HANDLE *device, CHAR16 *file, CHAR8 *content, CHAR16 *loaded_image_path) {
+ ConfigEntry *entry;
+ CHAR8 *line;
+ UINTN pos = 0;
+ CHAR8 *key, *value;
+ UINTN len;
+ CHAR16 *initrd = NULL;
+
+ entry = AllocateZeroPool(sizeof(ConfigEntry));
+
+ line = content;
+ while ((line = line_get_key_value(content, (CHAR8 *)" \t", &pos, &key, &value))) {
+ if (strcmpa((CHAR8 *)"title", key) == 0) {
+ FreePool(entry->title);
+ entry->title = stra_to_str(value);
+ continue;
+ }
+
+ if (strcmpa((CHAR8 *)"version", key) == 0) {
+ FreePool(entry->version);
+ entry->version = stra_to_str(value);
+ continue;
+ }
+
+ if (strcmpa((CHAR8 *)"machine-id", key) == 0) {
+ FreePool(entry->machine_id);
+ entry->machine_id = stra_to_str(value);
+ continue;
+ }
+
+ if (strcmpa((CHAR8 *)"linux", key) == 0) {
+ FreePool(entry->loader);
+ entry->type = LOADER_LINUX;
+ entry->loader = stra_to_path(value);
+ entry->key = 'l';
+ continue;
+ }
+
+ if (strcmpa((CHAR8 *)"efi", key) == 0) {
+ entry->type = LOADER_EFI;
+ FreePool(entry->loader);
+ entry->loader = stra_to_path(value);
+
+ /* do not add an entry for ourselves */
+ if (StriCmp(entry->loader, loaded_image_path) == 0) {
+ entry->type = LOADER_UNDEFINED;
+ break;
+ }
+ continue;
+ }
+
+ if (strcmpa((CHAR8 *)"architecture", key) == 0) {
+ /* do not add an entry for an EFI image of architecture not matching with that of the image */
+ if (strcmpa((CHAR8 *)EFI_MACHINE_TYPE_NAME, value) != 0) {
+ entry->type = LOADER_UNDEFINED;
+ break;
+ }
+ continue;
+ }
+
+ if (strcmpa((CHAR8 *)"initrd", key) == 0) {
+ CHAR16 *new;
+
+ new = stra_to_path(value);
+ if (initrd) {
+ CHAR16 *s;
+
+ s = PoolPrint(L"%s initrd=%s", initrd, new);
+ FreePool(initrd);
+ initrd = s;
+ } else
+ initrd = PoolPrint(L"initrd=%s", new);
+ FreePool(new);
+ continue;
+ }
+
+ if (strcmpa((CHAR8 *)"options", key) == 0) {
+ CHAR16 *new;
+
+ new = stra_to_str(value);
+ if (entry->options) {
+ CHAR16 *s;
+
+ s = PoolPrint(L"%s %s", entry->options, new);
+ FreePool(entry->options);
+ entry->options = s;
+ } else {
+ entry->options = new;
+ new = NULL;
+ }
+ FreePool(new);
+ continue;
+ }
+ }
+
+ if (entry->type == LOADER_UNDEFINED) {
+ config_entry_free(entry);
+ FreePool(initrd);
+ FreePool(entry);
+ return;
+ }
+
+ /* add initrd= to options */
+ if (entry->type == LOADER_LINUX && initrd) {
+ if (entry->options) {
+ CHAR16 *s;
+
+ s = PoolPrint(L"%s %s", initrd, entry->options);
+ FreePool(entry->options);
+ entry->options = s;
+ } else {
+ entry->options = initrd;
+ initrd = NULL;
+ }
+ }
+ FreePool(initrd);
+
+ entry->device = device;
+ entry->file = StrDuplicate(file);
+ len = StrLen(entry->file);
+ /* remove ".conf" */
+ if (len > 5)
+ entry->file[len - 5] = '\0';
+ StrLwr(entry->file);
+
+ config_add_entry(config, entry);
+}
+
+static VOID config_load(Config *config, EFI_HANDLE *device, EFI_FILE *root_dir, CHAR16 *loaded_image_path) {
+ EFI_FILE_HANDLE entries_dir;
+ EFI_STATUS err;
+ CHAR8 *content = NULL;
+ UINTN sec;
+ UINTN len;
+ UINTN i;
+
+ len = file_read(root_dir, L"\\loader\\loader.conf", 0, 0, &content);
+ if (len > 0)
+ config_defaults_load_from_file(config, content);
+ FreePool(content);
+
+ err = efivar_get_int(L"LoaderConfigTimeout", &sec);
+ if (!EFI_ERROR(err)) {
+ config->timeout_sec_efivar = sec;
+ config->timeout_sec = sec;
+ } else
+ config->timeout_sec_efivar = -1;
+
+ err = uefi_call_wrapper(root_dir->Open, 5, root_dir, &entries_dir, L"\\loader\\entries", EFI_FILE_MODE_READ, 0ULL);
+ if (!EFI_ERROR(err)) {
+ for (;;) {
+ CHAR16 buf[256];
+ UINTN bufsize;
+ EFI_FILE_INFO *f;
+ CHAR8 *content = NULL;
+ UINTN len;
+
+ bufsize = sizeof(buf);
+ err = uefi_call_wrapper(entries_dir->Read, 3, entries_dir, &bufsize, buf);
+ if (bufsize == 0 || EFI_ERROR(err))
+ break;
+
+ f = (EFI_FILE_INFO *) buf;
+ if (f->FileName[0] == '.')
+ continue;
+ if (f->Attribute & EFI_FILE_DIRECTORY)
+ continue;
+
+ len = StrLen(f->FileName);
+ if (len < 6)
+ continue;
+ if (StriCmp(f->FileName + len - 5, L".conf") != 0)
+ continue;
+ if (StrnCmp(f->FileName, L"auto-", 5) == 0)
+ continue;
+
+ len = file_read(entries_dir, f->FileName, 0, 0, &content);
+ if (len > 0)
+ config_entry_add_from_file(config, device, f->FileName, content, loaded_image_path);
+ FreePool(content);
+ }
+ uefi_call_wrapper(entries_dir->Close, 1, entries_dir);
+ }
+
+ /* sort entries after version number */
+ for (i = 1; i < config->entry_count; i++) {
+ BOOLEAN more;
+ UINTN k;
+
+ more = FALSE;
+ for (k = 0; k < config->entry_count - i; k++) {
+ ConfigEntry *entry;
+
+ if (str_verscmp(config->entries[k]->file, config->entries[k+1]->file) <= 0)
+ continue;
+ entry = config->entries[k];
+ config->entries[k] = config->entries[k+1];
+ config->entries[k+1] = entry;
+ more = TRUE;
+ }
+ if (!more)
+ break;
+ }
+}
+
+static VOID config_default_entry_select(Config *config) {
+ CHAR16 *var;
+ EFI_STATUS err;
+ UINTN i;
+
+ /*
+ * The EFI variable to specify a boot entry for the next, and only the
+ * next reboot. The variable is always cleared directly after it is read.
+ */
+ err = efivar_get(L"LoaderEntryOneShot", &var);
+ if (!EFI_ERROR(err)) {
+ BOOLEAN found = FALSE;
+
+ for (i = 0; i < config->entry_count; i++) {
+ if (StrCmp(config->entries[i]->file, var) == 0) {
+ config->idx_default = i;
+ found = TRUE;
+ break;
+ }
+ }
+
+ config->entry_oneshot = StrDuplicate(var);
+ efivar_set(L"LoaderEntryOneShot", NULL, TRUE);
+ FreePool(var);
+ if (found)
+ return;
+ }
+
+ /*
+ * The EFI variable to select the default boot entry overrides the
+ * configured pattern. The variable can be set and cleared by pressing
+ * the 'd' key in the loader selection menu, the entry is marked with
+ * an '*'.
+ */
+ err = efivar_get(L"LoaderEntryDefault", &var);
+ if (!EFI_ERROR(err)) {
+ BOOLEAN found = FALSE;
+
+ for (i = 0; i < config->entry_count; i++) {
+ if (StrCmp(config->entries[i]->file, var) == 0) {
+ config->idx_default = i;
+ config->idx_default_efivar = i;
+ found = TRUE;
+ break;
+ }
+ }
+ FreePool(var);
+ if (found)
+ return;
+ }
+ config->idx_default_efivar = -1;
+
+ if (config->entry_count == 0)
+ return;
+
+ /*
+ * Match the pattern from the end of the list to the start, find last
+ * entry (largest number) matching the given pattern.
+ */
+ if (config->entry_default_pattern) {
+ i = config->entry_count;
+ while (i--) {
+ if (config->entries[i]->no_autoselect)
+ continue;
+ if (MetaiMatch(config->entries[i]->file, config->entry_default_pattern)) {
+ config->idx_default = i;
+ return;
+ }
+ }
+ }
+
+ /* select the last suitable entry */
+ i = config->entry_count;
+ while (i--) {
+ if (config->entries[i]->no_autoselect)
+ continue;
+ config->idx_default = i;
+ return;
+ }
+
+ /* no entry found */
+ config->idx_default = -1;
+}
+
+/* generate a unique title, avoiding non-distinguishable menu entries */
+static VOID config_title_generate(Config *config) {
+ UINTN i, k;
+ BOOLEAN unique;
+
+ /* set title */
+ for (i = 0; i < config->entry_count; i++) {
+ CHAR16 *title;
+
+ FreePool(config->entries[i]->title_show);
+ title = config->entries[i]->title;
+ if (!title)
+ title = config->entries[i]->file;
+ config->entries[i]->title_show = StrDuplicate(title);
+ }
+
+ unique = TRUE;
+ for (i = 0; i < config->entry_count; i++) {
+ for (k = 0; k < config->entry_count; k++) {
+ if (i == k)
+ continue;
+ if (StrCmp(config->entries[i]->title_show, config->entries[k]->title_show) != 0)
+ continue;
+
+ unique = FALSE;
+ config->entries[i]->non_unique = TRUE;
+ config->entries[k]->non_unique = TRUE;
+ }
+ }
+ if (unique)
+ return;
+
+ /* add version to non-unique titles */
+ for (i = 0; i < config->entry_count; i++) {
+ CHAR16 *s;
+
+ if (!config->entries[i]->non_unique)
+ continue;
+ if (!config->entries[i]->version)
+ continue;
+
+ s = PoolPrint(L"%s (%s)", config->entries[i]->title_show, config->entries[i]->version);
+ FreePool(config->entries[i]->title_show);
+ config->entries[i]->title_show = s;
+ config->entries[i]->non_unique = FALSE;
+ }
+
+ unique = TRUE;
+ for (i = 0; i < config->entry_count; i++) {
+ for (k = 0; k < config->entry_count; k++) {
+ if (i == k)
+ continue;
+ if (StrCmp(config->entries[i]->title_show, config->entries[k]->title_show) != 0)
+ continue;
+
+ unique = FALSE;
+ config->entries[i]->non_unique = TRUE;
+ config->entries[k]->non_unique = TRUE;
+ }
+ }
+ if (unique)
+ return;
+
+ /* add machine-id to non-unique titles */
+ for (i = 0; i < config->entry_count; i++) {
+ CHAR16 *s;
+ CHAR16 *m;
+
+ if (!config->entries[i]->non_unique)
+ continue;
+ if (!config->entries[i]->machine_id)
+ continue;
+
+ m = StrDuplicate(config->entries[i]->machine_id);
+ m[8] = '\0';
+ s = PoolPrint(L"%s (%s)", config->entries[i]->title_show, m);
+ FreePool(config->entries[i]->title_show);
+ config->entries[i]->title_show = s;
+ config->entries[i]->non_unique = FALSE;
+ FreePool(m);
+ }
+
+ unique = TRUE;
+ for (i = 0; i < config->entry_count; i++) {
+ for (k = 0; k < config->entry_count; k++) {
+ if (i == k)
+ continue;
+ if (StrCmp(config->entries[i]->title_show, config->entries[k]->title_show) != 0)
+ continue;
+
+ unique = FALSE;
+ config->entries[i]->non_unique = TRUE;
+ config->entries[k]->non_unique = TRUE;
+ }
+ }
+ if (unique)
+ return;
+
+ /* add file name to non-unique titles */
+ for (i = 0; i < config->entry_count; i++) {
+ CHAR16 *s;
+
+ if (!config->entries[i]->non_unique)
+ continue;
+ s = PoolPrint(L"%s (%s)", config->entries[i]->title_show, config->entries[i]->file);
+ FreePool(config->entries[i]->title_show);
+ config->entries[i]->title_show = s;
+ config->entries[i]->non_unique = FALSE;
+ }
+}
+
+static BOOLEAN config_entry_add_call(Config *config, CHAR16 *title, EFI_STATUS (*call)(VOID)) {
+ ConfigEntry *entry;
+
+ entry = AllocateZeroPool(sizeof(ConfigEntry));
+ entry->title = StrDuplicate(title);
+ entry->call = call;
+ entry->no_autoselect = TRUE;
+ config_add_entry(config, entry);
+ return TRUE;
+}
+
+static ConfigEntry *config_entry_add_loader(Config *config, EFI_HANDLE *device,
+ enum loader_type type,CHAR16 *file, CHAR16 key, CHAR16 *title, CHAR16 *loader) {
+ ConfigEntry *entry;
+
+ entry = AllocateZeroPool(sizeof(ConfigEntry));
+ entry->type = type;
+ entry->title = StrDuplicate(title);
+ entry->device = device;
+ entry->loader = StrDuplicate(loader);
+ entry->file = StrDuplicate(file);
+ StrLwr(entry->file);
+ entry->key = key;
+ config_add_entry(config, entry);
+
+ return entry;
+}
+
+static BOOLEAN config_entry_add_loader_auto(Config *config, EFI_HANDLE *device, EFI_FILE *root_dir, CHAR16 *loaded_image_path,
+ CHAR16 *file, CHAR16 key, CHAR16 *title, CHAR16 *loader) {
+ EFI_FILE_HANDLE handle;
+ ConfigEntry *entry;
+ EFI_STATUS err;
+
+ /* do not add an entry for ourselves */
+ if (loaded_image_path && StriCmp(loader, loaded_image_path) == 0)
+ return FALSE;
+
+ /* check existence */
+ err = uefi_call_wrapper(root_dir->Open, 5, root_dir, &handle, loader, EFI_FILE_MODE_READ, 0ULL);
+ if (EFI_ERROR(err))
+ return FALSE;
+ uefi_call_wrapper(handle->Close, 1, handle);
+
+ entry = config_entry_add_loader(config, device, LOADER_UNDEFINED, file, key, title, loader);
+ if (!entry)
+ return FALSE;
+
+ /* do not boot right away into auto-detected entries */
+ entry->no_autoselect = TRUE;
+
+ return TRUE;
+}
+
+static VOID config_entry_add_osx(Config *config) {
+ EFI_STATUS err;
+ UINTN handle_count = 0;
+ EFI_HANDLE *handles = NULL;
+
+ err = LibLocateHandle(ByProtocol, &FileSystemProtocol, NULL, &handle_count, &handles);
+ if (!EFI_ERROR(err)) {
+ UINTN i;
+
+ for (i = 0; i < handle_count; i++) {
+ EFI_FILE *root;
+ BOOLEAN found;
+
+ root = LibOpenRoot(handles[i]);
+ if (!root)
+ continue;
+ found = config_entry_add_loader_auto(config, handles[i], root, NULL, L"auto-osx", 'a', L"OS X",
+ L"\\System\\Library\\CoreServices\\boot.efi");
+ uefi_call_wrapper(root->Close, 1, root);
+ if (found)
+ break;
+ }
+
+ FreePool(handles);
+ }
+}
+
+static VOID config_entry_add_linux( Config *config, EFI_LOADED_IMAGE *loaded_image, EFI_FILE *root_dir) {
+ EFI_FILE_HANDLE linux_dir;
+ EFI_STATUS err;
+
+ err = uefi_call_wrapper(root_dir->Open, 5, root_dir, &linux_dir, L"\\EFI\\Linux", EFI_FILE_MODE_READ, 0ULL);
+ if (!EFI_ERROR(err)) {
+ for (;;) {
+ CHAR16 buf[256];
+ UINTN bufsize;
+ EFI_FILE_INFO *f;
+ CHAR8 *sections[] = {
+ (UINT8 *)".osrel",
+ NULL
+ };
+ UINTN offs[ELEMENTSOF(sections)-1] = {};
+ UINTN szs[ELEMENTSOF(sections)-1] = {};
+ UINTN addrs[ELEMENTSOF(sections)-1] = {};
+ CHAR8 *content = NULL;
+ UINTN len;
+ CHAR8 *line;
+ UINTN pos = 0;
+ CHAR8 *key, *value;
+ CHAR16 *os_name = NULL;
+ CHAR16 *os_id = NULL;
+ CHAR16 *os_version = NULL;
+
+ bufsize = sizeof(buf);
+ err = uefi_call_wrapper(linux_dir->Read, 3, linux_dir, &bufsize, buf);
+ if (bufsize == 0 || EFI_ERROR(err))
+ break;
+
+ f = (EFI_FILE_INFO *) buf;
+ if (f->FileName[0] == '.')
+ continue;
+ if (f->Attribute & EFI_FILE_DIRECTORY)
+ continue;
+ len = StrLen(f->FileName);
+ if (len < 5)
+ continue;
+ if (StriCmp(f->FileName + len - 4, L".efi") != 0)
+ continue;
+
+ /* look for an .osrel section in the .efi binary */
+ err = pefile_locate_sections(linux_dir, f->FileName, sections, addrs, offs, szs);
+ if (EFI_ERROR(err))
+ continue;
+
+ len = file_read(linux_dir, f->FileName, offs[0], szs[0], &content);
+ if (len <= 0)
+ continue;
+
+ /* read properties from the embedded os-release file */
+ line = content;
+ while ((line = line_get_key_value(content, (CHAR8 *)"=", &pos, &key, &value))) {
+ if (strcmpa((CHAR8 *)"PRETTY_NAME", key) == 0) {
+ os_name = stra_to_str(value);
+ continue;
+ }
+
+ if (strcmpa((CHAR8 *)"ID", key) == 0) {
+ os_id = stra_to_str(value);
+ continue;
+ }
+
+ if (strcmpa((CHAR8 *)"VERSION_ID", key) == 0) {
+ os_version = stra_to_str(value);
+ continue;
+ }
+ }
+
+ if (os_name && os_id && os_version) {
+ CHAR16 *conf;
+ CHAR16 *path;
+
+ conf = PoolPrint(L"%s-%s", os_id, os_version);
+ path = PoolPrint(L"\\EFI\\Linux\\%s", f->FileName);
+ config_entry_add_loader(config, loaded_image->DeviceHandle, LOADER_LINUX, conf, 'l', os_name, path);
+ FreePool(conf);
+ FreePool(path);
+ FreePool(os_name);
+ FreePool(os_id);
+ FreePool(os_version);
+ }
+
+ FreePool(content);
+ }
+ uefi_call_wrapper(linux_dir->Close, 1, linux_dir);
+ }
+}
+
+static EFI_STATUS image_start(EFI_HANDLE parent_image, const Config *config, const ConfigEntry *entry) {
+ EFI_HANDLE image;
+ EFI_DEVICE_PATH *path;
+ CHAR16 *options;
+ EFI_STATUS err;
+
+ path = FileDevicePath(entry->device, entry->loader);
+ if (!path) {
+ Print(L"Error getting device path.");
+ uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ err = uefi_call_wrapper(BS->LoadImage, 6, FALSE, parent_image, path, NULL, 0, &image);
+ if (EFI_ERROR(err)) {
+ Print(L"Error loading %s: %r", entry->loader, err);
+ uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
+ goto out;
+ }
+
+ if (config->options_edit)
+ options = config->options_edit;
+ else if (entry->options)
+ options = entry->options;
+ else
+ options = NULL;
+ if (options) {
+ EFI_LOADED_IMAGE *loaded_image;
+
+ err = uefi_call_wrapper(BS->OpenProtocol, 6, image, &LoadedImageProtocol, (VOID **)&loaded_image,
+ parent_image, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+ if (EFI_ERROR(err)) {
+ Print(L"Error getting LoadedImageProtocol handle: %r", err);
+ uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
+ goto out_unload;
+ }
+ loaded_image->LoadOptions = options;
+ loaded_image->LoadOptionsSize = (StrLen(loaded_image->LoadOptions)+1) * sizeof(CHAR16);
+ }
+
+ efivar_set_time_usec(L"LoaderTimeExecUSec", 0);
+ err = uefi_call_wrapper(BS->StartImage, 3, image, NULL, NULL);
+out_unload:
+ uefi_call_wrapper(BS->UnloadImage, 1, image);
+out:
+ FreePool(path);
+ return err;
+}
+
+static EFI_STATUS reboot_into_firmware(VOID) {
+ CHAR8 *b;
+ UINTN size;
+ UINT64 osind;
+ EFI_STATUS err;
+
+ osind = EFI_OS_INDICATIONS_BOOT_TO_FW_UI;
+
+ err = efivar_get_raw(&global_guid, L"OsIndications", &b, &size);
+ if (!EFI_ERROR(err))
+ osind |= (UINT64)*b;
+ FreePool(b);
+
+ err = efivar_set_raw(&global_guid, L"OsIndications", (CHAR8 *)&osind, sizeof(UINT64), TRUE);
+ if (EFI_ERROR(err))
+ return err;
+
+ err = uefi_call_wrapper(RT->ResetSystem, 4, EfiResetCold, EFI_SUCCESS, 0, NULL);
+ Print(L"Error calling ResetSystem: %r", err);
+ uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
+ return err;
+}
+
+static VOID config_free(Config *config) {
+ UINTN i;
+
+ for (i = 0; i < config->entry_count; i++)
+ config_entry_free(config->entries[i]);
+ FreePool(config->entries);
+ FreePool(config->entry_default_pattern);
+ FreePool(config->options_edit);
+ FreePool(config->entry_oneshot);
+}
+
+EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
+ CHAR16 *s;
+ CHAR8 *b;
+ UINTN size;
+ EFI_LOADED_IMAGE *loaded_image;
+ EFI_FILE *root_dir;
+ CHAR16 *loaded_image_path;
+ EFI_DEVICE_PATH *device_path;
+ EFI_STATUS err;
+ Config config;
+ UINT64 init_usec;
+ BOOLEAN menu = FALSE;
+
+ InitializeLib(image, sys_table);
+ init_usec = time_usec();
+ efivar_set_time_usec(L"LoaderTimeInitUSec", init_usec);
+ efivar_set(L"LoaderInfo", L"systemd-boot " VERSION, FALSE);
+ s = PoolPrint(L"%s %d.%02d", ST->FirmwareVendor, ST->FirmwareRevision >> 16, ST->FirmwareRevision & 0xffff);
+ efivar_set(L"LoaderFirmwareInfo", s, FALSE);
+ FreePool(s);
+ s = PoolPrint(L"UEFI %d.%02d", ST->Hdr.Revision >> 16, ST->Hdr.Revision & 0xffff);
+ efivar_set(L"LoaderFirmwareType", s, FALSE);
+ FreePool(s);
+
+ err = uefi_call_wrapper(BS->OpenProtocol, 6, image, &LoadedImageProtocol, (VOID **)&loaded_image,
+ image, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+ if (EFI_ERROR(err)) {
+ Print(L"Error getting a LoadedImageProtocol handle: %r ", err);
+ uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
+ return err;
+ }
+
+ /* export the device path this image is started from */
+ device_path = DevicePathFromHandle(loaded_image->DeviceHandle);
+ if (device_path) {
+ EFI_DEVICE_PATH *path, *paths;
+
+ paths = UnpackDevicePath(device_path);
+ for (path = paths; !IsDevicePathEnd(path); path = NextDevicePathNode(path)) {
+ HARDDRIVE_DEVICE_PATH *drive;
+ CHAR16 uuid[37];
+
+ if (DevicePathType(path) != MEDIA_DEVICE_PATH)
+ continue;
+ if (DevicePathSubType(path) != MEDIA_HARDDRIVE_DP)
+ continue;
+ drive = (HARDDRIVE_DEVICE_PATH *)path;
+ if (drive->SignatureType != SIGNATURE_TYPE_GUID)
+ continue;
+
+ GuidToString(uuid, (EFI_GUID *)&drive->Signature);
+ efivar_set(L"LoaderDevicePartUUID", uuid, FALSE);
+ break;
+ }
+ FreePool(paths);
+ }
+
+ root_dir = LibOpenRoot(loaded_image->DeviceHandle);
+ if (!root_dir) {
+ Print(L"Unable to open root directory: %r ", err);
+ uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
+ return EFI_LOAD_ERROR;
+ }
+
+
+ /* the filesystem path to this image, to prevent adding ourselves to the menu */
+ loaded_image_path = DevicePathToStr(loaded_image->FilePath);
+ efivar_set(L"LoaderImageIdentifier", loaded_image_path, FALSE);
+
+ /* scan "\loader\entries\*.conf" files */
+ ZeroMem(&config, sizeof(Config));
+ config_load(&config, loaded_image->DeviceHandle, root_dir, loaded_image_path);
+
+ /* if we find some well-known loaders, add them to the end of the list */
+ config_entry_add_linux(&config, loaded_image, root_dir);
+ config_entry_add_loader_auto(&config, loaded_image->DeviceHandle, root_dir, loaded_image_path,
+ L"auto-windows", 'w', L"Windows Boot Manager", L"\\EFI\\Microsoft\\Boot\\bootmgfw.efi");
+ config_entry_add_loader_auto(&config, loaded_image->DeviceHandle, root_dir, loaded_image_path,
+ L"auto-efi-shell", 's', L"EFI Shell", L"\\shell" EFI_MACHINE_TYPE_NAME ".efi");
+ config_entry_add_loader_auto(&config, loaded_image->DeviceHandle, root_dir, loaded_image_path,
+ L"auto-efi-default", '\0', L"EFI Default Loader", L"\\EFI\\Boot\\boot" EFI_MACHINE_TYPE_NAME ".efi");
+ config_entry_add_osx(&config);
+
+ if (efivar_get_raw(&global_guid, L"OsIndicationsSupported", &b, &size) == EFI_SUCCESS) {
+ UINT64 osind = (UINT64)*b;
+
+ if (osind & EFI_OS_INDICATIONS_BOOT_TO_FW_UI)
+ config_entry_add_call(&config, L"Reboot Into Firmware Interface", reboot_into_firmware);
+ FreePool(b);
+ }
+
+ if (config.entry_count == 0) {
+ Print(L"No loader found. Configuration files in \\loader\\entries\\*.conf are needed.");
+ uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
+ goto out;
+ }
+
+ config_title_generate(&config);
+
+ /* select entry by configured pattern or EFI LoaderDefaultEntry= variable*/
+ config_default_entry_select(&config);
+
+ /* if no configured entry to select from was found, enable the menu */
+ if (config.idx_default == -1) {
+ config.idx_default = 0;
+ if (config.timeout_sec == 0)
+ config.timeout_sec = 10;
+ }
+
+ /* select entry or show menu when key is pressed or timeout is set */
+ if (config.timeout_sec == 0) {
+ UINT64 key;
+
+ err = console_key_read(&key, FALSE);
+ if (!EFI_ERROR(err)) {
+ INT16 idx;
+
+ /* find matching key in config entries */
+ idx = entry_lookup_key(&config, config.idx_default, KEYCHAR(key));
+ if (idx >= 0)
+ config.idx_default = idx;
+ else
+ menu = TRUE;
+ }
+ } else
+ menu = TRUE;
+
+ for (;;) {
+ ConfigEntry *entry;
+
+ entry = config.entries[config.idx_default];
+ if (menu) {
+ efivar_set_time_usec(L"LoaderTimeMenuUSec", 0);
+ uefi_call_wrapper(BS->SetWatchdogTimer, 4, 0, 0x10000, 0, NULL);
+ if (!menu_run(&config, &entry, loaded_image_path))
+ break;
+
+ /* run special entry like "reboot" */
+ if (entry->call) {
+ entry->call();
+ continue;
+ }
+ }
+
+ /* export the selected boot entry to the system */
+ efivar_set(L"LoaderEntrySelected", entry->file, FALSE);
+
+ uefi_call_wrapper(BS->SetWatchdogTimer, 4, 5 * 60, 0x10000, 0, NULL);
+ err = image_start(image, &config, entry);
+ if (EFI_ERROR(err)) {
+ graphics_mode(FALSE);
+ Print(L"\nFailed to execute %s (%s): %r\n", entry->title, entry->loader, err);
+ uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
+ goto out;
+ }
+
+ menu = TRUE;
+ config.timeout_sec = 0;
+ }
+ err = EFI_SUCCESS;
+out:
+ FreePool(loaded_image_path);
+ config_free(&config);
+ uefi_call_wrapper(root_dir->Close, 1, root_dir);
+ uefi_call_wrapper(BS->CloseProtocol, 4, image, &LoadedImageProtocol, image, NULL);
+ return err;
+}
diff --git a/src/boot/efi/console.c b/src/boot/efi/console.c
new file mode 100644
index 0000000000..66aa88f32e
--- /dev/null
+++ b/src/boot/efi/console.c
@@ -0,0 +1,141 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that 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.
+ *
+ * Copyright (C) 2012-2013 Kay Sievers <kay@vrfy.org>
+ * Copyright (C) 2012 Harald Hoyer <harald@redhat.com>
+ */
+
+#include <efi.h>
+#include <efilib.h>
+
+#include "util.h"
+#include "console.h"
+
+#define EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID \
+ { 0xdd9e7534, 0x7762, 0x4698, { 0x8c, 0x14, 0xf5, 0x85, 0x17, 0xa6, 0x25, 0xaa } }
+
+struct _EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL;
+
+typedef EFI_STATUS (EFIAPI *EFI_INPUT_RESET_EX)(
+ struct _EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This,
+ BOOLEAN ExtendedVerification
+);
+
+typedef UINT8 EFI_KEY_TOGGLE_STATE;
+
+typedef struct {
+ UINT32 KeyShiftState;
+ EFI_KEY_TOGGLE_STATE KeyToggleState;
+} EFI_KEY_STATE;
+
+typedef struct {
+ EFI_INPUT_KEY Key;
+ EFI_KEY_STATE KeyState;
+} EFI_KEY_DATA;
+
+typedef EFI_STATUS (EFIAPI *EFI_INPUT_READ_KEY_EX)(
+ struct _EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This,
+ EFI_KEY_DATA *KeyData
+);
+
+typedef EFI_STATUS (EFIAPI *EFI_SET_STATE)(
+ struct _EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This,
+ EFI_KEY_TOGGLE_STATE *KeyToggleState
+);
+
+typedef EFI_STATUS (EFIAPI *EFI_KEY_NOTIFY_FUNCTION)(
+ EFI_KEY_DATA *KeyData
+);
+
+typedef EFI_STATUS (EFIAPI *EFI_REGISTER_KEYSTROKE_NOTIFY)(
+ struct _EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This,
+ EFI_KEY_DATA KeyData,
+ EFI_KEY_NOTIFY_FUNCTION KeyNotificationFunction,
+ VOID **NotifyHandle
+);
+
+typedef EFI_STATUS (EFIAPI *EFI_UNREGISTER_KEYSTROKE_NOTIFY)(
+ struct _EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This,
+ VOID *NotificationHandle
+);
+
+typedef struct _EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL {
+ EFI_INPUT_RESET_EX Reset;
+ EFI_INPUT_READ_KEY_EX ReadKeyStrokeEx;
+ EFI_EVENT WaitForKeyEx;
+ EFI_SET_STATE SetState;
+ EFI_REGISTER_KEYSTROKE_NOTIFY RegisterKeyNotify;
+ EFI_UNREGISTER_KEYSTROKE_NOTIFY UnregisterKeyNotify;
+} EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL;
+
+EFI_STATUS console_key_read(UINT64 *key, BOOLEAN wait) {
+ EFI_GUID EfiSimpleTextInputExProtocolGuid = EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID;
+ static EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *TextInputEx;
+ static BOOLEAN checked;
+ UINTN index;
+ EFI_INPUT_KEY k;
+ EFI_STATUS err;
+
+ if (!checked) {
+ err = LibLocateProtocol(&EfiSimpleTextInputExProtocolGuid, (VOID **)&TextInputEx);
+ if (EFI_ERROR(err))
+ TextInputEx = NULL;
+
+ checked = TRUE;
+ }
+
+ /* wait until key is pressed */
+ if (wait) {
+ if (TextInputEx)
+ uefi_call_wrapper(BS->WaitForEvent, 3, 1, &TextInputEx->WaitForKeyEx, &index);
+ else
+ uefi_call_wrapper(BS->WaitForEvent, 3, 1, &ST->ConIn->WaitForKey, &index);
+ }
+
+ if (TextInputEx) {
+ EFI_KEY_DATA keydata;
+ UINT64 keypress;
+
+ err = uefi_call_wrapper(TextInputEx->ReadKeyStrokeEx, 2, TextInputEx, &keydata);
+ if (!EFI_ERROR(err)) {
+ UINT32 shift = 0;
+
+ /* do not distinguish between left and right keys */
+ if (keydata.KeyState.KeyShiftState & EFI_SHIFT_STATE_VALID) {
+ if (keydata.KeyState.KeyShiftState & (EFI_RIGHT_CONTROL_PRESSED|EFI_LEFT_CONTROL_PRESSED))
+ shift |= EFI_CONTROL_PRESSED;
+ if (keydata.KeyState.KeyShiftState & (EFI_RIGHT_ALT_PRESSED|EFI_LEFT_ALT_PRESSED))
+ shift |= EFI_ALT_PRESSED;
+ };
+
+ /* 32 bit modifier keys + 16 bit scan code + 16 bit unicode */
+ keypress = KEYPRESS(shift, keydata.Key.ScanCode, keydata.Key.UnicodeChar);
+ if (keypress > 0) {
+ *key = keypress;
+ return 0;
+ }
+ }
+ }
+
+ /* fallback for firmware which does not support SimpleTextInputExProtocol
+ *
+ * This is also called in case ReadKeyStrokeEx did not return a key, because
+ * some broken firmwares offer SimpleTextInputExProtocol, but never acually
+ * handle any key. */
+ err = uefi_call_wrapper(ST->ConIn->ReadKeyStroke, 2, ST->ConIn, &k);
+ if (EFI_ERROR(err))
+ return err;
+
+ *key = KEYPRESS(0, k.ScanCode, k.UnicodeChar);
+ return 0;
+}
diff --git a/src/boot/efi/console.h b/src/boot/efi/console.h
new file mode 100644
index 0000000000..5c7808a067
--- /dev/null
+++ b/src/boot/efi/console.h
@@ -0,0 +1,34 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that 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.
+ *
+ * Copyright (C) 2012-2013 Kay Sievers <kay@vrfy.org>
+ * Copyright (C) 2012 Harald Hoyer <harald@redhat.com>
+ */
+
+#ifndef __SDBOOT_CONSOLE_H
+#define __SDBOOT_CONSOLE_H
+
+#define EFI_SHIFT_STATE_VALID 0x80000000
+#define EFI_RIGHT_CONTROL_PRESSED 0x00000004
+#define EFI_LEFT_CONTROL_PRESSED 0x00000008
+#define EFI_RIGHT_ALT_PRESSED 0x00000010
+#define EFI_LEFT_ALT_PRESSED 0x00000020
+
+#define EFI_CONTROL_PRESSED (EFI_RIGHT_CONTROL_PRESSED|EFI_LEFT_CONTROL_PRESSED)
+#define EFI_ALT_PRESSED (EFI_RIGHT_ALT_PRESSED|EFI_LEFT_ALT_PRESSED)
+#define KEYPRESS(keys, scan, uni) ((((UINT64)keys) << 32) | ((scan) << 16) | (uni))
+#define KEYCHAR(k) ((k) & 0xffff)
+#define CHAR_CTRL(c) ((c) - 'a' + 1)
+
+EFI_STATUS console_key_read(UINT64 *key, BOOLEAN wait);
+#endif
diff --git a/src/boot/efi/graphics.c b/src/boot/efi/graphics.c
new file mode 100644
index 0000000000..2e9c11f5a0
--- /dev/null
+++ b/src/boot/efi/graphics.c
@@ -0,0 +1,91 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that 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.
+ *
+ * Copyright (C) 2012-2013 Kay Sievers <kay@vrfy.org>
+ * Copyright (C) 2012 Harald Hoyer <harald@redhat.com>
+ * Copyright (C) 2013 Intel Corporation
+ * Authored by Joonas Lahtinen <joonas.lahtinen@linux.intel.com>
+ */
+
+#include <efi.h>
+#include <efilib.h>
+
+#include "util.h"
+#include "graphics.h"
+
+EFI_STATUS graphics_mode(BOOLEAN on) {
+ #define EFI_CONSOLE_CONTROL_PROTOCOL_GUID \
+ { 0xf42f7782, 0x12e, 0x4c12, { 0x99, 0x56, 0x49, 0xf9, 0x43, 0x4, 0xf7, 0x21 } };
+
+ struct _EFI_CONSOLE_CONTROL_PROTOCOL;
+
+ typedef enum {
+ EfiConsoleControlScreenText,
+ EfiConsoleControlScreenGraphics,
+ EfiConsoleControlScreenMaxValue,
+ } EFI_CONSOLE_CONTROL_SCREEN_MODE;
+
+ typedef EFI_STATUS (EFIAPI *EFI_CONSOLE_CONTROL_PROTOCOL_GET_MODE)(
+ struct _EFI_CONSOLE_CONTROL_PROTOCOL *This,
+ EFI_CONSOLE_CONTROL_SCREEN_MODE *Mode,
+ BOOLEAN *UgaExists,
+ BOOLEAN *StdInLocked
+ );
+
+ typedef EFI_STATUS (EFIAPI *EFI_CONSOLE_CONTROL_PROTOCOL_SET_MODE)(
+ struct _EFI_CONSOLE_CONTROL_PROTOCOL *This,
+ EFI_CONSOLE_CONTROL_SCREEN_MODE Mode
+ );
+
+ typedef EFI_STATUS (EFIAPI *EFI_CONSOLE_CONTROL_PROTOCOL_LOCK_STD_IN)(
+ struct _EFI_CONSOLE_CONTROL_PROTOCOL *This,
+ CHAR16 *Password
+ );
+
+ typedef struct _EFI_CONSOLE_CONTROL_PROTOCOL {
+ EFI_CONSOLE_CONTROL_PROTOCOL_GET_MODE GetMode;
+ EFI_CONSOLE_CONTROL_PROTOCOL_SET_MODE SetMode;
+ EFI_CONSOLE_CONTROL_PROTOCOL_LOCK_STD_IN LockStdIn;
+ } EFI_CONSOLE_CONTROL_PROTOCOL;
+
+ EFI_GUID ConsoleControlProtocolGuid = EFI_CONSOLE_CONTROL_PROTOCOL_GUID;
+ EFI_CONSOLE_CONTROL_PROTOCOL *ConsoleControl = NULL;
+ EFI_CONSOLE_CONTROL_SCREEN_MODE new;
+ EFI_CONSOLE_CONTROL_SCREEN_MODE current;
+ BOOLEAN uga_exists;
+ BOOLEAN stdin_locked;
+ EFI_STATUS err;
+
+ err = LibLocateProtocol(&ConsoleControlProtocolGuid, (VOID **)&ConsoleControl);
+ if (EFI_ERROR(err)) {
+ /* console control protocol is nonstandard and might not exist. */
+ return err == EFI_NOT_FOUND ? EFI_SUCCESS : err;
+ }
+
+ /* check current mode */
+ err = uefi_call_wrapper(ConsoleControl->GetMode, 4, ConsoleControl, &current, &uga_exists, &stdin_locked);
+ if (EFI_ERROR(err))
+ return err;
+
+ /* do not touch the mode */
+ new = on ? EfiConsoleControlScreenGraphics : EfiConsoleControlScreenText;
+ if (new == current)
+ return EFI_SUCCESS;
+
+ err = uefi_call_wrapper(ConsoleControl->SetMode, 2, ConsoleControl, new);
+
+ /* some firmware enables the cursor when switching modes */
+ uefi_call_wrapper(ST->ConOut->EnableCursor, 2, ST->ConOut, FALSE);
+
+ return err;
+}
diff --git a/src/boot/efi/graphics.h b/src/boot/efi/graphics.h
new file mode 100644
index 0000000000..3ee4972798
--- /dev/null
+++ b/src/boot/efi/graphics.h
@@ -0,0 +1,24 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that 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.
+ *
+ * Copyright (C) 2012-2013 Kay Sievers <kay@vrfy.org>
+ * Copyright (C) 2012 Harald Hoyer <harald@redhat.com>
+ * Copyright (C) 2013 Intel Corporation
+ * Authored by Joonas Lahtinen <joonas.lahtinen@linux.intel.com>
+ */
+
+#ifndef __SDBOOT_GRAPHICS_H
+#define __SDBOOT_GRAPHICS_H
+
+EFI_STATUS graphics_mode(BOOLEAN on);
+#endif
diff --git a/src/boot/efi/linux.c b/src/boot/efi/linux.c
new file mode 100644
index 0000000000..809c69310e
--- /dev/null
+++ b/src/boot/efi/linux.c
@@ -0,0 +1,130 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that 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.
+ *
+ * Copyright (C) 2015 Kay Sievers <kay@vrfy.org>
+ */
+
+#include <efi.h>
+#include <efilib.h>
+
+#include "util.h"
+#include "linux.h"
+
+#define SETUP_MAGIC 0x53726448 /* "HdrS" */
+struct SetupHeader {
+ UINT8 boot_sector[0x01f1];
+ UINT8 setup_secs;
+ UINT16 root_flags;
+ UINT32 sys_size;
+ UINT16 ram_size;
+ UINT16 video_mode;
+ UINT16 root_dev;
+ UINT16 signature;
+ UINT16 jump;
+ UINT32 header;
+ UINT16 version;
+ UINT16 su_switch;
+ UINT16 setup_seg;
+ UINT16 start_sys;
+ UINT16 kernel_ver;
+ UINT8 loader_id;
+ UINT8 load_flags;
+ UINT16 movesize;
+ UINT32 code32_start;
+ UINT32 ramdisk_start;
+ UINT32 ramdisk_len;
+ UINT32 bootsect_kludge;
+ UINT16 heap_end;
+ UINT8 ext_loader_ver;
+ UINT8 ext_loader_type;
+ UINT32 cmd_line_ptr;
+ UINT32 ramdisk_max;
+ UINT32 kernel_alignment;
+ UINT8 relocatable_kernel;
+ UINT8 min_alignment;
+ UINT16 xloadflags;
+ UINT32 cmdline_size;
+ UINT32 hardware_subarch;
+ UINT64 hardware_subarch_data;
+ UINT32 payload_offset;
+ UINT32 payload_length;
+ UINT64 setup_data;
+ UINT64 pref_address;
+ UINT32 init_size;
+ UINT32 handover_offset;
+} __attribute__((packed));
+
+#ifdef __x86_64__
+typedef VOID(*handover_f)(VOID *image, EFI_SYSTEM_TABLE *table, struct SetupHeader *setup);
+static inline VOID linux_efi_handover(EFI_HANDLE image, struct SetupHeader *setup) {
+ handover_f handover;
+
+ asm volatile ("cli");
+ handover = (handover_f)((UINTN)setup->code32_start + 512 + setup->handover_offset);
+ handover(image, ST, setup);
+}
+#else
+typedef VOID(*handover_f)(VOID *image, EFI_SYSTEM_TABLE *table, struct SetupHeader *setup) __attribute__((regparm(0)));
+static inline VOID linux_efi_handover(EFI_HANDLE image, struct SetupHeader *setup) {
+ handover_f handover;
+
+ handover = (handover_f)((UINTN)setup->code32_start + setup->handover_offset);
+ handover(image, ST, setup);
+}
+#endif
+
+EFI_STATUS linux_exec(EFI_HANDLE *image,
+ CHAR8 *cmdline, UINTN cmdline_len,
+ UINTN linux_addr,
+ UINTN initrd_addr, UINTN initrd_size) {
+ struct SetupHeader *image_setup;
+ struct SetupHeader *boot_setup;
+ EFI_PHYSICAL_ADDRESS addr;
+ EFI_STATUS err;
+
+ image_setup = (struct SetupHeader *)(linux_addr);
+ if (image_setup->signature != 0xAA55 || image_setup->header != SETUP_MAGIC)
+ return EFI_LOAD_ERROR;
+
+ if (image_setup->version < 0x20b || !image_setup->relocatable_kernel)
+ return EFI_LOAD_ERROR;
+
+ addr = 0x3fffffff;
+ err = uefi_call_wrapper(BS->AllocatePages, 4, AllocateMaxAddress, EfiLoaderData,
+ EFI_SIZE_TO_PAGES(0x4000), &addr);
+ if (EFI_ERROR(err))
+ return err;
+ boot_setup = (struct SetupHeader *)(UINTN)addr;
+ ZeroMem(boot_setup, 0x4000);
+ CopyMem(boot_setup, image_setup, sizeof(struct SetupHeader));
+ boot_setup->loader_id = 0xff;
+
+ boot_setup->code32_start = (UINT32)linux_addr + (image_setup->setup_secs+1) * 512;
+
+ if (cmdline) {
+ addr = 0xA0000;
+ err = uefi_call_wrapper(BS->AllocatePages, 4, AllocateMaxAddress, EfiLoaderData,
+ EFI_SIZE_TO_PAGES(cmdline_len + 1), &addr);
+ if (EFI_ERROR(err))
+ return err;
+ CopyMem((VOID *)(UINTN)addr, cmdline, cmdline_len);
+ ((CHAR8 *)addr)[cmdline_len] = 0;
+ boot_setup->cmd_line_ptr = (UINT32)addr;
+ }
+
+ boot_setup->ramdisk_start = (UINT32)initrd_addr;
+ boot_setup->ramdisk_len = (UINT32)initrd_size;
+
+ linux_efi_handover(image, boot_setup);
+ return EFI_LOAD_ERROR;
+}
diff --git a/src/boot/efi/linux.h b/src/boot/efi/linux.h
new file mode 100644
index 0000000000..aff69a9778
--- /dev/null
+++ b/src/boot/efi/linux.h
@@ -0,0 +1,24 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that 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.
+ *
+ * Copyright (C) 2015 Kay Sievers <kay@vrfy.org>
+ */
+
+#ifndef __SDBOOT_kernel_H
+#define __SDBOOT_kernel_H
+
+EFI_STATUS linux_exec(EFI_HANDLE *image,
+ CHAR8 *cmdline, UINTN cmdline_size,
+ UINTN linux_addr,
+ UINTN initrd_addr, UINTN initrd_size);
+#endif
diff --git a/src/boot/efi/pefile.c b/src/boot/efi/pefile.c
new file mode 100644
index 0000000000..e6fedbc929
--- /dev/null
+++ b/src/boot/efi/pefile.c
@@ -0,0 +1,172 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that 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.
+ *
+ * Copyright (C) 2015 Kay Sievers <kay@vrfy.org>
+ */
+
+#include <efi.h>
+#include <efilib.h>
+
+#include "util.h"
+#include "pefile.h"
+
+struct DosFileHeader {
+ UINT8 Magic[2];
+ UINT16 LastSize;
+ UINT16 nBlocks;
+ UINT16 nReloc;
+ UINT16 HdrSize;
+ UINT16 MinAlloc;
+ UINT16 MaxAlloc;
+ UINT16 ss;
+ UINT16 sp;
+ UINT16 Checksum;
+ UINT16 ip;
+ UINT16 cs;
+ UINT16 RelocPos;
+ UINT16 nOverlay;
+ UINT16 reserved[4];
+ UINT16 OEMId;
+ UINT16 OEMInfo;
+ UINT16 reserved2[10];
+ UINT32 ExeHeader;
+} __attribute__((packed));
+
+#define PE_HEADER_MACHINE_I386 0x014c
+#define PE_HEADER_MACHINE_X64 0x8664
+struct PeFileHeader {
+ UINT16 Machine;
+ UINT16 NumberOfSections;
+ UINT32 TimeDateStamp;
+ UINT32 PointerToSymbolTable;
+ UINT32 NumberOfSymbols;
+ UINT16 SizeOfOptionalHeader;
+ UINT16 Characteristics;
+} __attribute__((packed));
+
+struct PeSectionHeader {
+ UINT8 Name[8];
+ UINT32 VirtualSize;
+ UINT32 VirtualAddress;
+ UINT32 SizeOfRawData;
+ UINT32 PointerToRawData;
+ UINT32 PointerToRelocations;
+ UINT32 PointerToLinenumbers;
+ UINT16 NumberOfRelocations;
+ UINT16 NumberOfLinenumbers;
+ UINT32 Characteristics;
+} __attribute__((packed));
+
+
+EFI_STATUS pefile_locate_sections(EFI_FILE *dir, CHAR16 *path, CHAR8 **sections, UINTN *addrs, UINTN *offsets, UINTN *sizes) {
+ EFI_FILE_HANDLE handle;
+ struct DosFileHeader dos;
+ uint8_t magic[4];
+ struct PeFileHeader pe;
+ UINTN len;
+ UINTN i;
+ EFI_STATUS err;
+
+ err = uefi_call_wrapper(dir->Open, 5, dir, &handle, path, EFI_FILE_MODE_READ, 0ULL);
+ if (EFI_ERROR(err))
+ return err;
+
+ /* MS-DOS stub */
+ len = sizeof(dos);
+ err = uefi_call_wrapper(handle->Read, 3, handle, &len, &dos);
+ if (EFI_ERROR(err))
+ goto out;
+ if (len != sizeof(dos)) {
+ err = EFI_LOAD_ERROR;
+ goto out;
+ }
+
+ if (CompareMem(dos.Magic, "MZ", 2) != 0) {
+ err = EFI_LOAD_ERROR;
+ goto out;
+ }
+
+ err = uefi_call_wrapper(handle->SetPosition, 2, handle, dos.ExeHeader);
+ if (EFI_ERROR(err))
+ goto out;
+
+ /* PE header */
+ len = sizeof(magic);
+ err = uefi_call_wrapper(handle->Read, 3, handle, &len, &magic);
+ if (EFI_ERROR(err))
+ goto out;
+ if (len != sizeof(magic)) {
+ err = EFI_LOAD_ERROR;
+ goto out;
+ }
+
+ if (CompareMem(magic, "PE\0\0", 2) != 0) {
+ err = EFI_LOAD_ERROR;
+ goto out;
+ }
+
+ len = sizeof(pe);
+ err = uefi_call_wrapper(handle->Read, 3, handle, &len, &pe);
+ if (EFI_ERROR(err))
+ goto out;
+ if (len != sizeof(pe)) {
+ err = EFI_LOAD_ERROR;
+ goto out;
+ }
+
+ /* PE32+ Subsystem type */
+ if (pe.Machine != PE_HEADER_MACHINE_X64 &&
+ pe.Machine != PE_HEADER_MACHINE_I386) {
+ err = EFI_LOAD_ERROR;
+ goto out;
+ }
+
+ if (pe.NumberOfSections > 96) {
+ err = EFI_LOAD_ERROR;
+ goto out;
+ }
+
+ /* the sections start directly after the headers */
+ err = uefi_call_wrapper(handle->SetPosition, 2, handle, dos.ExeHeader + sizeof(magic) + sizeof(pe) + pe.SizeOfOptionalHeader);
+ if (EFI_ERROR(err))
+ goto out;
+
+ for (i = 0; i < pe.NumberOfSections; i++) {
+ struct PeSectionHeader sect;
+ UINTN j;
+
+ len = sizeof(sect);
+ err = uefi_call_wrapper(handle->Read, 3, handle, &len, &sect);
+ if (EFI_ERROR(err))
+ goto out;
+ if (len != sizeof(sect)) {
+ err = EFI_LOAD_ERROR;
+ goto out;
+ }
+ for (j = 0; sections[j]; j++) {
+ if (CompareMem(sect.Name, sections[j], strlena(sections[j])) != 0)
+ continue;
+
+ if (addrs)
+ addrs[j] = (UINTN)sect.VirtualAddress;
+ if (offsets)
+ offsets[j] = (UINTN)sect.PointerToRawData;
+ if (sizes)
+ sizes[j] = (UINTN)sect.VirtualSize;
+ }
+ }
+
+out:
+ uefi_call_wrapper(handle->Close, 1, handle);
+ return err;
+}
diff --git a/src/boot/efi/pefile.h b/src/boot/efi/pefile.h
new file mode 100644
index 0000000000..ca2f9a2508
--- /dev/null
+++ b/src/boot/efi/pefile.h
@@ -0,0 +1,22 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that 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.
+ *
+ * Copyright (C) 2015 Kay Sievers <kay@vrfy.org>
+ */
+
+#ifndef __SDBOOT_PEFILE_H
+#define __SDBOOT_PEFILE_H
+
+EFI_STATUS pefile_locate_sections(EFI_FILE *dir, CHAR16 *path,
+ CHAR8 **sections, UINTN *addrs, UINTN *offsets, UINTN *sizes);
+#endif
diff --git a/src/boot/efi/splash.c b/src/boot/efi/splash.c
new file mode 100644
index 0000000000..470ea3e2cc
--- /dev/null
+++ b/src/boot/efi/splash.c
@@ -0,0 +1,323 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that 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.
+ *
+ * Copyright (C) 2012-2013 Kay Sievers <kay@vrfy.org>
+ * Copyright (C) 2012 Harald Hoyer <harald@redhat.com>
+ */
+
+#include <efi.h>
+#include <efilib.h>
+
+#include "util.h"
+#include "graphics.h"
+#include "splash.h"
+
+struct bmp_file {
+ CHAR8 signature[2];
+ UINT32 size;
+ UINT16 reserved[2];
+ UINT32 offset;
+} __attribute__((packed));
+
+/* we require at least BITMAPINFOHEADER, later versions are
+ accepted, but their features ignored */
+struct bmp_dib {
+ UINT32 size;
+ UINT32 x;
+ UINT32 y;
+ UINT16 planes;
+ UINT16 depth;
+ UINT32 compression;
+ UINT32 image_size;
+ INT32 x_pixel_meter;
+ INT32 y_pixel_meter;
+ UINT32 colors_used;
+ UINT32 colors_important;
+} __attribute__((packed));
+
+struct bmp_map {
+ UINT8 blue;
+ UINT8 green;
+ UINT8 red;
+ UINT8 reserved;
+} __attribute__((packed));
+
+EFI_STATUS bmp_parse_header(UINT8 *bmp, UINTN size, struct bmp_dib **ret_dib,
+ struct bmp_map **ret_map, UINT8 **pixmap) {
+ struct bmp_file *file;
+ struct bmp_dib *dib;
+ struct bmp_map *map;
+ UINTN row_size;
+
+ if (size < sizeof(struct bmp_file) + sizeof(struct bmp_dib))
+ return EFI_INVALID_PARAMETER;
+
+ /* check file header */
+ file = (struct bmp_file *)bmp;
+ if (file->signature[0] != 'B' || file->signature[1] != 'M')
+ return EFI_INVALID_PARAMETER;
+ if (file->size != size)
+ return EFI_INVALID_PARAMETER;
+ if (file->size < file->offset)
+ return EFI_INVALID_PARAMETER;
+
+ /* check device-independent bitmap */
+ dib = (struct bmp_dib *)(bmp + sizeof(struct bmp_file));
+ if (dib->size < sizeof(struct bmp_dib))
+ return EFI_UNSUPPORTED;
+
+ switch (dib->depth) {
+ case 1:
+ case 4:
+ case 8:
+ case 24:
+ if (dib->compression != 0)
+ return EFI_UNSUPPORTED;
+
+ break;
+
+ case 16:
+ case 32:
+ if (dib->compression != 0 && dib->compression != 3)
+ return EFI_UNSUPPORTED;
+
+ break;
+
+ default:
+ return EFI_UNSUPPORTED;
+ }
+
+ row_size = ((UINTN) dib->depth * dib->x + 31) / 32 * 4;
+ if (file->size - file->offset < dib->y * row_size)
+ return EFI_INVALID_PARAMETER;
+ if (row_size * dib->y > 64 * 1024 * 1024)
+ return EFI_INVALID_PARAMETER;
+
+ /* check color table */
+ map = (struct bmp_map *)(bmp + sizeof(struct bmp_file) + dib->size);
+ if (file->offset < sizeof(struct bmp_file) + dib->size)
+ return EFI_INVALID_PARAMETER;
+
+ if (file->offset > sizeof(struct bmp_file) + dib->size) {
+ UINT32 map_count;
+ UINTN map_size;
+
+ if (dib->colors_used)
+ map_count = dib->colors_used;
+ else {
+ switch (dib->depth) {
+ case 1:
+ case 4:
+ case 8:
+ map_count = 1 << dib->depth;
+ break;
+
+ default:
+ map_count = 0;
+ break;
+ }
+ }
+
+ map_size = file->offset - (sizeof(struct bmp_file) + dib->size);
+ if (map_size != sizeof(struct bmp_map) * map_count)
+ return EFI_INVALID_PARAMETER;
+ }
+
+ *ret_map = map;
+ *ret_dib = dib;
+ *pixmap = bmp + file->offset;
+
+ return EFI_SUCCESS;
+}
+
+static VOID pixel_blend(UINT32 *dst, const UINT32 source) {
+ UINT32 alpha, src, src_rb, src_g, dst_rb, dst_g, rb, g;
+
+ alpha = (source & 0xff);
+
+ /* convert src from RGBA to XRGB */
+ src = source >> 8;
+
+ /* decompose into RB and G components */
+ src_rb = (src & 0xff00ff);
+ src_g = (src & 0x00ff00);
+
+ dst_rb = (*dst & 0xff00ff);
+ dst_g = (*dst & 0x00ff00);
+
+ /* blend */
+ rb = ((((src_rb - dst_rb) * alpha + 0x800080) >> 8) + dst_rb) & 0xff00ff;
+ g = ((((src_g - dst_g) * alpha + 0x008000) >> 8) + dst_g) & 0x00ff00;
+
+ *dst = (rb | g);
+}
+
+EFI_STATUS bmp_to_blt(EFI_GRAPHICS_OUTPUT_BLT_PIXEL *buf,
+ struct bmp_dib *dib, struct bmp_map *map,
+ UINT8 *pixmap) {
+ UINT8 *in;
+ UINTN y;
+
+ /* transform and copy pixels */
+ in = pixmap;
+ for (y = 0; y < dib->y; y++) {
+ EFI_GRAPHICS_OUTPUT_BLT_PIXEL *out;
+ UINTN row_size;
+ UINTN x;
+
+ out = &buf[(dib->y - y - 1) * dib->x];
+ for (x = 0; x < dib->x; x++, in++, out++) {
+ switch (dib->depth) {
+ case 1: {
+ UINTN i;
+
+ for (i = 0; i < 8 && x < dib->x; i++) {
+ out->Red = map[((*in) >> (7 - i)) & 1].red;
+ out->Green = map[((*in) >> (7 - i)) & 1].green;
+ out->Blue = map[((*in) >> (7 - i)) & 1].blue;
+ out++;
+ x++;
+ }
+ out--;
+ x--;
+ break;
+ }
+
+ case 4: {
+ UINTN i;
+
+ i = (*in) >> 4;
+ out->Red = map[i].red;
+ out->Green = map[i].green;
+ out->Blue = map[i].blue;
+ if (x < (dib->x - 1)) {
+ out++;
+ x++;
+ i = (*in) & 0x0f;
+ out->Red = map[i].red;
+ out->Green = map[i].green;
+ out->Blue = map[i].blue;
+ }
+ break;
+ }
+
+ case 8:
+ out->Red = map[*in].red;
+ out->Green = map[*in].green;
+ out->Blue = map[*in].blue;
+ break;
+
+ case 16: {
+ UINT16 i = *(UINT16 *) in;
+
+ out->Red = (i & 0x7c00) >> 7;
+ out->Green = (i & 0x3e0) >> 2;
+ out->Blue = (i & 0x1f) << 3;
+ in += 1;
+ break;
+ }
+
+ case 24:
+ out->Red = in[2];
+ out->Green = in[1];
+ out->Blue = in[0];
+ in += 2;
+ break;
+
+ case 32: {
+ UINT32 i = *(UINT32 *) in;
+
+ pixel_blend((UINT32 *)out, i);
+
+ in += 3;
+ break;
+ }
+ }
+ }
+
+ /* add row padding; new lines always start at 32 bit boundary */
+ row_size = in - pixmap;
+ in += ((row_size + 3) & ~3) - row_size;
+ }
+
+ return EFI_SUCCESS;
+}
+
+EFI_STATUS graphics_splash(UINT8 *content, UINTN len, const EFI_GRAPHICS_OUTPUT_BLT_PIXEL *background) {
+ EFI_GRAPHICS_OUTPUT_BLT_PIXEL pixel = {};
+ EFI_GUID GraphicsOutputProtocolGuid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID;
+ EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput = NULL;
+ struct bmp_dib *dib;
+ struct bmp_map *map;
+ UINT8 *pixmap;
+ UINT64 blt_size;
+ VOID *blt = NULL;
+ UINTN x_pos = 0;
+ UINTN y_pos = 0;
+ EFI_STATUS err;
+
+ if (!background) {
+ if (StriCmp(L"Apple", ST->FirmwareVendor) == 0) {
+ pixel.Red = 0xc0;
+ pixel.Green = 0xc0;
+ pixel.Blue = 0xc0;
+ }
+ background = &pixel;
+ }
+
+ err = LibLocateProtocol(&GraphicsOutputProtocolGuid, (VOID **)&GraphicsOutput);
+ if (EFI_ERROR(err))
+ return err;
+
+ err = bmp_parse_header(content, len, &dib, &map, &pixmap);
+ if (EFI_ERROR(err))
+ goto err;
+
+ if(dib->x < GraphicsOutput->Mode->Info->HorizontalResolution)
+ x_pos = (GraphicsOutput->Mode->Info->HorizontalResolution - dib->x) / 2;
+ if(dib->y < GraphicsOutput->Mode->Info->VerticalResolution)
+ y_pos = (GraphicsOutput->Mode->Info->VerticalResolution - dib->y) / 2;
+
+ uefi_call_wrapper(GraphicsOutput->Blt, 10, GraphicsOutput,
+ (EFI_GRAPHICS_OUTPUT_BLT_PIXEL *)background,
+ EfiBltVideoFill, 0, 0, 0, 0,
+ GraphicsOutput->Mode->Info->HorizontalResolution,
+ GraphicsOutput->Mode->Info->VerticalResolution, 0);
+
+ /* EFI buffer */
+ blt_size = dib->x * dib->y * sizeof(EFI_GRAPHICS_OUTPUT_BLT_PIXEL);
+ blt = AllocatePool(blt_size);
+ if (!blt)
+ return EFI_OUT_OF_RESOURCES;
+
+ err = uefi_call_wrapper(GraphicsOutput->Blt, 10, GraphicsOutput,
+ blt, EfiBltVideoToBltBuffer, x_pos, y_pos, 0, 0,
+ dib->x, dib->y, 0);
+ if (EFI_ERROR(err))
+ goto err;
+
+ err = bmp_to_blt(blt, dib, map, pixmap);
+ if (EFI_ERROR(err))
+ goto err;
+
+ err = graphics_mode(TRUE);
+ if (EFI_ERROR(err))
+ goto err;
+
+ err = uefi_call_wrapper(GraphicsOutput->Blt, 10, GraphicsOutput,
+ blt, EfiBltBufferToVideo, 0, 0, x_pos, y_pos,
+ dib->x, dib->y, 0);
+err:
+ FreePool(blt);
+ return err;
+}
diff --git a/src/boot/efi/splash.h b/src/boot/efi/splash.h
new file mode 100644
index 0000000000..0183e79b16
--- /dev/null
+++ b/src/boot/efi/splash.h
@@ -0,0 +1,22 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that 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.
+ *
+ * Copyright (C) 2012-2013 Kay Sievers <kay@vrfy.org>
+ * Copyright (C) 2012 Harald Hoyer <harald@redhat.com>
+ */
+
+#ifndef __SDBOOT_SPLASH_H
+#define __SDBOOT_SPLASH_H
+
+EFI_STATUS graphics_splash(UINT8 *content, UINTN len, const EFI_GRAPHICS_OUTPUT_BLT_PIXEL *background);
+#endif
diff --git a/src/boot/efi/stub.c b/src/boot/efi/stub.c
new file mode 100644
index 0000000000..0b1bc491ed
--- /dev/null
+++ b/src/boot/efi/stub.c
@@ -0,0 +1,113 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/* This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that 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.
+ *
+ * Copyright (C) 2015 Kay Sievers <kay@vrfy.org>
+ */
+
+#include <efi.h>
+#include <efilib.h>
+
+#include "util.h"
+#include "pefile.h"
+#include "graphics.h"
+#include "splash.h"
+#include "linux.h"
+
+/* magic string to find in the binary image */
+static const char __attribute__((used)) magic[] = "#### LoaderInfo: systemd-stub " VERSION " ####";
+
+static const EFI_GUID global_guid = EFI_GLOBAL_VARIABLE;
+
+EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
+ EFI_LOADED_IMAGE *loaded_image;
+ EFI_FILE *root_dir;
+ CHAR16 *loaded_image_path;
+ CHAR8 *b;
+ UINTN size;
+ BOOLEAN secure = FALSE;
+ CHAR8 *sections[] = {
+ (UINT8 *)".cmdline",
+ (UINT8 *)".linux",
+ (UINT8 *)".initrd",
+ (UINT8 *)".splash",
+ NULL
+ };
+ UINTN addrs[ELEMENTSOF(sections)-1] = {};
+ UINTN offs[ELEMENTSOF(sections)-1] = {};
+ UINTN szs[ELEMENTSOF(sections)-1] = {};
+ CHAR8 *cmdline = NULL;
+ UINTN cmdline_len;
+ EFI_STATUS err;
+
+ InitializeLib(image, sys_table);
+
+ err = uefi_call_wrapper(BS->OpenProtocol, 6, image, &LoadedImageProtocol, (VOID **)&loaded_image,
+ image, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+ if (EFI_ERROR(err)) {
+ Print(L"Error getting a LoadedImageProtocol handle: %r ", err);
+ uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
+ return err;
+ }
+
+ root_dir = LibOpenRoot(loaded_image->DeviceHandle);
+ if (!root_dir) {
+ Print(L"Unable to open root directory: %r ", err);
+ uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
+ return EFI_LOAD_ERROR;
+ }
+
+ loaded_image_path = DevicePathToStr(loaded_image->FilePath);
+
+ if (efivar_get_raw(&global_guid, L"SecureBoot", &b, &size) == EFI_SUCCESS) {
+ if (*b > 0)
+ secure = TRUE;
+ FreePool(b);
+ }
+
+ err = pefile_locate_sections(root_dir, loaded_image_path, sections, addrs, offs, szs);
+ if (EFI_ERROR(err)) {
+ Print(L"Unable to locate embedded .linux section: %r ", err);
+ uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
+ return err;
+ }
+
+ if (szs[0] > 0)
+ cmdline = (CHAR8 *)(loaded_image->ImageBase + addrs[0]);
+
+ cmdline_len = szs[0];
+
+ /* if we are not in secure boot mode, accept a custom command line and replace the built-in one */
+ if (!secure && loaded_image->LoadOptionsSize > 0) {
+ CHAR16 *options;
+ CHAR8 *line;
+ UINTN i;
+
+ options = (CHAR16 *)loaded_image->LoadOptions;
+ cmdline_len = (loaded_image->LoadOptionsSize / sizeof(CHAR16)) * sizeof(CHAR8);
+ line = AllocatePool(cmdline_len);
+ for (i = 0; i < cmdline_len; i++)
+ line[i] = options[i];
+ cmdline = line;
+ }
+
+ if (szs[3] > 0)
+ graphics_splash((UINT8 *)((UINTN)loaded_image->ImageBase + addrs[3]), szs[3], NULL);
+
+ err = linux_exec(image, cmdline, cmdline_len,
+ (UINTN)loaded_image->ImageBase + addrs[1],
+ (UINTN)loaded_image->ImageBase + addrs[2], szs[2]);
+
+ graphics_mode(FALSE);
+ Print(L"Execution of embedded linux image failed: %r\n", err);
+ uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
+ return err;
+}
diff --git a/src/boot/efi/util.c b/src/boot/efi/util.c
new file mode 100644
index 0000000000..5e374edacf
--- /dev/null
+++ b/src/boot/efi/util.c
@@ -0,0 +1,347 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that 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.
+ *
+ * Copyright (C) 2012-2013 Kay Sievers <kay@vrfy.org>
+ * Copyright (C) 2012 Harald Hoyer <harald@redhat.com>
+ */
+
+#include <efi.h>
+#include <efilib.h>
+
+#include "util.h"
+
+/*
+ * Allocated random UUID, intended to be shared across tools that implement
+ * the (ESP)\loader\entries\<vendor>-<revision>.conf convention and the
+ * associated EFI variables.
+ */
+static const EFI_GUID loader_guid = { 0x4a67b082, 0x0a4c, 0x41cf, {0xb6, 0xc7, 0x44, 0x0b, 0x29, 0xbb, 0x8c, 0x4f} };
+
+#ifdef __x86_64__
+UINT64 ticks_read(VOID) {
+ UINT64 a, d;
+ __asm__ volatile ("rdtsc" : "=a" (a), "=d" (d));
+ return (d << 32) | a;
+}
+#elif defined(__i386__)
+UINT64 ticks_read(VOID) {
+ UINT64 val;
+ __asm__ volatile ("rdtsc" : "=A" (val));
+ return val;
+}
+#else
+UINT64 ticks_read(VOID) {
+ UINT64 val = 1;
+ return val;
+}
+#endif
+
+/* count TSC ticks during a millisecond delay */
+UINT64 ticks_freq(VOID) {
+ UINT64 ticks_start, ticks_end;
+
+ ticks_start = ticks_read();
+ uefi_call_wrapper(BS->Stall, 1, 1000);
+ ticks_end = ticks_read();
+
+ return (ticks_end - ticks_start) * 1000;
+}
+
+UINT64 time_usec(VOID) {
+ UINT64 ticks;
+ static UINT64 freq;
+
+ ticks = ticks_read();
+ if (ticks == 0)
+ return 0;
+
+ if (freq == 0) {
+ freq = ticks_freq();
+ if (freq == 0)
+ return 0;
+ }
+
+ return 1000 * 1000 * ticks / freq;
+}
+
+EFI_STATUS parse_boolean(CHAR8 *v, BOOLEAN *b) {
+ if (strcmpa(v, (CHAR8 *)"1") == 0 ||
+ strcmpa(v, (CHAR8 *)"yes") == 0 ||
+ strcmpa(v, (CHAR8 *)"y") == 0 ||
+ strcmpa(v, (CHAR8 *)"true") == 0) {
+ *b = TRUE;
+ return EFI_SUCCESS;
+ }
+
+ if (strcmpa(v, (CHAR8 *)"0") == 0 ||
+ strcmpa(v, (CHAR8 *)"no") == 0 ||
+ strcmpa(v, (CHAR8 *)"n") == 0 ||
+ strcmpa(v, (CHAR8 *)"false") == 0) {
+ *b = FALSE;
+ return EFI_SUCCESS;
+ }
+
+ return EFI_INVALID_PARAMETER;
+}
+
+EFI_STATUS efivar_set_raw(const EFI_GUID *vendor, CHAR16 *name, CHAR8 *buf, UINTN size, BOOLEAN persistent) {
+ UINT32 flags;
+
+ flags = EFI_VARIABLE_BOOTSERVICE_ACCESS|EFI_VARIABLE_RUNTIME_ACCESS;
+ if (persistent)
+ flags |= EFI_VARIABLE_NON_VOLATILE;
+
+ return uefi_call_wrapper(RT->SetVariable, 5, name, (EFI_GUID *)vendor, flags, size, buf);
+}
+
+EFI_STATUS efivar_set(CHAR16 *name, CHAR16 *value, BOOLEAN persistent) {
+ return efivar_set_raw(&loader_guid, name, (CHAR8 *)value, value ? (StrLen(value)+1) * sizeof(CHAR16) : 0, persistent);
+}
+
+EFI_STATUS efivar_set_int(CHAR16 *name, UINTN i, BOOLEAN persistent) {
+ CHAR16 str[32];
+
+ SPrint(str, 32, L"%d", i);
+ return efivar_set(name, str, persistent);
+}
+
+EFI_STATUS efivar_get(CHAR16 *name, CHAR16 **value) {
+ CHAR8 *buf;
+ CHAR16 *val;
+ UINTN size;
+ EFI_STATUS err;
+
+ err = efivar_get_raw(&loader_guid, name, &buf, &size);
+ if (EFI_ERROR(err))
+ return err;
+
+ val = StrDuplicate((CHAR16 *)buf);
+ if (!val) {
+ FreePool(buf);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ *value = val;
+ return EFI_SUCCESS;
+}
+
+EFI_STATUS efivar_get_int(CHAR16 *name, UINTN *i) {
+ CHAR16 *val;
+ EFI_STATUS err;
+
+ err = efivar_get(name, &val);
+ if (!EFI_ERROR(err)) {
+ *i = Atoi(val);
+ FreePool(val);
+ }
+ return err;
+}
+
+EFI_STATUS efivar_get_raw(const EFI_GUID *vendor, CHAR16 *name, CHAR8 **buffer, UINTN *size) {
+ CHAR8 *buf;
+ UINTN l;
+ EFI_STATUS err;
+
+ l = sizeof(CHAR16 *) * EFI_MAXIMUM_VARIABLE_SIZE;
+ buf = AllocatePool(l);
+ if (!buf)
+ return EFI_OUT_OF_RESOURCES;
+
+ err = uefi_call_wrapper(RT->GetVariable, 5, name, (EFI_GUID *)vendor, NULL, &l, buf);
+ if (!EFI_ERROR(err)) {
+ *buffer = buf;
+ if (size)
+ *size = l;
+ } else
+ FreePool(buf);
+ return err;
+
+}
+
+VOID efivar_set_time_usec(CHAR16 *name, UINT64 usec) {
+ CHAR16 str[32];
+
+ if (usec == 0)
+ usec = time_usec();
+ if (usec == 0)
+ return;
+
+ SPrint(str, 32, L"%ld", usec);
+ efivar_set(name, str, FALSE);
+}
+
+static INTN utf8_to_16(CHAR8 *stra, CHAR16 *c) {
+ CHAR16 unichar;
+ UINTN len;
+ UINTN i;
+
+ if (stra[0] < 0x80)
+ len = 1;
+ else if ((stra[0] & 0xe0) == 0xc0)
+ len = 2;
+ else if ((stra[0] & 0xf0) == 0xe0)
+ len = 3;
+ else if ((stra[0] & 0xf8) == 0xf0)
+ len = 4;
+ else if ((stra[0] & 0xfc) == 0xf8)
+ len = 5;
+ else if ((stra[0] & 0xfe) == 0xfc)
+ len = 6;
+ else
+ return -1;
+
+ switch (len) {
+ case 1:
+ unichar = stra[0];
+ break;
+ case 2:
+ unichar = stra[0] & 0x1f;
+ break;
+ case 3:
+ unichar = stra[0] & 0x0f;
+ break;
+ case 4:
+ unichar = stra[0] & 0x07;
+ break;
+ case 5:
+ unichar = stra[0] & 0x03;
+ break;
+ case 6:
+ unichar = stra[0] & 0x01;
+ break;
+ }
+
+ for (i = 1; i < len; i++) {
+ if ((stra[i] & 0xc0) != 0x80)
+ return -1;
+ unichar <<= 6;
+ unichar |= stra[i] & 0x3f;
+ }
+
+ *c = unichar;
+ return len;
+}
+
+CHAR16 *stra_to_str(CHAR8 *stra) {
+ UINTN strlen;
+ UINTN len;
+ UINTN i;
+ CHAR16 *str;
+
+ len = strlena(stra);
+ str = AllocatePool((len + 1) * sizeof(CHAR16));
+
+ strlen = 0;
+ i = 0;
+ while (i < len) {
+ INTN utf8len;
+
+ utf8len = utf8_to_16(stra + i, str + strlen);
+ if (utf8len <= 0) {
+ /* invalid utf8 sequence, skip the garbage */
+ i++;
+ continue;
+ }
+
+ strlen++;
+ i += utf8len;
+ }
+ str[strlen] = '\0';
+ return str;
+}
+
+CHAR16 *stra_to_path(CHAR8 *stra) {
+ CHAR16 *str;
+ UINTN strlen;
+ UINTN len;
+ UINTN i;
+
+ len = strlena(stra);
+ str = AllocatePool((len + 2) * sizeof(CHAR16));
+
+ str[0] = '\\';
+ strlen = 1;
+ i = 0;
+ while (i < len) {
+ INTN utf8len;
+
+ utf8len = utf8_to_16(stra + i, str + strlen);
+ if (utf8len <= 0) {
+ /* invalid utf8 sequence, skip the garbage */
+ i++;
+ continue;
+ }
+
+ if (str[strlen] == '/')
+ str[strlen] = '\\';
+ if (str[strlen] == '\\' && str[strlen-1] == '\\') {
+ /* skip double slashes */
+ i += utf8len;
+ continue;
+ }
+
+ strlen++;
+ i += utf8len;
+ }
+ str[strlen] = '\0';
+ return str;
+}
+
+CHAR8 *strchra(CHAR8 *s, CHAR8 c) {
+ do {
+ if (*s == c)
+ return s;
+ } while (*s++);
+ return NULL;
+}
+
+INTN file_read(EFI_FILE_HANDLE dir, CHAR16 *name, UINTN off, UINTN size, CHAR8 **content) {
+ EFI_FILE_HANDLE handle;
+ CHAR8 *buf;
+ UINTN buflen;
+ EFI_STATUS err;
+ UINTN len;
+
+ err = uefi_call_wrapper(dir->Open, 5, dir, &handle, name, EFI_FILE_MODE_READ, 0ULL);
+ if (EFI_ERROR(err))
+ return err;
+
+ if (size == 0) {
+ EFI_FILE_INFO *info;
+
+ info = LibFileInfo(handle);
+ buflen = info->FileSize+1;
+ FreePool(info);
+ } else
+ buflen = size;
+
+ if (off > 0) {
+ err = uefi_call_wrapper(handle->SetPosition, 2, handle, off);
+ if (EFI_ERROR(err))
+ return err;
+ }
+
+ buf = AllocatePool(buflen);
+ err = uefi_call_wrapper(handle->Read, 3, handle, &buflen, buf);
+ if (!EFI_ERROR(err)) {
+ buf[buflen] = '\0';
+ *content = buf;
+ len = buflen;
+ } else {
+ len = err;
+ FreePool(buf);
+ }
+
+ uefi_call_wrapper(handle->Close, 1, handle);
+ return len;
+}
diff --git a/src/boot/efi/util.h b/src/boot/efi/util.h
new file mode 100644
index 0000000000..4727a34d1f
--- /dev/null
+++ b/src/boot/efi/util.h
@@ -0,0 +1,50 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that 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.
+ *
+ * Copyright (C) 2012-2013 Kay Sievers <kay@vrfy.org>
+ * Copyright (C) 2012 Harald Hoyer <harald@redhat.com>
+ */
+
+#ifndef __SDBOOT_UTIL_H
+#define __SDBOOT_UTIL_H
+
+#include <efi.h>
+#include <efilib.h>
+
+#define ELEMENTSOF(x) (sizeof(x)/sizeof((x)[0]))
+
+static inline const CHAR16 *yes_no(BOOLEAN b) {
+ return b ? L"yes" : L"no";
+}
+
+EFI_STATUS parse_boolean(CHAR8 *v, BOOLEAN *b);
+
+UINT64 ticks_read(void);
+UINT64 ticks_freq(void);
+UINT64 time_usec(void);
+
+EFI_STATUS efivar_set(CHAR16 *name, CHAR16 *value, BOOLEAN persistent);
+EFI_STATUS efivar_set_raw(const EFI_GUID *vendor, CHAR16 *name, CHAR8 *buf, UINTN size, BOOLEAN persistent);
+EFI_STATUS efivar_set_int(CHAR16 *name, UINTN i, BOOLEAN persistent);
+VOID efivar_set_time_usec(CHAR16 *name, UINT64 usec);
+
+EFI_STATUS efivar_get(CHAR16 *name, CHAR16 **value);
+EFI_STATUS efivar_get_raw(const EFI_GUID *vendor, CHAR16 *name, CHAR8 **buffer, UINTN *size);
+EFI_STATUS efivar_get_int(CHAR16 *name, UINTN *i);
+
+CHAR8 *strchra(CHAR8 *s, CHAR8 c);
+CHAR16 *stra_to_path(CHAR8 *stra);
+CHAR16 *stra_to_str(CHAR8 *stra);
+
+INTN file_read(EFI_FILE_HANDLE dir, CHAR16 *name, UINTN off, UINTN size, CHAR8 **content);
+#endif
diff --git a/src/bootchart/bootchart.c b/src/bootchart/bootchart.c
index 64a384bacf..45fab92598 100644
--- a/src/bootchart/bootchart.c
+++ b/src/bootchart/bootchart.c
@@ -33,10 +33,7 @@
***/
-#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>
@@ -61,47 +58,33 @@
#include "bootchart.h"
#include "list.h"
-double graph_start;
-double log_start;
-struct ps_struct *ps_first;
-int pscount;
-int cpus;
-double interval;
-FILE *of = NULL;
-int overrun = 0;
static int exiting = 0;
-int sysfd=-1;
#define DEFAULT_SAMPLES_LEN 500
#define DEFAULT_HZ 25.0
#define DEFAULT_SCALE_X 100.0 /* 100px = 1sec */
#define DEFAULT_SCALE_Y 20.0 /* 16px = 1 process bar */
-#define DEFAULT_INIT ROOTLIBDIR "/systemd/systemd"
+#define DEFAULT_INIT ROOTLIBEXECDIR "/systemd"
#define DEFAULT_OUTPUT "/run/log"
/* graph defaults */
bool arg_entropy = false;
-bool initcall = true;
+bool arg_initcall = true;
bool arg_relative = false;
bool arg_filter = true;
bool arg_show_cmdline = false;
bool arg_show_cgroup = false;
bool arg_pss = false;
bool arg_percpu = false;
-int samples;
int arg_samples_len = DEFAULT_SAMPLES_LEN; /* we record len+1 (1 start sample) */
double arg_hz = DEFAULT_HZ;
double arg_scale_x = DEFAULT_SCALE_X;
double arg_scale_y = DEFAULT_SCALE_Y;
-static struct list_sample_data *sampledata;
-struct list_sample_data *head;
char arg_init_path[PATH_MAX] = DEFAULT_INIT;
char arg_output_path[PATH_MAX] = DEFAULT_OUTPUT;
static void signal_handler(int sig) {
- if (sig++)
- sig--;
exiting = 1;
}
@@ -138,31 +121,30 @@ static void parse_conf(void) {
}
static void help(void) {
- fprintf(stdout,
- "Usage: %s [OPTIONS]\n\n"
- "Options:\n"
- " -r, --rel Record time relative to recording\n"
- " -f, --freq=FREQ Sample frequency [%g]\n"
- " -n, --samples=N Stop sampling at [%d] samples\n"
- " -x, --scale-x=N Scale the graph horizontally [%g] \n"
- " -y, --scale-y=N Scale the graph vertically [%g] \n"
- " -p, --pss Enable PSS graph (CPU intensive)\n"
- " -e, --entropy Enable the entropy_avail graph\n"
- " -o, --output=PATH Path to output files [%s]\n"
- " -i, --init=PATH Path to init executable [%s]\n"
- " -F, --no-filter Disable filtering of unimportant or ephemeral processes\n"
- " -C, --cmdline Display full command lines with arguments\n"
- " -c, --control-group Display process control group\n"
- " --per-cpu Draw each CPU utilization and wait bar also\n"
- " -h, --help Display this message\n\n"
- "See bootchart.conf for more information.\n",
- program_invocation_short_name,
- DEFAULT_HZ,
- DEFAULT_SAMPLES_LEN,
- DEFAULT_SCALE_X,
- DEFAULT_SCALE_Y,
- DEFAULT_OUTPUT,
- DEFAULT_INIT);
+ printf("Usage: %s [OPTIONS]\n\n"
+ "Options:\n"
+ " -r --rel Record time relative to recording\n"
+ " -f --freq=FREQ Sample frequency [%g]\n"
+ " -n --samples=N Stop sampling at [%d] samples\n"
+ " -x --scale-x=N Scale the graph horizontally [%g] \n"
+ " -y --scale-y=N Scale the graph vertically [%g] \n"
+ " -p --pss Enable PSS graph (CPU intensive)\n"
+ " -e --entropy Enable the entropy_avail graph\n"
+ " -o --output=PATH Path to output files [%s]\n"
+ " -i --init=PATH Path to init executable [%s]\n"
+ " -F --no-filter Disable filtering of unimportant or ephemeral processes\n"
+ " -C --cmdline Display full command lines with arguments\n"
+ " -c --control-group Display process control group\n"
+ " --per-cpu Draw each CPU utilization and wait bar also\n"
+ " -h --help Display this message\n\n"
+ "See bootchart.conf for more information.\n",
+ program_invocation_short_name,
+ DEFAULT_HZ,
+ DEFAULT_SAMPLES_LEN,
+ DEFAULT_SCALE_X,
+ DEFAULT_SCALE_Y,
+ DEFAULT_OUTPUT,
+ DEFAULT_INIT);
}
static int parse_argv(int argc, char *argv[]) {
@@ -269,43 +251,41 @@ static int parse_argv(int argc, char *argv[]) {
return 1;
}
-static void do_journal_append(char *file) {
+static int do_journal_append(char *file) {
+ _cleanup_free_ char *bootchart_message = NULL;
+ _cleanup_free_ char *bootchart_file = NULL;
+ _cleanup_free_ char *p = NULL;
+ _cleanup_close_ int fd = -1;
struct iovec iovec[5];
- int r, f, j = 0;
+ int r, 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);
+ if (!bootchart_file)
+ return log_oom();
+ 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);
+ if (!bootchart_message)
+ return log_oom();
- p = malloc(9 + BOOTCHART_MAX);
- if (!p) {
- log_oom();
- return;
- }
+ IOVEC_SET_STRING(iovec[j++], bootchart_message);
+
+ p = malloc(10 + BOOTCHART_MAX);
+ if (!p)
+ return log_oom();
memcpy(p, "BOOTCHART=", 10);
- f = open(file, O_RDONLY|O_CLOEXEC);
- if (f < 0) {
- log_error_errno(errno, "Failed to read bootchart data: %m");
- return;
- }
- n = loop_read(f, p + 10, BOOTCHART_MAX, false);
- if (n < 0) {
- log_error_errno(n, "Failed to read bootchart data: %m");
- close(f);
- return;
- }
- close(f);
+ fd = open(file, O_RDONLY|O_CLOEXEC);
+ if (fd < 0)
+ return log_error_errno(errno, "Failed to open bootchart data \"%s\": %m", file);
+
+ n = loop_read(fd, p + 10, BOOTCHART_MAX, false);
+ if (n < 0)
+ return log_error_errno(n, "Failed to read bootchart data: %m");
iovec[j].iov_base = p;
iovec[j].iov_len = 10 + n;
@@ -314,26 +294,42 @@ static void do_journal_append(char *file) {
r = sd_journal_sendv(iovec, j);
if (r < 0)
log_error_errno(r, "Failed to send bootchart: %m");
+
+ return 0;
}
int main(int argc, char *argv[]) {
+ static struct list_sample_data *sampledata;
+ _cleanup_closedir_ DIR *proc = NULL;
_cleanup_free_ char *build = NULL;
- struct sigaction sig = {
- .sa_handler = signal_handler,
- };
- struct ps_struct *ps;
+ _cleanup_fclose_ FILE *of = NULL;
+ _cleanup_close_ int sysfd = -1;
+ struct ps_struct *ps_first;
+ double graph_start;
+ double log_start;
+ double interval;
char output_file[PATH_MAX];
char datestr[200];
+ int pscount = 0;
+ int n_cpus = 0;
+ int overrun = 0;
time_t t = 0;
- int r;
+ int r, samples;
+ struct ps_struct *ps;
struct rlimit rlim;
- bool has_procfs = false;
+ struct list_sample_data *head;
+ struct sigaction sig = {
+ .sa_handler = signal_handler,
+ };
parse_conf();
r = parse_argv(argc, argv);
- if (r <= 0)
- return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
+ if (r < 0)
+ return EXIT_FAILURE;
+
+ if (r == 0)
+ return EXIT_SUCCESS;
/*
* If the kernel executed us through init=/usr/lib/systemd/systemd-bootchart, then
@@ -365,17 +361,25 @@ int main(int argc, char *argv[]) {
interval = (1.0 / arg_hz) * 1000000000.0;
- log_uptime();
+ if (arg_relative)
+ graph_start = log_start = gettime_ns();
+ else {
+ struct timespec n;
+ double uptime;
- if (graph_start < 0.0) {
- fprintf(stderr,
- "Failed to setup graph start time.\n\nThe system uptime "
- "probably includes time that the system was suspended. "
- "Use --rel to bypass this issue.\n");
- exit (EXIT_FAILURE);
+ clock_gettime(CLOCK_BOOTTIME, &n);
+ uptime = (n.tv_sec + (n.tv_nsec / (double) NSEC_PER_SEC));
+
+ log_start = gettime_ns();
+ graph_start = log_start - uptime;
}
- has_procfs = access("/proc/vmstat", F_OK) == 0;
+ if (graph_start < 0.0) {
+ log_error("Failed to setup graph start time.\n\n"
+ "The system uptime probably includes time that the system was suspended. "
+ "Use --rel to bypass this issue.");
+ return EXIT_FAILURE;
+ }
LIST_HEAD_INIT(head);
@@ -398,15 +402,6 @@ int main(int argc, char *argv[]) {
sampledata->sampletime = gettime_ns();
sampledata->counter = samples;
- if (!of && (access(arg_output_path, R_OK|W_OK|X_OK) == 0)) {
- t = time(NULL);
- r = strftime(datestr, sizeof(datestr), "%Y%m%d-%H%M", localtime(&t));
- assert_se(r > 0);
-
- snprintf(output_file, PATH_MAX, "%s/bootchart-%s.svg", arg_output_path, datestr);
- of = fopen(output_file, "we");
- }
-
if (sysfd < 0)
sysfd = open("/sys", O_RDONLY|O_CLOEXEC);
@@ -415,11 +410,17 @@ int main(int argc, char *argv[]) {
parse_env_file("/usr/lib/os-release", NEWLINE, "PRETTY_NAME", &build, NULL);
}
- if (has_procfs)
- log_sample(samples, &sampledata);
+ if (proc)
+ rewinddir(proc);
else
- /* wait for /proc to become available, discarding samples */
- has_procfs = access("/proc/vmstat", F_OK) == 0;
+ proc = opendir("/proc");
+
+ /* wait for /proc to become available, discarding samples */
+ if (proc) {
+ r = log_sample(proc, samples, ps_first, &sampledata, &pscount, &n_cpus);
+ if (r < 0)
+ return EXIT_FAILURE;
+ }
sample_stop = gettime_ns();
@@ -446,7 +447,7 @@ int main(int argc, char *argv[]) {
break;
}
log_error_errno(errno, "nanosleep() failed: %m");
- exit(EXIT_FAILURE);
+ return EXIT_FAILURE;
}
} else {
overrun++;
@@ -460,12 +461,12 @@ int main(int argc, char *argv[]) {
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)
+ ps->schedstat = safe_close(ps->schedstat);
+ ps->sched = safe_close(ps->sched);
+ if (ps->smaps) {
fclose(ps->smaps);
+ ps->smaps = NULL;
+ }
}
if (!of) {
@@ -478,22 +479,24 @@ int main(int argc, char *argv[]) {
}
if (!of) {
- fprintf(stderr, "opening output file '%s': %m\n", output_file);
- exit (EXIT_FAILURE);
+ log_error("Error opening output file '%s': %m\n", output_file);
+ return EXIT_FAILURE;
}
- svg_do(strna(build));
-
- fprintf(stderr, "systemd-bootchart wrote %s\n", output_file);
+ r = svg_do(of, strna(build), head, ps_first,
+ samples, pscount, n_cpus, graph_start,
+ log_start, interval, overrun);
- do_journal_append(output_file);
+ if (r < 0) {
+ log_error_errno(r, "Error generating svg file: %m");
+ return EXIT_FAILURE;
+ }
- if (of)
- fclose(of);
+ log_info("systemd-bootchart wrote %s\n", output_file);
- closedir(proc);
- if (sysfd >= 0)
- close(sysfd);
+ r = do_journal_append(output_file);
+ if (r < 0)
+ return EXIT_FAILURE;
/* nitpic cleanups */
ps = ps_first->next_ps;
@@ -513,6 +516,7 @@ int main(int argc, char *argv[]) {
free(old->sample);
free(old);
}
+
free(ps->cgroup);
free(ps->sample);
free(ps);
@@ -524,9 +528,10 @@ int main(int argc, char *argv[]) {
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);
+ log_warning("systemd-boochart: sample time overrun %i times\n", overrun);
return 0;
}
diff --git a/src/bootchart/bootchart.conf b/src/bootchart/bootchart.conf
index 2d7cb61217..4f5e50936e 100644
--- a/src/bootchart/bootchart.conf
+++ b/src/bootchart/bootchart.conf
@@ -5,10 +5,11 @@
# the Free Software Foundation; either version 2.1 of the License, or
# (at your option) any later version.
#
-# You can override the directives in this file by creating files in
-# /etc/systemd/bootchart.conf.d/*.conf.
+# Entries in this file show the compile time defaults.
+# You can change settings by editing this file.
+# Defaults can be restored by simply deleting this file.
#
-# See bootchart.conf(5) for details
+# See bootchart.conf(5) for details.
[Bootchart]
#Samples=500
diff --git a/src/bootchart/bootchart.h b/src/bootchart/bootchart.h
index 26de0dd9f8..bdb4b00199 100644
--- a/src/bootchart/bootchart.h
+++ b/src/bootchart/bootchart.h
@@ -24,7 +24,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <dirent.h>
#include <stdbool.h>
#include "list.h"
@@ -104,14 +103,6 @@ struct ps_struct {
struct ps_sched_struct *sample;
};
-extern int entropy_avail[];
-
-extern double graph_start;
-extern double log_start;
-extern double sampletime[];
-extern struct ps_struct *ps_first;
-extern struct block_stat_struct blockstat[];
-extern int pscount;
extern bool arg_relative;
extern bool arg_filter;
extern bool arg_show_cmdline;
@@ -119,18 +110,11 @@ extern bool arg_show_cgroup;
extern bool arg_pss;
extern bool arg_entropy;
extern bool arg_percpu;
-extern bool initcall;
-extern int samples;
-extern int cpus;
-extern int arg_samples_len;
+extern bool arg_initcall;
+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 arg_output_path[PATH_MAX];
extern char arg_init_path[PATH_MAX];
-
-extern FILE *of;
-extern int sysfd;
diff --git a/src/bootchart/store.c b/src/bootchart/store.c
index a66387c304..f159cbafe2 100644
--- a/src/bootchart/store.c
+++ b/src/bootchart/store.c
@@ -25,8 +25,6 @@
#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>
@@ -47,8 +45,6 @@
*/
static char smaps_buf[4096];
static int skip = 0;
-DIR *proc;
-int procfd = -1;
double gettime_ns(void) {
struct timespec n;
@@ -58,24 +54,6 @@ double gettime_ns(void) {
return (n.tv_sec + (n.tv_nsec / (double) NSEC_PER_SEC));
}
-static double gettime_up(void) {
- struct timespec n;
-
- clock_gettime(CLOCK_BOOTTIME, &n);
- return (n.tv_sec + (n.tv_nsec / (double) NSEC_PER_SEC));
-}
-
-void log_uptime(void) {
- if (arg_relative)
- graph_start = log_start = gettime_ns();
- else {
- double uptime = gettime_up();
-
- log_start = gettime_ns();
- graph_start = log_start - uptime;
- }
-}
-
static char *bufgetline(char *buf) {
char *c;
@@ -85,16 +63,17 @@ static char *bufgetline(char *buf) {
c = strchr(buf, '\n');
if (c)
c++;
+
return c;
}
-static int pid_cmdline_strscpy(char *buffer, size_t buf_len, int pid) {
+static int pid_cmdline_strscpy(int procfd, char *buffer, size_t buf_len, int pid) {
char filename[PATH_MAX];
- _cleanup_close_ int fd=-1;
+ _cleanup_close_ int fd = -1;
ssize_t n;
sprintf(filename, "%d/cmdline", pid);
- fd = openat(procfd, filename, O_RDONLY);
+ fd = openat(procfd, filename, O_RDONLY|O_CLOEXEC);
if (fd < 0)
return -errno;
@@ -106,12 +85,19 @@ static int pid_cmdline_strscpy(char *buffer, size_t buf_len, int pid) {
buffer[i] = ' ';
buffer[n] = '\0';
}
+
return 0;
}
-void log_sample(int sample, struct list_sample_data **ptr) {
- static int vmstat;
- static int schedstat;
+int log_sample(DIR *proc,
+ int sample,
+ struct ps_struct *ps_first,
+ struct list_sample_data **ptr,
+ int *pscount,
+ int *cpus) {
+
+ static int vmstat = -1;
+ static int schedstat = -1;
char buf[4096];
char key[256];
char val[256];
@@ -121,41 +107,36 @@ void log_sample(int sample, struct list_sample_data **ptr) {
int c;
int p;
int mod;
- static int e_fd;
+ static int e_fd = -1;
ssize_t s;
ssize_t n;
struct dirent *ent;
int fd;
struct list_sample_data *sampledata;
struct ps_sched_struct *ps_prev = NULL;
+ int procfd;
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);
- }
+ procfd = dirfd(proc);
+ if (procfd < 0)
+ return -errno;
- if (!vmstat) {
+ if (vmstat < 0) {
/* block stuff */
- vmstat = openat(procfd, "vmstat", O_RDONLY);
- if (vmstat == -1) {
- log_error_errno(errno, "Failed to open /proc/vmstat: %m");
- exit(EXIT_FAILURE);
- }
+ vmstat = openat(procfd, "vmstat", O_RDONLY|O_CLOEXEC);
+ if (vmstat < 0)
+ return log_error_errno(errno, "Failed to open /proc/vmstat: %m");
}
n = pread(vmstat, buf, sizeof(buf) - 1, 0);
if (n <= 0) {
- close(vmstat);
- return;
+ vmstat = safe_close(vmstat);
+ if (n < 0)
+ return -errno;
+ return -ENODATA;
}
+
buf[n] = '\0';
m = buf;
@@ -174,20 +155,21 @@ vmstat_next:
break;
}
- if (!schedstat) {
+ if (schedstat < 0) {
/* overall CPU utilization */
- schedstat = openat(procfd, "schedstat", O_RDONLY);
- if (schedstat == -1) {
- log_error_errno(errno, "Failed to open /proc/schedstat: %m");
- exit(EXIT_FAILURE);
- }
+ schedstat = openat(procfd, "schedstat", O_RDONLY|O_CLOEXEC);
+ if (schedstat < 0)
+ return log_error_errno(errno, "Failed to open /proc/schedstat (requires CONFIG_SCHEDSTATS=y in kernel config): %m");
}
n = pread(schedstat, buf, sizeof(buf) - 1, 0);
if (n <= 0) {
- close(schedstat);
- return;
+ schedstat = safe_close(schedstat);
+ if (n < 0)
+ return -errno;
+ return -ENODATA;
}
+
buf[n] = '\0';
m = buf;
@@ -205,8 +187,8 @@ vmstat_next:
sampledata->runtime[c] = atoll(rt);
sampledata->waittime[c] = atoll(wt);
- if (c == cpus)
- cpus = c + 1;
+ if (c == *cpus)
+ *cpus = c + 1;
}
schedstat_next:
m = bufgetline(m);
@@ -215,16 +197,18 @@ schedstat_next:
}
if (arg_entropy) {
- if (!e_fd) {
- e_fd = openat(procfd, "sys/kernel/random/entropy_avail", O_RDONLY);
+ if (e_fd < 0) {
+ e_fd = openat(procfd, "sys/kernel/random/entropy_avail", O_RDONLY|O_CLOEXEC);
+ if (e_fd < 0)
+ return log_error_errno(errno, "Failed to open /proc/sys/kernel/random/entropy_avail: %m");
}
- if (e_fd) {
- n = pread(e_fd, buf, sizeof(buf) - 1, 0);
- if (n > 0) {
- buf[n] = '\0';
- sampledata->entropy_avail = atoi(buf);
- }
+ n = pread(e_fd, buf, sizeof(buf) - 1, 0);
+ if (n <= 0) {
+ e_fd = safe_close(e_fd);
+ } else {
+ buf[n] = '\0';
+ sampledata->entropy_avail = atoi(buf);
}
}
@@ -256,21 +240,21 @@ schedstat_next:
int r;
ps->next_ps = new0(struct ps_struct, 1);
- if (!ps->next_ps) {
- log_oom();
- exit (EXIT_FAILURE);
- }
+ if (!ps->next_ps)
+ return log_oom();
+
ps = ps->next_ps;
ps->pid = pid;
+ ps->sched = -1;
+ ps->schedstat = -1;
ps->sample = new0(struct ps_sched_struct, 1);
- if (!ps->sample) {
- log_oom();
- exit (EXIT_FAILURE);
- }
+ if (!ps->sample)
+ return log_oom();
+
ps->sample->sampledata = sampledata;
- pscount++;
+ (*pscount)++;
/* mark our first sample */
ps->first = ps->last = ps->sample;
@@ -278,16 +262,16 @@ schedstat_next:
ps->sample->waittime = atoll(wt);
/* get name, start time */
- if (!ps->sched) {
+ if (ps->sched < 0) {
sprintf(filename, "%d/sched", pid);
- ps->sched = openat(procfd, filename, O_RDONLY);
- if (ps->sched == -1)
+ ps->sched = openat(procfd, filename, O_RDONLY|O_CLOEXEC);
+ if (ps->sched < 0)
continue;
}
s = pread(ps->sched, buf, sizeof(buf) - 1, 0);
if (s <= 0) {
- close(ps->sched);
+ ps->sched = safe_close(ps->sched);
continue;
}
buf[s] = '\0';
@@ -299,7 +283,7 @@ schedstat_next:
/* cmdline */
if (arg_show_cmdline)
- pid_cmdline_strscpy(ps->name, sizeof(ps->name), pid);
+ pid_cmdline_strscpy(procfd, ps->name, sizeof(ps->name), pid);
/* discard line 2 */
m = bufgetline(buf);
@@ -326,13 +310,19 @@ schedstat_next:
/* ppid */
sprintf(filename, "%d/stat", pid);
- fd = openat(procfd, filename, O_RDONLY);
- st = fdopen(fd, "r");
- if (!st)
+ fd = openat(procfd, filename, O_RDONLY|O_CLOEXEC);
+ if (fd < 0)
continue;
- if (!fscanf(st, "%*s %*s %*s %i", &p)) {
+
+ st = fdopen(fd, "re");
+ if (!st) {
+ close(fd);
continue;
}
+
+ if (!fscanf(st, "%*s %*s %*s %i", &p))
+ continue;
+
ps->ppid = p;
/*
@@ -369,6 +359,7 @@ schedstat_next:
children = parent->children;
while (children->next)
children = children->next;
+
children->next = ps;
}
}
@@ -379,32 +370,31 @@ schedstat_next:
* iteration */
/* rt, wt */
- if (!ps->schedstat) {
+ if (ps->schedstat < 0) {
sprintf(filename, "%d/schedstat", pid);
- ps->schedstat = openat(procfd, filename, O_RDONLY);
- if (ps->schedstat == -1)
+ ps->schedstat = openat(procfd, filename, O_RDONLY|O_CLOEXEC);
+ if (ps->schedstat < 0)
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);
+ ps->schedstat = -1;
+ ps->sched = safe_close(ps->sched);
continue;
}
+
buf[s] = '\0';
if (!sscanf(buf, "%s %s %*s", rt, wt))
continue;
ps->sample->next = new0(struct ps_sched_struct, 1);
- if (!ps->sample->next) {
- log_oom();
- exit(EXIT_FAILURE);
- }
+ if (!ps->sample->next)
+ return log_oom();
+
ps->sample->next->prev = ps->sample;
ps->sample = ps->sample->next;
ps->last = ps->sample;
@@ -412,9 +402,9 @@ schedstat_next:
ps->sample->waittime = atoll(wt);
ps->sample->sampledata = sampledata;
ps->sample->ps_new = ps;
- if (ps_prev) {
+ if (ps_prev)
ps_prev->cross = ps->sample;
- }
+
ps_prev = ps->sample;
ps->total = (ps->last->runtime - ps->first->runtime)
/ 1000000000.0;
@@ -425,15 +415,19 @@ schedstat_next:
/* Pss */
if (!ps->smaps) {
sprintf(filename, "%d/smaps", pid);
- fd = openat(procfd, filename, O_RDONLY);
- ps->smaps = fdopen(fd, "r");
- if (!ps->smaps)
+ fd = openat(procfd, filename, O_RDONLY|O_CLOEXEC);
+ if (fd < 0)
+ continue;
+ ps->smaps = fdopen(fd, "re");
+ if (!ps->smaps) {
+ close(fd);
continue;
+ }
setvbuf(ps->smaps, smaps_buf, _IOFBF, sizeof(smaps_buf));
- }
- else {
+ } 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) {
@@ -450,6 +444,7 @@ schedstat_next:
}
rewind(ps->smaps);
}
+
while (1) {
int pss_kb;
@@ -470,32 +465,32 @@ schedstat_next:
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) {
+ if (((sample - ps->pid) + pid) % (int)(mod) == 0) {
/* re-fetch name */
/* get name, start time */
- if (!ps->sched) {
+ if (ps->sched < 0) {
sprintf(filename, "%d/sched", pid);
- ps->sched = openat(procfd, filename, O_RDONLY);
- if (ps->sched == -1)
+ ps->sched = openat(procfd, filename, O_RDONLY|O_CLOEXEC);
+ if (ps->sched < 0)
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);
+ ps->sched = safe_close(ps->sched);
+ ps->schedstat = safe_close(ps->schedstat);
continue;
}
+
buf[s] = '\0';
if (!sscanf(buf, "%s %*s %*s", key))
@@ -505,7 +500,9 @@ catch_rename:
/* cmdline */
if (arg_show_cmdline)
- pid_cmdline_strscpy(ps->name, sizeof(ps->name), pid);
+ pid_cmdline_strscpy(procfd, ps->name, sizeof(ps->name), pid);
}
}
+
+ return 0;
}
diff --git a/src/bootchart/store.h b/src/bootchart/store.h
index f211b6f53b..bbb4796efd 100644
--- a/src/bootchart/store.h
+++ b/src/bootchart/store.h
@@ -27,9 +27,11 @@
#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);
+int log_sample(DIR *proc,
+ int sample,
+ struct ps_struct *ps_first,
+ struct list_sample_data **ptr,
+ int *pscount,
+ int *cpus);
diff --git a/src/bootchart/svg.c b/src/bootchart/svg.c
index e111fa9cce..0132475e10 100644
--- a/src/bootchart/svg.c
+++ b/src/bootchart/svg.c
@@ -23,17 +23,15 @@
***/
#include <stdio.h>
-#include <stdarg.h>
-#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <limits.h>
#include <unistd.h>
#include <sys/utsname.h>
-#include <sys/stat.h>
#include <fcntl.h>
#include "util.h"
+#include "fileio.h"
#include "macro.h"
#include "store.h"
#include "svg.h"
@@ -46,10 +44,6 @@
#define kb_to_graph(m) ((m) * arg_scale_y * 0.0001)
#define to_color(n) (192.0 - ((n) * 192.0))
-static char str[8092];
-
-#define svg(a...) do { snprintf(str, 8092, ## a); fputs(str, of); fflush(of); } while (0)
-
static const char * const colorwheel[12] = {
"rgb(255,32,32)", // red
"rgb(32,192,192)", // cyan
@@ -74,9 +68,8 @@ static double ksize = 0;
static double 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) {
+static void svg_header(FILE *of, struct list_sample_data *head, double graph_start) {
double w;
double h;
struct list_sample_data *sampledata_last;
@@ -99,101 +92,97 @@ static void svg_header(void) {
+ (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");
+ fprintf(of, "<?xml version=\"1.0\" standalone=\"no\"?>\n");
+ fprintf(of, "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" ");
+ fprintf(of, "\"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");
+ //fprintf(of, "<g transform=\"translate(10,%d)\">\n", 1000 + 150 + (pcount * 20));
+ fprintf(of, "<svg width=\"%.0fpx\" height=\"%.0fpx\" version=\"1.1\" ", w, h);
+ fprintf(of, "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);
+ fprintf(of, "<!-- This file is a bootchart SVG file. It is best rendered in a browser -->\n");
+ fprintf(of, "<!-- such as Chrome, Chromium, or Firefox. Other applications that -->\n");
+ fprintf(of, "<!-- render these files properly but more slowly are ImageMagick, gimp, -->\n");
+ fprintf(of, "<!-- inkscape, etc. To display the files on your system, just point -->\n");
+ fprintf(of, "<!-- your browser to file:///run/log/ and click. This bootchart was -->\n\n");
+
+ fprintf(of, "<!-- generated by bootchart version %s, running with options: -->\n", VERSION);
+ fprintf(of, "<!-- hz=\"%f\" n=\"%d\" -->\n", arg_hz, arg_samples_len);
+ fprintf(of, "<!-- x=\"%f\" y=\"%f\" -->\n", arg_scale_x, arg_scale_y);
+ fprintf(of, "<!-- rel=\"%d\" f=\"%d\" -->\n", arg_relative, arg_filter);
+ fprintf(of, "<!-- p=\"%d\" e=\"%d\" -->\n", arg_pss, arg_entropy);
+ fprintf(of, "<!-- 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.bg { fill: rgb(255,255,255); }\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");
+ fprintf(of, "<defs>\n <style type=\"text/css\">\n <![CDATA[\n");
+
+ fprintf(of, " rect { stroke-width: 1; }\n");
+ fprintf(of, " rect.bg { fill: rgb(255,255,255); }\n");
+ fprintf(of, " rect.cpu { fill: rgb(64,64,240); stroke-width: 0; fill-opacity: 0.7; }\n");
+ fprintf(of, " rect.wait { fill: rgb(240,240,0); stroke-width: 0; fill-opacity: 0.7; }\n");
+ fprintf(of, " rect.bi { fill: rgb(240,128,128); stroke-width: 0; fill-opacity: 0.7; }\n");
+ fprintf(of, " rect.bo { fill: rgb(192,64,64); stroke-width: 0; fill-opacity: 0.7; }\n");
+ fprintf(of, " rect.ps { fill: rgb(192,192,192); stroke: rgb(128,128,128); fill-opacity: 0.7; }\n");
+ fprintf(of, " rect.krnl { fill: rgb(240,240,0); stroke: rgb(128,128,128); fill-opacity: 0.7; }\n");
+ fprintf(of, " rect.box { fill: rgb(240,240,240); stroke: rgb(192,192,192); }\n");
+ fprintf(of, " rect.clrw { stroke-width: 0; fill-opacity: 0.7;}\n");
+ fprintf(of, " line { stroke: rgb(64,64,64); stroke-width: 1; }\n");
+ fprintf(of, "// line.sec1 { }\n");
+ fprintf(of, " line.sec5 { stroke-width: 2; }\n");
+ fprintf(of, " line.sec01 { stroke: rgb(224,224,224); stroke-width: 1; }\n");
+ fprintf(of, " line.dot { stroke-dasharray: 2 4; }\n");
+ fprintf(of, " line.idle { stroke: rgb(64,64,64); stroke-dasharray: 10 6; stroke-opacity: 0.7; }\n");
+
+ fprintf(of, " .run { font-size: 8; font-style: italic; }\n");
+ fprintf(of, " text { font-family: Verdana, Helvetica; font-size: 10; }\n");
+ fprintf(of, " text.sec { font-size: 8; }\n");
+ fprintf(of, " text.t1 { font-size: 24; }\n");
+ fprintf(of, " text.t2 { font-size: 12; }\n");
+ fprintf(of, " text.idle { font-size: 18; }\n");
+
+ fprintf(of, " ]]>\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";
+static int svg_title(FILE *of, const char *build, int pscount, double log_start, int overrun) {
+ _cleanup_free_ char *cmdline = NULL;
+ _cleanup_free_ char *model = NULL;
+ _cleanup_free_ char *buf = NULL;
char date[256] = "Unknown";
- char cpu[256] = "Unknown";
+ char *cpu;
char *c;
- FILE *f;
time_t t;
- int fd, r;
+ int r;
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);
+ r = read_one_line_file("/proc/cmdline", &cmdline);
+ if (r < 0) {
+ log_error_errno(r, "Unable to read cmdline: %m");
+ return r;
}
/* 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);
+ char rootbdev[4];
+ char filename[32];
+
+ strncpy(rootbdev, &c[10], sizeof(rootbdev) - 1);
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);
- }
+ snprintf(filename, sizeof(filename), "/sys/block/%s/device/model", rootbdev);
+
+ r = read_one_line_file(filename, &model);
+ if (r < 0)
+ log_warning("Error reading disk model for %s: %m\n", rootbdev);
}
/* various utsname parameters */
- if (uname(&uts))
- fprintf(stderr, "Error getting uname info\n");
+ r = uname(&uts);
+ if (r < 0) {
+ log_error("Error getting uname info\n");
+ return -errno;
+ }
/* date */
t = time(NULL);
@@ -201,43 +190,45 @@ static void svg_title(const char *build) {
assert_se(r > 0);
/* 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);
+ r = read_full_file("/proc/cpuinfo", &buf, NULL);
+ if (r < 0)
+ return log_error_errno(r, "Unable to read cpuinfo: %m");
+
+ cpu = strstr(buf, "model name");
+ if (!cpu) {
+ log_error("Unable to read module name from cpuinfo.\n");
+ return -ENOENT;
}
- 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: ");
+ cpu += 13;
+ c = strchr(cpu, '\n');
+ if (c)
+ *c = '\0';
+
+ fprintf(of, "<text class=\"t1\" x=\"0\" y=\"30\">Bootchart for %s - %s</text>\n",
+ uts.nodename, date);
+ fprintf(of, "<text class=\"t2\" x=\"20\" y=\"50\">System: %s %s %s %s</text>\n",
+ uts.sysname, uts.release, uts.version, uts.machine);
+ fprintf(of, "<text class=\"t2\" x=\"20\" y=\"65\">CPU: %s</text>\n", cpu);
+ fprintf(of, "<text class=\"t2\" x=\"20\" y=\"80\">Disk: %s</text>\n", model);
+ fprintf(of, "<text class=\"t2\" x=\"20\" y=\"95\">Boot options: %s</text>\n", cmdline);
+ fprintf(of, "<text class=\"t2\" x=\"20\" y=\"110\">Build: %s</text>\n", build);
+ fprintf(of, "<text class=\"t2\" x=\"20\" y=\"125\">Log start time: %.03fs</text>\n", log_start);
+ fprintf(of, "<text class=\"t2\" x=\"20\" y=\"140\">Idle time: ");
if (idletime >= 0.0)
- svg("%.03fs", idletime);
+ fprintf(of, "%.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);
+ fprintf(of, "Not detected");
+
+ fprintf(of, "</text>\n");
+ fprintf(of, "<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);
+
+ return 0;
}
-static void svg_graph_box(int height) {
+static void svg_graph_box(FILE *of, struct list_sample_data *head, int height, double graph_start) {
double d = 0.0;
int i = 0;
double finalsample = 0.0;
@@ -251,36 +242,35 @@ static void svg_graph_box(int height) {
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));
+ fprintf(of, "<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));
+ fprintf(of, " <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));
+ fprintf(of, " <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));
+ fprintf(of, " <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);
+ fprintf(of, " <text class=\"sec\" x=\"%.03f\" y=\"%.03f\" >%.01fs</text>\n",
+ time_to_graph(d - graph_start),
+ -5.0, d - graph_start);
i++;
}
@@ -301,7 +291,10 @@ static char* xml_comment_encode(const char* name) {
return enc_name;
}
-static void svg_pss_graph(void) {
+static void svg_pss_graph(FILE *of,
+ struct list_sample_data *head,
+ struct ps_struct *ps_first,
+ double graph_start) {
struct ps_struct *ps;
int i;
struct list_sample_data *sampledata_last;
@@ -312,24 +305,24 @@ static void svg_pss_graph(void) {
}
- svg("\n\n<!-- Pss memory size graph -->\n");
+ fprintf(of, "\n\n<!-- Pss memory size graph -->\n");
- svg("\n <text class=\"t2\" x=\"5\" y=\"-15\">Memory allocation - Pss</text>\n");
+ fprintf(of, "\n <text class=\"t2\" x=\"5\" y=\"-15\">Memory allocation - Pss</text>\n");
/* vsize 1000 == 1000mb */
- svg_graph_box(100);
+ svg_graph_box(of, head, 100, graph_start);
/* 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",
+ fprintf(of, " <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);
+ fprintf(of, " <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");
+ fprintf(of, "\n");
/* now plot the graph itself */
i = 1;
@@ -368,12 +361,12 @@ static void svg_pss_graph(void) {
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));
+ fprintf(of, " <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 */
@@ -389,34 +382,36 @@ static void svg_pss_graph(void) {
break;
}
/* don't draw anything smaller than 2mb */
- if (ps->sample->sampledata == sampledata) {
- if (ps->sample->pss > (100 * arg_scale_y)) {
+ if (ps->sample->sampledata != sampledata)
+ continue;
+ 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));
+ fprintf(of, " <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;
}
+ 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));
+ fprintf(of, " <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++;
}
@@ -435,18 +430,22 @@ static void svg_pss_graph(void) {
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;
@@ -475,11 +474,10 @@ static void svg_pss_graph(void) {
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);
+ fprintf(of, " <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;
@@ -493,19 +491,19 @@ static void svg_pss_graph(void) {
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);
+ fprintf(of, " <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");
+ fprintf(of, "\n\n<!-- PSS map - csv format -->\n");
ps = ps_first;
while (ps->next_ps) {
_cleanup_free_ char *enc_name = NULL;
@@ -517,19 +515,25 @@ static void svg_pss_graph(void) {
if (!enc_name)
continue;
- svg("<!-- %s [%d] pss=", enc_name, ps->pid);
+ fprintf(of, "<!-- %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);
+ fprintf(of, "%d," , ps->sample->pss);
}
- svg(" -->\n");
+
+ fprintf(of, " -->\n");
}
}
-static void svg_io_bi_bar(void) {
+static void svg_io_bi_bar(FILE *of,
+ struct list_sample_data *head,
+ int n_samples,
+ double graph_start,
+ double interval) {
+
double max = 0.0;
double range;
int max_here = 0;
@@ -538,9 +542,8 @@ static void svg_io_bi_bar(void) {
struct list_sample_data *start_sampledata;
struct list_sample_data *stop_sampledata;
- svg("<!-- IO utilization graph - In -->\n");
-
- svg("<text class=\"t2\" x=\"5\" y=\"-15\">IO utilization - read</text>\n");
+ fprintf(of, "<!-- IO utilization graph - In -->\n");
+ fprintf(of, "<text class=\"t2\" x=\"5\" y=\"-15\">IO utilization - read</text>\n");
/*
* calculate rounding range
@@ -554,7 +557,7 @@ static void svg_io_bi_bar(void) {
range = 2.0; /* no smoothing */
/* surrounding box */
- svg_graph_box(5);
+ svg_graph_box(of, head, 5, graph_start);
/* find the max IO first */
i = 1;
@@ -565,27 +568,26 @@ static void svg_io_bi_bar(void) {
double tot;
start = MAX(i - ((range / 2) - 1), 0);
- stop = MIN(i + (range / 2), samples - 1);
+ stop = MIN(i + (range / 2), n_samples - 1);
diff = (stop - start);
start_sampledata = sampledata;
stop_sampledata = sampledata;
- for (k=0;(k<((range/2)-1))&&(start_sampledata->link_next);k++)
+ 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++)
+
+ 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;
+ 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;
+ tot = (double)(stop_sampledata->blockstat.bo - start_sampledata->blockstat.bo) / diff;
if (tot > max)
max = tot;
@@ -604,43 +606,48 @@ static void svg_io_bi_bar(void) {
double pbi = 0;
start = MAX(i - ((range / 2) - 1), 0);
- stop = MIN(i + (range / 2), samples);
+ stop = MIN(i + (range / 2), n_samples);
diff = (stop - start);
start_sampledata = sampledata;
stop_sampledata = sampledata;
- for (k=0;(k<((range/2)-1))&&(start_sampledata->link_next);k++)
+ 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++)
+
+ 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;
+ 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));
+ fprintf(of, "<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));
+ fprintf(of, " <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) {
+static void svg_io_bo_bar(FILE *of,
+ struct list_sample_data *head,
+ int n_samples,
+ double graph_start,
+ double interval) {
double max = 0.0;
double range;
int max_here = 0;
@@ -649,9 +656,8 @@ static void svg_io_bo_bar(void) {
struct list_sample_data *start_sampledata;
struct list_sample_data *stop_sampledata;
- svg("<!-- IO utilization graph - out -->\n");
-
- svg("<text class=\"t2\" x=\"5\" y=\"-15\">IO utilization - write</text>\n");
+ fprintf(of, "<!-- IO utilization graph - out -->\n");
+ fprintf(of, "<text class=\"t2\" x=\"5\" y=\"-15\">IO utilization - write</text>\n");
/*
* calculate rounding range
@@ -665,7 +671,7 @@ static void svg_io_bo_bar(void) {
range = 2.0; /* no smoothing */
/* surrounding box */
- svg_graph_box(5);
+ svg_graph_box(of, head, 5, graph_start);
/* find the max IO first */
i = 0;
@@ -676,52 +682,52 @@ static void svg_io_bo_bar(void) {
double tot;
start = MAX(i - ((range / 2) - 1), 0);
- stop = MIN(i + (range / 2), samples - 1);
+ stop = MIN(i + (range / 2), n_samples - 1);
diff = (stop - start);
start_sampledata = sampledata;
stop_sampledata = sampledata;
- for (k=0;(k<((range/2)-1))&&(start_sampledata->link_next);k++)
+ 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++)
+
+ 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;
+ 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;
+
+ 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;
+ i = 1;
+
LIST_FOREACH_BEFORE(link, sampledata, head) {
- int start;
- int stop;
- int diff;
- double tot;
- double pbo;
+ int start, stop, diff;
+ double tot, pbo;
pbo = 0;
start = MAX(i - ((range / 2) - 1), 0);
- stop = MIN(i + (range / 2), samples);
+ stop = MIN(i + (range / 2), n_samples);
diff = (stop - start);
start_sampledata = sampledata;
stop_sampledata = sampledata;
- for (k=0;(k<((range/2)-1))&&(start_sampledata->link_next);k++)
+ 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++)
+
+ 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)
@@ -731,34 +737,36 @@ static void svg_io_bo_bar(void) {
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));
+ fprintf(of, "<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));
+ fprintf(of, " <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(int cpu_num) {
+static void svg_cpu_bar(FILE *of, struct list_sample_data *head, int n_cpus, int cpu_num, double graph_start) {
- svg("<!-- CPU utilization graph -->\n");
+ fprintf(of, "<!-- CPU utilization graph -->\n");
if (cpu_num < 0)
- svg("<text class=\"t2\" x=\"5\" y=\"-15\">CPU[overall] utilization</text>\n");
+ fprintf(of, "<text class=\"t2\" x=\"5\" y=\"-15\">CPU[overall] utilization</text>\n");
else
- svg("<text class=\"t2\" x=\"5\" y=\"-15\">CPU[%d] utilization</text>\n", cpu_num);
+ fprintf(of, "<text class=\"t2\" x=\"5\" y=\"-15\">CPU[%d] utilization</text>\n", cpu_num);
+
/* surrounding box */
- svg_graph_box(5);
+ svg_graph_box(of, head, 5, graph_start);
/* bars for each sample, proportional to the CPU util. */
prev_sampledata = head;
@@ -770,7 +778,7 @@ static void svg_cpu_bar(int cpu_num) {
ptrt = trt = 0.0;
if (cpu_num < 0)
- for (c = 0; c < cpus; c++)
+ for (c = 0; c < n_cpus; c++)
trt += sampledata->runtime[c] - prev_sampledata->runtime[c];
else
trt = sampledata->runtime[cpu_num] - prev_sampledata->runtime[cpu_num];
@@ -778,7 +786,7 @@ static void svg_cpu_bar(int cpu_num) {
trt = trt / 1000000000.0;
if (cpu_num < 0)
- trt = trt / (double)cpus;
+ trt = trt / (double)n_cpus;
if (trt > 0.0)
ptrt = trt / (sampledata->sampletime - prev_sampledata->sampletime);
@@ -786,28 +794,28 @@ static void svg_cpu_bar(int cpu_num) {
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(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));
- }
+ if (ptrt > 0.001)
+ fprintf(of, "<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(int cpu_num) {
+static void svg_wait_bar(FILE *of, struct list_sample_data *head, int n_cpus, int cpu_num, double graph_start) {
- svg("<!-- Wait time aggregation box -->\n");
+ fprintf(of, "<!-- Wait time aggregation box -->\n");
if (cpu_num < 0)
- svg("<text class=\"t2\" x=\"5\" y=\"-15\">CPU[overall] wait</text>\n");
+ fprintf(of, "<text class=\"t2\" x=\"5\" y=\"-15\">CPU[overall] wait</text>\n");
else
- svg("<text class=\"t2\" x=\"5\" y=\"-15\">CPU[%d] wait</text>\n", cpu_num);
+ fprintf(of, "<text class=\"t2\" x=\"5\" y=\"-15\">CPU[%d] wait</text>\n", cpu_num);
/* surrounding box */
- svg_graph_box(5);
+ svg_graph_box(of, head, 5, graph_start);
/* bars for each sample, proportional to the CPU util. */
prev_sampledata = head;
@@ -819,7 +827,7 @@ static void svg_wait_bar(int cpu_num) {
ptwt = twt = 0.0;
if (cpu_num < 0)
- for (c = 0; c < cpus; c++)
+ for (c = 0; c < n_cpus; c++)
twt += sampledata->waittime[c] - prev_sampledata->waittime[c];
else
twt = sampledata->waittime[cpu_num] - prev_sampledata->waittime[cpu_num];
@@ -827,7 +835,7 @@ static void svg_wait_bar(int cpu_num) {
twt = twt / 1000000000.0;
if (cpu_num < 0)
- twt = twt / (double)cpus;
+ twt = twt / (double)n_cpus;
if (twt > 0.0)
ptwt = twt / (sampledata->sampletime - prev_sampledata->sampletime);
@@ -835,39 +843,38 @@ static void svg_wait_bar(int cpu_num) {
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(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));
- }
+ if (ptwt > 0.001)
+ fprintf(of, "<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) {
+static void svg_entropy_bar(FILE *of, struct list_sample_data *head, double graph_start) {
- svg("<!-- entropy pool graph -->\n");
+ fprintf(of, "<!-- entropy pool graph -->\n");
- svg("<text class=\"t2\" x=\"5\" y=\"-15\">Entropy pool size</text>\n");
+ fprintf(of, "<text class=\"t2\" x=\"5\" y=\"-15\">Entropy pool size</text>\n");
/* surrounding box */
- svg_graph_box(5);
+ svg_graph_box(of, head, 5, graph_start);
/* 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));
+ fprintf(of, "<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) {
+static struct ps_struct *get_next_ps(struct ps_struct *ps, struct ps_struct *ps_first) {
/*
* walk the list of processes and return the next one to be
* painted
@@ -885,9 +892,9 @@ static struct ps_struct *get_next_ps(struct ps_struct *ps) {
/* go back for parent siblings */
while (1) {
- if (ps->parent)
- if (ps->parent->next)
- return ps->parent->next;
+ if (ps->parent && ps->parent->next)
+ return ps->parent->next;
+
ps = ps->parent;
if (!ps)
return ps;
@@ -915,7 +922,7 @@ static bool ps_filter(struct ps_struct *ps) {
return 0;
}
-static void svg_do_initcall(int count_only) {
+static void svg_do_initcall(FILE *of, struct list_sample_data *head, int count_only, double graph_start) {
_cleanup_pclose_ FILE *f = NULL;
double t;
char func[256];
@@ -923,17 +930,16 @@ static void svg_do_initcall(int count_only) {
int usecs;
/* can't plot initcall when disabled or in relative mode */
- if (!initcall || arg_relative) {
+ if (!arg_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");
+ fprintf(of, "<!-- initcall -->\n");
+ fprintf(of, "<text class=\"t2\" x=\"5\" y=\"-15\">Kernel init threads</text>\n");
/* surrounding box */
- svg_graph_box(kcount);
+ svg_graph_box(of, head, kcount, graph_start);
}
kcount = 0;
@@ -979,47 +985,52 @@ static void svg_do_initcall(int count_only) {
continue;
}
- svg("<!-- thread=\"%s\" time=\"%.3f\" elapsed=\"%d\" result=\"%d\" -->\n",
- func, t, usecs, ret);
+ fprintf(of, "<!-- 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));
+ fprintf(of, " <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);
+ fprintf(of, " <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) {
+static void svg_ps_bars(FILE *of,
+ struct list_sample_data *head,
+ int n_samples,
+ int n_cpus,
+ struct ps_struct *ps_first,
+ double graph_start,
+ double interval) {
+
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");
+ fprintf(of, "<!-- Process graph -->\n");
+ fprintf(of, "<text class=\"t2\" x=\"5\" y=\"-15\">Processes</text>\n");
/* surrounding box */
- svg_graph_box(pcount);
+ svg_graph_box(of, head, pcount, graph_start);
/* pass 2 - ps boxes */
ps = ps_first;
- while ((ps = get_next_ps(ps))) {
+ while ((ps = get_next_ps(ps, ps_first))) {
_cleanup_free_ char *enc_name = NULL, *escaped = NULL;
double endtime;
double starttime;
@@ -1033,8 +1044,8 @@ static void svg_ps_bars(void) {
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);
+ fprintf(of, "<!-- %s [%i] ppid=%i runtime=%.03fs -->\n", enc_name, ps->pid,
+ ps->ppid, ps->total);
starttime = ps->first->sampledata->sampletime;
@@ -1049,20 +1060,20 @@ static void svg_ps_bars(void) {
/* 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);
+ fprintf(of, " <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));
+ fprintf(of, " <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;
@@ -1091,18 +1102,18 @@ static void svg_ps_bars(void) {
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));
+ fprintf(of, " <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));
+ fprintf(of, " <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++;
}
@@ -1114,34 +1125,34 @@ static void svg_ps_bars(void) {
w = starttime;
/* text label of process name */
- svg(" <text x=\"%.03f\" y=\"%.03f\"><![CDATA[%s]]> [%i]<tspan class=\"run\">%.03fs</tspan> %s</text>\n",
- time_to_graph(w - graph_start) + 5.0,
- ps_to_graph(j) + 14.0,
- escaped ? escaped : ps->name,
- ps->pid,
- (ps->last->runtime - ps->first->runtime) / 1000000000.0,
- arg_show_cgroup ? ps->cgroup : "");
+ fprintf(of, " <text x=\"%.03f\" y=\"%.03f\"><![CDATA[%s]]> [%i]<tspan class=\"run\">%.03fs</tspan> %s</text>\n",
+ time_to_graph(w - graph_start) + 5.0,
+ ps_to_graph(j) + 14.0,
+ escaped ? escaped : ps->name,
+ ps->pid,
+ (ps->last->runtime - ps->first->runtime) / 1000000000.0,
+ arg_show_cgroup ? ps->cgroup : "");
/* 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);
+ fprintf(of, " <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);
+ fprintf(of, " <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");
+ fprintf(of, "\n");
}
/* last pass - determine when idle */
@@ -1161,7 +1172,7 @@ static void svg_ps_bars(void) {
ps->sample = ps->first;
i = ps->sample->next->sampledata->counter;
- while (ps->sample->next && i<(samples-(arg_hz/2))) {
+ while (ps->sample->next && i<(n_samples-(arg_hz/2))) {
double crt;
double brt;
int c;
@@ -1170,13 +1181,14 @@ static void svg_ps_bars(void) {
ps->sample = ps->sample->next;
sample_hz = ps->sample;
- for (ii=0;((ii<(int)arg_hz/2)&&(ps->sample->next));ii++)
+ for (ii = 0; (ii < (int)arg_hz/2) && sample_hz->next; ii++)
sample_hz = sample_hz->next;
/* subtract bootchart cpu utilization from total */
crt = 0.0;
- for (c = 0; c < cpus; c++)
+ for (c = 0; c < n_cpus; c++)
crt += sample_hz->sampledata->runtime[c] - ps->sample->sampledata->runtime[c];
+
brt = sample_hz->runtime - ps->sample->runtime;
/*
* our definition of "idle":
@@ -1186,24 +1198,24 @@ static void svg_ps_bars(void) {
*/
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);
+ fprintf(of, "\n<!-- idle detected at %.03f seconds -->\n", idletime);
+ fprintf(of, "<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);
+ fprintf(of, "<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) {
+static void svg_top_ten_cpu(FILE *of, struct ps_struct *ps_first) {
struct ps_struct *top[10];
struct ps_struct emptyps = {};
struct ps_struct *ps;
@@ -1214,7 +1226,7 @@ static void svg_top_ten_cpu(void) {
/* walk all ps's and setup ptrs */
ps = ps_first;
- while ((ps = get_next_ps(ps))) {
+ while ((ps = get_next_ps(ps, ps_first))) {
for (n = 0; n < 10; n++) {
if (ps->total <= top[n]->total)
continue;
@@ -1226,16 +1238,16 @@ static void svg_top_ten_cpu(void) {
}
}
- svg("<text class=\"t2\" x=\"20\" y=\"0\">Top CPU consumers:</text>\n");
+ fprintf(of, "<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);
+ fprintf(of, "<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) {
+static void svg_top_ten_pss(FILE *of, struct ps_struct *ps_first) {
struct ps_struct *top[10];
struct ps_struct emptyps = {};
struct ps_struct *ps;
@@ -1246,10 +1258,11 @@ static void svg_top_ten_pss(void) {
/* walk all ps's and setup ptrs */
ps = ps_first;
- while ((ps = get_next_ps(ps))) {
+ while ((ps = get_next_ps(ps, ps_first))) {
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];
@@ -1258,30 +1271,39 @@ static void svg_top_ten_pss(void) {
}
}
- svg("<text class=\"t2\" x=\"20\" y=\"0\">Top PSS consumers:</text>\n");
+ fprintf(of, "<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);
+ fprintf(of, "<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) {
+int svg_do(FILE *of,
+ const char *build,
+ struct list_sample_data *head,
+ struct ps_struct *ps_first,
+ int n_samples,
+ int pscount,
+ int n_cpus,
+ double graph_start,
+ double log_start,
+ double interval,
+ int overrun) {
+
struct ps_struct *ps;
double offset = 7;
- int c;
-
- memzero(&str, sizeof(str));
+ int r, c;
ps = ps_first;
/* count initcall thread count first */
- svg_do_initcall(1);
- ksize = (kcount ? ps_to_graph(kcount) + (arg_scale_y * 2) : 0);
+ svg_do_initcall(of, head, 1, graph_start);
+ ksize = kcount ? ps_to_graph(kcount) + (arg_scale_y * 2) : 0;
/* then count processes */
- while ((ps = get_next_ps(ps))) {
+ while ((ps = get_next_ps(ps, ps_first))) {
if (!ps_filter(ps))
pcount++;
else
@@ -1292,65 +1314,70 @@ void svg_do(const char *build) {
esize = (arg_entropy ? arg_scale_y * 7 : 0);
/* after this, we can draw the header with proper sizing */
- svg_header();
- svg("<rect class=\"bg\" width=\"100%%\" height=\"100%%\" />\n\n");
+ svg_header(of, head, graph_start);
+ fprintf(of, "<rect class=\"bg\" width=\"100%%\" height=\"100%%\" />\n\n");
- svg("<g transform=\"translate(10,400)\">\n");
- svg_io_bi_bar();
- svg("</g>\n\n");
+ fprintf(of, "<g transform=\"translate(10,400)\">\n");
+ svg_io_bi_bar(of, head, n_samples, graph_start, interval);
+ fprintf(of, "</g>\n\n");
- svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * offset));
- svg_io_bo_bar();
- svg("</g>\n\n");
+ fprintf(of, "<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * offset));
+ svg_io_bo_bar(of, head, n_samples, graph_start, interval);
+ fprintf(of, "</g>\n\n");
- for (c = -1; c < (arg_percpu ? cpus : 0); c++) {
+ for (c = -1; c < (arg_percpu ? n_cpus : 0); c++) {
offset += 7;
- svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * offset));
- svg_cpu_bar(c);
- svg("</g>\n\n");
+ fprintf(of, "<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * offset));
+ svg_cpu_bar(of, head, n_cpus, c, graph_start);
+ fprintf(of, "</g>\n\n");
offset += 7;
- svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * offset));
- svg_wait_bar(c);
- svg("</g>\n\n");
+ fprintf(of, "<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * offset));
+ svg_wait_bar(of, head, n_cpus, c, graph_start);
+ fprintf(of, "</g>\n\n");
}
if (kcount) {
offset += 7;
- svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * offset));
- svg_do_initcall(0);
- svg("</g>\n\n");
+ fprintf(of, "<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * offset));
+ svg_do_initcall(of, head, 0, graph_start);
+ fprintf(of, "</g>\n\n");
}
offset += 7;
- svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * offset) + ksize);
- svg_ps_bars();
- svg("</g>\n\n");
+ fprintf(of, "<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * offset) + ksize);
+ svg_ps_bars(of, head, n_samples, n_cpus, ps_first, graph_start, interval);
+ fprintf(of, "</g>\n\n");
+
+ fprintf(of, "<g transform=\"translate(10, 0)\">\n");
+ r = svg_title(of, build, pscount, log_start, overrun);
+ fprintf(of, "</g>\n\n");
- svg("<g transform=\"translate(10, 0)\">\n");
- svg_title(build);
- svg("</g>\n\n");
+ if (r < 0)
+ return r;
- svg("<g transform=\"translate(10,200)\">\n");
- svg_top_ten_cpu();
- svg("</g>\n\n");
+ fprintf(of, "<g transform=\"translate(10,200)\">\n");
+ svg_top_ten_cpu(of, ps_first);
+ fprintf(of, "</g>\n\n");
if (arg_entropy) {
- svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * offset) + ksize + psize);
- svg_entropy_bar();
- svg("</g>\n\n");
+ fprintf(of, "<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * offset) + ksize + psize);
+ svg_entropy_bar(of, head, graph_start);
+ fprintf(of, "</g>\n\n");
}
if (arg_pss) {
- svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * offset) + ksize + psize + esize);
- svg_pss_graph();
- svg("</g>\n\n");
+ fprintf(of, "<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * offset) + ksize + psize + esize);
+ svg_pss_graph(of, head, ps_first, graph_start);
+ fprintf(of, "</g>\n\n");
- svg("<g transform=\"translate(410,200)\">\n");
- svg_top_ten_pss();
- svg("</g>\n\n");
+ fprintf(of, "<g transform=\"translate(410,200)\">\n");
+ svg_top_ten_pss(of, ps_first);
+ fprintf(of, "</g>\n\n");
}
- /* svg footer */
- svg("\n</svg>\n");
+ /* fprintf footer */
+ fprintf(of, "\n</svg>\n");
+
+ return 0;
}
diff --git a/src/bootchart/svg.h b/src/bootchart/svg.h
index df3a7bf8ef..75efa22614 100644
--- a/src/bootchart/svg.h
+++ b/src/bootchart/svg.h
@@ -24,4 +24,14 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-void svg_do(const char *build);
+int svg_do(FILE *of,
+ const char *build,
+ struct list_sample_data *head,
+ struct ps_struct *ps_first,
+ int n_samples,
+ int pscount,
+ int n_cpus,
+ double graph_start,
+ double log_start,
+ double interval,
+ int overrun);
diff --git a/src/bus-proxyd/bus-proxyd.c b/src/bus-proxyd/bus-proxyd.c
index b6b0056362..3e398b53e9 100644
--- a/src/bus-proxyd/bus-proxyd.c
+++ b/src/bus-proxyd/bus-proxyd.c
@@ -23,13 +23,9 @@
***/
#include <sys/socket.h>
-#include <sys/un.h>
-#include <sys/types.h>
-#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
-#include <poll.h>
#include <sys/prctl.h>
#include <stddef.h>
#include <getopt.h>
@@ -37,24 +33,15 @@
#include "log.h"
#include "util.h"
-#include "hashmap.h"
-#include "socket-util.h"
#include "sd-daemon.h"
-#include "sd-bus.h"
#include "bus-internal.h"
-#include "bus-message.h"
-#include "bus-util.h"
#include "build.h"
#include "strv.h"
#include "def.h"
#include "capability.h"
-#include "bus-control.h"
-#include "smack-util.h"
-#include "set.h"
#include "bus-xml-policy.h"
-#include "driver.h"
#include "proxy.h"
-#include "synthesize.h"
+#include "formats-util.h"
static char *arg_address = NULL;
static char **arg_configuration = NULL;
@@ -82,7 +69,7 @@ static int client_context_new(ClientContext **out) {
c = new0(ClientContext, 1);
if (!c)
- return log_oom();
+ return -ENOMEM;
c->fd = -1;
diff --git a/src/bus-proxyd/bus-xml-policy.c b/src/bus-proxyd/bus-xml-policy.c
index f6ac0c0093..675d24485e 100644
--- a/src/bus-proxyd/bus-xml-policy.c
+++ b/src/bus-proxyd/bus-xml-policy.c
@@ -25,9 +25,9 @@
#include "set.h"
#include "conf-files.h"
#include "bus-internal.h"
-#include "bus-message.h"
#include "bus-xml-policy.h"
#include "sd-login.h"
+#include "formats-util.h"
static void policy_item_free(PolicyItem *i) {
assert(i);
diff --git a/src/bus-proxyd/bus-xml-policy.h b/src/bus-proxyd/bus-xml-policy.h
index f2ec1bbea4..8f0ab8f17f 100644
--- a/src/bus-proxyd/bus-xml-policy.h
+++ b/src/bus-proxyd/bus-xml-policy.h
@@ -21,12 +21,10 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <inttypes.h>
#include <pthread.h>
#include "list.h"
#include "hashmap.h"
-#include "set.h"
typedef enum PolicyItemType {
_POLICY_ITEM_TYPE_UNSET = 0,
diff --git a/src/bus-proxyd/driver.c b/src/bus-proxyd/driver.c
index bc2c0c86f3..4ac955da41 100644
--- a/src/bus-proxyd/driver.c
+++ b/src/bus-proxyd/driver.c
@@ -21,23 +21,16 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <sys/types.h>
-#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <stddef.h>
-#include "log.h"
#include "util.h"
#include "sd-bus.h"
#include "bus-internal.h"
#include "bus-message.h"
#include "bus-util.h"
-#include "build.h"
#include "strv.h"
-#include "def.h"
-#include "capability.h"
-#include "bus-control.h"
#include "set.h"
#include "driver.h"
#include "synthesize.h"
@@ -56,9 +49,6 @@ static int get_creds_by_name(sd_bus *bus, const char *name, uint64_t mask, sd_bu
if (r < 0)
return r;
- if ((c->mask & mask) != mask)
- return -ENOTSUP;
-
*_creds = c;
c = NULL;
@@ -116,6 +106,10 @@ int bus_proxy_process_driver(sd_bus *a, sd_bus *b, sd_bus_message *m, SharedPoli
" <method name=\"RemoveMatch\">\n"
" <arg type=\"s\" direction=\"in\"/>\n"
" </method>\n"
+ " <method name=\"GetConnectionCredentials\">\n"
+ " <arg type=\"s\" direction=\"in\"/>\n"
+ " <arg type=\"a{sv}\" direction=\"out\"/>\n"
+ " </method>\n"
" <method name=\"GetConnectionSELinuxSecurityContext\">\n"
" <arg type=\"s\" direction=\"in\"/>\n"
" <arg type=\"ay\" direction=\"out\"/>\n"
@@ -219,7 +213,74 @@ int bus_proxy_process_driver(sd_bus *a, sd_bus *b, sd_bus_message *m, SharedPoli
return synthetic_reply_method_return(m, NULL);
+ } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus", "GetConnectionCredentials")) {
+ _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
+ _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+ _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+
+ if (!sd_bus_message_has_signature(m, "s"))
+ return synthetic_reply_method_error(m, &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid parameters"));
+
+ r = get_creds_by_message(a, m, SD_BUS_CREDS_PID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_SELINUX_CONTEXT, &creds, &error);
+ if (r < 0)
+ return synthetic_reply_method_errno(m, r, &error);
+
+ r = sd_bus_message_new_method_return(m, &reply);
+ if (r < 0)
+ return synthetic_reply_method_errno(m, r, NULL);
+
+ r = sd_bus_message_open_container(reply, 'a', "{sv}");
+ if (r < 0)
+ return synthetic_reply_method_errno(m, r, NULL);
+
+ /* Due to i.e. namespace translations some data might be missing */
+
+ if (creds->mask & SD_BUS_CREDS_PID) {
+ r = sd_bus_message_append(reply, "{sv}", "ProcessID", "u", (uint32_t) creds->pid);
+ if (r < 0)
+ return synthetic_reply_method_errno(m, r, NULL);
+ }
+
+ if (creds->mask & SD_BUS_CREDS_EUID) {
+ r = sd_bus_message_append(reply, "{sv}", "UnixUserID", "u", (uint32_t) creds->euid);
+ if (r < 0)
+ return synthetic_reply_method_errno(m, r, NULL);
+ }
+
+ if (creds->mask & SD_BUS_CREDS_SELINUX_CONTEXT) {
+ r = sd_bus_message_open_container(reply, 'e', "sv");
+ if (r < 0)
+ return synthetic_reply_method_errno(m, r, NULL);
+
+ r = sd_bus_message_append(reply, "s", "LinuxSecurityLabel");
+ if (r < 0)
+ return synthetic_reply_method_errno(m, r, NULL);
+
+ r = sd_bus_message_open_container(reply, 'v', "ay");
+ if (r < 0)
+ return synthetic_reply_method_errno(m, r, NULL);
+
+ r = sd_bus_message_append_array(reply, 'y', creds->label, strlen(creds->label));
+ if (r < 0)
+ return synthetic_reply_method_errno(m, r, NULL);
+
+ r = sd_bus_message_close_container(reply);
+ if (r < 0)
+ return synthetic_reply_method_errno(m, r, NULL);
+
+ r = sd_bus_message_close_container(reply);
+ if (r < 0)
+ return synthetic_reply_method_errno(m, r, NULL);
+ }
+
+ r = sd_bus_message_close_container(reply);
+ if (r < 0)
+ return synthetic_reply_method_errno(m, r, NULL);
+
+ return synthetic_driver_send(m->bus, reply);
+
} else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus", "GetConnectionSELinuxSecurityContext")) {
+ _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
_cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
_cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
@@ -230,7 +291,18 @@ int bus_proxy_process_driver(sd_bus *a, sd_bus *b, sd_bus_message *m, SharedPoli
if (r < 0)
return synthetic_reply_method_errno(m, r, &error);
- return synthetic_reply_method_return(m, "y", creds->label, strlen(creds->label));
+ if (!(creds->mask & SD_BUS_CREDS_SELINUX_CONTEXT))
+ return synthetic_reply_method_errno(m, -EOPNOTSUPP, NULL);
+
+ r = sd_bus_message_new_method_return(m, &reply);
+ if (r < 0)
+ return synthetic_reply_method_errno(m, r, NULL);
+
+ r = sd_bus_message_append_array(reply, 'y', creds->label, strlen(creds->label));
+ if (r < 0)
+ return synthetic_reply_method_errno(m, r, NULL);
+
+ return synthetic_driver_send(m->bus, reply);
} else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus", "GetConnectionUnixProcessID")) {
_cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
@@ -243,6 +315,9 @@ int bus_proxy_process_driver(sd_bus *a, sd_bus *b, sd_bus_message *m, SharedPoli
if (r < 0)
return synthetic_reply_method_errno(m, r, &error);
+ if (!(creds->mask & SD_BUS_CREDS_PID))
+ return synthetic_reply_method_errno(m, -EOPNOTSUPP, NULL);
+
return synthetic_reply_method_return(m, "u", (uint32_t) creds->pid);
} else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus", "GetConnectionUnixUser")) {
@@ -256,6 +331,9 @@ int bus_proxy_process_driver(sd_bus *a, sd_bus *b, sd_bus_message *m, SharedPoli
if (r < 0)
return synthetic_reply_method_errno(m, r, &error);
+ if (!(creds->mask & SD_BUS_CREDS_EUID))
+ return synthetic_reply_method_errno(m, -EOPNOTSUPP, NULL);
+
return synthetic_reply_method_return(m, "u", (uint32_t) creds->euid);
} else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus", "GetId")) {
@@ -290,6 +368,9 @@ int bus_proxy_process_driver(sd_bus *a, sd_bus *b, sd_bus_message *m, SharedPoli
if (r < 0)
return synthetic_reply_method_errno(m, r, &error);
+ if (!(creds->mask & SD_BUS_CREDS_UNIQUE_NAME))
+ return synthetic_reply_method_errno(m, -EOPNOTSUPP, NULL);
+
return synthetic_reply_method_return(m, "s", creds->unique_name);
} else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus", "ListActivatableNames")) {
diff --git a/src/bus-proxyd/proxy.c b/src/bus-proxyd/proxy.c
index 3dea908f5b..aa5010c1ac 100644
--- a/src/bus-proxyd/proxy.c
+++ b/src/bus-proxyd/proxy.c
@@ -23,35 +23,26 @@
***/
#include <sys/socket.h>
-#include <sys/un.h>
#include <sys/types.h>
-#include <fcntl.h>
-#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <poll.h>
-#include <stddef.h>
-#include <getopt.h>
#include "log.h"
#include "util.h"
-#include "socket-util.h"
#include "sd-daemon.h"
#include "sd-bus.h"
#include "bus-internal.h"
#include "bus-message.h"
#include "bus-util.h"
-#include "build.h"
#include "strv.h"
-#include "def.h"
-#include "capability.h"
#include "bus-control.h"
-#include "smack-util.h"
#include "set.h"
#include "bus-xml-policy.h"
#include "driver.h"
#include "proxy.h"
#include "synthesize.h"
+#include "formats-util.h"
static int proxy_create_destination(Proxy *p, const char *destination, const char *local_sec, bool negotiate_fds) {
_cleanup_bus_close_unref_ sd_bus *b = NULL;
@@ -729,13 +720,21 @@ static int proxy_process_destination_to_local(Proxy *p) {
/* Return the error to the client, if we can */
synthetic_reply_method_errnof(m, r, "Failed to forward message we got from destination: %m");
- log_error_errno(r,
- "Failed to forward message we got from destination: uid=" UID_FMT " gid=" GID_FMT" message=%s destination=%s path=%s interface=%s member=%s: %m",
- p->local_creds.uid, p->local_creds.gid, bus_message_type_to_string(m->header->type),
- strna(m->destination), strna(m->path), strna(m->interface), strna(m->member));
+ if (r == -ENOBUFS) {
+ /* if local dbus1 peer does not dispatch its queue, warn only once */
+ if (!p->queue_overflow)
+ log_error("Dropped messages due to queue overflow of local peer (pid: "PID_FMT" uid: "UID_FMT")", p->local_creds.pid, p->local_creds.uid);
+ p->queue_overflow = true;
+ } else
+ log_error_errno(r,
+ "Failed to forward message we got from destination: uid=" UID_FMT " gid=" GID_FMT" message=%s destination=%s path=%s interface=%s member=%s: %m",
+ p->local_creds.uid, p->local_creds.gid, bus_message_type_to_string(m->header->type),
+ strna(m->destination), strna(m->path), strna(m->interface), strna(m->member));
+
return 1;
}
+ p->queue_overflow = false;
return 1;
}
diff --git a/src/bus-proxyd/proxy.h b/src/bus-proxyd/proxy.h
index 913d47071b..ff278a2465 100644
--- a/src/bus-proxyd/proxy.h
+++ b/src/bus-proxyd/proxy.h
@@ -21,10 +21,8 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdlib.h>
#include "sd-bus.h"
#include "bus-xml-policy.h"
-#include "util.h"
typedef struct Proxy Proxy;
@@ -40,6 +38,7 @@ struct Proxy {
SharedPolicy *policy;
bool got_hello : 1;
+ bool queue_overflow : 1;
};
int proxy_new(Proxy **out, int in_fd, int out_fd, const char *dest);
diff --git a/src/bus-proxyd/stdio-bridge.c b/src/bus-proxyd/stdio-bridge.c
index 9fb3e9fc49..61bc08ae33 100644
--- a/src/bus-proxyd/stdio-bridge.c
+++ b/src/bus-proxyd/stdio-bridge.c
@@ -21,36 +21,23 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <sys/socket.h>
-#include <sys/un.h>
-#include <sys/types.h>
-#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
-#include <poll.h>
#include <stddef.h>
#include <getopt.h>
#include "log.h"
#include "util.h"
-#include "socket-util.h"
#include "sd-daemon.h"
#include "sd-bus.h"
#include "bus-internal.h"
-#include "bus-message.h"
#include "bus-util.h"
#include "build.h"
#include "strv.h"
#include "def.h"
-#include "capability.h"
-#include "bus-control.h"
-#include "smack-util.h"
-#include "set.h"
-#include "bus-xml-policy.h"
-#include "driver.h"
#include "proxy.h"
-#include "synthesize.h"
+#include "formats-util.h"
static char *arg_address = NULL;
static char *arg_command_line_buffer = NULL;
diff --git a/src/bus-proxyd/synthesize.c b/src/bus-proxyd/synthesize.c
index e1b0fd3535..67bcc7a242 100644
--- a/src/bus-proxyd/synthesize.c
+++ b/src/bus-proxyd/synthesize.c
@@ -21,24 +21,16 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <sys/types.h>
-#include <unistd.h>
-#include <string.h>
-#include <errno.h>
#include <stddef.h>
-#include "log.h"
#include "util.h"
#include "sd-bus.h"
#include "bus-internal.h"
#include "bus-message.h"
#include "bus-util.h"
-#include "strv.h"
-#include "def.h"
-#include "bus-control.h"
#include "synthesize.h"
-static int synthetic_driver_send(sd_bus *b, sd_bus_message *m) {
+int synthetic_driver_send(sd_bus *b, sd_bus_message *m) {
int r;
assert(b);
diff --git a/src/bus-proxyd/synthesize.h b/src/bus-proxyd/synthesize.h
index a55f171cb2..e850350bc5 100644
--- a/src/bus-proxyd/synthesize.h
+++ b/src/bus-proxyd/synthesize.h
@@ -23,6 +23,8 @@
#include "sd-bus.h"
+int synthetic_driver_send(sd_bus *b, sd_bus_message *m);
+
int synthetic_reply_method_return(sd_bus_message *call, const char *types, ...);
int synthetic_reply_method_return_strv(sd_bus_message *call, char **l);
diff --git a/src/bus-proxyd/test-bus-xml-policy.c b/src/bus-proxyd/test-bus-xml-policy.c
index 421487e038..d19d0e1b60 100644
--- a/src/bus-proxyd/test-bus-xml-policy.c
+++ b/src/bus-proxyd/test-bus-xml-policy.c
@@ -19,27 +19,14 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <sys/socket.h>
-#include <sys/un.h>
-#include <sys/types.h>
-#include <fcntl.h>
#include <unistd.h>
-#include <string.h>
#include <errno.h>
-#include <poll.h>
#include <stddef.h>
-#include <getopt.h>
#include "log.h"
#include "util.h"
#include "sd-bus.h"
-#include "bus-internal.h"
-#include "bus-message.h"
-#include "bus-util.h"
-#include "build.h"
#include "strv.h"
-#include "def.h"
-#include "capability.h"
#include "bus-xml-policy.h"
static int test_policy_load(Policy *p, const char *name) {
diff --git a/src/cgls/cgls.c b/src/cgls/cgls.c
index 579e06de5b..c6f5485716 100644
--- a/src/cgls/cgls.c
+++ b/src/cgls/cgls.c
@@ -19,7 +19,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <limits.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
diff --git a/src/cgtop/cgtop.c b/src/cgtop/cgtop.c
index 3c7ad40605..a390cf3256 100644
--- a/src/cgtop/cgtop.c
+++ b/src/cgtop/cgtop.c
@@ -29,6 +29,7 @@
#include <getopt.h>
#include "path-util.h"
+#include "terminal-util.h"
#include "util.h"
#include "hashmap.h"
#include "cgroup-util.h"
@@ -447,7 +448,7 @@ static int display(Hashmap *a) {
Group *g;
Group **array;
signed path_columns;
- unsigned rows, n = 0, j, maxtcpu = 0, maxtpath = 0;
+ unsigned rows, n = 0, j, maxtcpu = 0, maxtpath = 3; /* 3 for ellipsize() to work properly */
char buffer[MAX3(21, FORMAT_BYTES_MAX, FORMAT_TIMESPAN_MAX)];
assert(a);
diff --git a/src/console/consoled-display.c b/src/console/consoled-display.c
index a30a2f1022..569c011dc0 100644
--- a/src/console/consoled-display.c
+++ b/src/console/consoled-display.c
@@ -20,7 +20,6 @@
***/
#include <errno.h>
-#include <inttypes.h>
#include <stdlib.h>
#include "consoled.h"
#include "grdev.h"
diff --git a/src/console/consoled-manager.c b/src/console/consoled-manager.c
index 9dd62f04a0..e560dcf524 100644
--- a/src/console/consoled-manager.c
+++ b/src/console/consoled-manager.c
@@ -20,20 +20,18 @@
***/
#include <errno.h>
-#include <libudev.h>
#include <stdlib.h>
-#include <string.h>
-#include "consoled.h"
-#include "grdev.h"
-#include "idev.h"
-#include "log.h"
#include "sd-bus.h"
-#include "sd-daemon.h"
#include "sd-event.h"
#include "sd-login.h"
+#include "log.h"
+#include "signal-util.h"
+#include "util.h"
+#include "consoled.h"
+#include "idev.h"
+#include "grdev.h"
#include "sysview.h"
#include "unifont.h"
-#include "util.h"
int manager_new(Manager **out) {
_cleanup_(manager_freep) Manager *m = NULL;
@@ -228,7 +226,7 @@ static int manager_sysview_session_control(Manager *m, sysview_event *event) {
sysview_session_get_name(session));
session_free(s);
sysview_session_set_userdata(session, NULL);
- return -error;
+ return error;
}
return 0;
diff --git a/src/console/consoled-session.c b/src/console/consoled-session.c
index 927965e02c..264a4d009a 100644
--- a/src/console/consoled-session.c
+++ b/src/console/consoled-session.c
@@ -20,16 +20,12 @@
***/
#include <errno.h>
-#include <inttypes.h>
-#include <libudev.h>
#include <stdlib.h>
#include "consoled.h"
#include "grdev.h"
-#include "hashmap.h"
#include "idev.h"
#include "list.h"
#include "macro.h"
-#include "sd-bus.h"
#include "sd-event.h"
#include "sysview.h"
#include "util.h"
diff --git a/src/console/consoled-terminal.c b/src/console/consoled-terminal.c
index 19d14016ad..03447d1b92 100644
--- a/src/console/consoled-terminal.c
+++ b/src/console/consoled-terminal.c
@@ -20,7 +20,6 @@
***/
#include <errno.h>
-#include <inttypes.h>
#include <stdlib.h>
#include "consoled.h"
#include "list.h"
@@ -99,7 +98,7 @@ Terminal *terminal_free(Terminal *t) {
assert(t->workspace);
if (t->pty) {
- (void)pty_signal(t->pty, SIGHUP);
+ (void) pty_signal(t->pty, SIGHUP);
pty_close(t->pty);
pty_unref(t->pty);
}
diff --git a/src/console/consoled-workspace.c b/src/console/consoled-workspace.c
index 56344ef2cf..5e9e5c7c49 100644
--- a/src/console/consoled-workspace.c
+++ b/src/console/consoled-workspace.c
@@ -20,7 +20,6 @@
***/
#include <errno.h>
-#include <inttypes.h>
#include <stdlib.h>
#include "consoled.h"
#include "grdev.h"
diff --git a/src/console/consoled.c b/src/console/consoled.c
index 6f65dcdcb9..9f69e8983f 100644
--- a/src/console/consoled.c
+++ b/src/console/consoled.c
@@ -21,11 +21,10 @@
#include <errno.h>
#include <stdlib.h>
-#include <string.h>
-#include "consoled.h"
-#include "log.h"
#include "sd-daemon.h"
-#include "util.h"
+#include "log.h"
+#include "signal-util.h"
+#include "consoled.h"
int main(int argc, char *argv[]) {
_cleanup_(manager_freep) Manager *m = NULL;
diff --git a/src/console/consoled.h b/src/console/consoled.h
index b0db610400..f85c1a0791 100644
--- a/src/console/consoled.h
+++ b/src/console/consoled.h
@@ -21,10 +21,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <errno.h>
-#include <inttypes.h>
-#include <libudev.h>
-#include <stdlib.h>
#include "grdev.h"
#include "idev.h"
#include "list.h"
diff --git a/src/core/automount.c b/src/core/automount.c
index 9f6bd84b21..d847dc1629 100644
--- a/src/core/automount.c
+++ b/src/core/automount.c
@@ -32,8 +32,6 @@
#include "unit.h"
#include "automount.h"
#include "mount.h"
-#include "load-fragment.h"
-#include "load-dropin.h"
#include "unit-name.h"
#include "special.h"
#include "label.h"
@@ -42,6 +40,9 @@
#include "dbus-automount.h"
#include "bus-util.h"
#include "bus-error.h"
+#include "formats-util.h"
+#include "process-util.h"
+#include "async.h"
static const UnitActiveState state_translation_table[_AUTOMOUNT_STATE_MAX] = {
[AUTOMOUNT_DEAD] = UNIT_INACTIVE,
@@ -50,6 +51,22 @@ static const UnitActiveState state_translation_table[_AUTOMOUNT_STATE_MAX] = {
[AUTOMOUNT_FAILED] = UNIT_FAILED
};
+struct expire_data {
+ int dev_autofs_fd;
+ int ioctl_fd;
+};
+
+static inline void expire_data_free(struct expire_data *data) {
+ if (!data)
+ return;
+
+ safe_close(data->dev_autofs_fd);
+ safe_close(data->ioctl_fd);
+ free(data);
+}
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(struct expire_data*, expire_data_free);
+
static int open_dev_autofs(Manager *m);
static int automount_dispatch_io(sd_event_source *s, int fd, uint32_t events, void *userdata);
@@ -81,13 +98,16 @@ static void repeat_unmount(const char *path) {
}
}
+static int automount_send_ready(Automount *a, Set *tokens, int status);
+
static void unmount_autofs(Automount *a) {
assert(a);
if (a->pipe_fd < 0)
return;
- automount_send_ready(a, -EHOSTDOWN);
+ automount_send_ready(a, a->tokens, -EHOSTDOWN);
+ automount_send_ready(a, a->expire_tokens, -EHOSTDOWN);
a->pipe_event_source = sd_event_source_unref(a->pipe_event_source);
a->pipe_fd = safe_close(a->pipe_fd);
@@ -112,6 +132,10 @@ static void automount_done(Unit *u) {
set_free(a->tokens);
a->tokens = NULL;
+ set_free(a->expire_tokens);
+ a->expire_tokens = NULL;
+
+ a->expire_event_source = sd_event_source_unref(a->expire_event_source);
}
static int automount_add_mount_links(Automount *a) {
@@ -132,7 +156,7 @@ static int automount_add_default_dependencies(Automount *a) {
assert(a);
- if (UNIT(a)->manager->running_as != SYSTEMD_SYSTEM)
+ if (UNIT(a)->manager->running_as != MANAGER_SYSTEM)
return 0;
r = unit_add_two_dependencies_by_name(UNIT(a), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_UMOUNT_TARGET, NULL, true);
@@ -143,26 +167,25 @@ static int automount_add_default_dependencies(Automount *a) {
}
static int automount_verify(Automount *a) {
- bool b;
_cleanup_free_ char *e = NULL;
+ int r;
+
assert(a);
if (UNIT(a)->load_state != UNIT_LOADED)
return 0;
if (path_equal(a->where, "/")) {
- log_unit_error(UNIT(a)->id, "Cannot have an automount unit for the root directory. Refusing.");
+ log_unit_error(UNIT(a), "Cannot have an automount unit for the root directory. Refusing.");
return -EINVAL;
}
- e = unit_name_from_path(a->where, ".automount");
- if (!e)
- return -ENOMEM;
-
- b = unit_has_name(UNIT(a), e);
+ r = unit_name_from_path(a->where, ".automount", &e);
+ if (r < 0)
+ return log_unit_error(UNIT(a), "Failed to generate unit name from path: %m");
- if (!b) {
- log_unit_error(UNIT(a)->id, "%s's Where setting doesn't match unit name. Refusing.", UNIT(a)->id);
+ if (!unit_has_name(UNIT(a), e)) {
+ log_unit_error(UNIT(a), "Where= setting doesn't match unit name. Refusing.");
return -EINVAL;
}
@@ -185,9 +208,9 @@ static int automount_load(Unit *u) {
Unit *x;
if (!a->where) {
- a->where = unit_name_to_path(u->id);
- if (!a->where)
- return -ENOMEM;
+ r = unit_name_to_path(u->id, &a->where);
+ if (r < 0)
+ return r;
}
path_kill_slashes(a->where);
@@ -226,11 +249,7 @@ static void automount_set_state(Automount *a, AutomountState state) {
unmount_autofs(a);
if (state != old_state)
- log_unit_debug(UNIT(a)->id,
- "%s changed %s -> %s",
- UNIT(a)->id,
- automount_state_to_string(old_state),
- automount_state_to_string(state));
+ log_unit_debug(UNIT(a), "Changed %s -> %s", automount_state_to_string(old_state), automount_state_to_string(state));
unit_notify(UNIT(a), state_translation_table[old_state], state_translation_table[state], true);
}
@@ -250,12 +269,13 @@ static int automount_coldplug(Unit *u) {
if (a->deserialized_state == AUTOMOUNT_WAITING ||
a->deserialized_state == AUTOMOUNT_RUNNING) {
-
assert(a->pipe_fd >= 0);
r = sd_event_add_io(u->manager->event, &a->pipe_event_source, a->pipe_fd, EPOLLIN, automount_dispatch_io, u);
if (r < 0)
return r;
+
+ (void) sd_event_source_set_description(a->pipe_event_source, "automount-io");
}
automount_set_state(a, a->deserialized_state);
@@ -265,6 +285,7 @@ static int automount_coldplug(Unit *u) {
}
static void automount_dump(Unit *u, FILE *f, const char *prefix) {
+ char time_string[FORMAT_TIMESPAN_MAX];
Automount *a = AUTOMOUNT(u);
assert(a);
@@ -273,11 +294,13 @@ static void automount_dump(Unit *u, FILE *f, const char *prefix) {
"%sAutomount State: %s\n"
"%sResult: %s\n"
"%sWhere: %s\n"
- "%sDirectoryMode: %04o\n",
+ "%sDirectoryMode: %04o\n"
+ "%sTimeoutIdleUSec: %s\n",
prefix, automount_state_to_string(a->state),
prefix, automount_result_to_string(a->result),
prefix, a->where,
- prefix, a->directory_mode);
+ prefix, a->directory_mode,
+ prefix, format_timespan(time_string, FORMAT_TIMESPAN_MAX, a->timeout_idle_usec, USEC_PER_SEC));
}
static void automount_enter_dead(Automount *a, AutomountResult f) {
@@ -336,7 +359,7 @@ static int open_ioctl_fd(int dev_autofs_fd, const char *where, dev_t devid) {
if (param->ioctlfd < 0)
return -EIO;
- fd_cloexec(param->ioctlfd, true);
+ (void) fd_cloexec(param->ioctlfd, true);
return param->ioctlfd;
}
@@ -367,7 +390,7 @@ static int autofs_protocol(int dev_autofs_fd, int ioctl_fd) {
return 0;
}
-static int autofs_set_timeout(int dev_autofs_fd, int ioctl_fd, time_t sec) {
+static int autofs_set_timeout(int dev_autofs_fd, int ioctl_fd, usec_t usec) {
struct autofs_dev_ioctl param;
assert(dev_autofs_fd >= 0);
@@ -375,7 +398,9 @@ static int autofs_set_timeout(int dev_autofs_fd, int ioctl_fd, time_t sec) {
init_autofs_dev_ioctl(&param);
param.ioctlfd = ioctl_fd;
- param.timeout.timeout = sec;
+
+ /* Convert to seconds, rounding up. */
+ param.timeout.timeout = (usec + USEC_PER_SEC - 1) / USEC_PER_SEC;
if (ioctl(dev_autofs_fd, AUTOFS_DEV_IOCTL_TIMEOUT, &param) < 0)
return -errno;
@@ -404,7 +429,7 @@ static int autofs_send_ready(int dev_autofs_fd, int ioctl_fd, uint32_t token, in
return 0;
}
-int automount_send_ready(Automount *a, int status) {
+static int automount_send_ready(Automount *a, Set *tokens, int status) {
_cleanup_close_ int ioctl_fd = -1;
unsigned token;
int r;
@@ -412,7 +437,7 @@ int automount_send_ready(Automount *a, int status) {
assert(a);
assert(status <= 0);
- if (set_isempty(a->tokens))
+ if (set_isempty(tokens))
return 0;
ioctl_fd = open_ioctl_fd(UNIT(a)->manager->dev_autofs_fd, a->where, a->dev_id);
@@ -420,14 +445,14 @@ int automount_send_ready(Automount *a, int status) {
return ioctl_fd;
if (status)
- log_unit_debug_errno(UNIT(a)->id, status, "Sending failure: %m");
+ log_unit_debug_errno(UNIT(a), status, "Sending failure: %m");
else
- log_unit_debug(UNIT(a)->id, "Sending success.");
+ log_unit_debug(UNIT(a), "Sending success.");
r = 0;
/* Autofs thankfully does not hand out 0 as a token */
- while ((token = PTR_TO_UINT(set_steal_first(a->tokens)))) {
+ while ((token = PTR_TO_UINT(set_steal_first(tokens)))) {
int k;
/* Autofs fun fact II:
@@ -446,6 +471,53 @@ int automount_send_ready(Automount *a, int status) {
return r;
}
+int automount_update_mount(Automount *a, MountState old_state, MountState state) {
+ assert(a);
+
+ switch (state) {
+ case MOUNT_MOUNTED:
+ case MOUNT_REMOUNTING:
+ automount_send_ready(a, a->tokens, 0);
+ break;
+ case MOUNT_DEAD:
+ case MOUNT_UNMOUNTING:
+ case MOUNT_MOUNTING_SIGTERM:
+ case MOUNT_MOUNTING_SIGKILL:
+ case MOUNT_REMOUNTING_SIGTERM:
+ case MOUNT_REMOUNTING_SIGKILL:
+ case MOUNT_UNMOUNTING_SIGTERM:
+ case MOUNT_UNMOUNTING_SIGKILL:
+ case MOUNT_FAILED:
+ if (old_state != state)
+ automount_send_ready(a, a->tokens, -ENODEV);
+ break;
+ default:
+ break;
+ }
+
+ switch (state) {
+ case MOUNT_DEAD:
+ automount_send_ready(a, a->expire_tokens, 0);
+ break;
+ case MOUNT_MOUNTING:
+ case MOUNT_MOUNTING_DONE:
+ case MOUNT_MOUNTING_SIGTERM:
+ case MOUNT_MOUNTING_SIGKILL:
+ case MOUNT_REMOUNTING_SIGTERM:
+ case MOUNT_REMOUNTING_SIGKILL:
+ case MOUNT_UNMOUNTING_SIGTERM:
+ case MOUNT_UNMOUNTING_SIGKILL:
+ case MOUNT_FAILED:
+ if (old_state != state)
+ automount_send_ready(a, a->expire_tokens, -ENODEV);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
static void automount_enter_waiting(Automount *a) {
_cleanup_close_ int ioctl_fd = -1;
int p[2] = { -1, -1 };
@@ -460,8 +532,15 @@ static void automount_enter_waiting(Automount *a) {
assert(a->pipe_fd < 0);
assert(a->where);
- if (a->tokens)
- set_clear(a->tokens);
+ set_clear(a->tokens);
+
+ r = unit_fail_if_symlink(UNIT(a), a->where);
+ if (r < 0)
+ goto fail;
+
+ (void) mkdir_p_label(a->where, 0555);
+
+ unit_warn_if_dir_nonempty(UNIT(a), a->where);
dev_autofs_fd = open_dev_autofs(UNIT(a)->manager);
if (dev_autofs_fd < 0) {
@@ -469,11 +548,6 @@ static void automount_enter_waiting(Automount *a) {
goto fail;
}
- /* We knowingly ignore the results of this call */
- mkdir_p_label(a->where, 0555);
-
- warn_if_dir_nonempty(a->meta.id, a->where);
-
if (pipe2(p, O_NONBLOCK|O_CLOEXEC) < 0) {
r = -errno;
goto fail;
@@ -505,7 +579,7 @@ static void automount_enter_waiting(Automount *a) {
if (r < 0)
goto fail;
- r = autofs_set_timeout(dev_autofs_fd, ioctl_fd, 300);
+ r = autofs_set_timeout(dev_autofs_fd, ioctl_fd, a->timeout_idle_usec);
if (r < 0)
goto fail;
@@ -519,6 +593,8 @@ static void automount_enter_waiting(Automount *a) {
if (r < 0)
goto fail;
+ (void) sd_event_source_set_description(a->pipe_event_source, "automount-io");
+
a->pipe_fd = p[0];
a->dev_id = st.st_dev;
@@ -532,11 +608,93 @@ fail:
if (mounted)
repeat_unmount(a->where);
- log_unit_error(UNIT(a)->id,
- "Failed to initialize automounter: %s", strerror(-r));
+ log_unit_error_errno(UNIT(a), r, "Failed to initialize automounter: %m");
automount_enter_dead(a, AUTOMOUNT_FAILURE_RESOURCES);
}
+static void *expire_thread(void *p) {
+ struct autofs_dev_ioctl param;
+ _cleanup_(expire_data_freep) struct expire_data *data = (struct expire_data*)p;
+ int r;
+
+ assert(data->dev_autofs_fd >= 0);
+ assert(data->ioctl_fd >= 0);
+
+ init_autofs_dev_ioctl(&param);
+ param.ioctlfd = data->ioctl_fd;
+
+ do {
+ r = ioctl(data->dev_autofs_fd, AUTOFS_DEV_IOCTL_EXPIRE, &param);
+ } while (r >= 0);
+
+ if (errno != EAGAIN)
+ log_warning_errno(errno, "Failed to expire automount, ignoring: %m");
+
+ return NULL;
+}
+
+static int automount_start_expire(Automount *a);
+
+static int automount_dispatch_expire(sd_event_source *source, usec_t usec, void *userdata) {
+ Automount *a = AUTOMOUNT(userdata);
+ _cleanup_(expire_data_freep) struct expire_data *data = NULL;
+ int r;
+
+ assert(a);
+ assert(source == a->expire_event_source);
+
+ data = new0(struct expire_data, 1);
+ if (!data)
+ return log_oom();
+
+ data->ioctl_fd = -1;
+
+ data->dev_autofs_fd = fcntl(UNIT(a)->manager->dev_autofs_fd, F_DUPFD_CLOEXEC, 3);
+ if (data->dev_autofs_fd < 0)
+ return log_unit_error_errno(UNIT(a), errno, "Failed to duplicate autofs fd: %m");
+
+ data->ioctl_fd = open_ioctl_fd(UNIT(a)->manager->dev_autofs_fd, a->where, a->dev_id);
+ if (data->ioctl_fd < 0)
+ return log_unit_error_errno(UNIT(a), data->ioctl_fd, "Couldn't open autofs ioctl fd: %m");
+
+ r = asynchronous_job(expire_thread, data);
+ if (r < 0)
+ return log_unit_error_errno(UNIT(a), r, "Failed to start expire job: %m");
+
+ data = NULL;
+
+ return automount_start_expire(a);
+}
+
+static int automount_start_expire(Automount *a) {
+ int r;
+ usec_t timeout;
+
+ assert(a);
+
+ timeout = now(CLOCK_MONOTONIC) + MAX(a->timeout_idle_usec/10, USEC_PER_SEC);
+
+ if (a->expire_event_source) {
+ r = sd_event_source_set_time(a->expire_event_source, timeout);
+ if (r < 0)
+ return r;
+
+ return sd_event_source_set_enabled(a->expire_event_source, SD_EVENT_ONESHOT);
+ }
+
+ r = sd_event_add_time(
+ UNIT(a)->manager->event,
+ &a->expire_event_source,
+ CLOCK_MONOTONIC, timeout, 0,
+ automount_dispatch_expire, a);
+ if (r < 0)
+ return r;
+
+ (void) sd_event_source_set_description(a->expire_event_source, "automount-expire");
+
+ return 0;
+}
+
static void automount_enter_runnning(Automount *a) {
_cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
struct stat st;
@@ -547,9 +705,9 @@ static void automount_enter_runnning(Automount *a) {
/* We don't take mount requests anymore if we are supposed to
* shut down anyway */
if (unit_stop_pending(UNIT(a))) {
- log_unit_debug(UNIT(a)->id,
- "Suppressing automount request on %s since unit stop is scheduled.", UNIT(a)->id);
- automount_send_ready(a, -EHOSTDOWN);
+ log_unit_debug(UNIT(a), "Suppressing automount request since unit stop is scheduled.");
+ automount_send_ready(a, a->tokens, -EHOSTDOWN);
+ automount_send_ready(a, a->expire_tokens, -EHOSTDOWN);
return;
}
@@ -557,25 +715,25 @@ static void automount_enter_runnning(Automount *a) {
/* Before we do anything, let's see if somebody is playing games with us? */
if (lstat(a->where, &st) < 0) {
- log_unit_warning(UNIT(a)->id,
- "%s failed to stat automount point: %m", UNIT(a)->id);
+ log_unit_warning_errno(UNIT(a), errno, "Failed to stat automount point: %m");
goto fail;
}
if (!S_ISDIR(st.st_mode) || st.st_dev != a->dev_id)
- log_unit_info(UNIT(a)->id,
- "%s's automount point already active?", UNIT(a)->id);
+ log_unit_info(UNIT(a), "Automount point already active?");
else {
r = manager_add_job(UNIT(a)->manager, JOB_START, UNIT_TRIGGER(UNIT(a)),
JOB_REPLACE, true, &error, NULL);
if (r < 0) {
- log_unit_warning(UNIT(a)->id,
- "%s failed to queue mount startup job: %s",
- UNIT(a)->id, bus_error_message(&error, r));
+ log_unit_warning(UNIT(a), "Failed to queue mount startup job: %s", bus_error_message(&error, r));
goto fail;
}
}
+ r = automount_start_expire(a);
+ if (r < 0)
+ log_unit_warning_errno(UNIT(a), r, "Failed to start expiration timer, ignoring: %m");
+
automount_set_state(a, AUTOMOUNT_RUNNING);
return;
@@ -589,10 +747,8 @@ static int automount_start(Unit *u) {
assert(a);
assert(a->state == AUTOMOUNT_DEAD || a->state == AUTOMOUNT_FAILED);
- if (path_is_mount_point(a->where, false)) {
- log_unit_error(u->id,
- "Path %s is already a mount point, refusing start for %s",
- a->where, u->id);
+ if (path_is_mount_point(a->where, 0) > 0) {
+ log_unit_error(u, "Path %s is already a mount point, refusing start.", a->where);
return -EEXIST;
}
@@ -629,6 +785,8 @@ static int automount_serialize(Unit *u, FILE *f, FDSet *fds) {
SET_FOREACH(p, a->tokens, i)
unit_serialize_item_format(u, f, "token", "%u", PTR_TO_UINT(p));
+ SET_FOREACH(p, a->expire_tokens, i)
+ unit_serialize_item_format(u, f, "expire-token", "%u", PTR_TO_UINT(p));
if (a->pipe_fd >= 0) {
int copy;
@@ -655,7 +813,7 @@ static int automount_deserialize_item(Unit *u, const char *key, const char *valu
state = automount_state_from_string(value);
if (state < 0)
- log_unit_debug(u->id, "Failed to parse state value %s", value);
+ log_unit_debug(u, "Failed to parse state value: %s", value);
else
a->deserialized_state = state;
} else if (streq(key, "result")) {
@@ -663,7 +821,7 @@ static int automount_deserialize_item(Unit *u, const char *key, const char *valu
f = automount_result_from_string(value);
if (f < 0)
- log_unit_debug(u->id, "Failed to parse result value %s", value);
+ log_unit_debug(u, "Failed to parse result value: %s", value);
else if (f != AUTOMOUNT_SUCCESS)
a->result = f;
@@ -671,34 +829,52 @@ static int automount_deserialize_item(Unit *u, const char *key, const char *valu
unsigned d;
if (safe_atou(value, &d) < 0)
- log_unit_debug(u->id, "Failed to parse dev-id value %s", value);
+ log_unit_debug(u, "Failed to parse dev-id value: %s", value);
else
a->dev_id = (unsigned) d;
} else if (streq(key, "token")) {
unsigned token;
if (safe_atou(value, &token) < 0)
- log_unit_debug(u->id, "Failed to parse token value %s", value);
+ log_unit_debug(u, "Failed to parse token value: %s", value);
else {
- if (!a->tokens)
- if (!(a->tokens = set_new(NULL)))
- return -ENOMEM;
+ r = set_ensure_allocated(&a->tokens, NULL);
+ if (r < 0) {
+ log_oom();
+ return 0;
+ }
r = set_put(a->tokens, UINT_TO_PTR(token));
if (r < 0)
- return r;
+ log_unit_error_errno(u, r, "Failed to add token to set: %m");
+ }
+ } else if (streq(key, "expire-token")) {
+ unsigned token;
+
+ if (safe_atou(value, &token) < 0)
+ log_unit_debug(u, "Failed to parse token value: %s", value);
+ else {
+ r = set_ensure_allocated(&a->expire_tokens, NULL);
+ if (r < 0) {
+ log_oom();
+ return 0;
+ }
+
+ r = set_put(a->expire_tokens, UINT_TO_PTR(token));
+ if (r < 0)
+ log_unit_error_errno(u, r, "Failed to add expire token to set: %m");
}
} else if (streq(key, "pipe-fd")) {
int fd;
if (safe_atoi(value, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd))
- log_unit_debug(u->id, "Failed to parse pipe-fd value %s", value);
+ log_unit_debug(u, "Failed to parse pipe-fd value: %s", value);
else {
safe_close(a->pipe_fd);
a->pipe_fd = fdset_remove(fds, fd);
}
} else
- log_unit_debug(u->id, "Unknown serialization key '%s'", key);
+ log_unit_debug(u, "Unknown serialization key: %s", key);
return 0;
}
@@ -725,25 +901,22 @@ static bool automount_check_gc(Unit *u) {
}
static int automount_dispatch_io(sd_event_source *s, int fd, uint32_t events, void *userdata) {
+ _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
union autofs_v5_packet_union packet;
Automount *a = AUTOMOUNT(userdata);
- ssize_t l;
int r;
assert(a);
assert(fd == a->pipe_fd);
if (events != EPOLLIN) {
- log_unit_error(UNIT(a)->id, "Got invalid poll event on pipe.");
+ log_unit_error(UNIT(a), "Got invalid poll event %"PRIu32" on pipe (fd=%d)", events, fd);
goto fail;
}
- l = loop_read(a->pipe_fd, &packet, sizeof(packet), true);
- if (l != sizeof(packet)) {
- if (l < 0)
- log_unit_error_errno(UNIT(a)->id, l, "Invalid read from pipe: %m");
- else
- log_unit_error(UNIT(a)->id, "Invalid read from pipe: short read");
+ r = loop_read_exact(a->pipe_fd, &packet, sizeof(packet), true);
+ if (r < 0) {
+ log_unit_error_errno(UNIT(a), r, "Invalid read from pipe: %m");
goto fail;
}
@@ -755,29 +928,50 @@ static int automount_dispatch_io(sd_event_source *s, int fd, uint32_t events, vo
_cleanup_free_ char *p = NULL;
get_process_comm(packet.v5_packet.pid, &p);
- log_unit_info(UNIT(a)->id,
- "Got automount request for %s, triggered by %"PRIu32" (%s)",
- a->where, packet.v5_packet.pid, strna(p));
+ log_unit_info(UNIT(a), "Got automount request for %s, triggered by %"PRIu32" (%s)", a->where, packet.v5_packet.pid, strna(p));
} else
- log_unit_debug(UNIT(a)->id, "Got direct mount request on %s", a->where);
+ log_unit_debug(UNIT(a), "Got direct mount request on %s", a->where);
r = set_ensure_allocated(&a->tokens, NULL);
if (r < 0) {
- log_unit_error(UNIT(a)->id, "Failed to allocate token set.");
+ log_unit_error(UNIT(a), "Failed to allocate token set.");
goto fail;
}
r = set_put(a->tokens, UINT_TO_PTR(packet.v5_packet.wait_queue_token));
if (r < 0) {
- log_unit_error_errno(UNIT(a)->id, r, "Failed to remember token: %m");
+ log_unit_error_errno(UNIT(a), r, "Failed to remember token: %m");
goto fail;
}
automount_enter_runnning(a);
break;
+ case autofs_ptype_expire_direct:
+ log_unit_debug(UNIT(a), "Got direct umount request on %s", a->where);
+
+ (void) sd_event_source_set_enabled(a->expire_event_source, SD_EVENT_OFF);
+
+ r = set_ensure_allocated(&a->expire_tokens, NULL);
+ if (r < 0) {
+ log_unit_error(UNIT(a), "Failed to allocate token set.");
+ goto fail;
+ }
+
+ r = set_put(a->expire_tokens, UINT_TO_PTR(packet.v5_packet.wait_queue_token));
+ if (r < 0) {
+ log_unit_error_errno(UNIT(a), r, "Failed to remember token: %m");
+ goto fail;
+ }
+ r = manager_add_job(UNIT(a)->manager, JOB_STOP, UNIT_TRIGGER(UNIT(a)), JOB_REPLACE, true, &error, NULL);
+ if (r < 0) {
+ log_unit_warning(UNIT(a), "Failed to queue umount startup job: %s", bus_error_message(&error, r));
+ goto fail;
+ }
+ break;
+
default:
- log_unit_error(UNIT(a)->id, "Received unknown automount request %i", packet.hdr.type);
+ log_unit_error(UNIT(a), "Received unknown automount request %i", packet.hdr.type);
break;
}
@@ -805,11 +999,9 @@ static void automount_reset_failed(Unit *u) {
a->result = AUTOMOUNT_SUCCESS;
}
-static bool automount_supported(Manager *m) {
+static bool automount_supported(void) {
static int supported = -1;
- assert(m);
-
if (supported < 0)
supported = access("/dev/autofs", F_OK) >= 0;
diff --git a/src/core/automount.h b/src/core/automount.h
index 60f5522389..2a50fef68d 100644
--- a/src/core/automount.h
+++ b/src/core/automount.h
@@ -47,6 +47,7 @@ struct Automount {
AutomountState state, deserialized_state;
char *where;
+ usec_t timeout_idle_usec;
int pipe_fd;
sd_event_source *pipe_event_source;
@@ -54,13 +55,16 @@ struct Automount {
dev_t dev_id;
Set *tokens;
+ Set *expire_tokens;
+
+ sd_event_source *expire_event_source;
AutomountResult result;
};
extern const UnitVTable automount_vtable;
-int automount_send_ready(Automount *a, int status);
+int automount_update_mount(Automount *a, MountState old_state, MountState state);
const char* automount_state_to_string(AutomountState i) _const_;
AutomountState automount_state_from_string(const char *s) _pure_;
diff --git a/src/core/bus-policy.c b/src/core/bus-policy.c
index 710283dcd1..a6a8fcd4d3 100644
--- a/src/core/bus-policy.c
+++ b/src/core/bus-policy.c
@@ -150,8 +150,11 @@ int bus_kernel_make_starter(
hello->attach_flags_send = _KDBUS_ATTACH_ANY;
hello->attach_flags_recv = _KDBUS_ATTACH_ANY;
- if (ioctl(fd, KDBUS_CMD_HELLO, hello) < 0)
+ if (ioctl(fd, KDBUS_CMD_HELLO, hello) < 0) {
+ if (errno == ENOTTY) /* Major API change */
+ return -ESOCKTNOSUPPORT;
return -errno;
+ }
/* not interested in any output values */
cmd_free.offset = hello->offset;
@@ -160,7 +163,7 @@ int bus_kernel_make_starter(
/* The higher 32bit of the bus_flags fields are considered
* 'incompatible flags'. Refuse them all for now. */
if (hello->bus_flags > 0xFFFFFFFFULL)
- return -ENOTSUP;
+ return -ESOCKTNOSUPPORT;
return fd;
}
diff --git a/src/core/busname.c b/src/core/busname.c
index 1d77292f9b..11f3b98009 100644
--- a/src/core/busname.c
+++ b/src/core/busname.c
@@ -22,12 +22,14 @@
#include <sys/mman.h>
#include "special.h"
+#include "formats-util.h"
+#include "signal-util.h"
#include "bus-kernel.h"
#include "bus-internal.h"
#include "bus-util.h"
-#include "service.h"
#include "kdbus.h"
#include "bus-policy.h"
+#include "service.h"
#include "dbus-busname.h"
#include "busname.h"
@@ -123,12 +125,18 @@ static int busname_arm_timer(BusName *n) {
return sd_event_source_set_enabled(n->timer_event_source, SD_EVENT_ONESHOT);
}
- return sd_event_add_time(
+ r = sd_event_add_time(
UNIT(n)->manager->event,
&n->timer_event_source,
CLOCK_MONOTONIC,
now(CLOCK_MONOTONIC) + n->timeout_usec, 0,
busname_dispatch_timer, n);
+ if (r < 0)
+ return r;
+
+ (void) sd_event_source_set_description(n->timer_event_source, "busname-timer");
+
+ return 0;
}
static int busname_add_default_default_dependencies(BusName *n) {
@@ -140,7 +148,7 @@ static int busname_add_default_default_dependencies(BusName *n) {
if (r < 0)
return r;
- if (UNIT(n)->manager->running_as == SYSTEMD_SYSTEM) {
+ if (UNIT(n)->manager->running_as == MANAGER_SYSTEM) {
r = unit_add_two_dependencies_by_name(UNIT(n), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, NULL, true);
if (r < 0)
return r;
@@ -156,9 +164,9 @@ static int busname_add_extras(BusName *n) {
assert(n);
if (!n->name) {
- n->name = unit_name_to_prefix(u->id);
- if (!n->name)
- return -ENOMEM;
+ r = unit_name_to_prefix(u->id, &n->name);
+ if (r < 0)
+ return r;
}
if (!u->description) {
@@ -201,13 +209,13 @@ static int busname_verify(BusName *n) {
return 0;
if (!service_name_is_valid(n->name)) {
- log_unit_error(UNIT(n)->id, "%s's Name= setting is not a valid service name Refusing.", UNIT(n)->id);
+ log_unit_error(UNIT(n), "Name= setting is not a valid service name Refusing.");
return -EINVAL;
}
e = strjoina(n->name, ".busname");
if (!unit_has_name(UNIT(n), e)) {
- log_unit_error(UNIT(n)->id, "%s's Name= setting doesn't match unit name. Refusing.", UNIT(n)->id);
+ log_unit_error(UNIT(n), "Name= setting doesn't match unit name. Refusing.");
return -EINVAL;
}
@@ -269,7 +277,7 @@ static void busname_unwatch_fd(BusName *n) {
r = sd_event_source_set_enabled(n->starter_event_source, SD_EVENT_OFF);
if (r < 0)
- log_unit_debug(UNIT(n)->id, "Failed to disable event source.");
+ log_unit_debug_errno(UNIT(n), r, "Failed to disable event source: %m");
}
static int busname_watch_fd(BusName *n) {
@@ -280,17 +288,24 @@ static int busname_watch_fd(BusName *n) {
if (n->starter_fd < 0)
return 0;
- if (n->starter_event_source)
+ if (n->starter_event_source) {
r = sd_event_source_set_enabled(n->starter_event_source, SD_EVENT_ON);
- else
+ if (r < 0)
+ goto fail;
+ } else {
r = sd_event_add_io(UNIT(n)->manager->event, &n->starter_event_source, n->starter_fd, EPOLLIN, busname_dispatch_io, n);
- if (r < 0) {
- log_unit_warning_errno(UNIT(n)->id, r, "Failed to watch starter fd: %m");
- busname_unwatch_fd(n);
- return r;
+ if (r < 0)
+ goto fail;
+
+ (void) sd_event_source_set_description(n->starter_event_source, "busname-starter");
}
return 0;
+
+fail:
+ log_unit_warning_errno(UNIT(n), r, "Failed to watch starter fd: %m");
+ busname_unwatch_fd(n);
+ return r;
}
static int busname_open_fd(BusName *n) {
@@ -302,10 +317,10 @@ static int busname_open_fd(BusName *n) {
if (n->starter_fd >= 0)
return 0;
- mode = UNIT(n)->manager->running_as == SYSTEMD_SYSTEM ? "system" : "user";
+ mode = UNIT(n)->manager->running_as == MANAGER_SYSTEM ? "system" : "user";
n->starter_fd = bus_kernel_open_bus_fd(mode, &path);
if (n->starter_fd < 0)
- return log_unit_warning_errno(UNIT(n)->id, n->starter_fd, "Failed to open %s: %m", path ?: "kdbus");
+ return log_unit_warning_errno(UNIT(n), n->starter_fd, "Failed to open %s: %m", path ?: "kdbus");
return 0;
}
@@ -329,8 +344,7 @@ static void busname_set_state(BusName *n, BusNameState state) {
busname_close_fd(n);
if (state != old_state)
- log_unit_debug(UNIT(n)->id, "%s changed %s -> %s",
- UNIT(n)->id, busname_state_to_string(old_state), busname_state_to_string(state));
+ log_unit_debug(UNIT(n), "Changed %s -> %s", busname_state_to_string(old_state), busname_state_to_string(state));
unit_notify(UNIT(n), state_translation_table[old_state], state_translation_table[state], true);
}
@@ -452,14 +466,14 @@ static void busname_enter_signal(BusName *n, BusNameState state, BusNameResult f
n->control_pid,
false);
if (r < 0) {
- log_unit_warning_errno(UNIT(n)->id, r, "%s failed to kill control process: %m", UNIT(n)->id);
+ log_unit_warning_errno(UNIT(n), r, "Failed to kill control process: %m");
goto fail;
}
if (r > 0) {
r = busname_arm_timer(n);
if (r < 0) {
- log_unit_warning_errno(UNIT(n)->id, r, "%s failed to arm timer: %m", UNIT(n)->id);
+ log_unit_warning_errno(UNIT(n), r, "Failed to arm timer: %m");
goto fail;
}
@@ -483,7 +497,7 @@ static void busname_enter_listening(BusName *n) {
if (n->activating) {
r = busname_watch_fd(n);
if (r < 0) {
- log_unit_warning_errno(UNIT(n)->id, r, "%s failed to watch names: %m", UNIT(n)->id);
+ log_unit_warning_errno(UNIT(n), r, "Failed to watch names: %m");
goto fail;
}
@@ -514,7 +528,7 @@ static void busname_enter_making(BusName *n) {
r = busname_make_starter(n, &n->control_pid);
if (r < 0) {
- log_unit_warning_errno(UNIT(n)->id, r, "%s failed to fork 'making' task: %m", UNIT(n)->id);
+ log_unit_warning_errno(UNIT(n), r, "Failed to fork 'making' task: %m");
goto fail;
}
@@ -525,7 +539,7 @@ static void busname_enter_making(BusName *n) {
r = bus_kernel_make_starter(n->starter_fd, n->name, n->activating, n->accept_fd, NULL, n->policy_world);
if (r < 0) {
- log_unit_warning_errno(UNIT(n)->id, r, "%s failed to make starter: %m", UNIT(n)->id);
+ log_unit_warning_errno(UNIT(n), r, "Failed to make starter: %m");
goto fail;
}
@@ -550,11 +564,11 @@ static void busname_enter_running(BusName *n) {
if (!n->activating)
return;
- /* We don't take conenctions anymore if we are supposed to
+ /* We don't take connections anymore if we are supposed to
* shut down anyway */
if (unit_stop_pending(UNIT(n))) {
- log_unit_debug(UNIT(n)->id, "Suppressing activation request on %s since unit stop is scheduled.", UNIT(n)->id);
+ log_unit_debug(UNIT(n), "Suppressing activation request since unit stop is scheduled.");
/* Flush all queued activation reqeuest by closing and reopening the connection */
bus_kernel_drop_one(n->starter_fd);
@@ -581,7 +595,7 @@ static void busname_enter_running(BusName *n) {
return;
fail:
- log_unit_warning(UNIT(n)->id, "%s failed to queue service startup job: %s", UNIT(n)->id, bus_error_message(&error, r));
+ log_unit_warning(UNIT(n), "Failed to queue service startup job: %s", bus_error_message(&error, r));
busname_enter_dead(n, BUSNAME_FAILURE_RESOURCES);
}
@@ -605,7 +619,7 @@ static int busname_start(Unit *u) {
service = SERVICE(UNIT_DEREF(n->service));
if (UNIT(service)->load_state != UNIT_LOADED) {
- log_unit_error(u->id, "Bus service %s not loaded, refusing.", UNIT(service)->id);
+ log_unit_error(u, "Bus service %s not loaded, refusing.", UNIT(service)->id);
return -ENOENT;
}
}
@@ -679,7 +693,7 @@ static int busname_deserialize_item(Unit *u, const char *key, const char *value,
state = busname_state_from_string(value);
if (state < 0)
- log_unit_debug(u->id, "Failed to parse state value %s", value);
+ log_unit_debug(u, "Failed to parse state value: %s", value);
else
n->deserialized_state = state;
@@ -688,7 +702,7 @@ static int busname_deserialize_item(Unit *u, const char *key, const char *value,
f = busname_result_from_string(value);
if (f < 0)
- log_unit_debug(u->id, "Failed to parse result value %s", value);
+ log_unit_debug(u, "Failed to parse result value: %s", value);
else if (f != BUSNAME_SUCCESS)
n->result = f;
@@ -696,20 +710,20 @@ static int busname_deserialize_item(Unit *u, const char *key, const char *value,
pid_t pid;
if (parse_pid(value, &pid) < 0)
- log_unit_debug(u->id, "Failed to parse control-pid value %s", value);
+ log_unit_debug(u, "Failed to parse control-pid value: %s", value);
else
n->control_pid = pid;
} else if (streq(key, "starter-fd")) {
int fd;
if (safe_atoi(value, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd))
- log_unit_debug(u->id, "Failed to parse starter fd value %s", value);
+ log_unit_debug(u, "Failed to parse starter fd value: %s", value);
else {
safe_close(n->starter_fd);
n->starter_fd = fdset_remove(fds, fd);
}
} else
- log_unit_debug(u->id, "Unknown serialization key '%s'", key);
+ log_unit_debug(u, "Unknown serialization key: %s", key);
return 0;
}
@@ -759,8 +773,7 @@ static int busname_peek_message(BusName *n) {
if (errno == EINTR || errno == EAGAIN)
return 0;
- log_unit_error(UNIT(n)->id, "%s: Failed to query activation message: %m", UNIT(n)->id);
- return -errno;
+ return log_unit_error_errno(UNIT(n), errno, "Failed to query activation message: %m");
}
/* We map as late as possible, and unmap imemdiately after
@@ -776,8 +789,7 @@ static int busname_peek_message(BusName *n) {
p = mmap(NULL, sz, PROT_READ, MAP_SHARED, n->starter_fd, start);
if (p == MAP_FAILED) {
- log_unit_error(UNIT(n)->id, "%s: Failed to map activation message: %m", UNIT(n)->id);
- r = -errno;
+ r = log_unit_error_errno(UNIT(n), errno, "Failed to map activation message: %m");
goto finish;
}
@@ -796,7 +808,7 @@ static int busname_peek_message(BusName *n) {
}
if (pid > 0)
- log_unit_debug(UNIT(n)->id, "%s: Activation triggered by process " PID_FMT " (%s)", UNIT(n)->id, pid, strna(comm));
+ log_unit_debug(UNIT(n), "Activation triggered by process " PID_FMT " (%s)", pid, strna(comm));
r = 0;
@@ -806,7 +818,7 @@ finish:
cmd_free.offset = cmd_recv.msg.offset;
if (ioctl(n->starter_fd, KDBUS_CMD_FREE, &cmd_free) < 0)
- log_unit_warning(UNIT(n)->id, "Failed to free peeked message, ignoring: %m");
+ log_unit_warning(UNIT(n), "Failed to free peeked message, ignoring: %m");
return r;
}
@@ -820,11 +832,10 @@ static int busname_dispatch_io(sd_event_source *source, int fd, uint32_t revents
if (n->state != BUSNAME_LISTENING)
return 0;
- log_unit_debug(UNIT(n)->id, "Activation request on %s", UNIT(n)->id);
+ log_unit_debug(UNIT(n), "Activation request");
if (revents != EPOLLIN) {
- log_unit_error(UNIT(n)->id, "%s: Got unexpected poll event (0x%x) on starter fd.",
- UNIT(n)->id, revents);
+ log_unit_error(UNIT(n), "Got unexpected poll event (0x%x) on starter fd.", revents);
goto fail;
}
@@ -860,10 +871,8 @@ static void busname_sigchld_event(Unit *u, pid_t pid, int code, int status) {
else
assert_not_reached("Unknown sigchld code");
- log_unit_full(u->id,
- f == BUSNAME_SUCCESS ? LOG_DEBUG : LOG_NOTICE,
- "%s control process exited, code=%s status=%i",
- u->id, sigchld_code_to_string(code), status);
+ log_unit_full(u, f == BUSNAME_SUCCESS ? LOG_DEBUG : LOG_NOTICE, 0,
+ "Control process exited, code=%s status=%i", sigchld_code_to_string(code), status);
if (f != BUSNAME_SUCCESS)
n->result = f;
@@ -899,17 +908,17 @@ static int busname_dispatch_timer(sd_event_source *source, usec_t usec, void *us
switch (n->state) {
case BUSNAME_MAKING:
- log_unit_warning(UNIT(n)->id, "%s making timed out. Terminating.", UNIT(n)->id);
+ log_unit_warning(UNIT(n), "Making timed out. Terminating.");
busname_enter_signal(n, BUSNAME_SIGTERM, BUSNAME_FAILURE_TIMEOUT);
break;
case BUSNAME_SIGTERM:
- log_unit_warning(UNIT(n)->id, "%s stopping timed out. Killing.", UNIT(n)->id);
+ log_unit_warning(UNIT(n), "Stopping timed out. Killing.");
busname_enter_signal(n, BUSNAME_SIGKILL, BUSNAME_FAILURE_TIMEOUT);
break;
case BUSNAME_SIGKILL:
- log_unit_warning(UNIT(n)->id, "%s still around after SIGKILL. Ignoring.", UNIT(n)->id);
+ log_unit_warning(UNIT(n), "Processes still around after SIGKILL. Ignoring.");
busname_enter_dead(n, BUSNAME_FAILURE_TIMEOUT);
break;
@@ -974,12 +983,11 @@ static int busname_get_timeout(Unit *u, uint64_t *timeout) {
return 1;
}
-static bool busname_supported(Manager *m) {
+static bool busname_supported(void) {
static int supported = -1;
- assert(m);
if (supported < 0)
- supported = access("/sys/fs/kdbus", F_OK) >= 0;
+ supported = is_kdbus_available();
return supported;
}
@@ -1018,6 +1026,9 @@ const UnitVTable busname_vtable = {
"Install\0",
.private_section = "BusName",
+ .no_alias = true,
+ .no_instances = true,
+
.init = busname_init,
.done = busname_done,
.load = busname_load,
diff --git a/src/core/busname.h b/src/core/busname.h
index 775822d8de..69528a2aef 100644
--- a/src/core/busname.h
+++ b/src/core/busname.h
@@ -24,7 +24,6 @@
typedef struct BusName BusName;
typedef struct BusNamePolicy BusNamePolicy;
-#include "unit.h"
typedef enum BusNameState {
BUSNAME_DEAD,
diff --git a/src/core/cgroup.c b/src/core/cgroup.c
index 10fdcc9984..6474e08bd2 100644
--- a/src/core/cgroup.c
+++ b/src/core/cgroup.c
@@ -22,6 +22,7 @@
#include <fcntl.h>
#include <fnmatch.h>
+#include "process-util.h"
#include "path-util.h"
#include "special.h"
#include "cgroup-util.h"
@@ -907,7 +908,7 @@ int manager_setup_cgroup(Manager *m) {
/* LEGACY: Already in /system.slice? If so, let's cut this
* off. This is to support live upgrades from older systemd
* versions where PID 1 was moved there. */
- if (m->running_as == SYSTEMD_SYSTEM) {
+ if (m->running_as == MANAGER_SYSTEM) {
char *e;
e = endswith(m->cgroup_root, "/" SPECIAL_SYSTEM_SLICE);
@@ -932,7 +933,7 @@ int manager_setup_cgroup(Manager *m) {
if (!m->test_run) {
/* 3. Install agent */
- if (m->running_as == SYSTEMD_SYSTEM) {
+ if (m->running_as == MANAGER_SYSTEM) {
r = cg_install_release_agent(SYSTEMD_CGROUP_CONTROLLER, SYSTEMD_CGROUP_AGENT_PATH);
if (r < 0)
log_warning_errno(r, "Failed to install release agent, ignoring: %m");
@@ -1029,16 +1030,100 @@ int manager_notify_cgroup_empty(Manager *m, const char *cgroup) {
assert(cgroup);
u = manager_get_unit_by_cgroup(m, cgroup);
- if (u) {
- r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, true);
- if (r > 0) {
- if (UNIT_VTABLE(u)->notify_cgroup_empty)
- UNIT_VTABLE(u)->notify_cgroup_empty(u);
+ if (!u)
+ return 0;
- unit_add_to_gc_queue(u);
- }
+ r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, true);
+ if (r <= 0)
+ return r;
+
+ if (UNIT_VTABLE(u)->notify_cgroup_empty)
+ UNIT_VTABLE(u)->notify_cgroup_empty(u);
+
+ unit_add_to_gc_queue(u);
+ return 0;
+}
+
+int unit_get_memory_current(Unit *u, uint64_t *ret) {
+ _cleanup_free_ char *v = NULL;
+ int r;
+
+ assert(u);
+ assert(ret);
+
+ if (!u->cgroup_path)
+ return -ENODATA;
+
+ if ((u->cgroup_realized_mask & CGROUP_MEMORY) == 0)
+ return -ENODATA;
+
+ r = cg_get_attribute("memory", u->cgroup_path, "memory.usage_in_bytes", &v);
+ if (r == -ENOENT)
+ return -ENODATA;
+ if (r < 0)
+ return r;
+
+ return safe_atou64(v, ret);
+}
+
+static int unit_get_cpu_usage_raw(Unit *u, nsec_t *ret) {
+ _cleanup_free_ char *v = NULL;
+ uint64_t ns;
+ int r;
+
+ assert(u);
+ assert(ret);
+
+ if (!u->cgroup_path)
+ return -ENODATA;
+
+ if ((u->cgroup_realized_mask & CGROUP_CPUACCT) == 0)
+ return -ENODATA;
+
+ r = cg_get_attribute("cpuacct", u->cgroup_path, "cpuacct.usage", &v);
+ if (r == -ENOENT)
+ return -ENODATA;
+ if (r < 0)
+ return r;
+
+ r = safe_atou64(v, &ns);
+ if (r < 0)
+ return r;
+
+ *ret = ns;
+ return 0;
+}
+
+int unit_get_cpu_usage(Unit *u, nsec_t *ret) {
+ nsec_t ns;
+ int r;
+
+ r = unit_get_cpu_usage_raw(u, &ns);
+ if (r < 0)
+ return r;
+
+ if (ns > u->cpuacct_usage_base)
+ ns -= u->cpuacct_usage_base;
+ else
+ ns = 0;
+
+ *ret = ns;
+ return 0;
+}
+
+int unit_reset_cpu_usage(Unit *u) {
+ nsec_t ns;
+ int r;
+
+ assert(u);
+
+ r = unit_get_cpu_usage_raw(u, &ns);
+ if (r < 0) {
+ u->cpuacct_usage_base = 0;
+ return r;
}
+ u->cpuacct_usage_base = ns;
return 0;
}
diff --git a/src/core/cgroup.h b/src/core/cgroup.h
index 8fa851de32..869ddae8c4 100644
--- a/src/core/cgroup.h
+++ b/src/core/cgroup.h
@@ -91,7 +91,6 @@ struct CGroupContext {
};
#include "unit.h"
-#include "manager.h"
#include "cgroup-util.h"
void cgroup_context_init(CGroupContext *c);
@@ -127,5 +126,9 @@ pid_t unit_search_main_pid(Unit *u);
int manager_notify_cgroup_empty(Manager *m, const char *group);
+int unit_get_memory_current(Unit *u, uint64_t *ret);
+int unit_get_cpu_usage(Unit *u, nsec_t *ret);
+int unit_reset_cpu_usage(Unit *u);
+
const char* cgroup_device_policy_to_string(CGroupDevicePolicy i) _const_;
CGroupDevicePolicy cgroup_device_policy_from_string(const char *s) _pure_;
diff --git a/src/core/dbus-automount.c b/src/core/dbus-automount.c
index b2a510ad09..5162ce34cb 100644
--- a/src/core/dbus-automount.c
+++ b/src/core/dbus-automount.c
@@ -19,9 +19,7 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "unit.h"
#include "automount.h"
-#include "dbus-unit.h"
#include "dbus-automount.h"
#include "bus-util.h"
@@ -32,5 +30,6 @@ const sd_bus_vtable bus_automount_vtable[] = {
SD_BUS_PROPERTY("Where", "s", NULL, offsetof(Automount, where), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("DirectoryMode", "u", bus_property_get_mode, offsetof(Automount, directory_mode), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Result", "s", property_get_result, offsetof(Automount, result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+ SD_BUS_PROPERTY("TimeoutIdleUSec", "t", bus_property_get_usec, offsetof(Automount, timeout_idle_usec), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_VTABLE_END
};
diff --git a/src/core/dbus-automount.h b/src/core/dbus-automount.h
index 1bec953788..a2b124d756 100644
--- a/src/core/dbus-automount.h
+++ b/src/core/dbus-automount.h
@@ -21,6 +21,5 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "sd-bus.h"
extern const sd_bus_vtable bus_automount_vtable[];
diff --git a/src/core/dbus-busname.c b/src/core/dbus-busname.c
index 28f192403b..b1ceb05b1a 100644
--- a/src/core/dbus-busname.c
+++ b/src/core/dbus-busname.c
@@ -21,7 +21,6 @@
#include "unit.h"
#include "busname.h"
-#include "dbus-unit.h"
#include "dbus-busname.h"
#include "bus-util.h"
diff --git a/src/core/dbus-busname.h b/src/core/dbus-busname.h
index b5eed37d58..ea55b6c8c9 100644
--- a/src/core/dbus-busname.h
+++ b/src/core/dbus-busname.h
@@ -21,7 +21,5 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "sd-bus.h"
-#include "unit.h"
extern const sd_bus_vtable bus_busname_vtable[];
diff --git a/src/core/dbus-device.c b/src/core/dbus-device.c
index f556536f33..cb156fd37c 100644
--- a/src/core/dbus-device.c
+++ b/src/core/dbus-device.c
@@ -21,7 +21,6 @@
#include "unit.h"
#include "device.h"
-#include "dbus-unit.h"
#include "dbus-device.h"
const sd_bus_vtable bus_device_vtable[] = {
diff --git a/src/core/dbus-device.h b/src/core/dbus-device.h
index 4aff226ab2..10e945e402 100644
--- a/src/core/dbus-device.h
+++ b/src/core/dbus-device.h
@@ -21,7 +21,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "sd-bus.h"
#include "unit.h"
extern const sd_bus_vtable bus_device_vtable[];
diff --git a/src/core/dbus-job.c b/src/core/dbus-job.c
index 8b5ea2566d..cd6b909426 100644
--- a/src/core/dbus-job.c
+++ b/src/core/dbus-job.c
@@ -29,22 +29,6 @@
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_type, job_type, JobType);
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_state, job_state, JobState);
-static int verify_sys_admin_or_owner_sync(sd_bus_message *message, Job *j, sd_bus_error *error) {
- int r;
-
- if (sd_bus_track_contains(j->clients, sd_bus_message_get_sender(message)))
- return 0; /* One of the job owners is calling us */
-
- r = sd_bus_query_sender_privilege(message, CAP_SYS_ADMIN);
- if (r < 0)
- return r;
- if (r == 0)
- return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Access denied to perform action");
-
- /* Root has called us */
- return 0;
-}
-
static int property_get_unit(
sd_bus *bus,
const char *path,
@@ -68,22 +52,28 @@ static int property_get_unit(
return sd_bus_message_append(reply, "(so)", j->unit->id, p);
}
-int bus_job_method_cancel(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+int bus_job_method_cancel(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Job *j = userdata;
int r;
- assert(bus);
assert(message);
assert(j);
- r = verify_sys_admin_or_owner_sync(message, j, error);
- if (r < 0)
- return r;
-
r = mac_selinux_unit_access_check(j->unit, message, "stop", error);
if (r < 0)
return r;
+ /* Access is granted to the job owner */
+ if (!sd_bus_track_contains(j->clients, sd_bus_message_get_sender(message))) {
+
+ /* And for everybody else consult PolicyKit */
+ r = bus_verify_manage_units_async(j->unit->manager, message, error);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
+ }
+
job_finish_and_invalidate(j, JOB_CANCELED, true);
return sd_bus_reply_method_return(message, NULL);
diff --git a/src/core/dbus-job.h b/src/core/dbus-job.h
index 6c2fc0789c..fb5f1b513e 100644
--- a/src/core/dbus-job.h
+++ b/src/core/dbus-job.h
@@ -26,7 +26,7 @@
extern const sd_bus_vtable bus_job_vtable[];
-int bus_job_method_cancel(sd_bus *bus, sd_bus_message *message, void *job, sd_bus_error *error);
+int bus_job_method_cancel(sd_bus_message *message, void *job, sd_bus_error *error);
void bus_job_send_change_signal(Job *j);
void bus_job_send_removed_signal(Job *j);
diff --git a/src/core/dbus-kill.c b/src/core/dbus-kill.c
index fb29e147cb..3b8116281c 100644
--- a/src/core/dbus-kill.c
+++ b/src/core/dbus-kill.c
@@ -19,9 +19,11 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include "signal-util.h"
+#include "bus-util.h"
+
#include "kill.h"
#include "dbus-kill.h"
-#include "bus-util.h"
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_kill_mode, kill_mode, KillMode);
diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c
index 8ba665dc3d..d8b39bdf5f 100644
--- a/src/core/dbus-manager.c
+++ b/src/core/dbus-manager.c
@@ -40,6 +40,7 @@
#include "dbus-snapshot.h"
#include "dbus-execute.h"
#include "bus-common-errors.h"
+#include "formats-util.h"
static int property_get_version(
sd_bus *bus,
@@ -342,14 +343,13 @@ static int property_set_runtime_watchdog(
return watchdog_set_timeout(t);
}
-static int method_get_unit(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_get_unit(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_free_ char *path = NULL;
Manager *m = userdata;
const char *name;
Unit *u;
int r;
- assert(bus);
assert(message);
assert(m);
@@ -359,9 +359,26 @@ static int method_get_unit(sd_bus *bus, sd_bus_message *message, void *userdata,
if (r < 0)
return r;
- u = manager_get_unit(m, name);
- if (!u)
- return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s not loaded.", name);
+ if (isempty(name)) {
+ _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
+ pid_t pid;
+
+ r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_PID, &creds);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_creds_get_pid(creds, &pid);
+ if (r < 0)
+ return r;
+
+ u = manager_get_unit_by_pid(m, pid);
+ if (!u)
+ return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_UNIT, "Client not member of any unit.");
+ } else {
+ u = manager_get_unit(m, name);
+ if (!u)
+ return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s not loaded.", name);
+ }
r = mac_selinux_unit_access_check(u, message, "status", error);
if (r < 0)
@@ -374,14 +391,13 @@ static int method_get_unit(sd_bus *bus, sd_bus_message *message, void *userdata,
return sd_bus_reply_method_return(message, "o", path);
}
-static int method_get_unit_by_pid(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_get_unit_by_pid(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_free_ char *path = NULL;
Manager *m = userdata;
pid_t pid;
Unit *u;
int r;
- assert(bus);
assert(message);
assert(m);
@@ -392,6 +408,8 @@ static int method_get_unit_by_pid(sd_bus *bus, sd_bus_message *message, void *us
r = sd_bus_message_read(message, "u", &pid);
if (r < 0)
return r;
+ if (pid < 0)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid PID " PID_FMT, pid);
if (pid == 0) {
_cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
@@ -420,14 +438,13 @@ static int method_get_unit_by_pid(sd_bus *bus, sd_bus_message *message, void *us
return sd_bus_reply_method_return(message, "o", path);
}
-static int method_load_unit(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_load_unit(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_free_ char *path = NULL;
Manager *m = userdata;
const char *name;
Unit *u;
int r;
- assert(bus);
assert(message);
assert(m);
@@ -437,9 +454,26 @@ static int method_load_unit(sd_bus *bus, sd_bus_message *message, void *userdata
if (r < 0)
return r;
- r = manager_load_unit(m, name, NULL, error, &u);
- if (r < 0)
- return r;
+ if (isempty(name)) {
+ _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
+ pid_t pid;
+
+ r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_PID, &creds);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_creds_get_pid(creds, &pid);
+ if (r < 0)
+ return r;
+
+ u = manager_get_unit_by_pid(m, pid);
+ if (!u)
+ return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_UNIT, "Client not member of any unit.");
+ } else {
+ r = manager_load_unit(m, name, NULL, error, &u);
+ if (r < 0)
+ return r;
+ }
r = mac_selinux_unit_access_check(u, message, "status", error);
if (r < 0)
@@ -452,21 +486,14 @@ static int method_load_unit(sd_bus *bus, sd_bus_message *message, void *userdata
return sd_bus_reply_method_return(message, "o", path);
}
-static int method_start_unit_generic(sd_bus *bus, sd_bus_message *message, Manager *m, JobType job_type, bool reload_if_possible, sd_bus_error *error) {
+static int method_start_unit_generic(sd_bus_message *message, Manager *m, JobType job_type, bool reload_if_possible, sd_bus_error *error) {
const char *name;
Unit *u;
int r;
- assert(bus);
assert(message);
assert(m);
- r = bus_verify_manage_unit_async(m, message, error);
- if (r < 0)
- return r;
- if (r == 0)
- return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
-
r = sd_bus_message_read(message, "s", &name);
if (r < 0)
return r;
@@ -475,53 +502,46 @@ static int method_start_unit_generic(sd_bus *bus, sd_bus_message *message, Manag
if (r < 0)
return r;
- return bus_unit_method_start_generic(bus, message, u, job_type, reload_if_possible, error);
+ return bus_unit_method_start_generic(message, u, job_type, reload_if_possible, error);
}
-static int method_start_unit(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
- return method_start_unit_generic(bus, message, userdata, JOB_START, false, error);
+static int method_start_unit(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ return method_start_unit_generic(message, userdata, JOB_START, false, error);
}
-static int method_stop_unit(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
- return method_start_unit_generic(bus, message, userdata, JOB_STOP, false, error);
+static int method_stop_unit(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ return method_start_unit_generic(message, userdata, JOB_STOP, false, error);
}
-static int method_reload_unit(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
- return method_start_unit_generic(bus, message, userdata, JOB_RELOAD, false, error);
+static int method_reload_unit(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ return method_start_unit_generic(message, userdata, JOB_RELOAD, false, error);
}
-static int method_restart_unit(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
- return method_start_unit_generic(bus, message, userdata, JOB_RESTART, false, error);
+static int method_restart_unit(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ return method_start_unit_generic(message, userdata, JOB_RESTART, false, error);
}
-static int method_try_restart_unit(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
- return method_start_unit_generic(bus, message, userdata, JOB_TRY_RESTART, false, error);
+static int method_try_restart_unit(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ return method_start_unit_generic(message, userdata, JOB_TRY_RESTART, false, error);
}
-static int method_reload_or_restart_unit(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
- return method_start_unit_generic(bus, message, userdata, JOB_RESTART, true, error);
+static int method_reload_or_restart_unit(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ return method_start_unit_generic(message, userdata, JOB_RESTART, true, error);
}
-static int method_reload_or_try_restart_unit(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
- return method_start_unit_generic(bus, message, userdata, JOB_TRY_RESTART, true, error);
+static int method_reload_or_try_restart_unit(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ return method_start_unit_generic(message, userdata, JOB_TRY_RESTART, true, error);
}
-static int method_start_unit_replace(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_start_unit_replace(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Manager *m = userdata;
const char *old_name;
Unit *u;
int r;
- assert(bus);
assert(message);
assert(m);
- r = bus_verify_manage_unit_async(m, message, error);
- if (r < 0)
- return r;
- if (r == 0)
- return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
-
r = sd_bus_message_read(message, "s", &old_name);
if (r < 0)
return r;
@@ -530,26 +550,18 @@ static int method_start_unit_replace(sd_bus *bus, sd_bus_message *message, void
if (!u || !u->job || u->job->type != JOB_START)
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_JOB, "No job queued for unit %s", old_name);
- return method_start_unit_generic(bus, message, m, JOB_START, false, error);
+ return method_start_unit_generic(message, m, JOB_START, false, error);
}
-static int method_kill_unit(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_kill_unit(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Manager *m = userdata;
const char *name;
Unit *u;
int r;
- assert(bus);
assert(message);
assert(m);
- /* Like bus_verify_manage_unit_async(), but uses CAP_SYS_KILL */
- r = bus_verify_manage_unit_async_for_kill(m, message, error);
- if (r < 0)
- return r;
- if (r == 0)
- return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
-
r = sd_bus_message_read(message, "s", &name);
if (r < 0)
return r;
@@ -558,25 +570,18 @@ static int method_kill_unit(sd_bus *bus, sd_bus_message *message, void *userdata
if (!u)
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s is not loaded.", name);
- return bus_unit_method_kill(bus, message, u, error);
+ return bus_unit_method_kill(message, u, error);
}
-static int method_reset_failed_unit(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_reset_failed_unit(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Manager *m = userdata;
const char *name;
Unit *u;
int r;
- assert(bus);
assert(message);
assert(m);
- r = bus_verify_manage_unit_async(m, message, error);
- if (r < 0)
- return r;
- if (r == 0)
- return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
-
r = sd_bus_message_read(message, "s", &name);
if (r < 0)
return r;
@@ -585,25 +590,18 @@ static int method_reset_failed_unit(sd_bus *bus, sd_bus_message *message, void *
if (!u)
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s is not loaded.", name);
- return bus_unit_method_reset_failed(bus, message, u, error);
+ return bus_unit_method_reset_failed(message, u, error);
}
-static int method_set_unit_properties(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_set_unit_properties(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Manager *m = userdata;
const char *name;
Unit *u;
int r;
- assert(bus);
assert(message);
assert(m);
- r = bus_verify_manage_unit_async(m, message, error);
- if (r < 0)
- return r;
- if (r == 0)
- return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
-
r = sd_bus_message_read(message, "s", &name);
if (r < 0)
return r;
@@ -612,7 +610,7 @@ static int method_set_unit_properties(sd_bus *bus, sd_bus_message *message, void
if (!u)
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s is not loaded.", name);
- return bus_unit_method_set_properties(bus, message, u, error);
+ return bus_unit_method_set_properties(message, u, error);
}
static int transient_unit_from_message(
@@ -670,9 +668,6 @@ static int transient_aux_units_from_message(
return r;
while ((r = sd_bus_message_enter_container(message, 'r', "sa(sv)")) > 0) {
- if (r <= 0)
- return r;
-
r = sd_bus_message_read(message, "s", &name);
if (r < 0)
return r;
@@ -701,7 +696,7 @@ static int transient_aux_units_from_message(
return 0;
}
-static int method_start_transient_unit(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_start_transient_unit(sd_bus_message *message, void *userdata, sd_bus_error *error) {
const char *name, *smode;
Manager *m = userdata;
JobMode mode;
@@ -709,15 +704,12 @@ static int method_start_transient_unit(sd_bus *bus, sd_bus_message *message, voi
Unit *u;
int r;
- assert(bus);
assert(message);
assert(m);
- r = bus_verify_manage_unit_async(m, message, error);
+ r = mac_selinux_access_check(message, "start", error);
if (r < 0)
return r;
- if (r == 0)
- return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
r = sd_bus_message_read(message, "ss", &name, &smode);
if (r < 0)
@@ -734,9 +726,11 @@ static int method_start_transient_unit(sd_bus *bus, sd_bus_message *message, voi
if (mode < 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Job mode %s is invalid.", smode);
- r = mac_selinux_access_check(message, "start", error);
+ r = bus_verify_manage_units_async(m, message, error);
if (r < 0)
return r;
+ if (r == 0)
+ return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
r = transient_unit_from_message(m, message, name, &u, error);
if (r < 0)
@@ -754,17 +748,16 @@ static int method_start_transient_unit(sd_bus *bus, sd_bus_message *message, voi
manager_dispatch_load_queue(m);
/* Finally, start it */
- return bus_unit_queue_job(bus, message, u, JOB_START, mode, false, error);
+ return bus_unit_queue_job(message, u, JOB_START, mode, false, error);
}
-static int method_get_job(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_get_job(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_free_ char *path = NULL;
Manager *m = userdata;
uint32_t id;
Job *j;
int r;
- assert(bus);
assert(message);
assert(m);
@@ -789,13 +782,12 @@ static int method_get_job(sd_bus *bus, sd_bus_message *message, void *userdata,
return sd_bus_reply_method_return(message, "o", path);
}
-static int method_cancel_job(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_cancel_job(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Manager *m = userdata;
uint32_t id;
Job *j;
int r;
- assert(bus);
assert(message);
assert(m);
@@ -807,31 +799,35 @@ static int method_cancel_job(sd_bus *bus, sd_bus_message *message, void *userdat
if (!j)
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_JOB, "Job %u does not exist.", (unsigned) id);
- return bus_job_method_cancel(bus, message, j, error);
+ return bus_job_method_cancel(message, j, error);
}
-static int method_clear_jobs(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_clear_jobs(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Manager *m = userdata;
int r;
- assert(bus);
assert(message);
assert(m);
- r = mac_selinux_access_check(message, "reboot", error);
+ r = mac_selinux_access_check(message, "reload", error);
+ if (r < 0)
+ return r;
+
+ r = bus_verify_manage_units_async(m, message, error);
if (r < 0)
return r;
+ if (r == 0)
+ return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
manager_clear_jobs(m);
return sd_bus_reply_method_return(message, NULL);
}
-static int method_reset_failed(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_reset_failed(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Manager *m = userdata;
int r;
- assert(bus);
assert(message);
assert(m);
@@ -839,12 +835,18 @@ static int method_reset_failed(sd_bus *bus, sd_bus_message *message, void *userd
if (r < 0)
return r;
+ r = bus_verify_manage_units_async(m, message, error);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
+
manager_reset_failed(m);
return sd_bus_reply_method_return(message, NULL);
}
-static int list_units_filtered(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error, char **states) {
+static int list_units_filtered(sd_bus_message *message, void *userdata, sd_bus_error *error, char **states) {
_cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
Manager *m = userdata;
const char *k;
@@ -852,7 +854,6 @@ static int list_units_filtered(sd_bus *bus, sd_bus_message *message, void *userd
Unit *u;
int r;
- assert(bus);
assert(message);
assert(m);
@@ -915,14 +916,14 @@ static int list_units_filtered(sd_bus *bus, sd_bus_message *message, void *userd
if (r < 0)
return r;
- return sd_bus_send(bus, reply, NULL);
+ return sd_bus_send(NULL, reply, NULL);
}
-static int method_list_units(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
- return list_units_filtered(bus, message, userdata, error, NULL);
+static int method_list_units(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ return list_units_filtered(message, userdata, error, NULL);
}
-static int method_list_units_filtered(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_list_units_filtered(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_strv_free_ char **states = NULL;
int r;
@@ -930,17 +931,16 @@ static int method_list_units_filtered(sd_bus *bus, sd_bus_message *message, void
if (r < 0)
return r;
- return list_units_filtered(bus, message, userdata, error, states);
+ return list_units_filtered(message, userdata, error, states);
}
-static int method_list_jobs(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_list_jobs(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
Manager *m = userdata;
Iterator i;
Job *j;
int r;
- assert(bus);
assert(message);
assert(m);
@@ -985,14 +985,13 @@ static int method_list_jobs(sd_bus *bus, sd_bus_message *message, void *userdata
if (r < 0)
return r;
- return sd_bus_send(bus, reply, NULL);
+ return sd_bus_send(NULL, reply, NULL);
}
-static int method_subscribe(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_subscribe(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Manager *m = userdata;
int r;
- assert(bus);
assert(message);
assert(m);
@@ -1002,13 +1001,13 @@ static int method_subscribe(sd_bus *bus, sd_bus_message *message, void *userdata
if (r < 0)
return r;
- if (bus == m->api_bus) {
+ if (sd_bus_message_get_bus(message) == m->api_bus) {
/* Note that direct bus connection subscribe by
* default, we only track peers on the API bus here */
if (!m->subscribed) {
- r = sd_bus_track_new(bus, &m->subscribed, NULL, NULL);
+ r = sd_bus_track_new(sd_bus_message_get_bus(message), &m->subscribed, NULL, NULL);
if (r < 0)
return r;
}
@@ -1023,11 +1022,10 @@ static int method_subscribe(sd_bus *bus, sd_bus_message *message, void *userdata
return sd_bus_reply_method_return(message, NULL);
}
-static int method_unsubscribe(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_unsubscribe(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Manager *m = userdata;
int r;
- assert(bus);
assert(message);
assert(m);
@@ -1037,7 +1035,7 @@ static int method_unsubscribe(sd_bus *bus, sd_bus_message *message, void *userda
if (r < 0)
return r;
- if (bus == m->api_bus) {
+ if (sd_bus_message_get_bus(message) == m->api_bus) {
r = sd_bus_track_remove_sender(m->subscribed, message);
if (r < 0)
return r;
@@ -1048,14 +1046,13 @@ static int method_unsubscribe(sd_bus *bus, sd_bus_message *message, void *userda
return sd_bus_reply_method_return(message, NULL);
}
-static int method_dump(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_dump(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_free_ char *dump = NULL;
_cleanup_fclose_ FILE *f = NULL;
Manager *m = userdata;
size_t size;
int r;
- assert(bus);
assert(message);
assert(m);
@@ -1080,7 +1077,7 @@ static int method_dump(sd_bus *bus, sd_bus_message *message, void *userdata, sd_
return sd_bus_reply_method_return(message, "s", dump);
}
-static int method_create_snapshot(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_create_snapshot(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_free_ char *path = NULL;
Manager *m = userdata;
const char *name;
@@ -1088,7 +1085,6 @@ static int method_create_snapshot(sd_bus *bus, sd_bus_message *message, void *us
Snapshot *s = NULL;
int r;
- assert(bus);
assert(message);
assert(m);
@@ -1103,6 +1099,12 @@ static int method_create_snapshot(sd_bus *bus, sd_bus_message *message, void *us
if (isempty(name))
name = NULL;
+ r = bus_verify_manage_units_async(m, message, error);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
+
r = snapshot_create(m, name, cleanup, error, &s);
if (r < 0)
return r;
@@ -1114,20 +1116,15 @@ static int method_create_snapshot(sd_bus *bus, sd_bus_message *message, void *us
return sd_bus_reply_method_return(message, "o", path);
}
-static int method_remove_snapshot(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_remove_snapshot(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Manager *m = userdata;
const char *name;
Unit *u;
int r;
- assert(bus);
assert(message);
assert(m);
- r = mac_selinux_access_check(message, "stop", error);
- if (r < 0)
- return r;
-
r = sd_bus_message_read(message, "s", &name);
if (r < 0)
return r;
@@ -1139,26 +1136,25 @@ static int method_remove_snapshot(sd_bus *bus, sd_bus_message *message, void *us
if (u->type != UNIT_SNAPSHOT)
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s is not a snapshot", name);
- return bus_snapshot_method_remove(bus, message, u, error);
+ return bus_snapshot_method_remove(message, u, error);
}
-static int method_reload(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_reload(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Manager *m = userdata;
int r;
- assert(bus);
assert(message);
assert(m);
- r = bus_verify_reload_daemon_async(m, message, error);
+ r = mac_selinux_access_check(message, "reload", error);
if (r < 0)
return r;
- if (r == 0)
- return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
- r = mac_selinux_access_check(message, "reload", error);
+ r = bus_verify_reload_daemon_async(m, message, error);
if (r < 0)
return r;
+ if (r == 0)
+ return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
/* Instead of sending the reply back right away, we just
* remember that we need to and then send it after the reload
@@ -1170,29 +1166,27 @@ static int method_reload(sd_bus *bus, sd_bus_message *message, void *userdata, s
if (r < 0)
return r;
- m->queued_message_bus = sd_bus_ref(bus);
m->exit_code = MANAGER_RELOAD;
return 1;
}
-static int method_reexecute(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_reexecute(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Manager *m = userdata;
int r;
- assert(bus);
assert(message);
assert(m);
- r = bus_verify_reload_daemon_async(m, message, error);
+ r = mac_selinux_access_check(message, "reload", error);
if (r < 0)
return r;
- if (r == 0)
- return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
- r = mac_selinux_access_check(message, "reload", error);
+ r = bus_verify_reload_daemon_async(m, message, error);
if (r < 0)
return r;
+ if (r == 0)
+ return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
/* We don't send a reply back here, the client should
* just wait for us disconnecting. */
@@ -1201,11 +1195,10 @@ static int method_reexecute(sd_bus *bus, sd_bus_message *message, void *userdata
return 1;
}
-static int method_exit(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_exit(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Manager *m = userdata;
int r;
- assert(bus);
assert(message);
assert(m);
@@ -1213,7 +1206,7 @@ static int method_exit(sd_bus *bus, sd_bus_message *message, void *userdata, sd_
if (r < 0)
return r;
- if (m->running_as == SYSTEMD_SYSTEM)
+ if (m->running_as == MANAGER_SYSTEM)
return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Exit is only supported for user service managers.");
m->exit_code = MANAGER_EXIT;
@@ -1221,11 +1214,10 @@ static int method_exit(sd_bus *bus, sd_bus_message *message, void *userdata, sd_
return sd_bus_reply_method_return(message, NULL);
}
-static int method_reboot(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_reboot(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Manager *m = userdata;
int r;
- assert(bus);
assert(message);
assert(m);
@@ -1233,7 +1225,7 @@ static int method_reboot(sd_bus *bus, sd_bus_message *message, void *userdata, s
if (r < 0)
return r;
- if (m->running_as != SYSTEMD_SYSTEM)
+ if (m->running_as != MANAGER_SYSTEM)
return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Reboot is only supported for system managers.");
m->exit_code = MANAGER_REBOOT;
@@ -1241,12 +1233,10 @@ static int method_reboot(sd_bus *bus, sd_bus_message *message, void *userdata, s
return sd_bus_reply_method_return(message, NULL);
}
-
-static int method_poweroff(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_poweroff(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Manager *m = userdata;
int r;
- assert(bus);
assert(message);
assert(m);
@@ -1254,7 +1244,7 @@ static int method_poweroff(sd_bus *bus, sd_bus_message *message, void *userdata,
if (r < 0)
return r;
- if (m->running_as != SYSTEMD_SYSTEM)
+ if (m->running_as != MANAGER_SYSTEM)
return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Powering off is only supported for system managers.");
m->exit_code = MANAGER_POWEROFF;
@@ -1262,11 +1252,10 @@ static int method_poweroff(sd_bus *bus, sd_bus_message *message, void *userdata,
return sd_bus_reply_method_return(message, NULL);
}
-static int method_halt(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_halt(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Manager *m = userdata;
int r;
- assert(bus);
assert(message);
assert(m);
@@ -1274,7 +1263,7 @@ static int method_halt(sd_bus *bus, sd_bus_message *message, void *userdata, sd_
if (r < 0)
return r;
- if (m->running_as != SYSTEMD_SYSTEM)
+ if (m->running_as != MANAGER_SYSTEM)
return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Halt is only supported for system managers.");
m->exit_code = MANAGER_HALT;
@@ -1282,11 +1271,10 @@ static int method_halt(sd_bus *bus, sd_bus_message *message, void *userdata, sd_
return sd_bus_reply_method_return(message, NULL);
}
-static int method_kexec(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_kexec(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Manager *m = userdata;
int r;
- assert(bus);
assert(message);
assert(m);
@@ -1294,7 +1282,7 @@ static int method_kexec(sd_bus *bus, sd_bus_message *message, void *userdata, sd
if (r < 0)
return r;
- if (m->running_as != SYSTEMD_SYSTEM)
+ if (m->running_as != MANAGER_SYSTEM)
return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "KExec is only supported for system managers.");
m->exit_code = MANAGER_KEXEC;
@@ -1302,13 +1290,12 @@ static int method_kexec(sd_bus *bus, sd_bus_message *message, void *userdata, sd
return sd_bus_reply_method_return(message, NULL);
}
-static int method_switch_root(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_switch_root(sd_bus_message *message, void *userdata, sd_bus_error *error) {
char *ri = NULL, *rt = NULL;
const char *root, *init;
Manager *m = userdata;
int r;
- assert(bus);
assert(message);
assert(m);
@@ -1316,7 +1303,7 @@ static int method_switch_root(sd_bus *bus, sd_bus_message *message, void *userda
if (r < 0)
return r;
- if (m->running_as != SYSTEMD_SYSTEM)
+ if (m->running_as != MANAGER_SYSTEM)
return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Root switching is only supported by system manager.");
r = sd_bus_message_read(message, "ss", &root, &init);
@@ -1367,12 +1354,11 @@ static int method_switch_root(sd_bus *bus, sd_bus_message *message, void *userda
return sd_bus_reply_method_return(message, NULL);
}
-static int method_set_environment(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_set_environment(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_strv_free_ char **plus = NULL;
Manager *m = userdata;
int r;
- assert(bus);
assert(message);
assert(m);
@@ -1386,6 +1372,12 @@ static int method_set_environment(sd_bus *bus, sd_bus_message *message, void *us
if (!strv_env_is_valid(plus))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid environment assignments");
+ r = bus_verify_set_environment_async(m, message, error);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
+
r = manager_environment_add(m, NULL, plus);
if (r < 0)
return r;
@@ -1393,12 +1385,11 @@ static int method_set_environment(sd_bus *bus, sd_bus_message *message, void *us
return sd_bus_reply_method_return(message, NULL);
}
-static int method_unset_environment(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_unset_environment(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_strv_free_ char **minus = NULL;
Manager *m = userdata;
int r;
- assert(bus);
assert(message);
assert(m);
@@ -1413,6 +1404,12 @@ static int method_unset_environment(sd_bus *bus, sd_bus_message *message, void *
if (!strv_env_name_or_assignment_is_valid(minus))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid environment variable names or assignments");
+ r = bus_verify_set_environment_async(m, message, error);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
+
r = manager_environment_add(m, minus, NULL);
if (r < 0)
return r;
@@ -1420,12 +1417,11 @@ static int method_unset_environment(sd_bus *bus, sd_bus_message *message, void *
return sd_bus_reply_method_return(message, NULL);
}
-static int method_unset_and_set_environment(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_unset_and_set_environment(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_strv_free_ char **minus = NULL, **plus = NULL;
Manager *m = userdata;
int r;
- assert(bus);
assert(message);
assert(m);
@@ -1446,6 +1442,12 @@ static int method_unset_and_set_environment(sd_bus *bus, sd_bus_message *message
if (!strv_env_is_valid(plus))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid environment assignments");
+ r = bus_verify_set_environment_async(m, message, error);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
+
r = manager_environment_add(m, minus, plus);
if (r < 0)
return r;
@@ -1453,7 +1455,7 @@ static int method_unset_and_set_environment(sd_bus *bus, sd_bus_message *message
return sd_bus_reply_method_return(message, NULL);
}
-static int method_list_unit_files(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_list_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
Manager *m = userdata;
UnitFileList *item;
@@ -1461,7 +1463,6 @@ static int method_list_unit_files(sd_bus *bus, sd_bus_message *message, void *us
Iterator i;
int r;
- assert(bus);
assert(message);
assert(m);
@@ -1479,7 +1480,7 @@ static int method_list_unit_files(sd_bus *bus, sd_bus_message *message, void *us
if (!h)
return -ENOMEM;
- r = unit_file_get_list(m->running_as == SYSTEMD_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER, NULL, h);
+ r = unit_file_get_list(m->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER, NULL, h);
if (r < 0)
goto fail;
@@ -1500,21 +1501,20 @@ static int method_list_unit_files(sd_bus *bus, sd_bus_message *message, void *us
if (r < 0)
return r;
- return sd_bus_send(bus, reply, NULL);
+ return sd_bus_send(NULL, reply, NULL);
fail:
unit_file_list_free(h);
return r;
}
-static int method_get_unit_file_state(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_get_unit_file_state(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Manager *m = userdata;
const char *name;
UnitFileState state;
UnitFileScope scope;
int r;
- assert(bus);
assert(message);
assert(m);
@@ -1528,7 +1528,7 @@ static int method_get_unit_file_state(sd_bus *bus, sd_bus_message *message, void
if (r < 0)
return r;
- scope = m->running_as == SYSTEMD_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER;
+ scope = m->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER;
state = unit_file_get_state(scope, NULL, name);
if (state < 0)
@@ -1537,13 +1537,12 @@ static int method_get_unit_file_state(sd_bus *bus, sd_bus_message *message, void
return sd_bus_reply_method_return(message, "s", unit_file_state_to_string(state));
}
-static int method_get_default_target(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_get_default_target(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_free_ char *default_target = NULL;
Manager *m = userdata;
UnitFileScope scope;
int r;
- assert(bus);
assert(message);
assert(m);
@@ -1553,7 +1552,7 @@ static int method_get_default_target(sd_bus *bus, sd_bus_message *message, void
if (r < 0)
return r;
- scope = m->running_as == SYSTEMD_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER;
+ scope = m->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER;
r = unit_file_get_default(scope, NULL, &default_target);
if (r < 0)
@@ -1577,7 +1576,6 @@ static int send_unit_files_changed(sd_bus *bus, void *userdata) {
static int reply_unit_file_changes_and_free(
Manager *m,
- sd_bus *bus,
sd_bus_message *message,
int carries_install_info,
UnitFileChange *changes,
@@ -1621,7 +1619,7 @@ static int reply_unit_file_changes_and_free(
if (r < 0)
goto fail;
- return sd_bus_send(bus, reply, NULL);
+ return sd_bus_send(NULL, reply, NULL);
fail:
unit_file_changes_free(changes, n_changes);
@@ -1629,7 +1627,6 @@ fail:
}
static int method_enable_unit_files_generic(
- sd_bus *bus,
sd_bus_message *message,
Manager *m,
const char *verb,
@@ -1643,16 +1640,9 @@ static int method_enable_unit_files_generic(
UnitFileScope scope;
int runtime, force, r;
- assert(bus);
assert(message);
assert(m);
- r = bus_verify_manage_unit_files_async(m, message, error);
- if (r < 0)
- return r;
- if (r == 0)
- return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
-
r = sd_bus_message_read_strv(message, &l);
if (r < 0)
return r;
@@ -1665,40 +1655,46 @@ static int method_enable_unit_files_generic(
if (r < 0)
return r;
- scope = m->running_as == SYSTEMD_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER;
+ r = bus_verify_manage_unit_files_async(m, message, error);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
+
+ scope = m->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER;
r = call(scope, runtime, NULL, l, force, &changes, &n_changes);
if (r < 0)
return r;
- return reply_unit_file_changes_and_free(m, bus, message, carries_install_info ? r : -1, changes, n_changes);
+ return reply_unit_file_changes_and_free(m, message, carries_install_info ? r : -1, changes, n_changes);
}
-static int method_enable_unit_files(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
- return method_enable_unit_files_generic(bus, message, userdata, "enable", unit_file_enable, true, error);
+static int method_enable_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ return method_enable_unit_files_generic(message, userdata, "enable", unit_file_enable, true, error);
}
-static int method_reenable_unit_files(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
- return method_enable_unit_files_generic(bus, message, userdata, "enable", unit_file_reenable, true, error);
+static int method_reenable_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ return method_enable_unit_files_generic(message, userdata, "enable", unit_file_reenable, true, error);
}
-static int method_link_unit_files(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
- return method_enable_unit_files_generic(bus, message, userdata, "enable", unit_file_link, false, error);
+static int method_link_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ return method_enable_unit_files_generic(message, userdata, "enable", unit_file_link, false, error);
}
static int unit_file_preset_without_mode(UnitFileScope scope, bool runtime, const char *root_dir, char **files, bool force, UnitFileChange **changes, unsigned *n_changes) {
return unit_file_preset(scope, runtime, root_dir, files, UNIT_FILE_PRESET_FULL, force, changes, n_changes);
}
-static int method_preset_unit_files(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
- return method_enable_unit_files_generic(bus, message, userdata, "enable", unit_file_preset_without_mode, true, error);
+static int method_preset_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ return method_enable_unit_files_generic(message, userdata, "enable", unit_file_preset_without_mode, true, error);
}
-static int method_mask_unit_files(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
- return method_enable_unit_files_generic(bus, message, userdata, "disable", unit_file_mask, false, error);
+static int method_mask_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ return method_enable_unit_files_generic(message, userdata, "disable", unit_file_mask, false, error);
}
-static int method_preset_unit_files_with_mode(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_preset_unit_files_with_mode(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_strv_free_ char **l = NULL;
UnitFileChange *changes = NULL;
@@ -1709,16 +1705,9 @@ static int method_preset_unit_files_with_mode(sd_bus *bus, sd_bus_message *messa
int runtime, force, r;
const char *mode;
- assert(bus);
assert(message);
assert(m);
- r = bus_verify_manage_unit_files_async(m, message, error);
- if (r < 0)
- return r;
- if (r == 0)
- return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
-
r = sd_bus_message_read_strv(message, &l);
if (r < 0)
return r;
@@ -1739,17 +1728,22 @@ static int method_preset_unit_files_with_mode(sd_bus *bus, sd_bus_message *messa
if (r < 0)
return r;
- scope = m->running_as == SYSTEMD_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER;
+ r = bus_verify_manage_unit_files_async(m, message, error);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
+
+ scope = m->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER;
r = unit_file_preset(scope, runtime, NULL, l, mm, force, &changes, &n_changes);
if (r < 0)
return r;
- return reply_unit_file_changes_and_free(m, bus, message, r, changes, n_changes);
+ return reply_unit_file_changes_and_free(m, message, r, changes, n_changes);
}
static int method_disable_unit_files_generic(
- sd_bus *bus,
sd_bus_message *message,
Manager *m, const
char *verb,
@@ -1762,46 +1756,45 @@ static int method_disable_unit_files_generic(
UnitFileScope scope;
int r, runtime;
- assert(bus);
assert(message);
assert(m);
- r = bus_verify_manage_unit_files_async(m, message, error);
+ r = sd_bus_message_read_strv(message, &l);
if (r < 0)
return r;
- if (r == 0)
- return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
- r = mac_selinux_access_check(message, verb, error);
+ r = sd_bus_message_read(message, "b", &runtime);
if (r < 0)
return r;
- r = sd_bus_message_read_strv(message, &l);
+ r = mac_selinux_unit_access_check_strv(l, message, m, verb, error);
if (r < 0)
return r;
- r = sd_bus_message_read(message, "b", &runtime);
+ scope = m->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER;
+
+ r = bus_verify_manage_unit_files_async(m, message, error);
if (r < 0)
return r;
-
- scope = m->running_as == SYSTEMD_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER;
+ if (r == 0)
+ return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
r = call(scope, runtime, NULL, l, &changes, &n_changes);
if (r < 0)
return r;
- return reply_unit_file_changes_and_free(m, bus, message, -1, changes, n_changes);
+ return reply_unit_file_changes_and_free(m, message, -1, changes, n_changes);
}
-static int method_disable_unit_files(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
- return method_disable_unit_files_generic(bus, message, userdata, "disable", unit_file_disable, error);
+static int method_disable_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ return method_disable_unit_files_generic(message, userdata, "disable", unit_file_disable, error);
}
-static int method_unmask_unit_files(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
- return method_disable_unit_files_generic(bus, message, userdata, "enable", unit_file_unmask, error);
+static int method_unmask_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ return method_disable_unit_files_generic(message, userdata, "enable", unit_file_unmask, error);
}
-static int method_set_default_target(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_set_default_target(sd_bus_message *message, void *userdata, sd_bus_error *error) {
UnitFileChange *changes = NULL;
unsigned n_changes = 0;
Manager *m = userdata;
@@ -1809,34 +1802,33 @@ static int method_set_default_target(sd_bus *bus, sd_bus_message *message, void
const char *name;
int force, r;
- assert(bus);
assert(message);
assert(m);
- r = bus_verify_manage_unit_files_async(m, message, error);
+ r = mac_selinux_access_check(message, "enable", error);
if (r < 0)
return r;
- if (r == 0)
- return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
- r = mac_selinux_access_check(message, "enable", error);
+ r = sd_bus_message_read(message, "sb", &name, &force);
if (r < 0)
return r;
- r = sd_bus_message_read(message, "sb", &name, &force);
+ r = bus_verify_manage_unit_files_async(m, message, error);
if (r < 0)
return r;
+ if (r == 0)
+ return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
- scope = m->running_as == SYSTEMD_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER;
+ scope = m->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER;
r = unit_file_set_default(scope, NULL, name, force, &changes, &n_changes);
if (r < 0)
return r;
- return reply_unit_file_changes_and_free(m, bus, message, -1, changes, n_changes);
+ return reply_unit_file_changes_and_free(m, message, -1, changes, n_changes);
}
-static int method_preset_all_unit_files(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_preset_all_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) {
UnitFileChange *changes = NULL;
unsigned n_changes = 0;
Manager *m = userdata;
@@ -1845,16 +1837,9 @@ static int method_preset_all_unit_files(sd_bus *bus, sd_bus_message *message, vo
const char *mode;
int force, runtime, r;
- assert(bus);
assert(message);
assert(m);
- r = bus_verify_manage_unit_files_async(m, message, error);
- if (r < 0)
- return r;
- if (r == 0)
- return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
-
r = mac_selinux_access_check(message, "enable", error);
if (r < 0)
return r;
@@ -1871,16 +1856,22 @@ static int method_preset_all_unit_files(sd_bus *bus, sd_bus_message *message, vo
return -EINVAL;
}
- scope = m->running_as == SYSTEMD_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER;
+ r = bus_verify_manage_unit_files_async(m, message, error);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
+
+ scope = m->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER;
r = unit_file_preset_all(scope, runtime, NULL, mm, force, &changes, &n_changes);
if (r < 0)
return r;
- return reply_unit_file_changes_and_free(m, bus, message, -1, changes, n_changes);
+ return reply_unit_file_changes_and_free(m, message, -1, changes, n_changes);
}
-static int method_add_dependency_unit_files(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_add_dependency_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_strv_free_ char **l = NULL;
Manager *m = userdata;
UnitFileChange *changes = NULL;
@@ -1891,7 +1882,6 @@ static int method_add_dependency_unit_files(sd_bus *bus, sd_bus_message *message
char *type;
UnitDependency dep;
- assert(bus);
assert(message);
assert(m);
@@ -1917,13 +1907,13 @@ static int method_add_dependency_unit_files(sd_bus *bus, sd_bus_message *message
if (r < 0)
return r;
- scope = m->running_as == SYSTEMD_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER;
+ scope = m->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER;
r = unit_file_add_dependency(scope, runtime, NULL, l, target, dep, force, &changes, &n_changes);
if (r < 0)
return r;
- return reply_unit_file_changes_and_free(m, bus, message, -1, changes, n_changes);
+ return reply_unit_file_changes_and_free(m, message, -1, changes, n_changes);
}
const sd_bus_vtable bus_manager_vtable[] = {
@@ -1949,7 +1939,7 @@ const sd_bus_vtable bus_manager_vtable[] = {
SD_BUS_WRITABLE_PROPERTY("LogLevel", "s", property_get_log_level, property_set_log_level, 0, 0),
SD_BUS_WRITABLE_PROPERTY("LogTarget", "s", property_get_log_target, property_set_log_target, 0, 0),
SD_BUS_PROPERTY("NNames", "u", property_get_n_names, 0, 0),
- SD_BUS_PROPERTY("NFailedUnits", "u", property_get_n_failed_units, 0, 0),
+ SD_BUS_PROPERTY("NFailedUnits", "u", property_get_n_failed_units, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("NJobs", "u", property_get_n_jobs, 0, 0),
SD_BUS_PROPERTY("NInstalledJobs", "u", bus_property_get_unsigned, offsetof(Manager, n_installed_jobs), 0),
SD_BUS_PROPERTY("NFailedJobs", "u", bus_property_get_unsigned, offsetof(Manager, n_failed_jobs), 0),
@@ -1982,16 +1972,16 @@ const sd_bus_vtable bus_manager_vtable[] = {
SD_BUS_METHOD("StartTransientUnit", "ssa(sv)a(sa(sv))", "o", method_start_transient_unit, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("GetJob", "u", "o", method_get_job, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("CancelJob", "u", NULL, method_cancel_job, SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD("ClearJobs", NULL, NULL, method_clear_jobs, 0),
- SD_BUS_METHOD("ResetFailed", NULL, NULL, method_reset_failed, 0),
+ SD_BUS_METHOD("ClearJobs", NULL, NULL, method_clear_jobs, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("ResetFailed", NULL, NULL, method_reset_failed, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("ListUnits", NULL, "a(ssssssouso)", method_list_units, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("ListUnitsFiltered", "as", "a(ssssssouso)", method_list_units_filtered, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("ListJobs", NULL, "a(usssoo)", method_list_jobs, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("Subscribe", NULL, NULL, method_subscribe, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("Unsubscribe", NULL, NULL, method_unsubscribe, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("Dump", NULL, "s", method_dump, SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD("CreateSnapshot", "sb", "o", method_create_snapshot, 0),
- SD_BUS_METHOD("RemoveSnapshot", "s", NULL, method_remove_snapshot, 0),
+ SD_BUS_METHOD("CreateSnapshot", "sb", "o", method_create_snapshot, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("RemoveSnapshot", "s", NULL, method_remove_snapshot, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("Reload", NULL, NULL, method_reload, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("Reexecute", NULL, NULL, method_reexecute, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("Exit", NULL, NULL, method_exit, 0),
@@ -2000,9 +1990,9 @@ const sd_bus_vtable bus_manager_vtable[] = {
SD_BUS_METHOD("Halt", NULL, NULL, method_halt, SD_BUS_VTABLE_CAPABILITY(CAP_SYS_BOOT)),
SD_BUS_METHOD("KExec", NULL, NULL, method_kexec, SD_BUS_VTABLE_CAPABILITY(CAP_SYS_BOOT)),
SD_BUS_METHOD("SwitchRoot", "ss", NULL, method_switch_root, SD_BUS_VTABLE_CAPABILITY(CAP_SYS_BOOT)),
- SD_BUS_METHOD("SetEnvironment", "as", NULL, method_set_environment, 0),
- SD_BUS_METHOD("UnsetEnvironment", "as", NULL, method_unset_environment, 0),
- SD_BUS_METHOD("UnsetAndSetEnvironment", "asas", NULL, method_unset_and_set_environment, 0),
+ SD_BUS_METHOD("SetEnvironment", "as", NULL, method_set_environment, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("UnsetEnvironment", "as", NULL, method_unset_environment, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("UnsetAndSetEnvironment", "asas", NULL, method_unset_and_set_environment, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("ListUnitFiles", NULL, "a(ss)", method_list_unit_files, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("GetUnitFileState", "s", "s", method_get_unit_file_state, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("EnableUnitFiles", "asbb", "ba(sss)", method_enable_unit_files, SD_BUS_VTABLE_UNPRIVILEGED),
@@ -2102,5 +2092,23 @@ void bus_manager_send_reloading(Manager *m, bool active) {
r = bus_foreach_bus(m, NULL, send_reloading, INT_TO_PTR(active));
if (r < 0)
log_debug_errno(r, "Failed to send reloading signal: %m");
+}
+
+static int send_changed_signal(sd_bus *bus, void *userdata) {
+ assert(bus);
+ return sd_bus_emit_properties_changed_strv(bus,
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ NULL);
+}
+
+void bus_manager_send_change_signal(Manager *m) {
+ int r;
+
+ assert(m);
+
+ r = bus_foreach_bus(m, NULL, send_changed_signal, NULL);
+ if (r < 0)
+ log_debug_errno(r, "Failed to send manager change signal: %m");
}
diff --git a/src/core/dbus-manager.h b/src/core/dbus-manager.h
index e1903fa16a..5bdf6e17ab 100644
--- a/src/core/dbus-manager.h
+++ b/src/core/dbus-manager.h
@@ -21,10 +21,10 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "sd-bus.h"
#include "manager.h"
extern const sd_bus_vtable bus_manager_vtable[];
void bus_manager_send_finished(Manager *m, usec_t firmware_usec, usec_t loader_usec, usec_t kernel_usec, usec_t initrd_usec, usec_t userspace_usec, usec_t total_usec);
void bus_manager_send_reloading(Manager *m, bool active);
+void bus_manager_send_change_signal(Manager *m);
diff --git a/src/core/dbus-mount.c b/src/core/dbus-mount.c
index 53fe4edc34..24813c6d20 100644
--- a/src/core/dbus-mount.c
+++ b/src/core/dbus-mount.c
@@ -21,7 +21,6 @@
#include "unit.h"
#include "mount.h"
-#include "dbus-unit.h"
#include "dbus-execute.h"
#include "dbus-kill.h"
#include "dbus-cgroup.h"
diff --git a/src/core/dbus-path.c b/src/core/dbus-path.c
index b5e894101d..683561999b 100644
--- a/src/core/dbus-path.c
+++ b/src/core/dbus-path.c
@@ -21,7 +21,6 @@
#include "unit.h"
#include "path.h"
-#include "dbus-unit.h"
#include "dbus-path.h"
#include "bus-util.h"
diff --git a/src/core/dbus-path.h b/src/core/dbus-path.h
index 667da05223..389b0d7f9b 100644
--- a/src/core/dbus-path.h
+++ b/src/core/dbus-path.h
@@ -22,6 +22,5 @@
***/
-#include "sd-bus.h"
extern const sd_bus_vtable bus_path_vtable[];
diff --git a/src/core/dbus-scope.c b/src/core/dbus-scope.c
index 60215a1935..f8fb373bf0 100644
--- a/src/core/dbus-scope.c
+++ b/src/core/dbus-scope.c
@@ -19,37 +19,40 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include "selinux-access.h"
#include "unit.h"
#include "scope.h"
-#include "dbus-unit.h"
-#include "dbus-cgroup.h"
-#include "dbus-kill.h"
-#include "dbus-scope.h"
#include "dbus.h"
#include "bus-util.h"
#include "bus-internal.h"
#include "bus-common-errors.h"
+#include "dbus-unit.h"
+#include "dbus-cgroup.h"
+#include "dbus-kill.h"
+#include "dbus-scope.h"
-static int bus_scope_abandon(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int bus_scope_abandon(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Scope *s = userdata;
int r;
- assert(bus);
assert(message);
assert(s);
- r = bus_verify_manage_unit_async(UNIT(s)->manager, message, error);
+ r = mac_selinux_unit_access_check(UNIT(s), message, "stop", error);
+ if (r < 0)
+ return r;
+
+ r = bus_verify_manage_units_async(UNIT(s)->manager, message, error);
if (r < 0)
return r;
if (r == 0)
return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
r = scope_abandon(s);
- if (sd_bus_error_is_set(error))
- return r;
-
if (r == -ESTALE)
return sd_bus_error_setf(error, BUS_ERROR_SCOPE_NOT_RUNNING, "Scope %s is not running, cannot abandon.", UNIT(s)->id);
+ if (r < 0)
+ return r;
return sd_bus_reply_method_return(message, NULL);
}
@@ -62,7 +65,7 @@ const sd_bus_vtable bus_scope_vtable[] = {
SD_BUS_PROPERTY("TimeoutStopUSec", "t", bus_property_get_usec, offsetof(Scope, timeout_stop_usec), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Result", "s", property_get_result, offsetof(Scope, result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_SIGNAL("RequestStop", NULL, 0),
- SD_BUS_METHOD("Abandon", NULL, NULL, bus_scope_abandon, 0),
+ SD_BUS_METHOD("Abandon", NULL, NULL, bus_scope_abandon, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_VTABLE_END
};
diff --git a/src/core/dbus-service.c b/src/core/dbus-service.c
index 6d4713babc..e1f3d56495 100644
--- a/src/core/dbus-service.c
+++ b/src/core/dbus-service.c
@@ -23,7 +23,6 @@
#include "path-util.h"
#include "unit.h"
#include "service.h"
-#include "dbus-unit.h"
#include "dbus-execute.h"
#include "dbus-kill.h"
#include "dbus-cgroup.h"
diff --git a/src/core/dbus-slice.c b/src/core/dbus-slice.c
index 8bc90b1dad..09e78d1f33 100644
--- a/src/core/dbus-slice.c
+++ b/src/core/dbus-slice.c
@@ -21,7 +21,6 @@
#include "unit.h"
#include "slice.h"
-#include "dbus-unit.h"
#include "dbus-cgroup.h"
#include "dbus-slice.h"
diff --git a/src/core/dbus-snapshot.c b/src/core/dbus-snapshot.c
index 06a58e429e..cfe44c9c15 100644
--- a/src/core/dbus-snapshot.c
+++ b/src/core/dbus-snapshot.c
@@ -21,15 +21,14 @@
#include "selinux-access.h"
#include "unit.h"
+#include "dbus.h"
#include "snapshot.h"
-#include "dbus-unit.h"
#include "dbus-snapshot.h"
-int bus_snapshot_method_remove(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+int bus_snapshot_method_remove(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Snapshot *s = userdata;
int r;
- assert(bus);
assert(message);
assert(s);
@@ -37,6 +36,12 @@ int bus_snapshot_method_remove(sd_bus *bus, sd_bus_message *message, void *userd
if (r < 0)
return r;
+ r = bus_verify_manage_units_async(UNIT(s)->manager, message, error);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
+
snapshot_remove(s);
return sd_bus_reply_method_return(message, NULL);
@@ -44,7 +49,7 @@ int bus_snapshot_method_remove(sd_bus *bus, sd_bus_message *message, void *userd
const sd_bus_vtable bus_snapshot_vtable[] = {
SD_BUS_VTABLE_START(0),
- SD_BUS_METHOD("Remove", NULL, NULL, bus_snapshot_method_remove, 0),
SD_BUS_PROPERTY("Cleanup", "b", bus_property_get_bool, offsetof(Snapshot, cleanup), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_METHOD("Remove", NULL, NULL, bus_snapshot_method_remove, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_VTABLE_END
};
diff --git a/src/core/dbus-snapshot.h b/src/core/dbus-snapshot.h
index d7551cbcdc..9288f44e15 100644
--- a/src/core/dbus-snapshot.h
+++ b/src/core/dbus-snapshot.h
@@ -25,4 +25,4 @@
extern const sd_bus_vtable bus_snapshot_vtable[];
-int bus_snapshot_method_remove(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_snapshot_method_remove(sd_bus_message *message, void *userdata, sd_bus_error *error);
diff --git a/src/core/dbus-socket.c b/src/core/dbus-socket.c
index 50b1674427..02599a9e55 100644
--- a/src/core/dbus-socket.c
+++ b/src/core/dbus-socket.c
@@ -21,9 +21,7 @@
#include "unit.h"
#include "socket.h"
-#include "dbus-unit.h"
#include "dbus-execute.h"
-#include "dbus-kill.h"
#include "dbus-cgroup.h"
#include "dbus-socket.h"
#include "bus-util.h"
diff --git a/src/core/dbus-swap.c b/src/core/dbus-swap.c
index 1e7f66d053..0093371306 100644
--- a/src/core/dbus-swap.c
+++ b/src/core/dbus-swap.c
@@ -22,9 +22,7 @@
#include "unit.h"
#include "swap.h"
-#include "dbus-unit.h"
#include "dbus-execute.h"
-#include "dbus-kill.h"
#include "dbus-cgroup.h"
#include "dbus-swap.h"
#include "bus-util.h"
diff --git a/src/core/dbus-target.c b/src/core/dbus-target.c
index 205d1c4a88..350f5c3ed2 100644
--- a/src/core/dbus-target.c
+++ b/src/core/dbus-target.c
@@ -20,8 +20,6 @@
***/
#include "unit.h"
-#include "target.h"
-#include "dbus-unit.h"
#include "dbus-target.h"
const sd_bus_vtable bus_target_vtable[] = {
diff --git a/src/core/dbus-target.h b/src/core/dbus-target.h
index 6be9c9f708..4c4297bc9e 100644
--- a/src/core/dbus-target.h
+++ b/src/core/dbus-target.h
@@ -21,6 +21,5 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "sd-bus.h"
extern const sd_bus_vtable bus_target_vtable[];
diff --git a/src/core/dbus-timer.c b/src/core/dbus-timer.c
index 43e785246c..74a9914358 100644
--- a/src/core/dbus-timer.c
+++ b/src/core/dbus-timer.c
@@ -21,10 +21,8 @@
#include "unit.h"
#include "timer.h"
-#include "dbus-unit.h"
#include "dbus-timer.h"
#include "bus-util.h"
-#include "errno-list.h"
#include "strv.h"
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_result, timer_result, TimerResult);
diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c
index 625d21ab8b..1892725f91 100644
--- a/src/core/dbus-unit.c
+++ b/src/core/dbus-unit.c
@@ -24,11 +24,8 @@
#include "selinux-access.h"
#include "cgroup-util.h"
#include "strv.h"
-#include "path-util.h"
-#include "fileio.h"
#include "bus-common-errors.h"
#include "dbus.h"
-#include "dbus-manager.h"
#include "dbus-unit.h"
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_load_state, unit_load_state, UnitLoadState);
@@ -393,16 +390,25 @@ static int property_get_load_error(
return sd_bus_message_append(reply, "(ss)", e.name, e.message);
}
-int bus_unit_method_start_generic(sd_bus *bus, sd_bus_message *message, Unit *u, JobType job_type, bool reload_if_possible, sd_bus_error *error) {
+int bus_unit_method_start_generic(
+ sd_bus_message *message,
+ Unit *u,
+ JobType job_type,
+ bool reload_if_possible,
+ sd_bus_error *error) {
+
const char *smode;
JobMode mode;
int r;
- assert(bus);
assert(message);
assert(u);
assert(job_type >= 0 && job_type < _JOB_TYPE_MAX);
+ r = mac_selinux_unit_access_check(u, message, job_type == JOB_STOP ? "stop" : "start", error);
+ if (r < 0)
+ return r;
+
r = sd_bus_message_read(message, "s", &smode);
if (r < 0)
return r;
@@ -411,53 +417,56 @@ int bus_unit_method_start_generic(sd_bus *bus, sd_bus_message *message, Unit *u,
if (mode < 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Job mode %s invalid", smode);
- return bus_unit_queue_job(bus, message, u, job_type, mode, reload_if_possible, error);
+ r = bus_verify_manage_units_async(u->manager, message, error);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
+
+ return bus_unit_queue_job(message, u, job_type, mode, reload_if_possible, error);
}
-static int method_start(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
- return bus_unit_method_start_generic(bus, message, userdata, JOB_START, false, error);
+static int method_start(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ return bus_unit_method_start_generic(message, userdata, JOB_START, false, error);
}
-static int method_stop(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
- return bus_unit_method_start_generic(bus, message, userdata, JOB_STOP, false, error);
+static int method_stop(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ return bus_unit_method_start_generic(message, userdata, JOB_STOP, false, error);
}
-static int method_reload(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
- return bus_unit_method_start_generic(bus, message, userdata, JOB_RELOAD, false, error);
+static int method_reload(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ return bus_unit_method_start_generic(message, userdata, JOB_RELOAD, false, error);
}
-static int method_restart(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
- return bus_unit_method_start_generic(bus, message, userdata, JOB_RESTART, false, error);
+static int method_restart(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ return bus_unit_method_start_generic(message, userdata, JOB_RESTART, false, error);
}
-static int method_try_restart(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
- return bus_unit_method_start_generic(bus, message, userdata, JOB_TRY_RESTART, false, error);
+static int method_try_restart(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ return bus_unit_method_start_generic(message, userdata, JOB_TRY_RESTART, false, error);
}
-static int method_reload_or_restart(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
- return bus_unit_method_start_generic(bus, message, userdata, JOB_RESTART, true, error);
+static int method_reload_or_restart(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ return bus_unit_method_start_generic(message, userdata, JOB_RESTART, true, error);
}
-static int method_reload_or_try_restart(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
- return bus_unit_method_start_generic(bus, message, userdata, JOB_TRY_RESTART, true, error);
+static int method_reload_or_try_restart(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ return bus_unit_method_start_generic(message, userdata, JOB_TRY_RESTART, true, error);
}
-int bus_unit_method_kill(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+int bus_unit_method_kill(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Unit *u = userdata;
const char *swho;
int32_t signo;
KillWho who;
int r;
- assert(bus);
assert(message);
assert(u);
- r = bus_verify_manage_unit_async_for_kill(u->manager, message, error);
+ r = mac_selinux_unit_access_check(u, message, "stop", error);
if (r < 0)
return r;
- if (r == 0)
- return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
r = sd_bus_message_read(message, "si", &swho, &signo);
if (r < 0)
@@ -474,9 +483,11 @@ int bus_unit_method_kill(sd_bus *bus, sd_bus_message *message, void *userdata, s
if (signo <= 0 || signo >= _NSIG)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Signal number out of range.");
- r = mac_selinux_unit_access_check(u, message, "stop", error);
+ r = bus_verify_manage_units_async_for_kill(u->manager, message, error);
if (r < 0)
return r;
+ if (r == 0)
+ return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
r = unit_kill(u, who, signo, error);
if (r < 0)
@@ -485,50 +496,48 @@ int bus_unit_method_kill(sd_bus *bus, sd_bus_message *message, void *userdata, s
return sd_bus_reply_method_return(message, NULL);
}
-int bus_unit_method_reset_failed(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+int bus_unit_method_reset_failed(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Unit *u = userdata;
int r;
- assert(bus);
assert(message);
assert(u);
- r = bus_verify_manage_unit_async(u->manager, message, error);
+ r = mac_selinux_unit_access_check(u, message, "reload", error);
if (r < 0)
return r;
- if (r == 0)
- return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
- r = mac_selinux_unit_access_check(u, message, "reload", error);
+ r = bus_verify_manage_units_async(u->manager, message, error);
if (r < 0)
return r;
+ if (r == 0)
+ return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
unit_reset_failed(u);
return sd_bus_reply_method_return(message, NULL);
}
-int bus_unit_method_set_properties(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+int bus_unit_method_set_properties(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Unit *u = userdata;
int runtime, r;
- assert(bus);
assert(message);
assert(u);
- r = bus_verify_manage_unit_async(u->manager, message, error);
+ r = mac_selinux_unit_access_check(u, message, "start", error);
if (r < 0)
return r;
- if (r == 0)
- return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
r = sd_bus_message_read(message, "b", &runtime);
if (r < 0)
return r;
- r = mac_selinux_unit_access_check(u, message, "start", error);
+ r = bus_verify_manage_units_async(u->manager, message, error);
if (r < 0)
return r;
+ if (r == 0)
+ return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
r = bus_unit_set_properties(u, message, runtime ? UNIT_RUNTIME : UNIT_PERSISTENT, true, error);
if (r < 0)
@@ -552,6 +561,8 @@ const sd_bus_vtable bus_unit_vtable[] = {
SD_BUS_PROPERTY("PartOf", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_PART_OF]), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("RequiredBy", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_REQUIRED_BY]), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("RequiredByOverridable", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_REQUIRED_BY_OVERRIDABLE]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("RequisiteOf", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_REQUISITE_OF]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("RequisiteOfOverridable", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_REQUISITE_OF_OVERRIDABLE]), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("WantedBy", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_WANTED_BY]), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("BoundBy", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_BOUND_BY]), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("ConsistsOf", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_CONSISTS_OF]), SD_BUS_VTABLE_PROPERTY_CONST),
@@ -606,16 +617,16 @@ const sd_bus_vtable bus_unit_vtable[] = {
SD_BUS_PROPERTY("LoadError", "(ss)", property_get_load_error, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Transient", "b", bus_property_get_bool, offsetof(Unit, transient), SD_BUS_VTABLE_PROPERTY_CONST),
- SD_BUS_METHOD("Start", "s", "o", method_start, 0),
- SD_BUS_METHOD("Stop", "s", "o", method_stop, 0),
- SD_BUS_METHOD("Reload", "s", "o", method_reload, 0),
- SD_BUS_METHOD("Restart", "s", "o", method_restart, 0),
- SD_BUS_METHOD("TryRestart", "s", "o", method_try_restart, 0),
- SD_BUS_METHOD("ReloadOrRestart", "s", "o", method_reload_or_restart, 0),
- SD_BUS_METHOD("ReloadOrTryRestart", "s", "o", method_reload_or_try_restart, 0),
- SD_BUS_METHOD("Kill", "si", NULL, bus_unit_method_kill, 0),
- SD_BUS_METHOD("ResetFailed", NULL, NULL, bus_unit_method_reset_failed, 0),
- SD_BUS_METHOD("SetProperties", "ba(sv)", NULL, bus_unit_method_set_properties, 0),
+ SD_BUS_METHOD("Start", "s", "o", method_start, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("Stop", "s", "o", method_stop, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("Reload", "s", "o", method_reload, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("Restart", "s", "o", method_restart, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("TryRestart", "s", "o", method_try_restart, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("ReloadOrRestart", "s", "o", method_reload_or_restart, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("ReloadOrTryRestart", "s", "o", method_reload_or_try_restart, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("Kill", "si", NULL, bus_unit_method_kill, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("ResetFailed", NULL, NULL, bus_unit_method_reset_failed, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("SetProperties", "ba(sv)", NULL, bus_unit_method_set_properties, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_VTABLE_END
};
@@ -647,30 +658,43 @@ static int property_get_current_memory(
void *userdata,
sd_bus_error *error) {
- Unit *u = userdata;
uint64_t sz = (uint64_t) -1;
+ Unit *u = userdata;
int r;
assert(bus);
assert(reply);
assert(u);
- if (u->cgroup_path &&
- (u->cgroup_realized_mask & CGROUP_MEMORY)) {
- _cleanup_free_ char *v = NULL;
+ r = unit_get_memory_current(u, &sz);
+ if (r < 0 && r != -ENODATA)
+ log_unit_warning_errno(u, r, "Failed to get memory.usage_in_bytes attribute: %m");
- r = cg_get_attribute("memory", u->cgroup_path, "memory.usage_in_bytes", &v);
- if (r < 0 && r != -ENOENT)
- log_unit_warning_errno(u->id, r, "Couldn't read memory.usage_in_bytes attribute: %m");
+ return sd_bus_message_append(reply, "t", sz);
+}
- if (v) {
- r = safe_atou64(v, &sz);
- if (r < 0)
- log_unit_warning_errno(u->id, r, "Failed to parse memory.usage_in_bytes attribute: %m");
- }
- }
+static int property_get_cpu_usage(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
- return sd_bus_message_append(reply, "t", sz);
+ nsec_t ns = (nsec_t) -1;
+ Unit *u = userdata;
+ int r;
+
+ assert(bus);
+ assert(reply);
+ assert(u);
+
+ r = unit_get_cpu_usage(u, &ns);
+ if (r < 0 && r != -ENODATA)
+ log_unit_warning_errno(u, r, "Failed to get cpuacct.usage attribute: %m");
+
+ return sd_bus_message_append(reply, "t", ns);
}
const sd_bus_vtable bus_unit_cgroup_vtable[] = {
@@ -678,6 +702,7 @@ const sd_bus_vtable bus_unit_cgroup_vtable[] = {
SD_BUS_PROPERTY("Slice", "s", property_get_slice, 0, 0),
SD_BUS_PROPERTY("ControlGroup", "s", NULL, offsetof(Unit, cgroup_path), 0),
SD_BUS_PROPERTY("MemoryCurrent", "t", property_get_current_memory, 0, 0),
+ SD_BUS_PROPERTY("CPUUsageNSec", "t", property_get_cpu_usage, 0, 0),
SD_BUS_VTABLE_END
};
@@ -691,7 +716,7 @@ static int send_new_signal(sd_bus *bus, void *userdata) {
assert(u);
p = unit_dbus_path(u);
- if (!u)
+ if (!p)
return -ENOMEM;
r = sd_bus_message_new_signal(
@@ -753,7 +778,7 @@ void bus_unit_send_change_signal(Unit *u) {
r = bus_foreach_bus(u->manager, NULL, u->sent_dbus_new_signal ? send_changed_signal : send_new_signal, u);
if (r < 0)
- log_debug_errno(r, "Failed to send unit change signal for %s: %m", u->id);
+ log_unit_debug_errno(u, r, "Failed to send unit change signal for %s: %m", u->id);
u->sent_dbus_new_signal = true;
}
@@ -768,7 +793,7 @@ static int send_removed_signal(sd_bus *bus, void *userdata) {
assert(u);
p = unit_dbus_path(u);
- if (!u)
+ if (!p)
return -ENOMEM;
r = sd_bus_message_new_signal(
@@ -799,11 +824,10 @@ void bus_unit_send_removed_signal(Unit *u) {
r = bus_foreach_bus(u->manager, NULL, send_removed_signal, u);
if (r < 0)
- log_debug_errno(r, "Failed to send unit remove signal for %s: %m", u->id);
+ log_unit_debug_errno(u, r, "Failed to send unit remove signal for %s: %m", u->id);
}
int bus_unit_queue_job(
- sd_bus *bus,
sd_bus_message *message,
Unit *u,
JobType type,
@@ -815,7 +839,6 @@ int bus_unit_queue_job(
Job *j;
int r;
- assert(bus);
assert(message);
assert(u);
assert(type >= 0 && type < _JOB_TYPE_MAX);
@@ -849,9 +872,9 @@ int bus_unit_queue_job(
if (r < 0)
return r;
- if (bus == u->manager->api_bus) {
+ if (sd_bus_message_get_bus(message) == u->manager->api_bus) {
if (!j->clients) {
- r = sd_bus_track_new(bus, &j->clients, NULL, NULL);
+ r = sd_bus_track_new(sd_bus_message_get_bus(message), &j->clients, NULL, NULL);
if (r < 0)
return r;
}
@@ -919,7 +942,7 @@ static int bus_unit_set_transient_property(
if (r < 0)
return r;
- if (!unit_name_is_valid(s, TEMPLATE_INVALID) || !endswith(s, ".slice"))
+ if (!unit_name_is_valid(s, UNIT_NAME_PLAIN) || !endswith(s, ".slice"))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid slice name %s", s);
if (isempty(s)) {
@@ -967,7 +990,7 @@ static int bus_unit_set_transient_property(
return r;
while ((r = sd_bus_message_read(message, "s", &other)) > 0) {
- if (!unit_name_is_valid(other, TEMPLATE_INVALID))
+ if (!unit_name_is_valid(other, UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid unit name %s", other);
if (mode != UNIT_CHECK) {
diff --git a/src/core/dbus-unit.h b/src/core/dbus-unit.h
index 57a5e19744..b622e0ae8d 100644
--- a/src/core/dbus-unit.h
+++ b/src/core/dbus-unit.h
@@ -30,10 +30,10 @@ extern const sd_bus_vtable bus_unit_cgroup_vtable[];
void bus_unit_send_change_signal(Unit *u);
void bus_unit_send_removed_signal(Unit *u);
-int bus_unit_method_start_generic(sd_bus *bus, sd_bus_message *message, Unit *u, JobType job_type, bool reload_if_possible, sd_bus_error *error);
-int bus_unit_method_kill(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error);
-int bus_unit_method_reset_failed(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_unit_method_start_generic(sd_bus_message *message, Unit *u, JobType job_type, bool reload_if_possible, sd_bus_error *error);
+int bus_unit_method_kill(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_unit_method_reset_failed(sd_bus_message *message, void *userdata, sd_bus_error *error);
-int bus_unit_queue_job(sd_bus *bus, sd_bus_message *message, Unit *u, JobType type, JobMode mode, bool reload_if_possible, sd_bus_error *error);
+int bus_unit_queue_job(sd_bus_message *message, Unit *u, JobType type, JobMode mode, bool reload_if_possible, sd_bus_error *error);
int bus_unit_set_properties(Unit *u, sd_bus_message *message, UnitSetPropertiesMode mode, bool commit, sd_bus_error *error);
-int bus_unit_method_set_properties(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_unit_method_set_properties(sd_bus_message *message, void *userdata, sd_bus_error *error);
diff --git a/src/core/dbus.c b/src/core/dbus.c
index 260775cd85..86886e6d2c 100644
--- a/src/core/dbus.c
+++ b/src/core/dbus.c
@@ -20,7 +20,6 @@
***/
#include <sys/epoll.h>
-#include <sys/timerfd.h>
#include <errno.h>
#include <unistd.h>
@@ -44,7 +43,7 @@
#include "bus-internal.h"
#include "selinux-access.h"
-#define CONNECTIONS_MAX 512
+#define CONNECTIONS_MAX 4096
static void destroy_bus(Manager *m, sd_bus **bus);
@@ -56,28 +55,24 @@ int bus_send_queued_message(Manager *m) {
if (!m->queued_message)
return 0;
- assert(m->queued_message_bus);
-
/* If we cannot get rid of this message we won't dispatch any
* D-Bus messages, so that we won't end up wanting to queue
* another message. */
- r = sd_bus_send(m->queued_message_bus, m->queued_message, NULL);
+ r = sd_bus_send(NULL, m->queued_message, NULL);
if (r < 0)
log_warning_errno(r, "Failed to send queued message: %m");
m->queued_message = sd_bus_message_unref(m->queued_message);
- m->queued_message_bus = sd_bus_unref(m->queued_message_bus);
return 0;
}
-static int signal_agent_released(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int signal_agent_released(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Manager *m = userdata;
const char *cgroup;
int r;
- assert(bus);
assert(message);
assert(m);
@@ -89,24 +84,29 @@ static int signal_agent_released(sd_bus *bus, sd_bus_message *message, void *use
manager_notify_cgroup_empty(m, cgroup);
- if (m->running_as == SYSTEMD_SYSTEM && m->system_bus) {
- /* If we are running as system manager, forward the
- * message to the system bus */
+ /* only forward to system bus if running as system instance */
+ if (m->running_as != MANAGER_SYSTEM || !m->system_bus)
+ return 0;
- r = sd_bus_send(m->system_bus, message, NULL);
- if (r < 0)
- log_warning_errno(r, "Failed to forward Released message: %m");
- }
+ r = sd_bus_message_rewind(message, 1);
+ if (r < 0)
+ goto exit;
+ r = sd_bus_send(m->system_bus, message, NULL);
+
+exit:
+ if (r < 0)
+ log_warning_errno(r, "Failed to forward Released message: %m");
return 0;
}
-static int signal_disconnected(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int signal_disconnected(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Manager *m = userdata;
+ sd_bus *bus;
- assert(bus);
assert(message);
assert(m);
+ assert_se(bus = sd_bus_message_get_bus(message));
if (bus == m->api_bus)
destroy_bus(m, &m->api_bus);
@@ -120,12 +120,11 @@ static int signal_disconnected(sd_bus *bus, sd_bus_message *message, void *userd
return 0;
}
-static int signal_name_owner_changed(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int signal_name_owner_changed(sd_bus_message *message, void *userdata, sd_bus_error *error) {
const char *name, *old_owner, *new_owner;
Manager *m = userdata;
int r;
- assert(bus);
assert(message);
assert(m);
@@ -143,7 +142,7 @@ static int signal_name_owner_changed(sd_bus *bus, sd_bus_message *message, void
return 0;
}
-static int signal_activation_request(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *ret_error) {
+static int signal_activation_request(sd_bus_message *message, void *userdata, sd_bus_error *ret_error) {
_cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
Manager *m = userdata;
@@ -151,7 +150,6 @@ static int signal_activation_request(sd_bus *bus, sd_bus_message *message, void
Unit *u;
int r;
- assert(bus);
assert(message);
assert(m);
@@ -189,7 +187,7 @@ failed:
log_debug("D-Bus activation failed for %s: %s", name, bus_error_message(&error, r));
- r = sd_bus_message_new_signal(bus, &reply, "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Activator", "ActivationFailure");
+ r = sd_bus_message_new_signal(sd_bus_message_get_bus(message), &reply, "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Activator", "ActivationFailure");
if (r < 0) {
bus_log_create_error(r);
return 0;
@@ -201,7 +199,7 @@ failed:
return 0;
}
- r = sd_bus_send_to(bus, reply, "org.freedesktop.DBus", NULL);
+ r = sd_bus_send_to(NULL, reply, "org.freedesktop.DBus", NULL);
if (r < 0)
return log_error_errno(r, "Failed to respond with to bus activation request: %m");
@@ -209,14 +207,13 @@ failed:
}
#ifdef HAVE_SELINUX
-static int mac_selinux_filter(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int mac_selinux_filter(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Manager *m = userdata;
const char *verb, *path;
Unit *u = NULL;
Job *j;
int r;
- assert(bus);
assert(message);
/* Our own method calls are all protected individually with
@@ -667,6 +664,15 @@ static int bus_on_connection(sd_event_source *s, int fd, uint32_t revents, void
return 0;
}
+ r = sd_bus_negotiate_creds(bus, 1,
+ SD_BUS_CREDS_PID|SD_BUS_CREDS_UID|
+ SD_BUS_CREDS_EUID|SD_BUS_CREDS_EFFECTIVE_CAPS|
+ SD_BUS_CREDS_SELINUX_CONTEXT);
+ if (r < 0) {
+ log_warning_errno(r, "Failed to enable credentials for new connection: %m");
+ return 0;
+ }
+
r = sd_bus_start(bus);
if (r < 0) {
log_warning_errno(r, "Failed to start new connection bus: %m");
@@ -679,7 +685,7 @@ static int bus_on_connection(sd_event_source *s, int fd, uint32_t revents, void
return 0;
}
- if (m->running_as == SYSTEMD_SYSTEM) {
+ if (m->running_as == MANAGER_SYSTEM) {
/* When we run as system instance we get the Released
* signal via a direct connection */
@@ -710,7 +716,7 @@ static int bus_on_connection(sd_event_source *s, int fd, uint32_t revents, void
r = set_put(m->private_buses, bus);
if (r < 0) {
- log_warning_errno(r, "Failed to add new conenction bus to set: %m");
+ log_warning_errno(r, "Failed to add new connection bus to set: %m");
return 0;
}
@@ -807,10 +813,10 @@ static int bus_init_api(Manager *m) {
return 0;
/* The API and system bus is the same if we are running in system mode */
- if (m->running_as == SYSTEMD_SYSTEM && m->system_bus)
+ if (m->running_as == MANAGER_SYSTEM && m->system_bus)
bus = sd_bus_ref(m->system_bus);
else {
- if (m->running_as == SYSTEMD_SYSTEM)
+ if (m->running_as == MANAGER_SYSTEM)
r = sd_bus_open_system(&bus);
else
r = sd_bus_open_user(&bus);
@@ -849,22 +855,19 @@ static int bus_setup_system(Manager *m, sd_bus *bus) {
assert(m);
assert(bus);
- if (m->running_as == SYSTEMD_SYSTEM)
- return 0;
-
- /* If we are a user instance we get the Released message via
- * the system bus */
- r = sd_bus_add_match(
- bus,
- NULL,
- "type='signal',"
- "interface='org.freedesktop.systemd1.Agent',"
- "member='Released',"
- "path='/org/freedesktop/systemd1/agent'",
- signal_agent_released, m);
-
- if (r < 0)
- log_warning_errno(r, "Failed to register Released match on system bus: %m");
+ /* On kdbus or if we are a user instance we get the Released message via the system bus */
+ if (m->running_as == MANAGER_USER || m->kdbus_fd >= 0) {
+ r = sd_bus_add_match(
+ bus,
+ NULL,
+ "type='signal',"
+ "interface='org.freedesktop.systemd1.Agent',"
+ "member='Released',"
+ "path='/org/freedesktop/systemd1/agent'",
+ signal_agent_released, m);
+ if (r < 0)
+ log_warning_errno(r, "Failed to register Released match on system bus: %m");
+ }
log_debug("Successfully connected to system bus.");
return 0;
@@ -878,7 +881,7 @@ static int bus_init_system(Manager *m) {
return 0;
/* The API and system bus is the same if we are running in system mode */
- if (m->running_as == SYSTEMD_SYSTEM && m->api_bus) {
+ if (m->running_as == MANAGER_SYSTEM && m->api_bus) {
m->system_bus = sd_bus_ref(m->api_bus);
return 0;
}
@@ -929,7 +932,7 @@ static int bus_init_private(Manager *m) {
if (m->kdbus_fd >= 0)
return 0;
- if (m->running_as == SYSTEMD_SYSTEM) {
+ if (m->running_as == MANAGER_SYSTEM) {
/* We want the private bus only when running as init */
if (getpid() != 1)
@@ -973,6 +976,8 @@ static int bus_init_private(Manager *m) {
if (r < 0)
return log_error_errno(r, "Failed to allocate event source: %m");
+ (void) sd_event_source_set_description(s, "bus-connection");
+
m->private_listen_fd = fd;
m->private_listen_event_source = s;
fd = -1;
@@ -1021,16 +1026,12 @@ static void destroy_bus(Manager *m, sd_bus **bus) {
j->clients = sd_bus_track_unref(j->clients);
/* Get rid of queued message on this bus */
- if (m->queued_message_bus == *bus) {
- m->queued_message_bus = sd_bus_unref(m->queued_message_bus);
-
- if (m->queued_message)
- m->queued_message = sd_bus_message_unref(m->queued_message);
- }
+ if (m->queued_message && sd_bus_message_get_bus(m->queued_message) == *bus)
+ m->queued_message = sd_bus_message_unref(m->queued_message);
/* Possibly flush unwritten data, but only if we are
* unprivileged, since we don't want to sync here */
- if (m->running_as != SYSTEMD_SYSTEM)
+ if (m->running_as != MANAGER_SYSTEM)
sd_bus_flush(*bus);
/* And destroy the object */
@@ -1115,7 +1116,7 @@ int bus_foreach_bus(
sd_bus *b;
int r, ret = 0;
- /* Send to all direct busses, unconditionally */
+ /* Send to all direct buses, unconditionally */
SET_FOREACH(b, m->private_buses, i) {
r = send_message(b, userdata);
if (r < 0)
@@ -1192,19 +1193,23 @@ int bus_track_coldplug(Manager *m, sd_bus_track **t, char ***l) {
return r;
}
-int bus_verify_manage_unit_async(Manager *m, sd_bus_message *call, sd_bus_error *error) {
- return bus_verify_polkit_async(call, CAP_SYS_ADMIN, "org.freedesktop.systemd1.manage-units", false, &m->polkit_registry, error);
+int bus_verify_manage_units_async(Manager *m, sd_bus_message *call, sd_bus_error *error) {
+ return bus_verify_polkit_async(call, CAP_SYS_ADMIN, "org.freedesktop.systemd1.manage-units", false, UID_INVALID, &m->polkit_registry, error);
}
/* Same as bus_verify_manage_unit_async(), but checks for CAP_KILL instead of CAP_SYS_ADMIN */
-int bus_verify_manage_unit_async_for_kill(Manager *m, sd_bus_message *call, sd_bus_error *error) {
- return bus_verify_polkit_async(call, CAP_KILL, "org.freedesktop.systemd1.manage-units", false, &m->polkit_registry, error);
+int bus_verify_manage_units_async_for_kill(Manager *m, sd_bus_message *call, sd_bus_error *error) {
+ return bus_verify_polkit_async(call, CAP_KILL, "org.freedesktop.systemd1.manage-units", false, UID_INVALID, &m->polkit_registry, error);
}
int bus_verify_manage_unit_files_async(Manager *m, sd_bus_message *call, sd_bus_error *error) {
- return bus_verify_polkit_async(call, CAP_SYS_ADMIN, "org.freedesktop.systemd1.manage-unit-files", false, &m->polkit_registry, error);
+ return bus_verify_polkit_async(call, CAP_SYS_ADMIN, "org.freedesktop.systemd1.manage-unit-files", false, UID_INVALID, &m->polkit_registry, error);
}
int bus_verify_reload_daemon_async(Manager *m, sd_bus_message *call, sd_bus_error *error) {
- return bus_verify_polkit_async(call, CAP_SYS_ADMIN, "org.freedesktop.systemd1.reload-daemon", false, &m->polkit_registry, error);
+ return bus_verify_polkit_async(call, CAP_SYS_ADMIN, "org.freedesktop.systemd1.reload-daemon", false, UID_INVALID, &m->polkit_registry, error);
+}
+
+int bus_verify_set_environment_async(Manager *m, sd_bus_message *call, sd_bus_error *error) {
+ return bus_verify_polkit_async(call, CAP_SYS_ADMIN, "org.freedesktop.systemd1.set-environment", false, UID_INVALID, &m->polkit_registry, error);
}
diff --git a/src/core/dbus.h b/src/core/dbus.h
index d04f5326c6..4832722069 100644
--- a/src/core/dbus.h
+++ b/src/core/dbus.h
@@ -36,7 +36,8 @@ int bus_track_coldplug(Manager *m, sd_bus_track **t, char ***l);
int bus_foreach_bus(Manager *m, sd_bus_track *subscribed2, int (*send_message)(sd_bus *bus, void *userdata), void *userdata);
-int bus_verify_manage_unit_async(Manager *m, sd_bus_message *call, sd_bus_error *error);
-int bus_verify_manage_unit_async_for_kill(Manager *m, sd_bus_message *call, sd_bus_error *error);
+int bus_verify_manage_units_async(Manager *m, sd_bus_message *call, sd_bus_error *error);
+int bus_verify_manage_units_async_for_kill(Manager *m, sd_bus_message *call, sd_bus_error *error);
int bus_verify_manage_unit_files_async(Manager *m, sd_bus_message *call, sd_bus_error *error);
int bus_verify_reload_daemon_async(Manager *m, sd_bus_message *call, sd_bus_error *error);
+int bus_verify_set_environment_async(Manager *m, sd_bus_message *call, sd_bus_error *error);
diff --git a/src/core/device.c b/src/core/device.c
index d3deac3936..e7efcf0f0a 100644
--- a/src/core/device.c
+++ b/src/core/device.c
@@ -23,11 +23,9 @@
#include <sys/epoll.h>
#include <libudev.h>
-#include "strv.h"
#include "log.h"
#include "unit-name.h"
#include "dbus-device.h"
-#include "def.h"
#include "path-util.h"
#include "udev-util.h"
#include "unit.h"
@@ -36,7 +34,8 @@
static const UnitActiveState state_translation_table[_DEVICE_STATE_MAX] = {
[DEVICE_DEAD] = UNIT_INACTIVE,
- [DEVICE_PLUGGED] = UNIT_ACTIVE
+ [DEVICE_TENTATIVE] = UNIT_ACTIVATING,
+ [DEVICE_PLUGGED] = UNIT_ACTIVE,
};
static int device_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata);
@@ -65,6 +64,41 @@ static void device_unset_sysfs(Device *d) {
d->sysfs = NULL;
}
+static int device_set_sysfs(Device *d, const char *sysfs) {
+ Device *first;
+ char *copy;
+ int r;
+
+ assert(d);
+
+ if (streq_ptr(d->sysfs, sysfs))
+ return 0;
+
+ r = hashmap_ensure_allocated(&UNIT(d)->manager->devices_by_sysfs, &string_hash_ops);
+ if (r < 0)
+ return r;
+
+ copy = strdup(sysfs);
+ if (!copy)
+ return -ENOMEM;
+
+ device_unset_sysfs(d);
+
+ first = hashmap_get(UNIT(d)->manager->devices_by_sysfs, sysfs);
+ LIST_PREPEND(same_sysfs, first, d);
+
+ r = hashmap_replace(UNIT(d)->manager->devices_by_sysfs, copy, first);
+ if (r < 0) {
+ LIST_REMOVE(same_sysfs, first, d);
+ free(copy);
+ return r;
+ }
+
+ d->sysfs = copy;
+
+ return 0;
+}
+
static void device_init(Unit *u) {
Device *d = DEVICE(u);
@@ -98,10 +132,7 @@ static void device_set_state(Device *d, DeviceState state) {
d->state = state;
if (state != old_state)
- log_unit_debug(UNIT(d)->id,
- "%s changed %s -> %s", UNIT(d)->id,
- device_state_to_string(old_state),
- device_state_to_string(state));
+ log_unit_debug(UNIT(d), "Changed %s -> %s", device_state_to_string(old_state), device_state_to_string(state));
unit_notify(UNIT(d), state_translation_table[old_state], state_translation_table[state], true);
}
@@ -112,8 +143,48 @@ static int device_coldplug(Unit *u) {
assert(d);
assert(d->state == DEVICE_DEAD);
- if (d->sysfs)
+ if (d->found & DEVICE_FOUND_UDEV)
+ /* If udev says the device is around, it's around */
device_set_state(d, DEVICE_PLUGGED);
+ else if (d->found != DEVICE_NOT_FOUND && d->deserialized_state != DEVICE_PLUGGED)
+ /* If a device is found in /proc/self/mountinfo or
+ * /proc/swaps, and was not yet announced via udev,
+ * it's "tentatively" around. */
+ device_set_state(d, DEVICE_TENTATIVE);
+
+ return 0;
+}
+
+static int device_serialize(Unit *u, FILE *f, FDSet *fds) {
+ Device *d = DEVICE(u);
+
+ assert(u);
+ assert(f);
+ assert(fds);
+
+ unit_serialize_item(u, f, "state", device_state_to_string(d->state));
+
+ return 0;
+}
+
+static int device_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) {
+ Device *d = DEVICE(u);
+
+ assert(u);
+ assert(key);
+ assert(value);
+ assert(fds);
+
+ if (streq(key, "state")) {
+ DeviceState state;
+
+ state = device_state_from_string(value);
+ if (state < 0)
+ log_unit_debug(u, "Failed to parse state value: %s", value);
+ else
+ d->deserialized_state = state;
+ } else
+ log_unit_debug(u, "Unknown serialization key: %s", key);
return 0;
}
@@ -142,49 +213,9 @@ _pure_ static const char *device_sub_state_to_string(Unit *u) {
return device_state_to_string(DEVICE(u)->state);
}
-static int device_add_escaped_name(Unit *u, const char *dn) {
- _cleanup_free_ char *e = NULL;
- int r;
-
- assert(u);
- assert(dn);
- assert(dn[0] == '/');
-
- e = unit_name_from_path(dn, ".device");
- if (!e)
- return -ENOMEM;
-
- r = unit_add_name(u, e);
- if (r < 0 && r != -EEXIST)
- return r;
-
- return 0;
-}
-
-static int device_find_escape_name(Manager *m, const char *dn, Unit **_u) {
- _cleanup_free_ char *e = NULL;
- Unit *u;
-
- assert(m);
- assert(dn);
- assert(dn[0] == '/');
- assert(_u);
-
- e = unit_name_from_path(dn, ".device");
- if (!e)
- return -ENOMEM;
-
- u = manager_get_unit(m, e);
- if (u) {
- *_u = u;
- return 1;
- }
-
- return 0;
-}
-
-static int device_make_description(Unit *u, struct udev_device *dev, const char *path) {
+static int device_update_description(Unit *u, struct udev_device *dev, const char *path) {
const char *model;
+ int r;
assert(u);
assert(dev);
@@ -209,13 +240,18 @@ static int device_make_description(Unit *u, struct udev_device *dev, const char
j = strjoin(model, " ", label, NULL);
if (j)
- return unit_set_description(u, j);
- }
+ r = unit_set_description(u, j);
+ else
+ r = -ENOMEM;
+ } else
+ r = unit_set_description(u, model);
+ } else
+ r = unit_set_description(u, path);
- return unit_set_description(u, model);
- }
+ if (r < 0)
+ log_unit_error_errno(u, r, "Failed to set device description: %m");
- return unit_set_description(u, path);
+ return r;
}
static int device_add_udev_wants(Unit *u, struct udev_device *dev) {
@@ -228,7 +264,7 @@ static int device_add_udev_wants(Unit *u, struct udev_device *dev) {
assert(u);
assert(dev);
- property = u->manager->running_as == SYSTEMD_USER ? "SYSTEMD_USER_WANTS" : "SYSTEMD_WANTS";
+ property = u->manager->running_as == MANAGER_USER ? "MANAGER_USER_WANTS" : "SYSTEMD_WANTS";
wants = udev_device_get_property_value(dev, property);
if (!wants)
return 0;
@@ -240,41 +276,49 @@ static int device_add_udev_wants(Unit *u, struct udev_device *dev) {
memcpy(e, word, l);
e[l] = 0;
- n = unit_name_mangle(e, MANGLE_NOGLOB);
- if (!n)
- return -ENOMEM;
+ r = unit_name_mangle(e, UNIT_NAME_NOGLOB, &n);
+ if (r < 0)
+ return log_unit_error_errno(u, r, "Failed to mangle unit name: %m");
r = unit_add_dependency_by_name(u, UNIT_WANTS, n, NULL, true);
if (r < 0)
- return r;
+ return log_unit_error_errno(u, r, "Failed to add wants dependency: %m");
}
if (!isempty(state))
- log_unit_warning(u->id, "Property %s on %s has trailing garbage, ignoring.",
- property, strna(udev_device_get_syspath(dev)));
+ log_unit_warning(u, "Property %s on %s has trailing garbage, ignoring.", property, strna(udev_device_get_syspath(dev)));
return 0;
}
-static int device_update_unit(Manager *m, struct udev_device *dev, const char *path, bool main) {
- const char *sysfs;
+static int device_setup_unit(Manager *m, struct udev_device *dev, const char *path, bool main) {
+ _cleanup_free_ char *e = NULL;
+ const char *sysfs = NULL;
Unit *u = NULL;
bool delete;
int r;
assert(m);
- assert(dev);
assert(path);
- sysfs = udev_device_get_syspath(dev);
- if (!sysfs)
- return 0;
+ if (dev) {
+ sysfs = udev_device_get_syspath(dev);
+ if (!sysfs)
+ return 0;
+ }
- r = device_find_escape_name(m, path, &u);
+ r = unit_name_from_path(path, ".device", &e);
if (r < 0)
- return r;
+ return log_error_errno(r, "Failed to generate unit name from device path: %m");
+
+ u = manager_get_unit(m, e);
- if (u && DEVICE(u)->sysfs && !path_equal(DEVICE(u)->sysfs, sysfs))
+ if (u &&
+ sysfs &&
+ DEVICE(u)->sysfs &&
+ !path_equal(DEVICE(u)->sysfs, sysfs)) {
+ log_unit_debug(u, "Device %s appeared twice with different sysfs paths %s and %s", e, DEVICE(u)->sysfs, sysfs);
return -EEXIST;
+ }
if (!u) {
delete = true;
@@ -283,7 +327,7 @@ static int device_update_unit(Manager *m, struct udev_device *dev, const char *p
if (!u)
return log_oom();
- r = device_add_escaped_name(u, path);
+ r = unit_add_name(u, e);
if (r < 0)
goto fail;
@@ -294,38 +338,19 @@ static int device_update_unit(Manager *m, struct udev_device *dev, const char *p
/* If this was created via some dependency and has not
* actually been seen yet ->sysfs will not be
* initialized. Hence initialize it if necessary. */
-
- if (!DEVICE(u)->sysfs) {
- Device *first;
-
- DEVICE(u)->sysfs = strdup(sysfs);
- if (!DEVICE(u)->sysfs) {
- r = -ENOMEM;
- goto fail;
- }
-
- r = hashmap_ensure_allocated(&m->devices_by_sysfs, &string_hash_ops);
+ if (sysfs) {
+ r = device_set_sysfs(DEVICE(u), sysfs);
if (r < 0)
goto fail;
- first = hashmap_get(m->devices_by_sysfs, sysfs);
- LIST_PREPEND(same_sysfs, first, DEVICE(u));
+ (void) device_update_description(u, dev, path);
- r = hashmap_replace(m->devices_by_sysfs, DEVICE(u)->sysfs, first);
- if (r < 0)
- goto fail;
+ /* The additional systemd udev properties we only interpret
+ * for the main object */
+ if (main)
+ (void) device_add_udev_wants(u, dev);
}
- device_make_description(u, dev, path);
-
- if (main) {
- /* The additional systemd udev properties we only
- * interpret for the main object */
-
- r = device_add_udev_wants(u, dev);
- if (r < 0)
- goto fail;
- }
/* Note that this won't dispatch the load queue, the caller
* has to do that if needed and appropriate */
@@ -334,15 +359,15 @@ static int device_update_unit(Manager *m, struct udev_device *dev, const char *p
return 0;
fail:
- log_warning_errno(r, "Failed to load device unit: %m");
+ log_unit_warning_errno(u, r, "Failed to set up device unit: %m");
- if (delete && u)
+ if (delete)
unit_free(u);
return r;
}
-static int device_process_new_device(Manager *m, struct udev_device *dev) {
+static int device_process_new(Manager *m, struct udev_device *dev) {
const char *sysfs, *dn, *alias;
struct udev_list_entry *item = NULL, *first = NULL;
int r;
@@ -354,14 +379,14 @@ static int device_process_new_device(Manager *m, struct udev_device *dev) {
return 0;
/* Add the main unit named after the sysfs path */
- r = device_update_unit(m, dev, sysfs, true);
+ r = device_setup_unit(m, dev, sysfs, true);
if (r < 0)
return r;
/* Add an additional unit for the device node */
dn = udev_device_get_devnode(dev);
if (dn)
- device_update_unit(m, dev, dn, false);
+ (void) device_setup_unit(m, dev, dn, false);
/* Add additional units for all symlinks */
first = udev_device_get_devlinks_list_entry(dev);
@@ -388,7 +413,7 @@ static int device_process_new_device(Manager *m, struct udev_device *dev) {
st.st_rdev != udev_device_get_devnum(dev))
continue;
- device_update_unit(m, dev, p, false);
+ (void) device_setup_unit(m, dev, p, false);
}
/* Add additional units for all explicitly configured
@@ -405,7 +430,7 @@ static int device_process_new_device(Manager *m, struct udev_device *dev) {
e[l] = 0;
if (path_is_absolute(e))
- device_update_unit(m, dev, e, false);
+ (void) device_setup_unit(m, dev, e, false);
else
log_warning("SYSTEMD_ALIAS for %s is not an absolute path, ignoring: %s", sysfs, e);
}
@@ -416,39 +441,74 @@ static int device_process_new_device(Manager *m, struct udev_device *dev) {
return 0;
}
-static void device_set_path_plugged(Manager *m, struct udev_device *dev) {
- const char *sysfs;
+static void device_update_found_one(Device *d, bool add, DeviceFound found, bool now) {
+ DeviceFound n, previous;
+
+ assert(d);
+
+ n = add ? (d->found | found) : (d->found & ~found);
+ if (n == d->found)
+ return;
+
+ previous = d->found;
+ d->found = n;
+
+ if (!now)
+ return;
+
+ if (d->found & DEVICE_FOUND_UDEV)
+ /* When the device is known to udev we consider it
+ * plugged. */
+ device_set_state(d, DEVICE_PLUGGED);
+ else if (d->found != DEVICE_NOT_FOUND && (previous & DEVICE_FOUND_UDEV) == 0)
+ /* If the device has not been seen by udev yet, but is
+ * now referenced by the kernel, then we assume the
+ * kernel knows it now, and udev might soon too. */
+ device_set_state(d, DEVICE_TENTATIVE);
+ else
+ /* If nobody sees the device, or if the device was
+ * previously seen by udev and now is only referenced
+ * from the kernel, then we consider the device is
+ * gone, the kernel just hasn't noticed it yet. */
+ device_set_state(d, DEVICE_DEAD);
+}
+
+static int device_update_found_by_sysfs(Manager *m, const char *sysfs, bool add, DeviceFound found, bool now) {
Device *d, *l;
assert(m);
- assert(dev);
+ assert(sysfs);
- sysfs = udev_device_get_syspath(dev);
- if (!sysfs)
- return;
+ if (found == DEVICE_NOT_FOUND)
+ return 0;
l = hashmap_get(m->devices_by_sysfs, sysfs);
LIST_FOREACH(same_sysfs, d, l)
- device_set_state(d, DEVICE_PLUGGED);
+ device_update_found_one(d, add, found, now);
+
+ return 0;
}
-static int device_process_removed_device(Manager *m, struct udev_device *dev) {
- const char *sysfs;
- Device *d;
+static int device_update_found_by_name(Manager *m, const char *path, bool add, DeviceFound found, bool now) {
+ _cleanup_free_ char *e = NULL;
+ Unit *u;
+ int r;
assert(m);
- assert(dev);
+ assert(path);
- sysfs = udev_device_get_syspath(dev);
- if (!sysfs)
- return -ENOMEM;
+ if (found == DEVICE_NOT_FOUND)
+ return 0;
- /* Remove all units of this sysfs path */
- while ((d = hashmap_get(m->devices_by_sysfs, sysfs))) {
- device_unset_sysfs(d);
- device_set_state(d, DEVICE_DEAD);
- }
+ r = unit_name_from_path(path, ".device", &e);
+ if (r < 0)
+ return log_error_errno(r, "Failed to generate unit name from device path: %m");
+
+ u = manager_get_unit(m, e);
+ if (!u)
+ return 0;
+ device_update_found_one(DEVICE(u), add, found, now);
return 0;
}
@@ -464,22 +524,6 @@ static bool device_is_ready(struct udev_device *dev) {
return parse_boolean(ready) != 0;
}
-static int device_process_new_path(Manager *m, const char *path) {
- _cleanup_udev_device_unref_ struct udev_device *dev = NULL;
-
- assert(m);
- assert(path);
-
- dev = udev_device_new_from_syspath(m->udev, path);
- if (!dev)
- return log_oom();
-
- if (!device_is_ready(dev))
- return 0;
-
- return device_process_new_device(m, dev);
-}
-
static Unit *device_following(Unit *u) {
Device *d = DEVICE(u);
Device *other, *first = NULL;
@@ -572,7 +616,7 @@ static int device_enumerate(Manager *m) {
/* This will fail if we are unprivileged, but that
* should not matter much, as user instances won't run
* during boot. */
- udev_monitor_set_receive_buffer_size(m->udev_monitor, 128*1024*1024);
+ (void) udev_monitor_set_receive_buffer_size(m->udev_monitor, 128*1024*1024);
r = udev_monitor_filter_add_match_tag(m->udev_monitor, "systemd");
if (r < 0)
@@ -585,6 +629,8 @@ static int device_enumerate(Manager *m) {
r = sd_event_add_io(m->event, &m->udev_event_source, udev_monitor_get_fd(m->udev_monitor), EPOLLIN, device_dispatch_io, m);
if (r < 0)
goto fail;
+
+ (void) sd_event_source_set_description(m->udev_event_source, "device");
}
e = udev_enumerate_new(m->udev);
@@ -606,12 +652,31 @@ static int device_enumerate(Manager *m) {
goto fail;
first = udev_enumerate_get_list_entry(e);
- udev_list_entry_foreach(item, first)
- device_process_new_path(m, udev_list_entry_get_name(item));
+ udev_list_entry_foreach(item, first) {
+ _cleanup_udev_device_unref_ struct udev_device *dev = NULL;
+ const char *sysfs;
+
+ sysfs = udev_list_entry_get_name(item);
+
+ dev = udev_device_new_from_syspath(m->udev, sysfs);
+ if (!dev) {
+ log_oom();
+ continue;
+ }
+
+ if (!device_is_ready(dev))
+ continue;
+
+ (void) device_process_new(m, dev);
+
+ device_update_found_by_sysfs(m, sysfs, true, DEVICE_FOUND_UDEV, false);
+ }
return 0;
fail:
+ log_error_errno(r, "Failed to enumerate devices: %m");
+
device_shutdown(m);
return r;
}
@@ -619,7 +684,7 @@ fail:
static int device_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata) {
_cleanup_udev_device_unref_ struct udev_device *dev = NULL;
Manager *m = userdata;
- const char *action;
+ const char *action, *sysfs;
int r;
assert(m);
@@ -641,41 +706,54 @@ static int device_dispatch_io(sd_event_source *source, int fd, uint32_t revents,
if (!dev)
return 0;
+ sysfs = udev_device_get_syspath(dev);
+ if (!sysfs) {
+ log_error("Failed to get udev sys path.");
+ return 0;
+ }
+
action = udev_device_get_action(dev);
if (!action) {
log_error("Failed to get udev action string.");
return 0;
}
- if (streq(action, "remove") || !device_is_ready(dev)) {
- r = device_process_removed_device(m, dev);
- if (r < 0)
- log_error_errno(r, "Failed to process device remove event: %m");
-
- r = swap_process_removed_device(m, dev);
+ if (streq(action, "remove")) {
+ r = swap_process_device_remove(m, dev);
if (r < 0)
log_error_errno(r, "Failed to process swap device remove event: %m");
- } else {
- r = device_process_new_device(m, dev);
- if (r < 0)
- log_error_errno(r, "Failed to process device new event: %m");
+ /* If we get notified that a device was removed by
+ * udev, then it's completely gone, hence unset all
+ * found bits */
+ device_update_found_by_sysfs(m, sysfs, false, DEVICE_FOUND_UDEV|DEVICE_FOUND_MOUNT|DEVICE_FOUND_SWAP, true);
+
+ } else if (device_is_ready(dev)) {
- r = swap_process_new_device(m, dev);
+ (void) device_process_new(m, dev);
+
+ r = swap_process_device_new(m, dev);
if (r < 0)
log_error_errno(r, "Failed to process swap device new event: %m");
manager_dispatch_load_queue(m);
- device_set_path_plugged(m, dev);
+ /* The device is found now, set the udev found bit */
+ device_update_found_by_sysfs(m, sysfs, true, DEVICE_FOUND_UDEV, true);
+
+ } else {
+ /* The device is nominally around, but not ready for
+ * us. Hence unset the udev bit, but leave the rest
+ * around. */
+
+ device_update_found_by_sysfs(m, sysfs, false, DEVICE_FOUND_UDEV, true);
}
return 0;
}
-static bool device_supported(Manager *m) {
+static bool device_supported(void) {
static int read_only = -1;
- assert(m);
/* If /sys is read-only we don't support device units, and any
* attempts to start one should fail immediately. */
@@ -686,9 +764,64 @@ static bool device_supported(Manager *m) {
return read_only <= 0;
}
+int device_found_node(Manager *m, const char *node, bool add, DeviceFound found, bool now) {
+ _cleanup_udev_device_unref_ struct udev_device *dev = NULL;
+ struct stat st;
+
+ assert(m);
+ assert(node);
+
+ if (!device_supported())
+ return 0;
+
+ /* This is called whenever we find a device referenced in
+ * /proc/swaps or /proc/self/mounts. Such a device might be
+ * mounted/enabled at a time where udev has not finished
+ * probing it yet, and we thus haven't learned about it
+ * yet. In this case we will set the device unit to
+ * "tentative" state. */
+
+ if (add) {
+ if (!path_startswith(node, "/dev"))
+ return 0;
+
+ /* We make an extra check here, if the device node
+ * actually exists. If it's missing, then this is an
+ * indication that device was unplugged but is still
+ * referenced in /proc/swaps or
+ * /proc/self/mountinfo. Note that this check doesn't
+ * really cover all cases where a device might be gone
+ * away, since drives that can have a medium inserted
+ * will still have a device node even when the medium
+ * is not there... */
+
+ if (stat(node, &st) >= 0) {
+ if (!S_ISBLK(st.st_mode) && !S_ISCHR(st.st_mode))
+ return 0;
+
+ dev = udev_device_new_from_devnum(m->udev, S_ISBLK(st.st_mode) ? 'b' : 'c', st.st_rdev);
+ if (!dev && errno != ENOENT)
+ return log_error_errno(errno, "Failed to get udev device from devnum %u:%u: %m", major(st.st_rdev), minor(st.st_rdev));
+
+ } else if (errno != ENOENT)
+ return log_error_errno(errno, "Failed to stat device node file %s: %m", node);
+
+ /* If the device is known in the kernel and newly
+ * appeared, then we'll create a device unit for it,
+ * under the name referenced in /proc/swaps or
+ * /proc/self/mountinfo. */
+
+ (void) device_setup_unit(m, dev, node, false);
+ }
+
+ /* Update the device unit's state, should it exist */
+ return device_update_found_by_name(m, node, add, found, now);
+}
+
static const char* const device_state_table[_DEVICE_STATE_MAX] = {
[DEVICE_DEAD] = "dead",
- [DEVICE_PLUGGED] = "plugged"
+ [DEVICE_TENTATIVE] = "tentative",
+ [DEVICE_PLUGGED] = "plugged",
};
DEFINE_STRING_TABLE_LOOKUP(device_state, DeviceState);
@@ -708,6 +841,9 @@ const UnitVTable device_vtable = {
.coldplug = device_coldplug,
+ .serialize = device_serialize,
+ .deserialize_item = device_deserialize_item,
+
.dump = device_dump,
.active_state = device_active_state,
diff --git a/src/core/device.h b/src/core/device.h
index bb7ae07834..10ab113176 100644
--- a/src/core/device.h
+++ b/src/core/device.h
@@ -23,32 +23,40 @@
typedef struct Device Device;
-#include "unit.h"
-
/* We simply watch devices, we cannot plug/unplug them. That
* simplifies the state engine greatly */
typedef enum DeviceState {
DEVICE_DEAD,
- DEVICE_PLUGGED,
+ DEVICE_TENTATIVE, /* mounted or swapped, but not (yet) announced by udev */
+ DEVICE_PLUGGED, /* announced by udev */
_DEVICE_STATE_MAX,
_DEVICE_STATE_INVALID = -1
} DeviceState;
+typedef enum DeviceFound {
+ DEVICE_NOT_FOUND = 0,
+ DEVICE_FOUND_UDEV = 1,
+ DEVICE_FOUND_MOUNT = 2,
+ DEVICE_FOUND_SWAP = 4,
+} DeviceFound;
+
struct Device {
Unit meta;
char *sysfs;
+ DeviceFound found;
/* In order to be able to distinguish dependencies on
different device nodes we might end up creating multiple
devices for the same sysfs path. We chain them up here. */
-
LIST_FIELDS(struct Device, same_sysfs);
- DeviceState state;
+ DeviceState state, deserialized_state;
};
extern const UnitVTable device_vtable;
const char* device_state_to_string(DeviceState i) _const_;
DeviceState device_state_from_string(const char *s) _pure_;
+
+int device_found_node(Manager *m, const char *node, bool add, DeviceFound found, bool now);
diff --git a/src/core/execute.c b/src/core/execute.c
index 1815e3de2d..4120493bda 100644
--- a/src/core/execute.c
+++ b/src/core/execute.c
@@ -19,8 +19,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <assert.h>
-#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
@@ -29,14 +27,8 @@
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/prctl.h>
-#include <linux/sched.h>
-#include <sys/types.h>
#include <sys/stat.h>
#include <grp.h>
-#include <pwd.h>
-#include <sys/mount.h>
-#include <linux/fs.h>
-#include <linux/oom.h>
#include <poll.h>
#include <glob.h>
#include <sys/personality.h>
@@ -57,13 +49,13 @@
#include <sys/apparmor.h>
#endif
-#include "execute.h"
+#include "sd-messages.h"
+#include "rm-rf.h"
#include "strv.h"
#include "macro.h"
#include "capability.h"
#include "util.h"
#include "log.h"
-#include "sd-messages.h"
#include "ioprio.h"
#include "securebits.h"
#include "namespace.h"
@@ -80,16 +72,24 @@
#include "errno-list.h"
#include "af-list.h"
#include "mkdir.h"
-#include "apparmor-util.h"
#include "smack-util.h"
#include "bus-endpoint.h"
-#include "label.h"
#include "cap-list.h"
+#include "formats-util.h"
+#include "process-util.h"
+#include "terminal-util.h"
+#include "signal-util.h"
+
+#ifdef HAVE_APPARMOR
+#include "apparmor-util.h"
+#endif
#ifdef HAVE_SECCOMP
#include "seccomp-util.h"
#endif
+#include "execute.h"
+
#define IDLE_TIMEOUT_USEC (5*USEC_PER_SEC)
#define IDLE_TIMEOUT2_USEC (1*USEC_PER_SEC)
@@ -394,11 +394,12 @@ static int setup_input(const ExecContext *context, int socket_fd, bool apply_tty
}
}
-static int setup_output(const ExecContext *context, int fileno, int socket_fd, const char *ident, const char *unit_id, bool apply_tty_stdin, uid_t uid, gid_t gid) {
+static int setup_output(Unit *unit, const ExecContext *context, int fileno, int socket_fd, const char *ident, bool apply_tty_stdin, uid_t uid, gid_t gid) {
ExecOutput o;
ExecInput i;
int r;
+ assert(unit);
assert(context);
assert(ident);
@@ -461,15 +462,9 @@ static int setup_output(const ExecContext *context, int fileno, int socket_fd, c
case EXEC_OUTPUT_KMSG_AND_CONSOLE:
case EXEC_OUTPUT_JOURNAL:
case EXEC_OUTPUT_JOURNAL_AND_CONSOLE:
- r = connect_logger_as(context, o, ident, unit_id, fileno, uid, gid);
+ r = connect_logger_as(context, o, ident, unit->id, fileno, uid, gid);
if (r < 0) {
- log_unit_struct(unit_id,
- LOG_ERR,
- LOG_MESSAGE("Failed to connect %s of %s to the journal socket: %s",
- fileno == STDOUT_FILENO ? "stdout" : "stderr",
- unit_id, strerror(-r)),
- LOG_ERRNO(-r),
- NULL);
+ log_unit_error_errno(unit, r, "Failed to connect %s to the journal socket, ignoring: %m", fileno == STDOUT_FILENO ? "stdout" : "stderr");
r = open_null_as(O_WRONLY, fileno);
}
return r;
@@ -1165,10 +1160,10 @@ static void do_idle_pipe_dance(int idle_pipe[4]) {
if (idle_pipe[3] >= 0 && r == 0 /* timeout */) {
/* Signal systemd that we are bored and want to continue. */
- write(idle_pipe[3], "x", 1);
-
- /* Wait for systemd to react to the signal above. */
- fd_wait_for_event(idle_pipe[0], POLLHUP, IDLE_TIMEOUT2_USEC);
+ r = write(idle_pipe[3], "x", 1);
+ if (r > 0)
+ /* Wait for systemd to react to the signal above. */
+ fd_wait_for_event(idle_pipe[0], POLLHUP, IDLE_TIMEOUT2_USEC);
}
safe_close(idle_pipe[0]);
@@ -1264,7 +1259,38 @@ static int build_environment(
return 0;
}
+static bool exec_needs_mount_namespace(
+ const ExecContext *context,
+ const ExecParameters *params,
+ ExecRuntime *runtime) {
+
+ assert(context);
+ assert(params);
+
+ if (!strv_isempty(context->read_write_dirs) ||
+ !strv_isempty(context->read_only_dirs) ||
+ !strv_isempty(context->inaccessible_dirs))
+ return true;
+
+ if (context->mount_flags != 0)
+ return true;
+
+ if (context->private_tmp && runtime && (runtime->tmp_dir || runtime->var_tmp_dir))
+ return true;
+
+ if (params->bus_endpoint_path)
+ return true;
+
+ if (context->private_devices ||
+ context->protect_system != PROTECT_SYSTEM_NO ||
+ context->protect_home != PROTECT_HOME_NO)
+ return true;
+
+ return false;
+}
+
static int exec_child(
+ Unit *unit,
ExecCommand *command,
const ExecContext *context,
const ExecParameters *params,
@@ -1283,7 +1309,9 @@ static int exec_child(
uid_t uid = UID_INVALID;
gid_t gid = GID_INVALID;
int i, r;
+ bool needs_mount_namespace;
+ assert(unit);
assert(command);
assert(context);
assert(params);
@@ -1385,13 +1413,13 @@ static int exec_child(
return r;
}
- r = setup_output(context, STDOUT_FILENO, socket_fd, basename(command->path), params->unit_id, params->apply_tty_stdin, uid, gid);
+ r = setup_output(unit, context, STDOUT_FILENO, socket_fd, basename(command->path), params->apply_tty_stdin, uid, gid);
if (r < 0) {
*exit_status = EXIT_STDOUT;
return r;
}
- r = setup_output(context, STDERR_FILENO, socket_fd, basename(command->path), params->unit_id, params->apply_tty_stdin, uid, gid);
+ r = setup_output(unit, context, STDERR_FILENO, socket_fd, basename(command->path), params->apply_tty_stdin, uid, gid);
if (r < 0) {
*exit_status = EXIT_STDERR;
return r;
@@ -1417,7 +1445,7 @@ static int exec_child(
r = write_string_file("/proc/self/oom_score_adj", t);
if (r == -EPERM || r == -EACCES) {
log_open();
- log_unit_debug_errno(params->unit_id, r, "Failed to adjust OOM setting, assuming containerized execution, ignoring: %m");
+ log_unit_debug_errno(unit, r, "Failed to adjust OOM setting, assuming containerized execution, ignoring: %m");
log_close();
} else if (r < 0) {
*exit_status = EXIT_OOM_ADJUST;
@@ -1465,7 +1493,7 @@ static int exec_child(
return -errno;
}
- if (context->personality != 0xffffffffUL)
+ if (context->personality != PERSONALITY_INVALID)
if (personality(context->personality) < 0) {
*exit_status = EXIT_PERSONALITY;
return -errno;
@@ -1560,16 +1588,9 @@ static int exec_child(
}
}
- if (!strv_isempty(context->read_write_dirs) ||
- !strv_isempty(context->read_only_dirs) ||
- !strv_isempty(context->inaccessible_dirs) ||
- context->mount_flags != 0 ||
- (context->private_tmp && runtime && (runtime->tmp_dir || runtime->var_tmp_dir)) ||
- params->bus_endpoint_path ||
- context->private_devices ||
- context->protect_system != PROTECT_SYSTEM_NO ||
- context->protect_home != PROTECT_HOME_NO) {
+ needs_mount_namespace = exec_needs_mount_namespace(context, params, runtime);
+ if (needs_mount_namespace) {
char *tmp = NULL, *var = NULL;
/* The runtime struct only contains the parent
@@ -1586,6 +1607,7 @@ static int exec_child(
}
r = setup_namespace(
+ params->apply_chroot ? context->root_directory : NULL,
context->read_write_dirs,
context->read_only_dirs,
context->inaccessible_dirs,
@@ -1602,7 +1624,7 @@ static int exec_child(
* silently proceeed. */
if (r == -EPERM || r == -EACCES) {
log_open();
- log_unit_debug_errno(params->unit_id, r, "Failed to set up namespace, assuming containerized execution, ignoring: %m");
+ log_unit_debug_errno(unit, r, "Failed to set up namespace, assuming containerized execution, ignoring: %m");
log_close();
} else if (r < 0) {
*exit_status = EXIT_NAMESPACE;
@@ -1611,7 +1633,7 @@ static int exec_child(
}
if (params->apply_chroot) {
- if (context->root_directory)
+ if (!needs_mount_namespace && context->root_directory)
if (chroot(context->root_directory) < 0) {
*exit_status = EXIT_CHROOT;
return -errno;
@@ -1803,20 +1825,22 @@ static int exec_child(
line = exec_command_line(final_argv);
if (line) {
log_open();
- log_unit_struct(params->unit_id,
- LOG_DEBUG,
- "EXECUTABLE=%s", command->path,
- LOG_MESSAGE("Executing: %s", line),
- NULL);
+ log_struct(LOG_DEBUG,
+ LOG_UNIT_ID(unit),
+ "EXECUTABLE=%s", command->path,
+ LOG_UNIT_MESSAGE(unit, "Executing: %s", line),
+ NULL);
log_close();
}
}
+
execve(command->path, final_argv, final_env);
*exit_status = EXIT_EXEC;
return -errno;
}
-int exec_spawn(ExecCommand *command,
+int exec_spawn(Unit *unit,
+ ExecCommand *command,
const ExecContext *context,
const ExecParameters *params,
ExecRuntime *runtime,
@@ -1829,6 +1853,7 @@ int exec_spawn(ExecCommand *command,
char **argv;
pid_t pid;
+ assert(unit);
assert(command);
assert(context);
assert(ret);
@@ -1840,7 +1865,7 @@ int exec_spawn(ExecCommand *command,
context->std_error == EXEC_OUTPUT_SOCKET) {
if (params->n_fds != 1) {
- log_unit_error(params->unit_id, "Got more than one socket.");
+ log_unit_error(unit, "Got more than one socket.");
return -EINVAL;
}
@@ -1851,28 +1876,29 @@ int exec_spawn(ExecCommand *command,
n_fds = params->n_fds;
}
- r = exec_context_load_environment(context, params->unit_id, &files_env);
+ r = exec_context_load_environment(unit, context, &files_env);
if (r < 0)
- return log_unit_error_errno(params->unit_id, r, "Failed to load environment files: %m");
+ return log_unit_error_errno(unit, r, "Failed to load environment files: %m");
argv = params->argv ?: command->argv;
line = exec_command_line(argv);
if (!line)
return log_oom();
- log_unit_struct(params->unit_id,
- LOG_DEBUG,
- "EXECUTABLE=%s", command->path,
- LOG_MESSAGE("About to execute: %s", line),
- NULL);
+ log_struct(LOG_DEBUG,
+ LOG_UNIT_ID(unit),
+ LOG_UNIT_MESSAGE(unit, "About to execute: %s", line),
+ "EXECUTABLE=%s", command->path,
+ NULL);
pid = fork();
if (pid < 0)
- return log_unit_error_errno(params->unit_id, r, "Failed to fork: %m");
+ return log_unit_error_errno(unit, r, "Failed to fork: %m");
if (pid == 0) {
int exit_status;
- r = exec_child(command,
+ r = exec_child(unit,
+ command,
context,
params,
runtime,
@@ -1883,21 +1909,20 @@ int exec_spawn(ExecCommand *command,
&exit_status);
if (r < 0) {
log_open();
- log_unit_struct(params->unit_id,
- LOG_ERR,
- LOG_MESSAGE_ID(SD_MESSAGE_SPAWN_FAILED),
- "EXECUTABLE=%s", command->path,
- LOG_MESSAGE("Failed at step %s spawning %s: %s",
- exit_status_to_string(exit_status, EXIT_STATUS_SYSTEMD),
- command->path, strerror(-r)),
- LOG_ERRNO(r),
- NULL);
+ log_struct_errno(LOG_ERR, r,
+ LOG_MESSAGE_ID(SD_MESSAGE_SPAWN_FAILED),
+ LOG_UNIT_ID(unit),
+ LOG_UNIT_MESSAGE(unit, "Failed at step %s spawning %s: %m",
+ exit_status_to_string(exit_status, EXIT_STATUS_SYSTEMD),
+ command->path),
+ "EXECUTABLE=%s", command->path,
+ NULL);
}
_exit(exit_status);
}
- log_unit_debug(params->unit_id, "Forked %s as "PID_FMT, command->path, pid);
+ log_unit_debug(unit, "Forked %s as "PID_FMT, command->path, pid);
/* 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
@@ -1905,7 +1930,7 @@ int exec_spawn(ExecCommand *command,
* sure that when we kill the cgroup the process will be
* killed too). */
if (params->cgroup_path)
- cg_attach(SYSTEMD_CGROUP_CONTROLLER, params->cgroup_path, pid);
+ (void) cg_attach(SYSTEMD_CGROUP_CONTROLLER, params->cgroup_path, pid);
exec_status_start(&command->exec_status, pid);
@@ -1923,7 +1948,7 @@ void exec_context_init(ExecContext *c) {
c->syslog_level_prefix = true;
c->ignore_sigpipe = true;
c->timer_slack_nsec = NSEC_INFINITY;
- c->personality = 0xffffffffUL;
+ c->personality = PERSONALITY_INVALID;
c->runtime_directory_mode = 0755;
}
@@ -2026,7 +2051,7 @@ int exec_context_destroy_runtime_directory(ExecContext *c, const char *runtime_p
/* We execute this synchronously, since we need to be
* sure this is gone when we start the service
* next. */
- rm_rf(p, false, true, false);
+ (void) rm_rf(p, REMOVE_ROOT);
}
return 0;
@@ -2069,17 +2094,17 @@ void exec_command_free_array(ExecCommand **c, unsigned n) {
}
typedef struct InvalidEnvInfo {
- const char *unit_id;
+ Unit *unit;
const char *path;
} InvalidEnvInfo;
static void invalid_env(const char *p, void *userdata) {
InvalidEnvInfo *info = userdata;
- log_unit_error(info->unit_id, "Ignoring invalid environment assignment '%s': %s", p, info->path);
+ log_unit_error(info->unit, "Ignoring invalid environment assignment '%s': %s", p, info->path);
}
-int exec_context_load_environment(const ExecContext *c, const char *unit_id, char ***l) {
+int exec_context_load_environment(Unit *unit, const ExecContext *c, char ***l) {
char **i, **r = NULL;
assert(c);
@@ -2137,7 +2162,7 @@ int exec_context_load_environment(const ExecContext *c, const char *unit_id, cha
/* Log invalid environment variables with filename */
if (p) {
InvalidEnvInfo info = {
- .unit_id = unit_id,
+ .unit = unit,
.path = pglob.gl_pathv[n]
};
@@ -2404,7 +2429,7 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) {
"%sSELinuxContext: %s%s\n",
prefix, c->selinux_context_ignore ? "-" : "", c->selinux_context);
- if (c->personality != 0xffffffffUL)
+ if (c->personality != PERSONALITY_INVALID)
fprintf(f,
"%sPersonality: %s\n",
prefix, strna(personality_to_string(c->personality)));
@@ -2735,17 +2760,18 @@ ExecRuntime *exec_runtime_unref(ExecRuntime *r) {
assert(r->n_ref > 0);
r->n_ref--;
- if (r->n_ref <= 0) {
- free(r->tmp_dir);
- free(r->var_tmp_dir);
- safe_close_pair(r->netns_storage_socket);
- free(r);
- }
+ if (r->n_ref > 0)
+ return NULL;
+
+ free(r->tmp_dir);
+ free(r->var_tmp_dir);
+ safe_close_pair(r->netns_storage_socket);
+ free(r);
return NULL;
}
-int exec_runtime_serialize(ExecRuntime *rt, Unit *u, FILE *f, FDSet *fds) {
+int exec_runtime_serialize(Unit *u, ExecRuntime *rt, FILE *f, FDSet *fds) {
assert(u);
assert(f);
assert(fds);
@@ -2782,7 +2808,7 @@ int exec_runtime_serialize(ExecRuntime *rt, Unit *u, FILE *f, FDSet *fds) {
return 0;
}
-int exec_runtime_deserialize_item(ExecRuntime **rt, Unit *u, const char *key, const char *value, FDSet *fds) {
+int exec_runtime_deserialize_item(Unit *u, ExecRuntime **rt, const char *key, const char *value, FDSet *fds) {
int r;
assert(rt);
@@ -2794,7 +2820,7 @@ int exec_runtime_deserialize_item(ExecRuntime **rt, Unit *u, const char *key, co
r = exec_runtime_allocate(rt);
if (r < 0)
- return r;
+ return log_oom();
copy = strdup(value);
if (!copy)
@@ -2808,7 +2834,7 @@ int exec_runtime_deserialize_item(ExecRuntime **rt, Unit *u, const char *key, co
r = exec_runtime_allocate(rt);
if (r < 0)
- return r;
+ return log_oom();
copy = strdup(value);
if (!copy)
@@ -2822,10 +2848,10 @@ int exec_runtime_deserialize_item(ExecRuntime **rt, Unit *u, const char *key, co
r = exec_runtime_allocate(rt);
if (r < 0)
- return r;
+ return log_oom();
if (safe_atoi(value, &fd) < 0 || !fdset_contains(fds, fd))
- log_unit_debug(u->id, "Failed to parse netns socket value %s", value);
+ log_unit_debug(u, "Failed to parse netns socket value: %s", value);
else {
safe_close((*rt)->netns_storage_socket[0]);
(*rt)->netns_storage_socket[0] = fdset_remove(fds, fd);
@@ -2835,10 +2861,10 @@ int exec_runtime_deserialize_item(ExecRuntime **rt, Unit *u, const char *key, co
r = exec_runtime_allocate(rt);
if (r < 0)
- return r;
+ return log_oom();
if (safe_atoi(value, &fd) < 0 || !fdset_contains(fds, fd))
- log_unit_debug(u->id, "Failed to parse netns socket value %s", value);
+ log_unit_debug(u, "Failed to parse netns socket value: %s", value);
else {
safe_close((*rt)->netns_storage_socket[1]);
(*rt)->netns_storage_socket[1] = fdset_remove(fds, fd);
@@ -2852,7 +2878,7 @@ int exec_runtime_deserialize_item(ExecRuntime **rt, Unit *u, const char *key, co
static void *remove_tmpdir_thread(void *p) {
_cleanup_free_ char *path = p;
- rm_rf_dangerous(path, false, true, false);
+ (void) rm_rf(path, REMOVE_ROOT|REMOVE_PHYSICAL);
return NULL;
}
diff --git a/src/core/execute.h b/src/core/execute.h
index 6e0c9faa75..f5d5c1dee7 100644
--- a/src/core/execute.h
+++ b/src/core/execute.h
@@ -27,17 +27,12 @@ typedef struct ExecContext ExecContext;
typedef struct ExecRuntime ExecRuntime;
typedef struct ExecParameters ExecParameters;
-#include <linux/types.h>
-#include <sys/time.h>
-#include <sys/resource.h>
#include <sys/capability.h>
#include <stdbool.h>
#include <stdio.h>
#include <sched.h>
#include "list.h"
-#include "util.h"
-#include "set.h"
#include "fdset.h"
#include "missing.h"
#include "namespace.h"
@@ -170,7 +165,7 @@ struct ExecContext {
/* This is not exposed to the user but available
* internally. We need it to make sure that whenever we spawn
- * /bin/mount it is run in the same process group as us so
+ * /usr/bin/mount it is run in the same process group as us so
* that the autofs logic detects that it belongs to us and we
* don't enter a trigger loop. */
bool same_pgrp;
@@ -214,14 +209,14 @@ struct ExecParameters {
const char *cgroup_path;
bool cgroup_delegate;
const char *runtime_prefix;
- const char *unit_id;
usec_t watchdog_usec;
int *idle_pipe;
char *bus_endpoint_path;
int bus_endpoint_fd;
};
-int exec_spawn(ExecCommand *command,
+int exec_spawn(Unit *unit,
+ ExecCommand *command,
const ExecContext *context,
const ExecParameters *exec_params,
ExecRuntime *runtime,
@@ -247,7 +242,7 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix);
int exec_context_destroy_runtime_directory(ExecContext *c, const char *runtime_root);
-int exec_context_load_environment(const ExecContext *c, const char *unit_id, char ***l);
+int exec_context_load_environment(Unit *unit, const ExecContext *c, char ***l);
bool exec_context_may_touch_console(ExecContext *c);
bool exec_context_maintains_privileges(ExecContext *c);
@@ -260,8 +255,8 @@ int exec_runtime_make(ExecRuntime **rt, ExecContext *c, const char *id);
ExecRuntime *exec_runtime_ref(ExecRuntime *r);
ExecRuntime *exec_runtime_unref(ExecRuntime *r);
-int exec_runtime_serialize(ExecRuntime *rt, Unit *u, FILE *f, FDSet *fds);
-int exec_runtime_deserialize_item(ExecRuntime **rt, Unit *u, const char *key, const char *value, FDSet *fds);
+int exec_runtime_serialize(Unit *unit, ExecRuntime *rt, FILE *f, FDSet *fds);
+int exec_runtime_deserialize_item(Unit *unit, ExecRuntime **rt, const char *key, const char *value, FDSet *fds);
void exec_runtime_destroy(ExecRuntime *rt);
diff --git a/src/core/failure-action.c b/src/core/failure-action.c
index ce522a4e4f..b06a7d2ae5 100644
--- a/src/core/failure-action.c
+++ b/src/core/failure-action.c
@@ -22,12 +22,12 @@
#include <sys/reboot.h>
#include <linux/reboot.h>
-#include <sys/syscall.h>
#include "bus-util.h"
#include "bus-error.h"
#include "special.h"
#include "failure-action.h"
+#include "terminal-util.h"
static void log_and_status(Manager *m, const char *message) {
log_warning("%s", message);
@@ -50,7 +50,7 @@ int failure_action(
if (action == FAILURE_ACTION_NONE)
return -ECANCELED;
- if (m->running_as == SYSTEMD_USER) {
+ if (m->running_as == MANAGER_USER) {
/* Downgrade all options to simply exiting if we run
* in user mode */
diff --git a/src/core/hostname-setup.c b/src/core/hostname-setup.c
index 6664e8952c..932ddbf95a 100644
--- a/src/core/hostname-setup.c
+++ b/src/core/hostname-setup.c
@@ -19,39 +19,16 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <unistd.h>
#include <stdio.h>
#include <errno.h>
-#include <string.h>
#include <stdlib.h>
-#include "hostname-setup.h"
#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;
- int r;
-
- assert(path);
- assert(hn);
-
- r = read_one_line_file(path, &s);
- if (r < 0)
- return r;
-
- hostname_cleanup(s, false);
-
- if (isempty(s)) {
- free(s);
- return -ENOENT;
- }
-
- *hn = s;
- return 0;
-}
+#include "hostname-util.h"
+#include "hostname-setup.h"
int hostname_setup(void) {
int r;
@@ -59,7 +36,7 @@ int hostname_setup(void) {
const char *hn;
bool enoent = false;
- r = read_and_strip_hostname("/etc/hostname", &b);
+ r = read_hostname_config("/etc/hostname", &b);
if (r < 0) {
if (r == -ENOENT)
enoent = true;
diff --git a/src/core/ima-setup.c b/src/core/ima-setup.c
index 0e0d16a7c9..4d8b638115 100644
--- a/src/core/ima-setup.c
+++ b/src/core/ima-setup.c
@@ -22,14 +22,12 @@
***/
#include <unistd.h>
-#include <stdio.h>
#include <errno.h>
-#include <sys/types.h>
-#include <sys/stat.h>
#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
#include "ima-setup.h"
-#include "copy.h"
#include "util.h"
#include "log.h"
@@ -42,6 +40,8 @@ int ima_setup(void) {
#ifdef HAVE_IMA
_cleanup_close_ int policyfd = -1, imafd = -1;
+ struct stat st;
+ char *policy;
if (access(IMA_SECFS_DIR, F_OK) < 0) {
log_debug("IMA support is disabled in the kernel, ignoring.");
@@ -66,12 +66,20 @@ int ima_setup(void) {
return 0;
}
- r = copy_bytes(policyfd, imafd, (off_t) -1, false);
+ if (fstat(policyfd, &st) < 0)
+ return log_error_errno(errno, "Failed to fstat "IMA_POLICY_PATH": %m");
+
+ policy = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, policyfd, 0);
+ if (policy == MAP_FAILED)
+ return log_error_errno(errno, "Failed to mmap "IMA_POLICY_PATH": %m");
+
+ r = loop_write(imafd, policy, (size_t) st.st_size, false);
if (r < 0)
log_error_errno(r, "Failed to load the IMA custom policy file "IMA_POLICY_PATH": %m");
else
log_info("Successfully loaded the IMA custom policy "IMA_POLICY_PATH".");
+ munmap(policy, st.st_size);
#endif /* HAVE_IMA */
return r;
}
diff --git a/src/core/job.c b/src/core/job.c
index 4740ff18cb..8a047df0c3 100644
--- a/src/core/job.c
+++ b/src/core/job.c
@@ -19,10 +19,7 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <assert.h>
#include <errno.h>
-#include <sys/timerfd.h>
-#include <sys/epoll.h>
#include "sd-id128.h"
#include "sd-messages.h"
@@ -30,14 +27,13 @@
#include "unit.h"
#include "macro.h"
#include "strv.h"
-#include "load-fragment.h"
-#include "load-dropin.h"
#include "log.h"
#include "dbus-job.h"
#include "special.h"
#include "async.h"
#include "virt.h"
#include "dbus.h"
+#include "terminal-util.h"
Job* job_new_raw(Unit *unit) {
Job *j;
@@ -197,7 +193,7 @@ Job* job_install(Job *j) {
if (uj->state == JOB_WAITING ||
(job_type_allows_late_merge(j->type) && job_type_is_superset(uj->type, j->type))) {
job_merge_into_installed(uj, j);
- log_unit_debug(uj->unit->id,
+ log_unit_debug(uj->unit,
"Merged into installed job %s/%s as %u",
uj->unit->id, job_type_to_string(uj->type), (unsigned) uj->id);
return uj;
@@ -207,7 +203,7 @@ Job* job_install(Job *j) {
/* XXX It should be safer to queue j to run after uj finishes, but it is
* not currently possible to have more than one installed job per unit. */
job_merge_into_installed(uj, j);
- log_unit_debug(uj->unit->id,
+ log_unit_debug(uj->unit,
"Merged into running job, re-running: %s/%s as %u",
uj->unit->id, job_type_to_string(uj->type), (unsigned) uj->id);
@@ -222,7 +218,7 @@ Job* job_install(Job *j) {
j->installed = true;
j->manager->n_installed_jobs ++;
- log_unit_debug(j->unit->id,
+ log_unit_debug(j->unit,
"Installed new job %s/%s as %u",
j->unit->id, job_type_to_string(j->type), (unsigned) j->id);
return j;
@@ -240,7 +236,7 @@ int job_install_deserialized(Job *j) {
pj = (j->type == JOB_NOP) ? &j->unit->nop_job : &j->unit->job;
if (*pj) {
- log_unit_debug(j->unit->id, "Unit %s already has a job installed. Not installing deserialized job.", j->unit->id);
+ log_unit_debug(j->unit, "Unit already has a job installed. Not installing deserialized job.");
return -EEXIST;
}
@@ -250,7 +246,7 @@ int job_install_deserialized(Job *j) {
if (j->state == JOB_RUNNING)
j->unit->manager->n_running_jobs++;
- log_unit_debug(j->unit->id,
+ log_unit_debug(j->unit,
"Reinstalled deserialized job %s/%s as %u",
j->unit->id, job_type_to_string(j->type), (unsigned) j->id);
return 0;
@@ -322,8 +318,8 @@ void job_dump(Job *j, FILE*f, const char *prefix) {
* the JOB_RELOAD_OR_START, which lies outside the lookup function's domain),
* the following properties hold:
*
- * Merging is associative! A merged with B merged with C is the same as
- * A merged with C merged with B.
+ * Merging is associative! A merged with B, and then merged with C is the same
+ * as A merged with the result of B merged with C.
*
* Mergeability is transitive! If A can be merged with B and B with C then
* A also with C.
@@ -392,38 +388,38 @@ bool job_type_is_redundant(JobType a, UnitActiveState b) {
}
}
-void job_type_collapse(JobType *t, Unit *u) {
+JobType job_type_collapse(JobType t, Unit *u) {
UnitActiveState s;
- switch (*t) {
+ switch (t) {
case JOB_TRY_RESTART:
s = unit_active_state(u);
if (UNIT_IS_INACTIVE_OR_DEACTIVATING(s))
- *t = JOB_NOP;
- else
- *t = JOB_RESTART;
- break;
+ return JOB_NOP;
+
+ return JOB_RESTART;
case JOB_RELOAD_OR_START:
s = unit_active_state(u);
if (UNIT_IS_INACTIVE_OR_DEACTIVATING(s))
- *t = JOB_START;
- else
- *t = JOB_RELOAD;
- break;
+ return JOB_START;
+
+ return JOB_RELOAD;
default:
- ;
+ return t;
}
}
int job_type_merge_and_collapse(JobType *a, JobType b, Unit *u) {
- JobType t = job_type_lookup_merge(*a, b);
+ JobType t;
+
+ t = job_type_lookup_merge(*a, b);
if (t < 0)
return -EEXIST;
- *a = t;
- job_type_collapse(a, u);
+
+ *a = job_type_collapse(t, u);
return 0;
}
@@ -489,7 +485,9 @@ static bool job_is_runnable(Job *j) {
}
static void job_change_type(Job *j, JobType newtype) {
- log_unit_debug(j->unit->id,
+ assert(j);
+
+ log_unit_debug(j->unit,
"Converting job %s/%s -> %s/%s",
j->unit->id, job_type_to_string(j->type),
j->unit->id, job_type_to_string(newtype));
@@ -578,7 +576,7 @@ int job_run_and_invalidate(Job *j) {
r = job_finish_and_invalidate(j, JOB_INVALID, true);
else if (r == -EPROTO)
r = job_finish_and_invalidate(j, JOB_ASSERT, true);
- else if (r == -ENOTSUP)
+ else if (r == -EOPNOTSUPP)
r = job_finish_and_invalidate(j, JOB_UNSUPPORTED, true);
else if (r == -EAGAIN)
job_set_state(j, JOB_WAITING);
@@ -679,15 +677,13 @@ static void job_print_status_message(Unit *u, JobType t, JobResult result) {
break;
case JOB_FAILED: {
- bool quotes;
+ _cleanup_free_ char *quoted = NULL;
- quotes = chars_intersect(u->id, SHELL_NEED_QUOTES);
+ quoted = shell_maybe_quote(u->id);
manager_flip_auto_status(u->manager, true);
unit_status_printf(u, ANSI_HIGHLIGHT_RED_ON "FAILED" ANSI_HIGHLIGHT_OFF, format);
- manager_status_printf(u->manager, STATUS_TYPE_NORMAL, NULL,
- "See \"systemctl status %s%s%s\" for details.",
- quotes ? "'" : "", u->id, quotes ? "'" : "");
+ manager_status_printf(u->manager, STATUS_TYPE_NORMAL, NULL, "See 'systemctl status %s' for details.", strna(quoted));
break;
}
@@ -770,28 +766,46 @@ 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_unit_struct(u->id,
- result == JOB_DONE ? LOG_INFO : LOG_ERR,
- LOG_MESSAGE_ID(mid),
- LOG_MESSAGE("%s", buf),
- "RESULT=%s", job_result_to_string(result),
- NULL);
+ log_struct(result == JOB_DONE ? LOG_INFO : LOG_ERR,
+ LOG_MESSAGE_ID(mid),
+ LOG_UNIT_ID(u),
+ LOG_MESSAGE("%s", buf),
+ "RESULT=%s", job_result_to_string(result),
+ NULL);
} else if (t == JOB_STOP)
- log_unit_struct(u->id,
- result == JOB_DONE ? LOG_INFO : LOG_ERR,
- LOG_MESSAGE_ID(SD_MESSAGE_UNIT_STOPPED),
- LOG_MESSAGE("%s", buf),
- "RESULT=%s", job_result_to_string(result),
- NULL);
+ log_struct(result == JOB_DONE ? LOG_INFO : LOG_ERR,
+ LOG_MESSAGE_ID(SD_MESSAGE_UNIT_STOPPED),
+ LOG_UNIT_ID(u),
+ LOG_MESSAGE("%s", buf),
+ "RESULT=%s", job_result_to_string(result),
+ NULL);
else if (t == JOB_RELOAD)
- log_unit_struct(u->id,
- result == JOB_DONE ? LOG_INFO : LOG_ERR,
- LOG_MESSAGE_ID(SD_MESSAGE_UNIT_RELOADED),
- LOG_MESSAGE("%s", buf),
- "RESULT=%s", job_result_to_string(result),
- NULL);
+ log_struct(result == JOB_DONE ? LOG_INFO : LOG_ERR,
+ LOG_MESSAGE_ID(SD_MESSAGE_UNIT_RELOADED),
+ LOG_UNIT_ID(u),
+ LOG_MESSAGE("%s", buf),
+ "RESULT=%s", job_result_to_string(result),
+ NULL);
+}
+
+static void job_fail_dependencies(Unit *u, UnitDependency d) {
+ Unit *other;
+ Iterator i;
+
+ assert(u);
+
+ SET_FOREACH(other, u->dependencies[d], i) {
+ Job *j = other->job;
+
+ if (!j)
+ continue;
+ if (!IN_SET(j->type, JOB_START, JOB_VERIFY_ACTIVE))
+ continue;
+
+ job_finish_and_invalidate(j, JOB_DEPENDENCY, true);
+ }
}
int job_finish_and_invalidate(Job *j, JobResult result, bool recursive) {
@@ -809,8 +823,7 @@ int job_finish_and_invalidate(Job *j, JobResult result, bool recursive) {
j->result = result;
- log_unit_debug(u->id, "Job %s/%s finished, result=%s",
- u->id, job_type_to_string(t), job_result_to_string(result));
+ log_unit_debug(u, "Job %s/%s finished, result=%s", u->id, job_type_to_string(t), job_result_to_string(result));
job_print_status_message(u, t, result);
job_log_status_message(u, t, result);
@@ -836,37 +849,14 @@ int job_finish_and_invalidate(Job *j, JobResult result, bool recursive) {
/* Fail depending jobs on failure */
if (result != JOB_DONE && recursive) {
-
- if (t == JOB_START ||
- t == JOB_VERIFY_ACTIVE) {
-
- SET_FOREACH(other, u->dependencies[UNIT_REQUIRED_BY], i)
- if (other->job &&
- (other->job->type == JOB_START ||
- other->job->type == JOB_VERIFY_ACTIVE))
- job_finish_and_invalidate(other->job, JOB_DEPENDENCY, true);
-
- SET_FOREACH(other, u->dependencies[UNIT_BOUND_BY], i)
- if (other->job &&
- (other->job->type == JOB_START ||
- other->job->type == JOB_VERIFY_ACTIVE))
- job_finish_and_invalidate(other->job, JOB_DEPENDENCY, true);
-
- SET_FOREACH(other, u->dependencies[UNIT_REQUIRED_BY_OVERRIDABLE], i)
- if (other->job &&
- !other->job->override &&
- (other->job->type == JOB_START ||
- other->job->type == JOB_VERIFY_ACTIVE))
- job_finish_and_invalidate(other->job, JOB_DEPENDENCY, true);
-
- } else if (t == JOB_STOP) {
-
- SET_FOREACH(other, u->dependencies[UNIT_CONFLICTED_BY], i)
- if (other->job &&
- (other->job->type == JOB_START ||
- other->job->type == JOB_VERIFY_ACTIVE))
- job_finish_and_invalidate(other->job, JOB_DEPENDENCY, true);
- }
+ if (IN_SET(t, JOB_START, JOB_VERIFY_ACTIVE)) {
+ job_fail_dependencies(u, UNIT_REQUIRED_BY);
+ job_fail_dependencies(u, UNIT_REQUISITE_OF);
+ job_fail_dependencies(u, UNIT_BOUND_BY);
+ job_fail_dependencies(u, UNIT_REQUIRED_BY_OVERRIDABLE);
+ job_fail_dependencies(u, UNIT_REQUISITE_OF_OVERRIDABLE);
+ } else if (t == JOB_STOP)
+ job_fail_dependencies(u, UNIT_CONFLICTED_BY);
}
/* Trigger OnFailure dependencies that are not generated by
@@ -874,15 +864,15 @@ 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_unit_struct(u->id,
- LOG_NOTICE,
- "JOB_TYPE=%s", job_type_to_string(t),
- "JOB_RESULT=%s", job_result_to_string(result),
- LOG_MESSAGE("Job %s/%s failed with result '%s'.",
+ log_struct(LOG_NOTICE,
+ "JOB_TYPE=%s", job_type_to_string(t),
+ "JOB_RESULT=%s", job_result_to_string(result),
+ LOG_UNIT_ID(u),
+ LOG_UNIT_MESSAGE(u, "Job %s/%s failed with result '%s'.",
u->id,
job_type_to_string(t),
job_result_to_string(result)),
- NULL);
+ NULL);
unit_start_on_failure(u);
}
@@ -910,7 +900,7 @@ static int job_dispatch_timer(sd_event_source *s, uint64_t monotonic, void *user
assert(j);
assert(s == j->timer_event_source);
- log_unit_warning(j->unit->id, "Job %s/%s timed out.", j->unit->id, job_type_to_string(j->type));
+ log_unit_warning(j->unit, "Job %s/%s timed out.", j->unit->id, job_type_to_string(j->type));
u = j->unit;
job_finish_and_invalidate(j, JOB_TIMEOUT, true);
@@ -940,6 +930,8 @@ int job_start_timer(Job *j) {
if (r < 0)
return r;
+ (void) sd_event_source_set_description(j->timer_event_source, "job-start");
+
return 0;
}
@@ -1136,6 +1128,8 @@ int job_coldplug(Job *j) {
if (r < 0)
log_debug_errno(r, "Failed to restart timeout for job: %m");
+ (void) sd_event_source_set_description(j->timer_event_source, "job-timeout");
+
return r;
}
@@ -1153,7 +1147,7 @@ void job_shutdown_magic(Job *j) {
if (j->type != JOB_START)
return;
- if (j->unit->manager->running_as != SYSTEMD_SYSTEM)
+ if (j->unit->manager->running_as != MANAGER_SYSTEM)
return;
if (!unit_has_name(j->unit, SPECIAL_SHUTDOWN_TARGET))
diff --git a/src/core/job.h b/src/core/job.h
index d967b68a3f..1d1b10f1d3 100644
--- a/src/core/job.h
+++ b/src/core/job.h
@@ -22,8 +22,6 @@
***/
#include <stdbool.h>
-#include <inttypes.h>
-#include <errno.h>
typedef struct Job Job;
typedef struct JobDependency JobDependency;
@@ -108,9 +106,7 @@ enum JobResult {
};
#include "sd-event.h"
-#include "manager.h"
#include "unit.h"
-#include "hashmap.h"
#include "list.h"
struct JobDependency {
@@ -210,7 +206,7 @@ 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. */
-void job_type_collapse(JobType *t, Unit *u);
+JobType job_type_collapse(JobType t, Unit *u);
int job_type_merge_and_collapse(JobType *a, JobType b, Unit *u);
diff --git a/src/core/kill.c b/src/core/kill.c
index 4271346511..2de71c6bf9 100644
--- a/src/core/kill.c
+++ b/src/core/kill.c
@@ -19,10 +19,9 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <string.h>
-
-#include "kill.h"
#include "util.h"
+#include "signal-util.h"
+#include "kill.h"
void kill_context_init(KillContext *c) {
assert(c);
diff --git a/src/core/killall.c b/src/core/killall.c
index 5a50ae6f04..6e85923581 100644
--- a/src/core/killall.c
+++ b/src/core/killall.c
@@ -25,9 +25,11 @@
#include <unistd.h>
#include "util.h"
-#include "def.h"
#include "killall.h"
#include "set.h"
+#include "formats-util.h"
+#include "process-util.h"
+#include "terminal-util.h"
#define TIMEOUT_USEC (10 * USEC_PER_SEC)
diff --git a/src/core/kmod-setup.c b/src/core/kmod-setup.c
index c0a05b97aa..6cc5951719 100644
--- a/src/core/kmod-setup.c
+++ b/src/core/kmod-setup.c
@@ -19,18 +19,16 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <sys/wait.h>
#include <unistd.h>
#include <string.h>
-#include <errno.h>
#ifdef HAVE_KMOD
#include <libkmod.h>
#endif
#include "macro.h"
-#include "execute.h"
#include "capability.h"
+#include "bus-util.h"
#include "kmod-setup.h"
#ifdef HAVE_KMOD
@@ -47,10 +45,6 @@ static void systemd_kmod_log(
log_internalv(LOG_DEBUG, 0, file, line, fn, format, args);
REENABLE_WARNING;
}
-
-static bool cmdline_check_kdbus(void) {
- return get_proc_cmdline_key("kdbus", NULL) > 0;
-}
#endif
int kmod_setup(void) {
@@ -63,16 +57,23 @@ int kmod_setup(void) {
bool (*condition_fn)(void);
} kmod_table[] = {
/* auto-loading on use doesn't work before udev is up */
- { "autofs4", "/sys/class/misc/autofs", true, NULL },
+ { "autofs4", "/sys/class/misc/autofs", true, NULL },
/* early configure of ::1 on the loopback device */
- { "ipv6", "/sys/module/ipv6", true, NULL },
+ { "ipv6", "/sys/module/ipv6", true, NULL },
/* this should never be a module */
- { "unix", "/proc/net/unix", true, NULL },
+ { "unix", "/proc/net/unix", true, NULL },
+#ifdef ENABLE_KDBUS
/* IPC is needed before we bring up any other services */
- { "kdbus", "/sys/fs/kdbus", false, cmdline_check_kdbus },
+ { "kdbus", "/sys/fs/kdbus", false, is_kdbus_wanted },
+#endif
+
+#ifdef HAVE_LIBIPTC
+ /* netfilter is needed by networkd, nspawn among others, and cannot be autoloaded */
+ { "ip_tables", "/proc/net/ip_tables_names", false, NULL },
+#endif
};
struct kmod_ctx *ctx = NULL;
unsigned int i;
diff --git a/src/core/load-dropin.c b/src/core/load-dropin.c
index 8be190040e..11566af51b 100644
--- a/src/core/load-dropin.c
+++ b/src/core/load-dropin.c
@@ -19,8 +19,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <dirent.h>
-#include <errno.h>
#include "unit.h"
#include "load-dropin.h"
@@ -29,7 +27,6 @@
#include "unit-name.h"
#include "conf-parser.h"
#include "load-fragment.h"
-#include "conf-files.h"
static int add_dependency_consumer(
UnitDependency dependency,
diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4
index 53059845e3..66c9145aa6 100644
--- a/src/core/load-fragment-gperf.gperf.m4
+++ b/src/core/load-fragment-gperf.gperf.m4
@@ -318,6 +318,7 @@ KILL_CONTEXT_CONFIG_ITEMS(Mount)m4_dnl
m4_dnl
Automount.Where, config_parse_path, 0, offsetof(Automount, where)
Automount.DirectoryMode, config_parse_mode, 0, offsetof(Automount, directory_mode)
+Automount.TimeoutIdleSec, config_parse_sec, 0, offsetof(Automount, timeout_idle_usec)
m4_dnl
Swap.What, config_parse_path, 0, offsetof(Swap, parameters_fragment.what)
Swap.Priority, config_parse_int, 0, offsetof(Swap, parameters_fragment.priority)
diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c
index 90bf5634c8..df5fe6fb32 100644
--- a/src/core/load-fragment.c
+++ b/src/core/load-fragment.c
@@ -21,26 +21,18 @@
***/
#include <linux/oom.h>
-#include <assert.h>
#include <errno.h>
#include <string.h>
-#include <unistd.h>
#include <fcntl.h>
#include <sched.h>
-#include <sys/prctl.h>
-#include <sys/mount.h>
#include <linux/fs.h>
#include <sys/stat.h>
-#include <sys/time.h>
#include <sys/resource.h>
-#include <sys/types.h>
-#include <grp.h>
#ifdef HAVE_SECCOMP
#include <seccomp.h>
#endif
-#include "sd-messages.h"
#include "unit.h"
#include "strv.h"
#include "conf-parser.h"
@@ -60,6 +52,7 @@
#include "errno-list.h"
#include "af-list.h"
#include "cap-list.h"
+#include "signal-util.h"
#include "bus-internal.h"
#ifdef HAVE_SECCOMP
@@ -364,7 +357,7 @@ int config_parse_socket_listen(const char *unit,
log_syntax(unit, LOG_ERR, filename, line, -r,
"Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r));
- r = socket_address_parse(&p->address, k ? k : rvalue);
+ r = socket_address_parse_and_warn(&p->address, k ? k : rvalue);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, -r,
"Failed to parse address value, ignoring: %s", rvalue);
@@ -381,7 +374,7 @@ int config_parse_socket_listen(const char *unit,
}
if (socket_address_family(&p->address) != AF_LOCAL && p->address.type == SOCK_SEQPACKET) {
- log_syntax(unit, LOG_ERR, filename, line, ENOTSUP,
+ log_syntax(unit, LOG_ERR, filename, line, EOPNOTSUPP,
"Address family not supported, ignoring: %s", rvalue);
return 0;
}
@@ -515,16 +508,17 @@ int config_parse_exec_oom_score_adjust(const char* unit,
return 0;
}
-int config_parse_exec(const char *unit,
- const char *filename,
- unsigned line,
- const char *section,
- unsigned section_line,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
+int config_parse_exec(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
ExecCommand **e = data, *nce;
char *path, **n;
@@ -576,15 +570,13 @@ int config_parse_exec(const char *unit,
word ++;
}
}
- } else
- if (strneq(word, ";", MAX(l, 1U)))
- goto found;
+ } else if (strneq(word, ";", MAX(l, 1U)))
+ goto found;
k++;
}
if (!isempty(state)) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Trailing garbage, ignoring.");
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Trailing garbage, ignoring.");
return 0;
}
@@ -604,7 +596,12 @@ int config_parse_exec(const char *unit,
skip = separate_argv0 + ignore;
/* skip special chars in the beginning */
- assert(skip < l);
+ if (l <= skip) {
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ "Empty path in command line, ignoring: \"%s\"", rvalue);
+ r = 0;
+ goto fail;
+ }
} else if (strneq(word, ";", MAX(l, 1U)))
/* new commandline */
@@ -613,9 +610,10 @@ int config_parse_exec(const char *unit,
else
skip = strneq(word, "\\;", MAX(l, 1U));
- c = cunescape_length(word + skip, l - skip);
- if (!c) {
- r = log_oom();
+ r = cunescape_length(word + skip, l - skip, UNESCAPE_RELAX, &c);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to unescape command line, ignoring: %s", rvalue);
+ r = 0;
goto fail;
}
@@ -634,8 +632,6 @@ int config_parse_exec(const char *unit,
n[k] = NULL;
- log_debug("path: %s", path ?: n[0]);
-
if (!n[0])
reason = "Empty executable name or zeroeth argument";
else if (!string_is_safe(path ?: n[0]))
@@ -647,8 +643,7 @@ int config_parse_exec(const char *unit,
else
goto ok;
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "%s, ignoring: %s", reason, rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL, "%s, ignoring: %s", reason, rvalue);
r = 0;
goto fail;
@@ -1213,17 +1208,15 @@ int config_parse_exec_mount_flags(const char *unit,
flags = MS_SHARED;
else if (streq(t, "slave"))
flags = MS_SLAVE;
- else if (streq(word, "private"))
+ else if (streq(t, "private"))
flags = MS_PRIVATE;
else {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Failed to parse mount flag %s, ignoring: %s", t, rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Failed to parse mount flag %s, ignoring: %s", t, rvalue);
return 0;
}
}
if (!isempty(state))
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Trailing garbage, ignoring.");
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Trailing garbage, ignoring.");
c->mount_flags = flags;
return 0;
@@ -1986,8 +1979,7 @@ int config_parse_environ(const char *unit,
if (u) {
r = unit_full_printf(u, rvalue, &k);
if (r < 0)
- log_syntax(unit, LOG_ERR, filename, line, -r,
- "Failed to resolve specifiers, ignoring: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, -r, "Failed to resolve specifiers, ignoring: %s", rvalue);
}
if (!k)
@@ -1999,13 +1991,14 @@ int config_parse_environ(const char *unit,
_cleanup_free_ char *n;
char **x;
- n = cunescape_length(word, l);
- if (!n)
- return log_oom();
+ r = cunescape_length(word, l, 0, &n);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Couldn't unescape assignment, ignoring: %s", rvalue);
+ continue;
+ }
if (!env_assignment_is_valid(n)) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Invalid environment assignment, ignoring: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Invalid environment assignment, ignoring: %s", rvalue);
continue;
}
@@ -2380,7 +2373,7 @@ int config_parse_syscall_filter(
continue;
r = set_put(c->syscall_filter, INT_TO_PTR(id + 1));
- if (r == -EEXIST)
+ if (r == 0)
continue;
if (r < 0)
return log_oom();
@@ -2408,7 +2401,7 @@ int config_parse_syscall_filter(
*/
if (!invert == c->syscall_whitelist) {
r = set_put(c->syscall_filter, INT_TO_PTR(id + 1));
- if (r == -EEXIST)
+ if (r == 0)
continue;
if (r < 0)
return log_oom();
@@ -2421,7 +2414,7 @@ int config_parse_syscall_filter(
/* Turn on NNP, but only if it wasn't configured explicitly
* before, and only if we are in user mode. */
- if (!c->no_new_privileges_set && u->manager->running_as == SYSTEMD_USER)
+ if (!c->no_new_privileges_set && u->manager->running_as == MANAGER_USER)
c->no_new_privileges = true;
return 0;
@@ -2470,7 +2463,7 @@ int config_parse_syscall_archs(
}
r = set_put(*archs, UINT32_TO_PTR(a + 1));
- if (r == -EEXIST)
+ if (r == 0)
continue;
if (r < 0)
return log_oom();
@@ -2581,7 +2574,7 @@ int config_parse_address_families(
*/
if (!invert == c->address_families_whitelist) {
r = set_put(c->address_families, INT_TO_PTR(af));
- if (r == -EEXIST)
+ if (r == 0)
continue;
if (r < 0)
return log_oom();
@@ -3054,7 +3047,7 @@ int config_parse_personality(
assert(personality);
p = personality_from_string(rvalue);
- if (p == 0xffffffffUL) {
+ if (p == PERSONALITY_INVALID) {
log_syntax(unit, LOG_ERR, filename, line, EINVAL,
"Failed to parse personality, ignoring: %s", rvalue);
return 0;
@@ -3407,7 +3400,7 @@ static int open_follow(char **filename, FILE **_f, Set *names, char **_final) {
* unit name. */
name = basename(*filename);
- if (unit_name_is_valid(name, TEMPLATE_VALID)) {
+ if (unit_name_is_valid(name, UNIT_NAME_ANY)) {
id = set_get(names, name);
if (!id) {
@@ -3440,9 +3433,8 @@ static int open_follow(char **filename, FILE **_f, Set *names, char **_final) {
f = fdopen(fd, "re");
if (!f) {
- r = -errno;
safe_close(fd);
- return r;
+ return -errno;
}
*_f = f;
@@ -3656,11 +3648,11 @@ int unit_load_fragment(Unit *u) {
/* Look for a template */
if (u->load_state == UNIT_STUB && u->instance) {
- _cleanup_free_ char *k;
+ _cleanup_free_ char *k = NULL;
- k = unit_name_template(u->id);
- if (!k)
- return -ENOMEM;
+ r = unit_name_template(u->id, &k);
+ if (r < 0)
+ return r;
r = load_from_path(u, k);
if (r < 0)
@@ -3673,9 +3665,9 @@ int unit_load_fragment(Unit *u) {
if (t == u->id)
continue;
- z = unit_name_template(t);
- if (!z)
- return -ENOMEM;
+ r = unit_name_template(t, &z);
+ if (r < 0)
+ return r;
r = load_from_path(u, z);
if (r < 0)
diff --git a/src/core/locale-setup.c b/src/core/locale-setup.c
index e993c57321..108072ca9f 100644
--- a/src/core/locale-setup.c
+++ b/src/core/locale-setup.c
@@ -19,13 +19,11 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include "locale-setup.h"
#include "util.h"
-#include "macro.h"
#include "virt.h"
#include "fileio.h"
#include "strv.h"
diff --git a/src/core/loopback-setup.c b/src/core/loopback-setup.c
index 67ce160c19..63b15c1200 100644
--- a/src/core/loopback-setup.c
+++ b/src/core/loopback-setup.c
@@ -19,18 +19,10 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <sys/socket.h>
#include <net/if.h>
-#include <asm/types.h>
-#include <netinet/in.h>
-#include <string.h>
#include <stdlib.h>
-#include <unistd.h>
#include "sd-rtnl.h"
-#include "util.h"
-#include "macro.h"
-#include "socket-util.h"
#include "rtnl-util.h"
#include "missing.h"
#include "loopback-setup.h"
diff --git a/src/core/machine-id-setup.c b/src/core/machine-id-setup.c
index d00a53246f..b3d22840cf 100644
--- a/src/core/machine-id-setup.c
+++ b/src/core/machine-id-setup.c
@@ -23,7 +23,6 @@
#include <stdio.h>
#include <errno.h>
#include <string.h>
-#include <stdlib.h>
#include <fcntl.h>
#include <sys/mount.h>
@@ -37,10 +36,17 @@
#include "virt.h"
#include "fileio.h"
#include "path-util.h"
+#include "process-util.h"
static int shorten_uuid(char destination[34], const char source[36]) {
unsigned i, j;
+ assert(destination);
+ assert(source);
+
+ /* Converts a UUID into a machine ID, by lowercasing it and
+ * removing dashes. Validates everything. */
+
for (i = 0, j = 0; i < 36 && j < 32; i++) {
int t;
@@ -51,21 +57,57 @@ static int shorten_uuid(char destination[34], const char source[36]) {
destination[j++] = hexchar(t);
}
- if (i == 36 && j == 32) {
- destination[32] = '\n';
- destination[33] = 0;
- return 0;
- }
+ if (i != 36 || j != 32)
+ return -EINVAL;
+
+ destination[32] = '\n';
+ destination[33] = 0;
+ return 0;
+}
+
+static int read_machine_id(int fd, char id[34]) {
+ char id_to_validate[34];
+ int r;
+
+ assert(fd >= 0);
+ assert(id);
- return -EINVAL;
+ /* Reads a machine ID from a file, validates it, and returns
+ * it. The returned ID ends in a newline. */
+
+ r = loop_read_exact(fd, id_to_validate, 33, false);
+ if (r < 0)
+ return r;
+
+ if (id_to_validate[32] != '\n')
+ return -EINVAL;
+
+ id_to_validate[32] = 0;
+
+ if (!id128_is_valid(id_to_validate))
+ return -EINVAL;
+
+ memcpy(id, id_to_validate, 32);
+ id[32] = '\n';
+ id[33] = 0;
+ return 0;
}
-static int generate(char id[34], const char *root) {
+static int write_machine_id(int fd, char id[34]) {
+ assert(fd >= 0);
+ assert(id);
+
+ if (lseek(fd, 0, SEEK_SET) < 0)
+ return -errno;
+
+ return loop_write(fd, id, 33, false);
+}
+
+static int generate_machine_id(char id[34], const char *root) {
int fd, r;
unsigned char *p;
sd_id128_t buf;
char *q;
- ssize_t k;
const char *vm_id, *dbus_machine_id;
assert(id);
@@ -78,19 +120,12 @@ static int generate(char id[34], const char *root) {
/* First, try reading the D-Bus machine id, unless it is a symlink */
fd = open(dbus_machine_id, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
if (fd >= 0) {
- k = loop_read(fd, id, 33, false);
+ r = read_machine_id(fd, id);
safe_close(fd);
- if (k == 33 && id[32] == '\n') {
-
- 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;
- }
+ if (r >= 0) {
+ log_info("Initializing machine ID from D-Bus machine ID.");
+ return 0;
}
}
@@ -104,12 +139,10 @@ static int generate(char id[34], const char *root) {
r = getenv_for_pid(1, "container_uuid", &e);
if (r > 0) {
- if (strlen(e) >= 36) {
- r = shorten_uuid(id, e);
- if (r >= 0) {
- log_info("Initializing machine ID from container UUID.");
- return 0;
- }
+ r = shorten_uuid(id, e);
+ if (r >= 0) {
+ log_info("Initializing machine ID from container UUID.");
+ return 0;
}
}
@@ -120,14 +153,14 @@ static int generate(char id[34], const char *root) {
r = detect_vm(&vm_id);
if (r > 0 && streq(vm_id, "kvm")) {
- char uuid[37];
+ char uuid[36];
fd = open("/sys/class/dmi/id/product_uuid", O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
if (fd >= 0) {
- k = loop_read(fd, uuid, 36, false);
+ r = loop_read_exact(fd, uuid, 36, false);
safe_close(fd);
- if (k >= 36) {
+ if (r >= 0) {
r = shorten_uuid(id, uuid);
if (r >= 0) {
log_info("Initializing machine ID from KVM UUID.");
@@ -157,35 +190,96 @@ static int generate(char id[34], const char *root) {
return 0;
}
-static int get_valid_machine_id(int fd, char id[34]) {
- char id_to_validate[34];
+int machine_id_setup(const char *root) {
+ const char *etc_machine_id, *run_machine_id;
+ _cleanup_close_ int fd = -1;
+ bool writable = true;
+ char id[34]; /* 32 + \n + \0 */
+ int r;
- assert(fd >= 0);
- assert(id);
+ if (isempty(root)) {
+ etc_machine_id = "/etc/machine-id";
+ run_machine_id = "/run/machine-id";
+ } else {
+ char *x;
- if (loop_read(fd, id_to_validate, 33, false) == 33 && id_to_validate[32] == '\n') {
- id_to_validate[32] = 0;
+ x = strjoina(root, "/etc/machine-id");
+ etc_machine_id = path_kill_slashes(x);
- if (id128_is_valid(id_to_validate)) {
- memcpy(id, id_to_validate, 32);
- id[32] = '\n';
- id[33] = 0;
- return 0;
- }
+ x = strjoina(root, "/run/machine-id");
+ run_machine_id = path_kill_slashes(x);
}
- return -EINVAL;
-}
+ 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. */
-static int write_machine_id(int fd, char id[34]) {
- assert(fd >= 0);
- assert(id);
- lseek(fd, 0, SEEK_SET);
+ mkdir_parents(etc_machine_id, 0755);
+ fd = open(etc_machine_id, O_RDWR|O_CREAT|O_CLOEXEC|O_NOCTTY, 0444);
+ if (fd < 0) {
+ int old_errno = errno;
+
+ fd = open(etc_machine_id, O_RDONLY|O_CLOEXEC|O_NOCTTY);
+ if (fd < 0) {
+ if (old_errno == EROFS && errno == ENOENT)
+ log_error_errno(errno,
+ "System cannot boot: Missing /etc/machine-id and /etc is mounted read-only.\n"
+ "Booting up is supported only when:\n"
+ "1) /etc/machine-id exists and is populated.\n"
+ "2) /etc/machine-id exists and is empty.\n"
+ "3) /etc/machine-id is missing and /etc is writable.\n");
+ else
+ log_error_errno(errno, "Cannot open %s: %m", etc_machine_id);
- if (loop_write(fd, id, 33, false) == 0)
+ return -errno;
+ }
+
+ writable = false;
+ }
+ }
+
+ if (read_machine_id(fd, id) >= 0)
return 0;
- return -errno;
+ /* Hmm, so, the id currently stored is not useful, then let's
+ * generate one */
+
+ r = generate_machine_id(id, root);
+ if (r < 0)
+ return r;
+
+ if (writable)
+ if (write_machine_id(fd, id) >= 0)
+ return 0;
+
+ fd = safe_close(fd);
+
+ /* Hmm, we couldn't write it? So let's write it to
+ * /run/machine-id as a replacement */
+
+ RUN_WITH_UMASK(0022) {
+ r = write_string_file(run_machine_id, id);
+ }
+ if (r < 0) {
+ (void) unlink(run_machine_id);
+ return log_error_errno(r, "Cannot write %s: %m", run_machine_id);
+ }
+
+ /* And now, let's mount it over */
+ if (mount(run_machine_id, etc_machine_id, NULL, MS_BIND, NULL) < 0) {
+ (void) unlink_noerrno(run_machine_id);
+ return log_error_errno(errno, "Failed to mount %s: %m", etc_machine_id);
+ }
+
+ log_info("Installed transient %s file.", etc_machine_id);
+
+ /* Mark the mount read-only */
+ if (mount(NULL, etc_machine_id, NULL, MS_BIND|MS_RDONLY|MS_REMOUNT, NULL) < 0)
+ log_warning_errno(errno, "Failed to make transient %s read-only: %m", etc_machine_id);
+
+ return 0;
}
int machine_id_commit(const char *root) {
@@ -203,7 +297,7 @@ int machine_id_commit(const char *root) {
etc_machine_id = path_kill_slashes(x);
}
- r = path_is_mount_point(etc_machine_id, false);
+ r = path_is_mount_point(etc_machine_id, 0);
if (r < 0)
return log_error_errno(r, "Failed to determine whether %s is a mount point: %m", etc_machine_id);
if (r == 0) {
@@ -216,11 +310,11 @@ int machine_id_commit(const char *root) {
if (fd < 0)
return log_error_errno(errno, "Cannot open %s: %m", etc_machine_id);
- r = get_valid_machine_id(fd, id);
+ r = read_machine_id(fd, id);
if (r < 0)
return log_error_errno(r, "We didn't find a valid machine ID in %s.", etc_machine_id);
- r = is_fd_on_temporary_fs(fd);
+ r = fd_is_temporary_fs(fd);
if (r < 0)
return log_error_errno(r, "Failed to determine whether %s is on a temporary file system: %m", etc_machine_id);
if (r == 0) {
@@ -266,100 +360,3 @@ int machine_id_commit(const char *root) {
return 0;
}
-
-int machine_id_setup(const char *root) {
- const char *etc_machine_id, *run_machine_id;
- _cleanup_close_ int fd = -1;
- bool writable = true;
- struct stat st;
- char id[34]; /* 32 + \n + \0 */
- int r;
-
- if (isempty(root)) {
- etc_machine_id = "/etc/machine-id";
- run_machine_id = "/run/machine-id";
- } else {
- char *x;
-
- x = strjoina(root, "/etc/machine-id");
- etc_machine_id = path_kill_slashes(x);
-
- x = strjoina(root, "/run/machine-id");
- run_machine_id = path_kill_slashes(x);
- }
-
- 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. */
-
- mkdir_parents(etc_machine_id, 0755);
- fd = open(etc_machine_id, O_RDWR|O_CREAT|O_CLOEXEC|O_NOCTTY, 0444);
- if (fd < 0) {
- int old_errno = errno;
-
- fd = open(etc_machine_id, O_RDONLY|O_CLOEXEC|O_NOCTTY);
- if (fd < 0) {
- if (old_errno == EROFS && errno == ENOENT)
- log_error("System cannot boot: Missing /etc/machine-id and /etc is mounted read-only.\n"
- "Booting up is supported only when:\n"
- "1) /etc/machine-id exists and is populated.\n"
- "2) /etc/machine-id exists and is empty.\n"
- "3) /etc/machine-id is missing and /etc is writable.\n");
- else
- log_error_errno(errno, "Cannot open %s: %m", etc_machine_id);
- return -errno;
- }
-
- writable = false;
- }
- }
-
- if (fstat(fd, &st) < 0)
- return log_error_errno(errno, "fstat() failed: %m");
-
- if (S_ISREG(st.st_mode) && get_valid_machine_id(fd, id) == 0)
- return 0;
-
- /* Hmm, so, the id currently stored is not useful, then let's
- * generate one */
-
- r = generate(id, root);
- if (r < 0)
- return r;
-
- if (S_ISREG(st.st_mode) && writable)
- if (write_machine_id(fd, id) == 0)
- return 0;
-
- fd = safe_close(fd);
-
- /* Hmm, we couldn't write it? So let's write it to
- * /run/machine-id as a replacement */
-
- RUN_WITH_UMASK(0022) {
- r = write_string_file(run_machine_id, id);
- }
- if (r < 0) {
- log_error_errno(r, "Cannot write %s: %m", run_machine_id);
- unlink(run_machine_id);
- return r;
- }
-
- /* And now, let's mount it over */
- r = mount(run_machine_id, etc_machine_id, NULL, MS_BIND, NULL);
- if (r < 0) {
- log_error_errno(errno, "Failed to mount %s: %m", etc_machine_id);
- unlink_noerrno(run_machine_id);
- return -errno;
- }
-
- log_info("Installed transient %s file.", etc_machine_id);
-
- /* Mark the mount read-only */
- if (mount(NULL, etc_machine_id, NULL, MS_BIND|MS_RDONLY|MS_REMOUNT, NULL) < 0)
- log_warning_errno(errno, "Failed to make transient %s read-only: %m", etc_machine_id);
-
- return 0;
-}
diff --git a/src/core/main.c b/src/core/main.c
index ba2de85bd3..674e47e788 100644
--- a/src/core/main.c
+++ b/src/core/main.c
@@ -23,11 +23,9 @@
#include <errno.h>
#include <string.h>
#include <unistd.h>
-#include <sys/types.h>
#include <sys/stat.h>
#include <getopt.h>
#include <signal.h>
-#include <sys/wait.h>
#include <fcntl.h>
#include <sys/prctl.h>
#include <sys/mount.h>
@@ -40,14 +38,12 @@
#endif
#include "sd-daemon.h"
-#include "sd-messages.h"
#include "sd-bus.h"
#include "log.h"
#include "fdset.h"
#include "special.h"
#include "conf-parser.h"
#include "missing.h"
-#include "label.h"
#include "pager.h"
#include "build.h"
#include "strv.h"
@@ -55,7 +51,6 @@
#include "virt.h"
#include "architecture.h"
#include "watchdog.h"
-#include "path-util.h"
#include "switch-root.h"
#include "capability.h"
#include "killall.h"
@@ -65,6 +60,10 @@
#include "bus-error.h"
#include "bus-util.h"
#include "selinux-util.h"
+#include "formats-util.h"
+#include "process-util.h"
+#include "terminal-util.h"
+#include "signal-util.h"
#include "manager.h"
#include "dbus-manager.h"
#include "load-fragment.h"
@@ -87,7 +86,7 @@ static enum {
ACTION_DONE
} arg_action = ACTION_RUN;
static char *arg_default_unit = NULL;
-static SystemdRunningAs arg_running_as = _SYSTEMD_RUNNING_AS_INVALID;
+static ManagerRunningAs arg_running_as = _MANAGER_RUNNING_AS_INVALID;
static bool arg_dump_core = true;
static bool arg_crash_shell = false;
static int arg_crash_chvt = -1;
@@ -161,7 +160,7 @@ noreturn static void crash(int sig) {
setrlimit(RLIMIT_CORE, &rl);
/* Just to be sure... */
- chdir("/");
+ (void) chdir("/");
/* Raise the signal again */
pid = raw_getpid();
@@ -280,10 +279,10 @@ static int parse_proc_cmdline_item(const char *key, const char *value) {
"s", SPECIAL_RESCUE_TARGET,
"S", SPECIAL_RESCUE_TARGET,
"1", SPECIAL_RESCUE_TARGET,
- "2", SPECIAL_RUNLEVEL2_TARGET,
- "3", SPECIAL_RUNLEVEL3_TARGET,
- "4", SPECIAL_RUNLEVEL4_TARGET,
- "5", SPECIAL_RUNLEVEL5_TARGET,
+ "2", SPECIAL_MULTI_USER_TARGET,
+ "3", SPECIAL_MULTI_USER_TARGET,
+ "4", SPECIAL_MULTI_USER_TARGET,
+ "5", SPECIAL_GRAPHICAL_TARGET,
};
int r;
@@ -473,7 +472,7 @@ static int config_parse_cpu_affinity2(
if (c) {
if (sched_setaffinity(0, CPU_ALLOC_SIZE(ncpus), c) < 0)
- log_unit_warning(unit, "Failed to set CPU affinity: %m");
+ log_warning("Failed to set CPU affinity: %m");
CPU_FREE(c);
}
@@ -678,8 +677,8 @@ static int parse_config_file(void) {
const char *fn, *conf_dirs_nulstr;
- fn = arg_running_as == SYSTEMD_SYSTEM ? PKGSYSCONFDIR "/system.conf" : PKGSYSCONFDIR "/user.conf";
- conf_dirs_nulstr = arg_running_as == SYSTEMD_SYSTEM ? CONF_DIRS_NULSTR("systemd/system.conf") : CONF_DIRS_NULSTR("systemd/user.conf");
+ fn = arg_running_as == MANAGER_SYSTEM ? PKGSYSCONFDIR "/system.conf" : PKGSYSCONFDIR "/user.conf";
+ conf_dirs_nulstr = arg_running_as == MANAGER_SYSTEM ? CONF_DIRS_NULSTR("systemd/system.conf") : CONF_DIRS_NULSTR("systemd/user.conf");
config_parse_many(fn, conf_dirs_nulstr, "Manager\0",
config_item_table_lookup, items, false, NULL);
@@ -816,11 +815,11 @@ static int parse_argv(int argc, char *argv[]) {
break;
case ARG_SYSTEM:
- arg_running_as = SYSTEMD_SYSTEM;
+ arg_running_as = MANAGER_SYSTEM;
break;
case ARG_USER:
- arg_running_as = SYSTEMD_USER;
+ arg_running_as = MANAGER_USER;
break;
case ARG_TEST:
@@ -1277,7 +1276,7 @@ int main(int argc, char *argv[]) {
if (getpid() == 1 && detect_container(NULL) <= 0) {
/* Running outside of a container as PID 1 */
- arg_running_as = SYSTEMD_SYSTEM;
+ arg_running_as = MANAGER_SYSTEM;
make_null_stdio();
log_set_target(LOG_TARGET_KMSG);
log_open();
@@ -1351,7 +1350,7 @@ int main(int argc, char *argv[]) {
} else if (getpid() == 1) {
/* Running inside a container, as PID 1 */
- arg_running_as = SYSTEMD_SYSTEM;
+ arg_running_as = MANAGER_SYSTEM;
log_set_target(LOG_TARGET_CONSOLE);
log_close_console(); /* force reopen of /dev/console */
log_open();
@@ -1366,7 +1365,7 @@ int main(int argc, char *argv[]) {
} else {
/* Running as user instance */
- arg_running_as = SYSTEMD_USER;
+ arg_running_as = MANAGER_USER;
log_set_target(LOG_TARGET_AUTO);
log_open();
@@ -1386,7 +1385,7 @@ int main(int argc, char *argv[]) {
r = initialize_join_controllers();
if (r < 0) {
- error_message = "Failed to initalize cgroup controllers";
+ error_message = "Failed to initialize cgroup controllers";
goto finish;
}
@@ -1415,7 +1414,7 @@ int main(int argc, char *argv[]) {
goto finish;
}
- if (arg_running_as == SYSTEMD_SYSTEM) {
+ if (arg_running_as == MANAGER_SYSTEM) {
r = parse_proc_cmdline(parse_proc_cmdline_item);
if (r < 0)
log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m");
@@ -1436,14 +1435,14 @@ int main(int argc, char *argv[]) {
goto finish;
}
- if (arg_running_as == SYSTEMD_USER &&
+ if (arg_running_as == MANAGER_USER &&
arg_action == ACTION_RUN &&
sd_booted() <= 0) {
log_error("Trying to run as user instance, but the system has not been booted with systemd.");
goto finish;
}
- if (arg_running_as == SYSTEMD_SYSTEM &&
+ if (arg_running_as == MANAGER_SYSTEM &&
arg_action == ACTION_RUN &&
running_in_chroot() > 0) {
log_error("Cannot be run in a chroot() environment.");
@@ -1470,7 +1469,7 @@ int main(int argc, char *argv[]) {
goto finish;
}
- if (arg_running_as == SYSTEMD_USER &&
+ if (arg_running_as == MANAGER_USER &&
!getenv("XDG_RUNTIME_DIR")) {
log_error("Trying to run as user instance, but $XDG_RUNTIME_DIR is not set.");
goto finish;
@@ -1493,16 +1492,16 @@ int main(int argc, char *argv[]) {
if (arg_serialization)
assert_se(fdset_remove(fds, fileno(arg_serialization)) >= 0);
- if (arg_running_as == SYSTEMD_SYSTEM)
+ if (arg_running_as == MANAGER_SYSTEM)
/* Become a session leader if we aren't one yet. */
setsid();
/* Move out of the way, so that we won't block unmounts */
- assert_se(chdir("/") == 0);
+ assert_se(chdir("/") == 0);
/* Reset the console, but only if this is really init and we
* are freshly booted */
- if (arg_running_as == SYSTEMD_SYSTEM && arg_action == ACTION_RUN) {
+ if (arg_running_as == MANAGER_SYSTEM && arg_action == ACTION_RUN) {
/* If we are init, we connect stdin/stdout/stderr to
* /dev/null and make sure we don't have a controlling
@@ -1529,7 +1528,7 @@ int main(int argc, char *argv[]) {
goto finish;
}
- if (arg_running_as == SYSTEMD_SYSTEM) {
+ if (arg_running_as == MANAGER_SYSTEM) {
const char *virtualization = NULL;
log_info(PACKAGE_STRING " running in %ssystem mode. (" SYSTEMD_FEATURES ")",
@@ -1537,11 +1536,11 @@ int main(int argc, char *argv[]) {
detect_virtualization(&virtualization);
if (virtualization)
- log_info("Detected virtualization '%s'.", virtualization);
+ log_info("Detected virtualization %s.", virtualization);
write_container_id();
- log_info("Detected architecture '%s'.", architecture_to_string(uname_architecture()));
+ log_info("Detected architecture %s.", architecture_to_string(uname_architecture()));
if (in_initrd())
log_info("Running in initial RAM disk.");
@@ -1565,8 +1564,8 @@ int main(int argc, char *argv[]) {
arg_action == ACTION_TEST ? " test" : "", getuid(), t);
}
- if (arg_running_as == SYSTEMD_SYSTEM && !skip_setup) {
- if (arg_show_status > 0 || plymouth_running())
+ if (arg_running_as == MANAGER_SYSTEM && !skip_setup) {
+ if (arg_show_status > 0)
status_welcome();
hostname_setup();
@@ -1577,7 +1576,7 @@ int main(int argc, char *argv[]) {
test_usr();
}
- if (arg_running_as == SYSTEMD_SYSTEM && arg_runtime_watchdog > 0)
+ if (arg_running_as == MANAGER_SYSTEM && arg_runtime_watchdog > 0)
watchdog_set_timeout(&arg_runtime_watchdog);
if (arg_timer_slack_nsec != NSEC_INFINITY)
@@ -1607,7 +1606,7 @@ int main(int argc, char *argv[]) {
}
}
- if (arg_running_as == SYSTEMD_USER) {
+ if (arg_running_as == MANAGER_USER) {
/* Become reaper of our children */
if (prctl(PR_SET_CHILD_SUBREAPER, 1) < 0) {
log_warning_errno(errno, "Failed to make us a subreaper: %m");
@@ -1616,11 +1615,11 @@ int main(int argc, char *argv[]) {
}
}
- if (arg_running_as == SYSTEMD_SYSTEM) {
+ if (arg_running_as == MANAGER_SYSTEM) {
bump_rlimit_nofile(&saved_rlimit_nofile);
if (empty_etc) {
- r = unit_file_preset_all(UNIT_FILE_SYSTEM, false, NULL, UNIT_FILE_PRESET_FULL, false, NULL, 0);
+ r = unit_file_preset_all(UNIT_FILE_SYSTEM, false, NULL, UNIT_FILE_PRESET_ENABLE_ONLY, false, NULL, 0);
if (r < 0)
log_warning_errno(r, "Failed to populate /etc with preset unit settings, ignoring: %m");
else
@@ -1897,7 +1896,7 @@ finish:
args[i++] = SYSTEMD_BINARY_PATH;
if (switch_root_dir)
args[i++] = "--switched-root";
- args[i++] = arg_running_as == SYSTEMD_SYSTEM ? "--system" : "--user";
+ args[i++] = arg_running_as == MANAGER_SYSTEM ? "--system" : "--user";
args[i++] = "--deserialize";
args[i++] = sfd;
args[i++] = NULL;
diff --git a/src/core/manager.c b/src/core/manager.c
index 4775219e4a..a1c54339ea 100644
--- a/src/core/manager.c
+++ b/src/core/manager.c
@@ -19,7 +19,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <assert.h>
#include <errno.h>
#include <string.h>
#include <signal.h>
@@ -27,14 +26,10 @@
#include <unistd.h>
#include <sys/inotify.h>
#include <sys/epoll.h>
-#include <poll.h>
#include <sys/reboot.h>
#include <sys/ioctl.h>
#include <linux/kd.h>
-#include <termios.h>
#include <fcntl.h>
-#include <sys/types.h>
-#include <sys/stat.h>
#include <dirent.h>
#include <sys/timerfd.h>
@@ -43,11 +38,8 @@
#endif
#include "sd-daemon.h"
-#include "sd-id128.h"
#include "sd-messages.h"
-#include "manager.h"
-#include "transaction.h"
#include "hashmap.h"
#include "macro.h"
#include "strv.h"
@@ -56,15 +48,14 @@
#include "mkdir.h"
#include "ratelimit.h"
#include "locale-setup.h"
-#include "mount-setup.h"
#include "unit-name.h"
#include "missing.h"
+#include "rm-rf.h"
#include "path-lookup.h"
#include "special.h"
#include "exit-status.h"
#include "virt.h"
#include "watchdog.h"
-#include "cgroup-util.h"
#include "path-util.h"
#include "audit-fd.h"
#include "boot-timestamps.h"
@@ -72,12 +63,17 @@
#include "bus-common-errors.h"
#include "bus-error.h"
#include "bus-util.h"
+#include "bus-kernel.h"
+#include "time-util.h"
+#include "process-util.h"
+#include "terminal-util.h"
+#include "signal-util.h"
#include "dbus.h"
#include "dbus-unit.h"
#include "dbus-job.h"
#include "dbus-manager.h"
-#include "bus-kernel.h"
-#include "time-util.h"
+#include "manager.h"
+#include "transaction.h"
/* Initial delay and the interval for printing status messages about running jobs */
#define JOBS_IN_PROGRESS_WAIT_USEC (5*USEC_PER_SEC)
@@ -93,21 +89,26 @@ static int manager_dispatch_run_queue(sd_event_source *source, void *userdata);
static int manager_run_generators(Manager *m);
static void manager_undo_generators(Manager *m);
-static int manager_watch_jobs_in_progress(Manager *m) {
+static void manager_watch_jobs_in_progress(Manager *m) {
usec_t next;
+ int r;
assert(m);
if (m->jobs_in_progress_event_source)
- return 0;
+ return;
next = now(CLOCK_MONOTONIC) + JOBS_IN_PROGRESS_WAIT_USEC;
- return sd_event_add_time(
+ r = sd_event_add_time(
m->event,
&m->jobs_in_progress_event_source,
CLOCK_MONOTONIC,
next, 0,
manager_dispatch_jobs_in_progress, m);
+ if (r < 0)
+ return;
+
+ (void) sd_event_source_set_description(m->jobs_in_progress_event_source, "manager-jobs-in-progress");
}
#define CYLON_BUFFER_EXTRA (2*(sizeof(ANSI_RED_ON)-1) + sizeof(ANSI_HIGHLIGHT_RED_ON)-1 + 2*(sizeof(ANSI_HIGHLIGHT_OFF)-1))
@@ -186,8 +187,10 @@ static void manager_print_jobs_in_progress(Manager *m) {
m->jobs_in_progress_iteration++;
- if (m->n_running_jobs > 1)
- asprintf(&job_of_n, "(%u of %u) ", counter, m->n_running_jobs);
+ if (m->n_running_jobs > 1) {
+ if (asprintf(&job_of_n, "(%u of %u) ", counter, m->n_running_jobs) < 0)
+ job_of_n = NULL;
+ }
format_timespan(time, sizeof(time), now(CLOCK_MONOTONIC) - j->begin_usec, 1*USEC_PER_SEC);
if (job_get_timeout(j, &x) > 0)
@@ -281,6 +284,8 @@ static int manager_check_ask_password(Manager *m) {
return -errno;
}
+ (void) sd_event_source_set_description(m->ask_password_event_source, "manager-ask-password");
+
/* Queries might have been added meanwhile... */
manager_dispatch_ask_password_fd(m->ask_password_event_source,
m->ask_password_inotify_fd, EPOLLIN, m);
@@ -304,6 +309,8 @@ static int manager_watch_idle_pipe(Manager *m) {
if (r < 0)
return log_error_errno(r, "Failed to watch idle pipe: %m");
+ (void) sd_event_source_set_description(m->idle_pipe_event_source, "manager-idle-pipe");
+
return 0;
}
@@ -346,6 +353,8 @@ static int manager_setup_time_change(Manager *m) {
if (r < 0)
return log_error_errno(r, "Failed to create time change event source: %m");
+ (void) sd_event_source_set_description(m->time_change_event_source, "manager-time-change");
+
log_debug("Set up TFD_TIMER_CANCEL_ON_SET timerfd.");
return 0;
@@ -456,6 +465,8 @@ static int manager_setup_signals(Manager *m) {
if (r < 0)
return r;
+ (void) sd_event_source_set_description(m->signal_event_source, "manager-signal");
+
/* Process signals a bit earlier than the rest of things, but
* later than notify_fd processing, so that the notify
* processing can still figure out to which process/service a
@@ -464,7 +475,7 @@ static int manager_setup_signals(Manager *m) {
if (r < 0)
return r;
- if (m->running_as == SYSTEMD_SYSTEM)
+ if (m->running_as == MANAGER_SYSTEM)
return enable_special_signals(m);
return 0;
@@ -490,7 +501,7 @@ static void manager_clean_environment(Manager *m) {
static int manager_default_environment(Manager *m) {
assert(m);
- if (m->running_as == SYSTEMD_SYSTEM) {
+ if (m->running_as == MANAGER_SYSTEM) {
/* The system manager always starts with a clean
* environment for its children. It does not import
* the kernel or the parents exported variables.
@@ -518,20 +529,32 @@ static int manager_default_environment(Manager *m) {
return 0;
}
-int manager_new(SystemdRunningAs running_as, bool test_run, Manager **_m) {
+
+int manager_new(ManagerRunningAs running_as, bool test_run, Manager **_m) {
+
+ static const char * const unit_log_fields[_MANAGER_RUNNING_AS_MAX] = {
+ [MANAGER_SYSTEM] = "UNIT=",
+ [MANAGER_USER] = "USER_UNIT=",
+ };
+
+ static const char * const unit_log_format_strings[_MANAGER_RUNNING_AS_MAX] = {
+ [MANAGER_SYSTEM] = "UNIT=%s",
+ [MANAGER_USER] = "USER_UNIT=%s",
+ };
+
Manager *m;
int r;
assert(_m);
assert(running_as >= 0);
- assert(running_as < _SYSTEMD_RUNNING_AS_MAX);
+ assert(running_as < _MANAGER_RUNNING_AS_MAX);
m = new0(Manager, 1);
if (!m)
return -ENOMEM;
#ifdef ENABLE_EFI
- if (running_as == SYSTEMD_SYSTEM && detect_container(NULL) <= 0)
+ if (running_as == MANAGER_SYSTEM && detect_container(NULL) <= 0)
boot_timestamps(&m->userspace_timestamp, &m->firmware_timestamp, &m->loader_timestamp);
#endif
@@ -539,6 +562,10 @@ int manager_new(SystemdRunningAs running_as, bool test_run, Manager **_m) {
m->exit_code = _MANAGER_EXIT_CODE_INVALID;
m->default_timer_accuracy_usec = USEC_PER_MINUTE;
+ /* Prepare log fields we can use for structured logging */
+ m->unit_log_field = unit_log_fields[running_as];
+ m->unit_log_format_string = unit_log_format_strings[running_as];
+
m->idle_pipe[0] = m->idle_pipe[1] = m->idle_pipe[2] = m->idle_pipe[3] = -1;
m->pin_cgroupfs_fd = m->notify_fd = m->signal_fd = m->time_change_fd = m->dev_autofs_fd = m->private_listen_fd = m->kdbus_fd = m->utab_inotify_fd = -1;
@@ -596,6 +623,8 @@ int manager_new(SystemdRunningAs running_as, bool test_run, Manager **_m) {
if (r < 0)
goto fail;
+ (void) sd_event_source_set_description(m->run_queue_event_source, "manager-run-queue");
+
r = manager_setup_signals(m);
if (r < 0)
goto fail;
@@ -650,7 +679,7 @@ static int manager_setup_notify(Manager *m) {
if (fd < 0)
return log_error_errno(errno, "Failed to allocate notification socket: %m");
- if (m->running_as == SYSTEMD_SYSTEM)
+ if (m->running_as == MANAGER_SYSTEM)
m->notify_socket = strdup("/run/systemd/notify");
else {
const char *e;
@@ -694,6 +723,8 @@ static int manager_setup_notify(Manager *m) {
r = sd_event_source_set_priority(m->notify_event_source, -7);
if (r < 0)
return log_error_errno(r, "Failed to set priority of notify event source: %m");
+
+ (void) sd_event_source_set_description(m->notify_event_source, "manager-notify");
}
return 0;
@@ -707,13 +738,12 @@ static int manager_setup_kdbus(Manager *m) {
if (m->test_run || m->kdbus_fd >= 0)
return 0;
-
- if (m->running_as == SYSTEMD_SYSTEM && detect_container(NULL) <= 0)
- bus_kernel_fix_attach_mask();
+ if (!is_kdbus_available())
+ return -ESOCKTNOSUPPORT;
m->kdbus_fd = bus_kernel_create_bus(
- m->running_as == SYSTEMD_SYSTEM ? "system" : "user",
- m->running_as == SYSTEMD_SYSTEM, &p);
+ m->running_as == MANAGER_SYSTEM ? "system" : "user",
+ m->running_as == MANAGER_SYSTEM, &p);
if (m->kdbus_fd < 0)
return log_debug_errno(m->kdbus_fd, "Failed to set up kdbus: %m");
@@ -735,9 +765,9 @@ static int manager_connect_bus(Manager *m, bool reexecuting) {
try_bus_connect =
m->kdbus_fd >= 0 ||
reexecuting ||
- (m->running_as == SYSTEMD_USER && getenv("DBUS_SESSION_BUS_ADDRESS"));
+ (m->running_as == MANAGER_USER && getenv("DBUS_SESSION_BUS_ADDRESS"));
- /* Try to connect to the busses, if possible. */
+ /* Try to connect to the buses, if possible. */
return bus_init(m, try_bus_connect);
}
@@ -844,7 +874,8 @@ static unsigned manager_dispatch_gc_queue(Manager *m) {
if (u->gc_marker == gc_marker + GC_OFFSET_BAD ||
u->gc_marker == gc_marker + GC_OFFSET_UNSURE) {
- log_unit_debug(u->id, "Collecting %s", u->id);
+ if (u->id)
+ log_unit_debug(u, "Collecting.");
u->gc_marker = gc_marker + GC_OFFSET_BAD;
unit_add_to_cleanup_queue(u);
}
@@ -960,8 +991,8 @@ int manager_enumerate(Manager *m) {
for (c = 0; c < _UNIT_TYPE_MAX; c++) {
int q;
- if (unit_vtable[c]->supported && !unit_vtable[c]->supported(m)) {
- log_info("Unit type .%s is not supported on this system.", unit_type_to_string(c));
+ if (!unit_type_supported(c)) {
+ log_debug("Unit type .%s is not supported on this system.", unit_type_to_string(c));
continue;
}
@@ -977,28 +1008,25 @@ int manager_enumerate(Manager *m) {
return r;
}
-static int manager_coldplug(Manager *m) {
- int r = 0;
+static void manager_coldplug(Manager *m) {
Iterator i;
Unit *u;
char *k;
+ int r;
assert(m);
/* Then, let's set up their initial state. */
HASHMAP_FOREACH_KEY(u, k, m->units, i) {
- int q;
/* ignore aliases */
if (u->id != k)
continue;
- q = unit_coldplug(u);
- if (q < 0)
- r = q;
+ r = unit_coldplug(u);
+ if (r < 0)
+ log_warning_errno(r, "We couldn't coldplug %s, proceeding anyway: %m", u->id);
}
-
- return r;
}
static void manager_build_unit_path_cache(Manager *m) {
@@ -1142,9 +1170,7 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) {
bus_track_coldplug(m, &m->subscribed, &m->deserialized_subscribed);
/* Third, fire things up! */
- q = manager_coldplug(m);
- if (q < 0 && r == 0)
- r = q;
+ manager_coldplug(m);
if (serialization) {
assert(m->n_reloading > 0);
@@ -1174,11 +1200,9 @@ int manager_add_job(Manager *m, JobType type, Unit *unit, JobMode mode, bool ove
if (mode == JOB_ISOLATE && !unit->allow_isolate)
return sd_bus_error_setf(e, BUS_ERROR_NO_ISOLATION, "Operation refused, unit may not be isolated.");
- log_unit_debug(unit->id,
- "Trying to enqueue job %s/%s/%s", unit->id,
- job_type_to_string(type), job_mode_to_string(mode));
+ log_unit_debug(unit, "Trying to enqueue job %s/%s/%s", unit->id, job_type_to_string(type), job_mode_to_string(mode));
- job_type_collapse(&type, unit);
+ type = job_type_collapse(type, unit);
tr = transaction_new(mode == JOB_REPLACE_IRREVERSIBLY);
if (!tr)
@@ -1200,7 +1224,7 @@ int manager_add_job(Manager *m, JobType type, Unit *unit, JobMode mode, bool ove
if (r < 0)
goto tr_abort;
- log_unit_debug(unit->id,
+ log_unit_debug(unit,
"Enqueued job %s/%s as %u", unit->id,
job_type_to_string(type), (unsigned) tr->anchor_job->id);
@@ -1296,7 +1320,7 @@ int manager_load_unit_prepare(
t = unit_name_to_type(name);
- if (t == _UNIT_TYPE_INVALID || !unit_name_is_valid(name, TEMPLATE_INVALID))
+ if (t == _UNIT_TYPE_INVALID || !unit_name_is_valid(name, UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE))
return sd_bus_error_setf(e, SD_BUS_ERROR_INVALID_ARGS, "Unit name %s is not valid.", name);
ret = manager_get_unit(m, name);
@@ -1470,10 +1494,10 @@ static void manager_invoke_notify_message(Manager *m, Unit *u, pid_t pid, char *
return;
}
- log_unit_debug(u->id, "Got notification message for unit %s", u->id);
-
if (UNIT_VTABLE(u)->notify_message)
UNIT_VTABLE(u)->notify_message(u, pid, tags, fds);
+ else
+ log_unit_debug(u, "Got notification message for unit. Ignoring.");
}
static int manager_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata) {
@@ -1593,7 +1617,7 @@ static void invoke_sigchld_event(Manager *m, Unit *u, siginfo_t *si) {
assert(u);
assert(si);
- log_unit_debug(u->id, "Child "PID_FMT" belongs to %s", si->si_pid, u->id);
+ log_unit_debug(u, "Child "PID_FMT" belongs to %s", si->si_pid, u->id);
unit_unwatch_pid(u, si->si_pid);
UNIT_VTABLE(u)->sigchld_event(u, si->si_pid, si->si_code, si->si_status);
@@ -1665,11 +1689,11 @@ static int manager_start_target(Manager *m, const char *name, JobMode mode) {
_cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
int r;
- log_unit_debug(name, "Activating special unit %s", name);
+ log_debug("Activating special unit %s", name);
r = manager_add_job_by_name(m, JOB_START, name, mode, true, &error, NULL);
if (r < 0)
- log_unit_error(name, "Failed to enqueue %s job: %s", name, bus_error_message(&error, r));
+ log_error("Failed to enqueue %s job: %s", name, bus_error_message(&error, r));
return r;
}
@@ -1702,7 +1726,7 @@ static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t
}
log_received_signal(sfsi.ssi_signo == SIGCHLD ||
- (sfsi.ssi_signo == SIGTERM && m->running_as == SYSTEMD_USER)
+ (sfsi.ssi_signo == SIGTERM && m->running_as == MANAGER_USER)
? LOG_DEBUG : LOG_INFO,
&sfsi);
@@ -1713,7 +1737,7 @@ static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t
break;
case SIGTERM:
- if (m->running_as == SYSTEMD_SYSTEM) {
+ if (m->running_as == MANAGER_SYSTEM) {
/* This is for compatibility with the
* original sysvinit */
m->exit_code = MANAGER_REEXECUTE;
@@ -1723,7 +1747,7 @@ static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t
/* Fall through */
case SIGINT:
- if (m->running_as == SYSTEMD_SYSTEM) {
+ if (m->running_as == MANAGER_SYSTEM) {
/* If the user presses C-A-D more than
* 7 times within 2s, we reboot
@@ -1749,14 +1773,14 @@ static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t
break;
case SIGWINCH:
- if (m->running_as == SYSTEMD_SYSTEM)
+ if (m->running_as == MANAGER_SYSTEM)
manager_start_target(m, SPECIAL_KBREQUEST_TARGET, JOB_REPLACE);
/* This is a nop on non-init */
break;
case SIGPWR:
- if (m->running_as == SYSTEMD_SYSTEM)
+ if (m->running_as == MANAGER_SYSTEM)
manager_start_target(m, SPECIAL_SIGPWR_TARGET, JOB_REPLACE);
/* This is a nop on non-init */
@@ -1870,7 +1894,7 @@ static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t
break;
case 24:
- if (m->running_as == SYSTEMD_USER) {
+ if (m->running_as == MANAGER_USER) {
m->exit_code = MANAGER_EXIT;
return 0;
}
@@ -1988,7 +2012,7 @@ int manager_loop(Manager *m) {
while (m->exit_code == MANAGER_OK) {
usec_t wait_usec;
- if (m->runtime_watchdog > 0 && m->running_as == SYSTEMD_SYSTEM)
+ if (m->runtime_watchdog > 0 && m->running_as == MANAGER_SYSTEM)
watchdog_ping();
if (!ratelimit_test(&rl)) {
@@ -2014,7 +2038,7 @@ int manager_loop(Manager *m) {
continue;
/* Sleep for half the watchdog time */
- if (m->runtime_watchdog > 0 && m->running_as == SYSTEMD_SYSTEM) {
+ if (m->runtime_watchdog > 0 && m->running_as == MANAGER_SYSTEM) {
wait_usec = m->runtime_watchdog / 2;
if (wait_usec <= 0)
wait_usec = 1;
@@ -2083,7 +2107,7 @@ void manager_send_unit_audit(Manager *m, Unit *u, int type, bool success) {
#ifdef HAVE_AUDIT
_cleanup_free_ char *p = NULL;
const char *msg;
- int audit_fd;
+ int audit_fd, r;
audit_fd = get_audit_fd();
if (audit_fd < 0)
@@ -2094,15 +2118,15 @@ void manager_send_unit_audit(Manager *m, Unit *u, int type, bool success) {
if (m->n_reloading > 0)
return;
- if (m->running_as != SYSTEMD_SYSTEM)
+ if (m->running_as != MANAGER_SYSTEM)
return;
if (u->type != UNIT_SERVICE)
return;
- p = unit_name_to_prefix_and_instance(u->id);
- if (!p) {
- log_oom();
+ r = unit_name_to_prefix_and_instance(u->id, &p);
+ if (r < 0) {
+ log_error_errno(r, "Failed to extract prefix and instance of unit name: %m");
return;
}
@@ -2131,7 +2155,7 @@ void manager_send_unit_plymouth(Manager *m, Unit *u) {
if (m->n_reloading > 0)
return;
- if (m->running_as != SYSTEMD_SYSTEM)
+ if (m->running_as != MANAGER_SYSTEM)
return;
if (detect_container(NULL) > 0)
@@ -2193,7 +2217,7 @@ int manager_open_serialization(Manager *m, FILE **_f) {
assert(_f);
- path = m->running_as == SYSTEMD_SYSTEM ? "/run/systemd" : "/tmp";
+ path = m->running_as == MANAGER_SYSTEM ? "/run/systemd" : "/tmp";
fd = open_tmpfile(path, O_RDWR|O_CLOEXEC);
if (fd < 0)
return -errno;
@@ -2399,11 +2423,9 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) {
_cleanup_free_ char *uce = NULL;
char **e;
- uce = cunescape(l+4);
- if (!uce) {
- r = -ENOMEM;
+ r = cunescape(l + 4, UNESCAPE_RELAX, &uce);
+ if (r < 0)
goto finish;
- }
e = strv_env_set(m->environment, uce);
if (!e) {
@@ -2564,9 +2586,7 @@ int manager_reload(Manager *m) {
r = q;
/* Third, fire things up! */
- q = manager_coldplug(m);
- if (q < 0 && r >= 0)
- r = q;
+ manager_coldplug(m);
assert(m->n_reloading > 0);
m->n_reloading--;
@@ -2613,7 +2633,7 @@ static void manager_notify_finished(Manager *m) {
if (m->test_run)
return;
- if (m->running_as == SYSTEMD_SYSTEM && detect_container(NULL) <= 0) {
+ if (m->running_as == MANAGER_SYSTEM && detect_container(NULL) <= 0) {
/* Note that m->kernel_usec.monotonic is always at 0,
* and m->firmware_usec.monotonic and
@@ -2681,10 +2701,21 @@ void manager_check_finished(Manager *m) {
assert(m);
+ if (m->n_reloading > 0)
+ return;
+
+ /* Verify that we are actually running currently. Initially
+ * the exit code is set to invalid, and during operation it is
+ * then set to MANAGER_OK */
+ if (m->exit_code != MANAGER_OK)
+ return;
+
if (hashmap_size(m->jobs) > 0) {
if (m->jobs_in_progress_event_source)
- sd_event_source_set_time(m->jobs_in_progress_event_source, now(CLOCK_MONOTONIC) + JOBS_IN_PROGRESS_WAIT_USEC);
+ /* Ignore any failure, this is only for feedback */
+ (void) sd_event_source_set_time(m->jobs_in_progress_event_source,
+ now(CLOCK_MONOTONIC) + JOBS_IN_PROGRESS_WAIT_USEC);
return;
}
@@ -2727,7 +2758,7 @@ static int create_generator_dir(Manager *m, char **generator, const char *name)
if (*generator)
return 0;
- if (m->running_as == SYSTEMD_SYSTEM && getpid() == 1) {
+ if (m->running_as == MANAGER_SYSTEM && getpid() == 1) {
/* systemd --system, not running --test */
p = strappend("/run/systemd/", name);
@@ -2740,7 +2771,7 @@ static int create_generator_dir(Manager *m, char **generator, const char *name)
free(p);
return r;
}
- } else if (m->running_as == SYSTEMD_USER) {
+ } else if (m->running_as == MANAGER_USER) {
const char *s = NULL;
s = getenv("XDG_RUNTIME_DIR");
@@ -2791,7 +2822,7 @@ static void trim_generator_dir(Manager *m, char **generator) {
}
static int manager_run_generators(Manager *m) {
- _cleanup_free_ char **paths = NULL;
+ _cleanup_strv_free_ char **paths = NULL;
const char *argv[5];
char **path;
int r;
@@ -2853,7 +2884,7 @@ static void remove_generator_dir(Manager *m, char **generator) {
return;
strv_remove(m->lookup_paths.unit_path, *generator);
- rm_rf(*generator, false, true, false);
+ (void) rm_rf(*generator, REMOVE_ROOT);
free(*generator);
*generator = NULL;
@@ -2927,7 +2958,7 @@ void manager_recheck_journal(Manager *m) {
assert(m);
- if (m->running_as != SYSTEMD_SYSTEM)
+ if (m->running_as != MANAGER_SYSTEM)
return;
u = manager_get_unit(m, SPECIAL_JOURNALD_SOCKET);
@@ -2951,7 +2982,7 @@ void manager_set_show_status(Manager *m, ShowStatus mode) {
assert(m);
assert(IN_SET(mode, SHOW_STATUS_AUTO, SHOW_STATUS_NO, SHOW_STATUS_YES, SHOW_STATUS_TEMPORARY));
- if (m->running_as != SYSTEMD_SYSTEM)
+ if (m->running_as != MANAGER_SYSTEM)
return;
m->show_status = mode;
@@ -2965,7 +2996,7 @@ void manager_set_show_status(Manager *m, ShowStatus mode) {
static bool manager_get_show_status(Manager *m, StatusType type) {
assert(m);
- if (m->running_as != SYSTEMD_SYSTEM)
+ if (m->running_as != MANAGER_SYSTEM)
return false;
if (m->no_console_output)
@@ -2981,15 +3012,13 @@ static bool manager_get_show_status(Manager *m, StatusType type) {
if (m->show_status > 0)
return true;
- /* If Plymouth is running make sure we show the status, so
- * that there's something nice to see when people press Esc */
- return plymouth_running();
+ return false;
}
void manager_set_first_boot(Manager *m, bool b) {
assert(m);
- if (m->running_as != SYSTEMD_SYSTEM)
+ if (m->running_as != MANAGER_SYSTEM)
return;
m->first_boot = b;
@@ -3021,15 +3050,16 @@ void manager_status_printf(Manager *m, StatusType type, const char *status, cons
int manager_get_unit_by_path(Manager *m, const char *path, const char *suffix, Unit **_found) {
_cleanup_free_ char *p = NULL;
Unit *found;
+ int r;
assert(m);
assert(path);
assert(suffix);
assert(_found);
- p = unit_name_from_path(path, suffix);
- if (!p)
- return -ENOMEM;
+ r = unit_name_from_path(path, suffix, &p);
+ if (r < 0)
+ return r;
found = manager_get_unit(m, p);
if (!found) {
@@ -3056,11 +3086,29 @@ Set *manager_get_units_requiring_mounts_for(Manager *m, const char *path) {
const char *manager_get_runtime_prefix(Manager *m) {
assert(m);
- return m->running_as == SYSTEMD_SYSTEM ?
+ return m->running_as == MANAGER_SYSTEM ?
"/run" :
getenv("XDG_RUNTIME_DIR");
}
+void manager_update_failed_units(Manager *m, Unit *u, bool failed) {
+ unsigned size;
+
+ assert(m);
+ assert(u->manager == m);
+
+ size = set_size(m->failed_units);
+
+ if (failed) {
+ if (set_put(m->failed_units, u) < 0)
+ log_oom();
+ } else
+ set_remove(m->failed_units, u);
+
+ if (set_size(m->failed_units) != size)
+ bus_manager_send_change_signal(m);
+}
+
ManagerState manager_state(Manager *m) {
Unit *u;
diff --git a/src/core/manager.h b/src/core/manager.h
index d3971f1684..4ef869d14a 100644
--- a/src/core/manager.h
+++ b/src/core/manager.h
@@ -22,7 +22,6 @@
***/
#include <stdbool.h>
-#include <inttypes.h>
#include <stdio.h>
#include "sd-bus.h"
@@ -31,7 +30,6 @@
#include "cgroup-util.h"
#include "hashmap.h"
#include "list.h"
-#include "set.h"
#include "ratelimit.h"
/* Enforce upper limit how many names we allow */
@@ -70,14 +68,11 @@ typedef enum StatusType {
STATUS_TYPE_EMERGENCY,
} StatusType;
-#include "unit.h"
#include "job.h"
#include "path-lookup.h"
#include "execute.h"
#include "unit-name.h"
-#include "exit-status.h"
#include "show-status.h"
-#include "failure-action.h"
struct Manager {
/* Note that the set of units we know of is allowed to be
@@ -204,11 +199,9 @@ struct Manager {
sd_bus_track *subscribed;
char **deserialized_subscribed;
- sd_bus_message *queued_message; /* This is used during reloading:
- * before the reload we queue the
- * reply message here, and
- * afterwards we send it */
- sd_bus *queued_message_bus; /* The connection to send the queued message on */
+ /* This is used during reloading: before the reload we queue
+ * the reply message here, and afterwards we send it */
+ sd_bus_message *queued_message;
Hashmap *watch_bus; /* D-Bus names => Unit object n:1 */
@@ -233,7 +226,7 @@ struct Manager {
int pin_cgroupfs_fd;
/* Flags */
- SystemdRunningAs running_as;
+ ManagerRunningAs running_as;
ManagerExitCode exit_code:5;
bool dispatching_load_queue:1;
@@ -299,9 +292,12 @@ struct Manager {
/* When the user hits C-A-D more than 7 times per 2s, reboot immediately... */
RateLimit ctrl_alt_del_ratelimit;
+
+ const char *unit_log_field;
+ const char *unit_log_format_string;
};
-int manager_new(SystemdRunningAs running_as, bool test_run, Manager **m);
+int manager_new(ManagerRunningAs running_as, bool test_run, Manager **m);
Manager* manager_free(Manager *m);
int manager_enumerate(Manager *m);
@@ -367,5 +363,7 @@ const char *manager_get_runtime_prefix(Manager *m);
ManagerState manager_state(Manager *m);
+void manager_update_failed_units(Manager *m, Unit *u, bool failed);
+
const char *manager_state_to_string(ManagerState m) _const_;
ManagerState manager_state_from_string(const char *s) _pure_;
diff --git a/src/core/mount-setup.c b/src/core/mount-setup.c
index 521545e5ce..c35248eeae 100644
--- a/src/core/mount-setup.c
+++ b/src/core/mount-setup.c
@@ -21,10 +21,7 @@
#include <sys/mount.h>
#include <errno.h>
-#include <sys/stat.h>
#include <stdlib.h>
-#include <string.h>
-#include <assert.h>
#include <unistd.h>
#include <ftw.h>
@@ -42,7 +39,6 @@
#include "virt.h"
#include "efivars.h"
#include "smack-util.h"
-#include "def.h"
#include "cgroup-util.h"
typedef enum MountMode {
@@ -160,10 +156,9 @@ static int mount_one(const MountPoint *p, bool relabel) {
if (relabel)
label_fix(p->where, true, true);
- r = path_is_mount_point(p->where, true);
- if (r < 0)
+ r = path_is_mount_point(p->where, AT_SYMLINK_FOLLOW);
+ if (r < 0 && r != -ENOENT)
return r;
-
if (r > 0)
return 0;
@@ -378,7 +373,7 @@ int mount_setup(bool loaded_policy) {
/* Create a few default symlinks, which are normally created
* by udevd, but some scripts might need them before we start
* udevd. */
- dev_setup(NULL);
+ dev_setup(NULL, UID_INVALID, GID_INVALID);
/* Mark the root directory as shared in regards to mount
* propagation. The kernel defaults to "private", but we think
diff --git a/src/core/mount.c b/src/core/mount.c
index f3977e62de..ba1dcf1e85 100644
--- a/src/core/mount.c
+++ b/src/core/mount.c
@@ -21,7 +21,6 @@
#include <errno.h>
#include <stdio.h>
-#include <mntent.h>
#include <sys/epoll.h>
#include <signal.h>
#include <libmount.h>
@@ -30,8 +29,6 @@
#include "manager.h"
#include "unit.h"
#include "mount.h"
-#include "load-fragment.h"
-#include "load-dropin.h"
#include "log.h"
#include "sd-messages.h"
#include "strv.h"
@@ -41,10 +38,9 @@
#include "unit-name.h"
#include "dbus-mount.h"
#include "special.h"
-#include "bus-common-errors.h"
#include "exit-status.h"
-#include "def.h"
#include "fstab-util.h"
+#include "formats-util.h"
#define RETRY_UMOUNT_MAX 32
@@ -107,7 +103,9 @@ static bool mount_is_auto(const MountParameters *p) {
static bool needs_quota(const MountParameters *p) {
assert(p);
- if (mount_is_network(p))
+ /* Quotas are not enabled on network filesystems,
+ * but we want them, for example, on storage connected via iscsi */
+ if (p->fstype && fstype_is_network(p->fstype))
return false;
if (mount_is_bind(p))
@@ -137,8 +135,8 @@ static void mount_init(Unit *u) {
m->exec_context.std_error = u->manager->default_std_error;
}
- /* We need to make sure that /bin/mount is always called in
- * the same process group as us, so that the autofs kernel
+ /* We need to make sure that /usr/bin/mount is always called
+ * in the same process group as us, so that the autofs kernel
* side doesn't send us another mount request while we are
* already trying to comply its last one. */
m->exec_context.same_pgrp = true;
@@ -166,12 +164,18 @@ static int mount_arm_timer(Mount *m) {
return sd_event_source_set_enabled(m->timer_event_source, SD_EVENT_ONESHOT);
}
- return sd_event_add_time(
+ r = sd_event_add_time(
UNIT(m)->manager->event,
&m->timer_event_source,
CLOCK_MONOTONIC,
now(CLOCK_MONOTONIC) + m->timeout_usec, 0,
mount_dispatch_timer, m);
+ if (r < 0)
+ return r;
+
+ (void) sd_event_source_set_description(m->timer_event_source, "mount-timer");
+
+ return 0;
}
static void mount_unwatch_control_pid(Mount *m) {
@@ -313,10 +317,16 @@ static int mount_add_device_links(Mount *m) {
if (!is_device_path(p->what))
return 0;
+ /* /dev/root is a really weird thing, it's not a real device,
+ * but just a path the kernel exports for the root file system
+ * specified on the kernel command line. Ignore it here. */
+ if (path_equal(p->what, "/dev/root"))
+ return 0;
+
if (path_equal(m->where, "/"))
return 0;
- if (mount_is_auto(p) && UNIT(m)->manager->running_as == SYSTEMD_SYSTEM)
+ if (mount_is_auto(p) && UNIT(m)->manager->running_as == MANAGER_SYSTEM)
device_wants_mount = true;
r = unit_add_node_link(UNIT(m), p->what, device_wants_mount);
@@ -332,7 +342,7 @@ static int mount_add_quota_links(Mount *m) {
assert(m);
- if (UNIT(m)->manager->running_as != SYSTEMD_SYSTEM)
+ if (UNIT(m)->manager->running_as != MANAGER_SYSTEM)
return 0;
p = get_mount_parameters_fragment(m);
@@ -375,7 +385,7 @@ static int mount_add_default_dependencies(Mount *m) {
assert(m);
- if (UNIT(m)->manager->running_as != SYSTEMD_SYSTEM)
+ if (UNIT(m)->manager->running_as != MANAGER_SYSTEM)
return 0;
/* We do not add any default dependencies to / and /usr, since
@@ -431,7 +441,7 @@ static int mount_add_default_dependencies(Mount *m) {
static int mount_verify(Mount *m) {
_cleanup_free_ char *e = NULL;
- bool b;
+ int r;
assert(m);
@@ -441,28 +451,27 @@ static int mount_verify(Mount *m) {
if (!m->from_fragment && !m->from_proc_self_mountinfo)
return -ENOENT;
- e = unit_name_from_path(m->where, ".mount");
- if (!e)
- return -ENOMEM;
+ r = unit_name_from_path(m->where, ".mount", &e);
+ if (r < 0)
+ return log_unit_error_errno(UNIT(m), r, "Failed to generate unit name from mount path: %m");
- b = unit_has_name(UNIT(m), e);
- if (!b) {
- log_unit_error(UNIT(m)->id, "%s's Where= setting doesn't match unit name. Refusing.", UNIT(m)->id);
+ if (!unit_has_name(UNIT(m), e)) {
+ log_unit_error(UNIT(m), "Where= setting doesn't match unit name. Refusing.");
return -EINVAL;
}
if (mount_point_is_api(m->where) || mount_point_ignore(m->where)) {
- log_unit_error(UNIT(m)->id, "Cannot create mount unit for API file system %s. Refusing.", m->where);
+ log_unit_error(UNIT(m), "Cannot create mount unit for API file system %s. Refusing.", m->where);
return -EINVAL;
}
if (UNIT(m)->fragment_path && !m->parameters_fragment.what) {
- log_unit_error(UNIT(m)->id, "%s's What setting is missing. Refusing.", UNIT(m)->id);
+ log_unit_error(UNIT(m), "What= setting is missing. Refusing.");
return -EBADMSG;
}
if (m->exec_context.pam_name && m->kill_context.kill_mode != KILL_CONTROL_GROUP) {
- log_unit_error(UNIT(m)->id, "%s has PAM enabled. Kill mode must be set to control-group'. Refusing.",UNIT(m)->id);
+ log_unit_error(UNIT(m), "Unit has PAM enabled. Kill mode must be set to control-group'. Refusing.");
return -EINVAL;
}
@@ -479,9 +488,9 @@ static int mount_add_extras(Mount *m) {
m->from_fragment = true;
if (!m->where) {
- m->where = unit_name_to_path(u->id);
- if (!m->where)
- return -ENOMEM;
+ r = unit_name_to_path(u->id, &m->where);
+ if (r < 0)
+ return r;
}
path_kill_slashes(m->where);
@@ -550,7 +559,7 @@ static int mount_load(Unit *u) {
return mount_verify(m);
}
-static int mount_notify_automount(Mount *m, int status) {
+static int mount_notify_automount(Mount *m, MountState old_state, MountState state) {
Unit *p;
int r;
Iterator i;
@@ -559,7 +568,7 @@ static int mount_notify_automount(Mount *m, int status) {
SET_FOREACH(p, UNIT(m)->dependencies[UNIT_TRIGGERED_BY], i)
if (p->type == UNIT_AUTOMOUNT) {
- r = automount_send_ready(AUTOMOUNT(p), status);
+ r = automount_update_mount(AUTOMOUNT(p), old_state, state);
if (r < 0)
return r;
}
@@ -590,28 +599,10 @@ static void mount_set_state(Mount *m, MountState state) {
m->control_command_id = _MOUNT_EXEC_COMMAND_INVALID;
}
- if (state == MOUNT_MOUNTED ||
- state == MOUNT_REMOUNTING)
- mount_notify_automount(m, 0);
- else if (state == MOUNT_DEAD ||
- state == MOUNT_UNMOUNTING ||
- state == MOUNT_MOUNTING_SIGTERM ||
- state == MOUNT_MOUNTING_SIGKILL ||
- state == MOUNT_REMOUNTING_SIGTERM ||
- state == MOUNT_REMOUNTING_SIGKILL ||
- state == MOUNT_UNMOUNTING_SIGTERM ||
- state == MOUNT_UNMOUNTING_SIGKILL ||
- state == MOUNT_FAILED) {
- if (state != old_state)
- mount_notify_automount(m, -ENODEV);
- }
+ mount_notify_automount(m, old_state, state);
if (state != old_state)
- log_unit_debug(UNIT(m)->id,
- "%s changed %s -> %s",
- UNIT(m)->id,
- mount_state_to_string(old_state),
- mount_state_to_string(state));
+ log_unit_debug(UNIT(m), "Changed %s -> %s", mount_state_to_string(old_state), mount_state_to_string(state));
unit_notify(UNIT(m), state_translation_table[old_state], state_translation_table[state], m->reload_result == MOUNT_SUCCESS);
m->reload_result = MOUNT_SUCCESS;
@@ -705,13 +696,18 @@ static int mount_spawn(Mount *m, ExecCommand *c, pid_t *_pid) {
.apply_permissions = true,
.apply_chroot = true,
.apply_tty_stdin = true,
+ .bus_endpoint_fd = -1,
};
assert(m);
assert(c);
assert(_pid);
- unit_realize_cgroup(UNIT(m));
+ (void) unit_realize_cgroup(UNIT(m));
+ if (m->reset_cpu_usage) {
+ (void) unit_reset_cpu_usage(UNIT(m));
+ m->reset_cpu_usage = false;
+ }
r = unit_setup_exec_runtime(UNIT(m));
if (r < 0)
@@ -727,9 +723,9 @@ static int mount_spawn(Mount *m, ExecCommand *c, pid_t *_pid) {
exec_params.cgroup_path = UNIT(m)->cgroup_path;
exec_params.cgroup_delegate = m->cgroup_context.delegate;
exec_params.runtime_prefix = manager_get_runtime_prefix(UNIT(m)->manager);
- exec_params.unit_id = UNIT(m)->id;
- r = exec_spawn(c,
+ r = exec_spawn(UNIT(m),
+ c,
&m->exec_context,
&exec_params,
m->exec_runtime,
@@ -814,8 +810,7 @@ static void mount_enter_signal(Mount *m, MountState state, MountResult f) {
return;
fail:
- log_unit_warning(UNIT(m)->id,
- "%s failed to kill processes: %s", UNIT(m)->id, strerror(-r));
+ log_unit_warning_errno(UNIT(m), r, "Failed to kill processes: %m");
if (state == MOUNT_REMOUNTING_SIGTERM || state == MOUNT_REMOUNTING_SIGKILL)
mount_enter_mounted(m, MOUNT_FAILURE_RESOURCES);
@@ -823,46 +818,6 @@ fail:
mount_enter_dead(m, MOUNT_FAILURE_RESOURCES);
}
-void warn_if_dir_nonempty(const char *unit, const char* where) {
- int r;
-
- assert(unit);
- assert(where);
-
- r = dir_is_empty(where);
- if (r > 0)
- return;
- else if (r == 0)
- log_unit_struct(unit,
- LOG_NOTICE,
- LOG_MESSAGE_ID(SD_MESSAGE_OVERMOUNTING),
- LOG_MESSAGE("%s: Directory %s to mount over is not empty, mounting anyway.",
- unit, where),
- "WHERE=%s", where,
- NULL);
- else
- log_unit_warning(unit,
- "MESSAGE=Failed to check directory %s: %s",
- where, strerror(-r));
-}
-
-static int fail_if_symlink(const char *unit, const char* where) {
- assert(where);
-
- if (is_symlink(where) > 0) {
- log_unit_struct(unit,
- LOG_ERR,
- LOG_MESSAGE_ID(SD_MESSAGE_OVERMOUNTING),
- LOG_MESSAGE("%s: Mount on symlink %s not allowed.",
- unit, where),
- "WHERE=%s", where,
- NULL);
-
- return -ELOOP;
- }
- return 0;
-}
-
static void mount_enter_unmounting(Mount *m) {
int r;
@@ -878,8 +833,8 @@ static void mount_enter_unmounting(Mount *m) {
m->control_command_id = MOUNT_EXEC_UNMOUNT;
m->control_command = m->exec_command + MOUNT_EXEC_UNMOUNT;
- r = exec_command_set(m->control_command, "/bin/umount", m->where, NULL);
- if (r >= 0 && UNIT(m)->manager->running_as == SYSTEMD_SYSTEM)
+ r = exec_command_set(m->control_command, UMOUNT_PATH, m->where, NULL);
+ if (r >= 0 && UNIT(m)->manager->running_as == MANAGER_SYSTEM)
r = exec_command_append(m->control_command, "-n", NULL);
if (r < 0)
goto fail;
@@ -895,9 +850,7 @@ static void mount_enter_unmounting(Mount *m) {
return;
fail:
- log_unit_warning(UNIT(m)->id,
- "%s failed to run 'umount' task: %s",
- UNIT(m)->id, strerror(-r));
+ log_unit_warning_errno(UNIT(m), r, "Failed to run 'umount' task: %m");
mount_enter_mounted(m, MOUNT_FAILURE_RESOURCES);
}
@@ -910,18 +863,18 @@ static void mount_enter_mounting(Mount *m) {
m->control_command_id = MOUNT_EXEC_MOUNT;
m->control_command = m->exec_command + MOUNT_EXEC_MOUNT;
- mkdir_p_label(m->where, m->directory_mode);
+ r = unit_fail_if_symlink(UNIT(m), m->where);
+ if (r < 0)
+ goto fail;
- warn_if_dir_nonempty(m->meta.id, m->where);
+ (void) mkdir_p_label(m->where, m->directory_mode);
+
+ unit_warn_if_dir_nonempty(UNIT(m), m->where);
/* Create the source directory for bind-mounts if needed */
p = get_mount_parameters_fragment(m);
if (p && mount_is_bind(p))
- mkdir_p_label(p->what, m->directory_mode);
-
- r = fail_if_symlink(m->meta.id, m->where);
- if (r < 0)
- goto fail;
+ (void) mkdir_p_label(p->what, m->directory_mode);
if (m->from_fragment) {
_cleanup_free_ char *opts = NULL;
@@ -931,9 +884,9 @@ static void mount_enter_mounting(Mount *m) {
if (r < 0)
goto fail;
- r = exec_command_set(m->control_command, "/bin/mount",
+ r = exec_command_set(m->control_command, MOUNT_PATH,
m->parameters_fragment.what, m->where, NULL);
- if (r >= 0 && UNIT(m)->manager->running_as == SYSTEMD_SYSTEM)
+ if (r >= 0 && UNIT(m)->manager->running_as == MANAGER_SYSTEM)
r = exec_command_append(m->control_command, "-n", NULL);
if (r >= 0 && m->sloppy_options)
r = exec_command_append(m->control_command, "-s", NULL);
@@ -958,9 +911,7 @@ static void mount_enter_mounting(Mount *m) {
return;
fail:
- log_unit_warning(UNIT(m)->id,
- "%s failed to run 'mount' task: %s",
- UNIT(m)->id, strerror(-r));
+ log_unit_warning_errno(UNIT(m), r, "Failed to run 'mount' task: %m");
mount_enter_dead(m, MOUNT_FAILURE_RESOURCES);
}
@@ -980,10 +931,10 @@ static void mount_enter_remounting(Mount *m) {
else
o = "remount";
- r = exec_command_set(m->control_command, "/bin/mount",
+ r = exec_command_set(m->control_command, MOUNT_PATH,
m->parameters_fragment.what, m->where,
"-o", o, NULL);
- if (r >= 0 && UNIT(m)->manager->running_as == SYSTEMD_SYSTEM)
+ if (r >= 0 && UNIT(m)->manager->running_as == MANAGER_SYSTEM)
r = exec_command_append(m->control_command, "-n", NULL);
if (r >= 0 && m->sloppy_options)
r = exec_command_append(m->control_command, "-s", NULL);
@@ -1006,9 +957,7 @@ static void mount_enter_remounting(Mount *m) {
return;
fail:
- log_unit_warning(UNIT(m)->id,
- "%s failed to run 'remount' task: %s",
- UNIT(m)->id, strerror(-r));
+ log_unit_warning_errno(UNIT(m), r, "Failed to run 'remount' task: %m");
m->reload_result = MOUNT_FAILURE_RESOURCES;
mount_enter_mounted(m, MOUNT_SUCCESS);
}
@@ -1035,6 +984,7 @@ static int mount_start(Unit *u) {
m->result = MOUNT_SUCCESS;
m->reload_result = MOUNT_SUCCESS;
+ m->reset_cpu_usage = true;
mount_enter_mounting(m);
return 1;
@@ -1110,7 +1060,7 @@ static int mount_deserialize_item(Unit *u, const char *key, const char *value, F
MountState state;
if ((state = mount_state_from_string(value)) < 0)
- log_unit_debug(u->id, "Failed to parse state value %s", value);
+ log_unit_debug(u, "Failed to parse state value: %s", value);
else
m->deserialized_state = state;
} else if (streq(key, "result")) {
@@ -1118,8 +1068,7 @@ static int mount_deserialize_item(Unit *u, const char *key, const char *value, F
f = mount_result_from_string(value);
if (f < 0)
- log_unit_debug(UNIT(m)->id,
- "Failed to parse result value %s", value);
+ log_unit_debug(u, "Failed to parse result value: %s", value);
else if (f != MOUNT_SUCCESS)
m->result = f;
@@ -1128,8 +1077,7 @@ static int mount_deserialize_item(Unit *u, const char *key, const char *value, F
f = mount_result_from_string(value);
if (f < 0)
- log_unit_debug(UNIT(m)->id,
- "Failed to parse reload result value %s", value);
+ log_unit_debug(u, "Failed to parse reload result value: %s", value);
else if (f != MOUNT_SUCCESS)
m->reload_result = f;
@@ -1137,23 +1085,21 @@ static int mount_deserialize_item(Unit *u, const char *key, const char *value, F
pid_t pid;
if (parse_pid(value, &pid) < 0)
- log_unit_debug(UNIT(m)->id,
- "Failed to parse control-pid value %s", value);
+ log_unit_debug(u, "Failed to parse control-pid value: %s", value);
else
m->control_pid = pid;
} else if (streq(key, "control-command")) {
MountExecCommand id;
- if ((id = mount_exec_command_from_string(value)) < 0)
- log_unit_debug(UNIT(m)->id,
- "Failed to parse exec-command value %s", value);
+ id = mount_exec_command_from_string(value);
+ if (id < 0)
+ log_unit_debug(u, "Failed to parse exec-command value: %s", value);
else {
m->control_command_id = id;
m->control_command = m->exec_command + id;
}
} else
- log_unit_debug(UNIT(m)->id,
- "Unknown serialization key '%s'", key);
+ log_unit_debug(u, "Unknown serialization key: %s", key);
return 0;
}
@@ -1211,10 +1157,8 @@ static void mount_sigchld_event(Unit *u, pid_t pid, int code, int status) {
m->control_command_id = _MOUNT_EXEC_COMMAND_INVALID;
}
- log_unit_full(u->id,
- f == MOUNT_SUCCESS ? LOG_DEBUG : LOG_NOTICE,
- "%s mount process exited, code=%s status=%i",
- u->id, sigchld_code_to_string(code), status);
+ log_unit_full(u, f == MOUNT_SUCCESS ? LOG_DEBUG : LOG_NOTICE, 0,
+ "Mount process exited, code=%s status=%i", sigchld_code_to_string(code), status);
/* Note that mount(8) returning and the kernel sending us a
* mount table change event might happen out-of-order. If an
@@ -1268,11 +1212,11 @@ static void mount_sigchld_event(Unit *u, pid_t pid, int code, int status) {
* the mount command. */
if (m->n_retry_umount < RETRY_UMOUNT_MAX) {
- log_unit_debug(u->id, "%s: mount still present, trying again.", u->id);
+ log_unit_debug(u, "Mount still present, trying again.");
m->n_retry_umount++;
mount_enter_unmounting(m);
} else {
- log_unit_debug(u->id, "%s: mount still present after %u attempts to unmount, giving up.", u->id, m->n_retry_umount);
+ log_unit_debug(u, "Mount still present after %u attempts to unmount, giving up.", m->n_retry_umount);
mount_enter_mounted(m, f);
}
} else
@@ -1302,33 +1246,27 @@ static int mount_dispatch_timer(sd_event_source *source, usec_t usec, void *user
case MOUNT_MOUNTING:
case MOUNT_MOUNTING_DONE:
- log_unit_warning(UNIT(m)->id,
- "%s mounting timed out. Stopping.", UNIT(m)->id);
+ log_unit_warning(UNIT(m), "Mounting timed out. Stopping.");
mount_enter_signal(m, MOUNT_MOUNTING_SIGTERM, MOUNT_FAILURE_TIMEOUT);
break;
case MOUNT_REMOUNTING:
- log_unit_warning(UNIT(m)->id,
- "%s remounting timed out. Stopping.", UNIT(m)->id);
+ log_unit_warning(UNIT(m), "Remounting timed out. Stopping.");
m->reload_result = MOUNT_FAILURE_TIMEOUT;
mount_enter_mounted(m, MOUNT_SUCCESS);
break;
case MOUNT_UNMOUNTING:
- log_unit_warning(UNIT(m)->id,
- "%s unmounting timed out. Stopping.", UNIT(m)->id);
+ log_unit_warning(UNIT(m), "Unmounting timed out. Stopping.");
mount_enter_signal(m, MOUNT_UNMOUNTING_SIGTERM, MOUNT_FAILURE_TIMEOUT);
break;
case MOUNT_MOUNTING_SIGTERM:
if (m->kill_context.send_sigkill) {
- log_unit_warning(UNIT(m)->id,
- "%s mounting timed out. Killing.", UNIT(m)->id);
+ log_unit_warning(UNIT(m), "Mounting timed out. Killing.");
mount_enter_signal(m, MOUNT_MOUNTING_SIGKILL, MOUNT_FAILURE_TIMEOUT);
} else {
- log_unit_warning(UNIT(m)->id,
- "%s mounting timed out. Skipping SIGKILL. Ignoring.",
- UNIT(m)->id);
+ log_unit_warning(UNIT(m), "Mounting timed out. Skipping SIGKILL. Ignoring.");
if (m->from_proc_self_mountinfo)
mount_enter_mounted(m, MOUNT_FAILURE_TIMEOUT);
@@ -1339,13 +1277,10 @@ static int mount_dispatch_timer(sd_event_source *source, usec_t usec, void *user
case MOUNT_REMOUNTING_SIGTERM:
if (m->kill_context.send_sigkill) {
- log_unit_warning(UNIT(m)->id,
- "%s remounting timed out. Killing.", UNIT(m)->id);
+ log_unit_warning(UNIT(m), "Remounting timed out. Killing.");
mount_enter_signal(m, MOUNT_REMOUNTING_SIGKILL, MOUNT_FAILURE_TIMEOUT);
} else {
- log_unit_warning(UNIT(m)->id,
- "%s remounting timed out. Skipping SIGKILL. Ignoring.",
- UNIT(m)->id);
+ log_unit_warning(UNIT(m), "Remounting timed out. Skipping SIGKILL. Ignoring.");
if (m->from_proc_self_mountinfo)
mount_enter_mounted(m, MOUNT_FAILURE_TIMEOUT);
@@ -1356,13 +1291,10 @@ static int mount_dispatch_timer(sd_event_source *source, usec_t usec, void *user
case MOUNT_UNMOUNTING_SIGTERM:
if (m->kill_context.send_sigkill) {
- log_unit_warning(UNIT(m)->id,
- "%s unmounting timed out. Killing.", UNIT(m)->id);
+ log_unit_warning(UNIT(m), "Unmounting timed out. Killing.");
mount_enter_signal(m, MOUNT_UNMOUNTING_SIGKILL, MOUNT_FAILURE_TIMEOUT);
} else {
- log_unit_warning(UNIT(m)->id,
- "%s unmounting timed out. Skipping SIGKILL. Ignoring.",
- UNIT(m)->id);
+ log_unit_warning(UNIT(m), "Unmounting timed out. Skipping SIGKILL. Ignoring.");
if (m->from_proc_self_mountinfo)
mount_enter_mounted(m, MOUNT_FAILURE_TIMEOUT);
@@ -1374,9 +1306,7 @@ static int mount_dispatch_timer(sd_event_source *source, usec_t usec, void *user
case MOUNT_MOUNTING_SIGKILL:
case MOUNT_REMOUNTING_SIGKILL:
case MOUNT_UNMOUNTING_SIGKILL:
- log_unit_warning(UNIT(m)->id,
- "%s mount process still around after SIGKILL. Ignoring.",
- UNIT(m)->id);
+ log_unit_warning(UNIT(m),"Mount process still around after SIGKILL. Ignoring.");
if (m->from_proc_self_mountinfo)
mount_enter_mounted(m, MOUNT_FAILURE_TIMEOUT);
@@ -1391,7 +1321,7 @@ static int mount_dispatch_timer(sd_event_source *source, usec_t usec, void *user
return 0;
}
-static int mount_add_one(
+static int mount_setup_unit(
Manager *m,
const char *what,
const char *where,
@@ -1424,9 +1354,9 @@ static int mount_add_one(
if (!is_path(where))
return 0;
- e = unit_name_from_path(where, ".mount");
- if (!e)
- return -ENOMEM;
+ r = unit_name_from_path(where, ".mount", &e);
+ if (r < 0)
+ return r;
u = manager_get_unit(m, e);
if (!u) {
@@ -1434,7 +1364,7 @@ static int mount_add_one(
u = unit_new(m, sizeof(Mount));
if (!u)
- return -ENOMEM;
+ return log_oom();
r = unit_add_name(u, e);
if (r < 0)
@@ -1452,7 +1382,7 @@ static int mount_add_one(
goto fail;
}
- if (m->running_as == SYSTEMD_SYSTEM) {
+ if (m->running_as == MANAGER_SYSTEM) {
const char* target;
target = mount_needs_network(options, fstype) ? SPECIAL_REMOTE_FS_TARGET : SPECIAL_LOCAL_FS_TARGET;
@@ -1480,7 +1410,7 @@ static int mount_add_one(
}
}
- if (m->running_as == SYSTEMD_SYSTEM &&
+ if (m->running_as == MANAGER_SYSTEM &&
mount_needs_network(options, fstype)) {
/* _netdev option may have shown up late, or on a
* remount. Add remote-fs dependencies, even though
@@ -1547,6 +1477,8 @@ static int mount_add_one(
return 0;
fail:
+ log_warning_errno(r, "Failed to set up mount unit: %m");
+
if (delete && u)
unit_free(u);
@@ -1554,45 +1486,51 @@ fail:
}
static int mount_load_proc_self_mountinfo(Manager *m, bool set_flags) {
- _cleanup_(mnt_free_tablep) struct libmnt_table *tb = NULL;
- _cleanup_(mnt_free_iterp) struct libmnt_iter *itr = NULL;
- struct libmnt_fs *fs;
+ _cleanup_(mnt_free_tablep) struct libmnt_table *t = NULL;
+ _cleanup_(mnt_free_iterp) struct libmnt_iter *i = NULL;
int r = 0;
assert(m);
- tb = mnt_new_table();
- itr = mnt_new_iter(MNT_ITER_FORWARD);
- if (!tb || !itr)
+ t = mnt_new_table();
+ if (!t)
return log_oom();
- r = mnt_table_parse_mtab(tb, NULL);
+ i = mnt_new_iter(MNT_ITER_FORWARD);
+ if (!i)
+ return log_oom();
+
+ r = mnt_table_parse_mtab(t, NULL);
if (r < 0)
- return r;
+ return log_error_errno(r, "Failed to parse /proc/self/mountinfo: %m");
r = 0;
for (;;) {
const char *device, *path, *options, *fstype;
- _cleanup_free_ const char *d = NULL, *p = NULL;
+ _cleanup_free_ char *d = NULL, *p = NULL;
+ struct libmnt_fs *fs;
int k;
- k = mnt_table_next_fs(tb, itr, &fs);
+ k = mnt_table_next_fs(t, i, &fs);
if (k == 1)
break;
- else if (k < 0)
- return log_error_errno(k, "Failed to get next entry from /etc/fstab: %m");
+ if (k < 0)
+ return log_error_errno(k, "Failed to get next entry from /proc/self/mountinfo: %m");
device = mnt_fs_get_source(fs);
path = mnt_fs_get_target(fs);
options = mnt_fs_get_options(fs);
fstype = mnt_fs_get_fstype(fs);
- d = cunescape(device);
- p = cunescape(path);
- if (!d || !p)
+ if (cunescape(device, UNESCAPE_RELAX, &d) < 0)
return log_oom();
- k = mount_add_one(m, d, p, options, fstype, set_flags);
+ if (cunescape(path, UNESCAPE_RELAX, &p) < 0)
+ return log_oom();
+
+ (void) device_found_node(m, d, true, DEVICE_FOUND_MOUNT, set_flags);
+
+ k = mount_setup_unit(m, d, p, options, fstype, set_flags);
if (r == 0 && k < 0)
r = k;
}
@@ -1644,10 +1582,12 @@ static int mount_enumerate(Manager *m) {
/* Dispatch this before we dispatch SIGCHLD, so that
* we always get the events from /proc/self/mountinfo
- * before the SIGCHLD of /bin/mount. */
+ * before the SIGCHLD of /usr/bin/mount. */
r = sd_event_source_set_priority(m->mount_event_source, -10);
if (r < 0)
goto fail;
+
+ (void) sd_event_source_set_description(m->mount_event_source, "mount-mountinfo-dispatch");
}
if (m->utab_inotify_fd < 0) {
@@ -1672,6 +1612,8 @@ static int mount_enumerate(Manager *m) {
r = sd_event_source_set_priority(m->mount_utab_event_source, -10);
if (r < 0)
goto fail;
+
+ (void) sd_event_source_set_description(m->mount_utab_event_source, "mount-utab-dispatch");
}
r = mount_load_proc_self_mountinfo(m, false);
@@ -1686,7 +1628,10 @@ fail:
}
static int mount_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata) {
+ _cleanup_set_free_ Set *around = NULL, *gone = NULL;
Manager *m = userdata;
+ const char *what;
+ Iterator i;
Unit *u;
int r;
@@ -1736,8 +1681,6 @@ static int mount_dispatch_io(sd_event_source *source, int fd, uint32_t revents,
r = mount_load_proc_self_mountinfo(m, true);
if (r < 0) {
- log_error_errno(r, "Failed to reread /proc/self/mountinfo: %m");
-
/* Reset flags, just in case, for later calls */
LIST_FOREACH(units_by_type, u, m->units_by_type[UNIT_MOUNT]) {
Mount *mount = MOUNT(u);
@@ -1755,6 +1698,19 @@ static int mount_dispatch_io(sd_event_source *source, int fd, uint32_t revents,
if (!mount->is_mounted) {
+ /* A mount point is not around right now. It
+ * might be gone, or might never have
+ * existed. */
+
+ if (mount->from_proc_self_mountinfo &&
+ mount->parameters_proc_self_mountinfo.what) {
+
+ /* Remember that this device might just have disappeared */
+ if (set_ensure_allocated(&gone, &string_hash_ops) < 0 ||
+ set_put(gone, mount->parameters_proc_self_mountinfo.what) < 0)
+ log_oom(); /* we don't care too much about OOM here... */
+ }
+
mount->from_proc_self_mountinfo = false;
switch (mount->state) {
@@ -1772,7 +1728,7 @@ static int mount_dispatch_io(sd_event_source *source, int fd, uint32_t revents,
} else if (mount->just_mounted || mount->just_changed) {
- /* New or changed mount entry */
+ /* A mount point was added or changed */
switch (mount->state) {
@@ -1799,10 +1755,27 @@ static int mount_dispatch_io(sd_event_source *source, int fd, uint32_t revents,
}
}
+ if (mount->is_mounted &&
+ mount->from_proc_self_mountinfo &&
+ mount->parameters_proc_self_mountinfo.what) {
+
+ if (set_ensure_allocated(&around, &string_hash_ops) < 0 ||
+ set_put(around, mount->parameters_proc_self_mountinfo.what) < 0)
+ log_oom();
+ }
+
/* Reset the flags for later calls */
mount->is_mounted = mount->just_mounted = mount->just_changed = false;
}
+ SET_FOREACH(what, gone, i) {
+ if (set_contains(around, what))
+ continue;
+
+ /* Let the device units know that the device is no longer mounted */
+ (void) device_found_node(m, what, false, DEVICE_FOUND_MOUNT, true);
+ }
+
return 0;
}
diff --git a/src/core/mount.h b/src/core/mount.h
index d6987e6fa1..280ea0d638 100644
--- a/src/core/mount.h
+++ b/src/core/mount.h
@@ -23,15 +23,13 @@
typedef struct Mount Mount;
-#include "unit.h"
#include "kill.h"
#include "execute.h"
-#include "cgroup.h"
typedef enum MountState {
MOUNT_DEAD,
- MOUNT_MOUNTING, /* /bin/mount is running, but the mount is not done yet. */
- MOUNT_MOUNTING_DONE, /* /bin/mount is running, and the mount is done. */
+ MOUNT_MOUNTING, /* /usr/bin/mount is running, but the mount is not done yet. */
+ MOUNT_MOUNTING_DONE, /* /usr/bin/mount is running, and the mount is done. */
MOUNT_MOUNTED,
MOUNT_REMOUNTING,
MOUNT_UNMOUNTING,
@@ -88,6 +86,8 @@ struct Mount {
bool just_mounted:1;
bool just_changed:1;
+ bool reset_cpu_usage:1;
+
bool sloppy_options;
MountResult result;
@@ -128,5 +128,3 @@ MountExecCommand mount_exec_command_from_string(const char *s) _pure_;
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 4fecd32363..01a817bf23 100644
--- a/src/core/namespace.c
+++ b/src/core/namespace.c
@@ -25,25 +25,18 @@
#include <stdio.h>
#include <unistd.h>
#include <sys/stat.h>
-#include <sys/types.h>
#include <sched.h>
-#include <sys/syscall.h>
-#include <limits.h>
#include <linux/fs.h>
-#include <sys/file.h>
#include "strv.h"
#include "util.h"
#include "path-util.h"
#include "missing.h"
-#include "execute.h"
#include "loopback-setup.h"
-#include "mkdir.h"
#include "dev-setup.h"
-#include "def.h"
-#include "label.h"
#include "selinux-util.h"
#include "namespace.h"
+#include "mkdir.h"
typedef enum MountMode {
/* This is ordered by priority! */
@@ -91,9 +84,11 @@ static int append_mounts(BindMount **p, char **strv, MountMode mode) {
static int mount_path_compare(const void *a, const void *b) {
const BindMount *p = a, *q = b;
+ int d;
- if (path_equal(p->path, q->path)) {
+ d = path_compare(p->path, q->path);
+ if (d == 0) {
/* If the paths are equal, check the mode */
if (p->mode < q->mode)
return -1;
@@ -105,13 +100,7 @@ static int mount_path_compare(const void *a, const void *b) {
}
/* If the paths are not equal, then order prefixes first */
- if (path_startswith(p->path, q->path))
- return 1;
-
- if (path_startswith(q->path, p->path))
- return -1;
-
- return 0;
+ return d;
}
static void drop_duplicates(BindMount *m, unsigned *n) {
@@ -158,24 +147,27 @@ static int mount_dev(BindMount *m) {
return -errno;
dev = strjoina(temporary_mount, "/dev");
- (void)mkdir(dev, 0755);
+ (void) mkdir(dev, 0755);
if (mount("tmpfs", dev, "tmpfs", MS_NOSUID|MS_STRICTATIME, "mode=755") < 0) {
r = -errno;
goto fail;
}
devpts = strjoina(temporary_mount, "/dev/pts");
- (void)mkdir(devpts, 0755);
+ (void) mkdir(devpts, 0755);
if (mount("/dev/pts", devpts, NULL, MS_BIND, NULL) < 0) {
r = -errno;
goto fail;
}
devptmx = strjoina(temporary_mount, "/dev/ptmx");
- symlink("pts/ptmx", devptmx);
+ if (symlink("pts/ptmx", devptmx) < 0) {
+ r = -errno;
+ goto fail;
+ }
devshm = strjoina(temporary_mount, "/dev/shm");
- (void)mkdir(devshm, 01777);
+ (void) mkdir(devshm, 01777);
r = mount("/dev/shm", devshm, NULL, MS_BIND, NULL);
if (r < 0) {
r = -errno;
@@ -183,15 +175,15 @@ static int mount_dev(BindMount *m) {
}
devmqueue = strjoina(temporary_mount, "/dev/mqueue");
- (void)mkdir(devmqueue, 0755);
- mount("/dev/mqueue", devmqueue, NULL, MS_BIND, NULL);
+ (void) mkdir(devmqueue, 0755);
+ (void) mount("/dev/mqueue", devmqueue, NULL, MS_BIND, NULL);
devhugepages = strjoina(temporary_mount, "/dev/hugepages");
- (void)mkdir(devhugepages, 0755);
- mount("/dev/hugepages", devhugepages, NULL, MS_BIND, NULL);
+ (void) mkdir(devhugepages, 0755);
+ (void) mount("/dev/hugepages", devhugepages, NULL, MS_BIND, NULL);
devlog = strjoina(temporary_mount, "/dev/log");
- symlink("/run/systemd/journal/dev-log", devlog);
+ (void) symlink("/run/systemd/journal/dev-log", devlog);
NULSTR_FOREACH(d, devnodes) {
_cleanup_free_ char *dn = NULL;
@@ -232,9 +224,15 @@ static int mount_dev(BindMount *m) {
}
}
- dev_setup(temporary_mount);
+ dev_setup(temporary_mount, UID_INVALID, GID_INVALID);
+
+ /* Create the /dev directory if missing. It is more likely to be
+ * missing when the service is started with RootDirectory. This is
+ * consistent with mount units creating the mount points when missing.
+ */
+ (void) mkdir_p_label(m->path, 0755);
- if (mount(dev, "/dev/", NULL, MS_MOVE, NULL) < 0) {
+ if (mount(dev, m->path, NULL, MS_MOVE, NULL) < 0) {
r = -errno;
goto fail;
}
@@ -281,7 +279,7 @@ static int mount_kdbus(BindMount *m) {
return log_error_errno(errno, "Failed create temp dir: %m");
root = strjoina(temporary_mount, "/kdbus");
- (void)mkdir(root, 0755);
+ (void) mkdir(root, 0755);
if (mount("tmpfs", root, "tmpfs", MS_NOSUID|MS_STRICTATIME, "mode=777") < 0) {
r = -errno;
goto fail;
@@ -302,7 +300,7 @@ static int mount_kdbus(BindMount *m) {
goto fail;
}
- r = mount(m->path, busnode, "bind", MS_BIND, NULL);
+ r = mount(m->path, busnode, NULL, MS_BIND, NULL);
if (r < 0) {
log_error_errno(errno, "bind mount of %s failed: %m", m->path);
r = -errno;
@@ -413,6 +411,7 @@ static int make_read_only(BindMount *m) {
}
int setup_namespace(
+ const char* root_directory,
char** read_write_dirs,
char** read_only_dirs,
char** inaccessible_dirs,
@@ -458,37 +457,56 @@ int setup_namespace(
return r;
if (tmp_dir) {
- m->path = "/tmp";
+ m->path = prefix_roota(root_directory, "/tmp");
m->mode = PRIVATE_TMP;
m++;
}
if (var_tmp_dir) {
- m->path = "/var/tmp";
+ m->path = prefix_roota(root_directory, "/var/tmp");
m->mode = PRIVATE_VAR_TMP;
m++;
}
if (private_dev) {
- m->path = "/dev";
+ m->path = prefix_roota(root_directory, "/dev");
m->mode = PRIVATE_DEV;
m++;
}
if (bus_endpoint_path) {
- m->path = bus_endpoint_path;
+ m->path = prefix_roota(root_directory, bus_endpoint_path);
m->mode = PRIVATE_BUS_ENDPOINT;
m++;
}
if (protect_home != PROTECT_HOME_NO) {
- r = append_mounts(&m, STRV_MAKE("-/home", "-/run/user", "-/root"), protect_home == PROTECT_HOME_READ_ONLY ? READONLY : INACCESSIBLE);
+ const char *home_dir, *run_user_dir, *root_dir;
+
+ home_dir = prefix_roota(root_directory, "/home");
+ home_dir = strjoina("-", home_dir);
+ run_user_dir = prefix_roota(root_directory, "/run/user");
+ run_user_dir = strjoina("-", run_user_dir);
+ root_dir = prefix_roota(root_directory, "/root");
+ root_dir = strjoina("-", root_dir);
+
+ r = append_mounts(&m, STRV_MAKE(home_dir, run_user_dir, root_dir),
+ protect_home == PROTECT_HOME_READ_ONLY ? READONLY : INACCESSIBLE);
if (r < 0)
return r;
}
if (protect_system != PROTECT_SYSTEM_NO) {
- r = append_mounts(&m, protect_system == PROTECT_SYSTEM_FULL ? STRV_MAKE("/usr", "-/boot", "/etc") : STRV_MAKE("/usr", "-/boot"), READONLY);
+ const char *usr_dir, *boot_dir, *etc_dir;
+
+ usr_dir = prefix_roota(root_directory, "/usr");
+ boot_dir = prefix_roota(root_directory, "/boot");
+ boot_dir = strjoina("-", boot_dir);
+ etc_dir = prefix_roota(root_directory, "/etc");
+
+ r = append_mounts(&m, protect_system == PROTECT_SYSTEM_FULL
+ ? STRV_MAKE(usr_dir, boot_dir, etc_dir)
+ : STRV_MAKE(usr_dir, boot_dir), READONLY);
if (r < 0)
return r;
}
@@ -499,12 +517,20 @@ int setup_namespace(
drop_duplicates(mounts, &n);
}
- if (n > 0) {
+ if (n > 0 || root_directory) {
/* 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)
return -errno;
+ }
+
+ if (root_directory) {
+ /* Turn directory into bind mount */
+ if (mount(root_directory, root_directory, NULL, MS_BIND|MS_REC, NULL) < 0)
+ return -errno;
+ }
+ if (n > 0) {
for (m = mounts; m < mounts + n; ++m) {
r = apply_mount(m, tmp_dir, var_tmp_dir);
if (r < 0)
@@ -518,12 +544,21 @@ int setup_namespace(
}
}
+ if (root_directory) {
+ /* MS_MOVE does not work on MS_SHARED so the remount MS_SHARED will be done later */
+ r = mount_move_root(root_directory);
+
+ /* at this point, we cannot rollback */
+ if (r < 0)
+ return r;
+ }
+
/* Remount / as the desired mode. Not that this will not
* reestablish propagation from our side to the host, since
* what's disconnected is disconnected. */
if (mount(NULL, "/", NULL, mount_flags | MS_REC, NULL) < 0) {
- r = -errno;
- goto fail;
+ /* at this point, we cannot rollback */
+ return -errno;
}
return 0;
@@ -532,7 +567,7 @@ fail:
if (n > 0) {
for (m = mounts; m < mounts + n; ++m)
if (m->done)
- umount2(m->path, MNT_DETACH);
+ (void) umount2(m->path, MNT_DETACH);
}
return r;
diff --git a/src/core/namespace.h b/src/core/namespace.h
index 42b92e7803..00ab22bf2e 100644
--- a/src/core/namespace.h
+++ b/src/core/namespace.h
@@ -41,7 +41,8 @@ typedef enum ProtectSystem {
_PROTECT_SYSTEM_INVALID = -1
} ProtectSystem;
-int setup_namespace(char **read_write_dirs,
+int setup_namespace(const char *chroot,
+ char **read_write_dirs,
char **read_only_dirs,
char **inaccessible_dirs,
const char *tmp_dir,
diff --git a/src/core/org.freedesktop.systemd1.policy.in.in b/src/core/org.freedesktop.systemd1.policy.in.in
index fd771b4b26..cc39a9e1c3 100644
--- a/src/core/org.freedesktop.systemd1.policy.in.in
+++ b/src/core/org.freedesktop.systemd1.policy.in.in
@@ -28,8 +28,8 @@
</action>
<action id="org.freedesktop.systemd1.manage-units">
- <_description>Manage system services or units</_description>
- <_message>Authentication is required to manage system services or units.</_message>
+ <_description>Manage system services or other units</_description>
+ <_message>Authentication is required to manage system services or other units.</_message>
<defaults>
<allow_any>auth_admin</allow_any>
<allow_inactive>auth_admin</allow_inactive>
@@ -47,6 +47,16 @@
</defaults>
</action>
+ <action id="org.freedesktop.systemd1.set-environment">
+ <_description>Set or unset system and service manager environment variables</_description>
+ <_message>Authentication is required to set or unset system and service manager environment variables.</_message>
+ <defaults>
+ <allow_any>auth_admin</allow_any>
+ <allow_inactive>auth_admin</allow_inactive>
+ <allow_active>auth_admin_keep</allow_active>
+ </defaults>
+ </action>
+
<action id="org.freedesktop.systemd1.reload-daemon">
<_description>Reload the systemd state</_description>
<_message>Authentication is required to reload the systemd state.</_message>
diff --git a/src/core/path.c b/src/core/path.c
index e5ea79fec7..6d26d89e82 100644
--- a/src/core/path.c
+++ b/src/core/path.c
@@ -21,7 +21,6 @@
#include <sys/inotify.h>
#include <sys/epoll.h>
-#include <sys/ioctl.h>
#include <errno.h>
#include <unistd.h>
@@ -31,7 +30,6 @@
#include "mkdir.h"
#include "dbus-path.h"
#include "special.h"
-#include "path-util.h"
#include "macro.h"
#include "bus-util.h"
#include "bus-error.h"
@@ -75,6 +73,8 @@ int path_spec_watch(PathSpec *s, sd_event_io_handler_t handler) {
if (r < 0)
goto fail;
+ (void) sd_event_source_set_description(s->event_source, "path");
+
/* This assumes the path was passed through path_kill_slashes()! */
for (slash = strchr(s->path, '/'); ; slash = strchr(slash+1, '/')) {
@@ -99,9 +99,7 @@ int path_spec_watch(PathSpec *s, sd_event_io_handler_t handler) {
break;
}
- log_warning("Failed to add watch on %s: %s", s->path,
- errno == ENOSPC ? "too many watches" : strerror(-r));
- r = -errno;
+ r = log_warning_errno(errno, "Failed to add watch on %s: %s", s->path, errno == ENOSPC ? "too many watches" : strerror(-r));
if (cut)
*cut = tmp;
goto fail;
@@ -136,9 +134,8 @@ int path_spec_watch(PathSpec *s, sd_event_io_handler_t handler) {
}
if (!exists) {
- log_error_errno(errno, "Failed to add watch on any of the components of %s: %m",
- s->path);
- r = -errno; /* either EACCESS or ENOENT */
+ r = log_error_errno(errno, "Failed to add watch on any of the components of %s: %m", s->path);
+ /* either EACCESS or ENOENT */
goto fail;
}
@@ -300,8 +297,7 @@ static int path_verify(Path *p) {
return 0;
if (!p->specs) {
- log_unit_error(UNIT(p)->id,
- "%s lacks path setting. Refusing.", UNIT(p)->id);
+ log_unit_error(UNIT(p), "Path unit lacks path setting. Refusing.");
return -EINVAL;
}
@@ -318,7 +314,7 @@ static int path_add_default_dependencies(Path *p) {
if (r < 0)
return r;
- if (UNIT(p)->manager->running_as == SYSTEMD_SYSTEM) {
+ if (UNIT(p)->manager->running_as == MANAGER_SYSTEM) {
r = unit_add_two_dependencies_by_name(UNIT(p), UNIT_AFTER, UNIT_REQUIRES,
SPECIAL_SYSINIT_TARGET, NULL, true);
if (r < 0)
@@ -430,10 +426,7 @@ static void path_set_state(Path *p, PathState state) {
path_unwatch(p);
if (state != old_state)
- log_debug("%s changed %s -> %s",
- UNIT(p)->id,
- path_state_to_string(old_state),
- path_state_to_string(state));
+ log_debug("Changed %s -> %s", path_state_to_string(old_state), path_state_to_string(state));
unit_notify(UNIT(p), state_translation_table[old_state], state_translation_table[state], true);
}
@@ -492,8 +485,7 @@ static void path_enter_running(Path *p) {
return;
fail:
- log_warning("%s failed to queue unit startup job: %s",
- UNIT(p)->id, bus_error_message(&error, r));
+ log_unit_warning(UNIT(p), "Failed to queue unit startup job: %s", bus_error_message(&error, r));
path_enter_dead(p, PATH_FAILURE_RESOURCES);
}
@@ -518,7 +510,7 @@ static void path_enter_waiting(Path *p, bool initial, bool recheck) {
if (recheck)
if (path_check_good(p, initial)) {
- log_debug("%s got triggered.", UNIT(p)->id);
+ log_unit_debug(UNIT(p), "Got triggered.");
path_enter_running(p);
return;
}
@@ -533,7 +525,7 @@ static void path_enter_waiting(Path *p, bool initial, bool recheck) {
if (recheck)
if (path_check_good(p, false)) {
- log_debug("%s got triggered.", UNIT(p)->id);
+ log_unit_debug(UNIT(p), "Got triggered.");
path_enter_running(p);
return;
}
@@ -542,7 +534,7 @@ static void path_enter_waiting(Path *p, bool initial, bool recheck) {
return;
fail:
- log_warning_errno(r, "%s failed to enter waiting state: %m", UNIT(p)->id);
+ log_unit_warning_errno(UNIT(p), r, "Failed to enter waiting state: %m");
path_enter_dead(p, PATH_FAILURE_RESOURCES);
}
@@ -611,7 +603,7 @@ static int path_deserialize_item(Unit *u, const char *key, const char *value, FD
state = path_state_from_string(value);
if (state < 0)
- log_debug("Failed to parse state value %s", value);
+ log_unit_debug(u, "Failed to parse state value: %s", value);
else
p->deserialized_state = state;
@@ -620,12 +612,12 @@ static int path_deserialize_item(Unit *u, const char *key, const char *value, FD
f = path_result_from_string(value);
if (f < 0)
- log_debug("Failed to parse result value %s", value);
+ log_unit_debug(u, "Failed to parse result value: %s", value);
else if (f != PATH_SUCCESS)
p->result = f;
} else
- log_debug("Unknown serialization key '%s'", key);
+ log_unit_debug(u, "Unknown serialization key: %s", key);
return 0;
}
@@ -703,9 +695,7 @@ static void path_trigger_notify(Unit *u, Unit *other) {
if (p->state == PATH_RUNNING &&
UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(other))) {
- log_unit_debug(UNIT(p)->id,
- "%s got notified about unit deactivation.",
- UNIT(p)->id);
+ log_unit_debug(UNIT(p), "Got notified about unit deactivation.");
/* Hmm, so inotify was triggered since the
* last activation, so I guess we need to
diff --git a/src/core/path.h b/src/core/path.h
index 0d36aab960..dec39333e4 100644
--- a/src/core/path.h
+++ b/src/core/path.h
@@ -25,7 +25,6 @@ typedef struct Path Path;
typedef struct PathSpec PathSpec;
#include "unit.h"
-#include "mount.h"
typedef enum PathState {
PATH_DEAD,
diff --git a/src/core/scope.c b/src/core/scope.c
index b41db7872c..ab1769b46b 100644
--- a/src/core/scope.c
+++ b/src/core/scope.c
@@ -20,12 +20,10 @@
***/
#include <errno.h>
-#include <signal.h>
#include <unistd.h>
#include "unit.h"
#include "scope.h"
-#include "load-fragment.h"
#include "log.h"
#include "dbus-scope.h"
#include "special.h"
@@ -83,12 +81,18 @@ static int scope_arm_timer(Scope *s) {
return sd_event_source_set_enabled(s->timer_event_source, SD_EVENT_ONESHOT);
}
- return sd_event_add_time(
+ r = sd_event_add_time(
UNIT(s)->manager->event,
&s->timer_event_source,
CLOCK_MONOTONIC,
now(CLOCK_MONOTONIC) + s->timeout_stop_usec, 0,
scope_dispatch_timer, s);
+ if (r < 0)
+ return r;
+
+ (void) sd_event_source_set_description(s->timer_event_source, "scope-timer");
+
+ return 0;
}
static void scope_set_state(Scope *s, ScopeState state) {
@@ -133,7 +137,7 @@ static int scope_verify(Scope *s) {
return 0;
if (set_isempty(UNIT(s)->pids) && UNIT(s)->manager->n_reloading <= 0) {
- log_unit_error(UNIT(s)->id, "Scope %s has no PIDs. Refusing.", UNIT(s)->id);
+ log_unit_error(UNIT(s), "Scope has no PIDs. Refusing.");
return -EINVAL;
}
@@ -264,8 +268,7 @@ static void scope_enter_signal(Scope *s, ScopeState state, ScopeResult f) {
return;
fail:
- log_unit_warning(UNIT(s)->id,
- "%s failed to kill processes: %s", UNIT(s)->id, strerror(-r));
+ log_unit_warning_errno(UNIT(s), r, "Failed to kill processes: %m");
scope_enter_dead(s, SCOPE_FAILURE_RESOURCES);
}
@@ -279,6 +282,7 @@ static int scope_start(Unit *u) {
if (s->state == SCOPE_FAILED)
return -EPERM;
+ /* We can't fulfill this right now, please try again later */
if (s->state == SCOPE_STOP_SIGTERM ||
s->state == SCOPE_STOP_SIGKILL)
return -EAGAIN;
@@ -288,9 +292,15 @@ static int scope_start(Unit *u) {
if (!u->transient && UNIT(s)->manager->n_reloading <= 0)
return -ENOENT;
+ (void) unit_realize_cgroup(u);
+ (void) unit_reset_cpu_usage(u);
+
r = unit_attach_pids_to_cgroup(u);
- if (r < 0)
+ if (r < 0) {
+ log_unit_warning_errno(UNIT(s), r, "Failed to add PIDs to scope's control group: %m");
+ scope_enter_dead(s, SCOPE_FAILURE_RESOURCES);
return r;
+ }
s->result = SCOPE_SUCCESS;
@@ -367,12 +377,12 @@ static int scope_deserialize_item(Unit *u, const char *key, const char *value, F
state = scope_state_from_string(value);
if (state < 0)
- log_debug("Failed to parse state value %s", value);
+ log_unit_debug(u, "Failed to parse state value: %s", value);
else
s->deserialized_state = state;
} else
- log_debug("Unknown serialization key '%s'", key);
+ log_unit_debug(u, "Unknown serialization key: %s", key);
return 0;
}
@@ -398,7 +408,7 @@ static void scope_notify_cgroup_empty_event(Unit *u) {
Scope *s = SCOPE(u);
assert(u);
- log_unit_debug(u->id, "%s: cgroup is empty", u->id);
+ log_unit_debug(u, "cgroup is empty");
if (IN_SET(s->state, SCOPE_RUNNING, SCOPE_ABANDONED, SCOPE_STOP_SIGTERM, SCOPE_STOP_SIGKILL))
scope_enter_dead(s, SCOPE_SUCCESS);
@@ -430,17 +440,17 @@ static int scope_dispatch_timer(sd_event_source *source, usec_t usec, void *user
case SCOPE_STOP_SIGTERM:
if (s->kill_context.send_sigkill) {
- log_unit_warning(UNIT(s)->id, "%s stopping timed out. Killing.", UNIT(s)->id);
+ log_unit_warning(UNIT(s), "Stopping timed out. Killing.");
scope_enter_signal(s, SCOPE_STOP_SIGKILL, SCOPE_FAILURE_TIMEOUT);
} else {
- log_unit_warning(UNIT(s)->id, "%s stopping timed out. Skipping SIGKILL.", UNIT(s)->id);
+ log_unit_warning(UNIT(s), "Stopping timed out. Skipping SIGKILL.");
scope_enter_dead(s, SCOPE_FAILURE_TIMEOUT);
}
break;
case SCOPE_STOP_SIGKILL:
- log_unit_warning(UNIT(s)->id, "%s still around after SIGKILL. Ignoring.", UNIT(s)->id);
+ log_unit_warning(UNIT(s), "Still around after SIGKILL. Ignoring.");
scope_enter_dead(s, SCOPE_FAILURE_TIMEOUT);
break;
diff --git a/src/core/scope.h b/src/core/scope.h
index 6c59126422..4452fe2c94 100644
--- a/src/core/scope.h
+++ b/src/core/scope.h
@@ -23,7 +23,6 @@
typedef struct Scope Scope;
-#include "unit.h"
#include "kill.h"
typedef enum ScopeState {
diff --git a/src/core/selinux-access.c b/src/core/selinux-access.c
index 18888747f2..5e9a4a5e02 100644
--- a/src/core/selinux-access.c
+++ b/src/core/selinux-access.c
@@ -24,12 +24,9 @@
#ifdef HAVE_SELINUX
#include <stdio.h>
-#include <string.h>
#include <errno.h>
-#include <limits.h>
#include <selinux/selinux.h>
#include <selinux/avc.h>
-#include <sys/socket.h>
#ifdef HAVE_AUDIT
#include <libaudit.h>
#endif
@@ -38,7 +35,6 @@
#include "bus-util.h"
#include "util.h"
#include "log.h"
-#include "audit.h"
#include "selinux-util.h"
#include "audit-fd.h"
#include "strv.h"
@@ -84,17 +80,33 @@ static int audit_callback(
return 0;
}
+static int callback_type_to_priority(int type) {
+ switch(type) {
+ case SELINUX_ERROR: return LOG_ERR;
+ case SELINUX_WARNING: return LOG_WARNING;
+ case SELINUX_INFO: return LOG_INFO;
+ case SELINUX_AVC:
+ default: return LOG_NOTICE;
+ }
+}
+
/*
- Any time an access gets denied this callback will be called
- code copied from dbus. If audit is turned on the messages will go as
- user_avc's into the /var/log/audit/audit.log, otherwise they will be
- sent to syslog.
+ libselinux uses this callback when access gets denied or other
+ events happen. If audit is turned on, messages will be reported
+ using audit netlink, otherwise they will be logged using the usual
+ channels.
+
+ Code copied from dbus and modified.
*/
_printf_(2, 3) static int log_callback(int type, const char *fmt, ...) {
va_list ap;
#ifdef HAVE_AUDIT
- if (get_audit_fd() >= 0) {
+ int fd;
+
+ fd = get_audit_fd();
+
+ if (fd >= 0) {
_cleanup_free_ char *buf = NULL;
int r;
@@ -103,14 +115,15 @@ _printf_(2, 3) static int log_callback(int type, const char *fmt, ...) {
va_end(ap);
if (r >= 0) {
- audit_log_user_avc_message(get_audit_fd(), AUDIT_USER_AVC, buf, NULL, NULL, NULL, 0);
+ audit_log_user_avc_message(fd, AUDIT_USER_AVC, buf, NULL, NULL, NULL, 0);
return 0;
}
}
#endif
va_start(ap, fmt);
- log_internalv(LOG_AUTH | LOG_INFO, 0, __FILE__, __LINE__, __FUNCTION__, fmt, ap);
+ log_internalv(LOG_AUTH | callback_type_to_priority(type),
+ 0, __FILE__, __LINE__, __FUNCTION__, fmt, ap);
va_end(ap);
return 0;
@@ -209,6 +222,14 @@ int mac_selinux_generic_access_check(
if (r < 0)
goto finish;
+ /* The SELinux context is something we really should have
+ * gotten directly from the message or sender, and not be an
+ * augmented field. If it was augmented we cannot use it for
+ * authorization, since this is racy and vulnerable. Let's add
+ * an extra check, just in case, even though this really
+ * shouldn't be possible. */
+ assert_return((sd_bus_creds_get_augmented_mask(creds) & SD_BUS_CREDS_SELINUX_CONTEXT) == 0, -EPERM);
+
r = sd_bus_creds_get_selinux_context(creds, &scon);
if (r < 0)
goto finish;
diff --git a/src/core/selinux-access.h b/src/core/selinux-access.h
index dd1e8bb9d0..b5758e2e42 100644
--- a/src/core/selinux-access.h
+++ b/src/core/selinux-access.h
@@ -22,7 +22,6 @@
***/
#include "sd-bus.h"
-#include "bus-error.h"
#include "bus-util.h"
#include "manager.h"
diff --git a/src/core/selinux-setup.c b/src/core/selinux-setup.c
index fba915d7da..a4678500e6 100644
--- a/src/core/selinux-setup.c
+++ b/src/core/selinux-setup.c
@@ -22,8 +22,6 @@
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
-#include <string.h>
-#include <stdlib.h>
#ifdef HAVE_SELINUX
#include <selinux/selinux.h>
@@ -31,8 +29,6 @@
#include "selinux-setup.h"
#include "selinux-util.h"
-#include "label.h"
-#include "mount-setup.h"
#include "macro.h"
#include "util.h"
#include "log.h"
diff --git a/src/core/service.c b/src/core/service.c
index 15e29be149..c7e65772ea 100644
--- a/src/core/service.c
+++ b/src/core/service.c
@@ -21,7 +21,6 @@
#include <errno.h>
#include <signal.h>
-#include <dirent.h>
#include <unistd.h>
#include "async.h"
@@ -46,6 +45,9 @@
#include "bus-error.h"
#include "bus-util.h"
#include "bus-kernel.h"
+#include "formats-util.h"
+#include "process-util.h"
+#include "signal-util.h"
static const UnitActiveState state_translation_table[_SERVICE_STATE_MAX] = {
[SERVICE_DEAD] = UNIT_INACTIVE,
@@ -137,7 +139,7 @@ static void service_unwatch_pid_file(Service *s) {
if (!s->pid_file_pathspec)
return;
- log_unit_debug(UNIT(s)->id, "Stopping watch for %s's PID file %s", UNIT(s)->id, s->pid_file_pathspec->path);
+ log_unit_debug(UNIT(s), "Stopping watch for PID file %s", s->pid_file_pathspec->path);
path_spec_unwatch(s->pid_file_pathspec);
path_spec_done(s->pid_file_pathspec);
free(s->pid_file_pathspec);
@@ -167,7 +169,7 @@ static int service_set_main_pid(Service *s, pid_t pid) {
s->main_pid_known = true;
if (get_parent_of_pid(pid, &ppid) >= 0 && ppid != getpid()) {
- log_unit_warning(UNIT(s)->id, "%s: Supervising process "PID_FMT" which is not our child. We'll most likely not notice when it exits.", UNIT(s)->id, pid);
+ log_unit_warning(UNIT(s), "Supervising process "PID_FMT" which is not our child. We'll most likely not notice when it exits.", pid);
s->main_pid_alien = true;
} else
s->main_pid_alien = false;
@@ -209,7 +211,7 @@ static void service_start_watchdog(Service *s) {
if (s->watchdog_event_source) {
r = sd_event_source_set_time(s->watchdog_event_source, s->watchdog_timestamp.monotonic + s->watchdog_usec);
if (r < 0) {
- log_unit_warning_errno(UNIT(s)->id, r, "%s failed to reset watchdog timer: %m", UNIT(s)->id);
+ log_unit_warning_errno(UNIT(s), r, "Failed to reset watchdog timer: %m");
return;
}
@@ -222,17 +224,19 @@ static void service_start_watchdog(Service *s) {
s->watchdog_timestamp.monotonic + s->watchdog_usec, 0,
service_dispatch_watchdog, s);
if (r < 0) {
- log_unit_warning_errno(UNIT(s)->id, r, "%s failed to add watchdog timer: %m", UNIT(s)->id);
+ log_unit_warning_errno(UNIT(s), r, "Failed to add watchdog timer: %m");
return;
}
+ (void) sd_event_source_set_description(s->watchdog_event_source, "service-watchdog");
+
/* Let's process everything else which might be a sign
* of living before we consider a service died. */
r = sd_event_source_set_priority(s->watchdog_event_source, SD_EVENT_PRIORITY_IDLE);
}
if (r < 0)
- log_unit_warning_errno(UNIT(s)->id, r, "%s failed to install watchdog timer: %m", UNIT(s)->id);
+ log_unit_warning_errno(UNIT(s), r, "Failed to install watchdog timer: %m");
}
static void service_reset_watchdog(Service *s) {
@@ -270,7 +274,7 @@ static void service_release_resources(Unit *u) {
if (!s->fd_store)
return;
- log_debug("Releasing all resources for %s", u->id);
+ log_unit_debug(u, "Releasing all resources.");
while (s->fd_store)
service_fd_store_unlink(s->fd_store);
@@ -371,6 +375,8 @@ static int service_add_fd_store(Service *s, int fd) {
return r;
}
+ (void) sd_event_source_set_description(fs->event_source, "service-fd-store");
+
LIST_PREPEND(fd_store, s->fd_store, fs);
s->n_fd_store++;
@@ -394,16 +400,16 @@ static int service_add_fd_store_set(Service *s, FDSet *fds) {
r = service_add_fd_store(s, fd);
if (r < 0)
- return log_unit_error_errno(UNIT(s)->id, r, "%s: Couldn't add fd to fd store: %m", UNIT(s)->id);
+ return log_unit_error_errno(UNIT(s), r, "Couldn't add fd to fd store: %m");
if (r > 0) {
- log_unit_debug(UNIT(s)->id, "%s: added fd to fd store.", UNIT(s)->id);
+ log_unit_debug(UNIT(s), "Added fd to fd store.");
fd = -1;
}
}
if (fdset_size(fds) > 0)
- log_unit_warning(UNIT(s)->id, "%s: tried to store more fds than FDStoreMax=%u allows, closing remaining.", UNIT(s)->id, s->n_fd_store_max);
+ log_unit_warning(UNIT(s), "Tried to store more fds than FDStoreMax=%u allows, closing remaining.", s->n_fd_store_max);
return 0;
}
@@ -421,12 +427,18 @@ static int service_arm_timer(Service *s, usec_t usec) {
return sd_event_source_set_enabled(s->timer_event_source, SD_EVENT_ONESHOT);
}
- return sd_event_add_time(
+ r = sd_event_add_time(
UNIT(s)->manager->event,
&s->timer_event_source,
CLOCK_MONOTONIC,
now(CLOCK_MONOTONIC) + usec, 0,
service_dispatch_timer, s);
+ if (r < 0)
+ return r;
+
+ (void) sd_event_source_set_description(s->timer_event_source, "service-timer");
+
+ return 0;
}
static int service_verify(Service *s) {
@@ -436,45 +448,45 @@ static int service_verify(Service *s) {
return 0;
if (!s->exec_command[SERVICE_EXEC_START] && !s->exec_command[SERVICE_EXEC_STOP]) {
- log_unit_error(UNIT(s)->id, "%s lacks both ExecStart= and ExecStop= setting. Refusing.", UNIT(s)->id);
+ log_unit_error(UNIT(s), "Service lacks both ExecStart= and ExecStop= setting. Refusing.");
return -EINVAL;
}
if (s->type != SERVICE_ONESHOT && !s->exec_command[SERVICE_EXEC_START]) {
- log_unit_error(UNIT(s)->id, "%s has no ExecStart= setting, which is only allowed for Type=oneshot services. Refusing.", UNIT(s)->id);
+ log_unit_error(UNIT(s), "Service has no ExecStart= setting, which is only allowed for Type=oneshot services. Refusing.");
return -EINVAL;
}
if (!s->remain_after_exit && !s->exec_command[SERVICE_EXEC_START]) {
- log_unit_error(UNIT(s)->id, "%s has no ExecStart= setting, which is only allowed for RemainAfterExit=yes services. Refusing.", UNIT(s)->id);
+ log_unit_error(UNIT(s), "Service has no ExecStart= setting, which is only allowed for RemainAfterExit=yes services. Refusing.");
return -EINVAL;
}
if (s->type != SERVICE_ONESHOT && s->exec_command[SERVICE_EXEC_START]->command_next) {
- log_unit_error(UNIT(s)->id, "%s has more than one ExecStart= setting, which is only allowed for Type=oneshot services. Refusing.", UNIT(s)->id);
+ log_unit_error(UNIT(s), "Service has more than one ExecStart= setting, which is only allowed for Type=oneshot services. Refusing.");
return -EINVAL;
}
if (s->type == SERVICE_ONESHOT && s->restart != SERVICE_RESTART_NO) {
- log_unit_error(UNIT(s)->id, "%s has Restart= setting other than no, which isn't allowed for Type=oneshot services. Refusing.", UNIT(s)->id);
+ log_unit_error(UNIT(s), "Service has Restart= setting other than no, which isn't allowed for Type=oneshot services. Refusing.");
return -EINVAL;
}
if (s->type == SERVICE_ONESHOT && !exit_status_set_is_empty(&s->restart_force_status)) {
- log_unit_error(UNIT(s)->id, "%s has RestartForceStatus= set, which isn't allowed for Type=oneshot services. Refusing.", UNIT(s)->id);
+ log_unit_error(UNIT(s), "Service has RestartForceStatus= set, which isn't allowed for Type=oneshot services. Refusing.");
return -EINVAL;
}
if (s->type == SERVICE_DBUS && !s->bus_name) {
- log_unit_error(UNIT(s)->id, "%s is of type D-Bus but no D-Bus service name has been specified. Refusing.", UNIT(s)->id);
+ log_unit_error(UNIT(s), "Service is of type D-Bus but no D-Bus service name has been specified. Refusing.");
return -EINVAL;
}
if (s->bus_name && s->type != SERVICE_DBUS)
- log_unit_warning(UNIT(s)->id, "%s has a D-Bus service name specified, but is not of type dbus. Ignoring.", UNIT(s)->id);
+ log_unit_warning(UNIT(s), "Service has a D-Bus service name specified, but is not of type dbus. Ignoring.");
if (s->exec_context.pam_name && !(s->kill_context.kill_mode == KILL_CONTROL_GROUP || s->kill_context.kill_mode == KILL_MIXED)) {
- log_unit_error(UNIT(s)->id, "%s has PAM enabled. Kill mode must be set to 'control-group' or 'mixed'. Refusing.", UNIT(s)->id);
+ log_unit_error(UNIT(s), "Service has PAM enabled. Kill mode must be set to 'control-group' or 'mixed'. Refusing.");
return -EINVAL;
}
@@ -556,14 +568,16 @@ static int service_add_extras(Service *s) {
s->notify_access = NOTIFY_MAIN;
if (s->bus_name) {
+#ifdef ENABLE_KDBUS
const char *n;
- r = unit_watch_bus_name(UNIT(s), s->bus_name);
+ n = strjoina(s->bus_name, ".busname");
+ r = unit_add_dependency_by_name(UNIT(s), UNIT_AFTER, n, NULL, true);
if (r < 0)
return r;
+#endif
- n = strjoina(s->bus_name, ".busname");
- r = unit_add_dependency_by_name(UNIT(s), UNIT_AFTER, n, NULL, true);
+ r = unit_watch_bus_name(UNIT(s), s->bus_name);
if (r < 0)
return r;
}
@@ -711,20 +725,20 @@ static int service_load_pid_file(Service *s, bool may_warn) {
r = read_one_line_file(s->pid_file, &k);
if (r < 0) {
if (may_warn)
- log_unit_info(UNIT(s)->id, "PID file %s not readable (yet?) after %s.", s->pid_file, service_state_to_string(s->state));
+ log_unit_info_errno(UNIT(s), r, "PID file %s not readable (yet?) after %s: %m", s->pid_file, service_state_to_string(s->state));
return r;
}
r = parse_pid(k, &pid);
if (r < 0) {
if (may_warn)
- log_unit_info_errno(UNIT(s)->id, r, "Failed to read PID from file %s: %m", s->pid_file);
+ log_unit_info_errno(UNIT(s), r, "Failed to read PID from file %s: %m", s->pid_file);
return r;
}
if (!pid_is_alive(pid)) {
if (may_warn)
- log_unit_info(UNIT(s)->id, "PID "PID_FMT" read from file %s does not exist or is a zombie.", pid, s->pid_file);
+ log_unit_info(UNIT(s), "PID "PID_FMT" read from file %s does not exist or is a zombie.", pid, s->pid_file);
return -ESRCH;
}
@@ -732,12 +746,12 @@ static int service_load_pid_file(Service *s, bool may_warn) {
if (pid == s->main_pid)
return 0;
- log_unit_debug(UNIT(s)->id, "Main PID changing: "PID_FMT" -> "PID_FMT, s->main_pid, pid);
+ log_unit_debug(UNIT(s), "Main PID changing: "PID_FMT" -> "PID_FMT, s->main_pid, pid);
service_unwatch_main_pid(s);
s->main_pid_known = false;
} else
- log_unit_debug(UNIT(s)->id, "Main PID loaded: "PID_FMT, pid);
+ log_unit_debug(UNIT(s), "Main PID loaded: "PID_FMT, pid);
r = service_set_main_pid(s, pid);
if (r < 0)
@@ -746,7 +760,7 @@ static int service_load_pid_file(Service *s, bool may_warn) {
r = unit_watch_pid(UNIT(s), pid);
if (r < 0) {
/* FIXME: we need to do something here */
- log_unit_warning(UNIT(s)->id, "Failed to watch PID "PID_FMT" from service %s", pid, UNIT(s)->id);
+ log_unit_warning_errno(UNIT(s), r, "Failed to watch PID "PID_FMT" for service: %m", pid);
return r;
}
@@ -773,7 +787,7 @@ static int service_search_main_pid(Service *s) {
if (pid <= 0)
return -ENOENT;
- log_unit_debug(UNIT(s)->id, "Main PID guessed: "PID_FMT, pid);
+ log_unit_debug(UNIT(s), "Main PID guessed: "PID_FMT, pid);
r = service_set_main_pid(s, pid);
if (r < 0)
return r;
@@ -781,7 +795,7 @@ static int service_search_main_pid(Service *s) {
r = unit_watch_pid(UNIT(s), pid);
if (r < 0) {
/* FIXME: we need to do something here */
- log_unit_warning(UNIT(s)->id, "Failed to watch PID "PID_FMT" from service %s", pid, UNIT(s)->id);
+ log_unit_warning_errno(UNIT(s), r, "Failed to watch PID "PID_FMT" from: %m", pid);
return r;
}
@@ -804,8 +818,7 @@ static void service_set_state(Service *s, ServiceState state) {
if (!IN_SET(state,
SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST,
SERVICE_RELOAD,
- SERVICE_STOP, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL,
- SERVICE_STOP_SIGABRT, SERVICE_STOP_POST,
+ SERVICE_STOP, SERVICE_STOP_SIGABRT, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL,
SERVICE_AUTO_RESTART))
s->timer_event_source = sd_event_source_unref(s->timer_event_source);
@@ -813,8 +826,7 @@ static void service_set_state(Service *s, ServiceState state) {
if (!IN_SET(state,
SERVICE_START, SERVICE_START_POST,
SERVICE_RUNNING, SERVICE_RELOAD,
- SERVICE_STOP, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL,
- SERVICE_STOP_SIGABRT, SERVICE_STOP_POST,
+ SERVICE_STOP, SERVICE_STOP_SIGABRT, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL)) {
service_unwatch_main_pid(s);
s->main_command = NULL;
@@ -823,8 +835,7 @@ static void service_set_state(Service *s, ServiceState state) {
if (!IN_SET(state,
SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST,
SERVICE_RELOAD,
- SERVICE_STOP, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL,
- SERVICE_STOP_SIGABRT, SERVICE_STOP_POST,
+ SERVICE_STOP, SERVICE_STOP_SIGABRT, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL)) {
service_unwatch_control_pid(s);
s->control_command = NULL;
@@ -837,8 +848,8 @@ static void service_set_state(Service *s, ServiceState state) {
if (!IN_SET(state,
SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST,
SERVICE_RUNNING, SERVICE_RELOAD,
- SERVICE_STOP, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
- SERVICE_STOP_SIGABRT, SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL) &&
+ SERVICE_STOP, SERVICE_STOP_SIGABRT, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
+ SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL) &&
!(state == SERVICE_DEAD && UNIT(s)->job)) {
service_close_socket_fd(s);
service_connection_unref(s);
@@ -873,7 +884,7 @@ static void service_set_state(Service *s, ServiceState state) {
}
if (old_state != state)
- log_unit_debug(UNIT(s)->id, "%s changed %s -> %s", UNIT(s)->id, service_state_to_string(old_state), service_state_to_string(state));
+ log_unit_debug(UNIT(s), "Changed %s -> %s", service_state_to_string(old_state), service_state_to_string(state));
unit_notify(UNIT(s), table[old_state], table[state], s->reload_result == SERVICE_SUCCESS);
s->reload_result = SERVICE_SUCCESS;
@@ -891,8 +902,7 @@ static int service_coldplug(Unit *u) {
if (IN_SET(s->deserialized_state,
SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST,
SERVICE_RELOAD,
- SERVICE_STOP, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL,
- SERVICE_STOP_SIGABRT, SERVICE_STOP_POST,
+ SERVICE_STOP, SERVICE_STOP_SIGABRT, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL)) {
usec_t k;
@@ -920,8 +930,7 @@ static int service_coldplug(Unit *u) {
IN_SET(s->deserialized_state,
SERVICE_START, SERVICE_START_POST,
SERVICE_RUNNING, SERVICE_RELOAD,
- SERVICE_STOP, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL,
- SERVICE_STOP_SIGABRT, SERVICE_STOP_POST,
+ SERVICE_STOP, SERVICE_STOP_SIGABRT, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL))) {
r = unit_watch_pid(UNIT(s), s->main_pid);
if (r < 0)
@@ -932,8 +941,7 @@ static int service_coldplug(Unit *u) {
IN_SET(s->deserialized_state,
SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST,
SERVICE_RELOAD,
- SERVICE_STOP, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL,
- SERVICE_STOP_SIGABRT, SERVICE_STOP_POST,
+ SERVICE_STOP, SERVICE_STOP_SIGABRT, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL)) {
r = unit_watch_pid(UNIT(s), s->control_pid);
if (r < 0)
@@ -1047,10 +1055,10 @@ static int service_spawn(
**argv = NULL, **final_env = NULL, **our_env = NULL;
const char *path;
ExecParameters exec_params = {
- .apply_permissions = apply_permissions,
- .apply_chroot = apply_chroot,
- .apply_tty_stdin = apply_tty_stdin,
- .bus_endpoint_fd = -1,
+ .apply_permissions = apply_permissions,
+ .apply_chroot = apply_chroot,
+ .apply_tty_stdin = apply_tty_stdin,
+ .bus_endpoint_fd = -1,
.selinux_context_net = s->socket_fd_selinux_context_net
};
@@ -1058,7 +1066,11 @@ static int service_spawn(
assert(c);
assert(_pid);
- unit_realize_cgroup(UNIT(s));
+ (void) unit_realize_cgroup(UNIT(s));
+ if (s->reset_cpu_usage) {
+ (void) unit_reset_cpu_usage(UNIT(s));
+ s->reset_cpu_usage = false;
+ }
r = unit_setup_exec_runtime(UNIT(s));
if (r < 0)
@@ -1092,7 +1104,7 @@ static int service_spawn(
if (r < 0)
goto fail;
- our_env = new0(char*, 4);
+ our_env = new0(char*, 6);
if (!our_env) {
r = -ENOMEM;
goto fail;
@@ -1110,12 +1122,52 @@ static int service_spawn(
goto fail;
}
- if (UNIT(s)->manager->running_as != SYSTEMD_SYSTEM)
+ if (UNIT(s)->manager->running_as != MANAGER_SYSTEM)
if (asprintf(our_env + n_env++, "MANAGERPID="PID_FMT, getpid()) < 0) {
r = -ENOMEM;
goto fail;
}
+ if (UNIT_DEREF(s->accept_socket)) {
+ union sockaddr_union sa;
+ socklen_t salen = sizeof(sa);
+
+ r = getpeername(s->socket_fd, &sa.sa, &salen);
+ if (r < 0) {
+ r = -errno;
+ goto fail;
+ }
+
+ if (IN_SET(sa.sa.sa_family, AF_INET, AF_INET6)) {
+ _cleanup_free_ char *addr = NULL;
+ char *t;
+ int port;
+
+ r = sockaddr_pretty(&sa.sa, salen, true, false, &addr);
+ if (r < 0)
+ goto fail;
+
+ t = strappend("REMOTE_ADDR=", addr);
+ if (!t) {
+ r = -ENOMEM;
+ goto fail;
+ }
+ our_env[n_env++] = t;
+
+ port = sockaddr_port(&sa.sa);
+ if (port < 0) {
+ r = port;
+ goto fail;
+ }
+
+ if (asprintf(&t, "REMOTE_PORT=%u", port) < 0) {
+ r = -ENOMEM;
+ goto fail;
+ }
+ our_env[n_env++] = t;
+ }
+ }
+
final_env = strv_env_merge(2, UNIT(s)->manager->environment, our_env, NULL);
if (!final_env) {
r = -ENOMEM;
@@ -1130,7 +1182,7 @@ static int service_spawn(
#ifdef ENABLE_KDBUS
if (s->exec_context.bus_endpoint) {
- r = bus_kernel_create_endpoint(UNIT(s)->manager->running_as == SYSTEMD_SYSTEM ? "system" : "user",
+ r = bus_kernel_create_endpoint(UNIT(s)->manager->running_as == MANAGER_SYSTEM ? "system" : "user",
UNIT(s)->id, &bus_endpoint_path);
if (r < 0)
goto fail;
@@ -1151,13 +1203,13 @@ static int service_spawn(
exec_params.cgroup_path = path;
exec_params.cgroup_delegate = s->cgroup_context.delegate;
exec_params.runtime_prefix = manager_get_runtime_prefix(UNIT(s)->manager);
- exec_params.unit_id = UNIT(s)->id;
exec_params.watchdog_usec = s->watchdog_usec;
exec_params.bus_endpoint_path = bus_endpoint_path;
if (s->type == SERVICE_IDLE)
exec_params.idle_pipe = UNIT(s)->manager->idle_pipe;
- r = exec_spawn(c,
+ r = exec_spawn(UNIT(s),
+ c,
&s->exec_context,
&exec_params,
s->exec_runtime,
@@ -1227,6 +1279,49 @@ static int cgroup_good(Service *s) {
return !r;
}
+static bool service_shall_restart(Service *s) {
+ assert(s);
+
+ /* Don't restart after manual stops */
+ if (s->forbid_restart)
+ return false;
+
+ /* Never restart if this is configured as special exception */
+ if (exit_status_set_test(&s->restart_prevent_status, s->main_exec_status.code, s->main_exec_status.status))
+ return false;
+
+ /* Restart if the exit code/status are configured as restart triggers */
+ if (exit_status_set_test(&s->restart_force_status, s->main_exec_status.code, s->main_exec_status.status))
+ return true;
+
+ switch (s->restart) {
+
+ case SERVICE_RESTART_NO:
+ return false;
+
+ case SERVICE_RESTART_ALWAYS:
+ return true;
+
+ case SERVICE_RESTART_ON_SUCCESS:
+ return s->result == SERVICE_SUCCESS;
+
+ case SERVICE_RESTART_ON_FAILURE:
+ return s->result != SERVICE_SUCCESS;
+
+ case SERVICE_RESTART_ON_ABNORMAL:
+ return !IN_SET(s->result, SERVICE_SUCCESS, SERVICE_FAILURE_EXIT_CODE);
+
+ case SERVICE_RESTART_ON_WATCHDOG:
+ return s->result == SERVICE_FAILURE_WATCHDOG;
+
+ case SERVICE_RESTART_ON_ABORT:
+ return IN_SET(s->result, SERVICE_FAILURE_SIGNAL, SERVICE_FAILURE_CORE_DUMP);
+
+ default:
+ assert_not_reached("unknown restart setting");
+ }
+}
+
static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart) {
int r;
assert(s);
@@ -1237,22 +1332,11 @@ static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart)
service_set_state(s, s->result != SERVICE_SUCCESS ? SERVICE_FAILED : SERVICE_DEAD);
if (s->result != SERVICE_SUCCESS) {
- log_unit_warning(UNIT(s)->id, "%s failed.", UNIT(s)->id);
+ log_unit_warning(UNIT(s), "Failed with result '%s'.", service_result_to_string(s->result));
failure_action(UNIT(s)->manager, s->failure_action, s->reboot_arg);
}
- if (allow_restart &&
- !s->forbid_restart &&
- (s->restart == SERVICE_RESTART_ALWAYS ||
- (s->restart == SERVICE_RESTART_ON_SUCCESS && s->result == SERVICE_SUCCESS) ||
- (s->restart == SERVICE_RESTART_ON_FAILURE && s->result != SERVICE_SUCCESS) ||
- (s->restart == SERVICE_RESTART_ON_ABNORMAL && !IN_SET(s->result, SERVICE_SUCCESS, SERVICE_FAILURE_EXIT_CODE)) ||
- (s->restart == SERVICE_RESTART_ON_WATCHDOG && s->result == SERVICE_FAILURE_WATCHDOG) ||
- (s->restart == SERVICE_RESTART_ON_ABORT && IN_SET(s->result, SERVICE_FAILURE_SIGNAL, SERVICE_FAILURE_CORE_DUMP)) ||
- (s->main_exec_status.code == CLD_EXITED && set_contains(s->restart_force_status.status, INT_TO_PTR(s->main_exec_status.status))) ||
- (IN_SET(s->main_exec_status.code, CLD_KILLED, CLD_DUMPED) && set_contains(s->restart_force_status.signal, INT_TO_PTR(s->main_exec_status.status)))) &&
- (s->main_exec_status.code != CLD_EXITED || !set_contains(s->restart_prevent_status.status, INT_TO_PTR(s->main_exec_status.status))) &&
- (!IN_SET(s->main_exec_status.code, CLD_KILLED, CLD_DUMPED) || !set_contains(s->restart_prevent_status.signal, INT_TO_PTR(s->main_exec_status.status)))) {
+ if (allow_restart && service_shall_restart(s)) {
r = service_arm_timer(s, s->restart_usec);
if (r < 0)
@@ -1261,6 +1345,7 @@ static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart)
service_set_state(s, SERVICE_AUTO_RESTART);
}
+ /* The next restart might not be a manual stop, hence reset the flag indicating manual stops */
s->forbid_restart = false;
/* We want fresh tmpdirs in case service is started again immediately */
@@ -1279,7 +1364,7 @@ static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart)
return;
fail:
- log_unit_warning_errno(UNIT(s)->id, r, "%s failed to run install restart timer: %m", UNIT(s)->id);
+ log_unit_warning_errno(UNIT(s), r, "Failed to run install restart timer: %m");
service_enter_dead(s, SERVICE_FAILURE_RESOURCES, false);
}
@@ -1316,10 +1401,29 @@ static void service_enter_stop_post(Service *s, ServiceResult f) {
return;
fail:
- log_unit_warning_errno(UNIT(s)->id, r, "%s failed to run 'stop-post' task: %m", UNIT(s)->id);
+ log_unit_warning_errno(UNIT(s), r, "Failed to run 'stop-post' task: %m");
service_enter_signal(s, SERVICE_FINAL_SIGTERM, SERVICE_FAILURE_RESOURCES);
}
+static int state_to_kill_operation(ServiceState state) {
+ switch (state) {
+
+ case SERVICE_STOP_SIGABRT:
+ return KILL_ABORT;
+
+ case SERVICE_STOP_SIGTERM:
+ case SERVICE_FINAL_SIGTERM:
+ return KILL_TERMINATE;
+
+ case SERVICE_STOP_SIGKILL:
+ case SERVICE_FINAL_SIGKILL:
+ return KILL_KILL;
+
+ default:
+ return _KILL_OPERATION_INVALID;
+ }
+}
+
static void service_enter_signal(Service *s, ServiceState state, ServiceResult f) {
int r;
@@ -1333,8 +1437,7 @@ static void service_enter_signal(Service *s, ServiceState state, ServiceResult f
r = unit_kill_context(
UNIT(s),
&s->kill_context,
- (state != SERVICE_STOP_SIGTERM && state != SERVICE_FINAL_SIGTERM && state != SERVICE_STOP_SIGABRT) ?
- KILL_KILL : (state == SERVICE_STOP_SIGABRT ? KILL_ABORT : KILL_TERMINATE),
+ state_to_kill_operation(state),
s->main_pid,
s->control_pid,
s->main_pid_alien);
@@ -1350,11 +1453,11 @@ static void service_enter_signal(Service *s, ServiceState state, ServiceResult f
}
service_set_state(s, state);
- } else if (state == SERVICE_STOP_SIGTERM || state == SERVICE_STOP_SIGABRT)
+ } else if (IN_SET(state, SERVICE_STOP_SIGABRT, SERVICE_STOP_SIGTERM) && s->kill_context.send_sigkill)
service_enter_signal(s, SERVICE_STOP_SIGKILL, SERVICE_SUCCESS);
- else if (state == SERVICE_STOP_SIGKILL)
+ else if (IN_SET(state, SERVICE_STOP_SIGABRT, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL))
service_enter_stop_post(s, SERVICE_SUCCESS);
- else if (state == SERVICE_FINAL_SIGTERM)
+ else if (state == SERVICE_FINAL_SIGTERM && s->kill_context.send_sigkill)
service_enter_signal(s, SERVICE_FINAL_SIGKILL, SERVICE_SUCCESS);
else
service_enter_dead(s, SERVICE_SUCCESS, true);
@@ -1362,10 +1465,9 @@ static void service_enter_signal(Service *s, ServiceState state, ServiceResult f
return;
fail:
- log_unit_warning_errno(UNIT(s)->id, r, "%s failed to kill processes: %m", UNIT(s)->id);
+ log_unit_warning_errno(UNIT(s), r, "Failed to kill processes: %m");
- if (state == SERVICE_STOP_SIGTERM || state == SERVICE_STOP_SIGKILL ||
- state == SERVICE_STOP_SIGABRT)
+ if (IN_SET(state, SERVICE_STOP_SIGABRT, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL))
service_enter_stop_post(s, SERVICE_FAILURE_RESOURCES);
else
service_enter_dead(s, SERVICE_FAILURE_RESOURCES, true);
@@ -1417,7 +1519,7 @@ static void service_enter_stop(Service *s, ServiceResult f) {
return;
fail:
- log_unit_warning_errno(UNIT(s)->id, r, "%s failed to run 'stop' task: %m", UNIT(s)->id);
+ log_unit_warning_errno(UNIT(s), r, "Failed to run 'stop' task: %m");
service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_FAILURE_RESOURCES);
}
@@ -1479,7 +1581,7 @@ static void service_enter_start_post(Service *s) {
return;
fail:
- log_unit_warning_errno(UNIT(s)->id, r, "%s failed to run 'start-post' task: %m", UNIT(s)->id);
+ log_unit_warning_errno(UNIT(s), r, "Failed to run 'start-post' task: %m");
service_enter_stop(s, SERVICE_FAILURE_RESOURCES);
}
@@ -1572,7 +1674,7 @@ static void service_enter_start(Service *s) {
return;
fail:
- log_unit_warning_errno(UNIT(s)->id, r, "%s failed to run 'start' task: %m", UNIT(s)->id);
+ log_unit_warning_errno(UNIT(s), r, "Failed to run 'start' task: %m");
service_enter_signal(s, SERVICE_FINAL_SIGTERM, SERVICE_FAILURE_RESOURCES);
}
@@ -1610,7 +1712,7 @@ static void service_enter_start_pre(Service *s) {
return;
fail:
- log_unit_warning_errno(UNIT(s)->id, r, "%s failed to run 'start-pre' task: %m", UNIT(s)->id);
+ log_unit_warning_errno(UNIT(s), r, "Failed to run 'start-pre' task: %m");
service_enter_dead(s, SERVICE_FAILURE_RESOURCES, true);
}
@@ -1622,7 +1724,7 @@ static void service_enter_restart(Service *s) {
if (UNIT(s)->job && UNIT(s)->job->type == JOB_STOP) {
/* Don't restart things if we are going down anyway */
- log_unit_info(UNIT(s)->id, "Stop job pending for unit, delaying automatic restart.");
+ log_unit_info(UNIT(s), "Stop job pending for unit, delaying automatic restart.");
r = service_arm_timer(s, s->restart_usec);
if (r < 0)
@@ -1643,11 +1745,11 @@ static void service_enter_restart(Service *s) {
* it will be canceled as part of the service_stop() call that
* is executed as part of JOB_RESTART. */
- log_unit_debug(UNIT(s)->id, "%s scheduled restart job.", UNIT(s)->id);
+ log_unit_debug(UNIT(s), "Scheduled restart job.");
return;
fail:
- log_unit_warning(UNIT(s)->id, "%s failed to schedule restart job: %s", UNIT(s)->id, bus_error_message(&error, -r));
+ log_unit_warning(UNIT(s), "Failed to schedule restart job: %s", bus_error_message(&error, -r));
service_enter_dead(s, SERVICE_FAILURE_RESOURCES, false);
}
@@ -1690,7 +1792,7 @@ static void service_enter_reload(Service *s) {
return;
fail:
- log_unit_warning_errno(UNIT(s)->id, r, "%s failed to run 'reload' task: %m", UNIT(s)->id);
+ log_unit_warning_errno(UNIT(s), r, "Failed to run 'reload' task: %m");
s->reload_result = SERVICE_FAILURE_RESOURCES;
service_enter_running(s, SERVICE_SUCCESS);
}
@@ -1723,7 +1825,7 @@ static void service_run_next_control(Service *s) {
return;
fail:
- log_unit_warning_errno(UNIT(s)->id, r, "%s failed to run next control task: %m", UNIT(s)->id);
+ log_unit_warning_errno(UNIT(s), r, "Failed to run next control task: %m");
if (s->state == SERVICE_START_PRE)
service_enter_signal(s, SERVICE_FINAL_SIGTERM, SERVICE_FAILURE_RESOURCES);
@@ -1767,7 +1869,7 @@ static void service_run_next_main(Service *s) {
return;
fail:
- log_unit_warning_errno(UNIT(s)->id, r, "%s failed to run next main task: %m", UNIT(s)->id);
+ log_unit_warning_errno(UNIT(s), r, "Failed to run next main task: %m");
service_enter_stop(s, SERVICE_FAILURE_RESOURCES);
}
@@ -1777,7 +1879,7 @@ static int service_start_limit_test(Service *s) {
if (ratelimit_test(&s->start_limit))
return 0;
- log_unit_warning(UNIT(s)->id, "start request repeated too quickly for %s", UNIT(s)->id);
+ log_unit_warning(UNIT(s), "Start request repeated too quickly.");
return failure_action(UNIT(s)->manager, s->start_limit_action, s->reboot_arg);
}
@@ -1790,19 +1892,13 @@ static int service_start(Unit *u) {
/* We cannot fulfill this request right now, try again later
* please! */
- if (s->state == SERVICE_STOP ||
- s->state == SERVICE_STOP_SIGABRT ||
- s->state == SERVICE_STOP_SIGTERM ||
- s->state == SERVICE_STOP_SIGKILL ||
- s->state == SERVICE_STOP_POST ||
- s->state == SERVICE_FINAL_SIGTERM ||
- s->state == SERVICE_FINAL_SIGKILL)
+ if (IN_SET(s->state,
+ SERVICE_STOP, SERVICE_STOP_SIGABRT, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
+ SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL))
return -EAGAIN;
/* Already on it! */
- if (s->state == SERVICE_START_PRE ||
- s->state == SERVICE_START ||
- s->state == SERVICE_START_POST)
+ if (IN_SET(s->state, SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST))
return 0;
/* A service that will be restarted must be stopped first to
@@ -1815,7 +1911,7 @@ static int service_start(Unit *u) {
if (s->state == SERVICE_AUTO_RESTART)
return -EAGAIN;
- assert(s->state == SERVICE_DEAD || s->state == SERVICE_FAILED);
+ assert(IN_SET(s->state, SERVICE_DEAD, SERVICE_FAILED));
/* Make sure we don't enter a busy loop of some kind. */
r = service_start_limit_test(s);
@@ -1829,6 +1925,7 @@ static int service_start(Unit *u) {
s->main_pid_known = false;
s->main_pid_alien = false;
s->forbid_restart = false;
+ s->reset_cpu_usage = true;
free(s->status_text);
s->status_text = NULL;
@@ -1845,17 +1942,13 @@ static int service_stop(Unit *u) {
assert(s);
- /* Don't create restart jobs from here. */
+ /* Don't create restart jobs from manual stops. */
s->forbid_restart = true;
/* Already on it */
- if (s->state == SERVICE_STOP ||
- s->state == SERVICE_STOP_SIGABRT ||
- s->state == SERVICE_STOP_SIGTERM ||
- s->state == SERVICE_STOP_SIGKILL ||
- s->state == SERVICE_STOP_POST ||
- s->state == SERVICE_FINAL_SIGTERM ||
- s->state == SERVICE_FINAL_SIGKILL)
+ if (IN_SET(s->state,
+ SERVICE_STOP, SERVICE_STOP_SIGABRT, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
+ SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL))
return 0;
/* A restart will be scheduled or is in progress. */
@@ -1866,16 +1959,12 @@ static int service_stop(Unit *u) {
/* If there's already something running we go directly into
* kill mode. */
- if (s->state == SERVICE_START_PRE ||
- s->state == SERVICE_START ||
- s->state == SERVICE_START_POST ||
- s->state == SERVICE_RELOAD) {
+ if (IN_SET(s->state, SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST, SERVICE_RELOAD)) {
service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_SUCCESS);
return 0;
}
- assert(s->state == SERVICE_RUNNING ||
- s->state == SERVICE_EXITED);
+ assert(IN_SET(s->state, SERVICE_RUNNING, SERVICE_EXITED));
service_enter_stop(s, SERVICE_SUCCESS);
return 1;
@@ -1913,23 +2002,25 @@ 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", PID_FMT,
- s->control_pid);
+ unit_serialize_item_format(u, f, "control-pid", PID_FMT, s->control_pid);
if (s->main_pid_known && s->main_pid > 0)
unit_serialize_item_format(u, f, "main-pid", PID_FMT, s->main_pid);
unit_serialize_item(u, f, "main-pid-known", yes_no(s->main_pid_known));
- if (s->status_text)
- unit_serialize_item(u, f, "status-text", s->status_text);
+ if (s->status_text) {
+ _cleanup_free_ char *c = NULL;
+
+ c = cescape(s->status_text);
+ unit_serialize_item(u, f, "status-text", strempty(c));
+ }
/* FIXME: There's a minor uncleanliness here: if there are
* multiple commands attached here, we will start from the
* first one again */
if (s->control_command_id >= 0)
- unit_serialize_item(u, f, "control-command",
- service_exec_command_to_string(s->control_command_id));
+ unit_serialize_item(u, f, "control-command", service_exec_command_to_string(s->control_command_id));
if (s->socket_fd >= 0) {
int copy;
@@ -1962,20 +2053,16 @@ 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", PID_FMT,
- 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", PID_FMT, 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);
@@ -1999,7 +2086,7 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value,
state = service_state_from_string(value);
if (state < 0)
- log_unit_debug(u->id, "Failed to parse state value %s", value);
+ log_unit_debug(u, "Failed to parse state value: %s", value);
else
s->deserialized_state = state;
} else if (streq(key, "result")) {
@@ -2007,7 +2094,7 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value,
f = service_result_from_string(value);
if (f < 0)
- log_unit_debug(u->id, "Failed to parse result value %s", value);
+ log_unit_debug(u, "Failed to parse result value: %s", value);
else if (f != SERVICE_SUCCESS)
s->result = f;
@@ -2016,7 +2103,7 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value,
f = service_result_from_string(value);
if (f < 0)
- log_unit_debug(u->id, "Failed to parse reload result value %s", value);
+ log_unit_debug(u, "Failed to parse reload result value: %s", value);
else if (f != SERVICE_SUCCESS)
s->reload_result = f;
@@ -2024,14 +2111,14 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value,
pid_t pid;
if (parse_pid(value, &pid) < 0)
- log_unit_debug(u->id, "Failed to parse control-pid value %s", value);
+ log_unit_debug(u, "Failed to parse control-pid value: %s", value);
else
s->control_pid = pid;
} else if (streq(key, "main-pid")) {
pid_t pid;
if (parse_pid(value, &pid) < 0)
- log_unit_debug(u->id, "Failed to parse main-pid value %s", value);
+ log_unit_debug(u, "Failed to parse main-pid value: %s", value);
else {
service_set_main_pid(s, pid);
unit_watch_pid(UNIT(s), pid);
@@ -2041,15 +2128,15 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value,
b = parse_boolean(value);
if (b < 0)
- log_unit_debug(u->id, "Failed to parse main-pid-known value %s", value);
+ log_unit_debug(u, "Failed to parse main-pid-known value: %s", value);
else
s->main_pid_known = b;
} else if (streq(key, "status-text")) {
char *t;
- t = strdup(value);
- if (!t)
- log_oom();
+ r = cunescape(value, 0, &t);
+ if (r < 0)
+ log_unit_debug_errno(u, r, "Failed to unescape status text: %s", value);
else {
free(s->status_text);
s->status_text = t;
@@ -2060,7 +2147,7 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value,
id = service_exec_command_from_string(value);
if (id < 0)
- log_unit_debug(u->id, "Failed to parse exec-command value %s", value);
+ log_unit_debug(u, "Failed to parse exec-command value: %s", value);
else {
s->control_command_id = id;
s->control_command = s->exec_command[id];
@@ -2069,7 +2156,7 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value,
int fd;
if (safe_atoi(value, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd))
- log_unit_debug(u->id, "Failed to parse socket-fd value %s", value);
+ log_unit_debug(u, "Failed to parse socket-fd value: %s", value);
else {
asynchronous_close(s->socket_fd);
s->socket_fd = fdset_remove(fds, fd);
@@ -2078,7 +2165,7 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value,
int fd;
if (safe_atoi(value, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd))
- log_unit_debug(u->id, "Failed to parse endpoint-fd value %s", value);
+ log_unit_debug(u, "Failed to parse endpoint-fd value: %s", value);
else {
safe_close(s->bus_endpoint_fd);
s->bus_endpoint_fd = fdset_remove(fds, fd);
@@ -2087,11 +2174,11 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value,
int fd;
if (safe_atoi(value, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd))
- log_unit_debug(u->id, "Failed to parse fd-store-fd value %s", value);
+ log_unit_debug(u, "Failed to parse fd-store-fd value: %s", value);
else {
r = service_add_fd_store(s, fd);
if (r < 0)
- log_unit_error_errno(u->id, r, "Failed to add fd to store: %m");
+ log_unit_error_errno(u, r, "Failed to add fd to store: %m");
else if (r > 0)
fdset_remove(fds, fd);
}
@@ -2100,21 +2187,21 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value,
pid_t pid;
if (parse_pid(value, &pid) < 0)
- log_unit_debug(u->id, "Failed to parse main-exec-status-pid value %s", value);
+ log_unit_debug(u, "Failed to parse main-exec-status-pid value: %s", value);
else
s->main_exec_status.pid = pid;
} else if (streq(key, "main-exec-status-code")) {
int i;
if (safe_atoi(value, &i) < 0)
- log_unit_debug(u->id, "Failed to parse main-exec-status-code value %s", value);
+ log_unit_debug(u, "Failed to parse main-exec-status-code value: %s", value);
else
s->main_exec_status.code = i;
} else if (streq(key, "main-exec-status-status")) {
int i;
if (safe_atoi(value, &i) < 0)
- log_unit_debug(u->id, "Failed to parse main-exec-status-status value %s", value);
+ log_unit_debug(u, "Failed to parse main-exec-status-status value: %s", value);
else
s->main_exec_status.status = i;
} else if (streq(key, "main-exec-status-start"))
@@ -2128,11 +2215,11 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value,
b = parse_boolean(value);
if (b < 0)
- log_unit_debug(u->id, "Failed to parse forbid-restart value %s", value);
+ log_unit_debug(u, "Failed to parse forbid-restart value: %s", value);
else
s->forbid_restart = b;
} else
- log_unit_debug(u->id, "Unknown serialization key '%s'", key);
+ log_unit_debug(u, "Unknown serialization key: %s", key);
return 0;
}
@@ -2195,19 +2282,19 @@ static int service_retry_pid_file(Service *s) {
static int service_watch_pid_file(Service *s) {
int r;
- log_unit_debug(UNIT(s)->id, "Setting watch for %s's PID file %s", UNIT(s)->id, s->pid_file_pathspec->path);
+ log_unit_debug(UNIT(s), "Setting watch for PID file %s", s->pid_file_pathspec->path);
r = path_spec_watch(s->pid_file_pathspec, service_dispatch_io);
if (r < 0)
goto fail;
/* the pidfile might have appeared just before we set the watch */
- log_unit_debug(UNIT(s)->id, "Trying to read %s's PID file %s in case it changed", UNIT(s)->id, s->pid_file_pathspec->path);
+ log_unit_debug(UNIT(s), "Trying to read PID file %s in case it changed", s->pid_file_pathspec->path);
service_retry_pid_file(s);
return 0;
fail:
- log_unit_error_errno(UNIT(s)->id, r, "Failed to set a watch for %s's PID file %s: %m", UNIT(s)->id, s->pid_file_pathspec->path);
+ log_unit_error_errno(UNIT(s), r, "Failed to set a watch for PID file %s: %m", s->pid_file_pathspec->path);
service_unwatch_pid_file(s);
return r;
}
@@ -2255,7 +2342,7 @@ static int service_dispatch_io(sd_event_source *source, int fd, uint32_t events,
assert(s->pid_file_pathspec);
assert(path_spec_owns_inotify_fd(s->pid_file_pathspec, fd));
- log_unit_debug(UNIT(s)->id, "inotify event for %s", UNIT(s)->id);
+ log_unit_debug(UNIT(s), "inotify event");
if (path_spec_fd_event(p, events) < 0)
goto fail;
@@ -2279,7 +2366,7 @@ static void service_notify_cgroup_empty_event(Unit *u) {
assert(u);
- log_unit_debug(u->id, "%s: cgroup is empty", u->id);
+ log_unit_debug(u, "cgroup is empty");
switch (s->state) {
@@ -2294,7 +2381,7 @@ static void service_notify_cgroup_empty_event(Unit *u) {
/* If we were hoping for the daemon to write its PID file,
* we can give up now. */
if (s->pid_file_pathspec) {
- log_unit_warning(u->id, "%s never wrote its PID file. Failing.", UNIT(s)->id);
+ log_unit_warning(u, "Daemon never wrote its PID file. Failing.");
service_unwatch_pid_file(s);
if (s->state == SERVICE_START)
@@ -2381,16 +2468,16 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
f = SERVICE_SUCCESS;
}
- log_unit_struct(u->id,
- f == SERVICE_SUCCESS ? LOG_DEBUG : LOG_NOTICE,
- LOG_MESSAGE("%s: main process exited, code=%s, status=%i/%s",
- u->id, sigchld_code_to_string(code), status,
+ log_struct(f == SERVICE_SUCCESS ? LOG_DEBUG : LOG_NOTICE,
+ LOG_UNIT_ID(u),
+ LOG_UNIT_MESSAGE(u, "Main process exited, code=%s, status=%i/%s",
+ sigchld_code_to_string(code), status,
strna(code == CLD_EXITED
? exit_status_to_string(status, EXIT_STATUS_FULL)
: signal_to_string(status))),
- "EXIT_CODE=%s", sigchld_code_to_string(code),
- "EXIT_STATUS=%i", status,
- NULL);
+ "EXIT_CODE=%s", sigchld_code_to_string(code),
+ "EXIT_STATUS=%i", status,
+ NULL);
if (f != SERVICE_SUCCESS)
s->result = f;
@@ -2402,7 +2489,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
/* There is another command to *
* execute, so let's do that. */
- log_unit_debug(u->id, "%s running next main command for state %s", u->id, service_state_to_string(s->state));
+ log_unit_debug(u, "Running next main command for state %s.", service_state_to_string(s->state));
service_run_next_main(s);
} else {
@@ -2469,10 +2556,9 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
f = SERVICE_SUCCESS;
}
- log_unit_full(u->id,
- f == SERVICE_SUCCESS ? LOG_DEBUG : LOG_NOTICE,
- "%s: control process exited, code=%s status=%i",
- u->id, sigchld_code_to_string(code), status);
+ log_unit_full(u, f == SERVICE_SUCCESS ? LOG_DEBUG : LOG_NOTICE, 0,
+ "Control process exited, code=%s status=%i",
+ sigchld_code_to_string(code), status);
if (f != SERVICE_SUCCESS)
s->result = f;
@@ -2489,7 +2575,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
/* There is another command to *
* execute, so let's do that. */
- log_unit_debug(u->id, "%s running next control command for state %s", u->id, service_state_to_string(s->state));
+ log_unit_debug(u, "Running next control command for state %s.", service_state_to_string(s->state));
service_run_next_control(s);
} else {
@@ -2499,7 +2585,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
s->control_command = NULL;
s->control_command_id = _SERVICE_EXEC_COMMAND_INVALID;
- log_unit_debug(u->id, "%s got final SIGCHLD for state %s", u->id, service_state_to_string(s->state));
+ log_unit_debug(u, "Got final SIGCHLD for state %s.", service_state_to_string(s->state));
switch (s->state) {
@@ -2628,38 +2714,37 @@ static int service_dispatch_timer(sd_event_source *source, usec_t usec, void *us
case SERVICE_START_PRE:
case SERVICE_START:
- log_unit_warning(UNIT(s)->id, "%s %s operation timed out. Terminating.", UNIT(s)->id, s->state == SERVICE_START ? "start" : "start-pre");
+ log_unit_warning(UNIT(s), "%s operation timed out. Terminating.", s->state == SERVICE_START ? "Start" : "Start-pre");
service_enter_signal(s, SERVICE_FINAL_SIGTERM, SERVICE_FAILURE_TIMEOUT);
break;
case SERVICE_START_POST:
- log_unit_warning(UNIT(s)->id, "%s start-post operation timed out. Stopping.", UNIT(s)->id);
+ log_unit_warning(UNIT(s), "Start-post operation timed out. Stopping.");
service_enter_stop(s, SERVICE_FAILURE_TIMEOUT);
break;
case SERVICE_RELOAD:
- log_unit_warning(UNIT(s)->id, "%s reload operation timed out. Stopping.", UNIT(s)->id);
+ log_unit_warning(UNIT(s), "Reload operation timed out. Stopping.");
s->reload_result = SERVICE_FAILURE_TIMEOUT;
service_enter_running(s, SERVICE_SUCCESS);
break;
case SERVICE_STOP:
- log_unit_warning(UNIT(s)->id, "%s stopping timed out. Terminating.", UNIT(s)->id);
+ log_unit_warning(UNIT(s), "Stopping timed out. Terminating.");
service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_FAILURE_TIMEOUT);
break;
case SERVICE_STOP_SIGABRT:
- log_unit_warning(UNIT(s)->id,
- "%s stop-sigabrt timed out. Terminating.", UNIT(s)->id);
- service_enter_signal(s, SERVICE_STOP_SIGTERM, s->result);
+ log_unit_warning(UNIT(s), "State 'stop-sigabrt' timed out. Terminating.");
+ service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_FAILURE_TIMEOUT);
break;
case SERVICE_STOP_SIGTERM:
if (s->kill_context.send_sigkill) {
- log_unit_warning(UNIT(s)->id, "%s stop-sigterm timed out. Killing.", UNIT(s)->id);
+ log_unit_warning(UNIT(s), "State 'stop-sigterm' timed out. Killing.");
service_enter_signal(s, SERVICE_STOP_SIGKILL, SERVICE_FAILURE_TIMEOUT);
} else {
- log_unit_warning(UNIT(s)->id, "%s stop-sigterm timed out. Skipping SIGKILL.", UNIT(s)->id);
+ log_unit_warning(UNIT(s), "State 'stop-sigterm' timed out. Skipping SIGKILL.");
service_enter_stop_post(s, SERVICE_FAILURE_TIMEOUT);
}
@@ -2670,37 +2755,36 @@ static int service_dispatch_timer(sd_event_source *source, usec_t usec, void *us
* Must be something we cannot kill, so let's just be
* weirded out and continue */
- log_unit_warning(UNIT(s)->id, "%s still around after SIGKILL. Ignoring.", UNIT(s)->id);
+ log_unit_warning(UNIT(s), "Processes still around after SIGKILL. Ignoring.");
service_enter_stop_post(s, SERVICE_FAILURE_TIMEOUT);
break;
case SERVICE_STOP_POST:
- log_unit_warning(UNIT(s)->id, "%s stop-post timed out. Terminating.", UNIT(s)->id);
+ log_unit_warning(UNIT(s), "State 'stop-post' timed out. Terminating.");
service_enter_signal(s, SERVICE_FINAL_SIGTERM, SERVICE_FAILURE_TIMEOUT);
break;
case SERVICE_FINAL_SIGTERM:
if (s->kill_context.send_sigkill) {
- log_unit_warning(UNIT(s)->id, "%s stop-final-sigterm timed out. Killing.", UNIT(s)->id);
+ log_unit_warning(UNIT(s), "State 'stop-final-sigterm' timed out. Killing.");
service_enter_signal(s, SERVICE_FINAL_SIGKILL, SERVICE_FAILURE_TIMEOUT);
} else {
- log_unit_warning(UNIT(s)->id, "%s stop-final-sigterm timed out. Skipping SIGKILL. Entering failed mode.", UNIT(s)->id);
+ log_unit_warning(UNIT(s), "State 'stop-final-sigterm' timed out. Skipping SIGKILL. Entering failed mode.");
service_enter_dead(s, SERVICE_FAILURE_TIMEOUT, false);
}
break;
case SERVICE_FINAL_SIGKILL:
- log_unit_warning(UNIT(s)->id, "%s still around after final SIGKILL. Entering failed mode.", UNIT(s)->id);
+ log_unit_warning(UNIT(s), "Processes still around after final SIGKILL. Entering failed mode.");
service_enter_dead(s, SERVICE_FAILURE_TIMEOUT, true);
break;
case SERVICE_AUTO_RESTART:
- log_unit_info(UNIT(s)->id,
+ log_unit_info(UNIT(s),
s->restart_usec > 0 ?
- "%s holdoff time over, scheduling restart." :
- "%s has no holdoff time, scheduling restart.",
- UNIT(s)->id);
+ "Service hold-off time over, scheduling restart." :
+ "Service has no hold-off time, scheduling restart.");
service_enter_restart(s);
break;
@@ -2718,7 +2802,7 @@ static int service_dispatch_watchdog(sd_event_source *source, usec_t usec, void
assert(s);
assert(source == s->watchdog_event_source);
- log_unit_error(UNIT(s)->id, "%s watchdog timeout (limit %s)!", UNIT(s)->id,
+ log_unit_error(UNIT(s), "Watchdog timeout (limit %s)!",
format_timespan(t, sizeof(t), s->watchdog_usec, 1));
service_enter_signal(s, SERVICE_STOP_SIGABRT, SERVICE_FAILURE_WATCHDOG);
@@ -2735,30 +2819,25 @@ static void service_notify_message(Unit *u, pid_t pid, char **tags, FDSet *fds)
assert(u);
cc = strv_join(tags, ", ");
- log_unit_debug(u->id, "%s: Got notification message from PID "PID_FMT" (%s)",
- u->id, pid, isempty(cc) ? "n/a" : cc);
if (s->notify_access == NOTIFY_NONE) {
- log_unit_warning(u->id, "%s: Got notification message from PID "PID_FMT", but reception is disabled.", u->id, pid);
+ log_unit_warning(u, "Got notification message from PID "PID_FMT", but reception is disabled.", pid);
return;
- }
-
- if (s->notify_access == NOTIFY_MAIN && pid != s->main_pid) {
+ } else if (s->notify_access == NOTIFY_MAIN && pid != s->main_pid) {
if (s->main_pid != 0)
- log_unit_warning(u->id, "%s: Got notification message from PID "PID_FMT", but reception only permitted for main PID "PID_FMT, u->id, pid, s->main_pid);
+ log_unit_warning(u, "Got notification message from PID "PID_FMT", but reception only permitted for main PID "PID_FMT, pid, s->main_pid);
else
- log_unit_debug(u->id, "%s: Got notification message from PID "PID_FMT", but reception only permitted for main PID which is currently not known", u->id, pid);
+ log_unit_debug(u, "Got notification message from PID "PID_FMT", but reception only permitted for main PID which is currently not known", pid);
return;
- }
+ } else
+ log_unit_debug(u, "Got notification message from PID "PID_FMT" (%s)", pid, isempty(cc) ? "n/a" : cc);
/* Interpret MAINPID= */
e = strv_find_startswith(tags, "MAINPID=");
if (e && IN_SET(s->state, SERVICE_START, SERVICE_START_POST, SERVICE_RUNNING, SERVICE_RELOAD)) {
if (parse_pid(e, &pid) < 0)
- log_unit_warning(u->id, "Failed to parse MAINPID= field in notification message: %s", e);
+ log_unit_warning(u, "Failed to parse MAINPID= field in notification message: %s", e);
else {
- log_unit_debug(u->id, "%s: got MAINPID=%s", u->id, e);
-
service_set_main_pid(s, pid);
unit_watch_pid(UNIT(s), pid);
notify_dbus = true;
@@ -2768,7 +2847,6 @@ static void service_notify_message(Unit *u, pid_t pid, char **tags, FDSet *fds)
/* Interpret RELOADING= */
if (strv_find(tags, "RELOADING=1")) {
- log_unit_debug(u->id, "%s: got RELOADING=1", u->id);
s->notify_state = NOTIFY_RELOADING;
if (s->state == SERVICE_RUNNING)
@@ -2780,7 +2858,6 @@ static void service_notify_message(Unit *u, pid_t pid, char **tags, FDSet *fds)
/* Interpret READY= */
if (strv_find(tags, "READY=1")) {
- log_unit_debug(u->id, "%s: got READY=1", u->id);
s->notify_state = NOTIFY_READY;
/* Type=notify services inform us about completed
@@ -2799,7 +2876,6 @@ static void service_notify_message(Unit *u, pid_t pid, char **tags, FDSet *fds)
/* Interpret STOPPING= */
if (strv_find(tags, "STOPPING=1")) {
- log_unit_debug(u->id, "%s: got STOPPING=1", u->id);
s->notify_state = NOTIFY_STOPPING;
if (s->state == SERVICE_RUNNING)
@@ -2815,10 +2891,8 @@ static void service_notify_message(Unit *u, pid_t pid, char **tags, FDSet *fds)
if (!isempty(e)) {
if (!utf8_is_valid(e))
- log_unit_warning(u->id, "Status message in notification is not UTF-8 clean.");
+ log_unit_warning(u, "Status message in notification message is not UTF-8 clean.");
else {
- log_unit_debug(u->id, "%s: got STATUS=%s", u->id, e);
-
t = strdup(e);
if (!t)
log_oom();
@@ -2841,10 +2915,8 @@ static void service_notify_message(Unit *u, pid_t pid, char **tags, FDSet *fds)
int status_errno;
if (safe_atoi(e, &status_errno) < 0 || status_errno < 0)
- log_unit_warning(u->id, "Failed to parse ERRNO= field in notification message: %s", e);
+ log_unit_warning(u, "Failed to parse ERRNO= field in notification message: %s", e);
else {
- log_unit_debug(u->id, "%s: got ERRNO=%s", u->id, e);
-
if (s->status_errno != status_errno) {
s->status_errno = status_errno;
notify_dbus = true;
@@ -2854,13 +2926,11 @@ static void service_notify_message(Unit *u, pid_t pid, char **tags, FDSet *fds)
/* Interpret WATCHDOG= */
if (strv_find(tags, "WATCHDOG=1")) {
- log_unit_debug(u->id, "%s: got WATCHDOG=1", u->id);
service_reset_watchdog(s);
}
/* Add the passed fds to the fd store */
if (strv_find(tags, "FDSTORE=1")) {
- log_unit_debug(u->id, "%s: got FDSTORE=1", u->id);
service_add_fd_store_set(s, fds);
}
@@ -2899,11 +2969,11 @@ static void service_bus_name_owner_change(
assert(old_owner || new_owner);
if (old_owner && new_owner)
- log_unit_debug(u->id, "%s's D-Bus name %s changed owner from %s to %s", u->id, name, old_owner, new_owner);
+ log_unit_debug(u, "D-Bus name %s changed owner from %s to %s", name, old_owner, new_owner);
else if (old_owner)
- log_unit_debug(u->id, "%s's D-Bus name %s no longer registered by %s", u->id, name, old_owner);
+ log_unit_debug(u, "D-Bus name %s no longer registered by %s", name, old_owner);
else
- log_unit_debug(u->id, "%s's D-Bus name %s now registered by %s", u->id, name, new_owner);
+ log_unit_debug(u, "D-Bus name %s now registered by %s", name, new_owner);
s->bus_name_good = !!new_owner;
@@ -2932,7 +3002,7 @@ static void service_bus_name_owner_change(
if (r >= 0)
r = sd_bus_creds_get_pid(creds, &pid);
if (r >= 0) {
- log_unit_debug(u->id, "%s's D-Bus name %s is now owned by process %u", u->id, name, (unsigned) pid);
+ log_unit_debug(u, "D-Bus name %s is now owned by process %u", name, (unsigned) pid);
service_set_main_pid(s, pid);
unit_watch_pid(UNIT(s), pid);
diff --git a/src/core/service.h b/src/core/service.h
index dfeee6a68c..7da0a93961 100644
--- a/src/core/service.h
+++ b/src/core/service.h
@@ -24,12 +24,10 @@
typedef struct Service Service;
typedef struct ServiceFDStore ServiceFDStore;
-#include "unit.h"
#include "path.h"
#include "ratelimit.h"
#include "kill.h"
#include "exit-status.h"
-#include "failure-action.h"
typedef enum ServiceState {
SERVICE_DEAD,
@@ -191,6 +189,8 @@ struct Service {
bool forbid_restart:1;
bool start_timeout_defined:1;
+ bool reset_cpu_usage:1;
+
char *bus_name;
char *status_text;
@@ -216,8 +216,6 @@ struct Service {
extern const UnitVTable service_vtable;
-struct Socket;
-
int service_set_socket_fd(Service *s, int fd, struct Socket *socket, bool selinux_context_net);
const char* service_state_to_string(ServiceState i) _const_;
diff --git a/src/core/shutdown.c b/src/core/shutdown.c
index 71f001ac13..aba16b4689 100644
--- a/src/core/shutdown.c
+++ b/src/core/shutdown.c
@@ -20,21 +20,15 @@
***/
#include <sys/mman.h>
-#include <sys/types.h>
#include <sys/reboot.h>
#include <linux/reboot.h>
-#include <sys/wait.h>
#include <sys/stat.h>
#include <sys/mount.h>
-#include <sys/syscall.h>
-#include <fcntl.h>
-#include <dirent.h>
#include <errno.h>
#include <unistd.h>
#include <signal.h>
#include <stdbool.h>
#include <stdlib.h>
-#include <string.h>
#include <getopt.h>
#include "missing.h"
@@ -42,14 +36,14 @@
#include "fileio.h"
#include "umount.h"
#include "util.h"
-#include "mkdir.h"
#include "virt.h"
#include "watchdog.h"
#include "killall.h"
#include "cgroup-util.h"
#include "def.h"
#include "switch-root.h"
-#include "strv.h"
+#include "process-util.h"
+#include "terminal-util.h"
#define FINALIZE_ATTEMPTS 50
diff --git a/src/core/slice.c b/src/core/slice.c
index ae9819d015..e52bf71515 100644
--- a/src/core/slice.c
+++ b/src/core/slice.c
@@ -20,12 +20,9 @@
***/
#include <errno.h>
-#include <signal.h>
-#include <unistd.h>
#include "unit.h"
#include "slice.h"
-#include "load-fragment.h"
#include "log.h"
#include "dbus-slice.h"
#include "special.h"
@@ -96,27 +93,28 @@ static int slice_add_default_dependencies(Slice *s) {
return 0;
}
+
static int slice_verify(Slice *s) {
+ _cleanup_free_ char *parent = NULL;
+ int r;
+
assert(s);
if (UNIT(s)->load_state != UNIT_LOADED)
return 0;
- if (UNIT_DEREF(UNIT(s)->slice)) {
- char *a, *dash;
+ if (!slice_name_is_valid(UNIT(s)->id)) {
+ log_unit_error(UNIT(s), "Slice name %s is not valid. Refusing.", UNIT(s)->id);
+ return -EINVAL;
+ }
- a = strdupa(UNIT(s)->id);
- dash = strrchr(a, '-');
- if (dash)
- strcpy(dash, ".slice");
- else
- a = (char*) SPECIAL_ROOT_SLICE;
+ r = slice_build_parent_slice(UNIT(s)->id, &parent);
+ if (r < 0)
+ return log_unit_error_errno(UNIT(s), r, "Failed to determine parent slice: %m");
- if (!unit_has_name(UNIT_DEREF(UNIT(s)->slice), a)) {
- log_unit_error(UNIT(s)->id,
- "%s located outside its parent slice. Refusing.", UNIT(s)->id);
- return -EINVAL;
- }
+ if (parent ? !unit_has_name(UNIT_DEREF(UNIT(s)->slice), parent) : UNIT_ISSET(UNIT(s)->slice)) {
+ log_unit_error(UNIT(s), "Located outside of parent slice. Refusing.");
+ return -EINVAL;
}
return 0;
@@ -184,7 +182,8 @@ static int slice_start(Unit *u) {
assert(t);
assert(t->state == SLICE_DEAD);
- unit_realize_cgroup(u);
+ (void) unit_realize_cgroup(u);
+ (void) unit_reset_cpu_usage(u);
slice_set_state(t, SLICE_ACTIVE);
return 1;
diff --git a/src/core/slice.h b/src/core/slice.h
index ad0c63902b..ac648e56f8 100644
--- a/src/core/slice.h
+++ b/src/core/slice.h
@@ -23,7 +23,6 @@
typedef struct Slice Slice;
-#include "unit.h"
typedef enum SliceState {
SLICE_DEAD,
diff --git a/src/core/smack-setup.c b/src/core/smack-setup.c
index 59f6832bc2..ff2a02004d 100644
--- a/src/core/smack-setup.c
+++ b/src/core/smack-setup.c
@@ -24,21 +24,15 @@
#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 "fileio.h"
#include "log.h"
-#include "label.h"
#define SMACK_CONFIG "/etc/smack/accesses.d/"
#define CIPSO_CONFIG "/etc/smack/cipso.d/"
diff --git a/src/core/snapshot.c b/src/core/snapshot.c
index b70c3beb60..1e634b9bc1 100644
--- a/src/core/snapshot.c
+++ b/src/core/snapshot.c
@@ -51,11 +51,7 @@ static void snapshot_set_state(Snapshot *s, SnapshotState state) {
s->state = state;
if (state != old_state)
- log_unit_debug(UNIT(s)->id,
- "%s changed %s -> %s",
- UNIT(s)->id,
- snapshot_state_to_string(old_state),
- snapshot_state_to_string(state));
+ log_unit_debug(UNIT(s), "Changed %s -> %s", snapshot_state_to_string(old_state), snapshot_state_to_string(state));
unit_notify(UNIT(s), state_translation_table[old_state], state_translation_table[state], true);
}
@@ -155,7 +151,7 @@ static int snapshot_deserialize_item(Unit *u, const char *key, const char *value
state = snapshot_state_from_string(value);
if (state < 0)
- log_unit_debug(u->id, "Failed to parse state value %s", value);
+ log_unit_debug(u, "Failed to parse state value: %s", value);
else
s->deserialized_state = state;
@@ -163,7 +159,7 @@ static int snapshot_deserialize_item(Unit *u, const char *key, const char *value
r = parse_boolean(value);
if (r < 0)
- log_unit_debug(u->id, "Failed to parse cleanup value %s", value);
+ log_unit_debug(u, "Failed to parse cleanup value: %s", value);
else
s->cleanup = r;
@@ -173,7 +169,7 @@ static int snapshot_deserialize_item(Unit *u, const char *key, const char *value
if (r < 0)
return r;
} else
- log_unit_debug(u->id, "Unknown serialization key '%s'", key);
+ log_unit_debug(u, "Unknown serialization key: %s", key);
return 0;
}
@@ -201,10 +197,10 @@ int snapshot_create(Manager *m, const char *name, bool cleanup, sd_bus_error *e,
assert(_s);
if (name) {
- if (!unit_name_is_valid(name, TEMPLATE_INVALID))
+ if (!unit_name_is_valid(name, UNIT_NAME_PLAIN))
return sd_bus_error_setf(e, SD_BUS_ERROR_INVALID_ARGS, "Unit name %s is not valid.", name);
- if (unit_name_to_type(name) != UNIT_SNAPSHOT)
+ if (!endswith(name, ".snapshot"))
return sd_bus_error_setf(e, SD_BUS_ERROR_INVALID_ARGS, "Unit name %s lacks snapshot suffix.", name);
if (manager_get_unit(m, name))
@@ -258,7 +254,7 @@ int snapshot_create(Manager *m, const char *name, bool cleanup, sd_bus_error *e,
SNAPSHOT(u)->cleanup = cleanup;
*_s = SNAPSHOT(u);
- log_unit_info(u->id, "Created snapshot %s.", u->id);
+ log_unit_info(u, "Created snapshot.");
return 0;
@@ -272,7 +268,7 @@ fail:
void snapshot_remove(Snapshot *s) {
assert(s);
- log_unit_info(UNIT(s)->id, "Removing snapshot %s.", UNIT(s)->id);
+ log_unit_info(UNIT(s), "Removing snapshot.");
unit_add_to_cleanup_queue(UNIT(s));
}
diff --git a/src/core/snapshot.h b/src/core/snapshot.h
index e6dc661060..f2451b1193 100644
--- a/src/core/snapshot.h
+++ b/src/core/snapshot.h
@@ -23,7 +23,6 @@
typedef struct Snapshot Snapshot;
-#include "unit.h"
typedef enum SnapshotState {
SNAPSHOT_DEAD,
diff --git a/src/core/socket.c b/src/core/socket.c
index 48c43a2880..fc5eb1464a 100644
--- a/src/core/socket.c
+++ b/src/core/socket.c
@@ -19,7 +19,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
@@ -29,12 +28,9 @@
#include <arpa/inet.h>
#include <netinet/tcp.h>
#include <mqueue.h>
-#include <sys/xattr.h>
#include "sd-event.h"
#include "log.h"
-#include "load-dropin.h"
-#include "load-fragment.h"
#include "strv.h"
#include "mkdir.h"
#include "path-util.h"
@@ -51,6 +47,8 @@
#include "selinux-util.h"
#include "dbus-socket.h"
#include "unit.h"
+#include "formats-util.h"
+#include "signal-util.h"
#include "socket.h"
static const UnitActiveState state_translation_table[_SOCKET_STATE_MAX] = {
@@ -173,12 +171,18 @@ static int socket_arm_timer(Socket *s) {
return sd_event_source_set_enabled(s->timer_event_source, SD_EVENT_ONESHOT);
}
- return sd_event_add_time(
+ r = sd_event_add_time(
UNIT(s)->manager->event,
&s->timer_event_source,
CLOCK_MONOTONIC,
now(CLOCK_MONOTONIC) + s->timeout_usec, 0,
socket_dispatch_timer, s);
+ if (r < 0)
+ return r;
+
+ (void) sd_event_source_set_description(s->timer_event_source, "socket-timer");
+
+ return 0;
}
int socket_instantiate_service(Socket *s) {
@@ -193,12 +197,15 @@ int socket_instantiate_service(Socket *s) {
* here. For Accept=no this is mostly a NOP since the service
* is figured out at load time anyway. */
- if (UNIT_DEREF(s->service) || !s->accept)
+ if (UNIT_DEREF(s->service))
return 0;
- prefix = unit_name_to_prefix(UNIT(s)->id);
- if (!prefix)
- return -ENOMEM;
+ if (!s->accept)
+ return 0;
+
+ r = unit_name_to_prefix(UNIT(s)->id, &prefix);
+ if (r < 0)
+ return r;
if (asprintf(&name, "%s@%u.service", prefix, s->n_accepted) < 0)
return -ENOMEM;
@@ -278,7 +285,7 @@ static int socket_add_default_dependencies(Socket *s) {
if (r < 0)
return r;
- if (UNIT(s)->manager->running_as == SYSTEMD_SYSTEM) {
+ if (UNIT(s)->manager->running_as == MANAGER_SYSTEM) {
r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, NULL, true);
if (r < 0)
return r;
@@ -392,33 +399,32 @@ static int socket_verify(Socket *s) {
return 0;
if (!s->ports) {
- log_unit_error(UNIT(s)->id, "%s lacks Listen setting. Refusing.", UNIT(s)->id);
+ log_unit_error(UNIT(s), "Unit lacks Listen setting. Refusing.");
return -EINVAL;
}
if (s->accept && have_non_accept_socket(s)) {
- log_unit_error(UNIT(s)->id, "%s configured for accepting sockets, but sockets are non-accepting. Refusing.",
- UNIT(s)->id);
+ log_unit_error(UNIT(s), "Unit configured for accepting sockets, but sockets are non-accepting. Refusing.");
return -EINVAL;
}
if (s->accept && s->max_connections <= 0) {
- log_unit_error(UNIT(s)->id, "%s's MaxConnection setting too small. Refusing.", UNIT(s)->id);
+ log_unit_error(UNIT(s), "MaxConnection= setting too small. Refusing.");
return -EINVAL;
}
if (s->accept && UNIT_DEREF(s->service)) {
- log_unit_error(UNIT(s)->id, "Explicit service configuration for accepting sockets not supported on %s. Refusing.", UNIT(s)->id);
+ log_unit_error(UNIT(s), "Explicit service configuration for accepting socket units not supported. Refusing.");
return -EINVAL;
}
if (s->exec_context.pam_name && s->kill_context.kill_mode != KILL_CONTROL_GROUP) {
- log_unit_error(UNIT(s)->id, "%s has PAM enabled. Kill mode must be set to 'control-group'. Refusing.", UNIT(s)->id);
+ log_unit_error(UNIT(s), "Unit has PAM enabled. Kill mode must be set to 'control-group'. Refusing.");
return -EINVAL;
}
if (!strv_isempty(s->symlinks) && !socket_find_symlink_target(s)) {
- log_unit_error(UNIT(s)->id, "%s has symlinks set but none or more than one node in the file system. Refusing.", UNIT(s)->id);
+ log_unit_error(UNIT(s), "Unit has symlinks set but none or more than one node in the file system. Refusing.");
return -EINVAL;
}
@@ -816,60 +822,60 @@ static void socket_apply_socket_options(Socket *s, int fd) {
if (s->keep_alive) {
int b = s->keep_alive;
if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &b, sizeof(b)) < 0)
- log_unit_warning(UNIT(s)->id, "SO_KEEPALIVE failed: %m");
+ log_unit_warning_errno(UNIT(s), errno, "SO_KEEPALIVE failed: %m");
}
if (s->keep_alive_time) {
int value = s->keep_alive_time / USEC_PER_SEC;
if (setsockopt(fd, SOL_TCP, TCP_KEEPIDLE, &value, sizeof(value)) < 0)
- log_unit_warning(UNIT(s)->id, "TCP_KEEPIDLE failed: %m");
+ log_unit_warning_errno(UNIT(s), errno, "TCP_KEEPIDLE failed: %m");
}
if (s->keep_alive_interval) {
int value = s->keep_alive_interval / USEC_PER_SEC;
if (setsockopt(fd, SOL_TCP, TCP_KEEPINTVL, &value, sizeof(value)) < 0)
- log_unit_warning(UNIT(s)->id, "TCP_KEEPINTVL failed: %m");
+ log_unit_warning_errno(UNIT(s), errno, "TCP_KEEPINTVL failed: %m");
}
if (s->keep_alive_cnt) {
int value = s->keep_alive_cnt;
if (setsockopt(fd, SOL_SOCKET, TCP_KEEPCNT, &value, sizeof(value)) < 0)
- log_unit_warning(UNIT(s)->id, "TCP_KEEPCNT failed: %m");
+ log_unit_warning_errno(UNIT(s), errno, "TCP_KEEPCNT failed: %m");
}
if (s->defer_accept) {
int value = s->defer_accept / USEC_PER_SEC;
if (setsockopt(fd, SOL_TCP, TCP_DEFER_ACCEPT, &value, sizeof(value)) < 0)
- log_unit_warning(UNIT(s)->id, "TCP_DEFER_ACCEPT failed: %m");
+ log_unit_warning_errno(UNIT(s), errno, "TCP_DEFER_ACCEPT failed: %m");
}
if (s->no_delay) {
int b = s->no_delay;
if (setsockopt(fd, SOL_TCP, TCP_NODELAY, &b, sizeof(b)) < 0)
- log_unit_warning(UNIT(s)->id, "TCP_NODELAY failed: %m");
+ log_unit_warning_errno(UNIT(s), errno, "TCP_NODELAY failed: %m");
}
if (s->broadcast) {
int one = 1;
if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &one, sizeof(one)) < 0)
- log_unit_warning(UNIT(s)->id, "SO_BROADCAST failed: %m");
+ log_unit_warning_errno(UNIT(s), errno, "SO_BROADCAST failed: %m");
}
if (s->pass_cred) {
int one = 1;
if (setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)) < 0)
- log_unit_warning(UNIT(s)->id, "SO_PASSCRED failed: %m");
+ log_unit_warning_errno(UNIT(s), errno, "SO_PASSCRED failed: %m");
}
if (s->pass_sec) {
int one = 1;
if (setsockopt(fd, SOL_SOCKET, SO_PASSSEC, &one, sizeof(one)) < 0)
- log_unit_warning(UNIT(s)->id, "SO_PASSSEC failed: %m");
+ log_unit_warning_errno(UNIT(s), errno, "SO_PASSSEC failed: %m");
}
if (s->priority >= 0)
if (setsockopt(fd, SOL_SOCKET, SO_PRIORITY, &s->priority, sizeof(s->priority)) < 0)
- log_unit_warning(UNIT(s)->id, "SO_PRIORITY failed: %m");
+ log_unit_warning_errno(UNIT(s), errno, "SO_PRIORITY failed: %m");
if (s->receive_buffer > 0) {
int value = (int) s->receive_buffer;
@@ -878,23 +884,23 @@ static void socket_apply_socket_options(Socket *s, int fd) {
if (setsockopt(fd, SOL_SOCKET, SO_RCVBUFFORCE, &value, sizeof(value)) < 0)
if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &value, sizeof(value)) < 0)
- log_unit_warning(UNIT(s)->id, "SO_RCVBUF failed: %m");
+ log_unit_warning_errno(UNIT(s), errno, "SO_RCVBUF failed: %m");
}
if (s->send_buffer > 0) {
int value = (int) s->send_buffer;
if (setsockopt(fd, SOL_SOCKET, SO_SNDBUFFORCE, &value, sizeof(value)) < 0)
if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &value, sizeof(value)) < 0)
- log_unit_warning(UNIT(s)->id, "SO_SNDBUF failed: %m");
+ log_unit_warning_errno(UNIT(s), errno, "SO_SNDBUF failed: %m");
}
if (s->mark >= 0)
if (setsockopt(fd, SOL_SOCKET, SO_MARK, &s->mark, sizeof(s->mark)) < 0)
- log_unit_warning(UNIT(s)->id, "SO_MARK failed: %m");
+ log_unit_warning_errno(UNIT(s), errno, "SO_MARK failed: %m");
if (s->ip_tos >= 0)
if (setsockopt(fd, IPPROTO_IP, IP_TOS, &s->ip_tos, sizeof(s->ip_tos)) < 0)
- log_unit_warning(UNIT(s)->id, "IP_TOS failed: %m");
+ log_unit_warning_errno(UNIT(s), errno, "IP_TOS failed: %m");
if (s->ip_ttl >= 0) {
int x;
@@ -909,30 +915,29 @@ static void socket_apply_socket_options(Socket *s, int fd) {
}
if (r < 0 && x < 0)
- log_unit_warning(UNIT(s)->id,
- "IP_TTL/IPV6_UNICAST_HOPS failed: %m");
+ log_unit_warning_errno(UNIT(s), errno, "IP_TTL/IPV6_UNICAST_HOPS failed: %m");
}
if (s->tcp_congestion)
if (setsockopt(fd, SOL_TCP, TCP_CONGESTION, s->tcp_congestion, strlen(s->tcp_congestion)+1) < 0)
- log_unit_warning(UNIT(s)->id, "TCP_CONGESTION failed: %m");
+ log_unit_warning_errno(UNIT(s), errno, "TCP_CONGESTION failed: %m");
if (s->reuse_port) {
int b = s->reuse_port;
if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &b, sizeof(b)) < 0)
- log_unit_warning(UNIT(s)->id, "SO_REUSEPORT failed: %m");
+ log_unit_warning_errno(UNIT(s), errno, "SO_REUSEPORT failed: %m");
}
if (s->smack_ip_in) {
r = mac_smack_apply_ip_in_fd(fd, s->smack_ip_in);
if (r < 0)
- log_unit_error_errno(UNIT(s)->id, r, "mac_smack_apply_ip_in_fd: %m");
+ log_unit_error_errno(UNIT(s), r, "mac_smack_apply_ip_in_fd: %m");
}
if (s->smack_ip_out) {
r = mac_smack_apply_ip_out_fd(fd, s->smack_ip_out);
if (r < 0)
- log_unit_error_errno(UNIT(s)->id, r, "mac_smack_apply_ip_out_fd: %m");
+ log_unit_error_errno(UNIT(s), r, "mac_smack_apply_ip_out_fd: %m");
}
}
@@ -944,12 +949,12 @@ static void socket_apply_fifo_options(Socket *s, int fd) {
if (s->pipe_size > 0)
if (fcntl(fd, F_SETPIPE_SZ, s->pipe_size) < 0)
- log_unit_warning(UNIT(s)->id, "F_SETPIPE_SZ: %m");
+ log_unit_warning_errno(UNIT(s), errno, "F_SETPIPE_SZ: %m");
if (s->smack) {
r = mac_smack_apply_fd(fd, s->smack);
if (r < 0)
- log_unit_error_errno(UNIT(s)->id, r, "mac_smack_apply_fd: %m");
+ log_unit_error_errno(UNIT(s), r, "mac_smack_apply_fd: %m");
}
}
@@ -1250,7 +1255,7 @@ static void socket_unwatch_fds(Socket *s) {
r = sd_event_source_set_enabled(p->event_source, SD_EVENT_OFF);
if (r < 0)
- log_unit_debug(UNIT(s)->id, "Failed to disable event source.");
+ log_unit_debug_errno(UNIT(s), r, "Failed to disable event source: %m");
}
}
@@ -1264,20 +1269,23 @@ static int socket_watch_fds(Socket *s) {
if (p->fd < 0)
continue;
- if (p->event_source)
+ if (p->event_source) {
r = sd_event_source_set_enabled(p->event_source, SD_EVENT_ON);
- else
+ if (r < 0)
+ goto fail;
+ } else {
r = sd_event_add_io(UNIT(s)->manager->event, &p->event_source, p->fd, EPOLLIN, socket_dispatch_io, p);
+ if (r < 0)
+ goto fail;
- if (r < 0) {
- log_unit_warning_errno(UNIT(s)->id, r, "Failed to watch listening fds: %m");
- goto fail;
+ (void) sd_event_source_set_description(p->event_source, "socket-port-io");
}
}
return 0;
fail:
+ log_unit_warning_errno(UNIT(s), r, "Failed to watch listening fds: %m");
socket_unwatch_fds(s);
return r;
}
@@ -1320,8 +1328,7 @@ static void socket_set_state(Socket *s, SocketState state) {
socket_close_fds(s);
if (state != old_state)
- log_unit_debug(UNIT(s)->id, "%s changed %s -> %s",
- UNIT(s)->id, socket_state_to_string(old_state), socket_state_to_string(state));
+ log_unit_debug(UNIT(s), "Changed %s -> %s", socket_state_to_string(old_state), socket_state_to_string(state));
unit_notify(UNIT(s), state_translation_table[old_state], state_translation_table[state], true);
}
@@ -1390,13 +1397,18 @@ static int socket_spawn(Socket *s, ExecCommand *c, pid_t *_pid) {
.apply_permissions = true,
.apply_chroot = true,
.apply_tty_stdin = true,
+ .bus_endpoint_fd = -1,
};
assert(s);
assert(c);
assert(_pid);
- unit_realize_cgroup(UNIT(s));
+ (void) unit_realize_cgroup(UNIT(s));
+ if (s->reset_cpu_usage) {
+ (void) unit_reset_cpu_usage(UNIT(s));
+ s->reset_cpu_usage = false;
+ }
r = unit_setup_exec_runtime(UNIT(s));
if (r < 0)
@@ -1417,9 +1429,9 @@ static int socket_spawn(Socket *s, ExecCommand *c, pid_t *_pid) {
exec_params.cgroup_path = UNIT(s)->cgroup_path;
exec_params.cgroup_delegate = s->cgroup_context.delegate;
exec_params.runtime_prefix = manager_get_runtime_prefix(UNIT(s)->manager);
- exec_params.unit_id = UNIT(s)->id;
- r = exec_spawn(c,
+ r = exec_spawn(UNIT(s),
+ c,
&s->exec_context,
&exec_params,
s->exec_runtime,
@@ -1563,9 +1575,7 @@ static void socket_enter_stop_post(Socket *s, SocketResult f) {
return;
fail:
- log_unit_warning(UNIT(s)->id,
- "%s failed to run 'stop-post' task: %s",
- UNIT(s)->id, strerror(-r));
+ log_unit_warning_errno(UNIT(s), r, "Failed to run 'stop-post' task: %m");
socket_enter_signal(s, SOCKET_FINAL_SIGTERM, SOCKET_FAILURE_RESOURCES);
}
@@ -1606,7 +1616,7 @@ static void socket_enter_signal(Socket *s, SocketState state, SocketResult f) {
return;
fail:
- log_unit_warning_errno(UNIT(s)->id, r, "%s failed to kill processes: %m", UNIT(s)->id);
+ log_unit_warning_errno(UNIT(s), r, "Failed to kill processes: %m");
if (state == SOCKET_STOP_PRE_SIGTERM || state == SOCKET_STOP_PRE_SIGKILL)
socket_enter_stop_post(s, SOCKET_FAILURE_RESOURCES);
@@ -1637,7 +1647,7 @@ static void socket_enter_stop_pre(Socket *s, SocketResult f) {
return;
fail:
- log_unit_warning_errno(UNIT(s)->id, r, "%s failed to run 'stop-pre' task: %m", UNIT(s)->id);
+ log_unit_warning_errno(UNIT(s), r, "Failed to run 'stop-pre' task: %m");
socket_enter_stop_post(s, SOCKET_FAILURE_RESOURCES);
}
@@ -1647,7 +1657,7 @@ static void socket_enter_listening(Socket *s) {
r = socket_watch_fds(s);
if (r < 0) {
- log_unit_warning_errno(UNIT(s)->id, r, "%s failed to watch sockets: %m", UNIT(s)->id);
+ log_unit_warning_errno(UNIT(s), r, "Failed to watch sockets: %m");
goto fail;
}
@@ -1669,7 +1679,7 @@ static void socket_enter_start_post(Socket *s) {
if (s->control_command) {
r = socket_spawn(s, s->control_command, &s->control_pid);
if (r < 0) {
- log_unit_warning_errno(UNIT(s)->id, r, "%s failed to run 'start-post' task: %m", UNIT(s)->id);
+ log_unit_warning_errno(UNIT(s), r, "Failed to run 'start-post' task: %m");
goto fail;
}
@@ -1690,7 +1700,7 @@ static void socket_enter_start_chown(Socket *s) {
r = socket_open_fds(s);
if (r < 0) {
- log_unit_warning_errno(UNIT(s)->id, r, "%s failed to listen on sockets: %m", UNIT(s)->id);
+ log_unit_warning_errno(UNIT(s), r, "Failed to listen on sockets: %m");
goto fail;
}
@@ -1702,7 +1712,7 @@ static void socket_enter_start_chown(Socket *s) {
r = socket_chown(s, &s->control_pid);
if (r < 0) {
- log_unit_warning_errno(UNIT(s)->id, r, "%s failed to fork 'start-chown' task: %m", UNIT(s)->id);
+ log_unit_warning_errno(UNIT(s), r, "Failed to fork 'start-chown' task: %m");
goto fail;
}
@@ -1727,7 +1737,7 @@ static void socket_enter_start_pre(Socket *s) {
if (s->control_command) {
r = socket_spawn(s, s->control_command, &s->control_pid);
if (r < 0) {
- log_unit_warning_errno(UNIT(s)->id, r, "%s failed to run 'start-pre' task: %m", UNIT(s)->id);
+ log_unit_warning_errno(UNIT(s), r, "Failed to run 'start-pre' task: %m");
goto fail;
}
@@ -1751,7 +1761,7 @@ static void socket_enter_running(Socket *s, int cfd) {
* shut down anyway */
if (unit_stop_pending(UNIT(s))) {
- log_unit_debug(UNIT(s)->id, "Suppressing connection request on %s since unit stop is scheduled.", UNIT(s)->id);
+ log_unit_debug(UNIT(s), "Suppressing connection request since unit stop is scheduled.");
if (cfd >= 0)
safe_close(cfd);
@@ -1761,14 +1771,14 @@ static void socket_enter_running(Socket *s, int cfd) {
r = socket_open_fds(s);
if (r < 0) {
- log_unit_warning_errno(UNIT(s)->id, r, "%s failed to listen on sockets: %m", UNIT(s)->id);
+ log_unit_warning_errno(UNIT(s), r, "Failed to listen on sockets: %m");
socket_enter_stop_pre(s, SOCKET_FAILURE_RESOURCES);
return;
}
r = socket_watch_fds(s);
if (r < 0) {
- log_unit_warning_errno(UNIT(s)->id, r, "%s failed to watch sockets: %m", UNIT(s)->id);
+ log_unit_warning_errno(UNIT(s), r, "Failed to watch sockets: %m");
socket_enter_stop_pre(s, SOCKET_FAILURE_RESOURCES);
}
}
@@ -1791,7 +1801,7 @@ static void socket_enter_running(Socket *s, int cfd) {
if (!pending) {
if (!UNIT_ISSET(s->service)) {
- log_unit_error(UNIT(s)->id, "%s: service to activate vanished, refusing activation.", UNIT(s)->id);
+ log_unit_error(UNIT(s), "Service to activate vanished, refusing activation.");
r = -ENOENT;
goto fail;
}
@@ -1807,7 +1817,7 @@ static void socket_enter_running(Socket *s, int cfd) {
Service *service;
if (s->n_connections >= s->max_connections) {
- log_unit_warning(UNIT(s)->id, "%s: Too many incoming connections (%u)", UNIT(s)->id, s->n_connections);
+ log_unit_warning(UNIT(s), "Too many incoming connections (%u)", s->n_connections);
safe_close(cfd);
return;
}
@@ -1827,17 +1837,13 @@ static void socket_enter_running(Socket *s, int cfd) {
return;
}
- prefix = unit_name_to_prefix(UNIT(s)->id);
- if (!prefix) {
- r = -ENOMEM;
+ r = unit_name_to_prefix(UNIT(s)->id, &prefix);
+ if (r < 0)
goto fail;
- }
- name = unit_name_build(prefix, instance, ".service");
- if (!name) {
- r = -ENOMEM;
+ r = unit_name_build(prefix, instance, ".service", &name);
+ if (r < 0)
goto fail;
- }
r = unit_add_name(UNIT_DEREF(s->service), name);
if (r < 0)
@@ -1869,8 +1875,8 @@ static void socket_enter_running(Socket *s, int cfd) {
return;
fail:
- log_unit_warning(UNIT(s)->id, "%s failed to queue service startup job (Maybe the service file is missing or not a %s unit?): %s",
- UNIT(s)->id, cfd >= 0 ? "template" : "non-template",
+ log_unit_warning(UNIT(s), "Failed to queue service startup job (Maybe the service file is missing or not a %s unit?): %s",
+ cfd >= 0 ? "template" : "non-template",
bus_error_message(&error, r));
socket_enter_stop_pre(s, SOCKET_FAILURE_RESOURCES);
@@ -1895,7 +1901,7 @@ static void socket_run_next(Socket *s) {
return;
fail:
- log_unit_warning_errno(UNIT(s)->id, r, "%s failed to run next task: %m", UNIT(s)->id);
+ log_unit_warning_errno(UNIT(s), r, "Failed to run next task: %m");
if (s->state == SOCKET_START_POST)
socket_enter_stop_pre(s, SOCKET_FAILURE_RESOURCES);
@@ -1935,7 +1941,7 @@ static int socket_start(Unit *u) {
service = SERVICE(UNIT_DEREF(s->service));
if (UNIT(service)->load_state != UNIT_LOADED) {
- log_unit_error(u->id, "Socket service %s not loaded, refusing.", UNIT(service)->id);
+ log_unit_error(u, "Socket service %s not loaded, refusing.", UNIT(service)->id);
return -ENOENT;
}
@@ -1944,7 +1950,7 @@ static int socket_start(Unit *u) {
if (service->state != SERVICE_DEAD &&
service->state != SERVICE_FAILED &&
service->state != SERVICE_AUTO_RESTART) {
- log_unit_error(u->id, "Socket service %s already active, refusing.", UNIT(service)->id);
+ log_unit_error(u, "Socket service %s already active, refusing.", UNIT(service)->id);
return -EBUSY;
}
}
@@ -1952,6 +1958,8 @@ static int socket_start(Unit *u) {
assert(s->state == SOCKET_DEAD || s->state == SOCKET_FAILED);
s->result = SOCKET_SUCCESS;
+ s->reset_cpu_usage = true;
+
socket_enter_start_pre(s);
return 1;
@@ -2054,7 +2062,7 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value,
state = socket_state_from_string(value);
if (state < 0)
- log_unit_debug(u->id, "Failed to parse state value %s", value);
+ log_unit_debug(u, "Failed to parse state value: %s", value);
else
s->deserialized_state = state;
} else if (streq(key, "result")) {
@@ -2062,7 +2070,7 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value,
f = socket_result_from_string(value);
if (f < 0)
- log_unit_debug(u->id, "Failed to parse result value %s", value);
+ log_unit_debug(u, "Failed to parse result value: %s", value);
else if (f != SOCKET_SUCCESS)
s->result = f;
@@ -2070,14 +2078,14 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value,
unsigned k;
if (safe_atou(value, &k) < 0)
- log_unit_debug(u->id, "Failed to parse n-accepted value %s", value);
+ log_unit_debug(u, "Failed to parse n-accepted value: %s", value);
else
s->n_accepted += k;
} else if (streq(key, "control-pid")) {
pid_t pid;
if (parse_pid(value, &pid) < 0)
- log_unit_debug(u->id, "Failed to parse control-pid value %s", value);
+ log_unit_debug(u, "Failed to parse control-pid value: %s", value);
else
s->control_pid = pid;
} else if (streq(key, "control-command")) {
@@ -2085,7 +2093,7 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value,
id = socket_exec_command_from_string(value);
if (id < 0)
- log_unit_debug(u->id, "Failed to parse exec-command value %s", value);
+ log_unit_debug(u, "Failed to parse exec-command value: %s", value);
else {
s->control_command_id = id;
s->control_command = s->exec_command[id];
@@ -2095,12 +2103,12 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value,
SocketPort *p;
if (sscanf(value, "%i %n", &fd, &skip) < 1 || fd < 0 || !fdset_contains(fds, fd))
- log_unit_debug(u->id, "Failed to parse fifo value %s", value);
+ log_unit_debug(u, "Failed to parse fifo value: %s", value);
else {
LIST_FOREACH(port, p, s->ports)
if (p->type == SOCKET_FIFO &&
- streq_ptr(p->path, value+skip))
+ path_equal_or_files_same(p->path, value+skip))
break;
if (p) {
@@ -2114,12 +2122,12 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value,
SocketPort *p;
if (sscanf(value, "%i %n", &fd, &skip) < 1 || fd < 0 || !fdset_contains(fds, fd))
- log_unit_debug(u->id, "Failed to parse special value %s", value);
+ log_unit_debug(u, "Failed to parse special value: %s", value);
else {
LIST_FOREACH(port, p, s->ports)
if (p->type == SOCKET_SPECIAL &&
- streq_ptr(p->path, value+skip))
+ path_equal_or_files_same(p->path, value+skip))
break;
if (p) {
@@ -2133,12 +2141,12 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value,
SocketPort *p;
if (sscanf(value, "%i %n", &fd, &skip) < 1 || fd < 0 || !fdset_contains(fds, fd))
- log_unit_debug(u->id, "Failed to parse mqueue value %s", value);
+ log_unit_debug(u, "Failed to parse mqueue value: %s", value);
else {
LIST_FOREACH(port, p, s->ports)
if (p->type == SOCKET_MQUEUE &&
- streq_ptr(p->path, value+skip))
+ streq(p->path, value+skip))
break;
if (p) {
@@ -2152,7 +2160,7 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value,
SocketPort *p;
if (sscanf(value, "%i %i %n", &fd, &type, &skip) < 2 || fd < 0 || type < 0 || !fdset_contains(fds, fd))
- log_unit_debug(u->id, "Failed to parse socket value %s", value);
+ log_unit_debug(u, "Failed to parse socket value: %s", value);
else {
LIST_FOREACH(port, p, s->ports)
@@ -2170,7 +2178,7 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value,
SocketPort *p;
if (sscanf(value, "%i %n", &fd, &skip) < 1 || fd < 0 || !fdset_contains(fds, fd))
- log_unit_debug(u->id, "Failed to parse socket value %s", value);
+ log_unit_debug(u, "Failed to parse socket value: %s", value);
else {
LIST_FOREACH(port, p, s->ports)
@@ -2183,7 +2191,7 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value,
}
}
} else
- log_unit_debug(UNIT(s)->id, "Unknown serialization key '%s'", key);
+ log_unit_debug(UNIT(s), "Unknown serialization key: %s", key);
return 0;
}
@@ -2287,16 +2295,14 @@ static int socket_dispatch_io(sd_event_source *source, int fd, uint32_t revents,
if (p->socket->state != SOCKET_LISTENING)
return 0;
- log_unit_debug(UNIT(p->socket)->id, "Incoming traffic on %s", UNIT(p->socket)->id);
+ log_unit_debug(UNIT(p->socket), "Incoming traffic");
if (revents != EPOLLIN) {
if (revents & EPOLLHUP)
- log_unit_error(UNIT(p->socket)->id, "%s: Got POLLHUP on a listening socket. The service probably invoked shutdown() on it, and should better not do that.",
- UNIT(p->socket)->id);
+ log_unit_error(UNIT(p->socket), "Got POLLHUP on a listening socket. The service probably invoked shutdown() on it, and should better not do that.");
else
- log_unit_error(UNIT(p->socket)->id, "%s: Got unexpected poll event (0x%x) on socket.",
- UNIT(p->socket)->id, revents);
+ log_unit_error(UNIT(p->socket), "Got unexpected poll event (0x%x) on socket.", revents);
goto fail;
}
@@ -2313,8 +2319,7 @@ static int socket_dispatch_io(sd_event_source *source, int fd, uint32_t revents,
if (errno == EINTR)
continue;
- log_unit_error(UNIT(p->socket)->id,
- "Failed to accept socket: %m");
+ log_unit_error_errno(UNIT(p->socket), errno, "Failed to accept socket: %m");
goto fail;
}
@@ -2362,10 +2367,9 @@ static void socket_sigchld_event(Unit *u, pid_t pid, int code, int status) {
f = SOCKET_SUCCESS;
}
- log_unit_full(u->id,
- f == SOCKET_SUCCESS ? LOG_DEBUG : LOG_NOTICE,
- "%s control process exited, code=%s status=%i",
- u->id, sigchld_code_to_string(code), status);
+ log_unit_full(u, f == SOCKET_SUCCESS ? LOG_DEBUG : LOG_NOTICE, 0,
+ "Control process exited, code=%s status=%i",
+ sigchld_code_to_string(code), status);
if (f != SOCKET_SUCCESS)
s->result = f;
@@ -2374,9 +2378,7 @@ static void socket_sigchld_event(Unit *u, pid_t pid, int code, int status) {
s->control_command->command_next &&
f == SOCKET_SUCCESS) {
- log_unit_debug(u->id,
- "%s running next command for state %s",
- u->id, socket_state_to_string(s->state));
+ log_unit_debug(u, "Running next command for state %s", socket_state_to_string(s->state));
socket_run_next(s);
} else {
s->control_command = NULL;
@@ -2385,9 +2387,7 @@ static void socket_sigchld_event(Unit *u, pid_t pid, int code, int status) {
/* No further commands for this step, so let's figure
* out what to do next */
- log_unit_debug(u->id,
- "%s got final SIGCHLD for state %s",
- u->id, socket_state_to_string(s->state));
+ log_unit_debug(u, "Got final SIGCHLD for state %s", socket_state_to_string(s->state));
switch (s->state) {
@@ -2442,53 +2442,53 @@ static int socket_dispatch_timer(sd_event_source *source, usec_t usec, void *use
switch (s->state) {
case SOCKET_START_PRE:
- log_unit_warning(UNIT(s)->id, "%s starting timed out. Terminating.", UNIT(s)->id);
+ log_unit_warning(UNIT(s), "Starting timed out. Terminating.");
socket_enter_signal(s, SOCKET_FINAL_SIGTERM, SOCKET_FAILURE_TIMEOUT);
break;
case SOCKET_START_CHOWN:
case SOCKET_START_POST:
- log_unit_warning(UNIT(s)->id, "%s starting timed out. Stopping.", UNIT(s)->id);
+ log_unit_warning(UNIT(s), "Starting timed out. Stopping.");
socket_enter_stop_pre(s, SOCKET_FAILURE_TIMEOUT);
break;
case SOCKET_STOP_PRE:
- log_unit_warning(UNIT(s)->id, "%s stopping timed out. Terminating.", UNIT(s)->id);
+ log_unit_warning(UNIT(s), "Stopping timed out. Terminating.");
socket_enter_signal(s, SOCKET_STOP_PRE_SIGTERM, SOCKET_FAILURE_TIMEOUT);
break;
case SOCKET_STOP_PRE_SIGTERM:
if (s->kill_context.send_sigkill) {
- log_unit_warning(UNIT(s)->id, "%s stopping timed out. Killing.", UNIT(s)->id);
+ log_unit_warning(UNIT(s), "Stopping timed out. Killing.");
socket_enter_signal(s, SOCKET_STOP_PRE_SIGKILL, SOCKET_FAILURE_TIMEOUT);
} else {
- log_unit_warning(UNIT(s)->id, "%s stopping timed out. Skipping SIGKILL. Ignoring.", UNIT(s)->id);
+ log_unit_warning(UNIT(s), "Stopping timed out. Skipping SIGKILL. Ignoring.");
socket_enter_stop_post(s, SOCKET_FAILURE_TIMEOUT);
}
break;
case SOCKET_STOP_PRE_SIGKILL:
- log_unit_warning(UNIT(s)->id, "%s still around after SIGKILL. Ignoring.", UNIT(s)->id);
+ log_unit_warning(UNIT(s), "Processes still around after SIGKILL. Ignoring.");
socket_enter_stop_post(s, SOCKET_FAILURE_TIMEOUT);
break;
case SOCKET_STOP_POST:
- log_unit_warning(UNIT(s)->id, "%s stopping timed out (2). Terminating.", UNIT(s)->id);
+ log_unit_warning(UNIT(s), "Stopping timed out (2). Terminating.");
socket_enter_signal(s, SOCKET_FINAL_SIGTERM, SOCKET_FAILURE_TIMEOUT);
break;
case SOCKET_FINAL_SIGTERM:
if (s->kill_context.send_sigkill) {
- log_unit_warning(UNIT(s)->id, "%s stopping timed out (2). Killing.", UNIT(s)->id);
+ log_unit_warning(UNIT(s), "Stopping timed out (2). Killing.");
socket_enter_signal(s, SOCKET_FINAL_SIGKILL, SOCKET_FAILURE_TIMEOUT);
} else {
- log_unit_warning(UNIT(s)->id, "%s stopping timed out (2). Skipping SIGKILL. Ignoring.", UNIT(s)->id);
+ log_unit_warning(UNIT(s), "Stopping timed out (2). Skipping SIGKILL. Ignoring.");
socket_enter_dead(s, SOCKET_FAILURE_TIMEOUT);
}
break;
case SOCKET_FINAL_SIGKILL:
- log_unit_warning(UNIT(s)->id, "%s still around after SIGKILL (2). Entering failed mode.", UNIT(s)->id);
+ log_unit_warning(UNIT(s), "Still around after SIGKILL (2). Entering failed mode.");
socket_enter_dead(s, SOCKET_FAILURE_TIMEOUT);
break;
@@ -2521,7 +2521,8 @@ int socket_collect_fds(Socket *s, int **fds, unsigned *n_fds) {
return 0;
}
- if (!(rfds = new(int, rn_fds)))
+ rfds = new(int, rn_fds);
+ if (!rfds)
return -ENOMEM;
k = 0;
@@ -2557,7 +2558,7 @@ static void socket_notify_service_dead(Socket *s, bool failed_permanent) {
* services. */
if (s->state == SOCKET_RUNNING) {
- log_unit_debug(UNIT(s)->id, "%s got notified about service death (failed permanently: %s)", UNIT(s)->id, yes_no(failed_permanent));
+ log_unit_debug(UNIT(s), "Got notified about service death (failed permanently: %s)", yes_no(failed_permanent));
if (failed_permanent)
socket_enter_stop_pre(s, SOCKET_FAILURE_SERVICE_FAILED_PERMANENT);
else
@@ -2576,7 +2577,7 @@ void socket_connection_unref(Socket *s) {
assert(s->n_connections > 0);
s->n_connections--;
- log_unit_debug(UNIT(s)->id, "%s: One connection closed, %u left.", UNIT(s)->id, s->n_connections);
+ log_unit_debug(UNIT(s), "One connection closed, %u left.", s->n_connections);
}
static void socket_trigger_notify(Unit *u, Unit *other) {
diff --git a/src/core/socket.h b/src/core/socket.h
index a2e08998c0..fa3ebdafa0 100644
--- a/src/core/socket.h
+++ b/src/core/socket.h
@@ -23,8 +23,6 @@
typedef struct Socket Socket;
-#include "manager.h"
-#include "unit.h"
#include "socket-util.h"
#include "mount.h"
#include "service.h"
@@ -168,6 +166,8 @@ struct Socket {
bool selinux_context_from_net;
char *user, *group;
+
+ bool reset_cpu_usage:1;
};
/* Called from the service code when collecting fds */
diff --git a/src/core/swap.c b/src/core/swap.c
index 6997921fde..193c8c3767 100644
--- a/src/core/swap.c
+++ b/src/core/swap.c
@@ -20,28 +20,22 @@
***/
#include <errno.h>
-#include <limits.h>
#include <unistd.h>
-#include <fcntl.h>
#include <sys/epoll.h>
#include <sys/stat.h>
-#include <sys/swap.h>
#include <libudev.h>
#include "unit.h"
#include "swap.h"
-#include "load-fragment.h"
-#include "load-dropin.h"
#include "unit-name.h"
#include "dbus-swap.h"
#include "special.h"
-#include "bus-common-errors.h"
#include "exit-status.h"
-#include "def.h"
#include "path-util.h"
#include "virt.h"
#include "udev-util.h"
#include "fstab-util.h"
+#include "formats-util.h"
static const UnitActiveState state_translation_table[_SWAP_STATE_MAX] = {
[SWAP_DEAD] = UNIT_INACTIVE,
@@ -183,12 +177,18 @@ static int swap_arm_timer(Swap *s) {
return sd_event_source_set_enabled(s->timer_event_source, SD_EVENT_ONESHOT);
}
- return sd_event_add_time(
+ r = sd_event_add_time(
UNIT(s)->manager->event,
&s->timer_event_source,
CLOCK_MONOTONIC,
now(CLOCK_MONOTONIC) + s->timeout_usec, 0,
swap_dispatch_timer, s);
+ if (r < 0)
+ return r;
+
+ (void) sd_event_source_set_description(s->timer_event_source, "swap-timer");
+
+ return 0;
}
static int swap_add_device_links(Swap *s) {
@@ -201,7 +201,7 @@ static int swap_add_device_links(Swap *s) {
return 0;
if (is_device_path(s->what))
- return unit_add_node_link(UNIT(s), s->what, UNIT(s)->manager->running_as == SYSTEMD_SYSTEM);
+ return unit_add_node_link(UNIT(s), s->what, UNIT(s)->manager->running_as == MANAGER_SYSTEM);
else
/* File based swap devices need to be ordered after
* systemd-remount-fs.service, since they might need a
@@ -212,7 +212,7 @@ static int swap_add_device_links(Swap *s) {
static int swap_add_default_dependencies(Swap *s) {
assert(s);
- if (UNIT(s)->manager->running_as != SYSTEMD_SYSTEM)
+ if (UNIT(s)->manager->running_as != MANAGER_SYSTEM)
return 0;
if (detect_container(NULL) > 0)
@@ -222,24 +222,23 @@ static int swap_add_default_dependencies(Swap *s) {
}
static int swap_verify(Swap *s) {
- bool b;
_cleanup_free_ char *e = NULL;
+ int r;
if (UNIT(s)->load_state != UNIT_LOADED)
return 0;
- e = unit_name_from_path(s->what, ".swap");
- if (!e)
- return log_oom();
+ r = unit_name_from_path(s->what, ".swap", &e);
+ if (r < 0)
+ return log_unit_error_errno(UNIT(s), r, "Failed to generate unit name from path: %m");
- b = unit_has_name(UNIT(s), e);
- if (!b) {
- log_unit_error(UNIT(s)->id, "%s: Value of \"What\" and unit name do not match, not loading.", UNIT(s)->id);
+ if (!unit_has_name(UNIT(s), e)) {
+ log_unit_error(UNIT(s), "Value of What= and unit name do not match, not loading.");
return -EINVAL;
}
if (s->exec_context.pam_name && s->kill_context.kill_mode != KILL_CONTROL_GROUP) {
- log_unit_error(UNIT(s)->id, "%s has PAM enabled. Kill mode must be set to 'control-group'. Refusing to load.", UNIT(s)->id);
+ log_unit_error(UNIT(s), "Unit has PAM enabled. Kill mode must be set to 'control-group'. Refusing to load.");
return -EINVAL;
}
@@ -289,8 +288,11 @@ static int swap_load(Unit *u) {
s->what = strdup(s->parameters_fragment.what);
else if (s->parameters_proc_swaps.what)
s->what = strdup(s->parameters_proc_swaps.what);
- else
- s->what = unit_name_to_path(u->id);
+ else {
+ r = unit_name_to_path(u->id, &s->what);
+ if (r < 0)
+ return r;
+ }
if (!s->what)
return -ENOMEM;
@@ -338,7 +340,7 @@ static int swap_load(Unit *u) {
return swap_verify(s);
}
-static int swap_add_one(
+static int swap_setup_unit(
Manager *m,
const char *what,
const char *what_proc_swaps,
@@ -355,16 +357,18 @@ static int swap_add_one(
assert(what);
assert(what_proc_swaps);
- e = unit_name_from_path(what, ".swap");
- if (!e)
- return log_oom();
+ r = unit_name_from_path(what, ".swap", &e);
+ if (r < 0)
+ return log_unit_error_errno(u, r, "Failed to generate unit name from path: %m");
u = manager_get_unit(m, e);
if (u &&
SWAP(u)->from_proc_swaps &&
- !path_equal(SWAP(u)->parameters_proc_swaps.what, what_proc_swaps))
+ !path_equal(SWAP(u)->parameters_proc_swaps.what, what_proc_swaps)) {
+ log_error("Swap %s appeared twice with different device paths %s and %s", e, SWAP(u)->parameters_proc_swaps.what, what_proc_swaps);
return -EEXIST;
+ }
if (!u) {
delete = true;
@@ -379,7 +383,7 @@ static int swap_add_one(
SWAP(u)->what = strdup(what);
if (!SWAP(u)->what) {
- r = log_oom();
+ r = -ENOMEM;
goto fail;
}
@@ -407,11 +411,10 @@ static int swap_add_one(
p->priority = priority;
unit_add_to_dbus_queue(u);
-
return 0;
fail:
- log_unit_warning_errno(e, r, "Failed to load swap unit: %m");
+ log_unit_warning_errno(u, r, "Failed to load swap unit: %m");
if (delete && u)
unit_free(u);
@@ -419,7 +422,7 @@ fail:
return r;
}
-static int swap_process_new_swap(Manager *m, const char *device, int prio, bool set_flags) {
+static int swap_process_new(Manager *m, const char *device, int prio, bool set_flags) {
_cleanup_udev_device_unref_ struct udev_device *d = NULL;
struct udev_list_entry *item = NULL, *first = NULL;
const char *dn;
@@ -428,7 +431,7 @@ static int swap_process_new_swap(Manager *m, const char *device, int prio, bool
assert(m);
- r = swap_add_one(m, device, device, prio, set_flags);
+ r = swap_setup_unit(m, device, device, prio, set_flags);
if (r < 0)
return r;
@@ -444,7 +447,7 @@ static int swap_process_new_swap(Manager *m, const char *device, int prio, bool
/* Add the main device node */
dn = udev_device_get_devnode(d);
if (dn && !streq(dn, device))
- swap_add_one(m, dn, device, prio, set_flags);
+ swap_setup_unit(m, dn, device, prio, set_flags);
/* Add additional units for all symlinks */
first = udev_device_get_devlinks_list_entry(d);
@@ -465,7 +468,7 @@ static int swap_process_new_swap(Manager *m, const char *device, int prio, bool
st.st_rdev != udev_device_get_devnum(d))
continue;
- swap_add_one(m, p, device, prio, set_flags);
+ swap_setup_unit(m, p, device, prio, set_flags);
}
return r;
@@ -494,11 +497,7 @@ static void swap_set_state(Swap *s, SwapState state) {
}
if (state != old_state)
- log_unit_debug(UNIT(s)->id,
- "%s changed %s -> %s",
- UNIT(s)->id,
- swap_state_to_string(old_state),
- swap_state_to_string(state));
+ log_unit_debug(UNIT(s), "Changed %s -> %s", swap_state_to_string(old_state), swap_state_to_string(state));
unit_notify(UNIT(s), state_translation_table[old_state], state_translation_table[state], true);
@@ -604,13 +603,18 @@ static int swap_spawn(Swap *s, ExecCommand *c, pid_t *_pid) {
.apply_permissions = true,
.apply_chroot = true,
.apply_tty_stdin = true,
+ .bus_endpoint_fd = -1,
};
assert(s);
assert(c);
assert(_pid);
- unit_realize_cgroup(UNIT(s));
+ (void) unit_realize_cgroup(UNIT(s));
+ if (s->reset_cpu_usage) {
+ (void) unit_reset_cpu_usage(UNIT(s));
+ s->reset_cpu_usage = false;
+ }
r = unit_setup_exec_runtime(UNIT(s));
if (r < 0)
@@ -626,9 +630,9 @@ static int swap_spawn(Swap *s, ExecCommand *c, pid_t *_pid) {
exec_params.cgroup_path = UNIT(s)->cgroup_path;
exec_params.cgroup_delegate = s->cgroup_context.delegate;
exec_params.runtime_prefix = manager_get_runtime_prefix(UNIT(s)->manager);
- exec_params.unit_id = UNIT(s)->id;
- r = exec_spawn(c,
+ r = exec_spawn(UNIT(s),
+ c,
&s->exec_context,
&exec_params,
s->exec_runtime,
@@ -708,13 +712,13 @@ static void swap_enter_signal(Swap *s, SwapState state, SwapResult f) {
return;
fail:
- log_unit_warning_errno(UNIT(s)->id, r, "%s failed to kill processes: %m", UNIT(s)->id);
+ log_unit_warning_errno(UNIT(s), r, "Failed to kill processes: %m");
swap_enter_dead(s, SWAP_FAILURE_RESOURCES);
}
static void swap_enter_activating(Swap *s) {
- _cleanup_free_ char *discard = NULL;
- int r, priority = -1;
+ _cleanup_free_ char *opts = NULL;
+ int r;
assert(s);
@@ -722,36 +726,31 @@ static void swap_enter_activating(Swap *s) {
s->control_command = s->exec_command + SWAP_EXEC_ACTIVATE;
if (s->from_fragment) {
- fstab_filter_options(s->parameters_fragment.options, "discard\0",
- NULL, &discard, NULL);
+ int priority = -1;
+
+ r = fstab_find_pri(s->parameters_fragment.options, &priority);
+ if (r < 0)
+ log_warning_errno(r, "Failed to parse swap priority \"%s\", ignoring: %m", s->parameters_fragment.options);
+ else if (r == 1 && s->parameters_fragment.priority >= 0)
+ log_warning("Duplicate swap priority configuration by Priority and Options fields.");
- priority = s->parameters_fragment.priority;
- if (priority < 0)
- fstab_find_pri(s->parameters_fragment.options, &priority);
+ if (r <= 0 && s->parameters_fragment.priority >= 0) {
+ if (s->parameters_fragment.options)
+ r = asprintf(&opts, "%s,pri=%i", s->parameters_fragment.options, s->parameters_fragment.priority);
+ else
+ r = asprintf(&opts, "pri=%i", s->parameters_fragment.priority);
+ if (r < 0)
+ goto fail;
+ }
}
r = exec_command_set(s->control_command, "/sbin/swapon", NULL);
if (r < 0)
goto fail;
- if (priority >= 0) {
- char p[DECIMAL_STR_MAX(int)];
-
- sprintf(p, "%i", priority);
- r = exec_command_append(s->control_command, "-p", p, NULL);
- if (r < 0)
- goto fail;
- }
-
- if (discard && !streq(discard, "none")) {
- const char *discard_arg;
-
- if (streq(discard, "all"))
- discard_arg = "--discard";
- else
- discard_arg = strjoina("--discard=", discard);
-
- r = exec_command_append(s->control_command, discard_arg, NULL);
+ if (s->parameters_fragment.options || opts) {
+ r = exec_command_append(s->control_command, "-o",
+ opts ? : s->parameters_fragment.options, NULL);
if (r < 0)
goto fail;
}
@@ -771,7 +770,7 @@ static void swap_enter_activating(Swap *s) {
return;
fail:
- log_unit_warning_errno(UNIT(s)->id, r, "%s failed to run 'swapon' task: %m", UNIT(s)->id);
+ log_unit_warning_errno(UNIT(s), r, "Failed to run 'swapon' task: %m");
swap_enter_dead(s, SWAP_FAILURE_RESOURCES);
}
@@ -801,7 +800,7 @@ static void swap_enter_deactivating(Swap *s) {
return;
fail:
- log_unit_warning_errno(UNIT(s)->id, r, "%s failed to run 'swapoff' task: %m", UNIT(s)->id);
+ log_unit_warning_errno(UNIT(s), r, "Failed to run 'swapoff' task: %m");
swap_enter_active(s, SWAP_FAILURE_RESOURCES);
}
@@ -836,6 +835,8 @@ static int swap_start(Unit *u) {
return -EAGAIN;
s->result = SWAP_SUCCESS;
+ s->reset_cpu_usage = true;
+
swap_enter_activating(s);
return 1;
}
@@ -893,7 +894,7 @@ static int swap_deserialize_item(Unit *u, const char *key, const char *value, FD
state = swap_state_from_string(value);
if (state < 0)
- log_unit_debug(u->id, "Failed to parse state value %s", value);
+ log_unit_debug(u, "Failed to parse state value: %s", value);
else
s->deserialized_state = state;
} else if (streq(key, "result")) {
@@ -901,14 +902,14 @@ static int swap_deserialize_item(Unit *u, const char *key, const char *value, FD
f = swap_result_from_string(value);
if (f < 0)
- log_unit_debug(u->id, "Failed to parse result value %s", value);
+ log_unit_debug(u, "Failed to parse result value: %s", value);
else if (f != SWAP_SUCCESS)
s->result = f;
} else if (streq(key, "control-pid")) {
pid_t pid;
if (parse_pid(value, &pid) < 0)
- log_unit_debug(u->id, "Failed to parse control-pid value %s", value);
+ log_unit_debug(u, "Failed to parse control-pid value: %s", value);
else
s->control_pid = pid;
@@ -917,13 +918,13 @@ static int swap_deserialize_item(Unit *u, const char *key, const char *value, FD
id = swap_exec_command_from_string(value);
if (id < 0)
- log_unit_debug(u->id, "Failed to parse exec-command value %s", value);
+ log_unit_debug(u, "Failed to parse exec-command value: %s", value);
else {
s->control_command_id = id;
s->control_command = s->exec_command + id;
}
} else
- log_unit_debug(u->id, "Unknown serialization key '%s'", key);
+ log_unit_debug(u, "Unknown serialization key: %s", key);
return 0;
}
@@ -981,10 +982,8 @@ static void swap_sigchld_event(Unit *u, pid_t pid, int code, int status) {
s->control_command_id = _SWAP_EXEC_COMMAND_INVALID;
}
- log_unit_full(u->id,
- f == SWAP_SUCCESS ? LOG_DEBUG : LOG_NOTICE,
- "%s swap process exited, code=%s status=%i",
- u->id, sigchld_code_to_string(code), status);
+ log_unit_full(u, f == SWAP_SUCCESS ? LOG_DEBUG : LOG_NOTICE, 0,
+ "Swap process exited, code=%s status=%i", sigchld_code_to_string(code), status);
switch (s->state) {
@@ -1024,38 +1023,38 @@ static int swap_dispatch_timer(sd_event_source *source, usec_t usec, void *userd
case SWAP_ACTIVATING:
case SWAP_ACTIVATING_DONE:
- log_unit_warning(UNIT(s)->id, "%s activation timed out. Stopping.", UNIT(s)->id);
+ log_unit_warning(UNIT(s), "Activation timed out. Stopping.");
swap_enter_signal(s, SWAP_ACTIVATING_SIGTERM, SWAP_FAILURE_TIMEOUT);
break;
case SWAP_DEACTIVATING:
- log_unit_warning(UNIT(s)->id, "%s deactivation timed out. Stopping.", UNIT(s)->id);
+ log_unit_warning(UNIT(s), "Deactivation timed out. Stopping.");
swap_enter_signal(s, SWAP_DEACTIVATING_SIGTERM, SWAP_FAILURE_TIMEOUT);
break;
case SWAP_ACTIVATING_SIGTERM:
if (s->kill_context.send_sigkill) {
- log_unit_warning(UNIT(s)->id, "%s activation timed out. Killing.", UNIT(s)->id);
+ log_unit_warning(UNIT(s), "Activation timed out. Killing.");
swap_enter_signal(s, SWAP_ACTIVATING_SIGKILL, SWAP_FAILURE_TIMEOUT);
} else {
- log_unit_warning(UNIT(s)->id, "%s activation timed out. Skipping SIGKILL. Ignoring.", UNIT(s)->id);
+ log_unit_warning(UNIT(s), "Activation timed out. Skipping SIGKILL. Ignoring.");
swap_enter_dead(s, SWAP_FAILURE_TIMEOUT);
}
break;
case SWAP_DEACTIVATING_SIGTERM:
if (s->kill_context.send_sigkill) {
- log_unit_warning(UNIT(s)->id, "%s deactivation timed out. Killing.", UNIT(s)->id);
+ log_unit_warning(UNIT(s), "Deactivation timed out. Killing.");
swap_enter_signal(s, SWAP_DEACTIVATING_SIGKILL, SWAP_FAILURE_TIMEOUT);
} else {
- log_unit_warning(UNIT(s)->id, "%s deactivation timed out. Skipping SIGKILL. Ignoring.", UNIT(s)->id);
+ log_unit_warning(UNIT(s), "Deactivation timed out. Skipping SIGKILL. Ignoring.");
swap_enter_dead(s, SWAP_FAILURE_TIMEOUT);
}
break;
case SWAP_ACTIVATING_SIGKILL:
case SWAP_DEACTIVATING_SIGKILL:
- log_unit_warning(UNIT(s)->id, "%s swap process still around after SIGKILL. Ignoring.", UNIT(s)->id);
+ log_unit_warning(UNIT(s), "Swap process still around after SIGKILL. Ignoring.");
swap_enter_dead(s, SWAP_FAILURE_TIMEOUT);
break;
@@ -1091,15 +1090,16 @@ static int swap_load_proc_swaps(Manager *m, bool set_flags) {
if (k == EOF)
break;
- log_warning("Failed to parse /proc/swaps:%u", i);
+ log_warning("Failed to parse /proc/swaps:%u.", i);
continue;
}
- d = cunescape(dev);
- if (!d)
- return -ENOMEM;
+ if (cunescape(dev, UNESCAPE_RELAX, &d) < 0)
+ return log_oom();
- k = swap_process_new_swap(m, d, prio, set_flags);
+ device_found_node(m, d, true, DEVICE_FOUND_SWAP, set_flags);
+
+ k = swap_process_new(m, d, prio, set_flags);
if (k < 0)
r = k;
}
@@ -1151,6 +1151,9 @@ static int swap_dispatch_io(sd_event_source *source, int fd, uint32_t revents, v
break;
}
+ if (swap->what)
+ device_found_node(m, swap->what, false, DEVICE_FOUND_SWAP, true);
+
} else if (swap->just_activated) {
/* New swap entry */
@@ -1285,6 +1288,8 @@ static int swap_enumerate(Manager *m) {
r = sd_event_source_set_priority(m->swap_event_source, -10);
if (r < 0)
goto fail;
+
+ (void) sd_event_source_set_description(m->swap_event_source, "swap-proc");
}
r = swap_load_proc_swaps(m, false);
@@ -1298,7 +1303,7 @@ fail:
return r;
}
-int swap_process_new_device(Manager *m, struct udev_device *dev) {
+int swap_process_device_new(Manager *m, struct udev_device *dev) {
struct udev_list_entry *item = NULL, *first = NULL;
_cleanup_free_ char *e = NULL;
const char *dn;
@@ -1312,9 +1317,9 @@ int swap_process_new_device(Manager *m, struct udev_device *dev) {
if (!dn)
return 0;
- e = unit_name_from_path(dn, ".swap");
- if (!e)
- return -ENOMEM;
+ r = unit_name_from_path(dn, ".swap", &e);
+ if (r < 0)
+ return r;
s = hashmap_get(m->units, e);
if (s)
@@ -1323,15 +1328,14 @@ int swap_process_new_device(Manager *m, struct udev_device *dev) {
first = udev_device_get_devlinks_list_entry(dev);
udev_list_entry_foreach(item, first) {
_cleanup_free_ char *n = NULL;
+ int q;
- n = unit_name_from_path(udev_list_entry_get_name(item), ".swap");
- if (!n)
- return -ENOMEM;
+ q = unit_name_from_path(udev_list_entry_get_name(item), ".swap", &n);
+ if (q < 0)
+ return q;
s = hashmap_get(m->units, n);
if (s) {
- int q;
-
q = swap_set_devnode(s, dn);
if (q < 0)
r = q;
@@ -1341,7 +1345,7 @@ int swap_process_new_device(Manager *m, struct udev_device *dev) {
return r;
}
-int swap_process_removed_device(Manager *m, struct udev_device *dev) {
+int swap_process_device_remove(Manager *m, struct udev_device *dev) {
const char *dn;
int r = 0;
Swap *s;
@@ -1390,7 +1394,7 @@ static int swap_get_timeout(Unit *u, uint64_t *timeout) {
return 1;
}
-static bool swap_supported(Manager *m) {
+static bool swap_supported(void) {
static int supported = -1;
/* If swap support is not available in the kernel, or we are
diff --git a/src/core/swap.h b/src/core/swap.h
index 73e64d87a4..9136b9abab 100644
--- a/src/core/swap.h
+++ b/src/core/swap.h
@@ -26,7 +26,6 @@
typedef struct Swap Swap;
-#include "unit.h"
typedef enum SwapState {
SWAP_DEAD,
@@ -88,6 +87,8 @@ struct Swap {
bool is_active:1;
bool just_activated:1;
+ bool reset_cpu_usage:1;
+
SwapResult result;
usec_t timeout_usec;
@@ -116,8 +117,8 @@ struct Swap {
extern const UnitVTable swap_vtable;
-int swap_process_new_device(Manager *m, struct udev_device *dev);
-int swap_process_removed_device(Manager *m, struct udev_device *dev);
+int swap_process_device_new(Manager *m, struct udev_device *dev);
+int swap_process_device_remove(Manager *m, struct udev_device *dev);
const char* swap_state_to_string(SwapState i) _const_;
SwapState swap_state_from_string(const char *s) _pure_;
diff --git a/src/core/system.conf b/src/core/system.conf
index a3727200df..231609033b 100644
--- a/src/core/system.conf
+++ b/src/core/system.conf
@@ -5,10 +5,11 @@
# the Free Software Foundation; either version 2.1 of the License, or
# (at your option) any later version.
#
-# You can override the directives in this file by creating files in
-# /etc/systemd/system.conf.d/*.conf.
+# Entries in this file show the compile time defaults.
+# You can change settings by editing this file.
+# Defaults can be restored by simply deleting this file.
#
-# See systemd-system.conf(5) for details
+# See systemd-system.conf(5) for details.
[Manager]
#LogLevel=info
diff --git a/src/core/systemd.pc.in b/src/core/systemd.pc.in
index d5b86bf65c..ac52b30dd3 100644
--- a/src/core/systemd.pc.in
+++ b/src/core/systemd.pc.in
@@ -6,7 +6,6 @@
# (at your option) any later version.
prefix=@prefix@
-libdir=@libdir@
systemdutildir=@rootlibexecdir@
systemdsystemunitdir=@systemunitdir@
systemdsystempresetdir=@systempresetdir@
diff --git a/src/core/target.c b/src/core/target.c
index 33fb66bc3f..8817ef21c4 100644
--- a/src/core/target.c
+++ b/src/core/target.c
@@ -19,13 +19,9 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <errno.h>
-#include <signal.h>
-#include <unistd.h>
#include "unit.h"
#include "target.h"
-#include "load-fragment.h"
#include "log.h"
#include "dbus-target.h"
#include "special.h"
diff --git a/src/core/target.h b/src/core/target.h
index a5398d9e98..0a25ef469a 100644
--- a/src/core/target.h
+++ b/src/core/target.h
@@ -23,7 +23,6 @@
typedef struct Target Target;
-#include "unit.h"
typedef enum TargetState {
TARGET_DEAD,
diff --git a/src/core/timer.c b/src/core/timer.c
index 45744c7de5..7f4a2eb716 100644
--- a/src/core/timer.c
+++ b/src/core/timer.c
@@ -28,7 +28,6 @@
#include "special.h"
#include "bus-util.h"
#include "bus-error.h"
-#include "mkdir.h"
static const UnitActiveState state_translation_table[_TIMER_STATE_MAX] = {
[TIMER_DEAD] = UNIT_INACTIVE,
@@ -86,7 +85,7 @@ static int timer_verify(Timer *t) {
return 0;
if (!t->values) {
- log_unit_error(UNIT(t)->id, "%s lacks value setting. Refusing.", UNIT(t)->id);
+ log_unit_error(UNIT(t), "Timer unit lacks value setting. Refusing.");
return -EINVAL;
}
@@ -103,7 +102,7 @@ static int timer_add_default_dependencies(Timer *t) {
if (r < 0)
return r;
- if (UNIT(t)->manager->running_as == SYSTEMD_SYSTEM) {
+ if (UNIT(t)->manager->running_as == MANAGER_SYSTEM) {
r = unit_add_two_dependencies_by_name(UNIT(t), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, NULL, true);
if (r < 0)
return r;
@@ -129,7 +128,7 @@ static int timer_setup_persistent(Timer *t) {
if (!t->persistent)
return 0;
- if (UNIT(t)->manager->running_as == SYSTEMD_SYSTEM) {
+ if (UNIT(t)->manager->running_as == MANAGER_SYSTEM) {
r = unit_require_mounts_for(UNIT(t), "/var/lib/systemd/timers");
if (r < 0)
@@ -148,7 +147,7 @@ static int timer_setup_persistent(Timer *t) {
r = get_home_dir(&h);
if (r < 0)
- return log_error_errno(r, "Failed to determine home directory: %m");
+ return log_unit_error_errno(UNIT(t), r, "Failed to determine home directory: %m");
t->stamp_path = strjoin(h, "/.local/share/systemd/timers/stamp-", UNIT(t)->id, NULL);
}
@@ -258,10 +257,7 @@ static void timer_set_state(Timer *t, TimerState state) {
}
if (state != old_state)
- log_unit_debug(UNIT(t)->id,
- "%s changed %s -> %s", UNIT(t)->id,
- timer_state_to_string(old_state),
- timer_state_to_string(state));
+ log_unit_debug(UNIT(t), "Changed %s -> %s", timer_state_to_string(old_state), timer_state_to_string(state));
unit_notify(UNIT(t), state_translation_table[old_state], state_translation_table[state], true);
}
@@ -417,7 +413,7 @@ static void timer_enter_waiting(Timer *t, bool initial) {
}
if (!found_monotonic && !found_realtime) {
- log_unit_debug(UNIT(t)->id, "%s: Timer is elapsed.", UNIT(t)->id);
+ log_unit_debug(UNIT(t), "Timer is elapsed.");
timer_set_state(t, TIMER_ELAPSED);
return;
}
@@ -425,9 +421,7 @@ static void timer_enter_waiting(Timer *t, bool initial) {
if (found_monotonic) {
char buf[FORMAT_TIMESPAN_MAX];
- log_unit_debug(UNIT(t)->id, "%s: Monotonic timer elapses in %s.",
- UNIT(t)->id,
- format_timespan(buf, sizeof(buf), t->next_elapse_monotonic_or_boottime > ts_monotonic ? t->next_elapse_monotonic_or_boottime - ts_monotonic : 0, 0));
+ log_unit_debug(UNIT(t), "Monotonic timer elapses in %s.", format_timespan(buf, sizeof(buf), t->next_elapse_monotonic_or_boottime > ts_monotonic ? t->next_elapse_monotonic_or_boottime - ts_monotonic : 0, 0));
if (t->monotonic_event_source) {
r = sd_event_source_set_time(t->monotonic_event_source, t->next_elapse_monotonic_or_boottime);
@@ -435,15 +429,21 @@ static void timer_enter_waiting(Timer *t, bool initial) {
goto fail;
r = sd_event_source_set_enabled(t->monotonic_event_source, SD_EVENT_ONESHOT);
- } else
+ if (r < 0)
+ goto fail;
+ } else {
+
r = sd_event_add_time(
UNIT(t)->manager->event,
&t->monotonic_event_source,
t->wake_system ? CLOCK_BOOTTIME_ALARM : CLOCK_MONOTONIC,
t->next_elapse_monotonic_or_boottime, t->accuracy_usec,
timer_dispatch, t);
- if (r < 0)
- goto fail;
+ if (r < 0)
+ goto fail;
+
+ (void) sd_event_source_set_description(t->monotonic_event_source, "timer-monotonic");
+ }
} else if (t->monotonic_event_source) {
@@ -454,7 +454,7 @@ static void timer_enter_waiting(Timer *t, bool initial) {
if (found_realtime) {
char buf[FORMAT_TIMESTAMP_MAX];
- log_unit_debug(UNIT(t)->id, "%s: Realtime timer elapses at %s.", UNIT(t)->id, format_timestamp(buf, sizeof(buf), t->next_elapse_realtime));
+ log_unit_debug(UNIT(t), "Realtime timer elapses at %s.", format_timestamp(buf, sizeof(buf), t->next_elapse_realtime));
if (t->realtime_event_source) {
r = sd_event_source_set_time(t->realtime_event_source, t->next_elapse_realtime);
@@ -462,15 +462,20 @@ static void timer_enter_waiting(Timer *t, bool initial) {
goto fail;
r = sd_event_source_set_enabled(t->realtime_event_source, SD_EVENT_ONESHOT);
- } else
+ if (r < 0)
+ goto fail;
+ } else {
r = sd_event_add_time(
UNIT(t)->manager->event,
&t->realtime_event_source,
t->wake_system ? CLOCK_REALTIME_ALARM : CLOCK_REALTIME,
t->next_elapse_realtime, t->accuracy_usec,
timer_dispatch, t);
- if (r < 0)
- goto fail;
+ if (r < 0)
+ goto fail;
+
+ (void) sd_event_source_set_description(t->realtime_event_source, "timer-realtime");
+ }
} else if (t->realtime_event_source) {
@@ -483,7 +488,7 @@ static void timer_enter_waiting(Timer *t, bool initial) {
return;
fail:
- log_unit_warning_errno(UNIT(t)->id, r, "%s failed to enter waiting state: %m", UNIT(t)->id);
+ log_unit_warning_errno(UNIT(t), r, "Failed to enter waiting state: %m");
timer_enter_dead(t, TIMER_FAILURE_RESOURCES);
}
@@ -511,9 +516,7 @@ static void timer_enter_running(Timer *t) {
return;
fail:
- log_unit_warning(UNIT(t)->id,
- "%s failed to queue unit startup job: %s",
- UNIT(t)->id, bus_error_message(&error, r));
+ log_unit_warning(UNIT(t), "Failed to queue unit startup job: %s", bus_error_message(&error, r));
timer_enter_dead(t, TIMER_FAILURE_RESOURCES);
}
@@ -594,7 +597,7 @@ static int timer_deserialize_item(Unit *u, const char *key, const char *value, F
state = timer_state_from_string(value);
if (state < 0)
- log_unit_debug(u->id, "Failed to parse state value %s", value);
+ log_unit_debug(u, "Failed to parse state value: %s", value);
else
t->deserialized_state = state;
} else if (streq(key, "result")) {
@@ -602,23 +605,23 @@ static int timer_deserialize_item(Unit *u, const char *key, const char *value, F
f = timer_result_from_string(value);
if (f < 0)
- log_unit_debug(u->id, "Failed to parse result value %s", value);
+ log_unit_debug(u, "Failed to parse result value: %s", value);
else if (f != TIMER_SUCCESS)
t->result = f;
} else if (streq(key, "last-trigger-realtime")) {
r = safe_atou64(value, &t->last_trigger.realtime);
if (r < 0)
- log_unit_debug(u->id, "Failed to parse last-trigger-realtime value %s", value);
+ log_unit_debug(u, "Failed to parse last-trigger-realtime value: %s", value);
} else if (streq(key, "last-trigger-monotonic")) {
r = safe_atou64(value, &t->last_trigger.monotonic);
if (r < 0)
- log_unit_debug(u->id, "Failed to parse last-trigger-monotonic value %s", value);
+ log_unit_debug(u, "Failed to parse last-trigger-monotonic value: %s", value);
} else
- log_unit_debug(u->id, "Unknown serialization key '%s'", key);
+ log_unit_debug(u, "Unknown serialization key: %s", key);
return 0;
}
@@ -643,7 +646,7 @@ static int timer_dispatch(sd_event_source *s, uint64_t usec, void *userdata) {
if (t->state != TIMER_WAITING)
return 0;
- log_unit_debug(UNIT(t)->id, "Timer elapsed on %s", UNIT(t)->id);
+ log_unit_debug(UNIT(t), "Timer elapsed.");
timer_enter_running(t);
return 0;
}
@@ -676,7 +679,7 @@ static void timer_trigger_notify(Unit *u, Unit *other) {
case TIMER_RUNNING:
if (UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(other))) {
- log_unit_debug(UNIT(t)->id, "%s got notified about unit deactivation.", UNIT(t)->id);
+ log_unit_debug(UNIT(t), "Got notified about unit deactivation.");
timer_enter_waiting(t, false);
}
break;
@@ -709,7 +712,7 @@ static void timer_time_change(Unit *u) {
if (t->state != TIMER_WAITING)
return;
- log_unit_debug(u->id, "%s: time change, recalculating next elapse.", u->id);
+ log_unit_debug(u, "Time change, recalculating next elapse.");
timer_enter_waiting(t, false);
}
diff --git a/src/core/timer.h b/src/core/timer.h
index de412a043e..9d919e4d3e 100644
--- a/src/core/timer.h
+++ b/src/core/timer.h
@@ -23,7 +23,6 @@
typedef struct Timer Timer;
-#include "unit.h"
#include "calendarspec.h"
typedef enum TimerState {
diff --git a/src/core/transaction.c b/src/core/transaction.c
index b0b3d6bd60..090103fbda 100644
--- a/src/core/transaction.c
+++ b/src/core/transaction.c
@@ -23,9 +23,9 @@
#include <fcntl.h>
#include "bus-common-errors.h"
-#include "bus-util.h"
#include "bus-error.h"
#include "transaction.h"
+#include "terminal-util.h"
static void transaction_unlink_job(Transaction *tr, Job *j, bool delete_dependencies);
@@ -189,11 +189,11 @@ static int delete_one_unmergeable_job(Transaction *tr, Job *j) {
* another unit in which case we
* rather remove the start. */
- log_unit_debug(j->unit->id,
+ log_unit_debug(j->unit,
"Looking at job %s/%s conflicted_by=%s",
j->unit->id, job_type_to_string(j->type),
yes_no(j->type == JOB_STOP && job_is_conflicted_by(j)));
- log_unit_debug(k->unit->id,
+ log_unit_debug(k->unit,
"Looking at job %s/%s conflicted_by=%s",
k->unit->id, job_type_to_string(k->type),
yes_no(k->type == JOB_STOP && job_is_conflicted_by(k)));
@@ -222,7 +222,7 @@ static int delete_one_unmergeable_job(Transaction *tr, Job *j) {
return -ENOEXEC;
/* Ok, we can drop one, so let's do so. */
- log_unit_debug(d->unit->id,
+ log_unit_debug(d->unit,
"Fixing conflicting jobs %s/%s,%s/%s by deleting job %s/%s",
j->unit->id, job_type_to_string(j->type),
k->unit->id, job_type_to_string(k->type),
@@ -368,7 +368,7 @@ static int transaction_verify_order_one(Transaction *tr, Job *j, Job *from, unsi
* job to remove. We use the marker to find our way
* back, since smart how we are we stored our way back
* in there. */
- log_unit_warning(j->unit->id,
+ log_unit_warning(j->unit,
"Found ordering cycle on %s/%s",
j->unit->id, job_type_to_string(j->type));
@@ -376,7 +376,7 @@ static int transaction_verify_order_one(Transaction *tr, Job *j, Job *from, unsi
for (k = from; k; k = ((k->generation == generation && k->marker != k) ? k->marker : NULL)) {
/* logging for j not k here here to provide consistent narrative */
- log_unit_warning(j->unit->id,
+ log_unit_warning(j->unit,
"Found dependency on %s/%s",
k->unit->id, job_type_to_string(k->type));
@@ -396,10 +396,10 @@ static int transaction_verify_order_one(Transaction *tr, Job *j, Job *from, unsi
if (delete) {
/* logging for j not k here here to provide consistent narrative */
- log_unit_warning(j->unit->id,
+ log_unit_warning(j->unit,
"Breaking ordering cycle by deleting job %s/%s",
delete->unit->id, job_type_to_string(delete->type));
- log_unit_error(delete->unit->id,
+ log_unit_error(delete->unit,
"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));
@@ -552,17 +552,17 @@ rescan:
continue;
if (stops_running_service)
- log_unit_debug(j->unit->id,
+ log_unit_debug(j->unit,
"%s/%s would stop a running service.",
j->unit->id, job_type_to_string(j->type));
if (changes_existing_job)
- log_unit_debug(j->unit->id,
+ log_unit_debug(j->unit,
"%s/%s would change existing job.",
j->unit->id, job_type_to_string(j->type));
/* Ok, let's get rid of this */
- log_unit_debug(j->unit->id,
+ log_unit_debug(j->unit,
"Deleting %s/%s to minimize impact.",
j->unit->id, job_type_to_string(j->type));
@@ -817,7 +817,7 @@ static void transaction_unlink_job(Transaction *tr, Job *j, bool delete_dependen
job_dependency_free(j->object_list);
if (other && delete_dependencies) {
- log_unit_debug(other->unit->id,
+ log_unit_debug(other->unit,
"Deleting job %s/%s as dependency of job %s/%s",
other->unit->id, job_type_to_string(other->type),
j->unit->id, job_type_to_string(j->type));
@@ -848,14 +848,20 @@ int transaction_add_job_and_dependencies(
assert(type < _JOB_TYPE_MAX_IN_TRANSACTION);
assert(unit);
+ /* Before adding jobs for this unit, let's ensure that its state has been loaded
+ * This matters when jobs are spawned as part of coldplugging itself (see e. g. path_coldplug()).
+ * This way, we "recursively" coldplug units, ensuring that we do not look at state of
+ * not-yet-coldplugged units. */
+ if (unit->manager->n_reloading > 0)
+ unit_coldplug(unit);
+
/* log_debug("Pulling in %s/%s from %s/%s", */
/* unit->id, job_type_to_string(type), */
/* by ? by->unit->id : "NA", */
/* by ? job_type_to_string(by->type) : "NA"); */
if (!IN_SET(unit->load_state, UNIT_LOADED, UNIT_ERROR, UNIT_NOT_FOUND, UNIT_MASKED))
- return sd_bus_error_setf(e, BUS_ERROR_LOAD_FAILED,
- "Unit %s is not loaded properly.", unit->id);
+ return sd_bus_error_setf(e, BUS_ERROR_LOAD_FAILED, "Unit %s is not loaded properly.", unit->id);
if (type != JOB_STOP && unit->load_state == UNIT_ERROR) {
if (unit->load_error == -ENOENT || unit->manager->test_run)
@@ -913,12 +919,8 @@ int transaction_add_job_and_dependencies(
SET_FOREACH(dep, following, i) {
r = transaction_add_job_and_dependencies(tr, type, dep, ret, false, override, false, false, ignore_order, e);
if (r < 0) {
- log_unit_warning(dep->id,
- "Cannot add dependency job for unit %s, ignoring: %s",
- dep->id, bus_error_message(e, r));
-
- if (e)
- sd_bus_error_free(e);
+ log_unit_warning(dep, "Cannot add dependency job for, ignoring: %s", bus_error_message(e, r));
+ sd_bus_error_free(e);
}
}
@@ -933,8 +935,7 @@ int transaction_add_job_and_dependencies(
if (r != -EBADR)
goto fail;
- if (e)
- sd_bus_error_free(e);
+ sd_bus_error_free(e);
}
}
@@ -944,34 +945,29 @@ int transaction_add_job_and_dependencies(
if (r != -EBADR)
goto fail;
- if (e)
- sd_bus_error_free(e);
+ sd_bus_error_free(e);
}
}
SET_FOREACH(dep, ret->unit->dependencies[UNIT_REQUIRES_OVERRIDABLE], i) {
r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, !override, override, false, false, ignore_order, e);
if (r < 0) {
- log_unit_full(dep->id,
- r == -EADDRNOTAVAIL ? LOG_DEBUG : LOG_WARNING,
- "Cannot add dependency job for unit %s, ignoring: %s",
- dep->id, bus_error_message(e, r));
-
- if (e)
- sd_bus_error_free(e);
+ log_unit_full(dep,
+ r == -EADDRNOTAVAIL ? LOG_DEBUG : LOG_WARNING, r,
+ "Cannot add dependency job, ignoring: %s",
+ bus_error_message(e, r));
+ sd_bus_error_free(e);
}
}
SET_FOREACH(dep, ret->unit->dependencies[UNIT_WANTS], i) {
r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, false, false, false, false, ignore_order, e);
if (r < 0) {
- log_unit_full(dep->id,
- r == -EADDRNOTAVAIL ? LOG_DEBUG : LOG_WARNING,
- "Cannot add dependency job for unit %s, ignoring: %s",
- dep->id, bus_error_message(e, r));
-
- if (e)
- sd_bus_error_free(e);
+ log_unit_full(dep,
+ r == -EADDRNOTAVAIL ? LOG_DEBUG : LOG_WARNING, r,
+ "Cannot add dependency job, ignoring: %s",
+ bus_error_message(e, r));
+ sd_bus_error_free(e);
}
}
@@ -981,21 +977,18 @@ int transaction_add_job_and_dependencies(
if (r != -EBADR)
goto fail;
- if (e)
- sd_bus_error_free(e);
+ sd_bus_error_free(e);
}
}
SET_FOREACH(dep, ret->unit->dependencies[UNIT_REQUISITE_OVERRIDABLE], i) {
r = transaction_add_job_and_dependencies(tr, JOB_VERIFY_ACTIVE, dep, ret, !override, override, false, false, ignore_order, e);
if (r < 0) {
- log_unit_full(dep->id,
- r == -EADDRNOTAVAIL ? LOG_DEBUG : LOG_WARNING,
- "Cannot add dependency job for unit %s, ignoring: %s",
- dep->id, bus_error_message(e, r));
-
- if (e)
- sd_bus_error_free(e);
+ log_unit_full(dep,
+ r == -EADDRNOTAVAIL ? LOG_DEBUG : LOG_WARNING, r,
+ "Cannot add dependency job, ignoring: %s",
+ bus_error_message(e, r));
+ sd_bus_error_free(e);
}
}
@@ -1005,60 +998,54 @@ int transaction_add_job_and_dependencies(
if (r != -EBADR)
goto fail;
- if (e)
- sd_bus_error_free(e);
+ sd_bus_error_free(e);
}
}
SET_FOREACH(dep, ret->unit->dependencies[UNIT_CONFLICTED_BY], i) {
r = transaction_add_job_and_dependencies(tr, JOB_STOP, dep, ret, false, override, false, false, ignore_order, e);
if (r < 0) {
- log_unit_warning(dep->id,
- "Cannot add dependency job for unit %s, ignoring: %s",
- dep->id, bus_error_message(e, r));
-
- if (e)
- sd_bus_error_free(e);
+ log_unit_warning(dep,
+ "Cannot add dependency job, ignoring: %s",
+ bus_error_message(e, r));
+ sd_bus_error_free(e);
}
}
}
if (type == JOB_STOP || type == JOB_RESTART) {
+ static const UnitDependency propagate_deps[] = {
+ UNIT_REQUIRED_BY,
+ UNIT_REQUISITE_OF,
+ UNIT_BOUND_BY,
+ UNIT_CONSISTS_OF,
+ };
+
+ JobType ptype;
+ unsigned j;
+
+ /* We propagate STOP as STOP, but RESTART only
+ * as TRY_RESTART, in order not to start
+ * dependencies that are not around. */
+ ptype = type == JOB_RESTART ? JOB_TRY_RESTART : type;
+
+ for (j = 0; j < ELEMENTSOF(propagate_deps); j++)
+ SET_FOREACH(dep, ret->unit->dependencies[propagate_deps[j]], i) {
+ JobType nt;
+
+ nt = job_type_collapse(ptype, dep);
+ if (nt == JOB_NOP)
+ continue;
+
+ r = transaction_add_job_and_dependencies(tr, nt, dep, ret, true, override, false, false, ignore_order, e);
+ if (r < 0) {
+ if (r != -EBADR)
+ goto fail;
- SET_FOREACH(dep, ret->unit->dependencies[UNIT_REQUIRED_BY], i) {
- r = transaction_add_job_and_dependencies(tr, type, dep, ret, true, override, false, false, ignore_order, e);
- if (r < 0) {
- if (r != -EBADR)
- goto fail;
-
- if (e)
sd_bus_error_free(e);
+ }
}
- }
-
- SET_FOREACH(dep, ret->unit->dependencies[UNIT_BOUND_BY], i) {
- r = transaction_add_job_and_dependencies(tr, type, dep, ret, true, override, false, false, ignore_order, e);
- if (r < 0) {
- if (r != -EBADR)
- goto fail;
-
- if (e)
- sd_bus_error_free(e);
- }
- }
-
- SET_FOREACH(dep, ret->unit->dependencies[UNIT_CONSISTS_OF], i) {
- r = transaction_add_job_and_dependencies(tr, type, dep, ret, true, override, false, false, ignore_order, e);
- if (r < 0) {
- if (r != -EBADR)
- goto fail;
-
- if (e)
- sd_bus_error_free(e);
- }
- }
-
}
if (type == JOB_RELOAD) {
@@ -1066,12 +1053,10 @@ int transaction_add_job_and_dependencies(
SET_FOREACH(dep, ret->unit->dependencies[UNIT_PROPAGATES_RELOAD_TO], i) {
r = transaction_add_job_and_dependencies(tr, JOB_RELOAD, dep, ret, false, override, false, false, ignore_order, e);
if (r < 0) {
- log_unit_warning(dep->id,
- "Cannot add dependency reload job for unit %s, ignoring: %s",
- dep->id, bus_error_message(e, r));
-
- if (e)
- sd_bus_error_free(e);
+ log_unit_warning(dep,
+ "Cannot add dependency reload job, ignoring: %s",
+ bus_error_message(e, r));
+ sd_bus_error_free(e);
}
}
}
@@ -1113,9 +1098,7 @@ int transaction_add_isolate_jobs(Transaction *tr, Manager *m) {
r = transaction_add_job_and_dependencies(tr, JOB_STOP, u, tr->anchor_job, true, false, false, false, false, NULL);
if (r < 0)
- log_unit_warning(u->id,
- "Cannot add isolate job for unit %s, ignoring: %s",
- u->id, strerror(-r));
+ log_unit_warning_errno(u, r, "Cannot add isolate job, ignoring: %m");
}
return 0;
diff --git a/src/core/umount.c b/src/core/umount.c
index dd7df194de..bee267a5ad 100644
--- a/src/core/umount.c
+++ b/src/core/umount.c
@@ -24,7 +24,6 @@
#include <string.h>
#include <sys/mount.h>
#include <sys/swap.h>
-#include <unistd.h>
#include <linux/loop.h>
#include <linux/dm-ioctl.h>
@@ -63,6 +62,7 @@ static void mount_points_list_free(MountPoint **head) {
static int mount_points_list_get(MountPoint **head) {
_cleanup_fclose_ FILE *proc_self_mountinfo = NULL;
unsigned int i;
+ int r;
assert(head);
@@ -98,9 +98,9 @@ static int mount_points_list_get(MountPoint **head) {
continue;
}
- p = cunescape(path);
- if (!p)
- return -ENOMEM;
+ r = cunescape(path, UNESCAPE_RELAX, &p);
+ if (r < 0)
+ return r;
/* Ignore mount points we can't unmount because they
* are API or because we are keeping them open (like
@@ -134,10 +134,12 @@ static int mount_points_list_get(MountPoint **head) {
static int swap_list_get(MountPoint **head) {
_cleanup_fclose_ FILE *proc_swaps = NULL;
unsigned int i;
+ int r;
assert(head);
- if (!(proc_swaps = fopen("/proc/swaps", "re")))
+ proc_swaps = fopen("/proc/swaps", "re");
+ if (!proc_swaps)
return (errno == ENOENT) ? 0 : -errno;
(void) fscanf(proc_swaps, "%*s %*s %*s %*s %*s\n");
@@ -147,19 +149,19 @@ static int swap_list_get(MountPoint **head) {
char *dev = NULL, *d;
int k;
- if ((k = fscanf(proc_swaps,
- "%ms " /* device/file */
- "%*s " /* type of swap */
- "%*s " /* swap size */
- "%*s " /* used */
- "%*s\n", /* priority */
- &dev)) != 1) {
+ k = fscanf(proc_swaps,
+ "%ms " /* device/file */
+ "%*s " /* type of swap */
+ "%*s " /* swap size */
+ "%*s " /* used */
+ "%*s\n", /* priority */
+ &dev);
+ if (k != 1) {
if (k == EOF)
break;
log_warning("Failed to parse /proc/swaps:%u.", i);
-
free(dev);
continue;
}
@@ -169,14 +171,13 @@ static int swap_list_get(MountPoint **head) {
continue;
}
- d = cunescape(dev);
+ r = cunescape(dev, UNESCAPE_RELAX, &d);
free(dev);
+ if (r < 0)
+ return r;
- if (!d) {
- return -ENOMEM;
- }
-
- if (!(swap = new0(MountPoint, 1))) {
+ swap = new0(MountPoint, 1);
+ if (!swap) {
free(d);
return -ENOMEM;
}
diff --git a/src/core/unit-printf.c b/src/core/unit-printf.c
index 97135db551..0889769d03 100644
--- a/src/core/unit-printf.c
+++ b/src/core/unit-printf.c
@@ -19,96 +19,65 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "sd-id128.h"
#include "unit.h"
#include "specifier.h"
-#include "path-util.h"
#include "strv.h"
#include "unit-name.h"
#include "unit-printf.h"
#include "macro.h"
#include "cgroup-util.h"
-#include "special.h"
+#include "formats-util.h"
static int specifier_prefix_and_instance(char specifier, void *data, void *userdata, char **ret) {
Unit *u = userdata;
- char *n;
assert(u);
- n = unit_name_to_prefix_and_instance(u->id);
- if (!n)
- return -ENOMEM;
-
- *ret = n;
- return 0;
+ return unit_name_to_prefix_and_instance(u->id, ret);
}
static int specifier_prefix(char specifier, void *data, void *userdata, char **ret) {
Unit *u = userdata;
- char *n;
assert(u);
- n = unit_name_to_prefix(u->id);
- if (!n)
- return -ENOMEM;
-
- *ret = n;
- return 0;
+ return unit_name_to_prefix(u->id, ret);
}
static int specifier_prefix_unescaped(char specifier, void *data, void *userdata, char **ret) {
- Unit *u = userdata;
_cleanup_free_ char *p = NULL;
- char *n;
+ Unit *u = userdata;
+ int r;
assert(u);
- p = unit_name_to_prefix(u->id);
- if (!p)
- return -ENOMEM;
+ r = unit_name_to_prefix(u->id, &p);
+ if (r < 0)
+ return r;
- n = unit_name_unescape(p);
- if (!n)
- return -ENOMEM;
-
- *ret = n;
- return 0;
+ return unit_name_unescape(p, ret);
}
static int specifier_instance_unescaped(char specifier, void *data, void *userdata, char **ret) {
Unit *u = userdata;
- char *n;
assert(u);
if (!u->instance)
- return -ENOTSUP;
-
- n = unit_name_unescape(u->instance);
- if (!n)
- return -ENOMEM;
+ return -EINVAL;
- *ret = n;
- return 0;
+ return unit_name_unescape(u->instance, ret);
}
static int specifier_filename(char specifier, void *data, void *userdata, char **ret) {
Unit *u = userdata;
- char *n;
assert(u);
if (u->instance)
- n = unit_name_path_unescape(u->instance);
+ return unit_name_path_unescape(u->instance, ret);
else
- n = unit_name_to_path(u->id);
- if (!n)
- return -ENOMEM;
-
- *ret = n;
- return 0;
+ return unit_name_to_path(u->id, ret);
}
static int specifier_cgroup(char specifier, void *data, void *userdata, char **ret) {
@@ -130,26 +99,35 @@ static int specifier_cgroup(char specifier, void *data, void *userdata, char **r
static int specifier_cgroup_root(char specifier, void *data, void *userdata, char **ret) {
Unit *u = userdata;
- const char *slice;
char *n;
- int r;
assert(u);
- slice = unit_slice_name(u);
- if (specifier == 'R' || !slice)
- n = strdup(u->manager->cgroup_root);
- else {
- _cleanup_free_ char *p = NULL;
+ n = strdup(u->manager->cgroup_root);
+ if (!n)
+ return -ENOMEM;
- r = cg_slice_to_path(slice, &p);
- if (r < 0)
- return r;
+ *ret = n;
+ return 0;
+}
- n = strjoin(u->manager->cgroup_root, "/", p, NULL);
- if (!n)
- return -ENOMEM;
- }
+static int specifier_cgroup_slice(char specifier, void *data, void *userdata, char **ret) {
+ Unit *u = userdata;
+ char *n;
+
+ assert(u);
+
+ if (UNIT_ISSET(u->slice)) {
+ Unit *slice;
+
+ slice = UNIT_DEREF(u->slice);
+
+ if (slice->cgroup_path)
+ n = strdup(slice->cgroup_path);
+ else
+ n = unit_default_cgroup_path(slice);
+ } else
+ n = strdup(u->manager->cgroup_root);
*ret = n;
return 0;
@@ -162,12 +140,12 @@ static int specifier_runtime(char specifier, void *data, void *userdata, char **
assert(u);
- if (u->manager->running_as == SYSTEMD_SYSTEM)
+ if (u->manager->running_as == MANAGER_SYSTEM)
e = "/run";
else {
e = getenv("XDG_RUNTIME_DIR");
if (!e)
- return -ENOTSUP;
+ return -EOPNOTSUPP;
}
n = strdup(e);
@@ -188,9 +166,9 @@ static int specifier_user_name(char specifier, void *data, void *userdata, char
c = unit_get_exec_context(u);
if (!c)
- return -ENOTSUP;
+ return -EINVAL;
- if (u->manager->running_as == SYSTEMD_SYSTEM) {
+ if (u->manager->running_as == MANAGER_SYSTEM) {
/* We cannot use NSS from PID 1, hence try to make the
* best of it in that case, and fail if we can't help
@@ -251,9 +229,9 @@ static int specifier_user_home(char specifier, void *data, void *userdata, char
c = unit_get_exec_context(u);
if (!c)
- return -ENOTSUP;
+ return -EOPNOTSUPP;
- if (u->manager->running_as == SYSTEMD_SYSTEM) {
+ if (u->manager->running_as == MANAGER_SYSTEM) {
/* We cannot use NSS from PID 1, hence try to make the
* best of it if we can, but fail if we can't */
@@ -261,7 +239,7 @@ static int specifier_user_home(char specifier, void *data, void *userdata, char
if (!c->user || streq(c->user, "root") || streq(c->user, "0"))
n = strdup("/root");
else
- return -ENOTSUP;
+ return -EOPNOTSUPP;
} else {
@@ -299,9 +277,9 @@ static int specifier_user_shell(char specifier, void *data, void *userdata, char
c = unit_get_exec_context(u);
if (!c)
- return -ENOTSUP;
+ return -EOPNOTSUPP;
- if (u->manager->running_as == SYSTEMD_SYSTEM) {
+ if (u->manager->running_as == MANAGER_SYSTEM) {
/* We cannot use NSS from PID 1, hence try to make the
* best of it if we can, but fail if we can't */
@@ -309,7 +287,7 @@ static int specifier_user_shell(char specifier, void *data, void *userdata, char
if (!c->user || streq(c->user, "root") || streq(c->user, "0"))
n = strdup("/bin/sh");
else
- return -ENOTSUP;
+ return -EOPNOTSUPP;
} else {
@@ -394,7 +372,7 @@ int unit_full_printf(Unit *u, const char *format, char **ret) {
{ 'f', specifier_filename, NULL },
{ 'c', specifier_cgroup, NULL },
- { 'r', specifier_cgroup_root, NULL },
+ { 'r', specifier_cgroup_slice, NULL },
{ 'R', specifier_cgroup_root, NULL },
{ 't', specifier_runtime, NULL },
{ 'U', specifier_user_name, NULL },
diff --git a/src/core/unit.c b/src/core/unit.c
index ee8e607c27..e380276d49 100644
--- a/src/core/unit.c
+++ b/src/core/unit.c
@@ -19,12 +19,8 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <assert.h>
#include <errno.h>
#include <string.h>
-#include <sys/epoll.h>
-#include <sys/timerfd.h>
-#include <poll.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
@@ -45,13 +41,13 @@
#include "cgroup-util.h"
#include "missing.h"
#include "mkdir.h"
-#include "label.h"
#include "fileio-label.h"
#include "bus-common-errors.h"
#include "dbus.h"
#include "execute.h"
-#include "virt.h"
#include "dropin.h"
+#include "formats-util.h"
+#include "process-util.h"
const UnitVTable * const unit_vtable[_UNIT_TYPE_MAX] = {
[UNIT_SERVICE] = &service_vtable,
@@ -69,7 +65,7 @@ const UnitVTable * const unit_vtable[_UNIT_TYPE_MAX] = {
[UNIT_SCOPE] = &scope_vtable
};
-static int maybe_warn_about_dependency(const char *id, const char *other, UnitDependency dependency);
+static void maybe_warn_about_dependency(Unit *u, const char *other, UnitDependency dependency);
Unit *unit_new(Manager *m, size_t size) {
Unit *u;
@@ -89,12 +85,13 @@ Unit *unit_new(Manager *m, size_t size) {
u->manager = m;
u->type = _UNIT_TYPE_INVALID;
- u->deserialized_job = _JOB_TYPE_INVALID;
u->default_dependencies = true;
u->unit_file_state = _UNIT_FILE_STATE_INVALID;
u->unit_file_preset = -1;
u->on_failure_job_mode = JOB_REPLACE;
+ RATELIMIT_INIT(u->auto_stop_ratelimit, 10 * USEC_PER_SEC, 16);
+
return u;
}
@@ -147,21 +144,31 @@ int unit_add_name(Unit *u, const char *text) {
assert(u);
assert(text);
- if (unit_name_is_template(text)) {
+ if (unit_name_is_valid(text, UNIT_NAME_TEMPLATE)) {
if (!u->instance)
return -EINVAL;
- s = unit_name_replace_instance(text, u->instance);
- } else
+ r = unit_name_replace_instance(text, u->instance, &s);
+ if (r < 0)
+ return r;
+ } else {
s = strdup(text);
- if (!s)
- return -ENOMEM;
+ if (!s)
+ return -ENOMEM;
+ }
+
+ if (set_contains(u->names, s))
+ return 0;
+ if (hashmap_contains(u->manager->units, s))
+ return -EEXIST;
- if (!unit_name_is_valid(s, TEMPLATE_INVALID))
+ if (!unit_name_is_valid(s, UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE))
return -EINVAL;
- assert_se((t = unit_name_to_type(s)) >= 0);
+ t = unit_name_to_type(s);
+ if (t < 0)
+ return -EINVAL;
if (u->type != _UNIT_TYPE_INVALID && t != u->type)
return -EINVAL;
@@ -174,29 +181,25 @@ int unit_add_name(Unit *u, const char *text) {
return -EINVAL;
/* Ensure that this unit is either instanced or not instanced,
- * but not both. */
+ * but not both. Note that we do allow names with different
+ * instance names however! */
if (u->type != _UNIT_TYPE_INVALID && !u->instance != !i)
return -EINVAL;
- if (unit_vtable[t]->no_alias &&
- !set_isempty(u->names) &&
- !set_get(u->names, s))
+ if (unit_vtable[t]->no_alias && !set_isempty(u->names))
return -EEXIST;
if (hashmap_size(u->manager->units) >= MANAGER_MAX_NAMES)
return -E2BIG;
r = set_put(u->names, s);
- if (r < 0) {
- if (r == -EEXIST)
- return 0;
-
+ if (r < 0)
return r;
- }
+ assert(r > 0);
r = hashmap_put(u->manager->units, s, u);
if (r < 0) {
- set_remove(u->names, s);
+ (void) set_remove(u->names, s);
return r;
}
@@ -226,14 +229,14 @@ int unit_choose_id(Unit *u, const char *name) {
assert(u);
assert(name);
- if (unit_name_is_template(name)) {
+ if (unit_name_is_valid(name, UNIT_NAME_TEMPLATE)) {
if (!u->instance)
return -EINVAL;
- t = unit_name_replace_instance(name, u->instance);
- if (!t)
- return -ENOMEM;
+ r = unit_name_replace_instance(name, u->instance, &t);
+ if (r < 0)
+ return r;
name = t;
}
@@ -243,6 +246,7 @@ int unit_choose_id(Unit *u, const char *name) {
if (!s)
return -ENOENT;
+ /* Determine the new instance from the new id */
r = unit_name_to_instance(s, &i);
if (r < 0)
return r;
@@ -522,7 +526,7 @@ void unit_free(Unit *u) {
free(u->cgroup_path);
}
- set_remove(u->manager->failed_units, u);
+ manager_update_failed_units(u->manager, u, false);
set_remove(u->manager->startup_units, u);
free(u->description);
@@ -649,7 +653,7 @@ static void merge_dependencies(Unit *u, Unit *other, const char *other_id, UnitD
/* Do not add dependencies between u and itself */
if (back == u) {
if (set_remove(back->dependencies[k], other))
- maybe_warn_about_dependency(u->id, other_id, k);
+ maybe_warn_about_dependency(u, other_id, k);
} else {
r = set_remove_and_put(back->dependencies[k], other, u);
if (r == -EEXIST)
@@ -663,7 +667,7 @@ static void merge_dependencies(Unit *u, Unit *other, const char *other_id, UnitD
/* Also do not move dependencies on u to itself */
back = set_remove(other->dependencies[d], u);
if (back)
- maybe_warn_about_dependency(u->id, other_id, d);
+ maybe_warn_about_dependency(u, other_id, d);
/* The move cannot fail. The caller must have performed a reservation. */
assert_se(complete_move(&u->dependencies[d], &other->dependencies[d]) == 0);
@@ -756,24 +760,22 @@ int unit_merge_by_name(Unit *u, const char *name) {
assert(u);
assert(name);
- if (unit_name_is_template(name)) {
+ if (unit_name_is_valid(name, UNIT_NAME_TEMPLATE)) {
if (!u->instance)
return -EINVAL;
- s = unit_name_replace_instance(name, u->instance);
- if (!s)
- return -ENOMEM;
+ r = unit_name_replace_instance(name, u->instance, &s);
+ if (r < 0)
+ return r;
name = s;
}
other = manager_get_unit(u->manager, name);
- if (!other)
- r = unit_add_name(u, name);
- else
- r = unit_merge(u, other);
+ if (other)
+ return unit_merge(u, other);
- return r;
+ return unit_add_name(u, name);
}
Unit* unit_follow_merge(Unit *u) {
@@ -803,7 +805,7 @@ int unit_add_exec_dependencies(Unit *u, ExecContext *c) {
return r;
}
- if (u->manager->running_as != SYSTEMD_SYSTEM)
+ if (u->manager->running_as != MANAGER_SYSTEM)
return 0;
if (c->private_tmp) {
@@ -1091,6 +1093,8 @@ static int unit_add_target_dependencies(Unit *u) {
static const UnitDependency deps[] = {
UNIT_REQUIRED_BY,
UNIT_REQUIRED_BY_OVERRIDABLE,
+ UNIT_REQUISITE_OF,
+ UNIT_REQUISITE_OF_OVERRIDABLE,
UNIT_WANTED_BY,
UNIT_BOUND_BY
};
@@ -1167,7 +1171,6 @@ static int unit_add_mount_dependencies(Unit *u) {
static int unit_add_startup_units(Unit *u) {
CGroupContext *c;
- int r = 0;
c = unit_get_cgroup_context(u);
if (!c)
@@ -1177,11 +1180,7 @@ static int unit_add_startup_units(Unit *u) {
c->startup_blockio_weight == (unsigned long) -1)
return 0;
- r = set_put(u->manager->startup_units, u);
- if (r == -EEXIST)
- return 0;
-
- return r;
+ return set_put(u->manager->startup_units, u);
}
int unit_load(Unit *u) {
@@ -1230,7 +1229,7 @@ int unit_load(Unit *u) {
goto fail;
if (u->on_failure_job_mode == JOB_ISOLATE && set_size(u->dependencies[UNIT_ON_FAILURE]) > 1) {
- log_unit_error(u->id, "More than one OnFailure= dependencies specified for %s but OnFailureJobMode=isolate set. Refusing.", u->id);
+ log_unit_error(u, "More than one OnFailure= dependencies specified but OnFailureJobMode=isolate set. Refusing.");
r = -EINVAL;
goto fail;
}
@@ -1251,8 +1250,7 @@ fail:
unit_add_to_dbus_queue(u);
unit_add_to_gc_queue(u);
- log_unit_debug(u->id, "Failed to load configuration for %s: %s",
- u->id, strerror(-r));
+ log_unit_debug_errno(u, r, "Failed to load configuration: %m");
return r;
}
@@ -1276,23 +1274,20 @@ static bool unit_condition_test_list(Unit *u, Condition *first, const char *(*to
r = condition_test(c);
if (r < 0)
- log_unit_warning(u->id,
- "Couldn't determine result for %s=%s%s%s for %s, assuming failed: %s",
+ log_unit_warning(u,
+ "Couldn't determine result for %s=%s%s%s, assuming failed: %m",
to_string(c->type),
c->trigger ? "|" : "",
c->negate ? "!" : "",
- c->parameter,
- u->id,
- strerror(-r));
+ c->parameter);
else
- log_unit_debug(u->id,
- "%s=%s%s%s %s for %s.",
+ log_unit_debug(u,
+ "%s=%s%s%s %s.",
to_string(c->type),
c->trigger ? "|" : "",
c->negate ? "!" : "",
c->parameter,
- condition_result_to_string(c->result),
- u->id);
+ condition_result_to_string(c->result));
if (!c->trigger && r <= 0)
return false;
@@ -1405,11 +1400,17 @@ static void unit_status_log_starting_stopping_reloading(Unit *u, JobType t) {
t == JOB_STOP ? SD_MESSAGE_UNIT_STOPPING :
SD_MESSAGE_UNIT_RELOADING;
- log_unit_struct(u->id,
- LOG_INFO,
- LOG_MESSAGE_ID(mid),
- LOG_MESSAGE("%s", buf),
- NULL);
+ /* Note that we deliberately use LOG_MESSAGE() instead of
+ * LOG_UNIT_MESSAGE() here, since this is supposed to mimic
+ * closely what is written to screen using the status output,
+ * which is supposed the highest level, friendliest output
+ * possible, which means we should avoid the low-level unit
+ * name. */
+ log_struct(LOG_INFO,
+ LOG_MESSAGE_ID(mid),
+ LOG_UNIT_ID(u),
+ LOG_MESSAGE("%s", buf),
+ NULL);
}
/* Errors:
@@ -1443,26 +1444,26 @@ 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_unit_debug(u->id, "Starting of %s requested but condition failed. Not starting unit.", u->id);
+ log_unit_debug(u, "Starting requested but condition failed. Not starting unit.");
return -EALREADY;
}
/* If the asserts failed, fail the entire job */
if (state != UNIT_ACTIVATING &&
!unit_assert_test(u)) {
- log_unit_debug(u->id, "Starting of %s requested but asserts failed.", u->id);
+ log_unit_notice(u, "Starting requested but asserts failed.");
return -EPROTO;
}
/* Forward to the main object, if we aren't it. */
following = unit_following(u);
if (following) {
- log_unit_debug(u->id, "Redirecting start request from %s to %s.", u->id, following->id);
+ log_unit_debug(u, "Redirecting start request from %s to %s.", u->id, following->id);
return unit_start(following);
}
- if (UNIT_VTABLE(u)->supported && !UNIT_VTABLE(u)->supported(u->manager))
- return -ENOTSUP;
+ if (!unit_supported(u))
+ return -EOPNOTSUPP;
/* If it is stopped, but we cannot start it, then fail */
if (!UNIT_VTABLE(u)->start)
@@ -1517,7 +1518,7 @@ int unit_stop(Unit *u) {
following = unit_following(u);
if (following) {
- log_unit_debug(u->id, "Redirecting stop request from %s to %s.", u->id, following->id);
+ log_unit_debug(u, "Redirecting stop request from %s to %s.", u->id, following->id);
return unit_stop(following);
}
@@ -1558,13 +1559,13 @@ int unit_reload(Unit *u) {
return -EALREADY;
if (state != UNIT_ACTIVE) {
- log_unit_warning(u->id, "Unit %s cannot be reloaded because it is inactive.", u->id);
+ log_unit_warning(u, "Unit cannot be reloaded because it is inactive.");
return -ENOEXEC;
}
following = unit_following(u);
if (following) {
- log_unit_debug(u->id, "Redirecting reload request from %s to %s.", u->id, following->id);
+ log_unit_debug(u, "Redirecting reload request from %s to %s.", u->id, following->id);
return unit_reload(following);
}
@@ -1591,8 +1592,20 @@ bool unit_can_reload(Unit *u) {
}
static void unit_check_unneeded(Unit *u) {
- Iterator i;
+
+ static const UnitDependency needed_dependencies[] = {
+ UNIT_REQUIRED_BY,
+ UNIT_REQUIRED_BY_OVERRIDABLE,
+ UNIT_REQUISITE,
+ UNIT_REQUISITE_OF_OVERRIDABLE,
+ UNIT_WANTED_BY,
+ UNIT_BOUND_BY,
+ };
+
Unit *other;
+ Iterator i;
+ unsigned j;
+ int r;
assert(u);
@@ -1605,32 +1618,32 @@ static void unit_check_unneeded(Unit *u) {
if (!UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(u)))
return;
- SET_FOREACH(other, u->dependencies[UNIT_REQUIRED_BY], i)
- if (unit_active_or_pending(other))
- return;
-
- SET_FOREACH(other, u->dependencies[UNIT_REQUIRED_BY_OVERRIDABLE], i)
- if (unit_active_or_pending(other))
- return;
-
- SET_FOREACH(other, u->dependencies[UNIT_WANTED_BY], i)
- if (unit_active_or_pending(other))
- return;
+ for (j = 0; j < ELEMENTSOF(needed_dependencies); j++)
+ SET_FOREACH(other, u->dependencies[needed_dependencies[j]], i)
+ if (unit_active_or_pending(other))
+ return;
- SET_FOREACH(other, u->dependencies[UNIT_BOUND_BY], i)
- if (unit_active_or_pending(other))
- return;
+ /* If stopping a unit fails continously we might enter a stop
+ * loop here, hence stop acting on the service being
+ * unnecessary after a while. */
+ if (!ratelimit_test(&u->auto_stop_ratelimit)) {
+ log_unit_warning(u, "Unit not needed anymore, but not stopping since we tried this too often recently.");
+ return;
+ }
- log_unit_info(u->id, "Unit %s is not needed anymore. Stopping.", u->id);
+ log_unit_info(u, "Unit not needed anymore. Stopping.");
/* Ok, nobody needs us anymore. Sniff. Then let's commit suicide */
- manager_add_job(u->manager, JOB_STOP, u, JOB_FAIL, true, NULL, NULL);
+ r = manager_add_job(u->manager, JOB_STOP, u, JOB_FAIL, true, NULL, NULL);
+ if (r < 0)
+ log_unit_warning_errno(u, r, "Failed to enqueue stop job, ignoring: %m");
}
static void unit_check_binds_to(Unit *u) {
bool stop = false;
Unit *other;
Iterator i;
+ int r;
assert(u);
@@ -1648,15 +1661,27 @@ static void unit_check_binds_to(Unit *u) {
continue;
stop = true;
+ break;
}
if (!stop)
return;
- log_unit_info(u->id, "Unit %s is bound to inactive unit. Stopping, too.", u->id);
+ /* If stopping a unit fails continously we might enter a stop
+ * loop here, hence stop acting on the service being
+ * unnecessary after a while. */
+ if (!ratelimit_test(&u->auto_stop_ratelimit)) {
+ log_unit_warning(u, "Unit is bound to inactive unit %s, but not stopping since we tried this too often recently.", other->id);
+ return;
+ }
+
+ assert(other);
+ log_unit_info(u, "Unit is bound to inactive unit %s. Stopping, too.", other->id);
/* A unit we need to run is gone. Sniff. Let's stop this. */
- manager_add_job(u->manager, JOB_STOP, u, JOB_FAIL, true, NULL, NULL);
+ r = manager_add_job(u->manager, JOB_STOP, u, JOB_FAIL, true, NULL, NULL);
+ if (r < 0)
+ log_unit_warning_errno(u, r, "Failed to enqueue stop job, ignoring: %m");
}
static void retroactively_start_dependencies(Unit *u) {
@@ -1745,14 +1770,14 @@ void unit_start_on_failure(Unit *u) {
if (set_size(u->dependencies[UNIT_ON_FAILURE]) <= 0)
return;
- log_unit_info(u->id, "Triggering OnFailure= dependencies of %s.", u->id);
+ log_unit_info(u, "Triggering OnFailure= dependencies.");
SET_FOREACH(other, u->dependencies[UNIT_ON_FAILURE], i) {
int r;
r = manager_add_job(u->manager, JOB_START, other, u->on_failure_job_mode, true, NULL, NULL);
if (r < 0)
- log_unit_error_errno(u->id, r, "Failed to enqueue OnFailure= job: %m");
+ log_unit_error_errno(u, r, "Failed to enqueue OnFailure= job: %m");
}
}
@@ -1801,10 +1826,7 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su
}
/* Keep track of failed units */
- if (ns == UNIT_FAILED)
- set_put(u->manager->failed_units, u);
- else
- set_remove(u->manager->failed_units, u);
+ manager_update_failed_units(u->manager, u, ns == UNIT_FAILED);
/* Make sure the cgroup is always removed when we become inactive */
if (UNIT_IS_INACTIVE_OR_FAILED(ns))
@@ -1916,7 +1938,7 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su
check_unneeded_dependencies(u);
if (ns != os && ns == UNIT_FAILED) {
- log_unit_notice(u->id, "Unit %s entered failed state.", u->id);
+ log_unit_notice(u, "Unit entered failed state.");
unit_start_on_failure(u);
}
}
@@ -2147,51 +2169,17 @@ bool unit_job_is_applicable(Unit *u, JobType j) {
}
}
-static int maybe_warn_about_dependency(const char *id, const char *other, UnitDependency dependency) {
- assert(id);
-
- switch (dependency) {
- case UNIT_REQUIRES:
- case UNIT_REQUIRES_OVERRIDABLE:
- case UNIT_WANTS:
- case UNIT_REQUISITE:
- case UNIT_REQUISITE_OVERRIDABLE:
- case UNIT_BINDS_TO:
- case UNIT_PART_OF:
- case UNIT_REQUIRED_BY:
- case UNIT_REQUIRED_BY_OVERRIDABLE:
- case UNIT_WANTED_BY:
- case UNIT_BOUND_BY:
- case UNIT_CONSISTS_OF:
- case UNIT_REFERENCES:
- case UNIT_REFERENCED_BY:
- case UNIT_PROPAGATES_RELOAD_TO:
- case UNIT_RELOAD_PROPAGATED_FROM:
- case UNIT_JOINS_NAMESPACE_OF:
- return 0;
-
- case UNIT_CONFLICTS:
- case UNIT_CONFLICTED_BY:
- case UNIT_BEFORE:
- case UNIT_AFTER:
- case UNIT_ON_FAILURE:
- case UNIT_TRIGGERS:
- case UNIT_TRIGGERED_BY:
- if (streq_ptr(id, other))
- log_unit_warning(id, "Dependency %s=%s dropped from unit %s",
- unit_dependency_to_string(dependency), id, other);
- else
- log_unit_warning(id, "Dependency %s=%s dropped from unit %s merged into %s",
- unit_dependency_to_string(dependency), id,
- strna(other), id);
- return -EINVAL;
+static void maybe_warn_about_dependency(Unit *u, const char *other, UnitDependency dependency) {
+ assert(u);
- case _UNIT_DEPENDENCY_MAX:
- case _UNIT_DEPENDENCY_INVALID:
- break;
- }
+ /* Only warn about some unit types */
+ if (!IN_SET(dependency, UNIT_CONFLICTS, UNIT_CONFLICTED_BY, UNIT_BEFORE, UNIT_AFTER, UNIT_ON_FAILURE, UNIT_TRIGGERS, UNIT_TRIGGERED_BY))
+ return;
- assert_not_reached("Invalid dependency type");
+ if (streq_ptr(u->id, other))
+ log_unit_warning(u, "Dependency %s=%s dropped", unit_dependency_to_string(dependency), u->id);
+ else
+ log_unit_warning(u, "Dependency %s=%s dropped, merged into %s", unit_dependency_to_string(dependency), strna(other), u->id);
}
int unit_add_dependency(Unit *u, UnitDependency d, Unit *other, bool add_reference) {
@@ -2200,13 +2188,15 @@ int unit_add_dependency(Unit *u, UnitDependency d, Unit *other, bool add_referen
[UNIT_REQUIRES] = UNIT_REQUIRED_BY,
[UNIT_REQUIRES_OVERRIDABLE] = UNIT_REQUIRED_BY_OVERRIDABLE,
[UNIT_WANTS] = UNIT_WANTED_BY,
- [UNIT_REQUISITE] = UNIT_REQUIRED_BY,
- [UNIT_REQUISITE_OVERRIDABLE] = UNIT_REQUIRED_BY_OVERRIDABLE,
+ [UNIT_REQUISITE] = UNIT_REQUISITE_OF,
+ [UNIT_REQUISITE_OVERRIDABLE] = UNIT_REQUISITE_OF_OVERRIDABLE,
[UNIT_BINDS_TO] = UNIT_BOUND_BY,
[UNIT_PART_OF] = UNIT_CONSISTS_OF,
- [UNIT_REQUIRED_BY] = _UNIT_DEPENDENCY_INVALID,
- [UNIT_REQUIRED_BY_OVERRIDABLE] = _UNIT_DEPENDENCY_INVALID,
- [UNIT_WANTED_BY] = _UNIT_DEPENDENCY_INVALID,
+ [UNIT_REQUIRED_BY] = UNIT_REQUIRES,
+ [UNIT_REQUIRED_BY_OVERRIDABLE] = UNIT_REQUIRES_OVERRIDABLE,
+ [UNIT_REQUISITE_OF] = UNIT_REQUISITE,
+ [UNIT_REQUISITE_OF_OVERRIDABLE] = UNIT_REQUISITE_OVERRIDABLE,
+ [UNIT_WANTED_BY] = UNIT_WANTS,
[UNIT_BOUND_BY] = UNIT_BINDS_TO,
[UNIT_CONSISTS_OF] = UNIT_PART_OF,
[UNIT_CONFLICTS] = UNIT_CONFLICTED_BY,
@@ -2235,7 +2225,7 @@ int unit_add_dependency(Unit *u, UnitDependency d, Unit *other, bool add_referen
/* We won't allow dependencies on ourselves. We will not
* consider them an error however. */
if (u == other) {
- maybe_warn_about_dependency(orig_u->id, orig_other->id, d);
+ maybe_warn_about_dependency(orig_u, orig_other->id, d);
return 0;
}
@@ -2308,58 +2298,55 @@ int unit_add_two_dependencies(Unit *u, UnitDependency d, UnitDependency e, Unit
if (r < 0)
return r;
- r = unit_add_dependency(u, e, other, add_reference);
- if (r < 0)
- return r;
-
- return 0;
+ return unit_add_dependency(u, e, other, add_reference);
}
-static const char *resolve_template(Unit *u, const char *name, const char*path, char **p) {
- char *s;
+static int resolve_template(Unit *u, const char *name, const char*path, char **buf, const char **ret) {
+ int r;
assert(u);
assert(name || path);
- assert(p);
+ assert(buf);
+ assert(ret);
if (!name)
name = basename(path);
- if (!unit_name_is_template(name)) {
- *p = NULL;
- return name;
+ if (!unit_name_is_valid(name, UNIT_NAME_TEMPLATE)) {
+ *buf = NULL;
+ *ret = name;
+ return 0;
}
if (u->instance)
- s = unit_name_replace_instance(name, u->instance);
+ r = unit_name_replace_instance(name, u->instance, buf);
else {
_cleanup_free_ char *i = NULL;
- i = unit_name_to_prefix(u->id);
- if (!i)
- return NULL;
+ r = unit_name_to_prefix(u->id, &i);
+ if (r < 0)
+ return r;
- s = unit_name_replace_instance(name, i);
+ r = unit_name_replace_instance(name, i, buf);
}
+ if (r < 0)
+ return r;
- if (!s)
- return NULL;
-
- *p = s;
- return s;
+ *ret = *buf;
+ return 0;
}
int unit_add_dependency_by_name(Unit *u, UnitDependency d, const char *name, const char *path, bool add_reference) {
+ _cleanup_free_ char *buf = NULL;
Unit *other;
int r;
- _cleanup_free_ char *s = NULL;
assert(u);
assert(name || path);
- name = resolve_template(u, name, path, &s);
- if (!name)
- return -ENOMEM;
+ r = resolve_template(u, name, path, &buf, &name);
+ if (r < 0)
+ return r;
r = manager_load_unit(u->manager, name, path, NULL, &other);
if (r < 0)
@@ -2369,16 +2356,16 @@ int unit_add_dependency_by_name(Unit *u, UnitDependency d, const char *name, con
}
int unit_add_two_dependencies_by_name(Unit *u, UnitDependency d, UnitDependency e, const char *name, const char *path, bool add_reference) {
- _cleanup_free_ char *s = NULL;
+ _cleanup_free_ char *buf = NULL;
Unit *other;
int r;
assert(u);
assert(name || path);
- name = resolve_template(u, name, path, &s);
- if (!name)
- return -ENOMEM;
+ r = resolve_template(u, name, path, &buf, &name);
+ if (r < 0)
+ return r;
r = manager_load_unit(u->manager, name, path, NULL, &other);
if (r < 0)
@@ -2388,16 +2375,16 @@ int unit_add_two_dependencies_by_name(Unit *u, UnitDependency d, UnitDependency
}
int unit_add_dependency_by_name_inverse(Unit *u, UnitDependency d, const char *name, const char *path, bool add_reference) {
+ _cleanup_free_ char *buf = NULL;
Unit *other;
int r;
- _cleanup_free_ char *s = NULL;
assert(u);
assert(name || path);
- name = resolve_template(u, name, path, &s);
- if (!name)
- return -ENOMEM;
+ r = resolve_template(u, name, path, &buf, &name);
+ if (r < 0)
+ return r;
r = manager_load_unit(u->manager, name, path, NULL, &other);
if (r < 0)
@@ -2407,26 +2394,22 @@ int unit_add_dependency_by_name_inverse(Unit *u, UnitDependency d, const char *n
}
int unit_add_two_dependencies_by_name_inverse(Unit *u, UnitDependency d, UnitDependency e, const char *name, const char *path, bool add_reference) {
+ _cleanup_free_ char *buf = NULL;
Unit *other;
int r;
- _cleanup_free_ char *s = NULL;
assert(u);
assert(name || path);
- name = resolve_template(u, name, path, &s);
- if (!name)
- return -ENOMEM;
-
- r = manager_load_unit(u->manager, name, path, NULL, &other);
+ r = resolve_template(u, name, path, &buf, &name);
if (r < 0)
return r;
- r = unit_add_two_dependencies(other, d, e, u, add_reference);
+ r = manager_load_unit(u->manager, name, path, NULL, &other);
if (r < 0)
return r;
- return r;
+ return unit_add_two_dependencies(other, d, e, u, add_reference);
}
int set_unit_path(const char *p) {
@@ -2489,18 +2472,18 @@ int unit_add_default_slice(Unit *u, CGroupContext *c) {
/* Implicitly place all instantiated units in their
* own per-template slice */
- prefix = unit_name_to_prefix(u->id);
- if (!prefix)
- return -ENOMEM;
+ r = unit_name_to_prefix(u->id, &prefix);
+ if (r < 0)
+ return r;
/* The prefix is already escaped, but it might include
* "-" which has a special meaning for slice units,
* hence escape it here extra. */
- escaped = strreplace(prefix, "-", "\\x2d");
+ escaped = unit_name_escape(prefix);
if (!escaped)
return -ENOMEM;
- if (u->manager->running_as == SYSTEMD_SYSTEM)
+ if (u->manager->running_as == MANAGER_SYSTEM)
b = strjoin("system-", escaped, ".slice", NULL);
else
b = strappend(escaped, ".slice");
@@ -2510,7 +2493,7 @@ int unit_add_default_slice(Unit *u, CGroupContext *c) {
slice_name = b;
} else
slice_name =
- u->manager->running_as == SYSTEMD_SYSTEM
+ u->manager->running_as == MANAGER_SYSTEM
? SPECIAL_SYSTEM_SLICE
: SPECIAL_ROOT_SLICE;
@@ -2539,11 +2522,11 @@ int unit_load_related_unit(Unit *u, const char *type, Unit **_found) {
assert(type);
assert(_found);
- t = unit_name_change_suffix(u->id, type);
- if (!t)
- return -ENOMEM;
-
- assert(!unit_has_name(u, t));
+ r = unit_name_change_suffix(u->id, type, &t);
+ if (r < 0)
+ return r;
+ if (unit_has_name(u, t))
+ return -EINVAL;
r = manager_load_unit(u->manager, t, NULL, NULL, _found);
assert(r < 0 || *_found != u);
@@ -2589,7 +2572,7 @@ int unit_serialize(Unit *u, FILE *f, FDSet *fds, bool serialize_jobs) {
rt = unit_get_exec_runtime(u);
if (rt) {
- r = exec_runtime_serialize(rt, u, f, fds);
+ r = exec_runtime_serialize(u, rt, f, fds);
if (r < 0)
return r;
}
@@ -2609,6 +2592,7 @@ int unit_serialize(Unit *u, FILE *f, FDSet *fds, bool serialize_jobs) {
unit_serialize_item(u, f, "assert-result", yes_no(u->assert_result));
unit_serialize_item(u, f, "transient", yes_no(u->transient));
+ unit_serialize_item_format(u, f, "cpuacct-usage-base", "%" PRIu64, u->cpuacct_usage_base);
if (u->cgroup_path)
unit_serialize_item(u, f, "cgroup", u->cgroup_path);
@@ -2657,6 +2641,40 @@ void unit_serialize_item(Unit *u, FILE *f, const char *key, const char *value) {
fprintf(f, "%s=%s\n", key, value);
}
+static int unit_set_cgroup_path(Unit *u, const char *path) {
+ _cleanup_free_ char *p = NULL;
+ int r;
+
+ assert(u);
+
+ if (path) {
+ p = strdup(path);
+ if (!p)
+ return -ENOMEM;
+ } else
+ p = NULL;
+
+ if (streq_ptr(u->cgroup_path, p))
+ return 0;
+
+ if (p) {
+ r = hashmap_put(u->manager->cgroup_unit, p, u);
+ if (r < 0)
+ return r;
+ }
+
+ if (u->cgroup_path) {
+ log_unit_debug(u, "Changing cgroup path from %s to %s.", u->cgroup_path, strna(p));
+ hashmap_remove(u->manager->cgroup_unit, u->cgroup_path);
+ free(u->cgroup_path);
+ }
+
+ u->cgroup_path = p;
+ p = NULL;
+
+ return 0;
+}
+
int unit_deserialize(Unit *u, FILE *f, FDSet *fds) {
ExecRuntime **rt = NULL;
size_t offset;
@@ -2684,7 +2702,7 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) {
l = strstrip(line);
/* End marker */
- if (l[0] == 0)
+ if (isempty(l))
return 0;
k = strcspn(l, "=");
@@ -2702,7 +2720,7 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) {
j = job_new_raw(u);
if (!j)
- return -ENOMEM;
+ return log_oom();
r = job_deserialize(j, f, fds);
if (r < 0) {
@@ -2722,16 +2740,8 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) {
job_free(j);
return r;
}
- } else {
- /* legacy */
- JobType type;
-
- type = job_type_from_string(v);
- if (type < 0)
- log_debug("Failed to parse job type value %s", v);
- else
- u->deserialized_job = type;
- }
+ } else /* legacy for pre-44 */
+ log_unit_warning(u, "Update from too old systemd versions are unsupported, cannot deserialize job: %s", v);
continue;
} else if (streq(l, "inactive-exit-timestamp")) {
dual_timestamp_deserialize(v, &u->inactive_exit_timestamp);
@@ -2752,71 +2762,68 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) {
dual_timestamp_deserialize(v, &u->assert_timestamp);
continue;
} else if (streq(l, "condition-result")) {
- int b;
- b = parse_boolean(v);
- if (b < 0)
- log_debug("Failed to parse condition result value %s", v);
+ r = parse_boolean(v);
+ if (r < 0)
+ log_unit_debug(u, "Failed to parse condition result value %s, ignoring.", v);
else
- u->condition_result = b;
+ u->condition_result = r;
continue;
} else if (streq(l, "assert-result")) {
- int b;
- b = parse_boolean(v);
- if (b < 0)
- log_debug("Failed to parse assert result value %s", v);
+ r = parse_boolean(v);
+ if (r < 0)
+ log_unit_debug(u, "Failed to parse assert result value %s, ignoring.", v);
else
- u->assert_result = b;
+ u->assert_result = r;
continue;
} else if (streq(l, "transient")) {
- int b;
- b = parse_boolean(v);
- if (b < 0)
- log_debug("Failed to parse transient bool %s", v);
+ r = parse_boolean(v);
+ if (r < 0)
+ log_unit_debug(u, "Failed to parse transient bool %s, ignoring.", v);
else
- u->transient = b;
+ u->transient = r;
continue;
- } else if (streq(l, "cgroup")) {
- char *s;
- s = strdup(v);
- if (!s)
- return -ENOMEM;
+ } else if (streq(l, "cpuacct-usage-base")) {
- if (u->cgroup_path) {
- void *p;
+ r = safe_atou64(v, &u->cpuacct_usage_base);
+ if (r < 0)
+ log_unit_debug(u, "Failed to parse CPU usage %s, ignoring.", v);
- p = hashmap_remove(u->manager->cgroup_unit, u->cgroup_path);
- log_info("Removing cgroup_path %s from hashmap (%p)",
- u->cgroup_path, p);
- free(u->cgroup_path);
- }
+ continue;
- u->cgroup_path = s;
- assert(hashmap_put(u->manager->cgroup_unit, s, u) == 1);
+ } else if (streq(l, "cgroup")) {
+
+ r = unit_set_cgroup_path(u, v);
+ if (r < 0)
+ log_unit_debug_errno(u, r, "Failed to set cgroup path %s, ignoring: %m", v);
continue;
}
if (unit_can_serialize(u)) {
if (rt) {
- r = exec_runtime_deserialize_item(rt, u, l, v, fds);
- if (r < 0)
- return r;
+ r = exec_runtime_deserialize_item(u, rt, l, v, fds);
+ if (r < 0) {
+ log_unit_warning(u, "Failed to deserialize runtime parameter '%s', ignoring.", l);
+ continue;
+ }
+
+ /* Returns positive if key was handled by the call */
if (r > 0)
continue;
}
r = UNIT_VTABLE(u)->deserialize_item(u, l, v, fds);
if (r < 0)
- return r;
+ log_unit_warning(u, "Failed to deserialize unit parameter '%s', ignoring.", l);
}
}
}
@@ -2828,24 +2835,27 @@ int unit_add_node_link(Unit *u, const char *what, bool wants) {
assert(u);
- if (!what)
- return 0;
-
/* Adds in links to the device node that this unit is based on */
+ if (isempty(what))
+ return 0;
if (!is_device_path(what))
return 0;
- e = unit_name_from_path(what, ".device");
- if (!e)
- return -ENOMEM;
+ /* When device units aren't supported (such as in a
+ * container), don't create dependencies on them. */
+ if (!unit_type_supported(UNIT_DEVICE))
+ return 0;
- r = manager_load_unit(u->manager, e, NULL, NULL, &device);
+ r = unit_name_from_path(what, ".device", &e);
+ if (r < 0)
+ return r;
+ r = manager_load_unit(u->manager, e, NULL, NULL, &device);
if (r < 0)
return r;
- r = unit_add_two_dependencies(u, UNIT_AFTER, UNIT_BINDS_TO, device, true);
+ r = unit_add_two_dependencies(u, UNIT_AFTER, u->manager->running_as == MANAGER_SYSTEM ? UNIT_BINDS_TO : UNIT_WANTS, device, true);
if (r < 0)
return r;
@@ -2863,21 +2873,23 @@ int unit_coldplug(Unit *u) {
assert(u);
- if (UNIT_VTABLE(u)->coldplug)
- if ((r = UNIT_VTABLE(u)->coldplug(u)) < 0)
+ /* Make sure we don't enter a loop, when coldplugging
+ * recursively. */
+ if (u->coldplugged)
+ return 0;
+
+ u->coldplugged = true;
+
+ if (UNIT_VTABLE(u)->coldplug) {
+ r = UNIT_VTABLE(u)->coldplug(u);
+ if (r < 0)
return r;
+ }
if (u->job) {
r = job_coldplug(u->job);
if (r < 0)
return r;
- } else if (u->deserialized_job >= 0) {
- /* legacy */
- r = manager_add_job(u->manager, u->deserialized_job, u, JOB_IGNORE_REQUIREMENTS, false, NULL, NULL);
- if (r < 0)
- return r;
-
- u->deserialized_job = _JOB_TYPE_INVALID;
}
return 0;
@@ -3011,7 +3023,7 @@ int unit_kill(Unit *u, KillWho w, int signo, sd_bus_error *error) {
assert(signo < _NSIG);
if (!UNIT_VTABLE(u)->kill)
- return -ENOTSUP;
+ return -EOPNOTSUPP;
return UNIT_VTABLE(u)->kill(u, w, signo, error);
}
@@ -3111,7 +3123,7 @@ UnitFileState unit_get_unit_file_state(Unit *u) {
if (u->unit_file_state < 0 && u->fragment_path)
u->unit_file_state = unit_file_get_state(
- u->manager->running_as == SYSTEMD_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER,
+ u->manager->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER,
NULL, basename(u->fragment_path));
return u->unit_file_state;
@@ -3122,7 +3134,7 @@ int unit_get_unit_file_preset(Unit *u) {
if (u->unit_file_preset < 0 && u->fragment_path)
u->unit_file_preset = unit_file_query_preset(
- u->manager->running_as == SYSTEMD_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER,
+ u->manager->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER,
NULL, basename(u->fragment_path));
return u->unit_file_preset;
@@ -3172,7 +3184,7 @@ int unit_patch_contexts(Unit *u) {
return -ENOMEM;
}
- if (u->manager->running_as == SYSTEMD_USER &&
+ if (u->manager->running_as == MANAGER_USER &&
!ec->working_directory) {
r = get_home_dir(&ec->working_directory);
@@ -3184,7 +3196,7 @@ int unit_patch_contexts(Unit *u) {
ec->working_directory_missing_ok = true;
}
- if (u->manager->running_as == SYSTEMD_USER &&
+ if (u->manager->running_as == MANAGER_USER &&
(ec->syscall_whitelist ||
!set_isempty(ec->syscall_filter) ||
!set_isempty(ec->syscall_archs) ||
@@ -3263,7 +3275,7 @@ ExecRuntime *unit_get_exec_runtime(Unit *u) {
}
static int unit_drop_in_dir(Unit *u, UnitSetPropertiesMode mode, bool transient, char **dir) {
- if (u->manager->running_as == SYSTEMD_USER) {
+ if (u->manager->running_as == MANAGER_USER) {
int r;
if (mode == UNIT_PERSISTENT && !transient)
@@ -3432,7 +3444,7 @@ int unit_make_transient(Unit *u) {
free(u->fragment_path);
u->fragment_path = NULL;
- if (u->manager->running_as == SYSTEMD_USER) {
+ if (u->manager->running_as == MANAGER_USER) {
_cleanup_free_ char *c = NULL;
r = user_runtime_dir(&c);
@@ -3494,7 +3506,7 @@ int unit_kill_context(
_cleanup_free_ char *comm = NULL;
get_process_comm(main_pid, &comm);
- log_unit_warning_errno(u->id, r, "Failed to kill main process " PID_FMT " (%s): %m", main_pid, strna(comm));
+ log_unit_warning_errno(u, r, "Failed to kill main process " PID_FMT " (%s): %m", main_pid, strna(comm));
} else {
if (!main_pid_alien)
wait_for_exit = true;
@@ -3511,7 +3523,7 @@ int unit_kill_context(
_cleanup_free_ char *comm = NULL;
get_process_comm(control_pid, &comm);
- log_unit_warning_errno(u->id, r, "Failed to kill control process " PID_FMT " (%s): %m", control_pid, strna(comm));
+ log_unit_warning_errno(u, r, "Failed to kill control process " PID_FMT " (%s): %m", control_pid, strna(comm));
} else {
wait_for_exit = true;
@@ -3531,7 +3543,7 @@ int unit_kill_context(
r = cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, sig, true, true, false, pid_set);
if (r < 0) {
if (r != -EAGAIN && r != -ESRCH && r != -ENOENT)
- log_unit_warning_errno(u->id, r, "Failed to kill control group: %m");
+ log_unit_warning_errno(u, r, "Failed to kill control group: %m");
} else if (r > 0) {
/* FIXME: For now, we will not wait for the
@@ -3603,11 +3615,9 @@ int unit_require_mounts_for(Unit *u, const char *path) {
if (!x) {
char *q;
- if (!u->manager->units_requiring_mounts_for) {
- u->manager->units_requiring_mounts_for = hashmap_new(&string_hash_ops);
- if (!u->manager->units_requiring_mounts_for)
- return -ENOMEM;
- }
+ r = hashmap_ensure_allocated(&u->manager->units_requiring_mounts_for, &string_hash_ops);
+ if (r < 0)
+ return r;
q = strdup(prefix);
if (!q)
@@ -3662,6 +3672,64 @@ int unit_setup_exec_runtime(Unit *u) {
return exec_runtime_make(rt, unit_get_exec_context(u), u->id);
}
+bool unit_type_supported(UnitType t) {
+ if (_unlikely_(t < 0))
+ return false;
+ if (_unlikely_(t >= _UNIT_TYPE_MAX))
+ return false;
+
+ if (!unit_vtable[t]->supported)
+ return true;
+
+ return unit_vtable[t]->supported();
+}
+
+void unit_warn_if_dir_nonempty(Unit *u, const char* where) {
+ int r;
+
+ assert(u);
+ assert(where);
+
+ r = dir_is_empty(where);
+ if (r > 0)
+ return;
+ if (r < 0) {
+ log_unit_warning_errno(u, r, "Failed to check directory %s: %m", where);
+ return;
+ }
+
+ log_struct(LOG_NOTICE,
+ LOG_MESSAGE_ID(SD_MESSAGE_OVERMOUNTING),
+ LOG_UNIT_ID(u),
+ LOG_UNIT_MESSAGE(u, "Directory %s to mount over is not empty, mounting anyway.", where),
+ "WHERE=%s", where,
+ NULL);
+}
+
+int unit_fail_if_symlink(Unit *u, const char* where) {
+ int r;
+
+ assert(u);
+ assert(where);
+
+ r = is_symlink(where);
+ if (r < 0) {
+ log_unit_debug_errno(u, r, "Failed to check symlink %s, ignoring: %m", where);
+ return 0;
+ }
+ if (r == 0)
+ return 0;
+
+ log_struct(LOG_ERR,
+ LOG_MESSAGE_ID(SD_MESSAGE_OVERMOUNTING),
+ LOG_UNIT_ID(u),
+ LOG_UNIT_MESSAGE(u, "Mount on symlink %s not allowed.", where),
+ "WHERE=%s", where,
+ NULL);
+
+ return -ELOOP;
+}
+
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 291bc77a76..9491ef64f9 100644
--- a/src/core/unit.h
+++ b/src/core/unit.h
@@ -31,13 +31,7 @@ typedef enum UnitActiveState UnitActiveState;
typedef struct UnitRef UnitRef;
typedef struct UnitStatusMessageFormats UnitStatusMessageFormats;
-#include "sd-event.h"
-#include "set.h"
-#include "util.h"
#include "list.h"
-#include "socket-util.h"
-#include "execute.h"
-#include "cgroup.h"
#include "condition.h"
#include "install.h"
#include "unit-name.h"
@@ -58,6 +52,8 @@ typedef enum KillOperation {
KILL_TERMINATE,
KILL_KILL,
KILL_ABORT,
+ _KILL_OPERATION_MAX,
+ _KILL_OPERATION_INVALID = -1
} KillOperation;
static inline bool UNIT_IS_ACTIVE_OR_RELOADING(UnitActiveState t) {
@@ -76,7 +72,6 @@ static inline bool UNIT_IS_INACTIVE_OR_FAILED(UnitActiveState t) {
return t == UNIT_INACTIVE || t == UNIT_FAILED;
}
-#include "manager.h"
#include "job.h"
struct UnitRef {
@@ -109,6 +104,7 @@ 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;
@@ -170,19 +166,19 @@ struct Unit {
/* Used during GC sweeps */
unsigned gc_marker;
- /* When deserializing, temporarily store the job type for this
- * unit here, if there was a job scheduled.
- * Only for deserializing from a legacy version. New style uses full
- * serialized jobs. */
- int deserialized_job; /* This is actually of type JobType */
-
/* Error code when we didn't manage to load the unit (negative) */
int load_error;
+ /* Make sure we never enter endless loops with the check unneeded logic, or the BindsTo= logic */
+ RateLimit auto_stop_ratelimit;
+
/* Cached unit file state and preset */
UnitFileState unit_file_state;
int unit_file_preset;
+ /* Where the cpuacct.usage cgroup counter was at the time the unit was started */
+ nsec_t cpuacct_usage_base;
+
/* Counterparts in the cgroup filesystem */
char *cgroup_path;
CGroupControllerMask cgroup_realized_mask;
@@ -235,6 +231,9 @@ struct Unit {
bool cgroup_realized:1;
bool cgroup_members_mask_valid:1;
bool cgroup_subtree_mask_valid:1;
+
+ /* Did we already invoke unit_coldplug() for this unit? */
+ bool coldplugged:1;
};
struct UnitStatusMessageFormats {
@@ -249,13 +248,11 @@ typedef enum UnitSetPropertiesMode {
UNIT_PERSISTENT = 2,
} UnitSetPropertiesMode;
-#include "service.h"
#include "socket.h"
#include "busname.h"
#include "target.h"
#include "snapshot.h"
#include "device.h"
-#include "mount.h"
#include "automount.h"
#include "swap.h"
#include "timer.h"
@@ -402,7 +399,7 @@ struct UnitVTable {
/* If this function is set and return false all jobs for units
* of this type will immediately fail. */
- bool (*supported)(Manager *m);
+ bool (*supported)(void);
/* The interface name */
const char *bus_interface;
@@ -601,25 +598,38 @@ int unit_make_transient(Unit *u);
int unit_require_mounts_for(Unit *u, const char *path);
-const char *unit_active_state_to_string(UnitActiveState i) _const_;
-UnitActiveState unit_active_state_from_string(const char *s) _pure_;
+bool unit_type_supported(UnitType t);
-/* Macros which append UNIT= or USER_UNIT= to the message */
+static inline bool unit_supported(Unit *u) {
+ return unit_type_supported(u->type);
+}
-#define log_unit_full_errno(unit, level, error, ...) log_object_internal(level, error, __FILE__, __LINE__, __func__, getpid() == 1 ? "UNIT=" : "USER_UNIT=", unit, __VA_ARGS__)
-#define log_unit_full(unit, level, ...) log_unit_full_errno(unit, level, 0, __VA_ARGS__)
+void unit_warn_if_dir_nonempty(Unit *u, const char* where);
+int unit_fail_if_symlink(Unit *u, const char* where);
-#define log_unit_debug(unit, ...) log_unit_full(unit, LOG_DEBUG, __VA_ARGS__)
-#define log_unit_info(unit, ...) log_unit_full(unit, LOG_INFO, __VA_ARGS__)
-#define log_unit_notice(unit, ...) log_unit_full(unit, LOG_NOTICE, __VA_ARGS__)
-#define log_unit_warning(unit, ...) log_unit_full(unit, LOG_WARNING, __VA_ARGS__)
-#define log_unit_error(unit, ...) log_unit_full(unit, LOG_ERR, __VA_ARGS__)
+const char *unit_active_state_to_string(UnitActiveState i) _const_;
+UnitActiveState unit_active_state_from_string(const char *s) _pure_;
-#define log_unit_debug_errno(unit, error, ...) log_unit_full_errno(unit, LOG_DEBUG, error, __VA_ARGS__)
-#define log_unit_info_errno(unit, error, ...) log_unit_full_errno(unit, LOG_INFO, error, __VA_ARGS__)
-#define log_unit_notice_errno(unit, error, ...) log_unit_full_errno(unit, LOG_NOTICE, error, __VA_ARGS__)
-#define log_unit_warning_errno(unit, error, ...) log_unit_full_errno(unit, LOG_WARNING, error, __VA_ARGS__)
-#define log_unit_error_errno(unit, error, ...) log_unit_full_errno(unit, LOG_ERR, error, __VA_ARGS__)
+/* Macros which append UNIT= or USER_UNIT= to the message */
-#define log_unit_struct(unit, level, ...) log_struct(level, getpid() == 1 ? "UNIT=%s" : "USER_UNIT=%s", unit, __VA_ARGS__)
-#define log_unit_struct_errno(unit, level, error, ...) log_struct_errno(level, error, getpid() == 1 ? "UNIT=%s" : "USER_UNIT=%s", unit, __VA_ARGS__)
+#define log_unit_full(unit, level, error, ...) \
+ ({ \
+ Unit *_u = (unit); \
+ _u ? log_object_internal(level, error, __FILE__, __LINE__, __func__, _u->manager->unit_log_field, _u->id, ##__VA_ARGS__) : \
+ log_internal(level, error, __FILE__, __LINE__, __func__, ##__VA_ARGS__); \
+ })
+
+#define log_unit_debug(unit, ...) log_unit_full(unit, LOG_DEBUG, 0, ##__VA_ARGS__)
+#define log_unit_info(unit, ...) log_unit_full(unit, LOG_INFO, 0, ##__VA_ARGS__)
+#define log_unit_notice(unit, ...) log_unit_full(unit, LOG_NOTICE, 0, ##__VA_ARGS__)
+#define log_unit_warning(unit, ...) log_unit_full(unit, LOG_WARNING, 0, ##__VA_ARGS__)
+#define log_unit_error(unit, ...) log_unit_full(unit, LOG_ERR, 0, ##__VA_ARGS__)
+
+#define log_unit_debug_errno(unit, error, ...) log_unit_full(unit, LOG_DEBUG, error, ##__VA_ARGS__)
+#define log_unit_info_errno(unit, error, ...) log_unit_full(unit, LOG_INFO, error, ##__VA_ARGS__)
+#define log_unit_notice_errno(unit, error, ...) log_unit_full(unit, LOG_NOTICE, error, ##__VA_ARGS__)
+#define log_unit_warning_errno(unit, error, ...) log_unit_full(unit, LOG_WARNING, error, ##__VA_ARGS__)
+#define log_unit_error_errno(unit, error, ...) log_unit_full(unit, LOG_ERR, error, ##__VA_ARGS__)
+
+#define LOG_UNIT_MESSAGE(unit, fmt, ...) "MESSAGE=%s: " fmt, (unit)->id, ##__VA_ARGS__
+#define LOG_UNIT_ID(unit) (unit)->manager->unit_log_format_string, (unit)->id
diff --git a/src/cryptsetup/cryptsetup-generator.c b/src/cryptsetup/cryptsetup-generator.c
index 05061c0704..6fad8ad80c 100644
--- a/src/cryptsetup/cryptsetup-generator.c
+++ b/src/cryptsetup/cryptsetup-generator.c
@@ -20,11 +20,8 @@
***/
#include <errno.h>
-#include <string.h>
-#include <unistd.h>
#include "dropin.h"
-#include "fileio.h"
#include "generator.h"
#include "hashmap.h"
#include "log.h"
@@ -81,9 +78,9 @@ static int create_disk(
if (!e)
return log_oom();
- n = unit_name_build("systemd-cryptsetup", e, ".service");
- if (!n)
- return log_oom();
+ r = unit_name_build("systemd-cryptsetup", e, ".service", &n);
+ if (r < 0)
+ return log_error_errno(r, "Failed to generate unit name: %m");
p = strjoin(arg_dest, "/", n, NULL);
if (!p)
@@ -93,9 +90,9 @@ static int create_disk(
if (!u)
return log_oom();
- d = unit_name_from_path(u, ".device");
- if (!d)
- return log_oom();
+ r = unit_name_from_path(u, ".device", &d);
+ if (r < 0)
+ return log_error_errno(r, "Failed to generate unit name: %m");
f = fopen(p, "wxe");
if (!f)
@@ -131,11 +128,11 @@ static int create_disk(
if (!path_equal(uu, "/dev/null")) {
if (is_device_path(uu)) {
- _cleanup_free_ char *dd;
+ _cleanup_free_ char *dd = NULL;
- dd = unit_name_from_path(uu, ".device");
- if (!dd)
- return log_oom();
+ r = unit_name_from_path(uu, ".device", &dd);
+ if (r < 0)
+ return log_error_errno(r, "Failed to generate unit name: %m");
fprintf(f, "After=%1$s\nRequires=%1$s\n", dd);
} else
@@ -179,9 +176,9 @@ static int create_disk(
"ExecStartPost=/sbin/mkswap '/dev/mapper/%s'\n",
name);
- fflush(f);
- if (ferror(f))
- return log_error_errno(errno, "Failed to write file %s: %m", p);
+ r = fflush_and_check(f);
+ if (r < 0)
+ return log_error_errno(r, "Failed to write file %s: %m", p);
from = strjoina("../", n);
@@ -377,13 +374,6 @@ static int add_crypttab_devices(void) {
return 0;
}
- /* If we readd support for specifying passphrases
- * directly in crypttab 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.");
-
for (;;) {
int r, k;
char line[LINE_MAX], *l, *uuid;
diff --git a/src/cryptsetup/cryptsetup.c b/src/cryptsetup/cryptsetup.c
index 3f613d9b65..a5018f13ed 100644
--- a/src/cryptsetup/cryptsetup.c
+++ b/src/cryptsetup/cryptsetup.c
@@ -32,9 +32,8 @@
#include "path-util.h"
#include "strv.h"
#include "ask-password-api.h"
-#include "def.h"
-#include "libudev.h"
-#include "udev-util.h"
+#include "sd-device.h"
+#include "device-util.h"
static const char *arg_type = NULL; /* CRYPT_LUKS1, CRYPT_TCRYPT or CRYPT_PLAIN */
static char *arg_cipher = NULL;
@@ -51,12 +50,12 @@ static bool arg_discards = false;
static bool arg_tcrypt_hidden = false;
static bool arg_tcrypt_system = false;
static char **arg_tcrypt_keyfiles = NULL;
+static uint64_t arg_offset = 0;
+static uint64_t arg_skip = 0;
static usec_t arg_timeout = 0;
/* Options Debian's crypttab knows we don't:
- offset=
- skip=
precheck=
check=
checkargs=
@@ -186,6 +185,20 @@ static int parse_one_option(const char *option) {
return 0;
}
+ } else if (startswith(option, "offset=")) {
+
+ if (safe_atou64(option+7, &arg_offset) < 0) {
+ log_error("offset= parse failure, refusing.");
+ return -EINVAL;
+ }
+
+ } else if (startswith(option, "skip=")) {
+
+ if (safe_atou64(option+5, &arg_skip) < 0) {
+ log_error("skip= parse failure, refusing.");
+ return -EINVAL;
+ }
+
} else if (!streq(option, "none"))
log_error("Encountered unknown /etc/crypttab option '%s', ignoring.", option);
@@ -210,6 +223,14 @@ static int parse_options(const char *options) {
return r;
}
+ /* sanity-check options */
+ if (arg_type != NULL && !streq(arg_type, CRYPT_PLAIN)) {
+ if (arg_offset)
+ log_warning("offset= ignored with type %s", arg_type);
+ if (arg_skip)
+ log_warning("skip= ignored with type %s", arg_type);
+ }
+
return 0;
}
@@ -225,10 +246,10 @@ static char* disk_description(const char *path) {
"ID_MODEL_FROM_DATABASE\0"
"ID_MODEL\0";
- _cleanup_udev_unref_ struct udev *udev = NULL;
- _cleanup_udev_device_unref_ struct udev_device *device = NULL;
+ _cleanup_device_unref_ sd_device *device = NULL;
struct stat st;
const char *i;
+ int r;
assert(path);
@@ -238,19 +259,15 @@ static char* disk_description(const char *path) {
if (!S_ISBLK(st.st_mode))
return NULL;
- udev = udev_new();
- if (!udev)
- return NULL;
-
- device = udev_device_new_from_devnum(udev, 'b', st.st_rdev);
- if (!device)
+ r = sd_device_new_from_devnum(&device, 'b', st.st_rdev);
+ if (r < 0)
return NULL;
NULSTR_FOREACH(i, name_fields) {
const char *name;
- name = udev_device_get_property_value(device, i);
- if (!isempty(name))
+ r = sd_device_get_property_value(device, i, &name);
+ if (r >= 0 && !isempty(name))
return strdup(name);
}
@@ -415,7 +432,10 @@ static int attach_luks_or_plain(struct crypt_device *cd,
}
if ((!arg_type && r < 0) || streq_ptr(arg_type, CRYPT_PLAIN)) {
- struct crypt_params_plain params = {};
+ struct crypt_params_plain params = {
+ .offset = arg_offset,
+ .skip = arg_skip,
+ };
const char *cipher, *cipher_mode;
_cleanup_free_ char *truncated_cipher = NULL;
diff --git a/src/dbus1-generator/dbus1-generator.c b/src/dbus1-generator/dbus1-generator.c
index 2e08af2df2..4980fccc31 100644
--- a/src/dbus1-generator/dbus1-generator.c
+++ b/src/dbus1-generator/dbus1-generator.c
@@ -188,7 +188,7 @@ static int add_dbus(const char *path, const char *fname, const char *type) {
}
if (service) {
- if (!unit_name_is_valid(service, TEMPLATE_INVALID)) {
+ if (!unit_name_is_valid(service, UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE)) {
log_warning("Unit name %s is not valid, ignoring.", service);
return 0;
}
@@ -302,7 +302,7 @@ int main(int argc, char *argv[]) {
umask(0022);
- if (access("/sys/fs/kdbus/control", F_OK) < 0)
+ if (!is_kdbus_available())
return 0;
r = cg_pid_get_owner_uid(0, NULL);
@@ -310,7 +310,7 @@ int main(int argc, char *argv[]) {
path = "/usr/share/dbus-1/services";
type = "session";
units = USER_DATA_UNIT_PATH;
- } else if (r == -ENOENT) {
+ } else if (r == -ENXIO) {
path = "/usr/share/dbus-1/system-services";
type = "system";
units = SYSTEM_DATA_UNIT_PATH;
diff --git a/src/debug-generator/debug-generator.c b/src/debug-generator/debug-generator.c
index 1b9019325c..9d0ab06e2f 100644
--- a/src/debug-generator/debug-generator.c
+++ b/src/debug-generator/debug-generator.c
@@ -41,9 +41,9 @@ static int parse_proc_cmdline_item(const char *key, const char *value) {
else {
char *n;
- n = unit_name_mangle(value, MANGLE_NOGLOB);
- if (!n)
- return log_oom();
+ r = unit_name_mangle(value, UNIT_NAME_NOGLOB, &n);
+ if (r < 0)
+ return log_error_errno(r, "Failed to glob unit name: %m");
r = strv_consume(&arg_mask, n);
if (r < 0)
@@ -57,9 +57,9 @@ static int parse_proc_cmdline_item(const char *key, const char *value) {
else {
char *n;
- n = unit_name_mangle(value, MANGLE_NOGLOB);
- if (!n)
- return log_oom();
+ r = unit_name_mangle(value, UNIT_NAME_NOGLOB, &n);
+ if (r < 0)
+ return log_error_errno(r, "Failed to glob unit name: %m");
r = strv_consume(&arg_wants, n);
if (r < 0)
diff --git a/src/delta/delta.c b/src/delta/delta.c
index de963f7009..c764bb4b46 100644
--- a/src/delta/delta.c
+++ b/src/delta/delta.c
@@ -21,7 +21,6 @@
***/
#include <errno.h>
-#include <assert.h>
#include <string.h>
#include <unistd.h>
#include <getopt.h>
@@ -33,6 +32,8 @@
#include "pager.h"
#include "build.h"
#include "strv.h"
+#include "process-util.h"
+#include "terminal-util.h"
static const char prefixes[] =
"/etc\0"
diff --git a/src/detect-virt/detect-virt.c b/src/detect-virt/detect-virt.c
index 9a924117db..606d073cbc 100644
--- a/src/detect-virt/detect-virt.c
+++ b/src/detect-virt/detect-virt.c
@@ -22,7 +22,6 @@
#include <stdlib.h>
#include <stdbool.h>
#include <errno.h>
-#include <string.h>
#include <getopt.h>
#include "util.h"
diff --git a/src/efi-boot-generator/efi-boot-generator.c b/src/efi-boot-generator/efi-boot-generator.c
index b3ff3a8b78..e6b15c9bb0 100644
--- a/src/efi-boot-generator/efi-boot-generator.c
+++ b/src/efi-boot-generator/efi-boot-generator.c
@@ -26,7 +26,6 @@
#include "path-util.h"
#include "util.h"
#include "mkdir.h"
-#include "unit-name.h"
#include "virt.h"
#include "generator.h"
#include "special.h"
@@ -69,8 +68,14 @@ int main(int argc, char *argv[]) {
return EXIT_SUCCESS;
}
- if (path_is_mount_point("/boot", true) <= 0 &&
- dir_is_empty("/boot") <= 0) {
+ r = path_is_mount_point("/boot", AT_SYMLINK_FOLLOW);
+ if (r > 0) {
+ log_debug("/boot is already a mount point, exiting.");
+ return EXIT_SUCCESS;
+ }
+ if (r == -ENOENT)
+ log_debug("/boot does not exist, continuing.");
+ else if (dir_is_empty("/boot") <= 0) {
log_debug("/boot already populated, exiting.");
return EXIT_SUCCESS;
}
@@ -118,9 +123,9 @@ int main(int argc, char *argv[]) {
"Options=umask=0077,noauto\n",
what);
- fflush(f);
- if (ferror(f)) {
- log_error_errno(errno, "Failed to write mount unit file: %m");
+ r = fflush_and_check(f);
+ if (r < 0) {
+ log_error_errno(r, "Failed to write mount unit file: %m");
return EXIT_FAILURE;
}
@@ -136,11 +141,12 @@ int main(int argc, char *argv[]) {
"[Unit]\n"
"Description=EFI System Partition Automount\n\n"
"[Automount]\n"
- "Where=/boot\n", f);
+ "Where=/boot\n"
+ "TimeoutIdleSec=120\n", f);
- fflush(f);
- if (ferror(f)) {
- log_error_errno(errno, "Failed to write automount unit file: %m");
+ r = fflush_and_check(f);
+ if (r < 0) {
+ log_error_errno(r, "Failed to write automount unit file: %m");
return EXIT_FAILURE;
}
diff --git a/src/escape/escape.c b/src/escape/escape.c
index f2a0721861..9ccb015538 100644
--- a/src/escape/escape.c
+++ b/src/escape/escape.c
@@ -99,7 +99,7 @@ static int parse_argv(int argc, char *argv[]) {
case ARG_TEMPLATE:
- if (!unit_name_is_valid(optarg, true) || !unit_name_is_template(optarg)) {
+ if (!unit_name_is_valid(optarg, UNIT_NAME_TEMPLATE)) {
log_error("Template name %s is not valid.", optarg);
return -EINVAL;
}
@@ -166,22 +166,26 @@ int main(int argc, char *argv[]) {
switch (arg_action) {
case ACTION_ESCAPE:
- if (arg_path)
- e = unit_name_path_escape(*i);
- else
+ if (arg_path) {
+ r = unit_name_path_escape(*i, &e);
+ if (r < 0) {
+ log_error_errno(r, "Failed to escape string: %m");
+ goto finish;
+ }
+ } else {
e = unit_name_escape(*i);
-
- if (!e) {
- r = log_oom();
- goto finish;
+ if (!e) {
+ r = log_oom();
+ goto finish;
+ }
}
if (arg_template) {
char *x;
- x = unit_name_replace_instance(arg_template, e);
- if (!x) {
- r = log_oom();
+ r = unit_name_replace_instance(arg_template, e, &x);
+ if (r < 0) {
+ log_error_errno(r, "Failed to replace instance: %m");
goto finish;
}
@@ -204,20 +208,20 @@ int main(int argc, char *argv[]) {
case ACTION_UNESCAPE:
if (arg_path)
- e = unit_name_path_unescape(*i);
+ r = unit_name_path_unescape(*i, &e);
else
- e = unit_name_unescape(*i);
+ r = unit_name_unescape(*i, &e);
- if (!e) {
- r = log_oom();
+ if (r < 0) {
+ log_error_errno(r, "Failed to unescape string: %m");
goto finish;
}
break;
case ACTION_MANGLE:
- e = unit_name_mangle(*i, MANGLE_NOGLOB);
- if (!e) {
- r = log_oom();
+ r = unit_name_mangle(*i, UNIT_NAME_NOGLOB, &e);
+ if (r < 0) {
+ log_error_errno(r, "Failed to mangle name: %m");
goto finish;
}
break;
diff --git a/src/firstboot/firstboot.c b/src/firstboot/firstboot.c
index a765d6d219..cda96d484a 100644
--- a/src/firstboot/firstboot.c
+++ b/src/firstboot/firstboot.c
@@ -32,8 +32,11 @@
#include "mkdir.h"
#include "time-util.h"
#include "path-util.h"
+#include "random-util.h"
#include "locale-util.h"
#include "ask-password-api.h"
+#include "terminal-util.h"
+#include "hostname-util.h"
static char *arg_root = NULL;
static char *arg_locale = NULL; /* $LANG */
@@ -50,8 +53,6 @@ static bool arg_copy_locale = false;
static bool arg_copy_timezone = false;
static bool arg_copy_root_password = false;
-#define prefix_roota(p) (arg_root ? (const char*) strjoina(arg_root, p) : (const char*) p)
-
static void clear_string(char *x) {
if (!x)
@@ -85,13 +86,13 @@ static void print_welcome(void) {
if (done)
return;
- os_release = prefix_roota("/etc/os-release");
+ os_release = prefix_roota(arg_root, "/etc/os-release");
r = parse_env_file(os_release, NEWLINE,
"PRETTY_NAME", &pretty_name,
NULL);
if (r == -ENOENT) {
- os_release = prefix_roota("/usr/lib/os-release");
+ os_release = prefix_roota(arg_root, "/usr/lib/os-release");
r = parse_env_file(os_release, NEWLINE,
"PRETTY_NAME", &pretty_name,
NULL);
@@ -249,7 +250,7 @@ static int process_locale(void) {
unsigned i = 0;
int r;
- etc_localeconf = prefix_roota("/etc/locale.conf");
+ etc_localeconf = prefix_roota(arg_root, "/etc/locale.conf");
if (faccessat(AT_FDCWD, etc_localeconf, F_OK, AT_SYMLINK_NOFOLLOW) >= 0)
return 0;
@@ -323,7 +324,7 @@ static int process_timezone(void) {
const char *etc_localtime, *e;
int r;
- etc_localtime = prefix_roota("/etc/localtime");
+ etc_localtime = prefix_roota(arg_root, "/etc/localtime");
if (faccessat(AT_FDCWD, etc_localtime, F_OK, AT_SYMLINK_NOFOLLOW) >= 0)
return 0;
@@ -402,7 +403,7 @@ static int process_hostname(void) {
const char *etc_hostname;
int r;
- etc_hostname = prefix_roota("/etc/hostname");
+ etc_hostname = prefix_roota(arg_root, "/etc/hostname");
if (faccessat(AT_FDCWD, etc_hostname, F_OK, AT_SYMLINK_NOFOLLOW) >= 0)
return 0;
@@ -427,7 +428,7 @@ static int process_machine_id(void) {
char id[SD_ID128_STRING_MAX];
int r;
- etc_machine_id = prefix_roota("/etc/machine-id");
+ etc_machine_id = prefix_roota(arg_root, "/etc/machine-id");
if (faccessat(AT_FDCWD, etc_machine_id, F_OK, AT_SYMLINK_NOFOLLOW) >= 0)
return 0;
@@ -453,7 +454,7 @@ static int prompt_root_password(void) {
if (!arg_prompt_root_password)
return 0;
- etc_shadow = prefix_roota("/etc/shadow");
+ etc_shadow = prefix_roota(arg_root, "/etc/shadow");
if (faccessat(AT_FDCWD, etc_shadow, F_OK, AT_SYMLINK_NOFOLLOW) >= 0)
return 0;
@@ -525,9 +526,9 @@ static int process_root_password(void) {
struct spwd item = {
.sp_namp = (char*) "root",
- .sp_min = 0,
- .sp_max = 99999,
- .sp_warn = 7,
+ .sp_min = -1,
+ .sp_max = -1,
+ .sp_warn = -1,
.sp_inact = -1,
.sp_expire = -1,
.sp_flag = (unsigned long) -1, /* this appears to be what everybody does ... */
@@ -542,7 +543,7 @@ static int process_root_password(void) {
const char *etc_shadow;
int r;
- etc_shadow = prefix_roota("/etc/shadow");
+ etc_shadow = prefix_roota(arg_root, "/etc/shadow");
if (faccessat(AT_FDCWD, etc_shadow, F_OK, AT_SYMLINK_NOFOLLOW) >= 0)
return 0;
diff --git a/src/fsck/fsck.c b/src/fsck/fsck.c
index 78ceeb6fab..f0e5c5f239 100644
--- a/src/fsck/fsck.c
+++ b/src/fsck/fsck.c
@@ -22,23 +22,38 @@
#include <stdio.h>
#include <stdbool.h>
-#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/prctl.h>
#include "sd-bus.h"
-#include "libudev.h"
+#include "sd-device.h"
#include "util.h"
+#include "process-util.h"
+#include "signal-util.h"
#include "special.h"
#include "bus-util.h"
#include "bus-error.h"
#include "bus-common-errors.h"
-#include "fileio.h"
-#include "udev-util.h"
+#include "device-util.h"
#include "path-util.h"
+#include "socket-util.h"
+
+/* exit codes as defined in fsck(8) */
+enum {
+ FSCK_SUCCESS = 0,
+ FSCK_ERROR_CORRECTED = 1,
+ FSCK_SYSTEM_SHOULD_REBOOT = 2,
+ FSCK_ERRORS_LEFT_UNCORRECTED = 4,
+ FSCK_OPERATIONAL_ERROR = 8,
+ FSCK_USAGE_OR_SYNTAX_ERROR = 16,
+ FSCK_USER_CANCELLED = 32,
+ FSCK_SHARED_LIB_ERROR = 128,
+};
static bool arg_skip = false;
static bool arg_force = false;
@@ -72,10 +87,13 @@ static void start_target(const char *target) {
/* Don't print a warning if we aren't called during startup */
if (r < 0 && !sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_JOB))
- log_error("Failed to start unit: %s", bus_error_message(&error, -r));
+ log_error("Failed to start unit: %s", bus_error_message(&error, r));
}
static int parse_proc_cmdline_item(const char *key, const char *value) {
+ int r;
+
+ assert(key);
if (streq(key, "fsck.mode") && value) {
@@ -92,12 +110,15 @@ static int parse_proc_cmdline_item(const char *key, const char *value) {
if (streq(value, "preen"))
arg_repair = "-a";
- else if (streq(value, "yes"))
- arg_repair = "-y";
- else if (streq(value, "no"))
- arg_repair = "-n";
- else
- log_warning("Invalid fsck.repair= parameter '%s'. Ignoring.", value);
+ else {
+ r = parse_boolean(value);
+ if (r > 0)
+ arg_repair = "-y";
+ else if (r == 0)
+ arg_repair = "-n";
+ else
+ log_warning("Invalid fsck.repair= parameter '%s'. Ignoring.", value);
+ }
}
#ifdef HAVE_SYSV_COMPAT
@@ -128,8 +149,7 @@ static void test_files(void) {
}
#endif
- if (access("/run/systemd/show-status", F_OK) >= 0 || plymouth_running())
- arg_show_progress = true;
+ arg_show_progress = access("/run/systemd/show-status", F_OK) >= 0;
}
static double percent(int pass, unsigned long cur, unsigned long max) {
@@ -154,9 +174,13 @@ static int process_progress(int fd) {
_cleanup_fclose_ FILE *console = NULL, *f = NULL;
usec_t last = 0;
bool locked = false;
- int clear = 0;
+ int clear = 0, r;
+
+ /* No progress pipe to process? Then we are a NOP. */
+ if (fd < 0)
+ return 0;
- f = fdopen(fd, "r");
+ f = fdopen(fd, "re");
if (!f) {
safe_close(fd);
return -errno;
@@ -166,15 +190,25 @@ static int process_progress(int fd) {
if (!console)
return -ENOMEM;
- while (!feof(f)) {
+ for (;;) {
int pass, m;
unsigned long cur, max;
_cleanup_free_ char *device = NULL;
double p;
usec_t t;
- if (fscanf(f, "%i %lu %lu %ms", &pass, &cur, &max, &device) != 4)
+ if (fscanf(f, "%i %lu %lu %ms", &pass, &cur, &max, &device) != 4) {
+
+ if (ferror(f))
+ r = log_warning_errno(errno, "Failed to read from progress pipe: %m");
+ else if (feof(f))
+ r = 0;
+ else {
+ log_warning("Failed to parse progress pipe data");
+ r = -EBADMSG;
+ }
break;
+ }
/* Only show one progress counter at max */
if (!locked) {
@@ -209,21 +243,40 @@ static int process_progress(int fd) {
fflush(console);
}
- return 0;
+ return r;
+}
+
+static int fsck_progress_socket(void) {
+ static const union sockaddr_union sa = {
+ .un.sun_family = AF_UNIX,
+ .un.sun_path = "/run/systemd/fsck.progress",
+ };
+
+ int fd, r;
+
+ fd = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (fd < 0)
+ return log_warning_errno(errno, "socket(): %m");
+
+ if (connect(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path)) < 0) {
+ r = log_full_errno(errno == ECONNREFUSED || errno == ENOENT ? LOG_DEBUG : LOG_WARNING,
+ errno, "Failed to connect to progress socket %s, ignoring: %m", sa.un.sun_path);
+ safe_close(fd);
+ return r;
+ }
+
+ return fd;
}
int main(int argc, char *argv[]) {
- const char *cmdline[9];
- int i = 0, r = EXIT_FAILURE, q;
- pid_t pid;
- siginfo_t status;
- _cleanup_udev_unref_ struct udev *udev = NULL;
- _cleanup_udev_device_unref_ struct udev_device *udev_device = NULL;
+ _cleanup_close_pair_ int progress_pipe[2] = { -1, -1 };
+ _cleanup_device_unref_ sd_device *dev = NULL;
const char *device, *type;
bool root_directory;
- int progress_pipe[2] = { -1, -1 };
- char dash_c[sizeof("-C")-1 + DECIMAL_STR_MAX(int) + 1];
+ siginfo_t status;
struct stat st;
+ int r;
+ pid_t pid;
if (argc > 2) {
log_error("This program expects one or no arguments.");
@@ -236,135 +289,164 @@ int main(int argc, char *argv[]) {
umask(0022);
- q = parse_proc_cmdline(parse_proc_cmdline_item);
- if (q < 0)
- log_warning_errno(q, "Failed to parse kernel command line, ignoring: %m");
+ r = parse_proc_cmdline(parse_proc_cmdline_item);
+ if (r < 0)
+ log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m");
test_files();
- if (!arg_force && arg_skip)
- return 0;
-
- udev = udev_new();
- if (!udev) {
- log_oom();
- return EXIT_FAILURE;
+ if (!arg_force && arg_skip) {
+ r = 0;
+ goto finish;
}
if (argc > 1) {
device = argv[1];
- root_directory = false;
if (stat(device, &st) < 0) {
- log_error_errno(errno, "Failed to stat '%s': %m", device);
- return EXIT_FAILURE;
+ r = log_error_errno(errno, "Failed to stat %s: %m", device);
+ goto finish;
+ }
+
+ if (!S_ISBLK(st.st_mode)) {
+ log_error("%s is not a block device.", device);
+ r = -EINVAL;
+ goto finish;
}
- udev_device = udev_device_new_from_devnum(udev, 'b', st.st_rdev);
- if (!udev_device) {
- log_error("Failed to detect device %s", device);
- return EXIT_FAILURE;
+ r = sd_device_new_from_devnum(&dev, 'b', st.st_rdev);
+ if (r < 0) {
+ log_error_errno(r, "Failed to detect device %s: %m", device);
+ goto finish;
}
+
+ root_directory = false;
} else {
struct timespec times[2];
/* Find root device */
if (stat("/", &st) < 0) {
- log_error_errno(errno, "Failed to stat() the root directory: %m");
- return EXIT_FAILURE;
+ r = log_error_errno(errno, "Failed to stat() the root directory: %m");
+ goto finish;
}
/* Virtual root devices don't need an fsck */
- if (major(st.st_dev) == 0)
- return EXIT_SUCCESS;
+ if (major(st.st_dev) == 0) {
+ log_debug("Root directory is virtual or btrfs, skipping check.");
+ r = 0;
+ goto finish;
+ }
/* check if we are already writable */
times[0] = st.st_atim;
times[1] = st.st_mtim;
+
if (utimensat(AT_FDCWD, "/", times, 0) == 0) {
log_info("Root directory is writable, skipping check.");
- return EXIT_SUCCESS;
+ r = 0;
+ goto finish;
}
- udev_device = udev_device_new_from_devnum(udev, 'b', st.st_dev);
- if (!udev_device) {
- log_error("Failed to detect root device.");
- return EXIT_FAILURE;
+ r = sd_device_new_from_devnum(&dev, 'b', st.st_dev);
+ if (r < 0) {
+ log_error_errno(r, "Failed to detect root device: %m");
+ goto finish;
}
- device = udev_device_get_devnode(udev_device);
- if (!device) {
- log_error("Failed to detect device node of root directory.");
- return EXIT_FAILURE;
+ r = sd_device_get_devname(dev, &device);
+ if (r < 0) {
+ log_error_errno(r, "Failed to detect device node of root directory: %m");
+ goto finish;
}
root_directory = true;
}
- type = udev_device_get_property_value(udev_device, "ID_FS_TYPE");
- if (type) {
+ r = sd_device_get_property_value(dev, "ID_FS_TYPE", &type);
+ if (r >= 0) {
r = fsck_exists(type);
if (r == -ENOENT) {
log_info("fsck.%s doesn't exist, not checking file system on %s", type, device);
- return EXIT_SUCCESS;
+ r = 0;
+ goto finish;
} else if (r < 0)
- log_warning_errno(r, "fsck.%s cannot be used for %s: %m", type, device);
+ log_warning_errno(r, "Couldn't detect if fsck.%s may be used for %s: %m", type, device);
}
- if (arg_show_progress)
+ if (arg_show_progress) {
if (pipe(progress_pipe) < 0) {
- log_error_errno(errno, "pipe(): %m");
- return EXIT_FAILURE;
+ r = log_error_errno(errno, "pipe(): %m");
+ goto finish;
}
+ }
- cmdline[i++] = "/sbin/fsck";
- cmdline[i++] = arg_repair;
- cmdline[i++] = "-T";
+ pid = fork();
+ if (pid < 0) {
+ r = log_error_errno(errno, "fork(): %m");
+ goto finish;
+ }
+ if (pid == 0) {
+ char dash_c[sizeof("-C")-1 + DECIMAL_STR_MAX(int) + 1];
+ int progress_socket = -1;
+ const char *cmdline[9];
+ int i = 0;
- /*
- * Since util-linux v2.25 fsck uses /run/fsck/<diskname>.lock files.
- * The previous versions use flock for the device and conflict with
- * udevd, see https://bugs.freedesktop.org/show_bug.cgi?id=79576#c5
- */
- cmdline[i++] = "-l";
+ /* Child */
- if (!root_directory)
- cmdline[i++] = "-M";
+ reset_all_signal_handlers();
+ reset_signal_mask();
+ assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
- if (arg_force)
- cmdline[i++] = "-f";
+ /* Close the reading side of the progress pipe */
+ progress_pipe[0] = safe_close(progress_pipe[0]);
- if (progress_pipe[1] >= 0) {
- xsprintf(dash_c, "-C%i", progress_pipe[1]);
- cmdline[i++] = dash_c;
- }
+ /* Try to connect to a progress management daemon, if there is one */
+ progress_socket = fsck_progress_socket();
+ if (progress_socket >= 0) {
+ /* If this worked we close the progress pipe early, and just use the socket */
+ progress_pipe[1] = safe_close(progress_pipe[1]);
+ xsprintf(dash_c, "-C%i", progress_socket);
+ } else if (progress_pipe[1] >= 0) {
+ /* Otherwise if we have the progress pipe to our own local handle, we use it */
+ xsprintf(dash_c, "-C%i", progress_pipe[1]);
+ } else
+ dash_c[0] = 0;
- cmdline[i++] = device;
- cmdline[i++] = NULL;
+ cmdline[i++] = "/sbin/fsck";
+ cmdline[i++] = arg_repair;
+ cmdline[i++] = "-T";
+
+ /*
+ * Since util-linux v2.25 fsck uses /run/fsck/<diskname>.lock files.
+ * The previous versions use flock for the device and conflict with
+ * udevd, see https://bugs.freedesktop.org/show_bug.cgi?id=79576#c5
+ */
+ cmdline[i++] = "-l";
+
+ if (!root_directory)
+ cmdline[i++] = "-M";
+
+ if (arg_force)
+ cmdline[i++] = "-f";
+
+ if (!isempty(dash_c))
+ cmdline[i++] = dash_c;
+
+ cmdline[i++] = device;
+ cmdline[i++] = NULL;
- pid = fork();
- if (pid < 0) {
- log_error_errno(errno, "fork(): %m");
- goto finish;
- } else if (pid == 0) {
- /* Child */
- if (progress_pipe[0] >= 0)
- safe_close(progress_pipe[0]);
execv(cmdline[0], (char**) cmdline);
- _exit(8); /* Operational error */
+ _exit(FSCK_OPERATIONAL_ERROR);
}
progress_pipe[1] = safe_close(progress_pipe[1]);
+ (void) process_progress(progress_pipe[0]);
+ progress_pipe[0] = -1;
- if (progress_pipe[0] >= 0) {
- process_progress(progress_pipe[0]);
- progress_pipe[0] = -1;
- }
-
- q = wait_for_terminate(pid, &status);
- if (q < 0) {
- log_error_errno(q, "waitid(): %m");
+ r = wait_for_terminate(pid, &status);
+ if (r < 0) {
+ log_error_errno(r, "waitid(): %m");
goto finish;
}
@@ -377,25 +459,25 @@ int main(int argc, char *argv[]) {
else
log_error("fsck failed due to unknown reason.");
- if (status.si_code == CLD_EXITED && (status.si_status & 2) && root_directory)
+ r = -EINVAL;
+
+ if (status.si_code == CLD_EXITED && (status.si_status & FSCK_SYSTEM_SHOULD_REBOOT) && root_directory)
/* System should be rebooted. */
start_target(SPECIAL_REBOOT_TARGET);
- else if (status.si_code == CLD_EXITED && (status.si_status & 6))
+ else if (status.si_code == CLD_EXITED && (status.si_status & (FSCK_SYSTEM_SHOULD_REBOOT | FSCK_ERRORS_LEFT_UNCORRECTED)))
/* Some other problem */
start_target(SPECIAL_EMERGENCY_TARGET);
else {
- r = EXIT_SUCCESS;
log_warning("Ignoring error.");
+ r = 0;
}
} else
- r = EXIT_SUCCESS;
+ r = 0;
- if (status.si_code == CLD_EXITED && (status.si_status & 1))
- touch("/run/systemd/quotacheck");
+ if (status.si_code == CLD_EXITED && (status.si_status & FSCK_ERROR_CORRECTED))
+ (void) touch("/run/systemd/quotacheck");
finish:
- safe_close_pair(progress_pipe);
-
- return r;
+ return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}
diff --git a/src/fstab-generator/fstab-generator.c b/src/fstab-generator/fstab-generator.c
index 5662b5fde1..a88b68e2c0 100644
--- a/src/fstab-generator/fstab-generator.c
+++ b/src/fstab-generator/fstab-generator.c
@@ -33,7 +33,6 @@
#include "mount-setup.h"
#include "special.h"
#include "mkdir.h"
-#include "fileio.h"
#include "generator.h"
#include "strv.h"
#include "virt.h"
@@ -56,7 +55,7 @@ static int add_swap(
_cleanup_free_ char *name = NULL, *unit = NULL, *lnk = NULL;
_cleanup_fclose_ FILE *f = NULL;
- int r, pri = -1;
+ int r;
assert(what);
assert(me);
@@ -71,13 +70,9 @@ static int add_swap(
return 0;
}
- r = fstab_find_pri(me->mnt_opts, &pri);
+ r = unit_name_from_path(what, ".swap", &name);
if (r < 0)
- return log_error_errno(r, "Failed to parse priority: %m");
-
- name = unit_name_from_path(what, ".swap");
- if (!name)
- return log_oom();
+ return log_error_errno(r, "Failed to generate unit name: %m");
unit = strjoin(arg_dest, "/", name, NULL);
if (!unit)
@@ -101,11 +96,6 @@ static int add_swap(
"What=%s\n",
what);
- /* Note that we currently pass the priority field twice, once
- * in Priority=, and once in Options= */
- if (pri >= 0)
- fprintf(f, "Priority=%i\n", pri);
-
if (!isempty(me->mnt_opts) && !streq(me->mnt_opts, "defaults"))
fprintf(f, "Options=%s\n", me->mnt_opts);
@@ -146,6 +136,88 @@ static bool mount_in_initrd(struct mntent *me) {
streq(me->mnt_dir, "/usr");
}
+static int write_idle_timeout(FILE *f, const char *where, const char *opts) {
+ _cleanup_free_ char *timeout = NULL;
+ char timespan[FORMAT_TIMESPAN_MAX];
+ usec_t u;
+ int r;
+
+ r = fstab_filter_options(opts, "x-systemd.idle-timeout\0", NULL, &timeout, NULL);
+ if (r < 0)
+ return log_warning_errno(r, "Failed to parse options: %m");
+ if (r == 0)
+ return 0;
+
+ r = parse_sec(timeout, &u);
+ if (r < 0) {
+ log_warning("Failed to parse timeout for %s, ignoring: %s", where, timeout);
+ return 0;
+ }
+
+ fprintf(f, "TimeoutIdleSec=%s\n", format_timespan(timespan, sizeof(timespan), u, 0));
+
+ return 0;
+}
+
+static int write_requires_after(FILE *f, const char *opts) {
+ _cleanup_strv_free_ char **names = NULL, **units = NULL;
+ _cleanup_free_ char *res = NULL;
+ char **s;
+ int r;
+
+ assert(f);
+ assert(opts);
+
+ r = fstab_extract_values(opts, "x-systemd.requires", &names);
+ if (r < 0)
+ return log_warning_errno(r, "Failed to parse options: %m");
+ if (r == 0)
+ return 0;
+
+ STRV_FOREACH(s, names) {
+ char *x;
+
+ r = unit_name_mangle_with_suffix(*s, UNIT_NAME_NOGLOB, ".mount", &x);
+ if (r < 0)
+ return log_error_errno(r, "Failed to generate unit name: %m");
+ r = strv_consume(&units, x);
+ if (r < 0)
+ return log_oom();
+ }
+
+ if (units) {
+ res = strv_join(units, " ");
+ if (!res)
+ return log_oom();
+ fprintf(f, "After=%1$s\nRequires=%1$s\n", res);
+ }
+
+ return 0;
+}
+
+static int write_requires_mounts_for(FILE *f, const char *opts) {
+ _cleanup_strv_free_ char **paths = NULL;
+ _cleanup_free_ char *res = NULL;
+ int r;
+
+ assert(f);
+ assert(opts);
+
+ r = fstab_extract_values(opts, "x-systemd.requires-mounts-for", &paths);
+ if (r < 0)
+ return log_warning_errno(r, "Failed to parse options: %m");
+ if (r == 0)
+ return 0;
+
+ res = strv_join(paths, " ");
+ if (!res)
+ return log_oom();
+
+ fprintf(f, "RequiresMountsFor=%s\n", res);
+
+ return 0;
+}
+
static int add_mount(
const char *what,
const char *where,
@@ -183,15 +255,19 @@ static int add_mount(
return 0;
if (path_equal(where, "/")) {
- /* The root disk is not an option */
- automount = false;
- noauto = false;
- nofail = false;
+ if (noauto)
+ log_warning("Ignoring \"noauto\" for root device");
+ if (nofail)
+ log_warning("Ignoring \"nofail\" for root device");
+ if (automount)
+ log_warning("Ignoring automount option for root device");
+
+ noauto = nofail = automount = false;
}
- name = unit_name_from_path(where, ".mount");
- if (!name)
- return log_oom();
+ r = unit_name_from_path(where, ".mount", &name);
+ if (r < 0)
+ return log_error_errno(r, "Failed to generate unit name: %m");
unit = strjoin(arg_dest, "/", name, NULL);
if (!unit)
@@ -216,6 +292,15 @@ static int add_mount(
if (post && !noauto && !nofail && !automount)
fprintf(f, "Before=%s\n", post);
+ if (!automount && opts) {
+ r = write_requires_after(f, opts);
+ if (r < 0)
+ return r;
+ r = write_requires_mounts_for(f, opts);
+ if (r < 0)
+ return r;
+ }
+
if (passno != 0) {
r = generator_write_fsck_deps(f, arg_dest, what, where, fstype);
if (r < 0)
@@ -240,9 +325,9 @@ static int add_mount(
if (!isempty(filtered) && !streq(filtered, "defaults"))
fprintf(f, "Options=%s\n", filtered);
- fflush(f);
- if (ferror(f))
- return log_error_errno(errno, "Failed to write unit file %s: %m", unit);
+ r = fflush_and_check(f);
+ if (r < 0)
+ return log_error_errno(r, "Failed to write unit file %s: %m", unit);
if (!noauto && post) {
lnk = strjoin(arg_dest, "/", post, nofail || automount ? ".wants/" : ".requires/", name, NULL);
@@ -255,9 +340,9 @@ static int add_mount(
}
if (automount) {
- automount_name = unit_name_from_path(where, ".automount");
- if (!automount_name)
- return log_oom();
+ r = unit_name_from_path(where, ".automount", &automount_name);
+ if (r < 0)
+ return log_error_errno(r, "Failed to generate unit name: %m");
automount_unit = strjoin(arg_dest, "/", automount_name, NULL);
if (!automount_unit)
@@ -280,14 +365,27 @@ static int add_mount(
"Before=%s\n",
post);
+ if (opts) {
+ r = write_requires_after(f, opts);
+ if (r < 0)
+ return r;
+ r = write_requires_mounts_for(f, opts);
+ if (r < 0)
+ return r;
+ }
+
fprintf(f,
"[Automount]\n"
"Where=%s\n",
where);
- fflush(f);
- if (ferror(f))
- return log_error_errno(errno, "Failed to write unit file %s: %m", automount_unit);
+ r = write_idle_timeout(f, where, opts);
+ if (r < 0)
+ return r;
+
+ r = fflush_and_check(f);
+ if (r < 0)
+ return log_error_errno(r, "Failed to write unit file %s: %m", automount_unit);
free(lnk);
lnk = strjoin(arg_dest, "/", post, nofail ? ".wants/" : ".requires/", automount_name, NULL);
@@ -385,7 +483,7 @@ static int parse_fstab(bool initrd) {
return r;
}
-static int add_root_mount(void) {
+static int add_sysroot_mount(void) {
_cleanup_free_ char *what = NULL;
const char *opts;
@@ -395,10 +493,8 @@ static int add_root_mount(void) {
}
what = fstab_node_to_udev_node(arg_root_what);
- if (!path_is_absolute(what)) {
- log_debug("Skipping entry what=%s where=/sysroot type=%s", what, strna(arg_root_fstype));
- return 0;
- }
+ if (!what)
+ return log_oom();
if (!arg_root_options)
opts = arg_root_rw > 0 ? "rw" : "ro";
@@ -413,7 +509,7 @@ static int add_root_mount(void) {
"/sysroot",
arg_root_fstype,
opts,
- 1,
+ is_device_path(what) ? 1 : 0,
false,
false,
false,
@@ -421,7 +517,7 @@ static int add_root_mount(void) {
"/proc/cmdline");
}
-static int add_usr_mount(void) {
+static int add_sysroot_usr_mount(void) {
_cleanup_free_ char *what = NULL;
const char *opts;
@@ -568,9 +664,9 @@ int main(int argc, char *argv[]) {
/* Always honour root= and usr= in the kernel command line if we are in an initrd */
if (in_initrd()) {
- r = add_root_mount();
+ r = add_sysroot_mount();
if (r == 0)
- r = add_usr_mount();
+ r = add_sysroot_usr_mount();
}
/* Honour /etc/fstab only when that's enabled */
diff --git a/src/getty-generator/getty-generator.c b/src/getty-generator/getty-generator.c
index f8f5fb30c2..d23caab44a 100644
--- a/src/getty-generator/getty-generator.c
+++ b/src/getty-generator/getty-generator.c
@@ -31,6 +31,8 @@
#include "virt.h"
#include "fileio.h"
#include "path-util.h"
+#include "process-util.h"
+#include "terminal-util.h"
static const char *arg_dest = "/tmp";
@@ -48,13 +50,11 @@ static int add_symlink(const char *fservice, const char *tservice) {
r = symlink(from, to);
if (r < 0) {
+ /* In case console=hvc0 is passed this will very likely result in EEXIST */
if (errno == EEXIST)
- /* In case console=hvc0 is passed this will very likely result in EEXIST */
return 0;
- else {
- log_error_errno(errno, "Failed to create symlink %s: %m", to);
- return -errno;
- }
+
+ return log_error_errno(errno, "Failed to create symlink %s: %m", to);
}
return 0;
@@ -62,28 +62,30 @@ static int add_symlink(const char *fservice, const char *tservice) {
static int add_serial_getty(const char *tty) {
_cleanup_free_ char *n = NULL;
+ int r;
assert(tty);
log_debug("Automatically adding serial getty for /dev/%s.", tty);
- n = unit_name_from_path_instance("serial-getty", tty, ".service");
- if (!n)
- return log_oom();
+ r = unit_name_from_path_instance("serial-getty", tty, ".service", &n);
+ if (r < 0)
+ return log_error_errno(r, "Failed to generate service name: %m");
return add_symlink("serial-getty@.service", n);
}
static int add_container_getty(const char *tty) {
_cleanup_free_ char *n = NULL;
+ int r;
assert(tty);
log_debug("Automatically adding container getty for /dev/pts/%s.", tty);
- n = unit_name_from_path_instance("container-getty", tty, ".service");
- if (!n)
- return log_oom();
+ r = unit_name_from_path_instance("container-getty", tty, ".service", &n);
+ if (r < 0)
+ return log_error_errno(r, "Failed to generate service name: %m");
return add_symlink("container-getty@.service", n);
}
diff --git a/src/gpt-auto-generator/gpt-auto-generator.c b/src/gpt-auto-generator/gpt-auto-generator.c
index 5c58b58f8a..b46e160888 100644
--- a/src/gpt-auto-generator/gpt-auto-generator.c
+++ b/src/gpt-auto-generator/gpt-auto-generator.c
@@ -21,8 +21,6 @@
#include <unistd.h>
#include <stdlib.h>
-#include <fcntl.h>
-#include <sys/ioctl.h>
#include <sys/statfs.h>
#include <blkid/blkid.h>
@@ -51,14 +49,15 @@ static bool arg_root_rw = false;
static int add_swap(const char *path) {
_cleanup_free_ char *name = NULL, *unit = NULL, *lnk = NULL;
_cleanup_fclose_ FILE *f = NULL;
+ int r;
assert(path);
log_debug("Adding swap: %s", path);
- name = unit_name_from_path(path, ".swap");
- if (!name)
- return log_oom();
+ r = unit_name_from_path(path, ".swap", &name);
+ if (r < 0)
+ return log_error_errno(r, "Failed to generate unit name: %m");
unit = strjoin(arg_dest, "/", name, NULL);
if (!unit)
@@ -102,17 +101,17 @@ static int add_cryptsetup(const char *id, const char *what, bool rw, char **devi
assert(what);
assert(device);
- d = unit_name_from_path(what, ".device");
- if (!d)
- return log_oom();
+ r = unit_name_from_path(what, ".device", &d);
+ if (r < 0)
+ return log_error_errno(r, "Failed to generate unit name: %m");
e = unit_name_escape(id);
if (!e)
return log_oom();
- n = unit_name_build("systemd-cryptsetup", e, ".service");
- if (!n)
- return log_oom();
+ r = unit_name_build("systemd-cryptsetup", e, ".service", &n);
+ if (r < 0)
+ return log_error_errno(r, "Failed to generate unit name: %m");
p = strjoin(arg_dest, "/", n, NULL);
if (!p)
@@ -226,9 +225,9 @@ static int add_mount(
fstype = NULL;
}
- unit = unit_name_from_path(where, ".mount");
- if (!unit)
- return log_oom();
+ r = unit_name_from_path(where, ".mount", &unit);
+ if (r < 0)
+ return log_error_errno(r, "Failed to generate unit name: %m");
p = strjoin(arg_dest, "/", unit, NULL);
if (!p)
@@ -290,7 +289,7 @@ static int probe_and_add_mount(
const char *post) {
_cleanup_blkid_free_probe_ blkid_probe b = NULL;
- const char *fstype;
+ const char *fstype = NULL;
int r;
assert(id);
@@ -298,7 +297,7 @@ static int probe_and_add_mount(
assert(where);
assert(description);
- if (path_is_mount_point(where, true) <= 0 &&
+ if (path_is_mount_point(where, AT_SYMLINK_FOLLOW) <= 0 &&
dir_is_empty(where) <= 0) {
log_debug("%s already populated, ignoring.", where);
return 0;
@@ -323,14 +322,11 @@ static int probe_and_add_mount(
r = blkid_do_safeprobe(b);
if (r == -2 || r == 1) /* no result or uncertain */
return 0;
- else if (r != 0) {
- if (errno == 0)
- errno = EIO;
- log_error_errno(errno, "Failed to probe %s: %m", what);
- return -errno;
- }
+ else if (r != 0)
+ return log_error_errno(errno ?: EIO, "Failed to probe %s: %m", what);
- blkid_probe_lookup_value(b, "TYPE", &fstype, NULL);
+ /* add_mount is OK with fstype being NULL. */
+ (void) blkid_probe_lookup_value(b, "TYPE", &fstype, NULL);
return add_mount(
id,
@@ -351,7 +347,7 @@ static int enumerate_partitions(dev_t devnum) {
_cleanup_free_ char *home = NULL, *srv = NULL;
struct udev_list_entry *first, *item;
struct udev_device *parent = NULL;
- const char *node, *pttype, *devtype;
+ const char *name, *node, *pttype, *devtype;
int home_nr = -1, srv_nr = -1;
bool home_rw = true, srv_rw = true;
blkid_partlist pl;
@@ -366,33 +362,42 @@ static int enumerate_partitions(dev_t devnum) {
if (!d)
return log_oom();
+ name = udev_device_get_devnode(d);
+ if (!name)
+ name = udev_device_get_syspath(d);
+ if (!name) {
+ log_debug("Device %u:%u does not have a name, ignoring.",
+ major(devnum), minor(devnum));
+ return 0;
+ }
+
parent = udev_device_get_parent(d);
if (!parent) {
- log_debug("Not a partitioned device, ignoring.");
+ log_debug("%s: not a partitioned device, ignoring.", name);
return 0;
}
/* Does it have a devtype? */
devtype = udev_device_get_devtype(parent);
if (!devtype) {
- log_debug("Parent doesn't have a device type, ignoring.");
+ log_debug("%s: parent doesn't have a device type, ignoring.", name);
return 0;
}
/* Is this a disk or a partition? We only care for disks... */
if (!streq(devtype, "disk")) {
- log_debug("Parent isn't a raw disk, ignoring.");
+ log_debug("%s: parent isn't a raw disk, ignoring.", name);
return 0;
}
/* Does it have a device node? */
node = udev_device_get_devnode(parent);
if (!node) {
- log_debug("Parent device does not have device node, ignoring.");
+ log_debug("%s: parent device does not have device node, ignoring.", name);
return 0;
}
- log_debug("Root device %s.", node);
+ log_debug("%s: root device %s.", name, node);
pn = udev_device_get_devnum(parent);
if (major(pn) == 0)
@@ -404,8 +409,7 @@ static int enumerate_partitions(dev_t devnum) {
if (errno == 0)
return log_oom();
- log_error_errno(errno, "Failed allocate prober: %m");
- return -errno;
+ return log_error_errno(errno, "%s: failed to allocate prober: %m", node);
}
blkid_probe_enable_partitions(b, 1);
@@ -415,25 +419,18 @@ static int enumerate_partitions(dev_t devnum) {
r = blkid_do_safeprobe(b);
if (r == -2 || r == 1) /* no result or uncertain */
return 0;
- else if (r != 0) {
- if (errno == 0)
- errno = EIO;
- log_error_errno(errno, "Failed to probe %s: %m", node);
- return -errno;
- }
+ else if (r != 0)
+ return log_error_errno(errno ?: EIO, "%s: failed to probe: %m", node);
errno = 0;
r = blkid_probe_lookup_value(b, "PTTYPE", &pttype, NULL);
- if (r != 0) {
- if (errno == 0)
- errno = EIO;
- log_error_errno(errno, "Failed to determine partition table type of %s: %m", node);
- return -errno;
- }
+ if (r != 0)
+ return log_error_errno(errno ?: EIO,
+ "%s: failed to determine partition table type: %m", node);
/* We only do this all for GPT... */
if (!streq_ptr(pttype, "gpt")) {
- log_debug("Not a GPT partition table, ignoring.");
+ log_debug("%s: not a GPT partition table, ignoring.", node);
return 0;
}
@@ -443,8 +440,7 @@ static int enumerate_partitions(dev_t devnum) {
if (errno == 0)
return log_oom();
- log_error_errno(errno, "Failed to list partitions of %s: %m", node);
- return -errno;
+ return log_error_errno(errno, "%s: failed to list partitions: %m", node);
}
e = udev_enumerate_new(udev);
@@ -461,7 +457,7 @@ static int enumerate_partitions(dev_t devnum) {
r = udev_enumerate_scan_devices(e);
if (r < 0)
- return log_error_errno(r, "Failed to enumerate partitions on %s: %m", node);
+ return log_error_errno(r, "%s: failed to enumerate partitions: %m", node);
first = udev_enumerate_get_list_entry(e);
udev_list_entry_foreach(item, first) {
@@ -516,7 +512,7 @@ static int enumerate_partitions(dev_t devnum) {
if (sd_id128_equal(type_id, GPT_SWAP)) {
if (flags & GPT_FLAG_READ_ONLY) {
- log_debug("%s marked as read-only swap partition, which is bogus, ignoring.", subnode);
+ log_debug("%s marked as read-only swap partition, which is bogus. Ignoring.", subnode);
continue;
}
@@ -548,7 +544,7 @@ static int enumerate_partitions(dev_t devnum) {
srv_rw = !(flags & GPT_FLAG_READ_ONLY),
free(srv);
- srv = strdup(node);
+ srv = strdup(subnode);
if (!srv)
return log_oom();
}
@@ -602,7 +598,7 @@ static int parse_proc_cmdline_item(const char *key, const char *value) {
r = parse_boolean(value);
if (r < 0)
- log_warning("Failed to parse gpt-auto switch %s. Ignoring.", value);
+ log_warning("Failed to parse gpt-auto switch \"%s\". Ignoring.", value);
else
arg_enabled = r;
@@ -663,8 +659,13 @@ static int add_mounts(void) {
if (r < 0)
return log_error_errno(r, "Failed to determine block device of root file system: %m");
else if (r == 0) {
- log_debug("Root file system not on a (single) block device.");
- return 0;
+ r = get_block_device("/usr", &devno);
+ if (r < 0)
+ return log_error_errno(r, "Failed to determine block device of /usr file system: %m");
+ else if (r == 0) {
+ log_debug("Neither root nor /usr file system are on a (single) block device.");
+ return 0;
+ }
}
return enumerate_partitions(devno);
diff --git a/src/gudev/.gitignore b/src/gudev/.gitignore
deleted file mode 100644
index 4577903c40..0000000000
--- a/src/gudev/.gitignore
+++ /dev/null
@@ -1,7 +0,0 @@
-gudev-1.0.pc
-gudevenumtypes.c
-gudevenumtypes.h
-gudevmarshal.c
-gudevmarshal.h
-GUdev-1.0.gir
-GUdev-1.0.typelib
diff --git a/src/gudev/gjs-example.js b/src/gudev/gjs-example.js
deleted file mode 100755
index 5586fd6a61..0000000000
--- a/src/gudev/gjs-example.js
+++ /dev/null
@@ -1,75 +0,0 @@
-#!/usr/bin/env gjs-console
-
-// This currently depends on the following patches to gjs
-//
-// http://bugzilla.gnome.org/show_bug.cgi?id=584558
-// http://bugzilla.gnome.org/show_bug.cgi?id=584560
-// http://bugzilla.gnome.org/show_bug.cgi?id=584568
-
-const GUdev = imports.gi.GUdev;
-const Mainloop = imports.mainloop;
-
-function print_device (device) {
- print (" subsystem: " + device.get_subsystem ());
- print (" devtype: " + device.get_devtype ());
- print (" name: " + device.get_name ());
- print (" number: " + device.get_number ());
- print (" sysfs_path: " + device.get_sysfs_path ());
- print (" driver: " + device.get_driver ());
- print (" action: " + device.get_action ());
- print (" seqnum: " + device.get_seqnum ());
- print (" device type: " + device.get_device_type ());
- print (" device number: " + device.get_device_number ());
- print (" device file: " + device.get_device_file ());
- print (" device file symlinks: " + device.get_device_file_symlinks ());
- print (" foo: " + device.get_sysfs_attr_as_strv ("stat"));
- var keys = device.get_property_keys ();
- for (var n = 0; n < keys.length; n++) {
- print (" " + keys[n] + "=" + device.get_property (keys[n]));
- }
-}
-
-function on_uevent (client, action, device) {
- print ("action " + action + " on device " + device.get_sysfs_path());
- print_device (device);
- print ("");
-}
-
-var client = new GUdev.Client ({subsystems: ["block", "usb/usb_interface"]});
-client.connect ("uevent", on_uevent);
-
-var block_devices = client.query_by_subsystem ("block");
-for (var n = 0; n < block_devices.length; n++) {
- print ("block device: " + block_devices[n].get_device_file ());
-}
-
-var d;
-
-d = client.query_by_device_number (GUdev.DeviceType.BLOCK, 0x0810);
-if (d == null) {
- print ("query_by_device_number 0x810 -> null");
-} else {
- print ("query_by_device_number 0x810 -> " + d.get_device_file ());
- var dd = d.get_parent_with_subsystem ("usb", null);
- print_device (dd);
- print ("--------------------------------------------------------------------------");
- while (d != null) {
- print_device (d);
- print ("");
- d = d.get_parent ();
- }
-}
-
-d = client.query_by_sysfs_path ("/sys/block/sda/sda1");
-print ("query_by_sysfs_path (\"/sys/block/sda1\") -> " + d.get_device_file ());
-
-d = client.query_by_subsystem_and_name ("block", "sda2");
-print ("query_by_subsystem_and_name (\"block\", \"sda2\") -> " + d.get_device_file ());
-
-d = client.query_by_device_file ("/dev/sda");
-print ("query_by_device_file (\"/dev/sda\") -> " + d.get_device_file ());
-
-d = client.query_by_device_file ("/dev/block/8:0");
-print ("query_by_device_file (\"/dev/block/8:0\") -> " + d.get_device_file ());
-
-Mainloop.run('udev-example');
diff --git a/src/gudev/gudev-1.0.pc.in b/src/gudev/gudev-1.0.pc.in
deleted file mode 100644
index 058262d767..0000000000
--- a/src/gudev/gudev-1.0.pc.in
+++ /dev/null
@@ -1,11 +0,0 @@
-prefix=@prefix@
-exec_prefix=@exec_prefix@
-libdir=@libdir@
-includedir=@includedir@
-
-Name: gudev-1.0
-Description: GObject bindings for libudev
-Version: @VERSION@
-Requires: glib-2.0, gobject-2.0
-Libs: -L${libdir} -lgudev-1.0
-Cflags: -I${includedir}/gudev-1.0
diff --git a/src/gudev/gudev.h b/src/gudev/gudev.h
deleted file mode 100644
index 1dc42b11b7..0000000000
--- a/src/gudev/gudev.h
+++ /dev/null
@@ -1,32 +0,0 @@
-/* -*- Mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2008 David Zeuthen <davidz@redhat.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#ifndef __G_UDEV_H__
-#define __G_UDEV_H__
-
-#define _GUDEV_INSIDE_GUDEV_H
-#include <gudev/gudevenums.h>
-#include <gudev/gudevenumtypes.h>
-#include <gudev/gudevtypes.h>
-#include <gudev/gudevclient.h>
-#include <gudev/gudevdevice.h>
-#include <gudev/gudevenumerator.h>
-#undef _GUDEV_INSIDE_GUDEV_H
-
-#endif /* __G_UDEV_H__ */
diff --git a/src/gudev/gudevclient.c b/src/gudev/gudevclient.c
deleted file mode 100644
index 1eec758935..0000000000
--- a/src/gudev/gudevclient.c
+++ /dev/null
@@ -1,532 +0,0 @@
-/* -*- Mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2008-2010 David Zeuthen <davidz@redhat.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#ifdef HAVE_CONFIG_H
-# include "config.h"
-#endif
-
-#include <stdlib.h>
-#include <string.h>
-
-#include "gudevclient.h"
-#include "gudevdevice.h"
-#include "gudevmarshal.h"
-#include "gudevprivate.h"
-
-/**
- * SECTION:gudevclient
- * @short_description: Query devices and listen to uevents
- *
- * #GUdevClient is used to query information about devices on a Linux
- * system from the Linux kernel and the udev device
- * manager.
- *
- * Device information is retrieved from the kernel (through the
- * <literal>sysfs</literal> filesystem) and the udev daemon (through a
- * <literal>tmpfs</literal> filesystem) and presented through
- * #GUdevDevice objects. This means that no blocking IO ever happens
- * (in both cases, we are essentially just reading data from kernel
- * memory) and as such there are no asynchronous versions of the
- * provided methods.
- *
- * To get #GUdevDevice objects, use
- * g_udev_client_query_by_subsystem(),
- * g_udev_client_query_by_device_number(),
- * g_udev_client_query_by_device_file(),
- * g_udev_client_query_by_sysfs_path(),
- * g_udev_client_query_by_subsystem_and_name()
- * or the #GUdevEnumerator type.
- *
- * To listen to uevents, connect to the #GUdevClient::uevent signal.
- */
-
-struct _GUdevClientPrivate
-{
- GSource *watch_source;
- struct udev *udev;
- struct udev_monitor *monitor;
-
- gchar **subsystems;
-};
-
-enum
-{
- PROP_0,
- PROP_SUBSYSTEMS,
-};
-
-enum
-{
- UEVENT_SIGNAL,
- LAST_SIGNAL,
-};
-
-static guint signals[LAST_SIGNAL] = { 0 };
-
-G_DEFINE_TYPE (GUdevClient, g_udev_client, G_TYPE_OBJECT)
-
-/* ---------------------------------------------------------------------------------------------------- */
-
-static gboolean
-monitor_event (GIOChannel *source,
- GIOCondition condition,
- gpointer data)
-{
- GUdevClient *client = (GUdevClient *) data;
- GUdevDevice *device;
- struct udev_device *udevice;
-
- if (client->priv->monitor == NULL)
- goto out;
- udevice = udev_monitor_receive_device (client->priv->monitor);
- if (udevice == NULL)
- goto out;
-
- device = _g_udev_device_new (udevice);
- udev_device_unref (udevice);
- g_signal_emit (client,
- signals[UEVENT_SIGNAL],
- 0,
- g_udev_device_get_action (device),
- device);
- g_object_unref (device);
-
- out:
- return TRUE;
-}
-
-static void
-g_udev_client_finalize (GObject *object)
-{
- GUdevClient *client = G_UDEV_CLIENT (object);
-
- if (client->priv->watch_source != NULL)
- {
- g_source_destroy (client->priv->watch_source);
- client->priv->watch_source = NULL;
- }
-
- if (client->priv->monitor != NULL)
- {
- udev_monitor_unref (client->priv->monitor);
- client->priv->monitor = NULL;
- }
-
- if (client->priv->udev != NULL)
- {
- udev_unref (client->priv->udev);
- client->priv->udev = NULL;
- }
-
- g_strfreev (client->priv->subsystems);
-
- if (G_OBJECT_CLASS (g_udev_client_parent_class)->finalize != NULL)
- G_OBJECT_CLASS (g_udev_client_parent_class)->finalize (object);
-}
-
-static void
-g_udev_client_set_property (GObject *object,
- guint prop_id,
- const GValue *value,
- GParamSpec *pspec)
-{
- GUdevClient *client = G_UDEV_CLIENT (object);
-
- switch (prop_id)
- {
- case PROP_SUBSYSTEMS:
- if (client->priv->subsystems != NULL)
- g_strfreev (client->priv->subsystems);
- client->priv->subsystems = g_strdupv (g_value_get_boxed (value));
- break;
-
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- break;
- }
-}
-
-static void
-g_udev_client_get_property (GObject *object,
- guint prop_id,
- GValue *value,
- GParamSpec *pspec)
-{
- GUdevClient *client = G_UDEV_CLIENT (object);
-
- switch (prop_id)
- {
- case PROP_SUBSYSTEMS:
- g_value_set_boxed (value, client->priv->subsystems);
- break;
-
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- break;
- }
-}
-
-static void
-g_udev_client_constructed (GObject *object)
-{
- GUdevClient *client = G_UDEV_CLIENT (object);
- GIOChannel *channel;
- guint n;
-
- client->priv->udev = udev_new ();
-
- /* connect to event source */
- client->priv->monitor = udev_monitor_new_from_netlink (client->priv->udev, "udev");
-
- //g_debug ("ss = %p", client->priv->subsystems);
-
- if (client->priv->subsystems != NULL)
- {
- /* install subsystem filters to only wake up for certain events */
- for (n = 0; client->priv->subsystems[n] != NULL; n++)
- {
- gchar *subsystem;
- gchar *devtype;
- gchar *s;
-
- subsystem = g_strdup (client->priv->subsystems[n]);
- devtype = NULL;
-
- //g_debug ("s = '%s'", subsystem);
-
- s = strstr (subsystem, "/");
- if (s != NULL)
- {
- devtype = s + 1;
- *s = '\0';
- }
-
- if (client->priv->monitor != NULL)
- udev_monitor_filter_add_match_subsystem_devtype (client->priv->monitor, subsystem, devtype);
-
- g_free (subsystem);
- }
-
- /* listen to events, and buffer them */
- if (client->priv->monitor != NULL)
- {
- udev_monitor_enable_receiving (client->priv->monitor);
- channel = g_io_channel_unix_new (udev_monitor_get_fd (client->priv->monitor));
- client->priv->watch_source = g_io_create_watch (channel, G_IO_IN);
- g_io_channel_unref (channel);
- g_source_set_callback (client->priv->watch_source, (GSourceFunc) monitor_event, client, NULL);
- g_source_attach (client->priv->watch_source, g_main_context_get_thread_default ());
- g_source_unref (client->priv->watch_source);
- }
- else
- {
- client->priv->watch_source = NULL;
- }
- }
-
- if (G_OBJECT_CLASS (g_udev_client_parent_class)->constructed != NULL)
- G_OBJECT_CLASS (g_udev_client_parent_class)->constructed (object);
-}
-
-
-static void
-g_udev_client_class_init (GUdevClientClass *klass)
-{
- GObjectClass *gobject_class = (GObjectClass *) klass;
-
- gobject_class->constructed = g_udev_client_constructed;
- gobject_class->set_property = g_udev_client_set_property;
- gobject_class->get_property = g_udev_client_get_property;
- gobject_class->finalize = g_udev_client_finalize;
-
- /**
- * GUdevClient:subsystems:
- *
- * The subsystems to listen for uevents on.
- *
- * To listen for only a specific DEVTYPE for a given SUBSYSTEM, use
- * "subsystem/devtype". For example, to only listen for uevents
- * where SUBSYSTEM is usb and DEVTYPE is usb_interface, use
- * "usb/usb_interface".
- *
- * If this property is %NULL, then no events will be reported. If
- * it's the empty array, events from all subsystems will be
- * reported.
- */
- g_object_class_install_property (gobject_class,
- PROP_SUBSYSTEMS,
- g_param_spec_boxed ("subsystems",
- "The subsystems to listen for changes on",
- "The subsystems to listen for changes on",
- G_TYPE_STRV,
- G_PARAM_CONSTRUCT_ONLY |
- G_PARAM_READWRITE));
-
- /**
- * GUdevClient::uevent:
- * @client: The #GUdevClient receiving the event.
- * @action: The action for the uevent e.g. "add", "remove", "change", "move", etc.
- * @device: Details about the #GUdevDevice the event is for.
- *
- * Emitted when @client receives an uevent.
- *
- * This signal is emitted in the
- * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
- * of the thread that @client was created in.
- */
- signals[UEVENT_SIGNAL] = g_signal_new ("uevent",
- G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (GUdevClientClass, uevent),
- NULL,
- NULL,
- g_udev_marshal_VOID__STRING_OBJECT,
- G_TYPE_NONE,
- 2,
- G_TYPE_STRING,
- G_UDEV_TYPE_DEVICE);
-
- g_type_class_add_private (klass, sizeof (GUdevClientPrivate));
-}
-
-static void
-g_udev_client_init (GUdevClient *client)
-{
- client->priv = G_TYPE_INSTANCE_GET_PRIVATE (client,
- G_UDEV_TYPE_CLIENT,
- GUdevClientPrivate);
-}
-
-/**
- * g_udev_client_new:
- * @subsystems: (array zero-terminated=1) (element-type utf8) (transfer none) (allow-none): A %NULL terminated string array of subsystems to listen for uevents on, %NULL to not listen on uevents at all, or an empty array to listen to uevents on all subsystems. See the documentation for the #GUdevClient:subsystems property for details on this parameter.
- *
- * Constructs a #GUdevClient object that can be used to query
- * information about devices. Connect to the #GUdevClient::uevent
- * signal to listen for uevents. Note that signals are emitted in the
- * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
- * of the thread that you call this constructor from.
- *
- * Returns: A new #GUdevClient object. Free with g_object_unref().
- */
-GUdevClient *
-g_udev_client_new (const gchar * const *subsystems)
-{
- return G_UDEV_CLIENT (g_object_new (G_UDEV_TYPE_CLIENT, "subsystems", subsystems, NULL));
-}
-
-/**
- * g_udev_client_query_by_subsystem:
- * @client: A #GUdevClient.
- * @subsystem: (allow-none): The subsystem to get devices for or %NULL to get all devices.
- *
- * Gets all devices belonging to @subsystem.
- *
- * Returns: (nullable) (element-type GUdevDevice) (transfer full): A
- * list of #GUdevDevice objects. The caller should free the result by
- * using g_object_unref() on each element in the list and then
- * g_list_free() on the list.
- */
-GList *
-g_udev_client_query_by_subsystem (GUdevClient *client,
- const gchar *subsystem)
-{
- struct udev_enumerate *enumerate;
- struct udev_list_entry *l, *devices;
- GList *ret;
-
- g_return_val_if_fail (G_UDEV_IS_CLIENT (client), NULL);
-
- ret = NULL;
-
- /* prepare a device scan */
- enumerate = udev_enumerate_new (client->priv->udev);
-
- /* filter for subsystem */
- if (subsystem != NULL)
- udev_enumerate_add_match_subsystem (enumerate, subsystem);
- /* retrieve the list */
- udev_enumerate_scan_devices (enumerate);
-
- /* add devices to the list */
- devices = udev_enumerate_get_list_entry (enumerate);
- for (l = devices; l != NULL; l = udev_list_entry_get_next (l))
- {
- struct udev_device *udevice;
- GUdevDevice *device;
-
- udevice = udev_device_new_from_syspath (udev_enumerate_get_udev (enumerate),
- udev_list_entry_get_name (l));
- if (udevice == NULL)
- continue;
- device = _g_udev_device_new (udevice);
- udev_device_unref (udevice);
- ret = g_list_prepend (ret, device);
- }
- udev_enumerate_unref (enumerate);
-
- ret = g_list_reverse (ret);
-
- return ret;
-}
-
-/**
- * g_udev_client_query_by_device_number:
- * @client: A #GUdevClient.
- * @type: A value from the #GUdevDeviceType enumeration.
- * @number: A device number.
- *
- * Looks up a device for a type and device number.
- *
- * Returns: (nullable) (transfer full): A #GUdevDevice object or %NULL
- * if the device was not found. Free with g_object_unref().
- */
-GUdevDevice *
-g_udev_client_query_by_device_number (GUdevClient *client,
- GUdevDeviceType type,
- GUdevDeviceNumber number)
-{
- struct udev_device *udevice;
- GUdevDevice *device;
-
- g_return_val_if_fail (G_UDEV_IS_CLIENT (client), NULL);
-
- device = NULL;
- udevice = udev_device_new_from_devnum (client->priv->udev, type, number);
-
- if (udevice == NULL)
- goto out;
-
- device = _g_udev_device_new (udevice);
- udev_device_unref (udevice);
-
- out:
- return device;
-}
-
-/**
- * g_udev_client_query_by_device_file:
- * @client: A #GUdevClient.
- * @device_file: A device file.
- *
- * Looks up a device for a device file.
- *
- * Returns: (nullable) (transfer full): A #GUdevDevice object or %NULL
- * if the device was not found. Free with g_object_unref().
- */
-GUdevDevice *
-g_udev_client_query_by_device_file (GUdevClient *client,
- const gchar *device_file)
-{
- struct stat stat_buf;
- GUdevDevice *device;
-
- g_return_val_if_fail (G_UDEV_IS_CLIENT (client), NULL);
- g_return_val_if_fail (device_file != NULL, NULL);
-
- device = NULL;
-
- if (stat (device_file, &stat_buf) != 0)
- goto out;
-
- if (stat_buf.st_rdev == 0)
- goto out;
-
- if (S_ISBLK (stat_buf.st_mode))
- device = g_udev_client_query_by_device_number (client, G_UDEV_DEVICE_TYPE_BLOCK, stat_buf.st_rdev);
- else if (S_ISCHR (stat_buf.st_mode))
- device = g_udev_client_query_by_device_number (client, G_UDEV_DEVICE_TYPE_CHAR, stat_buf.st_rdev);
-
- out:
- return device;
-}
-
-/**
- * g_udev_client_query_by_sysfs_path:
- * @client: A #GUdevClient.
- * @sysfs_path: A sysfs path.
- *
- * Looks up a device for a sysfs path.
- *
- * Returns: (nullable) (transfer full): A #GUdevDevice object or %NULL
- * if the device was not found. Free with g_object_unref().
- */
-GUdevDevice *
-g_udev_client_query_by_sysfs_path (GUdevClient *client,
- const gchar *sysfs_path)
-{
- struct udev_device *udevice;
- GUdevDevice *device;
-
- g_return_val_if_fail (G_UDEV_IS_CLIENT (client), NULL);
- g_return_val_if_fail (sysfs_path != NULL, NULL);
-
- device = NULL;
- udevice = udev_device_new_from_syspath (client->priv->udev, sysfs_path);
- if (udevice == NULL)
- goto out;
-
- device = _g_udev_device_new (udevice);
- udev_device_unref (udevice);
-
- out:
- return device;
-}
-
-/**
- * g_udev_client_query_by_subsystem_and_name:
- * @client: A #GUdevClient.
- * @subsystem: A subsystem name.
- * @name: The name of the device.
- *
- * Looks up a device for a subsystem and name.
- *
- * Returns: (nullable) (transfer full): A #GUdevDevice object or %NULL
- * if the device was not found. Free with g_object_unref().
- */
-GUdevDevice *
-g_udev_client_query_by_subsystem_and_name (GUdevClient *client,
- const gchar *subsystem,
- const gchar *name)
-{
- struct udev_device *udevice;
- GUdevDevice *device;
-
- g_return_val_if_fail (G_UDEV_IS_CLIENT (client), NULL);
- g_return_val_if_fail (subsystem != NULL, NULL);
- g_return_val_if_fail (name != NULL, NULL);
-
- device = NULL;
- udevice = udev_device_new_from_subsystem_sysname (client->priv->udev, subsystem, name);
- if (udevice == NULL)
- goto out;
-
- device = _g_udev_device_new (udevice);
- udev_device_unref (udevice);
-
- out:
- return device;
-}
-
-struct udev *
-_g_udev_client_get_udev (GUdevClient *client)
-{
- g_return_val_if_fail (G_UDEV_IS_CLIENT (client), NULL);
- return client->priv->udev;
-}
diff --git a/src/gudev/gudevclient.h b/src/gudev/gudevclient.h
deleted file mode 100644
index 23bfce615f..0000000000
--- a/src/gudev/gudevclient.h
+++ /dev/null
@@ -1,99 +0,0 @@
-/* -*- Mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2008 David Zeuthen <davidz@redhat.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#if !defined (_GUDEV_COMPILATION) && !defined(_GUDEV_INSIDE_GUDEV_H)
-#error "Only <gudev/gudev.h> can be included directly, this file may disappear or change contents."
-#endif
-
-#ifndef __G_UDEV_CLIENT_H__
-#define __G_UDEV_CLIENT_H__
-
-#include <gudev/gudevtypes.h>
-
-G_BEGIN_DECLS
-
-#define G_UDEV_TYPE_CLIENT (g_udev_client_get_type ())
-#define G_UDEV_CLIENT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_UDEV_TYPE_CLIENT, GUdevClient))
-#define G_UDEV_CLIENT_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_UDEV_TYPE_CLIENT, GUdevClientClass))
-#define G_UDEV_IS_CLIENT(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_UDEV_TYPE_CLIENT))
-#define G_UDEV_IS_CLIENT_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_UDEV_TYPE_CLIENT))
-#define G_UDEV_CLIENT_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_UDEV_TYPE_CLIENT, GUdevClientClass))
-
-typedef struct _GUdevClientClass GUdevClientClass;
-typedef struct _GUdevClientPrivate GUdevClientPrivate;
-
-/**
- * GUdevClient:
- *
- * The #GUdevClient struct is opaque and should not be accessed directly.
- */
-struct _GUdevClient
-{
- GObject parent;
-
- /*< private >*/
- GUdevClientPrivate *priv;
-};
-
-/**
- * GUdevClientClass:
- * @parent_class: Parent class.
- * @uevent: Signal class handler for the #GUdevClient::uevent signal.
- *
- * Class structure for #GUdevClient.
- */
-struct _GUdevClientClass
-{
- GObjectClass parent_class;
-
- /* signals */
- void (*uevent) (GUdevClient *client,
- const gchar *action,
- GUdevDevice *device);
-
- /*< private >*/
- /* Padding for future expansion */
- void (*reserved1) (void);
- void (*reserved2) (void);
- void (*reserved3) (void);
- void (*reserved4) (void);
- void (*reserved5) (void);
- void (*reserved6) (void);
- void (*reserved7) (void);
- void (*reserved8) (void);
-};
-
-GType g_udev_client_get_type (void) G_GNUC_CONST;
-GUdevClient *g_udev_client_new (const gchar* const *subsystems);
-GList *g_udev_client_query_by_subsystem (GUdevClient *client,
- const gchar *subsystem);
-GUdevDevice *g_udev_client_query_by_device_number (GUdevClient *client,
- GUdevDeviceType type,
- GUdevDeviceNumber number);
-GUdevDevice *g_udev_client_query_by_device_file (GUdevClient *client,
- const gchar *device_file);
-GUdevDevice *g_udev_client_query_by_sysfs_path (GUdevClient *client,
- const gchar *sysfs_path);
-GUdevDevice *g_udev_client_query_by_subsystem_and_name (GUdevClient *client,
- const gchar *subsystem,
- const gchar *name);
-
-G_END_DECLS
-
-#endif /* __G_UDEV_CLIENT_H__ */
diff --git a/src/gudev/gudevdevice.c b/src/gudev/gudevdevice.c
deleted file mode 100644
index 7106719111..0000000000
--- a/src/gudev/gudevdevice.c
+++ /dev/null
@@ -1,1026 +0,0 @@
-/* -*- Mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2008 David Zeuthen <davidz@redhat.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#ifdef HAVE_CONFIG_H
-# include "config.h"
-#endif
-
-#include <stdlib.h>
-#include <string.h>
-
-#include "gudevdevice.h"
-#include "gudevprivate.h"
-
-/**
- * SECTION:gudevdevice
- * @short_description: Get information about a device
- *
- * The #GUdevDevice class is used to get information about a specific
- * device. Note that you cannot instantiate a #GUdevDevice object
- * yourself. Instead you must use #GUdevClient to obtain #GUdevDevice
- * objects.
- *
- * To get basic information about a device, use
- * g_udev_device_get_subsystem(), g_udev_device_get_devtype(),
- * g_udev_device_get_name(), g_udev_device_get_number(),
- * g_udev_device_get_sysfs_path(), g_udev_device_get_driver(),
- * g_udev_device_get_action(), g_udev_device_get_seqnum(),
- * g_udev_device_get_device_type(), g_udev_device_get_device_number(),
- * g_udev_device_get_device_file(),
- * g_udev_device_get_device_file_symlinks().
- *
- * To navigate the device tree, use g_udev_device_get_parent() and
- * g_udev_device_get_parent_with_subsystem().
- *
- * To access udev properties for the device, use
- * g_udev_device_get_property_keys(),
- * g_udev_device_has_property(),
- * g_udev_device_get_property(),
- * g_udev_device_get_property_as_int(),
- * g_udev_device_get_property_as_uint64(),
- * g_udev_device_get_property_as_double(),
- * g_udev_device_get_property_as_boolean() and
- * g_udev_device_get_property_as_strv().
- *
- * To access sysfs attributes for the device, use
- * g_udev_device_get_sysfs_attr_keys(),
- * g_udev_device_has_sysfs_attr(),
- * g_udev_device_get_sysfs_attr(),
- * g_udev_device_get_sysfs_attr_as_int(),
- * g_udev_device_get_sysfs_attr_as_uint64(),
- * g_udev_device_get_sysfs_attr_as_double(),
- * g_udev_device_get_sysfs_attr_as_boolean() and
- * g_udev_device_get_sysfs_attr_as_strv().
- *
- * Note that all getters on #GUdevDevice are non-reffing – returned
- * values are owned by the object, should not be freed and are only
- * valid as long as the object is alive.
- *
- * By design, #GUdevDevice will not react to changes for a device – it
- * only contains a snapshot of information when the #GUdevDevice
- * object was created. To work with changes, you typically connect to
- * the #GUdevClient::uevent signal on a #GUdevClient and get a new
- * #GUdevDevice whenever an event happens.
- */
-
-struct _GUdevDevicePrivate
-{
- struct udev_device *udevice;
-
- /* computed ondemand and cached */
- gchar **device_file_symlinks;
- gchar **property_keys;
- gchar **sysfs_attr_keys;
- gchar **tags;
- GHashTable *prop_strvs;
- GHashTable *sysfs_attr_strvs;
-};
-
-G_DEFINE_TYPE (GUdevDevice, g_udev_device, G_TYPE_OBJECT)
-
-static void
-g_udev_device_finalize (GObject *object)
-{
- GUdevDevice *device = G_UDEV_DEVICE (object);
-
- g_strfreev (device->priv->device_file_symlinks);
- g_strfreev (device->priv->property_keys);
- g_strfreev (device->priv->sysfs_attr_keys);
- g_strfreev (device->priv->tags);
-
- if (device->priv->udevice != NULL)
- udev_device_unref (device->priv->udevice);
-
- if (device->priv->prop_strvs != NULL)
- g_hash_table_unref (device->priv->prop_strvs);
-
- if (device->priv->sysfs_attr_strvs != NULL)
- g_hash_table_unref (device->priv->sysfs_attr_strvs);
-
- if (G_OBJECT_CLASS (g_udev_device_parent_class)->finalize != NULL)
- (* G_OBJECT_CLASS (g_udev_device_parent_class)->finalize) (object);
-}
-
-static void
-g_udev_device_class_init (GUdevDeviceClass *klass)
-{
- GObjectClass *gobject_class = (GObjectClass *) klass;
-
- gobject_class->finalize = g_udev_device_finalize;
-
- g_type_class_add_private (klass, sizeof (GUdevDevicePrivate));
-}
-
-static void
-g_udev_device_init (GUdevDevice *device)
-{
- device->priv = G_TYPE_INSTANCE_GET_PRIVATE (device,
- G_UDEV_TYPE_DEVICE,
- GUdevDevicePrivate);
-}
-
-
-GUdevDevice *
-_g_udev_device_new (struct udev_device *udevice)
-{
- GUdevDevice *device;
-
- device = G_UDEV_DEVICE (g_object_new (G_UDEV_TYPE_DEVICE, NULL));
- device->priv->udevice = udev_device_ref (udevice);
-
- return device;
-}
-
-/**
- * g_udev_device_get_subsystem:
- * @device: A #GUdevDevice.
- *
- * Gets the subsystem for @device.
- *
- * Returns: The subsystem for @device.
- */
-const gchar *
-g_udev_device_get_subsystem (GUdevDevice *device)
-{
- g_return_val_if_fail (G_UDEV_IS_DEVICE (device), NULL);
- return udev_device_get_subsystem (device->priv->udevice);
-}
-
-/**
- * g_udev_device_get_devtype:
- * @device: A #GUdevDevice.
- *
- * Gets the device type for @device.
- *
- * Returns: The devtype for @device.
- */
-const gchar *
-g_udev_device_get_devtype (GUdevDevice *device)
-{
- g_return_val_if_fail (G_UDEV_IS_DEVICE (device), NULL);
- return udev_device_get_devtype (device->priv->udevice);
-}
-
-/**
- * g_udev_device_get_name:
- * @device: A #GUdevDevice.
- *
- * Gets the name of @device, e.g. "sda3".
- *
- * Returns: The name of @device.
- */
-const gchar *
-g_udev_device_get_name (GUdevDevice *device)
-{
- g_return_val_if_fail (G_UDEV_IS_DEVICE (device), NULL);
- return udev_device_get_sysname (device->priv->udevice);
-}
-
-/**
- * g_udev_device_get_number:
- * @device: A #GUdevDevice.
- *
- * Gets the number of @device, e.g. "3" if g_udev_device_get_name() returns "sda3".
- *
- * Returns: The number of @device.
- */
-const gchar *
-g_udev_device_get_number (GUdevDevice *device)
-{
- g_return_val_if_fail (G_UDEV_IS_DEVICE (device), NULL);
- return udev_device_get_sysnum (device->priv->udevice);
-}
-
-/**
- * g_udev_device_get_sysfs_path:
- * @device: A #GUdevDevice.
- *
- * Gets the sysfs path for @device.
- *
- * Returns: The sysfs path for @device.
- */
-const gchar *
-g_udev_device_get_sysfs_path (GUdevDevice *device)
-{
- g_return_val_if_fail (G_UDEV_IS_DEVICE (device), NULL);
- return udev_device_get_syspath (device->priv->udevice);
-}
-
-/**
- * g_udev_device_get_driver:
- * @device: A #GUdevDevice.
- *
- * Gets the name of the driver used for @device.
- *
- * Returns: (nullable): The name of the driver for @device or %NULL if
- * unknown.
- */
-const gchar *
-g_udev_device_get_driver (GUdevDevice *device)
-{
- g_return_val_if_fail (G_UDEV_IS_DEVICE (device), NULL);
- return udev_device_get_driver (device->priv->udevice);
-}
-
-/**
- * g_udev_device_get_action:
- * @device: A #GUdevDevice.
- *
- * Gets the most recent action (e.g. "add", "remove", "change", etc.) for @device.
- *
- * Returns: An action string.
- */
-const gchar *
-g_udev_device_get_action (GUdevDevice *device)
-{
- g_return_val_if_fail (G_UDEV_IS_DEVICE (device), NULL);
- return udev_device_get_action (device->priv->udevice);
-}
-
-/**
- * g_udev_device_get_seqnum:
- * @device: A #GUdevDevice.
- *
- * Gets the most recent sequence number for @device.
- *
- * Returns: A sequence number.
- */
-guint64
-g_udev_device_get_seqnum (GUdevDevice *device)
-{
- g_return_val_if_fail (G_UDEV_IS_DEVICE (device), 0);
- return udev_device_get_seqnum (device->priv->udevice);
-}
-
-/**
- * g_udev_device_get_device_type:
- * @device: A #GUdevDevice.
- *
- * Gets the type of the device file, if any, for @device.
- *
- * Returns: The device number for @device or #G_UDEV_DEVICE_TYPE_NONE if the device does not have a device file.
- */
-GUdevDeviceType
-g_udev_device_get_device_type (GUdevDevice *device)
-{
- struct stat stat_buf;
- const gchar *device_file;
- GUdevDeviceType type;
-
- g_return_val_if_fail (G_UDEV_IS_DEVICE (device), G_UDEV_DEVICE_TYPE_NONE);
-
- type = G_UDEV_DEVICE_TYPE_NONE;
-
- /* TODO: would be better to have support for this in libudev... */
-
- device_file = g_udev_device_get_device_file (device);
- if (device_file == NULL)
- goto out;
-
- if (stat (device_file, &stat_buf) != 0)
- goto out;
-
- if (S_ISBLK (stat_buf.st_mode))
- type = G_UDEV_DEVICE_TYPE_BLOCK;
- else if (S_ISCHR (stat_buf.st_mode))
- type = G_UDEV_DEVICE_TYPE_CHAR;
-
- out:
- return type;
-}
-
-/**
- * g_udev_device_get_device_number:
- * @device: A #GUdevDevice.
- *
- * Gets the device number, if any, for @device.
- *
- * Returns: The device number for @device or 0 if unknown.
- */
-GUdevDeviceNumber
-g_udev_device_get_device_number (GUdevDevice *device)
-{
- g_return_val_if_fail (G_UDEV_IS_DEVICE (device), 0);
- return udev_device_get_devnum (device->priv->udevice);
-}
-
-/**
- * g_udev_device_get_device_file:
- * @device: A #GUdevDevice.
- *
- * Gets the device file for @device.
- *
- * Returns: (nullable): The device file for @device or %NULL if no
- * device file exists.
- */
-const gchar *
-g_udev_device_get_device_file (GUdevDevice *device)
-{
- g_return_val_if_fail (G_UDEV_IS_DEVICE (device), NULL);
- return udev_device_get_devnode (device->priv->udevice);
-}
-
-/**
- * g_udev_device_get_device_file_symlinks:
- * @device: A #GUdevDevice.
- *
- * Gets a list of symlinks (in <literal>/dev</literal>) that points to
- * the device file for @device.
- *
- * Returns: (transfer none) (array zero-terminated=1) (element-type utf8): A %NULL terminated string array of symlinks. This array is owned by @device and should not be freed by the caller.
- */
-const gchar * const *
-g_udev_device_get_device_file_symlinks (GUdevDevice *device)
-{
- struct udev_list_entry *l;
- GPtrArray *p;
-
- g_return_val_if_fail (G_UDEV_IS_DEVICE (device), NULL);
-
- if (device->priv->device_file_symlinks != NULL)
- goto out;
-
- p = g_ptr_array_new ();
- for (l = udev_device_get_devlinks_list_entry (device->priv->udevice); l != NULL; l = udev_list_entry_get_next (l))
- {
- g_ptr_array_add (p, g_strdup (udev_list_entry_get_name (l)));
- }
- g_ptr_array_add (p, NULL);
- device->priv->device_file_symlinks = (gchar **) g_ptr_array_free (p, FALSE);
-
- out:
- return (const gchar * const *) device->priv->device_file_symlinks;
-}
-
-/* ---------------------------------------------------------------------------------------------------- */
-
-/**
- * g_udev_device_get_parent:
- * @device: A #GUdevDevice.
- *
- * Gets the immediate parent of @device, if any.
- *
- * Returns: (nullable) (transfer full): A #GUdevDevice or %NULL if
- * @device has no parent. Free with g_object_unref().
- */
-GUdevDevice *
-g_udev_device_get_parent (GUdevDevice *device)
-{
- GUdevDevice *ret;
- struct udev_device *udevice;
-
- g_return_val_if_fail (G_UDEV_IS_DEVICE (device), NULL);
-
- ret = NULL;
-
- udevice = udev_device_get_parent (device->priv->udevice);
- if (udevice == NULL)
- goto out;
-
- ret = _g_udev_device_new (udevice);
-
- out:
- return ret;
-}
-
-/**
- * g_udev_device_get_parent_with_subsystem:
- * @device: A #GUdevDevice.
- * @subsystem: The subsystem of the parent to get.
- * @devtype: (allow-none): The devtype of the parent to get or %NULL.
- *
- * Walks up the chain of parents of @device and returns the first
- * device encountered where @subsystem and @devtype matches, if any.
- *
- * Returns: (nullable) (transfer full): A #GUdevDevice or %NULL if
- * @device has no parent with @subsystem and @devtype. Free with
- * g_object_unref().
- */
-GUdevDevice *
-g_udev_device_get_parent_with_subsystem (GUdevDevice *device,
- const gchar *subsystem,
- const gchar *devtype)
-{
- GUdevDevice *ret;
- struct udev_device *udevice;
-
- g_return_val_if_fail (G_UDEV_IS_DEVICE (device), NULL);
- g_return_val_if_fail (subsystem != NULL, NULL);
-
- ret = NULL;
-
- udevice = udev_device_get_parent_with_subsystem_devtype (device->priv->udevice,
- subsystem,
- devtype);
- if (udevice == NULL)
- goto out;
-
- ret = _g_udev_device_new (udevice);
-
- out:
- return ret;
-}
-
-/* ---------------------------------------------------------------------------------------------------- */
-
-/**
- * g_udev_device_get_property_keys:
- * @device: A #GUdevDevice.
- *
- * Gets all keys for properties on @device.
- *
- * Returns: (transfer none) (array zero-terminated=1) (element-type utf8): A %NULL terminated string array of property keys. This array is owned by @device and should not be freed by the caller.
- */
-const gchar* const *
-g_udev_device_get_property_keys (GUdevDevice *device)
-{
- struct udev_list_entry *l;
- GPtrArray *p;
-
- g_return_val_if_fail (G_UDEV_IS_DEVICE (device), NULL);
-
- if (device->priv->property_keys != NULL)
- goto out;
-
- p = g_ptr_array_new ();
- for (l = udev_device_get_properties_list_entry (device->priv->udevice); l != NULL; l = udev_list_entry_get_next (l))
- {
- g_ptr_array_add (p, g_strdup (udev_list_entry_get_name (l)));
- }
- g_ptr_array_add (p, NULL);
- device->priv->property_keys = (gchar **) g_ptr_array_free (p, FALSE);
-
- out:
- return (const gchar * const *) device->priv->property_keys;
-}
-
-
-/**
- * g_udev_device_has_property:
- * @device: A #GUdevDevice.
- * @key: Name of property.
- *
- * Check if a the property with the given key exists.
- *
- * Returns: %TRUE only if the value for @key exist.
- */
-gboolean
-g_udev_device_has_property (GUdevDevice *device,
- const gchar *key)
-{
- g_return_val_if_fail (G_UDEV_IS_DEVICE (device), FALSE);
- g_return_val_if_fail (key != NULL, FALSE);
- return udev_device_get_property_value (device->priv->udevice, key) != NULL;
-}
-
-/**
- * g_udev_device_get_property:
- * @device: A #GUdevDevice.
- * @key: Name of property.
- *
- * Look up the value for @key on @device.
- *
- * Returns: (nullable): The value for @key or %NULL if @key doesn't
- * exist on @device. Do not free this string, it is owned by @device.
- */
-const gchar *
-g_udev_device_get_property (GUdevDevice *device,
- const gchar *key)
-{
- g_return_val_if_fail (G_UDEV_IS_DEVICE (device), NULL);
- g_return_val_if_fail (key != NULL, NULL);
- return udev_device_get_property_value (device->priv->udevice, key);
-}
-
-/**
- * g_udev_device_get_property_as_int:
- * @device: A #GUdevDevice.
- * @key: Name of property.
- *
- * Look up the value for @key on @device and convert it to an integer
- * using strtol().
- *
- * Returns: The value for @key or 0 if @key doesn't exist or
- * isn't an integer.
- */
-gint
-g_udev_device_get_property_as_int (GUdevDevice *device,
- const gchar *key)
-{
- gint result;
- const gchar *s;
-
- g_return_val_if_fail (G_UDEV_IS_DEVICE (device), 0);
- g_return_val_if_fail (key != NULL, 0);
-
- result = 0;
- s = g_udev_device_get_property (device, key);
- if (s == NULL)
- goto out;
-
- result = strtol (s, NULL, 0);
-out:
- return result;
-}
-
-/**
- * g_udev_device_get_property_as_uint64:
- * @device: A #GUdevDevice.
- * @key: Name of property.
- *
- * Look up the value for @key on @device and convert it to an unsigned
- * 64-bit integer using g_ascii_strtoull().
- *
- * Returns: The value for @key or 0 if @key doesn't exist or isn't a
- * #guint64.
- */
-guint64
-g_udev_device_get_property_as_uint64 (GUdevDevice *device,
- const gchar *key)
-{
- guint64 result;
- const gchar *s;
-
- g_return_val_if_fail (G_UDEV_IS_DEVICE (device), 0);
- g_return_val_if_fail (key != NULL, 0);
-
- result = 0;
- s = g_udev_device_get_property (device, key);
- if (s == NULL)
- goto out;
-
- result = g_ascii_strtoull (s, NULL, 0);
-out:
- return result;
-}
-
-/**
- * g_udev_device_get_property_as_double:
- * @device: A #GUdevDevice.
- * @key: Name of property.
- *
- * Look up the value for @key on @device and convert it to a double
- * precision floating point number using strtod().
- *
- * Returns: The value for @key or 0.0 if @key doesn't exist or isn't a
- * #gdouble.
- */
-gdouble
-g_udev_device_get_property_as_double (GUdevDevice *device,
- const gchar *key)
-{
- gdouble result;
- const gchar *s;
-
- g_return_val_if_fail (G_UDEV_IS_DEVICE (device), 0.0);
- g_return_val_if_fail (key != NULL, 0.0);
-
- result = 0.0;
- s = g_udev_device_get_property (device, key);
- if (s == NULL)
- goto out;
-
- result = strtod (s, NULL);
-out:
- return result;
-}
-
-/**
- * g_udev_device_get_property_as_boolean:
- * @device: A #GUdevDevice.
- * @key: Name of property.
- *
- * Look up the value for @key on @device and convert it to an
- * boolean. This is done by doing a case-insensitive string comparison
- * on the string value against "1" and "true".
- *
- * Returns: The value for @key or %FALSE if @key doesn't exist or
- * isn't a #gboolean.
- */
-gboolean
-g_udev_device_get_property_as_boolean (GUdevDevice *device,
- const gchar *key)
-{
- gboolean result;
- const gchar *s;
-
- g_return_val_if_fail (G_UDEV_IS_DEVICE (device), FALSE);
- g_return_val_if_fail (key != NULL, FALSE);
-
- result = FALSE;
- s = g_udev_device_get_property (device, key);
- if (s == NULL)
- goto out;
-
- if (strcmp (s, "1") == 0 || g_ascii_strcasecmp (s, "true") == 0)
- result = TRUE;
- out:
- return result;
-}
-
-static gchar **
-split_at_whitespace (const gchar *s)
-{
- gchar **result;
- guint n;
- guint m;
-
- result = g_strsplit_set (s, " \v\t\r\n", 0);
-
- /* remove empty strings, thanks GLib */
- for (n = 0; result[n] != NULL; n++)
- {
- if (strlen (result[n]) == 0)
- {
- g_free (result[n]);
- for (m = n; result[m] != NULL; m++)
- result[m] = result[m + 1];
- n--;
- }
- }
-
- return result;
-}
-
-/**
- * g_udev_device_get_property_as_strv:
- * @device: A #GUdevDevice.
- * @key: Name of property.
- *
- * Look up the value for @key on @device and return the result of
- * splitting it into non-empty tokens split at white space (only space
- * (' '), form-feed ('\f'), newline ('\n'), carriage return ('\r'),
- * horizontal tab ('\t'), and vertical tab ('\v') are considered; the
- * locale is not taken into account).
- *
- * Returns: (nullable) (transfer none) (array zero-terminated=1) (element-type utf8):
- * The value of @key on @device split into tokens or %NULL if @key
- * doesn't exist. This array is owned by @device and should not be
- * freed by the caller.
- */
-const gchar* const *
-g_udev_device_get_property_as_strv (GUdevDevice *device,
- const gchar *key)
-{
- gchar **result;
- const gchar *s;
-
- g_return_val_if_fail (G_UDEV_IS_DEVICE (device), NULL);
- g_return_val_if_fail (key != NULL, NULL);
-
- if (device->priv->prop_strvs != NULL)
- {
- result = g_hash_table_lookup (device->priv->prop_strvs, key);
- if (result != NULL)
- goto out;
- }
-
- result = NULL;
- s = g_udev_device_get_property (device, key);
- if (s == NULL)
- goto out;
-
- result = split_at_whitespace (s);
- if (result == NULL)
- goto out;
-
- if (device->priv->prop_strvs == NULL)
- device->priv->prop_strvs = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_strfreev);
- g_hash_table_insert (device->priv->prop_strvs, g_strdup (key), result);
-
-out:
- return (const gchar* const *) result;
-}
-
-/* ---------------------------------------------------------------------------------------------------- */
-
-/**
- * g_udev_device_get_sysfs_attr_keys:
- * @device: A #GUdevDevice.
- *
- * Gets all keys for sysfs attributes on @device.
- *
- * Returns: (transfer none) (array zero-terminated=1) (element-type utf8): A %NULL terminated string array of sysfs attribute keys. This array is owned by @device and should not be freed by the caller.
- */
-const gchar * const *
-g_udev_device_get_sysfs_attr_keys (GUdevDevice *device)
-{
- struct udev_list_entry *l;
- GPtrArray *p;
-
- g_return_val_if_fail (G_UDEV_IS_DEVICE (device), NULL);
-
- if (device->priv->sysfs_attr_keys != NULL)
- goto out;
-
- p = g_ptr_array_new ();
- for (l = udev_device_get_sysattr_list_entry (device->priv->udevice); l != NULL; l = udev_list_entry_get_next (l))
- {
- g_ptr_array_add (p, g_strdup (udev_list_entry_get_name (l)));
- }
- g_ptr_array_add (p, NULL);
- device->priv->sysfs_attr_keys = (gchar **) g_ptr_array_free (p, FALSE);
-
- out:
- return (const gchar * const *) device->priv->sysfs_attr_keys;
-}
-
-/**
- * g_udev_device_has_sysfs_attr:
- * @device: A #GUdevDevice.
- * @key: Name of sysfs attribute.
- *
- * Check if a the sysfs attribute with the given key exists.
- *
- * Returns: %TRUE only if the value for @key exist.
- */
-gboolean
-g_udev_device_has_sysfs_attr (GUdevDevice *device,
- const gchar *key)
-{
- g_return_val_if_fail (G_UDEV_IS_DEVICE (device), FALSE);
- g_return_val_if_fail (key != NULL, FALSE);
- return udev_device_get_sysattr_value (device->priv->udevice, key) != NULL;
-}
-
-/**
- * g_udev_device_get_sysfs_attr:
- * @device: A #GUdevDevice.
- * @name: Name of the sysfs attribute.
- *
- * Look up the sysfs attribute with @name on @device.
- *
- * Returns: (nullable): The value of the sysfs attribute or %NULL if
- * there is no such attribute. Do not free this string, it is owned by
- * @device.
- */
-const gchar *
-g_udev_device_get_sysfs_attr (GUdevDevice *device,
- const gchar *name)
-{
- g_return_val_if_fail (G_UDEV_IS_DEVICE (device), NULL);
- g_return_val_if_fail (name != NULL, NULL);
- return udev_device_get_sysattr_value (device->priv->udevice, name);
-}
-
-/**
- * g_udev_device_get_sysfs_attr_as_int:
- * @device: A #GUdevDevice.
- * @name: Name of the sysfs attribute.
- *
- * Look up the sysfs attribute with @name on @device and convert it to an integer
- * using strtol().
- *
- * Returns: The value of the sysfs attribute or 0 if there is no such
- * attribute.
- */
-gint
-g_udev_device_get_sysfs_attr_as_int (GUdevDevice *device,
- const gchar *name)
-{
- gint result;
- const gchar *s;
-
- g_return_val_if_fail (G_UDEV_IS_DEVICE (device), 0);
- g_return_val_if_fail (name != NULL, 0);
-
- result = 0;
- s = g_udev_device_get_sysfs_attr (device, name);
- if (s == NULL)
- goto out;
-
- result = strtol (s, NULL, 0);
-out:
- return result;
-}
-
-/**
- * g_udev_device_get_sysfs_attr_as_uint64:
- * @device: A #GUdevDevice.
- * @name: Name of the sysfs attribute.
- *
- * Look up the sysfs attribute with @name on @device and convert it to an unsigned
- * 64-bit integer using g_ascii_strtoull().
- *
- * Returns: The value of the sysfs attribute or 0 if there is no such
- * attribute.
- */
-guint64
-g_udev_device_get_sysfs_attr_as_uint64 (GUdevDevice *device,
- const gchar *name)
-{
- guint64 result;
- const gchar *s;
-
- g_return_val_if_fail (G_UDEV_IS_DEVICE (device), 0);
- g_return_val_if_fail (name != NULL, 0);
-
- result = 0;
- s = g_udev_device_get_sysfs_attr (device, name);
- if (s == NULL)
- goto out;
-
- result = g_ascii_strtoull (s, NULL, 0);
-out:
- return result;
-}
-
-/**
- * g_udev_device_get_sysfs_attr_as_double:
- * @device: A #GUdevDevice.
- * @name: Name of the sysfs attribute.
- *
- * Look up the sysfs attribute with @name on @device and convert it to a double
- * precision floating point number using strtod().
- *
- * Returns: The value of the sysfs attribute or 0.0 if there is no such
- * attribute.
- */
-gdouble
-g_udev_device_get_sysfs_attr_as_double (GUdevDevice *device,
- const gchar *name)
-{
- gdouble result;
- const gchar *s;
-
- g_return_val_if_fail (G_UDEV_IS_DEVICE (device), 0.0);
- g_return_val_if_fail (name != NULL, 0.0);
-
- result = 0.0;
- s = g_udev_device_get_sysfs_attr (device, name);
- if (s == NULL)
- goto out;
-
- result = strtod (s, NULL);
-out:
- return result;
-}
-
-/**
- * g_udev_device_get_sysfs_attr_as_boolean:
- * @device: A #GUdevDevice.
- * @name: Name of the sysfs attribute.
- *
- * Look up the sysfs attribute with @name on @device and convert it to an
- * boolean. This is done by doing a case-insensitive string comparison
- * on the string value against "1" and "true".
- *
- * Returns: The value of the sysfs attribute or %FALSE if there is no such
- * attribute.
- */
-gboolean
-g_udev_device_get_sysfs_attr_as_boolean (GUdevDevice *device,
- const gchar *name)
-{
- gboolean result;
- const gchar *s;
-
- g_return_val_if_fail (G_UDEV_IS_DEVICE (device), FALSE);
- g_return_val_if_fail (name != NULL, FALSE);
-
- result = FALSE;
- s = g_udev_device_get_sysfs_attr (device, name);
- if (s == NULL)
- goto out;
-
- if (strcmp (s, "1") == 0 || g_ascii_strcasecmp (s, "true") == 0)
- result = TRUE;
- out:
- return result;
-}
-
-/**
- * g_udev_device_get_sysfs_attr_as_strv:
- * @device: A #GUdevDevice.
- * @name: Name of the sysfs attribute.
- *
- * Look up the sysfs attribute with @name on @device and return the result of
- * splitting it into non-empty tokens split at white space (only space (' '),
- * form-feed ('\f'), newline ('\n'), carriage return ('\r'), horizontal
- * tab ('\t'), and vertical tab ('\v') are considered; the locale is
- * not taken into account).
- *
- * Returns: (nullable) (transfer none) (array zero-terminated=1) (element-type utf8):
- * The value of the sysfs attribute split into tokens or %NULL if
- * there is no such attribute. This array is owned by @device and
- * should not be freed by the caller.
- */
-const gchar * const *
-g_udev_device_get_sysfs_attr_as_strv (GUdevDevice *device,
- const gchar *name)
-{
- gchar **result;
- const gchar *s;
-
- g_return_val_if_fail (G_UDEV_IS_DEVICE (device), NULL);
- g_return_val_if_fail (name != NULL, NULL);
-
- if (device->priv->sysfs_attr_strvs != NULL)
- {
- result = g_hash_table_lookup (device->priv->sysfs_attr_strvs, name);
- if (result != NULL)
- goto out;
- }
-
- result = NULL;
- s = g_udev_device_get_sysfs_attr (device, name);
- if (s == NULL)
- goto out;
-
- result = split_at_whitespace (s);
- if (result == NULL)
- goto out;
-
- if (device->priv->sysfs_attr_strvs == NULL)
- device->priv->sysfs_attr_strvs = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_strfreev);
- g_hash_table_insert (device->priv->sysfs_attr_strvs, g_strdup (name), result);
-
-out:
- return (const gchar* const *) result;
-}
-
-/**
- * g_udev_device_get_tags:
- * @device: A #GUdevDevice.
- *
- * Gets all tags for @device.
- *
- * Returns: (transfer none) (array zero-terminated=1) (element-type utf8): A %NULL terminated string array of tags. This array is owned by @device and should not be freed by the caller.
- *
- * Since: 165
- */
-const gchar* const *
-g_udev_device_get_tags (GUdevDevice *device)
-{
- struct udev_list_entry *l;
- GPtrArray *p;
-
- g_return_val_if_fail (G_UDEV_IS_DEVICE (device), NULL);
-
- if (device->priv->tags != NULL)
- goto out;
-
- p = g_ptr_array_new ();
- for (l = udev_device_get_tags_list_entry (device->priv->udevice); l != NULL; l = udev_list_entry_get_next (l))
- {
- g_ptr_array_add (p, g_strdup (udev_list_entry_get_name (l)));
- }
- g_ptr_array_add (p, NULL);
- device->priv->tags = (gchar **) g_ptr_array_free (p, FALSE);
-
- out:
- return (const gchar * const *) device->priv->tags;
-}
-
-/**
- * g_udev_device_get_is_initialized:
- * @device: A #GUdevDevice.
- *
- * Gets whether @device has been initalized.
- *
- * Returns: Whether @device has been initialized.
- *
- * Since: 165
- */
-gboolean
-g_udev_device_get_is_initialized (GUdevDevice *device)
-{
- g_return_val_if_fail (G_UDEV_IS_DEVICE (device), FALSE);
- return udev_device_get_is_initialized (device->priv->udevice);
-}
-
-/**
- * g_udev_device_get_usec_since_initialized:
- * @device: A #GUdevDevice.
- *
- * Gets number of micro-seconds since @device was initialized.
- *
- * This only works for devices with properties in the udev
- * database. All other devices return 0.
- *
- * Returns: Number of micro-seconds since @device was initialized or 0 if unknown.
- *
- * Since: 165
- */
-guint64
-g_udev_device_get_usec_since_initialized (GUdevDevice *device)
-{
- g_return_val_if_fail (G_UDEV_IS_DEVICE (device), 0);
- return udev_device_get_usec_since_initialized (device->priv->udevice);
-}
diff --git a/src/gudev/gudevdevice.h b/src/gudev/gudevdevice.h
deleted file mode 100644
index 72ec180f55..0000000000
--- a/src/gudev/gudevdevice.h
+++ /dev/null
@@ -1,130 +0,0 @@
-/* -*- Mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2008 David Zeuthen <davidz@redhat.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#if !defined (_GUDEV_COMPILATION) && !defined(_GUDEV_INSIDE_GUDEV_H)
-#error "Only <gudev/gudev.h> can be included directly, this file may disappear or change contents."
-#endif
-
-#ifndef __G_UDEV_DEVICE_H__
-#define __G_UDEV_DEVICE_H__
-
-#include <gudev/gudevtypes.h>
-
-G_BEGIN_DECLS
-
-#define G_UDEV_TYPE_DEVICE (g_udev_device_get_type ())
-#define G_UDEV_DEVICE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_UDEV_TYPE_DEVICE, GUdevDevice))
-#define G_UDEV_DEVICE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_UDEV_TYPE_DEVICE, GUdevDeviceClass))
-#define G_UDEV_IS_DEVICE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_UDEV_TYPE_DEVICE))
-#define G_UDEV_IS_DEVICE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_UDEV_TYPE_DEVICE))
-#define G_UDEV_DEVICE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_UDEV_TYPE_DEVICE, GUdevDeviceClass))
-
-typedef struct _GUdevDeviceClass GUdevDeviceClass;
-typedef struct _GUdevDevicePrivate GUdevDevicePrivate;
-
-/**
- * GUdevDevice:
- *
- * The #GUdevDevice struct is opaque and should not be accessed directly.
- */
-struct _GUdevDevice
-{
- GObject parent;
-
- /*< private >*/
- GUdevDevicePrivate *priv;
-};
-
-/**
- * GUdevDeviceClass:
- * @parent_class: Parent class.
- *
- * Class structure for #GUdevDevice.
- */
-struct _GUdevDeviceClass
-{
- GObjectClass parent_class;
-
- /*< private >*/
- /* Padding for future expansion */
- void (*reserved1) (void);
- void (*reserved2) (void);
- void (*reserved3) (void);
- void (*reserved4) (void);
- void (*reserved5) (void);
- void (*reserved6) (void);
- void (*reserved7) (void);
- void (*reserved8) (void);
-};
-
-GType g_udev_device_get_type (void) G_GNUC_CONST;
-gboolean g_udev_device_get_is_initialized (GUdevDevice *device);
-guint64 g_udev_device_get_usec_since_initialized (GUdevDevice *device);
-const gchar *g_udev_device_get_subsystem (GUdevDevice *device);
-const gchar *g_udev_device_get_devtype (GUdevDevice *device);
-const gchar *g_udev_device_get_name (GUdevDevice *device);
-const gchar *g_udev_device_get_number (GUdevDevice *device);
-const gchar *g_udev_device_get_sysfs_path (GUdevDevice *device);
-const gchar *g_udev_device_get_driver (GUdevDevice *device);
-const gchar *g_udev_device_get_action (GUdevDevice *device);
-guint64 g_udev_device_get_seqnum (GUdevDevice *device);
-GUdevDeviceType g_udev_device_get_device_type (GUdevDevice *device);
-GUdevDeviceNumber g_udev_device_get_device_number (GUdevDevice *device);
-const gchar *g_udev_device_get_device_file (GUdevDevice *device);
-const gchar* const *g_udev_device_get_device_file_symlinks (GUdevDevice *device);
-GUdevDevice *g_udev_device_get_parent (GUdevDevice *device);
-GUdevDevice *g_udev_device_get_parent_with_subsystem (GUdevDevice *device,
- const gchar *subsystem,
- const gchar *devtype);
-const gchar* const *g_udev_device_get_property_keys (GUdevDevice *device);
-gboolean g_udev_device_has_property (GUdevDevice *device,
- const gchar *key);
-const gchar *g_udev_device_get_property (GUdevDevice *device,
- const gchar *key);
-gint g_udev_device_get_property_as_int (GUdevDevice *device,
- const gchar *key);
-guint64 g_udev_device_get_property_as_uint64 (GUdevDevice *device,
- const gchar *key);
-gdouble g_udev_device_get_property_as_double (GUdevDevice *device,
- const gchar *key);
-gboolean g_udev_device_get_property_as_boolean (GUdevDevice *device,
- const gchar *key);
-const gchar* const *g_udev_device_get_property_as_strv (GUdevDevice *device,
- const gchar *key);
-
-const gchar* const *g_udev_device_get_sysfs_attr_keys (GUdevDevice *device);
-gboolean g_udev_device_has_sysfs_attr (GUdevDevice *device,
- const gchar *key);
-const gchar *g_udev_device_get_sysfs_attr (GUdevDevice *device,
- const gchar *name);
-gint g_udev_device_get_sysfs_attr_as_int (GUdevDevice *device,
- const gchar *name);
-guint64 g_udev_device_get_sysfs_attr_as_uint64 (GUdevDevice *device,
- const gchar *name);
-gdouble g_udev_device_get_sysfs_attr_as_double (GUdevDevice *device,
- const gchar *name);
-gboolean g_udev_device_get_sysfs_attr_as_boolean (GUdevDevice *device,
- const gchar *name);
-const gchar* const *g_udev_device_get_sysfs_attr_as_strv (GUdevDevice *device,
- const gchar *name);
-const gchar* const *g_udev_device_get_tags (GUdevDevice *device);
-
-G_END_DECLS
-
-#endif /* __G_UDEV_DEVICE_H__ */
diff --git a/src/gudev/gudevenumerator.c b/src/gudev/gudevenumerator.c
deleted file mode 100644
index 1fb3098709..0000000000
--- a/src/gudev/gudevenumerator.c
+++ /dev/null
@@ -1,429 +0,0 @@
-/* -*- Mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2008-2010 David Zeuthen <davidz@redhat.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#ifdef HAVE_CONFIG_H
-# include "config.h"
-#endif
-
-#include <stdlib.h>
-#include <string.h>
-
-#include "gudevclient.h"
-#include "gudevenumerator.h"
-#include "gudevdevice.h"
-#include "gudevmarshal.h"
-#include "gudevprivate.h"
-
-/**
- * SECTION:gudevenumerator
- * @short_description: Lookup and sort devices
- *
- * #GUdevEnumerator is used to lookup and sort devices.
- *
- * Since: 165
- */
-
-struct _GUdevEnumeratorPrivate
-{
- GUdevClient *client;
- struct udev_enumerate *e;
-};
-
-enum
-{
- PROP_0,
- PROP_CLIENT,
-};
-
-G_DEFINE_TYPE (GUdevEnumerator, g_udev_enumerator, G_TYPE_OBJECT)
-
-/* ---------------------------------------------------------------------------------------------------- */
-
-static void
-g_udev_enumerator_finalize (GObject *object)
-{
- GUdevEnumerator *enumerator = G_UDEV_ENUMERATOR (object);
-
- if (enumerator->priv->client != NULL)
- {
- g_object_unref (enumerator->priv->client);
- enumerator->priv->client = NULL;
- }
-
- if (enumerator->priv->e != NULL)
- {
- udev_enumerate_unref (enumerator->priv->e);
- enumerator->priv->e = NULL;
- }
-
- if (G_OBJECT_CLASS (g_udev_enumerator_parent_class)->finalize != NULL)
- G_OBJECT_CLASS (g_udev_enumerator_parent_class)->finalize (object);
-}
-
-static void
-g_udev_enumerator_set_property (GObject *object,
- guint prop_id,
- const GValue *value,
- GParamSpec *pspec)
-{
- GUdevEnumerator *enumerator = G_UDEV_ENUMERATOR (object);
-
- switch (prop_id)
- {
- case PROP_CLIENT:
- if (enumerator->priv->client != NULL)
- g_object_unref (enumerator->priv->client);
- enumerator->priv->client = g_value_dup_object (value);
- break;
-
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- break;
- }
-}
-
-static void
-g_udev_enumerator_get_property (GObject *object,
- guint prop_id,
- GValue *value,
- GParamSpec *pspec)
-{
- GUdevEnumerator *enumerator = G_UDEV_ENUMERATOR (object);
-
- switch (prop_id)
- {
- case PROP_CLIENT:
- g_value_set_object (value, enumerator->priv->client);
- break;
-
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- break;
- }
-}
-
-static void
-g_udev_enumerator_constructed (GObject *object)
-{
- GUdevEnumerator *enumerator = G_UDEV_ENUMERATOR (object);
-
- g_assert (G_UDEV_IS_CLIENT (enumerator->priv->client));
-
- enumerator->priv->e = udev_enumerate_new (_g_udev_client_get_udev (enumerator->priv->client));
-
- if (G_OBJECT_CLASS (g_udev_enumerator_parent_class)->constructed != NULL)
- G_OBJECT_CLASS (g_udev_enumerator_parent_class)->constructed (object);
-}
-
-static void
-g_udev_enumerator_class_init (GUdevEnumeratorClass *klass)
-{
- GObjectClass *gobject_class = (GObjectClass *) klass;
-
- gobject_class->finalize = g_udev_enumerator_finalize;
- gobject_class->set_property = g_udev_enumerator_set_property;
- gobject_class->get_property = g_udev_enumerator_get_property;
- gobject_class->constructed = g_udev_enumerator_constructed;
-
- /**
- * GUdevEnumerator:client:
- *
- * The #GUdevClient to enumerate devices from.
- *
- * Since: 165
- */
- g_object_class_install_property (gobject_class,
- PROP_CLIENT,
- g_param_spec_object ("client",
- "The client to enumerate devices from",
- "The client to enumerate devices from",
- G_UDEV_TYPE_CLIENT,
- G_PARAM_CONSTRUCT_ONLY |
- G_PARAM_READWRITE));
-
- g_type_class_add_private (klass, sizeof (GUdevEnumeratorPrivate));
-}
-
-static void
-g_udev_enumerator_init (GUdevEnumerator *enumerator)
-{
- enumerator->priv = G_TYPE_INSTANCE_GET_PRIVATE (enumerator,
- G_UDEV_TYPE_ENUMERATOR,
- GUdevEnumeratorPrivate);
-}
-
-/**
- * g_udev_enumerator_new:
- * @client: A #GUdevClient to enumerate devices from.
- *
- * Constructs a #GUdevEnumerator object that can be used to enumerate
- * and sort devices. Use the add_match_*() and add_nomatch_*() methods
- * and execute the query to get a list of devices with
- * g_udev_enumerator_execute().
- *
- * Returns: A new #GUdevEnumerator object. Free with g_object_unref().
- *
- * Since: 165
- */
-GUdevEnumerator *
-g_udev_enumerator_new (GUdevClient *client)
-{
- g_return_val_if_fail (G_UDEV_IS_CLIENT (client), NULL);
- return G_UDEV_ENUMERATOR (g_object_new (G_UDEV_TYPE_ENUMERATOR, "client", client, NULL));
-}
-
-
-/**
- * g_udev_enumerator_add_match_subsystem:
- * @enumerator: A #GUdevEnumerator.
- * @subsystem: Wildcard for subsystem name e.g. 'scsi' or 'a*'.
- *
- * All returned devices will match the given @subsystem.
- *
- * Returns: (transfer none): The passed in @enumerator.
- *
- * Since: 165
- */
-GUdevEnumerator *
-g_udev_enumerator_add_match_subsystem (GUdevEnumerator *enumerator,
- const gchar *subsystem)
-{
- g_return_val_if_fail (G_UDEV_IS_ENUMERATOR (enumerator), NULL);
- g_return_val_if_fail (subsystem != NULL, NULL);
- udev_enumerate_add_match_subsystem (enumerator->priv->e, subsystem);
- return enumerator;
-}
-
-/**
- * g_udev_enumerator_add_nomatch_subsystem:
- * @enumerator: A #GUdevEnumerator.
- * @subsystem: Wildcard for subsystem name e.g. 'scsi' or 'a*'.
- *
- * All returned devices will not match the given @subsystem.
- *
- * Returns: (transfer none): The passed in @enumerator.
- *
- * Since: 165
- */
-GUdevEnumerator *
-g_udev_enumerator_add_nomatch_subsystem (GUdevEnumerator *enumerator,
- const gchar *subsystem)
-{
- g_return_val_if_fail (G_UDEV_IS_ENUMERATOR (enumerator), NULL);
- g_return_val_if_fail (subsystem != NULL, NULL);
- udev_enumerate_add_nomatch_subsystem (enumerator->priv->e, subsystem);
- return enumerator;
-}
-
-/**
- * g_udev_enumerator_add_match_sysfs_attr:
- * @enumerator: A #GUdevEnumerator.
- * @name: Wildcard filter for sysfs attribute key.
- * @value: Wildcard filter for sysfs attribute value.
- *
- * All returned devices will have a sysfs attribute matching the given @name and @value.
- *
- * Returns: (transfer none): The passed in @enumerator.
- *
- * Since: 165
- */
-GUdevEnumerator *
-g_udev_enumerator_add_match_sysfs_attr (GUdevEnumerator *enumerator,
- const gchar *name,
- const gchar *value)
-{
- g_return_val_if_fail (G_UDEV_IS_ENUMERATOR (enumerator), NULL);
- g_return_val_if_fail (name != NULL, NULL);
- g_return_val_if_fail (value != NULL, NULL);
- udev_enumerate_add_match_sysattr (enumerator->priv->e, name, value);
- return enumerator;
-}
-
-/**
- * g_udev_enumerator_add_nomatch_sysfs_attr:
- * @enumerator: A #GUdevEnumerator.
- * @name: Wildcard filter for sysfs attribute key.
- * @value: Wildcard filter for sysfs attribute value.
- *
- * All returned devices will not have a sysfs attribute matching the given @name and @value.
- *
- * Returns: (transfer none): The passed in @enumerator.
- *
- * Since: 165
- */
-GUdevEnumerator *
-g_udev_enumerator_add_nomatch_sysfs_attr (GUdevEnumerator *enumerator,
- const gchar *name,
- const gchar *value)
-{
- g_return_val_if_fail (G_UDEV_IS_ENUMERATOR (enumerator), NULL);
- g_return_val_if_fail (name != NULL, NULL);
- g_return_val_if_fail (value != NULL, NULL);
- udev_enumerate_add_nomatch_sysattr (enumerator->priv->e, name, value);
- return enumerator;
-}
-
-/**
- * g_udev_enumerator_add_match_property:
- * @enumerator: A #GUdevEnumerator.
- * @name: Wildcard filter for property name.
- * @value: Wildcard filter for property value.
- *
- * All returned devices will have a property matching the given @name and @value.
- *
- * Returns: (transfer none): The passed in @enumerator.
- *
- * Since: 165
- */
-GUdevEnumerator *
-g_udev_enumerator_add_match_property (GUdevEnumerator *enumerator,
- const gchar *name,
- const gchar *value)
-{
- g_return_val_if_fail (G_UDEV_IS_ENUMERATOR (enumerator), NULL);
- g_return_val_if_fail (name != NULL, NULL);
- g_return_val_if_fail (value != NULL, NULL);
- udev_enumerate_add_match_property (enumerator->priv->e, name, value);
- return enumerator;
-}
-
-/**
- * g_udev_enumerator_add_match_name:
- * @enumerator: A #GUdevEnumerator.
- * @name: Wildcard filter for kernel name e.g. "sda*".
- *
- * All returned devices will match the given @name.
- *
- * Returns: (transfer none): The passed in @enumerator.
- *
- * Since: 165
- */
-GUdevEnumerator *
-g_udev_enumerator_add_match_name (GUdevEnumerator *enumerator,
- const gchar *name)
-{
- g_return_val_if_fail (G_UDEV_IS_ENUMERATOR (enumerator), NULL);
- g_return_val_if_fail (name != NULL, NULL);
- udev_enumerate_add_match_sysname (enumerator->priv->e, name);
- return enumerator;
-}
-
-/**
- * g_udev_enumerator_add_sysfs_path:
- * @enumerator: A #GUdevEnumerator.
- * @sysfs_path: A sysfs path, e.g. "/sys/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda"
- *
- * Add a device to the list of devices, to retrieve it back sorted in dependency order.
- *
- * Returns: (transfer none): The passed in @enumerator.
- *
- * Since: 165
- */
-GUdevEnumerator *
-g_udev_enumerator_add_sysfs_path (GUdevEnumerator *enumerator,
- const gchar *sysfs_path)
-{
- g_return_val_if_fail (G_UDEV_IS_ENUMERATOR (enumerator), NULL);
- g_return_val_if_fail (sysfs_path != NULL, NULL);
- udev_enumerate_add_syspath (enumerator->priv->e, sysfs_path);
- return enumerator;
-}
-
-/**
- * g_udev_enumerator_add_match_tag:
- * @enumerator: A #GUdevEnumerator.
- * @tag: A udev tag e.g. "udev-acl".
- *
- * All returned devices will match the given @tag.
- *
- * Returns: (transfer none): The passed in @enumerator.
- *
- * Since: 165
- */
-GUdevEnumerator *
-g_udev_enumerator_add_match_tag (GUdevEnumerator *enumerator,
- const gchar *tag)
-{
- g_return_val_if_fail (G_UDEV_IS_ENUMERATOR (enumerator), NULL);
- g_return_val_if_fail (tag != NULL, NULL);
- udev_enumerate_add_match_tag (enumerator->priv->e, tag);
- return enumerator;
-}
-
-/**
- * g_udev_enumerator_add_match_is_initialized:
- * @enumerator: A #GUdevEnumerator.
- *
- * All returned devices will be initialized.
- *
- * Returns: (transfer none): The passed in @enumerator.
- *
- * Since: 165
- */
-GUdevEnumerator *
-g_udev_enumerator_add_match_is_initialized (GUdevEnumerator *enumerator)
-{
- g_return_val_if_fail (G_UDEV_IS_ENUMERATOR (enumerator), NULL);
- udev_enumerate_add_match_is_initialized (enumerator->priv->e);
- return enumerator;
-}
-
-/**
- * g_udev_enumerator_execute:
- * @enumerator: A #GUdevEnumerator.
- *
- * Executes the query in @enumerator.
- *
- * Returns: (element-type GUdevDevice) (transfer full): A list of #GUdevDevice objects. The caller should free the result by using g_object_unref() on each element in the list and then g_list_free() on the list.
- *
- * Since: 165
- */
-GList *
-g_udev_enumerator_execute (GUdevEnumerator *enumerator)
-{
- GList *ret;
- struct udev_list_entry *l, *devices;
-
- g_return_val_if_fail (G_UDEV_IS_ENUMERATOR (enumerator), NULL);
-
- ret = NULL;
-
- /* retrieve the list */
- udev_enumerate_scan_devices (enumerator->priv->e);
-
- devices = udev_enumerate_get_list_entry (enumerator->priv->e);
- for (l = devices; l != NULL; l = udev_list_entry_get_next (l))
- {
- struct udev_device *udevice;
- GUdevDevice *device;
-
- udevice = udev_device_new_from_syspath (udev_enumerate_get_udev (enumerator->priv->e),
- udev_list_entry_get_name (l));
- if (udevice == NULL)
- continue;
-
- device = _g_udev_device_new (udevice);
- udev_device_unref (udevice);
- ret = g_list_prepend (ret, device);
- }
-
- ret = g_list_reverse (ret);
-
- return ret;
-}
diff --git a/src/gudev/gudevenumerator.h b/src/gudev/gudevenumerator.h
deleted file mode 100644
index e1dbcf1441..0000000000
--- a/src/gudev/gudevenumerator.h
+++ /dev/null
@@ -1,106 +0,0 @@
-/* -*- Mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2008-2010 David Zeuthen <davidz@redhat.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#if !defined (_GUDEV_COMPILATION) && !defined(_GUDEV_INSIDE_GUDEV_H)
-#error "Only <gudev/gudev.h> can be included directly, this file may disappear or change contents."
-#endif
-
-#ifndef __G_UDEV_ENUMERATOR_H__
-#define __G_UDEV_ENUMERATOR_H__
-
-#include <gudev/gudevtypes.h>
-
-G_BEGIN_DECLS
-
-#define G_UDEV_TYPE_ENUMERATOR (g_udev_enumerator_get_type ())
-#define G_UDEV_ENUMERATOR(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_UDEV_TYPE_ENUMERATOR, GUdevEnumerator))
-#define G_UDEV_ENUMERATOR_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_UDEV_TYPE_ENUMERATOR, GUdevEnumeratorClass))
-#define G_UDEV_IS_ENUMERATOR(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_UDEV_TYPE_ENUMERATOR))
-#define G_UDEV_IS_ENUMERATOR_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_UDEV_TYPE_ENUMERATOR))
-#define G_UDEV_ENUMERATOR_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_UDEV_TYPE_ENUMERATOR, GUdevEnumeratorClass))
-
-typedef struct _GUdevEnumeratorClass GUdevEnumeratorClass;
-typedef struct _GUdevEnumeratorPrivate GUdevEnumeratorPrivate;
-
-/**
- * GUdevEnumerator:
- *
- * The #GUdevEnumerator struct is opaque and should not be accessed directly.
- *
- * Since: 165
- */
-struct _GUdevEnumerator
-{
- GObject parent;
-
- /*< private >*/
- GUdevEnumeratorPrivate *priv;
-};
-
-/**
- * GUdevEnumeratorClass:
- * @parent_class: Parent class.
- *
- * Class structure for #GUdevEnumerator.
- *
- * Since: 165
- */
-struct _GUdevEnumeratorClass
-{
- GObjectClass parent_class;
-
- /*< private >*/
- /* Padding for future expansion */
- void (*reserved1) (void);
- void (*reserved2) (void);
- void (*reserved3) (void);
- void (*reserved4) (void);
- void (*reserved5) (void);
- void (*reserved6) (void);
- void (*reserved7) (void);
- void (*reserved8) (void);
-};
-
-GType g_udev_enumerator_get_type (void) G_GNUC_CONST;
-GUdevEnumerator *g_udev_enumerator_new (GUdevClient *client);
-GUdevEnumerator *g_udev_enumerator_add_match_subsystem (GUdevEnumerator *enumerator,
- const gchar *subsystem);
-GUdevEnumerator *g_udev_enumerator_add_nomatch_subsystem (GUdevEnumerator *enumerator,
- const gchar *subsystem);
-GUdevEnumerator *g_udev_enumerator_add_match_sysfs_attr (GUdevEnumerator *enumerator,
- const gchar *name,
- const gchar *value);
-GUdevEnumerator *g_udev_enumerator_add_nomatch_sysfs_attr (GUdevEnumerator *enumerator,
- const gchar *name,
- const gchar *value);
-GUdevEnumerator *g_udev_enumerator_add_match_property (GUdevEnumerator *enumerator,
- const gchar *name,
- const gchar *value);
-GUdevEnumerator *g_udev_enumerator_add_match_name (GUdevEnumerator *enumerator,
- const gchar *name);
-GUdevEnumerator *g_udev_enumerator_add_match_tag (GUdevEnumerator *enumerator,
- const gchar *tag);
-GUdevEnumerator *g_udev_enumerator_add_match_is_initialized (GUdevEnumerator *enumerator);
-GUdevEnumerator *g_udev_enumerator_add_sysfs_path (GUdevEnumerator *enumerator,
- const gchar *sysfs_path);
-GList *g_udev_enumerator_execute (GUdevEnumerator *enumerator);
-
-G_END_DECLS
-
-#endif /* __G_UDEV_ENUMERATOR_H__ */
diff --git a/src/gudev/gudevenums.h b/src/gudev/gudevenums.h
deleted file mode 100644
index 467e93bd60..0000000000
--- a/src/gudev/gudevenums.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/* -*- Mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2008 David Zeuthen <davidz@redhat.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#if !defined (_GUDEV_COMPILATION) && !defined(_GUDEV_INSIDE_GUDEV_H)
-#error "Only <gudev/gudev.h> can be included directly, this file may disappear or change contents."
-#endif
-
-#ifndef __G_UDEV_ENUMS_H__
-#define __G_UDEV_ENUMS_H__
-
-#include <glib-object.h>
-
-G_BEGIN_DECLS
-
-/**
- * GUdevDeviceType:
- * @G_UDEV_DEVICE_TYPE_NONE: Device does not have a device file.
- * @G_UDEV_DEVICE_TYPE_BLOCK: Device is a block device.
- * @G_UDEV_DEVICE_TYPE_CHAR: Device is a character device.
- *
- * Enumeration used to specify a the type of a device.
- */
-typedef enum
-{
- G_UDEV_DEVICE_TYPE_NONE = 0,
- G_UDEV_DEVICE_TYPE_BLOCK = 'b',
- G_UDEV_DEVICE_TYPE_CHAR = 'c',
-} GUdevDeviceType;
-
-G_END_DECLS
-
-#endif /* __G_UDEV_ENUMS_H__ */
diff --git a/src/gudev/gudevenumtypes.c.template b/src/gudev/gudevenumtypes.c.template
deleted file mode 100644
index fc30b39e2e..0000000000
--- a/src/gudev/gudevenumtypes.c.template
+++ /dev/null
@@ -1,39 +0,0 @@
-/*** BEGIN file-header ***/
-#include <gudev.h>
-
-/*** END file-header ***/
-
-/*** BEGIN file-production ***/
-/* enumerations from "@filename@" */
-/*** END file-production ***/
-
-/*** BEGIN value-header ***/
-GType
-@enum_name@_get_type (void)
-{
- static volatile gsize g_define_type_id__volatile = 0;
-
- if (g_once_init_enter (&g_define_type_id__volatile))
- {
- static const G@Type@Value values[] = {
-/*** END value-header ***/
-
-/*** BEGIN value-production ***/
- { @VALUENAME@, "@VALUENAME@", "@valuenick@" },
-/*** END value-production ***/
-
-/*** BEGIN value-tail ***/
- { 0, NULL, NULL }
- };
- GType g_define_type_id =
- g_@type@_register_static (g_intern_static_string ("@EnumName@"), values);
- g_once_init_leave (&g_define_type_id__volatile, g_define_type_id);
- }
-
- return g_define_type_id__volatile;
-}
-
-/*** END value-tail ***/
-
-/*** BEGIN file-tail ***/
-/*** END file-tail ***/
diff --git a/src/gudev/gudevenumtypes.h.template b/src/gudev/gudevenumtypes.h.template
deleted file mode 100644
index d0ab3393e6..0000000000
--- a/src/gudev/gudevenumtypes.h.template
+++ /dev/null
@@ -1,24 +0,0 @@
-/*** BEGIN file-header ***/
-#ifndef __GUDEV_ENUM_TYPES_H__
-#define __GUDEV_ENUM_TYPES_H__
-
-#include <glib-object.h>
-
-G_BEGIN_DECLS
-/*** END file-header ***/
-
-/*** BEGIN file-production ***/
-
-/* enumerations from "@filename@" */
-/*** END file-production ***/
-
-/*** BEGIN value-header ***/
-GType @enum_name@_get_type (void) G_GNUC_CONST;
-#define @ENUMPREFIX@_TYPE_@ENUMSHORT@ (@enum_name@_get_type ())
-/*** END value-header ***/
-
-/*** BEGIN file-tail ***/
-G_END_DECLS
-
-#endif /* __GUDEV_ENUM_TYPES_H__ */
-/*** END file-tail ***/
diff --git a/src/gudev/gudevmarshal.list b/src/gudev/gudevmarshal.list
deleted file mode 100644
index 7e665999e8..0000000000
--- a/src/gudev/gudevmarshal.list
+++ /dev/null
@@ -1 +0,0 @@
-VOID:STRING,OBJECT
diff --git a/src/gudev/gudevprivate.h b/src/gudev/gudevprivate.h
deleted file mode 100644
index 52e272be3d..0000000000
--- a/src/gudev/gudevprivate.h
+++ /dev/null
@@ -1,40 +0,0 @@
-/* -*- Mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2008 David Zeuthen <davidz@redhat.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#if !defined (_GUDEV_COMPILATION) && !defined(_GUDEV_INSIDE_GUDEV_H)
-#error "Only <gudev/gudev.h> can be included directly, this file may disappear or change contents."
-#endif
-
-#ifndef __G_UDEV_PRIVATE_H__
-#define __G_UDEV_PRIVATE_H__
-
-#include <gudev/gudevtypes.h>
-
-#include <libudev.h>
-
-G_BEGIN_DECLS
-
-GUdevDevice *
-_g_udev_device_new (struct udev_device *udevice);
-
-struct udev *_g_udev_client_get_udev (GUdevClient *client);
-
-G_END_DECLS
-
-#endif /* __G_UDEV_PRIVATE_H__ */
diff --git a/src/gudev/gudevtypes.h b/src/gudev/gudevtypes.h
deleted file mode 100644
index e2f688ff2d..0000000000
--- a/src/gudev/gudevtypes.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/* -*- Mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2008 David Zeuthen <davidz@redhat.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#if !defined (_GUDEV_COMPILATION) && !defined(_GUDEV_INSIDE_GUDEV_H)
-#error "Only <gudev/gudev.h> can be included directly, this file may disappear or change contents."
-#endif
-
-#ifndef __G_UDEV_TYPES_H__
-#define __G_UDEV_TYPES_H__
-
-#include <gudev/gudevenums.h>
-#include <sys/types.h>
-
-G_BEGIN_DECLS
-
-typedef struct _GUdevClient GUdevClient;
-typedef struct _GUdevDevice GUdevDevice;
-typedef struct _GUdevEnumerator GUdevEnumerator;
-
-/**
- * GUdevDeviceNumber:
- *
- * Corresponds to the standard #dev_t type as defined by POSIX (Until
- * bug 584517 is resolved this work-around is needed).
- */
-#ifdef _GUDEV_WORK_AROUND_DEV_T_BUG
-typedef guint64 GUdevDeviceNumber; /* __UQUAD_TYPE */
-#else
-typedef dev_t GUdevDeviceNumber;
-#endif
-
-G_END_DECLS
-
-#endif /* __G_UDEV_TYPES_H__ */
diff --git a/src/gudev/libgudev-1.0.sym b/src/gudev/libgudev-1.0.sym
deleted file mode 100644
index 0600486e9c..0000000000
--- a/src/gudev/libgudev-1.0.sym
+++ /dev/null
@@ -1,68 +0,0 @@
-/***
- This file is part of systemd.
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-***/
-
-{
-global:
- g_udev_client_get_type;
- g_udev_client_new;
- g_udev_client_query_by_device_file;
- g_udev_client_query_by_device_number;
- g_udev_client_query_by_subsystem;
- g_udev_client_query_by_subsystem_and_name;
- g_udev_client_query_by_sysfs_path;
- g_udev_device_get_action;
- g_udev_device_get_device_file;
- g_udev_device_get_device_file_symlinks;
- g_udev_device_get_device_number;
- g_udev_device_get_device_type;
- g_udev_device_get_devtype;
- g_udev_device_get_driver;
- g_udev_device_get_is_initialized;
- g_udev_device_get_name;
- g_udev_device_get_number;
- g_udev_device_get_parent;
- g_udev_device_get_parent_with_subsystem;
- g_udev_device_get_property;
- g_udev_device_get_property_as_boolean;
- g_udev_device_get_property_as_double;
- g_udev_device_get_property_as_int;
- g_udev_device_get_property_as_strv;
- g_udev_device_get_property_as_uint64;
- g_udev_device_get_property_keys;
- g_udev_device_get_seqnum;
- g_udev_device_get_subsystem;
- g_udev_device_get_sysfs_attr;
- g_udev_device_get_sysfs_attr_as_boolean;
- g_udev_device_get_sysfs_attr_as_double;
- g_udev_device_get_sysfs_attr_as_int;
- g_udev_device_get_sysfs_attr_as_strv;
- g_udev_device_get_sysfs_attr_as_uint64;
- g_udev_device_get_sysfs_attr_keys;
- g_udev_device_get_sysfs_path;
- g_udev_device_get_tags;
- g_udev_device_get_type;
- g_udev_device_get_usec_since_initialized;
- g_udev_device_has_property;
- g_udev_device_has_sysfs_attr;
- g_udev_device_type_get_type;
- g_udev_enumerator_add_match_is_initialized;
- g_udev_enumerator_add_match_name;
- g_udev_enumerator_add_match_property;
- g_udev_enumerator_add_match_subsystem;
- g_udev_enumerator_add_match_sysfs_attr;
- g_udev_enumerator_add_match_tag;
- g_udev_enumerator_add_nomatch_subsystem;
- g_udev_enumerator_add_nomatch_sysfs_attr;
- g_udev_enumerator_add_sysfs_path;
- g_udev_enumerator_execute;
- g_udev_enumerator_get_type;
- g_udev_enumerator_new;
-local:
- *;
-};
diff --git a/src/gudev/seed-example-enum.js b/src/gudev/seed-example-enum.js
deleted file mode 100755
index 66206ad806..0000000000
--- a/src/gudev/seed-example-enum.js
+++ /dev/null
@@ -1,38 +0,0 @@
-#!/usr/bin/env seed
-
-const GLib = imports.gi.GLib;
-const GUdev = imports.gi.GUdev;
-
-function print_device(device) {
- print(" initialized: " + device.get_is_initialized());
- print(" usec since initialized: " + device.get_usec_since_initialized());
- print(" subsystem: " + device.get_subsystem());
- print(" devtype: " + device.get_devtype());
- print(" name: " + device.get_name());
- print(" number: " + device.get_number());
- print(" sysfs_path: " + device.get_sysfs_path());
- print(" driver: " + device.get_driver());
- print(" action: " + device.get_action());
- print(" seqnum: " + device.get_seqnum());
- print(" device type: " + device.get_device_type());
- print(" device number: " + device.get_device_number());
- print(" device file: " + device.get_device_file());
- print(" device file symlinks: " + device.get_device_file_symlinks());
- print(" tags: " + device.get_tags());
- var keys = device.get_property_keys();
- for (var n = 0; n < keys.length; n++) {
- print(" " + keys[n] + "=" + device.get_property(keys[n]));
- }
-}
-
-var client = new GUdev.Client({subsystems: []});
-var enumerator = new GUdev.Enumerator({client: client});
-enumerator.add_match_subsystem('b*')
-
-var devices = enumerator.execute();
-
-for (var n=0; n < devices.length; n++) {
- var device = devices[n];
- print_device(device);
- print("");
-}
diff --git a/src/gudev/seed-example.js b/src/gudev/seed-example.js
deleted file mode 100755
index e2ac324d23..0000000000
--- a/src/gudev/seed-example.js
+++ /dev/null
@@ -1,72 +0,0 @@
-#!/usr/bin/env seed
-
-// seed example
-
-const GLib = imports.gi.GLib;
-const GUdev = imports.gi.GUdev;
-
-function print_device (device) {
- print (" subsystem: " + device.get_subsystem ());
- print (" devtype: " + device.get_devtype ());
- print (" name: " + device.get_name ());
- print (" number: " + device.get_number ());
- print (" sysfs_path: " + device.get_sysfs_path ());
- print (" driver: " + device.get_driver ());
- print (" action: " + device.get_action ());
- print (" seqnum: " + device.get_seqnum ());
- print (" device type: " + device.get_device_type ());
- print (" device number: " + device.get_device_number ());
- print (" device file: " + device.get_device_file ());
- print (" device file symlinks: " + device.get_device_file_symlinks ());
- print (" foo: " + device.get_sysfs_attr_as_strv ("stat"));
- var keys = device.get_property_keys ();
- for (var n = 0; n < keys.length; n++) {
- print (" " + keys[n] + "=" + device.get_property (keys[n]));
- }
-}
-
-function on_uevent (client, action, device) {
- print ("action " + action + " on device " + device.get_sysfs_path());
- print_device (device);
- print ("");
-}
-
-var client = new GUdev.Client ({subsystems: ["block", "usb/usb_interface"]});
-client.signal.connect ("uevent", on_uevent);
-
-var block_devices = client.query_by_subsystem ("block");
-for (var n = 0; n < block_devices.length; n++) {
- print ("block device: " + block_devices[n].get_device_file ());
-}
-
-var d;
-
-d = client.query_by_device_number (GUdev.DeviceType.BLOCK, 0x0810);
-if (d == null) {
- print ("query_by_device_number 0x810 -> null");
-} else {
- print ("query_by_device_number 0x810 -> " + d.get_device_file ());
- dd = d.get_parent_with_subsystem ("usb", null);
- print_device (dd);
- print ("--------------------------------------------------------------------------");
- while (d != null) {
- print_device (d);
- print ("");
- d = d.get_parent ();
- }
-}
-
-d = client.query_by_sysfs_path ("/sys/block/sda/sda1");
-print ("query_by_sysfs_path (\"/sys/block/sda1\") -> " + d.get_device_file ());
-
-d = client.query_by_subsystem_and_name ("block", "sda2");
-print ("query_by_subsystem_and_name (\"block\", \"sda2\") -> " + d.get_device_file ());
-
-d = client.query_by_device_file ("/dev/sda");
-print ("query_by_device_file (\"/dev/sda\") -> " + d.get_device_file ());
-
-d = client.query_by_device_file ("/dev/block/8:0");
-print ("query_by_device_file (\"/dev/block/8:0\") -> " + d.get_device_file ());
-
-var mainloop = GLib.main_loop_new ();
-GLib.main_loop_run (mainloop);
diff --git a/src/hibernate-resume/hibernate-resume-generator.c b/src/hibernate-resume/hibernate-resume-generator.c
index 0207346b28..9fb6233336 100644
--- a/src/hibernate-resume/hibernate-resume-generator.c
+++ b/src/hibernate-resume/hibernate-resume-generator.c
@@ -32,6 +32,7 @@ static const char *arg_dest = "/tmp";
static char *arg_resume_dev = NULL;
static int parse_proc_cmdline_item(const char *key, const char *value) {
+
if (streq(key, "resume") && value) {
free(arg_resume_dev);
arg_resume_dev = fstab_node_to_udev_node(value);
@@ -44,13 +45,14 @@ static int parse_proc_cmdline_item(const char *key, const char *value) {
static int process_resume(void) {
_cleanup_free_ char *name = NULL, *lnk = NULL;
+ int r;
if (!arg_resume_dev)
return 0;
- name = unit_name_from_path_instance("systemd-hibernate-resume", arg_resume_dev, ".service");
- if (!name)
- return log_oom();
+ r = unit_name_from_path_instance("systemd-hibernate-resume", arg_resume_dev, ".service", &name);
+ if (r < 0)
+ return log_error_errno(r, "Failed to generate unit name: %m");
lnk = strjoin(arg_dest, "/" SPECIAL_SYSINIT_TARGET ".wants/", name, NULL);
if (!lnk)
diff --git a/src/hibernate-resume/hibernate-resume.c b/src/hibernate-resume/hibernate-resume.c
index f28eabbe37..43aac616b6 100644
--- a/src/hibernate-resume/hibernate-resume.c
+++ b/src/hibernate-resume/hibernate-resume.c
@@ -21,9 +21,7 @@
#include <stdio.h>
#include <errno.h>
-#include <sys/types.h>
#include <sys/stat.h>
-#include <unistd.h>
#include "log.h"
#include "util.h"
diff --git a/src/hostname/hostnamectl.c b/src/hostname/hostnamectl.c
index de4ce7ede6..69ecd61f60 100644
--- a/src/hostname/hostnamectl.c
+++ b/src/hostname/hostnamectl.c
@@ -21,26 +21,19 @@
#include <stdlib.h>
#include <stdbool.h>
-#include <unistd.h>
#include <getopt.h>
#include <locale.h>
#include <string.h>
-#include <sys/timex.h>
-#include <sys/utsname.h>
#include "sd-bus.h"
-
+#include "sd-id128.h"
+#include "hostname-util.h"
#include "bus-util.h"
#include "bus-error.h"
#include "util.h"
#include "spawn-polkit-agent.h"
#include "build.h"
-#include "clock-util.h"
-#include "strv.h"
-#include "sd-id128.h"
-#include "virt.h"
#include "architecture.h"
-#include "fileio.h"
static bool arg_ask_password = true;
static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
diff --git a/src/hostname/hostnamed.c b/src/hostname/hostnamed.c
index 7cd4a1d001..7ff3a4e224 100644
--- a/src/hostname/hostnamed.c
+++ b/src/hostname/hostnamed.c
@@ -30,10 +30,10 @@
#include "virt.h"
#include "env-util.h"
#include "fileio-label.h"
-#include "label.h"
#include "bus-util.h"
#include "event-util.h"
#include "selinux-util.h"
+#include "hostname-util.h"
#define VALID_DEPLOYMENT_CHARS (DIGITS LETTERS "-.:")
@@ -96,7 +96,7 @@ static int context_read_data(Context *c) {
if (!c->data[PROP_HOSTNAME])
return -ENOMEM;
- r = read_one_line_file("/etc/hostname", &c->data[PROP_STATIC_HOSTNAME]);
+ r = read_hostname_config("/etc/hostname", &c->data[PROP_STATIC_HOSTNAME]);
if (r < 0 && r != -ENOENT)
return r;
@@ -404,13 +404,16 @@ static int property_get_chassis(
return sd_bus_message_append(reply, "s", name);
}
-static int method_set_hostname(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
+static int method_set_hostname(sd_bus_message *m, void *userdata, sd_bus_error *error) {
Context *c = userdata;
const char *name;
int interactive;
char *h;
int r;
+ assert(m);
+ assert(c);
+
r = sd_bus_message_read(m, "sb", &name, &interactive);
if (r < 0)
return r;
@@ -427,7 +430,14 @@ static int method_set_hostname(sd_bus *bus, sd_bus_message *m, void *userdata, s
if (streq_ptr(name, c->data[PROP_HOSTNAME]))
return sd_bus_reply_method_return(m, NULL);
- r = bus_verify_polkit_async(m, CAP_SYS_ADMIN, "org.freedesktop.hostname1.set-hostname", interactive, &c->polkit_registry, error);
+ r = bus_verify_polkit_async(
+ m,
+ CAP_SYS_ADMIN,
+ "org.freedesktop.hostname1.set-hostname",
+ interactive,
+ UID_INVALID,
+ &c->polkit_registry,
+ error);
if (r < 0)
return r;
if (r == 0)
@@ -448,17 +458,20 @@ static int method_set_hostname(sd_bus *bus, sd_bus_message *m, void *userdata, s
log_info("Changed host name to '%s'", strna(c->data[PROP_HOSTNAME]));
- sd_bus_emit_properties_changed(bus, "/org/freedesktop/hostname1", "org.freedesktop.hostname1", "Hostname", NULL);
+ (void) sd_bus_emit_properties_changed(sd_bus_message_get_bus(m), "/org/freedesktop/hostname1", "org.freedesktop.hostname1", "Hostname", NULL);
return sd_bus_reply_method_return(m, NULL);
}
-static int method_set_static_hostname(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
+static int method_set_static_hostname(sd_bus_message *m, void *userdata, sd_bus_error *error) {
Context *c = userdata;
const char *name;
int interactive;
int r;
+ assert(m);
+ assert(c);
+
r = sd_bus_message_read(m, "sb", &name, &interactive);
if (r < 0)
return r;
@@ -469,7 +482,14 @@ static int method_set_static_hostname(sd_bus *bus, sd_bus_message *m, void *user
if (streq_ptr(name, c->data[PROP_STATIC_HOSTNAME]))
return sd_bus_reply_method_return(m, NULL);
- r = bus_verify_polkit_async(m, CAP_SYS_ADMIN, "org.freedesktop.hostname1.set-static-hostname", interactive, &c->polkit_registry, error);
+ r = bus_verify_polkit_async(
+ m,
+ CAP_SYS_ADMIN,
+ "org.freedesktop.hostname1.set-static-hostname",
+ interactive,
+ UID_INVALID,
+ &c->polkit_registry,
+ error);
if (r < 0)
return r;
if (r == 0)
@@ -506,18 +526,17 @@ static int method_set_static_hostname(sd_bus *bus, sd_bus_message *m, void *user
log_info("Changed static host name to '%s'", strna(c->data[PROP_STATIC_HOSTNAME]));
- sd_bus_emit_properties_changed(bus, "/org/freedesktop/hostname1", "org.freedesktop.hostname1", "StaticHostname", NULL);
+ (void) sd_bus_emit_properties_changed(sd_bus_message_get_bus(m), "/org/freedesktop/hostname1", "org.freedesktop.hostname1", "StaticHostname", NULL);
return sd_bus_reply_method_return(m, NULL);
}
-static int set_machine_info(Context *c, sd_bus *bus, sd_bus_message *m, int prop, sd_bus_message_handler_t cb, sd_bus_error *error) {
+static int set_machine_info(Context *c, sd_bus_message *m, int prop, sd_bus_message_handler_t cb, sd_bus_error *error) {
int interactive;
const char *name;
int r;
assert(c);
- assert(bus);
assert(m);
r = sd_bus_message_read(m, "sb", &name, &interactive);
@@ -534,10 +553,14 @@ static int set_machine_info(Context *c, sd_bus *bus, sd_bus_message *m, int prop
* same time as the static one, use the same policy action for
* both... */
- r = bus_verify_polkit_async(m, CAP_SYS_ADMIN,
- prop == PROP_PRETTY_HOSTNAME ?
- "org.freedesktop.hostname1.set-static-hostname" :
- "org.freedesktop.hostname1.set-machine-info", interactive, &c->polkit_registry, error);
+ r = bus_verify_polkit_async(
+ m,
+ CAP_SYS_ADMIN,
+ prop == PROP_PRETTY_HOSTNAME ? "org.freedesktop.hostname1.set-static-hostname" : "org.freedesktop.hostname1.set-machine-info",
+ interactive,
+ UID_INVALID,
+ &c->polkit_registry,
+ error);
if (r < 0)
return r;
if (r == 0)
@@ -583,33 +606,36 @@ static int set_machine_info(Context *c, sd_bus *bus, sd_bus_message *m, int prop
prop == PROP_LOCATION ? "location" :
prop == PROP_CHASSIS ? "chassis" : "icon name", strna(c->data[prop]));
- sd_bus_emit_properties_changed(bus, "/org/freedesktop/hostname1", "org.freedesktop.hostname1",
- prop == PROP_PRETTY_HOSTNAME ? "PrettyHostname" :
- prop == PROP_DEPLOYMENT ? "Deployment" :
- prop == PROP_LOCATION ? "Location" :
- prop == PROP_CHASSIS ? "Chassis" : "IconName" , NULL);
+ (void) sd_bus_emit_properties_changed(
+ sd_bus_message_get_bus(m),
+ "/org/freedesktop/hostname1",
+ "org.freedesktop.hostname1",
+ prop == PROP_PRETTY_HOSTNAME ? "PrettyHostname" :
+ prop == PROP_DEPLOYMENT ? "Deployment" :
+ prop == PROP_LOCATION ? "Location" :
+ prop == PROP_CHASSIS ? "Chassis" : "IconName" , NULL);
return sd_bus_reply_method_return(m, NULL);
}
-static int method_set_pretty_hostname(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
- return set_machine_info(userdata, bus, m, PROP_PRETTY_HOSTNAME, method_set_pretty_hostname, error);
+static int method_set_pretty_hostname(sd_bus_message *m, void *userdata, sd_bus_error *error) {
+ return set_machine_info(userdata, m, PROP_PRETTY_HOSTNAME, method_set_pretty_hostname, error);
}
-static int method_set_icon_name(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
- return set_machine_info(userdata, bus, m, PROP_ICON_NAME, method_set_icon_name, error);
+static int method_set_icon_name(sd_bus_message *m, void *userdata, sd_bus_error *error) {
+ return set_machine_info(userdata, m, PROP_ICON_NAME, method_set_icon_name, error);
}
-static int method_set_chassis(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
- return set_machine_info(userdata, bus, m, PROP_CHASSIS, method_set_chassis, error);
+static int method_set_chassis(sd_bus_message *m, void *userdata, sd_bus_error *error) {
+ return set_machine_info(userdata, m, PROP_CHASSIS, method_set_chassis, error);
}
-static int method_set_deployment(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
- return set_machine_info(userdata, bus, m, PROP_DEPLOYMENT, method_set_deployment, error);
+static int method_set_deployment(sd_bus_message *m, void *userdata, sd_bus_error *error) {
+ return set_machine_info(userdata, m, PROP_DEPLOYMENT, method_set_deployment, error);
}
-static int method_set_location(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
- return set_machine_info(userdata, bus, m, PROP_LOCATION, method_set_location, error);
+static int method_set_location(sd_bus_message *m, void *userdata, sd_bus_error *error) {
+ return set_machine_info(userdata, m, PROP_LOCATION, method_set_location, error);
}
static const sd_bus_vtable hostname_vtable[] = {
diff --git a/src/hwdb/hwdb.c b/src/hwdb/hwdb.c
index 4539673ead..446de3a2fc 100644
--- a/src/hwdb/hwdb.c
+++ b/src/hwdb/hwdb.c
@@ -18,7 +18,6 @@
***/
#include <stdlib.h>
-#include <unistd.h>
#include <getopt.h>
#include <string.h>
#include <ctype.h>
@@ -28,7 +27,6 @@
#include "conf-files.h"
#include "strv.h"
#include "mkdir.h"
-#include "fileio.h"
#include "verbs.h"
#include "build.h"
diff --git a/src/import/aufs-util.c b/src/import/aufs-util.c
index c1301cdb4a..18c42b8b6d 100644
--- a/src/import/aufs-util.c
+++ b/src/import/aufs-util.c
@@ -22,6 +22,7 @@
#include <ftw.h>
#include "util.h"
+#include "rm-rf.h"
#include "aufs-util.h"
static int nftw_cb(
@@ -43,7 +44,7 @@ static int nftw_cb(
return FTW_CONTINUE;
log_debug("Removing whiteout indicator %s.", fpath);
- r = rm_rf_dangerous(fpath, false, true, false);
+ r = rm_rf(fpath, REMOVE_ROOT|REMOVE_PHYSICAL);
if (r < 0)
return FTW_STOP;
@@ -53,7 +54,7 @@ static int nftw_cb(
strcpy(mempcpy(p, fpath, ftwbuf->base), original);
log_debug("Removing deleted file %s.", p);
- r = rm_rf_dangerous(p, false, true, false);
+ r = rm_rf(p, REMOVE_ROOT|REMOVE_PHYSICAL);
if (r < 0)
return FTW_STOP;
}
diff --git a/src/import/export-raw.c b/src/import/export-raw.c
new file mode 100644
index 0000000000..4b6d8dac32
--- /dev/null
+++ b/src/import/export-raw.c
@@ -0,0 +1,345 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2015 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/sendfile.h>
+#include <libgen.h>
+#undef basename
+
+#include "sd-daemon.h"
+#include "util.h"
+#include "ratelimit.h"
+#include "btrfs-util.h"
+#include "copy.h"
+#include "import-common.h"
+#include "export-raw.h"
+
+#define COPY_BUFFER_SIZE (16*1024)
+
+struct RawExport {
+ sd_event *event;
+
+ RawExportFinished on_finished;
+ void *userdata;
+
+ char *path;
+
+ int input_fd;
+ int output_fd;
+
+ ImportCompress compress;
+
+ sd_event_source *output_event_source;
+
+ void *buffer;
+ size_t buffer_size;
+ size_t buffer_allocated;
+
+ uint64_t written_compressed;
+ uint64_t written_uncompressed;
+
+ unsigned last_percent;
+ RateLimit progress_rate_limit;
+
+ struct stat st;
+
+ bool eof;
+ bool tried_reflink;
+ bool tried_sendfile;
+};
+
+RawExport *raw_export_unref(RawExport *e) {
+ if (!e)
+ return NULL;
+
+ sd_event_source_unref(e->output_event_source);
+
+ import_compress_free(&e->compress);
+
+ sd_event_unref(e->event);
+
+ safe_close(e->input_fd);
+
+ free(e->buffer);
+ free(e->path);
+ free(e);
+
+ return NULL;
+}
+
+int raw_export_new(
+ RawExport **ret,
+ sd_event *event,
+ RawExportFinished on_finished,
+ void *userdata) {
+
+ _cleanup_(raw_export_unrefp) RawExport *e = NULL;
+ int r;
+
+ assert(ret);
+
+ e = new0(RawExport, 1);
+ if (!e)
+ return -ENOMEM;
+
+ e->output_fd = e->input_fd = -1;
+ e->on_finished = on_finished;
+ e->userdata = userdata;
+
+ RATELIMIT_INIT(e->progress_rate_limit, 100 * USEC_PER_MSEC, 1);
+ e->last_percent = (unsigned) -1;
+
+ if (event)
+ e->event = sd_event_ref(event);
+ else {
+ r = sd_event_default(&e->event);
+ if (r < 0)
+ return r;
+ }
+
+ *ret = e;
+ e = NULL;
+
+ return 0;
+}
+
+static void raw_export_report_progress(RawExport *e) {
+ unsigned percent;
+ assert(e);
+
+ if (e->written_uncompressed >= (uint64_t) e->st.st_size)
+ percent = 100;
+ else
+ percent = (unsigned) ((e->written_uncompressed * UINT64_C(100)) / (uint64_t) e->st.st_size);
+
+ if (percent == e->last_percent)
+ return;
+
+ if (!ratelimit_test(&e->progress_rate_limit))
+ return;
+
+ sd_notifyf(false, "X_IMPORT_PROGRESS=%u", percent);
+ log_info("Exported %u%%.", percent);
+
+ e->last_percent = percent;
+}
+
+static int raw_export_process(RawExport *e) {
+ ssize_t l;
+ int r;
+
+ assert(e);
+
+ if (!e->tried_reflink && e->compress.type == IMPORT_COMPRESS_UNCOMPRESSED) {
+
+ /* If we shall take an uncompressed snapshot we can
+ * reflink source to destination directly. Let's see
+ * if this works. */
+
+ r = btrfs_reflink(e->input_fd, e->output_fd);
+ if (r >= 0) {
+ r = 0;
+ goto finish;
+ }
+
+ e->tried_reflink = true;
+ }
+
+ if (!e->tried_sendfile && e->compress.type == IMPORT_COMPRESS_UNCOMPRESSED) {
+
+ l = sendfile(e->output_fd, e->input_fd, NULL, COPY_BUFFER_SIZE);
+ if (l < 0) {
+ if (errno == EAGAIN)
+ return 0;
+
+ e->tried_sendfile = true;
+ } else if (l == 0) {
+ r = 0;
+ goto finish;
+ } else {
+ e->written_uncompressed += l;
+ e->written_compressed += l;
+
+ raw_export_report_progress(e);
+
+ return 0;
+ }
+ }
+
+ while (e->buffer_size <= 0) {
+ uint8_t input[COPY_BUFFER_SIZE];
+
+ if (e->eof) {
+ r = 0;
+ goto finish;
+ }
+
+ l = read(e->input_fd, input, sizeof(input));
+ if (l < 0) {
+ r = log_error_errno(errno, "Failed to read raw file: %m");
+ goto finish;
+ }
+
+ if (l == 0) {
+ e->eof = true;
+ r = import_compress_finish(&e->compress, &e->buffer, &e->buffer_size, &e->buffer_allocated);
+ } else {
+ e->written_uncompressed += l;
+ r = import_compress(&e->compress, input, l, &e->buffer, &e->buffer_size, &e->buffer_allocated);
+ }
+ if (r < 0) {
+ r = log_error_errno(r, "Failed to encode: %m");
+ goto finish;
+ }
+ }
+
+ l = write(e->output_fd, e->buffer, e->buffer_size);
+ if (l < 0) {
+ if (errno == EAGAIN)
+ return 0;
+
+ r = log_error_errno(errno, "Failed to write output file: %m");
+ goto finish;
+ }
+
+ assert((size_t) l <= e->buffer_size);
+ memmove(e->buffer, (uint8_t*) e->buffer + l, e->buffer_size - l);
+ e->buffer_size -= l;
+ e->written_compressed += l;
+
+ raw_export_report_progress(e);
+
+ return 0;
+
+finish:
+ if (r >= 0) {
+ (void) copy_times(e->input_fd, e->output_fd);
+ (void) copy_xattr(e->input_fd, e->output_fd);
+ }
+
+ if (e->on_finished)
+ e->on_finished(e, r, e->userdata);
+ else
+ sd_event_exit(e->event, r);
+
+ return 0;
+}
+
+static int raw_export_on_output(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
+ RawExport *i = userdata;
+
+ return raw_export_process(i);
+}
+
+static int raw_export_on_defer(sd_event_source *s, void *userdata) {
+ RawExport *i = userdata;
+
+ return raw_export_process(i);
+}
+
+static int reflink_snapshot(int fd, const char *path) {
+ char *p, *d;
+ int new_fd, r;
+
+ p = strdupa(path);
+ d = dirname(p);
+
+ new_fd = open(d, O_TMPFILE|O_CLOEXEC|O_NOCTTY|O_RDWR, 0600);
+ if (new_fd < 0) {
+ _cleanup_free_ char *t = NULL;
+
+ r = tempfn_random(path, &t);
+ if (r < 0)
+ return r;
+
+ new_fd = open(t, O_CLOEXEC|O_CREAT|O_NOCTTY|O_RDWR, 0600);
+ if (new_fd < 0)
+ return -errno;
+
+ (void) unlink(t);
+ }
+
+ r = btrfs_reflink(fd, new_fd);
+ if (r < 0) {
+ safe_close(new_fd);
+ return r;
+ }
+
+ return new_fd;
+}
+
+int raw_export_start(RawExport *e, const char *path, int fd, ImportCompressType compress) {
+ _cleanup_close_ int sfd = -1, tfd = -1;
+ int r;
+
+ assert(e);
+ assert(path);
+ assert(fd >= 0);
+ assert(compress < _IMPORT_COMPRESS_TYPE_MAX);
+ assert(compress != IMPORT_COMPRESS_UNKNOWN);
+
+ if (e->output_fd >= 0)
+ return -EBUSY;
+
+ r = fd_nonblock(fd, true);
+ if (r < 0)
+ return r;
+
+ r = free_and_strdup(&e->path, path);
+ if (r < 0)
+ return r;
+
+ sfd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY);
+ if (sfd < 0)
+ return -errno;
+
+ if (fstat(sfd, &e->st) < 0)
+ return -errno;
+ if (!S_ISREG(e->st.st_mode))
+ return -ENOTTY;
+
+ /* Try to take a reflink snapshot of the file, if we can t make the export atomic */
+ tfd = reflink_snapshot(sfd, path);
+ if (tfd >= 0) {
+ e->input_fd = tfd;
+ tfd = -1;
+ } else {
+ e->input_fd = sfd;
+ sfd = -1;
+ }
+
+ r = import_compress_init(&e->compress, compress);
+ if (r < 0)
+ return r;
+
+ r = sd_event_add_io(e->event, &e->output_event_source, fd, EPOLLOUT, raw_export_on_output, e);
+ if (r == -EPERM) {
+ r = sd_event_add_defer(e->event, &e->output_event_source, raw_export_on_defer, e);
+ if (r < 0)
+ return r;
+
+ r = sd_event_source_set_enabled(e->output_event_source, SD_EVENT_ON);
+ }
+ if (r < 0)
+ return r;
+
+ e->output_fd = fd;
+ return r;
+}
diff --git a/src/import/export-raw.h b/src/import/export-raw.h
new file mode 100644
index 0000000000..b71de6cb82
--- /dev/null
+++ b/src/import/export-raw.h
@@ -0,0 +1,37 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2015 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-event.h"
+#include "macro.h"
+#include "import-compress.h"
+
+typedef struct RawExport RawExport;
+
+typedef void (*RawExportFinished)(RawExport *export, int error, void *userdata);
+
+int raw_export_new(RawExport **export, sd_event *event, RawExportFinished on_finished, void *userdata);
+RawExport* raw_export_unref(RawExport *export);
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(RawExport*, raw_export_unref);
+
+int raw_export_start(RawExport *export, const char *path, int fd, ImportCompressType compress);
diff --git a/src/import/export-tar.c b/src/import/export-tar.c
new file mode 100644
index 0000000000..d31295745f
--- /dev/null
+++ b/src/import/export-tar.c
@@ -0,0 +1,329 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2015 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/sendfile.h>
+
+#include "sd-daemon.h"
+#include "util.h"
+#include "ratelimit.h"
+#include "btrfs-util.h"
+#include "import-common.h"
+#include "export-tar.h"
+#include "process-util.h"
+
+#define COPY_BUFFER_SIZE (16*1024)
+
+struct TarExport {
+ sd_event *event;
+
+ TarExportFinished on_finished;
+ void *userdata;
+
+ char *path;
+ char *temp_path;
+
+ int output_fd;
+ int tar_fd;
+
+ ImportCompress compress;
+
+ sd_event_source *output_event_source;
+
+ void *buffer;
+ size_t buffer_size;
+ size_t buffer_allocated;
+
+ uint64_t written_compressed;
+ uint64_t written_uncompressed;
+
+ pid_t tar_pid;
+
+ struct stat st;
+ uint64_t quota_referenced;
+
+ unsigned last_percent;
+ RateLimit progress_rate_limit;
+
+ bool eof;
+ bool tried_splice;
+};
+
+TarExport *tar_export_unref(TarExport *e) {
+ if (!e)
+ return NULL;
+
+ sd_event_source_unref(e->output_event_source);
+
+ if (e->tar_pid > 1) {
+ (void) kill_and_sigcont(e->tar_pid, SIGKILL);
+ (void) wait_for_terminate(e->tar_pid, NULL);
+ }
+
+ if (e->temp_path) {
+ (void) btrfs_subvol_remove(e->temp_path, false);
+ free(e->temp_path);
+ }
+
+ import_compress_free(&e->compress);
+
+ sd_event_unref(e->event);
+
+ safe_close(e->tar_fd);
+
+ free(e->buffer);
+ free(e->path);
+ free(e);
+
+ return NULL;
+}
+
+int tar_export_new(
+ TarExport **ret,
+ sd_event *event,
+ TarExportFinished on_finished,
+ void *userdata) {
+
+ _cleanup_(tar_export_unrefp) TarExport *e = NULL;
+ int r;
+
+ assert(ret);
+
+ e = new0(TarExport, 1);
+ if (!e)
+ return -ENOMEM;
+
+ e->output_fd = e->tar_fd = -1;
+ e->on_finished = on_finished;
+ e->userdata = userdata;
+ e->quota_referenced = (uint64_t) -1;
+
+ RATELIMIT_INIT(e->progress_rate_limit, 100 * USEC_PER_MSEC, 1);
+ e->last_percent = (unsigned) -1;
+
+ if (event)
+ e->event = sd_event_ref(event);
+ else {
+ r = sd_event_default(&e->event);
+ if (r < 0)
+ return r;
+ }
+
+ *ret = e;
+ e = NULL;
+
+ return 0;
+}
+
+static void tar_export_report_progress(TarExport *e) {
+ unsigned percent;
+ assert(e);
+
+ /* Do we have any quota info? If not, we don't know anything about the progress */
+ if (e->quota_referenced == (uint64_t) -1)
+ return;
+
+ if (e->written_uncompressed >= e->quota_referenced)
+ percent = 100;
+ else
+ percent = (unsigned) ((e->written_uncompressed * UINT64_C(100)) / e->quota_referenced);
+
+ if (percent == e->last_percent)
+ return;
+
+ if (!ratelimit_test(&e->progress_rate_limit))
+ return;
+
+ sd_notifyf(false, "X_IMPORT_PROGRESS=%u", percent);
+ log_info("Exported %u%%.", percent);
+
+ e->last_percent = percent;
+}
+
+static int tar_export_process(TarExport *e) {
+ ssize_t l;
+ int r;
+
+ assert(e);
+
+ if (!e->tried_splice && e->compress.type == IMPORT_COMPRESS_UNCOMPRESSED) {
+
+ l = splice(e->tar_fd, NULL, e->output_fd, NULL, COPY_BUFFER_SIZE, 0);
+ if (l < 0) {
+ if (errno == EAGAIN)
+ return 0;
+
+ e->tried_splice = true;
+ } else if (l == 0) {
+ r = 0;
+ goto finish;
+ } else {
+ e->written_uncompressed += l;
+ e->written_compressed += l;
+
+ tar_export_report_progress(e);
+
+ return 0;
+ }
+ }
+
+ while (e->buffer_size <= 0) {
+ uint8_t input[COPY_BUFFER_SIZE];
+
+ if (e->eof) {
+ r = 0;
+ goto finish;
+ }
+
+ l = read(e->tar_fd, input, sizeof(input));
+ if (l < 0) {
+ r = log_error_errno(errno, "Failed to read tar file: %m");
+ goto finish;
+ }
+
+ if (l == 0) {
+ e->eof = true;
+ r = import_compress_finish(&e->compress, &e->buffer, &e->buffer_size, &e->buffer_allocated);
+ } else {
+ e->written_uncompressed += l;
+ r = import_compress(&e->compress, input, l, &e->buffer, &e->buffer_size, &e->buffer_allocated);
+ }
+ if (r < 0) {
+ r = log_error_errno(r, "Failed to encode: %m");
+ goto finish;
+ }
+ }
+
+ l = write(e->output_fd, e->buffer, e->buffer_size);
+ if (l < 0) {
+ if (errno == EAGAIN)
+ return 0;
+
+ r = log_error_errno(errno, "Failed to write output file: %m");
+ goto finish;
+ }
+
+ assert((size_t) l <= e->buffer_size);
+ memmove(e->buffer, (uint8_t*) e->buffer + l, e->buffer_size - l);
+ e->buffer_size -= l;
+ e->written_compressed += l;
+
+ tar_export_report_progress(e);
+
+ return 0;
+
+finish:
+ if (e->on_finished)
+ e->on_finished(e, r, e->userdata);
+ else
+ sd_event_exit(e->event, r);
+
+ return 0;
+}
+
+static int tar_export_on_output(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
+ TarExport *i = userdata;
+
+ return tar_export_process(i);
+}
+
+static int tar_export_on_defer(sd_event_source *s, void *userdata) {
+ TarExport *i = userdata;
+
+ return tar_export_process(i);
+}
+
+int tar_export_start(TarExport *e, const char *path, int fd, ImportCompressType compress) {
+ _cleanup_close_ int sfd = -1;
+ int r;
+
+ assert(e);
+ assert(path);
+ assert(fd >= 0);
+ assert(compress < _IMPORT_COMPRESS_TYPE_MAX);
+ assert(compress != IMPORT_COMPRESS_UNKNOWN);
+
+ if (e->output_fd >= 0)
+ return -EBUSY;
+
+ sfd = open(path, O_DIRECTORY|O_RDONLY|O_NOCTTY|O_CLOEXEC);
+ if (sfd < 0)
+ return -errno;
+
+ if (fstat(sfd, &e->st) < 0)
+ return -errno;
+
+ r = fd_nonblock(fd, true);
+ if (r < 0)
+ return r;
+
+ r = free_and_strdup(&e->path, path);
+ if (r < 0)
+ return r;
+
+ e->quota_referenced = (uint64_t) -1;
+
+ if (e->st.st_ino == 256) { /* might be a btrfs subvolume? */
+ BtrfsQuotaInfo q;
+
+ r = btrfs_subvol_get_quota_fd(sfd, &q);
+ if (r >= 0)
+ e->quota_referenced = q.referenced;
+
+ free(e->temp_path);
+ e->temp_path = NULL;
+
+ r = tempfn_random(path, &e->temp_path);
+ if (r < 0)
+ return r;
+
+ /* Let's try to make a snapshot, if we can, so that the export is atomic */
+ r = btrfs_subvol_snapshot_fd(sfd, e->temp_path, BTRFS_SNAPSHOT_READ_ONLY|BTRFS_SNAPSHOT_RECURSIVE);
+ if (r < 0) {
+ log_debug_errno(r, "Couldn't create snapshot %s of %s, not exporting atomically: %m", e->temp_path, path);
+ free(e->temp_path);
+ e->temp_path = NULL;
+ }
+ }
+
+ r = import_compress_init(&e->compress, compress);
+ if (r < 0)
+ return r;
+
+ r = sd_event_add_io(e->event, &e->output_event_source, fd, EPOLLOUT, tar_export_on_output, e);
+ if (r == -EPERM) {
+ r = sd_event_add_defer(e->event, &e->output_event_source, tar_export_on_defer, e);
+ if (r < 0)
+ return r;
+
+ r = sd_event_source_set_enabled(e->output_event_source, SD_EVENT_ON);
+ }
+ if (r < 0)
+ return r;
+
+ e->tar_fd = import_fork_tar_c(e->temp_path ?: e->path, &e->tar_pid);
+ if (e->tar_fd < 0) {
+ e->output_event_source = sd_event_source_unref(e->output_event_source);
+ return e->tar_fd;
+ }
+
+ e->output_fd = fd;
+ return r;
+}
diff --git a/src/import/export-tar.h b/src/import/export-tar.h
new file mode 100644
index 0000000000..ce27a9fc1e
--- /dev/null
+++ b/src/import/export-tar.h
@@ -0,0 +1,37 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2015 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-event.h"
+#include "macro.h"
+#include "import-compress.h"
+
+typedef struct TarExport TarExport;
+
+typedef void (*TarExportFinished)(TarExport *export, int error, void *userdata);
+
+int tar_export_new(TarExport **export, sd_event *event, TarExportFinished on_finished, void *userdata);
+TarExport* tar_export_unref(TarExport *export);
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(TarExport*, tar_export_unref);
+
+int tar_export_start(TarExport *export, const char *path, int fd, ImportCompressType compress);
diff --git a/src/import/export.c b/src/import/export.c
new file mode 100644
index 0000000000..d4bc88e010
--- /dev/null
+++ b/src/import/export.c
@@ -0,0 +1,320 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2015 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 "sd-event.h"
+#include "event-util.h"
+#include "signal-util.h"
+#include "verbs.h"
+#include "build.h"
+#include "machine-image.h"
+#include "import-util.h"
+#include "export-tar.h"
+#include "export-raw.h"
+
+static ImportCompressType arg_compress = IMPORT_COMPRESS_UNKNOWN;
+
+static void determine_compression_from_filename(const char *p) {
+
+ if (arg_compress != IMPORT_COMPRESS_UNKNOWN)
+ return;
+
+ if (!p) {
+ arg_compress = IMPORT_COMPRESS_UNCOMPRESSED;
+ return;
+ }
+
+ if (endswith(p, ".xz"))
+ arg_compress = IMPORT_COMPRESS_XZ;
+ else if (endswith(p, ".gz"))
+ arg_compress = IMPORT_COMPRESS_GZIP;
+ else if (endswith(p, ".bz2"))
+ arg_compress = IMPORT_COMPRESS_BZIP2;
+ else
+ arg_compress = IMPORT_COMPRESS_UNCOMPRESSED;
+}
+
+static int interrupt_signal_handler(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
+ log_notice("Transfer aborted.");
+ sd_event_exit(sd_event_source_get_event(s), EINTR);
+ return 0;
+}
+
+static void on_tar_finished(TarExport *export, int error, void *userdata) {
+ sd_event *event = userdata;
+ assert(export);
+
+ if (error == 0)
+ log_info("Operation completed successfully.");
+
+ sd_event_exit(event, abs(error));
+}
+
+static int export_tar(int argc, char *argv[], void *userdata) {
+ _cleanup_(tar_export_unrefp) TarExport *export = NULL;
+ _cleanup_event_unref_ sd_event *event = NULL;
+ _cleanup_(image_unrefp) Image *image = NULL;
+ const char *path = NULL, *local = NULL;
+ _cleanup_close_ int open_fd = -1;
+ int r, fd;
+
+ if (machine_name_is_valid(argv[1])) {
+ r = image_find(argv[1], &image);
+ if (r < 0)
+ return log_error_errno(r, "Failed to look for machine %s: %m", argv[1]);
+ if (r == 0) {
+ log_error("Machine image %s not found.", argv[1]);
+ return -ENOENT;
+ }
+
+ local = image->path;
+ } else
+ local = argv[1];
+
+ if (argc >= 3)
+ path = argv[2];
+ if (isempty(path) || streq(path, "-"))
+ path = NULL;
+
+ determine_compression_from_filename(path);
+
+ if (path) {
+ open_fd = open(path, O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC|O_NOCTTY, 0666);
+ if (open_fd < 0)
+ return log_error_errno(errno, "Failed to open tar image for export: %m");
+
+ fd = open_fd;
+
+ log_info("Exporting '%s', saving to '%s' with compression '%s'.", local, path, import_compress_type_to_string(arg_compress));
+ } else {
+ _cleanup_free_ char *pretty = NULL;
+
+ fd = STDOUT_FILENO;
+
+ (void) readlink_malloc("/proc/self/fd/1", &pretty);
+ log_info("Exporting '%s', saving to '%s' with compression '%s'.", local, strna(pretty), import_compress_type_to_string(arg_compress));
+ }
+
+ r = sd_event_default(&event);
+ if (r < 0)
+ return log_error_errno(r, "Failed to allocate event loop: %m");
+
+ assert_se(sigprocmask_many(SIG_BLOCK, SIGTERM, SIGINT, -1) == 0);
+ sd_event_add_signal(event, NULL, SIGTERM, interrupt_signal_handler, NULL);
+ sd_event_add_signal(event, NULL, SIGINT, interrupt_signal_handler, NULL);
+
+ r = tar_export_new(&export, event, on_tar_finished, event);
+ if (r < 0)
+ return log_error_errno(r, "Failed to allocate exporter: %m");
+
+ r = tar_export_start(export, local, fd, arg_compress);
+ if (r < 0)
+ return log_error_errno(r, "Failed to export image: %m");
+
+ r = sd_event_loop(event);
+ if (r < 0)
+ return log_error_errno(r, "Failed to run event loop: %m");
+
+ log_info("Exiting.");
+ return -r;
+}
+
+static void on_raw_finished(RawExport *export, int error, void *userdata) {
+ sd_event *event = userdata;
+ assert(export);
+
+ if (error == 0)
+ log_info("Operation completed successfully.");
+
+ sd_event_exit(event, abs(error));
+}
+
+static int export_raw(int argc, char *argv[], void *userdata) {
+ _cleanup_(raw_export_unrefp) RawExport *export = NULL;
+ _cleanup_event_unref_ sd_event *event = NULL;
+ _cleanup_(image_unrefp) Image *image = NULL;
+ const char *path = NULL, *local = NULL;
+ _cleanup_close_ int open_fd = -1;
+ int r, fd;
+
+ if (machine_name_is_valid(argv[1])) {
+ r = image_find(argv[1], &image);
+ if (r < 0)
+ return log_error_errno(r, "Failed to look for machine %s: %m", argv[1]);
+ if (r == 0) {
+ log_error("Machine image %s not found.", argv[1]);
+ return -ENOENT;
+ }
+
+ local = image->path;
+ } else
+ local = argv[1];
+
+ if (argc >= 3)
+ path = argv[2];
+ if (isempty(path) || streq(path, "-"))
+ path = NULL;
+
+ determine_compression_from_filename(path);
+
+ if (path) {
+ open_fd = open(path, O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC|O_NOCTTY, 0666);
+ if (open_fd < 0)
+ return log_error_errno(errno, "Failed to open raw image for export: %m");
+
+ fd = open_fd;
+
+ log_info("Exporting '%s', saving to '%s' with compression '%s'.", local, path, import_compress_type_to_string(arg_compress));
+ } else {
+ _cleanup_free_ char *pretty = NULL;
+
+ fd = STDOUT_FILENO;
+
+ (void) readlink_malloc("/proc/self/fd/1", &pretty);
+ log_info("Exporting '%s', saving to '%s' with compression '%s'.", local, strna(pretty), import_compress_type_to_string(arg_compress));
+ }
+
+ r = sd_event_default(&event);
+ if (r < 0)
+ return log_error_errno(r, "Failed to allocate event loop: %m");
+
+ assert_se(sigprocmask_many(SIG_BLOCK, SIGTERM, SIGINT, -1) == 0);
+ sd_event_add_signal(event, NULL, SIGTERM, interrupt_signal_handler, NULL);
+ sd_event_add_signal(event, NULL, SIGINT, interrupt_signal_handler, NULL);
+
+ r = raw_export_new(&export, event, on_raw_finished, event);
+ if (r < 0)
+ return log_error_errno(r, "Failed to allocate exporter: %m");
+
+ r = raw_export_start(export, local, fd, arg_compress);
+ if (r < 0)
+ return log_error_errno(r, "Failed to export image: %m");
+
+ r = sd_event_loop(event);
+ if (r < 0)
+ return log_error_errno(r, "Failed to run event loop: %m");
+
+ log_info("Exiting.");
+ return -r;
+}
+
+static int help(int argc, char *argv[], void *userdata) {
+
+ printf("%s [OPTIONS...] {COMMAND} ...\n\n"
+ "Export container or virtual machine images.\n\n"
+ " -h --help Show this help\n"
+ " --version Show package version\n"
+ " --format=FORMAT Select format\n\n"
+ "Commands:\n"
+ " tar NAME [FILE] Export a TAR image\n"
+ " raw NAME [FILE] Export a RAW image\n",
+ program_invocation_short_name);
+
+ return 0;
+}
+
+static int parse_argv(int argc, char *argv[]) {
+
+ enum {
+ ARG_VERSION = 0x100,
+ ARG_FORMAT,
+ };
+
+ static const struct option options[] = {
+ { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, ARG_VERSION },
+ { "format", required_argument, NULL, ARG_FORMAT },
+ {}
+ };
+
+ int c;
+
+ assert(argc >= 0);
+ assert(argv);
+
+ while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
+
+ switch (c) {
+
+ case 'h':
+ return help(0, NULL, NULL);
+
+ case ARG_VERSION:
+ puts(PACKAGE_STRING);
+ puts(SYSTEMD_FEATURES);
+ return 0;
+
+ case ARG_FORMAT:
+ if (streq(optarg, "uncompressed"))
+ arg_compress = IMPORT_COMPRESS_UNCOMPRESSED;
+ else if (streq(optarg, "xz"))
+ arg_compress = IMPORT_COMPRESS_XZ;
+ else if (streq(optarg, "gzip"))
+ arg_compress = IMPORT_COMPRESS_GZIP;
+ else if (streq(optarg, "bzip2"))
+ arg_compress = IMPORT_COMPRESS_BZIP2;
+ else {
+ log_error("Unknown format: %s", optarg);
+ return -EINVAL;
+ }
+ break;
+
+ case '?':
+ return -EINVAL;
+
+ default:
+ assert_not_reached("Unhandled option");
+ }
+
+ return 1;
+}
+
+static int export_main(int argc, char *argv[]) {
+
+ static const Verb verbs[] = {
+ { "help", VERB_ANY, VERB_ANY, 0, help },
+ { "tar", 2, 3, 0, export_tar },
+ { "raw", 2, 3, 0, export_raw },
+ {}
+ };
+
+ return dispatch_verb(argc, argv, verbs, NULL);
+}
+
+int main(int argc, char *argv[]) {
+ int r;
+
+ setlocale(LC_ALL, "");
+ log_parse_environment();
+ log_open();
+
+ r = parse_argv(argc, argv);
+ if (r <= 0)
+ goto finish;
+
+ ignore_signals(SIGPIPE, -1);
+
+ r = export_main(argc, argv);
+
+finish:
+ return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+}
diff --git a/src/import/import-common.c b/src/import/import-common.c
index 2acf380f99..9711614000 100644
--- a/src/import/import-common.c
+++ b/src/import/import-common.c
@@ -20,127 +20,15 @@
***/
#include <sys/prctl.h>
+#include <sys/stat.h>
+#include <unistd.h>
#include "util.h"
-#include "strv.h"
-#include "copy.h"
#include "btrfs-util.h"
#include "capability.h"
-#include "import-job.h"
+#include "signal-util.h"
#include "import-common.h"
-#define FILENAME_ESCAPE "/.#\"\'"
-
-int import_find_old_etags(const char *url, const char *image_root, int dt, const char *prefix, const char *suffix, char ***etags) {
- _cleanup_free_ char *escaped_url = NULL;
- _cleanup_closedir_ DIR *d = NULL;
- _cleanup_strv_free_ char **l = NULL;
- struct dirent *de;
- int r;
-
- assert(url);
- assert(etags);
-
- if (!image_root)
- image_root = "/var/lib/machines";
-
- escaped_url = xescape(url, FILENAME_ESCAPE);
- if (!escaped_url)
- return -ENOMEM;
-
- d = opendir(image_root);
- if (!d) {
- if (errno == ENOENT) {
- *etags = NULL;
- return 0;
- }
-
- return -errno;
- }
-
- FOREACH_DIRENT_ALL(de, d, return -errno) {
- const char *a, *b;
- char *u;
-
- if (de->d_type != DT_UNKNOWN &&
- de->d_type != dt)
- continue;
-
- if (prefix) {
- a = startswith(de->d_name, prefix);
- if (!a)
- continue;
- } else
- a = de->d_name;
-
- a = startswith(a, escaped_url);
- if (!a)
- continue;
-
- a = startswith(a, ".");
- if (!a)
- continue;
-
- if (suffix) {
- b = endswith(de->d_name, suffix);
- if (!b)
- continue;
- } else
- b = strchr(de->d_name, 0);
-
- if (a >= b)
- continue;
-
- u = cunescape_length(a, b - a);
- if (!u)
- return -ENOMEM;
-
- if (!http_etag_is_valid(u)) {
- free(u);
- continue;
- }
-
- r = strv_consume(&l, u);
- if (r < 0)
- return r;
- }
-
- *etags = l;
- l = NULL;
-
- return 0;
-}
-
-int import_make_local_copy(const char *final, const char *image_root, const char *local, bool force_local) {
- const char *p;
- int r;
-
- assert(final);
- assert(local);
-
- if (!image_root)
- image_root = "/var/lib/machines";
-
- p = strjoina(image_root, "/", local);
-
- if (force_local) {
- (void) btrfs_subvol_remove(p);
- (void) rm_rf_dangerous(p, false, true, false);
- }
-
- r = btrfs_subvol_snapshot(final, p, false, false);
- if (r == -ENOTTY) {
- r = copy_tree(final, p, false);
- if (r < 0)
- return log_error_errno(r, "Failed to copy image: %m");
- } else if (r < 0)
- return log_error_errno(r, "Failed to create local image: %m");
-
- log_info("Created new local image '%s'.", local);
-
- return 0;
-}
-
int import_make_read_only_fd(int fd) {
int r;
@@ -182,191 +70,30 @@ int import_make_read_only(const char *path) {
return import_make_read_only_fd(fd);
}
-int import_make_path(const char *url, const char *etag, const char *image_root, const char *prefix, const char *suffix, char **ret) {
- _cleanup_free_ char *escaped_url = NULL;
- char *path;
-
- assert(url);
- assert(ret);
-
- if (!image_root)
- image_root = "/var/lib/machines";
-
- escaped_url = xescape(url, FILENAME_ESCAPE);
- if (!escaped_url)
- return -ENOMEM;
-
- if (etag) {
- _cleanup_free_ char *escaped_etag = NULL;
-
- escaped_etag = xescape(etag, FILENAME_ESCAPE);
- if (!escaped_etag)
- return -ENOMEM;
-
- path = strjoin(image_root, "/", strempty(prefix), escaped_url, ".", escaped_etag, strempty(suffix), NULL);
- } else
- path = strjoin(image_root, "/", strempty(prefix), escaped_url, strempty(suffix), NULL);
- if (!path)
- return -ENOMEM;
-
- *ret = path;
- return 0;
-}
-
-int import_make_verification_jobs(
- ImportJob **ret_checksum_job,
- ImportJob **ret_signature_job,
- ImportVerify verify,
- const char *url,
- CurlGlue *glue,
- ImportJobFinished on_finished,
- void *userdata) {
-
- _cleanup_(import_job_unrefp) ImportJob *checksum_job = NULL, *signature_job = NULL;
- int r;
-
- assert(ret_checksum_job);
- assert(ret_signature_job);
- assert(verify >= 0);
- assert(verify < _IMPORT_VERIFY_MAX);
- assert(url);
- assert(glue);
-
- if (verify != IMPORT_VERIFY_NO) {
- _cleanup_free_ char *checksum_url = NULL;
-
- /* Queue job for the SHA256SUMS file for the image */
- r = import_url_change_last_component(url, "SHA256SUMS", &checksum_url);
- if (r < 0)
- return r;
-
- r = import_job_new(&checksum_job, checksum_url, glue, userdata);
- if (r < 0)
- return r;
-
- checksum_job->on_finished = on_finished;
- checksum_job->uncompressed_max = checksum_job->compressed_max = 1ULL * 1024ULL * 1024ULL;
- }
-
- if (verify == IMPORT_VERIFY_SIGNATURE) {
- _cleanup_free_ char *signature_url = NULL;
-
- /* Queue job for the SHA256SUMS.gpg file for the image. */
- r = import_url_change_last_component(url, "SHA256SUMS.gpg", &signature_url);
- if (r < 0)
- return r;
-
- r = import_job_new(&signature_job, signature_url, glue, userdata);
- if (r < 0)
- return r;
-
- signature_job->on_finished = on_finished;
- signature_job->uncompressed_max = signature_job->compressed_max = 1ULL * 1024ULL * 1024ULL;
- }
-
- *ret_checksum_job = checksum_job;
- *ret_signature_job = signature_job;
-
- checksum_job = signature_job = NULL;
-
- return 0;
-}
-
-int import_verify(
- ImportJob *main_job,
- ImportJob *checksum_job,
- ImportJob *signature_job) {
-
- _cleanup_close_pair_ int gpg_pipe[2] = { -1, -1 };
- _cleanup_free_ char *fn = NULL;
- _cleanup_close_ int sig_file = -1;
- const char *p, *line;
- char sig_file_path[] = "/tmp/sigXXXXXX";
- _cleanup_sigkill_wait_ pid_t pid = 0;
+int import_fork_tar_x(const char *path, pid_t *ret) {
+ _cleanup_close_pair_ int pipefd[2] = { -1, -1 };
+ pid_t pid;
int r;
- assert(main_job);
- assert(main_job->state == IMPORT_JOB_DONE);
-
- if (!checksum_job)
- return 0;
-
- assert(main_job->calc_checksum);
- assert(main_job->checksum);
- assert(checksum_job->state == IMPORT_JOB_DONE);
-
- if (!checksum_job->payload || checksum_job->payload_size <= 0) {
- log_error("Checksum is empty, cannot verify.");
- return -EBADMSG;
- }
-
- r = import_url_last_component(main_job->url, &fn);
- if (r < 0)
- return log_oom();
-
- if (!filename_is_valid(fn)) {
- log_error("Cannot verify checksum, could not determine valid server-side file name.");
- return -EBADMSG;
- }
-
- line = strjoina(main_job->checksum, " *", fn, "\n");
-
- p = memmem(checksum_job->payload,
- checksum_job->payload_size,
- line,
- strlen(line));
-
- if (!p || (p != (char*) checksum_job->payload && p[-1] != '\n')) {
- log_error("Checksum did not check out, payload has been tempered with.");
- return -EBADMSG;
- }
-
- log_info("SHA256 checksum of %s is valid.", main_job->url);
-
- if (!signature_job)
- return 0;
-
- assert(signature_job->state == IMPORT_JOB_DONE);
-
- if (!signature_job->payload || signature_job->payload_size <= 0) {
- log_error("Signature is empty, cannot verify.");
- return -EBADMSG;
- }
-
- r = pipe2(gpg_pipe, O_CLOEXEC);
- if (r < 0)
- return log_error_errno(errno, "Failed to create pipe for gpg: %m");
-
- sig_file = mkostemp(sig_file_path, O_RDWR);
- if (sig_file < 0)
- return log_error_errno(errno, "Failed to create temporary file: %m");
+ assert(path);
+ assert(ret);
- r = loop_write(sig_file, signature_job->payload, signature_job->payload_size, false);
- if (r < 0) {
- log_error_errno(r, "Failed to write to temporary file: %m");
- goto finish;
- }
+ if (pipe2(pipefd, O_CLOEXEC) < 0)
+ return log_error_errno(errno, "Failed to create pipe for tar: %m");
pid = fork();
if (pid < 0)
- return log_error_errno(errno, "Failed to fork off gpg: %m");
+ return log_error_errno(errno, "Failed to fork off tar: %m");
+
if (pid == 0) {
- const char *cmd[] = {
- "gpg",
- "--no-options",
- "--no-default-keyring",
- "--no-auto-key-locate",
- "--no-auto-check-trustdb",
- "--batch",
- "--trust-model=always",
- NULL, /* keyring to use */
- NULL, /* --verify */
- NULL, /* signature file */
- NULL, /* dash */
- NULL /* trailing NULL */
- };
- unsigned k = ELEMENTSOF(cmd) - 5;
int null_fd;
+ uint64_t retain =
+ (1ULL << CAP_CHOWN) |
+ (1ULL << CAP_FOWNER) |
+ (1ULL << CAP_FSETID) |
+ (1ULL << CAP_MKNOD) |
+ (1ULL << CAP_SETFCAP) |
+ (1ULL << CAP_DAC_OVERRIDE);
/* Child */
@@ -374,15 +101,15 @@ int import_verify(
reset_signal_mask();
assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
- gpg_pipe[1] = safe_close(gpg_pipe[1]);
+ pipefd[1] = safe_close(pipefd[1]);
- if (dup2(gpg_pipe[0], STDIN_FILENO) != STDIN_FILENO) {
+ if (dup2(pipefd[0], STDIN_FILENO) != STDIN_FILENO) {
log_error_errno(errno, "Failed to dup2() fd: %m");
_exit(EXIT_FAILURE);
}
- if (gpg_pipe[0] != STDIN_FILENO)
- gpg_pipe[0] = safe_close(gpg_pipe[0]);
+ if (pipefd[0] != STDIN_FILENO)
+ pipefd[0] = safe_close(pipefd[0]);
null_fd = open("/dev/null", O_WRONLY|O_NOCTTY);
if (null_fd < 0) {
@@ -398,58 +125,32 @@ int import_verify(
if (null_fd != STDOUT_FILENO)
null_fd = safe_close(null_fd);
- /* We add the user keyring only to the command line
- * arguments, if it's around since gpg fails
- * otherwise. */
- if (access(USER_KEYRING_PATH, F_OK) >= 0)
- cmd[k++] = "--keyring=" USER_KEYRING_PATH;
- else
- cmd[k++] = "--keyring=" VENDOR_KEYRING_PATH;
-
- cmd[k++] = "--verify";
- cmd[k++] = sig_file_path;
- cmd[k++] = "-";
- cmd[k++] = NULL;
-
fd_cloexec(STDIN_FILENO, false);
fd_cloexec(STDOUT_FILENO, false);
fd_cloexec(STDERR_FILENO, false);
- execvp("gpg", (char * const *) cmd);
- log_error_errno(errno, "Failed to execute gpg: %m");
- _exit(EXIT_FAILURE);
- }
+ if (unshare(CLONE_NEWNET) < 0)
+ log_error_errno(errno, "Failed to lock tar into network namespace, ignoring: %m");
- gpg_pipe[0] = safe_close(gpg_pipe[0]);
+ r = capability_bounding_set_drop(~retain, true);
+ if (r < 0)
+ log_error_errno(r, "Failed to drop capabilities, ignoring: %m");
- r = loop_write(gpg_pipe[1], checksum_job->payload, checksum_job->payload_size, false);
- if (r < 0) {
- log_error_errno(r, "Failed to write to pipe: %m");
- goto finish;
+ execlp("tar", "tar", "--numeric-owner", "-C", path, "-px", NULL);
+ log_error_errno(errno, "Failed to execute tar: %m");
+ _exit(EXIT_FAILURE);
}
- gpg_pipe[1] = safe_close(gpg_pipe[1]);
-
- r = wait_for_terminate_and_warn("gpg", pid, true);
- pid = 0;
- if (r < 0)
- goto finish;
- if (r > 0) {
- log_error("Signature verification failed.");
- r = -EBADMSG;
- } else {
- log_info("Signature verification succeeded.");
- r = 0;
- }
+ pipefd[0] = safe_close(pipefd[0]);
+ r = pipefd[1];
+ pipefd[1] = -1;
-finish:
- if (sig_file >= 0)
- unlink(sig_file_path);
+ *ret = pid;
return r;
}
-int import_fork_tar(const char *path, pid_t *ret) {
+int import_fork_tar_c(const char *path, pid_t *ret) {
_cleanup_close_pair_ int pipefd[2] = { -1, -1 };
pid_t pid;
int r;
@@ -466,13 +167,7 @@ int import_fork_tar(const char *path, pid_t *ret) {
if (pid == 0) {
int null_fd;
- uint64_t retain =
- (1ULL << CAP_CHOWN) |
- (1ULL << CAP_FOWNER) |
- (1ULL << CAP_FSETID) |
- (1ULL << CAP_MKNOD) |
- (1ULL << CAP_SETFCAP) |
- (1ULL << CAP_DAC_OVERRIDE);
+ uint64_t retain = (1ULL << CAP_DAC_OVERRIDE);
/* Child */
@@ -480,28 +175,28 @@ int import_fork_tar(const char *path, pid_t *ret) {
reset_signal_mask();
assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
- pipefd[1] = safe_close(pipefd[1]);
+ pipefd[0] = safe_close(pipefd[0]);
- if (dup2(pipefd[0], STDIN_FILENO) != STDIN_FILENO) {
+ if (dup2(pipefd[1], STDOUT_FILENO) != STDOUT_FILENO) {
log_error_errno(errno, "Failed to dup2() fd: %m");
_exit(EXIT_FAILURE);
}
- if (pipefd[0] != STDIN_FILENO)
- pipefd[0] = safe_close(pipefd[0]);
+ if (pipefd[1] != STDOUT_FILENO)
+ pipefd[1] = safe_close(pipefd[1]);
- null_fd = open("/dev/null", O_WRONLY|O_NOCTTY);
+ null_fd = open("/dev/null", O_RDONLY|O_NOCTTY);
if (null_fd < 0) {
log_error_errno(errno, "Failed to open /dev/null: %m");
_exit(EXIT_FAILURE);
}
- if (dup2(null_fd, STDOUT_FILENO) != STDOUT_FILENO) {
+ if (dup2(null_fd, STDIN_FILENO) != STDIN_FILENO) {
log_error_errno(errno, "Failed to dup2() fd: %m");
_exit(EXIT_FAILURE);
}
- if (null_fd != STDOUT_FILENO)
+ if (null_fd != STDIN_FILENO)
null_fd = safe_close(null_fd);
fd_cloexec(STDIN_FILENO, false);
@@ -515,14 +210,14 @@ int import_fork_tar(const char *path, pid_t *ret) {
if (r < 0)
log_error_errno(r, "Failed to drop capabilities, ignoring: %m");
- execlp("tar", "tar", "--numeric-owner", "-C", path, "-px", NULL);
+ execlp("tar", "tar", "--sparse", "-C", path, "-c", ".", NULL);
log_error_errno(errno, "Failed to execute tar: %m");
_exit(EXIT_FAILURE);
}
- pipefd[0] = safe_close(pipefd[0]);
- r = pipefd[1];
- pipefd[1] = -1;
+ pipefd[1] = safe_close(pipefd[1]);
+ r = pipefd[0];
+ pipefd[0] = -1;
*ret = pid;
diff --git a/src/import/import-common.h b/src/import/import-common.h
index f6b4268fd7..7b60de80c2 100644
--- a/src/import/import-common.h
+++ b/src/import/import-common.h
@@ -21,21 +21,8 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdbool.h>
-
-#include "import-job.h"
-#include "import-util.h"
-
-int import_make_local_copy(const char *final, const char *root, const char *local, bool force_local);
-
-int import_find_old_etags(const char *url, const char *root, int dt, const char *prefix, const char *suffix, char ***etags);
-
int import_make_read_only_fd(int fd);
int import_make_read_only(const char *path);
-int import_make_path(const char *url, const char *etag, const char *image_root, const char *prefix, const char *suffix, char **ret);
-
-int import_make_verification_jobs(ImportJob **ret_checksum_job, ImportJob **ret_signature_job, ImportVerify verify, const char *url, CurlGlue *glue, ImportJobFinished on_finished, void *userdata);
-int import_verify(ImportJob *main_job, ImportJob *checksum_job, ImportJob *signature_job);
-
-int import_fork_tar(const char *path, pid_t *ret);
+int import_fork_tar_c(const char *path, pid_t *ret);
+int import_fork_tar_x(const char *path, pid_t *ret);
diff --git a/src/import/import-compress.c b/src/import/import-compress.c
new file mode 100644
index 0000000000..d6b8133036
--- /dev/null
+++ b/src/import/import-compress.c
@@ -0,0 +1,470 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2015 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 "import-compress.h"
+
+void import_compress_free(ImportCompress *c) {
+ assert(c);
+
+ if (c->type == IMPORT_COMPRESS_XZ)
+ lzma_end(&c->xz);
+ else if (c->type == IMPORT_COMPRESS_GZIP) {
+ if (c->encoding)
+ deflateEnd(&c->gzip);
+ else
+ inflateEnd(&c->gzip);
+ } else if (c->type == IMPORT_COMPRESS_BZIP2) {
+ if (c->encoding)
+ BZ2_bzCompressEnd(&c->bzip2);
+ else
+ BZ2_bzDecompressEnd(&c->bzip2);
+ }
+
+ c->type = IMPORT_COMPRESS_UNKNOWN;
+}
+
+int import_uncompress_detect(ImportCompress *c, const void *data, size_t size) {
+ static const uint8_t xz_signature[] = {
+ 0xfd, '7', 'z', 'X', 'Z', 0x00
+ };
+ static const uint8_t gzip_signature[] = {
+ 0x1f, 0x8b
+ };
+ static const uint8_t bzip2_signature[] = {
+ 'B', 'Z', 'h'
+ };
+
+ int r;
+
+ assert(c);
+
+ if (c->type != IMPORT_COMPRESS_UNKNOWN)
+ return 1;
+
+ if (size < MAX3(sizeof(xz_signature),
+ sizeof(gzip_signature),
+ sizeof(bzip2_signature)))
+ return 0;
+
+ assert(data);
+
+ if (memcmp(data, xz_signature, sizeof(xz_signature)) == 0) {
+ lzma_ret xzr;
+
+ xzr = lzma_stream_decoder(&c->xz, UINT64_MAX, LZMA_TELL_UNSUPPORTED_CHECK);
+ if (xzr != LZMA_OK)
+ return -EIO;
+
+ c->type = IMPORT_COMPRESS_XZ;
+
+ } else if (memcmp(data, gzip_signature, sizeof(gzip_signature)) == 0) {
+ r = inflateInit2(&c->gzip, 15+16);
+ if (r != Z_OK)
+ return -EIO;
+
+ c->type = IMPORT_COMPRESS_GZIP;
+
+ } else if (memcmp(data, bzip2_signature, sizeof(bzip2_signature)) == 0) {
+ r = BZ2_bzDecompressInit(&c->bzip2, 0, 0);
+ if (r != BZ_OK)
+ return -EIO;
+
+ c->type = IMPORT_COMPRESS_BZIP2;
+ } else
+ c->type = IMPORT_COMPRESS_UNCOMPRESSED;
+
+ c->encoding = false;
+
+ return 1;
+}
+
+int import_uncompress(ImportCompress *c, const void *data, size_t size, ImportCompressCallback callback, void *userdata) {
+ int r;
+
+ assert(c);
+ assert(callback);
+
+ r = import_uncompress_detect(c, data, size);
+ if (r <= 0)
+ return r;
+
+ if (c->encoding)
+ return -EINVAL;
+
+ if (size <= 0)
+ return 1;
+
+ assert(data);
+
+ switch (c->type) {
+
+ case IMPORT_COMPRESS_UNCOMPRESSED:
+ r = callback(data, size, userdata);
+ if (r < 0)
+ return r;
+
+ break;
+
+ case IMPORT_COMPRESS_XZ:
+ c->xz.next_in = data;
+ c->xz.avail_in = size;
+
+ while (c->xz.avail_in > 0) {
+ uint8_t buffer[16 * 1024];
+ lzma_ret lzr;
+
+ c->xz.next_out = buffer;
+ c->xz.avail_out = sizeof(buffer);
+
+ lzr = lzma_code(&c->xz, LZMA_RUN);
+ if (lzr != LZMA_OK && lzr != LZMA_STREAM_END)
+ return -EIO;
+
+ r = callback(buffer, sizeof(buffer) - c->xz.avail_out, userdata);
+ if (r < 0)
+ return r;
+ }
+
+ break;
+
+ case IMPORT_COMPRESS_GZIP:
+ c->gzip.next_in = (void*) data;
+ c->gzip.avail_in = size;
+
+ while (c->gzip.avail_in > 0) {
+ uint8_t buffer[16 * 1024];
+
+ c->gzip.next_out = buffer;
+ c->gzip.avail_out = sizeof(buffer);
+
+ r = inflate(&c->gzip, Z_NO_FLUSH);
+ if (r != Z_OK && r != Z_STREAM_END)
+ return -EIO;
+
+ r = callback(buffer, sizeof(buffer) - c->gzip.avail_out, userdata);
+ if (r < 0)
+ return r;
+ }
+
+ break;
+
+ case IMPORT_COMPRESS_BZIP2:
+ c->bzip2.next_in = (void*) data;
+ c->bzip2.avail_in = size;
+
+ while (c->bzip2.avail_in > 0) {
+ uint8_t buffer[16 * 1024];
+
+ c->bzip2.next_out = (char*) buffer;
+ c->bzip2.avail_out = sizeof(buffer);
+
+ r = BZ2_bzDecompress(&c->bzip2);
+ if (r != BZ_OK && r != BZ_STREAM_END)
+ return -EIO;
+
+ r = callback(buffer, sizeof(buffer) - c->bzip2.avail_out, userdata);
+ if (r < 0)
+ return r;
+ }
+
+ break;
+
+ default:
+ assert_not_reached("Unknown compression");
+ }
+
+ return 1;
+}
+
+int import_compress_init(ImportCompress *c, ImportCompressType t) {
+ int r;
+
+ assert(c);
+
+ switch (t) {
+
+ case IMPORT_COMPRESS_XZ: {
+ lzma_ret xzr;
+
+ xzr = lzma_easy_encoder(&c->xz, LZMA_PRESET_DEFAULT, LZMA_CHECK_CRC64);
+ if (xzr != LZMA_OK)
+ return -EIO;
+
+ c->type = IMPORT_COMPRESS_XZ;
+ break;
+ }
+
+ case IMPORT_COMPRESS_GZIP:
+ r = deflateInit2(&c->gzip, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 15 + 16, 8, Z_DEFAULT_STRATEGY);
+ if (r != Z_OK)
+ return -EIO;
+
+ c->type = IMPORT_COMPRESS_GZIP;
+ break;
+
+ case IMPORT_COMPRESS_BZIP2:
+ r = BZ2_bzCompressInit(&c->bzip2, 9, 0, 0);
+ if (r != BZ_OK)
+ return -EIO;
+
+ c->type = IMPORT_COMPRESS_BZIP2;
+ break;
+
+ case IMPORT_COMPRESS_UNCOMPRESSED:
+ c->type = IMPORT_COMPRESS_UNCOMPRESSED;
+ break;
+
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ c->encoding = true;
+ return 0;
+}
+
+static int enlarge_buffer(void **buffer, size_t *buffer_size, size_t *buffer_allocated) {
+ size_t l;
+ void *p;
+
+ if (*buffer_allocated > *buffer_size)
+ return 0;
+
+ l = MAX(16*1024U, (*buffer_size * 2));
+ p = realloc(*buffer, l);
+ if (!p)
+ return -ENOMEM;
+
+ *buffer = p;
+ *buffer_allocated = l;
+
+ return 1;
+}
+
+int import_compress(ImportCompress *c, const void *data, size_t size, void **buffer, size_t *buffer_size, size_t *buffer_allocated) {
+ int r;
+
+ assert(c);
+ assert(buffer);
+ assert(buffer_size);
+ assert(buffer_allocated);
+
+ if (!c->encoding)
+ return -EINVAL;
+
+ if (size <= 0)
+ return 0;
+
+ assert(data);
+
+ *buffer_size = 0;
+
+ switch (c->type) {
+
+ case IMPORT_COMPRESS_XZ:
+
+ c->xz.next_in = data;
+ c->xz.avail_in = size;
+
+ while (c->xz.avail_in > 0) {
+ lzma_ret lzr;
+
+ r = enlarge_buffer(buffer, buffer_size, buffer_allocated);
+ if (r < 0)
+ return r;
+
+ c->xz.next_out = (uint8_t*) *buffer + *buffer_size;
+ c->xz.avail_out = *buffer_allocated - *buffer_size;
+
+ lzr = lzma_code(&c->xz, LZMA_RUN);
+ if (lzr != LZMA_OK)
+ return -EIO;
+
+ *buffer_size += (*buffer_allocated - *buffer_size) - c->xz.avail_out;
+ }
+
+ break;
+
+ case IMPORT_COMPRESS_GZIP:
+
+ c->gzip.next_in = (void*) data;
+ c->gzip.avail_in = size;
+
+ while (c->gzip.avail_in > 0) {
+ r = enlarge_buffer(buffer, buffer_size, buffer_allocated);
+ if (r < 0)
+ return r;
+
+ c->gzip.next_out = (uint8_t*) *buffer + *buffer_size;
+ c->gzip.avail_out = *buffer_allocated - *buffer_size;
+
+ r = deflate(&c->gzip, Z_NO_FLUSH);
+ if (r != Z_OK)
+ return -EIO;
+
+ *buffer_size += (*buffer_allocated - *buffer_size) - c->gzip.avail_out;
+ }
+
+ break;
+
+ case IMPORT_COMPRESS_BZIP2:
+
+ c->bzip2.next_in = (void*) data;
+ c->bzip2.avail_in = size;
+
+ while (c->bzip2.avail_in > 0) {
+ r = enlarge_buffer(buffer, buffer_size, buffer_allocated);
+ if (r < 0)
+ return r;
+
+ c->bzip2.next_out = (void*) ((uint8_t*) *buffer + *buffer_size);
+ c->bzip2.avail_out = *buffer_allocated - *buffer_size;
+
+ r = BZ2_bzCompress(&c->bzip2, BZ_RUN);
+ if (r != BZ_RUN_OK)
+ return -EIO;
+
+ *buffer_size += (*buffer_allocated - *buffer_size) - c->bzip2.avail_out;
+ }
+
+ break;
+
+ case IMPORT_COMPRESS_UNCOMPRESSED:
+
+ if (*buffer_allocated < size) {
+ void *p;
+
+ p = realloc(*buffer, size);
+ if (!p)
+ return -ENOMEM;
+
+ *buffer = p;
+ *buffer_allocated = size;
+ }
+
+ memcpy(*buffer, data, size);
+ *buffer_size = size;
+ break;
+
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+int import_compress_finish(ImportCompress *c, void **buffer, size_t *buffer_size, size_t *buffer_allocated) {
+ int r;
+
+ assert(c);
+ assert(buffer);
+ assert(buffer_size);
+ assert(buffer_allocated);
+
+ if (!c->encoding)
+ return -EINVAL;
+
+ *buffer_size = 0;
+
+ switch (c->type) {
+
+ case IMPORT_COMPRESS_XZ: {
+ lzma_ret lzr;
+
+ c->xz.avail_in = 0;
+
+ do {
+ r = enlarge_buffer(buffer, buffer_size, buffer_allocated);
+ if (r < 0)
+ return r;
+
+ c->xz.next_out = (uint8_t*) *buffer + *buffer_size;
+ c->xz.avail_out = *buffer_allocated - *buffer_size;
+
+ lzr = lzma_code(&c->xz, LZMA_FINISH);
+ if (lzr != LZMA_OK && lzr != LZMA_STREAM_END)
+ return -EIO;
+
+ *buffer_size += (*buffer_allocated - *buffer_size) - c->xz.avail_out;
+ } while (lzr != LZMA_STREAM_END);
+
+ break;
+ }
+
+ case IMPORT_COMPRESS_GZIP:
+ c->gzip.avail_in = 0;
+
+ do {
+ r = enlarge_buffer(buffer, buffer_size, buffer_allocated);
+ if (r < 0)
+ return r;
+
+ c->gzip.next_out = (uint8_t*) *buffer + *buffer_size;
+ c->gzip.avail_out = *buffer_allocated - *buffer_size;
+
+ r = deflate(&c->gzip, Z_FINISH);
+ if (r != Z_OK && r != Z_STREAM_END)
+ return -EIO;
+
+ *buffer_size += (*buffer_allocated - *buffer_size) - c->gzip.avail_out;
+ } while (r != Z_STREAM_END);
+
+ break;
+
+ case IMPORT_COMPRESS_BZIP2:
+ c->bzip2.avail_in = 0;
+
+ do {
+ r = enlarge_buffer(buffer, buffer_size, buffer_allocated);
+ if (r < 0)
+ return r;
+
+ c->bzip2.next_out = (void*) ((uint8_t*) *buffer + *buffer_size);
+ c->bzip2.avail_out = *buffer_allocated - *buffer_size;
+
+ r = BZ2_bzCompress(&c->bzip2, BZ_FINISH);
+ if (r != BZ_FINISH_OK && r != BZ_STREAM_END)
+ return -EIO;
+
+ *buffer_size += (*buffer_allocated - *buffer_size) - c->bzip2.avail_out;
+ } while (r != BZ_STREAM_END);
+
+ break;
+
+ case IMPORT_COMPRESS_UNCOMPRESSED:
+ break;
+
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+static const char* const import_compress_type_table[_IMPORT_COMPRESS_TYPE_MAX] = {
+ [IMPORT_COMPRESS_UNKNOWN] = "unknown",
+ [IMPORT_COMPRESS_UNCOMPRESSED] = "uncompressed",
+ [IMPORT_COMPRESS_XZ] = "xz",
+ [IMPORT_COMPRESS_GZIP] = "gzip",
+ [IMPORT_COMPRESS_BZIP2] = "bzip2",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(import_compress_type, ImportCompressType);
diff --git a/src/import/import-compress.h b/src/import/import-compress.h
new file mode 100644
index 0000000000..50d91f732c
--- /dev/null
+++ b/src/import/import-compress.h
@@ -0,0 +1,64 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2015 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 <lzma.h>
+#include <zlib.h>
+#include <bzlib.h>
+
+#include "macro.h"
+
+typedef enum ImportCompressType {
+ IMPORT_COMPRESS_UNKNOWN,
+ IMPORT_COMPRESS_UNCOMPRESSED,
+ IMPORT_COMPRESS_XZ,
+ IMPORT_COMPRESS_GZIP,
+ IMPORT_COMPRESS_BZIP2,
+ _IMPORT_COMPRESS_TYPE_MAX,
+ _IMPORT_COMPRESS_TYPE_INVALID = -1,
+} ImportCompressType;
+
+typedef struct ImportCompress {
+ ImportCompressType type;
+ bool encoding;
+ union {
+ lzma_stream xz;
+ z_stream gzip;
+ bz_stream bzip2;
+ };
+} ImportCompress;
+
+typedef int (*ImportCompressCallback)(const void *data, size_t size, void *userdata);
+
+void import_compress_free(ImportCompress *c);
+
+int import_uncompress_detect(ImportCompress *c, const void *data, size_t size);
+int import_uncompress(ImportCompress *c, const void *data, size_t size, ImportCompressCallback callback, void *userdata);
+
+int import_compress_init(ImportCompress *c, ImportCompressType t);
+int import_compress(ImportCompress *c, const void *data, size_t size, void **buffer, size_t *buffer_size, size_t *buffer_allocated);
+int import_compress_finish(ImportCompress *c, void **buffer, size_t *buffer_size, size_t *buffer_allocated);
+
+const char* import_compress_type_to_string(ImportCompressType t) _const_;
+ImportCompressType import_compress_type_from_string(const char *s) _pure_;
diff --git a/src/import/import-job.h b/src/import/import-job.h
deleted file mode 100644
index dcf89cb28c..0000000000
--- a/src/import/import-job.h
+++ /dev/null
@@ -1,119 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2015 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 <lzma.h>
-#include <zlib.h>
-#include <bzlib.h>
-#include <gcrypt.h>
-
-#include "macro.h"
-#include "curl-util.h"
-
-typedef struct ImportJob ImportJob;
-
-typedef void (*ImportJobFinished)(ImportJob *job);
-typedef int (*ImportJobOpenDisk)(ImportJob *job);
-typedef int (*ImportJobHeader)(ImportJob *job, const char *header, size_t sz);
-typedef void (*ImportJobProgress)(ImportJob *job);
-
-typedef enum ImportJobState {
- IMPORT_JOB_INIT,
- IMPORT_JOB_ANALYZING, /* Still reading into ->payload, to figure out what we have */
- IMPORT_JOB_RUNNING, /* Writing to destination */
- IMPORT_JOB_DONE,
- IMPORT_JOB_FAILED,
- _IMPORT_JOB_STATE_MAX,
- _IMPORT_JOB_STATE_INVALID = -1,
-} ImportJobState;
-
-#define IMPORT_JOB_STATE_IS_COMPLETE(j) (IN_SET((j)->state, IMPORT_JOB_DONE, IMPORT_JOB_FAILED))
-
-typedef enum ImportJobCompression {
- IMPORT_JOB_UNCOMPRESSED,
- IMPORT_JOB_XZ,
- IMPORT_JOB_GZIP,
- IMPORT_JOB_BZIP2,
- _IMPORT_JOB_COMPRESSION_MAX,
- _IMPORT_JOB_COMPRESSION_INVALID = -1,
-} ImportJobCompression;
-
-struct ImportJob {
- ImportJobState state;
- int error;
-
- char *url;
-
- void *userdata;
- ImportJobFinished on_finished;
- ImportJobOpenDisk on_open_disk;
- ImportJobHeader on_header;
- ImportJobProgress on_progress;
-
- CurlGlue *glue;
- CURL *curl;
- struct curl_slist *request_header;
-
- char *etag;
- char **old_etags;
- bool etag_exists;
-
- uint64_t content_length;
- uint64_t written_compressed;
- uint64_t written_uncompressed;
-
- uint64_t uncompressed_max;
- uint64_t compressed_max;
-
- uint8_t *payload;
- size_t payload_size;
- size_t payload_allocated;
-
- int disk_fd;
-
- usec_t mtime;
-
- ImportJobCompression compressed;
- lzma_stream xz;
- z_stream gzip;
- bz_stream bzip2;
-
- unsigned progress_percent;
- usec_t start_usec;
- usec_t last_status_usec;
-
- bool allow_sparse;
-
- bool calc_checksum;
- gcry_md_hd_t checksum_context;
-
- char *checksum;
-};
-
-int import_job_new(ImportJob **job, const char *url, CurlGlue *glue, void *userdata);
-ImportJob* import_job_unref(ImportJob *job);
-
-int import_job_begin(ImportJob *j);
-
-void import_job_curl_on_finished(CurlGlue *g, CURL *curl, CURLcode result);
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(ImportJob*, import_job_unref);
diff --git a/src/import/import-raw.c b/src/import/import-raw.c
index 8d99f1085c..97e1254f09 100644
--- a/src/import/import-raw.c
+++ b/src/import/import-raw.c
@@ -3,7 +3,7 @@
/***
This file is part of systemd.
- Copyright 2014 Lennart Poettering
+ Copyright 2015 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
@@ -19,64 +19,64 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <sys/xattr.h>
#include <linux/fs.h>
-#include <curl/curl.h>
#include "sd-daemon.h"
-#include "utf8.h"
-#include "strv.h"
-#include "copy.h"
-#include "btrfs-util.h"
+#include "sd-event.h"
#include "util.h"
-#include "macro.h"
+#include "path-util.h"
+#include "btrfs-util.h"
+#include "copy.h"
#include "mkdir.h"
-#include "import-util.h"
-#include "curl-util.h"
+#include "rm-rf.h"
+#include "ratelimit.h"
+#include "machine-pool.h"
#include "qcow2-util.h"
-#include "import-job.h"
+#include "import-compress.h"
#include "import-common.h"
#include "import-raw.h"
-typedef enum RawProgress {
- RAW_DOWNLOADING,
- RAW_VERIFYING,
- RAW_UNPACKING,
- RAW_FINALIZING,
- RAW_COPYING,
-} RawProgress;
-
struct RawImport {
sd_event *event;
- CurlGlue *glue;
char *image_root;
- ImportJob *raw_job;
- ImportJob *checksum_job;
- ImportJob *signature_job;
-
RawImportFinished on_finished;
void *userdata;
char *local;
bool force_local;
+ bool read_only;
+ bool grow_machine_directory;
char *temp_path;
char *final_path;
- ImportVerify verify;
+ int input_fd;
+ int output_fd;
+
+ ImportCompress compress;
+
+ uint64_t written_since_last_grow;
+
+ sd_event_source *input_event_source;
+
+ uint8_t buffer[16*1024];
+ size_t buffer_size;
+
+ uint64_t written_compressed;
+ uint64_t written_uncompressed;
+
+ struct stat st;
+
+ unsigned last_percent;
+ RateLimit progress_rate_limit;
};
RawImport* raw_import_unref(RawImport *i) {
if (!i)
return NULL;
- import_job_unref(i->raw_job);
- import_job_unref(i->checksum_job);
- import_job_unref(i->signature_job);
-
- curl_glue_unref(i->glue);
sd_event_unref(i->event);
if (i->temp_path) {
@@ -84,6 +84,12 @@ RawImport* raw_import_unref(RawImport *i) {
free(i->temp_path);
}
+ import_compress_free(&i->compress);
+
+ sd_event_source_unref(i->input_event_source);
+
+ safe_close(i->output_fd);
+
free(i->final_path);
free(i->image_root);
free(i->local);
@@ -108,13 +114,19 @@ int raw_import_new(
if (!i)
return -ENOMEM;
+ i->input_fd = i->output_fd = -1;
i->on_finished = on_finished;
i->userdata = userdata;
+ RATELIMIT_INIT(i->progress_rate_limit, 100 * USEC_PER_MSEC, 1);
+ i->last_percent = (unsigned) -1;
+
i->image_root = strdup(image_root ?: "/var/lib/machines");
if (!i->image_root)
return -ENOMEM;
+ i->grow_machine_directory = path_startswith(i->image_root, "/var/lib/machines");
+
if (event)
i->event = sd_event_ref(event);
else {
@@ -123,68 +135,35 @@ int raw_import_new(
return r;
}
- r = curl_glue_new(&i->glue, i->event);
- if (r < 0)
- return r;
-
- i->glue->on_finished = import_job_curl_on_finished;
- i->glue->userdata = i;
-
*ret = i;
i = NULL;
return 0;
}
-static void raw_import_report_progress(RawImport *i, RawProgress p) {
+static void raw_import_report_progress(RawImport *i) {
unsigned percent;
-
assert(i);
- switch (p) {
-
- case RAW_DOWNLOADING: {
- unsigned remain = 80;
-
- percent = 0;
-
- if (i->checksum_job) {
- percent += i->checksum_job->progress_percent * 5 / 100;
- remain -= 5;
- }
-
- if (i->signature_job) {
- percent += i->signature_job->progress_percent * 5 / 100;
- remain -= 5;
- }
-
- if (i->raw_job)
- percent += i->raw_job->progress_percent * remain / 100;
- break;
- }
-
- case RAW_VERIFYING:
- percent = 80;
- break;
-
- case RAW_UNPACKING:
- percent = 85;
- break;
+ /* We have no size information, unless the source is a regular file */
+ if (!S_ISREG(i->st.st_mode))
+ return;
- case RAW_FINALIZING:
- percent = 90;
- break;
+ if (i->written_compressed >= (uint64_t) i->st.st_size)
+ percent = 100;
+ else
+ percent = (unsigned) ((i->written_compressed * UINT64_C(100)) / (uint64_t) i->st.st_size);
- case RAW_COPYING:
- percent = 95;
- break;
+ if (percent == i->last_percent)
+ return;
- default:
- assert_not_reached("Unknown progress state");
- }
+ if (!ratelimit_test(&i->progress_rate_limit))
+ return;
sd_notifyf(false, "X_IMPORT_PROGRESS=%u", percent);
- log_debug("Combined progress %u%%", percent);
+ log_info("Imported %u%%.", percent);
+
+ i->last_percent = percent;
}
static int raw_import_maybe_convert_qcow2(RawImport *i) {
@@ -193,9 +172,8 @@ static int raw_import_maybe_convert_qcow2(RawImport *i) {
int r;
assert(i);
- assert(i->raw_job);
- r = qcow2_detect(i->raw_job->disk_fd);
+ r = qcow2_detect(i->output_fd);
if (r < 0)
return log_error_errno(r, "Failed to detect whether this is a QCOW2 image: %m");
if (r == 0)
@@ -206,308 +184,278 @@ static int raw_import_maybe_convert_qcow2(RawImport *i) {
if (r < 0)
return log_oom();
- converted_fd = open(t, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0644);
+ converted_fd = open(t, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664);
if (converted_fd < 0)
return log_error_errno(errno, "Failed to create %s: %m", t);
- r = chattr_fd(converted_fd, true, FS_NOCOW_FL);
+ r = chattr_fd(converted_fd, FS_NOCOW_FL, FS_NOCOW_FL);
if (r < 0)
log_warning_errno(errno, "Failed to set file attributes on %s: %m", t);
log_info("Unpacking QCOW2 file.");
- r = qcow2_convert(i->raw_job->disk_fd, converted_fd);
+ r = qcow2_convert(i->output_fd, converted_fd);
if (r < 0) {
unlink(t);
return log_error_errno(r, "Failed to convert qcow2 image: %m");
}
- unlink(i->temp_path);
+ (void) unlink(i->temp_path);
free(i->temp_path);
-
i->temp_path = t;
t = NULL;
- safe_close(i->raw_job->disk_fd);
- i->raw_job->disk_fd = converted_fd;
+ safe_close(i->output_fd);
+ i->output_fd = converted_fd;
converted_fd = -1;
return 1;
}
-static int raw_import_make_local_copy(RawImport *i) {
- _cleanup_free_ char *tp = NULL;
- _cleanup_close_ int dfd = -1;
- const char *p;
+static int raw_import_finish(RawImport *i) {
int r;
assert(i);
- assert(i->raw_job);
+ assert(i->output_fd >= 0);
+ assert(i->temp_path);
+ assert(i->final_path);
+
+ /* In case this was a sparse file, make sure the file system is right */
+ if (i->written_uncompressed > 0) {
+ if (ftruncate(i->output_fd, i->written_uncompressed) < 0)
+ return log_error_errno(errno, "Failed to truncate file: %m");
+ }
- if (!i->local)
- return 0;
+ r = raw_import_maybe_convert_qcow2(i);
+ if (r < 0)
+ return r;
- if (i->raw_job->etag_exists) {
- /* We have downloaded this one previously, reopen it */
+ if (S_ISREG(i->st.st_mode)) {
+ (void) copy_times(i->input_fd, i->output_fd);
+ (void) copy_xattr(i->input_fd, i->output_fd);
+ }
- assert(i->raw_job->disk_fd < 0);
+ if (i->read_only) {
+ r = import_make_read_only_fd(i->output_fd);
+ if (r < 0)
+ return r;
+ }
- if (!i->final_path) {
- r = import_make_path(i->raw_job->url, i->raw_job->etag, i->image_root, ".raw-", ".raw", &i->final_path);
- if (r < 0)
- return log_oom();
- }
+ if (i->force_local)
+ (void) rm_rf(i->final_path, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME);
- i->raw_job->disk_fd = open(i->final_path, O_RDONLY|O_NOCTTY|O_CLOEXEC);
- if (i->raw_job->disk_fd < 0)
- return log_error_errno(errno, "Failed to open vendor image: %m");
- } else {
- /* We freshly downloaded the image, use it */
+ r = rename_noreplace(AT_FDCWD, i->temp_path, AT_FDCWD, i->final_path);
+ if (r < 0)
+ return log_error_errno(r, "Failed to move image into place: %m");
- assert(i->raw_job->disk_fd >= 0);
+ free(i->temp_path);
+ i->temp_path = NULL;
- if (lseek(i->raw_job->disk_fd, SEEK_SET, 0) == (off_t) -1)
- return log_error_errno(errno, "Failed to seek to beginning of vendor image: %m");
- }
+ return 0;
+}
- p = strjoina(i->image_root, "/", i->local, ".raw");
+static int raw_import_open_disk(RawImport *i) {
+ int r;
- if (i->force_local) {
- (void) btrfs_subvol_remove(p);
- (void) rm_rf_dangerous(p, false, true, false);
- }
+ assert(i);
- r = tempfn_random(p, &tp);
+ assert(!i->final_path);
+ assert(!i->temp_path);
+ assert(i->output_fd < 0);
+
+ i->final_path = strjoin(i->image_root, "/", i->local, ".raw", NULL);
+ if (!i->final_path)
+ return log_oom();
+
+ r = tempfn_random(i->final_path, &i->temp_path);
if (r < 0)
return log_oom();
- dfd = open(tp, O_WRONLY|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664);
- if (dfd < 0)
- return log_error_errno(errno, "Failed to create writable copy of image: %m");
+ (void) mkdir_parents_label(i->temp_path, 0700);
- /* Turn off COW writing. This should greatly improve
- * performance on COW file systems like btrfs, since it
- * reduces fragmentation caused by not allowing in-place
- * writes. */
- r = chattr_fd(dfd, true, FS_NOCOW_FL);
+ i->output_fd = open(i->temp_path, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664);
+ if (i->output_fd < 0)
+ return log_error_errno(errno, "Failed to open destination %s: %m", i->temp_path);
+
+ r = chattr_fd(i->output_fd, FS_NOCOW_FL, FS_NOCOW_FL);
if (r < 0)
- log_warning_errno(errno, "Failed to set file attributes on %s: %m", tp);
+ log_warning_errno(errno, "Failed to set file attributes on %s: %m", i->temp_path);
- r = copy_bytes(i->raw_job->disk_fd, dfd, (off_t) -1, true);
- if (r < 0) {
- unlink(tp);
- return log_error_errno(r, "Failed to make writable copy of image: %m");
- }
+ return 0;
+}
- (void) copy_times(i->raw_job->disk_fd, dfd);
- (void) copy_xattr(i->raw_job->disk_fd, dfd);
+static int raw_import_try_reflink(RawImport *i) {
+ off_t p;
+ int r;
- dfd = safe_close(dfd);
+ assert(i);
+ assert(i->input_fd >= 0);
+ assert(i->output_fd >= 0);
- r = rename(tp, p);
- if (r < 0) {
- unlink(tp);
- return log_error_errno(errno, "Failed to move writable image into place: %m");
- }
+ if (i->compress.type != IMPORT_COMPRESS_UNCOMPRESSED)
+ return 0;
+
+ if (!S_ISREG(i->st.st_mode))
+ return 0;
+
+ p = lseek(i->input_fd, 0, SEEK_CUR);
+ if (p == (off_t) -1)
+ return log_error_errno(errno, "Failed to read file offset of input file: %m");
+
+ /* Let's only try a btrfs reflink, if we are reading from the beginning of the file */
+ if ((uint64_t) p != (uint64_t) i->buffer_size)
+ return 0;
+
+ r = btrfs_reflink(i->input_fd, i->output_fd);
+ if (r >= 0)
+ return 1;
- log_info("Created new local image '%s'.", i->local);
return 0;
}
-static bool raw_import_is_done(RawImport *i) {
- assert(i);
- assert(i->raw_job);
+static int raw_import_write(const void *p, size_t sz, void *userdata) {
+ RawImport *i = userdata;
+ ssize_t n;
+
+ if (i->grow_machine_directory && i->written_since_last_grow >= GROW_INTERVAL_BYTES) {
+ i->written_since_last_grow = 0;
+ grow_machine_directory();
+ }
- if (i->raw_job->state != IMPORT_JOB_DONE)
- return false;
- if (i->checksum_job && i->checksum_job->state != IMPORT_JOB_DONE)
- return false;
- if (i->signature_job && i->signature_job->state != IMPORT_JOB_DONE)
- return false;
+ n = sparse_write(i->output_fd, p, sz, 64);
+ if (n < 0)
+ return -errno;
+ if ((size_t) n < sz)
+ return -EIO;
- return true;
+ i->written_uncompressed += sz;
+ i->written_since_last_grow += sz;
+
+ return 0;
}
-static void raw_import_job_on_finished(ImportJob *j) {
- RawImport *i;
+static int raw_import_process(RawImport *i) {
+ ssize_t l;
int r;
- assert(j);
- assert(j->userdata);
+ assert(i);
+ assert(i->buffer_size < sizeof(i->buffer));
- i = j->userdata;
- if (j->error != 0) {
- if (j == i->checksum_job)
- log_error_errno(j->error, "Failed to retrieve SHA256 checksum, cannot verify. (Try --verify=no?)");
- else if (j == i->signature_job)
- log_error_errno(j->error, "Failed to retrieve signature file, cannot verify. (Try --verify=no?)");
- else
- log_error_errno(j->error, "Failed to retrieve image file. (Wrong URL?)");
+ l = read(i->input_fd, i->buffer + i->buffer_size, sizeof(i->buffer) - i->buffer_size);
+ if (l < 0) {
+ if (errno == EAGAIN)
+ return 0;
- r = j->error;
+ r = log_error_errno(errno, "Failed to read input file: %m");
goto finish;
}
+ if (l == 0) {
+ if (i->compress.type == IMPORT_COMPRESS_UNKNOWN) {
+ log_error("Premature end of file: %m");
+ r = -EIO;
+ goto finish;
+ }
- /* This is invoked if either the download completed
- * successfully, or the download was skipped because we
- * already have the etag. In this case ->etag_exists is
- * true.
- *
- * We only do something when we got all three files */
-
- if (!raw_import_is_done(i))
- return;
-
- if (!i->raw_job->etag_exists) {
- /* This is a new download, verify it, and move it into place */
- assert(i->raw_job->disk_fd >= 0);
+ r = raw_import_finish(i);
+ goto finish;
+ }
- raw_import_report_progress(i, RAW_VERIFYING);
+ i->buffer_size += l;
- r = import_verify(i->raw_job, i->checksum_job, i->signature_job);
- if (r < 0)
+ if (i->compress.type == IMPORT_COMPRESS_UNKNOWN) {
+ r = import_uncompress_detect(&i->compress, i->buffer, i->buffer_size);
+ if (r < 0) {
+ log_error("Failed to detect file compression: %m");
goto finish;
+ }
+ if (r == 0) /* Need more data */
+ return 0;
- raw_import_report_progress(i, RAW_UNPACKING);
-
- r = raw_import_maybe_convert_qcow2(i);
+ r = raw_import_open_disk(i);
if (r < 0)
goto finish;
- raw_import_report_progress(i, RAW_FINALIZING);
-
- r = import_make_read_only_fd(i->raw_job->disk_fd);
+ r = raw_import_try_reflink(i);
if (r < 0)
goto finish;
-
- r = rename(i->temp_path, i->final_path);
- if (r < 0) {
- r = log_error_errno(errno, "Failed to move RAW file into place: %m");
+ if (r > 0) {
+ r = raw_import_finish(i);
goto finish;
}
+ }
- free(i->temp_path);
- i->temp_path = NULL;
+ r = import_uncompress(&i->compress, i->buffer, i->buffer_size, raw_import_write, i);
+ if (r < 0) {
+ log_error_errno(r, "Failed to decode and write: %m");
+ goto finish;
}
- raw_import_report_progress(i, RAW_COPYING);
+ i->written_compressed += i->buffer_size;
+ i->buffer_size = 0;
- r = raw_import_make_local_copy(i);
- if (r < 0)
- goto finish;
+ raw_import_report_progress(i);
- r = 0;
+ return 0;
finish:
if (i->on_finished)
i->on_finished(i, r, i->userdata);
else
sd_event_exit(i->event, r);
-}
-
-static int raw_import_job_on_open_disk(ImportJob *j) {
- RawImport *i;
- int r;
-
- assert(j);
- assert(j->userdata);
-
- i = j->userdata;
- assert(i->raw_job == j);
- assert(!i->final_path);
- assert(!i->temp_path);
-
- r = import_make_path(j->url, j->etag, i->image_root, ".raw-", ".raw", &i->final_path);
- if (r < 0)
- return log_oom();
-
- r = tempfn_random(i->final_path, &i->temp_path);
- if (r <0)
- return log_oom();
-
- mkdir_parents_label(i->temp_path, 0700);
-
- j->disk_fd = open(i->temp_path, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0644);
- if (j->disk_fd < 0)
- return log_error_errno(errno, "Failed to create %s: %m", i->temp_path);
-
- r = chattr_fd(j->disk_fd, true, FS_NOCOW_FL);
- if (r < 0)
- log_warning_errno(errno, "Failed to set file attributes on %s: %m", i->temp_path);
return 0;
}
-static void raw_import_job_on_progress(ImportJob *j) {
- RawImport *i;
+static int raw_import_on_input(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
+ RawImport *i = userdata;
- assert(j);
- assert(j->userdata);
+ return raw_import_process(i);
+}
- i = j->userdata;
+static int raw_import_on_defer(sd_event_source *s, void *userdata) {
+ RawImport *i = userdata;
- raw_import_report_progress(i, RAW_DOWNLOADING);
+ return raw_import_process(i);
}
-int raw_import_pull(RawImport *i, const char *url, const char *local, bool force_local, ImportVerify verify) {
+int raw_import_start(RawImport *i, int fd, const char *local, bool force_local, bool read_only) {
int r;
assert(i);
- assert(verify < _IMPORT_VERIFY_MAX);
- assert(verify >= 0);
+ assert(fd >= 0);
+ assert(local);
- if (!http_url_is_valid(url))
+ if (!machine_name_is_valid(local))
return -EINVAL;
- if (local && !machine_name_is_valid(local))
- return -EINVAL;
-
- if (i->raw_job)
+ if (i->input_fd >= 0)
return -EBUSY;
- r = free_and_strdup(&i->local, local);
+ r = fd_nonblock(fd, true);
if (r < 0)
return r;
- i->force_local = force_local;
- i->verify = verify;
- /* Queue job for the image itself */
- r = import_job_new(&i->raw_job, url, i->glue, i);
- if (r < 0)
- return r;
-
- i->raw_job->on_finished = raw_import_job_on_finished;
- i->raw_job->on_open_disk = raw_import_job_on_open_disk;
- i->raw_job->on_progress = raw_import_job_on_progress;
- i->raw_job->calc_checksum = verify != IMPORT_VERIFY_NO;
-
- r = import_find_old_etags(url, i->image_root, DT_REG, ".raw-", ".raw", &i->raw_job->old_etags);
- if (r < 0)
- return r;
-
- r = import_make_verification_jobs(&i->checksum_job, &i->signature_job, verify, url, i->glue, raw_import_job_on_finished, i);
- if (r < 0)
- return r;
-
- r = import_job_begin(i->raw_job);
+ r = free_and_strdup(&i->local, local);
if (r < 0)
return r;
+ i->force_local = force_local;
+ i->read_only = read_only;
- if (i->checksum_job) {
- i->checksum_job->on_progress = raw_import_job_on_progress;
+ if (fstat(fd, &i->st) < 0)
+ return -errno;
- r = import_job_begin(i->checksum_job);
+ r = sd_event_add_io(i->event, &i->input_event_source, fd, EPOLLIN, raw_import_on_input, i);
+ if (r == -EPERM) {
+ /* This fd does not support epoll, for example because it is a regular file. Busy read in that case */
+ r = sd_event_add_defer(i->event, &i->input_event_source, raw_import_on_defer, i);
if (r < 0)
return r;
- }
- if (i->signature_job) {
- i->signature_job->on_progress = raw_import_job_on_progress;
-
- r = import_job_begin(i->signature_job);
- if (r < 0)
- return r;
+ r = sd_event_source_set_enabled(i->input_event_source, SD_EVENT_ON);
}
+ if (r < 0)
+ return r;
- return 0;
+ i->input_fd = fd;
+ return r;
}
diff --git a/src/import/import-raw.h b/src/import/import-raw.h
index ae2c29991f..bf7c770340 100644
--- a/src/import/import-raw.h
+++ b/src/import/import-raw.h
@@ -5,7 +5,7 @@
/***
This file is part of systemd.
- Copyright 2014 Lennart Poettering
+ Copyright 2015 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
@@ -34,4 +34,4 @@ RawImport* raw_import_unref(RawImport *import);
DEFINE_TRIVIAL_CLEANUP_FUNC(RawImport*, raw_import_unref);
-int raw_import_pull(RawImport *import, const char *url, const char *local, bool force_local, ImportVerify verify);
+int raw_import_start(RawImport *i, int fd, const char *local, bool force_local, bool read_only);
diff --git a/src/import/import-tar.c b/src/import/import-tar.c
index 999aa8ab5e..12701bfcef 100644
--- a/src/import/import-tar.c
+++ b/src/import/import-tar.c
@@ -19,76 +19,85 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <sys/prctl.h>
-#include <curl/curl.h>
+#include <linux/fs.h>
#include "sd-daemon.h"
-#include "utf8.h"
-#include "strv.h"
-#include "copy.h"
-#include "btrfs-util.h"
+#include "sd-event.h"
#include "util.h"
-#include "macro.h"
+#include "path-util.h"
+#include "btrfs-util.h"
+#include "copy.h"
#include "mkdir.h"
-#include "import-util.h"
-#include "curl-util.h"
-#include "import-job.h"
+#include "rm-rf.h"
+#include "ratelimit.h"
+#include "machine-pool.h"
+#include "qcow2-util.h"
+#include "import-compress.h"
#include "import-common.h"
#include "import-tar.h"
-
-typedef enum TarProgress {
- TAR_DOWNLOADING,
- TAR_VERIFYING,
- TAR_FINALIZING,
- TAR_COPYING,
-} TarProgress;
+#include "process-util.h"
struct TarImport {
sd_event *event;
- CurlGlue *glue;
char *image_root;
- ImportJob *tar_job;
- ImportJob *checksum_job;
- ImportJob *signature_job;
-
TarImportFinished on_finished;
void *userdata;
char *local;
bool force_local;
-
- pid_t tar_pid;
+ bool read_only;
+ bool grow_machine_directory;
char *temp_path;
char *final_path;
- ImportVerify verify;
+ int input_fd;
+ int tar_fd;
+
+ ImportCompress compress;
+
+ uint64_t written_since_last_grow;
+
+ sd_event_source *input_event_source;
+
+ uint8_t buffer[16*1024];
+ size_t buffer_size;
+
+ uint64_t written_compressed;
+ uint64_t written_uncompressed;
+
+ struct stat st;
+
+ pid_t tar_pid;
+
+ unsigned last_percent;
+ RateLimit progress_rate_limit;
};
TarImport* tar_import_unref(TarImport *i) {
if (!i)
return NULL;
+ sd_event_source_unref(i->input_event_source);
+
if (i->tar_pid > 1) {
(void) kill_and_sigcont(i->tar_pid, SIGKILL);
(void) wait_for_terminate(i->tar_pid, NULL);
}
- import_job_unref(i->tar_job);
- import_job_unref(i->checksum_job);
- import_job_unref(i->signature_job);
-
- curl_glue_unref(i->glue);
- sd_event_unref(i->event);
-
if (i->temp_path) {
- (void) btrfs_subvol_remove(i->temp_path);
- (void) rm_rf_dangerous(i->temp_path, false, true, false);
+ (void) rm_rf(i->temp_path, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME);
free(i->temp_path);
}
+ import_compress_free(&i->compress);
+
+ sd_event_unref(i->event);
+
+ safe_close(i->tar_fd);
+
free(i->final_path);
free(i->image_root);
free(i->local);
@@ -108,19 +117,24 @@ int tar_import_new(
int r;
assert(ret);
- assert(event);
i = new0(TarImport, 1);
if (!i)
return -ENOMEM;
+ i->input_fd = i->tar_fd = -1;
i->on_finished = on_finished;
i->userdata = userdata;
+ RATELIMIT_INIT(i->progress_rate_limit, 100 * USEC_PER_MSEC, 1);
+ i->last_percent = (unsigned) -1;
+
i->image_root = strdup(image_root ?: "/var/lib/machines");
if (!i->image_root)
return -ENOMEM;
+ i->grow_machine_directory = path_startswith(i->image_root, "/var/lib/machines");
+
if (event)
i->event = sd_event_ref(event);
else {
@@ -129,282 +143,239 @@ int tar_import_new(
return r;
}
- r = curl_glue_new(&i->glue, i->event);
- if (r < 0)
- return r;
-
- i->glue->on_finished = import_job_curl_on_finished;
- i->glue->userdata = i;
-
*ret = i;
i = NULL;
return 0;
}
-static void tar_import_report_progress(TarImport *i, TarProgress p) {
+static void tar_import_report_progress(TarImport *i) {
unsigned percent;
-
assert(i);
- switch (p) {
+ /* We have no size information, unless the source is a regular file */
+ if (!S_ISREG(i->st.st_mode))
+ return;
- case TAR_DOWNLOADING: {
- unsigned remain = 85;
+ if (i->written_compressed >= (uint64_t) i->st.st_size)
+ percent = 100;
+ else
+ percent = (unsigned) ((i->written_compressed * UINT64_C(100)) / (uint64_t) i->st.st_size);
- percent = 0;
+ if (percent == i->last_percent)
+ return;
- if (i->checksum_job) {
- percent += i->checksum_job->progress_percent * 5 / 100;
- remain -= 5;
- }
+ if (!ratelimit_test(&i->progress_rate_limit))
+ return;
- if (i->signature_job) {
- percent += i->signature_job->progress_percent * 5 / 100;
- remain -= 5;
- }
+ sd_notifyf(false, "X_IMPORT_PROGRESS=%u", percent);
+ log_info("Imported %u%%.", percent);
- if (i->tar_job)
- percent += i->tar_job->progress_percent * remain / 100;
- break;
- }
+ i->last_percent = percent;
+}
- case TAR_VERIFYING:
- percent = 85;
- break;
+static int tar_import_finish(TarImport *i) {
+ int r;
- case TAR_FINALIZING:
- percent = 90;
- break;
+ assert(i);
+ assert(i->tar_fd >= 0);
+ assert(i->temp_path);
+ assert(i->final_path);
- case TAR_COPYING:
- percent = 95;
- break;
+ i->tar_fd = safe_close(i->tar_fd);
- default:
- assert_not_reached("Unknown progress state");
+ if (i->tar_pid > 0) {
+ r = wait_for_terminate_and_warn("tar", i->tar_pid, true);
+ i->tar_pid = 0;
+ if (r < 0)
+ return r;
}
- sd_notifyf(false, "X_IMPORT_PROGRESS=%u", percent);
- log_debug("Combined progress %u%%", percent);
+ if (i->read_only) {
+ r = import_make_read_only(i->temp_path);
+ if (r < 0)
+ return r;
+ }
+
+ if (i->force_local)
+ (void) rm_rf(i->final_path, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME);
+
+ r = rename_noreplace(AT_FDCWD, i->temp_path, AT_FDCWD, i->final_path);
+ if (r < 0)
+ return log_error_errno(r, "Failed to move image into place: %m");
+
+ free(i->temp_path);
+ i->temp_path = NULL;
+
+ return 0;
}
-static int tar_import_make_local_copy(TarImport *i) {
+static int tar_import_fork_tar(TarImport *i) {
int r;
assert(i);
- assert(i->tar_job);
- if (!i->local)
- return 0;
+ assert(!i->final_path);
+ assert(!i->temp_path);
+ assert(i->tar_fd < 0);
- if (!i->final_path) {
- r = import_make_path(i->tar_job->url, i->tar_job->etag, i->image_root, ".tar-", NULL, &i->final_path);
- if (r < 0)
- return log_oom();
- }
+ i->final_path = strjoin(i->image_root, "/", i->local, NULL);
+ if (!i->final_path)
+ return log_oom();
- r = import_make_local_copy(i->final_path, i->image_root, i->local, i->force_local);
+ r = tempfn_random(i->final_path, &i->temp_path);
if (r < 0)
- return r;
+ return log_oom();
- return 0;
-}
+ (void) mkdir_parents_label(i->temp_path, 0700);
-static bool tar_import_is_done(TarImport *i) {
- assert(i);
- assert(i->tar_job);
+ r = btrfs_subvol_make(i->temp_path);
+ if (r == -ENOTTY) {
+ if (mkdir(i->temp_path, 0755) < 0)
+ return log_error_errno(errno, "Failed to create directory %s: %m", i->temp_path);
+ } else if (r < 0)
+ return log_error_errno(errno, "Failed to create subvolume %s: %m", i->temp_path);
- if (i->tar_job->state != IMPORT_JOB_DONE)
- return false;
- if (i->checksum_job && i->checksum_job->state != IMPORT_JOB_DONE)
- return false;
- if (i->signature_job && i->signature_job->state != IMPORT_JOB_DONE)
- return false;
+ i->tar_fd = import_fork_tar_x(i->temp_path, &i->tar_pid);
+ if (i->tar_fd < 0)
+ return i->tar_fd;
- return true;
+ return 0;
}
-static void tar_import_job_on_finished(ImportJob *j) {
- TarImport *i;
+static int tar_import_write(const void *p, size_t sz, void *userdata) {
+ TarImport *i = userdata;
int r;
- assert(j);
- assert(j->userdata);
-
- i = j->userdata;
- if (j->error != 0) {
- if (j == i->checksum_job)
- log_error_errno(j->error, "Failed to retrieve SHA256 checksum, cannot verify. (Try --verify=no?)");
- else if (j == i->signature_job)
- log_error_errno(j->error, "Failed to retrieve signature file, cannot verify. (Try --verify=no?)");
- else
- log_error_errno(j->error, "Failed to retrieve image file. (Wrong URL?)");
-
- r = j->error;
- goto finish;
+ if (i->grow_machine_directory && i->written_since_last_grow >= GROW_INTERVAL_BYTES) {
+ i->written_since_last_grow = 0;
+ grow_machine_directory();
}
- /* This is invoked if either the download completed
- * successfully, or the download was skipped because we
- * already have the etag. */
+ r = loop_write(i->tar_fd, p, sz, false);
+ if (r < 0)
+ return r;
- if (!tar_import_is_done(i))
- return;
+ i->written_uncompressed += sz;
+ i->written_since_last_grow += sz;
- j->disk_fd = safe_close(i->tar_job->disk_fd);
+ return 0;
+}
- if (i->tar_pid > 0) {
- r = wait_for_terminate_and_warn("tar", i->tar_pid, true);
- i->tar_pid = 0;
- if (r < 0)
- goto finish;
- }
+static int tar_import_process(TarImport *i) {
+ ssize_t l;
+ int r;
- if (!i->tar_job->etag_exists) {
- /* This is a new download, verify it, and move it into place */
+ assert(i);
+ assert(i->buffer_size < sizeof(i->buffer));
- tar_import_report_progress(i, TAR_VERIFYING);
+ l = read(i->input_fd, i->buffer + i->buffer_size, sizeof(i->buffer) - i->buffer_size);
+ if (l < 0) {
+ if (errno == EAGAIN)
+ return 0;
- r = import_verify(i->tar_job, i->checksum_job, i->signature_job);
- if (r < 0)
+ r = log_error_errno(errno, "Failed to read input file: %m");
+ goto finish;
+ }
+ if (l == 0) {
+ if (i->compress.type == IMPORT_COMPRESS_UNKNOWN) {
+ log_error("Premature end of file: %m");
+ r = -EIO;
goto finish;
+ }
+
+ r = tar_import_finish(i);
+ goto finish;
+ }
- tar_import_report_progress(i, TAR_FINALIZING);
+ i->buffer_size += l;
- r = import_make_read_only(i->temp_path);
- if (r < 0)
+ if (i->compress.type == IMPORT_COMPRESS_UNKNOWN) {
+ r = import_uncompress_detect(&i->compress, i->buffer, i->buffer_size);
+ if (r < 0) {
+ log_error("Failed to detect file compression: %m");
goto finish;
+ }
+ if (r == 0) /* Need more data */
+ return 0;
- if (rename(i->temp_path, i->final_path) < 0) {
- r = log_error_errno(errno, "Failed to rename to final image name: %m");
+ r = tar_import_fork_tar(i);
+ if (r < 0)
goto finish;
- }
+ }
- free(i->temp_path);
- i->temp_path = NULL;
+ r = import_uncompress(&i->compress, i->buffer, i->buffer_size, tar_import_write, i);
+ if (r < 0) {
+ log_error_errno(r, "Failed to decode and write: %m");
+ goto finish;
}
- tar_import_report_progress(i, TAR_COPYING);
+ i->written_compressed += i->buffer_size;
+ i->buffer_size = 0;
- r = tar_import_make_local_copy(i);
- if (r < 0)
- goto finish;
+ tar_import_report_progress(i);
- r = 0;
+ return 0;
finish:
if (i->on_finished)
i->on_finished(i, r, i->userdata);
else
sd_event_exit(i->event, r);
-}
-
-static int tar_import_job_on_open_disk(ImportJob *j) {
- _cleanup_close_pair_ int pipefd[2] = { -1 , -1 };
- TarImport *i;
- int r;
-
- assert(j);
- assert(j->userdata);
-
- i = j->userdata;
- assert(i->tar_job == j);
- assert(!i->final_path);
- assert(!i->temp_path);
- assert(i->tar_pid <= 0);
-
- r = import_make_path(j->url, j->etag, i->image_root, ".tar-", NULL, &i->final_path);
- if (r < 0)
- return log_oom();
-
- r = tempfn_random(i->final_path, &i->temp_path);
- if (r < 0)
- return log_oom();
-
- mkdir_parents_label(i->temp_path, 0700);
-
- r = btrfs_subvol_make(i->temp_path);
- if (r == -ENOTTY) {
- if (mkdir(i->temp_path, 0755) < 0)
- return log_error_errno(errno, "Failed to create directory %s: %m", i->temp_path);
- } else if (r < 0)
- return log_error_errno(errno, "Failed to create subvolume %s: %m", i->temp_path);
-
- j->disk_fd = import_fork_tar(i->temp_path, &i->tar_pid);
- if (j->disk_fd < 0)
- return j->disk_fd;
return 0;
}
-static void tar_import_job_on_progress(ImportJob *j) {
- TarImport *i;
+static int tar_import_on_input(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
+ TarImport *i = userdata;
- assert(j);
- assert(j->userdata);
+ return tar_import_process(i);
+}
- i = j->userdata;
+static int tar_import_on_defer(sd_event_source *s, void *userdata) {
+ TarImport *i = userdata;
- tar_import_report_progress(i, TAR_DOWNLOADING);
+ return tar_import_process(i);
}
-int tar_import_pull(TarImport *i, const char *url, const char *local, bool force_local, ImportVerify verify) {
+int tar_import_start(TarImport *i, int fd, const char *local, bool force_local, bool read_only) {
int r;
assert(i);
+ assert(fd >= 0);
+ assert(local);
- if (!http_url_is_valid(url))
- return -EINVAL;
-
- if (local && !machine_name_is_valid(local))
+ if (!machine_name_is_valid(local))
return -EINVAL;
- if (i->tar_job)
+ if (i->input_fd >= 0)
return -EBUSY;
- r = free_and_strdup(&i->local, local);
+ r = fd_nonblock(fd, true);
if (r < 0)
return r;
- i->force_local = force_local;
- i->verify = verify;
- r = import_job_new(&i->tar_job, url, i->glue, i);
- if (r < 0)
- return r;
-
- i->tar_job->on_finished = tar_import_job_on_finished;
- i->tar_job->on_open_disk = tar_import_job_on_open_disk;
- i->tar_job->on_progress = tar_import_job_on_progress;
- i->tar_job->calc_checksum = verify != IMPORT_VERIFY_NO;
-
- r = import_find_old_etags(url, i->image_root, DT_DIR, ".tar-", NULL, &i->tar_job->old_etags);
- if (r < 0)
- return r;
-
- r = import_make_verification_jobs(&i->checksum_job, &i->signature_job, verify, url, i->glue, tar_import_job_on_finished, i);
- if (r < 0)
- return r;
-
- r = import_job_begin(i->tar_job);
+ r = free_and_strdup(&i->local, local);
if (r < 0)
return r;
+ i->force_local = force_local;
+ i->read_only = read_only;
- if (i->checksum_job) {
- i->checksum_job->on_progress = tar_import_job_on_progress;
+ if (fstat(fd, &i->st) < 0)
+ return -errno;
- r = import_job_begin(i->checksum_job);
+ r = sd_event_add_io(i->event, &i->input_event_source, fd, EPOLLIN, tar_import_on_input, i);
+ if (r == -EPERM) {
+ /* This fd does not support epoll, for example because it is a regular file. Busy read in that case */
+ r = sd_event_add_defer(i->event, &i->input_event_source, tar_import_on_defer, i);
if (r < 0)
return r;
- }
- if (i->signature_job) {
- i->signature_job->on_progress = tar_import_job_on_progress;
-
- r = import_job_begin(i->signature_job);
- if (r < 0)
- return r;
+ r = sd_event_source_set_enabled(i->input_event_source, SD_EVENT_ON);
}
+ if (r < 0)
+ return r;
- return 0;
+ i->input_fd = fd;
+ return r;
}
diff --git a/src/import/import-tar.h b/src/import/import-tar.h
index 212f804d16..aaecb51398 100644
--- a/src/import/import-tar.h
+++ b/src/import/import-tar.h
@@ -34,4 +34,4 @@ TarImport* tar_import_unref(TarImport *import);
DEFINE_TRIVIAL_CLEANUP_FUNC(TarImport*, tar_import_unref);
-int tar_import_pull(TarImport *import, const char *url, const char *local, bool force_local, ImportVerify verify);
+int tar_import_start(TarImport *import, int fd, const char *local, bool force_local, bool read_only);
diff --git a/src/import/import.c b/src/import/import.c
new file mode 100644
index 0000000000..fff5a104b1
--- /dev/null
+++ b/src/import/import.c
@@ -0,0 +1,337 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2015 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 "sd-event.h"
+#include "event-util.h"
+#include "verbs.h"
+#include "build.h"
+#include "signal-util.h"
+#include "machine-image.h"
+#include "import-util.h"
+#include "import-tar.h"
+#include "import-raw.h"
+
+static bool arg_force = false;
+static bool arg_read_only = false;
+static const char *arg_image_root = "/var/lib/machines";
+
+static int interrupt_signal_handler(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
+ log_notice("Transfer aborted.");
+ sd_event_exit(sd_event_source_get_event(s), EINTR);
+ return 0;
+}
+
+static void on_tar_finished(TarImport *import, int error, void *userdata) {
+ sd_event *event = userdata;
+ assert(import);
+
+ if (error == 0)
+ log_info("Operation completed successfully.");
+
+ sd_event_exit(event, abs(error));
+}
+
+static int import_tar(int argc, char *argv[], void *userdata) {
+ _cleanup_(tar_import_unrefp) TarImport *import = NULL;
+ _cleanup_event_unref_ sd_event *event = NULL;
+ const char *path = NULL, *local = NULL;
+ _cleanup_free_ char *ll = NULL;
+ _cleanup_close_ int open_fd = -1;
+ int r, fd;
+
+ if (argc >= 2)
+ path = argv[1];
+ if (isempty(path) || streq(path, "-"))
+ path = NULL;
+
+ if (argc >= 3)
+ local = argv[2];
+ else if (path)
+ local = basename(path);
+ if (isempty(local) || streq(local, "-"))
+ local = NULL;
+
+ if (local) {
+ r = tar_strip_suffixes(local, &ll);
+ if (r < 0)
+ return log_oom();
+
+ local = ll;
+
+ if (!machine_name_is_valid(local)) {
+ log_error("Local image name '%s' is not valid.", local);
+ return -EINVAL;
+ }
+
+ if (!arg_force) {
+ r = image_find(local, NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to check whether image '%s' exists: %m", local);
+ else if (r > 0) {
+ log_error_errno(EEXIST, "Image '%s' already exists.", local);
+ return -EEXIST;
+ }
+ }
+ } else
+ local = "imported";
+
+ if (path) {
+ open_fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY);
+ if (open_fd < 0)
+ return log_error_errno(errno, "Failed to open tar image to import: %m");
+
+ fd = open_fd;
+
+ log_info("Importing '%s', saving as '%s'.", path, local);
+ } else {
+ _cleanup_free_ char *pretty = NULL;
+
+ fd = STDIN_FILENO;
+
+ (void) readlink_malloc("/proc/self/fd/0", &pretty);
+ log_info("Importing '%s', saving as '%s'.", strna(pretty), local);
+ }
+
+ r = sd_event_default(&event);
+ if (r < 0)
+ return log_error_errno(r, "Failed to allocate event loop: %m");
+
+ assert_se(sigprocmask_many(SIG_BLOCK, SIGTERM, SIGINT, -1) == 0);
+ sd_event_add_signal(event, NULL, SIGTERM, interrupt_signal_handler, NULL);
+ sd_event_add_signal(event, NULL, SIGINT, interrupt_signal_handler, NULL);
+
+ r = tar_import_new(&import, event, arg_image_root, on_tar_finished, event);
+ if (r < 0)
+ return log_error_errno(r, "Failed to allocate importer: %m");
+
+ r = tar_import_start(import, fd, local, arg_force, arg_read_only);
+ if (r < 0)
+ return log_error_errno(r, "Failed to import image: %m");
+
+ r = sd_event_loop(event);
+ if (r < 0)
+ return log_error_errno(r, "Failed to run event loop: %m");
+
+ log_info("Exiting.");
+ return -r;
+}
+
+static void on_raw_finished(RawImport *import, int error, void *userdata) {
+ sd_event *event = userdata;
+ assert(import);
+
+ if (error == 0)
+ log_info("Operation completed successfully.");
+
+ sd_event_exit(event, abs(error));
+}
+
+static int import_raw(int argc, char *argv[], void *userdata) {
+ _cleanup_(raw_import_unrefp) RawImport *import = NULL;
+ _cleanup_event_unref_ sd_event *event = NULL;
+ const char *path = NULL, *local = NULL;
+ _cleanup_free_ char *ll = NULL;
+ _cleanup_close_ int open_fd = -1;
+ int r, fd;
+
+ if (argc >= 2)
+ path = argv[1];
+ if (isempty(path) || streq(path, "-"))
+ path = NULL;
+
+ if (argc >= 3)
+ local = argv[2];
+ else if (path)
+ local = basename(path);
+ if (isempty(local) || streq(local, "-"))
+ local = NULL;
+
+ if (local) {
+ r = raw_strip_suffixes(local, &ll);
+ if (r < 0)
+ return log_oom();
+
+ local = ll;
+
+ if (!machine_name_is_valid(local)) {
+ log_error("Local image name '%s' is not valid.", local);
+ return -EINVAL;
+ }
+
+ if (!arg_force) {
+ r = image_find(local, NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to check whether image '%s' exists: %m", local);
+ else if (r > 0) {
+ log_error_errno(EEXIST, "Image '%s' already exists.", local);
+ return -EEXIST;
+ }
+ }
+ } else
+ local = "imported";
+
+ if (path) {
+ open_fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY);
+ if (open_fd < 0)
+ return log_error_errno(errno, "Failed to open raw image to import: %m");
+
+ fd = open_fd;
+
+ log_info("Importing '%s', saving as '%s'.", path, local);
+ } else {
+ _cleanup_free_ char *pretty = NULL;
+
+ fd = STDIN_FILENO;
+
+ (void) readlink_malloc("/proc/self/fd/0", &pretty);
+ log_info("Importing '%s', saving as '%s'.", pretty, local);
+ }
+
+ r = sd_event_default(&event);
+ if (r < 0)
+ return log_error_errno(r, "Failed to allocate event loop: %m");
+
+ assert_se(sigprocmask_many(SIG_BLOCK, SIGTERM, SIGINT, -1) == 0);
+ sd_event_add_signal(event, NULL, SIGTERM, interrupt_signal_handler, NULL);
+ sd_event_add_signal(event, NULL, SIGINT, interrupt_signal_handler, NULL);
+
+ r = raw_import_new(&import, event, arg_image_root, on_raw_finished, event);
+ if (r < 0)
+ return log_error_errno(r, "Failed to allocate importer: %m");
+
+ r = raw_import_start(import, fd, local, arg_force, arg_read_only);
+ if (r < 0)
+ return log_error_errno(r, "Failed to import image: %m");
+
+ r = sd_event_loop(event);
+ if (r < 0)
+ return log_error_errno(r, "Failed to run event loop: %m");
+
+ log_info("Exiting.");
+ return -r;
+}
+
+static int help(int argc, char *argv[], void *userdata) {
+
+ printf("%s [OPTIONS...] {COMMAND} ...\n\n"
+ "Import container or virtual machine images.\n\n"
+ " -h --help Show this help\n"
+ " --version Show package version\n"
+ " --force Force creation of image\n"
+ " --image-root=PATH Image root directory\n"
+ " --read-only Create a read-only image\n\n"
+ "Commands:\n"
+ " tar FILE [NAME] Import a TAR image\n"
+ " raw FILE [NAME] Import a RAW image\n",
+ program_invocation_short_name);
+
+ return 0;
+}
+
+static int parse_argv(int argc, char *argv[]) {
+
+ enum {
+ ARG_VERSION = 0x100,
+ ARG_FORCE,
+ ARG_IMAGE_ROOT,
+ ARG_READ_ONLY,
+ };
+
+ static const struct option options[] = {
+ { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, ARG_VERSION },
+ { "force", no_argument, NULL, ARG_FORCE },
+ { "image-root", required_argument, NULL, ARG_IMAGE_ROOT },
+ { "read-only", no_argument, NULL, ARG_READ_ONLY },
+ {}
+ };
+
+ int c;
+
+ assert(argc >= 0);
+ assert(argv);
+
+ while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
+
+ switch (c) {
+
+ case 'h':
+ return help(0, NULL, NULL);
+
+ case ARG_VERSION:
+ puts(PACKAGE_STRING);
+ puts(SYSTEMD_FEATURES);
+ return 0;
+
+ case ARG_FORCE:
+ arg_force = true;
+ break;
+
+ case ARG_IMAGE_ROOT:
+ arg_image_root = optarg;
+ break;
+
+ case ARG_READ_ONLY:
+ arg_read_only = true;
+ break;
+
+ case '?':
+ return -EINVAL;
+
+ default:
+ assert_not_reached("Unhandled option");
+ }
+
+ return 1;
+}
+
+static int import_main(int argc, char *argv[]) {
+
+ static const Verb verbs[] = {
+ { "help", VERB_ANY, VERB_ANY, 0, help },
+ { "tar", 2, 3, 0, import_tar },
+ { "raw", 2, 3, 0, import_raw },
+ {}
+ };
+
+ return dispatch_verb(argc, argv, verbs, NULL);
+}
+
+int main(int argc, char *argv[]) {
+ int r;
+
+ setlocale(LC_ALL, "");
+ log_parse_environment();
+ log_open();
+
+ r = parse_argv(argc, argv);
+ if (r <= 0)
+ goto finish;
+
+ ignore_signals(SIGPIPE, -1);
+
+ r = import_main(argc, argv);
+
+finish:
+ return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+}
diff --git a/src/import/importd.c b/src/import/importd.c
index 1222bf3cd2..50566a6e5c 100644
--- a/src/import/importd.c
+++ b/src/import/importd.c
@@ -26,19 +26,27 @@
#include "strv.h"
#include "bus-util.h"
#include "bus-common-errors.h"
-#include "def.h"
#include "socket-util.h"
#include "mkdir.h"
-#include "import-util.h"
#include "def.h"
+#include "missing.h"
+#include "machine-pool.h"
+#include "path-util.h"
+#include "import-util.h"
+#include "process-util.h"
+#include "signal-util.h"
typedef struct Transfer Transfer;
typedef struct Manager Manager;
typedef enum TransferType {
- TRANSFER_TAR,
- TRANSFER_RAW,
- TRANSFER_DKR,
+ TRANSFER_IMPORT_TAR,
+ TRANSFER_IMPORT_RAW,
+ TRANSFER_EXPORT_TAR,
+ TRANSFER_EXPORT_RAW,
+ TRANSFER_PULL_TAR,
+ TRANSFER_PULL_RAW,
+ TRANSFER_PULL_DKR,
_TRANSFER_TYPE_MAX,
_TRANSFER_TYPE_INVALID = -1,
} TransferType;
@@ -55,8 +63,10 @@ struct Transfer {
char *remote;
char *local;
bool force_local;
+ bool read_only;
char *dkr_index_url;
+ char *format;
pid_t pid;
@@ -70,6 +80,9 @@ struct Transfer {
unsigned n_canceled;
unsigned progress_percent;
+
+ int stdin_fd;
+ int stdout_fd;
};
struct Manager {
@@ -89,9 +102,13 @@ struct Manager {
#define TRANSFERS_MAX 64
static const char* const transfer_type_table[_TRANSFER_TYPE_MAX] = {
- [TRANSFER_TAR] = "tar",
- [TRANSFER_RAW] = "raw",
- [TRANSFER_DKR] = "dkr",
+ [TRANSFER_IMPORT_TAR] = "import-tar",
+ [TRANSFER_IMPORT_RAW] = "import-raw",
+ [TRANSFER_EXPORT_TAR] = "export-tar",
+ [TRANSFER_EXPORT_RAW] = "export-raw",
+ [TRANSFER_PULL_TAR] = "pull-tar",
+ [TRANSFER_PULL_RAW] = "pull-raw",
+ [TRANSFER_PULL_DKR] = "pull-dkr",
};
DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(transfer_type, TransferType);
@@ -109,6 +126,7 @@ static Transfer *transfer_unref(Transfer *t) {
free(t->remote);
free(t->local);
free(t->dkr_index_url);
+ free(t->format);
free(t->object_path);
if (t->pid > 0) {
@@ -117,6 +135,8 @@ static Transfer *transfer_unref(Transfer *t) {
}
safe_close(t->log_fd);
+ safe_close(t->stdin_fd);
+ safe_close(t->stdout_fd);
free(t);
return NULL;
@@ -145,6 +165,8 @@ static int transfer_new(Manager *m, Transfer **ret) {
t->type = _TRANSFER_TYPE_INVALID;
t->log_fd = -1;
+ t->stdin_fd = -1;
+ t->verify = _IMPORT_VERIFY_INVALID;
id = m->current_transfer_id + 1;
@@ -225,7 +247,7 @@ static void transfer_send_logs(Transfer *t, bool flush) {
n = strndup(t->log_message, e - t->log_message);
/* Skip over NUL and newlines */
- while (e < t->log_message + t->log_message_size && (*e == 0 || *e == '\n'))
+ while ((e < t->log_message + t->log_message_size) && (*e == 0 || *e == '\n'))
e++;
memmove(t->log_message, e, t->log_message + sizeof(t->log_message) - e);
@@ -350,19 +372,21 @@ static int transfer_start(Transfer *t) {
return -errno;
if (t->pid == 0) {
const char *cmd[] = {
- "systemd-pull",
- transfer_type_to_string(t->type),
- "--verify",
+ NULL, /* systemd-import, systemd-export or systemd-pull */
+ NULL, /* tar, raw, dkr */
+ NULL, /* --verify= */
NULL, /* verify argument */
NULL, /* maybe --force */
+ NULL, /* maybe --read-only */
NULL, /* maybe --dkr-index-url */
- NULL, /* the actual URL */
+ NULL, /* if so: the actual URL */
+ NULL, /* maybe --format= */
+ NULL, /* if so: the actual format */
NULL, /* remote */
NULL, /* local */
NULL
};
- int null_fd;
- unsigned k = 3;
+ unsigned k = 0;
/* Child */
@@ -372,33 +396,55 @@ static int transfer_start(Transfer *t) {
pipefd[0] = safe_close(pipefd[0]);
- if (dup2(pipefd[1], STDOUT_FILENO) != STDOUT_FILENO) {
+ if (dup2(pipefd[1], STDERR_FILENO) != STDERR_FILENO) {
log_error_errno(errno, "Failed to dup2() fd: %m");
_exit(EXIT_FAILURE);
}
- if (dup2(pipefd[1], STDERR_FILENO) != STDERR_FILENO) {
- log_error_errno(errno, "Failed to dup2() fd: %m");
- _exit(EXIT_FAILURE);
+ if (t->stdout_fd >= 0) {
+ if (dup2(t->stdout_fd, STDOUT_FILENO) != STDOUT_FILENO) {
+ log_error_errno(errno, "Failed to dup2() fd: %m");
+ _exit(EXIT_FAILURE);
+ }
+
+ if (t->stdout_fd != STDOUT_FILENO)
+ safe_close(t->stdout_fd);
+ } else {
+ if (dup2(pipefd[1], STDOUT_FILENO) != STDOUT_FILENO) {
+ log_error_errno(errno, "Failed to dup2() fd: %m");
+ _exit(EXIT_FAILURE);
+ }
}
if (pipefd[1] != STDOUT_FILENO && pipefd[1] != STDERR_FILENO)
pipefd[1] = safe_close(pipefd[1]);
- null_fd = open("/dev/null", O_RDONLY|O_NOCTTY);
- if (null_fd < 0) {
- log_error_errno(errno, "Failed to open /dev/null: %m");
- _exit(EXIT_FAILURE);
+ if (t->stdin_fd >= 0) {
+ if (dup2(t->stdin_fd, STDIN_FILENO) != STDIN_FILENO) {
+ log_error_errno(errno, "Failed to dup2() fd: %m");
+ _exit(EXIT_FAILURE);
+ }
+
+ if (t->stdin_fd != STDIN_FILENO)
+ safe_close(t->stdin_fd);
+ } else {
+ int null_fd;
+
+ null_fd = open("/dev/null", O_RDONLY|O_NOCTTY);
+ if (null_fd < 0) {
+ log_error_errno(errno, "Failed to open /dev/null: %m");
+ _exit(EXIT_FAILURE);
+ }
+
+ if (dup2(null_fd, STDIN_FILENO) != STDIN_FILENO) {
+ log_error_errno(errno, "Failed to dup2() fd: %m");
+ _exit(EXIT_FAILURE);
+ }
+
+ if (null_fd != STDIN_FILENO)
+ safe_close(null_fd);
}
- if (dup2(null_fd, STDIN_FILENO) != STDIN_FILENO) {
- log_error_errno(errno, "Failed to dup2() fd: %m");
- _exit(EXIT_FAILURE);
- }
-
- if (null_fd != STDIN_FILENO)
- safe_close(null_fd);
-
fd_cloexec(STDIN_FILENO, false);
fd_cloexec(STDOUT_FILENO, false);
fd_cloexec(STDERR_FILENO, false);
@@ -406,22 +452,53 @@ static int transfer_start(Transfer *t) {
setenv("SYSTEMD_LOG_TARGET", "console-prefixed", 1);
setenv("NOTIFY_SOCKET", "/run/systemd/import/notify", 1);
- cmd[k++] = import_verify_to_string(t->verify);
+ if (IN_SET(t->type, TRANSFER_IMPORT_TAR, TRANSFER_IMPORT_RAW))
+ cmd[k++] = SYSTEMD_IMPORT_PATH;
+ else if (IN_SET(t->type, TRANSFER_EXPORT_TAR, TRANSFER_EXPORT_RAW))
+ cmd[k++] = SYSTEMD_EXPORT_PATH;
+ else
+ cmd[k++] = SYSTEMD_PULL_PATH;
+
+ if (IN_SET(t->type, TRANSFER_IMPORT_TAR, TRANSFER_EXPORT_TAR, TRANSFER_PULL_TAR))
+ cmd[k++] = "tar";
+ else if (IN_SET(t->type, TRANSFER_IMPORT_RAW, TRANSFER_EXPORT_RAW, TRANSFER_PULL_RAW))
+ cmd[k++] = "raw";
+ else
+ cmd[k++] = "dkr";
+
+ if (t->verify != _IMPORT_VERIFY_INVALID) {
+ cmd[k++] = "--verify";
+ cmd[k++] = import_verify_to_string(t->verify);
+ }
+
if (t->force_local)
cmd[k++] = "--force";
+ if (t->read_only)
+ cmd[k++] = "--read-only";
if (t->dkr_index_url) {
cmd[k++] = "--dkr-index-url";
cmd[k++] = t->dkr_index_url;
}
- cmd[k++] = t->remote;
+ if (t->format) {
+ cmd[k++] = "--format";
+ cmd[k++] = t->format;
+ }
+
+ if (!IN_SET(t->type, TRANSFER_EXPORT_TAR, TRANSFER_EXPORT_RAW)) {
+ if (t->remote)
+ cmd[k++] = t->remote;
+ else
+ cmd[k++] = "-";
+ }
+
if (t->local)
cmd[k++] = t->local;
cmd[k] = NULL;
- execv(SYSTEMD_PULL_PATH, (char * const *) cmd);
- log_error_errno(errno, "Failed to execute import tool: %m");
+ execv(cmd[0], (char * const *) cmd);
+ log_error_errno(errno, "Failed to execute %s tool: %m", cmd[0]);
_exit(EXIT_FAILURE);
}
@@ -429,6 +506,8 @@ static int transfer_start(Transfer *t) {
t->log_fd = pipefd[0];
pipefd[0] = -1;
+ t->stdin_fd = safe_close(t->stdin_fd);
+
r = sd_event_add_child(t->manager->event, &t->pid_event_source, t->pid, WEXITED, transfer_on_pid, t);
if (r < 0)
return r;
@@ -518,12 +597,10 @@ static int manager_on_notify(sd_event_source *s, int fd, uint32_t revents, void
return -errno;
}
+ cmsg_close_all(&msghdr);
+
for (cmsg = CMSG_FIRSTHDR(&msghdr); cmsg; cmsg = CMSG_NXTHDR(&msghdr, cmsg)) {
- if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
- close_many((int*) CMSG_DATA(cmsg), (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int));
- log_warning("Somebody sent us unexpected fds, ignoring.");
- return 0;
- } else if (cmsg->cmsg_level == SOL_SOCKET &&
+ if (cmsg->cmsg_level == SOL_SOCKET &&
cmsg->cmsg_type == SCM_CREDENTIALS &&
cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred))) {
@@ -643,7 +720,135 @@ static Transfer *manager_find(Manager *m, TransferType type, const char *dkr_ind
return NULL;
}
-static int method_pull_tar_or_raw(sd_bus *bus, sd_bus_message *msg, void *userdata, sd_bus_error *error) {
+static int method_import_tar_or_raw(sd_bus_message *msg, void *userdata, sd_bus_error *error) {
+ _cleanup_(transfer_unrefp) Transfer *t = NULL;
+ int fd, force, read_only, r;
+ const char *local, *object;
+ Manager *m = userdata;
+ TransferType type;
+ uint32_t id;
+
+ assert(msg);
+ assert(m);
+
+ r = bus_verify_polkit_async(
+ msg,
+ CAP_SYS_ADMIN,
+ "org.freedesktop.import1.import",
+ false,
+ UID_INVALID,
+ &m->polkit_registry,
+ error);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return 1; /* Will call us back */
+
+ r = sd_bus_message_read(msg, "hsbb", &fd, &local, &force, &read_only);
+ if (r < 0)
+ return r;
+
+ if (!machine_name_is_valid(local))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Local name %s is invalid", local);
+
+ r = setup_machine_directory((uint64_t) -1, error);
+ if (r < 0)
+ return r;
+
+ type = streq_ptr(sd_bus_message_get_member(msg), "ImportTar") ? TRANSFER_IMPORT_TAR : TRANSFER_IMPORT_RAW;
+
+ r = transfer_new(m, &t);
+ if (r < 0)
+ return r;
+
+ t->type = type;
+ t->force_local = force;
+ t->read_only = read_only;
+
+ t->local = strdup(local);
+ if (!t->local)
+ return -ENOMEM;
+
+ t->stdin_fd = fcntl(fd, F_DUPFD_CLOEXEC, 3);
+ if (t->stdin_fd < 0)
+ return -errno;
+
+ r = transfer_start(t);
+ if (r < 0)
+ return r;
+
+ object = t->object_path;
+ id = t->id;
+ t = NULL;
+
+ return sd_bus_reply_method_return(msg, "uo", id, object);
+}
+
+static int method_export_tar_or_raw(sd_bus_message *msg, void *userdata, sd_bus_error *error) {
+ _cleanup_(transfer_unrefp) Transfer *t = NULL;
+ int fd, r;
+ const char *local, *object, *format;
+ Manager *m = userdata;
+ TransferType type;
+ uint32_t id;
+
+ assert(msg);
+ assert(m);
+
+ r = bus_verify_polkit_async(
+ msg,
+ CAP_SYS_ADMIN,
+ "org.freedesktop.import1.export",
+ false,
+ UID_INVALID,
+ &m->polkit_registry,
+ error);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return 1; /* Will call us back */
+
+ r = sd_bus_message_read(msg, "shs", &local, &fd, &format);
+ if (r < 0)
+ return r;
+
+ if (!machine_name_is_valid(local))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Local name %s is invalid", local);
+
+ type = streq_ptr(sd_bus_message_get_member(msg), "ExportTar") ? TRANSFER_EXPORT_TAR : TRANSFER_EXPORT_RAW;
+
+ r = transfer_new(m, &t);
+ if (r < 0)
+ return r;
+
+ t->type = type;
+
+ if (!isempty(format)) {
+ t->format = strdup(format);
+ if (!t->format)
+ return -ENOMEM;
+ }
+
+ t->local = strdup(local);
+ if (!t->local)
+ return -ENOMEM;
+
+ t->stdout_fd = fcntl(fd, F_DUPFD_CLOEXEC, 3);
+ if (t->stdout_fd < 0)
+ return -errno;
+
+ r = transfer_start(t);
+ if (r < 0)
+ return r;
+
+ object = t->object_path;
+ id = t->id;
+ t = NULL;
+
+ return sd_bus_reply_method_return(msg, "uo", id, object);
+}
+
+static int method_pull_tar_or_raw(sd_bus_message *msg, void *userdata, sd_bus_error *error) {
_cleanup_(transfer_unrefp) Transfer *t = NULL;
const char *remote, *local, *verify, *object;
Manager *m = userdata;
@@ -652,7 +857,6 @@ static int method_pull_tar_or_raw(sd_bus *bus, sd_bus_message *msg, void *userda
int force, r;
uint32_t id;
- assert(bus);
assert(msg);
assert(m);
@@ -661,6 +865,7 @@ static int method_pull_tar_or_raw(sd_bus *bus, sd_bus_message *msg, void *userda
CAP_SYS_ADMIN,
"org.freedesktop.import1.pull",
false,
+ UID_INVALID,
&m->polkit_registry,
error);
if (r < 0)
@@ -687,7 +892,11 @@ static int method_pull_tar_or_raw(sd_bus *bus, sd_bus_message *msg, void *userda
if (v < 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown verification mode %s", verify);
- type = streq_ptr(sd_bus_message_get_member(msg), "PullTar") ? TRANSFER_TAR : TRANSFER_RAW;
+ r = setup_machine_directory((uint64_t) -1, error);
+ if (r < 0)
+ return r;
+
+ type = streq_ptr(sd_bus_message_get_member(msg), "PullTar") ? TRANSFER_PULL_TAR : TRANSFER_PULL_RAW;
if (manager_find(m, type, NULL, remote))
return sd_bus_error_setf(error, BUS_ERROR_TRANSFER_IN_PROGRESS, "Transfer for %s already in progress.", remote);
@@ -704,9 +913,11 @@ static int method_pull_tar_or_raw(sd_bus *bus, sd_bus_message *msg, void *userda
if (!t->remote)
return -ENOMEM;
- t->local = strdup(local);
- if (!t->local)
- return -ENOMEM;
+ if (local) {
+ t->local = strdup(local);
+ if (!t->local)
+ return -ENOMEM;
+ }
r = transfer_start(t);
if (r < 0)
@@ -719,7 +930,7 @@ static int method_pull_tar_or_raw(sd_bus *bus, sd_bus_message *msg, void *userda
return sd_bus_reply_method_return(msg, "uo", id, object);
}
-static int method_pull_dkr(sd_bus *bus, sd_bus_message *msg, void *userdata, sd_bus_error *error) {
+static int method_pull_dkr(sd_bus_message *msg, void *userdata, sd_bus_error *error) {
_cleanup_(transfer_unrefp) Transfer *t = NULL;
const char *index_url, *remote, *tag, *local, *verify, *object;
Manager *m = userdata;
@@ -727,7 +938,6 @@ static int method_pull_dkr(sd_bus *bus, sd_bus_message *msg, void *userdata, sd_
int force, r;
uint32_t id;
- assert(bus);
assert(msg);
assert(m);
@@ -736,6 +946,7 @@ static int method_pull_dkr(sd_bus *bus, sd_bus_message *msg, void *userdata, sd_
CAP_SYS_ADMIN,
"org.freedesktop.import1.pull",
false,
+ UID_INVALID,
&m->polkit_registry,
error);
if (r < 0)
@@ -777,14 +988,18 @@ static int method_pull_dkr(sd_bus *bus, sd_bus_message *msg, void *userdata, sd_
if (v != IMPORT_VERIFY_NO)
return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "DKR does not support verification.");
- if (manager_find(m, TRANSFER_DKR, index_url, remote))
+ r = setup_machine_directory((uint64_t) -1, error);
+ if (r < 0)
+ return r;
+
+ if (manager_find(m, TRANSFER_PULL_DKR, index_url, remote))
return sd_bus_error_setf(error, BUS_ERROR_TRANSFER_IN_PROGRESS, "Transfer for %s already in progress.", remote);
r = transfer_new(m, &t);
if (r < 0)
return r;
- t->type = TRANSFER_DKR;
+ t->type = TRANSFER_PULL_DKR;
t->verify = v;
t->force_local = force;
@@ -796,9 +1011,11 @@ static int method_pull_dkr(sd_bus *bus, sd_bus_message *msg, void *userdata, sd_
if (!t->remote)
return -ENOMEM;
- t->local = strdup(local);
- if (!t->local)
- return -ENOMEM;
+ if (local) {
+ t->local = strdup(local);
+ if (!t->local)
+ return -ENOMEM;
+ }
r = transfer_start(t);
if (r < 0)
@@ -811,14 +1028,13 @@ static int method_pull_dkr(sd_bus *bus, sd_bus_message *msg, void *userdata, sd_
return sd_bus_reply_method_return(msg, "uo", id, object);
}
-static int method_list_transfers(sd_bus *bus, sd_bus_message *msg, void *userdata, sd_bus_error *error) {
+static int method_list_transfers(sd_bus_message *msg, void *userdata, sd_bus_error *error) {
_cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
Manager *m = userdata;
Transfer *t;
Iterator i;
int r;
- assert(bus);
assert(msg);
assert(m);
@@ -849,14 +1065,13 @@ static int method_list_transfers(sd_bus *bus, sd_bus_message *msg, void *userdat
if (r < 0)
return r;
- return sd_bus_send(bus, reply, NULL);
+ return sd_bus_send(NULL, reply, NULL);
}
-static int method_cancel(sd_bus *bus, sd_bus_message *msg, void *userdata, sd_bus_error *error) {
+static int method_cancel(sd_bus_message *msg, void *userdata, sd_bus_error *error) {
Transfer *t = userdata;
int r;
- assert(bus);
assert(msg);
assert(t);
@@ -865,6 +1080,7 @@ static int method_cancel(sd_bus *bus, sd_bus_message *msg, void *userdata, sd_bu
CAP_SYS_ADMIN,
"org.freedesktop.import1.pull",
false,
+ UID_INVALID,
&t->manager->polkit_registry,
error);
if (r < 0)
@@ -879,13 +1095,12 @@ static int method_cancel(sd_bus *bus, sd_bus_message *msg, void *userdata, sd_bu
return sd_bus_reply_method_return(msg, NULL);
}
-static int method_cancel_transfer(sd_bus *bus, sd_bus_message *msg, void *userdata, sd_bus_error *error) {
+static int method_cancel_transfer(sd_bus_message *msg, void *userdata, sd_bus_error *error) {
Manager *m = userdata;
Transfer *t;
uint32_t id;
int r;
- assert(bus);
assert(msg);
assert(m);
@@ -894,6 +1109,7 @@ static int method_cancel_transfer(sd_bus *bus, sd_bus_message *msg, void *userda
CAP_SYS_ADMIN,
"org.freedesktop.import1.pull",
false,
+ UID_INVALID,
&m->polkit_registry,
error);
if (r < 0)
@@ -954,6 +1170,10 @@ static const sd_bus_vtable transfer_vtable[] = {
static const sd_bus_vtable manager_vtable[] = {
SD_BUS_VTABLE_START(0),
+ SD_BUS_METHOD("ImportTar", "hsbb", "uo", method_import_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("ImportRaw", "hsbb", "uo", method_import_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("ExportTar", "shs", "uo", method_export_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("ExportRaw", "shs", "uo", method_export_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("PullTar", "sssb", "uo", method_pull_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("PullRaw", "sssb", "uo", method_pull_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("PullDkr", "sssssb", "uo", method_pull_dkr, SD_BUS_VTABLE_UNPRIVILEGED),
diff --git a/src/import/org.freedesktop.import1.policy.in b/src/import/org.freedesktop.import1.policy.in
index 1003f46447..85924ed743 100644
--- a/src/import/org.freedesktop.import1.policy.in
+++ b/src/import/org.freedesktop.import1.policy.in
@@ -16,6 +16,26 @@
<vendor>The systemd Project</vendor>
<vendor_url>http://www.freedesktop.org/wiki/Software/systemd</vendor_url>
+ <action id="org.freedesktop.import1.import">
+ <_description>Import a VM or container image</_description>
+ <_message>Authentication is required to import a VM or container image</_message>
+ <defaults>
+ <allow_any>auth_admin</allow_any>
+ <allow_inactive>auth_admin</allow_inactive>
+ <allow_active>auth_admin_keep</allow_active>
+ </defaults>
+ </action>
+
+ <action id="org.freedesktop.import1.export">
+ <_description>Export a VM or container image</_description>
+ <_message>Authentication is required to export a VM or container image</_message>
+ <defaults>
+ <allow_any>auth_admin</allow_any>
+ <allow_inactive>auth_admin</allow_inactive>
+ <allow_active>auth_admin_keep</allow_active>
+ </defaults>
+ </action>
+
<action id="org.freedesktop.import1.pull">
<_description>Download a VM or container image</_description>
<_message>Authentication is required to download a VM or container image</_message>
diff --git a/src/import/pull-common.c b/src/import/pull-common.c
new file mode 100644
index 0000000000..d2588d4fa0
--- /dev/null
+++ b/src/import/pull-common.c
@@ -0,0 +1,425 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2015 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/prctl.h>
+
+#include "util.h"
+#include "strv.h"
+#include "copy.h"
+#include "rm-rf.h"
+#include "btrfs-util.h"
+#include "capability.h"
+#include "pull-job.h"
+#include "pull-common.h"
+#include "process-util.h"
+#include "signal-util.h"
+
+#define FILENAME_ESCAPE "/.#\"\'"
+
+int pull_find_old_etags(const char *url, const char *image_root, int dt, const char *prefix, const char *suffix, char ***etags) {
+ _cleanup_free_ char *escaped_url = NULL;
+ _cleanup_closedir_ DIR *d = NULL;
+ _cleanup_strv_free_ char **l = NULL;
+ struct dirent *de;
+ int r;
+
+ assert(url);
+ assert(etags);
+
+ if (!image_root)
+ image_root = "/var/lib/machines";
+
+ escaped_url = xescape(url, FILENAME_ESCAPE);
+ if (!escaped_url)
+ return -ENOMEM;
+
+ d = opendir(image_root);
+ if (!d) {
+ if (errno == ENOENT) {
+ *etags = NULL;
+ return 0;
+ }
+
+ return -errno;
+ }
+
+ FOREACH_DIRENT_ALL(de, d, return -errno) {
+ const char *a, *b;
+ char *u;
+
+ if (de->d_type != DT_UNKNOWN &&
+ de->d_type != dt)
+ continue;
+
+ if (prefix) {
+ a = startswith(de->d_name, prefix);
+ if (!a)
+ continue;
+ } else
+ a = de->d_name;
+
+ a = startswith(a, escaped_url);
+ if (!a)
+ continue;
+
+ a = startswith(a, ".");
+ if (!a)
+ continue;
+
+ if (suffix) {
+ b = endswith(de->d_name, suffix);
+ if (!b)
+ continue;
+ } else
+ b = strchr(de->d_name, 0);
+
+ if (a >= b)
+ continue;
+
+ r = cunescape_length(a, b - a, 0, &u);
+ if (r < 0)
+ return r;
+
+ if (!http_etag_is_valid(u)) {
+ free(u);
+ continue;
+ }
+
+ r = strv_consume(&l, u);
+ if (r < 0)
+ return r;
+ }
+
+ *etags = l;
+ l = NULL;
+
+ return 0;
+}
+
+int pull_make_local_copy(const char *final, const char *image_root, const char *local, bool force_local) {
+ const char *p;
+ int r;
+
+ assert(final);
+ assert(local);
+
+ if (!image_root)
+ image_root = "/var/lib/machines";
+
+ p = strjoina(image_root, "/", local);
+
+ if (force_local)
+ (void) rm_rf(p, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME);
+
+ r = btrfs_subvol_snapshot(final, p, 0);
+ if (r == -ENOTTY) {
+ r = copy_tree(final, p, false);
+ if (r < 0)
+ return log_error_errno(r, "Failed to copy image: %m");
+ } else if (r < 0)
+ return log_error_errno(r, "Failed to create local image: %m");
+
+ log_info("Created new local image '%s'.", local);
+
+ return 0;
+}
+
+int pull_make_path(const char *url, const char *etag, const char *image_root, const char *prefix, const char *suffix, char **ret) {
+ _cleanup_free_ char *escaped_url = NULL;
+ char *path;
+
+ assert(url);
+ assert(ret);
+
+ if (!image_root)
+ image_root = "/var/lib/machines";
+
+ escaped_url = xescape(url, FILENAME_ESCAPE);
+ if (!escaped_url)
+ return -ENOMEM;
+
+ if (etag) {
+ _cleanup_free_ char *escaped_etag = NULL;
+
+ escaped_etag = xescape(etag, FILENAME_ESCAPE);
+ if (!escaped_etag)
+ return -ENOMEM;
+
+ path = strjoin(image_root, "/", strempty(prefix), escaped_url, ".", escaped_etag, strempty(suffix), NULL);
+ } else
+ path = strjoin(image_root, "/", strempty(prefix), escaped_url, strempty(suffix), NULL);
+ if (!path)
+ return -ENOMEM;
+
+ *ret = path;
+ return 0;
+}
+
+int pull_make_verification_jobs(
+ PullJob **ret_checksum_job,
+ PullJob **ret_signature_job,
+ ImportVerify verify,
+ const char *url,
+ CurlGlue *glue,
+ PullJobFinished on_finished,
+ void *userdata) {
+
+ _cleanup_(pull_job_unrefp) PullJob *checksum_job = NULL, *signature_job = NULL;
+ int r;
+
+ assert(ret_checksum_job);
+ assert(ret_signature_job);
+ assert(verify >= 0);
+ assert(verify < _IMPORT_VERIFY_MAX);
+ assert(url);
+ assert(glue);
+
+ if (verify != IMPORT_VERIFY_NO) {
+ _cleanup_free_ char *checksum_url = NULL;
+
+ /* Queue job for the SHA256SUMS file for the image */
+ r = import_url_change_last_component(url, "SHA256SUMS", &checksum_url);
+ if (r < 0)
+ return r;
+
+ r = pull_job_new(&checksum_job, checksum_url, glue, userdata);
+ if (r < 0)
+ return r;
+
+ checksum_job->on_finished = on_finished;
+ checksum_job->uncompressed_max = checksum_job->compressed_max = 1ULL * 1024ULL * 1024ULL;
+ }
+
+ if (verify == IMPORT_VERIFY_SIGNATURE) {
+ _cleanup_free_ char *signature_url = NULL;
+
+ /* Queue job for the SHA256SUMS.gpg file for the image. */
+ r = import_url_change_last_component(url, "SHA256SUMS.gpg", &signature_url);
+ if (r < 0)
+ return r;
+
+ r = pull_job_new(&signature_job, signature_url, glue, userdata);
+ if (r < 0)
+ return r;
+
+ signature_job->on_finished = on_finished;
+ signature_job->uncompressed_max = signature_job->compressed_max = 1ULL * 1024ULL * 1024ULL;
+ }
+
+ *ret_checksum_job = checksum_job;
+ *ret_signature_job = signature_job;
+
+ checksum_job = signature_job = NULL;
+
+ return 0;
+}
+
+int pull_verify(
+ PullJob *main_job,
+ PullJob *checksum_job,
+ PullJob *signature_job) {
+
+ _cleanup_close_pair_ int gpg_pipe[2] = { -1, -1 };
+ _cleanup_free_ char *fn = NULL;
+ _cleanup_close_ int sig_file = -1;
+ const char *p, *line;
+ char sig_file_path[] = "/tmp/sigXXXXXX", gpg_home[] = "/tmp/gpghomeXXXXXX";
+ _cleanup_sigkill_wait_ pid_t pid = 0;
+ bool gpg_home_created = false;
+ int r;
+
+ assert(main_job);
+ assert(main_job->state == PULL_JOB_DONE);
+
+ if (!checksum_job)
+ return 0;
+
+ assert(main_job->calc_checksum);
+ assert(main_job->checksum);
+ assert(checksum_job->state == PULL_JOB_DONE);
+
+ if (!checksum_job->payload || checksum_job->payload_size <= 0) {
+ log_error("Checksum is empty, cannot verify.");
+ return -EBADMSG;
+ }
+
+ r = import_url_last_component(main_job->url, &fn);
+ if (r < 0)
+ return log_oom();
+
+ if (!filename_is_valid(fn)) {
+ log_error("Cannot verify checksum, could not determine valid server-side file name.");
+ return -EBADMSG;
+ }
+
+ line = strjoina(main_job->checksum, " *", fn, "\n");
+
+ p = memmem(checksum_job->payload,
+ checksum_job->payload_size,
+ line,
+ strlen(line));
+
+ if (!p || (p != (char*) checksum_job->payload && p[-1] != '\n')) {
+ log_error("Checksum did not check out, payload has been tempered with.");
+ return -EBADMSG;
+ }
+
+ log_info("SHA256 checksum of %s is valid.", main_job->url);
+
+ if (!signature_job)
+ return 0;
+
+ assert(signature_job->state == PULL_JOB_DONE);
+
+ if (!signature_job->payload || signature_job->payload_size <= 0) {
+ log_error("Signature is empty, cannot verify.");
+ return -EBADMSG;
+ }
+
+ r = pipe2(gpg_pipe, O_CLOEXEC);
+ if (r < 0)
+ return log_error_errno(errno, "Failed to create pipe for gpg: %m");
+
+ sig_file = mkostemp(sig_file_path, O_RDWR);
+ if (sig_file < 0)
+ return log_error_errno(errno, "Failed to create temporary file: %m");
+
+ r = loop_write(sig_file, signature_job->payload, signature_job->payload_size, false);
+ if (r < 0) {
+ log_error_errno(r, "Failed to write to temporary file: %m");
+ goto finish;
+ }
+
+ if (!mkdtemp(gpg_home)) {
+ r = log_error_errno(errno, "Failed to create tempory home for gpg: %m");
+ goto finish;
+ }
+
+ gpg_home_created = true;
+
+ pid = fork();
+ if (pid < 0)
+ return log_error_errno(errno, "Failed to fork off gpg: %m");
+ if (pid == 0) {
+ const char *cmd[] = {
+ "gpg",
+ "--no-options",
+ "--no-default-keyring",
+ "--no-auto-key-locate",
+ "--no-auto-check-trustdb",
+ "--batch",
+ "--trust-model=always",
+ NULL, /* --homedir= */
+ NULL, /* --keyring= */
+ NULL, /* --verify */
+ NULL, /* signature file */
+ NULL, /* dash */
+ NULL /* trailing NULL */
+ };
+ unsigned k = ELEMENTSOF(cmd) - 6;
+ int null_fd;
+
+ /* Child */
+
+ reset_all_signal_handlers();
+ reset_signal_mask();
+ assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
+
+ gpg_pipe[1] = safe_close(gpg_pipe[1]);
+
+ if (dup2(gpg_pipe[0], STDIN_FILENO) != STDIN_FILENO) {
+ log_error_errno(errno, "Failed to dup2() fd: %m");
+ _exit(EXIT_FAILURE);
+ }
+
+ if (gpg_pipe[0] != STDIN_FILENO)
+ gpg_pipe[0] = safe_close(gpg_pipe[0]);
+
+ null_fd = open("/dev/null", O_WRONLY|O_NOCTTY);
+ if (null_fd < 0) {
+ log_error_errno(errno, "Failed to open /dev/null: %m");
+ _exit(EXIT_FAILURE);
+ }
+
+ if (dup2(null_fd, STDOUT_FILENO) != STDOUT_FILENO) {
+ log_error_errno(errno, "Failed to dup2() fd: %m");
+ _exit(EXIT_FAILURE);
+ }
+
+ if (null_fd != STDOUT_FILENO)
+ null_fd = safe_close(null_fd);
+
+ cmd[k++] = strjoina("--homedir=", gpg_home);
+
+ /* We add the user keyring only to the command line
+ * arguments, if it's around since gpg fails
+ * otherwise. */
+ if (access(USER_KEYRING_PATH, F_OK) >= 0)
+ cmd[k++] = "--keyring=" USER_KEYRING_PATH;
+ else
+ cmd[k++] = "--keyring=" VENDOR_KEYRING_PATH;
+
+ cmd[k++] = "--verify";
+ cmd[k++] = sig_file_path;
+ cmd[k++] = "-";
+ cmd[k++] = NULL;
+
+ fd_cloexec(STDIN_FILENO, false);
+ fd_cloexec(STDOUT_FILENO, false);
+ fd_cloexec(STDERR_FILENO, false);
+
+ execvp("gpg2", (char * const *) cmd);
+ execvp("gpg", (char * const *) cmd);
+ log_error_errno(errno, "Failed to execute gpg: %m");
+ _exit(EXIT_FAILURE);
+ }
+
+ gpg_pipe[0] = safe_close(gpg_pipe[0]);
+
+ r = loop_write(gpg_pipe[1], checksum_job->payload, checksum_job->payload_size, false);
+ if (r < 0) {
+ log_error_errno(r, "Failed to write to pipe: %m");
+ goto finish;
+ }
+
+ gpg_pipe[1] = safe_close(gpg_pipe[1]);
+
+ r = wait_for_terminate_and_warn("gpg", pid, true);
+ pid = 0;
+ if (r < 0)
+ goto finish;
+ if (r > 0) {
+ log_error("Signature verification failed.");
+ r = -EBADMSG;
+ } else {
+ log_info("Signature verification succeeded.");
+ r = 0;
+ }
+
+finish:
+ if (sig_file >= 0)
+ unlink(sig_file_path);
+
+ if (gpg_home_created)
+ (void) rm_rf(gpg_home, REMOVE_ROOT|REMOVE_PHYSICAL);
+
+ return r;
+}
diff --git a/src/import/pull-common.h b/src/import/pull-common.h
new file mode 100644
index 0000000000..bb9cf3efc1
--- /dev/null
+++ b/src/import/pull-common.h
@@ -0,0 +1,36 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2015 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 "pull-job.h"
+#include "import-util.h"
+
+int pull_make_local_copy(const char *final, const char *root, const char *local, bool force_local);
+
+int pull_find_old_etags(const char *url, const char *root, int dt, const char *prefix, const char *suffix, char ***etags);
+
+int pull_make_path(const char *url, const char *etag, const char *image_root, const char *prefix, const char *suffix, char **ret);
+
+int pull_make_verification_jobs(PullJob **ret_checksum_job, PullJob **ret_signature_job, ImportVerify verify, const char *url, CurlGlue *glue, PullJobFinished on_finished, void *userdata);
+int pull_verify(PullJob *main_job, PullJob *checksum_job, PullJob *signature_job);
diff --git a/src/import/import-dkr.c b/src/import/pull-dkr.c
index fb72f6cee3..d7476dc340 100644
--- a/src/import/import-dkr.c
+++ b/src/import/pull-dkr.c
@@ -28,12 +28,17 @@
#include "btrfs-util.h"
#include "utf8.h"
#include "mkdir.h"
+#include "rm-rf.h"
+#include "path-util.h"
#include "import-util.h"
#include "curl-util.h"
#include "aufs-util.h"
-#include "import-job.h"
+#include "pull-job.h"
+#include "pull-common.h"
#include "import-common.h"
-#include "import-dkr.h"
+#include "pull-dkr.h"
+#include "process-util.h"
+#include "hostname-util.h"
typedef enum DkrProgress {
DKR_SEARCHING,
@@ -43,23 +48,27 @@ typedef enum DkrProgress {
DKR_COPYING,
} DkrProgress;
-struct DkrImport {
+struct DkrPull {
sd_event *event;
CurlGlue *glue;
+ char *index_protocol;
+ char *index_address;
+
char *index_url;
char *image_root;
- ImportJob *images_job;
- ImportJob *tags_job;
- ImportJob *ancestry_job;
- ImportJob *json_job;
- ImportJob *layer_job;
+ PullJob *images_job;
+ PullJob *tags_job;
+ PullJob *ancestry_job;
+ PullJob *json_job;
+ PullJob *layer_job;
char *name;
- char *tag;
+ char *reference;
char *id;
+ char *response_digest;
char *response_token;
char **response_registries;
@@ -67,11 +76,12 @@ struct DkrImport {
unsigned n_ancestry;
unsigned current_ancestry;
- DkrImportFinished on_finished;
+ DkrPullFinished on_finished;
void *userdata;
char *local;
bool force_local;
+ bool grow_machine_directory;
char *temp_path;
char *final_path;
@@ -82,13 +92,13 @@ struct DkrImport {
#define PROTOCOL_PREFIX "https://"
#define HEADER_TOKEN "X-Do" /* the HTTP header for the auth token */ "cker-Token:"
-#define HEADER_REGISTRY "X-Do" /*the HTTP header for the registry */ "cker-Endpoints:"
-
-#define LAYERS_MAX 2048
+#define HEADER_REGISTRY "X-Do" /* the HTTP header for the registry */ "cker-Endpoints:"
+#define HEADER_DIGEST "Do" /* the HTTP header for the manifest digest */ "cker-Content-Digest:"
+#define LAYERS_MAX 127
-static void dkr_import_job_on_finished(ImportJob *j);
+static void dkr_pull_job_on_finished(PullJob *j);
-DkrImport* dkr_import_unref(DkrImport *i) {
+DkrPull* dkr_pull_unref(DkrPull *i) {
if (!i)
return NULL;
@@ -97,28 +107,28 @@ DkrImport* dkr_import_unref(DkrImport *i) {
(void) wait_for_terminate(i->tar_pid, NULL);
}
- import_job_unref(i->images_job);
- import_job_unref(i->tags_job);
- import_job_unref(i->ancestry_job);
- import_job_unref(i->json_job);
- import_job_unref(i->layer_job);
+ pull_job_unref(i->images_job);
+ pull_job_unref(i->tags_job);
+ pull_job_unref(i->ancestry_job);
+ pull_job_unref(i->json_job);
+ pull_job_unref(i->layer_job);
curl_glue_unref(i->glue);
sd_event_unref(i->event);
if (i->temp_path) {
- (void) btrfs_subvol_remove(i->temp_path);
- (void) rm_rf_dangerous(i->temp_path, false, true, false);
+ (void) rm_rf(i->temp_path, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME);
free(i->temp_path);
}
free(i->name);
- free(i->tag);
+ free(i->reference);
free(i->id);
free(i->response_token);
- free(i->response_registries);
strv_free(i->ancestry);
free(i->final_path);
+ free(i->index_address);
+ free(i->index_protocol);
free(i->index_url);
free(i->image_root);
free(i->local);
@@ -127,15 +137,15 @@ DkrImport* dkr_import_unref(DkrImport *i) {
return NULL;
}
-int dkr_import_new(
- DkrImport **ret,
+int dkr_pull_new(
+ DkrPull **ret,
sd_event *event,
const char *index_url,
const char *image_root,
- DkrImportFinished on_finished,
+ DkrPullFinished on_finished,
void *userdata) {
- _cleanup_(dkr_import_unrefp) DkrImport *i = NULL;
+ _cleanup_(dkr_pull_unrefp) DkrPull *i = NULL;
char *e;
int r;
@@ -145,7 +155,7 @@ int dkr_import_new(
if (!http_url_is_valid(index_url))
return -EINVAL;
- i = new0(DkrImport, 1);
+ i = new0(DkrPull, 1);
if (!i)
return -ENOMEM;
@@ -156,6 +166,8 @@ int dkr_import_new(
if (!i->image_root)
return -ENOMEM;
+ i->grow_machine_directory = path_startswith(i->image_root, "/var/lib/machines");
+
i->index_url = strdup(index_url);
if (!i->index_url)
return -ENOMEM;
@@ -176,7 +188,7 @@ int dkr_import_new(
if (r < 0)
return r;
- i->glue->on_finished = import_job_curl_on_finished;
+ i->glue->on_finished = pull_job_curl_on_finished;
i->glue->userdata = i;
*ret = i;
@@ -185,7 +197,7 @@ int dkr_import_new(
return 0;
}
-static void dkr_import_report_progress(DkrImport *i, DkrProgress p) {
+static void dkr_pull_report_progress(DkrPull *i, DkrProgress p) {
unsigned percent;
assert(i);
@@ -371,7 +383,7 @@ static int parse_ancestry(const void *payload, size_t size, char ***ret) {
}
}
-static const char *dkr_import_current_layer(DkrImport *i) {
+static const char *dkr_pull_current_layer(DkrPull *i) {
assert(i);
if (strv_isempty(i->ancestry))
@@ -380,7 +392,7 @@ static const char *dkr_import_current_layer(DkrImport *i) {
return i->ancestry[i->current_ancestry];
}
-static const char *dkr_import_current_base_layer(DkrImport *i) {
+static const char *dkr_pull_current_base_layer(DkrPull *i) {
assert(i);
if (strv_isempty(i->ancestry))
@@ -392,7 +404,7 @@ static const char *dkr_import_current_base_layer(DkrImport *i) {
return i->ancestry[i->current_ancestry-1];
}
-static int dkr_import_add_token(DkrImport *i, ImportJob *j) {
+static int dkr_pull_add_token(DkrPull *i, PullJob *j) {
const char *t;
assert(i);
@@ -410,33 +422,51 @@ static int dkr_import_add_token(DkrImport *i, ImportJob *j) {
return 0;
}
-static bool dkr_import_is_done(DkrImport *i) {
+static int dkr_pull_add_bearer_token(DkrPull *i, PullJob *j) {
+ const char *t = NULL;
+
assert(i);
- assert(i->images_job);
+ assert(j);
- if (i->images_job->state != IMPORT_JOB_DONE)
+ if (i->response_token)
+ t = strjoina("Authorization: Bearer ", i->response_token);
+ else
+ return -EINVAL;
+
+ j->request_header = curl_slist_new("Accept: application/json", t, NULL);
+ if (!j->request_header)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static bool dkr_pull_is_done(DkrPull *i) {
+ assert(i);
+ assert(i->images_job);
+ if (i->images_job->state != PULL_JOB_DONE)
return false;
- if (!i->tags_job || i->tags_job->state != IMPORT_JOB_DONE)
+ if (!i->tags_job || i->tags_job->state != PULL_JOB_DONE)
return false;
- if (!i->ancestry_job || i->ancestry_job->state != IMPORT_JOB_DONE)
+ if (!i->ancestry_job || i->ancestry_job->state != PULL_JOB_DONE)
return false;
- if (!i->json_job || i->json_job->state != IMPORT_JOB_DONE)
+ if (i->json_job && i->json_job->state != PULL_JOB_DONE)
return false;
- if (i->layer_job && i->layer_job->state != IMPORT_JOB_DONE)
+ if (i->layer_job && i->layer_job->state != PULL_JOB_DONE)
return false;
- if (dkr_import_current_layer(i))
+ if (dkr_pull_current_layer(i))
return false;
return true;
}
-static int dkr_import_make_local_copy(DkrImport *i) {
+static int dkr_pull_make_local_copy(DkrPull *i, DkrPullVersion version) {
int r;
+ _cleanup_free_ char *p = NULL;
assert(i);
@@ -449,16 +479,36 @@ static int dkr_import_make_local_copy(DkrImport *i) {
return log_oom();
}
- r = import_make_local_copy(i->final_path, i->image_root, i->local, i->force_local);
+ if (version == DKR_PULL_V2) {
+ r = path_get_parent(i->image_root, &p);
+ if (r < 0)
+ return r;
+ }
+
+ r = pull_make_local_copy(i->final_path, p ?: i->image_root, i->local, i->force_local);
if (r < 0)
return r;
+ if (version == DKR_PULL_V2) {
+ char **k = NULL;
+ STRV_FOREACH(k, i->ancestry) {
+ _cleanup_free_ char *d = strjoin(i->image_root, "/.dkr-", *k, NULL);
+ r = btrfs_subvol_remove(d, false);
+ if (r < 0)
+ return r;
+ }
+
+ r = rmdir(i->image_root);
+ if (r < 0)
+ return r;
+ }
+
return 0;
}
-static int dkr_import_job_on_open_disk(ImportJob *j) {
+static int dkr_pull_job_on_open_disk(PullJob *j) {
const char *base;
- DkrImport *i;
+ DkrPull *i;
int r;
assert(j);
@@ -476,33 +526,33 @@ static int dkr_import_job_on_open_disk(ImportJob *j) {
mkdir_parents_label(i->temp_path, 0700);
- base = dkr_import_current_base_layer(i);
+ base = dkr_pull_current_base_layer(i);
if (base) {
const char *base_path;
base_path = strjoina(i->image_root, "/.dkr-", base);
- r = btrfs_subvol_snapshot(base_path, i->temp_path, false, true);
+ r = btrfs_subvol_snapshot(base_path, i->temp_path, BTRFS_SNAPSHOT_FALLBACK_COPY);
} else
r = btrfs_subvol_make(i->temp_path);
if (r < 0)
return log_error_errno(r, "Failed to make btrfs subvolume %s: %m", i->temp_path);
- j->disk_fd = import_fork_tar(i->temp_path, &i->tar_pid);
+ j->disk_fd = import_fork_tar_x(i->temp_path, &i->tar_pid);
if (j->disk_fd < 0)
return j->disk_fd;
return 0;
}
-static void dkr_import_job_on_progress(ImportJob *j) {
- DkrImport *i;
+static void dkr_pull_job_on_progress(PullJob *j) {
+ DkrPull *i;
assert(j);
assert(j->userdata);
i = j->userdata;
- dkr_import_report_progress(
+ dkr_pull_report_progress(
i,
j == i->images_job ? DKR_SEARCHING :
j == i->tags_job ? DKR_RESOLVING :
@@ -510,7 +560,9 @@ static void dkr_import_job_on_progress(ImportJob *j) {
DKR_DOWNLOADING);
}
-static int dkr_import_pull_layer(DkrImport *i) {
+static void dkr_pull_job_on_finished_v2(PullJob *j);
+
+static int dkr_pull_pull_layer_v2(DkrPull *i) {
_cleanup_free_ char *path = NULL;
const char *url, *layer = NULL;
int r;
@@ -521,7 +573,67 @@ static int dkr_import_pull_layer(DkrImport *i) {
assert(!i->final_path);
for (;;) {
- layer = dkr_import_current_layer(i);
+ layer = dkr_pull_current_layer(i);
+ if (!layer)
+ return 0; /* no more layers */
+
+ path = strjoin(i->image_root, "/.dkr-", layer, NULL);
+ if (!path)
+ return log_oom();
+
+ if (laccess(path, F_OK) < 0) {
+ if (errno == ENOENT)
+ break;
+
+ return log_error_errno(errno, "Failed to check for container: %m");
+ }
+
+ log_info("Layer %s already exists, skipping.", layer);
+
+ i->current_ancestry++;
+
+ free(path);
+ path = NULL;
+ }
+
+ log_info("Pulling layer %s...", layer);
+
+ i->final_path = path;
+ path = NULL;
+
+ url = strjoina(PROTOCOL_PREFIX, i->response_registries[0], "/v2/", i->name, "/blobs/", layer);
+ r = pull_job_new(&i->layer_job, url, i->glue, i);
+ if (r < 0)
+ return log_error_errno(r, "Failed to allocate layer job: %m");
+
+ r = dkr_pull_add_bearer_token(i, i->layer_job);
+ if (r < 0)
+ return log_oom();
+
+ i->layer_job->on_finished = dkr_pull_job_on_finished_v2;
+ i->layer_job->on_open_disk = dkr_pull_job_on_open_disk;
+ i->layer_job->on_progress = dkr_pull_job_on_progress;
+ i->layer_job->grow_machine_directory = i->grow_machine_directory;
+
+ r = pull_job_begin(i->layer_job);
+ if (r < 0)
+ return log_error_errno(r, "Failed to start layer job: %m");
+
+ return 0;
+}
+
+static int dkr_pull_pull_layer(DkrPull *i) {
+ _cleanup_free_ char *path = NULL;
+ const char *url, *layer = NULL;
+ int r;
+
+ assert(i);
+ assert(!i->layer_job);
+ assert(!i->temp_path);
+ assert(!i->final_path);
+
+ for (;;) {
+ layer = dkr_pull_current_layer(i);
if (!layer)
return 0; /* no more layers */
@@ -550,27 +662,364 @@ static int dkr_import_pull_layer(DkrImport *i) {
path = NULL;
url = strjoina(PROTOCOL_PREFIX, i->response_registries[0], "/v1/images/", layer, "/layer");
- r = import_job_new(&i->layer_job, url, i->glue, i);
+ r = pull_job_new(&i->layer_job, url, i->glue, i);
if (r < 0)
return log_error_errno(r, "Failed to allocate layer job: %m");
- r = dkr_import_add_token(i, i->layer_job);
+ r = dkr_pull_add_token(i, i->layer_job);
if (r < 0)
return log_oom();
- i->layer_job->on_finished = dkr_import_job_on_finished;
- i->layer_job->on_open_disk = dkr_import_job_on_open_disk;
- i->layer_job->on_progress = dkr_import_job_on_progress;
+ i->layer_job->on_finished = dkr_pull_job_on_finished;
+ i->layer_job->on_open_disk = dkr_pull_job_on_open_disk;
+ i->layer_job->on_progress = dkr_pull_job_on_progress;
+ i->layer_job->grow_machine_directory = i->grow_machine_directory;
- r = import_job_begin(i->layer_job);
+ r = pull_job_begin(i->layer_job);
if (r < 0)
return log_error_errno(r, "Failed to start layer job: %m");
return 0;
}
-static void dkr_import_job_on_finished(ImportJob *j) {
- DkrImport *i;
+static int dkr_pull_job_on_header(PullJob *j, const char *header, size_t sz) {
+ _cleanup_free_ char *registry = NULL;
+ char *token, *digest;
+ DkrPull *i;
+ int r;
+
+ assert(j);
+ assert(j->userdata);
+
+ i = j->userdata;
+ r = curl_header_strdup(header, sz, HEADER_TOKEN, &token);
+ if (r < 0)
+ return log_oom();
+ if (r > 0) {
+ free(i->response_token);
+ i->response_token = token;
+ return 0;
+ }
+
+ r = curl_header_strdup(header, sz, HEADER_DIGEST, &digest);
+ if (r < 0)
+ return log_oom();
+ if (r > 0) {
+ free(i->response_digest);
+ i->response_digest = digest;
+ return 0;
+ }
+
+ r = curl_header_strdup(header, sz, HEADER_REGISTRY, &registry);
+ if (r < 0)
+ return log_oom();
+ if (r > 0) {
+ char **l, **k;
+
+ l = strv_split(registry, ",");
+ if (!l)
+ return log_oom();
+
+ STRV_FOREACH(k, l) {
+ if (!hostname_is_valid(*k)) {
+ log_error("Registry hostname is not valid.");
+ strv_free(l);
+ return -EBADMSG;
+ }
+ }
+
+ strv_free(i->response_registries);
+ i->response_registries = l;
+ }
+
+ return 0;
+}
+
+static void dkr_pull_job_on_finished_v2(PullJob *j) {
+ DkrPull *i;
+ int r;
+
+ assert(j);
+ assert(j->userdata);
+
+ i = j->userdata;
+ if (j->error != 0) {
+ if (j == i->images_job)
+ log_error_errno(j->error, "Failed to retrieve images list. (Wrong index URL?)");
+ else if (j == i->ancestry_job)
+ log_error_errno(j->error, "Failed to retrieve manifest.");
+ else if (j == i->json_job)
+ log_error_errno(j->error, "Failed to retrieve json data.");
+ else
+ log_error_errno(j->error, "Failed to retrieve layer data.");
+
+ r = j->error;
+ goto finish;
+ }
+
+ if (i->images_job == j) {
+ const char *url;
+
+ assert(!i->tags_job);
+ assert(!i->ancestry_job);
+ assert(!i->json_job);
+ assert(!i->layer_job);
+
+ if (strv_isempty(i->response_registries)) {
+ r = -EBADMSG;
+ log_error("Didn't get registry information.");
+ goto finish;
+ }
+
+ log_info("Index lookup succeeded, directed to registry %s.", i->response_registries[0]);
+ dkr_pull_report_progress(i, DKR_RESOLVING);
+
+ url = strjoina(i->index_protocol, "auth.", i->index_address, "/v2/token/?scope=repository:",
+ i->name, ":pull&service=registry.", i->index_address);
+ r = pull_job_new(&i->tags_job, url, i->glue, i);
+ if (r < 0) {
+ log_error_errno(r, "Failed to allocate tags job: %m");
+ goto finish;
+ }
+
+ i->tags_job->on_finished = dkr_pull_job_on_finished_v2;
+ i->tags_job->on_progress = dkr_pull_job_on_progress;
+
+ r = pull_job_begin(i->tags_job);
+ if (r < 0) {
+ log_error_errno(r, "Failed to start tags job: %m");
+ goto finish;
+ }
+
+ } else if (i->tags_job == j) {
+ const char *url;
+ _cleanup_free_ const char *buf;
+ _cleanup_json_variant_unref_ JsonVariant *doc = NULL;
+ JsonVariant *e = NULL;
+
+ assert(!i->ancestry_job);
+ assert(!i->json_job);
+ assert(!i->layer_job);
+
+ buf = strndup((const char *)j->payload, j->payload_size);
+ if (!buf) {
+ r = -ENOMEM;
+ log_oom();
+ goto finish;
+ }
+
+ r = json_parse(buf, &doc);
+ if (r < 0) {
+ log_error("Unable to parse bearer token\n%s", j->payload);
+ goto finish;
+ }
+
+ e = json_variant_value(doc, "token");
+ if (!e || e->type != JSON_VARIANT_STRING) {
+ r = -EBADMSG;
+ log_error("Invalid JSON format for Bearer token");
+ goto finish;
+ }
+
+ r = free_and_strdup(&i->response_token, json_variant_string(e));
+ if (r < 0) {
+ log_oom();
+ goto finish;
+ }
+
+ url = strjoina(PROTOCOL_PREFIX, i->response_registries[0], "/v2/", i->name, "/manifests/", i->reference);
+ r = pull_job_new(&i->ancestry_job, url, i->glue, i);
+ if (r < 0) {
+ log_error_errno(r, "Failed to allocate ancestry job: %m");
+ goto finish;
+ }
+
+ r = dkr_pull_add_bearer_token(i, i->ancestry_job);
+ if (r < 0)
+ goto finish;
+
+ i->ancestry_job->on_finished = dkr_pull_job_on_finished_v2;
+ i->ancestry_job->on_progress = dkr_pull_job_on_progress;
+ i->ancestry_job->on_header = dkr_pull_job_on_header;
+
+
+ r = pull_job_begin(i->ancestry_job);
+ if (r < 0) {
+ log_error_errno(r, "Failed to start ancestry job: %m");
+ goto finish;
+ }
+
+ } else if (i->ancestry_job == j) {
+
+ _cleanup_json_variant_unref_ JsonVariant *doc = NULL, *compat = NULL;
+ JsonVariant *e = NULL;
+ _cleanup_strv_free_ char **ancestry = NULL;
+ size_t allocated = 0, size = 0;
+ char *path = NULL, **k = NULL;
+
+ r = json_parse((const char *)j->payload, &doc);
+ if (r < 0) {
+ log_error("Invalid JSON Manifest");
+ goto finish;
+ }
+
+ e = json_variant_value(doc, "fsLayers");
+ if (!e || e->type != JSON_VARIANT_ARRAY || e->size == 0) {
+ r = -EBADMSG;
+ goto finish;
+ }
+
+ log_info("JSON manifest with schema v%"PRIi64" for %s parsed!",
+ json_variant_integer(json_variant_value(doc, "schemaVersion")),
+ json_variant_string(json_variant_value(doc, "name")));
+
+ for (unsigned z = 0; z < e->size; z++) {
+ JsonVariant *f = json_variant_element(e, z), *g = NULL;
+ const char *layer;
+ if (f->type != JSON_VARIANT_OBJECT) {
+ r = -EBADMSG;
+ goto finish;
+ }
+
+ g = json_variant_value(f, "blobSum");
+
+ layer = json_variant_string(g);
+ if (!dkr_digest_is_valid(layer)) {
+ r = -EBADMSG;
+ goto finish;
+ }
+
+ if (!GREEDY_REALLOC(ancestry, allocated, size + 2)) {
+ r = -ENOMEM;
+ log_oom();
+ goto finish;
+ }
+
+ ancestry[size] = strdup(layer);
+ if (!ancestry[size]) {
+ r = -ENOMEM;
+ log_oom();
+ goto finish;
+ }
+
+ ancestry[size+1] = NULL;
+ size += 1;
+ }
+
+ e = json_variant_value(doc, "history");
+ if (!e || e->type != JSON_VARIANT_ARRAY) {
+ r = -EBADMSG;
+ goto finish;
+ }
+
+ e = json_variant_element(e, 0);
+ e = json_variant_value(e, "v1Compatibility");
+ r = json_parse(json_variant_string(e), &compat);
+ if (r < 0) {
+ log_error("Invalid v1Compatibility JSON");
+ goto finish;
+ }
+
+ e = json_variant_value(compat, "id");
+
+ strv_free(i->ancestry);
+ i->ancestry = strv_reverse(strv_uniq(ancestry));
+ i->n_ancestry = strv_length(i->ancestry);
+ i->current_ancestry = 0;
+ i->id = strdup(i->ancestry[i->n_ancestry - 1]);
+ if (!i->id) {
+ r = -ENOMEM;
+ log_oom();
+ goto finish;
+ }
+ path = strjoin(i->image_root, "/.dkr-", json_variant_string(e), NULL);
+ if (!path) {
+ r = -ENOMEM;
+ log_oom();
+ goto finish;
+ }
+ free(i->image_root);
+ i->image_root = path;
+ ancestry = NULL;
+
+ log_info("Required layers:\n");
+ STRV_FOREACH(k, i->ancestry)
+ log_info("\t%s", *k);
+ log_info("\nProvenance:\n\tImageID: %s\n\tDigest: %s", json_variant_string(e), i->response_digest);
+
+ dkr_pull_report_progress(i, DKR_DOWNLOADING);
+
+ r = dkr_pull_pull_layer_v2(i);
+ if (r < 0)
+ goto finish;
+
+ } else if (i->layer_job == j) {
+ assert(i->temp_path);
+ assert(i->final_path);
+
+ j->disk_fd = safe_close(j->disk_fd);
+
+ if (i->tar_pid > 0) {
+ r = wait_for_terminate_and_warn("tar", i->tar_pid, true);
+ i->tar_pid = 0;
+ if (r < 0)
+ goto finish;
+ }
+
+ r = aufs_resolve(i->temp_path);
+ if (r < 0) {
+ log_error_errno(r, "Failed to resolve aufs whiteouts: %m");
+ goto finish;
+ }
+
+ r = btrfs_subvol_set_read_only(i->temp_path, true);
+ if (r < 0) {
+ log_error_errno(r, "Failed to mark snapshot read-only: %m");
+ goto finish;
+ }
+
+ if (rename(i->temp_path, i->final_path) < 0) {
+ log_error_errno(errno, "Failed to rename snaphsot: %m");
+ goto finish;
+ }
+
+ log_info("Completed writing to layer %s.", i->final_path);
+
+ i->layer_job = pull_job_unref(i->layer_job);
+ free(i->temp_path);
+ i->temp_path = NULL;
+ free(i->final_path);
+ i->final_path = NULL;
+
+ i->current_ancestry ++;
+ r = dkr_pull_pull_layer_v2(i);
+ if (r < 0)
+ goto finish;
+
+ } else if (i->json_job != j)
+ assert_not_reached("Got finished event for unknown curl object");
+
+ if (!dkr_pull_is_done(i))
+ return;
+
+ dkr_pull_report_progress(i, DKR_COPYING);
+
+ r = dkr_pull_make_local_copy(i, DKR_PULL_V2);
+ if (r < 0)
+ goto finish;
+
+ r = 0;
+
+finish:
+ if (i->on_finished)
+ i->on_finished(i, r, i->userdata);
+ else
+ sd_event_exit(i->event, r);
+
+}
+
+static void dkr_pull_job_on_finished(PullJob *j) {
+ DkrPull *i;
int r;
assert(j);
@@ -608,25 +1057,25 @@ static void dkr_import_job_on_finished(ImportJob *j) {
}
log_info("Index lookup succeeded, directed to registry %s.", i->response_registries[0]);
- dkr_import_report_progress(i, DKR_RESOLVING);
+ dkr_pull_report_progress(i, DKR_RESOLVING);
- url = strjoina(PROTOCOL_PREFIX, i->response_registries[0], "/v1/repositories/", i->name, "/tags/", i->tag);
- r = import_job_new(&i->tags_job, url, i->glue, i);
+ url = strjoina(PROTOCOL_PREFIX, i->response_registries[0], "/v1/repositories/", i->name, "/tags/", i->reference);
+ r = pull_job_new(&i->tags_job, url, i->glue, i);
if (r < 0) {
log_error_errno(r, "Failed to allocate tags job: %m");
goto finish;
}
- r = dkr_import_add_token(i, i->tags_job);
+ r = dkr_pull_add_token(i, i->tags_job);
if (r < 0) {
log_oom();
goto finish;
}
- i->tags_job->on_finished = dkr_import_job_on_finished;
- i->tags_job->on_progress = dkr_import_job_on_progress;
+ i->tags_job->on_finished = dkr_pull_job_on_finished;
+ i->tags_job->on_progress = dkr_pull_job_on_progress;
- r = import_job_begin(i->tags_job);
+ r = pull_job_begin(i->tags_job);
if (r < 0) {
log_error_errno(r, "Failed to start tags job: %m");
goto finish;
@@ -650,47 +1099,47 @@ static void dkr_import_job_on_finished(ImportJob *j) {
i->id = id;
log_info("Tag lookup succeeded, resolved to layer %s.", i->id);
- dkr_import_report_progress(i, DKR_METADATA);
+ dkr_pull_report_progress(i, DKR_METADATA);
url = strjoina(PROTOCOL_PREFIX, i->response_registries[0], "/v1/images/", i->id, "/ancestry");
- r = import_job_new(&i->ancestry_job, url, i->glue, i);
+ r = pull_job_new(&i->ancestry_job, url, i->glue, i);
if (r < 0) {
log_error_errno(r, "Failed to allocate ancestry job: %m");
goto finish;
}
- r = dkr_import_add_token(i, i->ancestry_job);
+ r = dkr_pull_add_token(i, i->ancestry_job);
if (r < 0) {
log_oom();
goto finish;
}
- i->ancestry_job->on_finished = dkr_import_job_on_finished;
- i->ancestry_job->on_progress = dkr_import_job_on_progress;
+ i->ancestry_job->on_finished = dkr_pull_job_on_finished;
+ i->ancestry_job->on_progress = dkr_pull_job_on_progress;
url = strjoina(PROTOCOL_PREFIX, i->response_registries[0], "/v1/images/", i->id, "/json");
- r = import_job_new(&i->json_job, url, i->glue, i);
+ r = pull_job_new(&i->json_job, url, i->glue, i);
if (r < 0) {
log_error_errno(r, "Failed to allocate json job: %m");
goto finish;
}
- r = dkr_import_add_token(i, i->json_job);
+ r = dkr_pull_add_token(i, i->json_job);
if (r < 0) {
log_oom();
goto finish;
}
- i->json_job->on_finished = dkr_import_job_on_finished;
- i->json_job->on_progress = dkr_import_job_on_progress;
+ i->json_job->on_finished = dkr_pull_job_on_finished;
+ i->json_job->on_progress = dkr_pull_job_on_progress;
- r = import_job_begin(i->ancestry_job);
+ r = pull_job_begin(i->ancestry_job);
if (r < 0) {
log_error_errno(r, "Failed to start ancestry job: %m");
goto finish;
}
- r = import_job_begin(i->json_job);
+ r = pull_job_begin(i->json_job);
if (r < 0) {
log_error_errno(r, "Failed to start json job: %m");
goto finish;
@@ -725,9 +1174,9 @@ static void dkr_import_job_on_finished(ImportJob *j) {
i->n_ancestry = n;
i->current_ancestry = 0;
- dkr_import_report_progress(i, DKR_DOWNLOADING);
+ dkr_pull_report_progress(i, DKR_DOWNLOADING);
- r = dkr_import_pull_layer(i);
+ r = dkr_pull_pull_layer(i);
if (r < 0)
goto finish;
@@ -763,31 +1212,30 @@ static void dkr_import_job_on_finished(ImportJob *j) {
log_info("Completed writing to layer %s.", i->final_path);
- i->layer_job = import_job_unref(i->layer_job);
+ i->layer_job = pull_job_unref(i->layer_job);
free(i->temp_path);
i->temp_path = NULL;
free(i->final_path);
i->final_path = NULL;
i->current_ancestry ++;
- r = dkr_import_pull_layer(i);
+ r = dkr_pull_pull_layer(i);
if (r < 0)
goto finish;
} else if (i->json_job != j)
assert_not_reached("Got finished event for unknown curl object");
- if (!dkr_import_is_done(i))
+ if (!dkr_pull_is_done(i))
return;
- dkr_import_report_progress(i, DKR_COPYING);
+ dkr_pull_report_progress(i, DKR_COPYING);
- r = dkr_import_make_local_copy(i);
+ r = dkr_pull_make_local_copy(i, DKR_PULL_V1);
if (r < 0)
goto finish;
r = 0;
-
finish:
if (i->on_finished)
i->on_finished(i, r, i->userdata);
@@ -795,52 +1243,38 @@ finish:
sd_event_exit(i->event, r);
}
-static int dkr_import_job_on_header(ImportJob *j, const char *header, size_t sz) {
- _cleanup_free_ char *registry = NULL;
- char *token;
- DkrImport *i;
- int r;
+static int get_protocol_address(char **protocol, char **address, const char *url) {
+ const char *sep, *dot;
+ _cleanup_free_ char *a = NULL, *p = NULL;
- assert(j);
- assert(j->userdata);
+ sep = strstr(url, "://");
+ if (!sep)
+ return -EINVAL;
- i = j->userdata;
+ dot = strrchr(url, '.');
+ if (!dot)
+ return -EINVAL;
+ dot--;
- r = curl_header_strdup(header, sz, HEADER_TOKEN, &token);
- if (r < 0)
+ p = strndup(url, (sep - url) + 3);
+ if (!p)
return log_oom();
- if (r > 0) {
- free(i->response_token);
- i->response_token = token;
- return 0;
- }
- r = curl_header_strdup(header, sz, HEADER_REGISTRY, &registry);
- if (r < 0)
- return log_oom();
- if (r > 0) {
- char **l, **k;
-
- l = strv_split(registry, ",");
- if (!l)
- return log_oom();
+ while (dot > (sep + 3) && *dot != '.')
+ dot--;
- STRV_FOREACH(k, l) {
- if (!hostname_is_valid(*k)) {
- log_error("Registry hostname is not valid.");
- strv_free(l);
- return -EBADMSG;
- }
- }
+ a = strdup(dot + 1);
+ if (!a)
+ return log_oom();
- strv_free(i->response_registries);
- i->response_registries = l;
- }
+ *address = a;
+ *protocol = p;
+ a = p = NULL;
return 0;
}
-int dkr_import_pull(DkrImport *i, const char *name, const char *tag, const char *local, bool force_local) {
+int dkr_pull_start(DkrPull *i, const char *name, const char *reference, const char *local, bool force_local, DkrPullVersion version) {
const char *url;
int r;
@@ -849,7 +1283,7 @@ int dkr_import_pull(DkrImport *i, const char *name, const char *tag, const char
if (!dkr_name_is_valid(name))
return -EINVAL;
- if (tag && !dkr_tag_is_valid(tag))
+ if (reference && !dkr_ref_is_valid(reference))
return -EINVAL;
if (local && !machine_name_is_valid(local))
@@ -858,8 +1292,14 @@ int dkr_import_pull(DkrImport *i, const char *name, const char *tag, const char
if (i->images_job)
return -EBUSY;
- if (!tag)
- tag = "latest";
+ if (!reference)
+ reference = "latest";
+
+ free(i->index_protocol);
+ free(i->index_address);
+ r = get_protocol_address(&i->index_protocol, &i->index_address, i->index_url);
+ if (r < 0)
+ return r;
r = free_and_strdup(&i->local, local);
if (r < 0)
@@ -869,23 +1309,27 @@ int dkr_import_pull(DkrImport *i, const char *name, const char *tag, const char
r = free_and_strdup(&i->name, name);
if (r < 0)
return r;
- r = free_and_strdup(&i->tag, tag);
+ r = free_and_strdup(&i->reference, reference);
if (r < 0)
return r;
url = strjoina(i->index_url, "/v1/repositories/", name, "/images");
- r = import_job_new(&i->images_job, url, i->glue, i);
+ r = pull_job_new(&i->images_job, url, i->glue, i);
if (r < 0)
return r;
- r = dkr_import_add_token(i, i->images_job);
+ r = dkr_pull_add_token(i, i->images_job);
if (r < 0)
return r;
- i->images_job->on_finished = dkr_import_job_on_finished;
- i->images_job->on_header = dkr_import_job_on_header;
- i->images_job->on_progress = dkr_import_job_on_progress;
+ if (version == DKR_PULL_V1)
+ i->images_job->on_finished = dkr_pull_job_on_finished;
+ else
+ i->images_job->on_finished = dkr_pull_job_on_finished_v2;
+
+ i->images_job->on_header = dkr_pull_job_on_header;
+ i->images_job->on_progress = dkr_pull_job_on_progress;
- return import_job_begin(i->images_job);
+ return pull_job_begin(i->images_job);
}
diff --git a/src/import/pull-dkr.h b/src/import/pull-dkr.h
new file mode 100644
index 0000000000..33d18cb394
--- /dev/null
+++ b/src/import/pull-dkr.h
@@ -0,0 +1,37 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2014 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#pragma once
+
+#include "sd-event.h"
+#include "util.h"
+
+typedef enum { DKR_PULL_V1, DKR_PULL_V2 } DkrPullVersion;
+typedef struct DkrPull DkrPull;
+
+typedef void (*DkrPullFinished)(DkrPull *pull, int error, void *userdata);
+
+int dkr_pull_new(DkrPull **pull, sd_event *event, const char *index_url, const char *image_root, DkrPullFinished on_finished, void *userdata);
+DkrPull* dkr_pull_unref(DkrPull *pull);
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(DkrPull*, dkr_pull_unref);
+
+int dkr_pull_start(DkrPull *pull, const char *name, const char *tag, const char *local, bool force_local, DkrPullVersion version);
diff --git a/src/import/import-job.c b/src/import/pull-job.c
index 809486500b..42939f2104 100644
--- a/src/import/import-job.c
+++ b/src/import/pull-job.c
@@ -22,9 +22,10 @@
#include <sys/xattr.h>
#include "strv.h"
-#include "import-job.h"
+#include "machine-pool.h"
+#include "pull-job.h"
-ImportJob* import_job_unref(ImportJob *j) {
+PullJob* pull_job_unref(PullJob *j) {
if (!j)
return NULL;
@@ -33,12 +34,7 @@ ImportJob* import_job_unref(ImportJob *j) {
safe_close(j->disk_fd);
- if (j->compressed == IMPORT_JOB_XZ)
- lzma_end(&j->xz);
- else if (j->compressed == IMPORT_JOB_GZIP)
- inflateEnd(&j->gzip);
- else if (j->compressed == IMPORT_JOB_BZIP2)
- BZ2_bzDecompressEnd(&j->bzip2);
+ import_compress_free(&j->compress);
if (j->checksum_context)
gcry_md_close(j->checksum_context);
@@ -54,19 +50,19 @@ ImportJob* import_job_unref(ImportJob *j) {
return NULL;
}
-static void import_job_finish(ImportJob *j, int ret) {
+static void pull_job_finish(PullJob *j, int ret) {
assert(j);
- if (j->state == IMPORT_JOB_DONE ||
- j->state == IMPORT_JOB_FAILED)
+ if (j->state == PULL_JOB_DONE ||
+ j->state == PULL_JOB_FAILED)
return;
if (ret == 0) {
- j->state = IMPORT_JOB_DONE;
+ j->state = PULL_JOB_DONE;
j->progress_percent = 100;
log_info("Download of %s complete.", j->url);
} else {
- j->state = IMPORT_JOB_FAILED;
+ j->state = PULL_JOB_FAILED;
j->error = ret;
}
@@ -74,16 +70,16 @@ static void import_job_finish(ImportJob *j, int ret) {
j->on_finished(j);
}
-void import_job_curl_on_finished(CurlGlue *g, CURL *curl, CURLcode result) {
- ImportJob *j = NULL;
+void pull_job_curl_on_finished(CurlGlue *g, CURL *curl, CURLcode result) {
+ PullJob *j = NULL;
CURLcode code;
long status;
int r;
- if (curl_easy_getinfo(curl, CURLINFO_PRIVATE, &j) != CURLE_OK)
+ if (curl_easy_getinfo(curl, CURLINFO_PRIVATE, (char **)&j) != CURLE_OK)
return;
- if (!j || j->state == IMPORT_JOB_DONE || j->state == IMPORT_JOB_FAILED)
+ if (!j || j->state == PULL_JOB_DONE || j->state == PULL_JOB_FAILED)
return;
if (result != CURLE_OK) {
@@ -112,7 +108,7 @@ void import_job_curl_on_finished(CurlGlue *g, CURL *curl, CURLcode result) {
goto finish;
}
- if (j->state != IMPORT_JOB_RUNNING) {
+ if (j->state != PULL_JOB_RUNNING) {
log_error("Premature connection termination.");
r = -EIO;
goto finish;
@@ -149,8 +145,7 @@ void import_job_curl_on_finished(CurlGlue *g, CURL *curl, CURLcode result) {
* sparse and we just seeked for the last part */
if (ftruncate(j->disk_fd, j->written_uncompressed) < 0) {
- log_error_errno(errno, "Failed to truncate file: %m");
- r = -errno;
+ r = log_error_errno(errno, "Failed to truncate file: %m");
goto finish;
}
@@ -173,10 +168,11 @@ void import_job_curl_on_finished(CurlGlue *g, CURL *curl, CURLcode result) {
r = 0;
finish:
- import_job_finish(j, r);
+ pull_job_finish(j, r);
}
-static int import_job_write_uncompressed(ImportJob *j, void *p, size_t sz) {
+static int pull_job_write_uncompressed(const void *p, size_t sz, void *userdata) {
+ PullJob *j = userdata;
ssize_t n;
assert(j);
@@ -197,14 +193,17 @@ static int import_job_write_uncompressed(ImportJob *j, void *p, size_t sz) {
if (j->disk_fd >= 0) {
+ if (j->grow_machine_directory && j->written_since_last_grow >= GROW_INTERVAL_BYTES) {
+ j->written_since_last_grow = 0;
+ grow_machine_directory();
+ }
+
if (j->allow_sparse)
n = sparse_write(j->disk_fd, p, sz, 64);
else
n = write(j->disk_fd, p, sz);
- if (n < 0) {
- log_error_errno(errno, "Failed to write file: %m");
- return -errno;
- }
+ if (n < 0)
+ return log_error_errno(errno, "Failed to write file: %m");
if ((size_t) n < sz) {
log_error("Short write");
return -EIO;
@@ -219,11 +218,12 @@ static int import_job_write_uncompressed(ImportJob *j, void *p, size_t sz) {
}
j->written_uncompressed += sz;
+ j->written_since_last_grow += sz;
return 0;
}
-static int import_job_write_compressed(ImportJob *j, void *p, size_t sz) {
+static int pull_job_write_compressed(PullJob *j, void *p, size_t sz) {
int r;
assert(j);
@@ -251,95 +251,16 @@ static int import_job_write_compressed(ImportJob *j, void *p, size_t sz) {
if (j->checksum_context)
gcry_md_write(j->checksum_context, p, sz);
- switch (j->compressed) {
-
- case IMPORT_JOB_UNCOMPRESSED:
- r = import_job_write_uncompressed(j, p, sz);
- if (r < 0)
- return r;
-
- break;
-
- case IMPORT_JOB_XZ:
- j->xz.next_in = p;
- j->xz.avail_in = sz;
-
- while (j->xz.avail_in > 0) {
- uint8_t buffer[16 * 1024];
- lzma_ret lzr;
-
- j->xz.next_out = buffer;
- j->xz.avail_out = sizeof(buffer);
-
- lzr = lzma_code(&j->xz, LZMA_RUN);
- if (lzr != LZMA_OK && lzr != LZMA_STREAM_END) {
- log_error("Decompression error.");
- return -EIO;
- }
-
- r = import_job_write_uncompressed(j, buffer, sizeof(buffer) - j->xz.avail_out);
- if (r < 0)
- return r;
- }
-
- break;
-
- case IMPORT_JOB_GZIP:
- j->gzip.next_in = p;
- j->gzip.avail_in = sz;
-
- while (j->gzip.avail_in > 0) {
- uint8_t buffer[16 * 1024];
-
- j->gzip.next_out = buffer;
- j->gzip.avail_out = sizeof(buffer);
-
- r = inflate(&j->gzip, Z_NO_FLUSH);
- if (r != Z_OK && r != Z_STREAM_END) {
- log_error("Decompression error.");
- return -EIO;
- }
-
- r = import_job_write_uncompressed(j, buffer, sizeof(buffer) - j->gzip.avail_out);
- if (r < 0)
- return r;
- }
-
- break;
-
- case IMPORT_JOB_BZIP2:
- j->bzip2.next_in = p;
- j->bzip2.avail_in = sz;
-
- while (j->bzip2.avail_in > 0) {
- uint8_t buffer[16 * 1024];
-
- j->bzip2.next_out = (char*) buffer;
- j->bzip2.avail_out = sizeof(buffer);
-
- r = BZ2_bzDecompress(&j->bzip2);
- if (r != BZ_OK && r != BZ_STREAM_END) {
- log_error("Decompression error.");
- return -EIO;
- }
-
- r = import_job_write_uncompressed(j, buffer, sizeof(buffer) - j->bzip2.avail_out);
- if (r < 0)
- return r;
- }
-
- break;
-
- default:
- assert_not_reached("Unknown compression");
- }
+ r = import_uncompress(&j->compress, p, sz, pull_job_write_uncompressed, j);
+ if (r < 0)
+ return r;
j->written_compressed += sz;
return 0;
}
-static int import_job_open_disk(ImportJob *j) {
+static int pull_job_open_disk(PullJob *j) {
int r;
assert(j);
@@ -373,17 +294,7 @@ static int import_job_open_disk(ImportJob *j) {
return 0;
}
-static int import_job_detect_compression(ImportJob *j) {
- static const uint8_t xz_signature[] = {
- 0xfd, '7', 'z', 'X', 'Z', 0x00
- };
- static const uint8_t gzip_signature[] = {
- 0x1f, 0x8b
- };
- static const uint8_t bzip2_signature[] = {
- 'B', 'Z', 'h'
- };
-
+static int pull_job_detect_compression(PullJob *j) {
_cleanup_free_ uint8_t *stub = NULL;
size_t stub_size;
@@ -391,49 +302,15 @@ static int import_job_detect_compression(ImportJob *j) {
assert(j);
- if (j->payload_size < MAX3(sizeof(xz_signature),
- sizeof(gzip_signature),
- sizeof(bzip2_signature)))
+ r = import_uncompress_detect(&j->compress, j->payload, j->payload_size);
+ if (r < 0)
+ return log_error_errno(r, "Failed to initialize compressor: %m");
+ if (r == 0)
return 0;
- if (memcmp(j->payload, xz_signature, sizeof(xz_signature)) == 0)
- j->compressed = IMPORT_JOB_XZ;
- else if (memcmp(j->payload, gzip_signature, sizeof(gzip_signature)) == 0)
- j->compressed = IMPORT_JOB_GZIP;
- else if (memcmp(j->payload, bzip2_signature, sizeof(bzip2_signature)) == 0)
- j->compressed = IMPORT_JOB_BZIP2;
- else
- j->compressed = IMPORT_JOB_UNCOMPRESSED;
-
- log_debug("Stream is XZ compressed: %s", yes_no(j->compressed == IMPORT_JOB_XZ));
- log_debug("Stream is GZIP compressed: %s", yes_no(j->compressed == IMPORT_JOB_GZIP));
- log_debug("Stream is BZIP2 compressed: %s", yes_no(j->compressed == IMPORT_JOB_BZIP2));
-
- if (j->compressed == IMPORT_JOB_XZ) {
- lzma_ret xzr;
-
- xzr = lzma_stream_decoder(&j->xz, UINT64_MAX, LZMA_TELL_UNSUPPORTED_CHECK);
- if (xzr != LZMA_OK) {
- log_error("Failed to initialize XZ decoder.");
- return -EIO;
- }
- }
- if (j->compressed == IMPORT_JOB_GZIP) {
- r = inflateInit2(&j->gzip, 15+16);
- if (r != Z_OK) {
- log_error("Failed to initialize gzip decoder.");
- return -EIO;
- }
- }
- if (j->compressed == IMPORT_JOB_BZIP2) {
- r = BZ2_bzDecompressInit(&j->bzip2, 0, 0);
- if (r != BZ_OK) {
- log_error("Failed to initialize bzip2 decoder.");
- return -EIO;
- }
- }
+ log_debug("Stream is compressed: %s", import_compress_type_to_string(j->compress.type));
- r = import_job_open_disk(j);
+ r = pull_job_open_disk(j);
if (r < 0)
return r;
@@ -445,17 +322,17 @@ static int import_job_detect_compression(ImportJob *j) {
j->payload_size = 0;
j->payload_allocated = 0;
- j->state = IMPORT_JOB_RUNNING;
+ j->state = PULL_JOB_RUNNING;
- r = import_job_write_compressed(j, stub, stub_size);
+ r = pull_job_write_compressed(j, stub, stub_size);
if (r < 0)
return r;
return 0;
}
-static size_t import_job_write_callback(void *contents, size_t size, size_t nmemb, void *userdata) {
- ImportJob *j = userdata;
+static size_t pull_job_write_callback(void *contents, size_t size, size_t nmemb, void *userdata) {
+ PullJob *j = userdata;
size_t sz = size * nmemb;
int r;
@@ -464,7 +341,7 @@ static size_t import_job_write_callback(void *contents, size_t size, size_t nmem
switch (j->state) {
- case IMPORT_JOB_ANALYZING:
+ case PULL_JOB_ANALYZING:
/* Let's first check what it actually is */
if (!GREEDY_REALLOC(j->payload, j->payload_allocated, j->payload_size + sz)) {
@@ -475,22 +352,22 @@ static size_t import_job_write_callback(void *contents, size_t size, size_t nmem
memcpy(j->payload + j->payload_size, contents, sz);
j->payload_size += sz;
- r = import_job_detect_compression(j);
+ r = pull_job_detect_compression(j);
if (r < 0)
goto fail;
break;
- case IMPORT_JOB_RUNNING:
+ case PULL_JOB_RUNNING:
- r = import_job_write_compressed(j, contents, sz);
+ r = pull_job_write_compressed(j, contents, sz);
if (r < 0)
goto fail;
break;
- case IMPORT_JOB_DONE:
- case IMPORT_JOB_FAILED:
+ case PULL_JOB_DONE:
+ case PULL_JOB_FAILED:
r = -ESTALE;
goto fail;
@@ -501,12 +378,12 @@ static size_t import_job_write_callback(void *contents, size_t size, size_t nmem
return sz;
fail:
- import_job_finish(j, r);
+ pull_job_finish(j, r);
return 0;
}
-static size_t import_job_header_callback(void *contents, size_t size, size_t nmemb, void *userdata) {
- ImportJob *j = userdata;
+static size_t pull_job_header_callback(void *contents, size_t size, size_t nmemb, void *userdata) {
+ PullJob *j = userdata;
size_t sz = size * nmemb;
_cleanup_free_ char *length = NULL, *last_modified = NULL;
char *etag;
@@ -515,12 +392,12 @@ static size_t import_job_header_callback(void *contents, size_t size, size_t nme
assert(contents);
assert(j);
- if (j->state == IMPORT_JOB_DONE || j->state == IMPORT_JOB_FAILED) {
+ if (j->state == PULL_JOB_DONE || j->state == PULL_JOB_FAILED) {
r = -ESTALE;
goto fail;
}
- assert(j->state == IMPORT_JOB_ANALYZING);
+ assert(j->state == PULL_JOB_ANALYZING);
r = curl_header_strdup(contents, sz, "ETag:", &etag);
if (r < 0) {
@@ -534,7 +411,7 @@ static size_t import_job_header_callback(void *contents, size_t size, size_t nme
if (strv_contains(j->old_etags, j->etag)) {
log_info("Image already downloaded. Skipping download.");
j->etag_exists = true;
- import_job_finish(j, 0);
+ pull_job_finish(j, 0);
return sz;
}
@@ -583,12 +460,12 @@ static size_t import_job_header_callback(void *contents, size_t size, size_t nme
return sz;
fail:
- import_job_finish(j, r);
+ pull_job_finish(j, r);
return 0;
}
-static int import_job_progress_callback(void *userdata, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow) {
- ImportJob *j = userdata;
+static int pull_job_progress_callback(void *userdata, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow) {
+ PullJob *j = userdata;
unsigned percent;
usec_t n;
@@ -630,18 +507,18 @@ static int import_job_progress_callback(void *userdata, curl_off_t dltotal, curl
return 0;
}
-int import_job_new(ImportJob **ret, const char *url, CurlGlue *glue, void *userdata) {
- _cleanup_(import_job_unrefp) ImportJob *j = NULL;
+int pull_job_new(PullJob **ret, const char *url, CurlGlue *glue, void *userdata) {
+ _cleanup_(pull_job_unrefp) PullJob *j = NULL;
assert(url);
assert(glue);
assert(ret);
- j = new0(ImportJob, 1);
+ j = new0(PullJob, 1);
if (!j)
return -ENOMEM;
- j->state = IMPORT_JOB_INIT;
+ j->state = PULL_JOB_INIT;
j->disk_fd = -1;
j->userdata = userdata;
j->glue = glue;
@@ -659,14 +536,17 @@ int import_job_new(ImportJob **ret, const char *url, CurlGlue *glue, void *userd
return 0;
}
-int import_job_begin(ImportJob *j) {
+int pull_job_begin(PullJob *j) {
int r;
assert(j);
- if (j->state != IMPORT_JOB_INIT)
+ if (j->state != PULL_JOB_INIT)
return -EBUSY;
+ if (j->grow_machine_directory)
+ grow_machine_directory();
+
r = curl_glue_make(&j->curl, j->url, j);
if (r < 0)
return r;
@@ -702,19 +582,19 @@ int import_job_begin(ImportJob *j) {
return -EIO;
}
- if (curl_easy_setopt(j->curl, CURLOPT_WRITEFUNCTION, import_job_write_callback) != CURLE_OK)
+ if (curl_easy_setopt(j->curl, CURLOPT_WRITEFUNCTION, pull_job_write_callback) != CURLE_OK)
return -EIO;
if (curl_easy_setopt(j->curl, CURLOPT_WRITEDATA, j) != CURLE_OK)
return -EIO;
- if (curl_easy_setopt(j->curl, CURLOPT_HEADERFUNCTION, import_job_header_callback) != CURLE_OK)
+ if (curl_easy_setopt(j->curl, CURLOPT_HEADERFUNCTION, pull_job_header_callback) != CURLE_OK)
return -EIO;
if (curl_easy_setopt(j->curl, CURLOPT_HEADERDATA, j) != CURLE_OK)
return -EIO;
- if (curl_easy_setopt(j->curl, CURLOPT_XFERINFOFUNCTION, import_job_progress_callback) != CURLE_OK)
+ if (curl_easy_setopt(j->curl, CURLOPT_XFERINFOFUNCTION, pull_job_progress_callback) != CURLE_OK)
return -EIO;
if (curl_easy_setopt(j->curl, CURLOPT_XFERINFODATA, j) != CURLE_OK)
@@ -727,7 +607,7 @@ int import_job_begin(ImportJob *j) {
if (r < 0)
return r;
- j->state = IMPORT_JOB_ANALYZING;
+ j->state = PULL_JOB_ANALYZING;
return 0;
}
diff --git a/src/import/pull-job.h b/src/import/pull-job.h
new file mode 100644
index 0000000000..3239aeac20
--- /dev/null
+++ b/src/import/pull-job.h
@@ -0,0 +1,117 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2015 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 <gcrypt.h>
+
+#include "macro.h"
+#include "curl-util.h"
+#include "import-compress.h"
+
+typedef struct PullJob PullJob;
+
+typedef void (*PullJobFinished)(PullJob *job);
+typedef int (*PullJobOpenDisk)(PullJob *job);
+typedef int (*PullJobHeader)(PullJob *job, const char *header, size_t sz);
+typedef void (*PullJobProgress)(PullJob *job);
+
+typedef enum PullJobState {
+ PULL_JOB_INIT,
+ PULL_JOB_ANALYZING, /* Still reading into ->payload, to figure out what we have */
+ PULL_JOB_RUNNING, /* Writing to destination */
+ PULL_JOB_DONE,
+ PULL_JOB_FAILED,
+ _PULL_JOB_STATE_MAX,
+ _PULL_JOB_STATE_INVALID = -1,
+} PullJobState;
+
+#define PULL_JOB_STATE_IS_COMPLETE(j) (IN_SET((j)->state, PULL_JOB_DONE, PULL_JOB_FAILED))
+
+typedef enum PullJobCompression {
+ PULL_JOB_UNCOMPRESSED,
+ PULL_JOB_XZ,
+ PULL_JOB_GZIP,
+ PULL_JOB_BZIP2,
+ _PULL_JOB_COMPRESSION_MAX,
+ _PULL_JOB_COMPRESSION_INVALID = -1,
+} PullJobCompression;
+
+struct PullJob {
+ PullJobState state;
+ int error;
+
+ char *url;
+
+ void *userdata;
+ PullJobFinished on_finished;
+ PullJobOpenDisk on_open_disk;
+ PullJobHeader on_header;
+ PullJobProgress on_progress;
+
+ CurlGlue *glue;
+ CURL *curl;
+ struct curl_slist *request_header;
+
+ char *etag;
+ char **old_etags;
+ bool etag_exists;
+
+ uint64_t content_length;
+ uint64_t written_compressed;
+ uint64_t written_uncompressed;
+
+ uint64_t uncompressed_max;
+ uint64_t compressed_max;
+
+ uint8_t *payload;
+ size_t payload_size;
+ size_t payload_allocated;
+
+ int disk_fd;
+
+ usec_t mtime;
+
+ ImportCompress compress;
+
+ unsigned progress_percent;
+ usec_t start_usec;
+ usec_t last_status_usec;
+
+ bool allow_sparse;
+
+ bool calc_checksum;
+ gcry_md_hd_t checksum_context;
+
+ char *checksum;
+
+ bool grow_machine_directory;
+ uint64_t written_since_last_grow;
+};
+
+int pull_job_new(PullJob **job, const char *url, CurlGlue *glue, void *userdata);
+PullJob* pull_job_unref(PullJob *job);
+
+int pull_job_begin(PullJob *j);
+
+void pull_job_curl_on_finished(CurlGlue *g, CURL *curl, CURLcode result);
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(PullJob*, pull_job_unref);
diff --git a/src/import/pull-raw.c b/src/import/pull-raw.c
new file mode 100644
index 0000000000..b65bb0c034
--- /dev/null
+++ b/src/import/pull-raw.c
@@ -0,0 +1,517 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2014 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/xattr.h>
+#include <linux/fs.h>
+#include <curl/curl.h>
+
+#include "sd-daemon.h"
+#include "utf8.h"
+#include "strv.h"
+#include "copy.h"
+#include "btrfs-util.h"
+#include "util.h"
+#include "macro.h"
+#include "mkdir.h"
+#include "rm-rf.h"
+#include "path-util.h"
+#include "import-util.h"
+#include "import-common.h"
+#include "curl-util.h"
+#include "qcow2-util.h"
+#include "pull-job.h"
+#include "pull-common.h"
+#include "pull-raw.h"
+
+typedef enum RawProgress {
+ RAW_DOWNLOADING,
+ RAW_VERIFYING,
+ RAW_UNPACKING,
+ RAW_FINALIZING,
+ RAW_COPYING,
+} RawProgress;
+
+struct RawPull {
+ sd_event *event;
+ CurlGlue *glue;
+
+ char *image_root;
+
+ PullJob *raw_job;
+ PullJob *checksum_job;
+ PullJob *signature_job;
+
+ RawPullFinished on_finished;
+ void *userdata;
+
+ char *local;
+ bool force_local;
+ bool grow_machine_directory;
+
+ char *temp_path;
+ char *final_path;
+
+ ImportVerify verify;
+};
+
+RawPull* raw_pull_unref(RawPull *i) {
+ if (!i)
+ return NULL;
+
+ pull_job_unref(i->raw_job);
+ pull_job_unref(i->checksum_job);
+ pull_job_unref(i->signature_job);
+
+ curl_glue_unref(i->glue);
+ sd_event_unref(i->event);
+
+ if (i->temp_path) {
+ (void) unlink(i->temp_path);
+ free(i->temp_path);
+ }
+
+ free(i->final_path);
+ free(i->image_root);
+ free(i->local);
+ free(i);
+
+ return NULL;
+}
+
+int raw_pull_new(
+ RawPull **ret,
+ sd_event *event,
+ const char *image_root,
+ RawPullFinished on_finished,
+ void *userdata) {
+
+ _cleanup_(raw_pull_unrefp) RawPull *i = NULL;
+ int r;
+
+ assert(ret);
+
+ i = new0(RawPull, 1);
+ if (!i)
+ return -ENOMEM;
+
+ i->on_finished = on_finished;
+ i->userdata = userdata;
+
+ i->image_root = strdup(image_root ?: "/var/lib/machines");
+ if (!i->image_root)
+ return -ENOMEM;
+
+ i->grow_machine_directory = path_startswith(i->image_root, "/var/lib/machines");
+
+ if (event)
+ i->event = sd_event_ref(event);
+ else {
+ r = sd_event_default(&i->event);
+ if (r < 0)
+ return r;
+ }
+
+ r = curl_glue_new(&i->glue, i->event);
+ if (r < 0)
+ return r;
+
+ i->glue->on_finished = pull_job_curl_on_finished;
+ i->glue->userdata = i;
+
+ *ret = i;
+ i = NULL;
+
+ return 0;
+}
+
+static void raw_pull_report_progress(RawPull *i, RawProgress p) {
+ unsigned percent;
+
+ assert(i);
+
+ switch (p) {
+
+ case RAW_DOWNLOADING: {
+ unsigned remain = 80;
+
+ percent = 0;
+
+ if (i->checksum_job) {
+ percent += i->checksum_job->progress_percent * 5 / 100;
+ remain -= 5;
+ }
+
+ if (i->signature_job) {
+ percent += i->signature_job->progress_percent * 5 / 100;
+ remain -= 5;
+ }
+
+ if (i->raw_job)
+ percent += i->raw_job->progress_percent * remain / 100;
+ break;
+ }
+
+ case RAW_VERIFYING:
+ percent = 80;
+ break;
+
+ case RAW_UNPACKING:
+ percent = 85;
+ break;
+
+ case RAW_FINALIZING:
+ percent = 90;
+ break;
+
+ case RAW_COPYING:
+ percent = 95;
+ break;
+
+ default:
+ assert_not_reached("Unknown progress state");
+ }
+
+ sd_notifyf(false, "X_IMPORT_PROGRESS=%u", percent);
+ log_debug("Combined progress %u%%", percent);
+}
+
+static int raw_pull_maybe_convert_qcow2(RawPull *i) {
+ _cleanup_close_ int converted_fd = -1;
+ _cleanup_free_ char *t = NULL;
+ int r;
+
+ assert(i);
+ assert(i->raw_job);
+
+ r = qcow2_detect(i->raw_job->disk_fd);
+ if (r < 0)
+ return log_error_errno(r, "Failed to detect whether this is a QCOW2 image: %m");
+ if (r == 0)
+ return 0;
+
+ /* This is a QCOW2 image, let's convert it */
+ r = tempfn_random(i->final_path, &t);
+ if (r < 0)
+ return log_oom();
+
+ converted_fd = open(t, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664);
+ if (converted_fd < 0)
+ return log_error_errno(errno, "Failed to create %s: %m", t);
+
+ r = chattr_fd(converted_fd, FS_NOCOW_FL, FS_NOCOW_FL);
+ if (r < 0)
+ log_warning_errno(errno, "Failed to set file attributes on %s: %m", t);
+
+ log_info("Unpacking QCOW2 file.");
+
+ r = qcow2_convert(i->raw_job->disk_fd, converted_fd);
+ if (r < 0) {
+ unlink(t);
+ return log_error_errno(r, "Failed to convert qcow2 image: %m");
+ }
+
+ (void) unlink(i->temp_path);
+ free(i->temp_path);
+ i->temp_path = t;
+ t = NULL;
+
+ safe_close(i->raw_job->disk_fd);
+ i->raw_job->disk_fd = converted_fd;
+ converted_fd = -1;
+
+ return 1;
+}
+
+static int raw_pull_make_local_copy(RawPull *i) {
+ _cleanup_free_ char *tp = NULL;
+ _cleanup_close_ int dfd = -1;
+ const char *p;
+ int r;
+
+ assert(i);
+ assert(i->raw_job);
+
+ if (!i->local)
+ return 0;
+
+ if (i->raw_job->etag_exists) {
+ /* We have downloaded this one previously, reopen it */
+
+ assert(i->raw_job->disk_fd < 0);
+
+ if (!i->final_path) {
+ r = pull_make_path(i->raw_job->url, i->raw_job->etag, i->image_root, ".raw-", ".raw", &i->final_path);
+ if (r < 0)
+ return log_oom();
+ }
+
+ i->raw_job->disk_fd = open(i->final_path, O_RDONLY|O_NOCTTY|O_CLOEXEC);
+ if (i->raw_job->disk_fd < 0)
+ return log_error_errno(errno, "Failed to open vendor image: %m");
+ } else {
+ /* We freshly downloaded the image, use it */
+
+ assert(i->raw_job->disk_fd >= 0);
+
+ if (lseek(i->raw_job->disk_fd, SEEK_SET, 0) == (off_t) -1)
+ return log_error_errno(errno, "Failed to seek to beginning of vendor image: %m");
+ }
+
+ p = strjoina(i->image_root, "/", i->local, ".raw");
+
+ if (i->force_local)
+ (void) rm_rf(p, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME);
+
+ r = tempfn_random(p, &tp);
+ if (r < 0)
+ return log_oom();
+
+ dfd = open(tp, O_WRONLY|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664);
+ if (dfd < 0)
+ return log_error_errno(errno, "Failed to create writable copy of image: %m");
+
+ /* Turn off COW writing. This should greatly improve
+ * performance on COW file systems like btrfs, since it
+ * reduces fragmentation caused by not allowing in-place
+ * writes. */
+ r = chattr_fd(dfd, FS_NOCOW_FL, FS_NOCOW_FL);
+ if (r < 0)
+ log_warning_errno(errno, "Failed to set file attributes on %s: %m", tp);
+
+ r = copy_bytes(i->raw_job->disk_fd, dfd, (off_t) -1, true);
+ if (r < 0) {
+ unlink(tp);
+ return log_error_errno(r, "Failed to make writable copy of image: %m");
+ }
+
+ (void) copy_times(i->raw_job->disk_fd, dfd);
+ (void) copy_xattr(i->raw_job->disk_fd, dfd);
+
+ dfd = safe_close(dfd);
+
+ r = rename(tp, p);
+ if (r < 0) {
+ unlink(tp);
+ return log_error_errno(errno, "Failed to move writable image into place: %m");
+ }
+
+ log_info("Created new local image '%s'.", i->local);
+ return 0;
+}
+
+static bool raw_pull_is_done(RawPull *i) {
+ assert(i);
+ assert(i->raw_job);
+
+ if (i->raw_job->state != PULL_JOB_DONE)
+ return false;
+ if (i->checksum_job && i->checksum_job->state != PULL_JOB_DONE)
+ return false;
+ if (i->signature_job && i->signature_job->state != PULL_JOB_DONE)
+ return false;
+
+ return true;
+}
+
+static void raw_pull_job_on_finished(PullJob *j) {
+ RawPull *i;
+ int r;
+
+ assert(j);
+ assert(j->userdata);
+
+ i = j->userdata;
+ if (j->error != 0) {
+ if (j == i->checksum_job)
+ log_error_errno(j->error, "Failed to retrieve SHA256 checksum, cannot verify. (Try --verify=no?)");
+ else if (j == i->signature_job)
+ log_error_errno(j->error, "Failed to retrieve signature file, cannot verify. (Try --verify=no?)");
+ else
+ log_error_errno(j->error, "Failed to retrieve image file. (Wrong URL?)");
+
+ r = j->error;
+ goto finish;
+ }
+
+ /* This is invoked if either the download completed
+ * successfully, or the download was skipped because we
+ * already have the etag. In this case ->etag_exists is
+ * true.
+ *
+ * We only do something when we got all three files */
+
+ if (!raw_pull_is_done(i))
+ return;
+
+ if (!i->raw_job->etag_exists) {
+ /* This is a new download, verify it, and move it into place */
+ assert(i->raw_job->disk_fd >= 0);
+
+ raw_pull_report_progress(i, RAW_VERIFYING);
+
+ r = pull_verify(i->raw_job, i->checksum_job, i->signature_job);
+ if (r < 0)
+ goto finish;
+
+ raw_pull_report_progress(i, RAW_UNPACKING);
+
+ r = raw_pull_maybe_convert_qcow2(i);
+ if (r < 0)
+ goto finish;
+
+ raw_pull_report_progress(i, RAW_FINALIZING);
+
+ r = import_make_read_only_fd(i->raw_job->disk_fd);
+ if (r < 0)
+ goto finish;
+
+ r = rename_noreplace(AT_FDCWD, i->temp_path, AT_FDCWD, i->final_path);
+ if (r < 0) {
+ log_error_errno(r, "Failed to move RAW file into place: %m");
+ goto finish;
+ }
+
+ free(i->temp_path);
+ i->temp_path = NULL;
+ }
+
+ raw_pull_report_progress(i, RAW_COPYING);
+
+ r = raw_pull_make_local_copy(i);
+ if (r < 0)
+ goto finish;
+
+ r = 0;
+
+finish:
+ if (i->on_finished)
+ i->on_finished(i, r, i->userdata);
+ else
+ sd_event_exit(i->event, r);
+}
+
+static int raw_pull_job_on_open_disk(PullJob *j) {
+ RawPull *i;
+ int r;
+
+ assert(j);
+ assert(j->userdata);
+
+ i = j->userdata;
+ assert(i->raw_job == j);
+ assert(!i->final_path);
+ assert(!i->temp_path);
+
+ r = pull_make_path(j->url, j->etag, i->image_root, ".raw-", ".raw", &i->final_path);
+ if (r < 0)
+ return log_oom();
+
+ r = tempfn_random(i->final_path, &i->temp_path);
+ if (r < 0)
+ return log_oom();
+
+ (void) mkdir_parents_label(i->temp_path, 0700);
+
+ j->disk_fd = open(i->temp_path, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664);
+ if (j->disk_fd < 0)
+ return log_error_errno(errno, "Failed to create %s: %m", i->temp_path);
+
+ r = chattr_fd(j->disk_fd, FS_NOCOW_FL, FS_NOCOW_FL);
+ if (r < 0)
+ log_warning_errno(errno, "Failed to set file attributes on %s: %m", i->temp_path);
+
+ return 0;
+}
+
+static void raw_pull_job_on_progress(PullJob *j) {
+ RawPull *i;
+
+ assert(j);
+ assert(j->userdata);
+
+ i = j->userdata;
+
+ raw_pull_report_progress(i, RAW_DOWNLOADING);
+}
+
+int raw_pull_start(RawPull *i, const char *url, const char *local, bool force_local, ImportVerify verify) {
+ int r;
+
+ assert(i);
+ assert(verify < _IMPORT_VERIFY_MAX);
+ assert(verify >= 0);
+
+ if (!http_url_is_valid(url))
+ return -EINVAL;
+
+ if (local && !machine_name_is_valid(local))
+ return -EINVAL;
+
+ if (i->raw_job)
+ return -EBUSY;
+
+ r = free_and_strdup(&i->local, local);
+ if (r < 0)
+ return r;
+ i->force_local = force_local;
+ i->verify = verify;
+
+ /* Queue job for the image itself */
+ r = pull_job_new(&i->raw_job, url, i->glue, i);
+ if (r < 0)
+ return r;
+
+ i->raw_job->on_finished = raw_pull_job_on_finished;
+ i->raw_job->on_open_disk = raw_pull_job_on_open_disk;
+ i->raw_job->on_progress = raw_pull_job_on_progress;
+ i->raw_job->calc_checksum = verify != IMPORT_VERIFY_NO;
+ i->raw_job->grow_machine_directory = i->grow_machine_directory;
+
+ r = pull_find_old_etags(url, i->image_root, DT_REG, ".raw-", ".raw", &i->raw_job->old_etags);
+ if (r < 0)
+ return r;
+
+ r = pull_make_verification_jobs(&i->checksum_job, &i->signature_job, verify, url, i->glue, raw_pull_job_on_finished, i);
+ if (r < 0)
+ return r;
+
+ r = pull_job_begin(i->raw_job);
+ if (r < 0)
+ return r;
+
+ if (i->checksum_job) {
+ i->checksum_job->on_progress = raw_pull_job_on_progress;
+
+ r = pull_job_begin(i->checksum_job);
+ if (r < 0)
+ return r;
+ }
+
+ if (i->signature_job) {
+ i->signature_job->on_progress = raw_pull_job_on_progress;
+
+ r = pull_job_begin(i->signature_job);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
diff --git a/src/import/import-dkr.h b/src/import/pull-raw.h
index 633c767965..808f7be818 100644
--- a/src/import/import-dkr.h
+++ b/src/import/pull-raw.h
@@ -1,5 +1,7 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+#pragma once
+
/***
This file is part of systemd.
@@ -19,18 +21,17 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#pragma once
-
#include "sd-event.h"
-#include "util.h"
+#include "macro.h"
+#include "import-util.h"
-typedef struct DkrImport DkrImport;
+typedef struct RawPull RawPull;
-typedef void (*DkrImportFinished)(DkrImport *import, int error, void *userdata);
+typedef void (*RawPullFinished)(RawPull *pull, int error, void *userdata);
-int dkr_import_new(DkrImport **import, sd_event *event, const char *index_url, const char *image_root, DkrImportFinished on_finished, void *userdata);
-DkrImport* dkr_import_unref(DkrImport *import);
+int raw_pull_new(RawPull **pull, sd_event *event, const char *image_root, RawPullFinished on_finished, void *userdata);
+RawPull* raw_pull_unref(RawPull *pull);
-DEFINE_TRIVIAL_CLEANUP_FUNC(DkrImport*, dkr_import_unref);
+DEFINE_TRIVIAL_CLEANUP_FUNC(RawPull*, raw_pull_unref);
-int dkr_import_pull(DkrImport *import, const char *name, const char *tag, const char *local, bool force_local);
+int raw_pull_start(RawPull *pull, const char *url, const char *local, bool force_local, ImportVerify verify);
diff --git a/src/import/pull-tar.c b/src/import/pull-tar.c
new file mode 100644
index 0000000000..27a9af804d
--- /dev/null
+++ b/src/import/pull-tar.c
@@ -0,0 +1,417 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2015 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/prctl.h>
+#include <curl/curl.h>
+
+#include "sd-daemon.h"
+#include "utf8.h"
+#include "strv.h"
+#include "copy.h"
+#include "btrfs-util.h"
+#include "util.h"
+#include "macro.h"
+#include "mkdir.h"
+#include "rm-rf.h"
+#include "path-util.h"
+#include "import-util.h"
+#include "import-common.h"
+#include "curl-util.h"
+#include "pull-job.h"
+#include "pull-common.h"
+#include "pull-tar.h"
+#include "process-util.h"
+
+typedef enum TarProgress {
+ TAR_DOWNLOADING,
+ TAR_VERIFYING,
+ TAR_FINALIZING,
+ TAR_COPYING,
+} TarProgress;
+
+struct TarPull {
+ sd_event *event;
+ CurlGlue *glue;
+
+ char *image_root;
+
+ PullJob *tar_job;
+ PullJob *checksum_job;
+ PullJob *signature_job;
+
+ TarPullFinished on_finished;
+ void *userdata;
+
+ char *local;
+ bool force_local;
+ bool grow_machine_directory;
+
+ pid_t tar_pid;
+
+ char *temp_path;
+ char *final_path;
+
+ ImportVerify verify;
+};
+
+TarPull* tar_pull_unref(TarPull *i) {
+ if (!i)
+ return NULL;
+
+ if (i->tar_pid > 1) {
+ (void) kill_and_sigcont(i->tar_pid, SIGKILL);
+ (void) wait_for_terminate(i->tar_pid, NULL);
+ }
+
+ pull_job_unref(i->tar_job);
+ pull_job_unref(i->checksum_job);
+ pull_job_unref(i->signature_job);
+
+ curl_glue_unref(i->glue);
+ sd_event_unref(i->event);
+
+ if (i->temp_path) {
+ (void) rm_rf(i->temp_path, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME);
+ free(i->temp_path);
+ }
+
+ free(i->final_path);
+ free(i->image_root);
+ free(i->local);
+ free(i);
+
+ return NULL;
+}
+
+int tar_pull_new(
+ TarPull **ret,
+ sd_event *event,
+ const char *image_root,
+ TarPullFinished on_finished,
+ void *userdata) {
+
+ _cleanup_(tar_pull_unrefp) TarPull *i = NULL;
+ int r;
+
+ assert(ret);
+ assert(event);
+
+ i = new0(TarPull, 1);
+ if (!i)
+ return -ENOMEM;
+
+ i->on_finished = on_finished;
+ i->userdata = userdata;
+
+ i->image_root = strdup(image_root ?: "/var/lib/machines");
+ if (!i->image_root)
+ return -ENOMEM;
+
+ i->grow_machine_directory = path_startswith(i->image_root, "/var/lib/machines");
+
+ if (event)
+ i->event = sd_event_ref(event);
+ else {
+ r = sd_event_default(&i->event);
+ if (r < 0)
+ return r;
+ }
+
+ r = curl_glue_new(&i->glue, i->event);
+ if (r < 0)
+ return r;
+
+ i->glue->on_finished = pull_job_curl_on_finished;
+ i->glue->userdata = i;
+
+ *ret = i;
+ i = NULL;
+
+ return 0;
+}
+
+static void tar_pull_report_progress(TarPull *i, TarProgress p) {
+ unsigned percent;
+
+ assert(i);
+
+ switch (p) {
+
+ case TAR_DOWNLOADING: {
+ unsigned remain = 85;
+
+ percent = 0;
+
+ if (i->checksum_job) {
+ percent += i->checksum_job->progress_percent * 5 / 100;
+ remain -= 5;
+ }
+
+ if (i->signature_job) {
+ percent += i->signature_job->progress_percent * 5 / 100;
+ remain -= 5;
+ }
+
+ if (i->tar_job)
+ percent += i->tar_job->progress_percent * remain / 100;
+ break;
+ }
+
+ case TAR_VERIFYING:
+ percent = 85;
+ break;
+
+ case TAR_FINALIZING:
+ percent = 90;
+ break;
+
+ case TAR_COPYING:
+ percent = 95;
+ break;
+
+ default:
+ assert_not_reached("Unknown progress state");
+ }
+
+ sd_notifyf(false, "X_IMPORT_PROGRESS=%u", percent);
+ log_debug("Combined progress %u%%", percent);
+}
+
+static int tar_pull_make_local_copy(TarPull *i) {
+ int r;
+
+ assert(i);
+ assert(i->tar_job);
+
+ if (!i->local)
+ return 0;
+
+ if (!i->final_path) {
+ r = pull_make_path(i->tar_job->url, i->tar_job->etag, i->image_root, ".tar-", NULL, &i->final_path);
+ if (r < 0)
+ return log_oom();
+ }
+
+ r = pull_make_local_copy(i->final_path, i->image_root, i->local, i->force_local);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+static bool tar_pull_is_done(TarPull *i) {
+ assert(i);
+ assert(i->tar_job);
+
+ if (i->tar_job->state != PULL_JOB_DONE)
+ return false;
+ if (i->checksum_job && i->checksum_job->state != PULL_JOB_DONE)
+ return false;
+ if (i->signature_job && i->signature_job->state != PULL_JOB_DONE)
+ return false;
+
+ return true;
+}
+
+static void tar_pull_job_on_finished(PullJob *j) {
+ TarPull *i;
+ int r;
+
+ assert(j);
+ assert(j->userdata);
+
+ i = j->userdata;
+ if (j->error != 0) {
+ if (j == i->checksum_job)
+ log_error_errno(j->error, "Failed to retrieve SHA256 checksum, cannot verify. (Try --verify=no?)");
+ else if (j == i->signature_job)
+ log_error_errno(j->error, "Failed to retrieve signature file, cannot verify. (Try --verify=no?)");
+ else
+ log_error_errno(j->error, "Failed to retrieve image file. (Wrong URL?)");
+
+ r = j->error;
+ goto finish;
+ }
+
+ /* This is invoked if either the download completed
+ * successfully, or the download was skipped because we
+ * already have the etag. */
+
+ if (!tar_pull_is_done(i))
+ return;
+
+ j->disk_fd = safe_close(i->tar_job->disk_fd);
+
+ if (i->tar_pid > 0) {
+ r = wait_for_terminate_and_warn("tar", i->tar_pid, true);
+ i->tar_pid = 0;
+ if (r < 0)
+ goto finish;
+ }
+
+ if (!i->tar_job->etag_exists) {
+ /* This is a new download, verify it, and move it into place */
+
+ tar_pull_report_progress(i, TAR_VERIFYING);
+
+ r = pull_verify(i->tar_job, i->checksum_job, i->signature_job);
+ if (r < 0)
+ goto finish;
+
+ tar_pull_report_progress(i, TAR_FINALIZING);
+
+ r = import_make_read_only(i->temp_path);
+ if (r < 0)
+ goto finish;
+
+ r = rename_noreplace(AT_FDCWD, i->temp_path, AT_FDCWD, i->final_path);
+ if (r < 0) {
+ log_error_errno(r, "Failed to rename to final image name: %m");
+ goto finish;
+ }
+
+ free(i->temp_path);
+ i->temp_path = NULL;
+ }
+
+ tar_pull_report_progress(i, TAR_COPYING);
+
+ r = tar_pull_make_local_copy(i);
+ if (r < 0)
+ goto finish;
+
+ r = 0;
+
+finish:
+ if (i->on_finished)
+ i->on_finished(i, r, i->userdata);
+ else
+ sd_event_exit(i->event, r);
+}
+
+static int tar_pull_job_on_open_disk(PullJob *j) {
+ TarPull *i;
+ int r;
+
+ assert(j);
+ assert(j->userdata);
+
+ i = j->userdata;
+ assert(i->tar_job == j);
+ assert(!i->final_path);
+ assert(!i->temp_path);
+ assert(i->tar_pid <= 0);
+
+ r = pull_make_path(j->url, j->etag, i->image_root, ".tar-", NULL, &i->final_path);
+ if (r < 0)
+ return log_oom();
+
+ r = tempfn_random(i->final_path, &i->temp_path);
+ if (r < 0)
+ return log_oom();
+
+ mkdir_parents_label(i->temp_path, 0700);
+
+ r = btrfs_subvol_make(i->temp_path);
+ if (r == -ENOTTY) {
+ if (mkdir(i->temp_path, 0755) < 0)
+ return log_error_errno(errno, "Failed to create directory %s: %m", i->temp_path);
+ } else if (r < 0)
+ return log_error_errno(errno, "Failed to create subvolume %s: %m", i->temp_path);
+
+ j->disk_fd = import_fork_tar_x(i->temp_path, &i->tar_pid);
+ if (j->disk_fd < 0)
+ return j->disk_fd;
+
+ return 0;
+}
+
+static void tar_pull_job_on_progress(PullJob *j) {
+ TarPull *i;
+
+ assert(j);
+ assert(j->userdata);
+
+ i = j->userdata;
+
+ tar_pull_report_progress(i, TAR_DOWNLOADING);
+}
+
+int tar_pull_start(TarPull *i, const char *url, const char *local, bool force_local, ImportVerify verify) {
+ int r;
+
+ assert(i);
+
+ if (!http_url_is_valid(url))
+ return -EINVAL;
+
+ if (local && !machine_name_is_valid(local))
+ return -EINVAL;
+
+ if (i->tar_job)
+ return -EBUSY;
+
+ r = free_and_strdup(&i->local, local);
+ if (r < 0)
+ return r;
+ i->force_local = force_local;
+ i->verify = verify;
+
+ r = pull_job_new(&i->tar_job, url, i->glue, i);
+ if (r < 0)
+ return r;
+
+ i->tar_job->on_finished = tar_pull_job_on_finished;
+ i->tar_job->on_open_disk = tar_pull_job_on_open_disk;
+ i->tar_job->on_progress = tar_pull_job_on_progress;
+ i->tar_job->calc_checksum = verify != IMPORT_VERIFY_NO;
+ i->tar_job->grow_machine_directory = i->grow_machine_directory;
+
+ r = pull_find_old_etags(url, i->image_root, DT_DIR, ".tar-", NULL, &i->tar_job->old_etags);
+ if (r < 0)
+ return r;
+
+ r = pull_make_verification_jobs(&i->checksum_job, &i->signature_job, verify, url, i->glue, tar_pull_job_on_finished, i);
+ if (r < 0)
+ return r;
+
+ r = pull_job_begin(i->tar_job);
+ if (r < 0)
+ return r;
+
+ if (i->checksum_job) {
+ i->checksum_job->on_progress = tar_pull_job_on_progress;
+
+ r = pull_job_begin(i->checksum_job);
+ if (r < 0)
+ return r;
+ }
+
+ if (i->signature_job) {
+ i->signature_job->on_progress = tar_pull_job_on_progress;
+
+ r = pull_job_begin(i->signature_job);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
diff --git a/src/import/pull-tar.h b/src/import/pull-tar.h
new file mode 100644
index 0000000000..0ed507748c
--- /dev/null
+++ b/src/import/pull-tar.h
@@ -0,0 +1,37 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2015 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-event.h"
+#include "macro.h"
+#include "import-util.h"
+
+typedef struct TarPull TarPull;
+
+typedef void (*TarPullFinished)(TarPull *pull, int error, void *userdata);
+
+int tar_pull_new(TarPull **pull, sd_event *event, const char *image_root, TarPullFinished on_finished, void *userdata);
+TarPull* tar_pull_unref(TarPull *pull);
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(TarPull*, tar_pull_unref);
+
+int tar_pull_start(TarPull *pull, const char *url, const char *local, bool force_local, ImportVerify verify);
diff --git a/src/import/pull.c b/src/import/pull.c
index ee3ff68036..eec4583868 100644
--- a/src/import/pull.c
+++ b/src/import/pull.c
@@ -25,11 +25,12 @@
#include "event-util.h"
#include "verbs.h"
#include "build.h"
+#include "signal-util.h"
#include "machine-image.h"
-#include "import-tar.h"
-#include "import-raw.h"
-#include "import-dkr.h"
#include "import-util.h"
+#include "pull-tar.h"
+#include "pull-raw.h"
+#include "pull-dkr.h"
static bool arg_force = false;
static const char *arg_image_root = "/var/lib/machines";
@@ -42,9 +43,9 @@ static int interrupt_signal_handler(sd_event_source *s, const struct signalfd_si
return 0;
}
-static void on_tar_finished(TarImport *import, int error, void *userdata) {
+static void on_tar_finished(TarPull *pull, int error, void *userdata) {
sd_event *event = userdata;
- assert(import);
+ assert(pull);
if (error == 0)
log_info("Operation completed successfully.");
@@ -53,7 +54,7 @@ static void on_tar_finished(TarImport *import, int error, void *userdata) {
}
static int pull_tar(int argc, char *argv[], void *userdata) {
- _cleanup_(tar_import_unrefp) TarImport *import = NULL;
+ _cleanup_(tar_pull_unrefp) TarPull *pull = NULL;
_cleanup_event_unref_ sd_event *event = NULL;
const char *url, *local;
_cleanup_free_ char *l = NULL, *ll = NULL;
@@ -112,11 +113,11 @@ static int pull_tar(int argc, char *argv[], void *userdata) {
sd_event_add_signal(event, NULL, SIGTERM, interrupt_signal_handler, NULL);
sd_event_add_signal(event, NULL, SIGINT, interrupt_signal_handler, NULL);
- r = tar_import_new(&import, event, arg_image_root, on_tar_finished, event);
+ r = tar_pull_new(&pull, event, arg_image_root, on_tar_finished, event);
if (r < 0)
- return log_error_errno(r, "Failed to allocate importer: %m");
+ return log_error_errno(r, "Failed to allocate puller: %m");
- r = tar_import_pull(import, url, local, arg_force, arg_verify);
+ r = tar_pull_start(pull, url, local, arg_force, arg_verify);
if (r < 0)
return log_error_errno(r, "Failed to pull image: %m");
@@ -128,9 +129,9 @@ static int pull_tar(int argc, char *argv[], void *userdata) {
return -r;
}
-static void on_raw_finished(RawImport *import, int error, void *userdata) {
+static void on_raw_finished(RawPull *pull, int error, void *userdata) {
sd_event *event = userdata;
- assert(import);
+ assert(pull);
if (error == 0)
log_info("Operation completed successfully.");
@@ -139,7 +140,7 @@ static void on_raw_finished(RawImport *import, int error, void *userdata) {
}
static int pull_raw(int argc, char *argv[], void *userdata) {
- _cleanup_(raw_import_unrefp) RawImport *import = NULL;
+ _cleanup_(raw_pull_unrefp) RawPull *pull = NULL;
_cleanup_event_unref_ sd_event *event = NULL;
const char *url, *local;
_cleanup_free_ char *l = NULL, *ll = NULL;
@@ -198,11 +199,11 @@ static int pull_raw(int argc, char *argv[], void *userdata) {
sd_event_add_signal(event, NULL, SIGTERM, interrupt_signal_handler, NULL);
sd_event_add_signal(event, NULL, SIGINT, interrupt_signal_handler, NULL);
- r = raw_import_new(&import, event, arg_image_root, on_raw_finished, event);
+ r = raw_pull_new(&pull, event, arg_image_root, on_raw_finished, event);
if (r < 0)
- return log_error_errno(r, "Failed to allocate importer: %m");
+ return log_error_errno(r, "Failed to allocate puller: %m");
- r = raw_import_pull(import, url, local, arg_force, arg_verify);
+ r = raw_pull_start(pull, url, local, arg_force, arg_verify);
if (r < 0)
return log_error_errno(r, "Failed to pull image: %m");
@@ -214,9 +215,9 @@ static int pull_raw(int argc, char *argv[], void *userdata) {
return -r;
}
-static void on_dkr_finished(DkrImport *import, int error, void *userdata) {
+static void on_dkr_finished(DkrPull *pull, int error, void *userdata) {
sd_event *event = userdata;
- assert(import);
+ assert(pull);
if (error == 0)
log_info("Operation completed successfully.");
@@ -225,9 +226,9 @@ static void on_dkr_finished(DkrImport *import, int error, void *userdata) {
}
static int pull_dkr(int argc, char *argv[], void *userdata) {
- _cleanup_(dkr_import_unrefp) DkrImport *import = NULL;
+ _cleanup_(dkr_pull_unrefp) DkrPull *pull = NULL;
_cleanup_event_unref_ sd_event *event = NULL;
- const char *name, *tag, *local;
+ const char *name, *reference, *local, *digest;
int r;
if (!arg_dkr_index_url) {
@@ -236,17 +237,23 @@ static int pull_dkr(int argc, char *argv[], void *userdata) {
}
if (arg_verify != IMPORT_VERIFY_NO) {
- log_error("Imports from dkr do not support image verification, please pass --verify=no.");
+ log_error("Pulls from dkr do not support image verification, please pass --verify=no.");
return -EINVAL;
}
- tag = strchr(argv[1], ':');
- if (tag) {
- name = strndupa(argv[1], tag - argv[1]);
- tag++;
+ digest = strchr(argv[1], '@');
+ if (digest) {
+ reference = digest + 1;
+ name = strndupa(argv[1], digest - argv[1]);
+ }
+
+ reference = strchr(argv[1], ':');
+ if (reference) {
+ name = strndupa(argv[1], reference - argv[1]);
+ reference++;
} else {
name = argv[1];
- tag = "latest";
+ reference = "latest";
}
if (!dkr_name_is_valid(name)) {
@@ -254,8 +261,8 @@ static int pull_dkr(int argc, char *argv[], void *userdata) {
return -EINVAL;
}
- if (!dkr_tag_is_valid(tag)) {
- log_error("Tag name '%s' is not valid.", tag);
+ if (!dkr_ref_is_valid(reference)) {
+ log_error("Tag name '%s' is not valid.", reference);
return -EINVAL;
}
@@ -288,9 +295,9 @@ static int pull_dkr(int argc, char *argv[], void *userdata) {
}
}
- log_info("Pulling '%s' with tag '%s', saving as '%s'.", name, tag, local);
+ log_info("Pulling '%s' with reference '%s', saving as '%s'.", name, reference, local);
} else
- log_info("Pulling '%s' with tag '%s'.", name, tag);
+ log_info("Pulling '%s' with reference '%s'.", name, reference);
r = sd_event_default(&event);
if (r < 0)
@@ -300,11 +307,11 @@ static int pull_dkr(int argc, char *argv[], void *userdata) {
sd_event_add_signal(event, NULL, SIGTERM, interrupt_signal_handler, NULL);
sd_event_add_signal(event, NULL, SIGINT, interrupt_signal_handler, NULL);
- r = dkr_import_new(&import, event, arg_dkr_index_url, arg_image_root, on_dkr_finished, event);
+ r = dkr_pull_new(&pull, event, arg_dkr_index_url, arg_image_root, on_dkr_finished, event);
if (r < 0)
- return log_error_errno(r, "Failed to allocate importer: %m");
+ return log_error_errno(r, "Failed to allocate puller: %m");
- r = dkr_import_pull(import, name, tag, local, arg_force);
+ r = dkr_pull_start(pull, name, reference, local, arg_force, DKR_PULL_V2);
if (r < 0)
return log_error_errno(r, "Failed to pull image: %m");
@@ -319,13 +326,13 @@ static int pull_dkr(int argc, char *argv[], void *userdata) {
static int help(int argc, char *argv[], void *userdata) {
printf("%s [OPTIONS...] {COMMAND} ...\n\n"
- "Import container or virtual machine image.\n\n"
+ "Download container or virtual machine images.\n\n"
" -h --help Show this help\n"
" --version Show package version\n"
" --force Force creation of image\n"
" --verify= Verify downloaded image, one of: 'no',\n"
" 'checksum', 'signature'.\n"
- " --image-root= Image root directory\n"
+ " --image-root=PATH Image root directory\n"
" --dkr-index-url=URL Specify index URL to use for downloads\n\n"
"Commands:\n"
" tar URL [NAME] Download a TAR image\n"
@@ -409,7 +416,7 @@ static int parse_argv(int argc, char *argv[]) {
return 1;
}
-static int import_main(int argc, char *argv[]) {
+static int pull_main(int argc, char *argv[]) {
static const Verb verbs[] = {
{ "help", VERB_ANY, VERB_ANY, 0, help },
@@ -433,7 +440,9 @@ int main(int argc, char *argv[]) {
if (r <= 0)
goto finish;
- r = import_main(argc, argv);
+ ignore_signals(SIGPIPE, -1);
+
+ r = pull_main(argc, argv);
finish:
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
diff --git a/src/import/qcow2-util.c b/src/import/qcow2-util.c
index 9b0c23bb14..fd3cf1b0e3 100644
--- a/src/import/qcow2-util.c
+++ b/src/import/qcow2-util.c
@@ -177,7 +177,7 @@ static int normalize_offset(
uint64_t sz, csize_shift, csize_mask;
if (!compressed)
- return -ENOTSUP;
+ return -EOPNOTSUPP;
csize_shift = 64 - 2 - (HEADER_CLUSTER_BITS(header) - 8);
csize_mask = (1ULL << (HEADER_CLUSTER_BITS(header) - 8)) - 1;
@@ -216,10 +216,10 @@ static int verify_header(const Header *header) {
if (HEADER_VERSION(header) != 2 &&
HEADER_VERSION(header) != 3)
- return -ENOTSUP;
+ return -EOPNOTSUPP;
if (HEADER_CRYPT_METHOD(header) != 0)
- return -ENOTSUP;
+ return -EOPNOTSUPP;
if (HEADER_CLUSTER_BITS(header) < 9) /* 512K */
return -EBADMSG;
@@ -236,7 +236,7 @@ static int verify_header(const Header *header) {
if (HEADER_VERSION(header) == 3) {
if (header->incompatible_features != 0)
- return -ENOTSUP;
+ return -EOPNOTSUPP;
if (HEADER_HEADER_LENGTH(header) < sizeof(Header))
return -EBADMSG;
diff --git a/src/initctl/initctl.c b/src/initctl/initctl.c
index 7a6a383b32..19d6468fcc 100644
--- a/src/initctl/initctl.c
+++ b/src/initctl/initctl.c
@@ -19,18 +19,10 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <sys/socket.h>
-#include <sys/types.h>
-#include <assert.h>
-#include <time.h>
-#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
-#include <poll.h>
#include <sys/epoll.h>
-#include <sys/un.h>
-#include <fcntl.h>
#include <ctype.h>
#include "sd-daemon.h"
@@ -44,6 +36,7 @@
#include "bus-util.h"
#include "bus-error.h"
#include "def.h"
+#include "formats-util.h"
#define SERVER_FD_MAX 16
#define TIMEOUT_MSEC ((int) (DEFAULT_EXIT_USEC/USEC_PER_MSEC))
@@ -78,15 +71,15 @@ static const char *translate_runlevel(int runlevel, bool *isolate) {
const char *special;
bool isolate;
} table[] = {
- { '0', SPECIAL_POWEROFF_TARGET, false },
- { '1', SPECIAL_RESCUE_TARGET, true },
- { 's', SPECIAL_RESCUE_TARGET, true },
- { 'S', SPECIAL_RESCUE_TARGET, true },
- { '2', SPECIAL_RUNLEVEL2_TARGET, true },
- { '3', SPECIAL_RUNLEVEL3_TARGET, true },
- { '4', SPECIAL_RUNLEVEL4_TARGET, true },
- { '5', SPECIAL_RUNLEVEL5_TARGET, true },
- { '6', SPECIAL_REBOOT_TARGET, false },
+ { '0', SPECIAL_POWEROFF_TARGET, false },
+ { '1', SPECIAL_RESCUE_TARGET, true },
+ { 's', SPECIAL_RESCUE_TARGET, true },
+ { 'S', SPECIAL_RESCUE_TARGET, true },
+ { '2', SPECIAL_MULTI_USER_TARGET, true },
+ { '3', SPECIAL_MULTI_USER_TARGET, true },
+ { '4', SPECIAL_MULTI_USER_TARGET, true },
+ { '5', SPECIAL_GRAPHICAL_TARGET, true },
+ { '6', SPECIAL_REBOOT_TARGET, false },
};
unsigned i;
diff --git a/src/journal-remote/journal-gatewayd.c b/src/journal-remote/journal-gatewayd.c
index 576f7cae7d..d9450ae8cd 100644
--- a/src/journal-remote/journal-gatewayd.c
+++ b/src/journal-remote/journal-gatewayd.c
@@ -42,6 +42,7 @@
#include "build.h"
#include "fileio.h"
#include "sigbus.h"
+#include "hostname-util.h"
static char *arg_key_pem = NULL;
static char *arg_cert_pem = NULL;
@@ -121,6 +122,26 @@ static int open_journal(RequestMeta *m) {
return sd_journal_open(&m->journal, SD_JOURNAL_LOCAL_ONLY|SD_JOURNAL_SYSTEM);
}
+static int request_meta_ensure_tmp(RequestMeta *m) {
+ if (m->tmp)
+ rewind(m->tmp);
+ else {
+ int fd;
+
+ fd = open_tmpfile("/tmp", O_RDWR|O_CLOEXEC);
+ if (fd < 0)
+ return fd;
+
+ m->tmp = fdopen(fd, "rw");
+ if (!m->tmp) {
+ safe_close(fd);
+ return -errno;
+ }
+ }
+
+ return 0;
+}
+
static ssize_t request_reader_entries(
void *cls,
uint64_t pos,
@@ -194,14 +215,10 @@ static ssize_t request_reader_entries(
m->n_skip = 0;
- if (m->tmp)
- rewind(m->tmp);
- else {
- m->tmp = tmpfile();
- if (!m->tmp) {
- log_error_errno(errno, "Failed to create temporary file: %m");
- return MHD_CONTENT_READER_END_WITH_ERROR;
- }
+ r = request_meta_ensure_tmp(m);
+ if (r < 0) {
+ log_error_errno(r, "Failed to create temporary file: %m");
+ return MHD_CONTENT_READER_END_WITH_ERROR;
}
r = output_journal(m->tmp, m->journal, m->mode, 0, OUTPUT_FULL_WIDTH, NULL);
@@ -555,14 +572,10 @@ static ssize_t request_reader_fields(
if (m->n_fields_set)
m->n_fields -= 1;
- if (m->tmp)
- rewind(m->tmp);
- else {
- m->tmp = tmpfile();
- if (!m->tmp) {
- log_error_errno(errno, "Failed to create temporary file: %m");
- return MHD_CONTENT_READER_END_WITH_ERROR;
- }
+ r = request_meta_ensure_tmp(m);
+ if (r < 0) {
+ log_error_errno(r, "Failed to create temporary file: %m");
+ return MHD_CONTENT_READER_END_WITH_ERROR;
}
r = output_field(m->tmp, m->mode, d, l);
@@ -736,7 +749,7 @@ static int request_handler_machine(
RequestMeta *m = connection_cls;
int r;
_cleanup_free_ char* hostname = NULL, *os_name = NULL;
- uint64_t cutoff_from = 0, cutoff_to = 0, usage;
+ uint64_t cutoff_from = 0, cutoff_to = 0, usage = 0;
char *json;
sd_id128_t mid, bid;
_cleanup_free_ char *v = NULL;
@@ -769,7 +782,7 @@ static int request_handler_machine(
return mhd_respondf(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, "Failed to determine disk usage: %s\n", strerror(-r));
if (parse_env_file("/etc/os-release", NEWLINE, "PRETTY_NAME", &os_name, NULL) == -ENOENT)
- parse_env_file("/usr/lib/os-release", NEWLINE, "PRETTY_NAME", &os_name, NULL);
+ (void) parse_env_file("/usr/lib/os-release", NEWLINE, "PRETTY_NAME", &os_name, NULL);
get_virtualization(&v);
@@ -982,10 +995,9 @@ int main(int argc, char *argv[]) {
sigbus_install();
-#ifdef HAVE_GNUTLS
- gnutls_global_set_log_function(log_func_gnutls);
- log_reset_gnutls_level();
-#endif
+ r = setup_gnutls_logger(NULL);
+ if (r < 0)
+ return EXIT_FAILURE;
n = sd_listen_fds(1);
if (n < 0) {
diff --git a/src/journal-remote/journal-remote-parse.c b/src/journal-remote/journal-remote-parse.c
index d9dea8deb0..5ff05d3ad6 100644
--- a/src/journal-remote/journal-remote-parse.c
+++ b/src/journal-remote/journal-remote-parse.c
@@ -41,6 +41,7 @@ void source_free(RemoteSource *source) {
writer_unref(source->writer);
sd_event_source_unref(source->event);
+ sd_event_source_unref(source->buffer_event);
free(source);
}
@@ -111,21 +112,26 @@ static int get_line(RemoteSource *source, char **line, size_t *size) {
if (source->passive_fd)
/* we have to wait for some data to come to us */
- return -EWOULDBLOCK;
+ return -EAGAIN;
+ /* We know that source->filled is at most DATA_SIZE_MAX, so if
+ we reallocate it, we'll increase the size at least a bit. */
+ assert_cc(DATA_SIZE_MAX < ENTRY_SIZE_MAX);
if (source->size - source->filled < LINE_CHUNK &&
- !realloc_buffer(source,
- MIN(source->filled + LINE_CHUNK, ENTRY_SIZE_MAX)))
+ !realloc_buffer(source, MIN(source->filled + LINE_CHUNK, ENTRY_SIZE_MAX)))
return log_oom();
+ assert(source->buf);
assert(source->size - source->filled >= LINE_CHUNK ||
source->size == ENTRY_SIZE_MAX);
- n = read(source->fd, source->buf + source->filled,
+ n = read(source->fd,
+ source->buf + source->filled,
source->size - source->filled);
if (n < 0) {
- if (errno != EAGAIN && errno != EWOULDBLOCK)
- log_error_errno(errno, "read(%d, ..., %zu): %m", source->fd,
+ if (errno != EAGAIN)
+ log_error_errno(errno, "read(%d, ..., %zu): %m",
+ source->fd,
source->size - source->filled);
return -errno;
} else if (n == 0)
@@ -177,7 +183,7 @@ static int fill_fixed_size(RemoteSource *source, void **data, size_t size) {
if (source->passive_fd)
/* we have to wait for some data to come to us */
- return -EWOULDBLOCK;
+ return -EAGAIN;
if (!realloc_buffer(source, source->offset + size))
return log_oom();
@@ -185,7 +191,7 @@ static int fill_fixed_size(RemoteSource *source, void **data, size_t size) {
n = read(source->fd, source->buf + source->filled,
source->size - source->filled);
if (n < 0) {
- if (errno != EAGAIN && errno != EWOULDBLOCK)
+ if (errno != EAGAIN)
log_error_errno(errno, "read(%d, ..., %zu): %m", source->fd,
source->size - source->filled);
return -errno;
@@ -309,13 +315,13 @@ static int process_dunder(RemoteSource *source, char *line, size_t n) {
return 0;
}
-int process_data(RemoteSource *source) {
+static int process_data(RemoteSource *source) {
int r;
switch(source->state) {
case STATE_LINE: {
char *line, *sep;
- size_t n;
+ size_t n = 0;
assert(source->data_size == 0);
@@ -344,22 +350,25 @@ int process_data(RemoteSource *source) {
LLLLLLLL0011223344...\n
*/
sep = memchr(line, '=', n);
- if (sep)
+ if (sep) {
/* chomp newline */
n--;
- else
+
+ r = iovw_put(&source->iovw, line, n);
+ if (r < 0)
+ return r;
+ } else {
/* replace \n with = */
line[n-1] = '=';
- log_trace("Received: %.*s", (int) n, line);
- r = iovw_put(&source->iovw, line, n);
- if (r < 0) {
- log_error("Failed to put line in iovect");
- return r;
+ source->field_len = n;
+ source->state = STATE_DATA_START;
+
+ /* we cannot put the field in iovec until we have all data */
}
- if (!sep)
- source->state = STATE_DATA_START;
+ log_trace("Received: %.*s (%s)", (int) n, line, sep ? "text" : "binary");
+
return 0; /* continue */
}
@@ -382,6 +391,7 @@ int process_data(RemoteSource *source) {
case STATE_DATA: {
void *data;
+ char *field;
assert(source->data_size > 0);
@@ -396,11 +406,12 @@ int process_data(RemoteSource *source) {
assert(data);
- r = iovw_put(&source->iovw, data, source->data_size);
- if (r < 0) {
- log_error("failed to put binary buffer in iovect");
+ field = (char*) data - sizeof(uint64_t) - source->field_len;
+ memmove(field + sizeof(uint64_t), field, source->field_len);
+
+ r = iovw_put(&source->iovw, field + sizeof(uint64_t), source->field_len + source->data_size);
+ if (r < 0)
return r;
- }
source->state = STATE_DATA_FINISH;
@@ -438,7 +449,7 @@ int process_source(RemoteSource *source, bool compress, bool seal) {
return r;
/* We have a full event */
- log_trace("Received a full event from source@%p fd:%d (%s)",
+ log_trace("Received full event from source@%p fd:%d (%s)",
source, source->fd, source->name);
if (!source->iovw.count) {
diff --git a/src/journal-remote/journal-remote-parse.h b/src/journal-remote/journal-remote-parse.h
index 8499f4eb82..14bfadc132 100644
--- a/src/journal-remote/journal-remote-parse.h
+++ b/src/journal-remote/journal-remote-parse.h
@@ -42,7 +42,9 @@ typedef struct RemoteSource {
size_t offset; /* offset to the beginning of live data in the buffer */
size_t scanned; /* number of bytes since the beginning of data without a newline */
size_t filled; /* total number of bytes in the buffer */
- size_t data_size; /* size of the binary data chunk being processed */
+
+ size_t field_len; /* used for binary fields: the field name length */
+ size_t data_size; /* and the size of the binary data chunk being processed */
struct iovec_wrapper iovw;
@@ -52,6 +54,7 @@ typedef struct RemoteSource {
Writer *writer;
sd_event_source *event;
+ sd_event_source *buffer_event;
} RemoteSource;
RemoteSource* source_new(int fd, bool passive_fd, char *name, Writer *writer);
@@ -63,6 +66,5 @@ static inline size_t source_non_empty(RemoteSource *source) {
}
void source_free(RemoteSource *source);
-int process_data(RemoteSource *source);
int push_data(RemoteSource *source, const char *data, size_t size);
int process_source(RemoteSource *source, bool compress, bool seal);
diff --git a/src/journal-remote/journal-remote-write.c b/src/journal-remote/journal-remote-write.c
index df30049397..99820fa7b8 100644
--- a/src/journal-remote/journal-remote-write.c
+++ b/src/journal-remote/journal-remote-write.c
@@ -156,7 +156,7 @@ int writer_write(Writer *w,
if (r < 0)
return r;
else
- log_info("%s: Successfully rotated journal", w->journal->path);
+ log_debug("%s: Successfully rotated journal", w->journal->path);
log_debug("Retrying write.");
r = journal_file_append_entry(w->journal, ts, iovw->iovec, iovw->count,
diff --git a/src/journal-remote/journal-remote-write.h b/src/journal-remote/journal-remote-write.h
index aa381c661e..7f47f8b014 100644
--- a/src/journal-remote/journal-remote-write.h
+++ b/src/journal-remote/journal-remote-write.h
@@ -21,7 +21,6 @@
#pragma once
-#include <stdlib.h>
#include "journal-file.h"
diff --git a/src/journal-remote/journal-remote.c b/src/journal-remote/journal-remote.c
index 8f32a9a988..911e2a178b 100644
--- a/src/journal-remote/journal-remote.c
+++ b/src/journal-remote/journal-remote.c
@@ -26,22 +26,19 @@
#include <string.h>
#include <sys/prctl.h>
#include <sys/socket.h>
-#include <sys/stat.h>
-#include <sys/types.h>
#include <unistd.h>
#include <getopt.h>
#include "sd-daemon.h"
+#include "signal-util.h"
#include "journal-file.h"
#include "journald-native.h"
#include "socket-util.h"
-#include "mkdir.h"
#include "build.h"
#include "macro.h"
#include "strv.h"
#include "fileio.h"
#include "conf-parser.h"
-#include "siphash24.h"
#ifdef HAVE_GNUTLS
#include <gnutls/gnutls.h>
@@ -147,7 +144,7 @@ static int spawn_getter(const char *getter, const char *url) {
_cleanup_strv_free_ char **words = NULL;
assert(getter);
- r = strv_split_quoted(&words, getter, false);
+ r = strv_split_quoted(&words, getter, 0);
if (r < 0)
return log_error_errno(r, "Failed to split getter option: %m");
@@ -207,7 +204,7 @@ static int open_output(Writer *w, const char* host) {
log_error_errno(r, "Failed to open output journal %s: %m",
output);
else
- log_info("Opened output file %s", w->journal->path);
+ log_debug("Opened output file %s", w->journal->path);
return r;
}
@@ -289,6 +286,8 @@ static int dispatch_raw_source_event(sd_event_source *event,
int fd,
uint32_t revents,
void *userdata);
+static int dispatch_raw_source_until_block(sd_event_source *event,
+ void *userdata);
static int dispatch_blocking_source_event(sd_event_source *event,
void *userdata);
static int dispatch_raw_connection_event(sd_event_source *event,
@@ -351,7 +350,7 @@ static int remove_source(RemoteServer *s, int fd) {
static int add_source(RemoteServer *s, int fd, char* name, bool own_name) {
- RemoteSource *source;
+ RemoteSource *source = NULL;
int r;
/* This takes ownership of name, even on failure, if own_name is true. */
@@ -376,8 +375,15 @@ static int add_source(RemoteServer *s, int fd, char* name, bool own_name) {
r = sd_event_add_io(s->events, &source->event,
fd, EPOLLIN|EPOLLRDHUP|EPOLLPRI,
- dispatch_raw_source_event, s);
- if (r == -EPERM) {
+ dispatch_raw_source_event, source);
+ if (r == 0) {
+ /* Add additional source for buffer processing. It will be
+ * enabled later. */
+ r = sd_event_add_defer(s->events, &source->buffer_event,
+ dispatch_raw_source_until_block, source);
+ if (r == 0)
+ sd_event_source_set_enabled(source->buffer_event, SD_EVENT_OFF);
+ } else if (r == -EPERM) {
log_debug("Falling back to sd_event_add_defer for fd:%d (%s)", fd, name);
r = sd_event_add_defer(s->events, &source->event,
dispatch_blocking_source_event, source);
@@ -511,7 +517,7 @@ static int process_http_upload(
while (true) {
r = process_source(source, arg_compress, arg_seal);
- if (r == -EAGAIN || r == -EWOULDBLOCK)
+ if (r == -EAGAIN)
break;
else if (r < 0) {
log_warning("Failed to process data for connection %p", connection);
@@ -693,7 +699,7 @@ static int setup_microhttpd_server(RemoteServer *s,
info = MHD_get_daemon_info(d->daemon, MHD_DAEMON_INFO_EPOLL_FD_LINUX_ONLY);
if (!info) {
log_error("µhttp returned NULL daemon info");
- r = -ENOTSUP;
+ r = -EOPNOTSUPP;
goto error;
}
@@ -747,7 +753,7 @@ static int setup_microhttpd_socket(RemoteServer *s,
const char *trust) {
int fd;
- fd = make_socket_fd(LOG_INFO, address, SOCK_STREAM | SOCK_CLOEXEC);
+ fd = make_socket_fd(LOG_DEBUG, address, SOCK_STREAM | SOCK_CLOEXEC);
if (fd < 0)
return fd;
@@ -844,7 +850,7 @@ static int remoteserver_init(RemoteServer *s,
if (n < 0)
return log_error_errno(n, "Failed to read listening file descriptors from environment: %m");
else
- log_info("Received %d descriptors", n);
+ log_debug("Received %d descriptors", n);
if (MAX(http_socket, https_socket) >= SD_LISTEN_FDS_START + n) {
log_error("Received fewer sockets than expected");
@@ -853,7 +859,7 @@ static int remoteserver_init(RemoteServer *s,
for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd++) {
if (sd_is_socket(fd, AF_UNSPEC, 0, true)) {
- log_info("Received a listening socket (fd:%d)", fd);
+ log_debug("Received a listening socket (fd:%d)", fd);
if (fd == http_socket)
r = setup_microhttpd_server(s, fd, NULL, NULL, NULL);
@@ -868,7 +874,7 @@ static int remoteserver_init(RemoteServer *s,
if (r < 0)
return log_error_errno(r, "Failed to retrieve remote name: %m");
- log_info("Received a connection socket (fd:%d) from %s", fd, hostname);
+ log_debug("Received a connection socket (fd:%d) from %s", fd, hostname);
r = add_source(s, fd, hostname, true);
} else {
@@ -908,7 +914,7 @@ static int remoteserver_init(RemoteServer *s,
}
if (arg_listen_raw) {
- log_info("Listening on a socket...");
+ log_debug("Listening on a socket...");
r = setup_raw_socket(s, arg_listen_raw);
if (r < 0)
return r;
@@ -930,12 +936,12 @@ static int remoteserver_init(RemoteServer *s,
const char *output_name;
if (streq(*file, "-")) {
- log_info("Using standard input as source.");
+ log_debug("Using standard input as source.");
fd = STDIN_FILENO;
output_name = "stdin";
} else {
- log_info("Reading file %s...", *file);
+ log_debug("Reading file %s...", *file);
fd = open(*file, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
if (fd < 0)
@@ -997,15 +1003,18 @@ static void server_destroy(RemoteServer *s) {
**********************************************************************
**********************************************************************/
-static int dispatch_raw_source_event(sd_event_source *event,
- int fd,
- uint32_t revents,
- void *userdata) {
+static int handle_raw_source(sd_event_source *event,
+ int fd,
+ uint32_t revents,
+ RemoteServer *s) {
- RemoteServer *s = userdata;
RemoteSource *source;
int r;
+ /* Returns 1 if there might be more data pending,
+ * 0 if data is currently exhausted, negative on error.
+ */
+
assert(fd >= 0 && fd < (ssize_t) s->sources_size);
source = s->sources[fd];
assert(source->fd == fd);
@@ -1014,33 +1023,70 @@ static int dispatch_raw_source_event(sd_event_source *event,
if (source->state == STATE_EOF) {
size_t remaining;
- log_info("EOF reached with source fd:%d (%s)",
- source->fd, source->name);
+ log_debug("EOF reached with source fd:%d (%s)",
+ source->fd, source->name);
remaining = source_non_empty(source);
if (remaining > 0)
- log_warning("Premature EOF. %zu bytes lost.", remaining);
+ log_notice("Premature EOF. %zu bytes lost.", remaining);
remove_source(s, source->fd);
- log_info("%zu active sources remaining", s->active);
+ log_debug("%zu active sources remaining", s->active);
return 0;
} else if (r == -E2BIG) {
- log_error("Entry too big, skipped");
+ log_notice_errno(E2BIG, "Entry too big, skipped");
return 1;
} else if (r == -EAGAIN) {
return 0;
} else if (r < 0) {
- log_info_errno(r, "Closing connection: %m");
+ log_debug_errno(r, "Closing connection: %m");
remove_source(server, fd);
return 0;
} else
return 1;
}
+static int dispatch_raw_source_until_block(sd_event_source *event,
+ void *userdata) {
+ RemoteSource *source = userdata;
+ int r;
+
+ /* Make sure event stays around even if source is destroyed */
+ sd_event_source_ref(event);
+
+ r = handle_raw_source(event, source->fd, EPOLLIN, server);
+ if (r != 1)
+ /* No more data for now */
+ sd_event_source_set_enabled(event, SD_EVENT_OFF);
+
+ sd_event_source_unref(event);
+
+ return r;
+}
+
+static int dispatch_raw_source_event(sd_event_source *event,
+ int fd,
+ uint32_t revents,
+ void *userdata) {
+ RemoteSource *source = userdata;
+ int r;
+
+ assert(source->event);
+ assert(source->buffer_event);
+
+ r = handle_raw_source(event, fd, EPOLLIN, server);
+ if (r == 1)
+ /* Might have more data. We need to rerun the handler
+ * until we are sure the buffer is exhausted. */
+ sd_event_source_set_enabled(source->buffer_event, SD_EVENT_ON);
+
+ return r;
+}
+
static int dispatch_blocking_source_event(sd_event_source *event,
void *userdata) {
RemoteSource *source = userdata;
- return dispatch_raw_source_event(event, source->fd, EPOLLIN, server);
+ return handle_raw_source(event, source->fd, EPOLLIN, server);
}
static int accept_connection(const char* type, int fd,
@@ -1071,10 +1117,10 @@ static int accept_connection(const char* type, int fd,
return r;
}
- log_info("Accepted %s %s connection from %s",
- type,
- socket_address_family(addr) == AF_INET ? "IP" : "IPv6",
- a);
+ log_debug("Accepted %s %s connection from %s",
+ type,
+ socket_address_family(addr) == AF_INET ? "IP" : "IPv6",
+ a);
*hostname = b;
@@ -1099,7 +1145,7 @@ static int dispatch_raw_connection_event(sd_event_source *event,
.size = sizeof(union sockaddr_union),
.type = SOCK_STREAM,
};
- char *hostname;
+ char *hostname = NULL;
fd2 = accept_connection("raw", fd, &addr, &hostname);
if (fd2 < 0)
@@ -1458,31 +1504,6 @@ static int load_certificates(char **key, char **cert, char **trust) {
return 0;
}
-static int setup_gnutls_logger(char **categories) {
- if (!arg_listen_http && !arg_listen_https)
- return 0;
-
-#ifdef HAVE_GNUTLS
- {
- char **cat;
- int r;
-
- gnutls_global_set_log_function(log_func_gnutls);
-
- if (categories) {
- STRV_FOREACH(cat, categories) {
- r = log_enable_gnutls_category(*cat);
- if (r < 0)
- return r;
- }
- } else
- log_reset_gnutls_level();
- }
-#endif
-
- return 0;
-}
-
int main(int argc, char **argv) {
RemoteServer s = {};
int r;
@@ -1499,9 +1520,12 @@ int main(int argc, char **argv) {
if (r <= 0)
return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
- r = setup_gnutls_logger(arg_gnutls_log);
- if (r < 0)
- return EXIT_FAILURE;
+
+ if (arg_listen_http || arg_listen_https) {
+ r = setup_gnutls_logger(arg_gnutls_log);
+ if (r < 0)
+ return EXIT_FAILURE;
+ }
if (arg_listen_https || https_socket >= 0)
if (load_certificates(&key, &cert, &trust) < 0)
diff --git a/src/journal-remote/journal-remote.h b/src/journal-remote/journal-remote.h
index 1cf22f6383..6c2ccb9735 100644
--- a/src/journal-remote/journal-remote.h
+++ b/src/journal-remote/journal-remote.h
@@ -21,7 +21,6 @@
#pragma once
-#include <inttypes.h>
#include "sd-event.h"
#include "hashmap.h"
diff --git a/src/journal-remote/journal-upload.c b/src/journal-remote/journal-upload.c
index 75bb434c08..ddb1ef0396 100644
--- a/src/journal-remote/journal-upload.c
+++ b/src/journal-remote/journal-upload.c
@@ -33,6 +33,8 @@
#include "mkdir.h"
#include "conf-parser.h"
#include "sigbus.h"
+#include "formats-util.h"
+#include "signal-util.h"
#include "journal-upload.h"
#define PRIV_KEY_FILE CERTIFICATE_ROOT "/private/journal-upload.pem"
diff --git a/src/journal-remote/microhttpd-util.c b/src/journal-remote/microhttpd-util.c
index 34d93379da..8a11fba044 100644
--- a/src/journal-remote/microhttpd-util.c
+++ b/src/journal-remote/microhttpd-util.c
@@ -121,7 +121,7 @@ static struct {
{ {"9", "enc", "int"}, LOG_DEBUG },
};
-void log_func_gnutls(int level, const char *message) {
+static void log_func_gnutls(int level, const char *message) {
assert_se(message);
if (0 <= level && level < (int) ELEMENTSOF(gnutls_log_map)) {
@@ -133,7 +133,18 @@ void log_func_gnutls(int level, const char *message) {
}
}
-int log_enable_gnutls_category(const char *cat) {
+static void log_reset_gnutls_level(void) {
+ int i;
+
+ for (i = ELEMENTSOF(gnutls_log_map) - 1; i >= 0; i--)
+ if (gnutls_log_map[i].enabled) {
+ log_debug("Setting gnutls log level to %d", i);
+ gnutls_global_set_log_level(i);
+ break;
+ }
+}
+
+static int log_enable_gnutls_category(const char *cat) {
unsigned i;
if (streq(cat, "all")) {
@@ -152,15 +163,22 @@ int log_enable_gnutls_category(const char *cat) {
return -EINVAL;
}
-void log_reset_gnutls_level(void) {
- int i;
+int setup_gnutls_logger(char **categories) {
+ char **cat;
+ int r;
- for (i = ELEMENTSOF(gnutls_log_map) - 1; i >= 0; i--)
- if (gnutls_log_map[i].enabled) {
- log_debug("Setting gnutls log level to %d", i);
- gnutls_global_set_log_level(i);
- break;
+ gnutls_global_set_log_function(log_func_gnutls);
+
+ if (categories) {
+ STRV_FOREACH(cat, categories) {
+ r = log_enable_gnutls_category(*cat);
+ if (r < 0)
+ return r;
}
+ } else
+ log_reset_gnutls_level();
+
+ return 0;
}
static int verify_cert_authorized(gnutls_session_t session) {
@@ -178,7 +196,8 @@ static int verify_cert_authorized(gnutls_session_t session) {
if (r < 0)
return log_error_errno(r, "gnutls_certificate_verification_status_print failed: %m");
- log_info("Certificate status: %s", out.data);
+ log_debug("Certificate status: %s", out.data);
+ gnutls_free(out.data);
return status == 0 ? 0 : -EPERM;
}
@@ -238,10 +257,14 @@ static int get_auth_dn(gnutls_x509_crt_t client_cert, char **buf) {
return 0;
}
+static inline void gnutls_x509_crt_deinitp(gnutls_x509_crt_t *p) {
+ gnutls_x509_crt_deinit(*p);
+}
+
int check_permissions(struct MHD_Connection *connection, int *code, char **hostname) {
const union MHD_ConnectionInfo *ci;
gnutls_session_t session;
- gnutls_x509_crt_t client_cert;
+ _cleanup_(gnutls_x509_crt_deinitp) gnutls_x509_crt_t client_cert = NULL;
_cleanup_free_ char *buf = NULL;
int r;
@@ -275,7 +298,7 @@ int check_permissions(struct MHD_Connection *connection, int *code, char **hostn
return -EPERM;
}
- log_info("Connection from %s", buf);
+ log_debug("Connection from %s", buf);
if (hostname) {
*hostname = buf;
@@ -295,4 +318,10 @@ int check_permissions(struct MHD_Connection *connection, int *code, char **hostn
int check_permissions(struct MHD_Connection *connection, int *code, char **hostname) {
return -EPERM;
}
+
+int setup_gnutls_logger(char **categories) {
+ if (categories)
+ log_notice("Ignoring specified gnutls logging categories — gnutls not available.");
+ return 0;
+}
#endif
diff --git a/src/journal-remote/microhttpd-util.h b/src/journal-remote/microhttpd-util.h
index c43d7f75a3..b2feb9180a 100644
--- a/src/journal-remote/microhttpd-util.h
+++ b/src/journal-remote/microhttpd-util.h
@@ -43,13 +43,11 @@ int mhd_respond_oom(struct MHD_Connection *connection);
int check_permissions(struct MHD_Connection *connection, int *code, char **hostname);
-#ifdef HAVE_GNUTLS
-void log_func_gnutls(int level, const char *message);
-int log_enable_gnutls_category(const char *cat);
-void log_reset_gnutls_level(void);
-
-/* This is additionally filtered by our internal log level, so it
- * should be set fairly high to capture all potentially interesting
- * events without overwhelming detail.
+/* Set gnutls internal logging function to a callback which uses our
+ * own logging framework.
+ *
+ * gnutls categories are additionally filtered by our internal log
+ * level, so it should be set fairly high to capture all potentially
+ * interesting events without overwhelming detail.
*/
-#endif
+int setup_gnutls_logger(char **categories);
diff --git a/src/journal/.gitignore b/src/journal/.gitignore
index d6a79460cd..04d5852547 100644
--- a/src/journal/.gitignore
+++ b/src/journal/.gitignore
@@ -1,2 +1,4 @@
/journald-gperf.c
/libsystemd-journal.pc
+/audit_type-list.txt
+/audit_type-*-name.*
diff --git a/src/shared/time-dst.h b/src/journal/audit-type.c
index 536b6bbdfb..4888c7d05d 100644
--- a/src/shared/time-dst.h
+++ b/src/journal/audit-type.c
@@ -1,11 +1,9 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-#pragma once
-
/***
This file is part of systemd.
- Copyright 2012 Kay Sievers
+ Copyright 2015 Zbigniew Jędrzejewski-Szmek
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
@@ -21,6 +19,14 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-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);
+#include <stdio.h>
+#include <linux/audit.h>
+#ifdef HAVE_AUDIT
+# include <libaudit.h>
+#endif
+
+#include "audit-type.h"
+#include "macro.h"
+#include "missing.h"
+
+#include "audit_type-to-name.h"
diff --git a/src/journal/audit-type.h b/src/journal/audit-type.h
new file mode 100644
index 0000000000..fa5284e027
--- /dev/null
+++ b/src/journal/audit-type.h
@@ -0,0 +1,39 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2015 Zbigniew Jędrzejewski-Szmek
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "macro.h"
+
+const char *audit_type_to_string(int type);
+int audit_type_from_string(const char *s);
+
+/* This is inspired by DNS TYPEnnn formatting */
+#define audit_type_name_alloca(type) \
+ ({ \
+ const char *_s_; \
+ _s_ = audit_type_to_string(type); \
+ if (!_s_) { \
+ _s_ = alloca(strlen("AUDIT") + DECIMAL_STR_MAX(int)); \
+ sprintf((char*) _s_, "AUDIT%04i", type); \
+ } \
+ _s_; \
+ })
diff --git a/src/journal/cat.c b/src/journal/cat.c
index 79706b692d..2e236f0004 100644
--- a/src/journal/cat.c
+++ b/src/journal/cat.c
@@ -21,7 +21,6 @@
#include <stdio.h>
#include <getopt.h>
-#include <assert.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
diff --git a/src/journal/catalog.c b/src/journal/catalog.c
index f170232841..0801e13599 100644
--- a/src/journal/catalog.c
+++ b/src/journal/catalog.c
@@ -34,7 +34,6 @@
#include "hashmap.h"
#include "strv.h"
#include "strbuf.h"
-#include "strxcpyx.h"
#include "conf-files.h"
#include "mkdir.h"
#include "catalog.h"
@@ -559,7 +558,7 @@ static const char *find_id(void *p, sd_id128_t id) {
int catalog_get(const char* database, sd_id128_t id, char **_text) {
_cleanup_close_ int fd = -1;
void *p = NULL;
- struct stat st;
+ struct stat st = {};
char *text = NULL;
int r;
const char *s;
diff --git a/src/journal/compress.c b/src/journal/compress.c
index 6923753f89..383f6a6e96 100644
--- a/src/journal/compress.c
+++ b/src/journal/compress.c
@@ -19,7 +19,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
@@ -590,14 +589,12 @@ int decompress_stream_lz4(int fdf, int fdt, off_t max_bytes) {
return log_oom();
for (;;) {
- ssize_t n, m;
+ ssize_t m;
int r;
- n = read(fdf, &header, sizeof(header));
- if (n < 0)
- return -errno;
- if (n != sizeof(header))
- return errno ? -errno : -EIO;
+ r = loop_read_exact(fdf, &header, sizeof(header), false);
+ if (r < 0)
+ return r;
m = le32toh(header);
if (m == 0)
@@ -619,12 +616,9 @@ int decompress_stream_lz4(int fdf, int fdt, off_t max_bytes) {
if (!GREEDY_REALLOC(buf, buf_size, m))
return log_oom();
- errno = 0;
- n = loop_read(fdf, buf, m, false);
- if (n < 0)
- return n;
- if (n != m)
- return errno ? -errno : -EIO;
+ r = loop_read_exact(fdf, buf, m, false);
+ if (r < 0)
+ return r;
r = LZ4_decompress_safe_continue(&lz4_data, buf, out, m, 4*LZ4_BUFSIZE);
if (r <= 0)
@@ -637,9 +631,9 @@ int decompress_stream_lz4(int fdf, int fdt, off_t max_bytes) {
return -EFBIG;
}
- n = loop_write(fdt, out, r, false);
- if (n < 0)
- return n;
+ r = loop_write(fdt, out, r, false);
+ if (r < 0)
+ return r;
}
log_debug("LZ4 decompression finished (%zu -> %zu bytes, %.1f%%)",
diff --git a/src/journal/compress.h b/src/journal/compress.h
index 136dda6d39..6294f16faa 100644
--- a/src/journal/compress.h
+++ b/src/journal/compress.h
@@ -21,8 +21,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <inttypes.h>
-#include <stdbool.h>
#include <unistd.h>
#include "journal-def.h"
diff --git a/src/journal/coredump-vacuum.c b/src/journal/coredump-vacuum.c
index 9b73795e5b..c0347ef569 100644
--- a/src/journal/coredump-vacuum.c
+++ b/src/journal/coredump-vacuum.c
@@ -103,8 +103,7 @@ static bool vacuum_necessary(int fd, off_t sum, off_t keep_free, off_t max_use)
if (max_use < DEFAULT_MAX_USE_LOWER)
max_use = DEFAULT_MAX_USE_LOWER;
- }
- else
+ } else
max_use = DEFAULT_MAX_USE_LOWER;
} else
max_use = PAGE_ALIGN(max_use);
@@ -135,7 +134,7 @@ int coredump_vacuum(int exclude_fd, off_t keep_free, off_t max_use) {
struct stat exclude_st;
int r;
- if (keep_free <= 0 && max_use <= 0)
+ if (keep_free == 0 && max_use == 0)
return 0;
if (exclude_fd >= 0) {
diff --git a/src/journal/coredump.c b/src/journal/coredump.c
index f7ba0191e1..1c747aa2b4 100644
--- a/src/journal/coredump.c
+++ b/src/journal/coredump.c
@@ -23,7 +23,6 @@
#include <unistd.h>
#include <stdio.h>
#include <sys/prctl.h>
-#include <sys/types.h>
#include <sys/xattr.h>
#ifdef HAVE_ELFUTILS
@@ -44,12 +43,12 @@
#include "conf-parser.h"
#include "copy.h"
#include "stacktrace.h"
-#include "path-util.h"
#include "compress.h"
#include "acl-util.h"
#include "capability.h"
#include "journald-native.h"
#include "coredump-vacuum.h"
+#include "process-util.h"
/* The maximum size up to which we process coredumps */
#define PROCESS_SIZE_MAX ((off_t) (2LLU*1024LLU*1024LLU*1024LLU))
@@ -244,7 +243,7 @@ static int maybe_remove_external_coredump(const char *filename, off_t size) {
static int make_filename(const char *info[_INFO_LEN], char **ret) {
_cleanup_free_ char *c = NULL, *u = NULL, *p = NULL, *t = NULL;
- sd_id128_t boot;
+ sd_id128_t boot = {};
int r;
assert(info);
@@ -843,7 +842,7 @@ log:
/* Optionally store the entire coredump in the journal */
if (IN_SET(arg_storage, COREDUMP_STORAGE_JOURNAL, COREDUMP_STORAGE_BOTH) &&
coredump_size <= (off_t) arg_journal_size_max) {
- size_t sz;
+ size_t sz = 0;
/* Store the coredump itself in the journal */
diff --git a/src/journal/coredump.conf b/src/journal/coredump.conf
index 0fe9fe801a..c2f0643e03 100644
--- a/src/journal/coredump.conf
+++ b/src/journal/coredump.conf
@@ -5,10 +5,11 @@
# the Free Software Foundation; either version 2.1 of the License, or
# (at your option) any later version.
#
-# You can override the directives in this file by creating files in
-# /etc/systemd/coredump.conf.d/*.conf.
+# Entries in this file show the compile time defaults.
+# You can change settings by editing this file.
+# Defaults can be restored by simply deleting this file.
#
-# See coredump.conf(5) for details
+# See coredump.conf(5) for details.
[Coredump]
#Storage=external
diff --git a/src/journal/coredumpctl.c b/src/journal/coredumpctl.c
index afb39ad035..381bf72776 100644
--- a/src/journal/coredumpctl.c
+++ b/src/journal/coredumpctl.c
@@ -35,9 +35,11 @@
#include "pager.h"
#include "macro.h"
#include "journal-internal.h"
-#include "copy.h"
#include "compress.h"
#include "sigbus.h"
+#include "process-util.h"
+#include "terminal-util.h"
+#include "signal-util.h"
static enum {
ACTION_NONE,
@@ -650,7 +652,7 @@ static int save_core(sd_journal *j, int fd, char **path, bool *unlink_temp) {
}
#else
log_error("Cannot decompress file. Compiled without compression support.");
- r = -ENOTSUP;
+ r = -EOPNOTSUPP;
goto error;
#endif
} else {
diff --git a/src/journal/fsprg.c b/src/journal/fsprg.c
index 5c8d6d6feb..a9f564c249 100644
--- a/src/journal/fsprg.c
+++ b/src/journal/fsprg.c
@@ -30,7 +30,6 @@
#include <gcrypt.h>
#include <string.h>
-#include <assert.h>
#include "fsprg.h"
diff --git a/src/journal/journal-authenticate.c b/src/journal/journal-authenticate.c
index b3e2601c4e..cdc80e2d26 100644
--- a/src/journal/journal-authenticate.c
+++ b/src/journal/journal-authenticate.c
@@ -106,7 +106,7 @@ static int journal_file_get_epoch(JournalFile *f, uint64_t realtime, uint64_t *e
if (f->fss_start_usec == 0 ||
f->fss_interval_usec == 0)
- return -ENOTSUP;
+ return -EOPNOTSUPP;
if (realtime < f->fss_start_usec)
return -ESTALE;
@@ -446,7 +446,7 @@ int journal_file_hmac_setup(JournalFile *f) {
e = gcry_md_open(&f->hmac, GCRY_MD_SHA256, GCRY_MD_FLAG_HMAC);
if (e != 0)
- return -ENOTSUP;
+ return -EOPNOTSUPP;
return 0;
}
diff --git a/src/journal/journal-authenticate.h b/src/journal/journal-authenticate.h
index 565fe8432c..118bb1367b 100644
--- a/src/journal/journal-authenticate.h
+++ b/src/journal/journal-authenticate.h
@@ -22,7 +22,6 @@
***/
#include <stdbool.h>
-#include <inttypes.h>
#include "journal-file.h"
diff --git a/src/journal/journal-def.h b/src/journal/journal-def.h
index ab089cb966..39c9dd0dbf 100644
--- a/src/journal/journal-def.h
+++ b/src/journal/journal-def.h
@@ -220,7 +220,7 @@ struct Header {
le64_t n_tags;
le64_t n_entry_arrays;
- /* Size: 224 */
+ /* Size: 240 */
} _packed_;
#define FSS_HEADER_SIGNATURE ((char[]) { 'K', 'S', 'H', 'H', 'R', 'H', 'L', 'P' })
diff --git a/src/journal/journal-file.c b/src/journal/journal-file.c
index 2845e05ce0..be6a5522fa 100644
--- a/src/journal/journal-file.c
+++ b/src/journal/journal-file.c
@@ -34,7 +34,7 @@
#include "journal-authenticate.h"
#include "lookup3.h"
#include "compress.h"
-#include "fsprg.h"
+#include "random-util.h"
#define DEFAULT_DATA_HASH_TABLE_SIZE (2047ULL*sizeof(HashItem))
#define DEFAULT_FIELD_HASH_TABLE_SIZE (333ULL*sizeof(HashItem))
@@ -150,7 +150,7 @@ void journal_file_close(JournalFile *f) {
* reenable all the good bits COW usually provides
* (such as data checksumming). */
- (void) chattr_fd(f->fd, false, FS_NOCOW_FL);
+ (void) chattr_fd(f->fd, 0, FS_NOCOW_FL);
(void) btrfs_defrag_fd(f->fd);
}
@@ -888,7 +888,7 @@ int journal_file_find_data_object_with_hash(
if (o->object.flags & OBJECT_COMPRESSION_MASK) {
#if defined(HAVE_XZ) || defined(HAVE_LZ4)
uint64_t l;
- size_t rsize;
+ size_t rsize = 0;
l = le64toh(o->object.size);
if (l <= offsetof(Object, data.payload))
@@ -1053,7 +1053,7 @@ static int journal_file_append_data(
#if defined(HAVE_XZ) || defined(HAVE_LZ4)
if (f->compress_xz &&
size >= COMPRESSION_SIZE_THRESHOLD) {
- size_t rsize;
+ size_t rsize = 0;
compression = compress_blob(data, size, o->data.payload, &rsize);
@@ -2014,8 +2014,7 @@ void journal_file_reset_location(JournalFile *f) {
f->current_xor_hash = 0;
}
-void journal_file_save_location(JournalFile *f, direction_t direction, Object *o, uint64_t offset) {
- f->last_direction = direction;
+void journal_file_save_location(JournalFile *f, Object *o, uint64_t offset) {
f->location_type = LOCATION_SEEK;
f->current_offset = offset;
f->current_seqnum = le64toh(o->entry.seqnum);
@@ -2523,6 +2522,41 @@ void journal_file_print_header(JournalFile *f) {
printf("Disk usage: %s\n", format_bytes(bytes, sizeof(bytes), (off_t) st.st_blocks * 512ULL));
}
+static int journal_file_warn_btrfs(JournalFile *f) {
+ unsigned attrs;
+ int r;
+
+ assert(f);
+
+ /* Before we write anything, check if the COW logic is turned
+ * off on btrfs. Given our write pattern that is quite
+ * unfriendly to COW file systems this should greatly improve
+ * performance on COW file systems, such as btrfs, at the
+ * expense of data integrity features (which shouldn't be too
+ * bad, given that we do our own checksumming). */
+
+ r = btrfs_is_filesystem(f->fd);
+ if (r < 0)
+ return log_warning_errno(r, "Failed to determine if journal is on btrfs: %m");
+ if (!r)
+ return 0;
+
+ r = read_attr_fd(f->fd, &attrs);
+ if (r < 0)
+ return log_warning_errno(r, "Failed to read file attributes: %m");
+
+ if (attrs & FS_NOCOW_FL) {
+ log_debug("Detected btrfs file system with copy-on-write disabled, all is good.");
+ return 0;
+ }
+
+ log_notice("Creating journal file %s on a btrfs file system, and copy-on-write is enabled. "
+ "This is likely to slow down journal access substantially, please consider turning "
+ "off the copy-on-write file attribute on the journal directory, using chattr +C.", f->path);
+
+ return 1;
+}
+
int journal_file_open(
const char *fname,
int flags,
@@ -2603,16 +2637,7 @@ int journal_file_open(
if (f->last_stat.st_size == 0 && f->writable) {
- /* Before we write anything, turn off COW logic. Given
- * our write pattern that is quite unfriendly to COW
- * file systems this should greatly improve
- * performance on COW file systems, such as btrfs, at
- * the expense of data integrity features (which
- * shouldn't be too bad, given that we do our own
- * checksumming). */
- r = chattr_fd(f->fd, true, FS_NOCOW_FL);
- if (r < 0)
- log_warning_errno(errno, "Failed to set file attributes: %m");
+ (void) journal_file_warn_btrfs(f);
/* Let's attach the creation time to the journal file,
* so that the vacuuming code knows the age of this
@@ -2653,10 +2678,8 @@ int journal_file_open(
}
r = mmap_cache_get(f->mmap, f->fd, f->prot, CONTEXT_HEADER, true, 0, PAGE_ALIGN(sizeof(Header)), &f->last_stat, &h);
- if (r < 0) {
- r = -errno;
+ if (r < 0)
goto fail;
- }
f->header = h;
@@ -2797,14 +2820,15 @@ int journal_file_open_reliably(
r = journal_file_open(fname, flags, mode, compress, seal,
metrics, mmap_cache, template, ret);
- if (r != -EBADMSG && /* corrupted */
- r != -ENODATA && /* truncated */
- r != -EHOSTDOWN && /* other machine */
- r != -EPROTONOSUPPORT && /* incompatible feature */
- r != -EBUSY && /* unclean shutdown */
- r != -ESHUTDOWN && /* already archived */
- r != -EIO && /* IO error, including SIGBUS on mmap */
- r != -EIDRM /* File has been deleted */)
+ if (!IN_SET(r,
+ -EBADMSG, /* corrupted */
+ -ENODATA, /* truncated */
+ -EHOSTDOWN, /* other machine */
+ -EPROTONOSUPPORT, /* incompatible feature */
+ -EBUSY, /* unclean shutdown */
+ -ESHUTDOWN, /* already archived */
+ -EIO, /* IO error, including SIGBUS on mmap */
+ -EIDRM /* File has been deleted */))
return r;
if ((flags & O_ACCMODE) == O_RDONLY)
@@ -2819,9 +2843,9 @@ int journal_file_open_reliably(
/* The file is corrupted. Rotate it away and try it again (but only once) */
l = strlen(fname);
- if (asprintf(&p, "%.*s@%016llx-%016" PRIx64 ".journal~",
+ if (asprintf(&p, "%.*s@%016"PRIx64 "-%016"PRIx64 ".journal~",
(int) l - 8, fname,
- (unsigned long long) now(CLOCK_REALTIME),
+ now(CLOCK_REALTIME),
random_u64()) < 0)
return -ENOMEM;
@@ -2889,7 +2913,7 @@ int journal_file_copy_entry(JournalFile *from, JournalFile *to, Object *o, uint6
if (o->object.flags & OBJECT_COMPRESSION_MASK) {
#if defined(HAVE_XZ) || defined(HAVE_LZ4)
- size_t rsize;
+ size_t rsize = 0;
r = decompress_blob(o->object.flags & OBJECT_COMPRESSION_MASK,
o->data.payload, l, &from->compress_buffer, &from->compress_buffer_size, &rsize, 0);
diff --git a/src/journal/journal-file.h b/src/journal/journal-file.h
index 2526e14d65..403c8f760c 100644
--- a/src/journal/journal-file.h
+++ b/src/journal/journal-file.h
@@ -199,7 +199,7 @@ int journal_file_find_field_object(JournalFile *f, const void *field, uint64_t s
int journal_file_find_field_object_with_hash(JournalFile *f, const void *field, uint64_t size, uint64_t hash, Object **ret, uint64_t *offset);
void journal_file_reset_location(JournalFile *f);
-void journal_file_save_location(JournalFile *f, direction_t direction, Object *o, uint64_t offset);
+void journal_file_save_location(JournalFile *f, Object *o, uint64_t offset);
int journal_file_compare_locations(JournalFile *af, JournalFile *bf);
int journal_file_next_entry(JournalFile *f, uint64_t p, direction_t direction, Object **ret, uint64_t *offset);
diff --git a/src/journal/journal-qrcode.h b/src/journal/journal-qrcode.h
index c527e65553..3ff6a3ad4a 100644
--- a/src/journal/journal-qrcode.h
+++ b/src/journal/journal-qrcode.h
@@ -21,8 +21,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <inttypes.h>
-#include <sys/types.h>
#include <stdio.h>
#include "systemd/sd-id128.h"
diff --git a/src/journal/journal-vacuum.c b/src/journal/journal-vacuum.c
index 832c327b31..81a577ea27 100644
--- a/src/journal/journal-vacuum.c
+++ b/src/journal/journal-vacuum.c
@@ -19,12 +19,9 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <sys/types.h>
#include <fcntl.h>
#include <sys/stat.h>
-#include <sys/statvfs.h>
#include <unistd.h>
-#include <sys/xattr.h>
#include "journal-def.h"
#include "journal-file.h"
@@ -76,7 +73,7 @@ static void patch_realtime(
unsigned long long *realtime) {
_cleanup_free_ const char *path = NULL;
- usec_t x, crtime;
+ usec_t x, crtime = 0;
/* The timestamp was determined by the file name, but let's
* see if the file might actually be older than the file name
diff --git a/src/journal/journal-vacuum.h b/src/journal/journal-vacuum.h
index a7fb6f0f0d..c45cc31d0e 100644
--- a/src/journal/journal-vacuum.h
+++ b/src/journal/journal-vacuum.h
@@ -21,6 +21,5 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <inttypes.h>
int journal_directory_vacuum(const char *directory, uint64_t max_use, usec_t max_retention_usec, usec_t *oldest_usec, bool vacuum);
diff --git a/src/journal/journal-verify.c b/src/journal/journal-verify.c
index b03335ef31..ce734d8df7 100644
--- a/src/journal/journal-verify.c
+++ b/src/journal/journal-verify.c
@@ -32,7 +32,7 @@
#include "journal-verify.h"
#include "lookup3.h"
#include "compress.h"
-#include "fsprg.h"
+#include "terminal-util.h"
static void draw_progress(uint64_t p, usec_t *last_usec) {
unsigned n, i, j, k;
@@ -818,7 +818,7 @@ int journal_file_verify(
return r;
}
#else
- return -ENOTSUP;
+ return -EOPNOTSUPP;
#endif
} else if (f->seal)
return -ENOKEY;
@@ -846,7 +846,7 @@ int journal_file_verify(
if (le32toh(f->header->compatible_flags) & ~HEADER_COMPATIBLE_SUPPORTED) {
log_error("Cannot verify file with unknown extensions.");
- r = -ENOTSUP;
+ r = -EOPNOTSUPP;
goto fail;
}
diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c
index 55c7786331..76ec0827e7 100644
--- a/src/journal/journalctl.c
+++ b/src/journal/journalctl.c
@@ -28,18 +28,15 @@
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
-#include <time.h>
#include <getopt.h>
#include <signal.h>
#include <poll.h>
#include <sys/stat.h>
-#include <sys/ioctl.h>
#include <sys/inotify.h>
#include <linux/fs.h>
#include "sd-journal.h"
#include "sd-bus.h"
-
#include "log.h"
#include "logs-show.h"
#include "util.h"
@@ -54,7 +51,6 @@
#include "journal-internal.h"
#include "journal-def.h"
#include "journal-verify.h"
-#include "journal-authenticate.h"
#include "journal-qrcode.h"
#include "journal-vacuum.h"
#include "fsprg.h"
@@ -63,6 +59,8 @@
#include "mkdir.h"
#include "bus-util.h"
#include "bus-error.h"
+#include "terminal-util.h"
+#include "hostname-util.h"
#define DEFAULT_FSS_INTERVAL_USEC (15*USEC_PER_MINUTE)
@@ -127,11 +125,12 @@ static enum {
ACTION_VACUUM,
} arg_action = ACTION_SHOW;
-typedef struct boot_id_t {
+typedef struct BootId {
sd_id128_t id;
uint64_t first;
uint64_t last;
-} boot_id_t;
+ LIST_FIELDS(struct BootId, boot_list);
+} BootId;
static void pager_open_if_enabled(void) {
@@ -452,7 +451,7 @@ static int parse_argv(int argc, char *argv[]) {
arg_boot = true;
if (optarg) {
- r = parse_boot_descriptor(optarg, &arg_boot_id, &arg_boot_offset);
+ r = parse_boot_descriptor(optarg, &arg_boot_id, &arg_boot_offset);
if (r < 0) {
log_error("Failed to parse boot descriptor '%s'", optarg);
return -EINVAL;
@@ -579,7 +578,7 @@ static int parse_argv(int argc, char *argv[]) {
case ARG_INTERVAL:
case ARG_FORCE:
log_error("Forward-secure sealing not available.");
- return -ENOTSUP;
+ return -EOPNOTSUPP;
#endif
case 'p': {
@@ -735,6 +734,11 @@ static int parse_argv(int argc, char *argv[]) {
return -EINVAL;
}
+ if ((arg_boot || arg_action == ACTION_LIST_BOOTS) && (arg_file || arg_directory || arg_merge)) {
+ log_error("Using --boot or --list-boots with --file, --directory or --merge is not supported.");
+ return -EINVAL;
+ }
+
return 1;
}
@@ -791,7 +795,7 @@ static int add_matches(sd_journal *j, char **args) {
p = canonicalize_file_name(*i);
path = p ? p : *i;
- if (stat(path, &st) < 0)
+ if (lstat(path, &st) < 0)
return log_error_errno(errno, "Couldn't stat file: %m");
if (S_ISREG(st.st_mode) && (0111 & st.st_mode)) {
@@ -814,17 +818,11 @@ static int add_matches(sd_journal *j, char **args) {
}
} else
t = strappend("_EXE=", path);
- } else if (S_ISCHR(st.st_mode)) {
- if (asprintf(&t, "_KERNEL_DEVICE=c%u:%u",
- major(st.st_rdev),
- minor(st.st_rdev)) < 0)
- return -ENOMEM;
- } else if (S_ISBLK(st.st_mode)) {
- if (asprintf(&t, "_KERNEL_DEVICE=b%u:%u",
- major(st.st_rdev),
- minor(st.st_rdev)) < 0)
- return -ENOMEM;
- } else {
+ } else if (S_ISCHR(st.st_mode))
+ (void) asprintf(&t, "_KERNEL_DEVICE=c%u:%u", major(st.st_rdev), minor(st.st_rdev));
+ else if (S_ISBLK(st.st_mode))
+ (void) asprintf(&t, "_KERNEL_DEVICE=b%u:%u", major(st.st_rdev), minor(st.st_rdev));
+ else {
log_error("File is neither a device node, nor regular file, nor executable: %s", *i);
return -EINVAL;
}
@@ -854,111 +852,217 @@ static int add_matches(sd_journal *j, char **args) {
return 0;
}
-static int boot_id_cmp(const void *a, const void *b) {
- uint64_t _a, _b;
-
- _a = ((const boot_id_t *)a)->first;
- _b = ((const boot_id_t *)b)->first;
+static void boot_id_free_all(BootId *l) {
- return _a < _b ? -1 : (_a > _b ? 1 : 0);
+ while (l) {
+ BootId *i = l;
+ LIST_REMOVE(boot_list, l, i);
+ free(i);
+ }
}
-static int get_boots(sd_journal *j,
- boot_id_t **boots,
- unsigned int *count,
- boot_id_t *query_ref_boot) {
+static int discover_next_boot(
+ sd_journal *j,
+ BootId **boot,
+ bool advance_older,
+ bool read_realtime) {
+
int r;
- const void *data;
- size_t length, allocated = 0;
+ char match[9+32+1] = "_BOOT_ID=";
+ _cleanup_free_ BootId *next_boot = NULL;
assert(j);
- assert(boots);
- assert(count);
+ assert(boot);
+
+ /* We expect the journal to be on the last position of a boot
+ * (in relation to the direction we are going), so that the next
+ * invocation of sd_journal_next/previous will be from a different
+ * boot. We then collect any information we desire and then jump
+ * to the last location of the new boot by using a _BOOT_ID match
+ * coming from the other journal direction. */
+
+ /* Make sure we aren't restricted by any _BOOT_ID matches, so that
+ * we can actually advance to a *different* boot. */
+ sd_journal_flush_matches(j);
- r = sd_journal_query_unique(j, "_BOOT_ID");
+ if (advance_older)
+ r = sd_journal_previous(j);
+ else
+ r = sd_journal_next(j);
if (r < 0)
return r;
+ else if (r == 0)
+ return 0; /* End of journal, yay. */
- *count = 0;
- SD_JOURNAL_FOREACH_UNIQUE(j, data, length) {
- boot_id_t *id;
+ next_boot = new0(BootId, 1);
+ if (!next_boot)
+ return -ENOMEM;
- assert(startswith(data, "_BOOT_ID="));
+ r = sd_journal_get_monotonic_usec(j, NULL, &next_boot->id);
+ if (r < 0)
+ return r;
- if (!GREEDY_REALLOC(*boots, allocated, *count + 1))
- return log_oom();
+ if (read_realtime) {
+ r = sd_journal_get_realtime_usec(j, &next_boot->first);
+ if (r < 0)
+ return r;
+ }
+
+ /* Now seek to the last occurrence of this boot ID. */
+ sd_id128_to_string(next_boot->id, match + 9);
+ r = sd_journal_add_match(j, match, sizeof(match) - 1);
+ if (r < 0)
+ return r;
+
+ if (advance_older)
+ r = sd_journal_seek_head(j);
+ else
+ r = sd_journal_seek_tail(j);
+ if (r < 0)
+ return r;
- id = *boots + *count;
+ if (advance_older)
+ r = sd_journal_next(j);
+ else
+ r = sd_journal_previous(j);
+ if (r < 0)
+ return r;
+ else if (r == 0)
+ return -ENODATA; /* This shouldn't happen. We just came from this very boot ID. */
- r = sd_id128_from_string(((const char *)data) + strlen("_BOOT_ID="), &id->id);
+ if (read_realtime) {
+ r = sd_journal_get_realtime_usec(j, &next_boot->last);
if (r < 0)
- continue;
+ return r;
+ }
- r = sd_journal_add_match(j, data, length);
+ *boot = next_boot;
+ next_boot = NULL;
+
+ return 0;
+}
+
+static int get_boots(
+ sd_journal *j,
+ BootId **boots,
+ BootId *query_ref_boot,
+ int ref_boot_offset) {
+
+ bool skip_once;
+ int r, count = 0;
+ BootId *head = NULL, *tail = NULL;
+ const bool advance_older = query_ref_boot && ref_boot_offset <= 0;
+
+ assert(j);
+
+ /* Adjust for the asymmetry that offset 0 is
+ * the last (and current) boot, while 1 is considered the
+ * (chronological) first boot in the journal. */
+ skip_once = query_ref_boot && sd_id128_is_null(query_ref_boot->id) && ref_boot_offset < 0;
+
+ /* Advance to the earliest/latest occurrence of our reference
+ * boot ID (taking our lookup direction into account), so that
+ * discover_next_boot() can do its job.
+ * If no reference is given, the journal head/tail will do,
+ * they're "virtual" boots after all. */
+ if (query_ref_boot && !sd_id128_is_null(query_ref_boot->id)) {
+ char match[9+32+1] = "_BOOT_ID=";
+
+ sd_journal_flush_matches(j);
+
+ sd_id128_to_string(query_ref_boot->id, match + 9);
+ r = sd_journal_add_match(j, match, sizeof(match) - 1);
if (r < 0)
return r;
- r = sd_journal_seek_head(j);
+ if (advance_older)
+ r = sd_journal_seek_head(j);
+ else
+ r = sd_journal_seek_tail(j);
if (r < 0)
return r;
- r = sd_journal_next(j);
+ if (advance_older)
+ r = sd_journal_next(j);
+ else
+ r = sd_journal_previous(j);
if (r < 0)
return r;
else if (r == 0)
- goto flush;
-
- r = sd_journal_get_realtime_usec(j, &id->first);
+ goto finish;
+ else if (ref_boot_offset == 0) {
+ count = 1;
+ goto finish;
+ }
+ } else {
+ if (advance_older)
+ r = sd_journal_seek_tail(j);
+ else
+ r = sd_journal_seek_head(j);
if (r < 0)
return r;
- if (query_ref_boot) {
- id->last = 0;
- if (sd_id128_equal(id->id, query_ref_boot->id))
- *query_ref_boot = *id;
- } else {
- r = sd_journal_seek_tail(j);
- if (r < 0)
- return r;
+ /* No sd_journal_next/previous here. */
+ }
- r = sd_journal_previous(j);
- if (r < 0)
- return r;
- else if (r == 0)
- goto flush;
+ for (;;) {
+ _cleanup_free_ BootId *current = NULL;
- r = sd_journal_get_realtime_usec(j, &id->last);
- if (r < 0)
- return r;
+ r = discover_next_boot(j, &current, advance_older, !query_ref_boot);
+ if (r < 0) {
+ boot_id_free_all(head);
+ return r;
}
- (*count)++;
- flush:
- sd_journal_flush_matches(j);
+ if (!current)
+ break;
+
+ if (query_ref_boot) {
+ if (!skip_once)
+ ref_boot_offset += advance_older ? 1 : -1;
+ skip_once = false;
+
+ if (ref_boot_offset == 0) {
+ count = 1;
+ query_ref_boot->id = current->id;
+ break;
+ }
+ } else {
+ LIST_INSERT_AFTER(boot_list, head, tail, current);
+ tail = current;
+ current = NULL;
+ count++;
+ }
}
- qsort_safe(*boots, *count, sizeof(boot_id_t), boot_id_cmp);
- return 0;
+finish:
+ if (boots)
+ *boots = head;
+
+ sd_journal_flush_matches(j);
+
+ return count;
}
static int list_boots(sd_journal *j) {
- int r, w, i;
- unsigned int count;
- boot_id_t *id;
- _cleanup_free_ boot_id_t *all_ids = NULL;
+ int w, i, count;
+ BootId *id, *all_ids;
assert(j);
- r = get_boots(j, &all_ids, &count, NULL);
- if (r < 0)
- return r;
+ count = get_boots(j, &all_ids, NULL, 0);
+ if (count < 0)
+ return log_error_errno(count, "Failed to determine boots: %m");
+ if (count == 0)
+ return count;
pager_open_if_enabled();
/* numbers are one less, but we need an extra char for the sign */
w = DECIMAL_STR_WIDTH(count - 1) + 1;
- for (id = all_ids, i = 0; id < all_ids + count; id++, i++) {
+ i = 0;
+ LIST_FOREACH(boot_list, id, all_ids) {
char a[FORMAT_TIMESTAMP_MAX], b[FORMAT_TIMESTAMP_MAX];
printf("% *i " SD_ID128_FORMAT_STR " %s—%s\n",
@@ -966,40 +1070,10 @@ static int list_boots(sd_journal *j) {
SD_ID128_FORMAT_VAL(id->id),
format_timestamp_maybe_utc(a, sizeof(a), id->first),
format_timestamp_maybe_utc(b, sizeof(b), id->last));
+ i++;
}
- return 0;
-}
-
-static int get_boot_id_by_offset(sd_journal *j, sd_id128_t *boot_id, int offset) {
- int r;
- unsigned int count;
- boot_id_t ref_boot_id = {}, *id;
- _cleanup_free_ boot_id_t *all_ids = NULL;
-
- assert(j);
- assert(boot_id);
-
- ref_boot_id.id = *boot_id;
- r = get_boots(j, &all_ids, &count, &ref_boot_id);
- if (r < 0)
- return r;
-
- if (sd_id128_equal(*boot_id, SD_ID128_NULL)) {
- if (offset > (int) count || offset <= -(int)count)
- return -EADDRNOTAVAIL;
-
- *boot_id = all_ids[(offset <= 0)*count + offset - 1].id;
- } else {
- id = bsearch(&ref_boot_id, all_ids, count, sizeof(boot_id_t), boot_id_cmp);
-
- if (!id ||
- offset <= 0 ? (id - all_ids) + offset < 0 :
- (id - all_ids) + offset >= (int) count)
- return -EADDRNOTAVAIL;
-
- *boot_id = (id + offset)->id;
- }
+ boot_id_free_all(all_ids);
return 0;
}
@@ -1007,6 +1081,7 @@ static int get_boot_id_by_offset(sd_journal *j, sd_id128_t *boot_id, int offset)
static int add_boot(sd_journal *j) {
char match[9+32+1] = "_BOOT_ID=";
int r;
+ BootId ref_boot_id = {};
assert(j);
@@ -1016,17 +1091,22 @@ static int add_boot(sd_journal *j) {
if (arg_boot_offset == 0 && sd_id128_equal(arg_boot_id, SD_ID128_NULL))
return add_match_this_boot(j, arg_machine);
- r = get_boot_id_by_offset(j, &arg_boot_id, arg_boot_offset);
- if (r < 0) {
- if (sd_id128_equal(arg_boot_id, SD_ID128_NULL))
- log_error_errno(r, "Failed to look up boot %+i: %m", arg_boot_offset);
+ ref_boot_id.id = arg_boot_id;
+ r = get_boots(j, NULL, &ref_boot_id, arg_boot_offset);
+ assert(r <= 1);
+ if (r <= 0) {
+ const char *reason = (r == 0) ? "No such boot ID in journal" : strerror(-r);
+
+ if (sd_id128_is_null(arg_boot_id))
+ log_error("Failed to look up boot %+i: %s", arg_boot_offset, reason);
else
log_error("Failed to look up boot ID "SD_ID128_FORMAT_STR"%+i: %s",
- SD_ID128_FORMAT_VAL(arg_boot_id), arg_boot_offset, strerror(-r));
- return r;
+ SD_ID128_FORMAT_VAL(arg_boot_id), arg_boot_offset, reason);
+
+ return r == 0 ? -ENODATA : r;
}
- sd_id128_to_string(arg_boot_id, match + 9);
+ sd_id128_to_string(ref_boot_id.id, match + 9);
r = sd_journal_add_match(j, match, sizeof(match) - 1);
if (r < 0)
@@ -1034,7 +1114,7 @@ static int add_boot(sd_journal *j) {
r = sd_journal_add_conjunction(j);
if (r < 0)
- return r;
+ return log_error_errno(r, "Failed to add conjunction: %m");
return 0;
}
@@ -1052,22 +1132,24 @@ static int add_dmesg(sd_journal *j) {
r = sd_journal_add_conjunction(j);
if (r < 0)
- return r;
+ return log_error_errno(r, "Failed to add conjunction: %m");
return 0;
}
-static int get_possible_units(sd_journal *j,
- const char *fields,
- char **patterns,
- Set **units) {
+static int get_possible_units(
+ sd_journal *j,
+ const char *fields,
+ char **patterns,
+ Set **units) {
+
_cleanup_set_free_free_ Set *found;
const char *field;
int r;
found = set_new(&string_hash_ops);
if (!found)
- return log_oom();
+ return -ENOMEM;
NULSTR_FOREACH(field, fields) {
const void *data;
@@ -1090,7 +1172,7 @@ static int get_possible_units(sd_journal *j,
u = strndup((char*) data + prefix, size - prefix);
if (!u)
- return log_oom();
+ return -ENOMEM;
STRV_FOREACH(pattern, patterns)
if (fnmatch(*pattern, u, FNM_NOESCAPE) == 0) {
@@ -1137,9 +1219,9 @@ static int add_units(sd_journal *j) {
STRV_FOREACH(i, arg_system_units) {
_cleanup_free_ char *u = NULL;
- u = unit_name_mangle(*i, MANGLE_GLOB);
- if (!u)
- return log_oom();
+ r = unit_name_mangle(*i, UNIT_NAME_GLOB, &u);
+ if (r < 0)
+ return r;
if (string_is_glob(u)) {
r = strv_push(&patterns, u);
@@ -1183,9 +1265,9 @@ static int add_units(sd_journal *j) {
STRV_FOREACH(i, arg_user_units) {
_cleanup_free_ char *u = NULL;
- u = unit_name_mangle(*i, MANGLE_GLOB);
- if (!u)
- return log_oom();
+ r = unit_name_mangle(*i, UNIT_NAME_GLOB, &u);
+ if (r < 0)
+ return r;
if (string_is_glob(u)) {
r = strv_push(&patterns, u);
@@ -1254,7 +1336,7 @@ static int add_priorities(sd_journal *j) {
r = sd_journal_add_conjunction(j);
if (r < 0)
- return r;
+ return log_error_errno(r, "Failed to add conjunction: %m");
return 0;
}
@@ -1289,7 +1371,6 @@ static int setup_keys(void) {
#ifdef HAVE_GCRYPT
size_t mpk_size, seed_size, state_size, i;
uint8_t *mpk, *seed, *state;
- ssize_t l;
int fd = -1, r;
sd_id128_t machine, boot;
char *p = NULL, *k = NULL;
@@ -1319,19 +1400,16 @@ static int setup_keys(void) {
SD_ID128_FORMAT_VAL(machine)) < 0)
return log_oom();
- if (access(p, F_OK) >= 0) {
- if (arg_force) {
- r = unlink(p);
- if (r < 0) {
- log_error_errno(errno, "unlink(\"%s\") failed: %m", p);
- r = -errno;
- goto finish;
- }
- } else {
- log_error("Sealing key file %s exists already. (--force to recreate)", p);
- r = -EEXIST;
+ if (arg_force) {
+ r = unlink(p);
+ if (r < 0 && errno != ENOENT) {
+ r = log_error_errno(errno, "unlink(\"%s\") failed: %m", p);
goto finish;
}
+ } else if (access(p, F_OK) >= 0) {
+ log_error("Sealing key file %s exists already. Use --force to recreate.", p);
+ r = -EEXIST;
+ goto finish;
}
if (asprintf(&k, "/var/log/journal/" SD_ID128_FORMAT_STR "/fss.tmp.XXXXXX",
@@ -1357,10 +1435,9 @@ static int setup_keys(void) {
}
log_info("Generating seed...");
- l = loop_read(fd, seed, seed_size, true);
- if (l < 0 || (size_t) l != seed_size) {
- log_error_errno(EIO, "Failed to read random seed: %m");
- r = -EIO;
+ r = loop_read_exact(fd, seed, seed_size, true);
+ if (r < 0) {
+ log_error_errno(r, "Failed to read random seed: %m");
goto finish;
}
@@ -1385,7 +1462,7 @@ static int setup_keys(void) {
/* Enable secure remove, exclusion from dump, synchronous
* writing and in-place updating */
- r = chattr_fd(fd, true, FS_SECRM_FL|FS_NODUMP_FL|FS_SYNC_FL|FS_NOCOW_FL);
+ r = chattr_fd(fd, FS_SECRM_FL|FS_NODUMP_FL|FS_SYNC_FL|FS_NOCOW_FL, FS_SECRM_FL|FS_NODUMP_FL|FS_SYNC_FL|FS_NOCOW_FL);
if (r < 0)
log_warning_errno(errno, "Failed to set file attributes: %m");
@@ -1480,7 +1557,7 @@ finish:
return r;
#else
log_error("Forward-secure sealing not available.");
- return -ENOTSUP;
+ return -EOPNOTSUPP;
#endif
}
@@ -1495,7 +1572,7 @@ static int verify(sd_journal *j) {
ORDERED_HASHMAP_FOREACH(f, j->files, i) {
int k;
- usec_t first, validated, last;
+ usec_t first = 0, validated = 0, last = 0;
#ifdef HAVE_GCRYPT
if (!arg_verify_key && JOURNAL_HEADER_SEALED(f->header))
@@ -1531,54 +1608,76 @@ static int verify(sd_journal *j) {
return r;
}
-#ifdef HAVE_ACL
static int access_check_var_log_journal(sd_journal *j) {
+#ifdef HAVE_ACL
_cleanup_strv_free_ char **g = NULL;
- bool have_access;
+ const char* dir;
+#endif
int r;
assert(j);
- have_access = in_group("systemd-journal") > 0;
+ if (arg_quiet)
+ return 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 we are root, we should have access, don't warn. */
+ if (getuid() == 0)
+ return 0;
- if (!have_access) {
+ /* If we are in the 'systemd-journal' group, we should have
+ * access too. */
+ r = in_group("systemd-journal");
+ if (r < 0)
+ return log_error_errno(r, "Failed to check if we are in the 'systemd-journal' group: %m");
+ if (r > 0)
+ return 0;
- 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;
+#ifdef HAVE_ACL
+ if (laccess("/run/log/journal", F_OK) >= 0)
+ dir = "/run/log/journal";
+ else
+ dir = "/var/log/journal";
- r = strv_extend(&g, "systemd-journal");
- if (r < 0)
- return log_oom();
+ /* If we are in any of the groups listed in the journal ACLs,
+ * then all is good, too. Let's enumerate all groups from the
+ * default ACL of the directory, which generally should allow
+ * access to most journal files too. */
+ r = acl_search_groups(dir, &g);
+ if (r < 0)
+ return log_error_errno(r, "Failed to search journal ACL: %m");
+ if (r > 0)
+ return 0;
- strv_sort(g);
- strv_uniq(g);
+ /* Print a pretty list, if there were ACLs set. */
+ if (!strv_isempty(g)) {
+ _cleanup_free_ char *s = NULL;
- s = strv_join(g, "', '");
- if (!s)
- return log_oom();
+ /* Thre are groups in the ACL, let's list them */
+ r = strv_extend(&g, "systemd-journal");
+ if (r < 0)
+ 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);
- }
+ 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 groups '%s' can see all messages.\n"
+ " Pass -q to turn off this notice.", s);
+ return 1;
}
+#endif
- return 0;
+ /* If no ACLs were found, print a short version of the message. */
+ 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.");
+
+ return 1;
}
-#endif
static int access_check(sd_journal *j) {
Iterator it;
@@ -1590,40 +1689,15 @@ static int access_check(sd_journal *j) {
if (set_isempty(j->errors)) {
if (ordered_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
+ (void) access_check_var_log_journal(j);
- if (ordered_hashmap_isempty(j->files)) {
- log_error("No journal files were opened due to insufficient permissions.");
- r = -EACCES;
- }
+ if (ordered_hashmap_isempty(j->files))
+ r = log_error_errno(EACCES, "No journal files were opened due to insufficient permissions.");
}
SET_FOREACH(code, j->errors, it) {
@@ -1632,8 +1706,12 @@ static int access_check(sd_journal *j) {
err = -PTR_TO_INT(code);
assert(err > 0);
- if (err != EACCES)
- log_warning_errno(err, "Error was encountered while opening journal files: %m");
+ if (err == EACCES)
+ continue;
+
+ log_warning_errno(err, "Error was encountered while opening journal files: %m");
+ if (r == 0)
+ r = -err;
}
return r;
@@ -1780,12 +1858,12 @@ int main(int argc, char *argv[]) {
if (r < 0) {
log_error_errno(r, "Failed to open %s: %m",
arg_directory ? arg_directory : arg_file ? "files" : "journal");
- return EXIT_FAILURE;
+ goto finish;
}
r = access_check(j);
if (r < 0)
- return EXIT_FAILURE;
+ goto finish;
if (arg_action == ACTION_VERIFY) {
r = verify(j);
@@ -1794,7 +1872,8 @@ int main(int argc, char *argv[]) {
if (arg_action == ACTION_PRINT_HEADER) {
journal_print_header(j);
- return EXIT_SUCCESS;
+ r = 0;
+ goto finish;
}
if (arg_action == ACTION_DISK_USAGE) {
@@ -1803,11 +1882,11 @@ int main(int argc, char *argv[]) {
r = sd_journal_get_usage(j, &bytes);
if (r < 0)
- return EXIT_FAILURE;
+ goto finish;
printf("Archived and active journals take up %s on disk.\n",
format_bytes(sbytes, sizeof(sbytes), bytes));
- return EXIT_SUCCESS;
+ goto finish;
}
if (arg_action == ACTION_VACUUM) {
@@ -1827,7 +1906,7 @@ int main(int argc, char *argv[]) {
}
}
- return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+ goto finish;
}
if (arg_action == ACTION_LIST_BOOTS) {
@@ -1839,43 +1918,39 @@ int main(int argc, char *argv[]) {
* It may need to seek the journal to find parent boot IDs. */
r = add_boot(j);
if (r < 0)
- return EXIT_FAILURE;
+ goto finish;
r = add_dmesg(j);
if (r < 0)
- return EXIT_FAILURE;
+ goto finish;
r = add_units(j);
- strv_free(arg_system_units);
- strv_free(arg_user_units);
-
if (r < 0) {
log_error_errno(r, "Failed to add filter for units: %m");
- return EXIT_FAILURE;
+ goto finish;
}
r = add_syslog_identifier(j);
if (r < 0) {
log_error_errno(r, "Failed to add filter for syslog identifiers: %m");
- return EXIT_FAILURE;
+ goto finish;
}
r = add_priorities(j);
- if (r < 0) {
- log_error_errno(r, "Failed to add filter for priorities: %m");
- return EXIT_FAILURE;
- }
+ if (r < 0)
+ goto finish;
r = add_matches(j, argv + optind);
- if (r < 0) {
- log_error_errno(r, "Failed to add filters: %m");
- return EXIT_FAILURE;
- }
+ if (r < 0)
+ goto finish;
if (_unlikely_(log_get_max_level() >= LOG_DEBUG)) {
_cleanup_free_ char *filter;
filter = journal_make_match_string(j);
+ if (!filter)
+ return log_oom();
+
log_debug("Journal filter: %s", filter);
}
@@ -1885,14 +1960,14 @@ int main(int argc, char *argv[]) {
r = sd_journal_set_data_threshold(j, 0);
if (r < 0) {
- log_error("Failed to unset data size threshold");
- return EXIT_FAILURE;
+ log_error_errno(r, "Failed to unset data size threshold: %m");
+ goto finish;
}
r = sd_journal_query_unique(j, arg_field);
if (r < 0) {
log_error_errno(r, "Failed to query unique data objects: %m");
- return EXIT_FAILURE;
+ goto finish;
}
SD_JOURNAL_FOREACH_UNIQUE(j, data, size) {
@@ -1910,22 +1985,26 @@ int main(int argc, char *argv[]) {
n_shown ++;
}
- return EXIT_SUCCESS;
+ r = 0;
+ goto finish;
}
/* Opening the fd now means the first sd_journal_wait() will actually wait */
if (arg_follow) {
r = sd_journal_get_fd(j);
- if (r < 0)
- return EXIT_FAILURE;
+ if (r < 0) {
+ log_error_errno(r, "Failed to get journal fd: %m");
+ goto finish;
+ }
}
if (arg_cursor || arg_after_cursor) {
r = sd_journal_seek_cursor(j, arg_cursor ?: arg_after_cursor);
if (r < 0) {
log_error_errno(r, "Failed to seek to cursor: %m");
- return EXIT_FAILURE;
+ goto finish;
}
+
if (!arg_reverse)
r = sd_journal_next_skip(j, 1 + !!arg_after_cursor);
else
@@ -1943,7 +2022,7 @@ int main(int argc, char *argv[]) {
r = sd_journal_seek_realtime_usec(j, arg_since);
if (r < 0) {
log_error_errno(r, "Failed to seek to date: %m");
- return EXIT_FAILURE;
+ goto finish;
}
r = sd_journal_next(j);
@@ -1951,7 +2030,7 @@ int main(int argc, char *argv[]) {
r = sd_journal_seek_realtime_usec(j, arg_until);
if (r < 0) {
log_error_errno(r, "Failed to seek to date: %m");
- return EXIT_FAILURE;
+ goto finish;
}
r = sd_journal_previous(j);
@@ -1959,7 +2038,7 @@ int main(int argc, char *argv[]) {
r = sd_journal_seek_tail(j);
if (r < 0) {
log_error_errno(r, "Failed to seek to tail: %m");
- return EXIT_FAILURE;
+ goto finish;
}
r = sd_journal_previous_skip(j, arg_lines);
@@ -1968,7 +2047,7 @@ int main(int argc, char *argv[]) {
r = sd_journal_seek_tail(j);
if (r < 0) {
log_error_errno(r, "Failed to seek to tail: %m");
- return EXIT_FAILURE;
+ goto finish;
}
r = sd_journal_previous(j);
@@ -1977,7 +2056,7 @@ int main(int argc, char *argv[]) {
r = sd_journal_seek_head(j);
if (r < 0) {
log_error_errno(r, "Failed to seek to head: %m");
- return EXIT_FAILURE;
+ goto finish;
}
r = sd_journal_next(j);
@@ -1985,7 +2064,7 @@ int main(int argc, char *argv[]) {
if (r < 0) {
log_error_errno(r, "Failed to iterate through journal: %m");
- return EXIT_FAILURE;
+ goto finish;
}
if (!arg_follow)
@@ -2113,5 +2192,9 @@ finish:
strv_free(arg_file);
+ strv_free(arg_syslog_identifier);
+ strv_free(arg_system_units);
+ strv_free(arg_user_units);
+
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}
diff --git a/src/journal/journald-audit.c b/src/journal/journald-audit.c
index c2f1545cc9..83c3332abf 100644
--- a/src/journal/journald-audit.c
+++ b/src/journal/journald-audit.c
@@ -21,6 +21,7 @@
#include "missing.h"
#include "journald-audit.h"
+#include "audit-type.h"
typedef struct MapField {
const char *audit_field;
@@ -336,7 +337,7 @@ static void process_audit_string(Server *s, int type, const char *data, size_t s
size_t n_iov_allocated = 0;
unsigned n_iov = 0, k;
uint64_t seconds, msec, id;
- const char *p;
+ const char *p, *type_name;
unsigned z;
char id_field[sizeof("_AUDIT_ID=") + DECIMAL_STR_MAX(uint64_t)],
type_field[sizeof("_AUDIT_TYPE=") + DECIMAL_STR_MAX(int)],
@@ -373,7 +374,7 @@ static void process_audit_string(Server *s, int type, const char *data, size_t s
if (isempty(p))
return;
- n_iov_allocated = N_IOVEC_META_FIELDS + 5;
+ n_iov_allocated = N_IOVEC_META_FIELDS + 7;
iov = new(struct iovec, n_iov_allocated);
if (!iov) {
log_oom();
@@ -392,8 +393,13 @@ static void process_audit_string(Server *s, int type, const char *data, size_t s
sprintf(id_field, "_AUDIT_ID=%" PRIu64, id);
IOVEC_SET_STRING(iov[n_iov++], id_field);
- m = alloca(strlen("MESSAGE=<audit-") + DECIMAL_STR_MAX(int) + strlen("> ") + strlen(p) + 1);
- sprintf(m, "MESSAGE=<audit-%i> %s", type, p);
+ assert_cc(32 == LOG_AUTH);
+ IOVEC_SET_STRING(iov[n_iov++], "SYSLOG_FACILITY=32");
+ IOVEC_SET_STRING(iov[n_iov++], "SYSLOG_IDENTIFIER=audit");
+
+ type_name = audit_type_name_alloca(type);
+
+ m = strjoina("MESSAGE=", type_name, " ", p);
IOVEC_SET_STRING(iov[n_iov++], m);
z = n_iov;
@@ -528,9 +534,14 @@ int server_open_audit(Server *s) {
return 0;
}
- r = bind(s->audit_fd, &sa.sa, sizeof(sa.nl));
- if (r < 0)
- return log_error_errno(errno, "Failed to join audit multicast group: %m");
+ if (bind(s->audit_fd, &sa.sa, sizeof(sa.nl)) < 0) {
+ log_warning_errno(errno,
+ "Failed to join audit multicast group. "
+ "The kernel is probably too old or multicast reading is not supported. "
+ "Ignoring: %m");
+ s->audit_fd = safe_close(s->audit_fd);
+ return 0;
+ }
} else
fd_nonblock(s->audit_fd, 1);
diff --git a/src/journal/journald-console.c b/src/journal/journald-console.c
index 5363aaa4ff..307bdc3949 100644
--- a/src/journal/journald-console.c
+++ b/src/journal/journald-console.c
@@ -21,12 +21,14 @@
#include <time.h>
#include <fcntl.h>
-#include <unistd.h>
#include <sys/socket.h>
#include "fileio.h"
#include "journald-server.h"
#include "journald-console.h"
+#include "formats-util.h"
+#include "process-util.h"
+#include "terminal-util.h"
static bool prefix_timestamp(void) {
diff --git a/src/journal/journald-kmsg.c b/src/journal/journald-kmsg.c
index c4216c4043..e5be7f7766 100644
--- a/src/journal/journald-kmsg.c
+++ b/src/journal/journald-kmsg.c
@@ -31,6 +31,8 @@
#include "journald-server.h"
#include "journald-kmsg.h"
#include "journald-syslog.h"
+#include "formats-util.h"
+#include "process-util.h"
void server_forward_kmsg(
Server *s,
@@ -201,8 +203,7 @@ static void dev_kmsg_record(Server *s, const char *p, size_t l) {
*e = 0;
- m = cunescape_length_with_prefix(k, e - k, "_KERNEL_");
- if (!m)
+ if (cunescape_length_with_prefix(k, e - k, "_KERNEL_", UNESCAPE_RELAX, &m) < 0)
break;
if (startswith(m, "_KERNEL_DEVICE="))
@@ -299,8 +300,7 @@ static void dev_kmsg_record(Server *s, const char *p, size_t l) {
}
}
- message = cunescape_length_with_prefix(p, pl, "MESSAGE=");
- if (message)
+ if (cunescape_length_with_prefix(p, pl, "MESSAGE=", UNESCAPE_RELAX, &message) >= 0)
IOVEC_SET_STRING(iovec[n++], message);
server_dispatch_message(s, iovec, n, ELEMENTSOF(iovec), NULL, NULL, NULL, 0, NULL, priority, 0);
diff --git a/src/journal/journald-native.c b/src/journal/journald-native.c
index 851625de04..da3faef93e 100644
--- a/src/journal/journald-native.c
+++ b/src/journal/journald-native.c
@@ -433,7 +433,7 @@ int server_open_native_socket(Server*s) {
if (r < 0)
return log_error_errno(errno, "bind(%s) failed: %m", sa.un.sun_path);
- chmod(sa.un.sun_path, 0666);
+ (void) chmod(sa.un.sun_path, 0666);
} else
fd_nonblock(s->native_fd, 1);
diff --git a/src/journal/journald-rate-limit.c b/src/journal/journald-rate-limit.c
index 6d779d2966..6f83035a4e 100644
--- a/src/journal/journald-rate-limit.c
+++ b/src/journal/journald-rate-limit.c
@@ -26,6 +26,7 @@
#include "list.h"
#include "util.h"
#include "hashmap.h"
+#include "random-util.h"
#define POOLS_MAX 5
#define BUCKETS_MAX 127
diff --git a/src/journal/journald-rate-limit.h b/src/journal/journald-rate-limit.h
index 648ab22786..466239d3c6 100644
--- a/src/journal/journald-rate-limit.h
+++ b/src/journal/journald-rate-limit.h
@@ -21,7 +21,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "macro.h"
#include "util.h"
typedef struct JournalRateLimit JournalRateLimit;
diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c
index 7ee8174ea2..3353024f4e 100644
--- a/src/journal/journald-server.c
+++ b/src/journal/journald-server.c
@@ -24,23 +24,30 @@
#include <linux/sockios.h>
#include <sys/statvfs.h>
#include <sys/mman.h>
-#include <sys/timerfd.h>
+
+#ifdef HAVE_SELINUX
+#include <selinux/selinux.h>
+#endif
#include <libudev.h>
#include "sd-journal.h"
#include "sd-messages.h"
#include "sd-daemon.h"
-#include "fileio.h"
#include "mkdir.h"
+#include "rm-rf.h"
#include "hashmap.h"
#include "journal-file.h"
#include "socket-util.h"
#include "cgroup-util.h"
-#include "list.h"
#include "missing.h"
#include "conf-parser.h"
#include "selinux-util.h"
+#include "acl-util.h"
+#include "formats-util.h"
+#include "process-util.h"
+#include "hostname-util.h"
+#include "signal-util.h"
#include "journal-internal.h"
#include "journal-vacuum.h"
#include "journal-authenticate.h"
@@ -48,15 +55,9 @@
#include "journald-kmsg.h"
#include "journald-syslog.h"
#include "journald-stream.h"
-#include "journald-console.h"
#include "journald-native.h"
#include "journald-audit.h"
#include "journald-server.h"
-#include "acl-util.h"
-
-#ifdef HAVE_SELINUX
-#include <selinux/selinux.h>
-#endif
#define USER_JOURNALS_MAX 1024
@@ -1092,7 +1093,7 @@ finish:
s->runtime_journal = NULL;
if (r >= 0)
- rm_rf("/run/log/journal", false, true, false);
+ (void) rm_rf("/run/log/journal", REMOVE_ROOT);
sd_journal_close(j);
diff --git a/src/journal/journald-server.h b/src/journal/journald-server.h
index c96877c508..559d100131 100644
--- a/src/journal/journald-server.h
+++ b/src/journal/journald-server.h
@@ -21,16 +21,12 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <inttypes.h>
#include <stdbool.h>
-#include <sys/epoll.h>
#include <sys/types.h>
-#include <sys/socket.h>
#include "sd-event.h"
#include "journal-file.h"
#include "hashmap.h"
-#include "util.h"
#include "audit.h"
#include "journald-rate-limit.h"
#include "list.h"
diff --git a/src/journal/journald-stream.c b/src/journal/journald-stream.c
index 942a857803..b572147a56 100644
--- a/src/journal/journald-stream.c
+++ b/src/journal/journald-stream.c
@@ -19,7 +19,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <fcntl.h>
#include <unistd.h>
#include <stddef.h>
@@ -721,7 +720,7 @@ int server_open_stdout_socket(Server *s, FDSet *fds) {
if (r < 0)
return log_error_errno(errno, "bind(%s) failed: %m", sa.un.sun_path);
- chmod(sa.un.sun_path, 0666);
+ (void) chmod(sa.un.sun_path, 0666);
if (listen(s->stdout_fd, SOMAXCONN) < 0)
return log_error_errno(errno, "listen(%s) failed: %m", sa.un.sun_path);
diff --git a/src/journal/journald-syslog.c b/src/journal/journald-syslog.c
index 7d545ca31d..90b7530946 100644
--- a/src/journal/journald-syslog.c
+++ b/src/journal/journald-syslog.c
@@ -31,6 +31,8 @@
#include "journald-kmsg.h"
#include "journald-console.h"
#include "journald-wall.h"
+#include "formats-util.h"
+#include "process-util.h"
/* Warn once every 30s if we missed syslog message */
#define WARN_FORWARD_SYSLOG_MISSED_USEC (30 * USEC_PER_SEC)
@@ -396,7 +398,7 @@ int server_open_syslog_socket(Server *s) {
if (r < 0)
return log_error_errno(errno, "bind(%s) failed: %m", sa.un.sun_path);
- chmod(sa.un.sun_path, 0666);
+ (void) chmod(sa.un.sun_path, 0666);
} else
fd_nonblock(s->syslog_fd, 1);
diff --git a/src/journal/journald-wall.c b/src/journal/journald-wall.c
index e3201674d4..7863766ae7 100644
--- a/src/journal/journald-wall.c
+++ b/src/journal/journald-wall.c
@@ -22,6 +22,8 @@
#include "utmp-wtmp.h"
#include "journald-server.h"
#include "journald-wall.h"
+#include "formats-util.h"
+#include "process-util.h"
void server_forward_wall(
Server *s,
@@ -63,7 +65,7 @@ void server_forward_wall(
} else
l = message;
- r = utmp_wall(l, "systemd-journald", NULL);
+ r = utmp_wall(l, "systemd-journald", NULL, NULL, NULL);
if (r < 0)
log_debug_errno(r, "Failed to send wall message: %m");
}
diff --git a/src/journal/journald.c b/src/journal/journald.c
index 80f4634f67..b2624c6d28 100644
--- a/src/journal/journald.c
+++ b/src/journal/journald.c
@@ -19,12 +19,8 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <sys/epoll.h>
-#include <sys/socket.h>
-#include <errno.h>
#include <unistd.h>
-#include "systemd/sd-journal.h"
#include "systemd/sd-messages.h"
#include "systemd/sd-daemon.h"
@@ -34,6 +30,7 @@
#include "journald-syslog.h"
#include "sigbus.h"
+#include "formats-util.h"
int main(int argc, char *argv[]) {
Server server;
diff --git a/src/journal/journald.conf b/src/journal/journald.conf
index 29bdf8f183..47eefe91c1 100644
--- a/src/journal/journald.conf
+++ b/src/journal/journald.conf
@@ -5,10 +5,11 @@
# the Free Software Foundation; either version 2.1 of the License, or
# (at your option) any later version.
#
-# You can override the directives in this file by creating files in
-# /etc/systemd/journald.conf.d/*.conf.
+# Entries in this file show the compile time defaults.
+# You can change settings by editing this file.
+# Defaults can be restored by simply deleting this file.
#
-# See journald.conf(5) for details
+# See journald.conf(5) for details.
[Journal]
#Storage=auto
diff --git a/src/journal/mmap-cache.c b/src/journal/mmap-cache.c
index ab21cdc288..22f75540b8 100644
--- a/src/journal/mmap-cache.c
+++ b/src/journal/mmap-cache.c
@@ -22,7 +22,6 @@
#include <errno.h>
#include <stdlib.h>
#include <sys/mman.h>
-#include <string.h>
#include "hashmap.h"
#include "list.h"
diff --git a/src/journal/mmap-cache.h b/src/journal/mmap-cache.h
index a85c2b6063..37ea7b4a9c 100644
--- a/src/journal/mmap-cache.h
+++ b/src/journal/mmap-cache.h
@@ -21,7 +21,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <inttypes.h>
#include <stdbool.h>
#include <sys/stat.h>
diff --git a/src/journal/sd-journal.c b/src/journal/sd-journal.c
index 94891cdf35..9f0f71aa81 100644
--- a/src/journal/sd-journal.c
+++ b/src/journal/sd-journal.c
@@ -42,6 +42,7 @@
#include "catalog.h"
#include "replace-var.h"
#include "fileio.h"
+#include "formats-util.h"
#define JOURNAL_FILES_MAX 7168
@@ -723,13 +724,17 @@ static int next_beyond_location(sd_journal *j, JournalFile *f, direction_t direc
assert(j);
assert(f);
- if (f->last_direction == direction && f->current_offset > 0) {
- /* If we hit EOF before, recheck if any new entries arrived. */
- n_entries = le64toh(f->header->n_entries);
- if (f->location_type == LOCATION_TAIL && n_entries == f->last_n_entries)
- return 0;
- f->last_n_entries = n_entries;
+ n_entries = le64toh(f->header->n_entries);
+
+ /* If we hit EOF before, we don't need to look into this file again
+ * unless direction changed or new entries appeared. */
+ if (f->last_direction == direction && f->location_type == LOCATION_TAIL &&
+ n_entries == f->last_n_entries)
+ return 0;
+
+ f->last_n_entries = n_entries;
+ if (f->last_direction == direction && f->current_offset > 0) {
/* LOCATION_SEEK here means we did the work in a previous
* iteration and the current location already points to a
* candidate entry. */
@@ -738,14 +743,16 @@ static int next_beyond_location(sd_journal *j, JournalFile *f, direction_t direc
if (r <= 0)
return r;
- journal_file_save_location(f, direction, c, cp);
+ journal_file_save_location(f, c, cp);
}
} else {
+ f->last_direction = direction;
+
r = find_location_with_matches(j, f, direction, &c, &cp);
if (r <= 0)
return r;
- journal_file_save_location(f, direction, c, cp);
+ journal_file_save_location(f, c, cp);
}
/* OK, we found the spot, now let's advance until an entry
@@ -773,7 +780,7 @@ static int next_beyond_location(sd_journal *j, JournalFile *f, direction_t direc
if (r <= 0)
return r;
- journal_file_save_location(f, direction, c, cp);
+ journal_file_save_location(f, c, cp);
}
}
@@ -1242,7 +1249,7 @@ static int add_file(sd_journal *j, const char *prefix, const char *filename) {
r = add_any_file(j, path);
if (r == -ENOENT)
return 0;
- return 0;
+ return r;
}
static int remove_file(sd_journal *j, const char *prefix, const char *filename) {
diff --git a/src/journal/stacktrace.c b/src/journal/stacktrace.c
index 6b9d2729f5..706c08eac7 100644
--- a/src/journal/stacktrace.c
+++ b/src/journal/stacktrace.c
@@ -25,6 +25,7 @@
#include "util.h"
#include "macro.h"
#include "stacktrace.h"
+#include "formats-util.h"
#define FRAMES_MAX 64
#define THREADS_MAX 64
diff --git a/src/journal/test-audit-type.c b/src/journal/test-audit-type.c
new file mode 100644
index 0000000000..7946cf3c41
--- /dev/null
+++ b/src/journal/test-audit-type.c
@@ -0,0 +1,44 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2015 Zbigniew Jędrzejewski-Szmek
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdio.h>
+#include <linux/audit.h>
+
+#include "audit-type.h"
+
+static void print_audit_label(int i) {
+ const char *name;
+
+ name = audit_type_name_alloca(i);
+ /* This is a separate function only because of alloca */
+ printf("%i → %s → %s\n", i, audit_type_to_string(i), name);
+}
+
+static void test_audit_type(void) {
+ int i;
+
+ for (i = 0; i <= AUDIT_KERNEL; i++)
+ print_audit_label(i);
+}
+
+int main(int argc, char **argv) {
+ test_audit_type();
+}
diff --git a/src/journal/test-catalog.c b/src/journal/test-catalog.c
index c605ee0e70..dbfdea609d 100644
--- a/src/journal/test-catalog.c
+++ b/src/journal/test-catalog.c
@@ -21,7 +21,6 @@
***/
#include <locale.h>
-#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
diff --git a/src/journal/test-compress.c b/src/journal/test-compress.c
index ae41c0c4c7..41a566d714 100644
--- a/src/journal/test-compress.c
+++ b/src/journal/test-compress.c
@@ -20,6 +20,7 @@
#include "compress.h"
#include "util.h"
#include "macro.h"
+#include "random-util.h"
#ifdef HAVE_XZ
# define XZ_OK 0
diff --git a/src/journal/test-journal-enum.c b/src/journal/test-journal-enum.c
index 980244e016..cde2025ae9 100644
--- a/src/journal/test-journal-enum.c
+++ b/src/journal/test-journal-enum.c
@@ -24,7 +24,6 @@
#include "log.h"
#include "sd-journal.h"
#include "macro.h"
-#include "util.h"
#include "journal-internal.h"
int main(int argc, char *argv[]) {
diff --git a/src/journal/test-journal-flush.c b/src/journal/test-journal-flush.c
index 914ca0b4d1..2d4f531e9b 100644
--- a/src/journal/test-journal-flush.c
+++ b/src/journal/test-journal-flush.c
@@ -27,8 +27,8 @@
#include "journal-internal.h"
int main(int argc, char *argv[]) {
-
- char dn[] = "/var/tmp/test-journal-flush.XXXXXX", *fn;
+ _cleanup_free_ char *fn = NULL;
+ char dn[] = "/var/tmp/test-journal-flush.XXXXXX";
JournalFile *new_journal = NULL;
sd_journal *j = NULL;
unsigned n = 0;
diff --git a/src/journal/test-journal-init.c b/src/journal/test-journal-init.c
index 11fb150fe8..e6599f366d 100644
--- a/src/journal/test-journal-init.c
+++ b/src/journal/test-journal-init.c
@@ -23,6 +23,7 @@
#include "log.h"
#include "util.h"
+#include "rm-rf.h"
int main(int argc, char *argv[]) {
sd_journal *j;
@@ -58,7 +59,7 @@ int main(int argc, char *argv[]) {
assert_se(j == NULL);
}
- assert_se(rm_rf_dangerous(t, false, true, false) >= 0);
+ assert_se(rm_rf(t, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
return 0;
}
diff --git a/src/journal/test-journal-interleaving.c b/src/journal/test-journal-interleaving.c
index 3c706018e9..c2fc123e42 100644
--- a/src/journal/test-journal-interleaving.c
+++ b/src/journal/test-journal-interleaving.c
@@ -23,13 +23,12 @@
#include <unistd.h>
#include <fcntl.h>
-#include "systemd/sd-journal.h"
-
+#include "sd-journal.h"
#include "journal-file.h"
-#include "journal-internal.h"
#include "journal-vacuum.h"
#include "util.h"
#include "log.h"
+#include "rm-rf.h"
/* This program tests skipping around in a multi-file journal.
*/
@@ -191,7 +190,7 @@ static void test_skip(void (*setup)(void)) {
else {
journal_directory_vacuum(".", 3000000, 0, NULL, true);
- assert_se(rm_rf_dangerous(t, false, true, false) >= 0);
+ assert_se(rm_rf(t, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
}
puts("------------------------------------------------------------");
@@ -276,7 +275,7 @@ static void test_sequence_numbers(void) {
else {
journal_directory_vacuum(".", 3000000, 0, NULL, true);
- assert_se(rm_rf_dangerous(t, false, true, false) >= 0);
+ assert_se(rm_rf(t, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
}
}
diff --git a/src/journal/test-journal-stream.c b/src/journal/test-journal-stream.c
index 3996e778e6..e1146c692d 100644
--- a/src/journal/test-journal-stream.c
+++ b/src/journal/test-journal-stream.c
@@ -22,13 +22,13 @@
#include <unistd.h>
#include <fcntl.h>
-#include "systemd/sd-journal.h"
-
-#include "journal-file.h"
-#include "journal-internal.h"
+#include "sd-journal.h"
#include "util.h"
#include "log.h"
#include "macro.h"
+#include "rm-rf.h"
+#include "journal-file.h"
+#include "journal-internal.h"
#define N_ENTRIES 200
@@ -42,7 +42,7 @@ static void verify_contents(sd_journal *j, unsigned skip) {
const void *d;
char *k, *c;
size_t l;
- unsigned u;
+ unsigned u = 0;
assert_se(sd_journal_get_cursor(j, &k) >= 0);
printf("cursor: %s\n", k);
@@ -180,7 +180,7 @@ int main(int argc, char *argv[]) {
SD_JOURNAL_FOREACH_UNIQUE(j, data, l)
printf("%.*s\n", (int) l, (const char*) data);
- assert_se(rm_rf_dangerous(t, false, true, false) >= 0);
+ assert_se(rm_rf(t, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
return 0;
}
diff --git a/src/journal/test-journal-verify.c b/src/journal/test-journal-verify.c
index 9da120c0bb..d24502d9a8 100644
--- a/src/journal/test-journal-verify.c
+++ b/src/journal/test-journal-verify.c
@@ -25,9 +25,10 @@
#include "util.h"
#include "log.h"
+#include "rm-rf.h"
#include "journal-file.h"
#include "journal-verify.h"
-#include "journal-authenticate.h"
+#include "terminal-util.h"
#define N_ENTRIES 6000
#define RANDOM_RANGE 77
@@ -145,7 +146,7 @@ int main(int argc, char *argv[]) {
log_info("Exiting...");
- assert_se(rm_rf_dangerous(t, false, true, false) >= 0);
+ assert_se(rm_rf(t, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
return 0;
}
diff --git a/src/journal/test-journal.c b/src/journal/test-journal.c
index 230d26596a..caaab258c9 100644
--- a/src/journal/test-journal.c
+++ b/src/journal/test-journal.c
@@ -22,9 +22,8 @@
#include <fcntl.h>
#include <unistd.h>
-#include "systemd/sd-journal.h"
-
#include "log.h"
+#include "rm-rf.h"
#include "journal-file.h"
#include "journal-authenticate.h"
#include "journal-vacuum.h"
@@ -119,7 +118,7 @@ static void test_non_empty(void) {
else {
journal_directory_vacuum(".", 3000000, 0, NULL, true);
- assert_se(rm_rf_dangerous(t, false, true, false) >= 0);
+ assert_se(rm_rf(t, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
}
puts("------------------------------------------------------------");
@@ -158,7 +157,7 @@ static void test_empty(void) {
else {
journal_directory_vacuum(".", 3000000, 0, NULL, true);
- assert_se(rm_rf_dangerous(t, false, true, false) >= 0);
+ assert_se(rm_rf(t, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
}
journal_file_close(f1);
diff --git a/src/journal/test-mmap-cache.c b/src/journal/test-mmap-cache.c
index 3fcd77475d..3258b22702 100644
--- a/src/journal/test-mmap-cache.c
+++ b/src/journal/test-mmap-cache.c
@@ -24,7 +24,6 @@
#include <unistd.h>
#include <fcntl.h>
-#include "log.h"
#include "macro.h"
#include "util.h"
#include "mmap-cache.h"
diff --git a/src/kernel-install/90-loaderentry.install b/src/kernel-install/90-loaderentry.install
index d433e00a5c..4c9b1f0327 100644
--- a/src/kernel-install/90-loaderentry.install
+++ b/src/kernel-install/90-loaderentry.install
@@ -43,14 +43,14 @@ fi
declare -a BOOT_OPTIONS
if [[ -f /etc/kernel/cmdline ]]; then
- readarray -t BOOT_OPTIONS < /etc/kernel/cmdline
+ read -r -d '' -a BOOT_OPTIONS < /etc/kernel/cmdline
fi
if ! [[ ${BOOT_OPTIONS[*]} ]]; then
- read -a line -r < /proc/cmdline
+ read -r -d '' -a line < /proc/cmdline
for i in "${line[@]}"; do
[[ "${i#initrd=*}" != "$i" ]] && continue
- BOOT_OPTIONS[${#BOOT_OPTIONS[@]}]="$i"
+ BOOT_OPTIONS+=("$i")
done
fi
diff --git a/src/libsystemd-network/dhcp-identifier.c b/src/libsystemd-network/dhcp-identifier.c
index 419a450436..70c68ad131 100644
--- a/src/libsystemd-network/dhcp-identifier.c
+++ b/src/libsystemd-network/dhcp-identifier.c
@@ -19,7 +19,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <net/ethernet.h>
#include "sd-id128.h"
#include "libudev.h"
@@ -28,7 +27,6 @@
#include "virt.h"
#include "sparse-endian.h"
#include "siphash24.h"
-#include "util.h"
#include "dhcp6-protocol.h"
#include "dhcp-identifier.h"
@@ -48,8 +46,9 @@ int dhcp_identifier_set_duid_en(struct duid *duid, size_t *len) {
if (r < 0)
return r;
- duid->type = htobe16(DHCP6_DUID_EN);
- duid->en.pen = htobe32(SYSTEMD_PEN);
+ unaligned_write_be16(&duid->type, DHCP6_DUID_EN);
+ unaligned_write_be32(&duid->en.pen, SYSTEMD_PEN);
+
*len = sizeof(duid->type) + sizeof(duid->en);
/* a bit of snake-oil perhaps, but no need to expose the machine-id
@@ -60,7 +59,7 @@ int dhcp_identifier_set_duid_en(struct duid *duid, size_t *len) {
}
-int dhcp_identifier_set_iaid(int ifindex, uint8_t *mac, size_t mac_len, uint32_t *_id) {
+int dhcp_identifier_set_iaid(int ifindex, uint8_t *mac, size_t mac_len, void *_id) {
/* name is a pointer to memory in the udev_device struct, so must
have the same scope */
_cleanup_udev_device_unref_ struct udev_device *device = NULL;
@@ -94,7 +93,7 @@ int dhcp_identifier_set_iaid(int ifindex, uint8_t *mac, size_t mac_len, uint32_t
siphash24((uint8_t*)&id, mac, mac_len, HASH_KEY.bytes);
/* fold into 32 bits */
- *_id = (id & 0xffffffff) ^ (id >> 32);
+ unaligned_write_be32(_id, (id & 0xffffffff) ^ (id >> 32));
return 0;
}
diff --git a/src/libsystemd-network/dhcp-identifier.h b/src/libsystemd-network/dhcp-identifier.h
index 7f44d25499..95117915f4 100644
--- a/src/libsystemd-network/dhcp-identifier.h
+++ b/src/libsystemd-network/dhcp-identifier.h
@@ -21,10 +21,10 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <net/ethernet.h>
#include "macro.h"
#include "sparse-endian.h"
+#include "unaligned.h"
#include "sd-id128.h"
/* RFC 3315 section 9.1:
@@ -62,4 +62,4 @@ struct duid {
} _packed_;
int dhcp_identifier_set_duid_en(struct duid *duid, size_t *len);
-int dhcp_identifier_set_iaid(int ifindex, uint8_t *mac, size_t mac_len, uint32_t *_id);
+int dhcp_identifier_set_iaid(int ifindex, uint8_t *mac, size_t mac_len, void *_id);
diff --git a/src/libsystemd-network/dhcp-network.c b/src/libsystemd-network/dhcp-network.c
index 5f2d2cfbfd..7f10838de1 100644
--- a/src/libsystemd-network/dhcp-network.c
+++ b/src/libsystemd-network/dhcp-network.c
@@ -18,7 +18,6 @@
***/
#include <errno.h>
-#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <linux/if_packet.h>
@@ -26,7 +25,6 @@
#include <net/ethernet.h>
#include <net/if_arp.h>
#include <stdio.h>
-#include <unistd.h>
#include <linux/filter.h>
#include "socket-util.h"
diff --git a/src/libsystemd-network/dhcp-packet.c b/src/libsystemd-network/dhcp-packet.c
index 7581daeeeb..cd7f5095ca 100644
--- a/src/libsystemd-network/dhcp-packet.c
+++ b/src/libsystemd-network/dhcp-packet.c
@@ -18,22 +18,14 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdlib.h>
#include <errno.h>
#include <string.h>
-#include <stdio.h>
#include <net/ethernet.h>
#include <net/if_arp.h>
-#include <sys/param.h>
-#include "util.h"
-#include "list.h"
#include "dhcp-protocol.h"
-#include "dhcp-lease-internal.h"
#include "dhcp-internal.h"
-#include "sd-dhcp-lease.h"
-#include "sd-dhcp-client.h"
#define DHCP_CLIENT_MIN_OPTIONS_SIZE 312
diff --git a/src/libsystemd-network/lldp-internal.c b/src/libsystemd-network/lldp-internal.c
index c6a989aac1..0f354461f7 100644
--- a/src/libsystemd-network/lldp-internal.c
+++ b/src/libsystemd-network/lldp-internal.c
@@ -54,14 +54,14 @@ int lldp_read_chassis_id(tlv_packet *tlv,
break;
default:
- r = -ENOTSUP;
+ r = -EOPNOTSUPP;
break;
}
*type = subtype;
out1:
- (void)lldp_tlv_packet_exit_container(tlv);
+ (void) lldp_tlv_packet_exit_container(tlv);
out2:
return r;
@@ -106,14 +106,14 @@ int lldp_read_port_id(tlv_packet *tlv,
break;
default:
- r = -ENOTSUP;
+ r = -EOPNOTSUPP;
break;
}
*type = subtype;
out1:
- (void)lldp_tlv_packet_exit_container(tlv);
+ (void) lldp_tlv_packet_exit_container(tlv);
out2:
return r;
@@ -130,7 +130,7 @@ int lldp_read_ttl(tlv_packet *tlv, uint16_t *ttl) {
r = tlv_packet_read_u16(tlv, ttl);
- (void)lldp_tlv_packet_exit_container(tlv);
+ (void) lldp_tlv_packet_exit_container(tlv);
out:
return r;
@@ -155,7 +155,7 @@ int lldp_read_system_name(tlv_packet *tlv,
*data = (char *) s;
out:
- (void)lldp_tlv_packet_exit_container(tlv);
+ (void) lldp_tlv_packet_exit_container(tlv);
return r;
}
@@ -179,7 +179,7 @@ int lldp_read_system_description(tlv_packet *tlv,
*data = (char *) s;
out:
- (void)lldp_tlv_packet_exit_container(tlv);
+ (void) lldp_tlv_packet_exit_container(tlv);
return r;
}
@@ -203,7 +203,7 @@ int lldp_read_port_description(tlv_packet *tlv,
*data = (char *) s;
out:
- (void)lldp_tlv_packet_exit_container(tlv);
+ (void) lldp_tlv_packet_exit_container(tlv);
return r;
}
@@ -224,7 +224,7 @@ int lldp_read_system_capability(tlv_packet *tlv, uint16_t *data) {
return 0;
out:
- (void)lldp_tlv_packet_exit_container(tlv);
+ (void) lldp_tlv_packet_exit_container(tlv);
return r;
}
diff --git a/src/libsystemd-network/lldp-network.c b/src/libsystemd-network/lldp-network.c
index a2bc7d5518..664d2f7867 100644
--- a/src/libsystemd-network/lldp-network.c
+++ b/src/libsystemd-network/lldp-network.c
@@ -27,7 +27,6 @@
#include "lldp-tlv.h"
#include "lldp-network.h"
#include "lldp-internal.h"
-#include "sd-lldp.h"
int lldp_network_bind_raw_socket(int ifindex) {
typedef struct LLDPFrame {
diff --git a/src/libsystemd-network/lldp-tlv.c b/src/libsystemd-network/lldp-tlv.c
index e43d70d3cf..e32783f3eb 100644
--- a/src/libsystemd-network/lldp-tlv.c
+++ b/src/libsystemd-network/lldp-tlv.c
@@ -156,7 +156,7 @@ static inline int tlv_packet_read_internal(tlv_section *m, void **data) {
}
int tlv_packet_read_u8(tlv_packet *m, uint8_t *data) {
- void *val;
+ void *val = NULL;
int r;
assert_return(m, -EINVAL);
@@ -174,7 +174,7 @@ int tlv_packet_read_u8(tlv_packet *m, uint8_t *data) {
int tlv_packet_read_u16(tlv_packet *m, uint16_t *data) {
uint16_t t;
- void *val;
+ void *val = NULL;
int r;
assert_return(m, -EINVAL);
@@ -211,7 +211,7 @@ int tlv_packet_read_u32(tlv_packet *m, uint32_t *data) {
}
int tlv_packet_read_string(tlv_packet *m, char **data, uint16_t *data_length) {
- void *val;
+ void *val = NULL;
int r;
assert_return(m, -EINVAL);
@@ -229,7 +229,7 @@ int tlv_packet_read_string(tlv_packet *m, char **data, uint16_t *data_length) {
}
int tlv_packet_read_bytes(tlv_packet *m, uint8_t **data, uint16_t *data_length) {
- void *val;
+ void *val = NULL;
int r;
assert_return(m, -EINVAL);
diff --git a/src/libsystemd-network/network-internal.c b/src/libsystemd-network/network-internal.c
index b8e4e21716..d579755cc8 100644
--- a/src/libsystemd-network/network-internal.c
+++ b/src/libsystemd-network/network-internal.c
@@ -25,7 +25,6 @@
#include "strv.h"
#include "siphash24.h"
-#include "libudev-private.h"
#include "dhcp-lease-internal.h"
#include "log.h"
#include "utf8.h"
diff --git a/src/libsystemd-network/network-internal.h b/src/libsystemd-network/network-internal.h
index e51717e919..06aba893ce 100644
--- a/src/libsystemd-network/network-internal.h
+++ b/src/libsystemd-network/network-internal.h
@@ -21,8 +21,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <netinet/ether.h>
-#include <netinet/in.h>
#include <stdbool.h>
#include "udev.h"
diff --git a/src/libsystemd-network/sd-dhcp-client.c b/src/libsystemd-network/sd-dhcp-client.c
index 5f90617b9e..0193e42d65 100644
--- a/src/libsystemd-network/sd-dhcp-client.c
+++ b/src/libsystemd-network/sd-dhcp-client.c
@@ -24,13 +24,11 @@
#include <net/ethernet.h>
#include <net/if_arp.h>
#include <linux/if_infiniband.h>
-#include <netinet/ether.h>
-#include <sys/param.h>
#include <sys/ioctl.h>
#include "util.h"
-#include "list.h"
#include "refcnt.h"
+#include "random-util.h"
#include "async.h"
#include "dhcp-protocol.h"
@@ -40,7 +38,7 @@
#include "sd-dhcp-client.h"
#define MAX_CLIENT_ID_LEN (sizeof(uint32_t) + MAX_DUID_LEN) /* Arbitrary limit */
-#define MAX_MAC_ADDR_LEN INFINIBAND_ALEN
+#define MAX_MAC_ADDR_LEN CONST_MAX(INFINIBAND_ALEN, ETH_ALEN)
struct sd_dhcp_client {
RefCount n_ref;
diff --git a/src/libsystemd-network/sd-dhcp-lease.c b/src/libsystemd-network/sd-dhcp-lease.c
index 34aa36c6e6..8a4220621b 100644
--- a/src/libsystemd-network/sd-dhcp-lease.c
+++ b/src/libsystemd-network/sd-dhcp-lease.c
@@ -22,22 +22,15 @@
#include <errno.h>
#include <string.h>
#include <stdio.h>
-#include <net/ethernet.h>
#include <arpa/inet.h>
-#include <sys/param.h>
-#include "util.h"
-#include "list.h"
-#include "mkdir.h"
#include "fileio.h"
#include "unaligned.h"
#include "in-addr-util.h"
-
+#include "hostname-util.h"
#include "dhcp-protocol.h"
-#include "dhcp-internal.h"
#include "dhcp-lease-internal.h"
#include "sd-dhcp-lease.h"
-#include "sd-dhcp-client.h"
#include "network-internal.h"
int sd_dhcp_lease_get_address(sd_dhcp_lease *lease, struct in_addr *addr) {
@@ -679,7 +672,7 @@ int sd_dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) {
if (r >= 0) {
_cleanup_free_ char *client_id_hex;
- client_id_hex = hexmem (client_id, client_id_len);
+ client_id_hex = hexmem(client_id, client_id_len);
if (!client_id_hex) {
r = -ENOMEM;
goto finish;
diff --git a/src/libsystemd-network/sd-dhcp-server.c b/src/libsystemd-network/sd-dhcp-server.c
index 3f89f344d7..a0a2320efa 100644
--- a/src/libsystemd-network/sd-dhcp-server.c
+++ b/src/libsystemd-network/sd-dhcp-server.c
@@ -21,7 +21,6 @@
***/
#include <sys/ioctl.h>
-#include <netinet/if_ether.h>
#include "siphash24.h"
@@ -776,7 +775,7 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message,
if (pool_offset >= 0 &&
server->bound_leases[pool_offset] == existing_lease) {
DHCPLease *lease;
- usec_t time_now;
+ usec_t time_now = 0;
if (!existing_lease) {
lease = new0(DHCPLease, 1);
diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c
index a432bbfc48..85162dc555 100644
--- a/src/libsystemd-network/sd-dhcp6-client.c
+++ b/src/libsystemd-network/sd-dhcp6-client.c
@@ -28,6 +28,7 @@
#include "udev-util.h"
#include "util.h"
#include "refcnt.h"
+#include "random-util.h"
#include "network-internal.h"
#include "sd-dhcp6-client.h"
@@ -158,6 +159,13 @@ int sd_dhcp6_client_set_mac(sd_dhcp6_client *client, const uint8_t *addr,
return 0;
}
+static int client_ensure_duid(sd_dhcp6_client *client)
+{
+ if (client->duid_len != 0)
+ return 0;
+ return dhcp_identifier_set_duid_en(&client->duid, &client->duid_len);
+}
+
int sd_dhcp6_client_set_duid(sd_dhcp6_client *client, uint16_t type, uint8_t *duid,
size_t duid_len)
{
@@ -378,6 +386,7 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
if (r < 0)
return r;
+ assert (client->duid_len);
r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_CLIENTID,
client->duid_len, &client->duid);
if (r < 0)
@@ -1108,6 +1117,10 @@ int sd_dhcp6_client_start(sd_dhcp6_client *client)
if (r < 0)
return r;
+ r = client_ensure_duid(client);
+ if (r < 0)
+ return r;
+
r = dhcp6_network_bind_udp_socket(client->index, NULL);
if (r < 0)
return r;
@@ -1192,6 +1205,7 @@ sd_dhcp6_client *sd_dhcp6_client_unref(sd_dhcp6_client *client) {
client_reset(client);
sd_dhcp6_client_detach_event(client);
+ sd_dhcp6_lease_unref(client->lease);
free(client->req_opts);
free(client);
@@ -1205,7 +1219,6 @@ sd_dhcp6_client *sd_dhcp6_client_unref(sd_dhcp6_client *client) {
int sd_dhcp6_client_new(sd_dhcp6_client **ret)
{
_cleanup_dhcp6_client_unref_ sd_dhcp6_client *client = NULL;
- int r;
size_t t;
assert_return(ret, -EINVAL);
@@ -1222,11 +1235,6 @@ int sd_dhcp6_client_new(sd_dhcp6_client **ret)
client->fd = -1;
- /* initialize DUID */
- r = dhcp_identifier_set_duid_en(&client->duid, &client->duid_len);
- if (r < 0)
- return r;
-
client->req_opts_len = ELEMENTSOF(default_req_opts);
client->req_opts = new0(be16_t, client->req_opts_len);
diff --git a/src/libsystemd-network/sd-ipv4ll.c b/src/libsystemd-network/sd-ipv4ll.c
index 02f2f9e0a9..9e04db96bb 100644
--- a/src/libsystemd-network/sd-ipv4ll.c
+++ b/src/libsystemd-network/sd-ipv4ll.c
@@ -27,6 +27,7 @@
#include "siphash24.h"
#include "list.h"
#include "refcnt.h"
+#include "random-util.h"
#include "ipv4ll-internal.h"
#include "sd-ipv4ll.h"
diff --git a/src/libsystemd-network/sd-lldp.c b/src/libsystemd-network/sd-lldp.c
index 19ef2ccdbd..fddda97f52 100644
--- a/src/libsystemd-network/sd-lldp.c
+++ b/src/libsystemd-network/sd-lldp.c
@@ -24,16 +24,13 @@
#include "siphash24.h"
#include "hashmap.h"
-#include "event-util.h"
#include "lldp-tlv.h"
#include "lldp-port.h"
#include "sd-lldp.h"
#include "prioq.h"
-#include "strv.h"
#include "lldp-internal.h"
#include "lldp-util.h"
-#include "ether-addr-util.h"
typedef enum LLDPAgentRXState {
LLDP_AGENT_RX_WAIT_PORT_OPERATIONAL = 4,
@@ -522,7 +519,7 @@ int sd_lldp_save(sd_lldp *lldp, const char *lldp_file) {
free(s);
s = k;
- (void)lldp_read_system_capability(p->packet, &data);
+ (void) lldp_read_system_capability(p->packet, &data);
sprintf(buf, "'_CAP=%x'", data);
diff --git a/src/libsystemd-network/sd-pppoe.c b/src/libsystemd-network/sd-pppoe.c
index 4f49b799ec..1de8a5e8bf 100644
--- a/src/libsystemd-network/sd-pppoe.c
+++ b/src/libsystemd-network/sd-pppoe.c
@@ -33,10 +33,10 @@
#include "event-util.h"
#include "util.h"
+#include "random-util.h"
#include "socket-util.h"
#include "async.h"
#include "refcnt.h"
-#include "unaligned.h"
#include "utf8.h"
#define PPPOE_MAX_PACKET_SIZE 1484
@@ -340,7 +340,7 @@ static int pppoe_timeout(sd_event_source *s, uint64_t usec, void *userdata);
static int pppoe_arm_timeout(sd_pppoe *ppp) {
_cleanup_event_source_unref_ sd_event_source *timeout = NULL;
- usec_t next_timeout;
+ usec_t next_timeout = 0;
int r;
assert(ppp);
diff --git a/src/libsystemd-network/test-dhcp-client.c b/src/libsystemd-network/test-dhcp-client.c
index 8e88641430..d341210887 100644
--- a/src/libsystemd-network/test-dhcp-client.c
+++ b/src/libsystemd-network/test-dhcp-client.c
@@ -19,16 +19,12 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdlib.h>
-#include <assert.h>
#include <errno.h>
#include <stdio.h>
-#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include "util.h"
-#include "socket-util.h"
#include "sd-event.h"
#include "event-util.h"
diff --git a/src/libsystemd-network/test-dhcp-option.c b/src/libsystemd-network/test-dhcp-option.c
index 46c8c27061..a63a4ea738 100644
--- a/src/libsystemd-network/test-dhcp-option.c
+++ b/src/libsystemd-network/test-dhcp-option.c
@@ -1,9 +1,9 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
#include <stdio.h>
#include <stdbool.h>
#include <errno.h>
#include <string.h>
-#include <assert.h>
#include "util.h"
#include "macro.h"
@@ -51,8 +51,7 @@ static struct option_desc option_tests[] = {
DHCP_OVERLOAD_FILE|DHCP_OVERLOAD_SNAME }, 3, true, },
};
-static const char *dhcp_type(int type)
-{
+static const char *dhcp_type(int type) {
switch(type) {
case DHCP_DISCOVER:
return "DHCPDISCOVER";
@@ -73,8 +72,7 @@ static const char *dhcp_type(int type)
}
}
-static void test_invalid_buffer_length(void)
-{
+static void test_invalid_buffer_length(void) {
DHCPMessage message;
assert_se(dhcp_option_parse(&message, 0, NULL, NULL) == -EINVAL);
@@ -82,8 +80,7 @@ static void test_invalid_buffer_length(void)
== -EINVAL);
}
-static void test_message_init(void)
-{
+static void test_message_init(void) {
_cleanup_free_ DHCPMessage *message = NULL;
size_t optlen = 4, optoffset;
size_t len = sizeof(DHCPMessage) + optlen;
@@ -109,8 +106,7 @@ static void test_message_init(void)
static DHCPMessage *create_message(uint8_t *options, uint16_t optlen,
uint8_t *file, uint8_t filelen,
- uint8_t *sname, uint8_t snamelen)
-{
+ uint8_t *sname, uint8_t snamelen) {
DHCPMessage *message;
size_t len = sizeof(DHCPMessage) + optlen;
@@ -129,8 +125,9 @@ static DHCPMessage *create_message(uint8_t *options, uint16_t optlen,
return message;
}
-static void test_ignore_opts(uint8_t *descoption, int *descpos, int *desclen)
-{
+static void test_ignore_opts(uint8_t *descoption, int *descpos, int *desclen) {
+ assert(*descpos >= 0);
+
while (*descpos < *desclen) {
switch(descoption[*descpos]) {
case DHCP_OPTION_PAD:
@@ -148,9 +145,7 @@ static void test_ignore_opts(uint8_t *descoption, int *descpos, int *desclen)
}
}
-static int test_options_cb(uint8_t code, uint8_t len, const uint8_t *option,
- void *user_data)
-{
+static int test_options_cb(uint8_t code, uint8_t len, const uint8_t *option, void *user_data) {
struct option_desc *desc = user_data;
uint8_t *descoption = NULL;
int *desclen = NULL, *descpos = NULL;
@@ -237,8 +232,7 @@ static int test_options_cb(uint8_t code, uint8_t len, const uint8_t *option,
return 0;
}
-static void test_options(struct option_desc *desc)
-{
+static void test_options(struct option_desc *desc) {
uint8_t *options = NULL;
uint8_t *file = NULL;
uint8_t *sname = NULL;
@@ -299,8 +293,7 @@ static uint8_t options[64] = {
255
};
-static void test_option_set(void)
-{
+static void test_option_set(void) {
_cleanup_free_ DHCPMessage *result = NULL;
size_t offset = 0, len, pos;
unsigned i;
@@ -372,9 +365,8 @@ static void test_option_set(void)
printf ("\n");
}
-int main(int argc, char *argv[])
-{
- unsigned int i;
+int main(int argc, char *argv[]) {
+ unsigned i;
test_invalid_buffer_length();
test_message_init();
diff --git a/src/libsystemd-network/test-dhcp-server.c b/src/libsystemd-network/test-dhcp-server.c
index 3e68f310f3..9f60ab761e 100644
--- a/src/libsystemd-network/test-dhcp-server.c
+++ b/src/libsystemd-network/test-dhcp-server.c
@@ -20,8 +20,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <netinet/if_ether.h>
-#include <assert.h>
#include <errno.h>
#include "sd-event.h"
diff --git a/src/libsystemd-network/test-dhcp6-client.c b/src/libsystemd-network/test-dhcp6-client.c
index 9386f31ce4..761854714b 100644
--- a/src/libsystemd-network/test-dhcp6-client.c
+++ b/src/libsystemd-network/test-dhcp6-client.c
@@ -701,7 +701,5 @@ int main(int argc, char *argv[]) {
test_advertise_option(e);
test_client_solicit(e);
- assert_se(!sd_event_unref(e));
-
return 0;
}
diff --git a/src/libsystemd-network/test-ipv4ll.c b/src/libsystemd-network/test-ipv4ll.c
index 459d5c324d..5677bfb2d2 100644
--- a/src/libsystemd-network/test-ipv4ll.c
+++ b/src/libsystemd-network/test-ipv4ll.c
@@ -28,6 +28,7 @@
#include "util.h"
#include "socket-util.h"
+#include "event-util.h"
#include "sd-ipv4ll.h"
#include "ipv4ll-internal.h"
@@ -212,7 +213,7 @@ static void test_basic_request(sd_event *e) {
}
int main(int argc, char *argv[]) {
- sd_event *e;
+ _cleanup_event_unref_ sd_event *e = NULL;
assert_se(sd_event_new(&e) >= 0);
diff --git a/src/libsystemd-network/test-lldp.c b/src/libsystemd-network/test-lldp.c
index ee74ebd418..06545aee59 100644
--- a/src/libsystemd-network/test-lldp.c
+++ b/src/libsystemd-network/test-lldp.c
@@ -21,13 +21,8 @@
***/
#include <stdio.h>
-#include <stdlib.h>
#include <string.h>
-#include <sys/socket.h>
-#include <linux/if.h>
-#include <linux/if_ether.h>
#include <net/ethernet.h>
-#include <sys/types.h>
#include <arpa/inet.h>
#include "macro.h"
diff --git a/src/libsystemd-network/test-pppoe.c b/src/libsystemd-network/test-pppoe.c
index dff83eaf6e..9c8d6f7779 100644
--- a/src/libsystemd-network/test-pppoe.c
+++ b/src/libsystemd-network/test-pppoe.c
@@ -20,23 +20,18 @@
***/
#include <stdlib.h>
-#include <assert.h>
#include <errno.h>
-#include <stdio.h>
-#include <sys/types.h>
-#include <sys/socket.h>
#include <unistd.h>
#include <linux/veth.h>
#include <net/if.h>
#include "util.h"
-#include "socket-util.h"
#include "sd-event.h"
#include "event-util.h"
#include "sd-rtnl.h"
-#include "rtnl-util.h"
#include "sd-pppoe.h"
+#include "process-util.h"
static void pppoe_handler(sd_pppoe *ppp, int event, void *userdata) {
static int pppoe_state = -1;
diff --git a/src/libsystemd-terminal/evcat.c b/src/libsystemd-terminal/evcat.c
index f4c25b5556..bfa166c489 100644
--- a/src/libsystemd-terminal/evcat.c
+++ b/src/libsystemd-terminal/evcat.c
@@ -26,33 +26,29 @@
* problems.
*/
-#include <assert.h>
#include <errno.h>
#include <getopt.h>
#include <libevdev/libevdev.h>
#include <linux/kd.h>
-#include <linux/vt.h>
-#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
-#include <string.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
-#include <systemd/sd-bus.h>
-#include <systemd/sd-event.h>
-#include <systemd/sd-login.h>
#include <termios.h>
#include <unistd.h>
#include <xkbcommon/xkbcommon.h>
+#include "sd-bus.h"
+#include "sd-event.h"
+#include "sd-login.h"
#include "build.h"
-#include "bus-util.h"
#include "event-util.h"
-#include "idev.h"
#include "macro.h"
+#include "signal-util.h"
+#include "util.h"
+#include "idev.h"
#include "sysview.h"
#include "term-internal.h"
-#include "util.h"
typedef struct Evcat Evcat;
diff --git a/src/libsystemd-terminal/grdev-drm.c b/src/libsystemd-terminal/grdev-drm.c
index bc4d4c9e76..4cee95f469 100644
--- a/src/libsystemd-terminal/grdev-drm.c
+++ b/src/libsystemd-terminal/grdev-drm.c
@@ -27,8 +27,6 @@
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/types.h>
-#include <systemd/sd-bus.h>
-#include <systemd/sd-event.h>
#include <unistd.h>
/* Yuck! DRM headers need system headers included first.. but we have to
@@ -37,13 +35,14 @@
#include <drm_fourcc.h>
#include <drm_mode.h>
-#include "bus-util.h"
+#include "sd-bus.h"
+#include "sd-event.h"
#include "hashmap.h"
-#include "grdev.h"
-#include "grdev-internal.h"
#include "macro.h"
-#include "udev-util.h"
#include "util.h"
+#include "bus-util.h"
+#include "grdev.h"
+#include "grdev-internal.h"
#define GRDRM_MAX_TRIES (16)
@@ -2656,8 +2655,7 @@ static void managed_card_disable(grdev_card *card) {
grdrm_card_disable(&cm->card);
}
-static int managed_card_pause_device_fn(sd_bus *bus,
- sd_bus_message *signal,
+static int managed_card_pause_device_fn(sd_bus_message *signal,
void *userdata,
sd_bus_error *ret_error) {
managed_card *cm = userdata;
@@ -2745,8 +2743,7 @@ static int managed_card_pause_device_fn(sd_bus *bus,
return 0;
}
-static int managed_card_resume_device_fn(sd_bus *bus,
- sd_bus_message *signal,
+static int managed_card_resume_device_fn(sd_bus_message *signal,
void *userdata,
sd_bus_error *ret_error) {
managed_card *cm = userdata;
@@ -2848,8 +2845,7 @@ static int managed_card_setup_bus(managed_card *cm) {
return 0;
}
-static int managed_card_take_device_fn(sd_bus *bus,
- sd_bus_message *reply,
+static int managed_card_take_device_fn(sd_bus_message *reply,
void *userdata,
sd_bus_error *ret_error) {
managed_card *cm = userdata;
diff --git a/src/libsystemd-terminal/grdev-internal.h b/src/libsystemd-terminal/grdev-internal.h
index f455dd4172..46d65f0248 100644
--- a/src/libsystemd-terminal/grdev-internal.h
+++ b/src/libsystemd-terminal/grdev-internal.h
@@ -25,12 +25,12 @@
#include <libudev.h>
#include <stdbool.h>
#include <stdlib.h>
-#include <systemd/sd-bus.h>
-#include <systemd/sd-event.h>
-#include "grdev.h"
+#include "sd-bus.h"
+#include "sd-event.h"
#include "hashmap.h"
#include "list.h"
#include "util.h"
+#include "grdev.h"
typedef struct grdev_tile grdev_tile;
typedef struct grdev_display_cache grdev_display_cache;
diff --git a/src/libsystemd-terminal/grdev.c b/src/libsystemd-terminal/grdev.c
index 3b3cf2737b..c386e65982 100644
--- a/src/libsystemd-terminal/grdev.c
+++ b/src/libsystemd-terminal/grdev.c
@@ -19,20 +19,17 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <inttypes.h>
#include <libudev.h>
#include <stdbool.h>
#include <stdlib.h>
-#include <systemd/sd-bus.h>
-#include <systemd/sd-event.h>
-#include <systemd/sd-login.h>
-#include "grdev.h"
-#include "grdev-internal.h"
+#include "sd-bus.h"
+#include "sd-event.h"
#include "hashmap.h"
#include "login-shared.h"
#include "macro.h"
-#include "udev-util.h"
#include "util.h"
+#include "grdev.h"
+#include "grdev-internal.h"
static void pipe_enable(grdev_pipe *pipe);
static void pipe_disable(grdev_pipe *pipe);
diff --git a/src/libsystemd-terminal/grdev.h b/src/libsystemd-terminal/grdev.h
index 35d6eb2abf..110d24e6d5 100644
--- a/src/libsystemd-terminal/grdev.h
+++ b/src/libsystemd-terminal/grdev.h
@@ -53,13 +53,11 @@
#pragma once
-#include <drm_fourcc.h>
-#include <inttypes.h>
#include <libudev.h>
#include <stdbool.h>
#include <stdlib.h>
-#include <systemd/sd-bus.h>
-#include <systemd/sd-event.h>
+#include "sd-bus.h"
+#include "sd-event.h"
#include "util.h"
typedef struct grdev_fb grdev_fb;
diff --git a/src/libsystemd-terminal/idev-evdev.c b/src/libsystemd-terminal/idev-evdev.c
index dfbb2d197f..f1a18b91d3 100644
--- a/src/libsystemd-terminal/idev-evdev.c
+++ b/src/libsystemd-terminal/idev-evdev.c
@@ -20,21 +20,17 @@
***/
#include <fcntl.h>
-#include <inttypes.h>
#include <libevdev/libevdev.h>
#include <libudev.h>
#include <stdbool.h>
#include <stdlib.h>
-#include <systemd/sd-bus.h>
-#include <systemd/sd-event.h>
-#include <unistd.h>
+#include "sd-bus.h"
+#include "sd-event.h"
+#include "macro.h"
+#include "util.h"
#include "bus-util.h"
-#include "hashmap.h"
#include "idev.h"
#include "idev-internal.h"
-#include "macro.h"
-#include "udev-util.h"
-#include "util.h"
typedef struct idev_evdev idev_evdev;
typedef struct unmanaged_evdev unmanaged_evdev;
@@ -531,8 +527,7 @@ static const idev_element_vtable unmanaged_evdev_vtable = {
* you run inside a user session with exclusive device access.
*/
-static int managed_evdev_take_device_fn(sd_bus *bus,
- sd_bus_message *reply,
+static int managed_evdev_take_device_fn(sd_bus_message *reply,
void *userdata,
sd_bus_error *ret_error) {
managed_evdev *em = userdata;
diff --git a/src/libsystemd-terminal/idev-internal.h b/src/libsystemd-terminal/idev-internal.h
index a159aef211..a02a16c408 100644
--- a/src/libsystemd-terminal/idev-internal.h
+++ b/src/libsystemd-terminal/idev-internal.h
@@ -26,13 +26,13 @@
#include <linux/input.h>
#include <stdbool.h>
#include <stdlib.h>
-#include <systemd/sd-bus.h>
-#include <systemd/sd-event.h>
#include <xkbcommon/xkbcommon.h>
+#include "sd-bus.h"
+#include "sd-event.h"
#include "hashmap.h"
-#include "idev.h"
#include "list.h"
#include "util.h"
+#include "idev.h"
typedef struct idev_link idev_link;
typedef struct idev_device_vtable idev_device_vtable;
diff --git a/src/libsystemd-terminal/idev-keyboard.c b/src/libsystemd-terminal/idev-keyboard.c
index 6ee3d8150c..93f49e9458 100644
--- a/src/libsystemd-terminal/idev-keyboard.c
+++ b/src/libsystemd-terminal/idev-keyboard.c
@@ -19,20 +19,19 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <inttypes.h>
#include <stdbool.h>
#include <stdlib.h>
-#include <systemd/sd-bus.h>
-#include <systemd/sd-event.h>
#include <xkbcommon/xkbcommon.h>
#include <xkbcommon/xkbcommon-compose.h>
-#include "bus-util.h"
+#include "sd-bus.h"
+#include "sd-event.h"
#include "hashmap.h"
+#include "macro.h"
+#include "util.h"
+#include "bus-util.h"
#include "idev.h"
#include "idev-internal.h"
-#include "macro.h"
#include "term-internal.h"
-#include "util.h"
typedef struct kbdtbl kbdtbl;
typedef struct kbdmap kbdmap;
@@ -385,10 +384,10 @@ static const struct bus_properties_map kbdctx_locale_map[] = {
{ "X11Layout", "s", NULL, offsetof(kbdctx, locale_x11_layout) },
{ "X11Variant", "s", NULL, offsetof(kbdctx, locale_x11_variant) },
{ "X11Options", "s", NULL, offsetof(kbdctx, locale_x11_options) },
+ { },
};
-static int kbdctx_locale_get_all_fn(sd_bus *bus,
- sd_bus_message *m,
+static int kbdctx_locale_get_all_fn(sd_bus_message *m,
void *userdata,
sd_bus_error *ret_err) {
kbdctx *kc = userdata;
@@ -404,7 +403,7 @@ static int kbdctx_locale_get_all_fn(sd_bus *bus,
return 0;
}
- r = bus_message_map_all_properties(bus, m, kbdctx_locale_map, kc);
+ r = bus_message_map_all_properties(m, kbdctx_locale_map, kc);
if (r < 0) {
log_debug("idev-keyboard: erroneous GetAll() reply from locale1");
return 0;
@@ -448,8 +447,7 @@ error:
return log_debug_errno(r, "idev-keyboard: cannot send GetAll to locale1: %m");
}
-static int kbdctx_locale_props_changed_fn(sd_bus *bus,
- sd_bus_message *signal,
+static int kbdctx_locale_props_changed_fn(sd_bus_message *signal,
void *userdata,
sd_bus_error *ret_err) {
kbdctx *kc = userdata;
@@ -462,7 +460,7 @@ static int kbdctx_locale_props_changed_fn(sd_bus *bus,
if (r < 0)
goto error;
- r = bus_message_map_properties_changed(bus, signal, kbdctx_locale_map, kc);
+ r = bus_message_map_properties_changed(signal, kbdctx_locale_map, kc);
if (r < 0)
goto error;
@@ -507,12 +505,9 @@ static void kbdctx_log_fn(struct xkb_context *ctx, enum xkb_log_level lvl, const
sd_lvl = LOG_INFO;
else if (lvl >= XKB_LOG_LEVEL_WARNING)
sd_lvl = LOG_INFO; /* most XKB warnings really are informational */
- else if (lvl >= XKB_LOG_LEVEL_ERROR)
- sd_lvl = LOG_ERR;
- else if (lvl >= XKB_LOG_LEVEL_CRITICAL)
- sd_lvl = LOG_CRIT;
else
- sd_lvl = LOG_CRIT;
+ /* XKB_LOG_LEVEL_ERROR and worse */
+ sd_lvl = LOG_ERR;
snprintf(buf, sizeof(buf), "idev-xkb: %s", format);
log_internalv(sd_lvl, 0, __FILE__, __LINE__, __func__, buf, args);
diff --git a/src/libsystemd-terminal/idev.c b/src/libsystemd-terminal/idev.c
index 989683f39a..b187934977 100644
--- a/src/libsystemd-terminal/idev.c
+++ b/src/libsystemd-terminal/idev.c
@@ -19,22 +19,17 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <inttypes.h>
#include <libudev.h>
-#include <linux/input.h>
#include <stdbool.h>
#include <stdlib.h>
-#include <systemd/sd-bus.h>
-#include <systemd/sd-event.h>
-#include <systemd/sd-login.h>
-#include <xkbcommon/xkbcommon.h>
+#include "sd-bus.h"
+#include "sd-event.h"
#include "hashmap.h"
-#include "idev.h"
-#include "idev-internal.h"
#include "login-shared.h"
#include "macro.h"
-#include "udev-util.h"
#include "util.h"
+#include "idev.h"
+#include "idev-internal.h"
static void element_open(idev_element *e);
static void element_close(idev_element *e);
@@ -433,8 +428,7 @@ idev_session *idev_find_session(idev_context *c, const char *name) {
return hashmap_get(c->session_map, name);
}
-static int session_resume_device_fn(sd_bus *bus,
- sd_bus_message *signal,
+static int session_resume_device_fn(sd_bus_message *signal,
void *userdata,
sd_bus_error *ret_error) {
idev_session *s = userdata;
@@ -456,8 +450,7 @@ static int session_resume_device_fn(sd_bus *bus,
return 0;
}
-static int session_pause_device_fn(sd_bus *bus,
- sd_bus_message *signal,
+static int session_pause_device_fn(sd_bus_message *signal,
void *userdata,
sd_bus_error *ret_error) {
idev_session *s = userdata;
diff --git a/src/libsystemd-terminal/idev.h b/src/libsystemd-terminal/idev.h
index c8c03f3d41..241677cbbe 100644
--- a/src/libsystemd-terminal/idev.h
+++ b/src/libsystemd-terminal/idev.h
@@ -25,15 +25,12 @@
#pragma once
-#include <inttypes.h>
#include <libudev.h>
#include <linux/input.h>
#include <stdbool.h>
-#include <stdlib.h>
-#include <systemd/sd-bus.h>
-#include <systemd/sd-event.h>
#include <xkbcommon/xkbcommon.h>
-#include "util.h"
+#include "sd-bus.h"
+#include "sd-event.h"
typedef struct idev_data idev_data;
typedef struct idev_data_evdev idev_data_evdev;
diff --git a/src/libsystemd-terminal/modeset.c b/src/libsystemd-terminal/modeset.c
index 6e13432d68..f3a60e1fb0 100644
--- a/src/libsystemd-terminal/modeset.c
+++ b/src/libsystemd-terminal/modeset.c
@@ -30,26 +30,23 @@
#include <errno.h>
#include <getopt.h>
#include <linux/kd.h>
-#include <linux/vt.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
-#include <string.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
-#include <systemd/sd-bus.h>
-#include <systemd/sd-event.h>
-#include <systemd/sd-login.h>
#include <termios.h>
#include <unistd.h>
+#include "sd-bus.h"
+#include "sd-event.h"
+#include "sd-login.h"
#include "build.h"
-#include "bus-util.h"
-#include "event-util.h"
-#include "grdev.h"
-#include "grdev-internal.h"
#include "macro.h"
-#include "sysview.h"
+#include "random-util.h"
+#include "signal-util.h"
#include "util.h"
+#include "grdev.h"
+#include "sysview.h"
typedef struct Modeset Modeset;
diff --git a/src/libsystemd-terminal/subterm.c b/src/libsystemd-terminal/subterm.c
index 63cd2a5ad6..d10e2f549f 100644
--- a/src/libsystemd-terminal/subterm.c
+++ b/src/libsystemd-terminal/subterm.c
@@ -26,7 +26,6 @@
* parent TTY. Think of this like what "GNU-screen" does.
*/
-#include <assert.h>
#include <errno.h>
#include <stdarg.h>
#include <stdbool.h>
@@ -35,13 +34,14 @@
#include <string.h>
#include <sys/ioctl.h>
#include <termios.h>
+#include "sd-event.h"
#include "macro.h"
#include "pty.h"
#include "ring.h"
-#include "sd-event.h"
-#include "term-internal.h"
-#include "util.h"
+#include "signal-util.h"
#include "utf8.h"
+#include "util.h"
+#include "term-internal.h"
typedef struct Output Output;
typedef struct Terminal Terminal;
diff --git a/src/libsystemd-terminal/sysview-internal.h b/src/libsystemd-terminal/sysview-internal.h
index 39ff933eaa..251c8d7300 100644
--- a/src/libsystemd-terminal/sysview-internal.h
+++ b/src/libsystemd-terminal/sysview-internal.h
@@ -25,13 +25,13 @@
#include <libudev.h>
#include <stdbool.h>
#include <stdlib.h>
-#include <systemd/sd-bus.h>
-#include <systemd/sd-event.h>
+#include "sd-bus.h"
+#include "sd-event.h"
#include "hashmap.h"
#include "list.h"
#include "macro.h"
-#include "sysview.h"
#include "util.h"
+#include "sysview.h"
/*
* Devices
@@ -113,6 +113,7 @@ struct sysview_context {
sd_bus *sysbus;
struct udev *ud;
uint64_t custom_sid;
+ unsigned int n_probe;
Hashmap *seat_map;
Hashmap *session_map;
@@ -137,6 +138,7 @@ struct sysview_context {
bool running : 1;
bool scanned : 1;
bool rescan : 1;
+ bool settled : 1;
};
int sysview_context_rescan(sysview_context *c);
diff --git a/src/libsystemd-terminal/sysview.c b/src/libsystemd-terminal/sysview.c
index 9ee32db1bc..2e9b15859a 100644
--- a/src/libsystemd-terminal/sysview.c
+++ b/src/libsystemd-terminal/sysview.c
@@ -23,17 +23,15 @@
#include <libudev.h>
#include <stdbool.h>
#include <stdlib.h>
-#include <systemd/sd-bus.h>
-#include <systemd/sd-event.h>
-#include <systemd/sd-login.h>
-#include "bus-util.h"
-#include "event-util.h"
+#include "sd-bus.h"
+#include "sd-event.h"
+#include "sd-login.h"
#include "macro.h"
-#include "set.h"
-#include "sysview.h"
-#include "sysview-internal.h"
#include "udev-util.h"
#include "util.h"
+#include "bus-util.h"
+#include "sysview.h"
+#include "sysview-internal.h"
static int context_raise_session_control(sysview_context *c, sysview_session *session, int error);
@@ -198,7 +196,7 @@ int sysview_session_new(sysview_session **out, sysview_seat *seat, const char *n
if (r < 0)
return r;
- session->custom = false;;
+ session->custom = false;
} else {
/*
* No session name was given. We assume this is an unmanaged
@@ -273,8 +271,7 @@ sysview_seat *sysview_session_get_seat(sysview_session *session) {
return session->seat;
}
-static int session_take_control_fn(sd_bus *bus,
- sd_bus_message *reply,
+static int session_take_control_fn(sd_bus_message *reply,
void *userdata,
sd_bus_error *ret_error) {
sysview_session *session = userdata;
@@ -476,6 +473,14 @@ static int context_raise(sysview_context *c, sysview_event *event, int def) {
return c->running ? c->event_fn(c, c->userdata, event) : def;
}
+static int context_raise_settle(sysview_context *c) {
+ sysview_event event = {
+ .type = SYSVIEW_EVENT_SETTLE,
+ };
+
+ return context_raise(c, &event, 0);
+}
+
static int context_raise_seat_add(sysview_context *c, sysview_seat *seat) {
sysview_event event = {
.type = SYSVIEW_EVENT_SEAT_ADD,
@@ -587,6 +592,21 @@ static int context_raise_session_refresh(sysview_context *c, sysview_session *se
return context_raise(c, &event, 0);
}
+static void context_settle(sysview_context *c) {
+ int r;
+
+ if (c->n_probe <= 0 || --c->n_probe > 0)
+ return;
+
+ log_debug("sysview: settle");
+
+ c->settled = true;
+
+ r = context_raise_settle(c);
+ if (r < 0)
+ log_debug_errno(r, "sysview: callback failed on settle: %m");
+}
+
static void context_add_device(sysview_context *c, sysview_device *device) {
sysview_session *session;
Iterator i;
@@ -1195,8 +1215,7 @@ static int context_ld_session_removed(sysview_context *c, sd_bus_message *signal
return 0;
}
-static int context_ld_manager_signal_fn(sd_bus *bus,
- sd_bus_message *signal,
+static int context_ld_manager_signal_fn(sd_bus_message *signal,
void *userdata,
sd_bus_error *ret_error) {
sysview_context *c = userdata;
@@ -1239,8 +1258,7 @@ static void context_ld_stop(sysview_context *c) {
c->ld_slot_manager_signal = sd_bus_slot_unref(c->ld_slot_manager_signal);
}
-static int context_ld_list_seats_fn(sd_bus *bus,
- sd_bus_message *reply,
+static int context_ld_list_seats_fn(sd_bus_message *reply,
void *userdata,
sd_bus_error *ret_error) {
sysview_context *c = userdata;
@@ -1253,7 +1271,8 @@ static int context_ld_list_seats_fn(sd_bus *bus,
log_debug("sysview: ListSeats on logind failed: %s: %s",
error->name, error->message);
- return -sd_bus_error_get_errno(error);
+ r = -sd_bus_error_get_errno(error);
+ goto settle;
}
r = sd_bus_message_enter_container(reply, 'a', "(so)");
@@ -1279,16 +1298,19 @@ static int context_ld_list_seats_fn(sd_bus *bus,
r = sd_bus_message_exit_container(reply);
if (r < 0)
- return r;
+ goto error;
- return 0;
+ r = 0;
+ goto settle;
error:
- return log_debug_errno(r, "sysview: erroneous ListSeats response from logind: %m");
+ log_debug_errno(r, "sysview: erroneous ListSeats response from logind: %m");
+settle:
+ context_settle(c);
+ return r;
}
-static int context_ld_list_sessions_fn(sd_bus *bus,
- sd_bus_message *reply,
+static int context_ld_list_sessions_fn(sd_bus_message *reply,
void *userdata,
sd_bus_error *ret_error) {
sysview_context *c = userdata;
@@ -1301,7 +1323,8 @@ static int context_ld_list_sessions_fn(sd_bus *bus,
log_debug("sysview: ListSessions on logind failed: %s: %s",
error->name, error->message);
- return -sd_bus_error_get_errno(error);
+ r = -sd_bus_error_get_errno(error);
+ goto settle;
}
r = sd_bus_message_enter_container(reply, 'a', "(susso)");
@@ -1343,12 +1366,16 @@ static int context_ld_list_sessions_fn(sd_bus *bus,
r = sd_bus_message_exit_container(reply);
if (r < 0)
- return r;
+ goto error;
- return 0;
+ r = 0;
+ goto settle;
error:
- return log_debug_errno(r, "sysview: erroneous ListSessions response from logind: %m");
+ log_debug_errno(r, "sysview: erroneous ListSessions response from logind: %m");
+settle:
+ context_settle(c);
+ return r;
}
static int context_ld_scan(sysview_context *c) {
@@ -1378,6 +1405,9 @@ static int context_ld_scan(sysview_context *c) {
if (r < 0)
return r;
+ if (!c->settled)
+ ++c->n_probe;
+
/* request session list */
m = sd_bus_message_unref(m);
@@ -1399,6 +1429,9 @@ static int context_ld_scan(sysview_context *c) {
if (r < 0)
return r;
+ if (!c->settled)
+ ++c->n_probe;
+
return 0;
}
@@ -1463,6 +1496,8 @@ void sysview_context_stop(sysview_context *c) {
c->running = false;
c->scanned = false;
+ c->settled = false;
+ c->n_probe = 0;
c->event_fn = NULL;
c->userdata = NULL;
c->scan_src = sd_event_source_unref(c->scan_src);
@@ -1476,6 +1511,8 @@ static int context_scan_fn(sd_event_source *s, void *userdata) {
Iterator i;
int r;
+ c->rescan = false;
+
if (!c->scanned) {
r = context_ld_scan(c);
if (r < 0)
@@ -1493,6 +1530,7 @@ static int context_scan_fn(sd_event_source *s, void *userdata) {
}
c->scanned = true;
+ context_settle(c);
return 0;
}
@@ -1503,6 +1541,12 @@ int sysview_context_rescan(sysview_context *c) {
if (!c->running)
return 0;
+ if (!c->rescan) {
+ c->rescan = true;
+ if (!c->settled)
+ ++c->n_probe;
+ }
+
if (c->scan_src)
return sd_event_source_set_enabled(c->scan_src, SD_EVENT_ONESHOT);
else
diff --git a/src/libsystemd-terminal/sysview.h b/src/libsystemd-terminal/sysview.h
index cad603d59f..a5e7a38df3 100644
--- a/src/libsystemd-terminal/sysview.h
+++ b/src/libsystemd-terminal/sysview.h
@@ -36,13 +36,9 @@
#pragma once
-#include <inttypes.h>
-#include <libudev.h>
#include <stdbool.h>
-#include <stdlib.h>
-#include <systemd/sd-bus.h>
-#include <systemd/sd-event.h>
-#include "util.h"
+#include "sd-bus.h"
+#include "sd-event.h"
typedef struct sysview_event sysview_event;
typedef struct sysview_device sysview_device;
@@ -55,6 +51,8 @@ typedef struct sysview_context sysview_context;
*/
enum {
+ SYSVIEW_EVENT_SETTLE,
+
SYSVIEW_EVENT_SEAT_ADD,
SYSVIEW_EVENT_SEAT_REMOVE,
diff --git a/src/libsystemd-terminal/term-charset.c b/src/libsystemd-terminal/term-charset.c
index a00a1912da..9db178861c 100644
--- a/src/libsystemd-terminal/term-charset.c
+++ b/src/libsystemd-terminal/term-charset.c
@@ -60,9 +60,6 @@
* include them. The mapper has to take care not to use them in GL.
*/
-#include <errno.h>
-#include <stdlib.h>
-#include <string.h>
#include "term-internal.h"
/*
diff --git a/src/libsystemd-terminal/term-page.c b/src/libsystemd-terminal/term-page.c
index ae73cdf627..bac85200f1 100644
--- a/src/libsystemd-terminal/term-page.c
+++ b/src/libsystemd-terminal/term-page.c
@@ -75,7 +75,6 @@
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
-#include <wchar.h>
#include "macro.h"
#include "term-internal.h"
#include "util.h"
@@ -799,11 +798,9 @@ static inline void line_insert(term_line *line, unsigned int from, unsigned int
age);
/* adjust fill-state */
- DISABLE_WARNING_SHADOW;
line->fill = MIN(line->width,
MAX(line->fill + num,
from + num));
- REENABLE_WARNING;
} else {
/* modify head-cell */
term_cell_set(line->cells + from,
@@ -872,11 +869,9 @@ void term_line_write(term_line *line, unsigned int pos_x, term_char_t ch, unsign
age);
/* adjust fill-state */
- DISABLE_WARNING_SHADOW;
line->fill = MIN(line->width,
MAX(line->fill,
pos_x + len));
- REENABLE_WARNING;
}
}
diff --git a/src/libsystemd-terminal/term-wcwidth.c b/src/libsystemd-terminal/term-wcwidth.c
index df20a40451..833a099bd7 100644
--- a/src/libsystemd-terminal/term-wcwidth.c
+++ b/src/libsystemd-terminal/term-wcwidth.c
@@ -62,7 +62,6 @@
* Latest version: http://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c
*/
-#include <wchar.h>
#include "term-internal.h"
struct interval {
diff --git a/src/libsystemd-terminal/test-term-page.c b/src/libsystemd-terminal/test-term-page.c
index 9e338776e8..d59139b62d 100644
--- a/src/libsystemd-terminal/test-term-page.c
+++ b/src/libsystemd-terminal/test-term-page.c
@@ -25,14 +25,10 @@
* those internals are changed. They should be fairly obvious, though.
*/
-#include <assert.h>
-#include <errno.h>
#include <stdio.h>
-#include <stdlib.h>
#include <string.h>
#include "macro.h"
#include "term-internal.h"
-#include "util.h"
#define MY_ASSERT_VALS __FILE__, __LINE__, __PRETTY_FUNCTION__
#define MY_ASSERT_FORW _FILE, _LINE, _FUNC
diff --git a/src/libsystemd-terminal/test-term-parser.c b/src/libsystemd-terminal/test-term-parser.c
index e22614d06d..e40b267b1c 100644
--- a/src/libsystemd-terminal/test-term-parser.c
+++ b/src/libsystemd-terminal/test-term-parser.c
@@ -22,14 +22,10 @@
* Terminal Parser Tests
*/
-#include <assert.h>
-#include <errno.h>
#include <stdio.h>
-#include <stdlib.h>
#include <string.h>
#include "macro.h"
#include "term-internal.h"
-#include "util.h"
#include "utf8.h"
static void test_term_utf8_invalid(void) {
diff --git a/src/libsystemd-terminal/test-unifont.c b/src/libsystemd-terminal/test-unifont.c
index cfeef61a47..2366d38574 100644
--- a/src/libsystemd-terminal/test-unifont.c
+++ b/src/libsystemd-terminal/test-unifont.c
@@ -24,14 +24,11 @@
* The glyphs are then compared to hard-coded glyphs.
*/
-#include <errno.h>
#include <stdio.h>
-#include <stdlib.h>
#include <string.h>
#include "macro.h"
#include "unifont-def.h"
#include "unifont.h"
-#include "util.h"
static void render(char *w, const unifont_glyph *g) {
unsigned int i, j;
diff --git a/src/libsystemd-terminal/unifont.c b/src/libsystemd-terminal/unifont.c
index 2acfa9821a..0da81e8ff2 100644
--- a/src/libsystemd-terminal/unifont.c
+++ b/src/libsystemd-terminal/unifont.c
@@ -28,13 +28,10 @@
#include <endian.h>
#include <fcntl.h>
-#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
#include "macro.h"
#include "unifont-def.h"
#include "unifont.h"
diff --git a/src/libsystemd-terminal/unifont.h b/src/libsystemd-terminal/unifont.h
index 30527cb3fa..74ee5ecb3c 100644
--- a/src/libsystemd-terminal/unifont.h
+++ b/src/libsystemd-terminal/unifont.h
@@ -21,10 +21,7 @@
#pragma once
-#include <stdbool.h>
#include <stdint.h>
-#include <stdlib.h>
-#include "util.h"
typedef struct unifont unifont;
typedef struct unifont_glyph unifont_glyph;
diff --git a/src/libsystemd/libsystemd.sym.m4 b/src/libsystemd/libsystemd.sym.m4
index 76a8c921c6..0b94a87dd6 100644
--- a/src/libsystemd/libsystemd.sym.m4
+++ b/src/libsystemd/libsystemd.sym.m4
@@ -63,7 +63,7 @@ global:
sd_journal_open_files;
sd_journal_open_container;
- /* sd-dameon */
+ /* sd-daemon */
sd_booted;
sd_is_fifo;
sd_is_mq;
@@ -163,8 +163,13 @@ global:
sd_pid_notify_with_fds;
} LIBSYSTEMD_217;
-m4_ifdef(`ENABLE_KDBUS',
-LIBSYSTEMD_FUTURE {
+LIBSYSTEMD_220 {
+global:
+ sd_pid_get_user_slice;
+ sd_peer_get_user_slice;
+} LIBSYSTEMD_219;
+
+LIBSYSTEMD_221 {
global:
/* sd-bus */
sd_bus_default;
@@ -179,25 +184,36 @@ global:
sd_bus_set_address;
sd_bus_set_fd;
sd_bus_set_exec;
+ sd_bus_get_address;
sd_bus_set_bus_client;
+ sd_bus_is_bus_client;
sd_bus_set_server;
+ sd_bus_is_server;
sd_bus_set_anonymous;
+ sd_bus_is_anonymous;
sd_bus_set_trusted;
+ sd_bus_is_trusted;
sd_bus_set_monitor;
+ sd_bus_is_monitor;
sd_bus_set_description;
- sd_bus_negotiate_fds;
- sd_bus_negotiate_timestamp;
+ sd_bus_get_description;
sd_bus_negotiate_creds;
+ sd_bus_negotiate_timestamp;
+ sd_bus_negotiate_fds;
+ sd_bus_can_send;
+ sd_bus_get_creds_mask;
+ sd_bus_set_allow_interactive_authorization;
+ sd_bus_get_allow_interactive_authorization;
sd_bus_start;
sd_bus_close;
sd_bus_try_close;
sd_bus_ref;
sd_bus_unref;
sd_bus_is_open;
- sd_bus_can_send;
sd_bus_get_bus_id;
+ sd_bus_get_scope;
+ sd_bus_get_tid;
sd_bus_get_owner_creds;
- sd_bus_get_description;
sd_bus_send;
sd_bus_send_to;
sd_bus_call;
@@ -209,9 +225,10 @@ global:
sd_bus_process_priority;
sd_bus_wait;
sd_bus_flush;
- sd_bus_get_current_message;
sd_bus_get_current_slot;
- sd_bus_get_tid;
+ sd_bus_get_current_message;
+ sd_bus_get_current_handler;
+ sd_bus_get_current_userdata;
sd_bus_attach_event;
sd_bus_detach_event;
sd_bus_get_event;
@@ -231,6 +248,8 @@ global:
sd_bus_slot_get_description;
sd_bus_slot_set_description;
sd_bus_slot_get_current_message;
+ sd_bus_slot_get_current_handler;
+ sd_bus_slot_get_current_userdata;
sd_bus_message_new_signal;
sd_bus_message_new_method_call;
sd_bus_message_new_method_return;
@@ -240,13 +259,13 @@ global:
sd_bus_message_new_method_errnof;
sd_bus_message_ref;
sd_bus_message_unref;
- sd_bus_message_get_bus;
sd_bus_message_get_type;
sd_bus_message_get_cookie;
sd_bus_message_get_reply_cookie;
+ sd_bus_message_get_priority;
sd_bus_message_get_expect_reply;
sd_bus_message_get_auto_start;
- sd_bus_message_get_priority;
+ sd_bus_message_get_allow_interactive_authorization;
sd_bus_message_get_signature;
sd_bus_message_get_path;
sd_bus_message_get_interface;
@@ -258,13 +277,16 @@ global:
sd_bus_message_get_monotonic_usec;
sd_bus_message_get_realtime_usec;
sd_bus_message_get_seqnum;
+ sd_bus_message_get_bus;
sd_bus_message_get_creds;
- sd_bus_message_is_empty;
sd_bus_message_is_signal;
sd_bus_message_is_method_call;
sd_bus_message_is_method_error;
+ sd_bus_message_is_empty;
+ sd_bus_message_has_signature;
sd_bus_message_set_expect_reply;
sd_bus_message_set_auto_start;
+ sd_bus_message_set_allow_interactive_authorization;
sd_bus_message_set_destination;
sd_bus_message_set_priority;
sd_bus_message_append;
@@ -316,22 +338,33 @@ global:
sd_bus_emit_interfaces_removed_strv;
sd_bus_emit_interfaces_removed;
sd_bus_query_sender_creds;
+ sd_bus_query_sender_privilege;
sd_bus_creds_new_from_pid;
sd_bus_creds_ref;
sd_bus_creds_unref;
sd_bus_creds_get_mask;
- sd_bus_creds_get_uid;
- sd_bus_creds_get_gid;
+ sd_bus_creds_get_augmented_mask;
sd_bus_creds_get_pid;
+ sd_bus_creds_get_ppid;
sd_bus_creds_get_tid;
+ sd_bus_creds_get_uid;
+ sd_bus_creds_get_euid;
+ sd_bus_creds_get_suid;
+ sd_bus_creds_get_fsuid;
+ sd_bus_creds_get_gid;
+ sd_bus_creds_get_egid;
+ sd_bus_creds_get_sgid;
+ sd_bus_creds_get_fsgid;
+ sd_bus_creds_get_supplementary_gids;
sd_bus_creds_get_comm;
sd_bus_creds_get_tid_comm;
sd_bus_creds_get_exe;
sd_bus_creds_get_cmdline;
sd_bus_creds_get_cgroup;
sd_bus_creds_get_unit;
- sd_bus_creds_get_user_unit;
sd_bus_creds_get_slice;
+ sd_bus_creds_get_user_unit;
+ sd_bus_creds_get_user_slice;
sd_bus_creds_get_session;
sd_bus_creds_get_owner_uid;
sd_bus_creds_has_effective_cap;
@@ -341,6 +374,7 @@ global:
sd_bus_creds_get_selinux_context;
sd_bus_creds_get_audit_session_id;
sd_bus_creds_get_audit_login_uid;
+ sd_bus_creds_get_tty;
sd_bus_creds_get_unique_name;
sd_bus_creds_get_well_known_names;
sd_bus_creds_get_description;
@@ -355,6 +389,7 @@ global:
sd_bus_error_copy;
sd_bus_error_is_set;
sd_bus_error_has_name;
+ sd_bus_error_add_map;
sd_bus_path_encode;
sd_bus_path_decode;
sd_bus_track_new;
@@ -382,9 +417,10 @@ global:
sd_event_add_signal;
sd_event_add_child;
sd_event_add_defer;
+ sd_event_add_post;
sd_event_add_exit;
- sd_event_wait;
sd_event_prepare;
+ sd_event_wait;
sd_event_dispatch;
sd_event_run;
sd_event_loop;
@@ -398,6 +434,9 @@ global:
sd_event_get_watchdog;
sd_event_source_ref;
sd_event_source_unref;
+ sd_event_source_get_event;
+ sd_event_source_get_userdata;
+ sd_event_source_set_userdata;
sd_event_source_set_description;
sd_event_source_get_description;
sd_event_source_set_prepare;
@@ -406,8 +445,6 @@ global:
sd_event_source_set_priority;
sd_event_source_get_enabled;
sd_event_source_set_enabled;
- sd_event_source_get_userdata;
- sd_event_source_set_userdata;
sd_event_source_get_io_fd;
sd_event_source_set_io_fd;
sd_event_source_get_io_events;
@@ -420,8 +457,11 @@ global:
sd_event_source_get_time_clock;
sd_event_source_get_signal;
sd_event_source_get_child_pid;
- sd_event_source_get_event;
+} LIBSYSTEMD_220;
+m4_ifdef(`ENABLE_KDBUS',
+LIBSYSTEMD_FUTURE {
+global:
/* sd-utf8 */
sd_utf8_is_valid;
sd_ascii_is_valid;
@@ -454,5 +494,5 @@ global:
/* sd-path */
sd_path_home;
sd_path_search;
-} LIBSYSTEMD_217;
+} LIBSYSTEMD_220;
)
diff --git a/src/libsystemd/sd-bus/bus-bloom.h b/src/libsystemd/sd-bus/bus-bloom.h
index 96c82d7e8f..a9350d7f51 100644
--- a/src/libsystemd/sd-bus/bus-bloom.h
+++ b/src/libsystemd/sd-bus/bus-bloom.h
@@ -23,7 +23,6 @@
#include <stdbool.h>
#include <stdint.h>
-#include <sys/types.h>
/*
* Our default bloom filter has the following parameters:
diff --git a/src/libsystemd/sd-bus/bus-common-errors.c b/src/libsystemd/sd-bus/bus-common-errors.c
index eb60ca3628..52f8dfd3be 100644
--- a/src/libsystemd/sd-bus/bus-common-errors.c
+++ b/src/libsystemd/sd-bus/bus-common-errors.c
@@ -35,9 +35,9 @@ BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map bus_common_errors[] = {
SD_BUS_ERROR_MAP(BUS_ERROR_NOT_SUBSCRIBED, EINVAL),
SD_BUS_ERROR_MAP(BUS_ERROR_ALREADY_SUBSCRIBED, EINVAL),
SD_BUS_ERROR_MAP(BUS_ERROR_ONLY_BY_DEPENDENCY, EINVAL),
- SD_BUS_ERROR_MAP(BUS_ERROR_TRANSACTION_JOBS_CONFLICTING, EDEADLOCK),
- SD_BUS_ERROR_MAP(BUS_ERROR_TRANSACTION_ORDER_IS_CYCLIC, EDEADLOCK),
- SD_BUS_ERROR_MAP(BUS_ERROR_TRANSACTION_IS_DESTRUCTIVE, EDEADLOCK),
+ SD_BUS_ERROR_MAP(BUS_ERROR_TRANSACTION_JOBS_CONFLICTING, EDEADLK),
+ SD_BUS_ERROR_MAP(BUS_ERROR_TRANSACTION_ORDER_IS_CYCLIC, EDEADLK),
+ SD_BUS_ERROR_MAP(BUS_ERROR_TRANSACTION_IS_DESTRUCTIVE, EDEADLK),
SD_BUS_ERROR_MAP(BUS_ERROR_UNIT_MASKED, EBADR),
SD_BUS_ERROR_MAP(BUS_ERROR_JOB_TYPE_NOT_APPLICABLE, EBADR),
SD_BUS_ERROR_MAP(BUS_ERROR_NO_ISOLATION, EPERM),
@@ -60,7 +60,7 @@ BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map bus_common_errors[] = {
SD_BUS_ERROR_MAP(BUS_ERROR_DEVICE_IS_TAKEN, EINVAL),
SD_BUS_ERROR_MAP(BUS_ERROR_DEVICE_NOT_TAKEN, EINVAL),
SD_BUS_ERROR_MAP(BUS_ERROR_OPERATION_IN_PROGRESS, EINPROGRESS),
- SD_BUS_ERROR_MAP(BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED, ENOTSUP),
+ SD_BUS_ERROR_MAP(BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED, EOPNOTSUPP),
SD_BUS_ERROR_MAP(BUS_ERROR_AUTOMATIC_TIME_SYNC_ENABLED, EALREADY),
@@ -70,7 +70,7 @@ BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map bus_common_errors[] = {
SD_BUS_ERROR_MAP(BUS_ERROR_INVALID_REPLY, EINVAL),
SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_RR, ENOENT),
SD_BUS_ERROR_MAP(BUS_ERROR_NO_RESOURCES, ENOMEM),
- SD_BUS_ERROR_MAP(BUS_ERROR_CNAME_LOOP, EDEADLOCK),
+ SD_BUS_ERROR_MAP(BUS_ERROR_CNAME_LOOP, EDEADLK),
SD_BUS_ERROR_MAP(BUS_ERROR_ABORTED, ECANCELED),
SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_TRANSFER, ENXIO),
diff --git a/src/libsystemd/sd-bus/bus-common-errors.h b/src/libsystemd/sd-bus/bus-common-errors.h
index e935833bc3..b17b62ac93 100644
--- a/src/libsystemd/sd-bus/bus-common-errors.h
+++ b/src/libsystemd/sd-bus/bus-common-errors.h
@@ -21,7 +21,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "sd-bus.h"
#include "bus-error.h"
#define BUS_ERROR_NO_SUCH_UNIT "org.freedesktop.systemd1.NoSuchUnit"
diff --git a/src/libsystemd/sd-bus/bus-container.c b/src/libsystemd/sd-bus/bus-container.c
index d29b98a269..f157c25bba 100644
--- a/src/libsystemd/sd-bus/bus-container.c
+++ b/src/libsystemd/sd-bus/bus-container.c
@@ -23,7 +23,7 @@
#include <fcntl.h>
#include "util.h"
-#include "fileio.h"
+#include "process-util.h"
#include "bus-internal.h"
#include "bus-socket.h"
#include "bus-container.h"
diff --git a/src/libsystemd/sd-bus/bus-control.c b/src/libsystemd/sd-bus/bus-control.c
index 06e5b4fd9a..43ddfc651d 100644
--- a/src/libsystemd/sd-bus/bus-control.c
+++ b/src/libsystemd/sd-bus/bus-control.c
@@ -34,7 +34,6 @@
#include "bus-bloom.h"
#include "bus-util.h"
#include "capability.h"
-#include "cgroup-util.h"
_public_ int sd_bus_get_unique_name(sd_bus *bus, const char **unique) {
int r;
@@ -43,6 +42,9 @@ _public_ int sd_bus_get_unique_name(sd_bus *bus, const char **unique) {
assert_return(unique, -EINVAL);
assert_return(!bus_pid_changed(bus), -ECHILD);
+ if (!bus->bus_client)
+ return -EINVAL;
+
r = bus_ensure_running(bus);
if (r < 0)
return r;
@@ -77,7 +79,7 @@ static int bus_request_name_kernel(sd_bus *bus, const char *name, uint64_t flags
if (r < 0)
return -errno;
- if (n->flags & KDBUS_NAME_IN_QUEUE)
+ if (n->return_flags & KDBUS_NAME_IN_QUEUE)
return 0;
return 1;
@@ -131,12 +133,14 @@ static int bus_request_name_dbus1(sd_bus *bus, const char *name, uint64_t flags)
_public_ int sd_bus_request_name(sd_bus *bus, const char *name, uint64_t flags) {
assert_return(bus, -EINVAL);
assert_return(name, -EINVAL);
- assert_return(bus->bus_client, -EINVAL);
assert_return(!bus_pid_changed(bus), -ECHILD);
assert_return(!(flags & ~(SD_BUS_NAME_ALLOW_REPLACEMENT|SD_BUS_NAME_REPLACE_EXISTING|SD_BUS_NAME_QUEUE)), -EINVAL);
assert_return(service_name_is_valid(name), -EINVAL);
assert_return(name[0] != ':', -EINVAL);
+ if (!bus->bus_client)
+ return -EINVAL;
+
/* Don't allow requesting the special driver and local names */
if (STR_IN_SET(name, "org.freedesktop.DBus", "org.freedesktop.DBus.Local"))
return -EINVAL;
@@ -214,11 +218,13 @@ static int bus_release_name_dbus1(sd_bus *bus, const char *name) {
_public_ int sd_bus_release_name(sd_bus *bus, const char *name) {
assert_return(bus, -EINVAL);
assert_return(name, -EINVAL);
- assert_return(bus->bus_client, -EINVAL);
assert_return(!bus_pid_changed(bus), -ECHILD);
assert_return(service_name_is_valid(name), -EINVAL);
assert_return(name[0] != ':', -EINVAL);
+ if (!bus->bus_client)
+ return -EINVAL;
+
/* Don't allow releasing the special driver and local names */
if (STR_IN_SET(name, "org.freedesktop.DBus", "org.freedesktop.DBus.Local"))
return -EINVAL;
@@ -375,6 +381,9 @@ _public_ int sd_bus_list_names(sd_bus *bus, char ***acquired, char ***activatabl
assert_return(acquired || activatable, -EINVAL);
assert_return(!bus_pid_changed(bus), -ECHILD);
+ if (!bus->bus_client)
+ return -EINVAL;
+
if (!BUS_IS_OPEN(bus->state))
return -ENOTCONN;
@@ -414,6 +423,26 @@ static int bus_populate_creds_from_items(
c->mask |= SD_BUS_CREDS_TID;
}
+ if (mask & SD_BUS_CREDS_PPID) {
+ if (item->pids.ppid > 0) {
+ c->ppid = (pid_t) item->pids.ppid;
+ c->mask |= SD_BUS_CREDS_PPID;
+ } else if (item->pids.pid == 1) {
+ /* The structure doesn't
+ * really distinguish the case
+ * where a process has no
+ * parent and where we don't
+ * know it because it could
+ * not be translated due to
+ * namespaces. However, we
+ * know that PID 1 has no
+ * parent process, hence let's
+ * patch that in, manually. */
+ c->ppid = 0;
+ c->mask |= SD_BUS_CREDS_PPID;
+ }
+ }
+
break;
case KDBUS_ITEM_CREDS:
@@ -551,12 +580,12 @@ static int bus_populate_creds_from_items(
break;
case KDBUS_ITEM_AUDIT:
- if (mask & SD_BUS_CREDS_AUDIT_SESSION_ID && (uint32_t) item->audit.sessionid != (uint32_t) -1) {
+ if (mask & SD_BUS_CREDS_AUDIT_SESSION_ID) {
c->audit_session_id = (uint32_t) item->audit.sessionid;
c->mask |= SD_BUS_CREDS_AUDIT_SESSION_ID;
}
- if (mask & SD_BUS_CREDS_AUDIT_LOGIN_UID && (uid_t) item->audit.loginuid != UID_INVALID) {
+ if (mask & SD_BUS_CREDS_AUDIT_LOGIN_UID) {
c->audit_login_uid = (uid_t) item->audit.loginuid;
c->mask |= SD_BUS_CREDS_AUDIT_LOGIN_UID;
}
@@ -584,16 +613,17 @@ static int bus_populate_creds_from_items(
case KDBUS_ITEM_AUXGROUPS:
if (mask & SD_BUS_CREDS_SUPPLEMENTARY_GIDS) {
- size_t n;
+ size_t i, n;
uid_t *g;
- assert_cc(sizeof(gid_t) == sizeof(uint32_t));
-
- n = (item->size - offsetof(struct kdbus_item, data32)) / sizeof(uint32_t);
- g = newdup(gid_t, item->data32, n);
+ n = (item->size - offsetof(struct kdbus_item, data64)) / sizeof(uint64_t);
+ g = new(gid_t, n);
if (!g)
return -ENOMEM;
+ for (i = 0; i < n; i++)
+ g[i] = item->data64[i];
+
free(c->supplementary_gids);
c->supplementary_gids = g;
c->n_supplementary_gids = n;
@@ -622,7 +652,7 @@ int bus_get_name_creds_kdbus(
int r;
if (streq(name, "org.freedesktop.DBus"))
- return -ENOTSUP;
+ return -EOPNOTSUPP;
r = bus_kernel_parse_unique_name(name, &id);
if (r < 0)
@@ -644,7 +674,8 @@ int bus_get_name_creds_kdbus(
* the bits we want, then ask for the PID/TID so that we
* can read the rest from /proc. */
if ((mask & SD_BUS_CREDS_AUGMENT) &&
- (mask & (SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_SUID|SD_BUS_CREDS_FSUID|
+ (mask & (SD_BUS_CREDS_PPID|
+ SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_SUID|SD_BUS_CREDS_FSUID|
SD_BUS_CREDS_GID|SD_BUS_CREDS_EGID|SD_BUS_CREDS_SGID|SD_BUS_CREDS_FSGID|
SD_BUS_CREDS_COMM|SD_BUS_CREDS_TID_COMM|SD_BUS_CREDS_EXE|SD_BUS_CREDS_CMDLINE|
SD_BUS_CREDS_CGROUP|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_USER_UNIT|SD_BUS_CREDS_SLICE|SD_BUS_CREDS_SESSION|SD_BUS_CREDS_OWNER_UID|
@@ -654,7 +685,7 @@ int bus_get_name_creds_kdbus(
mask |= SD_BUS_CREDS_PID;
cmd->size = size;
- cmd->flags = attach_flags_to_kdbus(mask);
+ cmd->attach_flags = attach_flags_to_kdbus(mask);
r = ioctl(bus->input_fd, KDBUS_CMD_CONN_INFO, cmd);
if (r < 0)
@@ -875,11 +906,13 @@ _public_ int sd_bus_get_name_creds(
assert_return(bus, -EINVAL);
assert_return(name, -EINVAL);
- assert_return((mask & ~SD_BUS_CREDS_AUGMENT) <= _SD_BUS_CREDS_ALL, -ENOTSUP);
+ assert_return((mask & ~SD_BUS_CREDS_AUGMENT) <= _SD_BUS_CREDS_ALL, -EOPNOTSUPP);
assert_return(mask == 0 || creds, -EINVAL);
assert_return(!bus_pid_changed(bus), -ECHILD);
assert_return(service_name_is_valid(name), -EINVAL);
- assert_return(bus->bus_client, -ENODATA);
+
+ if (!bus->bus_client)
+ return -EINVAL;
if (streq(name, "org.freedesktop.DBus.Local"))
return -EINVAL;
@@ -910,7 +943,8 @@ static int bus_get_owner_creds_kdbus(sd_bus *bus, uint64_t mask, sd_bus_creds **
* to get the bits we want, then ask for the PID/TID so that we
* can read the rest from /proc. */
if ((mask & SD_BUS_CREDS_AUGMENT) &&
- (mask & (SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_SUID|SD_BUS_CREDS_FSUID|
+ (mask & (SD_BUS_CREDS_PPID|
+ SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_SUID|SD_BUS_CREDS_FSUID|
SD_BUS_CREDS_GID|SD_BUS_CREDS_EGID|SD_BUS_CREDS_SGID|SD_BUS_CREDS_FSGID|
SD_BUS_CREDS_COMM|SD_BUS_CREDS_TID_COMM|SD_BUS_CREDS_EXE|SD_BUS_CREDS_CMDLINE|
SD_BUS_CREDS_CGROUP|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_USER_UNIT|SD_BUS_CREDS_SLICE|SD_BUS_CREDS_SESSION|SD_BUS_CREDS_OWNER_UID|
@@ -919,7 +953,7 @@ static int bus_get_owner_creds_kdbus(sd_bus *bus, uint64_t mask, sd_bus_creds **
SD_BUS_CREDS_AUDIT_SESSION_ID|SD_BUS_CREDS_AUDIT_LOGIN_UID)))
mask |= SD_BUS_CREDS_PID;
- cmd.flags = attach_flags_to_kdbus(mask);
+ cmd.attach_flags = attach_flags_to_kdbus(mask);
r = ioctl(bus->input_fd, KDBUS_CMD_BUS_CREATOR_INFO, &cmd);
if (r < 0)
@@ -989,7 +1023,7 @@ static int bus_get_owner_creds_dbus1(sd_bus *bus, uint64_t mask, sd_bus_creds **
_public_ int sd_bus_get_owner_creds(sd_bus *bus, uint64_t mask, sd_bus_creds **ret) {
assert_return(bus, -EINVAL);
- assert_return((mask & ~SD_BUS_CREDS_AUGMENT) <= _SD_BUS_CREDS_ALL, -ENOTSUP);
+ assert_return((mask & ~SD_BUS_CREDS_AUGMENT) <= _SD_BUS_CREDS_ALL, -EOPNOTSUPP);
assert_return(ret, -EINVAL);
assert_return(!bus_pid_changed(bus), -ECHILD);
@@ -1393,6 +1427,9 @@ int bus_add_match_internal(
assert(bus);
+ if (!bus->bus_client)
+ return -EINVAL;
+
if (bus->is_kernel)
return bus_add_match_internal_kernel(bus, components, n_components, cookie);
else
@@ -1452,6 +1489,9 @@ int bus_remove_match_internal(
assert(bus);
+ if (!bus->bus_client)
+ return -EINVAL;
+
if (bus->is_kernel)
return bus_remove_match_internal_kernel(bus, cookie);
else
@@ -1469,6 +1509,9 @@ _public_ int sd_bus_get_name_machine_id(sd_bus *bus, const char *name, sd_id128_
assert_return(!bus_pid_changed(bus), -ECHILD);
assert_return(service_name_is_valid(name), -EINVAL);
+ if (!bus->bus_client)
+ return -EINVAL;
+
if (!BUS_IS_OPEN(bus->state))
return -ENOTCONN;
diff --git a/src/libsystemd/sd-bus/bus-convenience.c b/src/libsystemd/sd-bus/bus-convenience.c
index a6317e9785..28bc8d2818 100644
--- a/src/libsystemd/sd-bus/bus-convenience.c
+++ b/src/libsystemd/sd-bus/bus-convenience.c
@@ -462,11 +462,22 @@ _public_ int sd_bus_query_sender_creds(sd_bus_message *call, uint64_t mask, sd_b
/* No data passed? Or not enough data passed to retrieve the missing bits? */
if (!c || !(c->mask & SD_BUS_CREDS_PID)) {
/* We couldn't read anything from the call, let's try
- * to get it from the sender or peer */
+ * to get it from the sender or peer. */
if (call->sender)
+ /* There's a sender, but the creds are
+ * missing. This means we are talking via
+ * dbus1, or are getting a message that was
+ * sent to us via kdbus, but was converted
+ * from a dbus1 message by the bus-proxy and
+ * thus also lacks the creds. */
return sd_bus_get_name_creds(call->bus, call->sender, mask, creds);
else
+ /* There's no sender, hence we are on a dbus1
+ * direct connection. For direct connections
+ * the credentials of the AF_UNIX peer matter,
+ * which may be queried via
+ * sd_bus_get_owner_creds(). */
return sd_bus_get_owner_creds(call->bus, mask, creds);
}
@@ -488,10 +499,18 @@ _public_ int sd_bus_query_sender_privilege(sd_bus_message *call, int capability)
return -ENOTCONN;
if (capability >= 0) {
+
r = sd_bus_query_sender_creds(call, SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_EFFECTIVE_CAPS, &creds);
if (r < 0)
return r;
+ /* We cannot use augmented caps for authorization,
+ * since then data is acquired raceful from
+ * /proc. This can never actually happen, but let's
+ * better be safe than sorry, and do an extra check
+ * here. */
+ assert_return((sd_bus_creds_get_augmented_mask(creds) & SD_BUS_CREDS_EFFECTIVE_CAPS) == 0, -EPERM);
+
/* Note that not even on kdbus we might have the caps
* field, due to faked identities, or namespace
* translation issues. */
@@ -512,6 +531,13 @@ _public_ int sd_bus_query_sender_privilege(sd_bus_message *call, int capability)
if (our_uid != 0 || !know_caps || capability < 0) {
uid_t sender_uid;
+ /* We cannot use augmented uid/euid for authorization,
+ * since then data is acquired raceful from
+ * /proc. This can never actually happen, but let's
+ * better be safe than sorry, and do an extra check
+ * here. */
+ assert_return((sd_bus_creds_get_augmented_mask(creds) & (SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID)) == 0, -EPERM);
+
/* Try to use the EUID, if we have it. */
r = sd_bus_creds_get_euid(creds, &sender_uid);
if (r < 0)
diff --git a/src/libsystemd/sd-bus/bus-creds.c b/src/libsystemd/sd-bus/bus-creds.c
index ea8a619c5a..4d67619cf8 100644
--- a/src/libsystemd/sd-bus/bus-creds.c
+++ b/src/libsystemd/sd-bus/bus-creds.c
@@ -23,13 +23,15 @@
#include <linux/capability.h>
#include "util.h"
+#include "formats-util.h"
+#include "process-util.h"
+#include "terminal-util.h"
#include "capability.h"
#include "cgroup-util.h"
#include "fileio.h"
#include "audit.h"
#include "bus-message.h"
#include "bus-util.h"
-#include "time-util.h"
#include "strv.h"
#include "bus-creds.h"
#include "bus-label.h"
@@ -51,7 +53,10 @@ void bus_creds_done(sd_bus_creds *c) {
free(c->unit);
free(c->user_unit);
free(c->slice);
+ free(c->user_slice);
free(c->unescaped_description);
+ free(c->supplementary_gids);
+ free(c->tty);
free(c->well_known_names); /* note that this is an strv, but
* we only free the array, not the
@@ -101,7 +106,9 @@ _public_ sd_bus_creds *sd_bus_creds_unref(sd_bus_creds *c) {
free(c->unique_name);
free(c->cgroup_root);
free(c->description);
+
free(c->supplementary_gids);
+ c->supplementary_gids = NULL;
strv_free(c->well_known_names);
c->well_known_names = NULL;
@@ -127,6 +134,12 @@ _public_ uint64_t sd_bus_creds_get_mask(const sd_bus_creds *c) {
return c->mask;
}
+_public_ uint64_t sd_bus_creds_get_augmented_mask(const sd_bus_creds *c) {
+ assert_return(c, 0);
+
+ return c->augmented;
+}
+
sd_bus_creds* bus_creds_new(void) {
sd_bus_creds *c;
@@ -144,7 +157,7 @@ _public_ int sd_bus_creds_new_from_pid(sd_bus_creds **ret, pid_t pid, uint64_t m
int r;
assert_return(pid >= 0, -EINVAL);
- assert_return(mask <= _SD_BUS_CREDS_ALL, -ENOTSUP);
+ assert_return(mask <= _SD_BUS_CREDS_ALL, -EOPNOTSUPP);
assert_return(ret, -EINVAL);
if (pid == 0)
@@ -227,7 +240,6 @@ _public_ int sd_bus_creds_get_gid(sd_bus_creds *c, gid_t *gid) {
return 0;
}
-
_public_ int sd_bus_creds_get_egid(sd_bus_creds *c, gid_t *egid) {
assert_return(c, -EINVAL);
assert_return(egid, -EINVAL);
@@ -284,6 +296,23 @@ _public_ int sd_bus_creds_get_pid(sd_bus_creds *c, pid_t *pid) {
return 0;
}
+_public_ int sd_bus_creds_get_ppid(sd_bus_creds *c, pid_t *ppid) {
+ assert_return(c, -EINVAL);
+ assert_return(ppid, -EINVAL);
+
+ if (!(c->mask & SD_BUS_CREDS_PPID))
+ return -ENODATA;
+
+ /* PID 1 has no parent process. Let's distinguish the case of
+ * not knowing and not having a parent process by the returned
+ * error code. */
+ if (c->ppid == 0)
+ return -ENXIO;
+
+ *ppid = c->ppid;
+ return 0;
+}
+
_public_ int sd_bus_creds_get_tid(sd_bus_creds *c, pid_t *tid) {
assert_return(c, -EINVAL);
assert_return(tid, -EINVAL);
@@ -338,7 +367,9 @@ _public_ int sd_bus_creds_get_exe(sd_bus_creds *c, const char **ret) {
if (!(c->mask & SD_BUS_CREDS_EXE))
return -ENODATA;
- assert(c->exe);
+ if (!c->exe)
+ return -ENXIO;
+
*ret = c->exe;
return 0;
}
@@ -436,6 +467,33 @@ _public_ int sd_bus_creds_get_slice(sd_bus_creds *c, const char **ret) {
return 0;
}
+_public_ int sd_bus_creds_get_user_slice(sd_bus_creds *c, const char **ret) {
+ int r;
+
+ assert_return(c, -EINVAL);
+ assert_return(ret, -EINVAL);
+
+ if (!(c->mask & SD_BUS_CREDS_USER_SLICE))
+ return -ENODATA;
+
+ assert(c->cgroup);
+
+ if (!c->user_slice) {
+ const char *shifted;
+
+ r = cg_shift_path(c->cgroup, c->cgroup_root, &shifted);
+ if (r < 0)
+ return r;
+
+ r = cg_path_get_user_slice(shifted, (char**) &c->user_slice);
+ if (r < 0)
+ return r;
+ }
+
+ *ret = c->user_slice;
+ return 0;
+}
+
_public_ int sd_bus_creds_get_session(sd_bus_creds *c, const char **ret) {
int r;
@@ -488,8 +546,8 @@ _public_ int sd_bus_creds_get_cmdline(sd_bus_creds *c, char ***cmdline) {
if (!(c->mask & SD_BUS_CREDS_CMDLINE))
return -ENODATA;
- assert_return(c->cmdline, -ESRCH);
- assert(c->cmdline);
+ if (!c->cmdline)
+ return -ENXIO;
if (!c->cmdline_array) {
c->cmdline_array = strv_parse_nulstr(c->cmdline, c->cmdline_size);
@@ -508,6 +566,9 @@ _public_ int sd_bus_creds_get_audit_session_id(sd_bus_creds *c, uint32_t *sessio
if (!(c->mask & SD_BUS_CREDS_AUDIT_SESSION_ID))
return -ENODATA;
+ if (c->audit_session_id == AUDIT_SESSION_INVALID)
+ return -ENXIO;
+
*sessionid = c->audit_session_id;
return 0;
}
@@ -519,10 +580,27 @@ _public_ int sd_bus_creds_get_audit_login_uid(sd_bus_creds *c, uid_t *uid) {
if (!(c->mask & SD_BUS_CREDS_AUDIT_LOGIN_UID))
return -ENODATA;
+ if (c->audit_login_uid == UID_INVALID)
+ return -ENXIO;
+
*uid = c->audit_login_uid;
return 0;
}
+_public_ int sd_bus_creds_get_tty(sd_bus_creds *c, const char **ret) {
+ assert_return(c, -EINVAL);
+ assert_return(ret, -EINVAL);
+
+ if (!(c->mask & SD_BUS_CREDS_TTY))
+ return -ENODATA;
+
+ if (!c->tty)
+ return -ENXIO;
+
+ *ret = c->tty;
+ return 0;
+}
+
_public_ int sd_bus_creds_get_unique_name(sd_bus_creds *c, const char **unique_name) {
assert_return(c, -EINVAL);
assert_return(unique_name, -EINVAL);
@@ -593,10 +671,11 @@ static int has_cap(sd_bus_creds *c, unsigned offset, int capability) {
assert(capability >= 0);
assert(c->capability);
- sz = DIV_ROUND_UP(cap_last_cap(), 32U);
- if ((unsigned)capability > cap_last_cap())
+ if ((unsigned) capability > cap_last_cap())
return 0;
+ sz = DIV_ROUND_UP(cap_last_cap(), 32U);
+
return !!(c->capability[offset * sz + CAP_TO_INDEX(capability)] & CAP_TO_MASK(capability));
}
@@ -693,32 +772,33 @@ int bus_creds_add_more(sd_bus_creds *c, uint64_t mask, pid_t pid, pid_t tid) {
if (!(mask & SD_BUS_CREDS_AUGMENT))
return 0;
- missing = mask & ~c->mask;
- if (missing == 0)
- return 0;
-
/* Try to retrieve PID from creds if it wasn't passed to us */
if (pid <= 0 && (c->mask & SD_BUS_CREDS_PID))
pid = c->pid;
- if (tid <= 0 && (c->mask & SD_BUS_CREDS_TID))
- tid = c->pid;
-
/* Without pid we cannot do much... */
if (pid <= 0)
return 0;
- if (pid > 0) {
- c->pid = pid;
- c->mask |= SD_BUS_CREDS_PID;
- }
+ /* Try to retrieve TID from creds if it wasn't passed to us */
+ if (tid <= 0 && (c->mask & SD_BUS_CREDS_TID))
+ tid = c->tid;
+
+ /* Calculate what we shall and can add */
+ missing = mask & ~(c->mask|SD_BUS_CREDS_PID|SD_BUS_CREDS_TID|SD_BUS_CREDS_UNIQUE_NAME|SD_BUS_CREDS_WELL_KNOWN_NAMES|SD_BUS_CREDS_DESCRIPTION|SD_BUS_CREDS_AUGMENT);
+ if (missing == 0)
+ return 0;
+
+ c->pid = pid;
+ c->mask |= SD_BUS_CREDS_PID;
if (tid > 0) {
c->tid = tid;
c->mask |= SD_BUS_CREDS_TID;
}
- if (missing & (SD_BUS_CREDS_UID | SD_BUS_CREDS_EUID | SD_BUS_CREDS_SUID | SD_BUS_CREDS_FSUID |
+ if (missing & (SD_BUS_CREDS_PPID |
+ SD_BUS_CREDS_UID | SD_BUS_CREDS_EUID | SD_BUS_CREDS_SUID | SD_BUS_CREDS_FSUID |
SD_BUS_CREDS_GID | SD_BUS_CREDS_EGID | SD_BUS_CREDS_SGID | SD_BUS_CREDS_FSGID |
SD_BUS_CREDS_SUPPLEMENTARY_GIDS |
SD_BUS_CREDS_EFFECTIVE_CAPS | SD_BUS_CREDS_INHERITABLE_CAPS |
@@ -741,6 +821,25 @@ int bus_creds_add_more(sd_bus_creds *c, uint64_t mask, pid_t pid, pid_t tid) {
FOREACH_LINE(line, f, return -errno) {
truncate_nl(line);
+ if (missing & SD_BUS_CREDS_PPID) {
+ p = startswith(line, "PPid:");
+ if (p) {
+ p += strspn(p, WHITESPACE);
+
+ /* Explicitly check for PPID 0 (which is the case for PID 1) */
+ if (!streq(p, "0")) {
+ r = parse_pid(p, &c->ppid);
+ if (r < 0)
+ return r;
+
+ } else
+ c->ppid = 0;
+
+ c->mask |= SD_BUS_CREDS_PPID;
+ continue;
+ }
+ }
+
if (missing & (SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_SUID|SD_BUS_CREDS_FSUID)) {
p = startswith(line, "Uid:");
if (p) {
@@ -750,10 +849,15 @@ int bus_creds_add_more(sd_bus_creds *c, uint64_t mask, pid_t pid, pid_t tid) {
if (sscanf(p, "%lu %lu %lu %lu", &uid, &euid, &suid, &fsuid) != 4)
return -EIO;
- c->uid = (uid_t) uid;
- c->euid = (uid_t) euid;
- c->suid = (uid_t) suid;
- c->fsuid = (uid_t) fsuid;
+ if (missing & SD_BUS_CREDS_UID)
+ c->uid = (uid_t) uid;
+ if (missing & SD_BUS_CREDS_EUID)
+ c->euid = (uid_t) euid;
+ if (missing & SD_BUS_CREDS_SUID)
+ c->suid = (uid_t) suid;
+ if (missing & SD_BUS_CREDS_FSUID)
+ c->fsuid = (uid_t) fsuid;
+
c->mask |= missing & (SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_SUID|SD_BUS_CREDS_FSUID);
continue;
}
@@ -768,10 +872,15 @@ int bus_creds_add_more(sd_bus_creds *c, uint64_t mask, pid_t pid, pid_t tid) {
if (sscanf(p, "%lu %lu %lu %lu", &gid, &egid, &sgid, &fsgid) != 4)
return -EIO;
- c->gid = (gid_t) gid;
- c->egid = (gid_t) egid;
- c->sgid = (gid_t) sgid;
- c->fsgid = (gid_t) fsgid;
+ if (missing & SD_BUS_CREDS_GID)
+ c->gid = (gid_t) gid;
+ if (missing & SD_BUS_CREDS_EGID)
+ c->egid = (gid_t) egid;
+ if (missing & SD_BUS_CREDS_SGID)
+ c->sgid = (gid_t) sgid;
+ if (missing & SD_BUS_CREDS_FSGID)
+ c->fsgid = (gid_t) fsgid;
+
c->mask |= missing & (SD_BUS_CREDS_GID|SD_BUS_CREDS_EGID|SD_BUS_CREDS_SGID|SD_BUS_CREDS_FSGID);
continue;
}
@@ -879,7 +988,17 @@ int bus_creds_add_more(sd_bus_creds *c, uint64_t mask, pid_t pid, pid_t tid) {
if (missing & SD_BUS_CREDS_EXE) {
r = get_process_exe(pid, &c->exe);
- if (r < 0) {
+ if (r == -ESRCH) {
+ /* Unfortunately we cannot really distinguish
+ * the case here where the process does not
+ * exist, and /proc/$PID/exe being unreadable
+ * because $PID is a kernel thread. Hence,
+ * assume it is a kernel thread, and rely on
+ * that this case is caught with a later
+ * call. */
+ c->exe = NULL;
+ c->mask |= SD_BUS_CREDS_EXE;
+ } else if (r < 0) {
if (r != -EPERM && r != -EACCES)
return r;
} else
@@ -891,17 +1010,18 @@ int bus_creds_add_more(sd_bus_creds *c, uint64_t mask, pid_t pid, pid_t tid) {
p = procfs_file_alloca(pid, "cmdline");
r = read_full_file(p, &c->cmdline, &c->cmdline_size);
+ if (r == -ENOENT)
+ return -ESRCH;
if (r < 0) {
- if (r == -ENOENT)
- return -ESRCH;
if (r != -EPERM && r != -EACCES)
return r;
} else {
if (c->cmdline_size == 0) {
free(c->cmdline);
c->cmdline = NULL;
- } else
- c->mask |= SD_BUS_CREDS_CMDLINE;
+ }
+
+ c->mask |= SD_BUS_CREDS_CMDLINE;
}
}
@@ -912,34 +1032,43 @@ int bus_creds_add_more(sd_bus_creds *c, uint64_t mask, pid_t pid, pid_t tid) {
return -ENOMEM;
r = read_one_line_file(p, &c->tid_comm);
+ if (r == -ENOENT)
+ return -ESRCH;
if (r < 0) {
- if (r == -ENOENT)
- return -ESRCH;
if (r != -EPERM && r != -EACCES)
return r;
} else
c->mask |= SD_BUS_CREDS_TID_COMM;
}
- if (missing & (SD_BUS_CREDS_CGROUP|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_USER_UNIT|SD_BUS_CREDS_SLICE|SD_BUS_CREDS_SESSION|SD_BUS_CREDS_OWNER_UID)) {
+ if (missing & (SD_BUS_CREDS_CGROUP|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_USER_UNIT|SD_BUS_CREDS_SLICE|SD_BUS_CREDS_USER_SLICE|SD_BUS_CREDS_SESSION|SD_BUS_CREDS_OWNER_UID)) {
- r = cg_pid_get_path(NULL, pid, &c->cgroup);
- if (r < 0) {
- if (r != -EPERM && r != -EACCES)
- return r;
- } else {
+ if (!c->cgroup) {
+ r = cg_pid_get_path(NULL, pid, &c->cgroup);
+ if (r < 0) {
+ if (r != -EPERM && r != -EACCES)
+ return r;
+ }
+ }
+
+ if (!c->cgroup_root) {
r = cg_get_root_path(&c->cgroup_root);
if (r < 0)
return r;
-
- c->mask |= missing & (SD_BUS_CREDS_CGROUP|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_USER_UNIT|SD_BUS_CREDS_SLICE|SD_BUS_CREDS_SESSION|SD_BUS_CREDS_OWNER_UID);
}
+
+ if (c->cgroup)
+ c->mask |= missing & (SD_BUS_CREDS_CGROUP|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_USER_UNIT|SD_BUS_CREDS_SLICE|SD_BUS_CREDS_USER_SLICE|SD_BUS_CREDS_SESSION|SD_BUS_CREDS_OWNER_UID);
}
if (missing & SD_BUS_CREDS_AUDIT_SESSION_ID) {
r = audit_session_from_pid(pid, &c->audit_session_id);
- if (r < 0) {
- if (r != -ENOTSUP && r != -ENXIO && r != -ENOENT && r != -EPERM && r != -EACCES)
+ if (r == -ENXIO) {
+ /* ENXIO means: no audit session id assigned */
+ c->audit_session_id = AUDIT_SESSION_INVALID;
+ c->mask |= SD_BUS_CREDS_AUDIT_SESSION_ID;
+ } else if (r < 0) {
+ if (r != -EOPNOTSUPP && r != -ENOENT && r != -EPERM && r != -EACCES)
return r;
} else
c->mask |= SD_BUS_CREDS_AUDIT_SESSION_ID;
@@ -947,13 +1076,43 @@ int bus_creds_add_more(sd_bus_creds *c, uint64_t mask, pid_t pid, pid_t tid) {
if (missing & SD_BUS_CREDS_AUDIT_LOGIN_UID) {
r = audit_loginuid_from_pid(pid, &c->audit_login_uid);
- if (r < 0) {
- if (r != -ENOTSUP && r != -ENXIO && r != -ENOENT && r != -EPERM && r != -EACCES)
+ if (r == -ENXIO) {
+ /* ENXIO means: no audit login uid assigned */
+ c->audit_login_uid = UID_INVALID;
+ c->mask |= SD_BUS_CREDS_AUDIT_LOGIN_UID;
+ } else if (r < 0) {
+ if (r != -EOPNOTSUPP && r != -ENOENT && r != -EPERM && r != -EACCES)
return r;
} else
c->mask |= SD_BUS_CREDS_AUDIT_LOGIN_UID;
}
+ if (missing & SD_BUS_CREDS_TTY) {
+ r = get_ctty(pid, NULL, &c->tty);
+ if (r == -ENXIO) {
+ /* ENXIO means: process has no controlling TTY */
+ c->tty = NULL;
+ c->mask |= SD_BUS_CREDS_TTY;
+ } else if (r < 0) {
+ if (r != -EPERM && r != -EACCES && r != -ENOENT)
+ return r;
+ } else
+ c->mask |= SD_BUS_CREDS_TTY;
+ }
+
+ /* In case only the exe path was to be read we cannot
+ * distinguish the case where the exe path was unreadable
+ * because the process was a kernel thread, or when the
+ * process didn't exist at all. Hence, let's do a final check,
+ * to be sure. */
+ if (!pid_is_alive(pid))
+ return -ESRCH;
+
+ if (tid > 0 && tid != pid && !pid_is_unwaited(tid))
+ return -ESRCH;
+
+ c->augmented = missing & c->mask;
+
return 0;
}
@@ -978,6 +1137,21 @@ int bus_creds_extend_by_pid(sd_bus_creds *c, uint64_t mask, sd_bus_creds **ret)
/* Copy the original data over */
+ if (c->mask & mask & SD_BUS_CREDS_PID) {
+ n->pid = c->pid;
+ n->mask |= SD_BUS_CREDS_PID;
+ }
+
+ if (c->mask & mask & SD_BUS_CREDS_TID) {
+ n->tid = c->tid;
+ n->mask |= SD_BUS_CREDS_TID;
+ }
+
+ if (c->mask & mask & SD_BUS_CREDS_PPID) {
+ n->ppid = c->ppid;
+ n->mask |= SD_BUS_CREDS_PPID;
+ }
+
if (c->mask & mask & SD_BUS_CREDS_UID) {
n->uid = c->uid;
n->mask |= SD_BUS_CREDS_UID;
@@ -1019,24 +1193,22 @@ int bus_creds_extend_by_pid(sd_bus_creds *c, uint64_t mask, sd_bus_creds **ret)
}
if (c->mask & mask & SD_BUS_CREDS_SUPPLEMENTARY_GIDS) {
- n->supplementary_gids = newdup(gid_t, c->supplementary_gids, c->n_supplementary_gids);
- if (!n->supplementary_gids)
- return -ENOMEM;
- n->n_supplementary_gids = c->n_supplementary_gids;
- n->mask |= SD_BUS_CREDS_SUPPLEMENTARY_GIDS;
- }
-
- if (c->mask & mask & SD_BUS_CREDS_PID) {
- n->pid = c->pid;
- n->mask |= SD_BUS_CREDS_PID;
- }
+ if (c->supplementary_gids) {
+ n->supplementary_gids = newdup(gid_t, c->supplementary_gids, c->n_supplementary_gids);
+ if (!n->supplementary_gids)
+ return -ENOMEM;
+ n->n_supplementary_gids = c->n_supplementary_gids;
+ } else {
+ n->supplementary_gids = NULL;
+ n->n_supplementary_gids = 0;
+ }
- if (c->mask & mask & SD_BUS_CREDS_TID) {
- n->tid = c->tid;
- n->mask |= SD_BUS_CREDS_TID;
+ n->mask |= SD_BUS_CREDS_SUPPLEMENTARY_GIDS;
}
if (c->mask & mask & SD_BUS_CREDS_COMM) {
+ assert(c->comm);
+
n->comm = strdup(c->comm);
if (!n->comm)
return -ENOMEM;
@@ -1045,6 +1217,8 @@ int bus_creds_extend_by_pid(sd_bus_creds *c, uint64_t mask, sd_bus_creds **ret)
}
if (c->mask & mask & SD_BUS_CREDS_TID_COMM) {
+ assert(c->tid_comm);
+
n->tid_comm = strdup(c->tid_comm);
if (!n->tid_comm)
return -ENOMEM;
@@ -1053,23 +1227,34 @@ int bus_creds_extend_by_pid(sd_bus_creds *c, uint64_t mask, sd_bus_creds **ret)
}
if (c->mask & mask & SD_BUS_CREDS_EXE) {
- n->exe = strdup(c->exe);
- if (!n->exe)
- return -ENOMEM;
+ if (c->exe) {
+ n->exe = strdup(c->exe);
+ if (!n->exe)
+ return -ENOMEM;
+ } else
+ n->exe = NULL;
n->mask |= SD_BUS_CREDS_EXE;
}
if (c->mask & mask & SD_BUS_CREDS_CMDLINE) {
- n->cmdline = memdup(c->cmdline, c->cmdline_size);
- if (!n->cmdline)
- return -ENOMEM;
+ if (c->cmdline) {
+ n->cmdline = memdup(c->cmdline, c->cmdline_size);
+ if (!n->cmdline)
+ return -ENOMEM;
+
+ n->cmdline_size = c->cmdline_size;
+ } else {
+ n->cmdline = NULL;
+ n->cmdline_size = 0;
+ }
- n->cmdline_size = c->cmdline_size;
n->mask |= SD_BUS_CREDS_CMDLINE;
}
- if (c->mask & mask & (SD_BUS_CREDS_CGROUP|SD_BUS_CREDS_SESSION|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_USER_UNIT|SD_BUS_CREDS_SLICE|SD_BUS_CREDS_OWNER_UID)) {
+ if (c->mask & mask & (SD_BUS_CREDS_CGROUP|SD_BUS_CREDS_SESSION|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_USER_UNIT|SD_BUS_CREDS_SLICE|SD_BUS_CREDS_USER_SLICE|SD_BUS_CREDS_OWNER_UID)) {
+ assert(c->cgroup);
+
n->cgroup = strdup(c->cgroup);
if (!n->cgroup)
return -ENOMEM;
@@ -1078,10 +1263,12 @@ int bus_creds_extend_by_pid(sd_bus_creds *c, uint64_t mask, sd_bus_creds **ret)
if (!n->cgroup_root)
return -ENOMEM;
- n->mask |= mask & (SD_BUS_CREDS_CGROUP|SD_BUS_CREDS_SESSION|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_USER_UNIT|SD_BUS_CREDS_SLICE|SD_BUS_CREDS_OWNER_UID);
+ n->mask |= mask & (SD_BUS_CREDS_CGROUP|SD_BUS_CREDS_SESSION|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_USER_UNIT|SD_BUS_CREDS_SLICE|SD_BUS_CREDS_USER_SLICE|SD_BUS_CREDS_OWNER_UID);
}
if (c->mask & mask & (SD_BUS_CREDS_EFFECTIVE_CAPS|SD_BUS_CREDS_PERMITTED_CAPS|SD_BUS_CREDS_INHERITABLE_CAPS|SD_BUS_CREDS_BOUNDING_CAPS)) {
+ assert(c->capability);
+
n->capability = memdup(c->capability, DIV_ROUND_UP(cap_last_cap(), 32U) * 4 * 4);
if (!n->capability)
return -ENOMEM;
@@ -1090,6 +1277,8 @@ int bus_creds_extend_by_pid(sd_bus_creds *c, uint64_t mask, sd_bus_creds **ret)
}
if (c->mask & mask & SD_BUS_CREDS_SELINUX_CONTEXT) {
+ assert(c->label);
+
n->label = strdup(c->label);
if (!n->label)
return -ENOMEM;
@@ -1105,7 +1294,19 @@ int bus_creds_extend_by_pid(sd_bus_creds *c, uint64_t mask, sd_bus_creds **ret)
n->mask |= SD_BUS_CREDS_AUDIT_LOGIN_UID;
}
+ if (c->mask & mask & SD_BUS_CREDS_TTY) {
+ if (c->tty) {
+ n->tty = strdup(c->tty);
+ if (!n->tty)
+ return -ENOMEM;
+ } else
+ n->tty = NULL;
+ n->mask |= SD_BUS_CREDS_TTY;
+ }
+
if (c->mask & mask & SD_BUS_CREDS_UNIQUE_NAME) {
+ assert(c->unique_name);
+
n->unique_name = strdup(c->unique_name);
if (!n->unique_name)
return -ENOMEM;
@@ -1113,24 +1314,31 @@ int bus_creds_extend_by_pid(sd_bus_creds *c, uint64_t mask, sd_bus_creds **ret)
}
if (c->mask & mask & SD_BUS_CREDS_WELL_KNOWN_NAMES) {
- n->well_known_names = strv_copy(c->well_known_names);
- if (!n->well_known_names)
- return -ENOMEM;
+ if (strv_isempty(c->well_known_names))
+ n->well_known_names = NULL;
+ else {
+ n->well_known_names = strv_copy(c->well_known_names);
+ if (!n->well_known_names)
+ return -ENOMEM;
+ }
+ n->well_known_names_driver = c->well_known_names_driver;
+ n->well_known_names_local = c->well_known_names_local;
n->mask |= SD_BUS_CREDS_WELL_KNOWN_NAMES;
}
if (c->mask & mask & SD_BUS_CREDS_DESCRIPTION) {
+ assert(c->description);
n->description = strdup(c->description);
if (!n->description)
return -ENOMEM;
n->mask |= SD_BUS_CREDS_DESCRIPTION;
}
+ n->augmented = c->augmented & n->mask;
+
/* Get more data */
- r = bus_creds_add_more(n, mask,
- c->mask & SD_BUS_CREDS_PID ? c->pid : 0,
- c->mask & SD_BUS_CREDS_TID ? c->tid : 0);
+ r = bus_creds_add_more(n, mask, 0, 0);
if (r < 0)
return r;
diff --git a/src/libsystemd/sd-bus/bus-creds.h b/src/libsystemd/sd-bus/bus-creds.h
index 3b337efa32..209d216123 100644
--- a/src/libsystemd/sd-bus/bus-creds.h
+++ b/src/libsystemd/sd-bus/bus-creds.h
@@ -24,12 +24,13 @@
#include <stdbool.h>
#include "sd-bus.h"
-#include "time-util.h"
struct sd_bus_creds {
bool allocated;
unsigned n_ref;
+
uint64_t mask;
+ uint64_t augmented;
uid_t uid;
uid_t euid;
@@ -43,6 +44,7 @@ struct sd_bus_creds {
gid_t *supplementary_gids;
unsigned n_supplementary_gids;
+ pid_t ppid;
pid_t pid;
pid_t tid;
@@ -59,6 +61,9 @@ struct sd_bus_creds {
char *unit;
char *user_unit;
char *slice;
+ char *user_slice;
+
+ char *tty;
uint32_t *capability;
diff --git a/src/libsystemd/sd-bus/bus-dump.c b/src/libsystemd/sd-bus/bus-dump.c
index 7181639645..9db86adb7f 100644
--- a/src/libsystemd/sd-bus/bus-dump.c
+++ b/src/libsystemd/sd-bus/bus-dump.c
@@ -22,9 +22,10 @@
#include "util.h"
#include "capability.h"
#include "strv.h"
-#include "audit.h"
#include "macro.h"
#include "cap-list.h"
+#include "formats-util.h"
+#include "terminal-util.h"
#include "bus-message.h"
#include "bus-internal.h"
@@ -330,13 +331,11 @@ static void dump_capabilities(
}
int bus_creds_dump(sd_bus_creds *c, FILE *f, bool terse) {
- bool audit_sessionid_is_set = false, audit_loginuid_is_set = false;
- const char *u = NULL, *uu = NULL, *s = NULL, *sl = NULL;
uid_t owner, audit_loginuid;
uint32_t audit_sessionid;
char **cmdline = NULL, **well_known = NULL;
- const char *prefix, *color, *suffix;
- int r;
+ const char *prefix, *color, *suffix, *s;
+ int r, q, v, w, z;
assert(c);
@@ -361,8 +360,16 @@ int bus_creds_dump(sd_bus_creds *c, FILE *f, bool terse) {
fprintf(f, "%sPID=%s"PID_FMT"%s", prefix, color, c->pid, suffix);
if (c->mask & SD_BUS_CREDS_TID)
fprintf(f, "%sTID=%s"PID_FMT"%s", prefix, color, c->tid, suffix);
+ if (c->mask & SD_BUS_CREDS_PPID) {
+ if (c->ppid == 0)
+ fprintf(f, "%sPPID=%sn/a%s", prefix, color, suffix);
+ else
+ fprintf(f, "%sPPID=%s"PID_FMT"%s", prefix, color, c->ppid, suffix);
+ }
+ if (c->mask & SD_BUS_CREDS_TTY)
+ fprintf(f, "%sTTY=%s%s%s", prefix, color, strna(c->tty), suffix);
- if (terse && ((c->mask & (SD_BUS_CREDS_PID|SD_BUS_CREDS_TID))))
+ if (terse && ((c->mask & (SD_BUS_CREDS_PID|SD_BUS_CREDS_TID|SD_BUS_CREDS_PPID|SD_BUS_CREDS_TTY))))
fputs("\n", f);
if (c->mask & SD_BUS_CREDS_UID)
@@ -404,12 +411,13 @@ int bus_creds_dump(sd_bus_creds *c, FILE *f, bool terse) {
if (c->mask & SD_BUS_CREDS_TID_COMM)
fprintf(f, "%sTIDComm=%s%s%s", prefix, color, c->tid_comm, suffix);
if (c->mask & SD_BUS_CREDS_EXE)
- fprintf(f, "%sExe=%s%s%s", prefix, color, c->exe, suffix);
+ fprintf(f, "%sExe=%s%s%s", prefix, color, strna(c->exe), suffix);
if (terse && (c->mask & (SD_BUS_CREDS_EXE|SD_BUS_CREDS_COMM|SD_BUS_CREDS_TID_COMM)))
fputs("\n", f);
- if (sd_bus_creds_get_cmdline(c, &cmdline) >= 0) {
+ r = sd_bus_creds_get_cmdline(c, &cmdline);
+ if (r >= 0) {
char **i;
fprintf(f, "%sCommandLine=%s", prefix, color);
@@ -421,7 +429,8 @@ int bus_creds_dump(sd_bus_creds *c, FILE *f, bool terse) {
}
fprintf(f, "%s", suffix);
- }
+ } else if (r != -ENODATA)
+ fprintf(f, "%sCommandLine=%sn/a%s", prefix, color, suffix);
if (c->mask & SD_BUS_CREDS_SELINUX_CONTEXT)
fprintf(f, "%sLabel=%s%s%s", prefix, color, c->label, suffix);
@@ -433,32 +442,42 @@ int bus_creds_dump(sd_bus_creds *c, FILE *f, bool terse) {
if (c->mask & SD_BUS_CREDS_CGROUP)
fprintf(f, "%sCGroup=%s%s%s", prefix, color, c->cgroup, suffix);
- (void) sd_bus_creds_get_unit(c, &u);
- if (u)
- fprintf(f, "%sUnit=%s%s%s", prefix, color, u, suffix);
- (void) sd_bus_creds_get_user_unit(c, &uu);
- if (uu)
- fprintf(f, "%sUserUnit=%s%s%s", prefix, color, uu, suffix);
- (void) sd_bus_creds_get_slice(c, &sl);
- if (sl)
- fprintf(f, "%sSlice=%s%s%s", prefix, color, sl, suffix);
- (void) sd_bus_creds_get_session(c, &s);
- if (s)
- fprintf(f, "%sSession=%s%s%s", prefix, color, s, suffix);
-
- if (terse && ((c->mask & SD_BUS_CREDS_CGROUP) || u || uu || sl || s))
+ s = NULL;
+ r = sd_bus_creds_get_unit(c, &s);
+ if (r != -ENODATA)
+ fprintf(f, "%sUnit=%s%s%s", prefix, color, strna(s), suffix);
+ s = NULL;
+ v = sd_bus_creds_get_slice(c, &s);
+ if (v != -ENODATA)
+ fprintf(f, "%sSlice=%s%s%s", prefix, color, strna(s), suffix);
+ s = NULL;
+ q = sd_bus_creds_get_user_unit(c, &s);
+ if (q != -ENODATA)
+ fprintf(f, "%sUserUnit=%s%s%s", prefix, color, strna(s), suffix);
+ s = NULL;
+ w = sd_bus_creds_get_user_slice(c, &s);
+ if (w != -ENODATA)
+ fprintf(f, "%sUserSlice=%s%s%s", prefix, color, strna(s), suffix);
+ s = NULL;
+ z = sd_bus_creds_get_session(c, &s);
+ if (z != -ENODATA)
+ fprintf(f, "%sSession=%s%s%s", prefix, color, strna(s), suffix);
+
+ if (terse && ((c->mask & SD_BUS_CREDS_CGROUP) || r != -ENODATA || q != -ENODATA || v != -ENODATA || w != -ENODATA || z != -ENODATA))
fputs("\n", f);
- if (sd_bus_creds_get_audit_login_uid(c, &audit_loginuid) >= 0) {
- audit_loginuid_is_set = true;
+ r = sd_bus_creds_get_audit_login_uid(c, &audit_loginuid);
+ if (r >= 0)
fprintf(f, "%sAuditLoginUID=%s"UID_FMT"%s", prefix, color, audit_loginuid, suffix);
- }
- if (sd_bus_creds_get_audit_session_id(c, &audit_sessionid) >= 0) {
- audit_sessionid_is_set = true;
+ else if (r != -ENODATA)
+ fprintf(f, "%sAuditLoginUID=%sn/a%s", prefix, color, suffix);
+ q = sd_bus_creds_get_audit_session_id(c, &audit_sessionid);
+ if (q >= 0)
fprintf(f, "%sAuditSessionID=%s%"PRIu32"%s", prefix, color, audit_sessionid, suffix);
- }
+ else if (q != -ENODATA)
+ fprintf(f, "%sAuditSessionID=%sn/a%s", prefix, color, suffix);
- if (terse && (audit_loginuid_is_set || audit_sessionid_is_set))
+ if (terse && (r != -ENODATA || q != -ENODATA))
fputs("\n", f);
if (c->mask & SD_BUS_CREDS_UNIQUE_NAME)
diff --git a/src/libsystemd/sd-bus/bus-error.c b/src/libsystemd/sd-bus/bus-error.c
index 3bf0c5d3e4..dac157be16 100644
--- a/src/libsystemd/sd-bus/bus-error.c
+++ b/src/libsystemd/sd-bus/bus-error.c
@@ -40,7 +40,7 @@ BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map bus_standard_errors[] = {
SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.NoReply", ETIMEDOUT),
SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.IOError", EIO),
SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.BadAddress", EADDRNOTAVAIL),
- SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.NotSupported", ENOTSUP),
+ SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.NotSupported", EOPNOTSUPP),
SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.LimitsExceeded", ENOBUFS),
SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.AccessDenied", EACCES),
SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.AuthFailed", EACCES),
@@ -168,7 +168,7 @@ static sd_bus_error errno_to_bus_error_const(int error) {
case ECONNRESET:
return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_DISCONNECTED, "Disconnected");
- case ENOTSUP:
+ case EOPNOTSUPP:
return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_NOT_SUPPORTED, "Not supported");
case EADDRNOTAVAIL:
diff --git a/src/libsystemd/sd-bus/bus-gvariant.c b/src/libsystemd/sd-bus/bus-gvariant.c
index 02b95cd136..2d18a4e6c1 100644
--- a/src/libsystemd/sd-bus/bus-gvariant.c
+++ b/src/libsystemd/sd-bus/bus-gvariant.c
@@ -19,7 +19,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "util.h"
#include "bus-type.h"
#include "bus-gvariant.h"
#include "bus-signature.h"
diff --git a/src/libsystemd/sd-bus/bus-internal.h b/src/libsystemd/sd-bus/bus-internal.h
index e9f1a816aa..1351938c80 100644
--- a/src/libsystemd/sd-bus/bus-internal.h
+++ b/src/libsystemd/sd-bus/bus-internal.h
@@ -22,8 +22,6 @@
***/
#include <sys/socket.h>
-#include <sys/un.h>
-#include <netinet/in.h>
#include <pthread.h>
#include "hashmap.h"
@@ -211,6 +209,7 @@ struct sd_bus {
bool manual_peer_interface:1;
bool is_system:1;
bool is_user:1;
+ bool allow_interactive_authorization:1;
int use_memfd;
diff --git a/src/libsystemd/sd-bus/bus-introspect.c b/src/libsystemd/sd-bus/bus-introspect.c
index d528ab2a04..e2f4550c7e 100644
--- a/src/libsystemd/sd-bus/bus-introspect.c
+++ b/src/libsystemd/sd-bus/bus-introspect.c
@@ -20,7 +20,6 @@
***/
#include "util.h"
-#include "sd-bus-protocol.h"
#include "bus-introspect.h"
#include "bus-signature.h"
#include "bus-internal.h"
diff --git a/src/libsystemd/sd-bus/bus-introspect.h b/src/libsystemd/sd-bus/bus-introspect.h
index 98312d123b..1914e6cb8b 100644
--- a/src/libsystemd/sd-bus/bus-introspect.h
+++ b/src/libsystemd/sd-bus/bus-introspect.h
@@ -21,7 +21,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <sys/types.h>
#include <stdio.h>
#include "sd-bus.h"
diff --git a/src/libsystemd/sd-bus/bus-kernel.c b/src/libsystemd/sd-bus/bus-kernel.c
index e90ee449d9..417e4d5903 100644
--- a/src/libsystemd/sd-bus/bus-kernel.c
+++ b/src/libsystemd/sd-bus/bus-kernel.c
@@ -38,8 +38,8 @@
#include "strv.h"
#include "memfd-util.h"
#include "capability.h"
-#include "cgroup-util.h"
#include "fileio.h"
+#include "formats-util.h"
#include "bus-internal.h"
#include "bus-message.h"
@@ -595,6 +595,14 @@ static int bus_kernel_make_message(sd_bus *bus, struct kdbus_msg *k) {
m->creds.mask |= SD_BUS_CREDS_TID & bus->creds_mask;
}
+ if (d->pids.ppid > 0) {
+ m->creds.ppid = (pid_t) d->pids.ppid;
+ m->creds.mask |= SD_BUS_CREDS_PPID & bus->creds_mask;
+ } else if (d->pids.pid == 1) {
+ m->creds.ppid = 0;
+ m->creds.mask |= SD_BUS_CREDS_PPID & bus->creds_mask;
+ }
+
break;
case KDBUS_ITEM_CREDS:
@@ -681,15 +689,11 @@ static int bus_kernel_make_message(sd_bus *bus, struct kdbus_msg *k) {
break;
case KDBUS_ITEM_AUDIT:
- if ((uint32_t) d->audit.sessionid != (uint32_t) -1) {
- m->creds.audit_session_id = (uint32_t) d->audit.sessionid;
- m->creds.mask |= SD_BUS_CREDS_AUDIT_SESSION_ID & bus->creds_mask;
- }
+ m->creds.audit_session_id = (uint32_t) d->audit.sessionid;
+ m->creds.mask |= SD_BUS_CREDS_AUDIT_SESSION_ID & bus->creds_mask;
- if ((uid_t) d->audit.loginuid != UID_INVALID) {
- m->creds.audit_login_uid = (uid_t) d->audit.loginuid;
- m->creds.mask |= SD_BUS_CREDS_AUDIT_LOGIN_UID & bus->creds_mask;
- }
+ m->creds.audit_login_uid = (uid_t) d->audit.loginuid;
+ m->creds.mask |= SD_BUS_CREDS_AUDIT_LOGIN_UID & bus->creds_mask;
break;
case KDBUS_ITEM_CAPS:
@@ -749,10 +753,21 @@ static int bus_kernel_make_message(sd_bus *bus, struct kdbus_msg *k) {
case KDBUS_ITEM_AUXGROUPS:
if (bus->creds_mask & SD_BUS_CREDS_SUPPLEMENTARY_GIDS) {
- assert_cc(sizeof(gid_t) == sizeof(uint32_t));
+ size_t i, n;
+ gid_t *g;
+
+ n = (d->size - offsetof(struct kdbus_item, data64)) / sizeof(uint64_t);
+ g = new(gid_t, n);
+ if (!g) {
+ r = -ENOMEM;
+ goto fail;
+ }
- m->creds.n_supplementary_gids = (d->size - offsetof(struct kdbus_item, data32)) / sizeof(uint32_t);
- m->creds.supplementary_gids = (gid_t*) d->data32;
+ for (i = 0; i < n; i++)
+ g[i] = d->data64[i];
+
+ m->creds.supplementary_gids = g;
+ m->creds.n_supplementary_gids = n;
m->creds.mask |= SD_BUS_CREDS_SUPPLEMENTARY_GIDS;
}
@@ -841,9 +856,8 @@ fail:
int bus_kernel_take_fd(sd_bus *b) {
struct kdbus_bloom_parameter *bloom = NULL;
+ struct kdbus_item *items, *item;
struct kdbus_cmd_hello *hello;
- struct kdbus_item_list *items;
- struct kdbus_item *item;
_cleanup_free_ char *g = NULL;
const char *name;
size_t l = 0, m = 0, sz;
@@ -948,8 +962,16 @@ int bus_kernel_take_fd(sd_bus *b) {
}
r = ioctl(b->input_fd, KDBUS_CMD_HELLO, hello);
- if (r < 0)
+ if (r < 0) {
+ if (errno == ENOTTY)
+ /* If the ioctl is not supported we assume that the
+ * API version changed in a major incompatible way,
+ * let's indicate an API incompatibility in this
+ * case. */
+ return -ESOCKTNOSUPPORT;
+
return -errno;
+ }
if (!b->kdbus_buffer) {
b->kdbus_buffer = mmap(NULL, KDBUS_POOL_SIZE, PROT_READ, MAP_SHARED, b->input_fd, 0);
@@ -963,13 +985,13 @@ int bus_kernel_take_fd(sd_bus *b) {
/* The higher 32bit of the bus_flags fields are considered
* 'incompatible flags'. Refuse them all for now. */
if (hello->bus_flags > 0xFFFFFFFFULL) {
- r = -ENOTSUP;
+ r = -ESOCKTNOSUPPORT;
goto fail;
}
/* extract bloom parameters from items */
items = (void*)((uint8_t*)b->kdbus_buffer + hello->offset);
- KDBUS_ITEM_FOREACH(item, items, items) {
+ KDBUS_FOREACH(item, items, hello->items_size) {
switch (item->type) {
case KDBUS_ITEM_BLOOM_PARAMETER:
bloom = &item->bloom_parameter;
@@ -978,7 +1000,7 @@ int bus_kernel_take_fd(sd_bus *b) {
}
if (!bloom || !bloom_validate_parameters((size_t) bloom->size, (unsigned) bloom->n_hash)) {
- r = -ENOTSUP;
+ r = -EOPNOTSUPP;
goto fail;
}
@@ -1345,15 +1367,12 @@ int bus_kernel_read_message(sd_bus *bus, bool hint_priority, int64_t priority) {
}
r = ioctl(bus->input_fd, KDBUS_CMD_RECV, &recv);
+ if (recv.return_flags & KDBUS_RECV_RETURN_DROPPED_MSGS)
+ log_debug("%s: kdbus reports %" PRIu64 " dropped broadcast messages, ignoring.", strna(bus->description), (uint64_t) recv.dropped_msgs);
if (r < 0) {
if (errno == EAGAIN)
return 0;
- if (errno == EOVERFLOW) {
- log_debug("%s: kdbus reports %" PRIu64 " dropped broadcast messages, ignoring.", strna(bus->description), (uint64_t) recv.dropped_msgs);
- return 0;
- }
-
return -errno;
}
@@ -1389,7 +1408,7 @@ int bus_kernel_pop_memfd(sd_bus *bus, void **address, size_t *mapped, size_t *al
assert(allocated);
if (!bus || !bus->is_kernel)
- return -ENOTSUP;
+ return -EOPNOTSUPP;
assert_se(pthread_mutex_lock(&bus->memfd_cache_mutex) >= 0);
@@ -1499,7 +1518,7 @@ uint64_t attach_flags_to_kdbus(uint64_t mask) {
SD_BUS_CREDS_GID|SD_BUS_CREDS_EGID|SD_BUS_CREDS_SGID|SD_BUS_CREDS_FSGID))
m |= KDBUS_ATTACH_CREDS;
- if (mask & (SD_BUS_CREDS_PID|SD_BUS_CREDS_TID))
+ if (mask & (SD_BUS_CREDS_PID|SD_BUS_CREDS_TID|SD_BUS_CREDS_PPID))
m |= KDBUS_ATTACH_PIDS;
if (mask & SD_BUS_CREDS_COMM)
@@ -1574,7 +1593,7 @@ int bus_kernel_create_bus(const char *name, bool world, char **s) {
make->size += ALIGN8(n->size);
- /* The busses we create make no restrictions on what metadata
+ /* The buses we create make no restrictions on what metadata
* peers can read from incoming messages. */
n = KDBUS_ITEM_NEXT(n);
n->type = KDBUS_ITEM_ATTACH_FLAGS_RECV;
@@ -1600,6 +1619,11 @@ int bus_kernel_create_bus(const char *name, bool world, char **s) {
if (ioctl(fd, KDBUS_CMD_BUS_MAKE, make) < 0) {
safe_close(fd);
+
+ /* Major API change? then the ioctls got shuffled around. */
+ if (errno == ENOTTY)
+ return -ESOCKTNOSUPPORT;
+
return -errno;
}
@@ -1746,32 +1770,6 @@ int bus_kernel_realize_attach_flags(sd_bus *bus) {
return 0;
}
-int bus_kernel_fix_attach_mask(void) {
- _cleanup_free_ char *mask = NULL;
- uint64_t m = (uint64_t) -1;
- char buf[2+16+2];
- int r;
-
- /* By default we don't want any kdbus metadata fields to be
- * suppressed, hence we reset the kernel mask for it to
- * (uint64_t) -1. If the module argument was overwritten by
- * the kernel cmdline, we leave it as is. */
-
- r = get_proc_cmdline_key("kdbus.attach_flags_mask=", &mask);
- if (r < 0)
- return log_warning_errno(r, "Failed to read kernel command line: %m");
-
- if (r == 0) {
- sprintf(buf, "0x%" PRIx64 "\n", m);
- r = write_string_file("/sys/module/kdbus/parameters/attach_flags_mask", buf);
- if (r < 0)
- return log_full_errno(IN_SET(r, -ENOENT, -EROFS) ? LOG_DEBUG : LOG_WARNING, r,
- "Failed to write kdbus attach mask: %m");
- }
-
- return 0;
-}
-
int bus_kernel_get_bus_name(sd_bus *bus, char **name) {
struct kdbus_cmd_info cmd = {
.size = sizeof(struct kdbus_cmd_info),
diff --git a/src/libsystemd/sd-bus/bus-kernel.h b/src/libsystemd/sd-bus/bus-kernel.h
index b9f31ba790..bb4dff6d82 100644
--- a/src/libsystemd/sd-bus/bus-kernel.h
+++ b/src/libsystemd/sd-bus/bus-kernel.h
@@ -90,8 +90,6 @@ int bus_kernel_drop_one(int fd);
int bus_kernel_realize_attach_flags(sd_bus *bus);
-int bus_kernel_fix_attach_mask(void);
-
int bus_kernel_get_bus_name(sd_bus *bus, char **name);
int bus_kernel_cmd_free(sd_bus *bus, uint64_t offset);
diff --git a/src/libsystemd/sd-bus/bus-match.c b/src/libsystemd/sd-bus/bus-match.c
index 162f0ab608..7c5264fad4 100644
--- a/src/libsystemd/sd-bus/bus-match.c
+++ b/src/libsystemd/sd-bus/bus-match.c
@@ -22,7 +22,6 @@
#include "bus-internal.h"
#include "bus-message.h"
#include "bus-match.h"
-#include "bus-error.h"
#include "bus-util.h"
#include "strv.h"
@@ -329,7 +328,7 @@ int bus_match_run(
bus->current_handler = node->leaf.callback->callback;
bus->current_userdata = slot->userdata;
}
- r = node->leaf.callback->callback(bus, m, slot->userdata, &error_buffer);
+ r = node->leaf.callback->callback(m, slot->userdata, &error_buffer);
if (bus) {
bus->current_userdata = NULL;
bus->current_handler = NULL;
diff --git a/src/libsystemd/sd-bus/bus-message.c b/src/libsystemd/sd-bus/bus-message.c
index 2959303033..6ee209dd1b 100644
--- a/src/libsystemd/sd-bus/bus-message.c
+++ b/src/libsystemd/sd-bus/bus-message.c
@@ -27,7 +27,6 @@
#include "utf8.h"
#include "strv.h"
#include "time-util.h"
-#include "cgroup-util.h"
#include "memfd-util.h"
#include "sd-bus.h"
@@ -421,7 +420,7 @@ static int message_append_reply_cookie(sd_bus_message *m, uint64_t cookie) {
else {
/* 64bit cookies are not supported on dbus1 */
if (cookie > 0xffffffffUL)
- return -ENOTSUP;
+ return -EOPNOTSUPP;
return message_append_field_uint32(m, BUS_MESSAGE_HEADER_REPLY_SERIAL, (uint32_t) cookie);
}
@@ -441,7 +440,7 @@ int bus_message_from_header(
size_t extra,
sd_bus_message **ret) {
- sd_bus_message *m;
+ _cleanup_free_ sd_bus_message *m = NULL;
struct bus_header *h;
size_t a, label_sz;
@@ -460,15 +459,13 @@ int bus_message_from_header(
return -EBADMSG;
h = header;
- if (h->version != 1 &&
- h->version != 2)
+ if (!IN_SET(h->version, 1, 2))
return -EBADMSG;
if (h->type == _SD_BUS_MESSAGE_TYPE_INVALID)
return -EBADMSG;
- if (h->endian != BUS_LITTLE_ENDIAN &&
- h->endian != BUS_BIG_ENDIAN)
+ if (!IN_SET(h->endian, BUS_LITTLE_ENDIAN, BUS_BIG_ENDIAN))
return -EBADMSG;
/* Note that we are happy with unknown flags in the flags header! */
@@ -557,6 +554,7 @@ int bus_message_from_header(
m->bus = sd_bus_ref(bus);
*ret = m;
+ m = NULL;
return 0;
}
@@ -634,6 +632,9 @@ static sd_bus_message *message_new(sd_bus *bus, uint8_t type) {
m->root_container.need_offsets = BUS_MESSAGE_IS_GVARIANT(m);
m->bus = sd_bus_ref(bus);
+ if (bus->allow_interactive_authorization)
+ m->header->flags |= BUS_MESSAGE_ALLOW_INTERACTIVE_AUTHORIZATION;
+
return m;
}
@@ -749,7 +750,7 @@ static int message_new_reply(
t->header->flags |= BUS_MESSAGE_NO_REPLY_EXPECTED;
t->reply_cookie = BUS_MESSAGE_COOKIE(call);
if (t->reply_cookie == 0)
- return -ENOTSUP;
+ return -EOPNOTSUPP;
r = message_append_reply_cookie(t, t->reply_cookie);
if (r < 0)
@@ -1462,7 +1463,7 @@ static int message_push_fd(sd_bus_message *m, int fd) {
return -EINVAL;
if (!m->allow_fds)
- return -ENOTSUP;
+ return -EOPNOTSUPP;
copy = fcntl(fd, F_DUPFD_CLOEXEC, 3);
if (copy < 0)
@@ -2941,7 +2942,7 @@ int bus_message_seal(sd_bus_message *m, uint64_t cookie, usec_t timeout) {
if (cookie > 0xffffffffULL &&
!BUS_MESSAGE_IS_GVARIANT(m))
- return -ENOTSUP;
+ return -EOPNOTSUPP;
/* In vtables the return signature of method calls is listed,
* let's check if they match if this is a response */
@@ -3488,8 +3489,6 @@ _public_ int sd_bus_message_read_basic(sd_bus_message *m, char type, void *p) {
return r;
} else {
- rindex = m->rindex;
-
if (IN_SET(type, SD_BUS_TYPE_STRING, SD_BUS_TYPE_OBJECT_PATH)) {
uint32_t l;
bool ok;
@@ -4797,7 +4796,7 @@ _public_ int sd_bus_message_read_array(
assert_return(bus_type_is_trivial(type), -EINVAL);
assert_return(ptr, -EINVAL);
assert_return(size, -EINVAL);
- assert_return(!BUS_MESSAGE_NEED_BSWAP(m), -ENOTSUP);
+ assert_return(!BUS_MESSAGE_NEED_BSWAP(m), -EOPNOTSUPP);
r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, CHAR_TO_STR(type));
if (r <= 0)
@@ -5508,7 +5507,7 @@ int bus_message_parse_fields(sd_bus_message *m) {
/* Try to read the error message, but if we can't it's a non-issue */
if (m->header->type == SD_BUS_MESSAGE_METHOD_ERROR)
- sd_bus_message_read(m, "s", &m->error.message);
+ (void) sd_bus_message_read(m, "s", &m->error.message);
return 0;
}
@@ -5551,6 +5550,7 @@ 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) {
+ const char *s;
int r;
assert(m);
@@ -5560,19 +5560,13 @@ int bus_message_read_strv_extend(sd_bus_message *m, char ***l) {
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;
-
+ while ((r = sd_bus_message_read_basic(m, 's', &s)) > 0) {
r = strv_extend(l, s);
if (r < 0)
return r;
}
+ if (r < 0)
+ return r;
r = sd_bus_message_exit_container(m);
if (r < 0)
diff --git a/src/libsystemd/sd-bus/bus-message.h b/src/libsystemd/sd-bus/bus-message.h
index 32955329b4..d784e603dd 100644
--- a/src/libsystemd/sd-bus/bus-message.h
+++ b/src/libsystemd/sd-bus/bus-message.h
@@ -27,7 +27,6 @@
#include "macro.h"
#include "sd-bus.h"
-#include "kdbus.h"
#include "time-util.h"
#include "bus-creds.h"
#include "bus-protocol.h"
diff --git a/src/libsystemd/sd-bus/bus-objects.c b/src/libsystemd/sd-bus/bus-objects.c
index fc6c223283..e4bbd880e5 100644
--- a/src/libsystemd/sd-bus/bus-objects.c
+++ b/src/libsystemd/sd-bus/bus-objects.c
@@ -277,7 +277,7 @@ static int node_callbacks_run(
bus->current_slot = sd_bus_slot_ref(slot);
bus->current_handler = c->callback;
bus->current_userdata = slot->userdata;
- r = c->callback(bus, m, slot->userdata, &error_buffer);
+ r = c->callback(m, slot->userdata, &error_buffer);
bus->current_userdata = NULL;
bus->current_handler = NULL;
bus->current_slot = sd_bus_slot_unref(slot);
@@ -395,7 +395,7 @@ static int method_callbacks_run(
bus->current_slot = sd_bus_slot_ref(slot);
bus->current_handler = c->vtable->x.method.handler;
bus->current_userdata = u;
- r = c->vtable->x.method.handler(bus, m, u, &error);
+ r = c->vtable->x.method.handler(m, u, &error);
bus->current_userdata = NULL;
bus->current_handler = NULL;
bus->current_slot = sd_bus_slot_unref(slot);
diff --git a/src/libsystemd/sd-bus/bus-signature.h b/src/libsystemd/sd-bus/bus-signature.h
index 2e06e30548..c4fed0b53d 100644
--- a/src/libsystemd/sd-bus/bus-signature.h
+++ b/src/libsystemd/sd-bus/bus-signature.h
@@ -22,7 +22,6 @@
***/
#include <stdbool.h>
-#include <sys/types.h>
bool signature_is_single(const char *s, bool allow_dict_entry);
bool signature_is_pair(const char *s);
diff --git a/src/libsystemd/sd-bus/bus-socket.c b/src/libsystemd/sd-bus/bus-socket.c
index 52883fa8cd..4fffc6581d 100644
--- a/src/libsystemd/sd-bus/bus-socket.c
+++ b/src/libsystemd/sd-bus/bus-socket.c
@@ -20,18 +20,17 @@
***/
#include <endian.h>
-#include <assert.h>
#include <stdlib.h>
#include <unistd.h>
#include <poll.h>
-#include <byteswap.h>
+#include "sd-daemon.h"
#include "util.h"
#include "macro.h"
#include "missing.h"
-#include "strv.h"
#include "utf8.h"
-#include "sd-daemon.h"
+#include "formats-util.h"
+#include "signal-util.h"
#include "sd-bus.h"
#include "bus-socket.h"
@@ -179,7 +178,7 @@ static int bus_socket_auth_verify_client(sd_bus *b) {
/* We expect two response lines: "OK" and possibly
* "AGREE_UNIX_FD" */
- e = memmem(b->rbuffer, b->rbuffer_size, "\r\n", 2);
+ e = memmem_safe(b->rbuffer, b->rbuffer_size, "\r\n", 2);
if (!e)
return 0;
@@ -494,7 +493,7 @@ static int bus_socket_auth_verify(sd_bus *b) {
static int bus_socket_read_auth(sd_bus *b) {
struct msghdr mh;
- struct iovec iov;
+ struct iovec iov = {};
size_t n;
ssize_t k;
int r;
@@ -529,7 +528,6 @@ static int bus_socket_read_auth(sd_bus *b) {
b->rbuffer = p;
- zero(iov);
iov.iov_base = (uint8_t*) b->rbuffer + b->rbuffer_size;
iov.iov_len = n - b->rbuffer_size;
@@ -609,10 +607,10 @@ void bus_socket_setup(sd_bus *b) {
/* Enable SO_PASSCRED + SO_PASSEC. We try this on any
* socket, just in case. */
enable = !b->bus_client;
- (void)setsockopt(b->input_fd, SOL_SOCKET, SO_PASSCRED, &enable, sizeof(enable));
+ (void) setsockopt(b->input_fd, SOL_SOCKET, SO_PASSCRED, &enable, sizeof(enable));
enable = !b->bus_client && (b->attach_flags & KDBUS_ATTACH_SECLABEL);
- (void)setsockopt(b->input_fd, SOL_SOCKET, SO_PASSSEC, &enable, sizeof(enable));
+ (void) setsockopt(b->input_fd, SOL_SOCKET, SO_PASSSEC, &enable, sizeof(enable));
/* Increase the buffers to 8 MB */
fd_inc_rcvbuf(b->input_fd, SNDBUF_SIZE);
@@ -810,23 +808,21 @@ int bus_socket_write_message(sd_bus *bus, sd_bus_message *m, size_t *idx) {
if (bus->prefer_writev)
k = writev(bus->output_fd, iov, m->n_iovec);
else {
- struct msghdr mh;
- zero(mh);
+ struct msghdr mh = {
+ .msg_iov = iov,
+ .msg_iovlen = m->n_iovec,
+ };
if (m->n_fds > 0) {
struct cmsghdr *control;
- control = alloca(CMSG_SPACE(sizeof(int) * m->n_fds));
- mh.msg_control = control;
+ mh.msg_control = control = alloca(CMSG_SPACE(sizeof(int) * m->n_fds));
+ mh.msg_controllen = control->cmsg_len = CMSG_LEN(sizeof(int) * m->n_fds);
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;
@@ -917,8 +913,8 @@ static int bus_socket_make_message(sd_bus *bus, size_t size) {
r = bus_message_from_malloc(bus,
bus->rbuffer, size,
bus->fds, bus->n_fds,
- !bus->bus_client && bus->ucred_valid ? &bus->ucred : NULL,
- !bus->bus_client && bus->label[0] ? bus->label : NULL,
+ NULL,
+ NULL,
&t);
if (r < 0) {
free(b);
@@ -938,7 +934,7 @@ static int bus_socket_make_message(sd_bus *bus, size_t size) {
int bus_socket_read_message(sd_bus *bus) {
struct msghdr mh;
- struct iovec iov;
+ struct iovec iov = {};
ssize_t k;
size_t need;
int r;
@@ -968,7 +964,6 @@ int bus_socket_read_message(sd_bus *bus) {
bus->rbuffer = b;
- zero(iov);
iov.iov_base = (uint8_t*) bus->rbuffer + bus->rbuffer_size;
iov.iov_len = need - bus->rbuffer_size;
diff --git a/src/libsystemd/sd-bus/bus-track.c b/src/libsystemd/sd-bus/bus-track.c
index 6be8310bbe..ec9340f8e1 100644
--- a/src/libsystemd/sd-bus/bus-track.c
+++ b/src/libsystemd/sd-bus/bus-track.c
@@ -20,7 +20,6 @@
***/
#include "sd-bus.h"
-#include "set.h"
#include "bus-util.h"
#include "bus-internal.h"
#include "bus-track.h"
@@ -91,6 +90,9 @@ _public_ int sd_bus_track_new(
assert_return(bus, -EINVAL);
assert_return(track, -EINVAL);
+ if (!bus->bus_client)
+ return -EINVAL;
+
t = new0(sd_bus_track, 1);
if (!t)
return -ENOMEM;
@@ -140,12 +142,11 @@ _public_ sd_bus_track* sd_bus_track_unref(sd_bus_track *track) {
return NULL;
}
-static int on_name_owner_changed(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int on_name_owner_changed(sd_bus_message *message, void *userdata, sd_bus_error *error) {
sd_bus_track *track = userdata;
const char *name, *old, *new;
int r;
- assert(bus);
assert(message);
assert(track);
diff --git a/src/libsystemd/sd-bus/bus-type.c b/src/libsystemd/sd-bus/bus-type.c
index b7914d15d3..6bc7b880a6 100644
--- a/src/libsystemd/sd-bus/bus-type.c
+++ b/src/libsystemd/sd-bus/bus-type.c
@@ -19,7 +19,6 @@
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) {
diff --git a/src/libsystemd/sd-bus/bus-type.h b/src/libsystemd/sd-bus/bus-type.h
index 0e507839ca..581574ab73 100644
--- a/src/libsystemd/sd-bus/bus-type.h
+++ b/src/libsystemd/sd-bus/bus-type.h
@@ -25,7 +25,6 @@
#include "macro.h"
#include "sd-bus.h"
-#include "sd-bus-protocol.h"
bool bus_type_is_valid(char c) _const_;
bool bus_type_is_valid_in_signature(char c) _const_;
diff --git a/src/libsystemd/sd-bus/bus-util.c b/src/libsystemd/sd-bus/bus-util.c
index 52d4ebe611..99937799b3 100644
--- a/src/libsystemd/sd-bus/bus-util.c
+++ b/src/libsystemd/sd-bus/bus-util.c
@@ -30,21 +30,23 @@
#include "path-util.h"
#include "missing.h"
#include "set.h"
+#include "signal-util.h"
+#include "unit-name.h"
#include "sd-bus.h"
#include "bus-error.h"
+#include "bus-label.h"
#include "bus-message.h"
#include "bus-util.h"
#include "bus-internal.h"
-static int name_owner_change_callback(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
+static int name_owner_change_callback(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
sd_event *e = userdata;
- assert(bus);
assert(m);
assert(e);
- sd_bus_close(bus);
+ sd_bus_close(sd_bus_message_get_bus(m));
sd_event_exit(e, 0);
return 1;
@@ -132,7 +134,7 @@ int bus_event_loop_with_idle(
/* Fallback for dbus1 connections: we
* unregister the name and wait for the
* response to come through for it */
- if (r == -ENOTSUP) {
+ if (r == -EOPNOTSUPP) {
/* Inform the service manager that we
* are going down, so that it will
@@ -190,11 +192,35 @@ int bus_name_has_owner(sd_bus *c, const char *name, sd_bus_error *error) {
return has_owner;
}
-int bus_verify_polkit(
+static int check_good_user(sd_bus_message *m, uid_t good_user) {
+ _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
+ uid_t sender_uid;
+ int r;
+
+ assert(m);
+
+ if (good_user == UID_INVALID)
+ return 0;
+
+ r = sd_bus_query_sender_creds(m, SD_BUS_CREDS_EUID, &creds);
+ if (r < 0)
+ return r;
+
+ /* Don't trust augmented credentials for authorization */
+ assert_return((sd_bus_creds_get_augmented_mask(creds) & SD_BUS_CREDS_EUID) == 0, -EPERM);
+
+ r = sd_bus_creds_get_euid(creds, &sender_uid);
+ if (r < 0)
+ return r;
+
+ return sender_uid == good_user;
+}
+
+int bus_test_polkit(
sd_bus_message *call,
int capability,
const char *action,
- bool interactive,
+ uid_t good_user,
bool *_challenge,
sd_bus_error *e) {
@@ -203,6 +229,12 @@ int bus_verify_polkit(
assert(call);
assert(action);
+ /* Tests non-interactively! */
+
+ r = check_good_user(call, good_user);
+ if (r != 0)
+ return r;
+
r = sd_bus_query_sender_privilege(call, capability);
if (r < 0)
return r;
@@ -211,19 +243,13 @@ int bus_verify_polkit(
#ifdef ENABLE_POLKIT
else {
_cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
- int authorized = false, challenge = false, c;
+ int authorized = false, challenge = false;
const char *sender;
sender = sd_bus_message_get_sender(call);
if (!sender)
return -EBADMSG;
- c = sd_bus_message_get_allow_interactive_authorization(call);
- if (c < 0)
- return c;
- if (c > 0)
- interactive = true;
-
r = sd_bus_call_method(
call->bus,
"org.freedesktop.PolicyKit1",
@@ -236,7 +262,7 @@ int bus_verify_polkit(
"system-bus-name", 1, "name", "s", sender,
action,
0,
- !!interactive,
+ 0,
"");
if (r < 0) {
@@ -296,12 +322,11 @@ static void async_polkit_query_free(AsyncPolkitQuery *q) {
free(q);
}
-static int async_polkit_callback(sd_bus *bus, sd_bus_message *reply, void *userdata, sd_bus_error *error) {
+static int async_polkit_callback(sd_bus_message *reply, void *userdata, sd_bus_error *error) {
_cleanup_bus_error_free_ sd_bus_error error_buffer = SD_BUS_ERROR_NULL;
AsyncPolkitQuery *q = userdata;
int r;
- assert(bus);
assert(reply);
assert(q);
@@ -314,7 +339,7 @@ static int async_polkit_callback(sd_bus *bus, sd_bus_message *reply, void *userd
goto finish;
}
- r = q->callback(bus, q->request, q->userdata, &error_buffer);
+ r = q->callback(q->request, q->userdata, &error_buffer);
r = bus_maybe_reply_error(q->request, r, &error_buffer);
finish:
@@ -330,6 +355,7 @@ int bus_verify_polkit_async(
int capability,
const char *action,
bool interactive,
+ uid_t good_user,
Hashmap **registry,
sd_bus_error *error) {
@@ -347,6 +373,10 @@ int bus_verify_polkit_async(
assert(action);
assert(registry);
+ r = check_good_user(call, good_user);
+ if (r != 0)
+ return r;
+
#ifdef ENABLE_POLKIT
q = hashmap_get(*registry, call);
if (q) {
@@ -691,6 +721,18 @@ int bus_print_property(const char *name, sd_bus_message *property, bool all) {
return 1;
}
+ case SD_BUS_TYPE_INT64: {
+ int64_t i;
+
+ r = sd_bus_message_read_basic(property, type, &i);
+ if (r < 0)
+ return r;
+
+ printf("%s=%lld\n", name, (long long) i);
+
+ return 1;
+ }
+
case SD_BUS_TYPE_UINT32: {
uint32_t u;
@@ -920,7 +962,6 @@ static int map_basic(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_
switch (type) {
case SD_BUS_TYPE_STRING: {
const char *s;
- char *str;
char **p = userdata;
r = sd_bus_message_read_basic(m, type, &s);
@@ -930,14 +971,7 @@ static int map_basic(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_
if (isempty(s))
break;
- str = strdup(s);
- if (!str) {
- r = -ENOMEM;
- break;
- }
- free(*p);
- *p = str;
-
+ r = free_and_strdup(p, s);
break;
}
@@ -1002,14 +1036,14 @@ static int map_basic(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_
return r;
}
-int bus_message_map_all_properties(sd_bus *bus,
- sd_bus_message *m,
- const struct bus_properties_map *map,
- void *userdata) {
+int bus_message_map_all_properties(
+ sd_bus_message *m,
+ const struct bus_properties_map *map,
+ void *userdata) {
+
_cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
int r;
- assert(bus);
assert(m);
assert(map);
@@ -1045,9 +1079,9 @@ int bus_message_map_all_properties(sd_bus *bus,
v = (uint8_t *)userdata + prop->offset;
if (map[i].set)
- r = prop->set(bus, member, m, &error, v);
+ r = prop->set(sd_bus_message_get_bus(m), member, m, &error, v);
else
- r = map_basic(bus, member, m, &error, v);
+ r = map_basic(sd_bus_message_get_bus(m), member, m, &error, v);
if (r < 0)
return r;
@@ -1064,22 +1098,24 @@ int bus_message_map_all_properties(sd_bus *bus,
if (r < 0)
return r;
}
+ if (r < 0)
+ return r;
return sd_bus_message_exit_container(m);
}
-int bus_message_map_properties_changed(sd_bus *bus,
- sd_bus_message *m,
- const struct bus_properties_map *map,
- void *userdata) {
+int bus_message_map_properties_changed(
+ sd_bus_message *m,
+ const struct bus_properties_map *map,
+ void *userdata) {
+
const char *member;
int r, invalidated, i;
- assert(bus);
assert(m);
assert(map);
- r = bus_message_map_all_properties(bus, m, map, userdata);
+ r = bus_message_map_all_properties(m, map, userdata);
if (r < 0)
return r;
@@ -1094,6 +1130,8 @@ int bus_message_map_properties_changed(sd_bus *bus,
++invalidated;
break;
}
+ if (r < 0)
+ return r;
r = sd_bus_message_exit_container(m);
if (r < 0)
@@ -1102,11 +1140,13 @@ int bus_message_map_properties_changed(sd_bus *bus,
return invalidated;
}
-int bus_map_all_properties(sd_bus *bus,
- const char *destination,
- const char *path,
- const struct bus_properties_map *map,
- void *userdata) {
+int bus_map_all_properties(
+ sd_bus *bus,
+ const char *destination,
+ const char *path,
+ const struct bus_properties_map *map,
+ void *userdata) {
+
_cleanup_bus_message_unref_ sd_bus_message *m = NULL;
_cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
int r;
@@ -1128,7 +1168,7 @@ int bus_map_all_properties(sd_bus *bus,
if (r < 0)
return r;
- return bus_message_map_all_properties(bus, m, map, userdata);
+ return bus_message_map_all_properties(m, map, userdata);
}
int bus_open_transport(BusTransport transport, const char *host, bool user, sd_bus **bus) {
@@ -1139,7 +1179,7 @@ int bus_open_transport(BusTransport transport, const char *host, bool user, sd_b
assert(bus);
assert_return((transport == BUS_TRANSPORT_LOCAL) == !host, -EINVAL);
- assert_return(transport == BUS_TRANSPORT_LOCAL || !user, -ENOTSUP);
+ assert_return(transport == BUS_TRANSPORT_LOCAL || !user, -EOPNOTSUPP);
switch (transport) {
@@ -1174,7 +1214,7 @@ int bus_open_transport_systemd(BusTransport transport, const char *host, bool us
assert(bus);
assert_return((transport == BUS_TRANSPORT_LOCAL) == !host, -EINVAL);
- assert_return(transport == BUS_TRANSPORT_LOCAL || !user, -ENOTSUP);
+ assert_return(transport == BUS_TRANSPORT_LOCAL || !user, -EOPNOTSUPP);
switch (transport) {
@@ -1565,24 +1605,22 @@ typedef struct BusWaitForJobs {
sd_bus_slot *slot_disconnected;
} BusWaitForJobs;
-static int match_disconnected(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
- assert(bus);
+static int match_disconnected(sd_bus_message *m, void *userdata, sd_bus_error *error) {
assert(m);
log_error("Warning! D-Bus connection terminated.");
- sd_bus_close(bus);
+ sd_bus_close(sd_bus_message_get_bus(m));
return 0;
}
-static int match_job_removed(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
+static int match_job_removed(sd_bus_message *m, void *userdata, sd_bus_error *error) {
const char *path, *unit, *result;
BusWaitForJobs *d = userdata;
uint32_t id;
char *found;
int r;
- assert(bus);
assert(m);
assert(d);
@@ -1690,6 +1728,73 @@ static int bus_process_wait(sd_bus *bus) {
}
}
+static int bus_job_get_service_result(BusWaitForJobs *d, char **result) {
+ _cleanup_free_ char *dbus_path = NULL;
+
+ assert(d);
+ assert(d->name);
+ assert(result);
+
+ dbus_path = unit_dbus_path_from_name(d->name);
+ if (!dbus_path)
+ return -ENOMEM;
+
+ return sd_bus_get_property_string(d->bus,
+ "org.freedesktop.systemd1",
+ dbus_path,
+ "org.freedesktop.systemd1.Service",
+ "Result",
+ NULL,
+ result);
+}
+
+static const struct {
+ const char *result, *explanation;
+} explanations [] = {
+ { "resources", "a configured resource limit was exceeded" },
+ { "timeout", "a timeout was exceeded" },
+ { "exit-code", "the control process exited with error code" },
+ { "signal", "a fatal signal was delivered to the control process" },
+ { "core-dump", "a fatal signal was delivered causing the control process to dump core" },
+ { "watchdog", "the service failed to send watchdog ping" },
+ { "start-limit", "start of the service was attempted too often" }
+};
+
+static void log_job_error_with_service_result(const char* service, const char *result) {
+ _cleanup_free_ char *service_shell_quoted = NULL;
+
+ assert(service);
+
+ service_shell_quoted = shell_maybe_quote(service);
+
+ if (!isempty(result)) {
+ unsigned i;
+
+ for (i = 0; i < ELEMENTSOF(explanations); ++i)
+ if (streq(result, explanations[i].result))
+ break;
+
+ if (i < ELEMENTSOF(explanations)) {
+ log_error("Job for %s failed because %s. See \"systemctl status %s\" and \"journalctl -xe\" for details.\n",
+ service,
+ explanations[i].explanation,
+ strna(service_shell_quoted));
+
+ goto finish;
+ }
+ }
+
+ log_error("Job for %s failed. See \"systemctl status %s\" and \"journalctl -xe\" for details.\n",
+ service,
+ strna(service_shell_quoted));
+
+finish:
+ /* For some results maybe additional explanation is required */
+ if (streq_ptr(result, "start-limit"))
+ log_info("To force a start use \"systemctl reset-failed %1$s\" followed by \"systemctl start %1$s\" again.",
+ strna(service_shell_quoted));
+}
+
static int check_wait_response(BusWaitForJobs *d, bool quiet) {
int r = 0;
@@ -1710,13 +1815,14 @@ static int check_wait_response(BusWaitForJobs *d, bool quiet) {
log_error("Operation on or unit type of %s not supported on this system.", strna(d->name));
else if (!streq(d->result, "done") && !streq(d->result, "skipped")) {
if (d->name) {
- bool quotes;
+ int q;
+ _cleanup_free_ char *result = NULL;
- quotes = chars_intersect(d->name, SHELL_NEED_QUOTES);
+ q = bus_job_get_service_result(d, &result);
+ if (q < 0)
+ log_debug_errno(q, "Failed to get Result property of service %s: %m", d->name);
- log_error("Job for %s failed. See \"systemctl status %s%s%s\" and \"journalctl -xe\" for details.",
- d->name,
- quotes ? "'" : "", d->name, quotes ? "'" : "");
+ log_job_error_with_service_result(d->name, result);
} else
log_error("Job failed. See \"journalctl -xe\" for details.");
}
@@ -1733,7 +1839,7 @@ static int check_wait_response(BusWaitForJobs *d, bool quiet) {
else if (streq(d->result, "assert"))
r = -EPROTO;
else if (streq(d->result, "unsupported"))
- r = -ENOTSUP;
+ r = -EOPNOTSUPP;
else if (!streq(d->result, "done") && !streq(d->result, "skipped"))
r = -EIO;
@@ -1759,7 +1865,6 @@ int bus_wait_for_jobs(BusWaitForJobs *d, bool quiet) {
if (q < 0 && r == 0)
r = q;
- errno = 0;
log_debug_errno(q, "Got result %s/%m for job %s", strna(d->result), strna(d->name));
}
@@ -1785,7 +1890,17 @@ int bus_wait_for_jobs_add(BusWaitForJobs *d, const char *path) {
return set_put_strdup(d->jobs, path);
}
-int bus_deserialize_and_dump_unit_file_changes(sd_bus_message *m, bool quiet) {
+int bus_wait_for_jobs_one(BusWaitForJobs *d, const char *path, bool quiet) {
+ int r;
+
+ r = bus_wait_for_jobs_add(d, path);
+ if (r < 0)
+ return log_oom();
+
+ return bus_wait_for_jobs(d, quiet);
+}
+
+int bus_deserialize_and_dump_unit_file_changes(sd_bus_message *m, bool quiet, UnitFileChange **changes, unsigned *n_changes) {
const char *type, *path, *source;
int r;
@@ -1800,6 +1915,10 @@ int bus_deserialize_and_dump_unit_file_changes(sd_bus_message *m, bool quiet) {
else
log_info("Removed symlink %s.", path);
}
+
+ r = unit_file_changes_add(changes, n_changes, streq(type, "symlink") ? UNIT_FILE_SYMLINK : UNIT_FILE_UNLINK, path, source);
+ if (r < 0)
+ return r;
}
if (r < 0)
return bus_log_parse_error(r);
@@ -1810,3 +1929,157 @@ int bus_deserialize_and_dump_unit_file_changes(sd_bus_message *m, bool quiet) {
return 0;
}
+
+/**
+ * bus_path_encode_unique() - encode unique object path
+ * @b: bus connection or NULL
+ * @prefix: object path prefix
+ * @sender_id: unique-name of client, or NULL
+ * @external_id: external ID to be chosen by client, or NULL
+ * @ret_path: storage for encoded object path pointer
+ *
+ * Whenever we provide a bus API that allows clients to create and manage
+ * server-side objects, we need to provide a unique name for these objects. If
+ * we let the server choose the name, we suffer from a race condition: If a
+ * client creates an object asynchronously, it cannot destroy that object until
+ * it received the method reply. It cannot know the name of the new object,
+ * thus, it cannot destroy it. Furthermore, it enforces a round-trip.
+ *
+ * Therefore, many APIs allow the client to choose the unique name for newly
+ * created objects. There're two problems to solve, though:
+ * 1) Object names are usually defined via dbus object paths, which are
+ * usually globally namespaced. Therefore, multiple clients must be able
+ * to choose unique object names without interference.
+ * 2) If multiple libraries share the same bus connection, they must be
+ * able to choose unique object names without interference.
+ * The first problem is solved easily by prefixing a name with the
+ * unique-bus-name of a connection. The server side must enforce this and
+ * reject any other name. The second problem is solved by providing unique
+ * suffixes from within sd-bus.
+ *
+ * This helper allows clients to create unique object-paths. It uses the
+ * template '/prefix/sender_id/external_id' and returns the new path in
+ * @ret_path (must be freed by the caller).
+ * If @sender_id is NULL, the unique-name of @b is used. If @external_id is
+ * NULL, this function allocates a unique suffix via @b (by requesting a new
+ * cookie). If both @sender_id and @external_id are given, @b can be passed as
+ * NULL.
+ *
+ * Returns: 0 on success, negative error code on failure.
+ */
+int bus_path_encode_unique(sd_bus *b, const char *prefix, const char *sender_id, const char *external_id, char **ret_path) {
+ _cleanup_free_ char *sender_label = NULL, *external_label = NULL;
+ char external_buf[DECIMAL_STR_MAX(uint64_t)], *p;
+ int r;
+
+ assert_return(b || (sender_id && external_id), -EINVAL);
+ assert_return(object_path_is_valid(prefix), -EINVAL);
+ assert_return(ret_path, -EINVAL);
+
+ if (!sender_id) {
+ r = sd_bus_get_unique_name(b, &sender_id);
+ if (r < 0)
+ return r;
+ }
+
+ if (!external_id) {
+ xsprintf(external_buf, "%"PRIu64, ++b->cookie);
+ external_id = external_buf;
+ }
+
+ sender_label = bus_label_escape(sender_id);
+ if (!sender_label)
+ return -ENOMEM;
+
+ external_label = bus_label_escape(external_id);
+ if (!external_label)
+ return -ENOMEM;
+
+ p = strjoin(prefix, "/", sender_label, "/", external_label, NULL);
+ if (!p)
+ return -ENOMEM;
+
+ *ret_path = p;
+ return 0;
+}
+
+/**
+ * bus_path_decode_unique() - decode unique object path
+ * @path: object path to decode
+ * @prefix: object path prefix
+ * @ret_sender: output parameter for sender-id label
+ * @ret_external: output parameter for external-id label
+ *
+ * This does the reverse of bus_path_encode_unique() (see its description for
+ * details). Both trailing labels, sender-id and external-id, are unescaped and
+ * returned in the given output parameters (the caller must free them).
+ *
+ * Note that this function returns 0 if the path does not match the template
+ * (see bus_path_encode_unique()), 1 if it matched.
+ *
+ * Returns: Negative error code on failure, 0 if the given object path does not
+ * match the template (return parameters are set to NULL), 1 if it was
+ * parsed successfully (return parameters contain allocated labels).
+ */
+int bus_path_decode_unique(const char *path, const char *prefix, char **ret_sender, char **ret_external) {
+ const char *p, *q;
+ char *sender, *external;
+
+ assert(object_path_is_valid(path));
+ assert(object_path_is_valid(prefix));
+ assert(ret_sender);
+ assert(ret_external);
+
+ p = object_path_startswith(path, prefix);
+ if (!p) {
+ *ret_sender = NULL;
+ *ret_external = NULL;
+ return 0;
+ }
+
+ q = strchr(p, '/');
+ if (!q) {
+ *ret_sender = NULL;
+ *ret_external = NULL;
+ return 0;
+ }
+
+ sender = bus_label_unescape_n(p, q - p);
+ external = bus_label_unescape(q + 1);
+ if (!sender || !external) {
+ free(sender);
+ free(external);
+ return -ENOMEM;
+ }
+
+ *ret_sender = sender;
+ *ret_external = external;
+ return 1;
+}
+
+bool is_kdbus_wanted(void) {
+ _cleanup_free_ char *value = NULL;
+ int r;
+
+ if (get_proc_cmdline_key("kdbus", NULL) <= 0) {
+ r = get_proc_cmdline_key("kdbus=", &value);
+ if (r <= 0 || parse_boolean(value) != 1)
+ return false;
+ }
+
+ return true;
+}
+
+bool is_kdbus_available(void) {
+ _cleanup_close_ int fd = -1;
+ struct kdbus_cmd cmd = { .size = sizeof(cmd), .flags = KDBUS_FLAG_NEGOTIATE };
+
+ if (!is_kdbus_wanted())
+ return false;
+
+ fd = open("/sys/fs/kdbus/control", O_RDWR | O_CLOEXEC | O_NONBLOCK | O_NOCTTY);
+ if (fd < 0)
+ return false;
+
+ return ioctl(fd, KDBUS_CMD_BUS_MAKE, &cmd) >= 0;
+}
diff --git a/src/libsystemd/sd-bus/bus-util.h b/src/libsystemd/sd-bus/bus-util.h
index e8a97cef9e..999a372cdd 100644
--- a/src/libsystemd/sd-bus/bus-util.h
+++ b/src/libsystemd/sd-bus/bus-util.h
@@ -24,8 +24,8 @@
#include "sd-event.h"
#include "sd-bus.h"
#include "hashmap.h"
+#include "install.h"
#include "time-util.h"
-#include "util.h"
typedef enum BusTransport {
BUS_TRANSPORT_LOCAL,
@@ -46,19 +46,9 @@ struct bus_properties_map {
int bus_map_id128(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata);
-int bus_message_map_all_properties(sd_bus *bus,
- sd_bus_message *m,
- const struct bus_properties_map *map,
- void *userdata);
-int bus_message_map_properties_changed(sd_bus *bus,
- sd_bus_message *m,
- const struct bus_properties_map *map,
- void *userdata);
-int bus_map_all_properties(sd_bus *bus,
- const char *destination,
- const char *path,
- const struct bus_properties_map *map,
- void *userdata);
+int bus_message_map_all_properties(sd_bus_message *m, const struct bus_properties_map *map, void *userdata);
+int bus_message_map_properties_changed(sd_bus_message *m, const struct bus_properties_map *map, void *userdata);
+int bus_map_all_properties(sd_bus *bus, const char *destination, const char *path, const struct bus_properties_map *map, void *userdata);
int bus_async_unregister_and_exit(sd_event *e, sd_bus *bus, const char *name);
@@ -70,9 +60,9 @@ int bus_name_has_owner(sd_bus *c, const char *name, sd_bus_error *error);
int bus_check_peercred(sd_bus *c);
-int bus_verify_polkit(sd_bus_message *call, int capability, const char *action, bool interactive, bool *_challenge, sd_bus_error *e);
+int bus_test_polkit(sd_bus_message *call, int capability, const char *action, uid_t good_user, bool *_challenge, sd_bus_error *e);
-int bus_verify_polkit_async(sd_bus_message *call, int capability, const char *action, bool interactive, Hashmap **registry, sd_bus_error *error);
+int bus_verify_polkit_async(sd_bus_message *call, int capability, const char *action, bool interactive, uid_t good_user, Hashmap **registry, sd_bus_error *error);
void bus_verify_polkit_async_registry_free(Hashmap *registry);
int bus_open_system_systemd(sd_bus **_bus);
@@ -208,7 +198,14 @@ int bus_wait_for_jobs_new(sd_bus *bus, BusWaitForJobs **ret);
void bus_wait_for_jobs_free(BusWaitForJobs *d);
int bus_wait_for_jobs_add(BusWaitForJobs *d, const char *path);
int bus_wait_for_jobs(BusWaitForJobs *d, bool quiet);
+int bus_wait_for_jobs_one(BusWaitForJobs *d, const char *path, bool quiet);
DEFINE_TRIVIAL_CLEANUP_FUNC(BusWaitForJobs*, bus_wait_for_jobs_free);
-int bus_deserialize_and_dump_unit_file_changes(sd_bus_message *m, bool quiet);
+int bus_deserialize_and_dump_unit_file_changes(sd_bus_message *m, bool quiet, UnitFileChange **changes, unsigned *n_changes);
+
+int bus_path_encode_unique(sd_bus *b, const char *prefix, const char *sender_id, const char *external_id, char **ret_path);
+int bus_path_decode_unique(const char *path, const char *prefix, char **ret_sender, char **ret_external);
+
+bool is_kdbus_wanted(void);
+bool is_kdbus_available(void);
diff --git a/src/libsystemd/sd-bus/busctl-introspect.h b/src/libsystemd/sd-bus/busctl-introspect.h
index d6b4cf05a7..ea807d5973 100644
--- a/src/libsystemd/sd-bus/busctl-introspect.h
+++ b/src/libsystemd/sd-bus/busctl-introspect.h
@@ -21,7 +21,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <inttypes.h>
#include <stdbool.h>
typedef struct XMLIntrospectOps {
diff --git a/src/libsystemd/sd-bus/busctl.c b/src/libsystemd/sd-bus/busctl.c
index d3c1772019..39caa4e7d6 100644
--- a/src/libsystemd/sd-bus/busctl.c
+++ b/src/libsystemd/sd-bus/busctl.c
@@ -26,18 +26,17 @@
#include "log.h"
#include "build.h"
#include "pager.h"
-#include "xml.h"
#include "path-util.h"
#include "set.h"
#include "sd-bus.h"
-#include "bus-message.h"
#include "bus-internal.h"
#include "bus-util.h"
#include "bus-dump.h"
#include "bus-signature.h"
#include "bus-type.h"
#include "busctl-introspect.h"
+#include "terminal-util.h"
static bool arg_no_pager = false;
static bool arg_legend = true;
diff --git a/src/libsystemd/sd-bus/kdbus.h b/src/libsystemd/sd-bus/kdbus.h
index 1cc475eb1d..fc1d77dd7c 100644
--- a/src/libsystemd/sd-bus/kdbus.h
+++ b/src/libsystemd/sd-bus/kdbus.h
@@ -70,14 +70,14 @@ struct kdbus_notify_name_change {
* KDBUS_ITEM_CREDS
*/
struct kdbus_creds {
- __u32 uid;
- __u32 euid;
- __u32 suid;
- __u32 fsuid;
- __u32 gid;
- __u32 egid;
- __u32 sgid;
- __u32 fsgid;
+ __u64 uid;
+ __u64 euid;
+ __u64 suid;
+ __u64 fsuid;
+ __u64 gid;
+ __u64 egid;
+ __u64 sgid;
+ __u64 fsgid;
} __attribute__((__aligned__(8)));
/**
@@ -457,24 +457,16 @@ struct kdbus_item {
} __attribute__((__aligned__(8)));
/**
- * struct kdbus_item_list - A list of items
- * @size: The total size of the structure
- * @items: Array of items
- */
-struct kdbus_item_list {
- __u64 size;
- struct kdbus_item items[0];
-} __attribute__((__aligned__(8)));
-
-/**
* enum kdbus_msg_flags - type of message
* @KDBUS_MSG_EXPECT_REPLY: Expect a reply message, used for
* method calls. The userspace-supplied
* cookie identifies the message and the
* respective reply carries the cookie
* in cookie_reply
- * @KDBUS_MSG_NO_AUTO_START: Do not start a service, if the addressed
- * name is not currently active
+ * @KDBUS_MSG_NO_AUTO_START: Do not start a service if the addressed
+ * name is not currently active. This flag is
+ * not looked at by the kernel but only
+ * serves as hint for userspace implementations.
* @KDBUS_MSG_SIGNAL: Treat this message as signal
*/
enum kdbus_msg_flags {
@@ -507,9 +499,12 @@ enum kdbus_payload_type {
* @cookie: Userspace-supplied cookie, for the connection
* to identify its messages
* @timeout_ns: The time to wait for a message reply from the peer.
- * If there is no reply, a kernel-generated message
+ * If there is no reply, and the send command is
+ * executed asynchronously, a kernel-generated message
* with an attached KDBUS_ITEM_REPLY_TIMEOUT item
- * is sent to @src_id. The timeout is expected in
+ * is sent to @src_id. For synchronously executed send
+ * command, the value denotes the maximum time the call
+ * blocks to wait for a reply. The timeout is expected in
* nanoseconds and as absolute CLOCK_MONOTONIC value.
* @cookie_reply: A reply to the requesting message with the same
* cookie. The requesting connection can match its
@@ -602,9 +597,15 @@ enum kdbus_recv_flags {
* @KDBUS_RECV_RETURN_INCOMPLETE_FDS: One or more file descriptors could not
* be installed. These descriptors in
* KDBUS_ITEM_FDS will carry the value -1.
+ * @KDBUS_RECV_RETURN_DROPPED_MSGS: There have been dropped messages since
+ * the last time a message was received.
+ * The 'dropped_msgs' counter contains the
+ * number of messages dropped pool
+ * overflows or other missed broadcasts.
*/
enum kdbus_recv_return_flags {
KDBUS_RECV_RETURN_INCOMPLETE_FDS = 1ULL << 0,
+ KDBUS_RECV_RETURN_DROPPED_MSGS = 1ULL << 1,
};
/**
@@ -614,10 +615,12 @@ enum kdbus_recv_return_flags {
* @return_flags: Command return flags, kernel → userspace
* @priority: Minimum priority of the messages to de-queue. Lowest
* values have the highest priority.
- * @dropped_msgs: In case the KDBUS_CMD_RECV ioctl returns
- * -EOVERFLOW, this field will contain the number of
- * broadcast messages that have been lost since the
- * last call.
+ * @dropped_msgs: In case there were any dropped messages since the last
+ * time a message was received, this will be set to the
+ * number of lost messages and
+ * KDBUS_RECV_RETURN_DROPPED_MSGS will be set in
+ * 'return_flags'. This can only happen if the ioctl
+ * returns 0 or EAGAIN.
* @msg: Return storage for received message.
* @items: Additional items for this command.
*
@@ -691,10 +694,10 @@ enum kdbus_hello_flags {
* @id: The ID of this connection (kernel → userspace)
* @pool_size: Size of the connection's buffer where the received
* messages are placed
- * @offset: Pool offset where additional items of type
- * kdbus_item_list are stored. They contain information
- * about the bus and the newly created connection.
- * @items_size: Copy of item_list.size stored in @offset.
+ * @offset: Pool offset where items are returned to report
+ * additional information about the bus and the newly
+ * created connection.
+ * @items_size: Size of buffer returned in the pool slice at @offset.
* @id128: Unique 128-bit ID of the bus (kernel → userspace)
* @items: A list of items
*
@@ -772,11 +775,13 @@ struct kdbus_cmd_list {
/**
* struct kdbus_cmd_info - struct used for KDBUS_CMD_CONN_INFO ioctl
* @size: The total size of the struct
- * @flags: KDBUS_ATTACH_* flags, userspace → kernel
+ * @flags: Flags for this ioctl, userspace → kernel
* @return_flags: Command return flags, kernel → userspace
* @id: The 64-bit ID of the connection. If set to zero, passing
* @name is required. kdbus will look up the name to
* determine the ID in this case.
+ * @attach_flags: Set of attach flags to specify the set of information
+ * to receive, userspace → kernel
* @offset: Returned offset in the caller's pool buffer where the
* kdbus_info struct result is stored. The user must
* use KDBUS_CMD_FREE to free the allocated memory.
@@ -794,6 +799,7 @@ struct kdbus_cmd_info {
__u64 flags;
__u64 return_flags;
__u64 id;
+ __u64 attach_flags;
__u64 offset;
__u64 info_size;
struct kdbus_item items[0];
diff --git a/src/libsystemd/sd-bus/sd-bus.c b/src/libsystemd/sd-bus/sd-bus.c
index cac9b65601..edc27aef87 100644
--- a/src/libsystemd/sd-bus/sd-bus.c
+++ b/src/libsystemd/sd-bus/sd-bus.c
@@ -20,19 +20,16 @@
***/
#include <endian.h>
-#include <assert.h>
#include <stdlib.h>
#include <unistd.h>
#include <netdb.h>
#include <poll.h>
-#include <byteswap.h>
#include <sys/mman.h>
#include <pthread.h>
#include "util.h"
#include "macro.h"
#include "strv.h"
-#include "set.h"
#include "missing.h"
#include "def.h"
#include "cgroup-util.h"
@@ -45,8 +42,6 @@
#include "bus-socket.h"
#include "bus-kernel.h"
#include "bus-control.h"
-#include "bus-introspect.h"
-#include "bus-signature.h"
#include "bus-objects.h"
#include "bus-util.h"
#include "bus-container.h"
@@ -357,13 +352,30 @@ _public_ int sd_bus_set_description(sd_bus *bus, const char *description) {
return free_and_strdup(&bus->description, description);
}
-static int hello_callback(sd_bus *bus, sd_bus_message *reply, void *userdata, sd_bus_error *error) {
+_public_ int sd_bus_set_allow_interactive_authorization(sd_bus *bus, int b) {
+ assert_return(bus, -EINVAL);
+ assert_return(!bus_pid_changed(bus), -ECHILD);
+
+ bus->allow_interactive_authorization = !!b;
+ return 0;
+}
+
+_public_ int sd_bus_get_allow_interactive_authorization(sd_bus *bus) {
+ assert_return(bus, -EINVAL);
+ assert_return(!bus_pid_changed(bus), -ECHILD);
+
+ return bus->allow_interactive_authorization;
+}
+
+static int hello_callback(sd_bus_message *reply, void *userdata, sd_bus_error *error) {
const char *s;
+ sd_bus *bus;
int r;
+ assert(reply);
+ bus = reply->bus;
assert(bus);
assert(bus->state == BUS_HELLO || bus->state == BUS_CLOSING);
- assert(reply);
r = sd_bus_message_get_errno(reply);
if (r > 0)
@@ -1513,15 +1525,27 @@ static int bus_seal_message(sd_bus *b, sd_bus_message *m, usec_t timeout) {
}
static int bus_remarshal_message(sd_bus *b, sd_bus_message **m) {
+ bool remarshal = false;
+
assert(b);
- /* Do packet version and endianness already match? */
- if ((b->message_version == 0 || b->message_version == (*m)->header->version) &&
- (b->message_endian == 0 || b->message_endian == (*m)->header->endian))
- return 0;
+ /* wrong packet version */
+ if (b->message_version != 0 && b->message_version != (*m)->header->version)
+ remarshal = true;
+
+ /* wrong packet endianness */
+ if (b->message_endian != 0 && b->message_endian != (*m)->header->endian)
+ remarshal = true;
- /* No? Then remarshal! */
- return bus_message_remarshal(b, m);
+ /* TODO: kdbus-messages received from the kernel contain data which is
+ * not allowed to be passed to KDBUS_CMD_SEND. Therefore, we have to
+ * force remarshaling of the message. Technically, we could just
+ * recreate the kdbus message, but that is non-trivial as other parts of
+ * the message refer to m->kdbus already. This should be fixed! */
+ if ((*m)->kdbus && (*m)->release_kdbus)
+ remarshal = true;
+
+ return remarshal ? bus_message_remarshal(b, m) : 0;
}
int bus_seal_synthetic_message(sd_bus *b, sd_bus_message *m) {
@@ -1671,8 +1695,11 @@ static int bus_send_internal(sd_bus *bus, sd_bus_message *_m, uint64_t *cookie,
_cleanup_bus_message_unref_ sd_bus_message *m = sd_bus_message_ref(_m);
int r;
- assert_return(bus, -EINVAL);
assert_return(m, -EINVAL);
+
+ if (!bus)
+ bus = m->bus;
+
assert_return(!bus_pid_changed(bus), -ECHILD);
assert_return(!bus->is_kernel || !(bus->hello_flags & KDBUS_HELLO_MONITOR), -EROFS);
@@ -1684,7 +1711,7 @@ static int bus_send_internal(sd_bus *bus, sd_bus_message *_m, uint64_t *cookie,
if (r < 0)
return r;
if (r == 0)
- return -ENOTSUP;
+ return -EOPNOTSUPP;
}
/* If the cookie number isn't kept, then we know that no reply
@@ -1757,8 +1784,11 @@ _public_ int sd_bus_send(sd_bus *bus, sd_bus_message *m, uint64_t *cookie) {
_public_ int sd_bus_send_to(sd_bus *bus, sd_bus_message *m, const char *destination, uint64_t *cookie) {
int r;
- assert_return(bus, -EINVAL);
assert_return(m, -EINVAL);
+
+ if (!bus)
+ bus = m->bus;
+
assert_return(!bus_pid_changed(bus), -ECHILD);
if (!BUS_IS_OPEN(bus->state))
@@ -1814,11 +1844,14 @@ _public_ int sd_bus_call_async(
_cleanup_bus_slot_unref_ sd_bus_slot *s = NULL;
int r;
- assert_return(bus, -EINVAL);
assert_return(m, -EINVAL);
assert_return(m->header->type == SD_BUS_MESSAGE_METHOD_CALL, -EINVAL);
assert_return(!(m->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED), -EINVAL);
assert_return(callback, -EINVAL);
+
+ if (!bus)
+ bus = m->bus;
+
assert_return(!bus_pid_changed(bus), -ECHILD);
assert_return(!bus->is_kernel || !(bus->hello_flags & KDBUS_HELLO_MONITOR), -EROFS);
@@ -1912,11 +1945,14 @@ _public_ int sd_bus_call(
unsigned i;
int r;
- assert_return(bus, -EINVAL);
assert_return(m, -EINVAL);
assert_return(m->header->type == SD_BUS_MESSAGE_METHOD_CALL, -EINVAL);
assert_return(!(m->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED), -EINVAL);
assert_return(!bus_error_is_dirty(error), -EINVAL);
+
+ if (!bus)
+ bus = m->bus;
+
assert_return(!bus_pid_changed(bus), -ECHILD);
assert_return(!bus->is_kernel || !(bus->hello_flags & KDBUS_HELLO_MONITOR), -EROFS);
@@ -2171,7 +2207,7 @@ static int process_timeout(sd_bus *bus) {
bus->current_slot = sd_bus_slot_ref(slot);
bus->current_handler = c->callback;
bus->current_userdata = slot->userdata;
- r = c->callback(bus, m, slot->userdata, &error_buffer);
+ r = c->callback(m, slot->userdata, &error_buffer);
bus->current_userdata = NULL;
bus->current_handler = NULL;
bus->current_slot = NULL;
@@ -2274,7 +2310,7 @@ static int process_reply(sd_bus *bus, sd_bus_message *m) {
bus->current_slot = sd_bus_slot_ref(slot);
bus->current_handler = c->callback;
bus->current_userdata = slot->userdata;
- r = c->callback(bus, m, slot->userdata, &error_buffer);
+ r = c->callback(m, slot->userdata, &error_buffer);
bus->current_userdata = NULL;
bus->current_handler = NULL;
bus->current_slot = NULL;
@@ -2321,7 +2357,7 @@ static int process_filter(sd_bus *bus, sd_bus_message *m) {
bus->current_slot = sd_bus_slot_ref(slot);
bus->current_handler = l->callback;
bus->current_userdata = slot->userdata;
- r = l->callback(bus, m, slot->userdata, &error_buffer);
+ r = l->callback(m, slot->userdata, &error_buffer);
bus->current_userdata = NULL;
bus->current_handler = NULL;
bus->current_slot = sd_bus_slot_unref(slot);
@@ -2602,7 +2638,7 @@ static int process_closing(sd_bus *bus, sd_bus_message **ret) {
bus->current_slot = sd_bus_slot_ref(slot);
bus->current_handler = c->callback;
bus->current_userdata = slot->userdata;
- r = c->callback(bus, m, slot->userdata, &error_buffer);
+ r = c->callback(m, slot->userdata, &error_buffer);
bus->current_userdata = NULL;
bus->current_handler = NULL;
bus->current_slot = NULL;
@@ -3376,7 +3412,7 @@ _public_ int sd_bus_try_close(sd_bus *bus) {
assert_return(!bus_pid_changed(bus), -ECHILD);
if (!bus->is_kernel)
- return -ENOTSUP;
+ return -EOPNOTSUPP;
if (!BUS_IS_OPEN(bus->state))
return -ENOTCONN;
@@ -3477,7 +3513,7 @@ _public_ int sd_bus_get_address(sd_bus *bus, const char **address) {
return -ENODATA;
}
-int sd_bus_get_creds_mask(sd_bus *bus, uint64_t *mask) {
+_public_ int sd_bus_get_creds_mask(sd_bus *bus, uint64_t *mask) {
assert_return(bus, -EINVAL);
assert_return(mask, -EINVAL);
assert_return(!bus_pid_changed(bus), -ECHILD);
@@ -3486,35 +3522,35 @@ int sd_bus_get_creds_mask(sd_bus *bus, uint64_t *mask) {
return 0;
}
-int sd_bus_is_bus_client(sd_bus *bus) {
+_public_ int sd_bus_is_bus_client(sd_bus *bus) {
assert_return(bus, -EINVAL);
assert_return(!bus_pid_changed(bus), -ECHILD);
return bus->bus_client;
}
-int sd_bus_is_server(sd_bus *bus) {
+_public_ int sd_bus_is_server(sd_bus *bus) {
assert_return(bus, -EINVAL);
assert_return(!bus_pid_changed(bus), -ECHILD);
return bus->is_server;
}
-int sd_bus_is_anonymous(sd_bus *bus) {
+_public_ int sd_bus_is_anonymous(sd_bus *bus) {
assert_return(bus, -EINVAL);
assert_return(!bus_pid_changed(bus), -ECHILD);
return bus->anonymous_auth;
}
-int sd_bus_is_trusted(sd_bus *bus) {
+_public_ int sd_bus_is_trusted(sd_bus *bus) {
assert_return(bus, -EINVAL);
assert_return(!bus_pid_changed(bus), -ECHILD);
return bus->trusted;
}
-int sd_bus_is_monitor(sd_bus *bus) {
+_public_ int sd_bus_is_monitor(sd_bus *bus) {
assert_return(bus, -EINVAL);
assert_return(!bus_pid_changed(bus), -ECHILD);
diff --git a/src/libsystemd/sd-bus/test-bus-kernel-benchmark.c b/src/libsystemd/sd-bus/test-bus-benchmark.c
index 35f87e91bd..d14110aa04 100644
--- a/src/libsystemd/sd-bus/test-bus-kernel-benchmark.c
+++ b/src/libsystemd/sd-bus/test-bus-benchmark.c
@@ -19,16 +19,13 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <ctype.h>
#include <sys/wait.h>
+#include "def.h"
#include "util.h"
-#include "log.h"
#include "time-util.h"
#include "sd-bus.h"
-#include "bus-message.h"
-#include "bus-error.h"
#include "bus-kernel.h"
#include "bus-internal.h"
#include "bus-util.h"
@@ -37,6 +34,12 @@
static usec_t arg_loop_usec = 100 * USEC_PER_MSEC;
+typedef enum Type {
+ TYPE_KDBUS,
+ TYPE_LEGACY,
+ TYPE_DIRECT,
+} Type;
+
static void server(sd_bus *b, size_t *result) {
int r;
@@ -60,7 +63,8 @@ static void server(sd_bus *b, size_t *result) {
/* Make sure the mmap is mapped */
assert_se(sd_bus_message_read_array(m, 'y', &p, &sz) > 0);
- assert_se(sd_bus_reply_method_return(m, NULL) >= 0);
+ r = sd_bus_reply_method_return(m, NULL);
+ assert_se(r >= 0);
} else if (sd_bus_message_is_method_call(m, "benchmark.server", "Exit")) {
uint64_t res;
assert_se(sd_bus_message_read(m, "t", &res) > 0);
@@ -68,16 +72,16 @@ static void server(sd_bus *b, size_t *result) {
*result = res;
return;
- } else
+ } else if (!sd_bus_message_is_signal(m, NULL, NULL))
assert_not_reached("Unknown method");
}
}
-static void transaction(sd_bus *b, size_t sz) {
+static void transaction(sd_bus *b, size_t sz, const char *server_name) {
_cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
uint8_t *p;
- assert_se(sd_bus_message_new_method_call(b, &m, ":1.1", "/", "benchmark.server", "Work") >= 0);
+ assert_se(sd_bus_message_new_method_call(b, &m, server_name, "/", "benchmark.server", "Work") >= 0);
assert_se(sd_bus_message_append_array_space(m, 'y', sz, (void**) &p) >= 0);
memset(p, 0x80, sz);
@@ -85,7 +89,7 @@ static void transaction(sd_bus *b, size_t sz) {
assert_se(sd_bus_call(b, m, 0, NULL, &reply) >= 0);
}
-static void client_bisect(const char *address) {
+static void client_bisect(const char *address, const char *server_name) {
_cleanup_bus_message_unref_ sd_bus_message *x = NULL;
size_t lsize, rsize, csize;
sd_bus *b;
@@ -100,7 +104,8 @@ static void client_bisect(const char *address) {
r = sd_bus_start(b);
assert_se(r >= 0);
- assert_se(sd_bus_call_method(b, ":1.1", "/", "benchmark.server", "Ping", NULL, NULL, NULL) >= 0);
+ r = sd_bus_call_method(b, server_name, "/", "benchmark.server", "Ping", NULL, NULL, NULL);
+ assert_se(r >= 0);
lsize = 1;
rsize = MAX_SIZE;
@@ -125,7 +130,7 @@ static void client_bisect(const char *address) {
t = now(CLOCK_MONOTONIC);
for (n_copying = 0;; n_copying++) {
- transaction(b, csize);
+ transaction(b, csize, server_name);
if (now(CLOCK_MONOTONIC) >= t + arg_loop_usec)
break;
}
@@ -135,7 +140,7 @@ static void client_bisect(const char *address) {
t = now(CLOCK_MONOTONIC);
for (n_memfd = 0;; n_memfd++) {
- transaction(b, csize);
+ transaction(b, csize, server_name);
if (now(CLOCK_MONOTONIC) >= t + arg_loop_usec)
break;
}
@@ -151,14 +156,14 @@ static void client_bisect(const char *address) {
}
b->use_memfd = 1;
- assert_se(sd_bus_message_new_method_call(b, &x, ":1.1", "/", "benchmark.server", "Exit") >= 0);
+ assert_se(sd_bus_message_new_method_call(b, &x, server_name, "/", "benchmark.server", "Exit") >= 0);
assert_se(sd_bus_message_append(x, "t", csize) >= 0);
assert_se(sd_bus_send(b, x, NULL) >= 0);
sd_bus_unref(b);
}
-static void client_chart(const char *address) {
+static void client_chart(Type type, const char *address, const char *server_name, int fd) {
_cleanup_bus_message_unref_ sd_bus_message *x = NULL;
size_t csize;
sd_bus *b;
@@ -167,15 +172,34 @@ static void client_chart(const char *address) {
r = sd_bus_new(&b);
assert_se(r >= 0);
- r = sd_bus_set_address(b, address);
- assert_se(r >= 0);
+ if (type == TYPE_DIRECT) {
+ r = sd_bus_set_fd(b, fd, fd);
+ assert_se(r >= 0);
+ } else {
+ r = sd_bus_set_address(b, address);
+ assert_se(r >= 0);
+
+ r = sd_bus_set_bus_client(b, true);
+ assert_se(r >= 0);
+ }
r = sd_bus_start(b);
assert_se(r >= 0);
- assert_se(sd_bus_call_method(b, ":1.1", "/", "benchmark.server", "Ping", NULL, NULL, NULL) >= 0);
+ r = sd_bus_call_method(b, server_name, "/", "benchmark.server", "Ping", NULL, NULL, NULL);
+ assert_se(r >= 0);
- printf("SIZE\tCOPY\tMEMFD\n");
+ switch (type) {
+ case TYPE_KDBUS:
+ printf("SIZE\tCOPY\tMEMFD\n");
+ break;
+ case TYPE_LEGACY:
+ printf("SIZE\tLEGACY\n");
+ break;
+ case TYPE_DIRECT:
+ printf("SIZE\tDIRECT\n");
+ break;
+ }
for (csize = 1; csize <= MAX_SIZE; csize *= 2) {
usec_t t;
@@ -183,22 +207,24 @@ static void client_chart(const char *address) {
printf("%zu\t", csize);
- b->use_memfd = 0;
+ if (type == TYPE_KDBUS) {
+ b->use_memfd = 0;
- t = now(CLOCK_MONOTONIC);
- for (n_copying = 0;; n_copying++) {
- transaction(b, csize);
- if (now(CLOCK_MONOTONIC) >= t + arg_loop_usec)
- break;
- }
+ t = now(CLOCK_MONOTONIC);
+ for (n_copying = 0;; n_copying++) {
+ transaction(b, csize, server_name);
+ if (now(CLOCK_MONOTONIC) >= t + arg_loop_usec)
+ break;
+ }
- printf("%u\t", (unsigned) ((n_copying * USEC_PER_SEC) / arg_loop_usec));
+ printf("%u\t", (unsigned) ((n_copying * USEC_PER_SEC) / arg_loop_usec));
- b->use_memfd = -1;
+ b->use_memfd = -1;
+ }
t = now(CLOCK_MONOTONIC);
for (n_memfd = 0;; n_memfd++) {
- transaction(b, csize);
+ transaction(b, csize, server_name);
if (now(CLOCK_MONOTONIC) >= t + arg_loop_usec)
break;
}
@@ -207,7 +233,7 @@ static void client_chart(const char *address) {
}
b->use_memfd = 1;
- assert_se(sd_bus_message_new_method_call(b, &x, ":1.1", "/", "benchmark.server", "Exit") >= 0);
+ assert_se(sd_bus_message_new_method_call(b, &x, server_name, "/", "benchmark.server", "Exit") >= 0);
assert_se(sd_bus_message_append(x, "t", csize) >= 0);
assert_se(sd_bus_send(b, x, NULL) >= 0);
@@ -219,9 +245,11 @@ int main(int argc, char *argv[]) {
MODE_BISECT,
MODE_CHART,
} mode = MODE_BISECT;
- int i;
- _cleanup_free_ char *name = NULL, *bus_name = NULL, *address = NULL;
+ Type type = TYPE_KDBUS;
+ int i, pair[2] = { -1, -1 };
+ _cleanup_free_ char *name = NULL, *bus_name = NULL, *address = NULL, *server_name = NULL;
_cleanup_close_ int bus_ref = -1;
+ const char *unique;
cpu_set_t cpuset;
size_t result;
sd_bus *b;
@@ -232,33 +260,72 @@ int main(int argc, char *argv[]) {
if (streq(argv[i], "chart")) {
mode = MODE_CHART;
continue;
+ } else if (streq(argv[i], "legacy")) {
+ type = TYPE_LEGACY;
+ continue;
+ } else if (streq(argv[i], "direct")) {
+ type = TYPE_DIRECT;
+ continue;
}
assert_se(parse_sec(argv[i], &arg_loop_usec) >= 0);
}
+ assert_se(!MODE_BISECT || TYPE_KDBUS);
+
assert_se(arg_loop_usec > 0);
- assert_se(asprintf(&name, "deine-mutter-%u", (unsigned) getpid()) >= 0);
+ if (type == TYPE_KDBUS) {
+ assert_se(asprintf(&name, "deine-mutter-%u", (unsigned) getpid()) >= 0);
+
+ bus_ref = bus_kernel_create_bus(name, false, &bus_name);
+ if (bus_ref == -ENOENT)
+ exit(EXIT_TEST_SKIP);
- bus_ref = bus_kernel_create_bus(name, false, &bus_name);
- if (bus_ref == -ENOENT)
- exit(EXIT_TEST_SKIP);
+ assert_se(bus_ref >= 0);
- assert_se(bus_ref >= 0);
+ address = strappend("kernel:path=", bus_name);
+ assert_se(address);
+ } else if (type == TYPE_LEGACY) {
+ const char *e;
- address = strappend("kernel:path=", bus_name);
- assert_se(address);
+ e = secure_getenv("DBUS_SESSION_BUS_ADDRESS");
+ assert_se(e);
+
+ address = strdup(e);
+ assert_se(address);
+ }
r = sd_bus_new(&b);
assert_se(r >= 0);
- r = sd_bus_set_address(b, address);
- assert_se(r >= 0);
+ if (type == TYPE_DIRECT) {
+ assert_se(socketpair(AF_UNIX, SOCK_STREAM, 0, pair) >= 0);
+
+ r = sd_bus_set_fd(b, pair[0], pair[0]);
+ assert_se(r >= 0);
+
+ r = sd_bus_set_server(b, true, SD_ID128_NULL);
+ assert_se(r >= 0);
+ } else {
+ r = sd_bus_set_address(b, address);
+ assert_se(r >= 0);
+
+ r = sd_bus_set_bus_client(b, true);
+ assert_se(r >= 0);
+ }
r = sd_bus_start(b);
assert_se(r >= 0);
+ if (type != TYPE_DIRECT) {
+ r = sd_bus_get_unique_name(b, &unique);
+ assert_se(r >= 0);
+
+ server_name = strdup(unique);
+ assert_se(server_name);
+ }
+
sync();
setpriority(PRIO_PROCESS, 0, -19);
@@ -275,11 +342,11 @@ int main(int argc, char *argv[]) {
switch (mode) {
case MODE_BISECT:
- client_bisect(address);
+ client_bisect(address, server_name);
break;
case MODE_CHART:
- client_chart(address);
+ client_chart(type, address, server_name, pair[1]);
break;
}
@@ -297,6 +364,7 @@ int main(int argc, char *argv[]) {
assert_se(waitpid(pid, NULL, 0) == pid);
+ safe_close(pair[1]);
sd_bus_unref(b);
return 0;
diff --git a/src/libsystemd/sd-bus/test-bus-chat.c b/src/libsystemd/sd-bus/test-bus-chat.c
index 8625ee6d89..046e999008 100644
--- a/src/libsystemd/sd-bus/test-bus-chat.c
+++ b/src/libsystemd/sd-bus/test-bus-chat.c
@@ -27,24 +27,22 @@
#include "log.h"
#include "util.h"
#include "macro.h"
+#include "formats-util.h"
#include "sd-bus.h"
-#include "bus-message.h"
#include "bus-error.h"
#include "bus-match.h"
#include "bus-internal.h"
#include "bus-util.h"
-static int match_callback(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
+static int match_callback(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
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, sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
+static int object_callback(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
int r;
- assert_se(bus);
-
if (sd_bus_message_is_method_error(m, NULL))
return 0;
@@ -264,11 +262,11 @@ fail:
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;
+ _cleanup_bus_close_unref_ sd_bus *bus = NULL;
+ _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
const char *hello;
int r;
- int pp[2] = { -1, -1 };
+ _cleanup_close_pair_ int pp[2] = { -1, -1 };
char x;
r = sd_bus_open_user(&bus);
@@ -347,18 +345,12 @@ finish:
else
sd_bus_send(bus, q, NULL);
- sd_bus_flush(bus);
- sd_bus_unref(bus);
}
- sd_bus_error_free(&error);
-
- safe_close_pair(pp);
-
return INT_TO_PTR(r);
}
-static int quit_callback(sd_bus *b, sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
+static int quit_callback(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
bool *x = userdata;
log_error("Quit callback: %s", strerror(sd_bus_message_get_errno(m)));
@@ -369,8 +361,8 @@ static int quit_callback(sd_bus *b, sd_bus_message *m, void *userdata, sd_bus_er
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;
+ _cleanup_bus_close_unref_ sd_bus *bus = NULL;
+ _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
bool quit = false;
const char *mid;
int r;
@@ -399,8 +391,7 @@ static void* client2(void*p) {
goto finish;
}
- sd_bus_message_unref(m);
- m = NULL;
+ m = sd_bus_message_unref(m);
r = sd_bus_message_new_signal(
bus,
@@ -419,8 +410,7 @@ static void* client2(void*p) {
goto finish;
}
- sd_bus_message_unref(m);
- m = NULL;
+ m = sd_bus_message_unref(m);
r = sd_bus_message_new_method_call(
bus,
@@ -448,8 +438,7 @@ static void* client2(void*p) {
log_info("Machine ID is %s.", mid);
- sd_bus_message_unref(m);
- m = NULL;
+ m = sd_bus_message_unref(m);
r = sd_bus_message_new_method_call(
bus,
@@ -463,8 +452,7 @@ static void* client2(void*p) {
goto finish;
}
- sd_bus_message_unref(reply);
- reply = NULL;
+ reply = sd_bus_message_unref(reply);
r = sd_bus_call(bus, m, 200 * USEC_PER_MSEC, &error, &reply);
if (r < 0)
@@ -472,8 +460,7 @@ static void* client2(void*p) {
else
log_info("Slow call succeed.");
- sd_bus_message_unref(m);
- m = NULL;
+ m = sd_bus_message_unref(m);
r = sd_bus_message_new_method_call(
bus,
@@ -526,12 +513,9 @@ finish:
goto finish;
}
- sd_bus_send(bus, q, NULL);
- sd_bus_flush(bus);
- sd_bus_unref(bus);
+ (void) sd_bus_send(bus, q, NULL);
}
- sd_bus_error_free(&error);
return INT_TO_PTR(r);
}
diff --git a/src/libsystemd/sd-bus/test-bus-creds.c b/src/libsystemd/sd-bus/test-bus-creds.c
index ff2602ba34..edd5033db2 100644
--- a/src/libsystemd/sd-bus/test-bus-creds.c
+++ b/src/libsystemd/sd-bus/test-bus-creds.c
@@ -22,7 +22,6 @@
#include "sd-bus.h"
#include "bus-dump.h"
#include "bus-util.h"
-#include "util.h"
int main(int argc, char *argv[]) {
_cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
diff --git a/src/libsystemd/sd-bus/test-bus-error.c b/src/libsystemd/sd-bus/test-bus-error.c
index 463fc81c75..5753c04b0e 100644
--- a/src/libsystemd/sd-bus/test-bus-error.c
+++ b/src/libsystemd/sd-bus/test-bus-error.c
@@ -35,11 +35,11 @@ static void test_error(void) {
};
assert_se(!sd_bus_error_is_set(&error));
- assert_se(sd_bus_error_set(&error, SD_BUS_ERROR_NOT_SUPPORTED, "xxx") == -ENOTSUP);
+ assert_se(sd_bus_error_set(&error, SD_BUS_ERROR_NOT_SUPPORTED, "xxx") == -EOPNOTSUPP);
assert_se(streq(error.name, SD_BUS_ERROR_NOT_SUPPORTED));
assert_se(streq(error.message, "xxx"));
assert_se(sd_bus_error_has_name(&error, SD_BUS_ERROR_NOT_SUPPORTED));
- assert_se(sd_bus_error_get_errno(&error) == ENOTSUP);
+ assert_se(sd_bus_error_get_errno(&error) == EOPNOTSUPP);
assert_se(sd_bus_error_is_set(&error));
sd_bus_error_free(&error);
diff --git a/src/libsystemd/sd-bus/test-bus-introspect.c b/src/libsystemd/sd-bus/test-bus-introspect.c
index 67b6461f30..b2caa02870 100644
--- a/src/libsystemd/sd-bus/test-bus-introspect.c
+++ b/src/libsystemd/sd-bus/test-bus-introspect.c
@@ -19,7 +19,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "util.h"
#include "log.h"
#include "bus-introspect.h"
diff --git a/src/libsystemd/sd-bus/test-bus-kernel-bloom.c b/src/libsystemd/sd-bus/test-bus-kernel-bloom.c
index 071b7e0cf9..b11c43bd7b 100644
--- a/src/libsystemd/sd-bus/test-bus-kernel-bloom.c
+++ b/src/libsystemd/sd-bus/test-bus-kernel-bloom.c
@@ -23,8 +23,6 @@
#include "log.h"
#include "sd-bus.h"
-#include "bus-message.h"
-#include "bus-error.h"
#include "bus-kernel.h"
#include "bus-util.h"
diff --git a/src/libsystemd/sd-bus/test-bus-kernel.c b/src/libsystemd/sd-bus/test-bus-kernel.c
index 3aec568229..6506eaab2e 100644
--- a/src/libsystemd/sd-bus/test-bus-kernel.c
+++ b/src/libsystemd/sd-bus/test-bus-kernel.c
@@ -25,8 +25,6 @@
#include "log.h"
#include "sd-bus.h"
-#include "bus-message.h"
-#include "bus-error.h"
#include "bus-kernel.h"
#include "bus-util.h"
#include "bus-dump.h"
@@ -45,8 +43,6 @@ int main(int argc, char *argv[]) {
assert_se(asprintf(&name, "deine-mutter-%u", (unsigned) getpid()) >= 0);
- bus_kernel_fix_attach_mask();
-
bus_ref = bus_kernel_create_bus(name, false, &bus_name);
if (bus_ref == -ENOENT)
return EXIT_TEST_SKIP;
@@ -119,7 +115,7 @@ int main(int argc, char *argv[]) {
assert_se(r == -EBUSY);
r = sd_bus_process_priority(b, -10, &m);
- assert_se(r == -ENOMSG);
+ assert_se(r == 0);
r = sd_bus_process(b, &m);
assert_se(r > 0);
diff --git a/src/libsystemd/sd-bus/test-bus-marshal.c b/src/libsystemd/sd-bus/test-bus-marshal.c
index d95a03c221..f8ecadf499 100644
--- a/src/libsystemd/sd-bus/test-bus-marshal.c
+++ b/src/libsystemd/sd-bus/test-bus-marshal.c
@@ -19,9 +19,7 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <assert.h>
#include <stdlib.h>
-#include <byteswap.h>
#include <math.h>
#ifdef HAVE_GLIB
@@ -41,6 +39,16 @@
#include "bus-dump.h"
#include "bus-label.h"
+static void test_bus_path_encode_unique(void) {
+ _cleanup_free_ char *a = NULL, *b = NULL, *c = NULL, *d = NULL, *e = NULL;
+
+ assert_se(bus_path_encode_unique(NULL, "/foo/bar", "some.sender", "a.suffix", &a) >= 0 && streq_ptr(a, "/foo/bar/some_2esender/a_2esuffix"));
+ assert_se(bus_path_decode_unique(a, "/foo/bar", &b, &c) > 0 && streq_ptr(b, "some.sender") && streq_ptr(c, "a.suffix"));
+ assert_se(bus_path_decode_unique(a, "/bar/foo", &d, &d) == 0 && !d);
+ assert_se(bus_path_decode_unique("/foo/bar/onlyOneSuffix", "/foo/bar", &d, &d) == 0 && !d);
+ assert_se(bus_path_decode_unique("/foo/bar/_/_", "/foo/bar", &d, &e) > 0 && streq_ptr(d, "") && streq_ptr(e, ""));
+}
+
static void test_bus_path_encode(void) {
_cleanup_free_ char *a = NULL, *b = NULL, *c = NULL, *d = NULL, *e = NULL, *f = NULL;
@@ -359,6 +367,7 @@ int main(int argc, char *argv[]) {
test_bus_label_escape();
test_bus_path_encode();
+ test_bus_path_encode_unique();
return 0;
}
diff --git a/src/libsystemd/sd-bus/test-bus-match.c b/src/libsystemd/sd-bus/test-bus-match.c
index 76ca0b6bf3..40c67046da 100644
--- a/src/libsystemd/sd-bus/test-bus-match.c
+++ b/src/libsystemd/sd-bus/test-bus-match.c
@@ -20,7 +20,6 @@
***/
#include "log.h"
-#include "util.h"
#include "macro.h"
#include "bus-match.h"
@@ -30,7 +29,7 @@
static bool mask[32];
-static int filter(sd_bus *b, sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
+static int filter(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
log_info("Ran %u", PTR_TO_UINT(userdata));
assert_se(PTR_TO_UINT(userdata) < ELEMENTSOF(mask));
mask[PTR_TO_UINT(userdata)] = true;
diff --git a/src/libsystemd/sd-bus/test-bus-objects.c b/src/libsystemd/sd-bus/test-bus-objects.c
index acf67a52b3..52952603e4 100644
--- a/src/libsystemd/sd-bus/test-bus-objects.c
+++ b/src/libsystemd/sd-bus/test-bus-objects.c
@@ -19,11 +19,8 @@
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"
@@ -44,7 +41,7 @@ struct context {
uint32_t automatic_integer_property;
};
-static int something_handler(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
+static int something_handler(sd_bus_message *m, void *userdata, sd_bus_error *error) {
struct context *c = userdata;
const char *s;
char *n = NULL;
@@ -70,7 +67,7 @@ static int something_handler(sd_bus *bus, sd_bus_message *m, void *userdata, sd_
return 1;
}
-static int exit_handler(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
+static int exit_handler(sd_bus_message *m, void *userdata, sd_bus_error *error) {
struct context *c = userdata;
int r;
@@ -132,10 +129,10 @@ static int value_handler(sd_bus *bus, const char *path, const char *interface, c
return 1;
}
-static int notify_test(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
+static int notify_test(sd_bus_message *m, void *userdata, sd_bus_error *error) {
int r;
- assert_se(sd_bus_emit_properties_changed(bus, m->path, "org.freedesktop.systemd.ValueTest", "Value", NULL) >= 0);
+ assert_se(sd_bus_emit_properties_changed(sd_bus_message_get_bus(m), m->path, "org.freedesktop.systemd.ValueTest", "Value", NULL) >= 0);
r = sd_bus_reply_method_return(m, NULL);
assert_se(r >= 0);
@@ -143,10 +140,10 @@ static int notify_test(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_er
return 1;
}
-static int notify_test2(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
+static int notify_test2(sd_bus_message *m, void *userdata, sd_bus_error *error) {
int r;
- assert_se(sd_bus_emit_properties_changed_strv(bus, m->path, "org.freedesktop.systemd.ValueTest", NULL) >= 0);
+ assert_se(sd_bus_emit_properties_changed_strv(sd_bus_message_get_bus(m), m->path, "org.freedesktop.systemd.ValueTest", NULL) >= 0);
r = sd_bus_reply_method_return(m, NULL);
assert_se(r >= 0);
@@ -154,10 +151,10 @@ static int notify_test2(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_e
return 1;
}
-static int emit_interfaces_added(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
+static int emit_interfaces_added(sd_bus_message *m, void *userdata, sd_bus_error *error) {
int r;
- assert_se(sd_bus_emit_interfaces_added(bus, m->path, "org.freedesktop.systemd.test", NULL) >= 0);
+ assert_se(sd_bus_emit_interfaces_added(sd_bus_message_get_bus(m), m->path, "org.freedesktop.systemd.test", NULL) >= 0);
r = sd_bus_reply_method_return(m, NULL);
assert_se(r >= 0);
@@ -165,10 +162,10 @@ static int emit_interfaces_added(sd_bus *bus, sd_bus_message *m, void *userdata,
return 1;
}
-static int emit_interfaces_removed(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
+static int emit_interfaces_removed(sd_bus_message *m, void *userdata, sd_bus_error *error) {
int r;
- assert_se(sd_bus_emit_interfaces_removed(bus, m->path, "org.freedesktop.systemd.test", NULL) >= 0);
+ assert_se(sd_bus_emit_interfaces_removed(sd_bus_message_get_bus(m), m->path, "org.freedesktop.systemd.test", NULL) >= 0);
r = sd_bus_reply_method_return(m, NULL);
assert_se(r >= 0);
@@ -176,10 +173,10 @@ static int emit_interfaces_removed(sd_bus *bus, sd_bus_message *m, void *userdat
return 1;
}
-static int emit_object_added(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
+static int emit_object_added(sd_bus_message *m, void *userdata, sd_bus_error *error) {
int r;
- assert_se(sd_bus_emit_object_added(bus, m->path) >= 0);
+ assert_se(sd_bus_emit_object_added(sd_bus_message_get_bus(m), m->path) >= 0);
r = sd_bus_reply_method_return(m, NULL);
assert_se(r >= 0);
@@ -187,10 +184,10 @@ static int emit_object_added(sd_bus *bus, sd_bus_message *m, void *userdata, sd_
return 1;
}
-static int emit_object_removed(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
+static int emit_object_removed(sd_bus_message *m, void *userdata, sd_bus_error *error) {
int r;
- assert_se(sd_bus_emit_object_removed(bus, m->path) >= 0);
+ assert_se(sd_bus_emit_object_removed(sd_bus_message_get_bus(m), m->path) >= 0);
r = sd_bus_reply_method_return(m, NULL);
assert_se(r >= 0);
diff --git a/src/libsystemd/sd-bus/test-bus-server.c b/src/libsystemd/sd-bus/test-bus-server.c
index 5f807c3b1e..080d8eddb7 100644
--- a/src/libsystemd/sd-bus/test-bus-server.c
+++ b/src/libsystemd/sd-bus/test-bus-server.c
@@ -19,11 +19,8 @@
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"
@@ -31,7 +28,6 @@
#include "sd-bus.h"
#include "bus-internal.h"
-#include "bus-message.h"
#include "bus-util.h"
struct context {
diff --git a/src/libsystemd/sd-bus/test-bus-signature.c b/src/libsystemd/sd-bus/test-bus-signature.c
index 3fc565c620..4165c9273a 100644
--- a/src/libsystemd/sd-bus/test-bus-signature.c
+++ b/src/libsystemd/sd-bus/test-bus-signature.c
@@ -19,8 +19,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <assert.h>
-#include <stdlib.h>
#include "log.h"
#include "bus-signature.h"
diff --git a/src/libsystemd/sd-bus/test-bus-zero-copy.c b/src/libsystemd/sd-bus/test-bus-zero-copy.c
index a054f74bf3..2d062fc9b5 100644
--- a/src/libsystemd/sd-bus/test-bus-zero-copy.c
+++ b/src/libsystemd/sd-bus/test-bus-zero-copy.c
@@ -19,7 +19,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <fcntl.h>
#include <sys/mman.h>
#include "util.h"
@@ -28,7 +27,6 @@
#include "sd-bus.h"
#include "bus-message.h"
-#include "bus-error.h"
#include "bus-kernel.h"
#include "bus-dump.h"
@@ -39,6 +37,7 @@
int main(int argc, char *argv[]) {
_cleanup_free_ char *name = NULL, *bus_name = NULL, *address = NULL;
+ const char *unique;
uint8_t *p;
sd_bus *a, *b;
int r, bus_ref;
@@ -81,7 +80,10 @@ int main(int argc, char *argv[]) {
r = sd_bus_start(b);
assert_se(r >= 0);
- r = sd_bus_message_new_method_call(b, &m, ":1.1", "/a/path", "an.inter.face", "AMethod");
+ r = sd_bus_get_unique_name(a, &unique);
+ assert_se(r >= 0);
+
+ r = sd_bus_message_new_method_call(b, &m, unique, "/a/path", "an.inter.face", "AMethod");
assert_se(r >= 0);
r = sd_bus_message_open_container(m, 'r', "aysay");
diff --git a/src/libsystemd/sd-daemon/sd-daemon.c b/src/libsystemd/sd-daemon/sd-daemon.c
index 028c2a7a5b..82ac72c72a 100644
--- a/src/libsystemd/sd-daemon/sd-daemon.c
+++ b/src/libsystemd/sd-daemon/sd-daemon.c
@@ -19,11 +19,9 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/un.h>
-#include <fcntl.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <errno.h>
@@ -352,16 +350,10 @@ _public_ int sd_pid_notify_with_fds(pid_t pid, int unset_environment, const char
.msg_iovlen = 1,
.msg_name = &sockaddr,
};
- union {
- struct cmsghdr cmsghdr;
- uint8_t buf[CMSG_SPACE(sizeof(struct ucred)) +
- CMSG_SPACE(sizeof(int) * n_fds)];
- } control;
_cleanup_close_ int fd = -1;
struct cmsghdr *cmsg = NULL;
const char *e;
- size_t controllen_without_ucred = 0;
- bool try_without_ucred = false;
+ bool have_pid;
int r;
if (!state) {
@@ -400,40 +392,37 @@ _public_ int sd_pid_notify_with_fds(pid_t pid, int unset_environment, const char
if (msghdr.msg_namelen > sizeof(struct sockaddr_un))
msghdr.msg_namelen = sizeof(struct sockaddr_un);
- if (n_fds > 0) {
- msghdr.msg_control = &control;
- msghdr.msg_controllen = CMSG_LEN(sizeof(int) * n_fds);
+ have_pid = pid != 0 && pid != getpid();
- cmsg = CMSG_FIRSTHDR(&msghdr);
- cmsg->cmsg_level = SOL_SOCKET;
- cmsg->cmsg_type = SCM_RIGHTS;
- cmsg->cmsg_len = CMSG_LEN(sizeof(int) * n_fds);
-
- memcpy(CMSG_DATA(cmsg), fds, sizeof(int) * n_fds);
- }
+ if (n_fds > 0 || have_pid) {
+ msghdr.msg_controllen = CMSG_SPACE(sizeof(int) * n_fds) +
+ CMSG_SPACE(sizeof(struct ucred) * have_pid);
+ msghdr.msg_control = alloca(msghdr.msg_controllen);
- if (pid != 0 && pid != getpid()) {
- struct ucred *ucred;
+ cmsg = CMSG_FIRSTHDR(&msghdr);
+ if (n_fds > 0) {
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(int) * n_fds);
- try_without_ucred = true;
- controllen_without_ucred = msghdr.msg_controllen;
+ memcpy(CMSG_DATA(cmsg), fds, sizeof(int) * n_fds);
- msghdr.msg_control = &control;
- msghdr.msg_controllen += CMSG_LEN(sizeof(struct ucred));
+ if (have_pid)
+ assert_se(cmsg = CMSG_NXTHDR(&msghdr, cmsg));
+ }
- if (cmsg)
- cmsg = CMSG_NXTHDR(&msghdr, cmsg);
- else
- cmsg = CMSG_FIRSTHDR(&msghdr);
+ if (have_pid) {
+ struct ucred *ucred;
- cmsg->cmsg_level = SOL_SOCKET;
- cmsg->cmsg_type = SCM_CREDENTIALS;
- cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred));
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_CREDENTIALS;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred));
- ucred = (struct ucred*) CMSG_DATA(cmsg);
- ucred->pid = pid;
- ucred->uid = getuid();
- ucred->gid = getgid();
+ ucred = (struct ucred*) CMSG_DATA(cmsg);
+ ucred->pid = pid;
+ ucred->uid = getuid();
+ ucred->gid = getgid();
+ }
}
/* First try with fake ucred data, as requested */
@@ -443,10 +432,10 @@ _public_ int sd_pid_notify_with_fds(pid_t pid, int unset_environment, const char
}
/* If that failed, try with our own ucred instead */
- if (try_without_ucred) {
- if (controllen_without_ucred <= 0)
+ if (have_pid) {
+ msghdr.msg_controllen -= CMSG_SPACE(sizeof(struct ucred));
+ if (msghdr.msg_controllen == 0)
msghdr.msg_control = NULL;
- msghdr.msg_controllen = controllen_without_ucred;
if (sendmsg(fd, &msghdr, MSG_NOSIGNAL) >= 0) {
r = 1;
diff --git a/src/libsystemd/sd-device/device-enumerator-private.h b/src/libsystemd/sd-device/device-enumerator-private.h
new file mode 100644
index 0000000000..8d04640dc7
--- /dev/null
+++ b/src/libsystemd/sd-device/device-enumerator-private.h
@@ -0,0 +1,36 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2015 Tom Gundersen <teg@jklm.no>
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser 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-device.h"
+
+int device_enumerator_scan_devices(sd_device_enumerator *enumeartor);
+int device_enumerator_scan_subsystems(sd_device_enumerator *enumeartor);
+int device_enumerator_add_device(sd_device_enumerator *enumerator, sd_device *device);
+int device_enumerator_add_match_is_initialized(sd_device_enumerator *enumerator);
+sd_device *device_enumerator_get_first(sd_device_enumerator *enumerator);
+sd_device *device_enumerator_get_next(sd_device_enumerator *enumerator);
+
+#define FOREACH_DEVICE_AND_SUBSYSTEM(enumerator, device) \
+ for (device = device_enumerator_get_first(enumerator); \
+ device; \
+ device = device_enumerator_get_next(enumerator))
diff --git a/src/libsystemd/sd-device/device-enumerator.c b/src/libsystemd/sd-device/device-enumerator.c
new file mode 100644
index 0000000000..7fd77e9480
--- /dev/null
+++ b/src/libsystemd/sd-device/device-enumerator.c
@@ -0,0 +1,983 @@
+/***
+ This file is part of systemd.
+
+ Copyright 2008-2012 Kay Sievers <kay@vrfy.org>
+ Copyright 2014-2015 Tom Gundersen <teg@jklm.no>
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser 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"
+#include "strv.h"
+#include "set.h"
+
+#include "sd-device.h"
+
+#include "device-util.h"
+#include "device-enumerator-private.h"
+
+#define DEVICE_ENUMERATE_MAX_DEPTH 256
+
+typedef enum DeviceEnumerationType {
+ DEVICE_ENUMERATION_TYPE_DEVICES,
+ DEVICE_ENUMERATION_TYPE_SUBSYSTEMS,
+ _DEVICE_ENUMERATION_TYPE_MAX,
+ _DEVICE_ENUMERATION_TYPE_INVALID = -1,
+} DeviceEnumerationType;
+
+struct sd_device_enumerator {
+ unsigned n_ref;
+
+ DeviceEnumerationType type;
+ Prioq *devices;
+ bool scan_uptodate;
+
+ Set *match_subsystem;
+ Set *nomatch_subsystem;
+ Hashmap *match_sysattr;
+ Hashmap *nomatch_sysattr;
+ Hashmap *match_property;
+ Set *match_sysname;
+ Set *match_tag;
+ sd_device *match_parent;
+ bool match_allow_uninitialized;
+};
+
+_public_ int sd_device_enumerator_new(sd_device_enumerator **ret) {
+ _cleanup_device_enumerator_unref_ sd_device_enumerator *enumerator = NULL;
+
+ assert(ret);
+
+ enumerator = new0(sd_device_enumerator, 1);
+ if (!enumerator)
+ return -ENOMEM;
+
+ enumerator->n_ref = 1;
+ enumerator->type = _DEVICE_ENUMERATION_TYPE_INVALID;
+
+ *ret = enumerator;
+ enumerator = NULL;
+
+ return 0;
+}
+
+_public_ sd_device_enumerator *sd_device_enumerator_ref(sd_device_enumerator *enumerator) {
+ assert_return(enumerator, NULL);
+
+ assert_se((++ enumerator->n_ref) >= 2);
+
+ return enumerator;
+}
+
+_public_ sd_device_enumerator *sd_device_enumerator_unref(sd_device_enumerator *enumerator) {
+ if (enumerator && (-- enumerator->n_ref) == 0) {
+ sd_device *device;
+
+ while ((device = prioq_pop(enumerator->devices)))
+ sd_device_unref(device);
+
+ prioq_free(enumerator->devices);
+
+ set_free_free(enumerator->match_subsystem);
+ set_free_free(enumerator->nomatch_subsystem);
+ hashmap_free_free_free(enumerator->match_sysattr);
+ hashmap_free_free_free(enumerator->nomatch_sysattr);
+ hashmap_free_free_free(enumerator->match_property);
+ set_free_free(enumerator->match_sysname);
+ set_free_free(enumerator->match_tag);
+ sd_device_unref(enumerator->match_parent);
+
+ free(enumerator);
+ }
+
+ return NULL;
+}
+
+_public_ int sd_device_enumerator_add_match_subsystem(sd_device_enumerator *enumerator, const char *subsystem, int match) {
+ Set **set;
+ int r;
+
+ assert_return(enumerator, -EINVAL);
+ assert_return(subsystem, -EINVAL);
+
+ if (match)
+ set = &enumerator->match_subsystem;
+ else
+ set = &enumerator->nomatch_subsystem;
+
+ r = set_ensure_allocated(set, NULL);
+ if (r < 0)
+ return r;
+
+ r = set_put_strdup(*set, subsystem);
+ if (r < 0)
+ return r;
+
+ enumerator->scan_uptodate = false;
+
+ return 0;
+}
+
+_public_ int sd_device_enumerator_add_match_sysattr(sd_device_enumerator *enumerator, const char *_sysattr, const char *_value, int match) {
+ _cleanup_free_ char *sysattr = NULL, *value = NULL;
+ Hashmap **hashmap;
+ int r;
+
+ assert_return(enumerator, -EINVAL);
+ assert_return(_sysattr, -EINVAL);
+
+ if (match)
+ hashmap = &enumerator->match_sysattr;
+ else
+ hashmap = &enumerator->nomatch_sysattr;
+
+ r = hashmap_ensure_allocated(hashmap, NULL);
+ if (r < 0)
+ return r;
+
+ sysattr = strdup(_sysattr);
+ if (!sysattr)
+ return -ENOMEM;
+
+ if (_value) {
+ value = strdup(_value);
+ if (!value)
+ return -ENOMEM;
+ }
+
+ r = hashmap_put(*hashmap, sysattr, value);
+ if (r < 0)
+ return r;
+
+ sysattr = NULL;
+ value = NULL;
+
+ enumerator->scan_uptodate = false;
+
+ return 0;
+}
+
+_public_ int sd_device_enumerator_add_match_property(sd_device_enumerator *enumerator, const char *_property, const char *_value) {
+ _cleanup_free_ char *property = NULL, *value = NULL;
+ int r;
+
+ assert_return(enumerator, -EINVAL);
+ assert_return(_property, -EINVAL);
+
+ r = hashmap_ensure_allocated(&enumerator->match_property, NULL);
+ if (r < 0)
+ return r;
+
+ property = strdup(_property);
+ if (!property)
+ return -ENOMEM;
+
+ if (_value) {
+ value = strdup(_value);
+ if (!value)
+ return -ENOMEM;
+ }
+
+ r = hashmap_put(enumerator->match_property, property, value);
+ if (r < 0)
+ return r;
+
+ property = NULL;
+ value = NULL;
+
+ enumerator->scan_uptodate = false;
+
+ return 0;
+}
+
+_public_ int sd_device_enumerator_add_match_sysname(sd_device_enumerator *enumerator, const char *sysname) {
+ int r;
+
+ assert_return(enumerator, -EINVAL);
+ assert_return(sysname, -EINVAL);
+
+ r = set_ensure_allocated(&enumerator->match_sysname, NULL);
+ if (r < 0)
+ return r;
+
+ r = set_put_strdup(enumerator->match_sysname, sysname);
+ if (r < 0)
+ return r;
+
+ enumerator->scan_uptodate = false;
+
+ return 0;
+}
+
+_public_ int sd_device_enumerator_add_match_tag(sd_device_enumerator *enumerator, const char *tag) {
+ int r;
+
+ assert_return(enumerator, -EINVAL);
+ assert_return(tag, -EINVAL);
+
+ r = set_ensure_allocated(&enumerator->match_tag, NULL);
+ if (r < 0)
+ return r;
+
+ r = set_put_strdup(enumerator->match_tag, tag);
+ if (r < 0)
+ return r;
+
+ enumerator->scan_uptodate = false;
+
+ return 0;
+}
+
+_public_ int sd_device_enumerator_add_match_parent(sd_device_enumerator *enumerator, sd_device *parent) {
+ assert_return(enumerator, -EINVAL);
+ assert_return(parent, -EINVAL);
+
+ sd_device_unref(enumerator->match_parent);
+ enumerator->match_parent = sd_device_ref(parent);
+
+ enumerator->scan_uptodate = false;
+
+ return 0;
+}
+
+_public_ int sd_device_enumerator_allow_uninitialized(sd_device_enumerator *enumerator) {
+ assert_return(enumerator, -EINVAL);
+
+ enumerator->match_allow_uninitialized = true;
+
+ enumerator->scan_uptodate = false;
+
+ return 0;
+}
+
+int device_enumerator_add_match_is_initialized(sd_device_enumerator *enumerator) {
+ assert_return(enumerator, -EINVAL);
+
+ enumerator->match_allow_uninitialized = false;
+
+ enumerator->scan_uptodate = false;
+
+ return 0;
+}
+
+static int device_compare(const void *_a, const void *_b) {
+ sd_device *a = (sd_device *)_a, *b = (sd_device *)_b;
+ const char *devpath_a, *devpath_b, *sound_a;
+ bool delay_a, delay_b;
+
+ assert_se(sd_device_get_devpath(a, &devpath_a) >= 0);
+ assert_se(sd_device_get_devpath(b, &devpath_b) >= 0);
+
+ sound_a = strstr(devpath_a, "/sound/card");
+ if (sound_a) {
+ /* For sound cards the control device must be enumerated last to
+ * make sure it's the final device node that gets ACLs applied.
+ * Applications rely on this fact and use ACL changes on the
+ * control node as an indicator that the ACL change of the
+ * entire sound card completed. The kernel makes this guarantee
+ * when creating those devices, and hence we should too when
+ * enumerating them. */
+ sound_a += strlen("/sound/card");
+ sound_a = strchr(sound_a, '/');
+
+ if (sound_a) {
+ unsigned prefix_len;
+
+ prefix_len = sound_a - devpath_a;
+
+ if (strncmp(devpath_a, devpath_b, prefix_len) == 0) {
+ const char *sound_b;
+
+ sound_b = devpath_b + prefix_len;
+
+ if (startswith(sound_a, "/controlC") &&
+ !startswith(sound_b, "/contolC"))
+ return 1;
+
+ if (!startswith(sound_a, "/controlC") &&
+ startswith(sound_b, "/controlC"))
+ return -1;
+ }
+ }
+ }
+
+ /* md and dm devices are enumerated after all other devices */
+ delay_a = strstr(devpath_a, "/block/md") || strstr(devpath_a, "/block/dm-");
+ delay_b = strstr(devpath_b, "/block/md") || strstr(devpath_b, "/block/dm-");
+ if (delay_a != delay_b)
+ return delay_a - delay_b;
+
+ return strcmp(devpath_a, devpath_b);
+}
+
+int device_enumerator_add_device(sd_device_enumerator *enumerator, sd_device *device) {
+ int r;
+
+ assert_return(enumerator, -EINVAL);
+ assert_return(device, -EINVAL);
+
+ r = prioq_ensure_allocated(&enumerator->devices, device_compare);
+ if (r < 0)
+ return r;
+
+ r = prioq_put(enumerator->devices, device, NULL);
+ if (r < 0)
+ return r;
+
+ sd_device_ref(device);
+
+ return 0;
+}
+
+static bool match_sysattr_value(sd_device *device, const char *sysattr, const char *match_value) {
+ const char *value;
+ int r;
+
+ assert(device);
+ assert(sysattr);
+
+ r = sd_device_get_sysattr_value(device, sysattr, &value);
+ if (r < 0)
+ return false;
+
+ if (!match_value)
+ return true;
+
+ if (fnmatch(match_value, value, 0) == 0)
+ return true;
+
+ return false;
+}
+
+static bool match_sysattr(sd_device_enumerator *enumerator, sd_device *device) {
+ const char *sysattr;
+ const char *value;
+ Iterator i;
+
+ assert(enumerator);
+ assert(device);
+
+ HASHMAP_FOREACH_KEY(value, sysattr, enumerator->nomatch_sysattr, i)
+ if (match_sysattr_value(device, sysattr, value))
+ return false;
+
+ HASHMAP_FOREACH_KEY(value, sysattr, enumerator->match_sysattr, i)
+ if (!match_sysattr_value(device, sysattr, value))
+ return false;
+
+ return true;
+}
+
+static bool match_property(sd_device_enumerator *enumerator, sd_device *device) {
+ const char *property;
+ const char *value;
+ Iterator i;
+
+ assert(enumerator);
+ assert(device);
+
+ if (hashmap_isempty(enumerator->match_property))
+ return true;
+
+ HASHMAP_FOREACH_KEY(value, property, enumerator->match_property, i) {
+ const char *property_dev, *value_dev;
+
+ FOREACH_DEVICE_PROPERTY(device, property_dev, value_dev) {
+ if (fnmatch(property, property_dev, 0) != 0)
+ continue;
+
+ if (!value && !value_dev)
+ return true;
+
+ if (!value || !value_dev)
+ continue;
+
+ if (fnmatch(value, value_dev, 0) == 0)
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static bool match_tag(sd_device_enumerator *enumerator, sd_device *device) {
+ const char *tag;
+ Iterator i;
+
+ assert(enumerator);
+ assert(device);
+
+ SET_FOREACH(tag, enumerator->match_tag, i)
+ if (!sd_device_has_tag(device, tag))
+ return false;
+
+ return true;
+}
+
+static bool match_parent(sd_device_enumerator *enumerator, sd_device *device) {
+ const char *devpath, *devpath_dev;
+ int r;
+
+ assert(enumerator);
+ assert(device);
+
+ if (!enumerator->match_parent)
+ return true;
+
+ r = sd_device_get_devpath(enumerator->match_parent, &devpath);
+ assert(r >= 0);
+
+ r = sd_device_get_devpath(device, &devpath_dev);
+ assert(r >= 0);
+
+ return startswith(devpath_dev, devpath);
+}
+
+static bool match_sysname(sd_device_enumerator *enumerator, const char *sysname) {
+ const char *sysname_match;
+ Iterator i;
+
+ assert(enumerator);
+ assert(sysname);
+
+ if (set_isempty(enumerator->match_sysname))
+ return true;
+
+ SET_FOREACH(sysname_match, enumerator->match_sysname, i)
+ if (fnmatch(sysname_match, sysname, 0) == 0)
+ return true;
+
+ return false;
+}
+
+static int enumerator_scan_dir_and_add_devices(sd_device_enumerator *enumerator, const char *basedir, const char *subdir1, const char *subdir2) {
+ _cleanup_closedir_ DIR *dir = NULL;
+ char *path;
+ struct dirent *dent;
+ int r = 0;
+
+ assert(enumerator);
+ assert(basedir);
+
+ path = strjoina("/sys/", basedir, "/");
+
+ if (subdir1)
+ path = strjoina(path, subdir1, "/");
+
+ if (subdir2)
+ path = strjoina(path, subdir2, "/");
+
+ dir = opendir(path);
+ if (!dir)
+ return -errno;
+
+ FOREACH_DIRENT_ALL(dent, dir, return -errno) {
+ _cleanup_device_unref_ sd_device *device = NULL;
+ char syspath[strlen(path) + 1 + strlen(dent->d_name) + 1];
+ dev_t devnum;
+ int ifindex, initialized, k;
+
+ if (dent->d_name[0] == '.')
+ continue;
+
+ if (!match_sysname(enumerator, dent->d_name))
+ continue;
+
+ (void)sprintf(syspath, "%s%s", path, dent->d_name);
+
+ k = sd_device_new_from_syspath(&device, syspath);
+ if (k < 0) {
+ if (k != -ENODEV)
+ /* this is necessarily racey, so ignore missing devices */
+ r = k;
+
+ continue;
+ }
+
+ k = sd_device_get_devnum(device, &devnum);
+ if (k < 0) {
+ r = k;
+ continue;
+ }
+
+ k = sd_device_get_ifindex(device, &ifindex);
+ if (k < 0) {
+ r = k;
+ continue;
+ }
+
+ k = sd_device_get_is_initialized(device, &initialized);
+ if (k < 0) {
+ r = k;
+ continue;
+ }
+
+ /*
+ * All devices with a device node or network interfaces
+ * possibly need udev to adjust the device node permission
+ * or context, or rename the interface before it can be
+ * reliably used from other processes.
+ *
+ * For now, we can only check these types of devices, we
+ * might not store a database, and have no way to find out
+ * for all other types of devices.
+ */
+ if (!enumerator->match_allow_uninitialized &&
+ !initialized &&
+ (major(devnum) > 0 || ifindex > 0))
+ continue;
+
+ if (!match_parent(enumerator, device))
+ continue;
+
+ if (!match_tag(enumerator, device))
+ continue;
+
+ if (!match_property(enumerator, device))
+ continue;
+
+ if (!match_sysattr(enumerator, device))
+ continue;
+
+ k = device_enumerator_add_device(enumerator, device);
+ if (k < 0)
+ r = k;
+ }
+
+ return r;
+}
+
+static bool match_subsystem(sd_device_enumerator *enumerator, const char *subsystem) {
+ const char *subsystem_match;
+ Iterator i;
+
+ assert(enumerator);
+
+ if (!subsystem)
+ return false;
+
+ SET_FOREACH(subsystem_match, enumerator->nomatch_subsystem, i)
+ if (fnmatch(subsystem_match, subsystem, 0) == 0)
+ return false;
+
+ if (set_isempty(enumerator->match_subsystem))
+ return true;
+
+ SET_FOREACH(subsystem_match, enumerator->match_subsystem, i)
+ if (fnmatch(subsystem_match, subsystem, 0) == 0)
+ return true;
+
+ return false;
+}
+
+static int enumerator_scan_dir(sd_device_enumerator *enumerator, const char *basedir, const char *subdir, const char *subsystem) {
+ _cleanup_closedir_ DIR *dir = NULL;
+ char *path;
+ struct dirent *dent;
+ int r = 0;
+
+ path = strjoina("/sys/", basedir);
+
+ dir = opendir(path);
+ if (!dir)
+ return -errno;
+
+ log_debug(" device-enumerator: scanning %s", path);
+
+ FOREACH_DIRENT_ALL(dent, dir, return -errno) {
+ int k;
+
+ if (dent->d_name[0] == '.')
+ continue;
+
+ if (!match_subsystem(enumerator, subsystem ? : dent->d_name))
+ continue;
+
+ k = enumerator_scan_dir_and_add_devices(enumerator, basedir, dent->d_name, subdir);
+ if (k < 0)
+ r = k;
+ }
+
+ return r;
+}
+
+static int enumerator_scan_devices_tag(sd_device_enumerator *enumerator, const char *tag) {
+ _cleanup_closedir_ DIR *dir = NULL;
+ char *path;
+ struct dirent *dent;
+ int r = 0;
+
+ assert(enumerator);
+ assert(tag);
+
+ path = strjoina("/run/udev/tags/", tag);
+
+ dir = opendir(path);
+ if (!dir) {
+ if (errno == ENOENT)
+ return 0;
+ else {
+ log_error("sd-device-enumerator: could not open tags directory %s: %m", path);
+ return -errno;
+ }
+ }
+
+ /* TODO: filter away subsystems? */
+
+ FOREACH_DIRENT_ALL(dent, dir, return -errno) {
+ _cleanup_device_unref_ sd_device *device = NULL;
+ const char *subsystem, *sysname;
+ int k;
+
+ if (dent->d_name[0] == '.')
+ continue;
+
+ k = sd_device_new_from_device_id(&device, dent->d_name);
+ if (k < 0) {
+ if (k != -ENODEV)
+ /* this is necessarily racy, so ignore missing devices */
+ r = k;
+
+ continue;
+ }
+
+ k = sd_device_get_subsystem(device, &subsystem);
+ if (k < 0) {
+ r = k;
+ continue;
+ }
+
+ if (!match_subsystem(enumerator, subsystem))
+ continue;
+
+ k = sd_device_get_sysname(device, &sysname);
+ if (k < 0) {
+ r = k;
+ continue;
+ }
+
+ if (!match_sysname(enumerator, sysname))
+ continue;
+
+ if (!match_parent(enumerator, device))
+ continue;
+
+ if (!match_property(enumerator, device))
+ continue;
+
+ if (!match_sysattr(enumerator, device))
+ continue;
+
+ k = device_enumerator_add_device(enumerator, device);
+ if (k < 0) {
+ r = k;
+ continue;
+ }
+ }
+
+ return r;
+}
+
+static int enumerator_scan_devices_tags(sd_device_enumerator *enumerator) {
+ const char *tag;
+ Iterator i;
+ int r;
+
+ assert(enumerator);
+
+ SET_FOREACH(tag, enumerator->match_tag, i) {
+ r = enumerator_scan_devices_tag(enumerator, tag);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+static int parent_add_child(sd_device_enumerator *enumerator, const char *path) {
+ _cleanup_device_unref_ sd_device *device = NULL;
+ const char *subsystem, *sysname;
+ int r;
+
+ r = sd_device_new_from_syspath(&device, path);
+ if (r == -ENODEV)
+ /* this is necessarily racy, so ignore missing devices */
+ return 0;
+ else if (r < 0)
+ return r;
+
+ r = sd_device_get_subsystem(device, &subsystem);
+ if (r < 0)
+ return r;
+
+ if (!match_subsystem(enumerator, subsystem))
+ return 0;
+
+ r = sd_device_get_sysname(device, &sysname);
+ if (r < 0)
+ return r;
+
+ if (!match_sysname(enumerator, sysname))
+ return 0;
+
+ if (!match_property(enumerator, device))
+ return 0;
+
+ if (!match_sysattr(enumerator, device))
+ return 0;
+
+ r = device_enumerator_add_device(enumerator, device);
+ if (r < 0)
+ return r;
+
+ return 1;
+}
+
+static int parent_crawl_children(sd_device_enumerator *enumerator, const char *path, unsigned maxdepth) {
+ _cleanup_closedir_ DIR *dir = NULL;
+ struct dirent *dent;
+ int r = 0;
+
+ dir = opendir(path);
+ if (!dir) {
+ log_debug("sd-device-enumerate: could not open parent directory %s: %m", path);
+ return -errno;
+ }
+
+ FOREACH_DIRENT_ALL(dent, dir, return -errno) {
+ _cleanup_free_ char *child = NULL;
+ int k;
+
+ if (dent->d_name[0] == '.')
+ continue;
+
+ if (dent->d_type != DT_DIR)
+ continue;
+
+ child = strjoin(path, "/", dent->d_name, NULL);
+ if (!child)
+ return -ENOMEM;
+
+ k = parent_add_child(enumerator, child);
+ if (k < 0)
+ r = k;
+
+ if (maxdepth > 0)
+ parent_crawl_children(enumerator, child, maxdepth - 1);
+ else
+ log_debug("device-enumerate: max depth reached, %s: ignoring devices", child);
+ }
+
+ return r;
+}
+
+static int enumerator_scan_devices_children(sd_device_enumerator *enumerator) {
+ const char *path;
+ int r = 0, k;
+
+ r = sd_device_get_syspath(enumerator->match_parent, &path);
+ if (r < 0)
+ return r;
+
+ k = parent_add_child(enumerator, path);
+ if (k < 0)
+ r = k;
+
+ k = parent_crawl_children(enumerator, path, DEVICE_ENUMERATE_MAX_DEPTH);
+ if (k < 0)
+ r = k;
+
+ return r;
+}
+
+static int enumerator_scan_devices_all(sd_device_enumerator *enumerator) {
+ int r = 0;
+
+ log_debug("device-enumerator: scan all dirs");
+
+ if (access("/sys/subsystem", F_OK) >= 0) {
+ /* we have /subsystem/, forget all the old stuff */
+ r = enumerator_scan_dir(enumerator, "subsystem", "devices", NULL);
+ if (r < 0) {
+ log_debug("device-enumerator: failed to scan /sys/subsystem: %s", strerror(-r));
+ return r;
+ }
+ } else {
+ int k;
+
+ k = enumerator_scan_dir(enumerator, "bus", "devices", NULL);
+ if (k < 0) {
+ log_debug_errno(k, "device-enumerator: failed to scan /sys/bus: %m");
+ r = k;
+ }
+
+ k = enumerator_scan_dir(enumerator, "class", NULL, NULL);
+ if (k < 0) {
+ log_debug_errno(k, "device-enumerator: failed to scan /sys/class: %m");
+ r = k;
+ }
+ }
+
+ return r;
+}
+
+int device_enumerator_scan_devices(sd_device_enumerator *enumerator) {
+ sd_device *device;
+ int r;
+
+ assert(enumerator);
+
+ if (enumerator->scan_uptodate &&
+ enumerator->type == DEVICE_ENUMERATION_TYPE_DEVICES)
+ return 0;
+
+ while ((device = prioq_pop(enumerator->devices)))
+ sd_device_unref(device);
+
+ if (!set_isempty(enumerator->match_tag)) {
+ r = enumerator_scan_devices_tags(enumerator);
+ if (r < 0)
+ return r;
+ } else if (enumerator->match_parent) {
+ r = enumerator_scan_devices_children(enumerator);
+ if (r < 0)
+ return r;
+ } else {
+ r = enumerator_scan_devices_all(enumerator);
+ if (r < 0)
+ return r;
+ }
+
+ enumerator->scan_uptodate = true;
+
+ return 0;
+}
+
+_public_ sd_device *sd_device_enumerator_get_device_first(sd_device_enumerator *enumerator) {
+ int r;
+
+ assert_return(enumerator, NULL);
+
+ r = device_enumerator_scan_devices(enumerator);
+ if (r < 0)
+ return NULL;
+
+ enumerator->type = DEVICE_ENUMERATION_TYPE_DEVICES;
+
+ return prioq_peek(enumerator->devices);
+}
+
+_public_ sd_device *sd_device_enumerator_get_device_next(sd_device_enumerator *enumerator) {
+ assert_return(enumerator, NULL);
+
+ if (!enumerator->scan_uptodate ||
+ enumerator->type != DEVICE_ENUMERATION_TYPE_DEVICES)
+ return NULL;
+
+ sd_device_unref(prioq_pop(enumerator->devices));
+
+ return prioq_peek(enumerator->devices);
+}
+
+int device_enumerator_scan_subsystems(sd_device_enumerator *enumerator) {
+ sd_device *device;
+ const char *subsysdir;
+ int r = 0, k;
+
+ assert(enumerator);
+
+ if (enumerator->scan_uptodate &&
+ enumerator->type == DEVICE_ENUMERATION_TYPE_SUBSYSTEMS)
+ return 0;
+
+ while ((device = prioq_pop(enumerator->devices)))
+ sd_device_unref(device);
+
+ /* modules */
+ if (match_subsystem(enumerator, "module")) {
+ k = enumerator_scan_dir_and_add_devices(enumerator, "module", NULL, NULL);
+ if (k < 0) {
+ log_debug_errno(k, "device-enumerator: failed to scan modules: %m");
+ r = k;
+ }
+ }
+
+ if (access("/sys/subsystem", F_OK) >= 0)
+ subsysdir = "subsystem";
+ else
+ subsysdir = "bus";
+
+ /* subsystems (only buses support coldplug) */
+ if (match_subsystem(enumerator, "subsystem")) {
+ k = enumerator_scan_dir_and_add_devices(enumerator, subsysdir, NULL, NULL);
+ if (k < 0) {
+ log_debug_errno(k, "device-enumerator: failed to scan subsystems: %m");
+ r = k;
+ }
+ }
+
+ /* subsystem drivers */
+ if (match_subsystem(enumerator, "drivers")) {
+ k = enumerator_scan_dir(enumerator, subsysdir, "drivers", "drivers");
+ if (k < 0) {
+ log_debug_errno(k, "device-enumerator: failed to scan drivers: %m");
+ r = k;
+ }
+ }
+
+ enumerator->scan_uptodate = true;
+
+ return r;
+}
+
+_public_ sd_device *sd_device_enumerator_get_subsystem_first(sd_device_enumerator *enumerator) {
+ int r;
+
+ assert_return(enumerator, NULL);
+
+ r = device_enumerator_scan_subsystems(enumerator);
+ if (r < 0)
+ return NULL;
+
+ enumerator->type = DEVICE_ENUMERATION_TYPE_SUBSYSTEMS;
+
+ return prioq_peek(enumerator->devices);
+}
+
+_public_ sd_device *sd_device_enumerator_get_subsystem_next(sd_device_enumerator *enumerator) {
+ assert_return(enumerator, NULL);
+
+ if (enumerator->scan_uptodate ||
+ enumerator->type != DEVICE_ENUMERATION_TYPE_SUBSYSTEMS)
+ return NULL;
+
+ sd_device_unref(prioq_pop(enumerator->devices));
+
+ return prioq_peek(enumerator->devices);
+}
+
+sd_device *device_enumerator_get_first(sd_device_enumerator *enumerator) {
+ assert_return(enumerator, NULL);
+
+ return prioq_peek(enumerator->devices);
+}
+
+sd_device *device_enumerator_get_next(sd_device_enumerator *enumerator) {
+ assert_return(enumerator, NULL);
+
+ sd_device_unref(prioq_pop(enumerator->devices));
+
+ return prioq_peek(enumerator->devices);
+}
diff --git a/src/libsystemd/sd-device/device-internal.h b/src/libsystemd/sd-device/device-internal.h
new file mode 100644
index 0000000000..b96441de56
--- /dev/null
+++ b/src/libsystemd/sd-device/device-internal.h
@@ -0,0 +1,126 @@
+/***
+ This file is part of systemd.
+
+ Copyright 2008-2012 Kay Sievers <kay@vrfy.org>
+ Copyright 2014 Tom Gundersen <teg@jklm.no>
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser 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 "hashmap.h"
+#include "set.h"
+
+struct sd_device {
+ uint64_t n_ref;
+
+ sd_device *parent;
+ bool parent_set; /* no need to try to reload parent */
+
+ OrderedHashmap *properties;
+ Iterator properties_iterator;
+ uint64_t properties_generation; /* changes whenever the properties are changed */
+ uint64_t properties_iterator_generation; /* generation when iteration was started */
+
+ /* the subset of the properties that should be written to the db*/
+ OrderedHashmap *properties_db;
+
+ Hashmap *sysattr_values; /* cached sysattr values */
+
+ Set *sysattrs; /* names of sysattrs */
+ Iterator sysattrs_iterator;
+ bool sysattrs_read; /* don't try to re-read sysattrs once read */
+
+ Set *tags;
+ Iterator tags_iterator;
+ uint64_t tags_generation; /* changes whenever the tags are changed */
+ uint64_t tags_iterator_generation; /* generation when iteration was started */
+ bool property_tags_outdated; /* need to update TAGS= property */
+
+ Set *devlinks;
+ Iterator devlinks_iterator;
+ uint64_t devlinks_generation; /* changes whenever the devlinks are changed */
+ uint64_t devlinks_iterator_generation; /* generation when iteration was started */
+ bool property_devlinks_outdated; /* need to update DEVLINKS= property */
+ int devlink_priority;
+
+ char **properties_strv; /* the properties hashmap as a strv */
+ uint8_t *properties_nulstr; /* the same as a nulstr */
+ size_t properties_nulstr_len;
+ bool properties_buf_outdated; /* need to reread hashmap */
+
+ int watch_handle;
+
+ char *syspath;
+ const char *devpath;
+ const char *sysnum;
+ char *sysname;
+ bool sysname_set; /* don't reread sysname */
+
+ char *devtype;
+ int ifindex;
+ char *devname;
+ dev_t devnum;
+
+ char *subsystem;
+ bool subsystem_set; /* don't reread subsystem */
+ char *driver;
+ bool driver_set; /* don't reread driver */
+
+ char *id_filename;
+
+ bool is_initialized;
+ uint64_t usec_initialized;
+
+ mode_t devmode;
+ uid_t devuid;
+ gid_t devgid;
+
+ bool uevent_loaded; /* don't reread uevent */
+ bool db_loaded; /* don't reread db */
+
+ bool sealed; /* don't read more information from uevent/db */
+ bool db_persist; /* don't clean up the db when switching from initrd to real root */
+};
+
+typedef enum DeviceAction {
+ DEVICE_ACTION_ADD,
+ DEVICE_ACTION_REMOVE,
+ DEVICE_ACTION_CHANGE,
+ DEVICE_ACTION_MOVE,
+ DEVICE_ACTION_ONLINE,
+ DEVICE_ACTION_OFFLINE,
+ _DEVICE_ACTION_MAX,
+ _DEVICE_ACTION_INVALID = -1,
+} DeviceAction;
+
+int device_new_aux(sd_device **ret);
+int device_add_property_aux(sd_device *device, const char *key, const char *value, bool db);
+int device_add_property_internal(sd_device *device, const char *key, const char *value);
+int device_read_uevent_file(sd_device *device);
+int device_read_db_aux(sd_device *device, bool force);
+
+int device_set_syspath(sd_device *device, const char *_syspath, bool verify);
+int device_set_ifindex(sd_device *device, const char *ifindex);
+int device_set_devmode(sd_device *device, const char *devmode);
+int device_set_devname(sd_device *device, const char *_devname);
+int device_set_devtype(sd_device *device, const char *_devtype);
+int device_set_devnum(sd_device *device, const char *major, const char *minor);
+int device_set_subsystem(sd_device *device, const char *_subsystem);
+int device_set_driver(sd_device *device, const char *_driver);
+int device_set_usec_initialized(sd_device *device, const char *initialized);
+
+DeviceAction device_action_from_string(const char *s) _pure_;
+const char *device_action_to_string(DeviceAction a) _const_;
diff --git a/src/libsystemd/sd-device/device-private.c b/src/libsystemd/sd-device/device-private.c
new file mode 100644
index 0000000000..2e60433246
--- /dev/null
+++ b/src/libsystemd/sd-device/device-private.c
@@ -0,0 +1,1117 @@
+/***
+ This file is part of systemd.
+
+ Copyright 2008-2012 Kay Sievers <kay@vrfy.org>
+ Copyright 2014 Tom Gundersen <teg@jklm.no>
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser 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 <ctype.h>
+#include <sys/types.h>
+#include <net/if.h>
+
+#include "util.h"
+#include "macro.h"
+#include "refcnt.h"
+#include "path-util.h"
+#include "strxcpyx.h"
+#include "fileio.h"
+#include "hashmap.h"
+#include "set.h"
+#include "strv.h"
+#include "mkdir.h"
+
+#include "sd-device.h"
+
+#include "device-util.h"
+#include "device-internal.h"
+#include "device-private.h"
+
+int device_add_property(sd_device *device, const char *key, const char *value) {
+ int r;
+
+ assert(device);
+ assert(key);
+
+ r = device_add_property_aux(device, key, value, false);
+ if (r < 0)
+ return r;
+
+ if (key[0] != '.') {
+ r = device_add_property_aux(device, key, value, true);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+static int device_add_property_internal_from_string(sd_device *device, const char *str) {
+ _cleanup_free_ char *key = NULL;
+ char *value;
+
+ assert(device);
+ assert(str);
+
+ key = strdup(str);
+ if (!key)
+ return -ENOMEM;
+
+ value = strchr(key, '=');
+ if (!value)
+ return -EINVAL;
+
+ *value = '\0';
+
+ if (isempty(++value))
+ value = NULL;
+
+ return device_add_property_internal(device, key, value);
+}
+
+static int handle_db_line(sd_device *device, char key, const char *value) {
+ char *path;
+ int r;
+
+ assert(device);
+ assert(value);
+
+ switch (key) {
+ case 'S':
+ path = strjoina("/dev/", value);
+ r = device_add_devlink(device, path);
+ if (r < 0)
+ return r;
+
+ break;
+ case 'L':
+ r = safe_atoi(value, &device->devlink_priority);
+ if (r < 0)
+ return r;
+
+ break;
+ case 'E':
+ r = device_add_property_internal_from_string(device, value);
+ if (r < 0)
+ return r;
+
+ break;
+ case 'G':
+ r = device_add_tag(device, value);
+ if (r < 0)
+ return r;
+
+ break;
+ case 'W':
+ r = safe_atoi(value, &device->watch_handle);
+ if (r < 0)
+ return r;
+
+ break;
+ case 'I':
+ r = device_set_usec_initialized(device, value);
+ if (r < 0)
+ return r;
+
+ break;
+ default:
+ log_debug("device db: unknown key '%c'", key);
+ }
+
+ return 0;
+}
+
+void device_set_devlink_priority(sd_device *device, int priority) {
+ assert(device);
+
+ device->devlink_priority = priority;
+}
+
+void device_set_is_initialized(sd_device *device) {
+ assert(device);
+
+ device->is_initialized = true;
+}
+
+int device_ensure_usec_initialized(sd_device *device, sd_device *device_old) {
+ char num[DECIMAL_STR_MAX(usec_t)];
+ usec_t usec_initialized;
+ int r;
+
+ assert(device);
+
+ if (device_old && device_old->usec_initialized > 0)
+ usec_initialized = device_old->usec_initialized;
+ else
+ usec_initialized = now(CLOCK_MONOTONIC);
+
+ r = snprintf(num, sizeof(num), USEC_FMT, usec_initialized);
+ if (r < 0)
+ return -errno;
+
+ r = device_set_usec_initialized(device, num);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+static int device_read_db(sd_device *device) {
+ _cleanup_free_ char *db = NULL;
+ char *path;
+ const char *id, *value;
+ char key;
+ size_t db_len;
+ unsigned i;
+ int r;
+
+ enum {
+ PRE_KEY,
+ KEY,
+ PRE_VALUE,
+ VALUE,
+ INVALID_LINE,
+ } state = PRE_KEY;
+
+ assert(device);
+
+ if (device->db_loaded || device->sealed)
+ return 0;
+
+ r = device_get_id_filename(device, &id);
+ if (r < 0)
+ return r;
+
+ path = strjoina("/run/udev/data/", id);
+
+ r = read_full_file(path, &db, &db_len);
+ if (r < 0) {
+ if (r == -ENOENT)
+ return 0;
+ else {
+ log_debug("sd-device: failed to read db '%s': %s", path, strerror(-r));
+ return r;
+ }
+ }
+
+ /* devices with a database entry are initialized */
+ device_set_is_initialized(device);
+
+ for (i = 0; i < db_len; i++) {
+ switch (state) {
+ case PRE_KEY:
+ if (!strchr(NEWLINE, db[i])) {
+ key = db[i];
+
+ state = KEY;
+ }
+
+ break;
+ case KEY:
+ if (db[i] != ':') {
+ log_debug("sd-device: ignoring invalid db entry with key '%c'", key);
+
+ state = INVALID_LINE;
+ } else {
+ db[i] = '\0';
+
+ state = PRE_VALUE;
+ }
+
+ break;
+ case PRE_VALUE:
+ value = &db[i];
+
+ state = VALUE;
+
+ break;
+ case INVALID_LINE:
+ if (strchr(NEWLINE, db[i]))
+ state = PRE_KEY;
+
+ break;
+ case VALUE:
+ if (strchr(NEWLINE, db[i])) {
+ db[i] = '\0';
+ r = handle_db_line(device, key, value);
+ if (r < 0)
+ log_debug("sd-device: failed to handle db entry '%c:%s': %s", key, value, strerror(-r));
+
+ state = PRE_KEY;
+ }
+
+ break;
+ default:
+ assert_not_reached("invalid state when parsing db");
+ }
+ }
+
+ device->db_loaded = true;
+
+ return 0;
+}
+
+uint64_t device_get_properties_generation(sd_device *device) {
+ assert(device);
+
+ return device->properties_generation;
+}
+
+uint64_t device_get_tags_generation(sd_device *device) {
+ assert(device);
+
+ return device->tags_generation;
+}
+
+uint64_t device_get_devlinks_generation(sd_device *device) {
+ assert(device);
+
+ return device->devlinks_generation;
+}
+
+int device_get_devnode_mode(sd_device *device, mode_t *mode) {
+ int r;
+
+ assert(device);
+ assert(mode);
+
+ r = device_read_db(device);
+ if (r < 0)
+ return r;
+
+ *mode = device->devmode;
+
+ return 0;
+}
+
+int device_get_devnode_uid(sd_device *device, uid_t *uid) {
+ int r;
+
+ assert(device);
+ assert(uid);
+
+ r = device_read_db(device);
+ if (r < 0)
+ return r;
+
+ *uid = device->devuid;
+
+ return 0;
+}
+
+static int device_set_devuid(sd_device *device, const char *uid) {
+ unsigned u;
+ int r;
+
+ assert(device);
+ assert(uid);
+
+ r = safe_atou(uid, &u);
+ if (r < 0)
+ return r;
+
+ r = device_add_property_internal(device, "DEVUID", uid);
+ if (r < 0)
+ return r;
+
+ device->devuid = u;
+
+ return 0;
+}
+
+int device_get_devnode_gid(sd_device *device, gid_t *gid) {
+ int r;
+
+ assert(device);
+ assert(gid);
+
+ r = device_read_db(device);
+ if (r < 0)
+ return r;
+
+ *gid = device->devgid;
+
+ return 0;
+}
+
+static int device_set_devgid(sd_device *device, const char *gid) {
+ unsigned g;
+ int r;
+
+ assert(device);
+ assert(gid);
+
+ r = safe_atou(gid, &g);
+ if (r < 0)
+ return r;
+
+ r = device_add_property_internal(device, "DEVGID", gid);
+ if (r < 0)
+ return r;
+
+ device->devgid = g;
+
+ return 0;
+}
+
+static int device_amend(sd_device *device, const char *key, const char *value) {
+ int r;
+
+ assert(device);
+ assert(key);
+ assert(value);
+
+ if (streq(key, "DEVPATH")) {
+ char *path;
+
+ path = strjoina("/sys", value);
+
+ /* the caller must verify or trust this data (e.g., if it comes from the kernel) */
+ r = device_set_syspath(device, path, false);
+ if (r < 0)
+ return log_debug_errno(r, "sd-device: could not set syspath to '%s': %m", path);
+ } else if (streq(key, "SUBSYSTEM")) {
+ r = device_set_subsystem(device, value);
+ if (r < 0)
+ return log_debug_errno(r, "sd-device: could not set subsystem to '%s': %m", value);
+ } else if (streq(key, "DEVTYPE")) {
+ r = device_set_devtype(device, value);
+ if (r < 0)
+ return log_debug_errno(r, "sd-device: could not set devtype to '%s': %m", value);
+ } else if (streq(key, "DEVNAME")) {
+ r = device_set_devname(device, value);
+ if (r < 0)
+ return log_debug_errno(r, "sd-device: could not set devname to '%s': %m", value);
+ } else if (streq(key, "USEC_INITIALIZED")) {
+ r = device_set_usec_initialized(device, value);
+ if (r < 0)
+ return log_debug_errno(r, "sd-device: could not set usec-initialized to '%s': %m", value);
+ } else if (streq(key, "DRIVER")) {
+ r = device_set_driver(device, value);
+ if (r < 0)
+ return log_debug_errno(r, "sd-device: could not set driver to '%s': %m", value);
+ } else if (streq(key, "IFINDEX")) {
+ r = device_set_ifindex(device, value);
+ if (r < 0)
+ return log_debug_errno(r, "sd-device: could not set ifindex to '%s': %m", value);
+ } else if (streq(key, "DEVMODE")) {
+ r = device_set_devmode(device, value);
+ if (r < 0)
+ return log_debug_errno(r, "sd-device: could not set devmode to '%s': %m", value);
+ } else if (streq(key, "DEVUID")) {
+ r = device_set_devuid(device, value);
+ if (r < 0)
+ return log_debug_errno(r, "sd-device: could not set devuid to '%s': %m", value);
+ } else if (streq(key, "DEVGID")) {
+ r = device_set_devgid(device, value);
+ if (r < 0)
+ return log_debug_errno(r, "sd-device: could not set devgid to '%s': %m", value);
+ } else if (streq(key, "DEVLINKS")) {
+ const char *word, *state;
+ size_t l;
+
+ FOREACH_WORD(word, l, value, state) {
+ char devlink[l + 1];
+
+ strncpy(devlink, word, l);
+ devlink[l] = '\0';
+
+ r = device_add_devlink(device, devlink);
+ if (r < 0)
+ return log_debug_errno(r, "sd-device: could not add devlink '%s': %m", devlink);
+ }
+ } else if (streq(key, "TAGS")) {
+ const char *word, *state;
+ size_t l;
+
+ FOREACH_WORD_SEPARATOR(word, l, value, ":", state) {
+ char tag[l + 1];
+
+ (void)strncpy(tag, word, l);
+ tag[l] = '\0';
+
+ r = device_add_tag(device, tag);
+ if (r < 0)
+ return log_debug_errno(r, "sd-device: could not add tag '%s': %m", tag);
+ }
+ } else {
+ r = device_add_property_internal(device, key, value);
+ if (r < 0)
+ return log_debug_errno(r, "sd-device: could not add property '%s=%s': %m", key, value);
+ }
+
+ return 0;
+}
+
+static const char* const device_action_table[_DEVICE_ACTION_MAX] = {
+ [DEVICE_ACTION_ADD] = "add",
+ [DEVICE_ACTION_REMOVE] = "remove",
+ [DEVICE_ACTION_CHANGE] = "change",
+ [DEVICE_ACTION_MOVE] = "move",
+ [DEVICE_ACTION_ONLINE] = "online",
+ [DEVICE_ACTION_OFFLINE] = "offline",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(device_action, DeviceAction);
+
+static int device_append(sd_device *device, char *key, const char **_major, const char **_minor, uint64_t *_seqnum,
+ DeviceAction *_action) {
+ DeviceAction action = _DEVICE_ACTION_INVALID;
+ uint64_t seqnum = 0;
+ const char *major = NULL, *minor = NULL;
+ char *value;
+ int r;
+
+ assert(device);
+ assert(key);
+ assert(_major);
+ assert(_minor);
+ assert(_seqnum);
+ assert(_action);
+
+ value = strchr(key, '=');
+ if (!value) {
+ log_debug("sd-device: not a key-value pair: '%s'", key);
+ return -EINVAL;
+ }
+
+ *value = '\0';
+
+ value++;
+
+ if (streq(key, "MAJOR"))
+ major = value;
+ else if (streq(key, "MINOR"))
+ minor = value;
+ else {
+ if (streq(key, "ACTION")) {
+ action = device_action_from_string(value);
+ if (action == _DEVICE_ACTION_INVALID)
+ return -EINVAL;
+ } else if (streq(key, "SEQNUM")) {
+ r = safe_atou64(value, &seqnum);
+ if (r < 0)
+ return r;
+ else if (seqnum == 0)
+ /* kernel only sends seqnum > 0 */
+ return -EINVAL;
+ }
+
+ r = device_amend(device, key, value);
+ if (r < 0)
+ return r;
+ }
+
+ if (major != 0)
+ *_major = major;
+
+ if (minor != 0)
+ *_minor = minor;
+
+ if (action != _DEVICE_ACTION_INVALID)
+ *_action = action;
+
+ if (seqnum > 0)
+ *_seqnum = seqnum;
+
+ return 0;
+}
+
+void device_seal(sd_device *device) {
+ assert(device);
+
+ device->sealed = true;
+}
+
+static int device_verify(sd_device *device, DeviceAction action, uint64_t seqnum) {
+ assert(device);
+
+ if (!device->devpath || !device->subsystem || action == _DEVICE_ACTION_INVALID || seqnum == 0) {
+ log_debug("sd-device: device created from strv lacks devpath, subsystem, action or seqnum");
+ return -EINVAL;
+ }
+
+ device->sealed = true;
+
+ return 0;
+}
+
+int device_new_from_strv(sd_device **ret, char **strv) {
+ _cleanup_device_unref_ sd_device *device = NULL;
+ char **key;
+ const char *major = NULL, *minor = NULL;
+ DeviceAction action = _DEVICE_ACTION_INVALID;
+ uint64_t seqnum;
+ int r;
+
+ assert(ret);
+ assert(strv);
+
+ r = device_new_aux(&device);
+ if (r < 0)
+ return r;
+
+ STRV_FOREACH(key, strv) {
+ r = device_append(device, *key, &major, &minor, &seqnum, &action);
+ if (r < 0)
+ return r;
+ }
+
+ if (major) {
+ r = device_set_devnum(device, major, minor);
+ if (r < 0)
+ return log_debug_errno(r, "sd-device: could not set devnum %s:%s: %m", major, minor);
+ }
+
+ r = device_verify(device, action, seqnum);
+ if (r < 0)
+ return r;
+
+ *ret = device;
+ device = NULL;
+
+ return 0;
+}
+
+int device_new_from_nulstr(sd_device **ret, uint8_t *nulstr, size_t len) {
+ _cleanup_device_unref_ sd_device *device = NULL;
+ const char *major = NULL, *minor = NULL;
+ DeviceAction action = _DEVICE_ACTION_INVALID;
+ uint64_t seqnum;
+ unsigned i = 0;
+ int r;
+
+ assert(ret);
+ assert(nulstr);
+ assert(len);
+
+ r = device_new_aux(&device);
+ if (r < 0)
+ return r;
+
+ while (i < len) {
+ char *key;
+ const char *end;
+
+ key = (char*)&nulstr[i];
+ end = memchr(key, '\0', len - i);
+ if (!end) {
+ log_debug("sd-device: failed to parse nulstr");
+ return -EINVAL;
+ }
+ i += end - key + 1;
+
+ r = device_append(device, key, &major, &minor, &seqnum, &action);
+ if (r < 0)
+ return r;
+ }
+
+ if (major) {
+ r = device_set_devnum(device, major, minor);
+ if (r < 0)
+ return log_debug_errno(r, "sd-device: could not set devnum %s:%s: %m", major, minor);
+ }
+
+ r = device_verify(device, action, seqnum);
+ if (r < 0)
+ return r;
+
+ *ret = device;
+ device = NULL;
+
+ return 0;
+}
+
+static int device_update_properties_bufs(sd_device *device) {
+ const char *val, *prop;
+ _cleanup_free_ char **buf_strv = NULL;
+ _cleanup_free_ uint8_t *buf_nulstr = NULL;
+ size_t allocated_nulstr = 0;
+ size_t nulstr_len = 0, num = 0, i = 0;
+
+ assert(device);
+
+ if (!device->properties_buf_outdated)
+ return 0;
+
+ FOREACH_DEVICE_PROPERTY(device, prop, val) {
+ size_t len = 0;
+
+ len = strlen(prop) + 1 + strlen(val);
+
+ buf_nulstr = GREEDY_REALLOC0(buf_nulstr, allocated_nulstr, nulstr_len + len + 2);
+ if (!buf_nulstr)
+ return -ENOMEM;
+
+ strscpyl((char *)buf_nulstr + nulstr_len, len + 1, prop, "=", val, NULL);
+ nulstr_len += len + 1;
+ ++num;
+ }
+
+ /* build buf_strv from buf_nulstr */
+ buf_strv = new0(char *, num + 1);
+ if (!buf_strv)
+ return -ENOMEM;
+
+ NULSTR_FOREACH(val, (char*) buf_nulstr) {
+ buf_strv[i] = (char *) val;
+ assert(i < num);
+ i++;
+ }
+
+ free(device->properties_nulstr);
+ device->properties_nulstr = buf_nulstr;
+ buf_nulstr = NULL;
+ device->properties_nulstr_len = nulstr_len;
+ free(device->properties_strv);
+ device->properties_strv = buf_strv;
+ buf_strv = NULL;
+
+ device->properties_buf_outdated = false;
+
+ return 0;
+}
+
+int device_get_properties_nulstr(sd_device *device, const uint8_t **nulstr, size_t *len) {
+ int r;
+
+ assert(device);
+ assert(nulstr);
+ assert(len);
+
+ r = device_update_properties_bufs(device);
+ if (r < 0)
+ return r;
+
+ *nulstr = device->properties_nulstr;
+ *len = device->properties_nulstr_len;
+
+ return 0;
+}
+
+int device_get_properties_strv(sd_device *device, char ***strv) {
+ int r;
+
+ assert(device);
+ assert(strv);
+
+ r = device_update_properties_bufs(device);
+ if (r < 0)
+ return r;
+
+ *strv = device->properties_strv;
+
+ return 0;
+}
+
+int device_get_devlink_priority(sd_device *device, int *priority) {
+ int r;
+
+ assert(device);
+ assert(priority);
+
+ r = device_read_db(device);
+ if (r < 0)
+ return r;
+
+ *priority = device->devlink_priority;
+
+ return 0;
+}
+
+int device_get_watch_handle(sd_device *device, int *handle) {
+ int r;
+
+ assert(device);
+ assert(handle);
+
+ r = device_read_db(device);
+ if (r < 0)
+ return r;
+
+ *handle = device->watch_handle;
+
+ return 0;
+}
+
+void device_set_watch_handle(sd_device *device, int handle) {
+ assert(device);
+
+ device->watch_handle = handle;
+}
+
+int device_rename(sd_device *device, const char *name) {
+ _cleanup_free_ char *dirname = NULL;
+ char *new_syspath;
+ const char *interface;
+ int r;
+
+ assert(device);
+ assert(name);
+
+ dirname = dirname_malloc(device->syspath);
+ if (!dirname)
+ return -ENOMEM;
+
+ new_syspath = strjoina(dirname, "/", name);
+
+ /* the user must trust that the new name is correct */
+ r = device_set_syspath(device, new_syspath, false);
+ if (r < 0)
+ return r;
+
+ r = sd_device_get_property_value(device, "INTERFACE", &interface);
+ if (r >= 0) {
+ r = device_add_property_internal(device, "INTERFACE", name);
+ if (r < 0)
+ return r;
+
+ /* like DEVPATH_OLD, INTERFACE_OLD is not saved to the db, but only stays around for the current event */
+ r = device_add_property_internal(device, "INTERFACE_OLD", interface);
+ if (r < 0)
+ return r;
+ } else if (r != -ENOENT)
+ return r;
+
+ return 0;
+}
+
+int device_shallow_clone(sd_device *old_device, sd_device **new_device) {
+ _cleanup_device_unref_ sd_device *ret = NULL;
+ int r;
+
+ assert(old_device);
+ assert(new_device);
+
+ r = device_new_aux(&ret);
+ if (r < 0)
+ return r;
+
+ r = device_set_syspath(ret, old_device->syspath, false);
+ if (r < 0)
+ return r;
+
+ r = device_set_subsystem(ret, old_device->subsystem);
+ if (r < 0)
+ return r;
+
+ ret->devnum = old_device->devnum;
+
+ *new_device = ret;
+ ret = NULL;
+
+ return 0;
+}
+
+int device_clone_with_db(sd_device *old_device, sd_device **new_device) {
+ _cleanup_device_unref_ sd_device *ret = NULL;
+ int r;
+
+ assert(old_device);
+ assert(new_device);
+
+ r = device_shallow_clone(old_device, &ret);
+ if (r < 0)
+ return r;
+
+ r = device_read_db(ret);
+ if (r < 0)
+ return r;
+
+ ret->sealed = true;
+
+ *new_device = ret;
+ ret = NULL;
+
+ return 0;
+}
+
+int device_new_from_synthetic_event(sd_device **new_device, const char *syspath, const char *action) {
+ _cleanup_device_unref_ sd_device *ret = NULL;
+ int r;
+
+ assert(new_device);
+ assert(syspath);
+ assert(action);
+
+ r = sd_device_new_from_syspath(&ret, syspath);
+ if (r < 0)
+ return r;
+
+ r = device_read_uevent_file(ret);
+ if (r < 0)
+ return r;
+
+ r = device_add_property_internal(ret, "ACTION", action);
+ if (r < 0)
+ return r;
+
+ *new_device = ret;
+ ret = NULL;
+
+ return 0;
+}
+
+int device_copy_properties(sd_device *device_dst, sd_device *device_src) {
+ const char *property, *value;
+ int r;
+
+ assert(device_dst);
+ assert(device_src);
+
+ FOREACH_DEVICE_PROPERTY(device_src, property, value) {
+ r = device_add_property(device_dst, property, value);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+void device_cleanup_tags(sd_device *device) {
+ assert(device);
+
+ set_free_free(device->tags);
+ device->tags = NULL;
+ device->property_tags_outdated = true;
+ device->tags_generation ++;
+}
+
+void device_cleanup_devlinks(sd_device *device) {
+ assert(device);
+
+ set_free_free(device->devlinks);
+ device->devlinks = NULL;
+ device->property_devlinks_outdated = true;
+ device->devlinks_generation ++;
+}
+
+void device_remove_tag(sd_device *device, const char *tag) {
+ assert(device);
+ assert(tag);
+
+ free(set_remove(device->tags, tag));
+ device->property_tags_outdated = true;
+ device->tags_generation ++;
+}
+
+static int device_tag(sd_device *device, const char *tag, bool add) {
+ const char *id;
+ char *path;
+ int r;
+
+ assert(device);
+ assert(tag);
+
+ r = device_get_id_filename(device, &id);
+ if (r < 0)
+ return r;
+
+ path = strjoina("/run/udev/tags/", tag, "/", id);
+
+ if (add) {
+ r = touch_file(path, true, USEC_INFINITY, UID_INVALID, GID_INVALID, 0444);
+ if (r < 0)
+ return r;
+ } else {
+ r = unlink(path);
+ if (r < 0 && errno != ENOENT)
+ return -errno;
+ }
+
+ return 0;
+}
+
+int device_tag_index(sd_device *device, sd_device *device_old, bool add) {
+ const char *tag;
+ int r = 0, k;
+
+ if (add && device_old) {
+ /* delete possible left-over tags */
+ FOREACH_DEVICE_TAG(device_old, tag) {
+ if (!sd_device_has_tag(device, tag)) {
+ k = device_tag(device_old, tag, false);
+ if (r >= 0 && k < 0)
+ r = k;
+ }
+ }
+ }
+
+ FOREACH_DEVICE_TAG(device, tag) {
+ k = device_tag(device, tag, add);
+ if (r >= 0 && k < 0)
+ r = k;
+ }
+
+ return r;
+}
+
+static bool device_has_info(sd_device *device) {
+ assert(device);
+
+ if (!set_isempty(device->devlinks))
+ return true;
+
+ if (device->devlink_priority != 0)
+ return true;
+
+ if (!ordered_hashmap_isempty(device->properties_db))
+ return true;
+
+ if (!set_isempty(device->tags))
+ return true;
+
+ if (device->watch_handle >= 0)
+ return true;
+
+ return false;
+}
+
+void device_set_db_persist(sd_device *device) {
+ assert(device);
+
+ device->db_persist = true;
+}
+
+int device_update_db(sd_device *device) {
+ const char *id;
+ char *path;
+ _cleanup_fclose_ FILE *f = NULL;
+ _cleanup_free_ char *path_tmp = NULL;
+ bool has_info;
+ int r;
+
+ assert(device);
+
+ has_info = device_has_info(device);
+
+ r = device_get_id_filename(device, &id);
+ if (r < 0)
+ return r;
+
+ path = strjoina("/run/udev/data/", id);
+
+ /* do not store anything for otherwise empty devices */
+ if (!has_info && major(device->devnum) == 0 && device->ifindex == 0) {
+ r = unlink(path);
+ if (r < 0 && errno != ENOENT)
+ return -errno;
+
+ return 0;
+ }
+
+ /* write a database file */
+ r = mkdir_parents(path, 0755);
+ if (r < 0)
+ return r;
+
+ r = fopen_temporary(path, &f, &path_tmp);
+ if (r < 0)
+ return r;
+
+ /*
+ * set 'sticky' bit to indicate that we should not clean the
+ * database when we transition from initramfs to the real root
+ */
+ if (device->db_persist) {
+ r = fchmod(fileno(f), 01644);
+ if (r < 0) {
+ r = -errno;
+ goto fail;
+ }
+ } else {
+ r = fchmod(fileno(f), 0644);
+ if (r < 0) {
+ r = -errno;
+ goto fail;
+ }
+ }
+
+ if (has_info) {
+ const char *property, *value, *tag;
+ Iterator i;
+
+ if (major(device->devnum) > 0) {
+ const char *devlink;
+
+ FOREACH_DEVICE_DEVLINK(device, devlink)
+ fprintf(f, "S:%s\n", devlink + strlen("/dev/"));
+
+ if (device->devlink_priority != 0)
+ fprintf(f, "L:%i\n", device->devlink_priority);
+
+ if (device->watch_handle >= 0)
+ fprintf(f, "W:%i\n", device->watch_handle);
+ }
+
+ if (device->usec_initialized > 0)
+ fprintf(f, "I:"USEC_FMT"\n", device->usec_initialized);
+
+ ORDERED_HASHMAP_FOREACH_KEY(value, property, device->properties_db, i)
+ fprintf(f, "E:%s=%s\n", property, value);
+
+ FOREACH_DEVICE_TAG(device, tag)
+ fprintf(f, "G:%s\n", tag);
+ }
+
+ r = fflush_and_check(f);
+ if (r < 0)
+ goto fail;
+
+ r = rename(path_tmp, path);
+ if (r < 0) {
+ r = -errno;
+ goto fail;
+ }
+
+ log_debug("created %s file '%s' for '%s'", has_info ? "db" : "empty",
+ path, device->devpath);
+
+ return 0;
+
+fail:
+ log_error_errno(r, "failed to create %s file '%s' for '%s'", has_info ? "db" : "empty",
+ path, device->devpath);
+ unlink(path);
+ unlink(path_tmp);
+
+ return r;
+}
+
+int device_delete_db(sd_device *device) {
+ const char *id;
+ char *path;
+ int r;
+
+ assert(device);
+
+ r = device_get_id_filename(device, &id);
+ if (r < 0)
+ return r;
+
+ path = strjoina("/run/udev/data/", id);
+
+ r = unlink(path);
+ if (r < 0 && errno != ENOENT)
+ return -errno;
+
+ return 0;
+}
+
+int device_read_db_force(sd_device *device) {
+ assert(device);
+
+ return device_read_db_aux(device, true);
+}
diff --git a/src/libsystemd/sd-device/device-private.h b/src/libsystemd/sd-device/device-private.h
new file mode 100644
index 0000000000..49a7b66a2b
--- /dev/null
+++ b/src/libsystemd/sd-device/device-private.h
@@ -0,0 +1,64 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2014 Tom Gundersen <teg@jklm.no>
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser 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 device_new_from_nulstr(sd_device **ret, uint8_t *nulstr, size_t len);
+int device_new_from_strv(sd_device **ret, char **strv);
+
+int device_get_id_filename(sd_device *device, const char **ret);
+
+int device_get_devlink_priority(sd_device *device, int *priority);
+int device_get_watch_handle(sd_device *device, int *handle);
+int device_get_devnode_mode(sd_device *device, mode_t *mode);
+int device_get_devnode_uid(sd_device *device, uid_t *uid);
+int device_get_devnode_gid(sd_device *device, gid_t *gid);
+
+void device_seal(sd_device *device);
+void device_set_is_initialized(sd_device *device);
+void device_set_watch_handle(sd_device *device, int fd);
+void device_set_db_persist(sd_device *device);
+void device_set_devlink_priority(sd_device *device, int priority);
+int device_ensure_usec_initialized(sd_device *device, sd_device *device_old);
+int device_add_devlink(sd_device *device, const char *devlink);
+int device_add_property(sd_device *device, const char *property, const char *value);
+int device_add_tag(sd_device *device, const char *tag);
+void device_remove_tag(sd_device *device, const char *tag);
+void device_cleanup_tags(sd_device *device);
+void device_cleanup_devlinks(sd_device *device);
+
+uint64_t device_get_properties_generation(sd_device *device);
+uint64_t device_get_tags_generation(sd_device *device);
+uint64_t device_get_devlinks_generation(sd_device *device);
+
+int device_get_properties_nulstr(sd_device *device, const uint8_t **nulstr, size_t *len);
+int device_get_properties_strv(sd_device *device, char ***strv);
+
+int device_rename(sd_device *device, const char *name);
+int device_shallow_clone(sd_device *old_device, sd_device **new_device);
+int device_clone_with_db(sd_device *old_device, sd_device **new_device);
+int device_copy_properties(sd_device *device_dst, sd_device *device_src);
+int device_new_from_synthetic_event(sd_device **new_device, const char *syspath, const char *action);
+
+int device_tag_index(sd_device *dev, sd_device *dev_old, bool add);
+int device_update_db(sd_device *device);
+int device_delete_db(sd_device *device);
+int device_read_db_force(sd_device *device);
diff --git a/src/libsystemd/sd-device/device-util.h b/src/libsystemd/sd-device/device-util.h
new file mode 100644
index 0000000000..9b05a2498d
--- /dev/null
+++ b/src/libsystemd/sd-device/device-util.h
@@ -0,0 +1,60 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2014-2015 Tom Gundersen <teg@jklm.no>
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser 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"
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(sd_device*, sd_device_unref);
+#define _cleanup_device_unref_ _cleanup_(sd_device_unrefp)
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(sd_device_enumerator*, sd_device_enumerator_unref);
+#define _cleanup_device_enumerator_unref_ _cleanup_(sd_device_enumerator_unrefp)
+
+#define FOREACH_DEVICE_PROPERTY(device, key, value) \
+ for (key = sd_device_get_property_first(device, &(value)); \
+ key; \
+ key = sd_device_get_property_next(device, &(value)))
+
+#define FOREACH_DEVICE_TAG(device, tag) \
+ for (tag = sd_device_get_tag_first(device); \
+ tag; \
+ tag = sd_device_get_tag_next(device))
+
+#define FOREACH_DEVICE_SYSATTR(device, attr) \
+ for (attr = sd_device_get_sysattr_first(device); \
+ attr; \
+ attr = sd_device_get_sysattr_next(device))
+
+#define FOREACH_DEVICE_DEVLINK(device, devlink) \
+ for (devlink = sd_device_get_devlink_first(device); \
+ devlink; \
+ devlink = sd_device_get_devlink_next(device))
+
+#define FOREACH_DEVICE(enumerator, device) \
+ for (device = sd_device_enumerator_get_device_first(enumerator); \
+ device; \
+ device = sd_device_enumerator_get_device_next(enumerator))
+
+#define FOREACH_SUBSYSTEM(enumerator, device) \
+ for (device = sd_device_enumerator_get_subsystem_first(enumerator); \
+ device; \
+ device = sd_device_enumerator_get_subsystem_next(enumerator))
diff --git a/src/libsystemd/sd-device/sd-device.c b/src/libsystemd/sd-device/sd-device.c
new file mode 100644
index 0000000000..8e63b9ef56
--- /dev/null
+++ b/src/libsystemd/sd-device/sd-device.c
@@ -0,0 +1,1839 @@
+/***
+ This file is part of systemd.
+
+ Copyright 2008-2012 Kay Sievers <kay@vrfy.org>
+ Copyright 2014 Tom Gundersen <teg@jklm.no>
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser 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 <ctype.h>
+#include <sys/types.h>
+#include <net/if.h>
+
+#include "util.h"
+#include "macro.h"
+#include "path-util.h"
+#include "strxcpyx.h"
+#include "fileio.h"
+#include "hashmap.h"
+#include "set.h"
+#include "strv.h"
+
+#include "sd-device.h"
+
+#include "device-util.h"
+#include "device-private.h"
+#include "device-internal.h"
+
+int device_new_aux(sd_device **ret) {
+ _cleanup_device_unref_ sd_device *device = NULL;
+
+ assert(ret);
+
+ device = new0(sd_device, 1);
+ if (!device)
+ return -ENOMEM;
+
+ device->n_ref = 1;
+ device->watch_handle = -1;
+
+ *ret = device;
+ device = NULL;
+
+ return 0;
+}
+
+_public_ sd_device *sd_device_ref(sd_device *device) {
+ if (device)
+ assert_se(++ device->n_ref >= 2);
+
+ return device;
+}
+
+_public_ sd_device *sd_device_unref(sd_device *device) {
+ if (device && -- device->n_ref == 0) {
+ sd_device_unref(device->parent);
+ free(device->syspath);
+ free(device->sysname);
+ free(device->devtype);
+ free(device->devname);
+ free(device->subsystem);
+ free(device->driver);
+ free(device->id_filename);
+ free(device->properties_strv);
+ free(device->properties_nulstr);
+
+ ordered_hashmap_free_free_free(device->properties);
+ ordered_hashmap_free_free_free(device->properties_db);
+ hashmap_free_free_free(device->sysattr_values);
+ set_free_free(device->sysattrs);
+ set_free_free(device->tags);
+ set_free_free(device->devlinks);
+
+ free(device);
+ }
+
+ return NULL;
+}
+
+int device_add_property_aux(sd_device *device, const char *_key, const char *_value, bool db) {
+ OrderedHashmap **properties;
+
+ assert(device);
+ assert(_key);
+
+ if (db)
+ properties = &device->properties_db;
+ else
+ properties = &device->properties;
+
+ if (_value) {
+ _cleanup_free_ char *key = NULL, *value = NULL, *old_key = NULL, *old_value = NULL;
+ int r;
+
+ r = ordered_hashmap_ensure_allocated(properties, &string_hash_ops);
+ if (r < 0)
+ return r;
+
+ key = strdup(_key);
+ if (!key)
+ return -ENOMEM;
+
+ value = strdup(_value);
+ if (!value)
+ return -ENOMEM;
+
+ old_value = ordered_hashmap_get2(*properties, key, (void**) &old_key);
+
+ r = ordered_hashmap_replace(*properties, key, value);
+ if (r < 0)
+ return r;
+
+ key = NULL;
+ value = NULL;
+ } else {
+ _cleanup_free_ char *key = NULL;
+ _cleanup_free_ char *value = NULL;
+
+ value = ordered_hashmap_remove2(*properties, _key, (void**) &key);
+ }
+
+ if (!db) {
+ device->properties_generation ++;
+ device->properties_buf_outdated = true;
+ }
+
+ return 0;
+}
+
+int device_add_property_internal(sd_device *device, const char *key, const char *value) {
+ return device_add_property_aux(device, key, value, false);
+}
+
+int device_set_syspath(sd_device *device, const char *_syspath, bool verify) {
+ _cleanup_free_ char *syspath = NULL;
+ const char *devpath;
+ int r;
+
+ assert(device);
+ assert(_syspath);
+
+ /* must be a subdirectory of /sys */
+ if (!path_startswith(_syspath, "/sys/")) {
+ log_debug("sd-device: syspath '%s' is not a subdirectory of /sys", _syspath);
+ return -EINVAL;
+ }
+
+ if (verify) {
+ r = readlink_and_canonicalize(_syspath, &syspath);
+ if (r == -ENOENT)
+ /* the device does not exist (any more?) */
+ return -ENODEV;
+ else if (r == -EINVAL) {
+ /* not a symlink */
+ syspath = canonicalize_file_name(_syspath);
+ if (!syspath) {
+ if (errno == ENOENT)
+ /* the device does not exist (any more?) */
+ return -ENODEV;
+
+ log_debug("sd-device: could not canonicalize '%s': %m", _syspath);
+ return -errno;
+ }
+ } else if (r < 0) {
+ log_debug("sd-device: could not get target of '%s': %s", _syspath, strerror(-r));
+ return r;
+ }
+
+ if (path_startswith(syspath, "/sys/devices/")) {
+ char *path;
+
+ /* all 'devices' require an 'uevent' file */
+ path = strjoina(syspath, "/uevent");
+ r = access(path, F_OK);
+ if (r < 0) {
+ if (errno == ENOENT)
+ /* this is not a valid device */
+ return -ENODEV;
+
+ log_debug("sd-device: %s does not have an uevent file: %m", syspath);
+ return -errno;
+ }
+ } else {
+ /* everything else just just needs to be a directory */
+ if (!is_dir(syspath, false))
+ return -ENODEV;
+ }
+ } else {
+ syspath = strdup(_syspath);
+ if (!syspath)
+ return -ENOMEM;
+ }
+
+ devpath = syspath + strlen("/sys");
+
+ r = device_add_property_internal(device, "DEVPATH", devpath);
+ if (r < 0)
+ return r;
+
+ free(device->syspath);
+ device->syspath = syspath;
+ syspath = NULL;
+
+ device->devpath = devpath;
+
+ return 0;
+}
+
+_public_ int sd_device_new_from_syspath(sd_device **ret, const char *syspath) {
+ _cleanup_device_unref_ sd_device *device = NULL;
+ int r;
+
+ assert_return(ret, -EINVAL);
+ assert_return(syspath, -EINVAL);
+
+ r = device_new_aux(&device);
+ if (r < 0)
+ return r;
+
+ r = device_set_syspath(device, syspath, true);
+ if (r < 0)
+ return r;
+
+ *ret = device;
+ device = NULL;
+
+ return 0;
+}
+
+_public_ int sd_device_new_from_devnum(sd_device **ret, char type, dev_t devnum) {
+ char *syspath;
+ char id[DECIMAL_STR_MAX(unsigned) * 2 + 1];
+
+ assert_return(ret, -EINVAL);
+ assert_return(type == 'b' || type == 'c', -EINVAL);
+
+ /* use /sys/dev/{block,char}/<maj>:<min> link */
+ snprintf(id, sizeof(id), "%u:%u", major(devnum), minor(devnum));
+
+ syspath = strjoina("/sys/dev/", (type == 'b' ? "block" : "char"), "/", id);
+
+ return sd_device_new_from_syspath(ret, syspath);
+}
+
+_public_ int sd_device_new_from_subsystem_sysname(sd_device **ret, const char *subsystem, const char *sysname) {
+ char *syspath;
+
+ assert_return(ret, -EINVAL);
+ assert_return(subsystem, -EINVAL);
+ assert_return(sysname, -EINVAL);
+
+ if (streq(subsystem, "subsystem")) {
+ syspath = strjoina("/sys/subsystem/", sysname);
+ if (access(syspath, F_OK) >= 0)
+ return sd_device_new_from_syspath(ret, syspath);
+
+ syspath = strjoina("/sys/bus/", sysname);
+ if (access(syspath, F_OK) >= 0)
+ return sd_device_new_from_syspath(ret, syspath);
+
+ syspath = strjoina("/sys/class/", sysname);
+ if (access(syspath, F_OK) >= 0)
+ return sd_device_new_from_syspath(ret, syspath);
+ } else if (streq(subsystem, "module")) {
+ syspath = strjoina("/sys/module/", sysname);
+ if (access(syspath, F_OK) >= 0)
+ return sd_device_new_from_syspath(ret, syspath);
+ } else if (streq(subsystem, "drivers")) {
+ char subsys[PATH_MAX];
+ char *driver;
+
+ strscpy(subsys, sizeof(subsys), sysname);
+ driver = strchr(subsys, ':');
+ if (driver) {
+ driver[0] = '\0';
+ driver++;
+
+ syspath = strjoina("/sys/subsystem/", subsys, "/drivers/", driver);
+ if (access(syspath, F_OK) >= 0)
+ return sd_device_new_from_syspath(ret, syspath);
+
+ syspath = strjoina("/sys/bus/", subsys, "/drivers/", driver);
+ if (access(syspath, F_OK) >= 0)
+ return sd_device_new_from_syspath(ret, syspath);
+ } else
+ return -EINVAL;
+ } else {
+ syspath = strjoina("/sys/subsystem/", subsystem, "/devices/", sysname);
+ if (access(syspath, F_OK) >= 0)
+ return sd_device_new_from_syspath(ret, syspath);
+
+ syspath = strjoina("/sys/bus/", subsystem, "/devices/", sysname);
+ if (access(syspath, F_OK) >= 0)
+ return sd_device_new_from_syspath(ret, syspath);
+
+ syspath = strjoina("/sys/class/", subsystem, "/", sysname);
+ if (access(syspath, F_OK) >= 0)
+ return sd_device_new_from_syspath(ret, syspath);
+ }
+
+ return -ENODEV;
+}
+
+int device_set_devtype(sd_device *device, const char *_devtype) {
+ _cleanup_free_ char *devtype = NULL;
+ int r;
+
+ assert(device);
+ assert(_devtype);
+
+ devtype = strdup(_devtype);
+ if (!devtype)
+ return -ENOMEM;
+
+ r = device_add_property_internal(device, "DEVTYPE", devtype);
+ if (r < 0)
+ return r;
+
+ free(device->devtype);
+ device->devtype = devtype;
+ devtype = NULL;
+
+ return 0;
+}
+
+int device_set_ifindex(sd_device *device, const char *_ifindex) {
+ int ifindex, r;
+
+ assert(device);
+ assert(_ifindex);
+
+ r = safe_atoi(_ifindex, &ifindex);
+ if (r < 0)
+ return r;
+
+ if (ifindex <= 0)
+ return -EINVAL;
+
+ r = device_add_property_internal(device, "IFINDEX", _ifindex);
+ if (r < 0)
+ return r;
+
+ device->ifindex = ifindex;
+
+ return 0;
+}
+
+int device_set_devname(sd_device *device, const char *_devname) {
+ _cleanup_free_ char *devname = NULL;
+ int r;
+
+ assert(device);
+ assert(_devname);
+
+ if (_devname[0] != '/') {
+ r = asprintf(&devname, "/dev/%s", _devname);
+ if (r < 0)
+ return -ENOMEM;
+ } else {
+ devname = strdup(_devname);
+ if (!devname)
+ return -ENOMEM;
+ }
+
+ r = device_add_property_internal(device, "DEVNAME", devname);
+ if (r < 0)
+ return r;
+
+ free(device->devname);
+ device->devname = devname;
+ devname = NULL;
+
+ return 0;
+}
+
+int device_set_devmode(sd_device *device, const char *_devmode) {
+ unsigned devmode;
+ int r;
+
+ assert(device);
+ assert(_devmode);
+
+ r = safe_atou(_devmode, &devmode);
+ if (r < 0)
+ return r;
+
+ if (devmode > 07777)
+ return -EINVAL;
+
+ r = device_add_property_internal(device, "DEVMODE", _devmode);
+ if (r < 0)
+ return r;
+
+ device->devmode = devmode;
+
+ return 0;
+}
+
+int device_set_devnum(sd_device *device, const char *major, const char *minor) {
+ unsigned maj = 0, min = 0;
+ int r;
+
+ assert(device);
+ assert(major);
+
+ r = safe_atou(major, &maj);
+ if (r < 0)
+ return r;
+ if (!maj)
+ return 0;
+
+ if (minor) {
+ r = safe_atou(minor, &min);
+ if (r < 0)
+ return r;
+ }
+
+ r = device_add_property_internal(device, "MAJOR", major);
+ if (r < 0)
+ return r;
+
+ if (minor) {
+ r = device_add_property_internal(device, "MINOR", minor);
+ if (r < 0)
+ return r;
+ }
+
+ device->devnum = makedev(maj, min);
+
+ return 0;
+}
+
+static int handle_uevent_line(sd_device *device, const char *key, const char *value, const char **major, const char **minor) {
+ int r;
+
+ assert(device);
+ assert(key);
+ assert(value);
+ assert(major);
+ assert(minor);
+
+ if (streq(key, "DEVTYPE")) {
+ r = device_set_devtype(device, value);
+ if (r < 0)
+ return r;
+ } else if (streq(key, "IFINDEX")) {
+ r = device_set_ifindex(device, value);
+ if (r < 0)
+ return r;
+ } else if (streq(key, "DEVNAME")) {
+ r = device_set_devname(device, value);
+ if (r < 0)
+ return r;
+ } else if (streq(key, "DEVMODE")) {
+ r = device_set_devmode(device, value);
+ if (r < 0)
+ return r;
+ } else if (streq(key, "MAJOR"))
+ *major = value;
+ else if (streq(key, "MINOR"))
+ *minor = value;
+ else {
+ r = device_add_property_internal(device, key, value);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+int device_read_uevent_file(sd_device *device) {
+ _cleanup_free_ char *uevent = NULL;
+ const char *syspath, *key, *value, *major = NULL, *minor = NULL;
+ char *path;
+ size_t uevent_len;
+ unsigned i;
+ int r;
+
+ enum {
+ PRE_KEY,
+ KEY,
+ PRE_VALUE,
+ VALUE,
+ INVALID_LINE,
+ } state = PRE_KEY;
+
+ assert(device);
+
+ if (device->uevent_loaded || device->sealed)
+ return 0;
+
+ device->uevent_loaded = true;
+
+ r = sd_device_get_syspath(device, &syspath);
+ if (r < 0)
+ return r;
+
+ path = strjoina(syspath, "/uevent");
+
+ r = read_full_file(path, &uevent, &uevent_len);
+ if (r == -EACCES)
+ /* empty uevent files may be write-only */
+ return 0;
+ else if (r == -ENOENT)
+ /* some devices may not have uevent files, see set_syspath() */
+ return 0;
+ else if (r < 0) {
+ log_debug("sd-device: failed to read uevent file '%s': %s", path, strerror(-r));
+ return r;
+ }
+
+ for (i = 0; i < uevent_len; i++) {
+ switch (state) {
+ case PRE_KEY:
+ if (!strchr(NEWLINE, uevent[i])) {
+ key = &uevent[i];
+
+ state = KEY;
+ }
+
+ break;
+ case KEY:
+ if (uevent[i] == '=') {
+ uevent[i] = '\0';
+
+ state = PRE_VALUE;
+ } else if (strchr(NEWLINE, uevent[i])) {
+ uevent[i] = '\0';
+ log_debug("sd-device: ignoring invalid uevent line '%s'", key);
+
+ state = PRE_KEY;
+ }
+
+ break;
+ case PRE_VALUE:
+ value = &uevent[i];
+
+ state = VALUE;
+
+ break;
+ case VALUE:
+ if (strchr(NEWLINE, uevent[i])) {
+ uevent[i] = '\0';
+
+ r = handle_uevent_line(device, key, value, &major, &minor);
+ if (r < 0)
+ log_debug("sd-device: failed to handle uevent entry '%s=%s': %s", key, value, strerror(-r));
+
+ state = PRE_KEY;
+ }
+
+ break;
+ default:
+ assert_not_reached("invalid state when parsing uevent file");
+ }
+ }
+
+ if (major) {
+ r = device_set_devnum(device, major, minor);
+ if (r < 0)
+ log_debug("sd-device: could not set 'MAJOR=%s' or 'MINOR=%s' from '%s': %s", major, minor, path, strerror(-r));
+ }
+
+ return 0;
+}
+
+_public_ int sd_device_get_ifindex(sd_device *device, int *ifindex) {
+ int r;
+
+ assert_return(device, -EINVAL);
+ assert_return(ifindex, -EINVAL);
+
+ r = device_read_uevent_file(device);
+ if (r < 0)
+ return r;
+
+ *ifindex = device->ifindex;
+
+ return 0;
+}
+
+_public_ int sd_device_new_from_device_id(sd_device **ret, const char *id) {
+ int r;
+
+ assert_return(ret, -EINVAL);
+ assert_return(id, -EINVAL);
+
+ switch (id[0]) {
+ case 'b':
+ case 'c':
+ {
+ char type;
+ int maj, min;
+
+ r = sscanf(id, "%c%i:%i", &type, &maj, &min);
+ if (r != 3)
+ return -EINVAL;
+
+ return sd_device_new_from_devnum(ret, type, makedev(maj, min));
+ }
+ case 'n':
+ {
+ _cleanup_device_unref_ sd_device *device = NULL;
+ _cleanup_close_ int sk = -1;
+ struct ifreq ifr = {};
+ int ifindex;
+
+ r = safe_atoi(&id[1], &ifr.ifr_ifindex);
+ if (r < 0)
+ return r;
+ else if (ifr.ifr_ifindex <= 0)
+ return -EINVAL;
+
+ sk = socket(PF_INET, SOCK_DGRAM, 0);
+ if (sk < 0)
+ return -errno;
+
+ r = ioctl(sk, SIOCGIFNAME, &ifr);
+ if (r < 0)
+ return -errno;
+
+ r = sd_device_new_from_subsystem_sysname(&device, "net", ifr.ifr_name);
+ if (r < 0)
+ return r;
+
+ r = sd_device_get_ifindex(device, &ifindex);
+ if (r < 0)
+ return r;
+
+ /* this is racey, so we might end up with the wrong device */
+ if (ifr.ifr_ifindex != ifindex)
+ return -ENODEV;
+
+ *ret = device;
+ device = NULL;
+
+ return 0;
+ }
+ case '+':
+ {
+ char subsys[PATH_MAX];
+ char *sysname;
+
+ (void)strscpy(subsys, sizeof(subsys), id + 1);
+ sysname = strchr(subsys, ':');
+ if (!sysname)
+ return -EINVAL;
+
+ sysname[0] = '\0';
+ sysname ++;
+
+ return sd_device_new_from_subsystem_sysname(ret, subsys, sysname);
+ }
+ default:
+ return -EINVAL;
+ }
+}
+
+_public_ int sd_device_get_syspath(sd_device *device, const char **ret) {
+ assert_return(device, -EINVAL);
+ assert_return(ret, -EINVAL);
+
+ assert(path_startswith(device->syspath, "/sys/"));
+
+ *ret = device->syspath;
+
+ return 0;
+}
+
+static int device_new_from_child(sd_device **ret, sd_device *child) {
+ _cleanup_free_ char *path = NULL;
+ const char *subdir, *syspath;
+ int r;
+
+ assert(ret);
+ assert(child);
+
+ r = sd_device_get_syspath(child, &syspath);
+ if (r < 0)
+ return r;
+
+ path = strdup(syspath);
+ if (!path)
+ return -ENOMEM;
+ subdir = path + strlen("/sys");
+
+ for (;;) {
+ char *pos;
+
+ pos = strrchr(subdir, '/');
+ if (!pos || pos < subdir + 2)
+ break;
+
+ *pos = '\0';
+
+ r = sd_device_new_from_syspath(ret, path);
+ if (r < 0)
+ continue;
+
+ return 0;
+ }
+
+ return -ENODEV;
+}
+
+_public_ int sd_device_get_parent(sd_device *child, sd_device **ret) {
+
+ assert_return(ret, -EINVAL);
+ assert_return(child, -EINVAL);
+
+ if (!child->parent_set) {
+ child->parent_set = true;
+
+ (void)device_new_from_child(&child->parent, child);
+ }
+
+ if (!child->parent)
+ return -ENOENT;
+
+ *ret = child->parent;
+
+ return 0;
+}
+
+int device_set_subsystem(sd_device *device, const char *_subsystem) {
+ _cleanup_free_ char *subsystem = NULL;
+ int r;
+
+ assert(device);
+ assert(_subsystem);
+
+ subsystem = strdup(_subsystem);
+ if (!subsystem)
+ return -ENOMEM;
+
+ r = device_add_property_internal(device, "SUBSYSTEM", subsystem);
+ if (r < 0)
+ return r;
+
+ free(device->subsystem);
+ device->subsystem = subsystem;
+ subsystem = NULL;
+
+ device->subsystem_set = true;
+
+ return 0;
+}
+
+_public_ int sd_device_get_subsystem(sd_device *device, const char **ret) {
+ assert_return(ret, -EINVAL);
+ assert_return(device, -EINVAL);
+
+ if (!device->subsystem_set) {
+ _cleanup_free_ char *subsystem = NULL;
+ const char *syspath;
+ char *path;
+ int r;
+
+ /* read 'subsystem' link */
+ r = sd_device_get_syspath(device, &syspath);
+ if (r < 0)
+ return r;
+
+ path = strjoina(syspath, "/subsystem");
+ r = readlink_value(path, &subsystem);
+ if (r >= 0)
+ r = device_set_subsystem(device, subsystem);
+ /* use implicit names */
+ else if (path_startswith(device->devpath, "/module/"))
+ r = device_set_subsystem(device, "module");
+ else if (strstr(device->devpath, "/drivers/"))
+ r = device_set_subsystem(device, "drivers");
+ else if (path_startswith(device->devpath, "/subsystem/") ||
+ path_startswith(device->devpath, "/class/") ||
+ path_startswith(device->devpath, "/bus/"))
+ r = device_set_subsystem(device, "subsystem");
+ if (r < 0 && r != -ENOENT)
+ return log_debug_errno(r, "sd-device: could not set subsystem for %s: %m", device->devpath);
+
+ device->subsystem_set = true;
+ }
+
+ *ret = device->subsystem;
+
+ return 0;
+}
+
+_public_ int sd_device_get_devtype(sd_device *device, const char **devtype) {
+ int r;
+
+ assert(devtype);
+ assert(device);
+
+ r = device_read_uevent_file(device);
+ if (r < 0)
+ return r;
+
+ *devtype = device->devtype;
+
+ return 0;
+}
+
+_public_ int sd_device_get_parent_with_subsystem_devtype(sd_device *child, const char *subsystem, const char *devtype, sd_device **ret) {
+ sd_device *parent = NULL;
+ int r;
+
+ assert_return(child, -EINVAL);
+ assert_return(subsystem, -EINVAL);
+
+ r = sd_device_get_parent(child, &parent);
+ while (r >= 0) {
+ const char *parent_subsystem = NULL;
+ const char *parent_devtype = NULL;
+
+ (void)sd_device_get_subsystem(parent, &parent_subsystem);
+ if (streq_ptr(parent_subsystem, subsystem)) {
+ if (!devtype)
+ break;
+
+ (void)sd_device_get_devtype(parent, &parent_devtype);
+ if (streq_ptr(parent_devtype, devtype))
+ break;
+ }
+ r = sd_device_get_parent(parent, &parent);
+ }
+
+ if (r < 0)
+ return r;
+
+ *ret = parent;
+
+ return 0;
+}
+
+_public_ int sd_device_get_devnum(sd_device *device, dev_t *devnum) {
+ int r;
+
+ assert_return(device, -EINVAL);
+ assert_return(devnum, -EINVAL);
+
+ r = device_read_uevent_file(device);
+ if (r < 0)
+ return r;
+
+ *devnum = device->devnum;
+
+ return 0;
+}
+
+int device_set_driver(sd_device *device, const char *_driver) {
+ _cleanup_free_ char *driver = NULL;
+ int r;
+
+ assert(device);
+ assert(_driver);
+
+ driver = strdup(_driver);
+ if (!driver)
+ return -ENOMEM;
+
+ r = device_add_property_internal(device, "DRIVER", driver);
+ if (r < 0)
+ return r;
+
+ free(device->driver);
+ device->driver = driver;
+ driver = NULL;
+
+ device->driver_set = true;
+
+ return 0;
+}
+
+_public_ int sd_device_get_driver(sd_device *device, const char **ret) {
+ assert_return(device, -EINVAL);
+ assert_return(ret, -EINVAL);
+
+ if (!device->driver_set) {
+ _cleanup_free_ char *driver = NULL;
+ const char *syspath;
+ char *path;
+ int r;
+
+ r = sd_device_get_syspath(device, &syspath);
+ if (r < 0)
+ return r;
+
+ path = strjoina(syspath, "/driver");
+ r = readlink_value(path, &driver);
+ if (r >= 0) {
+ r = device_set_driver(device, driver);
+ if (r < 0)
+ return log_debug_errno(r, "sd-device: could not set driver for %s: %m", device->devpath);
+ } else if (r == -ENOENT)
+ device->driver_set = true;
+ else
+ return log_debug_errno(r, "sd-device: could not set driver for %s: %m", device->devpath);
+ }
+
+ *ret = device->driver;
+
+ return 0;
+}
+
+_public_ int sd_device_get_devpath(sd_device *device, const char **devpath) {
+ assert_return(device, -EINVAL);
+ assert_return(devpath, -EINVAL);
+
+ assert(device->devpath);
+ assert(device->devpath[0] == '/');
+
+ *devpath = device->devpath;
+
+ return 0;
+}
+
+_public_ int sd_device_get_devname(sd_device *device, const char **devname) {
+ int r;
+
+ assert_return(device, -EINVAL);
+ assert_return(devname, -EINVAL);
+
+ r = device_read_uevent_file(device);
+ if (r < 0)
+ return r;
+
+ if (!device->devname)
+ return -ENOENT;
+
+ assert(path_startswith(device->devname, "/dev/"));
+
+ *devname = device->devname;
+
+ return 0;
+}
+
+static int device_set_sysname(sd_device *device) {
+ _cleanup_free_ char *sysname = NULL;
+ const char *sysnum = NULL;
+ const char *pos;
+ size_t len = 0;
+
+ pos = strrchr(device->devpath, '/');
+ if (!pos)
+ return -EINVAL;
+ pos ++;
+
+ /* devpath is not a root directory */
+ if (*pos == '\0' || pos <= device->devpath)
+ return -EINVAL;
+
+ sysname = strdup(pos);
+ if (!sysname)
+ return -ENOMEM;
+
+ /* some devices have '!' in their name, change that to '/' */
+ while (sysname[len] != '\0') {
+ if (sysname[len] == '!')
+ sysname[len] = '/';
+
+ len ++;
+ }
+
+ /* trailing number */
+ while (len > 0 && isdigit(sysname[--len]))
+ sysnum = &sysname[len];
+
+ if (len == 0)
+ sysnum = NULL;
+
+ free(device->sysname);
+ device->sysname = sysname;
+ sysname = NULL;
+
+ device->sysnum = sysnum;
+
+ device->sysname_set = true;
+
+ return 0;
+}
+
+_public_ int sd_device_get_sysname(sd_device *device, const char **ret) {
+ int r;
+
+ assert_return(device, -EINVAL);
+ assert_return(ret, -EINVAL);
+
+ if (!device->sysname_set) {
+ r = device_set_sysname(device);
+ if (r < 0)
+ return r;
+ }
+
+ *ret = device->sysname;
+
+ return 0;
+}
+
+_public_ int sd_device_get_sysnum(sd_device *device, const char **ret) {
+ int r;
+
+ assert_return(device, -EINVAL);
+ assert_return(ret, -EINVAL);
+
+ if (!device->sysname_set) {
+ r = device_set_sysname(device);
+ if (r < 0)
+ return r;
+ }
+
+ *ret = device->sysnum;
+
+ return 0;
+}
+
+static bool is_valid_tag(const char *tag) {
+ assert(tag);
+
+ return !strchr(tag, ':') && !strchr(tag, ' ');
+}
+
+int device_add_tag(sd_device *device, const char *tag) {
+ int r;
+
+ assert(device);
+ assert(tag);
+
+ if (!is_valid_tag(tag))
+ return -EINVAL;
+
+ r = set_ensure_allocated(&device->tags, &string_hash_ops);
+ if (r < 0)
+ return r;
+
+ r = set_put_strdup(device->tags, tag);
+ if (r < 0)
+ return r;
+
+ device->tags_generation ++;
+ device->property_tags_outdated = true;
+
+ return 0;
+}
+
+int device_add_devlink(sd_device *device, const char *devlink) {
+ int r;
+
+ assert(device);
+ assert(devlink);
+
+ r = set_ensure_allocated(&device->devlinks, &string_hash_ops);
+ if (r < 0)
+ return r;
+
+ r = set_put_strdup(device->devlinks, devlink);
+ if (r < 0)
+ return r;
+
+ device->devlinks_generation ++;
+ device->property_devlinks_outdated = true;
+
+ return 0;
+}
+
+static int device_add_property_internal_from_string(sd_device *device, const char *str) {
+ _cleanup_free_ char *key = NULL;
+ char *value;
+
+ assert(device);
+ assert(str);
+
+ key = strdup(str);
+ if (!key)
+ return -ENOMEM;
+
+ value = strchr(key, '=');
+ if (!value)
+ return -EINVAL;
+
+ *value = '\0';
+
+ if (isempty(++value))
+ value = NULL;
+
+ return device_add_property_internal(device, key, value);
+}
+
+int device_set_usec_initialized(sd_device *device, const char *initialized) {
+ uint64_t usec_initialized;
+ int r;
+
+ assert(device);
+ assert(initialized);
+
+ r = safe_atou64(initialized, &usec_initialized);
+ if (r < 0)
+ return r;
+
+ r = device_add_property_internal(device, "USEC_INITIALIZED", initialized);
+ if (r < 0)
+ return r;
+
+ device->usec_initialized = usec_initialized;
+
+ return 0;
+}
+
+static int handle_db_line(sd_device *device, char key, const char *value) {
+ char *path;
+ int r;
+
+ assert(device);
+ assert(value);
+
+ switch (key) {
+ case 'G':
+ r = device_add_tag(device, value);
+ if (r < 0)
+ return r;
+
+ break;
+ case 'S':
+ path = strjoina("/dev/", value);
+ r = device_add_devlink(device, path);
+ if (r < 0)
+ return r;
+
+ break;
+ case 'E':
+ r = device_add_property_internal_from_string(device, value);
+ if (r < 0)
+ return r;
+
+ break;
+ case 'I':
+ r = device_set_usec_initialized(device, value);
+ if (r < 0)
+ return r;
+
+ break;
+ case 'L':
+ r = safe_atoi(value, &device->devlink_priority);
+ if (r < 0)
+ return r;
+
+ break;
+ case 'W':
+ r = safe_atoi(value, &device->watch_handle);
+ if (r < 0)
+ return r;
+
+ break;
+ default:
+ log_debug("device db: unknown key '%c'", key);
+ }
+
+ return 0;
+}
+
+int device_get_id_filename(sd_device *device, const char **ret) {
+ assert(device);
+ assert(ret);
+
+ if (!device->id_filename) {
+ _cleanup_free_ char *id = NULL;
+ const char *subsystem;
+ dev_t devnum;
+ int ifindex, r;
+
+ r = sd_device_get_subsystem(device, &subsystem);
+ if (r < 0)
+ return r;
+
+ r = sd_device_get_devnum(device, &devnum);
+ if (r < 0)
+ return r;
+
+ r = sd_device_get_ifindex(device, &ifindex);
+ if (r < 0)
+ return r;
+
+ if (major(devnum) > 0) {
+ assert(subsystem);
+
+ /* use dev_t -- b259:131072, c254:0 */
+ r = asprintf(&id, "%c%u:%u",
+ streq(subsystem, "block") ? 'b' : 'c',
+ major(devnum), minor(devnum));
+ if (r < 0)
+ return -ENOMEM;
+ } else if (ifindex > 0) {
+ /* use netdev ifindex -- n3 */
+ r = asprintf(&id, "n%u", ifindex);
+ if (r < 0)
+ return -ENOMEM;
+ } else {
+ /* use $subsys:$sysname -- pci:0000:00:1f.2
+ * sysname() has '!' translated, get it from devpath
+ */
+ const char *sysname;
+
+ sysname = basename(device->devpath);
+ if (!sysname)
+ return -EINVAL;
+
+ if (!subsystem)
+ return -EINVAL;
+
+ r = asprintf(&id, "+%s:%s", subsystem, sysname);
+ if (r < 0)
+ return -ENOMEM;
+ }
+
+ device->id_filename = id;
+ id = NULL;
+ }
+
+ *ret = device->id_filename;
+
+ return 0;
+}
+
+int device_read_db_aux(sd_device *device, bool force) {
+ _cleanup_free_ char *db = NULL;
+ char *path;
+ const char *id, *value;
+ char key;
+ size_t db_len;
+ unsigned i;
+ int r;
+
+ enum {
+ PRE_KEY,
+ KEY,
+ PRE_VALUE,
+ VALUE,
+ INVALID_LINE,
+ } state = PRE_KEY;
+
+ if (device->db_loaded || (!force && device->sealed))
+ return 0;
+
+ device->db_loaded = true;
+
+ r = device_get_id_filename(device, &id);
+ if (r < 0)
+ return r;
+
+ path = strjoina("/run/udev/data/", id);
+
+ r = read_full_file(path, &db, &db_len);
+ if (r < 0) {
+ if (r == -ENOENT)
+ return 0;
+ else {
+ log_debug("sd-device: failed to read db '%s': %s", path, strerror(-r));
+ return r;
+ }
+ }
+
+ /* devices with a database entry are initialized */
+ device->is_initialized = true;
+
+ for (i = 0; i < db_len; i++) {
+ switch (state) {
+ case PRE_KEY:
+ if (!strchr(NEWLINE, db[i])) {
+ key = db[i];
+
+ state = KEY;
+ }
+
+ break;
+ case KEY:
+ if (db[i] != ':') {
+ log_debug("sd-device: ignoring invalid db entry with key '%c'", key);
+
+ state = INVALID_LINE;
+ } else {
+ db[i] = '\0';
+
+ state = PRE_VALUE;
+ }
+
+ break;
+ case PRE_VALUE:
+ value = &db[i];
+
+ state = VALUE;
+
+ break;
+ case INVALID_LINE:
+ if (strchr(NEWLINE, db[i]))
+ state = PRE_KEY;
+
+ break;
+ case VALUE:
+ if (strchr(NEWLINE, db[i])) {
+ db[i] = '\0';
+ r = handle_db_line(device, key, value);
+ if (r < 0)
+ log_debug("sd-device: failed to handle db entry '%c:%s': %s", key, value, strerror(-r));
+
+ state = PRE_KEY;
+ }
+
+ break;
+ default:
+ assert_not_reached("invalid state when parsing db");
+ }
+ }
+
+ return 0;
+}
+
+static int device_read_db(sd_device *device) {
+ return device_read_db_aux(device, false);
+}
+
+_public_ int sd_device_get_is_initialized(sd_device *device, int *initialized) {
+ int r;
+
+ assert_return(device, -EINVAL);
+ assert_return(initialized, -EINVAL);
+
+ r = device_read_db(device);
+ if (r < 0)
+ return r;
+
+ *initialized = device->is_initialized;
+
+ return 0;
+}
+
+_public_ int sd_device_get_usec_since_initialized(sd_device *device, uint64_t *usec) {
+ usec_t now_ts;
+ int r;
+
+ assert_return(device, -EINVAL);
+ assert_return(usec, -EINVAL);
+
+ r = device_read_db(device);
+ if (r < 0)
+ return r;
+
+ if (!device->is_initialized)
+ return -EBUSY;
+
+ if (!device->usec_initialized)
+ return -ENODATA;
+
+ now_ts = now(clock_boottime_or_monotonic());
+
+ if (now_ts < device->usec_initialized)
+ return -EIO;
+
+ *usec = now_ts - device->usec_initialized;
+
+ return 0;
+}
+
+_public_ const char *sd_device_get_tag_first(sd_device *device) {
+ assert_return(device, NULL);
+
+ (void) device_read_db(device);
+
+ device->tags_iterator_generation = device->tags_generation;
+ device->tags_iterator = ITERATOR_FIRST;
+
+ return set_iterate(device->tags, &device->tags_iterator);
+}
+
+_public_ const char *sd_device_get_tag_next(sd_device *device) {
+ assert_return(device, NULL);
+
+ (void) device_read_db(device);
+
+ if (device->tags_iterator_generation != device->tags_generation)
+ return NULL;
+
+ return set_iterate(device->tags, &device->tags_iterator);
+}
+
+_public_ const char *sd_device_get_devlink_first(sd_device *device) {
+ assert_return(device, NULL);
+
+ (void) device_read_db(device);
+
+ device->devlinks_iterator_generation = device->devlinks_generation;
+ device->devlinks_iterator = ITERATOR_FIRST;
+
+ return set_iterate(device->devlinks, &device->devlinks_iterator);
+}
+
+_public_ const char *sd_device_get_devlink_next(sd_device *device) {
+ assert_return(device, NULL);
+
+ (void) device_read_db(device);
+
+ if (device->devlinks_iterator_generation != device->devlinks_generation)
+ return NULL;
+
+ return set_iterate(device->devlinks, &device->devlinks_iterator);
+}
+
+static int device_properties_prepare(sd_device *device) {
+ int r;
+
+ assert(device);
+
+ r = device_read_uevent_file(device);
+ if (r < 0)
+ return r;
+
+ r = device_read_db(device);
+ if (r < 0)
+ return r;
+
+ if (device->property_devlinks_outdated) {
+ char *devlinks = NULL;
+ const char *devlink;
+
+ devlink = sd_device_get_devlink_first(device);
+ if (devlink)
+ devlinks = strdupa(devlink);
+
+ while ((devlink = sd_device_get_devlink_next(device)))
+ devlinks = strjoina(devlinks, " ", devlink);
+
+ r = device_add_property_internal(device, "DEVLINKS", devlinks);
+ if (r < 0)
+ return r;
+
+ device->property_devlinks_outdated = false;
+ }
+
+ if (device->property_tags_outdated) {
+ char *tags = NULL;
+ const char *tag;
+
+ tag = sd_device_get_tag_first(device);
+ if (tag)
+ tags = strjoina(":", tag);
+
+ while ((tag = sd_device_get_tag_next(device)))
+ tags = strjoina(tags, ":", tag);
+
+ tags = strjoina(tags, ":");
+
+ r = device_add_property_internal(device, "TAGS", tags);
+ if (r < 0)
+ return r;
+
+ device->property_tags_outdated = false;
+ }
+
+ return 0;
+}
+
+_public_ const char *sd_device_get_property_first(sd_device *device, const char **_value) {
+ const char *key;
+ const char *value;
+ int r;
+
+ assert_return(device, NULL);
+
+ r = device_properties_prepare(device);
+ if (r < 0)
+ return NULL;
+
+ device->properties_iterator_generation = device->properties_generation;
+ device->properties_iterator = ITERATOR_FIRST;
+
+ value = ordered_hashmap_iterate(device->properties, &device->properties_iterator, (const void**)&key);
+
+ if (_value)
+ *_value = value;
+
+ return key;
+}
+
+_public_ const char *sd_device_get_property_next(sd_device *device, const char **_value) {
+ const char *key;
+ const char *value;
+ int r;
+
+ assert_return(device, NULL);
+
+ r = device_properties_prepare(device);
+ if (r < 0)
+ return NULL;
+
+ if (device->properties_iterator_generation != device->properties_generation)
+ return NULL;
+
+ value = ordered_hashmap_iterate(device->properties, &device->properties_iterator, (const void**)&key);
+
+ if (_value)
+ *_value = value;
+
+ return key;
+}
+
+static int device_sysattrs_read_all(sd_device *device) {
+ _cleanup_closedir_ DIR *dir = NULL;
+ const char *syspath;
+ struct dirent *dent;
+ int r;
+
+ assert(device);
+
+ if (device->sysattrs_read)
+ return 0;
+
+ r = sd_device_get_syspath(device, &syspath);
+ if (r < 0)
+ return r;
+
+ dir = opendir(syspath);
+ if (!dir)
+ return -errno;
+
+ r = set_ensure_allocated(&device->sysattrs, &string_hash_ops);
+ if (r < 0)
+ return r;
+
+ for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
+ char *path;
+ struct stat statbuf;
+
+ /* only handle symlinks and regular files */
+ if (dent->d_type != DT_LNK && dent->d_type != DT_REG)
+ continue;
+
+ path = strjoina(syspath, "/", dent->d_name);
+
+ if (lstat(path, &statbuf) != 0)
+ continue;
+
+ if (!(statbuf.st_mode & S_IRUSR))
+ continue;
+
+ r = set_put_strdup(device->sysattrs, dent->d_name);
+ if (r < 0)
+ return r;
+ }
+
+ device->sysattrs_read = true;
+
+ return 0;
+}
+
+_public_ const char *sd_device_get_sysattr_first(sd_device *device) {
+ int r;
+
+ assert_return(device, NULL);
+
+ if (!device->sysattrs_read) {
+ r = device_sysattrs_read_all(device);
+ if (r < 0) {
+ errno = -r;
+ return NULL;
+ }
+ }
+
+ device->sysattrs_iterator = ITERATOR_FIRST;
+
+ return set_iterate(device->sysattrs, &device->sysattrs_iterator);
+}
+
+_public_ const char *sd_device_get_sysattr_next(sd_device *device) {
+ assert_return(device, NULL);
+
+ if (!device->sysattrs_read)
+ return NULL;
+
+ return set_iterate(device->sysattrs, &device->sysattrs_iterator);
+}
+
+_public_ int sd_device_has_tag(sd_device *device, const char *tag) {
+ assert_return(device, -EINVAL);
+ assert_return(tag, -EINVAL);
+
+ (void) device_read_db(device);
+
+ return !!set_contains(device->tags, tag);
+}
+
+_public_ int sd_device_get_property_value(sd_device *device, const char *key, const char **_value) {
+ char *value;
+ int r;
+
+ assert_return(device, -EINVAL);
+ assert_return(key, -EINVAL);
+ assert_return(_value, -EINVAL);
+
+ r = device_properties_prepare(device);
+ if (r < 0)
+ return r;
+
+ value = ordered_hashmap_get(device->properties, key);
+ if (!value)
+ return -ENOENT;
+
+ *_value = value;
+
+ return 0;
+}
+
+/* replaces the value if it already exists */
+static int device_add_sysattr_value(sd_device *device, const char *_key, char *value) {
+ _cleanup_free_ char *key = NULL;
+ _cleanup_free_ char *value_old = NULL;
+ int r;
+
+ assert(device);
+ assert(_key);
+
+ r = hashmap_ensure_allocated(&device->sysattr_values, &string_hash_ops);
+ if (r < 0)
+ return r;
+
+ value_old = hashmap_remove2(device->sysattr_values, _key, (void **)&key);
+ if (!key) {
+ key = strdup(_key);
+ if (!key)
+ return -ENOMEM;
+ }
+
+ r = hashmap_put(device->sysattr_values, key, value);
+ if (r < 0)
+ return r;
+
+ key = NULL;
+
+ return 0;
+}
+
+static int device_get_sysattr_value(sd_device *device, const char *_key, const char **_value) {
+ const char *key = NULL, *value;
+
+ assert(device);
+ assert(_key);
+
+ value = hashmap_get2(device->sysattr_values, _key, (void **) &key);
+ if (!key)
+ return -ENOENT;
+
+ if (_value)
+ *_value = value;
+
+ return 0;
+}
+
+/* We cache all sysattr lookups. If an attribute does not exist, it is stored
+ * with a NULL value in the cache, otherwise the returned string is stored */
+_public_ int sd_device_get_sysattr_value(sd_device *device, const char *sysattr, const char **_value) {
+ _cleanup_free_ char *value = NULL;
+ const char *syspath, *cached_value = NULL;
+ char *path;
+ struct stat statbuf;
+ int r;
+
+ assert_return(device, -EINVAL);
+ assert_return(sysattr, -EINVAL);
+
+ /* look for possibly already cached result */
+ r = device_get_sysattr_value(device, sysattr, &cached_value);
+ if (r != -ENOENT) {
+ if (r < 0)
+ return r;
+
+ if (!cached_value)
+ /* we looked up the sysattr before and it did not exist */
+ return -ENOENT;
+
+ if (_value)
+ *_value = cached_value;
+
+ return 0;
+ }
+
+ r = sd_device_get_syspath(device, &syspath);
+ if (r < 0)
+ return r;
+
+ path = strjoina(syspath, "/", sysattr);
+ r = lstat(path, &statbuf);
+ if (r < 0) {
+ /* remember that we could not access the sysattr */
+ r = device_add_sysattr_value(device, sysattr, NULL);
+ if (r < 0)
+ return r;
+
+ return -ENOENT;
+ } else if (S_ISLNK(statbuf.st_mode)) {
+ /* Some core links return only the last element of the target path,
+ * these are just values, the paths should not be exposed. */
+ if (STR_IN_SET(sysattr, "driver", "subsystem", "module")) {
+ r = readlink_value(path, &value);
+ if (r < 0)
+ return r;
+ } else
+ return -EINVAL;
+ } else if (S_ISDIR(statbuf.st_mode)) {
+ /* skip directories */
+ return -EINVAL;
+ } else if (!(statbuf.st_mode & S_IRUSR)) {
+ /* skip non-readable files */
+ return -EPERM;
+ } else {
+ size_t size;
+
+ /* read attribute value */
+ r = read_full_file(path, &value, &size);
+ if (r < 0)
+ return r;
+
+ /* drop trailing newlines */
+ while (size > 0 && value[--size] == '\n')
+ value[size] = '\0';
+ }
+
+ r = device_add_sysattr_value(device, sysattr, value);
+ if (r < 0)
+ return r;
+
+ *_value = value;
+ value = NULL;
+
+ return 0;
+}
+
+static void device_remove_sysattr_value(sd_device *device, const char *_key) {
+ _cleanup_free_ char *key = NULL;
+ _cleanup_free_ char *value = NULL;
+
+ assert(device);
+ assert(_key);
+
+ value = hashmap_remove2(device->sysattr_values, _key, (void **) &key);
+
+ return;
+}
+
+/* set the attribute and save it in the cache. If a NULL value is passed the
+ * attribute is cleared from the cache */
+_public_ int sd_device_set_sysattr_value(sd_device *device, const char *sysattr, char *_value) {
+ _cleanup_close_ int fd = -1;
+ _cleanup_free_ char *value = NULL;
+ const char *syspath;
+ char *path;
+ struct stat statbuf;
+ size_t value_len = 0;
+ ssize_t size;
+ int r;
+
+ assert_return(device, -EINVAL);
+ assert_return(sysattr, -EINVAL);
+
+ if (!_value) {
+ device_remove_sysattr_value(device, sysattr);
+
+ return 0;
+ }
+
+ r = sd_device_get_syspath(device, &syspath);
+ if (r < 0)
+ return r;
+
+ path = strjoina(syspath, "/", sysattr);
+ r = lstat(path, &statbuf);
+ if (r < 0) {
+ value = strdup("");
+ if (!value)
+ return -ENOMEM;
+
+ r = device_add_sysattr_value(device, sysattr, value);
+ if (r < 0)
+ return r;
+
+ return -ENXIO;
+ }
+
+ if (S_ISLNK(statbuf.st_mode))
+ return -EINVAL;
+
+ /* skip directories */
+ if (S_ISDIR(statbuf.st_mode))
+ return -EISDIR;
+
+ /* skip non-readable files */
+ if ((statbuf.st_mode & S_IRUSR) == 0)
+ return -EACCES;
+
+ value_len = strlen(_value);
+
+ /* drop trailing newlines */
+ while (value_len > 0 && _value[value_len - 1] == '\n')
+ _value[--value_len] = '\0';
+
+ /* value length is limited to 4k */
+ if (value_len > 4096)
+ return -EINVAL;
+
+ fd = open(path, O_WRONLY | O_CLOEXEC);
+ if (fd < 0)
+ return -errno;
+
+ value = strdup(_value);
+ if (!value)
+ return -ENOMEM;
+
+ size = write(fd, value, value_len);
+ if (size < 0)
+ return -errno;
+
+ if ((size_t)size != value_len)
+ return -EIO;
+
+ r = device_add_sysattr_value(device, sysattr, value);
+ if (r < 0)
+ return r;
+
+ value = NULL;
+
+ return 0;
+}
diff --git a/src/libsystemd/sd-event/sd-event.c b/src/libsystemd/sd-event/sd-event.c
index 25089a0335..cc8bc50c04 100644
--- a/src/libsystemd/sd-event/sd-event.c
+++ b/src/libsystemd/sd-event/sd-event.c
@@ -22,7 +22,6 @@
#include <sys/epoll.h>
#include <sys/timerfd.h>
#include <sys/wait.h>
-#include <pthread.h>
#include "sd-id128.h"
#include "sd-daemon.h"
@@ -34,10 +33,10 @@
#include "missing.h"
#include "set.h"
#include "list.h"
+#include "signal-util.h"
#include "sd-event.h"
-#define EPOLL_QUEUE_MAX 512U
#define DEFAULT_ACCURACY_USEC (250 * USEC_PER_MSEC)
typedef enum EventSourceType {
@@ -463,7 +462,7 @@ _public_ sd_event* sd_event_unref(sd_event *e) {
static bool event_pid_changed(sd_event *e) {
assert(e);
- /* We don't support people creating am event loop and keeping
+ /* We don't support people creating an event loop and keeping
* it around over a fork(). Let's complain. */
return e->original_pid != getpid();
@@ -921,7 +920,7 @@ _public_ int sd_event_add_time(
callback = time_exit_callback;
type = clock_to_event_source_type(clock);
- assert_return(type >= 0, -ENOTSUP);
+ assert_return(type >= 0, -EOPNOTSUPP);
d = event_get_clock_data(e, type);
assert(d);
@@ -2236,7 +2235,7 @@ static int dispatch_exit(sd_event *e) {
r = source_dispatch(p);
- e->state = SD_EVENT_PASSIVE;
+ e->state = SD_EVENT_INITIAL;
sd_event_unref(e);
return r;
@@ -2305,7 +2304,7 @@ _public_ int sd_event_prepare(sd_event *e) {
assert_return(e, -EINVAL);
assert_return(!event_pid_changed(e), -ECHILD);
assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
- assert_return(e->state == SD_EVENT_PASSIVE, -EBUSY);
+ assert_return(e->state == SD_EVENT_INITIAL, -EBUSY);
if (e->exit_requested)
goto pending;
@@ -2339,15 +2338,15 @@ _public_ int sd_event_prepare(sd_event *e) {
if (event_next_pending(e) || e->need_process_child)
goto pending;
- e->state = SD_EVENT_PREPARED;
+ e->state = SD_EVENT_ARMED;
return 0;
pending:
- e->state = SD_EVENT_PREPARED;
+ e->state = SD_EVENT_ARMED;
r = sd_event_wait(e, 0);
if (r == 0)
- e->state = SD_EVENT_PREPARED;
+ e->state = SD_EVENT_ARMED;
return r;
}
@@ -2360,14 +2359,14 @@ _public_ int sd_event_wait(sd_event *e, uint64_t timeout) {
assert_return(e, -EINVAL);
assert_return(!event_pid_changed(e), -ECHILD);
assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
- assert_return(e->state == SD_EVENT_PREPARED, -EBUSY);
+ assert_return(e->state == SD_EVENT_ARMED, -EBUSY);
if (e->exit_requested) {
e->state = SD_EVENT_PENDING;
return 1;
}
- ev_queue_max = CLAMP(e->n_sources, 1U, EPOLL_QUEUE_MAX);
+ ev_queue_max = MAX(e->n_sources, 1u);
ev_queue = newa(struct epoll_event, ev_queue_max);
m = epoll_wait(e->epoll_fd, ev_queue, ev_queue_max,
@@ -2448,7 +2447,7 @@ _public_ int sd_event_wait(sd_event *e, uint64_t timeout) {
r = 0;
finish:
- e->state = SD_EVENT_PASSIVE;
+ e->state = SD_EVENT_INITIAL;
return r;
}
@@ -2471,14 +2470,14 @@ _public_ int sd_event_dispatch(sd_event *e) {
e->state = SD_EVENT_RUNNING;
r = source_dispatch(p);
- e->state = SD_EVENT_PASSIVE;
+ e->state = SD_EVENT_INITIAL;
sd_event_unref(e);
return r;
}
- e->state = SD_EVENT_PASSIVE;
+ e->state = SD_EVENT_INITIAL;
return 1;
}
@@ -2489,19 +2488,23 @@ _public_ int sd_event_run(sd_event *e, uint64_t timeout) {
assert_return(e, -EINVAL);
assert_return(!event_pid_changed(e), -ECHILD);
assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
- assert_return(e->state == SD_EVENT_PASSIVE, -EBUSY);
+ assert_return(e->state == SD_EVENT_INITIAL, -EBUSY);
r = sd_event_prepare(e);
- if (r > 0)
- return sd_event_dispatch(e);
- else if (r < 0)
- return r;
+ if (r == 0)
+ /* There was nothing? Then wait... */
+ r = sd_event_wait(e, timeout);
- r = sd_event_wait(e, timeout);
- if (r > 0)
- return sd_event_dispatch(e);
- else
- return r;
+ if (r > 0) {
+ /* There's something now, then let's dispatch it */
+ r = sd_event_dispatch(e);
+ if (r < 0)
+ return r;
+
+ return 1;
+ }
+
+ return r;
}
_public_ int sd_event_loop(sd_event *e) {
@@ -2509,7 +2512,7 @@ _public_ int sd_event_loop(sd_event *e) {
assert_return(e, -EINVAL);
assert_return(!event_pid_changed(e), -ECHILD);
- assert_return(e->state == SD_EVENT_PASSIVE, -EBUSY);
+ assert_return(e->state == SD_EVENT_INITIAL, -EBUSY);
sd_event_ref(e);
diff --git a/src/libsystemd/sd-event/test-event.c b/src/libsystemd/sd-event/test-event.c
index 721700be7b..94e98e0077 100644
--- a/src/libsystemd/sd-event/test-event.c
+++ b/src/libsystemd/sd-event/test-event.c
@@ -23,6 +23,7 @@
#include "log.h"
#include "util.h"
#include "macro.h"
+#include "signal-util.h"
static int prepare_handler(sd_event_source *s, void *userdata) {
log_info("preparing %c", PTR_TO_INT(userdata));
diff --git a/src/libsystemd/sd-hwdb/sd-hwdb.c b/src/libsystemd/sd-hwdb/sd-hwdb.c
index 61c7b446b3..2a0e00f7d2 100644
--- a/src/libsystemd/sd-hwdb/sd-hwdb.c
+++ b/src/libsystemd/sd-hwdb/sd-hwdb.c
@@ -23,10 +23,8 @@
#include <errno.h>
#include <string.h>
#include <inttypes.h>
-#include <ctype.h>
#include <stdlib.h>
#include <fnmatch.h>
-#include <getopt.h>
#include <sys/mman.h>
#include "sd-hwdb.h"
@@ -319,7 +317,7 @@ _public_ int sd_hwdb_new(sd_hwdb **ret) {
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_path);
- return -EINVAL;;
+ return -EINVAL;
}
log_debug("=== trie on-disk ===");
diff --git a/src/libsystemd/sd-id128/sd-id128.c b/src/libsystemd/sd-id128/sd-id128.c
index c876f6e381..46f2181ea8 100644
--- a/src/libsystemd/sd-id128/sd-id128.c
+++ b/src/libsystemd/sd-id128/sd-id128.c
@@ -26,6 +26,7 @@
#include "util.h"
#include "macro.h"
#include "sd-id128.h"
+#include "random-util.h"
_public_ char *sd_id128_to_string(sd_id128_t id, char s[33]) {
unsigned n;
@@ -108,9 +109,9 @@ _public_ int sd_id128_get_machine(sd_id128_t *ret) {
static thread_local bool saved_machine_id_valid = false;
_cleanup_close_ int fd = -1;
char buf[33];
- ssize_t k;
unsigned j;
sd_id128_t t;
+ int r;
assert_return(ret, -EINVAL);
@@ -123,13 +124,9 @@ _public_ int sd_id128_get_machine(sd_id128_t *ret) {
if (fd < 0)
return -errno;
- k = loop_read(fd, buf, 33, false);
- if (k < 0)
- return (int) k;
-
- if (k != 33)
- return -EIO;
-
+ r = loop_read_exact(fd, buf, 33, false);
+ if (r < 0)
+ return r;
if (buf[32] !='\n')
return -EIO;
@@ -157,10 +154,10 @@ _public_ int sd_id128_get_boot(sd_id128_t *ret) {
static thread_local bool saved_boot_id_valid = false;
_cleanup_close_ int fd = -1;
char buf[36];
- ssize_t k;
unsigned j;
sd_id128_t t;
char *p;
+ int r;
assert_return(ret, -EINVAL);
@@ -173,22 +170,19 @@ _public_ int sd_id128_get_boot(sd_id128_t *ret) {
if (fd < 0)
return -errno;
- k = loop_read(fd, buf, 36, false);
- if (k < 0)
- return (int) k;
-
- if (k != 36)
- return -EIO;
+ r = loop_read_exact(fd, buf, 36, false);
+ if (r < 0)
+ return r;
for (j = 0, p = buf; j < 16; j++) {
int a, b;
- if (p >= buf + k - 1)
+ if (p >= buf + 35)
return -EIO;
if (*p == '-') {
p++;
- if (p >= buf + k - 1)
+ if (p >= buf + 35)
return -EIO;
}
diff --git a/src/libsystemd/sd-login/sd-login.c b/src/libsystemd/sd-login/sd-login.c
index f71749f72d..ed8aa0952a 100644
--- a/src/libsystemd/sd-login/sd-login.c
+++ b/src/libsystemd/sd-login/sd-login.c
@@ -31,6 +31,7 @@
#include "strv.h"
#include "fileio.h"
#include "login-shared.h"
+#include "formats-util.h"
#include "sd-login.h"
_public_ int sd_pid_get_session(pid_t pid, char **session) {
@@ -73,6 +74,14 @@ _public_ int sd_pid_get_slice(pid_t pid, char **slice) {
return cg_pid_get_slice(pid, slice);
}
+_public_ int sd_pid_get_user_slice(pid_t pid, char **slice) {
+
+ assert_return(pid >= 0, -EINVAL);
+ assert_return(slice, -EINVAL);
+
+ return cg_pid_get_user_slice(pid, slice);
+}
+
_public_ int sd_pid_get_owner_uid(pid_t pid, uid_t *uid) {
assert_return(pid >= 0, -EINVAL);
@@ -82,7 +91,7 @@ _public_ int sd_pid_get_owner_uid(pid_t pid, uid_t *uid) {
}
_public_ int sd_peer_get_session(int fd, char **session) {
- struct ucred ucred;
+ struct ucred ucred = {};
int r;
assert_return(fd >= 0, -EINVAL);
@@ -165,6 +174,20 @@ _public_ int sd_peer_get_slice(int fd, char **slice) {
return cg_pid_get_slice(ucred.pid, slice);
}
+_public_ int sd_peer_get_user_slice(int fd, char **slice) {
+ struct ucred ucred;
+ int r;
+
+ assert_return(fd >= 0, -EINVAL);
+ assert_return(slice, -EINVAL);
+
+ r = getpeercred(fd, &ucred);
+ if (r < 0)
+ return r;
+
+ return cg_pid_get_user_slice(ucred.pid, slice);
+}
+
static int file_of_uid(uid_t uid, char **p) {
assert(p);
@@ -496,9 +519,9 @@ _public_ int sd_session_get_desktop(const char *session, char **desktop) {
if (r < 0)
return r;
- t = cunescape(escaped);
- if (!t)
- return -ENOMEM;
+ r = cunescape(escaped, 0, &t);
+ if (r < 0)
+ return r;
*desktop = t;
return 0;
diff --git a/src/libsystemd/sd-login/test-login.c b/src/libsystemd/sd-login/test-login.c
index 2802e8246d..05affa442d 100644
--- a/src/libsystemd/sd-login/test-login.c
+++ b/src/libsystemd/sd-login/test-login.c
@@ -26,6 +26,7 @@
#include "util.h"
#include "strv.h"
+#include "formats-util.h"
static void test_login(void) {
_cleanup_close_pair_ int pair[2] = { -1, -1 };
diff --git a/src/libsystemd/sd-network/network-util.h b/src/libsystemd/sd-network/network-util.h
index 02bd545526..11a0012348 100644
--- a/src/libsystemd/sd-network/network-util.h
+++ b/src/libsystemd/sd-network/network-util.h
@@ -21,7 +21,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "util.h"
#include "sd-network.h"
DEFINE_TRIVIAL_CLEANUP_FUNC(sd_network_monitor*, sd_network_monitor_unref);
diff --git a/src/libsystemd/sd-network/sd-network.c b/src/libsystemd/sd-network/sd-network.c
index c4713feb5d..db1f6997cb 100644
--- a/src/libsystemd/sd-network/sd-network.c
+++ b/src/libsystemd/sd-network/sd-network.c
@@ -20,19 +20,16 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/inotify.h>
#include <poll.h>
-#include <net/if.h>
#include "util.h"
#include "macro.h"
#include "strv.h"
#include "fileio.h"
#include "sd-network.h"
-#include "network-internal.h"
_public_ int sd_network_get_operational_state(char **state) {
_cleanup_free_ char *s = NULL;
@@ -264,6 +261,14 @@ _public_ int sd_network_link_get_domains(int ifindex, char ***ret) {
return network_get_link_strv("DOMAINS", ifindex, ret);
}
+_public_ int sd_network_link_get_carrier_bound_to(int ifindex, char ***ret) {
+ return network_get_link_strv("CARRIER_BOUND_TO", ifindex, ret);
+}
+
+_public_ int sd_network_link_get_carrier_bound_by(int ifindex, char ***ret) {
+ return network_get_link_strv("CARRIER_BOUND_BY", ifindex, ret);
+}
+
_public_ int sd_network_link_get_wildcard_domain(int ifindex) {
int r;
_cleanup_free_ char *p = NULL, *s = NULL;
diff --git a/src/libsystemd/sd-path/sd-path.c b/src/libsystemd/sd-path/sd-path.c
index 651fceb79d..7363be2794 100644
--- a/src/libsystemd/sd-path/sd-path.c
+++ b/src/libsystemd/sd-path/sd-path.c
@@ -323,7 +323,7 @@ static int get_path(uint64_t type, char **buffer, const char **ret) {
return from_user_dir("XDG_DESKTOP_DIR", buffer, ret);
}
- return -ENOTSUP;
+ return -EOPNOTSUPP;
}
_public_ int sd_path_home(uint64_t type, const char *suffix, char **path) {
@@ -552,7 +552,7 @@ static int get_search(uint64_t type, char ***list) {
NULL);
}
- return -ENOTSUP;
+ return -EOPNOTSUPP;
}
_public_ int sd_path_search(uint64_t type, const char *suffix, char ***paths) {
diff --git a/src/libsystemd/sd-resolve/sd-resolve.c b/src/libsystemd/sd-resolve/sd-resolve.c
index 6448280c4f..b0dc822591 100644
--- a/src/libsystemd/sd-resolve/sd-resolve.c
+++ b/src/libsystemd/sd-resolve/sd-resolve.c
@@ -19,24 +19,13 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <assert.h>
-#include <fcntl.h>
#include <signal.h>
#include <unistd.h>
-#include <sys/select.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
-#include <sys/wait.h>
-#include <sys/types.h>
-#include <pwd.h>
-#include <netinet/in.h>
-#include <arpa/nameser.h>
#include <resolv.h>
-#include <dirent.h>
-#include <sys/time.h>
-#include <sys/resource.h>
#include <stdint.h>
#include <pthread.h>
#include <sys/prctl.h>
@@ -461,7 +450,7 @@ static int handle_request(int out_fd, const Packet *packet, size_t length) {
assert(length >= sizeof(ResRequest));
assert(length == sizeof(ResRequest) + res_req->dname_len);
- dname = (const char *) req + sizeof(ResRequest);
+ dname = (const char *) res_req + sizeof(ResRequest);
if (req->type == REQUEST_RES_QUERY)
ret = res_query(dname, res_req->class, res_req->type, (unsigned char *) &answer, BUFSIZE);
@@ -664,7 +653,7 @@ static void resolve_free(sd_resolve *resolve) {
/* Send one termination packet for each worker */
for (i = 0; i < resolve->n_valid_workers; i++)
- send(resolve->fds[REQUEST_SEND_FD], &req, req.length, MSG_NOSIGNAL);
+ (void) send(resolve->fds[REQUEST_SEND_FD], &req, req.length, MSG_NOSIGNAL);
}
/* Now terminate them and wait until they are gone. */
diff --git a/src/libsystemd/sd-resolve/test-resolve.c b/src/libsystemd/sd-resolve/test-resolve.c
index d08e1b5a05..354a4071b7 100644
--- a/src/libsystemd/sd-resolve/test-resolve.c
+++ b/src/libsystemd/sd-resolve/test-resolve.c
@@ -21,14 +21,11 @@
***/
#include <string.h>
-#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <netinet/in.h>
-#include <arpa/nameser.h>
#include <resolv.h>
-#include <signal.h>
#include <errno.h>
#include "socket-util.h"
@@ -49,7 +46,7 @@ static int getaddrinfo_handler(sd_resolve_query *q, int ret, const struct addrin
for (i = ai; i; i = i->ai_next) {
_cleanup_free_ char *addr = NULL;
- assert_se(sockaddr_pretty(i->ai_addr, i->ai_addrlen, false, &addr) == 0);
+ assert_se(sockaddr_pretty(i->ai_addr, i->ai_addrlen, false, true, &addr) == 0);
puts(addr);
}
diff --git a/src/libsystemd/sd-rtnl/local-addresses.h b/src/libsystemd/sd-rtnl/local-addresses.h
index ef7def530d..bdc28d3510 100644
--- a/src/libsystemd/sd-rtnl/local-addresses.h
+++ b/src/libsystemd/sd-rtnl/local-addresses.h
@@ -21,10 +21,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <inttypes.h>
-#include <sys/types.h>
-#include <assert.h>
-#include <sys/socket.h>
#include "sd-rtnl.h"
#include "in-addr-util.h"
diff --git a/src/libsystemd/sd-rtnl/rtnl-internal.h b/src/libsystemd/sd-rtnl/rtnl-internal.h
index a192198419..05b88b1ad8 100644
--- a/src/libsystemd/sd-rtnl/rtnl-internal.h
+++ b/src/libsystemd/sd-rtnl/rtnl-internal.h
@@ -109,6 +109,7 @@ struct sd_rtnl_message {
size_t *rta_offset_tb[RTNL_CONTAINER_DEPTH];
unsigned short rta_tb_size[RTNL_CONTAINER_DEPTH];
bool sealed:1;
+ bool broadcast:1;
sd_rtnl_message *next; /* next in a chain of multi-part messages */
};
diff --git a/src/libsystemd/sd-rtnl/rtnl-message.c b/src/libsystemd/sd-rtnl/rtnl-message.c
index 276591f31b..bab2a4ff08 100644
--- a/src/libsystemd/sd-rtnl/rtnl-message.c
+++ b/src/libsystemd/sd-rtnl/rtnl-message.c
@@ -20,11 +20,11 @@
***/
#include <netinet/in.h>
-#include <netinet/ether.h>
#include <stdbool.h>
#include <unistd.h>
#include "util.h"
+#include "formats-util.h"
#include "refcnt.h"
#include "missing.h"
@@ -45,7 +45,7 @@ static int message_new_empty(sd_rtnl *rtnl, sd_rtnl_message **ret) {
/* Note that 'rtnl' is currently unused, if we start using it internally
we must take care to avoid problems due to mutual references between
- busses and their queued messages. See sd-bus.
+ buses and their queued messages. See sd-bus.
*/
m = new0(sd_rtnl_message, 1);
@@ -649,13 +649,13 @@ int sd_rtnl_message_get_family(sd_rtnl_message *m, int *family) {
return 0;
}
- return -ENOTSUP;
+ return -EOPNOTSUPP;
}
int sd_rtnl_message_is_broadcast(sd_rtnl_message *m) {
assert_return(m, -EINVAL);
- return !m->hdr->nlmsg_pid;
+ return m->broadcast;
}
int sd_rtnl_message_link_get_ifindex(sd_rtnl_message *m, int *ifindex) {
@@ -1475,7 +1475,7 @@ static int socket_recv_message(int fd, struct iovec *iov, uint32_t *_group, bool
return 0;
}
- if (group)
+ if (_group)
*_group = group;
return r;
@@ -1501,7 +1501,7 @@ int socket_read_message(sd_rtnl *rtnl) {
assert(rtnl->rbuffer_allocated >= sizeof(struct nlmsghdr));
/* read nothing, just get the pending message size */
- r = socket_recv_message(rtnl->fd, &iov, &group, true);
+ r = socket_recv_message(rtnl->fd, &iov, NULL, true);
if (r <= 0)
return r;
else
@@ -1555,13 +1555,15 @@ int socket_read_message(sd_rtnl *rtnl) {
/* finished reading multi-part message */
done = true;
- continue;
+ /* if first is not defined, put NLMSG_DONE into the receive queue. */
+ if (first)
+ continue;
}
/* check that we support this message type */
r = type_system_get_type(NULL, &nl_type, new_msg->nlmsg_type);
if (r < 0) {
- if (r == -ENOTSUP)
+ if (r == -EOPNOTSUPP)
log_debug("sd-rtnl: ignored message with unknown type: %i",
new_msg->nlmsg_type);
@@ -1578,6 +1580,8 @@ int socket_read_message(sd_rtnl *rtnl) {
if (r < 0)
return r;
+ m->broadcast = !!group;
+
m->hdr = memdup(new_msg, new_msg->nlmsg_len);
if (!m->hdr)
return -ENOMEM;
diff --git a/src/libsystemd/sd-rtnl/rtnl-types.c b/src/libsystemd/sd-rtnl/rtnl-types.c
index a4c71f3785..d211684ff1 100644
--- a/src/libsystemd/sd-rtnl/rtnl-types.c
+++ b/src/libsystemd/sd-rtnl/rtnl-types.c
@@ -19,7 +19,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stddef.h>
#include <stdint.h>
#include <sys/socket.h>
#include <linux/netlink.h>
@@ -91,6 +90,30 @@ static const NLType rtnl_link_info_data_vxlan_types[IFLA_VXLAN_MAX+1] = {
[IFLA_VXLAN_L3MISS] = { .type = NLA_U8 },
};
+static const NLType rtnl_bond_arp_target_types[BOND_ARP_TARGETS_MAX + 1] = {
+ [BOND_ARP_TARGETS_0] = { .type = NLA_U32 },
+ [BOND_ARP_TARGETS_1] = { .type = NLA_U32 },
+ [BOND_ARP_TARGETS_2] = { .type = NLA_U32 },
+ [BOND_ARP_TARGETS_3] = { .type = NLA_U32 },
+ [BOND_ARP_TARGETS_4] = { .type = NLA_U32 },
+ [BOND_ARP_TARGETS_5] = { .type = NLA_U32 },
+ [BOND_ARP_TARGETS_6] = { .type = NLA_U32 },
+ [BOND_ARP_TARGETS_7] = { .type = NLA_U32 },
+ [BOND_ARP_TARGETS_8] = { .type = NLA_U32 },
+ [BOND_ARP_TARGETS_9] = { .type = NLA_U32 },
+ [BOND_ARP_TARGETS_10] = { .type = NLA_U32 },
+ [BOND_ARP_TARGETS_11] = { .type = NLA_U32 },
+ [BOND_ARP_TARGETS_12] = { .type = NLA_U32 },
+ [BOND_ARP_TARGETS_13] = { .type = NLA_U32 },
+ [BOND_ARP_TARGETS_14] = { .type = NLA_U32 },
+ [BOND_ARP_TARGETS_MAX] = { .type = NLA_U32 },
+};
+
+static const NLTypeSystem rtnl_bond_arp_type_system = {
+ .max = ELEMENTSOF(rtnl_bond_arp_target_types) - 1,
+ .types = rtnl_bond_arp_target_types,
+};
+
static const NLType rtnl_link_info_data_bond_types[IFLA_BOND_MAX + 1] = {
[IFLA_BOND_MODE] = { .type = NLA_U8 },
[IFLA_BOND_ACTIVE_SLAVE] = { .type = NLA_U32 },
@@ -99,7 +122,7 @@ static const NLType rtnl_link_info_data_bond_types[IFLA_BOND_MAX + 1] = {
[IFLA_BOND_DOWNDELAY] = { .type = NLA_U32 },
[IFLA_BOND_USE_CARRIER] = { .type = NLA_U8 },
[IFLA_BOND_ARP_INTERVAL] = { .type = NLA_U32 },
- [IFLA_BOND_ARP_IP_TARGET] = { .type = NLA_NESTED },
+ [IFLA_BOND_ARP_IP_TARGET] = { .type = NLA_NESTED, .type_system = &rtnl_bond_arp_type_system },
[IFLA_BOND_ARP_VALIDATE] = { .type = NLA_U32 },
[IFLA_BOND_ARP_ALL_TARGETS] = { .type = NLA_U32 },
[IFLA_BOND_PRIMARY] = { .type = NLA_U32 },
@@ -181,6 +204,7 @@ static const char* const nl_union_link_info_data_table[_NL_UNION_LINK_INFO_DATA_
[NL_UNION_LINK_INFO_DATA_IP6GRETAP_TUNNEL] = "ip6gretap",
[NL_UNION_LINK_INFO_DATA_SIT_TUNNEL] = "sit",
[NL_UNION_LINK_INFO_DATA_VTI_TUNNEL] = "vti",
+ [NL_UNION_LINK_INFO_DATA_VTI6_TUNNEL] = "vti6",
[NL_UNION_LINK_INFO_DATA_IP6TNL_TUNNEL] = "ip6tnl",
};
@@ -215,6 +239,8 @@ static const NLTypeSystem rtnl_link_info_data_type_systems[_NL_UNION_LINK_INFO_D
.types = rtnl_link_info_data_iptun_types },
[NL_UNION_LINK_INFO_DATA_VTI_TUNNEL] = { .max = ELEMENTSOF(rtnl_link_info_data_ipvti_types) - 1,
.types = rtnl_link_info_data_ipvti_types },
+ [NL_UNION_LINK_INFO_DATA_VTI6_TUNNEL] = { .max = ELEMENTSOF(rtnl_link_info_data_ipvti_types) - 1,
+ .types = rtnl_link_info_data_ipvti_types },
[NL_UNION_LINK_INFO_DATA_IP6TNL_TUNNEL] = { .max = ELEMENTSOF(rtnl_link_info_data_ip6tnl_types) - 1,
.types = rtnl_link_info_data_ip6tnl_types },
@@ -349,7 +375,9 @@ static const NLTypeSystem rtnl_link_type_system = {
.types = rtnl_link_types,
};
-static const NLType rtnl_address_types[IFA_MAX + 1] = {
+/* IFA_FLAGS was defined in kernel 3.14, but we still support older
+ * kernels where IFA_MAX is lower. */
+static const NLType rtnl_address_types[CONST_MAX(IFA_MAX, IFA_FLAGS) + 1] = {
[IFA_ADDRESS] = { .type = NLA_IN_ADDR },
[IFA_LOCAL] = { .type = NLA_IN_ADDR },
[IFA_LABEL] = { .type = NLA_STRING, .size = IFNAMSIZ - 1 },
@@ -359,9 +387,7 @@ static const NLType rtnl_address_types[IFA_MAX + 1] = {
[IFA_ANYCAST],
[IFA_MULTICAST],
*/
-#ifdef IFA_FLAGS
[IFA_FLAGS] = { .type = NLA_U32 },
-#endif
};
static const NLTypeSystem rtnl_address_type_system = {
@@ -412,6 +438,7 @@ static const NLTypeSystem rtnl_neigh_type_system = {
};
static const NLType rtnl_types[RTM_MAX + 1] = {
+ [NLMSG_DONE] = { .type = NLA_META, .size = 0 },
[NLMSG_ERROR] = { .type = NLA_META, .size = sizeof(struct nlmsgerr) },
[RTM_NEWLINK] = { .type = NLA_NESTED, .type_system = &rtnl_link_type_system, .size = sizeof(struct ifinfomsg) },
[RTM_DELLINK] = { .type = NLA_NESTED, .type_system = &rtnl_link_type_system, .size = sizeof(struct ifinfomsg) },
@@ -444,12 +471,12 @@ int type_system_get_type(const NLTypeSystem *type_system, const NLType **ret, ui
assert(type_system->types);
if (type > type_system->max)
- return -ENOTSUP;
+ return -EOPNOTSUPP;
nl_type = &type_system->types[type];
if (nl_type->type == NLA_UNSPEC)
- return -ENOTSUP;
+ return -EOPNOTSUPP;
*ret = nl_type;
@@ -466,8 +493,7 @@ int type_system_get_type_system(const NLTypeSystem *type_system, const NLTypeSys
if (r < 0)
return r;
- assert_return(nl_type->type == NLA_NESTED, -EINVAL);
-
+ assert(nl_type->type == NLA_NESTED);
assert(nl_type->type_system);
*ret = nl_type->type_system;
@@ -485,8 +511,7 @@ int type_system_get_type_system_union(const NLTypeSystem *type_system, const NLT
if (r < 0)
return r;
- assert_return(nl_type->type == NLA_UNION, -EINVAL);
-
+ assert(nl_type->type == NLA_UNION);
assert(nl_type->type_system_union);
*ret = nl_type->type_system_union;
@@ -498,7 +523,7 @@ int type_system_union_get_type_system(const NLTypeSystemUnion *type_system_union
int type;
assert(type_system_union);
- assert_return(type_system_union->match_type == NL_MATCH_SIBLING, -EINVAL);
+ assert(type_system_union->match_type == NL_MATCH_SIBLING);
assert(type_system_union->lookup);
assert(type_system_union->type_systems);
assert(ret);
@@ -506,7 +531,7 @@ int type_system_union_get_type_system(const NLTypeSystemUnion *type_system_union
type = type_system_union->lookup(key);
if (type < 0)
- return -ENOTSUP;
+ return -EOPNOTSUPP;
assert(type < type_system_union->num);
@@ -520,17 +545,15 @@ int type_system_union_protocol_get_type_system(const NLTypeSystemUnion *type_sys
assert(type_system_union);
assert(type_system_union->type_systems);
+ assert(type_system_union->match_type == NL_MATCH_PROTOCOL);
assert(ret);
- assert_return(type_system_union->match_type == NL_MATCH_PROTOCOL, -EINVAL);
- assert_return(protocol < type_system_union->num, -EINVAL);
if (protocol >= type_system_union->num)
- return -ENOTSUP;
+ return -EOPNOTSUPP;
type_system = &type_system_union->type_systems[protocol];
-
- if (!type_system)
- return -ENOTSUP;
+ if (type_system->max == 0)
+ return -EOPNOTSUPP;
*ret = type_system;
diff --git a/src/libsystemd/sd-rtnl/rtnl-types.h b/src/libsystemd/sd-rtnl/rtnl-types.h
index 1ab9444987..de1544bf36 100644
--- a/src/libsystemd/sd-rtnl/rtnl-types.h
+++ b/src/libsystemd/sd-rtnl/rtnl-types.h
@@ -87,6 +87,7 @@ typedef enum NLUnionLinkInfoData {
NL_UNION_LINK_INFO_DATA_IP6GRETAP_TUNNEL,
NL_UNION_LINK_INFO_DATA_SIT_TUNNEL,
NL_UNION_LINK_INFO_DATA_VTI_TUNNEL,
+ NL_UNION_LINK_INFO_DATA_VTI6_TUNNEL,
NL_UNION_LINK_INFO_DATA_IP6TNL_TUNNEL,
_NL_UNION_LINK_INFO_DATA_MAX,
_NL_UNION_LINK_INFO_DATA_INVALID = -1
@@ -94,3 +95,25 @@ typedef enum NLUnionLinkInfoData {
const char *nl_union_link_info_data_to_string(NLUnionLinkInfoData p) _const_;
NLUnionLinkInfoData nl_union_link_info_data_from_string(const char *p) _pure_;
+
+/* Maximum ARP IP target defined in kernel */
+#define BOND_MAX_ARP_TARGETS 16
+
+typedef enum BondArpTargets {
+ BOND_ARP_TARGETS_0,
+ BOND_ARP_TARGETS_1,
+ BOND_ARP_TARGETS_2,
+ BOND_ARP_TARGETS_3,
+ BOND_ARP_TARGETS_4,
+ BOND_ARP_TARGETS_5,
+ BOND_ARP_TARGETS_6,
+ BOND_ARP_TARGETS_7,
+ BOND_ARP_TARGETS_8,
+ BOND_ARP_TARGETS_9,
+ BOND_ARP_TARGETS_10,
+ BOND_ARP_TARGETS_11,
+ BOND_ARP_TARGETS_12,
+ BOND_ARP_TARGETS_13,
+ BOND_ARP_TARGETS_14,
+ BOND_ARP_TARGETS_MAX = BOND_MAX_ARP_TARGETS,
+} BondArpTargets;
diff --git a/src/libsystemd/sd-rtnl/rtnl-util.c b/src/libsystemd/sd-rtnl/rtnl-util.c
index 5666ea44cb..9ddf074c24 100644
--- a/src/libsystemd/sd-rtnl/rtnl-util.c
+++ b/src/libsystemd/sd-rtnl/rtnl-util.c
@@ -19,7 +19,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <netinet/ether.h>
#include "sd-rtnl.h"
diff --git a/src/libsystemd/sd-rtnl/rtnl-util.h b/src/libsystemd/sd-rtnl/rtnl-util.h
index ca9fbd4f41..9e4bdb867e 100644
--- a/src/libsystemd/sd-rtnl/rtnl-util.h
+++ b/src/libsystemd/sd-rtnl/rtnl-util.h
@@ -21,7 +21,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <netinet/ether.h>
#include "util.h"
#include "sd-rtnl.h"
diff --git a/src/libsystemd/sd-rtnl/sd-rtnl.c b/src/libsystemd/sd-rtnl/sd-rtnl.c
index ae49c77e01..40dea1252f 100644
--- a/src/libsystemd/sd-rtnl/sd-rtnl.c
+++ b/src/libsystemd/sd-rtnl/sd-rtnl.c
@@ -61,6 +61,11 @@ static int sd_rtnl_new(sd_rtnl **ret) {
sizeof(struct nlmsghdr), sizeof(uint8_t)))
return -ENOMEM;
+ /* Change notification responses have sequence 0, so we must
+ * start our request sequence numbers at 1, or we may confuse our
+ * responses with notifications from the kernel */
+ rtnl->serial = 1;
+
*ret = rtnl;
rtnl = NULL;
@@ -257,7 +262,9 @@ static void rtnl_seal_message(sd_rtnl *rtnl, sd_rtnl_message *m) {
assert(m);
assert(m->hdr);
- m->hdr->nlmsg_seq = rtnl->serial++;
+ /* don't use seq == 0, as that is used for broadcasts, so we
+ would get confused by replies to such messages */
+ m->hdr->nlmsg_seq = rtnl->serial++ ? : rtnl->serial++;
rtnl_message_seal(m);
@@ -414,16 +421,14 @@ static int process_timeout(sd_rtnl *rtnl) {
}
static int process_reply(sd_rtnl *rtnl, sd_rtnl_message *m) {
- struct reply_callback *c;
+ _cleanup_free_ struct reply_callback *c = NULL;
uint64_t serial;
+ uint16_t type;
int r;
assert(rtnl);
assert(m);
- if (sd_rtnl_message_is_broadcast(m))
- return 0;
-
serial = rtnl_message_get_serial(m);
c = hashmap_remove(rtnl->reply_callbacks, &serial);
if (!c)
@@ -432,12 +437,17 @@ static int process_reply(sd_rtnl *rtnl, sd_rtnl_message *m) {
if (c->timeout != 0)
prioq_remove(rtnl->reply_callbacks_prioq, c, &c->prioq_idx);
+ r = sd_rtnl_message_get_type(m, &type);
+ if (r < 0)
+ return 0;
+
+ if (type == NLMSG_DONE)
+ m = NULL;
+
r = c->callback(rtnl, m, c->userdata);
if (r < 0)
log_debug_errno(r, "sd-rtnl: callback failed: %m");
- free(c);
-
return 1;
}
@@ -488,13 +498,15 @@ static int process_running(sd_rtnl *rtnl, sd_rtnl_message **ret) {
if (!m)
goto null_message;
- r = process_reply(rtnl, m);
- if (r != 0)
- goto null_message;
-
- r = process_match(rtnl, m);
- if (r != 0)
- goto null_message;
+ if (sd_rtnl_message_is_broadcast(m)) {
+ r = process_match(rtnl, m);
+ if (r != 0)
+ goto null_message;
+ } else {
+ r = process_reply(rtnl, m);
+ if (r != 0)
+ goto null_message;
+ }
if (ret) {
*ret = m;
@@ -696,7 +708,6 @@ int sd_rtnl_call(sd_rtnl *rtnl,
sd_rtnl_message **ret) {
usec_t timeout;
uint32_t serial;
- unsigned i = 0;
int r;
assert_return(rtnl, -EINVAL);
@@ -711,36 +722,44 @@ int sd_rtnl_call(sd_rtnl *rtnl,
for (;;) {
usec_t left;
+ unsigned i;
- while (i < rtnl->rqueue_size) {
- sd_rtnl_message *incoming;
+ for (i = 0; i < rtnl->rqueue_size; i++) {
uint32_t received_serial;
- incoming = rtnl->rqueue[i];
- received_serial = rtnl_message_get_serial(incoming);
+ received_serial = rtnl_message_get_serial(rtnl->rqueue[i]);
if (received_serial == serial) {
+ _cleanup_rtnl_message_unref_ sd_rtnl_message *incoming = NULL;
+ uint16_t type;
+
+ incoming = rtnl->rqueue[i];
+
/* found a match, remove from rqueue and return it */
memmove(rtnl->rqueue + i,rtnl->rqueue + i + 1,
sizeof(sd_rtnl_message*) * (rtnl->rqueue_size - i - 1));
rtnl->rqueue_size--;
r = sd_rtnl_message_get_errno(incoming);
- if (r < 0) {
- sd_rtnl_message_unref(incoming);
+ if (r < 0)
+ return r;
+
+ r = sd_rtnl_message_get_type(incoming, &type);
+ if (r < 0)
return r;
+
+ if (type == NLMSG_DONE) {
+ *ret = NULL;
+ return 0;
}
if (ret) {
*ret = incoming;
- } else
- sd_rtnl_message_unref(incoming);
+ incoming = NULL;
+ }
return 1;
}
-
- /* Try to read more, right away */
- i ++;
}
r = socket_read_message(rtnl);
@@ -993,7 +1012,7 @@ int sd_rtnl_add_match(sd_rtnl *rtnl,
assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
assert_return(rtnl_message_type_is_link(type) ||
rtnl_message_type_is_addr(type) ||
- rtnl_message_type_is_route(type), -ENOTSUP);
+ rtnl_message_type_is_route(type), -EOPNOTSUPP);
c = new0(struct match_callback, 1);
if (!c)
diff --git a/src/libsystemd/sd-rtnl/test-rtnl.c b/src/libsystemd/sd-rtnl/test-rtnl.c
index 02f7a8e38a..47cce64816 100644
--- a/src/libsystemd/sd-rtnl/test-rtnl.c
+++ b/src/libsystemd/sd-rtnl/test-rtnl.c
@@ -29,7 +29,6 @@
#include "rtnl-util.h"
#include "event-util.h"
#include "missing.h"
-#include "rtnl-internal.h"
static void test_message_link_bridge(sd_rtnl *rtnl) {
_cleanup_rtnl_message_unref_ sd_rtnl_message *message = NULL;
diff --git a/src/libudev/libudev-device-internal.h b/src/libudev/libudev-device-internal.h
new file mode 100644
index 0000000000..aa36b8cb12
--- /dev/null
+++ b/src/libudev/libudev-device-internal.h
@@ -0,0 +1,57 @@
+/***
+ This file is part of systemd.
+
+ Copyright 2008-2012 Kay Sievers <kay@vrfy.org>
+ Copyright 2015 Tom Gundersen <teg@jklm.no>
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser 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 "libudev.h"
+#include "libudev-private.h"
+#include "sd-device.h"
+
+/**
+ * udev_device:
+ *
+ * Opaque object representing one kernel sys device.
+ */
+struct udev_device {
+ struct udev *udev;
+
+ /* real device object */
+ sd_device *device;
+
+ /* legacy */
+ int refcount;
+
+ struct udev_device *parent;
+ bool parent_set;
+
+ struct udev_list properties;
+ uint64_t properties_generation;
+ struct udev_list tags;
+ uint64_t tags_generation;
+ struct udev_list devlinks;
+ uint64_t devlinks_generation;
+ bool properties_read:1;
+ bool tags_read:1;
+ bool devlinks_read:1;
+ struct udev_list sysattrs;
+ bool sysattrs_read;
+};
+
+struct udev_device *udev_device_new(struct udev *udev);
diff --git a/src/libudev/libudev-device-private.c b/src/libudev/libudev-device-private.c
index fb4c6e2940..4b9c053b54 100644
--- a/src/libudev/libudev-device-private.c
+++ b/src/libudev/libudev-device-private.c
@@ -2,6 +2,7 @@
This file is part of systemd.
Copyright 2008-2012 Kay Sievers <kay@vrfy.org>
+ Copyright 2015 Tom Gundersen <teg@jklm.no>
systemd 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,172 +18,398 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <stddef.h>
-#include <stdbool.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <sys/stat.h>
-
#include "libudev.h"
#include "libudev-private.h"
+#include "libudev-device-internal.h"
+
+#include "device-private.h"
+
+int udev_device_tag_index(struct udev_device *udev_device, struct udev_device *udev_device_old, bool add) {
+ sd_device *device_old = NULL;
+ int r;
+
+ assert(udev_device);
+
+ if (udev_device_old)
+ device_old = udev_device_old->device;
+
+ r = device_tag_index(udev_device->device, device_old, add);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+int udev_device_update_db(struct udev_device *udev_device) {
+ int r;
+
+ assert(udev_device);
+
+ r = device_update_db(udev_device->device);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+int udev_device_delete_db(struct udev_device *udev_device) {
+ int r;
+
+ assert(udev_device);
+
+ r = device_delete_db(udev_device->device);
+ if (r < 0)
+ return r;
-static void udev_device_tag(struct udev_device *dev, const char *tag, bool add)
-{
- const char *id;
- char filename[UTIL_PATH_SIZE];
-
- id = udev_device_get_id_filename(dev);
- if (id == NULL)
- return;
- strscpyl(filename, sizeof(filename), "/run/udev/tags/", tag, "/", id, NULL);
-
- if (add) {
- int fd;
-
- mkdir_parents(filename, 0755);
- fd = open(filename, O_WRONLY|O_CREAT|O_CLOEXEC|O_TRUNC|O_NOFOLLOW, 0444);
- if (fd >= 0)
- close(fd);
- } else {
- unlink(filename);
+ return 0;
+}
+
+int udev_device_get_ifindex(struct udev_device *udev_device) {
+ int r, ifindex;
+
+ assert(udev_device);
+
+ r = sd_device_get_ifindex(udev_device->device, &ifindex);
+ if (r < 0)
+ return r;
+
+ return ifindex;
+}
+
+const char *udev_device_get_devpath_old(struct udev_device *udev_device) {
+ const char *devpath_old = NULL;
+ int r;
+
+ assert(udev_device);
+
+ r = sd_device_get_property_value(udev_device->device, "DEVPATH_OLD", &devpath_old);
+ if (r < 0 && r != -ENOENT) {
+ errno = -r;
+ return NULL;
}
+
+ return devpath_old;
}
-int udev_device_tag_index(struct udev_device *dev, struct udev_device *dev_old, bool add)
-{
- struct udev_list_entry *list_entry;
- bool found;
-
- if (add && dev_old != NULL) {
- /* delete possible left-over tags */
- udev_list_entry_foreach(list_entry, udev_device_get_tags_list_entry(dev_old)) {
- const char *tag_old = udev_list_entry_get_name(list_entry);
- struct udev_list_entry *list_entry_current;
-
- found = false;
- udev_list_entry_foreach(list_entry_current, udev_device_get_tags_list_entry(dev)) {
- const char *tag = udev_list_entry_get_name(list_entry_current);
-
- if (streq(tag, tag_old)) {
- found = true;
- break;
- }
- }
- if (!found)
- udev_device_tag(dev_old, tag_old, false);
- }
+mode_t udev_device_get_devnode_mode(struct udev_device *udev_device) {
+ mode_t mode;
+ int r;
+
+ assert(udev_device);
+
+ r = device_get_devnode_mode(udev_device->device, &mode);
+ if (r < 0) {
+ errno = -r;
+ return 0;
}
- udev_list_entry_foreach(list_entry, udev_device_get_tags_list_entry(dev))
- udev_device_tag(dev, udev_list_entry_get_name(list_entry), add);
+ return mode;
+}
- return 0;
+uid_t udev_device_get_devnode_uid(struct udev_device *udev_device) {
+ uid_t uid;
+ int r;
+
+ assert(udev_device);
+
+ r = device_get_devnode_uid(udev_device->device, &uid);
+ if (r < 0) {
+ errno = -r;
+ return 0;
+ }
+
+ return uid;
}
-static bool device_has_info(struct udev_device *udev_device)
-{
- struct udev_list_entry *list_entry;
-
- if (udev_device_get_devlinks_list_entry(udev_device) != NULL)
- return true;
- if (udev_device_get_devlink_priority(udev_device) != 0)
- return true;
- udev_list_entry_foreach(list_entry, udev_device_get_properties_list_entry(udev_device))
- if (udev_list_entry_get_num(list_entry))
- return true;
- if (udev_device_get_tags_list_entry(udev_device) != NULL)
- return true;
- if (udev_device_get_watch_handle(udev_device) >= 0)
- return true;
- return false;
-}
-
-int udev_device_update_db(struct udev_device *udev_device)
-{
- bool has_info;
- const char *id;
- char filename[UTIL_PATH_SIZE];
- char filename_tmp[UTIL_PATH_SIZE];
- FILE *f;
- int r;
-
- id = udev_device_get_id_filename(udev_device);
- if (id == NULL)
- return -1;
-
- has_info = device_has_info(udev_device);
- strscpyl(filename, sizeof(filename), "/run/udev/data/", id, NULL);
-
- /* do not store anything for otherwise empty devices */
- if (!has_info &&
- major(udev_device_get_devnum(udev_device)) == 0 &&
- udev_device_get_ifindex(udev_device) == 0) {
- unlink(filename);
+gid_t udev_device_get_devnode_gid(struct udev_device *udev_device) {
+ gid_t gid;
+ int r;
+
+ assert(udev_device);
+
+ r = device_get_devnode_gid(udev_device->device, &gid);
+ if (r < 0) {
+ errno = -r;
return 0;
}
- /* write a database file */
- strscpyl(filename_tmp, sizeof(filename_tmp), filename, ".tmp", NULL);
- mkdir_parents(filename_tmp, 0755);
- f = fopen(filename_tmp, "we");
- if (f == NULL)
- return log_debug_errno(errno, "unable to create temporary db file '%s': %m", filename_tmp);
-
- /*
- * set 'sticky' bit to indicate that we should not clean the
- * database when we transition from initramfs to the real root
- */
- if (udev_device_get_db_persist(udev_device))
- fchmod(fileno(f), 01644);
-
- if (has_info) {
- struct udev_list_entry *list_entry;
-
- if (major(udev_device_get_devnum(udev_device)) > 0) {
- udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(udev_device))
- fprintf(f, "S:%s\n", udev_list_entry_get_name(list_entry) + strlen("/dev/"));
- if (udev_device_get_devlink_priority(udev_device) != 0)
- fprintf(f, "L:%i\n", udev_device_get_devlink_priority(udev_device));
- if (udev_device_get_watch_handle(udev_device) >= 0)
- fprintf(f, "W:%i\n", udev_device_get_watch_handle(udev_device));
- }
-
- if (udev_device_get_usec_initialized(udev_device) > 0)
- fprintf(f, "I:"USEC_FMT"\n", udev_device_get_usec_initialized(udev_device));
-
- udev_list_entry_foreach(list_entry, udev_device_get_properties_list_entry(udev_device)) {
- if (!udev_list_entry_get_num(list_entry))
- continue;
- fprintf(f, "E:%s=%s\n",
- udev_list_entry_get_name(list_entry),
- udev_list_entry_get_value(list_entry));
- }
-
- udev_list_entry_foreach(list_entry, udev_device_get_tags_list_entry(udev_device))
- fprintf(f, "G:%s\n", udev_list_entry_get_name(list_entry));
+ return gid;
+}
+
+void udev_device_ensure_usec_initialized(struct udev_device *udev_device, struct udev_device *udev_device_old) {
+ sd_device *device_old = NULL;
+
+ assert(udev_device);
+
+ if (udev_device_old)
+ device_old = udev_device_old->device;
+
+ device_ensure_usec_initialized(udev_device->device, device_old);
+}
+
+char **udev_device_get_properties_envp(struct udev_device *udev_device) {
+ char **envp;
+ int r;
+
+ assert(udev_device);
+
+ r = device_get_properties_strv(udev_device->device, &envp);
+ if (r < 0) {
+ errno = -r;
+ return NULL;
}
- fclose(f);
- r = rename(filename_tmp, filename);
+ return envp;
+}
+
+ssize_t udev_device_get_properties_monitor_buf(struct udev_device *udev_device, const char **buf) {
+ const char *nulstr;
+ size_t len;
+ int r;
+
+ assert(udev_device);
+ assert(buf);
+
+ r = device_get_properties_nulstr(udev_device->device, (const uint8_t **)&nulstr, &len);
+ if (r < 0)
+ return r;
+
+ *buf = nulstr;
+
+ return len;
+}
+
+int udev_device_get_devlink_priority(struct udev_device *udev_device) {
+ int priority, r;
+
+ assert(udev_device);
+
+ r = device_get_devlink_priority(udev_device->device, &priority);
+ if (r < 0)
+ return r;
+
+ return priority;
+}
+
+int udev_device_get_watch_handle(struct udev_device *udev_device) {
+ int handle, r;
+
+ assert(udev_device);
+
+ r = device_get_watch_handle(udev_device->device, &handle);
if (r < 0)
- return -1;
- log_debug("created %s file '%s' for '%s'", has_info ? "db" : "empty",
- filename, udev_device_get_devpath(udev_device));
+ return r;
+
+ return handle;
+}
+
+void udev_device_set_is_initialized(struct udev_device *udev_device) {
+ assert(udev_device);
+
+ device_set_is_initialized(udev_device->device);
+}
+
+int udev_device_rename(struct udev_device *udev_device, const char *name) {
+ int r;
+
+ assert(udev_device);
+
+ r = device_rename(udev_device->device, name);
+ if (r < 0)
+ return r;
+
return 0;
}
-int udev_device_delete_db(struct udev_device *udev_device)
-{
- const char *id;
- char filename[UTIL_PATH_SIZE];
+struct udev_device *udev_device_shallow_clone(struct udev_device *old_device) {
+ struct udev_device *device;
+ int r;
+
+ assert(old_device);
+
+ device = udev_device_new(old_device->udev);
+ if (!device)
+ return NULL;
+
+ r = device_shallow_clone(old_device->device, &device->device);
+ if (r < 0) {
+ udev_device_unref(device);
+ errno = -r;
+ return NULL;
+ }
+
+ return device;
+}
+
+struct udev_device *udev_device_clone_with_db(struct udev_device *udev_device_old) {
+ struct udev_device *udev_device;
+ int r;
+
+ assert(udev_device_old);
+
+ udev_device = udev_device_new(udev_device_old->udev);
+ if (!udev_device)
+ return NULL;
+
+ r = device_clone_with_db(udev_device_old->device, &udev_device->device);
+ if (r < 0) {
+ udev_device_unref(udev_device);
+ errno = -r;
+ return NULL;
+ }
+
+ return udev_device;
+}
+
+struct udev_device *udev_device_new_from_nulstr(struct udev *udev, char *nulstr, ssize_t buflen) {
+ struct udev_device *device;
+ int r;
+
+ device = udev_device_new(udev);
+ if (!device)
+ return NULL;
+
+ r = device_new_from_nulstr(&device->device, (uint8_t*)nulstr, buflen);
+ if (r < 0) {
+ udev_device_unref(device);
+ errno = -r;
+ return NULL;
+ }
+
+ return device;
+}
+
+struct udev_device *udev_device_new_from_synthetic_event(struct udev *udev, const char *syspath, const char *action) {
+ struct udev_device *device;
+ int r;
+
+ device = udev_device_new(udev);
+ if (!device)
+ return NULL;
+
+ r = device_new_from_synthetic_event(&device->device, syspath, action);
+ if (r < 0) {
+ udev_device_unref(device);
+ errno = -r;
+ return NULL;
+ }
+
+ return device;
+}
+
+int udev_device_copy_properties(struct udev_device *udev_device_dst, struct udev_device *udev_device_src) {
+ int r;
+
+ assert(udev_device_dst);
+ assert(udev_device_src);
+
+ r = device_copy_properties(udev_device_dst->device, udev_device_src->device);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+const char *udev_device_get_id_filename(struct udev_device *udev_device) {
+ const char *filename;
+ int r;
+
+ assert(udev_device);
+
+ r = device_get_id_filename(udev_device->device, &filename);
+ if (r < 0) {
+ errno = -r;
+ return NULL;
+ }
+
+ return filename;
+}
+
+int udev_device_set_watch_handle(struct udev_device *udev_device, int handle) {
+
+ assert(udev_device);
+
+ device_set_watch_handle(udev_device->device, handle);
+
+ return 0;
+}
+
+void udev_device_set_db_persist(struct udev_device *udev_device) {
+ assert(udev_device);
+
+ device_set_db_persist(udev_device->device);
+}
+
+int udev_device_set_devlink_priority(struct udev_device *udev_device, int priority) {
+ assert(udev_device);
+
+ device_set_devlink_priority(udev_device->device, priority);
+
+ return 0;
+}
+
+int udev_device_add_devlink(struct udev_device *udev_device, const char *devlink) {
+ int r;
+
+ assert(udev_device);
+
+ r = device_add_devlink(udev_device->device, devlink);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+int udev_device_add_property(struct udev_device *udev_device, const char *property, const char *value) {
+ int r;
+
+ assert(udev_device);
+
+ r = device_add_property(udev_device->device, property, value);
+ if (r < 0)
+ return r;
- id = udev_device_get_id_filename(udev_device);
- if (id == NULL)
- return -1;
- strscpyl(filename, sizeof(filename), "/run/udev/data/", id, NULL);
- unlink(filename);
return 0;
}
+
+int udev_device_add_tag(struct udev_device *udev_device, const char *tag) {
+ int r;
+
+ assert(udev_device);
+
+ r = device_add_tag(udev_device->device, tag);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+void udev_device_remove_tag(struct udev_device *udev_device, const char *tag) {
+ assert(udev_device);
+
+ device_remove_tag(udev_device->device, tag);
+}
+
+void udev_device_cleanup_tags_list(struct udev_device *udev_device) {
+ assert(udev_device);
+
+ device_cleanup_tags(udev_device->device);
+}
+
+void udev_device_cleanup_devlinks_list(struct udev_device *udev_device) {
+ assert(udev_device);
+
+ device_cleanup_devlinks(udev_device->device);
+}
+
+void udev_device_set_info_loaded(struct udev_device *udev_device) {
+ assert(udev_device);
+
+ device_seal(udev_device->device);
+}
+
+void udev_device_read_db(struct udev_device *udev_device) {
+ assert(udev_device);
+
+ device_read_db_force(udev_device->device);
+}
diff --git a/src/libudev/libudev-device.c b/src/libudev/libudev-device.c
index 9863901a33..c27b01db96 100644
--- a/src/libudev/libudev-device.c
+++ b/src/libudev/libudev-device.c
@@ -2,6 +2,7 @@
This file is part of systemd.
Copyright 2008-2012 Kay Sievers <kay@vrfy.org>
+ Copyright 2015 Tom Gundersen <teg@jklm.no>
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
@@ -33,10 +34,13 @@
#include <sys/socket.h>
#include <linux/sockios.h>
+#include "sd-device.h"
+#include "device-util.h"
+#include "device-private.h"
+
#include "libudev.h"
#include "libudev-private.h"
-
-static int udev_device_set_devnode(struct udev_device *udev_device, const char *devnode);
+#include "libudev-device-internal.h"
/**
* SECTION:libudev-device
@@ -49,59 +53,6 @@ static int udev_device_set_devnode(struct udev_device *udev_device, const char *
*/
/**
- * udev_device:
- *
- * Opaque object representing one kernel sys device.
- */
-struct udev_device {
- struct udev *udev;
- struct udev_device *parent_device;
- char *syspath;
- const char *devpath;
- char *sysname;
- const char *sysnum;
- char *devnode;
- mode_t devnode_mode;
- uid_t devnode_uid;
- gid_t devnode_gid;
- char *subsystem;
- char *devtype;
- char *driver;
- char *action;
- char *devpath_old;
- char *id_filename;
- char **envp;
- char *monitor_buf;
- size_t monitor_buf_len;
- struct udev_list devlinks_list;
- struct udev_list properties_list;
- struct udev_list sysattr_value_list;
- struct udev_list sysattr_list;
- struct udev_list tags_list;
- unsigned long long int seqnum;
- usec_t usec_initialized;
- int devlink_priority;
- int refcount;
- dev_t devnum;
- int ifindex;
- int watch_handle;
- int maj, min;
- bool parent_set;
- bool subsystem_set;
- bool devtype_set;
- bool devlinks_uptodate;
- bool envp_uptodate;
- bool tags_uptodate;
- bool driver_set;
- bool info_loaded;
- bool db_loaded;
- bool uevent_loaded;
- bool is_initialized;
- bool sysattr_list_read;
- bool db_persist;
-};
-
-/**
* udev_device_get_seqnum:
* @udev_device: udev device
*
@@ -112,36 +63,27 @@ struct udev_device {
**/
_public_ unsigned long long int udev_device_get_seqnum(struct udev_device *udev_device)
{
- if (udev_device == NULL)
- return 0;
- return udev_device->seqnum;
-}
-
-static int udev_device_set_seqnum(struct udev_device *udev_device, unsigned long long int seqnum)
-{
- char num[32];
+ const char *seqnum;
+ unsigned long long ret;
+ int r;
- udev_device->seqnum = seqnum;
- snprintf(num, sizeof(num), "%llu", seqnum);
- udev_device_add_property(udev_device, "SEQNUM", num);
- return 0;
-}
+ assert_return_errno(udev_device, 0, EINVAL);
-int udev_device_get_ifindex(struct udev_device *udev_device)
-{
- if (!udev_device->info_loaded)
- udev_device_read_uevent_file(udev_device);
- return udev_device->ifindex;
-}
+ r = sd_device_get_property_value(udev_device->device, "SEQNUM", &seqnum);
+ if (r == -ENOENT)
+ return 0;
+ else if (r < 0) {
+ errno = -r;
+ return 0;
+ }
-static int udev_device_set_ifindex(struct udev_device *udev_device, int ifindex)
-{
- char num[32];
+ r = safe_atollu(seqnum, &ret);
+ if (r < 0) {
+ errno = -r;
+ return 0;
+ }
- udev_device->ifindex = ifindex;
- snprintf(num, sizeof(num), "%d", ifindex);
- udev_device_add_property(udev_device, "IFINDEX", num);
- return 0;
+ return ret;
}
/**
@@ -154,45 +96,18 @@ static int udev_device_set_ifindex(struct udev_device *udev_device, int ifindex)
**/
_public_ dev_t udev_device_get_devnum(struct udev_device *udev_device)
{
- if (udev_device == NULL)
- return makedev(0, 0);
- if (!udev_device->info_loaded)
- udev_device_read_uevent_file(udev_device);
- return udev_device->devnum;
-}
-
-static int udev_device_set_devnum(struct udev_device *udev_device, dev_t devnum)
-{
- char num[32];
-
- udev_device->devnum = devnum;
-
- snprintf(num, sizeof(num), "%u", major(devnum));
- udev_device_add_property(udev_device, "MAJOR", num);
- snprintf(num, sizeof(num), "%u", minor(devnum));
- udev_device_add_property(udev_device, "MINOR", num);
- return 0;
-}
-
-const char *udev_device_get_devpath_old(struct udev_device *udev_device)
-{
- return udev_device->devpath_old;
-}
+ dev_t devnum;
+ int r;
-static int udev_device_set_devpath_old(struct udev_device *udev_device, const char *devpath_old)
-{
- const char *pos;
+ assert_return_errno(udev_device, makedev(0, 0), EINVAL);
- free(udev_device->devpath_old);
- udev_device->devpath_old = strdup(devpath_old);
- if (udev_device->devpath_old == NULL)
- return -ENOMEM;
- udev_device_add_property(udev_device, "DEVPATH_OLD", udev_device->devpath_old);
+ r = sd_device_get_devnum(udev_device->device, &devnum);
+ if (r < 0) {
+ errno = -r;
+ return makedev(0, 0);
+ }
- pos = strrchr(udev_device->devpath_old, '/');
- if (pos == NULL)
- return -EINVAL;
- return 0;
+ return devnum;
}
/**
@@ -205,27 +120,18 @@ static int udev_device_set_devpath_old(struct udev_device *udev_device, const ch
**/
_public_ const char *udev_device_get_driver(struct udev_device *udev_device)
{
- char driver[UTIL_NAME_SIZE];
+ const char *driver;
+ int r;
- if (udev_device == NULL)
+ assert_return_errno(udev_device, NULL, EINVAL);
+
+ r = sd_device_get_driver(udev_device->device, &driver);
+ if (r < 0) {
+ errno = -r;
return NULL;
- if (!udev_device->driver_set) {
- udev_device->driver_set = true;
- if (util_get_sys_core_link_value(udev_device->udev, "driver", udev_device->syspath, driver, sizeof(driver)) > 0)
- udev_device->driver = strdup(driver);
}
- return udev_device->driver;
-}
-static int udev_device_set_driver(struct udev_device *udev_device, const char *driver)
-{
- free(udev_device->driver);
- udev_device->driver = strdup(driver);
- if (udev_device->driver == NULL)
- return -ENOMEM;
- udev_device->driver_set = true;
- udev_device_add_property(udev_device, "DRIVER", udev_device->driver);
- return 0;
+ return driver;
}
/**
@@ -238,35 +144,18 @@ static int udev_device_set_driver(struct udev_device *udev_device, const char *d
**/
_public_ const char *udev_device_get_devtype(struct udev_device *udev_device)
{
- if (udev_device == NULL)
+ const char *devtype;
+ int r;
+
+ assert_return_errno(udev_device, NULL, EINVAL);
+
+ r = sd_device_get_devtype(udev_device->device, &devtype);
+ if (r < 0) {
+ errno = -r;
return NULL;
- if (!udev_device->devtype_set) {
- udev_device->devtype_set = true;
- udev_device_read_uevent_file(udev_device);
}
- return udev_device->devtype;
-}
-static int udev_device_set_devtype(struct udev_device *udev_device, const char *devtype)
-{
- free(udev_device->devtype);
- udev_device->devtype = strdup(devtype);
- if (udev_device->devtype == NULL)
- return -ENOMEM;
- udev_device->devtype_set = true;
- udev_device_add_property(udev_device, "DEVTYPE", udev_device->devtype);
- return 0;
-}
-
-static int udev_device_set_subsystem(struct udev_device *udev_device, const char *subsystem)
-{
- free(udev_device->subsystem);
- udev_device->subsystem = strdup(subsystem);
- if (udev_device->subsystem == NULL)
- return -ENOMEM;
- udev_device->subsystem_set = true;
- udev_device_add_property(udev_device, "SUBSYSTEM", udev_device->subsystem);
- return 0;
+ return devtype;
}
/**
@@ -280,251 +169,19 @@ static int udev_device_set_subsystem(struct udev_device *udev_device, const char
**/
_public_ const char *udev_device_get_subsystem(struct udev_device *udev_device)
{
- char subsystem[UTIL_NAME_SIZE];
-
- if (udev_device == NULL)
- return NULL;
- if (!udev_device->subsystem_set) {
- udev_device->subsystem_set = true;
- /* read "subsystem" link */
- if (util_get_sys_core_link_value(udev_device->udev, "subsystem", udev_device->syspath, subsystem, sizeof(subsystem)) > 0) {
- udev_device_set_subsystem(udev_device, subsystem);
- return udev_device->subsystem;
- }
- /* implicit names */
- if (startswith(udev_device->devpath, "/module/")) {
- udev_device_set_subsystem(udev_device, "module");
- return udev_device->subsystem;
- }
- if (strstr(udev_device->devpath, "/drivers/") != NULL) {
- udev_device_set_subsystem(udev_device, "drivers");
- return udev_device->subsystem;
- }
- if (startswith(udev_device->devpath, "/subsystem/") ||
- startswith(udev_device->devpath, "/class/") ||
- startswith(udev_device->devpath, "/bus/")) {
- udev_device_set_subsystem(udev_device, "subsystem");
- return udev_device->subsystem;
- }
- }
- return udev_device->subsystem;
-}
-
-mode_t udev_device_get_devnode_mode(struct udev_device *udev_device)
-{
- if (!udev_device->info_loaded)
- udev_device_read_uevent_file(udev_device);
- return udev_device->devnode_mode;
-}
-
-static int udev_device_set_devnode_mode(struct udev_device *udev_device, mode_t mode)
-{
- char num[32];
-
- udev_device->devnode_mode = mode;
- snprintf(num, sizeof(num), "%#o", mode);
- udev_device_add_property(udev_device, "DEVMODE", num);
- return 0;
-}
-
-uid_t udev_device_get_devnode_uid(struct udev_device *udev_device)
-{
- if (!udev_device->info_loaded)
- udev_device_read_uevent_file(udev_device);
- return udev_device->devnode_uid;
-}
-
-static int udev_device_set_devnode_uid(struct udev_device *udev_device, uid_t uid)
-{
- char num[32];
-
- udev_device->devnode_uid = uid;
- snprintf(num, sizeof(num), "%u", uid);
- udev_device_add_property(udev_device, "DEVUID", num);
- return 0;
-}
-
-gid_t udev_device_get_devnode_gid(struct udev_device *udev_device)
-{
- if (!udev_device->info_loaded)
- udev_device_read_uevent_file(udev_device);
- return udev_device->devnode_gid;
-}
-
-static int udev_device_set_devnode_gid(struct udev_device *udev_device, gid_t gid)
-{
- char num[32];
-
- udev_device->devnode_gid = gid;
- snprintf(num, sizeof(num), "%u", gid);
- udev_device_add_property(udev_device, "DEVGID", num);
- return 0;
-}
-
-struct udev_list_entry *udev_device_add_property(struct udev_device *udev_device, const char *key, const char *value)
-{
- udev_device->envp_uptodate = false;
- if (value == NULL) {
- struct udev_list_entry *list_entry;
-
- list_entry = udev_device_get_properties_list_entry(udev_device);
- list_entry = udev_list_entry_get_by_name(list_entry, key);
- if (list_entry != NULL)
- udev_list_entry_delete(list_entry);
- return NULL;
- }
- return udev_list_entry_add(&udev_device->properties_list, key, value);
-}
+ const char *subsystem;
+ int r;
-static struct udev_list_entry *udev_device_add_property_from_string(struct udev_device *udev_device, const char *property)
-{
- char name[UTIL_LINE_SIZE];
- char *val;
+ assert_return_errno(udev_device, NULL, EINVAL);
- strscpy(name, sizeof(name), property);
- val = strchr(name, '=');
- if (val == NULL)
+ r = sd_device_get_subsystem(udev_device->device, &subsystem);
+ if (r < 0) {
+ errno = -r;
return NULL;
- val[0] = '\0';
- val = &val[1];
- if (val[0] == '\0')
- val = NULL;
- return udev_device_add_property(udev_device, name, val);
-}
-
-static int udev_device_set_syspath(struct udev_device *udev_device, const char *syspath)
-{
- const char *pos;
- size_t len;
-
- free(udev_device->syspath);
- udev_device->syspath = strdup(syspath);
- if (udev_device->syspath == NULL)
- return -ENOMEM;
- udev_device->devpath = udev_device->syspath + strlen("/sys");
- udev_device_add_property(udev_device, "DEVPATH", udev_device->devpath);
-
- pos = strrchr(udev_device->syspath, '/');
- if (pos == NULL)
- return -EINVAL;
- udev_device->sysname = strdup(&pos[1]);
- if (udev_device->sysname == NULL)
- return -ENOMEM;
-
- /* some devices have '!' in their name, change that to '/' */
- len = 0;
- while (udev_device->sysname[len] != '\0') {
- if (udev_device->sysname[len] == '!')
- udev_device->sysname[len] = '/';
- len++;
- }
-
- /* trailing number */
- while (len > 0 && isdigit(udev_device->sysname[--len]))
- udev_device->sysnum = &udev_device->sysname[len];
-
- /* sysname is completely numeric */
- if (len == 0)
- udev_device->sysnum = NULL;
-
- return 0;
-}
-
-/*
- * parse property string, and if needed, update internal values accordingly
- *
- * udev_device_add_property_from_string_parse_finish() needs to be
- * called after adding properties, and its return value checked
- *
- * udev_device_set_info_loaded() needs to be set, to avoid trying
- * to use a device without a DEVPATH set
- */
-void udev_device_add_property_from_string_parse(struct udev_device *udev_device, const char *property)
-{
- if (startswith(property, "DEVPATH=")) {
- char path[UTIL_PATH_SIZE];
-
- 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]);
- } else if (startswith(property, "DEVTYPE=")) {
- udev_device_set_devtype(udev_device, &property[8]);
- } else if (startswith(property, "DEVNAME=")) {
- udev_device_set_devnode(udev_device, &property[8]);
- } else if (startswith(property, "DEVLINKS=")) {
- char devlinks[UTIL_PATH_SIZE];
- char *slink;
- char *next;
-
- strscpy(devlinks, sizeof(devlinks), &property[9]);
- slink = devlinks;
- next = strchr(slink, ' ');
- while (next != NULL) {
- next[0] = '\0';
- udev_device_add_devlink(udev_device, slink);
- slink = &next[1];
- next = strchr(slink, ' ');
- }
- if (slink[0] != '\0')
- udev_device_add_devlink(udev_device, slink);
- } else if (startswith(property, "TAGS=")) {
- char tags[UTIL_PATH_SIZE];
- char *next;
-
- strscpy(tags, sizeof(tags), &property[5]);
- next = strchr(tags, ':');
- if (next != NULL) {
- next++;
- while (next[0] != '\0') {
- char *tag;
-
- tag = next;
- next = strchr(tag, ':');
- if (next == NULL)
- break;
- next[0] = '\0';
- next++;
- udev_device_add_tag(udev_device, tag);
- }
- }
- } else if (startswith(property, "USEC_INITIALIZED=")) {
- udev_device_set_usec_initialized(udev_device, strtoull(&property[19], NULL, 10));
- } else if (startswith(property, "DRIVER=")) {
- udev_device_set_driver(udev_device, &property[7]);
- } else if (startswith(property, "ACTION=")) {
- udev_device_set_action(udev_device, &property[7]);
- } else if (startswith(property, "MAJOR=")) {
- udev_device->maj = strtoull(&property[6], NULL, 10);
- } else if (startswith(property, "MINOR=")) {
- udev_device->min = strtoull(&property[6], NULL, 10);
- } else if (startswith(property, "DEVPATH_OLD=")) {
- udev_device_set_devpath_old(udev_device, &property[12]);
- } else if (startswith(property, "SEQNUM=")) {
- udev_device_set_seqnum(udev_device, strtoull(&property[7], NULL, 10));
- } else if (startswith(property, "IFINDEX=")) {
- udev_device_set_ifindex(udev_device, strtoull(&property[8], NULL, 10));
- } else if (startswith(property, "DEVMODE=")) {
- udev_device_set_devnode_mode(udev_device, strtoul(&property[8], NULL, 8));
- } else if (startswith(property, "DEVUID=")) {
- udev_device_set_devnode_uid(udev_device, strtoul(&property[7], NULL, 10));
- } else if (startswith(property, "DEVGID=")) {
- udev_device_set_devnode_gid(udev_device, strtoul(&property[7], NULL, 10));
- } else {
- udev_device_add_property_from_string(udev_device, property);
- }
-}
-
-int udev_device_add_property_from_string_parse_finish(struct udev_device *udev_device)
-{
- if (udev_device->maj > 0)
- udev_device_set_devnum(udev_device, makedev(udev_device->maj, udev_device->min));
- udev_device->maj = 0;
- udev_device->min = 0;
+ } else if (!subsystem)
+ errno = ENODATA;
- if (udev_device->devpath == NULL || udev_device->subsystem == NULL)
- return -EINVAL;
- return 0;
+ return subsystem;
}
/**
@@ -538,165 +195,36 @@ int udev_device_add_property_from_string_parse_finish(struct udev_device *udev_d
**/
_public_ const char *udev_device_get_property_value(struct udev_device *udev_device, const char *key)
{
- struct udev_list_entry *list_entry;
-
- if (udev_device == NULL)
- return NULL;
- if (key == NULL)
- return NULL;
-
- list_entry = udev_device_get_properties_list_entry(udev_device);
- list_entry = udev_list_entry_get_by_name(list_entry, key);
- return udev_list_entry_get_value(list_entry);
-}
-
-int udev_device_read_db(struct udev_device *udev_device, const char *dbfile)
-{
- char filename[UTIL_PATH_SIZE];
- char line[UTIL_LINE_SIZE];
- FILE *f;
-
- /* providing a database file will always force-load it */
- if (dbfile == NULL) {
- const char *id;
-
- if (udev_device->db_loaded)
- return 0;
- udev_device->db_loaded = true;
-
- id = udev_device_get_id_filename(udev_device);
- if (id == NULL)
- return -1;
- strscpyl(filename, sizeof(filename), "/run/udev/data/", id, NULL);
- dbfile = filename;
- }
-
- f = fopen(dbfile, "re");
- if (f == NULL)
- return log_debug_errno(errno, "no db file to read %s: %m", dbfile);
-
- /* devices with a database entry are initialized */
- udev_device->is_initialized = true;
-
- while (fgets(line, sizeof(line), f)) {
- ssize_t len;
- const char *val;
- struct udev_list_entry *entry;
-
- len = strlen(line);
- if (len < 4)
- break;
- line[len-1] = '\0';
- val = &line[2];
- switch(line[0]) {
- case 'S':
- strscpyl(filename, sizeof(filename), "/dev/", val, NULL);
- udev_device_add_devlink(udev_device, filename);
- break;
- case 'L':
- udev_device_set_devlink_priority(udev_device, atoi(val));
- break;
- case 'E':
- entry = udev_device_add_property_from_string(udev_device, val);
- udev_list_entry_set_num(entry, true);
- break;
- case 'G':
- udev_device_add_tag(udev_device, val);
- break;
- case 'W':
- udev_device_set_watch_handle(udev_device, atoi(val));
- break;
- case 'I':
- udev_device_set_usec_initialized(udev_device, strtoull(val, NULL, 10));
- break;
- }
- }
- fclose(f);
-
- log_debug("device %p filled with db file data", udev_device);
- return 0;
-}
-
-int udev_device_read_uevent_file(struct udev_device *udev_device)
-{
- char filename[UTIL_PATH_SIZE];
- FILE *f;
- char line[UTIL_LINE_SIZE];
- int maj = 0;
- int min = 0;
+ const char *value = NULL;
+ int r;
- if (udev_device->uevent_loaded)
- return 0;
+ assert_return_errno(udev_device && key, NULL, EINVAL);
- strscpyl(filename, sizeof(filename), udev_device->syspath, "/uevent", NULL);
- f = fopen(filename, "re");
- if (f == NULL)
- return -errno;
- udev_device->uevent_loaded = true;
-
- while (fgets(line, sizeof(line), f)) {
- char *pos;
-
- pos = strchr(line, '\n');
- if (pos == NULL)
- continue;
- pos[0] = '\0';
-
- if (startswith(line, "DEVTYPE=")) {
- udev_device_set_devtype(udev_device, &line[8]);
- continue;
- }
- if (startswith(line, "IFINDEX=")) {
- udev_device_set_ifindex(udev_device, strtoull(&line[8], NULL, 10));
- continue;
- }
- if (startswith(line, "DEVNAME=")) {
- udev_device_set_devnode(udev_device, &line[8]);
- continue;
- }
-
- if (startswith(line, "MAJOR="))
- maj = strtoull(&line[6], NULL, 10);
- else if (startswith(line, "MINOR="))
- min = strtoull(&line[6], NULL, 10);
- else if (startswith(line, "DEVMODE="))
- udev_device->devnode_mode = strtoul(&line[8], NULL, 8);
-
- udev_device_add_property_from_string(udev_device, line);
+ r = sd_device_get_property_value(udev_device->device, key, &value);
+ if (r < 0) {
+ errno = -r;
+ return NULL;
}
- udev_device->devnum = makedev(maj, min);
- fclose(f);
- return 0;
-}
-
-void udev_device_set_info_loaded(struct udev_device *device)
-{
- device->info_loaded = true;
+ return value;
}
-static struct udev_device *udev_device_new(struct udev *udev)
-{
+struct udev_device *udev_device_new(struct udev *udev) {
struct udev_device *udev_device;
- if (udev == NULL) {
- errno = EINVAL;
- return NULL;
- }
+ assert_return_errno(udev, NULL, EINVAL);
udev_device = new0(struct udev_device, 1);
- if (udev_device == NULL) {
+ if (!udev_device) {
errno = ENOMEM;
return NULL;
}
udev_device->refcount = 1;
udev_device->udev = udev;
- udev_list_init(udev, &udev_device->devlinks_list, true);
- udev_list_init(udev, &udev_device->properties_list, true);
- udev_list_init(udev, &udev_device->sysattr_value_list, true);
- udev_list_init(udev, &udev_device->sysattr_list, false);
- udev_list_init(udev, &udev_device->tags_list, true);
- udev_device->watch_handle = -1;
+ udev_list_init(udev, &udev_device->properties, true);
+ udev_list_init(udev, &udev_device->tags, true);
+ udev_list_init(udev, &udev_device->sysattrs, true);
+ udev_list_init(udev, &udev_device->devlinks, true);
return udev_device;
}
@@ -715,68 +243,21 @@ static struct udev_device *udev_device_new(struct udev *udev)
*
* Returns: a new udev device, or #NULL, if it does not exist
**/
-_public_ struct udev_device *udev_device_new_from_syspath(struct udev *udev, const char *syspath)
-{
- const char *subdir;
- char path[UTIL_PATH_SIZE];
- char *pos;
- struct stat statbuf;
+_public_ struct udev_device *udev_device_new_from_syspath(struct udev *udev, const char *syspath) {
struct udev_device *udev_device;
+ int r;
- if (udev == NULL) {
- errno = EINVAL;
- return NULL;
- }
-
- if (syspath == NULL) {
- errno = EINVAL;
- return NULL;
- }
-
- /* path starts in sys */
- if (!startswith(syspath, "/sys")) {
- log_debug("not in sys :%s", syspath);
- errno = EINVAL;
+ udev_device = udev_device_new(udev);
+ if (!udev_device)
return NULL;
- }
- /* path is not a root directory */
- subdir = syspath + strlen("/sys");
- pos = strrchr(subdir, '/');
- if (pos == NULL || pos[1] == '\0' || pos < &subdir[2]) {
- errno = EINVAL;
+ r = sd_device_new_from_syspath(&udev_device->device, syspath);
+ if (r < 0) {
+ errno = -r;
+ udev_device_unref(udev_device);
return NULL;
}
- /* resolve possible symlink to real path */
- 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 */
- strscpyl(file, sizeof(file), path, "/uevent", NULL);
- if (stat(file, &statbuf) != 0)
- return NULL;
- } else {
- /* everything else just needs to be a directory */
- if (stat(path, &statbuf) != 0)
- return NULL;
-
- if (!S_ISDIR(statbuf.st_mode)) {
- errno = EISDIR;
- return NULL;
- }
- }
-
- udev_device = udev_device_new(udev);
- if (udev_device == NULL)
- return NULL;
-
- udev_device_set_syspath(udev_device, path);
- log_debug("device %p has devpath '%s'", udev_device, udev_device_get_devpath(udev_device));
-
return udev_device;
}
@@ -798,22 +279,21 @@ _public_ struct udev_device *udev_device_new_from_syspath(struct udev *udev, con
**/
_public_ struct udev_device *udev_device_new_from_devnum(struct udev *udev, char type, dev_t devnum)
{
- char path[UTIL_PATH_SIZE];
- const char *type_str;
-
- if (type == 'b')
- type_str = "block";
- else if (type == 'c')
- type_str = "char";
- else {
- errno = EINVAL;
+ struct udev_device *udev_device;
+ int r;
+
+ udev_device = udev_device_new(udev);
+ if (!udev_device)
+ return NULL;
+
+ r = sd_device_new_from_devnum(&udev_device->device, type, devnum);
+ if (r < 0) {
+ errno = -r;
+ udev_device_unref(udev_device);
return NULL;
}
- /* use /sys/dev/{block,char}/<maj>:<min> link */
- snprintf(path, sizeof(path), "/sys/dev/%s/%u:%u",
- type_str, major(devnum), minor(devnum));
- return udev_device_new_from_syspath(udev, path);
+ return udev_device;
}
/**
@@ -836,65 +316,21 @@ _public_ struct udev_device *udev_device_new_from_devnum(struct udev *udev, char
**/
_public_ struct udev_device *udev_device_new_from_device_id(struct udev *udev, const char *id)
{
- char type;
- int maj, min;
- char subsys[UTIL_PATH_SIZE];
- char *sysname;
-
- switch(id[0]) {
- case 'b':
- case 'c':
- if (sscanf(id, "%c%i:%i", &type, &maj, &min) != 3)
- return NULL;
- return udev_device_new_from_devnum(udev, type, makedev(maj, min));
- case 'n': {
- int sk;
- struct ifreq ifr;
- struct udev_device *dev;
- int ifindex;
-
- ifindex = strtoul(&id[1], NULL, 10);
- if (ifindex <= 0) {
- errno = EINVAL;
- return NULL;
- }
-
- sk = socket(PF_INET, SOCK_DGRAM, 0);
- if (sk < 0)
- return NULL;
- memzero(&ifr, sizeof(struct ifreq));
- ifr.ifr_ifindex = ifindex;
- if (ioctl(sk, SIOCGIFNAME, &ifr) != 0) {
- close(sk);
- return NULL;
- }
- close(sk);
-
- dev = udev_device_new_from_subsystem_sysname(udev, "net", ifr.ifr_name);
- if (dev == NULL)
- return NULL;
- if (udev_device_get_ifindex(dev) == ifindex)
- return dev;
-
- /* this is racy, so we may end up with the wrong device */
- udev_device_unref(dev);
- errno = ENODEV;
+ struct udev_device *udev_device;
+ int r;
+
+ udev_device = udev_device_new(udev);
+ if (!udev_device)
return NULL;
- }
- case '+':
- strscpy(subsys, sizeof(subsys), &id[1]);
- sysname = strchr(subsys, ':');
- if (sysname == NULL) {
- errno = EINVAL;
- return NULL;
- }
- sysname[0] = '\0';
- sysname = &sysname[1];
- return udev_device_new_from_subsystem_sysname(udev, subsys, sysname);
- default:
- errno = EINVAL;
+
+ r = sd_device_new_from_device_id(&udev_device->device, id);
+ if (r < 0) {
+ errno = -r;
+ udev_device_unref(udev_device);
return NULL;
}
+
+ return udev_device;
}
/**
@@ -914,69 +350,21 @@ _public_ struct udev_device *udev_device_new_from_device_id(struct udev *udev, c
**/
_public_ struct udev_device *udev_device_new_from_subsystem_sysname(struct udev *udev, const char *subsystem, const char *sysname)
{
- char path[UTIL_PATH_SIZE];
- struct stat statbuf;
-
- if (streq(subsystem, "subsystem")) {
- strscpyl(path, sizeof(path), "/sys/subsystem/", sysname, NULL);
- if (stat(path, &statbuf) == 0)
- goto found;
-
- strscpyl(path, sizeof(path), "/sys/bus/", sysname, NULL);
- if (stat(path, &statbuf) == 0)
- goto found;
-
- strscpyl(path, sizeof(path), "/sys/class/", sysname, NULL);
- if (stat(path, &statbuf) == 0)
- goto found;
- goto out;
- }
-
- if (streq(subsystem, "module")) {
- strscpyl(path, sizeof(path), "/sys/module/", sysname, NULL);
- if (stat(path, &statbuf) == 0)
- goto found;
- goto out;
- }
-
- if (streq(subsystem, "drivers")) {
- char subsys[UTIL_NAME_SIZE];
- char *driver;
-
- strscpy(subsys, sizeof(subsys), sysname);
- driver = strchr(subsys, ':');
- if (driver != NULL) {
- driver[0] = '\0';
- driver = &driver[1];
-
- strscpyl(path, sizeof(path), "/sys/subsystem/", subsys, "/drivers/", driver, NULL);
- if (stat(path, &statbuf) == 0)
- goto found;
+ struct udev_device *udev_device;
+ int r;
- strscpyl(path, sizeof(path), "/sys/bus/", subsys, "/drivers/", driver, NULL);
- if (stat(path, &statbuf) == 0)
- goto found;
- } else
- errno = EINVAL;
+ udev_device = udev_device_new(udev);
+ if (!udev_device)
+ return NULL;
- goto out;
+ r = sd_device_new_from_subsystem_sysname(&udev_device->device, subsystem, sysname);
+ if (r < 0) {
+ errno = -r;
+ udev_device_unref(udev_device);
+ return NULL;
}
- strscpyl(path, sizeof(path), "/sys/subsystem/", subsystem, "/devices/", sysname, NULL);
- if (stat(path, &statbuf) == 0)
- goto found;
-
- strscpyl(path, sizeof(path), "/sys/bus/", subsystem, "/devices/", sysname, NULL);
- if (stat(path, &statbuf) == 0)
- goto found;
-
- strscpyl(path, sizeof(path), "/sys/class/", subsystem, "/", sysname, NULL);
- if (stat(path, &statbuf) == 0)
- goto found;
-out:
- return NULL;
-found:
- return udev_device_new_from_syspath(udev, path);
+ return udev_device;
}
/**
@@ -995,48 +383,45 @@ found:
**/
_public_ struct udev_device *udev_device_new_from_environment(struct udev *udev)
{
- int i;
struct udev_device *udev_device;
+ int r;
udev_device = udev_device_new(udev);
- if (udev_device == NULL)
+ if (!udev_device)
return NULL;
- udev_device_set_info_loaded(udev_device);
- for (i = 0; environ[i] != NULL; i++)
- udev_device_add_property_from_string_parse(udev_device, environ[i]);
-
- if (udev_device_add_property_from_string_parse_finish(udev_device) < 0) {
- log_debug("missing values, invalid device");
+ r = device_new_from_strv(&udev_device->device, environ);
+ if (r < 0) {
+ errno = -r;
udev_device_unref(udev_device);
- udev_device = NULL;
+ return NULL;
}
return udev_device;
}
-static struct udev_device *device_new_from_parent(struct udev_device *udev_device)
+static struct udev_device *device_new_from_parent(struct udev_device *child)
{
- struct udev_device *udev_device_parent = NULL;
- char path[UTIL_PATH_SIZE];
- const char *subdir;
-
- strscpy(path, sizeof(path), udev_device->syspath);
- subdir = path + strlen("/sys/");
- for (;;) {
- char *pos;
-
- pos = strrchr(subdir, '/');
- if (pos == NULL || pos < &subdir[2])
- break;
- pos[0] = '\0';
- udev_device_parent = udev_device_new_from_syspath(udev_device->udev, path);
- if (udev_device_parent != NULL)
- return udev_device_parent;
+ struct udev_device *parent;
+ int r;
+
+ assert_return_errno(child, NULL, EINVAL);
+
+ parent = udev_device_new(child->udev);
+ if (!parent)
+ return NULL;
+
+ r = sd_device_get_parent(child->device, &parent->device);
+ if (r < 0) {
+ errno = -r;
+ udev_device_unref(parent);
+ return NULL;
}
- errno = ENOENT;
- return NULL;
+ /* the parent is unref'ed with the child, so take a ref from libudev as well */
+ sd_device_ref(parent->device);
+
+ return parent;
}
/**
@@ -1059,15 +444,15 @@ static struct udev_device *device_new_from_parent(struct udev_device *udev_devic
**/
_public_ struct udev_device *udev_device_get_parent(struct udev_device *udev_device)
{
- if (udev_device == NULL) {
- errno = EINVAL;
- return NULL;
- }
+ assert_return_errno(udev_device, NULL, EINVAL);
+
if (!udev_device->parent_set) {
udev_device->parent_set = true;
- udev_device->parent_device = device_new_from_parent(udev_device);
+ udev_device->parent = device_new_from_parent(udev_device);
}
- return udev_device->parent_device;
+
+ /* TODO: errno will differ here in case parent == NULL */
+ return udev_device->parent;
}
/**
@@ -1093,33 +478,30 @@ _public_ struct udev_device *udev_device_get_parent(struct udev_device *udev_dev
**/
_public_ struct udev_device *udev_device_get_parent_with_subsystem_devtype(struct udev_device *udev_device, const char *subsystem, const char *devtype)
{
- struct udev_device *parent;
+ sd_device *parent;
+ int r;
- if (subsystem == NULL) {
- errno = EINVAL;
+ assert_return_errno(udev_device, NULL, EINVAL);
+
+ /* this relies on the fact that finding the subdevice of a parent or the
+ parent of a subdevice commute */
+
+ /* first find the correct sd_device */
+ r = sd_device_get_parent_with_subsystem_devtype(udev_device->device, subsystem, devtype, &parent);
+ if (r < 0) {
+ errno = -r;
return NULL;
}
- parent = udev_device_get_parent(udev_device);
- while (parent != NULL) {
- const char *parent_subsystem;
- const char *parent_devtype;
-
- parent_subsystem = udev_device_get_subsystem(parent);
- if (parent_subsystem != NULL && streq(parent_subsystem, subsystem)) {
- if (devtype == NULL)
- break;
- parent_devtype = udev_device_get_devtype(parent);
- if (parent_devtype != NULL && streq(parent_devtype, devtype))
- break;
- }
- parent = udev_device_get_parent(parent);
+ /* then walk the chain of udev_device parents until the correspanding
+ one is found */
+ while ((udev_device = udev_device_get_parent(udev_device))) {
+ if (udev_device->device == parent)
+ return udev_device;
}
- if (!parent)
- errno = ENOENT;
-
- return parent;
+ errno = ENOENT;
+ return NULL;
}
/**
@@ -1132,8 +514,8 @@ _public_ struct udev_device *udev_device_get_parent_with_subsystem_devtype(struc
**/
_public_ struct udev *udev_device_get_udev(struct udev_device *udev_device)
{
- if (udev_device == NULL)
- return NULL;
+ assert_return_errno(udev_device, NULL, EINVAL);
+
return udev_device->udev;
}
@@ -1147,9 +529,9 @@ _public_ struct udev *udev_device_get_udev(struct udev_device *udev_device)
**/
_public_ struct udev_device *udev_device_ref(struct udev_device *udev_device)
{
- if (udev_device == NULL)
- return NULL;
- udev_device->refcount++;
+ if (udev_device)
+ udev_device->refcount++;
+
return udev_device;
}
@@ -1164,30 +546,18 @@ _public_ struct udev_device *udev_device_ref(struct udev_device *udev_device)
**/
_public_ struct udev_device *udev_device_unref(struct udev_device *udev_device)
{
- if (udev_device == NULL)
- return NULL;
- udev_device->refcount--;
- if (udev_device->refcount > 0)
- return NULL;
- if (udev_device->parent_device != NULL)
- udev_device_unref(udev_device->parent_device);
- free(udev_device->syspath);
- free(udev_device->sysname);
- free(udev_device->devnode);
- free(udev_device->subsystem);
- free(udev_device->devtype);
- udev_list_cleanup(&udev_device->devlinks_list);
- udev_list_cleanup(&udev_device->properties_list);
- udev_list_cleanup(&udev_device->sysattr_value_list);
- udev_list_cleanup(&udev_device->sysattr_list);
- udev_list_cleanup(&udev_device->tags_list);
- free(udev_device->action);
- free(udev_device->driver);
- free(udev_device->devpath_old);
- free(udev_device->id_filename);
- free(udev_device->envp);
- free(udev_device->monitor_buf);
- free(udev_device);
+ if (udev_device && (-- udev_device->refcount) == 0) {
+ sd_device_unref(udev_device->device);
+ udev_device_unref(udev_device->parent);
+
+ udev_list_cleanup(&udev_device->properties);
+ udev_list_cleanup(&udev_device->sysattrs);
+ udev_list_cleanup(&udev_device->tags);
+ udev_list_cleanup(&udev_device->devlinks);
+
+ free(udev_device);
+ }
+
return NULL;
}
@@ -1202,9 +572,18 @@ _public_ struct udev_device *udev_device_unref(struct udev_device *udev_device)
**/
_public_ const char *udev_device_get_devpath(struct udev_device *udev_device)
{
- if (udev_device == NULL)
+ const char *devpath;
+ int r;
+
+ assert_return_errno(udev_device, NULL, EINVAL);
+
+ r = sd_device_get_devpath(udev_device->device, &devpath);
+ if (r < 0) {
+ errno = -r;
return NULL;
- return udev_device->devpath;
+ }
+
+ return devpath;
}
/**
@@ -1218,9 +597,18 @@ _public_ const char *udev_device_get_devpath(struct udev_device *udev_device)
**/
_public_ const char *udev_device_get_syspath(struct udev_device *udev_device)
{
- if (udev_device == NULL)
+ const char *syspath;
+ int r;
+
+ assert_return_errno(udev_device, NULL, EINVAL);
+
+ r = sd_device_get_syspath(udev_device->device, &syspath);
+ if (r < 0) {
+ errno = -r;
return NULL;
- return udev_device->syspath;
+ }
+
+ return syspath;
}
/**
@@ -1233,9 +621,18 @@ _public_ const char *udev_device_get_syspath(struct udev_device *udev_device)
**/
_public_ const char *udev_device_get_sysname(struct udev_device *udev_device)
{
- if (udev_device == NULL)
+ const char *sysname;
+ int r;
+
+ assert_return_errno(udev_device, NULL, EINVAL);
+
+ r = sd_device_get_sysname(udev_device->device, &sysname);
+ if (r < 0) {
+ errno = -r;
return NULL;
- return udev_device->sysname;
+ }
+
+ return sysname;
}
/**
@@ -1248,9 +645,18 @@ _public_ const char *udev_device_get_sysname(struct udev_device *udev_device)
**/
_public_ const char *udev_device_get_sysnum(struct udev_device *udev_device)
{
- if (udev_device == NULL)
+ const char *sysnum;
+ int r;
+
+ assert_return_errno(udev_device, NULL, EINVAL);
+
+ r = sd_device_get_sysnum(udev_device->device, &sysnum);
+ if (r < 0) {
+ errno = -r;
return NULL;
- return udev_device->sysnum;
+ }
+
+ return sysnum;
}
/**
@@ -1264,13 +670,18 @@ _public_ const char *udev_device_get_sysnum(struct udev_device *udev_device)
**/
_public_ const char *udev_device_get_devnode(struct udev_device *udev_device)
{
- if (udev_device == NULL)
+ const char *devnode;
+ int r;
+
+ assert_return_errno(udev_device, NULL, EINVAL);
+
+ r = sd_device_get_devname(udev_device->device, &devnode);
+ if (r < 0) {
+ errno = -r;
return NULL;
- if (udev_device->devnode != NULL)
- return udev_device->devnode;
- if (!udev_device->info_loaded)
- udev_device_read_uevent_file(udev_device);
- return udev_device->devnode;
+ }
+
+ return devnode;
}
/**
@@ -1288,21 +699,26 @@ _public_ const char *udev_device_get_devnode(struct udev_device *udev_device)
**/
_public_ struct udev_list_entry *udev_device_get_devlinks_list_entry(struct udev_device *udev_device)
{
- if (udev_device == NULL)
- return NULL;
- if (!udev_device->info_loaded)
- udev_device_read_db(udev_device, NULL);
- return udev_list_get_entry(&udev_device->devlinks_list);
-}
+ assert_return_errno(udev_device, NULL, EINVAL);
-void udev_device_cleanup_devlinks_list(struct udev_device *udev_device)
-{
- udev_device->devlinks_uptodate = false;
- udev_list_cleanup(&udev_device->devlinks_list);
+ if (device_get_devlinks_generation(udev_device->device) != udev_device->devlinks_generation ||
+ !udev_device->devlinks_read) {
+ const char *devlink;
+
+ udev_list_cleanup(&udev_device->devlinks);
+
+ FOREACH_DEVICE_DEVLINK(udev_device->device, devlink)
+ udev_list_entry_add(&udev_device->devlinks, devlink, NULL);
+
+ udev_device->devlinks_read = true;
+ udev_device->devlinks_generation = device_get_devlinks_generation(udev_device->device);
+ }
+
+ return udev_list_get_entry(&udev_device->devlinks);
}
/**
- * udev_device_get_properties_list_entry:
+ * udev_device_get_event_properties_entry:
* @udev_device: udev device
*
* Retrieve the list of key/value device properties of the udev
@@ -1315,45 +731,22 @@ void udev_device_cleanup_devlinks_list(struct udev_device *udev_device)
**/
_public_ struct udev_list_entry *udev_device_get_properties_list_entry(struct udev_device *udev_device)
{
- if (udev_device == NULL)
- return NULL;
- if (!udev_device->info_loaded) {
- udev_device_read_uevent_file(udev_device);
- udev_device_read_db(udev_device, NULL);
- }
- if (!udev_device->devlinks_uptodate) {
- char symlinks[UTIL_PATH_SIZE];
- struct udev_list_entry *list_entry;
-
- udev_device->devlinks_uptodate = true;
- list_entry = udev_device_get_devlinks_list_entry(udev_device);
- if (list_entry != NULL) {
- char *s;
- size_t l;
-
- s = symlinks;
- 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 = strpcpyl(&s, l, " ", udev_list_entry_get_name(list_entry), NULL);
- udev_device_add_property(udev_device, "DEVLINKS", symlinks);
- }
- }
- if (!udev_device->tags_uptodate) {
- udev_device->tags_uptodate = true;
- if (udev_device_get_tags_list_entry(udev_device) != NULL) {
- char tags[UTIL_PATH_SIZE];
- struct udev_list_entry *list_entry;
- char *s;
- size_t l;
-
- s = tags;
- l = strpcpyl(&s, sizeof(tags), ":", NULL);
- udev_list_entry_foreach(list_entry, udev_device_get_tags_list_entry(udev_device))
- l = strpcpyl(&s, l, udev_list_entry_get_name(list_entry), ":", NULL);
- udev_device_add_property(udev_device, "TAGS", tags);
- }
+ assert_return_errno(udev_device, NULL, EINVAL);
+
+ if (device_get_properties_generation(udev_device->device) != udev_device->properties_generation ||
+ !udev_device->properties_read) {
+ const char *key, *value;
+
+ udev_list_cleanup(&udev_device->properties);
+
+ FOREACH_DEVICE_PROPERTY(udev_device->device, key, value)
+ udev_list_entry_add(&udev_device->properties, key, value);
+
+ udev_device->properties_read = true;
+ udev_device->properties_generation = device_get_properties_generation(udev_device->device);
}
- return udev_list_get_entry(&udev_device->properties_list);
+
+ return udev_list_get_entry(&udev_device->properties);
}
/**
@@ -1366,11 +759,19 @@ _public_ struct udev_list_entry *udev_device_get_properties_list_entry(struct ud
*
* Returns: the kernel action value, or #NULL if there is no action value available.
**/
-_public_ const char *udev_device_get_action(struct udev_device *udev_device)
-{
- if (udev_device == NULL)
+_public_ const char *udev_device_get_action(struct udev_device *udev_device) {
+ const char *action = NULL;
+ int r;
+
+ assert_return_errno(udev_device, NULL, EINVAL);
+
+ r = sd_device_get_property_value(udev_device->device, "ACTION", &action);
+ if (r < 0 && r != -ENOENT) {
+ errno = -r;
return NULL;
- return udev_device->action;
+ }
+
+ return action;
}
/**
@@ -1387,32 +788,18 @@ _public_ const char *udev_device_get_action(struct udev_device *udev_device)
**/
_public_ unsigned long long int udev_device_get_usec_since_initialized(struct udev_device *udev_device)
{
- usec_t now_ts;
-
- if (udev_device == NULL)
- return 0;
- if (!udev_device->info_loaded)
- udev_device_read_db(udev_device, NULL);
- if (udev_device->usec_initialized == 0)
- return 0;
- now_ts = now(CLOCK_MONOTONIC);
- if (now_ts == 0)
- return 0;
- return now_ts - udev_device->usec_initialized;
-}
+ usec_t ts;
+ int r;
-usec_t udev_device_get_usec_initialized(struct udev_device *udev_device)
-{
- return udev_device->usec_initialized;
-}
+ assert_return(udev_device, -EINVAL);
-void udev_device_set_usec_initialized(struct udev_device *udev_device, usec_t usec_initialized)
-{
- char num[32];
+ r = sd_device_get_usec_since_initialized(udev_device->device, &ts);
+ if (r < 0) {
+ errno = EINVAL;
+ return 0;
+ }
- udev_device->usec_initialized = usec_initialized;
- snprintf(num, sizeof(num), USEC_FMT, usec_initialized);
- udev_device_add_property(udev_device, "USEC_INITIALIZED", num);
+ return ts;
}
/**
@@ -1427,76 +814,18 @@ void udev_device_set_usec_initialized(struct udev_device *udev_device, usec_t us
**/
_public_ const char *udev_device_get_sysattr_value(struct udev_device *udev_device, const char *sysattr)
{
- struct udev_list_entry *list_entry;
- char path[UTIL_PATH_SIZE];
- char value[4096];
- struct stat statbuf;
- int fd;
- ssize_t size;
- const char *val = NULL;
-
- if (udev_device == NULL)
- return NULL;
- if (sysattr == NULL)
- return NULL;
-
- /* look for possibly already cached result */
- list_entry = udev_list_get_entry(&udev_device->sysattr_value_list);
- list_entry = udev_list_entry_get_by_name(list_entry, sysattr);
- if (list_entry != NULL)
- return udev_list_entry_get_value(list_entry);
+ const char *value;
+ int r;
- 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;
- }
+ assert_return_errno(udev_device, NULL, EINVAL);
- if (S_ISLNK(statbuf.st_mode)) {
- /*
- * Some core links return only the last element of the target path,
- * these are just values, the paths should not be exposed.
- */
- if (streq(sysattr, "driver") ||
- streq(sysattr, "subsystem") ||
- streq(sysattr, "module")) {
- if (util_get_sys_core_link_value(udev_device->udev, sysattr,
- udev_device->syspath, value, sizeof(value)) < 0)
- return NULL;
- list_entry = udev_list_entry_add(&udev_device->sysattr_value_list, sysattr, value);
- val = udev_list_entry_get_value(list_entry);
- goto out;
- }
-
- goto out;
+ r = sd_device_get_sysattr_value(udev_device->device, sysattr, &value);
+ if (r < 0) {
+ errno = -r;
+ return NULL;
}
- /* skip directories */
- if (S_ISDIR(statbuf.st_mode))
- goto out;
-
- /* skip non-readable files */
- if ((statbuf.st_mode & S_IRUSR) == 0)
- goto out;
-
- /* read attribute value */
- fd = open(path, O_RDONLY|O_CLOEXEC);
- if (fd < 0)
- goto out;
- size = read(fd, value, sizeof(value));
- close(fd);
- if (size < 0)
- goto out;
- if (size == sizeof(value))
- goto out;
-
- /* got a valid value, store it in cache and return it */
- value[size] = '\0';
- util_remove_trailing_chars(value, '\n');
- list_entry = udev_list_entry_add(&udev_device->sysattr_value_list, sysattr, value);
- val = udev_list_entry_get_value(list_entry);
-out:
- return val;
+ return value;
}
/**
@@ -1511,116 +840,15 @@ out:
**/
_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;
- DIR *dir;
- int num = 0;
-
- if (udev_device == NULL)
- return -EINVAL;
- if (udev_device->sysattr_list_read)
- return 0;
-
- dir = opendir(udev_device_get_syspath(udev_device));
- if (!dir)
- return -errno;
-
- for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
- char path[UTIL_PATH_SIZE];
- struct stat statbuf;
-
- /* only handle symlinks and regular files */
- if (dent->d_type != DT_LNK && dent->d_type != DT_REG)
- continue;
-
- 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)
- continue;
+ int r;
- udev_list_entry_add(&udev_device->sysattr_list, dent->d_name, NULL);
- num++;
- }
+ assert_return(udev_device, -EINVAL);
- closedir(dir);
- udev_device->sysattr_list_read = true;
+ r = sd_device_set_sysattr_value(udev_device->device, sysattr, value);
+ if (r < 0)
+ return r;
- return num;
+ return 0;
}
/**
@@ -1635,74 +863,20 @@ static int udev_device_sysattr_list_read(struct udev_device *udev_device)
**/
_public_ struct udev_list_entry *udev_device_get_sysattr_list_entry(struct udev_device *udev_device)
{
- if (!udev_device->sysattr_list_read) {
- int ret;
- ret = udev_device_sysattr_list_read(udev_device);
- if (0 > ret)
- return NULL;
- }
+ assert_return_errno(udev_device, NULL, EINVAL);
- return udev_list_get_entry(&udev_device->sysattr_list);
-}
+ if (!udev_device->sysattrs_read) {
+ const char *sysattr;
-static int udev_device_set_devnode(struct udev_device *udev_device, const char *devnode)
-{
- free(udev_device->devnode);
- if (devnode[0] != '/') {
- if (asprintf(&udev_device->devnode, "/dev/%s", devnode) < 0)
- udev_device->devnode = NULL;
- } else {
- udev_device->devnode = strdup(devnode);
- }
- if (udev_device->devnode == NULL)
- return -ENOMEM;
- udev_device_add_property(udev_device, "DEVNAME", udev_device->devnode);
- return 0;
-}
+ udev_list_cleanup(&udev_device->sysattrs);
-int udev_device_add_devlink(struct udev_device *udev_device, const char *devlink)
-{
- struct udev_list_entry *list_entry;
-
- udev_device->devlinks_uptodate = false;
- list_entry = udev_list_entry_add(&udev_device->devlinks_list, devlink, NULL);
- if (list_entry == NULL)
- return -ENOMEM;
- return 0;
-}
+ FOREACH_DEVICE_SYSATTR(udev_device->device, sysattr)
+ udev_list_entry_add(&udev_device->properties, sysattr, NULL);
-const char *udev_device_get_id_filename(struct udev_device *udev_device)
-{
- if (udev_device->id_filename == NULL) {
- if (udev_device_get_subsystem(udev_device) == NULL)
- return NULL;
-
- if (major(udev_device_get_devnum(udev_device)) > 0) {
- /* use dev_t -- b259:131072, c254:0 */
- if (asprintf(&udev_device->id_filename, "%c%u:%u",
- streq(udev_device_get_subsystem(udev_device), "block") ? 'b' : 'c',
- major(udev_device_get_devnum(udev_device)),
- minor(udev_device_get_devnum(udev_device))) < 0)
- udev_device->id_filename = NULL;
- } else if (udev_device_get_ifindex(udev_device) > 0) {
- /* use netdev ifindex -- n3 */
- if (asprintf(&udev_device->id_filename, "n%i", udev_device_get_ifindex(udev_device)) < 0)
- udev_device->id_filename = NULL;
- } else {
- /*
- * use $subsys:$syname -- pci:0000:00:1f.2
- * sysname() has '!' translated, get it from devpath
- */
- const char *sysname;
- sysname = strrchr(udev_device->devpath, '/');
- if (sysname == NULL)
- return NULL;
- sysname = &sysname[1];
- if (asprintf(&udev_device->id_filename, "+%s:%s", udev_device_get_subsystem(udev_device), sysname) < 0)
- udev_device->id_filename = NULL;
- }
+ udev_device->sysattrs_read = true;
}
- return udev_device->id_filename;
+
+ return udev_list_get_entry(&udev_device->sysattrs);
}
/**
@@ -1720,49 +894,18 @@ const char *udev_device_get_id_filename(struct udev_device *udev_device)
**/
_public_ int udev_device_get_is_initialized(struct udev_device *udev_device)
{
- if (!udev_device->info_loaded)
- udev_device_read_db(udev_device, NULL);
- return udev_device->is_initialized;
-}
+ int r, initialized;
-void udev_device_set_is_initialized(struct udev_device *udev_device)
-{
- udev_device->is_initialized = true;
-}
+ assert_return(udev_device, -EINVAL);
-static bool is_valid_tag(const char *tag)
-{
- return !strchr(tag, ':') && !strchr(tag, ' ');
-}
+ r = sd_device_get_is_initialized(udev_device->device, &initialized);
+ if (r < 0) {
+ errno = -r;
-int udev_device_add_tag(struct udev_device *udev_device, const char *tag)
-{
- if (!is_valid_tag(tag))
- return -EINVAL;
- udev_device->tags_uptodate = false;
- if (udev_list_entry_add(&udev_device->tags_list, tag, NULL) != NULL)
return 0;
- return -ENOMEM;
-}
-
-void udev_device_remove_tag(struct udev_device *udev_device, const char *tag)
-{
- struct udev_list_entry *e;
-
- if (!is_valid_tag(tag))
- return;
- e = udev_list_get_entry(&udev_device->tags_list);
- e = udev_list_entry_get_by_name(e, tag);
- if (e) {
- udev_device->tags_uptodate = false;
- udev_list_entry_delete(e);
}
-}
-void udev_device_cleanup_tags_list(struct udev_device *udev_device)
-{
- udev_device->tags_uptodate = false;
- udev_list_cleanup(&udev_device->tags_list);
+ return initialized;
}
/**
@@ -1778,11 +921,22 @@ void udev_device_cleanup_tags_list(struct udev_device *udev_device)
**/
_public_ struct udev_list_entry *udev_device_get_tags_list_entry(struct udev_device *udev_device)
{
- if (udev_device == NULL)
- return NULL;
- if (!udev_device->info_loaded)
- udev_device_read_db(udev_device, NULL);
- return udev_list_get_entry(&udev_device->tags_list);
+ assert_return_errno(udev_device, NULL, EINVAL);
+
+ if (device_get_tags_generation(udev_device->device) != udev_device->tags_generation ||
+ !udev_device->tags_read) {
+ const char *tag;
+
+ udev_list_cleanup(&udev_device->tags);
+
+ FOREACH_DEVICE_TAG(udev_device->device, tag)
+ udev_list_entry_add(&udev_device->tags, tag, NULL);
+
+ udev_device->tags_read = true;
+ udev_device->tags_generation = device_get_tags_generation(udev_device->device);
+ }
+
+ return udev_list_get_entry(&udev_device->tags);
}
/**
@@ -1796,217 +950,7 @@ _public_ struct udev_list_entry *udev_device_get_tags_list_entry(struct udev_dev
**/
_public_ int udev_device_has_tag(struct udev_device *udev_device, const char *tag)
{
- struct udev_list_entry *list_entry;
-
- if (udev_device == NULL)
- return false;
- if (!udev_device->info_loaded)
- udev_device_read_db(udev_device, NULL);
- list_entry = udev_device_get_tags_list_entry(udev_device);
- if (udev_list_entry_get_by_name(list_entry, tag) != NULL)
- return true;
- return false;
-}
-
-#define ENVP_SIZE 128
-#define MONITOR_BUF_SIZE 4096
-static int update_envp_monitor_buf(struct udev_device *udev_device)
-{
- struct udev_list_entry *list_entry;
- char *s;
- size_t l;
- unsigned int i;
-
- /* monitor buffer of property strings */
- free(udev_device->monitor_buf);
- udev_device->monitor_buf_len = 0;
- udev_device->monitor_buf = malloc(MONITOR_BUF_SIZE);
- if (udev_device->monitor_buf == NULL)
- return -ENOMEM;
-
- /* envp array, strings will point into monitor buffer */
- if (udev_device->envp == NULL)
- udev_device->envp = malloc(sizeof(char *) * ENVP_SIZE);
- if (udev_device->envp == NULL)
- return -ENOMEM;
-
- i = 0;
- s = udev_device->monitor_buf;
- l = MONITOR_BUF_SIZE;
- udev_list_entry_foreach(list_entry, udev_device_get_properties_list_entry(udev_device)) {
- const char *key;
-
- key = udev_list_entry_get_name(list_entry);
- /* skip private variables */
- if (key[0] == '.')
- continue;
-
- /* add string to envp array */
- udev_device->envp[i++] = s;
- if (i+1 >= ENVP_SIZE)
- return -EINVAL;
-
- /* add property string to monitor buffer */
- l = strpcpyl(&s, l, key, "=", udev_list_entry_get_value(list_entry), NULL);
- if (l == 0)
- return -EINVAL;
- /* advance past the trailing '\0' that strpcpyl() guarantees */
- s++;
- l--;
- }
- udev_device->envp[i] = NULL;
- udev_device->monitor_buf_len = s - udev_device->monitor_buf;
- udev_device->envp_uptodate = true;
- return 0;
-}
-
-char **udev_device_get_properties_envp(struct udev_device *udev_device)
-{
- if (!udev_device->envp_uptodate)
- if (update_envp_monitor_buf(udev_device) != 0)
- return NULL;
- return udev_device->envp;
-}
-
-ssize_t udev_device_get_properties_monitor_buf(struct udev_device *udev_device, const char **buf)
-{
- if (!udev_device->envp_uptodate)
- if (update_envp_monitor_buf(udev_device) != 0)
- return -EINVAL;
- *buf = udev_device->monitor_buf;
- return udev_device->monitor_buf_len;
-}
-
-int udev_device_set_action(struct udev_device *udev_device, const char *action)
-{
- free(udev_device->action);
- udev_device->action = strdup(action);
- if (udev_device->action == NULL)
- return -ENOMEM;
- udev_device_add_property(udev_device, "ACTION", udev_device->action);
- return 0;
-}
-
-int udev_device_get_devlink_priority(struct udev_device *udev_device)
-{
- if (!udev_device->info_loaded)
- udev_device_read_db(udev_device, NULL);
- return udev_device->devlink_priority;
-}
-
-int udev_device_set_devlink_priority(struct udev_device *udev_device, int prio)
-{
- udev_device->devlink_priority = prio;
- return 0;
-}
-
-int udev_device_get_watch_handle(struct udev_device *udev_device)
-{
- if (!udev_device->info_loaded)
- udev_device_read_db(udev_device, NULL);
- return udev_device->watch_handle;
-}
-
-int udev_device_set_watch_handle(struct udev_device *udev_device, int handle)
-{
- udev_device->watch_handle = handle;
- return 0;
-}
-
-bool udev_device_get_db_persist(struct udev_device *udev_device)
-{
- return udev_device->db_persist;
-}
-
-void udev_device_set_db_persist(struct udev_device *udev_device)
-{
- udev_device->db_persist = true;
-}
-
-int udev_device_rename(struct udev_device *udev_device, const char *name)
-{
- _cleanup_free_ char *dirname = NULL;
- char *new_syspath;
- int r;
-
- if (udev_device == NULL || name == NULL)
- return -EINVAL;
-
- dirname = dirname_malloc(udev_device->syspath);
- if (!dirname)
- return -ENOMEM;
-
- new_syspath = strjoina(dirname, "/", name);
-
- r = udev_device_set_syspath(udev_device, new_syspath);
- if (r < 0)
- return r;
-
- return 0;
-}
-
-struct udev_device *udev_device_shallow_clone(struct udev_device *old_device)
-{
- struct udev_device *device;
-
- if (old_device == NULL)
- return NULL;
-
- device = udev_device_new(old_device->udev);
- if (!device) {
- errno = ENOMEM;
-
- return NULL;
- }
-
- udev_device_set_syspath(device, udev_device_get_syspath(old_device));
- udev_device_set_subsystem(device, udev_device_get_subsystem(old_device));
- udev_device_set_devnum(device, udev_device_get_devnum(old_device));
-
- return device;
-}
-
-struct udev_device *udev_device_new_from_nulstr(struct udev *udev, char *nulstr, ssize_t buflen) {
- struct udev_device *device;
- ssize_t bufpos = 0;
-
- if (nulstr == NULL || buflen <= 0) {
- errno = EINVAL;
-
- return NULL;
- }
-
- device = udev_device_new(udev);
- if (!device) {
- errno = ENOMEM;
-
- return NULL;
- }
-
- udev_device_set_info_loaded(device);
-
- while (bufpos < buflen) {
- char *key;
- size_t keylen;
-
- key = nulstr + bufpos;
- keylen = strlen(key);
- if (keylen == 0)
- break;
-
- bufpos += keylen + 1;
- udev_device_add_property_from_string_parse(device, key);
- }
-
- if (udev_device_add_property_from_string_parse_finish(device) < 0) {
- log_debug("missing values, invalid device");
-
- udev_device_unref(device);
-
- errno = EINVAL;
-
- return NULL;
- }
+ assert_return(udev_device, 0);
- return device;
+ return sd_device_has_tag(udev_device->device, tag);
}
diff --git a/src/libudev/libudev-enumerate.c b/src/libudev/libudev-enumerate.c
index 1a880c2a34..df088946df 100644
--- a/src/libudev/libudev-enumerate.c
+++ b/src/libudev/libudev-enumerate.c
@@ -2,6 +2,7 @@
This file is part of systemd.
Copyright 2008-2012 Kay Sievers <kay@vrfy.org>
+ Copyright 2015 Tom Gundersen <teg@jklm.no>
systemd 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,17 +21,19 @@
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
-#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <dirent.h>
#include <fnmatch.h>
#include <stdbool.h>
#include <sys/stat.h>
-#include <sys/param.h>
#include "libudev.h"
-#include "libudev-private.h"
+#include "libudev-device-internal.h"
+#include "sd-device.h"
+#include "device-util.h"
+#include "device-enumerator-private.h"
+
/**
* SECTION:libudev-enumerate
@@ -40,11 +43,6 @@
* and return a sorted list of devices.
*/
-struct syspath {
- char *syspath;
- size_t len;
-};
-
/**
* udev_enumerate:
*
@@ -53,20 +51,10 @@ struct syspath {
struct udev_enumerate {
struct udev *udev;
int refcount;
- struct udev_list sysattr_match_list;
- struct udev_list sysattr_nomatch_list;
- struct udev_list subsystem_match_list;
- struct udev_list subsystem_nomatch_list;
- struct udev_list sysname_match_list;
- struct udev_list properties_match_list;
- struct udev_list tags_match_list;
- struct udev_device *parent_match;
struct udev_list devices_list;
- struct syspath *devices;
- unsigned int devices_cur;
- unsigned int devices_max;
bool devices_uptodate:1;
- bool match_is_initialized;
+
+ sd_device_enumerator *enumerator;
};
/**
@@ -77,26 +65,40 @@ struct udev_enumerate {
*
* Returns: an enumeration context.
**/
-_public_ struct udev_enumerate *udev_enumerate_new(struct udev *udev)
-{
- struct udev_enumerate *udev_enumerate;
+_public_ struct udev_enumerate *udev_enumerate_new(struct udev *udev) {
+ _cleanup_free_ struct udev_enumerate *udev_enumerate = NULL;
+ struct udev_enumerate *ret;
+ int r;
+
+ assert_return_errno(udev, NULL, EINVAL);
- if (udev == NULL)
- return NULL;
udev_enumerate = new0(struct udev_enumerate, 1);
- if (udev_enumerate == NULL)
+ if (!udev_enumerate) {
+ errno = ENOMEM;
return NULL;
+ }
+
+ r = sd_device_enumerator_new(&udev_enumerate->enumerator);
+ if (r < 0) {
+ errno = -r;
+ return NULL;
+ }
+
+ r = sd_device_enumerator_allow_uninitialized(udev_enumerate->enumerator);
+ if (r < 0) {
+ errno = -r;
+ return NULL;
+ }
+
udev_enumerate->refcount = 1;
udev_enumerate->udev = udev;
- udev_list_init(udev, &udev_enumerate->sysattr_match_list, false);
- udev_list_init(udev, &udev_enumerate->sysattr_nomatch_list, false);
- udev_list_init(udev, &udev_enumerate->subsystem_match_list, true);
- udev_list_init(udev, &udev_enumerate->subsystem_nomatch_list, true);
- udev_list_init(udev, &udev_enumerate->sysname_match_list, true);
- udev_list_init(udev, &udev_enumerate->properties_match_list, false);
- udev_list_init(udev, &udev_enumerate->tags_match_list, true);
+
udev_list_init(udev, &udev_enumerate->devices_list, false);
- return udev_enumerate;
+
+ ret = udev_enumerate;
+ udev_enumerate = NULL;
+
+ return ret;
}
/**
@@ -107,11 +109,10 @@ _public_ struct udev_enumerate *udev_enumerate_new(struct udev *udev)
*
* Returns: the passed enumeration context
**/
-_public_ struct udev_enumerate *udev_enumerate_ref(struct udev_enumerate *udev_enumerate)
-{
- if (udev_enumerate == NULL)
- return NULL;
- udev_enumerate->refcount++;
+_public_ struct udev_enumerate *udev_enumerate_ref(struct udev_enumerate *udev_enumerate) {
+ if (udev_enumerate)
+ udev_enumerate->refcount ++;
+
return udev_enumerate;
}
@@ -124,28 +125,13 @@ _public_ struct udev_enumerate *udev_enumerate_ref(struct udev_enumerate *udev_e
*
* Returns: #NULL
**/
-_public_ struct udev_enumerate *udev_enumerate_unref(struct udev_enumerate *udev_enumerate)
-{
- unsigned int i;
+_public_ struct udev_enumerate *udev_enumerate_unref(struct udev_enumerate *udev_enumerate) {
+ if (udev_enumerate && (-- udev_enumerate->refcount) == 0) {
+ udev_list_cleanup(&udev_enumerate->devices_list);
+ sd_device_enumerator_unref(udev_enumerate->enumerator);
+ free(udev_enumerate);
+ }
- if (udev_enumerate == NULL)
- return NULL;
- udev_enumerate->refcount--;
- if (udev_enumerate->refcount > 0)
- return NULL;
- udev_list_cleanup(&udev_enumerate->sysattr_match_list);
- udev_list_cleanup(&udev_enumerate->sysattr_nomatch_list);
- udev_list_cleanup(&udev_enumerate->subsystem_match_list);
- udev_list_cleanup(&udev_enumerate->subsystem_nomatch_list);
- udev_list_cleanup(&udev_enumerate->sysname_match_list);
- udev_list_cleanup(&udev_enumerate->properties_match_list);
- udev_list_cleanup(&udev_enumerate->tags_match_list);
- udev_device_unref(udev_enumerate->parent_match);
- udev_list_cleanup(&udev_enumerate->devices_list);
- for (i = 0; i < udev_enumerate->devices_cur; i++)
- free(udev_enumerate->devices[i].syspath);
- free(udev_enumerate->devices);
- free(udev_enumerate);
return NULL;
}
@@ -157,103 +143,10 @@ _public_ struct udev_enumerate *udev_enumerate_unref(struct udev_enumerate *udev
*
* Returns: a pointer to the context.
*/
-_public_ struct udev *udev_enumerate_get_udev(struct udev_enumerate *udev_enumerate)
-{
- if (udev_enumerate == NULL)
- return NULL;
- return udev_enumerate->udev;
-}
+_public_ struct udev *udev_enumerate_get_udev(struct udev_enumerate *udev_enumerate) {
+ assert_return_errno(udev_enumerate, NULL, EINVAL);
-static int syspath_add(struct udev_enumerate *udev_enumerate, const char *syspath)
-{
- char *path;
- struct syspath *entry;
-
- /* double array size if needed */
- if (udev_enumerate->devices_cur >= udev_enumerate->devices_max) {
- struct syspath *buf;
- unsigned int add;
-
- add = udev_enumerate->devices_max;
- if (add < 1024)
- add = 1024;
- buf = realloc(udev_enumerate->devices, (udev_enumerate->devices_max + add) * sizeof(struct syspath));
- if (buf == NULL)
- return -ENOMEM;
- udev_enumerate->devices = buf;
- udev_enumerate->devices_max += add;
- }
-
- path = strdup(syspath);
- if (path == NULL)
- return -ENOMEM;
- entry = &udev_enumerate->devices[udev_enumerate->devices_cur];
- entry->syspath = path;
- entry->len = strlen(path);
- udev_enumerate->devices_cur++;
- udev_enumerate->devices_uptodate = false;
- return 0;
-}
-
-static int syspath_cmp(const void *p1, const void *p2)
-{
- const struct syspath *path1 = p1;
- const struct syspath *path2 = p2;
- size_t len;
- int ret;
-
- len = MIN(path1->len, path2->len);
- ret = memcmp(path1->syspath, path2->syspath, len);
- if (ret == 0) {
- if (path1->len < path2->len)
- ret = -1;
- else if (path1->len > path2->len)
- ret = 1;
- }
- return ret;
-}
-
-/* For devices that should be moved to the absolute end of the list */
-static bool devices_delay_end(struct udev *udev, const char *syspath)
-{
- static const char *delay_device_list[] = {
- "/block/md",
- "/block/dm-",
- NULL
- };
- int i;
-
- for (i = 0; delay_device_list[i] != NULL; i++) {
- if (strstr(syspath + strlen("/sys"), delay_device_list[i]) != NULL)
- return true;
- }
- return false;
-}
-
-/* For devices that should just be moved a little bit later, just
- * before the point where some common path prefix changes. Returns the
- * number of characters that make up that common prefix */
-static size_t devices_delay_later(struct udev *udev, const char *syspath)
-{
- const char *c;
-
- /* For sound cards the control device must be enumerated last
- * to make sure it's the final device node that gets ACLs
- * applied. Applications rely on this fact and use ACL changes
- * on the control node as an indicator that the ACL change of
- * the entire sound card completed. The kernel makes this
- * guarantee when creating those devices, and hence we should
- * too when enumerating them. */
-
- if ((c = strstr(syspath, "/sound/card"))) {
- c += 11;
- c += strcspn(c, "/");
-
- if (startswith(c, "/controlC"))
- return c - syspath + 1;
- }
-
- return 0;
+ return udev_enumerate->udev;
}
/**
@@ -264,77 +157,30 @@ static size_t devices_delay_later(struct udev *udev, const char *syspath)
*
* Returns: a udev_list_entry.
*/
-_public_ struct udev_list_entry *udev_enumerate_get_list_entry(struct udev_enumerate *udev_enumerate)
-{
- if (udev_enumerate == NULL)
- return NULL;
+_public_ struct udev_list_entry *udev_enumerate_get_list_entry(struct udev_enumerate *udev_enumerate) {
+ assert_return_errno(udev_enumerate, NULL, EINVAL);
+
if (!udev_enumerate->devices_uptodate) {
- unsigned int i;
- int move_later = -1;
- unsigned int max;
- struct syspath *prev = NULL;
- size_t move_later_prefix = 0;
+ sd_device *device;
udev_list_cleanup(&udev_enumerate->devices_list);
- qsort_safe(udev_enumerate->devices, udev_enumerate->devices_cur, sizeof(struct syspath), syspath_cmp);
-
- max = udev_enumerate->devices_cur;
- for (i = 0; i < max; i++) {
- struct syspath *entry = &udev_enumerate->devices[i];
-
- /* skip duplicated entries */
- if (prev != NULL &&
- entry->len == prev->len &&
- memcmp(entry->syspath, prev->syspath, entry->len) == 0)
- continue;
- prev = entry;
-
- /* skip to be delayed devices, and add them to the end of the list */
- if (devices_delay_end(udev_enumerate->udev, entry->syspath)) {
- syspath_add(udev_enumerate, entry->syspath);
- /* need to update prev here for the case realloc() gives a different address */
- prev = &udev_enumerate->devices[i];
- continue;
- }
- /* skip to be delayed devices, and move the to
- * the point where the prefix changes. We can
- * only move one item at a time. */
- if (move_later == -1) {
- move_later_prefix = devices_delay_later(udev_enumerate->udev, entry->syspath);
+ FOREACH_DEVICE_AND_SUBSYSTEM(udev_enumerate->enumerator, device) {
+ const char *syspath;
+ int r;
- if (move_later_prefix > 0) {
- move_later = i;
- continue;
- }
+ r = sd_device_get_syspath(device, &syspath);
+ if (r < 0) {
+ errno = -r;
+ return NULL;
}
- if ((move_later >= 0) &&
- !strneq(entry->syspath, udev_enumerate->devices[move_later].syspath, move_later_prefix)) {
-
- udev_list_entry_add(&udev_enumerate->devices_list,
- udev_enumerate->devices[move_later].syspath, NULL);
- move_later = -1;
- }
-
- udev_list_entry_add(&udev_enumerate->devices_list, entry->syspath, NULL);
+ udev_list_entry_add(&udev_enumerate->devices_list, syspath, NULL);
}
- if (move_later >= 0)
- udev_list_entry_add(&udev_enumerate->devices_list,
- udev_enumerate->devices[move_later].syspath, NULL);
-
- /* add and cleanup delayed devices from end of list */
- for (i = max; i < udev_enumerate->devices_cur; i++) {
- struct syspath *entry = &udev_enumerate->devices[i];
-
- udev_list_entry_add(&udev_enumerate->devices_list, entry->syspath, NULL);
- free(entry->syspath);
- }
- udev_enumerate->devices_cur = max;
-
udev_enumerate->devices_uptodate = true;
}
+
return udev_list_get_entry(&udev_enumerate->devices_list);
}
@@ -347,15 +193,13 @@ _public_ struct udev_list_entry *udev_enumerate_get_list_entry(struct udev_enume
*
* Returns: 0 on success, otherwise a negative error value.
*/
-_public_ int udev_enumerate_add_match_subsystem(struct udev_enumerate *udev_enumerate, const char *subsystem)
-{
- if (udev_enumerate == NULL)
- return -EINVAL;
- if (subsystem == NULL)
+_public_ int udev_enumerate_add_match_subsystem(struct udev_enumerate *udev_enumerate, const char *subsystem) {
+ assert_return(udev_enumerate, -EINVAL);
+
+ if (!subsystem)
return 0;
- if (udev_list_entry_add(&udev_enumerate->subsystem_match_list, subsystem, NULL) == NULL)
- return -ENOMEM;
- return 0;
+
+ return sd_device_enumerator_add_match_subsystem(udev_enumerate->enumerator, subsystem, true);
}
/**
@@ -367,15 +211,13 @@ _public_ int udev_enumerate_add_match_subsystem(struct udev_enumerate *udev_enum
*
* Returns: 0 on success, otherwise a negative error value.
*/
-_public_ int udev_enumerate_add_nomatch_subsystem(struct udev_enumerate *udev_enumerate, const char *subsystem)
-{
- if (udev_enumerate == NULL)
- return -EINVAL;
- if (subsystem == NULL)
+_public_ int udev_enumerate_add_nomatch_subsystem(struct udev_enumerate *udev_enumerate, const char *subsystem) {
+ assert_return(udev_enumerate, -EINVAL);
+
+ if (!subsystem)
return 0;
- if (udev_list_entry_add(&udev_enumerate->subsystem_nomatch_list, subsystem, NULL) == NULL)
- return -ENOMEM;
- return 0;
+
+ return sd_device_enumerator_add_match_subsystem(udev_enumerate->enumerator, subsystem, false);
}
/**
@@ -388,15 +230,13 @@ _public_ int udev_enumerate_add_nomatch_subsystem(struct udev_enumerate *udev_en
*
* Returns: 0 on success, otherwise a negative error value.
*/
-_public_ int udev_enumerate_add_match_sysattr(struct udev_enumerate *udev_enumerate, const char *sysattr, const char *value)
-{
- if (udev_enumerate == NULL)
- return -EINVAL;
- if (sysattr == NULL)
+_public_ int udev_enumerate_add_match_sysattr(struct udev_enumerate *udev_enumerate, const char *sysattr, const char *value) {
+ assert_return(udev_enumerate, -EINVAL);
+
+ if (!sysattr)
return 0;
- if (udev_list_entry_add(&udev_enumerate->sysattr_match_list, sysattr, value) == NULL)
- return -ENOMEM;
- return 0;
+
+ return sd_device_enumerator_add_match_sysattr(udev_enumerate->enumerator, sysattr, value, true);
}
/**
@@ -409,35 +249,13 @@ _public_ int udev_enumerate_add_match_sysattr(struct udev_enumerate *udev_enumer
*
* Returns: 0 on success, otherwise a negative error value.
*/
-_public_ int udev_enumerate_add_nomatch_sysattr(struct udev_enumerate *udev_enumerate, const char *sysattr, const char *value)
-{
- if (udev_enumerate == NULL)
- return -EINVAL;
- if (sysattr == NULL)
+_public_ int udev_enumerate_add_nomatch_sysattr(struct udev_enumerate *udev_enumerate, const char *sysattr, const char *value) {
+ assert_return(udev_enumerate, -EINVAL);
+
+ if (!sysattr)
return 0;
- if (udev_list_entry_add(&udev_enumerate->sysattr_nomatch_list, sysattr, value) == NULL)
- return -ENOMEM;
- return 0;
-}
-static int match_sysattr_value(struct udev_device *dev, const char *sysattr, const char *match_val)
-{
- const char *val = NULL;
- bool match = false;
-
- val = udev_device_get_sysattr_value(dev, sysattr);
- if (val == NULL)
- goto exit;
- if (match_val == NULL) {
- match = true;
- goto exit;
- }
- if (fnmatch(match_val, val, 0) == 0) {
- match = true;
- goto exit;
- }
-exit:
- return match;
+ return sd_device_enumerator_add_match_sysattr(udev_enumerate->enumerator, sysattr, value, false);
}
/**
@@ -450,15 +268,13 @@ exit:
*
* Returns: 0 on success, otherwise a negative error value.
*/
-_public_ int udev_enumerate_add_match_property(struct udev_enumerate *udev_enumerate, const char *property, const char *value)
-{
- if (udev_enumerate == NULL)
- return -EINVAL;
- if (property == NULL)
+_public_ int udev_enumerate_add_match_property(struct udev_enumerate *udev_enumerate, const char *property, const char *value) {
+ assert_return(udev_enumerate, -EINVAL);
+
+ if (!property)
return 0;
- if (udev_list_entry_add(&udev_enumerate->properties_match_list, property, value) == NULL)
- return -ENOMEM;
- return 0;
+
+ return sd_device_enumerator_add_match_property(udev_enumerate->enumerator, property, value);
}
/**
@@ -470,15 +286,13 @@ _public_ int udev_enumerate_add_match_property(struct udev_enumerate *udev_enume
*
* Returns: 0 on success, otherwise a negative error value.
*/
-_public_ int udev_enumerate_add_match_tag(struct udev_enumerate *udev_enumerate, const char *tag)
-{
- if (udev_enumerate == NULL)
- return -EINVAL;
- if (tag == NULL)
+_public_ int udev_enumerate_add_match_tag(struct udev_enumerate *udev_enumerate, const char *tag) {
+ assert_return(udev_enumerate, -EINVAL);
+
+ if (!tag)
return 0;
- if (udev_list_entry_add(&udev_enumerate->tags_match_list, tag, NULL) == NULL)
- return -ENOMEM;
- return 0;
+
+ return sd_device_enumerator_add_match_tag(udev_enumerate->enumerator, tag);
}
/**
@@ -494,16 +308,13 @@ _public_ int udev_enumerate_add_match_tag(struct udev_enumerate *udev_enumerate,
*
* Returns: 0 on success, otherwise a negative error value.
*/
-_public_ int udev_enumerate_add_match_parent(struct udev_enumerate *udev_enumerate, struct udev_device *parent)
-{
- if (udev_enumerate == NULL)
- return -EINVAL;
- if (parent == NULL)
+_public_ int udev_enumerate_add_match_parent(struct udev_enumerate *udev_enumerate, struct udev_device *parent) {
+ assert_return(udev_enumerate, -EINVAL);
+
+ if (!parent)
return 0;
- if (udev_enumerate->parent_match != NULL)
- udev_device_unref(udev_enumerate->parent_match);
- udev_enumerate->parent_match = udev_device_ref(parent);
- return 0;
+
+ return sd_device_enumerator_add_match_parent(udev_enumerate->enumerator, parent->device);
}
/**
@@ -524,12 +335,10 @@ _public_ int udev_enumerate_add_match_parent(struct udev_enumerate *udev_enumera
*
* Returns: 0 on success, otherwise a negative error value.
*/
-_public_ int udev_enumerate_add_match_is_initialized(struct udev_enumerate *udev_enumerate)
-{
- if (udev_enumerate == NULL)
- return -EINVAL;
- udev_enumerate->match_is_initialized = true;
- return 0;
+_public_ int udev_enumerate_add_match_is_initialized(struct udev_enumerate *udev_enumerate) {
+ assert_return(udev_enumerate, -EINVAL);
+
+ return device_enumerator_add_match_is_initialized(udev_enumerate->enumerator);
}
/**
@@ -541,224 +350,13 @@ _public_ int udev_enumerate_add_match_is_initialized(struct udev_enumerate *udev
*
* Returns: 0 on success, otherwise a negative error value.
*/
-_public_ int udev_enumerate_add_match_sysname(struct udev_enumerate *udev_enumerate, const char *sysname)
-{
- if (udev_enumerate == NULL)
- return -EINVAL;
- if (sysname == NULL)
- return 0;
- if (udev_list_entry_add(&udev_enumerate->sysname_match_list, sysname, NULL) == NULL)
- return -ENOMEM;
- return 0;
-}
-
-static bool match_sysattr(struct udev_enumerate *udev_enumerate, struct udev_device *dev)
-{
- struct udev_list_entry *list_entry;
-
- /* skip list */
- udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_enumerate->sysattr_nomatch_list)) {
- if (match_sysattr_value(dev, udev_list_entry_get_name(list_entry),
- udev_list_entry_get_value(list_entry)))
- return false;
- }
- /* include list */
- if (udev_list_get_entry(&udev_enumerate->sysattr_match_list) != NULL) {
- udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_enumerate->sysattr_match_list)) {
- /* anything that does not match, will make it FALSE */
- if (!match_sysattr_value(dev, udev_list_entry_get_name(list_entry),
- udev_list_entry_get_value(list_entry)))
- return false;
- }
- return true;
- }
- return true;
-}
-
-static bool match_property(struct udev_enumerate *udev_enumerate, struct udev_device *dev)
-{
- struct udev_list_entry *list_entry;
- bool match = false;
-
- /* no match always matches */
- if (udev_list_get_entry(&udev_enumerate->properties_match_list) == NULL)
- return true;
-
- /* loop over matches */
- udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_enumerate->properties_match_list)) {
- const char *match_key = udev_list_entry_get_name(list_entry);
- const char *match_value = udev_list_entry_get_value(list_entry);
- struct udev_list_entry *property_entry;
-
- /* loop over device properties */
- udev_list_entry_foreach(property_entry, udev_device_get_properties_list_entry(dev)) {
- const char *dev_key = udev_list_entry_get_name(property_entry);
- const char *dev_value = udev_list_entry_get_value(property_entry);
-
- if (fnmatch(match_key, dev_key, 0) != 0)
- continue;
- if (match_value == NULL && dev_value == NULL) {
- match = true;
- goto out;
- }
- if (match_value == NULL || dev_value == NULL)
- continue;
- if (fnmatch(match_value, dev_value, 0) == 0) {
- match = true;
- goto out;
- }
- }
- }
-out:
- return match;
-}
-
-static bool match_tag(struct udev_enumerate *udev_enumerate, struct udev_device *dev)
-{
- struct udev_list_entry *list_entry;
-
- /* no match always matches */
- if (udev_list_get_entry(&udev_enumerate->tags_match_list) == NULL)
- return true;
-
- /* loop over matches */
- udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_enumerate->tags_match_list))
- if (!udev_device_has_tag(dev, udev_list_entry_get_name(list_entry)))
- return false;
-
- return true;
-}
-
-static bool match_parent(struct udev_enumerate *udev_enumerate, struct udev_device *dev)
-{
- if (udev_enumerate->parent_match == NULL)
- return true;
-
- return startswith(udev_device_get_devpath(dev), udev_device_get_devpath(udev_enumerate->parent_match));
-}
-
-static bool match_sysname(struct udev_enumerate *udev_enumerate, const char *sysname)
-{
- struct udev_list_entry *list_entry;
-
- if (udev_list_get_entry(&udev_enumerate->sysname_match_list) == NULL)
- return true;
-
- udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_enumerate->sysname_match_list)) {
- if (fnmatch(udev_list_entry_get_name(list_entry), sysname, 0) != 0)
- continue;
- return true;
- }
- return false;
-}
-
-static int scan_dir_and_add_devices(struct udev_enumerate *udev_enumerate,
- const char *basedir, const char *subdir1, const char *subdir2)
-{
- char path[UTIL_PATH_SIZE];
- size_t l;
- char *s;
- DIR *dir;
- struct dirent *dent;
-
- s = path;
- l = strpcpyl(&s, sizeof(path), "/sys/", basedir, NULL);
- if (subdir1 != NULL)
- l = strpcpyl(&s, l, "/", subdir1, NULL);
- if (subdir2 != NULL)
- strpcpyl(&s, l, "/", subdir2, NULL);
- dir = opendir(path);
- if (dir == NULL)
- return -ENOENT;
- for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
- char syspath[UTIL_PATH_SIZE];
- struct udev_device *dev;
-
- if (dent->d_name[0] == '.')
- continue;
-
- if (!match_sysname(udev_enumerate, dent->d_name))
- continue;
-
- strscpyl(syspath, sizeof(syspath), path, "/", dent->d_name, NULL);
- dev = udev_device_new_from_syspath(udev_enumerate->udev, syspath);
- if (dev == NULL)
- continue;
-
- if (udev_enumerate->match_is_initialized) {
- /*
- * All devices with a device node or network interfaces
- * possibly need udev to adjust the device node permission
- * or context, or rename the interface before it can be
- * reliably used from other processes.
- *
- * For now, we can only check these types of devices, we
- * might not store a database, and have no way to find out
- * for all other types of devices.
- */
- if (!udev_device_get_is_initialized(dev) &&
- (major(udev_device_get_devnum(dev)) > 0 || udev_device_get_ifindex(dev) > 0))
- goto nomatch;
- }
- if (!match_parent(udev_enumerate, dev))
- goto nomatch;
- if (!match_tag(udev_enumerate, dev))
- goto nomatch;
- if (!match_property(udev_enumerate, dev))
- goto nomatch;
- if (!match_sysattr(udev_enumerate, dev))
- goto nomatch;
-
- syspath_add(udev_enumerate, udev_device_get_syspath(dev));
-nomatch:
- udev_device_unref(dev);
- }
- closedir(dir);
- return 0;
-}
-
-static bool match_subsystem(struct udev_enumerate *udev_enumerate, const char *subsystem)
-{
- struct udev_list_entry *list_entry;
-
- if (!subsystem)
- return false;
-
- udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_enumerate->subsystem_nomatch_list)) {
- if (fnmatch(udev_list_entry_get_name(list_entry), subsystem, 0) == 0)
- return false;
- }
+_public_ int udev_enumerate_add_match_sysname(struct udev_enumerate *udev_enumerate, const char *sysname) {
+ assert_return(udev_enumerate, -EINVAL);
- if (udev_list_get_entry(&udev_enumerate->subsystem_match_list) != NULL) {
- udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_enumerate->subsystem_match_list)) {
- if (fnmatch(udev_list_entry_get_name(list_entry), subsystem, 0) == 0)
- return true;
- }
- return false;
- }
-
- return true;
-}
+ if (!sysname)
+ return 0;
-static int scan_dir(struct udev_enumerate *udev_enumerate, const char *basedir, const char *subdir, const char *subsystem)
-{
- char path[UTIL_PATH_SIZE];
- DIR *dir;
- struct dirent *dent;
-
- strscpyl(path, sizeof(path), "/sys/", basedir, NULL);
- dir = opendir(path);
- if (dir == NULL)
- return -1;
- for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
- if (dent->d_name[0] == '.')
- continue;
- if (!match_subsystem(udev_enumerate, subsystem != NULL ? subsystem : dent->d_name))
- continue;
- scan_dir_and_add_devices(udev_enumerate, basedir, dent->d_name, subdir);
- }
- closedir(dir);
- return 0;
+ return sd_device_enumerator_add_match_sysname(udev_enumerate->enumerator, sysname);
}
/**
@@ -770,141 +368,23 @@ static int scan_dir(struct udev_enumerate *udev_enumerate, const char *basedir,
*
* Returns: 0 on success, otherwise a negative error value.
*/
-_public_ int udev_enumerate_add_syspath(struct udev_enumerate *udev_enumerate, const char *syspath)
-{
- struct udev_device *udev_device;
+_public_ int udev_enumerate_add_syspath(struct udev_enumerate *udev_enumerate, const char *syspath) {
+ _cleanup_device_unref_ sd_device *device = NULL;
+ int r;
- if (udev_enumerate == NULL)
- return -EINVAL;
- if (syspath == NULL)
- return 0;
- /* resolve to real syspath */
- udev_device = udev_device_new_from_syspath(udev_enumerate->udev, syspath);
- if (udev_device == NULL)
- return -EINVAL;
- syspath_add(udev_enumerate, udev_device_get_syspath(udev_device));
- udev_device_unref(udev_device);
- return 0;
-}
+ assert_return(udev_enumerate, -EINVAL);
-static int scan_devices_tags(struct udev_enumerate *udev_enumerate)
-{
- struct udev_list_entry *list_entry;
-
- /* scan only tagged devices, use tags reverse-index, instead of searching all devices in /sys */
- udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_enumerate->tags_match_list)) {
- DIR *dir;
- struct dirent *dent;
- char path[UTIL_PATH_SIZE];
-
- strscpyl(path, sizeof(path), "/run/udev/tags/", udev_list_entry_get_name(list_entry), NULL);
- dir = opendir(path);
- if (dir == NULL)
- continue;
- for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
- struct udev_device *dev;
-
- if (dent->d_name[0] == '.')
- continue;
-
- dev = udev_device_new_from_device_id(udev_enumerate->udev, dent->d_name);
- if (dev == NULL)
- continue;
-
- if (!match_subsystem(udev_enumerate, udev_device_get_subsystem(dev)))
- goto nomatch;
- if (!match_sysname(udev_enumerate, udev_device_get_sysname(dev)))
- goto nomatch;
- if (!match_parent(udev_enumerate, dev))
- goto nomatch;
- if (!match_property(udev_enumerate, dev))
- goto nomatch;
- if (!match_sysattr(udev_enumerate, dev))
- goto nomatch;
-
- syspath_add(udev_enumerate, udev_device_get_syspath(dev));
-nomatch:
- udev_device_unref(dev);
- }
- closedir(dir);
- }
- return 0;
-}
-
-static int parent_add_child(struct udev_enumerate *enumerate, const char *path)
-{
- struct udev_device *dev;
- int r = 0;
-
- dev = udev_device_new_from_syspath(enumerate->udev, path);
- if (dev == NULL)
- return -ENODEV;
-
- if (!match_subsystem(enumerate, udev_device_get_subsystem(dev)))
- goto nomatch;
- if (!match_sysname(enumerate, udev_device_get_sysname(dev)))
- goto nomatch;
- if (!match_property(enumerate, dev))
- goto nomatch;
- if (!match_sysattr(enumerate, dev))
- goto nomatch;
-
- syspath_add(enumerate, udev_device_get_syspath(dev));
- r = 1;
-
-nomatch:
- udev_device_unref(dev);
- return r;
-}
-
-static int parent_crawl_children(struct udev_enumerate *enumerate, const char *path, int maxdepth)
-{
- DIR *d;
- struct dirent *dent;
-
- d = opendir(path);
- if (d == NULL)
- return -errno;
-
- for (dent = readdir(d); dent != NULL; dent = readdir(d)) {
- char *child;
-
- if (dent->d_name[0] == '.')
- continue;
- if (dent->d_type != DT_DIR)
- continue;
- if (asprintf(&child, "%s/%s", path, dent->d_name) < 0)
- continue;
- parent_add_child(enumerate, child);
- if (maxdepth > 0)
- parent_crawl_children(enumerate, child, maxdepth-1);
- free(child);
- }
-
- closedir(d);
- return 0;
-}
+ if (!syspath)
+ return 0;
-static int scan_devices_children(struct udev_enumerate *enumerate)
-{
- const char *path;
+ r = sd_device_new_from_syspath(&device, syspath);
+ if (r < 0)
+ return r;
- path = udev_device_get_syspath(enumerate->parent_match);
- parent_add_child(enumerate, path);
- return parent_crawl_children(enumerate, path, 256);
-}
+ r = device_enumerator_add_device(udev_enumerate->enumerator, device);
+ if (r < 0)
+ return r;
-static int scan_devices_all(struct udev_enumerate *udev_enumerate)
-{
- struct stat statbuf;
-
- if (stat("/sys/subsystem", &statbuf) == 0) {
- /* we have /subsystem/, forget all the old stuff */
- scan_dir(udev_enumerate, "subsystem", "devices", NULL);
- } else {
- scan_dir(udev_enumerate, "bus", "devices", NULL);
- scan_dir(udev_enumerate, "class", NULL, NULL);
- }
return 0;
}
@@ -917,21 +397,10 @@ static int scan_devices_all(struct udev_enumerate *udev_enumerate)
*
* Returns: 0 on success, otherwise a negative error value.
**/
-_public_ int udev_enumerate_scan_devices(struct udev_enumerate *udev_enumerate)
-{
- if (udev_enumerate == NULL)
- return -EINVAL;
+_public_ int udev_enumerate_scan_devices(struct udev_enumerate *udev_enumerate) {
+ assert_return(udev_enumerate, -EINVAL);
- /* efficiently lookup tags only, we maintain a reverse-index */
- if (udev_list_get_entry(&udev_enumerate->tags_match_list) != NULL)
- return scan_devices_tags(udev_enumerate);
-
- /* walk the subtree of one parent device only */
- if (udev_enumerate->parent_match != NULL)
- return scan_devices_children(udev_enumerate);
-
- /* scan devices of all subsystems */
- return scan_devices_all(udev_enumerate);
+ return device_enumerator_scan_devices(udev_enumerate->enumerator);
}
/**
@@ -942,29 +411,8 @@ _public_ int udev_enumerate_scan_devices(struct udev_enumerate *udev_enumerate)
*
* Returns: 0 on success, otherwise a negative error value.
**/
-_public_ int udev_enumerate_scan_subsystems(struct udev_enumerate *udev_enumerate)
-{
- struct stat statbuf;
- const char *subsysdir;
-
- if (udev_enumerate == NULL)
- return -EINVAL;
-
- /* all kernel modules */
- if (match_subsystem(udev_enumerate, "module"))
- scan_dir_and_add_devices(udev_enumerate, "module", NULL, NULL);
-
- if (stat("/sys/subsystem", &statbuf) == 0)
- subsysdir = "subsystem";
- else
- subsysdir = "bus";
-
- /* all subsystems (only buses support coldplug) */
- if (match_subsystem(udev_enumerate, "subsystem"))
- scan_dir_and_add_devices(udev_enumerate, subsysdir, NULL, NULL);
-
- /* all subsystem drivers */
- if (match_subsystem(udev_enumerate, "drivers"))
- scan_dir(udev_enumerate, subsysdir, "drivers", "drivers");
- return 0;
+_public_ int udev_enumerate_scan_subsystems(struct udev_enumerate *udev_enumerate) {
+ assert_return(udev_enumerate, -EINVAL);
+
+ return device_enumerator_scan_subsystems(udev_enumerate->enumerator);
}
diff --git a/src/libudev/libudev-list.c b/src/libudev/libudev-list.c
index 42fcb853c7..044ee3a0cb 100644
--- a/src/libudev/libudev-list.c
+++ b/src/libudev/libudev-list.c
@@ -17,14 +17,11 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
-#include <unistd.h>
#include <errno.h>
#include <string.h>
-#include "libudev.h"
#include "libudev-private.h"
/**
diff --git a/src/libudev/libudev-monitor.c b/src/libudev/libudev-monitor.c
index 3f1fee7f7e..282aa2b0d9 100644
--- a/src/libudev/libudev-monitor.c
+++ b/src/libudev/libudev-monitor.c
@@ -23,12 +23,8 @@
#include <unistd.h>
#include <errno.h>
#include <string.h>
-#include <dirent.h>
#include <poll.h>
-#include <sys/stat.h>
#include <sys/socket.h>
-#include <sys/un.h>
-#include <arpa/inet.h>
#include <linux/netlink.h>
#include <linux/filter.h>
@@ -36,6 +32,7 @@
#include "libudev-private.h"
#include "socket-util.h"
#include "missing.h"
+#include "formats-util.h"
/**
* SECTION:libudev-monitor
@@ -147,6 +144,22 @@ static bool udev_has_devtmpfs(struct udev *udev) {
return false;
}
+static void monitor_set_nl_address(struct udev_monitor *udev_monitor) {
+ union sockaddr_union snl;
+ socklen_t addrlen;
+ int r;
+
+ assert(udev_monitor);
+
+ /* get the address the kernel has assigned us
+ * it is usually, but not necessarily the pid
+ */
+ addrlen = sizeof(struct sockaddr_nl);
+ r = getsockname(udev_monitor->sock, &snl.sa, &addrlen);
+ if (r >= 0)
+ udev_monitor->snl.nl.nl_pid = snl.nl.nl_pid;
+}
+
struct udev_monitor *udev_monitor_new_from_netlink_fd(struct udev *udev, const char *name, int fd)
{
struct udev_monitor *udev_monitor;
@@ -186,7 +199,7 @@ struct udev_monitor *udev_monitor_new_from_netlink_fd(struct udev *udev, const c
if (fd < 0) {
udev_monitor->sock = socket(PF_NETLINK, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, NETLINK_KOBJECT_UEVENT);
- if (udev_monitor->sock == -1) {
+ if (udev_monitor->sock < 0) {
log_debug_errno(errno, "error getting socket: %m");
free(udev_monitor);
return NULL;
@@ -194,6 +207,7 @@ struct udev_monitor *udev_monitor_new_from_netlink_fd(struct udev *udev, const c
} else {
udev_monitor->bound = true;
udev_monitor->sock = fd;
+ monitor_set_nl_address(udev_monitor);
}
udev_monitor->snl.nl.nl_family = AF_NETLINK;
@@ -369,6 +383,7 @@ int udev_monitor_allow_unicast_sender(struct udev_monitor *udev_monitor, struct
udev_monitor->snl_trusted_sender.nl.nl_pid = sender->snl.nl.nl_pid;
return 0;
}
+
/**
* udev_monitor_enable_receiving:
* @udev_monitor: the monitor which should receive events
@@ -391,19 +406,9 @@ _public_ int udev_monitor_enable_receiving(struct udev_monitor *udev_monitor)
udev_monitor->bound = true;
}
- if (err >= 0) {
- union sockaddr_union snl;
- socklen_t addrlen;
-
- /*
- * get the address the kernel has assigned us
- * it is usually, but not necessarily the pid
- */
- addrlen = sizeof(struct sockaddr_nl);
- err = getsockname(udev_monitor->sock, &snl.sa, &addrlen);
- if (err == 0)
- udev_monitor->snl.nl.nl_pid = snl.nl.nl_pid;
- } else {
+ if (err >= 0)
+ monitor_set_nl_address(udev_monitor);
+ else {
log_debug_errno(errno, "bind failed: %m");
return -errno;
}
@@ -647,6 +652,8 @@ retry:
return NULL;
}
if (buf.nlh.properties_off+32 > (size_t)buflen) {
+ log_debug("message smaller than expected (%u > %zd)",
+ buf.nlh.properties_off+32, buflen);
return NULL;
}
@@ -670,8 +677,10 @@ retry:
}
udev_device = udev_device_new_from_nulstr(udev_monitor->udev, &buf.raw[bufpos], buflen - bufpos);
- if (!udev_device)
+ if (!udev_device) {
+ log_debug("could not create device: %m");
return NULL;
+ }
if (is_initialized)
udev_device_set_is_initialized(udev_device);
@@ -698,32 +707,36 @@ retry:
int udev_monitor_send_device(struct udev_monitor *udev_monitor,
struct udev_monitor *destination, struct udev_device *udev_device)
{
- const char *buf;
- ssize_t blen;
- ssize_t count;
- struct msghdr smsg;
- struct iovec iov[2];
- const char *val;
- struct udev_monitor_netlink_header nlh;
+ const char *buf, *val;
+ ssize_t blen, count;
+ struct udev_monitor_netlink_header nlh = {
+ .prefix = "libudev",
+ .magic = htonl(UDEV_MONITOR_MAGIC),
+ .header_size = sizeof nlh,
+ };
+ struct iovec iov[2] = {
+ { .iov_base = &nlh, .iov_len = sizeof nlh },
+ };
+ struct msghdr smsg = {
+ .msg_iov = iov,
+ .msg_iovlen = 2,
+ };
struct udev_list_entry *list_entry;
uint64_t tag_bloom_bits;
blen = udev_device_get_properties_monitor_buf(udev_device, &buf);
- if (blen < 32)
+ if (blen < 32) {
+ log_debug("device buffer is too small to contain a valid device");
return -EINVAL;
+ }
- /* add versioned header */
- memzero(&nlh, sizeof(struct udev_monitor_netlink_header));
- memcpy(nlh.prefix, "libudev", 8);
- nlh.magic = htonl(UDEV_MONITOR_MAGIC);
- nlh.header_size = sizeof(struct udev_monitor_netlink_header);
+ /* fill in versioned header */
val = udev_device_get_subsystem(udev_device);
nlh.filter_subsystem_hash = htonl(util_string_hash32(val));
+
val = udev_device_get_devtype(udev_device);
if (val != NULL)
nlh.filter_devtype_hash = htonl(util_string_hash32(val));
- iov[0].iov_base = &nlh;
- iov[0].iov_len = sizeof(struct udev_monitor_netlink_header);
/* add tag bloom filter */
tag_bloom_bits = 0;
@@ -740,22 +753,27 @@ int udev_monitor_send_device(struct udev_monitor *udev_monitor,
iov[1].iov_base = (char *)buf;
iov[1].iov_len = blen;
- memzero(&smsg, sizeof(struct msghdr));
- smsg.msg_iov = iov;
- smsg.msg_iovlen = 2;
/*
* Use custom address for target, or the default one.
*
* If we send to a multicast group, we will get
* ECONNREFUSED, which is expected.
*/
- if (destination != NULL)
+ if (destination)
smsg.msg_name = &destination->snl;
else
smsg.msg_name = &udev_monitor->snl_destination;
smsg.msg_namelen = sizeof(struct sockaddr_nl);
count = sendmsg(udev_monitor->sock, &smsg, 0);
- log_debug("passed %zi bytes to netlink monitor %p", count, udev_monitor);
+ if (count < 0) {
+ if (!destination && errno == ECONNREFUSED) {
+ log_debug("passed device to netlink monitor %p", udev_monitor);
+ return 0;
+ } else
+ return -errno;
+ }
+
+ log_debug("passed %zi byte device to netlink monitor %p", count, udev_monitor);
return count;
}
diff --git a/src/libudev/libudev-private.h b/src/libudev/libudev-private.h
index 96363040a0..1240ea79cc 100644
--- a/src/libudev/libudev-private.h
+++ b/src/libudev/libudev-private.h
@@ -38,21 +38,19 @@ int udev_get_rules_path(struct udev *udev, char **path[], usec_t *ts_usec[]);
/* libudev-device.c */
struct udev_device *udev_device_new_from_nulstr(struct udev *udev, char *nulstr, ssize_t buflen);
+struct udev_device *udev_device_new_from_synthetic_event(struct udev *udev, const char *syspath, const char *action);
struct udev_device *udev_device_shallow_clone(struct udev_device *old_device);
+struct udev_device *udev_device_clone_with_db(struct udev_device *old_device);
+int udev_device_copy_properties(struct udev_device *dst, struct udev_device *src);
mode_t udev_device_get_devnode_mode(struct udev_device *udev_device);
uid_t udev_device_get_devnode_uid(struct udev_device *udev_device);
gid_t udev_device_get_devnode_gid(struct udev_device *udev_device);
int udev_device_rename(struct udev_device *udev_device, const char *new_name);
int udev_device_add_devlink(struct udev_device *udev_device, const char *devlink);
void udev_device_cleanup_devlinks_list(struct udev_device *udev_device);
-struct udev_list_entry *udev_device_add_property(struct udev_device *udev_device, const char *key, const char *value);
-void udev_device_add_property_from_string_parse(struct udev_device *udev_device, const char *property);
-int udev_device_add_property_from_string_parse_finish(struct udev_device *udev_device);
+int udev_device_add_property(struct udev_device *udev_device, const char *key, const char *value);
char **udev_device_get_properties_envp(struct udev_device *udev_device);
ssize_t udev_device_get_properties_monitor_buf(struct udev_device *udev_device, const char **buf);
-int udev_device_read_db(struct udev_device *udev_device, const char *dbfile);
-int udev_device_read_uevent_file(struct udev_device *udev_device);
-int udev_device_set_action(struct udev_device *udev_device, const char *action);
const char *udev_device_get_devpath_old(struct udev_device *udev_device);
const char *udev_device_get_id_filename(struct udev_device *udev_device);
void udev_device_set_is_initialized(struct udev_device *udev_device);
@@ -60,7 +58,7 @@ int udev_device_add_tag(struct udev_device *udev_device, const char *tag);
void udev_device_remove_tag(struct udev_device *udev_device, const char *tag);
void udev_device_cleanup_tags_list(struct udev_device *udev_device);
usec_t udev_device_get_usec_initialized(struct udev_device *udev_device);
-void udev_device_set_usec_initialized(struct udev_device *udev_device, usec_t usec_initialized);
+void udev_device_ensure_usec_initialized(struct udev_device *udev_device, struct udev_device *old_device);
int udev_device_get_devlink_priority(struct udev_device *udev_device);
int udev_device_set_devlink_priority(struct udev_device *udev_device, int prio);
int udev_device_get_watch_handle(struct udev_device *udev_device);
@@ -69,6 +67,7 @@ int udev_device_get_ifindex(struct udev_device *udev_device);
void udev_device_set_info_loaded(struct udev_device *device);
bool udev_device_get_db_persist(struct udev_device *udev_device);
void udev_device_set_db_persist(struct udev_device *udev_device);
+void udev_device_read_db(struct udev_device *udev_device);
/* libudev-device-private.c */
int udev_device_update_db(struct udev_device *udev_device);
@@ -94,7 +93,6 @@ struct udev_list {
unsigned int entries_max;
bool unique;
};
-#define UDEV_LIST(list) struct udev_list_node list = { &(list), &(list) }
void udev_list_node_init(struct udev_list_node *list);
int udev_list_node_is_empty(struct udev_list_node *list);
void udev_list_node_append(struct udev_list_node *new, struct udev_list_node *list);
diff --git a/src/libudev/libudev-queue.c b/src/libudev/libudev-queue.c
index 8ef1f3d4b0..11e15d13e6 100644
--- a/src/libudev/libudev-queue.c
+++ b/src/libudev/libudev-queue.c
@@ -18,17 +18,12 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <unistd.h>
#include <errno.h>
-#include <string.h>
-#include <limits.h>
-#include <sys/stat.h>
#include <sys/inotify.h>
-#include "libudev.h"
#include "libudev-private.h"
/**
diff --git a/src/libudev/libudev-util.c b/src/libudev/libudev-util.c
index 291829e6d8..f4656277c6 100644
--- a/src/libudev/libudev-util.c
+++ b/src/libudev/libudev-util.c
@@ -17,20 +17,12 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
-#include <dirent.h>
#include <ctype.h>
-#include <fcntl.h>
-#include <time.h>
-#include <pwd.h>
-#include <grp.h>
-#include <sys/stat.h>
-#include <sys/param.h>
#include "device-nodes.h"
#include "libudev.h"
@@ -224,7 +216,7 @@ int util_replace_whitespace(const char *str, char *to, size_t len)
/* strip leading whitespace */
i = 0;
- while (isspace(str[i]) && (i < len))
+ while ((i < len) && isspace(str[i]))
i++;
j = 0;
diff --git a/src/libudev/libudev.c b/src/libudev/libudev.c
index 8785f22071..ec15d2576b 100644
--- a/src/libudev/libudev.c
+++ b/src/libudev/libudev.c
@@ -21,11 +21,8 @@
#include <stdlib.h>
#include <stddef.h>
#include <stdarg.h>
-#include <unistd.h>
-#include <errno.h>
#include <string.h>
#include <ctype.h>
-#include <time.h>
#include "libudev.h"
#include "libudev-private.h"
diff --git a/src/libudev/libudev.h b/src/libudev/libudev.h
index a94505c09e..eb58740d26 100644
--- a/src/libudev/libudev.h
+++ b/src/libudev/libudev.h
@@ -22,7 +22,6 @@
#include <stdarg.h>
#include <sys/types.h>
-#include <sys/stat.h>
#ifdef __cplusplus
extern "C" {
diff --git a/src/locale/localectl.c b/src/locale/localectl.c
index 41a88a828c..8c60339e3e 100644
--- a/src/locale/localectl.c
+++ b/src/locale/localectl.c
@@ -23,25 +23,19 @@
#include <locale.h>
#include <stdlib.h>
#include <stdbool.h>
-#include <unistd.h>
#include <getopt.h>
#include <string.h>
#include <ftw.h>
-#include <sys/mman.h>
-#include <fcntl.h>
#include "sd-bus.h"
#include "bus-util.h"
#include "bus-error.h"
-#include "bus-message.h"
#include "util.h"
#include "spawn-polkit-agent.h"
#include "build.h"
#include "strv.h"
#include "pager.h"
#include "set.h"
-#include "path-util.h"
-#include "utf8.h"
#include "def.h"
#include "virt.h"
#include "fileio.h"
@@ -83,7 +77,7 @@ typedef struct StatusInfo {
const char *x11_options;
} StatusInfo;
-static void print_overriden_variables(void) {
+static void print_overridden_variables(void) {
int r;
char *variables[_VARIABLE_LC_MAX] = {};
LocaleVariable j;
@@ -182,7 +176,7 @@ static int show_status(sd_bus *bus, char **args, unsigned n) {
goto fail;
}
- print_overriden_variables();
+ print_overridden_variables();
print_status_info(&info);
fail:
diff --git a/src/locale/localed.c b/src/locale/localed.c
index d1c90d613a..0e59350e98 100644
--- a/src/locale/localed.c
+++ b/src/locale/localed.c
@@ -33,7 +33,6 @@
#include "env-util.h"
#include "fileio.h"
#include "fileio-label.h"
-#include "label.h"
#include "bus-util.h"
#include "bus-error.h"
#include "bus-message.h"
@@ -228,7 +227,7 @@ static int x11_read_data(Context *c) {
if (in_section && first_word(l, "Option")) {
_cleanup_strv_free_ char **a = NULL;
- r = strv_split_quoted(&a, l, false);
+ r = strv_split_quoted(&a, l, 0);
if (r < 0)
return r;
@@ -251,7 +250,7 @@ static int x11_read_data(Context *c) {
} else if (!in_section && first_word(l, "Section")) {
_cleanup_strv_free_ char **a = NULL;
- r = strv_split_quoted(&a, l, false);
+ r = strv_split_quoted(&a, l, 0);
if (r < 0)
return -ENOMEM;
@@ -540,7 +539,7 @@ static int read_next_mapping(const char* filename,
if (l[0] == 0 || l[0] == '#')
continue;
- r = strv_split_quoted(&b, l, false);
+ r = strv_split_quoted(&b, l, 0);
if (r < 0)
return r;
@@ -870,7 +869,7 @@ static int property_get_locale(
return sd_bus_message_append_strv(reply, l);
}
-static int method_set_locale(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
+static int method_set_locale(sd_bus_message *m, void *userdata, sd_bus_error *error) {
Context *c = userdata;
_cleanup_strv_free_ char **l = NULL;
char **i;
@@ -881,6 +880,9 @@ static int method_set_locale(sd_bus *bus, sd_bus_message *m, void *userdata, sd_
int p;
int r;
+ assert(m);
+ assert(c);
+
r = bus_message_read_strv_extend(m, &l);
if (r < 0)
return r;
@@ -949,7 +951,14 @@ static int method_set_locale(sd_bus *bus, sd_bus_message *m, void *userdata, sd_
if (modified) {
_cleanup_strv_free_ char **settings = NULL;
- r = bus_verify_polkit_async(m, CAP_SYS_ADMIN, "org.freedesktop.locale1.set-locale", interactive, &c->polkit_registry, error);
+ r = bus_verify_polkit_async(
+ m,
+ CAP_SYS_ADMIN,
+ "org.freedesktop.locale1.set-locale",
+ interactive,
+ UID_INVALID,
+ &c->polkit_registry,
+ error);
if (r < 0)
return r;
if (r == 0)
@@ -983,7 +992,7 @@ static int method_set_locale(sd_bus *bus, sd_bus_message *m, void *userdata, sd_
return sd_bus_error_set_errnof(error, r, "Failed to set locale: %s", strerror(-r));
}
- locale_update_system_manager(c, bus);
+ locale_update_system_manager(c, sd_bus_message_get_bus(m));
if (settings) {
_cleanup_free_ char *line;
@@ -993,7 +1002,8 @@ static int method_set_locale(sd_bus *bus, sd_bus_message *m, void *userdata, sd_
} else
log_info("Changed locale to unset.");
- sd_bus_emit_properties_changed(bus,
+ (void) sd_bus_emit_properties_changed(
+ sd_bus_message_get_bus(m),
"/org/freedesktop/locale1",
"org.freedesktop.locale1",
"Locale", NULL);
@@ -1004,12 +1014,15 @@ static int method_set_locale(sd_bus *bus, sd_bus_message *m, void *userdata, sd_
return sd_bus_reply_method_return(m, NULL);
}
-static int method_set_vc_keyboard(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
+static int method_set_vc_keyboard(sd_bus_message *m, void *userdata, sd_bus_error *error) {
Context *c = userdata;
const char *keymap, *keymap_toggle;
int convert, interactive;
int r;
+ assert(m);
+ assert(c);
+
r = sd_bus_message_read(m, "ssbb", &keymap, &keymap_toggle, &convert, &interactive);
if (r < 0)
return r;
@@ -1027,7 +1040,14 @@ static int method_set_vc_keyboard(sd_bus *bus, sd_bus_message *m, void *userdata
(keymap_toggle && (!filename_is_valid(keymap_toggle) || !string_is_safe(keymap_toggle))))
return sd_bus_error_set_errnof(error, -EINVAL, "Received invalid keymap data");
- r = bus_verify_polkit_async(m, CAP_SYS_ADMIN, "org.freedesktop.locale1.set-keyboard", interactive, &c->polkit_registry, error);
+ r = bus_verify_polkit_async(
+ m,
+ CAP_SYS_ADMIN,
+ "org.freedesktop.locale1.set-keyboard",
+ interactive,
+ UID_INVALID,
+ &c->polkit_registry,
+ error);
if (r < 0)
return r;
if (r == 0)
@@ -1046,17 +1066,18 @@ static int method_set_vc_keyboard(sd_bus *bus, sd_bus_message *m, void *userdata
log_info("Changed virtual console keymap to '%s' toggle '%s'",
strempty(c->vc_keymap), strempty(c->vc_keymap_toggle));
- r = vconsole_reload(bus);
+ r = vconsole_reload(sd_bus_message_get_bus(m));
if (r < 0)
log_error_errno(r, "Failed to request keymap reload: %m");
- sd_bus_emit_properties_changed(bus,
+ (void) sd_bus_emit_properties_changed(
+ sd_bus_message_get_bus(m),
"/org/freedesktop/locale1",
"org.freedesktop.locale1",
"VConsoleKeymap", "VConsoleKeymapToggle", NULL);
if (convert) {
- r = vconsole_convert_to_x11(c, bus);
+ r = vconsole_convert_to_x11(c, sd_bus_message_get_bus(m));
if (r < 0)
log_error_errno(r, "Failed to convert keymap data: %m");
}
@@ -1113,12 +1134,15 @@ static int verify_xkb_rmlvo(const char *model, const char *layout, const char *v
}
#endif
-static int method_set_x11_keyboard(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
+static int method_set_x11_keyboard(sd_bus_message *m, void *userdata, sd_bus_error *error) {
Context *c = userdata;
const char *layout, *model, *variant, *options;
int convert, interactive;
int r;
+ assert(m);
+ assert(c);
+
r = sd_bus_message_read(m, "ssssbb", &layout, &model, &variant, &options, &convert, &interactive);
if (r < 0)
return r;
@@ -1146,7 +1170,14 @@ static int method_set_x11_keyboard(sd_bus *bus, sd_bus_message *m, void *userdat
(options && !string_is_safe(options)))
return sd_bus_error_set_errnof(error, -EINVAL, "Received invalid keyboard data");
- r = bus_verify_polkit_async(m, CAP_SYS_ADMIN, "org.freedesktop.locale1.set-keyboard", interactive, &c->polkit_registry, error);
+ r = bus_verify_polkit_async(
+ m,
+ CAP_SYS_ADMIN,
+ "org.freedesktop.locale1.set-keyboard",
+ interactive,
+ UID_INVALID,
+ &c->polkit_registry,
+ error);
if (r < 0)
return r;
if (r == 0)
@@ -1177,13 +1208,14 @@ static int method_set_x11_keyboard(sd_bus *bus, sd_bus_message *m, void *userdat
strempty(c->x11_variant),
strempty(c->x11_options));
- sd_bus_emit_properties_changed(bus,
+ (void) sd_bus_emit_properties_changed(
+ sd_bus_message_get_bus(m),
"/org/freedesktop/locale1",
"org.freedesktop.locale1",
"X11Layout", "X11Model", "X11Variant", "X11Options", NULL);
if (convert) {
- r = x11_convert_to_vconsole(c, bus);
+ r = x11_convert_to_vconsole(c, sd_bus_message_get_bus(m));
if (r < 0)
log_error_errno(r, "Failed to convert keymap data: %m");
}
diff --git a/src/login/inhibit.c b/src/login/inhibit.c
index 44bda34aff..57cfb5d0b5 100644
--- a/src/login/inhibit.c
+++ b/src/login/inhibit.c
@@ -20,7 +20,6 @@
***/
#include <getopt.h>
-#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
@@ -32,6 +31,8 @@
#include "util.h"
#include "build.h"
#include "strv.h"
+#include "formats-util.h"
+#include "process-util.h"
static const char* arg_what = "idle:sleep:shutdown";
static const char* arg_who = NULL;
@@ -260,7 +261,7 @@ int main(int argc, char *argv[]) {
fd = inhibit(bus, &error);
if (fd < 0) {
- log_error("Failed to inhibit: %s", bus_error_message(&error, -r));
+ log_error("Failed to inhibit: %s", bus_error_message(&error, fd));
return EXIT_FAILURE;
}
diff --git a/src/login/loginctl.c b/src/login/loginctl.c
index b0eede9a34..02d240c704 100644
--- a/src/login/loginctl.c
+++ b/src/login/loginctl.c
@@ -23,7 +23,6 @@
#include <errno.h>
#include <string.h>
#include <getopt.h>
-#include <pwd.h>
#include <locale.h>
#include "sd-bus.h"
@@ -42,6 +41,9 @@
#include "cgroup-util.h"
#include "spawn-polkit-agent.h"
#include "verbs.h"
+#include "process-util.h"
+#include "terminal-util.h"
+#include "signal-util.h"
static char **arg_property = NULL;
static bool arg_all = false;
@@ -869,7 +871,7 @@ static int activate(int argc, char *argv[], void *userdata) {
for (i = 1; i < argc; i++) {
- r = sd_bus_call_method (
+ r = sd_bus_call_method(
bus,
"org.freedesktop.login1",
"/org/freedesktop/login1",
@@ -904,7 +906,7 @@ static int kill_session(int argc, char *argv[], void *userdata) {
for (i = 1; i < argc; i++) {
- r = sd_bus_call_method (
+ r = sd_bus_call_method(
bus,
"org.freedesktop.login1",
"/org/freedesktop/login1",
@@ -954,7 +956,7 @@ static int enable_linger(int argc, char *argv[], void *userdata) {
return log_error_errno(r, "Failed to look up user %s: %m", argv[i]);
}
- r = sd_bus_call_method (
+ r = sd_bus_call_method(
bus,
"org.freedesktop.login1",
"/org/freedesktop/login1",
@@ -988,7 +990,7 @@ static int terminate_user(int argc, char *argv[], void *userdata) {
if (r < 0)
return log_error_errno(r, "Failed to look up user %s: %m", argv[i]);
- r = sd_bus_call_method (
+ r = sd_bus_call_method(
bus,
"org.freedesktop.login1",
"/org/freedesktop/login1",
@@ -1025,7 +1027,7 @@ static int kill_user(int argc, char *argv[], void *userdata) {
if (r < 0)
return log_error_errno(r, "Failed to look up user %s: %m", argv[i]);
- r = sd_bus_call_method (
+ r = sd_bus_call_method(
bus,
"org.freedesktop.login1",
"/org/freedesktop/login1",
@@ -1054,7 +1056,7 @@ static int attach(int argc, char *argv[], void *userdata) {
for (i = 2; i < argc; i++) {
- r = sd_bus_call_method (
+ r = sd_bus_call_method(
bus,
"org.freedesktop.login1",
"/org/freedesktop/login1",
@@ -1082,7 +1084,7 @@ static int flush_devices(int argc, char *argv[], void *userdata) {
polkit_agent_open_if_enabled();
- r = sd_bus_call_method (
+ r = sd_bus_call_method(
bus,
"org.freedesktop.login1",
"/org/freedesktop/login1",
@@ -1375,6 +1377,8 @@ int main(int argc, char *argv[]) {
goto finish;
}
+ sd_bus_set_allow_interactive_authorization(bus, arg_ask_password);
+
r = loginctl_main(argc, argv, bus);
finish:
diff --git a/src/login/logind-acl.c b/src/login/logind-acl.c
index 5856f9079d..466225d69c 100644
--- a/src/login/logind-acl.c
+++ b/src/login/logind-acl.c
@@ -19,11 +19,11 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <assert.h>
#include <errno.h>
#include <string.h>
#include "util.h"
+#include "formats-util.h"
#include "acl-util.h"
#include "set.h"
#include "logind-acl.h"
@@ -254,8 +254,7 @@ int devnode_acl_all(struct udev *udev,
FOREACH_DIRENT(dent, dir, return -errno) {
_cleanup_free_ char *unescaped_devname = NULL;
- unescaped_devname = cunescape(dent->d_name);
- if (!unescaped_devname)
+ if (cunescape(dent->d_name, UNESCAPE_RELAX, &unescaped_devname) < 0)
return -ENOMEM;
n = strappend("/dev/", unescaped_devname);
diff --git a/src/login/logind-action.c b/src/login/logind-action.c
index 0844df20a9..f635fb1b63 100644
--- a/src/login/logind-action.c
+++ b/src/login/logind-action.c
@@ -21,13 +21,15 @@
#include <unistd.h>
-#include "sd-messages.h"
#include "conf-parser.h"
#include "special.h"
#include "sleep-config.h"
#include "bus-util.h"
#include "bus-error.h"
#include "logind-action.h"
+#include "formats-util.h"
+#include "process-util.h"
+#include "terminal-util.h"
int manager_handle_action(
Manager *m,
@@ -113,7 +115,7 @@ int manager_handle_action(
if (!supported) {
log_warning("Requested operation not supported, ignoring.");
- return -ENOTSUP;
+ return -EOPNOTSUPP;
}
if (m->action_what) {
diff --git a/src/login/logind-action.h b/src/login/logind-action.h
index e9b424b5f6..ff98065371 100644
--- a/src/login/logind-action.h
+++ b/src/login/logind-action.h
@@ -36,7 +36,6 @@ typedef enum HandleAction {
} HandleAction;
#include "logind.h"
-#include "logind-inhibit.h"
int manager_handle_action(
Manager *m,
diff --git a/src/login/logind-button.c b/src/login/logind-button.c
index d7211e66ce..610adc513e 100644
--- a/src/login/logind-button.c
+++ b/src/login/logind-button.c
@@ -19,7 +19,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <assert.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
@@ -28,9 +27,7 @@
#include <linux/input.h>
#include "sd-messages.h"
-#include "conf-parser.h"
#include "util.h"
-#include "special.h"
#include "logind-button.h"
Button* button_new(Manager *m, const char *name) {
diff --git a/src/login/logind-button.h b/src/login/logind-button.h
index 72a612e914..80d93c7e6b 100644
--- a/src/login/logind-button.h
+++ b/src/login/logind-button.h
@@ -23,8 +23,6 @@
typedef struct Button Button;
-#include "list.h"
-#include "util.h"
#include "logind.h"
struct Button {
diff --git a/src/login/logind-core.c b/src/login/logind-core.c
index a6ff5add95..440c32aa2c 100644
--- a/src/login/logind-core.c
+++ b/src/login/logind-core.c
@@ -20,20 +20,18 @@
***/
#include <sys/types.h>
-#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <pwd.h>
-#include <unistd.h>
#include <linux/vt.h>
#include "strv.h"
#include "cgroup-util.h"
-#include "audit.h"
#include "bus-util.h"
#include "bus-error.h"
#include "udev-util.h"
#include "logind.h"
+#include "terminal-util.h"
int manager_add_device(Manager *m, const char *sysfs, bool master, Device **_device) {
Device *d;
diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c
index 8b0bafd49e..10a9df0961 100644
--- a/src/login/logind-dbus.c
+++ b/src/login/logind-dbus.c
@@ -24,7 +24,6 @@
#include <unistd.h>
#include <pwd.h>
-#include "sd-id128.h"
#include "sd-messages.h"
#include "strv.h"
#include "mkdir.h"
@@ -32,17 +31,19 @@
#include "special.h"
#include "sleep-config.h"
#include "fileio-label.h"
-#include "label.h"
-#include "utf8.h"
#include "unit-name.h"
-#include "virt.h"
#include "audit.h"
#include "bus-util.h"
#include "bus-error.h"
#include "bus-common-errors.h"
#include "udev-util.h"
#include "selinux-util.h"
+#include "efivars.h"
#include "logind.h"
+#include "formats-util.h"
+#include "process-util.h"
+#include "terminal-util.h"
+#include "utmp-wtmp.h"
int manager_get_session_from_creds(Manager *m, sd_bus_message *message, const char *name, sd_bus_error *error, Session **ret) {
_cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
@@ -213,16 +214,42 @@ static int property_get_preparing(
return sd_bus_message_append(reply, "b", b);
}
+static int property_get_scheduled_shutdown(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+
+ Manager *m = userdata;
+ int r;
+
+ assert(bus);
+ assert(reply);
+ assert(m);
+
+ r = sd_bus_message_open_container(reply, 'r', "st");
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_append(reply, "st", m->scheduled_shutdown_type, m->scheduled_shutdown_timeout);
+ if (r < 0)
+ return r;
+
+ return sd_bus_message_close_container(reply);
+}
+
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_handle_action, handle_action, HandleAction);
-static int method_get_session(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_get_session(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_free_ char *p = NULL;
Manager *m = userdata;
const char *name;
Session *session;
int r;
- assert(bus);
assert(message);
assert(m);
@@ -241,14 +268,13 @@ static int method_get_session(sd_bus *bus, sd_bus_message *message, void *userda
return sd_bus_reply_method_return(message, "o", p);
}
-static int method_get_session_by_pid(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_get_session_by_pid(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_free_ char *p = NULL;
Session *session = NULL;
Manager *m = userdata;
pid_t pid;
int r;
- assert(bus);
assert(message);
assert(m);
@@ -278,14 +304,13 @@ static int method_get_session_by_pid(sd_bus *bus, sd_bus_message *message, void
return sd_bus_reply_method_return(message, "o", p);
}
-static int method_get_user(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_get_user(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_free_ char *p = NULL;
Manager *m = userdata;
uint32_t uid;
User *user;
int r;
- assert(bus);
assert(message);
assert(m);
@@ -304,14 +329,13 @@ static int method_get_user(sd_bus *bus, sd_bus_message *message, void *userdata,
return sd_bus_reply_method_return(message, "o", p);
}
-static int method_get_user_by_pid(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_get_user_by_pid(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_free_ char *p = NULL;
Manager *m = userdata;
User *user = NULL;
pid_t pid;
int r;
- assert(bus);
assert(message);
assert(m);
@@ -340,14 +364,13 @@ static int method_get_user_by_pid(sd_bus *bus, sd_bus_message *message, void *us
return sd_bus_reply_method_return(message, "o", p);
}
-static int method_get_seat(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_get_seat(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_free_ char *p = NULL;
Manager *m = userdata;
const char *name;
Seat *seat;
int r;
- assert(bus);
assert(message);
assert(m);
@@ -366,14 +389,13 @@ static int method_get_seat(sd_bus *bus, sd_bus_message *message, void *userdata,
return sd_bus_reply_method_return(message, "o", p);
}
-static int method_list_sessions(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_list_sessions(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
Manager *m = userdata;
Session *session;
Iterator i;
int r;
- assert(bus);
assert(message);
assert(m);
@@ -406,17 +428,16 @@ static int method_list_sessions(sd_bus *bus, sd_bus_message *message, void *user
if (r < 0)
return r;
- return sd_bus_send(bus, reply, NULL);
+ return sd_bus_send(NULL, reply, NULL);
}
-static int method_list_users(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_list_users(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
Manager *m = userdata;
User *user;
Iterator i;
int r;
- assert(bus);
assert(message);
assert(m);
@@ -447,17 +468,16 @@ static int method_list_users(sd_bus *bus, sd_bus_message *message, void *userdat
if (r < 0)
return r;
- return sd_bus_send(bus, reply, NULL);
+ return sd_bus_send(NULL, reply, NULL);
}
-static int method_list_seats(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_list_seats(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
Manager *m = userdata;
Seat *seat;
Iterator i;
int r;
- assert(bus);
assert(message);
assert(m);
@@ -485,16 +505,19 @@ static int method_list_seats(sd_bus *bus, sd_bus_message *message, void *userdat
if (r < 0)
return r;
- return sd_bus_send(bus, reply, NULL);
+ return sd_bus_send(NULL, reply, NULL);
}
-static int method_list_inhibitors(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_list_inhibitors(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
Manager *m = userdata;
Inhibitor *inhibitor;
Iterator i;
int r;
+ assert(message);
+ assert(m);
+
r = sd_bus_message_new_method_return(message, &reply);
if (r < 0)
return r;
@@ -520,10 +543,10 @@ static int method_list_inhibitors(sd_bus *bus, sd_bus_message *message, void *us
if (r < 0)
return r;
- return sd_bus_send(bus, reply, NULL);
+ return sd_bus_send(NULL, reply, NULL);
}
-static int method_create_session(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_create_session(sd_bus_message *message, void *userdata, sd_bus_error *error) {
const char *service, *type, *class, *cseat, *tty, *display, *remote_user, *remote_host, *desktop;
uint32_t uid, leader, audit_id = 0;
_cleanup_free_ char *id = NULL;
@@ -537,7 +560,6 @@ static int method_create_session(sd_bus *bus, sd_bus_message *message, void *use
SessionClass c;
int r;
- assert(bus);
assert(message);
assert(m);
@@ -813,13 +835,12 @@ fail:
return r;
}
-static int method_release_session(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_release_session(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Manager *m = userdata;
Session *session;
const char *name;
int r;
- assert(bus);
assert(message);
assert(m);
@@ -831,18 +852,19 @@ static int method_release_session(sd_bus *bus, sd_bus_message *message, void *us
if (r < 0)
return r;
- session_release(session);
+ r = session_release(session);
+ if (r < 0)
+ return r;
return sd_bus_reply_method_return(message, NULL);
}
-static int method_activate_session(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_activate_session(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Manager *m = userdata;
Session *session;
const char *name;
int r;
- assert(bus);
assert(message);
assert(m);
@@ -854,21 +876,16 @@ static int method_activate_session(sd_bus *bus, sd_bus_message *message, void *u
if (r < 0)
return r;
- r = session_activate(session);
- if (r < 0)
- return r;
-
- return sd_bus_reply_method_return(message, NULL);
+ return bus_session_method_activate(message, session, error);
}
-static int method_activate_session_on_seat(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_activate_session_on_seat(sd_bus_message *message, void *userdata, sd_bus_error *error) {
const char *session_name, *seat_name;
Manager *m = userdata;
Session *session;
Seat *seat;
int r;
- assert(bus);
assert(message);
assert(m);
@@ -897,13 +914,12 @@ static int method_activate_session_on_seat(sd_bus *bus, sd_bus_message *message,
return sd_bus_reply_method_return(message, NULL);
}
-static int method_lock_session(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_lock_session(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Manager *m = userdata;
Session *session;
const char *name;
int r;
- assert(bus);
assert(message);
assert(m);
@@ -915,21 +931,29 @@ static int method_lock_session(sd_bus *bus, sd_bus_message *message, void *userd
if (r < 0)
return r;
- r = session_send_lock(session, streq(sd_bus_message_get_member(message), "LockSession"));
- if (r < 0)
- return r;
-
- return sd_bus_reply_method_return(message, NULL);
+ return bus_session_method_lock(message, session, error);
}
-static int method_lock_sessions(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_lock_sessions(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Manager *m = userdata;
int r;
- assert(bus);
assert(message);
assert(m);
+ r = bus_verify_polkit_async(
+ message,
+ CAP_SYS_ADMIN,
+ "org.freedesktop.login1.lock-sessions",
+ false,
+ UID_INVALID,
+ &m->polkit_registry,
+ error);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return 1; /* Will call us back */
+
r = session_send_lock_all(m, streq(sd_bus_message_get_member(message), "LockSessions"));
if (r < 0)
return r;
@@ -937,80 +961,52 @@ static int method_lock_sessions(sd_bus *bus, sd_bus_message *message, void *user
return sd_bus_reply_method_return(message, NULL);
}
-static int method_kill_session(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
- const char *name, *swho;
+static int method_kill_session(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ const char *name;
Manager *m = userdata;
Session *session;
- int32_t signo;
- KillWho who;
int r;
- assert(bus);
assert(message);
assert(m);
- r = sd_bus_message_read(message, "ssi", &name, &swho, &signo);
+ r = sd_bus_message_read(message, "s", &name);
if (r < 0)
return r;
- if (isempty(swho))
- who = KILL_ALL;
- else {
- who = kill_who_from_string(swho);
- if (who < 0)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid kill parameter '%s'", swho);
- }
-
- if (signo <= 0 || signo >= _NSIG)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid signal %i", signo);
-
r = manager_get_session_from_creds(m, message, name, error, &session);
if (r < 0)
return r;
- r = session_kill(session, who, signo);
- if (r < 0)
- return r;
-
- return sd_bus_reply_method_return(message, NULL);
+ return bus_session_method_kill(message, session, error);
}
-static int method_kill_user(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_kill_user(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Manager *m = userdata;
uint32_t uid;
- int32_t signo;
User *user;
int r;
- assert(bus);
assert(message);
assert(m);
- r = sd_bus_message_read(message, "ui", &uid, &signo);
+ r = sd_bus_message_read(message, "u", &uid);
if (r < 0)
return r;
- if (signo <= 0 || signo >= _NSIG)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid signal %i", signo);
-
r = manager_get_user_from_creds(m, message, uid, error, &user);
if (r < 0)
return r;
- r = user_kill(user, signo);
- if (r < 0)
- return r;
-
- return sd_bus_reply_method_return(message, NULL);
+ return bus_user_method_kill(message, user, error);
}
-static int method_terminate_session(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_terminate_session(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Manager *m = userdata;
const char *name;
Session *session;
int r;
- assert(bus);
assert(message);
assert(m);
@@ -1022,20 +1018,15 @@ static int method_terminate_session(sd_bus *bus, sd_bus_message *message, void *
if (r < 0)
return r;
- r = session_stop(session, true);
- if (r < 0)
- return r;
-
- return sd_bus_reply_method_return(message, NULL);
+ return bus_session_method_terminate(message, session, error);
}
-static int method_terminate_user(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_terminate_user(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Manager *m = userdata;
uint32_t uid;
User *user;
int r;
- assert(bus);
assert(message);
assert(m);
@@ -1047,20 +1038,15 @@ static int method_terminate_user(sd_bus *bus, sd_bus_message *message, void *use
if (r < 0)
return r;
- r = user_stop(user, true);
- if (r < 0)
- return r;
-
- return sd_bus_reply_method_return(message, NULL);
+ return bus_user_method_terminate(message, user, error);
}
-static int method_terminate_seat(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_terminate_seat(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Manager *m = userdata;
const char *name;
Seat *seat;
int r;
- assert(bus);
assert(message);
assert(m);
@@ -1072,14 +1058,10 @@ static int method_terminate_seat(sd_bus *bus, sd_bus_message *message, void *use
if (r < 0)
return r;
- r = seat_stop_sessions(seat, true);
- if (r < 0)
- return r;
-
- return sd_bus_reply_method_return(message, NULL);
+ return bus_seat_method_terminate(message, seat, error);
}
-static int method_set_user_linger(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_set_user_linger(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_free_ char *cc = NULL;
Manager *m = userdata;
int b, r;
@@ -1088,7 +1070,6 @@ static int method_set_user_linger(sd_bus *bus, sd_bus_message *message, void *us
uint32_t uid;
int interactive;
- assert(bus);
assert(message);
assert(m);
@@ -1119,6 +1100,7 @@ static int method_set_user_linger(sd_bus *bus, sd_bus_message *message, void *us
CAP_SYS_ADMIN,
"org.freedesktop.login1.set-user-linger",
interactive,
+ UID_INVALID,
&m->polkit_registry,
error);
if (r < 0)
@@ -1267,12 +1249,11 @@ static int flush_devices(Manager *m) {
return trigger_device(m, NULL);
}
-static int method_attach_device(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_attach_device(sd_bus_message *message, void *userdata, sd_bus_error *error) {
const char *sysfs, *seat;
Manager *m = userdata;
int interactive, r;
- assert(bus);
assert(message);
assert(m);
@@ -1291,6 +1272,7 @@ static int method_attach_device(sd_bus *bus, sd_bus_message *message, void *user
CAP_SYS_ADMIN,
"org.freedesktop.login1.attach-device",
interactive,
+ UID_INVALID,
&m->polkit_registry,
error);
if (r < 0)
@@ -1305,11 +1287,10 @@ static int method_attach_device(sd_bus *bus, sd_bus_message *message, void *user
return sd_bus_reply_method_return(message, NULL);
}
-static int method_flush_devices(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_flush_devices(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Manager *m = userdata;
int interactive, r;
- assert(bus);
assert(message);
assert(m);
@@ -1322,6 +1303,7 @@ static int method_flush_devices(sd_bus *bus, sd_bus_message *message, void *user
CAP_SYS_ADMIN,
"org.freedesktop.login1.flush-devices",
interactive,
+ UID_INVALID,
&m->polkit_registry,
error);
if (r < 0)
@@ -1481,7 +1463,46 @@ static int execute_shutdown_or_sleep(
m->action_what = w;
/* Make sure the lid switch is ignored for a while */
- manager_set_lid_switch_ignore(m, now(CLOCK_MONOTONIC) + IGNORE_LID_SWITCH_SUSPEND_USEC);
+ manager_set_lid_switch_ignore(m, now(CLOCK_MONOTONIC) + m->holdoff_timeout_usec);
+
+ return 0;
+}
+
+static int manager_inhibit_timeout_handler(
+ sd_event_source *s,
+ uint64_t usec,
+ void *userdata) {
+
+ _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ Inhibitor *offending = NULL;
+ Manager *manager = userdata;
+ int r;
+
+ assert(manager);
+ assert(manager->inhibit_timeout_source == s);
+
+ if (manager->action_what == 0 || manager->action_job)
+ return 0;
+
+ if (manager_is_inhibited(manager, manager->action_what, INHIBIT_DELAY, NULL, false, false, 0, &offending)) {
+ _cleanup_free_ char *comm = NULL, *u = NULL;
+
+ (void) get_process_comm(offending->pid, &comm);
+ u = uid_to_name(offending->uid);
+
+ log_notice("Delay lock is active (UID "UID_FMT"/%s, PID "PID_FMT"/%s) but inhibitor timeout is reached.",
+ offending->uid, strna(u),
+ offending->pid, strna(comm));
+ }
+
+ /* Actually do the operation */
+ r = execute_shutdown_or_sleep(manager, manager->action_what, manager->action_unit, &error);
+ if (r < 0) {
+ log_warning("Failed to send delayed message: %s", bus_error_message(&error, r));
+
+ manager->action_unit = NULL;
+ manager->action_what = 0;
+ }
return 0;
}
@@ -1491,12 +1512,31 @@ static int delay_shutdown_or_sleep(
InhibitWhat w,
const char *unit_name) {
+ int r;
+ usec_t timeout_val;
+
assert(m);
assert(w >= 0);
assert(w < _INHIBIT_WHAT_MAX);
assert(unit_name);
- m->action_timestamp = now(CLOCK_MONOTONIC);
+ timeout_val = now(CLOCK_MONOTONIC) + m->inhibit_delay_max;
+
+ if (m->inhibit_timeout_source) {
+ r = sd_event_source_set_time(m->inhibit_timeout_source, timeout_val);
+ if (r < 0)
+ return log_error_errno(r, "sd_event_source_set_time() failed: %m");
+
+ r = sd_event_source_set_enabled(m->inhibit_timeout_source, SD_EVENT_ONESHOT);
+ if (r < 0)
+ return log_error_errno(r, "sd_event_source_set_enabled() failed: %m");
+ } else {
+ r = sd_event_add_time(m->event, &m->inhibit_timeout_source, CLOCK_MONOTONIC,
+ timeout_val, 0, manager_inhibit_timeout_handler, m);
+ if (r < 0)
+ return r;
+ }
+
m->action_unit = unit_name;
m->action_what = w;
@@ -1559,49 +1599,25 @@ int bus_manager_shutdown_or_sleep_now_or_later(
return r;
}
-static int method_do_shutdown_or_sleep(
+static int verify_shutdown_creds(
Manager *m,
sd_bus_message *message,
- const char *unit_name,
InhibitWhat w,
+ bool interactive,
const char *action,
const char *action_multiple_sessions,
const char *action_ignore_inhibit,
- const char *sleep_verb,
- sd_bus_message_handler_t method,
sd_bus_error *error) {
_cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
bool multiple_sessions, blocked;
- int interactive, r;
uid_t uid;
+ int r;
assert(m);
assert(message);
- assert(unit_name);
assert(w >= 0);
assert(w <= _INHIBIT_WHAT_MAX);
- assert(action);
- assert(action_multiple_sessions);
- assert(action_ignore_inhibit);
- assert(method);
-
- r = sd_bus_message_read(message, "b", &interactive);
- if (r < 0)
- return r;
-
- /* Don't allow multiple jobs being executed at the same time */
- if (m->action_what)
- return sd_bus_error_setf(error, BUS_ERROR_OPERATION_IN_PROGRESS, "There's already a shutdown or sleep operation in progress");
-
- if (sleep_verb) {
- r = can_sleep(sleep_verb);
- if (r < 0)
- return r;
-
- if (r == 0)
- return sd_bus_error_setf(error, BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED, "Sleep verb not supported");
- }
r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_EUID, &creds);
if (r < 0)
@@ -1618,30 +1634,74 @@ static int method_do_shutdown_or_sleep(
multiple_sessions = r > 0;
blocked = manager_is_inhibited(m, w, INHIBIT_BLOCK, NULL, false, true, uid, NULL);
- if (multiple_sessions) {
- r = bus_verify_polkit_async(message, CAP_SYS_BOOT, action_multiple_sessions, interactive, &m->polkit_registry, error);
+ if (multiple_sessions && action_multiple_sessions) {
+ r = bus_verify_polkit_async(message, CAP_SYS_BOOT, action_multiple_sessions, interactive, UID_INVALID, &m->polkit_registry, error);
if (r < 0)
return r;
if (r == 0)
return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
}
- if (blocked) {
- r = bus_verify_polkit_async(message, CAP_SYS_BOOT, action_ignore_inhibit, interactive, &m->polkit_registry, error);
+ if (blocked && action_ignore_inhibit) {
+ r = bus_verify_polkit_async(message, CAP_SYS_BOOT, action_ignore_inhibit, interactive, UID_INVALID, &m->polkit_registry, error);
if (r < 0)
return r;
if (r == 0)
return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
}
- if (!multiple_sessions && !blocked) {
- r = bus_verify_polkit_async(message, CAP_SYS_BOOT, action, interactive, &m->polkit_registry, error);
+ if (!multiple_sessions && !blocked && action) {
+ r = bus_verify_polkit_async(message, CAP_SYS_BOOT, action, interactive, UID_INVALID, &m->polkit_registry, error);
if (r < 0)
return r;
if (r == 0)
return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
}
+ return 0;
+}
+
+static int method_do_shutdown_or_sleep(
+ Manager *m,
+ sd_bus_message *message,
+ const char *unit_name,
+ InhibitWhat w,
+ const char *action,
+ const char *action_multiple_sessions,
+ const char *action_ignore_inhibit,
+ const char *sleep_verb,
+ sd_bus_error *error) {
+
+ int interactive, r;
+
+ assert(m);
+ assert(message);
+ assert(unit_name);
+ assert(w >= 0);
+ assert(w <= _INHIBIT_WHAT_MAX);
+
+ r = sd_bus_message_read(message, "b", &interactive);
+ if (r < 0)
+ return r;
+
+ /* Don't allow multiple jobs being executed at the same time */
+ if (m->action_what)
+ return sd_bus_error_setf(error, BUS_ERROR_OPERATION_IN_PROGRESS, "There's already a shutdown or sleep operation in progress");
+
+ if (sleep_verb) {
+ r = can_sleep(sleep_verb);
+ if (r < 0)
+ return r;
+
+ if (r == 0)
+ return sd_bus_error_setf(error, BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED, "Sleep verb not supported");
+ }
+
+ r = verify_shutdown_creds(m, message, w, interactive, action, action_multiple_sessions,
+ action_ignore_inhibit, error);
+ if (r != 0)
+ return r;
+
r = bus_manager_shutdown_or_sleep_now_or_later(m, unit_name, w, error);
if (r < 0)
return r;
@@ -1649,7 +1709,7 @@ static int method_do_shutdown_or_sleep(
return sd_bus_reply_method_return(message, NULL);
}
-static int method_poweroff(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_poweroff(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Manager *m = userdata;
return method_do_shutdown_or_sleep(
@@ -1660,11 +1720,10 @@ static int method_poweroff(sd_bus *bus, sd_bus_message *message, void *userdata,
"org.freedesktop.login1.power-off-multiple-sessions",
"org.freedesktop.login1.power-off-ignore-inhibit",
NULL,
- method_poweroff,
error);
}
-static int method_reboot(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_reboot(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Manager *m = userdata;
return method_do_shutdown_or_sleep(
@@ -1675,11 +1734,10 @@ static int method_reboot(sd_bus *bus, sd_bus_message *message, void *userdata, s
"org.freedesktop.login1.reboot-multiple-sessions",
"org.freedesktop.login1.reboot-ignore-inhibit",
NULL,
- method_reboot,
error);
}
-static int method_suspend(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_suspend(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Manager *m = userdata;
return method_do_shutdown_or_sleep(
@@ -1690,11 +1748,247 @@ static int method_suspend(sd_bus *bus, sd_bus_message *message, void *userdata,
"org.freedesktop.login1.suspend-multiple-sessions",
"org.freedesktop.login1.suspend-ignore-inhibit",
"suspend",
- method_suspend,
error);
}
-static int method_hibernate(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int nologin_timeout_handler(
+ sd_event_source *s,
+ uint64_t usec,
+ void *userdata) {
+
+ Manager *m = userdata;
+ int r;
+
+ log_info("Creating /run/nologin, blocking further logins...");
+
+ r = write_string_file_atomic("/run/nologin", "System is going down.");
+ if (r < 0)
+ log_error_errno(r, "Failed to create /run/nologin: %m");
+ else
+ m->unlink_nologin = true;
+
+ return 0;
+}
+
+static int update_schedule_file(Manager *m) {
+
+ int r;
+ _cleanup_fclose_ FILE *f = NULL;
+ _cleanup_free_ char *t = NULL, *temp_path = NULL;
+
+ assert(m);
+
+ r = mkdir_safe_label("/run/systemd/shutdown", 0755, 0, 0);
+ if (r < 0)
+ return log_error_errno(r, "Failed to create shutdown subdirectory: %m");
+
+ t = cescape(m->wall_message);
+ if (!t)
+ return log_oom();
+
+ r = fopen_temporary("/run/systemd/shutdown/scheduled", &f, &temp_path);
+ if (r < 0)
+ return log_error_errno(r, "Failed to save information about scheduled shutdowns: %m");
+
+ (void) fchmod(fileno(f), 0644);
+
+ fprintf(f,
+ "USEC="USEC_FMT"\n"
+ "WARN_WALL=%i\n"
+ "MODE=%s\n",
+ m->scheduled_shutdown_timeout,
+ m->enable_wall_messages,
+ m->scheduled_shutdown_type);
+
+ if (!isempty(m->wall_message))
+ fprintf(f, "WALL_MESSAGE=%s\n", t);
+
+ (void) fflush_and_check(f);
+
+ if (ferror(f) || rename(temp_path, "/run/systemd/shutdown/scheduled") < 0) {
+ log_error_errno(errno, "Failed to write information about scheduled shutdowns: %m");
+ r = -errno;
+
+ (void) unlink(temp_path);
+ (void) unlink("/run/systemd/shutdown/scheduled");
+ }
+
+ return r;
+}
+
+static int manager_scheduled_shutdown_handler(
+ sd_event_source *s,
+ uint64_t usec,
+ void *userdata) {
+
+ _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ Manager *m = userdata;
+ const char *target;
+ int r;
+
+ assert(m);
+
+ if (isempty(m->scheduled_shutdown_type))
+ return 0;
+
+ if (streq(m->scheduled_shutdown_type, "halt"))
+ target = SPECIAL_HALT_TARGET;
+ else if (streq(m->scheduled_shutdown_type, "poweroff"))
+ target = SPECIAL_POWEROFF_TARGET;
+ else
+ target = SPECIAL_REBOOT_TARGET;
+
+ r = execute_shutdown_or_sleep(m, 0, target, &error);
+ if (r < 0)
+ return log_error_errno(r, "Unable to execute transition to %s: %m", target);
+
+ return 0;
+}
+
+static int method_schedule_shutdown(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ Manager *m = userdata;
+ _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
+ const char *action_multiple_sessions = NULL;
+ const char *action_ignore_inhibit = NULL;
+ const char *action = NULL;
+ uint64_t elapse;
+ char *type;
+ int r;
+
+ assert(m);
+ assert(message);
+
+ r = sd_bus_message_read(message, "st", &type, &elapse);
+ if (r < 0)
+ return r;
+
+ if (streq(type, "reboot")) {
+ action = "org.freedesktop.login1.reboot";
+ action_multiple_sessions = "org.freedesktop.login1.reboot-multiple-sessions";
+ action_ignore_inhibit = "org.freedesktop.login1.reboot-ignore-inhibit";
+ } else if (streq(type, "halt")) {
+ action = "org.freedesktop.login1.halt";
+ action_multiple_sessions = "org.freedesktop.login1.halt-multiple-sessions";
+ action_ignore_inhibit = "org.freedesktop.login1.halt-ignore-inhibit";
+ } else if (streq(type, "poweroff")) {
+ action = "org.freedesktop.login1.poweroff";
+ action_multiple_sessions = "org.freedesktop.login1.poweroff-multiple-sessions";
+ action_ignore_inhibit = "org.freedesktop.login1.poweroff-ignore-inhibit";
+ } else
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unsupported shutdown type");
+
+ r = verify_shutdown_creds(m, message, INHIBIT_SHUTDOWN, false,
+ action, action_multiple_sessions, action_ignore_inhibit, error);
+ if (r != 0)
+ return r;
+
+ if (m->scheduled_shutdown_timeout_source) {
+ r = sd_event_source_set_time(m->scheduled_shutdown_timeout_source, elapse);
+ if (r < 0)
+ return log_error_errno(r, "sd_event_source_set_time() failed: %m");
+
+ r = sd_event_source_set_enabled(m->scheduled_shutdown_timeout_source, SD_EVENT_ONESHOT);
+ if (r < 0)
+ return log_error_errno(r, "sd_event_source_set_enabled() failed: %m");
+ } else {
+ r = sd_event_add_time(m->event, &m->scheduled_shutdown_timeout_source,
+ CLOCK_REALTIME, elapse, 0, manager_scheduled_shutdown_handler, m);
+ if (r < 0)
+ return log_error_errno(r, "sd_event_add_time() failed: %m");
+ }
+
+ r = free_and_strdup(&m->scheduled_shutdown_type, type);
+ if (r < 0) {
+ m->scheduled_shutdown_timeout_source = sd_event_source_unref(m->scheduled_shutdown_timeout_source);
+ return log_oom();
+ }
+
+ if (m->nologin_timeout_source) {
+ r = sd_event_source_set_time(m->nologin_timeout_source, elapse);
+ if (r < 0)
+ return log_error_errno(r, "sd_event_source_set_time() failed: %m");
+
+ r = sd_event_source_set_enabled(m->nologin_timeout_source, SD_EVENT_ONESHOT);
+ if (r < 0)
+ return log_error_errno(r, "sd_event_source_set_enabled() failed: %m");
+ } else {
+ r = sd_event_add_time(m->event, &m->nologin_timeout_source,
+ CLOCK_REALTIME, elapse - 5 * USEC_PER_MINUTE, 0, nologin_timeout_handler, m);
+ if (r < 0)
+ return log_error_errno(r, "sd_event_add_time() failed: %m");
+ }
+
+ m->scheduled_shutdown_timeout = elapse;
+
+ r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_AUGMENT|SD_BUS_CREDS_TTY|SD_BUS_CREDS_UID, &creds);
+ if (r >= 0) {
+ const char *tty;
+
+ (void) sd_bus_creds_get_uid(creds, &m->scheduled_shutdown_uid);
+ (void) sd_bus_creds_get_tty(creds, &tty);
+
+ r = free_and_strdup(&m->scheduled_shutdown_tty, tty);
+ if (r < 0) {
+ m->scheduled_shutdown_timeout_source = sd_event_source_unref(m->scheduled_shutdown_timeout_source);
+ return log_oom();
+ }
+ }
+
+ r = manager_setup_wall_message_timer(m);
+ if (r < 0)
+ return r;
+
+ if (!isempty(type)) {
+ r = update_schedule_file(m);
+ if (r < 0)
+ return r;
+ } else
+ (void) unlink("/run/systemd/shutdown/scheduled");
+
+ return sd_bus_reply_method_return(message, NULL);
+}
+
+static int method_cancel_scheduled_shutdown(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ Manager *m = userdata;
+ bool cancelled;
+
+ assert(m);
+ assert(message);
+
+ cancelled = m->scheduled_shutdown_type != NULL;
+
+ m->scheduled_shutdown_timeout_source = sd_event_source_unref(m->scheduled_shutdown_timeout_source);
+ m->wall_message_timeout_source = sd_event_source_unref(m->wall_message_timeout_source);
+ m->nologin_timeout_source = sd_event_source_unref(m->nologin_timeout_source);
+ free(m->scheduled_shutdown_type);
+ m->scheduled_shutdown_type = NULL;
+ m->scheduled_shutdown_timeout = 0;
+
+ if (m->unlink_nologin) {
+ (void) unlink("/run/nologin");
+ m->unlink_nologin = false;
+ }
+
+ if (cancelled) {
+ _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
+ const char *tty = NULL;
+ uid_t uid = 0;
+ int r;
+
+ r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_AUGMENT|SD_BUS_CREDS_TTY|SD_BUS_CREDS_UID, &creds);
+ if (r >= 0) {
+ (void) sd_bus_creds_get_uid(creds, &uid);
+ (void) sd_bus_creds_get_tty(creds, &tty);
+ }
+
+ utmp_wall("The system shutdown has been cancelled",
+ lookup_uid(uid), tty, logind_wall_tty_filter, m);
+ }
+
+ return sd_bus_reply_method_return(message, "b", cancelled);
+}
+
+static int method_hibernate(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Manager *m = userdata;
return method_do_shutdown_or_sleep(
@@ -1705,11 +1999,10 @@ static int method_hibernate(sd_bus *bus, sd_bus_message *message, void *userdata
"org.freedesktop.login1.hibernate-multiple-sessions",
"org.freedesktop.login1.hibernate-ignore-inhibit",
"hibernate",
- method_hibernate,
error);
}
-static int method_hybrid_sleep(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_hybrid_sleep(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Manager *m = userdata;
return method_do_shutdown_or_sleep(
@@ -1720,7 +2013,6 @@ static int method_hybrid_sleep(sd_bus *bus, sd_bus_message *message, void *userd
"org.freedesktop.login1.hibernate-multiple-sessions",
"org.freedesktop.login1.hibernate-ignore-inhibit",
"hybrid-sleep",
- method_hybrid_sleep,
error);
}
@@ -1772,7 +2064,7 @@ static int method_can_shutdown_or_sleep(
blocked = manager_is_inhibited(m, w, INHIBIT_BLOCK, NULL, false, true, uid, NULL);
if (multiple_sessions) {
- r = bus_verify_polkit(message, CAP_SYS_BOOT, action_multiple_sessions, false, &challenge, error);
+ r = bus_test_polkit(message, CAP_SYS_BOOT, action_multiple_sessions, UID_INVALID, &challenge, error);
if (r < 0)
return r;
@@ -1785,7 +2077,7 @@ static int method_can_shutdown_or_sleep(
}
if (blocked) {
- r = bus_verify_polkit(message, CAP_SYS_BOOT, action_ignore_inhibit, false, &challenge, error);
+ r = bus_test_polkit(message, CAP_SYS_BOOT, action_ignore_inhibit, UID_INVALID, &challenge, error);
if (r < 0)
return r;
@@ -1801,7 +2093,7 @@ static int method_can_shutdown_or_sleep(
/* If neither inhibit nor multiple sessions
* apply then just check the normal policy */
- r = bus_verify_polkit(message, CAP_SYS_BOOT, action, false, &challenge, error);
+ r = bus_test_polkit(message, CAP_SYS_BOOT, action, UID_INVALID, &challenge, error);
if (r < 0)
return r;
@@ -1816,7 +2108,7 @@ static int method_can_shutdown_or_sleep(
return sd_bus_reply_method_return(message, "s", result);
}
-static int method_can_poweroff(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_can_poweroff(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Manager *m = userdata;
return method_can_shutdown_or_sleep(
@@ -1829,7 +2121,7 @@ static int method_can_poweroff(sd_bus *bus, sd_bus_message *message, void *userd
error);
}
-static int method_can_reboot(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_can_reboot(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Manager *m = userdata;
return method_can_shutdown_or_sleep(
@@ -1842,7 +2134,7 @@ static int method_can_reboot(sd_bus *bus, sd_bus_message *message, void *userdat
error);
}
-static int method_can_suspend(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_can_suspend(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Manager *m = userdata;
return method_can_shutdown_or_sleep(
@@ -1855,7 +2147,7 @@ static int method_can_suspend(sd_bus *bus, sd_bus_message *message, void *userda
error);
}
-static int method_can_hibernate(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_can_hibernate(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Manager *m = userdata;
return method_can_shutdown_or_sleep(
@@ -1868,7 +2160,7 @@ static int method_can_hibernate(sd_bus *bus, sd_bus_message *message, void *user
error);
}
-static int method_can_hybrid_sleep(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_can_hybrid_sleep(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Manager *m = userdata;
return method_can_shutdown_or_sleep(
@@ -1881,7 +2173,100 @@ static int method_can_hybrid_sleep(sd_bus *bus, sd_bus_message *message, void *u
error);
}
-static int method_inhibit(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int property_get_reboot_to_firmware_setup(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+ int r;
+
+ assert(bus);
+ assert(reply);
+ assert(userdata);
+
+ r = efi_get_reboot_to_firmware();
+ if (r < 0 && r != -EOPNOTSUPP)
+ return r;
+
+ return sd_bus_message_append(reply, "b", r > 0);
+}
+
+static int method_set_reboot_to_firmware_setup(
+ sd_bus_message *message,
+ void *userdata,
+ sd_bus_error *error) {
+
+ int b, r;
+ Manager *m = userdata;
+
+ assert(message);
+ assert(m);
+
+ r = sd_bus_message_read(message, "b", &b);
+ if (r < 0)
+ return r;
+
+ r = bus_verify_polkit_async(message,
+ CAP_SYS_ADMIN,
+ "org.freedesktop.login1.set-reboot-to-firmware-setup",
+ false,
+ UID_INVALID,
+ &m->polkit_registry,
+ error);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
+
+ r = efi_set_reboot_to_firmware(b);
+ if (r < 0)
+ return r;
+
+ return sd_bus_reply_method_return(message, NULL);
+}
+
+static int method_can_reboot_to_firmware_setup(
+ sd_bus_message *message,
+ void *userdata,
+ sd_bus_error *error) {
+
+ int r;
+ bool challenge;
+ const char *result;
+ Manager *m = userdata;
+
+ assert(message);
+ assert(m);
+
+ r = efi_reboot_to_firmware_supported();
+ if (r == -EOPNOTSUPP)
+ return sd_bus_reply_method_return(message, "s", "na");
+ else if (r < 0)
+ return r;
+
+ r = bus_test_polkit(message,
+ CAP_SYS_ADMIN,
+ "org.freedesktop.login1.set-reboot-to-firmware-setup",
+ UID_INVALID,
+ &challenge,
+ error);
+ if (r < 0)
+ return r;
+
+ if (r > 0)
+ result = "yes";
+ else if (challenge)
+ result = "challenge";
+ else
+ result = "no";
+
+ return sd_bus_reply_method_return(message, "s", result);
+}
+
+static int method_inhibit(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
const char *who, *why, *what, *mode;
_cleanup_free_ char *id = NULL;
@@ -1894,7 +2279,6 @@ static int method_inhibit(sd_bus *bus, sd_bus_message *message, void *userdata,
uid_t uid;
int r;
- assert(bus);
assert(message);
assert(m);
@@ -1921,15 +2305,20 @@ static int method_inhibit(sd_bus *bus, sd_bus_message *message, void *userdata,
if (m->action_what & w)
return sd_bus_error_setf(error, BUS_ERROR_OPERATION_IN_PROGRESS, "The operation inhibition has been requested for is already running");
- r = bus_verify_polkit_async(message, CAP_SYS_BOOT,
- w == INHIBIT_SHUTDOWN ? (mm == INHIBIT_BLOCK ? "org.freedesktop.login1.inhibit-block-shutdown" : "org.freedesktop.login1.inhibit-delay-shutdown") :
- w == INHIBIT_SLEEP ? (mm == INHIBIT_BLOCK ? "org.freedesktop.login1.inhibit-block-sleep" : "org.freedesktop.login1.inhibit-delay-sleep") :
- w == INHIBIT_IDLE ? "org.freedesktop.login1.inhibit-block-idle" :
- w == INHIBIT_HANDLE_POWER_KEY ? "org.freedesktop.login1.inhibit-handle-power-key" :
- w == INHIBIT_HANDLE_SUSPEND_KEY ? "org.freedesktop.login1.inhibit-handle-suspend-key" :
- w == INHIBIT_HANDLE_HIBERNATE_KEY ? "org.freedesktop.login1.inhibit-handle-hibernate-key" :
- "org.freedesktop.login1.inhibit-handle-lid-switch",
- false, &m->polkit_registry, error);
+ r = bus_verify_polkit_async(
+ message,
+ CAP_SYS_BOOT,
+ w == INHIBIT_SHUTDOWN ? (mm == INHIBIT_BLOCK ? "org.freedesktop.login1.inhibit-block-shutdown" : "org.freedesktop.login1.inhibit-delay-shutdown") :
+ w == INHIBIT_SLEEP ? (mm == INHIBIT_BLOCK ? "org.freedesktop.login1.inhibit-block-sleep" : "org.freedesktop.login1.inhibit-delay-sleep") :
+ w == INHIBIT_IDLE ? "org.freedesktop.login1.inhibit-block-idle" :
+ w == INHIBIT_HANDLE_POWER_KEY ? "org.freedesktop.login1.inhibit-handle-power-key" :
+ w == INHIBIT_HANDLE_SUSPEND_KEY ? "org.freedesktop.login1.inhibit-handle-suspend-key" :
+ w == INHIBIT_HANDLE_HIBERNATE_KEY ? "org.freedesktop.login1.inhibit-handle-hibernate-key" :
+ "org.freedesktop.login1.inhibit-handle-lid-switch",
+ false,
+ UID_INVALID,
+ &m->polkit_registry,
+ error);
if (r < 0)
return r;
if (r == 0)
@@ -1992,10 +2381,14 @@ fail:
const sd_bus_vtable manager_vtable[] = {
SD_BUS_VTABLE_START(0),
+ SD_BUS_WRITABLE_PROPERTY("EnableWallMessages", "b", NULL, NULL, offsetof(Manager, enable_wall_messages), 0),
+ SD_BUS_WRITABLE_PROPERTY("WallMessage", "s", NULL, NULL, offsetof(Manager, wall_message), 0),
+
SD_BUS_PROPERTY("NAutoVTs", "u", NULL, offsetof(Manager, n_autovts), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("KillOnlyUsers", "as", NULL, offsetof(Manager, kill_only_users), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("KillExcludeUsers", "as", NULL, offsetof(Manager, kill_exclude_users), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("KillUserProcesses", "b", NULL, offsetof(Manager, kill_user_processes), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("RebootToFirmwareSetup", "b", property_get_reboot_to_firmware_setup, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("IdleHint", "b", property_get_idle_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("IdleSinceHint", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("IdleSinceHintMonotonic", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
@@ -2007,10 +2400,12 @@ const sd_bus_vtable manager_vtable[] = {
SD_BUS_PROPERTY("HandleHibernateKey", "s", property_get_handle_action, offsetof(Manager, handle_hibernate_key), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("HandleLidSwitch", "s", property_get_handle_action, offsetof(Manager, handle_lid_switch), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("HandleLidSwitchDocked", "s", property_get_handle_action, offsetof(Manager, handle_lid_switch_docked), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("HoldoffTimeoutUSec", "t", NULL, offsetof(Manager, holdoff_timeout_usec), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("IdleAction", "s", property_get_handle_action, offsetof(Manager, idle_action), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("IdleActionUSec", "t", NULL, offsetof(Manager, idle_action_usec), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("PreparingForShutdown", "b", property_get_preparing, 0, 0),
SD_BUS_PROPERTY("PreparingForSleep", "b", property_get_preparing, 0, 0),
+ SD_BUS_PROPERTY("ScheduledShutdown", "(st)", property_get_scheduled_shutdown, 0, 0),
SD_BUS_METHOD("GetSession", "s", "o", method_get_session, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("GetSessionByPID", "u", "o", method_get_session_by_pid, SD_BUS_VTABLE_UNPRIVILEGED),
@@ -2025,21 +2420,23 @@ const sd_bus_vtable manager_vtable[] = {
SD_BUS_METHOD("ReleaseSession", "s", NULL, method_release_session, 0),
SD_BUS_METHOD("ActivateSession", "s", NULL, method_activate_session, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("ActivateSessionOnSeat", "ss", NULL, method_activate_session_on_seat, SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD("LockSession", "s", NULL, method_lock_session, 0),
- SD_BUS_METHOD("UnlockSession", "s", NULL, method_lock_session, 0),
- SD_BUS_METHOD("LockSessions", NULL, NULL, method_lock_sessions, 0),
- SD_BUS_METHOD("UnlockSessions", NULL, NULL, method_lock_sessions, 0),
- SD_BUS_METHOD("KillSession", "ssi", NULL, method_kill_session, SD_BUS_VTABLE_CAPABILITY(CAP_KILL)),
- SD_BUS_METHOD("KillUser", "ui", NULL, method_kill_user, SD_BUS_VTABLE_CAPABILITY(CAP_KILL)),
- SD_BUS_METHOD("TerminateSession", "s", NULL, method_terminate_session, SD_BUS_VTABLE_CAPABILITY(CAP_KILL)),
- SD_BUS_METHOD("TerminateUser", "u", NULL, method_terminate_user, SD_BUS_VTABLE_CAPABILITY(CAP_KILL)),
- SD_BUS_METHOD("TerminateSeat", "s", NULL, method_terminate_seat, SD_BUS_VTABLE_CAPABILITY(CAP_KILL)),
+ SD_BUS_METHOD("LockSession", "s", NULL, method_lock_session, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("UnlockSession", "s", NULL, method_lock_session, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("LockSessions", NULL, NULL, method_lock_sessions, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("UnlockSessions", NULL, NULL, method_lock_sessions, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("KillSession", "ssi", NULL, method_kill_session, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("KillUser", "ui", NULL, method_kill_user, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("TerminateSession", "s", NULL, method_terminate_session, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("TerminateUser", "u", NULL, method_terminate_user, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("TerminateSeat", "s", NULL, method_terminate_seat, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("SetUserLinger", "ubb", NULL, method_set_user_linger, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("AttachDevice", "ssb", NULL, method_attach_device, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("FlushDevices", "b", NULL, method_flush_devices, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("PowerOff", "b", NULL, method_poweroff, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("Reboot", "b", NULL, method_reboot, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("Suspend", "b", NULL, method_suspend, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("ScheduleShutdown", "st", NULL, method_schedule_shutdown, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("CancelScheduledShutdown", NULL, "b", method_cancel_scheduled_shutdown, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("Hibernate", "b", NULL, method_hibernate, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("HybridSleep", "b", NULL, method_hybrid_sleep, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("CanPowerOff", NULL, "s", method_can_poweroff, SD_BUS_VTABLE_UNPRIVILEGED),
@@ -2048,6 +2445,8 @@ const sd_bus_vtable manager_vtable[] = {
SD_BUS_METHOD("CanHibernate", NULL, "s", method_can_hibernate, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("CanHybridSleep", NULL, "s", method_can_hybrid_sleep, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("Inhibit", "ssss", "h", method_inhibit, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("CanRebootToFirmwareSetup", NULL, "s", method_can_reboot_to_firmware_setup, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("SetRebootToFirmwareSetup", "b", NULL, method_set_reboot_to_firmware_setup, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_SIGNAL("SessionNew", "so", 0),
SD_BUS_SIGNAL("SessionRemoved", "so", 0),
@@ -2082,7 +2481,7 @@ static int session_jobs_reply(Session *s, const char *unit, const char *result)
return r;
}
-int match_job_removed(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+int match_job_removed(sd_bus_message *message, void *userdata, sd_bus_error *error) {
const char *path, *result, *unit;
Manager *m = userdata;
Session *session;
@@ -2090,7 +2489,6 @@ int match_job_removed(sd_bus *bus, sd_bus_message *message, void *userdata, sd_b
User *user;
int r;
- assert(bus);
assert(message);
assert(m);
@@ -2151,14 +2549,13 @@ int match_job_removed(sd_bus *bus, sd_bus_message *message, void *userdata, sd_b
return 0;
}
-int match_unit_removed(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+int match_unit_removed(sd_bus_message *message, void *userdata, sd_bus_error *error) {
const char *path, *unit;
Manager *m = userdata;
Session *session;
User *user;
int r;
- assert(bus);
assert(message);
assert(m);
@@ -2179,7 +2576,7 @@ int match_unit_removed(sd_bus *bus, sd_bus_message *message, void *userdata, sd_
return 0;
}
-int match_properties_changed(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+int match_properties_changed(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_free_ char *unit = NULL;
Manager *m = userdata;
const char *path;
@@ -2187,7 +2584,6 @@ int match_properties_changed(sd_bus *bus, sd_bus_message *message, void *userdat
User *user;
int r;
- assert(bus);
assert(message);
assert(m);
@@ -2212,13 +2608,14 @@ int match_properties_changed(sd_bus *bus, sd_bus_message *message, void *userdat
return 0;
}
-int match_reloading(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+int match_reloading(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Manager *m = userdata;
Session *session;
Iterator i;
int b, r;
- assert(bus);
+ assert(message);
+ assert(m);
r = sd_bus_message_read(message, "b", &b);
if (r < 0) {
@@ -2238,16 +2635,17 @@ int match_reloading(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus
return 0;
}
-int match_name_owner_changed(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+int match_name_owner_changed(sd_bus_message *message, void *userdata, sd_bus_error *error) {
const char *name, *old, *new;
Manager *m = userdata;
Session *session;
Iterator i;
int r;
-
-
char *key;
+ assert(message);
+ assert(m);
+
r = sd_bus_message_read(message, "sss", &name, &old, &new);
if (r < 0) {
bus_log_parse_error(r);
@@ -2286,44 +2684,6 @@ int manager_send_changed(Manager *manager, const char *property, ...) {
l);
}
-int manager_dispatch_delayed(Manager *manager) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
- Inhibitor *offending = NULL;
- int r;
-
- assert(manager);
-
- if (manager->action_what == 0 || manager->action_job)
- return 0;
-
- /* Continue delay? */
- if (manager_is_inhibited(manager, manager->action_what, INHIBIT_DELAY, NULL, false, false, 0, &offending)) {
- _cleanup_free_ char *comm = NULL, *u = NULL;
-
- get_process_comm(offending->pid, &comm);
- u = uid_to_name(offending->uid);
-
- if (manager->action_timestamp + manager->inhibit_delay_max > now(CLOCK_MONOTONIC))
- return 0;
-
- log_info("Delay lock is active (UID "UID_FMT"/%s, PID "PID_FMT"/%s) but inhibitor timeout is reached.",
- offending->uid, strna(u),
- offending->pid, strna(comm));
- }
-
- /* Actually do the operation */
- r = execute_shutdown_or_sleep(manager, manager->action_what, manager->action_unit, &error);
- if (r < 0) {
- log_warning("Failed to send delayed message: %s", bus_error_message(&error, r));
-
- manager->action_unit = NULL;
- manager->action_what = 0;
- return r;
- }
-
- return 1;
-}
-
int manager_start_scope(
Manager *manager,
const char *scope,
diff --git a/src/login/logind-device.c b/src/login/logind-device.c
index 76c5a5c88f..ee4c45fb8d 100644
--- a/src/login/logind-device.c
+++ b/src/login/logind-device.c
@@ -19,7 +19,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <assert.h>
#include <string.h>
#include "util.h"
diff --git a/src/login/logind-device.h b/src/login/logind-device.h
index c273d2bfa0..6b2728586c 100644
--- a/src/login/logind-device.h
+++ b/src/login/logind-device.h
@@ -24,8 +24,6 @@
typedef struct Device Device;
#include "list.h"
-#include "util.h"
-#include "logind.h"
#include "logind-seat.h"
#include "logind-session-device.h"
diff --git a/src/login/logind-gperf.gperf b/src/login/logind-gperf.gperf
index 62460673b9..9218d098e0 100644
--- a/src/login/logind-gperf.gperf
+++ b/src/login/logind-gperf.gperf
@@ -29,6 +29,7 @@ Login.PowerKeyIgnoreInhibited, config_parse_bool, 0, offsetof(Manag
Login.SuspendKeyIgnoreInhibited, config_parse_bool, 0, offsetof(Manager, suspend_key_ignore_inhibited)
Login.HibernateKeyIgnoreInhibited, config_parse_bool, 0, offsetof(Manager, hibernate_key_ignore_inhibited)
Login.LidSwitchIgnoreInhibited, config_parse_bool, 0, offsetof(Manager, lid_switch_ignore_inhibited)
+Login.HoldoffTimeoutSec, config_parse_sec, 0, offsetof(Manager, holdoff_timeout_usec)
Login.IdleAction, config_parse_handle_action, 0, offsetof(Manager, idle_action)
Login.IdleActionSec, config_parse_sec, 0, offsetof(Manager, idle_action_usec)
Login.RuntimeDirectorySize, config_parse_tmpfs_size, 0, offsetof(Manager, runtime_dir_size)
diff --git a/src/login/logind-inhibit.c b/src/login/logind-inhibit.c
index 84fee0e773..68304a1610 100644
--- a/src/login/logind-inhibit.c
+++ b/src/login/logind-inhibit.c
@@ -26,9 +26,9 @@
#include "util.h"
#include "mkdir.h"
-#include "path-util.h"
#include "logind-inhibit.h"
#include "fileio.h"
+#include "formats-util.h"
Inhibitor* inhibitor_new(Manager *m, const char* id) {
Inhibitor *i;
@@ -232,18 +232,18 @@ int inhibitor_load(Inhibitor *i) {
}
if (who) {
- cc = cunescape(who);
- if (!cc)
- return -ENOMEM;
+ r = cunescape(who, 0, &cc);
+ if (r < 0)
+ return r;
free(i->who);
i->who = cc;
}
if (why) {
- cc = cunescape(why);
- if (!cc)
- return -ENOMEM;
+ r = cunescape(why, 0, &cc);
+ if (r < 0)
+ return r;
free(i->why);
i->why = cc;
diff --git a/src/login/logind-inhibit.h b/src/login/logind-inhibit.h
index f767876a4c..1b77fc1e9e 100644
--- a/src/login/logind-inhibit.h
+++ b/src/login/logind-inhibit.h
@@ -23,8 +23,6 @@
typedef struct Inhibitor Inhibitor;
-#include "list.h"
-#include "util.h"
typedef enum InhibitWhat {
INHIBIT_SHUTDOWN = 1,
@@ -46,7 +44,6 @@ typedef enum InhibitMode {
} InhibitMode;
#include "logind.h"
-#include "logind-seat.h"
struct Inhibitor {
Manager *manager;
diff --git a/src/login/logind-seat-dbus.c b/src/login/logind-seat-dbus.c
index 50b0b8842f..ce67ffde37 100644
--- a/src/login/logind-seat-dbus.c
+++ b/src/login/logind-seat-dbus.c
@@ -193,14 +193,26 @@ static int property_get_idle_since_hint(
return sd_bus_message_append(reply, "t", u);
}
-static int method_terminate(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+int bus_seat_method_terminate(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Seat *s = userdata;
int r;
- assert(bus);
assert(message);
assert(s);
+ r = bus_verify_polkit_async(
+ message,
+ CAP_KILL,
+ "org.freedesktop.login1.manage",
+ false,
+ UID_INVALID,
+ &s->manager->polkit_registry,
+ error);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return 1; /* Will call us back */
+
r = seat_stop_sessions(s, true);
if (r < 0)
return r;
@@ -208,13 +220,12 @@ static int method_terminate(sd_bus *bus, sd_bus_message *message, void *userdata
return sd_bus_reply_method_return(message, NULL);
}
-static int method_activate_session(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_activate_session(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Seat *s = userdata;
const char *name;
Session *session;
int r;
- assert(bus);
assert(message);
assert(s);
@@ -236,12 +247,11 @@ static int method_activate_session(sd_bus *bus, sd_bus_message *message, void *u
return sd_bus_reply_method_return(message, NULL);
}
-static int method_switch_to(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_switch_to(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Seat *s = userdata;
unsigned int to;
int r;
- assert(bus);
assert(message);
assert(s);
@@ -259,11 +269,10 @@ static int method_switch_to(sd_bus *bus, sd_bus_message *message, void *userdata
return sd_bus_reply_method_return(message, NULL);
}
-static int method_switch_to_next(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_switch_to_next(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Seat *s = userdata;
int r;
- assert(bus);
assert(message);
assert(s);
@@ -274,11 +283,10 @@ static int method_switch_to_next(sd_bus *bus, sd_bus_message *message, void *use
return sd_bus_reply_method_return(message, NULL);
}
-static int method_switch_to_previous(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_switch_to_previous(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Seat *s = userdata;
int r;
- assert(bus);
assert(message);
assert(s);
@@ -302,7 +310,7 @@ const sd_bus_vtable seat_vtable[] = {
SD_BUS_PROPERTY("IdleSinceHint", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("IdleSinceHintMonotonic", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
- SD_BUS_METHOD("Terminate", NULL, NULL, method_terminate, SD_BUS_VTABLE_CAPABILITY(CAP_KILL)),
+ SD_BUS_METHOD("Terminate", NULL, NULL, bus_seat_method_terminate, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("ActivateSession", "s", NULL, method_activate_session, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("SwitchTo", "u", NULL, method_switch_to, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("SwitchToNext", NULL, NULL, method_switch_to_next, SD_BUS_VTABLE_UNPRIVILEGED),
diff --git a/src/login/logind-seat.c b/src/login/logind-seat.c
index 126c5b84cc..11d24ce5b4 100644
--- a/src/login/logind-seat.c
+++ b/src/login/logind-seat.c
@@ -19,21 +19,18 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <assert.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
-#include <sys/ioctl.h>
-#include <linux/vt.h>
#include <string.h>
-#include "sd-id128.h"
#include "sd-messages.h"
#include "logind-seat.h"
#include "logind-acl.h"
#include "util.h"
#include "mkdir.h"
-#include "path-util.h"
+#include "formats-util.h"
+#include "terminal-util.h"
Seat *seat_new(Manager *m, const char *id) {
Seat *s;
diff --git a/src/login/logind-seat.h b/src/login/logind-seat.h
index 9e469d41c6..248dbeb9d7 100644
--- a/src/login/logind-seat.h
+++ b/src/login/logind-seat.h
@@ -24,9 +24,6 @@
typedef struct Seat Seat;
#include "list.h"
-#include "util.h"
-#include "logind.h"
-#include "logind-device.h"
#include "logind-session.h"
struct Seat {
@@ -96,3 +93,5 @@ char *seat_bus_path(Seat *s);
int seat_send_signal(Seat *s, bool new_seat);
int seat_send_changed(Seat *s, const char *properties, ...) _sentinel_;
+
+int bus_seat_method_terminate(sd_bus_message *message, void *userdata, sd_bus_error *error);
diff --git a/src/login/logind-session-dbus.c b/src/login/logind-session-dbus.c
index 4e7edef52d..debaa31a29 100644
--- a/src/login/logind-session-dbus.c
+++ b/src/login/logind-session-dbus.c
@@ -180,14 +180,26 @@ static int property_get_idle_since_hint(
return sd_bus_message_append(reply, "t", u);
}
-static int method_terminate(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+int bus_session_method_terminate(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Session *s = userdata;
int r;
- assert(bus);
assert(message);
assert(s);
+ r = bus_verify_polkit_async(
+ message,
+ CAP_KILL,
+ "org.freedesktop.login1.manage",
+ false,
+ s->user->uid,
+ &s->manager->polkit_registry,
+ error);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return 1; /* Will call us back */
+
r = session_stop(s, true);
if (r < 0)
return r;
@@ -195,11 +207,10 @@ static int method_terminate(sd_bus *bus, sd_bus_message *message, void *userdata
return sd_bus_reply_method_return(message, NULL);
}
-static int method_activate(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+int bus_session_method_activate(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Session *s = userdata;
int r;
- assert(bus);
assert(message);
assert(s);
@@ -210,28 +221,39 @@ static int method_activate(sd_bus *bus, sd_bus_message *message, void *userdata,
return sd_bus_reply_method_return(message, NULL);
}
-static int method_lock(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+int bus_session_method_lock(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Session *s = userdata;
int r;
- assert(bus);
assert(message);
assert(s);
- r = session_send_lock(s, streq(sd_bus_message_get_member(message), "Lock"));
+ r = bus_verify_polkit_async(
+ message,
+ CAP_SYS_ADMIN,
+ "org.freedesktop.login1.lock-sessions",
+ false,
+ s->user->uid,
+ &s->manager->polkit_registry,
+ error);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return 1; /* Will call us back */
+
+ r = session_send_lock(s, strstr(sd_bus_message_get_member(message), "Lock"));
if (r < 0)
return r;
return sd_bus_reply_method_return(message, NULL);
}
-static int method_set_idle_hint(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_set_idle_hint(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
Session *s = userdata;
uid_t uid;
int r, b;
- assert(bus);
assert(message);
assert(s);
@@ -255,14 +277,13 @@ static int method_set_idle_hint(sd_bus *bus, sd_bus_message *message, void *user
return sd_bus_reply_method_return(message, NULL);
}
-static int method_kill(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+int bus_session_method_kill(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Session *s = userdata;
const char *swho;
int32_t signo;
KillWho who;
int r;
- assert(bus);
assert(message);
assert(s);
@@ -281,6 +302,19 @@ static int method_kill(sd_bus *bus, sd_bus_message *message, void *userdata, sd_
if (signo <= 0 || signo >= _NSIG)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid signal %i", signo);
+ r = bus_verify_polkit_async(
+ message,
+ CAP_KILL,
+ "org.freedesktop.login1.manage",
+ false,
+ s->user->uid,
+ &s->manager->polkit_registry,
+ error);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return 1; /* Will call us back */
+
r = session_kill(s, who, signo);
if (r < 0)
return r;
@@ -288,13 +322,12 @@ static int method_kill(sd_bus *bus, sd_bus_message *message, void *userdata, sd_
return sd_bus_reply_method_return(message, NULL);
}
-static int method_take_control(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_take_control(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
Session *s = userdata;
int r, force;
uid_t uid;
- assert(bus);
assert(message);
assert(s);
@@ -320,10 +353,9 @@ static int method_take_control(sd_bus *bus, sd_bus_message *message, void *userd
return sd_bus_reply_method_return(message, NULL);
}
-static int method_release_control(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_release_control(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Session *s = userdata;
- assert(bus);
assert(message);
assert(s);
@@ -335,14 +367,13 @@ static int method_release_control(sd_bus *bus, sd_bus_message *message, void *us
return sd_bus_reply_method_return(message, NULL);
}
-static int method_take_device(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_take_device(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Session *s = userdata;
uint32_t major, minor;
SessionDevice *sd;
dev_t dev;
int r;
- assert(bus);
assert(message);
assert(s);
@@ -374,14 +405,13 @@ static int method_take_device(sd_bus *bus, sd_bus_message *message, void *userda
return r;
}
-static int method_release_device(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_release_device(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Session *s = userdata;
uint32_t major, minor;
SessionDevice *sd;
dev_t dev;
int r;
- assert(bus);
assert(message);
assert(s);
@@ -401,14 +431,13 @@ static int method_release_device(sd_bus *bus, sd_bus_message *message, void *use
return sd_bus_reply_method_return(message, NULL);
}
-static int method_pause_device_complete(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_pause_device_complete(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Session *s = userdata;
uint32_t major, minor;
SessionDevice *sd;
dev_t dev;
int r;
- assert(bus);
assert(message);
assert(s);
@@ -456,12 +485,12 @@ const sd_bus_vtable session_vtable[] = {
SD_BUS_PROPERTY("IdleSinceHint", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("IdleSinceHintMonotonic", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
- SD_BUS_METHOD("Terminate", NULL, NULL, method_terminate, SD_BUS_VTABLE_CAPABILITY(CAP_KILL)),
- SD_BUS_METHOD("Activate", NULL, NULL, method_activate, SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD("Lock", NULL, NULL, method_lock, 0),
- SD_BUS_METHOD("Unlock", NULL, NULL, method_lock, 0),
+ SD_BUS_METHOD("Terminate", NULL, NULL, bus_session_method_terminate, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("Activate", NULL, NULL, bus_session_method_activate, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("Lock", NULL, NULL, bus_session_method_lock, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("Unlock", NULL, NULL, bus_session_method_lock, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("SetIdleHint", "b", NULL, method_set_idle_hint, SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD("Kill", "si", NULL, method_kill, SD_BUS_VTABLE_CAPABILITY(CAP_KILL)),
+ SD_BUS_METHOD("Kill", "si", NULL, bus_session_method_kill, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("TakeControl", "b", NULL, method_take_control, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("ReleaseControl", NULL, NULL, method_release_control, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("TakeDevice", "uu", "hb", method_take_device, SD_BUS_VTABLE_UNPRIVILEGED),
diff --git a/src/login/logind-session-device.c b/src/login/logind-session-device.c
index c2de862250..656f268dba 100644
--- a/src/login/logind-session-device.c
+++ b/src/login/logind-session-device.c
@@ -19,16 +19,12 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <assert.h>
#include <fcntl.h>
#include <libudev.h>
#include <linux/input.h>
-#include <linux/ioctl.h>
#include <string.h>
#include <sys/ioctl.h>
-#include <sys/stat.h>
#include <sys/types.h>
-#include <unistd.h>
#include "util.h"
#include "missing.h"
diff --git a/src/login/logind-session-device.h b/src/login/logind-session-device.h
index 61a843d09d..1c9f998371 100644
--- a/src/login/logind-session-device.h
+++ b/src/login/logind-session-device.h
@@ -25,11 +25,7 @@ typedef enum DeviceType DeviceType;
typedef struct SessionDevice SessionDevice;
#include "list.h"
-#include "util.h"
#include "logind.h"
-#include "logind-device.h"
-#include "logind-seat.h"
-#include "logind-session.h"
enum DeviceType {
DEVICE_TYPE_UNKNOWN,
diff --git a/src/login/logind-session.c b/src/login/logind-session.c
index a02a537f7c..6a450b02a0 100644
--- a/src/login/logind-session.c
+++ b/src/login/logind-session.c
@@ -28,9 +28,7 @@
#include <sys/ioctl.h>
#include <unistd.h>
-#include "sd-id128.h"
#include "sd-messages.h"
-#include "strv.h"
#include "util.h"
#include "mkdir.h"
#include "path-util.h"
@@ -39,6 +37,8 @@
#include "bus-util.h"
#include "bus-error.h"
#include "logind-session.h"
+#include "formats-util.h"
+#include "terminal-util.h"
#define RELEASE_USEC (20*USEC_PER_SEC)
@@ -463,7 +463,7 @@ int session_activate(Session *s) {
assert(s->user);
if (!s->seat)
- return -ENOTSUP;
+ return -EOPNOTSUPP;
if (s->seat->active == s)
return 0;
@@ -471,7 +471,7 @@ int session_activate(Session *s) {
/* on seats with VTs, we let VTs manage session-switching */
if (seat_has_vts(s->seat)) {
if (!s->vtnr)
- return -ENOTSUP;
+ return -EOPNOTSUPP;
return chvt(s->vtnr);
}
@@ -703,18 +703,20 @@ static int release_timeout_callback(sd_event_source *es, uint64_t usec, void *us
return 0;
}
-void session_release(Session *s) {
+int session_release(Session *s) {
assert(s);
if (!s->started || s->stopping)
- return;
+ return 0;
+
+ if (s->timer_event_source)
+ return 0;
- if (!s->timer_event_source)
- sd_event_add_time(s->manager->event,
- &s->timer_event_source,
- CLOCK_MONOTONIC,
- now(CLOCK_MONOTONIC) + RELEASE_USEC, 0,
- release_timeout_callback, s);
+ return sd_event_add_time(s->manager->event,
+ &s->timer_event_source,
+ CLOCK_MONOTONIC,
+ now(CLOCK_MONOTONIC) + RELEASE_USEC, 0,
+ release_timeout_callback, s);
}
bool session_is_active(Session *s) {
@@ -1041,15 +1043,15 @@ void session_restore_vt(Session *s) {
if (vt < 0)
return;
- ioctl(vt, KDSETMODE, KD_TEXT);
+ (void) ioctl(vt, KDSETMODE, KD_TEXT);
if (read_one_line_file("/sys/module/vt/parameters/default_utf8", &utf8) >= 0 && *utf8 == '1')
kb = K_UNICODE;
- ioctl(vt, KDSKBMODE, kb);
+ (void) ioctl(vt, KDSKBMODE, kb);
mode.mode = VT_AUTO;
- ioctl(vt, VT_SETMODE, &mode);
+ (void) ioctl(vt, VT_SETMODE, &mode);
fchown(vt, 0, -1);
diff --git a/src/login/logind-session.h b/src/login/logind-session.h
index a007fb5e84..7a329b94ad 100644
--- a/src/login/logind-session.h
+++ b/src/login/logind-session.h
@@ -25,10 +25,6 @@ typedef struct Session Session;
typedef enum KillWho KillWho;
#include "list.h"
-#include "util.h"
-#include "logind.h"
-#include "logind-seat.h"
-#include "logind-session-device.h"
#include "logind-user.h"
#include "login-shared.h"
@@ -141,7 +137,7 @@ int session_create_fifo(Session *s);
int session_start(Session *s);
int session_stop(Session *s, bool force);
int session_finalize(Session *s);
-void session_release(Session *s);
+int session_release(Session *s);
int session_save(Session *s);
int session_load(Session *s);
int session_kill(Session *s, KillWho who, int signo);
@@ -179,3 +175,8 @@ void session_leave_vt(Session *s);
bool session_is_controller(Session *s, const char *sender);
int session_set_controller(Session *s, const char *sender, bool force);
void session_drop_controller(Session *s);
+
+int bus_session_method_activate(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_session_method_lock(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_session_method_terminate(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_session_method_kill(sd_bus_message *message, void *userdata, sd_bus_error *error);
diff --git a/src/login/logind-user-dbus.c b/src/login/logind-user-dbus.c
index 5cfaac0d4f..8a710cef13 100644
--- a/src/login/logind-user-dbus.c
+++ b/src/login/logind-user-dbus.c
@@ -26,6 +26,7 @@
#include "bus-util.h"
#include "logind.h"
#include "logind-user.h"
+#include "formats-util.h"
static int property_get_display(
sd_bus *bus,
@@ -171,14 +172,26 @@ static int property_get_linger(
return sd_bus_message_append(reply, "b", r > 0);
}
-static int method_terminate(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+int bus_user_method_terminate(sd_bus_message *message, void *userdata, sd_bus_error *error) {
User *u = userdata;
int r;
- assert(bus);
assert(message);
assert(u);
+ r = bus_verify_polkit_async(
+ message,
+ CAP_KILL,
+ "org.freedesktop.login1.manage",
+ false,
+ u->uid,
+ &u->manager->polkit_registry,
+ error);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return 1; /* Will call us back */
+
r = user_stop(u, true);
if (r < 0)
return r;
@@ -186,15 +199,27 @@ static int method_terminate(sd_bus *bus, sd_bus_message *message, void *userdata
return sd_bus_reply_method_return(message, NULL);
}
-static int method_kill(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+int bus_user_method_kill(sd_bus_message *message, void *userdata, sd_bus_error *error) {
User *u = userdata;
int32_t signo;
int r;
- assert(bus);
assert(message);
assert(u);
+ r = bus_verify_polkit_async(
+ message,
+ CAP_KILL,
+ "org.freedesktop.login1.manage",
+ false,
+ u->uid,
+ &u->manager->polkit_registry,
+ error);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return 1; /* Will call us back */
+
r = sd_bus_message_read(message, "i", &signo);
if (r < 0)
return r;
@@ -227,8 +252,8 @@ const sd_bus_vtable user_vtable[] = {
SD_BUS_PROPERTY("IdleSinceHintMonotonic", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("Linger", "b", property_get_linger, 0, 0),
- SD_BUS_METHOD("Terminate", NULL, NULL, method_terminate, SD_BUS_VTABLE_CAPABILITY(CAP_KILL)),
- SD_BUS_METHOD("Kill", "i", NULL, method_kill, SD_BUS_VTABLE_CAPABILITY(CAP_KILL)),
+ SD_BUS_METHOD("Terminate", NULL, NULL, bus_user_method_terminate, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("Kill", "i", NULL, bus_user_method_kill, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_VTABLE_END
};
diff --git a/src/login/logind-user.c b/src/login/logind-user.c
index f4c4490e8f..dc3db9abda 100644
--- a/src/login/logind-user.c
+++ b/src/login/logind-user.c
@@ -26,8 +26,8 @@
#include "util.h"
#include "mkdir.h"
+#include "rm-rf.h"
#include "hashmap.h"
-#include "strv.h"
#include "fileio.h"
#include "path-util.h"
#include "special.h"
@@ -38,6 +38,7 @@
#include "clean-ipc.h"
#include "logind-user.h"
#include "smack-util.h"
+#include "formats-util.h"
User* user_new(Manager *m, uid_t uid, gid_t gid, const char *name) {
User *u;
@@ -319,7 +320,7 @@ static int user_mkdir_runtime_path(User *u) {
} else
p = u->runtime_path;
- if (path_is_mount_point(p, false) <= 0) {
+ if (path_is_mount_point(p, 0) <= 0) {
_cleanup_free_ char *t = NULL;
(void) mkdir(p, 0700);
@@ -377,7 +378,7 @@ static int user_start_slice(User *u) {
char lu[DECIMAL_STR_MAX(uid_t) + 1], *slice;
sprintf(lu, UID_FMT, u->uid);
- r = build_subslice(SPECIAL_USER_SLICE, lu, &slice);
+ r = slice_build_subslice(SPECIAL_USER_SLICE, lu, &slice);
if (r < 0)
return r;
@@ -410,9 +411,9 @@ static int user_start_service(User *u) {
char lu[DECIMAL_STR_MAX(uid_t) + 1], *service;
sprintf(lu, UID_FMT, u->uid);
- service = unit_name_build("user", lu, ".service");
- if (!service)
- return log_oom();
+ r = unit_name_build("user", lu, ".service", &service);
+ if (r < 0)
+ return log_error_errno(r, "Failed to build service name: %m");
r = manager_start_unit(u->manager, service, &error, &job);
if (r < 0) {
@@ -522,7 +523,7 @@ static int user_remove_runtime_path(User *u) {
if (!u->runtime_path)
return 0;
- r = rm_rf(u->runtime_path, false, false, false);
+ r = rm_rf(u->runtime_path, 0);
if (r < 0)
log_error_errno(r, "Failed to remove runtime directory %s: %m", u->runtime_path);
@@ -533,7 +534,7 @@ static int user_remove_runtime_path(User *u) {
if (r < 0 && errno != EINVAL && errno != ENOENT)
log_error_errno(errno, "Failed to unmount user runtime directory %s: %m", u->runtime_path);
- r = rm_rf(u->runtime_path, false, true, false);
+ r = rm_rf(u->runtime_path, REMOVE_ROOT);
if (r < 0)
log_error_errno(r, "Failed to remove runtime directory %s: %m", u->runtime_path);
diff --git a/src/login/logind-user.h b/src/login/logind-user.h
index 4e0568fea9..722247806b 100644
--- a/src/login/logind-user.h
+++ b/src/login/logind-user.h
@@ -24,9 +24,7 @@
typedef struct User User;
#include "list.h"
-#include "util.h"
#include "logind.h"
-#include "logind-session.h"
typedef enum UserState {
USER_OFFLINE, /* Not logged in at all */
@@ -92,3 +90,6 @@ int user_send_changed(User *u, const char *properties, ...) _sentinel_;
const char* user_state_to_string(UserState s) _const_;
UserState user_state_from_string(const char *s) _pure_;
+
+int bus_user_method_terminate(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_user_method_kill(sd_bus_message *message, void *userdata, sd_bus_error *error);
diff --git a/src/login/logind-utmp.c b/src/login/logind-utmp.c
new file mode 100644
index 0000000000..1e13ff01de
--- /dev/null
+++ b/src/login/logind-utmp.c
@@ -0,0 +1,182 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2015 Daniel Mack
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <pwd.h>
+
+#include "sd-messages.h"
+#include "strv.h"
+#include "special.h"
+#include "unit-name.h"
+#include "audit.h"
+#include "bus-util.h"
+#include "bus-error.h"
+#include "bus-common-errors.h"
+#include "logind.h"
+#include "formats-util.h"
+#include "utmp-wtmp.h"
+
+_const_ static usec_t when_wall(usec_t n, usec_t elapse) {
+
+ usec_t left;
+ unsigned int i;
+ static const int wall_timers[] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+ 25, 40, 55, 70, 100, 130, 150, 180,
+ };
+
+ /* If the time is already passed, then don't announce */
+ if (n >= elapse)
+ return 0;
+
+ left = elapse - n;
+
+ for (i = 1; i < ELEMENTSOF(wall_timers); i++)
+ if (wall_timers[i] * USEC_PER_MINUTE >= left)
+ return left - wall_timers[i-1] * USEC_PER_MINUTE;
+
+ return left % USEC_PER_HOUR;
+}
+
+bool logind_wall_tty_filter(const char *tty, void *userdata) {
+
+ Manager *m = userdata;
+
+ assert(m);
+
+ if (!startswith(tty, "/dev/"))
+ return true;
+
+ return !streq(tty + 5, m->scheduled_shutdown_tty);
+}
+
+static int warn_wall(Manager *m, usec_t n) {
+ char date[FORMAT_TIMESTAMP_MAX] = {};
+ _cleanup_free_ char *l = NULL;
+ usec_t left;
+ int r;
+
+ assert(m);
+
+ if (!m->enable_wall_messages)
+ return 0;
+
+ left = m->scheduled_shutdown_timeout > n;
+
+ r = asprintf(&l, "%s%sThe system is going down for %s %s%s!",
+ strempty(m->wall_message),
+ isempty(m->wall_message) ? "" : "\n",
+ m->scheduled_shutdown_type,
+ left ? "at " : "NOW",
+ left ? format_timestamp(date, sizeof(date), m->scheduled_shutdown_timeout) : "");
+ if (r < 0) {
+ log_oom();
+ return 0;
+ }
+
+ utmp_wall(l, lookup_uid(m->scheduled_shutdown_uid),
+ m->scheduled_shutdown_tty, logind_wall_tty_filter, m);
+
+ return 1;
+}
+
+static int wall_message_timeout_handler(
+ sd_event_source *s,
+ uint64_t usec,
+ void *userdata) {
+
+ Manager *m = userdata;
+ usec_t n, next;
+ int r;
+
+ assert(m);
+ assert(s == m->wall_message_timeout_source);
+
+ n = now(CLOCK_REALTIME);
+
+ r = warn_wall(m, n);
+ if (r == 0)
+ return 0;
+
+ next = when_wall(n, m->scheduled_shutdown_timeout);
+ if (next > 0) {
+ r = sd_event_source_set_time(s, n + next);
+ if (r < 0)
+ return log_error_errno(r, "sd_event_source_set_time() failed. %m");
+
+ r = sd_event_source_set_enabled(s, SD_EVENT_ONESHOT);
+ if (r < 0)
+ return log_error_errno(r, "sd_event_source_set_enabled() failed. %m");
+ }
+
+ return 0;
+}
+
+int manager_setup_wall_message_timer(Manager *m) {
+
+ usec_t n, elapse;
+ int r;
+
+ assert(m);
+
+ n = now(CLOCK_REALTIME);
+ elapse = m->scheduled_shutdown_timeout;
+
+ /* wall message handling */
+
+ if (isempty(m->scheduled_shutdown_type)) {
+ warn_wall(m, n);
+ return 0;
+ }
+
+ if (elapse < n)
+ return 0;
+
+ /* Warn immediately if less than 15 minutes are left */
+ if (elapse - n < 15 * USEC_PER_MINUTE) {
+ r = warn_wall(m, n);
+ if (r == 0)
+ return 0;
+ }
+
+ elapse = when_wall(n, elapse);
+ if (elapse == 0)
+ return 0;
+
+ if (m->wall_message_timeout_source) {
+ r = sd_event_source_set_time(m->wall_message_timeout_source, n + elapse);
+ if (r < 0)
+ return log_error_errno(r, "sd_event_source_set_time() failed. %m");
+
+ r = sd_event_source_set_enabled(m->wall_message_timeout_source, SD_EVENT_ONESHOT);
+ if (r < 0)
+ return log_error_errno(r, "sd_event_source_set_enabled() failed. %m");
+ } else {
+ r = sd_event_add_time(m->event, &m->wall_message_timeout_source,
+ CLOCK_REALTIME, n + elapse, 0, wall_message_timeout_handler, m);
+ if (r < 0)
+ return log_error_errno(r, "sd_event_add_time() failed. %m");
+ }
+
+ return 0;
+}
diff --git a/src/login/logind.c b/src/login/logind.c
index b44f376427..00f8dbdab2 100644
--- a/src/login/logind.c
+++ b/src/login/logind.c
@@ -24,19 +24,20 @@
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
-#include <linux/vt.h>
-#include <sys/timerfd.h>
#include "sd-daemon.h"
#include "strv.h"
#include "conf-parser.h"
-#include "mkdir.h"
#include "bus-util.h"
#include "bus-error.h"
-#include "logind.h"
#include "udev-util.h"
+#include "formats-util.h"
+#include "signal-util.h"
+#include "logind.h"
+
+static void manager_free(Manager *m);
-Manager *manager_new(void) {
+static Manager *manager_new(void) {
Manager *m;
int r;
@@ -57,6 +58,7 @@ Manager *manager_new(void) {
m->handle_lid_switch = HANDLE_SUSPEND;
m->handle_lid_switch_docked = HANDLE_IGNORE;
m->lid_switch_ignore_inhibited = true;
+ m->holdoff_timeout_usec = 30 * USEC_PER_SEC;
m->idle_action_usec = 30 * USEC_PER_MINUTE;
m->idle_action = HANDLE_IGNORE;
@@ -101,7 +103,7 @@ fail:
return NULL;
}
-void manager_free(Manager *m) {
+static void manager_free(Manager *m) {
Session *session;
User *u;
Device *d;
@@ -142,6 +144,10 @@ void manager_free(Manager *m) {
set_free_free(m->busnames);
sd_event_source_unref(m->idle_action_event_source);
+ sd_event_source_unref(m->inhibit_timeout_source);
+ sd_event_source_unref(m->scheduled_shutdown_timeout_source);
+ sd_event_source_unref(m->nologin_timeout_source);
+ sd_event_source_unref(m->wall_message_timeout_source);
sd_event_source_unref(m->console_active_event_source);
sd_event_source_unref(m->udev_seat_event_source);
@@ -164,6 +170,9 @@ void manager_free(Manager *m) {
if (m->udev)
udev_unref(m->udev);
+ if (m->unlink_nologin)
+ (void) unlink("/run/nologin");
+
bus_verify_polkit_async_registry_free(m->polkit_registry);
sd_bus_unref(m->bus);
@@ -174,6 +183,9 @@ void manager_free(Manager *m) {
strv_free(m->kill_only_users);
strv_free(m->kill_exclude_users);
+ free(m->scheduled_shutdown_type);
+ free(m->scheduled_shutdown_tty);
+ free(m->wall_message);
free(m->action_job);
free(m);
}
@@ -890,7 +902,7 @@ static int manager_connect_udev(Manager *m) {
return 0;
}
-void manager_gc(Manager *m, bool drop_not_started) {
+static void manager_gc(Manager *m, bool drop_not_started) {
Seat *seat;
Session *session;
User *user;
@@ -1001,7 +1013,7 @@ static int manager_dispatch_idle_action(sd_event_source *s, uint64_t t, void *us
return 0;
}
-int manager_startup(Manager *m) {
+static int manager_startup(Manager *m) {
int r;
Seat *seat;
Session *session;
@@ -1032,7 +1044,7 @@ int manager_startup(Manager *m) {
if (r < 0)
return log_error_errno(r, "Failed to add seat0: %m");
- r = manager_set_lid_switch_ignore(m, 0 + IGNORE_LID_SWITCH_STARTUP_USEC);
+ r = manager_set_lid_switch_ignore(m, 0 + m->holdoff_timeout_usec);
if (r < 0)
log_warning_errno(r, "Failed to set up lid switch ignore event source: %m");
@@ -1088,14 +1100,12 @@ int manager_startup(Manager *m) {
return 0;
}
-int manager_run(Manager *m) {
+static int manager_run(Manager *m) {
int r;
assert(m);
for (;;) {
- usec_t us = (uint64_t) -1;
-
r = sd_event_get_state(m->event);
if (r < 0)
return r;
@@ -1104,19 +1114,7 @@ int manager_run(Manager *m) {
manager_gc(m, true);
- if (manager_dispatch_delayed(m) > 0)
- continue;
-
- if (m->action_what != 0 && !m->action_job) {
- usec_t x, y;
-
- x = now(CLOCK_MONOTONIC);
- y = m->action_timestamp + m->inhibit_delay_max;
-
- us = x >= y ? 0 : y - x;
- }
-
- r = sd_event_run(m->event, us);
+ r = sd_event_run(m->event, (uint64_t) -1);
if (r < 0)
return r;
}
diff --git a/src/login/logind.conf b/src/login/logind.conf
index 6b1943a2d1..6df6f04c77 100644
--- a/src/login/logind.conf
+++ b/src/login/logind.conf
@@ -5,10 +5,11 @@
# the Free Software Foundation; either version 2.1 of the License, or
# (at your option) any later version.
#
-# You can override the directives in this file by creating files in
-# /etc/systemd/logind.conf.d/*.conf.
+# Entries in this file show the compile time defaults.
+# You can change settings by editing this file.
+# Defaults can be restored by simply deleting this file.
#
-# See logind.conf(5) for details
+# See logind.conf(5) for details.
[Login]
#NAutoVTs=6
@@ -26,6 +27,7 @@
#SuspendKeyIgnoreInhibited=no
#HibernateKeyIgnoreInhibited=no
#LidSwitchIgnoreInhibited=yes
+#HoldoffTimeoutSec=30s
#IdleAction=ignore
#IdleActionSec=30min
#RuntimeDirectorySize=10%
diff --git a/src/login/logind.h b/src/login/logind.h
index e0cb7d0238..cd226f55fc 100644
--- a/src/login/logind.h
+++ b/src/login/logind.h
@@ -22,12 +22,10 @@
***/
#include <stdbool.h>
-#include <inttypes.h>
#include <libudev.h>
#include "sd-event.h"
#include "sd-bus.h"
-#include "util.h"
#include "list.h"
#include "hashmap.h"
#include "set.h"
@@ -35,16 +33,10 @@
typedef struct Manager Manager;
#include "logind-device.h"
-#include "logind-seat.h"
-#include "logind-session.h"
-#include "logind-user.h"
#include "logind-inhibit.h"
#include "logind-button.h"
#include "logind-action.h"
-#define IGNORE_LID_SWITCH_STARTUP_USEC (3 * USEC_PER_MINUTE)
-#define IGNORE_LID_SWITCH_SUSPEND_USEC (30 * USEC_PER_SEC)
-
struct Manager {
sd_event *event;
sd_bus *bus;
@@ -103,7 +95,19 @@ struct Manager {
/* If a shutdown/suspend is currently executed, then this is
* the job of it */
char *action_job;
- usec_t action_timestamp;
+ sd_event_source *inhibit_timeout_source;
+
+ char *scheduled_shutdown_type;
+ usec_t scheduled_shutdown_timeout;
+ sd_event_source *scheduled_shutdown_timeout_source;
+ uid_t scheduled_shutdown_uid;
+ char *scheduled_shutdown_tty;
+ sd_event_source *nologin_timeout_source;
+ bool unlink_nologin;
+
+ char *wall_message;
+ unsigned enable_wall_messages;
+ sd_event_source *wall_message_timeout_source;
sd_event_source *idle_action_event_source;
usec_t idle_action_usec;
@@ -125,14 +129,12 @@ struct Manager {
Hashmap *polkit_registry;
+ usec_t holdoff_timeout_usec;
sd_event_source *lid_switch_ignore_event_source;
size_t runtime_dir_size;
};
-Manager *manager_new(void);
-void manager_free(Manager *m);
-
int manager_add_device(Manager *m, const char *sysfs, bool master, Device **_device);
int manager_add_button(Manager *m, const char *name, Button **_button);
int manager_add_seat(Manager *m, const char *id, Seat **_seat);
@@ -145,12 +147,8 @@ int manager_add_inhibitor(Manager *m, const char* id, Inhibitor **_inhibitor);
int manager_process_seat_device(Manager *m, struct udev_device *d);
int manager_process_button_device(Manager *m, struct udev_device *d);
-int manager_startup(Manager *m);
-int manager_run(Manager *m);
int manager_spawn_autovt(Manager *m, unsigned int vtnr);
-void manager_gc(Manager *m, bool drop_not_started);
-
bool manager_shall_kill(Manager *m, const char *user);
int manager_get_idle_hint(Manager *m, dual_timestamp *t);
@@ -164,18 +162,16 @@ bool manager_is_docked_or_multiple_displays(Manager *m);
extern const sd_bus_vtable manager_vtable[];
-int match_job_removed(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error);
-int match_unit_removed(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error);
-int match_properties_changed(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error);
-int match_reloading(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error);
-int match_name_owner_changed(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error);
+int match_job_removed(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int match_unit_removed(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int match_properties_changed(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int match_reloading(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int match_name_owner_changed(sd_bus_message *message, void *userdata, sd_bus_error *error);
int bus_manager_shutdown_or_sleep_now_or_later(Manager *m, const char *unit_name, InhibitWhat w, sd_bus_error *error);
int manager_send_changed(Manager *manager, const char *property, ...) _sentinel_;
-int manager_dispatch_delayed(Manager *manager);
-
int manager_start_scope(Manager *manager, const char *scope, pid_t pid, const char *slice, const char *description, const char *after, const char *after2, sd_bus_error *error, char **job);
int manager_start_unit(Manager *manager, const char *unit, sd_bus_error *error, char **job);
int manager_stop_unit(Manager *manager, const char *unit, sd_bus_error *error, char **job);
@@ -197,3 +193,6 @@ int config_parse_tmpfs_size(const char *unit, const char *filename, unsigned lin
int manager_get_session_from_creds(Manager *m, sd_bus_message *message, const char *name, sd_bus_error *error, Session **ret);
int manager_get_user_from_creds(Manager *m, sd_bus_message *message, uid_t uid, sd_bus_error *error, User **ret);
int manager_get_seat_from_creds(Manager *m, sd_bus_message *message, const char *name, sd_bus_error *error, Seat **ret);
+
+int manager_setup_wall_message_timer(Manager *m);
+bool logind_wall_tty_filter(const char *tty, void *userdata);
diff --git a/src/login/org.freedesktop.login1.conf b/src/login/org.freedesktop.login1.conf
index 1318328aa0..0ad78802dd 100644
--- a/src/login/org.freedesktop.login1.conf
+++ b/src/login/org.freedesktop.login1.conf
@@ -130,6 +130,14 @@
<allow send_destination="org.freedesktop.login1"
send_interface="org.freedesktop.login1.Manager"
+ send_member="CanRebootToFirmwareSetup"/>
+
+ <allow send_destination="org.freedesktop.login1"
+ send_interface="org.freedesktop.login1.Manager"
+ send_member="SetRebootToFirmwareSetup"/>
+
+ <allow send_destination="org.freedesktop.login1"
+ send_interface="org.freedesktop.login1.Manager"
send_member="AttachDevice"/>
<allow send_destination="org.freedesktop.login1"
diff --git a/src/login/org.freedesktop.login1.policy.in b/src/login/org.freedesktop.login1.policy.in
index 49094eeddb..83e7183323 100644
--- a/src/login/org.freedesktop.login1.policy.in
+++ b/src/login/org.freedesktop.login1.policy.in
@@ -270,4 +270,34 @@
<annotate key="org.freedesktop.policykit.imply">org.freedesktop.login1.hibernate</annotate>
</action>
+ <action id="org.freedesktop.login1.manage">
+ <_description>Manage active sessions, users and seats</_description>
+ <_message>Authentication is required for managing active sessions, users and seats.</_message>
+ <defaults>
+ <allow_any>auth_admin_keep</allow_any>
+ <allow_inactive>auth_admin_keep</allow_inactive>
+ <allow_active>auth_admin_keep</allow_active>
+ </defaults>
+ </action>
+
+ <action id="org.freedesktop.login1.lock-sessions">
+ <_description>Lock or unlock active sessions</_description>
+ <_message>Authentication is required to lock or unlock active sessions.</_message>
+ <defaults>
+ <allow_any>auth_admin_keep</allow_any>
+ <allow_inactive>auth_admin_keep</allow_inactive>
+ <allow_active>auth_admin_keep</allow_active>
+ </defaults>
+ </action>
+
+ <action id="org.freedesktop.login1.set-reboot-to-firmware-setup">
+ <_description>Allow indication to the firmware to boot to setup interface</_description>
+ <_message>Authentication is required to indicate to the firmware to boot to setup interface.</_message>
+ <defaults>
+ <allow_any>auth_admin_keep</allow_any>
+ <allow_inactive>auth_admin_keep</allow_inactive>
+ <allow_active>auth_admin_keep</allow_active>
+ </defaults>
+ </action>
+
</policyconfig>
diff --git a/src/login/pam_systemd.c b/src/login/pam_systemd.c
index d7a708fd0a..b5d419000c 100644
--- a/src/login/pam_systemd.c
+++ b/src/login/pam_systemd.c
@@ -40,6 +40,9 @@
#include "socket-util.h"
#include "fileio.h"
#include "bus-error.h"
+#include "formats-util.h"
+#include "terminal-util.h"
+#include "hostname-util.h"
static int parse_argv(
pam_handle_t *handle,
@@ -334,7 +337,7 @@ _public_ PAM_EXTERN int pam_sm_open_session(
/* If this fails vtnr will be 0, that's intended */
if (!isempty(cvtnr))
- safe_atou32(cvtnr, &vtnr);
+ (void) safe_atou32(cvtnr, &vtnr);
if (!isempty(display) && !vtnr) {
if (isempty(seat))
diff --git a/src/login/sysfs-show.c b/src/login/sysfs-show.c
index 9bd9152bed..9a9fb7622d 100644
--- a/src/login/sysfs-show.c
+++ b/src/login/sysfs-show.c
@@ -27,6 +27,7 @@
#include "sysfs-show.h"
#include "path-util.h"
#include "udev-util.h"
+#include "terminal-util.h"
static int show_sysfs_one(
struct udev *udev,
diff --git a/src/login/test-inhibit.c b/src/login/test-inhibit.c
index 274687d20f..03516de916 100644
--- a/src/login/test-inhibit.c
+++ b/src/login/test-inhibit.c
@@ -25,7 +25,6 @@
#include "util.h"
#include "sd-bus.h"
#include "bus-util.h"
-#include "bus-error.h"
static int inhibit(sd_bus *bus, const char *what) {
_cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
diff --git a/src/machine-id-commit/machine-id-commit.c b/src/machine-id-commit/machine-id-commit.c
index c7e4de8889..0f7748e453 100644
--- a/src/machine-id-commit/machine-id-commit.c
+++ b/src/machine-id-commit/machine-id-commit.c
@@ -19,7 +19,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <getopt.h>
@@ -29,7 +28,7 @@
#include "log.h"
#include "build.h"
-static const char *arg_root = "";
+static const char *arg_root = NULL;
static void help(void) {
printf("%s [OPTIONS...]\n\n"
@@ -99,7 +98,10 @@ int main(int argc, char *argv[]) {
r = parse_argv(argc, argv);
if (r <= 0)
- return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+ goto finish;
- return machine_id_commit(arg_root) < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+ r = machine_id_commit(arg_root);
+
+finish:
+ return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}
diff --git a/src/machine-id-setup/machine-id-setup-main.c b/src/machine-id-setup/machine-id-setup-main.c
index 85bbfc4299..20cb60b804 100644
--- a/src/machine-id-setup/machine-id-setup-main.c
+++ b/src/machine-id-setup/machine-id-setup-main.c
@@ -19,7 +19,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <getopt.h>
diff --git a/src/machine/image-dbus.c b/src/machine/image-dbus.c
index f5c7d4d880..95d7bca4bf 100644
--- a/src/machine/image-dbus.c
+++ b/src/machine/image-dbus.c
@@ -20,7 +20,6 @@
***/
#include "bus-label.h"
-#include "bus-common-errors.h"
#include "strv.h"
#include "bus-util.h"
#include "machine-image.h"
@@ -29,18 +28,30 @@
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_type, image_type, ImageType);
int bus_image_method_remove(
- sd_bus *bus,
sd_bus_message *message,
void *userdata,
sd_bus_error *error) {
Image *image = userdata;
+ Manager *m = image->userdata;
int r;
- assert(bus);
assert(message);
assert(image);
+ r = bus_verify_polkit_async(
+ message,
+ CAP_SYS_ADMIN,
+ "org.freedesktop.machine1.manage-images",
+ false,
+ UID_INVALID,
+ &m->polkit_registry,
+ error);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return 1; /* Will call us back */
+
r = image_remove(image);
if (r < 0)
return r;
@@ -49,16 +60,15 @@ int bus_image_method_remove(
}
int bus_image_method_rename(
- sd_bus *bus,
sd_bus_message *message,
void *userdata,
sd_bus_error *error) {
Image *image = userdata;
+ Manager *m = image->userdata;
const char *new_name;
int r;
- assert(bus);
assert(message);
assert(image);
@@ -69,6 +79,19 @@ int bus_image_method_rename(
if (!image_name_is_valid(new_name))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Image name '%s' is invalid.", new_name);
+ r = bus_verify_polkit_async(
+ message,
+ CAP_SYS_ADMIN,
+ "org.freedesktop.machine1.manage-images",
+ false,
+ UID_INVALID,
+ &m->polkit_registry,
+ error);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return 1; /* Will call us back */
+
r = image_rename(image, new_name);
if (r < 0)
return r;
@@ -77,16 +100,15 @@ int bus_image_method_rename(
}
int bus_image_method_clone(
- sd_bus *bus,
sd_bus_message *message,
void *userdata,
sd_bus_error *error) {
Image *image = userdata;
+ Manager *m = image->userdata;
const char *new_name;
int r, read_only;
- assert(bus);
assert(message);
assert(image);
@@ -97,6 +119,19 @@ int bus_image_method_clone(
if (!image_name_is_valid(new_name))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Image name '%s' is invalid.", new_name);
+ r = bus_verify_polkit_async(
+ message,
+ CAP_SYS_ADMIN,
+ "org.freedesktop.machine1.manage-images",
+ false,
+ UID_INVALID,
+ &m->polkit_registry,
+ error);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return 1; /* Will call us back */
+
r = image_clone(image, new_name, read_only);
if (r < 0)
return r;
@@ -105,21 +140,33 @@ int bus_image_method_clone(
}
int bus_image_method_mark_read_only(
- sd_bus *bus,
sd_bus_message *message,
void *userdata,
sd_bus_error *error) {
Image *image = userdata;
+ Manager *m = image->userdata;
int r, read_only;
- assert(bus);
assert(message);
r = sd_bus_message_read(message, "b", &read_only);
if (r < 0)
return r;
+ r = bus_verify_polkit_async(
+ message,
+ CAP_SYS_ADMIN,
+ "org.freedesktop.machine1.manage-images",
+ false,
+ UID_INVALID,
+ &m->polkit_registry,
+ error);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return 1; /* Will call us back */
+
r = image_read_only(image, read_only);
if (r < 0)
return r;
@@ -127,6 +174,42 @@ int bus_image_method_mark_read_only(
return sd_bus_reply_method_return(message, NULL);
}
+int bus_image_method_set_limit(
+ sd_bus_message *message,
+ void *userdata,
+ sd_bus_error *error) {
+
+ Image *image = userdata;
+ Manager *m = image->userdata;
+ uint64_t limit;
+ int r;
+
+ assert(message);
+
+ r = sd_bus_message_read(message, "t", &limit);
+ if (r < 0)
+ return r;
+
+ r = bus_verify_polkit_async(
+ message,
+ CAP_SYS_ADMIN,
+ "org.freedesktop.machine1.manage-images",
+ false,
+ UID_INVALID,
+ &m->polkit_registry,
+ error);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return 1; /* Will call us back */
+
+ r = image_set_limit(image, limit);
+ if (r < 0)
+ return r;
+
+ return sd_bus_reply_method_return(message, NULL);
+}
+
const sd_bus_vtable image_vtable[] = {
SD_BUS_VTABLE_START(0),
SD_BUS_PROPERTY("Name", "s", NULL, offsetof(Image, name), 0),
@@ -139,10 +222,11 @@ const sd_bus_vtable image_vtable[] = {
SD_BUS_PROPERTY("Limit", "t", NULL, offsetof(Image, limit), 0),
SD_BUS_PROPERTY("UsageExclusive", "t", NULL, offsetof(Image, usage_exclusive), 0),
SD_BUS_PROPERTY("LimitExclusive", "t", NULL, offsetof(Image, limit_exclusive), 0),
- SD_BUS_METHOD("Remove", NULL, NULL, bus_image_method_remove, 0),
- SD_BUS_METHOD("Rename", "s", NULL, bus_image_method_rename, 0),
- SD_BUS_METHOD("Clone", "sb", NULL, bus_image_method_clone, 0),
- SD_BUS_METHOD("MarkReadOnly", "b", NULL, bus_image_method_mark_read_only, 0),
+ SD_BUS_METHOD("Remove", NULL, NULL, bus_image_method_remove, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("Rename", "s", NULL, bus_image_method_rename, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("Clone", "sb", NULL, bus_image_method_clone, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("MarkReadOnly", "b", NULL, bus_image_method_mark_read_only, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("SetLimit", "t", NULL, bus_image_method_set_limit, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_VTABLE_END
};
@@ -207,6 +291,8 @@ int image_object_find(sd_bus *bus, const char *path, const char *interface, void
if (r <= 0)
return r;
+ image->userdata = m;
+
r = hashmap_put(m->image_cache, image->name, image);
if (r < 0) {
image_unref(image);
diff --git a/src/machine/image-dbus.h b/src/machine/image-dbus.h
index 1b4364cbea..d56d905c8e 100644
--- a/src/machine/image-dbus.h
+++ b/src/machine/image-dbus.h
@@ -30,7 +30,8 @@ char *image_bus_path(const char *name);
int image_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error);
int image_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error);
-int bus_image_method_remove(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error);
-int bus_image_method_rename(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error);
-int bus_image_method_clone(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error);
-int bus_image_method_mark_read_only(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_image_method_remove(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_image_method_rename(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_image_method_clone(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_image_method_mark_read_only(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_image_method_set_limit(sd_bus_message *message, void *userdata, sd_bus_error *error);
diff --git a/src/machine/machine-dbus.c b/src/machine/machine-dbus.c
index b46f0a8dac..0892479a9a 100644
--- a/src/machine/machine-dbus.c
+++ b/src/machine/machine-dbus.c
@@ -21,7 +21,13 @@
#include <errno.h>
#include <string.h>
-#include <arpa/inet.h>
+#include <sys/mount.h>
+
+/* When we include libgen.h because we need dirname() we immediately
+ * undefine basename() since libgen.h defines it as a macro to the XDG
+ * version which is really broken. */
+#include <libgen.h>
+#undef basename
#include "bus-util.h"
#include "bus-label.h"
@@ -32,9 +38,12 @@
#include "in-addr-util.h"
#include "local-addresses.h"
#include "path-util.h"
+#include "mkdir.h"
#include "bus-internal.h"
#include "machine.h"
#include "machine-dbus.h"
+#include "formats-util.h"
+#include "process-util.h"
static int property_get_id(
sd_bus *bus,
@@ -112,14 +121,26 @@ static int property_get_netif(
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_class, machine_class, MachineClass);
-int bus_machine_method_terminate(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+int bus_machine_method_terminate(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Machine *m = userdata;
int r;
- assert(bus);
assert(message);
assert(m);
+ r = bus_verify_polkit_async(
+ message,
+ CAP_KILL,
+ "org.freedesktop.machine1.manage-machines",
+ false,
+ UID_INVALID,
+ &m->manager->polkit_registry,
+ error);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return 1; /* Will call us back */
+
r = machine_stop(m);
if (r < 0)
return r;
@@ -127,14 +148,13 @@ int bus_machine_method_terminate(sd_bus *bus, sd_bus_message *message, void *use
return sd_bus_reply_method_return(message, NULL);
}
-int bus_machine_method_kill(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+int bus_machine_method_kill(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Machine *m = userdata;
const char *swho;
int32_t signo;
KillWho who;
int r;
- assert(bus);
assert(message);
assert(m);
@@ -153,6 +173,19 @@ int bus_machine_method_kill(sd_bus *bus, sd_bus_message *message, void *userdata
if (signo <= 0 || signo >= _NSIG)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid signal %i", signo);
+ r = bus_verify_polkit_async(
+ message,
+ CAP_KILL,
+ "org.freedesktop.machine1.manage-machines",
+ false,
+ UID_INVALID,
+ &m->manager->polkit_registry,
+ error);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return 1; /* Will call us back */
+
r = machine_kill(m, who, signo);
if (r < 0)
return r;
@@ -160,7 +193,7 @@ int bus_machine_method_kill(sd_bus *bus, sd_bus_message *message, void *userdata
return sd_bus_reply_method_return(message, NULL);
}
-int bus_machine_method_get_addresses(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+int bus_machine_method_get_addresses(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
_cleanup_close_pair_ int pair[2] = { -1, -1 };
_cleanup_free_ char *us = NULL, *them = NULL;
@@ -171,7 +204,6 @@ int bus_machine_method_get_addresses(sd_bus *bus, sd_bus_message *message, void
pid_t child;
int r;
- assert(bus);
assert(message);
assert(m);
@@ -180,26 +212,26 @@ int bus_machine_method_get_addresses(sd_bus *bus, sd_bus_message *message, void
r = readlink_malloc("/proc/self/ns/net", &us);
if (r < 0)
- return sd_bus_error_set_errno(error, r);
+ return r;
p = procfs_file_alloca(m->leader, "ns/net");
r = readlink_malloc(p, &them);
if (r < 0)
- return sd_bus_error_set_errno(error, r);
+ return r;
if (streq(us, them))
return sd_bus_error_setf(error, BUS_ERROR_NO_PRIVATE_NETWORKING, "Machine %s does not use private networking", m->name);
r = namespace_open(m->leader, NULL, NULL, &netns_fd, NULL);
if (r < 0)
- return sd_bus_error_set_errno(error, r);
+ return r;
if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, pair) < 0)
- return sd_bus_error_set_errno(error, -errno);
+ return -errno;
child = fork();
if (child < 0)
- return sd_bus_error_set_errno(error, -errno);
+ return sd_bus_error_set_errnof(error, errno, "Failed to fork(): %m");
if (child == 0) {
_cleanup_free_ struct local_address *addresses = NULL;
@@ -236,11 +268,11 @@ int bus_machine_method_get_addresses(sd_bus *bus, sd_bus_message *message, void
r = sd_bus_message_new_method_return(message, &reply);
if (r < 0)
- return sd_bus_error_set_errno(error, r);
+ return r;
r = sd_bus_message_open_container(reply, 'a', "(iay)");
if (r < 0)
- return sd_bus_error_set_errno(error, r);
+ return r;
for (;;) {
int family;
@@ -257,56 +289,56 @@ int bus_machine_method_get_addresses(sd_bus *bus, sd_bus_message *message, void
n = recvmsg(pair[0], &mh, 0);
if (n < 0)
- return sd_bus_error_set_errno(error, -errno);
+ return -errno;
if ((size_t) n < sizeof(family))
break;
r = sd_bus_message_open_container(reply, 'r', "iay");
if (r < 0)
- return sd_bus_error_set_errno(error, r);
+ return r;
r = sd_bus_message_append(reply, "i", family);
if (r < 0)
- return sd_bus_error_set_errno(error, r);
+ return r;
switch (family) {
case AF_INET:
if (n != sizeof(struct in_addr) + sizeof(family))
- return sd_bus_error_set_errno(error, EIO);
+ return -EIO;
r = sd_bus_message_append_array(reply, 'y', &in_addr.in, sizeof(in_addr.in));
break;
case AF_INET6:
if (n != sizeof(struct in6_addr) + sizeof(family))
- return sd_bus_error_set_errno(error, EIO);
+ return -EIO;
r = sd_bus_message_append_array(reply, 'y', &in_addr.in6, sizeof(in_addr.in6));
break;
}
if (r < 0)
- return sd_bus_error_set_errno(error, r);
+ return r;
r = sd_bus_message_close_container(reply);
if (r < 0)
- return sd_bus_error_set_errno(error, r);
+ return r;
}
r = wait_for_terminate(child, &si);
if (r < 0)
- return sd_bus_error_set_errno(error, r);
+ return sd_bus_error_set_errnof(error, r, "Failed to wait for client: %m");
if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS)
- return sd_bus_error_set_errno(error, EIO);
+ return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Client died abnormally.");
r = sd_bus_message_close_container(reply);
if (r < 0)
- return sd_bus_error_set_errno(error, r);
+ return r;
- return sd_bus_send(bus, reply, NULL);
+ return sd_bus_send(NULL, reply, NULL);
}
-int bus_machine_method_get_os_release(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+int bus_machine_method_get_os_release(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
_cleanup_close_ int mntns_fd = -1, root_fd = -1;
_cleanup_close_pair_ int pair[2] = { -1, -1 };
@@ -318,7 +350,6 @@ int bus_machine_method_get_os_release(sd_bus *bus, sd_bus_message *message, void
pid_t child;
int r;
- assert(bus);
assert(message);
assert(m);
@@ -334,7 +365,7 @@ int bus_machine_method_get_os_release(sd_bus *bus, sd_bus_message *message, void
child = fork();
if (child < 0)
- return -errno;
+ return sd_bus_error_set_errnof(error, errno, "Failed to fork(): %m");
if (child == 0) {
_cleanup_close_ int fd = -1;
@@ -373,9 +404,9 @@ int bus_machine_method_get_os_release(sd_bus *bus, sd_bus_message *message, void
r = wait_for_terminate(child, &si);
if (r < 0)
- return r;
+ return sd_bus_error_set_errnof(error, r, "Failed to wait for client: %m");
if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS)
- return -EIO;
+ return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Client died abnormally.");
r = sd_bus_message_new_method_return(message, &reply);
if (r < 0)
@@ -395,17 +426,16 @@ int bus_machine_method_get_os_release(sd_bus *bus, sd_bus_message *message, void
if (r < 0)
return r;
- return sd_bus_send(bus, reply, NULL);
+ return sd_bus_send(NULL, reply, NULL);
}
-int bus_machine_method_open_pty(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+int bus_machine_method_open_pty(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
_cleanup_free_ char *pty_name = NULL;
_cleanup_close_ int master = -1;
Machine *m = userdata;
int r;
- assert(bus);
assert(message);
assert(m);
@@ -428,18 +458,22 @@ int bus_machine_method_open_pty(sd_bus *bus, sd_bus_message *message, void *user
if (r < 0)
return r;
- return sd_bus_send(bus, reply, NULL);
+ return sd_bus_send(NULL, reply, NULL);
}
-int bus_machine_method_open_login(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+int bus_machine_method_open_login(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
_cleanup_free_ char *pty_name = NULL, *getty = NULL;
_cleanup_bus_unref_ sd_bus *container_bus = NULL;
_cleanup_close_ int master = -1;
Machine *m = userdata;
const char *p;
+ char *address;
int r;
+ assert(message);
+ assert(m);
+
if (m->class != MACHINE_CONTAINER)
return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Opening logins is only supported on container machines.");
@@ -448,6 +482,7 @@ int bus_machine_method_open_login(sd_bus *bus, sd_bus_message *message, void *us
CAP_SYS_ADMIN,
"org.freedesktop.machine1.login",
false,
+ UID_INVALID,
&m->manager->polkit_registry,
error);
if (r < 0)
@@ -475,13 +510,14 @@ int bus_machine_method_open_login(sd_bus *bus, sd_bus_message *message, void *us
return r;
#ifdef ENABLE_KDBUS
- asprintf(&container_bus->address, "x-machine-kernel:pid=" PID_FMT ";x-machine-unix:pid=" PID_FMT, m->leader, m->leader);
+# define ADDRESS_FMT "x-machine-kernel:pid=%1$" PID_PRI ";x-machine-unix:pid=%1$" PID_PRI
#else
- asprintf(&container_bus->address, "x-machine-kernel:pid=" PID_FMT, m->leader);
+# define ADDRESS_FMT "x-machine-unix:pid=%1$" PID_PRI
#endif
- if (!container_bus->address)
- return -ENOMEM;
+ if (asprintf(&address, ADDRESS_FMT, m->leader) < 0)
+ return log_oom();
+ container_bus->address = address;
container_bus->bus_client = true;
container_bus->trusted = false;
container_bus->is_system = true;
@@ -492,7 +528,7 @@ int bus_machine_method_open_login(sd_bus *bus, sd_bus_message *message, void *us
getty = strjoin("container-getty@", p, ".service", NULL);
if (!getty)
- return -ENOMEM;
+ return log_oom();
r = sd_bus_call_method(
container_bus,
@@ -515,7 +551,416 @@ int bus_machine_method_open_login(sd_bus *bus, sd_bus_message *message, void *us
if (r < 0)
return r;
- return sd_bus_send(bus, reply, NULL);
+ return sd_bus_send(NULL, reply, NULL);
+}
+
+int bus_machine_method_bind_mount(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ _cleanup_close_pair_ int errno_pipe_fd[2] = { -1, -1 };
+ char mount_slave[] = "/tmp/propagate.XXXXXX", *mount_tmp, *mount_outside, *p;
+ bool mount_slave_created = false, mount_slave_mounted = false,
+ mount_tmp_created = false, mount_tmp_mounted = false,
+ mount_outside_created = false, mount_outside_mounted = false;
+ const char *dest, *src;
+ Machine *m = userdata;
+ int read_only, make_directory;
+ pid_t child;
+ siginfo_t si;
+ int r;
+
+ assert(message);
+ assert(m);
+
+ if (m->class != MACHINE_CONTAINER)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Bind mounting is only supported on container machines.");
+
+ r = sd_bus_message_read(message, "ssbb", &src, &dest, &read_only, &make_directory);
+ if (r < 0)
+ return r;
+
+ if (!path_is_absolute(src) || !path_is_safe(src))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Source path must be absolute and not contain ../.");
+
+ if (isempty(dest))
+ dest = src;
+ else if (!path_is_absolute(dest) || !path_is_safe(dest))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Destination path must be absolute and not contain ../.");
+
+ r = bus_verify_polkit_async(
+ message,
+ CAP_SYS_ADMIN,
+ "org.freedesktop.machine1.manage-machines",
+ false,
+ UID_INVALID,
+ &m->manager->polkit_registry,
+ error);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return 1; /* Will call us back */
+
+ /* One day, when bind mounting /proc/self/fd/n works across
+ * namespace boundaries we should rework this logic to make
+ * use of it... */
+
+ p = strjoina("/run/systemd/nspawn/propagate/", m->name, "/");
+ if (laccess(p, F_OK) < 0)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Container does not allow propagation of mount points.");
+
+ /* Our goal is to install a new bind mount into the container,
+ possibly read-only. This is irritatingly complex
+ unfortunately, currently.
+
+ First, we start by creating a private playground in /tmp,
+ that we can mount MS_SLAVE. (Which is necessary, since
+ MS_MOUNT cannot be applied to mounts with MS_SHARED parent
+ mounts.) */
+
+ if (!mkdtemp(mount_slave))
+ return sd_bus_error_set_errnof(error, errno, "Failed to create playground %s: %m", mount_slave);
+
+ mount_slave_created = true;
+
+ if (mount(mount_slave, mount_slave, NULL, MS_BIND, NULL) < 0) {
+ r = sd_bus_error_set_errnof(error, errno, "Failed to make bind mount %s: %m", mount_slave);
+ goto finish;
+ }
+
+ mount_slave_mounted = true;
+
+ if (mount(NULL, mount_slave, NULL, MS_SLAVE, NULL) < 0) {
+ r = sd_bus_error_set_errnof(error, errno, "Failed to remount slave %s: %m", mount_slave);
+ goto finish;
+ }
+
+ /* Second, we mount the source directory to a directory inside
+ of our MS_SLAVE playground. */
+ mount_tmp = strjoina(mount_slave, "/mount");
+ if (mkdir(mount_tmp, 0700) < 0) {
+ r = sd_bus_error_set_errnof(error, errno, "Failed to create temporary mount point %s: %m", mount_tmp);
+ goto finish;
+ }
+
+ mount_tmp_created = true;
+
+ if (mount(src, mount_tmp, NULL, MS_BIND, NULL) < 0) {
+ r = sd_bus_error_set_errnof(error, errno, "Failed to overmount %s: %m", mount_tmp);
+ goto finish;
+ }
+
+ mount_tmp_mounted = true;
+
+ /* Third, we remount the new bind mount read-only if requested. */
+ if (read_only)
+ if (mount(NULL, mount_tmp, NULL, MS_BIND|MS_REMOUNT|MS_RDONLY, NULL) < 0) {
+ r = sd_bus_error_set_errnof(error, errno, "Failed to remount read-only %s: %m", mount_tmp);
+ goto finish;
+ }
+
+ /* Fourth, we move the new bind mount into the propagation
+ * directory. This way it will appear there read-only
+ * right-away. */
+
+ mount_outside = strjoina("/run/systemd/nspawn/propagate/", m->name, "/XXXXXX");
+ if (!mkdtemp(mount_outside)) {
+ r = sd_bus_error_set_errnof(error, errno, "Cannot create propagation directory %s: %m", mount_outside);
+ goto finish;
+ }
+
+ mount_outside_created = true;
+
+ if (mount(mount_tmp, mount_outside, NULL, MS_MOVE, NULL) < 0) {
+ r = sd_bus_error_set_errnof(error, errno, "Failed to move %s to %s: %m", mount_tmp, mount_outside);
+ goto finish;
+ }
+
+ mount_outside_mounted = true;
+ mount_tmp_mounted = false;
+
+ (void) rmdir(mount_tmp);
+ mount_tmp_created = false;
+
+ (void) umount(mount_slave);
+ mount_slave_mounted = false;
+
+ (void) rmdir(mount_slave);
+ mount_slave_created = false;
+
+ if (pipe2(errno_pipe_fd, O_CLOEXEC|O_NONBLOCK) < 0) {
+ r = sd_bus_error_set_errnof(error, errno, "Failed to create pipe: %m");
+ goto finish;
+ }
+
+ child = fork();
+ if (child < 0) {
+ r = sd_bus_error_set_errnof(error, errno, "Failed to fork(): %m");
+ goto finish;
+ }
+
+ if (child == 0) {
+ const char *mount_inside;
+ int mntfd;
+ const char *q;
+
+ errno_pipe_fd[0] = safe_close(errno_pipe_fd[0]);
+
+ q = procfs_file_alloca(m->leader, "ns/mnt");
+ mntfd = open(q, O_RDONLY|O_NOCTTY|O_CLOEXEC);
+ if (mntfd < 0) {
+ r = log_error_errno(errno, "Failed to open mount namespace of leader: %m");
+ goto child_fail;
+ }
+
+ if (setns(mntfd, CLONE_NEWNS) < 0) {
+ r = log_error_errno(errno, "Failed to join namespace of leader: %m");
+ goto child_fail;
+ }
+
+ if (make_directory)
+ (void) mkdir_p(dest, 0755);
+
+ /* Fifth, move the mount to the right place inside */
+ mount_inside = strjoina("/run/systemd/nspawn/incoming/", basename(mount_outside));
+ if (mount(mount_inside, dest, NULL, MS_MOVE, NULL) < 0) {
+ r = log_error_errno(errno, "Failed to mount: %m");
+ goto child_fail;
+ }
+
+ _exit(EXIT_SUCCESS);
+
+ child_fail:
+ (void) write(errno_pipe_fd[1], &r, sizeof(r));
+ errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]);
+
+ _exit(EXIT_FAILURE);
+ }
+
+ errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]);
+
+ r = wait_for_terminate(child, &si);
+ if (r < 0) {
+ r = sd_bus_error_set_errnof(error, r, "Failed to wait for client: %m");
+ goto finish;
+ }
+ if (si.si_code != CLD_EXITED) {
+ r = sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Client died abnormally.");
+ goto finish;
+ }
+ if (si.si_status != EXIT_SUCCESS) {
+
+ if (read(errno_pipe_fd[0], &r, sizeof(r)) == sizeof(r))
+ r = sd_bus_error_set_errnof(error, r, "Failed to mount: %m");
+ else
+ r = sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Client failed.");
+ goto finish;
+ }
+
+ r = sd_bus_reply_method_return(message, NULL);
+
+finish:
+ if (mount_outside_mounted)
+ umount(mount_outside);
+ if (mount_outside_created)
+ rmdir(mount_outside);
+
+ if (mount_tmp_mounted)
+ umount(mount_tmp);
+ if (mount_tmp_created)
+ rmdir(mount_tmp);
+
+ if (mount_slave_mounted)
+ umount(mount_slave);
+ if (mount_slave_created)
+ rmdir(mount_slave);
+
+ return r;
+}
+
+static int machine_operation_done(sd_event_source *s, const siginfo_t *si, void *userdata) {
+ _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ MachineOperation *o = userdata;
+ int r;
+
+ assert(o);
+ assert(si);
+
+ o->pid = 0;
+
+ if (si->si_code != CLD_EXITED) {
+ r = sd_bus_error_setf(&error, SD_BUS_ERROR_FAILED, "Client died abnormally.");
+ goto fail;
+ }
+
+ if (si->si_status != EXIT_SUCCESS) {
+ if (read(o->errno_fd, &r, sizeof(r)) == sizeof(r))
+ r = sd_bus_error_set_errnof(&error, r, "%m");
+ else
+ r = sd_bus_error_setf(&error, SD_BUS_ERROR_FAILED, "Client failed.");
+
+ goto fail;
+ }
+
+ r = sd_bus_reply_method_return(o->message, NULL);
+ if (r < 0)
+ log_error_errno(r, "Failed to reply to message: %m");
+
+ machine_operation_unref(o);
+ return 0;
+
+fail:
+ r = sd_bus_reply_method_error(o->message, &error);
+ if (r < 0)
+ log_error_errno(r, "Failed to reply to message: %m");
+
+ machine_operation_unref(o);
+ return 0;
+}
+
+int bus_machine_method_copy(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ const char *src, *dest, *host_path, *container_path, *host_basename, *host_dirname, *container_basename, *container_dirname;
+ _cleanup_close_pair_ int errno_pipe_fd[2] = { -1, -1 };
+ _cleanup_close_ int hostfd = -1;
+ Machine *m = userdata;
+ MachineOperation *o;
+ bool copy_from;
+ pid_t child;
+ char *t;
+ int r;
+
+ assert(message);
+ assert(m);
+
+ if (m->n_operations >= MACHINE_OPERATIONS_MAX)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_LIMITS_EXCEEDED, "Too many ongoing copies.");
+
+ if (m->class != MACHINE_CONTAINER)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Copying files is only supported on container machines.");
+
+ r = sd_bus_message_read(message, "ss", &src, &dest);
+ if (r < 0)
+ return r;
+
+ if (!path_is_absolute(src) || !path_is_safe(src))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Source path must be absolute and not contain ../.");
+
+ if (isempty(dest))
+ dest = src;
+ else if (!path_is_absolute(dest) || !path_is_safe(dest))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Destination path must be absolute and not contain ../.");
+
+ r = bus_verify_polkit_async(
+ message,
+ CAP_SYS_ADMIN,
+ "org.freedesktop.machine1.manage-machines",
+ false,
+ UID_INVALID,
+ &m->manager->polkit_registry,
+ error);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return 1; /* Will call us back */
+
+ copy_from = strstr(sd_bus_message_get_member(message), "CopyFrom");
+
+ if (copy_from) {
+ container_path = src;
+ host_path = dest;
+ } else {
+ host_path = src;
+ container_path = dest;
+ }
+
+ host_basename = basename(host_path);
+ t = strdupa(host_path);
+ host_dirname = dirname(t);
+
+ container_basename = basename(container_path);
+ t = strdupa(container_path);
+ container_dirname = dirname(t);
+
+ hostfd = open(host_dirname, O_CLOEXEC|O_RDONLY|O_NOCTTY|O_DIRECTORY);
+ if (hostfd < 0)
+ return sd_bus_error_set_errnof(error, errno, "Failed to open host directory %s: %m", host_dirname);
+
+ if (pipe2(errno_pipe_fd, O_CLOEXEC|O_NONBLOCK) < 0)
+ return sd_bus_error_set_errnof(error, errno, "Failed to create pipe: %m");
+
+ child = fork();
+ if (child < 0)
+ return sd_bus_error_set_errnof(error, errno, "Failed to fork(): %m");
+
+ if (child == 0) {
+ int containerfd;
+ const char *q;
+ int mntfd;
+
+ errno_pipe_fd[0] = safe_close(errno_pipe_fd[0]);
+
+ q = procfs_file_alloca(m->leader, "ns/mnt");
+ mntfd = open(q, O_RDONLY|O_NOCTTY|O_CLOEXEC);
+ if (mntfd < 0) {
+ r = log_error_errno(errno, "Failed to open mount namespace of leader: %m");
+ goto child_fail;
+ }
+
+ if (setns(mntfd, CLONE_NEWNS) < 0) {
+ r = log_error_errno(errno, "Failed to join namespace of leader: %m");
+ goto child_fail;
+ }
+
+ containerfd = open(container_dirname, O_CLOEXEC|O_RDONLY|O_NOCTTY|O_DIRECTORY);
+ if (containerfd < 0) {
+ r = log_error_errno(errno, "Failed top open destination directory: %m");
+ goto child_fail;
+ }
+
+ if (copy_from)
+ r = copy_tree_at(containerfd, container_basename, hostfd, host_basename, true);
+ else
+ r = copy_tree_at(hostfd, host_basename, containerfd, container_basename, true);
+
+ hostfd = safe_close(hostfd);
+ containerfd = safe_close(containerfd);
+
+ if (r < 0) {
+ r = log_error_errno(r, "Failed to copy tree: %m");
+ goto child_fail;
+ }
+
+ _exit(EXIT_SUCCESS);
+
+ child_fail:
+ (void) write(errno_pipe_fd[1], &r, sizeof(r));
+ errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]);
+
+ _exit(EXIT_FAILURE);
+ }
+
+ errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]);
+
+ /* Copying might take a while, hence install a watch the
+ * child, and return */
+
+ o = new0(MachineOperation, 1);
+ if (!o)
+ return log_oom();
+
+ o->pid = child;
+ o->message = sd_bus_message_ref(message);
+ o->errno_fd = errno_pipe_fd[0];
+ errno_pipe_fd[0] = -1;
+
+ r = sd_event_add_child(m->manager->event, &o->event_source, child, WEXITED, machine_operation_done, o);
+ if (r < 0) {
+ machine_operation_unref(o);
+ return log_oom();
+ }
+
+ LIST_PREPEND(operations, m->operations, o);
+ m->n_operations++;
+ o->machine = m;
+
+ return 1;
}
const sd_bus_vtable machine_vtable[] = {
@@ -531,12 +976,15 @@ const sd_bus_vtable machine_vtable[] = {
SD_BUS_PROPERTY("RootDirectory", "s", NULL, offsetof(Machine, root_directory), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("NetworkInterfaces", "ai", property_get_netif, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("State", "s", property_get_state, 0, 0),
- SD_BUS_METHOD("Terminate", NULL, NULL, bus_machine_method_terminate, SD_BUS_VTABLE_CAPABILITY(CAP_KILL)),
- SD_BUS_METHOD("Kill", "si", NULL, bus_machine_method_kill, SD_BUS_VTABLE_CAPABILITY(CAP_KILL)),
+ SD_BUS_METHOD("Terminate", NULL, NULL, bus_machine_method_terminate, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("Kill", "si", NULL, bus_machine_method_kill, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("GetAddresses", NULL, "a(iay)", bus_machine_method_get_addresses, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("GetOSRelease", NULL, "a{ss}", bus_machine_method_get_os_release, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("OpenPTY", NULL, "hs", bus_machine_method_open_pty, 0),
SD_BUS_METHOD("OpenLogin", NULL, "hs", bus_machine_method_open_login, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("BindMount", "ssbb", NULL, bus_machine_method_bind_mount, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("CopyFrom", "ss", NULL, bus_machine_method_copy, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("CopyTo", "ss", NULL, bus_machine_method_copy, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_VTABLE_END
};
diff --git a/src/machine/machine-dbus.h b/src/machine/machine-dbus.h
index 601252722d..d309131860 100644
--- a/src/machine/machine-dbus.h
+++ b/src/machine/machine-dbus.h
@@ -22,7 +22,6 @@
***/
#include "sd-bus.h"
-#include "machine.h"
extern const sd_bus_vtable machine_vtable[];
@@ -30,12 +29,14 @@ char *machine_bus_path(Machine *s);
int machine_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error);
int machine_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error);
-int bus_machine_method_terminate(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error);
-int bus_machine_method_kill(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error);
-int bus_machine_method_get_addresses(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error);
-int bus_machine_method_get_os_release(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error);
-int bus_machine_method_open_pty(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error);
-int bus_machine_method_open_login(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_machine_method_terminate(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_machine_method_kill(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_machine_method_get_addresses(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_machine_method_get_os_release(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_machine_method_open_pty(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_machine_method_open_login(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_machine_method_bind_mount(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_machine_method_copy(sd_bus_message *message, void *userdata, sd_bus_error *error);
int machine_send_signal(Machine *m, bool new_machine);
int machine_send_create_reply(Machine *m, sd_bus_error *error);
diff --git a/src/machine/machine.c b/src/machine/machine.c
index 223eb0f364..05fc4f849f 100644
--- a/src/machine/machine.c
+++ b/src/machine/machine.c
@@ -28,7 +28,6 @@
#include "util.h"
#include "mkdir.h"
#include "hashmap.h"
-#include "strv.h"
#include "fileio.h"
#include "special.h"
#include "unit-name.h"
@@ -36,6 +35,7 @@
#include "bus-error.h"
#include "machine.h"
#include "machine-dbus.h"
+#include "formats-util.h"
Machine* machine_new(Manager *manager, const char *name) {
Machine *m;
@@ -74,20 +74,20 @@ fail:
void machine_free(Machine *m) {
assert(m);
+ while (m->operations)
+ machine_operation_unref(m->operations);
+
if (m->in_gc_queue)
LIST_REMOVE(gc_queue, m->manager->machine_gc_queue, m);
- if (m->unit) {
- hashmap_remove(m->manager->machine_units, m->unit);
- free(m->unit);
- }
+ machine_release_unit(m);
free(m->scope_job);
- hashmap_remove(m->manager->machines, m->name);
+ (void) hashmap_remove(m->manager->machines, m->name);
if (m->leader > 0)
- hashmap_remove_value(m->manager->machine_leaders, UINT_TO_PTR(m->leader), m);
+ (void) hashmap_remove_value(m->manager->machine_leaders, UINT_TO_PTR(m->leader), m);
sd_bus_message_unref(m->create_message);
@@ -210,9 +210,9 @@ int machine_save(Machine *m) {
/* Create a symlink from the unit name to the machine
* name, so that we can quickly find the machine for
- * each given unit */
+ * each given unit. Ignore error. */
sl = strjoina("/run/systemd/machines/unit:", m->unit);
- symlink(m->name, sl);
+ (void) symlink(m->name, sl);
}
finish:
@@ -501,6 +501,39 @@ int machine_kill(Machine *m, KillWho who, int signo) {
return manager_kill_unit(m->manager, m->unit, signo, NULL);
}
+MachineOperation *machine_operation_unref(MachineOperation *o) {
+ if (!o)
+ return NULL;
+
+ sd_event_source_unref(o->event_source);
+
+ safe_close(o->errno_fd);
+
+ if (o->pid > 1)
+ (void) kill(o->pid, SIGKILL);
+
+ sd_bus_message_unref(o->message);
+
+ if (o->machine) {
+ LIST_REMOVE(operations, o->machine->operations, o);
+ o->machine->n_operations--;
+ }
+
+ free(o);
+ return NULL;
+}
+
+void machine_release_unit(Machine *m) {
+ assert(m);
+
+ if (!m->unit)
+ return;
+
+ (void) hashmap_remove(m->manager->machine_units, m->unit);
+ free(m->unit);
+ m->unit = NULL;
+}
+
static const char* const machine_class_table[_MACHINE_CLASS_MAX] = {
[MACHINE_CONTAINER] = "container",
[MACHINE_VM] = "vm"
diff --git a/src/machine/machine.h b/src/machine/machine.h
index 4827ba332f..bbe5217f65 100644
--- a/src/machine/machine.h
+++ b/src/machine/machine.h
@@ -22,10 +22,10 @@
***/
typedef struct Machine Machine;
+typedef struct MachineOperation MachineOperation;
typedef enum KillWho KillWho;
#include "list.h"
-#include "util.h"
#include "machined.h"
typedef enum MachineState {
@@ -50,6 +50,17 @@ enum KillWho {
_KILL_WHO_INVALID = -1
};
+#define MACHINE_OPERATIONS_MAX 64
+
+struct MachineOperation {
+ Machine *machine;
+ pid_t pid;
+ sd_bus_message *message;
+ int errno_fd;
+ sd_event_source *event_source;
+ LIST_FIELDS(MachineOperation, operations);
+};
+
struct Machine {
Manager *manager;
@@ -79,6 +90,9 @@ struct Machine {
unsigned n_netif;
LIST_FIELDS(Machine, gc_queue);
+
+ MachineOperation *operations;
+ unsigned n_operations;
};
Machine* machine_new(Manager *manager, const char *name);
@@ -91,8 +105,12 @@ int machine_save(Machine *m);
int machine_load(Machine *m);
int machine_kill(Machine *m, KillWho who, int signo);
+void machine_release_unit(Machine *m);
+
MachineState machine_get_state(Machine *u);
+MachineOperation *machine_operation_unref(MachineOperation *o);
+
const char* machine_class_to_string(MachineClass t) _const_;
MachineClass machine_class_from_string(const char *s) _pure_;
diff --git a/src/machine/machinectl.c b/src/machine/machinectl.c
index 9f8c68b184..c86c36c2de 100644
--- a/src/machine/machinectl.c
+++ b/src/machine/machinectl.c
@@ -24,7 +24,6 @@
#include <errno.h>
#include <string.h>
#include <getopt.h>
-#include <pwd.h>
#include <locale.h>
#include <fcntl.h>
#include <netinet/in.h>
@@ -32,12 +31,6 @@
#include <net/if.h>
#include <sys/mount.h>
-/* When we include libgen.h because we need dirname() we immediately
- * undefine basename() since libgen.h defines it as a macro to the XDG
- * version which is really broken. */
-#include <libgen.h>
-#undef basename
-
#include "sd-bus.h"
#include "log.h"
#include "util.h"
@@ -59,6 +52,9 @@
#include "copy.h"
#include "verbs.h"
#include "import-util.h"
+#include "process-util.h"
+#include "terminal-util.h"
+#include "signal-util.h"
static char **arg_property = NULL;
static bool arg_all = false;
@@ -78,6 +74,7 @@ static OutputMode arg_output = OUTPUT_SHORT;
static bool arg_force = false;
static ImportVerify arg_verify = IMPORT_VERIFY_SIGNATURE;
static const char* arg_dkr_index_url = NULL;
+static const char* arg_format = NULL;
static void pager_open_if_enabled(void) {
@@ -810,7 +807,7 @@ static void print_image_status_info(sd_bus *bus, ImageStatusInfo *i) {
printf("\t Limit: %s\n", s3);
}
-static int show_image_info(const char *verb, sd_bus *bus, const char *path, bool *new_line) {
+static int show_image_info(sd_bus *bus, const char *path, bool *new_line) {
static const struct bus_properties_map map[] = {
{ "Name", "s", NULL, offsetof(ImageStatusInfo, name) },
@@ -829,7 +826,6 @@ static int show_image_info(const char *verb, sd_bus *bus, const char *path, bool
ImageStatusInfo info = {};
int r;
- assert(verb);
assert(bus);
assert(path);
assert(new_line);
@@ -855,6 +851,59 @@ static int show_image_info(const char *verb, sd_bus *bus, const char *path, bool
return r;
}
+typedef struct PoolStatusInfo {
+ char *path;
+ uint64_t usage;
+ uint64_t limit;
+} PoolStatusInfo;
+
+static void print_pool_status_info(sd_bus *bus, PoolStatusInfo *i) {
+ char bs[FORMAT_BYTES_MAX], *s;
+
+ if (i->path)
+ printf("\t Path: %s\n", i->path);
+
+ s = format_bytes(bs, sizeof(bs), i->usage);
+ if (s)
+ printf("\t Usage: %s\n", s);
+
+ s = format_bytes(bs, sizeof(bs), i->limit);
+ if (s)
+ printf("\t Limit: %s\n", s);
+}
+
+static int show_pool_info(sd_bus *bus) {
+
+ static const struct bus_properties_map map[] = {
+ { "PoolPath", "s", NULL, offsetof(PoolStatusInfo, path) },
+ { "PoolUsage", "t", NULL, offsetof(PoolStatusInfo, usage) },
+ { "PoolLimit", "t", NULL, offsetof(PoolStatusInfo, limit) },
+ {}
+ };
+
+ PoolStatusInfo info = {
+ .usage = (uint64_t) -1,
+ .limit = (uint64_t) -1,
+ };
+ int r;
+
+ assert(bus);
+
+ r = bus_map_all_properties(bus,
+ "org.freedesktop.machine1",
+ "/org/freedesktop/machine1",
+ map,
+ &info);
+ if (r < 0)
+ return log_error_errno(r, "Could not get properties: %m");
+
+ print_pool_status_info(bus, &info);
+
+ free(info.path);
+ return 0;
+}
+
+
static int show_image_properties(sd_bus *bus, const char *path, bool *new_line) {
int r;
@@ -888,11 +937,15 @@ static int show_image(int argc, char *argv[], void *userdata) {
pager_open_if_enabled();
- if (properties && argc <= 1) {
+ if (argc <= 1) {
/* If no argument is specified, inspect the manager
* itself */
- r = show_image_properties(bus, "/org/freedesktop/machine1", &new_line);
+
+ if (properties)
+ r = show_image_properties(bus, "/org/freedesktop/machine1", &new_line);
+ else
+ r = show_pool_info(bus);
if (r < 0)
return r;
}
@@ -901,14 +954,14 @@ static int show_image(int argc, char *argv[], void *userdata) {
const char *path = NULL;
r = sd_bus_call_method(
- bus,
- "org.freedesktop.machine1",
- "/org/freedesktop/machine1",
- "org.freedesktop.machine1.Manager",
- "GetImage",
- &error,
- &reply,
- "s", argv[i]);
+ bus,
+ "org.freedesktop.machine1",
+ "/org/freedesktop/machine1",
+ "org.freedesktop.machine1.Manager",
+ "GetImage",
+ &error,
+ &reply,
+ "s", argv[i]);
if (r < 0) {
log_error("Could not get path to image: %s", bus_error_message(&error, -r));
return r;
@@ -921,7 +974,7 @@ static int show_image(int argc, char *argv[], void *userdata) {
if (properties)
r = show_image_properties(bus, path, &new_line);
else
- r = show_image_info(argv[0], bus, path, &new_line);
+ r = show_image_info(bus, path, &new_line);
}
return r;
@@ -930,7 +983,7 @@ static int show_image(int argc, char *argv[], void *userdata) {
static int kill_machine(int argc, char *argv[], void *userdata) {
_cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
sd_bus *bus = userdata;
- int i;
+ int r, i;
assert(bus);
@@ -940,8 +993,6 @@ static int kill_machine(int argc, char *argv[], void *userdata) {
arg_kill_who = "all";
for (i = 1; i < argc; i++) {
- int r;
-
r = sd_bus_call_method(
bus,
"org.freedesktop.machine1",
@@ -977,15 +1028,13 @@ static int poweroff_machine(int argc, char *argv[], void *userdata) {
static int terminate_machine(int argc, char *argv[], void *userdata) {
_cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
sd_bus *bus = userdata;
- int i;
+ int r, i;
assert(bus);
polkit_agent_open_if_enabled();
for (i = 1; i < argc; i++) {
- int r;
-
r = sd_bus_call_method(
bus,
"org.freedesktop.machine1",
@@ -1004,333 +1053,73 @@ static int terminate_machine(int argc, char *argv[], void *userdata) {
return 0;
}
-static int machine_get_leader(sd_bus *bus, const char *name, pid_t *ret) {
+static int copy_files(int argc, char *argv[], void *userdata) {
_cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL, *reply2 = NULL;
- const char *object;
- uint32_t leader;
+ sd_bus *bus = userdata;
+ bool copy_from;
int r;
assert(bus);
- assert(name);
- assert(ret);
+
+ polkit_agent_open_if_enabled();
+
+ copy_from = streq(argv[0], "copy-from");
r = sd_bus_call_method(
bus,
"org.freedesktop.machine1",
"/org/freedesktop/machine1",
"org.freedesktop.machine1.Manager",
- "GetMachine",
+ copy_from ? "CopyFromMachine" : "CopyToMachine",
&error,
- &reply,
- "s", name);
+ NULL,
+ "sss",
+ argv[1],
+ argv[2],
+ argv[3]);
if (r < 0) {
- log_error("Could not get path to machine: %s", bus_error_message(&error, -r));
- return r;
- }
-
- r = sd_bus_message_read(reply, "o", &object);
- if (r < 0)
- return bus_log_parse_error(r);
-
- r = sd_bus_get_property(
- bus,
- "org.freedesktop.machine1",
- object,
- "org.freedesktop.machine1.Machine",
- "Leader",
- &error,
- &reply2,
- "u");
- if (r < 0)
- return log_error_errno(r, "Failed to retrieve PID of leader: %m");
-
- r = sd_bus_message_read(reply2, "u", &leader);
- if (r < 0)
- return bus_log_parse_error(r);
-
- *ret = leader;
- return 0;
-}
-
-static int copy_files(int argc, char *argv[], void *userdata) {
- char *dest, *host_path, *container_path, *host_dirname, *host_basename, *container_dirname, *container_basename, *t;
- _cleanup_close_ int hostfd = -1;
- sd_bus *bus = userdata;
- pid_t child, leader;
- bool copy_from;
- siginfo_t si;
- int r;
-
- assert(bus);
-
- copy_from = streq(argv[0], "copy-from");
- dest = argv[3] ?: argv[2];
- host_path = strdupa(copy_from ? dest : argv[2]);
- container_path = strdupa(copy_from ? argv[2] : dest);
-
- if (!path_is_absolute(container_path)) {
- log_error("Container path not absolute.");
- return -EINVAL;
- }
-
- t = strdupa(host_path);
- host_basename = basename(t);
- host_dirname = dirname(host_path);
-
- t = strdupa(container_path);
- container_basename = basename(t);
- container_dirname = dirname(container_path);
-
- r = machine_get_leader(bus, argv[1], &leader);
- if (r < 0)
+ log_error("Failed to copy: %s", bus_error_message(&error, -r));
return r;
-
- hostfd = open(host_dirname, O_CLOEXEC|O_RDONLY|O_NOCTTY|O_DIRECTORY);
- if (r < 0)
- return log_error_errno(errno, "Failed to open source directory: %m");
-
- child = fork();
- if (child < 0)
- return log_error_errno(errno, "Failed to fork(): %m");
-
- if (child == 0) {
- int containerfd;
- const char *q;
- int mntfd;
-
- q = procfs_file_alloca(leader, "ns/mnt");
- mntfd = open(q, O_RDONLY|O_NOCTTY|O_CLOEXEC);
- if (mntfd < 0) {
- log_error_errno(errno, "Failed to open mount namespace of leader: %m");
- _exit(EXIT_FAILURE);
- }
-
- if (setns(mntfd, CLONE_NEWNS) < 0) {
- log_error_errno(errno, "Failed to join namespace of leader: %m");
- _exit(EXIT_FAILURE);
- }
-
- containerfd = open(container_dirname, O_CLOEXEC|O_RDONLY|O_NOCTTY|O_DIRECTORY);
- if (containerfd < 0) {
- log_error_errno(errno, "Failed top open destination directory: %m");
- _exit(EXIT_FAILURE);
- }
-
- if (copy_from)
- r = copy_tree_at(containerfd, container_basename, hostfd, host_basename, true);
- else
- r = copy_tree_at(hostfd, host_basename, containerfd, container_basename, true);
- if (r < 0) {
- log_error_errno(errno, "Failed to copy tree: %m");
- _exit(EXIT_FAILURE);
- }
-
- _exit(EXIT_SUCCESS);
- }
-
- r = wait_for_terminate(child, &si);
- if (r < 0)
- return log_error_errno(r, "Failed to wait for client: %m");
- if (si.si_code != CLD_EXITED) {
- log_error("Client died abnormally.");
- return -EIO;
}
- if (si.si_status != EXIT_SUCCESS)
- return -EIO;
return 0;
}
static int bind_mount(int argc, char *argv[], void *userdata) {
- char mount_slave[] = "/tmp/propagate.XXXXXX", *mount_tmp, *mount_outside, *p;
+ _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
sd_bus *bus = userdata;
- pid_t child, leader;
- const char *dest;
- siginfo_t si;
- bool mount_slave_created = false, mount_slave_mounted = false,
- mount_tmp_created = false, mount_tmp_mounted = false,
- mount_outside_created = false, mount_outside_mounted = false;
int r;
assert(bus);
- /* One day, when bind mounting /proc/self/fd/n works across
- * namespace boundaries we should rework this logic to make
- * use of it... */
-
- dest = argv[3] ?: argv[2];
- if (!path_is_absolute(dest)) {
- log_error("Destination path not absolute.");
- return -EINVAL;
- }
-
- p = strjoina("/run/systemd/nspawn/propagate/", argv[1], "/");
- if (access(p, F_OK) < 0) {
- log_error("Container does not allow propagation of mount points.");
- return -ENOTSUP;
- }
-
- r = machine_get_leader(bus, argv[1], &leader);
- if (r < 0)
- return r;
-
- /* Our goal is to install a new bind mount into the container,
- possibly read-only. This is irritatingly complex
- unfortunately, currently.
-
- First, we start by creating a private playground in /tmp,
- that we can mount MS_SLAVE. (Which is necessary, since
- MS_MOUNT cannot be applied to mounts with MS_SHARED parent
- mounts.) */
-
- if (!mkdtemp(mount_slave))
- return log_error_errno(errno, "Failed to create playground: %m");
-
- mount_slave_created = true;
-
- if (mount(mount_slave, mount_slave, NULL, MS_BIND, NULL) < 0) {
- r = log_error_errno(errno, "Failed to make bind mount: %m");
- goto finish;
- }
-
- mount_slave_mounted = true;
-
- if (mount(NULL, mount_slave, NULL, MS_SLAVE, NULL) < 0) {
- r = log_error_errno(errno, "Failed to remount slave: %m");
- goto finish;
- }
-
- /* Second, we mount the source directory to a directory inside
- of our MS_SLAVE playground. */
- mount_tmp = strjoina(mount_slave, "/mount");
- if (mkdir(mount_tmp, 0700) < 0) {
- r = log_error_errno(errno, "Failed to create temporary mount: %m");
- goto finish;
- }
-
- mount_tmp_created = true;
-
- if (mount(argv[2], mount_tmp, NULL, MS_BIND, NULL) < 0) {
- r = log_error_errno(errno, "Failed to overmount: %m");
- goto finish;
- }
-
- mount_tmp_mounted = true;
-
- /* Third, we remount the new bind mount read-only if requested. */
- if (arg_read_only)
- if (mount(NULL, mount_tmp, NULL, MS_BIND|MS_REMOUNT|MS_RDONLY, NULL) < 0) {
- r = log_error_errno(errno, "Failed to mark read-only: %m");
- goto finish;
- }
-
- /* Fourth, we move the new bind mount into the propagation
- * directory. This way it will appear there read-only
- * right-away. */
-
- mount_outside = strjoina("/run/systemd/nspawn/propagate/", argv[1], "/XXXXXX");
- if (!mkdtemp(mount_outside)) {
- r = log_error_errno(errno, "Cannot create propagation directory: %m");
- goto finish;
- }
-
- mount_outside_created = true;
-
- if (mount(mount_tmp, mount_outside, NULL, MS_MOVE, NULL) < 0) {
- r = log_error_errno(errno, "Failed to move: %m");
- goto finish;
- }
-
- mount_outside_mounted = true;
- mount_tmp_mounted = false;
-
- (void) rmdir(mount_tmp);
- mount_tmp_created = false;
-
- (void) umount(mount_slave);
- mount_slave_mounted = false;
-
- (void) rmdir(mount_slave);
- mount_slave_created = false;
-
- child = fork();
- if (child < 0) {
- r = log_error_errno(errno, "Failed to fork(): %m");
- goto finish;
- }
-
- if (child == 0) {
- const char *mount_inside;
- int mntfd;
- const char *q;
-
- q = procfs_file_alloca(leader, "ns/mnt");
- mntfd = open(q, O_RDONLY|O_NOCTTY|O_CLOEXEC);
- if (mntfd < 0) {
- log_error_errno(errno, "Failed to open mount namespace of leader: %m");
- _exit(EXIT_FAILURE);
- }
-
- if (setns(mntfd, CLONE_NEWNS) < 0) {
- log_error_errno(errno, "Failed to join namespace of leader: %m");
- _exit(EXIT_FAILURE);
- }
-
- if (arg_mkdir)
- mkdir_p(dest, 0755);
-
- /* Fifth, move the mount to the right place inside */
- mount_inside = strjoina("/run/systemd/nspawn/incoming/", basename(mount_outside));
- if (mount(mount_inside, dest, NULL, MS_MOVE, NULL) < 0) {
- log_error_errno(errno, "Failed to mount: %m");
- _exit(EXIT_FAILURE);
- }
-
- _exit(EXIT_SUCCESS);
- }
+ polkit_agent_open_if_enabled();
- r = wait_for_terminate(child, &si);
+ r = sd_bus_call_method(
+ bus,
+ "org.freedesktop.machine1",
+ "/org/freedesktop/machine1",
+ "org.freedesktop.machine1.Manager",
+ "BindMountMachine",
+ &error,
+ NULL,
+ "sssbb",
+ argv[1],
+ argv[2],
+ argv[3],
+ arg_read_only,
+ arg_mkdir);
if (r < 0) {
- log_error_errno(r, "Failed to wait for client: %m");
- goto finish;
- }
- if (si.si_code != CLD_EXITED) {
- log_error("Client died abnormally.");
- r = -EIO;
- goto finish;
- }
- if (si.si_status != EXIT_SUCCESS) {
- r = -EIO;
- goto finish;
+ log_error("Failed to bind mount: %s", bus_error_message(&error, -r));
+ return r;
}
- r = 0;
-
-finish:
- if (mount_outside_mounted)
- umount(mount_outside);
- if (mount_outside_created)
- rmdir(mount_outside);
-
- if (mount_tmp_mounted)
- umount(mount_tmp);
- if (mount_tmp_created)
- umount(mount_tmp);
-
- if (mount_slave_mounted)
- umount(mount_slave);
- if (mount_slave_created)
- umount(mount_slave);
-
- return r;
+ return 0;
}
-static int on_machine_removed(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
+static int on_machine_removed(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
PTYForward ** forward = (PTYForward**) userdata;
int r;
- assert(bus);
assert(m);
assert(forward);
@@ -1347,13 +1136,13 @@ static int on_machine_removed(sd_bus *bus, sd_bus_message *m, void *userdata, sd
}
/* On error, or when the forwarder is not initialized yet, quit immediately */
- sd_event_exit(sd_bus_get_event(bus), EXIT_FAILURE);
+ sd_event_exit(sd_bus_get_event(sd_bus_message_get_bus(m)), EXIT_FAILURE);
return 0;
}
static int login_machine(int argc, char *argv[], void *userdata) {
_cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
+ _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
_cleanup_bus_slot_unref_ sd_bus_slot *slot = NULL;
_cleanup_(pty_forward_freep) PTYForward *forward = NULL;
_cleanup_event_unref_ sd_event *event = NULL;
@@ -1368,7 +1157,7 @@ static int login_machine(int argc, char *argv[], void *userdata) {
if (arg_transport != BUS_TRANSPORT_LOCAL &&
arg_transport != BUS_TRANSPORT_MACHINE) {
log_error("Login only supported on local machines.");
- return -ENOTSUP;
+ return -EOPNOTSUPP;
}
polkit_agent_open_if_enabled();
@@ -1394,24 +1183,15 @@ static int login_machine(int argc, char *argv[], void *userdata) {
if (r < 0)
return log_error_errno(r, "Failed to add machine removal match: %m");
- r = sd_bus_message_new_method_call(bus,
- &m,
- "org.freedesktop.machine1",
- "/org/freedesktop/machine1",
- "org.freedesktop.machine1.Manager",
- "OpenMachineLogin");
- if (r < 0)
- return bus_log_create_error(r);
-
- r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
- if (r < 0)
- return bus_log_create_error(r);
-
- r = sd_bus_message_append(m, "s", argv[1]);
- if (r < 0)
- return bus_log_create_error(r);
-
- r = sd_bus_call(bus, m, 0, &error, &reply);
+ r = sd_bus_call_method(
+ bus,
+ "org.freedesktop.machine1",
+ "/org/freedesktop/machine1",
+ "org.freedesktop.machine1.Manager",
+ "OpenMachineLogin",
+ &error,
+ &reply,
+ "s", argv[1]);
if (r < 0) {
log_error("Failed to get machine PTY: %s", bus_error_message(&error, -r));
return r;
@@ -1428,7 +1208,7 @@ static int login_machine(int argc, char *argv[], void *userdata) {
sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
- r = pty_forward_new(event, master, true, &forward);
+ r = pty_forward_new(event, master, true, false, &forward);
if (r < 0)
return log_error_errno(r, "Failed to create PTY forwarder: %m");
@@ -1561,6 +1341,29 @@ static int read_only_image(int argc, char *argv[], void *userdata) {
return 0;
}
+static int make_service_name(const char *name, char **ret) {
+ _cleanup_free_ char *e = NULL;
+ int r;
+
+ assert(name);
+ assert(ret);
+
+ if (!machine_name_is_valid(name)) {
+ log_error("Invalid machine name %s.", name);
+ return -EINVAL;
+ }
+
+ e = unit_name_escape(name);
+ if (!e)
+ return log_oom();
+
+ r = unit_name_build("systemd-nspawn", e, ".service", ret);
+ if (r < 0)
+ return log_error_errno(r, "Failed to build unit name: %m");
+
+ return 0;
+}
+
static int start_machine(int argc, char *argv[], void *userdata) {
_cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL;
@@ -1576,42 +1379,23 @@ static int start_machine(int argc, char *argv[], void *userdata) {
return log_oom();
for (i = 1; i < argc; i++) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
- _cleanup_free_ char *e = NULL, *unit = NULL;
+ _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+ _cleanup_free_ char *unit = NULL;
const char *object;
- if (!machine_name_is_valid(argv[i])) {
- log_error("Invalid machine name %s.", argv[i]);
- return -EINVAL;
- }
-
- e = unit_name_escape(argv[i]);
- if (!e)
- return log_oom();
-
- unit = unit_name_build("systemd-nspawn", e, ".service");
- if (!unit)
- return log_oom();
+ r = make_service_name(argv[i], &unit);
+ if (r < 0)
+ return r;
- r = sd_bus_message_new_method_call(
+ r = sd_bus_call_method(
bus,
- &m,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
- "StartUnit");
- if (r < 0)
- return bus_log_create_error(r);
-
- r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
- if (r < 0)
- return bus_log_create_error(r);
-
- r = sd_bus_message_append(m, "ss", unit, "fail");
- if (r < 0)
- return bus_log_create_error(r);
-
- r = sd_bus_call(bus, m, 0, &error, &reply);
+ "StartUnit",
+ &error,
+ &reply,
+ "ss", unit, "fail");
if (r < 0) {
log_error("Failed to start unit: %s", bus_error_message(&error, -r));
return r;
@@ -1657,29 +1441,16 @@ static int enable_machine(int argc, char *argv[], void *userdata) {
if (r < 0)
return bus_log_create_error(r);
- r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
- if (r < 0)
- return bus_log_create_error(r);
-
r = sd_bus_message_open_container(m, 'a', "s");
if (r < 0)
return bus_log_create_error(r);
for (i = 1; i < argc; i++) {
- _cleanup_free_ char *e = NULL, *unit = NULL;
-
- if (!machine_name_is_valid(argv[i])) {
- log_error("Invalid machine name %s.", argv[i]);
- return -EINVAL;
- }
-
- e = unit_name_escape(argv[i]);
- if (!e)
- return log_oom();
+ _cleanup_free_ char *unit = NULL;
- unit = unit_name_build("systemd-nspawn", e, ".service");
- if (!unit)
- return log_oom();
+ r = make_service_name(argv[i], &unit);
+ if (r < 0)
+ return r;
r = sd_bus_message_append(m, "s", unit);
if (r < 0)
@@ -1709,27 +1480,19 @@ static int enable_machine(int argc, char *argv[], void *userdata) {
return bus_log_parse_error(r);
}
- r = bus_deserialize_and_dump_unit_file_changes(reply, arg_quiet);
+ r = bus_deserialize_and_dump_unit_file_changes(reply, arg_quiet, NULL, NULL);
if (r < 0)
return r;
- m = sd_bus_message_unref(m);
-
- r = sd_bus_message_new_method_call(
+ r = sd_bus_call_method(
bus,
- &m,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
- "Reload");
- if (r < 0)
- return bus_log_create_error(r);
-
- r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
- if (r < 0)
- return bus_log_create_error(r);
-
- r = sd_bus_call(bus, m, 0, &error, NULL);
+ "Reload",
+ &error,
+ NULL,
+ NULL);
if (r < 0) {
log_error("Failed to reload daemon: %s", bus_error_message(&error, -r));
return r;
@@ -1738,12 +1501,11 @@ static int enable_machine(int argc, char *argv[], void *userdata) {
return 0;
}
-static int match_log_message(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
+static int match_log_message(sd_bus_message *m, void *userdata, sd_bus_error *error) {
const char **our_path = userdata, *line;
unsigned priority;
int r;
- assert(bus);
assert(m);
assert(our_path);
@@ -1763,12 +1525,11 @@ static int match_log_message(sd_bus *bus, sd_bus_message *m, void *userdata, sd_
return 0;
}
-static int match_transfer_removed(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
+static int match_transfer_removed(sd_bus_message *m, void *userdata, sd_bus_error *error) {
const char **our_path = userdata, *path, *result;
uint32_t id;
int r;
- assert(bus);
assert(m);
assert(our_path);
@@ -1781,7 +1542,7 @@ static int match_transfer_removed(sd_bus *bus, sd_bus_message *m, void *userdata
if (!streq_ptr(*our_path, path))
return 0;
- sd_event_exit(sd_bus_get_event(bus), !streq_ptr(result, "done"));
+ sd_event_exit(sd_bus_get_event(sd_bus_message_get_bus(m)), !streq_ptr(result, "done"));
return 0;
}
@@ -1796,7 +1557,7 @@ static int transfer_signal_handler(sd_event_source *s, const struct signalfd_sig
return 0;
}
-static int pull_image_common(sd_bus *bus, sd_bus_message *m) {
+static int transfer_image_common(sd_bus *bus, sd_bus_message *m) {
_cleanup_bus_slot_unref_ sd_bus_slot *slot_job_removed = NULL, *slot_log_message = NULL;
_cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
@@ -1818,10 +1579,6 @@ static int pull_image_common(sd_bus *bus, sd_bus_message *m) {
if (r < 0)
return log_error_errno(r, "Failed to attach bus to event loop: %m");
- r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
- if (r < 0)
- return bus_log_create_error(r);
-
r = sd_bus_add_match(
bus,
&slot_job_removed,
@@ -1847,7 +1604,7 @@ static int pull_image_common(sd_bus *bus, sd_bus_message *m) {
r = sd_bus_call(bus, m, 0, &error, &reply);
if (r < 0) {
- log_error("Failed pull image: %s", bus_error_message(&error, -r));
+ log_error("Failed transfer image: %s", bus_error_message(&error, -r));
return r;
}
@@ -1870,6 +1627,255 @@ static int pull_image_common(sd_bus *bus, sd_bus_message *m) {
return -r;
}
+static int import_tar(int argc, char *argv[], void *userdata) {
+ _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ _cleanup_free_ char *ll = NULL;
+ _cleanup_close_ int fd = -1;
+ const char *local = NULL, *path = NULL;
+ sd_bus *bus = userdata;
+ int r;
+
+ assert(bus);
+
+ if (argc >= 2)
+ path = argv[1];
+ if (isempty(path) || streq(path, "-"))
+ path = NULL;
+
+ if (argc >= 3)
+ local = argv[2];
+ else if (path)
+ local = basename(path);
+ if (isempty(local) || streq(local, "-"))
+ local = NULL;
+
+ if (!local) {
+ log_error("Need either path or local name.");
+ return -EINVAL;
+ }
+
+ r = tar_strip_suffixes(local, &ll);
+ if (r < 0)
+ return log_oom();
+
+ local = ll;
+
+ if (!machine_name_is_valid(local)) {
+ log_error("Local name %s is not a suitable machine name.", local);
+ return -EINVAL;
+ }
+
+ if (path) {
+ fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY);
+ if (fd < 0)
+ return log_error_errno(errno, "Failed to open %s: %m", path);
+ }
+
+ r = sd_bus_message_new_method_call(
+ bus,
+ &m,
+ "org.freedesktop.import1",
+ "/org/freedesktop/import1",
+ "org.freedesktop.import1.Manager",
+ "ImportTar");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_append(
+ m,
+ "hsbb",
+ fd >= 0 ? fd : STDIN_FILENO,
+ local,
+ arg_force,
+ arg_read_only);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ return transfer_image_common(bus, m);
+}
+
+static int import_raw(int argc, char *argv[], void *userdata) {
+ _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ _cleanup_free_ char *ll = NULL;
+ _cleanup_close_ int fd = -1;
+ const char *local = NULL, *path = NULL;
+ sd_bus *bus = userdata;
+ int r;
+
+ assert(bus);
+
+ if (argc >= 2)
+ path = argv[1];
+ if (isempty(path) || streq(path, "-"))
+ path = NULL;
+
+ if (argc >= 3)
+ local = argv[2];
+ else if (path)
+ local = basename(path);
+ if (isempty(local) || streq(local, "-"))
+ local = NULL;
+
+ if (!local) {
+ log_error("Need either path or local name.");
+ return -EINVAL;
+ }
+
+ r = raw_strip_suffixes(local, &ll);
+ if (r < 0)
+ return log_oom();
+
+ local = ll;
+
+ if (!machine_name_is_valid(local)) {
+ log_error("Local name %s is not a suitable machine name.", local);
+ return -EINVAL;
+ }
+
+ if (path) {
+ fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY);
+ if (fd < 0)
+ return log_error_errno(errno, "Failed to open %s: %m", path);
+ }
+
+ r = sd_bus_message_new_method_call(
+ bus,
+ &m,
+ "org.freedesktop.import1",
+ "/org/freedesktop/import1",
+ "org.freedesktop.import1.Manager",
+ "ImportRaw");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_append(
+ m,
+ "hsbb",
+ fd >= 0 ? fd : STDIN_FILENO,
+ local,
+ arg_force,
+ arg_read_only);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ return transfer_image_common(bus, m);
+}
+
+static void determine_compression_from_filename(const char *p) {
+ if (arg_format)
+ return;
+
+ if (!p)
+ return;
+
+ if (endswith(p, ".xz"))
+ arg_format = "xz";
+ else if (endswith(p, ".gz"))
+ arg_format = "gzip";
+ else if (endswith(p, ".bz2"))
+ arg_format = "bzip2";
+}
+
+static int export_tar(int argc, char *argv[], void *userdata) {
+ _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ _cleanup_close_ int fd = -1;
+ const char *local = NULL, *path = NULL;
+ sd_bus *bus = userdata;
+ int r;
+
+ assert(bus);
+
+ local = argv[1];
+ if (!machine_name_is_valid(local)) {
+ log_error("Machine name %s is not valid.", local);
+ return -EINVAL;
+ }
+
+ if (argc >= 3)
+ path = argv[2];
+ if (isempty(path) || streq(path, "-"))
+ path = NULL;
+
+ if (path) {
+ determine_compression_from_filename(path);
+
+ fd = open(path, O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC|O_NOCTTY, 0666);
+ if (fd < 0)
+ return log_error_errno(errno, "Failed to open %s: %m", path);
+ }
+
+ r = sd_bus_message_new_method_call(
+ bus,
+ &m,
+ "org.freedesktop.import1",
+ "/org/freedesktop/import1",
+ "org.freedesktop.import1.Manager",
+ "ExportTar");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_append(
+ m,
+ "shs",
+ local,
+ fd >= 0 ? fd : STDOUT_FILENO,
+ arg_format);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ return transfer_image_common(bus, m);
+}
+
+static int export_raw(int argc, char *argv[], void *userdata) {
+ _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ _cleanup_close_ int fd = -1;
+ const char *local = NULL, *path = NULL;
+ sd_bus *bus = userdata;
+ int r;
+
+ assert(bus);
+
+ local = argv[1];
+ if (!machine_name_is_valid(local)) {
+ log_error("Machine name %s is not valid.", local);
+ return -EINVAL;
+ }
+
+ if (argc >= 3)
+ path = argv[2];
+ if (isempty(path) || streq(path, "-"))
+ path = NULL;
+
+ if (path) {
+ determine_compression_from_filename(path);
+
+ fd = open(path, O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC|O_NOCTTY, 0666);
+ if (fd < 0)
+ return log_error_errno(errno, "Failed to open %s: %m", path);
+ }
+
+ r = sd_bus_message_new_method_call(
+ bus,
+ &m,
+ "org.freedesktop.import1",
+ "/org/freedesktop/import1",
+ "org.freedesktop.import1.Manager",
+ "ExportRaw");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_append(
+ m,
+ "shs",
+ local,
+ fd >= 0 ? fd : STDOUT_FILENO,
+ arg_format);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ return transfer_image_common(bus, m);
+}
+
static int pull_tar(int argc, char *argv[], void *userdata) {
_cleanup_bus_message_unref_ sd_bus_message *m = NULL;
_cleanup_free_ char *l = NULL, *ll = NULL;
@@ -1901,7 +1907,7 @@ static int pull_tar(int argc, char *argv[], void *userdata) {
if (local) {
r = tar_strip_suffixes(local, &ll);
if (r < 0)
- return log_error_errno(r, "Failed to strip tar suffixes: %m");
+ return log_oom();
local = ll;
@@ -1931,7 +1937,7 @@ static int pull_tar(int argc, char *argv[], void *userdata) {
if (r < 0)
return bus_log_create_error(r);
- return pull_image_common(bus, m);
+ return transfer_image_common(bus, m);
}
static int pull_raw(int argc, char *argv[], void *userdata) {
@@ -1965,7 +1971,7 @@ static int pull_raw(int argc, char *argv[], void *userdata) {
if (local) {
r = raw_strip_suffixes(local, &ll);
if (r < 0)
- return log_error_errno(r, "Failed to strip tar suffixes: %m");
+ return log_oom();
local = ll;
@@ -1995,7 +2001,7 @@ static int pull_raw(int argc, char *argv[], void *userdata) {
if (r < 0)
return bus_log_create_error(r);
- return pull_image_common(bus, m);
+ return transfer_image_common(bus, m);
}
static int pull_dkr(int argc, char *argv[], void *userdata) {
@@ -2067,7 +2073,7 @@ static int pull_dkr(int argc, char *argv[], void *userdata) {
if (r < 0)
return bus_log_create_error(r);
- return pull_image_common(bus, m);
+ return transfer_image_common(bus, m);
}
typedef struct TransferInfo {
@@ -2210,6 +2216,56 @@ static int cancel_transfer(int argc, char *argv[], void *userdata) {
return 0;
}
+static int set_limit(int argc, char *argv[], void *userdata) {
+ _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ sd_bus *bus = userdata;
+ uint64_t limit;
+ int r;
+
+ if (streq(argv[argc-1], "-"))
+ limit = (uint64_t) -1;
+ else {
+ off_t off;
+
+ r = parse_size(argv[argc-1], 1024, &off);
+ if (r < 0)
+ return log_error("Failed to parse size: %s", argv[argc-1]);
+
+ limit = (uint64_t) off;
+ }
+
+ if (argc > 2)
+ /* With two arguments changes the quota limit of the
+ * specified image */
+ r = sd_bus_call_method(
+ bus,
+ "org.freedesktop.machine1",
+ "/org/freedesktop/machine1",
+ "org.freedesktop.machine1.Manager",
+ "SetImageLimit",
+ &error,
+ NULL,
+ "st", argv[1], limit);
+ else
+ /* With one argument changes the pool quota limit */
+ r = sd_bus_call_method(
+ bus,
+ "org.freedesktop.machine1",
+ "/org/freedesktop/machine1",
+ "org.freedesktop.machine1.Manager",
+ "SetPoolLimit",
+ &error,
+ NULL,
+ "t", limit);
+
+ if (r < 0) {
+ log_error("Could not set limit: %s", bus_error_message(&error, -r));
+ return r;
+ }
+
+ return 0;
+}
+
static int help(int argc, char *argv[], void *userdata) {
printf("%s [OPTIONS...] {COMMAND} ...\n\n"
@@ -2261,11 +2317,16 @@ static int help(int argc, char *argv[], void *userdata) {
" clone NAME NAME Clone an image\n"
" rename NAME NAME Rename an image\n"
" read-only NAME [BOOL] Mark or unmark image read-only\n"
- " remove NAME... Remove an image\n\n"
+ " remove NAME... Remove an image\n"
+ " set-limit [NAME] BYTES Set image or pool size limit (disk quota)\n\n"
"Image Transfer Commands:\n"
" pull-tar URL [NAME] Download a TAR container image\n"
" pull-raw URL [NAME] Download a RAW container or VM image\n"
" pull-dkr REMOTE [NAME] Download a DKR container image\n"
+ " import-tar FILE [NAME] Import a local TAR container image\n"
+ " import-raw FILE [NAME] Import a local RAW container or VM image\n"
+ " export-tar NAME [FILE] Export a TAR container image locally\n"
+ " export-raw NAME [FILE] Export a RAW container or VM image locally\n"
" list-transfers Show list of downloads in progress\n"
" cancel-transfer Cancel a download\n"
, program_invocation_short_name);
@@ -2286,6 +2347,7 @@ static int parse_argv(int argc, char *argv[]) {
ARG_VERIFY,
ARG_FORCE,
ARG_DKR_INDEX_URL,
+ ARG_FORMAT,
};
static const struct option options[] = {
@@ -2309,6 +2371,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "verify", required_argument, NULL, ARG_VERIFY },
{ "force", no_argument, NULL, ARG_FORCE },
{ "dkr-index-url", required_argument, NULL, ARG_DKR_INDEX_URL },
+ { "format", required_argument, NULL, ARG_FORMAT },
{}
};
@@ -2430,6 +2493,15 @@ static int parse_argv(int argc, char *argv[]) {
arg_dkr_index_url = optarg;
break;
+ case ARG_FORMAT:
+ if (!STR_IN_SET(optarg, "uncompressed", "xz", "gzip", "bzip2")) {
+ log_error("Unknown format: %s", optarg);
+ return -EINVAL;
+ }
+
+ arg_format = optarg;
+ break;
+
case '?':
return -EINVAL;
@@ -2447,7 +2519,7 @@ static int machinectl_main(int argc, char *argv[], sd_bus *bus) {
{ "list", VERB_ANY, 1, VERB_DEFAULT, list_machines },
{ "list-images", VERB_ANY, 1, 0, list_images },
{ "status", 2, VERB_ANY, 0, show_machine },
- { "image-status", 2, VERB_ANY, 0, show_image },
+ { "image-status", VERB_ANY, VERB_ANY, 0, show_image },
{ "show", VERB_ANY, VERB_ANY, 0, show_machine },
{ "show-image", VERB_ANY, VERB_ANY, 0, show_image },
{ "terminate", 2, VERB_ANY, 0, terminate_machine },
@@ -2465,11 +2537,16 @@ static int machinectl_main(int argc, char *argv[], sd_bus *bus) {
{ "start", 2, VERB_ANY, 0, start_machine },
{ "enable", 2, VERB_ANY, 0, enable_machine },
{ "disable", 2, VERB_ANY, 0, enable_machine },
+ { "import-tar", 2, 3, 0, import_tar },
+ { "import-raw", 2, 3, 0, import_raw },
+ { "export-tar", 2, 3, 0, export_tar },
+ { "export-raw", 2, 3, 0, export_raw },
{ "pull-tar", 2, 3, 0, pull_tar },
{ "pull-raw", 2, 3, 0, pull_raw },
{ "pull-dkr", 2, 3, 0, pull_dkr },
{ "list-transfers", VERB_ANY, 1, 0, list_transfers },
{ "cancel-transfer", 2, VERB_ANY, 0, cancel_transfer },
+ { "set-limit", 2, 3, 0, set_limit },
{}
};
@@ -2494,6 +2571,8 @@ int main(int argc, char*argv[]) {
goto finish;
}
+ sd_bus_set_allow_interactive_authorization(bus, arg_ask_password);
+
r = machinectl_main(argc, argv, bus);
finish:
diff --git a/src/machine/machined-dbus.c b/src/machine/machined-dbus.c
index ac19695c92..0e971a6789 100644
--- a/src/machine/machined-dbus.c
+++ b/src/machine/machined-dbus.c
@@ -22,35 +22,116 @@
#include <errno.h>
#include <string.h>
#include <unistd.h>
-#include <pwd.h>
#include "sd-id128.h"
-#include "sd-messages.h"
#include "strv.h"
-#include "mkdir.h"
#include "path-util.h"
-#include "special.h"
-#include "fileio-label.h"
-#include "label.h"
-#include "utf8.h"
#include "unit-name.h"
#include "bus-util.h"
#include "bus-common-errors.h"
-#include "time-util.h"
#include "cgroup-util.h"
+#include "btrfs-util.h"
#include "machine-image.h"
+#include "machine-pool.h"
#include "image-dbus.h"
#include "machined.h"
#include "machine-dbus.h"
+#include "formats-util.h"
-static int method_get_machine(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int property_get_pool_path(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+
+ assert(bus);
+ assert(reply);
+
+ return sd_bus_message_append(reply, "s", "/var/lib/machines");
+}
+
+static int property_get_pool_usage(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+
+ _cleanup_close_ int fd = -1;
+ uint64_t usage = (uint64_t) -1;
+ struct stat st;
+
+ assert(bus);
+ assert(reply);
+
+ /* We try to read the quota info from /var/lib/machines, as
+ * well as the usage of the loopback file
+ * /var/lib/machines.raw, and pick the larger value. */
+
+ fd = open("/var/lib/machines", O_RDONLY|O_CLOEXEC|O_DIRECTORY);
+ if (fd >= 0) {
+ BtrfsQuotaInfo q;
+
+ if (btrfs_subvol_get_quota_fd(fd, &q) >= 0)
+ usage = q.referenced;
+ }
+
+ if (stat("/var/lib/machines.raw", &st) >= 0) {
+ if (usage == (uint64_t) -1 || st.st_blocks * 512ULL > usage)
+ usage = st.st_blocks * 512ULL;
+ }
+
+ return sd_bus_message_append(reply, "t", usage);
+}
+
+static int property_get_pool_limit(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+
+ _cleanup_close_ int fd = -1;
+ uint64_t size = (uint64_t) -1;
+ struct stat st;
+
+ assert(bus);
+ assert(reply);
+
+ /* We try to read the quota limit from /var/lib/machines, as
+ * well as the size of the loopback file
+ * /var/lib/machines.raw, and pick the smaller value. */
+
+ fd = open("/var/lib/machines", O_RDONLY|O_CLOEXEC|O_DIRECTORY);
+ if (fd >= 0) {
+ BtrfsQuotaInfo q;
+
+ if (btrfs_subvol_get_quota_fd(fd, &q) >= 0)
+ size = q.referenced_max;
+ }
+
+ if (stat("/var/lib/machines.raw", &st) >= 0) {
+ if (size == (uint64_t) -1 || (uint64_t) st.st_size < size)
+ size = st.st_size;
+ }
+
+ return sd_bus_message_append(reply, "t", size);
+}
+
+static int method_get_machine(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_free_ char *p = NULL;
Manager *m = userdata;
Machine *machine;
const char *name;
int r;
- assert(bus);
assert(message);
assert(m);
@@ -69,13 +150,12 @@ static int method_get_machine(sd_bus *bus, sd_bus_message *message, void *userda
return sd_bus_reply_method_return(message, "o", p);
}
-static int method_get_image(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_get_image(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_free_ char *p = NULL;
Manager *m = userdata;
const char *name;
int r;
- assert(bus);
assert(message);
assert(m);
@@ -96,14 +176,13 @@ static int method_get_image(sd_bus *bus, sd_bus_message *message, void *userdata
return sd_bus_reply_method_return(message, "o", p);
}
-static int method_get_machine_by_pid(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_get_machine_by_pid(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_free_ char *p = NULL;
Manager *m = userdata;
Machine *machine = NULL;
pid_t pid;
int r;
- assert(bus);
assert(message);
assert(m);
@@ -138,14 +217,13 @@ static int method_get_machine_by_pid(sd_bus *bus, sd_bus_message *message, void
return sd_bus_reply_method_return(message, "o", p);
}
-static int method_list_machines(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_list_machines(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
Manager *m = userdata;
Machine *machine;
Iterator i;
int r;
- assert(bus);
assert(message);
assert(m);
@@ -177,7 +255,7 @@ static int method_list_machines(sd_bus *bus, sd_bus_message *message, void *user
if (r < 0)
return sd_bus_error_set_errno(error, r);
- return sd_bus_send(bus, reply, NULL);
+ return sd_bus_send(NULL, reply, NULL);
}
static int method_create_or_register_machine(Manager *manager, sd_bus_message *message, bool read_network, Machine **_m, sd_bus_error *error) {
@@ -305,11 +383,14 @@ fail:
return r;
}
-static int method_create_machine_internal(sd_bus *bus, sd_bus_message *message, bool read_network, void *userdata, sd_bus_error *error) {
+static int method_create_machine_internal(sd_bus_message *message, bool read_network, void *userdata, sd_bus_error *error) {
Manager *manager = userdata;
Machine *m = NULL;
int r;
+ assert(message);
+ assert(manager);
+
r = method_create_or_register_machine(manager, message, read_network, &m, error);
if (r < 0)
return r;
@@ -330,20 +411,23 @@ fail:
return r;
}
-static int method_create_machine_with_network(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
- return method_create_machine_internal(bus, message, true, userdata, error);
+static int method_create_machine_with_network(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ return method_create_machine_internal(message, true, userdata, error);
}
-static int method_create_machine(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
- return method_create_machine_internal(bus, message, false, userdata, error);
+static int method_create_machine(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ return method_create_machine_internal(message, false, userdata, error);
}
-static int method_register_machine_internal(sd_bus *bus, sd_bus_message *message, bool read_network, void *userdata, sd_bus_error *error) {
+static int method_register_machine_internal(sd_bus_message *message, bool read_network, void *userdata, sd_bus_error *error) {
Manager *manager = userdata;
_cleanup_free_ char *p = NULL;
Machine *m = NULL;
int r;
+ assert(message);
+ assert(manager);
+
r = method_create_or_register_machine(manager, message, read_network, &m, error);
if (r < 0)
return r;
@@ -371,21 +455,20 @@ fail:
return r;
}
-static int method_register_machine_with_network(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
- return method_register_machine_internal(bus, message, true, userdata, error);
+static int method_register_machine_with_network(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ return method_register_machine_internal(message, true, userdata, error);
}
-static int method_register_machine(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
- return method_register_machine_internal(bus, message, false, userdata, error);
+static int method_register_machine(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ return method_register_machine_internal(message, false, userdata, error);
}
-static int method_terminate_machine(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_terminate_machine(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Manager *m = userdata;
Machine *machine;
const char *name;
int r;
- assert(bus);
assert(message);
assert(m);
@@ -397,16 +480,15 @@ static int method_terminate_machine(sd_bus *bus, sd_bus_message *message, void *
if (!machine)
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name);
- return bus_machine_method_terminate(bus, message, machine, error);
+ return bus_machine_method_terminate(message, machine, error);
}
-static int method_kill_machine(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_kill_machine(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Manager *m = userdata;
Machine *machine;
const char *name;
int r;
- assert(bus);
assert(message);
assert(m);
@@ -418,16 +500,15 @@ static int method_kill_machine(sd_bus *bus, sd_bus_message *message, void *userd
if (!machine)
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name);
- return bus_machine_method_kill(bus, message, machine, error);
+ return bus_machine_method_kill(message, machine, error);
}
-static int method_get_machine_addresses(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_get_machine_addresses(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Manager *m = userdata;
Machine *machine;
const char *name;
int r;
- assert(bus);
assert(message);
assert(m);
@@ -439,16 +520,15 @@ static int method_get_machine_addresses(sd_bus *bus, sd_bus_message *message, vo
if (!machine)
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name);
- return bus_machine_method_get_addresses(bus, message, machine, error);
+ return bus_machine_method_get_addresses(message, machine, error);
}
-static int method_get_machine_os_release(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_get_machine_os_release(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Manager *m = userdata;
Machine *machine;
const char *name;
int r;
- assert(bus);
assert(message);
assert(m);
@@ -460,10 +540,10 @@ static int method_get_machine_os_release(sd_bus *bus, sd_bus_message *message, v
if (!machine)
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name);
- return bus_machine_method_get_os_release(bus, message, machine, error);
+ return bus_machine_method_get_os_release(message, machine, error);
}
-static int method_list_images(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_list_images(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
_cleanup_(image_hashmap_freep) Hashmap *images = NULL;
Manager *m = userdata;
@@ -471,7 +551,6 @@ static int method_list_images(sd_bus *bus, sd_bus_message *message, void *userda
Iterator i;
int r;
- assert(bus);
assert(message);
assert(m);
@@ -514,16 +593,15 @@ static int method_list_images(sd_bus *bus, sd_bus_message *message, void *userda
if (r < 0)
return r;
- return sd_bus_send(bus, reply, NULL);
+ return sd_bus_send(NULL, reply, NULL);
}
-static int method_open_machine_pty(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_open_machine_pty(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Manager *m = userdata;
Machine *machine;
const char *name;
int r;
- assert(bus);
assert(message);
assert(m);
@@ -535,16 +613,55 @@ static int method_open_machine_pty(sd_bus *bus, sd_bus_message *message, void *u
if (!machine)
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name);
- return bus_machine_method_open_pty(bus, message, machine, error);
+ return bus_machine_method_open_pty(message, machine, error);
}
-static int method_open_machine_login(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_open_machine_login(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ Manager *m = userdata;
+ Machine *machine;
+ const char *name;
+ int r;
+
+ assert(message);
+ assert(m);
+
+ r = sd_bus_message_read(message, "s", &name);
+ if (r < 0)
+ return r;
+
+ machine = hashmap_get(m->machines, name);
+ if (!machine)
+ return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name);
+
+ return bus_machine_method_open_login(message, machine, error);
+}
+
+static int method_bind_mount_machine(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ Manager *m = userdata;
+ Machine *machine;
+ const char *name;
+ int r;
+
+ assert(message);
+ assert(m);
+
+ r = sd_bus_message_read(message, "s", &name);
+ if (r < 0)
+ return r;
+
+ machine = hashmap_get(m->machines, name);
+ if (!machine)
+ return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name);
+
+ return bus_machine_method_bind_mount(message, machine, error);
+}
+
+static int method_copy_machine(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Manager *m = userdata;
Machine *machine;
const char *name;
int r;
- assert(bus);
assert(message);
assert(m);
@@ -556,15 +673,14 @@ static int method_open_machine_login(sd_bus *bus, sd_bus_message *message, void
if (!machine)
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name);
- return bus_machine_method_open_login(bus, message, machine, error);
+ return bus_machine_method_copy(message, machine, error);
}
-static int method_remove_image(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_remove_image(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_(image_unrefp) Image* i = NULL;
const char *name;
int r;
- assert(bus);
assert(message);
r = sd_bus_message_read(message, "s", &name);
@@ -580,15 +696,15 @@ static int method_remove_image(sd_bus *bus, sd_bus_message *message, void *userd
if (r == 0)
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_IMAGE, "No image '%s' known", name);
- return bus_image_method_remove(bus, message, i, error);
+ i->userdata = userdata;
+ return bus_image_method_remove(message, i, error);
}
-static int method_rename_image(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_rename_image(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_(image_unrefp) Image* i = NULL;
const char *old_name;
int r;
- assert(bus);
assert(message);
r = sd_bus_message_read(message, "s", &old_name);
@@ -604,15 +720,17 @@ static int method_rename_image(sd_bus *bus, sd_bus_message *message, void *userd
if (r == 0)
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_IMAGE, "No image '%s' known", old_name);
- return bus_image_method_rename(bus, message, i, error);
+ i->userdata = userdata;
+ return bus_image_method_rename(message, i, error);
}
-static int method_clone_image(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_clone_image(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_(image_unrefp) Image *i = NULL;
const char *old_name;
int r;
- assert(bus);
+ assert(message);
+
r = sd_bus_message_read(message, "s", &old_name);
if (r < 0)
return r;
@@ -626,15 +744,17 @@ static int method_clone_image(sd_bus *bus, sd_bus_message *message, void *userda
if (r == 0)
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_IMAGE, "No image '%s' known", old_name);
- return bus_image_method_clone(bus, message, i, error);
+ i->userdata = userdata;
+ return bus_image_method_clone(message, i, error);
}
-static int method_mark_image_read_only(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_mark_image_read_only(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_(image_unrefp) Image *i = NULL;
const char *name;
int r;
- assert(bus);
+ assert(message);
+
r = sd_bus_message_read(message, "s", &name);
if (r < 0)
return r;
@@ -648,11 +768,83 @@ static int method_mark_image_read_only(sd_bus *bus, sd_bus_message *message, voi
if (r == 0)
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_IMAGE, "No image '%s' known", name);
- return bus_image_method_mark_read_only(bus, message, i, error);
+ i->userdata = userdata;
+ return bus_image_method_mark_read_only(message, i, error);
+}
+
+static int method_set_pool_limit(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ Manager *m = userdata;
+ uint64_t limit;
+ int r;
+
+ assert(message);
+
+ r = sd_bus_message_read(message, "t", &limit);
+ if (r < 0)
+ return r;
+
+ r = bus_verify_polkit_async(
+ message,
+ CAP_SYS_ADMIN,
+ "org.freedesktop.machine1.manage-machines",
+ false,
+ UID_INVALID,
+ &m->polkit_registry,
+ error);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return 1; /* Will call us back */
+
+ /* Set up the machine directory if necessary */
+ r = setup_machine_directory(limit, error);
+ if (r < 0)
+ return r;
+
+ r = btrfs_resize_loopback("/var/lib/machines", limit, false);
+ if (r == -ENOTTY)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Quota is only supported on btrfs.");
+ if (r < 0 && r != -ENODEV) /* ignore ENODEV, as that's what is returned if the file system is not on loopback */
+ return sd_bus_error_set_errnof(error, r, "Failed to adjust loopback limit: %m");
+
+ r = btrfs_quota_limit("/var/lib/machines", limit);
+ if (r == -ENOTTY)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Quota is only supported on btrfs.");
+ if (r < 0)
+ return sd_bus_error_set_errnof(error, r, "Failed to adjust quota limit: %m");
+
+ return sd_bus_reply_method_return(message, NULL);
+}
+
+static int method_set_image_limit(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ _cleanup_(image_unrefp) Image *i = NULL;
+ const char *name;
+ int r;
+
+ assert(message);
+
+ r = sd_bus_message_read(message, "s", &name);
+ if (r < 0)
+ return r;
+
+ if (!image_name_is_valid(name))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Image name '%s' is invalid.", name);
+
+ r = image_find(name, &i);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_IMAGE, "No image '%s' known", name);
+
+ i->userdata = userdata;
+ return bus_image_method_set_limit(message, i, error);
}
const sd_bus_vtable manager_vtable[] = {
SD_BUS_VTABLE_START(0),
+ SD_BUS_PROPERTY("PoolPath", "s", property_get_pool_path, 0, 0),
+ SD_BUS_PROPERTY("PoolUsage", "t", property_get_pool_usage, 0, 0),
+ SD_BUS_PROPERTY("PoolLimit", "t", property_get_pool_limit, 0, 0),
SD_BUS_METHOD("GetMachine", "s", "o", method_get_machine, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("GetImage", "s", "o", method_get_image, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("GetMachineByPID", "u", "o", method_get_machine_by_pid, SD_BUS_VTABLE_UNPRIVILEGED),
@@ -662,29 +854,33 @@ const sd_bus_vtable manager_vtable[] = {
SD_BUS_METHOD("CreateMachineWithNetwork", "sayssusaia(sv)", "o", method_create_machine_with_network, 0),
SD_BUS_METHOD("RegisterMachine", "sayssus", "o", method_register_machine, 0),
SD_BUS_METHOD("RegisterMachineWithNetwork", "sayssusai", "o", method_register_machine_with_network, 0),
- SD_BUS_METHOD("KillMachine", "ssi", NULL, method_kill_machine, SD_BUS_VTABLE_CAPABILITY(CAP_KILL)),
- SD_BUS_METHOD("TerminateMachine", "s", NULL, method_terminate_machine, SD_BUS_VTABLE_CAPABILITY(CAP_KILL)),
+ SD_BUS_METHOD("TerminateMachine", "s", NULL, method_terminate_machine, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("KillMachine", "ssi", NULL, method_kill_machine, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("GetMachineAddresses", "s", "a(iay)", method_get_machine_addresses, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("GetMachineOSRelease", "s", "a{ss}", method_get_machine_os_release, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("OpenMachinePTY", "s", "hs", method_open_machine_pty, 0),
SD_BUS_METHOD("OpenMachineLogin", "s", "hs", method_open_machine_login, SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD("RemoveImage", "s", NULL, method_remove_image, 0),
- SD_BUS_METHOD("RenameImage", "ss", NULL, method_rename_image, 0),
- SD_BUS_METHOD("CloneImage", "ssb", NULL, method_clone_image, 0),
- SD_BUS_METHOD("MarkImageReadOnly", "sb", NULL, method_mark_image_read_only, 0),
+ SD_BUS_METHOD("BindMountMachine", "sssbb", NULL, method_bind_mount_machine, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("CopyFromMachine", "sss", NULL, method_copy_machine, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("CopyToMachine", "sss", NULL, method_copy_machine, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("RemoveImage", "s", NULL, method_remove_image, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("RenameImage", "ss", NULL, method_rename_image, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("CloneImage", "ssb", NULL, method_clone_image, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("MarkImageReadOnly", "sb", NULL, method_mark_image_read_only, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("SetPoolLimit", "t", NULL, method_set_pool_limit, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("SetImageLimit", "st", NULL, method_set_image_limit, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_SIGNAL("MachineNew", "so", 0),
SD_BUS_SIGNAL("MachineRemoved", "so", 0),
SD_BUS_VTABLE_END
};
-int match_job_removed(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+int match_job_removed(sd_bus_message *message, void *userdata, sd_bus_error *error) {
const char *path, *result, *unit;
Manager *m = userdata;
Machine *machine;
uint32_t id;
int r;
- assert(bus);
assert(message);
assert(m);
@@ -720,14 +916,13 @@ int match_job_removed(sd_bus *bus, sd_bus_message *message, void *userdata, sd_b
return 0;
}
-int match_properties_changed(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+int match_properties_changed(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_free_ char *unit = NULL;
+ const char *path, *interface;
Manager *m = userdata;
Machine *machine;
- const char *path;
int r;
- assert(bus);
assert(message);
assert(m);
@@ -738,46 +933,82 @@ int match_properties_changed(sd_bus *bus, sd_bus_message *message, void *userdat
r = unit_name_from_dbus_path(path, &unit);
if (r == -EINVAL) /* not for a unit */
return 0;
- if (r < 0)
- return r;
+ if (r < 0){
+ log_oom();
+ return 0;
+ }
machine = hashmap_get(m->machine_units, unit);
- if (machine)
- machine_add_to_gc_queue(machine);
+ if (!machine)
+ return 0;
+
+ r = sd_bus_message_read(message, "s", &interface);
+ if (r < 0) {
+ bus_log_parse_error(r);
+ return 0;
+ }
+
+ if (streq(interface, "org.freedesktop.systemd1.Unit")) {
+ struct properties {
+ char *active_state;
+ char *sub_state;
+ } properties = {};
+
+ const struct bus_properties_map map[] = {
+ { "ActiveState", "s", NULL, offsetof(struct properties, active_state) },
+ { "SubState", "s", NULL, offsetof(struct properties, sub_state) },
+ {}
+ };
+ r = bus_message_map_properties_changed(message, map, &properties);
+ if (r < 0)
+ bus_log_parse_error(r);
+ else if (streq_ptr(properties.active_state, "inactive") ||
+ streq_ptr(properties.active_state, "failed") ||
+ streq_ptr(properties.sub_state, "auto-restart"))
+ machine_release_unit(machine);
+
+ free(properties.active_state);
+ free(properties.sub_state);
+ }
+
+ machine_add_to_gc_queue(machine);
return 0;
}
-int match_unit_removed(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+int match_unit_removed(sd_bus_message *message, void *userdata, sd_bus_error *error) {
const char *path, *unit;
Manager *m = userdata;
Machine *machine;
int r;
- assert(bus);
assert(message);
assert(m);
r = sd_bus_message_read(message, "so", &unit, &path);
if (r < 0) {
bus_log_parse_error(r);
- return r;
+ return 0;
}
machine = hashmap_get(m->machine_units, unit);
- if (machine)
- machine_add_to_gc_queue(machine);
+ if (!machine)
+ return 0;
+
+ machine_release_unit(machine);
+ machine_add_to_gc_queue(machine);
return 0;
}
-int match_reloading(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+int match_reloading(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Manager *m = userdata;
Machine *machine;
Iterator i;
int b, r;
- assert(bus);
+ assert(message);
+ assert(m);
r = sd_bus_message_read(message, "b", &b);
if (r < 0) {
@@ -989,7 +1220,7 @@ int manager_unit_is_active(Manager *manager, const char *unit) {
if (r < 0)
return -EINVAL;
- return !streq(state, "inactive") && !streq(state, "failed");
+ return !STR_IN_SET(state, "inactive", "failed");
}
int manager_job_is_active(Manager *manager, const char *path) {
diff --git a/src/machine/machined.c b/src/machine/machined.c
index 6877c2b313..754c770040 100644
--- a/src/machine/machined.c
+++ b/src/machine/machined.c
@@ -20,20 +20,16 @@
***/
#include <errno.h>
-#include <pwd.h>
-#include <fcntl.h>
#include <string.h>
#include <unistd.h>
-#include <sys/epoll.h>
#include "sd-daemon.h"
-#include "strv.h"
-#include "conf-parser.h"
#include "cgroup-util.h"
-#include "mkdir.h"
#include "bus-util.h"
#include "bus-error.h"
#include "label.h"
+#include "formats-util.h"
+#include "signal-util.h"
#include "machine-image.h"
#include "machined.h"
@@ -199,7 +195,8 @@ static int manager_connect_bus(Manager *m) {
"type='signal',"
"sender='org.freedesktop.systemd1',"
"interface='org.freedesktop.DBus.Properties',"
- "member='PropertiesChanged'",
+ "member='PropertiesChanged',"
+ "arg0='org.freedesktop.systemd1.Unit'",
match_properties_changed,
m);
if (r < 0)
@@ -325,6 +322,8 @@ int main(int argc, char *argv[]) {
* check stays in. */
mkdir_label("/run/systemd/machines", 0755);
+ assert_se(sigprocmask_many(SIG_BLOCK, SIGCHLD, -1) >= 0);
+
m = manager_new();
if (!m) {
r = log_oom();
diff --git a/src/machine/machined.h b/src/machine/machined.h
index df0cb82f8d..61dbefb5f1 100644
--- a/src/machine/machined.h
+++ b/src/machine/machined.h
@@ -22,9 +22,7 @@
***/
#include <stdbool.h>
-#include <inttypes.h>
-#include "util.h"
#include "list.h"
#include "hashmap.h"
#include "sd-event.h"
@@ -67,10 +65,10 @@ int manager_get_machine_by_pid(Manager *m, pid_t pid, Machine **machine);
extern const sd_bus_vtable manager_vtable[];
-int match_reloading(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error);
-int match_unit_removed(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error);
-int match_properties_changed(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error);
-int match_job_removed(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error);
+int match_reloading(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int match_unit_removed(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int match_properties_changed(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int match_job_removed(sd_bus_message *message, void *userdata, sd_bus_error *error);
int manager_start_scope(Manager *manager, const char *scope, pid_t pid, const char *slice, const char *description, sd_bus_message *more_properties, sd_bus_error *error, char **job);
int manager_stop_unit(Manager *manager, const char *unit, sd_bus_error *error, char **job);
diff --git a/src/machine/org.freedesktop.machine1.conf b/src/machine/org.freedesktop.machine1.conf
index 37f84bd6f3..93aaf6a377 100644
--- a/src/machine/org.freedesktop.machine1.conf
+++ b/src/machine/org.freedesktop.machine1.conf
@@ -69,6 +69,50 @@
send_member="OpenMachineLogin"/>
<allow send_destination="org.freedesktop.machine1"
+ send_interface="org.freedesktop.machine1.Manager"
+ send_member="TerminateMachine"/>
+
+ <allow send_destination="org.freedesktop.machine1"
+ send_interface="org.freedesktop.machine1.Manager"
+ send_member="KillMachine"/>
+
+ <allow send_destination="org.freedesktop.machine1"
+ send_interface="org.freedesktop.machine1.Manager"
+ send_member="BindMountMachine"/>
+
+ <allow send_destination="org.freedesktop.machine1"
+ send_interface="org.freedesktop.machine1.Manager"
+ send_member="CopyFromMachine"/>
+
+ <allow send_destination="org.freedesktop.machine1"
+ send_interface="org.freedesktop.machine1.Manager"
+ send_member="CopyToMachine"/>
+
+ <allow send_destination="org.freedesktop.machine1"
+ send_interface="org.freedesktop.machine1.Manager"
+ send_member="RemoveImage"/>
+
+ <allow send_destination="org.freedesktop.machine1"
+ send_interface="org.freedesktop.machine1.Manager"
+ send_member="RenameImage"/>
+
+ <allow send_destination="org.freedesktop.machine1"
+ send_interface="org.freedesktop.machine1.Manager"
+ send_member="CloneImage"/>
+
+ <allow send_destination="org.freedesktop.machine1"
+ send_interface="org.freedesktop.machine1.Manager"
+ send_member="MarkImageReadOnly"/>
+
+ <allow send_destination="org.freedesktop.machine1"
+ send_interface="org.freedesktop.machine1.Manager"
+ send_member="SetPoolLimit"/>
+
+ <allow send_destination="org.freedesktop.machine1"
+ send_interface="org.freedesktop.machine1.Manager"
+ send_member="SetImageLimit"/>
+
+ <allow send_destination="org.freedesktop.machine1"
send_interface="org.freedesktop.machine1.Machine"
send_member="GetAddresses"/>
@@ -80,6 +124,46 @@
send_interface="org.freedesktop.machine1.Machine"
send_member="OpenLogin"/>
+ <allow send_destination="org.freedesktop.machine1"
+ send_interface="org.freedesktop.machine1.Machine"
+ send_member="Terminate"/>
+
+ <allow send_destination="org.freedesktop.machine1"
+ send_interface="org.freedesktop.machine1.Machine"
+ send_member="Kill"/>
+
+ <allow send_destination="org.freedesktop.machine1"
+ send_interface="org.freedesktop.machine1.Machine"
+ send_member="BindMount"/>
+
+ <allow send_destination="org.freedesktop.machine1"
+ send_interface="org.freedesktop.machine1.Machine"
+ send_member="CopyFrom"/>
+
+ <allow send_destination="org.freedesktop.machine1"
+ send_interface="org.freedesktop.machine1.Machine"
+ send_member="CopyTo"/>
+
+ <allow send_destination="org.freedesktop.machine1"
+ send_interface="org.freedesktop.machine1.Image"
+ send_member="Remove"/>
+
+ <allow send_destination="org.freedesktop.machine1"
+ send_interface="org.freedesktop.machine1.Image"
+ send_member="Rename"/>
+
+ <allow send_destination="org.freedesktop.machine1"
+ send_interface="org.freedesktop.machine1.Image"
+ send_member="Clone"/>
+
+ <allow send_destination="org.freedesktop.machine1"
+ send_interface="org.freedesktop.machine1.Image"
+ send_member="SetLimit"/>
+
+ <allow send_destination="org.freedesktop.machine1"
+ send_interface="org.freedesktop.machine1.Image"
+ send_member="MarkReadOnly"/>
+
<allow receive_sender="org.freedesktop.machine1"/>
</policy>
diff --git a/src/machine/org.freedesktop.machine1.policy.in b/src/machine/org.freedesktop.machine1.policy.in
index 43478a84d6..02714e83ae 100644
--- a/src/machine/org.freedesktop.machine1.policy.in
+++ b/src/machine/org.freedesktop.machine1.policy.in
@@ -18,7 +18,27 @@
<action id="org.freedesktop.machine1.login">
<_description>Log into a local container</_description>
- <_message>Authentication is required to log into a local container</_message>
+ <_message>Authentication is required to log into a local container.</_message>
+ <defaults>
+ <allow_any>auth_admin</allow_any>
+ <allow_inactive>auth_admin</allow_inactive>
+ <allow_active>auth_admin_keep</allow_active>
+ </defaults>
+ </action>
+
+ <action id="org.freedesktop.machine1.manage-machines">
+ <_description>Manage local virtual machines and containers</_description>
+ <_message>Authentication is required to manage local virtual machines and containers.</_message>
+ <defaults>
+ <allow_any>auth_admin</allow_any>
+ <allow_inactive>auth_admin</allow_inactive>
+ <allow_active>auth_admin_keep</allow_active>
+ </defaults>
+ </action>
+
+ <action id="org.freedesktop.machine1.manage-images">
+ <_description>Manage local virtual machine and container images</_description>
+ <_message>Authentication is required to manage local virtual machine and container images.</_message>
<defaults>
<allow_any>auth_admin</allow_any>
<allow_inactive>auth_admin</allow_inactive>
diff --git a/src/modules-load/modules-load.c b/src/modules-load/modules-load.c
index 5f678789ce..5bbe314ba0 100644
--- a/src/modules-load/modules-load.c
+++ b/src/modules-load/modules-load.c
@@ -19,13 +19,10 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <unistd.h>
-#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <sys/stat.h>
#include <limits.h>
-#include <dirent.h>
#include <getopt.h>
#include <libkmod.h>
@@ -33,7 +30,6 @@
#include "util.h"
#include "strv.h"
#include "conf-files.h"
-#include "fileio.h"
#include "build.h"
static char **arg_proc_cmdline_modules = NULL;
@@ -256,7 +252,7 @@ int main(int argc, char *argv[]) {
}
} else {
- _cleanup_free_ char **files = NULL;
+ _cleanup_strv_free_ char **files = NULL;
char **fn, **i;
STRV_FOREACH(i, arg_proc_cmdline_modules) {
diff --git a/src/network/networkctl.c b/src/network/networkctl.c
index aa83f32f53..3454394977 100644
--- a/src/network/networkctl.c
+++ b/src/network/networkctl.c
@@ -26,7 +26,7 @@
#include "sd-network.h"
#include "sd-rtnl.h"
#include "sd-hwdb.h"
-#include "libudev.h"
+#include "sd-device.h"
#include "strv.h"
#include "build.h"
@@ -34,13 +34,14 @@
#include "pager.h"
#include "lldp.h"
#include "rtnl-util.h"
-#include "udev-util.h"
+#include "device-util.h"
#include "hwdb-util.h"
#include "arphrd-list.h"
#include "local-addresses.h"
#include "socket-util.h"
#include "ether-addr-util.h"
#include "verbs.h"
+#include "terminal-util.h"
static bool arg_no_pager = false;
static bool arg_legend = true;
@@ -54,17 +55,20 @@ static void pager_open_if_enabled(void) {
pager_open(false);
}
-static int link_get_type_string(int iftype, struct udev_device *d, char **ret) {
+static int link_get_type_string(int iftype, sd_device *d, char **ret) {
const char *t;
char *p;
+ assert(ret);
+
if (iftype == ARPHRD_ETHER && d) {
- const char *devtype, *id = NULL;
+ const char *devtype = NULL, *id = NULL;
/* WLANs have iftype ARPHRD_ETHER, but we want
* to show a more useful type string for
* them */
- devtype = udev_device_get_devtype(d);
+ (void)sd_device_get_devtype(d, &devtype);
+
if (streq_ptr(devtype, "wlan"))
id = "wlan";
else if (streq_ptr(devtype, "wwan"))
@@ -189,7 +193,6 @@ static void setup_state_to_color(const char *state, const char **on, const char
static int list_links(int argc, char *argv[], void *userdata) {
_cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *reply = NULL;
- _cleanup_udev_unref_ struct udev *udev = NULL;
_cleanup_rtnl_unref_ sd_rtnl *rtnl = NULL;
_cleanup_free_ LinkInfo *links = NULL;
int r, c, i;
@@ -200,10 +203,6 @@ static int list_links(int argc, char *argv[], void *userdata) {
if (r < 0)
return log_error_errno(r, "Failed to connect to netlink: %m");
- udev = udev_new();
- if (!udev)
- return log_error_errno(errno, "Failed to connect to udev: %m");
-
r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
if (r < 0)
return rtnl_log_create_error(r);
@@ -225,7 +224,7 @@ static int list_links(int argc, char *argv[], void *userdata) {
for (i = 0; i < c; i++) {
_cleanup_free_ char *setup_state = NULL, *operational_state = NULL;
- _cleanup_udev_device_unref_ struct udev_device *d = NULL;
+ _cleanup_device_unref_ sd_device *d = NULL;
const char *on_color_operational, *off_color_operational,
*on_color_setup, *off_color_setup;
char devid[2 + DECIMAL_STR_MAX(int)];
@@ -238,7 +237,7 @@ static int list_links(int argc, char *argv[], void *userdata) {
setup_state_to_color(setup_state, &on_color_setup, &off_color_setup);
sprintf(devid, "n%i", links[i].ifindex);
- d = udev_device_new_from_device_id(udev, devid);
+ (void)sd_device_new_from_device_id(&d, devid);
link_get_type_string(links[i].iftype, d, &t);
@@ -495,19 +494,19 @@ static void dump_list(const char *prefix, char **l) {
static int link_status_one(
sd_rtnl *rtnl,
- struct udev *udev,
sd_hwdb *hwdb,
const char *name) {
-
_cleanup_strv_free_ char **dns = NULL, **ntp = NULL, **domains = NULL;
_cleanup_free_ char *setup_state = NULL, *operational_state = NULL;
_cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *reply = NULL;
- _cleanup_udev_device_unref_ struct udev_device *d = NULL;
+ _cleanup_device_unref_ sd_device *d = NULL;
char devid[2 + DECIMAL_STR_MAX(int)];
_cleanup_free_ char *t = NULL, *network = NULL;
const char *driver = NULL, *path = NULL, *vendor = NULL, *model = NULL, *link = NULL;
const char *on_color_operational, *off_color_operational,
*on_color_setup, *off_color_setup;
+ _cleanup_strv_free_ char **carrier_bound_to = NULL;
+ _cleanup_strv_free_ char **carrier_bound_by = NULL;
struct ether_addr e;
unsigned iftype;
int r, ifindex;
@@ -515,7 +514,6 @@ static int link_status_one(
uint32_t mtu;
assert(rtnl);
- assert(udev);
assert(name);
if (safe_atoi(name, &ifindex) >= 0 && ifindex > 0)
@@ -587,31 +585,36 @@ static int link_status_one(
}
sprintf(devid, "n%i", ifindex);
- d = udev_device_new_from_device_id(udev, devid);
+
+ (void)sd_device_new_from_device_id(&d, devid);
+
if (d) {
- link = udev_device_get_property_value(d, "ID_NET_LINK_FILE");
- driver = udev_device_get_property_value(d, "ID_NET_DRIVER");
- path = udev_device_get_property_value(d, "ID_PATH");
+ (void)sd_device_get_property_value(d, "ID_NET_LINK_FILE", &link);
+ (void)sd_device_get_property_value(d, "ID_NET_DRIVER", &driver);
+ (void)sd_device_get_property_value(d, "ID_PATH", &path);
- vendor = udev_device_get_property_value(d, "ID_VENDOR_FROM_DATABASE");
- if (!vendor)
- vendor = udev_device_get_property_value(d, "ID_VENDOR");
+ r = sd_device_get_property_value(d, "ID_VENDOR_FROM_DATABASE", &vendor);
+ if (r < 0)
+ (void)sd_device_get_property_value(d, "ID_VENDOR", &vendor);
- model = udev_device_get_property_value(d, "ID_MODEL_FROM_DATABASE");
- if (!model)
- model = udev_device_get_property_value(d, "ID_MODEL");
+ r = sd_device_get_property_value(d, "ID_MODEL_FROM_DATABASE", &model);
+ if (r < 0)
+ (void)sd_device_get_property_value(d, "ID_MODEL", &model);
}
link_get_type_string(iftype, d, &t);
sd_network_link_get_network_file(ifindex, &network);
+ sd_network_link_get_carrier_bound_to(ifindex, &carrier_bound_to);
+ sd_network_link_get_carrier_bound_by(ifindex, &carrier_bound_by);
+
printf("%s%s%s %i: %s\n", on_color_operational, draw_special_char(DRAW_BLACK_CIRCLE), off_color_operational, ifindex, name);
- printf(" Link File: %s\n"
- "Network File: %s\n"
- " Type: %s\n"
- " State: %s%s%s (%s%s%s)\n",
+ printf(" Link File: %s\n"
+ " Network File: %s\n"
+ " Type: %s\n"
+ " State: %s%s%s (%s%s%s)\n",
strna(link),
strna(network),
strna(t),
@@ -619,13 +622,13 @@ static int link_status_one(
on_color_setup, strna(setup_state), off_color_setup);
if (path)
- printf(" Path: %s\n", path);
+ printf(" Path: %s\n", path);
if (driver)
- printf(" Driver: %s\n", driver);
+ printf(" Driver: %s\n", driver);
if (vendor)
- printf(" Vendor: %s\n", vendor);
+ printf(" Vendor: %s\n", vendor);
if (model)
- printf(" Model: %s\n", model);
+ printf(" Model: %s\n", model);
if (have_mac) {
_cleanup_free_ char *description = NULL;
@@ -634,30 +637,35 @@ static int link_status_one(
ieee_oui(hwdb, &e, &description);
if (description)
- printf(" HW Address: %s (%s)\n", ether_addr_to_string(&e, ea), description);
+ printf(" HW Address: %s (%s)\n", ether_addr_to_string(&e, ea), description);
else
- printf(" HW Address: %s\n", ether_addr_to_string(&e, ea));
+ printf(" HW Address: %s\n", ether_addr_to_string(&e, ea));
}
if (mtu > 0)
- printf(" MTU: %u\n", mtu);
+ printf(" MTU: %u\n", mtu);
- dump_addresses(rtnl, " Address: ", ifindex);
- dump_gateways(rtnl, hwdb, " Gateway: ", ifindex);
+ dump_addresses(rtnl, " Address: ", ifindex);
+ dump_gateways(rtnl, hwdb, " Gateway: ", ifindex);
if (!strv_isempty(dns))
- dump_list(" DNS: ", dns);
+ dump_list(" DNS: ", dns);
if (!strv_isempty(domains))
- dump_list(" Domain: ", domains);
+ dump_list(" Domain: ", domains);
if (!strv_isempty(ntp))
- dump_list(" NTP: ", ntp);
+ dump_list(" NTP: ", ntp);
+
+ if (!strv_isempty(carrier_bound_to))
+ dump_list("Carrier Bound To: ", carrier_bound_to);
+
+ if (!strv_isempty(carrier_bound_by))
+ dump_list("Carrier Bound By: ", carrier_bound_by);
return 0;
}
static int link_status(int argc, char *argv[], void *userdata) {
_cleanup_hwdb_unref_ sd_hwdb *hwdb = NULL;
- _cleanup_udev_unref_ struct udev *udev = NULL;
_cleanup_rtnl_unref_ sd_rtnl *rtnl = NULL;
char **name;
int r;
@@ -666,10 +674,6 @@ static int link_status(int argc, char *argv[], void *userdata) {
if (r < 0)
return log_error_errno(r, "Failed to connect to netlink: %m");
- udev = udev_new();
- if (!udev)
- return log_error_errno(errno, "Failed to connect to udev: %m");
-
r = sd_hwdb_new(&hwdb);
if (r < 0)
log_debug_errno(r, "Failed to open hardware database: %m");
@@ -731,14 +735,14 @@ static int link_status(int argc, char *argv[], void *userdata) {
if (i > 0)
fputc('\n', stdout);
- link_status_one(rtnl, udev, hwdb, links[i].name);
+ link_status_one(rtnl, hwdb, links[i].name);
}
} else {
STRV_FOREACH(name, argv + 1) {
if (name != argv + 1)
fputc('\n', stdout);
- link_status_one(rtnl, udev, hwdb, *name);
+ link_status_one(rtnl, hwdb, *name);
}
}
@@ -954,17 +958,17 @@ static int link_lldp_status(int argc, char *argv[], void *userdata) {
continue;
if (streq(a, "_Chassis")) {
- chassis = strdup(b);
- if (!chassis)
- return -ENOMEM;
+ r = free_and_strdup(&chassis, b);
+ if (r < 0)
+ return r;
} else if (streq(a, "_Port")) {
- port = strdup(b);
- if (!port)
- return -ENOMEM;
+ r = free_and_strdup(&port, b);
+ if (r < 0)
+ return r;
} else if (streq(a, "_TTL")) {
- long long unsigned x;
+ long long unsigned x = 0;
usec_t time;
r = safe_atollu(b, &x);
diff --git a/src/network/networkd-address-pool.c b/src/network/networkd-address-pool.c
index 7ad11c6177..584a956a7e 100644
--- a/src/network/networkd-address-pool.c
+++ b/src/network/networkd-address-pool.c
@@ -19,7 +19,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <arpa/inet.h>
#include "networkd.h"
#include "networkd-link.h"
diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c
index 47033acde4..069ba3eccb 100644
--- a/src/network/networkd-address.c
+++ b/src/network/networkd-address.c
@@ -25,7 +25,6 @@
#include "util.h"
#include "conf-parser.h"
#include "fw-util.h"
-#include "network-internal.h"
#include "networkd.h"
#include "networkd-link.h"
@@ -210,10 +209,18 @@ int address_update(Address *address, Link *link,
if (r < 0)
return log_error_errno(r, "Could not set prefixlen: %m");
- r = sd_rtnl_message_addr_set_flags(req, IFA_F_PERMANENT);
+ address->flags |= IFA_F_PERMANENT;
+
+ r = sd_rtnl_message_addr_set_flags(req, address->flags & 0xff);
if (r < 0)
return log_error_errno(r, "Could not set flags: %m");
+ if (address->flags & ~0xff && link->rtnl_extended_attrs) {
+ r = sd_rtnl_message_append_u32(req, IFA_FLAGS, address->flags);
+ if (r < 0)
+ return log_error_errno(r, "Could not set extended flags: %m");
+ }
+
r = sd_rtnl_message_addr_set_scope(req, address->scope);
if (r < 0)
return log_error_errno(r, "Could not set scope: %m");
@@ -267,10 +274,8 @@ static int address_acquire(Link *link, Address *original, Address **ret) {
/* The address is configured to be 0.0.0.0 or [::] by the user?
* Then let's acquire something more useful from the pool. */
r = manager_address_pool_acquire(link->manager, original->family, original->prefixlen, &in_addr);
- if (r < 0) {
- log_link_error(link, "Failed to acquire address from pool: %s", strerror(-r));
- return r;
- }
+ if (r < 0)
+ return log_link_error_errno(link, r, "Failed to acquire address from pool: %m");
if (r == 0) {
log_link_error(link, "Couldn't find free address for interface, all taken.");
return -EBUSY;
@@ -336,10 +341,18 @@ int address_configure(Address *address, Link *link,
if (r < 0)
return log_error_errno(r, "Could not set prefixlen: %m");
- r = sd_rtnl_message_addr_set_flags(req, IFA_F_PERMANENT);
+ address->flags |= IFA_F_PERMANENT;
+
+ r = sd_rtnl_message_addr_set_flags(req, (address->flags & 0xff));
if (r < 0)
return log_error_errno(r, "Could not set flags: %m");
+ if (address->flags & ~0xff) {
+ r = sd_rtnl_message_append_u32(req, IFA_FLAGS, address->flags);
+ if (r < 0)
+ return log_error_errno(r, "Could not set extended flags: %m");
+ }
+
r = sd_rtnl_message_addr_set_scope(req, address->scope);
if (r < 0)
return log_error_errno(r, "Could not set scope: %m");
diff --git a/src/network/networkd-dhcp4.c b/src/network/networkd-dhcp4.c
index c3d0e3d39d..20aee53d6e 100644
--- a/src/network/networkd-dhcp4.c
+++ b/src/network/networkd-dhcp4.c
@@ -22,6 +22,7 @@
#include <netinet/ether.h>
#include <linux/if.h>
+#include "hostname-util.h"
#include "networkd-link.h"
#include "network-internal.h"
#include "dhcp-lease-internal.h"
@@ -409,62 +410,43 @@ static int dhcp_lease_acquired(sd_dhcp_client *client, Link *link) {
assert(link);
r = sd_dhcp_client_get_lease(client, &lease);
- if (r < 0) {
- log_link_warning(link, "DHCP error: no lease: %s",
- strerror(-r));
- return r;
- }
+ if (r < 0)
+ return log_link_error_errno(link, r, "DHCP error: no lease: %m");
r = sd_dhcp_lease_get_address(lease, &address);
- if (r < 0) {
- log_link_warning(link, "DHCP error: no address: %s",
- strerror(-r));
- return r;
- }
+ if (r < 0)
+ return log_link_error_errno(link, r, "DHCP error: no address: %m");
r = sd_dhcp_lease_get_netmask(lease, &netmask);
- if (r < 0) {
- log_link_warning(link, "DHCP error: no netmask: %s",
- strerror(-r));
- return r;
- }
+ if (r < 0)
+ return log_link_error_errno(link, r, "DHCP error: no netmask: %m");
prefixlen = in_addr_netmask_to_prefixlen(&netmask);
r = sd_dhcp_lease_get_router(lease, &gateway);
- if (r < 0 && r != -ENOENT) {
- log_link_warning(link, "DHCP error: could not get gateway: %s",
- strerror(-r));
- return r;
- }
+ if (r < 0 && r != -ENOENT)
+ return log_link_error_errno(link, r, "DHCP error: could not get gateway: %m");
if (r >= 0)
- log_link_struct(link, LOG_INFO,
- "MESSAGE=%-*s: DHCPv4 address %u.%u.%u.%u/%u via %u.%u.%u.%u",
- IFNAMSIZ,
- link->ifname,
- ADDRESS_FMT_VAL(address),
- prefixlen,
- ADDRESS_FMT_VAL(gateway),
- "ADDRESS=%u.%u.%u.%u",
- ADDRESS_FMT_VAL(address),
- "PREFIXLEN=%u",
- prefixlen,
- "GATEWAY=%u.%u.%u.%u",
- ADDRESS_FMT_VAL(gateway),
- NULL);
+ log_struct(LOG_INFO,
+ LOG_LINK_INTERFACE(link),
+ LOG_LINK_MESSAGE(link, "DHCPv4 address %u.%u.%u.%u/%u via %u.%u.%u.%u",
+ ADDRESS_FMT_VAL(address),
+ prefixlen,
+ ADDRESS_FMT_VAL(gateway)),
+ "ADDRESS=%u.%u.%u.%u", ADDRESS_FMT_VAL(address),
+ "PREFIXLEN=%u", prefixlen,
+ "GATEWAY=%u.%u.%u.%u", ADDRESS_FMT_VAL(gateway),
+ NULL);
else
- log_link_struct(link, LOG_INFO,
- "MESSAGE=%-*s: DHCPv4 address %u.%u.%u.%u/%u",
- IFNAMSIZ,
- link->ifname,
- ADDRESS_FMT_VAL(address),
- prefixlen,
- "ADDRESS=%u.%u.%u.%u",
- ADDRESS_FMT_VAL(address),
- "PREFIXLEN=%u",
- prefixlen,
- NULL);
+ log_struct(LOG_INFO,
+ LOG_LINK_INTERFACE(link),
+ LOG_LINK_MESSAGE(link, "DHCPv4 address %u.%u.%u.%u/%u",
+ ADDRESS_FMT_VAL(address),
+ prefixlen),
+ "ADDRESS=%u.%u.%u.%u", ADDRESS_FMT_VAL(address),
+ "PREFIXLEN=%u", prefixlen,
+ NULL);
link->dhcp_lease = lease;
@@ -475,8 +457,7 @@ static int dhcp_lease_acquired(sd_dhcp_client *client, Link *link) {
if (r >= 0) {
r = link_set_mtu(link, mtu);
if (r < 0)
- log_link_error(link, "Failed to set MTU "
- "to %" PRIu16, mtu);
+ log_link_error_errno(link, r, "Failed to set MTU to %" PRIu16 ": %m", mtu);
}
}
@@ -487,27 +468,21 @@ static int dhcp_lease_acquired(sd_dhcp_client *client, Link *link) {
if (r >= 0) {
r = link_set_hostname(link, hostname);
if (r < 0)
- log_link_error(link,
- "Failed to set transient hostname to '%s'",
- hostname);
+ log_link_error_errno(link, r, "Failed to set transient hostname to '%s': %m", hostname);
}
}
if (!link->network->dhcp_critical) {
- r = sd_dhcp_lease_get_lifetime(link->dhcp_lease,
- &lifetime);
+ r = sd_dhcp_lease_get_lifetime(link->dhcp_lease, &lifetime);
if (r < 0) {
- log_link_warning(link,
- "DHCP error: no lifetime: %s",
- strerror(-r));
+ log_link_warning_errno(link, r, "DHCP error: no lifetime: %m");
return r;
}
}
r = dhcp4_update_address(link, &address, &netmask, lifetime);
if (r < 0) {
- log_link_warning(link, "could not update IP address: %s",
- strerror(-r));
+ log_link_warning_errno(link, r, "Could not update IP address: %m");
link_enter_failed(link);
return r;
}
@@ -661,5 +636,21 @@ int dhcp4_configure(Link *link) {
return r;
}
+ switch (link->network->dhcp_client_identifier) {
+ case DHCP_CLIENT_ID_DUID:
+ /* Library defaults to this. */
+ break;
+ case DHCP_CLIENT_ID_MAC:
+ r = sd_dhcp_client_set_client_id(link->dhcp_client,
+ ARPHRD_ETHER,
+ (const uint8_t *) &link->mac,
+ sizeof (link->mac));
+ if (r < 0)
+ return r;
+ break;
+ default:
+ assert_not_reached("Unknown client identifier type.");
+ }
+
return 0;
}
diff --git a/src/network/networkd-dhcp6.c b/src/network/networkd-dhcp6.c
index bcfad4c03f..33e18137d2 100644
--- a/src/network/networkd-dhcp6.c
+++ b/src/network/networkd-dhcp6.c
@@ -28,6 +28,8 @@
#include "sd-icmp6-nd.h"
#include "sd-dhcp6-client.h"
+static int dhcp6_lease_address_acquired(sd_dhcp6_client *client, Link *link);
+
static int dhcp6_lease_information_acquired(sd_dhcp6_client *client,
Link *link) {
return 0;
@@ -42,6 +44,15 @@ static int dhcp6_address_handler(sd_rtnl *rtnl, sd_rtnl_message *m,
r = sd_rtnl_message_get_errno(m);
if (r < 0 && r != -EEXIST) {
+ if (link->rtnl_extended_attrs) {
+ log_link_warning(link, "Could not set extended netlink attributes, reverting to fallback mechanism");
+
+ link->rtnl_extended_attrs = false;
+ dhcp6_lease_address_acquired(link->dhcp6_client, link);
+
+ return 1;
+ }
+
log_link_error(link, "Could not set DHCPv6 address: %s",
strerror(-r));
@@ -65,61 +76,25 @@ static int dhcp6_address_update(Link *link, struct in6_addr *ip6_addr,
addr->family = AF_INET6;
memcpy(&addr->in_addr.in6, ip6_addr, sizeof(*ip6_addr));
+
+ addr->flags = IFA_F_NOPREFIXROUTE;
addr->prefixlen = prefixlen;
addr->cinfo.ifa_prefered = lifetime_preferred;
addr->cinfo.ifa_valid = lifetime_valid;
- log_link_struct(link, LOG_INFO, "MESSAGE=%-*s: DHCPv6 address "SD_ICMP6_ADDRESS_FORMAT_STR"/%d timeout preferred %d valid %d",
- IFNAMSIZ,
- link->ifname, SD_ICMP6_ADDRESS_FORMAT_VAL(addr->in_addr.in6),
- addr->prefixlen, lifetime_preferred, lifetime_valid,
- NULL);
+ log_link_info(link,
+ "DHCPv6 address "SD_ICMP6_ADDRESS_FORMAT_STR"/%d timeout preferred %d valid %d",
+ SD_ICMP6_ADDRESS_FORMAT_VAL(addr->in_addr.in6),
+ addr->prefixlen, lifetime_preferred, lifetime_valid);
r = address_update(addr, link, dhcp6_address_handler);
if (r < 0)
- log_link_warning(link, "Could not assign DHCPv6 address: %s",
- strerror(-r));
+ log_link_warning_errno(link, r, "Could not assign DHCPv6 address: %m");
return r;
}
-static int dhcp6_prefix_expired(Link *link) {
- int r;
- sd_dhcp6_lease *lease;
- struct in6_addr *expired_prefix, ip6_addr;
- uint8_t expired_prefixlen;
- uint32_t lifetime_preferred, lifetime_valid;
-
- r = sd_icmp6_ra_get_expired_prefix(link->icmp6_router_discovery,
- &expired_prefix, &expired_prefixlen);
- if (r < 0)
- return r;
-
- r = sd_dhcp6_client_get_lease(link->dhcp6_client, &lease);
- if (r < 0)
- return r;
-
- sd_dhcp6_lease_reset_address_iter(lease);
-
- while (sd_dhcp6_lease_get_address(lease, &ip6_addr,
- &lifetime_preferred,
- &lifetime_valid) >= 0) {
-
- r = sd_icmp6_prefix_match(expired_prefix, expired_prefixlen,
- &ip6_addr);
- if (r >= 0) {
- r = dhcp6_address_update(link, &ip6_addr, 128,
- lifetime_preferred,
- lifetime_valid);
-
- return r;
- }
- }
-
- return 0;
-}
-
static int dhcp6_lease_address_acquired(sd_dhcp6_client *client, Link *link) {
int r;
sd_dhcp6_lease *lease;
@@ -296,6 +271,45 @@ static int dhcp6_configure(Link *link, int event) {
return r;
}
+static int dhcp6_prefix_expired(Link *link) {
+ int r;
+ sd_dhcp6_lease *lease;
+ struct in6_addr *expired_prefix, ip6_addr;
+ uint8_t expired_prefixlen;
+ uint32_t lifetime_preferred, lifetime_valid;
+
+ r = sd_icmp6_ra_get_expired_prefix(link->icmp6_router_discovery,
+ &expired_prefix, &expired_prefixlen);
+ if (r < 0)
+ return r;
+
+ r = sd_dhcp6_client_get_lease(link->dhcp6_client, &lease);
+ if (r < 0)
+ return r;
+
+ log_link_info(link, "IPv6 prefix "SD_ICMP6_ADDRESS_FORMAT_STR"/%d expired",
+ SD_ICMP6_ADDRESS_FORMAT_VAL(*expired_prefix),
+ expired_prefixlen);
+
+ sd_dhcp6_lease_reset_address_iter(lease);
+
+ while (sd_dhcp6_lease_get_address(lease, &ip6_addr,
+ &lifetime_preferred,
+ &lifetime_valid) >= 0) {
+
+ r = sd_icmp6_prefix_match(expired_prefix, expired_prefixlen,
+ &ip6_addr);
+ if (r < 0)
+ continue;
+
+ log_link_info(link, "IPv6 prefix length updated "SD_ICMP6_ADDRESS_FORMAT_STR"/%d", SD_ICMP6_ADDRESS_FORMAT_VAL(ip6_addr), 128);
+
+ dhcp6_address_update(link, &ip6_addr, 128, lifetime_preferred, lifetime_valid);
+ }
+
+ return 0;
+}
+
static void icmp6_router_handler(sd_icmp6_nd *nd, int event, void *userdata) {
Link *link = userdata;
@@ -318,7 +332,8 @@ static void icmp6_router_handler(sd_icmp6_nd *nd, int event, void *userdata) {
break;
case ICMP6_EVENT_ROUTER_ADVERTISMENT_PREFIX_EXPIRED:
- dhcp6_prefix_expired(link);
+ if (!link->rtnl_extended_attrs)
+ dhcp6_prefix_expired(link);
break;
diff --git a/src/network/networkd-fdb.c b/src/network/networkd-fdb.c
index feab8d421e..796d1117b6 100644
--- a/src/network/networkd-fdb.c
+++ b/src/network/networkd-fdb.c
@@ -19,16 +19,11 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <ctype.h>
#include <net/if.h>
#include <net/ethernet.h>
#include "networkd.h"
-#include "networkd-netdev.h"
#include "networkd-link.h"
-#include "network-internal.h"
-#include "path-util.h"
-#include "conf-files.h"
#include "conf-parser.h"
#include "util.h"
@@ -93,7 +88,7 @@ static int set_fdb_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
r = sd_rtnl_message_get_errno(m);
if (r < 0 && r != -EEXIST)
- log_link_error(link, "Could not add FDB entry: %s", strerror(-r));
+ log_link_error_errno(link, r, "Could not add FDB entry: %m");
return 1;
}
@@ -138,10 +133,8 @@ int fdb_entry_configure(Link *const link, FdbEntry *const fdb_entry) {
/* send message to the kernel to update its internal static MAC table. */
r = sd_rtnl_call_async(rtnl, req, set_fdb_handler, link, 0, NULL);
- if (r < 0) {
- log_link_error(link, "Could not send rtnetlink message: %s", strerror(-r));
- return r;
- }
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
return 0;
}
@@ -155,9 +148,9 @@ void fdb_entry_free(FdbEntry *fdb_entry) {
LIST_REMOVE(static_fdb_entries, fdb_entry->network->static_fdb_entries,
fdb_entry);
- if(fdb_entry->section)
- hashmap_remove(fdb_entry->network->fdb_entries_by_section,
- UINT_TO_PTR(fdb_entry->section));
+ if (fdb_entry->section)
+ hashmap_remove(fdb_entry->network->fdb_entries_by_section,
+ UINT_TO_PTR(fdb_entry->section));
}
free(fdb_entry->mac_addr);
@@ -166,16 +159,18 @@ void fdb_entry_free(FdbEntry *fdb_entry) {
}
/* parse the HW address from config files. */
-int config_parse_fdb_hwaddr(const char *unit,
- const char *filename,
- unsigned line,
- const char *section,
- unsigned section_line,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
+int config_parse_fdb_hwaddr(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
Network *network = userdata;
_cleanup_fdbentry_free_ FdbEntry *fdb_entry = NULL;
int r;
@@ -187,10 +182,8 @@ int config_parse_fdb_hwaddr(const char *unit,
assert(data);
r = fdb_entry_new_static(network, section_line, &fdb_entry);
- if (r < 0) {
- log_error("Failed to allocate a new FDB entry: %s", strerror(-r));
- return r;
- }
+ if (r < 0)
+ return log_oom();
/* read in the MAC address for the FDB table. */
r = sscanf(rvalue, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
@@ -201,9 +194,8 @@ int config_parse_fdb_hwaddr(const char *unit,
&fdb_entry->mac_addr->ether_addr_octet[4],
&fdb_entry->mac_addr->ether_addr_octet[5]);
- if (ETHER_ADDR_LEN != r) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Not a valid MAC address, ignoring assignment: %s", rvalue);
+ if (ETHER_ADDR_LEN != r) {
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Not a valid MAC address, ignoring assignment: %s", rvalue);
return 0;
}
@@ -213,16 +205,18 @@ int config_parse_fdb_hwaddr(const char *unit,
}
/* parse the VLAN Id from config files. */
-int config_parse_fdb_vlan_id(const char *unit,
- const char *filename,
- unsigned line,
- const char *section,
- unsigned section_line,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
+int config_parse_fdb_vlan_id(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
Network *network = userdata;
_cleanup_fdbentry_free_ FdbEntry *fdb_entry = NULL;
int r;
@@ -234,18 +228,14 @@ int config_parse_fdb_vlan_id(const char *unit,
assert(data);
r = fdb_entry_new_static(network, section_line, &fdb_entry);
- if (r < 0) {
- log_error("Failed to allocate a new FDB entry: %s", strerror(-r));
- return r;
- }
+ if (r < 0)
+ return log_oom();
r = config_parse_unsigned(unit, filename, line, section,
section_line, lvalue, ltype,
rvalue, &fdb_entry->vlan_id, userdata);
- if (r < 0) {
- log_error("Failed to parse the unsigned integer: %s", strerror(-r));
+ if (r < 0)
return r;
- }
fdb_entry = NULL;
diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c
index f716e8231a..b30fd7ae89 100644
--- a/src/network/networkd-link.c
+++ b/src/network/networkd-link.c
@@ -26,14 +26,12 @@
#include "util.h"
#include "virt.h"
#include "fileio.h"
+#include "socket-util.h"
#include "bus-util.h"
#include "udev-util.h"
-#include "libudev-private.h"
#include "network-internal.h"
#include "networkd-link.h"
#include "networkd-netdev.h"
-#include "conf-parser.h"
-#include "dhcp-lease-internal.h"
bool link_dhcp6_enabled(Link *link) {
if (link->flags & IFF_LOOPBACK)
@@ -131,10 +129,8 @@ static int link_update_flags(Link *link, sd_rtnl_message *m) {
assert(link);
r = sd_rtnl_message_link_get_flags(m, &flags);
- if (r < 0) {
- log_link_warning(link, "Could not get link flags");
- return r;
- }
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Could not get link flags: %m");
r = sd_rtnl_message_read_u8(m, IFLA_OPERSTATE, &operstate);
if (r < 0)
@@ -146,7 +142,7 @@ static int link_update_flags(Link *link, sd_rtnl_message *m) {
return 0;
if (link->flags != flags) {
- log_link_debug(link, "flags change:%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
+ log_link_debug(link, "Flags change:%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
FLAG_STRING("LOOPBACK", IFF_LOOPBACK, link->flags, flags),
FLAG_STRING("MASTER", IFF_MASTER, link->flags, flags),
FLAG_STRING("SLAVE", IFF_SLAVE, link->flags, flags),
@@ -180,12 +176,12 @@ static int link_update_flags(Link *link, sd_rtnl_message *m) {
* printing 20 */
if (unknown_flags_added)
log_link_debug(link,
- "unknown link flags gained: %#.5x (ignoring)",
+ "Unknown link flags gained: %#.5x (ignoring)",
unknown_flags_added);
if (unknown_flags_removed)
log_link_debug(link,
- "unknown link flags lost: %#.5x (ignoring)",
+ "Unknown link flags lost: %#.5x (ignoring)",
unknown_flags_removed);
}
@@ -230,6 +226,7 @@ static int link_new(Manager *manager, sd_rtnl_message *message, Link **ret) {
link->n_ref = 1;
link->manager = manager;
link->state = LINK_STATE_PENDING;
+ link->rtnl_extended_attrs = true;
link->ifindex = ifindex;
link->ifname = strdup(ifname);
if (!link->ifname)
@@ -275,6 +272,8 @@ static int link_new(Manager *manager, sd_rtnl_message *message, Link **ret) {
static void link_free(Link *link) {
Address *address;
+ Iterator i;
+ Link *carrier;
if (!link)
return;
@@ -312,6 +311,14 @@ static void link_free(Link *link) {
udev_device_unref(link->udev_device);
+ HASHMAP_FOREACH (carrier, link->bound_to_links, i)
+ hashmap_remove(link->bound_to_links, INT_TO_PTR(carrier->ifindex));
+ hashmap_free(link->bound_to_links);
+
+ HASHMAP_FOREACH (carrier, link->bound_by_links, i)
+ hashmap_remove(link->bound_by_links, INT_TO_PTR(carrier->ifindex));
+ hashmap_free(link->bound_by_links);
+
free(link);
}
@@ -358,23 +365,10 @@ static void link_set_state(Link *link, LinkState state) {
return;
}
-void link_drop(Link *link) {
- if (!link || link->state == LINK_STATE_LINGER)
- return;
-
- link_set_state(link, LINK_STATE_LINGER);
-
- log_link_debug(link, "link removed");
-
- link_unref(link);
-
- return;
-}
-
static void link_enter_unmanaged(Link *link) {
assert(link);
- log_link_debug(link, "unmanaged");
+ log_link_debug(link, "Unmanaged");
link_set_state(link, LINK_STATE_UNMANAGED);
@@ -393,50 +387,32 @@ static int link_stop_clients(Link *link) {
if (link->dhcp_client) {
k = sd_dhcp_client_stop(link->dhcp_client);
- if (k < 0) {
- log_link_warning(link, "Could not stop DHCPv4 client: %s",
- strerror(-r));
- r = k;
- }
+ if (k < 0)
+ r = log_link_warning_errno(link, r, "Could not stop DHCPv4 client: %m");
}
if (link->ipv4ll) {
k = sd_ipv4ll_stop(link->ipv4ll);
- if (k < 0) {
- log_link_warning(link, "Could not stop IPv4 link-local: %s",
- strerror(-r));
- r = k;
- }
+ if (k < 0)
+ r = log_link_warning_errno(link, r, "Could not stop IPv4 link-local: %m");
}
if(link->icmp6_router_discovery) {
-
if (link->dhcp6_client) {
k = sd_dhcp6_client_stop(link->dhcp6_client);
- if (k < 0) {
- log_link_warning(link, "Could not stop DHCPv6 client: %s",
- strerror(-r));
- r = k;
- }
+ if (k < 0)
+ r = log_link_warning_errno(link, r, "Could not stop DHCPv6 client: %m");
}
k = sd_icmp6_nd_stop(link->icmp6_router_discovery);
- if (k < 0) {
- log_link_warning(link,
- "Could not stop ICMPv6 router discovery: %s",
- strerror(-r));
- r = k;
- }
+ if (k < 0)
+ r = log_link_warning_errno(link, r, "Could not stop ICMPv6 router discovery: %m");
}
if (link->lldp) {
-
k = sd_lldp_stop(link->lldp);
- if (k < 0) {
- log_link_warning(link, "Could not stop LLDP : %s",
- strerror(-r));
- r = k;
- }
+ if (k < 0)
+ r = log_link_warning_errno(link, r, "Could not stop LLDP: %m");
}
return r;
@@ -448,7 +424,7 @@ void link_enter_failed(Link *link) {
if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
return;
- log_link_warning(link, "failed");
+ log_link_warning(link, "Failed");
link_set_state(link, LINK_STATE_FAILED);
@@ -491,7 +467,7 @@ static int link_enter_configured(Link *link) {
assert(link->network);
assert(link->state == LINK_STATE_SETTING_ROUTES);
- log_link_info(link, "link configured");
+ log_link_info(link, "Configured");
link_set_state(link, LINK_STATE_CONFIGURED);
@@ -537,10 +513,10 @@ static int route_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
r = sd_rtnl_message_get_errno(m);
if (r < 0 && r != -EEXIST)
- log_link_warning_errno(link, -r, "%-*s: could not set route: %m", IFNAMSIZ, link->ifname);
+ log_link_warning_errno(link, r, "%-*s: could not set route: %m", IFNAMSIZ, link->ifname);
if (link->link_messages == 0) {
- log_link_debug(link, "routes set");
+ log_link_debug(link, "Routes set");
link->static_configured = true;
link_client_handler(link);
}
@@ -561,9 +537,7 @@ static int link_enter_set_routes(Link *link) {
LIST_FOREACH(routes, rt, link->network->static_routes) {
r = route_configure(rt, link, &route_handler);
if (r < 0) {
- log_link_warning(link,
- "could not set routes: %s",
- strerror(-r));
+ log_link_warning_errno(link, r, "Could not set routes: %m");
link_enter_failed(link);
return r;
}
@@ -575,7 +549,7 @@ static int link_enter_set_routes(Link *link) {
link->static_configured = true;
link_client_handler(link);
} else
- log_link_debug(link, "setting routes");
+ log_link_debug(link, "Setting routes");
return 0;
}
@@ -593,7 +567,7 @@ int link_route_drop_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
r = sd_rtnl_message_get_errno(m);
if (r < 0 && r != -ESRCH)
- log_link_warning_errno(link, -r, "%-*s: could not drop route: %m", IFNAMSIZ, link->ifname);
+ log_link_warning_errno(link, r, "%-*s: could not drop route: %m", IFNAMSIZ, link->ifname);
return 1;
}
@@ -617,12 +591,12 @@ static int address_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
r = sd_rtnl_message_get_errno(m);
if (r < 0 && r != -EEXIST)
- log_link_warning_errno(link, -r, "%-*s: could not set address: %m", IFNAMSIZ, link->ifname);
+ log_link_warning_errno(link, r, "%-*s: could not set address: %m", IFNAMSIZ, link->ifname);
else if (r >= 0)
link_rtnl_process_address(rtnl, m, link->manager);
if (link->link_messages == 0) {
- log_link_debug(link, "addresses set");
+ log_link_debug(link, "Addresses set");
link_enter_set_routes(link);
}
@@ -658,8 +632,7 @@ static int link_enter_set_addresses(Link *link) {
address = link_find_dhcp_server_address(link);
if (!address) {
- log_link_warning(link,
- "Failed to find suitable address for DHCPv4 server instance.");
+ log_link_warning(link, "Failed to find suitable address for DHCPv4 server instance.");
link_enter_failed(link);
return 0;
}
@@ -691,21 +664,20 @@ static int link_enter_set_addresses(Link *link) {
r = sd_dhcp_server_start(link->dhcp_server);
if (r < 0) {
- log_link_warning(link, "could not start DHCPv4 server "
- "instance: %s", strerror(-r));
+ log_link_warning_errno(link, r, "Could not start DHCPv4 server instance: %m");
link_enter_failed(link);
return 0;
}
- log_link_debug(link, "offering DHCPv4 leases");
+ log_link_debug(link, "Offering DHCPv4 leases");
}
- if (link->link_messages == 0) {
+ if (link->link_messages == 0)
link_enter_set_routes(link);
- } else
- log_link_debug(link, "setting addresses");
+ else
+ log_link_debug(link, "Setting addresses");
return 0;
}
@@ -723,7 +695,7 @@ int link_address_drop_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata)
r = sd_rtnl_message_get_errno(m);
if (r < 0 && r != -EADDRNOTAVAIL)
- log_link_warning_errno(link, -r, "%-*s: could not drop address: %m", IFNAMSIZ, link->ifname);
+ log_link_warning_errno(link, r, "%-*s: could not drop address: %m", IFNAMSIZ, link->ifname);
return 1;
}
@@ -735,7 +707,7 @@ static int link_set_bridge_fdb(Link *const link) {
LIST_FOREACH(static_fdb_entries, fdb_entry, link->network->static_fdb_entries) {
r = fdb_entry_configure(link, fdb_entry);
if(r < 0) {
- log_link_error(link, "Failed to add MAC entry to static MAC table: %s", strerror(-r));
+ log_link_error_errno(link, r, "Failed to add MAC entry to static MAC table: %m");
break;
}
}
@@ -747,16 +719,11 @@ static int link_set_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
_cleanup_link_unref_ Link *link = userdata;
int r;
- log_link_debug(link, "set link");
+ log_link_debug(link, "Set link");
r = sd_rtnl_message_get_errno(m);
if (r < 0 && r != -EEXIST) {
- log_link_struct(link, LOG_ERR,
- "MESSAGE=%-*s: could not join netdev: %s",
- IFNAMSIZ,
- link->ifname, strerror(-r),
- "ERRNO=%d", -r,
- NULL);
+ log_link_error_errno(link, r, "Could not join netdev: %m");
link_enter_failed(link);
return 1;
}
@@ -764,11 +731,11 @@ static int link_set_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
return 0;
}
-static int set_hostname_handler(sd_bus *bus, sd_bus_message *m, void *userdata,
- sd_bus_error *ret_error) {
+static int set_hostname_handler(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
_cleanup_link_unref_ Link *link = userdata;
int r;
+ assert(m);
assert(link);
if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
@@ -776,8 +743,7 @@ static int set_hostname_handler(sd_bus *bus, sd_bus_message *m, void *userdata,
r = sd_bus_message_get_errno(m);
if (r > 0)
- log_link_warning(link, "Could not set hostname: %s",
- strerror(r));
+ log_link_warning_errno(link, r, "Could not set hostname: %m");
return 1;
}
@@ -794,8 +760,7 @@ int link_set_hostname(Link *link, const char *hostname) {
if (!link->manager->bus) {
/* TODO: replace by assert when we can rely on kdbus */
- log_link_info(link,
- "Not connected to system bus, ignoring transient hostname.");
+ log_link_info(link, "Not connected to system bus, ignoring transient hostname.");
return 0;
}
@@ -815,11 +780,8 @@ int link_set_hostname(Link *link, const char *hostname) {
r = sd_bus_call_async(link->manager->bus, NULL, m, set_hostname_handler,
link, 0);
- if (r < 0) {
- log_link_error(link, "Could not set transient hostname: %s",
- strerror(-r));
- return r;
- }
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not set transient hostname: %m");
link_ref(link);
@@ -839,7 +801,7 @@ static int set_mtu_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
r = sd_rtnl_message_get_errno(m);
if (r < 0)
- log_link_warning_errno(link, -r, "%-*s: could not set MTU: %m", IFNAMSIZ, link->ifname);
+ log_link_warning_errno(link, r, "%-*s: could not set MTU: %m", IFNAMSIZ, link->ifname);
return 1;
}
@@ -852,29 +814,19 @@ int link_set_mtu(Link *link, uint32_t mtu) {
assert(link->manager);
assert(link->manager->rtnl);
- log_link_debug(link, "setting MTU: %" PRIu32, mtu);
+ log_link_debug(link, "Setting MTU: %" PRIu32, mtu);
- r = sd_rtnl_message_new_link(link->manager->rtnl, &req,
- RTM_SETLINK, link->ifindex);
- if (r < 0) {
- log_link_error(link, "Could not allocate RTM_SETLINK message");
- return r;
- }
+ r = sd_rtnl_message_new_link(link->manager->rtnl, &req, RTM_SETLINK, link->ifindex);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not allocate RTM_SETLINK message: %m");
r = sd_rtnl_message_append_u32(req, IFLA_MTU, mtu);
- if (r < 0) {
- log_link_error(link, "Could not append MTU: %s", strerror(-r));
- return r;
- }
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not append MTU: %m");
- r = sd_rtnl_call_async(link->manager->rtnl, req, set_mtu_handler, link,
- 0, NULL);
- if (r < 0) {
- log_link_error(link,
- "Could not send rtnetlink message: %s",
- strerror(-r));
- return r;
- }
+ r = sd_rtnl_call_async(link->manager->rtnl, req, set_mtu_handler, link, 0, NULL);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
link_ref(link);
@@ -891,53 +843,31 @@ static int link_set_bridge(Link *link) {
if(link->network->cost == 0)
return 0;
- r = sd_rtnl_message_new_link(link->manager->rtnl, &req,
- RTM_SETLINK, link->ifindex);
- if (r < 0) {
- log_link_error(link, "Could not allocate RTM_SETLINK message");
- return r;
- }
+ r = sd_rtnl_message_new_link(link->manager->rtnl, &req, RTM_SETLINK, link->ifindex);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not allocate RTM_SETLINK message: %m");
r = sd_rtnl_message_link_set_family(req, PF_BRIDGE);
- if (r < 0) {
- log_link_error(link,
- "Could not set message family %s", strerror(-r));
- return r;
- }
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not set message family: %m");
r = sd_rtnl_message_open_container(req, IFLA_PROTINFO);
- if (r < 0) {
- log_link_error(link,
- "Could not append IFLA_PROTINFO attribute: %s",
- strerror(-r));
- return r;
- }
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not append IFLA_PROTINFO attribute: %m");
if(link->network->cost != 0) {
r = sd_rtnl_message_append_u32(req, IFLA_BRPORT_COST, link->network->cost);
- if (r < 0) {
- log_link_error(link,
- "Could not append IFLA_BRPORT_COST attribute: %s",
- strerror(-r));
- return r;
- }
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not append IFLA_BRPORT_COST attribute: %m");
}
r = sd_rtnl_message_close_container(req);
- if (r < 0) {
- log_link_error(link,
- "Could not append IFLA_LINKINFO attribute: %s",
- strerror(-r));
- return r;
- }
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not append IFLA_LINKINFO attribute: %m");
r = sd_rtnl_call_async(link->manager->rtnl, req, link_set_handler, link, 0, NULL);
- if (r < 0) {
- log_link_error(link,
- "Could not send rtnetlink message: %s",
- strerror(-r));
- return r;
- }
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
link_ref(link);
@@ -957,7 +887,7 @@ static void lldp_handler(sd_lldp *lldp, int event, void *userdata) {
r = sd_lldp_save(link->lldp, link->lldp_file);
if (r < 0)
- log_link_warning(link, "could not save LLDP");
+ log_link_warning_errno(link, r, "Could not save LLDP: %m");
}
@@ -972,40 +902,31 @@ static int link_acquire_conf(Link *link) {
if (link_ipv4ll_enabled(link)) {
assert(link->ipv4ll);
- log_link_debug(link, "acquiring IPv4 link-local address");
+ log_link_debug(link, "Acquiring IPv4 link-local address");
r = sd_ipv4ll_start(link->ipv4ll);
- if (r < 0) {
- log_link_warning(link, "could not acquire IPv4 "
- "link-local address");
- return r;
- }
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Could not acquire IPv4 link-local address: %m");
}
if (link_dhcp4_enabled(link)) {
assert(link->dhcp_client);
- log_link_debug(link, "acquiring DHCPv4 lease");
+ log_link_debug(link, "Acquiring DHCPv4 lease");
r = sd_dhcp_client_start(link->dhcp_client);
- if (r < 0) {
- log_link_warning(link, "could not acquire DHCPv4 "
- "lease");
- return r;
- }
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Could not acquire DHCPv4 lease: %m");
}
if (link_dhcp6_enabled(link)) {
assert(link->icmp6_router_discovery);
- log_link_debug(link, "discovering IPv6 routers");
+ log_link_debug(link, "Discovering IPv6 routers");
r = sd_icmp6_router_solicitation_start(link->icmp6_router_discovery);
- if (r < 0) {
- log_link_warning(link,
- "could not start IPv6 router discovery");
- return r;
- }
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Could not start IPv6 router discovery: %m");
}
if (link_lldp_enabled(link)) {
@@ -1014,10 +935,8 @@ static int link_acquire_conf(Link *link) {
log_link_debug(link, "Starting LLDP");
r = sd_lldp_start(link->lldp);
- if (r < 0) {
- log_link_warning(link, "could not start LLDP ");
- return r;
- }
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Could not start LLDP: %m");
}
return 0;
@@ -1047,11 +966,10 @@ static int link_up_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
return 1;
r = sd_rtnl_message_get_errno(m);
- if (r < 0) {
- /* we warn but don't fail the link, as it may
- be brought up later */
- log_link_warning_errno(link, -r, "%-*s: could not bring up interface: %m", IFNAMSIZ, link->ifname);
- }
+ if (r < 0)
+ /* we warn but don't fail the link, as it may be
+ brought up later */
+ log_link_warning_errno(link, r, "%-*s: could not bring up interface: %m", IFNAMSIZ, link->ifname);
return 1;
}
@@ -1066,98 +984,370 @@ static int link_up(Link *link) {
assert(link->manager);
assert(link->manager->rtnl);
- log_link_debug(link, "bringing link up");
+ log_link_debug(link, "Bringing link up");
- r = sd_rtnl_message_new_link(link->manager->rtnl, &req,
- RTM_SETLINK, link->ifindex);
- if (r < 0) {
- log_link_error(link, "Could not allocate RTM_SETLINK message");
- return r;
- }
+ r = sd_rtnl_message_new_link(link->manager->rtnl, &req, RTM_SETLINK, link->ifindex);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not allocate RTM_SETLINK message: %m");
r = sd_rtnl_message_link_set_flags(req, IFF_UP, IFF_UP);
- if (r < 0) {
- log_link_error(link, "Could not set link flags: %s",
- strerror(-r));
- return r;
- }
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not set link flags: %m");
if (link->network->mac) {
r = sd_rtnl_message_append_ether_addr(req, IFLA_ADDRESS, link->network->mac);
- if (r < 0) {
- log_link_error(link, "Could not set MAC address: %s", strerror(-r));
- return r;
- }
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not set MAC address: %m");
}
if (link->network->mtu) {
r = sd_rtnl_message_append_u32(req, IFLA_MTU, link->network->mtu);
- if (r < 0) {
- log_link_error(link, "Could not set MTU: %s", strerror(-r));
- return r;
- }
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not set MTU: %m");
}
r = sd_rtnl_message_open_container(req, IFLA_AF_SPEC);
- if (r < 0) {
- log_link_error(link, "Could not open IFLA_AF_SPEC container: %s", strerror(-r));
- return r;
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not open IFLA_AF_SPEC container: %m");
+
+ if (socket_ipv6_is_supported()) {
+ /* if the kernel lacks ipv6 support setting IFF_UP fails if any ipv6 options are passed */
+ r = sd_rtnl_message_open_container(req, AF_INET6);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not open AF_INET6 container: %m");
+
+ ipv6ll_mode = link_ipv6ll_enabled(link) ? IN6_ADDR_GEN_MODE_EUI64 : IN6_ADDR_GEN_MODE_NONE;
+ r = sd_rtnl_message_append_u8(req, IFLA_INET6_ADDR_GEN_MODE, ipv6ll_mode);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not append IFLA_INET6_ADDR_GEN_MODE: %m");
+
+ if (!in_addr_is_null(AF_INET6, &link->network->ipv6_token)) {
+ r = sd_rtnl_message_append_in6_addr(req, IFLA_INET6_TOKEN, &link->network->ipv6_token.in6);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not append IFLA_INET6_TOKEN: %m");
+ }
+
+ r = sd_rtnl_message_close_container(req);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not close AF_INET6 container: %m");
}
- r = sd_rtnl_message_open_container(req, AF_INET6);
- if (r < 0) {
- log_link_error(link, "Could not open AF_INET6 container: %s", strerror(-r));
- return r;
+ r = sd_rtnl_message_close_container(req);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not close IFLA_AF_SPEC container: %m");
+
+ r = sd_rtnl_call_async(link->manager->rtnl, req, link_up_handler, link, 0, NULL);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
+
+ link_ref(link);
+
+ return 0;
+}
+
+static int link_down_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
+ _cleanup_link_unref_ Link *link = userdata;
+ int r;
+
+ assert(link);
+
+ if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
+ return 1;
+
+ r = sd_rtnl_message_get_errno(m);
+ if (r < 0)
+ log_link_warning_errno(link, r, "%-*s: could not bring down interface: %m", IFNAMSIZ, link->ifname);
+
+ return 1;
+}
+
+static int link_down(Link *link) {
+ _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL;
+ int r;
+
+ assert(link);
+ assert(link->manager);
+ assert(link->manager->rtnl);
+
+ log_link_debug(link, "Bringing link down");
+
+ r = sd_rtnl_message_new_link(link->manager->rtnl, &req,
+ RTM_SETLINK, link->ifindex);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not allocate RTM_SETLINK message: %m");
+
+ r = sd_rtnl_message_link_set_flags(req, 0, IFF_UP);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not set link flags: %m");
+
+ r = sd_rtnl_call_async(link->manager->rtnl, req, link_down_handler, link, 0, NULL);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
+
+ link_ref(link);
+
+ return 0;
+}
+
+static int link_handle_bound_to_list(Link *link) {
+ Link *l;
+ Iterator i;
+ int r;
+ bool required_up = false;
+ bool link_is_up = false;
+
+ assert(link);
+
+ if (hashmap_isempty(link->bound_to_links))
+ return 0;
+
+ if (link->flags & IFF_UP)
+ link_is_up = true;
+
+ HASHMAP_FOREACH (l, link->bound_to_links, i)
+ if (link_has_carrier(l)) {
+ required_up = true;
+ break;
+ }
+
+ if (!required_up && link_is_up) {
+ r = link_down(link);
+ if (r < 0)
+ return r;
+ } else if (required_up && !link_is_up) {
+ r = link_up(link);
+ if (r < 0)
+ return r;
}
- ipv6ll_mode = link_ipv6ll_enabled(link) ? IN6_ADDR_GEN_MODE_EUI64 : IN6_ADDR_GEN_MODE_NONE;
- r = sd_rtnl_message_append_u8(req, IFLA_INET6_ADDR_GEN_MODE, ipv6ll_mode);
- if (r < 0) {
- log_link_error(link, "Could not append IFLA_INET6_ADDR_GEN_MODE: %s", strerror(-r));
+ return 0;
+}
+
+static int link_handle_bound_by_list(Link *link) {
+ Iterator i;
+ Link *l;
+ int r;
+
+ assert(link);
+
+ if (hashmap_isempty(link->bound_by_links))
+ return 0;
+
+ HASHMAP_FOREACH (l, link->bound_by_links, i) {
+ r = link_handle_bound_to_list(l);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+static int link_put_carrier(Link *link, Link *carrier, Hashmap **h) {
+ int r;
+
+ assert(link);
+ assert(carrier);
+
+ if (link == carrier)
+ return 0;
+
+ if (hashmap_get(*h, INT_TO_PTR(carrier->ifindex)))
+ return 0;
+
+ r = hashmap_ensure_allocated(h, NULL);
+ if (r < 0)
+ return r;
+
+ r = hashmap_put(*h, INT_TO_PTR(carrier->ifindex), carrier);
+ if (r < 0)
return r;
+
+ return 0;
+}
+
+static int link_new_bound_by_list(Link *link) {
+ Manager *m;
+ Link *carrier;
+ Iterator i;
+ int r;
+ bool list_updated = false;
+
+ assert(link);
+ assert(link->manager);
+
+ m = link->manager;
+
+ HASHMAP_FOREACH (carrier, m->links, i) {
+ if (!carrier->network)
+ continue;
+
+ if (strv_isempty(carrier->network->bind_carrier))
+ continue;
+
+ if (strv_fnmatch(carrier->network->bind_carrier, link->ifname, 0)) {
+ r = link_put_carrier(link, carrier, &link->bound_by_links);
+ if (r < 0)
+ return r;
+
+ list_updated = true;
+ }
}
- if (!in_addr_is_null(AF_INET6, &link->network->ipv6_token)) {
- r = sd_rtnl_message_append_in6_addr(req, IFLA_INET6_TOKEN, &link->network->ipv6_token.in6);
- if (r < 0) {
- log_link_error(link, "Could not append IFLA_INET6_TOKEN: %s", strerror(-r));
+ if (list_updated)
+ link_save(link);
+
+ HASHMAP_FOREACH (carrier, link->bound_by_links, i) {
+ r = link_put_carrier(carrier, link, &carrier->bound_to_links);
+ if (r < 0)
return r;
+
+ link_save(carrier);
+ }
+
+ return 0;
+}
+
+static int link_new_bound_to_list(Link *link) {
+ Manager *m;
+ Link *carrier;
+ Iterator i;
+ int r;
+ bool list_updated = false;
+
+ assert(link);
+ assert(link->manager);
+
+ if (!link->network)
+ return 0;
+
+ if (strv_isempty(link->network->bind_carrier))
+ return 0;
+
+ m = link->manager;
+
+ HASHMAP_FOREACH (carrier, m->links, i) {
+ if (strv_fnmatch(link->network->bind_carrier, carrier->ifname, 0)) {
+ r = link_put_carrier(link, carrier, &link->bound_to_links);
+ if (r < 0)
+ return r;
+
+ list_updated = true;
}
}
- r = sd_rtnl_message_close_container(req);
- if (r < 0) {
- log_link_error(link, "Could not close AF_INET6 container: %s", strerror(-r));
- return r;
+ if (list_updated)
+ link_save(link);
+
+ HASHMAP_FOREACH (carrier, link->bound_to_links, i) {
+ r = link_put_carrier(carrier, link, &carrier->bound_by_links);
+ if (r < 0)
+ return r;
+
+ link_save(carrier);
}
- r = sd_rtnl_message_close_container(req);
- if (r < 0) {
- log_link_error(link, "Could not close IFLA_AF_SPEC container: %s", strerror(-r));
+ return 0;
+}
+
+static int link_new_carrier_maps(Link *link) {
+ int r;
+
+ r = link_new_bound_by_list(link);
+ if (r < 0)
return r;
- }
- r = sd_rtnl_call_async(link->manager->rtnl, req, link_up_handler, link,
- 0, NULL);
- if (r < 0) {
- log_link_error(link,
- "Could not send rtnetlink message: %s",
- strerror(-r));
+ r = link_handle_bound_by_list(link);
+ if (r < 0)
return r;
- }
- link_ref(link);
+ r = link_new_bound_to_list(link);
+ if (r < 0)
+ return r;
+
+ r = link_handle_bound_to_list(link);
+ if (r < 0)
+ return r;
return 0;
}
+static void link_free_bound_to_list(Link *link) {
+ Link *bound_to;
+ Iterator i;
+
+ HASHMAP_FOREACH (bound_to, link->bound_to_links, i) {
+ hashmap_remove(link->bound_to_links, INT_TO_PTR(bound_to->ifindex));
+
+ if (hashmap_remove(bound_to->bound_by_links, INT_TO_PTR(link->ifindex)))
+ link_save(bound_to);
+ }
+
+ return;
+}
+
+static void link_free_bound_by_list(Link *link) {
+ Link *bound_by;
+ Iterator i;
+
+ HASHMAP_FOREACH (bound_by, link->bound_by_links, i) {
+ hashmap_remove(link->bound_by_links, INT_TO_PTR(bound_by->ifindex));
+
+ if (hashmap_remove(bound_by->bound_to_links, INT_TO_PTR(link->ifindex))) {
+ link_save(bound_by);
+ link_handle_bound_to_list(bound_by);
+ }
+ }
+
+ return;
+}
+
+static void link_free_carrier_maps(Link *link) {
+ bool list_updated = false;
+
+ assert(link);
+
+ if (!hashmap_isempty(link->bound_to_links)) {
+ link_free_bound_to_list(link);
+ list_updated = true;
+ }
+
+ if (!hashmap_isempty(link->bound_by_links)) {
+ link_free_bound_by_list(link);
+ list_updated = true;
+ }
+
+ if (list_updated)
+ link_save(link);
+
+ return;
+}
+
+void link_drop(Link *link) {
+ if (!link || link->state == LINK_STATE_LINGER)
+ return;
+
+ link_set_state(link, LINK_STATE_LINGER);
+
+ link_free_carrier_maps(link);
+
+ log_link_debug(link, "Link removed");
+
+ link_unref(link);
+
+ return;
+}
+
static int link_joined(Link *link) {
int r;
assert(link);
assert(link->network);
- if (!(link->flags & IFF_UP)) {
+ if (!hashmap_isempty(link->bound_to_links)) {
+ r = link_handle_bound_to_list(link);
+ if (r < 0)
+ return r;
+ } else if (!(link->flags & IFF_UP)) {
r = link_up(link);
if (r < 0) {
link_enter_failed(link);
@@ -1167,11 +1357,8 @@ static int link_joined(Link *link) {
if(link->network->bridge) {
r = link_set_bridge(link);
- if (r < 0) {
- log_link_error(link,
- "Could not set bridge message: %s",
- strerror(-r));
- }
+ if (r < 0)
+ log_link_error_errno(link, r, "Could not set bridge message: %m");
}
return link_enter_set_addresses(link);
@@ -1192,11 +1379,11 @@ static int netdev_join_handler(sd_rtnl *rtnl, sd_rtnl_message *m,
r = sd_rtnl_message_get_errno(m);
if (r < 0 && r != -EEXIST) {
- log_link_error_errno(link, -r, "%-*s: could not join netdev: %m", IFNAMSIZ, link->ifname);
+ log_link_error_errno(link, r, "%-*s: could not join netdev: %m", IFNAMSIZ, link->ifname);
link_enter_failed(link);
return 1;
} else
- log_link_debug(link, "joined netdev");
+ log_link_debug(link, "Joined netdev");
if (link->enslaving <= 0)
link_joined(link);
@@ -1223,22 +1410,20 @@ static int link_enter_join_netdev(Link *link) {
return link_joined(link);
if (link->network->bond) {
- log_link_struct(link, LOG_DEBUG,
- "MESSAGE=%-*s: enslaving by '%s'",
- IFNAMSIZ,
- link->ifname, link->network->bond->ifname,
- NETDEVIF(link->network->bond),
- NULL);
-
- r = netdev_join(link->network->bond, link, &netdev_join_handler);
+ log_struct(LOG_DEBUG,
+ LOG_LINK_INTERFACE(link),
+ LOG_NETDEV_INTERFACE(link->network->bond),
+ LOG_LINK_MESSAGE(link, "Enslaving by '%s'", link->network->bond->ifname),
+ NULL);
+
+ r = netdev_join(link->network->bond, link, netdev_join_handler);
if (r < 0) {
- log_link_struct(link, LOG_WARNING,
- "MESSAGE=%-*s: could not join netdev '%s': %s",
- IFNAMSIZ,
- link->ifname, link->network->bond->ifname,
- strerror(-r),
- NETDEVIF(link->network->bond),
- NULL);
+ log_struct_errno(LOG_WARNING, r,
+ LOG_LINK_INTERFACE(link),
+ LOG_NETDEV_INTERFACE(link->network->bond),
+ LOG_LINK_MESSAGE(link, "Could not join netdev '%s': %m", link->network->bond->ifname),
+ NULL);
+
link_enter_failed(link);
return r;
}
@@ -1247,23 +1432,19 @@ static int link_enter_join_netdev(Link *link) {
}
if (link->network->bridge) {
- log_link_struct(link, LOG_DEBUG,
- "MESSAGE=%-*s: enslaving by '%s'",
- IFNAMSIZ,
- link->ifname, link->network->bridge->ifname,
- NETDEVIF(link->network->bridge),
- NULL);
-
- r = netdev_join(link->network->bridge, link,
- &netdev_join_handler);
+ log_struct(LOG_DEBUG,
+ LOG_LINK_INTERFACE(link),
+ LOG_NETDEV_INTERFACE(link->network->bridge),
+ LOG_LINK_MESSAGE(link, "Enslaving by '%s'", link->network->bridge->ifname),
+ NULL);
+
+ r = netdev_join(link->network->bridge, link, netdev_join_handler);
if (r < 0) {
- log_link_struct(link, LOG_WARNING,
- "MESSAGE=%-*s: could not join netdev '%s': %s",
- IFNAMSIZ,
- link->ifname, link->network->bridge->ifname,
- strerror(-r),
- NETDEVIF(link->network->bridge),
- NULL);
+ log_struct_errno(LOG_WARNING, r,
+ LOG_LINK_INTERFACE(link),
+ LOG_NETDEV_INTERFACE(link->network->bridge),
+ LOG_LINK_MESSAGE(link, "Could not join netdev '%s': %m", link->network->bridge->ifname),
+ NULL),
link_enter_failed(link);
return r;
}
@@ -1272,20 +1453,20 @@ static int link_enter_join_netdev(Link *link) {
}
HASHMAP_FOREACH(netdev, link->network->stacked_netdevs, i) {
- log_link_struct(link, LOG_DEBUG,
- "MESSAGE=%-*s: enslaving by '%s'",
- IFNAMSIZ,
- link->ifname, netdev->ifname, NETDEVIF(netdev),
- NULL);
- r = netdev_join(netdev, link, &netdev_join_handler);
+ log_struct(LOG_DEBUG,
+ LOG_LINK_INTERFACE(link),
+ LOG_NETDEV_INTERFACE(netdev),
+ LOG_LINK_MESSAGE(link, "Enslaving by '%s'", netdev->ifname),
+ NULL);
+
+ r = netdev_join(netdev, link, netdev_join_handler);
if (r < 0) {
- log_link_struct(link, LOG_WARNING,
- "MESSAGE=%-*s: could not join netdev '%s': %s",
- IFNAMSIZ,
- link->ifname, netdev->ifname,
- strerror(-r),
- NETDEVIF(netdev), NULL);
+ log_struct_errno(LOG_WARNING, r,
+ LOG_LINK_INTERFACE(link),
+ LOG_NETDEV_INTERFACE(netdev),
+ LOG_LINK_MESSAGE(link, "Could not join netdev '%s': %m", netdev->ifname),
+ NULL);
link_enter_failed(link);
return r;
}
@@ -1298,39 +1479,16 @@ static int link_enter_join_netdev(Link *link) {
static int link_set_ipv4_forward(Link *link) {
const char *p = NULL;
- bool b;
int r;
- b = link_ipv4_forward_enabled(link);
+ if (link->network->ip_forward == ADDRESS_FAMILY_KERNEL)
+ return 0;
p = strjoina("/proc/sys/net/ipv4/conf/", link->ifname, "/forwarding");
- r = write_string_file_no_create(p, one_zero(b));
+ r = write_string_file_no_create(p, one_zero(link_ipv4_forward_enabled(link)));
if (r < 0)
log_link_warning_errno(link, r, "Cannot configure IPv4 forwarding for interface %s: %m", link->ifname);
- if (b) {
- _cleanup_free_ char *buf = NULL;
-
- /* If IP forwarding is turned on for this interface,
- * then propagate this to the global setting. Given
- * that turning this on has side-effects on other
- * fields, we'll try to avoid doing this unless
- * necessary, hence check the previous value
- * first. Note that we never turn this option off
- * again, since all interfaces we manage do not do
- * forwarding anyway by default, and ownership rules
- * of this control are so unclear. */
-
- r = read_one_line_file("/proc/sys/net/ipv4/ip_forward", &buf);
- if (r < 0)
- log_link_warning_errno(link, r, "Cannot read /proc/sys/net/ipv4/ip_forward: %m");
- else if (!streq(buf, "1")) {
- r = write_string_file_no_create("/proc/sys/net/ipv4/ip_forward", "1");
- if (r < 0)
- log_link_warning_errno(link, r, "Cannot write /proc/sys/net/ipv4/ip_forward: %m");
- }
- }
-
return 0;
}
@@ -1338,6 +1496,13 @@ static int link_set_ipv6_forward(Link *link) {
const char *p = NULL;
int r;
+ /* Make this a NOP if IPv6 is not available */
+ if (!socket_ipv6_is_supported())
+ return 0;
+
+ if (link->network->ip_forward == ADDRESS_FAMILY_KERNEL)
+ return 0;
+
p = strjoina("/proc/sys/net/ipv6/conf/", link->ifname, "/forwarding");
r = write_string_file_no_create(p, one_zero(link_ipv6_forward_enabled(link)));
if (r < 0)
@@ -1430,7 +1595,15 @@ static int link_initialized_and_synced(sd_rtnl *rtnl, sd_rtnl_message *m,
if (link->state != LINK_STATE_PENDING)
return 1;
- log_link_debug(link, "link state is up-to-date");
+ log_link_debug(link, "Link state is up-to-date");
+
+ r = link_new_bound_by_list(link);
+ if (r < 0)
+ return r;
+
+ r = link_handle_bound_by_list(link);
+ if (r < 0)
+ return r;
r = network_get(link->manager, link->udev_device, link->ifname,
&link->mac, &network);
@@ -1442,19 +1615,23 @@ static int link_initialized_and_synced(sd_rtnl *rtnl, sd_rtnl_message *m,
if (link->flags & IFF_LOOPBACK) {
if (network->link_local != ADDRESS_FAMILY_NO)
- log_link_debug(link, "ignoring link-local autoconfiguration for loopback link");
+ log_link_debug(link, "Ignoring link-local autoconfiguration for loopback link");
if (network->dhcp != ADDRESS_FAMILY_NO)
- log_link_debug(link, "ignoring DHCP clients for loopback link");
+ log_link_debug(link, "Ignoring DHCP clients for loopback link");
if (network->dhcp_server)
- log_link_debug(link, "ignoring DHCP server for loopback link");
+ log_link_debug(link, "Ignoring DHCP server for loopback link");
}
r = network_apply(link->manager, network, link);
if (r < 0)
return r;
+ r = link_new_bound_to_list(link);
+ if (r < 0)
+ return r;
+
r = link_configure(link);
if (r < 0)
return r;
@@ -1519,6 +1696,7 @@ int link_rtnl_process_address(sd_rtnl *rtnl, sd_rtnl_message *message, void *use
Link *link = NULL;
uint16_t type;
_cleanup_address_free_ Address *address = NULL;
+ unsigned char flags;
Address *existing;
char buf[INET6_ADDRSTRLEN], valid_buf[FORMAT_TIMESPAN_MAX];
const char *valid_str = NULL;
@@ -1538,13 +1716,16 @@ int link_rtnl_process_address(sd_rtnl *rtnl, sd_rtnl_message *message, void *use
r = sd_rtnl_message_get_type(message, &type);
if (r < 0) {
- log_warning("rtnl: could not get message type");
+ log_warning_errno(r, "rtnl: could not get message type: %m");
+ return 0;
+ } else if (type != RTM_NEWADDR && type != RTM_DELADDR) {
+ log_warning("rtnl: received unexpected message type when processing address");
return 0;
}
r = sd_rtnl_message_addr_get_ifindex(message, &ifindex);
if (r < 0) {
- log_warning_errno(r, "rtnl: could not get ifindex: %m");
+ log_warning_errno(r, "rtnl: could not get ifindex from address: %m");
return 0;
} else if (ifindex <= 0) {
log_warning("rtnl: received address message with invalid ifindex: %d", ifindex);
@@ -1566,33 +1747,34 @@ int link_rtnl_process_address(sd_rtnl *rtnl, sd_rtnl_message *message, void *use
r = sd_rtnl_message_addr_get_family(message, &address->family);
if (r < 0 || !IN_SET(address->family, AF_INET, AF_INET6)) {
- log_link_warning(link, "rtnl: received address with invalid family, ignoring");
+ log_link_warning(link, "rtnl: received address with invalid family, ignoring.");
return 0;
}
r = sd_rtnl_message_addr_get_prefixlen(message, &address->prefixlen);
if (r < 0) {
- log_link_warning(link, "rtnl: received address with invalid prefixlen, ignoring");
+ log_link_warning_errno(link, r, "rtnl: received address with invalid prefixlen, ignoring: %m");
return 0;
}
r = sd_rtnl_message_addr_get_scope(message, &address->scope);
if (r < 0) {
- log_link_warning(link, "rtnl: received address with invalid scope, ignoring");
+ log_link_warning_errno(link, r, "rtnl: received address with invalid scope, ignoring: %m");
return 0;
}
- r = sd_rtnl_message_addr_get_flags(message, &address->flags);
+ r = sd_rtnl_message_addr_get_flags(message, &flags);
if (r < 0) {
- log_link_warning(link, "rtnl: received address with invalid flags, ignoring");
+ log_link_warning_errno(link, r, "rtnl: received address with invalid flags, ignoring: %m");
return 0;
}
+ address->flags = flags;
switch (address->family) {
case AF_INET:
r = sd_rtnl_message_read_in_addr(message, IFA_LOCAL, &address->in_addr.in);
if (r < 0) {
- log_link_warning(link, "rtnl: received address without valid address, ignoring");
+ log_link_warning_errno(link, r, "rtnl: received address without valid address, ignoring: %m");
return 0;
}
@@ -1601,7 +1783,7 @@ int link_rtnl_process_address(sd_rtnl *rtnl, sd_rtnl_message *message, void *use
case AF_INET6:
r = sd_rtnl_message_read_in6_addr(message, IFA_ADDRESS, &address->in_addr.in6);
if (r < 0) {
- log_link_warning(link, "rtnl: received address without valid address, ignoring");
+ log_link_warning_errno(link, r, "rtnl: received address without valid address, ignoring: %m");
return 0;
}
@@ -1612,7 +1794,7 @@ int link_rtnl_process_address(sd_rtnl *rtnl, sd_rtnl_message *message, void *use
}
if (!inet_ntop(address->family, &address->in_addr, buf, INET6_ADDRSTRLEN)) {
- log_link_warning(link, "could not print address");
+ log_link_warning(link, "Could not print address");
return 0;
}
@@ -1686,17 +1868,14 @@ int link_add(Manager *m, sd_rtnl_message *message, Link **ret) {
link = *ret;
- log_link_debug(link, "link %d added", link->ifindex);
+ log_link_debug(link, "Link %d added", link->ifindex);
if (detect_container(NULL) <= 0) {
/* not in a container, udev will be around */
sprintf(ifindex_str, "n%d", link->ifindex);
device = udev_device_new_from_device_id(m->udev, ifindex_str);
- if (!device) {
- log_link_warning(link,
- "could not find udev device: %m");
- return -errno;
- }
+ if (!device)
+ return log_link_warning_errno(link, errno, "Could not find udev device: %m");
if (udev_device_get_is_initialized(device) <= 0) {
/* not yet ready */
@@ -1732,6 +1911,10 @@ static int link_carrier_gained(Link *link) {
}
}
+ r = link_handle_bound_by_list(link);
+ if (r < 0)
+ return r;
+
return 0;
}
@@ -1746,6 +1929,10 @@ static int link_carrier_lost(Link *link) {
return r;
}
+ r = link_handle_bound_by_list(link);
+ if (r < 0)
+ return r;
+
return 0;
}
@@ -1763,7 +1950,7 @@ int link_carrier_reset(Link *link) {
if (r < 0)
return r;
- log_link_info(link, "reset carrier");
+ log_link_info(link, "Reset carrier");
}
return 0;
@@ -1783,18 +1970,28 @@ int link_update(Link *link, sd_rtnl_message *m) {
if (link->state == LINK_STATE_LINGER) {
link_ref(link);
- log_link_info(link, "link readded");
+ log_link_info(link, "Link readded");
link_set_state(link, LINK_STATE_ENSLAVING);
+
+ r = link_new_carrier_maps(link);
+ if (r < 0)
+ return r;
}
r = sd_rtnl_message_read_string(m, IFLA_IFNAME, &ifname);
if (r >= 0 && !streq(ifname, link->ifname)) {
- log_link_info(link, "renamed to %s", ifname);
+ log_link_info(link, "Renamed to %s", ifname);
+
+ link_free_carrier_maps(link);
free(link->ifname);
link->ifname = strdup(ifname);
if (!link->ifname)
return -ENOMEM;
+
+ r = link_new_carrier_maps(link);
+ if (r < 0)
+ return r;
}
r = sd_rtnl_message_read_u32(m, IFLA_MTU, &mtu);
@@ -1802,17 +1999,14 @@ int link_update(Link *link, sd_rtnl_message *m) {
link->mtu = mtu;
if (!link->original_mtu) {
link->original_mtu = mtu;
- log_link_debug(link, "saved original MTU: %"
- PRIu32, link->original_mtu);
+ log_link_debug(link, "Saved original MTU: %" PRIu32, link->original_mtu);
}
if (link->dhcp_client) {
r = sd_dhcp_client_set_mtu(link->dhcp_client,
link->mtu);
if (r < 0) {
- log_link_warning(link,
- "Could not update MTU in DHCP client: %s",
- strerror(-r));
+ log_link_warning_errno(link, r, "Could not update MTU in DHCP client: %m");
return r;
}
}
@@ -1839,12 +2033,8 @@ int link_update(Link *link, sd_rtnl_message *m) {
if (link->ipv4ll) {
r = sd_ipv4ll_set_mac(link->ipv4ll, &link->mac);
- if (r < 0) {
- log_link_warning(link,
- "Could not update MAC address in IPv4LL client: %s",
- strerror(-r));
- return r;
- }
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Could not update MAC address in IPv4LL client: %m");
}
if (link->dhcp_client) {
@@ -1852,12 +2042,8 @@ int link_update(Link *link, sd_rtnl_message *m) {
(const uint8_t *) &link->mac,
sizeof (link->mac),
ARPHRD_ETHER);
- if (r < 0) {
- log_link_warning(link,
- "Could not update MAC address in DHCP client: %s",
- strerror(-r));
- return r;
- }
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Could not update MAC address in DHCP client: %m");
}
if (link->dhcp6_client) {
@@ -1865,12 +2051,8 @@ int link_update(Link *link, sd_rtnl_message *m) {
(const uint8_t *) &link->mac,
sizeof (link->mac),
ARPHRD_ETHER);
- if (r < 0) {
- log_link_warning(link,
- "Could not update MAC address in DHCPv6 client: %s",
- strerror(-r));
- return r;
- }
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Could not update MAC address in DHCPv6 client: %m");
}
}
}
@@ -1885,13 +2067,13 @@ int link_update(Link *link, sd_rtnl_message *m) {
carrier_lost = had_carrier && !link_has_carrier(link);
if (carrier_gained) {
- log_link_info(link, "gained carrier");
+ log_link_info(link, "Gained carrier");
r = link_carrier_gained(link);
if (r < 0)
return r;
} else if (carrier_lost) {
- log_link_info(link, "lost carrier");
+ log_link_info(link, "Lost carrier");
r = link_carrier_lost(link);
if (r < 0)
@@ -1971,7 +2153,7 @@ int link_save(Link *link) {
r = fopen_temporary(link->state_file, &f, &temp_path);
if (r < 0)
- return r;
+ goto fail;
fchmod(fileno(f), 0644);
@@ -2063,6 +2245,38 @@ int link_save(Link *link) {
llmnr_support_to_string(link->network->llmnr));
}
+ if (!hashmap_isempty(link->bound_to_links)) {
+ Link *carrier;
+ Iterator i;
+ bool space = false;
+
+ fputs("CARRIER_BOUND_TO=", f);
+ HASHMAP_FOREACH(carrier, link->bound_to_links, i) {
+ if (space)
+ fputc(' ', f);
+ fputs(carrier->ifname, f);
+ space = true;
+ }
+
+ fputs("\n", f);
+ }
+
+ if (!hashmap_isempty(link->bound_by_links)) {
+ Link *carrier;
+ Iterator i;
+ bool space = false;
+
+ fputs("CARRIER_BOUND_BY=", f);
+ HASHMAP_FOREACH(carrier, link->bound_by_links, i) {
+ if (space)
+ fputc(' ', f);
+ fputs(carrier->ifname, f);
+ space = true;
+ }
+
+ fputs("\n", f);
+ }
+
if (link->dhcp_lease) {
assert(link->network);
@@ -2100,9 +2314,12 @@ int link_save(Link *link) {
return 0;
fail:
- log_link_error(link, "Failed to save link data to %s: %s", link->state_file, strerror(-r));
- unlink(link->state_file);
- unlink(temp_path);
+ log_link_error_errno(link, r, "Failed to save link data to %s: %m", link->state_file);
+ (void) unlink(link->state_file);
+
+ if (temp_path)
+ (void) unlink(temp_path);
+
return r;
}
diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h
index cec158e47d..c599952c45 100644
--- a/src/network/networkd-link.h
+++ b/src/network/networkd-link.h
@@ -82,9 +82,13 @@ struct Link {
sd_icmp6_nd *icmp6_router_discovery;
sd_dhcp6_client *dhcp6_client;
+ bool rtnl_extended_attrs;
sd_lldp *lldp;
char *lldp_file;
+
+ Hashmap *bound_by_links;
+ Hashmap *bound_to_links;
};
Link *link_unref(Link *link);
@@ -137,14 +141,18 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(Link*, link_unref);
/* Macros which append INTERFACE= to the message */
-#define log_link_full(link, level, error, fmt, ...) \
- log_object_internal(level, error, __FILE__, __LINE__, __func__, "INTERFACE=", link->ifname, "%-*s: " fmt, IFNAMSIZ, link->ifname, ##__VA_ARGS__)
+#define log_link_full(link, level, error, ...) \
+ ({ \
+ Link *_l = (link); \
+ _l ? log_object_internal(level, error, __FILE__, __LINE__, __func__, "INTERFACE=", _l->ifname, ##__VA_ARGS__) : \
+ log_internal(level, error, __FILE__, __LINE__, __func__, ##__VA_ARGS__); \
+ }) \
-#define log_link_debug(link, ...) log_link_full(link, LOG_DEBUG, 0, ##__VA_ARGS__)
-#define log_link_info(link, ...) log_link_full(link, LOG_INFO, 0, ##__VA_ARGS__)
-#define log_link_notice(link, ...) log_link_full(link, LOG_NOTICE, 0, ##__VA_ARGS__)
-#define log_link_warning(link, ...) log_link_full(link, LOG_WARNING, 0, ##__VA_ARGS__)
-#define log_link_error(link, ...) log_link_full(link, LOG_ERR, 0, ##__VA_ARGS__)
+#define log_link_debug(link, ...) log_link_full(link, LOG_DEBUG, 0, ##__VA_ARGS__)
+#define log_link_info(link, ...) log_link_full(link, LOG_INFO, 0, ##__VA_ARGS__)
+#define log_link_notice(link, ...) log_link_full(link, LOG_NOTICE, 0, ##__VA_ARGS__)
+#define log_link_warning(link, ...) log_link_full(link, LOG_WARNING, 0, ##__VA_ARGS__)
+#define log_link_error(link, ...) log_link_full(link, LOG_ERR, 0, ##__VA_ARGS__)
#define log_link_debug_errno(link, error, ...) log_link_full(link, LOG_DEBUG, error, ##__VA_ARGS__)
#define log_link_info_errno(link, error, ...) log_link_full(link, LOG_INFO, error, ##__VA_ARGS__)
@@ -152,7 +160,8 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(Link*, link_unref);
#define log_link_warning_errno(link, error, ...) log_link_full(link, LOG_WARNING, error, ##__VA_ARGS__)
#define log_link_error_errno(link, error, ...) log_link_full(link, LOG_ERR, error, ##__VA_ARGS__)
-#define log_link_struct(link, level, ...) log_struct(level, "INTERFACE=%s", link->ifname, __VA_ARGS__)
+#define LOG_LINK_MESSAGE(link, fmt, ...) "MESSAGE=%s: " fmt, (link)->ifname, ##__VA_ARGS__
+#define LOG_LINK_INTERFACE(link) "INTERFACE=%s", (link)->ifname
#define ADDRESS_FMT_VAL(address) \
be32toh((address).s_addr) >> 24, \
diff --git a/src/network/networkd-manager.c b/src/network/networkd-manager.c
index ce97ac0778..2cc53df4b1 100644
--- a/src/network/networkd-manager.c
+++ b/src/network/networkd-manager.c
@@ -27,13 +27,11 @@
#include "networkd.h"
#include "networkd-netdev.h"
#include "networkd-link.h"
-#include "network-internal.h"
#include "libudev-private.h"
#include "udev-util.h"
#include "rtnl-util.h"
#include "bus-util.h"
#include "def.h"
-#include "mkdir.h"
#include "virt.h"
#include "sd-rtnl.h"
@@ -107,12 +105,11 @@ static int manager_reset_all(Manager *m) {
return 0;
}
-static int match_prepare_for_sleep(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *ret_error) {
+static int match_prepare_for_sleep(sd_bus_message *message, void *userdata, sd_bus_error *ret_error) {
Manager *m = userdata;
int b, r;
- assert(bus);
- assert(bus);
+ assert(message);
r = sd_bus_message_read(message, "b", &b);
if (r < 0) {
@@ -301,11 +298,14 @@ static int manager_rtnl_process_link(sd_rtnl *rtnl, sd_rtnl_message *message, vo
if (r < 0) {
log_warning_errno(r, "rtnl: could not get message type: %m");
return 0;
+ } else if (type != RTM_NEWLINK && type != RTM_DELLINK) {
+ log_warning("rtnl: received unexpected message type when processing link");
+ return 0;
}
r = sd_rtnl_message_link_get_ifindex(message, &ifindex);
if (r < 0) {
- log_warning_errno(r, "rtnl: could not get ifindex: %m");
+ log_warning_errno(r, "rtnl: could not get ifindex from link: %m");
return 0;
} else if (ifindex <= 0) {
log_warning("rtnl: received link message with invalid ifindex: %d", ifindex);
@@ -853,6 +853,8 @@ const char *address_family_boolean_to_string(AddressFamilyBoolean b) {
return "ipv4";
if (b == ADDRESS_FAMILY_IPV6)
return "ipv6";
+ if (b == ADDRESS_FAMILY_KERNEL)
+ return "kernel";
return NULL;
}
@@ -872,6 +874,8 @@ AddressFamilyBoolean address_family_boolean_from_string(const char *s) {
return ADDRESS_FAMILY_IPV4;
if (streq(s, "ipv6"))
return ADDRESS_FAMILY_IPV6;
+ if (streq(s, "kernel"))
+ return ADDRESS_FAMILY_KERNEL;
return _ADDRESS_FAMILY_BOOLEAN_INVALID;
}
diff --git a/src/network/networkd-netdev-bond.c b/src/network/networkd-netdev-bond.c
index 88321ef84c..70df08a5e1 100644
--- a/src/network/networkd-netdev-bond.c
+++ b/src/network/networkd-netdev-bond.c
@@ -21,14 +21,45 @@
***/
#include <netinet/ether.h>
-#include <arpa/inet.h>
#include <linux/if_bonding.h>
#include "conf-parser.h"
#include "sd-rtnl.h"
+#include "rtnl-types.h"
#include "networkd-netdev-bond.h"
#include "missing.h"
+/*
+ * Number of seconds between instances where the bonding
+ * driver sends learning packets to each slaves peer switch
+ */
+#define LEARNING_PACKETS_INTERVAL_MIN_SEC (1 * USEC_PER_SEC)
+#define LEARNING_PACKETS_INTERVAL_MAX_SEC (0x7fffffff * USEC_PER_SEC)
+
+/* Number of IGMP membership reports to be issued after
+ * a failover event.
+ */
+#define RESEND_IGMP_MIN 0
+#define RESEND_IGMP_MAX 255
+#define RESEND_IGMP_DEFAULT 1
+
+/*
+ * Number of packets to transmit through a slave before
+ * moving to the next one.
+ */
+#define PACKETS_PER_SLAVE_MIN 0
+#define PACKETS_PER_SLAVE_MAX 65535
+#define PACKETS_PER_SLAVE_DEFAULT 1
+
+/*
+ * Number of peer notifications (gratuitous ARPs and
+ * unsolicited IPv6 Neighbor Advertisements) to be issued after a
+ * failover event.
+ */
+#define GRATUITOUS_ARP_MIN 0
+#define GRATUITOUS_ARP_MAX 255
+#define GRATUITOUS_ARP_DEFAULT 1
+
static const char* const bond_mode_table[_NETDEV_BOND_MODE_MAX] = {
[NETDEV_BOND_MODE_BALANCE_RR] = "balance-rr",
[NETDEV_BOND_MODE_ACTIVE_BACKUP] = "active-backup",
@@ -42,7 +73,6 @@ static const char* const bond_mode_table[_NETDEV_BOND_MODE_MAX] = {
DEFINE_STRING_TABLE_LOOKUP(bond_mode, BondMode);
DEFINE_CONFIG_PARSE_ENUM(config_parse_bond_mode, bond_mode, BondMode, "Failed to parse bond mode");
-
static const char* const bond_xmit_hash_policy_table[_NETDEV_BOND_XMIT_HASH_POLICY_MAX] = {
[NETDEV_BOND_XMIT_HASH_POLICY_LAYER2] = "layer2",
[NETDEV_BOND_XMIT_HASH_POLICY_LAYER34] = "layer3+4",
@@ -65,6 +95,51 @@ static const char* const bond_lacp_rate_table[_NETDEV_BOND_LACP_RATE_MAX] = {
DEFINE_STRING_TABLE_LOOKUP(bond_lacp_rate, BondLacpRate);
DEFINE_CONFIG_PARSE_ENUM(config_parse_bond_lacp_rate, bond_lacp_rate, BondLacpRate, "Failed to parse bond lacp rate")
+static const char* const bond_ad_select_table[_NETDEV_BOND_AD_SELECT_MAX] = {
+ [NETDEV_BOND_AD_SELECT_STABLE] = "stable",
+ [NETDEV_BOND_AD_SELECT_BANDWIDTH] = "bandwidth",
+ [NETDEV_BOND_AD_SELECT_COUNT] = "count",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(bond_ad_select, BondAdSelect);
+DEFINE_CONFIG_PARSE_ENUM(config_parse_bond_ad_select, bond_ad_select, BondAdSelect, "Failed to parse bond AD select");
+
+static const char* const bond_fail_over_mac_table[_NETDEV_BOND_FAIL_OVER_MAC_MAX] = {
+ [NETDEV_BOND_FAIL_OVER_MAC_NONE] = "none",
+ [NETDEV_BOND_FAIL_OVER_MAC_ACTIVE] = "active",
+ [NETDEV_BOND_FAIL_OVER_MAC_FOLLOW] = "follow",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(bond_fail_over_mac, BondFailOverMac);
+DEFINE_CONFIG_PARSE_ENUM(config_parse_bond_fail_over_mac, bond_fail_over_mac, BondFailOverMac, "Failed to parse bond fail over MAC");
+
+static const char *const bond_arp_validate_table[_NETDEV_BOND_ARP_VALIDATE_MAX] = {
+ [NETDEV_BOND_ARP_VALIDATE_NONE] = "none",
+ [NETDEV_BOND_ARP_VALIDATE_ACTIVE]= "active",
+ [NETDEV_BOND_ARP_VALIDATE_BACKUP]= "backup",
+ [NETDEV_BOND_ARP_VALIDATE_ALL]= "all",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(bond_arp_validate, BondArpValidate);
+DEFINE_CONFIG_PARSE_ENUM(config_parse_bond_arp_validate, bond_arp_validate, BondArpValidate, "Failed to parse bond arp validate");
+
+static const char *const bond_arp_all_targets_table[_NETDEV_BOND_ARP_ALL_TARGETS_MAX] = {
+ [NETDEV_BOND_ARP_ALL_TARGETS_ANY] = "any",
+ [NETDEV_BOND_ARP_ALL_TARGETS_ALL] = "all",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(bond_arp_all_targets, BondArpAllTargets);
+DEFINE_CONFIG_PARSE_ENUM(config_parse_bond_arp_all_targets, bond_arp_all_targets, BondArpAllTargets, "Failed to parse bond Arp all targets");
+
+static const char *bond_primary_reselect_table[_NETDEV_BOND_PRIMARY_RESELECT_MAX] = {
+ [NETDEV_BOND_PRIMARY_RESELECT_ALWAYS] = "always",
+ [NETDEV_BOND_PRIMARY_RESELECT_BETTER]= "better",
+ [NETDEV_BOND_PRIMARY_RESELECT_FAILURE]= "failure",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(bond_primary_reselect, BondPrimaryReselect);
+DEFINE_CONFIG_PARSE_ENUM(config_parse_bond_primary_reselect, bond_primary_reselect, BondPrimaryReselect, "Failed to parse bond primary reselect");
+
static uint8_t bond_mode_to_kernel(BondMode mode) {
switch (mode) {
case NETDEV_BOND_MODE_BALANCE_RR:
@@ -105,7 +180,8 @@ static uint8_t bond_xmit_hash_policy_to_kernel(BondXmitHashPolicy policy) {
static int netdev_bond_fill_message_create(NetDev *netdev, Link *link, sd_rtnl_message *m) {
Bond *b = BOND(netdev);
- int r;
+ ArpIpTarget *target = NULL;
+ int r, i = 0;
assert(netdev);
assert(!link);
@@ -175,9 +251,230 @@ static int netdev_bond_fill_message_create(NetDev *netdev, Link *link, sd_rtnl_m
}
}
+ if (b->arp_interval != 0) {
+ r = sd_rtnl_message_append_u32(m, IFLA_BOND_ARP_INTERVAL, b->arp_interval / USEC_PER_MSEC);
+ if (r < 0) {
+ log_netdev_error(netdev,
+ "Could not append IFLA_BOND_ARP_INTERVAL attribute: %s",
+ strerror(-r));
+ return r;
+ }
+ }
+
+ if ((b->lp_interval >= LEARNING_PACKETS_INTERVAL_MIN_SEC) &&
+ (b->lp_interval <= LEARNING_PACKETS_INTERVAL_MAX_SEC)) {
+ r = sd_rtnl_message_append_u32(m, IFLA_BOND_LP_INTERVAL, b->lp_interval / USEC_PER_SEC);
+ if (r < 0) {
+ log_netdev_error(netdev,
+ "Could not append IFLA_BOND_LP_INTERVAL attribute: %s",
+ strerror(-r));
+ return r;
+ }
+ }
+
+ if (b->ad_select != _NETDEV_BOND_AD_SELECT_INVALID &&
+ b->mode == BOND_MODE_8023AD) {
+ r = sd_rtnl_message_append_u8(m, IFLA_BOND_AD_SELECT, b->ad_select);
+ if (r < 0) {
+ log_netdev_error(netdev,
+ "Could not append IFLA_BOND_AD_SELECT attribute: %s",
+ strerror(-r));
+ return r;
+ }
+ }
+
+ if (b->fail_over_mac != _NETDEV_BOND_FAIL_OVER_MAC_INVALID &&
+ b->mode == NETDEV_BOND_MODE_ACTIVE_BACKUP) {
+ r = sd_rtnl_message_append_u8(m, IFLA_BOND_FAIL_OVER_MAC, b->fail_over_mac);
+ if (r < 0) {
+ log_netdev_error(netdev,
+ "Could not append IFLA_BOND_FAIL_OVER_MAC attribute: %s",
+ strerror(-r));
+ return r;
+ }
+ }
+
+ if (b->arp_validate != _NETDEV_BOND_ARP_VALIDATE_INVALID) {
+ r = sd_rtnl_message_append_u32(m, IFLA_BOND_ARP_VALIDATE, b->arp_validate);
+ if (r < 0) {
+ log_netdev_error(netdev,
+ "Could not append IFLA_BOND_ARP_VALIDATE attribute: %s",
+ strerror(-r));
+ return r;
+ }
+ }
+
+ if (b->arp_all_targets != _NETDEV_BOND_ARP_ALL_TARGETS_INVALID) {
+ r = sd_rtnl_message_append_u32(m, IFLA_BOND_ARP_ALL_TARGETS, b->arp_all_targets);
+ if (r < 0) {
+ log_netdev_error(netdev,
+ "Could not append IFLA_BOND_ARP_VALIDATE attribute: %s",
+ strerror(-r));
+ return r;
+ }
+ }
+
+ if (b->primary_reselect != _NETDEV_BOND_PRIMARY_RESELECT_INVALID) {
+ r = sd_rtnl_message_append_u32(m, IFLA_BOND_ARP_ALL_TARGETS, b->primary_reselect);
+ if (r < 0) {
+ log_netdev_error(netdev,
+ "Could not append IFLA_BOND_ARP_ALL_TARGETS attribute: %s",
+ strerror(-r));
+ return r;
+ }
+ }
+
+ if (b->resend_igmp <= RESEND_IGMP_MAX) {
+ r = sd_rtnl_message_append_u32(m, IFLA_BOND_RESEND_IGMP, b->resend_igmp);
+ if (r < 0) {
+ log_netdev_error(netdev,
+ "Could not append IFLA_BOND_RESEND_IGMP attribute: %s",
+ strerror(-r));
+ return r;
+ }
+ }
+
+ if (b->packets_per_slave <= PACKETS_PER_SLAVE_MAX) {
+ r = sd_rtnl_message_append_u32(m, IFLA_BOND_PACKETS_PER_SLAVE, b->packets_per_slave);
+ if (r < 0) {
+ log_netdev_error(netdev,
+ "Could not append IFLA_BOND_PACKETS_PER_SLAVE attribute: %s",
+ strerror(-r));
+ return r;
+ }
+ }
+
+ if (b->num_grat_arp <= GRATUITOUS_ARP_MAX) {
+ r = sd_rtnl_message_append_u8(m, IFLA_BOND_NUM_PEER_NOTIF, b->num_grat_arp);
+ if (r < 0) {
+ log_netdev_error(netdev,
+ "Could not append IFLA_BOND_NUM_PEER_NOTIF attribute: %s",
+ strerror(-r));
+ return r;
+ }
+ }
+
+ if (b->min_links != 0) {
+ r = sd_rtnl_message_append_u32(m, IFLA_BOND_MIN_LINKS, b->min_links);
+ if (r < 0) {
+ log_netdev_error(netdev,
+ "Could not append IFLA_BOND_MIN_LINKS attribute: %s",
+ strerror(-r));
+ return r;
+ }
+ }
+
+ r = sd_rtnl_message_append_u8(m, IFLA_BOND_ALL_SLAVES_ACTIVE, b->all_slaves_active);
+ if (r < 0) {
+ log_netdev_error(netdev,
+ "Could not append IFLA_BOND_ALL_SLAVES_ACTIVE attribute: %s",
+ strerror(-r));
+ return r;
+ }
+
+ if (b->arp_interval > 0) {
+ if (b->n_arp_ip_targets > 0) {
+
+ r = sd_rtnl_message_open_container(m, IFLA_BOND_ARP_IP_TARGET);
+ if (r < 0) {
+ log_netdev_error(netdev,
+ "Could not open contaniner IFLA_BOND_ARP_IP_TARGET : %s",
+ strerror(-r));
+ return r;
+ }
+
+ LIST_FOREACH(arp_ip_target, target, b->arp_ip_targets) {
+ r = sd_rtnl_message_append_u32(m, i++, target->ip.in.s_addr);
+ if (r < 0) {
+ log_netdev_error(netdev,
+ "Could not append IFLA_BOND_ARP_ALL_TARGETS attribute: %s",
+ strerror(-r));
+ return r;
+ }
+ }
+
+ r = sd_rtnl_message_close_container(m);
+ if (r < 0) {
+ log_netdev_error(netdev,
+ "Could not close contaniner IFLA_BOND_ARP_IP_TARGET : %s",
+ strerror(-r));
+ return r;
+ }
+ }
+ }
+
return 0;
}
+int config_parse_arp_ip_target_address(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+ Bond *b = userdata;
+ const char *word, *state;
+ size_t l;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ FOREACH_WORD_QUOTED(word, l, rvalue, state) {
+ _cleanup_free_ ArpIpTarget *buffer = NULL;
+ _cleanup_free_ char *n = NULL;
+ int f;
+
+ n = strndup(word, l);
+ if (!n)
+ return -ENOMEM;
+
+ buffer = new0(ArpIpTarget, 1);
+ if (!buffer)
+ return -ENOMEM;
+
+ r = in_addr_from_string_auto(n, &f, &buffer->ip);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Bond ARP ip target address is invalid, ignoring assignment: %s", n);
+ return 0;
+ }
+
+ if (f != AF_INET) {
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Bond ARP ip target address is invalid, ignoring assignment: %s", n);
+ return 0;
+ }
+
+ LIST_PREPEND(arp_ip_target, b->arp_ip_targets, buffer);
+ b->n_arp_ip_targets ++;
+
+ buffer = NULL;
+
+ if (b->n_arp_ip_targets > BOND_ARP_TARGETS_MAX)
+ break;
+ }
+
+ return 0;
+}
+
+static void bond_done(NetDev *netdev) {
+ ArpIpTarget *t = NULL, *n = NULL;
+ Bond *b = BOND(netdev);
+
+ assert(netdev);
+ assert(b);
+
+ LIST_FOREACH_SAFE(arp_ip_target, t, n, b->arp_ip_targets)
+ free(t);
+
+ b->arp_ip_targets = NULL;
+}
+
static void bond_init(NetDev *netdev) {
Bond *b = BOND(netdev);
@@ -187,11 +484,27 @@ static void bond_init(NetDev *netdev) {
b->mode = _NETDEV_BOND_MODE_INVALID;
b->xmit_hash_policy = _NETDEV_BOND_XMIT_HASH_POLICY_INVALID;
b->lacp_rate = _NETDEV_BOND_LACP_RATE_INVALID;
+ b->ad_select = _NETDEV_BOND_AD_SELECT_INVALID;
+ b->fail_over_mac = _NETDEV_BOND_FAIL_OVER_MAC_INVALID;
+ b->arp_validate = _NETDEV_BOND_ARP_VALIDATE_INVALID;
+ b->arp_all_targets = _NETDEV_BOND_ARP_ALL_TARGETS_INVALID;
+ b->primary_reselect = _NETDEV_BOND_PRIMARY_RESELECT_INVALID;
+
+ b->all_slaves_active = false;
+
+ b->resend_igmp = RESEND_IGMP_DEFAULT;
+ b->packets_per_slave = PACKETS_PER_SLAVE_DEFAULT;
+ b->num_grat_arp = GRATUITOUS_ARP_DEFAULT;
+ b->lp_interval = LEARNING_PACKETS_INTERVAL_MIN_SEC;
+
+ LIST_HEAD_INIT(b->arp_ip_targets);
+ b->n_arp_ip_targets = 0;
}
const NetDevVTable bond_vtable = {
.object_size = sizeof(Bond),
.init = bond_init,
+ .done = bond_done,
.sections = "Match\0NetDev\0Bond\0",
.fill_message_create = netdev_bond_fill_message_create,
.create_type = NETDEV_CREATE_MASTER,
diff --git a/src/network/networkd-netdev-bond.h b/src/network/networkd-netdev-bond.h
index c09af5fa52..32d1702d58 100644
--- a/src/network/networkd-netdev-bond.h
+++ b/src/network/networkd-netdev-bond.h
@@ -47,7 +47,6 @@ typedef enum BondXmitHashPolicy {
_NETDEV_BOND_XMIT_HASH_POLICY_INVALID = -1
} BondXmitHashPolicy;
-
typedef enum BondLacpRate {
NETDEV_BOND_LACP_RATE_SLOW,
NETDEV_BOND_LACP_RATE_FAST,
@@ -55,16 +54,79 @@ typedef enum BondLacpRate {
_NETDEV_BOND_LACP_RATE_INVALID = -1,
} BondLacpRate;
+typedef enum BondAdSelect {
+ NETDEV_BOND_AD_SELECT_STABLE,
+ NETDEV_BOND_AD_SELECT_BANDWIDTH,
+ NETDEV_BOND_AD_SELECT_COUNT,
+ _NETDEV_BOND_AD_SELECT_MAX,
+ _NETDEV_BOND_AD_SELECT_INVALID = -1,
+} BondAdSelect;
+
+typedef enum BondFailOverMac {
+ NETDEV_BOND_FAIL_OVER_MAC_NONE,
+ NETDEV_BOND_FAIL_OVER_MAC_ACTIVE,
+ NETDEV_BOND_FAIL_OVER_MAC_FOLLOW,
+ _NETDEV_BOND_FAIL_OVER_MAC_MAX,
+ _NETDEV_BOND_FAIL_OVER_MAC_INVALID = -1,
+} BondFailOverMac;
+
+typedef enum BondArpValidate {
+ NETDEV_BOND_ARP_VALIDATE_NONE,
+ NETDEV_BOND_ARP_VALIDATE_ACTIVE,
+ NETDEV_BOND_ARP_VALIDATE_BACKUP,
+ NETDEV_BOND_ARP_VALIDATE_ALL,
+ _NETDEV_BOND_ARP_VALIDATE_MAX,
+ _NETDEV_BOND_ARP_VALIDATE_INVALID = -1,
+} BondArpValidate;
+
+typedef enum BondArpAllTargets {
+ NETDEV_BOND_ARP_ALL_TARGETS_ANY,
+ NETDEV_BOND_ARP_ALL_TARGETS_ALL,
+ _NETDEV_BOND_ARP_ALL_TARGETS_MAX,
+ _NETDEV_BOND_ARP_ALL_TARGETS_INVALID = -1,
+} BondArpAllTargets;
+
+typedef enum BondPrimaryReselect {
+ NETDEV_BOND_PRIMARY_RESELECT_ALWAYS,
+ NETDEV_BOND_PRIMARY_RESELECT_BETTER,
+ NETDEV_BOND_PRIMARY_RESELECT_FAILURE,
+ _NETDEV_BOND_PRIMARY_RESELECT_MAX,
+ _NETDEV_BOND_PRIMARY_RESELECT_INVALID = -1,
+} BondPrimaryReselect;
+
+typedef struct ArpIpTarget {
+ union in_addr_union ip;
+
+ LIST_FIELDS(struct ArpIpTarget, arp_ip_target);
+} ArpIpTarget;
+
struct Bond {
NetDev meta;
BondMode mode;
BondXmitHashPolicy xmit_hash_policy;
BondLacpRate lacp_rate;
+ BondAdSelect ad_select;
+ BondFailOverMac fail_over_mac;
+ BondArpValidate arp_validate;
+ BondArpAllTargets arp_all_targets;
+ BondPrimaryReselect primary_reselect;
+
+ bool all_slaves_active;
+
+ unsigned resend_igmp;
+ unsigned packets_per_slave;
+ unsigned num_grat_arp;
+ unsigned min_links;
usec_t miimon;
usec_t updelay;
usec_t downdelay;
+ usec_t arp_interval;
+ usec_t lp_interval;
+
+ int n_arp_ip_targets;
+ ArpIpTarget *arp_ip_targets;
};
extern const NetDevVTable bond_vtable;
@@ -78,6 +140,27 @@ BondXmitHashPolicy bond_xmit_hash_policy_from_string(const char *d) _pure_;
const char *bond_lacp_rate_to_string(BondLacpRate d) _const_;
BondLacpRate bond_lacp_rate_from_string(const char *d) _pure_;
+const char *bond_fail_over_mac_to_string(BondFailOverMac d) _const_;
+BondFailOverMac bond_fail_over_mac_from_string(const char *d) _pure_;
+
+const char *bond_ad_select_to_string(BondAdSelect d) _const_;
+BondAdSelect bond_ad_select_from_string(const char *d) _pure_;
+
+const char *bond_arp_validate_to_string(BondArpValidate d) _const_;
+BondArpValidate bond_arp_validate_from_string(const char *d) _pure_;
+
+const char *bond_arp_all_targets_to_string(BondArpAllTargets d) _const_;
+BondArpAllTargets bond_arp_all_targets_from_string(const char *d) _pure_;
+
+const char *bond_primary_reselect_to_string(BondPrimaryReselect d) _const_;
+BondPrimaryReselect bond_primary_reselect_from_string(const char *d) _pure_;
+
int config_parse_bond_mode(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_bond_xmit_hash_policy(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_bond_lacp_rate(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_bond_ad_select(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_bond_fail_over_mac(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_bond_arp_validate(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_bond_arp_all_targets(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_bond_primary_reselect(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_arp_ip_target_address(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
diff --git a/src/network/networkd-netdev-bridge.c b/src/network/networkd-netdev-bridge.c
index 91b7051a06..fd6af7e99b 100644
--- a/src/network/networkd-netdev-bridge.c
+++ b/src/network/networkd-netdev-bridge.c
@@ -20,11 +20,7 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <netinet/ether.h>
-#include <arpa/inet.h>
-#include <net/if.h>
-#include "sd-rtnl.h"
#include "networkd-netdev-bridge.h"
#include "missing.h"
diff --git a/src/network/networkd-netdev-dummy.c b/src/network/networkd-netdev-dummy.c
index 01c10a2d73..bb246a2be0 100644
--- a/src/network/networkd-netdev-dummy.c
+++ b/src/network/networkd-netdev-dummy.c
@@ -20,12 +20,7 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <netinet/ether.h>
-#include <arpa/inet.h>
-#include <net/if.h>
-#include <linux/veth.h>
-#include "sd-rtnl.h"
#include "networkd-netdev-dummy.h"
const NetDevVTable dummy_vtable = {
diff --git a/src/network/networkd-netdev-gperf.gperf b/src/network/networkd-netdev-gperf.gperf
index 963c47c3e5..66ed2e013c 100644
--- a/src/network/networkd-netdev-gperf.gperf
+++ b/src/network/networkd-netdev-gperf.gperf
@@ -18,49 +18,65 @@ struct ConfigPerfItem;
%struct-type
%includes
%%
-Match.Host, config_parse_net_condition, CONDITION_HOST, offsetof(NetDev, match_host)
-Match.Virtualization, config_parse_net_condition, CONDITION_VIRTUALIZATION, offsetof(NetDev, match_virt)
-Match.KernelCommandLine, config_parse_net_condition, CONDITION_KERNEL_COMMAND_LINE, offsetof(NetDev, match_kernel)
-Match.Architecture, config_parse_net_condition, CONDITION_ARCHITECTURE, offsetof(NetDev, match_arch)
-NetDev.Description, config_parse_string, 0, offsetof(NetDev, description)
-NetDev.Name, config_parse_ifname, 0, offsetof(NetDev, ifname)
-NetDev.Kind, config_parse_netdev_kind, 0, offsetof(NetDev, kind)
-NetDev.MTUBytes, config_parse_iec_size, 0, offsetof(NetDev, mtu)
-NetDev.MACAddress, config_parse_hwaddr, 0, offsetof(NetDev, mac)
-VLAN.Id, config_parse_uint64, 0, offsetof(VLan, id)
-MACVLAN.Mode, config_parse_macvlan_mode, 0, offsetof(MacVlan, mode)
-IPVLAN.Mode, config_parse_ipvlan_mode, 0, offsetof(IPVlan, mode)
-Tunnel.Local, config_parse_tunnel_address, 0, offsetof(Tunnel, local)
-Tunnel.Remote, config_parse_tunnel_address, 0, offsetof(Tunnel, remote)
-Tunnel.TOS, config_parse_unsigned, 0, offsetof(Tunnel, tos)
-Tunnel.TTL, config_parse_unsigned, 0, offsetof(Tunnel, ttl)
-Tunnel.DiscoverPathMTU, config_parse_bool, 0, offsetof(Tunnel, pmtudisc)
-Tunnel.Mode, config_parse_ip6tnl_mode, 0, offsetof(Tunnel, ip6tnl_mode)
-Peer.Name, config_parse_ifname, 0, offsetof(Veth, ifname_peer)
-Peer.MACAddress, config_parse_hwaddr, 0, offsetof(Veth, mac_peer)
-VXLAN.Id, config_parse_uint64, 0, offsetof(VxLan, id)
-VXLAN.Group, config_parse_vxlan_group_address, 0, offsetof(VxLan, group)
-VXLAN.TOS, config_parse_unsigned, 0, offsetof(VxLan, tos)
-VXLAN.TTL, config_parse_unsigned, 0, offsetof(VxLan, ttl)
-VXLAN.MacLearning, config_parse_bool, 0, offsetof(VxLan, learning)
-VXLAN.ARPProxy, config_parse_bool, 0, offsetof(VxLan, arp_proxy)
-VXLAN.L2MissNotification, config_parse_bool, 0, offsetof(VxLan, l2miss)
-VXLAN.L3MissNotification, config_parse_bool, 0, offsetof(VxLan, l3miss)
-VXLAN.RouteShortCircuit, config_parse_bool, 0, offsetof(VxLan, route_short_circuit)
-VXLAN.FDBAgeingSec, config_parse_sec, 0, offsetof(VxLan, fdb_ageing)
-Tun.OneQueue, config_parse_bool, 0, offsetof(TunTap, one_queue)
-Tun.MultiQueue, config_parse_bool, 0, offsetof(TunTap, multi_queue)
-Tun.PacketInfo, config_parse_bool, 0, offsetof(TunTap, packet_info)
-Tun.User, config_parse_string, 0, offsetof(TunTap, user_name)
-Tun.Group, config_parse_string, 0, offsetof(TunTap, group_name)
-Tap.OneQueue, config_parse_bool, 0, offsetof(TunTap, one_queue)
-Tap.MultiQueue, config_parse_bool, 0, offsetof(TunTap, multi_queue)
-Tap.PacketInfo, config_parse_bool, 0, offsetof(TunTap, packet_info)
-Tap.User, config_parse_string, 0, offsetof(TunTap, user_name)
-Tap.Group, config_parse_string, 0, offsetof(TunTap, group_name)
-Bond.Mode, config_parse_bond_mode, 0, offsetof(Bond, mode)
-Bond.TransmitHashPolicy, config_parse_bond_xmit_hash_policy, 0, offsetof(Bond, xmit_hash_policy)
-Bond.LACPTransmitRate, config_parse_bond_lacp_rate, 0, offsetof(Bond, lacp_rate)
-Bond.MIIMonitorSec, config_parse_sec, 0, offsetof(Bond, miimon)
-Bond.UpDelaySec, config_parse_sec, 0, offsetof(Bond, updelay)
-Bond.DownDelaySec, config_parse_sec, 0, offsetof(Bond, downdelay)
+Match.Host, config_parse_net_condition, CONDITION_HOST, offsetof(NetDev, match_host)
+Match.Virtualization, config_parse_net_condition, CONDITION_VIRTUALIZATION, offsetof(NetDev, match_virt)
+Match.KernelCommandLine, config_parse_net_condition, CONDITION_KERNEL_COMMAND_LINE, offsetof(NetDev, match_kernel)
+Match.Architecture, config_parse_net_condition, CONDITION_ARCHITECTURE, offsetof(NetDev, match_arch)
+NetDev.Description, config_parse_string, 0, offsetof(NetDev, description)
+NetDev.Name, config_parse_ifname, 0, offsetof(NetDev, ifname)
+NetDev.Kind, config_parse_netdev_kind, 0, offsetof(NetDev, kind)
+NetDev.MTUBytes, config_parse_iec_size, 0, offsetof(NetDev, mtu)
+NetDev.MACAddress, config_parse_hwaddr, 0, offsetof(NetDev, mac)
+VLAN.Id, config_parse_uint64, 0, offsetof(VLan, id)
+MACVLAN.Mode, config_parse_macvlan_mode, 0, offsetof(MacVlan, mode)
+IPVLAN.Mode, config_parse_ipvlan_mode, 0, offsetof(IPVlan, mode)
+Tunnel.Local, config_parse_tunnel_address, 0, offsetof(Tunnel, local)
+Tunnel.Remote, config_parse_tunnel_address, 0, offsetof(Tunnel, remote)
+Tunnel.TOS, config_parse_unsigned, 0, offsetof(Tunnel, tos)
+Tunnel.TTL, config_parse_unsigned, 0, offsetof(Tunnel, ttl)
+Tunnel.DiscoverPathMTU, config_parse_bool, 0, offsetof(Tunnel, pmtudisc)
+Tunnel.Mode, config_parse_ip6tnl_mode, 0, offsetof(Tunnel, ip6tnl_mode)
+Peer.Name, config_parse_ifname, 0, offsetof(Veth, ifname_peer)
+Peer.MACAddress, config_parse_hwaddr, 0, offsetof(Veth, mac_peer)
+VXLAN.Id, config_parse_uint64, 0, offsetof(VxLan, id)
+VXLAN.Group, config_parse_vxlan_group_address, 0, offsetof(VxLan, group)
+VXLAN.TOS, config_parse_unsigned, 0, offsetof(VxLan, tos)
+VXLAN.TTL, config_parse_unsigned, 0, offsetof(VxLan, ttl)
+VXLAN.MacLearning, config_parse_bool, 0, offsetof(VxLan, learning)
+VXLAN.ARPProxy, config_parse_bool, 0, offsetof(VxLan, arp_proxy)
+VXLAN.L2MissNotification, config_parse_bool, 0, offsetof(VxLan, l2miss)
+VXLAN.L3MissNotification, config_parse_bool, 0, offsetof(VxLan, l3miss)
+VXLAN.RouteShortCircuit, config_parse_bool, 0, offsetof(VxLan, route_short_circuit)
+VXLAN.UDPCheckSum, config_parse_bool, 0, offsetof(VxLan, udpcsum)
+VXLAN.UDP6ZeroCheckSumRx, config_parse_bool, 0, offsetof(VxLan, udp6zerocsumrx)
+VXLAN.UDP6ZeroCheckSumTx, config_parse_bool, 0, offsetof(VxLan, udp6zerocsumtx)
+VXLAN.FDBAgeingSec, config_parse_sec, 0, offsetof(VxLan, fdb_ageing)
+Tun.OneQueue, config_parse_bool, 0, offsetof(TunTap, one_queue)
+Tun.MultiQueue, config_parse_bool, 0, offsetof(TunTap, multi_queue)
+Tun.PacketInfo, config_parse_bool, 0, offsetof(TunTap, packet_info)
+Tun.User, config_parse_string, 0, offsetof(TunTap, user_name)
+Tun.Group, config_parse_string, 0, offsetof(TunTap, group_name)
+Tap.OneQueue, config_parse_bool, 0, offsetof(TunTap, one_queue)
+Tap.MultiQueue, config_parse_bool, 0, offsetof(TunTap, multi_queue)
+Tap.PacketInfo, config_parse_bool, 0, offsetof(TunTap, packet_info)
+Tap.User, config_parse_string, 0, offsetof(TunTap, user_name)
+Tap.Group, config_parse_string, 0, offsetof(TunTap, group_name)
+Bond.Mode, config_parse_bond_mode, 0, offsetof(Bond, mode)
+Bond.TransmitHashPolicy, config_parse_bond_xmit_hash_policy, 0, offsetof(Bond, xmit_hash_policy)
+Bond.LACPTransmitRate, config_parse_bond_lacp_rate, 0, offsetof(Bond, lacp_rate)
+Bond.AdSelect, config_parse_bond_ad_select, 0, offsetof(Bond, ad_select)
+Bond.FailOverMACPolicy, config_parse_bond_fail_over_mac, 0, offsetof(Bond, fail_over_mac)
+Bond.ARPIPTargets, config_parse_arp_ip_target_address, 0, 0
+Bond.ARPValidate, config_parse_bond_arp_validate, 0, offsetof(Bond, arp_validate)
+Bond.ARPAllTargets, config_parse_bond_arp_all_targets, 0, offsetof(Bond, arp_all_targets)
+Bond.PrimaryReselectPolicy, config_parse_bond_primary_reselect, 0, offsetof(Bond, primary_reselect)
+Bond.ResendIGMP, config_parse_unsigned, 0, offsetof(Bond, resend_igmp)
+Bond.PacketsPerSlave, config_parse_unsigned, 0, offsetof(Bond, packets_per_slave)
+Bond.GratuitousARP, config_parse_unsigned, 0, offsetof(Bond, num_grat_arp)
+Bond.AllSlavesActive, config_parse_unsigned, 0, offsetof(Bond, all_slaves_active)
+Bond.MinLinks, config_parse_unsigned, 0, offsetof(Bond, min_links)
+Bond.MIIMonitorSec, config_parse_sec, 0, offsetof(Bond, miimon)
+Bond.UpDelaySec, config_parse_sec, 0, offsetof(Bond, updelay)
+Bond.DownDelaySec, config_parse_sec, 0, offsetof(Bond, downdelay)
+Bond.ARPIntervalSec, config_parse_sec, 0, offsetof(Bond, arp_interval)
+Bond.LearnPacketIntervalSec, config_parse_sec, 0, offsetof(Bond, lp_interval)
diff --git a/src/network/networkd-netdev-ipvlan.c b/src/network/networkd-netdev-ipvlan.c
index 9a7c280c6b..5189000c1f 100644
--- a/src/network/networkd-netdev-ipvlan.c
+++ b/src/network/networkd-netdev-ipvlan.c
@@ -20,12 +20,9 @@
***/
#include <net/if.h>
-#include <linux/if_link.h>
#include "networkd-netdev-ipvlan.h"
-#include "network-internal.h"
#include "conf-parser.h"
-#include "list.h"
static const char* const ipvlan_mode_table[_NETDEV_IPVLAN_MODE_MAX] = {
[NETDEV_IPVLAN_MODE_L2] = "L2",
diff --git a/src/network/networkd-netdev-macvlan.c b/src/network/networkd-netdev-macvlan.c
index 198fb575ee..5f41f1c865 100644
--- a/src/network/networkd-netdev-macvlan.c
+++ b/src/network/networkd-netdev-macvlan.c
@@ -22,9 +22,7 @@
#include <net/if.h>
#include "networkd-netdev-macvlan.h"
-#include "network-internal.h"
#include "conf-parser.h"
-#include "list.h"
static const char* const macvlan_mode_table[_NETDEV_MACVLAN_MODE_MAX] = {
[NETDEV_MACVLAN_MODE_PRIVATE] = "private",
diff --git a/src/network/networkd-netdev-tunnel.c b/src/network/networkd-netdev-tunnel.c
index e95082d119..8af4be4974 100644
--- a/src/network/networkd-netdev-tunnel.c
+++ b/src/network/networkd-netdev-tunnel.c
@@ -19,7 +19,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <netinet/ether.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <linux/ip.h>
@@ -29,7 +28,6 @@
#include "sd-rtnl.h"
#include "networkd-netdev-tunnel.h"
#include "networkd-link.h"
-#include "network-internal.h"
#include "util.h"
#include "missing.h"
#include "conf-parser.h"
@@ -56,44 +54,24 @@ static int netdev_ipip_fill_message_create(NetDev *netdev, Link *link, sd_rtnl_m
assert(t->family == AF_INET);
r = sd_rtnl_message_append_u32(m, IFLA_IPTUN_LINK, link->ifindex);
- if (r < 0) {
- log_netdev_error(netdev,
- "Could not append IFLA_IPTUN_LINK attribute: %s",
- strerror(-r));
- return r;
- }
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_LINK attribute: %m");
r = sd_rtnl_message_append_in_addr(m, IFLA_IPTUN_LOCAL, &t->local.in);
- if (r < 0) {
- log_netdev_error(netdev,
- "Could not append IFLA_IPTUN_LOCAL attribute: %s",
- strerror(-r));
- return r;
- }
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_LOCAL attribute: %m");
r = sd_rtnl_message_append_in_addr(m, IFLA_IPTUN_REMOTE, &t->remote.in);
- if (r < 0) {
- log_netdev_error(netdev,
- "Could not append IFLA_IPTUN_REMOTE attribute: %s",
- strerror(-r));
- return r;
- }
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_REMOTE attribute: %m");
r = sd_rtnl_message_append_u8(m, IFLA_IPTUN_TTL, t->ttl);
- if (r < 0) {
- log_netdev_error(netdev,
- "Could not append IFLA_IPTUN_TTL attribute: %s",
- strerror(-r));
- return r;
- }
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_TTL attribute: %m");
r = sd_rtnl_message_append_u8(m, IFLA_IPTUN_PMTUDISC, t->pmtudisc);
- if (r < 0) {
- log_netdev_error(netdev,
- "Could not append IFLA_IPTUN_PMTUDISC attribute: %s",
- strerror(-r));
- return r;
- }
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_PMTUDISC attribute: %m");
return r;
}
@@ -109,44 +87,24 @@ static int netdev_sit_fill_message_create(NetDev *netdev, Link *link, sd_rtnl_me
assert(t->family == AF_INET);
r = sd_rtnl_message_append_u32(m, IFLA_IPTUN_LINK, link->ifindex);
- if (r < 0) {
- log_netdev_error(netdev,
- "Could not append IFLA_IPTUN_LINK attribute: %s",
- strerror(-r));
- return r;
- }
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_LINK attribute: %m");
r = sd_rtnl_message_append_in_addr(m, IFLA_IPTUN_LOCAL, &t->local.in);
- if (r < 0) {
- log_netdev_error(netdev,
- "Could not append IFLA_IPTUN_LOCAL attribute: %s",
- strerror(-r));
- return r;
- }
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_LOCAL attribute: %m");
r = sd_rtnl_message_append_in_addr(m, IFLA_IPTUN_REMOTE, &t->remote.in);
- if (r < 0) {
- log_netdev_error(netdev,
- "Could not append IFLA_IPTUN_REMOTE attribute: %s",
- strerror(-r));
- return r;
- }
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_REMOTE attribute: %m");
r = sd_rtnl_message_append_u8(m, IFLA_IPTUN_TTL, t->ttl);
- if (r < 0) {
- log_netdev_error(netdev,
- "Could not append IFLA_IPTUN_TTL attribute: %s",
- strerror(-r));
- return r;
- }
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_TTL attribute: %m");
r = sd_rtnl_message_append_u8(m, IFLA_IPTUN_PMTUDISC, t->pmtudisc);
- if (r < 0) {
- log_netdev_error(netdev,
- "Could not append IFLA_IPTUN_PMTUDISC attribute: %s",
- strerror(-r));
- return r;
- }
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_PMTUDISC attribute: %m");
return r;
}
@@ -158,9 +116,9 @@ static int netdev_gre_fill_message_create(NetDev *netdev, Link *link, sd_rtnl_me
assert(netdev);
if (netdev->kind == NETDEV_KIND_GRE)
- t = GRE(netdev);
+ t = GRE(netdev);
else
- t = GRETAP(netdev);
+ t = GRETAP(netdev);
assert(t);
assert(t->family == AF_INET);
@@ -168,52 +126,28 @@ static int netdev_gre_fill_message_create(NetDev *netdev, Link *link, sd_rtnl_me
assert(m);
r = sd_rtnl_message_append_u32(m, IFLA_GRE_LINK, link->ifindex);
- if (r < 0) {
- log_netdev_error(netdev,
- "Could not append IFLA_GRE_LINK attribute: %s",
- strerror(-r));
- return r;
- }
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append IFLA_GRE_LINK attribute: %m");
r = sd_rtnl_message_append_in_addr(m, IFLA_GRE_LOCAL, &t->local.in);
- if (r < 0) {
- log_netdev_error(netdev,
- "Could not append IFLA_GRE_LOCAL attribute: %s",
- strerror(-r));
- return r;
- }
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append IFLA_GRE_LOCAL attribute: %m");
r = sd_rtnl_message_append_in_addr(m, IFLA_GRE_REMOTE, &t->remote.in);
- if (r < 0) {
- log_netdev_error(netdev,
- "Could not append IFLA_GRE_REMOTE attribute: %s",
- strerror(-r));
- return r;
- }
+ if (r < 0)
+ log_netdev_error_errno(netdev, r, "Could not append IFLA_GRE_REMOTE attribute: %m");
r = sd_rtnl_message_append_u8(m, IFLA_GRE_TTL, t->ttl);
- if (r < 0) {
- log_netdev_error(netdev,
- "Could not append IFLA_GRE_TTL attribute: %s",
- strerror(-r));
- return r;
- }
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append IFLA_GRE_TTL attribute: %m");
r = sd_rtnl_message_append_u8(m, IFLA_GRE_TOS, t->tos);
- if (r < 0) {
- log_netdev_error(netdev,
- "Could not append IFLA_GRE_TOS attribute: %s",
- strerror(-r));
- return r;
- }
+ if (r < 0)
+ log_netdev_error_errno(netdev, r, "Could not append IFLA_GRE_TOS attribute: %m");
r = sd_rtnl_message_append_u8(m, IFLA_GRE_PMTUDISC, t->pmtudisc);
- if (r < 0) {
- log_netdev_error(netdev,
- "Could not append IFLA_GRE_PMTUDISC attribute: %s",
- strerror(-r));
- return r;
- }
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append IFLA_GRE_PMTUDISC attribute: %m");
return r;
}
@@ -225,9 +159,9 @@ static int netdev_ip6gre_fill_message_create(NetDev *netdev, Link *link, sd_rtnl
assert(netdev);
if (netdev->kind == NETDEV_KIND_IP6GRE)
- t = IP6GRE(netdev);
+ t = IP6GRE(netdev);
else
- t = IP6GRETAP(netdev);
+ t = IP6GRETAP(netdev);
assert(t);
assert(t->family == AF_INET6);
@@ -235,36 +169,20 @@ static int netdev_ip6gre_fill_message_create(NetDev *netdev, Link *link, sd_rtnl
assert(m);
r = sd_rtnl_message_append_u32(m, IFLA_GRE_LINK, link->ifindex);
- if (r < 0) {
- log_netdev_error(netdev,
- "Could not append IFLA_GRE_LINK attribute: %s",
- strerror(-r));
- return r;
- }
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append IFLA_GRE_LINK attribute: %m");
r = sd_rtnl_message_append_in6_addr(m, IFLA_GRE_LOCAL, &t->local.in6);
- if (r < 0) {
- log_netdev_error(netdev,
- "Could not append IFLA_GRE_LOCAL attribute: %s",
- strerror(-r));
- return r;
- }
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append IFLA_GRE_LOCAL attribute: %m");
r = sd_rtnl_message_append_in6_addr(m, IFLA_GRE_REMOTE, &t->remote.in6);
- if (r < 0) {
- log_netdev_error(netdev,
- "Could not append IFLA_GRE_REMOTE attribute: %s",
- strerror(-r));
- return r;
- }
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append IFLA_GRE_REMOTE attribute: %m");
r = sd_rtnl_message_append_u8(m, IFLA_GRE_TTL, t->ttl);
- if (r < 0) {
- log_netdev_error(netdev,
- "Could not append IFLA_GRE_TTL attribute: %s",
- strerror(-r));
- return r;
- }
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append IFLA_GRE_TTL attribute: %m");
return r;
}
@@ -280,28 +198,41 @@ static int netdev_vti_fill_message_create(NetDev *netdev, Link *link, sd_rtnl_me
assert(t->family == AF_INET);
r = sd_rtnl_message_append_u32(m, IFLA_VTI_LINK, link->ifindex);
- if (r < 0) {
- log_netdev_error(netdev,
- "Could not append IFLA_IPTUN_LINK attribute: %s",
- strerror(-r));
- return r;
- }
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_LINK attribute: %m");
r = sd_rtnl_message_append_in_addr(m, IFLA_VTI_LOCAL, &t->local.in);
- if (r < 0) {
- log_netdev_error(netdev,
- "Could not append IFLA_IPTUN_LOCAL attribute: %s",
- strerror(-r));
- return r;
- }
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_LOCAL attribute: %m");
r = sd_rtnl_message_append_in_addr(m, IFLA_VTI_REMOTE, &t->remote.in);
- if (r < 0) {
- log_netdev_error(netdev,
- "Could not append IFLA_IPTUN_REMOTE attribute: %s",
- strerror(-r));
- return r;
- }
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_REMOTE attribute: %m");
+
+ return r;
+}
+
+static int netdev_vti6_fill_message_create(NetDev *netdev, Link *link, sd_rtnl_message *m) {
+ Tunnel *t = VTI6(netdev);
+ int r;
+
+ assert(netdev);
+ assert(link);
+ assert(m);
+ assert(t);
+ assert(t->family == AF_INET6);
+
+ r = sd_rtnl_message_append_u32(m, IFLA_VTI_LINK, link->ifindex);
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_LINK attribute: %m");
+
+ r = sd_rtnl_message_append_in6_addr(m, IFLA_VTI_LOCAL, &t->local.in6);
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_LOCAL attribute: %m");
+
+ r = sd_rtnl_message_append_in6_addr(m, IFLA_VTI_REMOTE, &t->remote.in6);
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_REMOTE attribute: %m");
return r;
}
@@ -318,36 +249,20 @@ static int netdev_ip6tnl_fill_message_create(NetDev *netdev, Link *link, sd_rtnl
assert(t->family == AF_INET6);
r = sd_rtnl_message_append_u32(m, IFLA_IPTUN_LINK, link->ifindex);
- if (r < 0) {
- log_netdev_error(netdev,
- "Could not append IFLA_IPTUN_LINK attribute: %s",
- strerror(-r));
- return r;
- }
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_LINK attribute: %m");
r = sd_rtnl_message_append_in6_addr(m, IFLA_IPTUN_LOCAL, &t->local.in6);
- if (r < 0) {
- log_netdev_error(netdev,
- "Could not append IFLA_IPTUN_LOCAL attribute: %s",
- strerror(-r));
- return r;
- }
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_LOCAL attribute: %m");
r = sd_rtnl_message_append_in6_addr(m, IFLA_IPTUN_REMOTE, &t->remote.in6);
- if (r < 0) {
- log_netdev_error(netdev,
- "Could not append IFLA_IPTUN_REMOTE attribute: %s",
- strerror(-r));
- return r;
- }
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_REMOTE attribute: %m");
r = sd_rtnl_message_append_u8(m, IFLA_IPTUN_TTL, t->ttl);
- if (r < 0) {
- log_netdev_error(netdev,
- "Could not append IFLA_IPTUN_TTL attribute: %s",
- strerror(-r));
- return r;
- }
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_TTL attribute: %m");
switch (t->ip6tnl_mode) {
case NETDEV_IP6_TNL_MODE_IP6IP6:
@@ -363,12 +278,8 @@ static int netdev_ip6tnl_fill_message_create(NetDev *netdev, Link *link, sd_rtnl
}
r = sd_rtnl_message_append_u8(m, IFLA_IPTUN_PROTO, proto);
- if (r < 0) {
- log_netdev_error(netdev,
- "Could not append IFLA_IPTUN_MODE attribute: %s",
- strerror(-r));
- return r;
- }
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_MODE attribute: %m");
return r;
}
@@ -401,6 +312,9 @@ static int netdev_tunnel_verify(NetDev *netdev, const char *filename) {
case NETDEV_KIND_VTI:
t = VTI(netdev);
break;
+ case NETDEV_KIND_VTI6:
+ t = VTI6(netdev);
+ break;
case NETDEV_KIND_IP6TNL:
t = IP6TNL(netdev);
break;
@@ -411,13 +325,13 @@ static int netdev_tunnel_verify(NetDev *netdev, const char *filename) {
assert(t);
if (t->remote.in.s_addr == INADDR_ANY) {
- log_warning("Tunnel without remote address configured in %s. Ignoring", filename);
- return -EINVAL;
+ log_warning("Tunnel without remote address configured in %s. Ignoring", filename);
+ return -EINVAL;
}
if (t->family != AF_INET && t->family != AF_INET6) {
- log_warning("Tunnel with invalid address family configured in %s. Ignoring", filename);
- return -EINVAL;
+ log_warning("Tunnel with invalid address family configured in %s. Ignoring", filename);
+ return -EINVAL;
}
if (netdev->kind == NETDEV_KIND_IP6TNL) {
@@ -485,9 +399,15 @@ static void sit_init(NetDev *n) {
}
static void vti_init(NetDev *n) {
- Tunnel *t = VTI(n);
+ Tunnel *t;
assert(n);
+
+ if (n->kind == NETDEV_KIND_VTI)
+ t = VTI(n);
+ else
+ t = VTI6(n);
+
assert(t);
t->pmtudisc = true;
@@ -561,6 +481,15 @@ const NetDevVTable vti_vtable = {
.config_verify = netdev_tunnel_verify,
};
+const NetDevVTable vti6_vtable = {
+ .object_size = sizeof(Tunnel),
+ .init = vti_init,
+ .sections = "Match\0NetDev\0Tunnel\0",
+ .fill_message_create = netdev_vti6_fill_message_create,
+ .create_type = NETDEV_CREATE_STACKED,
+ .config_verify = netdev_tunnel_verify,
+};
+
const NetDevVTable gre_vtable = {
.object_size = sizeof(Tunnel),
.init = gre_init,
diff --git a/src/network/networkd-netdev-tunnel.h b/src/network/networkd-netdev-tunnel.h
index 453d73c596..88f57ac105 100644
--- a/src/network/networkd-netdev-tunnel.h
+++ b/src/network/networkd-netdev-tunnel.h
@@ -55,6 +55,7 @@ struct Tunnel {
extern const NetDevVTable ipip_vtable;
extern const NetDevVTable sit_vtable;
extern const NetDevVTable vti_vtable;
+extern const NetDevVTable vti6_vtable;
extern const NetDevVTable gre_vtable;
extern const NetDevVTable gretap_vtable;
extern const NetDevVTable ip6gre_vtable;
diff --git a/src/network/networkd-netdev-tuntap.c b/src/network/networkd-netdev-tuntap.c
index 4f449aea48..4e974927cb 100644
--- a/src/network/networkd-netdev-tuntap.c
+++ b/src/network/networkd-netdev-tuntap.c
@@ -108,7 +108,7 @@ static int netdev_tuntap_add(NetDev *netdev, struct ifreq *ifr) {
}
}
- if(t->group_name) {
+ if (t->group_name) {
group = t->group_name;
@@ -173,15 +173,11 @@ static void tuntap_done(NetDev *netdev) {
static int tuntap_verify(NetDev *netdev, const char *filename) {
assert(netdev);
- if (netdev->mtu) {
- log_warning_netdev(netdev, "MTU configured for %s, ignoring",
- netdev_kind_to_string(netdev->kind));
- }
+ if (netdev->mtu)
+ log_netdev_warning(netdev, "MTU configured for %s, ignoring", netdev_kind_to_string(netdev->kind));
- if (netdev->mac) {
- log_warning_netdev(netdev, "MAC configured for %s, ignoring",
- netdev_kind_to_string(netdev->kind));
- }
+ if (netdev->mac)
+ log_netdev_warning(netdev, "MAC configured for %s, ignoring", netdev_kind_to_string(netdev->kind));
return 0;
}
diff --git a/src/network/networkd-netdev-veth.c b/src/network/networkd-netdev-veth.c
index 251cf92aec..9e9e1225e7 100644
--- a/src/network/networkd-netdev-veth.c
+++ b/src/network/networkd-netdev-veth.c
@@ -19,8 +19,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <netinet/ether.h>
-#include <arpa/inet.h>
#include <net/if.h>
#include <linux/veth.h>
diff --git a/src/network/networkd-netdev-vlan.c b/src/network/networkd-netdev-vlan.c
index 665559f895..0ed024b41d 100644
--- a/src/network/networkd-netdev-vlan.c
+++ b/src/network/networkd-netdev-vlan.c
@@ -22,8 +22,6 @@
#include <net/if.h>
#include "networkd-netdev-vlan.h"
-#include "network-internal.h"
-#include "list.h"
static int netdev_vlan_fill_message_create(NetDev *netdev, Link *link, sd_rtnl_message *req) {
VLan *v = VLAN(netdev);
diff --git a/src/network/networkd-netdev-vxlan.c b/src/network/networkd-netdev-vxlan.c
index d5128cb7c0..e2c2b108b9 100644
--- a/src/network/networkd-netdev-vxlan.c
+++ b/src/network/networkd-netdev-vxlan.c
@@ -19,8 +19,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <netinet/ether.h>
-#include <arpa/inet.h>
#include <net/if.h>
#include "sd-rtnl.h"
@@ -135,6 +133,30 @@ static int netdev_vxlan_fill_message_create(NetDev *netdev, Link *link, sd_rtnl_
}
}
+ r = sd_rtnl_message_append_u8(m, IFLA_VXLAN_UDP_CSUM, v->udpcsum);
+ if (r < 0) {
+ log_netdev_error(netdev,
+ "Could not append IFLA_VXLAN_UDP_CSUM attribute: %s",
+ strerror(-r));
+ return r;
+ }
+
+ r = sd_rtnl_message_append_u8(m, IFLA_VXLAN_UDP_ZERO_CSUM6_TX, v->udp6zerocsumtx);
+ if (r < 0) {
+ log_netdev_error(netdev,
+ "Could not append IFLA_VXLAN_UDP_ZERO_CSUM6_TX attribute: %s",
+ strerror(-r));
+ return r;
+ }
+
+ r = sd_rtnl_message_append_u8(m, IFLA_VXLAN_UDP_ZERO_CSUM6_RX, v->udp6zerocsumrx);
+ if (r < 0) {
+ log_netdev_error(netdev,
+ "Could not append IFLA_VXLAN_UDP_ZERO_CSUM6_RX attribute: %s",
+ strerror(-r));
+ return r;
+ }
+
return r;
}
@@ -199,6 +221,9 @@ static void vxlan_init(NetDev *netdev) {
v->id = VXLAN_VID_MAX + 1;
v->learning = true;
+ v->udpcsum = false;
+ v->udp6zerocsumtx = false;
+ v->udp6zerocsumrx = false;
}
const NetDevVTable vxlan_vtable = {
diff --git a/src/network/networkd-netdev-vxlan.h b/src/network/networkd-netdev-vxlan.h
index 6339af9add..fe5254e91f 100644
--- a/src/network/networkd-netdev-vxlan.h
+++ b/src/network/networkd-netdev-vxlan.h
@@ -47,6 +47,9 @@ struct VxLan {
bool route_short_circuit;
bool l2miss;
bool l3miss;
+ bool udpcsum;
+ bool udp6zerocsumtx;
+ bool udp6zerocsumrx;
};
extern const NetDevVTable vxlan_vtable;
diff --git a/src/network/networkd-netdev.c b/src/network/networkd-netdev.c
index 8119205dde..66fd0fac0a 100644
--- a/src/network/networkd-netdev.c
+++ b/src/network/networkd-netdev.c
@@ -24,7 +24,6 @@
#include "networkd-netdev.h"
#include "networkd-link.h"
#include "network-internal.h"
-#include "path-util.h"
#include "conf-files.h"
#include "conf-parser.h"
#include "list.h"
@@ -44,6 +43,7 @@ const NetDevVTable * const netdev_vtable[_NETDEV_KIND_MAX] = {
[NETDEV_KIND_IP6GRETAP] = &ip6gretap_vtable,
[NETDEV_KIND_SIT] = &sit_vtable,
[NETDEV_KIND_VTI] = &vti_vtable,
+ [NETDEV_KIND_VTI6] = &vti6_vtable,
[NETDEV_KIND_VETH] = &veth_vtable,
[NETDEV_KIND_DUMMY] = &dummy_vtable,
[NETDEV_KIND_TUN] = &tun_vtable,
@@ -66,6 +66,7 @@ static const char* const netdev_kind_table[_NETDEV_KIND_MAX] = {
[NETDEV_KIND_SIT] = "sit",
[NETDEV_KIND_VETH] = "veth",
[NETDEV_KIND_VTI] = "vti",
+ [NETDEV_KIND_VTI6] = "vti6",
[NETDEV_KIND_DUMMY] = "dummy",
[NETDEV_KIND_TUN] = "tun",
[NETDEV_KIND_TAP] = "tap",
@@ -191,34 +192,21 @@ static int netdev_enslave_ready(NetDev *netdev, Link* link, sd_rtnl_message_hand
assert(link);
assert(callback);
- r = sd_rtnl_message_new_link(netdev->manager->rtnl, &req,
- RTM_SETLINK, link->ifindex);
- if (r < 0) {
- log_netdev_error(netdev,
- "Could not allocate RTM_SETLINK message: %s",
- strerror(-r));
- return r;
- }
+ r = sd_rtnl_message_new_link(netdev->manager->rtnl, &req, RTM_SETLINK, link->ifindex);
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not allocate RTM_SETLINK message: %m");
r = sd_rtnl_message_append_u32(req, IFLA_MASTER, netdev->ifindex);
- if (r < 0) {
- log_netdev_error(netdev,
- "Could not append IFLA_MASTER attribute: %s",
- strerror(-r));
- return r;
- }
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append IFLA_MASTER attribute: %m");
r = sd_rtnl_call_async(netdev->manager->rtnl, req, callback, link, 0, NULL);
- if (r < 0) {
- log_netdev_error(netdev,
- "Could not send rtnetlink message: %s",
- strerror(-r));
- return r;
- }
+ if (r < 0)
+ return log_netdev_error(netdev, "Could not send rtnetlink message: %m");
link_ref(link);
- log_netdev_debug(netdev, "enslaving link '%s'", link->ifname);
+ log_netdev_debug(netdev, "Enslaving link '%s'", link->ifname);
return 0;
}
@@ -235,7 +223,7 @@ static int netdev_enter_ready(NetDev *netdev) {
netdev->state = NETDEV_STATE_READY;
- log_info_netdev(netdev, "netdev ready");
+ log_netdev_info(netdev, "netdev ready");
LIST_FOREACH_SAFE(callbacks, callback, callback_next, netdev->callbacks) {
/* enslave the links that were attempted to be enslaved before the
@@ -261,15 +249,15 @@ static int netdev_create_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userda
r = sd_rtnl_message_get_errno(m);
if (r == -EEXIST)
- log_netdev_debug(netdev, "netdev exists, using existing");
+ log_netdev_info(netdev, "netdev exists, using existing without changing its parameters");
else if (r < 0) {
- log_warning_netdev(netdev, "netdev could not be created: %s", strerror(-r));
+ log_netdev_warning_errno(netdev, r, "netdev could not be created: %m");
netdev_drop(netdev);
return 1;
}
- log_netdev_debug(netdev, "created");
+ log_netdev_debug(netdev, "Created");
return 1;
}
@@ -298,8 +286,7 @@ int netdev_enslave(NetDev *netdev, Link *link, sd_rtnl_message_handler_t callbac
LIST_PREPEND(callbacks, netdev->callbacks, cb);
- log_netdev_debug(netdev, "will enslave '%s', when reday",
- link->ifname);
+ log_netdev_debug(netdev, "Will enslave '%s', when ready", link->ifname);
}
return 0;
@@ -316,25 +303,23 @@ int netdev_set_ifindex(NetDev *netdev, sd_rtnl_message *message) {
assert(message);
r = sd_rtnl_message_get_type(message, &type);
- if (r < 0) {
- log_netdev_error(netdev, "Could not get rtnl message type");
- return r;
- }
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not get rtnl message type: %m");
if (type != RTM_NEWLINK) {
- log_netdev_error(netdev, "Can not set ifindex from unexpected rtnl message type");
+ log_netdev_error(netdev, "Cannot set ifindex from unexpected rtnl message type.");
return -EINVAL;
}
r = sd_rtnl_message_link_get_ifindex(message, &ifindex);
if (r < 0) {
- log_netdev_error(netdev, "Could not get ifindex: %s", strerror(-r));
+ log_netdev_error_errno(netdev, r, "Could not get ifindex: %m");
netdev_enter_failed(netdev);
return r;
} else if (ifindex <= 0) {
log_netdev_error(netdev, "Got invalid ifindex: %d", ifindex);
netdev_enter_failed(netdev);
- return r;
+ return -EINVAL;
}
if (netdev->ifindex > 0) {
@@ -349,35 +334,26 @@ int netdev_set_ifindex(NetDev *netdev, sd_rtnl_message *message) {
}
r = sd_rtnl_message_read_string(message, IFLA_IFNAME, &received_name);
- if (r < 0) {
- log_netdev_error(netdev, "Could not get IFNAME");
- return r;
- }
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not get IFNAME: %m");
if (!streq(netdev->ifname, received_name)) {
- log_netdev_error(netdev, "Received newlink with wrong IFNAME %s",
- received_name);
+ log_netdev_error(netdev, "Received newlink with wrong IFNAME %s", received_name);
netdev_enter_failed(netdev);
return r;
}
r = sd_rtnl_message_enter_container(message, IFLA_LINKINFO);
- if (r < 0) {
- log_netdev_error(netdev, "Could not get LINKINFO");
- return r;
- }
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not get LINKINFO: %m");
r = sd_rtnl_message_read_string(message, IFLA_INFO_KIND, &received_kind);
- if (r < 0) {
- log_netdev_error(netdev, "Could not get KIND");
- return r;
- }
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not get KIND: %m");
r = sd_rtnl_message_exit_container(message);
- if (r < 0) {
- log_netdev_error(netdev, "Could not exit container");
- return r;
- }
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not exit container: %m");
if (netdev->kind == NETDEV_KIND_TAP)
/* the kernel does not distinguish between tun and tap */
@@ -469,72 +445,43 @@ static int netdev_create(NetDev *netdev, Link *link,
if (r < 0)
return r;
- log_netdev_debug(netdev, "created");
+ log_netdev_debug(netdev, "Created");
} else {
_cleanup_rtnl_message_unref_ sd_rtnl_message *m = NULL;
r = sd_rtnl_message_new_link(netdev->manager->rtnl, &m, RTM_NEWLINK, 0);
- if (r < 0) {
- log_netdev_error(netdev,
- "Could not allocate RTM_NEWLINK message: %s",
- strerror(-r));
- return r;
- }
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not allocate RTM_NEWLINK message: %m");
r = sd_rtnl_message_append_string(m, IFLA_IFNAME, netdev->ifname);
- if (r < 0) {
- log_netdev_error(netdev,
- "Could not append IFLA_IFNAME, attribute: %s",
- strerror(-r));
- return r;
- }
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append IFLA_IFNAME, attribute: %m");
if (netdev->mac) {
r = sd_rtnl_message_append_ether_addr(m, IFLA_ADDRESS, netdev->mac);
- if (r < 0) {
- log_netdev_error(netdev,
- "Could not append IFLA_ADDRESS attribute: %s",
- strerror(-r));
- return r;
- }
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append IFLA_ADDRESS attribute: %m");
}
if (netdev->mtu) {
r = sd_rtnl_message_append_u32(m, IFLA_MTU, netdev->mtu);
- if (r < 0) {
- log_netdev_error(netdev,
- "Could not append IFLA_MTU attribute: %s",
- strerror(-r));
- return r;
- }
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append IFLA_MTU attribute: %m");
}
if (link) {
r = sd_rtnl_message_append_u32(m, IFLA_LINK, link->ifindex);
- if (r < 0) {
- log_netdev_error(netdev,
- "Could not append IFLA_LINK attribute: %s",
- strerror(-r));
- return r;
- }
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append IFLA_LINK attribute: %m");
}
r = sd_rtnl_message_open_container(m, IFLA_LINKINFO);
- if (r < 0) {
- log_netdev_error(netdev,
- "Could not append IFLA_LINKINFO attribute: %s",
- strerror(-r));
- return r;
- }
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append IFLA_LINKINFO attribute: %m");
- r = sd_rtnl_message_open_container_union(m, IFLA_INFO_DATA,
- netdev_kind_to_string(netdev->kind));
- if (r < 0) {
- log_netdev_error(netdev,
- "Could not append IFLA_INFO_DATA attribute: %s",
- strerror(-r));
- return r;
- }
+ r = sd_rtnl_message_open_container_union(m, IFLA_INFO_DATA, netdev_kind_to_string(netdev->kind));
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append IFLA_INFO_DATA attribute: %m");
if (NETDEV_VTABLE(netdev)->fill_message_create) {
r = NETDEV_VTABLE(netdev)->fill_message_create(netdev, link, m);
@@ -543,50 +490,30 @@ static int netdev_create(NetDev *netdev, Link *link,
}
r = sd_rtnl_message_close_container(m);
- if (r < 0) {
- log_netdev_error(netdev,
- "Could not append IFLA_LINKINFO attribute: %s",
- strerror(-r));
- return r;
- }
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append IFLA_LINKINFO attribute: %m");
r = sd_rtnl_message_close_container(m);
- if (r < 0) {
- log_netdev_error(netdev,
- "Could not append IFLA_LINKINFO attribute: %s",
- strerror(-r));
- return r;
- }
-
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append IFLA_LINKINFO attribute: %m");
if (link) {
- r = sd_rtnl_call_async(netdev->manager->rtnl, m,
- callback, link, 0, NULL);
- if (r < 0) {
- log_netdev_error(netdev,
- "Could not send rtnetlink message: %s",
- strerror(-r));
- return r;
- }
+ r = sd_rtnl_call_async(netdev->manager->rtnl, m, callback, link, 0, NULL);
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not send rtnetlink message: %m");
link_ref(link);
} else {
- r = sd_rtnl_call_async(netdev->manager->rtnl, m,
- netdev_create_handler, netdev, 0,
- NULL);
- if (r < 0) {
- log_netdev_error(netdev,
- "Could not send rtnetlink message: %s",
- strerror(-r));
- return r;
- }
+ r = sd_rtnl_call_async(netdev->manager->rtnl, m, netdev_create_handler, netdev, 0, NULL);
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not send rtnetlink message: %m");
netdev_ref(netdev);
}
netdev->state = NETDEV_STATE_CREATING;
- log_netdev_debug(netdev, "creating");
+ log_netdev_debug(netdev, "Creating");
}
return 0;
@@ -710,11 +637,8 @@ static int netdev_load_one(Manager *manager, const char *filename) {
if (!netdev->mac) {
r = netdev_get_mac(netdev->ifname, &netdev->mac);
- if (r < 0) {
- log_error("Failed to generate predictable MAC address for %s",
- netdev->ifname);
- return r;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to generate predictable MAC address for %s: %m", netdev->ifname);
}
r = hashmap_put(netdev->manager->netdevs, netdev->ifname, netdev);
@@ -743,8 +667,9 @@ static int netdev_load_one(Manager *manager, const char *filename) {
}
int netdev_load(Manager *manager) {
+ _cleanup_strv_free_ char **files = NULL;
NetDev *netdev;
- char **files, **f;
+ char **f;
int r;
assert(manager);
@@ -762,7 +687,5 @@ int netdev_load(Manager *manager) {
return r;
}
- strv_free(files);
-
return 0;
}
diff --git a/src/network/networkd-netdev.h b/src/network/networkd-netdev.h
index 3756b1e5a7..1ded495e4a 100644
--- a/src/network/networkd-netdev.h
+++ b/src/network/networkd-netdev.h
@@ -22,10 +22,7 @@
#pragma once
#include "networkd.h"
-#include "hashmap.h"
#include "list.h"
-#include "set.h"
-#include "in-addr-util.h"
typedef struct NetDevVTable NetDevVTable;
@@ -53,6 +50,7 @@ typedef enum NetDevKind {
NETDEV_KIND_SIT,
NETDEV_KIND_VETH,
NETDEV_KIND_VTI,
+ NETDEV_KIND_VTI6,
NETDEV_KIND_IP6TNL,
NETDEV_KIND_DUMMY,
NETDEV_KIND_TUN,
@@ -172,6 +170,7 @@ DEFINE_CAST(IP6GRE, Tunnel);
DEFINE_CAST(IP6GRETAP, Tunnel);
DEFINE_CAST(SIT, Tunnel);
DEFINE_CAST(VTI, Tunnel);
+DEFINE_CAST(VTI6, Tunnel);
DEFINE_CAST(IP6TNL, Tunnel);
DEFINE_CAST(VETH, Veth);
DEFINE_CAST(DUMMY, Dummy);
@@ -203,13 +202,24 @@ const struct ConfigPerfItem* network_netdev_gperf_lookup(const char *key, unsign
/* Macros which append INTERFACE= to the message */
-#define log_full_netdev(level, netdev, fmt, ...) log_object_internal(level, 0, __FILE__, __LINE__, __func__, "INTERFACE=", netdev->ifname, "%-*s: " fmt, IFNAMSIZ, netdev->ifname, ##__VA_ARGS__)
-#define log_netdev_debug(netdev, ...) log_full_netdev(LOG_DEBUG, netdev, ##__VA_ARGS__)
-#define log_info_netdev(netdev, ...) log_full_netdev(LOG_INFO, netdev, ##__VA_ARGS__)
-#define log_notice_netdev(netdev, ...) log_full_netdev(LOG_NOTICE, netdev, ##__VA_ARGS__)
-#define log_warning_netdev(netdev, ...) log_full_netdev(LOG_WARNING, netdev,## __VA_ARGS__)
-#define log_netdev_error(netdev, ...) log_full_netdev(LOG_ERR, netdev, ##__VA_ARGS__)
-
-#define log_struct_netdev(level, netdev, ...) log_struct(level, "INTERFACE=%s", netdev->ifname, __VA_ARGS__)
-
-#define NETDEVIF(netdev) "INTERFACE=%s", netdev->ifname
+#define log_netdev_full(netdev, level, error, ...) \
+ ({ \
+ NetDev *_n = (netdev); \
+ _n ? log_object_internal(level, error, __FILE__, __LINE__, __func__, "INTERFACE=", _n->ifname, ##__VA_ARGS__) : \
+ log_internal(level, error, __FILE__, __LINE__, __func__, ##__VA_ARGS__); \
+ })
+
+#define log_netdev_debug(netdev, ...) log_netdev_full(netdev, LOG_DEBUG, 0, ##__VA_ARGS__)
+#define log_netdev_info(netdev, ...) log_netdev_full(netdev, LOG_INFO, 0, ##__VA_ARGS__)
+#define log_netdev_notice(netdev, ...) log_netdev_full(netdev, LOG_NOTICE, 0, ##__VA_ARGS__)
+#define log_netdev_warning(netdev, ...) log_netdev_full(netdev, LOG_WARNING, 0, ## __VA_ARGS__)
+#define log_netdev_error(netdev, ...) log_netdev_full(netdev, LOG_ERR, 0, ##__VA_ARGS__)
+
+#define log_netdev_debug_errno(netdev, error, ...) log_netdev_full(netdev, LOG_DEBUG, error, ##__VA_ARGS__)
+#define log_netdev_info_errno(netdev, error, ...) log_netdev_full(netdev, LOG_INFO, error, ##__VA_ARGS__)
+#define log_netdev_notice_errno(netdev, error, ...) log_netdev_full(netdev, LOG_NOTICE, error, ##__VA_ARGS__)
+#define log_netdev_warning_errno(netdev, error, ...) log_netdev_full(netdev, LOG_WARNING, error, ##__VA_ARGS__)
+#define log_netdev_error_errno(netdev, error, ...) log_netdev_full(netdev, LOG_ERR, error, ##__VA_ARGS__)
+
+#define LOG_NETDEV_MESSAGE(netdev, fmt, ...) "MESSAGE=%s: " fmt, (netdev)->ifname, ##__VA_ARGS__
+#define LOG_NETDEV_INTERFACE(netdev) "INTERFACE=%s", (netdev)->ifname
diff --git a/src/network/networkd-network-bus.c b/src/network/networkd-network-bus.c
index 36b40d32b9..b5f8f5cfb2 100644
--- a/src/network/networkd-network-bus.c
+++ b/src/network/networkd-network-bus.c
@@ -19,8 +19,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "bus-util.h"
-#include "bus-label.h"
#include "strv.h"
#include "networkd.h"
diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf
index fc277df949..8abf5bcf2e 100644
--- a/src/network/networkd-network-gperf.gperf
+++ b/src/network/networkd-network-gperf.gperf
@@ -48,6 +48,7 @@ Network.LLMNR, config_parse_llmnr, 0,
Network.NTP, config_parse_strv, 0, offsetof(Network, ntp)
Network.IPForward, config_parse_address_family_boolean,0, offsetof(Network, ip_forward)
Network.IPMasquerade, config_parse_bool, 0, offsetof(Network, ip_masquerade)
+Network.BindCarrier, config_parse_strv, 0, offsetof(Network, bind_carrier)
Address.Address, config_parse_address, 0, 0
Address.Peer, config_parse_address, 0, 0
Address.Broadcast, config_parse_broadcast, 0, 0
@@ -57,7 +58,9 @@ Route.Destination, config_parse_destination, 0,
Route.Source, config_parse_destination, 0, 0
Route.Metric, config_parse_route_priority, 0, 0
Route.Scope, config_parse_route_scope, 0, 0
+DHCP.ClientIdentifier, config_parse_dhcp_client_identifier,0, offsetof(Network, dhcp_client_identifier)
DHCP.UseDNS, config_parse_bool, 0, offsetof(Network, dhcp_dns)
+DHCP.UseNTP, config_parse_bool, 0, offsetof(Network, dhcp_ntp)
DHCP.UseMTU, config_parse_bool, 0, offsetof(Network, dhcp_mtu)
DHCP.UseHostname, config_parse_bool, 0, offsetof(Network, dhcp_hostname)
DHCP.UseDomains, config_parse_bool, 0, offsetof(Network, dhcp_domains)
diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c
index 35ac064cf6..5947084106 100644
--- a/src/network/networkd-network.c
+++ b/src/network/networkd-network.c
@@ -22,10 +22,10 @@
#include <ctype.h>
#include <net/if.h>
-#include "path-util.h"
#include "conf-files.h"
#include "conf-parser.h"
#include "util.h"
+#include "hostname-util.h"
#include "networkd.h"
#include "networkd-netdev.h"
#include "networkd-link.h"
@@ -104,6 +104,7 @@ static int network_load_one(Manager *manager, const char *filename) {
network->dhcp_routes = true;
network->dhcp_sendhost = true;
network->dhcp_route_metric = DHCP_ROUTE_METRIC;
+ network->dhcp_client_identifier = DHCP_CLIENT_ID_DUID;
network->llmnr = LLMNR_SUPPORT_YES;
@@ -209,6 +210,7 @@ void network_free(Network *network) {
strv_free(network->ntp);
strv_free(network->dns);
strv_free(network->domains);
+ strv_free(network->bind_carrier);
netdev_unref(network->bridge);
@@ -271,29 +273,39 @@ int network_get(Manager *manager, struct udev_device *device,
const char *ifname, const struct ether_addr *address,
Network **ret) {
Network *network;
+ struct udev_device *parent;
+ const char *path = NULL, *parent_driver = NULL, *driver = NULL, *devtype = NULL;
assert(manager);
assert(ret);
+ if (device) {
+ path = udev_device_get_property_value(device, "ID_PATH");
+
+ parent = udev_device_get_parent(device);
+ if (parent)
+ parent_driver = udev_device_get_driver(parent);
+
+ driver = udev_device_get_property_value(device, "ID_NET_DRIVER");
+
+ devtype = udev_device_get_devtype(device);
+ }
+
LIST_FOREACH(networks, network, manager->networks) {
if (net_match_config(network->match_mac, network->match_path,
network->match_driver, network->match_type,
network->match_name, network->match_host,
network->match_virt, network->match_kernel,
network->match_arch,
- address,
- udev_device_get_property_value(device, "ID_PATH"),
- udev_device_get_driver(udev_device_get_parent(device)),
- udev_device_get_property_value(device, "ID_NET_DRIVER"),
- udev_device_get_devtype(device),
- ifname)) {
- if (network->match_name) {
+ address, path, parent_driver, driver,
+ devtype, ifname)) {
+ if (network->match_name && device) {
const char *attr;
uint8_t name_assign_type = NET_NAME_UNKNOWN;
attr = udev_device_get_sysattr_value(device, "name_assign_type");
if (attr)
- (void)safe_atou8(attr, &name_assign_type);
+ (void) safe_atou8(attr, &name_assign_type);
if (name_assign_type == NET_NAME_ENUM)
log_warning("%-*s: found matching network '%s', based on potentially unpredictable ifname",
@@ -413,8 +425,8 @@ int config_parse_netdev(const char *unit,
r = hashmap_put(network->stacked_netdevs, netdev->ifname, netdev);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Can not add VLAN '%s' to network: %s",
- rvalue, strerror(-r));
+ "Can not add VLAN '%s' to network: %m",
+ rvalue);
return 0;
}
@@ -490,8 +502,7 @@ int config_parse_tunnel(const char *unit,
r = netdev_get(network->manager, rvalue, &netdev);
if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Tunnel is invalid, ignoring assignment: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, r, "Tunnel is invalid, ignoring assignment: %s", rvalue);
return 0;
}
@@ -502,6 +513,7 @@ int config_parse_tunnel(const char *unit,
netdev->kind != NETDEV_KIND_IP6GRE &&
netdev->kind != NETDEV_KIND_IP6GRETAP &&
netdev->kind != NETDEV_KIND_VTI &&
+ netdev->kind != NETDEV_KIND_VTI6 &&
netdev->kind != NETDEV_KIND_IP6TNL
) {
log_syntax(unit, LOG_ERR, filename, line, EINVAL,
@@ -511,9 +523,7 @@ int config_parse_tunnel(const char *unit,
r = hashmap_put(network->stacked_netdevs, netdev->ifname, netdev);
if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Can not add VLAN '%s' to network: %s",
- rvalue, strerror(-r));
+ log_syntax(unit, LOG_ERR, filename, line, r, "Cannot add VLAN '%s' to network, ignoring: %m", rvalue);
return 0;
}
@@ -600,6 +610,14 @@ int config_parse_dhcp(
return 0;
}
+static const char* const dhcp_client_identifier_table[_DHCP_CLIENT_ID_MAX] = {
+ [DHCP_CLIENT_ID_MAC] = "mac",
+ [DHCP_CLIENT_ID_DUID] = "duid"
+};
+
+DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(dhcp_client_identifier, DCHPClientIdentifier);
+DEFINE_CONFIG_PARSE_ENUM(config_parse_dhcp_client_identifier, dhcp_client_identifier, DCHPClientIdentifier, "Failed to parse client identifier type");
+
static const char* const llmnr_support_table[_LLMNR_SUPPORT_MAX] = {
[LLMNR_SUPPORT_NO] = "no",
[LLMNR_SUPPORT_YES] = "yes",
@@ -674,13 +692,13 @@ int config_parse_ipv6token(
r = in_addr_from_string(AF_INET6, rvalue, &buffer);
if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, -r, "Failed to parse IPv6 token, ignoring: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse IPv6 token, ignoring: %s", rvalue);
return 0;
}
r = in_addr_is_null(AF_INET6, &buffer);
if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, -r, "IPv6 token can not be the ANY address, ignoring: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, r, "IPv6 token can not be the ANY address, ignoring: %s", rvalue);
return 0;
}
diff --git a/src/network/networkd-route.c b/src/network/networkd-route.c
index c2d1ffca25..7f110a5217 100644
--- a/src/network/networkd-route.c
+++ b/src/network/networkd-route.c
@@ -19,15 +19,12 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <net/if.h>
#include "networkd.h"
#include "networkd-link.h"
-#include "utf8.h"
#include "util.h"
#include "conf-parser.h"
-#include "network-internal.h"
int route_new_static(Network *network, unsigned section, Route **ret) {
_cleanup_route_free_ Route *route = NULL;
diff --git a/src/network/networkd-wait-online-link.c b/src/network/networkd-wait-online-link.c
index e091b20ed9..341bcae3fb 100644
--- a/src/network/networkd-wait-online-link.c
+++ b/src/network/networkd-wait-online-link.c
@@ -20,10 +20,8 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <net/if.h>
#include "sd-network.h"
-#include "strv.h"
#include "networkd-wait-online-link.h"
diff --git a/src/network/networkd-wait-online-manager.c b/src/network/networkd-wait-online-manager.c
index cafe110e50..1c997a50a4 100644
--- a/src/network/networkd-wait-online-manager.c
+++ b/src/network/networkd-wait-online-manager.c
@@ -25,7 +25,6 @@
#include "rtnl-util.h"
-#include "network-util.h"
#include "network-internal.h"
#include "networkd-wait-online-link.h"
#include "networkd-wait-online.h"
diff --git a/src/network/networkd-wait-online.c b/src/network/networkd-wait-online.c
index f0ca6def87..6a96f1de55 100644
--- a/src/network/networkd-wait-online.c
+++ b/src/network/networkd-wait-online.c
@@ -21,11 +21,10 @@
#include <getopt.h>
#include "sd-daemon.h"
-
-#include "networkd-wait-online.h"
-
#include "strv.h"
#include "build.h"
+#include "signal-util.h"
+#include "networkd-wait-online.h"
static bool arg_quiet = false;
static usec_t arg_timeout = 120 * USEC_PER_SEC;
diff --git a/src/network/networkd-wait-online.h b/src/network/networkd-wait-online.h
index 66b865cfe2..73d129699d 100644
--- a/src/network/networkd-wait-online.h
+++ b/src/network/networkd-wait-online.h
@@ -25,7 +25,6 @@
#include "sd-rtnl.h"
#include "sd-network.h"
-#include "util.h"
#include "hashmap.h"
typedef struct Manager Manager;
diff --git a/src/network/networkd.c b/src/network/networkd.c
index 7319276c53..41ec7cf904 100644
--- a/src/network/networkd.c
+++ b/src/network/networkd.c
@@ -19,10 +19,9 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "capability.h"
-#include "sd-event.h"
#include "sd-daemon.h"
-
+#include "capability.h"
+#include "signal-util.h"
#include "networkd.h"
int main(int argc, char *argv[]) {
@@ -54,21 +53,19 @@ int main(int argc, char *argv[]) {
* watches in. */
r = mkdir_safe_label("/run/systemd/netif", 0755, uid, gid);
if (r < 0)
- log_error_errno(r, "Could not create runtime directory: %m");
+ log_warning_errno(r, "Could not create runtime directory: %m");
r = mkdir_safe_label("/run/systemd/netif/links", 0755, uid, gid);
if (r < 0)
- log_error_errno(r, "Could not create runtime directory 'links': %m");
+ log_warning_errno(r, "Could not create runtime directory 'links': %m");
r = mkdir_safe_label("/run/systemd/netif/leases", 0755, uid, gid);
if (r < 0)
- log_error_errno(r, "Could not create runtime directory 'leases': %m");
+ log_warning_errno(r, "Could not create runtime directory 'leases': %m");
r = mkdir_safe_label("/run/systemd/netif/lldp", 0755, uid, gid);
if (r < 0)
- log_error("Could not create runtime directory 'lldp': %s",
- strerror(-r));
-
+ log_warning_errno(r, "Could not create runtime directory 'lldp': %m");
r = drop_privileges(uid, gid,
(1ULL << CAP_NET_ADMIN) |
diff --git a/src/network/networkd.h b/src/network/networkd.h
index bdb2f20e2f..49afeffe81 100644
--- a/src/network/networkd.h
+++ b/src/network/networkd.h
@@ -60,6 +60,7 @@ typedef enum AddressFamilyBoolean {
ADDRESS_FAMILY_IPV4 = 1,
ADDRESS_FAMILY_IPV6 = 2,
ADDRESS_FAMILY_YES = 3,
+ ADDRESS_FAMILY_KERNEL = 4,
_ADDRESS_FAMILY_BOOLEAN_MAX,
_ADDRESS_FAMILY_BOOLEAN_INVALID = -1,
} AddressFamilyBoolean;
@@ -83,6 +84,13 @@ typedef enum LinkOperationalState {
_LINK_OPERSTATE_INVALID = -1
} LinkOperationalState;
+typedef enum DCHPClientIdentifier {
+ DHCP_CLIENT_ID_MAC,
+ DHCP_CLIENT_ID_DUID,
+ _DHCP_CLIENT_ID_MAX,
+ _DHCP_CLIENT_ID_INVALID = -1,
+} DCHPClientIdentifier;
+
struct FdbEntry {
Network *network;
unsigned section;
@@ -115,6 +123,7 @@ struct Network {
NetDev *bond;
Hashmap *stacked_netdevs;
AddressFamilyBoolean dhcp;
+ DCHPClientIdentifier dhcp_client_identifier;
char *dhcp_vendor_class_identifier;
bool dhcp_dns;
bool dhcp_ntp;
@@ -151,7 +160,7 @@ struct Network {
Hashmap *fdb_entries_by_section;
bool wildcard_domain;
- char **domains, **dns, **ntp;
+ char **domains, **dns, **ntp, **bind_carrier;
LLMNRSupport llmnr;
@@ -165,7 +174,7 @@ struct Address {
int family;
unsigned char prefixlen;
unsigned char scope;
- unsigned char flags;
+ uint32_t flags;
char *label;
struct in_addr broadcast;
@@ -403,6 +412,9 @@ int config_parse_fdb_vlan_id(const char *unit, const char *filename, unsigned li
int config_parse_dhcp(const char *unit, const char *filename, unsigned line,
const char *section, unsigned section_line, const char *lvalue,
int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_dhcp_client_identifier(const char *unit, const char *filename, unsigned line,
+ const char *section, unsigned section_line, const char *lvalue,
+ int ltype, const char *rvalue, void *data, void *userdata);
/* IPv4LL support (legacy) */
@@ -437,7 +449,7 @@ AddressFamilyBoolean address_family_boolean_from_string(const char *s) _const_;
int config_parse_address_family_boolean(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-/* Opeartional State */
+/* Operational State */
const char* link_operstate_to_string(LinkOperationalState s) _const_;
LinkOperationalState link_operstate_from_string(const char *s) _pure_;
diff --git a/src/network/test-network-tables.c b/src/network/test-network-tables.c
index 6709ab0957..67fcca2ac0 100644
--- a/src/network/test-network-tables.c
+++ b/src/network/test-network-tables.c
@@ -1,5 +1,4 @@
#include "networkd.h"
-#include "networkd-link.h"
#include "networkd-netdev-bond.h"
#include "networkd-netdev-macvlan.h"
#include "dhcp6-internal.h"
diff --git a/src/notify/notify.c b/src/notify/notify.c
index f98075d513..c920b29297 100644
--- a/src/notify/notify.c
+++ b/src/notify/notify.c
@@ -24,7 +24,6 @@
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
-#include <string.h>
#include "systemd/sd-daemon.h"
@@ -33,6 +32,7 @@
#include "log.h"
#include "build.h"
#include "env-util.h"
+#include "formats-util.h"
static bool arg_ready = false;
static pid_t arg_pid = 0;
@@ -198,7 +198,7 @@ int main(int argc, char* argv[]) {
}
if (r == 0)
- r = -ENOTSUP;
+ r = -EOPNOTSUPP;
finish:
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c
index fb672510b4..4211a3d779 100644
--- a/src/nspawn/nspawn.c
+++ b/src/nspawn/nspawn.c
@@ -23,27 +23,21 @@
#include <sched.h>
#include <unistd.h>
#include <sys/types.h>
-#include <sys/syscall.h>
#include <sys/mount.h>
-#include <sys/wait.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <sys/prctl.h>
#include <getopt.h>
-#include <termios.h>
-#include <sys/signalfd.h>
#include <grp.h>
#include <linux/fs.h>
-#include <sys/un.h>
#include <sys/socket.h>
#include <linux/netlink.h>
#include <net/if.h>
#include <linux/veth.h>
#include <sys/personality.h>
#include <linux/loop.h>
-#include <poll.h>
#include <sys/file.h>
#ifdef HAVE_SELINUX
@@ -62,11 +56,12 @@
#include "sd-bus.h"
#include "sd-id128.h"
#include "sd-rtnl.h"
+#include "random-util.h"
#include "log.h"
#include "util.h"
#include "mkdir.h"
+#include "rm-rf.h"
#include "macro.h"
-#include "audit.h"
#include "missing.h"
#include "cgroup-util.h"
#include "strv.h"
@@ -79,9 +74,7 @@
#include "bus-util.h"
#include "bus-error.h"
#include "ptyfwd.h"
-#include "bus-kernel.h"
#include "env-util.h"
-#include "def.h"
#include "rtnl-util.h"
#include "udev-util.h"
#include "blkid-util.h"
@@ -99,6 +92,11 @@
#include "in-addr-util.h"
#include "fw-util.h"
#include "local-addresses.h"
+#include "formats-util.h"
+#include "process-util.h"
+#include "terminal-util.h"
+#include "hostname-util.h"
+#include "signal-util.h"
#ifdef HAVE_SECCOMP
#include "seccomp-util.h"
@@ -129,6 +127,22 @@ typedef enum Volatile {
VOLATILE_STATE,
} Volatile;
+typedef enum CustomMountType {
+ CUSTOM_MOUNT_BIND,
+ CUSTOM_MOUNT_TMPFS,
+ CUSTOM_MOUNT_OVERLAY,
+} CustomMountType;
+
+typedef struct CustomMount {
+ CustomMountType type;
+ bool read_only;
+ char *source; /* for overlayfs this is the upper directory */
+ char *destination;
+ char *options;
+ char *work_dir;
+ char **lower;
+} CustomMount;
+
static char *arg_directory = NULL;
static char *arg_template = NULL;
static char *arg_user = NULL;
@@ -170,9 +184,8 @@ static uint64_t arg_retain =
(1ULL << CAP_AUDIT_WRITE) |
(1ULL << CAP_AUDIT_CONTROL) |
(1ULL << CAP_MKNOD);
-static char **arg_bind = NULL;
-static char **arg_bind_ro = NULL;
-static char **arg_tmpfs = NULL;
+static CustomMount *arg_custom_mounts = NULL;
+static unsigned arg_n_custom_mounts = 0;
static char **arg_setenv = NULL;
static bool arg_quiet = false;
static bool arg_share_system = false;
@@ -183,10 +196,14 @@ static char **arg_network_macvlan = NULL;
static char **arg_network_ipvlan = NULL;
static bool arg_network_veth = false;
static const char *arg_network_bridge = NULL;
-static unsigned long arg_personality = 0xffffffffLU;
+static unsigned long arg_personality = PERSONALITY_INVALID;
static char *arg_image = NULL;
static Volatile arg_volatile = VOLATILE_NO;
static ExposePort *arg_expose_ports = NULL;
+static char **arg_property = NULL;
+static uid_t arg_uid_shift = UID_INVALID, arg_uid_range = 0x10000U;
+static bool arg_userns = false;
+static int arg_kill_signal = 0;
static void help(void) {
printf("%s [OPTIONS...] [PATH] [ARGUMENTS...]\n\n"
@@ -205,6 +222,9 @@ static void help(void) {
" -M --machine=NAME Set the machine name for the container\n"
" --uuid=UUID Set a specific machine UUID for the container\n"
" -S --slice=SLICE Place the container in the specified slice\n"
+ " --property=NAME=VALUE Set scope unit property\n"
+ " --private-users[=UIDBASE[:NUIDS]]\n"
+ " Run within user namespace\n"
" --private-network Disable network in container\n"
" --network-interface=INTERFACE\n"
" Assign an existing network interface to the\n"
@@ -232,6 +252,7 @@ static void help(void) {
" --capability=CAP In addition to the default, retain specified\n"
" capability\n"
" --drop-capability=CAP Drop the specified capability from the default set\n"
+ " --kill-signal=SIGNAL Select signal to use for shutting down PID 1\n"
" --link-journal=MODE Link up guest journal, one of no, auto, guest, host,\n"
" try-guest, try-host\n"
" -j Equivalent to --link-journal=try-guest\n"
@@ -240,6 +261,11 @@ static void help(void) {
" the container\n"
" --bind-ro=PATH[:PATH] Similar, but creates a read-only bind mount\n"
" --tmpfs=PATH:[OPTIONS] Mount an empty tmpfs to the specified directory\n"
+ " --overlay=PATH[:PATH...]:PATH\n"
+ " Create an overlay mount from the host to \n"
+ " the container\n"
+ " --overlay-ro=PATH[:PATH...]:PATH\n"
+ " Similar, but creates a read-only overlay mount\n"
" --setenv=NAME=VALUE Pass an environment variable to PID 1\n"
" --share-system Share system namespaces with host\n"
" --register=BOOLEAN Register container as machine\n"
@@ -249,6 +275,89 @@ static void help(void) {
, program_invocation_short_name);
}
+static CustomMount* custom_mount_add(CustomMountType t) {
+ CustomMount *c, *ret;
+
+ c = realloc(arg_custom_mounts, (arg_n_custom_mounts + 1) * sizeof(CustomMount));
+ if (!c)
+ return NULL;
+
+ arg_custom_mounts = c;
+ ret = arg_custom_mounts + arg_n_custom_mounts;
+ arg_n_custom_mounts++;
+
+ *ret = (CustomMount) { .type = t };
+
+ return ret;
+}
+
+static void custom_mount_free_all(void) {
+ unsigned i;
+
+ for (i = 0; i < arg_n_custom_mounts; i++) {
+ CustomMount *m = &arg_custom_mounts[i];
+
+ free(m->source);
+ free(m->destination);
+ free(m->options);
+
+ if (m->work_dir) {
+ (void) rm_rf(m->work_dir, REMOVE_ROOT|REMOVE_PHYSICAL);
+ free(m->work_dir);
+ }
+
+ strv_free(m->lower);
+ }
+
+ free(arg_custom_mounts);
+ arg_custom_mounts = NULL;
+ arg_n_custom_mounts = 0;
+}
+
+static int custom_mount_compare(const void *a, const void *b) {
+ const CustomMount *x = a, *y = b;
+ int r;
+
+ r = path_compare(x->destination, y->destination);
+ if (r != 0)
+ return r;
+
+ if (x->type < y->type)
+ return -1;
+ if (x->type > y->type)
+ return 1;
+
+ return 0;
+}
+
+static int custom_mounts_prepare(void) {
+ unsigned i;
+ int r;
+
+ /* Ensure the mounts are applied prefix first. */
+ qsort_safe(arg_custom_mounts, arg_n_custom_mounts, sizeof(CustomMount), custom_mount_compare);
+
+ /* Allocate working directories for the overlay file systems that need it */
+ for (i = 0; i < arg_n_custom_mounts; i++) {
+ CustomMount *m = &arg_custom_mounts[i];
+
+ if (m->type != CUSTOM_MOUNT_OVERLAY)
+ continue;
+
+ if (m->work_dir)
+ continue;
+
+ if (m->read_only)
+ continue;
+
+ r = tempfn_random(m->source, &m->work_dir);
+ if (r < 0)
+ return log_error_errno(r, "Failed to generate work directory from %s: %m", m->source);
+ }
+
+ return 0;
+}
+
static int set_sanitized_path(char **b, const char *path) {
char *p;
@@ -283,6 +392,8 @@ static int parse_argv(int argc, char *argv[]) {
ARG_BIND,
ARG_BIND_RO,
ARG_TMPFS,
+ ARG_OVERLAY,
+ ARG_OVERLAY_RO,
ARG_SETENV,
ARG_SHARE_SYSTEM,
ARG_REGISTER,
@@ -294,6 +405,9 @@ static int parse_argv(int argc, char *argv[]) {
ARG_PERSONALITY,
ARG_VOLATILE,
ARG_TEMPLATE,
+ ARG_PROPERTY,
+ ARG_PRIVATE_USERS,
+ ARG_KILL_SIGNAL,
};
static const struct option options[] = {
@@ -313,6 +427,8 @@ static int parse_argv(int argc, char *argv[]) {
{ "bind", required_argument, NULL, ARG_BIND },
{ "bind-ro", required_argument, NULL, ARG_BIND_RO },
{ "tmpfs", required_argument, NULL, ARG_TMPFS },
+ { "overlay", required_argument, NULL, ARG_OVERLAY },
+ { "overlay-ro", required_argument, NULL, ARG_OVERLAY_RO },
{ "machine", required_argument, NULL, 'M' },
{ "slice", required_argument, NULL, 'S' },
{ "setenv", required_argument, NULL, ARG_SETENV },
@@ -331,6 +447,9 @@ static int parse_argv(int argc, char *argv[]) {
{ "image", required_argument, NULL, 'i' },
{ "volatile", optional_argument, NULL, ARG_VOLATILE },
{ "port", required_argument, NULL, 'p' },
+ { "property", required_argument, NULL, ARG_PROPERTY },
+ { "private-users", optional_argument, NULL, ARG_PRIVATE_USERS },
+ { "kill-signal", required_argument, NULL, ARG_KILL_SIGNAL },
{}
};
@@ -534,72 +653,131 @@ static int parse_argv(int argc, char *argv[]) {
case ARG_BIND:
case ARG_BIND_RO: {
- _cleanup_free_ char *a = NULL, *b = NULL;
+ _cleanup_free_ char *source = NULL, *destination = NULL;
+ CustomMount *m;
char *e;
- char ***x;
-
- x = c == ARG_BIND ? &arg_bind : &arg_bind_ro;
e = strchr(optarg, ':');
if (e) {
- a = strndup(optarg, e - optarg);
- b = strdup(e + 1);
+ source = strndup(optarg, e - optarg);
+ destination = strdup(e + 1);
} else {
- a = strdup(optarg);
- b = strdup(optarg);
+ source = strdup(optarg);
+ destination = strdup(optarg);
}
- if (!a || !b)
+ if (!source || !destination)
return log_oom();
- if (!path_is_absolute(a) || !path_is_absolute(b)) {
+ if (!path_is_absolute(source) || !path_is_absolute(destination)) {
log_error("Invalid bind mount specification: %s", optarg);
return -EINVAL;
}
- r = strv_extend(x, a);
- if (r < 0)
+ m = custom_mount_add(CUSTOM_MOUNT_BIND);
+ if (!m)
return log_oom();
- r = strv_extend(x, b);
- if (r < 0)
- return log_oom();
+ m->source = source;
+ m->destination = destination;
+ m->read_only = c == ARG_BIND_RO;
+
+ source = destination = NULL;
break;
}
case ARG_TMPFS: {
- _cleanup_free_ char *a = NULL, *b = NULL;
+ _cleanup_free_ char *path = NULL, *opts = NULL;
+ CustomMount *m;
char *e;
e = strchr(optarg, ':');
if (e) {
- a = strndup(optarg, e - optarg);
- b = strdup(e + 1);
+ path = strndup(optarg, e - optarg);
+ opts = strdup(e + 1);
} else {
- a = strdup(optarg);
- b = strdup("mode=0755");
+ path = strdup(optarg);
+ opts = strdup("mode=0755");
}
- if (!a || !b)
+ if (!path || !opts)
return log_oom();
- if (!path_is_absolute(a)) {
+ if (!path_is_absolute(path)) {
log_error("Invalid tmpfs specification: %s", optarg);
return -EINVAL;
}
- r = strv_push(&arg_tmpfs, a);
- if (r < 0)
+ m = custom_mount_add(CUSTOM_MOUNT_TMPFS);
+ if (!m)
return log_oom();
- a = NULL;
+ m->destination = path;
+ m->options = opts;
- r = strv_push(&arg_tmpfs, b);
- if (r < 0)
+ path = opts = NULL;
+
+ break;
+ }
+
+ case ARG_OVERLAY:
+ case ARG_OVERLAY_RO: {
+ _cleanup_free_ char *upper = NULL, *destination = NULL;
+ _cleanup_strv_free_ char **lower = NULL;
+ CustomMount *m;
+ unsigned n = 0;
+ char **i;
+
+ lower = strv_split(optarg, ":");
+ if (!lower)
+ return log_oom();
+
+ STRV_FOREACH(i, lower) {
+ if (!path_is_absolute(*i)) {
+ log_error("Overlay path %s is not absolute.", *i);
+ return -EINVAL;
+ }
+
+ n++;
+ }
+
+ if (n < 2) {
+ log_error("--overlay= needs at least two colon-separated directories specified.");
+ return -EINVAL;
+ }
+
+ if (n == 2) {
+ /* If two parameters are specified,
+ * the first one is the lower, the
+ * second one the upper directory. And
+ * we'll also define the the
+ * destination mount point the same as
+ * the upper. */
+ upper = lower[1];
+ lower[1] = NULL;
+
+ destination = strdup(upper);
+ if (!destination)
+ return log_oom();
+
+ } else {
+ upper = lower[n - 2];
+ destination = lower[n - 1];
+ lower[n - 2] = NULL;
+ }
+
+ m = custom_mount_add(CUSTOM_MOUNT_OVERLAY);
+ if (!m)
return log_oom();
- b = NULL;
+ m->destination = destination;
+ m->source = upper;
+ m->lower = lower;
+ m->read_only = c == ARG_OVERLAY_RO;
+
+ upper = destination = NULL;
+ lower = NULL;
break;
}
@@ -646,7 +824,7 @@ static int parse_argv(int argc, char *argv[]) {
case ARG_PERSONALITY:
arg_personality = personality_from_string(optarg);
- if (arg_personality == 0xffffffffLU) {
+ if (arg_personality == PERSONALITY_INVALID) {
log_error("Unknown or unsupported personality '%s'.", optarg);
return -EINVAL;
}
@@ -731,6 +909,50 @@ static int parse_argv(int argc, char *argv[]) {
break;
}
+ case ARG_PROPERTY:
+ if (strv_extend(&arg_property, optarg) < 0)
+ return log_oom();
+
+ break;
+
+ case ARG_PRIVATE_USERS:
+ if (optarg) {
+ _cleanup_free_ char *buffer = NULL;
+ const char *range, *shift;
+
+ range = strchr(optarg, ':');
+ if (range) {
+ buffer = strndup(optarg, range - optarg);
+ if (!buffer)
+ return log_oom();
+ shift = buffer;
+
+ range++;
+ if (safe_atou32(range, &arg_uid_range) < 0 || arg_uid_range <= 0) {
+ log_error("Failed to parse UID range: %s", range);
+ return -EINVAL;
+ }
+ } else
+ shift = optarg;
+
+ if (parse_uid(shift, &arg_uid_shift) < 0) {
+ log_error("Failed to parse UID: %s", optarg);
+ return -EINVAL;
+ }
+ }
+
+ arg_userns = true;
+ break;
+
+ case ARG_KILL_SIGNAL:
+ arg_kill_signal = signal_from_string_try_harder(optarg);
+ if (arg_kill_signal < 0) {
+ log_error("Cannot parse signal: %s", optarg);
+ return -EINVAL;
+ }
+
+ break;
+
case '?':
return -EINVAL;
@@ -793,10 +1015,50 @@ static int parse_argv(int argc, char *argv[]) {
arg_retain = (arg_retain | plus | (arg_private_network ? 1ULL << CAP_NET_ADMIN : 0)) & ~minus;
+ if (arg_boot && arg_kill_signal <= 0)
+ arg_kill_signal = SIGRTMIN+3;
+
return 1;
}
-static int mount_all(const char *dest) {
+static int tmpfs_patch_options(const char *options, char **ret) {
+ char *buf = NULL;
+
+ if (arg_userns && arg_uid_shift != 0) {
+
+ if (options)
+ (void) asprintf(&buf, "%s,uid=" UID_FMT ",gid=" UID_FMT, options, arg_uid_shift, arg_uid_shift);
+ else
+ (void) asprintf(&buf, "uid=" UID_FMT ",gid=" UID_FMT, arg_uid_shift, arg_uid_shift);
+ if (!buf)
+ return -ENOMEM;
+
+ options = buf;
+ }
+
+#ifdef HAVE_SELINUX
+ if (arg_selinux_apifs_context) {
+ char *t;
+
+ if (options)
+ t = strjoin(options, ",context=\"", arg_selinux_apifs_context, "\"", NULL);
+ else
+ t = strjoin("context=\"", arg_selinux_apifs_context, "\"", NULL);
+ if (!t) {
+ free(buf);
+ return -ENOMEM;
+ }
+
+ free(buf);
+ buf = t;
+ }
+#endif
+
+ *ret = buf;
+ return !!buf;
+}
+
+static int mount_all(const char *dest, bool userns) {
typedef struct MountPoint {
const char *what;
@@ -805,78 +1067,64 @@ static int mount_all(const char *dest) {
const char *options;
unsigned long flags;
bool fatal;
+ bool userns;
} MountPoint;
static const MountPoint mount_table[] = {
- { "proc", "/proc", "proc", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, true },
- { "/proc/sys", "/proc/sys", NULL, NULL, MS_BIND, true }, /* Bind mount first */
- { 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 },
- { "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 },
- { "tmpfs", "/tmp", "tmpfs", "mode=1777", MS_STRICTATIME, true },
+ { "proc", "/proc", "proc", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, true, true },
+ { "/proc/sys", "/proc/sys", NULL, NULL, MS_BIND, true, true }, /* Bind mount first */
+ { NULL, "/proc/sys", NULL, NULL, MS_BIND|MS_RDONLY|MS_REMOUNT, true, true }, /* Then, make it r/o */
+ { "sysfs", "/sys", "sysfs", NULL, MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV, true, false },
+ { "tmpfs", "/sys/fs/cgroup", "tmpfs", "mode=755", MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME, true, false },
+ { "tmpfs", "/dev", "tmpfs", "mode=755", MS_NOSUID|MS_STRICTATIME, true, false },
+ { "tmpfs", "/dev/shm", "tmpfs", "mode=1777", MS_NOSUID|MS_NODEV|MS_STRICTATIME, true, false },
+ { "tmpfs", "/run", "tmpfs", "mode=755", MS_NOSUID|MS_NODEV|MS_STRICTATIME, true, false },
+ { "tmpfs", "/tmp", "tmpfs", "mode=1777", MS_STRICTATIME, true, false },
#ifdef HAVE_SELINUX
- { "/sys/fs/selinux", "/sys/fs/selinux", NULL, NULL, MS_BIND, false }, /* Bind mount first */
- { NULL, "/sys/fs/selinux", NULL, NULL, MS_BIND|MS_RDONLY|MS_REMOUNT, false }, /* Then, make it r/o */
+ { "/sys/fs/selinux", "/sys/fs/selinux", NULL, NULL, MS_BIND, false, false }, /* Bind mount first */
+ { NULL, "/sys/fs/selinux", NULL, NULL, MS_BIND|MS_RDONLY|MS_REMOUNT, false, false }, /* Then, make it r/o */
#endif
};
unsigned k;
- int r = 0;
+ int r;
for (k = 0; k < ELEMENTSOF(mount_table); k++) {
- _cleanup_free_ char *where = NULL;
-#ifdef HAVE_SELINUX
- _cleanup_free_ char *options = NULL;
-#endif
+ _cleanup_free_ char *where = NULL, *options = NULL;
const char *o;
- int t;
- where = strjoin(dest, "/", mount_table[k].where, NULL);
+ if (userns != mount_table[k].userns)
+ continue;
+
+ where = prefix_root(dest, mount_table[k].where);
if (!where)
return log_oom();
- t = path_is_mount_point(where, true);
- if (t < 0) {
- log_error_errno(t, "Failed to detect whether %s is a mount point: %m", where);
-
- if (r == 0)
- r = t;
-
- continue;
- }
+ r = path_is_mount_point(where, AT_SYMLINK_FOLLOW);
+ if (r < 0 && r != -ENOENT)
+ return log_error_errno(r, "Failed to detect whether %s is a mount point: %m", where);
/* Skip this entry if it is not a remount. */
- if (mount_table[k].what && t > 0)
+ if (mount_table[k].what && r > 0)
continue;
- t = mkdir_p(where, 0755);
- if (t < 0) {
- if (mount_table[k].fatal) {
- log_error_errno(t, "Failed to create directory %s: %m", where);
-
- if (r == 0)
- r = t;
- } else
- log_warning_errno(t, "Failed to create directory %s: %m", where);
+ r = mkdir_p(where, 0755);
+ if (r < 0) {
+ if (mount_table[k].fatal)
+ return log_error_errno(r, "Failed to create directory %s: %m", where);
+ log_warning_errno(r, "Failed to create directory %s: %m", where);
continue;
}
-#ifdef HAVE_SELINUX
- if (arg_selinux_apifs_context &&
- (streq_ptr(mount_table[k].what, "tmpfs") || streq_ptr(mount_table[k].what, "devpts"))) {
- options = strjoin(mount_table[k].options, ",context=\"", arg_selinux_apifs_context, "\"", NULL);
- if (!options)
+ o = mount_table[k].options;
+ if (streq_ptr(mount_table[k].type, "tmpfs")) {
+ r = tmpfs_patch_options(o, &options);
+ if (r < 0)
return log_oom();
-
- o = options;
- } else
-#endif
- o = mount_table[k].options;
-
+ if (r > 0)
+ o = options;
+ }
if (mount(mount_table[k].what,
where,
@@ -884,75 +1132,162 @@ static int mount_all(const char *dest) {
mount_table[k].flags,
o) < 0) {
- if (mount_table[k].fatal) {
- log_error_errno(errno, "mount(%s) failed: %m", where);
+ if (mount_table[k].fatal)
+ return log_error_errno(errno, "mount(%s) failed: %m", where);
- if (r == 0)
- r = -errno;
- } else
- log_warning_errno(errno, "mount(%s) failed: %m", where);
+ log_warning_errno(errno, "mount(%s) failed, ignoring: %m", where);
}
}
- return r;
+ return 0;
}
-static int mount_binds(const char *dest, char **l, bool ro) {
- char **x, **y;
+static int mount_bind(const char *dest, CustomMount *m) {
+ struct stat source_st, dest_st;
+ const char *where;
+ int r;
- STRV_FOREACH_PAIR(x, y, l) {
- _cleanup_free_ char *where = NULL;
- struct stat source_st, dest_st;
- int r;
+ assert(m);
- if (stat(*x, &source_st) < 0)
- return log_error_errno(errno, "Failed to stat %s: %m", *x);
+ if (stat(m->source, &source_st) < 0)
+ return log_error_errno(errno, "Failed to stat %s: %m", m->source);
- where = strappend(dest, *y);
- if (!where)
- return log_oom();
+ where = prefix_roota(dest, m->destination);
- r = stat(where, &dest_st);
- if (r == 0) {
- if (S_ISDIR(source_st.st_mode) && !S_ISDIR(dest_st.st_mode)) {
- log_error("Cannot bind mount directory %s on file %s.", *x, where);
- return -EINVAL;
- }
- if (!S_ISDIR(source_st.st_mode) && S_ISDIR(dest_st.st_mode)) {
- log_error("Cannot bind mount file %s on directory %s.", *x, where);
- return -EINVAL;
- }
- } else if (errno == ENOENT) {
- r = mkdir_parents_label(where, 0755);
- if (r < 0)
- return log_error_errno(r, "Failed to bind mount %s: %m", *x);
- } else {
- log_error_errno(errno, "Failed to bind mount %s: %m", *x);
- return -errno;
+ if (stat(where, &dest_st) >= 0) {
+ if (S_ISDIR(source_st.st_mode) && !S_ISDIR(dest_st.st_mode)) {
+ log_error("Cannot bind mount directory %s on file %s.", m->source, where);
+ return -EINVAL;
}
- /* Create the mount point. Any non-directory file can be
- * mounted on any non-directory file (regular, fifo, socket,
- * char, block).
- */
- if (S_ISDIR(source_st.st_mode)) {
- r = mkdir_label(where, 0755);
- if (r < 0 && errno != EEXIST)
- return log_error_errno(r, "Failed to create mount point %s: %m", where);
- } else {
- r = touch(where);
- if (r < 0)
- return log_error_errno(r, "Failed to create mount point %s: %m", where);
+ if (!S_ISDIR(source_st.st_mode) && S_ISDIR(dest_st.st_mode)) {
+ log_error("Cannot bind mount file %s on directory %s.", m->source, where);
+ return -EINVAL;
}
- if (mount(*x, where, "bind", MS_BIND, NULL) < 0)
- return log_error_errno(errno, "mount(%s) failed: %m", where);
+ } else if (errno == ENOENT) {
+ r = mkdir_parents_label(where, 0755);
+ if (r < 0)
+ return log_error_errno(r, "Failed to make parents of %s: %m", where);
+ } else {
+ log_error_errno(errno, "Failed to stat %s: %m", where);
+ return -errno;
+ }
+
+ /* Create the mount point. Any non-directory file can be
+ * mounted on any non-directory file (regular, fifo, socket,
+ * char, block).
+ */
+ if (S_ISDIR(source_st.st_mode))
+ r = mkdir_label(where, 0755);
+ else
+ r = touch(where);
+ if (r < 0 && r != -EEXIST)
+ return log_error_errno(r, "Failed to create mount point %s: %m", where);
- if (ro) {
- r = bind_remount_recursive(where, true);
- if (r < 0)
- return log_error_errno(r, "Read-Only bind mount failed: %m");
+ if (mount(m->source, where, NULL, MS_BIND, NULL) < 0)
+ return log_error_errno(errno, "mount(%s) failed: %m", where);
+
+ if (m->read_only) {
+ r = bind_remount_recursive(where, true);
+ if (r < 0)
+ return log_error_errno(r, "Read-only bind mount failed: %m");
+ }
+
+ return 0;
+}
+
+static int mount_tmpfs(const char *dest, CustomMount *m) {
+ const char *where, *options;
+ _cleanup_free_ char *buf = NULL;
+ int r;
+
+ assert(dest);
+ assert(m);
+
+ where = prefix_roota(dest, m->destination);
+
+ r = mkdir_p_label(where, 0755);
+ if (r < 0 && r != -EEXIST)
+ return log_error_errno(r, "Creating mount point for tmpfs %s failed: %m", where);
+
+ r = tmpfs_patch_options(m->options, &buf);
+ if (r < 0)
+ return log_oom();
+ options = r > 0 ? buf : m->options;
+
+ if (mount("tmpfs", where, "tmpfs", MS_NODEV|MS_STRICTATIME, options) < 0)
+ return log_error_errno(errno, "tmpfs mount to %s failed: %m", where);
+
+ return 0;
+}
+
+static int mount_overlay(const char *dest, CustomMount *m) {
+ _cleanup_free_ char *lower = NULL;
+ const char *where, *options;
+ int r;
+
+ assert(dest);
+ assert(m);
+
+ where = prefix_roota(dest, m->destination);
+
+ r = mkdir_label(where, 0755);
+ if (r < 0 && r != -EEXIST)
+ return log_error_errno(r, "Creating mount point for overlay %s failed: %m", where);
+
+ (void) mkdir_p_label(m->source, 0755);
+
+ strv_reverse(m->lower);
+ lower = strv_join(m->lower, ":");
+ strv_reverse(m->lower);
+ if (!lower)
+ return log_oom();
+
+ if (m->read_only)
+ options = strjoina("lowerdir=", m->source, ":", lower);
+ else {
+ assert(m->work_dir);
+ (void) mkdir_label(m->work_dir, 0700);
+
+ options = strjoina("lowerdir=", lower, ",upperdir=", m->source, ",workdir=", m->work_dir);
+ }
+
+ if (mount("overlay", where, "overlay", m->read_only ? MS_RDONLY : 0, options) < 0)
+ return log_error_errno(errno, "overlay mount to %s failed: %m", where);
+
+ return 0;
+}
+
+static int mount_custom(const char *dest) {
+ unsigned i;
+ int r;
+
+ assert(dest);
+
+ for (i = 0; i < arg_n_custom_mounts; i++) {
+ CustomMount *m = &arg_custom_mounts[i];
+
+ switch (m->type) {
+
+ case CUSTOM_MOUNT_BIND:
+ r = mount_bind(dest, m);
+ break;
+
+ case CUSTOM_MOUNT_TMPFS:
+ r = mount_tmpfs(dest, m);
+ break;
+
+ case CUSTOM_MOUNT_OVERLAY:
+ r = mount_overlay(dest, m);
+ break;
+
+ default:
+ assert_not_reached("Unknown custom mount type");
}
+
+ if (r < 0)
+ return r;
}
return 0;
@@ -964,8 +1299,8 @@ static int mount_cgroup_hierarchy(const char *dest, const char *controller, cons
to = strjoina(dest, "/sys/fs/cgroup/", hierarchy);
- r = path_is_mount_point(to, false);
- if (r < 0)
+ r = path_is_mount_point(to, 0);
+ if (r < 0 && r != -ENOENT)
return log_error_errno(r, "Failed to determine if %s is mounted already: %m", to);
if (r > 0)
return 0;
@@ -988,8 +1323,7 @@ static int mount_cgroup_hierarchy(const char *dest, const char *controller, cons
static int mount_cgroup(const char *dest) {
_cleanup_set_free_free_ Set *controllers = NULL;
- _cleanup_free_ char *own_cgroup_path = NULL;
- const char *cgroup_root, *systemd_root, *systemd_own;
+ const char *cgroup_root;
int r;
controllers = set_new(&string_hash_ops);
@@ -1000,14 +1334,6 @@ static int mount_cgroup(const char *dest) {
if (r < 0)
return log_error_errno(r, "Failed to determine cgroup controllers: %m");
- r = cg_pid_get_path(NULL, 0, &own_cgroup_path);
- if (r < 0)
- return log_error_errno(r, "Failed to determine our own cgroup path: %m");
-
- cgroup_root = strjoina(dest, "/sys/fs/cgroup");
- if (mount("tmpfs", cgroup_root, "tmpfs", MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME, "mode=755") < 0)
- return log_error_errno(errno, "Failed to mount tmpfs to /sys/fs/cgroup: %m");
-
for (;;) {
_cleanup_free_ char *controller = NULL, *origin = NULL, *combined = NULL;
@@ -1015,7 +1341,7 @@ static int mount_cgroup(const char *dest) {
if (!controller)
break;
- origin = strappend("/sys/fs/cgroup/", controller);
+ origin = prefix_root("/sys/fs/cgroup/", controller);
if (!origin)
return log_oom();
@@ -1032,7 +1358,7 @@ static int mount_cgroup(const char *dest) {
else {
_cleanup_free_ char *target = NULL;
- target = strjoin(dest, "/sys/fs/cgroup/", controller, NULL);
+ target = prefix_root(dest, origin);
if (!target)
return log_oom();
@@ -1047,8 +1373,13 @@ static int mount_cgroup(const char *dest) {
if (r < 0)
return r;
- if (symlink(combined, target) < 0)
- return log_error_errno(errno, "Failed to create symlink for combined hierarchy: %m");
+ r = symlink_idempotent(combined, target);
+ if (r == -EINVAL) {
+ log_error("Invalid existing symlink for combined hierarchy");
+ return r;
+ }
+ if (r < 0)
+ return log_error_errno(r, "Failed to create symlink for combined hierarchy: %m");
}
}
@@ -1056,46 +1387,82 @@ static int mount_cgroup(const char *dest) {
if (r < 0)
return r;
+ cgroup_root = prefix_roota(dest, "/sys/fs/cgroup");
+ if (mount(NULL, cgroup_root, NULL, MS_REMOUNT|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME|MS_RDONLY, "mode=755") < 0)
+ return log_error_errno(errno, "Failed to remount %s read-only: %m", cgroup_root);
+
+ return 0;
+}
+
+static int mount_systemd_cgroup_writable(const char *dest) {
+ _cleanup_free_ char *own_cgroup_path = NULL;
+ const char *systemd_root, *systemd_own;
+ int r;
+
+ assert(dest);
+
+ r = cg_pid_get_path(NULL, 0, &own_cgroup_path);
+ if (r < 0)
+ return log_error_errno(r, "Failed to determine our own cgroup path: %m");
+
/* Make our own cgroup a (writable) bind mount */
systemd_own = strjoina(dest, "/sys/fs/cgroup/systemd", own_cgroup_path);
if (mount(systemd_own, systemd_own, NULL, MS_BIND, NULL) < 0)
return log_error_errno(errno, "Failed to turn %s into a bind mount: %m", own_cgroup_path);
/* And then remount the systemd cgroup root read-only */
- systemd_root = strjoina(dest, "/sys/fs/cgroup/systemd");
+ systemd_root = prefix_roota(dest, "/sys/fs/cgroup/systemd");
if (mount(NULL, systemd_root, NULL, MS_BIND|MS_REMOUNT|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_RDONLY, NULL) < 0)
return log_error_errno(errno, "Failed to mount cgroup root read-only: %m");
- if (mount(NULL, cgroup_root, NULL, MS_REMOUNT|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME|MS_RDONLY, "mode=755") < 0)
- return log_error_errno(errno, "Failed to remount %s read-only: %m", cgroup_root);
-
return 0;
}
-static int mount_tmpfs(const char *dest) {
- char **i, **o;
+static int userns_lchown(const char *p, uid_t uid, gid_t gid) {
+ assert(p);
- STRV_FOREACH_PAIR(i, o, arg_tmpfs) {
- _cleanup_free_ char *where = NULL;
- int r;
+ if (!arg_userns)
+ return 0;
- where = strappend(dest, *i);
- if (!where)
- return log_oom();
+ if (uid == UID_INVALID && gid == GID_INVALID)
+ return 0;
- r = mkdir_label(where, 0755);
- if (r < 0 && r != -EEXIST)
- return log_error_errno(r, "Creating mount point for tmpfs %s failed: %m", where);
+ if (uid != UID_INVALID) {
+ uid += arg_uid_shift;
- if (mount("tmpfs", where, "tmpfs", MS_NODEV|MS_STRICTATIME, *o) < 0)
- return log_error_errno(errno, "tmpfs mount to %s failed: %m", where);
+ if (uid < arg_uid_shift || uid >= arg_uid_shift + arg_uid_range)
+ return -EOVERFLOW;
}
+ if (gid != GID_INVALID) {
+ gid += (gid_t) arg_uid_shift;
+
+ if (gid < (gid_t) arg_uid_shift || gid >= (gid_t) (arg_uid_shift + arg_uid_range))
+ return -EOVERFLOW;
+ }
+
+ if (lchown(p, uid, gid) < 0)
+ return -errno;
+
return 0;
}
+static int userns_mkdir(const char *root, const char *path, mode_t mode, uid_t uid, gid_t gid) {
+ const char *q;
+
+ q = prefix_roota(root, path);
+ if (mkdir(q, mode) < 0) {
+ if (errno == EEXIST)
+ return 0;
+ return -errno;
+ }
+
+ return userns_lchown(q, uid, gid);
+}
+
static int setup_timezone(const char *dest) {
- _cleanup_free_ char *where = NULL, *p = NULL, *q = NULL, *check = NULL, *what = NULL;
+ _cleanup_free_ char *p = NULL, *q = NULL;
+ const char *where, *check, *what;
char *z, *y;
int r;
@@ -1116,10 +1483,7 @@ static int setup_timezone(const char *dest) {
return 0;
}
- where = strappend(dest, "/etc/localtime");
- if (!where)
- return log_oom();
-
+ where = prefix_roota(dest, "/etc/localtime");
r = readlink_malloc(where, &q);
if (r >= 0) {
y = path_startswith(q, "../usr/share/zoneinfo/");
@@ -1131,43 +1495,34 @@ static int setup_timezone(const char *dest) {
return 0;
}
- check = strjoin(dest, "/usr/share/zoneinfo/", z, NULL);
- if (!check)
- return log_oom();
-
- if (access(check, F_OK) < 0) {
+ check = strjoina("/usr/share/zoneinfo/", z);
+ check = prefix_root(dest, check);
+ if (laccess(check, F_OK) < 0) {
log_warning("Timezone %s does not exist in container, not updating container timezone.", z);
return 0;
}
- what = strappend("../usr/share/zoneinfo/", z);
- if (!what)
- return log_oom();
-
- r = mkdir_parents(where, 0755);
- if (r < 0) {
- log_error_errno(r, "Failed to create directory for timezone info %s in container: %m", where);
-
- return 0;
- }
-
r = unlink(where);
if (r < 0 && errno != ENOENT) {
log_error_errno(errno, "Failed to remove existing timezone info %s in container: %m", where);
-
return 0;
}
+ what = strjoina("../usr/share/zoneinfo/", z);
if (symlink(what, where) < 0) {
log_error_errno(errno, "Failed to correct timezone of container: %m");
return 0;
}
+ r = userns_lchown(where, 0, 0);
+ if (r < 0)
+ return log_warning_errno(r, "Failed to chown /etc/localtime: %m");
+
return 0;
}
static int setup_resolv_conf(const char *dest) {
- _cleanup_free_ char *where = NULL;
+ const char *where = NULL;
int r;
assert(dest);
@@ -1176,31 +1531,24 @@ static int setup_resolv_conf(const char *dest) {
return 0;
/* Fix resolv.conf, if possible */
- where = strappend(dest, "/etc/resolv.conf");
- if (!where)
- return log_oom();
-
- /* We don't really care for the results of this really. If it
- * fails, it fails, but meh... */
- r = mkdir_parents(where, 0755);
- if (r < 0) {
- log_warning_errno(r, "Failed to create parent directory for resolv.conf %s: %m", where);
-
- return 0;
- }
+ where = prefix_roota(dest, "/etc/resolv.conf");
r = copy_file("/etc/resolv.conf", where, O_TRUNC|O_NOFOLLOW, 0644, 0);
if (r < 0) {
log_warning_errno(r, "Failed to copy /etc/resolv.conf to %s: %m", where);
-
return 0;
}
+ r = userns_lchown(where, 0, 0);
+ if (r < 0)
+ log_warning_errno(r, "Failed to chown /etc/resolv.conf: %m");
+
return 0;
}
static int setup_volatile_state(const char *directory) {
- const char *p;
+ _cleanup_free_ char *buf = NULL;
+ const char *p, *options;
int r;
assert(directory);
@@ -1215,12 +1563,19 @@ static int setup_volatile_state(const char *directory) {
if (r < 0)
return log_error_errno(r, "Failed to remount %s read-only: %m", directory);
- p = strjoina(directory, "/var");
+ p = prefix_roota(directory, "/var");
r = mkdir(p, 0755);
if (r < 0 && errno != EEXIST)
return log_error_errno(errno, "Failed to create %s: %m", directory);
- if (mount("tmpfs", p, "tmpfs", MS_STRICTATIME, "mode=755") < 0)
+ options = "mode=755";
+ r = tmpfs_patch_options(options, &buf);
+ if (r < 0)
+ return log_oom();
+ if (r > 0)
+ options = buf;
+
+ if (mount("tmpfs", p, "tmpfs", MS_STRICTATIME, options) < 0)
return log_error_errno(errno, "Failed to mount tmpfs to /var: %m");
return 0;
@@ -1229,7 +1584,8 @@ static int setup_volatile_state(const char *directory) {
static int setup_volatile(const char *directory) {
bool tmpfs_mounted = false, bind_mounted = false;
char template[] = "/tmp/nspawn-volatile-XXXXXX";
- const char *f, *t;
+ _cleanup_free_ char *buf = NULL;
+ const char *f, *t, *options;
int r;
assert(directory);
@@ -1243,27 +1599,31 @@ static int setup_volatile(const char *directory) {
if (!mkdtemp(template))
return log_error_errno(errno, "Failed to create temporary directory: %m");
- if (mount("tmpfs", template, "tmpfs", MS_STRICTATIME, "mode=755") < 0) {
- log_error_errno(errno, "Failed to mount tmpfs for root directory: %m");
- r = -errno;
+ options = "mode=755";
+ r = tmpfs_patch_options(options, &buf);
+ if (r < 0)
+ return log_oom();
+ if (r > 0)
+ options = buf;
+
+ if (mount("tmpfs", template, "tmpfs", MS_STRICTATIME, options) < 0) {
+ r = log_error_errno(errno, "Failed to mount tmpfs for root directory: %m");
goto fail;
}
tmpfs_mounted = true;
- f = strjoina(directory, "/usr");
- t = strjoina(template, "/usr");
+ f = prefix_roota(directory, "/usr");
+ t = prefix_roota(template, "/usr");
r = mkdir(t, 0755);
if (r < 0 && errno != EEXIST) {
- log_error_errno(errno, "Failed to create %s: %m", t);
- r = -errno;
+ r = log_error_errno(errno, "Failed to create %s: %m", t);
goto fail;
}
- if (mount(f, t, "bind", MS_BIND|MS_REC, NULL) < 0) {
- log_error_errno(errno, "Failed to create /usr bind mount: %m");
- r = -errno;
+ if (mount(f, t, NULL, MS_BIND|MS_REC, NULL) < 0) {
+ r = log_error_errno(errno, "Failed to create /usr bind mount: %m");
goto fail;
}
@@ -1276,25 +1636,26 @@ static int setup_volatile(const char *directory) {
}
if (mount(template, directory, NULL, MS_MOVE, NULL) < 0) {
- log_error_errno(errno, "Failed to move root mount: %m");
- r = -errno;
+ r = log_error_errno(errno, "Failed to move root mount: %m");
goto fail;
}
- rmdir(template);
+ (void) rmdir(template);
return 0;
fail:
if (bind_mounted)
- umount(t);
+ (void) umount(t);
+
if (tmpfs_mounted)
- umount(template);
- rmdir(template);
+ (void) umount(template);
+ (void) rmdir(template);
return r;
}
static char* id128_format_as_uuid(sd_id128_t id, char s[37]) {
+ assert(s);
snprintf(s, 37,
"%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
@@ -1304,23 +1665,19 @@ static char* id128_format_as_uuid(sd_id128_t id, char s[37]) {
}
static int setup_boot_id(const char *dest) {
- _cleanup_free_ char *from = NULL, *to = NULL;
+ const char *from, *to;
sd_id128_t rnd = {};
char as_uuid[37];
int r;
- assert(dest);
-
if (arg_share_system)
return 0;
/* Generate a new randomized boot ID, so that each boot-up of
* the container gets a new one */
- from = strappend(dest, "/dev/proc-sys-kernel-random-boot-id");
- to = strappend(dest, "/proc/sys/kernel/random/boot_id");
- if (!from || !to)
- return log_oom();
+ from = prefix_roota(dest, "/run/proc-sys-kernel-random-boot-id");
+ to = prefix_roota(dest, "/proc/sys/kernel/random/boot_id");
r = sd_id128_randomize(&rnd);
if (r < 0)
@@ -1332,10 +1689,9 @@ static int setup_boot_id(const char *dest) {
if (r < 0)
return log_error_errno(r, "Failed to write boot id: %m");
- if (mount(from, to, "bind", MS_BIND, NULL) < 0) {
- log_error_errno(errno, "Failed to bind mount boot id: %m");
- r = -errno;
- } else if (mount(from, to, "bind", MS_BIND|MS_REMOUNT|MS_RDONLY, NULL))
+ if (mount(from, to, NULL, MS_BIND, NULL) < 0)
+ r = log_error_errno(errno, "Failed to bind mount boot id: %m");
+ else if (mount(NULL, to, NULL, MS_BIND|MS_REMOUNT|MS_RDONLY|MS_NOSUID|MS_NODEV, NULL) < 0)
log_warning_errno(errno, "Failed to make boot id read-only: %m");
unlink(from);
@@ -1361,14 +1717,16 @@ static int copy_devnodes(const char *dest) {
u = umask(0000);
+ /* Create /dev/net, so that we can create /dev/net/tun in it */
+ if (userns_mkdir(dest, "/dev/net", 0755, 0, 0) < 0)
+ return log_error_errno(r, "Failed to create /dev/net directory: %m");
+
NULSTR_FOREACH(d, devnodes) {
_cleanup_free_ char *from = NULL, *to = NULL;
struct stat st;
from = strappend("/dev/", d);
- to = strjoin(dest, "/dev/", d, NULL);
- if (!from || !to)
- return log_oom();
+ to = prefix_root(dest, from);
if (stat(from, &st) < 0) {
@@ -1377,33 +1735,73 @@ static int copy_devnodes(const char *dest) {
} else if (!S_ISCHR(st.st_mode) && !S_ISBLK(st.st_mode)) {
- log_error("%s is not a char or block device, cannot copy", from);
+ log_error("%s is not a char or block device, cannot copy.", from);
return -EIO;
} else {
- r = mkdir_parents(to, 0775);
- if (r < 0) {
- log_error_errno(r, "Failed to create parent directory of %s: %m", to);
- return -r;
+ if (mknod(to, st.st_mode, st.st_rdev) < 0) {
+ if (errno != EPERM)
+ return log_error_errno(errno, "mknod(%s) failed: %m", to);
+
+ /* Some systems abusively restrict mknod but
+ * allow bind mounts. */
+ r = touch(to);
+ if (r < 0)
+ return log_error_errno(r, "touch (%s) failed: %m", to);
+ if (mount(from, to, NULL, MS_BIND, NULL) < 0)
+ return log_error_errno(errno, "Both mknod and bind mount (%s) failed: %m", to);
}
- if (mknod(to, st.st_mode, st.st_rdev) < 0)
- return log_error_errno(errno, "mknod(%s) failed: %m", to);
+ r = userns_lchown(to, 0, 0);
+ if (r < 0)
+ return log_error_errno(r, "chown() of device node %s failed: %m", to);
}
}
return r;
}
-static int setup_ptmx(const char *dest) {
- _cleanup_free_ char *p = NULL;
+static int setup_pts(const char *dest) {
+ _cleanup_free_ char *options = NULL;
+ const char *p;
- p = strappend(dest, "/dev/ptmx");
- if (!p)
+#ifdef HAVE_SELINUX
+ if (arg_selinux_apifs_context)
+ (void) asprintf(&options,
+ "newinstance,ptmxmode=0666,mode=620,uid=" UID_FMT ",gid=" GID_FMT ",context=\"%s\"",
+ arg_uid_shift,
+ arg_uid_shift + TTY_GID,
+ arg_selinux_apifs_context);
+ else
+#endif
+ (void) asprintf(&options,
+ "newinstance,ptmxmode=0666,mode=620,uid=" UID_FMT ",gid=" GID_FMT,
+ arg_uid_shift,
+ arg_uid_shift + TTY_GID);
+
+ if (!options)
return log_oom();
+ /* Mount /dev/pts itself */
+ p = prefix_roota(dest, "/dev/pts");
+ if (mkdir(p, 0755) < 0)
+ return log_error_errno(errno, "Failed to create /dev/pts: %m");
+ if (mount("devpts", p, "devpts", MS_NOSUID|MS_NOEXEC, options) < 0)
+ return log_error_errno(errno, "Failed to mount /dev/pts: %m");
+ if (userns_lchown(p, 0, 0) < 0)
+ return log_error_errno(errno, "Failed to chown /dev/pts: %m");
+
+ /* Create /dev/ptmx symlink */
+ p = prefix_roota(dest, "/dev/ptmx");
if (symlink("pts/ptmx", p) < 0)
return log_error_errno(errno, "Failed to create /dev/ptmx symlink: %m");
+ if (userns_lchown(p, 0, 0) < 0)
+ return log_error_errno(errno, "Failed to chown /dev/ptmx: %m");
+
+ /* And fix /dev/pts/ptmx ownership */
+ p = prefix_roota(dest, "/dev/pts/ptmx");
+ if (userns_lchown(p, 0, 0) < 0)
+ return log_error_errno(errno, "Failed to chown /dev/pts/ptmx: %m");
return 0;
}
@@ -1411,7 +1809,6 @@ static int setup_ptmx(const char *dest) {
static int setup_dev_console(const char *dest, const char *console) {
_cleanup_umask_ mode_t u;
const char *to;
- struct stat st;
int r;
assert(dest);
@@ -1419,35 +1816,29 @@ static int setup_dev_console(const char *dest, const char *console) {
u = umask(0000);
- if (stat("/dev/null", &st) < 0)
- return log_error_errno(errno, "Failed to stat /dev/null: %m");
-
- r = chmod_and_chown(console, 0600, 0, 0);
+ r = chmod_and_chown(console, 0600, arg_uid_shift, arg_uid_shift);
if (r < 0)
return log_error_errno(r, "Failed to correct access mode for TTY: %m");
/* We need to bind mount the right tty to /dev/console since
* ptys can only exist on pts file systems. To have something
- * to bind mount things on we create a device node first, and
- * use /dev/null for that since we the cgroups device policy
- * allows us to create that freely, while we cannot create
- * /dev/console. (Note that the major minor doesn't actually
- * matter here, since we mount it over anyway). */
+ * to bind mount things on we create a empty regular file. */
- to = strjoina(dest, "/dev/console");
- if (mknod(to, (st.st_mode & ~07777) | 0600, st.st_rdev) < 0)
- return log_error_errno(errno, "mknod() for /dev/console failed: %m");
+ to = prefix_roota(dest, "/dev/console");
+ r = touch(to);
+ if (r < 0)
+ return log_error_errno(r, "touch() for /dev/console failed: %m");
- if (mount(console, to, "bind", MS_BIND, NULL) < 0)
+ if (mount(console, to, NULL, MS_BIND, NULL) < 0)
return log_error_errno(errno, "Bind mount for /dev/console failed: %m");
return 0;
}
static int setup_kmsg(const char *dest, int kmsg_socket) {
- _cleanup_free_ char *from = NULL, *to = NULL;
+ const char *from, *to;
_cleanup_umask_ mode_t u;
- int r, fd, k;
+ int fd, k;
union {
struct cmsghdr cmsghdr;
uint8_t buf[CMSG_SPACE(sizeof(int))];
@@ -1458,30 +1849,23 @@ static int setup_kmsg(const char *dest, int kmsg_socket) {
};
struct cmsghdr *cmsg;
- assert(dest);
assert(kmsg_socket >= 0);
u = umask(0000);
- /* We create the kmsg FIFO as /dev/kmsg, but immediately
+ /* We create the kmsg FIFO as /run/kmsg, but immediately
* delete it after bind mounting it to /proc/kmsg. While FIFOs
* on the reading side behave very similar to /proc/kmsg,
* their writing side behaves differently from /dev/kmsg in
* that writing blocks when nothing is reading. In order to
* avoid any problems with containers deadlocking due to this
* we simply make /dev/kmsg unavailable to the container. */
- if (asprintf(&from, "%s/dev/kmsg", dest) < 0 ||
- asprintf(&to, "%s/proc/kmsg", dest) < 0)
- return log_oom();
+ from = prefix_roota(dest, "/run/kmsg");
+ to = prefix_roota(dest, "/proc/kmsg");
if (mkfifo(from, 0600) < 0)
- return log_error_errno(errno, "mkfifo() for /dev/kmsg failed: %m");
-
- r = chmod_and_chown(from, 0600, 0, 0);
- if (r < 0)
- return log_error_errno(r, "Failed to correct access mode for /dev/kmsg: %m");
-
- if (mount(from, to, "bind", MS_BIND, NULL) < 0)
+ return log_error_errno(errno, "mkfifo() for /run/kmsg failed: %m");
+ if (mount(from, to, NULL, MS_BIND, NULL) < 0)
return log_error_errno(errno, "Bind mount for /proc/kmsg failed: %m");
fd = open(from, O_RDWR|O_NDELAY|O_CLOEXEC);
@@ -1504,8 +1888,9 @@ static int setup_kmsg(const char *dest, int kmsg_socket) {
if (k < 0)
return log_error_errno(errno, "Failed to send FIFO fd: %m");
- /* And now make the FIFO unavailable as /dev/kmsg... */
- unlink(from);
+ /* And now make the FIFO unavailable as /run/kmsg... */
+ (void) unlink(from);
+
return 0;
}
@@ -1529,7 +1914,7 @@ static int send_rtnl(int send_fd) {
fd = socket(PF_NETLINK, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, NETLINK_ROUTE);
if (fd < 0)
- return log_error_errno(errno, "failed to allocate container netlink: %m");
+ return log_error_errno(errno, "Failed to allocate container netlink: %m");
cmsg = CMSG_FIRSTHDR(&mh);
cmsg->cmsg_level = SOL_SOCKET;
@@ -1714,7 +2099,8 @@ static int setup_hostname(void) {
static int setup_journal(const char *directory) {
sd_id128_t machine_id, this_id;
- _cleanup_free_ char *p = NULL, *b = NULL, *q = NULL, *d = NULL;
+ _cleanup_free_ char *b = NULL, *d = NULL;
+ const char *etc_machine_id, *p, *q;
char *id;
int r;
@@ -1722,15 +2108,13 @@ static int setup_journal(const char *directory) {
if (arg_ephemeral)
return 0;
- p = strappend(directory, "/etc/machine-id");
- if (!p)
- return log_oom();
+ etc_machine_id = prefix_roota(directory, "/etc/machine-id");
- r = read_one_line_file(p, &b);
+ r = read_one_line_file(etc_machine_id, &b);
if (r == -ENOENT && arg_link_journal == LINK_AUTO)
return 0;
else if (r < 0)
- return log_error_errno(r, "Failed to read machine ID from %s: %m", p);
+ return log_error_errno(r, "Failed to read machine ID from %s: %m", etc_machine_id);
id = strstrip(b);
if (isempty(id) && arg_link_journal == LINK_AUTO)
@@ -1739,7 +2123,7 @@ static int setup_journal(const char *directory) {
/* Verify validity */
r = sd_id128_from_string(id, &machine_id);
if (r < 0)
- return log_error_errno(r, "Failed to parse machine ID from %s: %m", p);
+ return log_error_errno(r, "Failed to parse machine ID from %s: %m", etc_machine_id);
r = sd_id128_get_machine(&this_id);
if (r < 0)
@@ -1756,13 +2140,22 @@ static int setup_journal(const char *directory) {
if (arg_link_journal == LINK_NO)
return 0;
- free(p);
- p = strappend("/var/log/journal/", id);
- q = strjoin(directory, "/var/log/journal/", id, NULL);
- if (!p || !q)
- return log_oom();
+ r = userns_mkdir(directory, "/var", 0755, 0, 0);
+ if (r < 0)
+ return log_error_errno(r, "Failed to create /var: %m");
- if (path_is_mount_point(p, false) > 0) {
+ r = userns_mkdir(directory, "/var/log", 0755, 0, 0);
+ if (r < 0)
+ return log_error_errno(r, "Failed to create /var/log: %m");
+
+ r = userns_mkdir(directory, "/var/log/journal", 0755, 0, 0);
+ if (r < 0)
+ return log_error_errno(r, "Failed to create /var/log/journal: %m");
+
+ p = strjoina("/var/log/journal/", id);
+ q = prefix_roota(directory, p);
+
+ if (path_is_mount_point(p, 0) > 0) {
if (arg_link_journal != LINK_AUTO) {
log_error("%s: already a mount point, refusing to use for journal", p);
return -EEXIST;
@@ -1771,7 +2164,7 @@ static int setup_journal(const char *directory) {
return 0;
}
- if (path_is_mount_point(q, false) > 0) {
+ if (path_is_mount_point(q, 0) > 0) {
if (arg_link_journal != LINK_AUTO) {
log_error("%s: already a mount point, refusing to use for journal", q);
return -EEXIST;
@@ -1786,7 +2179,7 @@ static int setup_journal(const char *directory) {
arg_link_journal == LINK_AUTO) &&
path_equal(d, q)) {
- r = mkdir_p(q, 0755);
+ r = userns_mkdir(directory, p, 0755, 0, 0);
if (r < 0)
log_warning_errno(errno, "Failed to create directory %s: %m", q);
return 0;
@@ -1824,7 +2217,7 @@ static int setup_journal(const char *directory) {
}
}
- r = mkdir_p(q, 0755);
+ r = userns_mkdir(directory, p, 0755, 0, 0);
if (r < 0)
log_warning_errno(errno, "Failed to create directory %s: %m", q);
return 0;
@@ -1850,13 +2243,13 @@ static int setup_journal(const char *directory) {
if (dir_is_empty(q) == 0)
log_warning("%s is not empty, proceeding anyway.", q);
- r = mkdir_p(q, 0755);
+ r = userns_mkdir(directory, p, 0755, 0, 0);
if (r < 0) {
log_error_errno(errno, "Failed to create %s: %m", q);
return r;
}
- if (mount(p, q, "bind", MS_BIND, NULL) < 0)
+ if (mount(p, q, NULL, MS_BIND, NULL) < 0)
return log_error_errno(errno, "Failed to bind mount journal from host into guest: %m");
return 0;
@@ -1897,6 +2290,8 @@ static int register_machine(pid_t pid, int local_ifindex) {
local_ifindex > 0 ? 1 : 0, local_ifindex);
} else {
_cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ char **i;
+ unsigned j;
r = sd_bus_message_new_method_call(
bus,
@@ -1906,7 +2301,7 @@ static int register_machine(pid_t pid, int local_ifindex) {
"org.freedesktop.machine1.Manager",
"CreateMachineWithNetwork");
if (r < 0)
- return log_error_errno(r, "Failed to create message: %m");
+ return bus_log_create_error(r);
r = sd_bus_message_append(
m,
@@ -1919,22 +2314,26 @@ static int register_machine(pid_t pid, int local_ifindex) {
strempty(arg_directory),
local_ifindex > 0 ? 1 : 0, local_ifindex);
if (r < 0)
- return log_error_errno(r, "Failed to append message arguments: %m");
+ return bus_log_create_error(r);
r = sd_bus_message_open_container(m, 'a', "(sv)");
if (r < 0)
- return log_error_errno(r, "Failed to open container: %m");
+ return bus_log_create_error(r);
if (!isempty(arg_slice)) {
r = sd_bus_message_append(m, "(sv)", "Slice", "s", arg_slice);
if (r < 0)
- return log_error_errno(r, "Failed to append slice: %m");
+ return bus_log_create_error(r);
}
r = sd_bus_message_append(m, "(sv)", "DevicePolicy", "s", "strict");
if (r < 0)
- return log_error_errno(r, "Failed to add device policy: %m");
+ return bus_log_create_error(r);
+ /* If you make changes here, also make sure to update
+ * systemd-nspawn@.service, to keep the device
+ * policies in sync regardless if we are run with or
+ * without the --keep-unit switch. */
r = sd_bus_message_append(m, "(sv)", "DeviceAllow", "a(ss)", 9,
/* Allow the container to
* access and create the API
@@ -1957,11 +2356,53 @@ static int register_machine(pid_t pid, int local_ifindex) {
"/dev/pts/ptmx", "rw",
"char-pts", "rw");
if (r < 0)
- return log_error_errno(r, "Failed to add device whitelist: %m");
+ return bus_log_create_error(r);
+
+ for (j = 0; j < arg_n_custom_mounts; j++) {
+ CustomMount *cm = &arg_custom_mounts[j];
+
+ if (cm->type != CUSTOM_MOUNT_BIND)
+ continue;
+
+ r = is_device_node(cm->source);
+ if (r < 0)
+ return log_error_errno(r, "Failed to stat %s: %m", cm->source);
+
+ if (r) {
+ r = sd_bus_message_append(m, "(sv)", "DeviceAllow", "a(ss)", 1,
+ cm->source, cm->read_only ? "r" : "rw");
+ if (r < 0)
+ return log_error_errno(r, "Failed to append message arguments: %m");
+ }
+ }
+
+ if (arg_kill_signal != 0) {
+ r = sd_bus_message_append(m, "(sv)", "KillSignal", "i", arg_kill_signal);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_append(m, "(sv)", "KillMode", "s", "mixed");
+ if (r < 0)
+ return bus_log_create_error(r);
+ }
+
+ STRV_FOREACH(i, arg_property) {
+ r = sd_bus_message_open_container(m, 'r', "sv");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = bus_append_unit_property_assignment(m, *i);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_close_container(m);
+ if (r < 0)
+ return bus_log_create_error(r);
+ }
r = sd_bus_message_close_container(m);
if (r < 0)
- return log_error_errno(r, "Failed to close container: %m");
+ return bus_log_create_error(r);
r = sd_bus_call(bus, m, 0, &error, NULL);
}
@@ -1984,6 +2425,11 @@ static int terminate_machine(pid_t pid) {
if (!arg_register)
return 0;
+ /* If we are reusing the unit, then just exit, systemd will do
+ * the right thing when we exit. */
+ if (arg_keep_unit)
+ return 0;
+
r = sd_bus_default_system(&bus);
if (r < 0)
return log_error_errno(r, "Failed to open system bus: %m");
@@ -2046,11 +2492,12 @@ static int reset_audit_loginuid(void) {
r = write_string_file("/proc/self/loginuid", "4294967295");
if (r < 0) {
- log_error("Failed to reset audit login UID. This probably means that your kernel is too\n"
- "old and you have audit enabled. Note that the auditing subsystem is known to\n"
- "be incompatible with containers on old kernels. Please make sure to upgrade\n"
- "your kernel or to off auditing with 'audit=0' on the kernel command line before\n"
- "using systemd-nspawn. Sleeping for 5s... (%s)\n", strerror(-r));
+ log_error_errno(r,
+ "Failed to reset audit login UID. This probably means that your kernel is too\n"
+ "old and you have audit enabled. Note that the auditing subsystem is known to\n"
+ "be incompatible with containers on old kernels. Please make sure to upgrade\n"
+ "your kernel or to off auditing with 'audit=0' on the kernel command line before\n"
+ "using systemd-nspawn. Sleeping for 5s... (%m)");
sleep(5);
}
@@ -2181,7 +2628,7 @@ static int setup_veth(pid_t pid, char iface_name[IFNAMSIZ], int *ifi) {
r = sd_rtnl_call(rtnl, m, 0, NULL);
if (r < 0)
- return log_error_errno(r, "Failed to add new veth interfaces: %m");
+ return log_error_errno(r, "Failed to add new veth interfaces (host0, %s): %m", iface_name);
i = (int) if_nametoindex(iface_name);
if (i <= 0)
@@ -2482,19 +2929,20 @@ static int setup_ipvlan(pid_t pid) {
static int setup_seccomp(void) {
#ifdef HAVE_SECCOMP
- static const int blacklist[] = {
- SCMP_SYS(kexec_load),
- SCMP_SYS(open_by_handle_at),
- SCMP_SYS(iopl),
- SCMP_SYS(ioperm),
- SCMP_SYS(swapon),
- SCMP_SYS(swapoff),
- };
-
- static const int kmod_blacklist[] = {
- SCMP_SYS(init_module),
- SCMP_SYS(finit_module),
- SCMP_SYS(delete_module),
+ static const struct {
+ uint64_t capability;
+ int syscall_num;
+ } blacklist[] = {
+ { CAP_SYS_RAWIO, SCMP_SYS(iopl) },
+ { CAP_SYS_RAWIO, SCMP_SYS(ioperm) },
+ { CAP_SYS_BOOT, SCMP_SYS(kexec_load) },
+ { CAP_SYS_ADMIN, SCMP_SYS(swapon) },
+ { CAP_SYS_ADMIN, SCMP_SYS(swapoff) },
+ { CAP_SYS_ADMIN, SCMP_SYS(open_by_handle_at) },
+ { CAP_SYS_MODULE, SCMP_SYS(init_module) },
+ { CAP_SYS_MODULE, SCMP_SYS(finit_module) },
+ { CAP_SYS_MODULE, SCMP_SYS(delete_module) },
+ { CAP_SYSLOG, SCMP_SYS(syslog) },
};
scmp_filter_ctx seccomp;
@@ -2512,7 +2960,10 @@ static int setup_seccomp(void) {
}
for (i = 0; i < ELEMENTSOF(blacklist); i++) {
- r = seccomp_rule_add(seccomp, SCMP_ACT_ERRNO(EPERM), blacklist[i], 0);
+ if (arg_retain & (1ULL << blacklist[i].capability))
+ continue;
+
+ r = seccomp_rule_add(seccomp, SCMP_ACT_ERRNO(EPERM), blacklist[i].syscall_num, 0);
if (r == -EFAULT)
continue; /* unknown syscall */
if (r < 0) {
@@ -2521,19 +2972,6 @@ static int setup_seccomp(void) {
}
}
- /* If the CAP_SYS_MODULE capability is not requested then
- * we'll block the kmod syscalls too */
- if (!(arg_retain & (1ULL << CAP_SYS_MODULE))) {
- for (i = 0; i < ELEMENTSOF(kmod_blacklist); i++) {
- r = seccomp_rule_add(seccomp, SCMP_ACT_ERRNO(EPERM), kmod_blacklist[i], 0);
- if (r == -EFAULT)
- continue; /* unknown syscall */
- if (r < 0) {
- log_error_errno(r, "Failed to block syscall: %m");
- goto finish;
- }
- }
- }
/*
Audit is broken in containers, much of the userspace audit
@@ -2584,10 +3022,16 @@ static int setup_propagate(const char *root) {
p = strjoina("/run/systemd/nspawn/propagate/", arg_machine);
(void) mkdir_p(p, 0600);
- q = strjoina(root, "/run/systemd/nspawn/incoming");
- mkdir_parents(q, 0755);
- mkdir_p(q, 0600);
+ if (userns_mkdir(root, "/run/systemd", 0755, 0, 0) < 0)
+ return log_error_errno(errno, "Failed to create /run/systemd: %m");
+
+ if (userns_mkdir(root, "/run/systemd/nspawn", 0755, 0, 0) < 0)
+ return log_error_errno(errno, "Failed to create /run/systemd/nspawn: %m");
+
+ if (userns_mkdir(root, "/run/systemd/nspawn/incoming", 0600, 0, 0) < 0)
+ return log_error_errno(errno, "Failed to create /run/systemd/nspawn/incoming: %m");
+ q = prefix_roota(root, "/run/systemd/nspawn/incoming");
if (mount(p, q, NULL, MS_BIND, NULL) < 0)
return log_error_errno(errno, "Failed to install propagation bind mount.");
@@ -2676,7 +3120,7 @@ static int setup_image(char **device_path, int *loop_nr) {
#define PARTITION_TABLE_BLURB \
"Note that the disk image needs to either contain only a single MBR partition of\n" \
- "type 0x83 that is marked bootable, or a sinlge GPT partition of type" \
+ "type 0x83 that is marked bootable, or a single GPT partition of type " \
"0FC63DAF-8483-4772-8E79-3D69D8477DE4 or follow\n" \
" http://www.freedesktop.org/wiki/Specifications/DiscoverablePartitionsSpec/\n" \
"to be bootable with systemd-nspawn."
@@ -2748,7 +3192,7 @@ static int dissect_image(
return -errno;
}
- blkid_probe_lookup_value(b, "PTTYPE", &pttype, NULL);
+ (void) blkid_probe_lookup_value(b, "PTTYPE", &pttype, NULL);
is_gpt = streq_ptr(pttype, "gpt");
is_mbr = streq_ptr(pttype, "dos");
@@ -3050,7 +3494,7 @@ static int dissect_image(
return 0;
#else
log_error("--image= is not supported, compiled without blkid support.");
- return -ENOTSUP;
+ return -EOPNOTSUPP;
#endif
}
@@ -3105,7 +3549,7 @@ static int mount_device(const char *what, const char *where, const char *directo
if (streq(fstype, "crypto_LUKS")) {
log_error("nspawn currently does not support LUKS disk images.");
- return -ENOTSUP;
+ return -EOPNOTSUPP;
}
if (mount(what, p, fstype, MS_NODEV|(rw ? 0 : MS_RDONLY), NULL) < 0)
@@ -3114,7 +3558,7 @@ static int mount_device(const char *what, const char *where, const char *directo
return 0;
#else
log_error("--image= is not supported, compiled without blkid support.");
- return -ENOTSUP;
+ return -EOPNOTSUPP;
#endif
}
@@ -3246,14 +3690,9 @@ static int change_uid_gid(char **_home) {
if (!arg_user || streq(arg_user, "root") || streq(arg_user, "0")) {
/* Reset everything fully to 0, just in case */
- if (setgroups(0, NULL) < 0)
- return log_error_errno(errno, "setgroups() failed: %m");
-
- if (setresgid(0, 0, 0) < 0)
- return log_error_errno(errno, "setregid() failed: %m");
-
- if (setresuid(0, 0, 0) < 0)
- return log_error_errno(errno, "setreuid() failed: %m");
+ r = reset_uid_gid();
+ if (r < 0)
+ return log_error_errno(r, "Failed to become root: %m");
*_home = NULL;
return 0;
@@ -3397,9 +3836,9 @@ static int change_uid_gid(char **_home) {
if (r < 0 && r != -EEXIST)
return log_error_errno(r, "Failed to make home directory: %m");
- fchown(STDIN_FILENO, uid, gid);
- fchown(STDOUT_FILENO, uid, gid);
- fchown(STDERR_FILENO, uid, gid);
+ (void) fchown(STDIN_FILENO, uid, gid);
+ (void) fchown(STDOUT_FILENO, uid, gid);
+ (void) fchown(STDERR_FILENO, uid, gid);
if (setgroups(n_uids, uids) < 0)
return log_error_errno(errno, "Failed to set auxiliary groups: %m");
@@ -3490,7 +3929,7 @@ static int on_orderly_shutdown(sd_event_source *s, const struct signalfd_siginfo
pid = PTR_TO_UINT32(userdata);
if (pid > 0) {
- if (kill(pid, SIGRTMIN+3) >= 0) {
+ if (kill(pid, arg_kill_signal) >= 0) {
log_info("Trying to halt container. Send SIGTERM again to trigger immediate termination.");
sd_event_source_set_userdata(s, NULL);
return 0;
@@ -3523,7 +3962,8 @@ static int determine_names(void) {
if (r < 0)
return log_error_errno(r, "Invalid image directory: %m");
- arg_read_only = arg_read_only || i->read_only;
+ if (!arg_ephemeral)
+ arg_read_only = arg_read_only || i->read_only;
} else
arg_directory = get_current_dir_name();
@@ -3567,6 +4007,447 @@ static int determine_names(void) {
return 0;
}
+static int determine_uid_shift(const char *directory) {
+ int r;
+
+ if (!arg_userns) {
+ arg_uid_shift = 0;
+ return 0;
+ }
+
+ if (arg_uid_shift == UID_INVALID) {
+ struct stat st;
+
+ r = stat(directory, &st);
+ if (r < 0)
+ return log_error_errno(errno, "Failed to determine UID base of %s: %m", directory);
+
+ arg_uid_shift = st.st_uid & UINT32_C(0xffff0000);
+
+ if (arg_uid_shift != (st.st_gid & UINT32_C(0xffff0000))) {
+ log_error("UID and GID base of %s don't match.", directory);
+ return -EINVAL;
+ }
+
+ arg_uid_range = UINT32_C(0x10000);
+ }
+
+ if (arg_uid_shift > (uid_t) -1 - arg_uid_range) {
+ log_error("UID base too high for UID range.");
+ return -EINVAL;
+ }
+
+ log_info("Using user namespaces with base " UID_FMT " and range " UID_FMT ".", arg_uid_shift, arg_uid_range);
+ return 0;
+}
+
+static int inner_child(
+ Barrier *barrier,
+ const char *directory,
+ bool secondary,
+ int kmsg_socket,
+ int rtnl_socket,
+ FDSet *fds,
+ int argc,
+ char *argv[]) {
+
+ _cleanup_free_ char *home = NULL;
+ unsigned n_env = 2;
+ const char *envp[] = {
+ "PATH=" DEFAULT_PATH_SPLIT_USR,
+ "container=systemd-nspawn", /* LXC sets container=lxc, so follow the scheme here */
+ NULL, /* TERM */
+ NULL, /* HOME */
+ NULL, /* USER */
+ NULL, /* LOGNAME */
+ NULL, /* container_uuid */
+ NULL, /* LISTEN_FDS */
+ NULL, /* LISTEN_PID */
+ NULL
+ };
+
+ _cleanup_strv_free_ char **env_use = NULL;
+ int r;
+
+ assert(barrier);
+ assert(directory);
+ assert(kmsg_socket >= 0);
+
+ if (arg_userns) {
+ /* Tell the parent, that it now can write the UID map. */
+ (void) barrier_place(barrier); /* #1 */
+
+ /* Wait until the parent wrote the UID map */
+ if (!barrier_place_and_sync(barrier)) { /* #2 */
+ log_error("Parent died too early");
+ return -ESRCH;
+ }
+ }
+
+ r = mount_all(NULL, true);
+ if (r < 0)
+ return r;
+
+ /* Wait until we are cgroup-ified, so that we
+ * can mount the right cgroup path writable */
+ if (!barrier_place_and_sync(barrier)) { /* #3 */
+ log_error("Parent died too early");
+ return -ESRCH;
+ }
+
+ r = mount_systemd_cgroup_writable("");
+ if (r < 0)
+ return r;
+
+ r = reset_uid_gid();
+ if (r < 0)
+ return log_error_errno(r, "Couldn't become new root: %m");
+
+ r = setup_boot_id(NULL);
+ if (r < 0)
+ return r;
+
+ r = setup_kmsg(NULL, kmsg_socket);
+ if (r < 0)
+ return r;
+ kmsg_socket = safe_close(kmsg_socket);
+
+ umask(0022);
+
+ if (setsid() < 0)
+ return log_error_errno(errno, "setsid() failed: %m");
+
+ if (arg_private_network)
+ loopback_setup();
+
+ r = send_rtnl(rtnl_socket);
+ if (r < 0)
+ return r;
+ rtnl_socket = safe_close(rtnl_socket);
+
+ if (drop_capabilities() < 0)
+ return log_error_errno(errno, "drop_capabilities() failed: %m");
+
+ setup_hostname();
+
+ if (arg_personality != PERSONALITY_INVALID) {
+ if (personality(arg_personality) < 0)
+ return log_error_errno(errno, "personality() failed: %m");
+ } else if (secondary) {
+ if (personality(PER_LINUX32) < 0)
+ return log_error_errno(errno, "personality() failed: %m");
+ }
+
+#ifdef HAVE_SELINUX
+ if (arg_selinux_context)
+ if (setexeccon((security_context_t) arg_selinux_context) < 0)
+ return log_error_errno(errno, "setexeccon(\"%s\") failed: %m", arg_selinux_context);
+#endif
+
+ r = change_uid_gid(&home);
+ if (r < 0)
+ return r;
+
+ envp[n_env] = strv_find_prefix(environ, "TERM=");
+ if (envp[n_env])
+ n_env ++;
+
+ if ((asprintf((char**)(envp + n_env++), "HOME=%s", home ? home: "/root") < 0) ||
+ (asprintf((char**)(envp + n_env++), "USER=%s", arg_user ? arg_user : "root") < 0) ||
+ (asprintf((char**)(envp + n_env++), "LOGNAME=%s", arg_user ? arg_user : "root") < 0))
+ return log_oom();
+
+ if (!sd_id128_equal(arg_uuid, SD_ID128_NULL)) {
+ char as_uuid[37];
+
+ if (asprintf((char**)(envp + n_env++), "container_uuid=%s", id128_format_as_uuid(arg_uuid, as_uuid)) < 0)
+ return log_oom();
+ }
+
+ if (fdset_size(fds) > 0) {
+ r = fdset_cloexec(fds, false);
+ if (r < 0)
+ return log_error_errno(r, "Failed to unset O_CLOEXEC for file descriptors.");
+
+ if ((asprintf((char **)(envp + n_env++), "LISTEN_FDS=%u", fdset_size(fds)) < 0) ||
+ (asprintf((char **)(envp + n_env++), "LISTEN_PID=1") < 0))
+ return log_oom();
+ }
+
+ env_use = strv_env_merge(2, envp, arg_setenv);
+ if (!env_use)
+ return log_oom();
+
+ /* Let the parent know that we are ready and
+ * wait until the parent is ready with the
+ * setup, too... */
+ if (!barrier_place_and_sync(barrier)) { /* #4 */
+ log_error("Parent died too early");
+ return -ESRCH;
+ }
+
+ /* Now, explicitly close the log, so that we
+ * then can close all remaining fds. Closing
+ * the log explicitly first has the benefit
+ * that the logging subsystem knows about it,
+ * and is thus ready to be reopened should we
+ * need it again. Note that the other fds
+ * closed here are at least the locking and
+ * barrier fds. */
+ log_close();
+ (void) fdset_close_others(fds);
+
+ if (arg_boot) {
+ char **a;
+ size_t m;
+
+ /* Automatically search for the init system */
+
+ m = 1 + argc - optind;
+ a = newa(char*, m + 1);
+ memcpy(a + 1, argv + optind, m * sizeof(char*));
+
+ a[0] = (char*) "/usr/lib/systemd/systemd";
+ execve(a[0], a, env_use);
+
+ a[0] = (char*) "/lib/systemd/systemd";
+ execve(a[0], a, env_use);
+
+ a[0] = (char*) "/sbin/init";
+ execve(a[0], a, env_use);
+ } else if (argc > optind)
+ execvpe(argv[optind], argv + optind, env_use);
+ else {
+ chdir(home ? home : "/root");
+ execle("/bin/bash", "-bash", NULL, env_use);
+ execle("/bin/sh", "-sh", NULL, env_use);
+ }
+
+ (void) log_open();
+ return log_error_errno(errno, "execv() failed: %m");
+}
+
+static int outer_child(
+ Barrier *barrier,
+ const char *directory,
+ const char *console,
+ const char *root_device, bool root_device_rw,
+ const char *home_device, bool home_device_rw,
+ const char *srv_device, bool srv_device_rw,
+ bool interactive,
+ bool secondary,
+ int pid_socket,
+ int kmsg_socket,
+ int rtnl_socket,
+ FDSet *fds,
+ int argc,
+ char *argv[]) {
+
+ pid_t pid;
+ ssize_t l;
+ int r;
+
+ assert(barrier);
+ assert(directory);
+ assert(console);
+ assert(pid_socket >= 0);
+ assert(kmsg_socket >= 0);
+
+ if (prctl(PR_SET_PDEATHSIG, SIGKILL) < 0)
+ return log_error_errno(errno, "PR_SET_PDEATHSIG failed: %m");
+
+ if (interactive) {
+ close_nointr(STDIN_FILENO);
+ close_nointr(STDOUT_FILENO);
+ close_nointr(STDERR_FILENO);
+
+ r = open_terminal(console, O_RDWR);
+ if (r != STDIN_FILENO) {
+ if (r >= 0) {
+ safe_close(r);
+ r = -EINVAL;
+ }
+
+ return log_error_errno(r, "Failed to open console: %m");
+ }
+
+ if (dup2(STDIN_FILENO, STDOUT_FILENO) != STDOUT_FILENO ||
+ dup2(STDIN_FILENO, STDERR_FILENO) != STDERR_FILENO)
+ return log_error_errno(errno, "Failed to duplicate console: %m");
+ }
+
+ r = reset_audit_loginuid();
+ if (r < 0)
+ return r;
+
+ /* Mark everything as slave, so that we still
+ * receive mounts from the real root, but don't
+ * propagate mounts to the real root. */
+ if (mount(NULL, "/", NULL, MS_SLAVE|MS_REC, NULL) < 0)
+ return log_error_errno(errno, "MS_SLAVE|MS_REC failed: %m");
+
+ r = mount_devices(directory,
+ root_device, root_device_rw,
+ home_device, home_device_rw,
+ srv_device, srv_device_rw);
+ if (r < 0)
+ return r;
+
+ r = determine_uid_shift(directory);
+ if (r < 0)
+ return r;
+
+ /* Turn directory into bind mount */
+ if (mount(directory, directory, NULL, MS_BIND|MS_REC, NULL) < 0)
+ return log_error_errno(errno, "Failed to make bind mount: %m");
+
+ r = setup_volatile(directory);
+ if (r < 0)
+ return r;
+
+ r = setup_volatile_state(directory);
+ if (r < 0)
+ return r;
+
+ r = base_filesystem_create(directory, arg_uid_shift, (gid_t) arg_uid_shift);
+ if (r < 0)
+ return r;
+
+ if (arg_read_only) {
+ r = bind_remount_recursive(directory, true);
+ if (r < 0)
+ return log_error_errno(r, "Failed to make tree read-only: %m");
+ }
+
+ r = mount_all(directory, false);
+ if (r < 0)
+ return r;
+
+ if (copy_devnodes(directory) < 0)
+ return r;
+
+ dev_setup(directory, arg_uid_shift, arg_uid_shift);
+
+ if (setup_pts(directory) < 0)
+ return r;
+
+ r = setup_propagate(directory);
+ if (r < 0)
+ return r;
+
+ r = setup_dev_console(directory, console);
+ if (r < 0)
+ return r;
+
+ r = setup_seccomp();
+ if (r < 0)
+ return r;
+
+ r = setup_timezone(directory);
+ if (r < 0)
+ return r;
+
+ r = setup_resolv_conf(directory);
+ if (r < 0)
+ return r;
+
+ r = setup_journal(directory);
+ if (r < 0)
+ return r;
+
+ r = mount_custom(directory);
+ if (r < 0)
+ return r;
+
+ r = mount_cgroup(directory);
+ if (r < 0)
+ return r;
+
+ r = mount_move_root(directory);
+ if (r < 0)
+ return log_error_errno(r, "Failed to move root directory: %m");
+
+ pid = raw_clone(SIGCHLD|CLONE_NEWNS|
+ (arg_share_system ? 0 : CLONE_NEWIPC|CLONE_NEWPID|CLONE_NEWUTS) |
+ (arg_private_network ? CLONE_NEWNET : 0) |
+ (arg_userns ? CLONE_NEWUSER : 0),
+ NULL);
+ if (pid < 0)
+ return log_error_errno(errno, "Failed to fork inner child: %m");
+
+ if (pid == 0) {
+ pid_socket = safe_close(pid_socket);
+
+ /* The inner child has all namespaces that are
+ * requested, so that we all are owned by the user if
+ * user namespaces are turned on. */
+
+ r = inner_child(barrier, directory, secondary, kmsg_socket, rtnl_socket, fds, argc, argv);
+ if (r < 0)
+ _exit(EXIT_FAILURE);
+
+ _exit(EXIT_SUCCESS);
+ }
+
+ l = send(pid_socket, &pid, sizeof(pid), MSG_NOSIGNAL);
+ if (l < 0)
+ return log_error_errno(errno, "Failed to send PID: %m");
+ if (l != sizeof(pid)) {
+ log_error("Short write while sending PID.");
+ return -EIO;
+ }
+
+ pid_socket = safe_close(pid_socket);
+
+ return 0;
+}
+
+static int setup_uid_map(pid_t pid) {
+ char uid_map[strlen("/proc//uid_map") + DECIMAL_STR_MAX(uid_t) + 1], line[DECIMAL_STR_MAX(uid_t)*3+3+1];
+ int r;
+
+ assert(pid > 1);
+
+ xsprintf(uid_map, "/proc/" PID_FMT "/uid_map", pid);
+ xsprintf(line, UID_FMT " " UID_FMT " " UID_FMT "\n", 0, arg_uid_shift, arg_uid_range);
+ r = write_string_file(uid_map, line);
+ if (r < 0)
+ return log_error_errno(r, "Failed to write UID map: %m");
+
+ /* We always assign the same UID and GID ranges */
+ xsprintf(uid_map, "/proc/" PID_FMT "/gid_map", pid);
+ r = write_string_file(uid_map, line);
+ if (r < 0)
+ return log_error_errno(r, "Failed to write GID map: %m");
+
+ return 0;
+}
+
+static int chown_cgroup(pid_t pid) {
+ _cleanup_free_ char *path = NULL, *fs = NULL;
+ _cleanup_close_ int fd = -1;
+ const char *fn;
+ int r;
+
+ r = cg_pid_get_path(NULL, pid, &path);
+ if (r < 0)
+ return log_error_errno(r, "Failed to get container cgroup path: %m");
+
+ r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, path, NULL, &fs);
+ if (r < 0)
+ return log_error_errno(r, "Failed to get file system path for container cgroup: %m");
+
+ fd = open(fs, O_RDONLY|O_CLOEXEC|O_DIRECTORY);
+ if (fd < 0)
+ return log_error_errno(errno, "Failed to open %s: %m", fs);
+
+ FOREACH_STRING(fn, ".", "tasks", "notify_on_release", "cgroup.procs", "cgroup.clone_children")
+ if (fchownat(fd, fn, arg_uid_shift, arg_uid_shift, 0) < 0)
+ log_warning_errno(errno, "Failed to chown() cgroup file %s, ignoring: %m", fn);
+
+ return 0;
+}
+
int main(int argc, char *argv[]) {
_cleanup_free_ char *device_path = NULL, *root_device = NULL, *home_device = NULL, *srv_device = NULL, *console = NULL;
@@ -3581,6 +4462,7 @@ int main(int argc, char *argv[]) {
int ret = EXIT_SUCCESS;
union in_addr_union exposed = {};
_cleanup_release_lock_file_ LockFile tree_global_lock = LOCK_FILE_INIT, tree_local_lock = LOCK_FILE_INIT;
+ bool interactive;
log_parse_environment();
log_open();
@@ -3599,13 +4481,6 @@ int main(int argc, char *argv[]) {
goto finish;
}
- if (sd_booted() <= 0) {
- log_error("Not running on a systemd system.");
- r = -EINVAL;
- goto finish;
- }
-
- log_close();
n_fd_passed = sd_listen_fds(false);
if (n_fd_passed > 0) {
r = fdset_new_listen_fds(&fds, false);
@@ -3614,8 +4489,6 @@ int main(int argc, char *argv[]) {
goto finish;
}
}
- fdset_close_others(fds);
- log_open();
if (arg_directory) {
assert(!arg_image);
@@ -3627,7 +4500,7 @@ int main(int argc, char *argv[]) {
}
if (arg_ephemeral) {
- char *np;
+ _cleanup_free_ char *np = NULL;
/* If the specified path is a mount point we
* generate the new snapshot immediately
@@ -3635,7 +4508,7 @@ int main(int argc, char *argv[]) {
* the specified is not a mount point we
* create the new snapshot in the parent
* directory, just next to it. */
- r = path_is_mount_point(arg_directory, false);
+ r = path_is_mount_point(arg_directory, 0);
if (r < 0) {
log_error_errno(r, "Failed to determine whether directory %s is mount point: %m", arg_directory);
goto finish;
@@ -3655,15 +4528,15 @@ int main(int argc, char *argv[]) {
goto finish;
}
- r = btrfs_subvol_snapshot(arg_directory, np, arg_read_only, true);
+ r = btrfs_subvol_snapshot(arg_directory, np, (arg_read_only ? BTRFS_SNAPSHOT_READ_ONLY : 0) | BTRFS_SNAPSHOT_FALLBACK_COPY | BTRFS_SNAPSHOT_RECURSIVE);
if (r < 0) {
- free(np);
log_error_errno(r, "Failed to create snapshot %s from %s: %m", np, arg_directory);
goto finish;
}
free(arg_directory);
arg_directory = np;
+ np = NULL;
remove_subvol = true;
@@ -3679,7 +4552,7 @@ int main(int argc, char *argv[]) {
}
if (arg_template) {
- r = btrfs_subvol_snapshot(arg_template, arg_directory, arg_read_only, true);
+ r = btrfs_subvol_snapshot(arg_template, arg_directory, (arg_read_only ? BTRFS_SNAPSHOT_READ_ONLY : 0) | BTRFS_SNAPSHOT_FALLBACK_COPY | BTRFS_SNAPSHOT_RECURSIVE);
if (r == -EEXIST) {
if (!arg_quiet)
log_info("Directory %s already exists, not populating from template %s.", arg_directory, arg_template);
@@ -3754,6 +4627,14 @@ int main(int argc, char *argv[]) {
goto finish;
}
+ r = custom_mounts_prepare();
+ if (r < 0)
+ goto finish;
+
+ interactive =
+ isatty(STDIN_FILENO) > 0 &&
+ isatty(STDOUT_FILENO) > 0;
+
master = posix_openpt(O_RDWR|O_NOCTTY|O_CLOEXEC|O_NDELAY);
if (master < 0) {
r = log_error_errno(errno, "Failed to acquire pseudo tty: %m");
@@ -3766,15 +4647,15 @@ int main(int argc, char *argv[]) {
goto finish;
}
- if (!arg_quiet)
- log_info("Spawning container %s on %s.\nPress ^] three times within 1s to kill container.",
- arg_machine, arg_image ?: arg_directory);
-
if (unlockpt(master) < 0) {
r = log_error_errno(errno, "Failed to unlock tty: %m");
goto finish;
}
+ if (!arg_quiet)
+ log_info("Spawning container %s on %s.\nPress ^] three times within 1s to kill container.",
+ arg_machine, arg_image ?: arg_directory);
+
assert_se(sigemptyset(&mask) == 0);
sigset_add_many(&mask, SIGCHLD, SIGWINCH, SIGTERM, SIGINT, -1);
assert_se(sigprocmask(SIG_BLOCK, &mask, NULL) == 0);
@@ -3782,14 +4663,25 @@ int main(int argc, char *argv[]) {
assert_se(sigemptyset(&mask_chld) == 0);
assert_se(sigaddset(&mask_chld, SIGCHLD) == 0);
+ if (prctl(PR_SET_CHILD_SUBREAPER, 1) < 0) {
+ r = log_error_errno(errno, "Failed to become subreaper: %m");
+ goto finish;
+ }
+
for (;;) {
- _cleanup_close_pair_ int kmsg_socket_pair[2] = { -1, -1 }, rtnl_socket_pair[2] = { -1, -1 };
+ _cleanup_close_pair_ int kmsg_socket_pair[2] = { -1, -1 }, rtnl_socket_pair[2] = { -1, -1 }, pid_socket_pair[2] = { -1, -1 };
ContainerStatus container_status;
_cleanup_(barrier_destroy) Barrier barrier = BARRIER_NULL;
- struct sigaction sa = {
+ static const struct sigaction sa = {
.sa_handler = nop_handler,
.sa_flags = SA_NOCLDSTOP,
};
+ int ifi = 0;
+ ssize_t l;
+ _cleanup_event_unref_ sd_event *event = NULL;
+ _cleanup_(pty_forward_freep) PTYForward *forward = NULL;
+ _cleanup_rtnl_unref_ sd_rtnl *rtnl = NULL;
+ char last_char = 0;
r = barrier_create(&barrier);
if (r < 0) {
@@ -3807,6 +4699,11 @@ int main(int argc, char *argv[]) {
goto finish;
}
+ if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, pid_socket_pair) < 0) {
+ r = log_error_errno(errno, "Failed to create pid socket pair: %m");
+ goto finish;
+ }
+
/* Child can be killed before execv(), so handle SIGCHLD
* in order to interrupt parent's blocking calls and
* give it a chance to call wait() and terminate. */
@@ -3822,9 +4719,7 @@ int main(int argc, char *argv[]) {
goto finish;
}
- pid = raw_clone(SIGCHLD|CLONE_NEWNS|
- (arg_share_system ? 0 : CLONE_NEWIPC|CLONE_NEWPID|CLONE_NEWUTS)|
- (arg_private_network ? CLONE_NEWNET : 0), NULL);
+ pid = raw_clone(SIGCHLD|CLONE_NEWNS, NULL);
if (pid < 0) {
if (errno == EINVAL)
r = log_error_errno(errno, "clone() failed, do you have namespace support enabled in your kernel? (You need UTS, IPC, PID and NET namespacing built in): %m");
@@ -3835,421 +4730,191 @@ int main(int argc, char *argv[]) {
}
if (pid == 0) {
- /* child */
- _cleanup_free_ char *home = NULL;
- unsigned n_env = 2;
- const char *envp[] = {
- "PATH=" DEFAULT_PATH_SPLIT_USR,
- "container=systemd-nspawn", /* LXC sets container=lxc, so follow the scheme here */
- NULL, /* TERM */
- NULL, /* HOME */
- NULL, /* USER */
- NULL, /* LOGNAME */
- NULL, /* container_uuid */
- NULL, /* LISTEN_FDS */
- NULL, /* LISTEN_PID */
- NULL
- };
- char **env_use;
-
+ /* The outer child only has a file system namespace. */
barrier_set_role(&barrier, BARRIER_CHILD);
- envp[n_env] = strv_find_prefix(environ, "TERM=");
- if (envp[n_env])
- n_env ++;
-
master = safe_close(master);
- close_nointr(STDIN_FILENO);
- close_nointr(STDOUT_FILENO);
- close_nointr(STDERR_FILENO);
-
kmsg_socket_pair[0] = safe_close(kmsg_socket_pair[0]);
rtnl_socket_pair[0] = safe_close(rtnl_socket_pair[0]);
+ pid_socket_pair[0] = safe_close(pid_socket_pair[0]);
reset_all_signal_handlers();
reset_signal_mask();
- r = open_terminal(console, O_RDWR);
- if (r != STDIN_FILENO) {
- if (r >= 0) {
- safe_close(r);
- r = -EINVAL;
- }
-
- log_error_errno(r, "Failed to open console: %m");
- _exit(EXIT_FAILURE);
- }
-
- if (dup2(STDIN_FILENO, STDOUT_FILENO) != STDOUT_FILENO ||
- dup2(STDIN_FILENO, STDERR_FILENO) != STDERR_FILENO) {
- log_error_errno(errno, "Failed to duplicate console: %m");
- _exit(EXIT_FAILURE);
- }
-
- if (setsid() < 0) {
- log_error_errno(errno, "setsid() failed: %m");
- _exit(EXIT_FAILURE);
- }
-
- if (reset_audit_loginuid() < 0)
- _exit(EXIT_FAILURE);
-
- if (prctl(PR_SET_PDEATHSIG, SIGKILL) < 0) {
- log_error_errno(errno, "PR_SET_PDEATHSIG failed: %m");
- _exit(EXIT_FAILURE);
- }
-
- /* Mark everything as slave, so that we still
- * receive mounts from the real root, but don't
- * propagate mounts to the real root. */
- if (mount(NULL, "/", NULL, MS_SLAVE|MS_REC, NULL) < 0) {
- log_error_errno(errno, "MS_SLAVE|MS_REC failed: %m");
- _exit(EXIT_FAILURE);
- }
-
- if (mount_devices(arg_directory,
- root_device, root_device_rw,
- home_device, home_device_rw,
- srv_device, srv_device_rw) < 0)
- _exit(EXIT_FAILURE);
-
- /* Turn directory into bind mount */
- if (mount(arg_directory, arg_directory, "bind", MS_BIND|MS_REC, NULL) < 0) {
- log_error_errno(errno, "Failed to make bind mount: %m");
- _exit(EXIT_FAILURE);
- }
-
- r = setup_volatile(arg_directory);
- if (r < 0)
- _exit(EXIT_FAILURE);
-
- if (setup_volatile_state(arg_directory) < 0)
- _exit(EXIT_FAILURE);
-
- r = base_filesystem_create(arg_directory);
+ r = outer_child(&barrier,
+ arg_directory,
+ console,
+ root_device, root_device_rw,
+ home_device, home_device_rw,
+ srv_device, srv_device_rw,
+ interactive,
+ secondary,
+ pid_socket_pair[1],
+ kmsg_socket_pair[1],
+ rtnl_socket_pair[1],
+ fds,
+ argc, argv);
if (r < 0)
_exit(EXIT_FAILURE);
- if (arg_read_only) {
- r = bind_remount_recursive(arg_directory, true);
- if (r < 0) {
- log_error_errno(r, "Failed to make tree read-only: %m");
- _exit(EXIT_FAILURE);
- }
- }
-
- if (mount_all(arg_directory) < 0)
- _exit(EXIT_FAILURE);
-
- if (copy_devnodes(arg_directory) < 0)
- _exit(EXIT_FAILURE);
-
- if (setup_ptmx(arg_directory) < 0)
- _exit(EXIT_FAILURE);
-
- dev_setup(arg_directory);
-
- if (setup_propagate(arg_directory) < 0)
- _exit(EXIT_FAILURE);
-
- if (setup_seccomp() < 0)
- _exit(EXIT_FAILURE);
-
- if (setup_dev_console(arg_directory, console) < 0)
- _exit(EXIT_FAILURE);
-
- if (setup_kmsg(arg_directory, kmsg_socket_pair[1]) < 0)
- _exit(EXIT_FAILURE);
- kmsg_socket_pair[1] = safe_close(kmsg_socket_pair[1]);
-
- if (send_rtnl(rtnl_socket_pair[1]) < 0)
- _exit(EXIT_FAILURE);
- rtnl_socket_pair[1] = safe_close(rtnl_socket_pair[1]);
-
- /* Tell the parent that we are ready, and that
- * it can cgroupify us to that we lack access
- * to certain devices and resources. */
- (void) barrier_place(&barrier);
-
- if (setup_boot_id(arg_directory) < 0)
- _exit(EXIT_FAILURE);
-
- if (setup_timezone(arg_directory) < 0)
- _exit(EXIT_FAILURE);
-
- if (setup_resolv_conf(arg_directory) < 0)
- _exit(EXIT_FAILURE);
-
- if (setup_journal(arg_directory) < 0)
- _exit(EXIT_FAILURE);
-
- if (mount_binds(arg_directory, arg_bind, false) < 0)
- _exit(EXIT_FAILURE);
-
- if (mount_binds(arg_directory, arg_bind_ro, true) < 0)
- _exit(EXIT_FAILURE);
-
- if (mount_tmpfs(arg_directory) < 0)
- _exit(EXIT_FAILURE);
-
- /* Wait until we are cgroup-ified, so that we
- * can mount the right cgroup path writable */
- (void) barrier_sync_next(&barrier);
-
- if (mount_cgroup(arg_directory) < 0)
- _exit(EXIT_FAILURE);
+ _exit(EXIT_SUCCESS);
+ }
- if (chdir(arg_directory) < 0) {
- log_error_errno(errno, "chdir(%s) failed: %m", arg_directory);
- _exit(EXIT_FAILURE);
- }
+ barrier_set_role(&barrier, BARRIER_PARENT);
- if (mount(arg_directory, "/", NULL, MS_MOVE, NULL) < 0) {
- log_error_errno(errno, "mount(MS_MOVE) failed: %m");
- _exit(EXIT_FAILURE);
- }
+ fdset_free(fds);
+ fds = NULL;
- if (chroot(".") < 0) {
- log_error_errno(errno, "chroot() failed: %m");
- _exit(EXIT_FAILURE);
- }
+ kmsg_socket_pair[1] = safe_close(kmsg_socket_pair[1]);
+ rtnl_socket_pair[1] = safe_close(rtnl_socket_pair[1]);
+ pid_socket_pair[1] = safe_close(pid_socket_pair[1]);
- if (chdir("/") < 0) {
- log_error_errno(errno, "chdir() failed: %m");
- _exit(EXIT_FAILURE);
- }
+ /* Wait for the outer child. */
+ r = wait_for_terminate_and_warn("namespace helper", pid, NULL);
+ if (r < 0)
+ goto finish;
+ if (r != 0) {
+ r = -EIO;
+ goto finish;
+ }
+ pid = 0;
- umask(0022);
+ /* And now retrieve the PID of the inner child. */
+ l = recv(pid_socket_pair[0], &pid, sizeof(pid), 0);
+ if (l < 0) {
+ r = log_error_errno(errno, "Failed to read inner child PID: %m");
+ goto finish;
+ }
+ if (l != sizeof(pid)) {
+ log_error("Short read while reading inner child PID: %m");
+ r = EIO;
+ goto finish;
+ }
- if (arg_private_network)
- loopback_setup();
+ log_debug("Init process invoked as PID " PID_FMT, pid);
- if (drop_capabilities() < 0) {
- log_error_errno(errno, "drop_capabilities() failed: %m");
- _exit(EXIT_FAILURE);
+ if (arg_userns) {
+ if (!barrier_place_and_sync(&barrier)) { /* #1 */
+ log_error("Child died too early.");
+ r = -ESRCH;
+ goto finish;
}
- r = change_uid_gid(&home);
+ r = setup_uid_map(pid);
if (r < 0)
- _exit(EXIT_FAILURE);
-
- if ((asprintf((char**)(envp + n_env++), "HOME=%s", home ? home: "/root") < 0) ||
- (asprintf((char**)(envp + n_env++), "USER=%s", arg_user ? arg_user : "root") < 0) ||
- (asprintf((char**)(envp + n_env++), "LOGNAME=%s", arg_user ? arg_user : "root") < 0)) {
- log_oom();
- _exit(EXIT_FAILURE);
- }
-
- if (!sd_id128_equal(arg_uuid, SD_ID128_NULL)) {
- char as_uuid[37];
-
- if (asprintf((char**)(envp + n_env++), "container_uuid=%s", id128_format_as_uuid(arg_uuid, as_uuid)) < 0) {
- log_oom();
- _exit(EXIT_FAILURE);
- }
- }
-
- if (fdset_size(fds) > 0) {
- r = fdset_cloexec(fds, false);
- if (r < 0) {
- log_error_errno(r, "Failed to unset O_CLOEXEC for file descriptors.");
- _exit(EXIT_FAILURE);
- }
-
- if ((asprintf((char **)(envp + n_env++), "LISTEN_FDS=%u", n_fd_passed) < 0) ||
- (asprintf((char **)(envp + n_env++), "LISTEN_PID=1") < 0)) {
- log_oom();
- _exit(EXIT_FAILURE);
- }
- }
-
- setup_hostname();
-
- if (arg_personality != 0xffffffffLU) {
- if (personality(arg_personality) < 0) {
- log_error_errno(errno, "personality() failed: %m");
- _exit(EXIT_FAILURE);
- }
- } else if (secondary) {
- if (personality(PER_LINUX32) < 0) {
- log_error_errno(errno, "personality() failed: %m");
- _exit(EXIT_FAILURE);
- }
- }
-
-#ifdef HAVE_SELINUX
- if (arg_selinux_context)
- if (setexeccon((security_context_t) arg_selinux_context) < 0) {
- log_error_errno(errno, "setexeccon(\"%s\") failed: %m", arg_selinux_context);
- _exit(EXIT_FAILURE);
- }
-#endif
+ goto finish;
- if (!strv_isempty(arg_setenv)) {
- char **n;
+ (void) barrier_place(&barrier); /* #2 */
+ }
- n = strv_env_merge(2, envp, arg_setenv);
- if (!n) {
- log_oom();
- _exit(EXIT_FAILURE);
- }
+ r = move_network_interfaces(pid);
+ if (r < 0)
+ goto finish;
- env_use = n;
- } else
- env_use = (char**) envp;
+ r = setup_veth(pid, veth_name, &ifi);
+ if (r < 0)
+ goto finish;
- /* Wait until the parent is ready with the setup, too... */
- if (!barrier_place_and_sync(&barrier))
- _exit(EXIT_FAILURE);
+ r = setup_bridge(veth_name, &ifi);
+ if (r < 0)
+ goto finish;
- if (arg_boot) {
- char **a;
- size_t l;
+ r = setup_macvlan(pid);
+ if (r < 0)
+ goto finish;
- /* Automatically search for the init system */
+ r = setup_ipvlan(pid);
+ if (r < 0)
+ goto finish;
- l = 1 + argc - optind;
- a = newa(char*, l + 1);
- memcpy(a + 1, argv + optind, l * sizeof(char*));
+ r = register_machine(pid, ifi);
+ if (r < 0)
+ goto finish;
- a[0] = (char*) "/usr/lib/systemd/systemd";
- execve(a[0], a, env_use);
+ r = chown_cgroup(pid);
+ if (r < 0)
+ goto finish;
- a[0] = (char*) "/lib/systemd/systemd";
- execve(a[0], a, env_use);
+ /* Notify the child that the parent is ready with all
+ * its setup (including cgroup-ification), and that
+ * the child can now hand over control to the code to
+ * run inside the container. */
+ (void) barrier_place(&barrier); /* #3 */
- a[0] = (char*) "/sbin/init";
- execve(a[0], a, env_use);
- } else if (argc > optind)
- execvpe(argv[optind], argv + optind, env_use);
- else {
- chdir(home ? home : "/root");
- execle("/bin/bash", "-bash", NULL, env_use);
- execle("/bin/sh", "-sh", NULL, env_use);
- }
+ /* Block SIGCHLD here, before notifying child.
+ * process_pty() will handle it with the other signals. */
+ assert_se(sigprocmask(SIG_BLOCK, &mask_chld, NULL) >= 0);
- log_error_errno(errno, "execv() failed: %m");
- _exit(EXIT_FAILURE);
+ /* Reset signal to default */
+ r = default_signals(SIGCHLD, -1);
+ if (r < 0) {
+ log_error_errno(r, "Failed to reset SIGCHLD: %m");
+ goto finish;
}
- barrier_set_role(&barrier, BARRIER_PARENT);
- fdset_free(fds);
- fds = NULL;
-
- kmsg_socket_pair[1] = safe_close(kmsg_socket_pair[1]);
- rtnl_socket_pair[1] = safe_close(rtnl_socket_pair[1]);
-
- /* Wait for the most basic Child-setup to be done,
- * before we add hardware to it, and place it in a
- * cgroup. */
- if (barrier_sync_next(&barrier)) {
- int ifi = 0;
-
- r = move_network_interfaces(pid);
- if (r < 0)
- goto finish;
-
- r = setup_veth(pid, veth_name, &ifi);
- if (r < 0)
- goto finish;
+ /* Let the child know that we are ready and wait that the child is completely ready now. */
+ if (!barrier_place_and_sync(&barrier)) { /* #5 */
+ log_error("Client died too early.");
+ r = -ESRCH;
+ goto finish;
+ }
- r = setup_bridge(veth_name, &ifi);
- if (r < 0)
- goto finish;
+ sd_notifyf(false,
+ "READY=1\n"
+ "STATUS=Container running.\n"
+ "X_NSPAWN_LEADER_PID=" PID_FMT, pid);
- r = setup_macvlan(pid);
- if (r < 0)
- goto finish;
-
- r = setup_ipvlan(pid);
- if (r < 0)
- goto finish;
+ r = sd_event_new(&event);
+ if (r < 0) {
+ log_error_errno(r, "Failed to get default event source: %m");
+ goto finish;
+ }
- r = register_machine(pid, ifi);
- if (r < 0)
- goto finish;
+ if (arg_kill_signal > 0) {
+ /* Try to kill the init system on SIGINT or SIGTERM */
+ sd_event_add_signal(event, NULL, SIGINT, on_orderly_shutdown, UINT32_TO_PTR(pid));
+ sd_event_add_signal(event, NULL, SIGTERM, on_orderly_shutdown, UINT32_TO_PTR(pid));
+ } else {
+ /* Immediately exit */
+ sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
+ sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
+ }
- /* Block SIGCHLD here, before notifying child.
- * process_pty() will handle it with the other signals. */
- r = sigprocmask(SIG_BLOCK, &mask_chld, NULL);
- if (r < 0)
- goto finish;
+ /* simply exit on sigchld */
+ sd_event_add_signal(event, NULL, SIGCHLD, NULL, NULL);
- /* Reset signal to default */
- r = default_signals(SIGCHLD, -1);
+ if (arg_expose_ports) {
+ r = watch_rtnl(event, rtnl_socket_pair[0], &exposed, &rtnl);
if (r < 0)
goto finish;
- /* Notify the child that the parent is ready with all
- * its setup, and that the child can now hand over
- * control to the code to run inside the container. */
- (void) barrier_place(&barrier);
-
- /* And wait that the child is completely ready now. */
- if (barrier_place_and_sync(&barrier)) {
- _cleanup_event_unref_ sd_event *event = NULL;
- _cleanup_(pty_forward_freep) PTYForward *forward = NULL;
- _cleanup_rtnl_unref_ sd_rtnl *rtnl = NULL;
- char last_char = 0;
-
- sd_notifyf(false,
- "READY=1\n"
- "STATUS=Container running.\n"
- "X_NSPAWN_LEADER_PID=" PID_FMT, pid);
-
- r = sd_event_new(&event);
- if (r < 0) {
- log_error_errno(r, "Failed to get default event source: %m");
- goto finish;
- }
-
- if (arg_boot) {
- /* Try to kill the init system on SIGINT or SIGTERM */
- sd_event_add_signal(event, NULL, SIGINT, on_orderly_shutdown, UINT32_TO_PTR(pid));
- sd_event_add_signal(event, NULL, SIGTERM, on_orderly_shutdown, UINT32_TO_PTR(pid));
- } else {
- /* Immediately exit */
- sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
- sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
- }
-
- /* simply exit on sigchld */
- sd_event_add_signal(event, NULL, SIGCHLD, NULL, NULL);
-
- if (arg_expose_ports) {
- r = watch_rtnl(event, rtnl_socket_pair[0], &exposed, &rtnl);
- if (r < 0)
- goto finish;
-
- (void) expose_ports(rtnl, &exposed);
- }
+ (void) expose_ports(rtnl, &exposed);
+ }
- rtnl_socket_pair[0] = safe_close(rtnl_socket_pair[0]);
+ rtnl_socket_pair[0] = safe_close(rtnl_socket_pair[0]);
- r = pty_forward_new(event, master, true, &forward);
- if (r < 0) {
- log_error_errno(r, "Failed to create PTY forwarder: %m");
- goto finish;
- }
+ r = pty_forward_new(event, master, true, !interactive, &forward);
+ if (r < 0) {
+ log_error_errno(r, "Failed to create PTY forwarder: %m");
+ goto finish;
+ }
- r = sd_event_loop(event);
- if (r < 0) {
- log_error_errno(r, "Failed to run event loop: %m");
- goto finish;
- }
+ r = sd_event_loop(event);
+ if (r < 0) {
+ log_error_errno(r, "Failed to run event loop: %m");
+ goto finish;
+ }
- pty_forward_get_last_char(forward, &last_char);
+ pty_forward_get_last_char(forward, &last_char);
- forward = pty_forward_free(forward);
+ forward = pty_forward_free(forward);
- if (!arg_quiet && last_char != '\n')
- putc('\n', stdout);
+ if (!arg_quiet && last_char != '\n')
+ putc('\n', stdout);
- /* Kill if it is not dead yet anyway */
- terminate_machine(pid);
- }
- }
+ /* Kill if it is not dead yet anyway */
+ terminate_machine(pid);
/* Normally redundant, but better safe than sorry */
kill(pid, SIGKILL);
@@ -4295,15 +4960,15 @@ finish:
"STOPPING=1\n"
"STATUS=Terminating...");
- loop_remove(loop_nr, &image_fd);
-
if (pid > 0)
kill(pid, SIGKILL);
+ loop_remove(loop_nr, &image_fd);
+
if (remove_subvol && arg_directory) {
int k;
- k = btrfs_subvol_remove(arg_directory);
+ k = btrfs_subvol_remove(arg_directory, true);
if (k < 0)
log_warning_errno(k, "Cannot remove subvolume '%s', ignoring: %m", arg_directory);
}
@@ -4312,7 +4977,7 @@ finish:
const char *p;
p = strjoina("/run/systemd/nspawn/propagate/", arg_machine);
- (void) rm_rf(p, false, true, false);
+ (void) rm_rf(p, REMOVE_ROOT);
}
free(arg_directory);
@@ -4324,9 +4989,7 @@ finish:
strv_free(arg_network_interfaces);
strv_free(arg_network_macvlan);
strv_free(arg_network_ipvlan);
- strv_free(arg_bind);
- strv_free(arg_bind_ro);
- strv_free(arg_tmpfs);
+ custom_mount_free_all();
flush_ports(&exposed);
diff --git a/src/nss-myhostname/nss-myhostname.c b/src/nss-myhostname/nss-myhostname.c
index a939bb267c..31db1aaf68 100644
--- a/src/nss-myhostname/nss-myhostname.c
+++ b/src/nss-myhostname/nss-myhostname.c
@@ -19,20 +19,17 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <limits.h>
#include <nss.h>
-#include <sys/types.h>
#include <netdb.h>
#include <errno.h>
#include <string.h>
-#include <unistd.h>
#include <net/if.h>
#include <stdlib.h>
-#include <arpa/inet.h>
#include "local-addresses.h"
#include "macro.h"
#include "nss-util.h"
+#include "hostname-util.h"
#include "util.h"
/* We use 127.0.0.2 as IPv4 address. This has the advantage over
diff --git a/src/nss-resolve/nss-resolve.c b/src/nss-resolve/nss-resolve.c
index 3f32ed0650..bc6c5dc2a0 100644
--- a/src/nss-resolve/nss-resolve.c
+++ b/src/nss-resolve/nss-resolve.c
@@ -19,16 +19,11 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <limits.h>
#include <nss.h>
-#include <sys/types.h>
#include <netdb.h>
#include <errno.h>
#include <string.h>
-#include <unistd.h>
-#include <net/if.h>
#include <stdlib.h>
-#include <arpa/inet.h>
#include <dlfcn.h>
#include "sd-bus.h"
diff --git a/src/path/path.c b/src/path/path.c
index 2f0148f074..f7736a4202 100644
--- a/src/path/path.c
+++ b/src/path/path.c
@@ -22,9 +22,7 @@
#include <stdio.h>
#include <getopt.h>
#include <errno.h>
-#include <unistd.h>
#include <stdlib.h>
-#include <string.h>
#include "sd-path.h"
#include "build.h"
@@ -117,7 +115,7 @@ static int print_home(const char *n) {
}
log_error("Path %s not known.", n);
- return -ENOTSUP;
+ return -EOPNOTSUPP;
}
static void help(void) {
diff --git a/src/python-systemd/_daemon.c b/src/python-systemd/_daemon.c
index 65cfec7ce8..7c5f1b2bb6 100644
--- a/src/python-systemd/_daemon.c
+++ b/src/python-systemd/_daemon.c
@@ -225,7 +225,7 @@ static PyObject* is_socket_inet(PyObject *self, PyObject *args) {
&fd, &family, &type, &listening, &port))
return NULL;
- if (port < 0 || port > INT16_MAX) {
+ if (port < 0 || port > UINT16_MAX) {
set_error(-EINVAL, NULL, "port must fit into uint16_t");
return NULL;
}
diff --git a/src/python-systemd/daemon.py b/src/python-systemd/daemon.py
index 1c386bb6fc..82011ca606 100644
--- a/src/python-systemd/daemon.py
+++ b/src/python-systemd/daemon.py
@@ -26,7 +26,7 @@ def is_socket(fileobj, family=_AF_UNSPEC, type=0, listening=-1):
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)
+ return _is_socket_inet(fd, family, type, listening, port)
def is_socket_unix(fileobj, type=0, listening=-1, path=None):
fd = _convert_fileobj(fileobj)
diff --git a/src/python-systemd/docs/layout.html b/src/python-systemd/docs/layout.html
index be5ff980ef..930a6a7afe 100644
--- a/src/python-systemd/docs/layout.html
+++ b/src/python-systemd/docs/layout.html
@@ -4,8 +4,6 @@
<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 %}
diff --git a/src/quotacheck/quotacheck.c b/src/quotacheck/quotacheck.c
index 9ae3abd990..a729f592cf 100644
--- a/src/quotacheck/quotacheck.c
+++ b/src/quotacheck/quotacheck.c
@@ -21,12 +21,11 @@
#include <stdio.h>
#include <stdbool.h>
-#include <string.h>
#include <errno.h>
#include <unistd.h>
#include "util.h"
-#include "fileio.h"
+#include "process-util.h"
static bool arg_skip = false;
static bool arg_force = false;
diff --git a/src/random-seed/random-seed.c b/src/random-seed/random-seed.c
index ce1bd195d2..92176b1e9f 100644
--- a/src/random-seed/random-seed.c
+++ b/src/random-seed/random-seed.c
@@ -38,7 +38,7 @@ int main(int argc, char *argv[]) {
ssize_t k;
int r;
FILE *f;
- bool cleanup_seed_file = true;
+ bool refresh_seed_file = true;
if (argc != 2) {
log_error("This program requires one argument.");
@@ -87,33 +87,29 @@ int main(int argc, char *argv[]) {
if (seed_fd < 0) {
seed_fd = open(RANDOM_SEED, O_RDONLY|O_CLOEXEC|O_NOCTTY);
if (seed_fd < 0) {
- log_error_errno(errno, "Failed to open " RANDOM_SEED ": %m");
- r = -errno;
+ r = log_error_errno(errno, "Failed to open " RANDOM_SEED ": %m");
goto finish;
}
- cleanup_seed_file = false;
+
+ refresh_seed_file = false;
}
random_fd = open("/dev/urandom", O_RDWR|O_CLOEXEC|O_NOCTTY, 0600);
if (random_fd < 0) {
random_fd = open("/dev/urandom", O_WRONLY|O_CLOEXEC|O_NOCTTY, 0600);
if (random_fd < 0) {
- log_error_errno(errno, "Failed to open /dev/urandom: %m");
- r = -errno;
+ r = log_error_errno(errno, "Failed to open /dev/urandom: %m");
goto finish;
}
}
k = loop_read(seed_fd, buf, buf_size, false);
- if (k <= 0) {
-
- if (r != 0)
- log_error_errno(errno, "Failed to read seed from " RANDOM_SEED ": %m");
-
- r = k == 0 ? -EIO : (int) k;
-
- } else {
- lseek(seed_fd, 0, SEEK_SET);
+ if (k < 0)
+ r = log_error_errno(k, "Failed to read seed from " RANDOM_SEED ": %m");
+ else if (k == 0)
+ log_debug("Seed file " RANDOM_SEED " not yet initialized, proceeding.");
+ else {
+ (void) lseek(seed_fd, 0, SEEK_SET);
r = loop_write(random_fd, buf, (size_t) k, false);
if (r < 0)
@@ -124,40 +120,44 @@ int main(int argc, char *argv[]) {
seed_fd = open(RANDOM_SEED, O_WRONLY|O_CLOEXEC|O_NOCTTY|O_CREAT, 0600);
if (seed_fd < 0) {
- log_error_errno(errno, "Failed to open " RANDOM_SEED ": %m");
- r = -errno;
+ r = log_error_errno(errno, "Failed to open " RANDOM_SEED ": %m");
goto finish;
}
random_fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC|O_NOCTTY);
if (random_fd < 0) {
- log_error_errno(errno, "Failed to open /dev/urandom: %m");
- r = -errno;
+ r = log_error_errno(errno, "Failed to open /dev/urandom: %m");
goto finish;
}
} else {
- log_error("Unknown verb %s.", argv[1]);
+ log_error("Unknown verb '%s'.", argv[1]);
r = -EINVAL;
goto finish;
}
- if (cleanup_seed_file) {
+ if (refresh_seed_file) {
+
/* This is just a safety measure. Given that we are root and
* most likely created the file ourselves the mode and owner
* should be correct anyway. */
- fchmod(seed_fd, 0600);
- fchown(seed_fd, 0, 0);
+ (void) fchmod(seed_fd, 0600);
+ (void) fchown(seed_fd, 0, 0);
k = loop_read(random_fd, buf, buf_size, false);
- if (k <= 0) {
- log_error("Failed to read new seed from /dev/urandom: %s", r < 0 ? strerror(-r) : "EOF");
- r = k == 0 ? -EIO : (int) k;
- } else {
- r = loop_write(seed_fd, buf, (size_t) k, false);
- if (r < 0)
- log_error_errno(r, "Failed to write new random seed file: %m");
+ if (k < 0) {
+ r = log_error_errno(k, "Failed to read new seed from /dev/urandom: %m");
+ goto finish;
}
+ if (k == 0) {
+ log_error("Got EOF while reading from /dev/urandom.");
+ r = -EIO;
+ goto finish;
+ }
+
+ r = loop_write(seed_fd, buf, (size_t) k, false);
+ if (r < 0)
+ log_error_errno(r, "Failed to write new random seed file: %m");
}
finish:
diff --git a/src/remount-fs/remount-fs.c b/src/remount-fs/remount-fs.c
index 57b47021e4..e701fc9fae 100644
--- a/src/remount-fs/remount-fs.c
+++ b/src/remount-fs/remount-fs.c
@@ -20,7 +20,6 @@
***/
#include <unistd.h>
-#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <sys/stat.h>
@@ -30,7 +29,7 @@
#include "log.h"
#include "util.h"
#include "path-util.h"
-#include "set.h"
+#include "signal-util.h"
#include "mount-setup.h"
#include "exit-status.h"
@@ -96,15 +95,15 @@ int main(int argc, char *argv[]) {
const char *arguments[5];
/* Child */
- arguments[0] = "/bin/mount";
+ arguments[0] = MOUNT_PATH;
arguments[1] = me->mnt_dir;
arguments[2] = "-o";
arguments[3] = "remount";
arguments[4] = NULL;
- execv("/bin/mount", (char **) arguments);
+ execv(MOUNT_PATH, (char **) arguments);
- log_error_errno(errno, "Failed to execute /bin/mount: %m");
+ log_error_errno(errno, "Failed to execute " MOUNT_PATH ": %m");
_exit(EXIT_FAILURE);
}
@@ -144,9 +143,9 @@ int main(int argc, char *argv[]) {
if (s) {
if (!is_clean_exit(si.si_code, si.si_status, NULL)) {
if (si.si_code == CLD_EXITED)
- log_error("/bin/mount for %s exited with exit status %i.", s, si.si_status);
+ log_error(MOUNT_PATH " for %s exited with exit status %i.", s, si.si_status);
else
- log_error("/bin/mount for %s terminated by signal %s.", s, signal_to_string(si.si_status));
+ log_error(MOUNT_PATH " for %s terminated by signal %s.", s, signal_to_string(si.si_status));
ret = EXIT_FAILURE;
}
diff --git a/src/reply-password/reply-password.c b/src/reply-password/reply-password.c
index abf5f525dd..d0d61b98ed 100644
--- a/src/reply-password/reply-password.c
+++ b/src/reply-password/reply-password.c
@@ -20,17 +20,9 @@
***/
#include <sys/socket.h>
-#include <poll.h>
-#include <sys/types.h>
-#include <assert.h>
#include <string.h>
#include <errno.h>
-#include <unistd.h>
-#include <fcntl.h>
#include <sys/un.h>
-#include <sys/stat.h>
-#include <sys/signalfd.h>
-#include <getopt.h>
#include <stddef.h>
#include "log.h"
diff --git a/src/resolve-host/resolve-host.c b/src/resolve-host/resolve-host.c
index 43ecf81ef6..068756cab1 100644
--- a/src/resolve-host/resolve-host.c
+++ b/src/resolve-host/resolve-host.c
@@ -19,14 +19,12 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <arpa/inet.h>
#include <net/if.h>
#include <getopt.h>
#include "sd-bus.h"
#include "bus-util.h"
#include "bus-error.h"
-#include "bus-common-errors.h"
#include "in-addr-util.h"
#include "af-list.h"
#include "build.h"
diff --git a/src/resolve/resolved-bus.c b/src/resolve/resolved-bus.c
index 03c84786db..171141e3a4 100644
--- a/src/resolve/resolved-bus.c
+++ b/src/resolve/resolved-bus.c
@@ -183,7 +183,7 @@ static void bus_method_resolve_hostname_complete(DnsQuery *q) {
}
}
- if (added <= 0) {
+ if (added == 0) {
if (!cname) {
r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_RR, "'%s' does not have any RR of requested type", q->request_hostname);
goto finish;
@@ -220,6 +220,8 @@ static void bus_method_resolve_hostname_complete(DnsQuery *q) {
added++;
}
+ // what about the cache?
+
/* If we didn't find anything, then let's restart the
* query, this time with the cname */
if (added <= 0) {
@@ -273,7 +275,7 @@ static int check_ifindex_flags(int ifindex, uint64_t *flags, sd_bus_error *error
return 0;
}
-static int bus_method_resolve_hostname(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int bus_method_resolve_hostname(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_(dns_question_unrefp) DnsQuestion *question = NULL;
Manager *m = userdata;
const char *hostname;
@@ -282,7 +284,6 @@ static int bus_method_resolve_hostname(sd_bus *bus, sd_bus_message *message, voi
DnsQuery *q;
int r;
- assert(bus);
assert(message);
assert(m);
@@ -338,7 +339,7 @@ static int bus_method_resolve_hostname(sd_bus *bus, sd_bus_message *message, voi
q->request_hostname = hostname;
q->complete = bus_method_resolve_hostname_complete;
- r = dns_query_bus_track(q, bus, message);
+ r = dns_query_bus_track(q, message);
if (r < 0)
return r;
@@ -398,7 +399,7 @@ static void bus_method_resolve_address_complete(DnsQuery *q) {
}
}
- if (added <= 0) {
+ if (added == 0) {
_cleanup_free_ char *ip = NULL;
in_addr_to_string(q->request_family, &q->request_address, &ip);
@@ -426,7 +427,7 @@ finish:
dns_query_free(q);
}
-static int bus_method_resolve_address(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int bus_method_resolve_address(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
_cleanup_(dns_question_unrefp) DnsQuestion *question = NULL;
_cleanup_free_ char *reverse = NULL;
@@ -438,7 +439,6 @@ static int bus_method_resolve_address(sd_bus *bus, sd_bus_message *message, void
size_t sz;
int r;
- assert(bus);
assert(message);
assert(m);
@@ -491,7 +491,7 @@ static int bus_method_resolve_address(sd_bus *bus, sd_bus_message *message, void
memcpy(&q->request_address, d, sz);
q->complete = bus_method_resolve_address_complete;
- r = dns_query_bus_track(q, bus, message);
+ r = dns_query_bus_track(q, message);
if (r < 0)
return r;
@@ -598,7 +598,7 @@ finish:
dns_query_free(q);
}
-static int bus_method_resolve_record(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int bus_method_resolve_record(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
_cleanup_(dns_question_unrefp) DnsQuestion *question = NULL;
Manager *m = userdata;
@@ -608,7 +608,6 @@ static int bus_method_resolve_record(sd_bus *bus, sd_bus_message *message, void
uint64_t flags;
DnsQuery *q;
- assert(bus);
assert(message);
assert(m);
@@ -644,7 +643,7 @@ static int bus_method_resolve_record(sd_bus *bus, sd_bus_message *message, void
q->request_hostname = name;
q->complete = bus_method_resolve_record_complete;
- r = dns_query_bus_track(q, bus, message);
+ r = dns_query_bus_track(q, message);
if (r < 0)
return r;
@@ -681,12 +680,12 @@ static int on_bus_retry(sd_event_source *s, usec_t usec, void *userdata) {
return 0;
}
-static int match_prepare_for_sleep(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *ret_error) {
+static int match_prepare_for_sleep(sd_bus_message *message, void *userdata, sd_bus_error *ret_error) {
Manager *m = userdata;
int b, r;
- assert(bus);
- assert(bus);
+ assert(message);
+ assert(m);
r = sd_bus_message_read(message, "b", &b);
if (r < 0) {
diff --git a/src/resolve/resolved-dns-answer.c b/src/resolve/resolved-dns-answer.c
index 7c4ab18b58..e08eb667cc 100644
--- a/src/resolve/resolved-dns-answer.c
+++ b/src/resolve/resolved-dns-answer.c
@@ -25,8 +25,6 @@
DnsAnswer *dns_answer_new(unsigned n) {
DnsAnswer *a;
- assert(n > 0);
-
a = malloc0(offsetof(DnsAnswer, rrs) + sizeof(DnsResourceRecord*) * n);
if (!a)
return NULL;
diff --git a/src/resolve/resolved-dns-cache.c b/src/resolve/resolved-dns-cache.c
index 33ca4d1a45..be52891681 100644
--- a/src/resolve/resolved-dns-cache.c
+++ b/src/resolve/resolved-dns-cache.c
@@ -91,11 +91,8 @@ void dns_cache_flush(DnsCache *c) {
assert(hashmap_size(c->by_key) == 0);
assert(prioq_size(c->by_expiry) == 0);
- hashmap_free(c->by_key);
- c->by_key = NULL;
-
- prioq_free(c->by_expiry);
- c->by_expiry = NULL;
+ c->by_key = hashmap_free(c->by_key);
+ c->by_expiry = prioq_free(c->by_expiry);
}
static void dns_cache_remove(DnsCache *c, DnsResourceKey *key) {
diff --git a/src/resolve/resolved-dns-cache.h b/src/resolve/resolved-dns-cache.h
index e92280c319..8a9b3d459d 100644
--- a/src/resolve/resolved-dns-cache.h
+++ b/src/resolve/resolved-dns-cache.h
@@ -21,7 +21,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <sys/types.h>
#include "hashmap.h"
#include "prioq.h"
diff --git a/src/resolve/resolved-dns-domain.h b/src/resolve/resolved-dns-domain.h
index 36f27ea031..516d244f7a 100644
--- a/src/resolve/resolved-dns-domain.h
+++ b/src/resolve/resolved-dns-domain.h
@@ -21,7 +21,6 @@
#pragma once
-#include <sys/types.h>
#include "hashmap.h"
#include "in-addr-util.h"
diff --git a/src/resolve/resolved-dns-packet.h b/src/resolve/resolved-dns-packet.h
index 561dd3adfa..c5867386c6 100644
--- a/src/resolve/resolved-dns-packet.h
+++ b/src/resolve/resolved-dns-packet.h
@@ -21,7 +21,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <inttypes.h>
#include "macro.h"
#include "sparse-endian.h"
diff --git a/src/resolve/resolved-dns-query.c b/src/resolve/resolved-dns-query.c
index f0483c9806..418d9721ef 100644
--- a/src/resolve/resolved-dns-query.c
+++ b/src/resolve/resolved-dns-query.c
@@ -19,10 +19,8 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "af-list.h"
#include "resolved-dns-query.h"
-#include "resolved-dns-domain.h"
/* How long to wait for the query in total */
#define QUERY_TIMEOUT_USEC (30 * USEC_PER_SEC)
@@ -469,14 +467,14 @@ static int on_bus_track(sd_bus_track *t, void *userdata) {
return 0;
}
-int dns_query_bus_track(DnsQuery *q, sd_bus *bus, sd_bus_message *m) {
+int dns_query_bus_track(DnsQuery *q, sd_bus_message *m) {
int r;
assert(q);
assert(m);
if (!q->bus_track) {
- r = sd_bus_track_new(bus, &q->bus_track, on_bus_track, q);
+ r = sd_bus_track_new(sd_bus_message_get_bus(m), &q->bus_track, on_bus_track, q);
if (r < 0)
return r;
}
diff --git a/src/resolve/resolved-dns-query.h b/src/resolve/resolved-dns-query.h
index 13b3ee4f81..5a319f0a62 100644
--- a/src/resolve/resolved-dns-query.h
+++ b/src/resolve/resolved-dns-query.h
@@ -21,21 +21,15 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <inttypes.h>
#include "sd-bus.h"
-#include "util.h"
#include "set.h"
typedef struct DnsQuery DnsQuery;
-#include "resolved-dns-scope.h"
-#include "resolved-dns-rr.h"
#include "resolved-dns-question.h"
#include "resolved-dns-answer.h"
#include "resolved-dns-stream.h"
-#include "resolved-dns-transaction.h"
-#include "resolved-manager.h"
struct DnsQuery {
Manager *manager;
@@ -81,6 +75,6 @@ void dns_query_ready(DnsQuery *q);
int dns_query_cname_redirect(DnsQuery *q, const char *name);
-int dns_query_bus_track(DnsQuery *q, sd_bus *bus, sd_bus_message *m);
+int dns_query_bus_track(DnsQuery *q, sd_bus_message *m);
DEFINE_TRIVIAL_CLEANUP_FUNC(DnsQuery*, dns_query_free);
diff --git a/src/resolve/resolved-dns-rr.h b/src/resolve/resolved-dns-rr.h
index 9d9a89d383..26796c842b 100644
--- a/src/resolve/resolved-dns-rr.h
+++ b/src/resolve/resolved-dns-rr.h
@@ -21,10 +21,8 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <inttypes.h>
#include <netinet/in.h>
-#include "util.h"
#include "hashmap.h"
#include "in-addr-util.h"
#include "dns-type.h"
diff --git a/src/resolve/resolved-dns-scope.c b/src/resolve/resolved-dns-scope.c
index abdb2e0732..7369cbf50f 100644
--- a/src/resolve/resolved-dns-scope.c
+++ b/src/resolve/resolved-dns-scope.c
@@ -25,6 +25,8 @@
#include "strv.h"
#include "socket-util.h"
#include "af-list.h"
+#include "random-util.h"
+#include "hostname-util.h"
#include "resolved-dns-domain.h"
#include "resolved-dns-scope.h"
@@ -144,7 +146,7 @@ int dns_scope_emit(DnsScope *s, DnsPacket *p) {
DnsServer *srv;
if (DNS_PACKET_QDCOUNT(p) > 1)
- return -ENOTSUP;
+ return -EOPNOTSUPP;
srv = dns_scope_get_dns_server(s);
if (!srv)
@@ -172,7 +174,7 @@ int dns_scope_emit(DnsScope *s, DnsPacket *p) {
} else if (s->protocol == DNS_PROTOCOL_LLMNR) {
if (DNS_PACKET_QDCOUNT(p) > 1)
- return -ENOTSUP;
+ return -EOPNOTSUPP;
if (!ratelimit_test(&s->ratelimit))
return -EBUSY;
@@ -387,7 +389,7 @@ int dns_scope_llmnr_membership(DnsScope *s, bool b) {
* one. This is necessary on some devices, such as
* veth. */
if (b)
- (void)setsockopt(fd, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreqn, sizeof(mreqn));
+ (void) setsockopt(fd, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreqn, sizeof(mreqn));
if (setsockopt(fd, IPPROTO_IP, b ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP, &mreqn, sizeof(mreqn)) < 0)
return -errno;
@@ -403,7 +405,7 @@ int dns_scope_llmnr_membership(DnsScope *s, bool b) {
return fd;
if (b)
- (void)setsockopt(fd, IPPROTO_IPV6, IPV6_DROP_MEMBERSHIP, &mreq, sizeof(mreq));
+ (void) setsockopt(fd, IPPROTO_IPV6, IPV6_DROP_MEMBERSHIP, &mreq, sizeof(mreq));
if (setsockopt(fd, IPPROTO_IPV6, b ? IPV6_ADD_MEMBERSHIP : IPV6_DROP_MEMBERSHIP, &mreq, sizeof(mreq)) < 0)
return -errno;
diff --git a/src/resolve/resolved-dns-scope.h b/src/resolve/resolved-dns-scope.h
index f05648e5a5..cfbde1343f 100644
--- a/src/resolve/resolved-dns-scope.h
+++ b/src/resolve/resolved-dns-scope.h
@@ -27,11 +27,8 @@ typedef struct DnsScope DnsScope;
#include "resolved-dns-server.h"
#include "resolved-dns-packet.h"
-#include "resolved-dns-query.h"
#include "resolved-dns-cache.h"
#include "resolved-dns-zone.h"
-#include "resolved-dns-stream.h"
-#include "resolved-manager.h"
#include "resolved-link.h"
typedef enum DnsScopeMatch {
diff --git a/src/resolve/resolved-dns-server.c b/src/resolve/resolved-dns-server.c
index caf06fe450..9a62a63258 100644
--- a/src/resolve/resolved-dns-server.c
+++ b/src/resolve/resolved-dns-server.c
@@ -78,23 +78,24 @@ DnsServer* dns_server_free(DnsServer *s) {
if (!s)
return NULL;
- if (s->manager) {
+ if (s->link) {
if (s->type == DNS_SERVER_LINK)
LIST_REMOVE(servers, s->link->dns_servers, s);
- else if (s->type == DNS_SERVER_SYSTEM)
+
+ if (s->link->current_dns_server == s)
+ link_set_dns_server(s->link, NULL);
+ }
+
+ if (s->manager) {
+ if (s->type == DNS_SERVER_SYSTEM)
LIST_REMOVE(servers, s->manager->dns_servers, s);
else if (s->type == DNS_SERVER_FALLBACK)
LIST_REMOVE(servers, s->manager->fallback_dns_servers, s);
- else
- assert_not_reached("Unknown server type");
if (s->manager->current_dns_server == s)
manager_set_dns_server(s->manager, NULL);
}
- if (s->link && s->link->current_dns_server == s)
- link_set_dns_server(s->link, NULL);
-
free(s);
return NULL;
diff --git a/src/resolve/resolved-dns-server.h b/src/resolve/resolved-dns-server.h
index a438a27763..70ff35b08f 100644
--- a/src/resolve/resolved-dns-server.h
+++ b/src/resolve/resolved-dns-server.h
@@ -32,7 +32,6 @@ typedef enum DnsServerType {
DNS_SERVER_LINK,
} DnsServerType;
-#include "resolved-manager.h"
#include "resolved-link.h"
struct DnsServer {
diff --git a/src/resolve/resolved-dns-stream.h b/src/resolve/resolved-dns-stream.h
index 46eae31c60..fb81e9f1ac 100644
--- a/src/resolve/resolved-dns-stream.h
+++ b/src/resolve/resolved-dns-stream.h
@@ -27,7 +27,6 @@ typedef struct DnsStream DnsStream;
#include "resolved-dns-packet.h"
#include "resolved-dns-transaction.h"
-#include "resolved-manager.h"
struct DnsStream {
Manager *manager;
diff --git a/src/resolve/resolved-dns-transaction.c b/src/resolve/resolved-dns-transaction.c
index 74b0634142..214938986d 100644
--- a/src/resolve/resolved-dns-transaction.c
+++ b/src/resolve/resolved-dns-transaction.c
@@ -22,6 +22,7 @@
#include "af-list.h"
#include "resolved-dns-transaction.h"
+#include "random-util.h"
DnsTransaction* dns_transaction_free(DnsTransaction *t) {
DnsQuery *q;
@@ -252,7 +253,7 @@ static int dns_transaction_open_tcp(DnsTransaction *t) {
fd = dns_scope_tcp_socket(t->scope, t->received->family, &t->received->sender, t->received->sender_port);
else {
union in_addr_union address;
- int family;
+ int family = AF_UNSPEC;
/* Otherwise, try to talk to the owner of a
* the IP address, in case this is a reverse
diff --git a/src/resolve/resolved-dns-transaction.h b/src/resolve/resolved-dns-transaction.h
index 182fb7714c..f6d539d315 100644
--- a/src/resolve/resolved-dns-transaction.h
+++ b/src/resolve/resolved-dns-transaction.h
@@ -40,11 +40,9 @@ enum DnsTransactionState {
};
#include "resolved-dns-scope.h"
-#include "resolved-dns-rr.h"
#include "resolved-dns-packet.h"
#include "resolved-dns-question.h"
#include "resolved-dns-answer.h"
-#include "resolved-dns-stream.h"
struct DnsTransaction {
DnsScope *scope;
diff --git a/src/resolve/resolved-link.c b/src/resolve/resolved-link.c
index f94e4bb6f0..27d9129e00 100644
--- a/src/resolve/resolved-link.c
+++ b/src/resolve/resolved-link.c
@@ -68,13 +68,13 @@ Link *link_free(Link *l) {
if (l->manager)
hashmap_remove(l->manager->links, INT_TO_PTR(l->ifindex));
+ while (l->dns_servers)
+ dns_server_free(l->dns_servers);
+
dns_scope_free(l->unicast_scope);
dns_scope_free(l->llmnr_ipv4_scope);
dns_scope_free(l->llmnr_ipv6_scope);
- while (l->dns_servers)
- dns_server_free(l->dns_servers);
-
free(l);
return NULL;
}
diff --git a/src/resolve/resolved-link.h b/src/resolve/resolved-link.h
index 4f0702e872..eee1846108 100644
--- a/src/resolve/resolved-link.h
+++ b/src/resolve/resolved-link.h
@@ -29,8 +29,6 @@
typedef struct Link Link;
typedef struct LinkAddress LinkAddress;
-#include "resolved-dns-server.h"
-#include "resolved-dns-scope.h"
#include "resolved-dns-rr.h"
#include "resolved-manager.h"
diff --git a/src/resolve/resolved-manager.c b/src/resolve/resolved-manager.c
index b5ad701611..52695376f0 100644
--- a/src/resolve/resolved-manager.c
+++ b/src/resolve/resolved-manager.c
@@ -19,22 +19,20 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <arpa/inet.h>
#include <resolv.h>
-#include <net/if.h>
#include <sys/ioctl.h>
#include <poll.h>
#include <netinet/in.h>
#include "rtnl-util.h"
-#include "event-util.h"
-#include "network-util.h"
#include "network-internal.h"
-#include "conf-parser.h"
#include "socket-util.h"
#include "af-list.h"
#include "utf8.h"
#include "fileio-label.h"
+#include "ordered-set.h"
+#include "random-util.h"
+#include "hostname-util.h"
#include "resolved-dns-domain.h"
#include "resolved-conf.h"
@@ -536,11 +534,11 @@ Manager *manager_free(Manager *m) {
while (m->dns_queries)
dns_query_free(m->dns_queries);
- dns_scope_free(m->unicast_scope);
-
manager_flush_dns_servers(m, DNS_SERVER_SYSTEM);
manager_flush_dns_servers(m, DNS_SERVER_FALLBACK);
+ dns_scope_free(m->unicast_scope);
+
hashmap_free(m->links);
hashmap_free(m->dns_transactions);
@@ -707,8 +705,11 @@ static void write_resolv_conf_server(DnsServer *s, FILE *f, unsigned *count) {
(*count) ++;
}
-static void write_resolv_conf_search(const char *domain, FILE *f,
- unsigned *count, unsigned *length) {
+static void write_resolv_conf_search(
+ const char *domain, FILE *f,
+ unsigned *count,
+ unsigned *length) {
+
assert(domain);
assert(f);
assert(length);
@@ -729,7 +730,7 @@ static void write_resolv_conf_search(const char *domain, FILE *f,
(*count) ++;
}
-static int write_resolv_conf_contents(FILE *f, Set *dns, Set *domains) {
+static int write_resolv_conf_contents(FILE *f, OrderedSet *dns, OrderedSet *domains) {
Iterator i;
fputs("# This file is managed by systemd-resolved(8). Do not edit.\n#\n"
@@ -738,22 +739,22 @@ static int write_resolv_conf_contents(FILE *f, Set *dns, Set *domains) {
"# resolv.conf(5) in a different way, replace the symlink by a\n"
"# static file or a different symlink.\n\n", f);
- if (set_isempty(dns))
+ if (ordered_set_isempty(dns))
fputs("# No DNS servers known.\n", f);
else {
DnsServer *s;
unsigned count = 0;
- SET_FOREACH(s, dns, i)
+ ORDERED_SET_FOREACH(s, dns, i)
write_resolv_conf_server(s, f, &count);
}
- if (!set_isempty(domains)) {
+ if (!ordered_set_isempty(domains)) {
unsigned length = 0, count = 0;
char *domain;
fputs("search", f);
- SET_FOREACH(domain, domains, i)
+ ORDERED_SET_FOREACH(domain, domains, i)
write_resolv_conf_search(domain, f, &count, &length);
fputs("\n", f);
}
@@ -761,12 +762,11 @@ static int write_resolv_conf_contents(FILE *f, Set *dns, Set *domains) {
return fflush_and_check(f);
}
-
int manager_write_resolv_conf(Manager *m) {
static const char path[] = "/run/systemd/resolve/resolv.conf";
_cleanup_free_ char *temp_path = NULL;
_cleanup_fclose_ FILE *f = NULL;
- _cleanup_set_free_ Set *dns = NULL, *domains = NULL;
+ _cleanup_ordered_set_free_ OrderedSet *dns = NULL, *domains = NULL;
DnsServer *s;
Iterator i;
Link *l;
@@ -778,17 +778,17 @@ int manager_write_resolv_conf(Manager *m) {
manager_read_resolv_conf(m);
/* Add the full list to a set, to filter out duplicates */
- dns = set_new(&dns_server_hash_ops);
+ dns = ordered_set_new(&dns_server_hash_ops);
if (!dns)
return -ENOMEM;
- domains = set_new(&dns_name_hash_ops);
+ domains = ordered_set_new(&dns_name_hash_ops);
if (!domains)
return -ENOMEM;
/* First add the system-wide servers */
LIST_FOREACH(servers, s, m->dns_servers) {
- r = set_put(dns, s);
+ r = ordered_set_put(dns, s);
if (r == -EEXIST)
continue;
if (r < 0)
@@ -800,7 +800,7 @@ int manager_write_resolv_conf(Manager *m) {
char **domain;
LIST_FOREACH(servers, s, l->dns_servers) {
- r = set_put(dns, s);
+ r = ordered_set_put(dns, s);
if (r == -EEXIST)
continue;
if (r < 0)
@@ -811,7 +811,7 @@ int manager_write_resolv_conf(Manager *m) {
continue;
STRV_FOREACH(domain, l->unicast_scope->domains) {
- r = set_put(domains, *domain);
+ r = ordered_set_put(domains, *domain);
if (r == -EEXIST)
continue;
if (r < 0)
@@ -820,9 +820,9 @@ int manager_write_resolv_conf(Manager *m) {
}
/* If we found nothing, add the fallback servers */
- if (set_isempty(dns)) {
+ if (ordered_set_isempty(dns)) {
LIST_FOREACH(servers, s, m->fallback_dns_servers) {
- r = set_put(dns, s);
+ r = ordered_set_put(dns, s);
if (r == -EEXIST)
continue;
if (r < 0)
@@ -848,8 +848,8 @@ int manager_write_resolv_conf(Manager *m) {
return 0;
fail:
- unlink(path);
- unlink(temp_path);
+ (void) unlink(path);
+ (void) unlink(temp_path);
return r;
}
diff --git a/src/resolve/resolved-manager.h b/src/resolve/resolved-manager.h
index 1151029d29..5a581cc13a 100644
--- a/src/resolve/resolved-manager.h
+++ b/src/resolve/resolved-manager.h
@@ -24,9 +24,7 @@
#include "sd-event.h"
#include "sd-network.h"
#include "sd-rtnl.h"
-#include "util.h"
#include "list.h"
-#include "in-addr-util.h"
#include "hashmap.h"
typedef struct Manager Manager;
@@ -41,8 +39,6 @@ enum Support {
};
#include "resolved-dns-query.h"
-#include "resolved-dns-server.h"
-#include "resolved-dns-scope.h"
#include "resolved-dns-stream.h"
#include "resolved-link.h"
diff --git a/src/resolve/resolved.c b/src/resolve/resolved.c
index ce15a8011d..e283d8a749 100644
--- a/src/resolve/resolved.c
+++ b/src/resolve/resolved.c
@@ -22,9 +22,9 @@
#include "sd-event.h"
#include "sd-daemon.h"
#include "mkdir.h"
-#include "label.h"
#include "capability.h"
#include "selinux-util.h"
+#include "signal-util.h"
#include "resolved-manager.h"
#include "resolved-conf.h"
diff --git a/src/resolve/resolved.conf.in b/src/resolve/resolved.conf.in
index e5a19ee474..3eb19e42b7 100644
--- a/src/resolve/resolved.conf.in
+++ b/src/resolve/resolved.conf.in
@@ -5,8 +5,9 @@
# the Free Software Foundation; either version 2.1 of the License, or
# (at your option) any later version.
#
-# You can override the directives in this file by creating files in
-# /etc/systemd/resolved.conf.d/*.conf.
+# Entries in this file show the compile time defaults.
+# You can change settings by editing this file.
+# Defaults can be restored by simply deleting this file.
#
# See resolved.conf(5) for details
diff --git a/src/resolve/test-dns-domain.c b/src/resolve/test-dns-domain.c
index ebc8d98fce..c3208abc78 100644
--- a/src/resolve/test-dns-domain.c
+++ b/src/resolve/test-dns-domain.c
@@ -19,7 +19,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "log.h"
#include "macro.h"
#include "resolved-dns-domain.h"
@@ -162,7 +161,7 @@ static void test_dns_name_single_label(void) {
static void test_dns_name_reverse_one(const char *address, const char *name) {
_cleanup_free_ char *p = NULL;
- union in_addr_union a, b;
+ union in_addr_union a, b = {};
int familya, familyb;
assert_se(in_addr_from_string_auto(address, &familya, &a) >= 0);
diff --git a/src/run/run.c b/src/run/run.c
index 0661b3bee9..5b9f31c4aa 100644
--- a/src/run/run.c
+++ b/src/run/run.c
@@ -34,9 +34,12 @@
#include "bus-error.h"
#include "calendarspec.h"
#include "ptyfwd.h"
+#include "formats-util.h"
+#include "signal-util.h"
static bool arg_scope = false;
static bool arg_remain_after_exit = false;
+static bool arg_no_block = false;
static const char *arg_unit = NULL;
static const char *arg_description = NULL;
static const char *arg_slice = NULL;
@@ -76,6 +79,7 @@ static void help(void) {
" -p --property=NAME=VALUE Set unit property\n"
" --description=TEXT Description for unit\n"
" --slice=SLICE Run in the specified slice\n"
+ " --no-block Do not wait until operation finished\n"
" -r --remain-after-exit Leave service around until explicitly stopped\n"
" --send-sighup Send SIGHUP when terminating\n"
" --service-type=TYPE Service type\n"
@@ -123,7 +127,8 @@ static int parse_argv(int argc, char *argv[]) {
ARG_ON_UNIT_ACTIVE,
ARG_ON_UNIT_INACTIVE,
ARG_ON_CALENDAR,
- ARG_TIMER_PROPERTY
+ ARG_TIMER_PROPERTY,
+ ARG_NO_BLOCK,
};
static const struct option options[] = {
@@ -154,6 +159,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "on-unit-inactive", required_argument, NULL, ARG_ON_UNIT_INACTIVE },
{ "on-calendar", required_argument, NULL, ARG_ON_CALENDAR },
{ "timer-property", required_argument, NULL, ARG_TIMER_PROPERTY },
+ { "no-block", no_argument, NULL, ARG_NO_BLOCK },
{},
};
@@ -241,14 +247,12 @@ static int parse_argv(int argc, char *argv[]) {
break;
case ARG_SETENV:
-
if (strv_extend(&arg_environment, optarg) < 0)
return log_oom();
break;
case 'p':
-
if (strv_extend(&arg_property, optarg) < 0)
return log_oom();
@@ -330,6 +334,10 @@ static int parse_argv(int argc, char *argv[]) {
break;
+ case ARG_NO_BLOCK:
+ arg_no_block = true;
+ break;
+
case '?':
return -EINVAL;
@@ -389,11 +397,8 @@ static int transient_unit_set_properties(sd_bus_message *m, char **properties) {
return r;
r = bus_append_unit_property_assignment(m, *i);
- if (r < 0) {
- r = sd_bus_message_append(m, "sv", 0);
- if (r < 0)
- return r;
- }
+ if (r < 0)
+ return r;
r = sd_bus_message_close_container(m);
if (r < 0)
@@ -410,9 +415,9 @@ static int transient_cgroup_set_properties(sd_bus_message *m) {
if (!isempty(arg_slice)) {
_cleanup_free_ char *slice;
- slice = unit_name_mangle_with_suffix(arg_slice, MANGLE_NOGLOB, ".slice");
- if (!slice)
- return -ENOMEM;
+ r = unit_name_mangle_with_suffix(arg_slice, UNIT_NAME_NOGLOB, ".slice", &slice);
+ if (r < 0)
+ return r;
r = sd_bus_message_append(m, "(sv)", "Slice", "s", slice);
if (r < 0)
@@ -655,8 +660,9 @@ static int start_transient_service(
sd_bus *bus,
char **argv) {
+ _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
_cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL;
_cleanup_free_ char *service = NULL, *pty_path = NULL;
_cleanup_close_ int master = -1;
int r;
@@ -677,7 +683,6 @@ static int start_transient_service(
} else if (arg_transport == BUS_TRANSPORT_MACHINE) {
_cleanup_bus_unref_ sd_bus *system_bus = NULL;
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
const char *s;
r = sd_bus_open_system(&system_bus);
@@ -701,6 +706,8 @@ static int start_transient_service(
if (r < 0)
return bus_log_parse_error(r);
+ reply = sd_bus_message_unref(reply);
+
master = fcntl(master, F_DUPFD_CLOEXEC, 3);
if (master < 0)
return log_error_errno(errno, "Failed to duplicate master fd: %m");
@@ -715,10 +722,16 @@ static int start_transient_service(
return log_error_errno(errno, "Failed to unlock tty: %m");
}
+ if (!arg_no_block) {
+ r = bus_wait_for_jobs_new(bus, &w);
+ if (r < 0)
+ return log_error_errno(r, "Could not watch jobs: %m");
+ }
+
if (arg_unit) {
- service = unit_name_mangle_with_suffix(arg_unit, MANGLE_NOGLOB, ".service");
- if (!service)
- return log_oom();
+ r = unit_name_mangle_with_suffix(arg_unit, UNIT_NAME_NOGLOB, ".service", &service);
+ if (r < 0)
+ return log_error_errno(r, "Failed to mangle unit name: %m");
} else if (asprintf(&service, "run-"PID_FMT".service", getpid()) < 0)
return log_oom();
@@ -755,12 +768,24 @@ static int start_transient_service(
if (r < 0)
return bus_log_create_error(r);
- r = sd_bus_call(bus, m, 0, &error, NULL);
+ r = sd_bus_call(bus, m, 0, &error, &reply);
if (r < 0) {
log_error("Failed to start transient service unit: %s", bus_error_message(&error, -r));
return r;
}
+ if (w) {
+ const char *object;
+
+ r = sd_bus_message_read(reply, "o", &object);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ r = bus_wait_for_jobs_one(w, object, arg_quiet);
+ if (r < 0)
+ return r;
+ }
+
if (master >= 0) {
_cleanup_(pty_forward_freep) PTYForward *forward = NULL;
_cleanup_event_unref_ sd_event *event = NULL;
@@ -781,7 +806,7 @@ static int start_transient_service(
if (!arg_quiet)
log_info("Running as unit %s.\nPress ^] three times within 1s to disconnect TTY.", service);
- r = pty_forward_new(event, master, false, &forward);
+ r = pty_forward_new(event, master, false, false, &forward);
if (r < 0)
return log_error_errno(r, "Failed to create PTY forwarder: %m");
@@ -807,18 +832,24 @@ static int start_transient_scope(
char **argv) {
_cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
+ _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL;
_cleanup_strv_free_ char **env = NULL, **user_env = NULL;
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
_cleanup_free_ char *scope = NULL;
+ const char *object = NULL;
int r;
assert(bus);
assert(argv);
+ r = bus_wait_for_jobs_new(bus, &w);
+ if (r < 0)
+ return log_oom();
+
if (arg_unit) {
- scope = unit_name_mangle_with_suffix(arg_unit, MANGLE_NOGLOB, ".scope");
- if (!scope)
- return log_oom();
+ r = unit_name_mangle_with_suffix(arg_unit, UNIT_NAME_NOGLOB, ".scope", &scope);
+ if (r < 0)
+ return log_error_errno(r, "Failed to mangle scope name: %m");
} else if (asprintf(&scope, "run-"PID_FMT".scope", getpid()) < 0)
return log_oom();
@@ -855,7 +886,7 @@ static int start_transient_scope(
if (r < 0)
return bus_log_create_error(r);
- r = sd_bus_call(bus, m, 0, &error, NULL);
+ r = sd_bus_call(bus, m, 0, &error, &reply);
if (r < 0) {
log_error("Failed to start transient scope unit: %s", bus_error_message(&error, -r));
return r;
@@ -915,8 +946,16 @@ static int start_transient_scope(
if (!env)
return log_oom();
+ r = sd_bus_message_read(reply, "o", &object);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ r = bus_wait_for_jobs_one(w, object, arg_quiet);
+ if (r < 0)
+ return r;
+
if (!arg_quiet)
- log_info("Running as unit %s.", scope);
+ log_info("Running scope as unit %s.", scope);
execvpe(argv[0], argv, env);
@@ -928,24 +967,30 @@ static int start_transient_timer(
char **argv) {
_cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
+ _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL;
_cleanup_free_ char *timer = NULL, *service = NULL;
+ const char *object = NULL;
int r;
assert(bus);
assert(argv);
+ r = bus_wait_for_jobs_new(bus, &w);
+ if (r < 0)
+ return log_oom();
+
if (arg_unit) {
- switch(unit_name_to_type(arg_unit)) {
+ switch (unit_name_to_type(arg_unit)) {
case UNIT_SERVICE:
service = strdup(arg_unit);
if (!service)
return log_oom();
- timer = unit_name_change_suffix(service, ".timer");
- if (!timer)
- return log_oom();
+ r = unit_name_change_suffix(service, ".timer", &timer);
+ if (r < 0)
+ return log_error_errno(r, "Failed to change unit suffix: %m");
break;
case UNIT_TIMER:
@@ -953,19 +998,19 @@ static int start_transient_timer(
if (!timer)
return log_oom();
- service = unit_name_change_suffix(timer, ".service");
- if (!service)
- return log_oom();
+ r = unit_name_change_suffix(timer, ".service", &service);
+ if (r < 0)
+ return log_error_errno(r, "Failed to change unit suffix: %m");
break;
default:
- service = unit_name_mangle_with_suffix(arg_unit, MANGLE_NOGLOB, ".service");
- if (!service)
- return log_oom();
+ r = unit_name_mangle_with_suffix(arg_unit, UNIT_NAME_NOGLOB, ".service", &service);
+ if (r < 0)
+ return log_error_errno(r, "Failed to mangle unit name: %m");
- timer = unit_name_mangle_with_suffix(arg_unit, MANGLE_NOGLOB, ".timer");
- if (!timer)
- return log_oom();
+ r = unit_name_mangle_with_suffix(arg_unit, UNIT_NAME_NOGLOB, ".timer", &timer);
+ if (r < 0)
+ return log_error_errno(r, "Failed to mangle unit name: %m");
break;
}
@@ -1035,15 +1080,23 @@ static int start_transient_timer(
if (r < 0)
return bus_log_create_error(r);
- r = sd_bus_call(bus, m, 0, &error, NULL);
+ r = sd_bus_call(bus, m, 0, &error, &reply);
if (r < 0) {
log_error("Failed to start transient timer unit: %s", bus_error_message(&error, -r));
return r;
}
- log_info("Running as unit %s.", timer);
+ r = sd_bus_message_read(reply, "o", &object);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ r = bus_wait_for_jobs_one(w, object, arg_quiet);
+ if (r < 0)
+ return r;
+
+ log_info("Running timer as unit %s.", timer);
if (argv[0])
- log_info("Will run as unit %s.", service);
+ log_info("Will run service as unit %s.", service);
return 0;
}
diff --git a/src/shared/acl-util.c b/src/shared/acl-util.c
index a4ff1ab878..466f9aa601 100644
--- a/src/shared/acl-util.c
+++ b/src/shared/acl-util.c
@@ -19,7 +19,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <assert.h>
#include <errno.h>
#include <stdbool.h>
@@ -82,17 +81,18 @@ int calc_acl_mask_if_needed(acl_t *acl_p) {
if (tag == ACL_MASK)
return 0;
- if (IN_SET(tag, ACL_USER, ACL_GROUP))
- goto calc;
+
+ if (IN_SET(tag, ACL_USER, ACL_GROUP)) {
+ if (acl_calc_mask(acl_p) < 0)
+ return -errno;
+
+ return 1;
+ }
}
if (r < 0)
return -errno;
- return 0;
-calc:
- if (acl_calc_mask(acl_p) < 0)
- return -errno;
- return 1;
+ return 0;
}
int add_base_acls_if_needed(acl_t *acl_p, const char *path) {
@@ -159,59 +159,68 @@ int add_base_acls_if_needed(acl_t *acl_p, const char *path) {
return 0;
}
-int search_acl_groups(char*** dst, const char* path, bool* belong) {
- acl_t acl;
+int acl_search_groups(const char *path, char ***ret_groups) {
+ _cleanup_strv_free_ char **g = NULL;
+ _cleanup_(acl_free) acl_t acl = NULL;
+ bool ret = false;
+ acl_entry_t entry;
+ int r;
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;
+ if (!acl)
+ return -errno;
- r = acl_get_tag_type(entry, &tag);
- if (r < 0)
- break;
+ r = acl_get_entry(acl, ACL_FIRST_ENTRY, &entry);
+ for (;;) {
+ _cleanup_(acl_free_gid_tpp) gid_t *gid = NULL;
+ acl_tag_t tag;
+
+ if (r < 0)
+ return -errno;
+ if (r == 0)
+ break;
- if (tag != ACL_GROUP)
- goto next;
+ if (acl_get_tag_type(entry, &tag) < 0)
+ return -errno;
- gid = acl_get_qualifier(entry);
- if (!gid)
- break;
+ if (tag != ACL_GROUP)
+ goto next;
- if (in_gid(*gid) > 0) {
- *belong = true;
- break;
- }
+ gid = acl_get_qualifier(entry);
+ if (!gid)
+ return -errno;
+
+ if (in_gid(*gid) > 0) {
+ if (!ret_groups)
+ return true;
+
+ ret = true;
+ }
+
+ if (ret_groups) {
+ char *name;
name = gid_to_name(*gid);
- if (!name) {
- acl_free(acl);
- return log_oom();
- }
-
- r = strv_consume(dst, name);
- if (r < 0) {
- acl_free(acl);
- return log_oom();
- }
-
- next:
- r = acl_get_entry(acl, ACL_NEXT_ENTRY, &entry);
+ if (!name)
+ return -ENOMEM;
+
+ r = strv_consume(&g, name);
+ if (r < 0)
+ return r;
}
- acl_free(acl);
+ next:
+ r = acl_get_entry(acl, ACL_NEXT_ENTRY, &entry);
}
- return 0;
+ if (ret_groups) {
+ *ret_groups = g;
+ g = NULL;
+ }
+
+ return ret;
}
int parse_acl(char *text, acl_t *acl_access, acl_t *acl_default, bool want_mask) {
@@ -282,6 +291,77 @@ int parse_acl(char *text, acl_t *acl_access, acl_t *acl_default, bool want_mask)
return 0;
}
+static int acl_entry_equal(acl_entry_t a, acl_entry_t b) {
+ acl_tag_t tag_a, tag_b;
+
+ if (acl_get_tag_type(a, &tag_a) < 0)
+ return -errno;
+
+ if (acl_get_tag_type(b, &tag_b) < 0)
+ return -errno;
+
+ if (tag_a != tag_b)
+ return false;
+
+ switch (tag_a) {
+ case ACL_USER_OBJ:
+ case ACL_GROUP_OBJ:
+ case ACL_MASK:
+ case ACL_OTHER:
+ /* can have only one of those */
+ return true;
+ case ACL_USER: {
+ _cleanup_(acl_free_uid_tpp) uid_t *uid_a = NULL, *uid_b = NULL;
+
+ uid_a = acl_get_qualifier(a);
+ if (!uid_a)
+ return -errno;
+
+ uid_b = acl_get_qualifier(b);
+ if (!uid_b)
+ return -errno;
+
+ return *uid_a == *uid_b;
+ }
+ case ACL_GROUP: {
+ _cleanup_(acl_free_gid_tpp) gid_t *gid_a = NULL, *gid_b = NULL;
+
+ gid_a = acl_get_qualifier(a);
+ if (!gid_a)
+ return -errno;
+
+ gid_b = acl_get_qualifier(b);
+ if (!gid_b)
+ return -errno;
+
+ return *gid_a == *gid_b;
+ }
+ default:
+ assert_not_reached("Unknown acl tag type");
+ }
+}
+
+static int find_acl_entry(acl_t acl, acl_entry_t entry, acl_entry_t *out) {
+ acl_entry_t i;
+ int r;
+
+ for (r = acl_get_entry(acl, ACL_FIRST_ENTRY, &i);
+ r > 0;
+ r = acl_get_entry(acl, ACL_NEXT_ENTRY, &i)) {
+
+ r = acl_entry_equal(i, entry);
+ if (r < 0)
+ return r;
+ if (r > 0) {
+ *out = i;
+ return 1;
+ }
+ }
+ if (r < 0)
+ return -errno;
+ return 0;
+}
+
int acls_for_file(const char *path, acl_type_t type, acl_t new, acl_t *acl) {
_cleanup_(acl_freep) acl_t old;
acl_entry_t i;
@@ -297,8 +377,12 @@ int acls_for_file(const char *path, acl_type_t type, acl_t new, acl_t *acl) {
acl_entry_t j;
- if (acl_create_entry(&old, &j) < 0)
- return -errno;
+ r = find_acl_entry(old, i, &j);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ if (acl_create_entry(&old, &j) < 0)
+ return -errno;
if (acl_copy_entry(j, i) < 0)
return -errno;
diff --git a/src/shared/acl-util.h b/src/shared/acl-util.h
index 90e88ffa26..c8bcc266d0 100644
--- a/src/shared/acl-util.h
+++ b/src/shared/acl-util.h
@@ -32,7 +32,7 @@
int acl_find_uid(acl_t acl, uid_t uid, acl_entry_t *entry);
int calc_acl_mask_if_needed(acl_t *acl_p);
int add_base_acls_if_needed(acl_t *acl_p, const char *path);
-int search_acl_groups(char*** dst, const char* path, bool* belong);
+int acl_search_groups(const char* path, char ***ret_groups);
int parse_acl(char *text, acl_t *acl_access, acl_t *acl_default, bool want_mask);
int acls_for_file(const char *path, acl_type_t type, acl_t new, acl_t *acl);
@@ -41,5 +41,9 @@ int acls_for_file(const char *path, acl_type_t type, acl_t new, acl_t *acl);
DEFINE_TRIVIAL_CLEANUP_FUNC(acl_t, acl_free);
#define acl_free_charp acl_free
DEFINE_TRIVIAL_CLEANUP_FUNC(char*, acl_free_charp);
+#define acl_free_uid_tp acl_free
+DEFINE_TRIVIAL_CLEANUP_FUNC(uid_t*, acl_free_uid_tp);
+#define acl_free_gid_tp acl_free
+DEFINE_TRIVIAL_CLEANUP_FUNC(gid_t*, acl_free_gid_tp);
#endif
diff --git a/src/shared/acpi-fpdt.c b/src/shared/acpi-fpdt.c
index 390c3236e0..64e50401b9 100644
--- a/src/shared/acpi-fpdt.c
+++ b/src/shared/acpi-fpdt.c
@@ -19,13 +19,11 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
-#include <sys/types.h>
#include <util.h>
#include <fileio.h>
diff --git a/src/shared/apparmor-util.c b/src/shared/apparmor-util.c
index c14843da49..c2bbd330bd 100644
--- a/src/shared/apparmor-util.c
+++ b/src/shared/apparmor-util.c
@@ -19,7 +19,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <unistd.h>
#include "util.h"
#include "fileio.h"
diff --git a/src/shared/architecture.c b/src/shared/architecture.c
index 34c5a53fa9..8e72e7a36a 100644
--- a/src/shared/architecture.c
+++ b/src/shared/architecture.c
@@ -35,7 +35,7 @@ int uname_architecture(void) {
* 1:1. Instead we try to clean it up and break down the
* confusion on x86 and arm in particular.
*
- * We do not try to distuingish CPUs not CPU features, but
+ * We do not try to distinguish CPUs not CPU features, but
* actual architectures, i.e. that have genuinely different
* code. */
@@ -108,8 +108,12 @@ int uname_architecture(void) {
{ "armv8l", ARCHITECTURE_ARM },
{ "armv8b", ARCHITECTURE_ARM_BE },
#elif defined(__sh__) || defined(__sh64__)
- { "sh64", ARCHITECTURE_SH64 },
- { "sh", ARCHITECTURE_SH },
+ { "sh5", ARCHITECTURE_SH64 },
+ { "sh2", ARCHITECTURE_SH },
+ { "sh2a", ARCHITECTURE_SH },
+ { "sh3", ARCHITECTURE_SH },
+ { "sh4", ARCHITECTURE_SH },
+ { "sh4a", ARCHITECTURE_SH },
#elif defined(__m68k__)
{ "m68k", ARCHITECTURE_M68K },
#elif defined(__tilegx__)
diff --git a/src/shared/architecture.h b/src/shared/architecture.h
index cb82418a5e..f5bbf65a90 100644
--- a/src/shared/architecture.h
+++ b/src/shared/architecture.h
@@ -27,7 +27,7 @@
/* A cleaned up architecture definition. We don't want to get lost in
* processor features, models, generations or even ABIs. Hence we
- * focus on general family, and distuignish word width and
+ * focus on general family, and distinguish word width and
* endianness. */
enum {
diff --git a/src/shared/arphrd-list.c b/src/shared/arphrd-list.c
index 6e113eff7a..284043cd90 100644
--- a/src/shared/arphrd-list.c
+++ b/src/shared/arphrd-list.c
@@ -20,7 +20,6 @@
***/
#include <net/if_arp.h>
-#include <sys/socket.h>
#include <string.h>
#include "util.h"
diff --git a/src/shared/ask-password-api.c b/src/shared/ask-password-api.c
index 0a61dafc59..ef3788be68 100644
--- a/src/shared/ask-password-api.c
+++ b/src/shared/ask-password-api.c
@@ -32,9 +32,12 @@
#include <sys/signalfd.h>
#include "util.h"
+#include "formats-util.h"
#include "mkdir.h"
#include "strv.h"
-
+#include "random-util.h"
+#include "terminal-util.h"
+#include "signal-util.h"
#include "ask-password-api.h"
static void backspace_chars(int ttyfd, size_t p) {
@@ -475,6 +478,8 @@ int ask_password_agent(
goto finish;
}
+ cmsg_close_all(&msghdr);
+
if (n <= 0) {
log_error("Message too short");
continue;
diff --git a/src/shared/ask-password-api.h b/src/shared/ask-password-api.h
index 704ee6e1b4..0954e072be 100644
--- a/src/shared/ask-password-api.h
+++ b/src/shared/ask-password-api.h
@@ -21,7 +21,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "util.h"
int ask_password_tty(const char *message, usec_t until, bool echo, const char *flag_file, char **_passphrase);
diff --git a/src/shared/audit.c b/src/shared/audit.c
index 4701c0a8de..54148fcf18 100644
--- a/src/shared/audit.c
+++ b/src/shared/audit.c
@@ -19,20 +19,14 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <assert.h>
-#include <string.h>
-#include <unistd.h>
#include <errno.h>
-#include <stdlib.h>
#include <stdio.h>
-#include <ctype.h>
#include "macro.h"
#include "audit.h"
#include "util.h"
-#include "log.h"
+#include "process-util.h"
#include "fileio.h"
-#include "virt.h"
int audit_session_from_pid(pid_t pid, uint32_t *id) {
_cleanup_free_ char *s = NULL;
@@ -52,7 +46,7 @@ int audit_session_from_pid(pid_t pid, uint32_t *id) {
if (r < 0)
return r;
- if (u == (uint32_t) -1 || u <= 0)
+ if (u == AUDIT_SESSION_INVALID || u <= 0)
return -ENXIO;
*id = u;
diff --git a/src/shared/audit.h b/src/shared/audit.h
index b4aecffb30..6de331c73e 100644
--- a/src/shared/audit.h
+++ b/src/shared/audit.h
@@ -21,9 +21,11 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <stdint.h>
+#include <stdbool.h>
#include <sys/types.h>
-#include "capability.h"
+#define AUDIT_SESSION_INVALID ((uint32_t) -1)
int audit_session_from_pid(pid_t pid, uint32_t *id);
int audit_loginuid_from_pid(pid_t pid, uid_t *uid);
diff --git a/src/shared/barrier.c b/src/shared/barrier.c
index f65363a67b..436ba95989 100644
--- a/src/shared/barrier.c
+++ b/src/shared/barrier.c
@@ -21,13 +21,10 @@
#include <errno.h>
#include <fcntl.h>
-#include <limits.h>
#include <poll.h>
#include <stdbool.h>
#include <stdint.h>
-#include <stdio.h>
#include <stdlib.h>
-#include <string.h>
#include <sys/eventfd.h>
#include <sys/types.h>
#include <unistd.h>
@@ -141,7 +138,7 @@ int barrier_create(Barrier *b) {
* barrier_create(). The object is released and reset to invalid
* state. Therefore, it is safe to call barrier_destroy() multiple
* times or even if barrier_create() failed. However, barrier must be
- * always initalized with BARRIER_NULL.
+ * always initialized with BARRIER_NULL.
*
* If @b is NULL, this is a no-op.
*/
@@ -178,7 +175,7 @@ void barrier_set_role(Barrier *b, unsigned int role) {
assert(b);
assert(role == BARRIER_PARENT || role == BARRIER_CHILD);
/* make sure this is only called once */
- assert(b->pipe[1] >= 0 && b->pipe[1] >= 0);
+ assert(b->pipe[0] >= 0 && b->pipe[1] >= 0);
if (role == BARRIER_PARENT)
b->pipe[1] = safe_close(b->pipe[1]);
diff --git a/src/shared/barrier.h b/src/shared/barrier.h
index d4ad2a419b..b8954694d3 100644
--- a/src/shared/barrier.h
+++ b/src/shared/barrier.h
@@ -21,14 +21,9 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <errno.h>
-#include <inttypes.h>
-#include <stdlib.h>
-#include <string.h>
#include <sys/types.h>
#include "macro.h"
-#include "util.h"
/* See source file for an API description. */
@@ -91,6 +86,6 @@ static inline bool barrier_is_aborted(Barrier *b) {
}
static inline bool barrier_place_and_sync(Barrier *b) {
- (void)barrier_place(b);
+ (void) barrier_place(b);
return barrier_sync(b);
}
diff --git a/src/shared/base-filesystem.c b/src/shared/base-filesystem.c
index 73907c6354..ab6fc171b0 100644
--- a/src/shared/base-filesystem.c
+++ b/src/shared/base-filesystem.c
@@ -22,17 +22,12 @@
#include <errno.h>
#include <sys/stat.h>
#include <stdlib.h>
-#include <string.h>
-#include <assert.h>
#include <unistd.h>
#include "base-filesystem.h"
#include "log.h"
#include "macro.h"
-#include "strv.h"
#include "util.h"
-#include "label.h"
-#include "mkdir.h"
typedef struct BaseFilesystem {
const char *dir;
@@ -46,16 +41,19 @@ static const BaseFilesystem table[] = {
{ "lib", 0, "usr/lib\0", NULL },
{ "root", 0755, NULL, NULL },
{ "sbin", 0, "usr/sbin\0", NULL },
+ { "usr", 0755, NULL, NULL },
+ { "var", 0755, NULL, NULL },
+ { "etc", 0755, NULL, NULL },
#if defined(__i386__) || defined(__x86_64__)
{ "lib64", 0, "usr/lib/x86_64-linux-gnu\0"
"usr/lib64\0", "ld-linux-x86-64.so.2" },
#endif
};
-int base_filesystem_create(const char *root) {
+int base_filesystem_create(const char *root, uid_t uid, gid_t gid) {
_cleanup_close_ int fd = -1;
unsigned i;
- int r;
+ int r = 0;
fd = open(root, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
if (fd < 0)
@@ -95,6 +93,12 @@ int base_filesystem_create(const char *root) {
r = symlinkat(target, fd, table[i].dir);
if (r < 0 && errno != EEXIST)
return log_error_errno(errno, "Failed to create symlink at %s/%s: %m", root, table[i].dir);
+
+ if (uid != UID_INVALID || gid != UID_INVALID) {
+ if (fchownat(fd, table[i].dir, uid, gid, AT_SYMLINK_NOFOLLOW) < 0)
+ return log_error_errno(errno, "Failed to chown symlink at %s/%s: %m", root, table[i].dir);
+ }
+
continue;
}
@@ -102,6 +106,11 @@ int base_filesystem_create(const char *root) {
r = mkdirat(fd, table[i].dir, table[i].mode);
if (r < 0 && errno != EEXIST)
return log_error_errno(errno, "Failed to create directory at %s/%s: %m", root, table[i].dir);
+
+ if (uid != UID_INVALID || gid != UID_INVALID) {
+ if (fchownat(fd, table[i].dir, uid, gid, AT_SYMLINK_NOFOLLOW) < 0)
+ return log_error_errno(errno, "Failed to chown directory at %s/%s: %m", root, table[i].dir);
+ }
}
return 0;
diff --git a/src/shared/base-filesystem.h b/src/shared/base-filesystem.h
index 03201f7083..39a496090f 100644
--- a/src/shared/base-filesystem.h
+++ b/src/shared/base-filesystem.h
@@ -21,4 +21,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-int base_filesystem_create(const char *root);
+#include <sys/types.h>
+
+int base_filesystem_create(const char *root, uid_t uid, gid_t gid);
diff --git a/src/shared/boot-timestamps.c b/src/shared/boot-timestamps.c
index 54e0537a21..ecbe1aaa0f 100644
--- a/src/shared/boot-timestamps.c
+++ b/src/shared/boot-timestamps.c
@@ -19,7 +19,6 @@
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 "boot-timestamps.h"
#include "acpi-fpdt.h"
@@ -40,10 +39,8 @@ int boot_timestamps(const dual_timestamp *n, dual_timestamp *firmware, dual_time
r = acpi_get_boot_usec(&x, &y);
if (r < 0) {
-#ifdef ENABLE_EFI
r = efi_loader_get_boot_usec(&x, &y);
if (r < 0)
-#endif
return r;
}
diff --git a/src/shared/btrfs-ctree.h b/src/shared/btrfs-ctree.h
index 8b6f1ab4f4..d3ae57331c 100644
--- a/src/shared/btrfs-ctree.h
+++ b/src/shared/btrfs-ctree.h
@@ -90,3 +90,9 @@ struct btrfs_qgroup_limit_item {
le64_t rsv_rfer;
le64_t rsv_excl;
} _packed_;
+
+struct btrfs_root_ref {
+ le64_t dirid;
+ le64_t sequence;
+ le16_t name_len;
+} _packed_;
diff --git a/src/shared/btrfs-util.c b/src/shared/btrfs-util.c
index b34ac8b15a..49528dbf01 100644
--- a/src/shared/btrfs-util.c
+++ b/src/shared/btrfs-util.c
@@ -31,13 +31,21 @@
#include "util.h"
#include "path-util.h"
#include "macro.h"
-#include "strv.h"
#include "copy.h"
#include "selinux-util.h"
#include "smack-util.h"
+#include "fileio.h"
#include "btrfs-ctree.h"
#include "btrfs-util.h"
+/* WARNING: Be careful with file system ioctls! When we get an fd, we
+ * need to make sure it either refers to only a regular file or
+ * directory, or that it is located on btrfs, before invoking any
+ * btrfs ioctls. The ioctl numbers are reused by some device drivers
+ * (such as DRM), and hence might have bad effects when invoked on
+ * device nodes (that reference drivers) rather than fds to normal
+ * files or directories. */
+
static int validate_subvolume_name(const char *name) {
if (!filename_is_valid(name))
@@ -83,17 +91,10 @@ static int extract_subvolume_name(const char *path, const char **subvolume) {
return 0;
}
-int btrfs_is_snapshot(int fd) {
- struct stat st;
+int btrfs_is_filesystem(int fd) {
struct statfs sfs;
- /* On btrfs subvolumes always have the inode 256 */
-
- if (fstat(fd, &st) < 0)
- return -errno;
-
- if (!S_ISDIR(st.st_mode) || st.st_ino != 256)
- return 0;
+ assert(fd >= 0);
if (fstatfs(fd, &sfs) < 0)
return -errno;
@@ -101,65 +102,20 @@ int btrfs_is_snapshot(int fd) {
return F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC);
}
-int btrfs_subvol_snapshot(const char *old_path, const char *new_path, bool read_only, bool fallback_copy) {
- struct btrfs_ioctl_vol_args_v2 args = {
- .flags = read_only ? BTRFS_SUBVOL_RDONLY : 0,
- };
- _cleanup_close_ int old_fd = -1, new_fd = -1;
- const char *subvolume;
- int r;
-
- assert(old_path);
-
- old_fd = open(old_path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
- if (old_fd < 0)
- return -errno;
-
- r = btrfs_is_snapshot(old_fd);
- if (r < 0)
- return r;
- if (r == 0) {
-
- if (fallback_copy) {
- r = btrfs_subvol_make(new_path);
- if (r < 0)
- return r;
-
- r = copy_directory_fd(old_fd, new_path, true);
- if (r < 0) {
- btrfs_subvol_remove(new_path);
- return r;
- }
-
- if (read_only) {
- r = btrfs_subvol_set_read_only(new_path, true);
- if (r < 0) {
- btrfs_subvol_remove(new_path);
- return r;
- }
- }
-
- return 0;
- }
-
- return -EISDIR;
- }
-
- r = extract_subvolume_name(new_path, &subvolume);
- if (r < 0)
- return r;
+int btrfs_is_subvol(int fd) {
+ struct stat st;
- new_fd = open_parent(new_path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
- if (new_fd < 0)
- return new_fd;
+ assert(fd >= 0);
- strncpy(args.name, subvolume, sizeof(args.name)-1);
- args.fd = old_fd;
+ /* On btrfs subvolumes always have the inode 256 */
- if (ioctl(new_fd, BTRFS_IOC_SNAP_CREATE_V2, &args) < 0)
+ if (fstat(fd, &st) < 0)
return -errno;
- return 0;
+ if (!S_ISDIR(st.st_mode) || st.st_ino != 256)
+ return 0;
+
+ return btrfs_is_filesystem(fd);
}
int btrfs_subvol_make(const char *path) {
@@ -204,30 +160,6 @@ int btrfs_subvol_make_label(const char *path) {
return mac_smack_fix(path, false, false);
}
-int btrfs_subvol_remove(const char *path) {
- struct btrfs_ioctl_vol_args args = {};
- _cleanup_close_ int fd = -1;
- const char *subvolume;
- int r;
-
- assert(path);
-
- r = extract_subvolume_name(path, &subvolume);
- if (r < 0)
- return r;
-
- fd = open_parent(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
- if (fd < 0)
- return fd;
-
- strncpy(args.name, subvolume, sizeof(args.name)-1);
-
- if (ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &args) < 0)
- return -errno;
-
- return 0;
-}
-
int btrfs_subvol_set_read_only_fd(int fd, bool b) {
uint64_t flags, nflags;
struct stat st;
@@ -269,6 +201,15 @@ int btrfs_subvol_set_read_only(const char *path, bool b) {
int btrfs_subvol_get_read_only_fd(int fd) {
uint64_t flags;
+ struct stat st;
+
+ assert(fd >= 0);
+
+ if (fstat(fd, &st) < 0)
+ return -errno;
+
+ if (!S_ISDIR(st.st_mode) || st.st_ino != 256)
+ return -EINVAL;
if (ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags) < 0)
return -errno;
@@ -277,11 +218,21 @@ int btrfs_subvol_get_read_only_fd(int fd) {
}
int btrfs_reflink(int infd, int outfd) {
+ struct stat st;
int r;
assert(infd >= 0);
assert(outfd >= 0);
+ /* Make sure we invoke the ioctl on a regular file, so that no
+ * device driver accidentally gets it. */
+
+ if (fstat(outfd, &st) < 0)
+ return -errno;
+
+ if (!S_ISREG(st.st_mode))
+ return -EINVAL;
+
r = ioctl(outfd, BTRFS_IOC_CLONE, infd);
if (r < 0)
return -errno;
@@ -296,12 +247,19 @@ int btrfs_clone_range(int infd, uint64_t in_offset, int outfd, uint64_t out_offs
.src_length = sz,
.dest_offset = out_offset,
};
+ struct stat st;
int r;
assert(infd >= 0);
assert(outfd >= 0);
assert(sz > 0);
+ if (fstat(outfd, &st) < 0)
+ return -errno;
+
+ if (!S_ISREG(st.st_mode))
+ return -EINVAL;
+
r = ioctl(outfd, BTRFS_IOC_CLONE_RANGE, &args);
if (r < 0)
return -errno;
@@ -309,17 +267,19 @@ int btrfs_clone_range(int infd, uint64_t in_offset, int outfd, uint64_t out_offs
return 0;
}
-int btrfs_get_block_device(const char *path, dev_t *dev) {
+int btrfs_get_block_device_fd(int fd, dev_t *dev) {
struct btrfs_ioctl_fs_info_args fsi = {};
- _cleanup_close_ int fd = -1;
uint64_t id;
+ int r;
- assert(path);
+ assert(fd >= 0);
assert(dev);
- fd = open(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
- if (fd < 0)
- return -errno;
+ r = btrfs_is_filesystem(fd);
+ if (r < 0)
+ return r;
+ if (!r)
+ return -ENOTTY;
if (ioctl(fd, BTRFS_IOC_FS_INFO, &fsi) < 0)
return -errno;
@@ -357,14 +317,34 @@ int btrfs_get_block_device(const char *path, dev_t *dev) {
return -ENODEV;
}
+int btrfs_get_block_device(const char *path, dev_t *dev) {
+ _cleanup_close_ int fd = -1;
+
+ assert(path);
+ assert(dev);
+
+ fd = open(path, O_RDONLY|O_NOCTTY|O_CLOEXEC);
+ if (fd < 0)
+ return -errno;
+
+ return btrfs_get_block_device_fd(fd, dev);
+}
+
int btrfs_subvol_get_id_fd(int fd, uint64_t *ret) {
struct btrfs_ioctl_ino_lookup_args args = {
.objectid = BTRFS_FIRST_FREE_OBJECTID
};
+ int r;
assert(fd >= 0);
assert(ret);
+ r = btrfs_is_filesystem(fd);
+ if (r < 0)
+ return r;
+ if (!r)
+ return -ENOTTY;
+
if (ioctl(fd, BTRFS_IOC_INO_LOOKUP, &args) < 0)
return -errno;
@@ -584,7 +564,7 @@ int btrfs_subvol_get_quota_fd(int fd, BtrfsQuotaInfo *ret) {
if (sh->type == BTRFS_QGROUP_INFO_KEY) {
const struct btrfs_qgroup_info_item *qii = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
- ret->referred = le64toh(qii->rfer);
+ ret->referenced = le64toh(qii->rfer);
ret->exclusive = le64toh(qii->excl);
found_info = true;
@@ -592,11 +572,11 @@ int btrfs_subvol_get_quota_fd(int fd, BtrfsQuotaInfo *ret) {
} else if (sh->type == BTRFS_QGROUP_LIMIT_KEY) {
const struct btrfs_qgroup_limit_item *qli = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
- ret->referred_max = le64toh(qli->max_rfer);
+ ret->referenced_max = le64toh(qli->max_rfer);
ret->exclusive_max = le64toh(qli->max_excl);
- if (ret->referred_max == 0)
- ret->referred_max = (uint64_t) -1;
+ if (ret->referenced_max == 0)
+ ret->referenced_max = (uint64_t) -1;
if (ret->exclusive_max == 0)
ret->exclusive_max = (uint64_t) -1;
@@ -617,12 +597,12 @@ finish:
return -ENODATA;
if (!found_info) {
- ret->referred = (uint64_t) -1;
+ ret->referenced = (uint64_t) -1;
ret->exclusive = (uint64_t) -1;
}
if (!found_limit) {
- ret->referred_max = (uint64_t) -1;
+ ret->referenced_max = (uint64_t) -1;
ret->exclusive_max = (uint64_t) -1;
}
@@ -630,8 +610,16 @@ finish:
}
int btrfs_defrag_fd(int fd) {
+ struct stat st;
+
assert(fd >= 0);
+ if (fstat(fd, &st) < 0)
+ return -errno;
+
+ if (!S_ISREG(st.st_mode))
+ return -EINVAL;
+
if (ioctl(fd, BTRFS_IOC_DEFRAG, NULL) < 0)
return -errno;
@@ -647,3 +635,518 @@ int btrfs_defrag(const char *p) {
return btrfs_defrag_fd(fd);
}
+
+int btrfs_quota_enable_fd(int fd, bool b) {
+ struct btrfs_ioctl_quota_ctl_args args = {
+ .cmd = b ? BTRFS_QUOTA_CTL_ENABLE : BTRFS_QUOTA_CTL_DISABLE,
+ };
+ int r;
+
+ assert(fd >= 0);
+
+ r = btrfs_is_filesystem(fd);
+ if (r < 0)
+ return r;
+ if (!r)
+ return -ENOTTY;
+
+ if (ioctl(fd, BTRFS_IOC_QUOTA_CTL, &args) < 0)
+ return -errno;
+
+ return 0;
+}
+
+int btrfs_quota_enable(const char *path, bool b) {
+ _cleanup_close_ int fd = -1;
+
+ fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
+ if (fd < 0)
+ return -errno;
+
+ return btrfs_quota_enable_fd(fd, b);
+}
+
+int btrfs_quota_limit_fd(int fd, uint64_t referenced_max) {
+ struct btrfs_ioctl_qgroup_limit_args args = {
+ .lim.max_rfer =
+ referenced_max == (uint64_t) -1 ? 0 :
+ referenced_max == 0 ? 1 : referenced_max,
+ .lim.flags = BTRFS_QGROUP_LIMIT_MAX_RFER,
+ };
+ int r;
+
+ assert(fd >= 0);
+
+ r = btrfs_is_filesystem(fd);
+ if (r < 0)
+ return r;
+ if (!r)
+ return -ENOTTY;
+
+ if (ioctl(fd, BTRFS_IOC_QGROUP_LIMIT, &args) < 0)
+ return -errno;
+
+ return 0;
+}
+
+int btrfs_quota_limit(const char *path, uint64_t referenced_max) {
+ _cleanup_close_ int fd = -1;
+
+ fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
+ if (fd < 0)
+ return -errno;
+
+ return btrfs_quota_limit_fd(fd, referenced_max);
+}
+
+int btrfs_resize_loopback_fd(int fd, uint64_t new_size, bool grow_only) {
+ struct btrfs_ioctl_vol_args args = {};
+ _cleanup_free_ char *p = NULL, *loop = NULL, *backing = NULL;
+ _cleanup_close_ int loop_fd = -1, backing_fd = -1;
+ struct stat st;
+ dev_t dev = 0;
+ int r;
+
+ /* btrfs cannot handle file systems < 16M, hence use this as minimum */
+ if (new_size < 16*1024*1024)
+ new_size = 16*1024*1024;
+
+ r = btrfs_get_block_device_fd(fd, &dev);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -ENODEV;
+
+ if (asprintf(&p, "/sys/dev/block/%u:%u/loop/backing_file", major(dev), minor(dev)) < 0)
+ return -ENOMEM;
+ r = read_one_line_file(p, &backing);
+ if (r == -ENOENT)
+ return -ENODEV;
+ if (r < 0)
+ return r;
+ if (isempty(backing) || !path_is_absolute(backing))
+ return -ENODEV;
+
+ backing_fd = open(backing, O_RDWR|O_CLOEXEC|O_NOCTTY);
+ if (backing_fd < 0)
+ return -errno;
+
+ if (fstat(backing_fd, &st) < 0)
+ return -errno;
+ if (!S_ISREG(st.st_mode))
+ return -ENODEV;
+
+ if (new_size == (uint64_t) st.st_size)
+ return 0;
+
+ if (grow_only && new_size < (uint64_t) st.st_size)
+ return -EINVAL;
+
+ if (asprintf(&loop, "/dev/block/%u:%u", major(dev), minor(dev)) < 0)
+ return -ENOMEM;
+ loop_fd = open(loop, O_RDWR|O_CLOEXEC|O_NOCTTY);
+ if (loop_fd < 0)
+ return -errno;
+
+ if (snprintf(args.name, sizeof(args.name), "%" PRIu64, new_size) >= (int) sizeof(args.name))
+ return -EINVAL;
+
+ if (new_size < (uint64_t) st.st_size) {
+ /* Decrease size: first decrease btrfs size, then shorten loopback */
+ if (ioctl(fd, BTRFS_IOC_RESIZE, &args) < 0)
+ return -errno;
+ }
+
+ if (ftruncate(backing_fd, new_size) < 0)
+ return -errno;
+
+ if (ioctl(loop_fd, LOOP_SET_CAPACITY, 0) < 0)
+ return -errno;
+
+ if (new_size > (uint64_t) st.st_size) {
+ /* Increase size: first enlarge loopback, then increase btrfs size */
+ if (ioctl(fd, BTRFS_IOC_RESIZE, &args) < 0)
+ return -errno;
+ }
+
+ /* Make sure the free disk space is correctly updated for both file systems */
+ (void) fsync(fd);
+ (void) fsync(backing_fd);
+
+ return 1;
+}
+
+int btrfs_resize_loopback(const char *p, uint64_t new_size, bool grow_only) {
+ _cleanup_close_ int fd = -1;
+
+ fd = open(p, O_RDONLY|O_NOCTTY|O_CLOEXEC);
+ if (fd < 0)
+ return -errno;
+
+ return btrfs_resize_loopback_fd(fd, new_size, grow_only);
+}
+
+static int subvol_remove_children(int fd, const char *subvolume, uint64_t subvol_id, bool recursive) {
+ struct btrfs_ioctl_search_args args = {
+ .key.tree_id = BTRFS_ROOT_TREE_OBJECTID,
+
+ .key.min_objectid = BTRFS_FIRST_FREE_OBJECTID,
+ .key.max_objectid = BTRFS_LAST_FREE_OBJECTID,
+
+ .key.min_type = BTRFS_ROOT_BACKREF_KEY,
+ .key.max_type = BTRFS_ROOT_BACKREF_KEY,
+
+ .key.min_transid = 0,
+ .key.max_transid = (uint64_t) -1,
+ };
+
+ struct btrfs_ioctl_vol_args vol_args = {};
+ _cleanup_close_ int subvol_fd = -1;
+ struct stat st;
+ bool made_writable = false;
+ int r;
+
+ assert(fd >= 0);
+ assert(subvolume);
+
+ if (fstat(fd, &st) < 0)
+ return -errno;
+
+ if (!S_ISDIR(st.st_mode))
+ return -EINVAL;
+
+ /* First, try to remove the subvolume. If it happens to be
+ * already empty, this will just work. */
+ strncpy(vol_args.name, subvolume, sizeof(vol_args.name)-1);
+ if (ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &vol_args) >= 0)
+ return 0;
+ if (!recursive || errno != ENOTEMPTY)
+ return -errno;
+
+ /* OK, the subvolume is not empty, let's look for child
+ * subvolumes, and remove them, first */
+ subvol_fd = openat(fd, subvolume, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
+ if (subvol_fd < 0)
+ return -errno;
+
+ if (subvol_id == 0) {
+ r = btrfs_subvol_get_id_fd(subvol_fd, &subvol_id);
+ if (r < 0)
+ return r;
+ }
+
+ args.key.min_offset = args.key.max_offset = subvol_id;
+
+ while (btrfs_ioctl_search_args_compare(&args) <= 0) {
+ const struct btrfs_ioctl_search_header *sh;
+ unsigned i;
+
+ args.key.nr_items = 256;
+ if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0)
+ return -errno;
+
+ if (args.key.nr_items <= 0)
+ break;
+
+ FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) {
+ _cleanup_free_ char *p = NULL;
+ const struct btrfs_root_ref *ref;
+ struct btrfs_ioctl_ino_lookup_args ino_args;
+
+ btrfs_ioctl_search_args_set(&args, sh);
+
+ if (sh->type != BTRFS_ROOT_BACKREF_KEY)
+ continue;
+ if (sh->offset != subvol_id)
+ continue;
+
+ ref = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
+
+ p = strndup((char*) ref + sizeof(struct btrfs_root_ref), le64toh(ref->name_len));
+ if (!p)
+ return -ENOMEM;
+
+ zero(ino_args);
+ ino_args.treeid = subvol_id;
+ ino_args.objectid = htole64(ref->dirid);
+
+ if (ioctl(fd, BTRFS_IOC_INO_LOOKUP, &ino_args) < 0)
+ return -errno;
+
+ if (!made_writable) {
+ r = btrfs_subvol_set_read_only_fd(subvol_fd, false);
+ if (r < 0)
+ return r;
+
+ made_writable = true;
+ }
+
+ if (isempty(ino_args.name))
+ /* Subvolume is in the top-level
+ * directory of the subvolume. */
+ r = subvol_remove_children(subvol_fd, p, sh->objectid, recursive);
+ else {
+ _cleanup_close_ int child_fd = -1;
+
+ /* Subvolume is somewhere further down,
+ * hence we need to open the
+ * containing directory first */
+
+ child_fd = openat(subvol_fd, ino_args.name, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
+ if (child_fd < 0)
+ return -errno;
+
+ r = subvol_remove_children(child_fd, p, sh->objectid, recursive);
+ }
+ if (r < 0)
+ return r;
+ }
+
+ /* Increase search key by one, to read the next item, if we can. */
+ if (!btrfs_ioctl_search_args_inc(&args))
+ break;
+ }
+
+ /* OK, the child subvolumes should all be gone now, let's try
+ * again to remove the subvolume */
+ if (ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &vol_args) < 0)
+ return -errno;
+
+ return 0;
+}
+
+int btrfs_subvol_remove(const char *path, bool recursive) {
+ _cleanup_close_ int fd = -1;
+ const char *subvolume;
+ int r;
+
+ assert(path);
+
+ r = extract_subvolume_name(path, &subvolume);
+ if (r < 0)
+ return r;
+
+ fd = open_parent(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
+ if (fd < 0)
+ return fd;
+
+ return subvol_remove_children(fd, subvolume, 0, recursive);
+}
+
+int btrfs_subvol_remove_fd(int fd, const char *subvolume, bool recursive) {
+ return subvol_remove_children(fd, subvolume, 0, recursive);
+}
+
+static int subvol_snapshot_children(int old_fd, int new_fd, const char *subvolume, uint64_t subvol_id, BtrfsSnapshotFlags flags) {
+
+ struct btrfs_ioctl_search_args args = {
+ .key.tree_id = BTRFS_ROOT_TREE_OBJECTID,
+
+ .key.min_objectid = BTRFS_FIRST_FREE_OBJECTID,
+ .key.max_objectid = BTRFS_LAST_FREE_OBJECTID,
+
+ .key.min_type = BTRFS_ROOT_BACKREF_KEY,
+ .key.max_type = BTRFS_ROOT_BACKREF_KEY,
+
+ .key.min_transid = 0,
+ .key.max_transid = (uint64_t) -1,
+ };
+
+ struct btrfs_ioctl_vol_args_v2 vol_args = {
+ .flags = flags & BTRFS_SNAPSHOT_READ_ONLY ? BTRFS_SUBVOL_RDONLY : 0,
+ .fd = old_fd,
+ };
+ int r;
+ _cleanup_close_ int subvolume_fd = -1;
+
+ assert(old_fd >= 0);
+ assert(new_fd >= 0);
+ assert(subvolume);
+
+ strncpy(vol_args.name, subvolume, sizeof(vol_args.name)-1);
+ vol_args.fd = old_fd;
+
+ if (ioctl(new_fd, BTRFS_IOC_SNAP_CREATE_V2, &vol_args) < 0)
+ return -errno;
+
+ if (!(flags & BTRFS_SNAPSHOT_RECURSIVE))
+ return 0;
+
+ if (subvol_id == 0) {
+ r = btrfs_subvol_get_id_fd(old_fd, &subvol_id);
+ if (r < 0)
+ return r;
+ }
+
+ args.key.min_offset = args.key.max_offset = subvol_id;
+
+ while (btrfs_ioctl_search_args_compare(&args) <= 0) {
+ const struct btrfs_ioctl_search_header *sh;
+ unsigned i;
+
+ args.key.nr_items = 256;
+ if (ioctl(old_fd, BTRFS_IOC_TREE_SEARCH, &args) < 0)
+ return -errno;
+
+ if (args.key.nr_items <= 0)
+ break;
+
+ FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) {
+ _cleanup_free_ char *p = NULL, *c = NULL, *np = NULL;
+ struct btrfs_ioctl_ino_lookup_args ino_args;
+ const struct btrfs_root_ref *ref;
+ _cleanup_close_ int old_child_fd = -1, new_child_fd = -1;
+
+ btrfs_ioctl_search_args_set(&args, sh);
+
+ if (sh->type != BTRFS_ROOT_BACKREF_KEY)
+ continue;
+ if (sh->offset != subvol_id)
+ continue;
+
+ ref = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
+
+ p = strndup((char*) ref + sizeof(struct btrfs_root_ref), le64toh(ref->name_len));
+ if (!p)
+ return -ENOMEM;
+
+ zero(ino_args);
+ ino_args.treeid = subvol_id;
+ ino_args.objectid = htole64(ref->dirid);
+
+ if (ioctl(old_fd, BTRFS_IOC_INO_LOOKUP, &ino_args) < 0)
+ return -errno;
+
+ /* The kernel returns an empty name if the
+ * subvolume is in the top-level directory,
+ * and otherwise appends a slash, so that we
+ * can just concatenate easily here, without
+ * adding a slash. */
+ c = strappend(ino_args.name, p);
+ if (!c)
+ return -ENOMEM;
+
+ old_child_fd = openat(old_fd, c, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
+ if (old_child_fd < 0)
+ return -errno;
+
+ np = strjoin(subvolume, "/", ino_args.name, NULL);
+ if (!np)
+ return -ENOMEM;
+
+ new_child_fd = openat(new_fd, np, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
+ if (new_child_fd < 0)
+ return -errno;
+
+ if (flags & BTRFS_SNAPSHOT_READ_ONLY) {
+ /* If the snapshot is read-only we
+ * need to mark it writable
+ * temporarily, to put the subsnapshot
+ * into place. */
+
+ if (subvolume_fd < 0) {
+ subvolume_fd = openat(new_fd, subvolume, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
+ if (subvolume_fd < 0)
+ return -errno;
+ }
+
+ r = btrfs_subvol_set_read_only_fd(subvolume_fd, false);
+ if (r < 0)
+ return r;
+ }
+
+ /* When btrfs clones the subvolumes, child
+ * subvolumes appear as directories. Remove
+ * them, so that we can create a new snapshot
+ * in their place */
+ if (unlinkat(new_child_fd, p, AT_REMOVEDIR) < 0) {
+ int k = -errno;
+
+ if (flags & BTRFS_SNAPSHOT_READ_ONLY)
+ (void) btrfs_subvol_set_read_only_fd(subvolume_fd, true);
+
+ return k;
+ }
+
+ r = subvol_snapshot_children(old_child_fd, new_child_fd, p, sh->objectid, flags & ~BTRFS_SNAPSHOT_FALLBACK_COPY);
+
+ /* Restore the readonly flag */
+ if (flags & BTRFS_SNAPSHOT_READ_ONLY) {
+ int k;
+
+ k = btrfs_subvol_set_read_only_fd(subvolume_fd, true);
+ if (r >= 0 && k < 0)
+ return k;
+ }
+
+ if (r < 0)
+ return r;
+ }
+
+ /* Increase search key by one, to read the next item, if we can. */
+ if (!btrfs_ioctl_search_args_inc(&args))
+ break;
+ }
+
+ return 0;
+}
+
+int btrfs_subvol_snapshot_fd(int old_fd, const char *new_path, BtrfsSnapshotFlags flags) {
+ _cleanup_close_ int new_fd = -1;
+ const char *subvolume;
+ int r;
+
+ assert(old_fd >= 0);
+ assert(new_path);
+
+ r = btrfs_is_subvol(old_fd);
+ if (r < 0)
+ return r;
+ if (r == 0) {
+ if (!(flags & BTRFS_SNAPSHOT_FALLBACK_COPY))
+ return -EISDIR;
+
+ r = btrfs_subvol_make(new_path);
+ if (r < 0)
+ return r;
+
+ r = copy_directory_fd(old_fd, new_path, true);
+ if (r < 0) {
+ btrfs_subvol_remove(new_path, false);
+ return r;
+ }
+
+ if (flags & BTRFS_SNAPSHOT_READ_ONLY) {
+ r = btrfs_subvol_set_read_only(new_path, true);
+ if (r < 0) {
+ btrfs_subvol_remove(new_path, false);
+ return r;
+ }
+ }
+
+ return 0;
+ }
+
+ r = extract_subvolume_name(new_path, &subvolume);
+ if (r < 0)
+ return r;
+
+ new_fd = open_parent(new_path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
+ if (new_fd < 0)
+ return new_fd;
+
+ return subvol_snapshot_children(old_fd, new_fd, subvolume, 0, flags);
+}
+
+int btrfs_subvol_snapshot(const char *old_path, const char *new_path, BtrfsSnapshotFlags flags) {
+ _cleanup_close_ int old_fd = -1;
+
+ assert(old_path);
+ assert(new_path);
+
+ old_fd = open(old_path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
+ if (old_fd < 0)
+ return -errno;
+
+ return btrfs_subvol_snapshot_fd(old_fd, new_path, flags);
+}
diff --git a/src/shared/btrfs-util.h b/src/shared/btrfs-util.h
index 1b9c142e5c..a7eb895c93 100644
--- a/src/shared/btrfs-util.h
+++ b/src/shared/btrfs-util.h
@@ -37,18 +37,26 @@ typedef struct BtrfsSubvolInfo {
} BtrfsSubvolInfo;
typedef struct BtrfsQuotaInfo {
- uint64_t referred;
+ uint64_t referenced;
uint64_t exclusive;
- uint64_t referred_max;
+ uint64_t referenced_max;
uint64_t exclusive_max;
} BtrfsQuotaInfo;
-int btrfs_is_snapshot(int fd);
+typedef enum BtrfsSnapshotFlags {
+ BTRFS_SNAPSHOT_FALLBACK_COPY = 1,
+ BTRFS_SNAPSHOT_READ_ONLY = 2,
+ BTRFS_SNAPSHOT_RECURSIVE = 4,
+} BtrfsSnapshotFlags;
+
+int btrfs_is_filesystem(int fd);
+int btrfs_is_subvol(int fd);
int btrfs_subvol_make(const char *path);
int btrfs_subvol_make_label(const char *path);
-int btrfs_subvol_remove(const char *path);
-int btrfs_subvol_snapshot(const char *old_path, const char *new_path, bool read_only, bool fallback_copy);
+
+int btrfs_subvol_snapshot_fd(int old_fd, const char *new_path, BtrfsSnapshotFlags flags);
+int btrfs_subvol_snapshot(const char *old_path, const char *new_path, BtrfsSnapshotFlags flags);
int btrfs_subvol_set_read_only_fd(int fd, bool b);
int btrfs_subvol_set_read_only(const char *path, bool b);
@@ -60,7 +68,20 @@ int btrfs_subvol_get_quota_fd(int fd, BtrfsQuotaInfo *quota);
int btrfs_reflink(int infd, int outfd);
int btrfs_clone_range(int infd, uint64_t in_offset, int ofd, uint64_t out_offset, uint64_t sz);
+int btrfs_get_block_device_fd(int fd, dev_t *dev);
int btrfs_get_block_device(const char *path, dev_t *dev);
int btrfs_defrag_fd(int fd);
int btrfs_defrag(const char *p);
+
+int btrfs_quota_enable_fd(int fd, bool b);
+int btrfs_quota_enable(const char *path, bool b);
+
+int btrfs_quota_limit_fd(int fd, uint64_t referenced_max);
+int btrfs_quota_limit(const char *path, uint64_t referenced_max);
+
+int btrfs_resize_loopback_fd(int fd, uint64_t size, bool grow_only);
+int btrfs_resize_loopback(const char *path, uint64_t size, bool grow_only);
+
+int btrfs_subvol_remove(const char *path, bool recursive);
+int btrfs_subvol_remove_fd(int fd, const char *subvolume, bool recursive);
diff --git a/src/shared/bus-label.c b/src/shared/bus-label.c
index 61eb75bca2..ccc9f2bf8e 100644
--- a/src/shared/bus-label.c
+++ b/src/shared/bus-label.c
@@ -19,13 +19,10 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <assert.h>
#include <stdlib.h>
-#include <unistd.h>
#include "util.h"
#include "macro.h"
-#include "def.h"
#include "bus-label.h"
@@ -66,34 +63,35 @@ char *bus_label_escape(const char *s) {
return r;
}
-char *bus_label_unescape(const char *f) {
+char *bus_label_unescape_n(const char *f, size_t l) {
char *r, *t;
+ size_t i;
assert_return(f, NULL);
/* Special case for the empty string */
- if (streq(f, "_"))
+ if (l == 1 && *f == '_')
return strdup("");
- r = new(char, strlen(f) + 1);
+ r = new(char, l + 1);
if (!r)
return NULL;
- for (t = r; *f; f++) {
-
- if (*f == '_') {
+ for (i = 0, t = r; i < l; ++i) {
+ if (f[i] == '_') {
int a, b;
- if ((a = unhexchar(f[1])) < 0 ||
- (b = unhexchar(f[2])) < 0) {
+ if (l - i < 3 ||
+ (a = unhexchar(f[i + 1])) < 0 ||
+ (b = unhexchar(f[i + 2])) < 0) {
/* Invalid escape code, let's take it literal then */
*(t++) = '_';
} else {
*(t++) = (char) ((a << 4) | b);
- f += 2;
+ i += 2;
}
} else
- *(t++) = *f;
+ *(t++) = f[i];
}
*t = 0;
diff --git a/src/shared/bus-label.h b/src/shared/bus-label.h
index c27c8517fd..ed1dc4e0a7 100644
--- a/src/shared/bus-label.h
+++ b/src/shared/bus-label.h
@@ -21,5 +21,12 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <stdlib.h>
+#include <string.h>
+
char *bus_label_escape(const char *s);
-char *bus_label_unescape(const char *f);
+char *bus_label_unescape_n(const char *f, size_t l);
+
+static inline char *bus_label_unescape(const char *f) {
+ return bus_label_unescape_n(f, f ? strlen(f) : 0);
+}
diff --git a/src/shared/cap-list.c b/src/shared/cap-list.c
index 8033e8c7b2..bd5bffbfa5 100644
--- a/src/shared/cap-list.c
+++ b/src/shared/cap-list.c
@@ -19,7 +19,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <linux/capability.h>
#include <string.h>
#include "util.h"
diff --git a/src/shared/capability.c b/src/shared/capability.c
index 915ceb9d9b..58f00e6dae 100644
--- a/src/shared/capability.c
+++ b/src/shared/capability.c
@@ -19,14 +19,9 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <assert.h>
-#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <stdio.h>
-#include <sys/types.h>
-#include <stdarg.h>
-#include <ctype.h>
#include <sys/capability.h>
#include <sys/prctl.h>
#include "grp.h"
@@ -55,7 +50,7 @@ unsigned long cap_last_cap(void) {
static thread_local unsigned long saved;
static thread_local bool valid = false;
_cleanup_free_ char *content = NULL;
- unsigned long p;
+ unsigned long p = 0;
int r;
if (valid)
diff --git a/src/shared/capability.h b/src/shared/capability.h
index 6f2f6f997d..4eb5c2a835 100644
--- a/src/shared/capability.h
+++ b/src/shared/capability.h
@@ -21,7 +21,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <inttypes.h>
#include <stdbool.h>
#include <sys/capability.h>
@@ -32,7 +31,7 @@ int have_effective_cap(int value);
int capability_bounding_set_drop(uint64_t drop, bool right_now);
int capability_bounding_set_drop_usermode(uint64_t drop);
-int drop_privileges(uid_t uid, gid_t gid, uint64_t keep_capabilites);
+int drop_privileges(uid_t uid, gid_t gid, uint64_t keep_capabilities);
int drop_capability(cap_value_t cv);
diff --git a/src/shared/cgroup-show.c b/src/shared/cgroup-show.c
index cbd94e86d9..1a2c4b28cd 100644
--- a/src/shared/cgroup-show.c
+++ b/src/shared/cgroup-show.c
@@ -25,10 +25,13 @@
#include <errno.h>
#include "util.h"
+#include "formats-util.h"
+#include "process-util.h"
#include "macro.h"
#include "path-util.h"
#include "cgroup-util.h"
#include "cgroup-show.h"
+#include "terminal-util.h"
static int compare(const void *a, const void *b) {
const pid_t *p = a, *q = b;
diff --git a/src/shared/cgroup-show.h b/src/shared/cgroup-show.h
index 3146f56cad..aa832454b5 100644
--- a/src/shared/cgroup-show.h
+++ b/src/shared/cgroup-show.h
@@ -23,7 +23,6 @@
#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, OutputFlags flags);
diff --git a/src/shared/cgroup-util.c b/src/shared/cgroup-util.c
index dfd8689b72..9988e5c574 100644
--- a/src/shared/cgroup-util.c
+++ b/src/shared/cgroup-util.c
@@ -30,16 +30,17 @@
#include <ftw.h>
#include "cgroup-util.h"
-#include "log.h"
#include "set.h"
#include "macro.h"
#include "util.h"
+#include "formats-util.h"
+#include "process-util.h"
#include "path-util.h"
-#include "strv.h"
#include "unit-name.h"
#include "fileio.h"
#include "special.h"
#include "mkdir.h"
+#include "login-shared.h"
int cg_enumerate_processes(const char *controller, const char *path, FILE **_f) {
_cleanup_free_ char *fs = NULL;
@@ -488,9 +489,11 @@ int cg_get_path(const char *controller, const char *path, const char *suffix, ch
if (_unlikely_(!good)) {
int r;
- r = path_is_mount_point("/sys/fs/cgroup", false);
- if (r <= 0)
- return r < 0 ? r : -ENOENT;
+ r = path_is_mount_point("/sys/fs/cgroup", 0);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -ENOENT;
/* Cache this to save a few stat()s */
good = true;
@@ -1138,17 +1141,21 @@ int cg_pid_get_path_shifted(pid_t pid, const char *root, char **cgroup) {
}
int cg_path_decode_unit(const char *cgroup, char **unit){
- char *e, *c, *s;
+ char *c, *s;
+ size_t n;
assert(cgroup);
assert(unit);
- e = strchrnul(cgroup, '/');
- c = strndupa(cgroup, e - cgroup);
+ n = strcspn(cgroup, "/");
+ if (n < 3)
+ return -ENXIO;
+
+ c = strndupa(cgroup, n);
c = cg_unescape(c);
- if (!unit_name_is_valid(c, TEMPLATE_INVALID))
- return -EINVAL;
+ if (!unit_name_is_valid(c, UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE))
+ return -ENXIO;
s = strdup(c);
if (!s)
@@ -1158,7 +1165,31 @@ int cg_path_decode_unit(const char *cgroup, char **unit){
return 0;
}
+static bool valid_slice_name(const char *p, size_t n) {
+
+ if (!p)
+ return false;
+
+ if (n < strlen("x.slice"))
+ return false;
+
+ if (memcmp(p + n - 6, ".slice", 6) == 0) {
+ char buf[n+1], *c;
+
+ memcpy(buf, p, n);
+ buf[n] = 0;
+
+ c = cg_unescape(buf);
+
+ return unit_name_is_valid(c, UNIT_NAME_PLAIN);
+ }
+
+ return false;
+}
+
static const char *skip_slices(const char *p) {
+ assert(p);
+
/* Skips over all slice assignments */
for (;;) {
@@ -1167,22 +1198,35 @@ static const char *skip_slices(const char *p) {
p += strspn(p, "/");
n = strcspn(p, "/");
- if (n <= 6 || memcmp(p + n - 6, ".slice", 6) != 0)
+ if (!valid_slice_name(p, n))
return p;
p += n;
}
}
-int cg_path_get_unit(const char *path, char **unit) {
+int cg_path_get_unit(const char *path, char **ret) {
const char *e;
+ char *unit;
+ int r;
assert(path);
- assert(unit);
+ assert(ret);
e = skip_slices(path);
- return cg_path_decode_unit(e, unit);
+ r = cg_path_decode_unit(e, &unit);
+ if (r < 0)
+ return r;
+
+ /* We skipped over the slices, don't accept any now */
+ if (endswith(unit, ".slice")) {
+ free(unit);
+ return -ENXIO;
+ }
+
+ *ret = unit;
+ return 0;
}
int cg_pid_get_unit(pid_t pid, char **unit) {
@@ -1204,18 +1248,35 @@ int cg_pid_get_unit(pid_t pid, char **unit) {
static const char *skip_session(const char *p) {
size_t n;
- assert(p);
+ if (isempty(p))
+ return NULL;
p += strspn(p, "/");
n = strcspn(p, "/");
- if (n < strlen("session-x.scope") || memcmp(p, "session-", 8) != 0 || memcmp(p + n - 6, ".scope", 6) != 0)
+ if (n < strlen("session-x.scope"))
return NULL;
- p += n;
- p += strspn(p, "/");
+ if (memcmp(p, "session-", 8) == 0 && memcmp(p + n - 6, ".scope", 6) == 0) {
+ char buf[n - 8 - 6 + 1];
+
+ memcpy(buf, p + 8, n - 8 - 6);
+ buf[n - 8 - 6] = 0;
+
+ /* Note that session scopes never need unescaping,
+ * since they cannot conflict with the kernel's own
+ * names, hence we don't need to call cg_unescape()
+ * here. */
+
+ if (!session_id_valid(buf))
+ return false;
- return p;
+ p += n;
+ p += strspn(p, "/");
+ return p;
+ }
+
+ return NULL;
}
/**
@@ -1224,44 +1285,69 @@ static const char *skip_session(const char *p) {
static const char *skip_user_manager(const char *p) {
size_t n;
- assert(p);
+ if (isempty(p))
+ return NULL;
p += strspn(p, "/");
n = strcspn(p, "/");
- if (n < strlen("user@x.service") || memcmp(p, "user@", 5) != 0 || memcmp(p + n - 8, ".service", 8) != 0)
+ if (n < strlen("user@x.service"))
return NULL;
- p += n;
- p += strspn(p, "/");
+ if (memcmp(p, "user@", 5) == 0 && memcmp(p + n - 8, ".service", 8) == 0) {
+ char buf[n - 5 - 8 + 1];
- return p;
+ memcpy(buf, p + 5, n - 5 - 8);
+ buf[n - 5 - 8] = 0;
+
+ /* Note that user manager services never need unescaping,
+ * since they cannot conflict with the kernel's own
+ * names, hence we don't need to call cg_unescape()
+ * here. */
+
+ if (parse_uid(buf, NULL) < 0)
+ return NULL;
+
+ p += n;
+ p += strspn(p, "/");
+
+ return p;
+ }
+
+ return NULL;
}
-int cg_path_get_user_unit(const char *path, char **unit) {
+static const char *skip_user_prefix(const char *path) {
const char *e, *t;
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 */
/* Skip slices, if there are any */
e = skip_slices(path);
- /* Skip the session scope or user manager... */
- t = skip_session(e);
- if (!t)
- t = skip_user_manager(e);
- if (!t)
- return -ENOENT;
+ /* Skip the user manager, if it's in the path now... */
+ t = skip_user_manager(e);
+ if (t)
+ return t;
+
+ /* Alternatively skip the user session if it is in the path... */
+ return skip_session(e);
+}
- /* ... and skip more slices if there are any */
- e = skip_slices(t);
+int cg_path_get_user_unit(const char *path, char **ret) {
+ const char *t;
- return cg_path_decode_unit(e, unit);
+ assert(path);
+ assert(ret);
+
+ t = skip_user_prefix(path);
+ if (!t)
+ return -ENXIO;
+
+ /* And from here on it looks pretty much the same as for a
+ * system unit, hence let's use the same parser from here
+ * on. */
+ return cg_path_get_unit(t, ret);
}
int cg_pid_get_user_unit(pid_t pid, char **unit) {
@@ -1306,36 +1392,35 @@ int cg_pid_get_machine_name(pid_t pid, char **machine) {
}
int cg_path_get_session(const char *path, char **session) {
- const char *e, *n, *x, *y;
- char *s;
+ _cleanup_free_ char *unit = NULL;
+ char *start, *end;
+ int r;
assert(path);
- /* Skip slices, if there are any */
- e = skip_slices(path);
-
- n = strchrnul(e, '/');
- if (e == n)
- return -ENOENT;
+ r = cg_path_get_unit(path, &unit);
+ if (r < 0)
+ return r;
- s = strndupa(e, n - e);
- s = cg_unescape(s);
+ start = startswith(unit, "session-");
+ if (!start)
+ return -ENXIO;
+ end = endswith(start, ".scope");
+ if (!end)
+ return -ENXIO;
- x = startswith(s, "session-");
- if (!x)
- return -ENOENT;
- y = endswith(x, ".scope");
- if (!y || x == y)
- return -ENOENT;
+ *end = 0;
+ if (!session_id_valid(start))
+ return -ENXIO;
if (session) {
- char *r;
+ char *rr;
- r = strndup(x, y - x);
- if (!r)
+ rr = strdup(start);
+ if (!rr)
return -ENOMEM;
- *session = r;
+ *session = rr;
}
return 0;
@@ -1354,9 +1439,7 @@ int cg_pid_get_session(pid_t pid, char **session) {
int cg_path_get_owner_uid(const char *path, uid_t *uid) {
_cleanup_free_ char *slice = NULL;
- const char *start, *end;
- char *s;
- uid_t u;
+ char *start, *end;
int r;
assert(path);
@@ -1367,20 +1450,14 @@ int cg_path_get_owner_uid(const char *path, uid_t *uid) {
start = startswith(slice, "user-");
if (!start)
- return -ENOENT;
- end = endswith(slice, ".slice");
+ return -ENXIO;
+ end = endswith(start, ".slice");
if (!end)
- return -ENOENT;
+ return -ENXIO;
- s = strndupa(start, end - start);
- if (!s)
- return -ENOENT;
-
- if (parse_uid(s, &u) < 0)
- return -EIO;
-
- if (uid)
- *uid = u;
+ *end = 0;
+ if (parse_uid(start, uid) < 0)
+ return -ENXIO;
return 0;
}
@@ -1398,34 +1475,36 @@ int cg_pid_get_owner_uid(pid_t pid, uid_t *uid) {
int cg_path_get_slice(const char *p, char **slice) {
const char *e = NULL;
- size_t m = 0;
assert(p);
assert(slice);
+ /* Finds the right-most slice unit from the beginning, but
+ * stops before we come to the first non-slice unit. */
+
for (;;) {
size_t n;
p += strspn(p, "/");
n = strcspn(p, "/");
- if (n <= 6 || memcmp(p + n - 6, ".slice", 6) != 0) {
- char *s;
+ if (!valid_slice_name(p, n)) {
- if (!e)
- return -ENOENT;
+ if (!e) {
+ char *s;
- s = strndup(e, m);
- if (!s)
- return -ENOMEM;
+ s = strdup("-.slice");
+ if (!s)
+ return -ENOMEM;
- *slice = s;
- return 0;
+ *slice = s;
+ return 0;
+ }
+
+ return cg_path_decode_unit(e, slice);
}
e = p;
- m = n;
-
p += n;
}
}
@@ -1443,6 +1522,33 @@ int cg_pid_get_slice(pid_t pid, char **slice) {
return cg_path_get_slice(cgroup, slice);
}
+int cg_path_get_user_slice(const char *p, char **slice) {
+ const char *t;
+ assert(p);
+ assert(slice);
+
+ t = skip_user_prefix(p);
+ if (!t)
+ return -ENXIO;
+
+ /* And now it looks pretty much the same as for a system
+ * slice, so let's just use the same parser from here on. */
+ return cg_path_get_slice(t, slice);
+}
+
+int cg_pid_get_user_slice(pid_t pid, char **slice) {
+ _cleanup_free_ char *cgroup = NULL;
+ int r;
+
+ assert(slice);
+
+ r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
+ if (r < 0)
+ return r;
+
+ return cg_path_get_user_slice(cgroup, slice);
+}
+
char *cg_escape(const char *p) {
bool need_prefix = false;
@@ -1532,28 +1638,47 @@ bool cg_controller_is_valid(const char *p, bool allow_named) {
int cg_slice_to_path(const char *unit, char **ret) {
_cleanup_free_ char *p = NULL, *s = NULL, *e = NULL;
const char *dash;
+ int r;
assert(unit);
assert(ret);
- if (!unit_name_is_valid(unit, TEMPLATE_INVALID))
+ if (streq(unit, "-.slice")) {
+ char *x;
+
+ x = strdup("");
+ if (!x)
+ return -ENOMEM;
+ *ret = x;
+ return 0;
+ }
+
+ if (!unit_name_is_valid(unit, UNIT_NAME_PLAIN))
return -EINVAL;
if (!endswith(unit, ".slice"))
return -EINVAL;
- p = unit_name_to_prefix(unit);
- if (!p)
- return -ENOMEM;
+ r = unit_name_to_prefix(unit, &p);
+ if (r < 0)
+ return r;
dash = strchr(p, '-');
+
+ /* Don't allow initial dashes */
+ if (dash == p)
+ return -EINVAL;
+
while (dash) {
_cleanup_free_ char *escaped = NULL;
char n[dash - p + sizeof(".slice")];
- strcpy(stpncpy(n, p, dash - p), ".slice");
+ /* Don't allow trailing or double dashes */
+ if (dash[1] == 0 || dash[1] == '-')
+ return -EINVAL;
- if (!unit_name_is_valid(n, TEMPLATE_INVALID))
+ strcpy(stpncpy(n, p, dash - p), ".slice");
+ if (!unit_name_is_valid(n, UNIT_NAME_PLAIN))
return -EINVAL;
escaped = cg_escape(n);
diff --git a/src/shared/cgroup-util.h b/src/shared/cgroup-util.h
index 96a3d3bafa..cbf7201370 100644
--- a/src/shared/cgroup-util.h
+++ b/src/shared/cgroup-util.h
@@ -104,6 +104,7 @@ 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_path_get_slice(const char *path, char **slice);
+int cg_path_get_user_slice(const char *path, char **slice);
int cg_shift_path(const char *cgroup, const char *cached_root, const char **shifted);
int cg_pid_get_path_shifted(pid_t pid, const char *cached_root, char **cgroup);
@@ -114,6 +115,7 @@ 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_pid_get_slice(pid_t pid, char **slice);
+int cg_pid_get_user_slice(pid_t pid, char **slice);
int cg_path_decode_unit(const char *cgroup, char **unit);
diff --git a/src/shared/clean-ipc.c b/src/shared/clean-ipc.c
index 39ab645133..48b10865da 100644
--- a/src/shared/clean-ipc.c
+++ b/src/shared/clean-ipc.c
@@ -24,12 +24,12 @@
#include <sys/sem.h>
#include <sys/msg.h>
#include <sys/stat.h>
-#include <sys/mman.h>
#include <fcntl.h>
#include <dirent.h>
#include <mqueue.h>
#include "util.h"
+#include "formats-util.h"
#include "strv.h"
#include "clean-ipc.h"
diff --git a/src/shared/clock-util.c b/src/shared/clock-util.c
index 96684681a4..e4e03df1e4 100644
--- a/src/shared/clock-util.c
+++ b/src/shared/clock-util.c
@@ -19,29 +19,16 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <assert.h>
-#include <string.h>
-#include <unistd.h>
#include <errno.h>
-#include <stdlib.h>
-#include <signal.h>
#include <stdio.h>
-#include <sys/types.h>
-#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
-#include <stdarg.h>
-#include <ctype.h>
-#include <sys/prctl.h>
#include <sys/time.h>
#include <linux/rtc.h>
#include "macro.h"
#include "util.h"
-#include "log.h"
-#include "strv.h"
#include "clock-util.h"
-#include "fileio.h"
int clock_get_hwclock(struct tm *tm) {
_cleanup_close_ int fd = -1;
diff --git a/src/shared/clock-util.h b/src/shared/clock-util.h
index 198a7b2753..8c2d235430 100644
--- a/src/shared/clock-util.h
+++ b/src/shared/clock-util.h
@@ -21,7 +21,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "util.h"
int clock_is_localtime(void);
int clock_set_timezone(int *min);
diff --git a/src/shared/condition.c b/src/shared/condition.c
index da7560f05f..24871b0dae 100644
--- a/src/shared/condition.c
+++ b/src/shared/condition.c
@@ -23,22 +23,21 @@
#include <errno.h>
#include <string.h>
#include <unistd.h>
-#include <sys/statvfs.h>
#include <fnmatch.h>
#include "sd-id128.h"
#include "util.h"
#include "virt.h"
#include "path-util.h"
-#include "fileio.h"
#include "architecture.h"
#include "smack-util.h"
#include "apparmor-util.h"
#include "ima-util.h"
#include "selinux-util.h"
#include "audit.h"
-#include "condition.h"
#include "cap-list.h"
+#include "hostname-util.h"
+#include "condition.h"
Condition* condition_new(ConditionType type, const char *parameter, bool trigger, bool negate) {
Condition *c;
@@ -46,7 +45,7 @@ Condition* condition_new(ConditionType type, const char *parameter, bool trigger
assert(type >= 0);
assert(type < _CONDITION_TYPE_MAX);
- assert(!parameter == (type == CONDITION_NULL));
+ assert((!parameter) == (type == CONDITION_NULL));
c = new0(Condition, 1);
if (!c)
@@ -102,7 +101,7 @@ static int condition_test_kernel_command_line(Condition *c) {
_cleanup_free_ char *word = NULL;
bool found;
- r = unquote_first_word(&p, &word, true);
+ r = unquote_first_word(&p, &word, UNQUOTE_RELAX);
if (r < 0)
return r;
if (r == 0)
@@ -350,7 +349,7 @@ static int condition_test_path_is_mount_point(Condition *c) {
assert(c->parameter);
assert(c->type == CONDITION_PATH_IS_MOUNT_POINT);
- return path_is_mount_point(c->parameter, true) > 0;
+ return path_is_mount_point(c->parameter, AT_SYMLINK_FOLLOW) > 0;
}
static int condition_test_path_is_read_write(Condition *c) {
diff --git a/src/shared/conf-files.c b/src/shared/conf-files.c
index db4937db88..da8745b284 100644
--- a/src/shared/conf-files.c
+++ b/src/shared/conf-files.c
@@ -19,13 +19,10 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <assert.h>
#include <string.h>
-#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
-#include <sys/stat.h>
#include <dirent.h>
#include "macro.h"
@@ -39,12 +36,13 @@
static int files_add(Hashmap *h, const char *root, const char *path, const char *suffix) {
_cleanup_closedir_ DIR *dir = NULL;
- char *dirpath;
+ const char *dirpath;
+ int r;
assert(path);
assert(suffix);
- dirpath = strjoina(root ? root : "", path);
+ dirpath = prefix_roota(root, path);
dir = opendir(dirpath);
if (!dir) {
@@ -56,7 +54,6 @@ static int files_add(Hashmap *h, const char *root, const char *path, const char
for (;;) {
struct dirent *de;
char *p;
- int r;
errno = 0;
de = readdir(dir);
diff --git a/src/shared/conf-files.h b/src/shared/conf-files.h
index 368c112eb3..3169a907f1 100644
--- a/src/shared/conf-files.h
+++ b/src/shared/conf-files.h
@@ -22,7 +22,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "macro.h"
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);
diff --git a/src/shared/conf-parser.c b/src/shared/conf-parser.c
index 0b1af6c577..7370c786f9 100644
--- a/src/shared/conf-parser.c
+++ b/src/shared/conf-parser.c
@@ -22,9 +22,7 @@
#include <string.h>
#include <stdio.h>
#include <errno.h>
-#include <assert.h>
#include <stdlib.h>
-#include <netinet/ether.h>
#include "conf-parser.h"
#include "conf-files.h"
@@ -34,54 +32,8 @@
#include "log.h"
#include "utf8.h"
#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,
- int 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,
- error,
- file, line, func,
- getpid() == 1 ? "UNIT=%s" : "USER_UNIT=%s", unit,
- LOG_MESSAGE_ID(SD_MESSAGE_CONFIG_ERROR),
- "CONFIG_FILE=%s", config_file,
- "CONFIG_LINE=%u", config_line,
- LOG_MESSAGE("[%s:%u] %s", config_file, config_line, msg),
- NULL);
- else
- r = log_struct_internal(level,
- error,
- file, line, func,
- LOG_MESSAGE_ID(SD_MESSAGE_CONFIG_ERROR),
- "CONFIG_FILE=%s", config_file,
- "CONFIG_LINE=%u", config_line,
- LOG_MESSAGE("[%s:%u] %s", config_file, config_line, msg),
- NULL);
-
- return r;
-}
-
int config_item_table_lookup(
const void *table,
const char *section,
@@ -492,7 +444,7 @@ int config_parse_many(const char *conf_file,
if (r < 0) \
log_syntax(unit, LOG_ERR, filename, line, -r, \
"Failed to parse %s value, ignoring: %s", \
- #vartype, rvalue); \
+ #type, rvalue); \
\
return 0; \
}
@@ -763,41 +715,30 @@ int config_parse_strv(const char *unit,
return 0;
}
-int config_parse_mode(const char *unit,
- const char *filename,
- unsigned line,
- const char *section,
+int config_parse_mode(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
unsigned section_line,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
mode_t *m = data;
- long l;
- char *x = NULL;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
- errno = 0;
- l = strtol(rvalue, &x, 8);
- if (!x || x == rvalue || *x || errno) {
- log_syntax(unit, LOG_ERR, filename, line, errno,
- "Failed to parse mode value, ignoring: %s", rvalue);
- return 0;
- }
-
- if (l < 0000 || l > 07777) {
- log_syntax(unit, LOG_ERR, filename, line, ERANGE,
- "Mode value out of range, ignoring: %s", rvalue);
+ if (parse_mode(rvalue, m) < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, errno, "Failed to parse mode value, ignoring: %s", rvalue);
return 0;
}
- *m = (mode_t) l;
return 0;
}
diff --git a/src/shared/conf-parser.h b/src/shared/conf-parser.h
index 7a2f855f9f..6152ee33b9 100644
--- a/src/shared/conf-parser.h
+++ b/src/shared/conf-parser.h
@@ -119,23 +119,6 @@ int config_parse_mode(const char *unit, const char *filename, unsigned line, con
int config_parse_log_facility(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_log_level(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int log_syntax_internal(
- const char *unit,
- int level,
- const char *file,
- int line,
- const char *func,
- const char *config_file,
- unsigned config_line,
- int error,
- const char *format, ...) _printf_(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 log_invalid_utf8(unit, level, config_file, config_line, error, rvalue) \
do { \
_cleanup_free_ char *_p = utf8_escape_invalid(rvalue); \
diff --git a/src/shared/copy.c b/src/shared/copy.c
index 0239a58066..1282cb88be 100644
--- a/src/shared/copy.c
+++ b/src/shared/copy.c
@@ -285,7 +285,7 @@ static int fd_copy_directory(
else if (S_ISBLK(buf.st_mode) || S_ISCHR(buf.st_mode))
q = fd_copy_node(dirfd(d), de->d_name, &buf, fdt, de->d_name);
else
- q = -ENOTSUP;
+ q = -EOPNOTSUPP;
if (q == -EEXIST && merge)
q = 0;
@@ -317,7 +317,7 @@ int copy_tree_at(int fdf, const char *from, int fdt, const char *to, bool merge)
else if (S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode))
return fd_copy_node(fdf, from, &st, fdt, to);
else
- return -ENOTSUP;
+ return -EOPNOTSUPP;
}
int copy_tree(const char *from, const char *to, bool merge) {
@@ -360,7 +360,7 @@ int copy_file_fd(const char *from, int fdt, bool try_reflink) {
}
int copy_file(const char *from, const char *to, int flags, mode_t mode, unsigned chattr_flags) {
- int fdt, r;
+ int fdt = -1, r;
assert(from);
assert(to);
@@ -372,7 +372,7 @@ int copy_file(const char *from, const char *to, int flags, mode_t mode, unsigned
}
if (chattr_flags != 0)
- (void) chattr_fd(fdt, true, chattr_flags);
+ (void) chattr_fd(fdt, chattr_flags, (unsigned) -1);
r = copy_file_fd(from, fdt, true);
if (r < 0) {
@@ -390,7 +390,7 @@ int copy_file(const char *from, const char *to, int flags, mode_t mode, unsigned
}
int copy_file_atomic(const char *from, const char *to, mode_t mode, bool replace, unsigned chattr_flags) {
- _cleanup_free_ char *t;
+ _cleanup_free_ char *t = NULL;
int r;
assert(from);
@@ -404,9 +404,15 @@ int copy_file_atomic(const char *from, const char *to, mode_t mode, bool replace
if (r < 0)
return r;
- if (renameat2(AT_FDCWD, t, AT_FDCWD, to, replace ? 0 : RENAME_NOREPLACE) < 0) {
- unlink_noerrno(t);
- return -errno;
+ if (replace) {
+ r = renameat(AT_FDCWD, t, AT_FDCWD, to);
+ if (r < 0)
+ r = -errno;
+ } else
+ r = rename_noreplace(AT_FDCWD, t, AT_FDCWD, to);
+ if (r < 0) {
+ (void) unlink_noerrno(t);
+ return r;
}
return 0;
@@ -415,7 +421,7 @@ int copy_file_atomic(const char *from, const char *to, mode_t mode, bool replace
int copy_times(int fdf, int fdt) {
struct timespec ut[2];
struct stat st;
- usec_t crtime;
+ usec_t crtime = 0;
assert(fdf >= 0);
assert(fdt >= 0);
diff --git a/src/shared/dev-setup.c b/src/shared/dev-setup.c
index e8b0810d23..25ad918b85 100644
--- a/src/shared/dev-setup.c
+++ b/src/shared/dev-setup.c
@@ -20,21 +20,15 @@
***/
#include <errno.h>
-#include <sys/stat.h>
#include <stdlib.h>
-#include <string.h>
-#include <assert.h>
#include <unistd.h>
-#include "dev-setup.h"
-#include "log.h"
-#include "macro.h"
#include "util.h"
#include "label.h"
+#include "path-util.h"
+#include "dev-setup.h"
-int dev_setup(const char *prefix) {
- const char *j, *k;
-
+int dev_setup(const char *prefix, uid_t uid, gid_t gid) {
static const char symlinks[] =
"-/proc/kcore\0" "/dev/core\0"
"/proc/self/fd\0" "/dev/fd\0"
@@ -42,7 +36,13 @@ int dev_setup(const char *prefix) {
"/proc/self/fd/1\0" "/dev/stdout\0"
"/proc/self/fd/2\0" "/dev/stderr\0";
+ const char *j, *k;
+ int r;
+
NULSTR_FOREACH_PAIR(j, k, symlinks) {
+ _cleanup_free_ char *link_name = NULL;
+ const char *n;
+
if (j[0] == '-') {
j++;
@@ -51,15 +51,21 @@ int dev_setup(const char *prefix) {
}
if (prefix) {
- _cleanup_free_ char *link_name = NULL;
-
- link_name = strjoin(prefix, "/", k, NULL);
+ link_name = prefix_root(prefix, k);
if (!link_name)
return -ENOMEM;
- symlink_label(j, link_name);
+ n = link_name;
} else
- symlink_label(j, k);
+ n = k;
+
+ r = symlink_label(j, n);
+ if (r < 0)
+ log_debug_errno(r, "Failed to symlink %s to %s: %m", j, n);
+
+ if (uid != UID_INVALID || gid != GID_INVALID)
+ if (lchown(n, uid, gid) < 0)
+ log_debug_errno(errno, "Failed to chown %s: %m", n);
}
return 0;
diff --git a/src/shared/dev-setup.h b/src/shared/dev-setup.h
index d41b6eefba..ab2748db7f 100644
--- a/src/shared/dev-setup.h
+++ b/src/shared/dev-setup.h
@@ -21,4 +21,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-int dev_setup(const char *pathprefix);
+#include <sys/types.h>
+
+int dev_setup(const char *prefix, uid_t uid, gid_t gid);
diff --git a/src/shared/device-nodes.c b/src/shared/device-nodes.c
index 73e9edd29d..9d5af72d27 100644
--- a/src/shared/device-nodes.c
+++ b/src/shared/device-nodes.c
@@ -19,21 +19,20 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdlib.h>
#include <stdio.h>
-#include <stdint.h>
-#include <sys/types.h>
#include "device-nodes.h"
#include "utf8.h"
int whitelisted_char_for_devnode(char c, const char *white) {
+
if ((c >= '0' && c <= '9') ||
(c >= 'A' && c <= 'Z') ||
(c >= 'a' && c <= 'z') ||
strchr("#+-.:=@_", c) != NULL ||
(white != NULL && strchr(white, c) != NULL))
return 1;
+
return 0;
}
@@ -48,27 +47,34 @@ int encode_devnode_name(const char *str, char *str_enc, size_t len) {
seqlen = utf8_encoded_valid_unichar(&str[i]);
if (seqlen > 1) {
+
if (len-j < (size_t)seqlen)
- goto err;
+ return -EINVAL;
+
memcpy(&str_enc[j], &str[i], seqlen);
j += seqlen;
i += (seqlen-1);
+
} else if (str[i] == '\\' || !whitelisted_char_for_devnode(str[i], NULL)) {
+
if (len-j < 4)
- goto err;
+ return -EINVAL;
+
sprintf(&str_enc[j], "\\x%02x", (unsigned char) str[i]);
j += 4;
+
} else {
if (len-j < 1)
- goto err;
+ return -EINVAL;
+
str_enc[j] = str[i];
j++;
}
}
+
if (len-j < 1)
- goto err;
+ return -EINVAL;
+
str_enc[j] = '\0';
return 0;
-err:
- return -EINVAL;
}
diff --git a/src/shared/dropin.c b/src/shared/dropin.c
index d1baad6192..963d05d32e 100644
--- a/src/shared/dropin.c
+++ b/src/shared/dropin.c
@@ -164,7 +164,7 @@ static int iterate_dir(
}
int unit_file_process_dir(
- Set * unit_path_cache,
+ Set *unit_path_cache,
const char *unit_path,
const char *name,
const char *suffix,
@@ -174,6 +174,7 @@ int unit_file_process_dir(
char ***strv) {
_cleanup_free_ char *path = NULL;
+ int r;
assert(unit_path);
assert(name);
@@ -184,22 +185,22 @@ int unit_file_process_dir(
return log_oom();
if (!unit_path_cache || set_get(unit_path_cache, path))
- iterate_dir(path, dependency, consumer, arg, strv);
+ (void) iterate_dir(path, dependency, consumer, arg, strv);
- if (unit_name_is_instance(name)) {
+ if (unit_name_is_valid(name, UNIT_NAME_INSTANCE)) {
_cleanup_free_ char *template = NULL, *p = NULL;
/* Also try the template dir */
- template = unit_name_template(name);
- if (!template)
- return log_oom();
+ r = unit_name_template(name, &template);
+ if (r < 0)
+ return log_error_errno(r, "Failed to generate template from unit name: %m");
p = strjoin(unit_path, "/", template, suffix, NULL);
if (!p)
return log_oom();
if (!unit_path_cache || set_get(unit_path_cache, p))
- iterate_dir(p, dependency, consumer, arg, strv);
+ (void) iterate_dir(p, dependency, consumer, arg, strv);
}
return 0;
diff --git a/src/shared/efivars.c b/src/shared/efivars.c
index a319574527..d34d977b9a 100644
--- a/src/shared/efivars.c
+++ b/src/shared/efivars.c
@@ -22,15 +22,49 @@
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
-#include <ctype.h>
-#include "acpi-fpdt.h"
#include "util.h"
#include "utf8.h"
+#include "virt.h"
#include "efivars.h"
#ifdef ENABLE_EFI
+#define LOAD_OPTION_ACTIVE 0x00000001
+#define MEDIA_DEVICE_PATH 0x04
+#define MEDIA_HARDDRIVE_DP 0x01
+#define MEDIA_FILEPATH_DP 0x04
+#define SIGNATURE_TYPE_GUID 0x02
+#define MBR_TYPE_EFI_PARTITION_TABLE_HEADER 0x02
+#define END_DEVICE_PATH_TYPE 0x7f
+#define END_ENTIRE_DEVICE_PATH_SUBTYPE 0xff
+#define EFI_OS_INDICATIONS_BOOT_TO_FW_UI 0x0000000000000001
+
+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_;
+
bool is_efi_boot(void) {
return access("/sys/firmware/efi", F_OK) >= 0;
}
@@ -53,12 +87,82 @@ static int read_flag(const char *varname) {
return r;
}
-int is_efi_secure_boot(void) {
- return read_flag("SecureBoot");
+bool is_efi_secure_boot(void) {
+ return read_flag("SecureBoot") > 0;
+}
+
+bool is_efi_secure_boot_setup_mode(void) {
+ return read_flag("SetupMode") > 0;
+}
+
+int efi_reboot_to_firmware_supported(void) {
+ int r;
+ size_t s;
+ uint64_t b;
+ _cleanup_free_ void *v = NULL;
+
+ if (!is_efi_boot() || detect_container(NULL) > 0)
+ return -EOPNOTSUPP;
+
+ r = efi_get_variable(EFI_VENDOR_GLOBAL, "OsIndicationsSupported", NULL, &v, &s);
+ if (r < 0)
+ return r;
+ else if (s != sizeof(uint64_t))
+ return -EINVAL;
+
+ b = *(uint64_t *)v;
+ b &= EFI_OS_INDICATIONS_BOOT_TO_FW_UI;
+ return b > 0 ? 0 : -EOPNOTSUPP;
+}
+
+static int get_os_indications(uint64_t *os_indication) {
+ int r;
+ size_t s;
+ _cleanup_free_ void *v = NULL;
+
+ r = efi_reboot_to_firmware_supported();
+ if (r < 0)
+ return r;
+
+ r = efi_get_variable(EFI_VENDOR_GLOBAL, "OsIndications", NULL, &v, &s);
+ if (r < 0)
+ return r;
+ else if (s != sizeof(uint64_t))
+ return -EINVAL;
+
+ *os_indication = *(uint64_t *)v;
+ return 0;
+}
+
+int efi_get_reboot_to_firmware(void) {
+ int r;
+ uint64_t b;
+
+ r = get_os_indications(&b);
+ if (r < 0)
+ return r;
+
+ return !!(b & EFI_OS_INDICATIONS_BOOT_TO_FW_UI);
}
-int is_efi_secure_boot_setup_mode(void) {
- return read_flag("SetupMode");
+int efi_set_reboot_to_firmware(bool value) {
+ int r;
+ uint64_t b, b_new;
+
+ r = get_os_indications(&b);
+ if (r < 0)
+ return r;
+
+ if (value)
+ b_new = b | EFI_OS_INDICATIONS_BOOT_TO_FW_UI;
+ else
+ b_new = b & ~EFI_OS_INDICATIONS_BOOT_TO_FW_UI;
+
+ /* Avoid writing to efi vars store if we can due to firmware bugs. */
+ if (b != b_new)
+ return efi_set_variable(EFI_VENDOR_GLOBAL, "OsIndications", &b_new, sizeof(uint64_t));
+
+ return 0;
}
int efi_get_variable(
@@ -73,7 +177,7 @@ int efi_get_variable(
uint32_t a;
ssize_t n;
struct stat st;
- void *r;
+ _cleanup_free_ void *buf = NULL;
assert(name);
assert(value);
@@ -101,25 +205,22 @@ int efi_get_variable(
if (n != sizeof(a))
return -EIO;
- r = malloc(st.st_size - 4 + 2);
- if (!r)
+ buf = malloc(st.st_size - 4 + 2);
+ if (!buf)
return -ENOMEM;
- n = read(fd, r, (size_t) st.st_size - 4);
- if (n < 0) {
- free(r);
+ n = read(fd, buf, (size_t) st.st_size - 4);
+ if (n < 0)
return -errno;
- }
- if (n != (ssize_t) st.st_size - 4) {
- free(r);
+ if (n != (ssize_t) st.st_size - 4)
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;
+ ((char*) buf)[st.st_size - 4] = 0;
+ ((char*) buf)[st.st_size - 4 + 1] = 0;
- *value = r;
+ *value = buf;
+ buf = NULL;
*size = (size_t) st.st_size - 4;
if (attribute)
@@ -128,6 +229,46 @@ int efi_get_variable(
return 0;
}
+int efi_set_variable(
+ sd_id128_t vendor,
+ const char *name,
+ const void *value,
+ size_t size) {
+
+ struct var {
+ uint32_t attr;
+ char buf[];
+ } _packed_ * _cleanup_free_ buf = NULL;
+ _cleanup_free_ char *p = NULL;
+ _cleanup_close_ int fd = -1;
+
+ assert(name);
+
+ 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;
+
+ if (size == 0) {
+ if (unlink(p) < 0)
+ return -errno;
+ return 0;
+ }
+
+ fd = open(p, O_WRONLY|O_CREAT|O_NOCTTY|O_CLOEXEC, 0644);
+ if (fd < 0)
+ return -errno;
+
+ buf = malloc(sizeof(uint32_t) + size);
+ if (!buf)
+ return -ENOMEM;
+
+ buf->attr = EFI_VARIABLE_NON_VOLATILE|EFI_VARIABLE_BOOTSERVICE_ACCESS|EFI_VARIABLE_RUNTIME_ACCESS;
+ memcpy(buf->buf, value, size);
+
+ return loop_write(fd, buf, sizeof(uint32_t) + size, false);
+}
+
int efi_get_variable_string(sd_id128_t vendor, const char *name, char **p) {
_cleanup_free_ void *s = NULL;
size_t ss = 0;
@@ -179,47 +320,22 @@ 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 **path,
+ bool *active) {
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;
+ _cleanup_free_ char *s = NULL, *p = NULL;
sd_id128_t p_uuid = SD_ID128_NULL;
- int err;
+ int r;
- 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;
+ xsprintf(boot_id, "Boot%04X", id);
+ r = efi_get_variable(EFI_VENDOR_GLOBAL, boot_id, NULL, (void **)&buf, &l);
+ if (r < 0)
+ return r;
if (l < sizeof(struct boot_option))
return -ENOENT;
@@ -230,10 +346,8 @@ int efi_get_boot_option(
if (title) {
s = utf16_to_utf8(header->title, title_size);
- if (!s) {
- err = -ENOMEM;
- goto err;
- }
+ if (!s)
+ return -ENOMEM;
}
if (header->path_len > 0) {
@@ -250,23 +364,23 @@ int efi_get_boot_option(
break;
/* Type 0x7F – End of Hardware Device Path, Sub-Type 0xFF – End Entire Device Path */
- if (dpath->type == 0x7f && dpath->sub_type == 0xff)
+ if (dpath->type == END_DEVICE_PATH_TYPE && dpath->sub_type == END_ENTIRE_DEVICE_PATH_SUBTYPE)
break;
dnext += dpath->length;
/* Type 0x04 – Media Device Path */
- if (dpath->type != 0x04)
+ if (dpath->type != MEDIA_DEVICE_PATH)
continue;
/* Sub-Type 1 – Hard Drive */
- if (dpath->sub_type == 0x01) {
+ if (dpath->sub_type == MEDIA_HARDDRIVE_DP) {
/* 0x02 – GUID Partition Table */
- if (dpath->drive.mbr_type != 0x02)
+ if (dpath->drive.mbr_type != MBR_TYPE_EFI_PARTITION_TABLE_HEADER)
continue;
/* 0x02 – GUID signature */
- if (dpath->drive.signature_type != 0x02)
+ if (dpath->drive.signature_type != SIGNATURE_TYPE_GUID)
continue;
if (part_uuid)
@@ -275,29 +389,135 @@ int efi_get_boot_option(
}
/* Sub-Type 4 – File Path */
- if (dpath->sub_type == 0x04 && !p && path) {
+ if (dpath->sub_type == MEDIA_FILEPATH_DP && !p && path) {
p = utf16_to_utf8(dpath->path, dpath->length-4);
+ efi_tilt_backslashes(p);
continue;
}
}
}
- if (title)
+ if (title) {
*title = s;
+ s = NULL;
+ }
if (part_uuid)
*part_uuid = p_uuid;
- if (path)
+ if (path) {
*path = p;
+ p = NULL;
+ }
+ if (active)
+ *active = !!(header->attr & LOAD_OPTION_ACTIVE);
return 0;
-err:
- free(s);
- free(p);
- return err;
+}
+
+static void to_utf16(uint16_t *dest, const char *src) {
+ int i;
+
+ for (i = 0; src[i] != '\0'; i++)
+ dest[i] = src[i];
+ dest[i] = '\0';
+}
+
+struct guid {
+ uint32_t u1;
+ uint16_t u2;
+ uint16_t u3;
+ uint8_t u4[8];
+} _packed_;
+
+static void id128_to_efi_guid(sd_id128_t id, void *guid) {
+ struct guid *uuid = guid;
+
+ uuid->u1 = id.bytes[0] << 24 | id.bytes[1] << 16 | id.bytes[2] << 8 | id.bytes[3];
+ uuid->u2 = id.bytes[4] << 8 | id.bytes[5];
+ uuid->u3 = id.bytes[6] << 8 | id.bytes[7];
+ memcpy(uuid->u4, id.bytes+8, sizeof(uuid->u4));
+}
+
+static uint16_t *tilt_slashes(uint16_t *s) {
+ uint16_t *p;
+
+ for (p = s; *p; p++)
+ if (*p == '/')
+ *p = '\\';
+
+ return s;
+}
+
+int efi_add_boot_option(uint16_t id, const char *title,
+ uint32_t part, uint64_t pstart, uint64_t psize,
+ sd_id128_t part_uuid, const char *path) {
+ char boot_id[9];
+ size_t size;
+ size_t title_len;
+ size_t path_len;
+ struct boot_option *option;
+ struct device_path *devicep;
+ _cleanup_free_ char *buf = NULL;
+
+ title_len = (strlen(title)+1) * 2;
+ path_len = (strlen(path)+1) * 2;
+
+ buf = calloc(sizeof(struct boot_option) + title_len +
+ sizeof(struct drive_path) +
+ sizeof(struct device_path) + path_len, 1);
+ if (!buf)
+ return -ENOMEM;
+
+ /* header */
+ option = (struct boot_option *)buf;
+ option->attr = LOAD_OPTION_ACTIVE;
+ option->path_len = offsetof(struct device_path, drive) + sizeof(struct drive_path) +
+ offsetof(struct device_path, path) + path_len +
+ offsetof(struct device_path, path);
+ to_utf16(option->title, title);
+ size = offsetof(struct boot_option, title) + title_len;
+
+ /* partition info */
+ devicep = (struct device_path *)(buf + size);
+ devicep->type = MEDIA_DEVICE_PATH;
+ devicep->sub_type = MEDIA_HARDDRIVE_DP;
+ devicep->length = offsetof(struct device_path, drive) + sizeof(struct drive_path);
+ devicep->drive.part_nr = part;
+ devicep->drive.part_start = pstart;
+ devicep->drive.part_size = psize;
+ devicep->drive.signature_type = SIGNATURE_TYPE_GUID;
+ devicep->drive.mbr_type = MBR_TYPE_EFI_PARTITION_TABLE_HEADER;
+ id128_to_efi_guid(part_uuid, devicep->drive.signature);
+ size += devicep->length;
+
+ /* path to loader */
+ devicep = (struct device_path *)(buf + size);
+ devicep->type = MEDIA_DEVICE_PATH;
+ devicep->sub_type = MEDIA_FILEPATH_DP;
+ devicep->length = offsetof(struct device_path, path) + path_len;
+ to_utf16(devicep->path, path);
+ tilt_slashes(devicep->path);
+ size += devicep->length;
+
+ /* end of path */
+ devicep = (struct device_path *)(buf + size);
+ devicep->type = END_DEVICE_PATH_TYPE;
+ devicep->sub_type = END_ENTIRE_DEVICE_PATH_SUBTYPE;
+ devicep->length = offsetof(struct device_path, path);
+ size += devicep->length;
+
+ xsprintf(boot_id, "Boot%04X", id);
+ return efi_set_variable(EFI_VENDOR_GLOBAL, boot_id, buf, size);
+}
+
+int efi_remove_boot_option(uint16_t id) {
+ char boot_id[9];
+
+ xsprintf(boot_id, "Boot%04X", id);
+ return efi_set_variable(EFI_VENDOR_GLOBAL, boot_id, NULL, 0);
}
int efi_get_boot_order(uint16_t **order) {
- void *buf;
+ _cleanup_free_ void *buf = NULL;
size_t l;
int r;
@@ -305,21 +525,22 @@ int efi_get_boot_order(uint16_t **order) {
if (r < 0)
return r;
- if (l <= 0) {
- free(buf);
+ if (l <= 0)
return -ENOENT;
- }
- if ((l % sizeof(uint16_t) > 0) ||
- (l / sizeof(uint16_t) > INT_MAX)) {
- free(buf);
+ if (l % sizeof(uint16_t) > 0 ||
+ l / sizeof(uint16_t) > INT_MAX)
return -EINVAL;
- }
*order = buf;
+ buf = NULL;
return (int) (l / sizeof(uint16_t));
}
+int efi_set_boot_order(uint16_t *order, size_t n) {
+ return efi_set_variable(EFI_VENDOR_GLOBAL, "BootOrder", order, n * sizeof(uint16_t));
+}
+
static int boot_id_hex(const char s[4]) {
int i;
int id = 0;
@@ -344,8 +565,9 @@ static int cmp_uint16(const void *_a, const void *_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;
+ _cleanup_free_ uint16_t *list = NULL;
+ size_t alloc = 0;
+ int count = 0;
assert(options);
@@ -353,9 +575,8 @@ int efi_get_boot_options(uint16_t **options) {
if (!dir)
return -errno;
- FOREACH_DIRENT(de, dir, r = -errno; goto fail) {
+ FOREACH_DIRENT(de, dir, return -errno) {
int id;
- uint16_t *t;
if (strncmp(de->d_name, "Boot", 4) != 0)
continue;
@@ -370,24 +591,17 @@ int efi_get_boot_options(uint16_t **options) {
if (id < 0)
continue;
- t = realloc(list, (count + 1) * sizeof(uint16_t));
- if (!t) {
- r = -ENOMEM;
- goto fail;
- }
+ if (!GREEDY_REALLOC(list, alloc, count + 1))
+ return -ENOMEM;
- list = t;
- list[count ++] = id;
+ list[count++] = id;
}
qsort_safe(list, count, sizeof(uint16_t), cmp_uint16);
*options = list;
+ list = NULL;
return count;
-
-fail:
- free(list);
- return r;
}
static int read_usec(sd_id128_t vendor, const char *name, usec_t *u) {
@@ -463,3 +677,13 @@ int efi_loader_get_device_part_uuid(sd_id128_t *u) {
}
#endif
+
+char *efi_tilt_backslashes(char *s) {
+ char *p;
+
+ for (p = s; *p; p++)
+ if (*p == '\\')
+ *p = '/';
+
+ return s;
+}
diff --git a/src/shared/efivars.h b/src/shared/efivars.h
index 7921bedc9f..e953a12737 100644
--- a/src/shared/efivars.h
+++ b/src/shared/efivars.h
@@ -21,8 +21,6 @@
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"
@@ -30,17 +28,103 @@
#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)
+#define EFI_VARIABLE_NON_VOLATILE 0x0000000000000001
+#define EFI_VARIABLE_BOOTSERVICE_ACCESS 0x0000000000000002
+#define EFI_VARIABLE_RUNTIME_ACCESS 0x0000000000000004
+
+#ifdef ENABLE_EFI
bool is_efi_boot(void);
-int is_efi_secure_boot(void);
-int is_efi_secure_boot_setup_mode(void);
+bool is_efi_secure_boot(void);
+bool is_efi_secure_boot_setup_mode(void);
+int efi_reboot_to_firmware_supported(void);
+int efi_get_reboot_to_firmware(void);
+int efi_set_reboot_to_firmware(bool value);
int efi_get_variable(sd_id128_t vendor, const char *name, uint32_t *attribute, void **value, size_t *size);
+int efi_set_variable(sd_id128_t vendor, const char *name, const 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_option(uint16_t nr, char **title, sd_id128_t *part_uuid, char **path, bool *active);
+int efi_add_boot_option(uint16_t id, const char *title, uint32_t part, uint64_t pstart, uint64_t psize, sd_id128_t part_uuid, const char *path);
+int efi_remove_boot_option(uint16_t id);
int efi_get_boot_order(uint16_t **order);
+int efi_set_boot_order(uint16_t *order, size_t n);
int efi_get_boot_options(uint16_t **options);
int efi_loader_get_device_part_uuid(sd_id128_t *u);
int efi_loader_get_boot_usec(usec_t *firmware, usec_t *loader);
+
+#else
+
+static inline bool is_efi_boot(void) {
+ return false;
+}
+
+static inline bool is_efi_secure_boot(void) {
+ return false;
+}
+
+static inline bool is_efi_secure_boot_setup_mode(void) {
+ return false;
+}
+
+static inline int efi_reboot_to_firmware_supported(void) {
+ return -EOPNOTSUPP;
+}
+
+static inline int efi_get_reboot_to_firmware(void) {
+ return -EOPNOTSUPP;
+}
+
+static inline int efi_set_reboot_to_firmware(bool value) {
+ return -EOPNOTSUPP;
+}
+
+static inline int efi_get_variable(sd_id128_t vendor, const char *name, uint32_t *attribute, void **value, size_t *size) {
+ return -EOPNOTSUPP;
+}
+
+static inline int efi_set_variable(sd_id128_t vendor, const char *name, const void *value, size_t size) {
+ return -EOPNOTSUPP;
+}
+
+static inline int efi_get_variable_string(sd_id128_t vendor, const char *name, char **p) {
+ return -EOPNOTSUPP;
+}
+
+static inline int efi_get_boot_option(uint16_t nr, char **title, sd_id128_t *part_uuid, char **path, bool *active) {
+ return -EOPNOTSUPP;
+}
+
+static inline int efi_add_boot_option(uint16_t id, const char *title, uint32_t part, uint64_t pstart, uint64_t psize, sd_id128_t part_uuid, const char *path) {
+ return -EOPNOTSUPP;
+}
+
+static inline int efi_remove_boot_option(uint16_t id) {
+ return -EOPNOTSUPP;
+}
+
+static inline int efi_get_boot_order(uint16_t **order) {
+ return -EOPNOTSUPP;
+}
+
+static inline int efi_set_boot_order(uint16_t *order, size_t n) {
+ return -EOPNOTSUPP;
+}
+
+static inline int efi_get_boot_options(uint16_t **options) {
+ return -EOPNOTSUPP;
+}
+
+static inline int efi_loader_get_device_part_uuid(sd_id128_t *u) {
+ return -EOPNOTSUPP;
+}
+
+static inline int efi_loader_get_boot_usec(usec_t *firmware, usec_t *loader) {
+ return -EOPNOTSUPP;
+}
+
+#endif
+
+char *efi_tilt_backslashes(char *s);
diff --git a/src/shared/env-util.c b/src/shared/env-util.c
index 038246d21b..ac7bbdc711 100644
--- a/src/shared/env-util.c
+++ b/src/shared/env-util.c
@@ -20,7 +20,6 @@
***/
#include <limits.h>
-#include <sys/param.h>
#include <unistd.h>
#include "strv.h"
@@ -449,3 +448,147 @@ char **strv_env_clean_with_callback(char **e, void (*invalid_callback)(const cha
return e;
}
+
+char *replace_env(const char *format, char **env) {
+ enum {
+ WORD,
+ CURLY,
+ VARIABLE
+ } state = WORD;
+
+ const char *e, *word = format;
+ char *r = NULL, *k;
+
+ assert(format);
+
+ for (e = format; *e; e ++) {
+
+ switch (state) {
+
+ case WORD:
+ if (*e == '$')
+ state = CURLY;
+ break;
+
+ case CURLY:
+ if (*e == '{') {
+ k = strnappend(r, word, e-word-1);
+ if (!k)
+ goto fail;
+
+ free(r);
+ r = k;
+
+ word = e-1;
+ state = VARIABLE;
+
+ } else if (*e == '$') {
+ k = strnappend(r, word, e-word);
+ if (!k)
+ goto fail;
+
+ free(r);
+ r = k;
+
+ word = e+1;
+ state = WORD;
+ } else
+ state = WORD;
+ break;
+
+ case VARIABLE:
+ if (*e == '}') {
+ const char *t;
+
+ t = strempty(strv_env_get_n(env, word+2, e-word-2));
+
+ k = strappend(r, t);
+ if (!k)
+ goto fail;
+
+ free(r);
+ r = k;
+
+ word = e+1;
+ state = WORD;
+ }
+ break;
+ }
+ }
+
+ k = strnappend(r, word, e-word);
+ if (!k)
+ goto fail;
+
+ free(r);
+ return k;
+
+fail:
+ free(r);
+ return NULL;
+}
+
+char **replace_env_argv(char **argv, char **env) {
+ char **ret, **i;
+ unsigned k = 0, l = 0;
+
+ l = strv_length(argv);
+
+ ret = new(char*, l+1);
+ if (!ret)
+ return NULL;
+
+ STRV_FOREACH(i, argv) {
+
+ /* If $FOO appears as single word, replace it by the split up variable */
+ if ((*i)[0] == '$' && (*i)[1] != '{') {
+ char *e;
+ char **w, **m = NULL;
+ unsigned q;
+
+ e = strv_env_get(env, *i+1);
+ if (e) {
+ int r;
+
+ r = strv_split_quoted(&m, e, UNQUOTE_RELAX);
+ if (r < 0) {
+ ret[k] = NULL;
+ strv_free(ret);
+ return NULL;
+ }
+ } else
+ m = NULL;
+
+ q = strv_length(m);
+ l = l + q - 1;
+
+ w = realloc(ret, sizeof(char*) * (l+1));
+ if (!w) {
+ ret[k] = NULL;
+ strv_free(ret);
+ strv_free(m);
+ return NULL;
+ }
+
+ ret = w;
+ if (m) {
+ memcpy(ret + k, m, q * sizeof(char*));
+ free(m);
+ }
+
+ k += q;
+ continue;
+ }
+
+ /* If ${FOO} appears as part of a word, replace it by the variable as-is */
+ ret[k] = replace_env(*i, env);
+ if (!ret[k]) {
+ strv_free(ret);
+ return NULL;
+ }
+ k++;
+ }
+
+ ret[k] = NULL;
+ return ret;
+}
diff --git a/src/shared/env-util.h b/src/shared/env-util.h
index 618441a655..803aa61cad 100644
--- a/src/shared/env-util.h
+++ b/src/shared/env-util.h
@@ -22,7 +22,6 @@
***/
#include <stdbool.h>
-#include <sys/types.h>
#include "macro.h"
@@ -30,6 +29,9 @@ bool env_name_is_valid(const char *e);
bool env_value_is_valid(const char *e);
bool env_assignment_is_valid(const char *e);
+char *replace_env(const char *format, char **env);
+char **replace_env_argv(char **argv, char **env);
+
bool strv_env_is_valid(char **e);
#define strv_env_clean(l) strv_env_clean_with_callback(l, NULL, NULL)
char **strv_env_clean_with_callback(char **l, void (*invalid_callback)(const char *p, void *userdata), void *userdata);
diff --git a/src/shared/errno-list.c b/src/shared/errno-list.c
index c63296f292..34d1331486 100644
--- a/src/shared/errno-list.c
+++ b/src/shared/errno-list.c
@@ -19,7 +19,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <errno.h>
#include <string.h>
#include "util.h"
diff --git a/src/shared/exit-status.c b/src/shared/exit-status.c
index 5c73b4d3c0..c09efdd2cb 100644
--- a/src/shared/exit-status.c
+++ b/src/shared/exit-status.c
@@ -20,7 +20,6 @@
***/
#include <stdlib.h>
-#include <sys/wait.h>
#include "exit-status.h"
#include "set.h"
@@ -167,7 +166,7 @@ const char* exit_status_to_string(ExitStatus status, ExitStatusLevel level) {
return "NOPERMISSION";
case EXIT_NOTINSTALLED:
- return "NOTINSSTALLED";
+ return "NOTINSTALLED";
case EXIT_NOTCONFIGURED:
return "NOTCONFIGURED";
@@ -226,3 +225,17 @@ bool exit_status_set_is_empty(ExitStatusSet *x) {
return set_isempty(x->status) && set_isempty(x->signal);
}
+
+bool exit_status_set_test(ExitStatusSet *x, int code, int status) {
+
+ if (exit_status_set_is_empty(x))
+ return false;
+
+ if (code == CLD_EXITED && set_contains(x->status, INT_TO_PTR(status)))
+ return true;
+
+ if (IN_SET(code, CLD_KILLED, CLD_DUMPED) && set_contains(x->signal, INT_TO_PTR(status)))
+ return true;
+
+ return false;
+}
diff --git a/src/shared/exit-status.h b/src/shared/exit-status.h
index 1d774f25dc..7259cd1d18 100644
--- a/src/shared/exit-status.h
+++ b/src/shared/exit-status.h
@@ -100,3 +100,4 @@ bool is_clean_exit_lsb(int code, int status, ExitStatusSet *success_status);
void exit_status_set_free(ExitStatusSet *x);
bool exit_status_set_is_empty(ExitStatusSet *x);
+bool exit_status_set_test(ExitStatusSet *x, int code, int status);
diff --git a/src/shared/fdset.c b/src/shared/fdset.c
index 9e35ce5cec..6101b628ec 100644
--- a/src/shared/fdset.c
+++ b/src/shared/fdset.c
@@ -22,7 +22,6 @@
#include <errno.h>
#include <dirent.h>
#include <fcntl.h>
-#include <unistd.h>
#include "set.h"
#include "util.h"
@@ -33,7 +32,7 @@
#define MAKE_SET(s) ((Set*) s)
#define MAKE_FDSET(s) ((FDSet*) s)
-/* Make sure we can distuingish fd 0 and NULL */
+/* Make sure we can distinguish fd 0 and NULL */
#define FD_TO_PTR(fd) INT_TO_PTR((fd)+1)
#define PTR_TO_FD(p) (PTR_TO_INT(p)-1)
diff --git a/src/shared/fdset.h b/src/shared/fdset.h
index c3c5e52286..340438d7c4 100644
--- a/src/shared/fdset.h
+++ b/src/shared/fdset.h
@@ -22,7 +22,6 @@
***/
#include "set.h"
-#include "util.h"
typedef struct FDSet FDSet;
diff --git a/src/shared/fileio-label.c b/src/shared/fileio-label.c
index 5fd69e0580..bec988ca78 100644
--- a/src/shared/fileio-label.c
+++ b/src/shared/fileio-label.c
@@ -19,13 +19,9 @@
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 "util.h"
#include "selinux-util.h"
-#include "label.h"
#include "fileio-label.h"
int write_string_file_atomic_label(const char *fn, const char *line) {
diff --git a/src/shared/formats-util.h b/src/shared/formats-util.h
new file mode 100644
index 0000000000..ce516b117d
--- /dev/null
+++ b/src/shared/formats-util.h
@@ -0,0 +1,63 @@
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2015 Ronny Chevalier
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser 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>
+
+#if SIZEOF_PID_T == 4
+# define PID_PRI PRIi32
+#elif SIZEOF_PID_T == 2
+# define PID_PRI PRIi16
+#else
+# error Unknown pid_t size
+#endif
+#define PID_FMT "%" PID_PRI
+
+#if SIZEOF_UID_T == 4
+# define UID_FMT "%" PRIu32
+#elif SIZEOF_UID_T == 2
+# define UID_FMT "%" PRIu16
+#else
+# error Unknown uid_t size
+#endif
+
+#if SIZEOF_GID_T == 4
+# define GID_FMT "%" PRIu32
+#elif SIZEOF_GID_T == 2
+# define GID_FMT "%" PRIu16
+#else
+# error Unknown gid_t size
+#endif
+
+#if SIZEOF_TIME_T == 8
+# define PRI_TIME PRIi64
+#elif SIZEOF_TIME_T == 4
+# define PRI_TIME PRIu32
+#else
+# error Unknown time_t size
+#endif
+
+#if SIZEOF_RLIM_T == 8
+# define RLIM_FMT "%" PRIu64
+#elif SIZEOF_RLIM_T == 4
+# define RLIM_FMT "%" PRIu32
+#else
+# error Unknown rlim_t size
+#endif
diff --git a/src/shared/fstab-util.c b/src/shared/fstab-util.c
index cf317e17bd..e231a0ff80 100644
--- a/src/shared/fstab-util.c
+++ b/src/shared/fstab-util.c
@@ -125,6 +125,36 @@ answer:
return !!n;
}
+int fstab_extract_values(const char *opts, const char *name, char ***values) {
+ _cleanup_strv_free_ char **optsv = NULL, **res = NULL;
+ char **s;
+
+ assert(opts);
+ assert(name);
+ assert(values);
+
+ optsv = strv_split(opts, ",");
+ if (!optsv)
+ return -ENOMEM;
+
+ STRV_FOREACH(s, optsv) {
+ char *arg;
+ int r;
+
+ arg = startswith(*s, name);
+ if (!arg || *arg != '=')
+ continue;
+ r = strv_extend(&res, arg + 1);
+ if (r < 0)
+ return r;
+ }
+
+ *values = res;
+ res = NULL;
+
+ return !!*values;
+}
+
int fstab_find_pri(const char *options, int *ret) {
_cleanup_free_ char *opt = NULL;
int r;
diff --git a/src/shared/fstab-util.h b/src/shared/fstab-util.h
index 9f6b32eaf4..387c562a96 100644
--- a/src/shared/fstab-util.h
+++ b/src/shared/fstab-util.h
@@ -28,6 +28,8 @@
int fstab_filter_options(const char *opts, const char *names,
const char **namefound, char **value, char **filtered);
+int fstab_extract_values(const char *opts, const char *name, char ***values);
+
static inline bool fstab_test_option(const char *opts, const char *names) {
return !!fstab_filter_options(opts, names, NULL, NULL, NULL);
}
diff --git a/src/shared/fw-util.c b/src/shared/fw-util.c
index ceb1ae508c..6b3599d90d 100644
--- a/src/shared/fw-util.c
+++ b/src/shared/fw-util.c
@@ -91,10 +91,10 @@ int fw_add_masquerade(
int r;
if (af != AF_INET)
- return -ENOTSUP;
+ return -EOPNOTSUPP;
if (protocol != 0 && protocol != IPPROTO_TCP && protocol != IPPROTO_UDP)
- return -ENOTSUP;
+ return -EOPNOTSUPP;
h = iptc_init("nat");
if (!h)
@@ -175,10 +175,10 @@ int fw_add_local_dnat(
assert(add || !previous_remote);
if (af != AF_INET)
- return -ENOTSUP;
+ return -EOPNOTSUPP;
if (protocol != IPPROTO_TCP && protocol != IPPROTO_UDP)
- return -ENOTSUP;
+ return -EOPNOTSUPP;
if (local_port <= 0)
return -EINVAL;
diff --git a/src/shared/fw-util.h b/src/shared/fw-util.h
index 698cc43daa..93152e3978 100644
--- a/src/shared/fw-util.h
+++ b/src/shared/fw-util.h
@@ -60,7 +60,7 @@ static inline int fw_add_masquerade(
const char *out_interface,
const union in_addr_union *destination,
unsigned destination_prefixlen) {
- return -ENOTSUP;
+ return -EOPNOTSUPP;
}
static inline int fw_add_local_dnat(
@@ -76,7 +76,7 @@ static inline int fw_add_local_dnat(
const union in_addr_union *remote,
uint16_t remote_port,
const union in_addr_union *previous_remote) {
- return -ENOTSUP;
+ return -EOPNOTSUPP;
}
#endif
diff --git a/src/shared/generator.c b/src/shared/generator.c
index 7f16d5cbef..807569a1b8 100644
--- a/src/shared/generator.c
+++ b/src/shared/generator.c
@@ -19,7 +19,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <string.h>
#include <unistd.h>
#include "util.h"
@@ -29,17 +28,63 @@
#include "generator.h"
#include "path-util.h"
#include "fstab-util.h"
+#include "fileio.h"
#include "dropin.h"
+static int write_fsck_sysroot_service(const char *dir, const char *what) {
+ const char *unit;
+ _cleanup_free_ char *device = NULL;
+ _cleanup_fclose_ FILE *f = NULL;
+ int r;
+
+ unit = strjoina(dir, "/systemd-fsck-root.service");
+ log_debug("Creating %s", unit);
+
+ r = unit_name_from_path(what, ".device", &device);
+ if (r < 0)
+ return log_error_errno(r, "Failed to convert device \"%s\" to unit name: %m", what);
+
+ f = fopen(unit, "wxe");
+ if (!f)
+ return log_error_errno(errno, "Failed to create unit file %s: %m", unit);
+
+ fprintf(f,
+ "# Automatically generated by %1$s\n\n"
+ "[Unit]\n"
+ "Documentation=man:systemd-fsck-root.service(8)\n"
+ "Description=File System Check on %2$s\n"
+ "DefaultDependencies=no\n"
+ "BindsTo=%3$s\n"
+ "After=%3$s\n"
+ "Before=shutdown.target\n"
+ "\n"
+ "[Service]\n"
+ "Type=oneshot\n"
+ "RemainAfterExit=yes\n"
+ "ExecStart=" SYSTEMD_FSCK_PATH " %2$s\n"
+ "TimeoutSec=0\n",
+ program_invocation_short_name,
+ what,
+ device);
+
+ r = fflush_and_check(f);
+ if (r < 0)
+ return log_error_errno(r, "Failed to write unit file %s: %m", unit);
+
+ return 0;
+}
+
int generator_write_fsck_deps(
FILE *f,
- const char *dest,
+ const char *dir,
const char *what,
const char *where,
const char *fstype) {
+ int r;
+
assert(f);
- assert(dest);
+ assert(dir);
assert(what);
assert(where);
@@ -49,7 +94,6 @@ int generator_write_fsck_deps(
}
if (!isempty(fstype) && !streq(fstype, "auto")) {
- int r;
r = fsck_exists(fstype);
if (r == -ENOENT) {
/* treat missing check as essentially OK */
@@ -59,34 +103,48 @@ int generator_write_fsck_deps(
return log_warning_errno(r, "Checking was requested for %s, but fsck.%s cannot be used: %m", what, fstype);
}
- if (streq(where, "/")) {
+ if (path_equal(where, "/")) {
char *lnk;
- lnk = strjoina(dest, "/" SPECIAL_LOCAL_FS_TARGET ".wants/systemd-fsck-root.service");
+ lnk = strjoina(dir, "/" SPECIAL_LOCAL_FS_TARGET ".wants/systemd-fsck-root.service");
mkdir_parents(lnk, 0755);
if (symlink(SYSTEM_DATA_UNIT_PATH "/systemd-fsck-root.service", lnk) < 0)
return log_error_errno(errno, "Failed to create symlink %s: %m", lnk);
} else {
- _cleanup_free_ char *fsck = NULL;
+ _cleanup_free_ char *_fsck = NULL;
+ const char *fsck;
+
+ if (in_initrd() && path_equal(where, "/sysroot")) {
+ r = write_fsck_sysroot_service(dir, what);
+ if (r < 0)
+ return r;
+
+ fsck = "systemd-fsck-root.service";
+ } else {
+ r = unit_name_from_path_instance("systemd-fsck", what, ".service", &_fsck);
+ if (r < 0)
+ return log_error_errno(r, "Failed to create fsck service name: %m");
- fsck = unit_name_from_path_instance("systemd-fsck", what, ".service");
- if (!fsck)
- return log_oom();
+ fsck = _fsck;
+ }
fprintf(f,
- "RequiresOverridable=%s\n"
- "After=%s\n",
- fsck,
+ "RequiresOverridable=%1$s\n"
+ "After=%1$s\n",
fsck);
}
return 0;
}
-int generator_write_timeouts(const char *dir, const char *what, const char *where,
- const char *opts, char **filtered) {
+int generator_write_timeouts(
+ const char *dir,
+ const char *what,
+ const char *where,
+ const char *opts,
+ char **filtered) {
/* Allow configuration how long we wait for a device that
* backs a mount point to show up. This is useful to support
@@ -104,8 +162,7 @@ int generator_write_timeouts(const char *dir, const char *what, const char *wher
r = parse_sec(timeout, &u);
if (r < 0) {
- log_warning("Failed to parse timeout for %s, ignoring: %s",
- where, timeout);
+ log_warning("Failed to parse timeout for %s, ignoring: %s", where, timeout);
return 0;
}
@@ -113,9 +170,9 @@ int generator_write_timeouts(const char *dir, const char *what, const char *wher
if (!node)
return log_oom();
- unit = unit_name_from_path(node, ".device");
- if (!unit)
- return log_oom();
+ r = unit_name_from_path(node, ".device", &unit);
+ if (r < 0)
+ return log_error_errno(r, "Failed to make unit name from path: %m");
return write_drop_in_format(dir, unit, 50, "device-timeout",
"# Automatically generated by %s\n\n"
diff --git a/src/shared/generator.h b/src/shared/generator.h
index 64bd28f596..6c3f38abba 100644
--- a/src/shared/generator.h
+++ b/src/shared/generator.h
@@ -23,7 +23,16 @@
#include <stdio.h>
-int generator_write_fsck_deps(FILE *f, const char *dest, const char *what, const char *where, const char *type);
-
-int generator_write_timeouts(const char *dir, const char *what, const char *where,
- const char *opts, char **filtered);
+int generator_write_fsck_deps(
+ FILE *f,
+ const char *dir,
+ const char *what,
+ const char *where,
+ const char *type);
+
+int generator_write_timeouts(
+ const char *dir,
+ const char *what,
+ const char *where,
+ const char *opts,
+ char **filtered);
diff --git a/src/shared/hashmap.c b/src/shared/hashmap.c
index e63ba4bb5a..20d599d04b 100644
--- a/src/shared/hashmap.c
+++ b/src/shared/hashmap.c
@@ -20,9 +20,7 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <assert.h>
#include <stdlib.h>
-#include <string.h>
#include <errno.h>
#include "util.h"
@@ -31,8 +29,12 @@
#include "macro.h"
#include "siphash24.h"
#include "strv.h"
-#include "list.h"
#include "mempool.h"
+#include "random-util.h"
+
+#ifdef ENABLE_DEBUG_HASHMAP
+#include "list.h"
+#endif
/*
* Implementation of hashmaps.
@@ -130,10 +132,10 @@ struct swap_entries {
/* Distance from Initial Bucket */
typedef uint8_t dib_raw_t;
-#define DIB_RAW_OVERFLOW ((dib_raw_t)0xfdU) /* indicates DIB value is greater than representable */
-#define DIB_RAW_REHASH ((dib_raw_t)0xfeU) /* entry yet to be rehashed during in-place resize */
-#define DIB_RAW_FREE ((dib_raw_t)0xffU) /* a free bucket */
-#define DIB_RAW_INIT ((char)DIB_RAW_FREE) /* a byte to memset a DIB store with when initializing */
+#define DIB_RAW_OVERFLOW ((dib_raw_t)0xfdU) /* indicates DIB value is greater than representable */
+#define DIB_RAW_REHASH ((dib_raw_t)0xfeU) /* entry yet to be rehashed during in-place resize */
+#define DIB_RAW_FREE ((dib_raw_t)0xffU) /* a free bucket */
+#define DIB_RAW_INIT ((char)DIB_RAW_FREE) /* a byte to memset a DIB store with when initializing */
#define DIB_FREE UINT_MAX
@@ -771,7 +773,7 @@ static void reset_direct_storage(HashmapBase *h) {
memset(p, DIB_RAW_INIT, sizeof(dib_raw_t) * hi->n_direct_buckets);
}
-static struct HashmapBase *hashmap_base_new(const struct hash_ops *hash_ops, enum HashmapType type HASHMAP_DEBUG_PARAMS) {
+static struct HashmapBase *hashmap_base_new(const struct hash_ops *hash_ops, enum HashmapType type HASHMAP_DEBUG_PARAMS) {
HashmapBase *h;
const struct hashmap_type_info *hi = &hashmap_type_info[type];
bool use_pool;
@@ -810,19 +812,19 @@ static struct HashmapBase *hashmap_base_new(const struct hash_ops *hash_ops, enu
}
Hashmap *internal_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) {
- return (Hashmap*) hashmap_base_new(hash_ops, HASHMAP_TYPE_PLAIN HASHMAP_DEBUG_PASS_ARGS);
+ return (Hashmap*) hashmap_base_new(hash_ops, HASHMAP_TYPE_PLAIN HASHMAP_DEBUG_PASS_ARGS);
}
OrderedHashmap *internal_ordered_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) {
- return (OrderedHashmap*) hashmap_base_new(hash_ops, HASHMAP_TYPE_ORDERED HASHMAP_DEBUG_PASS_ARGS);
+ return (OrderedHashmap*) hashmap_base_new(hash_ops, HASHMAP_TYPE_ORDERED HASHMAP_DEBUG_PASS_ARGS);
}
Set *internal_set_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) {
- return (Set*) hashmap_base_new(hash_ops, HASHMAP_TYPE_SET HASHMAP_DEBUG_PASS_ARGS);
+ return (Set*) hashmap_base_new(hash_ops, HASHMAP_TYPE_SET HASHMAP_DEBUG_PASS_ARGS);
}
static int hashmap_base_ensure_allocated(HashmapBase **h, const struct hash_ops *hash_ops,
- enum HashmapType type HASHMAP_DEBUG_PARAMS) {
+ enum HashmapType type HASHMAP_DEBUG_PARAMS) {
HashmapBase *q;
assert(h);
@@ -830,7 +832,7 @@ static int hashmap_base_ensure_allocated(HashmapBase **h, const struct hash_ops
if (*h)
return 0;
- q = hashmap_base_new(hash_ops, type HASHMAP_DEBUG_PASS_ARGS);
+ q = hashmap_base_new(hash_ops, type HASHMAP_DEBUG_PASS_ARGS);
if (!q)
return -ENOMEM;
@@ -839,15 +841,15 @@ static int hashmap_base_ensure_allocated(HashmapBase **h, const struct hash_ops
}
int internal_hashmap_ensure_allocated(Hashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) {
- return hashmap_base_ensure_allocated((HashmapBase**)h, hash_ops, HASHMAP_TYPE_PLAIN HASHMAP_DEBUG_PASS_ARGS);
+ return hashmap_base_ensure_allocated((HashmapBase**)h, hash_ops, HASHMAP_TYPE_PLAIN HASHMAP_DEBUG_PASS_ARGS);
}
int internal_ordered_hashmap_ensure_allocated(OrderedHashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) {
- return hashmap_base_ensure_allocated((HashmapBase**)h, hash_ops, HASHMAP_TYPE_ORDERED HASHMAP_DEBUG_PASS_ARGS);
+ return hashmap_base_ensure_allocated((HashmapBase**)h, hash_ops, HASHMAP_TYPE_ORDERED HASHMAP_DEBUG_PASS_ARGS);
}
int internal_set_ensure_allocated(Set **s, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) {
- return hashmap_base_ensure_allocated((HashmapBase**)s, hash_ops, HASHMAP_TYPE_SET HASHMAP_DEBUG_PASS_ARGS);
+ return hashmap_base_ensure_allocated((HashmapBase**)s, hash_ops, HASHMAP_TYPE_SET HASHMAP_DEBUG_PASS_ARGS);
}
static void hashmap_free_no_clear(HashmapBase *h) {
@@ -864,38 +866,41 @@ static void hashmap_free_no_clear(HashmapBase *h) {
free(h);
}
-void internal_hashmap_free(HashmapBase *h) {
+HashmapBase *internal_hashmap_free(HashmapBase *h) {
/* Free the hashmap, but nothing in it */
- if (!h)
- return;
+ if (h) {
+ internal_hashmap_clear(h);
+ hashmap_free_no_clear(h);
+ }
- internal_hashmap_clear(h);
- hashmap_free_no_clear(h);
+ return NULL;
}
-void internal_hashmap_free_free(HashmapBase *h) {
+HashmapBase *internal_hashmap_free_free(HashmapBase *h) {
/* Free the hashmap and all data objects in it, but not the
* keys */
- if (!h)
- return;
+ if (h) {
+ internal_hashmap_clear_free(h);
+ hashmap_free_no_clear(h);
+ }
- internal_hashmap_clear_free(h);
- hashmap_free_no_clear(h);
+ return NULL;
}
-void hashmap_free_free_free(Hashmap *h) {
+Hashmap *hashmap_free_free_free(Hashmap *h) {
/* Free the hashmap and all data and key objects in it */
- if (!h)
- return;
+ if (h) {
+ hashmap_clear_free_free(h);
+ hashmap_free_no_clear(HASHMAP_BASE(h));
+ }
- hashmap_clear_free_free(h);
- hashmap_free_no_clear(HASHMAP_BASE(h));
+ return NULL;
}
void internal_hashmap_clear(HashmapBase *h) {
@@ -990,7 +995,6 @@ static bool hashmap_put_robin_hood(HashmapBase *h, unsigned idx,
if (dib < distance) {
/* Found a wealthier entry. Go Robin Hood! */
-
bucket_set_dib(h, idx, distance);
/* swap the entries */
diff --git a/src/shared/hashmap.h b/src/shared/hashmap.h
index 894f67939e..a03ee5812a 100644
--- a/src/shared/hashmap.h
+++ b/src/shared/hashmap.h
@@ -144,25 +144,25 @@ OrderedHashmap *internal_ordered_hashmap_new(const struct hash_ops *hash_ops HA
#define hashmap_new(ops) internal_hashmap_new(ops HASHMAP_DEBUG_SRC_ARGS)
#define ordered_hashmap_new(ops) internal_ordered_hashmap_new(ops HASHMAP_DEBUG_SRC_ARGS)
-void internal_hashmap_free(HashmapBase *h);
-static inline void hashmap_free(Hashmap *h) {
- internal_hashmap_free(HASHMAP_BASE(h));
+HashmapBase *internal_hashmap_free(HashmapBase *h);
+static inline Hashmap *hashmap_free(Hashmap *h) {
+ return (void*)internal_hashmap_free(HASHMAP_BASE(h));
}
-static inline void ordered_hashmap_free(OrderedHashmap *h) {
- internal_hashmap_free(HASHMAP_BASE(h));
+static inline OrderedHashmap *ordered_hashmap_free(OrderedHashmap *h) {
+ return (void*)internal_hashmap_free(HASHMAP_BASE(h));
}
-void internal_hashmap_free_free(HashmapBase *h);
-static inline void hashmap_free_free(Hashmap *h) {
- internal_hashmap_free_free(HASHMAP_BASE(h));
+HashmapBase *internal_hashmap_free_free(HashmapBase *h);
+static inline Hashmap *hashmap_free_free(Hashmap *h) {
+ return (void*)internal_hashmap_free_free(HASHMAP_BASE(h));
}
-static inline void ordered_hashmap_free_free(OrderedHashmap *h) {
- internal_hashmap_free_free(HASHMAP_BASE(h));
+static inline OrderedHashmap *ordered_hashmap_free_free(OrderedHashmap *h) {
+ return (void*)internal_hashmap_free_free(HASHMAP_BASE(h));
}
-void hashmap_free_free_free(Hashmap *h);
-static inline void ordered_hashmap_free_free_free(OrderedHashmap *h) {
- hashmap_free_free_free(PLAIN_HASHMAP(h));
+Hashmap *hashmap_free_free_free(Hashmap *h);
+static inline OrderedHashmap *ordered_hashmap_free_free_free(OrderedHashmap *h) {
+ return (void*)hashmap_free_free_free(PLAIN_HASHMAP(h));
}
HashmapBase *internal_hashmap_copy(HashmapBase *h);
diff --git a/src/shared/hostname-util.c b/src/shared/hostname-util.c
new file mode 100644
index 0000000000..e336f269fa
--- /dev/null
+++ b/src/shared/hostname-util.c
@@ -0,0 +1,193 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2015 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/utsname.h>
+#include <ctype.h>
+
+#include "util.h"
+#include "hostname-util.h"
+
+bool hostname_is_set(void) {
+ struct utsname u;
+
+ assert_se(uname(&u) >= 0);
+
+ if (isempty(u.nodename))
+ return false;
+
+ /* This is the built-in kernel default host name */
+ if (streq(u.nodename, "(none)"))
+ return false;
+
+ return true;
+}
+
+char* gethostname_malloc(void) {
+ struct utsname u;
+
+ assert_se(uname(&u) >= 0);
+
+ if (isempty(u.nodename) || streq(u.nodename, "(none)"))
+ return strdup(u.sysname);
+
+ return strdup(u.nodename);
+}
+
+static bool hostname_valid_char(char c) {
+ return
+ (c >= 'a' && c <= 'z') ||
+ (c >= 'A' && c <= 'Z') ||
+ (c >= '0' && c <= '9') ||
+ c == '-' ||
+ c == '_' ||
+ c == '.';
+}
+
+bool hostname_is_valid(const char *s) {
+ const char *p;
+ bool dot;
+
+ if (isempty(s))
+ return false;
+
+ /* Doesn't accept empty hostnames, hostnames with trailing or
+ * leading dots, and hostnames with multiple dots in a
+ * sequence. Also ensures that the length stays below
+ * HOST_NAME_MAX. */
+
+ 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;
+
+ return true;
+}
+
+char* hostname_cleanup(char *s, bool lowercase) {
+ char *p, *d;
+ bool dot;
+
+ assert(s);
+
+ 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;
+ }
+
+ }
+
+ if (dot && d > s)
+ d[-1] = 0;
+ else
+ *d = 0;
+
+ strshorten(s, HOST_NAME_MAX);
+
+ return s;
+}
+
+bool is_localhost(const char *hostname) {
+ assert(hostname);
+
+ /* This tries to identify local host and domain names
+ * described in RFC6761 plus the redhatism of .localdomain */
+
+ return streq(hostname, "localhost") ||
+ streq(hostname, "localhost.") ||
+ streq(hostname, "localdomain.") ||
+ streq(hostname, "localdomain") ||
+ endswith(hostname, ".localhost") ||
+ endswith(hostname, ".localhost.") ||
+ endswith(hostname, ".localdomain") ||
+ endswith(hostname, ".localdomain.");
+}
+
+int sethostname_idempotent(const char *s) {
+ char buf[HOST_NAME_MAX + 1] = {};
+
+ assert(s);
+
+ if (gethostname(buf, sizeof(buf)) < 0)
+ return -errno;
+
+ if (streq(buf, s))
+ return 0;
+
+ if (sethostname(s, strlen(s)) < 0)
+ return -errno;
+
+ return 1;
+}
+
+int read_hostname_config(const char *path, char **hostname) {
+ _cleanup_fclose_ FILE *f = NULL;
+ char l[LINE_MAX];
+ char *name = NULL;
+
+ assert(path);
+ assert(hostname);
+
+ f = fopen(path, "re");
+ if (!f)
+ return -errno;
+
+ /* may have comments, ignore them */
+ FOREACH_LINE(l, f, return -errno) {
+ truncate_nl(l);
+ if (l[0] != '\0' && l[0] != '#') {
+ /* found line with value */
+ name = hostname_cleanup(l, false);
+ name = strdup(name);
+ if (!name)
+ return -ENOMEM;
+ break;
+ }
+ }
+
+ if (!name)
+ /* no non-empty line found */
+ return -ENOENT;
+
+ *hostname = name;
+ return 0;
+}
diff --git a/src/shared/hostname-util.h b/src/shared/hostname-util.h
new file mode 100644
index 0000000000..0c4763cf5a
--- /dev/null
+++ b/src/shared/hostname-util.h
@@ -0,0 +1,39 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010-2015 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 "macro.h"
+
+bool hostname_is_set(void);
+
+char* gethostname_malloc(void);
+
+bool hostname_is_valid(const char *s) _pure_;
+char* hostname_cleanup(char *s, bool lowercase);
+
+bool is_localhost(const char *hostname);
+
+int sethostname_idempotent(const char *s);
+
+int read_hostname_config(const char *path, char **hostname);
diff --git a/src/shared/import-util.c b/src/shared/import-util.c
index 660d92ac5d..001a8a37e8 100644
--- a/src/shared/import-util.c
+++ b/src/shared/import-util.c
@@ -150,6 +150,27 @@ int raw_strip_suffixes(const char *p, char **ret) {
return 0;
}
+bool dkr_digest_is_valid(const char *digest) {
+ /* 7 chars for prefix, 64 chars for the digest itself */
+ if (strlen(digest) != 71)
+ return false;
+
+ return startswith(digest, "sha256:") && in_charset(digest + 7, "0123456789abcdef");
+}
+
+bool dkr_ref_is_valid(const char *ref) {
+ const char *colon;
+
+ if (isempty(ref))
+ return false;
+
+ colon = strchr(ref, ':');
+ if (!colon)
+ return filename_is_valid(ref);
+
+ return dkr_digest_is_valid(ref);
+}
+
bool dkr_name_is_valid(const char *name) {
const char *slash, *p;
diff --git a/src/shared/import-util.h b/src/shared/import-util.h
index ff155b0ff2..7bf7d4ca40 100644
--- a/src/shared/import-util.h
+++ b/src/shared/import-util.h
@@ -44,4 +44,6 @@ int raw_strip_suffixes(const char *name, char **ret);
bool dkr_name_is_valid(const char *name);
bool dkr_id_is_valid(const char *id);
+bool dkr_ref_is_valid(const char *ref);
+bool dkr_digest_is_valid(const char *digest);
#define dkr_tag_is_valid(tag) filename_is_valid(tag)
diff --git a/src/shared/install-printf.c b/src/shared/install-printf.c
index d853f17772..cbe984d2fb 100644
--- a/src/shared/install-printf.c
+++ b/src/shared/install-printf.c
@@ -19,44 +19,32 @@
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"
+#include "formats-util.h"
static int specifier_prefix_and_instance(char specifier, void *data, void *userdata, char **ret) {
- InstallInfo *i = userdata;
- char *n;
+ UnitFileInstallInfo *i = userdata;
assert(i);
- n = unit_name_to_prefix_and_instance(i->name);
- if (!n)
- return -ENOMEM;
-
- *ret = n;
- return 0;
+ return unit_name_to_prefix_and_instance(i->name, ret);
}
static int specifier_prefix(char specifier, void *data, void *userdata, char **ret) {
- InstallInfo *i = userdata;
- char *n;
+ UnitFileInstallInfo *i = userdata;
assert(i);
- n = unit_name_to_prefix(i->name);
- if (!n)
- return -ENOMEM;
-
- *ret = n;
- return 0;
+ return unit_name_to_prefix(i->name, ret);
}
static int specifier_instance(char specifier, void *data, void *userdata, char **ret) {
- InstallInfo *i = userdata;
+ UnitFileInstallInfo *i = userdata;
char *instance;
int r;
@@ -77,7 +65,7 @@ static int specifier_instance(char specifier, void *data, void *userdata, char *
}
static int specifier_user_name(char specifier, void *data, void *userdata, char **ret) {
- InstallInfo *i = userdata;
+ UnitFileInstallInfo *i = userdata;
const char *username;
_cleanup_free_ char *tmp = NULL;
char *printed = NULL;
@@ -114,7 +102,7 @@ static int specifier_user_name(char specifier, void *data, void *userdata, char
}
-int install_full_printf(InstallInfo *i, const char *format, char **ret) {
+int install_full_printf(UnitFileInstallInfo *i, const char *format, char **ret) {
/* This is similar to unit_full_printf() but does not support
* anything path-related.
diff --git a/src/shared/install-printf.h b/src/shared/install-printf.h
index 6ffa488b1b..6550337824 100644
--- a/src/shared/install-printf.h
+++ b/src/shared/install-printf.h
@@ -22,4 +22,5 @@
#pragma once
#include "install.h"
-int install_full_printf(InstallInfo *i, const char *format, char **ret);
+
+int install_full_printf(UnitFileInstallInfo *i, const char *format, char **ret);
diff --git a/src/shared/install.c b/src/shared/install.c
index 65f1c245c6..6172c42d69 100644
--- a/src/shared/install.c
+++ b/src/shared/install.c
@@ -36,7 +36,6 @@
#include "install.h"
#include "conf-parser.h"
#include "conf-files.h"
-#include "specifier.h"
#include "install-printf.h"
#include "special.h"
@@ -113,51 +112,6 @@ static int get_config_path(UnitFileScope scope, bool runtime, const char *root_d
return 0;
}
-static int add_file_change(
- UnitFileChange **changes,
- unsigned *n_changes,
- UnitFileChangeType type,
- const char *path,
- const char *source) {
-
- UnitFileChange *c;
- unsigned i;
-
- assert(path);
- assert(!changes == !n_changes);
-
- if (!changes)
- return 0;
-
- c = realloc(*changes, (*n_changes + 1) * sizeof(UnitFileChange));
- if (!c)
- return -ENOMEM;
-
- *changes = c;
- i = *n_changes;
-
- c[i].type = type;
- c[i].path = strdup(path);
- if (!c[i].path)
- return -ENOMEM;
-
- path_kill_slashes(c[i].path);
-
- if (source) {
- c[i].source = strdup(source);
- if (!c[i].source) {
- free(c[i].path);
- return -ENOMEM;
- }
-
- path_kill_slashes(c[i].path);
- } else
- c[i].source = NULL;
-
- *n_changes = i+1;
- return 0;
-}
-
static int mark_symlink_for_removal(
Set **remove_symlinks_to,
const char *p) {
@@ -259,10 +213,10 @@ static int remove_marked_symlinks_fd(
int q;
bool found;
- if (!unit_name_is_valid(de->d_name, TEMPLATE_VALID))
+ if (!unit_name_is_valid(de->d_name, UNIT_NAME_ANY))
continue;
- if (unit_name_is_instance(de->d_name) &&
+ if (unit_name_is_valid(de->d_name, UNIT_NAME_INSTANCE) &&
instance_whitelist &&
!strv_contains(instance_whitelist, de->d_name)) {
@@ -273,9 +227,9 @@ static int remove_marked_symlinks_fd(
* the template of it might be
* listed. */
- w = unit_name_template(de->d_name);
- if (!w)
- return -ENOMEM;
+ r = unit_name_template(de->d_name, &w);
+ if (r < 0)
+ return r;
if (!strv_contains(instance_whitelist, w))
continue;
@@ -310,7 +264,7 @@ static int remove_marked_symlinks_fd(
path_kill_slashes(p);
rmdir_parents(p, config_path);
- add_file_change(changes, n_changes, UNIT_FILE_UNLINK, p, NULL);
+ unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, p, NULL);
if (!set_get(remove_symlinks_to, p)) {
@@ -515,7 +469,7 @@ static int find_symlinks_in_scope(
UnitFileState *state) {
int r;
- _cleanup_free_ char *path = NULL;
+ _cleanup_free_ char *normal_path = NULL, *runtime_path = NULL;
bool same_name_link_runtime = false, same_name_link = false;
assert(scope >= 0);
@@ -523,11 +477,11 @@ static int find_symlinks_in_scope(
assert(name);
/* First look in runtime config path */
- r = get_config_path(scope, true, root_dir, &path);
+ r = get_config_path(scope, true, root_dir, &normal_path);
if (r < 0)
return r;
- r = find_symlinks(name, path, &same_name_link_runtime);
+ r = find_symlinks(name, normal_path, &same_name_link_runtime);
if (r < 0)
return r;
else if (r > 0) {
@@ -536,11 +490,11 @@ static int find_symlinks_in_scope(
}
/* Then look in the normal config path */
- r = get_config_path(scope, false, root_dir, &path);
+ r = get_config_path(scope, false, root_dir, &runtime_path);
if (r < 0)
return r;
- r = find_symlinks(name, path, &same_name_link);
+ r = find_symlinks(name, runtime_path, &same_name_link);
if (r < 0)
return r;
else if (r > 0) {
@@ -584,7 +538,7 @@ int unit_file_mask(
STRV_FOREACH(i, files) {
_cleanup_free_ char *path = NULL;
- if (!unit_name_is_valid(*i, TEMPLATE_VALID)) {
+ if (!unit_name_is_valid(*i, UNIT_NAME_ANY)) {
if (r == 0)
r = -EINVAL;
continue;
@@ -597,7 +551,7 @@ int unit_file_mask(
}
if (symlink("/dev/null", path) >= 0) {
- add_file_change(changes, n_changes, UNIT_FILE_SYMLINK, path, "/dev/null");
+ unit_file_changes_add(changes, n_changes, UNIT_FILE_SYMLINK, path, "/dev/null");
continue;
}
@@ -608,8 +562,8 @@ int unit_file_mask(
if (force) {
if (symlink_atomic("/dev/null", path) >= 0) {
- add_file_change(changes, n_changes, UNIT_FILE_UNLINK, path, NULL);
- add_file_change(changes, n_changes, UNIT_FILE_SYMLINK, path, "/dev/null");
+ unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, path, NULL);
+ unit_file_changes_add(changes, n_changes, UNIT_FILE_SYMLINK, path, "/dev/null");
continue;
}
}
@@ -647,7 +601,7 @@ int unit_file_unmask(
STRV_FOREACH(i, files) {
_cleanup_free_ char *path = NULL;
- if (!unit_name_is_valid(*i, TEMPLATE_VALID)) {
+ if (!unit_name_is_valid(*i, UNIT_NAME_ANY)) {
if (r == 0)
r = -EINVAL;
continue;
@@ -665,7 +619,7 @@ int unit_file_unmask(
q = -errno;
else {
q = mark_symlink_for_removal(&remove_symlinks_to, path);
- add_file_change(changes, n_changes, UNIT_FILE_UNLINK, path, NULL);
+ unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, path, NULL);
}
}
@@ -718,7 +672,7 @@ int unit_file_link(
fn = basename(*i);
if (!path_is_absolute(*i) ||
- !unit_name_is_valid(fn, TEMPLATE_VALID)) {
+ !unit_name_is_valid(fn, UNIT_NAME_ANY)) {
if (r == 0)
r = -EINVAL;
continue;
@@ -747,7 +701,7 @@ int unit_file_link(
return -ENOMEM;
if (symlink(*i, path) >= 0) {
- add_file_change(changes, n_changes, UNIT_FILE_SYMLINK, path, *i);
+ unit_file_changes_add(changes, n_changes, UNIT_FILE_SYMLINK, path, *i);
continue;
}
@@ -766,8 +720,8 @@ int unit_file_link(
if (force) {
if (symlink_atomic(*i, path) >= 0) {
- add_file_change(changes, n_changes, UNIT_FILE_UNLINK, path, NULL);
- add_file_change(changes, n_changes, UNIT_FILE_SYMLINK, path, *i);
+ unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, path, NULL);
+ unit_file_changes_add(changes, n_changes, UNIT_FILE_SYMLINK, path, *i);
continue;
}
}
@@ -794,6 +748,51 @@ void unit_file_list_free(Hashmap *h) {
hashmap_free(h);
}
+int unit_file_changes_add(
+ UnitFileChange **changes,
+ unsigned *n_changes,
+ UnitFileChangeType type,
+ const char *path,
+ const char *source) {
+
+ UnitFileChange *c;
+ unsigned i;
+
+ assert(path);
+ assert(!changes == !n_changes);
+
+ if (!changes)
+ return 0;
+
+ c = realloc(*changes, (*n_changes + 1) * sizeof(UnitFileChange));
+ if (!c)
+ return -ENOMEM;
+
+ *changes = c;
+ i = *n_changes;
+
+ c[i].type = type;
+ c[i].path = strdup(path);
+ if (!c[i].path)
+ return -ENOMEM;
+
+ path_kill_slashes(c[i].path);
+
+ if (source) {
+ c[i].source = strdup(source);
+ if (!c[i].source) {
+ free(c[i].path);
+ return -ENOMEM;
+ }
+
+ path_kill_slashes(c[i].path);
+ } else
+ c[i].source = NULL;
+
+ *n_changes = i+1;
+ return 0;
+}
+
void unit_file_changes_free(UnitFileChange *changes, unsigned n_changes) {
unsigned i;
@@ -810,7 +809,7 @@ void unit_file_changes_free(UnitFileChange *changes, unsigned n_changes) {
free(changes);
}
-static void install_info_free(InstallInfo *i) {
+static void install_info_free(UnitFileInstallInfo *i) {
assert(i);
free(i->name);
@@ -824,7 +823,7 @@ static void install_info_free(InstallInfo *i) {
}
static void install_info_hashmap_free(OrderedHashmap *m) {
- InstallInfo *i;
+ UnitFileInstallInfo *i;
if (!m)
return;
@@ -848,7 +847,7 @@ static int install_info_add(
InstallContext *c,
const char *name,
const char *path) {
- InstallInfo *i = NULL;
+ UnitFileInstallInfo *i = NULL;
int r;
assert(c);
@@ -857,7 +856,7 @@ static int install_info_add(
if (!name)
name = basename(path);
- if (!unit_name_is_valid(name, TEMPLATE_VALID))
+ if (!unit_name_is_valid(name, UNIT_NAME_ANY))
return -EINVAL;
if (ordered_hashmap_get(c->have_installed, name) ||
@@ -868,7 +867,7 @@ static int install_info_add(
if (r < 0)
return r;
- i = new0(InstallInfo, 1);
+ i = new0(UnitFileInstallInfo, 1);
if (!i)
return -ENOMEM;
@@ -927,7 +926,7 @@ static int config_parse_also(
size_t l;
const char *word, *state;
InstallContext *c = data;
- InstallInfo *i = userdata;
+ UnitFileInstallInfo *i = userdata;
assert(filename);
assert(lvalue);
@@ -968,7 +967,7 @@ static int config_parse_user(
void *data,
void *userdata) {
- InstallInfo *i = data;
+ UnitFileInstallInfo *i = data;
char *printed;
int r;
@@ -998,7 +997,7 @@ static int config_parse_default_instance(
void *data,
void *userdata) {
- InstallInfo *i = data;
+ UnitFileInstallInfo *i = data;
char *printed;
int r;
@@ -1023,7 +1022,7 @@ static int config_parse_default_instance(
static int unit_file_load(
InstallContext *c,
- InstallInfo *info,
+ UnitFileInstallInfo *info,
const char *path,
const char *root_dir,
bool allow_symlink,
@@ -1083,8 +1082,8 @@ static int unit_file_load(
static int unit_file_search(
InstallContext *c,
- InstallInfo *info,
- LookupPaths *paths,
+ UnitFileInstallInfo *info,
+ const LookupPaths *paths,
const char *root_dir,
bool allow_symlink,
bool load,
@@ -1119,7 +1118,7 @@ static int unit_file_search(
return r;
}
- if (unit_name_is_instance(info->name)) {
+ if (unit_name_is_valid(info->name, UNIT_NAME_INSTANCE)) {
/* Unit file doesn't exist, however instance
* enablement was requested. We will check if it is
@@ -1127,9 +1126,9 @@ static int unit_file_search(
_cleanup_free_ char *template = NULL;
- template = unit_name_template(info->name);
- if (!template)
- return -ENOMEM;
+ r = unit_name_template(info->name, &template);
+ if (r < 0)
+ return r;
STRV_FOREACH(p, paths->unit_path) {
_cleanup_free_ char *path = NULL;
@@ -1153,14 +1152,14 @@ static int unit_file_search(
}
static int unit_file_can_install(
- LookupPaths *paths,
+ const LookupPaths *paths,
const char *root_dir,
const char *name,
bool allow_symlink,
bool *also) {
_cleanup_(install_context_done) InstallContext c = {};
- InstallInfo *i;
+ UnitFileInstallInfo *i;
int r;
assert(paths);
@@ -1199,7 +1198,7 @@ static int create_symlink(
mkdir_parents_label(new_path, 0755);
if (symlink(old_path, new_path) >= 0) {
- add_file_change(changes, n_changes, UNIT_FILE_SYMLINK, new_path, old_path);
+ unit_file_changes_add(changes, n_changes, UNIT_FILE_SYMLINK, new_path, old_path);
return 0;
}
@@ -1220,14 +1219,14 @@ static int create_symlink(
if (r < 0)
return r;
- add_file_change(changes, n_changes, UNIT_FILE_UNLINK, new_path, NULL);
- add_file_change(changes, n_changes, UNIT_FILE_SYMLINK, new_path, old_path);
+ unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, new_path, NULL);
+ unit_file_changes_add(changes, n_changes, UNIT_FILE_SYMLINK, new_path, old_path);
return 0;
}
static int install_info_symlink_alias(
- InstallInfo *i,
+ UnitFileInstallInfo *i,
const char *config_path,
bool force,
UnitFileChange **changes,
@@ -1259,7 +1258,7 @@ static int install_info_symlink_alias(
}
static int install_info_symlink_wants(
- InstallInfo *i,
+ UnitFileInstallInfo *i,
const char *config_path,
char **list,
const char *suffix,
@@ -1275,7 +1274,7 @@ static int install_info_symlink_wants(
assert(i);
assert(config_path);
- if (unit_name_is_template(i->name)) {
+ if (unit_name_is_valid(i->name, UNIT_NAME_TEMPLATE)) {
/* Don't install any symlink if there's no default
* instance configured */
@@ -1283,9 +1282,9 @@ static int install_info_symlink_wants(
if (!i->default_instance)
return 0;
- buf = unit_name_replace_instance(i->name, i->default_instance);
- if (!buf)
- return -ENOMEM;
+ r = unit_name_replace_instance(i->name, i->default_instance, &buf);
+ if (r < 0)
+ return r;
n = buf;
} else
@@ -1298,7 +1297,7 @@ static int install_info_symlink_wants(
if (q < 0)
return q;
- if (!unit_name_is_valid(dst, TEMPLATE_VALID)) {
+ if (!unit_name_is_valid(dst, UNIT_NAME_ANY)) {
r = -EINVAL;
continue;
}
@@ -1316,8 +1315,8 @@ static int install_info_symlink_wants(
}
static int install_info_symlink_link(
- InstallInfo *i,
- LookupPaths *paths,
+ UnitFileInstallInfo *i,
+ const LookupPaths *paths,
const char *config_path,
const char *root_dir,
bool force,
@@ -1344,8 +1343,8 @@ static int install_info_symlink_link(
}
static int install_info_apply(
- InstallInfo *i,
- LookupPaths *paths,
+ UnitFileInstallInfo *i,
+ const LookupPaths *paths,
const char *config_path,
const char *root_dir,
bool force,
@@ -1377,14 +1376,14 @@ static int install_info_apply(
static int install_context_apply(
InstallContext *c,
- LookupPaths *paths,
+ const LookupPaths *paths,
const char *config_path,
const char *root_dir,
bool force,
UnitFileChange **changes,
unsigned *n_changes) {
- InstallInfo *i;
+ UnitFileInstallInfo *i;
int r, q;
assert(c);
@@ -1424,12 +1423,12 @@ static int install_context_apply(
static int install_context_mark_for_removal(
InstallContext *c,
- LookupPaths *paths,
+ const LookupPaths *paths,
Set **remove_symlinks_to,
const char *config_path,
const char *root_dir) {
- InstallInfo *i;
+ UnitFileInstallInfo *i;
int r, q;
assert(c);
@@ -1463,13 +1462,13 @@ static int install_context_mark_for_removal(
} else if (r >= 0)
r += q;
- if (unit_name_is_instance(i->name)) {
+ if (unit_name_is_valid(i->name, UNIT_NAME_INSTANCE)) {
char *unit_file;
if (i->path) {
unit_file = basename(i->path);
- if (unit_name_is_instance(unit_file))
+ if (unit_name_is_valid(unit_file, UNIT_NAME_INSTANCE))
/* unit file named as instance exists, thus all symlinks
* pointing to it will be removed */
q = mark_symlink_for_removal(remove_symlinks_to, i->name);
@@ -1481,9 +1480,9 @@ static int install_context_mark_for_removal(
/* If i->path is not set, it means that we didn't actually find
* the unit file. But we can still remove symlinks to the
* nonexistent template. */
- unit_file = unit_name_template(i->name);
- if (!unit_file)
- return log_oom();
+ r = unit_name_template(i->name, &unit_file);
+ if (r < 0)
+ return r;
q = mark_symlink_for_removal(remove_symlinks_to, unit_file);
free(unit_file);
@@ -1514,7 +1513,7 @@ int unit_file_add_dependency(
_cleanup_free_ char *config_path = NULL;
char **i;
int r;
- InstallInfo *info;
+ UnitFileInstallInfo *info;
assert(scope >= 0);
assert(scope < _UNIT_FILE_SCOPE_MAX);
@@ -1536,7 +1535,7 @@ int unit_file_add_dependency(
if (state == UNIT_FILE_MASKED || state == UNIT_FILE_MASKED_RUNTIME) {
log_error("Failed to enable unit: Unit %s is masked", *i);
- return -ENOTSUP;
+ return -EOPNOTSUPP;
}
r = install_info_add_auto(&c, *i);
@@ -1614,7 +1613,7 @@ int unit_file_enable(
state = unit_file_get_state(scope, root_dir, *i);
if (state == UNIT_FILE_MASKED || state == UNIT_FILE_MASKED_RUNTIME) {
log_error("Failed to enable unit: Unit %s is masked", *i);
- return -ENOTSUP;
+ return -EOPNOTSUPP;
}
r = install_info_add_auto(&c, *i);
@@ -1703,7 +1702,7 @@ int unit_file_set_default(
_cleanup_free_ char *config_path = NULL;
char *path;
int r;
- InstallInfo *i = NULL;
+ UnitFileInstallInfo *i = NULL;
assert(scope >= 0);
assert(scope < _UNIT_FILE_SCOPE_MAX);
@@ -1785,39 +1784,28 @@ int unit_file_get_default(
return -ENOENT;
}
-UnitFileState unit_file_get_state(
+UnitFileState unit_file_lookup_state(
UnitFileScope scope,
const char *root_dir,
+ const LookupPaths *paths,
const char *name) {
- _cleanup_lookup_paths_free_ LookupPaths paths = {};
UnitFileState state = _UNIT_FILE_STATE_INVALID;
char **i;
_cleanup_free_ char *path = NULL;
- int r;
-
- assert(scope >= 0);
- assert(scope < _UNIT_FILE_SCOPE_MAX);
- assert(name);
+ int r = 0;
- if (root_dir && scope != UNIT_FILE_SYSTEM)
- return -EINVAL;
+ assert(paths);
- if (!unit_name_is_valid(name, TEMPLATE_VALID))
+ if (!unit_name_is_valid(name, UNIT_NAME_ANY))
return -EINVAL;
- r = lookup_paths_init_from_scope(&paths, scope, root_dir);
- if (r < 0)
- return r;
-
- STRV_FOREACH(i, paths.unit_path) {
+ STRV_FOREACH(i, paths->unit_path) {
struct stat st;
char *partial;
bool also = false;
free(path);
- path = NULL;
-
path = path_join(root_dir, *i, name);
if (!path)
return -ENOMEM;
@@ -1836,7 +1824,7 @@ UnitFileState unit_file_get_state(
if (errno != ENOENT)
return r;
- if (!unit_name_is_instance(name))
+ if (!unit_name_is_valid(name, UNIT_NAME_INSTANCE))
continue;
} else {
if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode))
@@ -1846,8 +1834,7 @@ UnitFileState unit_file_get_state(
if (r < 0 && r != -ENOENT)
return r;
else if (r > 0) {
- state = path_startswith(*i, "/run") ?
- UNIT_FILE_MASKED_RUNTIME : UNIT_FILE_MASKED;
+ state = path_startswith(*i, "/run") ? UNIT_FILE_MASKED_RUNTIME : UNIT_FILE_MASKED;
return state;
}
}
@@ -1858,7 +1845,7 @@ UnitFileState unit_file_get_state(
else if (r > 0)
return state;
- r = unit_file_can_install(&paths, root_dir, partial, true, &also);
+ r = unit_file_can_install(paths, root_dir, partial, true, &also);
if (r < 0 && errno != ENOENT)
return r;
else if (r > 0)
@@ -1873,6 +1860,28 @@ UnitFileState unit_file_get_state(
return r < 0 ? r : state;
}
+UnitFileState unit_file_get_state(
+ UnitFileScope scope,
+ const char *root_dir,
+ const char *name) {
+
+ _cleanup_lookup_paths_free_ LookupPaths paths = {};
+ int r;
+
+ assert(scope >= 0);
+ assert(scope < _UNIT_FILE_SCOPE_MAX);
+ assert(name);
+
+ if (root_dir && scope != UNIT_FILE_SYSTEM)
+ return -EINVAL;
+
+ r = lookup_paths_init_from_scope(&paths, scope, root_dir);
+ if (r < 0)
+ return r;
+
+ return unit_file_lookup_state(scope, root_dir, &paths, name);
+}
+
int unit_file_query_preset(UnitFileScope scope, const char *root_dir, const char *name) {
_cleanup_strv_free_ char **files = NULL;
char **p;
@@ -1985,7 +1994,7 @@ int unit_file_preset(
STRV_FOREACH(i, files) {
- if (!unit_name_is_valid(*i, TEMPLATE_VALID))
+ if (!unit_name_is_valid(*i, UNIT_NAME_ANY))
return -EINVAL;
r = unit_file_query_preset(scope, root_dir, *i);
@@ -2081,7 +2090,7 @@ int unit_file_preset_all(
if (hidden_file(de->d_name))
continue;
- if (!unit_name_is_valid(de->d_name, TEMPLATE_VALID))
+ if (!unit_name_is_valid(de->d_name, UNIT_NAME_ANY))
continue;
dirent_ensure_type(d, de);
@@ -2193,7 +2202,7 @@ int unit_file_get_list(
if (hidden_file(de->d_name))
continue;
- if (!unit_name_is_valid(de->d_name, TEMPLATE_VALID))
+ if (!unit_name_is_valid(de->d_name, UNIT_NAME_ANY))
continue;
if (hashmap_get(h, de->d_name))
diff --git a/src/shared/install.h b/src/shared/install.h
index 357be0f92d..a9d77dd91b 100644
--- a/src/shared/install.h
+++ b/src/shared/install.h
@@ -21,18 +21,27 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+typedef enum UnitFileScope UnitFileScope;
+typedef enum UnitFileState UnitFileState;
+typedef enum UnitFilePresetMode UnitFilePresetMode;
+typedef enum UnitFileChangeType UnitFileChangeType;
+typedef struct UnitFileChange UnitFileChange;
+typedef struct UnitFileList UnitFileList;
+typedef struct UnitFileInstallInfo UnitFileInstallInfo;
+
#include "hashmap.h"
#include "unit-name.h"
+#include "path-lookup.h"
-typedef enum UnitFileScope {
+enum UnitFileScope {
UNIT_FILE_SYSTEM,
UNIT_FILE_GLOBAL,
UNIT_FILE_USER,
_UNIT_FILE_SCOPE_MAX,
_UNIT_FILE_SCOPE_INVALID = -1
-} UnitFileScope;
+};
-typedef enum UnitFileState {
+enum UnitFileState {
UNIT_FILE_ENABLED,
UNIT_FILE_ENABLED_RUNTIME,
UNIT_FILE_LINKED,
@@ -45,35 +54,35 @@ typedef enum UnitFileState {
UNIT_FILE_INVALID,
_UNIT_FILE_STATE_MAX,
_UNIT_FILE_STATE_INVALID = -1
-} UnitFileState;
+};
-typedef enum UnitFilePresetMode {
+enum UnitFilePresetMode {
UNIT_FILE_PRESET_FULL,
UNIT_FILE_PRESET_ENABLE_ONLY,
UNIT_FILE_PRESET_DISABLE_ONLY,
_UNIT_FILE_PRESET_MAX,
_UNIT_FILE_PRESET_INVALID = -1
-} UnitFilePresetMode;
+};
-typedef enum UnitFileChangeType {
+enum UnitFileChangeType {
UNIT_FILE_SYMLINK,
UNIT_FILE_UNLINK,
_UNIT_FILE_CHANGE_TYPE_MAX,
_UNIT_FILE_CHANGE_TYPE_INVALID = -1
-} UnitFileChangeType;
+};
-typedef struct UnitFileChange {
+struct UnitFileChange {
UnitFileChangeType type;
char *path;
char *source;
-} UnitFileChange;
+};
-typedef struct UnitFileList {
+struct UnitFileList {
char *path;
UnitFileState state;
-} UnitFileList;
+};
-typedef struct {
+struct UnitFileInstallInfo {
char *name;
char *path;
char *user;
@@ -84,7 +93,7 @@ typedef struct {
char **also;
char *default_instance;
-} 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);
@@ -98,11 +107,20 @@ int unit_file_set_default(UnitFileScope scope, const char *root_dir, const char
int unit_file_get_default(UnitFileScope scope, const char *root_dir, char **name);
int unit_file_add_dependency(UnitFileScope scope, bool runtime, const char *root_dir, char **files, char *target, UnitDependency dep, bool force, UnitFileChange **changes, unsigned *n_changes);
-UnitFileState unit_file_get_state(UnitFileScope scope, const char *root_dir, const char *filename);
+UnitFileState unit_file_lookup_state(
+ UnitFileScope scope,
+ const char *root_dir,
+ const LookupPaths *paths,
+ const char *name);
+UnitFileState unit_file_get_state(
+ UnitFileScope scope,
+ const char *root_dir,
+ const char *filename);
int unit_file_get_list(UnitFileScope scope, const char *root_dir, Hashmap *h);
void unit_file_list_free(Hashmap *h);
+int unit_file_changes_add(UnitFileChange **changes, unsigned *n_changes, UnitFileChangeType type, const char *path, const char *source);
void unit_file_changes_free(UnitFileChange *changes, unsigned n_changes);
int unit_file_query_preset(UnitFileScope scope, const char *root_dir, const char *name);
diff --git a/src/shared/json.c b/src/shared/json.c
index bb3d26f0e5..be40a0d203 100644
--- a/src/shared/json.c
+++ b/src/shared/json.c
@@ -21,18 +21,178 @@
#include <sys/types.h>
#include <math.h>
-
#include "macro.h"
-#include "log.h"
-#include "util.h"
#include "utf8.h"
#include "json.h"
-enum {
- STATE_NULL,
- STATE_VALUE,
- STATE_VALUE_POST,
-};
+int json_variant_new(JsonVariant **ret, JsonVariantType type) {
+ JsonVariant *v;
+
+ v = new0(JsonVariant, 1);
+ if (!v)
+ return -ENOMEM;
+ v->type = type;
+ *ret = v;
+ return 0;
+}
+
+static int json_variant_deep_copy(JsonVariant *ret, JsonVariant *variant) {
+ int r;
+
+ assert(ret);
+ assert(variant);
+
+ ret->type = variant->type;
+ ret->size = variant->size;
+
+ if (variant->type == JSON_VARIANT_STRING) {
+ ret->string = memdup(variant->string, variant->size+1);
+ if (!ret->string)
+ return -ENOMEM;
+ } else if (variant->type == JSON_VARIANT_ARRAY || variant->type == JSON_VARIANT_OBJECT) {
+ size_t i;
+
+ ret->objects = new0(JsonVariant, variant->size);
+ if (!ret->objects)
+ return -ENOMEM;
+
+ for (i = 0; i < variant->size; ++i) {
+ r = json_variant_deep_copy(&ret->objects[i], &variant->objects[i]);
+ if (r < 0)
+ return r;
+ }
+ } else
+ ret->value = variant->value;
+
+ return 0;
+}
+
+static JsonVariant *json_object_unref(JsonVariant *variant);
+
+static JsonVariant *json_variant_unref_inner(JsonVariant *variant) {
+ if (!variant)
+ return NULL;
+
+ if (variant->type == JSON_VARIANT_ARRAY || variant->type == JSON_VARIANT_OBJECT)
+ return json_object_unref(variant);
+ else if (variant->type == JSON_VARIANT_STRING)
+ free(variant->string);
+
+ return NULL;
+}
+
+static JsonVariant *json_raw_unref(JsonVariant *variant, size_t size) {
+ if (!variant)
+ return NULL;
+
+ for (size_t i = 0; i < size; ++i)
+ json_variant_unref_inner(&variant[i]);
+
+ free(variant);
+ return NULL;
+}
+
+static JsonVariant *json_object_unref(JsonVariant *variant) {
+ size_t i;
+
+ assert(variant);
+
+ if (!variant->objects)
+ return NULL;
+
+ for (i = 0; i < variant->size; ++i)
+ json_variant_unref_inner(&variant->objects[i]);
+
+ free(variant->objects);
+ return NULL;
+}
+
+static JsonVariant **json_variant_array_unref(JsonVariant **variant) {
+ size_t i = 0;
+ JsonVariant *p = NULL;
+
+ if (!variant)
+ return NULL;
+
+ while((p = (variant[i++])) != NULL) {
+ if (p->type == JSON_VARIANT_STRING)
+ free(p->string);
+ free(p);
+ }
+
+ free(variant);
+
+ return NULL;
+}
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(JsonVariant **, json_variant_array_unref);
+
+JsonVariant *json_variant_unref(JsonVariant *variant) {
+ if (!variant)
+ return NULL;
+
+ if (variant->type == JSON_VARIANT_ARRAY || variant->type == JSON_VARIANT_OBJECT)
+ json_object_unref(variant);
+ else if (variant->type == JSON_VARIANT_STRING)
+ free(variant->string);
+
+ free(variant);
+
+ return NULL;
+}
+
+char *json_variant_string(JsonVariant *variant){
+ assert(variant);
+ assert(variant->type == JSON_VARIANT_STRING);
+
+ return variant->string;
+}
+
+bool json_variant_bool(JsonVariant *variant) {
+ assert(variant);
+ assert(variant->type == JSON_VARIANT_BOOLEAN);
+
+ return variant->value.boolean;
+}
+
+intmax_t json_variant_integer(JsonVariant *variant) {
+ assert(variant);
+ assert(variant->type == JSON_VARIANT_INTEGER);
+
+ return variant->value.integer;
+}
+
+double json_variant_real(JsonVariant *variant) {
+ assert(variant);
+ assert(variant->type == JSON_VARIANT_REAL);
+
+ return variant->value.real;
+}
+
+JsonVariant *json_variant_element(JsonVariant *variant, unsigned index) {
+ assert(variant);
+ assert(variant->type == JSON_VARIANT_ARRAY || variant->type == JSON_VARIANT_OBJECT);
+ assert(index < variant->size);
+ assert(variant->objects);
+
+ return &variant->objects[index];
+}
+
+JsonVariant *json_variant_value(JsonVariant *variant, const char *key) {
+ size_t i;
+
+ assert(variant);
+ assert(variant->type == JSON_VARIANT_OBJECT);
+ assert(variant->objects);
+
+ for (i = 0; i < variant->size; i += 2) {
+ JsonVariant *p = &variant->objects[i];
+ if (p->type == JSON_VARIANT_STRING && streq(key, p->string))
+ return &variant->objects[i + 1];
+ }
+
+ return NULL;
+}
static void inc_lines(unsigned *line, const char *s, size_t n) {
const char *p = s;
@@ -286,9 +446,6 @@ static int json_parse_number(const char **p, union json_value *ret) {
} while (strchr("0123456789", *c) && *c != 0);
}
- if (*c != 0)
- return -EINVAL;
-
*p = c;
if (is_double) {
@@ -311,6 +468,12 @@ int json_tokenize(
int t;
int r;
+ enum {
+ STATE_NULL,
+ STATE_VALUE,
+ STATE_VALUE_POST,
+ };
+
assert(p);
assert(*p);
assert(ret_string);
@@ -444,3 +607,260 @@ int json_tokenize(
}
}
+
+static bool json_is_value(JsonVariant *var) {
+ assert(var);
+
+ return var->type != JSON_VARIANT_CONTROL;
+}
+
+static int json_scoped_parse(JsonVariant **tokens, size_t *i, size_t n, JsonVariant *scope) {
+ bool arr = scope->type == JSON_VARIANT_ARRAY;
+ int terminator = arr ? JSON_ARRAY_CLOSE : JSON_OBJECT_CLOSE;
+ size_t allocated = 0, size = 0;
+ JsonVariant *key = NULL, *value = NULL, *var = NULL, *items = NULL;
+ enum {
+ STATE_KEY,
+ STATE_COLON,
+ STATE_COMMA,
+ STATE_VALUE
+ } state = arr ? STATE_VALUE : STATE_KEY;
+
+ assert(tokens);
+ assert(i);
+ assert(scope);
+
+ while((var = *i < n ? tokens[(*i)++] : NULL) != NULL) {
+ bool stopper;
+ int r;
+
+ stopper = !json_is_value(var) && var->value.integer == terminator;
+
+ if (stopper) {
+ if (state != STATE_COMMA && size > 0)
+ goto error;
+
+ goto out;
+ }
+
+ if (state == STATE_KEY) {
+ if (var->type != JSON_VARIANT_STRING)
+ goto error;
+ else {
+ key = var;
+ state = STATE_COLON;
+ }
+ }
+ else if (state == STATE_COLON) {
+ if (key == NULL)
+ goto error;
+
+ if (json_is_value(var))
+ goto error;
+
+ if (var->value.integer != JSON_COLON)
+ goto error;
+
+ state = STATE_VALUE;
+ }
+ else if (state == STATE_VALUE) {
+ _cleanup_json_variant_unref_ JsonVariant *v = NULL;
+ size_t toadd = arr ? 1 : 2;
+
+ if (!json_is_value(var)) {
+ int type = (var->value.integer == JSON_ARRAY_OPEN) ? JSON_VARIANT_ARRAY : JSON_VARIANT_OBJECT;
+
+ r = json_variant_new(&v, type);
+ if (r < 0)
+ goto error;
+
+ r = json_scoped_parse(tokens, i, n, v);
+ if (r < 0)
+ goto error;
+
+ value = v;
+ }
+ else
+ value = var;
+
+ if(!GREEDY_REALLOC(items, allocated, size + toadd))
+ goto error;
+
+ if (arr) {
+ r = json_variant_deep_copy(&items[size], value);
+ if (r < 0)
+ goto error;
+ } else {
+ r = json_variant_deep_copy(&items[size], key);
+ if (r < 0)
+ goto error;
+
+ r = json_variant_deep_copy(&items[size+1], value);
+ if (r < 0)
+ goto error;
+ }
+
+ size += toadd;
+ state = STATE_COMMA;
+ }
+ else if (state == STATE_COMMA) {
+ if (json_is_value(var))
+ goto error;
+
+ if (var->value.integer != JSON_COMMA)
+ goto error;
+
+ key = NULL;
+ value = NULL;
+
+ state = arr ? STATE_VALUE : STATE_KEY;
+ }
+ }
+
+error:
+ json_raw_unref(items, size);
+ return -EBADMSG;
+
+out:
+ scope->size = size;
+ scope->objects = items;
+
+ return scope->type;
+}
+
+static int json_parse_tokens(JsonVariant **tokens, size_t ntokens, JsonVariant **rv) {
+ size_t it = 0;
+ int r;
+ JsonVariant *e;
+ _cleanup_json_variant_unref_ JsonVariant *p = NULL;
+
+ assert(tokens);
+ assert(ntokens);
+
+ e = tokens[it++];
+ r = json_variant_new(&p, JSON_VARIANT_OBJECT);
+ if (r < 0)
+ return r;
+
+ if (e->type != JSON_VARIANT_CONTROL && e->value.integer != JSON_OBJECT_OPEN)
+ return -EBADMSG;
+
+ r = json_scoped_parse(tokens, &it, ntokens, p);
+ if (r < 0)
+ return r;
+
+ *rv = p;
+ p = NULL;
+
+ return 0;
+}
+
+static int json_tokens(const char *string, size_t size, JsonVariant ***tokens, size_t *n) {
+ _cleanup_free_ char *buf = NULL;
+ _cleanup_(json_variant_array_unrefp) JsonVariant **items = NULL;
+ union json_value v = {};
+ void *json_state = NULL;
+ const char *p;
+ int t, r;
+ size_t allocated = 0, s = 0;
+
+ assert(string);
+ assert(n);
+
+ if (size <= 0)
+ return -EBADMSG;
+
+ buf = strndup(string, size);
+ if (!buf)
+ return -ENOMEM;
+
+ p = buf;
+ for (;;) {
+ _cleanup_json_variant_unref_ JsonVariant *var = NULL;
+ _cleanup_free_ char *rstr = NULL;
+
+ t = json_tokenize(&p, &rstr, &v, &json_state, NULL);
+
+ if (t < 0)
+ return t;
+ else if (t == JSON_END)
+ break;
+
+ if (t <= JSON_ARRAY_CLOSE) {
+ r = json_variant_new(&var, JSON_VARIANT_CONTROL);
+ if (r < 0)
+ return r;
+ var->value.integer = t;
+ } else {
+ switch (t) {
+ case JSON_STRING:
+ r = json_variant_new(&var, JSON_VARIANT_STRING);
+ if (r < 0)
+ return r;
+ var->size = strlen(rstr);
+ var->string = strdup(rstr);
+ if (!var->string) {
+ return -ENOMEM;
+ }
+ break;
+ case JSON_INTEGER:
+ r = json_variant_new(&var, JSON_VARIANT_INTEGER);
+ if (r < 0)
+ return r;
+ var->value = v;
+ break;
+ case JSON_REAL:
+ r = json_variant_new(&var, JSON_VARIANT_REAL);
+ if (r < 0)
+ return r;
+ var->value = v;
+ break;
+ case JSON_BOOLEAN:
+ r = json_variant_new(&var, JSON_VARIANT_BOOLEAN);
+ if (r < 0)
+ return r;
+ var->value = v;
+ break;
+ case JSON_NULL:
+ r = json_variant_new(&var, JSON_VARIANT_NULL);
+ if (r < 0)
+ return r;
+ break;
+ }
+ }
+
+ if (!GREEDY_REALLOC(items, allocated, s+2))
+ return -ENOMEM;
+
+ items[s++] = var;
+ items[s] = NULL;
+ var = NULL;
+ }
+
+ *n = s;
+ *tokens = items;
+ items = NULL;
+
+ return 0;
+}
+
+int json_parse(const char *string, JsonVariant **rv) {
+ _cleanup_(json_variant_array_unrefp) JsonVariant **s = NULL;
+ JsonVariant *v = NULL;
+ size_t n = 0;
+ int r;
+
+ assert(string);
+ assert(rv);
+
+ r = json_tokens(string, strlen(string), &s, &n);
+ if (r < 0)
+ return r;
+
+ r = json_parse_tokens(s, n, &v);
+ if (r < 0)
+ return r;
+
+ *rv = v;
+ return 0;
+}
diff --git a/src/shared/json.h b/src/shared/json.h
index a8457132e7..e0b4d810b5 100644
--- a/src/shared/json.h
+++ b/src/shared/json.h
@@ -22,7 +22,7 @@
***/
#include <stdbool.h>
-#include <inttypes.h>
+#include "util.h"
enum {
JSON_END,
@@ -39,12 +39,50 @@ enum {
JSON_NULL,
};
+typedef enum {
+ JSON_VARIANT_CONTROL,
+ JSON_VARIANT_STRING,
+ JSON_VARIANT_INTEGER,
+ JSON_VARIANT_BOOLEAN,
+ JSON_VARIANT_REAL,
+ JSON_VARIANT_ARRAY,
+ JSON_VARIANT_OBJECT,
+ JSON_VARIANT_NULL
+} JsonVariantType;
+
union json_value {
bool boolean;
double real;
intmax_t integer;
};
+typedef struct JsonVariant {
+ JsonVariantType type;
+ size_t size;
+ union {
+ char *string;
+ struct JsonVariant *objects;
+ union json_value value;
+ };
+} JsonVariant;
+
+int json_variant_new(JsonVariant **ret, JsonVariantType type);
+JsonVariant *json_variant_unref(JsonVariant *v);
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(JsonVariant *, json_variant_unref);
+#define _cleanup_json_variant_unref_ _cleanup_(json_variant_unrefp)
+
+char *json_variant_string(JsonVariant *v);
+bool json_variant_bool(JsonVariant *v);
+intmax_t json_variant_integer(JsonVariant *v);
+double json_variant_real(JsonVariant *v);
+
+JsonVariant *json_variant_element(JsonVariant *v, unsigned index);
+JsonVariant *json_variant_value(JsonVariant *v, const char *key);
+
#define JSON_VALUE_NULL ((union json_value) {})
int json_tokenize(const char **p, char **ret_string, union json_value *ret_value, void **state, unsigned *line);
+
+int json_parse(const char *string, JsonVariant **rv);
+int json_parse_measure(const char *string, size_t *size);
diff --git a/src/shared/lockfile-util.c b/src/shared/lockfile-util.c
new file mode 100644
index 0000000000..05e16d1caa
--- /dev/null
+++ b/src/shared/lockfile-util.c
@@ -0,0 +1,154 @@
+/*-*- 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 <stdlib.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <limits.h>
+#include <sys/file.h>
+
+#include "util.h"
+#include "lockfile-util.h"
+#include "fileio.h"
+
+int make_lock_file(const char *p, int operation, LockFile *ret) {
+ _cleanup_close_ int fd = -1;
+ _cleanup_free_ char *t = NULL;
+ int r;
+
+ /*
+ * We use UNPOSIX locks if they are available. They have nice
+ * semantics, and are mostly compatible with NFS. However,
+ * they are only available on new kernels. When we detect we
+ * are running on an older kernel, then we fall back to good
+ * old BSD locks. They also have nice semantics, but are
+ * slightly problematic on NFS, where they are upgraded to
+ * POSIX locks, even though locally they are orthogonal to
+ * POSIX locks.
+ */
+
+ t = strdup(p);
+ if (!t)
+ return -ENOMEM;
+
+ for (;;) {
+ struct flock fl = {
+ .l_type = (operation & ~LOCK_NB) == LOCK_EX ? F_WRLCK : F_RDLCK,
+ .l_whence = SEEK_SET,
+ };
+ struct stat st;
+
+ fd = open(p, O_CREAT|O_RDWR|O_NOFOLLOW|O_CLOEXEC|O_NOCTTY, 0600);
+ if (fd < 0)
+ return -errno;
+
+ r = fcntl(fd, (operation & LOCK_NB) ? F_OFD_SETLK : F_OFD_SETLKW, &fl);
+ if (r < 0) {
+
+ /* If the kernel is too old, use good old BSD locks */
+ if (errno == EINVAL)
+ r = flock(fd, operation);
+
+ if (r < 0)
+ return errno == EAGAIN ? -EBUSY : -errno;
+ }
+
+ /* If we acquired the lock, let's check if the file
+ * still exists in the file system. If not, then the
+ * previous exclusive owner removed it and then closed
+ * it. In such a case our acquired lock is worthless,
+ * hence try again. */
+
+ r = fstat(fd, &st);
+ if (r < 0)
+ return -errno;
+ if (st.st_nlink > 0)
+ break;
+
+ fd = safe_close(fd);
+ }
+
+ ret->path = t;
+ ret->fd = fd;
+ ret->operation = operation;
+
+ fd = -1;
+ t = NULL;
+
+ return r;
+}
+
+int make_lock_file_for(const char *p, int operation, LockFile *ret) {
+ const char *fn;
+ char *t;
+
+ assert(p);
+ assert(ret);
+
+ fn = basename(p);
+ if (!filename_is_valid(fn))
+ return -EINVAL;
+
+ t = newa(char, strlen(p) + 2 + 4 + 1);
+ stpcpy(stpcpy(stpcpy(mempcpy(t, p, fn - p), ".#"), fn), ".lck");
+
+ return make_lock_file(t, operation, ret);
+}
+
+void release_lock_file(LockFile *f) {
+ int r;
+
+ if (!f)
+ return;
+
+ if (f->path) {
+
+ /* If we are the exclusive owner we can safely delete
+ * the lock file itself. If we are not the exclusive
+ * owner, we can try becoming it. */
+
+ if (f->fd >= 0 &&
+ (f->operation & ~LOCK_NB) == LOCK_SH) {
+ static const struct flock fl = {
+ .l_type = F_WRLCK,
+ .l_whence = SEEK_SET,
+ };
+
+ r = fcntl(f->fd, F_OFD_SETLK, &fl);
+ if (r < 0 && errno == EINVAL)
+ r = flock(f->fd, LOCK_EX|LOCK_NB);
+
+ if (r >= 0)
+ f->operation = LOCK_EX|LOCK_NB;
+ }
+
+ if ((f->operation & ~LOCK_NB) == LOCK_EX)
+ unlink_noerrno(f->path);
+
+ free(f->path);
+ f->path = NULL;
+ }
+
+ f->fd = safe_close(f->fd);
+ f->operation = 0;
+}
diff --git a/src/shared/lockfile-util.h b/src/shared/lockfile-util.h
new file mode 100644
index 0000000000..38d47094bd
--- /dev/null
+++ b/src/shared/lockfile-util.h
@@ -0,0 +1,39 @@
+/*-*- 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/>.
+***/
+
+#include "macro.h"
+#include "missing.h"
+
+typedef struct LockFile {
+ char *path;
+ int fd;
+ int operation;
+} LockFile;
+
+int make_lock_file(const char *p, int operation, LockFile *ret);
+int make_lock_file_for(const char *p, int operation, LockFile *ret);
+void release_lock_file(LockFile *f);
+
+#define _cleanup_release_lock_file_ _cleanup_(release_lock_file)
+
+#define LOCK_FILE_INIT { .fd = -1, .path = NULL }
diff --git a/src/shared/log.c b/src/shared/log.c
index 646a1d6389..b96afc4de4 100644
--- a/src/shared/log.c
+++ b/src/shared/log.c
@@ -29,11 +29,16 @@
#include <stddef.h>
#include <printf.h>
+#include "sd-messages.h"
#include "log.h"
#include "util.h"
#include "missing.h"
#include "macro.h"
#include "socket-util.h"
+#include "formats-util.h"
+#include "process-util.h"
+#include "terminal-util.h"
+#include "signal-util.h"
#define SNDBUF_SIZE (8*1024*1024)
@@ -689,7 +694,8 @@ int log_object_internalv(
va_list ap) {
PROTECT_ERRNO;
- char buffer[LINE_MAX];
+ char *buffer, *b;
+ size_t l;
if (error < 0)
error = -error;
@@ -701,7 +707,21 @@ int log_object_internalv(
if (error != 0)
errno = error;
- vsnprintf(buffer, sizeof(buffer), format, ap);
+ /* Prepend the object name before the message */
+ if (object) {
+ size_t n;
+
+ n = strlen(object);
+ l = n + 2 + LINE_MAX;
+
+ buffer = newa(char, l);
+ b = stpcpy(stpcpy(buffer, object), ": ");
+ } else {
+ l = LINE_MAX;
+ b = buffer = newa(char, l);
+ }
+
+ vsnprintf(b, l, format, ap);
return log_dispatch(level, error, file, line, func, object_field, object, buffer);
}
@@ -1061,3 +1081,58 @@ void log_received_signal(int level, const struct signalfd_siginfo *si) {
void log_set_upgrade_syslog_to_journal(bool b) {
upgrade_syslog_to_journal = b;
}
+
+int log_syntax_internal(
+ const char *unit,
+ int level,
+ const char *config_file,
+ unsigned config_line,
+ int error,
+ const char *file,
+ int line,
+ const char *func,
+ const char *format, ...) {
+
+ PROTECT_ERRNO;
+ char buffer[LINE_MAX];
+ int r;
+ va_list ap;
+
+ if (error < 0)
+ error = -error;
+
+ if (_likely_(LOG_PRI(level) > log_max_level))
+ return -error;
+
+ if (log_target == LOG_TARGET_NULL)
+ return -error;
+
+ if (error != 0)
+ errno = error;
+
+ va_start(ap, format);
+ vsnprintf(buffer, sizeof(buffer), format, ap);
+ va_end(ap);
+
+ if (unit)
+ r = log_struct_internal(
+ level, error,
+ file, line, func,
+ getpid() == 1 ? "UNIT=%s" : "USER_UNIT=%s", unit,
+ LOG_MESSAGE_ID(SD_MESSAGE_INVALID_CONFIGURATION),
+ "CONFIG_FILE=%s", config_file,
+ "CONFIG_LINE=%u", config_line,
+ LOG_MESSAGE("[%s:%u] %s", config_file, config_line, buffer),
+ NULL);
+ else
+ r = log_struct_internal(
+ level, error,
+ file, line, func,
+ LOG_MESSAGE_ID(SD_MESSAGE_INVALID_CONFIGURATION),
+ "CONFIG_FILE=%s", config_file,
+ "CONFIG_LINE=%u", config_line,
+ LOG_MESSAGE("[%s:%u] %s", config_file, config_line, buffer),
+ NULL);
+
+ return r;
+}
diff --git a/src/shared/log.h b/src/shared/log.h
index 2889e1e77f..569762d083 100644
--- a/src/shared/log.h
+++ b/src/shared/log.h
@@ -23,14 +23,13 @@
#include <stdbool.h>
#include <stdarg.h>
+#include <stdlib.h>
#include <syslog.h>
#include <sys/signalfd.h>
-#include <sys/types.h>
-#include <unistd.h>
#include <errno.h>
-#include "macro.h"
#include "sd-id128.h"
+#include "macro.h"
typedef enum LogTarget{
LOG_TARGET_CONSOLE,
@@ -156,12 +155,12 @@ void log_assert_failed_return(
const char *func);
/* Logging with level */
-#define log_full_errno(level, error, ...) \
- ({ \
- int _l = (level), _e = (error); \
- (log_get_max_level() >= LOG_PRI(_l)) \
- ? log_internal(_l, _e, __FILE__, __LINE__, __func__, __VA_ARGS__) \
- : -abs(_e); \
+#define log_full_errno(level, error, ...) \
+ ({ \
+ int _level = (level), _e = (error); \
+ (log_get_max_level() >= LOG_PRI(_level)) \
+ ? log_internal(_level, _e, __FILE__, __LINE__, __func__, __VA_ARGS__) \
+ : -abs(_e); \
})
#define log_full(level, ...) log_full_errno(level, 0, __VA_ARGS__)
@@ -205,8 +204,26 @@ LogTarget log_target_from_string(const char *s) _pure_;
/* Helpers to prepare various fields for structured logging */
#define LOG_MESSAGE(fmt, ...) "MESSAGE=" fmt, ##__VA_ARGS__
#define LOG_MESSAGE_ID(x) "MESSAGE_ID=" SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(x)
-#define LOG_ERRNO(error) "ERRNO=%i", abs(error)
void log_received_signal(int level, const struct signalfd_siginfo *si);
void log_set_upgrade_syslog_to_journal(bool b);
+
+int log_syntax_internal(
+ const char *unit,
+ int level,
+ const char *config_file,
+ unsigned config_line,
+ int error,
+ const char *file,
+ int line,
+ const char *func,
+ const char *format, ...) _printf_(9, 10);
+
+#define log_syntax(unit, level, config_file, config_line, error, ...) \
+ ({ \
+ int _level = (level), _e = (error); \
+ (log_get_max_level() >= LOG_PRI(_level)) \
+ ? log_syntax_internal(unit, _level, config_file, config_line, _e, __FILE__, __LINE__, __func__, __VA_ARGS__) \
+ : -abs(_e); \
+ })
diff --git a/src/shared/login-shared.c b/src/shared/login-shared.c
index 054c77503b..64650a9134 100644
--- a/src/shared/login-shared.c
+++ b/src/shared/login-shared.c
@@ -23,7 +23,9 @@
#include "def.h"
bool session_id_valid(const char *id) {
- assert(id);
- return id[0] && id[strspn(id, LETTERS DIGITS)] == '\0';
+ if (isempty(id))
+ return false;
+
+ return id[strspn(id, LETTERS DIGITS)] == '\0';
}
diff --git a/src/shared/logs-show.c b/src/shared/logs-show.c
index c2495056d7..ac5eb16f62 100644
--- a/src/shared/logs-show.c
+++ b/src/shared/logs-show.c
@@ -20,9 +20,7 @@
***/
#include <time.h>
-#include <assert.h>
#include <errno.h>
-#include <poll.h>
#include <sys/socket.h>
#include <string.h>
#include <fcntl.h>
@@ -32,8 +30,10 @@
#include "util.h"
#include "utf8.h"
#include "hashmap.h"
-#include "fileio.h"
#include "journal-internal.h"
+#include "formats-util.h"
+#include "process-util.h"
+#include "terminal-util.h"
/* up to three lines (each up to 100 characters),
or 300 characters, whichever is less */
@@ -272,7 +272,7 @@ static int output_short(
}
if (r < 0)
- return r;
+ return log_error_errno(r, "Failed to get journal fields: %m");
if (!message)
return 0;
@@ -408,11 +408,9 @@ static int output_verbose(
r = sd_journal_get_data(j, "_SOURCE_REALTIME_TIMESTAMP", &data, &length);
if (r == -ENOENT)
log_debug("Source realtime timestamp not found");
- else if (r < 0) {
- log_full(r == -EADDRNOTAVAIL ? LOG_DEBUG : LOG_ERR,
- "Failed to get source realtime timestamp: %s", strerror(-r));
- return r;
- } else {
+ else if (r < 0)
+ return log_full_errno(r == -EADDRNOTAVAIL ? LOG_DEBUG : LOG_ERR, r, "Failed to get source realtime timestamp: %m");
+ else {
_cleanup_free_ char *value = NULL;
size_t size;
@@ -428,11 +426,8 @@ static int output_verbose(
if (r < 0) {
r = sd_journal_get_realtime_usec(j, &realtime);
- if (r < 0) {
- log_full(r == -EADDRNOTAVAIL ? LOG_DEBUG : LOG_ERR,
- "Failed to get realtime timestamp: %s", strerror(-r));
- return r;
- }
+ if (r < 0)
+ return log_full_errno(r == -EADDRNOTAVAIL ? LOG_DEBUG : LOG_ERR, r, "Failed to get realtime timestamp: %m");
}
r = sd_journal_get_cursor(j, &cursor);
@@ -682,7 +677,7 @@ static int output_json(
h = hashmap_new(&string_hash_ops);
if (!h)
- return -ENOMEM;
+ return log_oom();
/* First round, iterate through the entry and count how often each field appears */
JOURNAL_FOREACH_DATA_RETVAL(j, data, length, r) {
@@ -700,7 +695,7 @@ static int output_json(
n = strndup(data, eq - (const char*) data);
if (!n) {
- r = -ENOMEM;
+ r = log_oom();
goto finish;
}
@@ -709,13 +704,16 @@ static int output_json(
r = hashmap_put(h, n, UINT_TO_PTR(1));
if (r < 0) {
free(n);
+ log_oom();
goto finish;
}
} else {
r = hashmap_update(h, n, UINT_TO_PTR(u + 1));
free(n);
- if (r < 0)
+ if (r < 0) {
+ log_oom();
goto finish;
+ }
}
}
@@ -753,7 +751,7 @@ static int output_json(
n = strndup(data, m);
if (!n) {
- r = -ENOMEM;
+ r = log_oom();
goto finish;
}
@@ -948,11 +946,11 @@ static int show_journal(FILE *f,
/* Seek to end */
r = sd_journal_seek_tail(j);
if (r < 0)
- goto finish;
+ return log_error_errno(r, "Failed to seek to tail: %m");
r = sd_journal_previous_skip(j, how_many);
if (r < 0)
- goto finish;
+ return log_error_errno(r, "Failed to skip previous: %m");
for (;;) {
for (;;) {
@@ -961,7 +959,7 @@ static int show_journal(FILE *f,
if (need_seek) {
r = sd_journal_next(j);
if (r < 0)
- goto finish;
+ return log_error_errno(r, "Failed to iterate through journal: %m");
}
if (r == 0)
@@ -977,7 +975,7 @@ static int show_journal(FILE *f,
if (r == -ESTALE)
continue;
else if (r < 0)
- goto finish;
+ return log_error_errno(r, "Failed to get journal time: %m");
if (usec < not_before)
continue;
@@ -988,22 +986,22 @@ static int show_journal(FILE *f,
r = output_journal(f, j, mode, n_columns, flags, ellipsized);
if (r < 0)
- goto finish;
+ return r;
}
if (warn_cutoff && line < how_many && not_before > 0) {
sd_id128_t boot_id;
- usec_t cutoff;
+ usec_t cutoff = 0;
/* Check whether the cutoff line is too early */
r = sd_id128_get_boot(&boot_id);
if (r < 0)
- goto finish;
+ return log_error_errno(r, "Failed to get boot id: %m");
r = sd_journal_get_cutoff_monotonic_usec(j, boot_id, &cutoff, NULL);
if (r < 0)
- goto finish;
+ return log_error_errno(r, "Failed to get journal cutoff time: %m");
if (r > 0 && not_before < cutoff) {
maybe_print_begin_newline(f, &flags);
@@ -1018,12 +1016,11 @@ static int show_journal(FILE *f,
r = sd_journal_wait(j, USEC_INFINITY);
if (r < 0)
- goto finish;
+ return log_error_errno(r, "Failed to wait for journal: %m");
}
-finish:
- return r;
+ return 0;
}
int add_matches_for_unit(sd_journal *j, const char *unit) {
@@ -1166,9 +1163,9 @@ static int get_boot_id_for_machine(const char *machine, sd_id128_t *boot_id) {
if (fd < 0)
_exit(EXIT_FAILURE);
- k = loop_read(fd, buf, 36, false);
+ r = loop_read_exact(fd, buf, 36, false);
safe_close(fd);
- if (k != 36)
+ if (r < 0)
_exit(EXIT_FAILURE);
k = send(pair[1], buf, 36, MSG_NOSIGNAL);
@@ -1220,7 +1217,7 @@ int add_match_this_boot(sd_journal *j, const char *machine) {
r = sd_journal_add_conjunction(j);
if (r < 0)
- return r;
+ return log_error_errno(r, "Failed to add conjunction: %m");
return 0;
}
@@ -1250,7 +1247,7 @@ int show_journal_by_unit(
r = sd_journal_open(&j, journal_open_flags);
if (r < 0)
- return r;
+ return log_error_errno(r, "Failed to open journal: %m");
r = add_match_this_boot(j, NULL);
if (r < 0)
@@ -1261,12 +1258,15 @@ int show_journal_by_unit(
else
r = add_matches_for_user_unit(j, unit, uid);
if (r < 0)
- return r;
+ return log_error_errno(r, "Failed to add unit matches: %m");
if (_unlikely_(log_get_max_level() >= LOG_DEBUG)) {
_cleanup_free_ char *filter;
filter = journal_make_match_string(j);
+ if (!filter)
+ return log_oom();
+
log_debug("Journal filter: %s", filter);
}
diff --git a/src/shared/logs-show.h b/src/shared/logs-show.h
index 8d9641e8ac..569e1faa55 100644
--- a/src/shared/logs-show.h
+++ b/src/shared/logs-show.h
@@ -22,7 +22,6 @@
***/
#include <stdbool.h>
-#include <unistd.h>
#include <sys/types.h>
#include "sd-journal.h"
diff --git a/src/shared/machine-image.c b/src/shared/machine-image.c
index 8d61507e84..273dacff1f 100644
--- a/src/shared/machine-image.c
+++ b/src/shared/machine-image.c
@@ -23,12 +23,12 @@
#include <linux/fs.h>
#include <fcntl.h>
-#include "strv.h"
#include "utf8.h"
#include "btrfs-util.h"
#include "path-util.h"
#include "copy.h"
#include "mkdir.h"
+#include "rm-rf.h"
#include "machine-image.h"
static const char image_search_path[] =
@@ -136,12 +136,11 @@ static int image_make(
/* btrfs subvolumes have inode 256 */
if (st.st_ino == 256) {
- struct statfs sfs;
- if (fstatfs(fd, &sfs) < 0)
- return -errno;
-
- if (F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC)) {
+ r = btrfs_is_filesystem(fd);
+ if (r < 0)
+ return r;
+ if (r) {
BtrfsSubvolInfo info;
BtrfsQuotaInfo quota;
@@ -164,10 +163,10 @@ static int image_make(
r = btrfs_subvol_get_quota_fd(fd, &quota);
if (r >= 0) {
- (*ret)->usage = quota.referred;
+ (*ret)->usage = quota.referenced;
(*ret)->usage_exclusive = quota.exclusive;
- (*ret)->limit = quota.referred_max;
+ (*ret)->limit = quota.referenced_max;
(*ret)->limit_exclusive = quota.exclusive_max;
}
@@ -358,19 +357,21 @@ int image_remove(Image *i) {
switch (i->type) {
case IMAGE_SUBVOLUME:
- return btrfs_subvol_remove(i->path);
+ return btrfs_subvol_remove(i->path, true);
case IMAGE_DIRECTORY:
/* Allow deletion of read-only directories */
(void) chattr_path(i->path, false, FS_IMMUTABLE_FL);
-
- /* fall through */
+ return rm_rf(i->path, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME);
case IMAGE_RAW:
- return rm_rf_dangerous(i->path, false, true, false);
+ if (unlink(i->path) < 0)
+ return -errno;
+
+ return 0;
default:
- return -ENOTSUP;
+ return -EOPNOTSUPP;
}
}
@@ -431,7 +432,7 @@ int image_rename(Image *i, const char *new_name) {
}
default:
- return -ENOTSUP;
+ return -EOPNOTSUPP;
}
if (!new_path)
@@ -441,8 +442,9 @@ int image_rename(Image *i, const char *new_name) {
if (!nn)
return -ENOMEM;
- if (renameat2(AT_FDCWD, i->path, AT_FDCWD, new_path, RENAME_NOREPLACE) < 0)
- return -errno;
+ r = rename_noreplace(AT_FDCWD, i->path, AT_FDCWD, new_path);
+ if (r < 0)
+ return r;
/* Restore the immutable bit, if it was set before */
if (file_attr & FS_IMMUTABLE_FL)
@@ -488,7 +490,7 @@ int image_clone(Image *i, const char *new_name, bool read_only) {
case IMAGE_DIRECTORY:
new_path = strjoina("/var/lib/machines/", new_name);
- r = btrfs_subvol_snapshot(i->path, new_path, read_only, true);
+ r = btrfs_subvol_snapshot(i->path, new_path, (read_only ? BTRFS_SNAPSHOT_READ_ONLY : 0) | BTRFS_SNAPSHOT_FALLBACK_COPY | BTRFS_SNAPSHOT_RECURSIVE);
break;
case IMAGE_RAW:
@@ -498,7 +500,7 @@ int image_clone(Image *i, const char *new_name, bool read_only) {
break;
default:
- return -ENOTSUP;
+ return -EOPNOTSUPP;
}
if (r < 0)
@@ -563,7 +565,7 @@ int image_read_only(Image *i, bool b) {
}
default:
- return -ENOTSUP;
+ return -EOPNOTSUPP;
}
return 0;
@@ -601,7 +603,7 @@ int image_path_lock(const char *path, int operation, LockFile *global, LockFile
return r;
if (p) {
- mkdir_p("/run/systemd/nspawn/locks", 0600);
+ mkdir_p("/run/systemd/nspawn/locks", 0700);
r = make_lock_file(p, operation, global);
if (r < 0) {
@@ -614,6 +616,19 @@ int image_path_lock(const char *path, int operation, LockFile *global, LockFile
return 0;
}
+int image_set_limit(Image *i, uint64_t referenced_max) {
+ assert(i);
+
+ if (path_equal(i->path, "/") ||
+ path_startswith(i->path, "/usr"))
+ return -EROFS;
+
+ if (i->type != IMAGE_SUBVOLUME)
+ return -EOPNOTSUPP;
+
+ return btrfs_quota_limit(i->path, referenced_max);
+}
+
int image_name_lock(const char *name, int operation, LockFile *ret) {
const char *p;
@@ -628,7 +643,7 @@ int image_name_lock(const char *name, int operation, LockFile *ret) {
if (streq(name, ".host"))
return -EBUSY;
- mkdir_p("/run/systemd/nspawn/locks", 0600);
+ mkdir_p("/run/systemd/nspawn/locks", 0700);
p = strjoina("/run/systemd/nspawn/locks/name-", name);
return make_lock_file(p, operation, ret);
diff --git a/src/shared/machine-image.h b/src/shared/machine-image.h
index 75fa5f4533..f041600fbf 100644
--- a/src/shared/machine-image.h
+++ b/src/shared/machine-image.h
@@ -22,6 +22,7 @@
***/
#include "time-util.h"
+#include "lockfile-util.h"
#include "hashmap.h"
typedef enum ImageType {
@@ -45,6 +46,8 @@ typedef struct Image {
uint64_t usage_exclusive;
uint64_t limit;
uint64_t limit_exclusive;
+
+ void *userdata;
} Image;
Image *image_unref(Image *i);
@@ -68,3 +71,5 @@ bool image_name_is_valid(const char *s) _pure_;
int image_path_lock(const char *path, int operation, LockFile *global, LockFile *local);
int image_name_lock(const char *name, int operation, LockFile *ret);
+
+int image_set_limit(Image *i, uint64_t referenced_max);
diff --git a/src/shared/machine-pool.c b/src/shared/machine-pool.c
new file mode 100644
index 0000000000..d27931cb4a
--- /dev/null
+++ b/src/shared/machine-pool.c
@@ -0,0 +1,379 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2015 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/prctl.h>
+#include <sys/vfs.h>
+#include <sys/statvfs.h>
+#include <sys/mount.h>
+
+#include "util.h"
+#include "process-util.h"
+#include "lockfile-util.h"
+#include "mkdir.h"
+#include "btrfs-util.h"
+#include "path-util.h"
+#include "signal-util.h"
+#include "machine-pool.h"
+
+#define VAR_LIB_MACHINES_SIZE_START (1024UL*1024UL*500UL)
+#define VAR_LIB_MACHINES_FREE_MIN (1024UL*1024UL*750UL)
+
+static int check_btrfs(void) {
+ struct statfs sfs;
+
+ if (statfs("/var/lib/machines", &sfs) < 0) {
+ if (errno != ENOENT)
+ return -errno;
+
+ if (statfs("/var/lib", &sfs) < 0)
+ return -errno;
+ }
+
+ return F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC);
+}
+
+static int setup_machine_raw(uint64_t size, sd_bus_error *error) {
+ _cleanup_free_ char *tmp = NULL;
+ _cleanup_close_ int fd = -1;
+ struct statvfs ss;
+ pid_t pid = 0;
+ siginfo_t si;
+ int r;
+
+ /* We want to be able to make use of btrfs-specific file
+ * system features, in particular subvolumes, reflinks and
+ * quota. Hence, if we detect that /var/lib/machines.raw is
+ * not located on btrfs, let's create a loopback file, place a
+ * btrfs file system into it, and mount it to
+ * /var/lib/machines. */
+
+ fd = open("/var/lib/machines.raw", O_RDWR|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
+ if (fd >= 0) {
+ r = fd;
+ fd = -1;
+ return r;
+ }
+
+ if (errno != ENOENT)
+ return sd_bus_error_set_errnof(error, errno, "Failed to open /var/lib/machines.raw: %m");
+
+ r = tempfn_xxxxxx("/var/lib/machines.raw", &tmp);
+ if (r < 0)
+ return r;
+
+ (void) mkdir_p_label("/var/lib", 0755);
+ fd = open(tmp, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0600);
+ if (fd < 0)
+ return sd_bus_error_set_errnof(error, errno, "Failed to create /var/lib/machines.raw: %m");
+
+ if (fstatvfs(fd, &ss) < 0) {
+ r = sd_bus_error_set_errnof(error, errno, "Failed to determine free space on /var/lib/machines.raw: %m");
+ goto fail;
+ }
+
+ if (ss.f_bsize * ss.f_bavail < VAR_LIB_MACHINES_FREE_MIN) {
+ r = sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Not enough free disk space to set up /var/lib/machines.");
+ goto fail;
+ }
+
+ if (ftruncate(fd, size) < 0) {
+ r = sd_bus_error_set_errnof(error, errno, "Failed to enlarge /var/lib/machines.raw: %m");
+ goto fail;
+ }
+
+ pid = fork();
+ if (pid < 0) {
+ r = sd_bus_error_set_errnof(error, errno, "Failed to fork mkfs.btrfs: %m");
+ goto fail;
+ }
+
+ if (pid == 0) {
+
+ /* Child */
+
+ reset_all_signal_handlers();
+ reset_signal_mask();
+ assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
+
+ fd = safe_close(fd);
+
+ execlp("mkfs.btrfs", "-Lvar-lib-machines", tmp, NULL);
+ if (errno == ENOENT)
+ return 99;
+
+ _exit(EXIT_FAILURE);
+ }
+
+ r = wait_for_terminate(pid, &si);
+ if (r < 0) {
+ sd_bus_error_set_errnof(error, r, "Failed to wait for mkfs.btrfs: %m");
+ goto fail;
+ }
+
+ pid = 0;
+
+ if (si.si_code != CLD_EXITED) {
+ r = sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "mkfs.btrfs died abnormally.");
+ goto fail;
+ }
+ if (si.si_status == 99) {
+ r = sd_bus_error_set_errnof(error, ENOENT, "Cannot set up /var/lib/machines, mkfs.btrfs is missing");
+ goto fail;
+ }
+ if (si.si_status != 0) {
+ r = sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "mkfs.btrfs failed with error code %i", si.si_status);
+ goto fail;
+ }
+
+ r = rename_noreplace(AT_FDCWD, tmp, AT_FDCWD, "/var/lib/machines.raw");
+ if (r < 0) {
+ sd_bus_error_set_errnof(error, r, "Failed to move /var/lib/machines.raw into place: %m");
+ goto fail;
+ }
+
+ r = fd;
+ fd = -1;
+
+ return r;
+
+fail:
+ unlink_noerrno(tmp);
+
+ if (pid > 1)
+ kill_and_sigcont(pid, SIGKILL);
+
+ return r;
+}
+
+int setup_machine_directory(uint64_t size, sd_bus_error *error) {
+ _cleanup_release_lock_file_ LockFile lock_file = LOCK_FILE_INIT;
+ struct loop_info64 info = {
+ .lo_flags = LO_FLAGS_AUTOCLEAR,
+ };
+ _cleanup_close_ int fd = -1, control = -1, loop = -1;
+ _cleanup_free_ char* loopdev = NULL;
+ char tmpdir[] = "/tmp/import-mount.XXXXXX", *mntdir = NULL;
+ bool tmpdir_made = false, mntdir_made = false, mntdir_mounted = false;
+ char buf[FORMAT_BYTES_MAX];
+ int r, nr = -1;
+
+ /* btrfs cannot handle file systems < 16M, hence use this as minimum */
+ if (size == (uint64_t) -1)
+ size = VAR_LIB_MACHINES_SIZE_START;
+ else if (size < 16*1024*1024)
+ size = 16*1024*1024;
+
+ /* Make sure we only set the directory up once at a time */
+ r = make_lock_file("/run/systemd/machines.lock", LOCK_EX, &lock_file);
+ if (r < 0)
+ return r;
+
+ r = check_btrfs();
+ if (r < 0)
+ return sd_bus_error_set_errnof(error, r, "Failed to determine whether /var/lib/machines is located on btrfs: %m");
+ if (r > 0) {
+ (void) btrfs_subvol_make_label("/var/lib/machines");
+
+ r = btrfs_quota_enable("/var/lib/machines", true);
+ if (r < 0)
+ log_warning_errno(r, "Failed to enable quota, ignoring: %m");
+
+ return 0;
+ }
+
+ if (path_is_mount_point("/var/lib/machines", AT_SYMLINK_FOLLOW) > 0 ||
+ dir_is_empty("/var/lib/machines") == 0)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "/var/lib/machines is not a btrfs file system. Operation is not supported on legacy file systems.");
+
+ fd = setup_machine_raw(size, error);
+ if (fd < 0)
+ return fd;
+
+ control = open("/dev/loop-control", O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
+ if (control < 0)
+ return sd_bus_error_set_errnof(error, errno, "Failed to open /dev/loop-control: %m");
+
+ nr = ioctl(control, LOOP_CTL_GET_FREE);
+ if (nr < 0)
+ return sd_bus_error_set_errnof(error, errno, "Failed to allocate loop device: %m");
+
+ if (asprintf(&loopdev, "/dev/loop%i", nr) < 0) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ loop = open(loopdev, O_CLOEXEC|O_RDWR|O_NOCTTY|O_NONBLOCK);
+ if (loop < 0) {
+ r = sd_bus_error_set_errnof(error, errno, "Failed to open loopback device: %m");
+ goto fail;
+ }
+
+ if (ioctl(loop, LOOP_SET_FD, fd) < 0) {
+ r = sd_bus_error_set_errnof(error, errno, "Failed to bind loopback device: %m");
+ goto fail;
+ }
+
+ if (ioctl(loop, LOOP_SET_STATUS64, &info) < 0) {
+ r = sd_bus_error_set_errnof(error, errno, "Failed to enable auto-clear for loopback device: %m");
+ goto fail;
+ }
+
+ /* We need to make sure the new /var/lib/machines directory
+ * has an access mode of 0700 at the time it is first made
+ * available. mkfs will create it with 0755 however. Hence,
+ * let's mount the directory into an inaccessible directory
+ * below /tmp first, fix the access mode, and move it to the
+ * public place then. */
+
+ if (!mkdtemp(tmpdir)) {
+ r = sd_bus_error_set_errnof(error, errno, "Failed to create temporary mount parent directory: %m");
+ goto fail;
+ }
+ tmpdir_made = true;
+
+ mntdir = strjoina(tmpdir, "/mnt");
+ if (mkdir(mntdir, 0700) < 0) {
+ r = sd_bus_error_set_errnof(error, errno, "Failed to create temporary mount directory: %m");
+ goto fail;
+ }
+ mntdir_made = true;
+
+ if (mount(loopdev, mntdir, "btrfs", 0, NULL) < 0) {
+ r = sd_bus_error_set_errnof(error, errno, "Failed to mount loopback device: %m");
+ goto fail;
+ }
+ mntdir_mounted = true;
+
+ r = btrfs_quota_enable(mntdir, true);
+ if (r < 0)
+ log_warning_errno(r, "Failed to enable quota, ignoring: %m");
+
+ if (chmod(mntdir, 0700) < 0) {
+ r = sd_bus_error_set_errnof(error, errno, "Failed to fix owner: %m");
+ goto fail;
+ }
+
+ (void) mkdir_p_label("/var/lib/machines", 0700);
+
+ if (mount(mntdir, "/var/lib/machines", NULL, MS_BIND, NULL) < 0) {
+ r = sd_bus_error_set_errnof(error, errno, "Failed to mount directory into right place: %m");
+ goto fail;
+ }
+
+ (void) syncfs(fd);
+
+ log_info("Set up /var/lib/machines as btrfs loopback file system of size %s mounted on /var/lib/machines.raw.", format_bytes(buf, sizeof(buf), size));
+
+ (void) umount2(mntdir, MNT_DETACH);
+ (void) rmdir(mntdir);
+ (void) rmdir(tmpdir);
+
+ return 0;
+
+fail:
+ if (mntdir_mounted)
+ (void) umount2(mntdir, MNT_DETACH);
+
+ if (mntdir_made)
+ (void) rmdir(mntdir);
+ if (tmpdir_made)
+ (void) rmdir(tmpdir);
+
+ if (loop >= 0) {
+ (void) ioctl(loop, LOOP_CLR_FD);
+ loop = safe_close(loop);
+ }
+
+ if (control >= 0 && nr >= 0)
+ (void) ioctl(control, LOOP_CTL_REMOVE, nr);
+
+ return r;
+}
+
+static int sync_path(const char *p) {
+ _cleanup_close_ int fd = -1;
+
+ fd = open(p, O_RDONLY|O_CLOEXEC|O_NOCTTY);
+ if (fd < 0)
+ return -errno;
+
+ if (syncfs(fd) < 0)
+ return -errno;
+
+ return 0;
+}
+
+int grow_machine_directory(void) {
+ char buf[FORMAT_BYTES_MAX];
+ struct statvfs a, b;
+ uint64_t old_size, new_size, max_add;
+ int r;
+
+ /* Ensure the disk space data is accurate */
+ sync_path("/var/lib/machines");
+ sync_path("/var/lib/machines.raw");
+
+ if (statvfs("/var/lib/machines.raw", &a) < 0)
+ return -errno;
+
+ if (statvfs("/var/lib/machines", &b) < 0)
+ return -errno;
+
+ /* Don't grow if not enough disk space is available on the host */
+ if (((uint64_t) a.f_bavail * (uint64_t) a.f_bsize) <= VAR_LIB_MACHINES_FREE_MIN)
+ return 0;
+
+ /* Don't grow if at least 1/3th of the fs is still free */
+ if (b.f_bavail > b.f_blocks / 3)
+ return 0;
+
+ /* Calculate how much we are willing to add at maximum */
+ max_add = ((uint64_t) a.f_bavail * (uint64_t) a.f_bsize) - VAR_LIB_MACHINES_FREE_MIN;
+
+ /* Calculate the old size */
+ old_size = (uint64_t) b.f_blocks * (uint64_t) b.f_bsize;
+
+ /* Calculate the new size as three times the size of what is used right now */
+ new_size = ((uint64_t) b.f_blocks - (uint64_t) b.f_bavail) * (uint64_t) b.f_bsize * 3;
+
+ /* Always, grow at least to the start size */
+ if (new_size < VAR_LIB_MACHINES_SIZE_START)
+ new_size = VAR_LIB_MACHINES_SIZE_START;
+
+ /* If the new size is smaller than the old size, don't grow */
+ if (new_size < old_size)
+ return 0;
+
+ /* Ensure we never add more than the maximum */
+ if (new_size > old_size + max_add)
+ new_size = old_size + max_add;
+
+ r = btrfs_resize_loopback("/var/lib/machines", new_size, true);
+ if (r <= 0)
+ return r;
+
+ r = btrfs_quota_limit("/var/lib/machines", new_size);
+ if (r < 0)
+ return r;
+
+ log_info("Grew /var/lib/machines btrfs loopback file system to %s.", format_bytes(buf, sizeof(buf), new_size));
+ return 1;
+}
diff --git a/src/shared/machine-pool.h b/src/shared/machine-pool.h
new file mode 100644
index 0000000000..fe01d3d47c
--- /dev/null
+++ b/src/shared/machine-pool.h
@@ -0,0 +1,30 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2015 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"
+
+/* Grow the /var/lib/machines directory after each 10MiB written */
+#define GROW_INTERVAL_BYTES (UINT64_C(10) * UINT64_C(1024) * UINT64_C(1024))
+
+int setup_machine_directory(uint64_t size, sd_bus_error *error);
+int grow_machine_directory(void);
diff --git a/src/shared/macro.h b/src/shared/macro.h
index 7f89951d62..7ae1ed80b6 100644
--- a/src/shared/macro.h
+++ b/src/shared/macro.h
@@ -256,6 +256,15 @@ static inline unsigned long ALIGN_POWER2(unsigned long u) {
} \
} while (false)
+#define assert_return_errno(expr, r, err) \
+ do { \
+ if (_unlikely_(!(expr))) { \
+ log_assert_failed_return(#expr, __FILE__, __LINE__, __PRETTY_FUNCTION__); \
+ errno = err; \
+ return (r); \
+ } \
+ } while (false)
+
#define PTR_TO_INT(p) ((int) ((intptr_t) (p)))
#define INT_TO_PTR(u) ((void *) ((intptr_t) (u)))
#define PTR_TO_UINT(p) ((unsigned int) ((uintptr_t) (p)))
diff --git a/src/shared/memfd-util.c b/src/shared/memfd-util.c
index 6624c5e7db..e99a738e1f 100644
--- a/src/shared/memfd-util.c
+++ b/src/shared/memfd-util.c
@@ -21,7 +21,6 @@
#include <stdio.h>
#include <fcntl.h>
-#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/prctl.h>
@@ -30,7 +29,6 @@
#endif
#include "util.h"
-#include "bus-label.h"
#include "memfd-util.h"
#include "utf8.h"
#include "missing.h"
diff --git a/src/shared/memfd-util.h b/src/shared/memfd-util.h
index cf588fe02f..3ed551fb37 100644
--- a/src/shared/memfd-util.h
+++ b/src/shared/memfd-util.h
@@ -21,12 +21,7 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <inttypes.h>
-#include <sys/types.h>
-#include <stdio.h>
-#include "macro.h"
-#include "util.h"
int memfd_new(const char *name);
int memfd_new_and_map(const char *name, size_t sz, void **p);
diff --git a/src/shared/missing.h b/src/shared/missing.h
index b33a70cb2c..be7f6186fc 100644
--- a/src/shared/missing.h
+++ b/src/shared/missing.h
@@ -35,6 +35,7 @@
#include <linux/loop.h>
#include <linux/audit.h>
#include <linux/capability.h>
+#include <linux/neighbour.h>
#ifdef HAVE_AUDIT
#include <libaudit.h>
@@ -179,6 +180,16 @@ static inline int memfd_create(const char *name, unsigned int flags) {
# define __NR_getrandom 349
# elif defined(__powerpc__)
# define __NR_getrandom 359
+# elif defined _MIPS_SIM
+# if _MIPS_SIM == _MIPS_SIM_ABI32
+# define __NR_getrandom 4353
+# endif
+# if _MIPS_SIM == _MIPS_SIM_NABI32
+# define __NR_getrandom 6317
+# endif
+# if _MIPS_SIM == _MIPS_SIM_ABI64
+# define __NR_getrandom 5313
+# endif
# else
# warning "__NR_getrandom unknown for your architecture"
# define __NR_getrandom 0xffffffff
@@ -219,12 +230,64 @@ static inline int getrandom(void *buffer, size_t count, unsigned flags) {
#define BTRFS_UUID_SIZE 16
#endif
+#ifndef BTRFS_SUBVOL_RDONLY
+#define BTRFS_SUBVOL_RDONLY (1ULL << 1)
+#endif
+
+#ifndef BTRFS_SUBVOL_NAME_MAX
+#define BTRFS_SUBVOL_NAME_MAX 4039
+#endif
+
+#ifndef BTRFS_INO_LOOKUP_PATH_MAX
+#define BTRFS_INO_LOOKUP_PATH_MAX 4080
+#endif
+
+#ifndef BTRFS_SEARCH_ARGS_BUFSIZE
+#define BTRFS_SEARCH_ARGS_BUFSIZE (4096 - sizeof(struct btrfs_ioctl_search_key))
+#endif
+
#ifndef HAVE_LINUX_BTRFS_H
struct btrfs_ioctl_vol_args {
int64_t fd;
char name[BTRFS_PATH_NAME_MAX + 1];
};
+struct btrfs_qgroup_limit {
+ __u64 flags;
+ __u64 max_rfer;
+ __u64 max_excl;
+ __u64 rsv_rfer;
+ __u64 rsv_excl;
+};
+
+struct btrfs_qgroup_inherit {
+ __u64 flags;
+ __u64 num_qgroups;
+ __u64 num_ref_copies;
+ __u64 num_excl_copies;
+ struct btrfs_qgroup_limit lim;
+ __u64 qgroups[0];
+};
+
+struct btrfs_ioctl_qgroup_limit_args {
+ __u64 qgroupid;
+ struct btrfs_qgroup_limit lim;
+};
+
+struct btrfs_ioctl_vol_args_v2 {
+ __s64 fd;
+ __u64 transid;
+ __u64 flags;
+ union {
+ struct {
+ __u64 size;
+ struct btrfs_qgroup_inherit *qgroup_inherit;
+ };
+ __u64 unused[4];
+ };
+ char name[BTRFS_SUBVOL_NAME_MAX + 1];
+};
+
struct btrfs_ioctl_dev_info_args {
uint64_t devid; /* in/out */
uint8_t uuid[BTRFS_UUID_SIZE]; /* in/out */
@@ -240,6 +303,76 @@ struct btrfs_ioctl_fs_info_args {
uint8_t fsid[BTRFS_FSID_SIZE]; /* out */
uint64_t reserved[124]; /* pad to 1k */
};
+
+struct btrfs_ioctl_ino_lookup_args {
+ __u64 treeid;
+ __u64 objectid;
+ char name[BTRFS_INO_LOOKUP_PATH_MAX];
+};
+
+struct btrfs_ioctl_search_key {
+ /* which root are we searching. 0 is the tree of tree roots */
+ __u64 tree_id;
+
+ /* keys returned will be >= min and <= max */
+ __u64 min_objectid;
+ __u64 max_objectid;
+
+ /* keys returned will be >= min and <= max */
+ __u64 min_offset;
+ __u64 max_offset;
+
+ /* max and min transids to search for */
+ __u64 min_transid;
+ __u64 max_transid;
+
+ /* keys returned will be >= min and <= max */
+ __u32 min_type;
+ __u32 max_type;
+
+ /*
+ * how many items did userland ask for, and how many are we
+ * returning
+ */
+ __u32 nr_items;
+
+ /* align to 64 bits */
+ __u32 unused;
+
+ /* some extra for later */
+ __u64 unused1;
+ __u64 unused2;
+ __u64 unused3;
+ __u64 unused4;
+};
+
+struct btrfs_ioctl_search_header {
+ __u64 transid;
+ __u64 objectid;
+ __u64 offset;
+ __u32 type;
+ __u32 len;
+};
+
+
+struct btrfs_ioctl_search_args {
+ struct btrfs_ioctl_search_key key;
+ char buf[BTRFS_SEARCH_ARGS_BUFSIZE];
+};
+
+struct btrfs_ioctl_clone_range_args {
+ __s64 src_fd;
+ __u64 src_offset, src_length;
+ __u64 dest_offset;
+};
+
+#define BTRFS_QUOTA_CTL_ENABLE 1
+#define BTRFS_QUOTA_CTL_DISABLE 2
+#define BTRFS_QUOTA_CTL_RESCAN__NOTUSED 3
+struct btrfs_ioctl_quota_ctl_args {
+ __u64 cmd;
+ __u64 status;
+};
#endif
#ifndef BTRFS_IOC_DEFRAG
@@ -247,6 +380,53 @@ struct btrfs_ioctl_fs_info_args {
struct btrfs_ioctl_vol_args)
#endif
+#ifndef BTRFS_IOC_RESIZE
+#define BTRFS_IOC_RESIZE _IOW(BTRFS_IOCTL_MAGIC, 3, \
+ struct btrfs_ioctl_vol_args)
+#endif
+
+#ifndef BTRFS_IOC_CLONE
+#define BTRFS_IOC_CLONE _IOW(BTRFS_IOCTL_MAGIC, 9, int)
+#endif
+
+#ifndef BTRFS_IOC_CLONE_RANGE
+#define BTRFS_IOC_CLONE_RANGE _IOW(BTRFS_IOCTL_MAGIC, 13, \
+ struct btrfs_ioctl_clone_range_args)
+#endif
+
+#ifndef BTRFS_IOC_SUBVOL_CREATE
+#define BTRFS_IOC_SUBVOL_CREATE _IOW(BTRFS_IOCTL_MAGIC, 14, \
+ struct btrfs_ioctl_vol_args)
+#endif
+
+#ifndef BTRFS_IOC_SNAP_DESTROY
+#define BTRFS_IOC_SNAP_DESTROY _IOW(BTRFS_IOCTL_MAGIC, 15, \
+ struct btrfs_ioctl_vol_args)
+#endif
+
+#ifndef BTRFS_IOC_TREE_SEARCH
+#define BTRFS_IOC_TREE_SEARCH _IOWR(BTRFS_IOCTL_MAGIC, 17, \
+ struct btrfs_ioctl_search_args)
+#endif
+
+#ifndef BTRFS_IOC_INO_LOOKUP
+#define BTRFS_IOC_INO_LOOKUP _IOWR(BTRFS_IOCTL_MAGIC, 18, \
+ struct btrfs_ioctl_ino_lookup_args)
+#endif
+
+#ifndef BTRFS_IOC_SNAP_CREATE_V2
+#define BTRFS_IOC_SNAP_CREATE_V2 _IOW(BTRFS_IOCTL_MAGIC, 23, \
+ struct btrfs_ioctl_vol_args_v2)
+#endif
+
+#ifndef BTRFS_IOC_SUBVOL_GETFLAGS
+#define BTRFS_IOC_SUBVOL_GETFLAGS _IOR(BTRFS_IOCTL_MAGIC, 25, __u64)
+#endif
+
+#ifndef BTRFS_IOC_SUBVOL_SETFLAGS
+#define BTRFS_IOC_SUBVOL_SETFLAGS _IOW(BTRFS_IOCTL_MAGIC, 26, __u64)
+#endif
+
#ifndef BTRFS_IOC_DEV_INFO
#define BTRFS_IOC_DEV_INFO _IOWR(BTRFS_IOCTL_MAGIC, 30, \
struct btrfs_ioctl_dev_info_args)
@@ -262,10 +442,24 @@ struct btrfs_ioctl_fs_info_args {
struct btrfs_ioctl_vol_args)
#endif
+#ifndef BTRFS_IOC_QUOTA_CTL
+#define BTRFS_IOC_QUOTA_CTL _IOWR(BTRFS_IOCTL_MAGIC, 40, \
+ struct btrfs_ioctl_quota_ctl_args)
+#endif
+
+#ifndef BTRFS_IOC_QGROUP_LIMIT
+#define BTRFS_IOC_QGROUP_LIMIT _IOR(BTRFS_IOCTL_MAGIC, 43, \
+ struct btrfs_ioctl_qgroup_limit_args)
+#endif
+
#ifndef BTRFS_FIRST_FREE_OBJECTID
#define BTRFS_FIRST_FREE_OBJECTID 256
#endif
+#ifndef BTRFS_LAST_FREE_OBJECTID
+#define BTRFS_LAST_FREE_OBJECTID -256ULL
+#endif
+
#ifndef BTRFS_ROOT_TREE_OBJECTID
#define BTRFS_ROOT_TREE_OBJECTID 1
#endif
@@ -290,6 +484,10 @@ struct btrfs_ioctl_fs_info_args {
#define BTRFS_QGROUP_LIMIT_KEY 244
#endif
+#ifndef BTRFS_ROOT_BACKREF_KEY
+#define BTRFS_ROOT_BACKREF_KEY 144
+#endif
+
#ifndef BTRFS_SUPER_MAGIC
#define BTRFS_SUPER_MAGIC 0x9123683E
#endif
@@ -543,7 +741,7 @@ static inline int setns(int fd, int nstype) {
#define IFLA_VLAN_MAX (__IFLA_VLAN_MAX - 1)
#endif
-#if !HAVE_DECL_IFLA_VXLAN_LOCAL6
+#if !HAVE_DECL_IFLA_VXLAN_REMCSUM_NOPARTIAL
#define IFLA_VXLAN_UNSPEC 0
#define IFLA_VXLAN_ID 1
#define IFLA_VXLAN_GROUP 2
@@ -562,7 +760,14 @@ static inline int setns(int fd, int nstype) {
#define IFLA_VXLAN_PORT 15
#define IFLA_VXLAN_GROUP6 16
#define IFLA_VXLAN_LOCAL6 17
-#define __IFLA_VXLAN_MAX 18
+#define IFLA_VXLAN_UDP_CSUM 18
+#define IFLA_VXLAN_UDP_ZERO_CSUM6_TX 19
+#define IFLA_VXLAN_UDP_ZERO_CSUM6_RX 20
+#define IFLA_VXLAN_REMCSUM_TX 21
+#define IFLA_VXLAN_REMCSUM_RX 22
+#define IFLA_VXLAN_GBP 23
+#define IFLA_VXLAN_REMCSUM_NOPARTIAL 24
+#define __IFLA_VXLAN_MAX 25
#define IFLA_VXLAN_MAX (__IFLA_VXLAN_MAX - 1)
#endif
@@ -613,6 +818,21 @@ static inline int setns(int fd, int nstype) {
#define IFLA_BRPORT_MAX (__IFLA_BRPORT_MAX - 1)
#endif
+#if !HAVE_DECL_NDA_IFINDEX
+#define NDA_UNSPEC 0
+#define NDA_DST 1
+#define NDA_LLADDR 2
+#define NDA_CACHEINFO 3
+#define NDA_PROBES 4
+#define NDA_VLAN 5
+#define NDA_PORT 6
+#define NDA_VNI 7
+#define NDA_IFINDEX 8
+#define __NDA_MAX 9
+
+#define NDA_MAX (__NDA_MAX - 1)
+#endif
+
#ifndef IPV6_UNICAST_IF
#define IPV6_UNICAST_IF 76
#endif
@@ -674,6 +894,14 @@ static inline int setns(int fd, int nstype) {
#define LOOPBACK_IFINDEX 1
#endif
+#if !HAVE_DECL_IFA_FLAGS
+#define IFA_FLAGS 8
+#endif
+
+#ifndef IFA_F_NOPREFIXROUTE
+#define IFA_F_NOPREFIXROUTE 0x200
+#endif
+
#ifndef MAX_AUDIT_MESSAGE_LENGTH
#define MAX_AUDIT_MESSAGE_LENGTH 8970
#endif
@@ -763,3 +991,11 @@ static inline int kcmp(pid_t pid1, pid_t pid2, int type, unsigned long idx1, uns
#ifndef KCMP_FILE
#define KCMP_FILE 0
#endif
+
+#ifndef INPUT_PROP_POINTING_STICK
+#define INPUT_PROP_POINTING_STICK 0x05
+#endif
+
+#ifndef INPUT_PROP_ACCELEROMETER
+#define INPUT_PROP_ACCELEROMETER 0x06
+#endif
diff --git a/src/shared/mkdir-label.c b/src/shared/mkdir-label.c
index ee11ac06ff..76bbc1edda 100644
--- a/src/shared/mkdir-label.c
+++ b/src/shared/mkdir-label.c
@@ -20,16 +20,10 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <assert.h>
-#include <string.h>
#include <unistd.h>
-#include <errno.h>
-#include <stdlib.h>
#include <stdio.h>
#include "label.h"
-#include "util.h"
-#include "path-util.h"
#include "mkdir.h"
int mkdir_safe_label(const char *path, mode_t mode, uid_t uid, gid_t gid) {
diff --git a/src/shared/mkdir.c b/src/shared/mkdir.c
index beefd1052a..7ee4546988 100644
--- a/src/shared/mkdir.c
+++ b/src/shared/mkdir.c
@@ -19,14 +19,9 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <assert.h>
#include <string.h>
-#include <unistd.h>
#include <errno.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include "label.h"
#include "util.h"
#include "path-util.h"
#include "mkdir.h"
@@ -46,10 +41,8 @@ int mkdir_safe_internal(const char *path, mode_t mode, uid_t uid, gid_t gid, mkd
(st.st_mode & 0700) > (mode & 0700) ||
(uid != UID_INVALID && st.st_uid != uid) ||
(gid != GID_INVALID && st.st_gid != gid) ||
- !S_ISDIR(st.st_mode)) {
- errno = EEXIST;
- return -errno;
- }
+ !S_ISDIR(st.st_mode))
+ return -EEXIST;
return 0;
}
diff --git a/src/shared/mkdir.h b/src/shared/mkdir.h
index e317df300e..2392d1fd1b 100644
--- a/src/shared/mkdir.h
+++ b/src/shared/mkdir.h
@@ -22,7 +22,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdbool.h>
#include <sys/types.h>
int mkdir_safe(const char *path, mode_t mode, uid_t uid, gid_t gid);
diff --git a/src/shared/ordered-set.h b/src/shared/ordered-set.h
new file mode 100644
index 0000000000..766a1f2e83
--- /dev/null
+++ b/src/shared/ordered-set.h
@@ -0,0 +1,59 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2015 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 OrderedSet OrderedSet;
+
+static inline OrderedSet* ordered_set_new(const struct hash_ops *ops) {
+ return (OrderedSet*) ordered_hashmap_new(ops);
+}
+
+static inline OrderedSet* ordered_set_free(OrderedSet *s) {
+ ordered_hashmap_free((OrderedHashmap*) s);
+ return NULL;
+}
+
+static inline OrderedSet* ordered_set_free_free(OrderedSet *s) {
+ ordered_hashmap_free_free((OrderedHashmap*) s);
+ return NULL;
+}
+
+static inline int ordered_set_put(OrderedSet *s, void *p) {
+ return ordered_hashmap_put((OrderedHashmap*) s, p, p);
+}
+
+static inline bool ordered_set_isempty(OrderedSet *s) {
+ return ordered_hashmap_isempty((OrderedHashmap*) s);
+}
+
+static inline void *ordered_set_iterate(OrderedSet *s, Iterator *i) {
+ return ordered_hashmap_iterate((OrderedHashmap*) s, i, NULL);
+}
+
+#define ORDERED_SET_FOREACH(e, s, i) \
+ for ((i) = ITERATOR_FIRST, (e) = ordered_set_iterate((s), &(i)); (e); (e) = ordered_set_iterate((s), &(i)))
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedSet*, ordered_set_free);
+
+#define _cleanup_ordered_set_free_ _cleanup_(ordered_set_freep)
diff --git a/src/shared/pager.c b/src/shared/pager.c
index 8635d9a600..58b62fdccf 100644
--- a/src/shared/pager.c
+++ b/src/shared/pager.c
@@ -19,7 +19,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <sys/types.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
@@ -28,7 +27,9 @@
#include "pager.h"
#include "util.h"
+#include "process-util.h"
#include "macro.h"
+#include "terminal-util.h"
static pid_t pager_pid = 0;
diff --git a/src/shared/path-lookup.c b/src/shared/path-lookup.c
index 291a2f4054..f6a127174c 100644
--- a/src/shared/path-lookup.c
+++ b/src/shared/path-lookup.c
@@ -19,18 +19,16 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
-#include <unistd.h>
#include <errno.h>
#include "util.h"
-#include "mkdir.h"
#include "strv.h"
#include "path-util.h"
#include "path-lookup.h"
+#include "install.h"
int user_config_home(char **config_home) {
const char *e;
@@ -220,8 +218,8 @@ static char** user_dirs(
return tmp;
}
-char **generator_paths(SystemdRunningAs running_as) {
- if (running_as == SYSTEMD_USER)
+char **generator_paths(ManagerRunningAs running_as) {
+ if (running_as == MANAGER_USER)
return strv_new("/run/systemd/user-generators",
"/etc/systemd/user-generators",
"/usr/local/lib/systemd/user-generators",
@@ -237,7 +235,7 @@ char **generator_paths(SystemdRunningAs running_as) {
int lookup_paths_init(
LookupPaths *p,
- SystemdRunningAs running_as,
+ ManagerRunningAs running_as,
bool personal,
const char *root_dir,
const char *generator,
@@ -279,7 +277,7 @@ int lookup_paths_init(
* we include /lib in the search path for the system
* stuff but avoid it for user stuff. */
- if (running_as == SYSTEMD_USER) {
+ if (running_as == MANAGER_USER) {
if (personal)
unit_path = user_dirs(generator, generator_early, generator_late);
else
@@ -339,7 +337,7 @@ int lookup_paths_init(
p->unit_path = NULL;
}
- if (running_as == SYSTEMD_SYSTEM) {
+ if (running_as == MANAGER_SYSTEM) {
#ifdef HAVE_SYSV_COMPAT
/* /etc/init.d/ compatibility does not matter to users */
@@ -439,7 +437,7 @@ int lookup_paths_init_from_scope(LookupPaths *paths,
zero(*paths);
return lookup_paths_init(paths,
- scope == UNIT_FILE_SYSTEM ? SYSTEMD_SYSTEM : SYSTEMD_USER,
+ scope == UNIT_FILE_SYSTEM ? MANAGER_SYSTEM : MANAGER_USER,
scope == UNIT_FILE_USER,
root_dir,
NULL, NULL, NULL);
diff --git a/src/shared/path-lookup.h b/src/shared/path-lookup.h
index 2ec888da81..e35c8d3c04 100644
--- a/src/shared/path-lookup.h
+++ b/src/shared/path-lookup.h
@@ -22,7 +22,6 @@
***/
#include "macro.h"
-#include "install.h"
typedef struct LookupPaths {
char **unit_path;
@@ -32,28 +31,31 @@ typedef struct LookupPaths {
#endif
} LookupPaths;
-typedef enum SystemdRunningAs {
- SYSTEMD_SYSTEM,
- SYSTEMD_USER,
- _SYSTEMD_RUNNING_AS_MAX,
- _SYSTEMD_RUNNING_AS_INVALID = -1
-} SystemdRunningAs;
+typedef enum ManagerRunningAs {
+ MANAGER_SYSTEM,
+ MANAGER_USER,
+ _MANAGER_RUNNING_AS_MAX,
+ _MANAGER_RUNNING_AS_INVALID = -1
+} ManagerRunningAs;
int user_config_home(char **config_home);
int user_runtime_dir(char **runtime_dir);
-char **generator_paths(SystemdRunningAs running_as);
+char **generator_paths(ManagerRunningAs running_as);
int lookup_paths_init(LookupPaths *p,
- SystemdRunningAs running_as,
+ ManagerRunningAs running_as,
bool personal,
const char *root_dir,
const char *generator,
const char *generator_early,
const char *generator_late);
-void lookup_paths_free(LookupPaths *p);
+
+#include "install.h"
+
int lookup_paths_init_from_scope(LookupPaths *paths,
UnitFileScope scope,
const char *root_dir);
+void lookup_paths_free(LookupPaths *p);
#define _cleanup_lookup_paths_free_ _cleanup_(lookup_paths_free)
diff --git a/src/shared/path-util.c b/src/shared/path-util.c
index b9db7f1047..be50a1865d 100644
--- a/src/shared/path-util.c
+++ b/src/shared/path-util.c
@@ -19,15 +19,12 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <assert.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
-#include <signal.h>
#include <stdio.h>
#include <fcntl.h>
-#include <dirent.h>
#include <sys/statvfs.h>
#include "macro.h"
@@ -36,6 +33,7 @@
#include "strv.h"
#include "path-util.h"
#include "missing.h"
+#include "fileio.h"
bool path_is_absolute(const char *p) {
return p[0] == '/';
@@ -403,12 +401,18 @@ char* path_startswith(const char *path, const char *prefix) {
}
}
-bool path_equal(const char *a, const char *b) {
+int path_compare(const char *a, const char *b) {
+ int d;
+
assert(a);
assert(b);
- if ((a[0] == '/') != (b[0] == '/'))
- return false;
+ /* A relative path and an abolute path must not compare as equal.
+ * Which one is sorted before the other does not really matter.
+ * Here a relative path is ordered before an absolute path. */
+ d = (a[0] == '/') - (b[0] == '/');
+ if (d)
+ return d;
for (;;) {
size_t j, k;
@@ -417,25 +421,40 @@ bool path_equal(const char *a, const char *b) {
b += strspn(b, "/");
if (*a == 0 && *b == 0)
- return true;
+ return 0;
- if (*a == 0 || *b == 0)
- return false;
+ /* Order prefixes first: "/foo" before "/foo/bar" */
+ if (*a == 0)
+ return -1;
+ if (*b == 0)
+ return 1;
j = strcspn(a, "/");
k = strcspn(b, "/");
- if (j != k)
- return false;
+ /* Alphabetical sort: "/foo/aaa" before "/foo/b" */
+ d = memcmp(a, b, MIN(j, k));
+ if (d)
+ return (d > 0) - (d < 0); /* sign of d */
- if (memcmp(a, b, j) != 0)
- return false;
+ /* Sort "/foo/a" before "/foo/aaa" */
+ d = (j > k) - (j < k); /* sign of (j - k) */
+ if (d)
+ return d;
a += j;
b += k;
}
}
+bool path_equal(const char *a, const char *b) {
+ return path_compare(a, b) == 0;
+}
+
+bool path_equal_or_files_same(const char *a, const char *b) {
+ return path_equal(a, b) || files_same(a, b) > 0;
+}
+
char* path_join(const char *root, const char *path, const char *rest) {
assert(path);
@@ -452,87 +471,192 @@ char* path_join(const char *root, const char *path, const char *rest) {
NULL);
}
-int path_is_mount_point(const char *t, bool allow_symlink) {
+static int fd_fdinfo_mnt_id(int fd, const char *filename, int flags, int *mnt_id) {
+ char path[strlen("/proc/self/fdinfo/") + DECIMAL_STR_MAX(int)];
+ _cleanup_free_ char *fdinfo = NULL;
+ _cleanup_close_ int subfd = -1;
+ char *p;
+ int r;
+
+ if ((flags & AT_EMPTY_PATH) && isempty(filename))
+ xsprintf(path, "/proc/self/fdinfo/%i", fd);
+ else {
+ subfd = openat(fd, filename, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_PATH);
+ if (subfd < 0)
+ return -errno;
+
+ xsprintf(path, "/proc/self/fdinfo/%i", subfd);
+ }
+
+ r = read_full_file(path, &fdinfo, NULL);
+ if (r == -ENOENT) /* The fdinfo directory is a relatively new addition */
+ return -EOPNOTSUPP;
+ if (r < 0)
+ return -errno;
+
+ p = startswith(fdinfo, "mnt_id:");
+ if (!p) {
+ p = strstr(fdinfo, "\nmnt_id:");
+ if (!p) /* The mnt_id field is a relatively new addition */
+ return -EOPNOTSUPP;
+
+ p += 8;
+ }
- union file_handle_union h = FILE_HANDLE_INIT;
+ p += strspn(p, WHITESPACE);
+ p[strcspn(p, WHITESPACE)] = 0;
+
+ return safe_atoi(p, mnt_id);
+}
+
+int fd_is_mount_point(int fd, const char *filename, int flags) {
+ union file_handle_union h = FILE_HANDLE_INIT, h_parent = FILE_HANDLE_INIT;
int mount_id = -1, mount_id_parent = -1;
- _cleanup_free_ char *parent = NULL;
+ bool nosupp = false, check_st_dev = true;
struct stat a, b;
int r;
- bool nosupp = false;
- /* We are not actually interested in the file handles, but
- * name_to_handle_at() also passes us the mount ID, hence use
- * it but throw the handle away */
+ assert(fd >= 0);
+ assert(filename);
- if (path_equal(t, "/"))
- return 1;
-
- r = name_to_handle_at(AT_FDCWD, t, &h.handle, &mount_id, allow_symlink ? AT_SYMLINK_FOLLOW : 0);
+ /* First we will try the name_to_handle_at() syscall, which
+ * tells us the mount id and an opaque file "handle". It is
+ * not supported everywhere though (kernel compile-time
+ * option, not all file systems are hooked up). If it works
+ * the mount id is usually good enough to tell us whether
+ * something is a mount point.
+ *
+ * If that didn't work we will try to read the mount id from
+ * /proc/self/fdinfo/<fd>. This is almost as good as
+ * name_to_handle_at(), however, does not return the the
+ * opaque file handle. The opaque file handle is pretty useful
+ * to detect the root directory, which we should always
+ * consider a mount point. Hence we use this only as
+ * fallback. Exporting the mnt_id in fdinfo is a pretty recent
+ * kernel addition.
+ *
+ * As last fallback we do traditional fstat() based st_dev
+ * comparisons. This is how things were traditionally done,
+ * but unionfs breaks breaks this since it exposes file
+ * systems with a variety of st_dev reported. Also, btrfs
+ * subvolumes have different st_dev, even though they aren't
+ * real mounts of their own. */
+
+ r = name_to_handle_at(fd, filename, &h.handle, &mount_id, flags);
if (r < 0) {
if (errno == ENOSYS)
/* This kernel does not support name_to_handle_at()
- * fall back to the traditional stat() logic. */
- goto fallback;
+ * fall back to simpler logic. */
+ goto fallback_fdinfo;
else if (errno == EOPNOTSUPP)
/* This kernel or file system does not support
- * name_to_handle_at(), hence fallback to the
+ * name_to_handle_at(), hence let's see if the
+ * upper fs supports it (in which case it is a
+ * mount point), otherwise fallback to the
* traditional stat() logic */
nosupp = true;
- else if (errno == ENOENT)
- return 0;
else
return -errno;
}
- r = path_get_parent(t, &parent);
- if (r < 0)
- return r;
-
- h.handle.handle_bytes = MAX_HANDLE_SZ;
- r = name_to_handle_at(AT_FDCWD, parent, &h.handle, &mount_id_parent, AT_SYMLINK_FOLLOW);
- if (r < 0)
- if (errno == EOPNOTSUPP)
+ r = name_to_handle_at(fd, "", &h_parent.handle, &mount_id_parent, AT_EMPTY_PATH);
+ if (r < 0) {
+ if (errno == EOPNOTSUPP) {
if (nosupp)
/* Neither parent nor child do name_to_handle_at()?
We have no choice but to fall back. */
- goto fallback;
+ goto fallback_fdinfo;
else
- /* The parent can't do name_to_handle_at() but
- * the directory we are interested in can?
- * Or the other way around?
+ /* The parent can't do name_to_handle_at() but the
+ * directory we are interested in can?
* If so, it must be a mount point. */
return 1;
- else
+ } else
return -errno;
- else
- return mount_id != mount_id_parent;
+ }
-fallback:
- if (allow_symlink)
- r = stat(t, &a);
- else
- r = lstat(t, &a);
+ /* The parent can do name_to_handle_at() but the
+ * directory we are interested in can't? If so, it
+ * must be a mount point. */
+ if (nosupp)
+ return 1;
- if (r < 0) {
- if (errno == ENOENT)
- return 0;
+ /* If the file handle for the directory we are
+ * interested in and its parent are identical, we
+ * assume this is the root directory, which is a mount
+ * point. */
+
+ if (h.handle.handle_bytes == h_parent.handle.handle_bytes &&
+ h.handle.handle_type == h_parent.handle.handle_type &&
+ memcmp(h.handle.f_handle, h_parent.handle.f_handle, h.handle.handle_bytes) == 0)
+ return 1;
+
+ return mount_id != mount_id_parent;
+
+fallback_fdinfo:
+ r = fd_fdinfo_mnt_id(fd, filename, flags, &mount_id);
+ if (r == -EOPNOTSUPP)
+ goto fallback_fstat;
+ if (r < 0)
+ return r;
+
+ r = fd_fdinfo_mnt_id(fd, "", AT_EMPTY_PATH, &mount_id_parent);
+ if (r < 0)
+ return r;
+
+ if (mount_id != mount_id_parent)
+ return 1;
+ /* Hmm, so, the mount ids are the same. This leaves one
+ * special case though for the root file system. For that,
+ * let's see if the parent directory has the same inode as we
+ * are interested in. Hence, let's also do fstat() checks now,
+ * too, but avoid the st_dev comparisons, since they aren't
+ * that useful on unionfs mounts. */
+ check_st_dev = false;
+
+fallback_fstat:
+ /* yay for fstatat() taking a different set of flags than the other
+ * _at() above */
+ if (flags & AT_SYMLINK_FOLLOW)
+ flags &= ~AT_SYMLINK_FOLLOW;
+ else
+ flags |= AT_SYMLINK_NOFOLLOW;
+ if (fstatat(fd, filename, &a, flags) < 0)
return -errno;
- }
- free(parent);
- parent = NULL;
+ if (fstatat(fd, "", &b, AT_EMPTY_PATH) < 0)
+ return -errno;
+
+ /* A directory with same device and inode as its parent? Must
+ * be the root directory */
+ if (a.st_dev == b.st_dev &&
+ a.st_ino == b.st_ino)
+ return 1;
+
+ return check_st_dev && (a.st_dev != b.st_dev);
+}
+
+/* flags can be AT_SYMLINK_FOLLOW or 0 */
+int path_is_mount_point(const char *t, int flags) {
+ _cleanup_close_ int fd = -1;
+ _cleanup_free_ char *parent = NULL;
+ int r;
+
+ assert(t);
+
+ if (path_equal(t, "/"))
+ return 1;
r = path_get_parent(t, &parent);
if (r < 0)
return r;
- r = stat(parent, &b);
- if (r < 0)
+ fd = openat(AT_FDCWD, parent, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_PATH);
+ if (fd < 0)
return -errno;
- return a.st_dev != b.st_dev;
+ return fd_is_mount_point(fd, basename(t), flags);
}
int path_is_read_only_fs(const char *path) {
@@ -683,3 +807,37 @@ int fsck_exists(const char *fstype) {
return 0;
}
+
+char *prefix_root(const char *root, const char *path) {
+ char *n, *p;
+ size_t l;
+
+ /* If root is passed, prefixes path with it. Otherwise returns
+ * it as is. */
+
+ assert(path);
+
+ /* First, drop duplicate prefixing slashes from the path */
+ while (path[0] == '/' && path[1] == '/')
+ path++;
+
+ if (isempty(root) || path_equal(root, "/"))
+ return strdup(path);
+
+ l = strlen(root) + 1 + strlen(path) + 1;
+
+ n = new(char, l);
+ if (!n)
+ return NULL;
+
+ p = stpcpy(n, root);
+
+ while (p > n && p[-1] == '/')
+ p--;
+
+ if (path[0] != '/')
+ *(p++) = '/';
+
+ strcpy(p, path);
+ return n;
+}
diff --git a/src/shared/path-util.h b/src/shared/path-util.h
index bd0d32473f..1eac89c51b 100644
--- a/src/shared/path-util.h
+++ b/src/shared/path-util.h
@@ -44,14 +44,17 @@ char* path_make_absolute_cwd(const char *p);
int path_make_relative(const char *from_dir, const char *to_path, char **_r);
char* path_kill_slashes(char *path);
char* path_startswith(const char *path, const char *prefix) _pure_;
+int path_compare(const char *a, const char *b) _pure_;
bool path_equal(const char *a, const char *b) _pure_;
+bool path_equal_or_files_same(const char *a, const char *b);
char* path_join(const char *root, const char *path, const char *rest);
char** path_strv_make_absolute_cwd(char **l);
char** path_strv_resolve(char **l, const char *prefix);
char** path_strv_resolve_uniq(char **l, const char *prefix);
-int path_is_mount_point(const char *path, bool allow_symlink);
+int fd_is_mount_point(int fd, const char *filename, int flags);
+int path_is_mount_point(const char *path, int flags);
int path_is_read_only_fs(const char *path);
int path_is_os_tree(const char *path);
@@ -70,3 +73,30 @@ int fsck_exists(const char *fstype);
/* Same as PATH_FOREACH_PREFIX but also includes the specified path itself */
#define PATH_FOREACH_PREFIX_MORE(prefix, path) \
for (char *_slash = ({ path_kill_slashes(strcpy(prefix, path)); if (streq(prefix, "/")) prefix[0] = 0; strrchr(prefix, 0); }); _slash && ((*_slash = 0), true); _slash = strrchr((prefix), '/'))
+
+char *prefix_root(const char *root, const char *path);
+
+/* Similar to prefix_root(), but returns an alloca() buffer, or
+ * possibly a const pointer into the path parameter */
+#define prefix_roota(root, path) \
+ ({ \
+ const char* _path = (path), *_root = (root), *_ret; \
+ char *_p, *_n; \
+ size_t _l; \
+ while (_path[0] == '/' && _path[1] == '/') \
+ _path ++; \
+ if (isempty(_root) || path_equal(_root, "/")) \
+ _ret = _path; \
+ else { \
+ _l = strlen(_root) + 1 + strlen(_path) + 1; \
+ _n = alloca(_l); \
+ _p = stpcpy(_n, _root); \
+ while (_p > _n && _p[-1] == '/') \
+ _p--; \
+ if (_path[0] != '/') \
+ *(_p++) = '/'; \
+ strcpy(_p, _path); \
+ _ret = _n; \
+ } \
+ _ret; \
+ })
diff --git a/src/shared/prioq.c b/src/shared/prioq.c
index 8af4c51c2f..b89888be0e 100644
--- a/src/shared/prioq.c
+++ b/src/shared/prioq.c
@@ -45,12 +45,14 @@ Prioq *prioq_new(compare_func_t compare_func) {
return q;
}
-void prioq_free(Prioq *q) {
+Prioq* prioq_free(Prioq *q) {
if (!q)
- return;
+ return NULL;
free(q->items);
free(q);
+
+ return NULL;
}
int prioq_ensure_allocated(Prioq **q, compare_func_t compare_func) {
diff --git a/src/shared/prioq.h b/src/shared/prioq.h
index d836b36cd9..1c044b135c 100644
--- a/src/shared/prioq.h
+++ b/src/shared/prioq.h
@@ -28,7 +28,7 @@ typedef struct Prioq Prioq;
#define PRIOQ_IDX_NULL ((unsigned) -1)
Prioq *prioq_new(compare_func_t compare);
-void prioq_free(Prioq *q);
+Prioq *prioq_free(Prioq *q);
int prioq_ensure_allocated(Prioq **q, compare_func_t compare_func);
int prioq_put(Prioq *q, void *data, unsigned *idx);
diff --git a/src/shared/process-util.c b/src/shared/process-util.c
new file mode 100644
index 0000000000..cfc876567d
--- /dev/null
+++ b/src/shared/process-util.c
@@ -0,0 +1,539 @@
+/***
+ 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 <stdbool.h>
+#include <sys/types.h>
+#include <string.h>
+#include <stdio.h>
+#include <assert.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/wait.h>
+#include <signal.h>
+#include <ctype.h>
+
+#include "fileio.h"
+#include "util.h"
+#include "log.h"
+#include "signal-util.h"
+#include "process-util.h"
+
+int get_process_state(pid_t pid) {
+ const char *p;
+ char state;
+ int r;
+ _cleanup_free_ char *line = NULL;
+
+ assert(pid >= 0);
+
+ p = procfs_file_alloca(pid, "stat");
+ r = read_one_line_file(p, &line);
+ if (r < 0)
+ return r;
+
+ p = strrchr(line, ')');
+ if (!p)
+ return -EIO;
+
+ p++;
+
+ if (sscanf(p, " %c", &state) != 1)
+ return -EIO;
+
+ return (unsigned char) state;
+}
+
+int get_process_comm(pid_t pid, char **name) {
+ const char *p;
+ int r;
+
+ assert(name);
+ assert(pid >= 0);
+
+ p = procfs_file_alloca(pid, "comm");
+
+ r = read_one_line_file(p, name);
+ if (r == -ENOENT)
+ return -ESRCH;
+
+ return r;
+}
+
+int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char **line) {
+ _cleanup_fclose_ FILE *f = NULL;
+ char *r = NULL, *k;
+ const char *p;
+ int c;
+
+ assert(line);
+ assert(pid >= 0);
+
+ p = procfs_file_alloca(pid, "cmdline");
+
+ f = fopen(p, "re");
+ if (!f)
+ return -errno;
+
+ if (max_length == 0) {
+ size_t len = 0, allocated = 0;
+
+ while ((c = getc(f)) != EOF) {
+
+ if (!GREEDY_REALLOC(r, allocated, len+2)) {
+ free(r);
+ return -ENOMEM;
+ }
+
+ r[len++] = isprint(c) ? c : ' ';
+ }
+
+ if (len > 0)
+ r[len-1] = 0;
+
+ } else {
+ bool space = false;
+ size_t left;
+
+ r = new(char, max_length);
+ if (!r)
+ return -ENOMEM;
+
+ k = r;
+ left = max_length;
+ while ((c = getc(f)) != EOF) {
+
+ if (isprint(c)) {
+ if (space) {
+ if (left <= 4)
+ break;
+
+ *(k++) = ' ';
+ left--;
+ space = false;
+ }
+
+ if (left <= 4)
+ break;
+
+ *(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;
+ }
+
+ /* Kernel threads have no argv[] */
+ if (isempty(r)) {
+ _cleanup_free_ char *t = NULL;
+ int h;
+
+ free(r);
+
+ if (!comm_fallback)
+ return -ENOENT;
+
+ h = get_process_comm(pid, &t);
+ if (h < 0)
+ return h;
+
+ r = strjoin("[", t, "]", NULL);
+ if (!r)
+ return -ENOMEM;
+ }
+
+ *line = r;
+ return 0;
+}
+
+int is_kernel_thread(pid_t pid) {
+ const char *p;
+ size_t count;
+ char c;
+ bool eof;
+ FILE *f;
+
+ if (pid == 0)
+ return 0;
+
+ assert(pid > 0);
+
+ p = procfs_file_alloca(pid, "cmdline");
+ f = fopen(p, "re");
+ if (!f)
+ return -errno;
+
+ count = fread(&c, 1, 1, f);
+ eof = feof(f);
+ fclose(f);
+
+ /* Kernel threads have an empty cmdline */
+
+ if (count <= 0)
+ return eof ? 1 : -errno;
+
+ return 0;
+}
+
+int get_process_capeff(pid_t pid, char **capeff) {
+ const char *p;
+
+ assert(capeff);
+ assert(pid >= 0);
+
+ p = procfs_file_alloca(pid, "status");
+
+ return get_status_field(p, "\nCapEff:", capeff);
+}
+
+static int get_process_link_contents(const char *proc_file, char **name) {
+ int r;
+
+ assert(proc_file);
+ assert(name);
+
+ r = readlink_malloc(proc_file, name);
+ if (r < 0)
+ return r == -ENOENT ? -ESRCH : r;
+
+ return 0;
+}
+
+int get_process_exe(pid_t pid, char **name) {
+ const char *p;
+ char *d;
+ int r;
+
+ assert(pid >= 0);
+
+ p = procfs_file_alloca(pid, "exe");
+ r = get_process_link_contents(p, name);
+ if (r < 0)
+ return r;
+
+ d = endswith(*name, " (deleted)");
+ if (d)
+ *d = '\0';
+
+ return 0;
+}
+
+static int get_process_id(pid_t pid, const char *field, uid_t *uid) {
+ _cleanup_fclose_ FILE *f = NULL;
+ char line[LINE_MAX];
+ const char *p;
+
+ assert(field);
+ assert(uid);
+
+ if (pid == 0)
+ return getuid();
+
+ p = procfs_file_alloca(pid, "status");
+ f = fopen(p, "re");
+ if (!f)
+ return -errno;
+
+ FOREACH_LINE(line, f, return -errno) {
+ char *l;
+
+ l = strstrip(line);
+
+ if (startswith(l, field)) {
+ l += strlen(field);
+ l += strspn(l, WHITESPACE);
+
+ l[strcspn(l, WHITESPACE)] = 0;
+
+ return parse_uid(l, uid);
+ }
+ }
+
+ return -EIO;
+}
+
+int get_process_uid(pid_t pid, uid_t *uid) {
+ return get_process_id(pid, "Uid:", 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);
+}
+
+int get_process_cwd(pid_t pid, char **cwd) {
+ const char *p;
+
+ assert(pid >= 0);
+
+ p = procfs_file_alloca(pid, "cwd");
+
+ return get_process_link_contents(p, cwd);
+}
+
+int get_process_root(pid_t pid, char **root) {
+ const char *p;
+
+ assert(pid >= 0);
+
+ p = procfs_file_alloca(pid, "root");
+
+ return get_process_link_contents(p, root);
+}
+
+int get_process_environ(pid_t pid, char **env) {
+ _cleanup_fclose_ FILE *f = NULL;
+ _cleanup_free_ char *outcome = NULL;
+ int c;
+ const char *p;
+ size_t allocated = 0, sz = 0;
+
+ assert(pid >= 0);
+ assert(env);
+
+ p = procfs_file_alloca(pid, "environ");
+
+ f = fopen(p, "re");
+ if (!f)
+ return -errno;
+
+ while ((c = fgetc(f)) != EOF) {
+ if (!GREEDY_REALLOC(outcome, allocated, sz + 5))
+ return -ENOMEM;
+
+ if (c == '\0')
+ outcome[sz++] = '\n';
+ else
+ sz += cescape_char(c, outcome + sz);
+ }
+
+ outcome[sz] = '\0';
+ *env = outcome;
+ outcome = NULL;
+
+ return 0;
+}
+
+int get_parent_of_pid(pid_t pid, pid_t *_ppid) {
+ int r;
+ _cleanup_free_ char *line = NULL;
+ long unsigned ppid;
+ const char *p;
+
+ assert(pid >= 0);
+ assert(_ppid);
+
+ if (pid == 0) {
+ *_ppid = getppid();
+ return 0;
+ }
+
+ p = procfs_file_alloca(pid, "stat");
+ r = read_one_line_file(p, &line);
+ if (r < 0)
+ return r;
+
+ /* Let's skip the pid and comm fields. The latter is enclosed
+ * in () but does not escape any () in its value, so let's
+ * skip over it manually */
+
+ p = strrchr(line, ')');
+ if (!p)
+ return -EIO;
+
+ p++;
+
+ if (sscanf(p, " "
+ "%*c " /* state */
+ "%lu ", /* ppid */
+ &ppid) != 1)
+ return -EIO;
+
+ if ((long unsigned) (pid_t) ppid != ppid)
+ return -ERANGE;
+
+ *_ppid = (pid_t) ppid;
+
+ return 0;
+}
+
+int wait_for_terminate(pid_t pid, siginfo_t *status) {
+ siginfo_t dummy;
+
+ assert(pid >= 1);
+
+ if (!status)
+ status = &dummy;
+
+ for (;;) {
+ zero(*status);
+
+ if (waitid(P_PID, pid, status, WEXITED) < 0) {
+
+ if (errno == EINTR)
+ continue;
+
+ return -errno;
+ }
+
+ return 0;
+ }
+}
+
+/*
+ * Return values:
+ * < 0 : wait_for_terminate() failed to get the state of the
+ * process, the process was terminated by a signal, or
+ * failed for an unknown reason.
+ * >=0 : The process terminated normally, and its exit code is
+ * returned.
+ *
+ * That is, success is indicated by a return value of zero, and an
+ * error is indicated by a non-zero value.
+ *
+ * A warning is emitted if the process terminates abnormally,
+ * and also if it returns non-zero unless check_exit_code is true.
+ */
+int wait_for_terminate_and_warn(const char *name, pid_t pid, bool check_exit_code) {
+ int r;
+ siginfo_t status;
+
+ assert(name);
+ assert(pid > 1);
+
+ r = wait_for_terminate(pid, &status);
+ if (r < 0)
+ return log_warning_errno(r, "Failed to wait for %s: %m", name);
+
+ if (status.si_code == CLD_EXITED) {
+ if (status.si_status != 0)
+ log_full(check_exit_code ? LOG_WARNING : LOG_DEBUG,
+ "%s failed with error code %i.", name, status.si_status);
+ else
+ log_debug("%s succeeded.", name);
+
+ return status.si_status;
+ } else if (status.si_code == CLD_KILLED ||
+ status.si_code == CLD_DUMPED) {
+
+ log_warning("%s terminated by signal %s.", name, signal_to_string(status.si_status));
+ return -EPROTO;
+ }
+
+ log_warning("%s failed due to unknown reason.", name);
+ return -EPROTO;
+}
+
+int kill_and_sigcont(pid_t pid, int sig) {
+ int r;
+
+ r = kill(pid, sig) < 0 ? -errno : 0;
+
+ if (r >= 0)
+ kill(pid, SIGCONT);
+
+ return r;
+}
+
+int getenv_for_pid(pid_t pid, const char *field, char **_value) {
+ _cleanup_fclose_ FILE *f = NULL;
+ char *value = NULL;
+ int r;
+ bool done = false;
+ size_t l;
+ const char *path;
+
+ assert(pid >= 0);
+ assert(field);
+ assert(_value);
+
+ path = procfs_file_alloca(pid, "environ");
+
+ f = fopen(path, "re");
+ if (!f)
+ return -errno;
+
+ l = strlen(field);
+ r = 0;
+
+ do {
+ char line[LINE_MAX];
+ unsigned i;
+
+ for (i = 0; i < sizeof(line)-1; i++) {
+ int c;
+
+ c = getc(f);
+ if (_unlikely_(c == EOF)) {
+ done = true;
+ break;
+ } else if (c == 0)
+ break;
+
+ line[i] = c;
+ }
+ line[i] = 0;
+
+ if (memcmp(line, field, l) == 0 && line[l] == '=') {
+ value = strdup(line + l + 1);
+ if (!value)
+ return -ENOMEM;
+
+ r = 1;
+ break;
+ }
+
+ } while (!done);
+
+ *_value = value;
+ return r;
+}
+
+bool pid_is_unwaited(pid_t pid) {
+ /* Checks whether a PID is still valid at all, including a zombie */
+
+ if (pid <= 0)
+ return false;
+
+ if (kill(pid, 0) >= 0)
+ return true;
+
+ return errno != ESRCH;
+}
+
+bool pid_is_alive(pid_t pid) {
+ int r;
+
+ /* Checks whether a PID is still valid and not a zombie */
+
+ if (pid <= 0)
+ return false;
+
+ r = get_process_state(pid);
+ if (r == -ENOENT || r == 'Z')
+ return false;
+
+ return true;
+}
diff --git a/src/shared/process-util.h b/src/shared/process-util.h
new file mode 100644
index 0000000000..07431d043b
--- /dev/null
+++ b/src/shared/process-util.h
@@ -0,0 +1,65 @@
+#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 <stdbool.h>
+#include <sys/types.h>
+#include <alloca.h>
+#include <stdio.h>
+#include <string.h>
+#include <signal.h>
+
+#include "formats-util.h"
+
+#define procfs_file_alloca(pid, field) \
+ ({ \
+ pid_t _pid_ = (pid); \
+ const char *_r_; \
+ if (_pid_ == 0) { \
+ _r_ = ("/proc/self/" field); \
+ } else { \
+ _r_ = alloca(strlen("/proc/") + DECIMAL_STR_MAX(pid_t) + 1 + sizeof(field)); \
+ sprintf((char*) _r_, "/proc/"PID_FMT"/" field, _pid_); \
+ } \
+ _r_; \
+ })
+
+int get_process_state(pid_t pid);
+int get_process_comm(pid_t pid, char **name);
+int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char **line);
+int get_process_exe(pid_t pid, char **name);
+int get_process_uid(pid_t pid, uid_t *uid);
+int get_process_gid(pid_t pid, gid_t *gid);
+int get_process_capeff(pid_t pid, char **capeff);
+int get_process_cwd(pid_t pid, char **cwd);
+int get_process_root(pid_t pid, char **root);
+int get_process_environ(pid_t pid, char **environ);
+
+int wait_for_terminate(pid_t pid, siginfo_t *status);
+int wait_for_terminate_and_warn(const char *name, pid_t pid, bool check_exit_code);
+
+int kill_and_sigcont(pid_t pid, int sig);
+pid_t get_parent_of_pid(pid_t pid, pid_t *ppid);
+void rename_process(const char name[8]);
+int is_kernel_thread(pid_t pid);
+int getenv_for_pid(pid_t pid, const char *field, char **_value);
+
+bool pid_is_alive(pid_t pid);
+bool pid_is_unwaited(pid_t pid);
diff --git a/src/shared/pty.c b/src/shared/pty.c
index fbe6295ea5..119d66e9a2 100644
--- a/src/shared/pty.c
+++ b/src/shared/pty.c
@@ -44,18 +44,12 @@
#include <errno.h>
#include <fcntl.h>
-#include <limits.h>
-#include <linux/ioctl.h>
#include <signal.h>
#include <stdbool.h>
#include <stdint.h>
-#include <stdio.h>
#include <stdlib.h>
-#include <string.h>
#include <sys/epoll.h>
-#include <sys/eventfd.h>
#include <sys/ioctl.h>
-#include <sys/types.h>
#include <sys/uio.h>
#include <sys/wait.h>
#include <termios.h>
@@ -63,9 +57,10 @@
#include "barrier.h"
#include "macro.h"
-#include "pty.h"
#include "ring.h"
#include "util.h"
+#include "signal-util.h"
+#include "pty.h"
#define PTY_BUFSIZE 4096
diff --git a/src/shared/pty.h b/src/shared/pty.h
index a87ceb58ca..63c7db2833 100644
--- a/src/shared/pty.h
+++ b/src/shared/pty.h
@@ -21,17 +21,12 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <errno.h>
#include <stdbool.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
#include <unistd.h>
#include "barrier.h"
#include "macro.h"
#include "sd-event.h"
-#include "util.h"
typedef struct Pty Pty;
diff --git a/src/shared/ptyfwd.c b/src/shared/ptyfwd.c
index 31274a1418..789f217efc 100644
--- a/src/shared/ptyfwd.c
+++ b/src/shared/ptyfwd.c
@@ -20,7 +20,6 @@
***/
#include <sys/epoll.h>
-#include <sys/signalfd.h>
#include <sys/ioctl.h>
#include <limits.h>
#include <termios.h>
@@ -42,6 +41,8 @@ struct PTYForward {
struct termios saved_stdin_attr;
struct termios saved_stdout_attr;
+ bool read_only:1;
+
bool saved_stdin:1;
bool saved_stdout:1;
@@ -298,7 +299,13 @@ static int on_sigwinch_event(sd_event_source *e, const struct signalfd_siginfo *
return 0;
}
-int pty_forward_new(sd_event *event, int master, bool ignore_vhangup, PTYForward **ret) {
+int pty_forward_new(
+ sd_event *event,
+ int master,
+ bool ignore_vhangup,
+ bool read_only,
+ PTYForward **ret) {
+
_cleanup_(pty_forward_freep) PTYForward *f = NULL;
struct winsize ws;
int r;
@@ -307,6 +314,7 @@ int pty_forward_new(sd_event *event, int master, bool ignore_vhangup, PTYForward
if (!f)
return -ENOMEM;
+ f->read_only = read_only;
f->ignore_vhangup = ignore_vhangup;
if (event)
@@ -317,13 +325,15 @@ int pty_forward_new(sd_event *event, int master, bool ignore_vhangup, PTYForward
return r;
}
- r = fd_nonblock(STDIN_FILENO, true);
- if (r < 0)
- return r;
+ if (!read_only) {
+ r = fd_nonblock(STDIN_FILENO, true);
+ if (r < 0)
+ return r;
- r = fd_nonblock(STDOUT_FILENO, true);
- if (r < 0)
- return r;
+ r = fd_nonblock(STDOUT_FILENO, true);
+ if (r < 0)
+ return r;
+ }
r = fd_nonblock(master, true);
if (r < 0)
@@ -332,38 +342,36 @@ int pty_forward_new(sd_event *event, int master, bool ignore_vhangup, PTYForward
f->master = master;
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) >= 0)
- (void)ioctl(master, TIOCSWINSZ, &ws);
-
- if (tcgetattr(STDIN_FILENO, &f->saved_stdin_attr) >= 0) {
- struct termios raw_stdin_attr;
+ (void) ioctl(master, TIOCSWINSZ, &ws);
- f->saved_stdin = true;
+ if (!read_only) {
+ if (tcgetattr(STDIN_FILENO, &f->saved_stdin_attr) >= 0) {
+ struct termios raw_stdin_attr;
- raw_stdin_attr = f->saved_stdin_attr;
- cfmakeraw(&raw_stdin_attr);
- raw_stdin_attr.c_oflag = f->saved_stdin_attr.c_oflag;
- tcsetattr(STDIN_FILENO, TCSANOW, &raw_stdin_attr);
- }
+ f->saved_stdin = true;
- if (tcgetattr(STDOUT_FILENO, &f->saved_stdout_attr) >= 0) {
- struct termios raw_stdout_attr;
+ raw_stdin_attr = f->saved_stdin_attr;
+ cfmakeraw(&raw_stdin_attr);
+ raw_stdin_attr.c_oflag = f->saved_stdin_attr.c_oflag;
+ tcsetattr(STDIN_FILENO, TCSANOW, &raw_stdin_attr);
+ }
- f->saved_stdout = true;
+ if (tcgetattr(STDOUT_FILENO, &f->saved_stdout_attr) >= 0) {
+ struct termios raw_stdout_attr;
- raw_stdout_attr = f->saved_stdout_attr;
- cfmakeraw(&raw_stdout_attr);
- raw_stdout_attr.c_iflag = f->saved_stdout_attr.c_iflag;
- raw_stdout_attr.c_lflag = f->saved_stdout_attr.c_lflag;
- tcsetattr(STDOUT_FILENO, TCSANOW, &raw_stdout_attr);
- }
+ f->saved_stdout = true;
- r = sd_event_add_io(f->event, &f->master_event_source, master, EPOLLIN|EPOLLOUT|EPOLLET, on_master_event, f);
- if (r < 0)
- return r;
+ raw_stdout_attr = f->saved_stdout_attr;
+ cfmakeraw(&raw_stdout_attr);
+ raw_stdout_attr.c_iflag = f->saved_stdout_attr.c_iflag;
+ raw_stdout_attr.c_lflag = f->saved_stdout_attr.c_lflag;
+ tcsetattr(STDOUT_FILENO, TCSANOW, &raw_stdout_attr);
+ }
- r = sd_event_add_io(f->event, &f->stdin_event_source, STDIN_FILENO, EPOLLIN|EPOLLET, on_stdin_event, f);
- if (r < 0 && r != -EPERM)
- return r;
+ r = sd_event_add_io(f->event, &f->stdin_event_source, STDIN_FILENO, EPOLLIN|EPOLLET, on_stdin_event, f);
+ if (r < 0 && r != -EPERM)
+ return r;
+ }
r = sd_event_add_io(f->event, &f->stdout_event_source, STDOUT_FILENO, EPOLLOUT|EPOLLET, on_stdout_event, f);
if (r == -EPERM)
@@ -372,6 +380,10 @@ int pty_forward_new(sd_event *event, int master, bool ignore_vhangup, PTYForward
else if (r < 0)
return r;
+ r = sd_event_add_io(f->event, &f->master_event_source, master, EPOLLIN|EPOLLOUT|EPOLLET, on_master_event, f);
+ if (r < 0)
+ return r;
+
r = sd_event_add_signal(f->event, &f->sigwinch_event_source, SIGWINCH, on_sigwinch_event, f);
if (r < 0)
return r;
diff --git a/src/shared/ptyfwd.h b/src/shared/ptyfwd.h
index d3e229bd70..6f84e4036a 100644
--- a/src/shared/ptyfwd.h
+++ b/src/shared/ptyfwd.h
@@ -21,16 +21,13 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <sys/types.h>
-#include <signal.h>
#include <stdbool.h>
-#include "util.h"
#include "sd-event.h"
typedef struct PTYForward PTYForward;
-int pty_forward_new(sd_event *event, int master, bool ignore_vhangup, PTYForward **f);
+int pty_forward_new(sd_event *event, int master, bool ignore_vhangup, bool read_only, PTYForward **f);
PTYForward *pty_forward_free(PTYForward *f);
int pty_forward_get_last_char(PTYForward *f, char *ch);
diff --git a/src/shared/random-util.c b/src/shared/random-util.c
new file mode 100644
index 0000000000..b230044f50
--- /dev/null
+++ b/src/shared/random-util.c
@@ -0,0 +1,129 @@
+/***
+ 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 <stdint.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <time.h>
+#ifdef HAVE_SYS_AUXV_H
+#include <sys/auxv.h>
+#endif
+#include <linux/random.h>
+
+#include "random-util.h"
+#include "time-util.h"
+#include "missing.h"
+#include "util.h"
+
+int dev_urandom(void *p, size_t n) {
+ static int have_syscall = -1;
+
+ _cleanup_close_ int fd = -1;
+ int r;
+
+ /* Gathers some randomness from the kernel. This call will
+ * never block, and will always return some data from the
+ * kernel, regardless if the random pool is fully initialized
+ * or not. It thus makes no guarantee for the quality of the
+ * returned entropy, but is good enough for or usual usecases
+ * of seeding the hash functions for hashtable */
+
+ /* Use the getrandom() syscall unless we know we don't have
+ * it, or when the requested size is too large for it. */
+ if (have_syscall != 0 || (size_t) (int) n != n) {
+ r = getrandom(p, n, GRND_NONBLOCK);
+ if (r == (int) n) {
+ have_syscall = true;
+ return 0;
+ }
+
+ if (r < 0) {
+ if (errno == ENOSYS)
+ /* we lack the syscall, continue with
+ * reading from /dev/urandom */
+ have_syscall = false;
+ else if (errno == EAGAIN)
+ /* not enough entropy for now. Let's
+ * remember to use the syscall the
+ * next time, again, but also read
+ * from /dev/urandom for now, which
+ * doesn't care about the current
+ * amount of entropy. */
+ have_syscall = true;
+ else
+ return -errno;
+ } else
+ /* too short read? */
+ return -ENODATA;
+ }
+
+ fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC|O_NOCTTY);
+ if (fd < 0)
+ return errno == ENOENT ? -ENOSYS : -errno;
+
+ return loop_read_exact(fd, p, n, true);
+}
+
+void initialize_srand(void) {
+ static bool srand_called = false;
+ unsigned x;
+#ifdef HAVE_SYS_AUXV_H
+ void *auxv;
+#endif
+
+ if (srand_called)
+ return;
+
+ x = 0;
+
+#ifdef HAVE_SYS_AUXV_H
+ /* The kernel provides us with a bit of entropy in auxv, so
+ * let's try to make use of that to seed the pseudo-random
+ * generator. It's better than nothing... */
+
+ auxv = (void*) getauxval(AT_RANDOM);
+ if (auxv)
+ x ^= *(unsigned*) auxv;
+#endif
+
+ x ^= (unsigned) now(CLOCK_REALTIME);
+ x ^= (unsigned) gettid();
+
+ srand(x);
+ srand_called = true;
+}
+
+void random_bytes(void *p, size_t n) {
+ uint8_t *q;
+ int r;
+
+ r = dev_urandom(p, n);
+ if (r >= 0)
+ return;
+
+ /* If some idiot made /dev/urandom unavailable to us, he'll
+ * get a PRNG instead. */
+
+ initialize_srand();
+
+ for (q = p; q < (uint8_t*) p + n; q ++)
+ *q = rand();
+}
diff --git a/src/shared/random-util.h b/src/shared/random-util.h
new file mode 100644
index 0000000000..f7862c8c8b
--- /dev/null
+++ b/src/shared/random-util.h
@@ -0,0 +1,38 @@
+#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 <stdint.h>
+
+int dev_urandom(void *p, size_t n);
+void random_bytes(void *p, size_t n);
+void initialize_srand(void);
+
+static inline uint64_t random_u64(void) {
+ uint64_t u;
+ random_bytes(&u, sizeof(u));
+ return u;
+}
+
+static inline uint32_t random_u32(void) {
+ uint32_t u;
+ random_bytes(&u, sizeof(u));
+ return u;
+}
diff --git a/src/shared/ratelimit.c b/src/shared/ratelimit.c
index 01b62b7b38..81fc9c19ff 100644
--- a/src/shared/ratelimit.c
+++ b/src/shared/ratelimit.c
@@ -19,10 +19,8 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <assert.h>
#include "ratelimit.h"
-#include "log.h"
/* Modelled after Linux' lib/ratelimit.c by Dave Young
* <hidave.darkstar@gmail.com>, which is licensed GPLv2. */
diff --git a/src/shared/ring.c b/src/shared/ring.c
index 309075e348..6814918464 100644
--- a/src/shared/ring.c
+++ b/src/shared/ring.c
@@ -20,7 +20,6 @@
***/
#include <errno.h>
-#include <inttypes.h>
#include <stdlib.h>
#include <string.h>
#include <sys/uio.h>
diff --git a/src/shared/ring.h b/src/shared/ring.h
index 1210aabdf6..a7c44d1b56 100644
--- a/src/shared/ring.h
+++ b/src/shared/ring.h
@@ -21,11 +21,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <errno.h>
-#include <inttypes.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/uio.h>
typedef struct Ring Ring;
diff --git a/src/shared/rm-rf.c b/src/shared/rm-rf.c
new file mode 100644
index 0000000000..bafd483be2
--- /dev/null
+++ b/src/shared/rm-rf.c
@@ -0,0 +1,224 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2015 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 "path-util.h"
+#include "btrfs-util.h"
+#include "rm-rf.h"
+
+int rm_rf_children(int fd, RemoveFlags flags, struct stat *root_dev) {
+ _cleanup_closedir_ DIR *d = NULL;
+ int ret = 0, r;
+
+ assert(fd >= 0);
+
+ /* This returns the first error we run into, but nevertheless
+ * tries to go on. This closes the passed fd. */
+
+ if (!(flags & REMOVE_PHYSICAL)) {
+
+ r = fd_is_temporary_fs(fd);
+ if (r < 0) {
+ safe_close(fd);
+ return r;
+ }
+
+ if (!r) {
+ /* We refuse to clean physical file systems
+ * with this call, unless explicitly
+ * requested. This is extra paranoia just to
+ * be sure we never ever remove non-state
+ * data */
+
+ log_error("Attempted to remove disk file system, and we can't allow that.");
+ safe_close(fd);
+ return -EPERM;
+ }
+ }
+
+ d = fdopendir(fd);
+ if (!d) {
+ safe_close(fd);
+ return errno == ENOENT ? 0 : -errno;
+ }
+
+ for (;;) {
+ struct dirent *de;
+ bool is_dir;
+ struct stat st;
+
+ errno = 0;
+ de = readdir(d);
+ if (!de) {
+ if (errno != 0 && ret == 0)
+ ret = -errno;
+ return ret;
+ }
+
+ if (streq(de->d_name, ".") || streq(de->d_name, ".."))
+ continue;
+
+ if (de->d_type == DT_UNKNOWN ||
+ (de->d_type == DT_DIR && (root_dev || (flags & REMOVE_SUBVOLUME)))) {
+ if (fstatat(fd, de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) {
+ if (ret == 0 && errno != ENOENT)
+ ret = -errno;
+ continue;
+ }
+
+ is_dir = S_ISDIR(st.st_mode);
+ } else
+ is_dir = de->d_type == DT_DIR;
+
+ if (is_dir) {
+ int subdir_fd;
+
+ /* if root_dev is set, remove subdirectories only if device is same */
+ if (root_dev && st.st_dev != root_dev->st_dev)
+ continue;
+
+ subdir_fd = openat(fd, de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME);
+ if (subdir_fd < 0) {
+ if (ret == 0 && errno != ENOENT)
+ ret = -errno;
+ continue;
+ }
+
+ /* Stop at mount points */
+ r = fd_is_mount_point(fd, de->d_name, 0);
+ if (r < 0) {
+ if (ret == 0 && r != -ENOENT)
+ ret = r;
+
+ safe_close(subdir_fd);
+ continue;
+ }
+ if (r) {
+ safe_close(subdir_fd);
+ continue;
+ }
+
+ if ((flags & REMOVE_SUBVOLUME) && st.st_ino == 256) {
+
+ /* This could be a subvolume, try to remove it */
+
+ r = btrfs_subvol_remove_fd(fd, de->d_name, true);
+ if (r < 0) {
+ if (r != -ENOTTY && r != -EINVAL) {
+ if (ret == 0)
+ ret = r;
+
+ safe_close(subdir_fd);
+ continue;
+ }
+
+ /* ENOTTY, then it wasn't a
+ * btrfs subvolume, continue
+ * below. */
+ } else {
+ /* It was a subvolume, continue. */
+ safe_close(subdir_fd);
+ continue;
+ }
+ }
+
+ /* We pass REMOVE_PHYSICAL here, to avoid
+ * doing the fstatfs() to check the file
+ * system type again for each directory */
+ r = rm_rf_children(subdir_fd, flags | REMOVE_PHYSICAL, root_dev);
+ if (r < 0 && ret == 0)
+ ret = r;
+
+ if (unlinkat(fd, de->d_name, AT_REMOVEDIR) < 0) {
+ if (ret == 0 && errno != ENOENT)
+ ret = -errno;
+ }
+
+ } else if (!(flags & REMOVE_ONLY_DIRECTORIES)) {
+
+ if (unlinkat(fd, de->d_name, 0) < 0) {
+ if (ret == 0 && errno != ENOENT)
+ ret = -errno;
+ }
+ }
+ }
+}
+
+int rm_rf(const char *path, RemoveFlags flags) {
+ int fd, r;
+ struct statfs s;
+
+ assert(path);
+
+ /* We refuse to clean the root file system with this
+ * call. This is extra paranoia to never cause a really
+ * seriously broken system. */
+ if (path_equal(path, "/")) {
+ log_error("Attempted to remove entire root file system, and we can't allow that.");
+ return -EPERM;
+ }
+
+ if ((flags & (REMOVE_SUBVOLUME|REMOVE_ROOT|REMOVE_PHYSICAL)) == (REMOVE_SUBVOLUME|REMOVE_ROOT|REMOVE_PHYSICAL)) {
+ /* Try to remove as subvolume first */
+ r = btrfs_subvol_remove(path, true);
+ if (r >= 0)
+ return r;
+
+ if (r != -ENOTTY && r != -EINVAL)
+ return r;
+
+ /* Not btrfs or not a subvolume */
+ }
+
+ fd = open(path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME);
+ if (fd < 0) {
+
+ if (errno != ENOTDIR && errno != ELOOP)
+ return -errno;
+
+ if (!(flags & REMOVE_PHYSICAL)) {
+ if (statfs(path, &s) < 0)
+ return -errno;
+
+ if (!is_temporary_fs(&s)) {
+ log_error("Attempted to remove disk file system, and we can't allow that.");
+ return -EPERM;
+ }
+ }
+
+ if ((flags & REMOVE_ROOT) && !(flags & REMOVE_ONLY_DIRECTORIES))
+ if (unlink(path) < 0 && errno != ENOENT)
+ return -errno;
+
+ return 0;
+ }
+
+ r = rm_rf_children(fd, flags, NULL);
+
+ if (flags & REMOVE_ROOT) {
+ if (rmdir(path) < 0) {
+ if (r == 0 && errno != ENOENT)
+ r = -errno;
+ }
+ }
+
+ return r;
+}
diff --git a/src/shared/rm-rf.h b/src/shared/rm-rf.h
new file mode 100644
index 0000000000..96579eb182
--- /dev/null
+++ b/src/shared/rm-rf.h
@@ -0,0 +1,34 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2015 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/stat.h>
+
+typedef enum RemoveFlags {
+ REMOVE_ONLY_DIRECTORIES = 1,
+ REMOVE_ROOT = 2,
+ REMOVE_PHYSICAL = 4, /* if not set, only removes files on tmpfs, never physical file systems */
+ REMOVE_SUBVOLUME = 8,
+} RemoveFlags;
+
+int rm_rf_children(int fd, RemoveFlags flags, struct stat *root_dev);
+int rm_rf(const char *path, RemoveFlags flags);
diff --git a/src/shared/selinux-util.c b/src/shared/selinux-util.c
index a2233e0cfb..7c58985cd2 100644
--- a/src/shared/selinux-util.c
+++ b/src/shared/selinux-util.c
@@ -20,7 +20,6 @@
***/
#include <errno.h>
-#include <unistd.h>
#include <malloc.h>
#include <sys/un.h>
@@ -117,6 +116,7 @@ void mac_selinux_finish(void) {
return;
selabel_close(label_hnd);
+ label_hnd = NULL;
#endif
}
@@ -146,7 +146,7 @@ int mac_selinux_fix(const char *path, bool ignore_enoent, bool ignore_erofs) {
r = lsetfilecon(path, fcon);
/* If the FS doesn't support labels, then exit without warning */
- if (r < 0 && errno == ENOTSUP)
+ if (r < 0 && errno == EOPNOTSUPP)
return 0;
}
}
diff --git a/src/shared/selinux-util.h b/src/shared/selinux-util.h
index a694441000..8467185291 100644
--- a/src/shared/selinux-util.h
+++ b/src/shared/selinux-util.h
@@ -22,7 +22,6 @@
***/
#include <sys/socket.h>
-#include <stdio.h>
#include <stdbool.h>
bool mac_selinux_use(void);
diff --git a/src/shared/set.h b/src/shared/set.h
index 2b49e2f287..4dffecd39d 100644
--- a/src/shared/set.h
+++ b/src/shared/set.h
@@ -57,7 +57,7 @@ static inline bool set_contains(Set *s, const void *key) {
return internal_hashmap_contains(HASHMAP_BASE(s), key);
}
-static inline void *set_remove(Set *s, void *key) {
+static inline void *set_remove(Set *s, const void *key) {
return internal_hashmap_remove(HASHMAP_BASE(s), key);
}
diff --git a/src/shared/signal-util.c b/src/shared/signal-util.c
new file mode 100644
index 0000000000..9a2973b6fd
--- /dev/null
+++ b/src/shared/signal-util.c
@@ -0,0 +1,228 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2015 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 "signal-util.h"
+
+int reset_all_signal_handlers(void) {
+ int sig, r = 0;
+
+ for (sig = 1; sig < _NSIG; sig++) {
+ static const struct sigaction sa = {
+ .sa_handler = SIG_DFL,
+ .sa_flags = SA_RESTART,
+ };
+
+ /* These two cannot be caught... */
+ if (sig == SIGKILL || sig == SIGSTOP)
+ continue;
+
+ /* On Linux the first two RT signals are reserved by
+ * glibc, and sigaction() will return EINVAL for them. */
+ if ((sigaction(sig, &sa, NULL) < 0))
+ if (errno != EINVAL && r == 0)
+ r = -errno;
+ }
+
+ return r;
+}
+
+int reset_signal_mask(void) {
+ sigset_t ss;
+
+ if (sigemptyset(&ss) < 0)
+ return -errno;
+
+ if (sigprocmask(SIG_SETMASK, &ss, NULL) < 0)
+ return -errno;
+
+ return 0;
+}
+
+int sigaction_many(const struct sigaction *sa, ...) {
+ va_list ap;
+ int r = 0, sig;
+
+ va_start(ap, sa);
+ while ((sig = va_arg(ap, int)) > 0)
+ if (sigaction(sig, sa, NULL) < 0)
+ r = -errno;
+ va_end(ap);
+
+ return r;
+}
+
+int ignore_signals(int sig, ...) {
+ static const struct sigaction sa = {
+ .sa_handler = SIG_IGN,
+ .sa_flags = SA_RESTART,
+ };
+ va_list ap;
+ int r = 0;
+
+ if (sigaction(sig, &sa, NULL) < 0)
+ r = -errno;
+
+ va_start(ap, sig);
+ while ((sig = va_arg(ap, int)) > 0)
+ if (sigaction(sig, &sa, NULL) < 0)
+ r = -errno;
+ va_end(ap);
+
+ return r;
+}
+
+int default_signals(int sig, ...) {
+ static const struct sigaction sa = {
+ .sa_handler = SIG_DFL,
+ .sa_flags = SA_RESTART,
+ };
+ va_list ap;
+ int r = 0;
+
+ if (sigaction(sig, &sa, NULL) < 0)
+ r = -errno;
+
+ va_start(ap, sig);
+ while ((sig = va_arg(ap, int)) > 0)
+ if (sigaction(sig, &sa, NULL) < 0)
+ r = -errno;
+ va_end(ap);
+
+ return r;
+}
+
+void sigset_add_many(sigset_t *ss, ...) {
+ va_list ap;
+ int sig;
+
+ assert(ss);
+
+ va_start(ap, ss);
+ while ((sig = va_arg(ap, int)) > 0)
+ assert_se(sigaddset(ss, sig) == 0);
+ va_end(ap);
+}
+
+int sigprocmask_many(int how, ...) {
+ va_list ap;
+ sigset_t ss;
+ int sig;
+
+ assert_se(sigemptyset(&ss) == 0);
+
+ va_start(ap, how);
+ while ((sig = va_arg(ap, int)) > 0)
+ assert_se(sigaddset(&ss, sig) == 0);
+ va_end(ap);
+
+ if (sigprocmask(how, &ss, NULL) < 0)
+ return -errno;
+
+ return 0;
+}
+
+static const char *const __signal_table[] = {
+ [SIGHUP] = "HUP",
+ [SIGINT] = "INT",
+ [SIGQUIT] = "QUIT",
+ [SIGILL] = "ILL",
+ [SIGTRAP] = "TRAP",
+ [SIGABRT] = "ABRT",
+ [SIGBUS] = "BUS",
+ [SIGFPE] = "FPE",
+ [SIGKILL] = "KILL",
+ [SIGUSR1] = "USR1",
+ [SIGSEGV] = "SEGV",
+ [SIGUSR2] = "USR2",
+ [SIGPIPE] = "PIPE",
+ [SIGALRM] = "ALRM",
+ [SIGTERM] = "TERM",
+#ifdef SIGSTKFLT
+ [SIGSTKFLT] = "STKFLT", /* Linux on SPARC doesn't know SIGSTKFLT */
+#endif
+ [SIGCHLD] = "CHLD",
+ [SIGCONT] = "CONT",
+ [SIGSTOP] = "STOP",
+ [SIGTSTP] = "TSTP",
+ [SIGTTIN] = "TTIN",
+ [SIGTTOU] = "TTOU",
+ [SIGURG] = "URG",
+ [SIGXCPU] = "XCPU",
+ [SIGXFSZ] = "XFSZ",
+ [SIGVTALRM] = "VTALRM",
+ [SIGPROF] = "PROF",
+ [SIGWINCH] = "WINCH",
+ [SIGIO] = "IO",
+ [SIGPWR] = "PWR",
+ [SIGSYS] = "SYS"
+};
+
+DEFINE_PRIVATE_STRING_TABLE_LOOKUP(__signal, int);
+
+const char *signal_to_string(int signo) {
+ static thread_local char buf[sizeof("RTMIN+")-1 + DECIMAL_STR_MAX(int) + 1];
+ const char *name;
+
+ name = __signal_to_string(signo);
+ if (name)
+ return name;
+
+ if (signo >= SIGRTMIN && signo <= SIGRTMAX)
+ snprintf(buf, sizeof(buf), "RTMIN+%d", signo - SIGRTMIN);
+ else
+ snprintf(buf, sizeof(buf), "%d", signo);
+
+ return buf;
+}
+
+int signal_from_string(const char *s) {
+ int signo;
+ int offset = 0;
+ unsigned u;
+
+ signo = __signal_from_string(s);
+ if (signo > 0)
+ return signo;
+
+ if (startswith(s, "RTMIN+")) {
+ s += 6;
+ offset = SIGRTMIN;
+ }
+ if (safe_atou(s, &u) >= 0) {
+ signo = (int) u + offset;
+ if (signo > 0 && signo < _NSIG)
+ return signo;
+ }
+ return -EINVAL;
+}
+
+int signal_from_string_try_harder(const char *s) {
+ int signo;
+ assert(s);
+
+ signo = signal_from_string(s);
+ if (signo <= 0)
+ if (startswith(s, "SIG"))
+ return signal_from_string(s+3);
+
+ return signo;
+}
diff --git a/src/shared/signal-util.h b/src/shared/signal-util.h
new file mode 100644
index 0000000000..ddf64cda76
--- /dev/null
+++ b/src/shared/signal-util.h
@@ -0,0 +1,41 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010-2015 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 <signal.h>
+
+#include "macro.h"
+
+int reset_all_signal_handlers(void);
+int reset_signal_mask(void);
+
+int ignore_signals(int sig, ...);
+int default_signals(int sig, ...);
+int sigaction_many(const struct sigaction *sa, ...);
+
+void sigset_add_many(sigset_t *ss, ...);
+int sigprocmask_many(int how, ...);
+
+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);
diff --git a/src/shared/smack-util.c b/src/shared/smack-util.c
index 64e213489e..2e24b1ea99 100644
--- a/src/shared/smack-util.c
+++ b/src/shared/smack-util.c
@@ -24,6 +24,7 @@
#include <sys/xattr.h>
#include "util.h"
+#include "process-util.h"
#include "path-util.h"
#include "fileio.h"
#include "smack-util.h"
@@ -187,7 +188,7 @@ int mac_smack_fix(const char *path, bool ignore_enoent, bool ignore_erofs) {
r = lsetxattr(path, "security.SMACK64", label, strlen(label), 0);
/* If the FS doesn't support labels, then exit without warning */
- if (r < 0 && errno == ENOTSUP)
+ if (r < 0 && errno == EOPNOTSUPP)
return 0;
}
diff --git a/src/shared/socket-label.c b/src/shared/socket-label.c
index 6806c51158..cbe3ff216e 100644
--- a/src/shared/socket-label.c
+++ b/src/shared/socket-label.c
@@ -19,24 +19,16 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <assert.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
-#include <stdlib.h>
-#include <arpa/inet.h>
-#include <stdio.h>
-#include <net/if.h>
-#include <sys/types.h>
#include <sys/stat.h>
#include <stddef.h>
-#include <sys/ioctl.h>
#include "macro.h"
#include "util.h"
#include "mkdir.h"
#include "missing.h"
-#include "label.h"
#include "selinux-util.h"
#include "socket-util.h"
@@ -117,9 +109,6 @@ int socket_address_listen(
/* Enforce the right access mode for the socket */
old_mask = umask(~ socket_mode);
- /* Include the original umask in our mask */
- umask(~socket_mode | old_mask);
-
r = mac_selinux_bind(fd, &a->sockaddr.sa, a->size);
if (r < 0 && errno == EADDRINUSE) {
diff --git a/src/shared/socket-util.c b/src/shared/socket-util.c
index c6f64876be..e8bb10dc9b 100644
--- a/src/shared/socket-util.c
+++ b/src/shared/socket-util.c
@@ -19,27 +19,23 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <assert.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
-#include <stdlib.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <net/if.h>
#include <sys/types.h>
-#include <sys/stat.h>
#include <stddef.h>
-#include <sys/ioctl.h>
#include <netdb.h>
#include "macro.h"
-#include "util.h"
-#include "mkdir.h"
#include "path-util.h"
+#include "util.h"
#include "socket-util.h"
#include "missing.h"
#include "fileio.h"
+#include "formats-util.h"
int socket_address_parse(SocketAddress *a, const char *s) {
char *e, *n;
@@ -55,11 +51,6 @@ int socket_address_parse(SocketAddress *a, const char *s) {
if (*s == '[') {
/* IPv6 in [x:.....:z]:p notation */
- if (!socket_ipv6_is_supported()) {
- log_warning("Binding to IPv6 address not available since kernel does not support IPv6.");
- return -EAFNOSUPPORT;
- }
-
e = strchr(s+1, ']');
if (!e)
return -EINVAL;
@@ -144,11 +135,6 @@ int socket_address_parse(SocketAddress *a, const char *s) {
if (idx == 0)
return -EINVAL;
- if (!socket_ipv6_is_supported()) {
- log_warning("Binding to interface is not available since kernel does not support IPv6.");
- return -EAFNOSUPPORT;
- }
-
a->sockaddr.in6.sin6_family = AF_INET6;
a->sockaddr.in6.sin6_port = htons((uint16_t) u);
a->sockaddr.in6.sin6_scope_id = idx;
@@ -182,6 +168,25 @@ int socket_address_parse(SocketAddress *a, const char *s) {
return 0;
}
+int socket_address_parse_and_warn(SocketAddress *a, const char *s) {
+ SocketAddress b;
+ int r;
+
+ /* Similar to socket_address_parse() but warns for IPv6 sockets when we don't support them. */
+
+ r = socket_address_parse(&b, s);
+ if (r < 0)
+ return r;
+
+ if (!socket_ipv6_is_supported() && b.sockaddr.sa.sa_family == AF_INET6) {
+ log_warning("Binding to IPv6 address not available since kernel does not support IPv6.");
+ return -EAFNOSUPPORT;
+ }
+
+ *a = b;
+ return 0;
+}
+
int socket_address_parse_netlink(SocketAddress *a, const char *s) {
int family;
unsigned group = 0;
@@ -302,7 +307,7 @@ int socket_address_print(const SocketAddress *a, char **ret) {
return 0;
}
- return sockaddr_pretty(&a->sockaddr.sa, a->size, false, ret);
+ return sockaddr_pretty(&a->sockaddr.sa, a->size, false, true, ret);
}
bool socket_address_can_accept(const SocketAddress *a) {
@@ -325,9 +330,6 @@ bool socket_address_equal(const SocketAddress *a, const SocketAddress *b) {
if (a->type != b->type)
return false;
- if (a->size != b->size)
- return false;
-
if (socket_address_family(a) != socket_address_family(b))
return false;
@@ -352,14 +354,20 @@ bool socket_address_equal(const SocketAddress *a, const SocketAddress *b) {
break;
case AF_UNIX:
+ if (a->size <= offsetof(struct sockaddr_un, sun_path) ||
+ b->size <= offsetof(struct sockaddr_un, sun_path))
+ return false;
if ((a->sockaddr.un.sun_path[0] == 0) != (b->sockaddr.un.sun_path[0] == 0))
return false;
if (a->sockaddr.un.sun_path[0]) {
- if (!strneq(a->sockaddr.un.sun_path, b->sockaddr.un.sun_path, sizeof(a->sockaddr.un.sun_path)))
+ if (!path_equal_or_files_same(a->sockaddr.un.sun_path, b->sockaddr.un.sun_path))
return false;
} else {
+ if (a->size != b->size)
+ return false;
+
if (memcmp(a->sockaddr.un.sun_path, b->sockaddr.un.sun_path, a->size) != 0)
return false;
}
@@ -367,7 +375,6 @@ bool socket_address_equal(const SocketAddress *a, const SocketAddress *b) {
break;
case AF_NETLINK:
-
if (a->protocol != b->protocol)
return false;
@@ -437,57 +444,55 @@ bool socket_ipv6_is_supported(void) {
}
bool socket_address_matches_fd(const SocketAddress *a, int fd) {
- union sockaddr_union sa;
- socklen_t salen = sizeof(sa), solen;
- int protocol, type;
+ SocketAddress b;
+ socklen_t solen;
assert(a);
assert(fd >= 0);
- if (getsockname(fd, &sa.sa, &salen) < 0)
+ b.size = sizeof(b.sockaddr);
+ if (getsockname(fd, &b.sockaddr.sa, &b.size) < 0)
return false;
- if (sa.sa.sa_family != a->sockaddr.sa.sa_family)
+ if (b.sockaddr.sa.sa_family != a->sockaddr.sa.sa_family)
return false;
- solen = sizeof(type);
- if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &type, &solen) < 0)
+ solen = sizeof(b.type);
+ if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &b.type, &solen) < 0)
return false;
- if (type != a->type)
+ if (b.type != a->type)
return false;
if (a->protocol != 0) {
- solen = sizeof(protocol);
- if (getsockopt(fd, SOL_SOCKET, SO_PROTOCOL, &protocol, &solen) < 0)
+ solen = sizeof(b.protocol);
+ if (getsockopt(fd, SOL_SOCKET, SO_PROTOCOL, &b.protocol, &solen) < 0)
return false;
- if (protocol != a->protocol)
+ if (b.protocol != a->protocol)
return false;
}
- switch (sa.sa.sa_family) {
-
- case AF_INET:
- return sa.in.sin_port == a->sockaddr.in.sin_port &&
- sa.in.sin_addr.s_addr == a->sockaddr.in.sin_addr.s_addr;
+ return socket_address_equal(a, &b);
+}
- case AF_INET6:
- return sa.in6.sin6_port == a->sockaddr.in6.sin6_port &&
- memcmp(&sa.in6.sin6_addr, &a->sockaddr.in6.sin6_addr, sizeof(struct in6_addr)) == 0;
+int sockaddr_port(const struct sockaddr *_sa) {
+ union sockaddr_union *sa = (union sockaddr_union*) _sa;
- case AF_UNIX:
- return salen == a->size &&
- memcmp(sa.un.sun_path, a->sockaddr.un.sun_path, salen - offsetof(struct sockaddr_un, sun_path)) == 0;
+ assert(sa);
- }
+ if (!IN_SET(sa->sa.sa_family, AF_INET, AF_INET6))
+ return -EAFNOSUPPORT;
- return false;
+ return ntohs(sa->sa.sa_family == AF_INET6 ?
+ sa->in6.sin6_port :
+ sa->in.sin_port);
}
-int sockaddr_pretty(const struct sockaddr *_sa, socklen_t salen, bool translate_ipv6, char **ret) {
+int sockaddr_pretty(const struct sockaddr *_sa, socklen_t salen, bool translate_ipv6, bool include_port, char **ret) {
union sockaddr_union *sa = (union sockaddr_union*) _sa;
char *p;
+ int r;
assert(sa);
assert(salen >= sizeof(sa->sa.sa_family));
@@ -499,12 +504,17 @@ int sockaddr_pretty(const struct sockaddr *_sa, socklen_t salen, bool translate_
a = ntohl(sa->in.sin_addr.s_addr);
- if (asprintf(&p,
- "%u.%u.%u.%u:%u",
- a >> 24, (a >> 16) & 0xFF, (a >> 8) & 0xFF, a & 0xFF,
- ntohs(sa->in.sin_port)) < 0)
+ if (include_port)
+ r = asprintf(&p,
+ "%u.%u.%u.%u:%u",
+ a >> 24, (a >> 16) & 0xFF, (a >> 8) & 0xFF, a & 0xFF,
+ ntohs(sa->in.sin_port));
+ else
+ r = asprintf(&p,
+ "%u.%u.%u.%u",
+ a >> 24, (a >> 16) & 0xFF, (a >> 8) & 0xFF, a & 0xFF);
+ if (r < 0)
return -ENOMEM;
-
break;
}
@@ -513,22 +523,37 @@ int sockaddr_pretty(const struct sockaddr *_sa, socklen_t salen, bool translate_
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF
};
- if (translate_ipv6 && memcmp(&sa->in6.sin6_addr, ipv4_prefix, sizeof(ipv4_prefix)) == 0) {
+ if (translate_ipv6 &&
+ memcmp(&sa->in6.sin6_addr, ipv4_prefix, sizeof(ipv4_prefix)) == 0) {
const uint8_t *a = sa->in6.sin6_addr.s6_addr+12;
-
- if (asprintf(&p,
- "%u.%u.%u.%u:%u",
- a[0], a[1], a[2], a[3],
- ntohs(sa->in6.sin6_port)) < 0)
+ if (include_port)
+ r = asprintf(&p,
+ "%u.%u.%u.%u:%u",
+ a[0], a[1], a[2], a[3],
+ ntohs(sa->in6.sin6_port));
+ else
+ r = asprintf(&p,
+ "%u.%u.%u.%u",
+ a[0], a[1], a[2], a[3]);
+ if (r < 0)
return -ENOMEM;
} else {
char a[INET6_ADDRSTRLEN];
- if (asprintf(&p,
- "[%s]:%u",
- inet_ntop(AF_INET6, &sa->in6.sin6_addr, a, sizeof(a)),
- ntohs(sa->in6.sin6_port)) < 0)
- return -ENOMEM;
+ inet_ntop(AF_INET6, &sa->in6.sin6_addr, a, sizeof(a));
+
+ if (include_port) {
+ r = asprintf(&p,
+ "[%s]:%u",
+ a,
+ ntohs(sa->in6.sin6_port));
+ if (r < 0)
+ return -ENOMEM;
+ } else {
+ p = strdup(a);
+ if (!p)
+ return -ENOMEM;
+ }
}
break;
@@ -565,7 +590,7 @@ int sockaddr_pretty(const struct sockaddr *_sa, socklen_t salen, bool translate_
break;
default:
- return -ENOTSUP;
+ return -EOPNOTSUPP;
}
@@ -603,7 +628,7 @@ int getpeername_pretty(int fd, char **ret) {
/* For remote sockets we translate IPv6 addresses back to IPv4
* if applicable, since that's nicer. */
- return sockaddr_pretty(&sa.sa, salen, true, ret);
+ return sockaddr_pretty(&sa.sa, salen, true, true, ret);
}
int getsockname_pretty(int fd, char **ret) {
@@ -621,7 +646,7 @@ int getsockname_pretty(int fd, char **ret) {
* listening sockets where the difference between IPv4 and
* IPv6 matters. */
- return sockaddr_pretty(&sa.sa, salen, false, ret);
+ return sockaddr_pretty(&sa.sa, salen, false, true, ret);
}
int socknameinfo_pretty(union sockaddr_union *sa, socklen_t salen, char **_ret) {
@@ -635,7 +660,7 @@ int socknameinfo_pretty(union sockaddr_union *sa, socklen_t salen, char **_ret)
if (r != 0) {
int saved_errno = errno;
- r = sockaddr_pretty(&sa->sa, salen, true, &ret);
+ r = sockaddr_pretty(&sa->sa, salen, true, true, &ret);
if (r < 0)
return log_error_errno(r, "sockadd_pretty() failed: %m");
diff --git a/src/shared/socket-util.h b/src/shared/socket-util.h
index 07d0aff72b..538cf59174 100644
--- a/src/shared/socket-util.h
+++ b/src/shared/socket-util.h
@@ -25,7 +25,6 @@
#include <netinet/in.h>
#include <netinet/ether.h>
#include <sys/un.h>
-#include <asm/types.h>
#include <linux/netlink.h>
#include <linux/if_packet.h>
@@ -67,6 +66,7 @@ typedef enum SocketAddressBindIPv6Only {
#define socket_address_family(a) ((a)->sockaddr.sa.sa_family)
int socket_address_parse(SocketAddress *a, const char *s);
+int socket_address_parse_and_warn(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) _pure_;
@@ -98,7 +98,9 @@ const char* socket_address_get_path(const SocketAddress *a);
bool socket_ipv6_is_supported(void);
-int sockaddr_pretty(const struct sockaddr *_sa, socklen_t salen, bool translate_ipv6, char **ret);
+int sockaddr_port(const struct sockaddr *_sa) _pure_;
+
+int sockaddr_pretty(const struct sockaddr *_sa, socklen_t salen, bool translate_ipv6, bool include_port, char **ret);
int getpeername_pretty(int fd, char **ret);
int getsockname_pretty(int fd, char **ret);
diff --git a/src/shared/spawn-ask-password-agent.c b/src/shared/spawn-ask-password-agent.c
index ee267833e6..70466d17e5 100644
--- a/src/shared/spawn-ask-password-agent.c
+++ b/src/shared/spawn-ask-password-agent.c
@@ -19,16 +19,13 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <sys/types.h>
#include <stdlib.h>
#include <unistd.h>
-#include <string.h>
-#include <sys/prctl.h>
#include <signal.h>
-#include <fcntl.h>
#include "log.h"
#include "util.h"
+#include "process-util.h"
#include "spawn-ask-password-agent.h"
static pid_t agent_pid = 0;
diff --git a/src/shared/spawn-polkit-agent.c b/src/shared/spawn-polkit-agent.c
index 8f259a8f39..4db249e1ca 100644
--- a/src/shared/spawn-polkit-agent.c
+++ b/src/shared/spawn-polkit-agent.c
@@ -19,18 +19,15 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <sys/types.h>
#include <stdlib.h>
#include <unistd.h>
-#include <string.h>
-#include <sys/prctl.h>
#include <signal.h>
-#include <fcntl.h>
#include <errno.h>
#include <poll.h>
#include "log.h"
#include "util.h"
+#include "process-util.h"
#include "spawn-polkit-agent.h"
#ifdef ENABLE_POLKIT
diff --git a/src/shared/special.h b/src/shared/special.h
index b045047d36..e51310eb6d 100644
--- a/src/shared/special.h
+++ b/src/shared/special.h
@@ -42,6 +42,8 @@
/* Special boot targets */
#define SPECIAL_RESCUE_TARGET "rescue.target"
#define SPECIAL_EMERGENCY_TARGET "emergency.target"
+#define SPECIAL_MULTI_USER_TARGET "multi-user.target"
+#define SPECIAL_GRAPHICAL_TARGET "graphical.target"
/* Early boot targets */
#define SPECIAL_SYSINIT_TARGET "sysinit.target"
@@ -108,13 +110,6 @@
#define SPECIAL_SIGPWR_TARGET "sigpwr.target"
#define SPECIAL_CTRL_ALT_DEL_TARGET "ctrl-alt-del.target"
-/* For SysV compatibility. Usually an alias for a saner target. On
- * SysV-free systems this doesn't exist. */
-#define SPECIAL_RUNLEVEL2_TARGET "runlevel2.target"
-#define SPECIAL_RUNLEVEL3_TARGET "runlevel3.target"
-#define SPECIAL_RUNLEVEL4_TARGET "runlevel4.target"
-#define SPECIAL_RUNLEVEL5_TARGET "runlevel5.target"
-
/* Where we add all our system units, users and machines by default */
#define SPECIAL_SYSTEM_SLICE "system.slice"
#define SPECIAL_USER_SLICE "user.slice"
diff --git a/src/shared/specifier.c b/src/shared/specifier.c
index 8fbf6db5df..85bd477f2d 100644
--- a/src/shared/specifier.c
+++ b/src/shared/specifier.c
@@ -24,6 +24,7 @@
#include "macro.h"
#include "util.h"
+#include "hostname-util.h"
#include "specifier.h"
/*
diff --git a/src/shared/strbuf.h b/src/shared/strbuf.h
index 2347fd4328..fbc4e5f2a1 100644
--- a/src/shared/strbuf.h
+++ b/src/shared/strbuf.h
@@ -21,9 +21,7 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdarg.h>
#include <stdint.h>
-#include <stdbool.h>
struct strbuf {
char *buf;
diff --git a/src/shared/strv.c b/src/shared/strv.c
index e27ac68151..d44a72fc48 100644
--- a/src/shared/strv.c
+++ b/src/shared/strv.c
@@ -19,7 +19,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <assert.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
@@ -81,9 +80,10 @@ void strv_clear(char **l) {
*l = NULL;
}
-void strv_free(char **l) {
+char **strv_free(char **l) {
strv_clear(l);
free(l);
+ return NULL;
}
char **strv_copy(char * const *l) {
@@ -278,7 +278,7 @@ char **strv_split_newlines(const char *s) {
return l;
}
-int strv_split_quoted(char ***t, const char *s, bool relax) {
+int strv_split_quoted(char ***t, const char *s, UnquoteFlags flags) {
size_t n = 0, allocated = 0;
_cleanup_strv_free_ char **l = NULL;
int r;
@@ -289,7 +289,7 @@ int strv_split_quoted(char ***t, const char *s, bool relax) {
for (;;) {
_cleanup_free_ char *word = NULL;
- r = unquote_first_word(&s, &word, relax);
+ r = unquote_first_word(&s, &word, flags);
if (r < 0)
return r;
if (r == 0)
diff --git a/src/shared/strv.h b/src/shared/strv.h
index 518c4c2aa8..22f8f98fda 100644
--- a/src/shared/strv.h
+++ b/src/shared/strv.h
@@ -31,7 +31,7 @@ char *strv_find(char **l, const char *name) _pure_;
char *strv_find_prefix(char **l, const char *name) _pure_;
char *strv_find_startswith(char **l, const char *name) _pure_;
-void strv_free(char **l);
+char **strv_free(char **l);
DEFINE_TRIVIAL_CLEANUP_FUNC(char**, strv_free);
#define _cleanup_strv_free_ _cleanup_(strv_freep)
@@ -73,7 +73,7 @@ static inline bool strv_isempty(char * const *l) {
char **strv_split(const char *s, const char *separator);
char **strv_split_newlines(const char *s);
-int strv_split_quoted(char ***t, const char *s, bool relax);
+int strv_split_quoted(char ***t, const char *s, UnquoteFlags flags);
char *strv_join(char **l, const char *separator);
char *strv_join_quoted(char **l);
diff --git a/src/shared/strxcpyx.h b/src/shared/strxcpyx.h
index 7be246d570..ccc7e52f37 100644
--- a/src/shared/strxcpyx.h
+++ b/src/shared/strxcpyx.h
@@ -21,8 +21,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdarg.h>
-#include <stdbool.h>
#include "macro.h"
diff --git a/src/shared/switch-root.c b/src/shared/switch-root.c
index 813641ad44..b12189cd10 100644
--- a/src/shared/switch-root.c
+++ b/src/shared/switch-root.c
@@ -29,10 +29,11 @@
#include "util.h"
#include "path-util.h"
-#include "switch-root.h"
#include "mkdir.h"
+#include "rm-rf.h"
#include "base-filesystem.h"
#include "missing.h"
+#include "switch-root.h"
int switch_root(const char *new_root, const char *oldroot, bool detach_oldroot, unsigned long mountflags) {
@@ -104,7 +105,7 @@ int switch_root(const char *new_root, const char *oldroot, bool detach_oldroot,
* to look like. They might even boot, if they are RO and
* don't have the FS layout. Just ignore the error and
* switch_root() nevertheless. */
- (void) base_filesystem_create(new_root);
+ (void) base_filesystem_create(new_root, UID_INVALID, GID_INVALID);
if (chdir(new_root) < 0)
return log_error_errno(errno, "Failed to change directory to %s: %m", new_root);
@@ -142,7 +143,7 @@ int switch_root(const char *new_root, const char *oldroot, bool detach_oldroot,
if (fstat(old_root_fd, &rb) < 0)
log_warning_errno(errno, "Failed to stat old root directory, leaving: %m");
else {
- rm_rf_children(old_root_fd, false, false, &rb);
+ (void) rm_rf_children(old_root_fd, 0, &rb);
old_root_fd = -1;
}
}
diff --git a/src/shared/sysctl-util.c b/src/shared/sysctl-util.c
new file mode 100644
index 0000000000..55f4e48601
--- /dev/null
+++ b/src/shared/sysctl-util.c
@@ -0,0 +1,80 @@
+/*-*- 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 <stdlib.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <limits.h>
+#include <getopt.h>
+
+#include "log.h"
+#include "util.h"
+#include "fileio.h"
+#include "build.h"
+#include "sysctl-util.h"
+
+char *sysctl_normalize(char *s) {
+ char *n;
+
+ n = strpbrk(s, "/.");
+ /* If the first separator is a slash, the path is
+ * assumed to be normalized and slashes remain slashes
+ * and dots remains dots. */
+ if (!n || *n == '/')
+ return s;
+
+ /* Otherwise, dots become slashes and slashes become
+ * dots. Fun. */
+ while (n) {
+ if (*n == '.')
+ *n = '/';
+ else
+ *n = '.';
+
+ n = strpbrk(n + 1, "/.");
+ }
+
+ return s;
+}
+
+int sysctl_write(const char *property, const char *value) {
+ char *p;
+
+ assert(property);
+ assert(value);
+
+ log_debug("Setting '%s' to '%s'", property, value);
+
+ p = strjoina("/proc/sys/", property);
+ return write_string_file(p, value);
+}
+
+int sysctl_read(const char *property, char **content) {
+ char *p;
+
+ assert(property);
+ assert(content);
+
+ p = strjoina("/proc/sys/", property);
+ return read_full_file(p, content, NULL);
+}
diff --git a/src/boot/boot-loader.h b/src/shared/sysctl-util.h
index b3fcdeedad..2ee6454e52 100644
--- a/src/boot/boot-loader.h
+++ b/src/shared/sysctl-util.h
@@ -5,7 +5,7 @@
/***
This file is part of systemd.
- Copyright 2013 Kay Sievers
+ 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
@@ -21,7 +21,7 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "boot.h"
+char *sysctl_normalize(char *s);
+int sysctl_read(const char *property, char **value);
+int sysctl_write(const char *property, const char *value);
-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/shared/terminal-util.c b/src/shared/terminal-util.c
new file mode 100644
index 0000000000..042b88f222
--- /dev/null
+++ b/src/shared/terminal-util.c
@@ -0,0 +1,1072 @@
+/***
+ 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 <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <termios.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <time.h>
+#include <assert.h>
+#include <poll.h>
+#include <linux/vt.h>
+#include <linux/tiocl.h>
+#include <linux/kd.h>
+
+#include "terminal-util.h"
+#include "time-util.h"
+#include "process-util.h"
+#include "util.h"
+#include "fileio.h"
+#include "path-util.h"
+
+static volatile unsigned cached_columns = 0;
+static volatile unsigned cached_lines = 0;
+
+int chvt(int vt) {
+ _cleanup_close_ int fd;
+
+ fd = open_terminal("/dev/tty0", O_RDWR|O_NOCTTY|O_CLOEXEC);
+ if (fd < 0)
+ return -errno;
+
+ if (vt < 0) {
+ int tiocl[2] = {
+ TIOCL_GETKMSGREDIRECT,
+ 0
+ };
+
+ if (ioctl(fd, TIOCLINUX, tiocl) < 0)
+ return -errno;
+
+ vt = tiocl[0] <= 0 ? 1 : tiocl[0];
+ }
+
+ if (ioctl(fd, VT_ACTIVATE, vt) < 0)
+ return -errno;
+
+ return 0;
+}
+
+int read_one_char(FILE *f, char *ret, usec_t t, bool *need_nl) {
+ struct termios old_termios, new_termios;
+ char c, line[LINE_MAX];
+
+ assert(f);
+ assert(ret);
+
+ if (tcgetattr(fileno(f), &old_termios) >= 0) {
+ new_termios = old_termios;
+
+ new_termios.c_lflag &= ~ICANON;
+ new_termios.c_cc[VMIN] = 1;
+ new_termios.c_cc[VTIME] = 0;
+
+ if (tcsetattr(fileno(f), TCSADRAIN, &new_termios) >= 0) {
+ size_t k;
+
+ if (t != USEC_INFINITY) {
+ if (fd_wait_for_event(fileno(f), POLLIN, t) <= 0) {
+ tcsetattr(fileno(f), TCSADRAIN, &old_termios);
+ return -ETIMEDOUT;
+ }
+ }
+
+ k = fread(&c, 1, 1, f);
+
+ tcsetattr(fileno(f), TCSADRAIN, &old_termios);
+
+ if (k <= 0)
+ return -EIO;
+
+ if (need_nl)
+ *need_nl = c != '\n';
+
+ *ret = c;
+ return 0;
+ }
+ }
+
+ if (t != USEC_INFINITY) {
+ if (fd_wait_for_event(fileno(f), POLLIN, t) <= 0)
+ return -ETIMEDOUT;
+ }
+
+ errno = 0;
+ if (!fgets(line, sizeof(line), f))
+ return errno ? -errno : -EIO;
+
+ truncate_nl(line);
+
+ if (strlen(line) != 1)
+ return -EBADMSG;
+
+ if (need_nl)
+ *need_nl = false;
+
+ *ret = line[0];
+ return 0;
+}
+
+int ask_char(char *ret, const char *replies, const char *text, ...) {
+ int r;
+
+ assert(ret);
+ assert(replies);
+ assert(text);
+
+ for (;;) {
+ va_list ap;
+ char c;
+ bool need_nl = true;
+
+ if (on_tty())
+ fputs(ANSI_HIGHLIGHT_ON, stdout);
+
+ va_start(ap, text);
+ vprintf(text, ap);
+ va_end(ap);
+
+ if (on_tty())
+ fputs(ANSI_HIGHLIGHT_OFF, stdout);
+
+ fflush(stdout);
+
+ r = read_one_char(stdin, &c, USEC_INFINITY, &need_nl);
+ if (r < 0) {
+
+ if (r == -EBADMSG) {
+ puts("Bad input, please try again.");
+ continue;
+ }
+
+ putchar('\n');
+ return r;
+ }
+
+ if (need_nl)
+ putchar('\n');
+
+ if (strchr(replies, c)) {
+ *ret = c;
+ return 0;
+ }
+
+ puts("Read unexpected character, please try again.");
+ }
+}
+
+int ask_string(char **ret, const char *text, ...) {
+ assert(ret);
+ assert(text);
+
+ for (;;) {
+ char line[LINE_MAX];
+ va_list ap;
+
+ if (on_tty())
+ fputs(ANSI_HIGHLIGHT_ON, stdout);
+
+ va_start(ap, text);
+ vprintf(text, ap);
+ va_end(ap);
+
+ if (on_tty())
+ fputs(ANSI_HIGHLIGHT_OFF, stdout);
+
+ fflush(stdout);
+
+ errno = 0;
+ if (!fgets(line, sizeof(line), stdin))
+ return errno ? -errno : -EIO;
+
+ if (!endswith(line, "\n"))
+ putchar('\n');
+ else {
+ char *s;
+
+ if (isempty(line))
+ continue;
+
+ truncate_nl(line);
+ s = strdup(line);
+ if (!s)
+ return -ENOMEM;
+
+ *ret = s;
+ return 0;
+ }
+ }
+}
+
+int reset_terminal_fd(int fd, bool switch_to_text) {
+ struct termios termios;
+ int r = 0;
+
+ /* Set terminal to some sane defaults */
+
+ assert(fd >= 0);
+
+ /* We leave locked terminal attributes untouched, so that
+ * Plymouth may set whatever it wants to set, and we don't
+ * interfere with that. */
+
+ /* Disable exclusive mode, just in case */
+ ioctl(fd, TIOCNXCL);
+
+ /* Switch to text mode */
+ if (switch_to_text)
+ ioctl(fd, KDSETMODE, KD_TEXT);
+
+ /* Enable console unicode mode */
+ ioctl(fd, KDSKBMODE, K_UNICODE);
+
+ if (tcgetattr(fd, &termios) < 0) {
+ r = -errno;
+ goto finish;
+ }
+
+ /* We only reset the stuff that matters to the software. How
+ * hardware is set up we don't touch assuming that somebody
+ * else will do that for us */
+
+ termios.c_iflag &= ~(IGNBRK | BRKINT | ISTRIP | INLCR | IGNCR | IUCLC);
+ termios.c_iflag |= ICRNL | IMAXBEL | IUTF8;
+ termios.c_oflag |= ONLCR;
+ termios.c_cflag |= CREAD;
+ termios.c_lflag = ISIG | ICANON | IEXTEN | ECHO | ECHOE | ECHOK | ECHOCTL | ECHOPRT | ECHOKE;
+
+ termios.c_cc[VINTR] = 03; /* ^C */
+ termios.c_cc[VQUIT] = 034; /* ^\ */
+ termios.c_cc[VERASE] = 0177;
+ termios.c_cc[VKILL] = 025; /* ^X */
+ termios.c_cc[VEOF] = 04; /* ^D */
+ termios.c_cc[VSTART] = 021; /* ^Q */
+ termios.c_cc[VSTOP] = 023; /* ^S */
+ termios.c_cc[VSUSP] = 032; /* ^Z */
+ termios.c_cc[VLNEXT] = 026; /* ^V */
+ termios.c_cc[VWERASE] = 027; /* ^W */
+ termios.c_cc[VREPRINT] = 022; /* ^R */
+ termios.c_cc[VEOL] = 0;
+ termios.c_cc[VEOL2] = 0;
+
+ termios.c_cc[VTIME] = 0;
+ termios.c_cc[VMIN] = 1;
+
+ if (tcsetattr(fd, TCSANOW, &termios) < 0)
+ r = -errno;
+
+finish:
+ /* Just in case, flush all crap out */
+ tcflush(fd, TCIOFLUSH);
+
+ return r;
+}
+
+int reset_terminal(const char *name) {
+ _cleanup_close_ int fd = -1;
+
+ fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC);
+ if (fd < 0)
+ return fd;
+
+ return reset_terminal_fd(fd, true);
+}
+
+int open_terminal(const char *name, int mode) {
+ int fd, r;
+ unsigned c = 0;
+
+ /*
+ * If a TTY is in the process of being closed opening it might
+ * cause EIO. This is horribly awful, but unlikely to be
+ * changed in the kernel. Hence we work around this problem by
+ * retrying a couple of times.
+ *
+ * https://bugs.launchpad.net/ubuntu/+source/linux/+bug/554172/comments/245
+ */
+
+ assert(!(mode & O_CREAT));
+
+ for (;;) {
+ fd = open(name, mode, 0);
+ if (fd >= 0)
+ break;
+
+ if (errno != EIO)
+ return -errno;
+
+ /* Max 1s in total */
+ if (c >= 20)
+ return -errno;
+
+ usleep(50 * USEC_PER_MSEC);
+ c++;
+ }
+
+ r = isatty(fd);
+ if (r < 0) {
+ safe_close(fd);
+ return -errno;
+ }
+
+ if (!r) {
+ safe_close(fd);
+ return -ENOTTY;
+ }
+
+ return fd;
+}
+
+int acquire_terminal(
+ const char *name,
+ bool fail,
+ bool force,
+ bool ignore_tiocstty_eperm,
+ usec_t timeout) {
+
+ int fd = -1, notify = -1, r = 0, wd = -1;
+ usec_t ts = 0;
+
+ assert(name);
+
+ /* We use inotify to be notified when the tty is closed. We
+ * create the watch before checking if we can actually acquire
+ * it, so that we don't lose any event.
+ *
+ * Note: strictly speaking this actually watches for the
+ * device being closed, it does *not* really watch whether a
+ * tty loses its controlling process. However, unless some
+ * rogue process uses TIOCNOTTY on /dev/tty *after* closing
+ * its tty otherwise this will not become a problem. As long
+ * as the administrator makes sure not configure any service
+ * on the same tty as an untrusted user this should not be a
+ * problem. (Which he probably should not do anyway.) */
+
+ if (timeout != USEC_INFINITY)
+ ts = now(CLOCK_MONOTONIC);
+
+ if (!fail && !force) {
+ notify = inotify_init1(IN_CLOEXEC | (timeout != USEC_INFINITY ? IN_NONBLOCK : 0));
+ if (notify < 0) {
+ r = -errno;
+ goto fail;
+ }
+
+ wd = inotify_add_watch(notify, name, IN_CLOSE);
+ if (wd < 0) {
+ r = -errno;
+ goto fail;
+ }
+ }
+
+ 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)
+ goto fail;
+ }
+
+ /* We pass here O_NOCTTY only so that we can check the return
+ * value TIOCSCTTY and have a reliable way to figure out if we
+ * successfully became the controlling process of the tty */
+ fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC);
+ if (fd < 0)
+ return fd;
+
+ /* Temporarily ignore SIGHUP, so that we don't get SIGHUP'ed
+ * if we already own the tty. */
+ assert_se(sigaction(SIGHUP, &sa_new, &sa_old) == 0);
+
+ /* First, try to get the tty */
+ if (ioctl(fd, TIOCSCTTY, force) < 0)
+ r = -errno;
+
+ assert_se(sigaction(SIGHUP, &sa_old, NULL) == 0);
+
+ /* Sometimes it makes sense to ignore TIOCSCTTY
+ * returning EPERM, i.e. when very likely we already
+ * are have this controlling terminal. */
+ if (r < 0 && r == -EPERM && ignore_tiocstty_eperm)
+ r = 0;
+
+ if (r < 0 && (force || fail || r != -EPERM)) {
+ goto fail;
+ }
+
+ if (r >= 0)
+ break;
+
+ assert(!fail);
+ assert(!force);
+ assert(notify >= 0);
+
+ for (;;) {
+ union inotify_event_buffer buffer;
+ struct inotify_event *e;
+ ssize_t l;
+
+ if (timeout != USEC_INFINITY) {
+ usec_t n;
+
+ n = now(CLOCK_MONOTONIC);
+ if (ts + timeout < n) {
+ r = -ETIMEDOUT;
+ goto fail;
+ }
+
+ r = fd_wait_for_event(fd, POLLIN, ts + timeout - n);
+ if (r < 0)
+ goto fail;
+
+ if (r == 0) {
+ r = -ETIMEDOUT;
+ goto fail;
+ }
+ }
+
+ l = read(notify, &buffer, sizeof(buffer));
+ if (l < 0) {
+ if (errno == EINTR || errno == EAGAIN)
+ continue;
+
+ r = -errno;
+ goto fail;
+ }
+
+ FOREACH_INOTIFY_EVENT(e, buffer, l) {
+ if (e->wd != wd || !(e->mask & IN_CLOSE)) {
+ r = -EIO;
+ goto fail;
+ }
+ }
+
+ break;
+ }
+
+ /* We close the tty fd here since if the old session
+ * ended our handle will be dead. It's important that
+ * we do this after sleeping, so that we don't enter
+ * an endless loop. */
+ fd = safe_close(fd);
+ }
+
+ safe_close(notify);
+
+ r = reset_terminal_fd(fd, true);
+ if (r < 0)
+ log_warning_errno(r, "Failed to reset terminal: %m");
+
+ return fd;
+
+fail:
+ safe_close(fd);
+ safe_close(notify);
+
+ return r;
+}
+
+int release_terminal(void) {
+ static const struct sigaction sa_new = {
+ .sa_handler = SIG_IGN,
+ .sa_flags = SA_RESTART,
+ };
+
+ _cleanup_close_ int fd = -1;
+ struct sigaction sa_old;
+ int r = 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 */
+ assert_se(sigaction(SIGHUP, &sa_new, &sa_old) == 0);
+
+ if (ioctl(fd, TIOCNOTTY) < 0)
+ r = -errno;
+
+ assert_se(sigaction(SIGHUP, &sa_old, NULL) == 0);
+
+ return r;
+}
+
+int terminal_vhangup_fd(int fd) {
+ assert(fd >= 0);
+
+ if (ioctl(fd, TIOCVHANGUP) < 0)
+ return -errno;
+
+ return 0;
+}
+
+int terminal_vhangup(const char *name) {
+ _cleanup_close_ int fd;
+
+ fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC);
+ if (fd < 0)
+ return fd;
+
+ return terminal_vhangup_fd(fd);
+}
+
+int vt_disallocate(const char *name) {
+ int fd, r;
+ unsigned u;
+
+ /* Deallocate the VT if possible. If not possible
+ * (i.e. because it is the active one), at least clear it
+ * entirely (including the scrollback buffer) */
+
+ if (!startswith(name, "/dev/"))
+ return -EINVAL;
+
+ if (!tty_is_vc(name)) {
+ /* So this is not a VT. I guess we cannot deallocate
+ * it then. But let's at least clear the screen */
+
+ fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC);
+ if (fd < 0)
+ return fd;
+
+ loop_write(fd,
+ "\033[r" /* clear scrolling region */
+ "\033[H" /* move home */
+ "\033[2J", /* clear screen */
+ 10, false);
+ safe_close(fd);
+
+ return 0;
+ }
+
+ if (!startswith(name, "/dev/tty"))
+ return -EINVAL;
+
+ r = safe_atou(name+8, &u);
+ if (r < 0)
+ return r;
+
+ if (u <= 0)
+ return -EINVAL;
+
+ /* Try to deallocate */
+ fd = open_terminal("/dev/tty0", O_RDWR|O_NOCTTY|O_CLOEXEC);
+ if (fd < 0)
+ return fd;
+
+ r = ioctl(fd, VT_DISALLOCATE, u);
+ safe_close(fd);
+
+ if (r >= 0)
+ return 0;
+
+ if (errno != EBUSY)
+ return -errno;
+
+ /* Couldn't deallocate, so let's clear it fully with
+ * scrollback */
+ fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC);
+ if (fd < 0)
+ return fd;
+
+ loop_write(fd,
+ "\033[r" /* clear scrolling region */
+ "\033[H" /* move home */
+ "\033[3J", /* clear screen including scrollback, requires Linux 2.6.40 */
+ 10, false);
+ safe_close(fd);
+
+ return 0;
+}
+
+void warn_melody(void) {
+ _cleanup_close_ int fd = -1;
+
+ fd = open("/dev/console", O_WRONLY|O_CLOEXEC|O_NOCTTY);
+ if (fd < 0)
+ return;
+
+ /* Yeah, this is synchronous. Kinda sucks. But well... */
+
+ ioctl(fd, KIOCSOUND, (int)(1193180/440));
+ usleep(125*USEC_PER_MSEC);
+
+ ioctl(fd, KIOCSOUND, (int)(1193180/220));
+ usleep(125*USEC_PER_MSEC);
+
+ ioctl(fd, KIOCSOUND, (int)(1193180/220));
+ usleep(125*USEC_PER_MSEC);
+
+ ioctl(fd, KIOCSOUND, 0);
+}
+
+int make_console_stdio(void) {
+ int fd, r;
+
+ /* Make /dev/console the controlling terminal and stdin/stdout/stderr */
+
+ fd = acquire_terminal("/dev/console", false, true, true, USEC_INFINITY);
+ if (fd < 0)
+ return log_error_errno(fd, "Failed to acquire terminal: %m");
+
+ r = make_stdio(fd);
+ if (r < 0)
+ return log_error_errno(r, "Failed to duplicate terminal fd: %m");
+
+ return 0;
+}
+
+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[6] = {};
+ int n = 0;
+ static bool prev_ephemeral;
+
+ assert(format);
+
+ /* This is independent of logging, as status messages are
+ * optional and go exclusively to the console. */
+
+ if (vasprintf(&s, format, ap) < 0)
+ return log_oom();
+
+ fd = open_terminal("/dev/console", O_WRONLY|O_NOCTTY|O_CLOEXEC);
+ if (fd < 0)
+ return fd;
+
+ if (ellipse) {
+ char *e;
+ size_t emax, sl;
+ int c;
+
+ c = fd_columns(fd);
+ if (c <= 0)
+ c = 80;
+
+ sl = status ? sizeof(status_indent)-1 : 0;
+
+ emax = c - sl - 1;
+ if (emax < 3)
+ emax = 3;
+
+ e = ellipsize(s, emax, 50);
+ if (e) {
+ free(s);
+ s = e;
+ }
+ }
+
+ if (prev_ephemeral)
+ IOVEC_SET_STRING(iovec[n++], "\r" ANSI_ERASE_TO_END_OF_LINE);
+ prev_ephemeral = ephemeral;
+
+ if (status) {
+ if (!isempty(status)) {
+ IOVEC_SET_STRING(iovec[n++], "[");
+ IOVEC_SET_STRING(iovec[n++], status);
+ IOVEC_SET_STRING(iovec[n++], "] ");
+ } else
+ IOVEC_SET_STRING(iovec[n++], status_indent);
+ }
+
+ IOVEC_SET_STRING(iovec[n++], s);
+ if (!ephemeral)
+ IOVEC_SET_STRING(iovec[n++], "\n");
+
+ if (writev(fd, iovec, n) < 0)
+ return -errno;
+
+ return 0;
+}
+
+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, ephemeral, format, ap);
+ va_end(ap);
+
+ return r;
+}
+
+bool tty_is_vc(const char *tty) {
+ assert(tty);
+
+ return vtnr_from_tty(tty) >= 0;
+}
+
+bool tty_is_console(const char *tty) {
+ assert(tty);
+
+ if (startswith(tty, "/dev/"))
+ tty += 5;
+
+ return streq(tty, "console");
+}
+
+int vtnr_from_tty(const char *tty) {
+ int i, r;
+
+ assert(tty);
+
+ if (startswith(tty, "/dev/"))
+ tty += 5;
+
+ if (!startswith(tty, "tty") )
+ return -EINVAL;
+
+ if (tty[3] < '0' || tty[3] > '9')
+ return -EINVAL;
+
+ r = safe_atoi(tty+3, &i);
+ if (r < 0)
+ return r;
+
+ if (i < 0 || i > 63)
+ return -EINVAL;
+
+ 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;
+
+ if (streq(tty, "tty0")) {
+ char *tmp;
+
+ /* Get the active VC (e.g. tty1) */
+ if (read_one_line_file("/sys/class/tty/tty0/active", &tmp) >= 0) {
+ free(*active);
+ tty = *active = tmp;
+ }
+ }
+
+ return tty;
+}
+
+bool tty_is_vc_resolve(const char *tty) {
+ _cleanup_free_ char *active = NULL;
+
+ assert(tty);
+
+ if (startswith(tty, "/dev/"))
+ tty += 5;
+
+ if (streq(tty, "console")) {
+ tty = resolve_dev_console(&active);
+ if (!tty)
+ return false;
+ }
+
+ return tty_is_vc(tty);
+}
+
+const char *default_term_for_tty(const char *tty) {
+ assert(tty);
+
+ return tty_is_vc_resolve(tty) ? "TERM=linux" : "TERM=vt220";
+}
+
+int fd_columns(int fd) {
+ struct winsize ws = {};
+
+ if (ioctl(fd, TIOCGWINSZ, &ws) < 0)
+ return -errno;
+
+ if (ws.ws_col <= 0)
+ return -EIO;
+
+ return ws.ws_col;
+}
+
+unsigned columns(void) {
+ const char *e;
+ int c;
+
+ if (_likely_(cached_columns > 0))
+ return cached_columns;
+
+ c = 0;
+ e = getenv("COLUMNS");
+ if (e)
+ (void) safe_atoi(e, &c);
+
+ if (c <= 0)
+ c = fd_columns(STDOUT_FILENO);
+
+ if (c <= 0)
+ c = 80;
+
+ cached_columns = c;
+ return cached_columns;
+}
+
+int fd_lines(int fd) {
+ struct winsize ws = {};
+
+ if (ioctl(fd, TIOCGWINSZ, &ws) < 0)
+ return -errno;
+
+ if (ws.ws_row <= 0)
+ return -EIO;
+
+ return ws.ws_row;
+}
+
+unsigned lines(void) {
+ const char *e;
+ int l;
+
+ if (_likely_(cached_lines > 0))
+ return cached_lines;
+
+ l = 0;
+ e = getenv("LINES");
+ if (e)
+ (void) safe_atoi(e, &l);
+
+ if (l <= 0)
+ l = fd_lines(STDOUT_FILENO);
+
+ if (l <= 0)
+ l = 24;
+
+ cached_lines = l;
+ return cached_lines;
+}
+
+/* intended to be used as a SIGWINCH sighandler */
+void columns_lines_cache_reset(int signum) {
+ cached_columns = 0;
+ cached_lines = 0;
+}
+
+bool on_tty(void) {
+ static int cached_on_tty = -1;
+
+ if (_unlikely_(cached_on_tty < 0))
+ cached_on_tty = isatty(STDOUT_FILENO) > 0;
+
+ return cached_on_tty;
+}
+
+int make_stdio(int fd) {
+ int r, s, t;
+
+ assert(fd >= 0);
+
+ r = dup2(fd, STDIN_FILENO);
+ s = dup2(fd, STDOUT_FILENO);
+ t = dup2(fd, STDERR_FILENO);
+
+ if (fd >= 3)
+ safe_close(fd);
+
+ if (r < 0 || s < 0 || t < 0)
+ return -errno;
+
+ /* Explicitly unset O_CLOEXEC, since if fd was < 3, then
+ * dup2() was a NOP and the bit hence possibly set. */
+ fd_cloexec(STDIN_FILENO, false);
+ fd_cloexec(STDOUT_FILENO, false);
+ fd_cloexec(STDERR_FILENO, false);
+
+ return 0;
+}
+
+int make_null_stdio(void) {
+ int null_fd;
+
+ null_fd = open("/dev/null", O_RDWR|O_NOCTTY);
+ if (null_fd < 0)
+ return -errno;
+
+ return make_stdio(null_fd);
+}
+
+int getttyname_malloc(int fd, char **ret) {
+ size_t l = 100;
+ int r;
+
+ assert(fd >= 0);
+ assert(ret);
+
+ for (;;) {
+ char path[l];
+
+ r = ttyname_r(fd, path, sizeof(path));
+ if (r == 0) {
+ const char *p;
+ char *c;
+
+ p = startswith(path, "/dev/");
+ c = strdup(p ?: path);
+ if (!c)
+ return -ENOMEM;
+
+ *ret = c;
+ return 0;
+ }
+
+ if (r != ERANGE)
+ return -r;
+
+ l *= 2;
+ }
+
+ return 0;
+}
+
+int getttyname_harder(int fd, char **r) {
+ int k;
+ char *s = NULL;
+
+ k = getttyname_malloc(fd, &s);
+ if (k < 0)
+ return k;
+
+ if (streq(s, "tty")) {
+ free(s);
+ return get_ctty(0, NULL, r);
+ }
+
+ *r = s;
+ return 0;
+}
+
+int get_ctty_devnr(pid_t pid, dev_t *d) {
+ int r;
+ _cleanup_free_ char *line = NULL;
+ const char *p;
+ unsigned long ttynr;
+
+ assert(pid >= 0);
+
+ p = procfs_file_alloca(pid, "stat");
+ r = read_one_line_file(p, &line);
+ if (r < 0)
+ return r;
+
+ p = strrchr(line, ')');
+ if (!p)
+ return -EIO;
+
+ p++;
+
+ if (sscanf(p, " "
+ "%*c " /* state */
+ "%*d " /* ppid */
+ "%*d " /* pgrp */
+ "%*d " /* session */
+ "%lu ", /* ttynr */
+ &ttynr) != 1)
+ return -EIO;
+
+ if (major(ttynr) == 0 && minor(ttynr) == 0)
+ return -ENXIO;
+
+ if (d)
+ *d = (dev_t) ttynr;
+
+ return 0;
+}
+
+int get_ctty(pid_t pid, dev_t *_devnr, char **r) {
+ char fn[sizeof("/dev/char/")-1 + 2*DECIMAL_STR_MAX(unsigned) + 1 + 1], *b = NULL;
+ _cleanup_free_ char *s = NULL;
+ const char *p;
+ dev_t devnr;
+ int k;
+
+ assert(r);
+
+ k = get_ctty_devnr(pid, &devnr);
+ if (k < 0)
+ return k;
+
+ sprintf(fn, "/dev/char/%u:%u", major(devnr), minor(devnr));
+
+ k = readlink_malloc(fn, &s);
+ if (k < 0) {
+
+ if (k != -ENOENT)
+ return k;
+
+ /* This is an ugly hack */
+ if (major(devnr) == 136) {
+ if (asprintf(&b, "pts/%u", minor(devnr)) < 0)
+ return -ENOMEM;
+ } else {
+ /* Probably something like the ptys which have no
+ * symlink in /dev/char. Let's return something
+ * vaguely useful. */
+
+ b = strdup(fn + 5);
+ if (!b)
+ return -ENOMEM;
+ }
+ } else {
+ if (startswith(s, "/dev/"))
+ p = s + 5;
+ else if (startswith(s, "../"))
+ p = s + 3;
+ else
+ p = s;
+
+ b = strdup(p);
+ if (!b)
+ return -ENOMEM;
+ }
+
+ *r = b;
+ if (_devnr)
+ *_devnr = devnr;
+
+ return 0;
+}
diff --git a/src/shared/terminal-util.h b/src/shared/terminal-util.h
new file mode 100644
index 0000000000..188714f228
--- /dev/null
+++ b/src/shared/terminal-util.h
@@ -0,0 +1,109 @@
+#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 <stdbool.h>
+#include <stdarg.h>
+#include <stdio.h>
+
+#include "macro.h"
+#include "time-util.h"
+
+#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_BLUE_ON "\x1B[1;34m"
+#define ANSI_HIGHLIGHT_OFF "\x1B[0m"
+#define ANSI_ERASE_TO_END_OF_LINE "\x1B[K"
+
+int reset_terminal_fd(int fd, bool switch_to_text);
+int reset_terminal(const char *name);
+
+int open_terminal(const char *name, int mode);
+int acquire_terminal(const char *name, bool fail, bool force, bool ignore_tiocstty_eperm, usec_t timeout);
+int release_terminal(void);
+
+int terminal_vhangup_fd(int fd);
+int terminal_vhangup(const char *name);
+
+int chvt(int vt);
+
+int read_one_char(FILE *f, char *ret, usec_t timeout, bool *need_nl);
+int ask_char(char *ret, const char *replies, const char *text, ...) _printf_(3, 4);
+int ask_string(char **ret, const char *text, ...) _printf_(2, 3);
+
+int vt_disallocate(const char *name);
+
+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) _pure_;
+int vtnr_from_tty(const char *tty);
+const char *default_term_for_tty(const char *tty);
+
+void warn_melody(void);
+
+int make_stdio(int fd);
+int make_null_stdio(void);
+int make_console_stdio(void);
+
+int status_vprintf(const char *status, bool ellipse, bool ephemeral, const char *format, va_list ap) _printf_(4,0);
+int status_printf(const char *status, bool ellipse, bool ephemeral, const char *format, ...) _printf_(4,5);
+
+int fd_columns(int fd);
+unsigned columns(void);
+int fd_lines(int fd);
+unsigned lines(void);
+void columns_lines_cache_reset(int _unused_ signum);
+
+bool on_tty(void);
+
+static inline const char *ansi_highlight(void) {
+ return on_tty() ? ANSI_HIGHLIGHT_ON : "";
+}
+
+static inline const char *ansi_highlight_red(void) {
+ return on_tty() ? ANSI_HIGHLIGHT_RED_ON : "";
+}
+
+static inline const char *ansi_highlight_green(void) {
+ return on_tty() ? ANSI_HIGHLIGHT_GREEN_ON : "";
+}
+
+static inline const char *ansi_highlight_yellow(void) {
+ return on_tty() ? ANSI_HIGHLIGHT_YELLOW_ON : "";
+}
+
+static inline const char *ansi_highlight_blue(void) {
+ return on_tty() ? ANSI_HIGHLIGHT_BLUE_ON : "";
+}
+
+static inline const char *ansi_highlight_off(void) {
+ return on_tty() ? ANSI_HIGHLIGHT_OFF : "";
+}
+
+int get_ctty_devnr(pid_t pid, dev_t *d);
+int get_ctty(pid_t, dev_t *_devnr, char **r);
+
+int getttyname_malloc(int fd, char **r);
+int getttyname_harder(int fd, char **r);
diff --git a/src/shared/time-dst.c b/src/shared/time-dst.c
deleted file mode 100644
index 1ce6f721b7..0000000000
--- a/src/shared/time-dst.c
+++ /dev/null
@@ -1,335 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Timezone file reading code from glibc 2.16.
-
- Copyright (C) 1991-2012 Free Software Foundation, Inc.
- Copyright 2012 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 <ctype.h>
-#include <errno.h>
-#include <stddef.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#include <endian.h>
-#include <byteswap.h>
-#include <assert.h>
-#include <limits.h>
-#include <unistd.h>
-#include <stdint.h>
-#include <stdbool.h>
-#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
- * of tzhead and a second instance of the data in which each coded transition
- * time uses 8 rather than 4 chars, then a POSIX-TZ-environment-variable-style
- * string for use in handling instants after the last transition time stored in
- * the file * (with nothing between the newlines if there is no POSIX
- * representation for such instants).
- */
-#define TZ_MAGIC "TZif"
-struct tzhead {
- char tzh_magic[4]; /* TZ_MAGIC */
- char tzh_version[1]; /* '\0' or '2' as of 2005 */
- char tzh_reserved[15]; /* reserved--must be zero */
- char tzh_ttisgmtcnt[4]; /* coded number of trans. time flags */
- char tzh_ttisstdcnt[4]; /* coded number of trans. time flags */
- char tzh_leapcnt[4]; /* coded number of leap seconds */
- char tzh_timecnt[4]; /* coded number of transition times */
- char tzh_typecnt[4]; /* coded number of local time types */
- char tzh_charcnt[4]; /* coded number of abbr. chars */
-};
-
-struct ttinfo {
- long int offset; /* Seconds east of GMT. */
- unsigned char isdst; /* Used to set tm_isdst. */
- unsigned char idx; /* Index into `zone_names'. */
- unsigned char isstd; /* Transition times are in standard time. */
- unsigned char isgmt; /* Transition times are in GMT. */
-};
-
-struct leap {
- time_t transition; /* Time the transition takes effect. */
- long int change; /* Seconds of correction to apply. */
-};
-
-static inline int decode(const void *ptr) {
- return be32toh(*(int *)ptr);
-}
-
-static inline int64_t decode64(const void *ptr) {
- return be64toh(*(int64_t *)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) {
- 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;
- struct tzhead tzhead;
- size_t chars;
- size_t i;
- size_t total_size;
- size_t types_idx;
- int trans_width = 4;
- size_t tzspec_len;
- size_t num_leaps;
- size_t lo, hi;
- 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)
- return -errno;
-
-read_again:
- if (fread((void *)&tzhead, sizeof(tzhead), 1, f) != 1 ||
- memcmp(tzhead.tzh_magic, TZ_MAGIC, sizeof(tzhead.tzh_magic)) != 0)
- return -EINVAL;
-
- num_transitions = (size_t)decode(tzhead.tzh_timecnt);
- num_types = (size_t)decode(tzhead.tzh_typecnt);
- chars = (size_t)decode(tzhead.tzh_charcnt);
- num_leaps = (size_t)decode(tzhead.tzh_leapcnt);
- num_isstd = (size_t)decode(tzhead.tzh_ttisstdcnt);
- num_isgmt = (size_t)decode(tzhead.tzh_ttisgmtcnt);
-
- /* For platforms with 64-bit time_t we use the new format if available. */
- if (sizeof(time_t) == 8 && trans_width == 4 && tzhead.tzh_version[0] != '\0') {
- size_t to_skip;
-
- /* We use the 8-byte format. */
- trans_width = 8;
-
- /* Position the stream before the second header. */
- to_skip = (num_transitions * (4 + 1)
- + num_types * 6
- + chars
- + num_leaps * 8 + num_isstd + num_isgmt);
- if (fseek(f, to_skip, SEEK_CUR) != 0)
- return -EINVAL;
-
- goto read_again;
- }
-
- if (num_transitions > ((SIZE_MAX - (__alignof__(struct ttinfo) - 1)) / (sizeof(time_t) + 1)))
- 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))
- return -EINVAL;
-
- total_size += num_types * sizeof(struct ttinfo);
- if (chars > SIZE_MAX - total_size)
- return -EINVAL;
-
- total_size += chars;
- if (__alignof__(struct leap) - 1 > SIZE_MAX - total_size)
- return -EINVAL;
-
- total_size = ((total_size + __alignof__(struct leap) - 1) & ~(__alignof__(struct leap) - 1));
- if (num_leaps > (SIZE_MAX - total_size) / sizeof(struct leap))
- return -EINVAL;
-
- total_size += num_leaps * sizeof(struct leap);
- tzspec_len = 0;
- if (sizeof(time_t) == 8 && trans_width == 8) {
- off_t rem = st.st_size - ftello(f);
-
- if (rem < 0 || (size_t) rem < (num_transitions * (8 + 1) + num_types * 6 + chars))
- 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)
- return -EINVAL;
- tzspec_len -= num_leaps * 12;
- if (tzspec_len < num_isstd)
- return -EINVAL;
- tzspec_len -= num_isstd;
- if (tzspec_len == 0 || tzspec_len - 1 < num_isgmt)
- return -EINVAL;
- tzspec_len -= num_isgmt + 1;
- if (SIZE_MAX - total_size < tzspec_len)
- return -EINVAL;
- }
-
- /* leave space for additional zone_names zero terminator */
- transitions = malloc0(total_size + tzspec_len + 1);
- if (transitions == NULL)
- return -EINVAL;
-
- type_idxs = (unsigned char *)transitions + (num_transitions
- * sizeof(time_t));
- types = (struct ttinfo *)((char *)transitions + types_idx);
- zone_names = (char *)types + num_types * sizeof(struct ttinfo);
-
- if (sizeof(time_t) == 4 || trans_width == 8) {
- if (fread(transitions, trans_width + 1, num_transitions, f) != num_transitions)
- return -EINVAL;
- } else {
- if (fread(transitions, 4, num_transitions, f) != num_transitions ||
- fread(type_idxs, 1, num_transitions, f) != num_transitions)
- 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)
- return -EINVAL;
-
- if (__BYTE_ORDER == __BIG_ENDIAN ? sizeof(time_t) == 8 && trans_width == 4
- : sizeof(time_t) == 4 || trans_width == 4) {
- /* Decode the transition times, stored as 4-byte integers in
- network (big-endian) byte order. We work from the end of
- the array so as not to clobber the next element to be
- processed when sizeof (time_t) > 4. */
- i = num_transitions;
- while (i-- > 0)
- transitions[i] = decode((char *)transitions + i * 4);
- } else if (__BYTE_ORDER != __BIG_ENDIAN && sizeof(time_t) == 8) {
- /* Decode the transition times, stored as 8-byte integers in
- network (big-endian) byte order. */
- for (i = 0; i < num_transitions; ++i)
- transitions[i] = decode64((char *)transitions + i * 8);
- }
-
- for (i = 0; i < num_types; ++i) {
- unsigned char x[4];
- int c;
-
- if (fread(x, 1, sizeof(x), f) != sizeof(x))
- return -EINVAL;
- c = getc(f);
- if ((unsigned int)c > 1u)
- return -EINVAL;
- types[i].isdst = c;
- c = getc(f);
- if ((size_t) c > chars)
- /* Bogus index in data file. */
- return -EINVAL;
- types[i].idx = c;
- types[i].offset = (long int)decode(x);
- }
-
- if (fread(zone_names, 1, chars, f) != chars)
- return -EINVAL;
-
- zone_names[chars] = '\0';
-
- for (i = 0; i < num_isstd; ++i) {
- int c = getc(f);
- if (c == EOF)
- return -EINVAL;
- types[i].isstd = c != 0;
- }
-
- while (i < num_types)
- types[i++].isstd = 0;
-
- for (i = 0; i < num_isgmt; ++i) {
- int c = getc(f);
- if (c == EOF)
- return -EINVAL;
- types[i].isgmt = c != 0;
- }
-
- while (i < num_types)
- types[i++].isgmt = 0;
-
- if (num_transitions == 0)
- return -EINVAL;
-
- if (date < transitions[0] || date >= transitions[num_transitions - 1])
- return -EINVAL;
-
- /* Find the first transition after TIMER, and
- then pick the type of the transition before it. */
- lo = 0;
- hi = num_transitions - 1;
-
- /* Assume that DST is changing twice a year and guess initial
- search spot from it.
- Half of a gregorian year has on average 365.2425 * 86400 / 2
- = 15778476 seconds. */
- i = (transitions[num_transitions - 1] - date) / 15778476;
- if (i < num_transitions) {
- i = num_transitions - 1 - i;
- if (date < transitions[i]) {
- if (i < 10 || date >= transitions[i - 10]) {
- /* Linear search. */
- while (date < transitions[i - 1])
- i--;
- goto found;
- }
- hi = i - 10;
- } else {
- if (i + 10 >= num_transitions || date < transitions[i + 10]) {
- /* Linear search. */
- while (date >= transitions[i])
- i++;
- goto found;
- }
- lo = i + 10;
- }
- }
-
- /* Binary search. */
- while (lo + 1 < hi) {
- i = (lo + hi) / 2;
- if (date < transitions[i])
- hi = i;
- else
- lo = i;
- }
- i = hi;
-
-found:
- if (switch_cur)
- *switch_cur = transitions[i-1];
- if (zone_cur)
- *zone_cur = strdup(&zone_names[types[type_idxs[i - 1]].idx]);
- if (dst_cur)
- *dst_cur = types[type_idxs[i-1]].isdst;
-
- if (switch_next)
- *switch_next = transitions[i];
- if (delta_next)
- *delta_next = (types[type_idxs[i]].offset - types[type_idxs[i-1]].offset) / 60;
- if (zone_next)
- *zone_next = strdup(&zone_names[types[type_idxs[i]].idx]);
- if (dst_next)
- *dst_next = types[type_idxs[i]].isdst;
-
- return 0;
-}
diff --git a/src/shared/time-util.c b/src/shared/time-util.c
index 947ac1fcfb..12f1b193be 100644
--- a/src/shared/time-util.c
+++ b/src/shared/time-util.c
@@ -398,18 +398,21 @@ void dual_timestamp_serialize(FILE *f, const char *name, dual_timestamp *t) {
t->monotonic);
}
-void dual_timestamp_deserialize(const char *value, dual_timestamp *t) {
+int dual_timestamp_deserialize(const char *value, dual_timestamp *t) {
unsigned long long a, b;
assert(value);
assert(t);
- if (sscanf(value, "%llu %llu", &a, &b) != 2)
- log_debug("Failed to parse finish timestamp value %s", value);
- else {
- t->realtime = a;
- t->monotonic = b;
+ if (sscanf(value, "%llu %llu", &a, &b) != 2) {
+ log_debug("Failed to parse finish timestamp value %s.", value);
+ return -EINVAL;
}
+
+ t->realtime = a;
+ t->monotonic = b;
+
+ return 0;
}
int parse_timestamp(const char *t, usec_t *usec) {
@@ -786,7 +789,7 @@ int parse_nsec(const char *t, nsec_t *nsec) {
s = startswith(p, "infinity");
if (s) {
s += strspn(s, WHITESPACE);
- if (!*s != 0)
+ if (*s != 0)
return -EINVAL;
*nsec = NSEC_INFINITY;
diff --git a/src/shared/time-util.h b/src/shared/time-util.h
index fca8a4db9b..7a64d454a0 100644
--- a/src/shared/time-util.h
+++ b/src/shared/time-util.h
@@ -94,7 +94,7 @@ char *format_timestamp_relative(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 dual_timestamp_deserialize(const char *value, dual_timestamp *t);
int parse_timestamp(const char *t, usec_t *usec);
diff --git a/src/shared/udev-util.h b/src/shared/udev-util.h
index 5f09ce181f..f758ce13e4 100644
--- a/src/shared/udev-util.h
+++ b/src/shared/udev-util.h
@@ -30,6 +30,8 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(struct udev_enumerate*, udev_enumerate_unref);
DEFINE_TRIVIAL_CLEANUP_FUNC(struct udev_event*, udev_event_unref);
DEFINE_TRIVIAL_CLEANUP_FUNC(struct udev_rules*, udev_rules_unref);
DEFINE_TRIVIAL_CLEANUP_FUNC(struct udev_ctrl*, udev_ctrl_unref);
+DEFINE_TRIVIAL_CLEANUP_FUNC(struct udev_ctrl_connection*, udev_ctrl_connection_unref);
+DEFINE_TRIVIAL_CLEANUP_FUNC(struct udev_ctrl_msg*, udev_ctrl_msg_unref);
DEFINE_TRIVIAL_CLEANUP_FUNC(struct udev_monitor*, udev_monitor_unref);
#define _cleanup_udev_unref_ _cleanup_(udev_unrefp)
@@ -38,5 +40,7 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(struct udev_monitor*, udev_monitor_unref);
#define _cleanup_udev_event_unref_ _cleanup_(udev_event_unrefp)
#define _cleanup_udev_rules_unref_ _cleanup_(udev_rules_unrefp)
#define _cleanup_udev_ctrl_unref_ _cleanup_(udev_ctrl_unrefp)
+#define _cleanup_udev_ctrl_connection_unref_ _cleanup_(udev_ctrl_connection_unrefp)
+#define _cleanup_udev_ctrl_msg_unref_ _cleanup_(udev_ctrl_msg_unrefp)
#define _cleanup_udev_monitor_unref_ _cleanup_(udev_monitor_unrefp)
#define _cleanup_udev_list_cleanup_ _cleanup_(udev_list_cleanup)
diff --git a/src/shared/unit-name.c b/src/shared/unit-name.c
index 21b66913c9..bf52463d81 100644
--- a/src/shared/unit-name.c
+++ b/src/shared/unit-name.c
@@ -21,7 +21,6 @@
#include <errno.h>
#include <string.h>
-#include <assert.h>
#include "path-util.h"
#include "bus-label.h"
@@ -34,45 +33,13 @@
DIGITS LETTERS \
":-_.\\"
-static const char* const unit_type_table[_UNIT_TYPE_MAX] = {
- [UNIT_SERVICE] = "service",
- [UNIT_SOCKET] = "socket",
- [UNIT_BUSNAME] = "busname",
- [UNIT_TARGET] = "target",
- [UNIT_SNAPSHOT] = "snapshot",
- [UNIT_DEVICE] = "device",
- [UNIT_MOUNT] = "mount",
- [UNIT_AUTOMOUNT] = "automount",
- [UNIT_SWAP] = "swap",
- [UNIT_TIMER] = "timer",
- [UNIT_PATH] = "path",
- [UNIT_SLICE] = "slice",
- [UNIT_SCOPE] = "scope"
-};
-
-DEFINE_STRING_TABLE_LOOKUP(unit_type, UnitType);
-
-static const char* const unit_load_state_table[_UNIT_LOAD_STATE_MAX] = {
- [UNIT_STUB] = "stub",
- [UNIT_LOADED] = "loaded",
- [UNIT_NOT_FOUND] = "not-found",
- [UNIT_ERROR] = "error",
- [UNIT_MERGED] = "merged",
- [UNIT_MASKED] = "masked"
-};
-
-DEFINE_STRING_TABLE_LOOKUP(unit_load_state, UnitLoadState);
-
-bool unit_name_is_valid(const char *n, enum template_valid template_ok) {
+bool unit_name_is_valid(const char *n, UnitNameFlags flags) {
const char *e, *i, *at;
- /* Valid formats:
- *
- * string@instance.suffix
- * string.suffix
- */
+ assert((flags & ~(UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE)) == 0);
- assert(IN_SET(template_ok, TEMPLATE_VALID, TEMPLATE_INVALID));
+ if (_unlikely_(flags == 0))
+ return false;
if (isempty(n))
return false;
@@ -96,15 +63,32 @@ bool unit_name_is_valid(const char *n, enum template_valid template_ok) {
return false;
}
- if (at) {
- if (at == n)
- return false;
+ if (at == n)
+ return false;
- if (!template_ok == TEMPLATE_VALID && at+1 == e)
- return false;
- }
+ if (flags & UNIT_NAME_PLAIN)
+ if (!at)
+ return true;
- return true;
+ if (flags & UNIT_NAME_INSTANCE)
+ if (at && e > at + 1)
+ return true;
+
+ if (flags & UNIT_NAME_TEMPLATE)
+ if (at && e == at + 1)
+ return true;
+
+ return false;
+}
+
+bool unit_prefix_is_valid(const char *p) {
+
+ /* We don't allow additional @ in the prefix string */
+
+ if (isempty(p))
+ return false;
+
+ return in_charset(p, VALID_CHARS);
}
bool unit_instance_is_valid(const char *i) {
@@ -121,14 +105,41 @@ bool unit_instance_is_valid(const char *i) {
return in_charset(i, "@" VALID_CHARS);
}
-bool unit_prefix_is_valid(const char *p) {
+bool unit_suffix_is_valid(const char *s) {
+ if (isempty(s))
+ return false;
- /* We don't allow additional @ in the instance string */
+ if (s[0] != '.')
+ return false;
- if (isempty(p))
+ if (unit_type_from_string(s + 1) < 0)
return false;
- return in_charset(p, VALID_CHARS);
+ return true;
+}
+
+int unit_name_to_prefix(const char *n, char **ret) {
+ const char *p;
+ char *s;
+
+ assert(n);
+ assert(ret);
+
+ if (!unit_name_is_valid(n, UNIT_NAME_ANY))
+ return -EINVAL;
+
+ p = strchr(n, '@');
+ if (!p)
+ p = strrchr(n, '.');
+
+ assert_se(p);
+
+ s = strndup(n, p - n);
+ if (!s)
+ return -ENOMEM;
+
+ *ret = s;
+ return 0;
}
int unit_name_to_instance(const char *n, char **instance) {
@@ -138,6 +149,9 @@ int unit_name_to_instance(const char *n, char **instance) {
assert(n);
assert(instance);
+ if (!unit_name_is_valid(n, UNIT_NAME_ANY))
+ return -EINVAL;
+
/* Everything past the first @ and before the last . is the instance */
p = strchr(n, '@');
if (!p) {
@@ -145,13 +159,13 @@ int unit_name_to_instance(const char *n, char **instance) {
return 0;
}
- d = strrchr(n, '.');
+ p++;
+
+ d = strrchr(p, '.');
if (!d)
return -EINVAL;
- if (d < p)
- return -EINVAL;
- i = strndup(p+1, d-p-1);
+ i = strndup(p, d-p);
if (!i)
return -ENOMEM;
@@ -159,55 +173,95 @@ int unit_name_to_instance(const char *n, char **instance) {
return 1;
}
-char *unit_name_to_prefix_and_instance(const char *n) {
+int unit_name_to_prefix_and_instance(const char *n, char **ret) {
const char *d;
+ char *s;
assert(n);
+ assert(ret);
- assert_se(d = strrchr(n, '.'));
- return strndup(n, d - n);
+ if (!unit_name_is_valid(n, UNIT_NAME_ANY))
+ return -EINVAL;
+
+ d = strrchr(n, '.');
+ if (!d)
+ return -EINVAL;
+
+ s = strndup(n, d - n);
+ if (!s)
+ return -ENOMEM;
+
+ *ret = s;
+ return 0;
}
-char *unit_name_to_prefix(const char *n) {
- const char *p;
+UnitType unit_name_to_type(const char *n) {
+ const char *e;
assert(n);
- p = strchr(n, '@');
- if (p)
- return strndup(n, p - n);
+ if (!unit_name_is_valid(n, UNIT_NAME_ANY))
+ return _UNIT_TYPE_INVALID;
+
+ assert_se(e = strrchr(n, '.'));
- return unit_name_to_prefix_and_instance(n);
+ return unit_type_from_string(e + 1);
}
-char *unit_name_change_suffix(const char *n, const char *suffix) {
- char *e, *r;
+int unit_name_change_suffix(const char *n, const char *suffix, char **ret) {
+ char *e, *s;
size_t a, b;
assert(n);
assert(suffix);
- assert(suffix[0] == '.');
+ assert(ret);
+
+ if (!unit_name_is_valid(n, UNIT_NAME_ANY))
+ return -EINVAL;
+
+ if (!unit_suffix_is_valid(suffix))
+ return -EINVAL;
assert_se(e = strrchr(n, '.'));
+
a = e - n;
b = strlen(suffix);
- r = new(char, a + b + 1);
- if (!r)
- return NULL;
+ s = new(char, a + b + 1);
+ if (!s)
+ return -ENOMEM;
- strcpy(mempcpy(r, n, a), suffix);
- return r;
+ strcpy(mempcpy(s, n, a), suffix);
+ *ret = s;
+
+ return 0;
}
-char *unit_name_build(const char *prefix, const char *instance, const char *suffix) {
+int unit_name_build(const char *prefix, const char *instance, const char *suffix, char **ret) {
+ char *s;
+
assert(prefix);
assert(suffix);
+ assert(ret);
+
+ if (!unit_prefix_is_valid(prefix))
+ return -EINVAL;
+
+ if (instance && !unit_instance_is_valid(instance))
+ return -EINVAL;
+
+ if (!unit_suffix_is_valid(suffix))
+ return -EINVAL;
if (!instance)
- return strappend(prefix, suffix);
+ s = strappend(prefix, suffix);
+ else
+ s = strjoin(prefix, "@", instance, suffix, NULL);
+ if (!s)
+ return -ENOMEM;
- return strjoin(prefix, "@", instance, suffix, NULL);
+ *ret = s;
+ return 0;
}
static char *do_escape_char(char c, char *t) {
@@ -243,30 +297,6 @@ static char *do_escape(const char *f, char *t) {
return t;
}
-static char *do_escape_mangle(const char *f, enum unit_name_mangle allow_globs, char *t) {
- const char *valid_chars;
-
- assert(f);
- assert(IN_SET(allow_globs, MANGLE_GLOB, MANGLE_NOGLOB));
- assert(t);
-
- /* We'll only escape the obvious characters here, to play
- * safe. */
-
- valid_chars = allow_globs == MANGLE_GLOB ? "@" VALID_CHARS "[]!-*?" : "@" VALID_CHARS;
-
- for (; *f; f++) {
- if (*f == '/')
- *(t++) = '-';
- else if (!strchr(valid_chars, *f))
- t = do_escape_char(*f, t);
- else
- *(t++) = *f;
- }
-
- return t;
-}
-
char *unit_name_escape(const char *f) {
char *r, *t;
@@ -282,14 +312,15 @@ char *unit_name_escape(const char *f) {
return r;
}
-char *unit_name_unescape(const char *f) {
- char *r, *t;
+int unit_name_unescape(const char *f, char **ret) {
+ _cleanup_free_ char *r = NULL;
+ char *t;
assert(f);
r = strdup(f);
if (!r)
- return NULL;
+ return -ENOMEM;
for (t = r; *f; f++) {
if (*f == '-')
@@ -297,180 +328,234 @@ char *unit_name_unescape(const char *f) {
else if (*f == '\\') {
int a, b;
- if (f[1] != 'x' ||
- (a = unhexchar(f[2])) < 0 ||
- (b = unhexchar(f[3])) < 0) {
- /* Invalid escape code, let's take it literal then */
- *(t++) = '\\';
- } else {
- *(t++) = (char) ((a << 4) | b);
- f += 3;
- }
+ if (f[1] != 'x')
+ return -EINVAL;
+
+ a = unhexchar(f[2]);
+ if (a < 0)
+ return -EINVAL;
+
+ b = unhexchar(f[3]);
+ if (b < 0)
+ return -EINVAL;
+
+ *(t++) = (char) (((uint8_t) a << 4U) | (uint8_t) b);
+ f += 3;
} else
*(t++) = *f;
}
*t = 0;
- return r;
+ *ret = r;
+ r = NULL;
+
+ return 0;
}
-char *unit_name_path_escape(const char *f) {
- _cleanup_free_ char *p = NULL;
+int unit_name_path_escape(const char *f, char **ret) {
+ char *p, *s;
assert(f);
+ assert(ret);
- p = strdup(f);
+ p = strdupa(f);
if (!p)
- return NULL;
+ return -ENOMEM;
path_kill_slashes(p);
if (STR_IN_SET(p, "/", ""))
- return strdup("-");
-
- return unit_name_escape(p[0] == '/' ? p + 1 : p);
-}
+ s = strdup("-");
+ else {
+ char *e;
-char *unit_name_path_unescape(const char *f) {
- char *e, *w;
+ if (!path_is_safe(p))
+ return -EINVAL;
- assert(f);
+ /* Truncate trailing slashes */
+ e = endswith(p, "/");
+ if (e)
+ *e = 0;
- e = unit_name_unescape(f);
- if (!e)
- return NULL;
+ /* Truncate leading slashes */
+ if (p[0] == '/')
+ p++;
- if (e[0] != '/') {
- w = strappend("/", e);
- free(e);
- return w;
+ s = unit_name_escape(p);
}
+ if (!s)
+ return -ENOMEM;
- return e;
+ *ret = s;
+ return 0;
}
-bool unit_name_is_template(const char *n) {
- const char *p, *e;
+int unit_name_path_unescape(const char *f, char **ret) {
+ char *s;
+ int r;
- assert(n);
+ assert(f);
- p = strchr(n, '@');
- if (!p)
- return false;
+ if (isempty(f))
+ return -EINVAL;
- e = strrchr(p+1, '.');
- if (!e)
- return false;
+ if (streq(f, "-")) {
+ s = strdup("/");
+ if (!s)
+ return -ENOMEM;
+ } else {
+ char *w;
- return e == p + 1;
-}
+ r = unit_name_unescape(f, &w);
+ if (r < 0)
+ return r;
-bool unit_name_is_instance(const char *n) {
- const char *p, *e;
+ /* Don't accept trailing or leading slashes */
+ if (startswith(w, "/") || endswith(w, "/")) {
+ free(w);
+ return -EINVAL;
+ }
- assert(n);
+ /* Prefix a slash again */
+ s = strappend("/", w);
+ free(w);
+ if (!s)
+ return -ENOMEM;
- p = strchr(n, '@');
- if (!p)
- return false;
+ if (!path_is_safe(s)) {
+ free(s);
+ return -EINVAL;
+ }
+ }
- e = strrchr(p+1, '.');
- if (!e)
- return false;
+ if (ret)
+ *ret = s;
+ else
+ free(s);
- return e > p + 1;
+ return 0;
}
-char *unit_name_replace_instance(const char *f, const char *i) {
+int unit_name_replace_instance(const char *f, const char *i, char **ret) {
const char *p, *e;
- char *r;
+ char *s;
size_t a, b;
assert(f);
assert(i);
+ assert(ret);
- p = strchr(f, '@');
- if (!p)
- return strdup(f);
+ if (!unit_name_is_valid(f, UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE))
+ return -EINVAL;
+ if (!unit_instance_is_valid(i))
+ return -EINVAL;
- e = strrchr(f, '.');
- if (!e)
- e = strchr(f, 0);
+ assert_se(p = strchr(f, '@'));
+ assert_se(e = strrchr(f, '.'));
a = p - f;
b = strlen(i);
- r = new(char, a + 1 + b + strlen(e) + 1);
- if (!r)
- return NULL;
+ s = new(char, a + 1 + b + strlen(e) + 1);
+ if (!s)
+ return -ENOMEM;
- strcpy(mempcpy(mempcpy(r, f, a + 1), i, b), e);
- return r;
+ strcpy(mempcpy(mempcpy(s, f, a + 1), i, b), e);
+
+ *ret = s;
+ return 0;
}
-char *unit_name_template(const char *f) {
+int unit_name_template(const char *f, char **ret) {
const char *p, *e;
- char *r;
+ char *s;
size_t a;
assert(f);
+ assert(ret);
- p = strchr(f, '@');
- if (!p)
- return strdup(f);
+ if (!unit_name_is_valid(f, UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE))
+ return -EINVAL;
- e = strrchr(f, '.');
- if (!e)
- e = strchr(f, 0);
+ assert_se(p = strchr(f, '@'));
+ assert_se(e = strrchr(f, '.'));
a = p - f;
- r = new(char, a + 1 + strlen(e) + 1);
- if (!r)
- return NULL;
+ s = new(char, a + 1 + strlen(e) + 1);
+ if (!s)
+ return -ENOMEM;
- strcpy(mempcpy(r, f, a + 1), e);
- return r;
+ strcpy(mempcpy(s, f, a + 1), e);
+
+ *ret = s;
+ return 0;
}
-char *unit_name_from_path(const char *path, const char *suffix) {
+int unit_name_from_path(const char *path, const char *suffix, char **ret) {
_cleanup_free_ char *p = NULL;
+ char *s = NULL;
+ int r;
assert(path);
assert(suffix);
+ assert(ret);
- p = unit_name_path_escape(path);
- if (!p)
- return NULL;
+ if (!unit_suffix_is_valid(suffix))
+ return -EINVAL;
+
+ r = unit_name_path_escape(path, &p);
+ if (r < 0)
+ return r;
- return strappend(p, suffix);
+ s = strappend(p, suffix);
+ if (!s)
+ return -ENOMEM;
+
+ *ret = s;
+ return 0;
}
-char *unit_name_from_path_instance(const char *prefix, const char *path, const char *suffix) {
+int unit_name_from_path_instance(const char *prefix, const char *path, const char *suffix, char **ret) {
_cleanup_free_ char *p = NULL;
+ char *s;
+ int r;
assert(prefix);
assert(path);
assert(suffix);
+ assert(ret);
- p = unit_name_path_escape(path);
- if (!p)
- return NULL;
+ if (!unit_prefix_is_valid(prefix))
+ return -EINVAL;
+
+ if (!unit_suffix_is_valid(suffix))
+ return -EINVAL;
+
+ r = unit_name_path_escape(path, &p);
+ if (r < 0)
+ return r;
+
+ s = strjoin(prefix, "@", p, suffix, NULL);
+ if (!s)
+ return -ENOMEM;
- return strjoin(prefix, "@", p, suffix, NULL);
+ *ret = s;
+ return 0;
}
-char *unit_name_to_path(const char *name) {
- _cleanup_free_ char *w = NULL;
+int unit_name_to_path(const char *name, char **ret) {
+ _cleanup_free_ char *prefix = NULL;
+ int r;
assert(name);
- w = unit_name_to_prefix(name);
- if (!w)
- return NULL;
+ r = unit_name_to_prefix(name, &prefix);
+ if (r < 0)
+ return r;
- return unit_name_path_unescape(w);
+ return unit_name_path_unescape(prefix, ret);
}
char *unit_dbus_path_from_name(const char *name) {
@@ -501,6 +586,30 @@ int unit_name_from_dbus_path(const char *path, char **name) {
return 0;
}
+static char *do_escape_mangle(const char *f, UnitNameMangle allow_globs, char *t) {
+ const char *valid_chars;
+
+ assert(f);
+ assert(IN_SET(allow_globs, UNIT_NAME_GLOB, UNIT_NAME_NOGLOB));
+ assert(t);
+
+ /* We'll only escape the obvious characters here, to play
+ * safe. */
+
+ valid_chars = allow_globs == UNIT_NAME_GLOB ? "@" VALID_CHARS "[]!-*?" : "@" VALID_CHARS;
+
+ for (; *f; f++) {
+ if (*f == '/')
+ *(t++) = '-';
+ else if (!strchr(valid_chars, *f))
+ t = do_escape_char(*f, t);
+ else
+ *(t++) = *f;
+ }
+
+ return t;
+}
+
/**
* Convert a string to a unit name. /dev/blah is converted to dev-blah.device,
* /blah/blah is converted to blah-blah.mount, anything else is left alone,
@@ -508,72 +617,191 @@ int unit_name_from_dbus_path(const char *path, char **name) {
*
* If @allow_globs, globs characters are preserved. Otherwise they are escaped.
*/
-char *unit_name_mangle_with_suffix(const char *name, enum unit_name_mangle allow_globs, const char *suffix) {
- char *r, *t;
+int unit_name_mangle_with_suffix(const char *name, UnitNameMangle allow_globs, const char *suffix, char **ret) {
+ char *s, *t;
+ int r;
assert(name);
assert(suffix);
- assert(suffix[0] == '.');
+ assert(ret);
+
+ if (isempty(name)) /* We cannot mangle empty unit names to become valid, sorry. */
+ return -EINVAL;
- if (is_device_path(name))
- return unit_name_from_path(name, ".device");
+ if (!unit_suffix_is_valid(suffix))
+ return -EINVAL;
- if (path_is_absolute(name))
- return unit_name_from_path(name, ".mount");
+ if (unit_name_is_valid(name, UNIT_NAME_ANY)) {
+ /* No mangling necessary... */
+ s = strdup(name);
+ if (!s)
+ return -ENOMEM;
- r = new(char, strlen(name) * 4 + strlen(suffix) + 1);
- if (!r)
- return NULL;
+ *ret = s;
+ return 0;
+ }
- t = do_escape_mangle(name, allow_globs, r);
+ if (is_device_path(name)) {
+ r = unit_name_from_path(name, ".device", ret);
+ if (r >= 0)
+ return 1;
+ if (r != -EINVAL)
+ return r;
+ }
+
+ if (path_is_absolute(name)) {
+ r = unit_name_from_path(name, ".mount", ret);
+ if (r >= 0)
+ return 1;
+ if (r != -EINVAL)
+ return r;
+ }
- if (unit_name_to_type(name) < 0)
+ s = new(char, strlen(name) * 4 + strlen(suffix) + 1);
+ if (!s)
+ return -ENOMEM;
+
+ t = do_escape_mangle(name, allow_globs, s);
+ *t = 0;
+
+ if (unit_name_to_type(s) < 0)
strcpy(t, suffix);
- else
- *t = 0;
- return r;
+ *ret = s;
+ return 1;
}
-UnitType unit_name_to_type(const char *n) {
- const char *e;
+int slice_build_parent_slice(const char *slice, char **ret) {
+ char *s, *dash;
- assert(n);
+ assert(slice);
+ assert(ret);
- e = strrchr(n, '.');
- if (!e)
- return _UNIT_TYPE_INVALID;
+ if (!slice_name_is_valid(slice))
+ return -EINVAL;
- return unit_type_from_string(e + 1);
+ if (streq(slice, "-.slice")) {
+ *ret = NULL;
+ return 0;
+ }
+
+ s = strdup(slice);
+ if (!s)
+ return -ENOMEM;
+
+ dash = strrchr(s, '-');
+ if (dash)
+ strcpy(dash, ".slice");
+ else {
+ free(s);
+
+ s = strdup("-.slice");
+ if (!s)
+ return -ENOMEM;
+ }
+
+ *ret = s;
+ return 1;
}
-int build_subslice(const char *slice, const char*name, char **subslice) {
- char *ret;
+int slice_build_subslice(const char *slice, const char*name, char **ret) {
+ char *subslice;
assert(slice);
assert(name);
- assert(subslice);
+ assert(ret);
+
+ if (!slice_name_is_valid(slice))
+ return -EINVAL;
+
+ if (!unit_prefix_is_valid(name))
+ return -EINVAL;
if (streq(slice, "-.slice"))
- ret = strappend(name, ".slice");
+ subslice = strappend(name, ".slice");
else {
char *e;
- e = endswith(slice, ".slice");
- if (!e)
- return -EINVAL;
+ assert_se(e = endswith(slice, ".slice"));
- ret = new(char, (e - slice) + 1 + strlen(name) + 6 + 1);
- if (!ret)
+ subslice = new(char, (e - slice) + 1 + strlen(name) + 6 + 1);
+ if (!subslice)
return -ENOMEM;
- stpcpy(stpcpy(stpcpy(mempcpy(ret, slice, e - slice), "-"), name), ".slice");
+ stpcpy(stpcpy(stpcpy(mempcpy(subslice, slice, e - slice), "-"), name), ".slice");
}
- *subslice = ret;
+ *ret = subslice;
return 0;
}
+bool slice_name_is_valid(const char *name) {
+ const char *p, *e;
+ bool dash = false;
+
+ if (!unit_name_is_valid(name, UNIT_NAME_PLAIN))
+ return false;
+
+ if (streq(name, "-.slice"))
+ return true;
+
+ e = endswith(name, ".slice");
+ if (!e)
+ return false;
+
+ for (p = name; p < e; p++) {
+
+ if (*p == '-') {
+
+ /* Don't allow initial dash */
+ if (p == name)
+ return false;
+
+ /* Don't allow multiple dashes */
+ if (dash)
+ return false;
+
+ dash = true;
+ } else
+ dash = false;
+ }
+
+ /* Don't allow trailing hash */
+ if (dash)
+ return false;
+
+ return true;
+}
+
+static const char* const unit_type_table[_UNIT_TYPE_MAX] = {
+ [UNIT_SERVICE] = "service",
+ [UNIT_SOCKET] = "socket",
+ [UNIT_BUSNAME] = "busname",
+ [UNIT_TARGET] = "target",
+ [UNIT_SNAPSHOT] = "snapshot",
+ [UNIT_DEVICE] = "device",
+ [UNIT_MOUNT] = "mount",
+ [UNIT_AUTOMOUNT] = "automount",
+ [UNIT_SWAP] = "swap",
+ [UNIT_TIMER] = "timer",
+ [UNIT_PATH] = "path",
+ [UNIT_SLICE] = "slice",
+ [UNIT_SCOPE] = "scope"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(unit_type, UnitType);
+
+static const char* const unit_load_state_table[_UNIT_LOAD_STATE_MAX] = {
+ [UNIT_STUB] = "stub",
+ [UNIT_LOADED] = "loaded",
+ [UNIT_NOT_FOUND] = "not-found",
+ [UNIT_ERROR] = "error",
+ [UNIT_MERGED] = "merged",
+ [UNIT_MASKED] = "masked"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(unit_load_state, UnitLoadState);
+
static const char* const unit_dependency_table[_UNIT_DEPENDENCY_MAX] = {
[UNIT_REQUIRES] = "Requires",
[UNIT_REQUIRES_OVERRIDABLE] = "RequiresOverridable",
@@ -584,6 +812,8 @@ static const char* const unit_dependency_table[_UNIT_DEPENDENCY_MAX] = {
[UNIT_PART_OF] = "PartOf",
[UNIT_REQUIRED_BY] = "RequiredBy",
[UNIT_REQUIRED_BY_OVERRIDABLE] = "RequiredByOverridable",
+ [UNIT_REQUISITE_OF] = "RequisiteOf",
+ [UNIT_REQUISITE_OF_OVERRIDABLE] = "RequisiteOfOverridable",
[UNIT_WANTED_BY] = "WantedBy",
[UNIT_BOUND_BY] = "BoundBy",
[UNIT_CONSISTS_OF] = "ConsistsOf",
diff --git a/src/shared/unit-name.h b/src/shared/unit-name.h
index 6f139cc4c4..b2043d0870 100644
--- a/src/shared/unit-name.h
+++ b/src/shared/unit-name.h
@@ -71,8 +71,10 @@ enum UnitDependency {
UNIT_PART_OF,
/* Inverse of the above */
- UNIT_REQUIRED_BY, /* inverse of 'requires' and 'requisite' is 'required_by' */
- UNIT_REQUIRED_BY_OVERRIDABLE, /* inverse of 'requires_overridable' and 'requisite_overridable' is 'soft_required_by' */
+ UNIT_REQUIRED_BY, /* inverse of 'requires' is 'required_by' */
+ UNIT_REQUIRED_BY_OVERRIDABLE, /* inverse of 'requires_overridable' is 'required_by_overridable' */
+ UNIT_REQUISITE_OF, /* inverse of 'requisite' is 'requisite_of' */
+ UNIT_REQUISITE_OF_OVERRIDABLE,/* inverse of 'requisite_overridable' is 'requisite_of_overridable' */
UNIT_WANTED_BY, /* inverse of 'wants' */
UNIT_BOUND_BY, /* inverse of 'binds_to' */
UNIT_CONSISTS_OF, /* inverse of 'part_of' */
@@ -107,61 +109,69 @@ enum UnitDependency {
_UNIT_DEPENDENCY_INVALID = -1
};
-const char *unit_type_to_string(UnitType i) _const_;
-UnitType unit_type_from_string(const char *s) _pure_;
-
-const char *unit_load_state_to_string(UnitLoadState i) _const_;
-UnitLoadState unit_load_state_from_string(const char *s) _pure_;
+typedef enum UnitNameFlags {
+ UNIT_NAME_PLAIN = 1, /* Allow foo.service */
+ UNIT_NAME_INSTANCE = 2, /* Allow foo@bar.service */
+ UNIT_NAME_TEMPLATE = 4, /* Allow foo@.service */
+ UNIT_NAME_ANY = UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE,
+} UnitNameFlags;
-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);
-
-enum template_valid {
- TEMPLATE_INVALID,
- TEMPLATE_VALID,
-};
-
-bool unit_name_is_valid(const char *n, enum template_valid template_ok) _pure_;
+bool unit_name_is_valid(const char *n, UnitNameFlags flags) _pure_;
bool unit_prefix_is_valid(const char *p) _pure_;
bool unit_instance_is_valid(const char *i) _pure_;
+bool unit_suffix_is_valid(const char *s) _pure_;
+
+static inline int unit_prefix_and_instance_is_valid(const char *p) {
+ /* For prefix+instance and instance the same rules apply */
+ return unit_instance_is_valid(p);
+}
+
+int unit_name_to_prefix(const char *n, char **prefix);
+int unit_name_to_instance(const char *n, char **instance);
+int unit_name_to_prefix_and_instance(const char *n, char **ret);
UnitType unit_name_to_type(const char *n) _pure_;
-char *unit_name_change_suffix(const char *n, const char *suffix);
+int unit_name_change_suffix(const char *n, const char *suffix, char **ret);
-char *unit_name_build(const char *prefix, const char *instance, const char *suffix);
+int unit_name_build(const char *prefix, const char *instance, const char *suffix, char **ret);
char *unit_name_escape(const char *f);
-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) _pure_;
-bool unit_name_is_instance(const char *n) _pure_;
+int unit_name_unescape(const char *f, char **ret);
+int unit_name_path_escape(const char *f, char **ret);
+int unit_name_path_unescape(const char *f, char **ret);
-char *unit_name_replace_instance(const char *f, const char *i);
+int unit_name_replace_instance(const char *f, const char *i, char **ret);
-char *unit_name_template(const char *f);
+int unit_name_template(const char *f, char **ret);
-char *unit_name_from_path(const char *path, const char *suffix);
-char *unit_name_from_path_instance(const char *prefix, const char *path, const char *suffix);
-char *unit_name_to_path(const char *name);
+int unit_name_from_path(const char *path, const char *suffix, char **ret);
+int unit_name_from_path_instance(const char *prefix, const char *path, const char *suffix, char **ret);
+int unit_name_to_path(const char *name, char **ret);
char *unit_dbus_path_from_name(const char *name);
int unit_name_from_dbus_path(const char *path, char **name);
-enum unit_name_mangle {
- MANGLE_NOGLOB,
- MANGLE_GLOB,
-};
+typedef enum UnitNameMangle {
+ UNIT_NAME_NOGLOB,
+ UNIT_NAME_GLOB,
+} UnitNameMangle;
+
+int unit_name_mangle_with_suffix(const char *name, UnitNameMangle allow_globs, const char *suffix, char **ret);
-char *unit_name_mangle_with_suffix(const char *name, enum unit_name_mangle allow_globs, const char *suffix);
-static inline char *unit_name_mangle(const char *name, enum unit_name_mangle allow_globs) {
- return unit_name_mangle_with_suffix(name, allow_globs, ".service");
+static inline int unit_name_mangle(const char *name, UnitNameMangle allow_globs, char **ret) {
+ return unit_name_mangle_with_suffix(name, allow_globs, ".service", ret);
}
-int build_subslice(const char *slice, const char*name, char **subslice);
+int slice_build_parent_slice(const char *slice, char **ret);
+int slice_build_subslice(const char *slice, const char*name, char **subslice);
+bool slice_name_is_valid(const char *name);
+
+const char *unit_type_to_string(UnitType i) _const_;
+UnitType unit_type_from_string(const char *s) _pure_;
+
+const char *unit_load_state_to_string(UnitLoadState i) _const_;
+UnitLoadState unit_load_state_from_string(const char *s) _pure_;
const char *unit_dependency_to_string(UnitDependency i) _const_;
UnitDependency unit_dependency_from_string(const char *s) _pure_;
diff --git a/src/shared/utf8.c b/src/shared/utf8.c
index 013c110f07..800884ffee 100644
--- a/src/shared/utf8.c
+++ b/src/shared/utf8.c
@@ -52,7 +52,7 @@
#include "utf8.h"
#include "util.h"
-static inline bool is_unicode_valid(uint32_t ch) {
+bool unichar_is_valid(uint32_t ch) {
if (ch >= 0x110000) /* End of unicode space */
return false;
@@ -66,7 +66,7 @@ static inline bool is_unicode_valid(uint32_t ch) {
return true;
}
-static bool is_unicode_control(uint32_t ch) {
+static bool unichar_is_control(uint32_t ch) {
/*
0 to ' '-1 is the C0 range.
@@ -156,7 +156,7 @@ bool utf8_is_printable_newline(const char* str, size_t length, bool newline) {
val = utf8_encoded_to_unichar(p);
if (val < 0 ||
- is_unicode_control(val) ||
+ unichar_is_control(val) ||
(!newline && val == '\n'))
return false;
@@ -276,6 +276,7 @@ char *ascii_is_valid(const char *str) {
* occupy.
*/
size_t utf8_encode_unichar(char *out_utf8, uint32_t g) {
+
if (g < (1 << 7)) {
if (out_utf8)
out_utf8[0] = g & 0x7f;
@@ -301,9 +302,9 @@ size_t utf8_encode_unichar(char *out_utf8, uint32_t g) {
out_utf8[3] = 0x80 | (g & 0x3f);
}
return 4;
- } else {
- return 0;
}
+
+ return 0;
}
char *utf16_to_utf8(const void *s, size_t length) {
@@ -394,7 +395,7 @@ int utf8_encoded_valid_unichar(const char *str) {
return -EINVAL;
/* check if value has valid range */
- if (!is_unicode_valid(unichar))
+ if (!unichar_is_valid(unichar))
return -EINVAL;
return len;
diff --git a/src/shared/utf8.h b/src/shared/utf8.h
index 77f663438e..e745649f06 100644
--- a/src/shared/utf8.h
+++ b/src/shared/utf8.h
@@ -27,6 +27,8 @@
#define UTF8_REPLACEMENT_CHARACTER "\xef\xbf\xbd"
+bool unichar_is_valid(uint32_t c);
+
const char *utf8_is_valid(const char *s) _pure_;
char *ascii_is_valid(const char *s) _pure_;
diff --git a/src/shared/util.c b/src/shared/util.c
index ba035caed0..311acbb349 100644
--- a/src/shared/util.c
+++ b/src/shared/util.c
@@ -19,12 +19,12 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <assert.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#include <signal.h>
+#include <libintl.h>
#include <stdio.h>
#include <syslog.h>
#include <sched.h>
@@ -35,9 +35,6 @@
#include <fcntl.h>
#include <dirent.h>
#include <sys/ioctl.h>
-#include <linux/vt.h>
-#include <linux/tiocl.h>
-#include <termios.h>
#include <stdarg.h>
#include <poll.h>
#include <ctype.h>
@@ -45,8 +42,6 @@
#include <sys/utsname.h>
#include <pwd.h>
#include <netinet/ip.h>
-#include <linux/kd.h>
-#include <dlfcn.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <glob.h>
@@ -74,13 +69,13 @@
#include <sys/auxv.h>
#endif
+#include "config.h"
#include "macro.h"
#include "util.h"
#include "ioprio.h"
#include "missing.h"
#include "log.h"
#include "strv.h"
-#include "label.h"
#include "mkdir.h"
#include "path-util.h"
#include "exit-status.h"
@@ -93,13 +88,19 @@
#include "virt.h"
#include "def.h"
#include "sparse-endian.h"
+#include "formats-util.h"
+#include "process-util.h"
+#include "random-util.h"
+#include "terminal-util.h"
+#include "hostname-util.h"
+#include "signal-util.h"
+
+/* Put this test here for a lack of better place */
+assert_cc(EAGAIN == EWOULDBLOCK);
int saved_argc = 0;
char **saved_argv = NULL;
-static volatile unsigned cached_columns = 0;
-static volatile unsigned cached_lines = 0;
-
size_t page_size(void) {
static thread_local size_t pgsz = 0;
long r;
@@ -148,6 +149,27 @@ char* endswith(const char *s, const char *postfix) {
return (char*) s + sl - pl;
}
+char* endswith_no_case(const char *s, const char *postfix) {
+ size_t sl, pl;
+
+ assert(s);
+ assert(postfix);
+
+ sl = strlen(s);
+ pl = strlen(postfix);
+
+ if (pl == 0)
+ return (char*) s + sl;
+
+ if (sl < pl)
+ return NULL;
+
+ if (strcasecmp(s + sl - pl, postfix) != 0)
+ return NULL;
+
+ return (char*) s + sl - pl;
+}
+
char* first_word(const char *s, const char *word) {
size_t sl, wl;
const char *p;
@@ -182,7 +204,7 @@ char* first_word(const char *s, const char *word) {
return (char*) p;
}
-static size_t cescape_char(char c, char *buf) {
+size_t cescape_char(char c, char *buf) {
char * buf_old = buf;
switch (c) {
@@ -351,7 +373,6 @@ int parse_uid(const char *s, uid_t* ret_uid) {
int r;
assert(s);
- assert(ret_uid);
r = safe_atolu(s, &ul);
if (r < 0)
@@ -370,7 +391,9 @@ int parse_uid(const char *s, uid_t* ret_uid) {
if (uid == (uid_t) 0xFFFF)
return -ENXIO;
- *ret_uid = uid;
+ if (ret_uid)
+ *ret_uid = uid;
+
return 0;
}
@@ -571,13 +594,12 @@ const char* split(const char **state, size_t *l, const char *separator, bool quo
char quotechars[2] = {*current, '\0'};
*l = strcspn_escaped(current + 1, quotechars);
- if (current[*l + 1] == '\0' ||
+ if (current[*l + 1] == '\0' || current[*l + 1] != quotechars[0] ||
(current[*l + 2] && !strchr(separator, current[*l + 2]))) {
/* right quote missing or garbage at the end */
*state = current;
return NULL;
}
- assert(current[*l + 1] == quotechars[0]);
*state = current++ + *l + 2;
} else if (quoted) {
*l = strcspn_escaped(current, separator);
@@ -595,49 +617,6 @@ const char* split(const char **state, size_t *l, const char *separator, bool quo
return current;
}
-int get_parent_of_pid(pid_t pid, pid_t *_ppid) {
- int r;
- _cleanup_free_ char *line = NULL;
- long unsigned ppid;
- const char *p;
-
- assert(pid >= 0);
- assert(_ppid);
-
- if (pid == 0) {
- *_ppid = getppid();
- return 0;
- }
-
- p = procfs_file_alloca(pid, "stat");
- r = read_one_line_file(p, &line);
- if (r < 0)
- return r;
-
- /* Let's skip the pid and comm fields. The latter is enclosed
- * in () but does not escape any () in its value, so let's
- * skip over it manually */
-
- p = strrchr(line, ')');
- if (!p)
- return -EIO;
-
- p++;
-
- if (sscanf(p, " "
- "%*c " /* state */
- "%lu ", /* ppid */
- &ppid) != 1)
- return -EIO;
-
- if ((long unsigned) (pid_t) ppid != ppid)
- return -ERANGE;
-
- *_ppid = (pid_t) ppid;
-
- return 0;
-}
-
int fchmod_umask(int fd, mode_t m) {
mode_t u;
int r;
@@ -656,308 +635,6 @@ char *truncate_nl(char *s) {
return s;
}
-int get_process_state(pid_t pid) {
- const char *p;
- char state;
- int r;
- _cleanup_free_ char *line = NULL;
-
- assert(pid >= 0);
-
- p = procfs_file_alloca(pid, "stat");
- r = read_one_line_file(p, &line);
- if (r < 0)
- return r;
-
- p = strrchr(line, ')');
- if (!p)
- return -EIO;
-
- p++;
-
- if (sscanf(p, " %c", &state) != 1)
- return -EIO;
-
- return (unsigned char) state;
-}
-
-int get_process_comm(pid_t pid, char **name) {
- const char *p;
- int r;
-
- assert(name);
- assert(pid >= 0);
-
- p = procfs_file_alloca(pid, "comm");
-
- r = read_one_line_file(p, name);
- if (r == -ENOENT)
- return -ESRCH;
-
- return r;
-}
-
-int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char **line) {
- _cleanup_fclose_ FILE *f = NULL;
- char *r = NULL, *k;
- const char *p;
- int c;
-
- assert(line);
- assert(pid >= 0);
-
- p = procfs_file_alloca(pid, "cmdline");
-
- f = fopen(p, "re");
- if (!f)
- return -errno;
-
- if (max_length == 0) {
- size_t len = 0, allocated = 0;
-
- while ((c = getc(f)) != EOF) {
-
- if (!GREEDY_REALLOC(r, allocated, len+2)) {
- free(r);
- return -ENOMEM;
- }
-
- r[len++] = isprint(c) ? c : ' ';
- }
-
- if (len > 0)
- r[len-1] = 0;
-
- } else {
- bool space = false;
- size_t left;
-
- r = new(char, max_length);
- if (!r)
- return -ENOMEM;
-
- k = r;
- left = max_length;
- while ((c = getc(f)) != EOF) {
-
- if (isprint(c)) {
- if (space) {
- if (left <= 4)
- break;
-
- *(k++) = ' ';
- left--;
- space = false;
- }
-
- if (left <= 4)
- break;
-
- *(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;
- }
-
- /* Kernel threads have no argv[] */
- if (isempty(r)) {
- _cleanup_free_ char *t = NULL;
- int h;
-
- free(r);
-
- if (!comm_fallback)
- return -ENOENT;
-
- h = get_process_comm(pid, &t);
- if (h < 0)
- return h;
-
- r = strjoin("[", t, "]", NULL);
- if (!r)
- return -ENOMEM;
- }
-
- *line = r;
- return 0;
-}
-
-int is_kernel_thread(pid_t pid) {
- const char *p;
- size_t count;
- char c;
- bool eof;
- FILE *f;
-
- if (pid == 0)
- return 0;
-
- assert(pid > 0);
-
- p = procfs_file_alloca(pid, "cmdline");
- f = fopen(p, "re");
- if (!f)
- return -errno;
-
- count = fread(&c, 1, 1, f);
- eof = feof(f);
- fclose(f);
-
- /* Kernel threads have an empty cmdline */
-
- if (count <= 0)
- return eof ? 1 : -errno;
-
- return 0;
-}
-
-int get_process_capeff(pid_t pid, char **capeff) {
- const char *p;
-
- assert(capeff);
- assert(pid >= 0);
-
- p = procfs_file_alloca(pid, "status");
-
- return get_status_field(p, "\nCapEff:", capeff);
-}
-
-static int get_process_link_contents(const char *proc_file, char **name) {
- int r;
-
- assert(proc_file);
- assert(name);
-
- r = readlink_malloc(proc_file, name);
- if (r < 0)
- return r == -ENOENT ? -ESRCH : r;
-
- return 0;
-}
-
-int get_process_exe(pid_t pid, char **name) {
- const char *p;
- char *d;
- int r;
-
- assert(pid >= 0);
-
- p = procfs_file_alloca(pid, "exe");
- r = get_process_link_contents(p, name);
- if (r < 0)
- return r;
-
- d = endswith(*name, " (deleted)");
- if (d)
- *d = '\0';
-
- return 0;
-}
-
-static int get_process_id(pid_t pid, const char *field, uid_t *uid) {
- _cleanup_fclose_ FILE *f = NULL;
- char line[LINE_MAX];
- const char *p;
-
- assert(field);
- assert(uid);
-
- if (pid == 0)
- return getuid();
-
- p = procfs_file_alloca(pid, "status");
- f = fopen(p, "re");
- if (!f)
- return -errno;
-
- FOREACH_LINE(line, f, return -errno) {
- char *l;
-
- l = strstrip(line);
-
- if (startswith(l, field)) {
- l += strlen(field);
- l += strspn(l, WHITESPACE);
-
- l[strcspn(l, WHITESPACE)] = 0;
-
- return parse_uid(l, uid);
- }
- }
-
- return -EIO;
-}
-
-int get_process_uid(pid_t pid, uid_t *uid) {
- return get_process_id(pid, "Uid:", 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);
-}
-
-int get_process_cwd(pid_t pid, char **cwd) {
- const char *p;
-
- assert(pid >= 0);
-
- p = procfs_file_alloca(pid, "cwd");
-
- return get_process_link_contents(p, cwd);
-}
-
-int get_process_root(pid_t pid, char **root) {
- const char *p;
-
- assert(pid >= 0);
-
- p = procfs_file_alloca(pid, "root");
-
- return get_process_link_contents(p, root);
-}
-
-int get_process_environ(pid_t pid, char **env) {
- _cleanup_fclose_ FILE *f = NULL;
- _cleanup_free_ char *outcome = NULL;
- int c;
- const char *p;
- size_t allocated = 0, sz = 0;
-
- assert(pid >= 0);
- assert(env);
-
- p = procfs_file_alloca(pid, "environ");
-
- f = fopen(p, "re");
- if (!f)
- return -errno;
-
- while ((c = fgetc(f)) != EOF) {
- if (!GREEDY_REALLOC(outcome, allocated, sz + 5))
- return -ENOMEM;
-
- if (c == '\0')
- outcome[sz++] = '\n';
- else
- sz += cescape_char(c, outcome + sz);
- }
-
- outcome[sz] = '\0';
- *env = outcome;
- outcome = NULL;
-
- return 0;
-}
-
char *strnappend(const char *s, const char *suffix, size_t b) {
size_t a;
char *r;
@@ -1095,41 +772,6 @@ int readlink_and_canonicalize(const char *p, char **r) {
return 0;
}
-int reset_all_signal_handlers(void) {
- int sig, r = 0;
-
- for (sig = 1; sig < _NSIG; sig++) {
- struct sigaction sa = {
- .sa_handler = SIG_DFL,
- .sa_flags = SA_RESTART,
- };
-
- /* These two cannot be caught... */
- if (sig == SIGKILL || sig == SIGSTOP)
- continue;
-
- /* On Linux the first two RT signals are reserved by
- * glibc, and sigaction() will return EINVAL for them. */
- if ((sigaction(sig, &sa, NULL) < 0))
- if (errno != EINVAL && r == 0)
- r = -errno;
- }
-
- return r;
-}
-
-int reset_signal_mask(void) {
- sigset_t ss;
-
- if (sigemptyset(&ss) < 0)
- return -errno;
-
- if (sigprocmask(SIG_SETMASK, &ss, NULL) < 0)
- return -errno;
-
- return 0;
-}
-
char *strstrip(char *s) {
char *e;
@@ -1330,7 +972,8 @@ char *cescape(const char *s) {
assert(s);
- /* Does C style string escaping. */
+ /* Does C style string escaping. May be reversed with
+ * cunescape(). */
r = new(char, strlen(s)*4 + 1);
if (!r)
@@ -1344,12 +987,214 @@ char *cescape(const char *s) {
return r;
}
-char *cunescape_length_with_prefix(const char *s, size_t length, const char *prefix) {
+static int cunescape_one(const char *p, size_t length, char *ret, uint32_t *ret_unicode) {
+ int r = 1;
+
+ assert(p);
+ assert(*p);
+ assert(ret);
+
+ /* Unescapes C style. Returns the unescaped character in ret,
+ * unless we encountered a \u sequence in which case the full
+ * unicode character is returned in ret_unicode, instead. */
+
+ if (length != (size_t) -1 && length < 1)
+ return -EINVAL;
+
+ switch (p[0]) {
+
+ case 'a':
+ *ret = '\a';
+ break;
+ case 'b':
+ *ret = '\b';
+ break;
+ case 'f':
+ *ret = '\f';
+ break;
+ case 'n':
+ *ret = '\n';
+ break;
+ case 'r':
+ *ret = '\r';
+ break;
+ case 't':
+ *ret = '\t';
+ break;
+ case 'v':
+ *ret = '\v';
+ break;
+ case '\\':
+ *ret = '\\';
+ break;
+ case '"':
+ *ret = '"';
+ break;
+ case '\'':
+ *ret = '\'';
+ break;
+
+ case 's':
+ /* This is an extension of the XDG syntax files */
+ *ret = ' ';
+ break;
+
+ case 'x': {
+ /* hexadecimal encoding */
+ int a, b;
+
+ if (length != (size_t) -1 && length < 3)
+ return -EINVAL;
+
+ a = unhexchar(p[1]);
+ if (a < 0)
+ return -EINVAL;
+
+ b = unhexchar(p[2]);
+ if (b < 0)
+ return -EINVAL;
+
+ /* Don't allow NUL bytes */
+ if (a == 0 && b == 0)
+ return -EINVAL;
+
+ *ret = (char) ((a << 4U) | b);
+ r = 3;
+ break;
+ }
+
+ case 'u': {
+ /* C++11 style 16bit unicode */
+
+ int a[4];
+ unsigned i;
+ uint32_t c;
+
+ if (length != (size_t) -1 && length < 5)
+ return -EINVAL;
+
+ for (i = 0; i < 4; i++) {
+ a[i] = unhexchar(p[1 + i]);
+ if (a[i] < 0)
+ return a[i];
+ }
+
+ c = ((uint32_t) a[0] << 12U) | ((uint32_t) a[1] << 8U) | ((uint32_t) a[2] << 4U) | (uint32_t) a[3];
+
+ /* Don't allow 0 chars */
+ if (c == 0)
+ return -EINVAL;
+
+ if (c < 128)
+ *ret = c;
+ else {
+ if (!ret_unicode)
+ return -EINVAL;
+
+ *ret = 0;
+ *ret_unicode = c;
+ }
+
+ r = 5;
+ break;
+ }
+
+ case 'U': {
+ /* C++11 style 32bit unicode */
+
+ int a[8];
+ unsigned i;
+ uint32_t c;
+
+ if (length != (size_t) -1 && length < 9)
+ return -EINVAL;
+
+ for (i = 0; i < 8; i++) {
+ a[i] = unhexchar(p[1 + i]);
+ if (a[i] < 0)
+ return a[i];
+ }
+
+ c = ((uint32_t) a[0] << 28U) | ((uint32_t) a[1] << 24U) | ((uint32_t) a[2] << 20U) | ((uint32_t) a[3] << 16U) |
+ ((uint32_t) a[4] << 12U) | ((uint32_t) a[5] << 8U) | ((uint32_t) a[6] << 4U) | (uint32_t) a[7];
+
+ /* Don't allow 0 chars */
+ if (c == 0)
+ return -EINVAL;
+
+ /* Don't allow invalid code points */
+ if (!unichar_is_valid(c))
+ return -EINVAL;
+
+ if (c < 128)
+ *ret = c;
+ else {
+ if (!ret_unicode)
+ return -EINVAL;
+
+ *ret = 0;
+ *ret_unicode = c;
+ }
+
+ r = 9;
+ break;
+ }
+
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7': {
+ /* octal encoding */
+ int a, b, c;
+ uint32_t m;
+
+ if (length != (size_t) -1 && length < 3)
+ return -EINVAL;
+
+ a = unoctchar(p[0]);
+ if (a < 0)
+ return -EINVAL;
+
+ b = unoctchar(p[1]);
+ if (b < 0)
+ return -EINVAL;
+
+ c = unoctchar(p[2]);
+ if (c < 0)
+ return -EINVAL;
+
+ /* don't allow NUL bytes */
+ if (a == 0 && b == 0 && c == 0)
+ return -EINVAL;
+
+ /* Don't allow bytes above 255 */
+ m = ((uint32_t) a << 6U) | ((uint32_t) b << 3U) | (uint32_t) c;
+ if (m > 255)
+ return -EINVAL;
+
+ *ret = m;
+ r = 3;
+ break;
+ }
+
+ default:
+ return -EINVAL;
+ }
+
+ return r;
+}
+
+int cunescape_length_with_prefix(const char *s, size_t length, const char *prefix, UnescapeFlags flags, char **ret) {
char *r, *t;
const char *f;
size_t pl;
assert(s);
+ assert(ret);
/* Undoes C style string escaping, and optionally prefixes it. */
@@ -1357,135 +1202,71 @@ char *cunescape_length_with_prefix(const char *s, size_t length, const char *pre
r = new(char, pl+length+1);
if (!r)
- return NULL;
+ return -ENOMEM;
if (prefix)
memcpy(r, prefix, pl);
for (f = s, t = r + pl; f < s + length; f++) {
- size_t remaining = s + length - f;
+ size_t remaining;
+ uint32_t u;
+ char c;
+ int k;
+
+ remaining = s + length - f;
assert(remaining > 0);
- if (*f != '\\') { /* a literal literal */
+ if (*f != '\\') {
+ /* A literal literal, copy verbatim */
*(t++) = *f;
continue;
}
- if (--remaining == 0) { /* copy trailing backslash verbatim */
- *(t++) = *f;
- break;
- }
-
- f++;
-
- switch (*f) {
-
- case 'a':
- *(t++) = '\a';
- break;
- case 'b':
- *(t++) = '\b';
- break;
- case 'f':
- *(t++) = '\f';
- break;
- case 'n':
- *(t++) = '\n';
- break;
- case 'r':
- *(t++) = '\r';
- break;
- case 't':
- *(t++) = '\t';
- break;
- case 'v':
- *(t++) = '\v';
- break;
- case '\\':
- *(t++) = '\\';
- break;
- case '"':
- *(t++) = '"';
- break;
- case '\'':
- *(t++) = '\'';
- break;
-
- case 's':
- /* This is an extension of the XDG syntax files */
- *(t++) = ' ';
- break;
-
- case 'x': {
- /* hexadecimal encoding */
- int a = -1, b = -1;
-
- if (remaining >= 2) {
- a = unhexchar(f[1]);
- b = unhexchar(f[2]);
- }
-
- if (a < 0 || b < 0 || (a == 0 && b == 0)) {
- /* Invalid escape code, let's take it literal then */
- *(t++) = '\\';
- *(t++) = 'x';
- } else {
- *(t++) = (char) ((a << 4) | b);
- f += 2;
+ if (remaining == 1) {
+ if (flags & UNESCAPE_RELAX) {
+ /* A trailing backslash, copy verbatim */
+ *(t++) = *f;
+ continue;
}
- break;
+ free(r);
+ return -EINVAL;
}
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7': {
- /* octal encoding */
- int a = -1, b = -1, c = -1;
-
- if (remaining >= 3) {
- a = unoctchar(f[0]);
- b = unoctchar(f[1]);
- c = unoctchar(f[2]);
- }
-
- if (a < 0 || b < 0 || c < 0 || (a == 0 && b == 0 && c == 0)) {
+ k = cunescape_one(f + 1, remaining - 1, &c, &u);
+ if (k < 0) {
+ if (flags & UNESCAPE_RELAX) {
/* Invalid escape code, let's take it literal then */
*(t++) = '\\';
- *(t++) = f[0];
- } else {
- *(t++) = (char) ((a << 6) | (b << 3) | c);
- f += 2;
+ continue;
}
- break;
+ free(r);
+ return k;
}
- default:
- /* Invalid escape code, let's take it literal then */
- *(t++) = '\\';
- *(t++) = *f;
- break;
- }
+ if (c != 0)
+ /* Non-Unicode? Let's encode this directly */
+ *(t++) = c;
+ else
+ /* Unicode? Then let's encode this in UTF-8 */
+ t += utf8_encode_unichar(t, u);
+
+ f += k;
}
*t = 0;
- return r;
-}
-char *cunescape_length(const char *s, size_t length) {
- return cunescape_length_with_prefix(s, length, NULL);
+ *ret = r;
+ return t - r;
}
-char *cunescape(const char *s) {
- assert(s);
+int cunescape_length(const char *s, size_t length, UnescapeFlags flags, char **ret) {
+ return cunescape_length_with_prefix(s, length, NULL, flags, ret);
+}
- return cunescape_length(s, strlen(s));
+int cunescape(const char *s, UnescapeFlags flags, char **ret) {
+ return cunescape_length(s, strlen(s), flags, ret);
}
char *xescape(const char *s, const char *bad) {
@@ -1494,7 +1275,7 @@ char *xescape(const char *s, const char *bad) {
/* Escapes all chars in bad, in addition to \ and all special
* chars, in \xFF style escaping. May be reversed with
- * cunescape. */
+ * cunescape(). */
r = new(char, strlen(s) * 4 + 1);
if (!r)
@@ -1689,6 +1470,7 @@ bool chars_intersect(const char *a, const char *b) {
bool fstype_is_network(const char *fstype) {
static const char table[] =
+ "afs\0"
"cifs\0"
"smbfs\0"
"sshfs\0"
@@ -1709,301 +1491,6 @@ bool fstype_is_network(const char *fstype) {
return nulstr_contains(table, fstype);
}
-int chvt(int vt) {
- _cleanup_close_ int fd;
-
- fd = open_terminal("/dev/tty0", O_RDWR|O_NOCTTY|O_CLOEXEC);
- if (fd < 0)
- return -errno;
-
- if (vt < 0) {
- int tiocl[2] = {
- TIOCL_GETKMSGREDIRECT,
- 0
- };
-
- if (ioctl(fd, TIOCLINUX, tiocl) < 0)
- return -errno;
-
- vt = tiocl[0] <= 0 ? 1 : tiocl[0];
- }
-
- if (ioctl(fd, VT_ACTIVATE, vt) < 0)
- return -errno;
-
- return 0;
-}
-
-int read_one_char(FILE *f, char *ret, usec_t t, bool *need_nl) {
- struct termios old_termios, new_termios;
- char c, line[LINE_MAX];
-
- assert(f);
- assert(ret);
-
- if (tcgetattr(fileno(f), &old_termios) >= 0) {
- new_termios = old_termios;
-
- new_termios.c_lflag &= ~ICANON;
- new_termios.c_cc[VMIN] = 1;
- new_termios.c_cc[VTIME] = 0;
-
- if (tcsetattr(fileno(f), TCSADRAIN, &new_termios) >= 0) {
- size_t k;
-
- if (t != USEC_INFINITY) {
- if (fd_wait_for_event(fileno(f), POLLIN, t) <= 0) {
- tcsetattr(fileno(f), TCSADRAIN, &old_termios);
- return -ETIMEDOUT;
- }
- }
-
- k = fread(&c, 1, 1, f);
-
- tcsetattr(fileno(f), TCSADRAIN, &old_termios);
-
- if (k <= 0)
- return -EIO;
-
- if (need_nl)
- *need_nl = c != '\n';
-
- *ret = c;
- return 0;
- }
- }
-
- if (t != USEC_INFINITY) {
- if (fd_wait_for_event(fileno(f), POLLIN, t) <= 0)
- return -ETIMEDOUT;
- }
-
- errno = 0;
- if (!fgets(line, sizeof(line), f))
- return errno ? -errno : -EIO;
-
- truncate_nl(line);
-
- if (strlen(line) != 1)
- return -EBADMSG;
-
- if (need_nl)
- *need_nl = false;
-
- *ret = line[0];
- return 0;
-}
-
-int ask_char(char *ret, const char *replies, const char *text, ...) {
- int r;
-
- assert(ret);
- assert(replies);
- assert(text);
-
- for (;;) {
- va_list ap;
- char c;
- bool need_nl = true;
-
- if (on_tty())
- fputs(ANSI_HIGHLIGHT_ON, stdout);
-
- va_start(ap, text);
- vprintf(text, ap);
- va_end(ap);
-
- if (on_tty())
- fputs(ANSI_HIGHLIGHT_OFF, stdout);
-
- fflush(stdout);
-
- r = read_one_char(stdin, &c, USEC_INFINITY, &need_nl);
- if (r < 0) {
-
- if (r == -EBADMSG) {
- puts("Bad input, please try again.");
- continue;
- }
-
- putchar('\n');
- return r;
- }
-
- if (need_nl)
- putchar('\n');
-
- if (strchr(replies, c)) {
- *ret = c;
- return 0;
- }
-
- puts("Read unexpected character, please try again.");
- }
-}
-
-int ask_string(char **ret, const char *text, ...) {
- assert(ret);
- assert(text);
-
- for (;;) {
- char line[LINE_MAX];
- va_list ap;
-
- if (on_tty())
- fputs(ANSI_HIGHLIGHT_ON, stdout);
-
- va_start(ap, text);
- vprintf(text, ap);
- va_end(ap);
-
- if (on_tty())
- fputs(ANSI_HIGHLIGHT_OFF, stdout);
-
- fflush(stdout);
-
- errno = 0;
- if (!fgets(line, sizeof(line), stdin))
- return errno ? -errno : -EIO;
-
- if (!endswith(line, "\n"))
- putchar('\n');
- else {
- char *s;
-
- if (isempty(line))
- continue;
-
- truncate_nl(line);
- s = strdup(line);
- if (!s)
- return -ENOMEM;
-
- *ret = s;
- return 0;
- }
- }
-}
-
-int reset_terminal_fd(int fd, bool switch_to_text) {
- struct termios termios;
- int r = 0;
-
- /* Set terminal to some sane defaults */
-
- assert(fd >= 0);
-
- /* We leave locked terminal attributes untouched, so that
- * Plymouth may set whatever it wants to set, and we don't
- * interfere with that. */
-
- /* Disable exclusive mode, just in case */
- ioctl(fd, TIOCNXCL);
-
- /* Switch to text mode */
- if (switch_to_text)
- ioctl(fd, KDSETMODE, KD_TEXT);
-
- /* Enable console unicode mode */
- ioctl(fd, KDSKBMODE, K_UNICODE);
-
- if (tcgetattr(fd, &termios) < 0) {
- r = -errno;
- goto finish;
- }
-
- /* We only reset the stuff that matters to the software. How
- * hardware is set up we don't touch assuming that somebody
- * else will do that for us */
-
- termios.c_iflag &= ~(IGNBRK | BRKINT | ISTRIP | INLCR | IGNCR | IUCLC);
- termios.c_iflag |= ICRNL | IMAXBEL | IUTF8;
- termios.c_oflag |= ONLCR;
- termios.c_cflag |= CREAD;
- termios.c_lflag = ISIG | ICANON | IEXTEN | ECHO | ECHOE | ECHOK | ECHOCTL | ECHOPRT | ECHOKE;
-
- termios.c_cc[VINTR] = 03; /* ^C */
- termios.c_cc[VQUIT] = 034; /* ^\ */
- termios.c_cc[VERASE] = 0177;
- termios.c_cc[VKILL] = 025; /* ^X */
- termios.c_cc[VEOF] = 04; /* ^D */
- termios.c_cc[VSTART] = 021; /* ^Q */
- termios.c_cc[VSTOP] = 023; /* ^S */
- termios.c_cc[VSUSP] = 032; /* ^Z */
- termios.c_cc[VLNEXT] = 026; /* ^V */
- termios.c_cc[VWERASE] = 027; /* ^W */
- termios.c_cc[VREPRINT] = 022; /* ^R */
- termios.c_cc[VEOL] = 0;
- termios.c_cc[VEOL2] = 0;
-
- termios.c_cc[VTIME] = 0;
- termios.c_cc[VMIN] = 1;
-
- if (tcsetattr(fd, TCSANOW, &termios) < 0)
- r = -errno;
-
-finish:
- /* Just in case, flush all crap out */
- tcflush(fd, TCIOFLUSH);
-
- return r;
-}
-
-int reset_terminal(const char *name) {
- _cleanup_close_ int fd = -1;
-
- fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC);
- if (fd < 0)
- return fd;
-
- return reset_terminal_fd(fd, true);
-}
-
-int open_terminal(const char *name, int mode) {
- int fd, r;
- unsigned c = 0;
-
- /*
- * If a TTY is in the process of being closed opening it might
- * cause EIO. This is horribly awful, but unlikely to be
- * changed in the kernel. Hence we work around this problem by
- * retrying a couple of times.
- *
- * https://bugs.launchpad.net/ubuntu/+source/linux/+bug/554172/comments/245
- */
-
- assert(!(mode & O_CREAT));
-
- for (;;) {
- fd = open(name, mode, 0);
- if (fd >= 0)
- break;
-
- if (errno != EIO)
- return -errno;
-
- /* Max 1s in total */
- if (c >= 20)
- return -errno;
-
- usleep(50 * USEC_PER_MSEC);
- c++;
- }
-
- r = isatty(fd);
- if (r < 0) {
- safe_close(fd);
- return -errno;
- }
-
- if (!r) {
- safe_close(fd);
- return -ENOTTY;
- }
-
- return fd;
-}
-
int flush_fd(int fd) {
struct pollfd pollfd = {
.fd = fd,
@@ -2040,238 +1527,6 @@ int flush_fd(int fd) {
}
}
-int acquire_terminal(
- const char *name,
- bool fail,
- bool force,
- bool ignore_tiocstty_eperm,
- usec_t timeout) {
-
- int fd = -1, notify = -1, r = 0, wd = -1;
- usec_t ts = 0;
-
- assert(name);
-
- /* We use inotify to be notified when the tty is closed. We
- * create the watch before checking if we can actually acquire
- * it, so that we don't lose any event.
- *
- * Note: strictly speaking this actually watches for the
- * device being closed, it does *not* really watch whether a
- * tty loses its controlling process. However, unless some
- * rogue process uses TIOCNOTTY on /dev/tty *after* closing
- * its tty otherwise this will not become a problem. As long
- * as the administrator makes sure not configure any service
- * on the same tty as an untrusted user this should not be a
- * problem. (Which he probably should not do anyway.) */
-
- if (timeout != USEC_INFINITY)
- ts = now(CLOCK_MONOTONIC);
-
- if (!fail && !force) {
- notify = inotify_init1(IN_CLOEXEC | (timeout != USEC_INFINITY ? IN_NONBLOCK : 0));
- if (notify < 0) {
- r = -errno;
- goto fail;
- }
-
- wd = inotify_add_watch(notify, name, IN_CLOSE);
- if (wd < 0) {
- r = -errno;
- goto fail;
- }
- }
-
- 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)
- goto fail;
- }
-
- /* We pass here O_NOCTTY only so that we can check the return
- * value TIOCSCTTY and have a reliable way to figure out if we
- * successfully became the controlling process of the tty */
- fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC);
- if (fd < 0)
- return fd;
-
- /* Temporarily ignore SIGHUP, so that we don't get SIGHUP'ed
- * if we already own the tty. */
- assert_se(sigaction(SIGHUP, &sa_new, &sa_old) == 0);
-
- /* First, try to get the tty */
- if (ioctl(fd, TIOCSCTTY, force) < 0)
- r = -errno;
-
- assert_se(sigaction(SIGHUP, &sa_old, NULL) == 0);
-
- /* Sometimes it makes sense to ignore TIOCSCTTY
- * returning EPERM, i.e. when very likely we already
- * are have this controlling terminal. */
- if (r < 0 && r == -EPERM && ignore_tiocstty_eperm)
- r = 0;
-
- if (r < 0 && (force || fail || r != -EPERM)) {
- goto fail;
- }
-
- if (r >= 0)
- break;
-
- assert(!fail);
- assert(!force);
- assert(notify >= 0);
-
- for (;;) {
- union inotify_event_buffer buffer;
- struct inotify_event *e;
- ssize_t l;
-
- if (timeout != USEC_INFINITY) {
- usec_t n;
-
- n = now(CLOCK_MONOTONIC);
- if (ts + timeout < n) {
- r = -ETIMEDOUT;
- goto fail;
- }
-
- r = fd_wait_for_event(fd, POLLIN, ts + timeout - n);
- if (r < 0)
- goto fail;
-
- if (r == 0) {
- r = -ETIMEDOUT;
- goto fail;
- }
- }
-
- l = read(notify, &buffer, sizeof(buffer));
- if (l < 0) {
- if (errno == EINTR || errno == EAGAIN)
- continue;
-
- r = -errno;
- goto fail;
- }
-
- FOREACH_INOTIFY_EVENT(e, buffer, l) {
- if (e->wd != wd || !(e->mask & IN_CLOSE)) {
- r = -EIO;
- goto fail;
- }
- }
-
- break;
- }
-
- /* We close the tty fd here since if the old session
- * ended our handle will be dead. It's important that
- * we do this after sleeping, so that we don't enter
- * an endless loop. */
- fd = safe_close(fd);
- }
-
- safe_close(notify);
-
- r = reset_terminal_fd(fd, true);
- if (r < 0)
- log_warning_errno(r, "Failed to reset terminal: %m");
-
- return fd;
-
-fail:
- safe_close(fd);
- safe_close(notify);
-
- return r;
-}
-
-int release_terminal(void) {
- static const struct sigaction sa_new = {
- .sa_handler = SIG_IGN,
- .sa_flags = SA_RESTART,
- };
-
- _cleanup_close_ int fd = -1;
- struct sigaction sa_old;
- int r = 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 */
- assert_se(sigaction(SIGHUP, &sa_new, &sa_old) == 0);
-
- if (ioctl(fd, TIOCNOTTY) < 0)
- r = -errno;
-
- assert_se(sigaction(SIGHUP, &sa_old, NULL) == 0);
-
- return r;
-}
-
-int sigaction_many(const struct sigaction *sa, ...) {
- va_list ap;
- int r = 0, sig;
-
- va_start(ap, sa);
- while ((sig = va_arg(ap, int)) > 0)
- if (sigaction(sig, sa, NULL) < 0)
- r = -errno;
- va_end(ap);
-
- return r;
-}
-
-int ignore_signals(int sig, ...) {
- struct sigaction sa = {
- .sa_handler = SIG_IGN,
- .sa_flags = SA_RESTART,
- };
- va_list ap;
- int r = 0;
-
- if (sigaction(sig, &sa, NULL) < 0)
- r = -errno;
-
- va_start(ap, sig);
- while ((sig = va_arg(ap, int)) > 0)
- if (sigaction(sig, &sa, NULL) < 0)
- r = -errno;
- va_end(ap);
-
- return r;
-}
-
-int default_signals(int sig, ...) {
- struct sigaction sa = {
- .sa_handler = SIG_DFL,
- .sa_flags = SA_RESTART,
- };
- va_list ap;
- int r = 0;
-
- if (sigaction(sig, &sa, NULL) < 0)
- r = -errno;
-
- va_start(ap, sig);
- while ((sig = va_arg(ap, int)) > 0)
- if (sigaction(sig, &sa, NULL) < 0)
- r = -errno;
- va_end(ap);
-
- return r;
-}
-
void safe_close_pair(int p[]) {
assert(p);
@@ -2325,6 +1580,17 @@ ssize_t loop_read(int fd, void *buf, size_t nbytes, bool do_poll) {
return n;
}
+int loop_read_exact(int fd, void *buf, size_t nbytes, bool do_poll) {
+ ssize_t n;
+
+ n = loop_read(fd, buf, nbytes, do_poll);
+ if (n < 0)
+ return n;
+ if ((size_t) n != nbytes)
+ return -EIO;
+ return 0;
+}
+
int loop_write(int fd, const void *buf, size_t nbytes, bool do_poll) {
const uint8_t *p = buf;
@@ -2333,7 +1599,7 @@ int loop_write(int fd, const void *buf, size_t nbytes, bool do_poll) {
errno = 0;
- while (nbytes > 0) {
+ do {
ssize_t k;
k = write(fd, p, nbytes);
@@ -2353,23 +1619,23 @@ int loop_write(int fd, const void *buf, size_t nbytes, bool do_poll) {
return -errno;
}
- if (k == 0) /* Can't really happen */
+ if (nbytes > 0 && k == 0) /* Can't really happen */
return -EIO;
p += k;
nbytes -= k;
- }
+ } while (nbytes > 0);
return 0;
}
int parse_size(const char *t, off_t base, off_t *size) {
- /* Soo, sometimes we want to parse IEC binary suffxies, and
+ /* Soo, sometimes we want to parse IEC binary suffixes, and
* sometimes SI decimal suffixes. This function can parse
* both. Which one is the right way depends on the
* context. Wikipedia suggests that SI is customary for
- * hardrware metrics and network speeds, while IEC is
+ * hardware metrics and network speeds, while IEC is
* customary for most data sizes used by software and volatile
* (RAM) memory. Hence be careful which one you pick!
*
@@ -2492,40 +1758,6 @@ int parse_size(const char *t, off_t base, off_t *size) {
return 0;
}
-int make_stdio(int fd) {
- int r, s, t;
-
- assert(fd >= 0);
-
- r = dup2(fd, STDIN_FILENO);
- s = dup2(fd, STDOUT_FILENO);
- t = dup2(fd, STDERR_FILENO);
-
- if (fd >= 3)
- safe_close(fd);
-
- if (r < 0 || s < 0 || t < 0)
- return -errno;
-
- /* Explicitly unset O_CLOEXEC, since if fd was < 3, then
- * dup2() was a NOP and the bit hence possibly set. */
- fd_cloexec(STDIN_FILENO, false);
- fd_cloexec(STDOUT_FILENO, false);
- fd_cloexec(STDERR_FILENO, false);
-
- return 0;
-}
-
-int make_null_stdio(void) {
- int null_fd;
-
- null_fd = open("/dev/null", O_RDWR|O_NOCTTY);
- if (null_fd < 0)
- return -errno;
-
- return make_stdio(null_fd);
-}
-
bool is_device_path(const char *path) {
/* Returns true on paths that refer to a device, either in
@@ -2577,108 +1809,6 @@ char* dirname_malloc(const char *path) {
return dir;
}
-int dev_urandom(void *p, size_t n) {
- static int have_syscall = -1;
- int r, fd;
- ssize_t k;
-
- /* Gathers some randomness from the kernel. This call will
- * never block, and will always return some data from the
- * kernel, regardless if the random pool is fully initialized
- * or not. It thus makes no guarantee for the quality of the
- * returned entropy, but is good enough for or usual usecases
- * of seeding the hash functions for hashtable */
-
- /* Use the getrandom() syscall unless we know we don't have
- * it, or when the requested size is too large for it. */
- if (have_syscall != 0 || (size_t) (int) n != n) {
- r = getrandom(p, n, GRND_NONBLOCK);
- if (r == (int) n) {
- have_syscall = true;
- return 0;
- }
-
- if (r < 0) {
- if (errno == ENOSYS)
- /* we lack the syscall, continue with
- * reading from /dev/urandom */
- have_syscall = false;
- else if (errno == EAGAIN)
- /* not enough entropy for now. Let's
- * remember to use the syscall the
- * next time, again, but also read
- * from /dev/urandom for now, which
- * doesn't care about the current
- * amount of entropy. */
- have_syscall = true;
- else
- return -errno;
- } else
- /* too short read? */
- return -EIO;
- }
-
- fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC|O_NOCTTY);
- if (fd < 0)
- return errno == ENOENT ? -ENOSYS : -errno;
-
- k = loop_read(fd, p, n, true);
- safe_close(fd);
-
- if (k < 0)
- return (int) k;
- if ((size_t) k != n)
- return -EIO;
-
- return 0;
-}
-
-void initialize_srand(void) {
- static bool srand_called = false;
- unsigned x;
-#ifdef HAVE_SYS_AUXV_H
- void *auxv;
-#endif
-
- if (srand_called)
- return;
-
- x = 0;
-
-#ifdef HAVE_SYS_AUXV_H
- /* The kernel provides us with a bit of entropy in auxv, so
- * let's try to make use of that to seed the pseudo-random
- * generator. It's better than nothing... */
-
- auxv = (void*) getauxval(AT_RANDOM);
- if (auxv)
- x ^= *(unsigned*) auxv;
-#endif
-
- x ^= (unsigned) now(CLOCK_REALTIME);
- x ^= (unsigned) gettid();
-
- srand(x);
- srand_called = true;
-}
-
-void random_bytes(void *p, size_t n) {
- uint8_t *q;
- int r;
-
- r = dev_urandom(p, n);
- if (r >= 0)
- return;
-
- /* If some idiot made /dev/urandom unavailable to us, he'll
- * get a PRNG instead. */
-
- initialize_srand();
-
- for (q = p; q < (uint8_t*) p + n; q ++)
- *q = rand();
-}
-
void rename_process(const char name[8]) {
assert(name);
@@ -2710,55 +1840,6 @@ void rename_process(const char name[8]) {
}
}
-void sigset_add_many(sigset_t *ss, ...) {
- va_list ap;
- int sig;
-
- assert(ss);
-
- va_start(ap, ss);
- while ((sig = va_arg(ap, int)) > 0)
- assert_se(sigaddset(ss, sig) == 0);
- va_end(ap);
-}
-
-int sigprocmask_many(int how, ...) {
- va_list ap;
- sigset_t ss;
- int sig;
-
- assert_se(sigemptyset(&ss) == 0);
-
- va_start(ap, how);
- while ((sig = va_arg(ap, int)) > 0)
- assert_se(sigaddset(&ss, sig) == 0);
- va_end(ap);
-
- if (sigprocmask(how, &ss, NULL) < 0)
- return -errno;
-
- return 0;
-}
-
-char* gethostname_malloc(void) {
- struct utsname u;
-
- assert_se(uname(&u) >= 0);
-
- if (!isempty(u.nodename) && !streq(u.nodename, "(none)"))
- return strdup(u.nodename);
-
- return strdup(u.sysname);
-}
-
-bool hostname_is_set(void) {
- struct utsname u;
-
- assert_se(uname(&u) >= 0);
-
- return !isempty(u.nodename) && !streq(u.nodename, "(none)");
-}
-
char *lookup_uid(uid_t uid) {
long bufsize;
char *name;
@@ -2808,243 +1889,14 @@ char *getusername_malloc(void) {
return lookup_uid(getuid());
}
-int getttyname_malloc(int fd, char **ret) {
- size_t l = 100;
- int r;
-
- assert(fd >= 0);
- assert(ret);
-
- for (;;) {
- char path[l];
-
- r = ttyname_r(fd, path, sizeof(path));
- if (r == 0) {
- const char *p;
- char *c;
-
- p = startswith(path, "/dev/");
- c = strdup(p ?: path);
- if (!c)
- return -ENOMEM;
-
- *ret = c;
- return 0;
- }
-
- if (r != ERANGE)
- return -r;
-
- l *= 2;
- }
-
- return 0;
-}
-
-int getttyname_harder(int fd, char **r) {
- int k;
- char *s;
-
- k = getttyname_malloc(fd, &s);
- if (k < 0)
- return k;
-
- if (streq(s, "tty")) {
- free(s);
- return get_ctty(0, NULL, r);
- }
-
- *r = s;
- return 0;
-}
-
-int get_ctty_devnr(pid_t pid, dev_t *d) {
- int r;
- _cleanup_free_ char *line = NULL;
- const char *p;
- unsigned long ttynr;
-
- assert(pid >= 0);
-
- p = procfs_file_alloca(pid, "stat");
- r = read_one_line_file(p, &line);
- if (r < 0)
- return r;
-
- p = strrchr(line, ')');
- if (!p)
- return -EIO;
-
- p++;
-
- if (sscanf(p, " "
- "%*c " /* state */
- "%*d " /* ppid */
- "%*d " /* pgrp */
- "%*d " /* session */
- "%lu ", /* ttynr */
- &ttynr) != 1)
- return -EIO;
-
- if (major(ttynr) == 0 && minor(ttynr) == 0)
- return -ENOENT;
-
- if (d)
- *d = (dev_t) ttynr;
-
- return 0;
-}
-
-int get_ctty(pid_t pid, dev_t *_devnr, char **r) {
- char fn[sizeof("/dev/char/")-1 + 2*DECIMAL_STR_MAX(unsigned) + 1 + 1], *b = NULL;
- _cleanup_free_ char *s = NULL;
- const char *p;
- dev_t devnr;
- int k;
-
- assert(r);
-
- k = get_ctty_devnr(pid, &devnr);
- if (k < 0)
- return k;
-
- sprintf(fn, "/dev/char/%u:%u", major(devnr), minor(devnr));
-
- k = readlink_malloc(fn, &s);
- if (k < 0) {
-
- if (k != -ENOENT)
- return k;
-
- /* This is an ugly hack */
- if (major(devnr) == 136) {
- asprintf(&b, "pts/%u", minor(devnr));
- goto finish;
- }
-
- /* Probably something like the ptys which have no
- * symlink in /dev/char. Let's return something
- * vaguely useful. */
-
- b = strdup(fn + 5);
- goto finish;
- }
-
- if (startswith(s, "/dev/"))
- p = s + 5;
- else if (startswith(s, "../"))
- p = s + 3;
- else
- p = s;
-
- b = strdup(p);
-
-finish:
- if (!b)
- return -ENOMEM;
-
- *r = b;
- if (_devnr)
- *_devnr = devnr;
-
- return 0;
-}
-
-int rm_rf_children_dangerous(int fd, bool only_dirs, bool honour_sticky, struct stat *root_dev) {
- _cleanup_closedir_ DIR *d = NULL;
- int ret = 0;
-
- assert(fd >= 0);
-
- /* This returns the first error we run into, but nevertheless
- * tries to go on. This closes the passed fd. */
-
- d = fdopendir(fd);
- if (!d) {
- safe_close(fd);
-
- return errno == ENOENT ? 0 : -errno;
- }
-
- for (;;) {
- struct dirent *de;
- bool is_dir, keep_around;
- struct stat st;
- int r;
-
- errno = 0;
- de = readdir(d);
- if (!de) {
- if (errno != 0 && ret == 0)
- ret = -errno;
- return ret;
- }
-
- if (streq(de->d_name, ".") || streq(de->d_name, ".."))
- continue;
-
- if (de->d_type == DT_UNKNOWN ||
- honour_sticky ||
- (de->d_type == DT_DIR && root_dev)) {
- if (fstatat(fd, de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) {
- if (ret == 0 && errno != ENOENT)
- ret = -errno;
- continue;
- }
-
- is_dir = S_ISDIR(st.st_mode);
- keep_around =
- honour_sticky &&
- (st.st_uid == 0 || st.st_uid == getuid()) &&
- (st.st_mode & S_ISVTX);
- } else {
- is_dir = de->d_type == DT_DIR;
- keep_around = false;
- }
-
- if (is_dir) {
- int subdir_fd;
-
- /* if root_dev is set, remove subdirectories only, if device is same as dir */
- if (root_dev && st.st_dev != root_dev->st_dev)
- continue;
-
- subdir_fd = openat(fd, de->d_name,
- O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME);
- if (subdir_fd < 0) {
- if (ret == 0 && errno != ENOENT)
- ret = -errno;
- continue;
- }
-
- r = rm_rf_children_dangerous(subdir_fd, only_dirs, honour_sticky, root_dev);
- if (r < 0 && ret == 0)
- ret = r;
-
- if (!keep_around)
- if (unlinkat(fd, de->d_name, AT_REMOVEDIR) < 0) {
- if (ret == 0 && errno != ENOENT)
- ret = -errno;
- }
-
- } else if (!only_dirs && !keep_around) {
-
- if (unlinkat(fd, de->d_name, 0) < 0) {
- if (ret == 0 && errno != ENOENT)
- ret = -errno;
- }
- }
- }
-}
-
-_pure_ static int is_temporary_fs(struct statfs *s) {
+bool is_temporary_fs(const struct statfs *s) {
assert(s);
return F_TYPE_EQUAL(s->f_type, TMPFS_MAGIC) ||
F_TYPE_EQUAL(s->f_type, RAMFS_MAGIC);
}
-int is_fd_on_temporary_fs(int fd) {
+int fd_is_temporary_fs(int fd) {
struct statfs s;
if (fstatfs(fd, &s) < 0)
@@ -3053,114 +1905,6 @@ int is_fd_on_temporary_fs(int fd) {
return is_temporary_fs(&s);
}
-int rm_rf_children(int fd, bool only_dirs, bool honour_sticky, struct stat *root_dev) {
- struct statfs s;
-
- assert(fd >= 0);
-
- if (fstatfs(fd, &s) < 0) {
- safe_close(fd);
- return -errno;
- }
-
- /* We refuse to clean disk file systems with this call. This
- * is extra paranoia just to be sure we never ever remove
- * non-state data */
- if (!is_temporary_fs(&s)) {
- log_error("Attempted to remove disk file system, and we can't allow that.");
- safe_close(fd);
- return -EPERM;
- }
-
- return rm_rf_children_dangerous(fd, only_dirs, honour_sticky, root_dev);
-}
-
-static int file_is_priv_sticky(const char *p) {
- struct stat st;
-
- assert(p);
-
- if (lstat(p, &st) < 0)
- return -errno;
-
- return
- (st.st_uid == 0 || st.st_uid == getuid()) &&
- (st.st_mode & S_ISVTX);
-}
-
-static int rm_rf_internal(const char *path, bool only_dirs, bool delete_root, bool honour_sticky, bool dangerous) {
- int fd, r;
- struct statfs s;
-
- assert(path);
-
- /* We refuse to clean the root file system with this
- * call. This is extra paranoia to never cause a really
- * seriously broken system. */
- if (path_equal(path, "/")) {
- log_error("Attempted to remove entire root file system, and we can't allow that.");
- return -EPERM;
- }
-
- fd = open(path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME);
- if (fd < 0) {
-
- if (errno != ENOTDIR && errno != ELOOP)
- return -errno;
-
- if (!dangerous) {
- if (statfs(path, &s) < 0)
- return -errno;
-
- if (!is_temporary_fs(&s)) {
- log_error("Attempted to remove disk file system, and we can't allow that.");
- return -EPERM;
- }
- }
-
- if (delete_root && !only_dirs)
- if (unlink(path) < 0 && errno != ENOENT)
- return -errno;
-
- return 0;
- }
-
- if (!dangerous) {
- if (fstatfs(fd, &s) < 0) {
- safe_close(fd);
- return -errno;
- }
-
- if (!is_temporary_fs(&s)) {
- log_error("Attempted to remove disk file system, and we can't allow that.");
- safe_close(fd);
- return -EPERM;
- }
- }
-
- r = rm_rf_children_dangerous(fd, only_dirs, honour_sticky, NULL);
- if (delete_root) {
-
- if (honour_sticky && file_is_priv_sticky(path) > 0)
- return r;
-
- if (rmdir(path) < 0 && errno != ENOENT) {
- if (r == 0)
- r = -errno;
- }
- }
-
- return r;
-}
-
-int rm_rf(const char *path, bool only_dirs, bool delete_root, bool honour_sticky) {
- return rm_rf_internal(path, only_dirs, delete_root, honour_sticky, false);
-}
-
-int rm_rf_dangerous(const char *path, bool only_dirs, bool delete_root, bool honour_sticky) {
- return rm_rf_internal(path, only_dirs, delete_root, honour_sticky, true);
-}
-
int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid) {
assert(path);
@@ -3225,311 +1969,6 @@ cpu_set_t* cpu_set_malloc(unsigned *ncpus) {
}
}
-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[6] = {};
- int n = 0;
- static bool prev_ephemeral;
-
- assert(format);
-
- /* This is independent of logging, as status messages are
- * optional and go exclusively to the console. */
-
- if (vasprintf(&s, format, ap) < 0)
- return log_oom();
-
- fd = open_terminal("/dev/console", O_WRONLY|O_NOCTTY|O_CLOEXEC);
- if (fd < 0)
- return fd;
-
- if (ellipse) {
- char *e;
- size_t emax, sl;
- int c;
-
- c = fd_columns(fd);
- if (c <= 0)
- c = 80;
-
- sl = status ? sizeof(status_indent)-1 : 0;
-
- emax = c - sl - 1;
- if (emax < 3)
- emax = 3;
-
- e = ellipsize(s, emax, 50);
- if (e) {
- free(s);
- s = e;
- }
- }
-
- if (prev_ephemeral)
- IOVEC_SET_STRING(iovec[n++], "\r" ANSI_ERASE_TO_END_OF_LINE);
- prev_ephemeral = ephemeral;
-
- if (status) {
- if (!isempty(status)) {
- IOVEC_SET_STRING(iovec[n++], "[");
- IOVEC_SET_STRING(iovec[n++], status);
- IOVEC_SET_STRING(iovec[n++], "] ");
- } else
- IOVEC_SET_STRING(iovec[n++], status_indent);
- }
-
- IOVEC_SET_STRING(iovec[n++], s);
- if (!ephemeral)
- IOVEC_SET_STRING(iovec[n++], "\n");
-
- if (writev(fd, iovec, n) < 0)
- return -errno;
-
- return 0;
-}
-
-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, ephemeral, format, ap);
- va_end(ap);
-
- return r;
-}
-
-char *replace_env(const char *format, char **env) {
- enum {
- WORD,
- CURLY,
- VARIABLE
- } state = WORD;
-
- const char *e, *word = format;
- char *r = NULL, *k;
-
- assert(format);
-
- for (e = format; *e; e ++) {
-
- switch (state) {
-
- case WORD:
- if (*e == '$')
- state = CURLY;
- break;
-
- case CURLY:
- if (*e == '{') {
- k = strnappend(r, word, e-word-1);
- if (!k)
- goto fail;
-
- free(r);
- r = k;
-
- word = e-1;
- state = VARIABLE;
-
- } else if (*e == '$') {
- k = strnappend(r, word, e-word);
- if (!k)
- goto fail;
-
- free(r);
- r = k;
-
- word = e+1;
- state = WORD;
- } else
- state = WORD;
- break;
-
- case VARIABLE:
- if (*e == '}') {
- const char *t;
-
- t = strempty(strv_env_get_n(env, word+2, e-word-2));
-
- k = strappend(r, t);
- if (!k)
- goto fail;
-
- free(r);
- r = k;
-
- word = e+1;
- state = WORD;
- }
- break;
- }
- }
-
- k = strnappend(r, word, e-word);
- if (!k)
- goto fail;
-
- free(r);
- return k;
-
-fail:
- free(r);
- return NULL;
-}
-
-char **replace_env_argv(char **argv, char **env) {
- char **ret, **i;
- unsigned k = 0, l = 0;
-
- l = strv_length(argv);
-
- ret = new(char*, l+1);
- if (!ret)
- return NULL;
-
- STRV_FOREACH(i, argv) {
-
- /* If $FOO appears as single word, replace it by the split up variable */
- if ((*i)[0] == '$' && (*i)[1] != '{') {
- char *e;
- char **w, **m;
- unsigned q;
-
- e = strv_env_get(env, *i+1);
- if (e) {
- int r;
-
- r = strv_split_quoted(&m, e, true);
- if (r < 0) {
- ret[k] = NULL;
- strv_free(ret);
- return NULL;
- }
- } else
- m = NULL;
-
- q = strv_length(m);
- l = l + q - 1;
-
- w = realloc(ret, sizeof(char*) * (l+1));
- if (!w) {
- ret[k] = NULL;
- strv_free(ret);
- strv_free(m);
- return NULL;
- }
-
- ret = w;
- if (m) {
- memcpy(ret + k, m, q * sizeof(char*));
- free(m);
- }
-
- k += q;
- continue;
- }
-
- /* If ${FOO} appears as part of a word, replace it by the variable as-is */
- ret[k] = replace_env(*i, env);
- if (!ret[k]) {
- strv_free(ret);
- return NULL;
- }
- k++;
- }
-
- ret[k] = NULL;
- return ret;
-}
-
-int fd_columns(int fd) {
- struct winsize ws = {};
-
- if (ioctl(fd, TIOCGWINSZ, &ws) < 0)
- return -errno;
-
- if (ws.ws_col <= 0)
- return -EIO;
-
- return ws.ws_col;
-}
-
-unsigned columns(void) {
- const char *e;
- int c;
-
- if (_likely_(cached_columns > 0))
- return cached_columns;
-
- c = 0;
- e = getenv("COLUMNS");
- if (e)
- (void) safe_atoi(e, &c);
-
- if (c <= 0)
- c = fd_columns(STDOUT_FILENO);
-
- if (c <= 0)
- c = 80;
-
- cached_columns = c;
- return cached_columns;
-}
-
-int fd_lines(int fd) {
- struct winsize ws = {};
-
- if (ioctl(fd, TIOCGWINSZ, &ws) < 0)
- return -errno;
-
- if (ws.ws_row <= 0)
- return -EIO;
-
- return ws.ws_row;
-}
-
-unsigned lines(void) {
- const char *e;
- int l;
-
- if (_likely_(cached_lines > 0))
- return cached_lines;
-
- l = 0;
- e = getenv("LINES");
- if (e)
- (void) safe_atoi(e, &l);
-
- if (l <= 0)
- l = fd_lines(STDOUT_FILENO);
-
- if (l <= 0)
- l = 24;
-
- cached_lines = l;
- return cached_lines;
-}
-
-/* intended to be used as a SIGWINCH sighandler */
-void columns_lines_cache_reset(int signum) {
- cached_columns = 0;
- cached_lines = 0;
-}
-
-bool on_tty(void) {
- static int cached_on_tty = -1;
-
- if (_unlikely_(cached_on_tty < 0))
- cached_on_tty = isatty(STDOUT_FILENO) > 0;
-
- return cached_on_tty;
-}
-
int files_same(const char *filea, const char *fileb) {
struct stat a, b;
@@ -3705,14 +2144,15 @@ int touch(const char *path) {
return touch_file(path, false, USEC_INFINITY, UID_INVALID, GID_INVALID, 0);
}
-char *unquote(const char *s, const char* quotes) {
+static char *unquote(const char *s, const char* quotes) {
size_t l;
assert(s);
/* This is rather stupid, simply removes the heading and
* trailing quotes if there is one. Doesn't care about
- * escaping or anything. We should make this smarter one
- * day... */
+ * escaping or anything.
+ *
+ * DON'T USE THIS FOR NEW CODE ANYMORE!*/
l = strlen(s);
if (l < 2)
@@ -3724,103 +2164,6 @@ char *unquote(const char *s, const char* quotes) {
return strdup(s);
}
-char *normalize_env_assignment(const char *s) {
- _cleanup_free_ char *value = NULL;
- const char *eq;
- char *p, *name;
-
- eq = strchr(s, '=');
- if (!eq) {
- char *r, *t;
-
- r = strdup(s);
- if (!r)
- return NULL;
-
- t = strstrip(r);
- if (t != r)
- memmove(r, t, strlen(t) + 1);
-
- return r;
- }
-
- name = strndupa(s, eq - s);
- p = strdupa(eq + 1);
-
- value = unquote(strstrip(p), QUOTES);
- if (!value)
- return NULL;
-
- return strjoin(strstrip(name), "=", value, NULL);
-}
-
-int wait_for_terminate(pid_t pid, siginfo_t *status) {
- siginfo_t dummy;
-
- assert(pid >= 1);
-
- if (!status)
- status = &dummy;
-
- for (;;) {
- zero(*status);
-
- if (waitid(P_PID, pid, status, WEXITED) < 0) {
-
- if (errno == EINTR)
- continue;
-
- return -errno;
- }
-
- return 0;
- }
-}
-
-/*
- * Return values:
- * < 0 : wait_for_terminate() failed to get the state of the
- * process, the process was terminated by a signal, or
- * failed for an unknown reason.
- * >=0 : The process terminated normally, and its exit code is
- * returned.
- *
- * That is, success is indicated by a return value of zero, and an
- * error is indicated by a non-zero value.
- *
- * A warning is emitted if the process terminates abnormally,
- * and also if it returns non-zero unless check_exit_code is true.
- */
-int wait_for_terminate_and_warn(const char *name, pid_t pid, bool check_exit_code) {
- int r;
- siginfo_t status;
-
- assert(name);
- assert(pid > 1);
-
- r = wait_for_terminate(pid, &status);
- if (r < 0)
- return log_warning_errno(r, "Failed to wait for %s: %m", name);
-
- if (status.si_code == CLD_EXITED) {
- if (status.si_status != 0)
- log_full(check_exit_code ? LOG_WARNING : LOG_DEBUG,
- "%s failed with error code %i.", name, status.si_status);
- else
- log_debug("%s succeeded.", name);
-
- return status.si_status;
- } else if (status.si_code == CLD_KILLED ||
- status.si_code == CLD_DUMPED) {
-
- log_warning("%s terminated by signal %s.", name, signal_to_string(status.si_status));
- return -EPROTO;
- }
-
- log_warning("%s failed due to unknown reason.", name);
- return -EPROTO;
-}
-
noreturn void freeze(void) {
/* Make sure nobody waits for us on a socket anymore */
@@ -3885,23 +2228,11 @@ DIR *xopendirat(int fd, const char *name, int flags) {
return d;
}
-int signal_from_string_try_harder(const char *s) {
- int signo;
- assert(s);
-
- signo = signal_from_string(s);
- if (signo <= 0)
- if (startswith(s, "SIG"))
- return signal_from_string(s+3);
-
- return signo;
-}
-
static char *tag_to_udev_node(const char *tagvalue, const char *by) {
_cleanup_free_ char *t = NULL, *u = NULL;
size_t enc_len;
- u = unquote(tagvalue, "\"\'");
+ u = unquote(tagvalue, QUOTES);
if (!u)
return NULL;
@@ -3934,101 +2265,6 @@ char *fstab_node_to_udev_node(const char *p) {
return strdup(p);
}
-bool tty_is_vc(const char *tty) {
- assert(tty);
-
- return vtnr_from_tty(tty) >= 0;
-}
-
-bool tty_is_console(const char *tty) {
- assert(tty);
-
- if (startswith(tty, "/dev/"))
- tty += 5;
-
- return streq(tty, "console");
-}
-
-int vtnr_from_tty(const char *tty) {
- int i, r;
-
- assert(tty);
-
- if (startswith(tty, "/dev/"))
- tty += 5;
-
- if (!startswith(tty, "tty") )
- return -EINVAL;
-
- if (tty[3] < '0' || tty[3] > '9')
- return -EINVAL;
-
- r = safe_atoi(tty+3, &i);
- if (r < 0)
- return r;
-
- if (i < 0 || i > 63)
- return -EINVAL;
-
- 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;
-
- if (streq(tty, "tty0")) {
- char *tmp;
-
- /* Get the active VC (e.g. tty1) */
- if (read_one_line_file("/sys/class/tty/tty0/active", &tmp) >= 0) {
- free(*active);
- tty = *active = tmp;
- }
- }
-
- return tty;
-}
-
-bool tty_is_vc_resolve(const char *tty) {
- _cleanup_free_ char *active = NULL;
-
- assert(tty);
-
- if (startswith(tty, "/dev/"))
- tty += 5;
-
- if (streq(tty, "console")) {
- tty = resolve_dev_console(&active);
- if (!tty)
- return false;
- }
-
- return tty_is_vc(tty);
-}
-
-const char *default_term_for_tty(const char *tty) {
- assert(tty);
-
- return tty_is_vc_resolve(tty) ? "TERM=linux" : "TERM=vt220";
-}
-
bool dirent_is_file(const struct dirent *de) {
assert(de);
@@ -4114,8 +2350,7 @@ static int do_execute(char **directories, usec_t timeout, char *argv[]) {
if (null_or_empty_path(path)) {
log_debug("%s is empty (a mask).", path);
continue;
- } else
- log_debug("%s will be executed.", path);
+ }
pid = fork();
if (pid < 0) {
@@ -4198,17 +2433,6 @@ void execute_directories(const char* const* directories, usec_t timeout, char *a
wait_for_terminate_and_warn(name, executor_pid, true);
}
-int kill_and_sigcont(pid_t pid, int sig) {
- int r;
-
- r = kill(pid, sig) < 0 ? -errno : 0;
-
- if (r >= 0)
- kill(pid, SIGCONT);
-
- return r;
-}
-
bool nulstr_contains(const char*nulstr, const char *needle) {
const char *i;
@@ -4235,79 +2459,6 @@ char* strshorten(char *s, size_t l) {
return s;
}
-static bool hostname_valid_char(char c) {
- return
- (c >= 'a' && c <= 'z') ||
- (c >= 'A' && c <= 'Z') ||
- (c >= '0' && c <= '9') ||
- c == '-' ||
- c == '_' ||
- c == '.';
-}
-
-bool hostname_is_valid(const char *s) {
- const char *p;
- bool dot;
-
- if (isempty(s))
- return false;
-
- /* Doesn't accept empty hostnames, hostnames with trailing or
- * leading dots, and hostnames with multiple dots in a
- * sequence. Also ensures that the length stays below
- * HOST_NAME_MAX. */
-
- 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;
-
- return true;
-}
-
-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;
- }
-
- }
-
- if (dot && d > s)
- d[-1] = 0;
- else
- *d = 0;
-
- strshorten(s, HOST_NAME_MAX);
-
- return s;
-}
-
bool machine_name_is_valid(const char *s) {
if (!hostname_is_valid(s))
@@ -4393,111 +2544,45 @@ int fopen_temporary(const char *path, FILE **_f, char **_temp_path) {
return 0;
}
-int terminal_vhangup_fd(int fd) {
- assert(fd >= 0);
-
- if (ioctl(fd, TIOCVHANGUP) < 0)
- return -errno;
-
- return 0;
-}
-
-int terminal_vhangup(const char *name) {
- _cleanup_close_ int fd;
-
- fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC);
- if (fd < 0)
- return fd;
-
- return terminal_vhangup_fd(fd);
-}
-
-int vt_disallocate(const char *name) {
- int fd, r;
- unsigned u;
-
- /* Deallocate the VT if possible. If not possible
- * (i.e. because it is the active one), at least clear it
- * entirely (including the scrollback buffer) */
-
- if (!startswith(name, "/dev/"))
- return -EINVAL;
-
- if (!tty_is_vc(name)) {
- /* So this is not a VT. I guess we cannot deallocate
- * it then. But let's at least clear the screen */
-
- fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC);
- if (fd < 0)
- return fd;
-
- loop_write(fd,
- "\033[r" /* clear scrolling region */
- "\033[H" /* move home */
- "\033[2J", /* clear screen */
- 10, false);
- safe_close(fd);
-
- return 0;
- }
+int symlink_atomic(const char *from, const char *to) {
+ _cleanup_free_ char *t = NULL;
+ int r;
- if (!startswith(name, "/dev/tty"))
- return -EINVAL;
+ assert(from);
+ assert(to);
- r = safe_atou(name+8, &u);
+ r = tempfn_random(to, &t);
if (r < 0)
return r;
- if (u <= 0)
- return -EINVAL;
-
- /* Try to deallocate */
- fd = open_terminal("/dev/tty0", O_RDWR|O_NOCTTY|O_CLOEXEC);
- if (fd < 0)
- return fd;
-
- r = ioctl(fd, VT_DISALLOCATE, u);
- safe_close(fd);
-
- if (r >= 0)
- return 0;
-
- if (errno != EBUSY)
+ if (symlink(from, t) < 0)
return -errno;
- /* Couldn't deallocate, so let's clear it fully with
- * scrollback */
- fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC);
- if (fd < 0)
- return fd;
-
- loop_write(fd,
- "\033[r" /* clear scrolling region */
- "\033[H" /* move home */
- "\033[3J", /* clear screen including scrollback, requires Linux 2.6.40 */
- 10, false);
- safe_close(fd);
+ if (rename(t, to) < 0) {
+ unlink_noerrno(t);
+ return -errno;
+ }
return 0;
}
-int symlink_atomic(const char *from, const char *to) {
- _cleanup_free_ char *t = NULL;
+int symlink_idempotent(const char *from, const char *to) {
+ _cleanup_free_ char *p = NULL;
int r;
assert(from);
assert(to);
- r = tempfn_random(to, &t);
- if (r < 0)
- return r;
+ if (symlink(from, to) < 0) {
+ if (errno != EEXIST)
+ return -errno;
- if (symlink(from, t) < 0)
- return -errno;
+ r = readlink_malloc(to, &p);
+ if (r < 0)
+ return r;
- if (rename(t, to) < 0) {
- unlink_noerrno(t);
- return -errno;
+ if (!streq(p, from))
+ return -EINVAL;
}
return 0;
@@ -5099,81 +3184,6 @@ static const char* const ip_tos_table[] = {
DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(ip_tos, int, 0xff);
-static const char *const __signal_table[] = {
- [SIGHUP] = "HUP",
- [SIGINT] = "INT",
- [SIGQUIT] = "QUIT",
- [SIGILL] = "ILL",
- [SIGTRAP] = "TRAP",
- [SIGABRT] = "ABRT",
- [SIGBUS] = "BUS",
- [SIGFPE] = "FPE",
- [SIGKILL] = "KILL",
- [SIGUSR1] = "USR1",
- [SIGSEGV] = "SEGV",
- [SIGUSR2] = "USR2",
- [SIGPIPE] = "PIPE",
- [SIGALRM] = "ALRM",
- [SIGTERM] = "TERM",
-#ifdef SIGSTKFLT
- [SIGSTKFLT] = "STKFLT", /* Linux on SPARC doesn't know SIGSTKFLT */
-#endif
- [SIGCHLD] = "CHLD",
- [SIGCONT] = "CONT",
- [SIGSTOP] = "STOP",
- [SIGTSTP] = "TSTP",
- [SIGTTIN] = "TTIN",
- [SIGTTOU] = "TTOU",
- [SIGURG] = "URG",
- [SIGXCPU] = "XCPU",
- [SIGXFSZ] = "XFSZ",
- [SIGVTALRM] = "VTALRM",
- [SIGPROF] = "PROF",
- [SIGWINCH] = "WINCH",
- [SIGIO] = "IO",
- [SIGPWR] = "PWR",
- [SIGSYS] = "SYS"
-};
-
-DEFINE_PRIVATE_STRING_TABLE_LOOKUP(__signal, int);
-
-const char *signal_to_string(int signo) {
- static thread_local char buf[sizeof("RTMIN+")-1 + DECIMAL_STR_MAX(int) + 1];
- const char *name;
-
- name = __signal_to_string(signo);
- if (name)
- return name;
-
- if (signo >= SIGRTMIN && signo <= SIGRTMAX)
- snprintf(buf, sizeof(buf), "RTMIN+%d", signo - SIGRTMIN);
- else
- snprintf(buf, sizeof(buf), "%d", signo);
-
- return buf;
-}
-
-int signal_from_string(const char *s) {
- int signo;
- int offset = 0;
- unsigned u;
-
- signo = __signal_from_string(s);
- if (signo > 0)
- return signo;
-
- if (startswith(s, "RTMIN+")) {
- s += 6;
- offset = SIGRTMIN;
- }
- if (safe_atou(s, &u) >= 0) {
- signo = (int) u + offset;
- if (signo > 0 && signo < _NSIG)
- return signo;
- }
- return -EINVAL;
-}
-
bool kexec_loaded(void) {
bool loaded = false;
char *s;
@@ -5417,60 +3427,6 @@ int setrlimit_closest(int resource, const struct rlimit *rlim) {
return 0;
}
-int getenv_for_pid(pid_t pid, const char *field, char **_value) {
- _cleanup_fclose_ FILE *f = NULL;
- char *value = NULL;
- int r;
- bool done = false;
- size_t l;
- const char *path;
-
- assert(pid >= 0);
- assert(field);
- assert(_value);
-
- path = procfs_file_alloca(pid, "environ");
-
- f = fopen(path, "re");
- if (!f)
- return -errno;
-
- l = strlen(field);
- r = 0;
-
- do {
- char line[LINE_MAX];
- unsigned i;
-
- for (i = 0; i < sizeof(line)-1; i++) {
- int c;
-
- c = getc(f);
- if (_unlikely_(c == EOF)) {
- done = true;
- break;
- } else if (c == 0)
- break;
-
- line[i] = c;
- }
- line[i] = 0;
-
- if (memcmp(line, field, l) == 0 && line[l] == '=') {
- value = strdup(line + l + 1);
- if (!value)
- return -ENOMEM;
-
- r = 1;
- break;
- }
-
- } while (!done);
-
- *_value = value;
- return r;
-}
-
bool http_etag_is_valid(const char *etag) {
if (isempty(etag))
return false;
@@ -5547,43 +3503,6 @@ bool in_initrd(void) {
return saved;
}
-void warn_melody(void) {
- _cleanup_close_ int fd = -1;
-
- fd = open("/dev/console", O_WRONLY|O_CLOEXEC|O_NOCTTY);
- if (fd < 0)
- return;
-
- /* Yeah, this is synchronous. Kinda sucks. But well... */
-
- ioctl(fd, KIOCSOUND, (int)(1193180/440));
- usleep(125*USEC_PER_MSEC);
-
- ioctl(fd, KIOCSOUND, (int)(1193180/220));
- usleep(125*USEC_PER_MSEC);
-
- ioctl(fd, KIOCSOUND, (int)(1193180/220));
- usleep(125*USEC_PER_MSEC);
-
- ioctl(fd, KIOCSOUND, 0);
-}
-
-int make_console_stdio(void) {
- int fd, r;
-
- /* Make /dev/console the controlling terminal and stdin/stdout/stderr */
-
- fd = acquire_terminal("/dev/console", false, true, true, USEC_INFINITY);
- if (fd < 0)
- return log_error_errno(fd, "Failed to acquire terminal: %m");
-
- r = make_stdio(fd);
- if (r < 0)
- return log_error_errno(r, "Failed to duplicate terminal fd: %m");
-
- return 0;
-}
-
int get_home_dir(char **_h) {
struct passwd *p;
const char *e;
@@ -5746,7 +3665,7 @@ bool path_is_safe(const char *p) {
if (streq(p, "..") || startswith(p, "../") || endswith(p, "/..") || strstr(p, "/../"))
return false;
- if (strlen(p) > PATH_MAX)
+ if (strlen(p)+1 > PATH_MAX)
return false;
/* The following two checks are not really dangerous, but hey, they still are confusing */
@@ -5782,6 +3701,11 @@ void *xbsearch_r(const void *key, const void *base, size_t nmemb, size_t size,
return NULL;
}
+void init_gettext(void) {
+ setlocale(LC_ALL, "");
+ textdomain(GETTEXT_PACKAGE);
+}
+
bool is_locale_utf8(void) {
const char *set;
static int cached_answer = -1;
@@ -5993,7 +3917,7 @@ int on_ac_power(void) {
d = opendir("/sys/class/power_supply");
if (!d)
- return -errno;
+ return errno == ENOENT ? true : -errno;
for (;;) {
struct dirent *de;
@@ -6371,7 +4295,7 @@ int parse_proc_cmdline(int (*parse_item)(const char *key, const char *value)) {
_cleanup_free_ char *word = NULL;
char *value = NULL;
- r = unquote_first_word(&p, &word, true);
+ r = unquote_first_word(&p, &word, UNQUOTE_RELAX);
if (r < 0)
return r;
if (r == 0)
@@ -6411,7 +4335,7 @@ int get_proc_cmdline_key(const char *key, char **value) {
_cleanup_free_ char *word = NULL;
const char *e;
- r = unquote_first_word(&p, &word, true);
+ r = unquote_first_word(&p, &word, UNQUOTE_RELAX);
if (r < 0)
return r;
if (r == 0)
@@ -6559,43 +4483,7 @@ int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int root_fd) {
return -errno;
}
- if (setresgid(0, 0, 0) < 0)
- return -errno;
-
- if (setgroups(0, NULL) < 0)
- return -errno;
-
- if (setresuid(0, 0, 0) < 0)
- return -errno;
-
- return 0;
-}
-
-bool pid_is_unwaited(pid_t pid) {
- /* Checks whether a PID is still valid at all, including a zombie */
-
- if (pid <= 0)
- return false;
-
- if (kill(pid, 0) >= 0)
- return true;
-
- return errno != ESRCH;
-}
-
-bool pid_is_alive(pid_t pid) {
- int r;
-
- /* Checks whether a PID is still valid and not a zombie */
-
- if (pid <= 0)
- return false;
-
- r = get_process_state(pid);
- if (r == -ENOENT || r == 'Z')
- return false;
-
- return true;
+ return reset_uid_gid();
}
int getpeercred(int fd, struct ucred *ucred) {
@@ -6658,7 +4546,7 @@ int getpeersec(int fd, char **ret) {
if (isempty(s)) {
free(s);
- return -ENOTSUP;
+ return -EOPNOTSUPP;
}
*ret = s;
@@ -6689,7 +4577,7 @@ int open_tmpfile(const char *path, int flags) {
#ifdef O_TMPFILE
/* Try O_TMPFILE first, if it is supported */
- fd = open(path, flags|O_TMPFILE, S_IRUSR|S_IWUSR);
+ fd = open(path, flags|O_TMPFILE|O_EXCL, S_IRUSR|S_IWUSR);
if (fd >= 0)
return fd;
#endif
@@ -6746,10 +4634,7 @@ unsigned long personality_from_string(const char *p) {
return PER_LINUX;
#endif
- /* personality(7) documents that 0xffffffffUL is used for
- * querying the current personality, hence let's use that here
- * as error indicator. */
- return 0xffffffffUL;
+ return PERSONALITY_INVALID;
}
const char* personality_to_string(unsigned long p) {
@@ -6884,9 +4769,9 @@ int umount_recursive(const char *prefix, int flags) {
continue;
}
- p = cunescape(path);
- if (!p)
- return -ENOMEM;
+ r = cunescape(path, UNESCAPE_RELAX, &p);
+ if (r < 0)
+ return r;
if (!path_startswith(p, prefix))
continue;
@@ -6986,9 +4871,9 @@ int bind_remount_recursive(const char *prefix, bool ro) {
continue;
}
- p = cunescape(path);
- if (!p)
- return -ENOMEM;
+ r = cunescape(path, UNESCAPE_RELAX, &p);
+ if (r < 0)
+ return r;
/* Let's ignore autofs mounts. If they aren't
* triggered yet, we want to avoid triggering
@@ -7184,23 +5069,6 @@ int tempfn_random_child(const char *p, char **ret) {
return 0;
}
-/* make sure the hostname is not "localhost" */
-bool is_localhost(const char *hostname) {
- assert(hostname);
-
- /* This tries to identify local host and domain names
- * described in RFC6761 plus the redhatism of .localdomain */
-
- return streq(hostname, "localhost") ||
- streq(hostname, "localhost.") ||
- streq(hostname, "localdomain.") ||
- streq(hostname, "localdomain") ||
- endswith(hostname, ".localhost") ||
- endswith(hostname, ".localhost.") ||
- endswith(hostname, ".localdomain") ||
- endswith(hostname, ".localdomain.");
-}
-
int take_password_lock(const char *root) {
struct flock flock = {
@@ -7264,9 +5132,19 @@ int is_dir(const char* path, bool follow) {
return !!S_ISDIR(st.st_mode);
}
-int unquote_first_word(const char **p, char **ret, bool relax) {
+int is_device_node(const char *path) {
+ struct stat info;
+
+ if (lstat(path, &info) < 0)
+ return -errno;
+
+ return !!(S_ISBLK(info.st_mode) || S_ISCHR(info.st_mode));
+}
+
+int unquote_first_word(const char **p, char **ret, UnquoteFlags flags) {
_cleanup_free_ char *s = NULL;
size_t allocated = 0, sz = 0;
+ int r;
enum {
START,
@@ -7324,22 +5202,36 @@ int unquote_first_word(const char **p, char **ret, bool relax) {
case VALUE_ESCAPE:
if (c == 0) {
- if (relax)
+ if (flags & UNQUOTE_RELAX)
goto finish;
return -EINVAL;
}
- if (!GREEDY_REALLOC(s, allocated, sz+2))
+ if (!GREEDY_REALLOC(s, allocated, sz+7))
return -ENOMEM;
- s[sz++] = c;
- state = VALUE;
+ if (flags & UNQUOTE_CUNESCAPE) {
+ uint32_t u;
+
+ r = cunescape_one(*p, (size_t) -1, &c, &u);
+ if (r < 0)
+ return -EINVAL;
+ (*p) += r - 1;
+
+ if (c != 0)
+ s[sz++] = c; /* normal explicit char */
+ else
+ sz += utf8_encode_unichar(s + sz, u); /* unicode chars we'll encode as utf8 */
+ } else
+ s[sz++] = c;
+
+ state = VALUE;
break;
case SINGLE_QUOTE:
if (c == 0) {
- if (relax)
+ if (flags & UNQUOTE_RELAX)
goto finish;
return -EINVAL;
} else if (c == '\'')
@@ -7357,15 +5249,30 @@ int unquote_first_word(const char **p, char **ret, bool relax) {
case SINGLE_QUOTE_ESCAPE:
if (c == 0) {
- if (relax)
+ if (flags & UNQUOTE_RELAX)
goto finish;
return -EINVAL;
}
- if (!GREEDY_REALLOC(s, allocated, sz+2))
+ if (!GREEDY_REALLOC(s, allocated, sz+7))
return -ENOMEM;
- s[sz++] = c;
+ if (flags & UNQUOTE_CUNESCAPE) {
+ uint32_t u;
+
+ r = cunescape_one(*p, (size_t) -1, &c, &u);
+ if (r < 0)
+ return -EINVAL;
+
+ (*p) += r - 1;
+
+ if (c != 0)
+ s[sz++] = c;
+ else
+ sz += utf8_encode_unichar(s + sz, u);
+ } else
+ s[sz++] = c;
+
state = SINGLE_QUOTE;
break;
@@ -7387,15 +5294,30 @@ int unquote_first_word(const char **p, char **ret, bool relax) {
case DOUBLE_QUOTE_ESCAPE:
if (c == 0) {
- if (relax)
+ if (flags & UNQUOTE_RELAX)
goto finish;
return -EINVAL;
}
- if (!GREEDY_REALLOC(s, allocated, sz+2))
+ if (!GREEDY_REALLOC(s, allocated, sz+7))
return -ENOMEM;
- s[sz++] = c;
+ if (flags & UNQUOTE_CUNESCAPE) {
+ uint32_t u;
+
+ r = cunescape_one(*p, (size_t) -1, &c, &u);
+ if (r < 0)
+ return -EINVAL;
+
+ (*p) += r - 1;
+
+ if (c != 0)
+ s[sz++] = c;
+ else
+ sz += utf8_encode_unichar(s + sz, u);
+ } else
+ s[sz++] = c;
+
state = DOUBLE_QUOTE;
break;
@@ -7424,7 +5346,7 @@ finish:
return 1;
}
-int unquote_many_words(const char **p, ...) {
+int unquote_many_words(const char **p, UnquoteFlags flags, ...) {
va_list ap;
char **l;
int n = 0, i, c, r;
@@ -7435,7 +5357,7 @@ int unquote_many_words(const char **p, ...) {
assert(p);
/* Count how many words are expected */
- va_start(ap, p);
+ va_start(ap, flags);
for (;;) {
if (!va_arg(ap, char **))
break;
@@ -7450,7 +5372,7 @@ int unquote_many_words(const char **p, ...) {
l = newa0(char*, n);
for (c = 0; c < n; c++) {
- r = unquote_first_word(p, &l[c], false);
+ r = unquote_first_word(p, &l[c], flags);
if (r < 0) {
int j;
@@ -7466,7 +5388,7 @@ int unquote_many_words(const char **p, ...) {
/* If we managed to parse all words, return them in the passed
* in parameters */
- va_start(ap, p);
+ va_start(ap, flags);
for (i = 0; i < n; i++) {
char **v;
@@ -7488,6 +5410,9 @@ int free_and_strdup(char **p, const char *s) {
/* Replaces a string pointer with an strdup()ed new string,
* possibly freeing the old one. */
+ if (streq_ptr(*p, s))
+ return 0;
+
if (s) {
t = strdup(s);
if (!t)
@@ -7498,26 +5423,6 @@ int free_and_strdup(char **p, const char *s) {
free(*p);
*p = t;
- return 0;
-}
-
-int sethostname_idempotent(const char *s) {
- int r;
- char buf[HOST_NAME_MAX + 1] = {};
-
- assert(s);
-
- r = gethostname(buf, sizeof(buf));
- if (r < 0)
- return -errno;
-
- if (streq(buf, s))
- return 0;
-
- r = sethostname(s, strlen(s));
- if (r < 0)
- return -errno;
-
return 1;
}
@@ -7779,7 +5684,7 @@ int same_fd(int a, int b) {
/* The fds refer to the same inode on disk, let's also check
* if they have the same fd flags. This is useful to
- * distuingish the read and write side of a pipe created with
+ * distinguish the read and write side of a pipe created with
* pipe(). */
fa = fcntl(a, F_GETFL);
if (fa < 0)
@@ -7792,32 +5697,41 @@ int same_fd(int a, int b) {
return fa == fb;
}
-int chattr_fd(int fd, bool b, unsigned mask) {
+int chattr_fd(int fd, unsigned value, unsigned mask) {
unsigned old_attr, new_attr;
+ struct stat st;
assert(fd >= 0);
+ if (fstat(fd, &st) < 0)
+ return -errno;
+
+ /* Explicitly check whether this is a regular file or
+ * directory. If it is anything else (such as a device node or
+ * fifo), then the ioctl will not hit the file systems but
+ * possibly drivers, where the ioctl might have different
+ * effects. Notably, DRM is using the same ioctl() number. */
+
+ if (!S_ISDIR(st.st_mode) && !S_ISREG(st.st_mode))
+ return -ENOTTY;
+
if (mask == 0)
return 0;
if (ioctl(fd, FS_IOC_GETFLAGS, &old_attr) < 0)
return -errno;
- if (b)
- new_attr = old_attr | mask;
- else
- new_attr = old_attr & ~mask;
-
+ new_attr = (old_attr & ~mask) | (value & mask);
if (new_attr == old_attr)
return 0;
if (ioctl(fd, FS_IOC_SETFLAGS, &new_attr) < 0)
return -errno;
- return 0;
+ return 1;
}
-int chattr_path(const char *p, bool b, unsigned mask) {
+int chattr_path(const char *p, unsigned value, unsigned mask) {
_cleanup_close_ int fd = -1;
assert(p);
@@ -7829,12 +5743,20 @@ int chattr_path(const char *p, bool b, unsigned mask) {
if (fd < 0)
return -errno;
- return chattr_fd(fd, b, mask);
+ return chattr_fd(fd, value, mask);
}
int read_attr_fd(int fd, unsigned *ret) {
+ struct stat st;
+
assert(fd >= 0);
+ if (fstat(fd, &st) < 0)
+ return -errno;
+
+ if (!S_ISDIR(st.st_mode) && !S_ISREG(st.st_mode))
+ return -ENOTTY;
+
if (ioctl(fd, FS_IOC_GETFLAGS, ret) < 0)
return -errno;
@@ -7854,128 +5776,6 @@ int read_attr_path(const char *p, unsigned *ret) {
return read_attr_fd(fd, ret);
}
-int make_lock_file(const char *p, int operation, LockFile *ret) {
- _cleanup_close_ int fd = -1;
- _cleanup_free_ char *t = NULL;
- int r;
-
- /*
- * We use UNPOSIX locks if they are available. They have nice
- * semantics, and are mostly compatible with NFS. However,
- * they are only available on new kernels. When we detect we
- * are running on an older kernel, then we fall back to good
- * old BSD locks. They also have nice semantics, but are
- * slightly problematic on NFS, where they are upgraded to
- * POSIX locks, even though locally they are orthogonal to
- * POSIX locks.
- */
-
- t = strdup(p);
- if (!t)
- return -ENOMEM;
-
- for (;;) {
- struct flock fl = {
- .l_type = (operation & ~LOCK_NB) == LOCK_EX ? F_WRLCK : F_RDLCK,
- .l_whence = SEEK_SET,
- };
- struct stat st;
-
- fd = open(p, O_CREAT|O_RDWR|O_NOFOLLOW|O_CLOEXEC|O_NOCTTY, 0600);
- if (fd < 0)
- return -errno;
-
- r = fcntl(fd, (operation & LOCK_NB) ? F_OFD_SETLK : F_OFD_SETLKW, &fl);
- if (r < 0) {
-
- /* If the kernel is too old, use good old BSD locks */
- if (errno == EINVAL)
- r = flock(fd, operation);
-
- if (r < 0)
- return errno == EAGAIN ? -EBUSY : -errno;
- }
-
- /* If we acquired the lock, let's check if the file
- * still exists in the file system. If not, then the
- * previous exclusive owner removed it and then closed
- * it. In such a case our acquired lock is worthless,
- * hence try again. */
-
- r = fstat(fd, &st);
- if (r < 0)
- return -errno;
- if (st.st_nlink > 0)
- break;
-
- fd = safe_close(fd);
- }
-
- ret->path = t;
- ret->fd = fd;
- ret->operation = operation;
-
- fd = -1;
- t = NULL;
-
- return r;
-}
-
-int make_lock_file_for(const char *p, int operation, LockFile *ret) {
- const char *fn;
- char *t;
-
- assert(p);
- assert(ret);
-
- fn = basename(p);
- if (!filename_is_valid(fn))
- return -EINVAL;
-
- t = newa(char, strlen(p) + 2 + 4 + 1);
- stpcpy(stpcpy(stpcpy(mempcpy(t, p, fn - p), ".#"), fn), ".lck");
-
- return make_lock_file(t, operation, ret);
-}
-
-void release_lock_file(LockFile *f) {
- int r;
-
- if (!f)
- return;
-
- if (f->path) {
-
- /* If we are the exclusive owner we can safely delete
- * the lock file itself. If we are not the exclusive
- * owner, we can try becoming it. */
-
- if (f->fd >= 0 &&
- (f->operation & ~LOCK_NB) == LOCK_SH) {
- static const struct flock fl = {
- .l_type = F_WRLCK,
- .l_whence = SEEK_SET,
- };
-
- r = fcntl(f->fd, F_OFD_SETLK, &fl);
- if (r < 0 && errno == EINVAL)
- r = flock(f->fd, LOCK_EX|LOCK_NB);
-
- if (r >= 0)
- f->operation = LOCK_EX|LOCK_NB;
- }
-
- if ((f->operation & ~LOCK_NB) == LOCK_EX)
- unlink_noerrno(f->path);
-
- free(f->path);
- f->path = NULL;
- }
-
- f->fd = safe_close(f->fd);
- f->operation = 0;
-}
-
static size_t nul_length(const uint8_t *p, size_t sz) {
size_t n = 0;
@@ -8102,3 +5902,147 @@ ssize_t string_table_lookup(const char * const *table, size_t len, const char *k
return -1;
}
+
+void cmsg_close_all(struct msghdr *mh) {
+ struct cmsghdr *cmsg;
+
+ assert(mh);
+
+ for (cmsg = CMSG_FIRSTHDR(mh); cmsg; cmsg = CMSG_NXTHDR(mh, cmsg))
+ if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS)
+ close_many((int*) CMSG_DATA(cmsg), (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int));
+}
+
+int rename_noreplace(int olddirfd, const char *oldpath, int newdirfd, const char *newpath) {
+ struct stat buf;
+ int ret;
+
+ ret = renameat2(olddirfd, oldpath, newdirfd, newpath, RENAME_NOREPLACE);
+ if (ret >= 0)
+ return 0;
+
+ /* Even though renameat2() exists since Linux 3.15, btrfs added
+ * support for it later. If it is not implemented, fallback to another
+ * method. */
+ if (errno != EINVAL)
+ return -errno;
+
+ /* The link()/unlink() fallback does not work on directories. But
+ * renameat() without RENAME_NOREPLACE gives the same semantics on
+ * directories, except when newpath is an *empty* directory. This is
+ * good enough. */
+ ret = fstatat(olddirfd, oldpath, &buf, AT_SYMLINK_NOFOLLOW);
+ if (ret >= 0 && S_ISDIR(buf.st_mode)) {
+ ret = renameat(olddirfd, oldpath, newdirfd, newpath);
+ return ret >= 0 ? 0 : -errno;
+ }
+
+ /* If it is not a directory, use the link()/unlink() fallback. */
+ ret = linkat(olddirfd, oldpath, newdirfd, newpath, 0);
+ if (ret < 0)
+ return -errno;
+
+ ret = unlinkat(olddirfd, oldpath, 0);
+ if (ret < 0) {
+ /* backup errno before the following unlinkat() alters it */
+ ret = errno;
+ (void) unlinkat(newdirfd, newpath, 0);
+ errno = ret;
+ return -errno;
+ }
+
+ return 0;
+}
+
+char *shell_maybe_quote(const char *s) {
+ const char *p;
+ char *r, *t;
+
+ assert(s);
+
+ /* Encloses a string in double quotes if necessary to make it
+ * OK as shell string. */
+
+ for (p = s; *p; p++)
+ if (*p <= ' ' ||
+ *p >= 127 ||
+ strchr(SHELL_NEED_QUOTES, *p))
+ break;
+
+ if (!*p)
+ return strdup(s);
+
+ r = new(char, 1+strlen(s)*2+1+1);
+ if (!r)
+ return NULL;
+
+ t = r;
+ *(t++) = '"';
+ t = mempcpy(t, s, p - s);
+
+ for (; *p; p++) {
+
+ if (strchr(SHELL_NEED_ESCAPE, *p))
+ *(t++) = '\\';
+
+ *(t++) = *p;
+ }
+
+ *(t++)= '"';
+ *t = 0;
+
+ return r;
+}
+
+int parse_mode(const char *s, mode_t *ret) {
+ char *x;
+ long l;
+
+ assert(s);
+ assert(ret);
+
+ errno = 0;
+ l = strtol(s, &x, 8);
+ if (errno != 0)
+ return -errno;
+
+ if (!x || x == s || *x)
+ return -EINVAL;
+ if (l < 0 || l > 07777)
+ return -ERANGE;
+
+ *ret = (mode_t) l;
+ return 0;
+}
+
+int mount_move_root(const char *path) {
+ assert(path);
+
+ if (chdir(path) < 0)
+ return -errno;
+
+ if (mount(path, "/", NULL, MS_MOVE, NULL) < 0)
+ return -errno;
+
+ if (chroot(".") < 0)
+ return -errno;
+
+ if (chdir("/") < 0)
+ return -errno;
+
+ return 0;
+}
+
+int reset_uid_gid(void) {
+
+ if (setgroups(0, NULL) < 0)
+ return -errno;
+
+ if (setresgid(0, 0, 0) < 0)
+ return -errno;
+
+ if (setresuid(0, 0, 0) < 0)
+ return -errno;
+
+ return 0;
+}
diff --git a/src/shared/util.h b/src/shared/util.h
index a83b588221..467ae234a0 100644
--- a/src/shared/util.h
+++ b/src/shared/util.h
@@ -25,69 +25,27 @@
#include <fcntl.h>
#include <inttypes.h>
#include <time.h>
-#include <sys/time.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
-#include <signal.h>
#include <sched.h>
#include <limits.h>
#include <sys/types.h>
+#include <sys/socket.h>
#include <sys/stat.h>
#include <dirent.h>
-#include <sys/resource.h>
#include <stddef.h>
#include <unistd.h>
#include <locale.h>
#include <mntent.h>
-#include <sys/socket.h>
#include <sys/inotify.h>
-
-#if SIZEOF_PID_T == 4
-# define PID_PRI PRIi32
-#elif SIZEOF_PID_T == 2
-# define PID_PRI PRIi16
-#else
-# error Unknown pid_t size
-#endif
-#define PID_FMT "%" PID_PRI
-
-#if SIZEOF_UID_T == 4
-# define UID_FMT "%" PRIu32
-#elif SIZEOF_UID_T == 2
-# define UID_FMT "%" PRIu16
-#else
-# error Unknown uid_t size
-#endif
-
-#if SIZEOF_GID_T == 4
-# define GID_FMT "%" PRIu32
-#elif SIZEOF_GID_T == 2
-# define GID_FMT "%" PRIu16
-#else
-# error Unknown gid_t size
-#endif
-
-#if SIZEOF_TIME_T == 8
-# define PRI_TIME PRIi64
-#elif SIZEOF_TIME_T == 4
-# define PRI_TIME PRIu32
-#else
-# error Unknown time_t size
-#endif
-
-#if SIZEOF_RLIM_T == 8
-# define RLIM_FMT "%" PRIu64
-#elif SIZEOF_RLIM_T == 4
-# define RLIM_FMT "%" PRIu32
-#else
-# error Unknown rlim_t size
-#endif
+#include <sys/statfs.h>
#include "macro.h"
#include "missing.h"
#include "time-util.h"
+#include "formats-util.h"
/* What is interpreted as whitespace? */
#define WHITESPACE " \t\n\r"
@@ -104,16 +62,6 @@
#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_BLUE_ON "\x1B[1;34m"
-#define ANSI_HIGHLIGHT_OFF "\x1B[0m"
-#define ANSI_ERASE_TO_END_OF_LINE "\x1B[K"
-
size_t page_size(void) _pure_;
#define PAGE_ALIGN(l) ALIGN_TO((l), page_size())
@@ -185,6 +133,7 @@ static inline char *startswith_no_case(const char *s, const char *prefix) {
}
char *endswith(const char *s, const char *postfix) _pure_;
+char *endswith_no_case(const char *s, const char *postfix) _pure_;
char *first_word(const char *s, const char *word) _pure_;
@@ -268,23 +217,15 @@ const char* split(const char **state, size_t *l, const char *separator, bool quo
#define _FOREACH_WORD(word, length, s, separator, quoted, state) \
for ((state) = (s), (word) = split(&(state), &(length), (separator), (quoted)); (word); (word) = split(&(state), &(length), (separator), (quoted)))
-pid_t get_parent_of_pid(pid_t pid, pid_t *ppid);
-
char *strappend(const char *s, const char *suffix);
char *strnappend(const char *s, const char *suffix, size_t length);
-char *replace_env(const char *format, char **env);
-char **replace_env_argv(char **argv, char **env);
-
int readlinkat_malloc(int fd, const char *p, char **ret);
int readlink_malloc(const char *p, char **r);
int readlink_value(const char *p, char **ret);
int readlink_and_make_absolute(const char *p, char **r);
int readlink_and_canonicalize(const char *p, char **r);
-int reset_all_signal_handlers(void);
-int reset_signal_mask(void);
-
char *strstrip(char *s);
char *delete_chars(char *s, const char *bad);
char *truncate_nl(char *s);
@@ -293,17 +234,6 @@ char *file_in_same_dir(const char *path, const char *filename);
int rmdir_parents(const char *path, const char *stop);
-int get_process_state(pid_t pid);
-int get_process_comm(pid_t pid, char **name);
-int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char **line);
-int get_process_exe(pid_t pid, char **name);
-int get_process_uid(pid_t pid, uid_t *uid);
-int get_process_gid(pid_t pid, gid_t *gid);
-int get_process_capeff(pid_t pid, char **capeff);
-int get_process_cwd(pid_t pid, char **cwd);
-int get_process_root(pid_t pid, char **root);
-int get_process_environ(pid_t pid, char **environ);
-
char hexchar(int x) _const_;
int unhexchar(char c) _const_;
char octchar(int x) _const_;
@@ -312,9 +242,15 @@ char decchar(int x) _const_;
int undecchar(char c) _const_;
char *cescape(const char *s);
-char *cunescape(const char *s);
-char *cunescape_length(const char *s, size_t length);
-char *cunescape_length_with_prefix(const char *s, size_t length, const char *prefix);
+size_t cescape_char(char c, char *buf);
+
+typedef enum UnescapeFlags {
+ UNESCAPE_RELAX = 1,
+} UnescapeFlags;
+
+int cunescape(const char *s, UnescapeFlags flags, char **ret);
+int cunescape_length(const char *s, size_t length, UnescapeFlags flags, char **ret);
+int cunescape_length_with_prefix(const char *s, size_t length, const char *prefix, UnescapeFlags flags, char **ret);
char *xescape(const char *s, const char *bad);
@@ -327,26 +263,6 @@ bool hidden_file(const char *filename) _pure_;
bool chars_intersect(const char *a, const char *b) _pure_;
-int make_stdio(int fd);
-int make_null_stdio(void);
-int make_console_stdio(void);
-
-int dev_urandom(void *p, size_t n);
-void random_bytes(void *p, size_t n);
-void initialize_srand(void);
-
-static inline uint64_t random_u64(void) {
- uint64_t u;
- random_bytes(&u, sizeof(u));
- return u;
-}
-
-static inline uint32_t random_u32(void) {
- uint32_t u;
- random_bytes(&u, sizeof(u));
- return u;
-}
-
/* For basic lookup tables with strictly enumerated entries */
#define _DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type,scope) \
scope const char *name##_to_string(type i) { \
@@ -412,28 +328,12 @@ int close_all_fds(const int except[], unsigned n_except);
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(char *ret, const char *replies, const char *text, ...) _printf_(3, 4);
-int ask_string(char **ret, const char *text, ...) _printf_(2, 3);
-
-int reset_terminal_fd(int fd, bool switch_to_text);
-int reset_terminal(const char *name);
-
-int open_terminal(const char *name, int mode);
-int acquire_terminal(const char *name, bool fail, bool force, bool ignore_tiocstty_eperm, usec_t timeout);
-int release_terminal(void);
-
int flush_fd(int fd);
-int ignore_signals(int sig, ...);
-int default_signals(int sig, ...);
-int sigaction_many(const struct sigaction *sa, ...);
-
int fopen_temporary(const char *path, FILE **_f, char **_temp_path);
ssize_t loop_read(int fd, void *buf, size_t nbytes, bool do_poll);
+int loop_read_exact(int fd, void *buf, size_t nbytes, bool do_poll);
int loop_write(int fd, const void *buf, size_t nbytes, bool do_poll);
bool is_device_path(const char *path);
@@ -441,75 +341,22 @@ 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]);
-
-void sigset_add_many(sigset_t *ss, ...);
-int sigprocmask_many(int how, ...);
-
-bool hostname_is_set(void);
-
char* lookup_uid(uid_t uid);
-char* gethostname_malloc(void);
char* getlogname_malloc(void);
char* getusername_malloc(void);
-int getttyname_malloc(int fd, char **r);
-int getttyname_harder(int fd, char **r);
-
-int get_ctty_devnr(pid_t pid, dev_t *d);
-int get_ctty(pid_t, dev_t *_devnr, char **r);
-
int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid);
int fchmod_and_fchown(int fd, mode_t mode, uid_t uid, gid_t gid);
-int is_fd_on_temporary_fs(int fd);
-
-int rm_rf_children(int fd, bool only_dirs, bool honour_sticky, struct stat *root_dev);
-int rm_rf_children_dangerous(int fd, bool only_dirs, bool honour_sticky, struct stat *root_dev);
-int rm_rf(const char *path, bool only_dirs, bool delete_root, bool honour_sticky);
-int rm_rf_dangerous(const char *path, bool only_dirs, bool delete_root, bool honour_sticky);
+bool is_temporary_fs(const struct statfs *s) _pure_;
+int fd_is_temporary_fs(int fd);
int pipe_eof(int fd);
cpu_set_t* cpu_set_malloc(unsigned *ncpus);
-int status_vprintf(const char *status, bool ellipse, bool ephemeral, const char *format, va_list ap) _printf_(4,0);
-int status_printf(const char *status, bool ellipse, bool ephemeral, const char *format, ...) _printf_(4,5);
-
#define xsprintf(buf, fmt, ...) assert_se((size_t) snprintf(buf, ELEMENTSOF(buf), fmt, __VA_ARGS__) < ELEMENTSOF(buf))
-int fd_columns(int fd);
-unsigned columns(void);
-int fd_lines(int fd);
-unsigned lines(void);
-void columns_lines_cache_reset(int _unused_ signum);
-
-bool on_tty(void);
-
-static inline const char *ansi_highlight(void) {
- return on_tty() ? ANSI_HIGHLIGHT_ON : "";
-}
-
-static inline const char *ansi_highlight_red(void) {
- return on_tty() ? ANSI_HIGHLIGHT_RED_ON : "";
-}
-
-static inline const char *ansi_highlight_green(void) {
- return on_tty() ? ANSI_HIGHLIGHT_GREEN_ON : "";
-}
-
-static inline const char *ansi_highlight_yellow(void) {
- return on_tty() ? ANSI_HIGHLIGHT_YELLOW_ON : "";
-}
-
-static inline const char *ansi_highlight_blue(void) {
- return on_tty() ? ANSI_HIGHLIGHT_BLUE_ON : "";
-}
-
-static inline const char *ansi_highlight_off(void) {
- return on_tty() ? ANSI_HIGHLIGHT_OFF : "";
-}
-
int files_same(const char *filea, const char *fileb);
int running_in_chroot(void);
@@ -521,12 +368,6 @@ char *ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigne
int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gid, mode_t mode);
int touch(const char *path);
-char *unquote(const char *s, const char *quotes);
-char *normalize_env_assignment(const char *s);
-
-int wait_for_terminate(pid_t pid, siginfo_t *status);
-int wait_for_terminate_and_warn(const char *name, pid_t pid, bool check_exit_code);
-
noreturn void freeze(void);
bool null_or_empty(struct stat *st) _pure_;
@@ -537,32 +378,17 @@ 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) _pure_;
-int vtnr_from_tty(const char *tty);
-const char *default_term_for_tty(const char *tty);
-
void execute_directories(const char* const* directories, usec_t timeout, char *argv[]);
-int kill_and_sigcont(pid_t pid, int sig);
-
bool nulstr_contains(const char*nulstr, const char *needle);
bool plymouth_running(void);
-bool hostname_is_valid(const char *s) _pure_;
-char* hostname_cleanup(char *s, bool lowercase);
-
bool machine_name_is_valid(const char *s) _pure_;
char* strshorten(char *s, size_t l);
-int terminal_vhangup_fd(int fd);
-int terminal_vhangup(const char *name);
-
-int vt_disallocate(const char *name);
+int symlink_idempotent(const char *from, const char *to);
int symlink_atomic(const char *from, const char *to);
int mknod_atomic(const char *path, mode_t mode, dev_t dev);
@@ -628,11 +454,6 @@ 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) _const_;
-int signal_from_string(const char *s) _pure_;
-
-int signal_from_string_try_harder(const char *s);
-
extern int saved_argc;
extern char **saved_argv;
@@ -646,8 +467,6 @@ int fd_wait_for_event(int fd, int event, usec_t timeout);
void* memdup(const void *p, size_t l) _alloc_(2);
-int is_kernel_thread(pid_t pid);
-
int fd_inc_sndbuf(int fd, size_t n);
int fd_inc_rcvbuf(int fd, size_t n);
@@ -655,8 +474,6 @@ int fork_agent(pid_t *pid, const int except[], unsigned n_except, const char *pa
int setrlimit_closest(int resource, const struct rlimit *rlim);
-int getenv_for_pid(pid_t pid, const char *field, char **_value);
-
bool http_url_is_valid(const char *url) _pure_;
bool documentation_url_is_valid(const char *url) _pure_;
@@ -664,8 +481,6 @@ bool http_etag_is_valid(const char *etag);
bool in_initrd(void);
-void warn_melody(void);
-
int get_home_dir(char **ret);
int get_shell(char **_ret);
@@ -737,6 +552,8 @@ void *xbsearch_r(const void *key, const void *base, size_t nmemb, size_t size,
int (*compar) (const void *, const void *, void *),
void *arg);
+#define _(String) gettext (String)
+void init_gettext(void);
bool is_locale_utf8(void);
typedef enum DrawSpecialChar {
@@ -931,19 +748,6 @@ int unlink_noerrno(const char *path);
_d_; \
})
-#define procfs_file_alloca(pid, field) \
- ({ \
- pid_t _pid_ = (pid); \
- const char *_r_; \
- if (_pid_ == 0) { \
- _r_ = ("/proc/self/" field); \
- } else { \
- _r_ = alloca(strlen("/proc/") + DECIMAL_STR_MAX(pid_t) + 1 + sizeof(field)); \
- sprintf((char*) _r_, "/proc/"PID_FMT"/" field, _pid_); \
- } \
- _r_; \
- })
-
bool id128_is_valid(const char *s) _pure_;
int split_pair(const char *s, const char *sep, char **l, char **r);
@@ -954,12 +758,27 @@ int shall_restore_state(void);
* Normal qsort requires base to be nonnull. Here were require
* that only if nmemb > 0.
*/
-static inline void qsort_safe(void *base, size_t nmemb, size_t size,
- int (*compar)(const void *, const void *)) {
- if (nmemb) {
- assert(base);
- qsort(base, nmemb, size, compar);
- }
+static inline void qsort_safe(void *base, size_t nmemb, size_t size, comparison_fn_t compar) {
+ if (nmemb <= 1)
+ return;
+
+ assert(base);
+ qsort(base, nmemb, size, compar);
+}
+
+/* Normal memmem() requires haystack to be nonnull, which is annoying for zero-length buffers */
+static inline void *memmem_safe(const void *haystack, size_t haystacklen, const void *needle, size_t needlelen) {
+
+ if (needlelen <= 0)
+ return (void*) haystack;
+
+ if (haystacklen < needlelen)
+ return NULL;
+
+ assert(haystack);
+ assert(needle);
+
+ return memmem(haystack, haystacklen, needle, needlelen);
}
int proc_cmdline(char **ret);
@@ -971,9 +790,6 @@ int container_get_leader(const char *machine, pid_t *pid);
int namespace_open(pid_t pid, int *pidns_fd, int *mntns_fd, int *netns_fd, int *root_fd);
int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int root_fd);
-bool pid_is_alive(pid_t pid);
-bool pid_is_unwaited(pid_t pid);
-
int getpeercred(int fd, struct ucred *ucred);
int getpeersec(int fd, char **ret);
@@ -984,6 +800,13 @@ int open_tmpfile(const char *path, int flags);
int fd_warn_permissions(const char *path, int fd);
+#ifndef PERSONALITY_INVALID
+/* personality(7) documents that 0xffffffffUL is used for querying the
+ * current personality, hence let's use that here as error
+ * indicator. */
+#define PERSONALITY_INVALID 0xffffffffLU
+#endif
+
unsigned long personality_from_string(const char *p);
const char *personality_to_string(unsigned long);
@@ -1009,19 +832,21 @@ int tempfn_xxxxxx(const char *p, char **ret);
int tempfn_random(const char *p, char **ret);
int tempfn_random_child(const char *p, char **ret);
-bool is_localhost(const char *hostname);
-
int take_password_lock(const char *root);
int is_symlink(const char *path);
int is_dir(const char *path, bool follow);
+int is_device_node(const char *path);
-int unquote_first_word(const char **p, char **ret, bool relax);
-int unquote_many_words(const char **p, ...) _sentinel_;
+typedef enum UnquoteFlags {
+ UNQUOTE_RELAX = 1,
+ UNQUOTE_CUNESCAPE = 2,
+} UnquoteFlags;
-int free_and_strdup(char **p, const char *s);
+int unquote_first_word(const char **p, char **ret, UnquoteFlags flags);
+int unquote_many_words(const char **p, UnquoteFlags flags, ...) _sentinel_;
-int sethostname_idempotent(const char *s);
+int free_and_strdup(char **p, const char *s);
#define INOTIFY_EVENT_MAX (sizeof(struct inotify_event) + NAME_MAX + 1)
@@ -1050,26 +875,12 @@ int fd_getcrtime_at(int dirfd, const char *name, usec_t *usec, int flags);
int same_fd(int a, int b);
-int chattr_fd(int fd, bool b, unsigned mask);
-int chattr_path(const char *p, bool b, unsigned mask);
+int chattr_fd(int fd, unsigned value, unsigned mask);
+int chattr_path(const char *p, unsigned value, unsigned mask);
int read_attr_fd(int fd, unsigned *ret);
int read_attr_path(const char *p, unsigned *ret);
-typedef struct LockFile {
- char *path;
- int fd;
- int operation;
-} LockFile;
-
-int make_lock_file(const char *p, int operation, LockFile *ret);
-int make_lock_file_for(const char *p, int operation, LockFile *ret);
-void release_lock_file(LockFile *f);
-
-#define _cleanup_release_lock_file_ _cleanup_(release_lock_file)
-
-#define LOCK_FILE_INIT { .fd = -1, .path = NULL }
-
#define RLIMIT_MAKE_CONST(lim) ((struct rlimit) { lim, lim })
ssize_t sparse_write(int fd, const void *p, size_t sz, size_t run_length);
@@ -1078,3 +889,15 @@ void sigkill_wait(pid_t *pid);
#define _cleanup_sigkill_wait_ _cleanup_(sigkill_wait)
int syslog_parse_priority(const char **p, int *priority, bool with_facility);
+
+void cmsg_close_all(struct msghdr *mh);
+
+int rename_noreplace(int olddirfd, const char *oldpath, int newdirfd, const char *newpath);
+
+char *shell_maybe_quote(const char *s);
+
+int parse_mode(const char *s, mode_t *ret);
+
+int mount_move_root(const char *path);
+
+int reset_uid_gid(void);
diff --git a/src/shared/utmp-wtmp.c b/src/shared/utmp-wtmp.c
index bdb962af34..8f66df7718 100644
--- a/src/shared/utmp-wtmp.c
+++ b/src/shared/utmp-wtmp.c
@@ -21,7 +21,6 @@
#include <utmpx.h>
#include <errno.h>
-#include <assert.h>
#include <string.h>
#include <sys/utsname.h>
#include <fcntl.h>
@@ -30,6 +29,8 @@
#include "macro.h"
#include "path-util.h"
+#include "terminal-util.h"
+#include "hostname-util.h"
#include "utmp-wtmp.h"
int utmp_get_runlevel(int *runlevel, int *previous) {
@@ -347,8 +348,14 @@ static int write_to_terminal(const char *tty, const char *message) {
return 0;
}
-int utmp_wall(const char *message, const char *username, bool (*match_tty)(const char *tty)) {
- _cleanup_free_ char *text = NULL, *hn = NULL, *un = NULL, *tty = NULL;
+int utmp_wall(
+ const char *message,
+ const char *username,
+ const char *origin_tty,
+ bool (*match_tty)(const char *tty, void *userdata),
+ void *userdata) {
+
+ _cleanup_free_ char *text = NULL, *hn = NULL, *un = NULL, *stdin_tty = NULL;
char date[FORMAT_TIMESTAMP_MAX];
struct utmpx *u;
int r;
@@ -362,14 +369,17 @@ int utmp_wall(const char *message, const char *username, bool (*match_tty)(const
return -ENOMEM;
}
- getttyname_harder(STDIN_FILENO, &tty);
+ if (!origin_tty) {
+ getttyname_harder(STDIN_FILENO, &stdin_tty);
+ origin_tty = stdin_tty;
+ }
if (asprintf(&text,
"\a\r\n"
"Broadcast message from %s@%s%s%s (%s):\r\n\r\n"
"%s\r\n\r\n",
un ?: username, hn,
- tty ? " on " : "", strempty(tty),
+ origin_tty ? " on " : "", strempty(origin_tty),
format_timestamp(date, sizeof(date), now(CLOCK_REALTIME)),
message) < 0)
return -ENOMEM;
@@ -396,7 +406,7 @@ int utmp_wall(const char *message, const char *username, bool (*match_tty)(const
path = buf;
}
- if (!match_tty || match_tty(path)) {
+ if (!match_tty || match_tty(path, userdata)) {
q = write_to_terminal(path, text);
if (q < 0)
r = q;
diff --git a/src/shared/utmp-wtmp.h b/src/shared/utmp-wtmp.h
index 87d004e615..5d26ba6fb1 100644
--- a/src/shared/utmp-wtmp.h
+++ b/src/shared/utmp-wtmp.h
@@ -33,7 +33,12 @@ int utmp_put_runlevel(int runlevel, int previous);
int utmp_put_dead_process(const char *id, pid_t pid, int code, int status);
int utmp_put_init_process(const char *id, pid_t pid, pid_t sid, const char *line);
-int utmp_wall(const char *message, const char *username, bool (*match_tty)(const char *tty));
+int utmp_wall(
+ const char *message,
+ const char *username,
+ const char *origin_tty,
+ bool (*match_tty)(const char *tty, void *userdata),
+ void *userdata);
#else /* HAVE_UTMP */
@@ -55,8 +60,12 @@ static inline int utmp_put_dead_process(const char *id, pid_t pid, int code, int
static inline int utmp_put_init_process(const char *id, pid_t pid, pid_t sid, const char *line) {
return 0;
}
-static inline int utmp_wall(const char *message, const char *username,
- bool (*match_tty)(const char *tty)) {
+static inline int utmp_wall(
+ const char *message,
+ const char *username,
+ const char *origin_tty,
+ bool (*match_tty)(const char *tty, void *userdata),
+ void *userdata) {
return 0;
}
diff --git a/src/shared/virt.c b/src/shared/virt.c
index 7c1381f4b8..1299a75ed5 100644
--- a/src/shared/virt.c
+++ b/src/shared/virt.c
@@ -24,6 +24,7 @@
#include <unistd.h>
#include "util.h"
+#include "process-util.h"
#include "virt.h"
#include "fileio.h"
@@ -102,15 +103,35 @@ static int detect_vm_cpuid(const char **_id) {
}
static int detect_vm_devicetree(const char **_id) {
-#if defined(__powerpc__) || defined(__powerpc64__)
+#if defined(__arm__) || defined(__aarch64__) || defined(__powerpc__) || defined(__powerpc64__)
_cleanup_free_ char *hvtype = NULL;
int r;
- r = read_one_line_file("/sys/firmware/devicetree/base/hypervisor/compatible", &hvtype);
+ r = read_one_line_file("/proc/device-tree/hypervisor/compatible", &hvtype);
if (r >= 0) {
if (streq(hvtype, "linux,kvm")) {
*_id = "kvm";
return 1;
+ } else if (strstr(hvtype, "xen")) {
+ *_id = "xen";
+ return 1;
+ }
+ } else if (r == -ENOENT) {
+ _cleanup_closedir_ DIR *dir = NULL;
+ struct dirent *dent;
+
+ dir = opendir("/proc/device-tree");
+ if (!dir) {
+ if (errno == ENOENT)
+ return 0;
+ return -errno;
+ }
+
+ FOREACH_DIRENT(dent, dir, return -errno) {
+ if (strstr(dent->d_name, "fw-cfg")) {
+ *_id = "qemu";
+ return 1;
+ }
}
}
#endif
diff --git a/src/shutdownd/shutdownd.c b/src/shutdownd/shutdownd.c
deleted file mode 100644
index 701882b96d..0000000000
--- a/src/shutdownd/shutdownd.c
+++ /dev/null
@@ -1,459 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2010 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser 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 <poll.h>
-#include <sys/types.h>
-#include <sys/timerfd.h>
-#include <assert.h>
-#include <string.h>
-#include <errno.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <stddef.h>
-
-#include "systemd/sd-daemon.h"
-#include "systemd/sd-shutdown.h"
-
-#include "log.h"
-#include "macro.h"
-#include "util.h"
-#include "utmp-wtmp.h"
-#include "mkdir.h"
-#include "fileio.h"
-
-union shutdown_buffer {
- struct sd_shutdown_command command;
- char space[offsetof(struct sd_shutdown_command, wall_message) + LINE_MAX];
-};
-
-static int read_packet(int fd, union shutdown_buffer *_b) {
- 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 = {
- .iov_base = &b,
- .iov_len = sizeof(b) - 1,
- };
- union {
- struct cmsghdr cmsghdr;
- uint8_t buf[CMSG_SPACE(sizeof(struct ucred))];
- } control = {};
- struct msghdr msghdr = {
- .msg_iov = &iovec,
- .msg_iovlen = 1,
- .msg_control = &control,
- .msg_controllen = sizeof(control),
- };
-
- assert(fd >= 0);
- assert(_b);
-
- n = recvmsg(fd, &msghdr, MSG_DONTWAIT);
- if (n <= 0) {
- if (n == 0) {
- log_error("Short read");
- return -EIO;
- }
-
- if (errno == EAGAIN || errno == EINTR)
- return 0;
-
- log_error_errno(errno, "recvmsg(): %m");
- return -errno;
- }
-
- if (msghdr.msg_controllen < CMSG_LEN(sizeof(struct ucred)) ||
- control.cmsghdr.cmsg_level != SOL_SOCKET ||
- control.cmsghdr.cmsg_type != SCM_CREDENTIALS ||
- control.cmsghdr.cmsg_len != CMSG_LEN(sizeof(struct ucred))) {
- log_warning("Received message without credentials. Ignoring.");
- return 0;
- }
-
- ucred = (struct ucred*) CMSG_DATA(&control.cmsghdr);
- if (ucred->uid != 0) {
- log_warning("Got request from unprivileged user. Ignoring.");
- return 0;
- }
-
- if ((size_t) n < offsetof(struct sd_shutdown_command, wall_message)) {
- log_warning("Message has invalid size. Ignoring.");
- return 0;
- }
-
- if (b.command.mode != SD_SHUTDOWN_NONE &&
- b.command.mode != SD_SHUTDOWN_REBOOT &&
- b.command.mode != SD_SHUTDOWN_POWEROFF &&
- b.command.mode != SD_SHUTDOWN_HALT &&
- b.command.mode != SD_SHUTDOWN_KEXEC) {
- log_warning("Message has invalid mode. Ignoring.");
- return 0;
- }
-
- b.space[n] = 0;
-
- *_b = b;
- return 1;
-}
-
-static void warn_wall(usec_t n, struct sd_shutdown_command *c) {
- char date[FORMAT_TIMESTAMP_MAX];
- const char *prefix;
- _cleanup_free_ char *l = NULL;
-
- assert(c);
- assert(c->warn_wall);
-
- if (n >= c->usec)
- return;
-
- if (c->mode == SD_SHUTDOWN_HALT)
- prefix = "The system is going down for system halt at ";
- else if (c->mode == SD_SHUTDOWN_POWEROFF)
- prefix = "The system is going down for power-off at ";
- else if (c->mode == SD_SHUTDOWN_REBOOT)
- prefix = "The system is going down for reboot at ";
- else if (c->mode == SD_SHUTDOWN_KEXEC)
- prefix = "The system is going down for kexec reboot at ";
- else if (c->mode == SD_SHUTDOWN_NONE)
- prefix = "The system shutdown has been cancelled at ";
- else
- assert_not_reached("Unknown mode!");
-
- if (asprintf(&l, "%s%s%s%s!", c->wall_message, c->wall_message[0] ? "\n" : "",
- prefix, format_timestamp(date, sizeof(date), c->usec)) >= 0)
- utmp_wall(l, NULL, NULL);
- else
- log_error("Failed to allocate wall message");
-}
-
-_const_ static usec_t when_wall(usec_t n, usec_t elapse) {
-
- static const struct {
- usec_t delay;
- usec_t interval;
- } table[] = {
- { 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 = ELEMENTSOF(table) - 1;
-
- /* If the time is already passed, then don't announce */
- if (n >= elapse)
- return 0;
-
- left = elapse - n;
- while (left < table[i].delay)
- i--;
- sub = (left / table[i].interval) * table[i].interval;
-
- assert(sub < elapse);
- return elapse - sub;
-}
-
-static usec_t when_nologin(usec_t elapse) {
- return elapse > 5*USEC_PER_MINUTE ? elapse - 5*USEC_PER_MINUTE : 1;
-}
-
-static const char *mode_to_string(enum sd_shutdown_mode m) {
- switch (m) {
- case SD_SHUTDOWN_REBOOT:
- return "reboot";
- case SD_SHUTDOWN_POWEROFF:
- return "poweroff";
- case SD_SHUTDOWN_HALT:
- return "halt";
- case SD_SHUTDOWN_KEXEC:
- return "kexec";
- default:
- return NULL;
- }
-}
-
-static int update_schedule_file(struct sd_shutdown_command *c) {
- int r;
- _cleanup_fclose_ FILE *f = NULL;
- _cleanup_free_ char *t = NULL, *temp_path = NULL;
-
- assert(c);
-
- r = mkdir_safe_label("/run/systemd/shutdown", 0755, 0, 0);
- if (r < 0)
- return log_error_errno(r, "Failed to create shutdown subdirectory: %m");
-
- t = cescape(c->wall_message);
- if (!t)
- return log_oom();
-
- r = fopen_temporary("/run/systemd/shutdown/scheduled", &f, &temp_path);
- if (r < 0)
- return log_error_errno(r, "Failed to save information about scheduled shutdowns: %m");
-
- fchmod(fileno(f), 0644);
-
- fprintf(f,
- "USEC="USEC_FMT"\n"
- "WARN_WALL=%i\n"
- "MODE=%s\n",
- c->usec,
- c->warn_wall,
- mode_to_string(c->mode));
-
- if (c->dry_run)
- fputs("DRY_RUN=1\n", f);
-
- if (!isempty(t))
- fprintf(f, "WALL_MESSAGE=%s\n", t);
-
- fflush(f);
-
- if (ferror(f) || rename(temp_path, "/run/systemd/shutdown/scheduled") < 0) {
- log_error_errno(errno, "Failed to write information about scheduled shutdowns: %m");
- r = -errno;
-
- unlink(temp_path);
- unlink("/run/systemd/shutdown/scheduled");
- }
-
- return r;
-}
-
-static bool scheduled(struct sd_shutdown_command *c) {
- return c->usec > 0 && c->mode != SD_SHUTDOWN_NONE;
-}
-
-int main(int argc, char *argv[]) {
- enum {
- FD_SOCKET,
- FD_WALL_TIMER,
- FD_NOLOGIN_TIMER,
- FD_SHUTDOWN_TIMER,
- _FD_MAX
- };
-
- int r = EXIT_FAILURE, n_fds;
- union shutdown_buffer b = {};
- struct pollfd pollfd[_FD_MAX] = {};
- bool exec_shutdown = false, unlink_nologin = false;
- unsigned i;
-
- if (getppid() != 1) {
- log_error("This program should be invoked by init only.");
- return EXIT_FAILURE;
- }
-
- if (argc > 1) {
- log_error("This program does not take arguments.");
- return EXIT_FAILURE;
- }
-
- log_set_target(LOG_TARGET_AUTO);
- log_parse_environment();
- log_open();
-
- umask(0022);
-
- n_fds = sd_listen_fds(true);
- if (n_fds < 0) {
- log_error_errno(r, "Failed to read listening file descriptors from environment: %m");
- return EXIT_FAILURE;
- }
-
- if (n_fds != 1) {
- log_error("Need exactly one file descriptor.");
- return EXIT_FAILURE;
- }
-
- pollfd[FD_SOCKET].fd = SD_LISTEN_FDS_START;
- pollfd[FD_SOCKET].events = POLLIN;
-
- for (i = FD_WALL_TIMER; i < _FD_MAX; i++) {
- pollfd[i].events = POLLIN;
- pollfd[i].fd = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK|TFD_CLOEXEC);
- if (pollfd[i].fd < 0) {
- log_error_errno(errno, "timerfd_create(): %m");
- goto finish;
- }
- }
-
- log_debug("systemd-shutdownd running as pid "PID_FMT, getpid());
-
- sd_notify(false,
- "READY=1\n"
- "STATUS=Processing requests...");
-
- for (;;) {
- int k;
- usec_t n;
-
- k = poll(pollfd, _FD_MAX, scheduled(&b.command) ? -1 : 0);
- if (k < 0) {
-
- if (errno == EAGAIN || errno == EINTR)
- continue;
-
- log_error_errno(errno, "poll(): %m");
- goto finish;
- }
-
- /* Exit on idle */
- if (k == 0)
- break;
-
- n = now(CLOCK_REALTIME);
-
- if (pollfd[FD_SOCKET].revents) {
-
- k = read_packet(pollfd[FD_SOCKET].fd, &b);
- if (k < 0)
- goto finish;
- else if (k > 0) {
- struct itimerspec its;
- char date[FORMAT_TIMESTAMP_MAX];
-
- if (!scheduled(&b.command)) {
- log_info("Shutdown canceled.");
- if (b.command.warn_wall)
- warn_wall(0, &b.command);
- break;
- }
-
- zero(its);
- if (b.command.warn_wall) {
- /* Send wall messages every so often */
- timespec_store(&its.it_value, when_wall(n, b.command.usec));
-
- /* Warn immediately if less than 15 minutes are left */
- if (n < b.command.usec &&
- n + 15*USEC_PER_MINUTE >= b.command.usec)
- warn_wall(n, &b.command);
- }
- if (timerfd_settime(pollfd[FD_WALL_TIMER].fd, TFD_TIMER_ABSTIME, &its, NULL) < 0) {
- log_error_errno(errno, "timerfd_settime(): %m");
- goto finish;
- }
-
- /* Disallow logins 5 minutes prior to shutdown */
- zero(its);
- timespec_store(&its.it_value, when_nologin(b.command.usec));
- if (timerfd_settime(pollfd[FD_NOLOGIN_TIMER].fd, TFD_TIMER_ABSTIME, &its, NULL) < 0) {
- log_error_errno(errno, "timerfd_settime(): %m");
- goto finish;
- }
-
- /* Shutdown after the specified time is reached */
- zero(its);
- timespec_store(&its.it_value, b.command.usec);
- if (timerfd_settime(pollfd[FD_SHUTDOWN_TIMER].fd, TFD_TIMER_ABSTIME, &its, NULL) < 0) {
- log_error_errno(errno, "timerfd_settime(): %m");
- goto finish;
- }
-
- update_schedule_file(&b.command);
-
- sd_notifyf(false,
- "STATUS=Shutting down at %s (%s)...",
- format_timestamp(date, sizeof(date), b.command.usec),
- mode_to_string(b.command.mode));
-
- log_info("Shutting down at %s (%s)...", date, mode_to_string(b.command.mode));
- }
- }
-
- if (pollfd[FD_WALL_TIMER].revents) {
- struct itimerspec its = {};
-
- warn_wall(n, &b.command);
- flush_fd(pollfd[FD_WALL_TIMER].fd);
-
- /* Restart timer */
- 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_errno(errno, "timerfd_settime(): %m");
- goto finish;
- }
- }
-
- if (pollfd[FD_NOLOGIN_TIMER].revents) {
- int e;
-
- log_info("Creating /run/nologin, blocking further logins...");
-
- e = write_string_file_atomic("/run/nologin", "System is going down.");
- if (e < 0)
- log_error_errno(e, "Failed to create /run/nologin: %m");
- else
- unlink_nologin = true;
-
- flush_fd(pollfd[FD_NOLOGIN_TIMER].fd);
- }
-
- if (pollfd[FD_SHUTDOWN_TIMER].revents) {
- exec_shutdown = true;
- goto finish;
- }
- }
-
- r = EXIT_SUCCESS;
-
- log_debug("systemd-shutdownd stopped as pid "PID_FMT, getpid());
-
-finish:
-
- for (i = 0; i < _FD_MAX; i++)
- safe_close(pollfd[i].fd);
-
- if (unlink_nologin)
- unlink("/run/nologin");
-
- unlink("/run/systemd/shutdown/scheduled");
-
- if (exec_shutdown && !b.command.dry_run) {
- char sw[3];
-
- sw[0] = '-';
- sw[1] = b.command.mode;
- sw[2] = 0;
-
- execl(SYSTEMCTL_BINARY_PATH,
- "shutdown",
- sw,
- "now",
- (b.command.warn_wall && b.command.wall_message[0]) ? b.command.wall_message :
- (b.command.warn_wall ? NULL : "--no-wall"),
- NULL);
-
- log_error_errno(errno, "Failed to execute /sbin/shutdown: %m");
- }
-
- sd_notify(false,
- "STOPPING=\n"
- "STATUS=Exiting...");
-
- return r;
-}
diff --git a/src/sleep/sleep.c b/src/sleep/sleep.c
index cc1ffa63f1..eee6bc8982 100644
--- a/src/sleep/sleep.c
+++ b/src/sleep/sleep.c
@@ -22,10 +22,8 @@
#include <stdio.h>
#include <errno.h>
-#include <string.h>
#include <getopt.h>
-#include "sd-id128.h"
#include "sd-messages.h"
#include "log.h"
#include "util.h"
diff --git a/src/socket-proxy/socket-proxyd.c b/src/socket-proxy/socket-proxyd.c
index a3c3c87f11..715f440cb1 100644
--- a/src/socket-proxy/socket-proxyd.c
+++ b/src/socket-proxy/socket-proxyd.c
@@ -19,7 +19,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <arpa/inet.h>
#include <errno.h>
#include <getopt.h>
#include <stdio.h>
@@ -37,7 +36,6 @@
#include "log.h"
#include "socket-util.h"
#include "util.h"
-#include "event-util.h"
#include "build.h"
#include "set.h"
#include "path-util.h"
diff --git a/src/sysctl/sysctl.c b/src/sysctl/sysctl.c
index 275a5b74ae..fe277a2015 100644
--- a/src/sysctl/sysctl.c
+++ b/src/sysctl/sysctl.c
@@ -35,75 +35,30 @@
#include "conf-files.h"
#include "fileio.h"
#include "build.h"
+#include "sysctl-util.h"
static char **arg_prefixes = NULL;
static const char conf_file_dirs[] = CONF_DIRS_NULSTR("sysctl");
-static char* normalize_sysctl(char *s) {
- char *n;
-
- n = strpbrk(s, "/.");
- /* If the first separator is a slash, the path is
- * assumed to be normalized and slashes remain slashes
- * and dots remains dots. */
- if (!n || *n == '/')
- return s;
-
- /* Otherwise, dots become slashes and slashes become
- * dots. Fun. */
- while (n) {
- if (*n == '.')
- *n = '/';
- else
- *n = '.';
-
- n = strpbrk(n + 1, "/.");
- }
-
- return s;
-}
-
-static int apply_sysctl(const char *property, const char *value) {
- _cleanup_free_ char *p = NULL;
- char *n;
- int r = 0, k;
-
- log_debug("Setting '%s' to '%s'", property, value);
-
- p = new(char, strlen("/proc/sys/") + strlen(property) + 1);
- if (!p)
- return log_oom();
-
- n = stpcpy(p, "/proc/sys/");
- strcpy(n, property);
-
- 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));
-
- if (k != -ENOENT && r == 0)
- r = k;
- }
-
- return r;
-}
-
static int apply_all(Hashmap *sysctl_options) {
- int r = 0;
char *property, *value;
Iterator i;
-
- assert(sysctl_options);
+ int r = 0;
HASHMAP_FOREACH_KEY(value, property, sysctl_options, i) {
int k;
- k = apply_sysctl(property, value);
- if (k < 0 && r == 0)
- r = k;
+ k = sysctl_write(property, value);
+ if (k < 0) {
+ log_full_errno(k == -ENOENT ? LOG_DEBUG : LOG_WARNING, k,
+ "Failed to write '%s' to '%s': %m", value, property);
+
+ if (r == 0 && k != -ENOENT)
+ r = k;
+ }
}
+
return r;
}
@@ -121,7 +76,7 @@ static int parse_file(Hashmap *sysctl_options, const char *path, bool ignore_eno
return log_error_errno(r, "Failed to open file '%s', ignoring: %m", path);
}
- log_debug("parse: %s", path);
+ log_debug("Parsing %s", path);
while (!feof(f)) {
char l[LINE_MAX], *p, *value, *new_value, *property, *existing;
void *v;
@@ -154,7 +109,7 @@ static int parse_file(Hashmap *sysctl_options, const char *path, bool ignore_eno
*value = 0;
value++;
- p = normalize_sysctl(strstrip(p));
+ p = sysctl_normalize(strstrip(p));
value = strstrip(value);
if (!strv_isempty(arg_prefixes)) {
@@ -176,7 +131,7 @@ found:
if (streq(value, existing))
continue;
- log_info("Overwriting earlier assignment of %s in file '%s'.", p, path);
+ log_debug("Overwriting earlier assignment of %s in file '%s'.", p, path);
free(hashmap_remove(sysctl_options, p));
free(v);
}
@@ -251,14 +206,15 @@ static int parse_argv(int argc, char *argv[]) {
* in /proc/sys in the past. This is kinda useless, but
* we need to keep compatibility. We now support any
* sysctl name available. */
- normalize_sysctl(optarg);
+ sysctl_normalize(optarg);
+
if (startswith(optarg, "/proc/sys"))
p = strdup(optarg);
else
p = strappend("/proc/sys/", optarg);
-
if (!p)
return log_oom();
+
if (strv_consume(&arg_prefixes, p) < 0)
return log_oom();
diff --git a/src/system-update-generator/system-update-generator.c b/src/system-update-generator/system-update-generator.c
index ad34ef1366..00045150f6 100644
--- a/src/system-update-generator/system-update-generator.c
+++ b/src/system-update-generator/system-update-generator.c
@@ -24,8 +24,6 @@
#include "log.h"
#include "util.h"
-#include "unit-name.h"
-#include "path-util.h"
/*
* Implements the logic described in
diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c
index 21cb898b9a..b3d90d2c33 100644
--- a/src/systemctl/systemctl.c
+++ b/src/systemctl/systemctl.c
@@ -22,24 +22,18 @@
#include <sys/reboot.h>
#include <linux/reboot.h>
-#include <sys/syscall.h>
#include <stdio.h>
#include <getopt.h>
#include <locale.h>
#include <stdbool.h>
#include <string.h>
#include <errno.h>
-#include <sys/ioctl.h>
-#include <termios.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/socket.h>
-#include <sys/stat.h>
#include <stddef.h>
-#include <sys/prctl.h>
#include "sd-daemon.h"
-#include "sd-shutdown.h"
#include "sd-login.h"
#include "sd-bus.h"
#include "log.h"
@@ -55,7 +49,6 @@
#include "cgroup-util.h"
#include "list.h"
#include "path-lookup.h"
-#include "conf-parser.h"
#include "exit-status.h"
#include "build.h"
#include "unit-name.h"
@@ -74,6 +67,12 @@
#include "bus-common-errors.h"
#include "mkdir.h"
#include "dropin.h"
+#include "efivars.h"
+#include "formats-util.h"
+#include "process-util.h"
+#include "terminal-util.h"
+#include "hostname-util.h"
+#include "signal-util.h"
static char **arg_types = NULL;
static char **arg_states = NULL;
@@ -138,6 +137,8 @@ static char *arg_host = NULL;
static unsigned arg_lines = 10;
static OutputMode arg_output = OUTPUT_SHORT;
static bool arg_plain = false;
+static bool arg_firmware_setup = false;
+static bool arg_now = false;
static bool original_stdout_is_tty;
@@ -252,7 +253,7 @@ static void warn_wall(enum action a) {
}
if (*p) {
- utmp_wall(p, NULL, NULL);
+ utmp_wall(p, NULL, NULL, NULL, NULL);
return;
}
}
@@ -260,7 +261,7 @@ static void warn_wall(enum action a) {
if (!table[a])
return;
- utmp_wall(table[a], NULL, NULL);
+ utmp_wall(table[a], NULL, NULL, NULL, NULL);
}
static bool avoid_bus(void) {
@@ -1327,7 +1328,6 @@ static void output_unit_file_list(const UnitFileList *units, unsigned c) {
static int list_unit_files(sd_bus *bus, char **args) {
_cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_free_ UnitFileList *units = NULL;
UnitFileList *unit;
size_t size = 0;
@@ -1374,6 +1374,8 @@ static int list_unit_files(sd_bus *bus, char **args) {
assert(c <= n_units);
hashmap_free(h);
} else {
+ _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+
r = sd_bus_call_method(
bus,
"org.freedesktop.systemd1",
@@ -1637,13 +1639,15 @@ static int list_dependencies(sd_bus *bus, char **args) {
_cleanup_strv_free_ char **units = NULL;
_cleanup_free_ char *unit = NULL;
const char *u;
+ int r;
assert(bus);
if (args[1]) {
- unit = unit_name_mangle(args[1], MANGLE_NOGLOB);
- if (!unit)
- return log_oom();
+ r = unit_name_mangle(args[1], UNIT_NAME_NOGLOB, &unit);
+ if (r < 0)
+ return log_error_errno(r, "Failed to mangle unit name: %m");
+
u = unit;
} else
u = SPECIAL_DEFAULT_TARGET;
@@ -1882,7 +1886,6 @@ static int list_machines(sd_bus *bus, char **args) {
static int get_default(sd_bus *bus, char **args) {
_cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_free_ char *_path = NULL;
const char *path;
int r;
@@ -1894,6 +1897,8 @@ static int get_default(sd_bus *bus, char **args) {
path = _path;
} else {
+ _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+
r = sd_bus_call_method(
bus,
"org.freedesktop.systemd1",
@@ -1938,9 +1943,9 @@ static int set_default(sd_bus *bus, char **args) {
unsigned n_changes = 0;
int r;
- unit = unit_name_mangle_with_suffix(args[1], MANGLE_NOGLOB, ".target");
- if (!unit)
- return log_oom();
+ r = unit_name_mangle_with_suffix(args[1], UNIT_NAME_NOGLOB, ".target", &unit);
+ if (r < 0)
+ return log_error_errno(r, "Failed to mangle unit name: %m");
if (!bus || avoid_bus()) {
r = unit_file_set_default(arg_scope, arg_root, unit, true, &changes, &n_changes);
@@ -1952,34 +1957,26 @@ static int set_default(sd_bus *bus, char **args) {
r = 0;
} else {
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL, *m = NULL;
_cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
- r = sd_bus_message_new_method_call(
+ polkit_agent_open_if_enabled();
+
+ r = sd_bus_call_method(
bus,
- &m,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
- "SetDefaultTarget");
- if (r < 0)
- return bus_log_create_error(r);
-
- r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
- if (r < 0)
- return bus_log_create_error(r);
-
- r = sd_bus_message_append(m, "sb", unit, 1);
- if (r < 0)
- return bus_log_create_error(r);
-
- r = sd_bus_call(bus, m, 0, &error, &reply);
+ "SetDefaultTarget",
+ &error,
+ &reply,
+ "sb", unit, 1);
if (r < 0) {
log_error("Failed to set default target: %s", bus_error_message(&error, -r));
return r;
}
- r = bus_deserialize_and_dump_unit_file_changes(reply, arg_quiet);
+ r = bus_deserialize_and_dump_unit_file_changes(reply, arg_quiet, NULL, NULL);
if (r < 0)
return r;
@@ -2130,7 +2127,6 @@ static int list_jobs(sd_bus *bus, char **args) {
}
static int cancel_job(sd_bus *bus, char **args) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
char **name;
int r = 0;
@@ -2139,8 +2135,10 @@ static int cancel_job(sd_bus *bus, char **args) {
if (strv_length(args) <= 1)
return daemon_reload(bus, args);
+ polkit_agent_open_if_enabled();
+
STRV_FOREACH(name, args+1) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
uint32_t id;
int q;
@@ -2148,25 +2146,15 @@ static int cancel_job(sd_bus *bus, char **args) {
if (q < 0)
return log_error_errno(q, "Failed to parse job id \"%s\": %m", *name);
- q = sd_bus_message_new_method_call(
+ q = sd_bus_call_method(
bus,
- &m,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
- "CancelJob");
- if (q < 0)
- return bus_log_create_error(q);
-
- q = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
- if (q < 0)
- return bus_log_create_error(1);
-
- q = sd_bus_message_append(m, "u", id);
- if (q < 0)
- return bus_log_create_error(q);
-
- q = sd_bus_call(bus, m, 0, &error, NULL);
+ "CancelJob",
+ &error,
+ NULL,
+ "u", id);
if (q < 0) {
log_error("Failed to cancel job %"PRIu32": %s", id, bus_error_message(&error, q));
if (r == 0)
@@ -2250,12 +2238,13 @@ static int unit_file_find_path(LookupPaths *lp, const char *unit_name, char **un
return 0;
}
-static int unit_find_paths(sd_bus *bus,
- const char *unit_name,
- bool avoid_bus_cache,
- LookupPaths *lp,
- char **fragment_path,
- char ***dropin_paths) {
+static int unit_find_paths(
+ sd_bus *bus,
+ const char *unit_name,
+ bool avoid_bus_cache,
+ LookupPaths *lp,
+ char **fragment_path,
+ char ***dropin_paths) {
_cleanup_free_ char *path = NULL;
_cleanup_strv_free_ char **dropins = NULL;
@@ -2273,7 +2262,7 @@ static int unit_find_paths(sd_bus *bus,
assert(fragment_path);
assert(lp);
- if (!avoid_bus_cache && !unit_name_is_template(unit_name)) {
+ if (!avoid_bus_cache && !unit_name_is_valid(unit_name, UNIT_NAME_TEMPLATE)) {
_cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_bus_message_unref_ sd_bus_message *unit_load_error = NULL;
_cleanup_free_ char *unit = NULL;
@@ -2339,24 +2328,23 @@ static int unit_find_paths(sd_bus *bus,
names = set_new(NULL);
if (!names)
- return -ENOMEM;
+ return log_oom();
r = set_put(names, unit_name);
if (r < 0)
- return r;
+ return log_error_errno(r, "Failed to add unit name: %m");
r = unit_file_find_path(lp, unit_name, &path);
if (r < 0)
return r;
if (r == 0) {
- _cleanup_free_ char *template;
+ _cleanup_free_ char *template = NULL;
- template = unit_name_template(unit_name);
- if (!template)
- return log_oom();
-
- if (!streq(template, unit_name)) {
+ r = unit_name_template(unit_name, &template);
+ if (r < 0 && r != -EINVAL)
+ return log_error_errno(r, "Failed to determine template name: %m");
+ if (r >= 0) {
r = unit_file_find_path(lp, template, &path);
if (r < 0)
return r;
@@ -2398,9 +2386,9 @@ static int check_one_unit(sd_bus *bus, const char *name, const char *good_states
assert(name);
- n = unit_name_mangle(name, MANGLE_NOGLOB);
- if (!n)
- return log_oom();
+ r = unit_name_mangle(name, UNIT_NAME_NOGLOB, &n);
+ if (r < 0)
+ return log_error_errno(r, "Failed to mangle unit name: %m");
/* We don't use unit_dbus_path_from_name() directly since we
* don't want to load the unit if it isn't loaded. */
@@ -2455,9 +2443,9 @@ static int check_triggering_units(
char **i;
int r;
- n = unit_name_mangle(name, MANGLE_NOGLOB);
- if (!n)
- return log_oom();
+ r = unit_name_mangle(name, UNIT_NAME_NOGLOB, &n);
+ if (r < 0)
+ return log_error_errno(r, "Failed to mangle unit name: %m");
path = unit_dbus_path_from_name(n);
if (!path)
@@ -2556,7 +2544,7 @@ static int start_unit_one(
sd_bus_error *error,
BusWaitForJobs *w) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
+ _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
const char *path;
int r;
@@ -2567,25 +2555,15 @@ static int start_unit_one(
log_debug("Calling manager for %s on %s, %s", method, name, mode);
- r = sd_bus_message_new_method_call(
+ r = sd_bus_call_method(
bus,
- &m,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
- method);
- if (r < 0)
- return bus_log_create_error(r);
-
- r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
- if (r < 0)
- return bus_log_create_error(r);
-
- r = sd_bus_message_append(m, "ss", name, mode);
- if (r < 0)
- return bus_log_create_error(r);
-
- r = sd_bus_call(bus, m, 0, error, &reply);
+ method,
+ error,
+ &reply,
+ "ss", name, mode);
if (r < 0) {
const char *verb;
@@ -2621,17 +2599,17 @@ static int expand_names(sd_bus *bus, char **names, const char* suffix, char ***r
_cleanup_strv_free_ char **mangled = NULL, **globs = NULL;
char **name;
- int r = 0, i;
+ int r, i;
STRV_FOREACH(name, names) {
char *t;
if (suffix)
- t = unit_name_mangle_with_suffix(*name, MANGLE_GLOB, suffix);
+ r = unit_name_mangle_with_suffix(*name, UNIT_NAME_GLOB, suffix, &t);
else
- t = unit_name_mangle(*name, MANGLE_GLOB);
- if (!t)
- return log_oom();
+ r = unit_name_mangle(*name, UNIT_NAME_GLOB, &t);
+ if (r < 0)
+ return log_error_errno(r, "Failed to mangle name: %m");
if (string_is_glob(t))
r = strv_consume(&globs, t);
@@ -2648,7 +2626,7 @@ static int expand_names(sd_bus *bus, char **names, const char* suffix, char ***r
_cleanup_free_ UnitInfo *unit_infos = NULL;
if (!bus)
- return log_error_errno(ENOTSUP, "Unit name globbing without bus is not implemented.");
+ return log_error_errno(EOPNOTSUPP, "Unit name globbing without bus is not implemented.");
r = get_unit_list(bus, NULL, globs, &unit_infos, 0, &reply);
if (r < 0)
@@ -2674,10 +2652,10 @@ static const struct {
[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_RUNLEVEL2] = { SPECIAL_MULTI_USER_TARGET, NULL, "isolate" },
+ [ACTION_RUNLEVEL3] = { SPECIAL_MULTI_USER_TARGET, NULL, "isolate" },
+ [ACTION_RUNLEVEL4] = { SPECIAL_MULTI_USER_TARGET, NULL, "isolate" },
+ [ACTION_RUNLEVEL5] = { SPECIAL_GRAPHICAL_TARGET, NULL, "isolate" },
[ACTION_RESCUE] = { SPECIAL_RESCUE_TARGET, "rescue", "isolate" },
[ACTION_EMERGENCY] = { SPECIAL_EMERGENCY_TARGET, "emergency", "isolate" },
[ACTION_DEFAULT] = { SPECIAL_DEFAULT_TARGET, "default", "isolate" },
@@ -2881,6 +2859,9 @@ static int check_inhibitors(sd_bus *bus, enum action a) {
if (!sv)
return log_oom();
+ if ((pid_t) pid < 0)
+ return log_error_errno(ERANGE, "Bad PID %"PRIu32": %m", pid);
+
if (!strv_contains(sv,
a == ACTION_HALT ||
a == ACTION_POWEROFF ||
@@ -2892,7 +2873,7 @@ static int check_inhibitors(sd_bus *bus, enum action a) {
user = uid_to_name(uid);
log_warning("Operation inhibited by \"%s\" (PID "PID_FMT" \"%s\", user %s), reason is \"%s\".",
- who, pid, strna(comm), strna(user), why);
+ who, (pid_t) pid, strna(comm), strna(user), why);
c++;
}
@@ -2938,6 +2919,46 @@ static int check_inhibitors(sd_bus *bus, enum action a) {
#endif
}
+static int prepare_firmware_setup(sd_bus *bus) {
+#ifdef HAVE_LOGIND
+ _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+#endif
+ int r;
+
+ if (!arg_firmware_setup)
+ return 0;
+
+ if (arg_transport == BUS_TRANSPORT_LOCAL) {
+
+ r = efi_set_reboot_to_firmware(true);
+ if (r < 0)
+ log_debug_errno(r, "Cannot indicate to EFI to boot into setup mode, will retry via logind: %m");
+ else
+ return r;
+ }
+
+#ifdef HAVE_LOGIND
+ r = sd_bus_call_method(
+ bus,
+ "org.freedesktop.login1",
+ "/org/freedesktop/login1",
+ "org.freedesktop.login1.Manager",
+ "SetRebootToFirmwareSetup",
+ &error,
+ NULL,
+ "b", true);
+ if (r < 0) {
+ log_error("Cannot indicate to EFI to boot into setup mode: %s", bus_error_message(&error, r));
+ return r;
+ }
+
+ return 0;
+#else
+ log_error("Cannot remotely indicate to EFI to boot into setup mode.");
+ return -EINVAL;
+#endif
+}
+
static int start_special(sd_bus *bus, char **args) {
enum action a;
int r;
@@ -2955,6 +2976,10 @@ static int start_special(sd_bus *bus, char **args) {
return -EPERM;
}
+ r = prepare_firmware_setup(bus);
+ if (r < 0)
+ return r;
+
if (a == ACTION_REBOOT && args[1]) {
r = update_reboot_param_file(args[1]);
if (r < 0)
@@ -2983,7 +3008,7 @@ static int start_special(sd_bus *bus, char **args) {
a == ACTION_HIBERNATE ||
a == ACTION_HYBRID_SLEEP)) {
r = reboot_with_logind(bus, a);
- if (r >= 0 || IN_SET(r, -ENOTSUP, -EINPROGRESS))
+ if (r >= 0 || IN_SET(r, -EOPNOTSUPP, -EINPROGRESS))
return r;
}
@@ -3029,7 +3054,6 @@ static int check_unit_failed(sd_bus *bus, char **args) {
}
static int kill_unit(sd_bus *bus, char **args) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_strv_free_ char **names = NULL;
char **name;
int r, q;
@@ -3047,27 +3071,17 @@ static int kill_unit(sd_bus *bus, char **args) {
log_error_errno(r, "Failed to expand names: %m");
STRV_FOREACH(name, names) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
- q = sd_bus_message_new_method_call(
+ q = sd_bus_call_method(
bus,
- &m,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
- "KillUnit");
- if (q < 0)
- return bus_log_create_error(q);
-
- q = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
- if (q < 0)
- return bus_log_create_error(q);
-
- q = sd_bus_message_append(m, "ssi", *names, arg_kill_who, arg_signal);
- if (q < 0)
- return bus_log_create_error(q);
-
- q = sd_bus_call(bus, m, 0, &error, NULL);
+ "KillUnit",
+ &error,
+ NULL,
+ "ssi", *names, arg_kill_who, arg_signal);
if (q < 0) {
log_error("Failed to kill unit %s: %s", *names, bus_error_message(&error, q));
if (r == 0)
@@ -3233,6 +3247,7 @@ typedef struct UnitStatusInfo {
/* CGroup */
uint64_t memory_current;
uint64_t memory_limit;
+ uint64_t cpu_usage_nsec;
LIST_HEAD(ExecStatusInfo, exec);
} UnitStatusInfo;
@@ -3503,6 +3518,11 @@ static void print_status_info(
printf("\n");
}
+ if (i->cpu_usage_nsec != (uint64_t) -1) {
+ char buf[FORMAT_TIMESPAN_MAX];
+ printf(" CPU: %s\n", format_timespan(buf, sizeof(buf), i->cpu_usage_nsec / NSEC_PER_USEC, USEC_PER_MSEC));
+ }
+
if (i->control_group &&
(i->main_pid > 0 || i->control_pid > 0 ||
((arg_transport != BUS_TRANSPORT_LOCAL && arg_transport != BUS_TRANSPORT_MACHINE) || cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, i->control_group, false) == 0))) {
@@ -3721,6 +3741,8 @@ static int status_property(const char *name, sd_bus_message *m, UnitStatusInfo *
i->memory_current = u;
else if (streq(name, "MemoryLimit"))
i->memory_limit = u;
+ else if (streq(name, "CPUUsageNSec"))
+ i->cpu_usage_nsec = u;
break;
}
@@ -4194,6 +4216,7 @@ static int show_one(
UnitStatusInfo info = {
.memory_current = (uint64_t) -1,
.memory_limit = (uint64_t) -1,
+ .cpu_usage_nsec = (uint64_t) -1,
};
ExecStatusInfo *p;
int r;
@@ -4446,6 +4469,12 @@ static int show(sd_bus *bus, char **args) {
if (show_properties)
pager_open_if_enabled();
+ if (show_status)
+ /* Increase max number of open files to 16K if we can, we
+ * might needs this when browsing journal files, which might
+ * be split up into many files. */
+ setrlimit_closest(RLIMIT_NOFILE, &RLIMIT_MAKE_CONST(16384));
+
/* If no argument is specified inspect the manager itself */
if (show_properties && strv_length(args) <= 1)
@@ -4552,6 +4581,23 @@ static int init_home_and_lookup_paths(char **user_home, char **user_runtime, Loo
return 0;
}
+static int cat_file(const char *filename, bool newline) {
+ _cleanup_close_ int fd;
+
+ fd = open(filename, O_RDONLY|O_CLOEXEC|O_NOCTTY);
+ if (fd < 0)
+ return -errno;
+
+ printf("%s%s# %s%s\n",
+ newline ? "\n" : "",
+ ansi_highlight_blue(),
+ filename,
+ ansi_highlight_off());
+ fflush(stdout);
+
+ return copy_bytes(fd, STDOUT_FILENO, (off_t) -1, false);
+}
+
static int cat(sd_bus *bus, char **args) {
_cleanup_free_ char *user_home = NULL;
_cleanup_free_ char *user_runtime = NULL;
@@ -4559,7 +4605,7 @@ static int cat(sd_bus *bus, char **args) {
_cleanup_strv_free_ char **names = NULL;
char **name;
bool first = true, avoid_bus_cache;
- int r = 0;
+ int r;
assert(args);
@@ -4597,36 +4643,19 @@ static int cat(sd_bus *bus, char **args) {
puts("");
if (fragment_path) {
- printf("%s# %s%s\n",
- ansi_highlight_blue(),
- fragment_path,
- ansi_highlight_off());
- fflush(stdout);
-
- r = copy_file_fd(fragment_path, STDOUT_FILENO, false);
- if (r < 0) {
- log_warning_errno(r, "Failed to cat %s: %m", fragment_path);
- continue;
- }
+ r = cat_file(fragment_path, false);
+ if (r < 0)
+ return log_warning_errno(r, "Failed to cat %s: %m", fragment_path);
}
STRV_FOREACH(path, dropin_paths) {
- printf("%s%s# %s%s\n",
- isempty(fragment_path) && path == dropin_paths ? "" : "\n",
- ansi_highlight_blue(),
- *path,
- ansi_highlight_off());
- fflush(stdout);
-
- r = copy_file_fd(*path, STDOUT_FILENO, false);
- if (r < 0) {
- log_warning_errno(r, "Failed to cat %s: %m", *path);
- continue;
- }
+ r = cat_file(*path, path == dropin_paths);
+ if (r < 0)
+ return log_warning_errno(r, "Failed to cat %s: %m", *path);
}
}
- return r < 0 ? r : 0;
+ return 0;
}
static int set_property(sd_bus *bus, char **args) {
@@ -4648,13 +4677,9 @@ static int set_property(sd_bus *bus, char **args) {
if (r < 0)
return bus_log_create_error(r);
- r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
+ r = unit_name_mangle(args[1], UNIT_NAME_NOGLOB, &n);
if (r < 0)
- return bus_log_create_error(r);
-
- n = unit_name_mangle(args[1], MANGLE_NOGLOB);
- if (!n)
- return log_oom();
+ return log_error_errno(r, "Failed to mangle unit name: %m");
r = sd_bus_message_append(m, "sb", n, arg_runtime);
if (r < 0)
@@ -4693,39 +4718,32 @@ static int set_property(sd_bus *bus, char **args) {
static int snapshot(sd_bus *bus, char **args) {
_cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
+ _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
_cleanup_free_ char *n = NULL, *id = NULL;
const char *path;
int r;
polkit_agent_open_if_enabled();
- if (strv_length(args) > 1)
- n = unit_name_mangle_with_suffix(args[1], MANGLE_NOGLOB, ".snapshot");
- else
+ if (strv_length(args) > 1) {
+ r = unit_name_mangle_with_suffix(args[1], UNIT_NAME_NOGLOB, ".snapshot", &n);
+ if (r < 0)
+ return log_error_errno(r, "Failed to generate unit name: %m");
+ } else {
n = strdup("");
- if (!n)
- return log_oom();
+ if (!n)
+ return log_oom();
+ }
- r = sd_bus_message_new_method_call(
+ r = sd_bus_call_method(
bus,
- &m,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
- "CreateSnapshot");
- if (r < 0)
- return bus_log_create_error(r);
-
- r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
- if (r < 0)
- return bus_log_create_error(r);
-
- r = sd_bus_message_append(m, "sb", n, false);
- if (r < 0)
- return bus_log_create_error(r);
-
- r = sd_bus_call(bus, m, 0, &error, &reply);
+ "CreateSnapshot",
+ &error,
+ &reply,
+ "sb", n, false);
if (r < 0) {
log_error("Failed to create snapshot: %s", bus_error_message(&error, r));
return r;
@@ -4755,7 +4773,6 @@ static int snapshot(sd_bus *bus, char **args) {
}
static int delete_snapshot(sd_bus *bus, char **args) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_strv_free_ char **names = NULL;
char **name;
int r;
@@ -4769,28 +4786,18 @@ static int delete_snapshot(sd_bus *bus, char **args) {
log_error_errno(r, "Failed to expand names: %m");
STRV_FOREACH(name, names) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
int q;
- q = sd_bus_message_new_method_call(
+ q = sd_bus_call_method(
bus,
- &m,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
- "RemoveSnapshot");
- if (q < 0)
- return bus_log_create_error(q);
-
- q = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
- if (q < 0)
- return bus_log_create_error(q);
-
- q = sd_bus_message_append(m, "s", *name);
- if (q < 0)
- return bus_log_create_error(q);
-
- q = sd_bus_call(bus, m, 0, &error, NULL);
+ "RemoveSnapshot",
+ &error,
+ NULL,
+ "s", *name);
if (q < 0) {
log_error("Failed to remove snapshot %s: %s", *name, bus_error_message(&error, q));
if (r == 0)
@@ -4803,7 +4810,6 @@ static int delete_snapshot(sd_bus *bus, char **args) {
static int daemon_reload(sd_bus *bus, char **args) {
_cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
const char *method;
int r;
@@ -4829,21 +4835,15 @@ static int daemon_reload(sd_bus *bus, char **args) {
/* "daemon-reload" */ "Reload";
}
- r = sd_bus_message_new_method_call(
+ r = sd_bus_call_method(
bus,
- &m,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
- method);
- if (r < 0)
- return bus_log_create_error(r);
-
- r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
- if (r < 0)
- return bus_log_create_error(r);
-
- r = sd_bus_call(bus, m, 0, &error, NULL);
+ method,
+ &error,
+ NULL,
+ NULL);
if (r == -ENOENT && arg_action != ACTION_SYSTEMCTL)
/* There's always a fallback possible for
* legacy actions. */
@@ -4859,7 +4859,6 @@ static int daemon_reload(sd_bus *bus, char **args) {
}
static int reset_failed(sd_bus *bus, char **args) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_strv_free_ char **names = NULL;
char **name;
int r, q;
@@ -4874,27 +4873,17 @@ static int reset_failed(sd_bus *bus, char **args) {
log_error_errno(r, "Failed to expand names: %m");
STRV_FOREACH(name, names) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
- q = sd_bus_message_new_method_call(
+ q = sd_bus_call_method(
bus,
- &m,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
- "ResetFailedUnit");
- if (q < 0)
- return bus_log_create_error(q);
-
- q = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
- if (q < 0)
- return bus_log_create_error(q);
-
- q = sd_bus_message_append(m, "s", *name);
- if (q < 0)
- return bus_log_create_error(q);
-
- q = sd_bus_call(bus, m, 0, &error, NULL);
+ "ResetFailedUnit",
+ &error,
+ NULL,
+ "s", *name);
if (q < 0) {
log_error("Failed to reset failed state of unit %s: %s", *name, bus_error_message(&error, q));
if (r == 0)
@@ -5013,6 +5002,8 @@ static int set_environment(sd_bus *bus, char **args) {
assert(bus);
assert(args);
+ polkit_agent_open_if_enabled();
+
method = streq(args[0], "set-environment")
? "SetEnvironment"
: "UnsetEnvironment";
@@ -5027,10 +5018,6 @@ static int set_environment(sd_bus *bus, char **args) {
if (r < 0)
return bus_log_create_error(r);
- r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
- if (r < 0)
- return bus_log_create_error(r);
-
r = sd_bus_message_append_strv(m, args + 1);
if (r < 0)
return bus_log_create_error(r);
@@ -5052,6 +5039,8 @@ static int import_environment(sd_bus *bus, char **args) {
assert(bus);
assert(args);
+ polkit_agent_open_if_enabled();
+
r = sd_bus_message_new_method_call(
bus,
&m,
@@ -5062,10 +5051,6 @@ static int import_environment(sd_bus *bus, char **args) {
if (r < 0)
return bus_log_create_error(r);
- r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
- if (r < 0)
- return bus_log_create_error(r);
-
if (strv_isempty(args + 1))
r = sd_bus_message_append_strv(m, environ);
else {
@@ -5114,7 +5099,7 @@ static int import_environment(sd_bus *bus, char **args) {
static int enable_sysv_units(const char *verb, char **args) {
int r = 0;
-#if defined(HAVE_SYSV_COMPAT) && defined(HAVE_CHKCONFIG)
+#if defined(HAVE_SYSV_COMPAT)
unsigned f = 0;
_cleanup_lookup_paths_free_ LookupPaths paths = {};
@@ -5129,7 +5114,7 @@ static int enable_sysv_units(const char *verb, char **args) {
/* Processes all SysV units, and reshuffles the array so that
* afterwards only the native units remain */
- r = lookup_paths_init(&paths, SYSTEMD_SYSTEM, false, arg_root, NULL, NULL, NULL);
+ r = lookup_paths_init(&paths, MANAGER_SYSTEM, false, arg_root, NULL, NULL, NULL);
if (r < 0)
return r;
@@ -5139,7 +5124,7 @@ static int enable_sysv_units(const char *verb, char **args) {
_cleanup_free_ char *p = NULL, *q = NULL, *l = NULL;
bool found_native = false, found_sysv;
unsigned c = 1;
- const char *argv[6] = { "/sbin/chkconfig", NULL, NULL, NULL, NULL };
+ const char *argv[6] = { ROOTLIBEXECDIR "/systemd-sysv-install", NULL, NULL, NULL, NULL };
char **k;
int j;
pid_t pid;
@@ -5165,7 +5150,10 @@ static int enable_sysv_units(const char *verb, char **args) {
break;
}
- if (found_native)
+ /* If we have both a native unit and a SysV script,
+ * enable/disable them both (below); for is-enabled, prefer the
+ * native unit */
+ if (found_native && streq(verb, "is-enabled"))
continue;
p = path_join(arg_root, SYSTEM_SYSVINIT_PATH, name);
@@ -5177,15 +5165,16 @@ static int enable_sysv_units(const char *verb, char **args) {
if (!found_sysv)
continue;
- log_info("%s is not a native service, redirecting to /sbin/chkconfig.", name);
+ if (found_native)
+ log_info("Synchronizing state of %s with SysV init with %s...", name, argv[0]);
+ else
+ log_info("%s is not a native service, redirecting to systemd-sysv-install", name);
if (!isempty(arg_root))
argv[c++] = q = strappend("--root=", arg_root);
+ argv[c++] = verb;
argv[c++] = basename(p);
- argv[c++] =
- streq(verb, "enable") ? "on" :
- streq(verb, "disable") ? "off" : "--level=5";
argv[c] = NULL;
l = strv_join((char**)argv, " ");
@@ -5201,6 +5190,7 @@ static int enable_sysv_units(const char *verb, char **args) {
/* Child */
execv(argv[0], (char**) argv);
+ log_error("Failed to execute %s: %m", argv[0]);
_exit(EXIT_FAILURE);
}
@@ -5226,6 +5216,9 @@ static int enable_sysv_units(const char *verb, char **args) {
} else
return -EPROTO;
+ if (found_native)
+ continue;
+
/* Remove this entry, so that we don't try enabling it as native unit */
assert(f > 0);
f--;
@@ -5239,25 +5232,29 @@ static int enable_sysv_units(const char *verb, char **args) {
static int mangle_names(char **original_names, char ***mangled_names) {
char **i, **l, **name;
+ int r;
- l = new(char*, strv_length(original_names) + 1);
+ l = i = new(char*, strv_length(original_names) + 1);
if (!l)
return log_oom();
- i = l;
STRV_FOREACH(name, original_names) {
/* When enabling units qualified path names are OK,
* too, hence allow them explicitly. */
- if (is_path(*name))
+ if (is_path(*name)) {
*i = strdup(*name);
- else
- *i = unit_name_mangle(*name, MANGLE_NOGLOB);
-
- if (!*i) {
- strv_free(l);
- return log_oom();
+ if (!*i) {
+ strv_free(l);
+ return log_oom();
+ }
+ } else {
+ r = unit_name_mangle(*name, UNIT_NAME_NOGLOB, i);
+ if (r < 0) {
+ strv_free(l);
+ return log_error_errno(r, "Failed to mangle unit name: %m");
+ }
}
i++;
@@ -5370,10 +5367,6 @@ static int enable_unit(sd_bus *bus, char **args) {
if (r < 0)
return bus_log_create_error(r);
- r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
- if (r < 0)
- return bus_log_create_error(r);
-
r = sd_bus_message_append_strv(m, names);
if (r < 0)
return bus_log_create_error(r);
@@ -5406,7 +5399,7 @@ static int enable_unit(sd_bus *bus, char **args) {
return bus_log_parse_error(r);
}
- r = bus_deserialize_and_dump_unit_file_changes(reply, arg_quiet);
+ r = bus_deserialize_and_dump_unit_file_changes(reply, arg_quiet, &changes, &n_changes);
if (r < 0)
return r;
@@ -5428,6 +5421,18 @@ static int enable_unit(sd_bus *bus, char **args) {
"3) A unit may be started when needed via activation (socket, path, timer,\n"
" D-Bus, udev, scripted systemctl call, ...).\n");
+ if (arg_now && n_changes > 0 && STR_IN_SET(args[0], "enable", "disable", "mask")) {
+ char *new_args[n_changes + 2];
+ unsigned i;
+
+ new_args[0] = streq(args[0], "enable") ? (char *)"start" : (char *)"stop";
+ for (i = 0; i < n_changes; i++)
+ new_args[i + 1] = basename(changes[i].path);
+ new_args[i + 1] = NULL;
+
+ r = start_unit(bus, new_args);
+ }
+
finish:
unit_file_changes_free(changes, n_changes);
@@ -5444,9 +5449,9 @@ static int add_dependency(sd_bus *bus, char **args) {
if (!args[1])
return 0;
- target = unit_name_mangle_with_suffix(args[1], MANGLE_NOGLOB, ".target");
- if (!target)
- return log_oom();
+ r = unit_name_mangle_with_suffix(args[1], UNIT_NAME_NOGLOB, ".target", &target);
+ if (r < 0)
+ return log_error_errno(r, "Failed to mangle unit name: %m");
r = mangle_names(args+2, &names);
if (r < 0)
@@ -5489,10 +5494,6 @@ static int add_dependency(sd_bus *bus, char **args) {
if (r < 0)
return bus_log_create_error(r);
- r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
- if (r < 0)
- return bus_log_create_error(r);
-
r = sd_bus_message_append_strv(m, names);
if (r < 0)
return bus_log_create_error(r);
@@ -5507,7 +5508,7 @@ static int add_dependency(sd_bus *bus, char **args) {
return r;
}
- r = bus_deserialize_and_dump_unit_file_changes(reply, arg_quiet);
+ r = bus_deserialize_and_dump_unit_file_changes(reply, arg_quiet, NULL, NULL);
if (r < 0)
return r;
@@ -5539,41 +5540,29 @@ static int preset_all(sd_bus *bus, char **args) {
r = 0;
} else {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
_cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
polkit_agent_open_if_enabled();
- r = sd_bus_message_new_method_call(
+ r = sd_bus_call_method(
bus,
- &m,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
- "PresetAllUnitFiles");
- if (r < 0)
- return bus_log_create_error(r);
-
- r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
- if (r < 0)
- return bus_log_create_error(r);
-
- r = sd_bus_message_append(
- m,
+ "PresetAllUnitFiles",
+ &error,
+ &reply,
"sbb",
unit_file_preset_mode_to_string(arg_preset_mode),
arg_runtime,
arg_force);
- if (r < 0)
- return bus_log_create_error(r);
-
- r = sd_bus_call(bus, m, 0, &error, &reply);
if (r < 0) {
log_error("Failed to execute operation: %s", bus_error_message(&error, r));
return r;
}
- r = bus_deserialize_and_dump_unit_file_changes(reply, arg_quiet);
+ r = bus_deserialize_and_dump_unit_file_changes(reply, arg_quiet, NULL, NULL);
if (r < 0)
return r;
@@ -5756,10 +5745,10 @@ static int get_file_to_edit(const char *name, const char *user_home, const char
if (arg_runtime) {
if (access(path, F_OK) >= 0)
- return log_error_errno(EEXIST, "Refusing to create \"%s\" because it would be overriden by \"%s\" anyway.",
+ return log_error_errno(EEXIST, "Refusing to create \"%s\" because it would be overridden by \"%s\" anyway.",
run, path);
if (path2 && access(path2, F_OK) >= 0)
- return log_error_errno(EEXIST, "Refusing to create \"%s\" because it would be overriden by \"%s\" anyway.",
+ return log_error_errno(EEXIST, "Refusing to create \"%s\" because it would be overridden by \"%s\" anyway.",
run, path2);
*ret_path = run;
run = NULL;
@@ -5771,7 +5760,6 @@ static int get_file_to_edit(const char *name, const char *user_home, const char
return 0;
}
-
static int unit_file_create_dropin(const char *unit_name, const char *user_home, const char *user_runtime, char **ret_new_path, char **ret_tmp_path) {
char *tmp_new_path, *ending;
char *tmp_tmp_path;
@@ -5798,12 +5786,14 @@ static int unit_file_create_dropin(const char *unit_name, const char *user_home,
return 0;
}
-static int unit_file_create_copy(const char *unit_name,
- const char *fragment_path,
- const char *user_home,
- const char *user_runtime,
- char **ret_new_path,
- char **ret_tmp_path) {
+static int unit_file_create_copy(
+ const char *unit_name,
+ const char *fragment_path,
+ const char *user_home,
+ const char *user_runtime,
+ char **ret_new_path,
+ char **ret_tmp_path) {
+
char *tmp_new_path;
char *tmp_tmp_path;
int r;
@@ -5859,9 +5849,8 @@ static int run_editor(char **paths) {
if (pid == 0) {
const char **args;
- char **backup_editors = STRV_MAKE("nano", "vim", "vi");
char *editor;
- char **tmp_path, **original_path, **p;
+ char **tmp_path, **original_path, *p;
unsigned i = 1;
size_t argc;
@@ -5890,9 +5879,9 @@ static int run_editor(char **paths) {
execvp(editor, (char* const*) args);
}
- STRV_FOREACH(p, backup_editors) {
- args[0] = *p;
- execvp(*p, (char* const*) args);
+ FOREACH_STRING(p, "editor", "nano", "vim", "vi") {
+ args[0] = p;
+ execvp(p, (char* const*) args);
/* We do not fail if the editor doesn't exist
* because we want to try each one of them before
* failing.
@@ -5903,7 +5892,7 @@ static int run_editor(char **paths) {
}
}
- log_error("Cannot edit unit(s), no editor available. Please set either $SYSTEMD_EDITOR or $EDITOR or $VISUAL.");
+ log_error("Cannot edit unit(s), no editor available. Please set either $SYSTEMD_EDITOR, $EDITOR or $VISUAL.");
_exit(EXIT_FAILURE);
}
@@ -6049,6 +6038,7 @@ static void systemctl_help(void) {
" 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"
+ " --now Start or stop unit in addition to enabling or disabling it\n"
" -q --quiet Suppress output\n"
" --no-block Do not wait until operation finished\n"
" --no-wall Don't send wall message before halt/power-off/reboot\n"
@@ -6067,6 +6057,7 @@ static void systemctl_help(void) {
" -o --output=STRING Change journal output mode (short, short-iso,\n"
" short-precise, short-monotonic, verbose,\n"
" export, json, json-pretty, json-sse, cat)\n"
+ " --firmware-setup Tell the firmware to show the setup menu on next boot\n"
" --plain Print unit dependencies as a list instead of a tree\n\n"
"Unit Commands:\n"
" list-units [PATTERN...] List loaded units\n"
@@ -6246,6 +6237,8 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
ARG_STATE,
ARG_JOB_MODE,
ARG_PRESET_MODE,
+ ARG_FIRMWARE_SETUP,
+ ARG_NOW,
};
static const struct option options[] = {
@@ -6288,6 +6281,8 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
{ "state", required_argument, NULL, ARG_STATE },
{ "recursive", no_argument, NULL, 'r' },
{ "preset-mode", required_argument, NULL, ARG_PRESET_MODE },
+ { "firmware-setup", no_argument, NULL, ARG_FIRMWARE_SETUP },
+ { "now", no_argument, NULL, ARG_NOW },
{}
};
@@ -6528,6 +6523,10 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
arg_plain = true;
break;
+ case ARG_FIRMWARE_SETUP:
+ arg_firmware_setup = true;
+ break;
+
case ARG_STATE: {
const char *word, *state;
size_t size;
@@ -6564,6 +6563,10 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
break;
+ case ARG_NOW:
+ arg_now = true;
+ break;
+
case '?':
return -EINVAL;
@@ -7207,51 +7210,6 @@ found:
return verb->dispatch(bus, argv + optind);
}
-static int send_shutdownd(usec_t t, char mode, bool dry_run, bool warn, const char *message) {
-
- 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)
- + strlen("/run/systemd/shutdownd"),
- .msg_iov = iovec,
- .msg_iovlen = 1,
- };
-
- _cleanup_close_ int fd;
-
- fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0);
- if (fd < 0)
- return -errno;
-
- if (!isempty(message)) {
- iovec[1].iov_base = (char*) message;
- iovec[1].iov_len = strlen(message);
- msghdr.msg_iovlen++;
- }
-
- if (sendmsg(fd, &msghdr, MSG_NOSIGNAL) < 0)
- return -errno;
-
- return 0;
-}
-
static int reload_with_fallback(sd_bus *bus) {
if (bus) {
@@ -7359,23 +7317,68 @@ static int halt_main(sd_bus *bus) {
}
if (arg_when > 0) {
- _cleanup_free_ char *m;
+ _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_bus_close_unref_ sd_bus *b = NULL;
+ _cleanup_free_ char *m = NULL;
+
+ if (avoid_bus()) {
+ log_error("Unable to perform operation without bus connection.");
+ return -ENOSYS;
+ }
+
+ r = sd_bus_open_system(&b);
+ if (r < 0)
+ return log_error_errno(r, "Unable to open system bus: %m");
m = strv_join(arg_wall, " ");
if (!m)
return log_oom();
- r = send_shutdownd(arg_when,
- arg_action == ACTION_HALT ? 'H' :
- arg_action == ACTION_POWEROFF ? 'P' :
- arg_action == ACTION_KEXEC ? 'K' :
- 'r',
- arg_dry,
- !arg_no_wall,
- m);
+ r = sd_bus_set_property(
+ b,
+ "org.freedesktop.login1",
+ "/org/freedesktop/login1",
+ "org.freedesktop.login1.Manager",
+ "WallMessage",
+ &error,
+ "s", m);
+ if (r < 0) {
+ log_warning_errno(r, "Failed to set WallMessage property in logind: %s",
+ bus_error_message(&error, r));
+ sd_bus_error_free(&error);
+ }
+
+ r = sd_bus_set_property(
+ b,
+ "org.freedesktop.login1",
+ "/org/freedesktop/login1",
+ "org.freedesktop.login1.Manager",
+ "EnableWallMessages",
+ &error,
+ "b", !arg_no_wall);
+ if (r < 0) {
+ log_warning_errno(r, "Failed to set EnableWallMessages property in logind: %s",
+ bus_error_message(&error, r));
+ sd_bus_error_free(&error);
+ }
+ r = sd_bus_call_method(
+ b,
+ "org.freedesktop.login1",
+ "/org/freedesktop/login1",
+ "org.freedesktop.login1.Manager",
+ "ScheduleShutdown",
+ &error,
+ NULL,
+ "st",
+ arg_action == ACTION_HALT ? "halt" :
+ arg_action == ACTION_POWEROFF ? "poweroff" :
+ arg_action == ACTION_KEXEC ? "kexec" :
+ "reboot",
+ arg_when);
if (r < 0)
- log_warning_errno(r, "Failed to talk to shutdownd, proceeding with immediate shutdown: %m");
+ log_warning_errno(r, "Failed to call ScheduleShutdown in logind, proceeding with immediate shutdown: %s",
+ bus_error_message(&error, r));
else {
char date[FORMAT_TIMESTAMP_MAX];
@@ -7453,14 +7456,12 @@ int main(int argc, char*argv[]) {
goto finish;
}
- /* Increase max number of open files to 16K if we can, we
- * might needs this when browsing journal files, which might
- * be split up into many files. */
- setrlimit_closest(RLIMIT_NOFILE, &RLIMIT_MAKE_CONST(16384));
-
if (!avoid_bus())
r = bus_open_transport_systemd(arg_transport, arg_host, arg_scope != UNIT_FILE_SYSTEM, &bus);
+ if (bus)
+ sd_bus_set_allow_interactive_authorization(bus, arg_ask_password);
+
/* systemctl_main() will print an error message for the bus
* connection, but only if it needs to */
@@ -7493,8 +7494,19 @@ int main(int argc, char*argv[]) {
break;
case ACTION_CANCEL_SHUTDOWN: {
+ _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_bus_close_unref_ sd_bus *b = NULL;
_cleanup_free_ char *m = NULL;
+ if (avoid_bus()) {
+ log_error("Unable to perform operation without bus connection.");
+ return -ENOSYS;
+ }
+
+ r = sd_bus_open_system(&b);
+ if (r < 0)
+ return log_error_errno(r, "Unable to open system bus: %m");
+
if (arg_wall) {
m = strv_join(arg_wall, " ");
if (!m) {
@@ -7503,9 +7515,45 @@ int main(int argc, char*argv[]) {
}
}
- r = send_shutdownd(arg_when, SD_SHUTDOWN_NONE, false, !arg_no_wall, m);
+ r = sd_bus_set_property(
+ b,
+ "org.freedesktop.login1",
+ "/org/freedesktop/login1",
+ "org.freedesktop.login1.Manager",
+ "WallMessage",
+ &error,
+ "s", arg_wall);
+ if (r < 0) {
+ log_warning_errno(r, "Failed to set WallMessage property in logind: %s",
+ bus_error_message(&error, r));
+ sd_bus_error_free(&error);
+ }
+
+ r = sd_bus_set_property(
+ b,
+ "org.freedesktop.login1",
+ "/org/freedesktop/login1",
+ "org.freedesktop.login1.Manager",
+ "EnableWallMessages",
+ &error,
+ "b", !arg_no_wall);
+ if (r < 0) {
+ log_warning_errno(r, "Failed to set EnableWallMessages property in logind: %s",
+ bus_error_message(&error, r));
+ sd_bus_error_free(&error);
+ }
+
+ r = sd_bus_call_method(
+ b,
+ "org.freedesktop.login1",
+ "/org/freedesktop/login1",
+ "org.freedesktop.login1.Manager",
+ "CancelScheduledShutdown",
+ &error,
+ NULL, NULL);
if (r < 0)
- log_warning_errno(r, "Failed to talk to shutdownd, shutdown hasn't been cancelled: %m");
+ log_warning_errno(r, "Failed to talk to logind, shutdown hasn't been cancelled: %s",
+ bus_error_message(&error, r));
break;
}
diff --git a/src/systemctl/systemd-sysv-install.SKELETON b/src/systemctl/systemd-sysv-install.SKELETON
new file mode 100755
index 0000000000..a53a3e6221
--- /dev/null
+++ b/src/systemctl/systemd-sysv-install.SKELETON
@@ -0,0 +1,47 @@
+#!/bin/sh
+# This script is called by "systemctl enable/disable" when the given unit is a
+# SysV init.d script. It needs to call the distribution's mechanism for
+# enabling/disabling those, such as chkconfig, update-rc.d, or similar. This
+# can optionally take a --root argument for enabling a SysV init script
+# in a chroot or similar.
+set -e
+
+usage() {
+ echo "Usage: $0 [--root=path] enable|disable|is-enabled <sysv script name>" >&2
+ exit 1
+}
+
+# parse options
+eval set -- "$(getopt -o r: --long root: -- "$@")"
+while true; do
+ case "$1" in
+ -r|--root)
+ ROOT="$2"
+ shift 2 ;;
+ --) shift ; break ;;
+ *) usage ;;
+ esac
+done
+
+NAME="$2"
+[ -n "$NAME" ] || usage
+
+case "$1" in
+ enable)
+ # call the command to enable SysV init script $NAME here
+ # (consider optional $ROOT)
+ echo "IMPLEMENT ME: enabling SysV init.d script $NAME"
+ ;;
+ disable)
+ # call the command to disable SysV init script $NAME here
+ # (consider optional $ROOT)
+ echo "IMPLEMENT ME: disabling SysV init.d script $NAME"
+ ;;
+ is-enabled)
+ # exit with 0 if $NAME is enabled, non-zero if it is disabled
+ # (consider optional $ROOT)
+ echo "IMPLEMENT ME: checking SysV init.d script $NAME"
+ ;;
+ *)
+ usage ;;
+esac
diff --git a/src/systemd/sd-bus.h b/src/systemd/sd-bus.h
index ca2d83e0e1..e6e2ecd0b7 100644
--- a/src/systemd/sd-bus.h
+++ b/src/systemd/sd-bus.h
@@ -57,37 +57,40 @@ typedef struct {
enum {
SD_BUS_CREDS_PID = 1ULL << 0,
SD_BUS_CREDS_TID = 1ULL << 1,
- SD_BUS_CREDS_UID = 1ULL << 2,
- SD_BUS_CREDS_EUID = 1ULL << 3,
- SD_BUS_CREDS_SUID = 1ULL << 4,
- SD_BUS_CREDS_FSUID = 1ULL << 5,
- SD_BUS_CREDS_GID = 1ULL << 6,
- SD_BUS_CREDS_EGID = 1ULL << 7,
- SD_BUS_CREDS_SGID = 1ULL << 8,
- SD_BUS_CREDS_FSGID = 1ULL << 9,
- SD_BUS_CREDS_SUPPLEMENTARY_GIDS = 1ULL << 10,
- SD_BUS_CREDS_COMM = 1ULL << 11,
- SD_BUS_CREDS_TID_COMM = 1ULL << 12,
- SD_BUS_CREDS_EXE = 1ULL << 13,
- SD_BUS_CREDS_CMDLINE = 1ULL << 14,
- SD_BUS_CREDS_CGROUP = 1ULL << 15,
- SD_BUS_CREDS_UNIT = 1ULL << 16,
- SD_BUS_CREDS_USER_UNIT = 1ULL << 17,
+ SD_BUS_CREDS_PPID = 1ULL << 2,
+ SD_BUS_CREDS_UID = 1ULL << 3,
+ SD_BUS_CREDS_EUID = 1ULL << 4,
+ SD_BUS_CREDS_SUID = 1ULL << 5,
+ SD_BUS_CREDS_FSUID = 1ULL << 6,
+ SD_BUS_CREDS_GID = 1ULL << 7,
+ SD_BUS_CREDS_EGID = 1ULL << 8,
+ SD_BUS_CREDS_SGID = 1ULL << 9,
+ SD_BUS_CREDS_FSGID = 1ULL << 10,
+ SD_BUS_CREDS_SUPPLEMENTARY_GIDS = 1ULL << 11,
+ SD_BUS_CREDS_COMM = 1ULL << 12,
+ SD_BUS_CREDS_TID_COMM = 1ULL << 13,
+ SD_BUS_CREDS_EXE = 1ULL << 14,
+ SD_BUS_CREDS_CMDLINE = 1ULL << 15,
+ SD_BUS_CREDS_CGROUP = 1ULL << 16,
+ SD_BUS_CREDS_UNIT = 1ULL << 17,
SD_BUS_CREDS_SLICE = 1ULL << 18,
- SD_BUS_CREDS_SESSION = 1ULL << 19,
- SD_BUS_CREDS_OWNER_UID = 1ULL << 20,
- SD_BUS_CREDS_EFFECTIVE_CAPS = 1ULL << 21,
- SD_BUS_CREDS_PERMITTED_CAPS = 1ULL << 22,
- SD_BUS_CREDS_INHERITABLE_CAPS = 1ULL << 23,
- SD_BUS_CREDS_BOUNDING_CAPS = 1ULL << 24,
- SD_BUS_CREDS_SELINUX_CONTEXT = 1ULL << 25,
- SD_BUS_CREDS_AUDIT_SESSION_ID = 1ULL << 26,
- SD_BUS_CREDS_AUDIT_LOGIN_UID = 1ULL << 27,
- SD_BUS_CREDS_UNIQUE_NAME = 1ULL << 28,
- SD_BUS_CREDS_WELL_KNOWN_NAMES = 1ULL << 29,
- SD_BUS_CREDS_DESCRIPTION = 1ULL << 30,
+ SD_BUS_CREDS_USER_UNIT = 1ULL << 19,
+ SD_BUS_CREDS_USER_SLICE = 1ULL << 20,
+ SD_BUS_CREDS_SESSION = 1ULL << 21,
+ SD_BUS_CREDS_OWNER_UID = 1ULL << 22,
+ SD_BUS_CREDS_EFFECTIVE_CAPS = 1ULL << 23,
+ SD_BUS_CREDS_PERMITTED_CAPS = 1ULL << 24,
+ SD_BUS_CREDS_INHERITABLE_CAPS = 1ULL << 25,
+ SD_BUS_CREDS_BOUNDING_CAPS = 1ULL << 26,
+ SD_BUS_CREDS_SELINUX_CONTEXT = 1ULL << 27,
+ SD_BUS_CREDS_AUDIT_SESSION_ID = 1ULL << 28,
+ SD_BUS_CREDS_AUDIT_LOGIN_UID = 1ULL << 29,
+ SD_BUS_CREDS_TTY = 1ULL << 30,
+ SD_BUS_CREDS_UNIQUE_NAME = 1ULL << 31,
+ SD_BUS_CREDS_WELL_KNOWN_NAMES = 1ULL << 32,
+ SD_BUS_CREDS_DESCRIPTION = 1ULL << 33,
SD_BUS_CREDS_AUGMENT = 1ULL << 63, /* special flag, if on sd-bus will augment creds struct, in a potentially race-full way. */
- _SD_BUS_CREDS_ALL = (1ULL << 32) -1,
+ _SD_BUS_CREDS_ALL = (1ULL << 34) -1,
};
enum {
@@ -98,11 +101,11 @@ enum {
/* Callbacks */
-typedef int (*sd_bus_message_handler_t)(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *ret_error);
+typedef int (*sd_bus_message_handler_t)(sd_bus_message *m, void *userdata, sd_bus_error *ret_error);
typedef int (*sd_bus_property_get_t) (sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *ret_error);
typedef int (*sd_bus_property_set_t) (sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *value, void *userdata, sd_bus_error *ret_error);
typedef int (*sd_bus_object_find_t) (sd_bus *bus, const char *path, const char *interface, void *userdata, void **ret_found, sd_bus_error *ret_error);
-typedef int (*sd_bus_node_enumerator_t) (sd_bus *bus, const char *path, void *userdata, char ***ret_nodes, sd_bus_error *ret_error);
+typedef int (*sd_bus_node_enumerator_t) (sd_bus *bus, const char *prefix, void *userdata, char ***ret_nodes, sd_bus_error *ret_error);
typedef int (*sd_bus_track_handler_t) (sd_bus_track *track, void *userdata);
#include "sd-bus-protocol.h"
@@ -138,11 +141,13 @@ int sd_bus_set_monitor(sd_bus *bus, int b);
int sd_bus_is_monitor(sd_bus *bus);
int sd_bus_set_description(sd_bus *bus, const char *description);
int sd_bus_get_description(sd_bus *bus, const char **description);
+int sd_bus_negotiate_creds(sd_bus *bus, int b, uint64_t creds_mask);
+int sd_bus_negotiate_timestamp(sd_bus *bus, int b);
int sd_bus_negotiate_fds(sd_bus *bus, int b);
int sd_bus_can_send(sd_bus *bus, char type);
-int sd_bus_negotiate_timestamp(sd_bus *bus, int b);
-int sd_bus_negotiate_creds(sd_bus *bus, int b, uint64_t creds_mask);
int sd_bus_get_creds_mask(sd_bus *bus, uint64_t *creds_mask);
+int sd_bus_set_allow_interactive_authorization(sd_bus *bus, int b);
+int sd_bus_get_allow_interactive_authorization(sd_bus *bus);
int sd_bus_start(sd_bus *ret);
@@ -298,7 +303,7 @@ int sd_bus_get_property(sd_bus *bus, const char *destination, const char *path,
int sd_bus_get_property_trivial(sd_bus *bus, const char *destination, const char *path, const char *interface, const char *member, sd_bus_error *ret_error, char type, void *ret_ptr);
int sd_bus_get_property_string(sd_bus *bus, const char *destination, const char *path, const char *interface, const char *member, sd_bus_error *ret_error, char **ret); /* free the result! */
int sd_bus_get_property_strv(sd_bus *bus, const char *destination, const char *path, const char *interface, const char *member, sd_bus_error *ret_error, char ***ret); /* free the result! */
-int sd_bus_set_property(sd_bus *bus, const char *destination, const char *path, const char *interface, const char *member, sd_bus_error *ret_error, const char *ret_type, ...);
+int sd_bus_set_property(sd_bus *bus, const char *destination, const char *path, const char *interface, const char *member, sd_bus_error *ret_error, const char *type, ...);
int sd_bus_reply_method_return(sd_bus_message *call, const char *types, ...);
int sd_bus_reply_method_error(sd_bus_message *call, const sd_bus_error *e);
@@ -327,8 +332,10 @@ int sd_bus_creds_new_from_pid(sd_bus_creds **ret, pid_t pid, uint64_t creds_mask
sd_bus_creds *sd_bus_creds_ref(sd_bus_creds *c);
sd_bus_creds *sd_bus_creds_unref(sd_bus_creds *c);
uint64_t sd_bus_creds_get_mask(const sd_bus_creds *c);
+uint64_t sd_bus_creds_get_augmented_mask(const sd_bus_creds *c);
int sd_bus_creds_get_pid(sd_bus_creds *c, pid_t *pid);
+int sd_bus_creds_get_ppid(sd_bus_creds *c, pid_t *ppid);
int sd_bus_creds_get_tid(sd_bus_creds *c, pid_t *tid);
int sd_bus_creds_get_uid(sd_bus_creds *c, uid_t *uid);
int sd_bus_creds_get_euid(sd_bus_creds *c, uid_t *euid);
@@ -345,8 +352,9 @@ int sd_bus_creds_get_exe(sd_bus_creds *c, const char **exe);
int sd_bus_creds_get_cmdline(sd_bus_creds *c, char ***cmdline);
int sd_bus_creds_get_cgroup(sd_bus_creds *c, const char **cgroup);
int sd_bus_creds_get_unit(sd_bus_creds *c, const char **unit);
-int sd_bus_creds_get_user_unit(sd_bus_creds *c, const char **unit);
int sd_bus_creds_get_slice(sd_bus_creds *c, const char **slice);
+int sd_bus_creds_get_user_unit(sd_bus_creds *c, const char **unit);
+int sd_bus_creds_get_user_slice(sd_bus_creds *c, const char **slice);
int sd_bus_creds_get_session(sd_bus_creds *c, const char **session);
int sd_bus_creds_get_owner_uid(sd_bus_creds *c, uid_t *uid);
int sd_bus_creds_has_effective_cap(sd_bus_creds *c, int capability);
@@ -356,6 +364,7 @@ int sd_bus_creds_has_bounding_cap(sd_bus_creds *c, int capability);
int sd_bus_creds_get_selinux_context(sd_bus_creds *c, const char **context);
int sd_bus_creds_get_audit_session_id(sd_bus_creds *c, uint32_t *sessionid);
int sd_bus_creds_get_audit_login_uid(sd_bus_creds *c, uid_t *loginuid);
+int sd_bus_creds_get_tty(sd_bus_creds *c, const char **tty);
int sd_bus_creds_get_unique_name(sd_bus_creds *c, const char **name);
int sd_bus_creds_get_well_known_names(sd_bus_creds *c, char ***names);
int sd_bus_creds_get_description(sd_bus_creds *c, const char **name);
diff --git a/src/systemd/sd-daemon.h b/src/systemd/sd-daemon.h
index b878b4d8a6..861dc8f1f4 100644
--- a/src/systemd/sd-daemon.h
+++ b/src/systemd/sd-daemon.h
@@ -168,7 +168,7 @@ int sd_is_mq(int fd, const char *path);
value daemons should send is "READY=1".
STATUS=... Passes a single-line status string back to systemd
- that describes the daemon state. This is free-from
+ that describes the daemon state. This is free-form
and can be used for various purposes: general state
feedback, fsck-like programs could pass completion
percentages and failing programs could pass a human
diff --git a/src/systemd/sd-device.h b/src/systemd/sd-device.h
new file mode 100644
index 0000000000..38cb2a1102
--- /dev/null
+++ b/src/systemd/sd-device.h
@@ -0,0 +1,99 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef foosddevicehfoo
+#define foosddevicehfoo
+
+/***
+ This file is part of systemd.
+
+ Copyright 2008-2012 Kay Sievers <kay@vrfy.org>
+ Copyright 2014-2015 Tom Gundersen <teg@jklm.no>
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser 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 <stdint.h>
+
+#include "_sd-common.h"
+
+_SD_BEGIN_DECLARATIONS;
+
+typedef struct sd_device sd_device;
+typedef struct sd_device_enumerator sd_device_enumerator;
+
+/* device */
+
+sd_device *sd_device_ref(sd_device *device);
+sd_device *sd_device_unref(sd_device *device);
+
+int sd_device_new_from_syspath(sd_device **ret, const char *syspath);
+int sd_device_new_from_devnum(sd_device **ret, char type, dev_t devnum);
+int sd_device_new_from_subsystem_sysname(sd_device **ret, const char *subsystem, const char *sysname);
+int sd_device_new_from_device_id(sd_device **ret, const char *id);
+
+int sd_device_get_parent(sd_device *child, sd_device **ret);
+int sd_device_get_parent_with_subsystem_devtype(sd_device *child, const char *subsystem, const char *devtype, sd_device **ret);
+
+int sd_device_get_syspath(sd_device *device, const char **ret);
+int sd_device_get_subsystem(sd_device *device, const char **ret);
+int sd_device_get_devtype(sd_device *device, const char **ret);
+int sd_device_get_devnum(sd_device *device, dev_t *devnum);
+int sd_device_get_ifindex(sd_device *device, int *ifindex);
+int sd_device_get_driver(sd_device *device, const char **ret);
+int sd_device_get_devpath(sd_device *device, const char **ret);
+int sd_device_get_devname(sd_device *device, const char **ret);
+int sd_device_get_sysname(sd_device *device, const char **ret);
+int sd_device_get_sysnum(sd_device *device, const char **ret);
+
+int sd_device_get_is_initialized(sd_device *device, int *initialized);
+int sd_device_get_usec_since_initialized(sd_device *device, uint64_t *usec);
+
+const char *sd_device_get_tag_first(sd_device *device);
+const char *sd_device_get_tag_next(sd_device *device);
+const char *sd_device_get_devlink_first(sd_device *device);
+const char *sd_device_get_devlink_next(sd_device *device);
+const char *sd_device_get_property_first(sd_device *device, const char **value);
+const char *sd_device_get_property_next(sd_device *device, const char **value);
+const char *sd_device_get_sysattr_first(sd_device *device);
+const char *sd_device_get_sysattr_next(sd_device *device);
+
+int sd_device_has_tag(sd_device *device, const char *tag);
+int sd_device_get_property_value(sd_device *device, const char *key, const char **value);
+int sd_device_get_sysattr_value(sd_device *device, const char *sysattr, const char **_value);
+
+int sd_device_set_sysattr_value(sd_device *device, const char *sysattr, char *value);
+
+/* device enumerator */
+
+int sd_device_enumerator_new(sd_device_enumerator **ret);
+sd_device_enumerator *sd_device_enumerator_ref(sd_device_enumerator *enumerator);
+sd_device_enumerator *sd_device_enumerator_unref(sd_device_enumerator *enumerator);
+
+sd_device *sd_device_enumerator_get_device_first(sd_device_enumerator *enumerator);
+sd_device *sd_device_enumerator_get_device_next(sd_device_enumerator *enumerator);
+sd_device *sd_device_enumerator_get_subsystem_first(sd_device_enumerator *enumerator);
+sd_device *sd_device_enumerator_get_subsystem_next(sd_device_enumerator *enumerator);
+
+int sd_device_enumerator_add_match_subsystem(sd_device_enumerator *enumerator, const char *subsystem, int match);
+int sd_device_enumerator_add_match_sysattr(sd_device_enumerator *enumerator, const char *sysattr, const char *value, int match);
+int sd_device_enumerator_add_match_property(sd_device_enumerator *enumerator, const char *property, const char *value);
+int sd_device_enumerator_add_match_sysname(sd_device_enumerator *enumerator, const char *sysname);
+int sd_device_enumerator_add_match_tag(sd_device_enumerator *enumerator, const char *tag);
+int sd_device_enumerator_add_match_parent(sd_device_enumerator *enumerator, sd_device *parent);
+int sd_device_enumerator_allow_uninitialized(sd_device_enumerator *enumerator);
+
+_SD_END_DECLARATIONS;
+
+#endif
diff --git a/src/systemd/sd-event.h b/src/systemd/sd-event.h
index 25a10f99ab..565de5495a 100644
--- a/src/systemd/sd-event.h
+++ b/src/systemd/sd-event.h
@@ -51,8 +51,8 @@ enum {
};
enum {
- SD_EVENT_PASSIVE,
- SD_EVENT_PREPARED,
+ SD_EVENT_INITIAL,
+ SD_EVENT_ARMED,
SD_EVENT_PENDING,
SD_EVENT_RUNNING,
SD_EVENT_EXITING,
diff --git a/src/systemd/sd-id128.h b/src/systemd/sd-id128.h
index 48fd87671b..9f445278bb 100644
--- a/src/systemd/sd-id128.h
+++ b/src/systemd/sd-id128.h
@@ -106,6 +106,10 @@ _sd_pure_ static inline int sd_id128_equal(sd_id128_t a, sd_id128_t b) {
return memcmp(&a, &b, 16) == 0;
}
+_sd_pure_ static inline int sd_id128_is_null(sd_id128_t a) {
+ return a.qwords[0] == 0 && a.qwords[1] == 0;
+}
+
#define SD_ID128_NULL ((const sd_id128_t) { .qwords = { 0, 0 }})
_SD_END_DECLARATIONS;
diff --git a/src/systemd/sd-login.h b/src/systemd/sd-login.h
index 24c8595064..9260396d5d 100644
--- a/src/systemd/sd-login.h
+++ b/src/systemd/sd-login.h
@@ -62,22 +62,25 @@ 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, for system
+/* Get systemd non-slice 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
+/* Get systemd non-slice 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 slice name from PID. */
+int sd_pid_get_slice(pid_t pid, char **slice);
+
+/* Get user slice name from PID. */
+int sd_pid_get_user_slice(pid_t pid, char **slice);
+
/* Get machine name from PID, for processes assigned to a VM or
* container. This will return an error for non-machine processes. */
int sd_pid_get_machine_name(pid_t pid, char **machine);
-/* Get slice name from PID. */
-int sd_pid_get_slice(pid_t pid, char **slice);
-
/* Similar to sd_pid_get_session(), but retrieves data about peer of
* connected AF_UNIX socket */
int sd_peer_get_session(int fd, char **session);
@@ -94,14 +97,18 @@ int sd_peer_get_unit(int fd, char **unit);
* connected AF_UNIX socket */
int sd_peer_get_user_unit(int fd, char **unit);
-/* Similar to sd_pid_get_machine_name(), but retrieves data about peer
- * of connected AF_UNIX socket */
-int sd_peer_get_machine_name(int fd, char **machine);
-
/* Similar to sd_pid_get_slice(), but retrieves data about peer of
* connected AF_UNIX socket */
int sd_peer_get_slice(int fd, char **slice);
+/* Similar to sd_pid_get_user_slice(), but retrieves data about peer of
+ * connected AF_UNIX socket */
+int sd_peer_get_user_slice(int fd, char **slice);
+
+/* Similar to sd_pid_get_machine_name(), but retrieves data about peer
+ * of connected AF_UNIX socket */
+int sd_peer_get_machine_name(int fd, char **machine);
+
/* Get state from UID. Possible states: offline, lingering, online, active, closing */
int sd_uid_get_state(uid_t uid, char **state);
diff --git a/src/systemd/sd-messages.h b/src/systemd/sd-messages.h
index a8379e098c..8aedaec6d1 100644
--- a/src/systemd/sd-messages.h
+++ b/src/systemd/sd-messages.h
@@ -81,7 +81,7 @@ _SD_BEGIN_DECLARATIONS;
#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_INVALID_CONFIGURATION 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)
diff --git a/src/systemd/sd-network.h b/src/systemd/sd-network.h
index 027730d11d..4d96c867d9 100644
--- a/src/systemd/sd-network.h
+++ b/src/systemd/sd-network.h
@@ -116,6 +116,12 @@ int sd_network_link_get_lldp(int ifindex, char **lldp);
/* Get the DNS domain names for a given link. */
int sd_network_link_get_domains(int ifindex, char ***domains);
+/* Get the CARRIERS to which current link is bound to. */
+int sd_network_link_get_carrier_bound_to(int ifindex, char ***carriers);
+
+/* Get the CARRIERS that are bound to current link. */
+int sd_network_link_get_carrier_bound_by(int ifindex, char ***carriers);
+
/* Returns whether or not domains that don't match any link should be resolved
* on this link. 1 for yes, 0 for no and negative value for error */
int sd_network_link_get_wildcard_domain(int ifindex);
diff --git a/src/systemd/sd-shutdown.h b/src/systemd/sd-shutdown.h
deleted file mode 100644
index 9ff377f4e4..0000000000
--- a/src/systemd/sd-shutdown.h
+++ /dev/null
@@ -1,119 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#ifndef foosdshutdownhfoo
-#define foosdshutdownhfoo
-
-/***
- 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/>.
-***/
-
-/* Interface for scheduling and cancelling timed shutdowns. */
-
-#include <inttypes.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#ifndef _sd_packed_
-# define _sd_packed_ __attribute__((packed))
-#endif
-
-typedef enum sd_shutdown_mode {
- SD_SHUTDOWN_NONE = 0,
- SD_SHUTDOWN_REBOOT = 'r',
- SD_SHUTDOWN_POWEROFF = 'P',
- SD_SHUTDOWN_HALT = 'H',
- SD_SHUTDOWN_KEXEC = 'K'
-} sd_shutdown_mode_t;
-
-/* Calculate the size of the message as "offsetof(struct
- * sd_shutdown_command, wall_message) +
- * strlen(command.wall_message)" */
-struct sd_shutdown_command {
- /* Microseconds after the epoch 1970 UTC */
- uint64_t usec;
-
- /* H, P, r, i.e. the switches usually passed to
- * /usr/bin/shutdown to select whether to halt, power-off or
- * reboot the machine */
- sd_shutdown_mode_t mode:8;
-
- /* If non-zero, don't actually shut down, just pretend */
- unsigned dry_run:1;
-
- /* If non-zero, send our wall message */
- unsigned warn_wall:1;
-
- /* The wall message to send around. Leave empty for the
- * default wall message */
- char wall_message[];
-} _sd_packed_;
-
-/* The scheme is very simple:
- *
- * To schedule a shutdown, simply fill in and send a single
- * AF_UNIX/SOCK_DGRAM datagram with the structure above suffixed with
- * the wall message to the socket /run/systemd/shutdownd (leave an
- * empty wall message for the default shutdown message). To calculate
- * the size of the message, use "offsetof(struct sd_shutdown_command,
- * wall_message) + strlen(command.wall_message)".
- *
- * To cancel a shutdown, do the same, but send a fully zeroed-out
- * structure.
- *
- * To be notified about scheduled shutdowns, create an inotify watch
- * on /run/shutdown/. Whenever a file called "scheduled" appears, a
- * shutdown is scheduled. If it is removed, it is canceled. If it is
- * replaced, the scheduled shutdown has been changed. The file contains
- * a simple, environment-like block that contains information about
- * the scheduled shutdown:
- *
- * USEC=
- * encodes the time for the shutdown in usecs since the epoch UTC,
- * formatted as a numeric string.
- *
- * WARN_WALL=
- * is 1 if a wall message shall be sent
- *
- * DRY_RUN=
- * is 1 if a dry-run shutdown is scheduled
- *
- * MODE=
- * is the shutdown mode, one of "poweroff", "reboot", "halt", "kexec"
- *
- * WALL_MESSAGE=
- * is the wall message to use, with all special characters escaped in C-style.
- *
- * Note that some fields might be missing if they do not apply.
- *
- * Note that the file is first written to a temporary file and then
- * renamed, in order to provide atomic properties for readers: if the
- * file exists under the name "scheduled", it is guaranteed to be fully
- * written. A reader should ignore all files in that directory by any
- * other name.
- *
- * Scheduled shutdowns are only accepted from privileged processes,
- * but anyone may watch the directory and the file in it.
- */
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/src/sysusers/sysusers.c b/src/sysusers/sysusers.c
index e47bcb4dca..d7ba482834 100644
--- a/src/sysusers/sysusers.c
+++ b/src/sysusers/sysusers.c
@@ -19,7 +19,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <sys/types.h>
#include <pwd.h>
#include <grp.h>
#include <shadow.h>
@@ -36,10 +35,10 @@
#include "conf-files.h"
#include "copy.h"
#include "utf8.h"
-#include "label.h"
#include "fileio-label.h"
#include "uid-range.h"
#include "selinux-util.h"
+#include "formats-util.h"
typedef enum ItemType {
ADD_USER = 'u',
@@ -81,15 +80,13 @@ static uid_t search_uid = UID_INVALID;
static UidRange *uid_range = NULL;
static unsigned n_uid_range = 0;
-#define fix_root(x) (arg_root ? strjoina(arg_root, x) : x)
-
static int load_user_database(void) {
_cleanup_fclose_ FILE *f = NULL;
const char *passwd_path;
struct passwd *pw;
int r;
- passwd_path = fix_root("/etc/passwd");
+ passwd_path = prefix_roota(arg_root, "/etc/passwd");
f = fopen(passwd_path, "re");
if (!f)
return errno == ENOENT ? 0 : -errno;
@@ -141,7 +138,7 @@ static int load_group_database(void) {
struct group *gr;
int r;
- group_path = fix_root("/etc/group");
+ group_path = prefix_roota(arg_root, "/etc/group");
f = fopen(group_path, "re");
if (!f)
return errno == ENOENT ? 0 : -errno;
@@ -370,7 +367,7 @@ static int write_files(void) {
_cleanup_fclose_ FILE *original = NULL;
/* First we update the actual group list file */
- group_path = fix_root("/etc/group");
+ group_path = prefix_roota(arg_root, "/etc/group");
r = fopen_temporary_label("/etc/group", group_path, &group, &group_tmp);
if (r < 0)
goto finish;
@@ -449,7 +446,7 @@ static int write_files(void) {
}
/* OK, now also update the shadow file for the group list */
- gshadow_path = fix_root("/etc/gshadow");
+ gshadow_path = prefix_roota(arg_root, "/etc/gshadow");
r = fopen_temporary_label("/etc/gshadow", gshadow_path, &gshadow, &gshadow_tmp);
if (r < 0)
goto finish;
@@ -515,7 +512,7 @@ static int write_files(void) {
long lstchg;
/* First we update the user database itself */
- passwd_path = fix_root("/etc/passwd");
+ passwd_path = prefix_roota(arg_root, "/etc/passwd");
r = fopen_temporary_label("/etc/passwd", passwd_path, &passwd, &passwd_tmp);
if (r < 0)
goto finish;
@@ -600,11 +597,13 @@ static int write_files(void) {
}
/* The we update the shadow database */
- shadow_path = fix_root("/etc/shadow");
+ shadow_path = prefix_roota(arg_root, "/etc/shadow");
r = fopen_temporary_label("/etc/shadow", shadow_path, &shadow, &shadow_tmp);
if (r < 0)
goto finish;
+ lstchg = (long) (now(CLOCK_REALTIME) / USEC_PER_DAY);
+
original = fopen(shadow_path, "re");
if (original) {
struct spwd *sp;
@@ -618,8 +617,13 @@ static int write_files(void) {
i = hashmap_get(users, sp->sp_namp);
if (i && i->todo_user) {
- r = -EEXIST;
- goto finish;
+ /* we will update the existing entry */
+ sp->sp_lstchg = lstchg;
+
+ /* only the /etc/shadow stage is left, so we can
+ * safely remove the item from the todo set */
+ i->todo_user = false;
+ hashmap_remove(todo_uids, UID_TO_PTR(i->uid));
}
errno = 0;
@@ -642,7 +646,6 @@ static int write_files(void) {
goto finish;
}
- lstchg = (long) (now(CLOCK_REALTIME) / USEC_PER_DAY);
HASHMAP_FOREACH(i, todo_uids, iterator) {
struct spwd n = {
.sp_namp = i->name,
@@ -797,7 +800,7 @@ static int uid_is_ok(uid_t uid, const char *name) {
static int root_stat(const char *p, struct stat *st) {
const char *fix;
- fix = fix_root(p);
+ fix = prefix_roota(arg_root, p);
if (stat(fix, st) < 0)
return -errno;
@@ -879,7 +882,6 @@ static int add_user(Item *i) {
if (!arg_root) {
struct passwd *p;
- struct spwd *sp;
/* Also check NSS */
errno = 0;
@@ -895,16 +897,6 @@ static int add_user(Item *i) {
}
if (!IN_SET(errno, 0, ENOENT))
return log_error_errno(errno, "Failed to check if user %s already exists: %m", i->name);
-
- /* And shadow too, just to be sure */
- errno = 0;
- sp = getspnam(i->name);
- if (sp) {
- log_error("User %s already exists in shadow database, but not in user database.", i->name);
- return -EBADMSG;
- }
- if (!IN_SET(errno, 0, ENOENT))
- return log_error_errno(errno, "Failed to check if user %s already exists in shadow database: %m", i->name);
}
/* Try to use the suggested numeric uid */
@@ -1391,7 +1383,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
/* Parse columns */
p = buffer;
- r = unquote_many_words(&p, &action, &name, &id, &description, &home, NULL);
+ r = unquote_many_words(&p, 0, &action, &name, &id, &description, &home, NULL);
if (r < 0) {
log_error("[%s:%u] Syntax error.", fname, line);
return r;
diff --git a/src/sysv-generator/sysv-generator.c b/src/sysv-generator/sysv-generator.c
index 6e39b449eb..9ae518ac4a 100644
--- a/src/sysv-generator/sysv-generator.c
+++ b/src/sysv-generator/sysv-generator.c
@@ -31,14 +31,11 @@
#include "path-util.h"
#include "path-lookup.h"
#include "log.h"
-#include "unit.h"
#include "unit-name.h"
#include "special.h"
-#include "exit-status.h"
-#include "def.h"
-#include "env-util.h"
-#include "fileio.h"
#include "hashmap.h"
+#include "set.h"
+#include "install.h"
typedef enum RunlevelType {
RUNLEVEL_UP,
@@ -51,11 +48,11 @@ static const struct {
const RunlevelType type;
} rcnd_table[] = {
/* Standard SysV runlevels for start-up */
- { "rc1.d", SPECIAL_RESCUE_TARGET, RUNLEVEL_UP },
- { "rc2.d", SPECIAL_RUNLEVEL2_TARGET, RUNLEVEL_UP },
- { "rc3.d", SPECIAL_RUNLEVEL3_TARGET, RUNLEVEL_UP },
- { "rc4.d", SPECIAL_RUNLEVEL4_TARGET, RUNLEVEL_UP },
- { "rc5.d", SPECIAL_RUNLEVEL5_TARGET, RUNLEVEL_UP },
+ { "rc1.d", SPECIAL_RESCUE_TARGET, RUNLEVEL_UP },
+ { "rc2.d", SPECIAL_MULTI_USER_TARGET, RUNLEVEL_UP },
+ { "rc3.d", SPECIAL_MULTI_USER_TARGET, RUNLEVEL_UP },
+ { "rc4.d", SPECIAL_MULTI_USER_TARGET, RUNLEVEL_UP },
+ { "rc5.d", SPECIAL_GRAPHICAL_TARGET, RUNLEVEL_UP },
/* Standard SysV runlevels for shutdown */
{ "rc0.d", SPECIAL_POWEROFF_TARGET, RUNLEVEL_DOWN },
@@ -68,6 +65,8 @@ static const struct {
UP must be read before DOWN */
};
+const char *arg_dest = "/tmp";
+
typedef struct SysvStub {
char *name;
char *path;
@@ -83,7 +82,29 @@ typedef struct SysvStub {
bool reload;
} SysvStub;
-const char *arg_dest = "/tmp";
+static void free_sysvstub(SysvStub *s) {
+ free(s->name);
+ free(s->path);
+ free(s->description);
+ free(s->pid_file);
+ strv_free(s->before);
+ strv_free(s->after);
+ strv_free(s->wants);
+ strv_free(s->wanted_by);
+ strv_free(s->conflicts);
+ free(s);
+}
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(SysvStub*, free_sysvstub);
+
+static void free_sysvstub_hashmapp(Hashmap **h) {
+ SysvStub *stub;
+
+ while ((stub = hashmap_steal_first(*h)))
+ free_sysvstub(stub);
+
+ hashmap_free(*h);
+}
static int add_symlink(const char *service, const char *where) {
_cleanup_free_ char *from = NULL, *to = NULL;
@@ -136,37 +157,23 @@ static int add_alias(const char *service, const char *alias) {
static int generate_unit_file(SysvStub *s) {
char **p;
_cleanup_fclose_ FILE *f = NULL;
- _cleanup_free_ char *unit = NULL;
- _cleanup_free_ char *before = NULL;
- _cleanup_free_ char *after = NULL;
- _cleanup_free_ char *wants = NULL;
- _cleanup_free_ char *conflicts = NULL;
+ _cleanup_free_ char *unit = NULL,
+ *before = NULL, *after = NULL,
+ *wants = NULL, *conflicts = NULL;
int r;
before = strv_join(s->before, " ");
- if (!before)
- return log_oom();
-
after = strv_join(s->after, " ");
- if (!after)
- return log_oom();
-
wants = strv_join(s->wants, " ");
- if (!wants)
- return log_oom();
-
conflicts = strv_join(s->conflicts, " ");
- if (!conflicts)
- return log_oom();
-
unit = strjoin(arg_dest, "/", s->name, NULL);
- if (!unit)
+ if (!before || !after || !wants || !conflicts || !unit)
return log_oom();
/* We might already have a symlink with the same name from a Provides:,
* or from backup files like /etc/init.d/foo.bak. Real scripts always win,
* so remove an existing link */
- if (is_symlink(unit)) {
+ if (is_symlink(unit) > 0) {
log_warning("Overwriting existing symlink %s with real service", unit);
(void) unlink(unit);
}
@@ -217,7 +224,7 @@ static int generate_unit_file(SysvStub *s) {
STRV_FOREACH(p, s->wanted_by) {
r = add_symlink(s->name, *p);
if (r < 0)
- log_unit_error_errno(s->name, r, "Failed to create 'Wants' symlink to %s: %m", *p);
+ log_error_errno(r, "Failed to create 'Wants' symlink to %s: %m", *p);
}
return 0;
@@ -304,11 +311,13 @@ static int sysv_translate_facility(const char *name, const char *filename, char
* out whether something is a target or a service alias. */
if (*name == '$') {
- if (!unit_prefix_is_valid(n))
- return -EINVAL;
+ int k;
/* Facilities starting with $ are most likely targets */
- r = unit_name_build(n, NULL, ".target");
+ k = unit_name_build(n, NULL, ".target", &r);
+ if (k < 0)
+ return k;
+
} else if (streq_ptr(n, filename))
/* Names equaling the file name of the services are redundant */
return 0;
@@ -324,6 +333,93 @@ finish:
return 1;
}
+static int handle_provides(SysvStub *s, unsigned line, const char *full_text, const char *text) {
+ const char *word, *state_;
+ size_t z;
+ int r;
+
+ FOREACH_WORD_QUOTED(word, z, text, state_) {
+ _cleanup_free_ char *n = NULL, *m = NULL;
+
+ n = strndup(word, z);
+ if (!n)
+ return log_oom();
+
+ r = sysv_translate_facility(n, basename(s->path), &m);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ continue;
+
+ if (unit_name_to_type(m) == UNIT_SERVICE) {
+ log_debug("Adding Provides: alias '%s' for '%s'", m, s->name);
+ r = add_alias(s->name, m);
+ if (r < 0)
+ log_warning_errno(r, "[%s:%u] Failed to add LSB Provides name %s, ignoring: %m", s->path, line, m);
+ } else {
+ /* NB: SysV targets which are provided by a
+ * service are pulled in by the services, as
+ * an indication that the generic service is
+ * now available. This is strictly one-way.
+ * The targets do NOT pull in SysV services! */
+ r = strv_extend(&s->before, m);
+ if (r < 0)
+ return log_oom();
+ r = strv_extend(&s->wants, m);
+ if (r < 0)
+ return log_oom();
+ if (streq(m, SPECIAL_NETWORK_ONLINE_TARGET)) {
+ r = strv_extend(&s->before, SPECIAL_NETWORK_TARGET);
+ if (r < 0)
+ return log_oom();
+ }
+ }
+ }
+ if (!isempty(state_))
+ log_error("[%s:%u] Trailing garbage in Provides, ignoring.", s->path, line);
+ return 0;
+}
+
+static int handle_dependencies(SysvStub *s, unsigned line, const char *full_text, const char *text) {
+ const char *word, *state_;
+ size_t z;
+ int r;
+
+ FOREACH_WORD_QUOTED(word, z, text, state_) {
+ _cleanup_free_ char *n = NULL, *m = NULL;
+ bool is_before;
+
+ n = strndup(word, z);
+ if (!n)
+ return log_oom();
+
+ r = sysv_translate_facility(n, basename(s->path), &m);
+ if (r < 0) {
+ log_warning_errno(r, "[%s:%u] Failed to translate LSB dependency %s, ignoring: %m", s->path, line, n);
+ continue;
+ }
+ if (r == 0)
+ continue;
+
+ is_before = startswith_no_case(full_text, "X-Start-Before:");
+
+ if (streq(m, SPECIAL_NETWORK_ONLINE_TARGET) && !is_before) {
+ /* the network-online target is special, as it needs to be actively pulled in */
+ r = strv_extend(&s->after, m);
+ if (r < 0)
+ return log_oom();
+ r = strv_extend(&s->wants, m);
+ } else
+ r = strv_extend(is_before ? &s->before : &s->after, m);
+
+ if (r < 0)
+ return log_oom();
+ }
+ if (!isempty(state_))
+ log_warning("[%s:%u] Trailing garbage in %*s, ignoring.", s->path, line, (int)(strchr(full_text, ':') - full_text), full_text);
+ return 0;
+}
+
static int load_sysv(SysvStub *s) {
_cleanup_fclose_ FILE *f;
unsigned line = 0;
@@ -354,10 +450,7 @@ static int load_sysv(SysvStub *s) {
if (feof(f))
break;
- log_unit_error(s->name,
- "Failed to read configuration file '%s': %m",
- s->path);
- return -errno;
+ return log_error_errno(errno, "Failed to read configuration file '%s': %m", s->path);
}
line++;
@@ -429,9 +522,7 @@ static int load_sysv(SysvStub *s) {
fn = strstrip(t+8);
if (!path_is_absolute(fn)) {
- log_unit_error(s->name,
- "[%s:%u] PID file not absolute. Ignoring.",
- s->path, line);
+ log_error("[%s:%u] PID file not absolute. Ignoring.", s->path, line);
continue;
}
@@ -475,126 +566,22 @@ static int load_sysv(SysvStub *s) {
} else if (state == LSB || state == LSB_DESCRIPTION) {
if (startswith_no_case(t, "Provides:")) {
- const char *word, *state_;
- size_t z;
-
state = LSB;
- FOREACH_WORD_QUOTED(word, z, t+9, state_) {
- _cleanup_free_ char *n = NULL, *m = NULL;
-
- n = strndup(word, z);
- if (!n)
- return -ENOMEM;
-
- r = sysv_translate_facility(n, basename(s->path), &m);
- if (r < 0)
- return r;
- if (r == 0)
- continue;
-
- if (unit_name_to_type(m) == UNIT_SERVICE) {
- log_debug("Adding Provides: alias '%s' for '%s'", m, s->name);
- r = add_alias(s->name, m);
- } else {
- /* NB: SysV targets
- * which are provided
- * by a service are
- * pulled in by the
- * services, as an
- * indication that the
- * generic service is
- * now available. This
- * is strictly
- * one-way. The
- * targets do NOT pull
- * in the SysV
- * services! */
- r = strv_extend(&s->before, m);
- if (r < 0)
- return log_oom();
- r = strv_extend(&s->wants, m);
- if (r < 0)
- return log_oom();
- if (streq(m, SPECIAL_NETWORK_ONLINE_TARGET)) {
- r = strv_extend(&s->before, SPECIAL_NETWORK_TARGET);
- if (r < 0)
- return log_oom();
- }
- }
-
- if (r < 0)
- log_unit_error(s->name,
- "[%s:%u] Failed to add LSB Provides name %s, ignoring: %s",
- s->path, line, m, strerror(-r));
- }
- if (!isempty(state_))
- log_unit_error(s->name,
- "[%s:%u] Trailing garbage in Provides, ignoring.",
- s->path, line);
-
+ r = handle_provides(s, line, t, t + 9);
+ if (r < 0)
+ return r;
} else if (startswith_no_case(t, "Required-Start:") ||
startswith_no_case(t, "Should-Start:") ||
startswith_no_case(t, "X-Start-Before:") ||
startswith_no_case(t, "X-Start-After:")) {
- const char *word, *state_;
- size_t z;
state = LSB;
- FOREACH_WORD_QUOTED(word, z, strchr(t, ':')+1, state_) {
- _cleanup_free_ char *n = NULL, *m = NULL;
- bool is_before;
+ r = handle_dependencies(s, line, t, strchr(t, ':') + 1);
+ if (r < 0)
+ return r;
- n = strndup(word, z);
- if (!n)
- return -ENOMEM;
-
- r = sysv_translate_facility(n, basename(s->path), &m);
- if (r < 0) {
- log_unit_error(s->name,
- "[%s:%u] Failed to translate LSB dependency %s, ignoring: %s",
- s->path, line, n, strerror(-r));
- continue;
- }
-
- if (r == 0)
- continue;
-
- is_before = startswith_no_case(t, "X-Start-Before:");
-
- if (streq(m, SPECIAL_NETWORK_ONLINE_TARGET) && !is_before) {
- /* the network-online target is special, as it needs to be actively pulled in */
- r = strv_extend(&s->after, m);
- if (r < 0)
- return log_oom();
- r = strv_extend(&s->wants, m);
- if (r < 0)
- return log_oom();
- }
- else {
- if (is_before) {
- r = strv_extend(&s->before, m);
- if (r < 0)
- return log_oom();
- }
- else {
- r = strv_extend(&s->after, m);
- if (r < 0)
- return log_oom();
- }
- }
-
- if (r < 0)
- log_unit_error(s->name,
- "[%s:%u] Failed to add dependency on %s, ignoring: %s",
- s->path, line, m, strerror(-r));
- }
- if (!isempty(state_))
- log_unit_error(s->name,
- "[%s:%u] Trailing garbage in %*s, ignoring.",
- s->path, line,
- (int)(strchr(t, ':') - t), t);
} else if (startswith_no_case(t, "Description:")) {
char *d, *j;
@@ -723,10 +710,10 @@ static int fix_order(SysvStub *s, Hashmap *all_services) {
return 0;
}
-static int enumerate_sysv(LookupPaths lp, Hashmap *all_services) {
+static int enumerate_sysv(const LookupPaths *lp, Hashmap *all_services) {
char **path;
- STRV_FOREACH(path, lp.sysvinit_path) {
+ STRV_FOREACH(path, lp->sysvinit_path) {
_cleanup_closedir_ DIR *d = NULL;
struct dirent *de;
@@ -739,7 +726,7 @@ static int enumerate_sysv(LookupPaths lp, Hashmap *all_services) {
while ((de = readdir(d))) {
_cleanup_free_ char *fpath = NULL, *name = NULL;
- _cleanup_free_ SysvStub *service = NULL;
+ _cleanup_(free_sysvstubp) SysvStub *service = NULL;
struct stat st;
int r;
@@ -768,7 +755,7 @@ static int enumerate_sysv(LookupPaths lp, Hashmap *all_services) {
if (!fpath)
return log_oom();
- if (unit_file_get_state(UNIT_FILE_SYSTEM, NULL, name) >= 0) {
+ if (unit_file_lookup_state(UNIT_FILE_SYSTEM, NULL, lp, name) >= 0) {
log_debug("Native unit for %s already exists, skipping", name);
continue;
}
@@ -793,18 +780,18 @@ static int enumerate_sysv(LookupPaths lp, Hashmap *all_services) {
return 0;
}
-static int set_dependencies_from_rcnd(LookupPaths lp, Hashmap *all_services) {
+static int set_dependencies_from_rcnd(const LookupPaths *lp, Hashmap *all_services) {
char **p;
unsigned i;
_cleanup_closedir_ DIR *d = NULL;
- _cleanup_free_ char *path = NULL, *fpath = NULL, *name = NULL;
+ _cleanup_free_ char *path = NULL, *fpath = NULL;
SysvStub *service;
Iterator j;
Set *runlevel_services[ELEMENTSOF(rcnd_table)] = {};
_cleanup_set_free_ Set *shutdown_services = NULL;
int r = 0;
- STRV_FOREACH(p, lp.sysvrcnd_path)
+ STRV_FOREACH(p, lp->sysvrcnd_path)
for (i = 0; i < ELEMENTSOF(rcnd_table); i ++) {
struct dirent *de;
@@ -825,6 +812,8 @@ static int set_dependencies_from_rcnd(LookupPaths lp, Hashmap *all_services) {
}
while ((de = readdir(d))) {
+ _cleanup_free_ char *name = NULL;
+
int a, b;
if (hidden_file(de->d_name))
@@ -923,8 +912,8 @@ finish:
int main(int argc, char *argv[]) {
int r, q;
- LookupPaths lp;
- Hashmap *all_services;
+ _cleanup_lookup_paths_free_ LookupPaths lp = {};
+ _cleanup_(free_sysvstub_hashmapp) Hashmap *all_services = NULL;
SysvStub *service;
Iterator j;
@@ -942,7 +931,7 @@ int main(int argc, char *argv[]) {
umask(0022);
- r = lookup_paths_init(&lp, SYSTEMD_SYSTEM, true, NULL, NULL, NULL, NULL);
+ r = lookup_paths_init(&lp, MANAGER_SYSTEM, true, NULL, NULL, NULL, NULL);
if (r < 0) {
log_error("Failed to find lookup paths.");
return EXIT_FAILURE;
@@ -954,13 +943,13 @@ int main(int argc, char *argv[]) {
return EXIT_FAILURE;
}
- r = enumerate_sysv(lp, all_services);
+ r = enumerate_sysv(&lp, all_services);
if (r < 0) {
log_error("Failed to generate units for all init scripts.");
return EXIT_FAILURE;
}
- r = set_dependencies_from_rcnd(lp, all_services);
+ r = set_dependencies_from_rcnd(&lp, all_services);
if (r < 0) {
log_error("Failed to read runlevels from rcnd links.");
return EXIT_FAILURE;
diff --git a/src/test/test-barrier.c b/src/test/test-barrier.c
index cb75f7314d..2d109a30e7 100644
--- a/src/test/test-barrier.c
+++ b/src/test/test-barrier.c
@@ -28,15 +28,12 @@
* increase it at the slightly cost of lengthen test-duration on other machines.
*/
-#include <errno.h>
#include <stdio.h>
-#include <string.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <unistd.h>
#include "barrier.h"
-#include "def.h"
#include "util.h"
/* 20ms to test deadlocks; All timings use multiples of this constant as
diff --git a/src/test/test-btrfs.c b/src/test/test-btrfs.c
index 150a32ad6f..838ffcba3d 100644
--- a/src/test/test-btrfs.c
+++ b/src/test/test-btrfs.c
@@ -19,7 +19,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdlib.h>
#include <fcntl.h>
#include "log.h"
@@ -28,8 +27,7 @@
#include "btrfs-util.h"
int main(int argc, char *argv[]) {
- int r;
- int fd;
+ int r, fd;
fd = open("/", O_RDONLY|O_CLOEXEC|O_DIRECTORY);
if (fd < 0)
@@ -51,9 +49,9 @@ int main(int argc, char *argv[]) {
if (r < 0)
log_error_errno(r, "Failed to get quota info: %m");
else {
- log_info("referred: %s", strna(format_bytes(bs, sizeof(bs), quota.referred)));
+ log_info("referenced: %s", strna(format_bytes(bs, sizeof(bs), quota.referenced)));
log_info("exclusive: %s", strna(format_bytes(bs, sizeof(bs), quota.exclusive)));
- log_info("referred_max: %s", strna(format_bytes(bs, sizeof(bs), quota.referred_max)));
+ log_info("referenced_max: %s", strna(format_bytes(bs, sizeof(bs), quota.referenced_max)));
log_info("exclusive_max: %s", strna(format_bytes(bs, sizeof(bs), quota.exclusive_max)));
}
@@ -74,33 +72,78 @@ int main(int argc, char *argv[]) {
if (r < 0)
log_error_errno(r, "Failed to write file: %m");
- r = btrfs_subvol_snapshot("/xxxtest", "/xxxtest2", false, false);
+ r = btrfs_subvol_snapshot("/xxxtest", "/xxxtest2", 0);
if (r < 0)
log_error_errno(r, "Failed to make snapshot: %m");
- r = btrfs_subvol_snapshot("/xxxtest", "/xxxtest3", true, false);
+ r = btrfs_subvol_snapshot("/xxxtest", "/xxxtest3", BTRFS_SNAPSHOT_READ_ONLY);
if (r < 0)
log_error_errno(r, "Failed to make snapshot: %m");
- r = btrfs_subvol_remove("/xxxtest");
+ r = btrfs_subvol_remove("/xxxtest", false);
if (r < 0)
log_error_errno(r, "Failed to remove subvolume: %m");
- r = btrfs_subvol_remove("/xxxtest2");
+ r = btrfs_subvol_remove("/xxxtest2", false);
if (r < 0)
log_error_errno(r, "Failed to remove subvolume: %m");
- r = btrfs_subvol_remove("/xxxtest3");
+ r = btrfs_subvol_remove("/xxxtest3", false);
if (r < 0)
log_error_errno(r, "Failed to remove subvolume: %m");
- r = btrfs_subvol_snapshot("/etc", "/etc2", true, true);
+ r = btrfs_subvol_snapshot("/etc", "/etc2", BTRFS_SNAPSHOT_READ_ONLY|BTRFS_SNAPSHOT_FALLBACK_COPY);
if (r < 0)
log_error_errno(r, "Failed to make snapshot: %m");
- r = btrfs_subvol_remove("/etc2");
+ r = btrfs_subvol_remove("/etc2", false);
if (r < 0)
log_error_errno(r, "Failed to remove subvolume: %m");
+ r = btrfs_subvol_make("/xxxrectest");
+ if (r < 0)
+ log_error_errno(r, "Failed to make subvolume: %m");
+
+ r = btrfs_subvol_make("/xxxrectest/xxxrectest2");
+ if (r < 0)
+ log_error_errno(r, "Failed to make subvolume: %m");
+
+ r = btrfs_subvol_make("/xxxrectest/xxxrectest3");
+ if (r < 0)
+ log_error_errno(r, "Failed to make subvolume: %m");
+
+ r = btrfs_subvol_make("/xxxrectest/xxxrectest3/sub");
+ if (r < 0)
+ log_error_errno(r, "Failed to make subvolume: %m");
+
+ if (mkdir("/xxxrectest/dir", 0755) < 0)
+ log_error_errno(errno, "Failed to make directory: %m");
+
+ r = btrfs_subvol_make("/xxxrectest/dir/xxxrectest4");
+ if (r < 0)
+ log_error_errno(r, "Failed to make subvolume: %m");
+
+ if (mkdir("/xxxrectest/dir/xxxrectest4/dir", 0755) < 0)
+ log_error_errno(errno, "Failed to make directory: %m");
+
+ r = btrfs_subvol_make("/xxxrectest/dir/xxxrectest4/dir/xxxrectest5");
+ if (r < 0)
+ log_error_errno(r, "Failed to make subvolume: %m");
+
+ if (mkdir("/xxxrectest/mnt", 0755) < 0)
+ log_error_errno(errno, "Failed to make directory: %m");
+
+ r = btrfs_subvol_snapshot("/xxxrectest", "/xxxrectest2", BTRFS_SNAPSHOT_RECURSIVE);
+ if (r < 0)
+ log_error_errno(r, "Failed to snapshot subvolume: %m");
+
+ r = btrfs_subvol_remove("/xxxrectest", true);
+ if (r < 0)
+ log_error_errno(r, "Failed to recursively remove subvolume: %m");
+
+ r = btrfs_subvol_remove("/xxxrectest2", true);
+ if (r < 0)
+ log_error_errno(r, "Failed to recursively remove subvolume: %m");
+
return 0;
}
diff --git a/src/test/test-cap-list.c b/src/test/test-cap-list.c
index 632d62ff8f..43a2d35b80 100644
--- a/src/test/test-cap-list.c
+++ b/src/test/test-cap-list.c
@@ -20,7 +20,6 @@
***/
#include "util.h"
-#include "log.h"
#include "fileio.h"
#include "cap-list.h"
#include "capability.h"
diff --git a/src/test/test-capability.c b/src/test/test-capability.c
index 43769923b0..f47452ce72 100644
--- a/src/test/test-capability.c
+++ b/src/test/test-capability.c
@@ -17,7 +17,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <sys/types.h>
#include <sys/wait.h>
#include <sys/capability.h>
#include <sys/socket.h>
diff --git a/src/test/test-cgroup-mask.c b/src/test/test-cgroup-mask.c
index 9e9de23e0e..289dddbaac 100644
--- a/src/test/test-cgroup-mask.c
+++ b/src/test/test-cgroup-mask.c
@@ -20,14 +20,9 @@
***/
#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
-#include <pwd.h>
#include "manager.h"
#include "unit.h"
-#include "util.h"
#include "macro.h"
#include "test-helper.h"
@@ -40,7 +35,7 @@ static int test_cgroup_mask(void) {
/* Prepare the manager. */
assert_se(set_unit_path(TEST_DIR) >= 0);
- r = manager_new(SYSTEMD_USER, true, &m);
+ r = manager_new(MANAGER_USER, true, &m);
if (r == -EPERM || r == -EACCES) {
puts("manager_new: Permission denied. Skipping test.");
return EXIT_TEST_SKIP;
diff --git a/src/test/test-cgroup-util.c b/src/test/test-cgroup-util.c
index 67eeeb56b7..4a89f64518 100644
--- a/src/test/test-cgroup-util.c
+++ b/src/test/test-cgroup-util.c
@@ -19,11 +19,12 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <assert.h>
#include "util.h"
#include "cgroup-util.h"
#include "test-helper.h"
+#include "formats-util.h"
+#include "process-util.h"
static void check_p_d_u(const char *path, int code, const char *result) {
_cleanup_free_ char *unit = NULL;
@@ -39,11 +40,11 @@ static void test_path_decode_unit(void) {
check_p_d_u("getty@tty2.service", 0, "getty@tty2.service");
check_p_d_u("getty@tty2.service/", 0, "getty@tty2.service");
check_p_d_u("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/", -ENXIO, NULL);
+ check_p_d_u("getty@.service", -ENXIO, NULL);
check_p_d_u("getty.service", 0, "getty.service");
- check_p_d_u("getty", -EINVAL, NULL);
- check_p_d_u("getty/waldo", -EINVAL, NULL);
+ check_p_d_u("getty", -ENXIO, NULL);
+ check_p_d_u("getty/waldo", -ENXIO, NULL);
check_p_d_u("_cpu.service", 0, "cpu.service");
}
@@ -63,12 +64,12 @@ static void test_path_get_unit(void) {
check_p_g_u("/system.slice/getty@tty5.service/aaa/bbb", 0, "getty@tty5.service");
check_p_g_u("/system.slice/getty@tty5.service/", 0, "getty@tty5.service");
check_p_g_u("/system.slice/getty@tty6.service/tty5", 0, "getty@tty6.service");
- check_p_g_u("sadfdsafsda", -EINVAL, NULL);
- check_p_g_u("/system.slice/getty####@tty6.service/xxx", -EINVAL, NULL);
+ check_p_g_u("sadfdsafsda", -ENXIO, NULL);
+ check_p_g_u("/system.slice/getty####@tty6.service/xxx", -ENXIO, NULL);
check_p_g_u("/system.slice/system-waldo.slice/foobar.service/sdfdsaf", 0, "foobar.service");
check_p_g_u("/system.slice/system-waldo.slice/_cpu.service/sdfdsaf", 0, "cpu.service");
check_p_g_u("/user.slice/user-1000.slice/user@1000.service/server.service", 0, "user@1000.service");
- check_p_g_u("/user.slice/user-1000.slice/user@.service/server.service", -EINVAL, NULL);
+ check_p_g_u("/user.slice/user-1000.slice/user@.service/server.service", -ENXIO, NULL);
}
static void check_p_g_u_u(const char *path, int code, const char *result) {
@@ -86,15 +87,15 @@ static void test_path_get_user_unit(void) {
check_p_g_u_u("/user.slice/user-1000.slice/session-2.scope/waldo.slice/foobar.service", 0, "foobar.service");
check_p_g_u_u("/user.slice/user-1002.slice/session-2.scope/foobar.service/waldo", 0, "foobar.service");
check_p_g_u_u("/user.slice/user-1000.slice/session-2.scope/foobar.service/waldo/uuuux", 0, "foobar.service");
- check_p_g_u_u("/user.slice/user-1000.slice/session-2.scope/waldo/waldo/uuuux", -EINVAL, NULL);
+ check_p_g_u_u("/user.slice/user-1000.slice/session-2.scope/waldo/waldo/uuuux", -ENXIO, NULL);
check_p_g_u_u("/user.slice/user-1000.slice/session-2.scope/foobar@pie.service/pa/po", 0, "foobar@pie.service");
check_p_g_u_u("/session-2.scope/foobar@pie.service/pa/po", 0, "foobar@pie.service");
check_p_g_u_u("/xyz.slice/xyz-waldo.slice/session-77.scope/foobar@pie.service/pa/po", 0, "foobar@pie.service");
- check_p_g_u_u("/meh.service", -ENOENT, NULL);
+ check_p_g_u_u("/meh.service", -ENXIO, NULL);
check_p_g_u_u("/session-3.scope/_cpu.service", 0, "cpu.service");
check_p_g_u_u("/user.slice/user-1000.slice/user@1000.service/server.service", 0, "server.service");
check_p_g_u_u("/user.slice/user-1000.slice/user@1000.service/foobar.slice/foobar@pie.service", 0, "foobar@pie.service");
- check_p_g_u_u("/user.slice/user-1000.slice/user@.service/server.service", -ENOENT, NULL);
+ check_p_g_u_u("/user.slice/user-1000.slice/user@.service/server.service", -ENXIO, NULL);
}
static void check_p_g_s(const char *path, int code, const char *result) {
@@ -107,8 +108,8 @@ static void check_p_g_s(const char *path, int code, const char *result) {
static void test_path_get_session(void) {
check_p_g_s("/user.slice/user-1000.slice/session-2.scope/foobar.service", 0, "2");
check_p_g_s("/session-3.scope", 0, "3");
- check_p_g_s("/session-.scope", -ENOENT, NULL);
- check_p_g_s("", -ENOENT, NULL);
+ check_p_g_s("/session-.scope", -ENXIO, NULL);
+ check_p_g_s("", -ENXIO, NULL);
}
static void check_p_g_o_u(const char *path, int code, uid_t result) {
@@ -121,7 +122,48 @@ static void check_p_g_o_u(const char *path, int code, uid_t result) {
static void test_path_get_owner_uid(void) {
check_p_g_o_u("/user.slice/user-1000.slice/session-2.scope/foobar.service", 0, 1000);
check_p_g_o_u("/user.slice/user-1006.slice", 0, 1006);
- check_p_g_o_u("", -ENOENT, 0);
+ check_p_g_o_u("", -ENXIO, 0);
+}
+
+static void check_p_g_slice(const char *path, int code, const char *result) {
+ _cleanup_free_ char *s = NULL;
+
+ assert_se(cg_path_get_slice(path, &s) == code);
+ assert_se(streq_ptr(s, result));
+}
+
+static void test_path_get_slice(void) {
+ check_p_g_slice("/user.slice", 0, "user.slice");
+ check_p_g_slice("/foobar", 0, "-.slice");
+ check_p_g_slice("/user.slice/user-waldo.slice", 0, "user-waldo.slice");
+ check_p_g_slice("", 0, "-.slice");
+ check_p_g_slice("foobar", 0, "-.slice");
+ check_p_g_slice("foobar.slice", 0, "foobar.slice");
+ check_p_g_slice("foo.slice/foo-bar.slice/waldo.service", 0, "foo-bar.slice");
+}
+
+static void check_p_g_u_slice(const char *path, int code, const char *result) {
+ _cleanup_free_ char *s = NULL;
+
+ assert_se(cg_path_get_user_slice(path, &s) == code);
+ assert_se(streq_ptr(s, result));
+}
+
+static void test_path_get_user_slice(void) {
+ check_p_g_u_slice("/user.slice", -ENXIO, NULL);
+ check_p_g_u_slice("/foobar", -ENXIO, NULL);
+ check_p_g_u_slice("/user.slice/user-waldo.slice", -ENXIO, NULL);
+ check_p_g_u_slice("", -ENXIO, NULL);
+ check_p_g_u_slice("foobar", -ENXIO, NULL);
+ check_p_g_u_slice("foobar.slice", -ENXIO, NULL);
+ check_p_g_u_slice("foo.slice/foo-bar.slice/waldo.service", -ENXIO, NULL);
+
+ check_p_g_u_slice("foo.slice/foo-bar.slice/user@1000.service", 0, "-.slice");
+ check_p_g_u_slice("foo.slice/foo-bar.slice/user@1000.service/", 0, "-.slice");
+ check_p_g_u_slice("foo.slice/foo-bar.slice/user@1000.service///", 0, "-.slice");
+ check_p_g_u_slice("foo.slice/foo-bar.slice/user@1000.service/waldo.service", 0, "-.slice");
+ check_p_g_u_slice("foo.slice/foo-bar.slice/user@1000.service/piep.slice/foo.service", 0, "piep.slice");
+ check_p_g_u_slice("/foo.slice//foo-bar.slice/user@1000.service/piep.slice//piep-pap.slice//foo.service", 0, "piep-pap.slice");
}
static void test_get_paths(void) {
@@ -226,9 +268,14 @@ static void test_slice_to_path(void) {
test_slice_to_path_one("foobar.slice", "foobar.slice", 0);
test_slice_to_path_one("foobar-waldo.slice", "foobar.slice/foobar-waldo.slice", 0);
test_slice_to_path_one("foobar-waldo.service", NULL, -EINVAL);
- test_slice_to_path_one("-.slice", NULL, -EINVAL);
+ test_slice_to_path_one("-.slice", "", 0);
+ test_slice_to_path_one("--.slice", NULL, -EINVAL);
+ test_slice_to_path_one("-", NULL, -EINVAL);
test_slice_to_path_one("-foo-.slice", NULL, -EINVAL);
test_slice_to_path_one("-foo.slice", NULL, -EINVAL);
+ test_slice_to_path_one("foo-.slice", NULL, -EINVAL);
+ test_slice_to_path_one("foo--bar.slice", NULL, -EINVAL);
+ test_slice_to_path_one("foo.slice/foo--bar.slice", NULL, -EINVAL);
test_slice_to_path_one("a-b.slice", "a.slice/a-b.slice", 0);
test_slice_to_path_one("a-b-c-d-e.slice", "a.slice/a-b.slice/a-b-c.slice/a-b-c-d.slice/a-b-c-d-e.slice", 0);
}
@@ -254,6 +301,8 @@ int main(void) {
test_path_get_user_unit();
test_path_get_session();
test_path_get_owner_uid();
+ test_path_get_slice();
+ test_path_get_user_slice();
TEST_REQ_RUNNING_SYSTEMD(test_get_paths());
test_proc();
TEST_REQ_RUNNING_SYSTEMD(test_escape());
diff --git a/src/test/test-cgroup.c b/src/test/test-cgroup.c
index 46642f92fe..4be69a408d 100644
--- a/src/test/test-cgroup.c
+++ b/src/test/test-cgroup.c
@@ -25,7 +25,6 @@
#include "cgroup-util.h"
#include "path-util.h"
#include "util.h"
-#include "log.h"
int main(int argc, char*argv[]) {
char *path;
diff --git a/src/test/test-condition.c b/src/test/test-condition.c
index 88147c8e0a..b788c9532d 100644
--- a/src/test/test-condition.c
+++ b/src/test/test-condition.c
@@ -28,6 +28,7 @@
#include "ima-util.h"
#include "apparmor-util.h"
#include "smack-util.h"
+#include "hostname-util.h"
static void test_condition_test_path(void) {
Condition *condition;
diff --git a/src/test/test-conf-files.c b/src/test/test-conf-files.c
index 894c7f742f..01ece022c1 100644
--- a/src/test/test-conf-files.c
+++ b/src/test/test-conf-files.c
@@ -26,7 +26,7 @@
#include "macro.h"
#include "strv.h"
#include "util.h"
-
+#include "rm-rf.h"
static void setup_test_dir(char *tmp_dir, const char *files, ...) {
va_list ap;
@@ -74,7 +74,7 @@ static void test_conf_files_list(bool use_root) {
assert_se(streq_ptr(found_files[1], expect_b));
assert_se(found_files[2] == NULL);
- assert_se(rm_rf_dangerous(tmp_dir, false, true, false) == 0);
+ assert_se(rm_rf(tmp_dir, REMOVE_ROOT|REMOVE_PHYSICAL) == 0);
}
int main(int argc, char **argv) {
diff --git a/src/test/test-conf-parser.c b/src/test/test-conf-parser.c
new file mode 100644
index 0000000000..463906d304
--- /dev/null
+++ b/src/test/test-conf-parser.c
@@ -0,0 +1,234 @@
+/***
+ This file is part of systemd.
+
+ Copyright 2015 Ronny Chevalier
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser 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 "conf-parser.h"
+#include "macro.h"
+#include "util.h"
+#include "strv.h"
+#include "log.h"
+
+static void test_config_parse_path_one(const char *rvalue, const char *expected) {
+ char *path = NULL;
+
+ assert_se(config_parse_path("unit", "filename", 1, "section", 1, "lvalue", 0, rvalue, &path, NULL) >= 0);
+ assert_se(streq_ptr(expected, path));
+
+ free(path);
+}
+
+static void test_config_parse_log_level_one(const char *rvalue, int expected) {
+ int log_level = 0;
+
+ assert_se(config_parse_log_level("unit", "filename", 1, "section", 1, "lvalue", 0, rvalue, &log_level, NULL) >= 0);
+ assert_se(expected == log_level);
+}
+
+static void test_config_parse_log_facility_one(const char *rvalue, int expected) {
+ int log_facility = 0;
+
+ assert_se(config_parse_log_facility("unit", "filename", 1, "section", 1, "lvalue", 0, rvalue, &log_facility, NULL) >= 0);
+ assert_se(expected == log_facility);
+}
+
+static void test_config_parse_iec_size_one(const char *rvalue, size_t expected) {
+ size_t iec_size = 0;
+
+ assert_se(config_parse_iec_size("unit", "filename", 1, "section", 1, "lvalue", 0, rvalue, &iec_size, NULL) >= 0);
+ assert_se(expected == iec_size);
+}
+
+static void test_config_parse_si_size_one(const char *rvalue, size_t expected) {
+ size_t si_size = 0;
+
+ assert_se(config_parse_si_size("unit", "filename", 1, "section", 1, "lvalue", 0, rvalue, &si_size, NULL) >= 0);
+ assert_se(expected == si_size);
+}
+
+static void test_config_parse_int_one(const char *rvalue, int expected) {
+ int v = -1;
+
+ assert_se(config_parse_int("unit", "filename", 1, "section", 1, "lvalue", 0, rvalue, &v, NULL) >= 0);
+ assert_se(expected == v);
+}
+
+static void test_config_parse_unsigned_one(const char *rvalue, unsigned expected) {
+ unsigned v = 0;
+
+ assert_se(config_parse_unsigned("unit", "filename", 1, "section", 1, "lvalue", 0, rvalue, &v, NULL) >= 0);
+ assert_se(expected == v);
+}
+
+static void test_config_parse_strv_one(const char *rvalue, char **expected) {
+ char **strv = 0;
+
+ assert_se(config_parse_strv("unit", "filename", 1, "section", 1, "lvalue", 0, rvalue, &strv, NULL) >= 0);
+ assert_se(strv_equal(expected, strv));
+
+ strv_free(strv);
+}
+
+static void test_config_parse_mode_one(const char *rvalue, mode_t expected) {
+ mode_t v = 0;
+
+ assert_se(config_parse_mode("unit", "filename", 1, "section", 1, "lvalue", 0, rvalue, &v, NULL) >= 0);
+ assert_se(expected == v);
+}
+
+static void test_config_parse_sec_one(const char *rvalue, usec_t expected) {
+ usec_t v = 0;
+
+ assert_se(config_parse_sec("unit", "filename", 1, "section", 1, "lvalue", 0, rvalue, &v, NULL) >= 0);
+ assert_se(expected == v);
+}
+
+static void test_config_parse_nsec_one(const char *rvalue, nsec_t expected) {
+ nsec_t v = 0;
+
+ assert_se(config_parse_nsec("unit", "filename", 1, "nsection", 1, "lvalue", 0, rvalue, &v, NULL) >= 0);
+ assert_se(expected == v);
+}
+
+static void test_config_parse_path(void) {
+ test_config_parse_path_one("/path", "/path");
+ test_config_parse_path_one("/path//////////", "/path");
+ test_config_parse_path_one("///path/foo///bar////bar//", "/path/foo/bar/bar");
+
+ test_config_parse_path_one("not_absolute/path", NULL);
+}
+
+static void test_config_parse_log_level(void) {
+ test_config_parse_log_level_one("debug", LOG_DEBUG);
+ test_config_parse_log_level_one("info", LOG_INFO);
+
+ test_config_parse_log_level_one("garbage", 0);
+}
+
+static void test_config_parse_log_facility(void) {
+ test_config_parse_log_facility_one("mail", LOG_MAIL);
+ test_config_parse_log_facility_one("user", LOG_USER);
+
+ test_config_parse_log_facility_one("garbage", 0);
+}
+
+static void test_config_parse_iec_size(void) {
+ test_config_parse_iec_size_one("1024", 1024);
+ test_config_parse_iec_size_one("2K", 2048);
+ test_config_parse_iec_size_one("10M", 10 * 1024 * 1024);
+ test_config_parse_iec_size_one("1G", 1 * 1024 * 1024 * 1024);
+ test_config_parse_iec_size_one("0G", 0);
+ test_config_parse_iec_size_one("0", 0);
+
+ test_config_parse_iec_size_one("-982", 0);
+ test_config_parse_iec_size_one("49874444198739873000000G", 0);
+ test_config_parse_iec_size_one("garbage", 0);
+}
+
+static void test_config_parse_si_size(void) {
+ test_config_parse_si_size_one("1024", 1024);
+ test_config_parse_si_size_one("2K", 2000);
+ test_config_parse_si_size_one("10M", 10 * 1000 * 1000);
+ test_config_parse_si_size_one("1G", 1 * 1000 * 1000 * 1000);
+ test_config_parse_si_size_one("0G", 0);
+ test_config_parse_si_size_one("0", 0);
+
+ test_config_parse_si_size_one("-982", 0);
+ test_config_parse_si_size_one("49874444198739873000000G", 0);
+ test_config_parse_si_size_one("garbage", 0);
+}
+
+static void test_config_parse_int(void) {
+ test_config_parse_int_one("1024", 1024);
+ test_config_parse_int_one("-1024", -1024);
+ test_config_parse_int_one("0", 0);
+
+ test_config_parse_int_one("99999999999999999999999999999999999999999999999999999999", -1);
+ test_config_parse_int_one("-99999999999999999999999999999999999999999999999999999999", -1);
+ test_config_parse_int_one("1G", -1);
+ test_config_parse_int_one("garbage", -1);
+}
+
+static void test_config_parse_unsigned(void) {
+ test_config_parse_unsigned_one("10241024", 10241024);
+ test_config_parse_unsigned_one("1024", 1024);
+ test_config_parse_unsigned_one("0", 0);
+
+ test_config_parse_unsigned_one("99999999999999999999999999999999999999999999999999999999", 0);
+ test_config_parse_unsigned_one("1G", 0);
+ test_config_parse_unsigned_one("garbage", 0);
+ test_config_parse_unsigned_one("1000garbage", 0);
+}
+
+static void test_config_parse_strv(void) {
+ test_config_parse_strv_one("", STRV_MAKE_EMPTY);
+ test_config_parse_strv_one("foo", STRV_MAKE("foo"));
+ test_config_parse_strv_one("foo bar foo", STRV_MAKE("foo", "bar", "foo"));
+ test_config_parse_strv_one("\"foo bar\" foo", STRV_MAKE("foo bar", "foo"));
+}
+
+static void test_config_parse_mode(void) {
+ test_config_parse_mode_one("777", 0777);
+ test_config_parse_mode_one("644", 0644);
+
+ test_config_parse_mode_one("-777", 0);
+ test_config_parse_mode_one("999", 0);
+ test_config_parse_mode_one("garbage", 0);
+ test_config_parse_mode_one("777garbage", 0);
+ test_config_parse_mode_one("777 garbage", 0);
+}
+
+static void test_config_parse_sec(void) {
+ test_config_parse_sec_one("1", 1 * USEC_PER_SEC);
+ test_config_parse_sec_one("1s", 1 * USEC_PER_SEC);
+ test_config_parse_sec_one("100ms", 100 * USEC_PER_MSEC);
+ test_config_parse_sec_one("5min 20s", 5 * 60 * USEC_PER_SEC + 20 * USEC_PER_SEC);
+
+ test_config_parse_sec_one("-1", 0);
+ test_config_parse_sec_one("10foo", 0);
+ test_config_parse_sec_one("garbage", 0);
+}
+
+static void test_config_parse_nsec(void) {
+ test_config_parse_nsec_one("1", 1);
+ test_config_parse_nsec_one("1s", 1 * NSEC_PER_SEC);
+ test_config_parse_nsec_one("100ms", 100 * NSEC_PER_MSEC);
+ test_config_parse_nsec_one("5min 20s", 5 * 60 * NSEC_PER_SEC + 20 * NSEC_PER_SEC);
+
+ test_config_parse_nsec_one("-1", 0);
+ test_config_parse_nsec_one("10foo", 0);
+ test_config_parse_nsec_one("garbage", 0);
+}
+
+int main(int argc, char **argv) {
+ log_parse_environment();
+ log_open();
+
+ test_config_parse_path();
+ test_config_parse_log_level();
+ test_config_parse_log_facility();
+ test_config_parse_iec_size();
+ test_config_parse_si_size();
+ test_config_parse_int();
+ test_config_parse_unsigned();
+ test_config_parse_strv();
+ test_config_parse_mode();
+ test_config_parse_sec();
+ test_config_parse_nsec();
+
+ return 0;
+}
diff --git a/src/test/test-copy.c b/src/test/test-copy.c
index 5c96f61005..403d85bff0 100644
--- a/src/test/test-copy.c
+++ b/src/test/test-copy.c
@@ -26,6 +26,7 @@
#include "strv.h"
#include "macro.h"
#include "util.h"
+#include "rm-rf.h"
static void test_copy_file(void) {
_cleanup_free_ char *buf = NULL;
@@ -86,8 +87,8 @@ static void test_copy_tree(void) {
"link2", "dir1/file");
char **p, **link;
- rm_rf_dangerous(copy_dir, false, true, false);
- rm_rf_dangerous(original_dir, false, true, false);
+ (void) rm_rf(copy_dir, REMOVE_ROOT|REMOVE_PHYSICAL);
+ (void) rm_rf(original_dir, REMOVE_ROOT|REMOVE_PHYSICAL);
STRV_FOREACH(p, files) {
char *f = strjoina(original_dir, *p);
@@ -128,8 +129,8 @@ static void test_copy_tree(void) {
assert_se(copy_tree(original_dir, copy_dir, false) < 0);
assert_se(copy_tree("/tmp/inexistent/foo/bar/fsdoi", copy_dir, false) < 0);
- rm_rf_dangerous(copy_dir, false, true, false);
- rm_rf_dangerous(original_dir, false, true, false);
+ (void) rm_rf(copy_dir, REMOVE_ROOT|REMOVE_PHYSICAL);
+ (void) rm_rf(original_dir, REMOVE_ROOT|REMOVE_PHYSICAL);
}
int main(int argc, char *argv[]) {
diff --git a/src/test/test-ellipsize.c b/src/test/test-ellipsize.c
index f97c78ebd6..27df9089c3 100644
--- a/src/test/test-ellipsize.c
+++ b/src/test/test-ellipsize.c
@@ -22,7 +22,7 @@
#include <stdio.h>
#include "util.h"
-#include "utf8.h"
+#include "terminal-util.h"
#include "def.h"
static void test_one(const char *p) {
diff --git a/src/test/test-engine.c b/src/test/test-engine.c
index 456999ca40..a7ab21a415 100644
--- a/src/test/test-engine.c
+++ b/src/test/test-engine.c
@@ -22,7 +22,6 @@
#include <stdio.h>
#include <errno.h>
#include <string.h>
-#include <unistd.h>
#include "manager.h"
#include "bus-util.h"
@@ -38,7 +37,7 @@ int main(int argc, char *argv[]) {
/* prepare the test */
assert_se(set_unit_path(TEST_DIR) >= 0);
- r = manager_new(SYSTEMD_USER, true, &m);
+ r = manager_new(MANAGER_USER, true, &m);
if (IN_SET(r, -EPERM, -EACCES, -EADDRINUSE, -EHOSTDOWN, -ENOENT)) {
printf("Skipping test: manager_new: %s", strerror(-r));
return EXIT_TEST_SKIP;
@@ -66,7 +65,7 @@ int main(int argc, char *argv[]) {
manager_dump_units(m, stdout, "\t");
printf("Test2: (Cyclic Order, Unfixable)\n");
- assert_se(manager_add_job(m, JOB_START, d, JOB_REPLACE, false, NULL, &j) == -EDEADLOCK);
+ assert_se(manager_add_job(m, JOB_START, d, JOB_REPLACE, false, NULL, &j) == -EDEADLK);
manager_dump_jobs(m, stdout, "\t");
printf("Test3: (Cyclic Order, Fixable, Garbage Collector)\n");
@@ -82,14 +81,14 @@ int main(int argc, char *argv[]) {
manager_dump_units(m, stdout, "\t");
printf("Test5: (Colliding transaction, fail)\n");
- assert_se(manager_add_job(m, JOB_START, g, JOB_FAIL, false, NULL, &j) == -EDEADLOCK);
+ assert_se(manager_add_job(m, JOB_START, g, JOB_FAIL, false, NULL, &j) == -EDEADLK);
printf("Test6: (Colliding transaction, replace)\n");
assert_se(manager_add_job(m, JOB_START, g, JOB_REPLACE, false, NULL, &j) == 0);
manager_dump_jobs(m, stdout, "\t");
printf("Test7: (Unmergeable job type, fail)\n");
- assert_se(manager_add_job(m, JOB_STOP, g, JOB_FAIL, false, NULL, &j) == -EDEADLOCK);
+ assert_se(manager_add_job(m, JOB_STOP, g, JOB_FAIL, false, NULL, &j) == -EDEADLK);
printf("Test8: (Mergeable job type, fail)\n");
assert_se(manager_add_job(m, JOB_RESTART, g, JOB_FAIL, false, NULL, &j) == 0);
diff --git a/src/test/test-env-replace.c b/src/test/test-env-replace.c
index 0274e97618..2e28c0c49b 100644
--- a/src/test/test-env-replace.c
+++ b/src/test/test-env-replace.c
@@ -19,7 +19,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <unistd.h>
#include <string.h>
#include "util.h"
@@ -137,31 +136,6 @@ static void test_replace_env_arg(void) {
assert_se(strv_length(r) == 9);
}
-static void test_one_normalize(const char *input, const char *output) {
- _cleanup_free_ char *t;
-
- t = normalize_env_assignment(input);
- assert_se(t);
- assert_se(streq(t, output));
-}
-
-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 ");
-}
-
static void test_env_clean(void) {
_cleanup_strv_free_ char **e;
@@ -210,7 +184,6 @@ int main(int argc, char *argv[]) {
test_strv_env_set();
test_strv_env_merge();
test_replace_env_arg();
- test_normalize_env_assignment();
test_env_clean();
test_env_name_is_valid();
diff --git a/src/test/test-execute.c b/src/test/test-execute.c
index 91ccaf72b8..0f4172e722 100644
--- a/src/test/test-execute.c
+++ b/src/test/test-execute.c
@@ -23,8 +23,8 @@
#include "manager.h"
#include "util.h"
#include "macro.h"
-#include "strv.h"
#include "mkdir.h"
+#include "rm-rf.h"
typedef void (*test_function_t)(Manager *m);
@@ -73,7 +73,7 @@ static void test_exec_workingdirectory(Manager *m) {
test(m, "exec-workingdirectory.service", 0, CLD_EXITED);
- rm_rf_dangerous("/tmp/test-exec_workingdirectory", false, true, false);
+ (void) rm_rf("/tmp/test-exec_workingdirectory", REMOVE_ROOT|REMOVE_PHYSICAL);
}
static void test_exec_personality(Manager *m) {
@@ -165,9 +165,9 @@ int main(int argc, char *argv[]) {
return EXIT_TEST_SKIP;
}
- assert_se(set_unit_path(TEST_DIR ":") >= 0);
+ assert_se(set_unit_path(TEST_DIR) >= 0);
- r = manager_new(SYSTEMD_USER, true, &m);
+ r = manager_new(MANAGER_USER, true, &m);
if (IN_SET(r, -EPERM, -EACCES, -EADDRINUSE, -EHOSTDOWN, -ENOENT)) {
printf("Skipping test: manager_new: %s", strerror(-r));
return EXIT_TEST_SKIP;
diff --git a/src/test/test-fdset.c b/src/test/test-fdset.c
index 91df7eb663..242c5d9dc2 100644
--- a/src/test/test-fdset.c
+++ b/src/test/test-fdset.c
@@ -154,6 +154,56 @@ static void test_fdset_iterate(void) {
unlink(name);
}
+static void test_fdset_isempty(void) {
+ int fd;
+ _cleanup_fdset_free_ FDSet *fdset = NULL;
+ char name[] = "/tmp/test-fdset_isempty.XXXXXX";
+
+ fd = mkostemp_safe(name, O_RDWR|O_CLOEXEC);
+ assert_se(fd >= 0);
+
+ fdset = fdset_new();
+ assert_se(fdset);
+
+ assert_se(fdset_isempty(fdset));
+ assert_se(fdset_put(fdset, fd) >= 0);
+ assert_se(!fdset_isempty(fdset));
+
+ unlink(name);
+}
+
+static void test_fdset_steal_first(void) {
+ int fd;
+ _cleanup_fdset_free_ FDSet *fdset = NULL;
+ char name[] = "/tmp/test-fdset_steal_first.XXXXXX";
+
+ fd = mkostemp_safe(name, O_RDWR|O_CLOEXEC);
+ assert_se(fd >= 0);
+
+ fdset = fdset_new();
+ assert_se(fdset);
+
+ assert_se(fdset_steal_first(fdset) < 0);
+ assert_se(fdset_put(fdset, fd) >= 0);
+ assert_se(fdset_steal_first(fdset) == fd);
+ assert_se(fdset_steal_first(fdset) < 0);
+ assert_se(fdset_put(fdset, fd) >= 0);
+
+ unlink(name);
+}
+
+static void test_fdset_new_array(void) {
+ int fds[] = {10, 11, 12, 13};
+ _cleanup_fdset_free_ FDSet *fdset = NULL;
+
+ assert_se(fdset_new_array(&fdset, fds, 4) >= 0);
+ assert_se(fdset_size(fdset) == 4);
+ assert_se(fdset_contains(fdset, 10));
+ assert_se(fdset_contains(fdset, 11));
+ assert_se(fdset_contains(fdset, 12));
+ assert_se(fdset_contains(fdset, 13));
+}
+
int main(int argc, char *argv[]) {
test_fdset_new_fill();
test_fdset_put_dup();
@@ -161,6 +211,9 @@ int main(int argc, char *argv[]) {
test_fdset_close_others();
test_fdset_remove();
test_fdset_iterate();
+ test_fdset_isempty();
+ test_fdset_steal_first();
+ test_fdset_new_array();
return 0;
}
diff --git a/src/test/test-fileio.c b/src/test/test-fileio.c
index 63e4a19b76..4c31b776bd 100644
--- a/src/test/test-fileio.c
+++ b/src/test/test-fileio.c
@@ -24,6 +24,7 @@
#include <unistd.h>
#include "util.h"
+#include "process-util.h"
#include "fileio.h"
#include "strv.h"
#include "env-util.h"
diff --git a/src/test/test-hashmap-plain.c b/src/test/test-hashmap-plain.c
index 84b508f874..c1a5ccf1f5 100644
--- a/src/test/test-hashmap-plain.c
+++ b/src/test/test-hashmap-plain.c
@@ -682,7 +682,7 @@ static void test_hashmap_get2(void) {
r = hashmap_get2(m, key_orig, &key_copy);
assert_se(streq(r, val));
assert_se(key_orig != key_copy);
- assert_se(streq(key_orig, key_orig));
+ assert_se(streq(key_orig, key_copy));
r = hashmap_get2(m, "no such key", NULL);
assert_se(r == NULL);
diff --git a/src/test/test-hashmap.c b/src/test/test-hashmap.c
index 6900da9e89..767cbd90e9 100644
--- a/src/test/test-hashmap.c
+++ b/src/test/test-hashmap.c
@@ -17,8 +17,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <inttypes.h>
-#include "strv.h"
#include "util.h"
#include "hashmap.h"
@@ -75,7 +73,7 @@ static void test_trivial_compare_func(void) {
}
static void test_string_compare_func(void) {
- assert_se(!string_compare_func("fred", "wilma") == 0);
+ assert_se(string_compare_func("fred", "wilma") != 0);
assert_se(string_compare_func("fred", "fred") == 0);
}
diff --git a/src/test/test-hostname.c b/src/test/test-hostname.c
index 86efa1a3f7..dd50c5148c 100644
--- a/src/test/test-hostname.c
+++ b/src/test/test-hostname.c
@@ -19,11 +19,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <errno.h>
-#include <string.h>
-#include <stdio.h>
-#include <fcntl.h>
-
#include "hostname-setup.h"
#include "util.h"
diff --git a/src/test/test-install.c b/src/test/test-install.c
index 467970b007..5ee52e64cb 100644
--- a/src/test/test-install.c
+++ b/src/test/test-install.c
@@ -19,13 +19,9 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <errno.h>
#include <string.h>
#include <stdio.h>
-#include <fcntl.h>
-#include "util.h"
-#include "path-util.h"
#include "install.h"
static void dump_changes(UnitFileChange *c, unsigned n) {
diff --git a/src/test/test-job-type.c b/src/test/test-job-type.c
index 1d9d49b228..af0d76e894 100644
--- a/src/test/test-job-type.c
+++ b/src/test/test-job-type.c
@@ -20,9 +20,6 @@
***/
#include <stdio.h>
-#include <errno.h>
-#include <string.h>
-#include <unistd.h>
#include "job.h"
#include "unit.h"
diff --git a/src/test/test-json.c b/src/test/test-json.c
index b09131891c..1058c583c3 100644
--- a/src/test/test-json.c
+++ b/src/test/test-json.c
@@ -21,7 +21,6 @@
#include <math.h>
-#include "log.h"
#include "util.h"
#include "json.h"
@@ -73,6 +72,97 @@ static void test_one(const char *data, ...) {
va_end(ap);
}
+typedef void (*Test)(JsonVariant *);
+
+static void test_file(const char *data, Test test) {
+ _cleanup_json_variant_unref_ JsonVariant *v = NULL;
+ int r;
+
+ r = json_parse(data, &v);
+ assert_se(r == 0);
+ assert_se(v != NULL);
+ assert_se(v->type == JSON_VARIANT_OBJECT);
+
+ if (test)
+ test(v);
+}
+
+static void test_1(JsonVariant *v) {
+ JsonVariant *p, *q;
+ unsigned i;
+
+ /* 3 keys + 3 values */
+ assert_se(v->size == 6);
+
+ /* has k */
+ p = json_variant_value(v, "k");
+ assert_se(p && p->type == JSON_VARIANT_STRING);
+
+ /* k equals v */
+ assert_se(streq(json_variant_string(p), "v"));
+
+ /* has foo */
+ p = json_variant_value(v, "foo");
+ assert_se(p && p->type == JSON_VARIANT_ARRAY && p->size == 3);
+
+ /* check foo[0] = 1, foo[1] = 2, foo[2] = 3 */
+ for (i = 0; i < 3; ++i) {
+ q = json_variant_element(p, i);
+ assert_se(q && q->type == JSON_VARIANT_INTEGER && json_variant_integer(q) == (i+1));
+ }
+
+ /* has bar */
+ p = json_variant_value(v, "bar");
+ assert_se(p && p->type == JSON_VARIANT_OBJECT && p->size == 2);
+
+ /* zap is null */
+ q = json_variant_value(p, "zap");
+ assert_se(q && q->type == JSON_VARIANT_NULL);
+}
+
+static void test_2(JsonVariant *v) {
+ JsonVariant *p, *q;
+
+ /* 2 keys + 2 values */
+ assert_se(v->size == 4);
+
+ /* has mutant */
+ p = json_variant_value(v, "mutant");
+ assert_se(p && p->type == JSON_VARIANT_ARRAY && p->size == 4);
+
+ /* mutant[0] == 1 */
+ q = json_variant_element(p, 0);
+ assert_se(q && q->type == JSON_VARIANT_INTEGER && json_variant_integer(q) == 1);
+
+ /* mutant[1] == null */
+ q = json_variant_element(p, 1);
+ assert_se(q && q->type == JSON_VARIANT_NULL);
+
+ /* mutant[2] == "1" */
+ q = json_variant_element(p, 2);
+ assert_se(q && q->type == JSON_VARIANT_STRING && streq(json_variant_string(q), "1"));
+
+ /* mutant[3] == JSON_VARIANT_OBJECT */
+ q = json_variant_element(p, 3);
+ assert_se(q && q->type == JSON_VARIANT_OBJECT && q->size == 2);
+
+ /* has 1 */
+ p = json_variant_value(q, "1");
+ assert_se(p && p->type == JSON_VARIANT_ARRAY && p->size == 2);
+
+ /* "1"[0] == 1 */
+ q = json_variant_element(p, 0);
+ assert_se(q && q->type == JSON_VARIANT_INTEGER && json_variant_integer(q) == 1);
+
+ /* "1"[1] == "1" */
+ q = json_variant_element(p, 1);
+ assert_se(q && q->type == JSON_VARIANT_STRING && streq(json_variant_string(q), "1"));
+
+ /* has blah */
+ p = json_variant_value(v, "blah");
+ assert_se(p && p->type == JSON_VARIANT_REAL && fabs(json_variant_real(p) - 1.27) < 0.001);
+}
+
int main(int argc, char *argv[]) {
test_one("x", -EINVAL);
@@ -103,5 +193,10 @@ int main(int argc, char *argv[]) {
test_one("\"\\udc00\\udc00\"", -EINVAL);
test_one("\"\\ud801\\udc37\"", JSON_STRING, "\xf0\x90\x90\xb7", JSON_END);
+ test_one("[1, 2]", JSON_ARRAY_OPEN, JSON_INTEGER, (intmax_t) 1, JSON_COMMA, JSON_INTEGER, (intmax_t) 2, JSON_ARRAY_CLOSE, JSON_END);
+
+ test_file("{\"k\": \"v\", \"foo\": [1, 2, 3], \"bar\": {\"zap\": null}}", test_1);
+ test_file("{\"mutant\": [1, null, \"1\", {\"1\": [1, \"1\"]}], \"blah\": 1.27}", test_2);
+
return 0;
}
diff --git a/src/test/test-libudev.c b/src/test/test-libudev.c
index 293c151d42..34c49b969a 100644
--- a/src/test/test-libudev.c
+++ b/src/test/test-libudev.c
@@ -19,13 +19,8 @@
***/
#include <stdio.h>
-#include <stdarg.h>
-#include <stdlib.h>
#include <unistd.h>
-#include <errno.h>
-#include <string.h>
#include <getopt.h>
-#include <fcntl.h>
#include <sys/epoll.h>
#include "libudev.h"
@@ -309,6 +304,7 @@ static int test_queue(struct udev *udev) {
static int test_enumerate(struct udev *udev, const char *subsystem) {
struct udev_enumerate *udev_enumerate;
+ int r;
printf("enumerate '%s'\n", subsystem == NULL ? "<all>" : subsystem);
udev_enumerate = udev_enumerate_new(udev);
@@ -344,7 +340,11 @@ static int test_enumerate(struct udev *udev, const char *subsystem) {
if (udev_enumerate == NULL)
return -1;
udev_enumerate_add_match_subsystem(udev_enumerate,"block");
- udev_enumerate_add_match_is_initialized(udev_enumerate);
+ r = udev_enumerate_add_match_is_initialized(udev_enumerate);
+ if (r < 0) {
+ udev_enumerate_unref(udev_enumerate);
+ return r;
+ }
udev_enumerate_scan_devices(udev_enumerate);
test_enumerate_print_list(udev_enumerate);
udev_enumerate_unref(udev_enumerate);
diff --git a/src/test/test-locale-util.c b/src/test/test-locale-util.c
index 1398a3a0b9..9765075365 100644
--- a/src/test/test-locale-util.c
+++ b/src/test/test-locale-util.c
@@ -17,7 +17,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <unistd.h>
#include "locale-util.h"
#include "strv.h"
diff --git a/src/test/test-log.c b/src/test/test-log.c
index ca64004b4c..9dcfa2f274 100644
--- a/src/test/test-log.c
+++ b/src/test/test-log.c
@@ -24,6 +24,7 @@
#include "log.h"
#include "util.h"
+#include "formats-util.h"
int main(int argc, char* argv[]) {
diff --git a/src/test/test-loopback.c b/src/test/test-loopback.c
index 75fe053b63..c03bda4382 100644
--- a/src/test/test-loopback.c
+++ b/src/test/test-loopback.c
@@ -19,14 +19,11 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <errno.h>
#include <string.h>
#include <stdio.h>
-#include <fcntl.h>
#include "loopback-setup.h"
#include "log.h"
-#include "util.h"
int main(int argc, char* argv[]) {
int r;
diff --git a/src/test/test-namespace.c b/src/test/test-namespace.c
index 2397db5fff..7d7e08dc5d 100644
--- a/src/test/test-namespace.c
+++ b/src/test/test-namespace.c
@@ -23,6 +23,7 @@
#include "namespace.h"
#include "util.h"
+#include "process-util.h"
static void test_tmpdir(const char *id, const char *A, const char *B) {
_cleanup_free_ char *a, *b;
diff --git a/src/test/test-ns.c b/src/test/test-ns.c
index 7cd7b77153..3050be9e9d 100644
--- a/src/test/test-ns.c
+++ b/src/test/test-ns.c
@@ -20,13 +20,9 @@
***/
#include <stdlib.h>
-#include <string.h>
#include <unistd.h>
-#include <sys/mount.h>
-#include <linux/fs.h>
#include "namespace.h"
-#include "execute.h"
#include "log.h"
int main(int argc, char *argv[]) {
@@ -42,10 +38,12 @@ int main(int argc, char *argv[]) {
NULL
};
- const char * const inaccessible[] = {
+ const char *inaccessible[] = {
"/home/lennart/projects",
NULL
};
+ char *root_directory;
+ char *projects_directory;
int r;
char tmp_dir[] = "/tmp/systemd-private-XXXXXX",
@@ -54,7 +52,20 @@ int main(int argc, char *argv[]) {
assert_se(mkdtemp(tmp_dir));
assert_se(mkdtemp(var_tmp_dir));
- r = setup_namespace((char **) writable,
+ root_directory = getenv("TEST_NS_CHROOT");
+ projects_directory = getenv("TEST_NS_PROJECTS");
+
+ if (projects_directory)
+ inaccessible[0] = projects_directory;
+
+ log_info("Inaccessible directory: '%s'", inaccessible[0]);
+ if (root_directory)
+ log_info("Chroot: '%s'", root_directory);
+ else
+ log_info("Not chrooted");
+
+ r = setup_namespace(root_directory,
+ (char **) writable,
(char **) readonly,
(char **) inaccessible,
tmp_dir,
@@ -66,6 +77,11 @@ int main(int argc, char *argv[]) {
0);
if (r < 0) {
log_error_errno(r, "Failed to setup namespace: %m");
+
+ log_info("Usage:\n"
+ " sudo TEST_NS_PROJECTS=/home/lennart/projects ./test-ns\n"
+ " sudo TEST_NS_CHROOT=/home/alban/debian-tree TEST_NS_PROJECTS=/home/alban/debian-tree/home/alban/Documents ./test-ns");
+
return 1;
}
diff --git a/src/test/test-path-lookup.c b/src/test/test-path-lookup.c
index 38e5c93df6..aa4bac6cdd 100644
--- a/src/test/test-path-lookup.c
+++ b/src/test/test-path-lookup.c
@@ -20,13 +20,13 @@
***/
#include <sys/stat.h>
-#include <sys/types.h>
#include "path-lookup.h"
#include "log.h"
#include "strv.h"
+#include "rm-rf.h"
-static void test_paths(SystemdRunningAs running_as, bool personal) {
+static void test_paths(ManagerRunningAs running_as, bool personal) {
char template[] = "/tmp/test-path-lookup.XXXXXXX";
_cleanup_lookup_paths_free_ LookupPaths lp = {};
@@ -43,14 +43,14 @@ static void test_paths(SystemdRunningAs running_as, bool personal) {
assert_se(strv_contains(lp.unit_path, exists));
assert_se(strv_contains(lp.unit_path, not));
- assert_se(rm_rf_dangerous(template, false, true, false) >= 0);
+ assert_se(rm_rf(template, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
}
-static void print_generator_paths(SystemdRunningAs running_as) {
+static void print_generator_paths(ManagerRunningAs running_as) {
_cleanup_strv_free_ char **paths;
char **dir;
- log_info("Generators dirs (%s):", running_as == SYSTEMD_SYSTEM ? "system" : "user");
+ log_info("Generators dirs (%s):", running_as == MANAGER_SYSTEM ? "system" : "user");
paths = generator_paths(running_as);
STRV_FOREACH(dir, paths)
@@ -62,13 +62,13 @@ int main(int argc, char **argv) {
log_parse_environment();
log_open();
- test_paths(SYSTEMD_SYSTEM, false);
- test_paths(SYSTEMD_SYSTEM, true);
- test_paths(SYSTEMD_USER, false);
- test_paths(SYSTEMD_USER, true);
+ test_paths(MANAGER_SYSTEM, false);
+ test_paths(MANAGER_SYSTEM, true);
+ test_paths(MANAGER_USER, false);
+ test_paths(MANAGER_USER, true);
- print_generator_paths(SYSTEMD_SYSTEM);
- print_generator_paths(SYSTEMD_USER);
+ print_generator_paths(MANAGER_SYSTEM);
+ print_generator_paths(MANAGER_USER);
return EXIT_SUCCESS;
}
diff --git a/src/test/test-path-util.c b/src/test/test-path-util.c
index 11aa52aaed..0045ae6824 100644
--- a/src/test/test-path-util.c
+++ b/src/test/test-path-util.c
@@ -21,29 +21,47 @@
#include <stdio.h>
#include <unistd.h>
+#include <sys/mount.h>
#include "path-util.h"
#include "util.h"
#include "macro.h"
#include "strv.h"
+#include "rm-rf.h"
+#define test_path_compare(a, b, result) { \
+ assert_se(path_compare(a, b) == result); \
+ assert_se(path_compare(b, a) == -result); \
+ assert_se(path_equal(a, b) == !result); \
+ assert_se(path_equal(b, a) == !result); \
+ }
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"));
+ _cleanup_close_ int fd = -1;
+
+ test_path_compare("/goo", "/goo", 0);
+ test_path_compare("/goo", "/goo", 0);
+ test_path_compare("//goo", "/goo", 0);
+ test_path_compare("//goo/////", "/goo", 0);
+ test_path_compare("goo/////", "goo", 0);
+
+ test_path_compare("/goo/boo", "/goo//boo", 0);
+ test_path_compare("//goo/boo", "/goo/boo//", 0);
- assert_se(path_equal("/goo/boo", "/goo//boo"));
- assert_se(path_equal("//goo/boo", "/goo/boo//"));
+ test_path_compare("/", "///", 0);
- assert_se(path_equal("/", "///"));
+ test_path_compare("/x", "x/", 1);
+ test_path_compare("x/", "/", -1);
- assert_se(!path_equal("/x", "x/"));
- assert_se(!path_equal("x/", "/"));
+ test_path_compare("/x/./y", "x/y", 1);
+ test_path_compare("x/.y", "x/y", -1);
- assert_se(!path_equal("/x/./y", "x/y"));
- assert_se(!path_equal("x/.y", "x/y"));
+ test_path_compare("foo", "/foo", -1);
+ test_path_compare("/foo", "/foo/bar", -1);
+ test_path_compare("/foo/aaa", "/foo/b", -1);
+ test_path_compare("/foo/aaa", "/foo/b/a", -1);
+ test_path_compare("/foo/a", "/foo/aaa", -1);
+ test_path_compare("/foo/a/b", "/foo/aaa", -1);
assert_se(path_is_absolute("/"));
assert_se(!path_is_absolute("./"));
@@ -71,8 +89,9 @@ static void test_path(void) {
test_parent("/aa///file...", "/aa///");
test_parent("file.../", NULL);
- assert_se(path_is_mount_point("/", true));
- assert_se(path_is_mount_point("/", false));
+ fd = open("/", O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_NOCTTY);
+ assert_se(fd >= 0);
+ assert_se(fd_is_mount_point(fd, "/", 0) > 0);
{
char p1[] = "aaa/bbb////ccc";
@@ -242,7 +261,7 @@ static void test_strv_resolve(void) {
assert_se(streq(search_dirs[1], "/dir2"));
assert_se(streq(search_dirs[2], "/dir2"));
- assert_se(rm_rf_dangerous(tmp_dir, false, true, false) == 0);
+ assert_se(rm_rf(tmp_dir, REMOVE_ROOT|REMOVE_PHYSICAL) == 0);
}
static void test_path_startswith(void) {
@@ -264,6 +283,94 @@ static void test_path_startswith(void) {
assert_se(!path_startswith("/foo/bar/barfoo/", "/f/b/b/"));
}
+static void test_prefix_root_one(const char *r, const char *p, const char *expected) {
+ _cleanup_free_ char *s = NULL;
+ const char *t;
+
+ assert_se(s = prefix_root(r, p));
+ assert_se(streq_ptr(s, expected));
+
+ t = prefix_roota(r, p);
+ assert_se(t);
+ assert_se(streq_ptr(t, expected));
+}
+
+static void test_prefix_root(void) {
+ test_prefix_root_one("/", "/foo", "/foo");
+ test_prefix_root_one(NULL, "/foo", "/foo");
+ test_prefix_root_one("", "/foo", "/foo");
+ test_prefix_root_one("///", "/foo", "/foo");
+ test_prefix_root_one("/", "////foo", "/foo");
+ test_prefix_root_one(NULL, "////foo", "/foo");
+
+ test_prefix_root_one("/foo", "/bar", "/foo/bar");
+ test_prefix_root_one("/foo", "bar", "/foo/bar");
+ test_prefix_root_one("foo", "bar", "foo/bar");
+ test_prefix_root_one("/foo/", "/bar", "/foo/bar");
+ test_prefix_root_one("/foo/", "//bar", "/foo/bar");
+ test_prefix_root_one("/foo///", "//bar", "/foo/bar");
+}
+
+static void test_path_is_mount_point(void) {
+ int fd, rt, rf, rlt, rlf;
+ char tmp_dir[] = "/tmp/test-path-is-mount-point-XXXXXX";
+ _cleanup_free_ char *file1 = NULL, *file2 = NULL, *link1 = NULL, *link2 = NULL;
+
+ assert_se(path_is_mount_point("/", AT_SYMLINK_FOLLOW) > 0);
+ assert_se(path_is_mount_point("/", 0) > 0);
+
+ assert_se(path_is_mount_point("/proc", AT_SYMLINK_FOLLOW) > 0);
+ assert_se(path_is_mount_point("/proc", 0) > 0);
+
+ assert_se(path_is_mount_point("/proc/1", AT_SYMLINK_FOLLOW) == 0);
+ assert_se(path_is_mount_point("/proc/1", 0) == 0);
+
+ assert_se(path_is_mount_point("/sys", AT_SYMLINK_FOLLOW) > 0);
+ assert_se(path_is_mount_point("/sys", 0) > 0);
+
+ /* file mountpoints */
+ assert_se(mkdtemp(tmp_dir) != NULL);
+ file1 = path_join(NULL, tmp_dir, "file1");
+ assert_se(file1);
+ file2 = path_join(NULL, tmp_dir, "file2");
+ assert_se(file2);
+ fd = open(file1, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0664);
+ assert_se(fd > 0);
+ close(fd);
+ fd = open(file2, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0664);
+ assert_se(fd > 0);
+ close(fd);
+ link1 = path_join(NULL, tmp_dir, "link1");
+ assert_se(link1);
+ assert_se(symlink("file1", link1) == 0);
+ link2 = path_join(NULL, tmp_dir, "link2");
+ assert_se(link1);
+ assert_se(symlink("file2", link2) == 0);
+
+ assert_se(path_is_mount_point(file1, AT_SYMLINK_FOLLOW) == 0);
+ assert_se(path_is_mount_point(file1, 0) == 0);
+ assert_se(path_is_mount_point(link1, AT_SYMLINK_FOLLOW) == 0);
+ assert_se(path_is_mount_point(link1, 0) == 0);
+
+ /* this test will only work as root */
+ if (mount(file1, file2, NULL, MS_BIND, NULL) >= 0) {
+ rf = path_is_mount_point(file2, 0);
+ rt = path_is_mount_point(file2, AT_SYMLINK_FOLLOW);
+ rlf = path_is_mount_point(link2, 0);
+ rlt = path_is_mount_point(link2, AT_SYMLINK_FOLLOW);
+
+ assert_se(umount(file2) == 0);
+
+ assert_se(rf == 1);
+ assert_se(rt == 1);
+ assert_se(rlf == 0);
+ assert_se(rlt == 1);
+ } else
+ printf("Skipping bind mount file test: %m\n");
+
+ assert_se(rm_rf(tmp_dir, REMOVE_ROOT|REMOVE_PHYSICAL) == 0);
+}
+
int main(int argc, char **argv) {
test_path();
test_find_binary(argv[0], true);
@@ -274,6 +381,8 @@ int main(int argc, char **argv) {
test_make_relative();
test_strv_resolve();
test_path_startswith();
+ test_prefix_root();
+ test_path_is_mount_point();
return 0;
}
diff --git a/src/test/test-path.c b/src/test/test-path.c
index 4f9f5c1344..5d190378f1 100644
--- a/src/test/test-path.c
+++ b/src/test/test-path.c
@@ -26,6 +26,7 @@
#include "macro.h"
#include "strv.h"
#include "mkdir.h"
+#include "rm-rf.h"
typedef void (*test_function_t)(Manager *m);
@@ -33,12 +34,12 @@ static int setup_test(Manager **m) {
char **tests_path = STRV_MAKE("exists", "existsglobFOOBAR", "changed", "modified", "unit",
"directorynotempty", "makedirectory");
char **test_path;
- Manager *tmp;
+ Manager *tmp = NULL;
int r;
assert_se(m);
- r = manager_new(SYSTEMD_USER, true, &tmp);
+ r = manager_new(MANAGER_USER, true, &tmp);
if (IN_SET(r, -EPERM, -EACCES, -EADDRINUSE, -EHOSTDOWN, -ENOENT)) {
printf("Skipping test: manager_new: %s", strerror(-r));
return -EXIT_TEST_SKIP;
@@ -47,7 +48,12 @@ static int setup_test(Manager **m) {
assert_se(manager_startup(tmp, NULL, NULL) >= 0);
STRV_FOREACH(test_path, tests_path) {
- rm_rf_dangerous(strjoina("/tmp/test-path_", *test_path), false, true, false);
+ _cleanup_free_ char *p = NULL;
+
+ p = strjoin("/tmp/test-path_", *test_path, NULL);
+ assert_se(p);
+
+ (void) rm_rf(p, REMOVE_ROOT|REMOVE_PHYSICAL);
}
*m = tmp;
@@ -104,7 +110,7 @@ static void check_stop_unlink(Manager *m, Unit *unit, const char *test_path, con
}
assert_se(UNIT_VTABLE(unit)->stop(unit) >= 0);
- rm_rf_dangerous(test_path, false, true, false);
+ (void) rm_rf(test_path, REMOVE_ROOT|REMOVE_PHYSICAL);
}
static void test_path_exists(Manager *m) {
@@ -228,7 +234,7 @@ static void test_path_makedirectory_directorymode(Manager *m) {
assert_se((s.st_mode & S_IRWXO) == 0004);
assert_se(UNIT_VTABLE(unit)->stop(unit) >= 0);
- rm_rf_dangerous(test_path, false, true, false);
+ (void) rm_rf(test_path, REMOVE_ROOT|REMOVE_PHYSICAL);
}
int main(int argc, char *argv[]) {
@@ -248,7 +254,7 @@ int main(int argc, char *argv[]) {
log_parse_environment();
log_open();
- assert_se(set_unit_path(TEST_DIR ":") >= 0);
+ assert_se(set_unit_path(TEST_DIR) >= 0);
for (test = tests; test && *test; test++) {
int r;
diff --git a/src/test/test-process-util.c b/src/test/test-process-util.c
new file mode 100644
index 0000000000..e4e2efecd5
--- /dev/null
+++ b/src/test/test-process-util.c
@@ -0,0 +1,138 @@
+/***
+ 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 <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "process-util.h"
+#include "log.h"
+#include "util.h"
+#include "macro.h"
+#include "virt.h"
+#include "terminal-util.h"
+
+static void test_get_process_comm(void) {
+ struct stat st;
+ _cleanup_free_ char *a = NULL, *c = NULL, *d = NULL, *f = NULL, *i = NULL, *cwd = NULL, *root = NULL;
+ _cleanup_free_ char *env = NULL;
+ pid_t e;
+ uid_t u;
+ gid_t g;
+ dev_t h;
+ int r;
+ pid_t me;
+
+ if (stat("/proc/1/comm", &st) == 0) {
+ assert_se(get_process_comm(1, &a) >= 0);
+ log_info("pid1 comm: '%s'", a);
+ } else
+ log_warning("/proc/1/comm does not exist.");
+
+ 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: "PID_FMT, 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: "UID_FMT, u);
+ assert_se(u == 0);
+
+ assert_se(get_process_gid(1, &g) == 0);
+ log_info("pid1 gid: "GID_FMT, g);
+ assert_se(g == 0);
+
+ me = getpid();
+
+ r = get_process_cwd(me, &cwd);
+ assert_se(r >= 0 || r == -EACCES);
+ log_info("pid1 cwd: '%s'", cwd);
+
+ r = get_process_root(me, &root);
+ assert_se(r >= 0 || r == -EACCES);
+ log_info("pid1 root: '%s'", root);
+
+ r = get_process_environ(me, &env);
+ assert_se(r >= 0 || r == -EACCES);
+ log_info("self strlen(environ): '%zu'", strlen(env));
+
+ if (!detect_container(NULL))
+ assert_se(get_ctty_devnr(1, &h) == -ENXIO);
+
+ getenv_for_pid(1, "PATH", &i);
+ log_info("pid1 $PATH: '%s'", strna(i));
+}
+
+static void test_pid_is_unwaited(void) {
+ pid_t pid;
+
+ pid = fork();
+ assert_se(pid >= 0);
+ if (pid == 0) {
+ _exit(EXIT_SUCCESS);
+ } else {
+ int status;
+
+ waitpid(pid, &status, 0);
+ assert_se(!pid_is_unwaited(pid));
+ }
+ assert_se(pid_is_unwaited(getpid()));
+ assert_se(!pid_is_unwaited(-1));
+}
+
+static void test_pid_is_alive(void) {
+ pid_t pid;
+
+ pid = fork();
+ assert_se(pid >= 0);
+ if (pid == 0) {
+ _exit(EXIT_SUCCESS);
+ } else {
+ int status;
+
+ waitpid(pid, &status, 0);
+ assert_se(!pid_is_alive(pid));
+ }
+ assert_se(pid_is_alive(getpid()));
+ assert_se(!pid_is_alive(-1));
+}
+
+int main(int argc, char *argv[]) {
+ log_parse_environment();
+ log_open();
+
+ test_get_process_comm();
+ test_pid_is_unwaited();
+ test_pid_is_alive();
+
+ return 0;
+}
diff --git a/src/test/test-pty.c b/src/test/test-pty.c
index cab569a9da..f8807c9150 100644
--- a/src/test/test-pty.c
+++ b/src/test/test-pty.c
@@ -20,15 +20,14 @@
***/
#include <errno.h>
-#include <fcntl.h>
#include <locale.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
-#include "def.h"
#include "pty.h"
#include "util.h"
+#include "signal-util.h"
static const char sndmsg[] = "message\n";
static const char rcvmsg[] = "message\r\n";
@@ -97,7 +96,7 @@ static void run_parent(Pty *pty) {
static void test_pty(void) {
pid_t pid;
- Pty *pty;
+ Pty *pty = NULL;
rcvsiz = 0;
zero(rcvbuf);
diff --git a/src/test/test-ring.c b/src/test/test-ring.c
index a9dd01ca01..cb8a5d4e9e 100644
--- a/src/test/test-ring.c
+++ b/src/test/test-ring.c
@@ -20,14 +20,9 @@
***/
#include <string.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <locale.h>
-#include <errno.h>
#include "def.h"
#include "ring.h"
-#include "util.h"
static void test_ring(void) {
static const char buf[8192];
diff --git a/src/test/test-rtnl-manual.c b/src/test/test-rtnl-manual.c
index c8133dbad7..c406454f77 100644
--- a/src/test/test-rtnl-manual.c
+++ b/src/test/test-rtnl-manual.c
@@ -19,7 +19,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <netinet/ether.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <linux/ip.h>
@@ -29,10 +28,6 @@
#include "util.h"
#include "macro.h"
#include "sd-rtnl.h"
-#include "socket-util.h"
-#include "rtnl-util.h"
-#include "event-util.h"
-#include "rtnl-internal.h"
static int load_module(const char *mod_name) {
struct kmod_ctx *ctx;
diff --git a/src/test/test-sched-prio.c b/src/test/test-sched-prio.c
index 6b3128d3b7..f915539e00 100644
--- a/src/test/test-sched-prio.c
+++ b/src/test/test-sched-prio.c
@@ -34,7 +34,7 @@ int main(int argc, char *argv[]) {
/* prepare the test */
assert_se(set_unit_path(TEST_DIR) >= 0);
- r = manager_new(SYSTEMD_USER, true, &m);
+ r = manager_new(MANAGER_USER, true, &m);
if (IN_SET(r, -EPERM, -EACCES, -EADDRINUSE, -EHOSTDOWN, -ENOENT)) {
printf("Skipping test: manager_new: %s", strerror(-r));
return EXIT_TEST_SKIP;
diff --git a/src/test/test-set.c b/src/test/test-set.c
index e280c8952d..0ee5ddcc9f 100644
--- a/src/test/test-set.c
+++ b/src/test/test-set.c
@@ -17,7 +17,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "util.h"
#include "set.h"
static void test_set_steal_first(void) {
@@ -40,8 +39,25 @@ static void test_set_steal_first(void) {
assert_se(set_isempty(m));
}
+static void test_set_put(void) {
+ _cleanup_set_free_ Set *m = NULL;
+
+ m = set_new(&string_hash_ops);
+ assert_se(m);
+
+ assert_se(set_put(m, (void*) "1") == 1);
+ assert_se(set_put(m, (void*) "22") == 1);
+ assert_se(set_put(m, (void*) "333") == 1);
+ assert_se(set_put(m, (void*) "333") == 0);
+ assert_se(set_remove(m, (void*) "333"));
+ assert_se(set_put(m, (void*) "333") == 1);
+ assert_se(set_put(m, (void*) "333") == 0);
+ assert_se(set_put(m, (void*) "22") == 0);
+}
+
int main(int argc, const char *argv[]) {
test_set_steal_first();
+ test_set_put();
return 0;
}
diff --git a/src/test/test-sleep.c b/src/test/test-sleep.c
index a1020ad14c..4308ddfb64 100644
--- a/src/test/test-sleep.c
+++ b/src/test/test-sleep.c
@@ -19,10 +19,7 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <errno.h>
-#include <string.h>
#include <stdio.h>
-#include <fcntl.h>
#include "util.h"
#include "log.h"
@@ -59,7 +56,7 @@ int main(int argc, char* argv[]) {
log_open();
if (getuid() != 0)
- log_warning("This program is unlikely to work for unpriviledged users");
+ log_warning("This program is unlikely to work for unprivileged users");
test_sleep();
diff --git a/src/test/test-socket-util.c b/src/test/test-socket-util.c
index 6fb4a40944..f257af445a 100644
--- a/src/test/test-socket-util.c
+++ b/src/test/test-socket-util.c
@@ -231,6 +231,24 @@ static void test_in_addr_prefix_next(void) {
}
+static void test_in_addr_to_string_one(int f, const char *addr) {
+ union in_addr_union ua;
+ _cleanup_free_ char *r = NULL;
+
+ assert_se(in_addr_from_string(f, addr, &ua) >= 0);
+ assert_se(in_addr_to_string(f, &ua, &r) >= 0);
+ printf("test_in_addr_to_string_one: %s == %s\n", addr, r);
+ assert_se(streq(addr, r));
+}
+
+static void test_in_addr_to_string(void) {
+ test_in_addr_to_string_one(AF_INET, "192.168.0.1");
+ test_in_addr_to_string_one(AF_INET, "10.11.12.13");
+ test_in_addr_to_string_one(AF_INET6, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff");
+ test_in_addr_to_string_one(AF_INET6, "::1");
+ test_in_addr_to_string_one(AF_INET6, "fe80::");
+}
+
static void *connect_thread(void *arg) {
union sockaddr_union *sa = arg;
_cleanup_close_ int fd = -1;
@@ -324,6 +342,7 @@ int main(int argc, char *argv[]) {
test_in_addr_prefix_intersect();
test_in_addr_prefix_next();
+ test_in_addr_to_string();
test_nameinfo_pretty();
diff --git a/src/test/test-strip-tab-ansi.c b/src/test/test-strip-tab-ansi.c
index 5016906ad0..358454842a 100644
--- a/src/test/test-strip-tab-ansi.c
+++ b/src/test/test-strip-tab-ansi.c
@@ -22,6 +22,7 @@
#include <stdio.h>
#include "util.h"
+#include "terminal-util.h"
int main(int argc, char *argv[]) {
char *p;
diff --git a/src/test/test-strv.c b/src/test/test-strv.c
index 5ae929c3f8..d5ea2b3fab 100644
--- a/src/test/test-strv.c
+++ b/src/test/test-strv.c
@@ -165,7 +165,7 @@ static void test_strv_quote_unquote(const char* const *split, const char *quoted
assert_se(p);
assert_se(streq(p, quoted));
- r = strv_split_quoted(&s, quoted, false);
+ r = strv_split_quoted(&s, quoted, 0);
assert_se(r == 0);
assert_se(s);
STRV_FOREACH(t, s) {
@@ -182,7 +182,7 @@ static void test_strv_unquote(const char *quoted, char **list) {
char **t;
int r;
- r = strv_split_quoted(&s, quoted, false);
+ r = strv_split_quoted(&s, quoted, 0);
assert_se(r == 0);
assert_se(s);
j = strv_join(s, " | ");
@@ -199,7 +199,7 @@ static void test_invalid_unquote(const char *quoted) {
char **s = NULL;
int r;
- r = strv_split_quoted(&s, quoted, false);
+ r = strv_split_quoted(&s, quoted, 0);
assert_se(s == NULL);
assert_se(r == -EINVAL);
}
diff --git a/src/test/test-strxcpyx.c b/src/test/test-strxcpyx.c
index a7c8e1267d..858a4081da 100644
--- a/src/test/test-strxcpyx.c
+++ b/src/test/test-strxcpyx.c
@@ -22,7 +22,6 @@
#include <string.h>
#include "util.h"
-#include "strv.h"
#include "strxcpyx.h"
static void test_strpcpy(void) {
diff --git a/src/test/test-tables.c b/src/test/test-tables.c
index bda224bec6..0e5ab1645f 100644
--- a/src/test/test-tables.c
+++ b/src/test/test-tables.c
@@ -23,14 +23,12 @@
#include "condition.h"
#include "device.h"
#include "execute.h"
-#include "exit-status.h"
#include "install.h"
#include "job.h"
#include "kill.h"
#include "log.h"
#include "logs-show.h"
#include "mount.h"
-#include "path-lookup.h"
#include "path.h"
#include "scope.h"
#include "service.h"
diff --git a/src/test/test-terminal-util.c b/src/test/test-terminal-util.c
new file mode 100644
index 0000000000..d81fdb9923
--- /dev/null
+++ b/src/test/test-terminal-util.c
@@ -0,0 +1,84 @@
+/***
+ 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 <stdio.h>
+#include <stdbool.h>
+
+#include "terminal-util.h"
+#include "macro.h"
+#include "util.h"
+#include "log.h"
+
+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_read_one_char(void) {
+ _cleanup_fclose_ FILE *file = NULL;
+ char r;
+ bool need_nl;
+ char name[] = "/tmp/test-read_one_char.XXXXXX";
+ int fd;
+
+ fd = mkostemp_safe(name, O_RDWR|O_CLOEXEC);
+ assert_se(fd >= 0);
+ file = fdopen(fd, "r+");
+ assert_se(file);
+ assert_se(fputs("c\n", file) >= 0);
+ rewind(file);
+
+ assert_se(read_one_char(file, &r, 1000000, &need_nl) >= 0);
+ assert_se(!need_nl);
+ assert_se(r == 'c');
+ assert_se(read_one_char(file, &r, 1000000, &need_nl) < 0);
+
+ rewind(file);
+ assert_se(fputs("foobar\n", file) >= 0);
+ rewind(file);
+ assert_se(read_one_char(file, &r, 1000000, &need_nl) < 0);
+
+ rewind(file);
+ assert_se(fputs("\n", file) >= 0);
+ rewind(file);
+ assert_se(read_one_char(file, &r, 1000000, &need_nl) < 0);
+
+ unlink(name);
+}
+
+int main(int argc, char *argv[]) {
+ log_parse_environment();
+ log_open();
+
+ test_default_term_for_tty();
+ test_read_one_char();
+
+ return 0;
+}
diff --git a/src/test/test-time.c b/src/test/test-time.c
index 8cfc4cc4fe..3840fff061 100644
--- a/src/test/test-time.c
+++ b/src/test/test-time.c
@@ -78,12 +78,18 @@ static void test_parse_nsec(void) {
assert_se(u == 2);
assert_se(parse_nsec(".7", &u) >= 0);
assert_se(u == 0);
+ assert_se(parse_nsec("infinity", &u) >= 0);
+ assert_se(u == NSEC_INFINITY);
+ assert_se(parse_nsec(" infinity ", &u) >= 0);
+ assert_se(u == NSEC_INFINITY);
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);
+ assert_se(parse_nsec(" infinity .7", &u) < 0);
+ assert_se(parse_nsec(".3 infinity", &u) < 0);
}
static void test_format_timespan_one(usec_t x, usec_t accuracy) {
diff --git a/src/test/test-tmpfiles.c b/src/test/test-tmpfiles.c
index 4b72c4a8fa..221dd67eb2 100644
--- a/src/test/test-tmpfiles.c
+++ b/src/test/test-tmpfiles.c
@@ -19,14 +19,13 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <sys/types.h>
-#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include "util.h"
+#include "formats-util.h"
int main(int argc, char** argv) {
const char *p = argv[1] ?: "/tmp";
diff --git a/src/test/test-udev.c b/src/test/test-udev.c
index b57d275efa..d1fe953071 100644
--- a/src/test/test-udev.c
+++ b/src/test/test-udev.c
@@ -19,20 +19,16 @@
***/
#include <stdio.h>
-#include <stddef.h>
#include <stdlib.h>
-#include <string.h>
-#include <fcntl.h>
-#include <ctype.h>
#include <errno.h>
#include <unistd.h>
-#include <grp.h>
#include <sched.h>
#include <sys/mount.h>
#include <sys/signalfd.h>
#include "missing.h"
#include "selinux-util.h"
+#include "signal-util.h"
#include "udev.h"
#include "udev-util.h"
@@ -84,7 +80,6 @@ int main(int argc, char *argv[]) {
char syspath[UTIL_PATH_SIZE];
const char *devpath;
const char *action;
- sigset_t mask, sigmask_orig;
int err;
err = fake_filesystems();
@@ -98,8 +93,6 @@ int main(int argc, char *argv[]) {
log_debug("version %s", VERSION);
mac_selinux_init("/dev");
- sigprocmask(SIG_SETMASK, NULL, &sigmask_orig);
-
action = argv[1];
if (action == NULL) {
log_error("action missing");
@@ -115,22 +108,15 @@ int main(int argc, char *argv[]) {
rules = udev_rules_new(udev, 1);
strscpyl(syspath, sizeof(syspath), "/sys", devpath, NULL);
- dev = udev_device_new_from_syspath(udev, syspath);
+ dev = udev_device_new_from_synthetic_event(udev, syspath, action);
if (dev == NULL) {
log_debug("unknown device '%s'", devpath);
goto out;
}
- udev_device_set_action(dev, action);
event = udev_event_new(dev);
- sigfillset(&mask);
- sigprocmask(SIG_SETMASK, &mask, &sigmask_orig);
- event->fd_signal = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC);
- if (event->fd_signal < 0) {
- fprintf(stderr, "error creating signalfd\n");
- goto out;
- }
+ assert_se(sigprocmask_many(SIG_BLOCK, SIGTERM, SIGINT, SIGHUP, SIGCHLD, -1) == 0);
/* do what devtmpfs usually provides us */
if (udev_device_get_devnode(dev) != NULL) {
@@ -153,14 +139,10 @@ int main(int argc, char *argv[]) {
udev_event_execute_rules(event,
3 * USEC_PER_SEC, USEC_PER_SEC,
NULL,
- rules,
- &sigmask_orig);
+ rules);
udev_event_execute_run(event,
- 3 * USEC_PER_SEC, USEC_PER_SEC,
- NULL);
+ 3 * USEC_PER_SEC, USEC_PER_SEC);
out:
- if (event != NULL && event->fd_signal >= 0)
- close(event->fd_signal);
mac_selinux_finish();
return err ? EXIT_FAILURE : EXIT_SUCCESS;
diff --git a/src/test/test-unit-file.c b/src/test/test-unit-file.c
index e517f571d6..a8025c825b 100644
--- a/src/test/test-unit-file.c
+++ b/src/test/test-unit-file.c
@@ -20,7 +20,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <assert.h>
#include <stdio.h>
#include <stddef.h>
#include <string.h>
@@ -37,6 +36,7 @@
#include "strv.h"
#include "fileio.h"
#include "test-helper.h"
+#include "hostname-util.h"
static int test_unit_file_get_set(void) {
int r;
@@ -92,6 +92,7 @@ static void check_execcommand(ExecCommand *c,
static void test_config_parse_exec(void) {
/* int config_parse_exec(
+ const char *unit,
const char *filename,
unsigned line,
const char *section,
@@ -224,6 +225,15 @@ static void test_config_parse_exec(void) {
check_execcommand(c1,
"/sbin/find", NULL, ";", "x", false);
+ log_info("/* encoded semicolon */");
+ r = config_parse_exec(NULL, "fake", 5, "section", 1,
+ "LValue", 0,
+ "/bin/find \\073",
+ &c, NULL);
+ assert_se(r >= 0);
+ c1 = c1->command_next;
+ check_execcommand(c1, "/bin/find", NULL, ";", NULL, false);
+
log_info("/* spaces in the filename */");
r = config_parse_exec(NULL, "fake", 5, "section", 1,
"LValue", 0,
@@ -295,6 +305,16 @@ static void test_config_parse_exec(void) {
c1 = c1->command_next;
check_execcommand(c1, "/path ", NULL, NULL, NULL, false);
+ log_info("/* quoted backslashes */");
+ r = config_parse_exec(NULL, "fake", 5, "section", 1,
+ "LValue", 0,
+ "/bin/grep '\\w+\\K'",
+ &c, NULL);
+ assert_se(r >= 0);
+ c1 = c1->command_next;
+ check_execcommand(c1, "/bin/grep", NULL, "\\w+\\K", NULL, false);
+
+
log_info("/* trailing backslash: \\ */");
/* backslash is invalid */
r = config_parse_exec(NULL, "fake", 4, "section", 1,
@@ -303,6 +323,41 @@ static void test_config_parse_exec(void) {
assert_se(r == 0);
assert_se(c1->command_next == NULL);
+ log_info("/* missing ending ' */");
+ r = config_parse_exec(NULL, "fake", 4, "section", 1,
+ "LValue", 0, "/path 'foo",
+ &c, NULL);
+ assert_se(r == 0);
+ assert_se(c1->command_next == NULL);
+
+ log_info("/* missing ending ' with trailing backslash */");
+ r = config_parse_exec(NULL, "fake", 4, "section", 1,
+ "LValue", 0, "/path 'foo\\",
+ &c, NULL);
+ assert_se(r == 0);
+ assert_se(c1->command_next == NULL);
+
+ log_info("/* invalid space between modifiers */");
+ r = config_parse_exec(NULL, "fake", 4, "section", 1,
+ "LValue", 0, "- /path",
+ &c, NULL);
+ assert_se(r == 0);
+ assert_se(c1->command_next == NULL);
+
+ log_info("/* only modifiers, no path */");
+ r = config_parse_exec(NULL, "fake", 4, "section", 1,
+ "LValue", 0, "-",
+ &c, NULL);
+ assert_se(r == 0);
+ assert_se(c1->command_next == NULL);
+
+ log_info("/* empty argument, reset */");
+ r = config_parse_exec(NULL, "fake", 4, "section", 1,
+ "LValue", 0, "",
+ &c, NULL);
+ assert_se(r == 0);
+ assert_se(c == NULL);
+
exec_command_free_list(c);
}
@@ -439,12 +494,12 @@ 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};
+ UnitFileInstallInfo i = {name, path, user};
+ UnitFileInstallInfo 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};
+ UnitFileInstallInfo i3 = {name3, path3, user};
+ UnitFileInstallInfo i4 = {name3, path3, NULL};
_cleanup_free_ char *mid, *bid, *host;
diff --git a/src/test/test-unit-name.c b/src/test/test-unit-name.c
index 5c7f8b40f7..e5405fb7f3 100644
--- a/src/test/test-unit-name.c
+++ b/src/test/test-unit-name.c
@@ -24,90 +24,166 @@
#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"
+#include "path-util.h"
#include "test-helper.h"
+#include "hostname-util.h"
+
+static void test_unit_name_is_valid(void) {
+ assert_se(unit_name_is_valid("foo.service", UNIT_NAME_ANY));
+ assert_se(unit_name_is_valid("foo.service", UNIT_NAME_PLAIN));
+ assert_se(!unit_name_is_valid("foo.service", UNIT_NAME_INSTANCE));
+ assert_se(!unit_name_is_valid("foo.service", UNIT_NAME_TEMPLATE));
+ assert_se(!unit_name_is_valid("foo.service", UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE));
+
+ assert_se(unit_name_is_valid("foo@bar.service", UNIT_NAME_ANY));
+ assert_se(!unit_name_is_valid("foo@bar.service", UNIT_NAME_PLAIN));
+ assert_se(unit_name_is_valid("foo@bar.service", UNIT_NAME_INSTANCE));
+ assert_se(!unit_name_is_valid("foo@bar.service", UNIT_NAME_TEMPLATE));
+ assert_se(unit_name_is_valid("foo@bar.service", UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE));
+
+ assert_se(unit_name_is_valid("foo@.service", UNIT_NAME_ANY));
+ assert_se(!unit_name_is_valid("foo@.service", UNIT_NAME_PLAIN));
+ assert_se(!unit_name_is_valid("foo@.service", UNIT_NAME_INSTANCE));
+ assert_se(unit_name_is_valid("foo@.service", UNIT_NAME_TEMPLATE));
+ assert_se(unit_name_is_valid("foo@.service", UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE));
+
+ assert_se(!unit_name_is_valid(".service", UNIT_NAME_ANY));
+ assert_se(!unit_name_is_valid("", UNIT_NAME_ANY));
+ assert_se(!unit_name_is_valid("foo.waldo", UNIT_NAME_ANY));
+ assert_se(!unit_name_is_valid("@.service", UNIT_NAME_ANY));
+ assert_se(!unit_name_is_valid("@piep.service", UNIT_NAME_ANY));
+}
-static void test_replacements(void) {
-#define expect(pattern, repl, expected) \
- { \
- _cleanup_free_ char *t = \
- unit_name_replace_instance(pattern, repl); \
- puts(t); \
- assert_se(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");
+static void test_u_n_r_i_one(const char *pattern, const char *repl, const char *expected, int ret) {
+ _cleanup_free_ char *t = NULL;
+ assert_se(unit_name_replace_instance(pattern, repl, &t) == ret);
+ puts(strna(t));
+ assert_se(streq_ptr(t, expected));
+}
+static void test_u_n_r_i(void) {
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_se(streq(k, expected ? expected : path)); \
- }
+ test_u_n_r_i_one("foo@.service", "waldo", "foo@waldo.service", 0);
+ test_u_n_r_i_one("foo@xyz.service", "waldo", "foo@waldo.service", 0);
+ test_u_n_r_i_one("xyz", "waldo", NULL, -EINVAL);
+ test_u_n_r_i_one("", "waldo", NULL, -EINVAL);
+ test_u_n_r_i_one("foo.service", "waldo", NULL, -EINVAL);
+ test_u_n_r_i_one(".service", "waldo", NULL, -EINVAL);
+ test_u_n_r_i_one("foo@", "waldo", NULL, -EINVAL);
+ test_u_n_r_i_one("@bar", "waldo", NULL, -EINVAL);
+}
- expect("/waldo", ".mount", NULL);
- expect("/waldo/quuix", ".mount", NULL);
- expect("/waldo/quuix/", ".mount", "/waldo/quuix");
- expect("/", ".mount", NULL);
- expect("///", ".mount", "/");
+static void test_u_n_f_p_one(const char *path, const char *suffix, const char *expected, int ret) {
+ _cleanup_free_ char *t = NULL;
- puts("-------------------------------------------------");
-#undef expect
-#define expect(pattern, path, suffix, expected) \
- { \
- _cleanup_free_ char *t = \
- unit_name_from_path_instance(pattern, path, suffix); \
- puts(t); \
- assert_se(streq(t, expected)); \
- }
+ assert_se(unit_name_from_path(path, suffix, &t) == ret);
+ puts(strna(t));
+ assert_se(streq_ptr(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");
+ if (t) {
+ _cleanup_free_ char *k = NULL;
+ assert_se(unit_name_to_path(t, &k) == 0);
+ puts(strna(k));
+ assert_se(path_equal(k, isempty(path) ? "/" : path));
+ }
+}
+static void test_u_n_f_p(void) {
puts("-------------------------------------------------");
-#undef expect
-#define expect(pattern) \
- { \
- _cleanup_free_ char *k, *t; \
- assert_se(t = unit_name_mangle(pattern, MANGLE_NOGLOB)); \
- assert_se(k = unit_name_mangle(t, MANGLE_NOGLOB)); \
- puts(t); \
- assert_se(streq(t, k)); \
+ test_u_n_f_p_one("/waldo", ".mount", "waldo.mount", 0);
+ test_u_n_f_p_one("/waldo/quuix", ".mount", "waldo-quuix.mount", 0);
+ test_u_n_f_p_one("/waldo/quuix/", ".mount", "waldo-quuix.mount", 0);
+ test_u_n_f_p_one("", ".mount", "-.mount", 0);
+ test_u_n_f_p_one("/", ".mount", "-.mount", 0);
+ test_u_n_f_p_one("///", ".mount", "-.mount", 0);
+ test_u_n_f_p_one("/foo/../bar", ".mount", NULL, -EINVAL);
+ test_u_n_f_p_one("/foo/./bar", ".mount", NULL, -EINVAL);
+}
+
+static void test_u_n_f_p_i_one(const char *pattern, const char *path, const char *suffix, const char *expected, int ret) {
+ _cleanup_free_ char *t = NULL;
+
+ assert_se(unit_name_from_path_instance(pattern, path, suffix, &t) == ret);
+ puts(strna(t));
+ assert_se(streq_ptr(t, expected));
+
+ if (t) {
+ _cleanup_free_ char *k = NULL, *v = NULL;
+
+ assert_se(unit_name_to_instance(t, &k) > 0);
+ assert_se(unit_name_path_unescape(k, &v) == 0);
+ assert_se(path_equal(v, isempty(path) ? "/" : path));
}
+}
- expect("/home");
- expect("/dev/sda");
- expect("üxknürz.service");
- expect("foobar-meh...waldi.service");
- expect("_____####----.....service");
- expect("_____##@;;;,,,##----.....service");
- expect("xxx@@@@/////\\\\\\\\\\yyy.service");
+static void test_u_n_f_p_i(void) {
+ puts("-------------------------------------------------");
-#undef expect
+ test_u_n_f_p_i_one("waldo", "/waldo", ".mount", "waldo@waldo.mount", 0);
+ test_u_n_f_p_i_one("waldo", "/waldo////quuix////", ".mount", "waldo@waldo-quuix.mount", 0);
+ test_u_n_f_p_i_one("waldo", "/", ".mount", "waldo@-.mount", 0);
+ test_u_n_f_p_i_one("waldo", "", ".mount", "waldo@-.mount", 0);
+ test_u_n_f_p_i_one("waldo", "///", ".mount", "waldo@-.mount", 0);
+ test_u_n_f_p_i_one("waldo", "..", ".mount", NULL, -EINVAL);
+ test_u_n_f_p_i_one("waldo", "/foo", ".waldi", NULL, -EINVAL);
+ test_u_n_f_p_i_one("wa--ldo", "/--", ".mount", "wa--ldo@\\x2d\\x2d.mount", 0);
+}
+
+static void test_u_n_t_p_one(const char *unit, const char *path, int ret) {
+ _cleanup_free_ char *p = NULL;
+
+ assert_se(unit_name_to_path(unit, &p) == ret);
+ assert_se(streq_ptr(path, p));
+}
+
+static void test_u_n_t_p(void) {
+ test_u_n_t_p_one("home.mount", "/home", 0);
+ test_u_n_t_p_one("home-lennart.mount", "/home/lennart", 0);
+ test_u_n_t_p_one("home-lennart-.mount", NULL, -EINVAL);
+ test_u_n_t_p_one("-home-lennart.mount", NULL, -EINVAL);
+ test_u_n_t_p_one("-home--lennart.mount", NULL, -EINVAL);
+ test_u_n_t_p_one("home-..-lennart.mount", NULL, -EINVAL);
+ test_u_n_t_p_one("", NULL, -EINVAL);
+ test_u_n_t_p_one("home/foo", NULL, -EINVAL);
+}
+
+static void test_u_n_m_one(const char *pattern, const char *expect, int ret) {
+ _cleanup_free_ char *t = NULL;
+
+ assert_se(unit_name_mangle(pattern, UNIT_NAME_NOGLOB, &t) == ret);
+ puts(strna(t));
+ assert_se(streq_ptr(t, expect));
+
+ if (t) {
+ _cleanup_free_ char *k = NULL;
+
+ assert_se(unit_name_is_valid(t, UNIT_NAME_ANY));
+
+ assert_se(unit_name_mangle(t, UNIT_NAME_NOGLOB, &k) == 0);
+ assert_se(streq_ptr(t, k));
+ }
+}
+
+static void test_u_n_m(void) {
+ puts("-------------------------------------------------");
+ test_u_n_m_one("foo.service", "foo.service", 0);
+ test_u_n_m_one("/home", "home.mount", 1);
+ test_u_n_m_one("/dev/sda", "dev-sda.device", 1);
+ test_u_n_m_one("üxknürz.service", "\\xc3\\xbcxkn\\xc3\\xbcrz.service", 1);
+ test_u_n_m_one("foobar-meh...waldi.service", "foobar-meh...waldi.service", 0);
+ test_u_n_m_one("_____####----.....service", "_____\\x23\\x23\\x23\\x23----.....service", 1);
+ test_u_n_m_one("_____##@;;;,,,##----.....service", "_____\\x23\\x23@\\x3b\\x3b\\x3b\\x2c\\x2c\\x2c\\x23\\x23----.....service", 1);
+ test_u_n_m_one("xxx@@@@/////\\\\\\\\\\yyy.service", "xxx@@@@-----\\\\\\\\\\yyy.service", 1);
+ test_u_n_m_one("", NULL, -EINVAL);
}
static int test_unit_printf(void) {
@@ -125,7 +201,7 @@ static int test_unit_printf(void) {
assert_se((root = getpwnam("root")));
assert_se(asprintf(&root_uid, "%d", (int) root->pw_uid) > 0);
- r = manager_new(SYSTEMD_USER, true, &m);
+ r = manager_new(MANAGER_USER, true, &m);
if (r == -EPERM || r == -EACCES || r == -EADDRINUSE) {
puts("manager_new: Permission denied. Skipping test.");
return EXIT_TEST_SKIP;
@@ -226,64 +302,83 @@ static void test_unit_prefix_is_valid(void) {
}
static void test_unit_name_change_suffix(void) {
- char *r;
+ char *t;
- r = unit_name_change_suffix("foo.bar", ".service");
- assert_se(r);
- assert_se(streq(r, "foo.service"));
- free(r);
+ assert_se(unit_name_change_suffix("foo.mount", ".service", &t) == 0);
+ assert_se(streq(t, "foo.service"));
+ free(t);
- r = unit_name_change_suffix("foo@stuff.bar", ".boo");
- assert_se(r);
- assert_se(streq(r, "foo@stuff.boo"));
- free(r);
+ assert_se(unit_name_change_suffix("foo@stuff.service", ".socket", &t) == 0);
+ assert_se(streq(t, "foo@stuff.socket"));
+ free(t);
}
static void test_unit_name_build(void) {
- char *r;
+ char *t;
- r = unit_name_build("foo", "bar", ".service");
- assert_se(r);
- assert_se(streq(r, "foo@bar.service"));
- free(r);
+ assert_se(unit_name_build("foo", "bar", ".service", &t) == 0);
+ assert_se(streq(t, "foo@bar.service"));
+ free(t);
- r = unit_name_build("fo0-stUff_b", "bar", ".mount");
- assert_se(r);
- assert_se(streq(r, "fo0-stUff_b@bar.mount"));
- free(r);
+ assert_se(unit_name_build("fo0-stUff_b", "bar", ".mount", &t) == 0);
+ assert_se(streq(t, "fo0-stUff_b@bar.mount"));
+ free(t);
- r = unit_name_build("foo", NULL, ".service");
- assert_se(r);
- assert_se(streq(r, "foo.service"));
- free(r);
+ assert_se(unit_name_build("foo", NULL, ".service", &t) == 0);
+ assert_se(streq(t, "foo.service"));
+ free(t);
}
-static void test_unit_name_is_instance(void) {
- assert_se(unit_name_is_instance("a@b.service"));
- assert_se(unit_name_is_instance("a-c_c01Aj@b05Dii_-oioi.service"));
-
- assert_se(!unit_name_is_instance("a.service"));
- assert_se(!unit_name_is_instance("a@.service"));
- assert_se(!unit_name_is_instance("junk"));
- assert_se(!unit_name_is_instance(""));
+static void test_slice_name_is_valid(void) {
+ assert_se(slice_name_is_valid("-.slice"));
+ assert_se(slice_name_is_valid("foo.slice"));
+ assert_se(slice_name_is_valid("foo-bar.slice"));
+ assert_se(slice_name_is_valid("foo-bar-baz.slice"));
+ assert_se(!slice_name_is_valid("-foo-bar-baz.slice"));
+ assert_se(!slice_name_is_valid("foo-bar-baz-.slice"));
+ assert_se(!slice_name_is_valid("-foo-bar-baz-.slice"));
+ assert_se(!slice_name_is_valid("foo-bar--baz.slice"));
+ assert_se(!slice_name_is_valid("foo--bar--baz.slice"));
+ assert_se(!slice_name_is_valid(".slice"));
+ assert_se(!slice_name_is_valid(""));
+ assert_se(!slice_name_is_valid("foo.service"));
}
static void test_build_subslice(void) {
char *a;
char *b;
- assert_se(build_subslice("-.slice", "foo", &a) >= 0);
- assert_se(build_subslice(a, "bar", &b) >= 0);
+ assert_se(slice_build_subslice("-.slice", "foo", &a) >= 0);
+ assert_se(slice_build_subslice(a, "bar", &b) >= 0);
free(a);
- assert_se(build_subslice(b, "barfoo", &a) >= 0);
+ assert_se(slice_build_subslice(b, "barfoo", &a) >= 0);
free(b);
- assert_se(build_subslice(a, "foobar", &b) >= 0);
+ assert_se(slice_build_subslice(a, "foobar", &b) >= 0);
free(a);
assert_se(streq(b, "foo-bar-barfoo-foobar.slice"));
free(b);
- assert_se(build_subslice("foo.service", "bar", &a) < 0);
- assert_se(build_subslice("foo", "bar", &a) < 0);
+ assert_se(slice_build_subslice("foo.service", "bar", &a) < 0);
+ assert_se(slice_build_subslice("foo", "bar", &a) < 0);
+}
+
+static void test_build_parent_slice_one(const char *name, const char *expect, int ret) {
+ _cleanup_free_ char *s = NULL;
+
+ assert_se(slice_build_parent_slice(name, &s) == ret);
+ assert_se(streq_ptr(s, expect));
+}
+
+static void test_build_parent_slice(void) {
+ test_build_parent_slice_one("-.slice", NULL, 0);
+ test_build_parent_slice_one("foo.slice", "-.slice", 1);
+ test_build_parent_slice_one("foo-bar.slice", "foo.slice", 1);
+ test_build_parent_slice_one("foo-bar-baz.slice", "foo-bar.slice", 1);
+ test_build_parent_slice_one("foo-bar--baz.slice", NULL, -EINVAL);
+ test_build_parent_slice_one("-foo-bar.slice", NULL, -EINVAL);
+ test_build_parent_slice_one("foo-bar-.slice", NULL, -EINVAL);
+ test_build_parent_slice_one("foo-bar.service", NULL, -EINVAL);
+ test_build_parent_slice_one(".slice", NULL, -EINVAL);
}
static void test_unit_name_to_instance(void) {
@@ -300,13 +395,13 @@ static void test_unit_name_to_instance(void) {
assert_se(streq(instance, ""));
free(instance);
- r = unit_name_to_instance("fo0-stUff_b@b.e", &instance);
+ r = unit_name_to_instance("fo0-stUff_b@b.service", &instance);
assert_se(r >= 0);
assert_se(streq(instance, "b"));
free(instance);
- r = unit_name_to_instance("foo.bar", &instance);
- assert_se(r >= 0);
+ r = unit_name_to_instance("foo.service", &instance);
+ assert_se(r == 0);
assert_se(!instance);
r = unit_name_to_instance("fooj@unk", &instance);
@@ -324,43 +419,63 @@ static void test_unit_name_escape(void) {
assert_se(streq(r, "ab\\x2b\\x2dc.a-bc\\x40foo.service"));
}
+
+static void test_u_n_t_one(const char *name, const char *expected, int ret) {
+ _cleanup_free_ char *f = NULL;
+
+ assert_se(unit_name_template(name, &f) == ret);
+ printf("got: %s, expected: %s\n", strna(f), strna(expected));
+ assert_se(streq_ptr(f, expected));
+}
+
static void test_unit_name_template(void) {
-#define expect(name, expected) \
- { \
- _cleanup_free_ char *f = NULL; \
- f = unit_name_template(name); \
- assert_se(f); \
- printf("got: %s, expected: %s\n", f, expected); \
- assert_se(streq(f, expected)); \
- }
- expect("foo@bar.service", "foo@.service")
- expect("foo.mount", "foo.mount")
-#undef expect
+ test_u_n_t_one("foo@bar.service", "foo@.service", 0);
+ test_u_n_t_one("foo.mount", NULL, -EINVAL);
}
-static void test_unit_name_is_template(void) {
- assert_se(unit_name_is_template("foo@.service"));
- assert_se(unit_name_is_template("bar@.path"));
+static void test_unit_name_path_unescape_one(const char *name, const char *path, int ret) {
+ _cleanup_free_ char *p = NULL;
+
+ assert_se(unit_name_path_unescape(name, &p) == ret);
+ assert_se(streq_ptr(path, p));
+}
- assert_se(!unit_name_is_template("bar@i.mount"));
- assert_se(!unit_name_is_template("bar@foobbbb.service"));
- assert_se(!unit_name_is_template("barfoo.service"));
+static void test_unit_name_path_unescape(void) {
+
+ test_unit_name_path_unescape_one("foo", "/foo", 0);
+ test_unit_name_path_unescape_one("foo-bar", "/foo/bar", 0);
+ test_unit_name_path_unescape_one("foo-.bar", "/foo/.bar", 0);
+ test_unit_name_path_unescape_one("foo-bar-baz", "/foo/bar/baz", 0);
+ test_unit_name_path_unescape_one("-", "/", 0);
+ test_unit_name_path_unescape_one("--", NULL, -EINVAL);
+ test_unit_name_path_unescape_one("-foo-bar", NULL, -EINVAL);
+ test_unit_name_path_unescape_one("foo--bar", NULL, -EINVAL);
+ test_unit_name_path_unescape_one("foo-bar-", NULL, -EINVAL);
+ test_unit_name_path_unescape_one(".-bar", NULL, -EINVAL);
+ test_unit_name_path_unescape_one("foo-..", NULL, -EINVAL);
+ test_unit_name_path_unescape_one("", NULL, -EINVAL);
}
int main(int argc, char* argv[]) {
int rc = 0;
- test_replacements();
+ test_unit_name_is_valid();
+ test_u_n_r_i();
+ test_u_n_f_p();
+ test_u_n_f_p_i();
+ test_u_n_m();
+ test_u_n_t_p();
TEST_REQ_RUNNING_SYSTEMD(rc = test_unit_printf());
test_unit_instance_is_valid();
test_unit_prefix_is_valid();
test_unit_name_change_suffix();
test_unit_name_build();
- test_unit_name_is_instance();
+ test_slice_name_is_valid();
test_build_subslice();
+ test_build_parent_slice();
test_unit_name_to_instance();
test_unit_name_escape();
test_unit_name_template();
- test_unit_name_is_template();
+ test_unit_name_path_unescape();
return rc;
}
diff --git a/src/test/test-utf8.c b/src/test/test-utf8.c
index befa385754..346f8524c6 100644
--- a/src/test/test-utf8.c
+++ b/src/test/test-utf8.c
@@ -95,7 +95,7 @@ static void test_utf8_escaping_printable(void) {
static void test_utf16_to_utf8(void) {
char *a = NULL;
- const uint16_t utf16[] = { 'a', 0xd800, 'b', 0xdc00, 'c', 0xd801, 0xdc37 };
+ const uint16_t utf16[] = { htole16('a'), htole16(0xd800), htole16('b'), htole16(0xdc00), htole16('c'), htole16(0xd801), htole16(0xdc37) };
const char utf8[] = { 'a', 'b', 'c', 0xf0, 0x90, 0x90, 0xb7, 0 };
a = utf16_to_utf8(utf16, 14);
diff --git a/src/test/test-util.c b/src/test/test-util.c
index 9515a8cbf1..9d5516a18d 100644
--- a/src/test/test-util.c
+++ b/src/test/test-util.c
@@ -31,11 +31,15 @@
#include "util.h"
#include "mkdir.h"
+#include "rm-rf.h"
#include "strv.h"
#include "def.h"
#include "fileio.h"
#include "conf-parser.h"
#include "virt.h"
+#include "process-util.h"
+#include "hostname-util.h"
+#include "signal-util.h"
static void test_streq_ptr(void) {
assert_se(streq_ptr(NULL, NULL));
@@ -416,24 +420,51 @@ static void test_cescape(void) {
static void test_cunescape(void) {
_cleanup_free_ char *unescaped;
- unescaped = cunescape("abc\\\\\\\"\\b\\f\\a\\n\\r\\t\\v\\003\\177\\234\\313\\000\\x00");
+ assert_se(cunescape("abc\\\\\\\"\\b\\f\\a\\n\\r\\t\\v\\003\\177\\234\\313\\000\\x00", 0, &unescaped) < 0);
+ assert_se(cunescape("abc\\\\\\\"\\b\\f\\a\\n\\r\\t\\v\\003\\177\\234\\313\\000\\x00", UNESCAPE_RELAX, &unescaped) >= 0);
assert_se(streq_ptr(unescaped, "abc\\\"\b\f\a\n\r\t\v\003\177\234\313\\000\\x00"));
+ free(unescaped);
+ unescaped = NULL;
/* incomplete sequences */
- unescaped = cunescape("\\x0");
+ assert_se(cunescape("\\x0", 0, &unescaped) < 0);
+ assert_se(cunescape("\\x0", UNESCAPE_RELAX, &unescaped) >= 0);
assert_se(streq_ptr(unescaped, "\\x0"));
+ free(unescaped);
+ unescaped = NULL;
- unescaped = cunescape("\\x");
+ assert_se(cunescape("\\x", 0, &unescaped) < 0);
+ assert_se(cunescape("\\x", UNESCAPE_RELAX, &unescaped) >= 0);
assert_se(streq_ptr(unescaped, "\\x"));
+ free(unescaped);
+ unescaped = NULL;
- unescaped = cunescape("\\");
+ assert_se(cunescape("\\", 0, &unescaped) < 0);
+ assert_se(cunescape("\\", UNESCAPE_RELAX, &unescaped) >= 0);
assert_se(streq_ptr(unescaped, "\\"));
+ free(unescaped);
+ unescaped = NULL;
- unescaped = cunescape("\\11");
+ assert_se(cunescape("\\11", 0, &unescaped) < 0);
+ assert_se(cunescape("\\11", UNESCAPE_RELAX, &unescaped) >= 0);
assert_se(streq_ptr(unescaped, "\\11"));
+ free(unescaped);
+ unescaped = NULL;
- unescaped = cunescape("\\1");
+ assert_se(cunescape("\\1", 0, &unescaped) < 0);
+ assert_se(cunescape("\\1", UNESCAPE_RELAX, &unescaped) >= 0);
assert_se(streq_ptr(unescaped, "\\1"));
+ free(unescaped);
+ unescaped = NULL;
+
+ assert_se(cunescape("\\u0000", 0, &unescaped) < 0);
+ assert_se(cunescape("\\u00DF\\U000000df\\u03a0\\U00000041", UNESCAPE_RELAX, &unescaped) >= 0);
+ assert_se(streq_ptr(unescaped, "ßßΠA"));
+ free(unescaped);
+ unescaped = NULL;
+
+ assert_se(cunescape("\\073", 0, &unescaped) >= 0);
+ assert_se(streq_ptr(unescaped, ";"));
}
static void test_foreach_word(void) {
@@ -498,21 +529,6 @@ static void test_foreach_word_quoted(void) {
true);
}
-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;
@@ -539,6 +555,59 @@ static void test_hostname_is_valid(void) {
assert_se(!hostname_is_valid("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"));
}
+static void test_read_hostname_config(void) {
+ char path[] = "/tmp/hostname.XXXXXX";
+ char *hostname;
+ int fd;
+
+ fd = mkostemp_safe(path, O_RDWR|O_CLOEXEC);
+ assert(fd > 0);
+ close(fd);
+
+ /* simple hostname */
+ write_string_file(path, "foo");
+ assert_se(read_hostname_config(path, &hostname) == 0);
+ assert_se(streq(hostname, "foo"));
+ free(hostname);
+ hostname = NULL;
+
+ /* with comment */
+ write_string_file(path, "# comment\nfoo");
+ assert_se(read_hostname_config(path, &hostname) == 0);
+ assert_se(hostname);
+ assert_se(streq(hostname, "foo"));
+ free(hostname);
+ hostname = NULL;
+
+ /* with comment and extra whitespace */
+ write_string_file(path, "# comment\n\n foo ");
+ assert_se(read_hostname_config(path, &hostname) == 0);
+ assert_se(hostname);
+ assert_se(streq(hostname, "foo"));
+ free(hostname);
+ hostname = NULL;
+
+ /* cleans up name */
+ write_string_file(path, "!foo/bar.com");
+ assert_se(read_hostname_config(path, &hostname) == 0);
+ assert_se(hostname);
+ assert_se(streq(hostname, "foobar.com"));
+ free(hostname);
+ hostname = NULL;
+
+ /* no value set */
+ hostname = (char*) 0x1234;
+ write_string_file(path, "# nothing here\n");
+ assert_se(read_hostname_config(path, &hostname) == -ENOENT);
+ assert_se(hostname == (char*) 0x1234); /* does not touch argument on error */
+
+ /* nonexisting file */
+ assert_se(read_hostname_config("/non/existing", &hostname) == -ENOENT);
+ assert_se(hostname == (char*) 0x1234); /* does not touch argument on error */
+
+ unlink(path);
+}
+
static void test_u64log2(void) {
assert_se(u64log2(0) == 0);
assert_se(u64log2(8) == 3);
@@ -549,69 +618,6 @@ static void test_u64log2(void) {
assert_se(u64log2(1024*1024+5) == 20);
}
-static void test_get_process_comm(void) {
- struct stat st;
- _cleanup_free_ char *a = NULL, *c = NULL, *d = NULL, *f = NULL, *i = NULL, *cwd = NULL, *root = NULL;
- _cleanup_free_ char *env = NULL;
- pid_t e;
- uid_t u;
- gid_t g;
- dev_t h;
- int r;
- pid_t me;
-
- if (stat("/proc/1/comm", &st) == 0) {
- assert_se(get_process_comm(1, &a) >= 0);
- log_info("pid1 comm: '%s'", a);
- } else {
- log_warning("/proc/1/comm does not exist.");
- }
-
- 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: "PID_FMT, 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: "UID_FMT, u);
- assert_se(u == 0);
-
- assert_se(get_process_gid(1, &g) == 0);
- log_info("pid1 gid: "GID_FMT, g);
- assert_se(g == 0);
-
- me = getpid();
-
- r = get_process_cwd(me, &cwd);
- assert_se(r >= 0 || r == -EACCES);
- log_info("pid1 cwd: '%s'", cwd);
-
- r = get_process_root(me, &root);
- assert_se(r >= 0 || r == -EACCES);
- log_info("pid1 root: '%s'", root);
-
- r = get_process_environ(me, &env);
- assert_se(r >= 0 || r == -EACCES);
- log_info("self strlen(environ): '%zu'", strlen(env));
-
- if (!detect_container(NULL))
- assert_se(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;
{
@@ -1017,39 +1023,7 @@ static void test_readlink_and_make_absolute(void) {
free(r);
assert_se(unlink(name_alias) >= 0);
- assert_se(rm_rf_dangerous(tempdir, false, true, false) >= 0);
-}
-
-static void test_read_one_char(void) {
- _cleanup_fclose_ FILE *file = NULL;
- char r;
- bool need_nl;
- char name[] = "/tmp/test-read_one_char.XXXXXX";
- int fd;
-
- fd = mkostemp_safe(name, O_RDWR|O_CLOEXEC);
- assert_se(fd >= 0);
- file = fdopen(fd, "r+");
- assert_se(file);
- assert_se(fputs("c\n", file) >= 0);
- rewind(file);
-
- assert_se(read_one_char(file, &r, 1000000, &need_nl) >= 0);
- assert_se(!need_nl);
- assert_se(r == 'c');
- assert_se(read_one_char(file, &r, 1000000, &need_nl) < 0);
-
- rewind(file);
- assert_se(fputs("foobar\n", file) >= 0);
- rewind(file);
- assert_se(read_one_char(file, &r, 1000000, &need_nl) < 0);
-
- rewind(file);
- assert_se(fputs("\n", file) >= 0);
- rewind(file);
- assert_se(read_one_char(file, &r, 1000000, &need_nl) < 0);
-
- unlink(name);
+ assert_se(rm_rf(tempdir, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
}
static void test_ignore_signals(void) {
@@ -1115,40 +1089,6 @@ static void test_is_symlink(void) {
unlink(name_link);
}
-static void test_pid_is_unwaited(void) {
- pid_t pid;
-
- pid = fork();
- assert_se(pid >= 0);
- if (pid == 0) {
- _exit(EXIT_SUCCESS);
- } else {
- int status;
-
- waitpid(pid, &status, 0);
- assert_se(!pid_is_unwaited(pid));
- }
- assert_se(pid_is_unwaited(getpid()));
- assert_se(!pid_is_unwaited(-1));
-}
-
-static void test_pid_is_alive(void) {
- pid_t pid;
-
- pid = fork();
- assert_se(pid >= 0);
- if (pid == 0) {
- _exit(EXIT_SUCCESS);
- } else {
- int status;
-
- waitpid(pid, &status, 0);
- assert_se(!pid_is_alive(pid));
- }
- assert_se(pid_is_alive(getpid()));
- assert_se(!pid_is_alive(-1));
-}
-
static void test_search_and_fopen(void) {
const char *dirs[] = {"/tmp/foo/bar", "/tmp", NULL};
char name[] = "/tmp/test-search_and_fopen.XXXXXX";
@@ -1274,8 +1214,8 @@ static void test_execute_directory(void) {
assert_se(access("it_works2", F_OK) >= 0);
assert_se(access("failed", F_OK) < 0);
- rm_rf_dangerous(template_lo, false, true, false);
- rm_rf_dangerous(template_hi, false, true, false);
+ (void) rm_rf(template_lo, REMOVE_ROOT|REMOVE_PHYSICAL);
+ (void) rm_rf(template_hi, REMOVE_ROOT|REMOVE_PHYSICAL);
}
static void test_unquote_first_word(void) {
@@ -1283,64 +1223,87 @@ static void test_unquote_first_word(void) {
char *t;
p = original = "foobar waldo";
- assert_se(unquote_first_word(&p, &t, false) > 0);
+ assert_se(unquote_first_word(&p, &t, 0) > 0);
assert_se(streq(t, "foobar"));
free(t);
assert_se(p == original + 7);
- assert_se(unquote_first_word(&p, &t, false) > 0);
+ assert_se(unquote_first_word(&p, &t, 0) > 0);
assert_se(streq(t, "waldo"));
free(t);
assert_se(p == original + 12);
- assert_se(unquote_first_word(&p, &t, false) == 0);
+ assert_se(unquote_first_word(&p, &t, 0) == 0);
assert_se(!t);
assert_se(p == original + 12);
p = original = "\"foobar\" \'waldo\'";
- assert_se(unquote_first_word(&p, &t, false) > 0);
+ assert_se(unquote_first_word(&p, &t, 0) > 0);
assert_se(streq(t, "foobar"));
free(t);
assert_se(p == original + 9);
- assert_se(unquote_first_word(&p, &t, false) > 0);
+ assert_se(unquote_first_word(&p, &t, 0) > 0);
assert_se(streq(t, "waldo"));
free(t);
assert_se(p == original + 16);
- assert_se(unquote_first_word(&p, &t, false) == 0);
+ assert_se(unquote_first_word(&p, &t, 0) == 0);
assert_se(!t);
assert_se(p == original + 16);
p = original = "\"";
- assert_se(unquote_first_word(&p, &t, false) == -EINVAL);
+ assert_se(unquote_first_word(&p, &t, 0) == -EINVAL);
assert_se(p == original + 1);
p = original = "\'";
- assert_se(unquote_first_word(&p, &t, false) == -EINVAL);
+ assert_se(unquote_first_word(&p, &t, 0) == -EINVAL);
assert_se(p == original + 1);
p = original = "\'fooo";
- assert_se(unquote_first_word(&p, &t, false) == -EINVAL);
+ assert_se(unquote_first_word(&p, &t, 0) == -EINVAL);
assert_se(p == original + 5);
p = original = "\'fooo";
- assert_se(unquote_first_word(&p, &t, true) > 0);
+ assert_se(unquote_first_word(&p, &t, UNQUOTE_RELAX) > 0);
assert_se(streq(t, "fooo"));
free(t);
assert_se(p == original + 5);
p = original = "yay\'foo\'bar";
- assert_se(unquote_first_word(&p, &t, false) > 0);
+ assert_se(unquote_first_word(&p, &t, 0) > 0);
assert_se(streq(t, "yayfoobar"));
free(t);
assert_se(p == original + 11);
p = original = " foobar ";
- assert_se(unquote_first_word(&p, &t, false) > 0);
+ assert_se(unquote_first_word(&p, &t, 0) > 0);
assert_se(streq(t, "foobar"));
free(t);
assert_se(p == original + 12);
+
+ p = original = " foo\\ba\\x6ar ";
+ assert_se(unquote_first_word(&p, &t, UNQUOTE_CUNESCAPE) > 0);
+ assert_se(streq(t, "foo\ba\x6ar"));
+ free(t);
+ assert_se(p == original + 13);
+
+ p = original = " foo\\ba\\x6ar ";
+ assert_se(unquote_first_word(&p, &t, 0) > 0);
+ assert_se(streq(t, "foobax6ar"));
+ free(t);
+ assert_se(p == original + 13);
+
+ p = original = " f\\u00f6o \"pi\\U0001F4A9le\" ";
+ assert_se(unquote_first_word(&p, &t, UNQUOTE_CUNESCAPE) > 0);
+ assert_se(streq(t, "föo"));
+ free(t);
+ assert_se(p == original + 13);
+
+ assert_se(unquote_first_word(&p, &t, UNQUOTE_CUNESCAPE) > 0);
+ assert_se(streq(t, "pi\360\237\222\251le"));
+ free(t);
+ assert_se(p == original + 32);
}
static void test_unquote_many_words(void) {
@@ -1348,7 +1311,7 @@ static void test_unquote_many_words(void) {
char *a, *b, *c;
p = original = "foobar waldi piep";
- assert_se(unquote_many_words(&p, &a, &b, &c, NULL) == 3);
+ assert_se(unquote_many_words(&p, 0, &a, &b, &c, NULL) == 3);
assert_se(p == original + 17);
assert_se(streq_ptr(a, "foobar"));
assert_se(streq_ptr(b, "waldi"));
@@ -1358,7 +1321,7 @@ static void test_unquote_many_words(void) {
free(c);
p = original = "'foobar' wa\"ld\"i ";
- assert_se(unquote_many_words(&p, &a, &b, &c, NULL) == 2);
+ assert_se(unquote_many_words(&p, 0, &a, &b, &c, NULL) == 2);
assert_se(p == original + 19);
assert_se(streq_ptr(a, "foobar"));
assert_se(streq_ptr(b, "waldi"));
@@ -1367,31 +1330,31 @@ static void test_unquote_many_words(void) {
free(b);
p = original = "";
- assert_se(unquote_many_words(&p, &a, &b, &c, NULL) == 0);
+ assert_se(unquote_many_words(&p, 0, &a, &b, &c, NULL) == 0);
assert_se(p == original);
assert_se(streq_ptr(a, NULL));
assert_se(streq_ptr(b, NULL));
assert_se(streq_ptr(c, NULL));
p = original = " ";
- assert_se(unquote_many_words(&p, &a, &b, &c, NULL) == 0);
+ assert_se(unquote_many_words(&p, 0, &a, &b, &c, NULL) == 0);
assert_se(p == original+2);
assert_se(streq_ptr(a, NULL));
assert_se(streq_ptr(b, NULL));
assert_se(streq_ptr(c, NULL));
p = original = "foobar";
- assert_se(unquote_many_words(&p, NULL) == 0);
+ assert_se(unquote_many_words(&p, 0, NULL) == 0);
assert_se(p == original);
p = original = "foobar waldi";
- assert_se(unquote_many_words(&p, &a, NULL) == 1);
+ assert_se(unquote_many_words(&p, 0, &a, NULL) == 1);
assert_se(p == original+7);
assert_se(streq_ptr(a, "foobar"));
free(a);
p = original = " foobar ";
- assert_se(unquote_many_words(&p, &a, NULL) == 1);
+ assert_se(unquote_many_words(&p, 0, &a, NULL) == 1);
assert_se(p == original+15);
assert_se(streq_ptr(a, "foobar"));
free(a);
@@ -1512,6 +1475,38 @@ static void test_sparse_write(void) {
test_sparse_write_one(fd, test_e, sizeof(test_e));
}
+static void test_shell_maybe_quote_one(const char *s, const char *expected) {
+ _cleanup_free_ char *r;
+
+ assert_se(r = shell_maybe_quote(s));
+ assert_se(streq(r, expected));
+}
+
+static void test_shell_maybe_quote(void) {
+
+ test_shell_maybe_quote_one("", "");
+ test_shell_maybe_quote_one("\\", "\"\\\\\"");
+ test_shell_maybe_quote_one("\"", "\"\\\"\"");
+ test_shell_maybe_quote_one("foobar", "foobar");
+ test_shell_maybe_quote_one("foo bar", "\"foo bar\"");
+ test_shell_maybe_quote_one("foo \"bar\" waldo", "\"foo \\\"bar\\\" waldo\"");
+ test_shell_maybe_quote_one("foo$bar", "\"foo\\$bar\"");
+}
+
+static void test_parse_mode(void) {
+ mode_t m;
+
+ assert_se(parse_mode("-1", &m) < 0);
+ assert_se(parse_mode("", &m) < 0);
+ assert_se(parse_mode("888", &m) < 0);
+ assert_se(parse_mode("77777", &m) < 0);
+
+ assert_se(parse_mode("544", &m) >= 0 && m == 0544);
+ assert_se(parse_mode("777", &m) >= 0 && m == 0777);
+ assert_se(parse_mode("7777", &m) >= 0 && m == 07777);
+ assert_se(parse_mode("0", &m) >= 0 && m == 0);
+}
+
int main(int argc, char *argv[]) {
log_parse_environment();
log_open();
@@ -1543,11 +1538,10 @@ int main(int argc, char *argv[]) {
test_cunescape();
test_foreach_word();
test_foreach_word_quoted();
- test_default_term_for_tty();
test_memdup_multiply();
test_hostname_is_valid();
+ test_read_hostname_config();
test_u64log2();
- test_get_process_comm();
test_protect_errno();
test_parse_size();
test_config_parse_iec_off();
@@ -1571,13 +1565,10 @@ int main(int argc, char *argv[]) {
test_close_nointr();
test_unlink_noerrno();
test_readlink_and_make_absolute();
- test_read_one_char();
test_ignore_signals();
test_strshorten();
test_strjoina();
test_is_symlink();
- test_pid_is_unwaited();
- test_pid_is_alive();
test_search_and_fopen();
test_search_and_fopen_nulstr();
test_glob_exists();
@@ -1589,6 +1580,8 @@ int main(int argc, char *argv[]) {
test_same_fd();
test_uid_ptr();
test_sparse_write();
+ test_shell_maybe_quote();
+ test_parse_mode();
return 0;
}
diff --git a/src/test/test-watchdog.c b/src/test/test-watchdog.c
index f6bb045c3c..2e5d0c3aae 100644
--- a/src/test/test-watchdog.c
+++ b/src/test/test-watchdog.c
@@ -20,7 +20,6 @@
***/
#include <unistd.h>
-#include <string.h>
#include "watchdog.h"
#include "log.h"
diff --git a/src/timedate/timedatectl.c b/src/timedate/timedatectl.c
index 4d89886736..61b6e765c7 100644
--- a/src/timedate/timedatectl.c
+++ b/src/timedate/timedatectl.c
@@ -22,11 +22,8 @@
#include <stdlib.h>
#include <stdbool.h>
-#include <unistd.h>
#include <getopt.h>
#include <locale.h>
-#include <string.h>
-#include <sys/timex.h>
#include "sd-bus.h"
#include "bus-util.h"
@@ -36,7 +33,7 @@
#include "build.h"
#include "strv.h"
#include "pager.h"
-#include "time-dst.h"
+#include "terminal-util.h"
static bool arg_no_pager = false;
static bool arg_ask_password = true;
@@ -76,51 +73,35 @@ typedef struct StatusInfo {
bool ntp_synced;
} StatusInfo;
-static const char *jump_str(int delta_minutes, char *s, size_t size) {
- if (delta_minutes == 60)
- return "one hour forward";
- if (delta_minutes == -60)
- return "one hour backwards";
- if (delta_minutes < 0) {
- snprintf(s, size, "%i minutes backwards", -delta_minutes);
- return s;
- }
- if (delta_minutes > 0) {
- snprintf(s, size, "%i minutes forward", delta_minutes);
- return s;
- }
- return "";
-}
-
static void print_status_info(const StatusInfo *i) {
char a[FORMAT_TIMESTAMP_MAX];
- char b[FORMAT_TIMESTAMP_MAX];
- char s[32];
struct tm tm;
time_t sec;
bool have_time = false;
- _cleanup_free_ char *zc = NULL, *zn = NULL;
- time_t t, tc, tn;
- int dn = 0;
- bool is_dstc = false, is_dstn = false;
+ const char *old_tz = NULL, *tz;
int r;
assert(i);
- /* Enforce the values of /etc/localtime */
- if (getenv("TZ")) {
- fprintf(stderr, "Warning: Ignoring the TZ variable. Reading the system's time zone setting only.\n\n");
- unsetenv("TZ");
- }
+ /* Save the old $TZ */
+ tz = getenv("TZ");
+ if (tz)
+ old_tz = strdupa(tz);
+
+ /* Set the new $TZ */
+ if (setenv("TZ", i->timezone, true) < 0)
+ log_warning_errno(errno, "Failed to set TZ environment variable, ignoring: %m");
+ else
+ tzset();
if (i->time != 0) {
sec = (time_t) (i->time / USEC_PER_SEC);
have_time = true;
- } else if (arg_transport == BUS_TRANSPORT_LOCAL) {
+ } else if (IN_SET(arg_transport, BUS_TRANSPORT_LOCAL, BUS_TRANSPORT_MACHINE)) {
sec = time(NULL);
have_time = true;
} else
- fprintf(stderr, "Warning: Could not get time from timedated and not operating locally.\n\n");
+ log_warning("Could not get time from timedated and not operating locally, ignoring.");
if (have_time) {
xstrftime(a, "%a %Y-%m-%d %H:%M:%S %Z", localtime_r(&sec, &tm));
@@ -136,7 +117,7 @@ static void print_status_info(const StatusInfo *i) {
if (i->rtc_time > 0) {
time_t rtc_sec;
- rtc_sec = (time_t)(i->rtc_time / USEC_PER_SEC);
+ rtc_sec = (time_t) (i->rtc_time / USEC_PER_SEC);
xstrftime(a, "%a %Y-%m-%d %H:%M:%S", gmtime_r(&rtc_sec, &tm));
printf(" RTC time: %.*s\n", (int) sizeof(a), a);
} else
@@ -145,8 +126,18 @@ static void print_status_info(const StatusInfo *i) {
if (have_time)
xstrftime(a, "%Z, %z", localtime_r(&sec, &tm));
+ /* Restore the $TZ */
+ if (old_tz)
+ r = setenv("TZ", old_tz, true);
+ else
+ r = unsetenv("TZ");
+ if (r < 0)
+ log_warning_errno(errno, "Failed to set TZ environment variable, ignoring: %m");
+ else
+ tzset();
+
printf(" Time zone: %s (%.*s)\n"
- " NTP enabled: %s\n"
+ " Network time on: %s\n"
"NTP synchronized: %s\n"
" RTC in local TZ: %s\n",
strna(i->timezone), (int) sizeof(a), have_time ? a : "n/a",
@@ -154,40 +145,6 @@ static void print_status_info(const StatusInfo *i) {
yes_no(i->ntp_synced),
yes_no(i->rtc_local));
- if (have_time) {
- r = time_get_dst(sec, "/etc/localtime",
- &tc, &zc, &is_dstc,
- &tn, &dn, &zn, &is_dstn);
- if (r < 0)
- printf(" DST active: %s\n", "n/a");
- else {
- printf(" DST active: %s\n", yes_no(is_dstc));
-
- t = tc - 1;
- xstrftime(a, "%a %Y-%m-%d %H:%M:%S %Z", localtime_r(&t, &tm));
-
- xstrftime(b, "%a %Y-%m-%d %H:%M:%S %Z", localtime_r(&tc, &tm));
- printf(" Last DST change: DST %s at\n"
- " %.*s\n"
- " %.*s\n",
- is_dstc ? "began" : "ended",
- (int) sizeof(a), a,
- (int) sizeof(b), b);
-
- t = tn - 1;
- xstrftime(a, "%a %Y-%m-%d %H:%M:%S %Z", localtime_r(&t, &tm));
- xstrftime(b, "%a %Y-%m-%d %H:%M:%S %Z", localtime_r(&tn, &tm));
- printf(" Next DST change: DST %s (the clock jumps %s) at\n"
- " %.*s\n"
- " %.*s\n",
- is_dstn ? "begins" : "ends",
- jump_str(dn, s, sizeof(s)),
- (int) sizeof(a), a,
- (int) sizeof(b), b);
- }
- } else
- printf(" DST active: %s\n", yes_no(is_dstc));
-
if (i->rtc_local)
fputs("\n" ANSI_HIGHLIGHT_ON
"Warning: The system is configured to read the RTC time in the local time zone. This\n"
@@ -375,7 +332,7 @@ static void help(void) {
" set-timezone ZONE Set system time zone\n"
" list-timezones Show known time zones\n"
" set-local-rtc BOOL Control whether RTC is in local time\n"
- " set-ntp BOOL Control whether NTP is enabled\n",
+ " set-ntp BOOL Enable or disable network time synchronization\n",
program_invocation_short_name);
}
diff --git a/src/timedate/timedated.c b/src/timedate/timedated.c
index 753c3d1d65..4e8ae94717 100644
--- a/src/timedate/timedated.c
+++ b/src/timedate/timedated.c
@@ -23,7 +23,6 @@
#include <string.h>
#include <unistd.h>
-#include "sd-id128.h"
#include "sd-messages.h"
#include "sd-event.h"
#include "sd-bus.h"
@@ -32,10 +31,8 @@
#include "strv.h"
#include "def.h"
#include "clock-util.h"
-#include "conf-files.h"
#include "path-util.h"
#include "fileio-label.h"
-#include "label.h"
#include "bus-util.h"
#include "bus-error.h"
#include "bus-common-errors.h"
@@ -46,7 +43,7 @@
#define NULL_ADJTIME_LOCAL "0.0 0 0\n0\nLOCAL\n"
static BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map timedated_errors[] = {
- SD_BUS_ERROR_MAP("org.freedesktop.timedate1.NoNTPSupport", ENOTSUP),
+ SD_BUS_ERROR_MAP("org.freedesktop.timedate1.NoNTPSupport", EOPNOTSUPP),
SD_BUS_ERROR_MAP_END
};
@@ -90,12 +87,9 @@ static int context_read_data(Context *c) {
c->zone = strdup(e);
if (!c->zone)
return log_oom();
-
- goto have_timezone;
}
}
-have_timezone:
if (isempty(c->zone)) {
free(c->zone);
c->zone = NULL;
@@ -188,7 +182,7 @@ static int context_write_data_local_rtc(Context *c) {
static int context_read_ntp(Context *c, sd_bus *bus) {
_cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
- sd_bus_message *reply = NULL;
+ _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
const char *s;
int r;
@@ -225,38 +219,23 @@ static int context_read_ntp(Context *c, sd_bus *bus) {
return 0;
}
-static int context_start_ntp(Context *c, sd_bus *bus, sd_bus_error *error) {
+static int context_start_ntp(sd_bus *bus, sd_bus_error *error, bool enabled) {
int r;
- assert(c);
assert(bus);
assert(error);
- if (c->use_ntp)
- r = sd_bus_call_method(
- bus,
- "org.freedesktop.systemd1",
- "/org/freedesktop/systemd1",
- "org.freedesktop.systemd1.Manager",
- "StartUnit",
- error,
- NULL,
- "ss",
- "systemd-timesyncd.service",
- "replace");
- else
- r = sd_bus_call_method(
- bus,
- "org.freedesktop.systemd1",
- "/org/freedesktop/systemd1",
- "org.freedesktop.systemd1.Manager",
- "StopUnit",
- error,
- NULL,
- "ss",
- "systemd-timesyncd.service",
- "replace");
-
+ r = sd_bus_call_method(
+ bus,
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ enabled ? "StartUnit" : "StopUnit",
+ error,
+ NULL,
+ "ss",
+ "systemd-timesyncd.service",
+ "replace");
if (r < 0) {
if (sd_bus_error_has_name(error, SD_BUS_ERROR_FILE_NOT_FOUND) ||
sd_bus_error_has_name(error, "org.freedesktop.systemd1.LoadFailed") ||
@@ -269,14 +248,13 @@ static int context_start_ntp(Context *c, sd_bus *bus, sd_bus_error *error) {
return 0;
}
-static int context_enable_ntp(Context*c, sd_bus *bus, sd_bus_error *error) {
+static int context_enable_ntp(sd_bus *bus, sd_bus_error *error, bool enabled) {
int r;
- assert(c);
assert(bus);
assert(error);
- if (c->use_ntp)
+ if (enabled)
r = sd_bus_call_method(
bus,
"org.freedesktop.systemd1",
@@ -345,7 +323,7 @@ static int property_get_rtc_time(
log_debug("/dev/rtc not found.");
t = 0; /* no RTC found */
} else if (r < 0)
- return sd_bus_error_set_errnof(error, r, "Failed to read RTC: %s", strerror(-r));
+ return sd_bus_error_set_errnof(error, r, "Failed to read RTC: %m");
else
t = (usec_t) timegm(&tm) * USEC_PER_SEC;
@@ -376,14 +354,13 @@ static int property_get_ntp_sync(
return sd_bus_message_append(reply, "b", ntp_synced());
}
-static int method_set_timezone(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
+static int method_set_timezone(sd_bus_message *m, void *userdata, sd_bus_error *error) {
Context *c = userdata;
const char *z;
int interactive;
char *t;
int r;
- assert(bus);
assert(m);
assert(c);
@@ -397,7 +374,14 @@ static int method_set_timezone(sd_bus *bus, sd_bus_message *m, void *userdata, s
if (streq_ptr(z, c->zone))
return sd_bus_reply_method_return(m, NULL);
- r = bus_verify_polkit_async(m, CAP_SYS_TIME, "org.freedesktop.timedate1.set-timezone", interactive, &c->polkit_registry, error);
+ r = bus_verify_polkit_async(
+ m,
+ CAP_SYS_TIME,
+ "org.freedesktop.timedate1.set-timezone",
+ interactive,
+ UID_INVALID,
+ &c->polkit_registry,
+ error);
if (r < 0)
return r;
if (r == 0)
@@ -414,7 +398,7 @@ static int method_set_timezone(sd_bus *bus, sd_bus_message *m, void *userdata, s
r = context_write_data_timezone(c);
if (r < 0) {
log_error_errno(r, "Failed to set time zone: %m");
- return sd_bus_error_set_errnof(error, r, "Failed to set time zone: %s", strerror(-r));
+ return sd_bus_error_set_errnof(error, r, "Failed to set time zone: %m");
}
/* 2. Tell the kernel our timezone */
@@ -436,18 +420,17 @@ static int method_set_timezone(sd_bus *bus, sd_bus_message *m, void *userdata, s
LOG_MESSAGE("Changed time zone to '%s'.", c->zone),
NULL);
- sd_bus_emit_properties_changed(bus, "/org/freedesktop/timedate1", "org.freedesktop.timedate1", "Timezone", NULL);
+ (void) sd_bus_emit_properties_changed(sd_bus_message_get_bus(m), "/org/freedesktop/timedate1", "org.freedesktop.timedate1", "Timezone", NULL);
return sd_bus_reply_method_return(m, NULL);
}
-static int method_set_local_rtc(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
+static int method_set_local_rtc(sd_bus_message *m, void *userdata, sd_bus_error *error) {
int lrtc, fix_system, interactive;
Context *c = userdata;
struct timespec ts;
int r;
- assert(bus);
assert(m);
assert(c);
@@ -458,7 +441,14 @@ static int method_set_local_rtc(sd_bus *bus, sd_bus_message *m, void *userdata,
if (lrtc == c->local_rtc)
return sd_bus_reply_method_return(m, NULL);
- r = bus_verify_polkit_async(m, CAP_SYS_TIME, "org.freedesktop.timedate1.set-local-rtc", interactive, &c->polkit_registry, error);
+ r = bus_verify_polkit_async(
+ m,
+ CAP_SYS_TIME,
+ "org.freedesktop.timedate1.set-local-rtc",
+ interactive,
+ UID_INVALID,
+ &c->polkit_registry,
+ error);
if (r < 0)
return r;
if (r == 0)
@@ -470,7 +460,7 @@ static int method_set_local_rtc(sd_bus *bus, sd_bus_message *m, void *userdata,
r = context_write_data_local_rtc(c);
if (r < 0) {
log_error_errno(r, "Failed to set RTC to local/UTC: %m");
- return sd_bus_error_set_errnof(error, r, "Failed to set RTC to local/UTC: %s", strerror(-r));
+ return sd_bus_error_set_errnof(error, r, "Failed to set RTC to local/UTC: %m");
}
/* 2. Tell the kernel our timezone */
@@ -519,26 +509,29 @@ static int method_set_local_rtc(sd_bus *bus, sd_bus_message *m, void *userdata,
log_info("RTC configured to %s time.", c->local_rtc ? "local" : "UTC");
- sd_bus_emit_properties_changed(bus, "/org/freedesktop/timedate1", "org.freedesktop.timedate1", "LocalRTC", NULL);
+ (void) sd_bus_emit_properties_changed(sd_bus_message_get_bus(m), "/org/freedesktop/timedate1", "org.freedesktop.timedate1", "LocalRTC", NULL);
return sd_bus_reply_method_return(m, NULL);
}
-static int method_set_time(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
+static int method_set_time(sd_bus_message *m, void *userdata, sd_bus_error *error) {
int relative, interactive;
Context *c = userdata;
int64_t utc;
struct timespec ts;
+ usec_t start;
struct tm* tm;
int r;
- assert(bus);
assert(m);
assert(c);
if (c->use_ntp)
return sd_bus_error_setf(error, BUS_ERROR_AUTOMATIC_TIME_SYNC_ENABLED, "Automatic time synchronization is enabled");
+ /* this only gets used if dbus does not provide a timestamp */
+ start = now(CLOCK_MONOTONIC);
+
r = sd_bus_message_read(m, "xbb", &utc, &relative, &interactive);
if (r < 0)
return r;
@@ -563,12 +556,27 @@ static int method_set_time(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bu
} else
timespec_store(&ts, (usec_t) utc);
- r = bus_verify_polkit_async(m, CAP_SYS_TIME, "org.freedesktop.timedate1.set-time", interactive, &c->polkit_registry, error);
+ r = bus_verify_polkit_async(
+ m,
+ CAP_SYS_TIME,
+ "org.freedesktop.timedate1.set-time",
+ interactive,
+ UID_INVALID,
+ &c->polkit_registry,
+ error);
if (r < 0)
return r;
if (r == 0)
return 1;
+ /* adjust ts for time spent in program */
+ r = sd_bus_message_get_monotonic_usec(m, &start);
+ /* when sd_bus_message_get_monotonic_usec() returns -ENODATA it does not modify &start */
+ if (r < 0 && r != -ENODATA)
+ return r;
+
+ timespec_store(&ts, timespec_load(&ts) + (now(CLOCK_MONOTONIC) - start));
+
/* Set system clock */
if (clock_settime(CLOCK_REALTIME, &ts) < 0) {
log_error_errno(errno, "Failed to set local time: %m");
@@ -591,37 +599,46 @@ static int method_set_time(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bu
return sd_bus_reply_method_return(m, NULL);
}
-static int method_set_ntp(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
- int ntp, interactive;
+static int method_set_ntp(sd_bus_message *m, void *userdata, sd_bus_error *error) {
+ int enabled, interactive;
Context *c = userdata;
int r;
- r = sd_bus_message_read(m, "bb", &ntp, &interactive);
+ assert(m);
+ assert(c);
+
+ r = sd_bus_message_read(m, "bb", &enabled, &interactive);
if (r < 0)
return r;
- if ((bool)ntp == c->use_ntp)
+ if ((bool)enabled == c->use_ntp)
return sd_bus_reply_method_return(m, NULL);
- r = bus_verify_polkit_async(m, CAP_SYS_TIME, "org.freedesktop.timedate1.set-ntp", interactive, &c->polkit_registry, error);
+ r = bus_verify_polkit_async(
+ m,
+ CAP_SYS_TIME,
+ "org.freedesktop.timedate1.set-ntp",
+ interactive,
+ UID_INVALID,
+ &c->polkit_registry,
+ error);
if (r < 0)
return r;
if (r == 0)
return 1;
- c->use_ntp = ntp;
-
- r = context_enable_ntp(c, bus, error);
+ r = context_enable_ntp(sd_bus_message_get_bus(m), error, enabled);
if (r < 0)
return r;
- r = context_start_ntp(c, bus, error);
+ r = context_start_ntp(sd_bus_message_get_bus(m), error, enabled);
if (r < 0)
return r;
- log_info("Set NTP to %s", c->use_ntp ? "enabled" : "disabled");
+ c->use_ntp = enabled;
+ log_info("Set NTP to %s", enabled ? "enabled" : "disabled");
- sd_bus_emit_properties_changed(bus, "/org/freedesktop/timedate1", "org.freedesktop.timedate1", "NTP", NULL);
+ (void) sd_bus_emit_properties_changed(sd_bus_message_get_bus(m), "/org/freedesktop/timedate1", "org.freedesktop.timedate1", "NTP", NULL);
return sd_bus_reply_method_return(m, NULL);
}
@@ -702,6 +719,8 @@ int main(int argc, char *argv[]) {
if (r < 0)
goto finish;
+ (void) sd_bus_negotiate_timestamp(bus, true);
+
r = context_read_data(&context);
if (r < 0) {
log_error_errno(r, "Failed to read time zone data: %m");
diff --git a/src/timesync/timesyncd-conf.c b/src/timesync/timesyncd-conf.c
index be1f4bb151..df4d89a620 100644
--- a/src/timesync/timesyncd-conf.c
+++ b/src/timesync/timesyncd-conf.c
@@ -19,7 +19,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "in-addr-util.h"
#include "timesyncd-manager.h"
#include "timesyncd-server.h"
diff --git a/src/timesync/timesyncd-manager.c b/src/timesync/timesyncd-manager.c
index 73ac7eecbf..88e9cf98ed 100644
--- a/src/timesync/timesyncd-manager.c
+++ b/src/timesync/timesyncd-manager.c
@@ -21,21 +21,15 @@
#include <stdlib.h>
#include <errno.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <string.h>
#include <time.h>
#include <math.h>
-#include <arpa/inet.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <sys/timerfd.h>
#include <sys/timex.h>
#include <sys/socket.h>
#include <resolv.h>
-#include <sys/prctl.h>
#include <sys/types.h>
-#include <grp.h>
#include "missing.h"
#include "util.h"
@@ -45,13 +39,8 @@
#include "list.h"
#include "ratelimit.h"
#include "strv.h"
-#include "conf-parser.h"
#include "sd-daemon.h"
-#include "event-util.h"
#include "network-util.h"
-#include "clock-util.h"
-#include "capability.h"
-#include "mkdir.h"
#include "timesyncd-conf.h"
#include "timesyncd-manager.h"
#include "time-util.h"
@@ -680,6 +669,16 @@ static int manager_receive_response(sd_event_source *source, int fd, uint32_t re
m->poll_interval_usec / USEC_PER_SEC, offset, delay, m->samples_jitter, m->drift_ppm,
spike ? " (ignored)" : "");
+ if (!m->good) {
+ _cleanup_free_ char *pretty = NULL;
+
+ m->good = true;
+
+ server_address_pretty(m->current_server_address, &pretty);
+ log_info("Synchronized to time server %s (%s).", strna(pretty), m->current_server_name->string);
+ sd_notifyf(false, "STATUS=Synchronized to time server %s (%s).", strna(pretty), m->current_server_name->string);
+ }
+
r = manager_arm_timer(m, m->poll_interval_usec);
if (r < 0)
return log_error_errno(r, "Failed to rearm timer: %m");
@@ -735,13 +734,14 @@ static int manager_begin(Manager *m) {
assert_return(m->current_server_name, -EHOSTUNREACH);
assert_return(m->current_server_address, -EHOSTUNREACH);
+ m->good = false;
m->missed_replies = NTP_MAX_MISSED_REPLIES;
if (m->poll_interval_usec == 0)
m->poll_interval_usec = NTP_POLL_INTERVAL_MIN_SEC * USEC_PER_SEC;
server_address_pretty(m->current_server_address, &pretty);
- log_debug("Using NTP server %s (%s).", strna(pretty), m->current_server_name->string);
- sd_notifyf(false, "STATUS=Using Time Server %s (%s).", strna(pretty), m->current_server_name->string);
+ log_debug("Connecting to time server %s (%s).", strna(pretty), m->current_server_name->string);
+ sd_notifyf(false, "STATUS=Connecting to time server %s (%s).", strna(pretty), m->current_server_name->string);
r = manager_clock_watch_setup(m);
if (r < 0)
diff --git a/src/timesync/timesyncd-manager.h b/src/timesync/timesyncd-manager.h
index c7efdc5dfb..090b2fcba8 100644
--- a/src/timesync/timesyncd-manager.h
+++ b/src/timesync/timesyncd-manager.h
@@ -25,7 +25,6 @@
#include "sd-resolve.h"
#include "sd-network.h"
#include "list.h"
-#include "socket-util.h"
#include "ratelimit.h"
typedef struct Manager Manager;
@@ -56,6 +55,7 @@ struct Manager {
int missed_replies;
uint64_t packet_count;
sd_event_source *event_timeout;
+ bool good;
/* last sent packet */
struct timespec trans_time_mon;
diff --git a/src/timesync/timesyncd-server.h b/src/timesync/timesyncd-server.h
index 243b44a0eb..18c44445e1 100644
--- a/src/timesync/timesyncd-server.h
+++ b/src/timesync/timesyncd-server.h
@@ -59,7 +59,7 @@ struct ServerName {
int server_address_new(ServerName *n, ServerAddress **ret, const union sockaddr_union *sockaddr, socklen_t socklen);
ServerAddress* server_address_free(ServerAddress *a);
static inline int server_address_pretty(ServerAddress *a, char **pretty) {
- return sockaddr_pretty(&a->sockaddr.sa, a->socklen, true, pretty);
+ return sockaddr_pretty(&a->sockaddr.sa, a->socklen, true, true, pretty);
}
int server_name_new(Manager *m, ServerName **ret, ServerType type,const char *string);
diff --git a/src/timesync/timesyncd.c b/src/timesync/timesyncd.c
index 2a73dac033..d69129ee03 100644
--- a/src/timesync/timesyncd.c
+++ b/src/timesync/timesyncd.c
@@ -21,10 +21,10 @@
#include "sd-event.h"
#include "sd-daemon.h"
-#include "mkdir.h"
#include "capability.h"
#include "clock-util.h"
#include "network-util.h"
+#include "signal-util.h"
#include "timesyncd-manager.h"
#include "timesyncd-conf.h"
diff --git a/src/timesync/timesyncd.conf.in b/src/timesync/timesyncd.conf.in
index fc3c6c49cf..b6a2ada273 100644
--- a/src/timesync/timesyncd.conf.in
+++ b/src/timesync/timesyncd.conf.in
@@ -5,10 +5,11 @@
# the Free Software Foundation; either version 2.1 of the License, or
# (at your option) any later version.
#
-# You can override the directives in this file by creating files in
-# /etc/systemd/timesyncd.conf.d/*.conf.
+# Entries in this file show the compile time defaults.
+# You can change settings by editing this file.
+# Defaults can be restored by simply deleting this file.
#
-# See timesyncd.conf(5) for details
+# See timesyncd.conf(5) for details.
[Time]
#NTP=
diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c
index c948d4d218..f7dad8491e 100644
--- a/src/tmpfiles/tmpfiles.c
+++ b/src/tmpfiles/tmpfiles.c
@@ -26,8 +26,6 @@
#include <string.h>
#include <limits.h>
#include <dirent.h>
-#include <grp.h>
-#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
@@ -37,9 +35,8 @@
#include <glob.h>
#include <fnmatch.h>
#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/param.h>
#include <sys/xattr.h>
+#include <linux/fs.h>
#include "log.h"
#include "util.h"
@@ -55,9 +52,11 @@
#include "specifier.h"
#include "build.h"
#include "copy.h"
+#include "rm-rf.h"
#include "selinux-util.h"
#include "btrfs-util.h"
#include "acl-util.h"
+#include "formats-util.h"
/* This reads all files listed in /etc/tmpfiles.d/?*.conf and creates
* them in the file system. This is intended to be used to create
@@ -78,18 +77,20 @@ typedef enum ItemType {
COPY_FILES = 'C',
/* These ones take globs */
+ WRITE_FILE = 'w',
SET_XATTR = 't',
RECURSIVE_SET_XATTR = 'T',
SET_ACL = 'a',
RECURSIVE_SET_ACL = 'A',
- WRITE_FILE = 'w',
+ SET_ATTRIBUTE = 'h',
+ RECURSIVE_SET_ATTRIBUTE = 'H',
IGNORE_PATH = 'x',
IGNORE_DIRECTORY_PATH = 'X',
REMOVE_PATH = 'r',
RECURSIVE_REMOVE_PATH = 'R',
- ADJUST_MODE = 'm', /* legacy, 'z' is identical to this */
RELABEL_PATH = 'z',
RECURSIVE_RELABEL_PATH = 'Z',
+ ADJUST_MODE = 'm', /* legacy, 'z' is identical to this */
} ItemType;
typedef struct Item {
@@ -108,12 +109,15 @@ typedef struct Item {
usec_t age;
dev_t major_minor;
+ unsigned attribute_value;
+ unsigned attribute_mask;
bool uid_set:1;
bool gid_set:1;
bool mode_set:1;
bool age_set:1;
bool mask_perms:1;
+ bool attribute_set:1;
bool keep_first_level:1;
@@ -141,9 +145,17 @@ static const char conf_file_dirs[] = CONF_DIRS_NULSTR("tmpfiles");
#define MAX_DEPTH 256
-static Hashmap *items = NULL, *globs = NULL;
+static OrderedHashmap *items = NULL, *globs = NULL;
static Set *unix_sockets = NULL;
+static const Specifier specifier_table[] = {
+ { 'm', specifier_machine_id, NULL },
+ { 'b', specifier_boot_id, NULL },
+ { 'H', specifier_host_name, NULL },
+ { 'v', specifier_kernel_release, NULL },
+ {}
+};
+
static bool needs_glob(ItemType t) {
return IN_SET(t,
WRITE_FILE,
@@ -157,7 +169,9 @@ static bool needs_glob(ItemType t) {
SET_XATTR,
RECURSIVE_SET_XATTR,
SET_ACL,
- RECURSIVE_SET_ACL);
+ RECURSIVE_SET_ACL,
+ SET_ATTRIBUTE,
+ RECURSIVE_SET_ATTRIBUTE);
}
static bool takes_ownership(ItemType t) {
@@ -172,7 +186,6 @@ static bool takes_ownership(ItemType t) {
CREATE_CHAR_DEVICE,
CREATE_BLOCK_DEVICE,
COPY_FILES,
-
WRITE_FILE,
IGNORE_PATH,
IGNORE_DIRECTORY_PATH,
@@ -180,11 +193,11 @@ static bool takes_ownership(ItemType t) {
RECURSIVE_REMOVE_PATH);
}
-static struct Item* find_glob(Hashmap *h, const char *match) {
+static struct Item* find_glob(OrderedHashmap *h, const char *match) {
ItemArray *j;
Iterator i;
- HASHMAP_FOREACH(j, h, i) {
+ ORDERED_HASHMAP_FOREACH(j, h, i) {
unsigned n;
for (n = 0; n < j->count; n++) {
@@ -312,16 +325,16 @@ static DIR* xopendirat_nomod(int dirfd, const char *path) {
DIR *dir;
dir = xopendirat(dirfd, path, O_NOFOLLOW|O_NOATIME);
- if (!dir) {
- log_debug_errno(errno, "Cannot open %sdirectory \"%s\": %m",
- dirfd == AT_FDCWD ? "" : "sub", path);
- if (errno == EPERM) {
- dir = xopendirat(dirfd, path, O_NOFOLLOW);
- if (!dir)
- log_debug_errno(errno, "Cannot open %sdirectory \"%s\": %m",
- dirfd == AT_FDCWD ? "" : "sub", path);
- }
- }
+ if (dir)
+ return dir;
+
+ log_debug_errno(errno, "Cannot open %sdirectory \"%s\": %m", dirfd == AT_FDCWD ? "" : "sub", path);
+ if (errno != EPERM)
+ return NULL;
+
+ dir = xopendirat(dirfd, path, O_NOFOLLOW);
+ if (!dir)
+ log_debug_errno(errno, "Cannot open %sdirectory \"%s\": %m", dirfd == AT_FDCWD ? "" : "sub", path);
return dir;
}
@@ -395,7 +408,7 @@ static int dir_cleanup(
}
/* Is there an item configured for this path? */
- if (hashmap_get(items, sub_path)) {
+ if (ordered_hashmap_get(items, sub_path)) {
log_debug("Ignoring \"%s\": a separate entry exists.", sub_path);
continue;
}
@@ -570,56 +583,73 @@ finish:
}
static int path_set_perms(Item *i, const char *path) {
+ _cleanup_close_ int fd = -1;
struct stat st;
- bool st_valid;
assert(i);
assert(path);
- st_valid = stat(path, &st) == 0;
+ /* We open the file with O_PATH here, to make the operation
+ * somewhat atomic. Also there's unfortunately no fchmodat()
+ * with AT_SYMLINK_NOFOLLOW, hence we emulate it here via
+ * O_PATH. */
- /* not using i->path directly because it may be a glob */
- if (i->mode_set) {
- mode_t m = i->mode;
+ fd = open(path, O_RDONLY|O_NOFOLLOW|O_CLOEXEC|O_PATH|O_NOATIME);
+ if (fd < 0)
+ return log_error_errno(errno, "Adjusting owner and mode for %s failed: %m", path);
- if (i->mask_perms && st_valid) {
- if (!(st.st_mode & 0111))
- m &= ~0111;
- if (!(st.st_mode & 0222))
+ if (fstatat(fd, "", &st, AT_EMPTY_PATH) < 0)
+ return log_error_errno(errno, "Failed to fstat() file %s: %m", path);
+
+ if (S_ISLNK(st.st_mode))
+ log_debug("Skipping mode an owner fix for symlink %s.", path);
+ else {
+ char fn[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
+ xsprintf(fn, "/proc/self/fd/%i", fd);
+
+ /* not using i->path directly because it may be a glob */
+ if (i->mode_set) {
+ mode_t m = i->mode;
+
+ if (i->mask_perms) {
+ if (!(st.st_mode & 0111))
+ m &= ~0111;
+ if (!(st.st_mode & 0222))
m &= ~0222;
- if (!(st.st_mode & 0444))
- m &= ~0444;
- if (!S_ISDIR(st.st_mode))
- m &= ~07000; /* remove sticky/sgid/suid bit, unless directory */
- }
+ if (!(st.st_mode & 0444))
+ m &= ~0444;
+ if (!S_ISDIR(st.st_mode))
+ m &= ~07000; /* remove sticky/sgid/suid bit, unless directory */
+ }
- if (st_valid && m == (st.st_mode & 07777))
- log_debug("\"%s\" has right mode %o", path, st.st_mode);
- else {
- log_debug("chmod \"%s\" to mode %o", path, m);
- if (chmod(path, m) < 0)
- return log_error_errno(errno, "chmod(%s) failed: %m", path);
+ if (m == (st.st_mode & 07777))
+ log_debug("\"%s\" has right mode %o", path, st.st_mode);
+ else {
+ log_debug("chmod \"%s\" to mode %o", path, m);
+ if (chmod(fn, m) < 0)
+ return log_error_errno(errno, "chmod(%s) failed: %m", path);
+ }
}
- }
-
- if ((!st_valid || i->uid != st.st_uid || i->gid != st.st_gid) &&
- (i->uid_set || i->gid_set)) {
- log_debug("chown \"%s\" to "UID_FMT"."GID_FMT,
- path,
- i->uid_set ? i->uid : UID_INVALID,
- i->gid_set ? i->gid : GID_INVALID);
- if (chown(path,
- i->uid_set ? i->uid : UID_INVALID,
- i->gid_set ? i->gid : GID_INVALID) < 0)
+ if ((i->uid != st.st_uid || i->gid != st.st_gid) &&
+ (i->uid_set || i->gid_set)) {
+ log_debug("chown \"%s\" to "UID_FMT"."GID_FMT,
+ path,
+ i->uid_set ? i->uid : UID_INVALID,
+ i->gid_set ? i->gid : GID_INVALID);
+ if (chown(fn,
+ i->uid_set ? i->uid : UID_INVALID,
+ i->gid_set ? i->gid : GID_INVALID) < 0)
return log_error_errno(errno, "chown(%s) failed: %m", path);
+ }
}
+ fd = safe_close(fd);
+
return label_fix(path, false, false);
}
-static int get_xattrs_from_arg(Item *i) {
- char *xattr;
+static int parse_xattrs_from_arg(Item *i) {
const char *p;
int r;
@@ -628,35 +658,37 @@ static int get_xattrs_from_arg(Item *i) {
p = i->argument;
- while ((r = unquote_first_word(&p, &xattr, false)) > 0) {
- _cleanup_free_ char *tmp = NULL, *name = NULL,
- *value = NULL, *value2 = NULL, *_xattr = xattr;
+ for (;;) {
+ _cleanup_free_ char *name = NULL, *value = NULL, *xattr = NULL, *xattr_replaced = NULL;
+
+ r = unquote_first_word(&p, &xattr, UNQUOTE_CUNESCAPE);
+ if (r < 0)
+ log_warning_errno(r, "Failed to parse extended attribute '%s', ignoring: %m", p);
+ if (r <= 0)
+ break;
- r = split_pair(xattr, "=", &name, &value);
+ r = specifier_printf(xattr, specifier_table, NULL, &xattr_replaced);
+ if (r < 0)
+ return log_error_errno(r, "Failed to replace specifiers in extended attribute '%s': %m", xattr);
+
+ r = split_pair(xattr_replaced, "=", &name, &value);
if (r < 0) {
- log_warning("Illegal xattr found: \"%s\" - ignoring.", xattr);
+ log_warning_errno(r, "Failed to parse extended attribute, ignoring: %s", xattr);
continue;
}
- if (strempty(name) || strempty(value)) {
- log_warning("Malformed xattr found: \"%s\" - ignoring.", xattr);
+ if (isempty(name) || isempty(value)) {
+ log_warning("Malformed extended attribute found, ignoring: %s", xattr);
continue;
}
- tmp = unquote(value, "\"");
- if (!tmp)
+ if (strv_push_pair(&i->xattrs, name, value) < 0)
return log_oom();
- value2 = cunescape(tmp);
- if (!value2)
- return log_oom();
-
- if (strv_push_pair(&i->xattrs, name, value2) < 0)
- return log_oom();
- name = value2 = NULL;
+ name = value = NULL;
}
- return r;
+ return 0;
}
static int path_set_xattrs(Item *i, const char *path) {
@@ -669,17 +701,16 @@ static int path_set_xattrs(Item *i, const char *path) {
int n;
n = strlen(*value);
- log_debug("\"%s\": setting xattr \"%s=%s\"", path, *name, *value);
+ log_debug("Setting extended attribute '%s=%s' on %s.", *name, *value, path);
if (lsetxattr(path, *name, *value, n, 0) < 0) {
- log_error("Setting extended attribute %s=%s on %s failed: %m",
- *name, *value, path);
+ log_error("Setting extended attribute %s=%s on %s failed: %m", *name, *value, path);
return -errno;
}
}
return 0;
}
-static int get_acls_from_arg(Item *item) {
+static int parse_acls_from_arg(Item *item) {
#ifdef HAVE_ACL
int r;
@@ -687,10 +718,10 @@ static int get_acls_from_arg(Item *item) {
/* If force (= modify) is set, we will not modify the acl
* afterwards, so the mask can be added now if necessary. */
+
r = parse_acl(item->argument, &item->acl_access, &item->acl_default, !item->force);
if (r < 0)
- log_warning_errno(errno, "Failed to parse ACL \"%s\": %m. Ignoring",
- item->argument);
+ log_warning_errno(r, "Failed to parse ACL \"%s\": %m. Ignoring", item->argument);
#else
log_warning_errno(ENOSYS, "ACLs are not supported. Ignoring");
#endif
@@ -699,10 +730,13 @@ static int get_acls_from_arg(Item *item) {
}
#ifdef HAVE_ACL
-static int path_set_acl(const char *path, acl_type_t type, acl_t acl, bool modify) {
+static int path_set_acl(const char *path, const char *pretty, acl_type_t type, acl_t acl, bool modify) {
+ _cleanup_(acl_free_charpp) char *t = NULL;
_cleanup_(acl_freep) acl_t dup = NULL;
int r;
- _cleanup_(acl_free_charpp) char *t = NULL;
+
+ /* Returns 0 for success, positive error if already warned,
+ * negative error otherwise. */
if (modify) {
r = acls_for_file(path, type, acl, &dup);
@@ -725,39 +759,204 @@ static int path_set_acl(const char *path, acl_type_t type, acl_t acl, bool modif
return r;
t = acl_to_any_text(dup, NULL, ',', TEXT_ABBREVIATE);
- log_debug("\"%s\": setting %s ACL \"%s\"", path,
+ log_debug("Setting %s ACL %s on %s.",
type == ACL_TYPE_ACCESS ? "access" : "default",
- strna(t));
+ strna(t), pretty);
r = acl_set_file(path, type, dup);
if (r < 0)
- return log_error_errno(-errno,
- "Setting %s ACL \"%s\" on %s failed: %m",
- type == ACL_TYPE_ACCESS ? "access" : "default",
- strna(t), path);
+ /* Return positive to indicate we already warned */
+ return -log_error_errno(errno,
+ "Setting %s ACL \"%s\" on %s failed: %m",
+ type == ACL_TYPE_ACCESS ? "access" : "default",
+ strna(t), pretty);
+
return 0;
}
#endif
static int path_set_acls(Item *item, const char *path) {
+ int r = 0;
#ifdef HAVE_ACL
- int r;
+ char fn[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
+ _cleanup_close_ int fd = -1;
+ struct stat st;
assert(item);
assert(path);
- if (item->acl_access) {
- r = path_set_acl(path, ACL_TYPE_ACCESS, item->acl_access, item->force);
- if (r < 0)
- return r;
- }
+ fd = open(path, O_RDONLY|O_NOFOLLOW|O_CLOEXEC|O_PATH|O_NOATIME);
+ if (fd < 0)
+ return log_error_errno(errno, "Adjusting ACL of %s failed: %m", path);
- if (item->acl_default) {
- r = path_set_acl(path, ACL_TYPE_DEFAULT, item->acl_default, item->force);
- if (r < 0)
- return r;
+ if (fstatat(fd, "", &st, AT_EMPTY_PATH) < 0)
+ return log_error_errno(errno, "Failed to fstat() file %s: %m", path);
+
+ if (S_ISLNK(st.st_mode)) {
+ log_debug("Skipping ACL fix for symlink %s.", path);
+ return 0;
}
+
+ xsprintf(fn, "/proc/self/fd/%i", fd);
+
+ if (item->acl_access)
+ r = path_set_acl(fn, path, ACL_TYPE_ACCESS, item->acl_access, item->force);
+
+ if (r == 0 && item->acl_default)
+ r = path_set_acl(fn, path, ACL_TYPE_DEFAULT, item->acl_default, item->force);
+
+ if (r > 0)
+ return -r; /* already warned */
+ else if (r == -EOPNOTSUPP) {
+ log_debug_errno(r, "ACLs not supported by file system at %s", path);
+ return 0;
+ } else if (r < 0)
+ log_error_errno(r, "ACL operation on \"%s\" failed: %m", path);
#endif
+ return r;
+}
+
+#define ATTRIBUTES_ALL \
+ (FS_NOATIME_FL | \
+ FS_SYNC_FL | \
+ FS_DIRSYNC_FL | \
+ FS_APPEND_FL | \
+ FS_COMPR_FL | \
+ FS_NODUMP_FL | \
+ FS_EXTENT_FL | \
+ FS_IMMUTABLE_FL | \
+ FS_JOURNAL_DATA_FL | \
+ FS_SECRM_FL | \
+ FS_UNRM_FL | \
+ FS_NOTAIL_FL | \
+ FS_TOPDIR_FL | \
+ FS_NOCOW_FL)
+
+static int parse_attribute_from_arg(Item *item) {
+
+ static const struct {
+ char character;
+ unsigned value;
+ } attributes[] = {
+ { 'A', FS_NOATIME_FL }, /* do not update atime */
+ { 'S', FS_SYNC_FL }, /* Synchronous updates */
+ { 'D', FS_DIRSYNC_FL }, /* dirsync behaviour (directories only) */
+ { 'a', FS_APPEND_FL }, /* writes to file may only append */
+ { 'c', FS_COMPR_FL }, /* Compress file */
+ { 'd', FS_NODUMP_FL }, /* do not dump file */
+ { 'e', FS_EXTENT_FL }, /* Top of directory hierarchies*/
+ { 'i', FS_IMMUTABLE_FL }, /* Immutable file */
+ { 'j', FS_JOURNAL_DATA_FL }, /* Reserved for ext3 */
+ { 's', FS_SECRM_FL }, /* Secure deletion */
+ { 'u', FS_UNRM_FL }, /* Undelete */
+ { 't', FS_NOTAIL_FL }, /* file tail should not be merged */
+ { 'T', FS_TOPDIR_FL }, /* Top of directory hierarchies*/
+ { 'C', FS_NOCOW_FL }, /* Do not cow file */
+ };
+
+ enum {
+ MODE_ADD,
+ MODE_DEL,
+ MODE_SET
+ } mode = MODE_ADD;
+
+ unsigned value = 0, mask = 0;
+ const char *p;
+
+ assert(item);
+
+ p = item->argument;
+ if (p) {
+ if (*p == '+') {
+ mode = MODE_ADD;
+ p++;
+ } else if (*p == '-') {
+ mode = MODE_DEL;
+ p++;
+ } else if (*p == '=') {
+ mode = MODE_SET;
+ p++;
+ }
+ }
+
+ if (isempty(p) && mode != MODE_SET) {
+ log_error("Setting file attribute on '%s' needs an attribute specification.", item->path);
+ return -EINVAL;
+ }
+
+ for (; p && *p ; p++) {
+ unsigned i, v;
+
+ for (i = 0; i < ELEMENTSOF(attributes); i++)
+ if (*p == attributes[i].character)
+ break;
+
+ if (i >= ELEMENTSOF(attributes)) {
+ log_error("Unknown file attribute '%c' on '%s'.", *p, item->path);
+ return -EINVAL;
+ }
+
+ v = attributes[i].value;
+
+ if (mode == MODE_ADD || mode == MODE_SET)
+ value |= v;
+ else
+ value &= ~v;
+
+ mask |= v;
+ }
+
+ if (mode == MODE_SET)
+ mask |= ATTRIBUTES_ALL;
+
+ assert(mask != 0);
+
+ item->attribute_mask = mask;
+ item->attribute_value = value;
+ item->attribute_set = true;
+
+ return 0;
+}
+
+static int path_set_attribute(Item *item, const char *path) {
+ _cleanup_close_ int fd = -1;
+ struct stat st;
+ unsigned f;
+ int r;
+
+ if (!item->attribute_set || item->attribute_mask == 0)
+ return 0;
+
+ fd = open(path, O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_NOATIME|O_NOFOLLOW);
+ if (fd < 0) {
+ if (errno == ELOOP)
+ return log_error_errno(errno, "Skipping file attributes adjustment on symlink %s.", path);
+
+ return log_error_errno(errno, "Cannot open '%s': %m", path);
+ }
+
+ if (fstat(fd, &st) < 0)
+ return log_error_errno(errno, "Cannot stat '%s': %m", path);
+
+ /* Issuing the file attribute ioctls on device nodes is not
+ * safe, as that will be delivered to the drivers, not the
+ * file system containing the device node. */
+ if (!S_ISREG(st.st_mode) && !S_ISDIR(st.st_mode)) {
+ log_error("Setting file flags is only supported on regular files and directories, cannot set on '%s'.", path);
+ return -EINVAL;
+ }
+
+ f = item->attribute_value & item->attribute_mask;
+
+ /* Mask away directory-specific flags */
+ if (!S_ISDIR(st.st_mode))
+ f &= ~FS_DIRSYNC_FL;
+
+ r = chattr_fd(fd, f, item->attribute_mask);
+ if (r < 0)
+ return log_error_errno(r,
+ "Cannot set file attribute for '%s', value=0x%08x, mask=0x%08x: %m",
+ path, item->attribute_value, item->attribute_mask);
return 0;
}
@@ -785,21 +984,28 @@ static int write_one_file(Item *i, const char *path) {
return 0;
}
- log_error_errno(errno, "Failed to create file %s: %m", path);
- return -errno;
+ r = -errno;
+ if (!i->argument && errno == EROFS && stat(path, &st) == 0 &&
+ (i->type == CREATE_FILE || st.st_size == 0))
+ goto check_mode;
+
+ return log_error_errno(r, "Failed to create file %s: %m", path);
}
if (i->argument) {
- _cleanup_free_ char *unescaped;
+ _cleanup_free_ char *unescaped = NULL, *replaced = NULL;
- log_debug("%s to \"%s\".",
- i->type == CREATE_FILE ? "Appending" : "Writing", path);
+ log_debug("%s to \"%s\".", i->type == CREATE_FILE ? "Appending" : "Writing", path);
- unescaped = cunescape(i->argument);
- if (!unescaped)
- return log_oom();
+ r = cunescape(i->argument, 0, &unescaped);
+ if (r < 0)
+ return log_error_errno(r, "Failed to unescape parameter to write: %s", i->argument);
- r = loop_write(fd, unescaped, strlen(unescaped), false);
+ r = specifier_printf(unescaped, specifier_table, NULL, &replaced);
+ if (r < 0)
+ return log_error_errno(r, "Failed to replace specifiers in parameter to write '%s': %m", unescaped);
+
+ r = loop_write(fd, replaced, strlen(replaced), false);
if (r < 0)
return log_error_errno(r, "Failed to write file \"%s\": %m", path);
} else
@@ -810,6 +1016,7 @@ static int write_one_file(Item *i, const char *path) {
if (stat(path, &st) < 0)
return log_error_errno(errno, "stat(%s) failed: %m", path);
+ check_mode:
if (!S_ISREG(st.st_mode)) {
log_error("%s is not a file.", path);
return -EEXIST;
@@ -921,6 +1128,7 @@ static const char *creation_mode_verb_table[_CREATION_MODE_MAX] = {
DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(creation_mode_verb, CreationMode);
static int create_item(Item *i) {
+ _cleanup_free_ char *resolved = NULL;
struct stat st;
int r = 0;
CreationMode creation;
@@ -944,17 +1152,25 @@ static int create_item(Item *i) {
return r;
break;
- case COPY_FILES:
- log_debug("Copying tree \"%s\" to \"%s\".", i->argument, i->path);
- r = copy_tree(i->argument, i->path, false);
+ case COPY_FILES: {
+ r = specifier_printf(i->argument, specifier_table, NULL, &resolved);
+ if (r < 0)
+ return log_error_errno(r, "Failed to substitute specifiers in copy source %s: %m", i->argument);
+
+ log_debug("Copying tree \"%s\" to \"%s\".", resolved, i->path);
+ r = copy_tree(resolved, i->path, false);
+
+ if (r == -EROFS && stat(i->path, &st) == 0)
+ r = -EEXIST;
+
if (r < 0) {
struct stat a, b;
if (r != -EEXIST)
return log_error_errno(r, "Failed to copy files to %s: %m", i->path);
- if (stat(i->argument, &a) < 0)
- return log_error_errno(errno, "stat(%s) failed: %m", i->argument);
+ if (stat(resolved, &a) < 0)
+ return log_error_errno(errno, "stat(%s) failed: %m", resolved);
if (stat(i->path, &b) < 0)
return log_error_errno(errno, "stat(%s) failed: %m", i->path);
@@ -998,20 +1214,25 @@ static int create_item(Item *i) {
r = mkdir_label(i->path, i->mode);
if (r < 0) {
- if (r != -EEXIST)
- return log_error_errno(r, "Failed to create directory or subvolume \"%s\": %m", i->path);
+ int k;
- if (stat(i->path, &st) < 0)
- return log_error_errno(errno, "stat(%s) failed: %m", i->path);
+ if (r != -EEXIST && r != -EROFS)
+ return log_error_errno(r, "Failed to create directory or subvolume \"%s\": %m", i->path);
- if (!S_ISDIR(st.st_mode)) {
- log_debug("\"%s\" already exists and is not a directory.", i->path);
+ k = is_dir(i->path, false);
+ if (k == -ENOENT && r == -EROFS)
+ return log_error_errno(r, "%s does not exist and cannot be created as the file system is read-only.", i->path);
+ if (k < 0)
+ return log_error_errno(k, "Failed to check if %s exists: %m", i->path);
+ if (!k) {
+ log_warning("\"%s\" already exists and is not a directory.", i->path);
return 0;
}
creation = CREATION_EXISTING;
} else
creation = CREATION_NORMAL;
+
log_debug("%s directory \"%s\".", creation_mode_verb_to_string(creation), i->path);
r = path_set_perms(i, i->path);
@@ -1032,13 +1253,12 @@ static int create_item(Item *i) {
if (errno != EEXIST)
return log_error_errno(errno, "Failed to create fifo %s: %m", i->path);
- if (stat(i->path, &st) < 0)
+ if (lstat(i->path, &st) < 0)
return log_error_errno(errno, "stat(%s) failed: %m", i->path);
if (!S_ISFIFO(st.st_mode)) {
if (i->force) {
-
RUN_WITH_UMASK(0000) {
mac_selinux_create_file_prepare(i->path, S_IFIFO);
r = mkfifo_atomic(i->path, i->mode);
@@ -1049,7 +1269,7 @@ static int create_item(Item *i) {
return log_error_errno(r, "Failed to create fifo %s: %m", i->path);
creation = CREATION_FORCE;
} else {
- log_debug("%s is not a fifo.", i->path);
+ log_warning("\"%s\" already exists and is not a fifo.", i->path);
return 0;
}
} else
@@ -1063,29 +1283,34 @@ static int create_item(Item *i) {
return r;
break;
+ }
- case CREATE_SYMLINK:
+ case CREATE_SYMLINK: {
+ r = specifier_printf(i->argument, specifier_table, NULL, &resolved);
+ if (r < 0)
+ return log_error_errno(r, "Failed to substitute specifiers in symlink target %s: %m", i->argument);
mac_selinux_create_file_prepare(i->path, S_IFLNK);
- r = symlink(i->argument, i->path);
+ r = symlink(resolved, i->path);
mac_selinux_create_file_clear();
if (r < 0) {
_cleanup_free_ char *x = NULL;
if (errno != EEXIST)
- return log_error_errno(errno, "symlink(%s, %s) failed: %m", i->argument, i->path);
+ return log_error_errno(errno, "symlink(%s, %s) failed: %m", resolved, i->path);
r = readlink_malloc(i->path, &x);
- if (r < 0 || !streq(i->argument, x)) {
+ if (r < 0 || !streq(resolved, x)) {
if (i->force) {
mac_selinux_create_file_prepare(i->path, S_IFLNK);
- r = symlink_atomic(i->argument, i->path);
+ r = symlink_atomic(resolved, i->path);
mac_selinux_create_file_clear();
if (r < 0)
- return log_error_errno(r, "symlink(%s, %s) failed: %m", i->argument, i->path);
+ return log_error_errno(r, "symlink(%s, %s) failed: %m", resolved, i->path);
+
creation = CREATION_FORCE;
} else {
log_debug("\"%s\" is not a symlink or does not point to the correct path.", i->path);
@@ -1094,10 +1319,11 @@ static int create_item(Item *i) {
} else
creation = CREATION_EXISTING;
} else
+
creation = CREATION_NORMAL;
log_debug("%s symlink \"%s\".", creation_mode_verb_to_string(creation), i->path);
-
break;
+ }
case CREATE_BLOCK_DEVICE:
case CREATE_CHAR_DEVICE: {
@@ -1131,7 +1357,7 @@ static int create_item(Item *i) {
if (errno != EEXIST)
return log_error_errno(errno, "Failed to create device node %s: %m", i->path);
- if (stat(i->path, &st) < 0)
+ if (lstat(i->path, &st) < 0)
return log_error_errno(errno, "stat(%s) failed: %m", i->path);
if ((st.st_mode & S_IFMT) != file_type) {
@@ -1155,6 +1381,7 @@ static int create_item(Item *i) {
creation = CREATION_EXISTING;
} else
creation = CREATION_NORMAL;
+
log_debug("%s %s device node \"%s\" %u:%u.",
creation_mode_verb_to_string(creation),
i->type == CREATE_BLOCK_DEVICE ? "block" : "char",
@@ -1203,9 +1430,19 @@ static int create_item(Item *i) {
if (r < 0)
return r;
break;
- }
- log_debug("%s created successfully.", i->path);
+ case SET_ATTRIBUTE:
+ r = glob_item(i, path_set_attribute, false);
+ if (r < 0)
+ return r;
+ break;
+
+ case RECURSIVE_SET_ATTRIBUTE:
+ r = glob_item(i, path_set_attribute, true);
+ if (r < 0)
+ return r;
+ break;
+ }
return 0;
}
@@ -1228,7 +1465,7 @@ static int remove_item_instance(Item *i, const char *instance) {
/* FIXME: we probably should use dir_cleanup() here
* instead of rm_rf() so that 'x' is honoured. */
log_debug("rm -rf \"%s\"", instance);
- r = rm_rf_dangerous(instance, false, i->type == RECURSIVE_REMOVE_PATH, false);
+ r = rm_rf(instance, (i->type == RECURSIVE_REMOVE_PATH ? REMOVE_ROOT : 0) | REMOVE_PHYSICAL);
if (r < 0 && r != -ENOENT)
return log_error_errno(r, "rm_rf(%s): %m", instance);
@@ -1269,6 +1506,8 @@ static int remove_item(Item *i) {
case RECURSIVE_SET_XATTR:
case SET_ACL:
case RECURSIVE_SET_ACL:
+ case SET_ATTRIBUTE:
+ case RECURSIVE_SET_ATTRIBUTE:
break;
case REMOVE_PATH:
@@ -1378,7 +1617,7 @@ static int process_item(Item *i) {
PATH_FOREACH_PREFIX(prefix, i->path) {
ItemArray *j;
- j = hashmap_get(items, prefix);
+ j = ordered_hashmap_get(items, prefix);
if (j) {
int s;
@@ -1437,6 +1676,20 @@ static void item_array_free(ItemArray *a) {
free(a);
}
+static int item_compare(const void *a, const void *b) {
+ const Item *x = a, *y = b;
+
+ /* Make sure that the ownership taking item is put first, so
+ * that we first create the node, and then can adjust it */
+
+ if (takes_ownership(x->type) && !takes_ownership(y->type))
+ return -1;
+ if (!takes_ownership(x->type) && takes_ownership(y->type))
+ return 1;
+
+ return (int) x->type - (int) y->type;
+}
+
static bool item_compatible(Item *a, Item *b) {
assert(a);
assert(b);
@@ -1494,39 +1747,40 @@ static bool should_include_path(const char *path) {
static int parse_line(const char *fname, unsigned line, const char *buffer) {
- static const Specifier specifier_table[] = {
- { 'm', specifier_machine_id, NULL },
- { 'b', specifier_boot_id, NULL },
- { 'H', specifier_host_name, NULL },
- { 'v', specifier_kernel_release, NULL },
- {}
- };
-
_cleanup_free_ char *action = NULL, *mode = NULL, *user = NULL, *group = NULL, *age = NULL, *path = NULL;
_cleanup_(item_free_contents) Item i = {};
ItemArray *existing;
- Hashmap *h;
- int r, c = -1, pos;
+ OrderedHashmap *h;
+ int r, pos;
bool force = false, boot = false;
assert(fname);
assert(line >= 1);
assert(buffer);
- r = sscanf(buffer,
- "%ms %ms %ms %ms %ms %ms %n",
- &action,
- &path,
- &mode,
- &user,
- &group,
- &age,
- &c);
- if (r < 2) {
+ r = unquote_many_words(
+ &buffer,
+ 0,
+ &action,
+ &path,
+ &mode,
+ &user,
+ &group,
+ &age,
+ NULL);
+ if (r < 0)
+ return log_error_errno(r, "[%s:%u] Failed to parse line: %m", fname, line);
+ else if (r < 2) {
log_error("[%s:%u] Syntax error.", fname, line);
return -EIO;
}
+ if (!isempty(buffer) && !streq(buffer, "-")) {
+ i.argument = strdup(buffer);
+ if (!i.argument)
+ return log_oom();
+ }
+
if (isempty(action)) {
log_error("[%s:%u] Command too short '%s'.", fname, line, action);
return -EINVAL;
@@ -1559,19 +1813,8 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
return r;
}
- if (c >= 0) {
- c += strspn(buffer+c, WHITESPACE);
- if (buffer[c] != 0 && (buffer[c] != '-' || buffer[c+1] != 0)) {
- i.argument = unquote(buffer+c, "\"");
- if (!i.argument)
- return log_oom();
- }
- }
-
switch (i.type) {
- case CREATE_FILE:
- case TRUNCATE_FILE:
case CREATE_DIRECTORY:
case CREATE_SUBVOLUME:
case TRUNCATE_DIRECTORY:
@@ -1583,6 +1826,13 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
case ADJUST_MODE:
case RELABEL_PATH:
case RECURSIVE_RELABEL_PATH:
+ if (i.argument)
+ log_warning("[%s:%u] %c lines don't take argument fields, ignoring.", fname, line, i.type);
+
+ break;
+
+ case CREATE_FILE:
+ case TRUNCATE_FILE:
break;
case CREATE_SYMLINK:
@@ -1637,7 +1887,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
log_error("[%s:%u] Set extended attribute requires argument.", fname, line);
return -EBADMSG;
}
- r = get_xattrs_from_arg(&i);
+ r = parse_xattrs_from_arg(&i);
if (r < 0)
return r;
break;
@@ -1648,7 +1898,18 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
log_error("[%s:%u] Set ACLs requires argument.", fname, line);
return -EBADMSG;
}
- r = get_acls_from_arg(&i);
+ r = parse_acls_from_arg(&i);
+ if (r < 0)
+ return r;
+ break;
+
+ case SET_ATTRIBUTE:
+ case RECURSIVE_SET_ATTRIBUTE:
+ if (!i.argument) {
+ log_error("[%s:%u] Set file attribute requires argument.", fname, line);
+ return -EBADMSG;
+ }
+ r = parse_attribute_from_arg(&i);
if (r < 0)
return r;
break;
@@ -1671,7 +1932,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
if (arg_root) {
char *p;
- p = strappend(arg_root, i.path);
+ p = prefix_root(arg_root, i.path);
if (!p)
return log_oom();
@@ -1679,7 +1940,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
i.path = p;
}
- if (user && !streq(user, "-")) {
+ if (!isempty(user) && !streq(user, "-")) {
const char *u = user;
r = get_user_creds(&u, &i.uid, NULL, NULL, NULL);
@@ -1691,7 +1952,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
i.uid_set = true;
}
- if (group && !streq(group, "-")) {
+ if (!isempty(group) && !streq(group, "-")) {
const char *g = group;
r = get_group_creds(&g, &i.gid);
@@ -1703,7 +1964,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
i.gid_set = true;
}
- if (mode && !streq(mode, "-")) {
+ if (!isempty(mode) && !streq(mode, "-")) {
const char *mm = mode;
unsigned m;
@@ -1712,9 +1973,9 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
mm++;
}
- if (sscanf(mm, "%o", &m) != 1) {
+ if (parse_mode(mm, &m) < 0) {
log_error("[%s:%u] Invalid mode '%s'.", fname, line, mode);
- return -ENOENT;
+ return -EBADMSG;
}
i.mode = m;
@@ -1723,7 +1984,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
i.mode = IN_SET(i.type, CREATE_DIRECTORY, CREATE_SUBVOLUME, TRUNCATE_DIRECTORY)
? 0755 : 0644;
- if (age && !streq(age, "-")) {
+ if (!isempty(age) && !streq(age, "-")) {
const char *a = age;
if (*a == '~') {
@@ -1741,18 +2002,20 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
h = needs_glob(i.type) ? globs : items;
- existing = hashmap_get(h, i.path);
+ existing = ordered_hashmap_get(h, i.path);
if (existing) {
unsigned n;
for (n = 0; n < existing->count; n++) {
- if (!item_compatible(existing->items + n, &i))
+ if (!item_compatible(existing->items + n, &i)) {
log_warning("[%s:%u] Duplicate line for path \"%s\", ignoring.",
fname, line, i.path);
+ return 0;
+ }
}
} else {
existing = new0(ItemArray, 1);
- r = hashmap_put(h, i.path, existing);
+ r = ordered_hashmap_put(h, i.path, existing);
if (r < 0)
return log_oom();
}
@@ -1761,6 +2024,10 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
return log_oom();
memcpy(existing->items + existing->count++, &i, sizeof(i));
+
+ /* Sort item array, to enforce stable ordering of application */
+ qsort_safe(existing->items, existing->count, sizeof(Item), item_compare);
+
zero(i);
return 0;
}
@@ -1911,14 +2178,14 @@ static int read_config_file(const char *fn, bool ignore_enoent) {
}
/* we have to determine age parameter for each entry of type X */
- HASHMAP_FOREACH(i, globs, iterator) {
+ ORDERED_HASHMAP_FOREACH(i, globs, iterator) {
Iterator iter;
Item *j, *candidate_item = NULL;
if (i->type != IGNORE_DIRECTORY_PATH)
continue;
- HASHMAP_FOREACH(j, items, iter) {
+ ORDERED_HASHMAP_FOREACH(j, items, iter) {
if (j->type != CREATE_DIRECTORY && j->type != TRUNCATE_DIRECTORY && j->type != CREATE_SUBVOLUME)
continue;
@@ -1964,8 +2231,8 @@ int main(int argc, char *argv[]) {
mac_selinux_init(NULL);
- items = hashmap_new(&string_hash_ops);
- globs = hashmap_new(&string_hash_ops);
+ items = ordered_hashmap_new(&string_hash_ops);
+ globs = ordered_hashmap_new(&string_hash_ops);
if (!items || !globs) {
r = log_oom();
@@ -2000,27 +2267,31 @@ int main(int argc, char *argv[]) {
}
}
- HASHMAP_FOREACH(a, globs, iterator) {
+ /* The non-globbing ones usually create things, hence we apply
+ * them first */
+ ORDERED_HASHMAP_FOREACH(a, items, iterator) {
k = process_item_array(a);
if (k < 0 && r == 0)
r = k;
}
- HASHMAP_FOREACH(a, items, iterator) {
+ /* The globbing ones usually alter things, hence we apply them
+ * second. */
+ ORDERED_HASHMAP_FOREACH(a, globs, iterator) {
k = process_item_array(a);
if (k < 0 && r == 0)
r = k;
}
finish:
- while ((a = hashmap_steal_first(items)))
+ while ((a = ordered_hashmap_steal_first(items)))
item_array_free(a);
- while ((a = hashmap_steal_first(globs)))
+ while ((a = ordered_hashmap_steal_first(globs)))
item_array_free(a);
- hashmap_free(items);
- hashmap_free(globs);
+ ordered_hashmap_free(items);
+ ordered_hashmap_free(globs);
free(arg_include_prefixes);
free(arg_exclude_prefixes);
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 47093b850d..97251ef0aa 100644
--- a/src/tty-ask-password-agent/tty-ask-password-agent.c
+++ b/src/tty-ask-password-agent/tty-ask-password-agent.c
@@ -42,6 +42,9 @@
#include "strv.h"
#include "build.h"
#include "def.h"
+#include "process-util.h"
+#include "terminal-util.h"
+#include "signal-util.h"
static enum {
ACTION_LIST,
@@ -379,7 +382,7 @@ static int wall_tty_block(void) {
return fd;
}
-static bool wall_tty_match(const char *path) {
+static bool wall_tty_match(const char *path, void *userdata) {
int fd, r;
struct stat st;
_cleanup_free_ char *p = NULL;
@@ -453,7 +456,7 @@ static int show_passwords(void) {
r = q;
if (wall)
- utmp_wall(wall, NULL, wall_tty_match);
+ utmp_wall(wall, NULL, NULL, wall_tty_match, NULL);
}
return r;
diff --git a/src/udev/.gitignore b/src/udev/.gitignore
index a229430e36..ba112ce218 100644
--- a/src/udev/.gitignore
+++ b/src/udev/.gitignore
@@ -2,4 +2,4 @@
/keyboard-keys-from-name.gperf
/keyboard-keys-from-name.h
/keyboard-keys-to-name.h
-/keyboard-keys.txt
+/keyboard-keys-list.txt
diff --git a/src/udev/accelerometer/accelerometer.c b/src/udev/accelerometer/accelerometer.c
index 0f1b3c6ec3..9e2c590c15 100644
--- a/src/udev/accelerometer/accelerometer.c
+++ b/src/udev/accelerometer/accelerometer.c
@@ -46,16 +46,10 @@
#include <stdio.h>
#include <string.h>
-#include <stdbool.h>
#include <math.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
#include <stdlib.h>
-#include <unistd.h>
#include <getopt.h>
#include <limits.h>
-#include <linux/limits.h>
#include <linux/input.h>
#include "libudev.h"
diff --git a/src/udev/ata_id/ata_id.c b/src/udev/ata_id/ata_id.c
index 9e4f674a9e..cc1bf45ae9 100644
--- a/src/udev/ata_id/ata_id.c
+++ b/src/udev/ata_id/ata_id.c
@@ -496,7 +496,7 @@ int main(int argc, char *argv[])
}
}
- memcpy (model, id.model, 40);
+ memcpy(model, id.model, 40);
model[40] = '\0';
udev_util_encode_string(model, model_enc, sizeof(model_enc));
util_replace_whitespace((char *) id.model, model, 40);
diff --git a/src/udev/cdrom_id/cdrom_id.c b/src/udev/cdrom_id/cdrom_id.c
index 6052f6abd8..3d74ae50f1 100644
--- a/src/udev/cdrom_id/cdrom_id.c
+++ b/src/udev/cdrom_id/cdrom_id.c
@@ -36,6 +36,7 @@
#include "libudev.h"
#include "libudev-private.h"
+#include "random-util.h"
/* device info */
static unsigned int cd_cd_rom;
@@ -650,8 +651,8 @@ static int cd_media_info(struct udev *udev, int fd)
* write protected; we need to check the contents if it is blank */
if ((cd_media_dvd_rw_ro || cd_media_dvd_plus_rw || cd_media_dvd_plus_rw_dl || cd_media_dvd_ram) && (header[2] & 3) > 1) {
unsigned char buffer[32 * 2048];
- unsigned char result, len;
- int block, offset;
+ unsigned char len;
+ int offset;
if (cd_media_dvd_ram) {
/* a write protected dvd-ram may report "complete" status */
@@ -732,22 +733,23 @@ static int cd_media_info(struct udev *udev, int fd)
/* if any non-zero data is found in sector 16 (iso and udf) or
* eventually 0 (fat32 boot sector, ext2 superblock, etc), disc
* is assumed non-blank */
- result = 0;
- for (block = 32768; block >= 0 && !result; block -= 32768) {
- offset = block;
- while (offset < (block + 2048) && !result) {
- result = buffer [offset];
- offset++;
+ for (offset = 32768; offset < (32768 + 2048); offset++) {
+ if (buffer [offset]) {
+ log_debug("data in block 16, assuming complete");
+ goto determined;
}
}
- if (!result) {
- cd_media_state = media_status[0];
- log_debug("no data in blocks 0 or 16, assuming blank");
- } else {
- log_debug("data in blocks 0 or 16, assuming complete");
+ for (offset = 0; offset < 2048; offset++) {
+ if (buffer [offset]) {
+ log_debug("data in block 0, assuming complete");
+ goto determined;
+ }
}
+
+ cd_media_state = media_status[0];
+ log_debug("no data in blocks 0 or 16, assuming blank");
}
determined:
diff --git a/src/udev/collect/collect.c b/src/udev/collect/collect.c
index 4bb6edbef1..6cf41c67bb 100644
--- a/src/udev/collect/collect.c
+++ b/src/udev/collect/collect.c
@@ -20,18 +20,10 @@
*/
#include <stdio.h>
-#include <stdlib.h>
#include <stddef.h>
-#include <unistd.h>
-#include <signal.h>
-#include <fcntl.h>
#include <errno.h>
-#include <string.h>
#include <getopt.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include "libudev.h"
#include "libudev-private.h"
#include "macro.h"
diff --git a/src/gudev/Makefile b/src/udev/mtd_probe/Makefile
index d0b0e8e008..d0b0e8e008 120000
--- a/src/gudev/Makefile
+++ b/src/udev/mtd_probe/Makefile
diff --git a/src/udev/mtd_probe/mtd_probe.c b/src/udev/mtd_probe/mtd_probe.c
index 13c757bd1c..67b750c4b3 100644
--- a/src/udev/mtd_probe/mtd_probe.c
+++ b/src/udev/mtd_probe/mtd_probe.c
@@ -16,7 +16,7 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301 USA
*/
-#include "mtd_probe.h"
+
#include <stdio.h>
#include <sys/ioctl.h>
#include <mtd/mtd-user.h>
@@ -26,6 +26,8 @@
#include <unistd.h>
#include <stdlib.h>
+#include "mtd_probe.h"
+
int main(int argc, char** argv)
{
int mtd_fd;
diff --git a/src/udev/mtd_probe/mtd_probe.h b/src/udev/mtd_probe/mtd_probe.h
index d99be9addc..caea5c2693 100644
--- a/src/udev/mtd_probe/mtd_probe.h
+++ b/src/udev/mtd_probe/mtd_probe.h
@@ -21,6 +21,8 @@
#include <mtd/mtd-user.h>
+#include "macro.h"
+
/* Full oob structure as written on the flash */
struct sm_oob {
uint32_t reserved;
@@ -30,8 +32,7 @@ struct sm_oob {
uint8_t ecc2[3];
uint8_t lba_copy2[2];
uint8_t ecc1[3];
-} __attribute__((packed));
-
+} _packed_;
/* one sector is always 512 bytes, but it can consist of two nand pages */
#define SM_SECTOR_SIZE 512
@@ -47,5 +48,4 @@ struct sm_oob {
#define SM_SMALL_PAGE 256
#define SM_SMALL_OOB_SIZE 8
-
void probe_smart_media(int mtd_fd, mtd_info_t *info);
diff --git a/src/udev/net/ethtool-util.c b/src/udev/net/ethtool-util.c
index ec67126b21..927b8abc64 100644
--- a/src/udev/net/ethtool-util.c
+++ b/src/udev/net/ethtool-util.c
@@ -93,8 +93,7 @@ int ethtool_get_driver(int *fd, const char *ifname, char **ret) {
return 0;
}
-int ethtool_set_speed(int *fd, const char *ifname, unsigned int speed, Duplex duplex)
-{
+int ethtool_set_speed(int *fd, const char *ifname, unsigned int speed, Duplex duplex) {
struct ethtool_cmd ecmd = {
.cmd = ETHTOOL_GSET
};
diff --git a/src/udev/net/link-config.c b/src/udev/net/link-config.c
index 8b3dc45d4e..5610b2808e 100644
--- a/src/udev/net/link-config.c
+++ b/src/udev/net/link-config.c
@@ -22,7 +22,6 @@
#include <netinet/ether.h>
#include <linux/netdevice.h>
-#include "sd-id128.h"
#include "missing.h"
#include "link-config.h"
@@ -36,11 +35,9 @@
#include "path-util.h"
#include "conf-parser.h"
#include "conf-files.h"
-#include "fileio.h"
-#include "hashmap.h"
#include "rtnl-util.h"
#include "network-internal.h"
-#include "siphash24.h"
+#include "random-util.h"
struct link_config_ctx {
LIST_HEAD(link_config, links);
@@ -70,9 +67,9 @@ static void link_config_free(link_config *link) {
free(link->filename);
free(link->match_mac);
- free(link->match_path);
- free(link->match_driver);
- free(link->match_type);
+ strv_free(link->match_path);
+ strv_free(link->match_driver);
+ strv_free(link->match_type);
free(link->match_name);
free(link->match_host);
free(link->match_virt);
@@ -177,6 +174,9 @@ static int load_link(link_config_ctx *ctx, const char *filename) {
else
log_debug("Parsed configuration file %s", filename);
+ if (link->mtu > UINT_MAX || link->speed > UINT_MAX)
+ return -ERANGE;
+
link->filename = strdup(filename);
LIST_PREPEND(links, ctx->links, link);
@@ -240,6 +240,10 @@ int link_config_get(link_config_ctx *ctx, struct udev_device *device,
link_config **ret) {
link_config *link;
+ assert(ctx);
+ assert(device);
+ assert(ret);
+
LIST_FOREACH(links, link, ctx->links) {
const char* attr_value;
@@ -259,7 +263,7 @@ int link_config_get(link_config_ctx *ctx, struct udev_device *device,
attr_value = udev_device_get_sysattr_value(device, "name_assign_type");
if (attr_value)
- (void)safe_atou8(attr_value, &name_assign_type);
+ (void) safe_atou8(attr_value, &name_assign_type);
if (name_assign_type == NET_NAME_ENUM) {
log_warning("Config file %s applies to device based on potentially unpredictable interface name '%s'",
@@ -379,10 +383,9 @@ int link_config_apply(link_config_ctx *ctx, link_config *config,
if (!old_name)
return -EINVAL;
- r = ethtool_set_speed(&ctx->ethtool_fd, old_name, config->speed / 1024,
- config->duplex);
+ r = ethtool_set_speed(&ctx->ethtool_fd, old_name, config->speed / 1024, config->duplex);
if (r < 0)
- log_warning_errno(r, "Could not set speed or duplex of %s to %u Mbps (%s): %m",
+ log_warning_errno(r, "Could not set speed or duplex of %s to %zu Mbps (%s): %m",
old_name, config->speed / 1024,
duplex_to_string(config->duplex));
@@ -461,8 +464,7 @@ int link_config_apply(link_config_ctx *ctx, link_config *config,
mac = config->mac;
}
- r = rtnl_set_link_properties(&ctx->rtnl, ifindex, config->alias, mac,
- config->mtu);
+ r = rtnl_set_link_properties(&ctx->rtnl, ifindex, config->alias, mac, config->mtu);
if (r < 0)
return log_warning_errno(r, "Could not set Alias, MACAddress or MTU on %s: %m", old_name);
@@ -473,7 +475,7 @@ int link_config_apply(link_config_ctx *ctx, link_config *config,
int link_get_driver(link_config_ctx *ctx, struct udev_device *device, char **ret) {
const char *name;
- char *driver;
+ char *driver = NULL;
int r;
name = udev_device_get_sysname(device);
diff --git a/src/udev/net/link-config.h b/src/udev/net/link-config.h
index cb434d1aee..9875057e84 100644
--- a/src/udev/net/link-config.h
+++ b/src/udev/net/link-config.h
@@ -23,7 +23,6 @@
#include "ethtool-util.h"
#include "condition.h"
-#include "util.h"
#include "list.h"
#include "libudev.h"
@@ -67,8 +66,8 @@ struct link_config {
NamePolicy *name_policy;
char *name;
char *alias;
- unsigned int mtu;
- unsigned int speed;
+ size_t mtu;
+ size_t speed;
Duplex duplex;
WakeOnLan wol;
diff --git a/src/udev/scsi_id/scsi_serial.c b/src/udev/scsi_id/scsi_serial.c
index dcfff1d4ea..3691a69d48 100644
--- a/src/udev/scsi_id/scsi_serial.c
+++ b/src/udev/scsi_id/scsi_serial.c
@@ -37,6 +37,7 @@
#include "libudev-private.h"
#include "scsi.h"
#include "scsi_id.h"
+#include "random-util.h"
/*
* A priority based list of id, naa, and binary/ascii for the identifier
diff --git a/src/udev/udev-builtin-blkid.c b/src/udev/udev-builtin-blkid.c
index 03e3dc2867..1dad4476f3 100644
--- a/src/udev/udev-builtin-blkid.c
+++ b/src/udev/udev-builtin-blkid.c
@@ -20,8 +20,6 @@
#include <stdio.h>
#include <stdlib.h>
-#include <stdarg.h>
-#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
@@ -265,7 +263,7 @@ static int builtin_blkid(struct udev_device *dev, int argc, char *argv[], bool t
fd = open(udev_device_get_devnode(dev), O_RDONLY|O_CLOEXEC);
if (fd < 0) {
- fprintf(stderr, "error: %s: %m\n", udev_device_get_devnode(dev));
+ err = log_debug_errno(errno, "Failure opening block device %s: %m", udev_device_get_devnode(dev));
goto out;
}
diff --git a/src/udev/udev-builtin-btrfs.c b/src/udev/udev-builtin-btrfs.c
index 3643596a70..3352821567 100644
--- a/src/udev/udev-builtin-btrfs.c
+++ b/src/udev/udev-builtin-btrfs.c
@@ -17,12 +17,8 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <unistd.h>
#include <stdlib.h>
-#include <string.h>
-#include <stdio.h>
#include <fcntl.h>
-#include <errno.h>
#include <sys/ioctl.h>
#ifdef HAVE_LINUX_BTRFS_H
diff --git a/src/udev/udev-builtin-hwdb.c b/src/udev/udev-builtin-hwdb.c
index 95476648f9..7dfc74e6fa 100644
--- a/src/udev/udev-builtin-hwdb.c
+++ b/src/udev/udev-builtin-hwdb.c
@@ -18,10 +18,6 @@
***/
#include <stdio.h>
-#include <errno.h>
-#include <string.h>
-#include <inttypes.h>
-#include <ctype.h>
#include <stdlib.h>
#include <fnmatch.h>
#include <getopt.h>
@@ -89,6 +85,8 @@ static int udev_builtin_hwdb_search(struct udev_device *dev, struct udev_device
bool last = false;
int r = 0;
+ assert(dev);
+
for (d = srcdev; d && !last; d = udev_device_get_parent(d)) {
const char *dsubsys;
const char *modalias = NULL;
diff --git a/src/udev/udev-builtin-input_id.c b/src/udev/udev-builtin-input_id.c
index 46f1c539d2..e3fa4bc162 100644
--- a/src/udev/udev-builtin-input_id.c
+++ b/src/udev/udev-builtin-input_id.c
@@ -126,77 +126,122 @@ static void get_cap_mask(struct udev_device *dev,
}
/* pointer devices */
-static void test_pointers (struct udev_device *dev,
- const unsigned long* bitmask_ev,
- const unsigned long* bitmask_abs,
- const unsigned long* bitmask_key,
- const unsigned long* bitmask_rel,
- bool test) {
- int is_mouse = 0;
- int is_touchpad = 0;
-
- if (!test_bit (EV_KEY, bitmask_ev)) {
- if (test_bit (EV_ABS, bitmask_ev) &&
- test_bit (ABS_X, bitmask_abs) &&
- test_bit (ABS_Y, bitmask_abs) &&
- test_bit (ABS_Z, bitmask_abs))
- udev_builtin_add_property(dev, test, "ID_INPUT_ACCELEROMETER", "1");
- return;
+static bool test_pointers(struct udev_device *dev,
+ const unsigned long* bitmask_ev,
+ const unsigned long* bitmask_abs,
+ const unsigned long* bitmask_key,
+ const unsigned long* bitmask_rel,
+ const unsigned long* bitmask_props,
+ bool test) {
+ bool has_abs_coordinates = false;
+ bool has_rel_coordinates = false;
+ bool has_mt_coordinates = false;
+ bool has_joystick_axes_or_buttons = false;
+ bool is_direct = false;
+ bool has_touch = false;
+ bool has_3d_coordinates = false;
+ bool has_keys = false;
+ bool stylus_or_pen = false;
+ bool finger_but_no_pen = false;
+ bool has_mouse_button = false;
+ bool is_mouse = false;
+ bool is_touchpad = false;
+ bool is_touchscreen = false;
+ bool is_tablet = false;
+ bool is_joystick = false;
+ bool is_accelerometer = false;
+ bool is_pointing_stick= false;
+
+ has_keys = test_bit(EV_KEY, bitmask_ev);
+ has_abs_coordinates = test_bit(ABS_X, bitmask_abs) && test_bit(ABS_Y, bitmask_abs);
+ has_3d_coordinates = has_abs_coordinates && test_bit(ABS_Z, bitmask_abs);
+ is_accelerometer = test_bit(INPUT_PROP_ACCELEROMETER, bitmask_props);
+
+ if (!has_keys && has_3d_coordinates)
+ is_accelerometer = true;
+
+ if (is_accelerometer) {
+ udev_builtin_add_property(dev, test, "ID_INPUT_ACCELEROMETER", "1");
+ return true;
}
- if (test_bit (EV_ABS, bitmask_ev) &&
- test_bit (ABS_X, bitmask_abs) && test_bit (ABS_Y, bitmask_abs)) {
- if (test_bit (BTN_STYLUS, bitmask_key) || test_bit (BTN_TOOL_PEN, bitmask_key))
- udev_builtin_add_property(dev, test, "ID_INPUT_TABLET", "1");
- else if (test_bit (BTN_TOOL_FINGER, bitmask_key) && !test_bit (BTN_TOOL_PEN, bitmask_key))
- is_touchpad = 1;
- else if (test_bit (BTN_MOUSE, bitmask_key))
+ is_pointing_stick = test_bit(INPUT_PROP_POINTING_STICK, bitmask_props);
+ stylus_or_pen = test_bit(BTN_STYLUS, bitmask_key) || test_bit(BTN_TOOL_PEN, bitmask_key);
+ finger_but_no_pen = test_bit(BTN_TOOL_FINGER, bitmask_key) && !test_bit(BTN_TOOL_PEN, bitmask_key);
+ has_mouse_button = test_bit(BTN_LEFT, bitmask_key);
+ has_rel_coordinates = test_bit(EV_REL, bitmask_ev) && test_bit(REL_X, bitmask_rel) && test_bit(REL_Y, bitmask_rel);
+ has_mt_coordinates = test_bit(ABS_MT_POSITION_X, bitmask_abs) && test_bit(ABS_MT_POSITION_Y, bitmask_abs);
+
+ /* unset has_mt_coordinates if devices claims to have all abs axis */
+ if(has_mt_coordinates && test_bit(ABS_MT_SLOT, bitmask_abs) && test_bit(ABS_MT_SLOT - 1, bitmask_abs))
+ has_mt_coordinates = false;
+ is_direct = test_bit(INPUT_PROP_DIRECT, bitmask_props);
+ has_touch = test_bit(BTN_TOUCH, bitmask_key);
+ /* joysticks don't necessarily have buttons; e. g.
+ * rudders/pedals are joystick-like, but buttonless; they have
+ * other fancy axes */
+ has_joystick_axes_or_buttons = test_bit(BTN_TRIGGER, bitmask_key) ||
+ test_bit(BTN_A, bitmask_key) ||
+ test_bit(BTN_1, bitmask_key) ||
+ test_bit(ABS_RX, bitmask_abs) ||
+ test_bit(ABS_RY, bitmask_abs) ||
+ test_bit(ABS_RZ, bitmask_abs) ||
+ test_bit(ABS_THROTTLE, bitmask_abs) ||
+ test_bit(ABS_RUDDER, bitmask_abs) ||
+ test_bit(ABS_WHEEL, bitmask_abs) ||
+ test_bit(ABS_GAS, bitmask_abs) ||
+ test_bit(ABS_BRAKE, bitmask_abs);
+
+ if (has_abs_coordinates) {
+ if (stylus_or_pen)
+ is_tablet = true;
+ else if (finger_but_no_pen && !is_direct)
+ is_touchpad = true;
+ else if (has_mouse_button)
/* This path is taken by VMware's USB mouse, which has
* absolute axes, but no touch/pressure button. */
- is_mouse = 1;
- else if (test_bit (BTN_TOUCH, bitmask_key))
- udev_builtin_add_property(dev, test, "ID_INPUT_TOUCHSCREEN", "1");
- /* joysticks don't necessarily have to have buttons; e. g.
- * rudders/pedals are joystick-like, but buttonless; they have
- * other fancy axes */
- else if (test_bit (BTN_TRIGGER, bitmask_key) ||
- test_bit (BTN_A, bitmask_key) ||
- test_bit (BTN_1, bitmask_key) ||
- test_bit (ABS_RX, bitmask_abs) ||
- test_bit (ABS_RY, bitmask_abs) ||
- test_bit (ABS_RZ, bitmask_abs) ||
- test_bit (ABS_THROTTLE, bitmask_abs) ||
- test_bit (ABS_RUDDER, bitmask_abs) ||
- test_bit (ABS_WHEEL, bitmask_abs) ||
- test_bit (ABS_GAS, bitmask_abs) ||
- test_bit (ABS_BRAKE, bitmask_abs))
- udev_builtin_add_property(dev, test, "ID_INPUT_JOYSTICK", "1");
+ is_mouse = true;
+ else if (has_touch)
+ is_touchscreen = true;
+ else if (has_joystick_axes_or_buttons)
+ is_joystick = true;
}
+ if (has_mt_coordinates && is_direct)
+ is_touchscreen = true;
- if (test_bit (EV_REL, bitmask_ev) &&
- test_bit (REL_X, bitmask_rel) && test_bit (REL_Y, bitmask_rel) &&
- test_bit (BTN_MOUSE, bitmask_key))
- is_mouse = 1;
+ if (has_rel_coordinates && has_mouse_button)
+ is_mouse = true;
+ if (is_pointing_stick)
+ udev_builtin_add_property(dev, test, "ID_INPUT_POINTINGSTICK", "1");
if (is_mouse)
udev_builtin_add_property(dev, test, "ID_INPUT_MOUSE", "1");
if (is_touchpad)
udev_builtin_add_property(dev, test, "ID_INPUT_TOUCHPAD", "1");
+ if (is_touchscreen)
+ udev_builtin_add_property(dev, test, "ID_INPUT_TOUCHSCREEN", "1");
+ if (is_joystick)
+ udev_builtin_add_property(dev, test, "ID_INPUT_JOYSTICK", "1");
+ if (is_tablet)
+ udev_builtin_add_property(dev, test, "ID_INPUT_TABLET", "1");
+
+ return is_tablet || is_mouse || is_touchpad || is_touchscreen || is_joystick || is_pointing_stick;
}
/* key like devices */
-static void test_key (struct udev_device *dev,
- const unsigned long* bitmask_ev,
- const unsigned long* bitmask_key,
- bool test) {
+static bool test_key(struct udev_device *dev,
+ const unsigned long* bitmask_ev,
+ const unsigned long* bitmask_key,
+ bool test) {
unsigned i;
unsigned long found;
unsigned long mask;
+ bool ret = false;
/* do we have any KEY_* capability? */
- if (!test_bit (EV_KEY, bitmask_ev)) {
+ if (!test_bit(EV_KEY, bitmask_ev)) {
log_debug("test_key: no EV_KEY capability");
- return;
+ return false;
}
/* only consider KEY_* here, not BTN_* */
@@ -208,7 +253,7 @@ static void test_key (struct udev_device *dev,
/* If there are no keys in the lower block, check the higher block */
if (!found) {
for (i = KEY_OK; i < BTN_TRIGGER_HAPPY; ++i) {
- if (test_bit (i, bitmask_key)) {
+ if (test_bit(i, bitmask_key)) {
log_debug("test_key: Found key %x in high block", i);
found = 1;
break;
@@ -216,14 +261,20 @@ static void test_key (struct udev_device *dev,
}
}
- if (found > 0)
+ if (found > 0) {
udev_builtin_add_property(dev, test, "ID_INPUT_KEY", "1");
+ ret = true;
+ }
/* the first 32 bits are ESC, numbers, and Q to D; if we have all of
* those, consider it a full keyboard; do not test KEY_RESERVED, though */
mask = 0xFFFFFFFE;
- if ((bitmask_key[0] & mask) == mask)
+ if ((bitmask_key[0] & mask) == mask) {
udev_builtin_add_property(dev, test, "ID_INPUT_KEYBOARD", "1");
+ ret = true;
+ }
+
+ return ret;
}
static int builtin_input_id(struct udev_device *dev, int argc, char *argv[], bool test) {
@@ -232,7 +283,12 @@ static int builtin_input_id(struct udev_device *dev, int argc, char *argv[], boo
unsigned long bitmask_abs[NBITS(ABS_MAX)];
unsigned long bitmask_key[NBITS(KEY_MAX)];
unsigned long bitmask_rel[NBITS(REL_MAX)];
+ unsigned long bitmask_props[NBITS(INPUT_PROP_MAX)];
const char *sysname, *devnode;
+ bool is_pointer;
+ bool is_key;
+
+ assert(dev);
/* walk up the parental chain until we find the real input device; the
* argument is very likely a subdevice of this, like eventN */
@@ -248,8 +304,15 @@ static int builtin_input_id(struct udev_device *dev, int argc, char *argv[], boo
get_cap_mask(dev, pdev, "capabilities/abs", bitmask_abs, sizeof(bitmask_abs), test);
get_cap_mask(dev, pdev, "capabilities/rel", bitmask_rel, sizeof(bitmask_rel), test);
get_cap_mask(dev, pdev, "capabilities/key", bitmask_key, sizeof(bitmask_key), test);
- test_pointers(dev, bitmask_ev, bitmask_abs, bitmask_key, bitmask_rel, test);
- test_key(dev, bitmask_ev, bitmask_key, test);
+ get_cap_mask(dev, pdev, "properties", bitmask_props, sizeof(bitmask_props), test);
+ is_pointer = test_pointers(dev, bitmask_ev, bitmask_abs,
+ bitmask_key, bitmask_rel,
+ bitmask_props, test);
+ is_key = test_key(dev, bitmask_ev, bitmask_key, test);
+ /* Some evdev nodes have only a scrollwheel */
+ if (!is_pointer && !is_key && test_bit(EV_REL, bitmask_ev) &&
+ (test_bit(REL_WHEEL, bitmask_rel) || test_bit(REL_HWHEEL, bitmask_rel)))
+ udev_builtin_add_property(dev, test, "ID_INPUT_KEY", "1");
}
devnode = udev_device_get_devnode(dev);
diff --git a/src/udev/udev-builtin-keyboard.c b/src/udev/udev-builtin-keyboard.c
index d8ee4cbb61..01f3879f37 100644
--- a/src/udev/udev-builtin-keyboard.c
+++ b/src/udev/udev-builtin-keyboard.c
@@ -18,19 +18,15 @@
***/
#include <stdio.h>
-#include <errno.h>
#include <string.h>
#include <stdlib.h>
-#include <fcntl.h>
#include <sys/ioctl.h>
-#include <linux/limits.h>
#include <linux/input.h>
#include "udev.h"
static const struct key *keyboard_lookup_key(const char *str, unsigned len);
#include "keyboard-keys-from-name.h"
-#include "keyboard-keys-to-name.h"
static int install_force_release(struct udev_device *dev, const unsigned *release, unsigned release_count) {
struct udev_device *atkbd;
@@ -41,6 +37,9 @@ static int install_force_release(struct udev_device *dev, const unsigned *releas
unsigned i;
int ret;
+ assert(dev);
+ assert(release);
+
atkbd = udev_device_get_parent_with_subsystem_devtype(dev, "serio", NULL);
if (!atkbd)
return -ENODEV;
@@ -66,99 +65,205 @@ static int install_force_release(struct udev_device *dev, const unsigned *releas
return ret;
}
-static int builtin_keyboard(struct udev_device *dev, int argc, char *argv[], bool test) {
- struct udev_list_entry *entry;
+static void map_keycode(int fd, const char *devnode, int scancode, const char *keycode)
+{
struct {
unsigned scan;
unsigned key;
- } map[1024];
- unsigned map_count = 0;
+ } map;
+ char *endptr;
+ const struct key *k;
+ unsigned keycode_num;
+
+ /* translate identifier to key code */
+ k = keyboard_lookup_key(keycode, strlen(keycode));
+ if (k) {
+ keycode_num = k->id;
+ } else {
+ /* check if it's a numeric code already */
+ keycode_num = strtoul(keycode, &endptr, 0);
+ if (endptr[0] !='\0') {
+ log_error("Unknown key identifier '%s'", keycode);
+ return;
+ }
+ }
+
+ map.scan = scancode;
+ map.key = keycode_num;
+
+ log_debug("keyboard: mapping scan code %d (0x%x) to key code %d (0x%x)",
+ map.scan, map.scan, map.key, map.key);
+
+ if (ioctl(fd, EVIOCSKEYCODE, &map) < 0)
+ log_error_errno(errno, "Error calling EVIOCSKEYCODE on device node '%s' (scan code 0x%x, key code %d): %m", devnode, map.scan, map.key);
+}
+
+static inline char* parse_token(const char *current, int32_t *val_out) {
+ char *next;
+ int32_t val;
+
+ if (!current)
+ return NULL;
+
+ val = strtol(current, &next, 0);
+ if (*next && *next != ':')
+ return NULL;
+
+ if (next != current)
+ *val_out = val;
+
+ if (*next)
+ next++;
+
+ return next;
+}
+
+static void override_abs(int fd, const char *devnode,
+ unsigned evcode, const char *value) {
+ struct input_absinfo absinfo;
+ int rc;
+ char *next;
+
+ rc = ioctl(fd, EVIOCGABS(evcode), &absinfo);
+ if (rc < 0) {
+ log_error_errno(errno, "Unable to EVIOCGABS device \"%s\"", devnode);
+ return;
+ }
+
+ next = parse_token(value, &absinfo.minimum);
+ next = parse_token(next, &absinfo.maximum);
+ next = parse_token(next, &absinfo.resolution);
+ next = parse_token(next, &absinfo.fuzz);
+ next = parse_token(next, &absinfo.flat);
+ if (!next) {
+ log_error("Unable to parse EV_ABS override '%s' for '%s'", value, devnode);
+ return;
+ }
+
+ log_debug("keyboard: %x overridden with %"PRIi32"/%"PRIi32"/%"PRIi32"/%"PRIi32"/%"PRIi32" for \"%s\"",
+ evcode,
+ absinfo.minimum, absinfo.maximum, absinfo.resolution, absinfo.fuzz, absinfo.flat,
+ devnode);
+ rc = ioctl(fd, EVIOCSABS(evcode), &absinfo);
+ if (rc < 0)
+ log_error_errno(errno, "Unable to EVIOCSABS device \"%s\"", devnode);
+}
+
+static void set_trackpoint_sensitivity(struct udev_device *dev, const char *value)
+{
+ struct udev_device *pdev;
+ char val_s[DECIMAL_STR_MAX(int)];
+ int r, val_i;
+
+ assert(dev);
+ assert(value);
+
+ /* The sensitivity sysfs attr belongs to the serio parent device */
+ pdev = udev_device_get_parent_with_subsystem_devtype(dev, "serio", NULL);
+ if (!pdev) {
+ log_warning("Failed to get serio parent for '%s'", udev_device_get_devnode(dev));
+ return;
+ }
+
+ r = safe_atoi(value, &val_i);
+ if (r < 0) {
+ log_error("Unable to parse POINTINGSTICK_SENSITIVITY '%s' for '%s'", value, udev_device_get_devnode(dev));
+ return;
+ }
+
+ xsprintf(val_s, "%d", val_i);
+
+ r = udev_device_set_sysattr_value(pdev, "sensitivity", val_s);
+ if (r < 0)
+ log_error_errno(r, "Failed to write 'sensitivity' attribute for '%s': %m", udev_device_get_devnode(pdev));
+}
+
+static int open_device(const char *devnode) {
+ int fd;
+
+ fd = open(devnode, O_RDWR|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
+ if (fd < 0)
+ return log_error_errno(errno, "Error opening device \"%s\": %m", devnode);
+
+ return fd;
+}
+
+static int builtin_keyboard(struct udev_device *dev, int argc, char *argv[], bool test) {
+ struct udev_list_entry *entry;
unsigned release[1024];
unsigned release_count = 0;
+ _cleanup_close_ int fd = -1;
+ const char *node;
+
+ node = udev_device_get_devnode(dev);
+ if (!node) {
+ log_error("No device node for \"%s\"", udev_device_get_syspath(dev));
+ return EXIT_FAILURE;
+ }
udev_list_entry_foreach(entry, udev_device_get_properties_list_entry(dev)) {
const char *key;
- unsigned scancode, keycode_num;
char *endptr;
- const char *keycode;
- const struct key *k;
key = udev_list_entry_get_name(entry);
- if (!startswith(key, "KEYBOARD_KEY_"))
- continue;
-
- /* KEYBOARD_KEY_<hex scan code>=<key identifier string> */
- scancode = strtoul(key + 13, &endptr, 16);
- if (endptr[0] != '\0') {
- log_error("Error, unable to parse scan code from '%s'", key);
- continue;
- }
+ if (startswith(key, "KEYBOARD_KEY_")) {
+ const char *keycode;
+ unsigned scancode;
+
+ /* KEYBOARD_KEY_<hex scan code>=<key identifier string> */
+ scancode = strtoul(key + 13, &endptr, 16);
+ if (endptr[0] != '\0') {
+ log_warning("Unable to parse scan code from \"%s\"", key);
+ continue;
+ }
- keycode = udev_list_entry_get_value(entry);
+ keycode = udev_list_entry_get_value(entry);
- /* a leading '!' needs a force-release entry */
- if (keycode[0] == '!') {
- keycode++;
+ /* a leading '!' needs a force-release entry */
+ if (keycode[0] == '!') {
+ keycode++;
- release[release_count] = scancode;
- if (release_count < ELEMENTSOF(release)-1)
- release_count++;
+ release[release_count] = scancode;
+ if (release_count < ELEMENTSOF(release)-1)
+ release_count++;
- if (keycode[0] == '\0')
- continue;
- }
-
- /* translate identifier to key code */
- k = keyboard_lookup_key(keycode, strlen(keycode));
- if (k) {
- keycode_num = k->id;
- } else {
- /* check if it's a numeric code already */
- keycode_num = strtoul(keycode, &endptr, 0);
- if (endptr[0] !='\0') {
- log_error("Error, unknown key identifier '%s'", keycode);
- continue;
+ if (keycode[0] == '\0')
+ continue;
}
- }
- map[map_count].scan = scancode;
- map[map_count].key = keycode_num;
- if (map_count < ELEMENTSOF(map)-1)
- map_count++;
- }
+ if (fd == -1) {
+ fd = open_device(node);
+ if (fd < 0)
+ return EXIT_FAILURE;
+ }
- if (map_count > 0 || release_count > 0) {
- const char *node;
- int fd;
- unsigned i;
+ map_keycode(fd, node, scancode, keycode);
+ } else if (startswith(key, "EVDEV_ABS_")) {
+ unsigned evcode;
- node = udev_device_get_devnode(dev);
- if (!node) {
- log_error("Error, no device node for '%s'", udev_device_get_syspath(dev));
- return EXIT_FAILURE;
- }
+ /* EVDEV_ABS_<EV_ABS code>=<min>:<max>:<res>:<fuzz>:<flat> */
+ evcode = strtoul(key + 10, &endptr, 16);
+ if (endptr[0] != '\0') {
+ log_warning("Unable to parse EV_ABS code from \"%s\"", key);
+ continue;
+ }
- fd = open(udev_device_get_devnode(dev), O_RDWR|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
- if (fd < 0) {
- log_error_errno(errno, "Error, opening device '%s': %m", node);
- return EXIT_FAILURE;
- }
+ if (fd == -1) {
+ fd = open_device(node);
+ if (fd < 0)
+ return EXIT_FAILURE;
+ }
- /* install list of map codes */
- for (i = 0; i < map_count; i++) {
- log_debug("keyboard: mapping scan code %d (0x%x) to key code %d (0x%x)",
- map[i].scan, map[i].scan, map[i].key, map[i].key);
- if (ioctl(fd, EVIOCSKEYCODE, &map[i]) < 0)
- log_error_errno(errno, "Error calling EVIOCSKEYCODE on device node '%s' (scan code 0x%x, key code %d): %m", node, map[i].scan, map[i].key);
+ override_abs(fd, node, evcode, udev_list_entry_get_value(entry));
+ } else if (streq(key, "POINTINGSTICK_SENSITIVITY")) {
+ set_trackpoint_sensitivity(dev, udev_list_entry_get_value(entry));
}
-
- /* install list of force-release codes */
- if (release_count > 0)
- install_force_release(dev, release, release_count);
-
- close(fd);
}
+ /* install list of force-release codes */
+ if (release_count > 0)
+ install_force_release(dev, release, release_count);
+
return EXIT_SUCCESS;
}
diff --git a/src/udev/udev-builtin-kmod.c b/src/udev/udev-builtin-kmod.c
index ad2829e500..81e78a8aa3 100644
--- a/src/udev/udev-builtin-kmod.c
+++ b/src/udev/udev-builtin-kmod.c
@@ -21,12 +21,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
-#include <unistd.h>
-#include <string.h>
#include <errno.h>
-#include <fcntl.h>
-#include <sys/stat.h>
-#include <sys/wait.h>
#include <libkmod.h>
#include "udev.h"
diff --git a/src/udev/udev-builtin-net_id.c b/src/udev/udev-builtin-net_id.c
index 37ff1b8008..6e7e1271fb 100644
--- a/src/udev/udev-builtin-net_id.c
+++ b/src/udev/udev-builtin-net_id.c
@@ -35,7 +35,7 @@
* Type of names:
* b<number> -- BCMA bus core number
* ccw<name> -- CCW bus group name
- * o<index> -- on-board device index number
+ * o<index>[d<dev_port>] -- on-board device index number
* s<slot>[f<function>][d<dev_port>] -- hotplug slot index number
* x<MAC> -- MAC address
* [P<domain>]p<bus>s<slot>[f<function>][d<dev_port>]
@@ -91,6 +91,7 @@
#include <stdlib.h>
#include <stdarg.h>
#include <unistd.h>
+#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <net/if.h>
@@ -128,36 +129,53 @@ struct netnames {
/* retrieve on-board index number and label from firmware */
static int dev_pci_onboard(struct udev_device *dev, struct netnames *names) {
- const char *index;
+ unsigned dev_port = 0;
+ size_t l;
+ char *s;
+ const char *attr;
int idx;
/* ACPI _DSM -- device specific method for naming a PCI or PCI Express device */
- index = udev_device_get_sysattr_value(names->pcidev, "acpi_index");
+ attr = udev_device_get_sysattr_value(names->pcidev, "acpi_index");
/* SMBIOS type 41 -- Onboard Devices Extended Information */
- if (!index)
- index = udev_device_get_sysattr_value(names->pcidev, "index");
- if (!index)
+ if (!attr)
+ attr = udev_device_get_sysattr_value(names->pcidev, "index");
+ if (!attr)
return -ENOENT;
- idx = strtoul(index, NULL, 0);
+
+ idx = strtoul(attr, NULL, 0);
if (idx <= 0)
return -EINVAL;
- snprintf(names->pci_onboard, sizeof(names->pci_onboard), "o%d", idx);
+
+ /* kernel provided port index for multiple ports on a single PCI function */
+ attr = udev_device_get_sysattr_value(dev, "dev_port");
+ if (attr)
+ dev_port = strtol(attr, NULL, 10);
+
+ s = names->pci_onboard;
+ l = sizeof(names->pci_onboard);
+ l = strpcpyf(&s, l, "o%d", idx);
+ if (dev_port > 0)
+ l = strpcpyf(&s, l, "d%d", dev_port);
+ if (l == 0)
+ names->pci_onboard[0] = '\0';
names->pci_onboard_label = udev_device_get_sysattr_value(names->pcidev, "label");
+
return 0;
}
/* read the 256 bytes PCI configuration space to check the multi-function bit */
static bool is_pci_multifunction(struct udev_device *dev) {
- _cleanup_fclose_ FILE *f = NULL;
+ _cleanup_close_ int fd = -1;
const char *filename;
uint8_t config[64];
filename = strjoina(udev_device_get_syspath(dev), "/config");
- f = fopen(filename, "re");
- if (!f)
+ fd = open(filename, O_RDONLY | O_CLOEXEC);
+ if (fd < 0)
return false;
- if (fread(&config, sizeof(config), 1, f) != 1)
+ if (read(fd, &config, sizeof(config)) != sizeof(config))
return false;
/* bit 0-6 header type, bit 7 multi/single function device */
@@ -182,7 +200,7 @@ static int dev_pci_slot(struct udev_device *dev, struct netnames *names) {
if (sscanf(udev_device_get_sysname(names->pcidev), "%x:%x:%x.%u", &domain, &bus, &slot, &func) != 4)
return -ENOENT;
- /* kernel provided multi-device index */
+ /* kernel provided port index for multiple ports on a single PCI function */
attr = udev_device_get_sysattr_value(dev, "dev_port");
if (attr)
dev_port = strtol(attr, NULL, 10);
@@ -248,7 +266,7 @@ static int dev_pci_slot(struct udev_device *dev, struct netnames *names) {
if (dev_port > 0)
l = strpcpyf(&s, l, "d%d", dev_port);
if (l == 0)
- names->pci_path[0] = '\0';
+ names->pci_slot[0] = '\0';
}
out:
udev_device_unref(pci);
@@ -258,6 +276,9 @@ out:
static int names_pci(struct udev_device *dev, struct netnames *names) {
struct udev_device *parent;
+ assert(dev);
+ assert(names);
+
parent = udev_device_get_parent(dev);
if (!parent)
return -ENOENT;
@@ -284,6 +305,9 @@ static int names_usb(struct udev_device *dev, struct netnames *names) {
size_t l;
char *s;
+ assert(dev);
+ assert(names);
+
usbdev = udev_device_get_parent_with_subsystem_devtype(dev, "usb", "usb_interface");
if (!usbdev)
return -ENOENT;
@@ -332,6 +356,9 @@ static int names_bcma(struct udev_device *dev, struct netnames *names) {
struct udev_device *bcmadev;
unsigned int core;
+ assert(dev);
+ assert(names);
+
bcmadev = udev_device_get_parent_with_subsystem_devtype(dev, "bcma", NULL);
if (!bcmadev)
return -ENOENT;
@@ -353,6 +380,9 @@ static int names_ccw(struct udev_device *dev, struct netnames *names) {
size_t bus_id_len;
int rc;
+ assert(dev);
+ assert(names);
+
/* Retrieve the associated CCW device */
cdev = udev_device_get_parent(dev);
if (!cdev)
diff --git a/src/udev/udev-builtin-path_id.c b/src/udev/udev-builtin-path_id.c
index b6749aab76..4ca0a69d7d 100644
--- a/src/udev/udev-builtin-path_id.c
+++ b/src/udev/udev-builtin-path_id.c
@@ -77,6 +77,9 @@ static int format_lun_number(struct udev_device *dev, char **path) {
static struct udev_device *skip_subsystem(struct udev_device *dev, const char *subsys) {
struct udev_device *parent = dev;
+ assert(dev);
+ assert(subsys);
+
while (parent != NULL) {
const char *subsystem;
@@ -96,6 +99,9 @@ static struct udev_device *handle_scsi_fibre_channel(struct udev_device *parent,
const char *port;
char *lun = NULL;
+ assert(parent);
+ assert(path);
+
targetdev = udev_device_get_parent_with_subsystem_devtype(parent, "scsi", "scsi_target");
if (targetdev == NULL)
return NULL;
@@ -126,6 +132,9 @@ static struct udev_device *handle_scsi_sas_wide_port(struct udev_device *parent,
const char *sas_address;
char *lun = NULL;
+ assert(parent);
+ assert(path);
+
targetdev = udev_device_get_parent_with_subsystem_devtype(parent, "scsi", "scsi_target");
if (targetdev == NULL)
return NULL;
@@ -169,6 +178,9 @@ static struct udev_device *handle_scsi_sas(struct udev_device *parent, char **pa
const char *phy_count;
char *lun = NULL;
+ assert(parent);
+ assert(path);
+
targetdev = udev_device_get_parent_with_subsystem_devtype(parent, "scsi", "scsi_target");
if (targetdev == NULL)
return NULL;
@@ -259,6 +271,9 @@ static struct udev_device *handle_scsi_iscsi(struct udev_device *parent, char **
const char *port;
char *lun = NULL;
+ assert(parent);
+ assert(path);
+
/* find iscsi session */
transportdev = parent;
for (;;) {
@@ -316,6 +331,9 @@ static struct udev_device *handle_scsi_default(struct udev_device *parent, char
struct dirent *dent;
int basenum;
+ assert(parent);
+ assert(path);
+
hostdev = udev_device_get_parent_with_subsystem_devtype(parent, "scsi", "scsi_host");
if (hostdev == NULL)
return NULL;
@@ -398,6 +416,9 @@ static struct udev_device *handle_scsi_hyperv(struct udev_device *parent, char *
char guid[38];
size_t i, k;
+ assert(parent);
+ assert(path);
+
hostdev = udev_device_get_parent_with_subsystem_devtype(parent, "scsi", "scsi_host");
if (!hostdev)
return NULL;
@@ -555,6 +576,10 @@ static struct udev_device *handle_bcma(struct udev_device *parent, char **path)
static struct udev_device *handle_ccw(struct udev_device *parent, struct udev_device *dev, char **path) {
struct udev_device *scsi_dev;
+ assert(parent);
+ assert(dev);
+ assert(path);
+
scsi_dev = udev_device_get_parent_with_subsystem_devtype(dev, "scsi", "scsi_device");
if (scsi_dev != NULL) {
const char *wwpn;
@@ -582,6 +607,8 @@ static int builtin_path_id(struct udev_device *dev, int argc, char *argv[], bool
bool supported_transport = false;
bool supported_parent = false;
+ assert(dev);
+
/* S390 ccw bus */
parent = udev_device_get_parent_with_subsystem_devtype(dev, "ccw", NULL);
if (parent != NULL) {
@@ -638,7 +665,8 @@ static int builtin_path_id(struct udev_device *dev, int argc, char *argv[], bool
supported_parent = true;
}
- parent = udev_device_get_parent(parent);
+ if (parent)
+ parent = udev_device_get_parent(parent);
}
/*
diff --git a/src/udev/udev-builtin-uaccess.c b/src/udev/udev-builtin-uaccess.c
index b78c09b910..99bb91ae57 100644
--- a/src/udev/udev-builtin-uaccess.c
+++ b/src/udev/udev-builtin-uaccess.c
@@ -20,14 +20,7 @@
#include <stdio.h>
#include <stdlib.h>
-#include <stdarg.h>
-#include <unistd.h>
-#include <string.h>
-#include <ctype.h>
-#include <fcntl.h>
#include <errno.h>
-#include <dirent.h>
-#include <getopt.h>
#include "systemd/sd-login.h"
#include "logind-acl.h"
diff --git a/src/udev/udev-builtin-usb_id.c b/src/udev/udev-builtin-usb_id.c
index ab0d96e377..d309dc31cb 100644
--- a/src/udev/udev-builtin-usb_id.c
+++ b/src/udev/udev-builtin-usb_id.c
@@ -150,35 +150,35 @@ static int dev_if_packed_info(struct udev_device *dev, char *ifs_str, size_t len
_cleanup_close_ int fd = -1;
ssize_t size;
unsigned char buf[18 + 65535];
- int pos = 0;
+ size_t pos = 0;
unsigned strpos = 0;
struct usb_interface_descriptor {
- u_int8_t bLength;
- u_int8_t bDescriptorType;
- u_int8_t bInterfaceNumber;
- u_int8_t bAlternateSetting;
- u_int8_t bNumEndpoints;
- u_int8_t bInterfaceClass;
- u_int8_t bInterfaceSubClass;
- u_int8_t bInterfaceProtocol;
- u_int8_t iInterface;
- } __attribute__((packed));
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint8_t bInterfaceNumber;
+ uint8_t bAlternateSetting;
+ uint8_t bNumEndpoints;
+ uint8_t bInterfaceClass;
+ uint8_t bInterfaceSubClass;
+ uint8_t bInterfaceProtocol;
+ uint8_t iInterface;
+ } _packed_;
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");
- return -errno;
- }
+ if (fd < 0)
+ return log_debug_errno(errno, "Error opening USB device 'descriptors' file: %m");
size = read(fd, buf, sizeof(buf));
if (size < 18 || size == sizeof(buf))
return -EIO;
ifs_str[0] = '\0';
- while (pos < size && strpos+7 < len-2) {
+ while (pos + sizeof(struct usb_interface_descriptor) < (size_t) size &&
+ strpos + 7 < len - 2) {
+
struct usb_interface_descriptor *desc;
char if_str[8];
@@ -229,17 +229,17 @@ static int dev_if_packed_info(struct udev_device *dev, char *ifs_str, size_t len
* is concatenated with the identification with an underscore '_'.
*/
static int builtin_usb_id(struct udev_device *dev, int argc, char *argv[], bool test) {
- char vendor_str[64];
+ char vendor_str[64] = "";
char vendor_str_enc[256];
const char *vendor_id;
- char model_str[64];
+ char model_str[64] = "";
char model_str_enc[256];
const char *product_id;
- char serial_str[UTIL_NAME_SIZE];
- char packed_if_str[UTIL_NAME_SIZE];
- char revision_str[64];
- char type_str[64];
- char instance_str[64];
+ char serial_str[UTIL_NAME_SIZE] = "";
+ char packed_if_str[UTIL_NAME_SIZE] = "";
+ char revision_str[64] = "";
+ char type_str[64] = "";
+ char instance_str[64] = "";
const char *ifnum = NULL;
const char *driver = NULL;
char serial[256];
@@ -252,13 +252,7 @@ static int builtin_usb_id(struct udev_device *dev, int argc, char *argv[], bool
size_t l;
char *s;
- vendor_str[0] = '\0';
- model_str[0] = '\0';
- serial_str[0] = '\0';
- packed_if_str[0] = '\0';
- revision_str[0] = '\0';
- type_str[0] = '\0';
- instance_str[0] = '\0';
+ assert(dev);
/* shortcut, if we are called directly for a "usb_device" type */
if (udev_device_get_devtype(dev) != NULL && streq(udev_device_get_devtype(dev), "usb_device")) {
@@ -310,7 +304,7 @@ static int builtin_usb_id(struct udev_device *dev, int argc, char *argv[], bool
dev_if_packed_info(dev_usb, packed_if_str, sizeof(packed_if_str));
/* mass storage : SCSI or ATAPI */
- if ((protocol == 6 || protocol == 2)) {
+ if (protocol == 6 || protocol == 2) {
struct udev_device *dev_scsi;
const char *scsi_model, *scsi_vendor, *scsi_type, *scsi_rev;
int host, bus, target, lun;
@@ -438,10 +432,10 @@ fallback:
s = serial;
l = strpcpyl(&s, sizeof(serial), vendor_str, "_", model_str, NULL);
- if (serial_str[0] != '\0')
+ if (!isempty(serial_str))
l = strpcpyl(&s, l, "_", serial_str, NULL);
- if (instance_str[0] != '\0')
+ if (!isempty(instance_str))
strpcpyl(&s, l, "-", instance_str, NULL);
udev_builtin_add_property(dev, test, "ID_VENDOR", vendor_str);
@@ -452,14 +446,14 @@ fallback:
udev_builtin_add_property(dev, test, "ID_MODEL_ID", product_id);
udev_builtin_add_property(dev, test, "ID_REVISION", revision_str);
udev_builtin_add_property(dev, test, "ID_SERIAL", serial);
- if (serial_str[0] != '\0')
+ if (!isempty(serial_str))
udev_builtin_add_property(dev, test, "ID_SERIAL_SHORT", serial_str);
- if (type_str[0] != '\0')
+ if (!isempty(type_str))
udev_builtin_add_property(dev, test, "ID_TYPE", type_str);
- if (instance_str[0] != '\0')
+ if (!isempty(instance_str))
udev_builtin_add_property(dev, test, "ID_INSTANCE", instance_str);
udev_builtin_add_property(dev, test, "ID_BUS", "usb");
- if (packed_if_str[0] != '\0')
+ if (!isempty(packed_if_str))
udev_builtin_add_property(dev, test, "ID_USB_INTERFACES", packed_if_str);
if (ifnum != NULL)
udev_builtin_add_property(dev, test, "ID_USB_INTERFACE_NUM", ifnum);
diff --git a/src/udev/udev-builtin.c b/src/udev/udev-builtin.c
index 1950ec23a1..fabc653800 100644
--- a/src/udev/udev-builtin.c
+++ b/src/udev/udev-builtin.c
@@ -17,12 +17,8 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <unistd.h>
#include <stdio.h>
-#include <stdlib.h>
-#include <stddef.h>
#include <string.h>
-#include <errno.h>
#include <getopt.h>
#include "udev.h"
@@ -127,12 +123,7 @@ int udev_builtin_run(struct udev_device *dev, enum udev_builtin_cmd cmd, const c
}
int udev_builtin_add_property(struct udev_device *dev, bool test, const char *key, const char *val) {
- struct udev_list_entry *entry;
-
- entry = udev_device_add_property(dev, key, val);
- /* store in db, skip private keys */
- if (key[0] != '.')
- udev_list_entry_set_num(entry, true);
+ udev_device_add_property(dev, key, val);
if (test)
printf("%s=%s\n", key, val);
diff --git a/src/udev/udev-ctrl.c b/src/udev/udev-ctrl.c
index 7b5ef6b2a8..b0ad277f73 100644
--- a/src/udev/udev-ctrl.c
+++ b/src/udev/udev-ctrl.c
@@ -10,16 +10,16 @@
*/
#include <errno.h>
-#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <unistd.h>
-#include <sys/types.h>
#include <poll.h>
#include <sys/socket.h>
#include <sys/un.h>
+#include "socket-util.h"
+#include "formats-util.h"
#include "udev.h"
/* wire protocol magic must match */
@@ -57,7 +57,7 @@ struct udev_ctrl {
int refcount;
struct udev *udev;
int sock;
- struct sockaddr_un saddr;
+ union sockaddr_union saddr;
socklen_t addrlen;
bool bound;
bool cleanup_socket;
@@ -96,9 +96,9 @@ struct udev_ctrl *udev_ctrl_new_from_fd(struct udev *udev, int fd) {
if (r < 0)
log_warning_errno(errno, "could not set SO_PASSCRED: %m");
- uctrl->saddr.sun_family = AF_LOCAL;
- 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);
+ uctrl->saddr.un.sun_family = AF_LOCAL;
+ strscpy(uctrl->saddr.un.sun_path, sizeof(uctrl->saddr.un.sun_path), "/run/udev/control");
+ uctrl->addrlen = offsetof(struct sockaddr_un, sun_path) + strlen(uctrl->saddr.un.sun_path);
return uctrl;
}
@@ -110,10 +110,10 @@ int udev_ctrl_enable_receiving(struct udev_ctrl *uctrl) {
int err;
if (!uctrl->bound) {
- err = bind(uctrl->sock, (struct sockaddr *)&uctrl->saddr, uctrl->addrlen);
+ err = bind(uctrl->sock, &uctrl->saddr.sa, uctrl->addrlen);
if (err < 0 && errno == EADDRINUSE) {
- unlink(uctrl->saddr.sun_path);
- err = bind(uctrl->sock, (struct sockaddr *)&uctrl->saddr, uctrl->addrlen);
+ unlink(uctrl->saddr.un.sun_path);
+ err = bind(uctrl->sock, &uctrl->saddr.sa, uctrl->addrlen);
}
if (err < 0) {
@@ -140,21 +140,19 @@ struct udev *udev_ctrl_get_udev(struct udev_ctrl *uctrl) {
}
static struct udev_ctrl *udev_ctrl_ref(struct udev_ctrl *uctrl) {
- if (uctrl == NULL)
- return NULL;
- uctrl->refcount++;
+ if (uctrl)
+ uctrl->refcount++;
+
return uctrl;
}
struct udev_ctrl *udev_ctrl_unref(struct udev_ctrl *uctrl) {
- if (uctrl == NULL)
- return NULL;
- uctrl->refcount--;
- if (uctrl->refcount > 0)
- return uctrl;
- if (uctrl->sock >= 0)
- close(uctrl->sock);
- free(uctrl);
+ if (uctrl && -- uctrl->refcount == 0) {
+ if (uctrl->sock >= 0)
+ close(uctrl->sock);
+ free(uctrl);
+ }
+
return NULL;
}
@@ -162,7 +160,7 @@ int udev_ctrl_cleanup(struct udev_ctrl *uctrl) {
if (uctrl == NULL)
return 0;
if (uctrl->cleanup_socket)
- unlink(uctrl->saddr.sun_path);
+ unlink(uctrl->saddr.un.sun_path);
return 0;
}
@@ -224,15 +222,15 @@ struct udev_ctrl_connection *udev_ctrl_connection_ref(struct udev_ctrl_connectio
}
struct udev_ctrl_connection *udev_ctrl_connection_unref(struct udev_ctrl_connection *conn) {
- if (conn == NULL)
- return NULL;
- conn->refcount--;
- if (conn->refcount > 0)
- return conn;
- if (conn->sock >= 0)
- close(conn->sock);
- udev_ctrl_unref(conn->uctrl);
- free(conn);
+ if (conn && -- conn->refcount == 0) {
+ if (conn->sock >= 0)
+ close(conn->sock);
+
+ udev_ctrl_unref(conn->uctrl);
+
+ free(conn);
+ }
+
return NULL;
}
@@ -251,7 +249,7 @@ static int ctrl_send(struct udev_ctrl *uctrl, enum udev_ctrl_msg_type type, int
ctrl_msg_wire.intval = intval;
if (!uctrl->connected) {
- if (connect(uctrl->sock, (struct sockaddr *)&uctrl->saddr, uctrl->addrlen) < 0) {
+ if (connect(uctrl->sock, &uctrl->saddr.sa, uctrl->addrlen) < 0) {
err = -errno;
goto out;
}
@@ -377,6 +375,9 @@ struct udev_ctrl_msg *udev_ctrl_receive_msg(struct udev_ctrl_connection *conn) {
log_error_errno(errno, "unable to receive ctrl message: %m");
goto err;
}
+
+ cmsg_close_all(&smsg);
+
cmsg = CMSG_FIRSTHDR(&smsg);
cred = (struct ucred *) CMSG_DATA(cmsg);
@@ -402,13 +403,11 @@ err:
}
struct udev_ctrl_msg *udev_ctrl_msg_unref(struct udev_ctrl_msg *ctrl_msg) {
- if (ctrl_msg == NULL)
- return NULL;
- ctrl_msg->refcount--;
- if (ctrl_msg->refcount > 0)
- return ctrl_msg;
- udev_ctrl_connection_unref(ctrl_msg->conn);
- free(ctrl_msg);
+ if (ctrl_msg && -- ctrl_msg->refcount == 0) {
+ udev_ctrl_connection_unref(ctrl_msg->conn);
+ free(ctrl_msg);
+ }
+
return NULL;
}
diff --git a/src/udev/udev-event.c b/src/udev/udev-event.c
index bc115f112d..4dcf8f2e1c 100644
--- a/src/udev/udev-event.c
+++ b/src/udev/udev-event.c
@@ -23,7 +23,6 @@
#include <errno.h>
#include <ctype.h>
#include <string.h>
-#include <time.h>
#include <net/if.h>
#include <sys/prctl.h>
#include <poll.h>
@@ -31,8 +30,19 @@
#include <sys/wait.h>
#include <sys/signalfd.h>
-#include "udev.h"
#include "rtnl-util.h"
+#include "event-util.h"
+#include "formats-util.h"
+#include "process-util.h"
+#include "signal-util.h"
+#include "udev.h"
+
+typedef struct Spawn {
+ const char *cmd;
+ pid_t pid;
+ usec_t timeout_warn;
+ usec_t timeout;
+} Spawn;
struct udev_event *udev_event_new(struct udev_device *dev) {
struct udev *udev = udev_device_get_udev(dev);
@@ -45,8 +55,7 @@ struct udev_event *udev_event_new(struct udev_device *dev) {
event->udev = udev;
udev_list_init(udev, &event->run_list, false);
udev_list_init(udev, &event->seclabel_list, false);
- event->fd_signal = -1;
- event->birth_usec = now(CLOCK_MONOTONIC);
+ event->birth_usec = clock_boottime_or_monotonic();
return event;
}
@@ -110,6 +119,8 @@ size_t udev_event_apply_format(struct udev_event *event, const char *src, char *
char *s;
size_t l;
+ assert(dev);
+
from = src;
s = dest;
l = size;
@@ -374,7 +385,7 @@ out:
}
static int spawn_exec(struct udev_event *event,
- const char *cmd, char *const argv[], char **envp, const sigset_t *sigmask,
+ const char *cmd, char *const argv[], char **envp,
int fd_stdout, int fd_stderr) {
_cleanup_close_ int fd = -1;
@@ -402,9 +413,8 @@ static int spawn_exec(struct udev_event *event,
/* terminate child in case parent goes away */
prctl(PR_SET_PDEATHSIG, SIGTERM);
- /* restore original udev sigmask before exec */
- if (sigmask)
- sigprocmask(SIG_SETMASK, sigmask, NULL);
+ /* restore sigmask before exec */
+ (void) reset_signal_mask();
execve(argv[0], argv, envp);
@@ -467,7 +477,7 @@ static void spawn_read(struct udev_event *event,
if (timeout_usec > 0) {
usec_t age_usec;
- age_usec = now(CLOCK_MONOTONIC) - event->birth_usec;
+ age_usec = clock_boottime_or_monotonic() - event->birth_usec;
if (age_usec >= timeout_usec) {
log_error("timeout '%s'", cmd);
return;
@@ -540,102 +550,116 @@ static void spawn_read(struct udev_event *event,
result[respos] = '\0';
}
+static int on_spawn_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
+ Spawn *spawn = userdata;
+ char timeout[FORMAT_TIMESTAMP_RELATIVE_MAX];
+
+ assert(spawn);
+
+ kill_and_sigcont(spawn->pid, SIGKILL);
+
+ log_error("spawned process '%s' ["PID_FMT"] timed out after %s, killing", spawn->cmd, spawn->pid,
+ format_timestamp_relative(timeout, sizeof(timeout), spawn->timeout));
+
+ return 1;
+}
+
+static int on_spawn_timeout_warning(sd_event_source *s, uint64_t usec, void *userdata) {
+ Spawn *spawn = userdata;
+ char timeout[FORMAT_TIMESTAMP_RELATIVE_MAX];
+
+ assert(spawn);
+
+ log_warning("spawned process '%s' ["PID_FMT"] is taking longer than %s to complete", spawn->cmd, spawn->pid,
+ format_timestamp_relative(timeout, sizeof(timeout), spawn->timeout));
+
+ return 1;
+}
+
+static int on_spawn_sigchld(sd_event_source *s, const siginfo_t *si, void *userdata) {
+ Spawn *spawn = userdata;
+
+ assert(spawn);
+
+ switch (si->si_code) {
+ case CLD_EXITED:
+ if (si->si_status != 0)
+ log_warning("process '%s' failed with exit code %i.", spawn->cmd, si->si_status);
+ else {
+ log_debug("process '%s' succeeded.", spawn->cmd);
+ sd_event_exit(sd_event_source_get_event(s), 0);
+
+ return 1;
+ }
+
+ break;
+ case CLD_KILLED:
+ case CLD_DUMPED:
+ log_warning("process '%s' terminated by signal %s.", spawn->cmd, signal_to_string(si->si_status));
+
+ break;
+ default:
+ log_error("process '%s' failed due to unknown reason.", spawn->cmd);
+ }
+
+ sd_event_exit(sd_event_source_get_event(s), -EIO);
+
+ return 1;
+}
+
static int spawn_wait(struct udev_event *event,
usec_t timeout_usec,
usec_t timeout_warn_usec,
const char *cmd, pid_t pid) {
- struct pollfd pfd[1];
- int err = 0;
-
- pfd[0].events = POLLIN;
- pfd[0].fd = event->fd_signal;
+ Spawn spawn = {
+ .cmd = cmd,
+ .pid = pid,
+ };
+ _cleanup_event_unref_ sd_event *e = NULL;
+ int r, ret;
- while (pid > 0) {
- int timeout;
- int timeout_warn = 0;
- int fdcount;
+ r = sd_event_new(&e);
+ if (r < 0)
+ return r;
- if (timeout_usec > 0) {
- usec_t age_usec;
+ if (timeout_usec > 0) {
+ usec_t usec, age_usec;
- age_usec = now(CLOCK_MONOTONIC) - event->birth_usec;
- if (age_usec >= timeout_usec)
- timeout = 1000;
- else {
- if (timeout_warn_usec > 0)
- timeout_warn = ((timeout_warn_usec - age_usec) / USEC_PER_MSEC) + MSEC_PER_SEC;
+ usec = now(clock_boottime_or_monotonic());
+ age_usec = usec - event->birth_usec;
+ if (age_usec < timeout_usec) {
+ if (timeout_warn_usec > 0 && timeout_warn_usec < timeout_usec && age_usec < timeout_warn_usec) {
+ spawn.timeout_warn = timeout_warn_usec - age_usec;
- timeout = ((timeout_usec - timeout_warn_usec - age_usec) / USEC_PER_MSEC) + MSEC_PER_SEC;
+ r = sd_event_add_time(e, NULL, clock_boottime_or_monotonic(),
+ usec + spawn.timeout_warn, USEC_PER_SEC,
+ on_spawn_timeout_warning, &spawn);
+ if (r < 0)
+ return r;
}
- } else {
- timeout = -1;
- }
- fdcount = poll(pfd, 1, timeout_warn);
- if (fdcount < 0) {
- if (errno == EINTR)
- continue;
- err = -errno;
- log_error_errno(errno, "failed to poll: %m");
- goto out;
- }
- if (fdcount == 0) {
- log_warning("slow: '%s' ["PID_FMT"]", cmd, pid);
+ spawn.timeout = timeout_usec - age_usec;
- fdcount = poll(pfd, 1, timeout);
- if (fdcount < 0) {
- if (errno == EINTR)
- continue;
- err = -errno;
- log_error_errno(errno, "failed to poll: %m");
- goto out;
- }
- if (fdcount == 0) {
- log_error("timeout: killing '%s' ["PID_FMT"]", cmd, pid);
- kill(pid, SIGKILL);
- }
+ r = sd_event_add_time(e, NULL, clock_boottime_or_monotonic(),
+ usec + spawn.timeout, USEC_PER_SEC, on_spawn_timeout, &spawn);
+ if (r < 0)
+ return r;
}
+ }
- if (pfd[0].revents & POLLIN) {
- struct signalfd_siginfo fdsi;
- int status;
- ssize_t size;
+ r = sd_event_add_child(e, NULL, pid, WEXITED, on_spawn_sigchld, &spawn);
+ if (r < 0)
+ return r;
- size = read(event->fd_signal, &fdsi, sizeof(struct signalfd_siginfo));
- if (size != sizeof(struct signalfd_siginfo))
- continue;
+ r = sd_event_loop(e);
+ if (r < 0)
+ return r;
- switch (fdsi.ssi_signo) {
- case SIGTERM:
- event->sigterm = true;
- break;
- case SIGCHLD:
- if (waitpid(pid, &status, WNOHANG) < 0)
- break;
- if (WIFEXITED(status)) {
- log_debug("'%s' ["PID_FMT"] exit with return code %i", cmd, pid, WEXITSTATUS(status));
- if (WEXITSTATUS(status) != 0)
- err = -1;
- } else if (WIFSIGNALED(status)) {
- log_error("'%s' ["PID_FMT"] terminated by signal %i (%s)", cmd, pid, WTERMSIG(status), strsignal(WTERMSIG(status)));
- err = -1;
- } else if (WIFSTOPPED(status)) {
- log_error("'%s' ["PID_FMT"] stopped", cmd, pid);
- err = -1;
- } else if (WIFCONTINUED(status)) {
- log_error("'%s' ["PID_FMT"] continued", cmd, pid);
- err = -1;
- } else {
- log_error("'%s' ["PID_FMT"] exit with status 0x%04x", cmd, pid, status);
- err = -1;
- }
- pid = 0;
- break;
- }
- }
- }
-out:
- return err;
+ r = sd_event_get_exit_code(e, &ret);
+ if (r < 0)
+ return r;
+
+ return ret;
}
int udev_build_argv(struct udev *udev, char *cmd, int *argc, char *argv[]) {
@@ -674,7 +698,7 @@ out:
int udev_event_spawn(struct udev_event *event,
usec_t timeout_usec,
usec_t timeout_warn_usec,
- const char *cmd, char **envp, const sigset_t *sigmask,
+ const char *cmd, char **envp,
char *result, size_t ressize) {
int outpipe[2] = {-1, -1};
int errpipe[2] = {-1, -1};
@@ -724,7 +748,7 @@ int udev_event_spawn(struct udev_event *event,
log_debug("starting '%s'", cmd);
- spawn_exec(event, cmd, argv, envp, sigmask,
+ spawn_exec(event, cmd, argv, envp,
outpipe[WRITE_END], errpipe[WRITE_END]);
_exit(2 );
@@ -786,59 +810,41 @@ static int rename_netif(struct udev_event *event) {
void udev_event_execute_rules(struct udev_event *event,
usec_t timeout_usec, usec_t timeout_warn_usec,
struct udev_list *properties_list,
- struct udev_rules *rules,
- const sigset_t *sigmask) {
+ struct udev_rules *rules) {
struct udev_device *dev = event->dev;
if (udev_device_get_subsystem(dev) == NULL)
return;
if (streq(udev_device_get_action(dev), "remove")) {
- udev_device_read_db(dev, NULL);
- udev_device_delete_db(dev);
+ udev_device_read_db(dev);
udev_device_tag_index(dev, NULL, false);
+ udev_device_delete_db(dev);
if (major(udev_device_get_devnum(dev)) != 0)
udev_watch_end(event->udev, dev);
udev_rules_apply_to_event(rules, event,
timeout_usec, timeout_warn_usec,
- properties_list,
- sigmask);
+ properties_list);
if (major(udev_device_get_devnum(dev)) != 0)
udev_node_remove(dev);
} else {
- event->dev_db = udev_device_shallow_clone(dev);
+ event->dev_db = udev_device_clone_with_db(dev);
if (event->dev_db != NULL) {
- udev_device_read_db(event->dev_db, NULL);
- udev_device_set_info_loaded(event->dev_db);
-
/* disable watch during event processing */
if (major(udev_device_get_devnum(dev)) != 0)
udev_watch_end(event->udev, event->dev_db);
}
if (major(udev_device_get_devnum(dev)) == 0 &&
- streq(udev_device_get_action(dev), "move")) {
- struct udev_list_entry *entry;
-
- for ((entry = udev_device_get_properties_list_entry(event->dev_db)); entry; entry = udev_list_entry_get_next(entry)) {
- const char *key, *value;
- struct udev_list_entry *property;
-
- key = udev_list_entry_get_name(entry);
- value = udev_list_entry_get_value(entry);
-
- property = udev_device_add_property(dev, key, value);
- udev_list_entry_set_num(property, true);
- }
- }
+ streq(udev_device_get_action(dev), "move"))
+ udev_device_copy_properties(dev, event->dev_db);
udev_rules_apply_to_event(rules, event,
timeout_usec, timeout_warn_usec,
- properties_list,
- sigmask);
+ properties_list);
/* rename a new network interface, if needed */
if (udev_device_get_ifindex(dev) > 0 && streq(udev_device_get_action(dev), "add") &&
@@ -850,20 +856,12 @@ void udev_event_execute_rules(struct udev_event *event,
log_warning_errno(r, "could not rename interface '%d' from '%s' to '%s': %m", udev_device_get_ifindex(dev),
udev_device_get_sysname(dev), event->name);
else {
- const char *interface_old;
-
- /* remember old name */
- interface_old = udev_device_get_sysname(dev);
-
r = udev_device_rename(dev, event->name);
if (r < 0)
log_warning_errno(r, "renamed interface '%d' from '%s' to '%s', but could not update udev_device: %m",
udev_device_get_ifindex(dev), udev_device_get_sysname(dev), event->name);
- else {
- udev_device_add_property(dev, "INTERFACE_OLD", interface_old);
- udev_device_add_property(dev, "INTERFACE", event->name);
+ else
log_debug("changed devpath to '%s'", udev_device_get_devpath(dev));
- }
}
}
@@ -898,22 +896,18 @@ void udev_event_execute_rules(struct udev_event *event,
}
/* preserve old, or get new initialization timestamp */
- if (event->dev_db != NULL && udev_device_get_usec_initialized(event->dev_db) > 0)
- udev_device_set_usec_initialized(event->dev, udev_device_get_usec_initialized(event->dev_db));
- else if (udev_device_get_usec_initialized(event->dev) == 0)
- udev_device_set_usec_initialized(event->dev, now(CLOCK_MONOTONIC));
+ udev_device_ensure_usec_initialized(event->dev, event->dev_db);
/* (re)write database file */
- udev_device_update_db(dev);
udev_device_tag_index(dev, event->dev_db, true);
+ udev_device_update_db(dev);
udev_device_set_is_initialized(dev);
- udev_device_unref(event->dev_db);
- event->dev_db = NULL;
+ event->dev_db = udev_device_unref(event->dev_db);
}
}
-void udev_event_execute_run(struct udev_event *event, usec_t timeout_usec, usec_t timeout_warn_usec, const sigset_t *sigmask) {
+void udev_event_execute_run(struct udev_event *event, usec_t timeout_usec, usec_t timeout_warn_usec) {
struct udev_list_entry *list_entry;
udev_list_entry_foreach(list_entry, udev_list_get_entry(&event->run_list)) {
@@ -936,7 +930,7 @@ void udev_event_execute_run(struct udev_event *event, usec_t timeout_usec, usec_
udev_event_apply_format(event, cmd, program, sizeof(program));
envp = udev_device_get_properties_envp(event->dev);
- udev_event_spawn(event, timeout_usec, timeout_warn_usec, program, envp, sigmask, NULL, 0);
+ udev_event_spawn(event, timeout_usec, timeout_warn_usec, program, envp, NULL, 0);
}
}
}
diff --git a/src/udev/udev-node.c b/src/udev/udev-node.c
index 793b48469d..d824172b89 100644
--- a/src/udev/udev-node.c
+++ b/src/udev/udev-node.c
@@ -15,7 +15,6 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stddef.h>
@@ -23,15 +22,13 @@
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
-#include <grp.h>
#include <dirent.h>
-#include <sys/time.h>
#include <sys/stat.h>
-#include <sys/types.h>
#include "udev.h"
#include "smack-util.h"
#include "selinux-util.h"
+#include "formats-util.h"
static int node_symlink(struct udev_device *dev, const char *node, const char *slink) {
struct stat stats;
diff --git a/src/udev/udev-rules.c b/src/udev/udev-rules.c
index c9a0197534..915371525f 100644
--- a/src/udev/udev-rules.c
+++ b/src/udev/udev-rules.c
@@ -35,6 +35,7 @@
#include "strbuf.h"
#include "strv.h"
#include "util.h"
+#include "sysctl-util.h"
#define PREALLOC_TOKEN 2048
@@ -128,6 +129,7 @@ enum token_type {
TK_M_DRIVER, /* val */
TK_M_WAITFOR, /* val */
TK_M_ATTR, /* val, attr */
+ TK_M_SYSCTL, /* val, attr */
TK_M_PARENTS_MIN,
TK_M_KERNELS, /* val */
@@ -166,6 +168,7 @@ enum token_type {
TK_A_NAME, /* val */
TK_A_DEVLINK, /* val */
TK_A_ATTR, /* val, attr */
+ TK_A_SYSCTL, /* val, attr */
TK_A_RUN_BUILTIN, /* val, bool */
TK_A_RUN_PROGRAM, /* val, bool */
TK_A_GOTO, /* size_t */
@@ -262,6 +265,7 @@ static const char *token_str(enum token_type type) {
[TK_M_DRIVER] = "M DRIVER",
[TK_M_WAITFOR] = "M WAITFOR",
[TK_M_ATTR] = "M ATTR",
+ [TK_M_SYSCTL] = "M SYSCTL",
[TK_M_PARENTS_MIN] = "M PARENTS_MIN",
[TK_M_KERNELS] = "M KERNELS",
@@ -300,6 +304,7 @@ static const char *token_str(enum token_type type) {
[TK_A_NAME] = "A NAME",
[TK_A_DEVLINK] = "A DEVLINK",
[TK_A_ATTR] = "A ATTR",
+ [TK_A_SYSCTL] = "A SYSCTL",
[TK_A_RUN_BUILTIN] = "A RUN_BUILTIN",
[TK_A_RUN_PROGRAM] = "A RUN_PROGRAM",
[TK_A_GOTO] = "A GOTO",
@@ -363,9 +368,11 @@ static void dump_token(struct udev_rules *rules, struct token *token) {
log_debug("%s %i '%s'", token_str(type), token->key.builtin_cmd, value);
break;
case TK_M_ATTR:
+ case TK_M_SYSCTL:
case TK_M_ATTRS:
case TK_M_ENV:
case TK_A_ATTR:
+ case TK_A_SYSCTL:
case TK_A_ENV:
log_debug("%s %s '%s' '%s'(%s)",
token_str(type), operation_str(op), attr, value, string_glob_str(glob));
@@ -555,7 +562,6 @@ static int import_property_from_string(struct udev_device *dev, char *line) {
char *key;
char *val;
size_t len;
- struct udev_list_entry *entry;
/* find key */
key = line;
@@ -606,10 +612,7 @@ static int import_property_from_string(struct udev_device *dev, char *line) {
val++;
}
- entry = udev_device_add_property(dev, key, val);
- /* store in db, skip private keys */
- if (key[0] != '.')
- udev_list_entry_set_num(entry, true);
+ udev_device_add_property(dev, key, val);
return 0;
}
@@ -630,7 +633,7 @@ static int import_file_into_properties(struct udev_device *dev, const char *file
static int import_program_into_properties(struct udev_event *event,
usec_t timeout_usec,
usec_t timeout_warn_usec,
- const char *program, const sigset_t *sigmask) {
+ const char *program) {
struct udev_device *dev = event->dev;
char **envp;
char result[UTIL_LINE_SIZE];
@@ -638,7 +641,7 @@ static int import_program_into_properties(struct udev_event *event,
int err;
envp = udev_device_get_properties_envp(dev);
- err = udev_event_spawn(event, timeout_usec, timeout_warn_usec, program, envp, sigmask, result, sizeof(result));
+ err = udev_event_spawn(event, timeout_usec, timeout_warn_usec, program, envp, result, sizeof(result));
if (err < 0)
return err;
@@ -661,6 +664,9 @@ static int import_parent_into_properties(struct udev_device *dev, const char *fi
struct udev_device *dev_parent;
struct udev_list_entry *list_entry;
+ assert(dev);
+ assert(filter);
+
dev_parent = udev_device_get_parent(dev);
if (dev_parent == NULL)
return -1;
@@ -670,12 +676,7 @@ static int import_parent_into_properties(struct udev_device *dev, const char *fi
const char *val = udev_list_entry_get_value(list_entry);
if (fnmatch(filter, key, 0) == 0) {
- struct udev_list_entry *entry;
-
- entry = udev_device_add_property(dev, key, val);
- /* store in db, skip private keys */
- if (key[0] != '.')
- udev_list_entry_set_num(entry, true);
+ udev_device_add_property(dev, key, val);
}
}
return 0;
@@ -906,8 +907,10 @@ static int rule_add_key(struct rule_tmp *rule_tmp, enum token_type type,
break;
case TK_M_ENV:
case TK_M_ATTR:
+ case TK_M_SYSCTL:
case TK_M_ATTRS:
case TK_A_ATTR:
+ case TK_A_SYSCTL:
case TK_A_ENV:
case TK_A_SECLABEL:
attr = data;
@@ -1144,11 +1147,27 @@ static int add_rule(struct udev_rules *rules, char *line,
log_error("invalid ATTR operation");
goto invalid;
}
- if (op < OP_MATCH_MAX) {
+ if (op < OP_MATCH_MAX)
rule_add_key(&rule_tmp, TK_M_ATTR, op, value, attr);
- } else {
+ else
rule_add_key(&rule_tmp, TK_A_ATTR, op, value, attr);
+ continue;
+ }
+
+ if (startswith(key, "SYSCTL{")) {
+ attr = get_key_attribute(rules->udev, key + strlen("SYSCTL"));
+ if (attr == NULL) {
+ log_error("error parsing SYSCTL attribute");
+ goto invalid;
+ }
+ if (op == OP_REMOVE) {
+ log_error("invalid SYSCTL operation");
+ goto invalid;
}
+ if (op < OP_MATCH_MAX)
+ rule_add_key(&rule_tmp, TK_M_SYSCTL, op, value, attr);
+ else
+ rule_add_key(&rule_tmp, TK_A_SYSCTL, op, value, attr);
continue;
}
@@ -1876,8 +1895,7 @@ int udev_rules_apply_to_event(struct udev_rules *rules,
struct udev_event *event,
usec_t timeout_usec,
usec_t timeout_warn_usec,
- struct udev_list *properties_list,
- const sigset_t *sigmask) {
+ struct udev_list *properties_list) {
struct token *cur;
struct token *rule;
enum escape_type esc = ESCAPE_UNSET;
@@ -1995,6 +2013,23 @@ int udev_rules_apply_to_event(struct udev_rules *rules,
if (match_attr(rules, event->dev, event, cur) != 0)
goto nomatch;
break;
+ case TK_M_SYSCTL: {
+ char filename[UTIL_PATH_SIZE];
+ _cleanup_free_ char *value = NULL;
+ size_t len;
+
+ udev_event_apply_format(event, rules_str(rules, cur->key.attr_off), filename, sizeof(filename));
+ sysctl_normalize(filename);
+ if (sysctl_read(filename, &value) < 0)
+ goto nomatch;
+
+ len = strlen(value);
+ while (len > 0 && isspace(value[--len]))
+ value[len] = '\0';
+ if (match_key(rules, cur, value) != 0)
+ goto nomatch;
+ break;
+ }
case TK_M_KERNELS:
case TK_M_SUBSYSTEMS:
case TK_M_DRIVERS:
@@ -2096,7 +2131,7 @@ int udev_rules_apply_to_event(struct udev_rules *rules,
rules_str(rules, rule->rule.filename_off),
rule->rule.filename_line);
- if (udev_event_spawn(event, timeout_usec, timeout_warn_usec, program, envp, sigmask, result, sizeof(result)) < 0) {
+ if (udev_event_spawn(event, timeout_usec, timeout_warn_usec, program, envp, result, sizeof(result)) < 0) {
if (cur->key.op != OP_NOMATCH)
goto nomatch;
} else {
@@ -2132,7 +2167,7 @@ int udev_rules_apply_to_event(struct udev_rules *rules,
rules_str(rules, rule->rule.filename_off),
rule->rule.filename_line);
- if (import_program_into_properties(event, timeout_usec, timeout_warn_usec, import, sigmask) != 0)
+ if (import_program_into_properties(event, timeout_usec, timeout_warn_usec, import) != 0)
if (cur->key.op != OP_NOMATCH)
goto nomatch;
break;
@@ -2178,12 +2213,9 @@ int udev_rules_apply_to_event(struct udev_rules *rules,
const char *value;
value = udev_device_get_property_value(event->dev_db, key);
- if (value != NULL) {
- struct udev_list_entry *entry;
-
- entry = udev_device_add_property(event->dev, key, value);
- udev_list_entry_set_num(entry, true);
- } else {
+ if (value != NULL)
+ udev_device_add_property(event->dev, key, value);
+ else {
if (cur->key.op != OP_NOMATCH)
goto nomatch;
}
@@ -2203,13 +2235,10 @@ int udev_rules_apply_to_event(struct udev_rules *rules,
pos = strstr(cmdline, key);
if (pos != NULL) {
- struct udev_list_entry *entry;
-
pos += strlen(key);
if (pos[0] == '\0' || isspace(pos[0])) {
/* we import simple flags as 'FLAG=1' */
- entry = udev_device_add_property(event->dev, key, "1");
- udev_list_entry_set_num(entry, true);
+ udev_device_add_property(event->dev, key, "1");
imported = true;
} else if (pos[0] == '=') {
const char *value;
@@ -2219,8 +2248,7 @@ int udev_rules_apply_to_event(struct udev_rules *rules,
while (pos[0] != '\0' && !isspace(pos[0]))
pos++;
pos[0] = '\0';
- entry = udev_device_add_property(event->dev, key, value);
- udev_list_entry_set_num(entry, true);
+ udev_device_add_property(event->dev, key, value);
imported = true;
}
}
@@ -2393,7 +2421,6 @@ int udev_rules_apply_to_event(struct udev_rules *rules,
char *value = rules_str(rules, cur->key.value_off);
char value_new[UTIL_NAME_SIZE];
const char *value_old = NULL;
- struct udev_list_entry *entry;
if (value[0] == '\0') {
if (cur->key.op == OP_ADD)
@@ -2413,10 +2440,7 @@ int udev_rules_apply_to_event(struct udev_rules *rules,
} else
udev_event_apply_format(event, value, value_new, sizeof(value_new));
- entry = udev_device_add_property(event->dev, name, value_new);
- /* store in db, skip private keys */
- if (name[0] != '.')
- udev_list_entry_set_num(entry, true);
+ udev_device_add_property(event->dev, name, value_new);
break;
}
case TK_A_TAG: {
@@ -2542,6 +2566,21 @@ int udev_rules_apply_to_event(struct udev_rules *rules,
}
break;
}
+ case TK_A_SYSCTL: {
+ char filename[UTIL_PATH_SIZE];
+ char value[UTIL_NAME_SIZE];
+ int r;
+
+ udev_event_apply_format(event, rules_str(rules, cur->key.attr_off), filename, sizeof(filename));
+ sysctl_normalize(filename);
+ udev_event_apply_format(event, rules_str(rules, cur->key.value_off), value, sizeof(value));
+ log_debug("SYSCTL '%s' writing '%s' %s:%u", filename, value,
+ rules_str(rules, rule->rule.filename_off), rule->rule.filename_line);
+ r = sysctl_write(filename, value);
+ if (r < 0)
+ log_error("error writing SYSCTL{%s}='%s': %s", filename, value, strerror(-r));
+ break;
+ }
case TK_A_RUN_BUILTIN:
case TK_A_RUN_PROGRAM: {
struct udev_list_entry *entry;
diff --git a/src/udev/udev-watch.c b/src/udev/udev-watch.c
index 6ba8674d77..15b76dd6ab 100644
--- a/src/udev/udev-watch.c
+++ b/src/udev/udev-watch.c
@@ -17,14 +17,10 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#include <sys/types.h>
#include <errno.h>
-#include <fcntl.h>
#include <stdio.h>
#include <dirent.h>
#include <stddef.h>
-#include <stdlib.h>
-#include <string.h>
#include <unistd.h>
#include <sys/inotify.h>
diff --git a/src/udev/udev.h b/src/udev/udev.h
index dece6eccab..fd8504c424 100644
--- a/src/udev/udev.h
+++ b/src/udev/udev.h
@@ -20,7 +20,6 @@
#include <sys/types.h>
#include <sys/param.h>
-#include <signal.h>
#include "macro.h"
#include "sd-rtnl.h"
@@ -44,11 +43,9 @@ struct udev_event {
struct udev_list run_list;
int exec_delay;
usec_t birth_usec;
- int fd_signal;
sd_rtnl *rtnl;
unsigned int builtin_run;
unsigned int builtin_ret;
- bool sigterm;
bool inotify_watch;
bool inotify_watch_final;
bool group_set;
@@ -75,8 +72,7 @@ struct udev_rules *udev_rules_unref(struct udev_rules *rules);
bool udev_rules_check_timestamp(struct udev_rules *rules);
int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event,
usec_t timeout_usec, usec_t timeout_warn_usec,
- struct udev_list *properties_list,
- const sigset_t *sigmask);
+ struct udev_list *properties_list);
int udev_rules_apply_static_dev_perms(struct udev_rules *rules);
/* udev-event.c */
@@ -88,14 +84,13 @@ int udev_event_apply_subsys_kernel(struct udev_event *event, const char *string,
int udev_event_spawn(struct udev_event *event,
usec_t timeout_usec,
usec_t timeout_warn_usec,
- const char *cmd, char **envp, const sigset_t *sigmask,
+ const char *cmd, char **envp,
char *result, size_t ressize);
void udev_event_execute_rules(struct udev_event *event,
usec_t timeout_usec, usec_t timeout_warn_usec,
struct udev_list *properties_list,
- struct udev_rules *rules,
- const sigset_t *sigset);
-void udev_event_execute_run(struct udev_event *event, usec_t timeout_usec, usec_t timeout_warn_usec, const sigset_t *sigset);
+ struct udev_rules *rules);
+void udev_event_execute_run(struct udev_event *event, usec_t timeout_usec, usec_t timeout_warn_usec);
int udev_build_argv(struct udev *udev, char *cmd, int *argc, char *argv[]);
/* udev-watch.c */
diff --git a/src/udev/udevadm-control.c b/src/udev/udevadm-control.c
index 6af7163d47..78170463b6 100644
--- a/src/udev/udevadm-control.c
+++ b/src/udev/udevadm-control.c
@@ -12,7 +12,6 @@
* GNU General Public License for more details.
*/
-#include <time.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
@@ -20,10 +19,6 @@
#include <string.h>
#include <unistd.h>
#include <getopt.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/wait.h>
-#include <sys/un.h>
#include "udev.h"
#include "udev-util.h"
diff --git a/src/udev/udevadm-hwdb.c b/src/udev/udevadm-hwdb.c
index d65e40c011..00609e31b5 100644
--- a/src/udev/udevadm-hwdb.c
+++ b/src/udev/udevadm-hwdb.c
@@ -18,7 +18,6 @@
***/
#include <stdlib.h>
-#include <unistd.h>
#include <getopt.h>
#include <string.h>
#include <ctype.h>
diff --git a/src/udev/udevadm-info.c b/src/udev/udevadm-info.c
index 0bab01a234..b3d5565c48 100644
--- a/src/udev/udevadm-info.c
+++ b/src/udev/udevadm-info.c
@@ -15,19 +15,16 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stddef.h>
#include <ctype.h>
-#include <stdarg.h>
#include <unistd.h>
#include <dirent.h>
#include <errno.h>
#include <getopt.h>
#include <fcntl.h>
#include <sys/stat.h>
-#include <sys/types.h>
#include "udev.h"
#include "udev-util.h"
@@ -208,17 +205,15 @@ static void cleanup_dir(DIR *dir, mode_t mask, int depth) {
if ((stats.st_mode & mask) != 0)
continue;
if (S_ISDIR(stats.st_mode)) {
- DIR *dir2;
+ _cleanup_closedir_ DIR *dir2;
dir2 = fdopendir(openat(dirfd(dir), dent->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC));
- if (dir2 != NULL) {
+ if (dir2 != NULL)
cleanup_dir(dir2, mask, depth-1);
- closedir(dir2);
- }
- unlinkat(dirfd(dir), dent->d_name, AT_REMOVEDIR);
- } else {
- unlinkat(dirfd(dir), dent->d_name, 0);
- }
+
+ (void) unlinkat(dirfd(dir), dent->d_name, AT_REMOVEDIR);
+ } else
+ (void) unlinkat(dirfd(dir), dent->d_name, 0);
}
}
diff --git a/src/udev/udevadm-monitor.c b/src/udev/udevadm-monitor.c
index 15ded09339..5e93955186 100644
--- a/src/udev/udevadm-monitor.c
+++ b/src/udev/udevadm-monitor.c
@@ -15,25 +15,19 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#include <unistd.h>
#include <stdio.h>
-#include <stdlib.h>
#include <stddef.h>
#include <string.h>
-#include <fcntl.h>
#include <errno.h>
#include <signal.h>
#include <getopt.h>
#include <time.h>
#include <sys/time.h>
-#include <sys/socket.h>
-#include <sys/un.h>
#include <sys/epoll.h>
-#include <linux/types.h>
-#include <linux/netlink.h>
#include "udev.h"
#include "udev-util.h"
+#include "formats-util.h"
static bool udev_exit;
diff --git a/src/udev/udevadm-settle.c b/src/udev/udevadm-settle.c
index fff5de7a8b..79f45610db 100644
--- a/src/udev/udevadm-settle.c
+++ b/src/udev/udevadm-settle.c
@@ -23,17 +23,10 @@
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
-#include <dirent.h>
-#include <fcntl.h>
#include <getopt.h>
-#include <signal.h>
-#include <time.h>
#include <poll.h>
-#include <sys/stat.h>
-#include <sys/types.h>
#include "udev.h"
-#include "udev-util.h"
#include "util.h"
static void help(void) {
@@ -56,6 +49,7 @@ static int adm_settle(struct udev *udev, int argc, char *argv[]) {
{ "quiet", no_argument, NULL, 'q' }, /* removed */
{}
};
+ usec_t deadline;
const char *exists = NULL;
unsigned int timeout = 120;
struct pollfd pfd[1] = { {.fd = -1}, };
@@ -105,13 +99,15 @@ static int adm_settle(struct udev *udev, int argc, char *argv[]) {
return EXIT_FAILURE;
}
+ deadline = now(CLOCK_MONOTONIC) + timeout * USEC_PER_SEC;
+
/* guarantee that the udev daemon isn't pre-processing */
if (getuid() == 0) {
struct udev_ctrl *uctrl;
uctrl = udev_ctrl_new(udev);
if (uctrl != NULL) {
- if (udev_ctrl_send_ping(uctrl, timeout) < 0) {
+ if (udev_ctrl_send_ping(uctrl, MAX(5U, timeout)) < 0) {
log_debug("no connection to daemon");
udev_ctrl_unref(uctrl);
return EXIT_SUCCESS;
@@ -146,6 +142,9 @@ static int adm_settle(struct udev *udev, int argc, char *argv[]) {
break;
}
+ if (now(CLOCK_MONOTONIC) >= deadline)
+ break;
+
/* wake up when queue is empty */
if (poll(pfd, 1, MSEC_PER_SEC) > 0 && pfd[0].revents & POLLIN)
udev_queue_flush(queue);
diff --git a/src/udev/udevadm-test-builtin.c b/src/udev/udevadm-test-builtin.c
index baaeca9352..35a7349439 100644
--- a/src/udev/udevadm-test-builtin.c
+++ b/src/udev/udevadm-test-builtin.c
@@ -17,19 +17,9 @@
#include <stdlib.h>
#include <stddef.h>
-#include <string.h>
#include <stdio.h>
-#include <unistd.h>
#include <errno.h>
-#include <dirent.h>
-#include <fcntl.h>
#include <getopt.h>
-#include <signal.h>
-#include <time.h>
-#include <sys/inotify.h>
-#include <poll.h>
-#include <sys/stat.h>
-#include <sys/types.h>
#include "udev.h"
diff --git a/src/udev/udevadm-test.c b/src/udev/udevadm-test.c
index 4922b5b6ac..d04e618d0d 100644
--- a/src/udev/udevadm-test.c
+++ b/src/udev/udevadm-test.c
@@ -17,13 +17,10 @@
*/
#include <stdlib.h>
-#include <string.h>
#include <stdio.h>
#include <stddef.h>
#include <unistd.h>
#include <errno.h>
-#include <ctype.h>
-#include <fcntl.h>
#include <signal.h>
#include <getopt.h>
#include <sys/signalfd.h>
@@ -120,34 +117,25 @@ static int adm_test(struct udev *udev, int argc, char *argv[]) {
strscpy(filename, sizeof(filename), syspath);
util_remove_trailing_chars(filename, '/');
- dev = udev_device_new_from_syspath(udev, filename);
+ dev = udev_device_new_from_synthetic_event(udev, filename, action);
if (dev == NULL) {
fprintf(stderr, "unable to open device '%s'\n", filename);
rc = 4;
goto out;
}
- /* skip reading of db, but read kernel parameters */
+ /* don't read info from the db */
udev_device_set_info_loaded(dev);
- udev_device_read_uevent_file(dev);
- udev_device_set_action(dev, action);
event = udev_event_new(dev);
sigfillset(&mask);
sigprocmask(SIG_SETMASK, &mask, &sigmask_orig);
- event->fd_signal = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC);
- if (event->fd_signal < 0) {
- fprintf(stderr, "error creating signalfd\n");
- rc = 5;
- goto out;
- }
udev_event_execute_rules(event,
60 * USEC_PER_SEC, 20 * USEC_PER_SEC,
NULL,
- rules,
- &sigmask_orig);
+ rules);
udev_list_entry_foreach(entry, udev_device_get_properties_list_entry(dev))
printf("%s=%s\n", udev_list_entry_get_name(entry), udev_list_entry_get_value(entry));
@@ -159,8 +147,6 @@ static int adm_test(struct udev *udev, int argc, char *argv[]) {
printf("run: '%s'\n", program);
}
out:
- if (event != NULL && event->fd_signal >= 0)
- close(event->fd_signal);
udev_builtin_exit(udev);
return rc;
}
diff --git a/src/udev/udevadm-trigger.c b/src/udev/udevadm-trigger.c
index 4dc756a28b..11e83f355f 100644
--- a/src/udev/udevadm-trigger.c
+++ b/src/udev/udevadm-trigger.c
@@ -15,20 +15,13 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <getopt.h>
#include <errno.h>
-#include <dirent.h>
#include <fcntl.h>
-#include <fnmatch.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/un.h>
#include "udev.h"
#include "udev-util.h"
diff --git a/src/udev/udevadm.c b/src/udev/udevadm.c
index 56cd0cd4ec..b86d8921f3 100644
--- a/src/udev/udevadm.c
+++ b/src/udev/udevadm.c
@@ -16,11 +16,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#include <unistd.h>
#include <stdio.h>
-#include <stdlib.h>
#include <stddef.h>
-#include <string.h>
#include <errno.h>
#include <getopt.h>
diff --git a/src/udev/udevd.c b/src/udev/udevd.c
index 99d4c8983a..eb43091190 100644
--- a/src/udev/udevd.c
+++ b/src/udev/udevd.c
@@ -26,58 +26,70 @@
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
-#include <ctype.h>
#include <fcntl.h>
-#include <time.h>
#include <getopt.h>
-#include <dirent.h>
#include <sys/file.h>
#include <sys/time.h>
#include <sys/prctl.h>
#include <sys/socket.h>
-#include <sys/un.h>
#include <sys/signalfd.h>
#include <sys/epoll.h>
#include <sys/mount.h>
-#include <poll.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/inotify.h>
-#include <sys/utsname.h>
#include "sd-daemon.h"
+#include "sd-event.h"
+
+#include "signal-util.h"
+#include "event-util.h"
#include "rtnl-util.h"
#include "cgroup-util.h"
+#include "process-util.h"
#include "dev-setup.h"
#include "fileio.h"
#include "selinux-util.h"
#include "udev.h"
#include "udev-util.h"
+#include "formats-util.h"
+#include "hashmap.h"
-static struct udev_rules *rules;
-static struct udev_ctrl *udev_ctrl;
-static struct udev_monitor *monitor;
-static int worker_watch[2] = { -1, -1 };
-static int fd_signal = -1;
-static int fd_ep = -1;
-static int fd_inotify = -1;
-static bool stop_exec_queue;
-static bool reload;
-static int children;
static bool arg_debug = false;
static int arg_daemonize = false;
static int arg_resolve_names = 1;
-static int arg_children_max;
+static unsigned arg_children_max;
static int arg_exec_delay;
static usec_t arg_event_timeout_usec = 180 * USEC_PER_SEC;
static usec_t arg_event_timeout_warn_usec = 180 * USEC_PER_SEC / 3;
-static sigset_t sigmask_orig;
-static UDEV_LIST(event_list);
-static UDEV_LIST(worker_list);
-static char *udev_cgroup;
-static struct udev_list properties_list;
-static bool udev_exit;
+
+typedef struct Manager {
+ struct udev *udev;
+ sd_event *event;
+ Hashmap *workers;
+ struct udev_list_node events;
+ const char *cgroup;
+ pid_t pid; /* the process that originally allocated the manager object */
+
+ struct udev_rules *rules;
+ struct udev_list properties;
+
+ struct udev_monitor *monitor;
+ struct udev_ctrl *ctrl;
+ struct udev_ctrl_connection *ctrl_conn_blocking;
+ int fd_inotify;
+ int worker_watch[2];
+
+ sd_event_source *ctrl_event;
+ sd_event_source *uevent_event;
+ sd_event_source *inotify_event;
+
+ usec_t last_usec;
+
+ bool stop_exec_queue:1;
+ bool exit:1;
+} Manager;
enum event_state {
EVENT_UNDEF,
@@ -87,10 +99,12 @@ enum event_state {
struct event {
struct udev_list_node node;
+ Manager *manager;
struct udev *udev;
struct udev_device *dev;
+ struct udev_device *dev_kernel;
+ struct worker *worker;
enum event_state state;
- int exitcode;
unsigned long long int delaying_seqnum;
unsigned long long int seqnum;
const char *devpath;
@@ -99,13 +113,15 @@ struct event {
dev_t devnum;
int ifindex;
bool is_block;
+ sd_event_source *timeout_warning;
+ sd_event_source *timeout;
};
static inline struct event *node_to_event(struct udev_list_node *node) {
return container_of(node, struct event, node);
}
-static void event_queue_cleanup(struct udev *udev, enum event_state type);
+static void event_queue_cleanup(Manager *manager, enum event_state type);
enum worker_state {
WORKER_UNDEF,
@@ -115,67 +131,200 @@ enum worker_state {
};
struct worker {
+ Manager *manager;
struct udev_list_node node;
- struct udev *udev;
int refcount;
pid_t pid;
struct udev_monitor *monitor;
enum worker_state state;
struct event *event;
- usec_t event_start_usec;
- bool event_warned;
};
/* passed from worker to main process */
struct worker_message {
- pid_t pid;
- int exitcode;
};
-static inline struct worker *node_to_worker(struct udev_list_node *node) {
- return container_of(node, struct worker, node);
-}
+static void event_free(struct event *event) {
+ int r;
+
+ if (!event)
+ return;
-static void event_queue_delete(struct event *event) {
udev_list_node_remove(&event->node);
udev_device_unref(event->dev);
+ udev_device_unref(event->dev_kernel);
+
+ sd_event_source_unref(event->timeout_warning);
+ sd_event_source_unref(event->timeout);
+
+ if (event->worker)
+ event->worker->event = NULL;
+
+ assert(event->manager);
+
+ if (udev_list_node_is_empty(&event->manager->events)) {
+ /* only clean up the queue from the process that created it */
+ if (event->manager->pid == getpid()) {
+ r = unlink("/run/udev/queue");
+ if (r < 0)
+ log_warning_errno(errno, "could not unlink /run/udev/queue: %m");
+ }
+ }
+
free(event);
}
-static struct worker *worker_ref(struct worker *worker) {
- worker->refcount++;
- return worker;
-}
+static void worker_free(struct worker *worker) {
+ if (!worker)
+ return;
-static void worker_cleanup(struct worker *worker) {
- udev_list_node_remove(&worker->node);
+ assert(worker->manager);
+
+ hashmap_remove(worker->manager->workers, UINT_TO_PTR(worker->pid));
udev_monitor_unref(worker->monitor);
- children--;
+ event_free(worker->event);
+
free(worker);
}
-static void worker_unref(struct worker *worker) {
- worker->refcount--;
- if (worker->refcount > 0)
+static void manager_workers_free(Manager *manager) {
+ struct worker *worker;
+ Iterator i;
+
+ assert(manager);
+
+ HASHMAP_FOREACH(worker, manager->workers, i)
+ worker_free(worker);
+
+ manager->workers = hashmap_free(manager->workers);
+}
+
+static int worker_new(struct worker **ret, Manager *manager, struct udev_monitor *worker_monitor, pid_t pid) {
+ _cleanup_free_ struct worker *worker = NULL;
+ int r;
+
+ assert(ret);
+ assert(manager);
+ assert(worker_monitor);
+ assert(pid > 1);
+
+ worker = new0(struct worker, 1);
+ if (!worker)
+ return -ENOMEM;
+
+ worker->refcount = 1;
+ worker->manager = manager;
+ /* close monitor, but keep address around */
+ udev_monitor_disconnect(worker_monitor);
+ worker->monitor = udev_monitor_ref(worker_monitor);
+ worker->pid = pid;
+
+ r = hashmap_ensure_allocated(&manager->workers, NULL);
+ if (r < 0)
+ return r;
+
+ r = hashmap_put(manager->workers, UINT_TO_PTR(pid), worker);
+ if (r < 0)
+ return r;
+
+ *ret = worker;
+ worker = NULL;
+
+ return 0;
+}
+
+static int on_event_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
+ struct event *event = userdata;
+
+ assert(event);
+ assert(event->worker);
+
+ kill_and_sigcont(event->worker->pid, SIGKILL);
+ event->worker->state = WORKER_KILLED;
+
+ log_error("seq %llu '%s' killed", udev_device_get_seqnum(event->dev), event->devpath);
+
+ return 1;
+}
+
+static int on_event_timeout_warning(sd_event_source *s, uint64_t usec, void *userdata) {
+ struct event *event = userdata;
+
+ assert(event);
+
+ log_warning("seq %llu '%s' is taking a long time", udev_device_get_seqnum(event->dev), event->devpath);
+
+ return 1;
+}
+
+static void worker_attach_event(struct worker *worker, struct event *event) {
+ sd_event *e;
+ uint64_t usec;
+ int r;
+
+ assert(worker);
+ assert(worker->manager);
+ assert(event);
+ assert(!event->worker);
+ assert(!worker->event);
+
+ worker->state = WORKER_RUNNING;
+ worker->event = event;
+ event->state = EVENT_RUNNING;
+ event->worker = worker;
+
+ e = worker->manager->event;
+
+ r = sd_event_now(e, clock_boottime_or_monotonic(), &usec);
+ if (r < 0)
return;
- log_debug("worker ["PID_FMT"] cleaned up", worker->pid);
- worker_cleanup(worker);
+
+ (void) sd_event_add_time(e, &event->timeout_warning, clock_boottime_or_monotonic(),
+ usec + arg_event_timeout_warn_usec, USEC_PER_SEC, on_event_timeout_warning, event);
+
+ (void) sd_event_add_time(e, &event->timeout, clock_boottime_or_monotonic(),
+ usec + arg_event_timeout_usec, USEC_PER_SEC, on_event_timeout, event);
}
-static void worker_list_cleanup(struct udev *udev) {
- struct udev_list_node *loop, *tmp;
+static void manager_free(Manager *manager) {
+ if (!manager)
+ return;
- udev_list_node_foreach_safe(loop, tmp, &worker_list) {
- struct worker *worker = node_to_worker(loop);
+ udev_builtin_exit(manager->udev);
- worker_cleanup(worker);
- }
+ sd_event_source_unref(manager->ctrl_event);
+ sd_event_source_unref(manager->uevent_event);
+ sd_event_source_unref(manager->inotify_event);
+
+ udev_unref(manager->udev);
+ sd_event_unref(manager->event);
+ manager_workers_free(manager);
+ event_queue_cleanup(manager, EVENT_UNDEF);
+
+ udev_monitor_unref(manager->monitor);
+ udev_ctrl_unref(manager->ctrl);
+ udev_ctrl_connection_unref(manager->ctrl_conn_blocking);
+
+ udev_list_cleanup(&manager->properties);
+ udev_rules_unref(manager->rules);
+
+ safe_close(manager->fd_inotify);
+ safe_close_pair(manager->worker_watch);
+
+ free(manager);
}
-static void worker_new(struct event *event) {
+DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free);
+
+static int worker_send_message(int fd) {
+ struct worker_message message = {};
+
+ return loop_write(fd, &message, sizeof(message), false);
+}
+
+static void worker_spawn(Manager *manager, struct event *event) {
struct udev *udev = event->udev;
- struct worker *worker;
- struct udev_monitor *worker_monitor;
+ _cleanup_udev_monitor_unref_ struct udev_monitor *worker_monitor = NULL;
pid_t pid;
/* listen for new events */
@@ -183,69 +332,62 @@ static void worker_new(struct event *event) {
if (worker_monitor == NULL)
return;
/* allow the main daemon netlink address to send devices to the worker */
- udev_monitor_allow_unicast_sender(worker_monitor, monitor);
+ udev_monitor_allow_unicast_sender(worker_monitor, manager->monitor);
udev_monitor_enable_receiving(worker_monitor);
- worker = new0(struct worker, 1);
- if (worker == NULL) {
- udev_monitor_unref(worker_monitor);
- return;
- }
- /* worker + event reference */
- worker->refcount = 2;
- worker->udev = udev;
-
pid = fork();
switch (pid) {
case 0: {
struct udev_device *dev = NULL;
- int fd_monitor;
_cleanup_rtnl_unref_ sd_rtnl *rtnl = NULL;
- struct epoll_event ep_signal, ep_monitor;
+ int fd_monitor;
+ _cleanup_close_ int fd_signal = -1, fd_ep = -1;
+ struct epoll_event ep_signal = { .events = EPOLLIN };
+ struct epoll_event ep_monitor = { .events = EPOLLIN };
sigset_t mask;
- int rc = EXIT_SUCCESS;
+ int r = 0;
/* take initial device from queue */
dev = event->dev;
event->dev = NULL;
- free(worker);
- worker_list_cleanup(udev);
- event_queue_cleanup(udev, EVENT_UNDEF);
- udev_monitor_unref(monitor);
- udev_ctrl_unref(udev_ctrl);
- close(fd_signal);
- close(fd_ep);
- close(worker_watch[READ_END]);
+ unsetenv("NOTIFY_SOCKET");
+
+ manager_workers_free(manager);
+ event_queue_cleanup(manager, EVENT_UNDEF);
+
+ manager->monitor = udev_monitor_unref(manager->monitor);
+ manager->ctrl_conn_blocking = udev_ctrl_connection_unref(manager->ctrl_conn_blocking);
+ manager->ctrl = udev_ctrl_unref(manager->ctrl);
+ manager->ctrl_conn_blocking = udev_ctrl_connection_unref(manager->ctrl_conn_blocking);
+ manager->worker_watch[READ_END] = safe_close(manager->worker_watch[READ_END]);
+
+ manager->ctrl_event = sd_event_source_unref(manager->ctrl_event);
+ manager->uevent_event = sd_event_source_unref(manager->uevent_event);
+ manager->inotify_event = sd_event_source_unref(manager->inotify_event);
+
+ manager->event = sd_event_unref(manager->event);
sigfillset(&mask);
fd_signal = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC);
if (fd_signal < 0) {
- log_error_errno(errno, "error creating signalfd %m");
- rc = 2;
+ r = log_error_errno(errno, "error creating signalfd %m");
goto out;
}
+ ep_signal.data.fd = fd_signal;
+
+ fd_monitor = udev_monitor_get_fd(worker_monitor);
+ ep_monitor.data.fd = fd_monitor;
fd_ep = epoll_create1(EPOLL_CLOEXEC);
if (fd_ep < 0) {
- log_error_errno(errno, "error creating epoll fd: %m");
- rc = 3;
+ r = log_error_errno(errno, "error creating epoll fd: %m");
goto out;
}
- memzero(&ep_signal, sizeof(struct epoll_event));
- ep_signal.events = EPOLLIN;
- ep_signal.data.fd = fd_signal;
-
- fd_monitor = udev_monitor_get_fd(worker_monitor);
- memzero(&ep_monitor, sizeof(struct epoll_event));
- ep_monitor.events = EPOLLIN;
- ep_monitor.data.fd = fd_monitor;
-
if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_signal, &ep_signal) < 0 ||
epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_monitor, &ep_monitor) < 0) {
- log_error_errno(errno, "fail to add fds to epoll: %m");
- rc = 4;
+ r = log_error_errno(errno, "fail to add fds to epoll: %m");
goto out;
}
@@ -257,20 +399,17 @@ static void worker_new(struct event *event) {
for (;;) {
struct udev_event *udev_event;
- struct worker_message msg;
int fd_lock = -1;
- int err = 0;
+
+ assert(dev);
log_debug("seq %llu running", udev_device_get_seqnum(dev));
udev_event = udev_event_new(dev);
if (udev_event == NULL) {
- rc = 5;
+ r = -ENOMEM;
goto out;
}
- /* needed for SIGCHLD/SIGTERM in spawn() */
- udev_event->fd_signal = fd_signal;
-
if (arg_exec_delay > 0)
udev_event->exec_delay = arg_exec_delay;
@@ -295,8 +434,8 @@ static void worker_new(struct event *event) {
fd_lock = open(udev_device_get_devnode(d), O_RDONLY|O_CLOEXEC|O_NOFOLLOW|O_NONBLOCK);
if (fd_lock >= 0 && flock(fd_lock, LOCK_SH|LOCK_NB) < 0) {
log_debug_errno(errno, "Unable to flock(%s), skipping event handling: %m", udev_device_get_devnode(d));
- err = -EWOULDBLOCK;
fd_lock = safe_close(fd_lock);
+ r = -EAGAIN;
goto skip;
}
}
@@ -308,13 +447,11 @@ static void worker_new(struct event *event) {
/* apply rules, create node, symlinks */
udev_event_execute_rules(udev_event,
arg_event_timeout_usec, arg_event_timeout_warn_usec,
- &properties_list,
- rules,
- &sigmask_orig);
+ &manager->properties,
+ manager->rules);
udev_event_execute_run(udev_event,
- arg_event_timeout_usec, arg_event_timeout_warn_usec,
- &sigmask_orig);
+ arg_event_timeout_usec, arg_event_timeout_warn_usec);
if (udev_event->rtnl)
/* in case rtnl was initialized */
@@ -332,22 +469,17 @@ static void worker_new(struct event *event) {
udev_monitor_send_device(worker_monitor, NULL, dev);
skip:
- /* send udevd the result of the event execution */
- memzero(&msg, sizeof(struct worker_message));
- msg.exitcode = err;
- msg.pid = getpid();
- send(worker_watch[WRITE_END], &msg, sizeof(struct worker_message), 0);
+ log_debug("seq %llu processed", udev_device_get_seqnum(dev));
- log_debug("seq %llu processed with %i", udev_device_get_seqnum(dev), err);
+ /* send udevd the result of the event execution */
+ r = worker_send_message(manager->worker_watch[WRITE_END]);
+ if (r < 0)
+ log_error_errno(r, "failed to send result of seq %llu to main daemon: %m",
+ udev_device_get_seqnum(dev));
udev_device_unref(dev);
dev = NULL;
- if (udev_event->sigterm) {
- udev_event_unref(udev_event);
- goto out;
- }
-
udev_event_unref(udev_event);
/* wait for more device messages from main udevd, or term signal */
@@ -360,7 +492,7 @@ skip:
if (fdcount < 0) {
if (errno == EINTR)
continue;
- log_error_errno(errno, "failed to poll: %m");
+ r = log_error_errno(errno, "failed to poll: %m");
goto out;
}
@@ -385,51 +517,45 @@ skip:
}
out:
udev_device_unref(dev);
- safe_close(fd_signal);
- safe_close(fd_ep);
- close(fd_inotify);
- close(worker_watch[WRITE_END]);
- udev_rules_unref(rules);
- udev_builtin_exit(udev);
- udev_monitor_unref(worker_monitor);
- udev_unref(udev);
+ manager_free(manager);
log_close();
- exit(rc);
+ _exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS);
}
case -1:
- udev_monitor_unref(worker_monitor);
event->state = EVENT_QUEUED;
- free(worker);
log_error_errno(errno, "fork of child failed: %m");
break;
default:
- /* close monitor, but keep address around */
- udev_monitor_disconnect(worker_monitor);
- worker->monitor = worker_monitor;
- worker->pid = pid;
- worker->state = WORKER_RUNNING;
- worker->event_start_usec = now(CLOCK_MONOTONIC);
- worker->event_warned = false;
- worker->event = event;
- event->state = EVENT_RUNNING;
- udev_list_node_append(&worker->node, &worker_list);
- children++;
+ {
+ struct worker *worker;
+ int r;
+
+ r = worker_new(&worker, manager, worker_monitor, pid);
+ if (r < 0)
+ return;
+
+ worker_attach_event(worker, event);
+
log_debug("seq %llu forked new worker ["PID_FMT"]", udev_device_get_seqnum(event->dev), pid);
break;
}
+ }
}
-static void event_run(struct event *event) {
- struct udev_list_node *loop;
+static void event_run(Manager *manager, struct event *event) {
+ struct worker *worker;
+ Iterator i;
+
+ assert(manager);
+ assert(event);
- udev_list_node_foreach(loop, &worker_list) {
- struct worker *worker = node_to_worker(loop);
+ HASHMAP_FOREACH(worker, manager->workers, i) {
ssize_t count;
if (worker->state != WORKER_IDLE)
continue;
- count = udev_monitor_send_device(monitor, worker->monitor, event->dev);
+ count = udev_monitor_send_device(manager->monitor, worker->monitor, event->dev);
if (count < 0) {
log_error_errno(errno, "worker ["PID_FMT"] did not accept message %zi (%m), kill it",
worker->pid, count);
@@ -437,34 +563,42 @@ static void event_run(struct event *event) {
worker->state = WORKER_KILLED;
continue;
}
- worker_ref(worker);
- worker->event = event;
- worker->state = WORKER_RUNNING;
- worker->event_start_usec = now(CLOCK_MONOTONIC);
- worker->event_warned = false;
- event->state = EVENT_RUNNING;
+ worker_attach_event(worker, event);
return;
}
- if (children >= arg_children_max) {
+ if (hashmap_size(manager->workers) >= arg_children_max) {
if (arg_children_max > 1)
- log_debug("maximum number (%i) of children reached", children);
+ log_debug("maximum number (%i) of children reached", hashmap_size(manager->workers));
return;
}
/* start new worker and pass initial device */
- worker_new(event);
+ worker_spawn(manager, event);
}
-static int event_queue_insert(struct udev_device *dev) {
+static int event_queue_insert(Manager *manager, struct udev_device *dev) {
struct event *event;
+ int r;
+
+ assert(manager);
+ assert(dev);
+
+ /* only one process can add events to the queue */
+ if (manager->pid == 0)
+ manager->pid = getpid();
+
+ assert(manager->pid == getpid());
event = new0(struct event, 1);
- if (event == NULL)
- return -1;
+ if (!event)
+ return -ENOMEM;
event->udev = udev_device_get_udev(dev);
+ event->manager = manager;
event->dev = dev;
+ event->dev_kernel = udev_device_shallow_clone(dev);
+ udev_device_copy_properties(event->dev_kernel, dev);
event->seqnum = udev_device_get_seqnum(dev);
event->devpath = udev_device_get_devpath(dev);
event->devpath_len = strlen(event->devpath);
@@ -477,16 +611,25 @@ static int event_queue_insert(struct udev_device *dev) {
udev_device_get_action(dev), udev_device_get_subsystem(dev));
event->state = EVENT_QUEUED;
- udev_list_node_append(&event->node, &event_list);
+
+ if (udev_list_node_is_empty(&manager->events)) {
+ r = touch("/run/udev/queue");
+ if (r < 0)
+ log_warning_errno(r, "could not touch /run/udev/queue: %m");
+ }
+
+ udev_list_node_append(&event->node, &manager->events);
+
return 0;
}
-static void worker_kill(struct udev *udev) {
- struct udev_list_node *loop;
+static void manager_kill_workers(Manager *manager) {
+ struct worker *worker;
+ Iterator i;
- udev_list_node_foreach(loop, &worker_list) {
- struct worker *worker = node_to_worker(loop);
+ assert(manager);
+ HASHMAP_FOREACH(worker, manager->workers, i) {
if (worker->state == WORKER_KILLED)
continue;
@@ -496,12 +639,12 @@ static void worker_kill(struct udev *udev) {
}
/* lookup event for identical, parent, child device */
-static bool is_devpath_busy(struct event *event) {
+static bool is_devpath_busy(Manager *manager, struct event *event) {
struct udev_list_node *loop;
size_t common;
/* check if queue contains events we depend on */
- udev_list_node_foreach(loop, &event_list) {
+ udev_list_node_foreach(loop, &manager->events) {
struct event *loop_event = node_to_event(loop);
/* we already found a later event, earlier can not block us, no need to check again */
@@ -567,111 +710,271 @@ static bool is_devpath_busy(struct event *event) {
return false;
}
-static void event_queue_start(struct udev *udev) {
+static int on_exit_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
+ Manager *manager = userdata;
+
+ assert(manager);
+
+ log_error_errno(ETIMEDOUT, "giving up waiting for workers to finish");
+
+ sd_event_exit(manager->event, -ETIMEDOUT);
+
+ return 1;
+}
+
+static void manager_exit(Manager *manager) {
+ uint64_t usec;
+ int r;
+
+ assert(manager);
+
+ manager->exit = true;
+
+ sd_notify(false,
+ "STOPPING=1\n"
+ "STATUS=Starting shutdown...");
+
+ /* close sources of new events and discard buffered events */
+ manager->ctrl = udev_ctrl_unref(manager->ctrl);
+ manager->ctrl_event = sd_event_source_unref(manager->ctrl_event);
+
+ manager->fd_inotify = safe_close(manager->fd_inotify);
+ manager->inotify_event = sd_event_source_unref(manager->inotify_event);
+
+ manager->monitor = udev_monitor_unref(manager->monitor);
+ manager->uevent_event = sd_event_source_unref(manager->uevent_event);
+
+ /* discard queued events and kill workers */
+ event_queue_cleanup(manager, EVENT_QUEUED);
+ manager_kill_workers(manager);
+
+ r = sd_event_now(manager->event, clock_boottime_or_monotonic(), &usec);
+ if (r < 0)
+ return;
+
+ r = sd_event_add_time(manager->event, NULL, clock_boottime_or_monotonic(),
+ usec + 30 * USEC_PER_SEC, USEC_PER_SEC, on_exit_timeout, manager);
+ if (r < 0)
+ return;
+}
+
+/* reload requested, HUP signal received, rules changed, builtin changed */
+static void manager_reload(Manager *manager) {
+
+ assert(manager);
+
+ sd_notify(false,
+ "RELOADING=1\n"
+ "STATUS=Flushing configuration...");
+
+ manager_kill_workers(manager);
+ manager->rules = udev_rules_unref(manager->rules);
+ udev_builtin_exit(manager->udev);
+
+ sd_notify(false,
+ "READY=1\n"
+ "STATUS=Processing...");
+}
+
+static void event_queue_start(Manager *manager) {
struct udev_list_node *loop;
+ usec_t usec;
+ int r;
+
+ assert(manager);
+
+ if (udev_list_node_is_empty(&manager->events) ||
+ manager->exit || manager->stop_exec_queue)
+ return;
- udev_list_node_foreach(loop, &event_list) {
+ r = sd_event_now(manager->event, clock_boottime_or_monotonic(), &usec);
+ if (r >= 0) {
+ /* check for changed config, every 3 seconds at most */
+ if (manager->last_usec == 0 ||
+ (usec - manager->last_usec) > 3 * USEC_PER_SEC) {
+ if (udev_rules_check_timestamp(manager->rules) ||
+ udev_builtin_validate(manager->udev))
+ manager_reload(manager);
+
+ manager->last_usec = usec;
+ }
+ }
+
+ udev_builtin_init(manager->udev);
+
+ if (!manager->rules) {
+ manager->rules = udev_rules_new(manager->udev, arg_resolve_names);
+ if (!manager->rules)
+ return;
+ }
+
+ udev_list_node_foreach(loop, &manager->events) {
struct event *event = node_to_event(loop);
if (event->state != EVENT_QUEUED)
continue;
/* do not start event if parent or child event is still running */
- if (is_devpath_busy(event))
+ if (is_devpath_busy(manager, event))
continue;
- event_run(event);
+ event_run(manager, event);
}
}
-static void event_queue_cleanup(struct udev *udev, enum event_state match_type) {
+static void event_queue_cleanup(Manager *manager, enum event_state match_type) {
struct udev_list_node *loop, *tmp;
- udev_list_node_foreach_safe(loop, tmp, &event_list) {
+ udev_list_node_foreach_safe(loop, tmp, &manager->events) {
struct event *event = node_to_event(loop);
if (match_type != EVENT_UNDEF && match_type != event->state)
continue;
- event_queue_delete(event);
+ event_free(event);
}
}
-static void worker_returned(int fd_worker) {
+static int on_worker(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
+ Manager *manager = userdata;
+
+ assert(manager);
+
for (;;) {
struct worker_message msg;
+ struct iovec iovec = {
+ .iov_base = &msg,
+ .iov_len = sizeof(msg),
+ };
+ union {
+ struct cmsghdr cmsghdr;
+ uint8_t buf[CMSG_SPACE(sizeof(struct ucred))];
+ } control = {};
+ struct msghdr msghdr = {
+ .msg_iov = &iovec,
+ .msg_iovlen = 1,
+ .msg_control = &control,
+ .msg_controllen = sizeof(control),
+ };
+ struct cmsghdr *cmsg;
ssize_t size;
- struct udev_list_node *loop;
+ struct ucred *ucred = NULL;
+ struct worker *worker;
- size = recv(fd_worker, &msg, sizeof(struct worker_message), MSG_DONTWAIT);
- if (size != sizeof(struct worker_message))
- break;
+ size = recvmsg(fd, &msghdr, MSG_DONTWAIT);
+ if (size < 0) {
+ if (errno == EINTR)
+ continue;
+ else if (errno == EAGAIN)
+ /* nothing more to read */
+ break;
- /* lookup worker who sent the signal */
- udev_list_node_foreach(loop, &worker_list) {
- struct worker *worker = node_to_worker(loop);
+ return log_error_errno(errno, "failed to receive message: %m");
+ } else if (size != sizeof(struct worker_message)) {
+ log_warning_errno(EIO, "ignoring worker message with invalid size %zi bytes", size);
+ continue;
+ }
- if (worker->pid != msg.pid)
- continue;
+ for (cmsg = CMSG_FIRSTHDR(&msghdr); cmsg; cmsg = CMSG_NXTHDR(&msghdr, cmsg)) {
+ if (cmsg->cmsg_level == SOL_SOCKET &&
+ cmsg->cmsg_type == SCM_CREDENTIALS &&
+ cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred)))
+ ucred = (struct ucred*) CMSG_DATA(cmsg);
+ }
- /* worker returned */
- if (worker->event) {
- worker->event->exitcode = msg.exitcode;
- event_queue_delete(worker->event);
- worker->event = NULL;
- }
- if (worker->state != WORKER_KILLED)
- worker->state = WORKER_IDLE;
- worker_unref(worker);
- break;
+ if (!ucred || ucred->pid <= 0) {
+ log_warning_errno(EIO, "ignoring worker message without valid PID");
+ continue;
}
+
+ /* lookup worker who sent the signal */
+ worker = hashmap_get(manager->workers, UINT_TO_PTR(ucred->pid));
+ if (!worker) {
+ log_debug("worker ["PID_FMT"] returned, but is no longer tracked", ucred->pid);
+ continue;
+ }
+
+ if (worker->state != WORKER_KILLED)
+ worker->state = WORKER_IDLE;
+
+ /* worker returned */
+ event_free(worker->event);
}
+
+ /* we have free workers, try to schedule events */
+ event_queue_start(manager);
+
+ return 1;
+}
+
+static int on_uevent(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
+ Manager *manager = userdata;
+ struct udev_device *dev;
+ int r;
+
+ assert(manager);
+
+ dev = udev_monitor_receive_device(manager->monitor);
+ if (dev) {
+ udev_device_ensure_usec_initialized(dev, NULL);
+ r = event_queue_insert(manager, dev);
+ if (r < 0)
+ udev_device_unref(dev);
+ else
+ /* we have fresh events, try to schedule them */
+ event_queue_start(manager);
+ }
+
+ return 1;
}
/* receive the udevd message from userspace */
-static struct udev_ctrl_connection *handle_ctrl_msg(struct udev_ctrl *uctrl) {
- struct udev *udev = udev_ctrl_get_udev(uctrl);
- struct udev_ctrl_connection *ctrl_conn;
- struct udev_ctrl_msg *ctrl_msg = NULL;
+static int on_ctrl_msg(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
+ Manager *manager = userdata;
+ _cleanup_udev_ctrl_connection_unref_ struct udev_ctrl_connection *ctrl_conn = NULL;
+ _cleanup_udev_ctrl_msg_unref_ struct udev_ctrl_msg *ctrl_msg = NULL;
const char *str;
int i;
- ctrl_conn = udev_ctrl_get_connection(uctrl);
- if (ctrl_conn == NULL)
- goto out;
+ assert(manager);
+
+ ctrl_conn = udev_ctrl_get_connection(manager->ctrl);
+ if (!ctrl_conn)
+ return 1;
ctrl_msg = udev_ctrl_receive_msg(ctrl_conn);
- if (ctrl_msg == NULL)
- goto out;
+ if (!ctrl_msg)
+ return 1;
i = udev_ctrl_get_set_log_level(ctrl_msg);
if (i >= 0) {
log_debug("udevd message (SET_LOG_LEVEL) received, log_priority=%i", i);
log_set_max_level(i);
- worker_kill(udev);
+ manager_kill_workers(manager);
}
if (udev_ctrl_get_stop_exec_queue(ctrl_msg) > 0) {
log_debug("udevd message (STOP_EXEC_QUEUE) received");
- stop_exec_queue = true;
+ manager->stop_exec_queue = true;
}
if (udev_ctrl_get_start_exec_queue(ctrl_msg) > 0) {
log_debug("udevd message (START_EXEC_QUEUE) received");
- stop_exec_queue = false;
+ manager->stop_exec_queue = false;
+ event_queue_start(manager);
}
if (udev_ctrl_get_reload(ctrl_msg) > 0) {
log_debug("udevd message (RELOAD) received");
- reload = true;
+ manager_reload(manager);
}
str = udev_ctrl_get_set_env(ctrl_msg);
if (str != NULL) {
- char *key;
+ _cleanup_free_ char *key = NULL;
key = strdup(str);
- if (key != NULL) {
+ if (key) {
char *val;
val = strchr(key, '=');
@@ -680,17 +983,15 @@ static struct udev_ctrl_connection *handle_ctrl_msg(struct udev_ctrl *uctrl) {
val = &val[1];
if (val[0] == '\0') {
log_debug("udevd message (ENV) received, unset '%s'", key);
- udev_list_entry_add(&properties_list, key, NULL);
+ udev_list_entry_add(&manager->properties, key, NULL);
} else {
log_debug("udevd message (ENV) received, set '%s=%s'", key, val);
- udev_list_entry_add(&properties_list, key, val);
+ udev_list_entry_add(&manager->properties, key, val);
}
- } else {
+ } else
log_error("wrong key format '%s'", key);
- }
- free(key);
}
- worker_kill(udev);
+ manager_kill_workers(manager);
}
i = udev_ctrl_get_set_children_max(ctrl_msg);
@@ -704,13 +1005,13 @@ static struct udev_ctrl_connection *handle_ctrl_msg(struct udev_ctrl *uctrl) {
if (udev_ctrl_get_exit(ctrl_msg) > 0) {
log_debug("udevd message (EXIT) received");
- udev_exit = true;
- /* keep reference to block the client until we exit */
- udev_ctrl_connection_ref(ctrl_conn);
+ manager_exit(manager);
+ /* keep reference to block the client until we exit
+ TODO: deal with several blocking exit requests */
+ manager->ctrl_conn_blocking = udev_ctrl_connection_ref(ctrl_conn);
}
-out:
- udev_ctrl_msg_unref(ctrl_msg);
- return udev_ctrl_connection_unref(ctrl_conn);
+
+ return 1;
}
static int synthesize_change(struct udev_device *dev) {
@@ -817,189 +1118,284 @@ static int synthesize_change(struct udev_device *dev) {
return 0;
}
-static int handle_inotify(struct udev *udev) {
+static int on_inotify(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
+ Manager *manager = userdata;
union inotify_event_buffer buffer;
struct inotify_event *e;
ssize_t l;
- l = read(fd_inotify, &buffer, sizeof(buffer));
+ assert(manager);
+
+ l = read(fd, &buffer, sizeof(buffer));
if (l < 0) {
if (errno == EAGAIN || errno == EINTR)
- return 0;
+ return 1;
return log_error_errno(errno, "Failed to read inotify fd: %m");
}
FOREACH_INOTIFY_EVENT(e, buffer, l) {
- struct udev_device *dev;
+ _cleanup_udev_device_unref_ struct udev_device *dev = NULL;
- dev = udev_watch_lookup(udev, e->wd);
+ dev = udev_watch_lookup(manager->udev, e->wd);
if (!dev)
continue;
log_debug("inotify event: %x for %s", e->mask, udev_device_get_devnode(dev));
- if (e->mask & IN_CLOSE_WRITE)
+ if (e->mask & IN_CLOSE_WRITE) {
synthesize_change(dev);
- else if (e->mask & IN_IGNORED)
- udev_watch_end(udev, dev);
- udev_device_unref(dev);
+ /* settle might be waiting on us to determine the queue
+ * state. If we just handled an inotify event, we might have
+ * generated a "change" event, but we won't have queued up
+ * the resultant uevent yet. Do that.
+ */
+ on_uevent(NULL, -1, 0, manager);
+ } else if (e->mask & IN_IGNORED)
+ udev_watch_end(manager->udev, dev);
}
- return 0;
+ return 1;
}
-static void handle_signal(struct udev *udev, int signo) {
- switch (signo) {
- case SIGINT:
- case SIGTERM:
- udev_exit = true;
- break;
- case SIGCHLD:
- for (;;) {
- pid_t pid;
- int status;
- struct udev_list_node *loop, *tmp;
+static int on_sigterm(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
+ Manager *manager = userdata;
- pid = waitpid(-1, &status, WNOHANG);
- if (pid <= 0)
- break;
+ assert(manager);
- udev_list_node_foreach_safe(loop, tmp, &worker_list) {
- struct worker *worker = node_to_worker(loop);
-
- if (worker->pid != pid)
- continue;
- log_debug("worker ["PID_FMT"] exit", pid);
-
- if (WIFEXITED(status)) {
- if (WEXITSTATUS(status) != 0)
- log_error("worker ["PID_FMT"] exit with return code %i",
- pid, WEXITSTATUS(status));
- } else if (WIFSIGNALED(status)) {
- log_error("worker ["PID_FMT"] terminated by signal %i (%s)",
- pid, WTERMSIG(status), strsignal(WTERMSIG(status)));
- } else if (WIFSTOPPED(status)) {
- log_error("worker ["PID_FMT"] stopped", pid);
- } else if (WIFCONTINUED(status)) {
- log_error("worker ["PID_FMT"] continued", pid);
- } else {
- log_error("worker ["PID_FMT"] exit with status 0x%04x", pid, status);
- }
+ manager_exit(manager);
- if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
- if (worker->event) {
- log_error("worker ["PID_FMT"] failed while handling '%s'",
- pid, worker->event->devpath);
- worker->event->exitcode = -32;
- event_queue_delete(worker->event);
+ return 1;
+}
- /* drop reference taken for state 'running' */
- worker_unref(worker);
- }
- }
- worker_unref(worker);
- break;
+static int on_sighup(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
+ Manager *manager = userdata;
+
+ assert(manager);
+
+ manager_reload(manager);
+
+ return 1;
+}
+
+static int on_sigchld(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
+ Manager *manager = userdata;
+
+ assert(manager);
+
+ for (;;) {
+ pid_t pid;
+ int status;
+ struct worker *worker;
+
+ pid = waitpid(-1, &status, WNOHANG);
+ if (pid <= 0)
+ break;
+
+ worker = hashmap_get(manager->workers, UINT_TO_PTR(pid));
+ if (!worker) {
+ log_warning("worker ["PID_FMT"] is unknown, ignoring", pid);
+ continue;
+ }
+
+ if (WIFEXITED(status)) {
+ if (WEXITSTATUS(status) == 0)
+ log_debug("worker ["PID_FMT"] exited", pid);
+ else
+ log_warning("worker ["PID_FMT"] exited with return code %i", pid, WEXITSTATUS(status));
+ } else if (WIFSIGNALED(status)) {
+ log_warning("worker ["PID_FMT"] terminated by signal %i (%s)", pid, WTERMSIG(status), strsignal(WTERMSIG(status)));
+ } else if (WIFSTOPPED(status)) {
+ log_info("worker ["PID_FMT"] stopped", pid);
+ continue;
+ } else if (WIFCONTINUED(status)) {
+ log_info("worker ["PID_FMT"] continued", pid);
+ continue;
+ } else
+ log_warning("worker ["PID_FMT"] exit with status 0x%04x", pid, status);
+
+ if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
+ if (worker->event) {
+ log_error("worker ["PID_FMT"] failed while handling '%s'", pid, worker->event->devpath);
+ /* delete state from disk */
+ udev_device_delete_db(worker->event->dev);
+ udev_device_tag_index(worker->event->dev, NULL, false);
+ /* forward kernel event without amending it */
+ udev_monitor_send_device(manager->monitor, NULL, worker->event->dev_kernel);
}
}
- break;
- case SIGHUP:
- reload = true;
- break;
+
+ worker_free(worker);
}
+
+ /* we can start new workers, try to schedule events */
+ event_queue_start(manager);
+
+ return 1;
+}
+
+static int on_post(sd_event_source *s, void *userdata) {
+ Manager *manager = userdata;
+ int r;
+
+ assert(manager);
+
+ if (udev_list_node_is_empty(&manager->events)) {
+ /* no pending events */
+ if (!hashmap_isempty(manager->workers)) {
+ /* there are idle workers */
+ log_debug("cleanup idle workers");
+ manager_kill_workers(manager);
+ } else {
+ /* we are idle */
+ if (manager->exit) {
+ r = sd_event_exit(manager->event, 0);
+ if (r < 0)
+ return r;
+ } else if (manager->cgroup)
+ /* cleanup possible left-over processes in our cgroup */
+ cg_kill(SYSTEMD_CGROUP_CONTROLLER, manager->cgroup, SIGKILL, false, true, NULL);
+ }
+ }
+
+ return 1;
}
-static int systemd_fds(struct udev *udev, int *rctrl, int *rnetlink) {
- int ctrl = -1, netlink = -1;
- int fd, n;
+static int listen_fds(int *rctrl, int *rnetlink) {
+ _cleanup_udev_unref_ struct udev *udev = NULL;
+ int ctrl_fd = -1, netlink_fd = -1;
+ int fd, n, r;
+
+ assert(rctrl);
+ assert(rnetlink);
n = sd_listen_fds(true);
- if (n <= 0)
- return -1;
+ if (n < 0)
+ return n;
for (fd = SD_LISTEN_FDS_START; fd < n + SD_LISTEN_FDS_START; fd++) {
if (sd_is_socket(fd, AF_LOCAL, SOCK_SEQPACKET, -1)) {
- if (ctrl >= 0)
- return -1;
- ctrl = fd;
+ if (ctrl_fd >= 0)
+ return -EINVAL;
+ ctrl_fd = fd;
continue;
}
if (sd_is_socket(fd, AF_NETLINK, SOCK_RAW, -1)) {
- if (netlink >= 0)
- return -1;
- netlink = fd;
+ if (netlink_fd >= 0)
+ return -EINVAL;
+ netlink_fd = fd;
continue;
}
- return -1;
+ return -EINVAL;
}
- if (ctrl < 0 || netlink < 0)
- return -1;
+ if (ctrl_fd < 0) {
+ _cleanup_udev_ctrl_unref_ struct udev_ctrl *ctrl = NULL;
+
+ udev = udev_new();
+ if (!udev)
+ return -ENOMEM;
+
+ ctrl = udev_ctrl_new(udev);
+ if (!ctrl)
+ return log_error_errno(EINVAL, "error initializing udev control socket");
+
+ r = udev_ctrl_enable_receiving(ctrl);
+ if (r < 0)
+ return log_error_errno(EINVAL, "error binding udev control socket");
+
+ fd = udev_ctrl_get_fd(ctrl);
+ if (fd < 0)
+ return log_error_errno(EIO, "could not get ctrl fd");
+
+ ctrl_fd = fcntl(fd, F_DUPFD_CLOEXEC, 3);
+ if (ctrl_fd < 0)
+ return log_error_errno(errno, "could not dup ctrl fd: %m");
+ }
+
+ if (netlink_fd < 0) {
+ _cleanup_udev_monitor_unref_ struct udev_monitor *monitor = NULL;
+
+ if (!udev) {
+ udev = udev_new();
+ if (!udev)
+ return -ENOMEM;
+ }
+
+ monitor = udev_monitor_new_from_netlink(udev, "kernel");
+ if (!monitor)
+ return log_error_errno(EINVAL, "error initializing netlink socket");
+
+ (void) udev_monitor_set_receive_buffer_size(monitor, 128 * 1024 * 1024);
+
+ r = udev_monitor_enable_receiving(monitor);
+ if (r < 0)
+ return log_error_errno(EINVAL, "error binding netlink socket");
+
+ fd = udev_monitor_get_fd(monitor);
+ if (fd < 0)
+ return log_error_errno(netlink_fd, "could not get uevent fd: %m");
+
+ netlink_fd = fcntl(fd, F_DUPFD_CLOEXEC, 3);
+ if (ctrl_fd < 0)
+ return log_error_errno(errno, "could not dup netlink fd: %m");
+ }
+
+ *rctrl = ctrl_fd;
+ *rnetlink = netlink_fd;
- log_debug("ctrl=%i netlink=%i", ctrl, netlink);
- *rctrl = ctrl;
- *rnetlink = netlink;
return 0;
}
/*
* read the kernel command line, in case we need to get into debug mode
- * udev.log-priority=<level> syslog priority
- * udev.children-max=<number of workers> events are fully serialized if set to 1
- * udev.exec-delay=<number of seconds> delay execution of every executed program
+ * udev.log-priority=<level> syslog priority
+ * udev.children-max=<number of workers> events are fully serialized if set to 1
+ * udev.exec-delay=<number of seconds> delay execution of every executed program
+ * udev.event-timeout=<number of seconds> seconds to wait before terminating an event
*/
-static void kernel_cmdline_options(struct udev *udev) {
- _cleanup_free_ char *line = NULL;
- const char *word, *state;
- size_t l;
+static int parse_proc_cmdline_item(const char *key, const char *value) {
int r;
- r = proc_cmdline(&line);
- if (r < 0) {
- log_warning_errno(r, "Failed to read /proc/cmdline, ignoring: %m");
- return;
- }
+ assert(key);
- FOREACH_WORD_QUOTED(word, l, line, state) {
- char *s, *opt, *value;
+ if (!value)
+ return 0;
- s = strndup(word, l);
- if (!s)
- break;
+ if (startswith(key, "rd."))
+ key += strlen("rd.");
- /* accept the same options for the initrd, prefixed with "rd." */
- if (in_initrd() && startswith(s, "rd."))
- opt = s + 3;
- else
- opt = s;
+ if (startswith(key, "udev."))
+ key += strlen("udev.");
+ else
+ return 0;
- if ((value = startswith(opt, "udev.log-priority="))) {
- int prio;
+ if (streq(key, "log-priority")) {
+ int prio;
- prio = util_log_priority(value);
- log_set_max_level(prio);
- } else if ((value = startswith(opt, "udev.children-max="))) {
- r = safe_atoi(value, &arg_children_max);
- if (r < 0)
- log_warning("Invalid udev.children-max ignored: %s", value);
- } else if ((value = startswith(opt, "udev.exec-delay="))) {
- r = safe_atoi(value, &arg_exec_delay);
- if (r < 0)
- log_warning("Invalid udev.exec-delay ignored: %s", value);
- } else if ((value = startswith(opt, "udev.event-timeout="))) {
- r = safe_atou64(value, &arg_event_timeout_usec);
- if (r < 0) {
- log_warning("Invalid udev.event-timeout ignored: %s", value);
- break;
- }
+ prio = util_log_priority(value);
+ log_set_max_level(prio);
+ } else if (streq(key, "children-max")) {
+ r = safe_atou(value, &arg_children_max);
+ if (r < 0)
+ log_warning("invalid udev.children-max ignored: %s", value);
+ } else if (streq(key, "exec-delay")) {
+ r = safe_atoi(value, &arg_exec_delay);
+ if (r < 0)
+ log_warning("invalid udev.exec-delay ignored: %s", value);
+ } else if (streq(key, "event-timeout")) {
+ r = safe_atou64(value, &arg_event_timeout_usec);
+ if (r < 0)
+ log_warning("invalid udev.event-timeout ignored: %s", value);
+ else {
arg_event_timeout_usec *= USEC_PER_SEC;
arg_event_timeout_warn_usec = (arg_event_timeout_usec / 3) ? : 1;
}
-
- free(s);
}
+
+ return 0;
}
static void help(void) {
@@ -1044,7 +1440,7 @@ static int parse_argv(int argc, char *argv[]) {
arg_daemonize = true;
break;
case 'c':
- r = safe_atoi(optarg, &arg_children_max);
+ r = safe_atou(optarg, &arg_children_max);
if (r < 0)
log_warning("Invalid --children-max ignored: %s", optarg);
break;
@@ -1094,23 +1490,127 @@ static int parse_argv(int argc, char *argv[]) {
return 1;
}
+static int manager_new(Manager **ret, int fd_ctrl, int fd_uevent, const char *cgroup) {
+ _cleanup_(manager_freep) Manager *manager = NULL;
+ int r, fd_worker, one = 1;
+
+ assert(ret);
+ assert(fd_ctrl >= 0);
+ assert(fd_uevent >= 0);
+
+ manager = new0(Manager, 1);
+ if (!manager)
+ return log_oom();
+
+ manager->fd_inotify = -1;
+ manager->worker_watch[WRITE_END] = -1;
+ manager->worker_watch[READ_END] = -1;
+
+ manager->udev = udev_new();
+ if (!manager->udev)
+ return log_error_errno(errno, "could not allocate udev context: %m");
+
+ udev_builtin_init(manager->udev);
+
+ manager->rules = udev_rules_new(manager->udev, arg_resolve_names);
+ if (!manager->rules)
+ return log_error_errno(ENOMEM, "error reading rules");
+
+ udev_list_node_init(&manager->events);
+ udev_list_init(manager->udev, &manager->properties, true);
+
+ manager->cgroup = cgroup;
+
+ manager->ctrl = udev_ctrl_new_from_fd(manager->udev, fd_ctrl);
+ if (!manager->ctrl)
+ return log_error_errno(EINVAL, "error taking over udev control socket");
+
+ manager->monitor = udev_monitor_new_from_netlink_fd(manager->udev, "kernel", fd_uevent);
+ if (!manager->monitor)
+ return log_error_errno(EINVAL, "error taking over netlink socket");
+
+ /* unnamed socket from workers to the main daemon */
+ r = socketpair(AF_LOCAL, SOCK_DGRAM|SOCK_CLOEXEC, 0, manager->worker_watch);
+ if (r < 0)
+ return log_error_errno(errno, "error creating socketpair: %m");
+
+ fd_worker = manager->worker_watch[READ_END];
+
+ r = setsockopt(fd_worker, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one));
+ if (r < 0)
+ return log_error_errno(errno, "could not enable SO_PASSCRED: %m");
+
+ manager->fd_inotify = udev_watch_init(manager->udev);
+ if (manager->fd_inotify < 0)
+ return log_error_errno(ENOMEM, "error initializing inotify");
+
+ udev_watch_restore(manager->udev);
+
+ /* block and listen to all signals on signalfd */
+ assert_se(sigprocmask_many(SIG_BLOCK, SIGTERM, SIGINT, SIGHUP, SIGCHLD, -1) == 0);
+
+ r = sd_event_default(&manager->event);
+ if (r < 0)
+ return log_error_errno(errno, "could not allocate event loop: %m");
+
+ r = sd_event_add_signal(manager->event, NULL, SIGINT, on_sigterm, manager);
+ if (r < 0)
+ return log_error_errno(r, "error creating sigint event source: %m");
+
+ r = sd_event_add_signal(manager->event, NULL, SIGTERM, on_sigterm, manager);
+ if (r < 0)
+ return log_error_errno(r, "error creating sigterm event source: %m");
+
+ r = sd_event_add_signal(manager->event, NULL, SIGHUP, on_sighup, manager);
+ if (r < 0)
+ return log_error_errno(r, "error creating sighup event source: %m");
+
+ r = sd_event_add_signal(manager->event, NULL, SIGCHLD, on_sigchld, manager);
+ if (r < 0)
+ return log_error_errno(r, "error creating sigchld event source: %m");
+
+ r = sd_event_set_watchdog(manager->event, true);
+ if (r < 0)
+ return log_error_errno(r, "error creating watchdog event source: %m");
+
+ r = sd_event_add_io(manager->event, &manager->ctrl_event, fd_ctrl, EPOLLIN, on_ctrl_msg, manager);
+ if (r < 0)
+ return log_error_errno(r, "error creating ctrl event source: %m");
+
+ /* This needs to be after the inotify and uevent handling, to make sure
+ * that the ping is send back after fully processing the pending uevents
+ * (including the synthetic ones we may create due to inotify events).
+ */
+ r = sd_event_source_set_priority(manager->ctrl_event, SD_EVENT_PRIORITY_IDLE);
+ if (r < 0)
+ return log_error_errno(r, "cold not set IDLE event priority for ctrl event source: %m");
+
+ r = sd_event_add_io(manager->event, &manager->inotify_event, manager->fd_inotify, EPOLLIN, on_inotify, manager);
+ if (r < 0)
+ return log_error_errno(r, "error creating inotify event source: %m");
+
+ r = sd_event_add_io(manager->event, &manager->uevent_event, fd_uevent, EPOLLIN, on_uevent, manager);
+ if (r < 0)
+ return log_error_errno(r, "error creating uevent event source: %m");
+
+ r = sd_event_add_io(manager->event, NULL, fd_worker, EPOLLIN, on_worker, manager);
+ if (r < 0)
+ return log_error_errno(r, "error creating worker event source: %m");
+
+ r = sd_event_add_post(manager->event, NULL, on_post, manager);
+ if (r < 0)
+ return log_error_errno(r, "error creating post event source: %m");
+
+ *ret = manager;
+ manager = NULL;
+
+ return 0;
+}
+
int main(int argc, char *argv[]) {
- struct udev *udev;
- sigset_t mask;
- int fd_ctrl = -1;
- int fd_netlink = -1;
- int fd_worker = -1;
- struct epoll_event ep_ctrl = { .events = EPOLLIN };
- struct epoll_event ep_inotify = { .events = EPOLLIN };
- struct epoll_event ep_signal = { .events = EPOLLIN };
- struct epoll_event ep_netlink = { .events = EPOLLIN };
- struct epoll_event ep_worker = { .events = EPOLLIN };
- struct udev_ctrl_connection *ctrl_conn = NULL;
- int rc = 1, r;
-
- udev = udev_new();
- if (udev == NULL)
- goto exit;
+ _cleanup_(manager_freep) Manager *manager = NULL;
+ _cleanup_free_ char *cgroup = NULL;
+ int r, fd_ctrl, fd_uevent;
log_set_target(LOG_TARGET_AUTO);
log_parse_environment();
@@ -1120,424 +1620,121 @@ int main(int argc, char *argv[]) {
if (r <= 0)
goto exit;
- kernel_cmdline_options(udev);
+ r = parse_proc_cmdline(parse_proc_cmdline_item);
+ if (r < 0)
+ log_warning_errno(r, "failed to parse kernel command line, ignoring: %m");
if (arg_debug)
log_set_max_level(LOG_DEBUG);
if (getuid() != 0) {
- log_error("root privileges required");
+ r = log_error_errno(EPERM, "root privileges required");
goto exit;
}
- r = mac_selinux_init("/dev");
- if (r < 0) {
- log_error_errno(r, "could not initialize labelling: %m");
- goto exit;
+ if (arg_children_max == 0) {
+ cpu_set_t cpu_set;
+
+ arg_children_max = 8;
+
+ if (sched_getaffinity(0, sizeof (cpu_set), &cpu_set) == 0) {
+ arg_children_max += CPU_COUNT(&cpu_set) * 2;
+ }
+
+ log_debug("set children_max to %u", arg_children_max);
}
/* set umask before creating any file/directory */
r = chdir("/");
if (r < 0) {
- log_error_errno(errno, "could not change dir to /: %m");
+ r = log_error_errno(errno, "could not change dir to /: %m");
goto exit;
}
umask(022);
- udev_list_init(udev, &properties_list, true);
+ r = mac_selinux_init("/dev");
+ if (r < 0) {
+ log_error_errno(r, "could not initialize labelling: %m");
+ goto exit;
+ }
r = mkdir("/run/udev", 0755);
if (r < 0 && errno != EEXIST) {
- log_error_errno(errno, "could not create /run/udev: %m");
+ r = log_error_errno(errno, "could not create /run/udev: %m");
goto exit;
}
- dev_setup(NULL);
-
- /* before opening new files, make sure std{in,out,err} fds are in a sane state */
- if (arg_daemonize) {
- int fd;
-
- fd = open("/dev/null", O_RDWR);
- if (fd >= 0) {
- if (write(STDOUT_FILENO, 0, 0) < 0)
- dup2(fd, STDOUT_FILENO);
- if (write(STDERR_FILENO, 0, 0) < 0)
- dup2(fd, STDERR_FILENO);
- if (fd > STDERR_FILENO)
- close(fd);
- } else {
- log_error("cannot open /dev/null");
- }
- }
-
- if (systemd_fds(udev, &fd_ctrl, &fd_netlink) >= 0) {
- /* get control and netlink socket from systemd */
- udev_ctrl = udev_ctrl_new_from_fd(udev, fd_ctrl);
- if (udev_ctrl == NULL) {
- log_error("error taking over udev control socket");
- rc = 1;
- goto exit;
- }
-
- monitor = udev_monitor_new_from_netlink_fd(udev, "kernel", fd_netlink);
- if (monitor == NULL) {
- log_error("error taking over netlink socket");
- rc = 3;
- goto exit;
- }
-
- /* get our own cgroup, we regularly kill everything udev has left behind */
- if (cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, 0, &udev_cgroup) < 0)
- udev_cgroup = NULL;
- } else {
- /* open control and netlink socket */
- udev_ctrl = udev_ctrl_new(udev);
- if (udev_ctrl == NULL) {
- log_error("error initializing udev control socket");
- rc = 1;
- goto exit;
- }
- fd_ctrl = udev_ctrl_get_fd(udev_ctrl);
-
- monitor = udev_monitor_new_from_netlink(udev, "kernel");
- if (monitor == NULL) {
- log_error("error initializing netlink socket");
- rc = 3;
- goto exit;
- }
- fd_netlink = udev_monitor_get_fd(monitor);
-
- udev_monitor_set_receive_buffer_size(monitor, 128 * 1024 * 1024);
- }
-
- if (udev_monitor_enable_receiving(monitor) < 0) {
- log_error("error binding netlink socket");
- rc = 3;
- goto exit;
- }
+ dev_setup(NULL, UID_INVALID, GID_INVALID);
- if (udev_ctrl_enable_receiving(udev_ctrl) < 0) {
- log_error("error binding udev control socket");
- rc = 1;
- goto exit;
+ if (getppid() == 1) {
+ /* get our own cgroup, we regularly kill everything udev has left behind
+ we only do this on systemd systems, and only if we are directly spawned
+ by PID1. otherwise we are not guaranteed to have a dedicated cgroup */
+ r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, 0, &cgroup);
+ if (r < 0)
+ log_warning_errno(r, "failed to get cgroup: %m");
}
- log_info("starting version " VERSION);
-
- udev_builtin_init(udev);
-
- rules = udev_rules_new(udev, arg_resolve_names);
- if (rules == NULL) {
- log_error("error reading rules");
+ r = listen_fds(&fd_ctrl, &fd_uevent);
+ if (r < 0) {
+ r = log_error_errno(r, "could not listen on fds: %m");
goto exit;
}
- rc = udev_rules_apply_static_dev_perms(rules);
- if (rc < 0)
- log_error_errno(rc, "failed to apply permissions on static device nodes - %m");
-
if (arg_daemonize) {
pid_t pid;
+ log_info("starting version " VERSION);
+
pid = fork();
switch (pid) {
case 0:
break;
case -1:
- log_error_errno(errno, "fork of daemon failed: %m");
- rc = 4;
+ r = log_error_errno(errno, "fork of daemon failed: %m");
goto exit;
default:
- rc = EXIT_SUCCESS;
- goto exit_daemonize;
+ mac_selinux_finish();
+ log_close();
+ _exit(EXIT_SUCCESS);
}
setsid();
write_string_file("/proc/self/oom_score_adj", "-1000");
- } else {
- sd_notify(1, "READY=1");
}
- if (arg_children_max <= 0) {
- cpu_set_t cpu_set;
-
- arg_children_max = 8;
-
- if (sched_getaffinity(0, sizeof (cpu_set), &cpu_set) == 0) {
- arg_children_max += CPU_COUNT(&cpu_set) * 2;
- }
- }
- log_debug("set children_max to %u", arg_children_max);
-
- udev_list_node_init(&event_list);
- udev_list_node_init(&worker_list);
-
- fd_inotify = udev_watch_init(udev);
- if (fd_inotify < 0) {
- log_error("error initializing inotify");
- rc = 4;
- goto exit;
- }
- udev_watch_restore(udev);
-
- /* block and listen to all signals on signalfd */
- sigfillset(&mask);
- sigprocmask(SIG_SETMASK, &mask, &sigmask_orig);
- fd_signal = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC);
- if (fd_signal < 0) {
- log_error("error creating signalfd");
- rc = 5;
+ r = manager_new(&manager, fd_ctrl, fd_uevent, cgroup);
+ if (r < 0) {
+ r = log_error_errno(r, "failed to allocate manager object: %m");
goto exit;
}
- /* unnamed socket from workers to the main daemon */
- if (socketpair(AF_LOCAL, SOCK_DGRAM|SOCK_CLOEXEC, 0, worker_watch) < 0) {
- log_error("error creating socketpair");
- rc = 6;
- goto exit;
- }
- fd_worker = worker_watch[READ_END];
+ r = udev_rules_apply_static_dev_perms(manager->rules);
+ if (r < 0)
+ log_error_errno(r, "failed to apply permissions on static device nodes: %m");
- ep_ctrl.data.fd = fd_ctrl;
- ep_inotify.data.fd = fd_inotify;
- ep_signal.data.fd = fd_signal;
- ep_netlink.data.fd = fd_netlink;
- ep_worker.data.fd = fd_worker;
+ (void) sd_notify(false,
+ "READY=1\n"
+ "STATUS=Processing...");
- fd_ep = epoll_create1(EPOLL_CLOEXEC);
- if (fd_ep < 0) {
- log_error_errno(errno, "error creating epoll fd: %m");
- goto exit;
- }
- if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_ctrl, &ep_ctrl) < 0 ||
- epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_inotify, &ep_inotify) < 0 ||
- epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_signal, &ep_signal) < 0 ||
- epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_netlink, &ep_netlink) < 0 ||
- epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_worker, &ep_worker) < 0) {
- log_error_errno(errno, "fail to add fds to epoll: %m");
+ r = sd_event_loop(manager->event);
+ if (r < 0) {
+ log_error_errno(r, "event loop failed: %m");
goto exit;
}
- for (;;) {
- static usec_t last_usec;
- struct epoll_event ev[8];
- int fdcount;
- int timeout;
- bool is_worker, is_signal, is_inotify, is_netlink, is_ctrl;
- int i;
-
- if (udev_exit) {
- /* close sources of new events and discard buffered events */
- if (fd_ctrl >= 0) {
- epoll_ctl(fd_ep, EPOLL_CTL_DEL, fd_ctrl, NULL);
- fd_ctrl = -1;
- }
- if (monitor != NULL) {
- epoll_ctl(fd_ep, EPOLL_CTL_DEL, fd_netlink, NULL);
- udev_monitor_unref(monitor);
- monitor = NULL;
- }
- if (fd_inotify >= 0) {
- epoll_ctl(fd_ep, EPOLL_CTL_DEL, fd_inotify, NULL);
- close(fd_inotify);
- fd_inotify = -1;
- }
-
- /* discard queued events and kill workers */
- event_queue_cleanup(udev, EVENT_QUEUED);
- worker_kill(udev);
-
- /* exit after all has cleaned up */
- if (udev_list_node_is_empty(&event_list) && children == 0)
- break;
-
- /* timeout at exit for workers to finish */
- timeout = 30 * MSEC_PER_SEC;
- } else if (udev_list_node_is_empty(&event_list) && children == 0) {
- /* we are idle */
- timeout = -1;
-
- /* cleanup possible left-over processes in our cgroup */
- if (udev_cgroup)
- cg_kill(SYSTEMD_CGROUP_CONTROLLER, udev_cgroup, SIGKILL, false, true, NULL);
- } else {
- /* kill idle or hanging workers */
- timeout = 3 * MSEC_PER_SEC;
- }
-
- /* tell settle that we are busy or idle */
- if (!udev_list_node_is_empty(&event_list)) {
- int fd;
-
- fd = open("/run/udev/queue", O_WRONLY|O_CREAT|O_CLOEXEC|O_TRUNC|O_NOFOLLOW, 0444);
- if (fd >= 0)
- close(fd);
- } else {
- unlink("/run/udev/queue");
- }
-
- fdcount = epoll_wait(fd_ep, ev, ELEMENTSOF(ev), timeout);
- if (fdcount < 0)
- continue;
-
- if (fdcount == 0) {
- struct udev_list_node *loop;
-
- /* timeout */
- if (udev_exit) {
- log_error("timeout, giving up waiting for workers to finish");
- break;
- }
-
- /* kill idle workers */
- if (udev_list_node_is_empty(&event_list)) {
- log_debug("cleanup idle workers");
- worker_kill(udev);
- }
-
- /* check for hanging events */
- udev_list_node_foreach(loop, &worker_list) {
- struct worker *worker = node_to_worker(loop);
- usec_t ts;
-
- if (worker->state != WORKER_RUNNING)
- continue;
-
- ts = now(CLOCK_MONOTONIC);
-
- if ((ts - worker->event_start_usec) > arg_event_timeout_warn_usec) {
- if ((ts - worker->event_start_usec) > arg_event_timeout_usec) {
- log_error("worker ["PID_FMT"] %s timeout; kill it", worker->pid, worker->event->devpath);
- kill(worker->pid, SIGKILL);
- worker->state = WORKER_KILLED;
-
- /* drop reference taken for state 'running' */
- worker_unref(worker);
- log_error("seq %llu '%s' killed", udev_device_get_seqnum(worker->event->dev), worker->event->devpath);
- worker->event->exitcode = -64;
- event_queue_delete(worker->event);
- worker->event = NULL;
- } else if (!worker->event_warned) {
- log_warning("worker ["PID_FMT"] %s is taking a long time", worker->pid, worker->event->devpath);
- worker->event_warned = true;
- }
- }
- }
+ sd_event_get_exit_code(manager->event, &r);
- }
-
- is_worker = is_signal = is_inotify = is_netlink = is_ctrl = false;
- for (i = 0; i < fdcount; i++) {
- if (ev[i].data.fd == fd_worker && ev[i].events & EPOLLIN)
- is_worker = true;
- else if (ev[i].data.fd == fd_netlink && ev[i].events & EPOLLIN)
- is_netlink = true;
- else if (ev[i].data.fd == fd_signal && ev[i].events & EPOLLIN)
- is_signal = true;
- else if (ev[i].data.fd == fd_inotify && ev[i].events & EPOLLIN)
- is_inotify = true;
- else if (ev[i].data.fd == fd_ctrl && ev[i].events & EPOLLIN)
- is_ctrl = true;
- }
-
- /* check for changed config, every 3 seconds at most */
- if ((now(CLOCK_MONOTONIC) - last_usec) > 3 * USEC_PER_SEC) {
- if (udev_rules_check_timestamp(rules))
- reload = true;
- if (udev_builtin_validate(udev))
- reload = true;
-
- last_usec = now(CLOCK_MONOTONIC);
- }
-
- /* reload requested, HUP signal received, rules changed, builtin changed */
- if (reload) {
- worker_kill(udev);
- rules = udev_rules_unref(rules);
- udev_builtin_exit(udev);
- reload = false;
- }
-
- /* event has finished */
- if (is_worker)
- worker_returned(fd_worker);
-
- if (is_netlink) {
- struct udev_device *dev;
-
- dev = udev_monitor_receive_device(monitor);
- if (dev != NULL) {
- udev_device_set_usec_initialized(dev, now(CLOCK_MONOTONIC));
- if (event_queue_insert(dev) < 0)
- udev_device_unref(dev);
- }
- }
-
- /* start new events */
- if (!udev_list_node_is_empty(&event_list) && !udev_exit && !stop_exec_queue) {
- udev_builtin_init(udev);
- if (rules == NULL)
- rules = udev_rules_new(udev, arg_resolve_names);
- if (rules != NULL)
- event_queue_start(udev);
- }
-
- if (is_signal) {
- struct signalfd_siginfo fdsi;
- ssize_t size;
-
- size = read(fd_signal, &fdsi, sizeof(struct signalfd_siginfo));
- if (size == sizeof(struct signalfd_siginfo))
- handle_signal(udev, fdsi.ssi_signo);
- }
-
- /* we are shutting down, the events below are not handled anymore */
- if (udev_exit)
- continue;
-
- /* device node watch */
- if (is_inotify)
- handle_inotify(udev);
-
- /*
- * This needs to be after the inotify handling, to make sure,
- * that the ping is send back after the possibly generated
- * "change" events by the inotify device node watch.
- *
- * A single time we may receive a client connection which we need to
- * keep open to block the client. It will be closed right before we
- * exit.
- */
- if (is_ctrl)
- ctrl_conn = handle_ctrl_msg(udev_ctrl);
- }
-
- rc = EXIT_SUCCESS;
exit:
- udev_ctrl_cleanup(udev_ctrl);
- unlink("/run/udev/queue");
-exit_daemonize:
- if (fd_ep >= 0)
- close(fd_ep);
- worker_list_cleanup(udev);
- event_queue_cleanup(udev, EVENT_UNDEF);
- udev_rules_unref(rules);
- udev_builtin_exit(udev);
- if (fd_signal >= 0)
- close(fd_signal);
- if (worker_watch[READ_END] >= 0)
- close(worker_watch[READ_END]);
- if (worker_watch[WRITE_END] >= 0)
- close(worker_watch[WRITE_END]);
- udev_monitor_unref(monitor);
- udev_ctrl_connection_unref(ctrl_conn);
- udev_ctrl_unref(udev_ctrl);
- udev_list_cleanup(&properties_list);
+ sd_notify(false,
+ "STOPPING=1\n"
+ "STATUS=Shutting down...");
+
+ if (manager)
+ udev_ctrl_cleanup(manager->ctrl);
mac_selinux_finish();
- udev_unref(udev);
log_close();
- return rc;
+ return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}
diff --git a/src/udev/v4l_id/v4l_id.c b/src/udev/v4l_id/v4l_id.c
index 0ebe434639..5c57db44c1 100644
--- a/src/udev/v4l_id/v4l_id.c
+++ b/src/udev/v4l_id/v4l_id.c
@@ -36,29 +36,28 @@ int main(int argc, char *argv[]) {
_cleanup_close_ int fd = -1;
char *device;
struct v4l2_capability v2cap;
+ int c;
- for (;;) {
- int option;
+ while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
- option = getopt_long(argc, argv, "h", options, NULL);
- if (option == -1)
- break;
-
- switch (option) {
+ switch (c) {
case 'h':
printf("%s [-h,--help] <device file>\n\n"
"Video4Linux device identification.\n\n"
" -h Print this message\n"
, program_invocation_short_name);
return 0;
+ case '?':
+ return -EINVAL;
+
default:
- return 1;
+ assert_not_reached("Unhandled option");
}
- }
- device = argv[optind];
+ device = argv[optind];
if (device == NULL)
return 2;
+
fd = open(device, O_RDONLY);
if (fd < 0)
return 3;
diff --git a/src/update-done/update-done.c b/src/update-done/update-done.c
index 561963e5eb..01bbde8455 100644
--- a/src/update-done/update-done.c
+++ b/src/update-done/update-done.c
@@ -20,7 +20,6 @@
***/
#include "util.h"
-#include "label.h"
#include "selinux-util.h"
#define MESSAGE \
@@ -29,22 +28,28 @@
"was updated. See systemd-update-done.service(8).\n"
static int apply_timestamp(const char *path, struct timespec *ts) {
- struct timespec twice[2];
+ struct timespec twice[2] = {
+ *ts,
+ *ts
+ };
struct stat st;
assert(path);
assert(ts);
if (stat(path, &st) >= 0) {
- /* Is the timestamp file already newer than the OS? If so, there's nothing to do. */
- if (st.st_mtim.tv_sec > ts->tv_sec ||
- (st.st_mtim.tv_sec == ts->tv_sec && st.st_mtim.tv_nsec >= ts->tv_nsec))
+ /* Is the timestamp file already newer than the OS? If
+ * so, there's nothing to do. We ignore the nanosecond
+ * component of the timestamp, since some file systems
+ * do not support any better accuracy than 1s and we
+ * have no way to identify the accuracy
+ * available. Most notably ext4 on small disks (where
+ * 128 byte inodes are used) does not support better
+ * accuracy than 1s. */
+ if (st.st_mtim.tv_sec > ts->tv_sec)
return 0;
/* It is older? Then let's update it */
- twice[0] = *ts;
- twice[1] = *ts;
-
if (utimensat(AT_FDCWD, path, twice, AT_SYMLINK_NOFOLLOW) < 0) {
if (errno == EROFS)
@@ -75,9 +80,6 @@ static int apply_timestamp(const char *path, struct timespec *ts) {
(void) loop_write(fd, MESSAGE, strlen(MESSAGE), false);
- twice[0] = *ts;
- twice[1] = *ts;
-
if (futimens(fd, twice) < 0)
return log_error_errno(errno, "Failed to update timestamp on %s: %m", path);
} else
diff --git a/src/update-utmp/update-utmp.c b/src/update-utmp/update-utmp.c
index 15da83193b..ea9b0c9c84 100644
--- a/src/update-utmp/update-utmp.c
+++ b/src/update-utmp/update-utmp.c
@@ -19,10 +19,8 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <assert.h>
#include <errno.h>
#include <string.h>
-#include <sys/types.h>
#include <unistd.h>
#ifdef HAVE_AUDIT
@@ -39,6 +37,7 @@
#include "bus-util.h"
#include "bus-error.h"
#include "unit-name.h"
+#include "formats-util.h"
typedef struct Context {
sd_bus *bus;
@@ -80,11 +79,9 @@ static int get_current_runlevel(Context *c) {
* here over the others, since these are the main
* runlevels used on Fedora. It might make sense to
* change the order on some distributions. */
- { '5', SPECIAL_RUNLEVEL5_TARGET },
- { '3', SPECIAL_RUNLEVEL3_TARGET },
- { '4', SPECIAL_RUNLEVEL4_TARGET },
- { '2', SPECIAL_RUNLEVEL2_TARGET },
- { '1', SPECIAL_RESCUE_TARGET },
+ { '5', SPECIAL_GRAPHICAL_TARGET },
+ { '3', SPECIAL_MULTI_USER_TARGET },
+ { '1', SPECIAL_RESCUE_TARGET },
};
_cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
diff --git a/src/shutdownd/Makefile b/src/user-sessions/Makefile
index d0b0e8e008..d0b0e8e008 120000
--- a/src/shutdownd/Makefile
+++ b/src/user-sessions/Makefile
diff --git a/src/login/user-sessions.c b/src/user-sessions/user-sessions.c
index 6edb823e8c..1c31769fde 100644
--- a/src/login/user-sessions.c
+++ b/src/user-sessions/user-sessions.c
@@ -19,7 +19,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <string.h>
#include <unistd.h>
#include <errno.h>
diff --git a/src/vconsole/90-vconsole.rules.in b/src/vconsole/90-vconsole.rules.in
index 062009640c..35b9ad5151 100644
--- a/src/vconsole/90-vconsole.rules.in
+++ b/src/vconsole/90-vconsole.rules.in
@@ -5,7 +5,6 @@
# the Free Software Foundation; either version 2.1 of the License, or
# (at your option) any later version.
-# Kernel resets vconsole state when changing console drivers so run
-# systemd-vconsole-setup when fbcon loads
-
-ACTION=="add", SUBSYSTEM=="graphics", KERNEL=="fbcon", RUN+="@rootlibexecdir@/systemd-vconsole-setup"
+# Each vtcon keeps its own state of fonts.
+#
+ACTION=="add", SUBSYSTEM=="vtconsole", KERNEL=="vtcon*", RUN+="@rootlibexecdir@/systemd-vconsole-setup"
diff --git a/src/vconsole/vconsole-setup.c b/src/vconsole/vconsole-setup.c
index 28371711b6..6c782b3130 100644
--- a/src/vconsole/vconsole-setup.c
+++ b/src/vconsole/vconsole-setup.c
@@ -23,23 +23,20 @@
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
-#include <string.h>
#include <fcntl.h>
-#include <ctype.h>
#include <stdbool.h>
-#include <stdarg.h>
#include <limits.h>
#include <sys/ioctl.h>
-#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"
+#include "process-util.h"
+#include "terminal-util.h"
static bool is_vconsole(int fd) {
unsigned char data[1];
@@ -101,16 +98,14 @@ static int enable_utf8(int fd) {
return r;
}
-static int keymap_load(const char *vc, const char *map, const char *map_toggle, bool utf8, pid_t *_pid) {
+static int keyboard_load_and_wait(const char *vc, const char *map, const char *map_toggle, bool utf8) {
const char *args[8];
- int i = 0;
+ int i = 0, r;
pid_t pid;
- if (isempty(map)) {
- /* An empty map means kernel map */
- *_pid = 0;
- return 0;
- }
+ /* An empty map means kernel map */
+ if (isempty(map))
+ return 1;
args[i++] = KBD_LOADKEYS;
args[i++] = "-q";
@@ -131,20 +126,21 @@ static int keymap_load(const char *vc, const char *map, const char *map_toggle,
_exit(EXIT_FAILURE);
}
- *_pid = pid;
- return 0;
+ r = wait_for_terminate_and_warn(KBD_LOADKEYS, pid, true);
+ if (r < 0)
+ return r;
+
+ return r == 0;
}
-static int font_load(const char *vc, const char *font, const char *map, const char *unimap, pid_t *_pid) {
+static int font_load_and_wait(const char *vc, const char *font, const char *map, const char *unimap) {
const char *args[9];
- int i = 0;
+ int i = 0, r;
pid_t pid;
- if (isempty(font)) {
- /* An empty font means kernel font */
- *_pid = 0;
- return 0;
- }
+ /* An empty font means kernel font */
+ if (isempty(font))
+ return 1;
args[i++] = KBD_SETFONT;
args[i++] = "-C";
@@ -168,8 +164,11 @@ static int font_load(const char *vc, const char *font, const char *map, const ch
_exit(EXIT_FAILURE);
}
- *_pid = pid;
- return 0;
+ r = wait_for_terminate_and_warn(KBD_SETFONT, pid, true);
+ if (r < 0)
+ return r;
+
+ return r == 0;
}
/*
@@ -188,11 +187,13 @@ static void font_copy_to_all_vcs(int fd) {
/* get active, and 16 bit mask of used VT numbers */
r = ioctl(fd, VT_GETSTATE, &vcs);
- if (r < 0)
+ if (r < 0) {
+ log_debug_errno(errno, "VT_GETSTATE failed, ignoring: %m");
return;
+ }
for (i = 1; i <= 15; i++) {
- char vcname[16];
+ char vcname[strlen("/dev/vcs") + DECIMAL_STR_MAX(int)];
_cleanup_close_ int vcfd = -1;
struct console_font_op cfo = {};
@@ -216,11 +217,11 @@ static void font_copy_to_all_vcs(int fd) {
/* copy map of 8bit chars */
if (ioctl(fd, GIO_SCRNMAP, map8) >= 0)
- (void) ioctl(vcfd, PIO_SCRNMAP, map8);
+ (void) ioctl(vcfd, PIO_SCRNMAP, map8);
/* copy map of 8bit chars -> 16bit Unicode values */
if (ioctl(fd, GIO_UNISCRNMAP, map16) >= 0)
- (void) ioctl(vcfd, PIO_UNISCRNMAP, map16);
+ (void) ioctl(vcfd, PIO_UNISCRNMAP, map16);
/* copy unicode translation table */
/* unimapd is a ushort count and a pointer to an
@@ -242,9 +243,7 @@ int main(int argc, char **argv) {
*vc_keymap = NULL, *vc_keymap_toggle = NULL,
*vc_font = NULL, *vc_font_map = NULL, *vc_font_unimap = NULL;
_cleanup_close_ int fd = -1;
- bool utf8;
- pid_t font_pid = 0, keymap_pid = 0;
- bool font_copy = false;
+ bool utf8, font_copy = false, font_ok, keyboard_ok;
int r = EXIT_FAILURE;
log_set_target(LOG_TARGET_AUTO);
@@ -299,31 +298,16 @@ int main(int argc, char **argv) {
}
if (utf8)
- enable_utf8(fd);
+ (void) enable_utf8(fd);
else
- disable_utf8(fd);
-
- r = font_load(vc, vc_font, vc_font_map, vc_font_unimap, &font_pid);
- if (r < 0) {
- log_error_errno(r, "Failed to start " KBD_SETFONT ": %m");
- return EXIT_FAILURE;
- }
-
- if (font_pid > 0)
- wait_for_terminate_and_warn(KBD_SETFONT, font_pid, true);
-
- r = keymap_load(vc, vc_keymap, vc_keymap_toggle, utf8, &keymap_pid);
- if (r < 0) {
- log_error_errno(r, "Failed to start " KBD_LOADKEYS ": %m");
- return EXIT_FAILURE;
- }
+ (void) disable_utf8(fd);
- if (keymap_pid > 0)
- wait_for_terminate_and_warn(KBD_LOADKEYS, keymap_pid, true);
+ font_ok = font_load_and_wait(vc, vc_font, vc_font_map, vc_font_unimap) > 0;
+ keyboard_ok = keyboard_load_and_wait(vc, vc_keymap, vc_keymap_toggle, utf8) > 0;
- /* Only copy the font when we started setfont successfully */
- if (font_copy && font_pid > 0)
- font_copy_to_all_vcs(fd);
+ /* Only copy the font when we executed setfont successfully */
+ if (font_copy && font_ok)
+ (void) font_copy_to_all_vcs(fd);
- return EXIT_SUCCESS;
+ return font_ok && keyboard_ok ? EXIT_SUCCESS : EXIT_FAILURE;
}